diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000000..1770177310d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +[*] +indent_style = tab +indent_size = 4 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +end_of_line = lf + +[*.yml] +indent_style = space +indent_size = 2 + +[*.py] +indent_style = space diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 45b7dc5a642..526be3bcaf0 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -15,7 +15,6 @@ add: Added new things del: Removed old things tweak: tweaked a few things -balance: rebalanced something fix: fixed a few things wip: added a few works in progress soundadd: added a new sound thingy diff --git a/.gitignore b/.gitignore index a4634f3fc84..74660294c3b 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,18 @@ data/ *.db stddef.dm .atom-build.json -*.vscode/* \ No newline at end of file +*.vscode/* +!/.vscode/extensions.json + +# ignore DMI tool build cache +/tools/dmitool/bin/ +/tools/dmitool/.gradle/ +/tools/dmitool/.settings/ +/tools/dmitool/.classpath +/tools/dmitool/.project + +# ignore midi2piano build cache +/tools/midi2piano/obj/* + + + diff --git a/.travis.yml b/.travis.yml index 146be925c77..7f74746d15f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -50,7 +50,7 @@ jobs: before_script: skip script: - shopt -s globstar - - (! grep 'step_[xy]' _maps/map_files/**/*.dmm) + - python3 tools/travis/check_line_endings.py - (num=$(grep -Ern '\\(red|blue|green|black|italic|bold|b|i[^mc])' code/ | wc -l); echo "$num BYOND text macros (expecting ${BYOND_MACRO_COUNT} or fewer)"; [ $num -le ${BYOND_MACRO_COUNT} ]) - md5sum -c - <<< "6dc1b6bf583f3bd4176b6df494caa5f1 *html/changelogs/example.yml" - python tools/ss13_genchangelog.py html/changelog.html html/changelogs @@ -69,4 +69,4 @@ jobs: - npm install --loglevel=error before_script: script: - - gulp + - node node_modules/gulp/bin/gulp.js --require less-loader diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000000..9ab4061ce6a --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "gbasood.byond-dm-language-support", + "platymuus.dm-langclient", + "EditorConfig.EditorConfig" + ] +} diff --git a/SQL/paradise_schema.sql b/SQL/paradise_schema.sql index 01f83ba0194..837f8f440d6 100644 --- a/SQL/paradise_schema.sql +++ b/SQL/paradise_schema.sql @@ -97,7 +97,7 @@ CREATE TABLE `customuseritems` ( `cuiPropAdjust` text, `cuiJobMask` text NOT NULL, PRIMARY KEY (`id`) -) ENGINE=MyISAM AUTO_INCREMENT=56 DEFAULT CHARSET=latin1; +) ENGINE=MyISAM AUTO_INCREMENT=56 DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -124,7 +124,7 @@ CREATE TABLE `death` ( `fireloss` int(11) NOT NULL, `oxyloss` int(11) NOT NULL, PRIMARY KEY (`id`) -) ENGINE=MyISAM AUTO_INCREMENT=166546 DEFAULT CHARSET=latin1; +) ENGINE=MyISAM AUTO_INCREMENT=166546 DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -160,7 +160,7 @@ CREATE TABLE `admin` ( `level` int(2) NOT NULL DEFAULT '0', `flags` int(16) NOT NULL DEFAULT '0', PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=99 DEFAULT CHARSET=latin1; +) ENGINE=InnoDB AUTO_INCREMENT=99 DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -177,7 +177,7 @@ CREATE TABLE `admin_log` ( `adminip` varchar(18) NOT NULL, `log` text NOT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -212,7 +212,7 @@ CREATE TABLE `ban` ( `unbanned_computerid` varchar(32) DEFAULT NULL, `unbanned_ip` varchar(32) DEFAULT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=10685 DEFAULT CHARSET=latin1; +) ENGINE=InnoDB AUTO_INCREMENT=10685 DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -230,7 +230,7 @@ CREATE TABLE `feedback` ( `var_value` int(16) DEFAULT NULL, `details` text, PRIMARY KEY (`id`) -) ENGINE=MyISAM AUTO_INCREMENT=257638 DEFAULT CHARSET=latin1; +) ENGINE=MyISAM AUTO_INCREMENT=257638 DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -272,7 +272,7 @@ CREATE TABLE `player` ( `parallax` tinyint(1) DEFAULT '8', PRIMARY KEY (`id`), UNIQUE KEY `ckey` (`ckey`) -) ENGINE=InnoDB AUTO_INCREMENT=32446 DEFAULT CHARSET=latin1; +) ENGINE=InnoDB AUTO_INCREMENT=32446 DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -293,7 +293,7 @@ CREATE TABLE `poll_option` ( `descmid` varchar(32) DEFAULT NULL, `descmax` varchar(32) DEFAULT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -314,7 +314,7 @@ CREATE TABLE `poll_question` ( `createdby_ckey` varchar(45) NULL DEFAULT NULL, `createdby_ip` varchar(45) NULL DEFAULT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -333,7 +333,7 @@ CREATE TABLE `poll_textreply` ( `replytext` text NOT NULL, `adminrank` varchar(32) NOT NULL DEFAULT 'Player', PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -353,7 +353,7 @@ CREATE TABLE `poll_vote` ( `adminrank` varchar(32) NOT NULL, `rating` int(2) DEFAULT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -369,7 +369,7 @@ CREATE TABLE `privacy` ( `consent` bit(1) NOT NULL, PRIMARY KEY (`ckey`), UNIQUE KEY `ckey_UNIQUE` (`ckey`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -391,7 +391,7 @@ CREATE TABLE `karma` ( `spenderip` text NOT NULL, `time` datetime NOT NULL, PRIMARY KEY (`id`) -) ENGINE=MyISAM AUTO_INCREMENT=73614 DEFAULT CHARSET=latin1; +) ENGINE=MyISAM AUTO_INCREMENT=73614 DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -407,7 +407,7 @@ CREATE TABLE `karmatotals` ( `karma` int(11) NOT NULL, `karmaspent` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`id`) -) ENGINE=MyISAM AUTO_INCREMENT=6765 DEFAULT CHARSET=latin1; +) ENGINE=MyISAM AUTO_INCREMENT=6765 DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -426,7 +426,7 @@ CREATE TABLE `library` ( `ckey` varchar(45) NOT NULL, `flagged` int(11) NOT NULL, PRIMARY KEY (`id`) -) ENGINE=MyISAM AUTO_INCREMENT=929 DEFAULT CHARSET=latin1; +) ENGINE=MyISAM AUTO_INCREMENT=929 DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -442,7 +442,7 @@ CREATE TABLE `legacy_population` ( `admincount` int(11) DEFAULT NULL, `time` datetime NOT NULL, PRIMARY KEY (`id`) -) ENGINE=MyISAM AUTO_INCREMENT=2550 DEFAULT CHARSET=latin1; +) ENGINE=MyISAM AUTO_INCREMENT=2550 DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -458,7 +458,7 @@ CREATE TABLE `whitelist` ( `job` text, `species` text, PRIMARY KEY (`id`) -) ENGINE=MyISAM AUTO_INCREMENT=877 DEFAULT CHARSET=latin1; +) ENGINE=MyISAM AUTO_INCREMENT=877 DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; @@ -485,7 +485,7 @@ CREATE TABLE `watch` ( `last_editor` varchar(32), `edits` text, PRIMARY KEY (`ckey`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; @@ -507,7 +507,7 @@ CREATE TABLE `notes` ( `server` varchar(50) NOT NULL, `crew_playtime` mediumint(8) UNSIGNED DEFAULT '0', PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -524,7 +524,7 @@ CREATE TABLE `memo` ( `last_editor` varchar(32), `edits` text, PRIMARY KEY (`ckey`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -538,7 +538,7 @@ CREATE TABLE `ipintel` ( `date` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL, `intel` real NOT NULL DEFAULT '0', PRIMARY KEY (`ip`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -551,7 +551,7 @@ CREATE TABLE `vpn_whitelist` ( `ckey` varchar(32) NOT NULL, `reason` text, PRIMARY KEY (`ckey`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -564,7 +564,7 @@ CREATE TABLE `oauth_tokens` ( `ckey` varchar(32) NOT NULL, `token` varchar(32) NOT NULL, PRIMARY KEY (`token`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; @@ -580,7 +580,7 @@ CREATE TABLE `playtime_history` ( `time_living` SMALLINT NOT NULL, `time_ghost` SMALLINT NOT NULL, PRIMARY KEY (`ckey`, `date`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- @@ -594,4 +594,4 @@ CREATE TABLE `connection_log` ( `ip` varchar(32) NOT NULL, `computerid` varchar(32) NOT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; \ No newline at end of file +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; \ No newline at end of file diff --git a/SQL/paradise_schema_prefixed.sql b/SQL/paradise_schema_prefixed.sql index 9801c54d6f9..b43141f6784 100644 --- a/SQL/paradise_schema_prefixed.sql +++ b/SQL/paradise_schema_prefixed.sql @@ -97,7 +97,7 @@ CREATE TABLE `SS13_customuseritems` ( `cuiPropAdjust` text, `cuiJobMask` text NOT NULL, PRIMARY KEY (`id`) -) ENGINE=MyISAM AUTO_INCREMENT=56 DEFAULT CHARSET=latin1; +) ENGINE=MyISAM AUTO_INCREMENT=56 DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -124,7 +124,7 @@ CREATE TABLE `SS13_death` ( `fireloss` int(11) NOT NULL, `oxyloss` int(11) NOT NULL, PRIMARY KEY (`id`) -) ENGINE=MyISAM AUTO_INCREMENT=166546 DEFAULT CHARSET=latin1; +) ENGINE=MyISAM AUTO_INCREMENT=166546 DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -159,7 +159,7 @@ CREATE TABLE `SS13_admin` ( `level` int(2) NOT NULL DEFAULT '0', `flags` int(16) NOT NULL DEFAULT '0', PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=99 DEFAULT CHARSET=latin1; +) ENGINE=InnoDB AUTO_INCREMENT=99 DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -176,7 +176,7 @@ CREATE TABLE `SS13_admin_log` ( `adminip` varchar(18) NOT NULL, `log` text NOT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -211,7 +211,7 @@ CREATE TABLE `SS13_ban` ( `unbanned_computerid` varchar(32) DEFAULT NULL, `unbanned_ip` varchar(32) DEFAULT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=10685 DEFAULT CHARSET=latin1; +) ENGINE=InnoDB AUTO_INCREMENT=10685 DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -229,7 +229,7 @@ CREATE TABLE `SS13_feedback` ( `var_value` int(16) DEFAULT NULL, `details` text, PRIMARY KEY (`id`) -) ENGINE=MyISAM AUTO_INCREMENT=257638 DEFAULT CHARSET=latin1; +) ENGINE=MyISAM AUTO_INCREMENT=257638 DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -271,7 +271,7 @@ CREATE TABLE `SS13_player` ( `parallax` tinyint(1) DEFAULT '8', PRIMARY KEY (`id`), UNIQUE KEY `ckey` (`ckey`) -) ENGINE=InnoDB AUTO_INCREMENT=32446 DEFAULT CHARSET=latin1; +) ENGINE=InnoDB AUTO_INCREMENT=32446 DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -292,7 +292,7 @@ CREATE TABLE `SS13_poll_option` ( `descmid` varchar(32) DEFAULT NULL, `descmax` varchar(32) DEFAULT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -313,7 +313,7 @@ CREATE TABLE `SS13_poll_question` ( `createdby_ckey` varchar(45) NULL DEFAULT NULL, `createdby_ip` varchar(45) NULL DEFAULT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -332,7 +332,7 @@ CREATE TABLE `SS13_poll_textreply` ( `replytext` text NOT NULL, `adminrank` varchar(32) NOT NULL DEFAULT 'Player', PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -352,7 +352,7 @@ CREATE TABLE `SS13_poll_vote` ( `adminrank` varchar(32) NOT NULL, `rating` int(2) DEFAULT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -368,7 +368,7 @@ CREATE TABLE `ss13_privacy` ( `consent` bit(1) NOT NULL, PRIMARY KEY (`ckey`), UNIQUE KEY `ckey_UNIQUE` (`ckey`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -390,7 +390,7 @@ CREATE TABLE `SS13_karma` ( `spenderip` text NOT NULL, `time` datetime NOT NULL, PRIMARY KEY (`id`) -) ENGINE=MyISAM AUTO_INCREMENT=73614 DEFAULT CHARSET=latin1; +) ENGINE=MyISAM AUTO_INCREMENT=73614 DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -406,7 +406,7 @@ CREATE TABLE `SS13_karmatotals` ( `karma` int(11) NOT NULL, `karmaspent` int(11) NOT NULL DEFAULT '0', PRIMARY KEY (`id`) -) ENGINE=MyISAM AUTO_INCREMENT=6765 DEFAULT CHARSET=latin1; +) ENGINE=MyISAM AUTO_INCREMENT=6765 DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -425,7 +425,7 @@ CREATE TABLE `SS13_library` ( `ckey` varchar(45) NOT NULL, `flagged` int(11) NOT NULL, PRIMARY KEY (`id`) -) ENGINE=MyISAM AUTO_INCREMENT=929 DEFAULT CHARSET=latin1; +) ENGINE=MyISAM AUTO_INCREMENT=929 DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -441,7 +441,7 @@ CREATE TABLE `SS13_legacy_population` ( `admincount` int(11) DEFAULT NULL, `time` datetime NOT NULL, PRIMARY KEY (`id`) -) ENGINE=MyISAM AUTO_INCREMENT=2550 DEFAULT CHARSET=latin1; +) ENGINE=MyISAM AUTO_INCREMENT=2550 DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -457,7 +457,7 @@ CREATE TABLE `SS13_whitelist` ( `job` text, `species` text, PRIMARY KEY (`id`) -) ENGINE=MyISAM AUTO_INCREMENT=877 DEFAULT CHARSET=latin1; +) ENGINE=MyISAM AUTO_INCREMENT=877 DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; @@ -484,7 +484,7 @@ CREATE TABLE `SS13_watch` ( `last_editor` varchar(32), `edits` text, PRIMARY KEY (`ckey`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; @@ -506,7 +506,7 @@ CREATE TABLE `SS13_notes` ( `server` varchar(50) NOT NULL, `crew_playtime` mediumint(8) UNSIGNED DEFAULT '0', PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -523,7 +523,7 @@ CREATE TABLE `SS13_memo` ( `last_editor` varchar(32), `edits` text, PRIMARY KEY (`ckey`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -537,7 +537,7 @@ CREATE TABLE `SS13_ipintel` ( `date` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL, `intel` real NOT NULL DEFAULT '0', PRIMARY key (`ip`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -550,7 +550,7 @@ CREATE TABLE `SS13_vpn_whitelist` ( `ckey` varchar(32) NOT NULL, `reason` text, PRIMARY KEY (`ckey`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -563,7 +563,7 @@ CREATE TABLE `SS13_oauth_tokens` ( `ckey` varchar(32) NOT NULL, `token` varchar(32) NOT NULL, PRIMARY KEY (`token`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -578,7 +578,7 @@ CREATE TABLE `SS13_playtime_history` ( `time_living` SMALLINT NOT NULL, `time_ghost` SMALLINT NOT NULL, PRIMARY KEY (`ckey`, `date`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- -- Table structure for table `SS13_connection_log` @@ -591,4 +591,4 @@ CREATE TABLE `SS13_connection_log` ( `ip` varchar(32) NOT NULL, `computerid` varchar(32) NOT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=latin1; \ No newline at end of file +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; \ No newline at end of file diff --git a/SQL/updates/10-11.sql b/SQL/updates/10-11.sql new file mode 100644 index 00000000000..9564d3efaad --- /dev/null +++ b/SQL/updates/10-11.sql @@ -0,0 +1,41 @@ +# Updating SQL from ver 10 to 11 +# Changes tables from latin1 to utf8mb4 +# Based on: https://github.com/BeeStation/BeeStation-Hornet/pull/1074/files +# -Kyet + +# Generation (not necessary to run unless your DB has extra tables) +SELECT CONCAT("ALTER TABLE ", TABLE_SCHEMA, '.', TABLE_NAME," CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;") AS ExecuteTheString +FROM INFORMATION_SCHEMA.TABLES +WHERE TABLE_SCHEMA="feedback" +AND TABLE_TYPE="BASE TABLE"; + +# Actual table conversion stuff +ALTER TABLE feedback.admin CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE feedback.admin_log CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE feedback.ban CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE feedback.characters CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE feedback.connection_log CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE feedback.customuseritems CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE feedback.death CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE feedback.donators CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE feedback.erro_privacy CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE feedback.feedback CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE feedback.ipintel CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE feedback.karma CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE feedback.karmatotals CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE feedback.legacy_population CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE feedback.library CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE feedback.memo CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE feedback.notes CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE feedback.oauth_tokens CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE feedback.player CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE feedback.playtime_history CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE feedback.poll_option CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE feedback.poll_question CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE feedback.poll_textreply CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE feedback.poll_vote CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE feedback.population CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE feedback.privacy CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE feedback.vpn_whitelist CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE feedback.watch CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +ALTER TABLE feedback.whitelist CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; \ No newline at end of file diff --git a/_maps/map_files/cyberiad/cyberiad.dmm b/_maps/map_files/cyberiad/cyberiad.dmm index 8de75d7aaf1..74e179255ce 100644 --- a/_maps/map_files/cyberiad/cyberiad.dmm +++ b/_maps/map_files/cyberiad/cyberiad.dmm @@ -27893,7 +27893,6 @@ /area/chapel/main) "aYA" = ( /obj/structure/table, -/obj/item/lighter/zippo/black, /turf/simulated/floor/plasteel{ icon_state = "vault"; dir = 5 @@ -29916,6 +29915,7 @@ network = list("SS13") }, /obj/structure/table/wood, +/obj/item/lighter/zippo/black, /turf/simulated/floor/carpet/black, /area/chapel/office) "bcx" = ( @@ -35447,7 +35447,6 @@ /area/library) "bmF" = ( /obj/structure/cult/archives, -/obj/item/videocam, /obj/machinery/light_switch{ pixel_y = -25 }, @@ -72015,7 +72014,7 @@ dir = 8; layer = 2.9 }, -/obj/item/circuitboard/rdconsole/public{ +/obj/item/circuitboard/rdconsole{ pixel_x = -4 }, /obj/item/circuitboard/rdserver{ diff --git a/code/ATMOSPHERICS/atmospherics.dm b/code/ATMOSPHERICS/atmospherics.dm index 0c1b0a35426..0e3c1b78798 100644 --- a/code/ATMOSPHERICS/atmospherics.dm +++ b/code/ATMOSPHERICS/atmospherics.dm @@ -1,345 +1,345 @@ -/* -Quick overview: - -Pipes combine to form pipelines -Pipelines and other atmospheric objects combine to form pipe_networks - Note: A single pipe_network represents a completely open space - -Pipes -> Pipelines -Pipelines + Other Objects -> Pipe network -*/ -GLOBAL_DATUM_INIT(pipe_icon_manager, /datum/pipe_icon_manager, new()) -/obj/machinery/atmospherics - anchored = 1 - layer = GAS_PIPE_HIDDEN_LAYER //under wires - resistance_flags = FIRE_PROOF - max_integrity = 200 - plane = FLOOR_PLANE - idle_power_usage = 0 - active_power_usage = 0 - power_channel = ENVIRON - on_blueprints = TRUE - var/nodealert = 0 - var/can_unwrench = 0 - - var/connect_types[] = list(1) //1=regular, 2=supply, 3=scrubber - var/connected_to = 1 //same as above, currently not used for anything - var/icon_connect_type = "" //"-supply" or "-scrubbers" - - var/initialize_directions = 0 - - var/pipe_color - var/obj/item/pipe/stored - var/image/pipe_image - -/obj/machinery/atmospherics/New() - if (!armor) - armor = list("melee" = 25, "bullet" = 10, "laser" = 10, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 70) - ..() - - if(!pipe_color) - pipe_color = color - color = null - - if(!pipe_color_check(pipe_color)) - pipe_color = null - -/obj/machinery/atmospherics/Initialize() - . = ..() - SSair.atmos_machinery += src - -/obj/machinery/atmospherics/proc/atmos_init() - if(can_unwrench) - stored = new(src, make_from = src) - -/obj/machinery/atmospherics/Destroy() - QDEL_NULL(stored) - SSair.atmos_machinery -= src - SSair.deferred_pipenet_rebuilds -= src - for(var/mob/living/L in src) //ventcrawling is serious business - L.remove_ventcrawl() - L.forceMove(get_turf(src)) - QDEL_NULL(pipe_image) //we have to del it, or it might keep a ref somewhere else - return ..() - -// Icons/overlays/underlays -/obj/machinery/atmospherics/update_icon() - var/turf/T = loc - if(level == 2 || !T.intact) - plane = GAME_PLANE - else - plane = FLOOR_PLANE - -/obj/machinery/atmospherics/proc/update_pipe_image() - pipe_image = image(src, loc, layer = ABOVE_HUD_LAYER, dir = dir) //the 20 puts it above Byond's darkness (not its opacity view) - pipe_image.plane = HUD_PLANE - -/obj/machinery/atmospherics/proc/check_icon_cache(var/safety = 0) - if(!istype(GLOB.pipe_icon_manager)) - if(!safety) //to prevent infinite loops - GLOB.pipe_icon_manager = new() - check_icon_cache(1) - return 0 - - return 1 - -/obj/machinery/atmospherics/proc/color_cache_name(var/obj/machinery/atmospherics/node) - //Don't use this for standard pipes - if(!istype(node)) - return null - - return node.pipe_color - -/obj/machinery/atmospherics/proc/add_underlay(var/turf/T, var/obj/machinery/atmospherics/node, var/direction, var/icon_connect_type) - if(node) - if(T.intact && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) - //underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay_down", direction, color_cache_name(node)) - underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "down" + icon_connect_type) - else - //underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay_intact", direction, color_cache_name(node)) - underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type) - else - //underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay_exposed", direction, pipe_color) - underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "exposed" + icon_connect_type) - -/obj/machinery/atmospherics/proc/update_underlays() - if(check_icon_cache()) - return 1 - else - return 0 - -// Connect types -/obj/machinery/atmospherics/proc/check_connect_types(obj/machinery/atmospherics/atmos1, obj/machinery/atmospherics/atmos2) - var/i - var/list1[] = atmos1.connect_types - var/list2[] = atmos2.connect_types - for(i=1,i<=list1.len,i++) - var/j - for(j=1,j<=list2.len,j++) - if(list1[i] == list2[j]) - var/n = list1[i] - return n - return 0 - -/obj/machinery/atmospherics/proc/check_connect_types_construction(obj/machinery/atmospherics/atmos1, obj/item/pipe/pipe2) - var/i - var/list1[] = atmos1.connect_types - var/list2[] = pipe2.connect_types - for(i=1,i<=list1.len,i++) - var/j - for(j=1,j<=list2.len,j++) - if(list1[i] == list2[j]) - var/n = list1[i] - return n - return 0 - -// Pipenet related functions -/obj/machinery/atmospherics/proc/returnPipenet() - return - -/obj/machinery/atmospherics/proc/returnPipenetAir() - return - -/obj/machinery/atmospherics/proc/setPipenet() - return - -/obj/machinery/atmospherics/proc/replacePipenet() - return - -/obj/machinery/atmospherics/proc/build_network(remove_deferral = FALSE) - // Called to build a network from this node - if(remove_deferral) - SSair.deferred_pipenet_rebuilds -= src - -/obj/machinery/atmospherics/proc/defer_build_network() - SSair.deferred_pipenet_rebuilds += src - -/obj/machinery/atmospherics/proc/disconnect(obj/machinery/atmospherics/reference) - return - -/obj/machinery/atmospherics/proc/nullifyPipenet(datum/pipeline/P) - if(P) - P.other_atmosmch -= src - -//(De)construction -/obj/machinery/atmospherics/attackby(obj/item/W, mob/user) - if(can_unwrench && istype(W, /obj/item/wrench)) - var/turf/T = get_turf(src) - if(level == 1 && isturf(T) && T.intact) - to_chat(user, "You must remove the plating first.") - return 1 - var/datum/gas_mixture/int_air = return_air() - var/datum/gas_mixture/env_air = loc.return_air() - add_fingerprint(user) - - var/unsafe_wrenching = FALSE - var/I = int_air ? int_air.return_pressure() : 0 - var/E = env_air ? env_air.return_pressure() : 0 - var/internal_pressure = I - E - - playsound(src.loc, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if(internal_pressure > 2*ONE_ATMOSPHERE) - to_chat(user, "As you begin unwrenching \the [src] a gush of air blows in your face... maybe you should reconsider?") - unsafe_wrenching = TRUE //Oh dear oh dear - - if(do_after(user, 40 * W.toolspeed, target = src) && !QDELETED(src)) - user.visible_message( \ - "[user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear ratchet.") - investigate_log("was REMOVED by [key_name(usr)]", "atmos") - - //You unwrenched a pipe full of pressure? let's splat you into the wall silly. - if(unsafe_wrenching) - unsafe_pressure_release(user,internal_pressure) - deconstruct(TRUE) - else - return ..() - -//Called when an atmospherics object is unwrenched while having a large pressure difference -//with it's locs air contents. -/obj/machinery/atmospherics/proc/unsafe_pressure_release(mob/user, pressures) - if(!user) - return - - if(!pressures) - var/datum/gas_mixture/int_air = return_air() - var/datum/gas_mixture/env_air = loc.return_air() - pressures = int_air.return_pressure() - env_air.return_pressure() - - var/fuck_you_dir = get_dir(src, user) - var/turf/general_direction = get_edge_target_turf(user, fuck_you_dir) - user.visible_message("[user] is sent flying by pressure!","The pressure sends you flying!") - //Values based on 2*ONE_ATMOS (the unsafe pressure), resulting in 20 range and 4 speed - user.throw_at(general_direction, pressures/10, pressures/50) - -/obj/machinery/atmospherics/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - if(can_unwrench) - if(stored) - stored.forceMove(get_turf(src)) - if(!disassembled) - stored.obj_integrity = stored.max_integrity * 0.5 - transfer_fingerprints_to(stored) - stored = null - ..() - -/obj/machinery/atmospherics/on_construction(D, P, C) - if(C) - color = C - dir = D - initialize_directions = P - var/turf/T = loc - level = T.intact ? 2 : 1 - add_fingerprint(usr) - if(!SSair.initialized) //If there's no atmos subsystem, we can't really initialize pipenets - SSair.machinery_to_construct.Add(src) - return - initialize_atmos_network() - -/obj/machinery/atmospherics/proc/initialize_atmos_network() - atmos_init() - var/list/nodes = pipeline_expansion() - for(var/obj/machinery/atmospherics/A in nodes) - A.atmos_init() - A.addMember(src) - build_network() - -// Find a connecting /obj/machinery/atmospherics in specified direction. -/obj/machinery/atmospherics/proc/findConnecting(var/direction) - for(var/obj/machinery/atmospherics/target in get_step(src,direction)) - var/can_connect = check_connect_types(target, src) - if(can_connect && (target.initialize_directions & get_dir(target,src))) - return target - -// Ventcrawling -#define VENT_SOUND_DELAY 30 -/obj/machinery/atmospherics/relaymove(mob/living/user, direction) - direction &= initialize_directions - if(!direction || !(direction in cardinal)) //cant go this way. - return - - if(user in buckled_mobs)// fixes buckle ventcrawl edgecase fuck bug - return - - var/obj/machinery/atmospherics/target_move = findConnecting(direction) - if(target_move) - if(is_type_in_list(target_move, ventcrawl_machinery) && target_move.can_crawl_through()) - user.remove_ventcrawl() - user.forceMove(target_move.loc) //handles entering and so on - user.visible_message("You hear something squeezing through the ducts.", "You climb out the ventilation system.") - else if(target_move.can_crawl_through()) - if(returnPipenet() != target_move.returnPipenet()) - user.update_pipe_vision(target_move) - user.loc = target_move - user.client.eye = target_move //if we don't do this, Byond only updates the eye every tick - required for smooth movement - if(world.time - user.last_played_vent > VENT_SOUND_DELAY) - user.last_played_vent = world.time - playsound(src, 'sound/machines/ventcrawl.ogg', 50, 1, -3) - else - if((direction & initialize_directions) || is_type_in_list(src, ventcrawl_machinery)) //if we move in a way the pipe can connect, but doesn't - or we're in a vent - user.remove_ventcrawl() - user.forceMove(src.loc) - user.visible_message("You hear something squeezing through the pipes.", "You climb out the ventilation system.") - user.canmove = 0 - spawn(1) - user.canmove = 1 - -/obj/machinery/atmospherics/AltClick(var/mob/living/L) - if(is_type_in_list(src, ventcrawl_machinery)) - L.handle_ventcrawl(src) - return - ..() - -/obj/machinery/atmospherics/proc/can_crawl_through() - return 1 - -/obj/machinery/atmospherics/proc/change_color(var/new_color) - //only pass valid pipe colors please ~otherwise your pipe will turn invisible - if(!pipe_color_check(new_color)) - return - - pipe_color = new_color - update_icon() - -// Additional icon procs -/obj/machinery/atmospherics/proc/universal_underlays(var/obj/machinery/atmospherics/node, var/direction) - var/turf/T = get_turf(src) - if(!istype(T)) return - if(node) - var/node_dir = get_dir(src,node) - if(node.icon_connect_type == "-supply") - add_underlay_adapter(T, , node_dir, "") - add_underlay_adapter(T, node, node_dir, "-supply") - add_underlay_adapter(T, , node_dir, "-scrubbers") - else if(node.icon_connect_type == "-scrubbers") - add_underlay_adapter(T, , node_dir, "") - add_underlay_adapter(T, , node_dir, "-supply") - add_underlay_adapter(T, node, node_dir, "-scrubbers") - else - add_underlay_adapter(T, node, node_dir, "") - add_underlay_adapter(T, , node_dir, "-supply") - add_underlay_adapter(T, , node_dir, "-scrubbers") - else - add_underlay_adapter(T, , direction, "-supply") - add_underlay_adapter(T, , direction, "-scrubbers") - add_underlay_adapter(T, , direction, "") - -/obj/machinery/atmospherics/proc/add_underlay_adapter(var/turf/T, var/obj/machinery/atmospherics/node, var/direction, var/icon_connect_type) //modified from add_underlay, does not make exposed underlays - if(node) - if(T.intact && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) - underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "down" + icon_connect_type) - else - underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type) - else - underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "retracted" + icon_connect_type) - -/obj/machinery/atmospherics/singularity_pull(S, current_size) - if(current_size >= STAGE_FIVE) - deconstruct(FALSE) - return ..() - -/obj/machinery/atmospherics/update_remote_sight(mob/user) - user.sight |= (SEE_TURFS|BLIND) - . = ..() +/* +Quick overview: + +Pipes combine to form pipelines +Pipelines and other atmospheric objects combine to form pipe_networks + Note: A single pipe_network represents a completely open space + +Pipes -> Pipelines +Pipelines + Other Objects -> Pipe network +*/ +GLOBAL_DATUM_INIT(pipe_icon_manager, /datum/pipe_icon_manager, new()) +/obj/machinery/atmospherics + anchored = 1 + layer = GAS_PIPE_HIDDEN_LAYER //under wires + resistance_flags = FIRE_PROOF + max_integrity = 200 + plane = FLOOR_PLANE + idle_power_usage = 0 + active_power_usage = 0 + power_channel = ENVIRON + on_blueprints = TRUE + var/nodealert = 0 + var/can_unwrench = 0 + + var/connect_types[] = list(1) //1=regular, 2=supply, 3=scrubber + var/connected_to = 1 //same as above, currently not used for anything + var/icon_connect_type = "" //"-supply" or "-scrubbers" + + var/initialize_directions = 0 + + var/pipe_color + var/obj/item/pipe/stored + var/image/pipe_image + +/obj/machinery/atmospherics/New() + if (!armor) + armor = list("melee" = 25, "bullet" = 10, "laser" = 10, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 70) + ..() + + if(!pipe_color) + pipe_color = color + color = null + + if(!pipe_color_check(pipe_color)) + pipe_color = null + +/obj/machinery/atmospherics/Initialize() + . = ..() + SSair.atmos_machinery += src + +/obj/machinery/atmospherics/proc/atmos_init() + if(can_unwrench) + stored = new(src, make_from = src) + +/obj/machinery/atmospherics/Destroy() + QDEL_NULL(stored) + SSair.atmos_machinery -= src + SSair.deferred_pipenet_rebuilds -= src + for(var/mob/living/L in src) //ventcrawling is serious business + L.remove_ventcrawl() + L.forceMove(get_turf(src)) + QDEL_NULL(pipe_image) //we have to del it, or it might keep a ref somewhere else + return ..() + +// Icons/overlays/underlays +/obj/machinery/atmospherics/update_icon() + var/turf/T = get_turf(loc) + if(!T || level == 2 || !T.intact) + plane = GAME_PLANE + else + plane = FLOOR_PLANE + +/obj/machinery/atmospherics/proc/update_pipe_image() + pipe_image = image(src, loc, layer = ABOVE_HUD_LAYER, dir = dir) //the 20 puts it above Byond's darkness (not its opacity view) + pipe_image.plane = HUD_PLANE + +/obj/machinery/atmospherics/proc/check_icon_cache(var/safety = 0) + if(!istype(GLOB.pipe_icon_manager)) + if(!safety) //to prevent infinite loops + GLOB.pipe_icon_manager = new() + check_icon_cache(1) + return 0 + + return 1 + +/obj/machinery/atmospherics/proc/color_cache_name(var/obj/machinery/atmospherics/node) + //Don't use this for standard pipes + if(!istype(node)) + return null + + return node.pipe_color + +/obj/machinery/atmospherics/proc/add_underlay(var/turf/T, var/obj/machinery/atmospherics/node, var/direction, var/icon_connect_type) + if(node) + if(T.intact && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) + //underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay_down", direction, color_cache_name(node)) + underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "down" + icon_connect_type) + else + //underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay_intact", direction, color_cache_name(node)) + underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type) + else + //underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay_exposed", direction, pipe_color) + underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "exposed" + icon_connect_type) + +/obj/machinery/atmospherics/proc/update_underlays() + if(check_icon_cache()) + return 1 + else + return 0 + +// Connect types +/obj/machinery/atmospherics/proc/check_connect_types(obj/machinery/atmospherics/atmos1, obj/machinery/atmospherics/atmos2) + var/i + var/list1[] = atmos1.connect_types + var/list2[] = atmos2.connect_types + for(i=1,i<=list1.len,i++) + var/j + for(j=1,j<=list2.len,j++) + if(list1[i] == list2[j]) + var/n = list1[i] + return n + return 0 + +/obj/machinery/atmospherics/proc/check_connect_types_construction(obj/machinery/atmospherics/atmos1, obj/item/pipe/pipe2) + var/i + var/list1[] = atmos1.connect_types + var/list2[] = pipe2.connect_types + for(i=1,i<=list1.len,i++) + var/j + for(j=1,j<=list2.len,j++) + if(list1[i] == list2[j]) + var/n = list1[i] + return n + return 0 + +// Pipenet related functions +/obj/machinery/atmospherics/proc/returnPipenet() + return + +/obj/machinery/atmospherics/proc/returnPipenetAir() + return + +/obj/machinery/atmospherics/proc/setPipenet() + return + +/obj/machinery/atmospherics/proc/replacePipenet() + return + +/obj/machinery/atmospherics/proc/build_network(remove_deferral = FALSE) + // Called to build a network from this node + if(remove_deferral) + SSair.deferred_pipenet_rebuilds -= src + +/obj/machinery/atmospherics/proc/defer_build_network() + SSair.deferred_pipenet_rebuilds += src + +/obj/machinery/atmospherics/proc/disconnect(obj/machinery/atmospherics/reference) + return + +/obj/machinery/atmospherics/proc/nullifyPipenet(datum/pipeline/P) + if(P) + P.other_atmosmch -= src + +//(De)construction +/obj/machinery/atmospherics/attackby(obj/item/W, mob/user) + if(can_unwrench && istype(W, /obj/item/wrench)) + var/turf/T = get_turf(src) + if(level == 1 && isturf(T) && T.intact) + to_chat(user, "You must remove the plating first.") + return 1 + var/datum/gas_mixture/int_air = return_air() + var/datum/gas_mixture/env_air = loc.return_air() + add_fingerprint(user) + + var/unsafe_wrenching = FALSE + var/I = int_air ? int_air.return_pressure() : 0 + var/E = env_air ? env_air.return_pressure() : 0 + var/internal_pressure = I - E + + playsound(src.loc, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if(internal_pressure > 2*ONE_ATMOSPHERE) + to_chat(user, "As you begin unwrenching \the [src] a gush of air blows in your face... maybe you should reconsider?") + unsafe_wrenching = TRUE //Oh dear oh dear + + if(do_after(user, 40 * W.toolspeed, target = src) && !QDELETED(src)) + user.visible_message( \ + "[user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear ratchet.") + investigate_log("was REMOVED by [key_name(usr)]", "atmos") + + //You unwrenched a pipe full of pressure? let's splat you into the wall silly. + if(unsafe_wrenching) + unsafe_pressure_release(user,internal_pressure) + deconstruct(TRUE) + else + return ..() + +//Called when an atmospherics object is unwrenched while having a large pressure difference +//with it's locs air contents. +/obj/machinery/atmospherics/proc/unsafe_pressure_release(mob/user, pressures) + if(!user) + return + + if(!pressures) + var/datum/gas_mixture/int_air = return_air() + var/datum/gas_mixture/env_air = loc.return_air() + pressures = int_air.return_pressure() - env_air.return_pressure() + + var/fuck_you_dir = get_dir(src, user) + var/turf/general_direction = get_edge_target_turf(user, fuck_you_dir) + user.visible_message("[user] is sent flying by pressure!","The pressure sends you flying!") + //Values based on 2*ONE_ATMOS (the unsafe pressure), resulting in 20 range and 4 speed + user.throw_at(general_direction, pressures/10, pressures/50) + +/obj/machinery/atmospherics/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + if(can_unwrench) + if(stored) + stored.forceMove(get_turf(src)) + if(!disassembled) + stored.obj_integrity = stored.max_integrity * 0.5 + transfer_fingerprints_to(stored) + stored = null + ..() + +/obj/machinery/atmospherics/on_construction(D, P, C) + if(C) + color = C + dir = D + initialize_directions = P + var/turf/T = loc + level = T.intact ? 2 : 1 + add_fingerprint(usr) + if(!SSair.initialized) //If there's no atmos subsystem, we can't really initialize pipenets + SSair.machinery_to_construct.Add(src) + return + initialize_atmos_network() + +/obj/machinery/atmospherics/proc/initialize_atmos_network() + atmos_init() + var/list/nodes = pipeline_expansion() + for(var/obj/machinery/atmospherics/A in nodes) + A.atmos_init() + A.addMember(src) + build_network() + +// Find a connecting /obj/machinery/atmospherics in specified direction. +/obj/machinery/atmospherics/proc/findConnecting(var/direction) + for(var/obj/machinery/atmospherics/target in get_step(src,direction)) + var/can_connect = check_connect_types(target, src) + if(can_connect && (target.initialize_directions & get_dir(target,src))) + return target + +// Ventcrawling +#define VENT_SOUND_DELAY 30 +/obj/machinery/atmospherics/relaymove(mob/living/user, direction) + direction &= initialize_directions + if(!direction || !(direction in cardinal)) //cant go this way. + return + + if(user in buckled_mobs)// fixes buckle ventcrawl edgecase fuck bug + return + + var/obj/machinery/atmospherics/target_move = findConnecting(direction) + if(target_move) + if(is_type_in_list(target_move, ventcrawl_machinery) && target_move.can_crawl_through()) + user.remove_ventcrawl() + user.forceMove(target_move.loc) //handles entering and so on + user.visible_message("You hear something squeezing through the ducts.", "You climb out the ventilation system.") + else if(target_move.can_crawl_through()) + if(returnPipenet() != target_move.returnPipenet()) + user.update_pipe_vision(target_move) + user.loc = target_move + user.client.eye = target_move //if we don't do this, Byond only updates the eye every tick - required for smooth movement + if(world.time - user.last_played_vent > VENT_SOUND_DELAY) + user.last_played_vent = world.time + playsound(src, 'sound/machines/ventcrawl.ogg', 50, 1, -3) + else + if((direction & initialize_directions) || is_type_in_list(src, ventcrawl_machinery)) //if we move in a way the pipe can connect, but doesn't - or we're in a vent + user.remove_ventcrawl() + user.forceMove(src.loc) + user.visible_message("You hear something squeezing through the pipes.", "You climb out the ventilation system.") + user.canmove = 0 + spawn(1) + user.canmove = 1 + +/obj/machinery/atmospherics/AltClick(var/mob/living/L) + if(is_type_in_list(src, ventcrawl_machinery)) + L.handle_ventcrawl(src) + return + ..() + +/obj/machinery/atmospherics/proc/can_crawl_through() + return 1 + +/obj/machinery/atmospherics/proc/change_color(var/new_color) + //only pass valid pipe colors please ~otherwise your pipe will turn invisible + if(!pipe_color_check(new_color)) + return + + pipe_color = new_color + update_icon() + +// Additional icon procs +/obj/machinery/atmospherics/proc/universal_underlays(var/obj/machinery/atmospherics/node, var/direction) + var/turf/T = get_turf(src) + if(!istype(T)) return + if(node) + var/node_dir = get_dir(src,node) + if(node.icon_connect_type == "-supply") + add_underlay_adapter(T, , node_dir, "") + add_underlay_adapter(T, node, node_dir, "-supply") + add_underlay_adapter(T, , node_dir, "-scrubbers") + else if(node.icon_connect_type == "-scrubbers") + add_underlay_adapter(T, , node_dir, "") + add_underlay_adapter(T, , node_dir, "-supply") + add_underlay_adapter(T, node, node_dir, "-scrubbers") + else + add_underlay_adapter(T, node, node_dir, "") + add_underlay_adapter(T, , node_dir, "-supply") + add_underlay_adapter(T, , node_dir, "-scrubbers") + else + add_underlay_adapter(T, , direction, "-supply") + add_underlay_adapter(T, , direction, "-scrubbers") + add_underlay_adapter(T, , direction, "") + +/obj/machinery/atmospherics/proc/add_underlay_adapter(var/turf/T, var/obj/machinery/atmospherics/node, var/direction, var/icon_connect_type) //modified from add_underlay, does not make exposed underlays + if(node) + if(T.intact && node.level == 1 && istype(node, /obj/machinery/atmospherics/pipe)) + underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "down" + icon_connect_type) + else + underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "intact" + icon_connect_type) + else + underlays += GLOB.pipe_icon_manager.get_atmos_icon("underlay", direction, color_cache_name(node), "retracted" + icon_connect_type) + +/obj/machinery/atmospherics/singularity_pull(S, current_size) + if(current_size >= STAGE_FIVE) + deconstruct(FALSE) + return ..() + +/obj/machinery/atmospherics/update_remote_sight(mob/user) + user.sight |= (SEE_TURFS|BLIND) + . = ..() diff --git a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm index 68a1033a266..0a8b9eb95a4 100644 --- a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm +++ b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm @@ -1,155 +1,155 @@ -/obj/machinery/atmospherics/binary - dir = SOUTH - initialize_directions = SOUTH|NORTH - use_power = IDLE_POWER_USE - - layer = GAS_PUMP_LAYER - - var/datum/gas_mixture/air1 - var/datum/gas_mixture/air2 - - var/obj/machinery/atmospherics/node1 - var/obj/machinery/atmospherics/node2 - - var/datum/pipeline/parent1 - var/datum/pipeline/parent2 - -/obj/machinery/atmospherics/binary/New() - ..() - switch(dir) - if(NORTH) - initialize_directions = NORTH|SOUTH - if(SOUTH) - initialize_directions = NORTH|SOUTH - if(EAST) - initialize_directions = EAST|WEST - if(WEST) - initialize_directions = EAST|WEST - - air1 = new - air2 = new - - air1.volume = 200 - air2.volume = 200 - -/obj/machinery/atmospherics/binary/Destroy() - if(node1) - node1.disconnect(src) - node1 = null - nullifyPipenet(parent1) - if(node2) - node2.disconnect(src) - node2 = null - nullifyPipenet(parent2) - return ..() - -/obj/machinery/atmospherics/binary/atmos_init() - ..() - var/node2_connect = dir - var/node1_connect = turn(dir, 180) - - for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect)) - if(target.initialize_directions & get_dir(target,src)) - var/c = check_connect_types(target,src) - if(c) - target.connected_to = c - connected_to = c - node1 = target - break - - for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect)) - if(target.initialize_directions & get_dir(target,src)) - var/c = check_connect_types(target,src) - if(c) - target.connected_to = c - connected_to = c - node2 = target - break - - update_icon() - update_underlays() - -/obj/machinery/atmospherics/binary/build_network(remove_deferral = FALSE) - if(!parent1) - parent1 = new /datum/pipeline() - parent1.build_pipeline(src) - - if(!parent2) - parent2 = new /datum/pipeline() - parent2.build_pipeline(src) - ..() - -/obj/machinery/atmospherics/binary/disconnect(obj/machinery/atmospherics/reference) - if(reference == node1) - if(istype(node1, /obj/machinery/atmospherics/pipe)) - qdel(parent1) - node1 = null - else if(reference == node2) - if(istype(node2, /obj/machinery/atmospherics/pipe)) - qdel(parent2) - node2 = null - update_icon() - -/obj/machinery/atmospherics/binary/nullifyPipenet(datum/pipeline/P) - ..() - if(!P) - return - if(P == parent1) - parent1.other_airs -= air1 - parent1 = null - else if(P == parent2) - parent2.other_airs -= air2 - parent2 = null - -/obj/machinery/atmospherics/binary/returnPipenetAir(datum/pipeline/P) - if(P == parent1) - return air1 - else if(P == parent2) - return air2 - -/obj/machinery/atmospherics/binary/pipeline_expansion(datum/pipeline/P) - if(P) - if(parent1 == P) - return list(node1) - else if(parent2 == P) - return list(node2) - else - return list(node1, node2) - -/obj/machinery/atmospherics/binary/setPipenet(datum/pipeline/P, obj/machinery/atmospherics/A) - if(A == node1) - parent1 = P - else if(A == node2) - parent2 = P - -/obj/machinery/atmospherics/binary/returnPipenet(obj/machinery/atmospherics/A) - if(A == node1) - return parent1 - else if(A == node2) - return parent2 - -/obj/machinery/atmospherics/binary/replacePipenet(datum/pipeline/Old, datum/pipeline/New) - if(Old == parent1) - parent1 = New - else if(Old == parent2) - parent2 = New - -/obj/machinery/atmospherics/binary/unsafe_pressure_release(var/mob/user,var/pressures) - ..() - - var/turf/T = get_turf(src) - if(T) - //Remove the gas from air1+air2 and assume it - var/datum/gas_mixture/environment = T.return_air() - var/lost = pressures*environment.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) - lost += pressures*environment.volume/(air2.temperature * R_IDEAL_GAS_EQUATION) - var/shared_loss = lost/2 - - var/datum/gas_mixture/to_release = air1.remove(shared_loss) - to_release.merge(air2.remove(shared_loss)) - T.assume_air(to_release) - air_update_turf(1) - -/obj/machinery/atmospherics/binary/process_atmos() - ..() - return parent1 && parent2 +/obj/machinery/atmospherics/binary + dir = SOUTH + initialize_directions = SOUTH|NORTH + use_power = IDLE_POWER_USE + + layer = GAS_PUMP_LAYER + + var/datum/gas_mixture/air1 + var/datum/gas_mixture/air2 + + var/obj/machinery/atmospherics/node1 + var/obj/machinery/atmospherics/node2 + + var/datum/pipeline/parent1 + var/datum/pipeline/parent2 + +/obj/machinery/atmospherics/binary/New() + ..() + switch(dir) + if(NORTH) + initialize_directions = NORTH|SOUTH + if(SOUTH) + initialize_directions = NORTH|SOUTH + if(EAST) + initialize_directions = EAST|WEST + if(WEST) + initialize_directions = EAST|WEST + + air1 = new + air2 = new + + air1.volume = 200 + air2.volume = 200 + +/obj/machinery/atmospherics/binary/Destroy() + if(node1) + node1.disconnect(src) + node1 = null + nullifyPipenet(parent1) + if(node2) + node2.disconnect(src) + node2 = null + nullifyPipenet(parent2) + return ..() + +/obj/machinery/atmospherics/binary/atmos_init() + ..() + var/node2_connect = dir + var/node1_connect = turn(dir, 180) + + for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect)) + if(target.initialize_directions & get_dir(target,src)) + var/c = check_connect_types(target,src) + if(c) + target.connected_to = c + connected_to = c + node1 = target + break + + for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect)) + if(target.initialize_directions & get_dir(target,src)) + var/c = check_connect_types(target,src) + if(c) + target.connected_to = c + connected_to = c + node2 = target + break + + update_icon() + update_underlays() + +/obj/machinery/atmospherics/binary/build_network(remove_deferral = FALSE) + if(!parent1) + parent1 = new /datum/pipeline() + parent1.build_pipeline(src) + + if(!parent2) + parent2 = new /datum/pipeline() + parent2.build_pipeline(src) + ..() + +/obj/machinery/atmospherics/binary/disconnect(obj/machinery/atmospherics/reference) + if(reference == node1) + if(istype(node1, /obj/machinery/atmospherics/pipe)) + qdel(parent1) + node1 = null + else if(reference == node2) + if(istype(node2, /obj/machinery/atmospherics/pipe)) + qdel(parent2) + node2 = null + update_icon() + +/obj/machinery/atmospherics/binary/nullifyPipenet(datum/pipeline/P) + ..() + if(!P) + return + if(P == parent1) + parent1.other_airs -= air1 + parent1 = null + else if(P == parent2) + parent2.other_airs -= air2 + parent2 = null + +/obj/machinery/atmospherics/binary/returnPipenetAir(datum/pipeline/P) + if(P == parent1) + return air1 + else if(P == parent2) + return air2 + +/obj/machinery/atmospherics/binary/pipeline_expansion(datum/pipeline/P) + if(P) + if(parent1 == P) + return list(node1) + else if(parent2 == P) + return list(node2) + else + return list(node1, node2) + +/obj/machinery/atmospherics/binary/setPipenet(datum/pipeline/P, obj/machinery/atmospherics/A) + if(A == node1) + parent1 = P + else if(A == node2) + parent2 = P + +/obj/machinery/atmospherics/binary/returnPipenet(obj/machinery/atmospherics/A) + if(A == node1) + return parent1 + else if(A == node2) + return parent2 + +/obj/machinery/atmospherics/binary/replacePipenet(datum/pipeline/Old, datum/pipeline/New) + if(Old == parent1) + parent1 = New + else if(Old == parent2) + parent2 = New + +/obj/machinery/atmospherics/binary/unsafe_pressure_release(var/mob/user,var/pressures) + ..() + + var/turf/T = get_turf(src) + if(T) + //Remove the gas from air1+air2 and assume it + var/datum/gas_mixture/environment = T.return_air() + var/lost = pressures*environment.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) + lost += pressures*environment.volume/(air2.temperature * R_IDEAL_GAS_EQUATION) + var/shared_loss = lost/2 + + var/datum/gas_mixture/to_release = air1.remove(shared_loss) + to_release.merge(air2.remove(shared_loss)) + T.assume_air(to_release) + air_update_turf(1) + +/obj/machinery/atmospherics/binary/process_atmos() + ..() + return parent1 && parent2 diff --git a/code/ATMOSPHERICS/components/binary_devices/circulator.dm b/code/ATMOSPHERICS/components/binary_devices/circulator.dm index e2dd952213a..b45dd8929b4 100644 --- a/code/ATMOSPHERICS/components/binary_devices/circulator.dm +++ b/code/ATMOSPHERICS/components/binary_devices/circulator.dm @@ -1,125 +1,125 @@ -//node1, air1, network1 correspond to input -//node2, air2, network2 correspond to output -/obj/machinery/atmospherics/binary/circulator - name = "circulator/heat exchanger" - desc = "A gas circulator pump and heat exchanger. Its input port is on the south side, and its output port is on the north side." - icon = 'icons/obj/atmospherics/circulator.dmi' - icon_state = "circ1-off" - - var/side = CIRC_LEFT - - var/global/const/CIRC_LEFT = WEST - var/global/const/CIRC_RIGHT = EAST - - var/last_pressure_delta = 0 - - var/obj/machinery/power/generator/generator - - anchored = 1 - density = 1 - - can_unwrench = 1 - var/side_inverted = 0 - -// Creating a custom circulator pipe subtype to be delivered through cargo -/obj/item/pipe/circulator - name = "circulator/heat exchanger fitting" - -/obj/item/pipe/circulator/New(loc) - var/obj/machinery/atmospherics/binary/circulator/C = new /obj/machinery/atmospherics/binary/circulator(null) - ..(loc, make_from = C) - -/obj/machinery/atmospherics/binary/circulator/Destroy() - if(generator && generator.cold_circ == src) - generator.cold_circ = null - else if(generator && generator.hot_circ == src) - generator.hot_circ = null - return ..() - -/obj/machinery/atmospherics/binary/circulator/proc/return_transfer_air() - var/datum/gas_mixture/inlet = get_inlet_air() - var/datum/gas_mixture/outlet = get_outlet_air() - var/output_starting_pressure = outlet.return_pressure() - var/input_starting_pressure = inlet.return_pressure() - - if(output_starting_pressure >= input_starting_pressure - 10) - //Need at least 10 KPa difference to overcome friction in the mechanism - last_pressure_delta = 0 - return null - - //Calculate necessary moles to transfer using PV = nRT - if(inlet.temperature > 0) - var/pressure_delta = (input_starting_pressure - output_starting_pressure) / 2 - - var/transfer_moles = pressure_delta * outlet.volume/(inlet.temperature * R_IDEAL_GAS_EQUATION) - - last_pressure_delta = pressure_delta - - //log_debug("pressure_delta = [pressure_delta]; transfer_moles = [transfer_moles];") - - //Actually transfer the gas - var/datum/gas_mixture/removed = inlet.remove(transfer_moles) - - parent1.update = 1 - parent2.update = 1 - - return removed - - else - last_pressure_delta = 0 - -/obj/machinery/atmospherics/binary/circulator/process_atmos() - ..() - update_icon() - -/obj/machinery/atmospherics/binary/circulator/proc/get_inlet_air() - if(side_inverted==0) - return air2 - else - return air1 - -/obj/machinery/atmospherics/binary/circulator/proc/get_outlet_air() - if(side_inverted==0) - return air1 - else - return air2 - -/obj/machinery/atmospherics/binary/circulator/proc/get_inlet_side() - if(dir==SOUTH||dir==NORTH) - if(side_inverted==0) - return "South" - else - return "North" - -/obj/machinery/atmospherics/binary/circulator/proc/get_outlet_side() - if(dir==SOUTH||dir==NORTH) - if(side_inverted==0) - return "North" - else - return "South" - -/obj/machinery/atmospherics/binary/circulator/attackby(obj/item/W as obj, mob/user as mob, params) - if(ismultitool(W)) - if(side_inverted == 0) - side_inverted = 1 - else - side_inverted = 0 - to_chat(user, "You reverse the circulator's valve settings. The inlet of the circulator is now on the [get_inlet_side(dir)] side.") - desc = "A gas circulator pump and heat exchanger. Its input port is on the [get_inlet_side(dir)] side, and its output port is on the [get_outlet_side(dir)] side." - else - return ..() - -/obj/machinery/atmospherics/binary/circulator/update_icon() - ..() - - if(stat & (BROKEN|NOPOWER)) - icon_state = "circ[side]-p" - else if(last_pressure_delta > 0) - if(last_pressure_delta > ONE_ATMOSPHERE) - icon_state = "circ[side]-run" - else - icon_state = "circ[side]-slow" - else - icon_state = "circ[side]-off" - - return 1 \ No newline at end of file +//node1, air1, network1 correspond to input +//node2, air2, network2 correspond to output +#define CIRC_LEFT WEST +#define CIRC_RIGHT EAST + +/obj/machinery/atmospherics/binary/circulator + name = "circulator/heat exchanger" + desc = "A gas circulator pump and heat exchanger. Its input port is on the south side, and its output port is on the north side." + icon = 'icons/obj/atmospherics/circulator.dmi' + icon_state = "circ1-off" + + var/side = CIRC_LEFT + + var/last_pressure_delta = 0 + + var/obj/machinery/power/generator/generator + + anchored = 1 + density = 1 + + can_unwrench = 1 + var/side_inverted = 0 + +// Creating a custom circulator pipe subtype to be delivered through cargo +/obj/item/pipe/circulator + name = "circulator/heat exchanger fitting" + +/obj/item/pipe/circulator/New(loc) + var/obj/machinery/atmospherics/binary/circulator/C = new /obj/machinery/atmospherics/binary/circulator(null) + ..(loc, make_from = C) + +/obj/machinery/atmospherics/binary/circulator/Destroy() + if(generator && generator.cold_circ == src) + generator.cold_circ = null + else if(generator && generator.hot_circ == src) + generator.hot_circ = null + return ..() + +/obj/machinery/atmospherics/binary/circulator/proc/return_transfer_air() + var/datum/gas_mixture/inlet = get_inlet_air() + var/datum/gas_mixture/outlet = get_outlet_air() + var/output_starting_pressure = outlet.return_pressure() + var/input_starting_pressure = inlet.return_pressure() + + if(output_starting_pressure >= input_starting_pressure - 10) + //Need at least 10 KPa difference to overcome friction in the mechanism + last_pressure_delta = 0 + return null + + //Calculate necessary moles to transfer using PV = nRT + if(inlet.temperature > 0) + var/pressure_delta = (input_starting_pressure - output_starting_pressure) / 2 + + var/transfer_moles = pressure_delta * outlet.volume/(inlet.temperature * R_IDEAL_GAS_EQUATION) + + last_pressure_delta = pressure_delta + + //log_debug("pressure_delta = [pressure_delta]; transfer_moles = [transfer_moles];") + + //Actually transfer the gas + var/datum/gas_mixture/removed = inlet.remove(transfer_moles) + + parent1.update = 1 + parent2.update = 1 + + return removed + + else + last_pressure_delta = 0 + +/obj/machinery/atmospherics/binary/circulator/process_atmos() + ..() + update_icon() + +/obj/machinery/atmospherics/binary/circulator/proc/get_inlet_air() + if(side_inverted==0) + return air2 + else + return air1 + +/obj/machinery/atmospherics/binary/circulator/proc/get_outlet_air() + if(side_inverted==0) + return air1 + else + return air2 + +/obj/machinery/atmospherics/binary/circulator/proc/get_inlet_side() + if(dir==SOUTH||dir==NORTH) + if(side_inverted==0) + return "South" + else + return "North" + +/obj/machinery/atmospherics/binary/circulator/proc/get_outlet_side() + if(dir==SOUTH||dir==NORTH) + if(side_inverted==0) + return "North" + else + return "South" + +/obj/machinery/atmospherics/binary/circulator/multitool_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(!side_inverted) + side_inverted = TRUE + else + side_inverted = FALSE + to_chat(user, "You reverse the circulator's valve settings. The inlet of the circulator is now on the [get_inlet_side(dir)] side.") + desc = "A gas circulator pump and heat exchanger. Its input port is on the [get_inlet_side(dir)] side, and its output port is on the [get_outlet_side(dir)] side." + +/obj/machinery/atmospherics/binary/circulator/update_icon() + ..() + + if(stat & (BROKEN|NOPOWER)) + icon_state = "circ[side]-p" + else if(last_pressure_delta > 0) + if(last_pressure_delta > ONE_ATMOSPHERE) + icon_state = "circ[side]-run" + else + icon_state = "circ[side]-slow" + else + icon_state = "circ[side]-off" + + return 1 diff --git a/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm b/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm index b9902dbb066..c0ed34ef206 100644 --- a/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/dp_vent_pump.dm @@ -1,269 +1,269 @@ -/obj/machinery/atmospherics/binary/dp_vent_pump - icon = 'icons/atmos/vent_pump.dmi' - icon_state = "map_dp_vent" - - //node2 is output port - //node1 is input port - - req_one_access_txt = "24;10" - - name = "dual-port air vent" - desc = "Has a valve and pump attached to it. There are two ports." - - can_unwrench = 1 - - level = 1 - - connect_types = list(1,2,3) //connects to regular, supply and scrubbers pipes - - var/on = 0 - var/pump_direction = 1 //0 = siphoning, 1 = releasing - - var/external_pressure_bound = ONE_ATMOSPHERE - var/input_pressure_min = 0 - var/output_pressure_max = 0 - - var/frequency = ATMOS_VENTSCRUB - var/id_tag = null - var/datum/radio_frequency/radio_connection - var/advcontrol = 0//does this device listen to the AAC - - settagwhitelist = list("id_tag") - - var/pressure_checks = 1 - //1: Do not pass external_pressure_bound - //2: Do not pass input_pressure_min - //4: Do not pass output_pressure_max - -/obj/machinery/atmospherics/binary/dp_vent_pump/New() - ..() - if(!id_tag) - assign_uid() - id_tag = num2text(uid) - icon = null - -/obj/machinery/atmospherics/binary/dp_vent_pump/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - return ..() - -/obj/machinery/atmospherics/binary/dp_vent_pump/atmos_init() - ..() - if(frequency) - set_frequency(frequency) - -/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume - name = "large dual port air vent" - -/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume/on - on = TRUE - -/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume/New() - ..() - air1.volume = 1000 - air2.volume = 1000 - -/obj/machinery/atmospherics/binary/volume_pump/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - add_underlay(T, node1, turn(dir, -180)) - add_underlay(T, node2, dir) - -/obj/machinery/atmospherics/binary/dp_vent_pump/update_icon(var/safety = 0) - ..() - - if(!check_icon_cache()) - return - - overlays.Cut() - - var/vent_icon = "vent" - - var/turf/T = get_turf(src) - if(!istype(T)) - return - - if(T.intact && node1 && node2 && node1.level == 1 && node2.level == 1 && istype(node1, /obj/machinery/atmospherics/pipe) && istype(node2, /obj/machinery/atmospherics/pipe)) - vent_icon += "h" - - if(!powered()) - vent_icon += "off" - else - vent_icon += "[on ? "[pump_direction ? "out" : "in"]" : "off"]" - - overlays += GLOB.pipe_icon_manager.get_atmos_icon("device", , , vent_icon) - -/obj/machinery/atmospherics/binary/dp_vent_pump/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - if(T.intact && node1 && node2 && node1.level == 1 && node2.level == 1 && istype(node1, /obj/machinery/atmospherics/pipe) && istype(node2, /obj/machinery/atmospherics/pipe)) - return - else - if(node1) - add_underlay(T, node1, turn(dir, -180), node1.icon_connect_type) - else - add_underlay(T, node1, turn(dir, -180)) - if(node2) - add_underlay(T, node2, dir, node2.icon_connect_type) - else - add_underlay(T, node2, dir) - -/obj/machinery/atmospherics/binary/dp_vent_pump/process_atmos() - ..() - if(!on) - return 0 - - var/datum/gas_mixture/environment = loc.return_air() - var/environment_pressure = environment.return_pressure() - - if(pump_direction) //input -> external - var/pressure_delta = 10000 - - if(pressure_checks&1) - pressure_delta = min(pressure_delta, (external_pressure_bound - environment_pressure)) - if(pressure_checks&2) - pressure_delta = min(pressure_delta, (air1.return_pressure() - input_pressure_min)) - - if(pressure_delta > 0) - if(air1.temperature > 0) - var/transfer_moles = pressure_delta*environment.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) - - var/datum/gas_mixture/removed = air1.remove(transfer_moles) - - loc.assume_air(removed) - - parent1.update = 1 - air_update_turf() - else //external -> output - var/pressure_delta = 10000 - - if(pressure_checks&1) - pressure_delta = min(pressure_delta, (environment_pressure - external_pressure_bound)) - if(pressure_checks&4) - pressure_delta = min(pressure_delta, (output_pressure_max - air2.return_pressure())) - - if(pressure_delta > 0) - if(environment.temperature > 0) - var/transfer_moles = pressure_delta*air2.volume/(environment.temperature * R_IDEAL_GAS_EQUATION) - - var/datum/gas_mixture/removed = loc.remove_air(transfer_moles) - - air2.merge(removed) - - parent2.update = 1 - air_update_turf() - return 1 - -//Radio remote control -/obj/machinery/atmospherics/binary/dp_vent_pump/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA) - -/obj/machinery/atmospherics/binary/dp_vent_pump/proc/broadcast_status() - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - - signal.data = list( - "tag" = id_tag, - "device" = "ADVP", - "power" = on, - "direction" = pump_direction?("release"):("siphon"), - "checks" = pressure_checks, - "input" = input_pressure_min, - "output" = output_pressure_max, - "external" = external_pressure_bound, - "sigtype" = "status" - ) - radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) - - return 1 - -/obj/machinery/atmospherics/binary/dp_vent_pump/receive_signal(datum/signal/signal) - if(!signal.data["tag"] || (signal.data["tag"] != id_tag) || (signal.data["sigtype"]!="command") || (signal.data["advcontrol"] && !advcontrol)) - return 0 - if(signal.data["power"] != null) - on = text2num(signal.data["power"]) - - if(signal.data["power_toggle"] != null) - on = !on - - if(signal.data["direction"] != null) - pump_direction = text2num(signal.data["direction"]) - - if(signal.data["checks"] != null) - pressure_checks = text2num(signal.data["checks"]) - - if(signal.data["purge"]) - pressure_checks &= ~1 - pump_direction = 0 - - if(signal.data["stabilize"])//the fact that this was "stabalize" shows how many fucks people give about these wonders, none - pressure_checks |= 1 - pump_direction = 1 - - if(signal.data["set_input_pressure"] != null) - input_pressure_min = between( - 0, - text2num(signal.data["set_input_pressure"]), - ONE_ATMOSPHERE*50 - ) - - if(signal.data["set_output_pressure"] != null) - output_pressure_max = between( - 0, - text2num(signal.data["set_output_pressure"]), - ONE_ATMOSPHERE*50 - ) - - if(signal.data["set_external_pressure"] != null) - external_pressure_bound = between( - 0, - text2num(signal.data["set_external_pressure"]), - ONE_ATMOSPHERE*50 - ) - - if(signal.data["status"]) - spawn(2) - broadcast_status() - return //do not update_icon - - spawn(2) - broadcast_status() - update_icon() - -/obj/machinery/atmospherics/binary/dp_vent_pump/attackby(var/obj/item/W as obj, var/mob/user as mob) - if(istype(W, /obj/item/multitool)) - update_multitool_menu(user) - return 1 - - return ..() - -/obj/machinery/atmospherics/binary/dp_vent_pump/multitool_menu(var/mob/user,var/obj/item/multitool/P) - return {" - - "} - -/obj/machinery/atmospherics/binary/dp_vent_pump/multitool_topic(var/mob/user, var/list/href_list, var/obj/O) - . = ..() - if(.) - return . - if("toggleadvcontrol" in href_list) - advcontrol = !advcontrol - return TRUE +/obj/machinery/atmospherics/binary/dp_vent_pump + icon = 'icons/atmos/vent_pump.dmi' + icon_state = "map_dp_vent" + + //node2 is output port + //node1 is input port + + req_one_access_txt = "24;10" + + name = "dual-port air vent" + desc = "Has a valve and pump attached to it. There are two ports." + + can_unwrench = 1 + + level = 1 + + connect_types = list(1,2,3) //connects to regular, supply and scrubbers pipes + + var/on = 0 + var/pump_direction = 1 //0 = siphoning, 1 = releasing + + var/external_pressure_bound = ONE_ATMOSPHERE + var/input_pressure_min = 0 + var/output_pressure_max = 0 + + var/frequency = ATMOS_VENTSCRUB + var/id_tag = null + var/datum/radio_frequency/radio_connection + var/advcontrol = 0//does this device listen to the AAC + + settagwhitelist = list("id_tag") + + var/pressure_checks = 1 + //1: Do not pass external_pressure_bound + //2: Do not pass input_pressure_min + //4: Do not pass output_pressure_max + +/obj/machinery/atmospherics/binary/dp_vent_pump/New() + ..() + if(!id_tag) + assign_uid() + id_tag = num2text(uid) + icon = null + +/obj/machinery/atmospherics/binary/dp_vent_pump/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + return ..() + +/obj/machinery/atmospherics/binary/dp_vent_pump/atmos_init() + ..() + if(frequency) + set_frequency(frequency) + +/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume + name = "large dual port air vent" + +/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume/on + on = TRUE + +/obj/machinery/atmospherics/binary/dp_vent_pump/high_volume/New() + ..() + air1.volume = 1000 + air2.volume = 1000 + +/obj/machinery/atmospherics/binary/volume_pump/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + add_underlay(T, node1, turn(dir, -180)) + add_underlay(T, node2, dir) + +/obj/machinery/atmospherics/binary/dp_vent_pump/update_icon(var/safety = 0) + ..() + + if(!check_icon_cache()) + return + + overlays.Cut() + + var/vent_icon = "vent" + + var/turf/T = get_turf(src) + if(!istype(T)) + return + + if(T.intact && node1 && node2 && node1.level == 1 && node2.level == 1 && istype(node1, /obj/machinery/atmospherics/pipe) && istype(node2, /obj/machinery/atmospherics/pipe)) + vent_icon += "h" + + if(!powered()) + vent_icon += "off" + else + vent_icon += "[on ? "[pump_direction ? "out" : "in"]" : "off"]" + + overlays += GLOB.pipe_icon_manager.get_atmos_icon("device", , , vent_icon) + +/obj/machinery/atmospherics/binary/dp_vent_pump/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + if(T.intact && node1 && node2 && node1.level == 1 && node2.level == 1 && istype(node1, /obj/machinery/atmospherics/pipe) && istype(node2, /obj/machinery/atmospherics/pipe)) + return + else + if(node1) + add_underlay(T, node1, turn(dir, -180), node1.icon_connect_type) + else + add_underlay(T, node1, turn(dir, -180)) + if(node2) + add_underlay(T, node2, dir, node2.icon_connect_type) + else + add_underlay(T, node2, dir) + +/obj/machinery/atmospherics/binary/dp_vent_pump/process_atmos() + ..() + if(!on) + return 0 + + var/datum/gas_mixture/environment = loc.return_air() + var/environment_pressure = environment.return_pressure() + + if(pump_direction) //input -> external + var/pressure_delta = 10000 + + if(pressure_checks&1) + pressure_delta = min(pressure_delta, (external_pressure_bound - environment_pressure)) + if(pressure_checks&2) + pressure_delta = min(pressure_delta, (air1.return_pressure() - input_pressure_min)) + + if(pressure_delta > 0) + if(air1.temperature > 0) + var/transfer_moles = pressure_delta*environment.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) + + var/datum/gas_mixture/removed = air1.remove(transfer_moles) + + loc.assume_air(removed) + + parent1.update = 1 + air_update_turf() + else //external -> output + var/pressure_delta = 10000 + + if(pressure_checks&1) + pressure_delta = min(pressure_delta, (environment_pressure - external_pressure_bound)) + if(pressure_checks&4) + pressure_delta = min(pressure_delta, (output_pressure_max - air2.return_pressure())) + + if(pressure_delta > 0) + if(environment.temperature > 0) + var/transfer_moles = pressure_delta*air2.volume/(environment.temperature * R_IDEAL_GAS_EQUATION) + + var/datum/gas_mixture/removed = loc.remove_air(transfer_moles) + + air2.merge(removed) + + parent2.update = 1 + air_update_turf() + return 1 + +//Radio remote control +/obj/machinery/atmospherics/binary/dp_vent_pump/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA) + +/obj/machinery/atmospherics/binary/dp_vent_pump/proc/broadcast_status() + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + + signal.data = list( + "tag" = id_tag, + "device" = "ADVP", + "power" = on, + "direction" = pump_direction?("release"):("siphon"), + "checks" = pressure_checks, + "input" = input_pressure_min, + "output" = output_pressure_max, + "external" = external_pressure_bound, + "sigtype" = "status" + ) + radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) + + return 1 + +/obj/machinery/atmospherics/binary/dp_vent_pump/receive_signal(datum/signal/signal) + if(!signal.data["tag"] || (signal.data["tag"] != id_tag) || (signal.data["sigtype"]!="command") || (signal.data["advcontrol"] && !advcontrol)) + return 0 + if(signal.data["power"] != null) + on = text2num(signal.data["power"]) + + if(signal.data["power_toggle"] != null) + on = !on + + if(signal.data["direction"] != null) + pump_direction = text2num(signal.data["direction"]) + + if(signal.data["checks"] != null) + pressure_checks = text2num(signal.data["checks"]) + + if(signal.data["purge"]) + pressure_checks &= ~1 + pump_direction = 0 + + if(signal.data["stabilize"])//the fact that this was "stabalize" shows how many fucks people give about these wonders, none + pressure_checks |= 1 + pump_direction = 1 + + if(signal.data["set_input_pressure"] != null) + input_pressure_min = between( + 0, + text2num(signal.data["set_input_pressure"]), + ONE_ATMOSPHERE*50 + ) + + if(signal.data["set_output_pressure"] != null) + output_pressure_max = between( + 0, + text2num(signal.data["set_output_pressure"]), + ONE_ATMOSPHERE*50 + ) + + if(signal.data["set_external_pressure"] != null) + external_pressure_bound = between( + 0, + text2num(signal.data["set_external_pressure"]), + ONE_ATMOSPHERE*50 + ) + + if(signal.data["status"]) + spawn(2) + broadcast_status() + return //do not update_icon + + spawn(2) + broadcast_status() + update_icon() + +/obj/machinery/atmospherics/binary/dp_vent_pump/attackby(var/obj/item/W as obj, var/mob/user as mob) + if(istype(W, /obj/item/multitool)) + update_multitool_menu(user) + return 1 + + return ..() + +/obj/machinery/atmospherics/binary/dp_vent_pump/multitool_menu(var/mob/user,var/obj/item/multitool/P) + return {" + + "} + +/obj/machinery/atmospherics/binary/dp_vent_pump/multitool_topic(var/mob/user, var/list/href_list, var/obj/O) + . = ..() + if(.) + return . + if("toggleadvcontrol" in href_list) + advcontrol = !advcontrol + return TRUE diff --git a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm index 3448655c953..6eeb382b3d0 100644 --- a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm +++ b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm @@ -1,192 +1,192 @@ -/obj/machinery/atmospherics/binary/passive_gate - //Tries to achieve target pressure at output (like a normal pump) except - // Uses no power but can not transfer gases from a low pressure area to a high pressure area - icon = 'icons/atmos/passive_gate.dmi' - icon_state = "map" - - name = "passive gate" - desc = "A one-way air valve that does not require power" - - can_unwrench = 1 - - var/on = 0 - var/target_pressure = ONE_ATMOSPHERE - - var/frequency = 0 - var/id = null - var/datum/radio_frequency/radio_connection - -/obj/machinery/atmospherics/binary/passive_gate/atmos_init() - ..() - if(frequency) - set_frequency(frequency) - -/obj/machinery/atmospherics/binary/passive_gate/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - return ..() - -/obj/machinery/atmospherics/binary/passive_gate/update_icon() - ..() - icon_state = "[on ? "on" : "off"]" - -/obj/machinery/atmospherics/binary/passive_gate/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - add_underlay(T, node1, turn(dir, 180)) - add_underlay(T, node2, dir) - -/obj/machinery/atmospherics/binary/passive_gate/process_atmos() - ..() - if(!on) - return 0 - - var/output_starting_pressure = air2.return_pressure() - var/input_starting_pressure = air1.return_pressure() - - if(output_starting_pressure >= min(target_pressure,input_starting_pressure-10)) - //No need to pump gas if target is already reached or input pressure is too low - //Need at least 10 KPa difference to overcome friction in the mechanism - return 1 - - //Calculate necessary moles to transfer using PV = nRT - if((air1.total_moles() > 0) && (air1.temperature>0)) - var/pressure_delta = min(target_pressure - output_starting_pressure, (input_starting_pressure - output_starting_pressure)/2) - //Can not have a pressure delta that would cause output_pressure > input_pressure - - var/transfer_moles = pressure_delta*air2.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) - - //Actually transfer the gas - var/datum/gas_mixture/removed = air1.remove(transfer_moles) - air2.merge(removed) - - parent1.update = 1 - - parent2.update = 1 - return 1 - -//Radio remote control -/obj/machinery/atmospherics/binary/passive_gate/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA) - -/obj/machinery/atmospherics/binary/passive_gate/proc/broadcast_status() - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - - signal.data = list( - "tag" = id, - "device" = "AGP", - "power" = on, - "target_output" = target_pressure, - "sigtype" = "status" - ) - - radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) - - return 1 - -/obj/machinery/atmospherics/binary/passive_gate/receive_signal(datum/signal/signal) - if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) - return 0 - - var/old_on = on //for logging - - if("power" in signal.data) - on = text2num(signal.data["power"]) - - if("power_toggle" in signal.data) - on = !on - - if("set_output_pressure" in signal.data) - target_pressure = between( - 0, - text2num(signal.data["set_output_pressure"]), - ONE_ATMOSPHERE*50 - ) - - if(on != old_on) - investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos") - - if("status" in signal.data) - spawn(2) - broadcast_status() - return //do not update_icon - - spawn(2) - broadcast_status() - update_icon() - return - -/obj/machinery/atmospherics/binary/passive_gate/attack_hand(mob/user) - if(..()) - return - - if(!allowed(user)) - to_chat(user, "Access denied.") - return - - add_fingerprint(user) - ui_interact(user) - -/obj/machinery/atmospherics/binary/passive_gate/attack_ghost(mob/user) - ui_interact(user) - -/obj/machinery/atmospherics/binary/passive_gate/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) - user.set_machine(src) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "atmos_pump.tmpl", name, 385, 115, state = state) - ui.open() - -/obj/machinery/atmospherics/binary/passive_gate/ui_data(mob/user) - var/list/data = list() - data["on"] = on - data["pressure"] = round(target_pressure) - data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) - return data - -/obj/machinery/atmospherics/binary/passive_gate/Topic(href,href_list) - if(..()) - return 1 - - if(href_list["power"]) - on = !on - investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") - . = TRUE - if(href_list["pressure"]) - var/pressure = href_list["pressure"] - if(pressure == "max") - pressure = MAX_OUTPUT_PRESSURE - . = TRUE - else if(pressure == "input") - pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null - if(!isnull(pressure)) - . = TRUE - else if(text2num(pressure) != null) - pressure = text2num(pressure) - . = TRUE - if(.) - target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE) - investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") - - update_icon() - SSnanoui.update_uis(src) - -/obj/machinery/atmospherics/binary/passive_gate/attackby(obj/item/W, mob/user, params) - if(!istype(W, /obj/item/wrench)) - return ..() - if(on) - to_chat(user, "You cannot unwrench this [src], turn it off first.") - return 1 - return ..() +/obj/machinery/atmospherics/binary/passive_gate + //Tries to achieve target pressure at output (like a normal pump) except + // Uses no power but can not transfer gases from a low pressure area to a high pressure area + icon = 'icons/atmos/passive_gate.dmi' + icon_state = "map" + + name = "passive gate" + desc = "A one-way air valve that does not require power" + + can_unwrench = 1 + + var/on = 0 + var/target_pressure = ONE_ATMOSPHERE + + var/frequency = 0 + var/id = null + var/datum/radio_frequency/radio_connection + +/obj/machinery/atmospherics/binary/passive_gate/atmos_init() + ..() + if(frequency) + set_frequency(frequency) + +/obj/machinery/atmospherics/binary/passive_gate/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + return ..() + +/obj/machinery/atmospherics/binary/passive_gate/update_icon() + ..() + icon_state = "[on ? "on" : "off"]" + +/obj/machinery/atmospherics/binary/passive_gate/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + add_underlay(T, node1, turn(dir, 180)) + add_underlay(T, node2, dir) + +/obj/machinery/atmospherics/binary/passive_gate/process_atmos() + ..() + if(!on) + return 0 + + var/output_starting_pressure = air2.return_pressure() + var/input_starting_pressure = air1.return_pressure() + + if(output_starting_pressure >= min(target_pressure,input_starting_pressure-10)) + //No need to pump gas if target is already reached or input pressure is too low + //Need at least 10 KPa difference to overcome friction in the mechanism + return 1 + + //Calculate necessary moles to transfer using PV = nRT + if((air1.total_moles() > 0) && (air1.temperature>0)) + var/pressure_delta = min(target_pressure - output_starting_pressure, (input_starting_pressure - output_starting_pressure)/2) + //Can not have a pressure delta that would cause output_pressure > input_pressure + + var/transfer_moles = pressure_delta*air2.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) + + //Actually transfer the gas + var/datum/gas_mixture/removed = air1.remove(transfer_moles) + air2.merge(removed) + + parent1.update = 1 + + parent2.update = 1 + return 1 + +//Radio remote control +/obj/machinery/atmospherics/binary/passive_gate/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA) + +/obj/machinery/atmospherics/binary/passive_gate/proc/broadcast_status() + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + + signal.data = list( + "tag" = id, + "device" = "AGP", + "power" = on, + "target_output" = target_pressure, + "sigtype" = "status" + ) + + radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) + + return 1 + +/obj/machinery/atmospherics/binary/passive_gate/receive_signal(datum/signal/signal) + if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) + return 0 + + var/old_on = on //for logging + + if("power" in signal.data) + on = text2num(signal.data["power"]) + + if("power_toggle" in signal.data) + on = !on + + if("set_output_pressure" in signal.data) + target_pressure = between( + 0, + text2num(signal.data["set_output_pressure"]), + ONE_ATMOSPHERE*50 + ) + + if(on != old_on) + investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos") + + if("status" in signal.data) + spawn(2) + broadcast_status() + return //do not update_icon + + spawn(2) + broadcast_status() + update_icon() + return + +/obj/machinery/atmospherics/binary/passive_gate/attack_hand(mob/user) + if(..()) + return + + if(!allowed(user)) + to_chat(user, "Access denied.") + return + + add_fingerprint(user) + ui_interact(user) + +/obj/machinery/atmospherics/binary/passive_gate/attack_ghost(mob/user) + ui_interact(user) + +/obj/machinery/atmospherics/binary/passive_gate/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) + user.set_machine(src) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "atmos_pump.tmpl", name, 385, 115, state = state) + ui.open() + +/obj/machinery/atmospherics/binary/passive_gate/ui_data(mob/user) + var/list/data = list() + data["on"] = on + data["pressure"] = round(target_pressure) + data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) + return data + +/obj/machinery/atmospherics/binary/passive_gate/Topic(href,href_list) + if(..()) + return 1 + + if(href_list["power"]) + on = !on + investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") + . = TRUE + if(href_list["pressure"]) + var/pressure = href_list["pressure"] + if(pressure == "max") + pressure = MAX_OUTPUT_PRESSURE + . = TRUE + else if(pressure == "input") + pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null + if(!isnull(pressure)) + . = TRUE + else if(text2num(pressure) != null) + pressure = text2num(pressure) + . = TRUE + if(.) + target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE) + investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") + + update_icon() + SSnanoui.update_uis(src) + +/obj/machinery/atmospherics/binary/passive_gate/attackby(obj/item/W, mob/user, params) + if(!istype(W, /obj/item/wrench)) + return ..() + if(on) + to_chat(user, "You cannot unwrench this [src], turn it off first.") + return 1 + return ..() diff --git a/code/ATMOSPHERICS/components/binary_devices/pump.dm b/code/ATMOSPHERICS/components/binary_devices/pump.dm index 1998e67e17d..92550bb9325 100644 --- a/code/ATMOSPHERICS/components/binary_devices/pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/pump.dm @@ -1,261 +1,261 @@ -/* -Every cycle, the pump uses the air in air_in to try and make air_out the perfect pressure. - -node1, air1, network1 correspond to input -node2, air2, network2 correspond to output - -Thus, the two variables affect pump operation are set in New(): - air1.volume - This is the volume of gas available to the pump that may be transfered to the output - air2.volume - Higher quantities of this cause more air to be perfected later - but overall network volume is also increased as this increases... -*/ - -/obj/machinery/atmospherics/binary/pump - icon = 'icons/atmos/pump.dmi' - icon_state = "map_off" - - name = "gas pump" - desc = "A pump" - - can_unwrench = 1 - - var/on = 0 - var/target_pressure = ONE_ATMOSPHERE - - var/frequency = 0 - var/id = null - var/datum/radio_frequency/radio_connection - -/obj/machinery/atmospherics/binary/pump/CtrlClick(mob/living/user) - if(!istype(user) || user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!in_range(src, user) && !issilicon(usr)) - return - if(!ishuman(usr) && !issilicon(usr)) - return - toggle() - return ..() - -/obj/machinery/atmospherics/binary/pump/AICtrlClick() - toggle() - return ..() - -/obj/machinery/atmospherics/binary/pump/AltClick(mob/living/user) - if(!istype(user) || user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!in_range(src, user) && !issilicon(usr)) - return - if(!ishuman(usr) && !issilicon(usr)) - return - set_max() - return - -/obj/machinery/atmospherics/binary/pump/AIAltClick() - set_max() - return ..() - -/obj/machinery/atmospherics/binary/pump/proc/toggle() - if(powered()) - on = !on - update_icon() - -/obj/machinery/atmospherics/binary/pump/proc/set_max() - if(powered()) - target_pressure = MAX_OUTPUT_PRESSURE - update_icon() - -/obj/machinery/atmospherics/binary/pump/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - return ..() - -/obj/machinery/atmospherics/binary/pump/on - icon_state = "map_on" - on = 1 - -/obj/machinery/atmospherics/binary/pump/update_icon() - ..() - - if(!powered()) - icon_state = "off" - else - icon_state = "[on ? "on" : "off"]" - -/obj/machinery/atmospherics/binary/pump/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - add_underlay(T, node1, turn(dir, -180)) - add_underlay(T, node2, dir) - -/obj/machinery/atmospherics/binary/pump/process_atmos() - ..() - if((stat & (NOPOWER|BROKEN)) || !on) - return 0 - - var/output_starting_pressure = air2.return_pressure() - - if( (target_pressure - output_starting_pressure) < 0.01) - //No need to pump gas if target is already reached! - return 1 - - //Calculate necessary moles to transfer using PV=nRT - if((air1.total_moles() > 0) && (air1.temperature>0)) - var/pressure_delta = target_pressure - output_starting_pressure - var/transfer_moles = pressure_delta*air2.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) - - //Actually transfer the gas - var/datum/gas_mixture/removed = air1.remove(transfer_moles) - air2.merge(removed) - - parent1.update = 1 - - parent2.update = 1 - return 1 - -//Radio remote control -/obj/machinery/atmospherics/binary/pump/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA) - -/obj/machinery/atmospherics/binary/pump/proc/broadcast_status() - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - - signal.data = list( - "tag" = id, - "device" = "AGP", - "power" = on, - "target_output" = target_pressure, - "sigtype" = "status" - ) - - radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) - return 1 - -/obj/machinery/atmospherics/binary/pump/atmos_init() - ..() - if(frequency) - set_frequency(frequency) - -/obj/machinery/atmospherics/binary/pump/receive_signal(datum/signal/signal) - if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) - return 0 - - var/old_on = on //for logging - - if(signal.data["power"]) - on = text2num(signal.data["power"]) - - if(signal.data["power_toggle"]) - on = !on - - if(signal.data["set_output_pressure"]) - target_pressure = between( - 0, - text2num(signal.data["set_output_pressure"]), - ONE_ATMOSPHERE*50 - ) - - if(on != old_on) - investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos") - - if(signal.data["status"]) - spawn(2) - broadcast_status() - return //do not update_icon - - spawn(2) - broadcast_status() - update_icon() - return - -/obj/machinery/atmospherics/binary/pump/attack_hand(mob/user) - if(..()) - return - - if(!allowed(user)) - to_chat(user, "Access denied.") - return - - add_fingerprint(user) - ui_interact(user) - -/obj/machinery/atmospherics/binary/pump/attack_ghost(mob/user) - ui_interact(user) - -/obj/machinery/atmospherics/binary/pump/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) - user.set_machine(src) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "atmos_pump.tmpl", name, 385, 115, state = state) - ui.open() - -/obj/machinery/atmospherics/binary/pump/ui_data(mob/user) - var/list/data = list() - data["on"] = on - data["pressure"] = round(target_pressure) - data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) - return data - -/obj/machinery/atmospherics/binary/pump/Topic(href,href_list) - if(..()) - return 1 - - if(href_list["power"]) - on = !on - investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") - . = TRUE - if(href_list["pressure"]) - var/pressure = href_list["pressure"] - if(pressure == "max") - pressure = MAX_OUTPUT_PRESSURE - . = TRUE - else if(pressure == "input") - pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null - if(!isnull(pressure)) - . = TRUE - else if(text2num(pressure) != null) - pressure = text2num(pressure) - . = TRUE - if(.) - target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE) - investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") - - update_icon() - SSnanoui.update_uis(src) - -/obj/machinery/atmospherics/binary/pump/power_change() - var/old_stat = stat - ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/binary/pump/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/pen)) - var/t = copytext(stripped_input(user, "Enter the name for the pump.", "Rename", name), 1, MAX_NAME_LEN) - if(!t) - return - if(!in_range(src, usr) && loc != usr) - return - name = t - return - else if(!istype(W, /obj/item/wrench)) - return ..() - if(!(stat & NOPOWER) && on) - to_chat(user, "You cannot unwrench this [src], turn it off first.") - return 1 - return ..() \ No newline at end of file +/* +Every cycle, the pump uses the air in air_in to try and make air_out the perfect pressure. + +node1, air1, network1 correspond to input +node2, air2, network2 correspond to output + +Thus, the two variables affect pump operation are set in New(): + air1.volume + This is the volume of gas available to the pump that may be transfered to the output + air2.volume + Higher quantities of this cause more air to be perfected later + but overall network volume is also increased as this increases... +*/ + +/obj/machinery/atmospherics/binary/pump + icon = 'icons/atmos/pump.dmi' + icon_state = "map_off" + + name = "gas pump" + desc = "A pump" + + can_unwrench = 1 + + var/on = 0 + var/target_pressure = ONE_ATMOSPHERE + + var/frequency = 0 + var/id = null + var/datum/radio_frequency/radio_connection + +/obj/machinery/atmospherics/binary/pump/CtrlClick(mob/living/user) + if(!istype(user) || user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!in_range(src, user) && !issilicon(usr)) + return + if(!ishuman(usr) && !issilicon(usr)) + return + toggle() + return ..() + +/obj/machinery/atmospherics/binary/pump/AICtrlClick() + toggle() + return ..() + +/obj/machinery/atmospherics/binary/pump/AltClick(mob/living/user) + if(!istype(user) || user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!in_range(src, user) && !issilicon(usr)) + return + if(!ishuman(usr) && !issilicon(usr)) + return + set_max() + return + +/obj/machinery/atmospherics/binary/pump/AIAltClick() + set_max() + return ..() + +/obj/machinery/atmospherics/binary/pump/proc/toggle() + if(powered()) + on = !on + update_icon() + +/obj/machinery/atmospherics/binary/pump/proc/set_max() + if(powered()) + target_pressure = MAX_OUTPUT_PRESSURE + update_icon() + +/obj/machinery/atmospherics/binary/pump/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + return ..() + +/obj/machinery/atmospherics/binary/pump/on + icon_state = "map_on" + on = 1 + +/obj/machinery/atmospherics/binary/pump/update_icon() + ..() + + if(!powered()) + icon_state = "off" + else + icon_state = "[on ? "on" : "off"]" + +/obj/machinery/atmospherics/binary/pump/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + add_underlay(T, node1, turn(dir, -180)) + add_underlay(T, node2, dir) + +/obj/machinery/atmospherics/binary/pump/process_atmos() + ..() + if((stat & (NOPOWER|BROKEN)) || !on) + return 0 + + var/output_starting_pressure = air2.return_pressure() + + if( (target_pressure - output_starting_pressure) < 0.01) + //No need to pump gas if target is already reached! + return 1 + + //Calculate necessary moles to transfer using PV=nRT + if((air1.total_moles() > 0) && (air1.temperature>0)) + var/pressure_delta = target_pressure - output_starting_pressure + var/transfer_moles = pressure_delta*air2.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) + + //Actually transfer the gas + var/datum/gas_mixture/removed = air1.remove(transfer_moles) + air2.merge(removed) + + parent1.update = 1 + + parent2.update = 1 + return 1 + +//Radio remote control +/obj/machinery/atmospherics/binary/pump/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = SSradio.add_object(src, frequency, filter = RADIO_ATMOSIA) + +/obj/machinery/atmospherics/binary/pump/proc/broadcast_status() + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + + signal.data = list( + "tag" = id, + "device" = "AGP", + "power" = on, + "target_output" = target_pressure, + "sigtype" = "status" + ) + + radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA) + return 1 + +/obj/machinery/atmospherics/binary/pump/atmos_init() + ..() + if(frequency) + set_frequency(frequency) + +/obj/machinery/atmospherics/binary/pump/receive_signal(datum/signal/signal) + if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) + return 0 + + var/old_on = on //for logging + + if(signal.data["power"]) + on = text2num(signal.data["power"]) + + if(signal.data["power_toggle"]) + on = !on + + if(signal.data["set_output_pressure"]) + target_pressure = between( + 0, + text2num(signal.data["set_output_pressure"]), + ONE_ATMOSPHERE*50 + ) + + if(on != old_on) + investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos") + + if(signal.data["status"]) + spawn(2) + broadcast_status() + return //do not update_icon + + spawn(2) + broadcast_status() + update_icon() + return + +/obj/machinery/atmospherics/binary/pump/attack_hand(mob/user) + if(..()) + return + + if(!allowed(user)) + to_chat(user, "Access denied.") + return + + add_fingerprint(user) + ui_interact(user) + +/obj/machinery/atmospherics/binary/pump/attack_ghost(mob/user) + ui_interact(user) + +/obj/machinery/atmospherics/binary/pump/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) + user.set_machine(src) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "atmos_pump.tmpl", name, 385, 115, state = state) + ui.open() + +/obj/machinery/atmospherics/binary/pump/ui_data(mob/user) + var/list/data = list() + data["on"] = on + data["pressure"] = round(target_pressure) + data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) + return data + +/obj/machinery/atmospherics/binary/pump/Topic(href,href_list) + if(..()) + return 1 + + if(href_list["power"]) + on = !on + investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") + . = TRUE + if(href_list["pressure"]) + var/pressure = href_list["pressure"] + if(pressure == "max") + pressure = MAX_OUTPUT_PRESSURE + . = TRUE + else if(pressure == "input") + pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null + if(!isnull(pressure)) + . = TRUE + else if(text2num(pressure) != null) + pressure = text2num(pressure) + . = TRUE + if(.) + target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE) + investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") + + update_icon() + SSnanoui.update_uis(src) + +/obj/machinery/atmospherics/binary/pump/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/binary/pump/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/pen)) + var/t = copytext(stripped_input(user, "Enter the name for the pump.", "Rename", name), 1, MAX_NAME_LEN) + if(!t) + return + if(!in_range(src, usr) && loc != usr) + return + name = t + return + else if(!istype(W, /obj/item/wrench)) + return ..() + if(!(stat & NOPOWER) && on) + to_chat(user, "You cannot unwrench this [src], turn it off first.") + return 1 + return ..() diff --git a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm index 0947b2ee860..558e30d44cc 100644 --- a/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm +++ b/code/ATMOSPHERICS/components/binary_devices/volume_pump.dm @@ -1,257 +1,257 @@ -/* -Every cycle, the pump uses the air in air_in to try and make air_out the perfect pressure. - -node1, air1, network1 correspond to input -node2, air2, network2 correspond to output - -Thus, the two variables affect pump operation are set in New(): - air1.volume - This is the volume of gas available to the pump that may be transfered to the output - air2.volume - Higher quantities of this cause more air to be perfected later - but overall network volume is also increased as this increases... -*/ - -/obj/machinery/atmospherics/binary/volume_pump - icon = 'icons/atmos/volume_pump.dmi' - icon_state = "map_off" - - name = "volumetric gas pump" - desc = "A volumetric pump" - - can_unwrench = 1 - - var/on = 0 - var/transfer_rate = 200 - - var/frequency = 0 - var/id = null - var/datum/radio_frequency/radio_connection - -/obj/machinery/atmospherics/binary/volume_pump/CtrlClick(mob/living/user) - if(!istype(user) || user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!in_range(src, user) && !issilicon(usr)) - return - if(!ishuman(usr) && !issilicon(usr)) - return - toggle() - return ..() - -/obj/machinery/atmospherics/binary/volume_pump/AICtrlClick() - toggle() - return ..() - -/obj/machinery/atmospherics/binary/volume_pump/AltClick(mob/living/user) - if(!istype(user) || user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!in_range(src, user) && !issilicon(usr)) - return - if(!ishuman(usr) && !issilicon(usr)) - return - set_max() - return - -/obj/machinery/atmospherics/binary/volume_pump/AIAltClick() - set_max() - return ..() - -/obj/machinery/atmospherics/binary/volume_pump/proc/toggle() - if(powered()) - on = !on - update_icon() - -/obj/machinery/atmospherics/binary/volume_pump/proc/set_max() - if(powered()) - transfer_rate = MAX_TRANSFER_RATE - update_icon() - -/obj/machinery/atmospherics/binary/volume_pump/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - return ..() - -/obj/machinery/atmospherics/binary/volume_pump/on - on = 1 - icon_state = "map_on" - -/obj/machinery/atmospherics/binary/volume_pump/atmos_init() - ..() - set_frequency(frequency) - -/obj/machinery/atmospherics/binary/volume_pump/update_icon() - ..() - - if(!powered()) - icon_state = "off" - else - icon_state = "[on ? "on" : "off"]" - -/obj/machinery/atmospherics/binary/volume_pump/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - add_underlay(T, node1, turn(dir, -180)) - add_underlay(T, node2, dir) - -/obj/machinery/atmospherics/binary/volume_pump/process_atmos() - ..() - if((stat & (NOPOWER|BROKEN)) || !on) - return 0 - - // Pump mechanism just won't do anything if the pressure is too high/too low - var/input_starting_pressure = air1.return_pressure() - var/output_starting_pressure = air2.return_pressure() - - if((input_starting_pressure < 0.01) || (output_starting_pressure > 9000)) - return 1 - - var/transfer_ratio = max(1, transfer_rate/air1.volume) - - var/datum/gas_mixture/removed = air1.remove_ratio(transfer_ratio) - - air2.merge(removed) - - - parent1.update = 1 - parent2.update = 1 - - return 1 - -/obj/machinery/atmospherics/binary/volume_pump/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = SSradio.add_object(src, frequency) - -/obj/machinery/atmospherics/binary/volume_pump/proc/broadcast_status() - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - - signal.data = list( - "tag" = id, - "device" = "APV", - "power" = on, - "transfer_rate" = transfer_rate, - "sigtype" = "status" - ) - radio_connection.post_signal(src, signal) - - return 1 - -/obj/machinery/atmospherics/binary/volume_pump/receive_signal(datum/signal/signal) - if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) - return 0 - - var/old_on = on //for logging - - if(signal.data["power"]) - on = text2num(signal.data["power"]) - - if(signal.data["power_toggle"]) - on = !on - - if(signal.data["set_transfer_rate"]) - transfer_rate = between( - 0, - text2num(signal.data["set_transfer_rate"]), - air1.volume - ) - - if(on != old_on) - investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos") - - if(signal.data["status"]) - spawn(2) - broadcast_status() - return //do not update_icon - - spawn(2) - broadcast_status() - update_icon() - -/obj/machinery/atmospherics/binary/volume_pump/attack_hand(mob/user) - if(..()) - return - - if(!allowed(user)) - to_chat(user, "Access denied.") - return - - add_fingerprint(user) - ui_interact(user) - -/obj/machinery/atmospherics/binary/volume_pump/attack_ghost(mob/user) - ui_interact(user) - -/obj/machinery/atmospherics/binary/volume_pump/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) - user.set_machine(src) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "atmos_pump.tmpl", name, 310, 115, state = state) - ui.open() - -/obj/machinery/atmospherics/binary/volume_pump/ui_data(mob/user) - var/list/data = list() - data["on"] = on - data["rate"] = round(transfer_rate) - data["max_rate"] = round(MAX_TRANSFER_RATE) - return data - -/obj/machinery/atmospherics/binary/volume_pump/Topic(href,href_list) - if(..()) - return 1 - - if(href_list["power"]) - on = !on - investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") - . = TRUE - if(href_list["rate"]) - var/rate = href_list["rate"] - if(rate == "max") - rate = MAX_TRANSFER_RATE - . = TRUE - else if(rate == "input") - rate = input("New transfer rate (0-[MAX_TRANSFER_RATE] L/s):", name, transfer_rate) as num|null - if(!isnull(rate)) - . = TRUE - else if(text2num(rate) != null) - rate = text2num(rate) - . = TRUE - if(.) - transfer_rate = Clamp(rate, 0, MAX_TRANSFER_RATE) - investigate_log("was set to [transfer_rate] L/s by [key_name(usr)]", "atmos") - - update_icon() - SSnanoui.update_uis(src) - -/obj/machinery/atmospherics/binary/volume_pump/power_change() - var/old_stat = stat - ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/binary/volume_pump/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/pen)) - var/t = copytext(stripped_input(user, "Enter the name for the volume pump.", "Rename", name), 1, MAX_NAME_LEN) - if(!t) - return - if(!in_range(src, usr) && loc != usr) - return - name = t - return - else if(!istype(W, /obj/item/wrench)) - return ..() - if(!(stat & NOPOWER) && on) - to_chat(user, "You cannot unwrench this [src], turn it off first.") - return 1 - return ..() \ No newline at end of file +/* +Every cycle, the pump uses the air in air_in to try and make air_out the perfect pressure. + +node1, air1, network1 correspond to input +node2, air2, network2 correspond to output + +Thus, the two variables affect pump operation are set in New(): + air1.volume + This is the volume of gas available to the pump that may be transfered to the output + air2.volume + Higher quantities of this cause more air to be perfected later + but overall network volume is also increased as this increases... +*/ + +/obj/machinery/atmospherics/binary/volume_pump + icon = 'icons/atmos/volume_pump.dmi' + icon_state = "map_off" + + name = "volumetric gas pump" + desc = "A volumetric pump" + + can_unwrench = 1 + + var/on = 0 + var/transfer_rate = 200 + + var/frequency = 0 + var/id = null + var/datum/radio_frequency/radio_connection + +/obj/machinery/atmospherics/binary/volume_pump/CtrlClick(mob/living/user) + if(!istype(user) || user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!in_range(src, user) && !issilicon(usr)) + return + if(!ishuman(usr) && !issilicon(usr)) + return + toggle() + return ..() + +/obj/machinery/atmospherics/binary/volume_pump/AICtrlClick() + toggle() + return ..() + +/obj/machinery/atmospherics/binary/volume_pump/AltClick(mob/living/user) + if(!istype(user) || user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!in_range(src, user) && !issilicon(usr)) + return + if(!ishuman(usr) && !issilicon(usr)) + return + set_max() + return + +/obj/machinery/atmospherics/binary/volume_pump/AIAltClick() + set_max() + return ..() + +/obj/machinery/atmospherics/binary/volume_pump/proc/toggle() + if(powered()) + on = !on + update_icon() + +/obj/machinery/atmospherics/binary/volume_pump/proc/set_max() + if(powered()) + transfer_rate = MAX_TRANSFER_RATE + update_icon() + +/obj/machinery/atmospherics/binary/volume_pump/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + return ..() + +/obj/machinery/atmospherics/binary/volume_pump/on + on = 1 + icon_state = "map_on" + +/obj/machinery/atmospherics/binary/volume_pump/atmos_init() + ..() + set_frequency(frequency) + +/obj/machinery/atmospherics/binary/volume_pump/update_icon() + ..() + + if(!powered()) + icon_state = "off" + else + icon_state = "[on ? "on" : "off"]" + +/obj/machinery/atmospherics/binary/volume_pump/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + add_underlay(T, node1, turn(dir, -180)) + add_underlay(T, node2, dir) + +/obj/machinery/atmospherics/binary/volume_pump/process_atmos() + ..() + if((stat & (NOPOWER|BROKEN)) || !on) + return 0 + + // Pump mechanism just won't do anything if the pressure is too high/too low + var/input_starting_pressure = air1.return_pressure() + var/output_starting_pressure = air2.return_pressure() + + if((input_starting_pressure < 0.01) || (output_starting_pressure > 9000)) + return 1 + + var/transfer_ratio = max(1, transfer_rate/air1.volume) + + var/datum/gas_mixture/removed = air1.remove_ratio(transfer_ratio) + + air2.merge(removed) + + + parent1.update = 1 + parent2.update = 1 + + return 1 + +/obj/machinery/atmospherics/binary/volume_pump/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = SSradio.add_object(src, frequency) + +/obj/machinery/atmospherics/binary/volume_pump/proc/broadcast_status() + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + + signal.data = list( + "tag" = id, + "device" = "APV", + "power" = on, + "transfer_rate" = transfer_rate, + "sigtype" = "status" + ) + radio_connection.post_signal(src, signal) + + return 1 + +/obj/machinery/atmospherics/binary/volume_pump/receive_signal(datum/signal/signal) + if(!signal.data["tag"] || (signal.data["tag"] != id) || (signal.data["sigtype"]!="command")) + return 0 + + var/old_on = on //for logging + + if(signal.data["power"]) + on = text2num(signal.data["power"]) + + if(signal.data["power_toggle"]) + on = !on + + if(signal.data["set_transfer_rate"]) + transfer_rate = between( + 0, + text2num(signal.data["set_transfer_rate"]), + air1.volume + ) + + if(on != old_on) + investigate_log("was turned [on ? "on" : "off"] by a remote signal", "atmos") + + if(signal.data["status"]) + spawn(2) + broadcast_status() + return //do not update_icon + + spawn(2) + broadcast_status() + update_icon() + +/obj/machinery/atmospherics/binary/volume_pump/attack_hand(mob/user) + if(..()) + return + + if(!allowed(user)) + to_chat(user, "Access denied.") + return + + add_fingerprint(user) + ui_interact(user) + +/obj/machinery/atmospherics/binary/volume_pump/attack_ghost(mob/user) + ui_interact(user) + +/obj/machinery/atmospherics/binary/volume_pump/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) + user.set_machine(src) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "atmos_pump.tmpl", name, 310, 115, state = state) + ui.open() + +/obj/machinery/atmospherics/binary/volume_pump/ui_data(mob/user) + var/list/data = list() + data["on"] = on + data["rate"] = round(transfer_rate) + data["max_rate"] = round(MAX_TRANSFER_RATE) + return data + +/obj/machinery/atmospherics/binary/volume_pump/Topic(href,href_list) + if(..()) + return 1 + + if(href_list["power"]) + on = !on + investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") + . = TRUE + if(href_list["rate"]) + var/rate = href_list["rate"] + if(rate == "max") + rate = MAX_TRANSFER_RATE + . = TRUE + else if(rate == "input") + rate = input("New transfer rate (0-[MAX_TRANSFER_RATE] L/s):", name, transfer_rate) as num|null + if(!isnull(rate)) + . = TRUE + else if(text2num(rate) != null) + rate = text2num(rate) + . = TRUE + if(.) + transfer_rate = Clamp(rate, 0, MAX_TRANSFER_RATE) + investigate_log("was set to [transfer_rate] L/s by [key_name(usr)]", "atmos") + + update_icon() + SSnanoui.update_uis(src) + +/obj/machinery/atmospherics/binary/volume_pump/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/binary/volume_pump/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/pen)) + var/t = copytext(stripped_input(user, "Enter the name for the volume pump.", "Rename", name), 1, MAX_NAME_LEN) + if(!t) + return + if(!in_range(src, usr) && loc != usr) + return + name = t + return + else if(!istype(W, /obj/item/wrench)) + return ..() + if(!(stat & NOPOWER) && on) + to_chat(user, "You cannot unwrench this [src], turn it off first.") + return 1 + return ..() diff --git a/code/ATMOSPHERICS/components/omni_devices/_omni_extras.dm b/code/ATMOSPHERICS/components/omni_devices/_omni_extras.dm index a1f80916aee..d03329e24fc 100644 --- a/code/ATMOSPHERICS/components/omni_devices/_omni_extras.dm +++ b/code/ATMOSPHERICS/components/omni_devices/_omni_extras.dm @@ -91,4 +91,4 @@ return WEST else return 0 - \ No newline at end of file + diff --git a/code/ATMOSPHERICS/components/trinary_devices/filter.dm b/code/ATMOSPHERICS/components/trinary_devices/filter.dm index e45a8c3956f..647167800eb 100755 --- a/code/ATMOSPHERICS/components/trinary_devices/filter.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/filter.dm @@ -1,267 +1,267 @@ -/obj/machinery/atmospherics/trinary/filter - icon = 'icons/atmos/filter.dmi' - icon_state = "map" - - can_unwrench = 1 - - name = "gas filter" - - var/target_pressure = ONE_ATMOSPHERE - - var/filter_type = 0 -/* -Filter types: --1: Nothing - 0: Toxins: Toxins, Oxygen Agent B - 1: Oxygen: Oxygen ONLY - 2: Nitrogen: Nitrogen ONLY - 3: Carbon Dioxide: Carbon Dioxide ONLY - 4: Sleeping Agent (N2O) -*/ - - var/frequency = 0 - var/datum/radio_frequency/radio_connection - -/obj/machinery/atmospherics/trinary/filter/CtrlClick(mob/living/user) - if(!istype(user) || user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!in_range(src, user) && !issilicon(usr)) - return - if(!ishuman(usr) && !issilicon(usr)) - return - toggle() - return ..() - -/obj/machinery/atmospherics/trinary/filter/AICtrlClick() - toggle() - return ..() - -/obj/machinery/atmospherics/trinary/filter/AltClick(mob/living/user) - if(!istype(user) || user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!in_range(src, user) && !issilicon(usr)) - return - if(!ishuman(usr) && !issilicon(usr)) - return - set_max() - return - -/obj/machinery/atmospherics/trinary/filter/AIAltClick() - set_max() - return ..() - -/obj/machinery/atmospherics/trinary/filter/proc/toggle() - if(powered()) - on = !on - update_icon() - -/obj/machinery/atmospherics/trinary/filter/proc/set_max() - if(powered()) - target_pressure = MAX_OUTPUT_PRESSURE - update_icon() - -/obj/machinery/atmospherics/trinary/filter/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - return ..() - -/obj/machinery/atmospherics/trinary/filter/flipped - icon_state = "mmap" - flipped = 1 - -/obj/machinery/atmospherics/trinary/filter/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = SSradio.add_object(src, frequency, RADIO_ATMOSIA) - -/obj/machinery/atmospherics/trinary/filter/update_icon() - ..() - - if(flipped) - icon_state = "m" - else - icon_state = "" - - if(!powered()) - icon_state += "off" - else if(node2 && node3 && node1) - icon_state += on ? "on" : "off" - else - icon_state += "off" - on = 0 - -/obj/machinery/atmospherics/trinary/filter/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - - add_underlay(T, node1, turn(dir, -180)) - - if(flipped) - add_underlay(T, node2, turn(dir, 90)) - else - add_underlay(T, node2, turn(dir, -90)) - - add_underlay(T, node3, dir) - -/obj/machinery/atmospherics/trinary/filter/power_change() - var/old_stat = stat - ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/trinary/filter/process_atmos() - ..() - if(!on) - return 0 - - var/output_starting_pressure = air3.return_pressure() - - if(output_starting_pressure >= target_pressure || air2.return_pressure() >= target_pressure ) - //No need to mix if target is already full! - return 1 - - //Calculate necessary moles to transfer using PV=nRT - - var/pressure_delta = target_pressure - output_starting_pressure - var/transfer_moles - - if(air1.temperature > 0) - transfer_moles = pressure_delta*air3.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) - - //Actually transfer the gas - - if(transfer_moles > 0) - var/datum/gas_mixture/removed = air1.remove(transfer_moles) - - if(!removed) - return - var/datum/gas_mixture/filtered_out = new - filtered_out.temperature = removed.temperature - - switch(filter_type) - if(0) //removing hydrocarbons - filtered_out.toxins = removed.toxins - removed.toxins = 0 - - if(removed.trace_gases.len>0) - for(var/datum/gas/trace_gas in removed.trace_gases) - if(istype(trace_gas, /datum/gas/oxygen_agent_b)) - removed.trace_gases -= trace_gas - filtered_out.trace_gases += trace_gas - - if(1) //removing O2 - filtered_out.oxygen = removed.oxygen - removed.oxygen = 0 - - if(2) //removing N2 - filtered_out.nitrogen = removed.nitrogen - removed.nitrogen = 0 - - if(3) //removing CO2 - filtered_out.carbon_dioxide = removed.carbon_dioxide - removed.carbon_dioxide = 0 - - if(4)//removing N2O - if(removed.trace_gases.len>0) - for(var/datum/gas/trace_gas in removed.trace_gases) - if(istype(trace_gas, /datum/gas/sleeping_agent)) - removed.trace_gases -= trace_gas - filtered_out.trace_gases += trace_gas - - else - filtered_out = null - - - air2.merge(filtered_out) - air3.merge(removed) - - parent2.update = 1 - - parent3.update = 1 - - parent1.update = 1 - - return 1 - -/obj/machinery/atmospherics/trinary/filter/atmos_init() - set_frequency(frequency) - ..() - -/obj/machinery/atmospherics/trinary/filter/attack_ghost(mob/user) - ui_interact(user) - -/obj/machinery/atmospherics/trinary/filter/attack_hand(mob/user) - if(..()) - return - - if(!allowed(user)) - to_chat(user, "Access denied.") - return - - add_fingerprint(user) - ui_interact(user) - -/obj/machinery/atmospherics/trinary/filter/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) - user.set_machine(src) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "atmos_filter.tmpl", name, 475, 155, state = state) - ui.open() - -/obj/machinery/atmospherics/trinary/filter/ui_data(mob/user) - var/list/data = list() - data["on"] = on - data["pressure"] = round(target_pressure) - data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) - data["filter_type"] = filter_type - return data - -/obj/machinery/atmospherics/trinary/filter/Topic(href, href_list) // -- TLE - if(..()) - return 1 - - if(href_list["power"]) - on = !on - investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") - . = TRUE - if(href_list["pressure"]) - var/pressure = href_list["pressure"] - if(pressure == "max") - pressure = MAX_OUTPUT_PRESSURE - . = TRUE - else if(pressure == "input") - pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null - if(!isnull(pressure) && !..()) - . = TRUE - else if(text2num(pressure) != null) - pressure = text2num(pressure) - . = TRUE - if(.) - target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE) - investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") - if(href_list["filter"]) - filter_type = text2num(href_list["filter"]) - investigate_log("was set to filter [filter_type] by [key_name(usr)]", "atmos") - . = TRUE - - update_icon() - SSnanoui.update_uis(src) - -/obj/machinery/atmospherics/trinary/filter/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/pen)) - var/t = copytext(stripped_input(user, "Enter the name for the filter.", "Rename", name), 1, MAX_NAME_LEN) - if(!t) - return - if(!in_range(src, usr) && loc != usr) - return - name = t - return - else - return ..() \ No newline at end of file +/obj/machinery/atmospherics/trinary/filter + icon = 'icons/atmos/filter.dmi' + icon_state = "map" + + can_unwrench = 1 + + name = "gas filter" + + var/target_pressure = ONE_ATMOSPHERE + + var/filter_type = 0 +/* +Filter types: +-1: Nothing + 0: Toxins: Toxins, Oxygen Agent B + 1: Oxygen: Oxygen ONLY + 2: Nitrogen: Nitrogen ONLY + 3: Carbon Dioxide: Carbon Dioxide ONLY + 4: Sleeping Agent (N2O) +*/ + + var/frequency = 0 + var/datum/radio_frequency/radio_connection + +/obj/machinery/atmospherics/trinary/filter/CtrlClick(mob/living/user) + if(!istype(user) || user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!in_range(src, user) && !issilicon(usr)) + return + if(!ishuman(usr) && !issilicon(usr)) + return + toggle() + return ..() + +/obj/machinery/atmospherics/trinary/filter/AICtrlClick() + toggle() + return ..() + +/obj/machinery/atmospherics/trinary/filter/AltClick(mob/living/user) + if(!istype(user) || user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!in_range(src, user) && !issilicon(usr)) + return + if(!ishuman(usr) && !issilicon(usr)) + return + set_max() + return + +/obj/machinery/atmospherics/trinary/filter/AIAltClick() + set_max() + return ..() + +/obj/machinery/atmospherics/trinary/filter/proc/toggle() + if(powered()) + on = !on + update_icon() + +/obj/machinery/atmospherics/trinary/filter/proc/set_max() + if(powered()) + target_pressure = MAX_OUTPUT_PRESSURE + update_icon() + +/obj/machinery/atmospherics/trinary/filter/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + return ..() + +/obj/machinery/atmospherics/trinary/filter/flipped + icon_state = "mmap" + flipped = 1 + +/obj/machinery/atmospherics/trinary/filter/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = SSradio.add_object(src, frequency, RADIO_ATMOSIA) + +/obj/machinery/atmospherics/trinary/filter/update_icon() + ..() + + if(flipped) + icon_state = "m" + else + icon_state = "" + + if(!powered()) + icon_state += "off" + else if(node2 && node3 && node1) + icon_state += on ? "on" : "off" + else + icon_state += "off" + on = 0 + +/obj/machinery/atmospherics/trinary/filter/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + + add_underlay(T, node1, turn(dir, -180)) + + if(flipped) + add_underlay(T, node2, turn(dir, 90)) + else + add_underlay(T, node2, turn(dir, -90)) + + add_underlay(T, node3, dir) + +/obj/machinery/atmospherics/trinary/filter/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/trinary/filter/process_atmos() + ..() + if(!on) + return 0 + + var/output_starting_pressure = air3.return_pressure() + + if(output_starting_pressure >= target_pressure || air2.return_pressure() >= target_pressure ) + //No need to mix if target is already full! + return 1 + + //Calculate necessary moles to transfer using PV=nRT + + var/pressure_delta = target_pressure - output_starting_pressure + var/transfer_moles + + if(air1.temperature > 0) + transfer_moles = pressure_delta*air3.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) + + //Actually transfer the gas + + if(transfer_moles > 0) + var/datum/gas_mixture/removed = air1.remove(transfer_moles) + + if(!removed) + return + var/datum/gas_mixture/filtered_out = new + filtered_out.temperature = removed.temperature + + switch(filter_type) + if(0) //removing hydrocarbons + filtered_out.toxins = removed.toxins + removed.toxins = 0 + + if(removed.trace_gases.len>0) + for(var/datum/gas/trace_gas in removed.trace_gases) + if(istype(trace_gas, /datum/gas/oxygen_agent_b)) + removed.trace_gases -= trace_gas + filtered_out.trace_gases += trace_gas + + if(1) //removing O2 + filtered_out.oxygen = removed.oxygen + removed.oxygen = 0 + + if(2) //removing N2 + filtered_out.nitrogen = removed.nitrogen + removed.nitrogen = 0 + + if(3) //removing CO2 + filtered_out.carbon_dioxide = removed.carbon_dioxide + removed.carbon_dioxide = 0 + + if(4)//removing N2O + if(removed.trace_gases.len>0) + for(var/datum/gas/trace_gas in removed.trace_gases) + if(istype(trace_gas, /datum/gas/sleeping_agent)) + removed.trace_gases -= trace_gas + filtered_out.trace_gases += trace_gas + + else + filtered_out = null + + + air2.merge(filtered_out) + air3.merge(removed) + + parent2.update = 1 + + parent3.update = 1 + + parent1.update = 1 + + return 1 + +/obj/machinery/atmospherics/trinary/filter/atmos_init() + set_frequency(frequency) + ..() + +/obj/machinery/atmospherics/trinary/filter/attack_ghost(mob/user) + ui_interact(user) + +/obj/machinery/atmospherics/trinary/filter/attack_hand(mob/user) + if(..()) + return + + if(!allowed(user)) + to_chat(user, "Access denied.") + return + + add_fingerprint(user) + ui_interact(user) + +/obj/machinery/atmospherics/trinary/filter/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) + user.set_machine(src) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "atmos_filter.tmpl", name, 475, 155, state = state) + ui.open() + +/obj/machinery/atmospherics/trinary/filter/ui_data(mob/user) + var/list/data = list() + data["on"] = on + data["pressure"] = round(target_pressure) + data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) + data["filter_type"] = filter_type + return data + +/obj/machinery/atmospherics/trinary/filter/Topic(href, href_list) // -- TLE + if(..()) + return 1 + + if(href_list["power"]) + on = !on + investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") + . = TRUE + if(href_list["pressure"]) + var/pressure = href_list["pressure"] + if(pressure == "max") + pressure = MAX_OUTPUT_PRESSURE + . = TRUE + else if(pressure == "input") + pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null + if(!isnull(pressure) && !..()) + . = TRUE + else if(text2num(pressure) != null) + pressure = text2num(pressure) + . = TRUE + if(.) + target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE) + investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") + if(href_list["filter"]) + filter_type = text2num(href_list["filter"]) + investigate_log("was set to filter [filter_type] by [key_name(usr)]", "atmos") + . = TRUE + + update_icon() + SSnanoui.update_uis(src) + +/obj/machinery/atmospherics/trinary/filter/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/pen)) + var/t = copytext(stripped_input(user, "Enter the name for the filter.", "Rename", name), 1, MAX_NAME_LEN) + if(!t) + return + if(!in_range(src, usr) && loc != usr) + return + name = t + return + else + return ..() diff --git a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm index 19849582a0f..18f27fb9a65 100644 --- a/code/ATMOSPHERICS/components/trinary_devices/mixer.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/mixer.dm @@ -1,233 +1,233 @@ -/obj/machinery/atmospherics/trinary/mixer - icon = 'icons/atmos/mixer.dmi' - icon_state = "map" - - can_unwrench = 1 - - name = "gas mixer" - - var/target_pressure = ONE_ATMOSPHERE - var/node1_concentration = 0.5 - var/node2_concentration = 0.5 - - //node 3 is the outlet, nodes 1 & 2 are intakes - -/obj/machinery/atmospherics/trinary/mixer/CtrlClick(mob/living/user) - if(!istype(user) || user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!in_range(src, user) && !issilicon(usr)) - return - if(!ishuman(usr) && !issilicon(usr)) - return - toggle() - return ..() - -/obj/machinery/atmospherics/trinary/mixer/AICtrlClick() - toggle() - return ..() - -/obj/machinery/atmospherics/trinary/mixer/AltClick(mob/living/user) - if(!istype(user) || user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!in_range(src, user) && !issilicon(usr)) - return - if(!ishuman(usr) && !issilicon(usr)) - return - set_max() - return - -/obj/machinery/atmospherics/trinary/mixer/AIAltClick() - set_max() - return ..() - -/obj/machinery/atmospherics/trinary/mixer/flipped - icon_state = "mmap" - flipped = 1 - -/obj/machinery/atmospherics/trinary/mixer/proc/toggle() - if(powered()) - on = !on - update_icon() - -/obj/machinery/atmospherics/trinary/mixer/proc/set_max() - if(powered()) - target_pressure = MAX_OUTPUT_PRESSURE - update_icon() - -/obj/machinery/atmospherics/trinary/mixer/update_icon(safety = 0) - ..() - - if(flipped) - icon_state = "m" - else - icon_state = "" - - if(!powered()) - icon_state += "off" - else if(node2 && node3 && node1) - icon_state += on ? "on" : "off" - else - icon_state += "off" - on = 0 - -/obj/machinery/atmospherics/trinary/mixer/update_underlays() - if(..()) - underlays.Cut() - var/turf/T = get_turf(src) - if(!istype(T)) - return - - add_underlay(T, node1, turn(dir, -180)) - - if(flipped) - add_underlay(T, node2, turn(dir, 90)) - else - add_underlay(T, node2, turn(dir, -90)) - - add_underlay(T, node3, dir) - -/obj/machinery/atmospherics/trinary/mixer/power_change() - var/old_stat = stat - ..() - if(old_stat != stat) - update_icon() - -/obj/machinery/atmospherics/trinary/mixer/New() - ..() - air3.volume = 300 - -/obj/machinery/atmospherics/trinary/mixer/process_atmos() - ..() - if(!on) - return 0 - - var/output_starting_pressure = air3.return_pressure() - - if(output_starting_pressure >= target_pressure) - //No need to mix if target is already full! - return 1 - - //Calculate necessary moles to transfer using PV=nRT - - var/pressure_delta = target_pressure - output_starting_pressure - var/transfer_moles1 = 0 - var/transfer_moles2 = 0 - - if(air1.temperature > 0) - transfer_moles1 = (node1_concentration*pressure_delta)*air3.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) - - if(air2.temperature > 0) - transfer_moles2 = (node2_concentration*pressure_delta)*air3.volume/(air2.temperature * R_IDEAL_GAS_EQUATION) - - var/air1_moles = air1.total_moles() - var/air2_moles = air2.total_moles() - - if((air1_moles < transfer_moles1) || (air2_moles < transfer_moles2)) - if(!transfer_moles1 || !transfer_moles2) return - var/ratio = min(air1_moles/transfer_moles1, air2_moles/transfer_moles2) - - transfer_moles1 *= ratio - transfer_moles2 *= ratio - - //Actually transfer the gas - - if(transfer_moles1 > 0) - var/datum/gas_mixture/removed1 = air1.remove(transfer_moles1) - air3.merge(removed1) - - if(transfer_moles2 > 0) - var/datum/gas_mixture/removed2 = air2.remove(transfer_moles2) - air3.merge(removed2) - - if(transfer_moles1) - parent1.update = 1 - - if(transfer_moles2) - parent2.update = 1 - - parent3.update = 1 - - return 1 - -/obj/machinery/atmospherics/trinary/mixer/attack_ghost(mob/user) - ui_interact(user) - -/obj/machinery/atmospherics/trinary/mixer/attack_hand(mob/user) - if(..()) - return - - if(!allowed(user)) - to_chat(user, "Access denied.") - return - - add_fingerprint(user) - ui_interact(user) - -/obj/machinery/atmospherics/trinary/mixer/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) - user.set_machine(src) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "atmos_mixer.tmpl", name, 370, 165, state = state) - ui.open() - -/obj/machinery/atmospherics/trinary/mixer/ui_data(mob/user) - var/list/data = list() - data["on"] = on - data["pressure"] = round(target_pressure) - data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) - data["node1_concentration"] = round(node1_concentration*100) - data["node2_concentration"] = round(node2_concentration*100) - return data - -/obj/machinery/atmospherics/trinary/mixer/Topic(href,href_list) - if(..()) - return 1 - - if(href_list["power"]) - on = !on - investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") - . = TRUE - if(href_list["pressure"]) - var/pressure = href_list["pressure"] - if(pressure == "max") - pressure = MAX_OUTPUT_PRESSURE - . = TRUE - else if(pressure == "input") - pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null - if(!isnull(pressure) && !..()) - . = TRUE - else if(text2num(pressure) != null) - pressure = text2num(pressure) - . = TRUE - if(.) - target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE) - investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") - if(href_list["node1"]) - var/value = text2num(href_list["node1"]) - node1_concentration = max(0, min(1, node1_concentration + value)) - node2_concentration = max(0, min(1, node2_concentration - value)) - investigate_log("was set to [node1_concentration] % on node 1 by [key_name(usr)]", "atmos") - . = TRUE - if(href_list["node2"]) - var/value = text2num(href_list["node2"]) - node2_concentration = max(0, min(1, node2_concentration + value)) - node1_concentration = max(0, min(1, node1_concentration - value)) - investigate_log("was set to [node2_concentration] % on node 2 by [key_name(usr)]", "atmos") - . = TRUE - - update_icon() - SSnanoui.update_uis(src) - -/obj/machinery/atmospherics/trinary/mixer/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/pen)) - var/t = copytext(stripped_input(user, "Enter the name for the mixer.", "Rename", name), 1, MAX_NAME_LEN) - if(!t) - return - if(!in_range(src, usr) && loc != usr) - return - name = t - return - else - return ..() \ No newline at end of file +/obj/machinery/atmospherics/trinary/mixer + icon = 'icons/atmos/mixer.dmi' + icon_state = "map" + + can_unwrench = 1 + + name = "gas mixer" + + var/target_pressure = ONE_ATMOSPHERE + var/node1_concentration = 0.5 + var/node2_concentration = 0.5 + + //node 3 is the outlet, nodes 1 & 2 are intakes + +/obj/machinery/atmospherics/trinary/mixer/CtrlClick(mob/living/user) + if(!istype(user) || user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!in_range(src, user) && !issilicon(usr)) + return + if(!ishuman(usr) && !issilicon(usr)) + return + toggle() + return ..() + +/obj/machinery/atmospherics/trinary/mixer/AICtrlClick() + toggle() + return ..() + +/obj/machinery/atmospherics/trinary/mixer/AltClick(mob/living/user) + if(!istype(user) || user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!in_range(src, user) && !issilicon(usr)) + return + if(!ishuman(usr) && !issilicon(usr)) + return + set_max() + return + +/obj/machinery/atmospherics/trinary/mixer/AIAltClick() + set_max() + return ..() + +/obj/machinery/atmospherics/trinary/mixer/flipped + icon_state = "mmap" + flipped = 1 + +/obj/machinery/atmospherics/trinary/mixer/proc/toggle() + if(powered()) + on = !on + update_icon() + +/obj/machinery/atmospherics/trinary/mixer/proc/set_max() + if(powered()) + target_pressure = MAX_OUTPUT_PRESSURE + update_icon() + +/obj/machinery/atmospherics/trinary/mixer/update_icon(safety = 0) + ..() + + if(flipped) + icon_state = "m" + else + icon_state = "" + + if(!powered()) + icon_state += "off" + else if(node2 && node3 && node1) + icon_state += on ? "on" : "off" + else + icon_state += "off" + on = 0 + +/obj/machinery/atmospherics/trinary/mixer/update_underlays() + if(..()) + underlays.Cut() + var/turf/T = get_turf(src) + if(!istype(T)) + return + + add_underlay(T, node1, turn(dir, -180)) + + if(flipped) + add_underlay(T, node2, turn(dir, 90)) + else + add_underlay(T, node2, turn(dir, -90)) + + add_underlay(T, node3, dir) + +/obj/machinery/atmospherics/trinary/mixer/power_change() + var/old_stat = stat + ..() + if(old_stat != stat) + update_icon() + +/obj/machinery/atmospherics/trinary/mixer/New() + ..() + air3.volume = 300 + +/obj/machinery/atmospherics/trinary/mixer/process_atmos() + ..() + if(!on) + return 0 + + var/output_starting_pressure = air3.return_pressure() + + if(output_starting_pressure >= target_pressure) + //No need to mix if target is already full! + return 1 + + //Calculate necessary moles to transfer using PV=nRT + + var/pressure_delta = target_pressure - output_starting_pressure + var/transfer_moles1 = 0 + var/transfer_moles2 = 0 + + if(air1.temperature > 0) + transfer_moles1 = (node1_concentration*pressure_delta)*air3.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) + + if(air2.temperature > 0) + transfer_moles2 = (node2_concentration*pressure_delta)*air3.volume/(air2.temperature * R_IDEAL_GAS_EQUATION) + + var/air1_moles = air1.total_moles() + var/air2_moles = air2.total_moles() + + if((air1_moles < transfer_moles1) || (air2_moles < transfer_moles2)) + if(!transfer_moles1 || !transfer_moles2) return + var/ratio = min(air1_moles/transfer_moles1, air2_moles/transfer_moles2) + + transfer_moles1 *= ratio + transfer_moles2 *= ratio + + //Actually transfer the gas + + if(transfer_moles1 > 0) + var/datum/gas_mixture/removed1 = air1.remove(transfer_moles1) + air3.merge(removed1) + + if(transfer_moles2 > 0) + var/datum/gas_mixture/removed2 = air2.remove(transfer_moles2) + air3.merge(removed2) + + if(transfer_moles1) + parent1.update = 1 + + if(transfer_moles2) + parent2.update = 1 + + parent3.update = 1 + + return 1 + +/obj/machinery/atmospherics/trinary/mixer/attack_ghost(mob/user) + ui_interact(user) + +/obj/machinery/atmospherics/trinary/mixer/attack_hand(mob/user) + if(..()) + return + + if(!allowed(user)) + to_chat(user, "Access denied.") + return + + add_fingerprint(user) + ui_interact(user) + +/obj/machinery/atmospherics/trinary/mixer/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) + user.set_machine(src) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "atmos_mixer.tmpl", name, 370, 165, state = state) + ui.open() + +/obj/machinery/atmospherics/trinary/mixer/ui_data(mob/user) + var/list/data = list() + data["on"] = on + data["pressure"] = round(target_pressure) + data["max_pressure"] = round(MAX_OUTPUT_PRESSURE) + data["node1_concentration"] = round(node1_concentration*100) + data["node2_concentration"] = round(node2_concentration*100) + return data + +/obj/machinery/atmospherics/trinary/mixer/Topic(href,href_list) + if(..()) + return 1 + + if(href_list["power"]) + on = !on + investigate_log("was turned [on ? "on" : "off"] by [key_name(usr)]", "atmos") + . = TRUE + if(href_list["pressure"]) + var/pressure = href_list["pressure"] + if(pressure == "max") + pressure = MAX_OUTPUT_PRESSURE + . = TRUE + else if(pressure == "input") + pressure = input("New output pressure (0-[MAX_OUTPUT_PRESSURE] kPa):", name, target_pressure) as num|null + if(!isnull(pressure) && !..()) + . = TRUE + else if(text2num(pressure) != null) + pressure = text2num(pressure) + . = TRUE + if(.) + target_pressure = Clamp(pressure, 0, MAX_OUTPUT_PRESSURE) + investigate_log("was set to [target_pressure] kPa by [key_name(usr)]", "atmos") + if(href_list["node1"]) + var/value = text2num(href_list["node1"]) + node1_concentration = max(0, min(1, node1_concentration + value)) + node2_concentration = max(0, min(1, node2_concentration - value)) + investigate_log("was set to [node1_concentration] % on node 1 by [key_name(usr)]", "atmos") + . = TRUE + if(href_list["node2"]) + var/value = text2num(href_list["node2"]) + node2_concentration = max(0, min(1, node2_concentration + value)) + node1_concentration = max(0, min(1, node1_concentration - value)) + investigate_log("was set to [node2_concentration] % on node 2 by [key_name(usr)]", "atmos") + . = TRUE + + update_icon() + SSnanoui.update_uis(src) + +/obj/machinery/atmospherics/trinary/mixer/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/pen)) + var/t = copytext(stripped_input(user, "Enter the name for the mixer.", "Rename", name), 1, MAX_NAME_LEN) + if(!t) + return + if(!in_range(src, usr) && loc != usr) + return + name = t + return + else + return ..() diff --git a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm index 8087db9966e..79fae0f7d88 100644 --- a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm @@ -1,213 +1,213 @@ -/obj/machinery/atmospherics/trinary - dir = SOUTH - initialize_directions = SOUTH|NORTH|WEST - use_power = IDLE_POWER_USE - - var/on = 0 - layer = GAS_FILTER_LAYER - - var/datum/gas_mixture/air1 - var/datum/gas_mixture/air2 - var/datum/gas_mixture/air3 - - var/obj/machinery/atmospherics/node1 - var/obj/machinery/atmospherics/node2 - var/obj/machinery/atmospherics/node3 - - var/datum/pipeline/parent1 - var/datum/pipeline/parent2 - var/datum/pipeline/parent3 - - var/flipped = 0 - -/obj/machinery/atmospherics/trinary/New() - ..() - - if(!flipped) - switch(dir) - if(NORTH) - initialize_directions = EAST|NORTH|SOUTH - if(SOUTH) - initialize_directions = SOUTH|WEST|NORTH - if(EAST) - initialize_directions = EAST|WEST|SOUTH - if(WEST) - initialize_directions = WEST|NORTH|EAST - else - switch(dir) - if(NORTH) - initialize_directions = SOUTH|NORTH|WEST - if(SOUTH) - initialize_directions = NORTH|SOUTH|EAST - if(EAST) - initialize_directions = WEST|EAST|NORTH - if(WEST) - initialize_directions = EAST|WEST|SOUTH - - air1 = new - air2 = new - air3 = new - - air1.volume = 200 - air2.volume = 200 - air3.volume = 200 - -/obj/machinery/atmospherics/trinary/Destroy() - if(node1) - node1.disconnect(src) - node1 = null - nullifyPipenet(parent1) - if(node2) - node2.disconnect(src) - node2 = null - nullifyPipenet(parent2) - if(node3) - node3.disconnect(src) - node3 = null - nullifyPipenet(parent3) - return ..() - -/obj/machinery/atmospherics/trinary/atmos_init() - ..() - //Mixer: - //1 and 2 is input - //Node 3 is output - //If we flip the mixer, 1 and 3 shall exchange positions - - //Filter: - //Node 1 is input - //Node 2 is filtered output - //Node 3 is rest output - //If we flip the filter, 1 and 3 shall exchange positions - - var/node1_connect = turn(dir, -180) - var/node2_connect = turn(dir, -90) - var/node3_connect = dir - - if(flipped) - node2_connect = turn(node2_connect, -180) - - for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect)) - if(target.initialize_directions & get_dir(target,src)) - node1 = target - break - - for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect)) - if(target.initialize_directions & get_dir(target,src)) - node2 = target - break - - for(var/obj/machinery/atmospherics/target in get_step(src,node3_connect)) - if(target.initialize_directions & get_dir(target,src)) - node3 = target - break - - update_icon() - update_underlays() - -/obj/machinery/atmospherics/trinary/build_network(remove_deferral = FALSE) - if(!parent1) - parent1 = new /datum/pipeline() - parent1.build_pipeline(src) - - if(!parent2) - parent2 = new /datum/pipeline() - parent2.build_pipeline(src) - - if(!parent3) - parent3 = new /datum/pipeline() - parent3.build_pipeline(src) - ..() - -/obj/machinery/atmospherics/trinary/disconnect(obj/machinery/atmospherics/reference) - if(reference == node1) - if(istype(node1, /obj/machinery/atmospherics/pipe)) - qdel(parent1) - node1 = null - else if(reference == node2) - if(istype(node2, /obj/machinery/atmospherics/pipe)) - qdel(parent2) - node2 = null - else if(reference == node3) - if(istype(node3, /obj/machinery/atmospherics/pipe)) - qdel(parent3) - node3 = null - update_icon() - -/obj/machinery/atmospherics/trinary/nullifyPipenet(datum/pipeline/P) - ..() - if(!P) - return - if(P == parent1) - parent1.other_airs -= air1 - parent1 = null - else if(P == parent2) - parent2.other_airs -= air2 - parent2 = null - else if(P == parent3) - parent3.other_airs -= air3 - parent3 = null - -/obj/machinery/atmospherics/trinary/returnPipenetAir(datum/pipeline/P) - if(P == parent1) - return air1 - else if(P == parent2) - return air2 - else if(P == parent3) - return air3 - -/obj/machinery/atmospherics/trinary/pipeline_expansion(datum/pipeline/P) - if(P) - if(parent1 == P) - return list(node1) - else if(parent2 == P) - return list(node2) - else if(parent3 == P) - return list(node3) - return list(node1, node2, node3) - -/obj/machinery/atmospherics/trinary/setPipenet(datum/pipeline/P, obj/machinery/atmospherics/A) - if(A == node1) - parent1 = P - else if(A == node2) - parent2 = P - else if(A == node3) - parent3 = P - -/obj/machinery/atmospherics/trinary/returnPipenet(obj/machinery/atmospherics/A) - if(A == node1) - return parent1 - else if(A == node2) - return parent2 - else if(A == node3) - return parent3 - -/obj/machinery/atmospherics/trinary/replacePipenet(datum/pipeline/Old, datum/pipeline/New) - if(Old == parent1) - parent1 = New - else if(Old == parent2) - parent2 = New - else if(Old == parent3) - parent3 = New - -/obj/machinery/atmospherics/trinary/unsafe_pressure_release(var/mob/user,var/pressures) - ..() - - var/turf/T = get_turf(src) - if(T) - //Remove the gas from air1+air2+air3 and assume it - var/datum/gas_mixture/environment = T.return_air() - var/lost = pressures*environment.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) - lost += pressures*environment.volume/(air2.temperature * R_IDEAL_GAS_EQUATION) - lost += pressures*environment.volume/(air3.temperature * R_IDEAL_GAS_EQUATION) - var/shared_loss = lost/3 - - var/datum/gas_mixture/to_release = air1.remove(shared_loss) - to_release.merge(air2.remove(shared_loss)) - to_release.merge(air3.remove(shared_loss)) - T.assume_air(to_release) - air_update_turf(1) - -/obj/machinery/atmospherics/trinary/process_atmos() - ..() - return parent1 && parent2 && parent3 +/obj/machinery/atmospherics/trinary + dir = SOUTH + initialize_directions = SOUTH|NORTH|WEST + use_power = IDLE_POWER_USE + + var/on = 0 + layer = GAS_FILTER_LAYER + + var/datum/gas_mixture/air1 + var/datum/gas_mixture/air2 + var/datum/gas_mixture/air3 + + var/obj/machinery/atmospherics/node1 + var/obj/machinery/atmospherics/node2 + var/obj/machinery/atmospherics/node3 + + var/datum/pipeline/parent1 + var/datum/pipeline/parent2 + var/datum/pipeline/parent3 + + var/flipped = 0 + +/obj/machinery/atmospherics/trinary/New() + ..() + + if(!flipped) + switch(dir) + if(NORTH) + initialize_directions = EAST|NORTH|SOUTH + if(SOUTH) + initialize_directions = SOUTH|WEST|NORTH + if(EAST) + initialize_directions = EAST|WEST|SOUTH + if(WEST) + initialize_directions = WEST|NORTH|EAST + else + switch(dir) + if(NORTH) + initialize_directions = SOUTH|NORTH|WEST + if(SOUTH) + initialize_directions = NORTH|SOUTH|EAST + if(EAST) + initialize_directions = WEST|EAST|NORTH + if(WEST) + initialize_directions = EAST|WEST|SOUTH + + air1 = new + air2 = new + air3 = new + + air1.volume = 200 + air2.volume = 200 + air3.volume = 200 + +/obj/machinery/atmospherics/trinary/Destroy() + if(node1) + node1.disconnect(src) + node1 = null + nullifyPipenet(parent1) + if(node2) + node2.disconnect(src) + node2 = null + nullifyPipenet(parent2) + if(node3) + node3.disconnect(src) + node3 = null + nullifyPipenet(parent3) + return ..() + +/obj/machinery/atmospherics/trinary/atmos_init() + ..() + //Mixer: + //1 and 2 is input + //Node 3 is output + //If we flip the mixer, 1 and 3 shall exchange positions + + //Filter: + //Node 1 is input + //Node 2 is filtered output + //Node 3 is rest output + //If we flip the filter, 1 and 3 shall exchange positions + + var/node1_connect = turn(dir, -180) + var/node2_connect = turn(dir, -90) + var/node3_connect = dir + + if(flipped) + node2_connect = turn(node2_connect, -180) + + for(var/obj/machinery/atmospherics/target in get_step(src,node1_connect)) + if(target.initialize_directions & get_dir(target,src)) + node1 = target + break + + for(var/obj/machinery/atmospherics/target in get_step(src,node2_connect)) + if(target.initialize_directions & get_dir(target,src)) + node2 = target + break + + for(var/obj/machinery/atmospherics/target in get_step(src,node3_connect)) + if(target.initialize_directions & get_dir(target,src)) + node3 = target + break + + update_icon() + update_underlays() + +/obj/machinery/atmospherics/trinary/build_network(remove_deferral = FALSE) + if(!parent1) + parent1 = new /datum/pipeline() + parent1.build_pipeline(src) + + if(!parent2) + parent2 = new /datum/pipeline() + parent2.build_pipeline(src) + + if(!parent3) + parent3 = new /datum/pipeline() + parent3.build_pipeline(src) + ..() + +/obj/machinery/atmospherics/trinary/disconnect(obj/machinery/atmospherics/reference) + if(reference == node1) + if(istype(node1, /obj/machinery/atmospherics/pipe)) + qdel(parent1) + node1 = null + else if(reference == node2) + if(istype(node2, /obj/machinery/atmospherics/pipe)) + qdel(parent2) + node2 = null + else if(reference == node3) + if(istype(node3, /obj/machinery/atmospherics/pipe)) + qdel(parent3) + node3 = null + update_icon() + +/obj/machinery/atmospherics/trinary/nullifyPipenet(datum/pipeline/P) + ..() + if(!P) + return + if(P == parent1) + parent1.other_airs -= air1 + parent1 = null + else if(P == parent2) + parent2.other_airs -= air2 + parent2 = null + else if(P == parent3) + parent3.other_airs -= air3 + parent3 = null + +/obj/machinery/atmospherics/trinary/returnPipenetAir(datum/pipeline/P) + if(P == parent1) + return air1 + else if(P == parent2) + return air2 + else if(P == parent3) + return air3 + +/obj/machinery/atmospherics/trinary/pipeline_expansion(datum/pipeline/P) + if(P) + if(parent1 == P) + return list(node1) + else if(parent2 == P) + return list(node2) + else if(parent3 == P) + return list(node3) + return list(node1, node2, node3) + +/obj/machinery/atmospherics/trinary/setPipenet(datum/pipeline/P, obj/machinery/atmospherics/A) + if(A == node1) + parent1 = P + else if(A == node2) + parent2 = P + else if(A == node3) + parent3 = P + +/obj/machinery/atmospherics/trinary/returnPipenet(obj/machinery/atmospherics/A) + if(A == node1) + return parent1 + else if(A == node2) + return parent2 + else if(A == node3) + return parent3 + +/obj/machinery/atmospherics/trinary/replacePipenet(datum/pipeline/Old, datum/pipeline/New) + if(Old == parent1) + parent1 = New + else if(Old == parent2) + parent2 = New + else if(Old == parent3) + parent3 = New + +/obj/machinery/atmospherics/trinary/unsafe_pressure_release(var/mob/user,var/pressures) + ..() + + var/turf/T = get_turf(src) + if(T) + //Remove the gas from air1+air2+air3 and assume it + var/datum/gas_mixture/environment = T.return_air() + var/lost = pressures*environment.volume/(air1.temperature * R_IDEAL_GAS_EQUATION) + lost += pressures*environment.volume/(air2.temperature * R_IDEAL_GAS_EQUATION) + lost += pressures*environment.volume/(air3.temperature * R_IDEAL_GAS_EQUATION) + var/shared_loss = lost/3 + + var/datum/gas_mixture/to_release = air1.remove(shared_loss) + to_release.merge(air2.remove(shared_loss)) + to_release.merge(air3.remove(shared_loss)) + T.assume_air(to_release) + air_update_turf(1) + +/obj/machinery/atmospherics/trinary/process_atmos() + ..() + return parent1 && parent2 && parent3 diff --git a/code/ATMOSPHERICS/components/trinary_devices/tvalve.dm b/code/ATMOSPHERICS/components/trinary_devices/tvalve.dm index 7b3f8299339..31039a412d5 100644 --- a/code/ATMOSPHERICS/components/trinary_devices/tvalve.dm +++ b/code/ATMOSPHERICS/components/trinary_devices/tvalve.dm @@ -177,4 +177,4 @@ switch_side() #undef TVALVE_STATE_STRAIGHT -#undef TVALVE_STATE_SIDE \ No newline at end of file +#undef TVALVE_STATE_SIDE diff --git a/code/ATMOSPHERICS/components/unary_devices/oxygen_generator.dm b/code/ATMOSPHERICS/components/unary_devices/oxygen_generator.dm index a9c3b98ad74..9b06019e49e 100644 --- a/code/ATMOSPHERICS/components/unary_devices/oxygen_generator.dm +++ b/code/ATMOSPHERICS/components/unary_devices/oxygen_generator.dm @@ -45,4 +45,4 @@ parent.update = 1 - return 1 \ No newline at end of file + return 1 diff --git a/code/ATMOSPHERICS/components/unary_devices/tank.dm b/code/ATMOSPHERICS/components/unary_devices/tank.dm index cb82417033b..11a752b9030 100644 --- a/code/ATMOSPHERICS/components/unary_devices/tank.dm +++ b/code/ATMOSPHERICS/components/unary_devices/tank.dm @@ -110,4 +110,4 @@ var/datum/gas/oxygen_agent_b/trace_gas = new trace_gas.moles = (50 * ONE_ATMOSPHERE) * (air_contents.volume) / (R_IDEAL_GAS_EQUATION * air_contents.temperature) - air_contents.trace_gases += trace_gas \ No newline at end of file + air_contents.trace_gases += trace_gas diff --git a/code/ATMOSPHERICS/components/unary_devices/thermal_plate.dm b/code/ATMOSPHERICS/components/unary_devices/thermal_plate.dm index 5a6e76a182b..6cd050de022 100644 --- a/code/ATMOSPHERICS/components/unary_devices/thermal_plate.dm +++ b/code/ATMOSPHERICS/components/unary_devices/thermal_plate.dm @@ -77,4 +77,4 @@ parent.update = 1 - return 1 \ No newline at end of file + return 1 diff --git a/code/ATMOSPHERICS/components/unary_devices/unary_base.dm b/code/ATMOSPHERICS/components/unary_devices/unary_base.dm index 4ef7743fc8c..14c93dd0490 100644 --- a/code/ATMOSPHERICS/components/unary_devices/unary_base.dm +++ b/code/ATMOSPHERICS/components/unary_devices/unary_base.dm @@ -102,4 +102,4 @@ /obj/machinery/atmospherics/unary/process_atmos() ..() - return parent \ No newline at end of file + return parent diff --git a/code/ATMOSPHERICS/components/unary_devices/vent_pump.dm b/code/ATMOSPHERICS/components/unary_devices/vent_pump.dm index 1b920002227..afdca1d19ce 100644 --- a/code/ATMOSPHERICS/components/unary_devices/vent_pump.dm +++ b/code/ATMOSPHERICS/components/unary_devices/vent_pump.dm @@ -344,27 +344,6 @@ playsound(loc, 'sound/weapons/bladeslice.ogg', 100, TRUE) /obj/machinery/atmospherics/unary/vent_pump/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/weldingtool)) - var/obj/item/weldingtool/WT = W - if(WT.remove_fuel(0,user)) - to_chat(user, "Now welding the vent.") - if(do_after(user, 20 * WT.toolspeed, target = src)) - if(!src || !WT.isOn()) return - playsound(src.loc, WT.usesound, 50, 1) - if(!welded) - user.visible_message("[user] welds the vent shut.", "You weld the vent shut.", "You hear welding.") - welded = 1 - update_icon() - else - user.visible_message("[user] unwelds the vent.", "You unweld the vent.", "You hear welding.") - welded = 0 - update_icon() - else - to_chat(user, "The welding tool needs to be on to start this task.") - return 1 - else - to_chat(user, "You need more welding fuel to complete this task.") - return 1 if(istype(W, /obj/item/screwdriver)) if(!welded) if(open) @@ -400,6 +379,23 @@ return ..() +/obj/machinery/atmospherics/unary/vent_pump/welder_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_WELD_MESSAGE + if(I.use_tool(src, user, 20, volume = I.tool_volume)) + if(!welded) + welded = TRUE + visible_message("[user] welds [src] shut!",\ + "You weld [src] shut!") + else + welded = FALSE + visible_message("[user] unwelds [src]!",\ + "You unweld [src]!") + update_icon() + + /obj/machinery/atmospherics/unary/vent_pump/attack_hand() if(!welded) if(open) diff --git a/code/ATMOSPHERICS/components/unary_devices/vent_scrubber.dm b/code/ATMOSPHERICS/components/unary_devices/vent_scrubber.dm index 5fcf7817a30..d9aff36688b 100644 --- a/code/ATMOSPHERICS/components/unary_devices/vent_scrubber.dm +++ b/code/ATMOSPHERICS/components/unary_devices/vent_scrubber.dm @@ -393,27 +393,6 @@ playsound(loc, 'sound/weapons/bladeslice.ogg', 100, TRUE) /obj/machinery/atmospherics/unary/vent_scrubber/attackby(var/obj/item/W as obj, var/mob/user as mob, params) - if(istype(W, /obj/item/weldingtool)) - var/obj/item/weldingtool/WT = W - if(WT.remove_fuel(0,user)) - to_chat(user, "Now welding the scrubber.") - if(do_after(user, 20 * WT.toolspeed, target = src)) - if(!src || !WT.isOn()) return - playsound(get_turf(src), WT.usesound, 50, 1) - if(!welded) - user.visible_message("[user] welds the scrubber shut.", "You weld the vent scrubber.", "You hear welding.") - welded = 1 - update_icon() - else - user.visible_message("[user] unwelds the scrubber.", "You unweld the scrubber.", "You hear welding.") - welded = 0 - update_icon() - else - to_chat(user, "The welding tool needs to be on to start this task.") - return 1 - else - to_chat(user, "You need more welding fuel to complete this task.") - return 1 if(istype(W, /obj/item/multitool)) update_multitool_menu(user) return 1 @@ -423,3 +402,19 @@ return 1 return ..() + +/obj/machinery/atmospherics/unary/vent_scrubber/welder_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_WELD_MESSAGE + if(I.use_tool(src, user, 20, volume = I.tool_volume)) + if(!welded) + welded = TRUE + visible_message("[user] welds [src] shut!",\ + "You weld [src] shut!") + else + welded = FALSE + visible_message("[user] unwelds [src]!",\ + "You unweld [src]!") + update_icon() diff --git a/code/ATMOSPHERICS/datum_pipeline.dm b/code/ATMOSPHERICS/datum_pipeline.dm index 43082c38ded..4d849686851 100644 --- a/code/ATMOSPHERICS/datum_pipeline.dm +++ b/code/ATMOSPHERICS/datum_pipeline.dm @@ -1,276 +1,276 @@ -/datum/pipeline - var/datum/gas_mixture/air - var/list/datum/gas_mixture/other_airs = list() - - var/list/obj/machinery/atmospherics/pipe/members = list() - var/list/obj/machinery/atmospherics/other_atmosmch = list() - - var/update = 1 - - var/alert_pressure = 0 - -/datum/pipeline/New() - SSair.networks += src - -/datum/pipeline/Destroy() - SSair.networks -= src - if(air && air.volume) - temporarily_store_air() - for(var/obj/machinery/atmospherics/pipe/P in members) - P.parent = null - for(var/obj/machinery/atmospherics/A in other_atmosmch) - A.nullifyPipenet(src) - return ..() - -/datum/pipeline/process()//This use to be called called from the pipe networks - if(update) - update = 0 - reconcile_air() - return - -var/pipenetwarnings = 10 - -/datum/pipeline/proc/build_pipeline(obj/machinery/atmospherics/base) - var/volume = 0 - if(istype(base, /obj/machinery/atmospherics/pipe)) - var/obj/machinery/atmospherics/pipe/E = base - volume = E.volume - alert_pressure = E.alert_pressure - members += E - if(E.air_temporary) - air = E.air_temporary - E.air_temporary = null - else - addMachineryMember(base) - if(!air) - air = new - var/list/possible_expansions = list(base) - while(possible_expansions.len>0) - for(var/obj/machinery/atmospherics/borderline in possible_expansions) - - var/list/result = borderline.pipeline_expansion(src) - - if(result.len>0) - for(var/obj/machinery/atmospherics/P in result) - if(istype(P, /obj/machinery/atmospherics/pipe)) - var/obj/machinery/atmospherics/pipe/item = P - if(!members.Find(item)) - - if(item.parent) - log_runtime(EXCEPTION("[item.type] \[\ref[item]] added to a pipenet while still having one ([item.parent]) (pipes leading to the same spot stacking in one turf). Nearby: [item.x], [item.y], [item.z].")) - members += item - possible_expansions += item - - volume += item.volume - item.parent = src - - alert_pressure = min(alert_pressure, item.alert_pressure) - - if(item.air_temporary) - air.merge(item.air_temporary) - item.air_temporary = null - else - P.setPipenet(src, borderline) - addMachineryMember(P) - - possible_expansions -= borderline - - air.volume = volume - -/datum/pipeline/proc/addMachineryMember(obj/machinery/atmospherics/A) - other_atmosmch |= A - var/datum/gas_mixture/G = A.returnPipenetAir(src) - other_airs |= G - -/datum/pipeline/proc/addMember(obj/machinery/atmospherics/A, obj/machinery/atmospherics/N) - if(istype(A, /obj/machinery/atmospherics/pipe)) - var/obj/machinery/atmospherics/pipe/P = A - P.parent = src - var/list/adjacent = P.pipeline_expansion() - for(var/obj/machinery/atmospherics/pipe/I in adjacent) - if(I.parent == src) - continue - var/datum/pipeline/E = I.parent - merge(E) - if(!members.Find(P)) - members += P - air.volume += P.volume - else - A.setPipenet(src, N) - addMachineryMember(A) - -/datum/pipeline/proc/merge(datum/pipeline/E) - air.volume += E.air.volume - members.Add(E.members) - for(var/obj/machinery/atmospherics/pipe/S in E.members) - S.parent = src - air.merge(E.air) - for(var/obj/machinery/atmospherics/A in E.other_atmosmch) - A.replacePipenet(E, src) - other_atmosmch.Add(E.other_atmosmch) - other_airs.Add(E.other_airs) - E.members.Cut() - E.other_atmosmch.Cut() - qdel(E) - -/obj/machinery/atmospherics/proc/addMember(obj/machinery/atmospherics/A) - var/datum/pipeline/P = returnPipenet(A) - P.addMember(A, src) - -/obj/machinery/atmospherics/pipe/addMember(obj/machinery/atmospherics/A) - parent.addMember(A, src) - -/datum/pipeline/proc/temporarily_store_air() - //Update individual gas_mixtures by volume ratio - - for(var/obj/machinery/atmospherics/pipe/member in members) - member.air_temporary = new - member.air_temporary.volume = member.volume - - member.air_temporary.oxygen = air.oxygen*member.volume/air.volume - member.air_temporary.nitrogen = air.nitrogen*member.volume/air.volume - member.air_temporary.toxins = air.toxins*member.volume/air.volume - member.air_temporary.carbon_dioxide = air.carbon_dioxide*member.volume/air.volume - - member.air_temporary.temperature = air.temperature - - if(air.trace_gases.len) - for(var/datum/gas/trace_gas in air.trace_gases) - var/datum/gas/corresponding = new trace_gas.type() - member.air_temporary.trace_gases += corresponding - - corresponding.moles = trace_gas.moles*member.volume/air.volume - -/datum/pipeline/proc/temperature_interact(turf/target, share_volume, thermal_conductivity) - var/total_heat_capacity = air.heat_capacity() - var/partial_heat_capacity = total_heat_capacity*(share_volume/air.volume) - - if(istype(target, /turf/simulated)) - var/turf/simulated/modeled_location = target - - if(modeled_location.blocks_air) - - if((modeled_location.heat_capacity>0) && (partial_heat_capacity>0)) - var/delta_temperature = air.temperature - modeled_location.temperature - - var/heat = thermal_conductivity*delta_temperature* \ - (partial_heat_capacity*modeled_location.heat_capacity/(partial_heat_capacity+modeled_location.heat_capacity)) - - air.temperature -= heat/total_heat_capacity - modeled_location.temperature += heat/modeled_location.heat_capacity - - else - var/delta_temperature = 0 - var/sharer_heat_capacity = 0 - - delta_temperature = (air.temperature - modeled_location.air.temperature) - sharer_heat_capacity = modeled_location.air.heat_capacity() - - var/self_temperature_delta = 0 - var/sharer_temperature_delta = 0 - - if((sharer_heat_capacity>0) && (partial_heat_capacity>0)) - var/heat = thermal_conductivity*delta_temperature* \ - (partial_heat_capacity*sharer_heat_capacity/(partial_heat_capacity+sharer_heat_capacity)) - - self_temperature_delta = -heat/total_heat_capacity - sharer_temperature_delta = heat/sharer_heat_capacity - else - return 1 - - air.temperature += self_temperature_delta - - modeled_location.air.temperature += sharer_temperature_delta - - - else - if((target.heat_capacity>0) && (partial_heat_capacity>0)) - var/delta_temperature = air.temperature - target.temperature - - var/heat = thermal_conductivity*delta_temperature* \ - (partial_heat_capacity*target.heat_capacity/(partial_heat_capacity+target.heat_capacity)) - - air.temperature -= heat/total_heat_capacity - update = 1 - -/datum/pipeline/proc/reconcile_air() - var/list/datum/gas_mixture/GL = list() - var/list/datum/pipeline/PL = list() - PL += src - - for(var/i=1;i<=PL.len;i++) - var/datum/pipeline/P = PL[i] - if(!P) - return - GL += P.air - GL += P.other_airs - for(var/obj/machinery/atmospherics/binary/valve/V in P.other_atmosmch) - if(V.open) - PL |= V.parent1 - PL |= V.parent2 - for(var/obj/machinery/atmospherics/trinary/tvalve/T in P.other_atmosmch) - if(!T.state) - if(src != T.parent2) // otherwise dc'd side connects to both other sides! - PL |= T.parent1 - PL |= T.parent3 - else - if(src != T.parent3) - PL |= T.parent1 - PL |= T.parent2 - for(var/obj/machinery/atmospherics/unary/portables_connector/C in P.other_atmosmch) - if(C.connected_device) - GL += C.portableConnectorReturnAir() - - var/total_volume = 0 - var/total_thermal_energy = 0 - var/total_heat_capacity = 0 - var/total_oxygen = 0 - var/total_nitrogen = 0 - var/total_toxins = 0 - var/total_carbon_dioxide = 0 - var/list/total_trace_gases = list() - - for(var/datum/gas_mixture/G in GL) - total_volume += G.volume - total_thermal_energy += G.thermal_energy() - total_heat_capacity += G.heat_capacity() - - total_oxygen += G.oxygen - total_nitrogen += G.nitrogen - total_toxins += G.toxins - total_carbon_dioxide += G.carbon_dioxide - - if(G.trace_gases.len) - for(var/datum/gas/trace_gas in G.trace_gases) - var/datum/gas/corresponding = locate(trace_gas.type) in total_trace_gases - if(!corresponding) - corresponding = new trace_gas.type() - total_trace_gases += corresponding - - corresponding.moles += trace_gas.moles - - if(total_volume > 0) - - //Calculate temperature - var/temperature = 0 - - if(total_heat_capacity > 0) - temperature = total_thermal_energy/total_heat_capacity - - //Update individual gas_mixtures by volume ratio - for(var/datum/gas_mixture/G in GL) - G.oxygen = total_oxygen*G.volume/total_volume - G.nitrogen = total_nitrogen*G.volume/total_volume - G.toxins = total_toxins*G.volume/total_volume - G.carbon_dioxide = total_carbon_dioxide*G.volume/total_volume - - G.temperature = temperature - - if(total_trace_gases.len) - for(var/datum/gas/trace_gas in total_trace_gases) - var/datum/gas/corresponding = locate(trace_gas.type) in G.trace_gases - if(!corresponding) - corresponding = new trace_gas.type() - G.trace_gases += corresponding - - corresponding.moles = trace_gas.moles*G.volume/total_volume +/datum/pipeline + var/datum/gas_mixture/air + var/list/datum/gas_mixture/other_airs = list() + + var/list/obj/machinery/atmospherics/pipe/members = list() + var/list/obj/machinery/atmospherics/other_atmosmch = list() + + var/update = 1 + + var/alert_pressure = 0 + +/datum/pipeline/New() + SSair.networks += src + +/datum/pipeline/Destroy() + SSair.networks -= src + if(air && air.volume) + temporarily_store_air() + for(var/obj/machinery/atmospherics/pipe/P in members) + P.parent = null + for(var/obj/machinery/atmospherics/A in other_atmosmch) + A.nullifyPipenet(src) + return ..() + +/datum/pipeline/process()//This use to be called called from the pipe networks + if(update) + update = 0 + reconcile_air() + return + +var/pipenetwarnings = 10 + +/datum/pipeline/proc/build_pipeline(obj/machinery/atmospherics/base) + var/volume = 0 + if(istype(base, /obj/machinery/atmospherics/pipe)) + var/obj/machinery/atmospherics/pipe/E = base + volume = E.volume + alert_pressure = E.alert_pressure + members += E + if(E.air_temporary) + air = E.air_temporary + E.air_temporary = null + else + addMachineryMember(base) + if(!air) + air = new + var/list/possible_expansions = list(base) + while(possible_expansions.len>0) + for(var/obj/machinery/atmospherics/borderline in possible_expansions) + + var/list/result = borderline.pipeline_expansion(src) + + if(result.len>0) + for(var/obj/machinery/atmospherics/P in result) + if(istype(P, /obj/machinery/atmospherics/pipe)) + var/obj/machinery/atmospherics/pipe/item = P + if(!members.Find(item)) + + if(item.parent) + log_runtime(EXCEPTION("[item.type] \[\ref[item]] added to a pipenet while still having one ([item.parent]) (pipes leading to the same spot stacking in one turf). Nearby: [item.x], [item.y], [item.z].")) + members += item + possible_expansions += item + + volume += item.volume + item.parent = src + + alert_pressure = min(alert_pressure, item.alert_pressure) + + if(item.air_temporary) + air.merge(item.air_temporary) + item.air_temporary = null + else + P.setPipenet(src, borderline) + addMachineryMember(P) + + possible_expansions -= borderline + + air.volume = volume + +/datum/pipeline/proc/addMachineryMember(obj/machinery/atmospherics/A) + other_atmosmch |= A + var/datum/gas_mixture/G = A.returnPipenetAir(src) + other_airs |= G + +/datum/pipeline/proc/addMember(obj/machinery/atmospherics/A, obj/machinery/atmospherics/N) + if(istype(A, /obj/machinery/atmospherics/pipe)) + var/obj/machinery/atmospherics/pipe/P = A + P.parent = src + var/list/adjacent = P.pipeline_expansion() + for(var/obj/machinery/atmospherics/pipe/I in adjacent) + if(I.parent == src) + continue + var/datum/pipeline/E = I.parent + merge(E) + if(!members.Find(P)) + members += P + air.volume += P.volume + else + A.setPipenet(src, N) + addMachineryMember(A) + +/datum/pipeline/proc/merge(datum/pipeline/E) + air.volume += E.air.volume + members.Add(E.members) + for(var/obj/machinery/atmospherics/pipe/S in E.members) + S.parent = src + air.merge(E.air) + for(var/obj/machinery/atmospherics/A in E.other_atmosmch) + A.replacePipenet(E, src) + other_atmosmch.Add(E.other_atmosmch) + other_airs.Add(E.other_airs) + E.members.Cut() + E.other_atmosmch.Cut() + qdel(E) + +/obj/machinery/atmospherics/proc/addMember(obj/machinery/atmospherics/A) + var/datum/pipeline/P = returnPipenet(A) + P.addMember(A, src) + +/obj/machinery/atmospherics/pipe/addMember(obj/machinery/atmospherics/A) + parent.addMember(A, src) + +/datum/pipeline/proc/temporarily_store_air() + //Update individual gas_mixtures by volume ratio + + for(var/obj/machinery/atmospherics/pipe/member in members) + member.air_temporary = new + member.air_temporary.volume = member.volume + + member.air_temporary.oxygen = air.oxygen*member.volume/air.volume + member.air_temporary.nitrogen = air.nitrogen*member.volume/air.volume + member.air_temporary.toxins = air.toxins*member.volume/air.volume + member.air_temporary.carbon_dioxide = air.carbon_dioxide*member.volume/air.volume + + member.air_temporary.temperature = air.temperature + + if(air.trace_gases.len) + for(var/datum/gas/trace_gas in air.trace_gases) + var/datum/gas/corresponding = new trace_gas.type() + member.air_temporary.trace_gases += corresponding + + corresponding.moles = trace_gas.moles*member.volume/air.volume + +/datum/pipeline/proc/temperature_interact(turf/target, share_volume, thermal_conductivity) + var/total_heat_capacity = air.heat_capacity() + var/partial_heat_capacity = total_heat_capacity*(share_volume/air.volume) + + if(istype(target, /turf/simulated)) + var/turf/simulated/modeled_location = target + + if(modeled_location.blocks_air) + + if((modeled_location.heat_capacity>0) && (partial_heat_capacity>0)) + var/delta_temperature = air.temperature - modeled_location.temperature + + var/heat = thermal_conductivity*delta_temperature* \ + (partial_heat_capacity*modeled_location.heat_capacity/(partial_heat_capacity+modeled_location.heat_capacity)) + + air.temperature -= heat/total_heat_capacity + modeled_location.temperature += heat/modeled_location.heat_capacity + + else + var/delta_temperature = 0 + var/sharer_heat_capacity = 0 + + delta_temperature = (air.temperature - modeled_location.air.temperature) + sharer_heat_capacity = modeled_location.air.heat_capacity() + + var/self_temperature_delta = 0 + var/sharer_temperature_delta = 0 + + if((sharer_heat_capacity>0) && (partial_heat_capacity>0)) + var/heat = thermal_conductivity*delta_temperature* \ + (partial_heat_capacity*sharer_heat_capacity/(partial_heat_capacity+sharer_heat_capacity)) + + self_temperature_delta = -heat/total_heat_capacity + sharer_temperature_delta = heat/sharer_heat_capacity + else + return 1 + + air.temperature += self_temperature_delta + + modeled_location.air.temperature += sharer_temperature_delta + + + else + if((target.heat_capacity>0) && (partial_heat_capacity>0)) + var/delta_temperature = air.temperature - target.temperature + + var/heat = thermal_conductivity*delta_temperature* \ + (partial_heat_capacity*target.heat_capacity/(partial_heat_capacity+target.heat_capacity)) + + air.temperature -= heat/total_heat_capacity + update = 1 + +/datum/pipeline/proc/reconcile_air() + var/list/datum/gas_mixture/GL = list() + var/list/datum/pipeline/PL = list() + PL += src + + for(var/i=1;i<=PL.len;i++) + var/datum/pipeline/P = PL[i] + if(!P) + return + GL += P.air + GL += P.other_airs + for(var/obj/machinery/atmospherics/binary/valve/V in P.other_atmosmch) + if(V.open) + PL |= V.parent1 + PL |= V.parent2 + for(var/obj/machinery/atmospherics/trinary/tvalve/T in P.other_atmosmch) + if(!T.state) + if(src != T.parent2) // otherwise dc'd side connects to both other sides! + PL |= T.parent1 + PL |= T.parent3 + else + if(src != T.parent3) + PL |= T.parent1 + PL |= T.parent2 + for(var/obj/machinery/atmospherics/unary/portables_connector/C in P.other_atmosmch) + if(C.connected_device) + GL += C.portableConnectorReturnAir() + + var/total_volume = 0 + var/total_thermal_energy = 0 + var/total_heat_capacity = 0 + var/total_oxygen = 0 + var/total_nitrogen = 0 + var/total_toxins = 0 + var/total_carbon_dioxide = 0 + var/list/total_trace_gases = list() + + for(var/datum/gas_mixture/G in GL) + total_volume += G.volume + total_thermal_energy += G.thermal_energy() + total_heat_capacity += G.heat_capacity() + + total_oxygen += G.oxygen + total_nitrogen += G.nitrogen + total_toxins += G.toxins + total_carbon_dioxide += G.carbon_dioxide + + if(G.trace_gases.len) + for(var/datum/gas/trace_gas in G.trace_gases) + var/datum/gas/corresponding = locate(trace_gas.type) in total_trace_gases + if(!corresponding) + corresponding = new trace_gas.type() + total_trace_gases += corresponding + + corresponding.moles += trace_gas.moles + + if(total_volume > 0) + + //Calculate temperature + var/temperature = 0 + + if(total_heat_capacity > 0) + temperature = total_thermal_energy/total_heat_capacity + + //Update individual gas_mixtures by volume ratio + for(var/datum/gas_mixture/G in GL) + G.oxygen = total_oxygen*G.volume/total_volume + G.nitrogen = total_nitrogen*G.volume/total_volume + G.toxins = total_toxins*G.volume/total_volume + G.carbon_dioxide = total_carbon_dioxide*G.volume/total_volume + + G.temperature = temperature + + if(total_trace_gases.len) + for(var/datum/gas/trace_gas in total_trace_gases) + var/datum/gas/corresponding = locate(trace_gas.type) in G.trace_gases + if(!corresponding) + corresponding = new trace_gas.type() + G.trace_gases += corresponding + + corresponding.moles = trace_gas.moles*G.volume/total_volume diff --git a/code/ATMOSPHERICS/pipes/cap.dm b/code/ATMOSPHERICS/pipes/cap.dm index 8421685d44c..15b53d83e9c 100644 --- a/code/ATMOSPHERICS/pipes/cap.dm +++ b/code/ATMOSPHERICS/pipes/cap.dm @@ -123,4 +123,4 @@ connect_types = list(2) layer = 2.39 icon_connect_type = "-supply" - color = PIPE_COLOR_BLUE \ No newline at end of file + color = PIPE_COLOR_BLUE diff --git a/code/ATMOSPHERICS/pipes/simple/pipe_simple_hidden.dm b/code/ATMOSPHERICS/pipes/simple/pipe_simple_hidden.dm index db982639c2f..e0bd78bc2de 100644 --- a/code/ATMOSPHERICS/pipes/simple/pipe_simple_hidden.dm +++ b/code/ATMOSPHERICS/pipes/simple/pipe_simple_hidden.dm @@ -68,4 +68,4 @@ color = PIPE_COLOR_GREEN /obj/machinery/atmospherics/pipe/simple/hidden/purple - color = PIPE_COLOR_PURPLE \ No newline at end of file + color = PIPE_COLOR_PURPLE diff --git a/code/ATMOSPHERICS/pipes/simple/pipe_simple_insulated.dm b/code/ATMOSPHERICS/pipes/simple/pipe_simple_insulated.dm index ce6ff78b2d0..e7e5ec55e90 100644 --- a/code/ATMOSPHERICS/pipes/simple/pipe_simple_insulated.dm +++ b/code/ATMOSPHERICS/pipes/simple/pipe_simple_insulated.dm @@ -8,4 +8,4 @@ fatigue_pressure = 900*ONE_ATMOSPHERE alert_pressure = 900*ONE_ATMOSPHERE - level = 2 \ No newline at end of file + level = 2 diff --git a/code/ATMOSPHERICS/pipes/simple/pipe_simple_visible.dm b/code/ATMOSPHERICS/pipes/simple/pipe_simple_visible.dm index 1ca4da646ac..410c992c74c 100644 --- a/code/ATMOSPHERICS/pipes/simple/pipe_simple_visible.dm +++ b/code/ATMOSPHERICS/pipes/simple/pipe_simple_visible.dm @@ -65,4 +65,4 @@ /obj/machinery/atmospherics/pipe/simple/visible/universal/update_underlays() ..() - update_icon() \ No newline at end of file + update_icon() diff --git a/code/LINDA/LINDA_fire.dm b/code/LINDA/LINDA_fire.dm index 9467f7a3742..2fbfb1dff76 100644 --- a/code/LINDA/LINDA_fire.dm +++ b/code/LINDA/LINDA_fire.dm @@ -338,4 +338,4 @@ if(prob(chance) || bypass_rng) T.visible_message("[T] melts!") T.burn_down() - return affected \ No newline at end of file + return affected diff --git a/code/LINDA/LINDA_system.dm b/code/LINDA/LINDA_system.dm index c9d168a0159..fd24dcc0c77 100644 --- a/code/LINDA/LINDA_system.dm +++ b/code/LINDA/LINDA_system.dm @@ -142,44 +142,33 @@ turf/CanPass(atom/movable/mover, turf/target, height=1.5) return T.atmos_spawn_air(text, amount) -var/const/SPAWN_HEAT = 1 -var/const/SPAWN_20C = 2 -var/const/SPAWN_TOXINS = 4 -var/const/SPAWN_OXYGEN = 8 -var/const/SPAWN_CO2 = 16 -var/const/SPAWN_NITROGEN = 32 - -var/const/SPAWN_N2O = 64 - -var/const/SPAWN_AIR = 256 - /turf/simulated/proc/atmos_spawn_air(var/flag, var/amount) if(!text || !amount || !air) return var/datum/gas_mixture/G = new - if(flag & SPAWN_20C) + if(flag & LINDA_SPAWN_20C) G.temperature = T20C - if(flag & SPAWN_HEAT) + if(flag & LINDA_SPAWN_HEAT) G.temperature += 1000 - if(flag & SPAWN_TOXINS) + if(flag & LINDA_SPAWN_TOXINS) G.toxins += amount - if(flag & SPAWN_OXYGEN) + if(flag & LINDA_SPAWN_OXYGEN) G.oxygen += amount - if(flag & SPAWN_CO2) + if(flag & LINDA_SPAWN_CO2) G.carbon_dioxide += amount - if(flag & SPAWN_NITROGEN) + if(flag & LINDA_SPAWN_NITROGEN) G.nitrogen += amount - if(flag & SPAWN_N2O) + if(flag & LINDA_SPAWN_N2O) var/datum/gas/sleeping_agent/T = new T.moles += amount G.trace_gases += T - if(flag & SPAWN_AIR) + if(flag & LINDA_SPAWN_AIR) G.oxygen += MOLES_O2STANDARD * amount G.nitrogen += MOLES_N2STANDARD * amount diff --git a/code/LINDA/LINDA_turf_tile.dm b/code/LINDA/LINDA_turf_tile.dm index a0a0b6b8784..b86ad2299ff 100644 --- a/code/LINDA/LINDA_turf_tile.dm +++ b/code/LINDA/LINDA_turf_tile.dm @@ -486,7 +486,7 @@ turf/simulated/proc/consider_superconductivity(starting) turf/simulated/proc/radiate_to_spess() //Radiate excess tile heat to space if(temperature > T0C) //Considering 0 degC as te break even point for radiation in and out - var/delta_temperature = (temperature_archived - 2.7) //hardcoded space temperature + var/delta_temperature = (temperature_archived - TCMB) //hardcoded space temperature if((heat_capacity > 0) && (abs(delta_temperature) > MINIMUM_TEMPERATURE_DELTA_TO_CONSIDER)) var/heat = thermal_conductivity*delta_temperature* \ diff --git a/code/__DEFINES/_readme.dm b/code/__DEFINES/_readme.dm index 42ad52286fe..8bf6ada6477 100644 --- a/code/__DEFINES/_readme.dm +++ b/code/__DEFINES/_readme.dm @@ -11,4 +11,4 @@ and most importantly, how to undo your changes if you screw it up. - Sayu -*/ \ No newline at end of file +*/ diff --git a/code/__DEFINES/_tick.dm b/code/__DEFINES/_tick.dm index 666971e7209..10b815e547c 100644 --- a/code/__DEFINES/_tick.dm +++ b/code/__DEFINES/_tick.dm @@ -10,4 +10,4 @@ #define CHECK_TICK ( TICK_CHECK ? stoplag() : 0 ) #define TICK_CHECK_HIGH_PRIORITY ( TICK_USAGE > 95 ) -#define CHECK_TICK_HIGH_PRIORITY ( TICK_CHECK_HIGH_PRIORITY? stoplag() : 0 ) \ No newline at end of file +#define CHECK_TICK_HIGH_PRIORITY ( TICK_CHECK_HIGH_PRIORITY? stoplag() : 0 ) diff --git a/code/__DEFINES/access.dm b/code/__DEFINES/access.dm new file mode 100644 index 00000000000..ea319971a1b --- /dev/null +++ b/code/__DEFINES/access.dm @@ -0,0 +1,111 @@ +#define ACCESS_SECURITY 1 // Security equipment +#define ACCESS_BRIG 2 // Brig timers and permabrig +#define ACCESS_ARMORY 3 +#define ACCESS_FORENSICS_LOCKERS 4 +#define ACCESS_MEDICAL 5 +#define ACCESS_MORGUE 6 +#define ACCESS_TOX 7 +#define ACCESS_TOX_STORAGE 8 +#define ACCESS_GENETICS 9 +#define ACCESS_ENGINE 10 +#define ACCESS_ENGINE_EQUIP 11 +#define ACCESS_MAINT_TUNNELS 12 +#define ACCESS_EXTERNAL_AIRLOCKS 13 +#define ACCESS_EMERGENCY_STORAGE 14 +#define ACCESS_CHANGE_IDS 15 +#define ACCESS_AI_UPLOAD 16 +#define ACCESS_TELEPORTER 17 +#define ACCESS_EVA 18 +#define ACCESS_HEADS 19 +#define ACCESS_CAPTAIN 20 +#define ACCESS_ALL_PERSONAL_LOCKERS 21 +#define ACCESS_CHAPEL_OFFICE 22 +#define ACCESS_TECH_STORAGE 23 +#define ACCESS_ATMOSPHERICS 24 +#define ACCESS_BAR 25 +#define ACCESS_JANITOR 26 +#define ACCESS_CREMATORIUM 27 +#define ACCESS_KITCHEN 28 +#define ACCESS_ROBOTICS 29 +#define ACCESS_RD 30 +#define ACCESS_CARGO 31 +#define ACCESS_CONSTRUCTION 32 +#define ACCESS_CHEMISTRY 33 +#define ACCESS_CARGO_BOT 34 +#define ACCESS_HYDROPONICS 35 +#define ACCESS_MANUFACTURING 36 +#define ACCESS_LIBRARY 37 +#define ACCESS_LAWYER 38 +#define ACCESS_VIROLOGY 39 +#define ACCESS_CMO 40 +#define ACCESS_QM 41 +#define ACCESS_COURT 42 +#define ACCESS_CLOWN 43 +#define ACCESS_MIME 44 +#define ACCESS_SURGERY 45 +#define ACCESS_THEATRE 46 +#define ACCESS_RESEARCH 47 +#define ACCESS_MINING 48 +#define ACCESS_MINING_OFFICE 49 //not in use +#define ACCESS_MAILSORTING 50 +#define ACCESS_MINT 51 +#define ACCESS_MINT_VAULT 52 +#define ACCESS_HEADS_VAULT 53 +#define ACCESS_MINING_STATION 54 +#define ACCESS_XENOBIOLOGY 55 +#define ACCESS_CE 56 +#define ACCESS_HOP 57 +#define ACCESS_HOS 58 +#define ACCESS_RC_ANNOUNCE 59 //Request console announcements +#define ACCESS_KEYCARD_AUTH 60 //Used for events which require at least two people to confirm them +#define ACCESS_TCOMSAT 61 // has access to the entire telecomms satellite / machinery +#define ACCESS_GATEWAY 62 +#define ACCESS_SEC_DOORS 63 // Security front doors +#define ACCESS_PSYCHIATRIST 64 // Psychiatrist's office +#define ACCESS_XENOARCH 65 +#define ACCESS_PARAMEDIC 66 +#define ACCESS_BLUESHIELD 67 +#define ACCESS_SALVAGE_CAPTAIN 69 // Salvage ship captain's quarters +#define ACCESS_MECHANIC 70 +#define ACCESS_PILOT 71 +#define ACCESS_NTREP 73 +#define ACCESS_MAGISTRATE 74 +#define ACCESS_MINISAT 75 +#define ACCESS_MINERAL_STOREROOM 76 +#define ACCESS_NETWORK 77 + +#define ACCESS_WEAPONS 99 //Weapon authorization for secbots + + //BEGIN CENTCOM ACCESS +#define ACCESS_CENT_GENERAL 101//General facilities. +#define ACCESS_CENT_LIVING 102//Living quarters. +#define ACCESS_CENT_MEDICAL 103//Medical. +#define ACCESS_CENT_SECURITY 104//Security. +#define ACCESS_CENT_STORAGE 105//Storage areas. +#define ACCESS_CENT_SHUTTLES 106//Shuttle docks. +#define ACCESS_CENT_TELECOMMS 107//Telecomms. +#define ACCESS_CENT_TELEPORTER 108//Teleporter +#define ACCESS_CENT_SPECOPS 109//Special Ops. +#define ACCESS_CENT_SPECOPS_COMMANDER 110//Special Ops Commander. +#define ACCESS_CENT_BLACKOPS 111//Black Ops. +#define ACCESS_CENT_THUNDER 112//Thunderdome. +#define ACCESS_CENT_BRIDGE 113//Bridge. +#define ACCESS_CENT_COMMANDER 114//Commander's Office/ID computer. + +//The Syndicate +#define ACCESS_SYNDICATE 150//General Syndicate Access +#define ACCESS_SYNDICATE_LEADER 151//Nuke Op Leader Access +#define ACCESS_VOX 152//Vox Access +#define ACCESS_SYNDICATE_COMMAND 153//Admin syndi officer + +//Trade Stations +#define ACCESS_TRADE_SOL 160 + +//MONEY +#define ACCESS_CRATE_CASH 200 + +//Awaymissions +#define ACCESS_AWAY01 271 + +//Ghost roles +#define ACCESS_FREE_GOLEMS 300 diff --git a/code/__DEFINES/admin.dm b/code/__DEFINES/admin.dm index 65f4da2b50a..bbf394c6c0b 100644 --- a/code/__DEFINES/admin.dm +++ b/code/__DEFINES/admin.dm @@ -70,4 +70,4 @@ ///Max amount of keypress messages per second over two seconds before client is autokicked #define MAX_KEYPRESS_AUTOKICK 50 ///Length of held key rolling buffer -#define HELD_KEY_BUFFER_LENGTH 15 \ No newline at end of file +#define HELD_KEY_BUFFER_LENGTH 15 diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm index e69de29bb2d..8b137891791 100644 --- a/code/__DEFINES/antagonists.dm +++ b/code/__DEFINES/antagonists.dm @@ -0,0 +1 @@ + diff --git a/code/__DEFINES/atmospherics.dm b/code/__DEFINES/atmospherics.dm index 2f296960622..de20ccd1a4c 100644 --- a/code/__DEFINES/atmospherics.dm +++ b/code/__DEFINES/atmospherics.dm @@ -131,4 +131,4 @@ #define ATMOS_ALARM_DANGER 2 //LAVALAND -#define LAVALAND_EQUIPMENT_EFFECT_PRESSURE 50 //what pressure you have to be under to increase the effect of equipment meant for lavaland \ No newline at end of file +#define LAVALAND_EQUIPMENT_EFFECT_PRESSURE 50 //what pressure you have to be under to increase the effect of equipment meant for lavaland diff --git a/code/__DEFINES/bots.dm b/code/__DEFINES/bots.dm index 75bf8a0a553..c283445d0ad 100644 --- a/code/__DEFINES/bots.dm +++ b/code/__DEFINES/bots.dm @@ -38,4 +38,4 @@ #define SENTIENCE_ARTIFICIAL 2 #define SENTIENCE_OTHER 3 #define SENTIENCE_MINEBOT 4 -#define SENTIENCE_BOSS 5 \ No newline at end of file +#define SENTIENCE_BOSS 5 diff --git a/code/__DEFINES/callbacks.dm b/code/__DEFINES/callbacks.dm index 26c81f8b143..18059a60839 100644 --- a/code/__DEFINES/callbacks.dm +++ b/code/__DEFINES/callbacks.dm @@ -1,4 +1,4 @@ #define GLOBAL_PROC "some_magic_bullshit" #define CALLBACK new /datum/callback -#define INVOKE_ASYNC ImmediateInvokeAsync \ No newline at end of file +#define INVOKE_ASYNC ImmediateInvokeAsync diff --git a/code/__DEFINES/clothing.dm b/code/__DEFINES/clothing.dm index c44c65397dd..c39ce0947de 100644 --- a/code/__DEFINES/clothing.dm +++ b/code/__DEFINES/clothing.dm @@ -97,4 +97,4 @@ //flags for muzzle speech blocking #define MUZZLE_MUTE_NONE 0 // Does not mute you. #define MUZZLE_MUTE_MUFFLE 1 // Muffles everything you say "MHHPHHMMM!!! -#define MUZZLE_MUTE_ALL 2 // Completely mutes you. \ No newline at end of file +#define MUZZLE_MUTE_ALL 2 // Completely mutes you. diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm index df957eaa11e..c9691c195b8 100644 --- a/code/__DEFINES/components.dm +++ b/code/__DEFINES/components.dm @@ -107,6 +107,7 @@ #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_CROSSED_MOVABLE "crossed_movable" //when we cross over something (calling Crossed() on that atom) #define COMSIG_MOVABLE_UNCROSSED "movable_uncrossed" //from base of atom/movable/Uncrossed(): (/atom/movable) #define COMSIG_MOVABLE_UNCROSS "movable_uncross" //from base of atom/movable/Uncross(): (/atom/movable) #define COMPONENT_MOVABLE_BLOCK_UNCROSS 1 @@ -287,4 +288,4 @@ #define COMSIG_XENO_SLIME_CLICK_SHIFT "xeno_slime_click_shift" //from slime ShiftClickOn(): (/mob) #define COMSIG_XENO_TURF_CLICK_SHIFT "xeno_turf_click_shift" //from turf ShiftClickOn(): (/mob) #define COMSIG_XENO_TURF_CLICK_CTRL "xeno_turf_click_alt" //from turf AltClickOn(): (/mob) -#define COMSIG_XENO_MONKEY_CLICK_CTRL "xeno_monkey_click_ctrl" //from monkey CtrlClickOn(): (/mob) \ No newline at end of file +#define COMSIG_XENO_MONKEY_CLICK_CTRL "xeno_monkey_click_ctrl" //from monkey CtrlClickOn(): (/mob) diff --git a/code/__DEFINES/crafting.dm b/code/__DEFINES/crafting.dm index 0fbd76b9e21..33c80e970cf 100644 --- a/code/__DEFINES/crafting.dm +++ b/code/__DEFINES/crafting.dm @@ -33,4 +33,4 @@ #define RECIPE_GRILL "Grill" #define RECIPE_CANDY "Candy" -#define RECIPE_FAIL null \ No newline at end of file +#define RECIPE_FAIL null diff --git a/code/__DEFINES/game.dm b/code/__DEFINES/game.dm index 5c262724a14..4ad2ca587d9 100644 --- a/code/__DEFINES/game.dm +++ b/code/__DEFINES/game.dm @@ -2,4 +2,4 @@ #define GAME_STATE_PREGAME 1 #define GAME_STATE_SETTING_UP 2 #define GAME_STATE_PLAYING 3 -#define GAME_STATE_FINISHED 4 \ No newline at end of file +#define GAME_STATE_FINISHED 4 diff --git a/code/__DEFINES/gamemode.dm b/code/__DEFINES/gamemode.dm index 308e7a5f6d9..73a812e8f5b 100644 --- a/code/__DEFINES/gamemode.dm +++ b/code/__DEFINES/gamemode.dm @@ -53,4 +53,4 @@ #define SPECIAL_ROLE_XENOMORPH_DRONE "Xenomorph Drone" #define SPECIAL_ROLE_XENOMORPH_SENTINEL "Xenomorph Sentinel" #define SPECIAL_ROLE_XENOMORPH_LARVA "Xenomorph Larva" -#define SPECIAL_ROLE_EVENTMISC "Event Role" \ No newline at end of file +#define SPECIAL_ROLE_EVENTMISC "Event Role" diff --git a/code/__DEFINES/genetics.dm b/code/__DEFINES/genetics.dm index c8f98c1fbeb..c465ab2d514 100644 --- a/code/__DEFINES/genetics.dm +++ b/code/__DEFINES/genetics.dm @@ -166,4 +166,4 @@ #define NO_GERMS "no_germs" #define NO_DECAY "no_decay" #define PIERCEIMMUNE "pierce_immunity" -#define NO_HUNGER "no_hunger" \ No newline at end of file +#define NO_HUNGER "no_hunger" diff --git a/code/__DEFINES/hydroponics.dm b/code/__DEFINES/hydroponics.dm index f6693b0bcfc..8a3890d45f9 100644 --- a/code/__DEFINES/hydroponics.dm +++ b/code/__DEFINES/hydroponics.dm @@ -57,4 +57,4 @@ #define TRAIT_BIOLUM_COLOUR 37 #define TRAIT_IMMUTABLE 38 #define TRAIT_RARITY 39 -#define TRAIT_BATTERY_RECHARGE 40 \ No newline at end of file +#define TRAIT_BATTERY_RECHARGE 40 diff --git a/code/__DEFINES/job.dm b/code/__DEFINES/job.dm new file mode 100644 index 00000000000..d72628e2cb7 --- /dev/null +++ b/code/__DEFINES/job.dm @@ -0,0 +1,64 @@ +/////////////////////////////// +// WARNING // +//////////////////////////////////////////////////////////////////////// +// Do NOT touch the values associated with these defines, as they are // +// used by the game database to keep track of job flags. Do NOT touch // +//////////////////////////////////////////////////////////////////////// + +#define JOBCAT_ENGSEC (1<<0) + +#define JOB_CAPTAIN (1<<0) +#define JOB_HOS (1<<1) +#define JOB_WARDEN (1<<2) +#define JOB_DETECTIVE (1<<3) +#define JOB_OFFICER (1<<4) +#define JOB_CHIEF (1<<5) +#define JOB_ENGINEER (1<<6) +#define JOB_ATMOSTECH (1<<7) +#define JOB_AI (1<<8) +#define JOB_CYBORG (1<<9) +#define JOB_CENTCOM (1<<10) +#define JOB_SYNDICATE (1<<11) + +#define JOBCAT_MEDSCI (1<<1) + +#define JOB_RD (1<<0) +#define JOB_SCIENTIST (1<<1) +#define JOB_CHEMIST (1<<2) +#define JOB_CMO (1<<3) +#define JOB_DOCTOR (1<<4) +#define JOB_GENETICIST (1<<5) +#define JOB_VIROLOGIST (1<<6) +#define JOB_PSYCHIATRIST (1<<7) +#define JOB_ROBOTICIST (1<<8) +#define JOB_PARAMEDIC (1<<9) +#define JOB_CORONER (1<<10) + + +#define JOBCAT_SUPPORT (1<<2) + +#define JOB_HOP (1<<0) +#define JOB_BARTENDER (1<<1) +#define JOB_BOTANIST (1<<2) +#define JOB_CHEF (1<<3) +#define JOB_JANITOR (1<<4) +#define JOB_LIBRARIAN (1<<5) +#define JOB_QUARTERMASTER (1<<6) +#define JOB_CARGOTECH (1<<7) +#define JOB_MINER (1<<8) +#define JOB_LAWYER (1<<9) +#define JOB_CHAPLAIN (1<<10) +#define JOB_CLOWN (1<<11) +#define JOB_MIME (1<<12) +#define JOB_CIVILIAN (1<<13) + + +#define JOBCAT_KARMA (1<<3) + +#define JOB_NANO (1<<0) +#define JOB_BLUESHIELD (1<<1) +#define JOB_BARBER (1<<3) +#define JOB_MECHANIC (1<<4) +#define JOB_BRIGDOC (1<<5) +#define JOB_JUDGE (1<<6) +#define JOB_PILOT (1<<7) diff --git a/code/__DEFINES/js.dm b/code/__DEFINES/js.dm new file mode 100644 index 00000000000..e1a86e664d4 --- /dev/null +++ b/code/__DEFINES/js.dm @@ -0,0 +1,91 @@ +//this function places received data into element with specified id. +#define JS_BYJAX {" + +function replaceContent() { + var args = Array.prototype.slice.call(arguments); + var id = args\[0\]; + var content = args\[1\]; + var callback = null; + if(args\[2\]){ + callback = args\[2\]; + if(args\[3\]){ + args = args.slice(3); + } + } + var parent = document.getElementById(id); + if(typeof(parent)!=='undefined' && parent!=null){ + parent.innerHTML = content?content:''; + } + if(callback && window\[callback\]){ + window\[callback\].apply(null,args); + } +} +"} + +/* +sends data to control_id:replaceContent + +receiver - mob +control_id - window id (for windows opened with browse(), it'll be "windowname.browser") +target_element - HTML element id +new_content - HTML content +callback - js function that will be called after the data is sent +callback_args - arguments for callback function + +Be sure to include required js functions in your page, or it'll raise an exception. + +And yes I know this is a proc in a defines file, but its highly relevant so it can be here +*/ +proc/send_byjax(receiver, control_id, target_element, new_content=null, callback=null, list/callback_args=null) + if(receiver && target_element && control_id) // && winexists(receiver, control_id)) + var/list/argums = list(target_element, new_content) + if(callback) + argums += callback + if(callback_args) + argums += callback_args + argums = list2params(argums) +/* if(callback_args) + argums += "&[list2params(callback_args)]" +*/ + receiver << output(argums,"[control_id]:replaceContent") + return + + +// Misc JS for some dropdowns +#define JS_DROPDOWNS {" +function dropdowns() { + var divs = document.getElementsByTagName('div'); + var headers = new Array(); + var links = new Array(); + for(var i=0;i=0) { + elem.className = elem.className.replace('visible','hidden'); + this.className = this.className.replace('open','closed'); + this.innerHTML = this.innerHTML.replace('-','+'); + } + else { + elem.className = elem.className.replace('hidden','visible'); + this.className = this.className.replace('closed','open'); + this.innerHTML = this.innerHTML.replace('+','-'); + } + return false; + } + })(links\[i\]); + } + } +} +"} diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm index db88f46a676..ed4cb0d5940 100644 --- a/code/__DEFINES/layers.dm +++ b/code/__DEFINES/layers.dm @@ -110,4 +110,4 @@ #define ABOVE_HUD_LAYER 22 #define SPLASHSCREEN_LAYER 23 -#define SPLASHSCREEN_PLANE 23 \ No newline at end of file +#define SPLASHSCREEN_PLANE 23 diff --git a/code/__DEFINES/lighting.dm b/code/__DEFINES/lighting.dm index 7357c5f441e..01dbc2f1c2b 100644 --- a/code/__DEFINES/lighting.dm +++ b/code/__DEFINES/lighting.dm @@ -89,4 +89,4 @@ #define FLASH_LIGHT_DURATION 2 #define FLASH_LIGHT_POWER 3 -#define FLASH_LIGHT_RANGE 3.8 \ No newline at end of file +#define FLASH_LIGHT_RANGE 3.8 diff --git a/code/__DEFINES/machines.dm b/code/__DEFINES/machines.dm index 7a691c44df2..b56024bfab8 100644 --- a/code/__DEFINES/machines.dm +++ b/code/__DEFINES/machines.dm @@ -88,3 +88,7 @@ #define SUPERMATTER_DANGER 4 // Integrity < 75% #define SUPERMATTER_EMERGENCY 5 // Integrity < 50% #define SUPERMATTER_DELAMINATING 6 // Pretty obvious, Integrity < 25% + +// Firelock states +#define FD_OPEN 1 +#define FD_CLOSED 2 diff --git a/code/__DEFINES/mecha.dm b/code/__DEFINES/mecha.dm index e8955e64b50..b414f146705 100644 --- a/code/__DEFINES/mecha.dm +++ b/code/__DEFINES/mecha.dm @@ -19,4 +19,4 @@ #define MECHA_SECURE_BOLTS 1 #define MECHA_LOOSE_BOLTS 2 #define MECHA_OPEN_HATCH 3 -#define MECHA_UNSECURE_CELL 4 \ No newline at end of file +#define MECHA_UNSECURE_CELL 4 diff --git a/code/__DEFINES/medal.dm b/code/__DEFINES/medal.dm index 81dea5f373b..b47d562efee 100644 --- a/code/__DEFINES/medal.dm +++ b/code/__DEFINES/medal.dm @@ -21,4 +21,4 @@ #define DRAKE_SCORE "Drakes Killed" #define LEGION_SCORE "Legion Killed" #define SWARMER_BEACON_SCORE "Swarmer Beacons Killed" -#define TENDRIL_CLEAR_SCORE "Tendrils Killed" \ No newline at end of file +#define TENDRIL_CLEAR_SCORE "Tendrils Killed" diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 6e1fa5e01fd..346664e0758 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -319,7 +319,7 @@ #define INVESTIGATE_BOMB "bombs" // The SQL version required by this version of the code -#define SQL_VERSION 10 +#define SQL_VERSION 11 // Vending machine stuff #define CAT_NORMAL 1 @@ -419,4 +419,19 @@ #define ENGTYPE_TESLA "Tesla" #define SUMMON_GUNS "guns" -#define SUMMON_MAGIC "magic" \ No newline at end of file +#define SUMMON_MAGIC "magic" + +// Medical stuff +#define SYMPTOM_ACTIVATION_PROB 3 + +// Atmos stuff that fucking terrifies me +#define LINDA_SPAWN_HEAT 1 +#define LINDA_SPAWN_20C 2 +#define LINDA_SPAWN_TOXINS 4 +#define LINDA_SPAWN_OXYGEN 8 +#define LINDA_SPAWN_CO2 16 +#define LINDA_SPAWN_NITROGEN 32 + +#define LINDA_SPAWN_N2O 64 + +#define LINDA_SPAWN_AIR 256 diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 293d97e4fa3..e1e15a926d6 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -239,4 +239,4 @@ #define is_admin(user) (check_rights(R_ADMIN, 0, (user)) != 0) -#define SLEEP_CHECK_DEATH(X) sleep(X); if(QDELETED(src) || stat == DEAD) return; \ No newline at end of file +#define SLEEP_CHECK_DEATH(X) sleep(X); if(QDELETED(src) || stat == DEAD) return; diff --git a/code/__DEFINES/move_force.dm b/code/__DEFINES/move_force.dm index 750821bd898..8051a516298 100644 --- a/code/__DEFINES/move_force.dm +++ b/code/__DEFINES/move_force.dm @@ -16,4 +16,4 @@ #define MOVE_FORCE_NORMAL MOVE_FORCE_DEFAULT #define MOVE_FORCE_WEAK MOVE_FORCE_DEFAULT / 2 #define MOVE_FORCE_VERY_WEAK (MOVE_FORCE_DEFAULT / MOVE_FORCE_CRUSH_RATIO) + 1 -#define MOVE_FORCE_EXTREMELY_WEAK MOVE_FORCE_DEFAULT / (MOVE_FORCE_CRUSH_RATIO * 3) \ No newline at end of file +#define MOVE_FORCE_EXTREMELY_WEAK MOVE_FORCE_DEFAULT / (MOVE_FORCE_CRUSH_RATIO * 3) diff --git a/code/__DEFINES/pda.dm b/code/__DEFINES/pda.dm index d22b90870b0..c5d32f03ba8 100644 --- a/code/__DEFINES/pda.dm +++ b/code/__DEFINES/pda.dm @@ -1,3 +1,3 @@ #define PDA_APP_UPDATE 0 #define PDA_APP_NOUPDATE 1 -#define PDA_APP_UPDATE_SLOW 2 \ No newline at end of file +#define PDA_APP_UPDATE_SLOW 2 diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm index 54fd59307f4..eb5fad0ff27 100644 --- a/code/__DEFINES/preferences.dm +++ b/code/__DEFINES/preferences.dm @@ -7,9 +7,10 @@ #define SOUND_BUZZ 64 #define SOUND_INSTRUMENTS 128 #define SOUND_MENTORHELP 256 -#define SOUND_DISCO 512 +#define SOUND_DISCO 512 +#define SOUND_AI_VOICE 1024 -#define SOUND_DEFAULT (SOUND_ADMINHELP|SOUND_MIDI|SOUND_AMBIENCE|SOUND_LOBBY|SOUND_HEARTBEAT|SOUND_BUZZ|SOUND_INSTRUMENTS|SOUND_MENTORHELP|SOUND_DISCO) +#define SOUND_DEFAULT (SOUND_ADMINHELP|SOUND_MIDI|SOUND_AMBIENCE|SOUND_LOBBY|SOUND_HEARTBEAT|SOUND_BUZZ|SOUND_INSTRUMENTS|SOUND_MENTORHELP|SOUND_DISCO|SOUND_AI_VOICE) #define CHAT_OOC 1 #define CHAT_DEAD 2 diff --git a/code/__DEFINES/qdel.dm b/code/__DEFINES/qdel.dm index 6adb876da8d..0aaee0533d4 100644 --- a/code/__DEFINES/qdel.dm +++ b/code/__DEFINES/qdel.dm @@ -20,4 +20,4 @@ #define QDELING(X) (X.gc_destroyed) #define QDELETED(X) (!X || QDELING(X)) -#define QDESTROYING(X) (!X || X.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) \ No newline at end of file +#define QDESTROYING(X) (!X || X.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) diff --git a/code/__DEFINES/radio.dm b/code/__DEFINES/radio.dm index e49a9ea9b67..e0c71836c72 100644 --- a/code/__DEFINES/radio.dm +++ b/code/__DEFINES/radio.dm @@ -1,60 +1,60 @@ - -#define DISPLAY_FREQ 1435 //status displays -#define ATMOS_FIRE_FREQ 1437 //air alarms -#define ENGINE_FREQ 1438 //engine components -#define ATMOS_VENTSCRUB 1439 //vents, scrubbers, atmos control -#define ATMOS_DISTRO_FREQ 1443 //distro loop -#define ATMOS_TANKS_FREQ 1441 //atmos supply tanks -#define BOT_BEACON_FREQ 1445 //bot navigation beacons -#define AIRLOCK_FREQ 1449 //airlock controls, electropack, magnets - -#define RSD_FREQ 1457 //radio signal device -#define IMPL_FREQ 1451 //tracking implant - -#define RADIO_LOW_FREQ 1200 //minimum radio freq -#define PUBLIC_LOW_FREQ 1441 //minimum radio chat freq -#define PUBLIC_HIGH_FREQ 1489 //maximum radio chat freq -#define RADIO_HIGH_FREQ 1600 //maximum radio freq - -#define SYND_FREQ 1213 -#define SYNDTEAM_FREQ 1244 -#define DTH_FREQ 1341 //Special Operations -#define AI_FREQ 1343 -#define ERT_FREQ 1345 -#define COMM_FREQ 1353 //Command -#define BOT_FREQ 1447 //mulebot, secbot, ed209 - - -// Department channels -#define PUB_FREQ 1459 //standard radio chat -#define SEC_FREQ 1359 //security -#define ENG_FREQ 1357 //engineering -#define SCI_FREQ 1351 //science -#define MED_FREQ 1355 //medical -#define SUP_FREQ 1347 //cargo -#define SRV_FREQ 1349 //service - -// Internal department channels -#define MED_I_FREQ 1485 -#define SEC_I_FREQ 1475 - -// Transmission methods -#define TRANSMISSION_WIRE 0 -#define TRANSMISSION_RADIO 1 - -//This filter is special because devices belonging to default also recieve signals sent to any other filter. -#define RADIO_DEFAULT "radio_default" -#define RADIO_TO_AIRALARM "radio_airalarm" //air alarms -#define RADIO_FROM_AIRALARM "radio_airalarm_rcvr" //devices interested in recieving signals from air alarms -#define RADIO_CHAT "radio_telecoms" -#define RADIO_ATMOSIA "radio_atmos" -#define RADIO_NAVBEACONS "radio_navbeacon" -#define RADIO_AIRLOCK "radio_airlock" -#define RADIO_SECBOT "radio_secbot" -#define RADIO_HONKBOT "radio_honkbot" -#define RADIO_MULEBOT "radio_mulebot" -#define RADIO_CLEANBOT "10" -#define RADIO_FLOORBOT "11" -#define RADIO_MEDBOT "12" -#define RADIO_MAGNETS "radio_magnet" -#define RADIO_LOGIC "radio_logic" + +#define DISPLAY_FREQ 1435 //status displays +#define ATMOS_FIRE_FREQ 1437 //air alarms +#define ENGINE_FREQ 1438 //engine components +#define ATMOS_VENTSCRUB 1439 //vents, scrubbers, atmos control +#define ATMOS_DISTRO_FREQ 1443 //distro loop +#define ATMOS_TANKS_FREQ 1441 //atmos supply tanks +#define BOT_BEACON_FREQ 1445 //bot navigation beacons +#define AIRLOCK_FREQ 1449 //airlock controls, electropack, magnets + +#define RSD_FREQ 1457 //radio signal device +#define IMPL_FREQ 1451 //tracking implant + +#define RADIO_LOW_FREQ 1200 //minimum radio freq +#define PUBLIC_LOW_FREQ 1441 //minimum radio chat freq +#define PUBLIC_HIGH_FREQ 1489 //maximum radio chat freq +#define RADIO_HIGH_FREQ 1600 //maximum radio freq + +#define SYND_FREQ 1213 +#define SYNDTEAM_FREQ 1244 +#define DTH_FREQ 1341 //Special Operations +#define AI_FREQ 1343 +#define ERT_FREQ 1345 +#define COMM_FREQ 1353 //Command +#define BOT_FREQ 1447 //mulebot, secbot, ed209 + + +// Department channels +#define PUB_FREQ 1459 //standard radio chat +#define SEC_FREQ 1359 //security +#define ENG_FREQ 1357 //engineering +#define SCI_FREQ 1351 //science +#define MED_FREQ 1355 //medical +#define SUP_FREQ 1347 //cargo +#define SRV_FREQ 1349 //service + +// Internal department channels +#define MED_I_FREQ 1485 +#define SEC_I_FREQ 1475 + +// Transmission methods +#define TRANSMISSION_WIRE 0 +#define TRANSMISSION_RADIO 1 + +//This filter is special because devices belonging to default also recieve signals sent to any other filter. +#define RADIO_DEFAULT "radio_default" +#define RADIO_TO_AIRALARM "radio_airalarm" //air alarms +#define RADIO_FROM_AIRALARM "radio_airalarm_rcvr" //devices interested in recieving signals from air alarms +#define RADIO_CHAT "radio_telecoms" +#define RADIO_ATMOSIA "radio_atmos" +#define RADIO_NAVBEACONS "radio_navbeacon" +#define RADIO_AIRLOCK "radio_airlock" +#define RADIO_SECBOT "radio_secbot" +#define RADIO_HONKBOT "radio_honkbot" +#define RADIO_MULEBOT "radio_mulebot" +#define RADIO_CLEANBOT "10" +#define RADIO_FLOORBOT "11" +#define RADIO_MEDBOT "12" +#define RADIO_MAGNETS "radio_magnet" +#define RADIO_LOGIC "radio_logic" diff --git a/code/__DEFINES/reagents.dm b/code/__DEFINES/reagents.dm index e2dd3ee3ac7..564247455e3 100644 --- a/code/__DEFINES/reagents.dm +++ b/code/__DEFINES/reagents.dm @@ -16,3 +16,6 @@ // Is an open container for all intents and purposes. #define OPENCONTAINER (REFILLABLE | DRAINABLE | TRANSPARENT) + +#define REAGENT_TOUCH 1 +#define REAGENT_INGEST 2 diff --git a/code/__DEFINES/role_preferences.dm b/code/__DEFINES/role_preferences.dm index 34ad4d69e54..2b9e2c56b4d 100644 --- a/code/__DEFINES/role_preferences.dm +++ b/code/__DEFINES/role_preferences.dm @@ -1,81 +1,81 @@ - - -//Values for antag preferences, event roles, etc. unified here - - - -//These are synced with the Database, if you change the values of the defines -//then you MUST update the database! -// If you're adding a new role, remember to update modules/admin/topic.dm, so admins can dish out -// justice if someone's abusing your role -#define ROLE_SYNDICATE "Syndicate" -#define ROLE_TRAITOR "traitor" -#define ROLE_OPERATIVE "operative" -#define ROLE_CHANGELING "changeling" -#define ROLE_WIZARD "wizard" -#define ROLE_REV "revolutionary" -#define ROLE_ALIEN "xenomorph" -#define ROLE_PAI "pAI" -#define ROLE_CULTIST "cultist" -#define ROLE_BLOB "blob" -#define ROLE_NINJA "space ninja" -#define ROLE_MONKEY "monkey" -#define ROLE_GANG "gangster" -#define ROLE_SHADOWLING "shadowling" -#define ROLE_ABDUCTOR "abductor" -#define ROLE_REVENANT "revenant" -#define ROLE_HOG_GOD "hand of god: god" // We're prolly gonna port this one day or another -#define ROLE_HOG_CULTIST "hand of god: cultist" -#define ROLE_DEVIL "devil" -#define ROLE_RAIDER "vox raider" -#define ROLE_TRADER "trader" -#define ROLE_VAMPIRE "vampire" -// Role tags for EVERYONE! -#define ROLE_BORER "cortical borer" -#define ROLE_DEMON "slaughter demon" -#define ROLE_SENTIENT "sentient animal" -#define ROLE_POSIBRAIN "positronic brain" -#define ROLE_GUARDIAN "guardian" -#define ROLE_MORPH "morph" -#define ROLE_ERT "emergency response team" -#define ROLE_NYMPH "Dionaea" -#define ROLE_GSPIDER "giant spider" -#define ROLE_DRONE "drone" -#define ROLE_DEATHSQUAD "deathsquad" -#define ROLE_EVENTMISC "eventmisc" -#define ROLE_GHOST "ghost role" - -//Missing assignment means it's not a gamemode specific role, IT'S NOT A BUG OR ERROR. -//The gamemode specific ones are just so the gamemodes can query whether a player is old enough -//(in game days played) to play that role -var/global/list/special_roles = list( - ROLE_ABDUCTOR = /datum/game_mode/abduction, // Abductor - ROLE_BLOB = /datum/game_mode/blob, // Blob - ROLE_CHANGELING = /datum/game_mode/changeling, // Changeling - ROLE_BORER, // Cortical borer - ROLE_CULTIST = /datum/game_mode/cult, // Cultist - ROLE_DEVIL = /datum/game_mode/devil/devil_agents, // Devil - ROLE_GSPIDER, // Giant spider - ROLE_GUARDIAN, // Guardian - ROLE_MORPH, // Morph - ROLE_OPERATIVE = /datum/game_mode/nuclear, // Operative - ROLE_PAI, // PAI - ROLE_POSIBRAIN, // Positronic brain - ROLE_REVENANT, // Revenant - ROLE_REV = /datum/game_mode/revolution, // Revolutionary - ROLE_SENTIENT, // Sentient animal - ROLE_SHADOWLING = /datum/game_mode/shadowling, // Shadowling - ROLE_DEMON, // Slaguther demon - ROLE_NINJA, // Space ninja - ROLE_TRADER, // Trader - ROLE_TRAITOR = /datum/game_mode/traitor, // Traitor - ROLE_VAMPIRE = /datum/game_mode/vampire, // Vampire - ROLE_RAIDER = /datum/game_mode/heist, // Vox raider - ROLE_ALIEN, // Xenomorph - ROLE_WIZARD = /datum/game_mode/wizard, // Wizard - // UNUSED/BROKEN ANTAGS -// ROLE_HOG_GOD = /datum/game_mode/hand_of_god, -// ROLE_HOG_CULTIST = /datum/game_mode/hand_of_god, -// ROLE_MONKEY = /datum/game_mode/monkey, Sooner or later these are going to get ported -// ROLE_GANG = /datum/game_mode/gang, -) + + +//Values for antag preferences, event roles, etc. unified here + + + +//These are synced with the Database, if you change the values of the defines +//then you MUST update the database! +// If you're adding a new role, remember to update modules/admin/topic.dm, so admins can dish out +// justice if someone's abusing your role +#define ROLE_SYNDICATE "Syndicate" +#define ROLE_TRAITOR "traitor" +#define ROLE_OPERATIVE "operative" +#define ROLE_CHANGELING "changeling" +#define ROLE_WIZARD "wizard" +#define ROLE_REV "revolutionary" +#define ROLE_ALIEN "xenomorph" +#define ROLE_PAI "pAI" +#define ROLE_CULTIST "cultist" +#define ROLE_BLOB "blob" +#define ROLE_NINJA "space ninja" +#define ROLE_MONKEY "monkey" +#define ROLE_GANG "gangster" +#define ROLE_SHADOWLING "shadowling" +#define ROLE_ABDUCTOR "abductor" +#define ROLE_REVENANT "revenant" +#define ROLE_HOG_GOD "hand of god: god" // We're prolly gonna port this one day or another +#define ROLE_HOG_CULTIST "hand of god: cultist" +#define ROLE_DEVIL "devil" +#define ROLE_RAIDER "vox raider" +#define ROLE_TRADER "trader" +#define ROLE_VAMPIRE "vampire" +// Role tags for EVERYONE! +#define ROLE_BORER "cortical borer" +#define ROLE_DEMON "slaughter demon" +#define ROLE_SENTIENT "sentient animal" +#define ROLE_POSIBRAIN "positronic brain" +#define ROLE_GUARDIAN "guardian" +#define ROLE_MORPH "morph" +#define ROLE_ERT "emergency response team" +#define ROLE_NYMPH "Dionaea" +#define ROLE_GSPIDER "giant spider" +#define ROLE_DRONE "drone" +#define ROLE_DEATHSQUAD "deathsquad" +#define ROLE_EVENTMISC "eventmisc" +#define ROLE_GHOST "ghost role" + +//Missing assignment means it's not a gamemode specific role, IT'S NOT A BUG OR ERROR. +//The gamemode specific ones are just so the gamemodes can query whether a player is old enough +//(in game days played) to play that role +var/global/list/special_roles = list( + ROLE_ABDUCTOR = /datum/game_mode/abduction, // Abductor + ROLE_BLOB = /datum/game_mode/blob, // Blob + ROLE_CHANGELING = /datum/game_mode/changeling, // Changeling + ROLE_BORER, // Cortical borer + ROLE_CULTIST = /datum/game_mode/cult, // Cultist + ROLE_DEVIL = /datum/game_mode/devil/devil_agents, // Devil + ROLE_GSPIDER, // Giant spider + ROLE_GUARDIAN, // Guardian + ROLE_MORPH, // Morph + ROLE_OPERATIVE = /datum/game_mode/nuclear, // Operative + ROLE_PAI, // PAI + ROLE_POSIBRAIN, // Positronic brain + ROLE_REVENANT, // Revenant + ROLE_REV = /datum/game_mode/revolution, // Revolutionary + ROLE_SENTIENT, // Sentient animal + ROLE_SHADOWLING = /datum/game_mode/shadowling, // Shadowling + ROLE_DEMON, // Slaguther demon + ROLE_NINJA, // Space ninja + ROLE_TRADER, // Trader + ROLE_TRAITOR = /datum/game_mode/traitor, // Traitor + ROLE_VAMPIRE = /datum/game_mode/vampire, // Vampire + ROLE_RAIDER = /datum/game_mode/heist, // Vox raider + ROLE_ALIEN, // Xenomorph + ROLE_WIZARD = /datum/game_mode/wizard, // Wizard + // UNUSED/BROKEN ANTAGS +// ROLE_HOG_GOD = /datum/game_mode/hand_of_god, +// ROLE_HOG_CULTIST = /datum/game_mode/hand_of_god, +// ROLE_MONKEY = /datum/game_mode/monkey, Sooner or later these are going to get ported +// ROLE_GANG = /datum/game_mode/gang, +) diff --git a/code/__DEFINES/shuttle.dm b/code/__DEFINES/shuttle.dm index 22af9a38670..72ed25d1518 100644 --- a/code/__DEFINES/shuttle.dm +++ b/code/__DEFINES/shuttle.dm @@ -13,4 +13,4 @@ #define SHUTTLE_DOCKER_LANDING_CLEAR 1 #define SHUTTLE_DOCKER_BLOCKED_BY_HIDDEN_PORT 2 -#define SHUTTLE_DOCKER_BLOCKED 3 \ No newline at end of file +#define SHUTTLE_DOCKER_BLOCKED 3 diff --git a/code/__DEFINES/sight.dm b/code/__DEFINES/sight.dm index 58f557c4b75..52fc66aa814 100644 --- a/code/__DEFINES/sight.dm +++ b/code/__DEFINES/sight.dm @@ -38,4 +38,4 @@ #define VISOR_TINT (1<<1) #define VISOR_VISIONFLAGS (1<<2) //all following flags only matter for glasses #define VISOR_DARKNESSVIEW (1<<3) -#define VISOR_INVISVIEW (1<<4) \ No newline at end of file +#define VISOR_INVISVIEW (1<<4) diff --git a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm index 7e131dcaa21..b0274e9d802 100644 --- a/code/__DEFINES/sound.dm +++ b/code/__DEFINES/sound.dm @@ -14,4 +14,4 @@ #define SOUND_MINIMUM_PRESSURE 10 -#define FALLOFF_SOUNDS 0.5 \ No newline at end of file +#define FALLOFF_SOUNDS 0.5 diff --git a/code/__DEFINES/station_goals.dm b/code/__DEFINES/station_goals.dm index 7c13f4f9e67..0c19da7b5f1 100644 --- a/code/__DEFINES/station_goals.dm +++ b/code/__DEFINES/station_goals.dm @@ -1,2 +1,2 @@ #define BSA_SIZE_FRONT 4 -#define BSA_SIZE_BACK 6 \ No newline at end of file +#define BSA_SIZE_BACK 6 diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm index 6a14e6fae66..54b85d80e76 100644 --- a/code/__DEFINES/status_effects.dm +++ b/code/__DEFINES/status_effects.dm @@ -81,4 +81,4 @@ #define STATUS_EFFECT_CRUSHERDAMAGETRACKING /datum/status_effect/crusher_damage //tracks total kinetic crusher damage on a target -#define STATUS_EFFECT_SYPHONMARK /datum/status_effect/syphon_mark //tracks kills for the KA death syphon module \ No newline at end of file +#define STATUS_EFFECT_SYPHONMARK /datum/status_effect/syphon_mark //tracks kills for the KA death syphon module diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index c55fec2cd87..300d3a41fc0 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -144,4 +144,4 @@ A.overlays |= po;\ }\ A.flags_2 &= ~OVERLAY_QUEUED_2;\ -} \ No newline at end of file +} diff --git a/code/__DEFINES/tools.dm b/code/__DEFINES/tools.dm new file mode 100644 index 00000000000..d0204e1d013 --- /dev/null +++ b/code/__DEFINES/tools.dm @@ -0,0 +1,46 @@ +#define TOOL_CROWBAR "crowbar" +#define TOOL_MULTITOOL "multitool" +#define TOOL_SCREWDRIVER "screwdriver" +#define TOOL_WIRECUTTER "wirecutter" +#define TOOL_WRENCH "wrench" +#define TOOL_WELDER "welder" + +#define MIN_TOOL_SOUND_DELAY 20 + +//Crowbar messages +#define CROWBAR_ATTEMPT_PRY_CIRCUIT_MESSAGE user.visible_message("[user] begins removing the circuit board from [src]...", "You begin removing the circuit board from [src]...", "You hear prying noises.") +#define CROWBAR_PRY_CIRCUIT_SUCCESS_MESSAGE user.visible_message("[user] pries out the circuit board from [src]!", "You pry out the circuit board from [src]!", "You hear prying noises.") + +//Screwdriver messages +#define SCREWDRIVER_SCREW_MESSAGE user.visible_message("[user] tightens the screws on [src]!", "You tighten the screws on [src]!", "You hear a screwdriver.") +#define SCREWDRIVER_UNSCREW_MESSAGE user.visible_message("[user] loosens the screws on [src]!", "You loosen the screws on [src]!", "You hear a screwdriver.") +#define SCREWDRIVER_OPEN_PANEL_MESSAGE user.visible_message("[user] opens the panel on [src]!", "You open the panel on [src]!", "You hear a screwdriver.") +#define SCREWDRIVER_CLOSE_PANEL_MESSAGE user.visible_message("[user] closes the panel on [src]!", "You close the panel on [src]!", "You hear a screwdriver.") + +//Wirecutter messages +#define WIRECUTTER_SNIP_MESSAGE user.visible_message("[user] cuts the wires from [src]!", "You cut the wires from [src]!", "You hear snipping.") +#define WIRECUTTER_ATTEMPT_DISMANTLE_MESSAGE user.visible_message("[user] begins cutting [src] apart... ", "You begin cutting [src] apart...", "You hear snipping.") +#define WIRECUTTER_DISMANTLE_SUCCESS_MESSAGE user.visible_message("[user] cuts [src] apart!", "You cut [src] apart!", "You hear snipping.") + +//Welder messages and other stuff +#define HEALPERWELD 15 +#define WELDER_ATTEMPT_WELD_MESSAGE user.visible_message("[user] begins welding [src]...", "You begin welding [src]...", "You hear welding.") +#define WELDER_WELD_SUCCESS_MESSAGE to_chat(user, "You finish welding [src]!") +#define WELDER_ATTEMPT_REPAIR_MESSAGE user.visible_message("[user] begins repairing the damage on [src]...", "You begin repairing [src]...", "You hear welding.") +#define WELDER_REPAIR_SUCCESS_MESSAGE to_chat(user, "You repair the damage on [src]!") +#define WELDER_ATTEMPT_SLICING_MESSAGE user.visible_message("[user] begins slicing through [src]...", "You begin slicing through [src]...", "You hear welding.") +#define WELDER_SLICING_SUCCESS_MESSAGE to_chat(user, "You slice clean through [src]!") +#define WELDER_ATTEMPT_FLOOR_SLICE_MESSAGE user.visible_message("[user] begins slicing [src] free from [get_turf(src)]...", "You begin slicing [src] free from [get_turf(src)]...", "You hear welding.") +#define WELDER_FLOOR_SLICE_SUCCESS_MESSAGE to_chat(user, "You slice [src] clear of [get_turf(src)]!") +#define WELDER_ATTEMPT_FLOOR_WELD_MESSAGE user.visible_message("[user] begins welding [src] to [get_turf(src)]...", "You begin welding [src] to [get_turf(src)]...", "You hear welding.") +#define WELDER_FLOOR_WELD_SUCCESS_MESSAGE to_chat(user, "You weld [src] to [get_turf(src)]!") + +//Wrench messages +#define WRENCH_ANCHOR_MESSAGE user.visible_message("[user] tightens the bolts on [src]!", "You tighten the bolts on [src]!", "You hear ratcheting.") +#define WRENCH_UNANCHOR_MESSAGE user.visible_message("[user] loosens the bolts on [src]!", "You loosen the bolts on [src]!", "You hear ratcheting.") +#define WRENCH_UNANCHOR_WALL_MESSAGE user.visible_message("[user] unwrenches [src] from the wall!", "You unwrench [src] from the wall!", "You hear ratcheting.") +#define WRENCH_ANCHOR_TO_WALL_MESSAGE user.visible_message("[user] affixes [src] to the wall!", "You affix [src] to the wall!", "You hear ratcheting.") + +//Generic tool messages that don't correspond to any particular tool +#define TOOL_ATTEMPT_DISMANTLE_MESSAGE user.visible_message("[user] begins to disassemble [src] with [I]...", "You begin to disassemble [src] with [I]...", "You hear someone using some kind of tool.") +#define TOOL_DISMANTLE_SUCCESS_MESSAGE user.visible_message("[user] dismantles [src]!", "You dismantle [src]!", "You hear someone using some kind of tool.") diff --git a/code/__DEFINES/typeids.dm b/code/__DEFINES/typeids.dm index 16b6e475b8e..56e0958d1a4 100644 --- a/code/__DEFINES/typeids.dm +++ b/code/__DEFINES/typeids.dm @@ -3,4 +3,4 @@ #define TYPEID_NORMAL_LIST "f" //helper macros #define GET_TYPEID(ref) ( ( (length(ref) <= 10) ? "TYPEID_NULL" : copytext(ref, 4, length(ref) - 6) ) ) -#define IS_NORMAL_LIST(L) (GET_TYPEID("\ref[L]") == TYPEID_NORMAL_LIST) \ No newline at end of file +#define IS_NORMAL_LIST(L) (GET_TYPEID("\ref[L]") == TYPEID_NORMAL_LIST) diff --git a/code/__DEFINES/vv.dm b/code/__DEFINES/vv.dm index 22e13f87f8a..0ac352f9ae9 100644 --- a/code/__DEFINES/vv.dm +++ b/code/__DEFINES/vv.dm @@ -19,4 +19,4 @@ #define VV_NULL "NULL" #define VV_RESTORE_DEFAULT "Restore to Default" #define VV_MARKED_DATUM "Marked Datum" -#define VV_REGEX "Regex" \ No newline at end of file +#define VV_REGEX "Regex" diff --git a/code/__HELPERS/AnimationLibrary.dm b/code/__HELPERS/AnimationLibrary.dm index fd79fe197f2..cc496a85b63 100644 --- a/code/__HELPERS/AnimationLibrary.dm +++ b/code/__HELPERS/AnimationLibrary.dm @@ -187,4 +187,4 @@ var/original_y = A.pixel_y animate(A, transform = matrix(punchstr, MATRIX_ROTATE), pixel_y = 16, time = 2, color = "#eeeeee", easing = BOUNCE_EASING) animate(transform = matrix(-punchstr, MATRIX_ROTATE), pixel_y = original_y, time = 2, color = "#ffffff", easing = BOUNCE_EASING) - animate(transform = null, time = 3, easing = BOUNCE_EASING) \ No newline at end of file + animate(transform = null, time = 3, easing = BOUNCE_EASING) diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm index 8c07fe175b2..7a91c3bf9bc 100644 --- a/code/__HELPERS/_logging.dm +++ b/code/__HELPERS/_logging.dm @@ -1,5 +1,5 @@ //location of the rust-g library -#define RUST_G "rust_g" +#define RUST_G (world.system_type == UNIX ? "./librust_g.so" : "./rust_g.dll") // On Linux/Unix systems the line endings are LF, on windows it's CRLF, admins that don't use notepad++ // will get logs that are one big line if the system is Linux and they are using notepad. This solves it by adding CR to every line ending diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm index 0db57b27eae..afb70cb81e6 100644 --- a/code/__HELPERS/cmp.dm +++ b/code/__HELPERS/cmp.dm @@ -47,4 +47,4 @@ if(A.plane != B.plane) return A.plane - B.plane else - return A.layer - B.layer \ No newline at end of file + return A.layer - B.layer diff --git a/code/__HELPERS/constants.dm b/code/__HELPERS/constants.dm index f7db3166605..b976a28e195 100644 --- a/code/__HELPERS/constants.dm +++ b/code/__HELPERS/constants.dm @@ -1,2 +1,2 @@ -var/global/const/TICKS_IN_DAY = 864000 -var/global/const/TICKS_IN_SECOND = 10 \ No newline at end of file +#define TICKS_IN_DAY 864000 +#define TICKS_IN_SECOND 10 diff --git a/code/__HELPERS/experimental.dm b/code/__HELPERS/experimental.dm index 533f67b68d9..4f13c07cbd4 100644 --- a/code/__HELPERS/experimental.dm +++ b/code/__HELPERS/experimental.dm @@ -48,4 +48,4 @@ if(A != myArea) myArea = A - . = myArea \ No newline at end of file + . = myArea diff --git a/code/__HELPERS/files.dm b/code/__HELPERS/files.dm index 27f3537d521..c96357299c2 100644 --- a/code/__HELPERS/files.dm +++ b/code/__HELPERS/files.dm @@ -1,60 +1,60 @@ -//checks if a file exists and contains text -//returns text as a string if these conditions are met -/proc/return_file_text(filename) - if(fexists(filename) == 0) - error("File not found ([filename])") - return - - var/text = file2text(filename) - if(!text) - error("File empty ([filename])") - return - - return text - -//Sends resource files to client cache -/client/proc/getFiles() - for(var/file in args) - src << browse_rsc(file) - -/client/proc/browse_files(root="data/logs/", max_iterations=10, list/valid_extensions=list(".txt",".log",".htm")) - var/path = root - - for(var/i=0, iError: browse_files(): File not found/Invalid file([path]).") - return - - return path - -#define FTPDELAY 200 //200 tick delay to discourage spam -/* This proc is a failsafe to prevent spamming of file requests. - It is just a timer that only permits a download every [FTPDELAY] ticks. - This can be changed by modifying FTPDELAY's value above. - - PLEASE USE RESPONSIBLY, Some log files canr each sizes of 4MB! */ -/client/proc/file_spam_check() - var/time_to_wait = fileaccess_timer - world.time - if(time_to_wait > 0) - to_chat(src, "Error: file_spam_check(): Spam. Please wait [round(time_to_wait/10)] seconds.") - return 1 - fileaccess_timer = world.time + FTPDELAY - return 0 -#undef FTPDELAY \ No newline at end of file +//checks if a file exists and contains text +//returns text as a string if these conditions are met +/proc/return_file_text(filename) + if(fexists(filename) == 0) + error("File not found ([filename])") + return + + var/text = file2text(filename) + if(!text) + error("File empty ([filename])") + return + + return text + +//Sends resource files to client cache +/client/proc/getFiles() + for(var/file in args) + src << browse_rsc(file) + +/client/proc/browse_files(root="data/logs/", max_iterations=10, list/valid_extensions=list(".txt",".log",".htm")) + var/path = root + + for(var/i=0, iError: browse_files(): File not found/Invalid file([path]).") + return + + return path + +#define FTPDELAY 200 //200 tick delay to discourage spam +/* This proc is a failsafe to prevent spamming of file requests. + It is just a timer that only permits a download every [FTPDELAY] ticks. + This can be changed by modifying FTPDELAY's value above. + + PLEASE USE RESPONSIBLY, Some log files canr each sizes of 4MB! */ +/client/proc/file_spam_check() + var/time_to_wait = fileaccess_timer - world.time + if(time_to_wait > 0) + to_chat(src, "Error: file_spam_check(): Spam. Please wait [round(time_to_wait/10)] seconds.") + return 1 + fileaccess_timer = world.time + FTPDELAY + return 0 +#undef FTPDELAY diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index 744b8b86a9a..f61b6452f22 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -1,536 +1,539 @@ -/proc/get_area(atom/A) - if(isarea(A)) - return A - var/turf/T = get_turf(A) - return T ? T.loc : null - -/proc/get_area_name(N) //get area by its name - for(var/area/A in world) - if(A.name == N) - return A - return 0 - -/proc/get_location_name(atom/X, format_text = FALSE) - var/area/A = isarea(X) ? X : get_area(X) - if(!A) - return null - return format_text ? format_text(A.name) : A.name - -/proc/get_areas_in_range(dist=0, atom/center=usr) - if(!dist) - var/turf/T = get_turf(center) - return T ? list(T.loc) : list() - if(!center) - return list() - - var/list/turfs = RANGE_TURFS(dist, center) - var/list/areas = list() - for(var/V in turfs) - var/turf/T = V - areas |= T.loc - return areas - -// Like view but bypasses luminosity check - -/proc/hear(var/range, var/atom/source) - var/lum = source.luminosity - source.luminosity = 6 - - var/list/heard = view(range, source) - source.luminosity = lum - - return heard - -/proc/circlerange(center=usr,radius=3) - - var/turf/centerturf = get_turf(center) - var/list/turfs = new/list() - var/rsq = radius * (radius+0.5) - - for(var/atom/T in range(radius, centerturf)) - var/dx = T.x - centerturf.x - var/dy = T.y - centerturf.y - if(dx*dx + dy*dy <= rsq) - turfs += T - - //turfs += centerturf - return turfs - -/proc/circleview(center=usr,radius=3) - - var/turf/centerturf = get_turf(center) - var/list/atoms = new/list() - var/rsq = radius * (radius+0.5) - - for(var/atom/A in view(radius, centerturf)) - var/dx = A.x - centerturf.x - var/dy = A.y - centerturf.y - if(dx*dx + dy*dy <= rsq) - atoms += A - - //turfs += centerturf - return atoms - -/proc/ff_cansee(atom/A, atom/B) - var/AT = get_turf(A) - var/BT = get_turf(B) - if(AT == BT) - return 1 - var/list/line = getline(A, B) - for(var/turf/T in line) - if(T == AT || T == BT) - break - if(T.density) - return FALSE - return TRUE - -/proc/get_dist_euclidian(atom/Loc1 as turf|mob|obj,atom/Loc2 as turf|mob|obj) - var/dx = Loc1.x - Loc2.x - var/dy = Loc1.y - Loc2.y - - var/dist = sqrt(dx**2 + dy**2) - - return dist - -/proc/circlerangeturfs(center=usr,radius=3) - - var/turf/centerturf = get_turf(center) - var/list/turfs = new/list() - var/rsq = radius * (radius+0.5) - - for(var/turf/T in range(radius, centerturf)) - var/dx = T.x - centerturf.x - var/dy = T.y - centerturf.y - if(dx*dx + dy*dy <= rsq) - turfs += T - return turfs - -/proc/circleviewturfs(center=usr,radius=3) //Is there even a diffrence between this proc and circlerangeturfs()? - - var/turf/centerturf = get_turf(center) - var/list/turfs = new/list() - var/rsq = radius * (radius+0.5) - - for(var/turf/T in view(radius, centerturf)) - var/dx = T.x - centerturf.x - var/dy = T.y - centerturf.y - if(dx*dx + dy*dy <= rsq) - turfs += T - return turfs - - - -//var/debug_mob = 0 - -// Will recursively loop through an atom's contents and check for mobs, then it will loop through every atom in that atom's contents. -// It will keep doing this until it checks every content possible. This will fix any problems with mobs, that are inside objects, -// being unable to hear people due to being in a box within a bag. - -/proc/recursive_mob_check(var/atom/O, var/list/L = list(), var/recursion_limit = 3, var/client_check = 1, var/sight_check = 1, var/include_radio = 1) - - //debug_mob += O.contents.len - if(!recursion_limit) - return L - for(var/atom/A in O.contents) - - if(ismob(A)) - var/mob/M = A - if(client_check && !M.client) - L |= recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio) - continue - if(sight_check && !isInSight(A, O)) - continue - L |= M - //log_world("[recursion_limit] = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])") - - else if(include_radio && istype(A, /obj/item/radio)) - if(sight_check && !isInSight(A, O)) - continue - L |= A - - if(isobj(A) || ismob(A)) - L |= recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio) - return L - -// The old system would loop through lists for a total of 5000 per function call, in an empty server. -// This new system will loop at around 1000 in an empty server. - -/proc/get_mobs_in_view(var/R, var/atom/source, var/include_clientless = FALSE) - // Returns a list of mobs in range of R from source. Used in radio and say code. - - var/turf/T = get_turf(source) - var/list/hear = list() - - if(!T) - return hear - - var/list/range = hear(R, T) - - for(var/atom/A in range) - if(ismob(A)) - var/mob/M = A - if(M.client || include_clientless) - hear += M - //log_world("Start = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])") - else if(istype(A, /obj/item/radio)) - hear += A - - if(isobj(A) || ismob(A)) - hear |= recursive_mob_check(A, hear, 3, 1, 0, 1) - - return hear - - -/proc/get_mobs_in_radio_ranges(var/list/obj/item/radio/radios) - - set background = 1 - - . = list() - // Returns a list of mobs who can hear any of the radios given in @radios - var/list/speaker_coverage = list() - for(var/obj/item/radio/R in radios) - if(R) - //Cyborg checks. Receiving message uses a bit of cyborg's charge. - var/obj/item/radio/borg/BR = R - if(istype(BR) && BR.myborg) - var/mob/living/silicon/robot/borg = BR.myborg - var/datum/robot_component/CO = borg.get_component("radio") - if(!CO) - continue //No radio component (Shouldn't happen) - if(!borg.is_component_functioning("radio")) - continue //No power. - - var/turf/speaker = get_turf(R) - if(speaker) - for(var/turf/T in hear(R.canhear_range,speaker)) - speaker_coverage[T] = T - - - // Try to find all the players who can hear the message - for(var/A in GLOB.player_list + GLOB.hear_radio_list) - var/mob/M = A - if(M) - var/turf/ear = get_turf(M) - if(ear) - // Ghostship is magic: Ghosts can hear radio chatter from anywhere - if(speaker_coverage[ear] || (istype(M, /mob/dead/observer) && M.get_preference(CHAT_GHOSTRADIO))) - . |= M // Since we're already looping through mobs, why bother using |= ? This only slows things down. - return . - -/proc/inLineOfSight(X1,Y1,X2,Y2,Z=1,PX1=16.5,PY1=16.5,PX2=16.5,PY2=16.5) - var/turf/T - if(X1==X2) - if(Y1==Y2) - return 1 //Light cannot be blocked on same tile - else - var/s = SIMPLE_SIGN(Y2-Y1) - Y1+=s - while(Y1!=Y2) - T=locate(X1,Y1,Z) - if(T.opacity) - return 0 - Y1+=s - else - var/m=(32*(Y2-Y1)+(PY2-PY1))/(32*(X2-X1)+(PX2-PX1)) - var/b=(Y1+PY1/32-0.015625)-m*(X1+PX1/32-0.015625) //In tiles - var/signX = SIMPLE_SIGN(X2-X1) - var/signY = SIMPLE_SIGN(Y2-Y1) - if(X1 abs (dx)) //slope is above 1:1 (move horizontally in a tie) - if(dy > 0) - return get_step(start, SOUTH) - else - return get_step(start, NORTH) - else - if(dx > 0) - return get_step(start, WEST) - else - return get_step(start, EAST) - -/proc/try_move_adjacent(atom/movable/AM) - var/turf/T = get_turf(AM) - for(var/direction in cardinal) - if(AM.Move(get_step(T, direction))) - break - -/proc/get_mob_by_key(var/key) - for(var/mob/M in GLOB.mob_list) - if(M.ckey == lowertext(key)) - return M - return null - -/proc/get_candidates(be_special_type, afk_bracket=3000, override_age=0, override_jobban=0) - var/roletext = get_roletext(be_special_type) - var/list/candidates = list() - // Keep looping until we find a non-afk candidate within the time bracket (we limit the bracket to 10 minutes (6000)) - while(!candidates.len && afk_bracket < 6000) - for(var/mob/dead/observer/G in GLOB.player_list) - if(G.client != null) - if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD)) - if(!G.client.is_afk(afk_bracket) && (be_special_type in G.client.prefs.be_special)) - if(!override_jobban || (!jobban_isbanned(G, roletext) && !jobban_isbanned(G,"Syndicate"))) - if(override_age || player_old_enough_antag(G.client,be_special_type)) - candidates += G.client - afk_bracket += 600 // Add a minute to the bracket, for every attempt - - return candidates - -/proc/get_candidate_ghosts(be_special_type, afk_bracket=3000, override_age=0, override_jobban=0) - var/roletext = get_roletext(be_special_type) - var/list/candidates = list() - // Keep looping until we find a non-afk candidate within the time bracket (we limit the bracket to 10 minutes (6000)) - while(!candidates.len && afk_bracket < 6000) - for(var/mob/dead/observer/G in GLOB.player_list) - if(G.client != null) - if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD)) - if(!G.client.is_afk(afk_bracket) && (be_special_type in G.client.prefs.be_special)) - if(!override_jobban || (!jobban_isbanned(G, roletext) && !jobban_isbanned(G,"Syndicate"))) - if(override_age || player_old_enough_antag(G.client,be_special_type)) - candidates += G - afk_bracket += 600 // Add a minute to the bracket, for every attempt - - return candidates - -/proc/ScreenText(obj/O, maptext="", screen_loc="CENTER-7,CENTER-7", maptext_height=480, maptext_width=480) - if(!isobj(O)) O = new /obj/screen/text() - O.maptext = maptext - O.maptext_height = maptext_height - O.maptext_width = maptext_width - O.screen_loc = screen_loc - return O - -/proc/Show2Group4Delay(obj/O, list/group, delay=0) - if(!isobj(O)) return - if(!group) group = GLOB.clients - for(var/client/C in group) - C.screen += O - if(delay) - spawn(delay) - for(var/client/C in group) - C.screen -= O - -/proc/flick_overlay(image/I, list/show_to, duration) - for(var/client/C in show_to) - C.images += I - spawn(duration) - for(var/client/C in show_to) - C.images -= I - -/proc/get_active_player_count() - // Get active players who are playing in the round - var/active_players = 0 - for(var/i = 1; i <= GLOB.player_list.len; i++) - var/mob/M = GLOB.player_list[i] - if(M && M.client) - if(istype(M, /mob/new_player)) // exclude people in the lobby - continue - else if(isobserver(M)) // Ghosts are fine if they were playing once (didn't start as observers) - var/mob/dead/observer/O = M - if(O.started_as_observer) // Exclude people who started as observers - continue - active_players++ - return active_players - -/datum/projectile_data - var/src_x - var/src_y - var/time - var/distance - var/power_x - var/power_y - var/dest_x - var/dest_y - -/datum/projectile_data/New(var/src_x, var/src_y, var/time, var/distance, \ - var/power_x, var/power_y, var/dest_x, var/dest_y) - src.src_x = src_x - src.src_y = src_y - src.time = time - src.distance = distance - src.power_x = power_x - src.power_y = power_y - src.dest_x = dest_x - src.dest_y = dest_y - -/proc/projectile_trajectory(var/src_x, var/src_y, var/rotation, var/angle, var/power) - - // returns the destination (Vx,y) that a projectile shot at [src_x], [src_y], with an angle of [angle], - // rotated at [rotation] and with the power of [power] - // Thanks to VistaPOWA for this function - - var/power_x = power * cos(angle) - var/power_y = power * sin(angle) - var/time = 2* power_y / 10 //10 = g - - var/distance = time * power_x - - var/dest_x = src_x + distance*sin(rotation); - var/dest_y = src_y + distance*cos(rotation); - - return new /datum/projectile_data(src_x, src_y, time, distance, power_x, power_y, dest_x, dest_y) - - -/proc/mobs_in_area(var/area/the_area, var/client_needed=0, var/moblist=GLOB.mob_list) - var/list/mobs_found[0] - var/area/our_area = get_area(the_area) - for(var/mob/M in moblist) - if(client_needed && !M.client) - continue - if(our_area != get_area(M)) - continue - mobs_found += M - return mobs_found - -/proc/alone_in_area(var/area/the_area, var/mob/must_be_alone, var/check_type = /mob/living/carbon) - var/area/our_area = get_area(the_area) - for(var/C in GLOB.living_mob_list) - if(!istype(C, check_type)) - continue - if(C == must_be_alone) - continue - if(our_area == get_area(C)) - return 0 - return 1 - - -/proc/GetRedPart(const/hexa) - return hex2num(copytext(hexa, 2, 4)) - -/proc/GetGreenPart(const/hexa) - return hex2num(copytext(hexa, 4, 6)) - -/proc/GetBluePart(const/hexa) - return hex2num(copytext(hexa, 6, 8)) - -/proc/lavaland_equipment_pressure_check(turf/T) - . = FALSE - if(!istype(T)) - return - var/datum/gas_mixture/environment = T.return_air() - if(!istype(environment)) - return - var/pressure = environment.return_pressure() - if(pressure <= LAVALAND_EQUIPMENT_EFFECT_PRESSURE) - . = TRUE - -/proc/GetHexColors(const/hexa) - return list( - GetRedPart(hexa), - GetGreenPart(hexa), - GetBluePart(hexa), - ) - -/proc/MinutesToTicks(var/minutes as num) - return minutes * 60 * 10 - -/proc/SecondsToTicks(var/seconds) - return seconds * 10 - -proc/pollCandidates(Question, be_special_type, antag_age_check = FALSE, poll_time = 300, ignore_respawnability = FALSE, min_hours = 0, flashwindow = TRUE, check_antaghud = TRUE) - var/roletext = be_special_type ? get_roletext(be_special_type) : null - var/list/mob/dead/observer/candidates = list() - var/time_passed = world.time - if(!Question) - Question = "Would you like to be a special role?" - - for(var/mob/dead/observer/G in (ignore_respawnability ? GLOB.player_list : GLOB.respawnable_list)) - if(!G.key || !G.client) - continue - if(be_special_type) - if(!(be_special_type in G.client.prefs.be_special)) - continue - if(antag_age_check) - if(!player_old_enough_antag(G.client, be_special_type)) - continue - if(roletext) - if(jobban_isbanned(G, roletext) || jobban_isbanned(G, "Syndicate")) - continue - if(config.use_exp_restrictions && min_hours) - if(G.client.get_exp_type_num(EXP_TYPE_LIVING) < min_hours * 60) - continue - if(check_antaghud && cannotPossess(G)) - continue - spawn(0) - G << 'sound/misc/notice2.ogg'//Alerting them to their consideration - if(flashwindow) - window_flash(G.client) - switch(alert(G,Question,"Please answer in [poll_time/10] seconds!","No","Yes","Not This Round")) - if("Yes") - to_chat(G, "Choice registered: Yes.") - if((world.time-time_passed)>poll_time)//If more than 30 game seconds passed. - to_chat(G, "Sorry, you were too late for the consideration!") - G << 'sound/machines/buzz-sigh.ogg' - return - candidates += G - if("No") - to_chat(G, "Choice registered: No.") - return - if("Not This Round") - to_chat(G, "Choice registered: No.") - to_chat(G, "You will no longer receive notifications for the role '[roletext]' for the rest of the round.") - G.client.prefs.be_special -= be_special_type - return - else - return - sleep(poll_time) - - //Check all our candidates, to make sure they didn't log off during the 30 second wait period. - for(var/mob/dead/observer/G in candidates) - if(!G.key || !G.client) - candidates.Remove(G) - - return candidates - -/proc/pollCandidatesWithVeto(adminclient, adminusr, max_slots, Question, be_special_type, antag_age_check = 0, poll_time = 300, ignore_respawnability = 0, min_hours = 0, flashwindow = TRUE, check_antaghud = TRUE) - var/list/willing_ghosts = pollCandidates(Question, be_special_type, antag_age_check, poll_time, ignore_respawnability, min_hours, flashwindow, check_antaghud) - var/list/selected_ghosts = list() - if(!willing_ghosts.len) - return selected_ghosts - - var/list/candidate_ghosts = willing_ghosts.Copy() - - to_chat(adminusr, "Candidate Ghosts:"); - for(var/mob/dead/observer/G in candidate_ghosts) - if(G.key && G.client) - to_chat(adminusr, "- [G] ([G.key])"); - else - candidate_ghosts -= G - - for(var/i = max_slots, (i > 0 && candidate_ghosts.len), i--) - var/this_ghost = input("Pick players. This will go on until there either no more ghosts to pick from or the [i] remaining slot(s) are full.", "Candidates") as null|anything in candidate_ghosts - candidate_ghosts -= this_ghost - selected_ghosts += this_ghost - return selected_ghosts - -/proc/window_flash(client/C) - if(ismob(C)) - var/mob/M = C - if(M.client) - C = M.client - if(!C || !C.prefs.windowflashing) - return - winset(C, "mainwindow", "flash=5") +/proc/get_area(atom/A) + if(isarea(A)) + return A + var/turf/T = get_turf(A) + return T ? T.loc : null + +/proc/get_area_name(N) //get area by its name + for(var/area/A in world) + if(A.name == N) + return A + return 0 + +/proc/get_location_name(atom/X, format_text = FALSE) + var/area/A = isarea(X) ? X : get_area(X) + if(!A) + return null + return format_text ? format_text(A.name) : A.name + +/proc/get_areas_in_range(dist=0, atom/center=usr) + if(!dist) + var/turf/T = get_turf(center) + return T ? list(T.loc) : list() + if(!center) + return list() + + var/list/turfs = RANGE_TURFS(dist, center) + var/list/areas = list() + for(var/V in turfs) + var/turf/T = V + areas |= T.loc + return areas + +// Like view but bypasses luminosity check + +/proc/hear(var/range, var/atom/source) + var/lum = source.luminosity + source.luminosity = 6 + + var/list/heard = view(range, source) + source.luminosity = lum + + return heard + +/proc/circlerange(center=usr,radius=3) + + var/turf/centerturf = get_turf(center) + var/list/turfs = new/list() + var/rsq = radius * (radius+0.5) + + for(var/atom/T in range(radius, centerturf)) + var/dx = T.x - centerturf.x + var/dy = T.y - centerturf.y + if(dx*dx + dy*dy <= rsq) + turfs += T + + //turfs += centerturf + return turfs + +/proc/circleview(center=usr,radius=3) + + var/turf/centerturf = get_turf(center) + var/list/atoms = new/list() + var/rsq = radius * (radius+0.5) + + for(var/atom/A in view(radius, centerturf)) + var/dx = A.x - centerturf.x + var/dy = A.y - centerturf.y + if(dx*dx + dy*dy <= rsq) + atoms += A + + //turfs += centerturf + return atoms + +/proc/ff_cansee(atom/A, atom/B) + var/AT = get_turf(A) + var/BT = get_turf(B) + if(AT == BT) + return 1 + var/list/line = getline(A, B) + for(var/turf/T in line) + if(T == AT || T == BT) + break + if(T.density) + return FALSE + return TRUE + +/proc/get_dist_euclidian(atom/Loc1 as turf|mob|obj,atom/Loc2 as turf|mob|obj) + var/dx = Loc1.x - Loc2.x + var/dy = Loc1.y - Loc2.y + + var/dist = sqrt(dx**2 + dy**2) + + return dist + +/proc/circlerangeturfs(center=usr,radius=3) + + var/turf/centerturf = get_turf(center) + var/list/turfs = new/list() + var/rsq = radius * (radius+0.5) + + for(var/turf/T in range(radius, centerturf)) + var/dx = T.x - centerturf.x + var/dy = T.y - centerturf.y + if(dx*dx + dy*dy <= rsq) + turfs += T + return turfs + +/proc/circleviewturfs(center=usr,radius=3) //Is there even a diffrence between this proc and circlerangeturfs()? + + var/turf/centerturf = get_turf(center) + var/list/turfs = new/list() + var/rsq = radius * (radius+0.5) + + for(var/turf/T in view(radius, centerturf)) + var/dx = T.x - centerturf.x + var/dy = T.y - centerturf.y + if(dx*dx + dy*dy <= rsq) + turfs += T + return turfs + + + +//var/debug_mob = 0 + +// Will recursively loop through an atom's contents and check for mobs, then it will loop through every atom in that atom's contents. +// It will keep doing this until it checks every content possible. This will fix any problems with mobs, that are inside objects, +// being unable to hear people due to being in a box within a bag. + +/proc/recursive_mob_check(var/atom/O, var/list/L = list(), var/recursion_limit = 3, var/client_check = 1, var/sight_check = 1, var/include_radio = 1) + + //debug_mob += O.contents.len + if(!recursion_limit) + return L + for(var/atom/A in O.contents) + + if(ismob(A)) + var/mob/M = A + if(client_check && !M.client) + L |= recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio) + continue + if(sight_check && !isInSight(A, O)) + continue + L |= M + //log_world("[recursion_limit] = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])") + + else if(include_radio && istype(A, /obj/item/radio)) + if(sight_check && !isInSight(A, O)) + continue + L |= A + + if(isobj(A) || ismob(A)) + L |= recursive_mob_check(A, L, recursion_limit - 1, client_check, sight_check, include_radio) + return L + +// The old system would loop through lists for a total of 5000 per function call, in an empty server. +// This new system will loop at around 1000 in an empty server. + +/proc/get_mobs_in_view(var/R, var/atom/source, var/include_clientless = FALSE) + // Returns a list of mobs in range of R from source. Used in radio and say code. + + var/turf/T = get_turf(source) + var/list/hear = list() + + if(!T) + return hear + + var/list/range = hear(R, T) + + for(var/atom/A in range) + if(ismob(A)) + var/mob/M = A + if(M.client || include_clientless) + hear += M + //log_world("Start = [M] - [get_turf(M)] - ([M.x], [M.y], [M.z])") + else if(istype(A, /obj/item/radio)) + hear += A + + if(isobj(A) || ismob(A)) + hear |= recursive_mob_check(A, hear, 3, 1, 0, 1) + + return hear + + +/proc/get_mobs_in_radio_ranges(var/list/obj/item/radio/radios) + + set background = 1 + + . = list() + // Returns a list of mobs who can hear any of the radios given in @radios + var/list/speaker_coverage = list() + for(var/obj/item/radio/R in radios) + if(R) + //Cyborg checks. Receiving message uses a bit of cyborg's charge. + var/obj/item/radio/borg/BR = R + if(istype(BR) && BR.myborg) + var/mob/living/silicon/robot/borg = BR.myborg + var/datum/robot_component/CO = borg.get_component("radio") + if(!CO) + continue //No radio component (Shouldn't happen) + if(!borg.is_component_functioning("radio")) + continue //No power. + + var/turf/speaker = get_turf(R) + if(speaker) + for(var/turf/T in hear(R.canhear_range,speaker)) + speaker_coverage[T] = T + + + // Try to find all the players who can hear the message + for(var/A in GLOB.player_list + GLOB.hear_radio_list) + var/mob/M = A + if(M) + var/turf/ear = get_turf(M) + if(ear) + // Ghostship is magic: Ghosts can hear radio chatter from anywhere + if(speaker_coverage[ear] || (istype(M, /mob/dead/observer) && M.get_preference(CHAT_GHOSTRADIO))) + . |= M // Since we're already looping through mobs, why bother using |= ? This only slows things down. + return . + +/proc/inLineOfSight(X1,Y1,X2,Y2,Z=1,PX1=16.5,PY1=16.5,PX2=16.5,PY2=16.5) + var/turf/T + if(X1==X2) + if(Y1==Y2) + return 1 //Light cannot be blocked on same tile + else + var/s = SIMPLE_SIGN(Y2-Y1) + Y1+=s + while(Y1!=Y2) + T=locate(X1,Y1,Z) + if(T.opacity) + return 0 + Y1+=s + else + var/m=(32*(Y2-Y1)+(PY2-PY1))/(32*(X2-X1)+(PX2-PX1)) + var/b=(Y1+PY1/32-0.015625)-m*(X1+PX1/32-0.015625) //In tiles + var/signX = SIMPLE_SIGN(X2-X1) + var/signY = SIMPLE_SIGN(Y2-Y1) + if(X1 abs (dx)) //slope is above 1:1 (move horizontally in a tie) + if(dy > 0) + return get_step(start, SOUTH) + else + return get_step(start, NORTH) + else + if(dx > 0) + return get_step(start, WEST) + else + return get_step(start, EAST) + +/proc/try_move_adjacent(atom/movable/AM) + var/turf/T = get_turf(AM) + for(var/direction in cardinal) + if(AM.Move(get_step(T, direction))) + break + +/proc/get_mob_by_key(var/key) + for(var/mob/M in GLOB.mob_list) + if(M.ckey == lowertext(key)) + return M + return null + +/proc/get_candidates(be_special_type, afk_bracket=3000, override_age=0, override_jobban=0) + var/roletext = get_roletext(be_special_type) + var/list/candidates = list() + // Keep looping until we find a non-afk candidate within the time bracket (we limit the bracket to 10 minutes (6000)) + while(!candidates.len && afk_bracket < 6000) + for(var/mob/dead/observer/G in GLOB.player_list) + if(G.client != null) + if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD)) + if(!G.client.is_afk(afk_bracket) && (be_special_type in G.client.prefs.be_special)) + if(!override_jobban || (!jobban_isbanned(G, roletext) && !jobban_isbanned(G,"Syndicate"))) + if(override_age || player_old_enough_antag(G.client,be_special_type)) + candidates += G.client + afk_bracket += 600 // Add a minute to the bracket, for every attempt + + return candidates + +/proc/get_candidate_ghosts(be_special_type, afk_bracket=3000, override_age=0, override_jobban=0) + var/roletext = get_roletext(be_special_type) + var/list/candidates = list() + // Keep looping until we find a non-afk candidate within the time bracket (we limit the bracket to 10 minutes (6000)) + while(!candidates.len && afk_bracket < 6000) + for(var/mob/dead/observer/G in GLOB.player_list) + if(G.client != null) + if(!(G.mind && G.mind.current && G.mind.current.stat != DEAD)) + if(!G.client.is_afk(afk_bracket) && (be_special_type in G.client.prefs.be_special)) + if(!override_jobban || (!jobban_isbanned(G, roletext) && !jobban_isbanned(G,"Syndicate"))) + if(override_age || player_old_enough_antag(G.client,be_special_type)) + candidates += G + afk_bracket += 600 // Add a minute to the bracket, for every attempt + + return candidates + +/proc/ScreenText(obj/O, maptext="", screen_loc="CENTER-7,CENTER-7", maptext_height=480, maptext_width=480) + if(!isobj(O)) O = new /obj/screen/text() + O.maptext = maptext + O.maptext_height = maptext_height + O.maptext_width = maptext_width + O.screen_loc = screen_loc + return O + +/proc/Show2Group4Delay(obj/O, list/group, delay=0) + if(!isobj(O)) return + if(!group) group = GLOB.clients + for(var/client/C in group) + C.screen += O + if(delay) + spawn(delay) + for(var/client/C in group) + C.screen -= O + +/proc/flick_overlay(image/I, list/show_to, duration) + for(var/client/C in show_to) + C.images += I + spawn(duration) + for(var/client/C in show_to) + C.images -= I + +/proc/get_active_player_count() + // Get active players who are playing in the round + var/active_players = 0 + for(var/i = 1; i <= GLOB.player_list.len; i++) + var/mob/M = GLOB.player_list[i] + if(M && M.client) + if(istype(M, /mob/new_player)) // exclude people in the lobby + continue + else if(isobserver(M)) // Ghosts are fine if they were playing once (didn't start as observers) + var/mob/dead/observer/O = M + if(O.started_as_observer) // Exclude people who started as observers + continue + active_players++ + return active_players + +/datum/projectile_data + var/src_x + var/src_y + var/time + var/distance + var/power_x + var/power_y + var/dest_x + var/dest_y + +/datum/projectile_data/New(var/src_x, var/src_y, var/time, var/distance, \ + var/power_x, var/power_y, var/dest_x, var/dest_y) + src.src_x = src_x + src.src_y = src_y + src.time = time + src.distance = distance + src.power_x = power_x + src.power_y = power_y + src.dest_x = dest_x + src.dest_y = dest_y + +/proc/projectile_trajectory(var/src_x, var/src_y, var/rotation, var/angle, var/power) + + // returns the destination (Vx,y) that a projectile shot at [src_x], [src_y], with an angle of [angle], + // rotated at [rotation] and with the power of [power] + // Thanks to VistaPOWA for this function + + var/power_x = power * cos(angle) + var/power_y = power * sin(angle) + var/time = 2* power_y / 10 //10 = g + + var/distance = time * power_x + + var/dest_x = src_x + distance*sin(rotation); + var/dest_y = src_y + distance*cos(rotation); + + return new /datum/projectile_data(src_x, src_y, time, distance, power_x, power_y, dest_x, dest_y) + + +/proc/mobs_in_area(var/area/the_area, var/client_needed=0, var/moblist=GLOB.mob_list) + var/list/mobs_found[0] + var/area/our_area = get_area(the_area) + for(var/mob/M in moblist) + if(client_needed && !M.client) + continue + if(our_area != get_area(M)) + continue + mobs_found += M + return mobs_found + +/proc/alone_in_area(var/area/the_area, var/mob/must_be_alone, var/check_type = /mob/living/carbon) + var/area/our_area = get_area(the_area) + for(var/C in GLOB.living_mob_list) + if(!istype(C, check_type)) + continue + if(C == must_be_alone) + continue + if(our_area == get_area(C)) + return 0 + return 1 + + +/proc/GetRedPart(const/hexa) + return hex2num(copytext(hexa, 2, 4)) + +/proc/GetGreenPart(const/hexa) + return hex2num(copytext(hexa, 4, 6)) + +/proc/GetBluePart(const/hexa) + return hex2num(copytext(hexa, 6, 8)) + +/proc/lavaland_equipment_pressure_check(turf/T) + . = FALSE + if(!istype(T)) + return + var/datum/gas_mixture/environment = T.return_air() + if(!istype(environment)) + return + var/pressure = environment.return_pressure() + if(pressure <= LAVALAND_EQUIPMENT_EFFECT_PRESSURE) + . = TRUE + +/proc/GetHexColors(const/hexa) + return list( + GetRedPart(hexa), + GetGreenPart(hexa), + GetBluePart(hexa), + ) + +/proc/MinutesToTicks(var/minutes as num) + return minutes * 60 * 10 + +/proc/SecondsToTicks(var/seconds) + return seconds * 10 + +proc/pollCandidates(Question, be_special_type, antag_age_check = FALSE, poll_time = 300, ignore_respawnability = FALSE, min_hours = 0, flashwindow = TRUE, check_antaghud = TRUE) + var/roletext = be_special_type ? get_roletext(be_special_type) : null + var/list/mob/dead/observer/candidates = list() + var/time_passed = world.time + if(!Question) + Question = "Would you like to be a special role?" + + for(var/mob/dead/observer/G in (ignore_respawnability ? GLOB.player_list : GLOB.respawnable_list)) + if(!G.key || !G.client) + continue + if(be_special_type) + if(!(be_special_type in G.client.prefs.be_special)) + continue + if(antag_age_check) + if(!player_old_enough_antag(G.client, be_special_type)) + continue + if(roletext) + if(jobban_isbanned(G, roletext) || jobban_isbanned(G, "Syndicate")) + continue + if(config.use_exp_restrictions && min_hours) + if(G.client.get_exp_type_num(EXP_TYPE_LIVING) < min_hours * 60) + continue + if(check_antaghud && cannotPossess(G)) + continue + spawn(0) + G << 'sound/misc/notice2.ogg'//Alerting them to their consideration + if(flashwindow) + window_flash(G.client) + var/ans = alert(G,Question,"Please answer in [poll_time/10] seconds!","No","Yes","Not This Round") + if(!G?.client) + return + switch(ans) + if("Yes") + to_chat(G, "Choice registered: Yes.") + if((world.time-time_passed)>poll_time)//If more than 30 game seconds passed. + to_chat(G, "Sorry, you were too late for the consideration!") + G << 'sound/machines/buzz-sigh.ogg' + return + candidates += G + if("No") + to_chat(G, "Choice registered: No.") + return + if("Not This Round") + to_chat(G, "Choice registered: No.") + to_chat(G, "You will no longer receive notifications for the role '[roletext]' for the rest of the round.") + G.client.prefs.be_special -= be_special_type + return + else + return + sleep(poll_time) + + //Check all our candidates, to make sure they didn't log off during the 30 second wait period. + for(var/mob/dead/observer/G in candidates) + if(!G.key || !G.client) + candidates.Remove(G) + + return candidates + +/proc/pollCandidatesWithVeto(adminclient, adminusr, max_slots, Question, be_special_type, antag_age_check = 0, poll_time = 300, ignore_respawnability = 0, min_hours = 0, flashwindow = TRUE, check_antaghud = TRUE) + var/list/willing_ghosts = pollCandidates(Question, be_special_type, antag_age_check, poll_time, ignore_respawnability, min_hours, flashwindow, check_antaghud) + var/list/selected_ghosts = list() + if(!willing_ghosts.len) + return selected_ghosts + + var/list/candidate_ghosts = willing_ghosts.Copy() + + to_chat(adminusr, "Candidate Ghosts:"); + for(var/mob/dead/observer/G in candidate_ghosts) + if(G.key && G.client) + to_chat(adminusr, "- [G] ([G.key])"); + else + candidate_ghosts -= G + + for(var/i = max_slots, (i > 0 && candidate_ghosts.len), i--) + var/this_ghost = input("Pick players. This will go on until there either no more ghosts to pick from or the [i] remaining slot(s) are full.", "Candidates") as null|anything in candidate_ghosts + candidate_ghosts -= this_ghost + selected_ghosts += this_ghost + return selected_ghosts + +/proc/window_flash(client/C) + if(ismob(C)) + var/mob/M = C + if(M.client) + C = M.client + if(!C || !C.prefs.windowflashing) + return + winset(C, "mainwindow", "flash=5") diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm index 2a23cc8baa0..0002bc0d488 100644 --- a/code/__HELPERS/global_lists.dm +++ b/code/__HELPERS/global_lists.dm @@ -1,90 +1,90 @@ - -////////////////////////// -/////Initial Building///// -////////////////////////// - -/proc/makeDatumRefLists() - //markings - init_sprite_accessory_subtypes(/datum/sprite_accessory/body_markings, GLOB.marking_styles_list) - //head accessory - init_sprite_accessory_subtypes(/datum/sprite_accessory/head_accessory, GLOB.head_accessory_styles_list) - //hair - init_sprite_accessory_subtypes(/datum/sprite_accessory/hair, GLOB.hair_styles_public_list, GLOB.hair_styles_male_list, GLOB.hair_styles_female_list, GLOB.hair_styles_full_list) - //facial hair - init_sprite_accessory_subtypes(/datum/sprite_accessory/facial_hair, GLOB.facial_hair_styles_list, GLOB.facial_hair_styles_male_list, GLOB.facial_hair_styles_female_list) - //underwear - init_sprite_accessory_subtypes(/datum/sprite_accessory/underwear, GLOB.underwear_list, GLOB.underwear_m, GLOB.underwear_f) - //undershirt - init_sprite_accessory_subtypes(/datum/sprite_accessory/undershirt, GLOB.undershirt_list, GLOB.undershirt_m, GLOB.undershirt_f) - //socks - init_sprite_accessory_subtypes(/datum/sprite_accessory/socks, GLOB.socks_list, GLOB.socks_m, GLOB.socks_f) - //alt heads - init_sprite_accessory_subtypes(/datum/sprite_accessory/alt_heads, GLOB.alt_heads_list) - - init_subtypes(/datum/surgery_step, GLOB.surgery_steps) - - for(var/path in (subtypesof(/datum/surgery))) - GLOB.surgeries_list += new path() - - init_datum_subtypes(/datum/job, GLOB.joblist, list(/datum/job/ai, /datum/job/cyborg), "title") - init_datum_subtypes(/datum/superheroes, GLOB.all_superheroes, null, "name") - init_datum_subtypes(/datum/language, GLOB.all_languages, null, "name") - - for(var/language_name in GLOB.all_languages) - var/datum/language/L = GLOB.all_languages[language_name] - if(!(L.flags & NONGLOBAL)) - GLOB.language_keys[":[lowertext(L.key)]"] = L - GLOB.language_keys[".[lowertext(L.key)]"] = L - GLOB.language_keys["#[lowertext(L.key)]"] = L - - var/rkey = 0 - for(var/spath in subtypesof(/datum/species)) - var/datum/species/S = new spath() - S.race_key = ++rkey //Used in mob icon caching. - GLOB.all_species[S.name] = S - - if(IS_WHITELISTED in S.species_traits) - GLOB.whitelisted_species += S.name - - init_subtypes(/datum/crafting_recipe, GLOB.crafting_recipes) - - //Pipe list building - init_subtypes(/datum/pipes, GLOB.construction_pipe_list) - for(var/D in GLOB.construction_pipe_list) - var/datum/pipes/P = D - if(P.rpd_dispensable) - GLOB.rpd_pipe_list += list(list("pipe_name" = P.pipe_name, "pipe_id" = P.pipe_id, "pipe_type" = P.pipe_type, "pipe_category" = P.pipe_category, "orientations" = P.orientations, "pipe_icon" = P.pipe_icon, "bendy" = P.bendy)) - return 1 - -/* // Uncomment to debug chemical reaction list. -/client/verb/debug_chemical_list() - - for(var/reaction in GLOB.chemical_reactions_list) - . += "GLOB.chemical_reactions_list\[\"[reaction]\"\] = \"[GLOB.chemical_reactions_list[reaction]]\"\n" - if(islist(GLOB.chemical_reactions_list[reaction])) - var/list/L = GLOB.chemical_reactions_list[reaction] - for(var/t in L) - . += " has: [t]\n" - to_chat(world, .) -*/ - - -//creates every subtype of prototype (excluding prototype) and adds it to list L. -//if no list/L is provided, one is created. -/proc/init_subtypes(prototype, list/L) - if(!istype(L)) L = list() - for(var/path in subtypesof(prototype)) - L += new path() - return L - -/proc/init_datum_subtypes(prototype, list/L, list/pexempt, assocvar) - if(!istype(L)) L = list() - for(var/path in subtypesof(prototype) - pexempt) - var/datum/D = new path() - if(istype(D)) - var/assoc - if(D.vars["[assocvar]"]) //has the var - assoc = D.vars["[assocvar]"] //access value of var - if(assoc) //value gotten - L["[assoc]"] = D //put in association - return L + +////////////////////////// +/////Initial Building///// +////////////////////////// + +/proc/makeDatumRefLists() + //markings + init_sprite_accessory_subtypes(/datum/sprite_accessory/body_markings, GLOB.marking_styles_list) + //head accessory + init_sprite_accessory_subtypes(/datum/sprite_accessory/head_accessory, GLOB.head_accessory_styles_list) + //hair + init_sprite_accessory_subtypes(/datum/sprite_accessory/hair, GLOB.hair_styles_public_list, GLOB.hair_styles_male_list, GLOB.hair_styles_female_list, GLOB.hair_styles_full_list) + //facial hair + init_sprite_accessory_subtypes(/datum/sprite_accessory/facial_hair, GLOB.facial_hair_styles_list, GLOB.facial_hair_styles_male_list, GLOB.facial_hair_styles_female_list) + //underwear + init_sprite_accessory_subtypes(/datum/sprite_accessory/underwear, GLOB.underwear_list, GLOB.underwear_m, GLOB.underwear_f) + //undershirt + init_sprite_accessory_subtypes(/datum/sprite_accessory/undershirt, GLOB.undershirt_list, GLOB.undershirt_m, GLOB.undershirt_f) + //socks + init_sprite_accessory_subtypes(/datum/sprite_accessory/socks, GLOB.socks_list, GLOB.socks_m, GLOB.socks_f) + //alt heads + init_sprite_accessory_subtypes(/datum/sprite_accessory/alt_heads, GLOB.alt_heads_list) + + init_subtypes(/datum/surgery_step, GLOB.surgery_steps) + + for(var/path in (subtypesof(/datum/surgery))) + GLOB.surgeries_list += new path() + + init_datum_subtypes(/datum/job, GLOB.joblist, list(/datum/job/ai, /datum/job/cyborg), "title") + init_datum_subtypes(/datum/superheroes, GLOB.all_superheroes, null, "name") + init_datum_subtypes(/datum/language, GLOB.all_languages, null, "name") + + for(var/language_name in GLOB.all_languages) + var/datum/language/L = GLOB.all_languages[language_name] + if(!(L.flags & NONGLOBAL)) + GLOB.language_keys[":[lowertext(L.key)]"] = L + GLOB.language_keys[".[lowertext(L.key)]"] = L + GLOB.language_keys["#[lowertext(L.key)]"] = L + + var/rkey = 0 + for(var/spath in subtypesof(/datum/species)) + var/datum/species/S = new spath() + S.race_key = ++rkey //Used in mob icon caching. + GLOB.all_species[S.name] = S + + if(IS_WHITELISTED in S.species_traits) + GLOB.whitelisted_species += S.name + + init_subtypes(/datum/crafting_recipe, GLOB.crafting_recipes) + + //Pipe list building + init_subtypes(/datum/pipes, GLOB.construction_pipe_list) + for(var/D in GLOB.construction_pipe_list) + var/datum/pipes/P = D + if(P.rpd_dispensable) + GLOB.rpd_pipe_list += list(list("pipe_name" = P.pipe_name, "pipe_id" = P.pipe_id, "pipe_type" = P.pipe_type, "pipe_category" = P.pipe_category, "orientations" = P.orientations, "pipe_icon" = P.pipe_icon, "bendy" = P.bendy)) + return 1 + +/* // Uncomment to debug chemical reaction list. +/client/verb/debug_chemical_list() + + for(var/reaction in GLOB.chemical_reactions_list) + . += "GLOB.chemical_reactions_list\[\"[reaction]\"\] = \"[GLOB.chemical_reactions_list[reaction]]\"\n" + if(islist(GLOB.chemical_reactions_list[reaction])) + var/list/L = GLOB.chemical_reactions_list[reaction] + for(var/t in L) + . += " has: [t]\n" + to_chat(world, .) +*/ + + +//creates every subtype of prototype (excluding prototype) and adds it to list L. +//if no list/L is provided, one is created. +/proc/init_subtypes(prototype, list/L) + if(!istype(L)) L = list() + for(var/path in subtypesof(prototype)) + L += new path() + return L + +/proc/init_datum_subtypes(prototype, list/L, list/pexempt, assocvar) + if(!istype(L)) L = list() + for(var/path in subtypesof(prototype) - pexempt) + var/datum/D = new path() + if(istype(D)) + var/assoc + if(D.vars["[assocvar]"]) //has the var + assoc = D.vars["[assocvar]"] //access value of var + if(assoc) //value gotten + L["[assoc]"] = D //put in association + return L diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm index 0b37872decd..a434dd75577 100644 --- a/code/__HELPERS/icons.dm +++ b/code/__HELPERS/icons.dm @@ -1,963 +1,963 @@ -/* -IconProcs README - -A BYOND library for manipulating icons and colors - -by Lummox JR - -version 1.0 - -The IconProcs library was made to make a lot of common icon operations much easier. BYOND's icon manipulation -routines are very capable but some of the advanced capabilities like using alpha transparency can be unintuitive to beginners. - -CHANGING ICONS - -Several new procs have been added to the /icon datum to simplify working with icons. To use them, -remember you first need to setup an /icon var like so: - -var/icon/my_icon = new('iconfile.dmi') - -icon/ChangeOpacity(amount = 1) - A very common operation in DM is to try to make an icon more or less transparent. Making an icon more - transparent is usually much easier than making it less so, however. This proc basically is a frontend - for MapColors() which can change opacity any way you like, in much the same way that SetIntensity() - can make an icon lighter or darker. If amount is 0.5, the opacity of the icon will be cut in half. - If amount is 2, opacity is doubled and anything more than half-opaque will become fully opaque. -icon/GrayScale() - Converts the icon to grayscale instead of a fully colored icon. Alpha values are left intact. -icon/ColorTone(tone) - Similar to GrayScale(), this proc converts the icon to a range of black -> tone -> white, where tone is an - RGB color (its alpha is ignored). This can be used to create a sepia tone or similar effect. - See also the global ColorTone() proc. -icon/MinColors(icon) - The icon is blended with a second icon where the minimum of each RGB pixel is the result. - Transparency may increase, as if the icons were blended with ICON_ADD. You may supply a color in place of an icon. -icon/MaxColors(icon) - The icon is blended with a second icon where the maximum of each RGB pixel is the result. - Opacity may increase, as if the icons were blended with ICON_OR. You may supply a color in place of an icon. -icon/Opaque(background = "#000000") - All alpha values are set to 255 throughout the icon. Transparent pixels become black, or whatever background color you specify. -icon/BecomeAlphaMask() - You can convert a simple grayscale icon into an alpha mask to use with other icons very easily with this proc. - The black parts become transparent, the white parts stay white, and anything in between becomes a translucent shade of white. -icon/AddAlphaMask(mask) - The alpha values of the mask icon will be blended with the current icon. Anywhere the mask is opaque, - the current icon is untouched. Anywhere the mask is transparent, the current icon becomes transparent. - Where the mask is translucent, the current icon becomes more transparent. -icon/UseAlphaMask(mask, mode) - Sometimes you may want to take the alpha values from one icon and use them on a different icon. - This proc will do that. Just supply the icon whose alpha mask you want to use, and src will change - so it has the same colors as before but uses the mask for opacity. - -COLOR MANAGEMENT AND HSV - -RGB isn't the only way to represent color. Sometimes it's more useful to work with a model called HSV, which stands for hue, saturation, and value. - - * The hue of a color describes where it is along the color wheel. It goes from red to yellow to green to - cyan to blue to magenta and back to red. - * The saturation of a color is how much color is in it. A color with low saturation will be more gray, - and with no saturation at all it is a shade of gray. - * The value of a color determines how bright it is. A high-value color is vivid, moderate value is dark, - and no value at all is black. - -Just as BYOND uses "#rrggbb" to represent RGB values, a similar format is used for HSV: "#hhhssvv". The hue is three -hex digits because it ranges from 0 to 0x5FF. - - * 0 to 0xFF - red to yellow - * 0x100 to 0x1FF - yellow to green - * 0x200 to 0x2FF - green to cyan - * 0x300 to 0x3FF - cyan to blue - * 0x400 to 0x4FF - blue to magenta - * 0x500 to 0x5FF - magenta to red - -Knowing this, you can figure out that red is "#000ffff" in HSV format, which is hue 0 (red), saturation 255 (as colorful as possible), -value 255 (as bright as possible). Green is "#200ffff" and blue is "#400ffff". - -More than one HSV color can match the same RGB color. - -Here are some procs you can use for color management: - -ReadRGB(rgb) - Takes an RGB string like "#ffaa55" and converts it to a list such as list(255,170,85). If an RGBA format is used - that includes alpha, the list will have a fourth item for the alpha value. -hsv(hue, sat, val, apha) - Counterpart to rgb(), this takes the values you input and converts them to a string in "#hhhssvv" or "#hhhssvvaa" - format. Alpha is not included in the result if null. -ReadHSV(rgb) - Takes an HSV string like "#100FF80" and converts it to a list such as list(256,255,128). If an HSVA format is used that - includes alpha, the list will have a fourth item for the alpha value. -RGBtoHSV(rgb) - Takes an RGB or RGBA string like "#ffaa55" and converts it into an HSV or HSVA color such as "#080aaff". -HSVtoRGB(hsv) - Takes an HSV or HSVA string like "#080aaff" and converts it into an RGB or RGBA color such as "#ff55aa". -BlendRGB(rgb1, rgb2, amount) - Blends between two RGB or RGBA colors using regular RGB blending. If amount is 0, the first color is the result; - if 1, the second color is the result. 0.5 produces an average of the two. Values outside the 0 to 1 range are allowed as well. - The returned value is an RGB or RGBA color. -BlendHSV(hsv1, hsv2, amount) - Blends between two HSV or HSVA colors using HSV blending, which tends to produce nicer results than regular RGB - blending because the brightness of the color is left intact. If amount is 0, the first color is the result; if 1, - the second color is the result. 0.5 produces an average of the two. Values outside the 0 to 1 range are allowed as well. - The returned value is an HSV or HSVA color. -BlendRGBasHSV(rgb1, rgb2, amount) - Like BlendHSV(), but the colors used and the return value are RGB or RGBA colors. The blending is done in HSV form. -HueToAngle(hue) - Converts a hue to an angle range of 0 to 360. Angle 0 is red, 120 is green, and 240 is blue. -AngleToHue(hue) - Converts an angle to a hue in the valid range. -RotateHue(hsv, angle) - Takes an HSV or HSVA value and rotates the hue forward through red, green, and blue by an angle from 0 to 360. - (Rotating red by 60° produces yellow.) The result is another HSV or HSVA color with the same saturation and value - as the original, but a different hue. -GrayScale(rgb) - Takes an RGB or RGBA color and converts it to grayscale. Returns an RGB or RGBA string. -ColorTone(rgb, tone) - Similar to GrayScale(), this proc converts an RGB or RGBA color to a range of black -> tone -> white instead of - using strict shades of gray. The tone value is an RGB color; any alpha value is ignored. -*/ - -/* -Get Flat Icon DEMO by DarkCampainger - -This is a test for the get flat icon proc, modified approprietly for icons and their states. -Probably not a good idea to run this unless you want to see how the proc works in detail. -mob - icon = 'old_or_unused.dmi' - icon_state = "green" - - Login() - // Testing image underlays - underlays += image(icon='old_or_unused.dmi',icon_state="red") - underlays += image(icon='old_or_unused.dmi',icon_state="red", pixel_x = 32) - underlays += image(icon='old_or_unused.dmi',icon_state="red", pixel_x = -32) - - // Testing image overlays - overlays += image(icon='old_or_unused.dmi',icon_state="green", pixel_x = 32, pixel_y = -32) - overlays += image(icon='old_or_unused.dmi',icon_state="green", pixel_x = 32, pixel_y = 32) - overlays += image(icon='old_or_unused.dmi',icon_state="green", pixel_x = -32, pixel_y = -32) - - // Testing icon file overlays (defaults to mob's state) - overlays += '_flat_demoIcons2.dmi' - - // Testing icon_state overlays (defaults to mob's icon) - overlays += "white" - - // Testing dynamic icon overlays - var/icon/I = icon('old_or_unused.dmi', icon_state="aqua") - I.Shift(NORTH,16,1) - overlays+=I - - // Testing dynamic image overlays - I=image(icon=I,pixel_x = -32, pixel_y = 32) - overlays+=I - - // Testing object types (and layers) - overlays+=/obj/effect/overlayTest - - loc = locate (10,10,1) - verb - Browse_Icon() - set name = "1. Browse Icon" - // Give it a name for the cache - var/iconName = "[ckey(src.name)]_flattened.dmi" - // Send the icon to src's local cache - src<

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    ", "\[/h3\]") + + text = replacetext(text, "
  • ", "\[*\]") + text = replacetext(text, "
    ", "\[hr\]") + text = replacetext(text, "", "\[small\]") + text = replacetext(text, "
      ", "\[list\]") + text = replacetext(text, "
    ", "\[/list\]") + text = replacetext(text, "", "\[table\]") + text = replacetext(text, "
    ", "\[/table\]") + text = replacetext(text, "", "\[grid\]") + text = replacetext(text, "
    ", "\[/grid\]") + text = replacetext(text, "", "\[row\]") + text = replacetext(text, "", "\[cell\]") + text = replacetext(text, "", "\[logo\]") + return text + +#define string2charlist(string) (splittext(string, regex("(\\x0A|.)")) - splittext(string, "")) diff --git a/code/__HELPERS/tool_helpers.dm b/code/__HELPERS/tool_helpers.dm new file mode 100644 index 00000000000..15c8e60591e --- /dev/null +++ b/code/__HELPERS/tool_helpers.dm @@ -0,0 +1,51 @@ +//Quick type checks for some tools + +/proc/iswrench(O) + if(istype(O, /obj/item/wrench)) + return TRUE + return FALSE + +/proc/iswelder(O) + if(istype(O, /obj/item/weldingtool)) + return TRUE + return FALSE + +/proc/iswirecutter(O) + if(istype(O, /obj/item/wirecutters)) + return TRUE + return FALSE + +/proc/isscrewdriver(O) + if(istype(O, /obj/item/screwdriver)) + return TRUE + return FALSE + +/proc/ismultitool(O) + if(istype(O, /obj/item/multitool)) + return TRUE + return FALSE + +/proc/iscrowbar(O) + if(istype(O, /obj/item/crowbar)) + return TRUE + return FALSE + +/proc/iscoil(O) + if(istype(O, /obj/item/stack/cable_coil)) + return TRUE + return FALSE + +/proc/ispowertool(O)//used to check if a tool can force powered doors + if(istype(O, /obj/item/crowbar/power) || istype(O, /obj/item/mecha_parts/mecha_equipment/medical/rescue_jaw)) + return TRUE + return FALSE + +/proc/is_surgery_tool(obj/item/W as obj) + return ( \ + istype(W, /obj/item/scalpel) || \ + istype(W, /obj/item/hemostat) || \ + istype(W, /obj/item/retractor) || \ + istype(W, /obj/item/cautery) || \ + istype(W, /obj/item/bonegel) || \ + istype(W, /obj/item/bonesetter) + ) diff --git a/code/__HELPERS/traits.dm b/code/__HELPERS/traits.dm index 7c1d2257e27..467b07a0ddf 100644 --- a/code/__HELPERS/traits.dm +++ b/code/__HELPERS/traits.dm @@ -65,4 +65,4 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_WATERBREATH "waterbreathing" // common trait sources -#define ROUNDSTART_TRAIT "roundstart" //cannot be removed without admin intervention \ No newline at end of file +#define ROUNDSTART_TRAIT "roundstart" //cannot be removed without admin intervention diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm index ae7704459b5..661d09f0412 100644 --- a/code/__HELPERS/type2type.dm +++ b/code/__HELPERS/type2type.dm @@ -1,393 +1,393 @@ -/* - * Holds procs designed to change one type of value, into another. - * Contains: - * hex2num & num2hex - * file2list - * angle2dir - * angle2text - * worldtime2text - */ - -//Returns an integer given a hex input -/proc/hex2num(hex) - if(!(istext(hex))) - return - - var/num = 0 - var/power = 0 - var/i = null - i = length(hex) - while(i > 0) - var/char = copytext(hex, i, i + 1) - switch(char) - if("0") - //Apparently, switch works with empty statements, yay! If that doesn't work, blame me, though. -- Urist - if("9", "8", "7", "6", "5", "4", "3", "2", "1") - num += text2num(char) * 16 ** power - if("a", "A") - num += 16 ** power * 10 - if("b", "B") - num += 16 ** power * 11 - if("c", "C") - num += 16 ** power * 12 - if("d", "D") - num += 16 ** power * 13 - if("e", "E") - num += 16 ** power * 14 - if("f", "F") - num += 16 ** power * 15 - else - return - power++ - i-- - return num - -//Returns the hex value of a number given a value assumed to be a base-ten value -/proc/num2hex(num, placeholder) - if(!isnum(num)) return - if(placeholder == null) placeholder = 2 - - var/hex = "" - while(num) - var/val = num % 16 - num = round(num / 16) - - if(val > 9) - val = ascii2text(55 + val) // 65 - 70 correspond to "A" - "F" - hex = "[val][hex]" - while(length(hex) < placeholder) - hex = "0[hex]" - return hex || "0" - -//Returns an integer value for R of R/G/B given a hex color input. -/proc/color2R(hex) - if(!(istext(hex))) - return - - return hex2num(copytext(hex, 2, 4)) //Returning R - -//Returns an integer value for G of R/G/B given a hex color input. -/proc/color2G(hex) - if(!(istext(hex))) - return - - return hex2num(copytext(hex, 4, 6)) //Returning G - -//Returns an integer value for B of R/G/B given a hex color input. -/proc/color2B(hex) - if(!(istext(hex))) - return - - return hex2num(copytext(hex, 6, 8)) //Returning B - -/proc/text2numlist(text, delimiter="\n") - var/list/num_list = list() - for(var/x in splittext(text, delimiter)) - num_list += text2num(x) - return num_list - -//Splits the text of a file at seperator and returns them in a list. -/proc/file2list(filename, seperator="\n") - return splittext(return_file_text(filename),seperator) - - -//Turns a direction into text - -/proc/num2dir(direction) - switch(direction) - if(1.0) return NORTH - if(2.0) return SOUTH - if(4.0) return EAST - if(8.0) return WEST - else - log_runtime(EXCEPTION("UNKNOWN DIRECTION: [direction]")) - -/proc/dir2text(direction) - switch(direction) - if(1.0) - return "north" - if(2.0) - return "south" - if(4.0) - return "east" - if(8.0) - return "west" - if(5.0) - return "northeast" - if(6.0) - return "southeast" - if(9.0) - return "northwest" - if(10.0) - return "southwest" - else - return - -//Turns text into proper directions -/proc/text2dir(direction) - switch(uppertext(direction)) - if("NORTH") - return 1 - if("SOUTH") - return 2 - if("EAST") - return 4 - if("WEST") - return 8 - if("NORTHEAST") - return 5 - if("NORTHWEST") - return 9 - if("SOUTHEAST") - return 6 - if("SOUTHWEST") - return 10 - else - return - -//Converts an angle (degrees) into an ss13 direction -/proc/angle2dir(var/degree) - degree = ((degree+22.5)%365) - if(degree < 45) return NORTH - if(degree < 90) return NORTHEAST - if(degree < 135) return EAST - if(degree < 180) return SOUTHEAST - if(degree < 225) return SOUTH - if(degree < 270) return SOUTHWEST - if(degree < 315) return WEST - return NORTH|WEST - -/proc/angle2dir_cardinal(angle) - switch(round(angle, 0.1)) - if(315.5 to 360, 0 to 45.5) - return NORTH - if(45.6 to 135.5) - return EAST - if(135.6 to 225.5) - return SOUTH - if(225.6 to 315.5) - return WEST - -//returns the north-zero clockwise angle in degrees, given a direction - -/proc/dir2angle(var/D) - switch(D) - if(NORTH) return 0 - if(SOUTH) return 180 - if(EAST) return 90 - if(WEST) return 270 - if(NORTHEAST) return 45 - if(SOUTHEAST) return 135 - if(NORTHWEST) return 315 - if(SOUTHWEST) return 225 - else return null - -//Returns the angle in english -/proc/angle2text(var/degree) - return dir2text(angle2dir(degree)) - -//Converts a blend_mode constant to one acceptable to icon.Blend() -/proc/blendMode2iconMode(blend_mode) - switch(blend_mode) - if(BLEND_MULTIPLY) return ICON_MULTIPLY - if(BLEND_ADD) return ICON_ADD - if(BLEND_SUBTRACT) return ICON_SUBTRACT - else return ICON_OVERLAY - -//Converts a rights bitfield into a string -/proc/rights2text(rights,seperator="") - if(rights & R_BUILDMODE) . += "[seperator]+BUILDMODE" - if(rights & R_ADMIN) . += "[seperator]+ADMIN" - if(rights & R_BAN) . += "[seperator]+BAN" - if(rights & R_EVENT) . += "[seperator]+EVENT" - if(rights & R_SERVER) . += "[seperator]+SERVER" - if(rights & R_DEBUG) . += "[seperator]+DEBUG" - if(rights & R_POSSESS) . += "[seperator]+POSSESS" - if(rights & R_PERMISSIONS) . += "[seperator]+PERMISSIONS" - if(rights & R_STEALTH) . += "[seperator]+STEALTH" - if(rights & R_REJUVINATE) . += "[seperator]+REJUVINATE" - if(rights & R_VAREDIT) . += "[seperator]+VAREDIT" - if(rights & R_SOUNDS) . += "[seperator]+SOUND" - if(rights & R_SPAWN) . += "[seperator]+SPAWN" - if(rights & R_PROCCALL) . += "[seperator]+PROCCALL" - if(rights & R_MOD) . += "[seperator]+MODERATOR" - if(rights & R_MENTOR) . += "[seperator]+MENTOR" - return . - -/proc/ui_style2icon(ui_style) - switch(ui_style) - if("Retro") - return 'icons/mob/screen_retro.dmi' - if("Plasmafire") - return 'icons/mob/screen_plasmafire.dmi' - if("Slimecore") - return 'icons/mob/screen_slimecore.dmi' - if("Operative") - return 'icons/mob/screen_operative.dmi' - if("White") - return 'icons/mob/screen_white.dmi' - else - return 'icons/mob/screen_midnight.dmi' - -//colour formats -/proc/rgb2hsl(red, green, blue) - red /= 255;green /= 255;blue /= 255; - var/max = max(red,green,blue) - var/min = min(red,green,blue) - var/range = max-min - - var/hue=0;var/saturation=0;var/lightness=0; - lightness = (max + min)/2 - if(range != 0) - if(lightness < 0.5) saturation = range/(max+min) - else saturation = range/(2-max-min) - - var/dred = ((max-red)/(6*max)) + 0.5 - var/dgreen = ((max-green)/(6*max)) + 0.5 - var/dblue = ((max-blue)/(6*max)) + 0.5 - - if(max==red) hue = dblue - dgreen - else if(max==green) hue = dred - dblue + (1/3) - else hue = dgreen - dred + (2/3) - if(hue < 0) hue++ - else if(hue > 1) hue-- - - return list(hue, saturation, lightness) - -/proc/hsl2rgb(hue, saturation, lightness) - var/red;var/green;var/blue; - if(saturation == 0) - red = lightness * 255 - green = red - blue = red - else - var/a;var/b; - if(lightness < 0.5) b = lightness*(1+saturation) - else b = (lightness+saturation) - (saturation*lightness) - a = 2*lightness - b - - red = round(255 * hue2rgb(a, b, hue+(1/3))) - green = round(255 * hue2rgb(a, b, hue)) - blue = round(255 * hue2rgb(a, b, hue-(1/3))) - - return list(red, green, blue) - -/proc/hue2rgb(a, b, hue) - if(hue < 0) hue++ - else if(hue > 1) hue-- - if(6*hue < 1) return (a+(b-a)*6*hue) - if(2*hue < 1) return b - if(3*hue < 2) return (a+(b-a)*((2/3)-hue)*6) - return a - -/proc/num2septext(var/theNum, var/sigFig = 7,var/sep=",") // default sigFig (1,000,000) - var/finalNum = num2text(theNum, sigFig) - - // Start from the end, or from the decimal point - var/end = findtextEx(finalNum, ".") || length(finalNum) + 1 - - // Moving towards start of string, insert comma every 3 characters - for(var/pos = end - 3, pos > 1, pos -= 3) - finalNum = copytext(finalNum, 1, pos) + sep + copytext(finalNum, pos) - - return finalNum - - -// heat2color functions. Adapted from: http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/ -/proc/heat2color(temp) - return rgb(heat2color_r(temp), heat2color_g(temp), heat2color_b(temp)) - -/proc/heat2color_r(temp) - temp /= 100 - if(temp <= 66) - . = 255 - else - . = max(0, min(255, 329.698727446 * (temp - 60) ** -0.1332047592)) - -/proc/heat2color_g(temp) - temp /= 100 - if(temp <= 66) - . = max(0, min(255, 99.4708025861 * log(temp) - 161.1195681661)) - else - . = max(0, min(255, 288.1221695283 * ((temp - 60) ** -0.0755148492))) - -/proc/heat2color_b(temp) - temp /= 100 - if(temp >= 66) - . = 255 - else - if(temp <= 16) - . = 0 - else - . = max(0, min(255, 138.5177312231 * log(temp - 10) - 305.0447927307)) - -//Argument: Give this a space-separated string consisting of 6 numbers. Returns null if you don't -/proc/text2matrix(var/matrixtext) - var/list/matrixtext_list = splittext(matrixtext, " ") - var/list/matrix_list = list() - for(var/item in matrixtext_list) - var/entry = text2num(item) - if(entry == null) - return null - matrix_list += entry - if(matrix_list.len < 6) - return null - var/a = matrix_list[1] - var/b = matrix_list[2] - var/c = matrix_list[3] - var/d = matrix_list[4] - var/e = matrix_list[5] - var/f = matrix_list[6] - return matrix(a, b, c, d, e, f) - - -//This is a weird one: -//It returns a list of all var names found in the string -//These vars must be in the [var_name] format -//It's only a proc because it's used in more than one place - -//Takes a string and a datum -//The string is well, obviously the string being checked -//The datum is used as a source for var names, to check validity -//Otherwise every single word could technically be a variable! -/proc/string2listofvars(var/t_string, var/datum/var_source) - if(!t_string || !var_source) - return list() - - . = list() - - var/var_found = findtext(t_string, "\[") //Not the actual variables, just a generic "should we even bother" check - if(var_found) - //Find var names - - // "A dog said hi [name]!" - // splittext() --> list("A dog said hi ","name]!" - // jointext() --> "A dog said hi name]!" - // splittext() --> list("A","dog","said","hi","name]!") - - t_string = replacetext(t_string, "\[", "\[ ")//Necessary to resolve "word[var_name]" scenarios - var/list/list_value = splittext(t_string, "\[") - var/intermediate_stage = jointext(list_value, null) - - list_value = splittext(intermediate_stage, " ") - for(var/value in list_value) - if(findtext(value, "]")) - value = splittext(value, "]") //"name]!" --> list("name","!") - for(var/A in value) - if(var_source.vars.Find(A)) - . += A - -/proc/type2parent(child) - var/string_type = "[child]" - var/last_slash = findlasttext(string_type, "/") - if(last_slash == 1) - switch(child) - if(/datum) - return null - if(/obj || /mob) - return /atom/movable - if(/area || /turf) - return /atom - else - return /datum - return text2path(copytext(string_type, 1, last_slash)) \ No newline at end of file +/* + * Holds procs designed to change one type of value, into another. + * Contains: + * hex2num & num2hex + * file2list + * angle2dir + * angle2text + * worldtime2text + */ + +//Returns an integer given a hex input +/proc/hex2num(hex) + if(!(istext(hex))) + return + + var/num = 0 + var/power = 0 + var/i = null + i = length(hex) + while(i > 0) + var/char = copytext(hex, i, i + 1) + switch(char) + if("0") + //Apparently, switch works with empty statements, yay! If that doesn't work, blame me, though. -- Urist + if("9", "8", "7", "6", "5", "4", "3", "2", "1") + num += text2num(char) * 16 ** power + if("a", "A") + num += 16 ** power * 10 + if("b", "B") + num += 16 ** power * 11 + if("c", "C") + num += 16 ** power * 12 + if("d", "D") + num += 16 ** power * 13 + if("e", "E") + num += 16 ** power * 14 + if("f", "F") + num += 16 ** power * 15 + else + return + power++ + i-- + return num + +//Returns the hex value of a number given a value assumed to be a base-ten value +/proc/num2hex(num, placeholder) + if(!isnum(num)) return + if(placeholder == null) placeholder = 2 + + var/hex = "" + while(num) + var/val = num % 16 + num = round(num / 16) + + if(val > 9) + val = ascii2text(55 + val) // 65 - 70 correspond to "A" - "F" + hex = "[val][hex]" + while(length(hex) < placeholder) + hex = "0[hex]" + return hex || "0" + +//Returns an integer value for R of R/G/B given a hex color input. +/proc/color2R(hex) + if(!(istext(hex))) + return + + return hex2num(copytext(hex, 2, 4)) //Returning R + +//Returns an integer value for G of R/G/B given a hex color input. +/proc/color2G(hex) + if(!(istext(hex))) + return + + return hex2num(copytext(hex, 4, 6)) //Returning G + +//Returns an integer value for B of R/G/B given a hex color input. +/proc/color2B(hex) + if(!(istext(hex))) + return + + return hex2num(copytext(hex, 6, 8)) //Returning B + +/proc/text2numlist(text, delimiter="\n") + var/list/num_list = list() + for(var/x in splittext(text, delimiter)) + num_list += text2num(x) + return num_list + +//Splits the text of a file at seperator and returns them in a list. +/proc/file2list(filename, seperator="\n") + return splittext(return_file_text(filename),seperator) + + +//Turns a direction into text + +/proc/num2dir(direction) + switch(direction) + if(1.0) return NORTH + if(2.0) return SOUTH + if(4.0) return EAST + if(8.0) return WEST + else + log_runtime(EXCEPTION("UNKNOWN DIRECTION: [direction]")) + +/proc/dir2text(direction) + switch(direction) + if(1.0) + return "north" + if(2.0) + return "south" + if(4.0) + return "east" + if(8.0) + return "west" + if(5.0) + return "northeast" + if(6.0) + return "southeast" + if(9.0) + return "northwest" + if(10.0) + return "southwest" + else + return + +//Turns text into proper directions +/proc/text2dir(direction) + switch(uppertext(direction)) + if("NORTH") + return 1 + if("SOUTH") + return 2 + if("EAST") + return 4 + if("WEST") + return 8 + if("NORTHEAST") + return 5 + if("NORTHWEST") + return 9 + if("SOUTHEAST") + return 6 + if("SOUTHWEST") + return 10 + else + return + +//Converts an angle (degrees) into an ss13 direction +/proc/angle2dir(var/degree) + degree = ((degree+22.5)%365) + if(degree < 45) return NORTH + if(degree < 90) return NORTHEAST + if(degree < 135) return EAST + if(degree < 180) return SOUTHEAST + if(degree < 225) return SOUTH + if(degree < 270) return SOUTHWEST + if(degree < 315) return WEST + return NORTH|WEST + +/proc/angle2dir_cardinal(angle) + switch(round(angle, 0.1)) + if(315.5 to 360, 0 to 45.5) + return NORTH + if(45.6 to 135.5) + return EAST + if(135.6 to 225.5) + return SOUTH + if(225.6 to 315.5) + return WEST + +//returns the north-zero clockwise angle in degrees, given a direction + +/proc/dir2angle(var/D) + switch(D) + if(NORTH) return 0 + if(SOUTH) return 180 + if(EAST) return 90 + if(WEST) return 270 + if(NORTHEAST) return 45 + if(SOUTHEAST) return 135 + if(NORTHWEST) return 315 + if(SOUTHWEST) return 225 + else return null + +//Returns the angle in english +/proc/angle2text(var/degree) + return dir2text(angle2dir(degree)) + +//Converts a blend_mode constant to one acceptable to icon.Blend() +/proc/blendMode2iconMode(blend_mode) + switch(blend_mode) + if(BLEND_MULTIPLY) return ICON_MULTIPLY + if(BLEND_ADD) return ICON_ADD + if(BLEND_SUBTRACT) return ICON_SUBTRACT + else return ICON_OVERLAY + +//Converts a rights bitfield into a string +/proc/rights2text(rights,seperator="") + if(rights & R_BUILDMODE) . += "[seperator]+BUILDMODE" + if(rights & R_ADMIN) . += "[seperator]+ADMIN" + if(rights & R_BAN) . += "[seperator]+BAN" + if(rights & R_EVENT) . += "[seperator]+EVENT" + if(rights & R_SERVER) . += "[seperator]+SERVER" + if(rights & R_DEBUG) . += "[seperator]+DEBUG" + if(rights & R_POSSESS) . += "[seperator]+POSSESS" + if(rights & R_PERMISSIONS) . += "[seperator]+PERMISSIONS" + if(rights & R_STEALTH) . += "[seperator]+STEALTH" + if(rights & R_REJUVINATE) . += "[seperator]+REJUVINATE" + if(rights & R_VAREDIT) . += "[seperator]+VAREDIT" + if(rights & R_SOUNDS) . += "[seperator]+SOUND" + if(rights & R_SPAWN) . += "[seperator]+SPAWN" + if(rights & R_PROCCALL) . += "[seperator]+PROCCALL" + if(rights & R_MOD) . += "[seperator]+MODERATOR" + if(rights & R_MENTOR) . += "[seperator]+MENTOR" + return . + +/proc/ui_style2icon(ui_style) + switch(ui_style) + if("Retro") + return 'icons/mob/screen_retro.dmi' + if("Plasmafire") + return 'icons/mob/screen_plasmafire.dmi' + if("Slimecore") + return 'icons/mob/screen_slimecore.dmi' + if("Operative") + return 'icons/mob/screen_operative.dmi' + if("White") + return 'icons/mob/screen_white.dmi' + else + return 'icons/mob/screen_midnight.dmi' + +//colour formats +/proc/rgb2hsl(red, green, blue) + red /= 255;green /= 255;blue /= 255; + var/max = max(red,green,blue) + var/min = min(red,green,blue) + var/range = max-min + + var/hue=0;var/saturation=0;var/lightness=0; + lightness = (max + min)/2 + if(range != 0) + if(lightness < 0.5) saturation = range/(max+min) + else saturation = range/(2-max-min) + + var/dred = ((max-red)/(6*max)) + 0.5 + var/dgreen = ((max-green)/(6*max)) + 0.5 + var/dblue = ((max-blue)/(6*max)) + 0.5 + + if(max==red) hue = dblue - dgreen + else if(max==green) hue = dred - dblue + (1/3) + else hue = dgreen - dred + (2/3) + if(hue < 0) hue++ + else if(hue > 1) hue-- + + return list(hue, saturation, lightness) + +/proc/hsl2rgb(hue, saturation, lightness) + var/red;var/green;var/blue; + if(saturation == 0) + red = lightness * 255 + green = red + blue = red + else + var/a;var/b; + if(lightness < 0.5) b = lightness*(1+saturation) + else b = (lightness+saturation) - (saturation*lightness) + a = 2*lightness - b + + red = round(255 * hue2rgb(a, b, hue+(1/3))) + green = round(255 * hue2rgb(a, b, hue)) + blue = round(255 * hue2rgb(a, b, hue-(1/3))) + + return list(red, green, blue) + +/proc/hue2rgb(a, b, hue) + if(hue < 0) hue++ + else if(hue > 1) hue-- + if(6*hue < 1) return (a+(b-a)*6*hue) + if(2*hue < 1) return b + if(3*hue < 2) return (a+(b-a)*((2/3)-hue)*6) + return a + +/proc/num2septext(var/theNum, var/sigFig = 7,var/sep=",") // default sigFig (1,000,000) + var/finalNum = num2text(theNum, sigFig) + + // Start from the end, or from the decimal point + var/end = findtextEx(finalNum, ".") || length(finalNum) + 1 + + // Moving towards start of string, insert comma every 3 characters + for(var/pos = end - 3, pos > 1, pos -= 3) + finalNum = copytext(finalNum, 1, pos) + sep + copytext(finalNum, pos) + + return finalNum + + +// heat2color functions. Adapted from: http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/ +/proc/heat2color(temp) + return rgb(heat2color_r(temp), heat2color_g(temp), heat2color_b(temp)) + +/proc/heat2color_r(temp) + temp /= 100 + if(temp <= 66) + . = 255 + else + . = max(0, min(255, 329.698727446 * (temp - 60) ** -0.1332047592)) + +/proc/heat2color_g(temp) + temp /= 100 + if(temp <= 66) + . = max(0, min(255, 99.4708025861 * log(temp) - 161.1195681661)) + else + . = max(0, min(255, 288.1221695283 * ((temp - 60) ** -0.0755148492))) + +/proc/heat2color_b(temp) + temp /= 100 + if(temp >= 66) + . = 255 + else + if(temp <= 16) + . = 0 + else + . = max(0, min(255, 138.5177312231 * log(temp - 10) - 305.0447927307)) + +//Argument: Give this a space-separated string consisting of 6 numbers. Returns null if you don't +/proc/text2matrix(var/matrixtext) + var/list/matrixtext_list = splittext(matrixtext, " ") + var/list/matrix_list = list() + for(var/item in matrixtext_list) + var/entry = text2num(item) + if(entry == null) + return null + matrix_list += entry + if(matrix_list.len < 6) + return null + var/a = matrix_list[1] + var/b = matrix_list[2] + var/c = matrix_list[3] + var/d = matrix_list[4] + var/e = matrix_list[5] + var/f = matrix_list[6] + return matrix(a, b, c, d, e, f) + + +//This is a weird one: +//It returns a list of all var names found in the string +//These vars must be in the [var_name] format +//It's only a proc because it's used in more than one place + +//Takes a string and a datum +//The string is well, obviously the string being checked +//The datum is used as a source for var names, to check validity +//Otherwise every single word could technically be a variable! +/proc/string2listofvars(var/t_string, var/datum/var_source) + if(!t_string || !var_source) + return list() + + . = list() + + var/var_found = findtext(t_string, "\[") //Not the actual variables, just a generic "should we even bother" check + if(var_found) + //Find var names + + // "A dog said hi [name]!" + // splittext() --> list("A dog said hi ","name]!" + // jointext() --> "A dog said hi name]!" + // splittext() --> list("A","dog","said","hi","name]!") + + t_string = replacetext(t_string, "\[", "\[ ")//Necessary to resolve "word[var_name]" scenarios + var/list/list_value = splittext(t_string, "\[") + var/intermediate_stage = jointext(list_value, null) + + list_value = splittext(intermediate_stage, " ") + for(var/value in list_value) + if(findtext(value, "]")) + value = splittext(value, "]") //"name]!" --> list("name","!") + for(var/A in value) + if(var_source.vars.Find(A)) + . += A + +/proc/type2parent(child) + var/string_type = "[child]" + var/last_slash = findlasttext(string_type, "/") + if(last_slash == 1) + switch(child) + if(/datum) + return null + if(/obj || /mob) + return /atom/movable + if(/area || /turf) + return /atom + else + return /datum + return text2path(copytext(string_type, 1, last_slash)) diff --git a/code/__HELPERS/typelists.dm b/code/__HELPERS/typelists.dm index f271b9204de..3519eb60f3a 100644 --- a/code/__HELPERS/typelists.dm +++ b/code/__HELPERS/typelists.dm @@ -40,4 +40,4 @@ GLOBAL_LIST_EMPTY(typelistkeys) for (var/saving in savings) to_chat(world, "Savings for [saving]: [savings[saving]] lists, [saveditems[saving]] items") -#endif \ No newline at end of file +#endif diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index f1611b38fd9..45283498ebd 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -1142,70 +1142,9 @@ var/list/can_embed_types = typecacheof(list( if(is_type_in_typecache(W, can_embed_types)) return 1 -//Quick type checks for some tools -var/global/list/common_tools = list( -/obj/item/stack/cable_coil, -/obj/item/wrench, -/obj/item/weldingtool, -/obj/item/screwdriver, -/obj/item/wirecutters, -/obj/item/multitool, -/obj/item/crowbar) - -/proc/istool(O) - if(O && is_type_in_list(O, common_tools)) - return 1 - return 0 - -/proc/iswrench(O) - if(istype(O, /obj/item/wrench)) - return 1 - return 0 - -/proc/iswelder(O) - if(istype(O, /obj/item/weldingtool)) - return 1 - return 0 - -/proc/iscoil(O) - if(istype(O, /obj/item/stack/cable_coil)) - return 1 - return 0 - -/proc/iswirecutter(O) - if(istype(O, /obj/item/wirecutters)) - return 1 - return 0 - -/proc/isscrewdriver(O) - if(istype(O, /obj/item/screwdriver)) - return 1 - return 0 - -/proc/ismultitool(O) - if(istype(O, /obj/item/multitool)) - return 1 - return 0 - -/proc/iscrowbar(O) - if(istype(O, /obj/item/crowbar)) - return 1 - return 0 - -/proc/ispowertool(O)//used to check if a tool can force powered doors - if(istype(O, /obj/item/crowbar/power) || istype(O, /obj/item/mecha_parts/mecha_equipment/medical/rescue_jaw)) - return TRUE - return FALSE - -/proc/iswire(O) - if(istype(O, /obj/item/stack/cable_coil)) - return 1 - return 0 - /proc/is_hot(obj/item/W as obj) - if(istype(W, /obj/item/weldingtool)) - var/obj/item/weldingtool/O = W - if(O.isOn()) + if(W.tool_behaviour == TOOL_WELDER) + if(W.tool_enabled) return 2500 else return 0 @@ -1260,16 +1199,6 @@ var/global/list/common_tools = list( return 1 return 0 -/proc/is_surgery_tool(obj/item/W as obj) - return ( \ - istype(W, /obj/item/scalpel) || \ - istype(W, /obj/item/hemostat) || \ - istype(W, /obj/item/retractor) || \ - istype(W, /obj/item/cautery) || \ - istype(W, /obj/item/bonegel) || \ - istype(W, /obj/item/bonesetter) - ) - /proc/reverse_direction(var/dir) switch(dir) if(NORTH) diff --git a/code/_compile_options.dm b/code/_compile_options.dm index 968a2f4d577..bd7e6484bdf 100644 --- a/code/_compile_options.dm +++ b/code/_compile_options.dm @@ -28,4 +28,4 @@ This may require updating to a beta release. #endif // Macros that must exist before world.dm -#define to_chat to_chat_filename=__FILE__;to_chat_line=__LINE__;to_chat_src=src;__to_chat \ No newline at end of file +#define to_chat to_chat_filename=__FILE__;to_chat_line=__LINE__;to_chat_src=src;__to_chat diff --git a/code/_globalvars/configuration.dm b/code/_globalvars/configuration.dm index 3d7259eef55..ee3f6a73a92 100644 --- a/code/_globalvars/configuration.dm +++ b/code/_globalvars/configuration.dm @@ -32,7 +32,7 @@ var/Debug = 0 // global debug switch var/Debug2 = 1 // enables detailed job debug file in secrets //This was a define, but I changed it to a variable so it can be changed in-game.(kept the all-caps definition because... code...) -Errorage -var/MAX_EX_DEVESTATION_RANGE = 3 +var/MAX_EX_DEVASTATION_RANGE = 3 var/MAX_EX_HEAVY_RANGE = 7 var/MAX_EX_LIGHT_RANGE = 14 var/MAX_EX_FLASH_RANGE = 14 diff --git a/code/_globalvars/lists/names.dm b/code/_globalvars/lists/names.dm index feadeece28f..744e595ad3b 100644 --- a/code/_globalvars/lists/names.dm +++ b/code/_globalvars/lists/names.dm @@ -21,4 +21,4 @@ GLOBAL_LIST_INIT(nightmare_strings, file2list("config/names/nightmares.txt")) //loaded on startup because of " //would include in rsc if ' was used -GLOBAL_LIST_INIT(vox_name_syllables, list("ti","hi","ki","ya","ta","ha","ka","ya","chi","cha","kah")) \ No newline at end of file +GLOBAL_LIST_INIT(vox_name_syllables, list("ti","hi","ki","ya","ta","ha","ka","ya","chi","cha","kah")) diff --git a/code/_globalvars/lists/objects.dm b/code/_globalvars/lists/objects.dm index f383ba3fc33..931a0e394e8 100644 --- a/code/_globalvars/lists/objects.dm +++ b/code/_globalvars/lists/objects.dm @@ -51,4 +51,4 @@ GLOBAL_LIST_EMPTY(mob_spawners) // All mob_spawn objects GLOBAL_LIST_EMPTY(explosive_walls) -GLOBAL_LIST_EMPTY(engine_beacon_list) \ No newline at end of file +GLOBAL_LIST_EMPTY(engine_beacon_list) diff --git a/code/_globalvars/lists/reagents.dm b/code/_globalvars/lists/reagents.dm index 981eb63aa7e..20620d60800 100644 --- a/code/_globalvars/lists/reagents.dm +++ b/code/_globalvars/lists/reagents.dm @@ -63,4 +63,4 @@ GLOBAL_LIST_INIT(blocked_chems, list("polonium", "initropidril", "concentrated_i GLOBAL_LIST_INIT(safe_chem_list, list("antihol", "charcoal", "epinephrine", "insulin", "teporone","silver_sulfadiazine", "salbutamol", "omnizine", "stimulants", "synaptizine", "potass_iodide", "oculine", "mannitol", "styptic_powder", "spaceacillin", "salglu_solution", "sal_acid", "cryoxadone", "blood", "synthflesh", "hydrocodone", - "mitocholide", "rezadone")) \ No newline at end of file + "mitocholide", "rezadone")) diff --git a/code/_globalvars/lists/typecache.dm b/code/_globalvars/lists/typecache.dm index 2600e5c287f..96807214f2f 100644 --- a/code/_globalvars/lists/typecache.dm +++ b/code/_globalvars/lists/typecache.dm @@ -9,4 +9,4 @@ GLOBAL_LIST_INIT(typecache_living, typecacheof(/mob/living)) GLOBAL_LIST_INIT(typecache_stack, typecacheof(/obj/item/stack)) -GLOBAL_LIST_INIT(typecache_machine_or_structure, typecacheof(list(/obj/machinery, /obj/structure))) \ No newline at end of file +GLOBAL_LIST_INIT(typecache_machine_or_structure, typecacheof(list(/obj/machinery, /obj/structure))) diff --git a/code/_globalvars/mapping.dm b/code/_globalvars/mapping.dm index c1efb29075d..9c54212cbb7 100644 --- a/code/_globalvars/mapping.dm +++ b/code/_globalvars/mapping.dm @@ -52,4 +52,4 @@ var/list/datum/map_template/ruins_templates = list() var/list/datum/map_template/space_ruins_templates = list() var/list/datum/map_template/lava_ruins_templates = list() var/list/datum/map_template/shelter_templates = list() -var/list/datum/map_template/shuttle_templates = list() \ No newline at end of file +var/list/datum/map_template/shuttle_templates = list() diff --git a/code/_globalvars/misc.dm b/code/_globalvars/misc.dm index f4cdd766cb9..04db7233401 100644 --- a/code/_globalvars/misc.dm +++ b/code/_globalvars/misc.dm @@ -90,4 +90,4 @@ GLOBAL_VAR(map_name) // Self explanatory var/global/datum/datacore/data_core = null // Station datacore, manifest, etc -GLOBAL_VAR_INIT(panic_bunker_enabled, 0) // Is the panic bunker enabled \ No newline at end of file +GLOBAL_VAR_INIT(panic_bunker_enabled, FALSE) // Is the panic bunker enabled diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm index d545a2aac1a..9e0d45643be 100644 --- a/code/_globalvars/traits.dm +++ b/code/_globalvars/traits.dm @@ -16,4 +16,4 @@ GLOBAL_LIST(trait_name_map) for(var/key in GLOB.traits_by_type) for(var/tname in GLOB.traits_by_type[key]) var/val = GLOB.traits_by_type[key][tname] - .[val] = tname \ No newline at end of file + .[val] = tname diff --git a/code/_globalvars/unused.dm b/code/_globalvars/unused.dm index 2e3f8f9c6dc..6e2075559b8 100644 --- a/code/_globalvars/unused.dm +++ b/code/_globalvars/unused.dm @@ -1 +1 @@ -var/going = 1.0 \ No newline at end of file +var/going = 1.0 diff --git a/code/_onclick/adjacent.dm b/code/_onclick/adjacent.dm index 038174388ff..58cfdf477f2 100644 --- a/code/_onclick/adjacent.dm +++ b/code/_onclick/adjacent.dm @@ -96,4 +96,4 @@ else if( !border_only ) // dense, not on border, cannot pass over return 0 - return 1 \ No newline at end of file + return 1 diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm index 8d58245eda5..7c2214e2287 100644 --- a/code/_onclick/ai.dm +++ b/code/_onclick/ai.dm @@ -42,8 +42,8 @@ if(control_disabled || stat) return - - var/turf/pixel_turf = get_turf_pixel(A) + + var/turf/pixel_turf = isturf(A) ? A : get_turf_pixel(A) if(isnull(pixel_turf)) return if(!can_see(A)) diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index ea9f1663ed0..6bfd72413de 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -447,4 +447,4 @@ params += "&catcher=1" if(T) T.Click(location, control, params) - . = 1 \ No newline at end of file + . = 1 diff --git a/code/_onclick/click_override.dm b/code/_onclick/click_override.dm index a6042386b3d..481d30da4b6 100644 --- a/code/_onclick/click_override.dm +++ b/code/_onclick/click_override.dm @@ -89,4 +89,4 @@ A = target_atom next_shocked.Cut() - P.last_shocked = world.time \ No newline at end of file + P.last_shocked = world.time diff --git a/code/_onclick/hud/ai.dm b/code/_onclick/hud/ai.dm index dc98d887279..ada0ce8cd30 100644 --- a/code/_onclick/hud/ai.dm +++ b/code/_onclick/hud/ai.dm @@ -262,4 +262,4 @@ using = new /obj/screen/act_intent/robot/AI() using.icon_state = mymob.a_intent static_inventory += using - action_intent = using \ No newline at end of file + action_intent = using diff --git a/code/_onclick/hud/blob_overmind.dm b/code/_onclick/hud/blob_overmind.dm index c1f14fc625d..01940be1d8b 100644 --- a/code/_onclick/hud/blob_overmind.dm +++ b/code/_onclick/hud/blob_overmind.dm @@ -196,4 +196,4 @@ using = new /obj/screen/blob/Split() using.screen_loc = ui_acti - static_inventory += using \ No newline at end of file + static_inventory += using diff --git a/code/_onclick/hud/bot.dm b/code/_onclick/hud/bot.dm index 5f4ddbd7622..12320f2da3e 100644 --- a/code/_onclick/hud/bot.dm +++ b/code/_onclick/hud/bot.dm @@ -30,4 +30,4 @@ mymob.pullin.icon = 'icons/mob/screen_bot.dmi' mymob.pullin.update_icon(mymob) mymob.pullin.screen_loc = ui_bot_pull - static_inventory += mymob.pullin \ No newline at end of file + static_inventory += mymob.pullin diff --git a/code/_onclick/hud/constructs.dm b/code/_onclick/hud/constructs.dm index 7ed1d66724f..0effd812b2d 100644 --- a/code/_onclick/hud/constructs.dm +++ b/code/_onclick/hud/constructs.dm @@ -60,4 +60,4 @@ mymob.pullin.icon = 'icons/mob/screen_construct.dmi' mymob.pullin.icon_state = "pull0" mymob.pullin.name = "pull" - mymob.pullin.screen_loc = ui_construct_pull \ No newline at end of file + mymob.pullin.screen_loc = ui_construct_pull diff --git a/code/_onclick/hud/guardian.dm b/code/_onclick/hud/guardian.dm index cc1ac13931f..d628124e052 100644 --- a/code/_onclick/hud/guardian.dm +++ b/code/_onclick/hud/guardian.dm @@ -1,94 +1,94 @@ -/mob/living/simple_animal/hostile/guardian/create_mob_hud() - if(client && !hud_used) - hud_used = new /datum/hud/guardian(src) - -/datum/hud/guardian/New(mob/owner) - ..() - var/obj/screen/using - - guardianhealthdisplay = new /obj/screen/healths/guardian() - infodisplay += guardianhealthdisplay - - using = new /obj/screen/act_intent/guardian() - using.icon_state = mymob.a_intent - static_inventory += using - action_intent = using - - using = new /obj/screen/guardian/Manifest() - using.screen_loc = ui_rhand - static_inventory += using - - using = new /obj/screen/guardian/Recall() - using.screen_loc = ui_lhand - static_inventory += using - - using = new /obj/screen/guardian/ToggleMode() - using.screen_loc = ui_storage1 - static_inventory += using - - using = new /obj/screen/guardian/ToggleLight() - using.screen_loc = ui_inventory - static_inventory += using - - using = new /obj/screen/guardian/Communicate() - using.screen_loc = ui_back - static_inventory += using - - -//HUD BUTTONS - -/obj/screen/guardian - icon = 'icons/mob/guardian.dmi' - icon_state = "base" - -/obj/screen/guardian/Manifest - icon_state = "manifest" - name = "Manifest" - desc = "Spring forth into battle!" - -/obj/screen/guardian/Manifest/Click() - if(isguardian(usr)) - var/mob/living/simple_animal/hostile/guardian/G = usr - G.Manifest() - - -/obj/screen/guardian/Recall - icon_state = "recall" - name = "Recall" - desc = "Return to your user." - -/obj/screen/guardian/Recall/Click() - if(isguardian(usr)) - var/mob/living/simple_animal/hostile/guardian/G = usr - G.Recall() - -/obj/screen/guardian/ToggleMode - icon_state = "toggle" - name = "Toggle Mode" - desc = "Switch between ability modes." - -/obj/screen/guardian/ToggleMode/Click() - if(isguardian(usr)) - var/mob/living/simple_animal/hostile/guardian/G = usr - G.ToggleMode() - -/obj/screen/guardian/Communicate - icon_state = "communicate" - name = "Communicate" - desc = "Communicate telepathically with your user." - -/obj/screen/guardian/Communicate/Click() - if(isguardian(usr)) - var/mob/living/simple_animal/hostile/guardian/G = usr - G.Communicate() - - -/obj/screen/guardian/ToggleLight - icon_state = "light" - name = "Toggle Light" - desc = "Glow like star dust." - -/obj/screen/guardian/ToggleLight/Click() - if(isguardian(usr)) - var/mob/living/simple_animal/hostile/guardian/G = usr - G.ToggleLight() \ No newline at end of file +/mob/living/simple_animal/hostile/guardian/create_mob_hud() + if(client && !hud_used) + hud_used = new /datum/hud/guardian(src) + +/datum/hud/guardian/New(mob/owner) + ..() + var/obj/screen/using + + guardianhealthdisplay = new /obj/screen/healths/guardian() + infodisplay += guardianhealthdisplay + + using = new /obj/screen/act_intent/guardian() + using.icon_state = mymob.a_intent + static_inventory += using + action_intent = using + + using = new /obj/screen/guardian/Manifest() + using.screen_loc = ui_rhand + static_inventory += using + + using = new /obj/screen/guardian/Recall() + using.screen_loc = ui_lhand + static_inventory += using + + using = new /obj/screen/guardian/ToggleMode() + using.screen_loc = ui_storage1 + static_inventory += using + + using = new /obj/screen/guardian/ToggleLight() + using.screen_loc = ui_inventory + static_inventory += using + + using = new /obj/screen/guardian/Communicate() + using.screen_loc = ui_back + static_inventory += using + + +//HUD BUTTONS + +/obj/screen/guardian + icon = 'icons/mob/guardian.dmi' + icon_state = "base" + +/obj/screen/guardian/Manifest + icon_state = "manifest" + name = "Manifest" + desc = "Spring forth into battle!" + +/obj/screen/guardian/Manifest/Click() + if(isguardian(usr)) + var/mob/living/simple_animal/hostile/guardian/G = usr + G.Manifest() + + +/obj/screen/guardian/Recall + icon_state = "recall" + name = "Recall" + desc = "Return to your user." + +/obj/screen/guardian/Recall/Click() + if(isguardian(usr)) + var/mob/living/simple_animal/hostile/guardian/G = usr + G.Recall() + +/obj/screen/guardian/ToggleMode + icon_state = "toggle" + name = "Toggle Mode" + desc = "Switch between ability modes." + +/obj/screen/guardian/ToggleMode/Click() + if(isguardian(usr)) + var/mob/living/simple_animal/hostile/guardian/G = usr + G.ToggleMode() + +/obj/screen/guardian/Communicate + icon_state = "communicate" + name = "Communicate" + desc = "Communicate telepathically with your user." + +/obj/screen/guardian/Communicate/Click() + if(isguardian(usr)) + var/mob/living/simple_animal/hostile/guardian/G = usr + G.Communicate() + + +/obj/screen/guardian/ToggleLight + icon_state = "light" + name = "Toggle Light" + desc = "Glow like star dust." + +/obj/screen/guardian/ToggleLight/Click() + if(isguardian(usr)) + var/mob/living/simple_animal/hostile/guardian/G = usr + G.ToggleLight() diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index beb6949ce2c..00b23ff3cdd 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -214,4 +214,4 @@ to_chat(usr, "This mob type does not use a HUD.") /datum/hud/proc/update_locked_slots() - return \ No newline at end of file + return diff --git a/code/_onclick/hud/other_mobs.dm b/code/_onclick/hud/other_mobs.dm index a82c847a246..f03d436bcaf 100644 --- a/code/_onclick/hud/other_mobs.dm +++ b/code/_onclick/hud/other_mobs.dm @@ -24,4 +24,4 @@ mymob.pullin.icon = 'icons/mob/screen_corgi.dmi' mymob.pullin.update_icon(mymob) mymob.pullin.screen_loc = ui_construct_pull - static_inventory += mymob.pullin \ No newline at end of file + static_inventory += mymob.pullin diff --git a/code/_onclick/hud/parallax.dm b/code/_onclick/hud/parallax.dm index 429344e6142..68847cbd97d 100644 --- a/code/_onclick/hud/parallax.dm +++ b/code/_onclick/hud/parallax.dm @@ -310,4 +310,4 @@ 1, 1, 1, 1, 0, 0, 0, 0 ) - screen_loc = "CENTER-7,CENTER-7" \ No newline at end of file + screen_loc = "CENTER-7,CENTER-7" diff --git a/code/_onclick/hud/picture_in_picture.dm b/code/_onclick/hud/picture_in_picture.dm index e011aea037c..872659d714b 100644 --- a/code/_onclick/hud/picture_in_picture.dm +++ b/code/_onclick/hud/picture_in_picture.dm @@ -141,4 +141,4 @@ /obj/screen/movable/pic_in_pic/proc/unshow_to(client/C) if(C) shown_to -= C - C.screen -= src \ No newline at end of file + C.screen -= src diff --git a/code/_onclick/hud/plane_master.dm b/code/_onclick/hud/plane_master.dm index 9c0f498cd83..fc3e926b396 100644 --- a/code/_onclick/hud/plane_master.dm +++ b/code/_onclick/hud/plane_master.dm @@ -51,4 +51,4 @@ /obj/screen/plane_master/lighting/backdrop(mob/mymob) mymob.overlay_fullscreen("lighting_backdrop_lit", /obj/screen/fullscreen/lighting_backdrop/lit) - mymob.overlay_fullscreen("lighting_backdrop_unlit", /obj/screen/fullscreen/lighting_backdrop/unlit) \ No newline at end of file + mymob.overlay_fullscreen("lighting_backdrop_unlit", /obj/screen/fullscreen/lighting_backdrop/unlit) diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index c3c9b6f7e65..c0f5cfd5f3b 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -362,7 +362,7 @@ object_overlays += item_overlay add_overlay(object_overlays) -/obj/screen/inventory/Click() +/obj/screen/inventory/Click(location, control, params) // At this point in client Click() code we have passed the 1/10 sec check and little else // We don't even know if it's a middle click if(world.time <= usr.next_move) @@ -371,6 +371,12 @@ return 1 if(istype(usr.loc,/obj/mecha)) // stops inventory actions in a mech return 1 + + if(hud?.mymob && slot_id) + var/obj/item/inv_item = hud.mymob.get_item_by_slot(slot_id) + if(inv_item) + return inv_item.Click(location, control, params) + if(usr.attack_ui(slot_id)) usr.update_inv_l_hand(0) usr.update_inv_r_hand(0) diff --git a/code/_onclick/hud/slime.dm b/code/_onclick/hud/slime.dm index f49228ddf66..273991a83c8 100644 --- a/code/_onclick/hud/slime.dm +++ b/code/_onclick/hud/slime.dm @@ -5,4 +5,4 @@ /mob/living/simple_animal/slime/create_mob_hud() if(client && !hud_used) - hud_used = new /datum/hud/slime(src) \ No newline at end of file + hud_used = new /datum/hud/slime(src) diff --git a/code/_onclick/hud/swarmer.dm b/code/_onclick/hud/swarmer.dm index 84f5811cc8d..2042442642a 100644 --- a/code/_onclick/hud/swarmer.dm +++ b/code/_onclick/hud/swarmer.dm @@ -1,97 +1,97 @@ - -////HUD NONSENSE//// -/obj/screen/swarmer - icon = 'icons/mob/swarmer.dmi' - -/obj/screen/swarmer/FabricateTrap - icon_state = "ui_trap" - name = "Create trap (Costs 5 Resources)" - desc = "Creates a trap that will nonlethally shock any non-swarmer that attempts to cross it. (Costs 5 resources)" - -/obj/screen/swarmer/FabricateTrap/Click() - if(isswarmer(usr)) - var/mob/living/simple_animal/hostile/swarmer/S = usr - S.CreateTrap() - -/obj/screen/swarmer/Barricade - icon_state = "ui_barricade" - name = "Create barricade (Costs 5 Resources)" - desc = "Creates a destructible barricade that will stop any non swarmer from passing it. Also allows disabler beams to pass through. (Costs 5 resources)" - -/obj/screen/swarmer/Barricade/Click() - if(isswarmer(usr)) - var/mob/living/simple_animal/hostile/swarmer/S = usr - S.CreateBarricade() - -/obj/screen/swarmer/Replicate - icon_state = "ui_replicate" - name = "Replicate (Costs 50 Resources)" - desc = "Creates a another of our kind." - -/obj/screen/swarmer/Replicate/Click() - if(isswarmer(usr)) - var/mob/living/simple_animal/hostile/swarmer/S = usr - S.CreateSwarmer() - -/obj/screen/swarmer/RepairSelf - icon_state = "ui_self_repair" - name = "Repair self" - desc = "Repairs damage to our body." - -/obj/screen/swarmer/RepairSelf/Click() - if(isswarmer(usr)) - var/mob/living/simple_animal/hostile/swarmer/S = usr - S.RepairSelf() - -/obj/screen/swarmer/ToggleLight - icon_state = "ui_light" - name = "Toggle light" - desc = "Toggles our inbuilt light on or off." - -/obj/screen/swarmer/ToggleLight/Click() - if(isswarmer(usr)) - var/mob/living/simple_animal/hostile/swarmer/S = usr - S.ToggleLight() - -/obj/screen/swarmer/ContactSwarmers - icon_state = "ui_contact_swarmers" - name = "Contact swarmers" - desc = "Sends a message to all other swarmers, should they exist." - -/obj/screen/swarmer/ContactSwarmers/Click() - if(isswarmer(usr)) - var/mob/living/simple_animal/hostile/swarmer/S = usr - S.ContactSwarmers() - -/mob/living/simple_animal/hostile/swarmer/create_mob_hud() - if(client && !hud_used) - hud_used = new /datum/hud/swarmer(src) - -/datum/hud/swarmer/New(mob/owner) - ..() - var/obj/screen/using - - using = new /obj/screen/swarmer/FabricateTrap() - using.screen_loc = ui_rhand - static_inventory += using - - using = new /obj/screen/swarmer/Barricade() - using.screen_loc = ui_lhand - static_inventory += using - - using = new /obj/screen/swarmer/Replicate() - using.screen_loc = ui_zonesel - static_inventory += using - - using = new /obj/screen/swarmer/RepairSelf() - using.screen_loc = ui_storage1 - static_inventory += using - - using = new /obj/screen/swarmer/ToggleLight() - using.screen_loc = ui_back - static_inventory += using - - using = new /obj/screen/swarmer/ContactSwarmers() - using.screen_loc = ui_inventory - static_inventory += using - + +////HUD NONSENSE//// +/obj/screen/swarmer + icon = 'icons/mob/swarmer.dmi' + +/obj/screen/swarmer/FabricateTrap + icon_state = "ui_trap" + name = "Create trap (Costs 5 Resources)" + desc = "Creates a trap that will nonlethally shock any non-swarmer that attempts to cross it. (Costs 5 resources)" + +/obj/screen/swarmer/FabricateTrap/Click() + if(isswarmer(usr)) + var/mob/living/simple_animal/hostile/swarmer/S = usr + S.CreateTrap() + +/obj/screen/swarmer/Barricade + icon_state = "ui_barricade" + name = "Create barricade (Costs 5 Resources)" + desc = "Creates a destructible barricade that will stop any non swarmer from passing it. Also allows disabler beams to pass through. (Costs 5 resources)" + +/obj/screen/swarmer/Barricade/Click() + if(isswarmer(usr)) + var/mob/living/simple_animal/hostile/swarmer/S = usr + S.CreateBarricade() + +/obj/screen/swarmer/Replicate + icon_state = "ui_replicate" + name = "Replicate (Costs 50 Resources)" + desc = "Creates a another of our kind." + +/obj/screen/swarmer/Replicate/Click() + if(isswarmer(usr)) + var/mob/living/simple_animal/hostile/swarmer/S = usr + S.CreateSwarmer() + +/obj/screen/swarmer/RepairSelf + icon_state = "ui_self_repair" + name = "Repair self" + desc = "Repairs damage to our body." + +/obj/screen/swarmer/RepairSelf/Click() + if(isswarmer(usr)) + var/mob/living/simple_animal/hostile/swarmer/S = usr + S.RepairSelf() + +/obj/screen/swarmer/ToggleLight + icon_state = "ui_light" + name = "Toggle light" + desc = "Toggles our inbuilt light on or off." + +/obj/screen/swarmer/ToggleLight/Click() + if(isswarmer(usr)) + var/mob/living/simple_animal/hostile/swarmer/S = usr + S.ToggleLight() + +/obj/screen/swarmer/ContactSwarmers + icon_state = "ui_contact_swarmers" + name = "Contact swarmers" + desc = "Sends a message to all other swarmers, should they exist." + +/obj/screen/swarmer/ContactSwarmers/Click() + if(isswarmer(usr)) + var/mob/living/simple_animal/hostile/swarmer/S = usr + S.ContactSwarmers() + +/mob/living/simple_animal/hostile/swarmer/create_mob_hud() + if(client && !hud_used) + hud_used = new /datum/hud/swarmer(src) + +/datum/hud/swarmer/New(mob/owner) + ..() + var/obj/screen/using + + using = new /obj/screen/swarmer/FabricateTrap() + using.screen_loc = ui_rhand + static_inventory += using + + using = new /obj/screen/swarmer/Barricade() + using.screen_loc = ui_lhand + static_inventory += using + + using = new /obj/screen/swarmer/Replicate() + using.screen_loc = ui_zonesel + static_inventory += using + + using = new /obj/screen/swarmer/RepairSelf() + using.screen_loc = ui_storage1 + static_inventory += using + + using = new /obj/screen/swarmer/ToggleLight() + using.screen_loc = ui_back + static_inventory += using + + using = new /obj/screen/swarmer/ContactSwarmers() + using.screen_loc = ui_inventory + static_inventory += using + diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index 47b5f2ba337..d2a91da4020 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -1,10 +1,17 @@ /obj/item/proc/melee_attack_chain(mob/user, atom/target, params) - if(pre_attackby(target, user, params)) + if(!tool_attack_chain(user, target) && pre_attackby(target, user, params)) // Return 1 in attackby() to prevent afterattack() effects (when safely moving items for example) var/resolved = target.attackby(src, user, params) if(!resolved && target && !QDELETED(src)) afterattack(target, user, 1, params) // 1: clicking something Adjacent +//Checks if the item can work as a tool, calling the appropriate tool behavior on the target +//Note that if tool_act returns TRUE, then the tool won't call attack_by. +/obj/item/proc/tool_attack_chain(mob/user, atom/target) + if(!tool_behaviour) + return FALSE + return target.tool_act(user, src, tool_behaviour) + // Called when the item is in the active hand, and clicked; alternately, there is an 'activate held object' verb or you can hit pagedown. /obj/item/proc/attack_self(mob/user) if(SEND_SIGNAL(src, COMSIG_ITEM_ATTACK_SELF, user) & COMPONENT_NO_INTERACT) @@ -50,17 +57,12 @@ return 0 else return 1 - - if(isscrewdriver(src) && ismachine(M) && user.a_intent == INTENT_HELP) + var/obj/item/organ/external/O = M.get_organ(user.zone_selected) + if((is_sharp(src) || (isscrewdriver(src) && O.is_robotic())) && user.a_intent == INTENT_HELP) if(!attempt_initiate_surgery(src, M, user)) - return 0 + return FALSE else - return 1 - if(is_sharp(src) && user.a_intent == INTENT_HELP) - if(!attempt_initiate_surgery(src, M, user)) - return 0 - else - return 1 + return TRUE if(force && HAS_TRAIT(user, TRAIT_PACIFISM)) to_chat(user, "You don't want to harm other living beings!") diff --git a/code/_onclick/observer.dm b/code/_onclick/observer.dm index 5f0c94ecee6..460bc0193f6 100644 --- a/code/_onclick/observer.dm +++ b/code/_onclick/observer.dm @@ -61,6 +61,8 @@ // health + cyborg analyzer for ghosts /mob/living/attack_ghost(mob/dead/observer/user) + if(!istype(user)) // Make sure user is actually an observer. Revenents also use attack_ghost, but do not have the health_scan var. + return if(user.client && user.health_scan) if(issilicon(src) || ismachine(src)) robot_healthscan(user, src) diff --git a/code/_onclick/overmind.dm b/code/_onclick/overmind.dm index 8ace273dd8f..419524c8711 100644 --- a/code/_onclick/overmind.dm +++ b/code/_onclick/overmind.dm @@ -32,4 +32,4 @@ /mob/camera/blob/AltClickOn(atom/A) //Remove a blob var/turf/T = get_turf(A) if(T) - remove_blob(T) \ No newline at end of file + remove_blob(T) diff --git a/code/_onclick/telekinesis.dm b/code/_onclick/telekinesis.dm index 6ef7a797136..e684707d460 100644 --- a/code/_onclick/telekinesis.dm +++ b/code/_onclick/telekinesis.dm @@ -3,7 +3,7 @@ This needs more thinking out, but I might as well. */ -var/const/tk_maxrange = 15 +#define TK_MAXRANGE 15 /* Telekinetic attack: @@ -118,7 +118,7 @@ var/const/tk_maxrange = 15 var/d = get_dist(user, target) if(focus) d = max(d,get_dist(user,focus)) // whichever is further - if(d > tk_maxrange) + if(d > TK_MAXRANGE) to_chat(user, "Your mind won't reach that far.") return diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index 86883654997..3f5785e44ab 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -1,893 +1,897 @@ -/datum/configuration - var/server_name = null // server name (for world name / status) - var/server_tag_line = null // server tagline (for showing on hub entry) - var/server_extra_features = null // server-specific extra features (for hub entry) - var/server_suffix = 0 // generate numeric suffix based on server port - - var/minimum_client_build = 1421 // Build 1421 due to the middle mouse button exploit - - var/nudge_script_path = "nudge.py" // where the nudge.py script is located - - var/log_ooc = 0 // log OOC channel - var/log_access = 0 // log login/logout - var/log_say = 0 // log client say - var/log_admin = 0 // log admin actions - var/log_debug = 1 // log debug output - var/log_game = 0 // log game events - var/log_vote = 0 // log voting - var/log_whisper = 0 // log client whisper - var/log_emote = 0 // log emotes - var/log_attack = 0 // log attack messages - var/log_adminchat = 0 // log admin chat messages - var/log_adminwarn = 0 // log warnings admins get about bomb construction and such - var/log_pda = 0 // log pda messages - var/log_world_output = 0 // log world.log << messages - var/log_runtimes = 0 // logs world.log to a file - var/log_hrefs = 0 // logs all links clicked in-game. Could be used for debugging and tracking down exploits - var/sql_enabled = 0 // for sql switching - var/allow_admin_ooccolor = 0 // Allows admins with relevant permissions to have their own ooc colour - var/pregame_timestart = 240 // Time it takes for the server to start the game - var/allow_vote_restart = 0 // allow votes to restart - var/allow_vote_mode = 0 // allow votes to change mode - var/vote_delay = 6000 // minimum time between voting sessions (deciseconds, 10 minute default) - var/vote_period = 600 // length of voting period (deciseconds, default 1 minute) - var/vote_autotransfer_initial = 72000 // Length of time before the first autotransfer vote is called - var/vote_autotransfer_interval = 18000 // length of time before next sequential autotransfer vote - var/vote_no_default = 0 // vote does not default to nochange/norestart (tbi) - var/vote_no_dead = 0 // dead people can't vote (tbi) -// var/enable_authentication = 0 // goon authentication - var/del_new_on_log = 1 // del's new players if they log before they spawn in - var/feature_object_spell_system = 0 //spawns a spellbook which gives object-type spells instead of verb-type spells for the wizard - var/traitor_scaling = 0 //if amount of traitors scales based on amount of players - var/protect_roles_from_antagonist = 0// If security and such can be tratior/cult/other - var/continuous_rounds = 0 // Gamemodes which end instantly will instead keep on going until the round ends by escape shuttle or nuke. - var/allow_Metadata = 0 // Metadata is supported. - var/popup_admin_pm = 0 //adminPMs to non-admins show in a pop-up 'reply' window when set to 1. - var/Ticklag = 0.5 - var/socket_talk = 0 // use socket_talk to communicate with other processes - var/list/resource_urls = null - var/antag_hud_allowed = 0 // Ghosts can turn on Antagovision to see a HUD of who is the bad guys this round. - var/antag_hud_restricted = 0 // Ghosts that turn on Antagovision cannot rejoin the round. - var/list/mode_names = list() - var/list/modes = list() // allowed modes - var/list/votable_modes = list() // votable modes - var/list/probabilities = list() // relative probability of each mode - var/humans_need_surnames = 0 - var/allow_random_events = 0 // enables random events mid-round when set to 1 - var/allow_ai = 1 // allow ai job - var/respawn = 0 - var/guest_jobban = 1 - var/usewhitelist = 0 - var/mods_are_mentors = 0 - var/load_jobs_from_txt = 0 - var/ToRban = 0 - var/automute_on = 0 //enables automuting/spam prevention - var/jobs_have_minimal_access = 0 //determines whether jobs use minimal access or expanded access. - var/round_abandon_penalty_period = 30 MINUTES // Time from round start during which ghosting out is penalized - var/medal_hub_address = null - var/medal_hub_password = null - - var/reactionary_explosions = 0 //If we use reactionary explosions, explosions that react to walls and doors - - var/assistantlimit = 0 //enables assistant limiting - var/assistantratio = 2 //how many assistants to security members - - // The AFK subsystem will not be activated if any of the below config values are equal or less than 0 - var/warn_afk_minimum = 0 // How long till you get a warning while being AFK - var/auto_cryo_afk = 0 // How long till you get put into cryo when you're AFK - var/auto_despawn_afk = 0 // How long till you actually despawn in cryo when you're AFK (Not ssd so not automatic) - - var/auto_cryo_ssd_mins = 0 - var/ssd_warning = 0 - - var/list_afk_minimum = 5 // How long people have to be AFK before it's listed on the "List AFK players" verb - - var/traitor_objectives_amount = 2 - var/shadowling_max_age = 0 - - var/max_maint_drones = 5 //This many drones can spawn, - var/allow_drone_spawn = 1 //assuming the admin allow them to. - var/drone_build_time = 1200 //A drone will become available every X ticks since last drone spawn. Default is 2 minutes. - - var/usealienwhitelist = 0 - var/limitalienplayers = 0 - var/alien_to_human_ratio = 0.5 - - var/server - var/banappeals - var/wikiurl = "http://example.org" - var/forumurl = "http://example.org" - var/rulesurl = "http://example.org" - var/githuburl = "http://example.org" - var/donationsurl = "http://example.org" - var/repositoryurl = "http://example.org" - var/discordurl = "http://example.org" - var/discordforumurl = "http://example.org" - - var/overflow_server_url - var/forbid_singulo_possession = 0 - - var/check_randomizer = 0 - - //game_options.txt configs - - var/bones_can_break = 1 - - var/revival_pod_plants = 1 - var/revival_cloning = 1 - var/revival_brain_life = -1 - - var/auto_toggle_ooc_during_round = 0 - - var/shuttle_refuel_delay = 12000 - - //Used for modifying movement speed for mobs. - //Unversal modifiers - var/run_speed = 0 - var/walk_speed = 0 - - //Mob specific modifiers. NOTE: These will affect different mob types in different ways - var/human_delay = 0 - var/robot_delay = 0 - var/monkey_delay = 0 - var/alien_delay = 0 - var/slime_delay = 0 - var/animal_delay = 0 - - //IP Intel vars - var/ipintel_email - var/ipintel_rating_bad = 1 - var/ipintel_save_good = 12 - var/ipintel_save_bad = 1 - var/ipintel_domain = "check.getipintel.net" - var/ipintel_maxplaytime = 0 - var/ipintel_whitelist = 0 - var/ipintel_detailsurl = "https://iphub.info/?ip=" - - var/forum_link_url - var/forum_playerinfo_url - - var/admin_legacy_system = 0 //Defines whether the server uses the legacy admin system with admins.txt or the SQL system. Config option in config.txt - var/ban_legacy_system = 0 //Defines whether the server uses the legacy banning system with the files in /data or the SQL system. Config option in config.txt - var/use_age_restriction_for_jobs = 0 //Do jobs use account age restrictions? --requires database - var/use_age_restriction_for_antags = 0 //Do antags use account age restrictions? --requires database - - var/use_exp_tracking = 0 - var/use_exp_restrictions = 0 - var/use_exp_restrictions_admin_bypass = 0 - - var/simultaneous_pm_warning_timeout = 100 - - var/assistant_maint = 0 //Do assistants get maint access? - var/gateway_delay = 6000 //How long the gateway takes before it activates. Default is half an hour. - var/ghost_interaction = 0 - - var/comms_password = "" - - var/use_irc_bot = 0 - var/list/irc_bot_host = list() - var/main_irc = "" - var/admin_irc = "" - var/admin_notify_irc = "" - var/cidrandomizer_irc = "" - - var/default_laws = 0 //Controls what laws the AI spawns with. - - var/list/station_levels = list(1) // Defines which Z-levels the station exists on. - var/list/admin_levels= list(2) // Defines which Z-levels which are for admin functionality, for example including such areas as Central Command and the Syndicate Shuttle - var/list/contact_levels = list(1, 5) // Defines which Z-levels which, for example, a Code Red announcement may affect - var/list/player_levels = list(1, 3, 4, 5, 6, 7) // Defines all Z-levels a character can typically reach - - var/const/minutes_to_ticks = 60 * 10 - // Event settings - var/expected_round_length = 60 * 2 * minutes_to_ticks // 2 hours - // If the first delay has a custom start time - // No custom time, no custom time, between 80 to 100 minutes respectively. - var/list/event_first_run = list(EVENT_LEVEL_MUNDANE = null, EVENT_LEVEL_MODERATE = null, EVENT_LEVEL_MAJOR = list("lower" = 48000, "upper" = 60000)) - // The lowest delay until next event - // 10, 30, 50 minutes respectively - var/list/event_delay_lower = list(EVENT_LEVEL_MUNDANE = 6000, EVENT_LEVEL_MODERATE = 18000, EVENT_LEVEL_MAJOR = 30000) - // The upper delay until next event - // 15, 45, 70 minutes respectively - var/list/event_delay_upper = list(EVENT_LEVEL_MUNDANE = 9000, EVENT_LEVEL_MODERATE = 27000, EVENT_LEVEL_MAJOR = 42000) - - var/starlight = 0 // Whether space turfs have ambient light or not - var/allow_holidays = 0 - var/player_overflow_cap = 0 //number of players before the server starts rerouting - var/list/overflow_whitelist = list() //whitelist for overflow - - var/disable_away_missions = 0 // disable away missions - var/disable_space_ruins = 0 //disable space ruins - - var/extra_space_ruin_levels_min = 2 - var/extra_space_ruin_levels_max = 4 - - var/ooc_allowed = 1 - var/looc_allowed = 1 - var/dooc_allowed = 1 - var/dsay_allowed = 1 - - var/disable_lobby_music = 0 // Disables the lobby music - var/disable_cid_warn_popup = 0 //disables the annoying "You have already logged in this round, disconnect or be banned" popup, because it annoys the shit out of me when testing. - - var/max_loadout_points = 5 // How many points can be spent on extra items in character setup - - var/disable_ooc_emoji = 0 // prevents people from using emoji in OOC - - var/shutdown_on_reboot = 0 // Whether to shut down the world instead of rebooting it - - var/disable_karma = 0 // Disable all karma functions and unlock karma jobs by default - - // StonedMC - var/tick_limit_mc_init = TICK_LIMIT_MC_INIT_DEFAULT //SSinitialization throttling - - // Highpop tickrates - var/base_mc_tick_rate = 1 - var/high_pop_mc_tick_rate = 1.1 - - var/high_pop_mc_mode_amount = 65 - var/disable_high_pop_mc_mode_amount = 60 - - // Nightshift - var/randomize_shift_time = FALSE - var/enable_night_shifts = FALSE - - // Developer - var/developer_express_start = 0 - - // Automatic localhost admin disable - var/disable_localhost_admin = 0 - - //Start now warning - var/start_now_confirmation = 0 - - // Lavaland - var/lavaland_budget = 60 - - //cube monkey limit - var/cubemonkeycap = 20 - - // Makes gamemodes respect player limits - var/enable_gamemode_player_limit = 0 - -/datum/configuration/New() - for(var/T in subtypesof(/datum/game_mode)) - var/datum/game_mode/M = T - - if(initial(M.config_tag)) - if(!(initial(M.config_tag) in modes)) // ensure each mode is added only once - src.modes += initial(M.config_tag) - src.mode_names[initial(M.config_tag)] = initial(M.name) - src.probabilities[initial(M.config_tag)] = initial(M.probability) - if(initial(M.votable)) - src.votable_modes += initial(M.config_tag) - src.votable_modes += "secret" - -/datum/configuration/proc/load(filename, type = "config") //the type can also be game_options, in which case it uses a different switch. not making it separate to not copypaste code - Urist - var/list/Lines = file2list(filename) - - for(var/t in Lines) - if(!t) continue - - t = trim(t) - if(length(t) == 0) - continue - else if(copytext(t, 1, 2) == "#") - continue - - var/pos = findtext(t, " ") - var/name = null - var/value = null - - if(pos) - name = lowertext(copytext(t, 1, pos)) - value = copytext(t, pos + 1) - else - name = lowertext(t) - - if(!name) - continue - - if(type == "config") - switch(name) - if("resource_urls") - config.resource_urls = splittext(value, " ") - - if("admin_legacy_system") - config.admin_legacy_system = 1 - - if("ban_legacy_system") - config.ban_legacy_system = 1 - - if("use_age_restriction_for_jobs") - config.use_age_restriction_for_jobs = 1 - - if("use_age_restriction_for_antags") - config.use_age_restriction_for_antags = 1 - - if("use_exp_tracking") - config.use_exp_tracking = 1 - - if("use_exp_restrictions") - config.use_exp_restrictions = 1 - - if("use_exp_restrictions_admin_bypass") - config.use_exp_restrictions_admin_bypass = 1 - - if("jobs_have_minimal_access") - config.jobs_have_minimal_access = 1 - - if("shadowling_max_age") - config.shadowling_max_age = text2num(value) - - if("warn_afk_minimum") - config.warn_afk_minimum = text2num(value) - if("auto_cryo_afk") - config.auto_cryo_afk = text2num(value) - if("auto_despawn_afk") - config.auto_despawn_afk = text2num(value) - - if("auto_cryo_ssd_mins") - config.auto_cryo_ssd_mins = text2num(value) - if("ssd_warning") - config.ssd_warning = 1 - - if("list_afk_minimum") - config.list_afk_minimum = text2num(value) - - if("ipintel_email") - if(value != "ch@nge.me") - config.ipintel_email = value - if("ipintel_rating_bad") - config.ipintel_rating_bad = text2num(value) - if("ipintel_domain") - config.ipintel_domain = value - if("ipintel_save_good") - config.ipintel_save_good = text2num(value) - if("ipintel_save_bad") - config.ipintel_save_bad = text2num(value) - if("ipintel_maxplaytime") - config.ipintel_maxplaytime = text2num(value) - if("ipintel_whitelist") - config.ipintel_whitelist = 1 - if("ipintel_detailsurl") - config.ipintel_detailsurl = value - - if("forum_link_url") - config.forum_link_url = value - - if("forum_playerinfo_url") - config.forum_playerinfo_url = value - - if("log_ooc") - config.log_ooc = 1 - - if("log_access") - config.log_access = 1 - - if("log_say") - config.log_say = 1 - - if("log_admin") - config.log_admin = 1 - - if("log_debug") - config.log_debug = 1 - - if("log_game") - config.log_game = 1 - - if("log_vote") - config.log_vote = 1 - - if("log_whisper") - config.log_whisper = 1 - - if("log_attack") - config.log_attack = 1 - - if("log_emote") - config.log_emote = 1 - - if("log_adminchat") - config.log_adminchat = 1 - - if("log_adminwarn") - config.log_adminwarn = 1 - - if("log_pda") - config.log_pda = 1 - - if("log_world_output") - config.log_world_output = 1 - - if("log_hrefs") - config.log_hrefs = 1 - - if("log_runtime") - config.log_runtimes = 1 - - if("mentors") - config.mods_are_mentors = 1 - - if("allow_admin_ooccolor") - config.allow_admin_ooccolor = 1 - - if("pregame_timestart") - config.pregame_timestart = text2num(value) - - if("allow_vote_restart") - config.allow_vote_restart = 1 - - if("allow_vote_mode") - config.allow_vote_mode = 1 - - if("no_dead_vote") - config.vote_no_dead = 1 - - if("vote_autotransfer_initial") - config.vote_autotransfer_initial = text2num(value) - - if("vote_autotransfer_interval") - config.vote_autotransfer_interval = text2num(value) - - if("default_no_vote") - config.vote_no_default = 1 - - if("vote_delay") - config.vote_delay = text2num(value) - - if("vote_period") - config.vote_period = text2num(value) - - if("allow_ai") - config.allow_ai = 1 - -// if("authentication") -// config.enable_authentication = 1 - - if("norespawn") - config.respawn = 0 - - if("servername") - config.server_name = value - - if("server_tag_line") - config.server_tag_line = value - - if("server_extra_features") - config.server_extra_features = value - - if("serversuffix") - config.server_suffix = 1 - - if("minimum_client_build") - config.minimum_client_build = text2num(value) - - if("nudge_script_path") - config.nudge_script_path = value - - if("server") - config.server = value - - if("banappeals") - config.banappeals = value - - if("wikiurl") - config.wikiurl = value - - if("forumurl") - config.forumurl = value - - if("rulesurl") - config.rulesurl = value - - if("githuburl") - config.githuburl = value - - if("discordurl") - config.discordurl = value - - if("discordforumurl") - config.discordforumurl = value - - if("donationsurl") - config.donationsurl = value - - if("repositoryurl") - config.repositoryurl = value - - if("guest_jobban") - config.guest_jobban = 1 - - if("guest_ban") - guests_allowed = 0 - - if("usewhitelist") - config.usewhitelist = 1 - - if("feature_object_spell_system") - config.feature_object_spell_system = 1 - - if("allow_metadata") - config.allow_Metadata = 1 - - if("traitor_scaling") - config.traitor_scaling = 1 - - if("protect_roles_from_antagonist") - config.protect_roles_from_antagonist = 1 - - if("probability") - var/prob_pos = findtext(value, " ") - var/prob_name = null - var/prob_value = null - - if(prob_pos) - prob_name = lowertext(copytext(value, 1, prob_pos)) - prob_value = copytext(value, prob_pos + 1) - if(prob_name in config.modes) - config.probabilities[prob_name] = text2num(prob_value) - else - log_config("Unknown game mode probability configuration definition: [prob_name].") - else - log_config("Incorrect probability configuration definition: [prob_name] [prob_value].") - - if("allow_random_events") - config.allow_random_events = 1 - - if("load_jobs_from_txt") - load_jobs_from_txt = 1 - - if("forbid_singulo_possession") - forbid_singulo_possession = 1 - - if("check_randomizer") - check_randomizer = 1 - - if("popup_admin_pm") - config.popup_admin_pm = 1 - - if("allow_holidays") - config.allow_holidays = 1 - - if("use_irc_bot") - use_irc_bot = 1 - - if("ticklag") - Ticklag = text2num(value) - - if("socket_talk") - socket_talk = text2num(value) - - if("allow_antag_hud") - config.antag_hud_allowed = 1 - - if("antag_hud_restricted") - config.antag_hud_restricted = 1 - - if("humans_need_surnames") - humans_need_surnames = 1 - - if("tor_ban") - ToRban = 1 - - if("automute_on") - automute_on = 1 - - if("usealienwhitelist") - usealienwhitelist = 1 - - if("alien_player_ratio") - limitalienplayers = 1 - alien_to_human_ratio = text2num(value) - - if("assistant_maint") - config.assistant_maint = 1 - - if("gateway_delay") - config.gateway_delay = text2num(value) - - if("continuous_rounds") - config.continuous_rounds = 1 - - if("ghost_interaction") - config.ghost_interaction = 1 - - if("comms_password") - config.comms_password = value - - if("irc_bot_host") - config.irc_bot_host = splittext(value, ";") - - if("main_irc") - config.main_irc = value - - if("admin_irc") - config.admin_irc = value - - if("admin_notify_irc") - config.admin_notify_irc = value - - if("cidrandomizer_irc") - config.cidrandomizer_irc = value - - if("python_path") - if(value) - python_path = value - else - if(world.system_type == UNIX) - python_path = "/usr/bin/env python2" - else //probably windows, if not this should work anyway - python_path = "pythonw" - - if("assistant_limit") - config.assistantlimit = 1 - - if("assistant_ratio") - config.assistantratio = text2num(value) - - if("allow_drone_spawn") - config.allow_drone_spawn = text2num(value) - - if("drone_build_time") - config.drone_build_time = text2num(value) - - if("max_maint_drones") - config.max_maint_drones = text2num(value) - - if("expected_round_length") - config.expected_round_length = MinutesToTicks(text2num(value)) - - if("event_custom_start_mundane") - var/values = text2numlist(value, ";") - config.event_first_run[EVENT_LEVEL_MUNDANE] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2])) - - if("event_custom_start_moderate") - var/values = text2numlist(value, ";") - config.event_first_run[EVENT_LEVEL_MODERATE] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2])) - - if("event_custom_start_major") - var/values = text2numlist(value, ";") - config.event_first_run[EVENT_LEVEL_MAJOR] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2])) - - if("event_delay_lower") - var/values = text2numlist(value, ";") - config.event_delay_lower[EVENT_LEVEL_MUNDANE] = MinutesToTicks(values[1]) - config.event_delay_lower[EVENT_LEVEL_MODERATE] = MinutesToTicks(values[2]) - config.event_delay_lower[EVENT_LEVEL_MAJOR] = MinutesToTicks(values[3]) - - if("event_delay_upper") - var/values = text2numlist(value, ";") - config.event_delay_upper[EVENT_LEVEL_MUNDANE] = MinutesToTicks(values[1]) - config.event_delay_upper[EVENT_LEVEL_MODERATE] = MinutesToTicks(values[2]) - config.event_delay_upper[EVENT_LEVEL_MAJOR] = MinutesToTicks(values[3]) - - if("starlight") - config.starlight = 1 - - if("player_reroute_cap") - var/vvalue = text2num(value) - config.player_overflow_cap = vvalue >= 0 ? vvalue : 0 - - if("overflow_server_url") - config.overflow_server_url = value - - if("disable_away_missions") - config.disable_away_missions = 1 - - if("disable_space_ruins") - config.disable_space_ruins = 1 - - if("disable_lobby_music") - config.disable_lobby_music = 1 - - if("disable_cid_warn_popup") - config.disable_cid_warn_popup = 1 - - if("extra_space_ruin_levels_min") - var/vvalue = text2num(value) - config.extra_space_ruin_levels_min = max(vvalue, 0) - - if("extra_space_ruin_levels_max") - var/vvalue = text2num(value) - config.extra_space_ruin_levels_max = max(vvalue, 0) - - if("max_loadout_points") - config.max_loadout_points = text2num(value) - - if("round_abandon_penalty_period") - config.round_abandon_penalty_period = MinutesToTicks(text2num(value)) - - if("medal_hub_address") - config.medal_hub_address = value - - if("medal_hub_password") - config.medal_hub_password = value - - if("disable_ooc_emoji") - config.disable_ooc_emoji = 1 - - if("shutdown_on_reboot") - config.shutdown_on_reboot = 1 - - if("shutdown_shell_command") - shutdown_shell_command = value - - if("disable_karma") - config.disable_karma = 1 - - if("start_now_confirmation") - config.start_now_confirmation = 1 - - if("tick_limit_mc_init") - config.tick_limit_mc_init = text2num(value) - if("base_mc_tick_rate") - config.base_mc_tick_rate = text2num(value) - if("high_pop_mc_tick_rate") - config.high_pop_mc_tick_rate = text2num(value) - if("high_pop_mc_mode_amount") - config.high_pop_mc_mode_amount = text2num(value) - if("disable_high_pop_mc_mode_amount") - config.disable_high_pop_mc_mode_amount = text2num(value) - if("developer_express_start") - config.developer_express_start = 1 - if("disable_localhost_admin") - config.disable_localhost_admin = 1 - if("enable_gamemode_player_limit") - config.enable_gamemode_player_limit = 1 - else - log_config("Unknown setting in configuration: '[name]'") - - - else if(type == "game_options") - value = text2num(value) - - switch(name) - if("revival_pod_plants") - config.revival_pod_plants = value - if("revival_cloning") - config.revival_cloning = value - if("revival_brain_life") - config.revival_brain_life = value - if("auto_toggle_ooc_during_round") - config.auto_toggle_ooc_during_round = 1 - if("run_speed") - config.run_speed = value - if("walk_speed") - config.walk_speed = value - if("human_delay") - config.human_delay = value - if("robot_delay") - config.robot_delay = value - if("monkey_delay") - config.monkey_delay = value - if("alien_delay") - config.alien_delay = value - if("slime_delay") - config.slime_delay = value - if("animal_delay") - config.animal_delay = value - if("bones_can_break") - config.bones_can_break = value - if("shuttle_refuel_delay") - config.shuttle_refuel_delay = text2num(value) - if("traitor_objectives_amount") - config.traitor_objectives_amount = text2num(value) - if("reactionary_explosions") - config.reactionary_explosions = 1 - if("bombcap") - var/BombCap = text2num(value) - if(!BombCap) - continue - if(BombCap < 4) - BombCap = 4 - if(BombCap > 128) - BombCap = 128 - - MAX_EX_DEVESTATION_RANGE = round(BombCap/4) - MAX_EX_HEAVY_RANGE = round(BombCap/2) - MAX_EX_LIGHT_RANGE = BombCap - MAX_EX_FLASH_RANGE = BombCap - MAX_EX_FLAME_RANGE = BombCap - if("default_laws") - config.default_laws = text2num(value) - if("randomize_shift_time") - config.randomize_shift_time = TRUE - if("enable_night_shifts") - config.enable_night_shifts = TRUE - if("lavaland_budget") - config.lavaland_budget = text2num(value) - if("cubemonkey_cap") - config.cubemonkeycap = text2num(value) - else - log_config("Unknown setting in configuration: '[name]'") - -/datum/configuration/proc/loadsql(filename) // -- TLE - var/list/Lines = file2list(filename) - var/db_version = 0 - for(var/t in Lines) - if(!t) continue - - t = trim(t) - if(length(t) == 0) - continue - else if(copytext(t, 1, 2) == "#") - continue - - var/pos = findtext(t, " ") - var/name = null - var/value = null - - if(pos) - name = lowertext(copytext(t, 1, pos)) - value = copytext(t, pos + 1) - else - name = lowertext(t) - - if(!name) - continue - - switch(name) - if("sql_enabled") - config.sql_enabled = 1 - if("address") - sqladdress = value - if("port") - sqlport = value - if("feedback_database") - sqlfdbkdb = value - if("feedback_login") - sqlfdbklogin = value - if("feedback_password") - sqlfdbkpass = value - if("feedback_tableprefix") - sqlfdbktableprefix = value - if("db_version") - db_version = text2num(value) - else - log_config("Unknown setting in configuration: '[name]'") - if(config.sql_enabled && db_version != SQL_VERSION) - config.sql_enabled = 0 - log_config("WARNING: DB_CONFIG DEFINITION MISMATCH!") - spawn(60) - if(SSticker.current_state == GAME_STATE_PREGAME) - going = 0 - spawn(600) - to_chat(world, "DB_CONFIG MISMATCH, ROUND START DELAYED.
    Please check database version for recent upstream changes!
    ") - -/datum/configuration/proc/loadoverflowwhitelist(filename) - var/list/Lines = file2list(filename) - for(var/t in Lines) - if(!t) continue - - t = trim(t) - if(length(t) == 0) - continue - else if(copytext(t, 1, 2) == "#") - continue - - config.overflow_whitelist += t - -/datum/configuration/proc/pick_mode(mode_name) - for(var/T in subtypesof(/datum/game_mode)) - var/datum/game_mode/M = T - if(initial(M.config_tag) && initial(M.config_tag) == mode_name) - return new T() - return new /datum/game_mode/extended() - -/datum/configuration/proc/get_runnable_modes() - var/list/datum/game_mode/runnable_modes = new - for(var/T in subtypesof(/datum/game_mode)) - var/datum/game_mode/M = new T() -// to_chat(world, "DEBUG: [T], tag=[M.config_tag], prob=[probabilities[M.config_tag]]") - if(!(M.config_tag in modes)) - qdel(M) - continue - if(probabilities[M.config_tag]<=0) - qdel(M) - continue - if(M.can_start()) - runnable_modes[M] = probabilities[M.config_tag] -// to_chat(world, "DEBUG: runnable_mode\[[runnable_modes.len]\] = [M.config_tag]") - return runnable_modes +/datum/configuration + var/server_name = null // server name (for world name / status) + var/server_tag_line = null // server tagline (for showing on hub entry) + var/server_extra_features = null // server-specific extra features (for hub entry) + var/server_suffix = 0 // generate numeric suffix based on server port + + var/minimum_client_build = 1421 // Build 1421 due to the middle mouse button exploit + + var/nudge_script_path = "nudge.py" // where the nudge.py script is located + + var/log_ooc = 0 // log OOC channel + var/log_access = 0 // log login/logout + var/log_say = 0 // log client say + var/log_admin = 0 // log admin actions + var/log_debug = 1 // log debug output + var/log_game = 0 // log game events + var/log_vote = 0 // log voting + var/log_whisper = 0 // log client whisper + var/log_emote = 0 // log emotes + var/log_attack = 0 // log attack messages + var/log_adminchat = 0 // log admin chat messages + var/log_adminwarn = 0 // log warnings admins get about bomb construction and such + var/log_pda = 0 // log pda messages + var/log_world_output = 0 // log world.log << messages + var/log_runtimes = 0 // logs world.log to a file + var/log_hrefs = 0 // logs all links clicked in-game. Could be used for debugging and tracking down exploits + var/sql_enabled = 0 // for sql switching + var/allow_admin_ooccolor = 0 // Allows admins with relevant permissions to have their own ooc colour + var/pregame_timestart = 240 // Time it takes for the server to start the game + var/allow_vote_restart = 0 // allow votes to restart + var/allow_vote_mode = 0 // allow votes to change mode + var/vote_delay = 6000 // minimum time between voting sessions (deciseconds, 10 minute default) + var/vote_period = 600 // length of voting period (deciseconds, default 1 minute) + var/vote_autotransfer_initial = 72000 // Length of time before the first autotransfer vote is called + var/vote_autotransfer_interval = 18000 // length of time before next sequential autotransfer vote + var/vote_no_default = 0 // vote does not default to nochange/norestart (tbi) + var/vote_no_dead = 0 // dead people can't vote (tbi) +// var/enable_authentication = 0 // goon authentication + var/del_new_on_log = 1 // del's new players if they log before they spawn in + var/feature_object_spell_system = 0 //spawns a spellbook which gives object-type spells instead of verb-type spells for the wizard + var/traitor_scaling = 0 //if amount of traitors scales based on amount of players + var/protect_roles_from_antagonist = 0// If security and such can be tratior/cult/other + var/continuous_rounds = 0 // Gamemodes which end instantly will instead keep on going until the round ends by escape shuttle or nuke. + var/allow_Metadata = 0 // Metadata is supported. + var/popup_admin_pm = 0 //adminPMs to non-admins show in a pop-up 'reply' window when set to 1. + var/Ticklag = 0.5 + var/socket_talk = 0 // use socket_talk to communicate with other processes + var/list/resource_urls = null + var/antag_hud_allowed = 0 // Ghosts can turn on Antagovision to see a HUD of who is the bad guys this round. + var/antag_hud_restricted = 0 // Ghosts that turn on Antagovision cannot rejoin the round. + var/list/mode_names = list() + var/list/modes = list() // allowed modes + var/list/votable_modes = list() // votable modes + var/list/probabilities = list() // relative probability of each mode + var/humans_need_surnames = 0 + var/allow_random_events = 0 // enables random events mid-round when set to 1 + var/allow_ai = 1 // allow ai job + var/respawn = 0 + var/guest_jobban = 1 + var/panic_bunker_threshold = 150 // above this player count threshold, never-before-seen players are blocked from connecting + var/usewhitelist = 0 + var/mods_are_mentors = 0 + var/load_jobs_from_txt = 0 + var/ToRban = 0 + var/automute_on = 0 //enables automuting/spam prevention + var/jobs_have_minimal_access = 0 //determines whether jobs use minimal access or expanded access. + var/round_abandon_penalty_period = 30 MINUTES // Time from round start during which ghosting out is penalized + var/medal_hub_address = null + var/medal_hub_password = null + + var/reactionary_explosions = 0 //If we use reactionary explosions, explosions that react to walls and doors + + var/assistantlimit = 0 //enables assistant limiting + var/assistantratio = 2 //how many assistants to security members + + // The AFK subsystem will not be activated if any of the below config values are equal or less than 0 + var/warn_afk_minimum = 0 // How long till you get a warning while being AFK + var/auto_cryo_afk = 0 // How long till you get put into cryo when you're AFK + var/auto_despawn_afk = 0 // How long till you actually despawn in cryo when you're AFK (Not ssd so not automatic) + + var/auto_cryo_ssd_mins = 0 + var/ssd_warning = 0 + + var/list_afk_minimum = 5 // How long people have to be AFK before it's listed on the "List AFK players" verb + + var/traitor_objectives_amount = 2 + var/shadowling_max_age = 0 + + var/max_maint_drones = 5 //This many drones can spawn, + var/allow_drone_spawn = 1 //assuming the admin allow them to. + var/drone_build_time = 1200 //A drone will become available every X ticks since last drone spawn. Default is 2 minutes. + + var/usealienwhitelist = 0 + var/limitalienplayers = 0 + var/alien_to_human_ratio = 0.5 + + var/server + var/banappeals + var/wikiurl = "http://example.org" + var/forumurl = "http://example.org" + var/rulesurl = "http://example.org" + var/githuburl = "http://example.org" + var/donationsurl = "http://example.org" + var/repositoryurl = "http://example.org" + var/discordurl = "http://example.org" + var/discordforumurl = "http://example.org" + + var/overflow_server_url + var/forbid_singulo_possession = 0 + + var/check_randomizer = 0 + + //game_options.txt configs + + var/bones_can_break = 1 + + var/revival_pod_plants = 1 + var/revival_cloning = 1 + var/revival_brain_life = -1 + + var/auto_toggle_ooc_during_round = 0 + + var/shuttle_refuel_delay = 12000 + + //Used for modifying movement speed for mobs. + //Unversal modifiers + var/run_speed = 0 + var/walk_speed = 0 + + //Mob specific modifiers. NOTE: These will affect different mob types in different ways + var/human_delay = 0 + var/robot_delay = 0 + var/monkey_delay = 0 + var/alien_delay = 0 + var/slime_delay = 0 + var/animal_delay = 0 + + //IP Intel vars + var/ipintel_email + var/ipintel_rating_bad = 1 + var/ipintel_save_good = 12 + var/ipintel_save_bad = 1 + var/ipintel_domain = "check.getipintel.net" + var/ipintel_maxplaytime = 0 + var/ipintel_whitelist = 0 + var/ipintel_detailsurl = "https://iphub.info/?ip=" + + var/forum_link_url + var/forum_playerinfo_url + + var/admin_legacy_system = 0 //Defines whether the server uses the legacy admin system with admins.txt or the SQL system. Config option in config.txt + var/ban_legacy_system = 0 //Defines whether the server uses the legacy banning system with the files in /data or the SQL system. Config option in config.txt + var/use_age_restriction_for_jobs = 0 //Do jobs use account age restrictions? --requires database + var/use_age_restriction_for_antags = 0 //Do antags use account age restrictions? --requires database + + var/use_exp_tracking = 0 + var/use_exp_restrictions = 0 + var/use_exp_restrictions_admin_bypass = 0 + + var/simultaneous_pm_warning_timeout = 100 + + var/assistant_maint = 0 //Do assistants get maint access? + var/gateway_delay = 6000 //How long the gateway takes before it activates. Default is half an hour. + var/ghost_interaction = 0 + + var/comms_password = "" + + var/use_irc_bot = 0 + var/list/irc_bot_host = list() + var/main_irc = "" + var/admin_irc = "" + var/admin_notify_irc = "" + var/cidrandomizer_irc = "" + + var/default_laws = 0 //Controls what laws the AI spawns with. + + var/list/station_levels = list(1) // Defines which Z-levels the station exists on. + var/list/admin_levels= list(2) // Defines which Z-levels which are for admin functionality, for example including such areas as Central Command and the Syndicate Shuttle + var/list/contact_levels = list(1, 5) // Defines which Z-levels which, for example, a Code Red announcement may affect + var/list/player_levels = list(1, 3, 4, 5, 6, 7) // Defines all Z-levels a character can typically reach + + var/const/minutes_to_ticks = 60 * 10 + // Event settings + var/expected_round_length = 60 * 2 * minutes_to_ticks // 2 hours + // If the first delay has a custom start time + // No custom time, no custom time, between 80 to 100 minutes respectively. + var/list/event_first_run = list(EVENT_LEVEL_MUNDANE = null, EVENT_LEVEL_MODERATE = null, EVENT_LEVEL_MAJOR = list("lower" = 48000, "upper" = 60000)) + // The lowest delay until next event + // 10, 30, 50 minutes respectively + var/list/event_delay_lower = list(EVENT_LEVEL_MUNDANE = 6000, EVENT_LEVEL_MODERATE = 18000, EVENT_LEVEL_MAJOR = 30000) + // The upper delay until next event + // 15, 45, 70 minutes respectively + var/list/event_delay_upper = list(EVENT_LEVEL_MUNDANE = 9000, EVENT_LEVEL_MODERATE = 27000, EVENT_LEVEL_MAJOR = 42000) + + var/starlight = 0 // Whether space turfs have ambient light or not + var/allow_holidays = 0 + var/player_overflow_cap = 0 //number of players before the server starts rerouting + var/list/overflow_whitelist = list() //whitelist for overflow + + var/disable_away_missions = 0 // disable away missions + var/disable_space_ruins = 0 //disable space ruins + + var/extra_space_ruin_levels_min = 2 + var/extra_space_ruin_levels_max = 4 + + var/ooc_allowed = 1 + var/looc_allowed = 1 + var/dooc_allowed = 1 + var/dsay_allowed = 1 + + var/disable_lobby_music = 0 // Disables the lobby music + var/disable_cid_warn_popup = 0 //disables the annoying "You have already logged in this round, disconnect or be banned" popup, because it annoys the shit out of me when testing. + + var/max_loadout_points = 5 // How many points can be spent on extra items in character setup + + var/disable_ooc_emoji = 0 // prevents people from using emoji in OOC + + var/shutdown_on_reboot = 0 // Whether to shut down the world instead of rebooting it + + var/disable_karma = 0 // Disable all karma functions and unlock karma jobs by default + + // StonedMC + var/tick_limit_mc_init = TICK_LIMIT_MC_INIT_DEFAULT //SSinitialization throttling + + // Highpop tickrates + var/base_mc_tick_rate = 1 + var/high_pop_mc_tick_rate = 1.1 + + var/high_pop_mc_mode_amount = 65 + var/disable_high_pop_mc_mode_amount = 60 + + // Nightshift + var/randomize_shift_time = FALSE + var/enable_night_shifts = FALSE + + // Developer + var/developer_express_start = 0 + + // Automatic localhost admin disable + var/disable_localhost_admin = 0 + + //Start now warning + var/start_now_confirmation = 0 + + // Lavaland + var/lavaland_budget = 60 + + //cube monkey limit + var/cubemonkeycap = 20 + + // Makes gamemodes respect player limits + var/enable_gamemode_player_limit = 0 + +/datum/configuration/New() + for(var/T in subtypesof(/datum/game_mode)) + var/datum/game_mode/M = T + + if(initial(M.config_tag)) + if(!(initial(M.config_tag) in modes)) // ensure each mode is added only once + src.modes += initial(M.config_tag) + src.mode_names[initial(M.config_tag)] = initial(M.name) + src.probabilities[initial(M.config_tag)] = initial(M.probability) + if(initial(M.votable)) + src.votable_modes += initial(M.config_tag) + src.votable_modes += "secret" + +/datum/configuration/proc/load(filename, type = "config") //the type can also be game_options, in which case it uses a different switch. not making it separate to not copypaste code - Urist + var/list/Lines = file2list(filename) + + for(var/t in Lines) + if(!t) continue + + t = trim(t) + if(length(t) == 0) + continue + else if(copytext(t, 1, 2) == "#") + continue + + var/pos = findtext(t, " ") + var/name = null + var/value = null + + if(pos) + name = lowertext(copytext(t, 1, pos)) + value = copytext(t, pos + 1) + else + name = lowertext(t) + + if(!name) + continue + + if(type == "config") + switch(name) + if("resource_urls") + config.resource_urls = splittext(value, " ") + + if("admin_legacy_system") + config.admin_legacy_system = 1 + + if("ban_legacy_system") + config.ban_legacy_system = 1 + + if("use_age_restriction_for_jobs") + config.use_age_restriction_for_jobs = 1 + + if("use_age_restriction_for_antags") + config.use_age_restriction_for_antags = 1 + + if("use_exp_tracking") + config.use_exp_tracking = 1 + + if("use_exp_restrictions") + config.use_exp_restrictions = 1 + + if("use_exp_restrictions_admin_bypass") + config.use_exp_restrictions_admin_bypass = 1 + + if("jobs_have_minimal_access") + config.jobs_have_minimal_access = 1 + + if("shadowling_max_age") + config.shadowling_max_age = text2num(value) + + if("warn_afk_minimum") + config.warn_afk_minimum = text2num(value) + if("auto_cryo_afk") + config.auto_cryo_afk = text2num(value) + if("auto_despawn_afk") + config.auto_despawn_afk = text2num(value) + + if("auto_cryo_ssd_mins") + config.auto_cryo_ssd_mins = text2num(value) + if("ssd_warning") + config.ssd_warning = 1 + + if("list_afk_minimum") + config.list_afk_minimum = text2num(value) + + if("ipintel_email") + if(value != "ch@nge.me") + config.ipintel_email = value + if("ipintel_rating_bad") + config.ipintel_rating_bad = text2num(value) + if("ipintel_domain") + config.ipintel_domain = value + if("ipintel_save_good") + config.ipintel_save_good = text2num(value) + if("ipintel_save_bad") + config.ipintel_save_bad = text2num(value) + if("ipintel_maxplaytime") + config.ipintel_maxplaytime = text2num(value) + if("ipintel_whitelist") + config.ipintel_whitelist = 1 + if("ipintel_detailsurl") + config.ipintel_detailsurl = value + + if("forum_link_url") + config.forum_link_url = value + + if("forum_playerinfo_url") + config.forum_playerinfo_url = value + + if("log_ooc") + config.log_ooc = 1 + + if("log_access") + config.log_access = 1 + + if("log_say") + config.log_say = 1 + + if("log_admin") + config.log_admin = 1 + + if("log_debug") + config.log_debug = 1 + + if("log_game") + config.log_game = 1 + + if("log_vote") + config.log_vote = 1 + + if("log_whisper") + config.log_whisper = 1 + + if("log_attack") + config.log_attack = 1 + + if("log_emote") + config.log_emote = 1 + + if("log_adminchat") + config.log_adminchat = 1 + + if("log_adminwarn") + config.log_adminwarn = 1 + + if("log_pda") + config.log_pda = 1 + + if("log_world_output") + config.log_world_output = 1 + + if("log_hrefs") + config.log_hrefs = 1 + + if("log_runtime") + config.log_runtimes = 1 + + if("mentors") + config.mods_are_mentors = 1 + + if("allow_admin_ooccolor") + config.allow_admin_ooccolor = 1 + + if("pregame_timestart") + config.pregame_timestart = text2num(value) + + if("allow_vote_restart") + config.allow_vote_restart = 1 + + if("allow_vote_mode") + config.allow_vote_mode = 1 + + if("no_dead_vote") + config.vote_no_dead = 1 + + if("vote_autotransfer_initial") + config.vote_autotransfer_initial = text2num(value) + + if("vote_autotransfer_interval") + config.vote_autotransfer_interval = text2num(value) + + if("default_no_vote") + config.vote_no_default = 1 + + if("vote_delay") + config.vote_delay = text2num(value) + + if("vote_period") + config.vote_period = text2num(value) + + if("allow_ai") + config.allow_ai = 1 + +// if("authentication") +// config.enable_authentication = 1 + + if("norespawn") + config.respawn = 0 + + if("servername") + config.server_name = value + + if("server_tag_line") + config.server_tag_line = value + + if("server_extra_features") + config.server_extra_features = value + + if("serversuffix") + config.server_suffix = 1 + + if("minimum_client_build") + config.minimum_client_build = text2num(value) + + if("nudge_script_path") + config.nudge_script_path = value + + if("server") + config.server = value + + if("banappeals") + config.banappeals = value + + if("wikiurl") + config.wikiurl = value + + if("forumurl") + config.forumurl = value + + if("rulesurl") + config.rulesurl = value + + if("githuburl") + config.githuburl = value + + if("discordurl") + config.discordurl = value + + if("discordforumurl") + config.discordforumurl = value + + if("donationsurl") + config.donationsurl = value + + if("repositoryurl") + config.repositoryurl = value + + if("guest_jobban") + config.guest_jobban = 1 + + if("guest_ban") + guests_allowed = 0 + + if("panic_bunker_threshold") + config.panic_bunker_threshold = text2num(value) + + if("usewhitelist") + config.usewhitelist = 1 + + if("feature_object_spell_system") + config.feature_object_spell_system = 1 + + if("allow_metadata") + config.allow_Metadata = 1 + + if("traitor_scaling") + config.traitor_scaling = 1 + + if("protect_roles_from_antagonist") + config.protect_roles_from_antagonist = 1 + + if("probability") + var/prob_pos = findtext(value, " ") + var/prob_name = null + var/prob_value = null + + if(prob_pos) + prob_name = lowertext(copytext(value, 1, prob_pos)) + prob_value = copytext(value, prob_pos + 1) + if(prob_name in config.modes) + config.probabilities[prob_name] = text2num(prob_value) + else + log_config("Unknown game mode probability configuration definition: [prob_name].") + else + log_config("Incorrect probability configuration definition: [prob_name] [prob_value].") + + if("allow_random_events") + config.allow_random_events = 1 + + if("load_jobs_from_txt") + load_jobs_from_txt = 1 + + if("forbid_singulo_possession") + forbid_singulo_possession = 1 + + if("check_randomizer") + check_randomizer = 1 + + if("popup_admin_pm") + config.popup_admin_pm = 1 + + if("allow_holidays") + config.allow_holidays = 1 + + if("use_irc_bot") + use_irc_bot = 1 + + if("ticklag") + Ticklag = text2num(value) + + if("socket_talk") + socket_talk = text2num(value) + + if("allow_antag_hud") + config.antag_hud_allowed = 1 + + if("antag_hud_restricted") + config.antag_hud_restricted = 1 + + if("humans_need_surnames") + humans_need_surnames = 1 + + if("tor_ban") + ToRban = 1 + + if("automute_on") + automute_on = 1 + + if("usealienwhitelist") + usealienwhitelist = 1 + + if("alien_player_ratio") + limitalienplayers = 1 + alien_to_human_ratio = text2num(value) + + if("assistant_maint") + config.assistant_maint = 1 + + if("gateway_delay") + config.gateway_delay = text2num(value) + + if("continuous_rounds") + config.continuous_rounds = 1 + + if("ghost_interaction") + config.ghost_interaction = 1 + + if("comms_password") + config.comms_password = value + + if("irc_bot_host") + config.irc_bot_host = splittext(value, ";") + + if("main_irc") + config.main_irc = value + + if("admin_irc") + config.admin_irc = value + + if("admin_notify_irc") + config.admin_notify_irc = value + + if("cidrandomizer_irc") + config.cidrandomizer_irc = value + + if("python_path") + if(value) + python_path = value + else + if(world.system_type == UNIX) + python_path = "/usr/bin/env python2" + else //probably windows, if not this should work anyway + python_path = "pythonw" + + if("assistant_limit") + config.assistantlimit = 1 + + if("assistant_ratio") + config.assistantratio = text2num(value) + + if("allow_drone_spawn") + config.allow_drone_spawn = text2num(value) + + if("drone_build_time") + config.drone_build_time = text2num(value) + + if("max_maint_drones") + config.max_maint_drones = text2num(value) + + if("expected_round_length") + config.expected_round_length = MinutesToTicks(text2num(value)) + + if("event_custom_start_mundane") + var/values = text2numlist(value, ";") + config.event_first_run[EVENT_LEVEL_MUNDANE] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2])) + + if("event_custom_start_moderate") + var/values = text2numlist(value, ";") + config.event_first_run[EVENT_LEVEL_MODERATE] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2])) + + if("event_custom_start_major") + var/values = text2numlist(value, ";") + config.event_first_run[EVENT_LEVEL_MAJOR] = list("lower" = MinutesToTicks(values[1]), "upper" = MinutesToTicks(values[2])) + + if("event_delay_lower") + var/values = text2numlist(value, ";") + config.event_delay_lower[EVENT_LEVEL_MUNDANE] = MinutesToTicks(values[1]) + config.event_delay_lower[EVENT_LEVEL_MODERATE] = MinutesToTicks(values[2]) + config.event_delay_lower[EVENT_LEVEL_MAJOR] = MinutesToTicks(values[3]) + + if("event_delay_upper") + var/values = text2numlist(value, ";") + config.event_delay_upper[EVENT_LEVEL_MUNDANE] = MinutesToTicks(values[1]) + config.event_delay_upper[EVENT_LEVEL_MODERATE] = MinutesToTicks(values[2]) + config.event_delay_upper[EVENT_LEVEL_MAJOR] = MinutesToTicks(values[3]) + + if("starlight") + config.starlight = 1 + + if("player_reroute_cap") + var/vvalue = text2num(value) + config.player_overflow_cap = vvalue >= 0 ? vvalue : 0 + + if("overflow_server_url") + config.overflow_server_url = value + + if("disable_away_missions") + config.disable_away_missions = 1 + + if("disable_space_ruins") + config.disable_space_ruins = 1 + + if("disable_lobby_music") + config.disable_lobby_music = 1 + + if("disable_cid_warn_popup") + config.disable_cid_warn_popup = 1 + + if("extra_space_ruin_levels_min") + var/vvalue = text2num(value) + config.extra_space_ruin_levels_min = max(vvalue, 0) + + if("extra_space_ruin_levels_max") + var/vvalue = text2num(value) + config.extra_space_ruin_levels_max = max(vvalue, 0) + + if("max_loadout_points") + config.max_loadout_points = text2num(value) + + if("round_abandon_penalty_period") + config.round_abandon_penalty_period = MinutesToTicks(text2num(value)) + + if("medal_hub_address") + config.medal_hub_address = value + + if("medal_hub_password") + config.medal_hub_password = value + + if("disable_ooc_emoji") + config.disable_ooc_emoji = 1 + + if("shutdown_on_reboot") + config.shutdown_on_reboot = 1 + + if("shutdown_shell_command") + shutdown_shell_command = value + + if("disable_karma") + config.disable_karma = 1 + + if("start_now_confirmation") + config.start_now_confirmation = 1 + + if("tick_limit_mc_init") + config.tick_limit_mc_init = text2num(value) + if("base_mc_tick_rate") + config.base_mc_tick_rate = text2num(value) + if("high_pop_mc_tick_rate") + config.high_pop_mc_tick_rate = text2num(value) + if("high_pop_mc_mode_amount") + config.high_pop_mc_mode_amount = text2num(value) + if("disable_high_pop_mc_mode_amount") + config.disable_high_pop_mc_mode_amount = text2num(value) + if("developer_express_start") + config.developer_express_start = 1 + if("disable_localhost_admin") + config.disable_localhost_admin = 1 + if("enable_gamemode_player_limit") + config.enable_gamemode_player_limit = 1 + else + log_config("Unknown setting in configuration: '[name]'") + + + else if(type == "game_options") + value = text2num(value) + + switch(name) + if("revival_pod_plants") + config.revival_pod_plants = value + if("revival_cloning") + config.revival_cloning = value + if("revival_brain_life") + config.revival_brain_life = value + if("auto_toggle_ooc_during_round") + config.auto_toggle_ooc_during_round = 1 + if("run_speed") + config.run_speed = value + if("walk_speed") + config.walk_speed = value + if("human_delay") + config.human_delay = value + if("robot_delay") + config.robot_delay = value + if("monkey_delay") + config.monkey_delay = value + if("alien_delay") + config.alien_delay = value + if("slime_delay") + config.slime_delay = value + if("animal_delay") + config.animal_delay = value + if("bones_can_break") + config.bones_can_break = value + if("shuttle_refuel_delay") + config.shuttle_refuel_delay = text2num(value) + if("traitor_objectives_amount") + config.traitor_objectives_amount = text2num(value) + if("reactionary_explosions") + config.reactionary_explosions = 1 + if("bombcap") + var/BombCap = text2num(value) + if(!BombCap) + continue + if(BombCap < 4) + BombCap = 4 + if(BombCap > 128) + BombCap = 128 + + MAX_EX_DEVASTATION_RANGE = round(BombCap/4) + MAX_EX_HEAVY_RANGE = round(BombCap/2) + MAX_EX_LIGHT_RANGE = BombCap + MAX_EX_FLASH_RANGE = BombCap + MAX_EX_FLAME_RANGE = BombCap + if("default_laws") + config.default_laws = text2num(value) + if("randomize_shift_time") + config.randomize_shift_time = TRUE + if("enable_night_shifts") + config.enable_night_shifts = TRUE + if("lavaland_budget") + config.lavaland_budget = text2num(value) + if("cubemonkey_cap") + config.cubemonkeycap = text2num(value) + else + log_config("Unknown setting in configuration: '[name]'") + +/datum/configuration/proc/loadsql(filename) // -- TLE + var/list/Lines = file2list(filename) + var/db_version = 0 + for(var/t in Lines) + if(!t) continue + + t = trim(t) + if(length(t) == 0) + continue + else if(copytext(t, 1, 2) == "#") + continue + + var/pos = findtext(t, " ") + var/name = null + var/value = null + + if(pos) + name = lowertext(copytext(t, 1, pos)) + value = copytext(t, pos + 1) + else + name = lowertext(t) + + if(!name) + continue + + switch(name) + if("sql_enabled") + config.sql_enabled = 1 + if("address") + sqladdress = value + if("port") + sqlport = value + if("feedback_database") + sqlfdbkdb = value + if("feedback_login") + sqlfdbklogin = value + if("feedback_password") + sqlfdbkpass = value + if("feedback_tableprefix") + sqlfdbktableprefix = value + if("db_version") + db_version = text2num(value) + else + log_config("Unknown setting in configuration: '[name]'") + if(config.sql_enabled && db_version != SQL_VERSION) + config.sql_enabled = 0 + log_config("WARNING: DB_CONFIG DEFINITION MISMATCH!") + spawn(60) + if(SSticker.current_state == GAME_STATE_PREGAME) + going = 0 + spawn(600) + to_chat(world, "DB_CONFIG MISMATCH, ROUND START DELAYED.
    Please check database version for recent upstream changes!
    ") + +/datum/configuration/proc/loadoverflowwhitelist(filename) + var/list/Lines = file2list(filename) + for(var/t in Lines) + if(!t) continue + + t = trim(t) + if(length(t) == 0) + continue + else if(copytext(t, 1, 2) == "#") + continue + + config.overflow_whitelist += t + +/datum/configuration/proc/pick_mode(mode_name) + for(var/T in subtypesof(/datum/game_mode)) + var/datum/game_mode/M = T + if(initial(M.config_tag) && initial(M.config_tag) == mode_name) + return new T() + return new /datum/game_mode/extended() + +/datum/configuration/proc/get_runnable_modes() + var/list/datum/game_mode/runnable_modes = new + for(var/T in subtypesof(/datum/game_mode)) + var/datum/game_mode/M = new T() +// to_chat(world, "DEBUG: [T], tag=[M.config_tag], prob=[probabilities[M.config_tag]]") + if(!(M.config_tag in modes)) + qdel(M) + continue + if(probabilities[M.config_tag]<=0) + qdel(M) + continue + if(M.can_start()) + runnable_modes[M] = probabilities[M.config_tag] +// to_chat(world, "DEBUG: runnable_mode\[[runnable_modes.len]\] = [M.config_tag]") + return runnable_modes diff --git a/code/controllers/controller.dm b/code/controllers/controller.dm index 06547d120d5..c9d5f1e5650 100644 --- a/code/controllers/controller.dm +++ b/code/controllers/controller.dm @@ -16,4 +16,4 @@ /datum/controller/proc/Recover() -/datum/controller/proc/stat_entry() \ No newline at end of file +/datum/controller/proc/stat_entry() diff --git a/code/controllers/failsafe.dm b/code/controllers/failsafe.dm index 8b0f0b1987e..bebcdc7a897 100644 --- a/code/controllers/failsafe.dm +++ b/code/controllers/failsafe.dm @@ -1,102 +1,102 @@ - /** - * Failsafe - * - * Pretty much pokes the MC to make sure it's still alive. - **/ - -GLOBAL_REAL(Failsafe, /datum/controller/failsafe) - -/datum/controller/failsafe // This thing pretty much just keeps poking the master controller - name = "Failsafe" - - // The length of time to check on the MC (in deciseconds). - // Set to 0 to disable. - var/processing_interval = 20 - // The alert level. For every failed poke, we drop a DEFCON level. Once we hit DEFCON 1, restart the MC. - var/defcon = 5 - //the world.time of the last check, so the mc can restart US if we hang. - // (Real friends look out for *eachother*) - var/lasttick = 0 - - // Track the MC iteration to make sure its still on track. - var/master_iteration = 0 - var/running = TRUE - -/datum/controller/failsafe/New() - // Highlander-style: there can only be one! Kill off the old and replace it with the new. - if(Failsafe != src) - if(istype(Failsafe)) - qdel(Failsafe) - Failsafe = src - Initialize() - -/datum/controller/failsafe/Initialize() - set waitfor = 0 - Failsafe.Loop() - if(!QDELETED(src)) - qdel(src) //when Loop() returns, we delete ourselves and let the mc recreate us - -/datum/controller/failsafe/Destroy() - running = FALSE - ..() - return QDEL_HINT_HARDDEL_NOW - -/datum/controller/failsafe/proc/Loop() - while(running) - lasttick = world.time - if(!Master) - // Replace the missing Master! This should never, ever happen. - new /datum/controller/master() - // Only poke it if overrides are not in effect. - if(processing_interval > 0) - if(Master.processing && Master.iteration) - // Check if processing is done yet. - if(Master.iteration == master_iteration) - switch(defcon) - if(4,5) - --defcon - if(3) - message_admins("Notice: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5 - defcon) * processing_interval] ticks.") - --defcon - if(2) - to_chat(GLOB.admins, "Warning: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5 - defcon) * processing_interval] ticks. Automatic restart in [processing_interval] ticks.") - --defcon - if(1) - - to_chat(GLOB.admins, "Warning: DEFCON [defcon_pretty()]. The Master Controller has still not fired within the last [(5 - defcon) * processing_interval] ticks. Killing and restarting...") - --defcon - var/rtn = Recreate_MC() - if(rtn > 0) - defcon = 4 - master_iteration = 0 - to_chat(GLOB.admins, "MC restarted successfully") - else if(rtn < 0) - log_game("FailSafe: Could not restart MC, runtime encountered. Entering defcon 0") - to_chat(GLOB.admins, "ERROR: DEFCON [defcon_pretty()]. Could not restart MC, runtime encountered. I will silently keep retrying.") - //if the return number was 0, it just means the mc was restarted too recently, and it just needs some time before we try again - //no need to handle that specially when defcon 0 can handle it - if(0) //DEFCON 0! (mc failed to restart) - var/rtn = Recreate_MC() - if(rtn > 0) - defcon = 4 - master_iteration = 0 - to_chat(GLOB.admins, "MC restarted successfully") - else - defcon = min(defcon + 1,5) - master_iteration = Master.iteration - if(defcon <= 1) - sleep(processing_interval * 2) - else - sleep(processing_interval) - else - defcon = 5 - sleep(initial(processing_interval)) - -/datum/controller/failsafe/proc/defcon_pretty() - return defcon - -/datum/controller/failsafe/stat_entry() - if(!statclick) - statclick = new/obj/effect/statclick/debug(null, "Initializing...", src) - - stat("Failsafe Controller:", statclick.update("Defcon: [defcon_pretty()] (Interval: [Failsafe.processing_interval] | Iteration: [Failsafe.master_iteration])")) + /** + * Failsafe + * + * Pretty much pokes the MC to make sure it's still alive. + **/ + +GLOBAL_REAL(Failsafe, /datum/controller/failsafe) + +/datum/controller/failsafe // This thing pretty much just keeps poking the master controller + name = "Failsafe" + + // The length of time to check on the MC (in deciseconds). + // Set to 0 to disable. + var/processing_interval = 20 + // The alert level. For every failed poke, we drop a DEFCON level. Once we hit DEFCON 1, restart the MC. + var/defcon = 5 + //the world.time of the last check, so the mc can restart US if we hang. + // (Real friends look out for *eachother*) + var/lasttick = 0 + + // Track the MC iteration to make sure its still on track. + var/master_iteration = 0 + var/running = TRUE + +/datum/controller/failsafe/New() + // Highlander-style: there can only be one! Kill off the old and replace it with the new. + if(Failsafe != src) + if(istype(Failsafe)) + qdel(Failsafe) + Failsafe = src + Initialize() + +/datum/controller/failsafe/Initialize() + set waitfor = 0 + Failsafe.Loop() + if(!QDELETED(src)) + qdel(src) //when Loop() returns, we delete ourselves and let the mc recreate us + +/datum/controller/failsafe/Destroy() + running = FALSE + ..() + return QDEL_HINT_HARDDEL_NOW + +/datum/controller/failsafe/proc/Loop() + while(running) + lasttick = world.time + if(!Master) + // Replace the missing Master! This should never, ever happen. + new /datum/controller/master() + // Only poke it if overrides are not in effect. + if(processing_interval > 0) + if(Master.processing && Master.iteration) + // Check if processing is done yet. + if(Master.iteration == master_iteration) + switch(defcon) + if(4,5) + --defcon + if(3) + message_admins("Notice: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5 - defcon) * processing_interval] ticks.") + --defcon + if(2) + to_chat(GLOB.admins, "Warning: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5 - defcon) * processing_interval] ticks. Automatic restart in [processing_interval] ticks.") + --defcon + if(1) + + to_chat(GLOB.admins, "Warning: DEFCON [defcon_pretty()]. The Master Controller has still not fired within the last [(5 - defcon) * processing_interval] ticks. Killing and restarting...") + --defcon + var/rtn = Recreate_MC() + if(rtn > 0) + defcon = 4 + master_iteration = 0 + to_chat(GLOB.admins, "MC restarted successfully") + else if(rtn < 0) + log_game("FailSafe: Could not restart MC, runtime encountered. Entering defcon 0") + to_chat(GLOB.admins, "ERROR: DEFCON [defcon_pretty()]. Could not restart MC, runtime encountered. I will silently keep retrying.") + //if the return number was 0, it just means the mc was restarted too recently, and it just needs some time before we try again + //no need to handle that specially when defcon 0 can handle it + if(0) //DEFCON 0! (mc failed to restart) + var/rtn = Recreate_MC() + if(rtn > 0) + defcon = 4 + master_iteration = 0 + to_chat(GLOB.admins, "MC restarted successfully") + else + defcon = min(defcon + 1,5) + master_iteration = Master.iteration + if(defcon <= 1) + sleep(processing_interval * 2) + else + sleep(processing_interval) + else + defcon = 5 + sleep(initial(processing_interval)) + +/datum/controller/failsafe/proc/defcon_pretty() + return defcon + +/datum/controller/failsafe/stat_entry() + if(!statclick) + statclick = new/obj/effect/statclick/debug(null, "Initializing...", src) + + stat("Failsafe Controller:", statclick.update("Defcon: [defcon_pretty()] (Interval: [Failsafe.processing_interval] | Iteration: [Failsafe.master_iteration])")) diff --git a/code/controllers/globals.dm b/code/controllers/globals.dm index 543f0aed4b2..5a520b547e1 100644 --- a/code/controllers/globals.dm +++ b/code/controllers/globals.dm @@ -64,4 +64,4 @@ GLOBAL_REAL(GLOB, /datum/controller/global_vars) call(src, I)() var/end_tick = world.time if(end_tick - start_tick) - warning("Global [replacetext("[I]", "InitGlobal", "")] slept during initialization!") \ No newline at end of file + warning("Global [replacetext("[I]", "InitGlobal", "")] slept during initialization!") diff --git a/code/controllers/master.dm b/code/controllers/master.dm index 10c6b42a369..f64e566e097 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -615,4 +615,4 @@ GLOBAL_REAL(Master, /datum/controller/master) = new if(client_count < config.disable_high_pop_mc_mode_amount) processing = config.base_mc_tick_rate else if(client_count > config.high_pop_mc_mode_amount) - processing = config.high_pop_mc_tick_rate \ No newline at end of file + processing = config.high_pop_mc_tick_rate diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm index 7ba1b68d4a6..b8996f2cf89 100644 --- a/code/controllers/subsystem.dm +++ b/code/controllers/subsystem.dm @@ -214,4 +214,4 @@ next_fire = world.time + wait if("queued_priority") //editing this breaks things. return 0 - . = ..() \ No newline at end of file + . = ..() diff --git a/code/controllers/subsystem/acid.dm b/code/controllers/subsystem/acid.dm index f86c8540af5..40063b00a87 100644 --- a/code/controllers/subsystem/acid.dm +++ b/code/controllers/subsystem/acid.dm @@ -33,4 +33,4 @@ SUBSYSTEM_DEF(acid) processing -= O if(MC_TICK_CHECK) - return \ No newline at end of file + return diff --git a/code/controllers/subsystem/afk.dm b/code/controllers/subsystem/afk.dm index 9f409fd35b4..6ab1d67a816 100644 --- a/code/controllers/subsystem/afk.dm +++ b/code/controllers/subsystem/afk.dm @@ -78,4 +78,4 @@ SUBSYSTEM_DEF(afk) break #undef AFK_WARNED -#undef AFK_CRYOD \ No newline at end of file +#undef AFK_CRYOD diff --git a/code/controllers/subsystem/air.dm b/code/controllers/subsystem/air.dm index fe48494f321..04141f6c3ab 100644 --- a/code/controllers/subsystem/air.dm +++ b/code/controllers/subsystem/air.dm @@ -382,4 +382,4 @@ SUBSYSTEM_DEF(air) #undef SSAIR_EXCITEDGROUPS #undef SSAIR_HIGHPRESSURE #undef SSAIR_HOTSPOTS -#undef SSAIR_SUPERCONDUCTIVITY \ No newline at end of file +#undef SSAIR_SUPERCONDUCTIVITY diff --git a/code/controllers/subsystem/alarm.dm b/code/controllers/subsystem/alarm.dm index 90e3ff960f1..bac32a449de 100644 --- a/code/controllers/subsystem/alarm.dm +++ b/code/controllers/subsystem/alarm.dm @@ -1,30 +1,30 @@ -SUBSYSTEM_DEF(alarms) - name = "Alarms" - init_order = INIT_ORDER_ALARMS // 2 - var/datum/alarm_handler/atmosphere/atmosphere_alarm = new() - var/datum/alarm_handler/burglar/burglar_alarm = new() - var/datum/alarm_handler/camera/camera_alarm = new() - var/datum/alarm_handler/fire/fire_alarm = new() - var/datum/alarm_handler/motion/motion_alarm = new() - var/datum/alarm_handler/power/power_alarm = new() - var/list/datum/alarm/all_handlers - -/datum/controller/subsystem/alarms/Initialize(start_timeofday) - all_handlers = list(SSalarms.atmosphere_alarm, SSalarms.burglar_alarm, SSalarms.camera_alarm, SSalarms.fire_alarm, SSalarms.motion_alarm, SSalarms.power_alarm) - return ..() - -/datum/controller/subsystem/alarms/fire() - for(var/datum/alarm_handler/AH in all_handlers) - AH.process() - -/datum/controller/subsystem/alarms/proc/active_alarms() - var/list/all_alarms = new () - for(var/datum/alarm_handler/AH in all_handlers) - var/list/alarms = AH.alarms - all_alarms += alarms - - return all_alarms - -/datum/controller/subsystem/alarms/proc/number_of_active_alarms() - var/list/alarms = active_alarms() - return alarms.len +SUBSYSTEM_DEF(alarms) + name = "Alarms" + init_order = INIT_ORDER_ALARMS // 2 + var/datum/alarm_handler/atmosphere/atmosphere_alarm = new() + var/datum/alarm_handler/burglar/burglar_alarm = new() + var/datum/alarm_handler/camera/camera_alarm = new() + var/datum/alarm_handler/fire/fire_alarm = new() + var/datum/alarm_handler/motion/motion_alarm = new() + var/datum/alarm_handler/power/power_alarm = new() + var/list/datum/alarm/all_handlers + +/datum/controller/subsystem/alarms/Initialize(start_timeofday) + all_handlers = list(SSalarms.atmosphere_alarm, SSalarms.burglar_alarm, SSalarms.camera_alarm, SSalarms.fire_alarm, SSalarms.motion_alarm, SSalarms.power_alarm) + return ..() + +/datum/controller/subsystem/alarms/fire() + for(var/datum/alarm_handler/AH in all_handlers) + AH.process() + +/datum/controller/subsystem/alarms/proc/active_alarms() + var/list/all_alarms = new () + for(var/datum/alarm_handler/AH in all_handlers) + var/list/alarms = AH.alarms + all_alarms += alarms + + return all_alarms + +/datum/controller/subsystem/alarms/proc/number_of_active_alarms() + var/list/alarms = active_alarms() + return alarms.len diff --git a/code/controllers/subsystem/assets.dm b/code/controllers/subsystem/assets.dm index cd531db614a..7a9a3d355f3 100644 --- a/code/controllers/subsystem/assets.dm +++ b/code/controllers/subsystem/assets.dm @@ -14,4 +14,4 @@ SUBSYSTEM_DEF(assets) for(var/client/C in GLOB.clients) addtimer(CALLBACK(GLOBAL_PROC, .proc/getFilesSlow, C, preload, FALSE), 10) - return ..() \ No newline at end of file + return ..() diff --git a/code/controllers/subsystem/chat.dm b/code/controllers/subsystem/chat.dm index a90e14d0ab4..dbe38634e6a 100644 --- a/code/controllers/subsystem/chat.dm +++ b/code/controllers/subsystem/chat.dm @@ -11,7 +11,8 @@ SUBSYSTEM_DEF(chat) /datum/controller/subsystem/chat/fire() for(var/i in payload) var/client/C = i - C << output(payload[C], "browseroutput:output") + if(C) + C << output(payload[C], "browseroutput:output") payload -= C if(MC_TICK_CHECK) @@ -62,4 +63,4 @@ SUBSYSTEM_DEF(chat) C.chatOutput.messageQueue += message return - payload[C] += twiceEncoded \ No newline at end of file + payload[C] += twiceEncoded diff --git a/code/controllers/subsystem/events.dm b/code/controllers/subsystem/events.dm index 2f415efa38d..0465f47e152 100644 --- a/code/controllers/subsystem/events.dm +++ b/code/controllers/subsystem/events.dm @@ -1,299 +1,299 @@ -SUBSYSTEM_DEF(events) - name = "Events" - init_order = INIT_ORDER_EVENTS - runlevels = RUNLEVEL_GAME - // Report events at the end of the rouund - var/report_at_round_end = 0 - - // UI vars - var/window_x = 700 - var/window_y = 600 - var/table_options = " align='center'" - var/head_options = " style='font-weight:bold;'" - var/row_options1 = " width='85px'" - var/row_options2 = " width='260px'" - var/row_options3 = " width='150px'" - - // Event vars - var/datum/event_container/selected_event_container = null - var/list/active_events = list() - var/list/finished_events = list() - var/list/allEvents - var/list/event_containers = list( - EVENT_LEVEL_MUNDANE = new/datum/event_container/mundane, - EVENT_LEVEL_MODERATE = new/datum/event_container/moderate, - EVENT_LEVEL_MAJOR = new/datum/event_container/major - ) - - var/datum/event_meta/new_event = new - -/datum/controller/subsystem/events/Initialize() - allEvents = subtypesof(/datum/event) - return ..() - -/datum/controller/subsystem/events/fire() - for(var/datum/event/E in active_events) - E.process() - - for(var/i = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR) - var/list/datum/event_container/EC = event_containers[i] - EC.process() - -/datum/controller/subsystem/events/proc/event_complete(var/datum/event/E) - if(!E.event_meta) // datum/event is used here and there for random reasons, maintaining "backwards compatibility" - log_debug("Event of '[E.type]' with missing meta-data has completed.") - return - - finished_events += E - - var/theseverity - - if(!E.severity) - theseverity = EVENT_LEVEL_MODERATE - - if(!E.severity == EVENT_LEVEL_MUNDANE && !E.severity == EVENT_LEVEL_MODERATE && !E.severity == EVENT_LEVEL_MAJOR) - theseverity = EVENT_LEVEL_MODERATE //just to be careful - - if(E.severity) - theseverity = E.severity - - // Add the event back to the list of available events - var/datum/event_container/EC = event_containers[theseverity] - var/datum/event_meta/EM = E.event_meta - EC.available_events += EM - - log_debug("Event '[EM.name]' has completed at [station_time_timestamp()].") - -/datum/controller/subsystem/events/proc/delay_events(var/severity, var/delay) - var/list/datum/event_container/EC = event_containers[severity] - EC.next_event_time += delay - -/datum/controller/subsystem/events/proc/Interact(var/mob/living/user) - - var/html = GetInteractWindow() - - var/datum/browser/popup = new(user, "event_manager", "Event Manager", window_x, window_y) - popup.set_content(html) - popup.open() - -/datum/controller/subsystem/events/proc/RoundEnd() - if(!report_at_round_end) - return - - to_chat(world, "


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

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

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

    Add Event

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

    Event Start

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

    Next Event

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

    Running Events

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


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

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

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

    Add Event

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

    Event Start

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

    Next Event

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

    Running Events

    " + html += "Estimated times, affected by master controller delays." + html += "" + html += "SeverityNameEnds AtEnds InStop" + for(var/datum/event/E in active_events) + if(!E.event_meta) + continue + var/datum/event_meta/EM = E.event_meta + var/ends_at = E.startedAt + (E.lastProcessAt() * 20) // A best estimate, based on how often the manager processes + var/ends_in = max(0, round((ends_at - world.time) / 600, 0.1)) + var/no_end = E.noAutoEnd + html += "" + html += "[severity_to_string[EM.severity]]" + html += "[EM.name]" + html += "[no_end ? "N/A" : station_time_timestamp("hh:mm:ss", ends_at)]" + html += "[no_end ? "N/A" : ends_in]" + html += "Stop" + html += "" + html += "" + html += "
    " + + return html + +/datum/controller/subsystem/events/Topic(href, href_list) + if(..()) + return + + + if(href_list["toggle_report"]) + report_at_round_end = !report_at_round_end + admin_log_and_message_admins("has [report_at_round_end ? "enabled" : "disabled"] the round end event report.") + else if(href_list["dec_timer"]) + var/datum/event_container/EC = locate(href_list["event"]) + var/decrease = (60 * RaiseToPower(10, text2num(href_list["dec_timer"]))) + EC.next_event_time -= decrease + admin_log_and_message_admins("decreased timer for [severity_to_string[EC.severity]] events by [decrease/600] minute(s).") + else if(href_list["inc_timer"]) + var/datum/event_container/EC = locate(href_list["event"]) + var/increase = (60 * RaiseToPower(10, text2num(href_list["inc_timer"]))) + EC.next_event_time += increase + admin_log_and_message_admins("increased timer for [severity_to_string[EC.severity]] events by [increase/600] minute(s).") + else if(href_list["select_event"]) + var/datum/event_container/EC = locate(href_list["select_event"]) + var/datum/event_meta/EM = EC.SelectEvent() + if(EM) + admin_log_and_message_admins("has queued the [severity_to_string[EC.severity]] event '[EM.name]'.") + else if(href_list["pause"]) + var/datum/event_container/EC = locate(href_list["pause"]) + EC.delayed = !EC.delayed + admin_log_and_message_admins("has [EC.delayed ? "paused" : "resumed"] countdown for [severity_to_string[EC.severity]] events.") + else if(href_list["interval"]) + var/delay = input("Enter delay modifier. A value less than one means events fire more often, higher than one less often.", "Set Interval Modifier") as num|null + if(delay && delay > 0) + var/datum/event_container/EC = locate(href_list["interval"]) + EC.delay_modifier = delay + admin_log_and_message_admins("has set the interval modifier for [severity_to_string[EC.severity]] events to [EC.delay_modifier].") + else if(href_list["stop"]) + if(alert("Stopping an event may have unintended side-effects. Continue?","Stopping Event!","Yes","No") != "Yes") + return + var/datum/event/E = locate(href_list["stop"]) + var/datum/event_meta/EM = E.event_meta + admin_log_and_message_admins("has stopped the [severity_to_string[EM.severity]] event '[EM.name]'.") + E.kill() + else if(href_list["view_events"]) + selected_event_container = locate(href_list["view_events"]) + else if(href_list["back"]) + selected_event_container = null + else if(href_list["set_name"]) + var/name = clean_input("Enter event name.", "Set Name") + if(name) + var/datum/event_meta/EM = locate(href_list["set_name"]) + EM.name = name + else if(href_list["set_type"]) + var/type = input("Select event type.", "Select") as null|anything in allEvents + if(type) + var/datum/event_meta/EM = locate(href_list["set_type"]) + EM.event_type = type + else if(href_list["set_weight"]) + var/weight = input("Enter weight. A higher value means higher chance for the event of being selected.", "Set Weight") as num|null + if(weight && weight > 0) + var/datum/event_meta/EM = locate(href_list["set_weight"]) + EM.weight = weight + if(EM != new_event) + admin_log_and_message_admins("has changed the weight of the [severity_to_string[EM.severity]] event '[EM.name]' to [EM.weight].") + else if(href_list["toggle_oneshot"]) + var/datum/event_meta/EM = locate(href_list["toggle_oneshot"]) + EM.one_shot = !EM.one_shot + if(EM != new_event) + admin_log_and_message_admins("has [EM.one_shot ? "set" : "unset"] the oneshot flag for the [severity_to_string[EM.severity]] event '[EM.name]'.") + else if(href_list["toggle_enabled"]) + var/datum/event_meta/EM = locate(href_list["toggle_enabled"]) + EM.enabled = !EM.enabled + admin_log_and_message_admins("has [EM.enabled ? "enabled" : "disabled"] the [severity_to_string[EM.severity]] event '[EM.name]'.") + else if(href_list["remove"]) + if(alert("This will remove the event from rotation. Continue?","Removing Event!","Yes","No") != "Yes") + return + var/datum/event_meta/EM = locate(href_list["remove"]) + var/datum/event_container/EC = locate(href_list["EC"]) + EC.available_events -= EM + admin_log_and_message_admins("has removed the [severity_to_string[EM.severity]] event '[EM.name]'.") + else if(href_list["add"]) + if(!new_event.name || !new_event.event_type) + return + if(alert("This will add a new event to the rotation. Continue?","Add Event!","Yes","No") != "Yes") + return + new_event.severity = selected_event_container.severity + selected_event_container.available_events += new_event + admin_log_and_message_admins("has added \a [severity_to_string[new_event.severity]] event '[new_event.name]' of type [new_event.event_type] with weight [new_event.weight].") + new_event = new + else if(href_list["clear"]) + var/datum/event_container/EC = locate(href_list["clear"]) + if(EC.next_event) + admin_log_and_message_admins("has dequeued the [severity_to_string[EC.severity]] event '[EC.next_event.name]'.") + EC.next_event = null + + Interact(usr) diff --git a/code/controllers/subsystem/garbage.dm b/code/controllers/subsystem/garbage.dm index 4c397636cab..4bc3965e6cf 100644 --- a/code/controllers/subsystem/garbage.dm +++ b/code/controllers/subsystem/garbage.dm @@ -432,4 +432,4 @@ SUBSYSTEM_DEF(garbage) CHECK_TICK #endif -#endif \ No newline at end of file +#endif diff --git a/code/controllers/subsystem/holiday.dm b/code/controllers/subsystem/holiday.dm index be9d6d4fc7d..ddde9cd62be 100644 --- a/code/controllers/subsystem/holiday.dm +++ b/code/controllers/subsystem/holiday.dm @@ -1,31 +1,31 @@ -SUBSYSTEM_DEF(holiday) - name = "Holiday" - init_order = INIT_ORDER_HOLIDAY // 3 - flags = SS_NO_FIRE - var/list/holidays - -/datum/controller/subsystem/holiday/Initialize(start_timeofday) - if(!config.allow_holidays) - return ..() //Holiday stuff was not enabled in the config! - - var/YY = text2num(time2text(world.timeofday, "YY")) // get the current year - var/MM = text2num(time2text(world.timeofday, "MM")) // get the current month - var/DD = text2num(time2text(world.timeofday, "DD")) // get the current day - - for(var/H in subtypesof(/datum/holiday)) - var/datum/holiday/holiday = new H() - if(holiday.shouldCelebrate(DD, MM, YY)) - holiday.celebrate() - if(!holidays) - holidays = list() - holidays[holiday.name] = holiday - - if(holidays) - holidays = shuffle(holidays) - world.update_status() - for(var/datum/holiday/H in holidays) - if(H.eventChance) - if(prob(H.eventChance)) - H.handle_event() - - return ..() \ No newline at end of file +SUBSYSTEM_DEF(holiday) + name = "Holiday" + init_order = INIT_ORDER_HOLIDAY // 3 + flags = SS_NO_FIRE + var/list/holidays + +/datum/controller/subsystem/holiday/Initialize(start_timeofday) + if(!config.allow_holidays) + return ..() //Holiday stuff was not enabled in the config! + + var/YY = text2num(time2text(world.timeofday, "YY")) // get the current year + var/MM = text2num(time2text(world.timeofday, "MM")) // get the current month + var/DD = text2num(time2text(world.timeofday, "DD")) // get the current day + + for(var/H in subtypesof(/datum/holiday)) + var/datum/holiday/holiday = new H() + if(holiday.shouldCelebrate(DD, MM, YY)) + holiday.celebrate() + if(!holidays) + holidays = list() + holidays[holiday.name] = holiday + + if(holidays) + holidays = shuffle(holidays) + world.update_status() + for(var/datum/holiday/H in holidays) + if(H.eventChance) + if(prob(H.eventChance)) + H.handle_event() + + return ..() diff --git a/code/controllers/subsystem/idlenpcpool.dm b/code/controllers/subsystem/idlenpcpool.dm index 2dad0abca0f..42df941e10f 100644 --- a/code/controllers/subsystem/idlenpcpool.dm +++ b/code/controllers/subsystem/idlenpcpool.dm @@ -29,6 +29,7 @@ SUBSYSTEM_DEF(idlenpcpool) var/mob/living/simple_animal/SA = currentrun[currentrun.len] --currentrun.len if(!SA) + log_debug("idlenpcpool encountered an invalid entry, resumed: [resumed], SA [SA], type of SA [SA?.type], null [SA == null], qdelled [QDELETED(SA)], SA in AI_IDLE list: [SA in GLOB.simple_animals[AI_IDLE]]") GLOB.simple_animals[AI_IDLE] -= SA continue @@ -38,4 +39,4 @@ SUBSYSTEM_DEF(idlenpcpool) if(SA.stat != DEAD) SA.consider_wakeup() if(MC_TICK_CHECK) - return \ No newline at end of file + return diff --git a/code/controllers/subsystem/jobs.dm b/code/controllers/subsystem/jobs.dm index 504a2d74a2c..29efa85b8a0 100644 --- a/code/controllers/subsystem/jobs.dm +++ b/code/controllers/subsystem/jobs.dm @@ -389,8 +389,11 @@ SUBSYSTEM_DEF(jobs) // Antags, who have to get in, come first for(var/mob/new_player/player in unassigned) if(player.mind.special_role) - GiveRandomJob(player) - if(player in unassigned) + if(player.client.prefs.alternate_option != BE_ASSISTANT) + GiveRandomJob(player) + if(player in unassigned) + AssignRole(player, "Civilian") + else AssignRole(player, "Civilian") // Then we assign what we can to everyone else. @@ -439,7 +442,7 @@ SUBSYSTEM_DEF(jobs) if(job.is_security) to_chat(H, "As a member of Security, you are to know Space Law, Legal Standard Operating Procedure, as well as your Department SOP") if(job.req_admin_notify) - to_chat(H, "You are playing a job that is important for the game progression. If you have to disconnect, please notify the admins via adminhelp.") + to_chat(H, "You are playing a job that is important for the game progression. If you have to disconnect, please go to cryo and inform command. If you are unable to do so, please notify the admins via adminhelp.") return H /datum/controller/subsystem/jobs/proc/EquipRank(mob/living/carbon/human/H, rank, joined_late = 0) // Equip and put them in an area @@ -690,4 +693,4 @@ SUBSYSTEM_DEF(jobs) .++ new_id_change_records["[id_change_counter]"] = thisrecord id_change_counter++ - id_change_records = new_id_change_records \ No newline at end of file + id_change_records = new_id_change_records diff --git a/code/controllers/subsystem/lighting.dm b/code/controllers/subsystem/lighting.dm index 82bbd52471a..7441d855f22 100644 --- a/code/controllers/subsystem/lighting.dm +++ b/code/controllers/subsystem/lighting.dm @@ -84,4 +84,4 @@ SUBSYSTEM_DEF(lighting) /datum/controller/subsystem/lighting/Recover() initialized = SSlighting.initialized - ..() \ No newline at end of file + ..() diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm index 75fd7519357..af741423526 100644 --- a/code/controllers/subsystem/mapping.dm +++ b/code/controllers/subsystem/mapping.dm @@ -33,4 +33,4 @@ SUBSYSTEM_DEF(mapping) return ..() /datum/controller/subsystem/mapping/Recover() - flags |= SS_NO_INIT \ No newline at end of file + flags |= SS_NO_INIT diff --git a/code/controllers/subsystem/medals.dm b/code/controllers/subsystem/medals.dm index f3c2752ddf2..5de6cdeb27a 100644 --- a/code/controllers/subsystem/medals.dm +++ b/code/controllers/subsystem/medals.dm @@ -83,4 +83,4 @@ SUBSYSTEM_DEF(medals) /datum/controller/subsystem/medals/proc/ClearScore(client/player) if(isnull(world.SetScores(player.ckey, "", config.medal_hub_address, config.medal_hub_password))) log_game("MEDAL ERROR: Could not contact hub to clear scores for [player.ckey].") - message_admins("Error! Failed to contact hub to clear scores for [player.ckey]!") \ No newline at end of file + message_admins("Error! Failed to contact hub to clear scores for [player.ckey]!") diff --git a/code/controllers/subsystem/mobs.dm b/code/controllers/subsystem/mobs.dm index a8a1fb72ceb..c21b8f0760a 100644 --- a/code/controllers/subsystem/mobs.dm +++ b/code/controllers/subsystem/mobs.dm @@ -33,4 +33,4 @@ SUBSYSTEM_DEF(mobs) else GLOB.mob_list.Remove(M) if(MC_TICK_CHECK) - return \ No newline at end of file + return diff --git a/code/controllers/subsystem/npcpool.dm b/code/controllers/subsystem/npcpool.dm index 0de306b12eb..5eaca5d19c4 100644 --- a/code/controllers/subsystem/npcpool.dm +++ b/code/controllers/subsystem/npcpool.dm @@ -21,6 +21,10 @@ SUBSYSTEM_DEF(npcpool) while(currentrun.len) var/mob/living/simple_animal/SA = currentrun[currentrun.len] --currentrun.len + if(!SA) + log_debug("npcpool encountered an invalid entry, resumed: [resumed], SA [SA], type of SA [SA?.type], null [SA == null], qdelled [QDELETED(SA)], SA in AI_ON list: [SA in GLOB.simple_animals[AI_ON]]") + GLOB.simple_animals[AI_ON] -= SA + continue if(!SA.ckey && !SA.notransform) if(SA.stat != DEAD) @@ -30,4 +34,4 @@ SUBSYSTEM_DEF(npcpool) if(SA.stat != DEAD) SA.handle_automated_speech() if(MC_TICK_CHECK) - return \ No newline at end of file + return diff --git a/code/controllers/subsystem/processing/fastprocess.dm b/code/controllers/subsystem/processing/fastprocess.dm index 732c5a3ba55..9622e021469 100644 --- a/code/controllers/subsystem/processing/fastprocess.dm +++ b/code/controllers/subsystem/processing/fastprocess.dm @@ -3,4 +3,4 @@ PROCESSING_SUBSYSTEM_DEF(fastprocess) name = "Fast Processing" wait = 2 - stat_tag = "FP" \ No newline at end of file + stat_tag = "FP" diff --git a/code/controllers/subsystem/processing/obj.dm b/code/controllers/subsystem/processing/obj.dm index 7ee2bb1f0f3..26021fb267a 100644 --- a/code/controllers/subsystem/processing/obj.dm +++ b/code/controllers/subsystem/processing/obj.dm @@ -2,4 +2,4 @@ PROCESSING_SUBSYSTEM_DEF(obj) name = "Objects" priority = FIRE_PRIORITY_OBJ flags = SS_NO_INIT - wait = 20 \ No newline at end of file + wait = 20 diff --git a/code/controllers/subsystem/processing/processing.dm b/code/controllers/subsystem/processing/processing.dm index 2e06d7ebcc6..a8bc823bbbe 100644 --- a/code/controllers/subsystem/processing/processing.dm +++ b/code/controllers/subsystem/processing/processing.dm @@ -34,4 +34,4 @@ SUBSYSTEM_DEF(processing) /datum/proc/process() set waitfor = 0 - return PROCESS_KILL \ No newline at end of file + return PROCESS_KILL diff --git a/code/controllers/subsystem/radio.dm b/code/controllers/subsystem/radio.dm index 93b5152ee45..49b30062786 100644 --- a/code/controllers/subsystem/radio.dm +++ b/code/controllers/subsystem/radio.dm @@ -1,99 +1,99 @@ -SUBSYSTEM_DEF(radio) - name = "Radio" - flags = SS_NO_INIT | SS_NO_FIRE - - var/list/radiochannels = list( - "Common" = PUB_FREQ, - "Science" = SCI_FREQ, - "Command" = COMM_FREQ, - "Medical" = MED_FREQ, - "Engineering" = ENG_FREQ, - "Security" = SEC_FREQ, - "Response Team" = ERT_FREQ, - "Special Ops" = DTH_FREQ, - "Syndicate" = SYND_FREQ, - "SyndTeam" = SYNDTEAM_FREQ, - "Supply" = SUP_FREQ, - "Service" = SRV_FREQ, - "AI Private" = AI_FREQ, - "Medical(I)" = MED_I_FREQ, - "Security(I)" = SEC_I_FREQ - ) - var/list/CENT_FREQS = list(ERT_FREQ, DTH_FREQ) - var/list/ANTAG_FREQS = list(SYND_FREQ, SYNDTEAM_FREQ) - var/list/DEPT_FREQS = list(AI_FREQ, COMM_FREQ, ENG_FREQ, MED_FREQ, SEC_FREQ, SCI_FREQ, SRV_FREQ, SUP_FREQ) - var/list/datum/radio_frequency/frequencies = list() - -// This is fucking disgusting and needs to die -/datum/controller/subsystem/radio/proc/frequency_span_class(var/frequency) - // Antags! - if(frequency in ANTAG_FREQS) - return "syndradio" - // centcomm channels (deathsquid and ert) - if(frequency in CENT_FREQS) - return "centradio" - // This switch used to be a shit tonne of if statements. I am gonna find who made this and give them a kind talking to - switch(frequency) - if(COMM_FREQ) - return "comradio" - if(AI_FREQ) - return "airadio" - if(SEC_FREQ) - return "secradio" - if(ENG_FREQ) - return "engradio" - if(SCI_FREQ) - return "sciradio" - if(MED_FREQ) - return "medradio" - if(SUP_FREQ) - return "supradio" - if(SRV_FREQ) - return "srvradio" - - // If the above switch somehow failed. And it needs the SSradio. part otherwise it fails to compile - if(frequency in DEPT_FREQS) - return "deptradio" - - // If its none of the others - return "radio" - - -/datum/controller/subsystem/radio/proc/add_object(obj/device as obj, var/new_frequency as num, var/filter = null as text|null) - var/f_text = num2text(new_frequency) - var/datum/radio_frequency/frequency = frequencies[f_text] - - if(!frequency) - frequency = new - frequency.frequency = new_frequency - frequencies[f_text] = frequency - - frequency.add_listener(device, filter) - return frequency - -/datum/controller/subsystem/radio/proc/remove_object(obj/device, old_frequency) - var/f_text = num2text(old_frequency) - var/datum/radio_frequency/frequency = frequencies[f_text] - - if(frequency) - frequency.remove_listener(device) - - if(frequency.devices.len == 0) - qdel(frequency) - frequencies -= f_text - - return 1 - -/datum/controller/subsystem/radio/proc/return_frequency(var/new_frequency as num) - var/f_text = num2text(new_frequency) - var/datum/radio_frequency/frequency = frequencies[f_text] - - if(!frequency) - frequency = new - frequency.frequency = new_frequency - frequencies[f_text] = frequency - - return frequency - - -// ALL THE SHIT BELOW THIS LINE ISNT PART OF THE SUBSYSTEM AND REALLY NEEDS ITS OWN FILE +SUBSYSTEM_DEF(radio) + name = "Radio" + flags = SS_NO_INIT | SS_NO_FIRE + + var/list/radiochannels = list( + "Common" = PUB_FREQ, + "Science" = SCI_FREQ, + "Command" = COMM_FREQ, + "Medical" = MED_FREQ, + "Engineering" = ENG_FREQ, + "Security" = SEC_FREQ, + "Response Team" = ERT_FREQ, + "Special Ops" = DTH_FREQ, + "Syndicate" = SYND_FREQ, + "SyndTeam" = SYNDTEAM_FREQ, + "Supply" = SUP_FREQ, + "Service" = SRV_FREQ, + "AI Private" = AI_FREQ, + "Medical(I)" = MED_I_FREQ, + "Security(I)" = SEC_I_FREQ + ) + var/list/CENT_FREQS = list(ERT_FREQ, DTH_FREQ) + var/list/ANTAG_FREQS = list(SYND_FREQ, SYNDTEAM_FREQ) + var/list/DEPT_FREQS = list(AI_FREQ, COMM_FREQ, ENG_FREQ, MED_FREQ, SEC_FREQ, SCI_FREQ, SRV_FREQ, SUP_FREQ) + var/list/datum/radio_frequency/frequencies = list() + +// This is fucking disgusting and needs to die +/datum/controller/subsystem/radio/proc/frequency_span_class(var/frequency) + // Antags! + if(frequency in ANTAG_FREQS) + return "syndradio" + // centcomm channels (deathsquid and ert) + if(frequency in CENT_FREQS) + return "centradio" + // This switch used to be a shit tonne of if statements. I am gonna find who made this and give them a kind talking to + switch(frequency) + if(COMM_FREQ) + return "comradio" + if(AI_FREQ) + return "airadio" + if(SEC_FREQ) + return "secradio" + if(ENG_FREQ) + return "engradio" + if(SCI_FREQ) + return "sciradio" + if(MED_FREQ) + return "medradio" + if(SUP_FREQ) + return "supradio" + if(SRV_FREQ) + return "srvradio" + + // If the above switch somehow failed. And it needs the SSradio. part otherwise it fails to compile + if(frequency in DEPT_FREQS) + return "deptradio" + + // If its none of the others + return "radio" + + +/datum/controller/subsystem/radio/proc/add_object(obj/device as obj, var/new_frequency as num, var/filter = null as text|null) + var/f_text = num2text(new_frequency) + var/datum/radio_frequency/frequency = frequencies[f_text] + + if(!frequency) + frequency = new + frequency.frequency = new_frequency + frequencies[f_text] = frequency + + frequency.add_listener(device, filter) + return frequency + +/datum/controller/subsystem/radio/proc/remove_object(obj/device, old_frequency) + var/f_text = num2text(old_frequency) + var/datum/radio_frequency/frequency = frequencies[f_text] + + if(frequency) + frequency.remove_listener(device) + + if(frequency.devices.len == 0) + qdel(frequency) + frequencies -= f_text + + return 1 + +/datum/controller/subsystem/radio/proc/return_frequency(var/new_frequency as num) + var/f_text = num2text(new_frequency) + var/datum/radio_frequency/frequency = frequencies[f_text] + + if(!frequency) + frequency = new + frequency.frequency = new_frequency + frequencies[f_text] = frequency + + return frequency + + +// ALL THE SHIT BELOW THIS LINE ISNT PART OF THE SUBSYSTEM AND REALLY NEEDS ITS OWN FILE diff --git a/code/controllers/subsystem/shuttles.dm b/code/controllers/subsystem/shuttles.dm index a1ca9b5fdad..da3d91c1de1 100644 --- a/code/controllers/subsystem/shuttles.dm +++ b/code/controllers/subsystem/shuttles.dm @@ -311,4 +311,4 @@ SUBSYSTEM_DEF(shuttle) QDEL_LIST(remove_images) -#undef CALL_SHUTTLE_REASON_LENGTH \ No newline at end of file +#undef CALL_SHUTTLE_REASON_LENGTH diff --git a/code/controllers/subsystem/sun.dm b/code/controllers/subsystem/sun.dm index 8e5e47e7abf..6cfb52f2d25 100644 --- a/code/controllers/subsystem/sun.dm +++ b/code/controllers/subsystem/sun.dm @@ -39,4 +39,4 @@ SUBSYSTEM_DEF(sun) if(!SC.powernet) solars.Remove(SC) continue - SC.update() \ No newline at end of file + SC.update() diff --git a/code/controllers/subsystem/timer.dm b/code/controllers/subsystem/timer.dm index 1b2938fc70f..1caa3d7819a 100644 --- a/code/controllers/subsystem/timer.dm +++ b/code/controllers/subsystem/timer.dm @@ -518,4 +518,4 @@ SUBSYSTEM_DEF(timer) #undef BUCKET_LEN #undef BUCKET_POS #undef TIMER_MAX -#undef TIMER_ID_MAX \ No newline at end of file +#undef TIMER_ID_MAX diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm index 27d499254a1..4cb49e8a3f1 100644 --- a/code/controllers/subsystem/vote.dm +++ b/code/controllers/subsystem/vote.dm @@ -1,401 +1,401 @@ -SUBSYSTEM_DEF(vote) - name = "Vote" - wait = 10 - flags = SS_KEEP_TIMING|SS_NO_INIT - runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT - - var/initiator = null - var/started_time = null - var/time_remaining = 0 - var/mode = null - var/question = null - var/list/choices = list() - var/list/voted = list() - var/list/voting = list() - var/list/current_votes = list() - var/list/round_voters = list() - var/auto_muted = 0 - -/datum/controller/subsystem/vote/fire() - if(mode) - // No more change mode votes after the game has started. - if(mode == "gamemode" && SSticker.current_state >= GAME_STATE_SETTING_UP) - to_chat(world, "Voting aborted due to game start.") - reset() - return - - // Calculate how much time is remaining by comparing current time, to time of vote start, - // plus vote duration - time_remaining = round((started_time + config.vote_period - world.time)/10) - - if(time_remaining < 0) - result() - for(var/client/C in voting) - if(C) - C << browse(null,"window=vote") - reset() - else - for(var/client/C in voting) - update_panel(C) - CHECK_TICK - -/datum/controller/subsystem/vote/proc/autotransfer() - initiate_vote("crew_transfer","the server") - -/datum/controller/subsystem/vote/proc/reset() - initiator = null - time_remaining = 0 - mode = null - question = null - choices.Cut() - voted.Cut() - voting.Cut() - current_votes.Cut() - - if(auto_muted && !config.ooc_allowed) - auto_muted = 0 - config.ooc_allowed = !( config.ooc_allowed ) - to_chat(world, "The OOC channel has been automatically enabled due to vote end.") - log_admin("OOC was toggled automatically due to vote end.") - message_admins("OOC has been toggled on automatically.") - - -/datum/controller/subsystem/vote/proc/get_result() - var/greatest_votes = 0 - var/total_votes = 0 - var/list/sorted_choices = list() - var/sorted_highest - var/sorted_votes = -1 - //get the highest number of votes, while also sorting the list - while(choices.len) - // This is a very inefficient sorting method, but that's okay - for(var/option in choices) - var/votes = choices[option] - if(sorted_votes < votes) - sorted_highest = option - sorted_votes = votes - if(votes > greatest_votes) - greatest_votes = votes - sorted_votes = -1 - total_votes += choices[sorted_highest] - sorted_choices[sorted_highest] = choices[sorted_highest] || 0 - choices -= sorted_highest - choices = sorted_choices - //default-vote for everyone who didn't vote - if(!config.vote_no_default && choices.len) - var/non_voters = (GLOB.clients.len - total_votes) - if(non_voters > 0) - if(mode == "restart") - choices["Continue Playing"] += non_voters - if(choices["Continue Playing"] >= greatest_votes) - greatest_votes = choices["Continue Playing"] - else if(mode == "gamemode") - if(master_mode in choices) - choices[master_mode] += non_voters - if(choices[master_mode] >= greatest_votes) - greatest_votes = choices[master_mode] - else if(mode == "crew_transfer") - var/factor = 0.5 - switch(world.time / (10 * 60)) // minutes - if(0 to 60) - factor = 0.5 - if(61 to 120) - factor = 0.8 - if(121 to 240) - factor = 1 - if(241 to 300) - factor = 1.2 - else - factor = 1.4 - choices["Initiate Crew Transfer"] = round(choices["Initiate Crew Transfer"] * factor) - to_chat(world, "Crew Transfer Factor: [factor]") - greatest_votes = max(choices["Initiate Crew Transfer"], choices["Continue The Round"]) - - - //get all options with that many votes and return them in a list - . = list() - if(greatest_votes) - for(var/option in choices) - if(choices[option] == greatest_votes) - . += option - return . - -/datum/controller/subsystem/vote/proc/announce_result() - var/list/winners = get_result() - var/text - if(winners.len > 0) - if(winners.len > 1) - if(mode != "gamemode" || SSticker.hide_mode == 0) // Here we are making sure we don't announce potential game modes - text = "Vote Tied Between:\n" - for(var/option in winners) - text += "\t[option]\n" - . = pick(winners) - - for(var/key in current_votes) - if(choices[current_votes[key]] == .) - round_voters += key // Keep track of who voted for the winning round. - if(mode == "gamemode" && (. == "extended" || SSticker.hide_mode == 0)) // Announce Extended gamemode, but not other gamemodes - text += "Vote Result: [.] ([choices[.]] vote\s)" - else - if(mode == "custom") - // Completely replace text to show all results in custom votes - text = "[question]\n" - for(var/option in winners) - text += "\t[option]: [choices[option]] vote\s\n" - for(var/option in (choices-winners)) - text += "\t[option]: [choices[option]] vote\s\n" - else if(mode != "gamemode") - text += "Vote Result: [.] ([choices[.]] vote\s)" - else - text += "The vote has ended." // What will be shown if it is a gamemode vote that isn't extended - - else - text += "Vote Result: Inconclusive - No Votes!" - log_vote(text) - to_chat(world, "[text]") - return . - -/datum/controller/subsystem/vote/proc/result() - . = announce_result() - var/restart = 0 - if(.) - switch(mode) - if("restart") - if(. == "Restart Round") - restart = 1 - if("gamemode") - if(master_mode != .) - world.save_mode(.) - if(SSticker && SSticker.mode) - restart = 1 - else - master_mode = . - if(!going) - going = 1 - to_chat(world, "The round will start soon.") - if("crew_transfer") - if(. == "Initiate Crew Transfer") - init_shift_change(null, 1) - - - if(restart) - world.Reboot("Restart vote successful.", "end_error", "restart vote") - - return . - -/datum/controller/subsystem/vote/proc/submit_vote(var/ckey, var/vote) - if(mode) - if(config.vote_no_dead && usr.stat == DEAD && !usr.client.holder) - return 0 - if(current_votes[ckey]) - choices[choices[current_votes[ckey]]]-- - if(vote && 1<=vote && vote<=choices.len) - voted += usr.ckey - choices[choices[vote]]++ //check this - current_votes[ckey] = vote - return vote - return 0 - -/datum/controller/subsystem/vote/proc/initiate_vote(var/vote_type, var/initiator_key) - if(!mode) - if(started_time != null && !check_rights(R_ADMIN)) - var/next_allowed_time = (started_time + config.vote_delay) - if(next_allowed_time > world.time) - return 0 - - reset() - switch(vote_type) - if("restart") - choices.Add("Restart Round","Continue Playing") - if("gamemode") - if(SSticker.current_state >= 2) - return 0 - choices.Add(config.votable_modes) - if("crew_transfer") - if(check_rights(R_ADMIN|R_MOD)) - if(SSticker.current_state <= 2) - return 0 - question = "End the shift?" - choices.Add("Initiate Crew Transfer", "Continue The Round") - else - if(SSticker.current_state <= 2) - return 0 - question = "End the shift?" - choices.Add("Initiate Crew Transfer", "Continue The Round") - if("custom") - question = html_encode(input(usr,"What is the vote for?") as text|null) - if(!question) return 0 - for(var/i=1,i<=10,i++) - var/option = capitalize(html_encode(input(usr,"Please enter an option or hit cancel to finish") as text|null)) - if(!option || mode || !usr.client) break - choices.Add(option) - else - return 0 - mode = vote_type - initiator = initiator_key - started_time = world.time - var/text = "[capitalize(mode)] vote started by [initiator]." - if(mode == "custom") - text += "\n[question]" - if(usr) - log_admin("[capitalize(mode)] ([question]) vote started by [key_name(usr)].") - else if(usr) - log_admin("[capitalize(mode)] vote started by [key_name(usr)].") - - log_vote(text) - to_chat(world, {"[text] - Click here or type vote to place your vote. - You have [config.vote_period/10] seconds to vote."}) - switch(vote_type) - if("crew_transfer") - world << sound('sound/ambience/alarm4.ogg') - if("gamemode") - world << sound('sound/ambience/alarm4.ogg') - if("custom") - world << sound('sound/ambience/alarm4.ogg') - if(mode == "gamemode" && going) - going = 0 - to_chat(world, "Round start has been delayed.") - if(mode == "crew_transfer" && config.ooc_allowed) - auto_muted = 1 - config.ooc_allowed = !( config.ooc_allowed ) - to_chat(world, "The OOC channel has been automatically disabled due to a crew transfer vote.") - log_admin("OOC was toggled automatically due to crew_transfer vote.") - message_admins("OOC has been toggled off automatically.") - if(mode == "gamemode" && config.ooc_allowed) - auto_muted = 1 - config.ooc_allowed = !( config.ooc_allowed ) - to_chat(world, "The OOC channel has been automatically disabled due to the gamemode vote.") - log_admin("OOC was toggled automatically due to gamemode vote.") - message_admins("OOC has been toggled off automatically.") - if(mode == "custom" && config.ooc_allowed) - auto_muted = 1 - config.ooc_allowed = !( config.ooc_allowed ) - to_chat(world, "The OOC channel has been automatically disabled due to a custom vote.") - log_admin("OOC was toggled automatically due to custom vote.") - message_admins("OOC has been toggled off automatically.") - - time_remaining = round(config.vote_period/10) - return 1 - return 0 - -/datum/controller/subsystem/vote/proc/browse_to(var/client/C) - if(!C) - return - var/admin = check_rights(R_ADMIN, 0, user = C.mob) - voting |= C - - var/dat = {""} - if(mode) - dat += "
    [vote_html(C)]

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

    Start a vote:



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

    Vote: '[question]'

    " - else - . += "

    Vote: [capitalize(mode)]

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

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

    Start a vote:



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

    Vote: '[question]'

    " + else + . += "

    Vote: [capitalize(mode)]

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

    " - out += "Announce objectives

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

    " + out += "Announce objectives

    " + usr << browse(out, "window=edit_memory[src];size=500x500") + +/datum/mind/Topic(href, href_list) + if(!check_rights(R_ADMIN)) + return + + if(href_list["role_edit"]) + var/new_role = input("Select new role", "Assigned role", assigned_role) as null|anything in GLOB.joblist + if(!new_role) + return + assigned_role = new_role + log_admin("[key_name(usr)] has changed [key_name(current)]'s assigned role to [assigned_role]") + message_admins("[key_name_admin(usr)] has changed [key_name_admin(current)]'s assigned role to [assigned_role]") + + else if(href_list["memory_edit"]) + var/messageinput = input("Write new memory", "Memory", memory) as null|message + if(isnull(messageinput)) + return + var/new_memo = copytext(messageinput, 1,MAX_MESSAGE_LEN) + var/confirmed = alert(usr, "Are you sure you want to edit their memory? It will wipe out their original memory!", "Edit Memory", "Yes", "No") + if(confirmed == "Yes") // Because it is too easy to accidentally wipe someone's memory + memory = new_memo + log_admin("[key_name(usr)] has edited [key_name(current)]'s memory") + message_admins("[key_name_admin(usr)] has edited [key_name_admin(current)]'s memory") + + else if(href_list["obj_edit"] || href_list["obj_add"]) + var/datum/objective/objective + var/objective_pos + var/def_value + + if(href_list["obj_edit"]) + objective = locate(href_list["obj_edit"]) + if(!objective) + return + objective_pos = objectives.Find(objective) + + //Text strings are easy to manipulate. Revised for simplicity. + var/temp_obj_type = "[objective.type]"//Convert path into a text string. + def_value = copytext(temp_obj_type, 19)//Convert last part of path into an objective keyword. + if(!def_value)//If it's a custom objective, it will be an empty string. + def_value = "custom" + + var/new_obj_type = input("Select objective type:", "Objective type", def_value) as null|anything in list("assassinate", "blood", "debrain", "protect", "prevent", "brig", "hijack", "escape", "survive", "steal", "download", "nuclear", "capture", "absorb", "destroy", "maroon", "identity theft", "custom") + if(!new_obj_type) + return + + var/datum/objective/new_objective = null + + switch(new_obj_type) + if("assassinate","protect","debrain", "brig", "maroon") + //To determine what to name the objective in explanation text. + var/objective_type_capital = uppertext(copytext(new_obj_type, 1,2))//Capitalize first letter. + var/objective_type_text = copytext(new_obj_type, 2)//Leave the rest of the text. + var/objective_type = "[objective_type_capital][objective_type_text]"//Add them together into a text string. + + var/list/possible_targets = list() + for(var/datum/mind/possible_target in SSticker.minds) + if((possible_target != src) && istype(possible_target.current, /mob/living/carbon/human)) + possible_targets += possible_target.current + + var/mob/def_target = null + var/objective_list[] = list(/datum/objective/assassinate, /datum/objective/protect, /datum/objective/debrain) + if(objective&&(objective.type in objective_list) && objective:target) + def_target = objective:target.current + possible_targets = sortAtom(possible_targets) + possible_targets += "Free objective" + + var/new_target = input("Select target:", "Objective target", def_target) as null|anything in possible_targets + if(!new_target) + return + + var/objective_path = text2path("/datum/objective/[new_obj_type]") + if(new_target == "Free objective") + new_objective = new objective_path + new_objective.owner = src + new_objective:target = null + new_objective.explanation_text = "Free objective" + else + new_objective = new objective_path + new_objective.owner = src + new_objective:target = new_target:mind + //Will display as special role if assigned mode is equal to special role.. Ninjas/commandos/nuke ops. + new_objective.explanation_text = "[objective_type] [new_target:real_name], the [new_target:mind:assigned_role == new_target:mind:special_role ? (new_target:mind:special_role) : (new_target:mind:assigned_role)]." + + if("destroy") + var/list/possible_targets = active_ais(1) + if(possible_targets.len) + var/mob/new_target = input("Select target:", "Objective target") as null|anything in possible_targets + new_objective = new /datum/objective/destroy + new_objective.target = new_target.mind + new_objective.owner = src + new_objective.explanation_text = "Destroy [new_target.name], the experimental AI." + else + to_chat(usr, "No active AIs with minds") + + if("prevent") + new_objective = new /datum/objective/block + new_objective.owner = src + + if("hijack") + new_objective = new /datum/objective/hijack + new_objective.owner = src + + if("escape") + new_objective = new /datum/objective/escape + new_objective.owner = src + + if("survive") + new_objective = new /datum/objective/survive + new_objective.owner = src + + if("die") + new_objective = new /datum/objective/die + new_objective.owner = src + + if("nuclear") + new_objective = new /datum/objective/nuclear + new_objective.owner = src + + if("steal") + if(!istype(objective, /datum/objective/steal)) + new_objective = new /datum/objective/steal + new_objective.owner = src + else + new_objective = objective + var/datum/objective/steal/steal = new_objective + if(!steal.select_target()) + return + + if("download","capture","absorb", "blood") + var/def_num + if(objective&&objective.type==text2path("/datum/objective/[new_obj_type]")) + def_num = objective.target_amount + + var/target_number = input("Input target number:", "Objective", def_num) as num|null + if(isnull(target_number))//Ordinarily, you wouldn't need isnull. In this case, the value may already exist. + return + + switch(new_obj_type) + if("download") + new_objective = new /datum/objective/download + new_objective.explanation_text = "Download [target_number] research levels." + if("capture") + new_objective = new /datum/objective/capture + new_objective.explanation_text = "Accumulate [target_number] capture points." + if("absorb") + new_objective = new /datum/objective/absorb + new_objective.explanation_text = "Absorb [target_number] compatible genomes." + if("blood") + new_objective = new /datum/objective/blood + new_objective.explanation_text = "Accumulate at least [target_number] total units of blood." + new_objective.owner = src + new_objective.target_amount = target_number + + if("identity theft") + var/list/possible_targets = list() + for(var/datum/mind/possible_target in SSticker.minds) + if((possible_target != src) && ishuman(possible_target.current)) + possible_targets += possible_target + possible_targets = sortAtom(possible_targets) + possible_targets += "Free objective" + var/new_target = input("Select target:", "Objective target") as null|anything in possible_targets + if(!new_target) + return + var/datum/mind/targ = new_target + if(!istype(targ)) + log_runtime(EXCEPTION("Invalid target for identity theft objective, cancelling"), src) + return + new_objective = new /datum/objective/escape/escape_with_identity + new_objective.owner = src + new_objective.target = new_target + new_objective.explanation_text = "Escape on the shuttle or an escape pod with the identity of [targ.current.real_name], the [targ.assigned_role] while wearing [targ.current.p_their()] identification card." + if("custom") + var/expl = sanitize(copytext(input("Custom objective:", "Objective", objective ? objective.explanation_text : "") as text|null,1,MAX_MESSAGE_LEN)) + if(!expl) + return + new_objective = new /datum/objective + new_objective.owner = src + new_objective.explanation_text = expl + + if(!new_objective) + return + + if(objective) + objectives -= objective + qdel(objective) + objectives.Insert(objective_pos, new_objective) + else + objectives += new_objective + + log_admin("[key_name(usr)] has updated [key_name(current)]'s objectives: [new_objective]") + message_admins("[key_name_admin(usr)] has updated [key_name_admin(current)]'s objectives: [new_objective]") + + else if(href_list["obj_delete"]) + var/datum/objective/objective = locate(href_list["obj_delete"]) + if(!istype(objective)) + return + objectives -= objective + + log_admin("[key_name(usr)] has removed one of [key_name(current)]'s objectives: [objective]") + message_admins("[key_name_admin(usr)] has removed one of [key_name_admin(current)]'s objectives: [objective]") + qdel(objective) + + else if(href_list["obj_completed"]) + var/datum/objective/objective = locate(href_list["obj_completed"]) + if(!istype(objective)) + return + objective.completed = !objective.completed + + log_admin("[key_name(usr)] has toggled the completion of one of [key_name(current)]'s objectives") + message_admins("[key_name_admin(usr)] has toggled the completion of one of [key_name_admin(current)]'s objectives") + + else if(href_list["implant"]) + var/mob/living/carbon/human/H = current + + switch(href_list["implant"]) + if("remove") + for(var/obj/item/implant/mindshield/I in H.contents) + if(I && I.implanted) + qdel(I) + to_chat(H, "Your mindshield implant has been deactivated.") + log_admin("[key_name(usr)] has deactivated [key_name(current)]'s mindshield implant") + message_admins("[key_name_admin(usr)] has deactivated [key_name_admin(current)]'s mindshield implant") + if("add") + var/obj/item/implant/mindshield/L = new/obj/item/implant/mindshield(H) + L.implant(H) + + log_admin("[key_name(usr)] has given [key_name(current)] a mindshield implant") + message_admins("[key_name_admin(usr)] has given [key_name_admin(current)] a mindshield implant") + + to_chat(H, "You somehow have become the recepient of a mindshield transplant, and it just activated!") + if(src in SSticker.mode.revolutionaries) + special_role = null + SSticker.mode.revolutionaries -= src + to_chat(src, "The nanobots in the mindshield implant remove all thoughts about being a revolutionary. Get back to work!") + if(src in SSticker.mode.head_revolutionaries) + special_role = null + SSticker.mode.head_revolutionaries -=src + to_chat(src, "The nanobots in the mindshield implant remove all thoughts about being a revolutionary. Get back to work!") + if(src in SSticker.mode.cult) + SSticker.mode.cult -= src + SSticker.mode.update_cult_icons_removed(src) + special_role = null + var/datum/game_mode/cult/cult = SSticker.mode + if(istype(cult)) + cult.memorize_cult_objectives(src) + to_chat(current, "The nanobots in the mindshield implant remove all thoughts about being in a cult. Have a productive day!") + memory = "" + + else if(href_list["revolution"]) + + switch(href_list["revolution"]) + if("clear") + if(src in SSticker.mode.revolutionaries) + SSticker.mode.revolutionaries -= src + to_chat(current, "You have been brainwashed! You are no longer a revolutionary!") + SSticker.mode.update_rev_icons_removed(src) + special_role = null + if(src in SSticker.mode.head_revolutionaries) + SSticker.mode.head_revolutionaries -= src + to_chat(current, "You have been brainwashed! You are no longer a head revolutionary!") + SSticker.mode.update_rev_icons_removed(src) + special_role = null + log_admin("[key_name(usr)] has de-rev'd [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-rev'd [key_name_admin(current)]") + + if("rev") + if(src in SSticker.mode.head_revolutionaries) + SSticker.mode.head_revolutionaries -= src + SSticker.mode.update_rev_icons_removed(src) + to_chat(current, "Revolution has been disappointed of your leadership traits! You are a regular revolutionary now!") + else if(!(src in SSticker.mode.revolutionaries)) + to_chat(current, " You are now a revolutionary! Help your cause. Do not harm your fellow freedom fighters. You can identify your comrades by the red \"R\" icons, and your leaders by the blue \"R\" icons. Help them kill the heads to win the revolution!") + else + return + SSticker.mode.revolutionaries += src + SSticker.mode.update_rev_icons_added(src) + special_role = SPECIAL_ROLE_REV + log_admin("[key_name(usr)] has rev'd [key_name(current)]") + message_admins("[key_name_admin(usr)] has rev'd [key_name_admin(current)]") + + if("headrev") + if(src in SSticker.mode.revolutionaries) + SSticker.mode.revolutionaries -= src + SSticker.mode.update_rev_icons_removed(src) + to_chat(current, "You have proven your devotion to revolution! You are a head revolutionary now!") + else if(!(src in SSticker.mode.head_revolutionaries)) + to_chat(current, "You are a member of the revolutionaries' leadership now!") + else + return + if(SSticker.mode.head_revolutionaries.len>0) + // copy targets + var/datum/mind/valid_head = locate() in SSticker.mode.head_revolutionaries + if(valid_head) + for(var/datum/objective/mutiny/O in valid_head.objectives) + var/datum/objective/mutiny/rev_obj = new + rev_obj.owner = src + rev_obj.target = O.target + rev_obj.explanation_text = "Assassinate [O.target.name], the [O.target.assigned_role]." + objectives += rev_obj + SSticker.mode.greet_revolutionary(src,0) + SSticker.mode.head_revolutionaries += src + SSticker.mode.update_rev_icons_added(src) + special_role = SPECIAL_ROLE_HEAD_REV + log_admin("[key_name(usr)] has head-rev'd [key_name(current)]") + message_admins("[key_name_admin(usr)] has head-rev'd [key_name_admin(current)]") + + if("autoobjectives") + SSticker.mode.forge_revolutionary_objectives(src) + SSticker.mode.greet_revolutionary(src,0) + log_admin("[key_name(usr)] has automatically forged revolutionary objectives for [key_name(current)]") + message_admins("[key_name_admin(usr)] has automatically forged revolutionary objectives for [key_name_admin(current)]") + + if("flash") + if(!SSticker.mode.equip_revolutionary(current)) + to_chat(usr, "Spawning flash failed!") + log_admin("[key_name(usr)] has given [key_name(current)] a flash") + message_admins("[key_name_admin(usr)] has given [key_name_admin(current)] a flash") + + if("takeflash") + var/list/L = current.get_contents() + var/obj/item/flash/flash = locate() in L + if(!flash) + to_chat(usr, "Deleting flash failed!") + qdel(flash) + log_admin("[key_name(usr)] has taken [key_name(current)]'s flash") + message_admins("[key_name_admin(usr)] has taken [key_name_admin(current)]'s flash") + + if("repairflash") + var/list/L = current.get_contents() + var/obj/item/flash/flash = locate() in L + if(!flash) + to_chat(usr, "Repairing flash failed!") + else + flash.broken = 0 + log_admin("[key_name(usr)] has repaired [key_name(current)]'s flash") + message_admins("[key_name_admin(usr)] has repaired [key_name_admin(current)]'s flash") + + if("reequip") + var/list/L = current.get_contents() + var/obj/item/flash/flash = locate() in L + qdel(flash) + take_uplink() + var/fail = 0 + var/datum/antagonist/traitor/T = has_antag_datum(/datum/antagonist/traitor) + fail |= !T.equip_traitor(src) + fail |= !SSticker.mode.equip_revolutionary(current) + if(fail) + to_chat(usr, "Reequipping revolutionary goes wrong!") + return + log_admin("[key_name(usr)] has equipped [key_name(current)] as a revolutionary") + message_admins("[key_name_admin(usr)] has equipped [key_name_admin(current)] as a revolutionary") + + else if(href_list["cult"]) + switch(href_list["cult"]) + if("clear") + if(src in SSticker.mode.cult) + SSticker.mode.remove_cultist(src) + special_role = null + log_admin("[key_name(usr)] has de-culted [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-culted [key_name_admin(current)]") + if("cultist") + if(!(src in SSticker.mode.cult)) + SSticker.mode.add_cultist(src) + special_role = SPECIAL_ROLE_CULTIST + to_chat(current, "You catch a glimpse of the Realm of [SSticker.cultdat.entity_name], [SSticker.cultdat.entity_title3]. You now see how flimsy the world is, you see that it should be open to the knowledge of [SSticker.cultdat.entity_name].") + to_chat(current, "Assist your new compatriots in their dark dealings. Their goal is yours, and yours is theirs. You serve [SSticker.cultdat.entity_title2] above all else. Bring It back.") + log_admin("[key_name(usr)] has culted [key_name(current)]") + message_admins("[key_name_admin(usr)] has culted [key_name_admin(current)]") + if(!summon_spots.len) + while(summon_spots.len < SUMMON_POSSIBILITIES) + var/area/summon = pick(return_sorted_areas() - summon_spots) + if(summon && is_station_level(summon.z) && summon.valid_territory) + summon_spots += summon + if("tome") + var/mob/living/carbon/human/H = current + if(istype(H)) + var/obj/item/tome/T = new(H) + + var/list/slots = list ( + "backpack" = slot_in_backpack, + "left pocket" = slot_l_store, + "right pocket" = slot_r_store, + "left hand" = slot_l_hand, + "right hand" = slot_r_hand, + ) + var/where = H.equip_in_one_of_slots(T, slots) + if(!where) + to_chat(usr, "Spawning tome failed!") + qdel(T) + else + to_chat(H, "A tome, a message from your new master, appears in your [where].") + log_admin("[key_name(usr)] has spawned a tome for [key_name(current)]") + message_admins("[key_name_admin(usr)] has spawned a tome for [key_name_admin(current)]") + + if("equip") + if(!SSticker.mode.equip_cultist(current)) + to_chat(usr, "Spawning equipment failed!") + log_admin("[key_name(usr)] has equipped [key_name(current)] as a cultist") + message_admins("[key_name_admin(usr)] has equipped [key_name_admin(current)] as a cultist") + + else if(href_list["wizard"]) + + switch(href_list["wizard"]) + if("clear") + if(src in SSticker.mode.wizards) + SSticker.mode.wizards -= src + special_role = null + current.spellremove(current) + current.faction = list("Station") + SSticker.mode.update_wiz_icons_removed(src) + to_chat(current, "You have been brainwashed! You are no longer a wizard!") + log_admin("[key_name(usr)] has de-wizarded [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-wizarded [key_name_admin(current)]") + if("wizard") + if(!(src in SSticker.mode.wizards)) + SSticker.mode.wizards += src + special_role = SPECIAL_ROLE_WIZARD + //ticker.mode.learn_basic_spells(current) + SSticker.mode.update_wiz_icons_added(src) + SEND_SOUND(current, 'sound/ambience/antag/ragesmages.ogg') + to_chat(current, "You are a Space Wizard!") + current.faction = list("wizard") + log_admin("[key_name(usr)] has wizarded [key_name(current)]") + message_admins("[key_name_admin(usr)] has wizarded [key_name_admin(current)]") + if("lair") + current.forceMove(pick(wizardstart)) + log_admin("[key_name(usr)] has moved [key_name(current)] to the wizard's lair") + message_admins("[key_name_admin(usr)] has moved [key_name_admin(current)] to the wizard's lair") + if("dressup") + SSticker.mode.equip_wizard(current) + log_admin("[key_name(usr)] has equipped [key_name(current)] as a wizard") + message_admins("[key_name_admin(usr)] has equipped [key_name_admin(current)] as a wizard") + if("name") + SSticker.mode.name_wizard(current) + log_admin("[key_name(usr)] has allowed wizard [key_name(current)] to name themselves") + message_admins("[key_name_admin(usr)] has allowed wizard [key_name_admin(current)] to name themselves") + if("autoobjectives") + SSticker.mode.forge_wizard_objectives(src) + to_chat(usr, "The objectives for wizard [key] have been generated. You can edit them and announce manually.") + log_admin("[key_name(usr)] has automatically forged wizard objectives for [key_name(current)]") + message_admins("[key_name_admin(usr)] has automatically forged wizard objectives for [key_name_admin(current)]") + + + else if(href_list["changeling"]) + switch(href_list["changeling"]) + if("clear") + if(src in SSticker.mode.changelings) + SSticker.mode.changelings -= src + special_role = null + if(changeling) + current.remove_changeling_powers() + qdel(changeling) + changeling = null + SSticker.mode.update_change_icons_removed(src) + to_chat(current, "You grow weak and lose your powers! You are no longer a changeling and are stuck in your current form!") + log_admin("[key_name(usr)] has de-changelinged [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-changelinged [key_name_admin(current)]") + if("changeling") + if(!(src in SSticker.mode.changelings)) + SSticker.mode.changelings += src + SSticker.mode.grant_changeling_powers(current) + SSticker.mode.update_change_icons_added(src) + special_role = SPECIAL_ROLE_CHANGELING + SEND_SOUND(current, 'sound/ambience/antag/ling_aler.ogg') + to_chat(current, "Your powers have awoken. A flash of memory returns to us... we are a changeling!") + log_admin("[key_name(usr)] has changelinged [key_name(current)]") + message_admins("[key_name_admin(usr)] has changelinged [key_name_admin(current)]") + + if("autoobjectives") + SSticker.mode.forge_changeling_objectives(src) + to_chat(usr, "The objectives for changeling [key] have been generated. You can edit them and announce manually.") + log_admin("[key_name(usr)] has automatically forged objectives for [key_name(current)]") + message_admins("[key_name_admin(usr)] has automatically forged objectives for [key_name_admin(current)]") + + if("initialdna") + if(!changeling || !changeling.absorbed_dna.len) + to_chat(usr, "Resetting DNA failed!") + else + current.dna = changeling.absorbed_dna[1] + current.real_name = current.dna.real_name + current.UpdateAppearance() + domutcheck(current, null) + log_admin("[key_name(usr)] has reset [key_name(current)]'s DNA") + message_admins("[key_name_admin(usr)] has reset [key_name_admin(current)]'s DNA") + + else if(href_list["vampire"]) + switch(href_list["vampire"]) + if("clear") + if(src in SSticker.mode.vampires) + SSticker.mode.vampires -= src + special_role = null + if(vampire) + vampire.remove_vampire_powers() + qdel(vampire) + vampire = null + SSticker.mode.update_vampire_icons_removed(src) + to_chat(current, "You grow weak and lose your powers! You are no longer a vampire and are stuck in your current form!") + log_admin("[key_name(usr)] has de-vampired [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-vampired [key_name_admin(current)]") + if("vampire") + if(!(src in SSticker.mode.vampires)) + SSticker.mode.vampires += src + SSticker.mode.grant_vampire_powers(current) + SSticker.mode.update_vampire_icons_added(src) + var/datum/mindslaves/slaved = new() + slaved.masters += src + som = slaved //we MIGT want to mindslave someone + special_role = SPECIAL_ROLE_VAMPIRE + SEND_SOUND(current, 'sound/ambience/antag/vampalert.ogg') + to_chat(current, "Your powers have awoken. Your lust for blood grows... You are a Vampire!") + log_admin("[key_name(usr)] has vampired [key_name(current)]") + message_admins("[key_name_admin(usr)] has vampired [key_name_admin(current)]") + + if("autoobjectives") + SSticker.mode.forge_vampire_objectives(src) + to_chat(usr, "The objectives for vampire [key] have been generated. You can edit them and announce manually.") + log_admin("[key_name(usr)] has automatically forged objectives for [key_name(current)]") + message_admins("[key_name_admin(usr)] has automatically forged objectives for [key_name_admin(current)]") + + else if(href_list["vampthrall"]) + switch(href_list["vampthrall"]) + if("clear") + if(src in SSticker.mode.vampire_enthralled) + SSticker.mode.remove_vampire_mind(src) + log_admin("[key_name(usr)] has de-vampthralled [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-vampthralled [key_name_admin(current)]") + + else if(href_list["nuclear"]) + var/mob/living/carbon/human/H = current + + switch(href_list["nuclear"]) + if("clear") + if(src in SSticker.mode.syndicates) + SSticker.mode.syndicates -= src + SSticker.mode.update_synd_icons_removed(src) + special_role = null + for(var/datum/objective/nuclear/O in objectives) + objectives-=O + qdel(O) + to_chat(current, "You have been brainwashed! You are no longer a syndicate operative!") + log_admin("[key_name(usr)] has de-nuke op'd [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-nuke op'd [key_name_admin(current)]") + if("nuclear") + if(!(src in SSticker.mode.syndicates)) + SSticker.mode.syndicates += src + SSticker.mode.update_synd_icons_added(src) + if(SSticker.mode.syndicates.len==1) + SSticker.mode.prepare_syndicate_leader(src) + else + current.real_name = "[syndicate_name()] Operative #[SSticker.mode.syndicates.len-1]" + special_role = SPECIAL_ROLE_NUKEOPS + to_chat(current, "You are a [syndicate_name()] agent!") + SSticker.mode.forge_syndicate_objectives(src) + SSticker.mode.greet_syndicate(src) + log_admin("[key_name(usr)] has nuke op'd [key_name(current)]") + message_admins("[key_name_admin(usr)] has nuke op'd [key_name_admin(current)]") + if("lair") + current.forceMove(get_turf(locate("landmark*Syndicate-Spawn"))) + log_admin("[key_name(usr)] has moved [key_name(current)] to the nuclear operative spawn") + message_admins("[key_name_admin(usr)] has moved [key_name_admin(current)] to the nuclear operative spawn") + if("dressup") + qdel(H.belt) + qdel(H.back) + qdel(H.l_ear) + qdel(H.r_ear) + qdel(H.gloves) + qdel(H.head) + qdel(H.shoes) + qdel(H.wear_id) + qdel(H.wear_pda) + qdel(H.wear_suit) + qdel(H.w_uniform) + + if(!SSticker.mode.equip_syndicate(current)) + to_chat(usr, "Equipping a syndicate failed!") + return + SSticker.mode.update_syndicate_id(current.mind, SSticker.mode.syndicates.len == 1) + log_admin("[key_name(usr)] has equipped [key_name(current)] as a nuclear operative") + message_admins("[key_name_admin(usr)] has equipped [key_name_admin(current)] as a nuclear operative") + + if("tellcode") + var/code + for(var/obj/machinery/nuclearbomb/bombue in GLOB.machines) + if(length(bombue.r_code) <= 5 && bombue.r_code != "LOLNO" && bombue.r_code != "ADMIN") + code = bombue.r_code + break + if(code) + store_memory("Syndicate Nuclear Bomb Code: [code]", 0, 0) + to_chat(current, "The nuclear authorization code is: [code]") + log_admin("[key_name(usr)] has given [key_name(current)] the nuclear authorization code") + message_admins("[key_name_admin(usr)] has given [key_name_admin(current)] the nuclear authorization code") + else + to_chat(usr, "No valid nuke found!") + + else if(href_list["eventmisc"]) + switch(href_list["eventmisc"]) + if("clear") + if(src in SSticker.mode.eventmiscs) + SSticker.mode.eventmiscs -= src + SSticker.mode.update_eventmisc_icons_removed(src) + special_role = null + message_admins("[key_name_admin(usr)] has de-eventantag'ed [current].") + log_admin("[key_name(usr)] has de-eventantag'ed [current].") + if("eventmisc") + SSticker.mode.eventmiscs += src + special_role = SPECIAL_ROLE_EVENTMISC + SSticker.mode.update_eventmisc_icons_added(src) + message_admins("[key_name_admin(usr)] has eventantag'ed [current].") + log_admin("[key_name(usr)] has eventantag'ed [current].") + else if(href_list["devil"]) + switch(href_list["devil"]) + if("clear") + if(src in SSticker.mode.devils) + if(istype(current,/mob/living/carbon/true_devil/)) + to_chat(usr,"This cannot be used on true or arch-devils.") + else + SSticker.mode.devils -= src + SSticker.mode.update_devil_icons_removed(src) + special_role = null + to_chat(current,"Your infernal link has been severed! You are no longer a devil!") + RemoveSpell(/obj/effect/proc_holder/spell/targeted/infernal_jaunt) + RemoveSpell(/obj/effect/proc_holder/spell/fireball/hellish) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/summon_contract) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork/greater) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/pitchfork/ascended) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/conjure_item/violin) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/summon_dancefloor) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/sintouch) + RemoveSpell(/obj/effect/proc_holder/spell/targeted/sintouch/ascended) + message_admins("[key_name_admin(usr)] has de-devil'ed [current].") + if(issilicon(current)) + var/mob/living/silicon/S = current + S.laws.clear_sixsixsix_laws() + devilinfo = null + log_admin("[key_name(usr)] has de-devil'ed [current].") + else if(src in SSticker.mode.sintouched) + SSticker.mode.sintouched -= src + message_admins("[key_name_admin(usr)] has de-sintouch'ed [current].") + log_admin("[key_name(usr)] has de-sintouch'ed [current].") + if("devil") + if(devilinfo) + devilinfo.ascendable = FALSE + message_admins("[key_name_admin(usr)] has made [current] unable to ascend as a devil.") + log_admin("[key_name_admin(usr)] has made [current] unable to ascend as a devil.") + return + if(!ishuman(current) && !isrobot(current)) + to_chat(usr, "This only works on humans and cyborgs!") + return + SSticker.mode.devils += src + special_role = "devil" + SSticker.mode.update_devil_icons_added(src) + SSticker.mode.finalize_devil(src, FALSE) + SSticker.mode.forge_devil_objectives(src, 2) + SSticker.mode.greet_devil(src) + message_admins("[key_name_admin(usr)] has devil'ed [current].") + log_admin("[key_name(usr)] has devil'ed [current].") + if("ascendable_devil") + if(devilinfo) + devilinfo.ascendable = TRUE + message_admins("[key_name_admin(usr)] has made [current] able to ascend as a devil.") + log_admin("[key_name_admin(usr)] has made [current] able to ascend as a devil.") + return + if(!ishuman(current) && !isrobot(current)) + to_chat(usr, "This only works on humans and cyborgs!") + return + SSticker.mode.devils += src + special_role = "devil" + SSticker.mode.update_devil_icons_added(src) + SSticker.mode.finalize_devil(src, TRUE) + SSticker.mode.forge_devil_objectives(src, 2) + SSticker.mode.greet_devil(src) + message_admins("[key_name_admin(usr)] has devil'ed [current]. The devil has been marked as ascendable.") + log_admin("[key_name(usr)] has devil'ed [current]. The devil has been marked as ascendable.") + if("sintouched") + var/mob/living/carbon/human/H = current + H.influenceSin() + message_admins("[key_name_admin(usr)] has sintouch'ed [current].") + log_admin("[key_name(usr)] has sintouch'ed [current].") + + else if(href_list["traitor"]) + switch(href_list["traitor"]) + if("clear") + if(has_antag_datum(/datum/antagonist/traitor)) + to_chat(current, "You have been brainwashed! You are no longer a traitor!") + remove_antag_datum(/datum/antagonist/traitor) + log_admin("[key_name(usr)] has de-traitored [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-traitored [key_name_admin(current)]") + + if("traitor") + if(!(has_antag_datum(/datum/antagonist/traitor))) + var/datum/antagonist/traitor/T = new() + T.give_objectives = FALSE + T.should_equip = FALSE + add_antag_datum(T) + log_admin("[key_name(usr)] has traitored [key_name(current)]") + message_admins("[key_name_admin(usr)] has traitored [key_name_admin(current)]") + + if("autoobjectives") + var/datum/antagonist/traitor/T = has_antag_datum(/datum/antagonist/traitor) + T.forge_traitor_objectives(src) + to_chat(usr, "The objectives for traitor [key] have been generated. You can edit them and announce manually.") + log_admin("[key_name(usr)] has automatically forged objectives for [key_name(current)]") + message_admins("[key_name_admin(usr)] has automatically forged objectives for [key_name_admin(current)]") + + else if(href_list["mindslave"]) + switch(href_list["mindslave"]) + if("clear") + if(has_antag_datum(/datum/antagonist/mindslave)) + var/mob/living/carbon/human/H = current + for(var/i in H.contents) + if(istype(i, /obj/item/implant/traitor)) + qdel(i) + break + remove_antag_datum(/datum/antagonist/mindslave) + log_admin("[key_name(usr)] has de-mindslaved [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-mindslaved [key_name_admin(current)]") + + else if(href_list["shadowling"]) + switch(href_list["shadowling"]) + if("clear") + SSticker.mode.update_shadow_icons_removed(src) + if(src in SSticker.mode.shadows) + SSticker.mode.shadows -= src + special_role = null + to_chat(current, "Your powers have been quenched! You are no longer a shadowling!") + message_admins("[key_name_admin(usr)] has de-shadowlinged [current].") + log_admin("[key_name(usr)] has de-shadowlinged [current].") + current.spellremove(current) + current.remove_language("Shadowling Hivemind") + else if(src in SSticker.mode.shadowling_thralls) + SSticker.mode.remove_thrall(src,0) + message_admins("[key_name_admin(usr)] has de-thrall'ed [current].") + log_admin("[key_name(usr)] has de-thralled [key_name(current)]") + message_admins("[key_name_admin(usr)] has de-thralled [key_name_admin(current)]") + if("shadowling") + if(!ishuman(current)) + to_chat(usr, "This only works on humans!") + return + SSticker.mode.shadows += src + special_role = SPECIAL_ROLE_SHADOWLING + to_chat(current, "Something stirs deep in your mind. A red light floods your vision, and slowly you remember. Though your human disguise has served you well, the \ + time is nigh to cast it off and enter your true form. You have disguised yourself amongst the humans, but you are not one of them. You are a shadowling, and you are to ascend at all costs.\ + ") + SSticker.mode.finalize_shadowling(src) + SSticker.mode.update_shadow_icons_added(src) + log_admin("[key_name(usr)] has shadowlinged [key_name(current)]") + message_admins("[key_name_admin(usr)] has shadowlinged [key_name_admin(current)]") + if("thrall") + if(!ishuman(current)) + to_chat(usr, "This only works on humans!") + return + SSticker.mode.add_thrall(src) + message_admins("[key_name_admin(usr)] has thralled [current].") + log_admin("[key_name(usr)] has thralled [current].") + + else if(href_list["abductor"]) + switch(href_list["abductor"]) + if("clear") + to_chat(usr, "Not implemented yet. Sorry!") + //ticker.mode.update_abductor_icons_removed(src) + if("abductor") + if(!ishuman(current)) + to_chat(usr, "This only works on humans!") + return + make_Abductor() + log_admin("[key_name(usr)] turned [current] into abductor.") + SSticker.mode.update_abductor_icons_added(src) + if("equip") + if(!ishuman(current)) + to_chat(usr, "This only works on humans!") + return + + var/mob/living/carbon/human/H = current + var/gear = alert("Agent or Scientist Gear","Gear","Agent","Scientist") + if(gear) + if(gear=="Agent") + H.equipOutfit(/datum/outfit/abductor/agent) + else + H.equipOutfit(/datum/outfit/abductor/scientist) + + else if(href_list["silicon"]) + switch(href_list["silicon"]) + if("unemag") + var/mob/living/silicon/robot/R = current + if(istype(R)) + R.emagged = 0 + if(R.module) + if(R.activated(R.module.emag)) + R.module_active = null + if(R.module_state_1 == R.module.emag) + R.module_state_1 = null + R.contents -= R.module.emag + else if(R.module_state_2 == R.module.emag) + R.module_state_2 = null + R.contents -= R.module.emag + else if(R.module_state_3 == R.module.emag) + R.module_state_3 = null + R.contents -= R.module.emag + R.clear_supplied_laws() + R.laws = new /datum/ai_laws/crewsimov + log_admin("[key_name(usr)] has un-emagged [key_name(current)]") + message_admins("[key_name_admin(usr)] has un-emagged [key_name_admin(current)]") + + if("unemagcyborgs") + if(isAI(current)) + var/mob/living/silicon/ai/ai = current + for(var/mob/living/silicon/robot/R in ai.connected_robots) + R.emagged = 0 + if(R.module) + if(R.activated(R.module.emag)) + R.module_active = null + if(R.module_state_1 == R.module.emag) + R.module_state_1 = null + R.contents -= R.module.emag + else if(R.module_state_2 == R.module.emag) + R.module_state_2 = null + R.contents -= R.module.emag + else if(R.module_state_3 == R.module.emag) + R.module_state_3 = null + R.contents -= R.module.emag + R.clear_supplied_laws() + R.laws = new /datum/ai_laws/crewsimov + log_admin("[key_name(usr)] has unemagged [key_name(ai)]'s cyborgs") + message_admins("[key_name_admin(usr)] has unemagged [key_name_admin(ai)]'s cyborgs") + + else if(href_list["common"]) + switch(href_list["common"]) + if("undress") + if(ishuman(current)) + var/mob/living/carbon/human/H = current + // Don't "undress" organs right out of the body + for(var/obj/item/W in H.contents - (H.bodyparts | H.internal_organs)) + current.unEquip(W, 1) + else + for(var/obj/item/W in current) + current.unEquip(W, 1) + log_admin("[key_name(usr)] has unequipped [key_name(current)]") + message_admins("[key_name_admin(usr)] has unequipped [key_name_admin(current)]") + if("takeuplink") + take_uplink() + var/datum/antagonist/traitor/T = has_antag_datum(/datum/antagonist/traitor) + T.antag_memory = "" //Remove any antag memory they may have had (uplink codes, code phrases) + log_admin("[key_name(usr)] has taken [key_name(current)]'s uplink") + message_admins("[key_name_admin(usr)] has taken [key_name_admin(current)]'s uplink") + if("crystals") + if(usr.client.holder.rights & (R_SERVER|R_EVENT)) + var/obj/item/uplink/hidden/suplink = find_syndicate_uplink() + var/crystals + if(suplink) + crystals = suplink.uses + crystals = input("Amount of telecrystals for [key]","Syndicate uplink", crystals) as null|num + if(!isnull(crystals)) + if(suplink) + suplink.uses = crystals + log_admin("[key_name(usr)] has set [key_name(current)]'s telecrystals to [crystals]") + message_admins("[key_name_admin(usr)] has set [key_name_admin(current)]'s telecrystals to [crystals]") + if("uplink") + if(has_antag_datum(/datum/antagonist/traitor)) + var/datum/antagonist/traitor/T = has_antag_datum(/datum/antagonist/traitor) + T.give_codewords() + if(!T.equip_traitor(src)) + to_chat(usr, "Equipping a syndicate failed!") + return + log_admin("[key_name(usr)] has given [key_name(current)] an uplink") + message_admins("[key_name_admin(usr)] has given [key_name_admin(current)] an uplink") + + else if(href_list["obj_announce"]) + announce_objectives() + SEND_SOUND(current, sound('sound/ambience/alarm4.ogg')) + log_admin("[key_name(usr)] has announced [key_name(current)]'s objectives") + message_admins("[key_name_admin(usr)] has announced [key_name_admin(current)]'s objectives") + + edit_memory() + + +// Datum antag mind procs +/datum/mind/proc/add_antag_datum(datum_type_or_instance, team) + if(!datum_type_or_instance) + return + var/datum/antagonist/A + if(!ispath(datum_type_or_instance)) + A = datum_type_or_instance + if(!istype(A)) + return + else + A = new datum_type_or_instance() + //Choose snowflake variation if antagonist handles it + var/datum/antagonist/S = A.specialization(src) + if(S && S != A) + qdel(A) + A = S + if(!A.can_be_owned(src)) + qdel(A) + return + A.owner = src + LAZYADD(antag_datums, A) + A.create_team(team) + var/datum/team/antag_team = A.get_team() + if(antag_team) + antag_team.add_member(src) + A.on_gain() + return A + +/datum/mind/proc/remove_antag_datum(datum_type) + if(!datum_type) + return + var/datum/antagonist/A = has_antag_datum(datum_type) + if(A) + A.on_removal() + return TRUE + + +/datum/mind/proc/remove_all_antag_datums() //For the Lazy amongst us. + for(var/a in antag_datums) + var/datum/antagonist/A = a + A.on_removal() + +/datum/mind/proc/has_antag_datum(datum_type, check_subtypes = TRUE) + if(!datum_type) + return + . = FALSE + for(var/a in antag_datums) + var/datum/antagonist/A = a + if(check_subtypes && istype(A, datum_type)) + return A + else if(A.type == datum_type) + return A + +/datum/mind/proc/announce_objectives() + to_chat(current, "Your current objectives:") + for(var/line in splittext(gen_objective_text(), "
    ")) + to_chat(current, line) + +/datum/mind/proc/find_syndicate_uplink() + var/list/L = current.get_contents() + for(var/obj/item/I in L) + if(I.hidden_uplink) + return I.hidden_uplink + return null + +/datum/mind/proc/take_uplink() + var/obj/item/uplink/hidden/H = find_syndicate_uplink() + if(H) + qdel(H) + +/datum/mind/proc/make_Traitor() + if(!has_antag_datum(/datum/antagonist/traitor)) + add_antag_datum(/datum/antagonist/traitor) + +/datum/mind/proc/make_Nuke() + if(!(src in SSticker.mode.syndicates)) + SSticker.mode.syndicates += src + SSticker.mode.update_synd_icons_added(src) + if(SSticker.mode.syndicates.len==1) + SSticker.mode.prepare_syndicate_leader(src) + else + current.real_name = "[syndicate_name()] Operative #[SSticker.mode.syndicates.len-1]" + special_role = SPECIAL_ROLE_NUKEOPS + assigned_role = SPECIAL_ROLE_NUKEOPS + to_chat(current, "You are a [syndicate_name()] agent!") + SSticker.mode.forge_syndicate_objectives(src) + SSticker.mode.greet_syndicate(src) + + current.loc = get_turf(locate("landmark*Syndicate-Spawn")) + + var/mob/living/carbon/human/H = current + qdel(H.belt) + qdel(H.back) + qdel(H.l_ear) + qdel(H.r_ear) + qdel(H.gloves) + qdel(H.head) + qdel(H.shoes) + qdel(H.wear_id) + qdel(H.wear_pda) + qdel(H.wear_suit) + qdel(H.w_uniform) + + SSticker.mode.equip_syndicate(current) + +/datum/mind/proc/make_Vampire() + if(!(src in SSticker.mode.vampires)) + SSticker.mode.vampires += src + SSticker.mode.grant_vampire_powers(current) + special_role = SPECIAL_ROLE_VAMPIRE + SSticker.mode.forge_vampire_objectives(src) + SSticker.mode.greet_vampire(src) + SSticker.mode.update_vampire_icons_added(src) + +/datum/mind/proc/make_Changeling() + if(!(src in SSticker.mode.changelings)) + SSticker.mode.changelings += src + SSticker.mode.grant_changeling_powers(current) + special_role = SPECIAL_ROLE_CHANGELING + SSticker.mode.forge_changeling_objectives(src) + SSticker.mode.greet_changeling(src) + SSticker.mode.update_change_icons_added(src) + +/datum/mind/proc/make_Overmind() + if(!(src in SSticker.mode.blob_overminds)) + SSticker.mode.blob_overminds += src + special_role = SPECIAL_ROLE_BLOB_OVERMIND + +/datum/mind/proc/make_Wizard() + if(!(src in SSticker.mode.wizards)) + SSticker.mode.wizards += src + special_role = SPECIAL_ROLE_WIZARD + assigned_role = SPECIAL_ROLE_WIZARD + //ticker.mode.learn_basic_spells(current) + if(!wizardstart.len) + current.loc = pick(latejoin) + to_chat(current, "HOT INSERTION, GO GO GO") + else + current.loc = pick(wizardstart) + + SSticker.mode.equip_wizard(current) + for(var/obj/item/spellbook/S in current.contents) + S.op = 0 + SSticker.mode.name_wizard(current) + SSticker.mode.forge_wizard_objectives(src) + SSticker.mode.greet_wizard(src) + SSticker.mode.update_wiz_icons_added(src) + +/datum/mind/proc/make_Rev() + if(SSticker.mode.head_revolutionaries.len>0) + // copy targets + var/datum/mind/valid_head = locate() in SSticker.mode.head_revolutionaries + if(valid_head) + for(var/datum/objective/mutiny/O in valid_head.objectives) + var/datum/objective/mutiny/rev_obj = new + rev_obj.owner = src + rev_obj.target = O.target + rev_obj.explanation_text = "Assassinate [O.target.current.real_name], the [O.target.assigned_role]." + objectives += rev_obj + SSticker.mode.greet_revolutionary(src,0) + SSticker.mode.head_revolutionaries += src + SSticker.mode.update_rev_icons_added(src) + special_role = SPECIAL_ROLE_HEAD_REV + + SSticker.mode.forge_revolutionary_objectives(src) + SSticker.mode.greet_revolutionary(src,0) + + var/list/L = current.get_contents() + var/obj/item/flash/flash = locate() in L + qdel(flash) + take_uplink() + var/fail = 0 +// fail |= !ticker.mode.equip_traitor(current, 1) + fail |= !SSticker.mode.equip_revolutionary(current) + +/datum/mind/proc/make_Abductor() + var/role = alert("Abductor Role ?","Role","Agent","Scientist") + var/team = input("Abductor Team ?","Team ?") in list(1,2,3,4) + var/teleport = alert("Teleport to ship ?","Teleport","Yes","No") + + if(!role || !team || !teleport) + return + + if(!ishuman(current)) + return + + SSticker.mode.abductors |= src + + var/datum/objective/stay_hidden/hidden_obj = new + hidden_obj.owner = src + objectives += hidden_obj + + var/datum/objective/experiment/O = new + O.owner = src + objectives += O + + var/mob/living/carbon/human/H = current + + H.set_species(/datum/species/abductor) + var/datum/species/abductor/S = H.dna.species + + if(role == "Scientist") + S.scientist = TRUE + + S.team = team + + var/list/obj/effect/landmark/abductor/agent_landmarks = new + var/list/obj/effect/landmark/abductor/scientist_landmarks = new + agent_landmarks.len = 4 + scientist_landmarks.len = 4 + for(var/obj/effect/landmark/abductor/A in GLOB.landmarks_list) + if(istype(A, /obj/effect/landmark/abductor/agent)) + agent_landmarks[text2num(A.team)] = A + else if(istype(A, /obj/effect/landmark/abductor/scientist)) + scientist_landmarks[text2num(A.team)] = A + + var/obj/effect/landmark/L + if(teleport == "Yes") + switch(role) + if("Agent") + L = agent_landmarks[team] + if("Scientist") + L = agent_landmarks[team] + H.forceMove(L.loc) + + +// check whether this mind's mob has been brigged for the given duration +// have to call this periodically for the duration to work properly +/datum/mind/proc/is_brigged(duration) + var/turf/T = current.loc + if(!istype(T)) + brigged_since = -1 + return 0 + + var/is_currently_brigged = current.is_in_brig() + if(!is_currently_brigged) + brigged_since = -1 + return 0 + + if(brigged_since == -1) + brigged_since = world.time + + return (duration <= world.time - brigged_since) + +/datum/mind/proc/AddSpell(obj/effect/proc_holder/spell/S) + spell_list += S + S.action.Grant(current) + +/datum/mind/proc/RemoveSpell(obj/effect/proc_holder/spell/spell) //To remove a specific spell from a mind + if(!spell) + return + for(var/obj/effect/proc_holder/spell/S in spell_list) + if(istype(S, spell)) + qdel(S) + spell_list -= S + +/datum/mind/proc/transfer_actions(mob/living/new_character) + if(current && current.actions) + for(var/datum/action/A in current.actions) + A.Grant(new_character) + transfer_mindbound_actions(new_character) + +/datum/mind/proc/transfer_mindbound_actions(mob/living/new_character) + for(var/X in spell_list) + var/obj/effect/proc_holder/spell/S = X + S.action.Grant(new_character) + +/datum/mind/proc/disrupt_spells(delay, list/exceptions = New()) + for(var/X in spell_list) + var/obj/effect/proc_holder/spell/S = X + for(var/type in exceptions) + if(istype(S, type)) + continue + S.charge_counter = delay + spawn(0) + S.start_recharge() + S.updateButtonIcon() + +/datum/mind/proc/get_ghost(even_if_they_cant_reenter) + for(var/mob/dead/observer/G in GLOB.dead_mob_list) + if(G.mind == src) + if(G.can_reenter_corpse || even_if_they_cant_reenter) + return G + break + +/datum/mind/proc/grab_ghost(force) + var/mob/dead/observer/G = get_ghost(even_if_they_cant_reenter = force) + . = G + if(G) + G.reenter_corpse() + + +/datum/mind/proc/make_zealot(mob/living/carbon/human/missionary, convert_duration = 6000, team_color = "red") + + zealot_master = missionary + + var/list/implanters + if(!(missionary.mind in SSticker.mode.implanter)) + SSticker.mode.implanter[missionary.mind] = list() + implanters = SSticker.mode.implanter[missionary.mind] + implanters.Add(src) + SSticker.mode.implanted.Add(src) + SSticker.mode.implanted[src] = missionary.mind + SSticker.mode.implanter[missionary.mind] = implanters + SSticker.mode.traitors += src + + + var/datum/objective/protect/zealot_objective = new + zealot_objective.target = missionary.mind + zealot_objective.owner = src + zealot_objective.explanation_text = "Obey every order from and protect [missionary.real_name], the [missionary.mind.assigned_role == missionary.mind.special_role ? (missionary.mind.special_role) : (missionary.mind.assigned_role)]." + objectives += zealot_objective + add_antag_datum(/datum/antagonist/mindslave) + + var/datum/antagonist/traitor/T = missionary.mind.has_antag_datum(/datum/antagonist) + T.update_traitor_icons_added(missionary.mind) + + to_chat(current, "You're now a loyal zealot of [missionary.name]! You now must lay down your life to protect [missionary.p_them()] and assist in [missionary.p_their()] goals at any cost.") + + var/datum/mindslaves/slaved = missionary.mind.som + som = slaved + slaved.serv += current + slaved.add_serv_hud(missionary.mind, "master") //handles master servent icons + slaved.add_serv_hud(src, "mindslave") + + var/obj/item/clothing/under/jumpsuit = null + if(ishuman(current)) //only bother with the jumpsuit stuff if we are a human type, since we won't have the slot otherwise + var/mob/living/carbon/human/H = current + if(H.w_uniform) + jumpsuit = H.w_uniform + jumpsuit.color = team_color + H.update_inv_w_uniform(0,0) + + add_attack_logs(missionary, current, "Converted to a zealot for [convert_duration/600] minutes") + addtimer(CALLBACK(src, .proc/remove_zealot, jumpsuit), convert_duration) //deconverts after the timer expires + return 1 + +/datum/mind/proc/remove_zealot(obj/item/clothing/under/jumpsuit = null) + if(!zealot_master) //if they aren't a zealot, we can't remove their zealot status, obviously. don't bother with the rest so we don't confuse them with the messages + return + remove_antag_datum(/datum/antagonist/mindslave) + add_attack_logs(zealot_master, current, "Lost control of zealot") + zealot_master = null + + if(jumpsuit) + jumpsuit.color = initial(jumpsuit.color) //reset the jumpsuit no matter where our mind is + if(ishuman(current)) //but only try updating us if we are still a human type since it is a human proc + var/mob/living/carbon/human/H = current + H.update_inv_w_uniform(0,0) + + to_chat(current, "You seem to have forgotten the events of the past 10 minutes or so, and your head aches a bit as if someone beat it savagely with a stick.") + to_chat(current, "This means you don't remember who you were working for or what you were doing.") + +/datum/mind/proc/is_revivable() //Note, this ONLY checks the mind. + if(damnation_type) + return FALSE + return TRUE + +// returns a mob to message to produce something visible for the target mind +/datum/mind/proc/messageable_mob() + if(!QDELETED(current) && current.client) + return current + else + return get_ghost(even_if_they_cant_reenter = TRUE) + +//Initialisation procs +/mob/proc/mind_initialize() + if(mind) + mind.key = key + else + mind = new /datum/mind(key) + if(SSticker) + SSticker.minds += mind + else + error("mind_initialize(): No ticker ready yet! Please inform Carn") + if(!mind.name) + mind.name = real_name + mind.current = src + +//HUMAN +/mob/living/carbon/human/mind_initialize() + ..() + if(!mind.assigned_role) + mind.assigned_role = "Civilian" //defualt + +/mob/proc/sync_mind() + mind_initialize() //updates the mind (or creates and initializes one if one doesn't exist) + mind.active = 1 //indicates that the mind is currently synced with a client + +//slime +/mob/living/simple_animal/slime/mind_initialize() + ..() + mind.assigned_role = "slime" + +//XENO +/mob/living/carbon/alien/mind_initialize() + ..() + mind.assigned_role = "Alien" + //XENO HUMANOID +/mob/living/carbon/alien/humanoid/queen/mind_initialize() + ..() + mind.special_role = SPECIAL_ROLE_XENOMORPH_QUEEN + +/mob/living/carbon/alien/humanoid/hunter/mind_initialize() + ..() + mind.special_role = SPECIAL_ROLE_XENOMORPH_HUNTER + +/mob/living/carbon/alien/humanoid/drone/mind_initialize() + ..() + mind.special_role = SPECIAL_ROLE_XENOMORPH_DRONE + +/mob/living/carbon/alien/humanoid/sentinel/mind_initialize() + ..() + mind.special_role = SPECIAL_ROLE_XENOMORPH_SENTINEL + //XENO LARVA +/mob/living/carbon/alien/larva/mind_initialize() + ..() + mind.special_role = SPECIAL_ROLE_XENOMORPH_LARVA + +//AI +/mob/living/silicon/ai/mind_initialize() + ..() + mind.assigned_role = "AI" + +//BORG +/mob/living/silicon/robot/mind_initialize() + ..() + mind.assigned_role = "Cyborg" + +//PAI +/mob/living/silicon/pai/mind_initialize() + ..() + mind.assigned_role = "pAI" + mind.special_role = null + +//BLOB +/mob/camera/overmind/mind_initialize() + ..() + mind.special_role = SPECIAL_ROLE_BLOB + +//Animals +/mob/living/simple_animal/mind_initialize() + ..() + mind.assigned_role = "Animal" + +/mob/living/simple_animal/pet/dog/corgi/mind_initialize() + ..() + mind.assigned_role = "Corgi" + +/mob/living/simple_animal/shade/mind_initialize() + ..() + mind.assigned_role = "Shade" + +/mob/living/simple_animal/construct/builder/mind_initialize() + ..() + mind.assigned_role = "Artificer" + mind.special_role = SPECIAL_ROLE_CULTIST + +/mob/living/simple_animal/construct/wraith/mind_initialize() + ..() + mind.assigned_role = "Wraith" + mind.special_role = SPECIAL_ROLE_CULTIST + +/mob/living/simple_animal/construct/armoured/mind_initialize() + ..() + mind.assigned_role = "Juggernaut" + mind.special_role = SPECIAL_ROLE_CULTIST diff --git a/code/datums/mixed.dm b/code/datums/mixed.dm index a40a9f9a34d..05fb0a99649 100644 --- a/code/datums/mixed.dm +++ b/code/datums/mixed.dm @@ -1,40 +1,40 @@ -/datum/data - var/name = "data" - var/size = 1.0 - - -/datum/data/function - name = "function" - size = 2.0 - - -/datum/data/function/data_control - name = "data control" - - -/datum/data/function/id_changer - name = "id changer" - - -/datum/data/record - name = "record" - size = 5.0 - var/list/fields = list( ) - -/datum/data/record/Destroy() - if(src in data_core.medical) - data_core.medical -= src - if(src in data_core.security) - data_core.security -= src - if(src in data_core.general) - data_core.general -= src - if(src in data_core.locked) - data_core.locked -= src - return ..() - -/datum/data/text - name = "text" - var/data = null - -/datum/debug - var/list/debuglist +/datum/data + var/name = "data" + var/size = 1.0 + + +/datum/data/function + name = "function" + size = 2.0 + + +/datum/data/function/data_control + name = "data control" + + +/datum/data/function/id_changer + name = "id changer" + + +/datum/data/record + name = "record" + size = 5.0 + var/list/fields = list( ) + +/datum/data/record/Destroy() + if(src in data_core.medical) + data_core.medical -= src + if(src in data_core.security) + data_core.security -= src + if(src in data_core.general) + data_core.general -= src + if(src in data_core.locked) + data_core.locked -= src + return ..() + +/datum/data/text + name = "text" + var/data = null + +/datum/debug + var/list/debuglist diff --git a/code/datums/mutable_appearance.dm b/code/datums/mutable_appearance.dm index 31c20315508..0887290a6f4 100644 --- a/code/datums/mutable_appearance.dm +++ b/code/datums/mutable_appearance.dm @@ -23,4 +23,4 @@ . = ..() alpha = 255 opacity = 1 - transform = null \ No newline at end of file + transform = null diff --git a/code/datums/outfits/outfit_admin.dm b/code/datums/outfits/outfit_admin.dm index 93e4015233b..d2c19f61bfa 100644 --- a/code/datums/outfits/outfit_admin.dm +++ b/code/datums/outfits/outfit_admin.dm @@ -291,7 +291,7 @@ var/obj/item/card/id/I = H.wear_id if(istype(I)) - apply_to_card(I, H, list(access_maint_tunnels), name) + apply_to_card(I, H, list(ACCESS_MAINT_TUNNELS), name) /datum/outfit/admin/pirate/first_mate name = "Space Pirate First Mate" @@ -375,7 +375,7 @@ var/obj/item/card/id/I = H.wear_id if(istype(I)) - apply_to_card(I, H, list(access_clown, access_theatre, access_maint_tunnels), "Tunnel Clown") + apply_to_card(I, H, list(ACCESS_CLOWN, ACCESS_THEATRE, ACCESS_MAINT_TUNNELS), "Tunnel Clown") /datum/outfit/admin/mime_assassin name = "Mime Assassin" @@ -424,7 +424,7 @@ var/obj/item/card/id/I = H.wear_id if(istype(I)) - apply_to_card(I, H, list(access_mime, access_theatre, access_maint_tunnels), "Mime") + apply_to_card(I, H, list(ACCESS_MIME, ACCESS_THEATRE, ACCESS_MAINT_TUNNELS), "Mime") H.sec_hud_set_ID() /datum/outfit/admin/greytide @@ -450,7 +450,7 @@ var/obj/item/card/id/I = H.wear_id if(istype(I)) - apply_to_card(I, H, list(access_maint_tunnels), "Greytide") + apply_to_card(I, H, list(ACCESS_MAINT_TUNNELS), "Greytide") /datum/outfit/admin/greytide/leader name = "Greytide Leader" @@ -471,7 +471,7 @@ var/obj/item/card/id/I = H.wear_id if(istype(I)) - apply_to_card(I, H, list(access_maint_tunnels), "Greytide Leader") + apply_to_card(I, H, list(ACCESS_MAINT_TUNNELS), "Greytide Leader") /datum/outfit/admin/greytide/xeno name = "Greytide Xeno" @@ -499,7 +499,7 @@ var/obj/item/card/id/I = H.wear_id if(istype(I)) - apply_to_card(I, H, list(access_maint_tunnels), "Legit Xenomorph") + apply_to_card(I, H, list(ACCESS_MAINT_TUNNELS), "Legit Xenomorph") @@ -534,7 +534,7 @@ var/obj/item/card/id/I = H.wear_id if(istype(I)) - apply_to_card(I, H, list(access_maint_tunnels), "Bard") + apply_to_card(I, H, list(ACCESS_MAINT_TUNNELS), "Bard") var/obj/item/clothing/ears/headphones/P = r_ear if(istype(P)) @@ -555,7 +555,7 @@ var/obj/item/card/id/I = H.wear_id if(istype(I)) - apply_to_card(I, H, list(access_maint_tunnels), name) + apply_to_card(I, H, list(ACCESS_MAINT_TUNNELS), name) /datum/outfit/admin/soviet/tourist name = "Soviet Tourist" @@ -719,7 +719,7 @@ var/obj/item/card/id/I = H.wear_id if(istype(I)) - apply_to_card(I, H, list(access_trade_sol, access_maint_tunnels, access_external_airlocks), name) + apply_to_card(I, H, list(ACCESS_TRADE_SOL, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS), name) /datum/outfit/admin/chrono name = "Chrono Legionnaire" @@ -907,7 +907,7 @@ var/obj/item/card/id/I = H.wear_id if(istype(I)) - apply_to_card(I, H, list(access_maint_tunnels), "Survivor") + apply_to_card(I, H, list(ACCESS_MAINT_TUNNELS), "Survivor") /datum/outfit/admin/masked_killer name = "Masked Killer" @@ -942,7 +942,7 @@ var/obj/item/card/id/I = H.wear_id if(istype(I)) - apply_to_card(I, H, list(access_maint_tunnels), "Masked Killer", "syndie") + apply_to_card(I, H, list(ACCESS_MAINT_TUNNELS), "Masked Killer", "syndie") /datum/outfit/admin/singuloth_knight name = "Singuloth Knight" diff --git a/code/datums/outfits/plasmamen.dm b/code/datums/outfits/plasmamen.dm index c14eafd08f2..376bf2af90c 100644 --- a/code/datums/outfits/plasmamen.dm +++ b/code/datums/outfits/plasmamen.dm @@ -175,4 +175,4 @@ name = "Blueshield Plasmaman" head = /obj/item/clothing/head/helmet/space/plasmaman/blueshield - uniform = /obj/item/clothing/under/plasmaman/blueshield \ No newline at end of file + uniform = /obj/item/clothing/under/plasmaman/blueshield diff --git a/code/datums/outfits/vv_outfit.dm b/code/datums/outfits/vv_outfit.dm index 7a07b087ae8..95c91318bb8 100644 --- a/code/datums/outfits/vv_outfit.dm +++ b/code/datums/outfits/vv_outfit.dm @@ -187,4 +187,4 @@ . = ..() stored_access = outfit_data["stored_access"] vv_values = outfit_data["vv_values"] - update_id_name = outfit_data["update_id_name"] \ No newline at end of file + update_id_name = outfit_data["update_id_name"] diff --git a/code/datums/radio.dm b/code/datums/radio.dm index 7670bc76e32..b748a9f9229 100644 --- a/code/datums/radio.dm +++ b/code/datums/radio.dm @@ -1,114 +1,114 @@ - -/datum/radio_frequency - var/frequency as num - var/list/list/obj/devices = list() - -/datum/radio_frequency/proc/post_signal(obj/source as obj|null, datum/signal/signal, var/filter = null as text|null, var/range = null as num|null) - var/turf/start_point - if(range) - start_point = get_turf(source) - if(!start_point) - qdel(signal) - return 0 - if(filter) - send_to_filter(source, signal, filter, start_point, range) - send_to_filter(source, signal, RADIO_DEFAULT, start_point, range) - else - //Broadcast the signal to everyone! - for(var/next_filter in devices) - send_to_filter(source, signal, next_filter, start_point, range) - -//Sends a signal to all machines belonging to a given filter. Should be called by post_signal() -/datum/radio_frequency/proc/send_to_filter(obj/source, datum/signal/signal, var/filter, var/turf/start_point = null, var/range = null) - if(range && !start_point) - return - - for(var/obj/device in devices[filter]) - if(device == source) - continue - if(range) - var/turf/end_point = get_turf(device) - if(!end_point) - continue - if(start_point.z!=end_point.z || get_dist(start_point, end_point) > range) - continue - - device.receive_signal(signal, TRANSMISSION_RADIO, frequency) - -/datum/radio_frequency/proc/add_listener(obj/device as obj, var/filter as text|null) - if(!filter) - filter = RADIO_DEFAULT - //log_admin("add_listener(device=[device],filter=[filter]) frequency=[frequency]") - var/list/obj/devices_line = devices[filter] - if(!devices_line) - devices_line = new - devices[filter] = devices_line - devices_line+=device -// var/list/obj/devices_line___ = devices[filter_str] -// var/l = devices_line___.len - //log_admin("DEBUG: devices_line.len=[devices_line.len]") - //log_admin("DEBUG: devices(filter_str).len=[l]") - -/datum/radio_frequency/proc/remove_listener(obj/device) - for(var/devices_filter in devices) - var/list/devices_line = devices[devices_filter] - devices_line-=device - while(null in devices_line) - devices_line -= null - if(devices_line.len==0) - devices -= devices_filter - qdel(devices_line) - -/datum/signal - var/obj/source - - var/transmission_method = 0 //unused at the moment - //0 = wire - //1 = radio transmission - //2 = subspace transmission - - var/list/data = list() - var/encryption - - var/frequency = 0 - -/datum/signal/proc/copy_from(datum/signal/model) - source = model.source - transmission_method = model.transmission_method - data = model.data - encryption = model.encryption - frequency = model.frequency - -/datum/signal/proc/debug_print() - if(source) - . = "signal = {source = '[source]' ([source:x],[source:y],[source:z])\n" - else - . = "signal = {source = '[source]' ()\n" - for(var/i in data) - . += "data\[\"[i]\"\] = \"[data[i]]\"\n" - if(islist(data[i])) - var/list/L = data[i] - for(var/t in L) - . += "data\[\"[i]\"\] list has: [t]" - -/datum/signal/proc/get_race(mob/M) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - . = H.dna.species.name - else if(isbrain(M)) - var/mob/living/carbon/brain/B = M - . = B.get_race() - else if(issilicon(M)) - . = "Artificial Life" - else if(isslime(M)) - . = "Slime" - else if(isbot(M)) - . = "Bot" - else if(isanimal(M)) - . = "Domestic Animal" - else - . = "Unidentifiable" - -//callback used by objects to react to incoming radio signals -/obj/proc/receive_signal(datum/signal/signal, receive_method, receive_param) - return null + +/datum/radio_frequency + var/frequency as num + var/list/list/obj/devices = list() + +/datum/radio_frequency/proc/post_signal(obj/source as obj|null, datum/signal/signal, var/filter = null as text|null, var/range = null as num|null) + var/turf/start_point + if(range) + start_point = get_turf(source) + if(!start_point) + qdel(signal) + return 0 + if(filter) + send_to_filter(source, signal, filter, start_point, range) + send_to_filter(source, signal, RADIO_DEFAULT, start_point, range) + else + //Broadcast the signal to everyone! + for(var/next_filter in devices) + send_to_filter(source, signal, next_filter, start_point, range) + +//Sends a signal to all machines belonging to a given filter. Should be called by post_signal() +/datum/radio_frequency/proc/send_to_filter(obj/source, datum/signal/signal, var/filter, var/turf/start_point = null, var/range = null) + if(range && !start_point) + return + + for(var/obj/device in devices[filter]) + if(device == source) + continue + if(range) + var/turf/end_point = get_turf(device) + if(!end_point) + continue + if(start_point.z!=end_point.z || get_dist(start_point, end_point) > range) + continue + + device.receive_signal(signal, TRANSMISSION_RADIO, frequency) + +/datum/radio_frequency/proc/add_listener(obj/device as obj, var/filter as text|null) + if(!filter) + filter = RADIO_DEFAULT + //log_admin("add_listener(device=[device],filter=[filter]) frequency=[frequency]") + var/list/obj/devices_line = devices[filter] + if(!devices_line) + devices_line = new + devices[filter] = devices_line + devices_line+=device +// var/list/obj/devices_line___ = devices[filter_str] +// var/l = devices_line___.len + //log_admin("DEBUG: devices_line.len=[devices_line.len]") + //log_admin("DEBUG: devices(filter_str).len=[l]") + +/datum/radio_frequency/proc/remove_listener(obj/device) + for(var/devices_filter in devices) + var/list/devices_line = devices[devices_filter] + devices_line-=device + while(null in devices_line) + devices_line -= null + if(devices_line.len==0) + devices -= devices_filter + qdel(devices_line) + +/datum/signal + var/obj/source + + var/transmission_method = 0 //unused at the moment + //0 = wire + //1 = radio transmission + //2 = subspace transmission + + var/list/data = list() + var/encryption + + var/frequency = 0 + +/datum/signal/proc/copy_from(datum/signal/model) + source = model.source + transmission_method = model.transmission_method + data = model.data + encryption = model.encryption + frequency = model.frequency + +/datum/signal/proc/debug_print() + if(source) + . = "signal = {source = '[source]' ([source:x],[source:y],[source:z])\n" + else + . = "signal = {source = '[source]' ()\n" + for(var/i in data) + . += "data\[\"[i]\"\] = \"[data[i]]\"\n" + if(islist(data[i])) + var/list/L = data[i] + for(var/t in L) + . += "data\[\"[i]\"\] list has: [t]" + +/datum/signal/proc/get_race(mob/M) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + . = H.dna.species.name + else if(isbrain(M)) + var/mob/living/carbon/brain/B = M + . = B.get_race() + else if(issilicon(M)) + . = "Artificial Life" + else if(isslime(M)) + . = "Slime" + else if(isbot(M)) + . = "Bot" + else if(isanimal(M)) + . = "Domestic Animal" + else + . = "Unidentifiable" + +//callback used by objects to react to incoming radio signals +/obj/proc/receive_signal(datum/signal/signal, receive_method, receive_param) + return null diff --git a/code/datums/recipe.dm b/code/datums/recipe.dm index 99d97e45626..52189fe527e 100644 --- a/code/datums/recipe.dm +++ b/code/datums/recipe.dm @@ -1,131 +1,131 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * - * /datum/recipe by rastaf0 13 apr 2011 * - * * * * * * * * * * * * * * * * * * * * * * * * * * - * This is powerful and flexible recipe system. - * It exists not only for food. - * supports both reagents and objects as prerequisites. - * In order to use this system you have to define a deriative from /datum/recipe - * * reagents are reagents. Acid, milc, booze, etc. - * * items are objects. Fruits, tools, circuit boards. - * * result is type to create as new object - * * time is optional parameter, you shall use in in your machine, - default /datum/recipe/ procs does not rely on this parameter. - * - * Functions you need: - * /datum/recipe/proc/make(var/obj/container as obj) - * Creates result inside container, - * deletes prerequisite reagents, - * transfers reagents from prerequisite objects, - * deletes all prerequisite objects (even not needed for recipe at the moment). - * - * /proc/select_recipe(list/datum/recipe/avaiable_recipes, obj/obj as obj, exact = 1) - * Wonderful function that select suitable recipe for you. - * obj is a machine (or magik hat) with prerequisites, - * exact = 0 forces algorithm to ignore superfluous stuff. - * - * - * Functions you do not need to call directly but could: - * /datum/recipe/proc/check_reagents(var/datum/reagents/avail_reagents) - * //1=precisely, 0=insufficiently, -1=superfluous - * - * /datum/recipe/proc/check_items(var/obj/container as obj) - * //1=precisely, 0=insufficiently, -1=superfluous - * - * */ - -/datum/recipe - var/list/reagents // example: = list("berryjuice" = 5) // do not list same reagent twice - var/list/items // example: =list(/obj/item/crowbar, /obj/item/welder) // place /foo/bar before /foo - var/result //example: = /obj/item/reagent_containers/food/snacks/donut - var/time = 100 // 1/10 part of second - var/byproduct // example: = /obj/item/kitchen/mould // byproduct to return, such as a mould or trash - -/datum/recipe/proc/check_reagents(datum/reagents/avail_reagents) //1=precisely, 0=insufficiently, -1=superfluous - . = 1 - for(var/r_r in reagents) - var/aval_r_amnt = avail_reagents.get_reagent_amount(r_r) - if(!(abs(aval_r_amnt - reagents[r_r])<0.5)) //if NOT equals - if(aval_r_amnt>reagents[r_r]) - . = -1 - else - return 0 - if((reagents?(reagents.len):(0)) < avail_reagents.reagent_list.len) - return -1 - return . - -/datum/recipe/proc/check_items(obj/container, list/ignored_items = null) //1=precisely, 0=insufficiently, -1=superfluous - . = 1 - var/list/checklist = items ? items.Copy() : list() - for(var/obj/O in container) - if(ignored_items && is_type_in_list(O, ignored_items)) //skip if this is something we are ignoring - continue - if(!items) - return -1 - var/found = 0 - for(var/type in checklist) - if(istype(O,type)) - checklist -= type - found = 1 - break - if(!found) - . = -1 - if(checklist.len) - return 0 - return . - -//general version -/datum/recipe/proc/make(obj/container) - var/obj/result_obj = new result(container) - for(var/obj/O in (container.contents-result_obj)) - O.reagents.trans_to(result_obj, O.reagents.total_volume) - qdel(O) - container.reagents.clear_reagents() - return result_obj - -// food-related -/datum/recipe/proc/make_food(obj/container) - var/obj/result_obj = new result(container) - for(var/obj/O in (container.contents-result_obj)) - if(O.reagents) - O.reagents.del_reagent("nutriment") - O.reagents.update_total() - O.reagents.trans_to(result_obj, O.reagents.total_volume) - qdel(O) - container.reagents.clear_reagents() - return result_obj - -/proc/select_recipe(list/datum/recipe/available_recipes, obj/obj, exact = 1 as num, list/ignored_items = null) - if(!exact) - exact = -1 - var/list/datum/recipe/possible_recipes = new - for(var/datum/recipe/recipe in available_recipes) - if(recipe.check_reagents(obj.reagents) == exact && recipe.check_items(obj, ignored_items) == exact) - possible_recipes += recipe - if(possible_recipes.len == 0) - return null - else if(possible_recipes.len == 1) - return possible_recipes[1] - else //okay, let's select the most complicated recipe - var/r_count = 0 - var/i_count = 0 - . = possible_recipes[1] - for(var/datum/recipe/recipe in possible_recipes) - var/N_i = (recipe.items)?(recipe.items.len):0 - var/N_r = (recipe.reagents)?(recipe.reagents.len):0 - if(N_i > i_count || (N_i== i_count && N_r > r_count )) - r_count = N_r - i_count = N_i - . = recipe - return . - -/datum/recipe/proc/get_byproduct() - if(byproduct) - return byproduct - else - return null - -/datum/recipe/proc/count_n_items() - var/count = 0 - if(items && items.len) - count += items.len - return count \ No newline at end of file +/* * * * * * * * * * * * * * * * * * * * * * * * * * + * /datum/recipe by rastaf0 13 apr 2011 * + * * * * * * * * * * * * * * * * * * * * * * * * * * + * This is powerful and flexible recipe system. + * It exists not only for food. + * supports both reagents and objects as prerequisites. + * In order to use this system you have to define a deriative from /datum/recipe + * * reagents are reagents. Acid, milc, booze, etc. + * * items are objects. Fruits, tools, circuit boards. + * * result is type to create as new object + * * time is optional parameter, you shall use in in your machine, + default /datum/recipe/ procs does not rely on this parameter. + * + * Functions you need: + * /datum/recipe/proc/make(var/obj/container as obj) + * Creates result inside container, + * deletes prerequisite reagents, + * transfers reagents from prerequisite objects, + * deletes all prerequisite objects (even not needed for recipe at the moment). + * + * /proc/select_recipe(list/datum/recipe/avaiable_recipes, obj/obj as obj, exact = 1) + * Wonderful function that select suitable recipe for you. + * obj is a machine (or magik hat) with prerequisites, + * exact = 0 forces algorithm to ignore superfluous stuff. + * + * + * Functions you do not need to call directly but could: + * /datum/recipe/proc/check_reagents(var/datum/reagents/avail_reagents) + * //1=precisely, 0=insufficiently, -1=superfluous + * + * /datum/recipe/proc/check_items(var/obj/container as obj) + * //1=precisely, 0=insufficiently, -1=superfluous + * + * */ + +/datum/recipe + var/list/reagents // example: = list("berryjuice" = 5) // do not list same reagent twice + var/list/items // example: =list(/obj/item/crowbar, /obj/item/welder) // place /foo/bar before /foo + var/result //example: = /obj/item/reagent_containers/food/snacks/donut + var/time = 100 // 1/10 part of second + var/byproduct // example: = /obj/item/kitchen/mould // byproduct to return, such as a mould or trash + +/datum/recipe/proc/check_reagents(datum/reagents/avail_reagents) //1=precisely, 0=insufficiently, -1=superfluous + . = 1 + for(var/r_r in reagents) + var/aval_r_amnt = avail_reagents.get_reagent_amount(r_r) + if(!(abs(aval_r_amnt - reagents[r_r])<0.5)) //if NOT equals + if(aval_r_amnt>reagents[r_r]) + . = -1 + else + return 0 + if((reagents?(reagents.len):(0)) < avail_reagents.reagent_list.len) + return -1 + return . + +/datum/recipe/proc/check_items(obj/container, list/ignored_items = null) //1=precisely, 0=insufficiently, -1=superfluous + . = 1 + var/list/checklist = items ? items.Copy() : list() + for(var/obj/O in container) + if(ignored_items && is_type_in_list(O, ignored_items)) //skip if this is something we are ignoring + continue + if(!items) + return -1 + var/found = 0 + for(var/type in checklist) + if(istype(O,type)) + checklist -= type + found = 1 + break + if(!found) + . = -1 + if(checklist.len) + return 0 + return . + +//general version +/datum/recipe/proc/make(obj/container) + var/obj/result_obj = new result(container) + for(var/obj/O in (container.contents-result_obj)) + O.reagents.trans_to(result_obj, O.reagents.total_volume) + qdel(O) + container.reagents.clear_reagents() + return result_obj + +// food-related +/datum/recipe/proc/make_food(obj/container) + var/obj/result_obj = new result(container) + for(var/obj/O in (container.contents-result_obj)) + if(O.reagents) + O.reagents.del_reagent("nutriment") + O.reagents.update_total() + O.reagents.trans_to(result_obj, O.reagents.total_volume) + qdel(O) + container.reagents.clear_reagents() + return result_obj + +/proc/select_recipe(list/datum/recipe/available_recipes, obj/obj, exact = 1 as num, list/ignored_items = null) + if(!exact) + exact = -1 + var/list/datum/recipe/possible_recipes = new + for(var/datum/recipe/recipe in available_recipes) + if(recipe.check_reagents(obj.reagents) == exact && recipe.check_items(obj, ignored_items) == exact) + possible_recipes += recipe + if(possible_recipes.len == 0) + return null + else if(possible_recipes.len == 1) + return possible_recipes[1] + else //okay, let's select the most complicated recipe + var/r_count = 0 + var/i_count = 0 + . = possible_recipes[1] + for(var/datum/recipe/recipe in possible_recipes) + var/N_i = (recipe.items)?(recipe.items.len):0 + var/N_r = (recipe.reagents)?(recipe.reagents.len):0 + if(N_i > i_count || (N_i== i_count && N_r > r_count )) + r_count = N_r + i_count = N_i + . = recipe + return . + +/datum/recipe/proc/get_byproduct() + if(byproduct) + return byproduct + else + return null + +/datum/recipe/proc/count_n_items() + var/count = 0 + if(items && items.len) + count += items.len + return count diff --git a/code/datums/ruins/lavaland.dm b/code/datums/ruins/lavaland.dm index bb80fe84d01..f826b6ff0d2 100644 --- a/code/datums/ruins/lavaland.dm +++ b/code/datums/ruins/lavaland.dm @@ -217,4 +217,4 @@ datum/map_template/ruin/lavaland/ash_walker id = "puzzle" description = "Mystery to be solved." suffix = "lavaland_surface_puzzle.dmm" - cost = 5 \ No newline at end of file + cost = 5 diff --git a/code/datums/spell.dm b/code/datums/spell.dm index f19f8100553..7f1541248d9 100644 --- a/code/datums/spell.dm +++ b/code/datums/spell.dm @@ -1,499 +1,499 @@ -#define TARGET_CLOSEST 1 -#define TARGET_RANDOM 2 - -/obj/effect/proc_holder - var/panel = "Debug"//What panel the proc holder needs to go on. - var/active = FALSE //Used by toggle based abilities. - var/ranged_mousepointer - var/mob/living/ranged_ability_user - -/obj/effect/proc_holder/singularity_act() - return - -/obj/effect/proc_holder/singularity_pull() - return - -var/list/spells = typesof(/obj/effect/proc_holder/spell) //needed for the badmin verb for now - -/obj/effect/proc_holder/proc/InterceptClickOn(mob/living/user, params, atom/A) - if(user.ranged_ability != src) - to_chat(user, "[user.ranged_ability.name] has been disabled.") - user.ranged_ability.remove_ranged_ability(user) - return TRUE //TRUE for failed, FALSE for passed. - user.changeNext_click(CLICK_CD_CLICK_ABILITY) - user.face_atom(A) - return FALSE - -/obj/effect/proc_holder/proc/add_ranged_ability(mob/living/user, var/msg) - if(!user || !user.client) - return - if(user.ranged_ability && user.ranged_ability != src) - to_chat(user, "[user.ranged_ability.name] has been replaced by [name].") - user.ranged_ability.remove_ranged_ability(user) - user.ranged_ability = src - ranged_ability_user = user - user.client.click_intercept = user.ranged_ability - add_mousepointer(user.client) - active = TRUE - if(msg) - to_chat(user, msg) - update_icon() - -/obj/effect/proc_holder/proc/add_mousepointer(client/C) - if(C && ranged_mousepointer && C.mouse_pointer_icon == initial(C.mouse_pointer_icon)) - C.mouse_pointer_icon = ranged_mousepointer - -/obj/effect/proc_holder/proc/remove_mousepointer(client/C) - if(C && ranged_mousepointer && C.mouse_pointer_icon == ranged_mousepointer) - C.mouse_pointer_icon = initial(C.mouse_pointer_icon) - -/obj/effect/proc_holder/proc/remove_ranged_ability(mob/living/user, var/msg) - if(!user || !user.client || (user.ranged_ability && user.ranged_ability != src)) //To avoid removing the wrong ability - return - user.ranged_ability = null - ranged_ability_user = null - user.client.click_intercept = null - remove_mousepointer(user.client) - active = FALSE - if(msg) - to_chat(user, msg) - update_icon() - -/obj/effect/proc_holder/spell - name = "Spell" - desc = "A wizard spell" - panel = "Spells"//What panel the proc holder needs to go on. - density = 0 - opacity = 0 - - var/school = "evocation" //not relevant at now, but may be important later if there are changes to how spells work. the ones I used for now will probably be changed... maybe spell presets? lacking flexibility but with some other benefit? - - var/charge_type = "recharge" //can be recharge or charges, see charge_max and charge_counter descriptions; can also be based on the holder's vars now, use "holder_var" for that - - var/charge_max = 100 //recharge time in deciseconds if charge_type = "recharge" or starting charges if charge_type = "charges" - var/starts_charged = TRUE //Does this spell start ready to go? - var/charge_counter = 0 //can only cast spells if it equals recharge, ++ each decisecond if charge_type = "recharge" or -- each cast if charge_type = "charges" - var/still_recharging_msg = "The spell is still recharging." - - var/holder_var_type = "bruteloss" //only used if charge_type equals to "holder_var" - var/holder_var_amount = 20 //same. The amount adjusted with the mob's var when the spell is used - - var/ghost = 0 // Skip life check. - var/clothes_req = 1 //see if it requires clothes - var/human_req = 0 //spell can only be cast by humans - var/nonabstract_req = 0 //spell can only be cast by mobs that are physical entities - var/stat_allowed = 0 //see if it requires being conscious/alive, need to set to 1 for ghostpells - var/invocation = "HURP DURP" //what is uttered when the wizard casts the spell - var/invocation_emote_self = null - var/invocation_type = "none" //can be none, whisper and shout - var/range = 7 //the range of the spell; outer radius for aoe spells - var/message = "" //whatever it says to the guy affected by it - var/selection_type = "view" //can be "range" or "view" - var/spell_level = 0 //if a spell can be taken multiple times, this raises - var/level_max = 4 //The max possible level_max is 4 - var/cooldown_min = 0 //This defines what spell quickened four timeshas as a cooldown. Make sure to set this for every spell - - var/overlay = 0 - var/overlay_icon = 'icons/obj/wizard.dmi' - var/overlay_icon_state = "spell" - var/overlay_lifespan = 0 - - var/sparks_spread = 0 - var/sparks_amt = 0 //cropped at 10 - var/smoke_spread = 0 //1 - harmless, 2 - harmful - var/smoke_amt = 0 //cropped at 10 - - var/critfailchance = 0 - var/centcom_cancast = 1 //Whether or not the spell should be allowed on z2 - - var/datum/action/spell_action/action = null - var/action_icon = 'icons/mob/actions/actions.dmi' - var/action_icon_state = "spell_default" - var/action_background_icon_state = "bg_spell" - var/special_availability_check = 0//Whether the spell needs to bypass the action button's IsAvailable() - - var/sound = null //The sound the spell makes when it is cast - -/obj/effect/proc_holder/spell/proc/cast_check(skipcharge = 0, mob/living/user = usr) //checks if the spell can be cast based on its settings; skipcharge is used when an additional cast_check is called inside the spell - if(((!user.mind) || !(src in user.mind.spell_list)) && !(src in user.mob_spell_list)) - to_chat(user, "You shouldn't have this spell! Something's wrong.") - return 0 - - if(ishuman(user)) - var/mob/living/carbon/human/caster = user - if(caster.remoteview_target) - caster.remoteview_target = null - caster.reset_perspective(0) - return 0 - - if(is_admin_level(user.z) && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel - return 0 - - if(!skipcharge) - switch(charge_type) - if("recharge") - if(charge_counter < charge_max) - to_chat(user, still_recharging_msg) - return 0 - if("charges") - if(!charge_counter) - to_chat(user, "[name] has no charges left.") - return 0 - - if(!ghost) - if(user.stat && !stat_allowed) - to_chat(user, "You can't cast this spell while incapacitated.") - return 0 - if(ishuman(user) && (invocation_type == "whisper" || invocation_type == "shout") && user.is_muzzled()) - to_chat(user, "Mmmf mrrfff!") - return 0 - - var/obj/effect/proc_holder/spell/noclothes/clothes_spell = locate() in (user.mob_spell_list | (user.mind ? user.mind.spell_list : list())) - if((ishuman(user) && clothes_req) && !istype(clothes_spell))//clothes check - var/mob/living/carbon/human/H = user - var/obj/item/clothing/robe = H.wear_suit - var/obj/item/clothing/hat = H.head - var/obj/item/clothing/shoes = H.shoes - if(!robe || !hat || !shoes) - to_chat(user, "Your outfit isn't complete, you should put on your robe and wizard hat, as well as sandals.") - return 0 - if(!robe.magical || !hat.magical || !shoes.magical) - to_chat(user, "Your outfit isn't magical enough, you should put on your robe and wizard hat, as well as your sandals.") - return 0 - else if(!ishuman(user)) - if(clothes_req || human_req) - to_chat(user, "This spell can only be cast by humans!") - return 0 - if(nonabstract_req && (isbrain(user) || ispAI(user))) - to_chat(user, "This spell can only be cast by physical beings!") - return 0 - - if(!skipcharge) - switch(charge_type) - if("recharge") - charge_counter = 0 //doesn't start recharging until the targets selecting ends - if("charges") - charge_counter-- //returns the charge if the targets selecting fails - if("holdervar") - adjust_var(user, holder_var_type, holder_var_amount) - - if(action) - action.UpdateButtonIcon() - return 1 - -/obj/effect/proc_holder/spell/proc/invocation(mob/user = usr) //spelling the spell out and setting it on recharge/reducing charges amount - switch(invocation_type) - if("shout") - if(!user.IsVocal()) - user.custom_emote(1, "makes frantic gestures!") - else - if(prob(50))//Auto-mute? Fuck that noise - user.say(invocation) - else - user.say(replacetext(invocation," ","`")) - if("whisper") - if(prob(50)) - user.whisper(invocation) - else - user.whisper(replacetext(invocation," ","`")) - if("emote") - user.visible_message(invocation, invocation_emote_self) //same style as in mob/living/emote.dm - -/obj/effect/proc_holder/spell/proc/playMagSound() - playsound(get_turf(usr), sound,50,1) - -/obj/effect/proc_holder/spell/New() - ..() - action = new(src) - still_recharging_msg = "[name] is still recharging." - if(starts_charged) - charge_counter = charge_max - else - start_recharge() - -/obj/effect/proc_holder/spell/Destroy() - QDEL_NULL(action) - return ..() - -/obj/effect/proc_holder/spell/Click() - if(cast_check()) - choose_targets() - return 1 - -/obj/effect/proc_holder/spell/proc/choose_targets(mob/user = usr) //depends on subtype - /targeted or /aoe_turf - return - -/obj/effect/proc_holder/spell/proc/start_recharge() - if(action) - action.UpdateButtonIcon() - START_PROCESSING(SSfastprocess, src) - -/obj/effect/proc_holder/spell/process() - charge_counter += 2 - if(charge_counter < charge_max) - return - STOP_PROCESSING(SSfastprocess, src) - charge_counter = charge_max - if(action) - action.UpdateButtonIcon() - -/obj/effect/proc_holder/spell/proc/perform(list/targets, recharge = 1, mob/user = usr) //if recharge is started is important for the trigger spells - before_cast(targets) - invocation() - if(user && user.ckey) - user.create_attack_log("[key_name(user)] cast the spell [name].") - spawn(0) - if(charge_type == "recharge" && recharge) - start_recharge() - - if(sound) - playMagSound() - - if(prob(critfailchance)) - critfail(targets) - else - cast(targets, user = user) - after_cast(targets) - if(action) - action.UpdateButtonIcon() - -/obj/effect/proc_holder/spell/proc/before_cast(list/targets) - if(overlay) - for(var/atom/target in targets) - var/location - if(istype(target,/mob/living)) - location = target.loc - else if(istype(target,/turf)) - location = target - var/obj/effect/overlay/spell = new /obj/effect/overlay(location) - spell.icon = overlay_icon - spell.icon_state = overlay_icon_state - spell.anchored = 1 - spell.density = 0 - spawn(overlay_lifespan) - qdel(spell) - -/obj/effect/proc_holder/spell/proc/after_cast(list/targets) - for(var/atom/target in targets) - var/location - if(istype(target,/mob/living)) - location = target.loc - else if(istype(target,/turf)) - location = target - if(istype(target,/mob/living) && message) - to_chat(target, text("[message]")) - if(sparks_spread) - do_sparks(sparks_amt, 0, location) - if(smoke_spread) - if(smoke_spread == 1) - var/datum/effect_system/smoke_spread/smoke = new - smoke.set_up(smoke_amt, 0, location) //no idea what the 0 is - smoke.start() - else if(smoke_spread == 2) - var/datum/effect_system/smoke_spread/bad/smoke = new - smoke.set_up(smoke_amt, 0, location) //no idea what the 0 is - smoke.start() - else if(smoke_spread == 3) - var/datum/effect_system/smoke_spread/sleeping/smoke = new - smoke.set_up(smoke_amt, 0, location) // same here - smoke.start() - -/obj/effect/proc_holder/spell/proc/cast(list/targets, mob/user = usr) - return - -/obj/effect/proc_holder/spell/proc/critfail(list/targets) - return - -/obj/effect/proc_holder/spell/proc/revert_cast(mob/user = usr) //resets recharge or readds a charge - switch(charge_type) - if("recharge") - charge_counter = charge_max - if("charges") - charge_counter++ - if("holdervar") - adjust_var(user, holder_var_type, -holder_var_amount) - if(action) - action.UpdateButtonIcon() - -/obj/effect/proc_holder/spell/proc/updateButtonIcon() - if(action) - action.UpdateButtonIcon() - -/obj/effect/proc_holder/spell/proc/adjust_var(mob/living/target = usr, type, amount) //handles the adjustment of the var when the spell is used. has some hardcoded types - switch(type) - if("bruteloss") - target.adjustBruteLoss(amount) - if("fireloss") - target.adjustFireLoss(amount) - if("toxloss") - target.adjustToxLoss(amount) - if("oxyloss") - target.adjustOxyLoss(amount) - if("stunned") - target.AdjustStunned(amount) - if("weakened") - target.AdjustWeakened(amount) - if("paralysis") - target.AdjustParalysis(amount) - else - target.vars[type] += amount //I bear no responsibility for the runtimes that'll happen if you try to adjust non-numeric or even non-existant vars - return - -/obj/effect/proc_holder/spell/targeted //can mean aoe for mobs (limited/unlimited number) or one target mob - var/max_targets = 1 //leave 0 for unlimited targets in range, 1 for one selectable target in range, more for limited number of casts (can all target one guy, depends on target_ignore_prev) in range - var/target_ignore_prev = 1 //only important if max_targets > 1, affects if the spell can be cast multiple times at one person from one cast - var/include_user = 0 //if it includes usr in the target list - var/random_target = 0 // chooses random viable target instead of asking the caster - var/random_target_priority = TARGET_CLOSEST // if random_target is enabled how it will pick the target - var/humans_only = 0 //for avoiding simple animals and only doing "human" mobs, 0 = all mobs, 1 = humans only - -/obj/effect/proc_holder/spell/aoe_turf //affects all turfs in view or range (depends) - var/inner_radius = -1 //for all your ring spell needs - -/obj/effect/proc_holder/spell/targeted/choose_targets(mob/user = usr) - var/list/targets = list() - - switch(max_targets) - if(0) //unlimited - - if(!humans_only) - for(var/mob/living/target in view_or_range(range, user, selection_type)) - targets += target - else - for(var/mob/living/carbon/human/target in view_or_range(range, user, selection_type)) - targets += target - - if(1) //single target can be picked - if(range < 0) - targets += user - else - var/possible_targets = list() - - if(!humans_only) - for(var/mob/living/M in view_or_range(range, user, selection_type)) - if(!include_user && user == M) - continue - possible_targets += M - else - for(var/mob/living/carbon/human/M in view_or_range(range, user, selection_type)) - if(!include_user && user == M) - continue - possible_targets += M - - //targets += input("Choose the target for the spell.", "Targeting") as mob in possible_targets - //Adds a safety check post-input to make sure those targets are actually in range. - var/mob/M - if(!random_target) - M = input("Choose the target for the spell.", "Targeting") as mob in possible_targets - else - switch(random_target_priority) - if(TARGET_RANDOM) - M = pick(possible_targets) - if(TARGET_CLOSEST) - for(var/mob/living/L in possible_targets) - if(M) - if(get_dist(user,L) < get_dist(user,M)) - if(los_check(user,L)) - M = L - else - if(los_check(user,L)) - M = L - if(M in view_or_range(range, user, selection_type)) targets += M - - else - var/list/possible_targets = list() - if(!humans_only) - for(var/mob/living/target in view_or_range(range, user, selection_type)) - possible_targets += target - else - for(var/mob/living/carbon/human/target in view_or_range(range, user, selection_type)) - possible_targets += target - for(var/i=1,i<=max_targets,i++) - if(!possible_targets.len) - break - if(target_ignore_prev) - var/target = pick(possible_targets) - possible_targets -= target - targets += target - else - targets += pick(possible_targets) - - if(!include_user && (user in targets)) - targets -= user - - if(!targets.len) //doesn't waste the spell - revert_cast(user) - return - - perform(targets, user=user) - - return - -/obj/effect/proc_holder/spell/aoe_turf/choose_targets(mob/user = usr) - var/list/targets = list() - - for(var/turf/target in view_or_range(range,user,selection_type)) - if(!(target in view_or_range(inner_radius,user,selection_type))) - targets += target - - if(!targets.len) //doesn't waste the spell - revert_cast() - return - - perform(targets, user=user) - - return - - -/obj/effect/proc_holder/spell/targeted/proc/los_check(mob/A,mob/B) - //Checks for obstacles from A to B - var/obj/dummy = new(A.loc) - dummy.pass_flags |= PASSTABLE - for(var/turf/turf in getline(A,B)) - for(var/atom/movable/AM in turf) - if(!AM.CanPass(dummy,turf,1)) - qdel(dummy) - return 0 - qdel(dummy) - return 1 - -/obj/effect/proc_holder/spell/proc/can_cast(mob/user = usr) - if(((!user.mind) || !(src in user.mind.spell_list)) && !(src in user.mob_spell_list)) - return 0 - - if(is_admin_level(user.z) && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel - return 0 - - switch(charge_type) - if("recharge") - if(charge_counter < charge_max) - return 0 - if("charges") - if(!charge_counter) - return 0 - - if(user.stat && !stat_allowed) - return 0 - - if(ishuman(user)) - var/mob/living/carbon/human/H = user - - if((invocation_type == "whisper" || invocation_type == "shout") && H.is_muzzled()) - return 0 - - var/clothcheck = locate(/obj/effect/proc_holder/spell/noclothes) in user.mob_spell_list - var/clothcheck2 = user.mind && (locate(/obj/effect/proc_holder/spell/noclothes) in user.mind.spell_list) - if(clothes_req && !clothcheck && !clothcheck2) //clothes check - var/obj/item/clothing/robe = H.wear_suit - var/obj/item/clothing/hat = H.head - var/obj/item/clothing/shoes = H.shoes - if(!robe || !hat || !shoes) - return 0 - if(!robe.magical || !hat.magical || !shoes.magical) - return 0 - else - if(clothes_req || human_req) - return 0 - if(nonabstract_req && (isbrain(user) || ispAI(user))) - return 0 - return 1 +#define TARGET_CLOSEST 1 +#define TARGET_RANDOM 2 + +/obj/effect/proc_holder + var/panel = "Debug"//What panel the proc holder needs to go on. + var/active = FALSE //Used by toggle based abilities. + var/ranged_mousepointer + var/mob/living/ranged_ability_user + +/obj/effect/proc_holder/singularity_act() + return + +/obj/effect/proc_holder/singularity_pull() + return + +var/list/spells = typesof(/obj/effect/proc_holder/spell) //needed for the badmin verb for now + +/obj/effect/proc_holder/proc/InterceptClickOn(mob/living/user, params, atom/A) + if(user.ranged_ability != src) + to_chat(user, "[user.ranged_ability.name] has been disabled.") + user.ranged_ability.remove_ranged_ability(user) + return TRUE //TRUE for failed, FALSE for passed. + user.changeNext_click(CLICK_CD_CLICK_ABILITY) + user.face_atom(A) + return FALSE + +/obj/effect/proc_holder/proc/add_ranged_ability(mob/living/user, var/msg) + if(!user || !user.client) + return + if(user.ranged_ability && user.ranged_ability != src) + to_chat(user, "[user.ranged_ability.name] has been replaced by [name].") + user.ranged_ability.remove_ranged_ability(user) + user.ranged_ability = src + ranged_ability_user = user + user.client.click_intercept = user.ranged_ability + add_mousepointer(user.client) + active = TRUE + if(msg) + to_chat(user, msg) + update_icon() + +/obj/effect/proc_holder/proc/add_mousepointer(client/C) + if(C && ranged_mousepointer && C.mouse_pointer_icon == initial(C.mouse_pointer_icon)) + C.mouse_pointer_icon = ranged_mousepointer + +/obj/effect/proc_holder/proc/remove_mousepointer(client/C) + if(C && ranged_mousepointer && C.mouse_pointer_icon == ranged_mousepointer) + C.mouse_pointer_icon = initial(C.mouse_pointer_icon) + +/obj/effect/proc_holder/proc/remove_ranged_ability(mob/living/user, var/msg) + if(!user || !user.client || (user.ranged_ability && user.ranged_ability != src)) //To avoid removing the wrong ability + return + user.ranged_ability = null + ranged_ability_user = null + user.client.click_intercept = null + remove_mousepointer(user.client) + active = FALSE + if(msg) + to_chat(user, msg) + update_icon() + +/obj/effect/proc_holder/spell + name = "Spell" + desc = "A wizard spell" + panel = "Spells"//What panel the proc holder needs to go on. + density = 0 + opacity = 0 + + var/school = "evocation" //not relevant at now, but may be important later if there are changes to how spells work. the ones I used for now will probably be changed... maybe spell presets? lacking flexibility but with some other benefit? + + var/charge_type = "recharge" //can be recharge or charges, see charge_max and charge_counter descriptions; can also be based on the holder's vars now, use "holder_var" for that + + var/charge_max = 100 //recharge time in deciseconds if charge_type = "recharge" or starting charges if charge_type = "charges" + var/starts_charged = TRUE //Does this spell start ready to go? + var/charge_counter = 0 //can only cast spells if it equals recharge, ++ each decisecond if charge_type = "recharge" or -- each cast if charge_type = "charges" + var/still_recharging_msg = "The spell is still recharging." + + var/holder_var_type = "bruteloss" //only used if charge_type equals to "holder_var" + var/holder_var_amount = 20 //same. The amount adjusted with the mob's var when the spell is used + + var/ghost = 0 // Skip life check. + var/clothes_req = 1 //see if it requires clothes + var/human_req = 0 //spell can only be cast by humans + var/nonabstract_req = 0 //spell can only be cast by mobs that are physical entities + var/stat_allowed = 0 //see if it requires being conscious/alive, need to set to 1 for ghostpells + var/invocation = "HURP DURP" //what is uttered when the wizard casts the spell + var/invocation_emote_self = null + var/invocation_type = "none" //can be none, whisper and shout + var/range = 7 //the range of the spell; outer radius for aoe spells + var/message = "" //whatever it says to the guy affected by it + var/selection_type = "view" //can be "range" or "view" + var/spell_level = 0 //if a spell can be taken multiple times, this raises + var/level_max = 4 //The max possible level_max is 4 + var/cooldown_min = 0 //This defines what spell quickened four timeshas as a cooldown. Make sure to set this for every spell + + var/overlay = 0 + var/overlay_icon = 'icons/obj/wizard.dmi' + var/overlay_icon_state = "spell" + var/overlay_lifespan = 0 + + var/sparks_spread = 0 + var/sparks_amt = 0 //cropped at 10 + var/smoke_spread = 0 //1 - harmless, 2 - harmful + var/smoke_amt = 0 //cropped at 10 + + var/critfailchance = 0 + var/centcom_cancast = 1 //Whether or not the spell should be allowed on z2 + + var/datum/action/spell_action/action = null + var/action_icon = 'icons/mob/actions/actions.dmi' + var/action_icon_state = "spell_default" + var/action_background_icon_state = "bg_spell" + var/special_availability_check = 0//Whether the spell needs to bypass the action button's IsAvailable() + + var/sound = null //The sound the spell makes when it is cast + +/obj/effect/proc_holder/spell/proc/cast_check(skipcharge = 0, mob/living/user = usr) //checks if the spell can be cast based on its settings; skipcharge is used when an additional cast_check is called inside the spell + if(((!user.mind) || !(src in user.mind.spell_list)) && !(src in user.mob_spell_list)) + to_chat(user, "You shouldn't have this spell! Something's wrong.") + return 0 + + if(ishuman(user)) + var/mob/living/carbon/human/caster = user + if(caster.remoteview_target) + caster.remoteview_target = null + caster.reset_perspective(0) + return 0 + + if(is_admin_level(user.z) && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel + return 0 + + if(!skipcharge) + switch(charge_type) + if("recharge") + if(charge_counter < charge_max) + to_chat(user, still_recharging_msg) + return 0 + if("charges") + if(!charge_counter) + to_chat(user, "[name] has no charges left.") + return 0 + + if(!ghost) + if(user.stat && !stat_allowed) + to_chat(user, "You can't cast this spell while incapacitated.") + return 0 + if(ishuman(user) && (invocation_type == "whisper" || invocation_type == "shout") && user.is_muzzled()) + to_chat(user, "Mmmf mrrfff!") + return 0 + + var/obj/effect/proc_holder/spell/noclothes/clothes_spell = locate() in (user.mob_spell_list | (user.mind ? user.mind.spell_list : list())) + if((ishuman(user) && clothes_req) && !istype(clothes_spell))//clothes check + var/mob/living/carbon/human/H = user + var/obj/item/clothing/robe = H.wear_suit + var/obj/item/clothing/hat = H.head + var/obj/item/clothing/shoes = H.shoes + if(!robe || !hat || !shoes) + to_chat(user, "Your outfit isn't complete, you should put on your robe and wizard hat, as well as sandals.") + return 0 + if(!robe.magical || !hat.magical || !shoes.magical) + to_chat(user, "Your outfit isn't magical enough, you should put on your robe and wizard hat, as well as your sandals.") + return 0 + else if(!ishuman(user)) + if(clothes_req || human_req) + to_chat(user, "This spell can only be cast by humans!") + return 0 + if(nonabstract_req && (isbrain(user) || ispAI(user))) + to_chat(user, "This spell can only be cast by physical beings!") + return 0 + + if(!skipcharge) + switch(charge_type) + if("recharge") + charge_counter = 0 //doesn't start recharging until the targets selecting ends + if("charges") + charge_counter-- //returns the charge if the targets selecting fails + if("holdervar") + adjust_var(user, holder_var_type, holder_var_amount) + + if(action) + action.UpdateButtonIcon() + return 1 + +/obj/effect/proc_holder/spell/proc/invocation(mob/user = usr) //spelling the spell out and setting it on recharge/reducing charges amount + switch(invocation_type) + if("shout") + if(!user.IsVocal()) + user.custom_emote(1, "makes frantic gestures!") + else + if(prob(50))//Auto-mute? Fuck that noise + user.say(invocation) + else + user.say(replacetext(invocation," ","`")) + if("whisper") + if(prob(50)) + user.whisper(invocation) + else + user.whisper(replacetext(invocation," ","`")) + if("emote") + user.visible_message(invocation, invocation_emote_self) //same style as in mob/living/emote.dm + +/obj/effect/proc_holder/spell/proc/playMagSound() + playsound(get_turf(usr), sound,50,1) + +/obj/effect/proc_holder/spell/New() + ..() + action = new(src) + still_recharging_msg = "[name] is still recharging." + if(starts_charged) + charge_counter = charge_max + else + start_recharge() + +/obj/effect/proc_holder/spell/Destroy() + QDEL_NULL(action) + return ..() + +/obj/effect/proc_holder/spell/Click() + if(cast_check()) + choose_targets() + return 1 + +/obj/effect/proc_holder/spell/proc/choose_targets(mob/user = usr) //depends on subtype - /targeted or /aoe_turf + return + +/obj/effect/proc_holder/spell/proc/start_recharge() + if(action) + action.UpdateButtonIcon() + START_PROCESSING(SSfastprocess, src) + +/obj/effect/proc_holder/spell/process() + charge_counter += 2 + if(charge_counter < charge_max) + return + STOP_PROCESSING(SSfastprocess, src) + charge_counter = charge_max + if(action) + action.UpdateButtonIcon() + +/obj/effect/proc_holder/spell/proc/perform(list/targets, recharge = 1, mob/user = usr) //if recharge is started is important for the trigger spells + before_cast(targets) + invocation() + if(user && user.ckey) + user.create_attack_log("[key_name(user)] cast the spell [name].") + spawn(0) + if(charge_type == "recharge" && recharge) + start_recharge() + + if(sound) + playMagSound() + + if(prob(critfailchance)) + critfail(targets) + else + cast(targets, user = user) + after_cast(targets) + if(action) + action.UpdateButtonIcon() + +/obj/effect/proc_holder/spell/proc/before_cast(list/targets) + if(overlay) + for(var/atom/target in targets) + var/location + if(istype(target,/mob/living)) + location = target.loc + else if(istype(target,/turf)) + location = target + var/obj/effect/overlay/spell = new /obj/effect/overlay(location) + spell.icon = overlay_icon + spell.icon_state = overlay_icon_state + spell.anchored = 1 + spell.density = 0 + spawn(overlay_lifespan) + qdel(spell) + +/obj/effect/proc_holder/spell/proc/after_cast(list/targets) + for(var/atom/target in targets) + var/location + if(istype(target,/mob/living)) + location = target.loc + else if(istype(target,/turf)) + location = target + if(istype(target,/mob/living) && message) + to_chat(target, text("[message]")) + if(sparks_spread) + do_sparks(sparks_amt, 0, location) + if(smoke_spread) + if(smoke_spread == 1) + var/datum/effect_system/smoke_spread/smoke = new + smoke.set_up(smoke_amt, 0, location) //no idea what the 0 is + smoke.start() + else if(smoke_spread == 2) + var/datum/effect_system/smoke_spread/bad/smoke = new + smoke.set_up(smoke_amt, 0, location) //no idea what the 0 is + smoke.start() + else if(smoke_spread == 3) + var/datum/effect_system/smoke_spread/sleeping/smoke = new + smoke.set_up(smoke_amt, 0, location) // same here + smoke.start() + +/obj/effect/proc_holder/spell/proc/cast(list/targets, mob/user = usr) + return + +/obj/effect/proc_holder/spell/proc/critfail(list/targets) + return + +/obj/effect/proc_holder/spell/proc/revert_cast(mob/user = usr) //resets recharge or readds a charge + switch(charge_type) + if("recharge") + charge_counter = charge_max + if("charges") + charge_counter++ + if("holdervar") + adjust_var(user, holder_var_type, -holder_var_amount) + if(action) + action.UpdateButtonIcon() + +/obj/effect/proc_holder/spell/proc/updateButtonIcon() + if(action) + action.UpdateButtonIcon() + +/obj/effect/proc_holder/spell/proc/adjust_var(mob/living/target = usr, type, amount) //handles the adjustment of the var when the spell is used. has some hardcoded types + switch(type) + if("bruteloss") + target.adjustBruteLoss(amount) + if("fireloss") + target.adjustFireLoss(amount) + if("toxloss") + target.adjustToxLoss(amount) + if("oxyloss") + target.adjustOxyLoss(amount) + if("stunned") + target.AdjustStunned(amount) + if("weakened") + target.AdjustWeakened(amount) + if("paralysis") + target.AdjustParalysis(amount) + else + target.vars[type] += amount //I bear no responsibility for the runtimes that'll happen if you try to adjust non-numeric or even non-existant vars + return + +/obj/effect/proc_holder/spell/targeted //can mean aoe for mobs (limited/unlimited number) or one target mob + var/max_targets = 1 //leave 0 for unlimited targets in range, 1 for one selectable target in range, more for limited number of casts (can all target one guy, depends on target_ignore_prev) in range + var/target_ignore_prev = 1 //only important if max_targets > 1, affects if the spell can be cast multiple times at one person from one cast + var/include_user = 0 //if it includes usr in the target list + var/random_target = 0 // chooses random viable target instead of asking the caster + var/random_target_priority = TARGET_CLOSEST // if random_target is enabled how it will pick the target + var/humans_only = 0 //for avoiding simple animals and only doing "human" mobs, 0 = all mobs, 1 = humans only + +/obj/effect/proc_holder/spell/aoe_turf //affects all turfs in view or range (depends) + var/inner_radius = -1 //for all your ring spell needs + +/obj/effect/proc_holder/spell/targeted/choose_targets(mob/user = usr) + var/list/targets = list() + + switch(max_targets) + if(0) //unlimited + + if(!humans_only) + for(var/mob/living/target in view_or_range(range, user, selection_type)) + targets += target + else + for(var/mob/living/carbon/human/target in view_or_range(range, user, selection_type)) + targets += target + + if(1) //single target can be picked + if(range < 0) + targets += user + else + var/possible_targets = list() + + if(!humans_only) + for(var/mob/living/M in view_or_range(range, user, selection_type)) + if(!include_user && user == M) + continue + possible_targets += M + else + for(var/mob/living/carbon/human/M in view_or_range(range, user, selection_type)) + if(!include_user && user == M) + continue + possible_targets += M + + //targets += input("Choose the target for the spell.", "Targeting") as mob in possible_targets + //Adds a safety check post-input to make sure those targets are actually in range. + var/mob/M + if(!random_target) + M = input("Choose the target for the spell.", "Targeting") as mob in possible_targets + else + switch(random_target_priority) + if(TARGET_RANDOM) + M = pick(possible_targets) + if(TARGET_CLOSEST) + for(var/mob/living/L in possible_targets) + if(M) + if(get_dist(user,L) < get_dist(user,M)) + if(los_check(user,L)) + M = L + else + if(los_check(user,L)) + M = L + if(M in view_or_range(range, user, selection_type)) targets += M + + else + var/list/possible_targets = list() + if(!humans_only) + for(var/mob/living/target in view_or_range(range, user, selection_type)) + possible_targets += target + else + for(var/mob/living/carbon/human/target in view_or_range(range, user, selection_type)) + possible_targets += target + for(var/i=1,i<=max_targets,i++) + if(!possible_targets.len) + break + if(target_ignore_prev) + var/target = pick(possible_targets) + possible_targets -= target + targets += target + else + targets += pick(possible_targets) + + if(!include_user && (user in targets)) + targets -= user + + if(!targets.len) //doesn't waste the spell + revert_cast(user) + return + + perform(targets, user=user) + + return + +/obj/effect/proc_holder/spell/aoe_turf/choose_targets(mob/user = usr) + var/list/targets = list() + + for(var/turf/target in view_or_range(range,user,selection_type)) + if(!(target in view_or_range(inner_radius,user,selection_type))) + targets += target + + if(!targets.len) //doesn't waste the spell + revert_cast() + return + + perform(targets, user=user) + + return + + +/obj/effect/proc_holder/spell/targeted/proc/los_check(mob/A,mob/B) + //Checks for obstacles from A to B + var/obj/dummy = new(A.loc) + dummy.pass_flags |= PASSTABLE + for(var/turf/turf in getline(A,B)) + for(var/atom/movable/AM in turf) + if(!AM.CanPass(dummy,turf,1)) + qdel(dummy) + return 0 + qdel(dummy) + return 1 + +/obj/effect/proc_holder/spell/proc/can_cast(mob/user = usr) + if(((!user.mind) || !(src in user.mind.spell_list)) && !(src in user.mob_spell_list)) + return 0 + + if(is_admin_level(user.z) && !centcom_cancast) //Certain spells are not allowed on the centcom zlevel + return 0 + + switch(charge_type) + if("recharge") + if(charge_counter < charge_max) + return 0 + if("charges") + if(!charge_counter) + return 0 + + if(user.stat && !stat_allowed) + return 0 + + if(ishuman(user)) + var/mob/living/carbon/human/H = user + + if((invocation_type == "whisper" || invocation_type == "shout") && H.is_muzzled()) + return 0 + + var/clothcheck = locate(/obj/effect/proc_holder/spell/noclothes) in user.mob_spell_list + var/clothcheck2 = user.mind && (locate(/obj/effect/proc_holder/spell/noclothes) in user.mind.spell_list) + if(clothes_req && !clothcheck && !clothcheck2) //clothes check + var/obj/item/clothing/robe = H.wear_suit + var/obj/item/clothing/hat = H.head + var/obj/item/clothing/shoes = H.shoes + if(!robe || !hat || !shoes) + return 0 + if(!robe.magical || !hat.magical || !shoes.magical) + return 0 + else + if(clothes_req || human_req) + return 0 + if(nonabstract_req && (isbrain(user) || ispAI(user))) + return 0 + return 1 diff --git a/code/datums/spells/area_teleport.dm b/code/datums/spells/area_teleport.dm index 4aa7eafbed6..fbde291ae98 100644 --- a/code/datums/spells/area_teleport.dm +++ b/code/datums/spells/area_teleport.dm @@ -1,98 +1,98 @@ -/obj/effect/proc_holder/spell/targeted/area_teleport - name = "Area teleport" - desc = "This spell teleports you to a type of area of your selection." - nonabstract_req = 1 - - var/randomise_selection = 0 //if it lets the usr choose the teleport loc or picks it from the list - var/invocation_area = 1 //if the invocation appends the selected area - - var/sound1 = 'sound/weapons/zapbang.ogg' - var/sound2 = 'sound/weapons/zapbang.ogg' - -/obj/effect/proc_holder/spell/targeted/area_teleport/perform(list/targets, recharge = 1, mob/living/user = usr) - var/thearea = before_cast(targets) - if(!thearea || !cast_check(1)) - revert_cast() - return - invocation(thearea) - spawn(0) - if(charge_type == "recharge" && recharge) - start_recharge() - cast(targets,thearea) - after_cast(targets) - -/obj/effect/proc_holder/spell/targeted/area_teleport/before_cast(list/targets) - var/A = null - - if(!randomise_selection) - A = input("Area to teleport to", "Teleport", A) as null|anything in teleportlocs - else - A = pick(teleportlocs) - - if(!A) - return - - var/area/thearea = teleportlocs[A] - - if(thearea.tele_proof && !istype(thearea, /area/wizard_station)) - to_chat(usr, "A mysterious force disrupts your arcane spell matrix, and you remain where you are.") - return - - return thearea - -/obj/effect/proc_holder/spell/targeted/area_teleport/cast(list/targets,area/thearea,mob/living/user = usr) - playsound(get_turf(user), sound1, 50,1) - for(var/mob/living/target in targets) - var/list/L = list() - for(var/turf/T in get_area_turfs(thearea.type)) - if(!T.density) - var/clear = 1 - for(var/obj/O in T) - if(O.density) - clear = 0 - break - if(clear) - L+=T - - if(!L.len) - to_chat(usr, "The spell matrix was unable to locate a suitable teleport destination for an unknown reason. Sorry.") - return - - if(target && target.buckled) - target.buckled.unbuckle_mob(target, force = TRUE) - - if(target && target.has_buckled_mobs()) - target.unbuckle_all_mobs(force = TRUE) - - var/list/tempL = L - var/attempt = null - var/success = 0 - while(tempL.len) - attempt = pick(tempL) - success = target.Move(attempt) - if(!success) - tempL.Remove(attempt) - else - break - - if(!success) - target.forceMove(pick(L)) - playsound(get_turf(user), sound2, 50,1) - - return - -/obj/effect/proc_holder/spell/targeted/area_teleport/invocation(area/chosenarea = null) - if(!invocation_area || !chosenarea) - ..() - else - switch(invocation_type) - if("shout") - usr.say("[invocation] [uppertext(chosenarea.name)]") - if(usr.gender==MALE) - playsound(usr.loc, pick('sound/misc/null.ogg','sound/misc/null.ogg'), 100, 1) - else - playsound(usr.loc, pick('sound/misc/null.ogg','sound/misc/null.ogg'), 100, 1) - if("whisper") - usr.whisper("[invocation] [uppertext(chosenarea.name)]") - - return \ No newline at end of file +/obj/effect/proc_holder/spell/targeted/area_teleport + name = "Area teleport" + desc = "This spell teleports you to a type of area of your selection." + nonabstract_req = 1 + + var/randomise_selection = 0 //if it lets the usr choose the teleport loc or picks it from the list + var/invocation_area = 1 //if the invocation appends the selected area + + var/sound1 = 'sound/weapons/zapbang.ogg' + var/sound2 = 'sound/weapons/zapbang.ogg' + +/obj/effect/proc_holder/spell/targeted/area_teleport/perform(list/targets, recharge = 1, mob/living/user = usr) + var/thearea = before_cast(targets) + if(!thearea || !cast_check(1)) + revert_cast() + return + invocation(thearea) + spawn(0) + if(charge_type == "recharge" && recharge) + start_recharge() + cast(targets,thearea) + after_cast(targets) + +/obj/effect/proc_holder/spell/targeted/area_teleport/before_cast(list/targets) + var/A = null + + if(!randomise_selection) + A = input("Area to teleport to", "Teleport", A) as null|anything in teleportlocs + else + A = pick(teleportlocs) + + if(!A) + return + + var/area/thearea = teleportlocs[A] + + if(thearea.tele_proof && !istype(thearea, /area/wizard_station)) + to_chat(usr, "A mysterious force disrupts your arcane spell matrix, and you remain where you are.") + return + + return thearea + +/obj/effect/proc_holder/spell/targeted/area_teleport/cast(list/targets,area/thearea,mob/living/user = usr) + playsound(get_turf(user), sound1, 50,1) + for(var/mob/living/target in targets) + var/list/L = list() + for(var/turf/T in get_area_turfs(thearea.type)) + if(!T.density) + var/clear = 1 + for(var/obj/O in T) + if(O.density) + clear = 0 + break + if(clear) + L+=T + + if(!L.len) + to_chat(usr, "The spell matrix was unable to locate a suitable teleport destination for an unknown reason. Sorry.") + return + + if(target && target.buckled) + target.buckled.unbuckle_mob(target, force = TRUE) + + if(target && target.has_buckled_mobs()) + target.unbuckle_all_mobs(force = TRUE) + + var/list/tempL = L + var/attempt = null + var/success = 0 + while(tempL.len) + attempt = pick(tempL) + success = target.Move(attempt) + if(!success) + tempL.Remove(attempt) + else + break + + if(!success) + target.forceMove(pick(L)) + playsound(get_turf(user), sound2, 50,1) + + return + +/obj/effect/proc_holder/spell/targeted/area_teleport/invocation(area/chosenarea = null) + if(!invocation_area || !chosenarea) + ..() + else + switch(invocation_type) + if("shout") + usr.say("[invocation] [uppertext(chosenarea.name)]") + if(usr.gender==MALE) + playsound(usr.loc, pick('sound/misc/null.ogg','sound/misc/null.ogg'), 100, 1) + else + playsound(usr.loc, pick('sound/misc/null.ogg','sound/misc/null.ogg'), 100, 1) + if("whisper") + usr.whisper("[invocation] [uppertext(chosenarea.name)]") + + return diff --git a/code/datums/spells/banana_touch.dm b/code/datums/spells/banana_touch.dm index 4e2f1d44e51..cbfa158531e 100644 --- a/code/datums/spells/banana_touch.dm +++ b/code/datums/spells/banana_touch.dm @@ -61,4 +61,4 @@ genemutcheck(src, COMICBLOCK, null, MUTCHK_FORCED) if(!(iswizard(src) || (mind && mind.special_role == SPECIAL_ROLE_WIZARD_APPRENTICE))) //Mutations are permanent on non-wizards. Can still be removed by genetics fuckery but not mutadone. dna.default_blocks.Add(CLUMSYBLOCK) - dna.default_blocks.Add(COMICBLOCK) \ No newline at end of file + dna.default_blocks.Add(COMICBLOCK) diff --git a/code/datums/spells/conjure.dm b/code/datums/spells/conjure.dm index ebf9fa59fe8..306ec8b9987 100644 --- a/code/datums/spells/conjure.dm +++ b/code/datums/spells/conjure.dm @@ -1,70 +1,70 @@ -/obj/effect/proc_holder/spell/aoe_turf/conjure - name = "Conjure" - desc = "This spell conjures objs of the specified types in range." - - var/list/summon_type = list() //determines what exactly will be summoned - //should be text, like list("/mob/simple_animal/bot/ed209") - - var/summon_lifespan = 0 // 0=permanent, any other time in deciseconds - var/summon_amt = 1 //amount of objects summoned - var/summon_ignore_density = 0 //if set to 1, adds dense tiles to possible spawn places - var/summon_ignore_prev_spawn_points = 0 //if set to 1, each new object is summoned on a new spawn point - - var/list/newVars = list() //vars of the summoned objects will be replaced with those where they meet - //should have format of list("emagged" = 1,"name" = "Wizard's Justicebot"), for example - var/delay = 1//Go Go Gadget Inheritance - - var/cast_sound = 'sound/items/welder.ogg' - -/obj/effect/proc_holder/spell/aoe_turf/conjure/cast(list/targets,mob/living/user = usr) - playsound(get_turf(user), cast_sound, 50,1) - for(var/turf/T in targets) - if(T.density && !summon_ignore_density) - targets -= T - playsound(get_turf(src), cast_sound, 50, 1) - - if(do_after(user, delay, target = user)) - for(var/i=0,iYou can't build things on shuttles!") - break - var/turf/O = spawn_place - var/N = summoned_object_type - O.ChangeTurf(N) - else - var/atom/summoned_object = new summoned_object_type(spawn_place) - - for(var/varName in newVars) - if(varName in summoned_object.vars) - summoned_object.vars[varName] = newVars[varName] - summoned_object.admin_spawned = TRUE - - if(summon_lifespan) - spawn(summon_lifespan) - if(summoned_object) - qdel(summoned_object) - else - switch(charge_type) - if("recharge") - charge_counter = charge_max - 5//So you don't lose charge for a failed spell(Also prevents most over-fill) - if("charges") - charge_counter++//Ditto, just for different spell types - - - return - -/obj/effect/proc_holder/spell/aoe_turf/conjure/summonEdSwarm //test purposes - name = "Dispense Wizard Justice" - desc = "This spell dispenses wizard justice." - - summon_type = list(/mob/living/simple_animal/bot/ed209) - summon_amt = 10 - range = 3 - newVars = list("emagged" = 1,"name" = "Wizard's Justicebot") \ No newline at end of file +/obj/effect/proc_holder/spell/aoe_turf/conjure + name = "Conjure" + desc = "This spell conjures objs of the specified types in range." + + var/list/summon_type = list() //determines what exactly will be summoned + //should be text, like list("/mob/simple_animal/bot/ed209") + + var/summon_lifespan = 0 // 0=permanent, any other time in deciseconds + var/summon_amt = 1 //amount of objects summoned + var/summon_ignore_density = 0 //if set to 1, adds dense tiles to possible spawn places + var/summon_ignore_prev_spawn_points = 0 //if set to 1, each new object is summoned on a new spawn point + + var/list/newVars = list() //vars of the summoned objects will be replaced with those where they meet + //should have format of list("emagged" = 1,"name" = "Wizard's Justicebot"), for example + var/delay = 1//Go Go Gadget Inheritance + + var/cast_sound = 'sound/items/welder.ogg' + +/obj/effect/proc_holder/spell/aoe_turf/conjure/cast(list/targets,mob/living/user = usr) + playsound(get_turf(user), cast_sound, 50,1) + for(var/turf/T in targets) + if(T.density && !summon_ignore_density) + targets -= T + playsound(get_turf(src), cast_sound, 50, 1) + + if(do_after(user, delay, target = user)) + for(var/i=0,iYou can't build things on shuttles!") + break + var/turf/O = spawn_place + var/N = summoned_object_type + O.ChangeTurf(N) + else + var/atom/summoned_object = new summoned_object_type(spawn_place) + + for(var/varName in newVars) + if(varName in summoned_object.vars) + summoned_object.vars[varName] = newVars[varName] + summoned_object.admin_spawned = TRUE + + if(summon_lifespan) + spawn(summon_lifespan) + if(summoned_object) + qdel(summoned_object) + else + switch(charge_type) + if("recharge") + charge_counter = charge_max - 5//So you don't lose charge for a failed spell(Also prevents most over-fill) + if("charges") + charge_counter++//Ditto, just for different spell types + + + return + +/obj/effect/proc_holder/spell/aoe_turf/conjure/summonEdSwarm //test purposes + name = "Dispense Wizard Justice" + desc = "This spell dispenses wizard justice." + + summon_type = list(/mob/living/simple_animal/bot/ed209) + summon_amt = 10 + range = 3 + newVars = list("emagged" = 1,"name" = "Wizard's Justicebot") diff --git a/code/datums/spells/dumbfire.dm b/code/datums/spells/dumbfire.dm index ca8cc43df9c..9efb93a0e7b 100644 --- a/code/datums/spells/dumbfire.dm +++ b/code/datums/spells/dumbfire.dm @@ -1,84 +1,84 @@ -/obj/effect/proc_holder/spell/dumbfire - - var/projectile_type = "" - var/activate_on_collision = 1 - - var/proj_icon = 'icons/obj/projectiles.dmi' - var/proj_icon_state = "spell" - var/proj_name = "a spell projectile" - - var/proj_trail = 0 //if it leaves a trail - var/proj_trail_lifespan = 0 //deciseconds - var/proj_trail_icon = 'icons/obj/wizard.dmi' - var/proj_trail_icon_state = "trail" - - var/proj_type = /obj/effect/proc_holder/spell //IMPORTANT use only subtypes of this - - var/proj_insubstantial = 0 //if it can pass through dense objects or not - var/proj_trigger_range = 1 //the range from target at which the projectile triggers cast(target) - - var/proj_lifespan = 100 //in deciseconds * proj_step_delay - var/proj_step_delay = 1 //lower = faster - -/obj/effect/proc_holder/spell/dumbfire/choose_targets(mob/user = usr) - - var/turf/T = get_turf(usr) - for(var/i = 1; i < range; i++) - var/turf/new_turf = get_step(T, usr.dir) - if(new_turf.density) - break - T = new_turf - perform(list(T), user = user) - -/obj/effect/proc_holder/spell/dumbfire/cast(list/targets, mob/user = usr) - - for(var/turf/target in targets) - spawn(0) - var/obj/effect/proc_holder/spell/targeted/projectile - projectile = new proj_type(user) - projectile.icon = proj_icon - projectile.icon_state = proj_icon_state - projectile.dir = get_dir(projectile, target) - projectile.name = proj_name - - var/current_loc = user.loc - - projectile.loc = current_loc - - for(var/i = 0,i < proj_lifespan,i++) - if(!projectile) - break - - if(proj_insubstantial) - projectile.loc = get_step(projectile, projectile.dir) - else - step(projectile, projectile.dir) - - if(projectile.loc == current_loc || i == proj_lifespan) - projectile.cast(current_loc) - break - - var/mob/living/L = locate(/mob/living) in range(projectile, proj_trigger_range) - user - if(L && L.stat != DEAD) - projectile.cast(L.loc) - break - - if(proj_trail && projectile) - spawn(0) - if(projectile) - var/obj/effect/overlay/trail = new /obj/effect/overlay(projectile.loc) - trail.icon = proj_trail_icon - trail.icon_state = proj_trail_icon_state - trail.density = 0 - spawn(proj_trail_lifespan) - qdel(trail) - - current_loc = projectile.loc - var/matrix/M = new - M.Turn(dir2angle(projectile.dir)) - projectile.transform = M - - sleep(proj_step_delay) - - if(projectile) - qdel(projectile) +/obj/effect/proc_holder/spell/dumbfire + + var/projectile_type = "" + var/activate_on_collision = 1 + + var/proj_icon = 'icons/obj/projectiles.dmi' + var/proj_icon_state = "spell" + var/proj_name = "a spell projectile" + + var/proj_trail = 0 //if it leaves a trail + var/proj_trail_lifespan = 0 //deciseconds + var/proj_trail_icon = 'icons/obj/wizard.dmi' + var/proj_trail_icon_state = "trail" + + var/proj_type = /obj/effect/proc_holder/spell //IMPORTANT use only subtypes of this + + var/proj_insubstantial = 0 //if it can pass through dense objects or not + var/proj_trigger_range = 1 //the range from target at which the projectile triggers cast(target) + + var/proj_lifespan = 100 //in deciseconds * proj_step_delay + var/proj_step_delay = 1 //lower = faster + +/obj/effect/proc_holder/spell/dumbfire/choose_targets(mob/user = usr) + + var/turf/T = get_turf(usr) + for(var/i = 1; i < range; i++) + var/turf/new_turf = get_step(T, usr.dir) + if(new_turf.density) + break + T = new_turf + perform(list(T), user = user) + +/obj/effect/proc_holder/spell/dumbfire/cast(list/targets, mob/user = usr) + + for(var/turf/target in targets) + spawn(0) + var/obj/effect/proc_holder/spell/targeted/projectile + projectile = new proj_type(user) + projectile.icon = proj_icon + projectile.icon_state = proj_icon_state + projectile.dir = get_dir(projectile, target) + projectile.name = proj_name + + var/current_loc = user.loc + + projectile.loc = current_loc + + for(var/i = 0,i < proj_lifespan,i++) + if(!projectile) + break + + if(proj_insubstantial) + projectile.loc = get_step(projectile, projectile.dir) + else + step(projectile, projectile.dir) + + if(projectile.loc == current_loc || i == proj_lifespan) + projectile.cast(current_loc) + break + + var/mob/living/L = locate(/mob/living) in range(projectile, proj_trigger_range) - user + if(L && L.stat != DEAD) + projectile.cast(L.loc) + break + + if(proj_trail && projectile) + spawn(0) + if(projectile) + var/obj/effect/overlay/trail = new /obj/effect/overlay(projectile.loc) + trail.icon = proj_trail_icon + trail.icon_state = proj_trail_icon_state + trail.density = 0 + spawn(proj_trail_lifespan) + qdel(trail) + + current_loc = projectile.loc + var/matrix/M = new + M.Turn(dir2angle(projectile.dir)) + projectile.transform = M + + sleep(proj_step_delay) + + if(projectile) + qdel(projectile) diff --git a/code/datums/spells/emplosion.dm b/code/datums/spells/emplosion.dm index b2f0a228282..cd84432aba8 100644 --- a/code/datums/spells/emplosion.dm +++ b/code/datums/spells/emplosion.dm @@ -1,15 +1,15 @@ -/obj/effect/proc_holder/spell/targeted/emplosion - name = "Emplosion" - desc = "This spell emplodes an area." - - var/emp_heavy = 2 - var/emp_light = 3 - - action_icon_state = "emp" - -/obj/effect/proc_holder/spell/targeted/emplosion/cast(list/targets, mob/user = usr) - - for(var/mob/living/target in targets) - empulse(target.loc, emp_heavy, emp_light, 1) - - return +/obj/effect/proc_holder/spell/targeted/emplosion + name = "Emplosion" + desc = "This spell emplodes an area." + + var/emp_heavy = 2 + var/emp_light = 3 + + action_icon_state = "emp" + +/obj/effect/proc_holder/spell/targeted/emplosion/cast(list/targets, mob/user = usr) + + for(var/mob/living/target in targets) + empulse(target.loc, emp_heavy, emp_light, 1) + + return diff --git a/code/datums/spells/ethereal_jaunt.dm b/code/datums/spells/ethereal_jaunt.dm index 3103d8d5e5d..4f2c88b659d 100644 --- a/code/datums/spells/ethereal_jaunt.dm +++ b/code/datums/spells/ethereal_jaunt.dm @@ -1,104 +1,104 @@ -/obj/effect/proc_holder/spell/targeted/ethereal_jaunt - name = "Ethereal Jaunt" - desc = "This spell creates your ethereal form, temporarily making you invisible and able to pass through walls." - - school = "transmutation" - charge_max = 300 - clothes_req = 1 - invocation = "none" - invocation_type = "none" - range = -1 - cooldown_min = 100 //50 deciseconds reduction per rank - include_user = 1 - nonabstract_req = 1 - centcom_cancast = 0 //Prevent people from getting to centcom - - var/jaunt_duration = 50 //in deciseconds - var/jaunt_in_time = 5 - var/jaunt_in_type = /obj/effect/temp_visual/wizard - var/jaunt_out_type = /obj/effect/temp_visual/wizard/out - - action_icon_state = "jaunt" - -/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/cast(list/targets, mob/user = usr) //magnets, so mostly hardcoded - playsound(get_turf(user), 'sound/magic/ethereal_enter.ogg', 50, 1, -1) - for(var/mob/living/target in targets) - if(!target.can_safely_leave_loc()) // No more brainmobs hopping out of their brains - to_chat(target, "You are somehow too bound to your current location to abandon it.") - continue - INVOKE_ASYNC(src, .proc/do_jaunt, target) - -/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/proc/do_jaunt(mob/living/target) - target.notransform = 1 - var/turf/mobloc = get_turf(target) - var/obj/effect/dummy/spell_jaunt/holder = new /obj/effect/dummy/spell_jaunt(mobloc) - new jaunt_out_type(mobloc, target.dir) - target.ExtinguishMob() - target.forceMove(holder) - target.reset_perspective(holder) - target.notransform = 0 //mob is safely inside holder now, no need for protection. - jaunt_steam(mobloc) - - sleep(jaunt_duration) - - if(target.loc != holder) //mob warped out of the warp - qdel(holder) - return - mobloc = get_turf(target.loc) - jaunt_steam(mobloc) - target.canmove = 0 - holder.reappearing = 1 - playsound(get_turf(target), 'sound/magic/ethereal_exit.ogg', 50, 1, -1) - sleep(25 - jaunt_in_time) - new jaunt_in_type(mobloc, holder.dir) - target.setDir(holder.dir) - sleep(jaunt_in_time) - qdel(holder) - if(!QDELETED(target)) - if(mobloc.density) - for(var/direction in alldirs) - var/turf/T = get_step(mobloc, direction) - if(T) - if(target.Move(T)) - break - target.canmove = 1 - -/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/proc/jaunt_steam(mobloc) - var/datum/effect_system/steam_spread/steam = new /datum/effect_system/steam_spread() - steam.set_up(10, 0, mobloc) - steam.start() - -/obj/effect/dummy/spell_jaunt - name = "water" - icon = 'icons/effects/effects.dmi' - icon_state = "nothing" - var/reappearing = 0 - var/movedelay = 0 - var/movespeed = 2 - density = 0 - anchored = 1 - invisibility = 60 - resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - -/obj/effect/dummy/spell_jaunt/Destroy() - // Eject contents if deleted somehow - for(var/atom/movable/AM in src) - AM.forceMove(get_turf(src)) - return ..() - -/obj/effect/dummy/spell_jaunt/relaymove(mob/user, direction) - if((movedelay > world.time) || reappearing || !direction) - return - var/turf/newLoc = get_step(src,direction) - setDir(direction) - if(!(newLoc.flags & NOJAUNT)) - forceMove(newLoc) - else - to_chat(user, "Some strange aura is blocking the way!") - movedelay = world.time + movespeed - -/obj/effect/dummy/spell_jaunt/ex_act(blah) - return - -/obj/effect/dummy/spell_jaunt/bullet_act(blah) - return \ No newline at end of file +/obj/effect/proc_holder/spell/targeted/ethereal_jaunt + name = "Ethereal Jaunt" + desc = "This spell creates your ethereal form, temporarily making you invisible and able to pass through walls." + + school = "transmutation" + charge_max = 300 + clothes_req = 1 + invocation = "none" + invocation_type = "none" + range = -1 + cooldown_min = 100 //50 deciseconds reduction per rank + include_user = 1 + nonabstract_req = 1 + centcom_cancast = 0 //Prevent people from getting to centcom + + var/jaunt_duration = 50 //in deciseconds + var/jaunt_in_time = 5 + var/jaunt_in_type = /obj/effect/temp_visual/wizard + var/jaunt_out_type = /obj/effect/temp_visual/wizard/out + + action_icon_state = "jaunt" + +/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/cast(list/targets, mob/user = usr) //magnets, so mostly hardcoded + playsound(get_turf(user), 'sound/magic/ethereal_enter.ogg', 50, 1, -1) + for(var/mob/living/target in targets) + if(!target.can_safely_leave_loc()) // No more brainmobs hopping out of their brains + to_chat(target, "You are somehow too bound to your current location to abandon it.") + continue + INVOKE_ASYNC(src, .proc/do_jaunt, target) + +/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/proc/do_jaunt(mob/living/target) + target.notransform = 1 + var/turf/mobloc = get_turf(target) + var/obj/effect/dummy/spell_jaunt/holder = new /obj/effect/dummy/spell_jaunt(mobloc) + new jaunt_out_type(mobloc, target.dir) + target.ExtinguishMob() + target.forceMove(holder) + target.reset_perspective(holder) + target.notransform = 0 //mob is safely inside holder now, no need for protection. + jaunt_steam(mobloc) + + sleep(jaunt_duration) + + if(target.loc != holder) //mob warped out of the warp + qdel(holder) + return + mobloc = get_turf(target.loc) + jaunt_steam(mobloc) + target.canmove = 0 + holder.reappearing = 1 + playsound(get_turf(target), 'sound/magic/ethereal_exit.ogg', 50, 1, -1) + sleep(25 - jaunt_in_time) + new jaunt_in_type(mobloc, holder.dir) + target.setDir(holder.dir) + sleep(jaunt_in_time) + qdel(holder) + if(!QDELETED(target)) + if(mobloc.density) + for(var/direction in alldirs) + var/turf/T = get_step(mobloc, direction) + if(T) + if(target.Move(T)) + break + target.canmove = 1 + +/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/proc/jaunt_steam(mobloc) + var/datum/effect_system/steam_spread/steam = new /datum/effect_system/steam_spread() + steam.set_up(10, 0, mobloc) + steam.start() + +/obj/effect/dummy/spell_jaunt + name = "water" + icon = 'icons/effects/effects.dmi' + icon_state = "nothing" + var/reappearing = 0 + var/movedelay = 0 + var/movespeed = 2 + density = 0 + anchored = 1 + invisibility = 60 + resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + +/obj/effect/dummy/spell_jaunt/Destroy() + // Eject contents if deleted somehow + for(var/atom/movable/AM in src) + AM.forceMove(get_turf(src)) + return ..() + +/obj/effect/dummy/spell_jaunt/relaymove(mob/user, direction) + if((movedelay > world.time) || reappearing || !direction) + return + var/turf/newLoc = get_step(src,direction) + setDir(direction) + if(!(newLoc.flags & NOJAUNT)) + forceMove(newLoc) + else + to_chat(user, "Some strange aura is blocking the way!") + movedelay = world.time + movespeed + +/obj/effect/dummy/spell_jaunt/ex_act(blah) + return + +/obj/effect/dummy/spell_jaunt/bullet_act(blah) + return diff --git a/code/datums/spells/explosion.dm b/code/datums/spells/explosion.dm index 4beeae1206a..22580d9a896 100644 --- a/code/datums/spells/explosion.dm +++ b/code/datums/spells/explosion.dm @@ -1,15 +1,15 @@ -/obj/effect/proc_holder/spell/targeted/explosion - name = "Explosion" - desc = "This spell explodes an area." - - var/ex_severe = 1 - var/ex_heavy = 2 - var/ex_light = 3 - var/ex_flash = 4 - -/obj/effect/proc_holder/spell/targeted/explosion/cast(list/targets, mob/user = usr) - - for(var/mob/living/target in targets) - explosion(target.loc,ex_severe,ex_heavy,ex_light,ex_flash) - - return \ No newline at end of file +/obj/effect/proc_holder/spell/targeted/explosion + name = "Explosion" + desc = "This spell explodes an area." + + var/ex_severe = 1 + var/ex_heavy = 2 + var/ex_light = 3 + var/ex_flash = 4 + +/obj/effect/proc_holder/spell/targeted/explosion/cast(list/targets, mob/user = usr) + + for(var/mob/living/target in targets) + explosion(target.loc,ex_severe,ex_heavy,ex_light,ex_flash) + + return diff --git a/code/datums/spells/fake_gib.dm b/code/datums/spells/fake_gib.dm index 6e2f31ea158..dc187576496 100644 --- a/code/datums/spells/fake_gib.dm +++ b/code/datums/spells/fake_gib.dm @@ -8,4 +8,4 @@ clothes_req = 0 cooldown_min = 200 //100 deciseconds reduction per rank - action_icon_state = "gib" \ No newline at end of file + action_icon_state = "gib" diff --git a/code/datums/spells/genetic.dm b/code/datums/spells/genetic.dm index 697337d85e0..3c2f997e461 100644 --- a/code/datums/spells/genetic.dm +++ b/code/datums/spells/genetic.dm @@ -1,37 +1,37 @@ -/obj/effect/proc_holder/spell/targeted/genetic - name = "Genetic" - desc = "This spell inflicts a set of mutations and disabilities upon the target." - - var/disabilities = 0 //bits - var/list/mutations = list() //mutation strings - var/duration = 100 //deciseconds - /* - Disabilities - 1st bit - ? - 2nd bit - ? - 3rd bit - ? - 4th bit - ? - 5th bit - ? - 6th bit - ? - */ - -/obj/effect/proc_holder/spell/targeted/genetic/cast(list/targets, mob/user = usr) - - for(var/mob/living/target in targets) - for(var/x in mutations) - target.mutations.Add(x) - /* if(x == HULK && ishuman(target)) - target:hulk_time=world.time + duration */ - target.disabilities |= disabilities - target.update_mutations() //update target's mutation overlays - var/mob/living/carbon/human/H = target - if(ishuman(target)) - H.update_body() - spawn(duration) - target.mutations.Remove(mutations) - target.disabilities &= ~disabilities - target.update_mutations() - if(ishuman(target)) - H.update_body() - - return \ No newline at end of file +/obj/effect/proc_holder/spell/targeted/genetic + name = "Genetic" + desc = "This spell inflicts a set of mutations and disabilities upon the target." + + var/disabilities = 0 //bits + var/list/mutations = list() //mutation strings + var/duration = 100 //deciseconds + /* + Disabilities + 1st bit - ? + 2nd bit - ? + 3rd bit - ? + 4th bit - ? + 5th bit - ? + 6th bit - ? + */ + +/obj/effect/proc_holder/spell/targeted/genetic/cast(list/targets, mob/user = usr) + + for(var/mob/living/target in targets) + for(var/x in mutations) + target.mutations.Add(x) + /* if(x == HULK && ishuman(target)) + target:hulk_time=world.time + duration */ + target.disabilities |= disabilities + target.update_mutations() //update target's mutation overlays + var/mob/living/carbon/human/H = target + if(ishuman(target)) + H.update_body() + spawn(duration) + target.mutations.Remove(mutations) + target.disabilities &= ~disabilities + target.update_mutations() + if(ishuman(target)) + H.update_body() + + return diff --git a/code/datums/spells/horsemask.dm b/code/datums/spells/horsemask.dm index f536041e807..2c000a0d232 100644 --- a/code/datums/spells/horsemask.dm +++ b/code/datums/spells/horsemask.dm @@ -1,48 +1,48 @@ -/obj/effect/proc_holder/spell/targeted/horsemask - name = "Curse of the Horseman" - desc = "This spell triggers a curse on a target, causing them to wield an unremovable horse head mask. They will speak like a horse! Any masks they are wearing will be disintegrated. This spell does not require robes." - school = "transmutation" - charge_type = "recharge" - charge_max = 150 - charge_counter = 0 - clothes_req = 0 - stat_allowed = 0 - invocation = "KN'A FTAGHU, PUCK 'BTHNK!" - invocation_type = "shout" - range = 7 - cooldown_min = 30 //30 deciseconds reduction per rank - selection_type = "range" - - action_icon_state = "barn" - sound = 'sound/magic/HorseHead_curse.ogg' - -/obj/effect/proc_holder/spell/targeted/horsemask/cast(list/targets, mob/user = usr) - if(!targets.len) - to_chat(user, "No target found in range.") - return - - var/mob/living/carbon/target = targets[1] - - if(!target) - return - - - if(!ishuman(target)) - to_chat(user, "It'd be stupid to curse [target] with a horse's head!") - return - - if(!(target in oview(range)))//If they are not in overview after selection. - to_chat(user, "They are too far away!") - return - - var/obj/item/clothing/mask/horsehead/magichead = new /obj/item/clothing/mask/horsehead - magichead.flags |= NODROP | DROPDEL //curses! - magichead.flags_inv = null //so you can still see their face - magichead.voicechange = 1 //NEEEEIIGHH - target.visible_message( "[target]'s face lights up in fire, and after the event a horse's head takes its place!", \ - "Your face burns up, and shortly after the fire you realise you have the face of a horse!") - if(!target.unEquip(target.wear_mask)) - qdel(target.wear_mask) - target.equip_to_slot_if_possible(magichead, slot_wear_mask, 1, 1) - - target.flash_eyes() \ No newline at end of file +/obj/effect/proc_holder/spell/targeted/horsemask + name = "Curse of the Horseman" + desc = "This spell triggers a curse on a target, causing them to wield an unremovable horse head mask. They will speak like a horse! Any masks they are wearing will be disintegrated. This spell does not require robes." + school = "transmutation" + charge_type = "recharge" + charge_max = 150 + charge_counter = 0 + clothes_req = 0 + stat_allowed = 0 + invocation = "KN'A FTAGHU, PUCK 'BTHNK!" + invocation_type = "shout" + range = 7 + cooldown_min = 30 //30 deciseconds reduction per rank + selection_type = "range" + + action_icon_state = "barn" + sound = 'sound/magic/HorseHead_curse.ogg' + +/obj/effect/proc_holder/spell/targeted/horsemask/cast(list/targets, mob/user = usr) + if(!targets.len) + to_chat(user, "No target found in range.") + return + + var/mob/living/carbon/target = targets[1] + + if(!target) + return + + + if(!ishuman(target)) + to_chat(user, "It'd be stupid to curse [target] with a horse's head!") + return + + if(!(target in oview(range)))//If they are not in overview after selection. + to_chat(user, "They are too far away!") + return + + var/obj/item/clothing/mask/horsehead/magichead = new /obj/item/clothing/mask/horsehead + magichead.flags |= NODROP | DROPDEL //curses! + magichead.flags_inv = null //so you can still see their face + magichead.voicechange = 1 //NEEEEIIGHH + target.visible_message( "[target]'s face lights up in fire, and after the event a horse's head takes its place!", \ + "Your face burns up, and shortly after the fire you realise you have the face of a horse!") + if(!target.unEquip(target.wear_mask)) + qdel(target.wear_mask) + target.equip_to_slot_if_possible(magichead, slot_wear_mask, 1, 1) + + target.flash_eyes() diff --git a/code/datums/spells/inflict_handler.dm b/code/datums/spells/inflict_handler.dm index 896e399187a..7615a37cfff 100644 --- a/code/datums/spells/inflict_handler.dm +++ b/code/datums/spells/inflict_handler.dm @@ -1,57 +1,57 @@ -/obj/effect/proc_holder/spell/targeted/inflict_handler - name = "Inflict Handler" - desc = "This spell blinds and/or destroys/damages/heals and/or weakens/stuns the target." - - var/amt_weakened = 0 - var/amt_paralysis = 0 - var/amt_stunned = 0 - - //set to negatives for healing - var/amt_dam_fire = 0 - var/amt_dam_brute = 0 - var/amt_dam_oxy = 0 - var/amt_dam_tox = 0 - - var/amt_eye_blind = 0 - var/amt_eye_blurry = 0 - - var/destroys = "none" //can be "none", "gib" or "disintegrate" - - var/summon_type = null //this will put an obj at the target's location - -/obj/effect/proc_holder/spell/targeted/inflict_handler/cast(list/targets, mob/user = usr) - - for(var/mob/living/target in targets) - switch(destroys) - if("gib") - target.gib() - if("disintegrate") - target.dust() - - if(!target) - continue - //damage - if(amt_dam_brute > 0) - if(amt_dam_fire >= 0) - target.take_overall_damage(amt_dam_brute,amt_dam_fire) - else if(amt_dam_fire < 0) - target.take_overall_damage(amt_dam_brute,0) - target.heal_overall_damage(0,amt_dam_fire) - else if(amt_dam_brute < 0) - if(amt_dam_fire > 0) - target.take_overall_damage(0,amt_dam_fire) - target.heal_overall_damage(amt_dam_brute,0) - else if(amt_dam_fire <= 0) - target.heal_overall_damage(amt_dam_brute,amt_dam_fire) - target.adjustToxLoss(amt_dam_tox) - target.adjustOxyLoss(amt_dam_oxy) - //disabling - target.Weaken(amt_weakened) - target.Paralyse(amt_paralysis) - target.Stun(amt_stunned) - - target.AdjustEyeBlind(amt_eye_blind) - target.AdjustEyeBlurry(amt_eye_blurry) - //summoning - if(summon_type) - new summon_type(target.loc, target) +/obj/effect/proc_holder/spell/targeted/inflict_handler + name = "Inflict Handler" + desc = "This spell blinds and/or destroys/damages/heals and/or weakens/stuns the target." + + var/amt_weakened = 0 + var/amt_paralysis = 0 + var/amt_stunned = 0 + + //set to negatives for healing + var/amt_dam_fire = 0 + var/amt_dam_brute = 0 + var/amt_dam_oxy = 0 + var/amt_dam_tox = 0 + + var/amt_eye_blind = 0 + var/amt_eye_blurry = 0 + + var/destroys = "none" //can be "none", "gib" or "disintegrate" + + var/summon_type = null //this will put an obj at the target's location + +/obj/effect/proc_holder/spell/targeted/inflict_handler/cast(list/targets, mob/user = usr) + + for(var/mob/living/target in targets) + switch(destroys) + if("gib") + target.gib() + if("disintegrate") + target.dust() + + if(!target) + continue + //damage + if(amt_dam_brute > 0) + if(amt_dam_fire >= 0) + target.take_overall_damage(amt_dam_brute,amt_dam_fire) + else if(amt_dam_fire < 0) + target.take_overall_damage(amt_dam_brute,0) + target.heal_overall_damage(0,amt_dam_fire) + else if(amt_dam_brute < 0) + if(amt_dam_fire > 0) + target.take_overall_damage(0,amt_dam_fire) + target.heal_overall_damage(amt_dam_brute,0) + else if(amt_dam_fire <= 0) + target.heal_overall_damage(amt_dam_brute,amt_dam_fire) + target.adjustToxLoss(amt_dam_tox) + target.adjustOxyLoss(amt_dam_oxy) + //disabling + target.Weaken(amt_weakened) + target.Paralyse(amt_paralysis) + target.Stun(amt_stunned) + + target.AdjustEyeBlind(amt_eye_blind) + target.AdjustEyeBlurry(amt_eye_blurry) + //summoning + if(summon_type) + new summon_type(target.loc, target) diff --git a/code/datums/spells/knock.dm b/code/datums/spells/knock.dm index b8fafb58bac..95c16978638 100644 --- a/code/datums/spells/knock.dm +++ b/code/datums/spells/knock.dm @@ -1,57 +1,57 @@ -/obj/effect/proc_holder/spell/aoe_turf/knock - name = "Knock" - desc = "This spell opens nearby doors and does not require wizard garb." - - school = "transmutation" - charge_max = 100 - clothes_req = 0 - invocation = "AULIE OXIN FIERA" - invocation_type = "whisper" - range = 3 - cooldown_min = 20 //20 deciseconds reduction per rank - - action_icon_state = "knock" - sound = 'sound/magic/knock.ogg' - -/obj/effect/proc_holder/spell/aoe_turf/knock/cast(list/targets, mob/user = usr) - for(var/turf/T in targets) - for(var/obj/machinery/door/door in T.contents) - spawn(1) - if(istype(door,/obj/machinery/door/airlock/hatch/gamma)) - return - if(istype(door,/obj/machinery/door/airlock)) - var/obj/machinery/door/airlock/A = door - A.unlock(1) //forced because it's magic! - door.open() - for(var/obj/structure/closet/C in T.contents) - spawn(1) - if(istype(C, /obj/structure/closet/secure_closet)) - var/obj/structure/closet/secure_closet/SC = C - SC.locked = 0 - C.open() - - return - -/obj/effect/proc_holder/spell/aoe_turf/knock/greater - name = "Greater Knock" - desc = "On first cast, will remove access restrictions on all airlocks on the station, and announce this spell's use to the station. On any further cast, will open all doors in sight. Cannot be refunded once bought!" - - charge_max = 200 - invocation = "MAIOR OXIN FIERA" - invocation_type = "shout" - range = 7 - level_max = 0 //Cannot be improved, quality of life since can't be refunded - cooldown_min = 200 - var/used = FALSE - -/obj/effect/proc_holder/spell/aoe_turf/knock/greater/cast(list/targets, mob/user = usr) - if(!used) - used = TRUE - for(var/obj/machinery/door/airlock/A in GLOB.airlocks) - if(is_station_level(A.z)) - A.req_access = list() - A.req_one_access = list() - command_announcement.Announce("We have removed all access requirements on your station's airlocks. You can thank us later!", "Greetings!", 'sound/misc/notice2.ogg', , , "Space Wizard Federation Message") - else - ..() - return +/obj/effect/proc_holder/spell/aoe_turf/knock + name = "Knock" + desc = "This spell opens nearby doors and does not require wizard garb." + + school = "transmutation" + charge_max = 100 + clothes_req = 0 + invocation = "AULIE OXIN FIERA" + invocation_type = "whisper" + range = 3 + cooldown_min = 20 //20 deciseconds reduction per rank + + action_icon_state = "knock" + sound = 'sound/magic/knock.ogg' + +/obj/effect/proc_holder/spell/aoe_turf/knock/cast(list/targets, mob/user = usr) + for(var/turf/T in targets) + for(var/obj/machinery/door/door in T.contents) + spawn(1) + if(istype(door,/obj/machinery/door/airlock/hatch/gamma)) + return + if(istype(door,/obj/machinery/door/airlock)) + var/obj/machinery/door/airlock/A = door + A.unlock(1) //forced because it's magic! + door.open() + for(var/obj/structure/closet/C in T.contents) + spawn(1) + if(istype(C, /obj/structure/closet/secure_closet)) + var/obj/structure/closet/secure_closet/SC = C + SC.locked = 0 + C.open() + + return + +/obj/effect/proc_holder/spell/aoe_turf/knock/greater + name = "Greater Knock" + desc = "On first cast, will remove access restrictions on all airlocks on the station, and announce this spell's use to the station. On any further cast, will open all doors in sight. Cannot be refunded once bought!" + + charge_max = 200 + invocation = "MAIOR OXIN FIERA" + invocation_type = "shout" + range = 7 + level_max = 0 //Cannot be improved, quality of life since can't be refunded + cooldown_min = 200 + var/used = FALSE + +/obj/effect/proc_holder/spell/aoe_turf/knock/greater/cast(list/targets, mob/user = usr) + if(!used) + used = TRUE + for(var/obj/machinery/door/airlock/A in GLOB.airlocks) + if(is_station_level(A.z)) + A.req_access = list() + A.req_one_access = list() + command_announcement.Announce("We have removed all access requirements on your station's airlocks. You can thank us later!", "Greetings!", 'sound/misc/notice2.ogg', , , "Space Wizard Federation Message") + else + ..() + return diff --git a/code/datums/spells/lichdom.dm b/code/datums/spells/lichdom.dm index f37d48c0a10..9db46b517db 100644 --- a/code/datums/spells/lichdom.dm +++ b/code/datums/spells/lichdom.dm @@ -130,4 +130,4 @@ H.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe/black(H), slot_wear_suit) H.equip_to_slot_or_del(new /obj/item/clothing/head/wizard/black(H), slot_head) H.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(H), slot_shoes) - H.equip_to_slot_or_del(new /obj/item/clothing/under/color/black(H), slot_w_uniform) \ No newline at end of file + H.equip_to_slot_or_del(new /obj/item/clothing/under/color/black(H), slot_w_uniform) diff --git a/code/datums/spells/lightning.dm b/code/datums/spells/lightning.dm index dcca0cfc919..e7821aed6c2 100644 --- a/code/datums/spells/lightning.dm +++ b/code/datums/spells/lightning.dm @@ -117,4 +117,4 @@ obj/effect/proc_holder/spell/targeted/lightning/proc/Reset(mob/user = usr) return var/mob/living/next = pick(possible_targets) if(next) - Bolt(current,next,bolt_energy,bounces-1,user) // 5 max bounces \ No newline at end of file + Bolt(current,next,bolt_energy,bounces-1,user) // 5 max bounces diff --git a/code/datums/spells/magnet.dm b/code/datums/spells/magnet.dm index 8a6e9c525c4..fea6780085c 100644 --- a/code/datums/spells/magnet.dm +++ b/code/datums/spells/magnet.dm @@ -122,4 +122,4 @@ obj/effect/proc_holder/spell/targeted/magnet/proc/Reset(mob/user = usr) return var/mob/living/next = pick(possible_targets) if(next) - Bolt(current,next,bolt_energy,bounces-1,user) // 5 max bounces \ No newline at end of file + Bolt(current,next,bolt_energy,bounces-1,user) // 5 max bounces diff --git a/code/datums/spells/mime.dm b/code/datums/spells/mime.dm index f11fb5b9572..31066001270 100644 --- a/code/datums/spells/mime.dm +++ b/code/datums/spells/mime.dm @@ -164,4 +164,4 @@ /obj/item/spellbook/oneuse/mime/greaterwall spell = /obj/effect/proc_holder/spell/targeted/forcewall/mime spellname = "Invisible Greater Wall" - desc = "It contains illustrations of the great walls of human history." \ No newline at end of file + desc = "It contains illustrations of the great walls of human history." diff --git a/code/datums/spells/mime_malaise.dm b/code/datums/spells/mime_malaise.dm index a5afd26d916..1c212c62125 100644 --- a/code/datums/spells/mime_malaise.dm +++ b/code/datums/spells/mime_malaise.dm @@ -51,4 +51,4 @@ equip_to_slot_if_possible(new /obj/item/clothing/suit/suspenders/nodrop, slot_wear_suit, TRUE, TRUE) dna.SetSEState(MUTEBLOCK , TRUE, TRUE) genemutcheck(src, MUTEBLOCK , null, MUTCHK_FORCED) - dna.default_blocks.Add(MUTEBLOCK) \ No newline at end of file + dna.default_blocks.Add(MUTEBLOCK) diff --git a/code/datums/spells/mind_transfer.dm b/code/datums/spells/mind_transfer.dm index e45d27ec28e..3d9031a8e03 100644 --- a/code/datums/spells/mind_transfer.dm +++ b/code/datums/spells/mind_transfer.dm @@ -1,82 +1,82 @@ -/obj/effect/proc_holder/spell/targeted/mind_transfer - name = "Mind Transfer" - desc = "This spell allows the user to switch bodies with a target." - - school = "transmutation" - charge_max = 600 - clothes_req = 0 - invocation = "GIN'YU CAPAN" - invocation_type = "whisper" - range = 1 - cooldown_min = 200 //100 deciseconds reduction per rank - var/list/protected_roles = list("Wizard","Changeling","Cultist") //which roles are immune to the spell - var/paralysis_amount_caster = 20 //how much the caster is paralysed for after the spell - var/paralysis_amount_victim = 20 //how much the victim is paralysed for after the spell - action_icon_state = "mindswap" - -/* -Urist: I don't feel like figuring out how you store object spells so I'm leaving this for you to do. -Make sure spells that are removed from spell_list are actually removed and deleted when mind transfering. -Also, you never added distance checking after target is selected. I've went ahead and did that. -*/ -/obj/effect/proc_holder/spell/targeted/mind_transfer/cast(list/targets, mob/user = usr, distanceoverride) - - var/mob/living/target = targets[range] - - if(!(target in oview(range)) && !distanceoverride)//If they are not in overview after selection. Do note that !() is necessary for in to work because ! takes precedence over it. - to_chat(user, "They are too far away!") - return - - if(target.stat == DEAD) - to_chat(user, "You don't particularly want to be dead.") - return - - if(!target.key || !target.mind) - to_chat(user, "[target.p_they(TRUE)] appear[target.p_s()] to be catatonic. Not even magic can affect [target.p_their()] vacant mind.") - return - - if(user.suiciding) - to_chat(user, "You're killing yourself! You can't concentrate enough to do this!") - return - - if(target.mind.special_role in protected_roles) - to_chat(user, "Their mind is resisting your spell.") - return - - if(istype(target, /mob/living/silicon)) - to_chat(user, "You feel this enslaved being is just as dead as its cold, hard exoskeleton.") - return - - var/mob/living/victim = target//The target of the spell whos body will be transferred to. - var/mob/caster = user//The wizard/whomever doing the body transferring. - - //MIND TRANSFER BEGIN - if(caster.mind.special_verbs.len)//If the caster had any special verbs, remove them from the mob verb list. - for(var/V in caster.mind.special_verbs)//Since the caster is using an object spell system, this is mostly moot. - caster.verbs -= V//But a safety nontheless. - - if(victim.mind.special_verbs.len)//Now remove all of the victim's verbs. - for(var/V in victim.mind.special_verbs) - victim.verbs -= V - - var/mob/dead/observer/ghost = victim.ghostize(0) - caster.mind.transfer_to(victim) - - if(victim.mind.special_verbs.len)//To add all the special verbs for the original caster. - for(var/V in caster.mind.special_verbs)//Not too important but could come into play. - caster.verbs += V - - ghost.mind.transfer_to(caster) - if(ghost.key) - GLOB.non_respawnable_keys -= ghost.ckey //ghostizing with an argument of 0 will make them unable to respawn forever, which is bad - caster.key = ghost.key //have to transfer the key since the mind was not active - qdel(ghost) - - if(caster.mind.special_verbs.len)//If they had any special verbs, we add them here. - for(var/V in caster.mind.special_verbs) - caster.verbs += V - //MIND TRANSFER END - - //Here we paralyze both mobs and knock them out for a time. - caster.Paralyse(paralysis_amount_caster) - victim.Paralyse(paralysis_amount_victim) +/obj/effect/proc_holder/spell/targeted/mind_transfer + name = "Mind Transfer" + desc = "This spell allows the user to switch bodies with a target." + + school = "transmutation" + charge_max = 600 + clothes_req = 0 + invocation = "GIN'YU CAPAN" + invocation_type = "whisper" + range = 1 + cooldown_min = 200 //100 deciseconds reduction per rank + var/list/protected_roles = list("Wizard","Changeling","Cultist") //which roles are immune to the spell + var/paralysis_amount_caster = 20 //how much the caster is paralysed for after the spell + var/paralysis_amount_victim = 20 //how much the victim is paralysed for after the spell + action_icon_state = "mindswap" + +/* +Urist: I don't feel like figuring out how you store object spells so I'm leaving this for you to do. +Make sure spells that are removed from spell_list are actually removed and deleted when mind transfering. +Also, you never added distance checking after target is selected. I've went ahead and did that. +*/ +/obj/effect/proc_holder/spell/targeted/mind_transfer/cast(list/targets, mob/user = usr, distanceoverride) + + var/mob/living/target = targets[range] + + if(!(target in oview(range)) && !distanceoverride)//If they are not in overview after selection. Do note that !() is necessary for in to work because ! takes precedence over it. + to_chat(user, "They are too far away!") + return + + if(target.stat == DEAD) + to_chat(user, "You don't particularly want to be dead.") + return + + if(!target.key || !target.mind) + to_chat(user, "[target.p_they(TRUE)] appear[target.p_s()] to be catatonic. Not even magic can affect [target.p_their()] vacant mind.") + return + + if(user.suiciding) + to_chat(user, "You're killing yourself! You can't concentrate enough to do this!") + return + + if(target.mind.special_role in protected_roles) + to_chat(user, "Their mind is resisting your spell.") + return + + if(istype(target, /mob/living/silicon)) + to_chat(user, "You feel this enslaved being is just as dead as its cold, hard exoskeleton.") + return + + var/mob/living/victim = target//The target of the spell whos body will be transferred to. + var/mob/caster = user//The wizard/whomever doing the body transferring. + + //MIND TRANSFER BEGIN + if(caster.mind.special_verbs.len)//If the caster had any special verbs, remove them from the mob verb list. + for(var/V in caster.mind.special_verbs)//Since the caster is using an object spell system, this is mostly moot. + caster.verbs -= V//But a safety nontheless. + + if(victim.mind.special_verbs.len)//Now remove all of the victim's verbs. + for(var/V in victim.mind.special_verbs) + victim.verbs -= V + + var/mob/dead/observer/ghost = victim.ghostize(0) + caster.mind.transfer_to(victim) + + if(victim.mind.special_verbs.len)//To add all the special verbs for the original caster. + for(var/V in caster.mind.special_verbs)//Not too important but could come into play. + caster.verbs += V + + ghost.mind.transfer_to(caster) + if(ghost.key) + GLOB.non_respawnable_keys -= ghost.ckey //ghostizing with an argument of 0 will make them unable to respawn forever, which is bad + caster.key = ghost.key //have to transfer the key since the mind was not active + qdel(ghost) + + if(caster.mind.special_verbs.len)//If they had any special verbs, we add them here. + for(var/V in caster.mind.special_verbs) + caster.verbs += V + //MIND TRANSFER END + + //Here we paralyze both mobs and knock them out for a time. + caster.Paralyse(paralysis_amount_caster) + victim.Paralyse(paralysis_amount_victim) diff --git a/code/datums/spells/projectile.dm b/code/datums/spells/projectile.dm index 258925653d8..955aa6e3537 100644 --- a/code/datums/spells/projectile.dm +++ b/code/datums/spells/projectile.dm @@ -1,86 +1,86 @@ -/obj/effect/proc_holder/spell/targeted/projectile - name = "Projectile" - desc = "This spell summons projectiles which try to hit the targets." - - var/proj_icon = 'icons/obj/projectiles.dmi' - var/proj_icon_state = "spell" - var/proj_name = "a spell projectile" - - var/proj_trail = 0 //if it leaves a trail - var/proj_trail_lifespan = 0 //deciseconds - var/proj_trail_icon = 'icons/obj/wizard.dmi' - var/proj_trail_icon_state = "trail" - - var/proj_type = "/obj/effect/proc_holder/spell/targeted" //IMPORTANT use only subtypes of this - - var/proj_lingering = 0 //if it lingers or disappears upon hitting an obstacle - var/proj_homing = 1 //if it follows the target - var/proj_insubstantial = 0 //if it can pass through dense objects or not - var/proj_trigger_range = 0 //the range from target at which the projectile triggers cast(target) - - var/proj_lifespan = 15 //in deciseconds * proj_step_delay - var/proj_step_delay = 1 //lower = faster - -/obj/effect/proc_holder/spell/targeted/projectile/cast(list/targets, mob/user = usr) - - for(var/mob/living/target in targets) - spawn(0) - var/obj/effect/proc_holder/spell/targeted/projectile - if(istext(proj_type)) - var/projectile_type = text2path(proj_type) - projectile = new projectile_type(user) - if(istype(proj_type,/obj/effect/proc_holder/spell)) - projectile = new /obj/effect/proc_holder/spell/targeted/trigger(user) - projectile:linked_spells += proj_type - projectile.icon = proj_icon - projectile.icon_state = proj_icon_state - projectile.dir = get_dir(target,projectile) - projectile.name = proj_name - - var/current_loc = user.loc - - projectile.loc = current_loc - - for(var/i = 0,i < proj_lifespan,i++) - if(!projectile) - break - - if(proj_homing) - if(proj_insubstantial) - projectile.dir = get_dir(projectile,target) - projectile.loc = get_step_to(projectile,target) - else - step_to(projectile,target) - else - if(proj_insubstantial) - projectile.loc = get_step(projectile,dir) - else - step(projectile,dir) - - if(!projectile) // step and step_to sleeps so we'll have to check again. - break - - if(!proj_lingering && projectile.loc == current_loc) //if it didn't move since last time - qdel(projectile) - break - - if(proj_trail && projectile) - spawn(0) - if(projectile) - var/obj/effect/overlay/trail = new /obj/effect/overlay(projectile.loc) - trail.icon = proj_trail_icon - trail.icon_state = proj_trail_icon_state - trail.density = 0 - spawn(proj_trail_lifespan) - qdel(trail) - - if(projectile.loc in range(target.loc,proj_trigger_range)) - projectile.perform(list(target), user = user) - break - - current_loc = projectile.loc - - sleep(proj_step_delay) - - if(projectile) - qdel(projectile) \ No newline at end of file +/obj/effect/proc_holder/spell/targeted/projectile + name = "Projectile" + desc = "This spell summons projectiles which try to hit the targets." + + var/proj_icon = 'icons/obj/projectiles.dmi' + var/proj_icon_state = "spell" + var/proj_name = "a spell projectile" + + var/proj_trail = 0 //if it leaves a trail + var/proj_trail_lifespan = 0 //deciseconds + var/proj_trail_icon = 'icons/obj/wizard.dmi' + var/proj_trail_icon_state = "trail" + + var/proj_type = "/obj/effect/proc_holder/spell/targeted" //IMPORTANT use only subtypes of this + + var/proj_lingering = 0 //if it lingers or disappears upon hitting an obstacle + var/proj_homing = 1 //if it follows the target + var/proj_insubstantial = 0 //if it can pass through dense objects or not + var/proj_trigger_range = 0 //the range from target at which the projectile triggers cast(target) + + var/proj_lifespan = 15 //in deciseconds * proj_step_delay + var/proj_step_delay = 1 //lower = faster + +/obj/effect/proc_holder/spell/targeted/projectile/cast(list/targets, mob/user = usr) + + for(var/mob/living/target in targets) + spawn(0) + var/obj/effect/proc_holder/spell/targeted/projectile + if(istext(proj_type)) + var/projectile_type = text2path(proj_type) + projectile = new projectile_type(user) + if(istype(proj_type,/obj/effect/proc_holder/spell)) + projectile = new /obj/effect/proc_holder/spell/targeted/trigger(user) + projectile:linked_spells += proj_type + projectile.icon = proj_icon + projectile.icon_state = proj_icon_state + projectile.dir = get_dir(target,projectile) + projectile.name = proj_name + + var/current_loc = user.loc + + projectile.loc = current_loc + + for(var/i = 0,i < proj_lifespan,i++) + if(!projectile) + break + + if(proj_homing) + if(proj_insubstantial) + projectile.dir = get_dir(projectile,target) + projectile.loc = get_step_to(projectile,target) + else + step_to(projectile,target) + else + if(proj_insubstantial) + projectile.loc = get_step(projectile,dir) + else + step(projectile,dir) + + if(!projectile) // step and step_to sleeps so we'll have to check again. + break + + if(!proj_lingering && projectile.loc == current_loc) //if it didn't move since last time + qdel(projectile) + break + + if(proj_trail && projectile) + spawn(0) + if(projectile) + var/obj/effect/overlay/trail = new /obj/effect/overlay(projectile.loc) + trail.icon = proj_trail_icon + trail.icon_state = proj_trail_icon_state + trail.density = 0 + spawn(proj_trail_lifespan) + qdel(trail) + + if(projectile.loc in range(target.loc,proj_trigger_range)) + projectile.perform(list(target), user = user) + break + + current_loc = projectile.loc + + sleep(proj_step_delay) + + if(projectile) + qdel(projectile) diff --git a/code/datums/spells/shapeshift.dm b/code/datums/spells/shapeshift.dm index ff303ce750b..7b72b5a42c4 100644 --- a/code/datums/spells/shapeshift.dm +++ b/code/datums/spells/shapeshift.dm @@ -111,4 +111,4 @@ shapeshift_type = /mob/living/simple_animal/hostile/hellhound/greater current_shapes = list(/mob/living/simple_animal/hostile/hellhound/greater) current_casters = list() - possible_shapes = list(/mob/living/simple_animal/hostile/hellhound/greater) \ No newline at end of file + possible_shapes = list(/mob/living/simple_animal/hostile/hellhound/greater) diff --git a/code/datums/spells/summonitem.dm b/code/datums/spells/summonitem.dm index 8dbaf5afa77..aaef2229856 100644 --- a/code/datums/spells/summonitem.dm +++ b/code/datums/spells/summonitem.dm @@ -52,73 +52,71 @@ marked_item = null else //Getting previously marked item - var/obj/item_to_retrive = marked_item + var/obj/item_to_retrieve = marked_item var/infinite_recursion = 0 //I don't want to know how someone could put something inside itself but these are wizards so let's be safe - while(!isturf(item_to_retrive.loc) && infinite_recursion < 10) //if it's in something you get the whole thing. - if(ismob(item_to_retrive.loc)) //If its on someone, properly drop it - var/mob/M = item_to_retrive.loc + while(!isturf(item_to_retrieve.loc) && infinite_recursion < 10) //if it's in something you get the whole thing. + if(ismob(item_to_retrieve.loc)) //If its on someone, properly drop it + var/mob/M = item_to_retrieve.loc - if(issilicon(M)) //Items in silicons warp the whole silicon - M.loc.visible_message("[M] suddenly disappears!") - M.loc = target.loc + if(issilicon(M) || !M.unEquip(item_to_retrieve)) //Items in silicons warp the whole silicon + M.visible_message("[M] suddenly disappears!", "A force suddenly pulls you away!") + M.forceMove(target.loc) M.loc.visible_message("[M] suddenly appears!") - item_to_retrive = null + item_to_retrieve = null break - M.unEquip(item_to_retrive) - if(ishuman(M)) //Edge case housekeeping var/mob/living/carbon/human/C = M - /*if(C.internal_bodyparts_by_name && item_to_retrive in C.internal_bodyparts_by_name ) //This won't work, as we use organ datums instead of objects. --DZD - C.internal_bodyparts_by_name -= item_to_retrive + /*if(C.internal_bodyparts_by_name && item_to_retrieve in C.internal_bodyparts_by_name ) //This won't work, as we use organ datums instead of objects. --DZD + C.internal_bodyparts_by_name -= item_to_retrieve if(istype(marked_item, /obj/item/brain)) //If this code ever runs I will be happy var/obj/item/brain/B = new /obj/item/brain(target.loc) B.transfer_identity(C) C.death() add_attack_logs(target, C, "Magically debrained INTENT: [uppertext(target.a_intent)]")*/ - if(C.stomach_contents && item_to_retrive in C.stomach_contents) - C.stomach_contents -= item_to_retrive + if(C.stomach_contents && item_to_retrieve in C.stomach_contents) + C.stomach_contents -= item_to_retrieve for(var/X in C.bodyparts) var/obj/item/organ/external/part = X - if(item_to_retrive in part.embedded_objects) - part.embedded_objects -= item_to_retrive - to_chat(C, "The [item_to_retrive] that was embedded in your [part] has mysteriously vanished. How fortunate!") + if(item_to_retrieve in part.embedded_objects) + part.embedded_objects -= item_to_retrieve + to_chat(C, "The [item_to_retrieve] that was embedded in your [part] has mysteriously vanished. How fortunate!") if(!C.has_embedded_objects()) C.clear_alert("embeddedobject") break else - if(istype(item_to_retrive.loc,/obj/machinery/portable_atmospherics/)) //Edge cases for moved machinery - var/obj/machinery/portable_atmospherics/P = item_to_retrive.loc + if(istype(item_to_retrieve.loc,/obj/machinery/portable_atmospherics/)) //Edge cases for moved machinery + var/obj/machinery/portable_atmospherics/P = item_to_retrieve.loc P.disconnect() P.update_icon() - if(istype(item_to_retrive.loc, /obj/structure/disposalholder) || istype(item_to_retrive.loc, /obj/machinery/disposal))//fixes the breaking of disposals. No more bluespace connected disposal bins! + if(istype(item_to_retrieve.loc, /obj/structure/disposalholder) || istype(item_to_retrieve.loc, /obj/machinery/disposal))//fixes the breaking of disposals. No more bluespace connected disposal bins! break - item_to_retrive = item_to_retrive.loc + item_to_retrieve = item_to_retrieve.loc infinite_recursion += 1 - if(!item_to_retrive) + if(!item_to_retrieve) return - item_to_retrive.loc.visible_message("The [item_to_retrive.name] suddenly disappears!") + item_to_retrieve.loc.visible_message("The [item_to_retrieve.name] suddenly disappears!") if(target.hand) //left active hand - if(!target.equip_to_slot_if_possible(item_to_retrive, slot_l_hand, 0, 1, 1)) - if(!target.equip_to_slot_if_possible(item_to_retrive, slot_r_hand, 0, 1, 1)) + if(!target.equip_to_slot_if_possible(item_to_retrieve, slot_l_hand, 0, 1, 1)) + if(!target.equip_to_slot_if_possible(item_to_retrieve, slot_r_hand, 0, 1, 1)) butterfingers = 1 else //right active hand - if(!target.equip_to_slot_if_possible(item_to_retrive, slot_r_hand, 0, 1, 1)) - if(!target.equip_to_slot_if_possible(item_to_retrive, slot_l_hand, 0, 1, 1)) + if(!target.equip_to_slot_if_possible(item_to_retrieve, slot_r_hand, 0, 1, 1)) + if(!target.equip_to_slot_if_possible(item_to_retrieve, slot_l_hand, 0, 1, 1)) butterfingers = 1 if(butterfingers) - item_to_retrive.loc = target.loc - item_to_retrive.loc.visible_message("The [item_to_retrive.name] suddenly appears!") + item_to_retrieve.loc = target.loc + item_to_retrieve.loc.visible_message("The [item_to_retrieve.name] suddenly appears!") playsound(get_turf(target),'sound/magic/summonitems_generic.ogg',50,1) else - item_to_retrive.loc.visible_message("The [item_to_retrive.name] suddenly appears in [target]'s hand!") + item_to_retrieve.loc.visible_message("The [item_to_retrieve.name] suddenly appears in [target]'s hand!") playsound(get_turf(target),'sound/magic/summonitems_generic.ogg',50,1) if(message) diff --git a/code/datums/spells/trigger.dm b/code/datums/spells/trigger.dm index bba1f22cb1a..bc768cef57b 100644 --- a/code/datums/spells/trigger.dm +++ b/code/datums/spells/trigger.dm @@ -1,29 +1,29 @@ -/obj/effect/proc_holder/spell/targeted/trigger - name = "Trigger" - desc = "This spell triggers another spell or a few." - - var/list/linked_spells = list() //those are just referenced by the trigger spell and are unaffected by it directly - var/list/starting_spells = list() //those are added on New() to contents from default spells and are deleted when the trigger spell is deleted to prevent memory leaks - -/obj/effect/proc_holder/spell/targeted/trigger/New() - ..() - - for(var/spell in starting_spells) - var/spell_to_add = text2path(spell) - new spell_to_add(src) //should result in adding to contents, needs testing - -/obj/effect/proc_holder/spell/targeted/trigger/Destroy() - for(var/spell in contents) - qdel(spell) - linked_spells = null - starting_spells = null - return ..() - -/obj/effect/proc_holder/spell/targeted/trigger/cast(list/targets, mob/user = usr) - for(var/mob/living/target in targets) - for(var/obj/effect/proc_holder/spell/spell in contents) - spell.perform(list(target), 0, user = user) - for(var/obj/effect/proc_holder/spell/spell in linked_spells) - spell.perform(list(target), 0, user = user) - - return \ No newline at end of file +/obj/effect/proc_holder/spell/targeted/trigger + name = "Trigger" + desc = "This spell triggers another spell or a few." + + var/list/linked_spells = list() //those are just referenced by the trigger spell and are unaffected by it directly + var/list/starting_spells = list() //those are added on New() to contents from default spells and are deleted when the trigger spell is deleted to prevent memory leaks + +/obj/effect/proc_holder/spell/targeted/trigger/New() + ..() + + for(var/spell in starting_spells) + var/spell_to_add = text2path(spell) + new spell_to_add(src) //should result in adding to contents, needs testing + +/obj/effect/proc_holder/spell/targeted/trigger/Destroy() + for(var/spell in contents) + qdel(spell) + linked_spells = null + starting_spells = null + return ..() + +/obj/effect/proc_holder/spell/targeted/trigger/cast(list/targets, mob/user = usr) + for(var/mob/living/target in targets) + for(var/obj/effect/proc_holder/spell/spell in contents) + spell.perform(list(target), 0, user = user) + for(var/obj/effect/proc_holder/spell/spell in linked_spells) + spell.perform(list(target), 0, user = user) + + return diff --git a/code/datums/spells/turf_teleport.dm b/code/datums/spells/turf_teleport.dm index 6ecc9d693d8..adaa4afc94c 100644 --- a/code/datums/spells/turf_teleport.dm +++ b/code/datums/spells/turf_teleport.dm @@ -1,40 +1,40 @@ -/obj/effect/proc_holder/spell/targeted/turf_teleport - name = "Turf Teleport" - desc = "This spell teleports the target to the turf in range." - nonabstract_req = 1 - - var/inner_tele_radius = 1 - var/outer_tele_radius = 2 - - var/include_space = 0 //whether it includes space tiles in possible teleport locations - var/include_dense = 0 //whether it includes dense tiles in possible teleport locations - - var/sound1 = 'sound/weapons/zapbang.ogg' - var/sound2 = 'sound/weapons/zapbang.ogg' - -/obj/effect/proc_holder/spell/targeted/turf_teleport/cast(list/targets,mob/living/user = usr) - playsound(get_turf(user), sound1, 50,1) - for(var/mob/living/target in targets) - var/list/turfs = new/list() - for(var/turf/T in range(target,outer_tele_radius)) - if(T in range(target,inner_tele_radius)) continue - if(istype(T,/turf/space) && !include_space) continue - if(T.density && !include_dense) continue - if(T.x>world.maxx-outer_tele_radius || T.xworld.maxy-outer_tele_radius || T.yworld.maxx-outer_tele_radius || T.xworld.maxy-outer_tele_radius || T.yYou're slammed into the floor by a mystical force!") - else - new sparkle_path(get_turf(AM), get_dir(user, AM)) //created sparkles will disappear on their own - if(isliving(AM)) - var/mob/living/M = AM - M.Weaken(stun_amt) - to_chat(M, "You're thrown back by a mystical force!") - spawn(0) - AM.throw_at(throwtarget, ((Clamp((maxthrow - (Clamp(distfromcaster - 2, 0, distfromcaster))), 3, maxthrow))), 1)//So stuff gets tossed around at the same time. - -/obj/effect/proc_holder/spell/targeted/sacred_flame - name = "Sacred Flame" - desc = "Makes everyone around you more flammable, and lights yourself on fire." - charge_max = 60 - clothes_req = 0 - invocation = "FI'RAN DADISKO" - invocation_type = "shout" - max_targets = 0 - range = 6 - include_user = 1 - selection_type = "view" - action_icon_state = "sacredflame" - sound = 'sound/magic/fireball.ogg' - -/obj/effect/proc_holder/spell/targeted/sacred_flame/cast(list/targets, mob/user = usr) - for(var/mob/living/L in targets) - L.adjust_fire_stacks(20) - if(isliving(user)) - var/mob/living/U = user - U.IgniteMob() +/obj/effect/proc_holder/spell/targeted/projectile/magic_missile + name = "Magic Missile" + desc = "This spell fires several, slow moving, magic projectiles at nearby targets." + + school = "evocation" + charge_max = 200 + clothes_req = 1 + invocation = "FORTI GY AMA" + invocation_type = "shout" + range = 7 + cooldown_min = 60 //35 deciseconds reduction per rank + + max_targets = 0 + + proj_icon_state = "magicm" + proj_name = "a magic missile" + proj_lingering = 1 + proj_type = "/obj/effect/proc_holder/spell/targeted/inflict_handler/magic_missile" + + proj_lifespan = 20 + proj_step_delay = 5 + + proj_trail = 1 + proj_trail_lifespan = 5 + proj_trail_icon_state = "magicmd" + + action_icon_state = "magicm" + + sound = 'sound/magic/magic_missile.ogg' + +/obj/effect/proc_holder/spell/targeted/inflict_handler/magic_missile + amt_weakened = 3 + sound = 'sound/magic/mm_hit.ogg' + + +/obj/effect/proc_holder/spell/targeted/projectile/honk_missile + name = "Honk Missile" + desc = "This spell fires several, slow moving, magic bikehorns at nearby targets." + + school = "evocation" + charge_max = 60 + clothes_req = 0 + invocation = "HONK GY AMA" + invocation_type = "shout" + range = 7 + cooldown_min = 60 //35 deciseconds reduction per rank + + max_targets = 0 + + proj_icon = 'icons/obj/items.dmi' + proj_icon_state = "bike_horn" + proj_name = "A bike horn" + proj_lingering = 1 + proj_type = "/obj/effect/proc_holder/spell/targeted/inflict_handler/honk_missile" + + proj_lifespan = 20 + proj_step_delay = 5 + + proj_trail_icon = 'icons/obj/items.dmi' + proj_trail = 1 + proj_trail_lifespan = 5 + proj_trail_icon_state = "bike_horn" + + action_icon_state = "magicm" + + sound = 'sound/items/bikehorn.ogg' + +/obj/effect/proc_holder/spell/targeted/inflict_handler/honk_missile + amt_weakened = 3 + sound = 'sound/items/bikehorn.ogg' + +/obj/effect/proc_holder/spell/noclothes + name = "No Clothes" + desc = "This always-on spell allows you to cast magic without your garments." + action_icon_state = "no_clothes" + +/obj/effect/proc_holder/spell/targeted/genetic/mutate + name = "Mutate" + desc = "This spell causes you to turn into a hulk and gain laser vision for a short while." + + school = "transmutation" + charge_max = 400 + clothes_req = 1 + invocation = "BIRUZ BENNAR" + invocation_type = "shout" + message = "You feel strong! You feel a pressure building behind your eyes!" + range = -1 + include_user = 1 + centcom_cancast = 0 + + mutations = list(LASER, HULK) + duration = 300 + cooldown_min = 300 //25 deciseconds reduction per rank + + action_icon_state = "mutate" + sound = 'sound/magic/mutate.ogg' + +/obj/effect/proc_holder/spell/targeted/genetic/mutate/cast(list/targets, mob/user = usr) + for(var/mob/living/target in targets) + target.dna.SetSEState(HULKBLOCK, 1) + genemutcheck(target, HULKBLOCK, null, MUTCHK_FORCED) + spawn(duration) + target.dna.SetSEState(HULKBLOCK, 0) + genemutcheck(target, HULKBLOCK, null, MUTCHK_FORCED) + ..() + +/obj/effect/proc_holder/spell/targeted/smoke + name = "Smoke" + desc = "This spell spawns a cloud of choking smoke at your location and does not require wizard garb." + + school = "conjuration" + charge_max = 120 + clothes_req = 0 + invocation = "none" + invocation_type = "none" + range = -1 + include_user = 1 + cooldown_min = 20 //25 deciseconds reduction per rank + + smoke_spread = 2 + smoke_amt = 10 + + action_icon_state = "smoke" + +/obj/effect/proc_holder/spell/targeted/emplosion/disable_tech + name = "Disable Tech" + desc = "This spell disables all weapons, cameras and most other technology in range." + charge_max = 400 + clothes_req = 1 + invocation = "NEC CANTIO" + invocation_type = "shout" + range = -1 + include_user = 1 + cooldown_min = 200 //50 deciseconds reduction per rank + + emp_heavy = 6 + emp_light = 10 + + sound = 'sound/magic/disable_tech.ogg' + +/obj/effect/proc_holder/spell/targeted/turf_teleport/blink + name = "Blink" + desc = "This spell randomly teleports you a short distance." + + school = "abjuration" + charge_max = 20 + clothes_req = 1 + invocation = "none" + invocation_type = "none" + range = -1 + include_user = 1 + cooldown_min = 5 //4 deciseconds reduction per rank + + + smoke_spread = 1 + smoke_amt = 1 + + inner_tele_radius = 0 + outer_tele_radius = 6 + + centcom_cancast = 0 //prevent people from getting to centcom + + action_icon_state = "blink" + + sound1 = 'sound/magic/blink.ogg' + sound2 = 'sound/magic/blink.ogg' + +/obj/effect/proc_holder/spell/targeted/area_teleport/teleport + name = "Teleport" + desc = "This spell teleports you to a type of area of your selection." + + school = "abjuration" + charge_max = 600 + clothes_req = 1 + invocation = "SCYAR NILA" + invocation_type = "shout" + range = -1 + include_user = 1 + cooldown_min = 200 //100 deciseconds reduction per rank + + smoke_spread = 1 + smoke_amt = 5 + + action_icon_state = "spell_teleport" + + sound1 = 'sound/magic/teleport_diss.ogg' + sound2 = 'sound/magic/teleport_app.ogg' + +/obj/effect/proc_holder/spell/targeted/forcewall + name = "Force Wall" + desc = "This spell creates a small unbreakable wall that only you can pass through, and does not need wizard garb. Lasts 30 seconds." + + school = "transmutation" + charge_max = 100 + clothes_req = FALSE + invocation = "TARCOL MINTI ZHERI" + invocation_type = "whisper" + sound = 'sound/magic/forcewall.ogg' + action_icon_state = "shield" + range = -1 + include_user = TRUE + cooldown_min = 50 //12 deciseconds reduction per rank + var/wall_type = /obj/effect/forcefield/wizard + var/large = FALSE + +/obj/effect/proc_holder/spell/targeted/forcewall/cast(list/targets, mob/user = usr) + new wall_type(get_turf(user), user) + if(large) //Extra THICK + if(user.dir == SOUTH || user.dir == NORTH) + new wall_type(get_step(user, EAST), user) + new wall_type(get_step(user, WEST), user) + else + new wall_type(get_step(user, NORTH), user) + new wall_type(get_step(user, SOUTH), user) + +/obj/effect/proc_holder/spell/targeted/forcewall/greater + name = "Greater Force Wall" + desc = "Create a larger magical barrier that only you can pass through, but requires wizard garb. Lasts 30 seconds." + + clothes_req = TRUE + invocation = "TARCOL GRANDI ZHERI" + invocation_type = "shout" + large = TRUE + +/obj/effect/proc_holder/spell/aoe_turf/conjure/timestop + name = "Stop Time" + desc = "This spell stops time for everyone except for you, allowing you to move freely while your enemies and even projectiles are frozen." + charge_max = 500 + clothes_req = 1 + invocation = "TOKI WO TOMARE" + invocation_type = "shout" + range = 0 + cooldown_min = 100 + summon_amt = 1 + action_icon_state = "time" + + summon_type = list(/obj/effect/timestop/wizard) + +/obj/effect/proc_holder/spell/aoe_turf/conjure/carp + name = "Summon Carp" + desc = "This spell conjures a simple carp." + + school = "conjuration" + charge_max = 1200 + clothes_req = 1 + invocation = "NOUK FHUNMM SACP RISSKA" + invocation_type = "shout" + range = 1 + + summon_type = list(/mob/living/simple_animal/hostile/carp) + + cast_sound = 'sound/magic/summon_karp.ogg' + +/obj/effect/proc_holder/spell/aoe_turf/conjure/construct + name = "Artificer" + desc = "This spell conjures a construct which may be controlled by Shades" + + school = "conjuration" + charge_max = 600 + clothes_req = 0 + invocation = "none" + invocation_type = "none" + range = 0 + + summon_type = list(/obj/structure/constructshell) + + action_icon_state = "artificer" + cast_sound = 'sound/magic/summonitems_generic.ogg' + +/obj/effect/proc_holder/spell/aoe_turf/conjure/creature + name = "Summon Creature Swarm" + desc = "This spell tears the fabric of reality, allowing horrific daemons to spill forth" + + school = "conjuration" + charge_max = 1200 + clothes_req = 0 + invocation = "IA IA" + invocation_type = "shout" + summon_amt = 10 + range = 3 + + summon_type = list(/mob/living/simple_animal/hostile/creature) + cast_sound = 'sound/magic/summonitems_generic.ogg' + +/obj/effect/proc_holder/spell/targeted/trigger/blind + name = "Blind" + desc = "This spell temporarily blinds a single person and does not require wizard garb." + + school = "transmutation" + charge_max = 300 + clothes_req = 0 + invocation = "STI KALY" + invocation_type = "whisper" + message = "Your eyes cry out in pain!" + cooldown_min = 50 //12 deciseconds reduction per rank + + starting_spells = list("/obj/effect/proc_holder/spell/targeted/inflict_handler/blind","/obj/effect/proc_holder/spell/targeted/genetic/blind") + + action_icon_state = "blind" + +/obj/effect/proc_holder/spell/targeted/inflict_handler/blind + amt_eye_blind = 10 + amt_eye_blurry = 20 + sound = 'sound/magic/blind.ogg' + +/obj/effect/proc_holder/spell/targeted/genetic/blind + disabilities = BLIND + duration = 300 + sound = 'sound/magic/blind.ogg' + +/obj/effect/proc_holder/spell/fireball + name = "Fireball" + desc = "This spell fires a fireball at a target and does not require wizard garb." + + school = "evocation" + charge_max = 60 + clothes_req = 0 + invocation = "ONI SOMA" + invocation_type = "shout" + range = 20 + cooldown_min = 20 //10 deciseconds reduction per rank + var/fireball_type = /obj/item/projectile/magic/fireball + action_icon_state = "fireball0" + sound = 'sound/magic/fireball.ogg' + + active = FALSE + +/obj/effect/proc_holder/spell/fireball/Click() + var/mob/living/user = usr + if(!istype(user)) + return + + var/msg + + if(!can_cast(user)) + msg = "You can no longer cast Fireball." + remove_ranged_ability(user, msg) + return + + if(active) + msg = "You extinguish your fireball...for now." + remove_ranged_ability(user, msg) + else + msg = "Your prepare to cast your fireball spell! Left-click to cast at a target!" + add_ranged_ability(user, msg) + +/obj/effect/proc_holder/spell/fireball/update_icon() + if(!action) + return + action.button_icon_state = "fireball[active]" + action.UpdateButtonIcon() + +/obj/effect/proc_holder/spell/fireball/InterceptClickOn(mob/living/user, params, atom/target) + if(..()) + return FALSE + + if(!cast_check(0, user)) + remove_ranged_ability(user) + return FALSE + + var/list/targets = list(target) + perform(targets, user = user) + + return TRUE + +/obj/effect/proc_holder/spell/fireball/cast(list/targets, mob/living/user = usr) + var/target = targets[1] //There is only ever one target for fireball + var/turf/T = user.loc + var/turf/U = get_step(user, user.dir) // Get the tile infront of the move, based on their direction + if(!isturf(U) || !isturf(T)) + return 0 + + var/obj/item/projectile/magic/fireball/FB = new fireball_type(user.loc) + FB.current = get_turf(user) + FB.preparePixelProjectile(target, get_turf(target), user) + FB.fire() + user.newtonian_move(get_dir(U, T)) + remove_ranged_ability(user) + + return 1 + +/obj/effect/proc_holder/spell/aoe_turf/repulse + name = "Repulse" + desc = "This spell throws everything around the user away." + charge_max = 400 + clothes_req = TRUE + invocation = "GITTAH WEIGH" + invocation_type = "shout" + range = 5 + cooldown_min = 150 + selection_type = "view" + sound = 'sound/magic/repulse.ogg' + var/maxthrow = 5 + var/sparkle_path = /obj/effect/temp_visual/gravpush + action_icon_state = "repulse" + +/obj/effect/proc_holder/spell/aoe_turf/repulse/cast(list/targets, mob/user = usr, stun_amt = 2) + var/list/thrownatoms = list() + var/atom/throwtarget + var/distfromcaster + playMagSound() + for(var/turf/T in targets) //Done this way so things don't get thrown all around hilariously. + for(var/atom/movable/AM in T) + thrownatoms += AM + + for(var/am in thrownatoms) + var/atom/movable/AM = am + if(AM == user || AM.anchored) + continue + + throwtarget = get_edge_target_turf(user, get_dir(user, get_step_away(AM, user))) + distfromcaster = get_dist(user, AM) + if(distfromcaster == 0) + if(isliving(AM)) + var/mob/living/M = AM + M.Weaken(5) + M.adjustBruteLoss(5) + to_chat(M, "You're slammed into the floor by a mystical force!") + else + new sparkle_path(get_turf(AM), get_dir(user, AM)) //created sparkles will disappear on their own + if(isliving(AM)) + var/mob/living/M = AM + M.Weaken(stun_amt) + to_chat(M, "You're thrown back by a mystical force!") + spawn(0) + AM.throw_at(throwtarget, ((Clamp((maxthrow - (Clamp(distfromcaster - 2, 0, distfromcaster))), 3, maxthrow))), 1)//So stuff gets tossed around at the same time. + +/obj/effect/proc_holder/spell/targeted/sacred_flame + name = "Sacred Flame" + desc = "Makes everyone around you more flammable, and lights yourself on fire." + charge_max = 60 + clothes_req = 0 + invocation = "FI'RAN DADISKO" + invocation_type = "shout" + max_targets = 0 + range = 6 + include_user = 1 + selection_type = "view" + action_icon_state = "sacredflame" + sound = 'sound/magic/fireball.ogg' + +/obj/effect/proc_holder/spell/targeted/sacred_flame/cast(list/targets, mob/user = usr) + for(var/mob/living/L in targets) + L.adjust_fire_stacks(20) + if(isliving(user)) + var/mob/living/U = user + U.IgniteMob() diff --git a/code/datums/statclick.dm b/code/datums/statclick.dm index 11a3ed41ce3..2c83e57fb45 100644 --- a/code/datums/statclick.dm +++ b/code/datums/statclick.dm @@ -30,4 +30,4 @@ INITIALIZE_IMMEDIATE(/obj/effect/statclick) class = "unknown" usr.client.debug_variables(target) - message_admins("Admin [key_name_admin(usr)] is debugging the [target] [class].") \ No newline at end of file + message_admins("Admin [key_name_admin(usr)] is debugging the [target] [class].") diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm index c4ba55552fa..479694de4d7 100644 --- a/code/datums/status_effects/buffs.dm +++ b/code/datums/status_effects/buffs.dm @@ -229,4 +229,4 @@ return TRUE /datum/status_effect/regenerative_core/on_remove() - owner.status_flags &= ~IGNORESLOWDOWN \ No newline at end of file + owner.status_flags &= ~IGNORESLOWDOWN diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm index 3ce68f85250..db6aef6ea0e 100644 --- a/code/datums/status_effects/debuffs.dm +++ b/code/datums/status_effects/debuffs.dm @@ -112,4 +112,4 @@ playsound(T, "desceration", 200, 1, -1) owner.adjustBruteLoss(bleed_damage) else - new /obj/effect/temp_visual/bleed(get_turf(owner)) \ No newline at end of file + new /obj/effect/temp_visual/bleed(get_turf(owner)) diff --git a/code/datums/status_effects/gas.dm b/code/datums/status_effects/gas.dm index bdd80e7fc32..00417993144 100644 --- a/code/datums/status_effects/gas.dm +++ b/code/datums/status_effects/gas.dm @@ -43,4 +43,4 @@ /datum/status_effect/freon/watcher duration = 8 - can_melt = FALSE \ No newline at end of file + can_melt = FALSE diff --git a/code/datums/status_effects/neutral.dm b/code/datums/status_effects/neutral.dm index b2365ad4e1c..7a86a00ccd7 100644 --- a/code/datums/status_effects/neutral.dm +++ b/code/datums/status_effects/neutral.dm @@ -44,4 +44,4 @@ alert_type = null /datum/status_effect/high_five/on_remove() - owner.visible_message("[owner] was left hanging....") \ No newline at end of file + owner.visible_message("[owner] was left hanging....") diff --git a/code/datums/status_effects/status_effect.dm b/code/datums/status_effects/status_effect.dm index 8ed41c559b5..896f4f999a9 100644 --- a/code/datums/status_effects/status_effect.dm +++ b/code/datums/status_effects/status_effect.dm @@ -234,4 +234,4 @@ owner.cut_overlay(status_overlay) owner.underlays -= status_underlay QDEL_NULL(status_overlay) - return ..() \ No newline at end of file + return ..() diff --git a/code/datums/supplypacks.dm b/code/datums/supplypacks.dm index 8e20ebb7a93..f17e9840e4f 100644 --- a/code/datums/supplypacks.dm +++ b/code/datums/supplypacks.dm @@ -6,37 +6,37 @@ //NEW NOTE: Do NOT set the price of any crates below 7 points. Doing so allows infinite points. // Supply Groups -var/const/supply_emergency = 1 -var/const/supply_security = 2 -var/const/supply_engineer = 3 -var/const/supply_medical = 4 -var/const/supply_science = 5 -var/const/supply_organic = 6 -var/const/supply_materials = 7 -var/const/supply_misc = 8 -var/const/supply_vend = 9 +#define SUPPLY_EMERGENCY 1 +#define SUPPLY_SECURITY 2 +#define SUPPLY_ENGINEER 3 +#define SUPPLY_MEDICAL 4 +#define SUPPLY_SCIENCE 5 +#define SUPPLY_ORGANIC 6 +#define SUPPLY_MATERIALS 7 +#define SUPPLY_MISC 8 +#define SUPPLY_VEND 9 -var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engineer,supply_medical,supply_science,supply_organic,supply_materials,supply_misc,supply_vend) +var/list/all_supply_groups = list(SUPPLY_EMERGENCY,SUPPLY_SECURITY,SUPPLY_ENGINEER,SUPPLY_MEDICAL,SUPPLY_SCIENCE,SUPPLY_ORGANIC,SUPPLY_MATERIALS,SUPPLY_MISC,SUPPLY_VEND) /proc/get_supply_group_name(var/cat) switch(cat) - if(1) + if(SUPPLY_EMERGENCY) return "Emergency" - if(2) + if(SUPPLY_SECURITY) return "Security" - if(3) + if(SUPPLY_ENGINEER) return "Engineering" - if(4) + if(SUPPLY_MEDICAL) return "Medical" - if(5) + if(SUPPLY_SCIENCE) return "Science" - if(6) + if(SUPPLY_ORGANIC) return "Food and Livestock" - if(7) + if(SUPPLY_MATERIALS) return "Raw Materials" - if(8) + if(SUPPLY_MISC) return "Miscellaneous" - if(9) + if(SUPPLY_VEND) return "Vending" @@ -51,7 +51,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine var/access = null var/hidden = 0 var/contraband = 0 - var/group = supply_misc + var/group = SUPPLY_MISC var/list/announce_beacons = list() // Particular beacons that we'll notify the relevant department when we reach var/special = FALSE //Event/Station Goals/Admin enabled packs var/special_enabled = FALSE @@ -74,7 +74,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine /datum/supply_packs/emergency // Section header - use these to set default supply group and crate type for sections name = "HEADER" // Use "HEADER" to denote section headers, this is needed for the supply computers to filter them containertype = /obj/structure/closet/crate/internals - group = supply_emergency + group = SUPPLY_EMERGENCY /datum/supply_packs/emergency/evac @@ -98,7 +98,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 35 containertype = /obj/structure/closet/crate/internals containername = "emergency crate" - group = supply_emergency + group = SUPPLY_EMERGENCY /datum/supply_packs/emergency/internals name = "Internals Crate" @@ -135,7 +135,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 10 containertype = /obj/structure/closet/crate/secure containername = "firefighting watertank crate" - access = access_atmospherics + access = ACCESS_ATMOSPHERICS /datum/supply_packs/emergency/weedcontrol name = "Weed Control Crate" @@ -146,7 +146,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 15 containertype = /obj/structure/closet/crate/secure/hydrosec containername = "weed control crate" - access = access_hydroponics + access = ACCESS_HYDROPONICS announce_beacons = list("Hydroponics" = list("Hydroponics")) /datum/supply_packs/emergency/voxsupport @@ -172,7 +172,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 20 containertype = /obj/structure/closet/crate/secure/plasma containername = "plasmaman life support supplies crate" - access = access_eva + access = ACCESS_EVA /datum/supply_packs/emergency/specialops name = "Special Ops Supplies" @@ -202,8 +202,8 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine /datum/supply_packs/security name = "HEADER" containertype = /obj/structure/closet/crate/secure/gear - access = access_security - group = supply_security + access = ACCESS_SECURITY + group = SUPPLY_SECURITY announce_beacons = list("Security" = list("Head of Security's Desk", "Warden", "Security")) @@ -299,7 +299,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine /datum/supply_packs/security/armory name = "HEADER" containertype = /obj/structure/closet/crate/secure/weapon - access = access_armory + access = ACCESS_ARMORY announce_beacons = list("Security" = list("Warden", "Head of Security's Desk")) ///// Armor: Specialist @@ -442,7 +442,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 15 // its a fecking flamethrower and some plasma, why the shit did this cost so much before!? containertype = /obj/structure/closet/crate/secure/plasma containername = "incendiary weapons crate" - access = access_heads + access = ACCESS_HEADS /datum/supply_packs/security/armory/wt550 name = "WT-550 Auto Rifle Crate" @@ -534,7 +534,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine /datum/supply_packs/engineering name = "HEADER" - group = supply_engineer + group = SUPPLY_ENGINEER announce_beacons = list("Engineering" = list("Engineering", "Chief Engineer's Desk")) containertype = /obj/structure/closet/crate/engineering @@ -635,7 +635,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 10 containertype = /obj/structure/closet/crate/secure containername = "emitter crate" - access = access_ce + access = ACCESS_CE containertype = /obj/structure/closet/crate/secure/engineering /datum/supply_packs/engineering/engine/field_gen @@ -703,7 +703,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 30 containertype = /obj/structure/closet/crate/secure containername = "space suit crate" - access = access_eva + access = ACCESS_EVA /datum/supply_packs/engineering/inflatable name = "Inflatable barriers Crate" @@ -719,7 +719,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 50 //So cargo thinks twice before killing themselves with it containertype = /obj/structure/closet/crate/secure containername = "supermatter shard crate" - access = access_ce + access = ACCESS_CE /datum/supply_packs/engineering/engine/teg name = "Thermo-Electric Generator Crate" @@ -730,7 +730,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 25 containertype = /obj/structure/closet/crate/secure containername = "thermo-electric generator crate" - access = access_ce + access = ACCESS_CE announce_beacons = list("Engineering" = list("Chief Engineer's Desk", "Atmospherics")) /datum/supply_packs/engineering/conveyor @@ -753,7 +753,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine /datum/supply_packs/medical name = "HEADER" containertype = /obj/structure/closet/crate/medical - group = supply_medical + group = SUPPLY_MEDICAL announce_beacons = list("Medbay" = list("Medbay", "Chief Medical Officer's Desk"), "Security" = list("Brig Medbay")) @@ -858,7 +858,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 25 containertype = /obj/structure/closet/crate/secure/plasma containername = "virus crate" - access = access_cmo + access = ACCESS_CMO announce_beacons = list("Medbay" = list("Virology", "Chief Medical Officer's Desk")) /datum/supply_packs/medical/vending @@ -888,7 +888,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 30 containertype = /obj/structure/closet/crate/secure containername = "IV drip crate" - access = access_medical + access = ACCESS_MEDICAL /datum/supply_packs/medical/surgery name = "Surgery Crate" @@ -906,7 +906,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 25 containertype = /obj/structure/closet/crate/secure containername = "surgery crate" - access = access_medical + access = ACCESS_MEDICAL ////////////////////////////////////////////////////////////////////////////// @@ -915,7 +915,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine /datum/supply_packs/science name = "HEADER" - group = supply_science + group = SUPPLY_SCIENCE announce_beacons = list("Research Division" = list("Science", "Research Director's Desk")) containertype = /obj/structure/closet/crate/sci @@ -931,7 +931,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 10 containertype = /obj/structure/closet/crate/secure/scisec containername = "robotics assembly crate" - access = access_robotics + access = ACCESS_ROBOTICS announce_beacons = list("Research Division" = list("Robotics", "Research Director's Desk")) @@ -967,8 +967,8 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 10 containertype = /obj/structure/closet/crate/secure/plasma containername = "plasma assembly crate" - access = access_tox_storage - group = supply_science + access = ACCESS_TOX_STORAGE + group = SUPPLY_SCIENCE /datum/supply_packs/science/shieldwalls name = "Shield Generators Crate" @@ -979,7 +979,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 20 containertype = /obj/structure/closet/crate/secure/scisec containername = "shield generators crate" - access = access_teleporter + access = ACCESS_TELEPORTER /datum/supply_packs/science/modularpc name = "Deluxe Silicate Selections restocking unit" @@ -994,7 +994,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 60 containertype = /obj/structure/closet/crate/secure/scisec containername = "tank transfer valves crate" - access = access_rd + access = ACCESS_RD /datum/supply_packs/science/prototype name = "Machine Prototype Crate" @@ -1002,7 +1002,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 80 containertype = /obj/structure/closet/crate/secure/scisec containername = "machine prototype crate" - access = access_research + access = ACCESS_RESEARCH /datum/supply_packs/science/oil name = "Oil Tank Crate" @@ -1018,7 +1018,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine /datum/supply_packs/organic name = "HEADER" - group = supply_organic + group = SUPPLY_ORGANIC containertype = /obj/structure/closet/crate/freezer @@ -1210,7 +1210,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 10 containertype = /obj/structure/closet/crate/secure containername = "hydroponics watertank crate" - access = access_hydroponics + access = ACCESS_HYDROPONICS announce_beacons = list("Hydroponics" = list("Hydroponics")) /datum/supply_packs/organic/hydroponics/seeds @@ -1296,7 +1296,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine /datum/supply_packs/materials name = "HEADER" - group = supply_materials + group = SUPPLY_MATERIALS announce_beacons = list("Engineering" = list("Engineering", "Chief Engineer's Desk", "Atmospherics")) @@ -1363,7 +1363,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine /datum/supply_packs/misc name = "HEADER" - group = supply_misc + group = SUPPLY_MISC /datum/supply_packs/misc/mule name = "MULEbot Crate" @@ -1421,7 +1421,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine /datum/supply_packs/misc/minerkit name = "Shaft Miner Starter Kit" cost = 30 - access = access_qm + access = ACCESS_QM contains = list(/obj/item/storage/backpack/duffel/mining_conscript) containertype = /obj/structure/closet/crate/secure containername = "shaft miner starter kit" @@ -1550,7 +1550,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 10 containertype = /obj/structure/closet/crate/secure containername = "janitor watertank crate" - access = access_janitor + access = ACCESS_JANITOR /datum/supply_packs/misc/janitor/lightbulbs name = "Replacement Lights Crate" @@ -1588,7 +1588,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine cost = 10 containertype = /obj/structure/closet/crate/secure containername = "standard costumes" - access = access_theatre + access = ACCESS_THEATRE /datum/supply_packs/misc/wizard name = "Wizard Costume Crate" @@ -1855,7 +1855,7 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine /datum/supply_packs/vending name = "HEADER" - group = supply_vend + group = SUPPLY_VEND /datum/supply_packs/vending/autodrobe name = "Autodrobe Supply Crate" diff --git a/code/datums/weather/weather_types/ash_storm.dm b/code/datums/weather/weather_types/ash_storm.dm index dbc75cb9bf0..57d8c11f80b 100644 --- a/code/datums/weather/weather_types/ash_storm.dm +++ b/code/datums/weather/weather_types/ash_storm.dm @@ -105,4 +105,4 @@ aesthetic = TRUE - probability = 10 \ No newline at end of file + probability = 10 diff --git a/code/datums/weather/weather_types/radiation_storm.dm b/code/datums/weather/weather_types/radiation_storm.dm index 08b30ecef48..85b8d3fd2af 100644 --- a/code/datums/weather/weather_types/radiation_storm.dm +++ b/code/datums/weather/weather_types/radiation_storm.dm @@ -61,4 +61,4 @@ post_status("alert", "radiation") else post_status("blank") - post_status("shuttle") \ No newline at end of file + post_status("shuttle") diff --git a/code/datums/wires/airlock.dm b/code/datums/wires/airlock.dm index 3e9f488bb29..e3d90b8d075 100644 --- a/code/datums/wires/airlock.dm +++ b/code/datums/wires/airlock.dm @@ -9,16 +9,16 @@ window_x = 410 window_y = 570 -var/const/AIRLOCK_WIRE_IDSCAN = 1 -var/const/AIRLOCK_WIRE_MAIN_POWER1 = 2 -var/const/AIRLOCK_WIRE_DOOR_BOLTS = 4 -var/const/AIRLOCK_WIRE_BACKUP_POWER1 = 8 -var/const/AIRLOCK_WIRE_OPEN_DOOR = 16 -var/const/AIRLOCK_WIRE_AI_CONTROL = 32 -var/const/AIRLOCK_WIRE_ELECTRIFY = 64 -var/const/AIRLOCK_WIRE_SAFETY = 128 -var/const/AIRLOCK_WIRE_SPEED = 256 -var/const/AIRLOCK_WIRE_LIGHT = 512 +#define AIRLOCK_WIRE_IDSCAN 1 +#define AIRLOCK_WIRE_MAIN_POWER1 2 +#define AIRLOCK_WIRE_DOOR_BOLTS 4 +#define AIRLOCK_WIRE_BACKUP_POWER1 8 +#define AIRLOCK_WIRE_OPEN_DOOR 16 +#define AIRLOCK_WIRE_AI_CONTROL 32 +#define AIRLOCK_WIRE_ELECTRIFY 64 +#define AIRLOCK_WIRE_SAFETY 128 +#define AIRLOCK_WIRE_SPEED 256 +#define AIRLOCK_WIRE_LIGHT 512 /datum/wires/airlock/GetWireName(index) switch(index) diff --git a/code/datums/wires/alarm.dm b/code/datums/wires/alarm.dm index f412e96b5ee..f6b49b0dd99 100644 --- a/code/datums/wires/alarm.dm +++ b/code/datums/wires/alarm.dm @@ -3,11 +3,11 @@ holder_type = /obj/machinery/alarm wire_count = 5 -var/const/AALARM_WIRE_IDSCAN = 1 -var/const/AALARM_WIRE_POWER = 2 -var/const/AALARM_WIRE_SYPHON = 4 -var/const/AALARM_WIRE_AI_CONTROL = 8 -var/const/AALARM_WIRE_AALARM = 16 +#define AALARM_WIRE_IDSCAN 1 +#define AALARM_WIRE_POWER 2 +#define AALARM_WIRE_SYPHON 4 +#define AALARM_WIRE_AI_CONTROL 8 +#define AALARM_WIRE_AALARM 16 /datum/wires/alarm/GetWireName(index) switch(index) @@ -54,8 +54,7 @@ var/const/AALARM_WIRE_AALARM = 16 // to_chat(world, "Power wire cut") if(AALARM_WIRE_AI_CONTROL) - if(A.aidisabled == !mended) - A.aidisabled = mended + A.aidisabled = !mended // to_chat(world, "AI Control Wire Cut") if(AALARM_WIRE_SYPHON) diff --git a/code/datums/wires/apc.dm b/code/datums/wires/apc.dm index df014edd4f4..b1076c8e29b 100644 --- a/code/datums/wires/apc.dm +++ b/code/datums/wires/apc.dm @@ -2,10 +2,10 @@ holder_type = /obj/machinery/power/apc wire_count = 4 -var/const/APC_WIRE_IDSCAN = 1 -var/const/APC_WIRE_MAIN_POWER1 = 2 -var/const/APC_WIRE_MAIN_POWER2 = 4 -var/const/APC_WIRE_AI_CONTROL = 8 +#define APC_WIRE_IDSCAN 1 +#define APC_WIRE_MAIN_POWER1 2 +#define APC_WIRE_MAIN_POWER2 4 +#define APC_WIRE_AI_CONTROL 8 /datum/wires/apc/GetWireName(index) switch(index) @@ -91,4 +91,4 @@ var/const/APC_WIRE_AI_CONTROL = 8 else if(A.aidisabled == 1) A.aidisabled = 0 - ..() \ No newline at end of file + ..() diff --git a/code/datums/wires/autolathe.dm b/code/datums/wires/autolathe.dm index a901695c880..c1413abd0cc 100644 --- a/code/datums/wires/autolathe.dm +++ b/code/datums/wires/autolathe.dm @@ -2,9 +2,9 @@ holder_type = /obj/machinery/autolathe wire_count = 10 -var/const/AUTOLATHE_HACK_WIRE = 1 -var/const/AUTOLATHE_SHOCK_WIRE = 2 -var/const/AUTOLATHE_DISABLE_WIRE = 4 +#define AUTOLATHE_HACK_WIRE 1 +#define AUTOLATHE_SHOCK_WIRE 2 +#define AUTOLATHE_DISABLE_WIRE 4 /datum/wires/autolathe/GetWireName(index) switch(index) @@ -71,4 +71,4 @@ var/const/AUTOLATHE_DISABLE_WIRE = 4 /datum/wires/autolathe/proc/updateUIs() SSnanoui.update_uis(src) if(holder) - SSnanoui.update_uis(holder) \ No newline at end of file + SSnanoui.update_uis(holder) diff --git a/code/datums/wires/camera.dm b/code/datums/wires/camera.dm index 609dce59e84..a6b1549fbe1 100644 --- a/code/datums/wires/camera.dm +++ b/code/datums/wires/camera.dm @@ -17,8 +17,8 @@ return FALSE return TRUE -var/const/CAMERA_WIRE_FOCUS = 1 -var/const/CAMERA_WIRE_POWER = 2 +#define CAMERA_WIRE_FOCUS 1 +#define CAMERA_WIRE_POWER 2 /datum/wires/camera/GetWireName(index) switch(index) @@ -58,4 +58,4 @@ var/const/CAMERA_WIRE_POWER = 2 if(IsIndexCut(CAMERA_WIRE_POWER) && IsIndexCut(CAMERA_WIRE_FOCUS)) return TRUE else - return FALSE \ No newline at end of file + return FALSE diff --git a/code/datums/wires/explosive.dm b/code/datums/wires/explosive.dm index 045923c7dcf..47fd627d597 100644 --- a/code/datums/wires/explosive.dm +++ b/code/datums/wires/explosive.dm @@ -1,7 +1,7 @@ /datum/wires/explosive wire_count = 1 -var/const/WIRE_EXPLODE = 1 +#define WIRE_EXPLODE 1 /datum/wires/explosive/GetWireName(index) switch(index) diff --git a/code/datums/wires/mulebot.dm b/code/datums/wires/mulebot.dm index cbc92497f33..988b520854f 100644 --- a/code/datums/wires/mulebot.dm +++ b/code/datums/wires/mulebot.dm @@ -4,43 +4,43 @@ wire_count = 10 window_x = 410 -var/const/WIRE_POWER1 = 1 // power connections -var/const/WIRE_POWER2 = 2 -var/const/WIRE_AVOIDANCE = 4 // mob avoidance -var/const/WIRE_LOADCHECK = 8 // load checking (non-crate) -var/const/WIRE_MOTOR1 = 16 // motor wires -var/const/WIRE_MOTOR2 = 32 // -var/const/WIRE_REMOTE_RX = 64 // remote recv functions -var/const/WIRE_REMOTE_TX = 128 // remote trans status -var/const/WIRE_BEACON_RX = 256 // beacon ping recv +#define MULEBOT_WIRE_POWER1 1 // power connections +#define MULEBOT_WIRE_POWER2 2 +#define MULEBOT_WIRE_AVOIDANCE 4 // mob avoidance +#define MULEBOT_WIRE_LOADCHECK 8 // load checking (non-crate) +#define MULEBOT_WIRE_MOTOR1 16 // motor wires +#define MULEBOT_WIRE_MOTOR2 32 // +#define MULEBOT_WIRE_REMOTE_RX 64 // remote recv functions +#define MULEBOT_WIRE_REMOTE_TX 128 // remote trans status +#define MULEBOT_WIRE_BEACON_RX 256 // beacon ping recv /datum/wires/mulebot/GetWireName(index) switch(index) - if(WIRE_POWER1) + if(MULEBOT_WIRE_POWER1) return "Primary Power" - if(WIRE_POWER2) + if(MULEBOT_WIRE_POWER2) return "Secondary Power" - if(WIRE_AVOIDANCE) + if(MULEBOT_WIRE_AVOIDANCE) return "Mob Avoidance" - if(WIRE_LOADCHECK) + if(MULEBOT_WIRE_LOADCHECK) return "Load Checking" - if(WIRE_MOTOR1) + if(MULEBOT_WIRE_MOTOR1) return "Primary Motor" - if(WIRE_MOTOR2) + if(MULEBOT_WIRE_MOTOR2) return "Secondary Motor" - if(WIRE_REMOTE_RX) + if(MULEBOT_WIRE_REMOTE_RX) return "Remote Signal Receiver" - if(WIRE_REMOTE_TX) + if(MULEBOT_WIRE_REMOTE_TX) return "Remote Signal Sender" - if(WIRE_BEACON_RX) + if(MULEBOT_WIRE_BEACON_RX) return "Navigation Beacon Receiver" /datum/wires/mulebot/CanUse(mob/living/L) @@ -51,13 +51,13 @@ var/const/WIRE_BEACON_RX = 256 // beacon ping recv /datum/wires/mulebot/UpdatePulsed(index) switch(index) - if(WIRE_POWER1, WIRE_POWER2) + if(MULEBOT_WIRE_POWER1, MULEBOT_WIRE_POWER2) holder.visible_message("[bicon(holder)] The charge light flickers.") - if(WIRE_AVOIDANCE) + if(MULEBOT_WIRE_AVOIDANCE) holder.visible_message("[bicon(holder)] The external warning lights flash briefly.") - if(WIRE_LOADCHECK) + if(MULEBOT_WIRE_LOADCHECK) holder.visible_message("[bicon(holder)] The load platform clunks.") - if(WIRE_MOTOR1, WIRE_MOTOR2) + if(MULEBOT_WIRE_MOTOR1, MULEBOT_WIRE_MOTOR2) holder.visible_message("[bicon(holder)] The drive motor whines briefly.") else holder.visible_message("[bicon(holder)] You hear a radio crackle.") @@ -66,25 +66,25 @@ var/const/WIRE_BEACON_RX = 256 // beacon ping recv // HELPER PROCS /datum/wires/mulebot/proc/Motor1() - return !(wires_status & WIRE_MOTOR1) + return !(wires_status & MULEBOT_WIRE_MOTOR1) /datum/wires/mulebot/proc/Motor2() - return !(wires_status & WIRE_MOTOR2) + return !(wires_status & MULEBOT_WIRE_MOTOR2) /datum/wires/mulebot/proc/HasPower() - return !(wires_status & WIRE_POWER1) && !(wires_status & WIRE_POWER2) + return !(wires_status & MULEBOT_WIRE_POWER1) && !(wires_status & MULEBOT_WIRE_POWER2) /datum/wires/mulebot/proc/LoadCheck() - return !(wires_status & WIRE_LOADCHECK) + return !(wires_status & MULEBOT_WIRE_LOADCHECK) /datum/wires/mulebot/proc/MobAvoid() - return !(wires_status & WIRE_AVOIDANCE) + return !(wires_status & MULEBOT_WIRE_AVOIDANCE) /datum/wires/mulebot/proc/RemoteTX() - return !(wires_status & WIRE_REMOTE_TX) + return !(wires_status & MULEBOT_WIRE_REMOTE_TX) /datum/wires/mulebot/proc/RemoteRX() - return !(wires_status & WIRE_REMOTE_RX) + return !(wires_status & MULEBOT_WIRE_REMOTE_RX) /datum/wires/mulebot/proc/BeaconRX() - return !(wires_status & WIRE_BEACON_RX) \ No newline at end of file + return !(wires_status & MULEBOT_WIRE_BEACON_RX) diff --git a/code/datums/wires/nuclearbomb.dm b/code/datums/wires/nuclearbomb.dm index 5b704a4526b..63687ad8839 100644 --- a/code/datums/wires/nuclearbomb.dm +++ b/code/datums/wires/nuclearbomb.dm @@ -3,9 +3,9 @@ random = 1 wire_count = 7 -var/const/NUCLEARBOMB_WIRE_LIGHT = 1 -var/const/NUCLEARBOMB_WIRE_TIMING = 2 -var/const/NUCLEARBOMB_WIRE_SAFETY = 4 +#define NUCLEARBOMB_WIRE_LIGHT 1 +#define NUCLEARBOMB_WIRE_TIMING 2 +#define NUCLEARBOMB_WIRE_SAFETY 4 /datum/wires/nuclearbomb/GetWireName(index) switch(index) @@ -79,4 +79,4 @@ var/const/NUCLEARBOMB_WIRE_SAFETY = 4 /datum/wires/nuclearbomb/proc/updateUIs() SSnanoui.update_uis(src) if(holder) - SSnanoui.update_uis(holder) \ No newline at end of file + SSnanoui.update_uis(holder) diff --git a/code/datums/wires/particle_accelerator.dm b/code/datums/wires/particle_accelerator.dm index 1ed68b799f4..d6e68a79cfb 100644 --- a/code/datums/wires/particle_accelerator.dm +++ b/code/datums/wires/particle_accelerator.dm @@ -2,10 +2,10 @@ wire_count = 5 holder_type = /obj/machinery/particle_accelerator/control_box -var/const/PARTICLE_TOGGLE_WIRE = 1 // Toggles whether the PA is on or not. -var/const/PARTICLE_STRENGTH_WIRE = 2 // Determines the strength of the PA. -var/const/PARTICLE_INTERFACE_WIRE = 4 // Determines the interface showing up. -var/const/PARTICLE_LIMIT_POWER_WIRE = 8 // Determines how strong the PA can be. +#define PARTICLE_TOGGLE_WIRE 1 // Toggles whether the PA is on or not. +#define PARTICLE_STRENGTH_WIRE 2 // Determines the strength of the PA. +#define PARTICLE_INTERFACE_WIRE 4 // Determines the interface showing up. +#define PARTICLE_LIMIT_POWER_WIRE 8 // Determines how strong the PA can be. /datum/wires/particle_acc/control_box/GetWireName(index) switch(index) @@ -64,4 +64,4 @@ var/const/PARTICLE_LIMIT_POWER_WIRE = 8 // Determines how strong the PA can be. C.strength_upper_limit = (mended ? 2 : 3) if(C.strength_upper_limit < C.strength) C.remove_strength() - ..() \ No newline at end of file + ..() diff --git a/code/datums/wires/radio.dm b/code/datums/wires/radio.dm index 18a2ef73f65..90a72935c53 100644 --- a/code/datums/wires/radio.dm +++ b/code/datums/wires/radio.dm @@ -2,19 +2,19 @@ holder_type = /obj/item/radio wire_count = 3 -var/const/WIRE_SIGNAL = 1 -var/const/WIRE_RECEIVE = 2 -var/const/WIRE_TRANSMIT = 4 +#define RADIO_WIRE_SIGNAL 1 +#define RADIO_WIRE_RECEIVE 2 +#define RADIO_WIRE_TRANSMIT 4 /datum/wires/radio/GetWireName(index) switch(index) - if(WIRE_SIGNAL) + if(RADIO_WIRE_SIGNAL) return "Signal" - if(WIRE_RECEIVE) + if(RADIO_WIRE_RECEIVE) return "Receiver" - if(WIRE_TRANSMIT) + if(RADIO_WIRE_TRANSMIT) return "Transmitter" /datum/wires/radio/CanUse(mob/living/L) @@ -26,27 +26,27 @@ var/const/WIRE_TRANSMIT = 4 /datum/wires/radio/UpdatePulsed(index) var/obj/item/radio/R = holder switch(index) - if(WIRE_SIGNAL) - R.listening = !R.listening && !IsIndexCut(WIRE_RECEIVE) - R.broadcasting = R.listening && !IsIndexCut(WIRE_TRANSMIT) + if(RADIO_WIRE_SIGNAL) + R.listening = !R.listening && !IsIndexCut(RADIO_WIRE_RECEIVE) + R.broadcasting = R.listening && !IsIndexCut(RADIO_WIRE_TRANSMIT) - if(WIRE_RECEIVE) - R.listening = !R.listening && !IsIndexCut(WIRE_SIGNAL) + if(RADIO_WIRE_RECEIVE) + R.listening = !R.listening && !IsIndexCut(RADIO_WIRE_SIGNAL) - if(WIRE_TRANSMIT) - R.broadcasting = !R.broadcasting && !IsIndexCut(WIRE_SIGNAL) + if(RADIO_WIRE_TRANSMIT) + R.broadcasting = !R.broadcasting && !IsIndexCut(RADIO_WIRE_SIGNAL) ..() /datum/wires/radio/UpdateCut(index, mended) var/obj/item/radio/R = holder switch(index) - if(WIRE_SIGNAL) - R.listening = mended && !IsIndexCut(WIRE_RECEIVE) - R.broadcasting = mended && !IsIndexCut(WIRE_TRANSMIT) + if(RADIO_WIRE_SIGNAL) + R.listening = mended && !IsIndexCut(RADIO_WIRE_RECEIVE) + R.broadcasting = mended && !IsIndexCut(RADIO_WIRE_TRANSMIT) - if(WIRE_RECEIVE) - R.listening = mended && !IsIndexCut(WIRE_SIGNAL) + if(RADIO_WIRE_RECEIVE) + R.listening = mended && !IsIndexCut(RADIO_WIRE_SIGNAL) - if(WIRE_TRANSMIT) - R.broadcasting = mended && !IsIndexCut(WIRE_SIGNAL) + if(RADIO_WIRE_TRANSMIT) + R.broadcasting = mended && !IsIndexCut(RADIO_WIRE_SIGNAL) ..() diff --git a/code/datums/wires/robot.dm b/code/datums/wires/robot.dm index bc03e80c327..789a0472869 100644 --- a/code/datums/wires/robot.dm +++ b/code/datums/wires/robot.dm @@ -3,21 +3,13 @@ holder_type = /mob/living/silicon/robot wire_count = 5 -/* /tg/ ordering doesn't work for us, we need lawsync at the end for MoMMIs. -var/const/BORG_WIRE_LAWCHECK = 1 -var/const/BORG_WIRE_MAIN_POWER = 2 // The power wires do nothing whyyyyyyyyyyyyy -var/const/BORG_WIRE_LOCKED_DOWN = 4 -var/const/BORG_WIRE_AI_CONTROL = 8 -var/const/BORG_WIRE_CAMERA = 16 -*/ - // /vg/ ordering -var/const/BORG_WIRE_MAIN_POWER = 1 // The power wires do nothing whyyyyyyyyyyyyy -var/const/BORG_WIRE_LOCKED_DOWN = 2 -var/const/BORG_WIRE_CAMERA = 4 -var/const/BORG_WIRE_AI_CONTROL = 8 // Not used on MoMMIs -var/const/BORG_WIRE_LAWCHECK = 16 // Not used on MoMMIs +#define BORG_WIRE_MAIN_POWER 1 // The power wires do nothing whyyyyyyyyyyyyy +#define BORG_WIRE_LOCKED_DOWN 2 +#define BORG_WIRE_CAMERA 4 +#define BORG_WIRE_AI_CONTROL 8 // Not used on MoMMIs +#define BORG_WIRE_LAWCHECK 16 // Not used on MoMMIs /datum/wires/robot/GetWireName(index) switch(index) @@ -111,4 +103,4 @@ var/const/BORG_WIRE_LAWCHECK = 16 // Not used on MoMMIs return wires_status & BORG_WIRE_LAWCHECK /datum/wires/robot/proc/AIHasControl() - return wires_status & BORG_WIRE_AI_CONTROL \ No newline at end of file + return wires_status & BORG_WIRE_AI_CONTROL diff --git a/code/datums/wires/smartfridge.dm b/code/datums/wires/smartfridge.dm index 53b33e0fd72..a65377ead1a 100644 --- a/code/datums/wires/smartfridge.dm +++ b/code/datums/wires/smartfridge.dm @@ -6,9 +6,9 @@ random = 1 wire_count = 4 -var/const/SMARTFRIDGE_WIRE_ELECTRIFY = 1 -var/const/SMARTFRIDGE_WIRE_THROW = 2 -var/const/SMARTFRIDGE_WIRE_IDSCAN = 4 +#define SMARTFRIDGE_WIRE_ELECTRIFY 1 +#define SMARTFRIDGE_WIRE_THROW 2 +#define SMARTFRIDGE_WIRE_IDSCAN 4 /datum/wires/smartfridge/GetWireName(index) switch(index) diff --git a/code/datums/wires/suitstorage.dm b/code/datums/wires/suitstorage.dm index b519b449c86..175b36e6081 100644 --- a/code/datums/wires/suitstorage.dm +++ b/code/datums/wires/suitstorage.dm @@ -2,10 +2,10 @@ holder_type = /obj/machinery/suit_storage_unit wire_count = 8 -var/const/SSU_WIRE_ID = 1 -var/const/SSU_WIRE_SHOCK = 2 -var/const/SSU_WIRE_SAFETY = 4 -var/const/SSU_WIRE_UV = 8 +#define SSU_WIRE_ID 1 +#define SSU_WIRE_SHOCK 2 +#define SSU_WIRE_SAFETY 4 +#define SSU_WIRE_UV 8 /datum/wires/suitstorage/GetWireName(index) @@ -66,4 +66,4 @@ datum/wires/suitstorage/UpdatePulsed(index) A.shocked = FALSE if(SSU_WIRE_UV) A.uv_super = !A.uv_super - ..() \ No newline at end of file + ..() diff --git a/code/datums/wires/syndicatebomb.dm b/code/datums/wires/syndicatebomb.dm index 616429b8cd8..b490d150efe 100644 --- a/code/datums/wires/syndicatebomb.dm +++ b/code/datums/wires/syndicatebomb.dm @@ -3,27 +3,27 @@ holder_type = /obj/machinery/syndicatebomb wire_count = 5 -var/const/WIRE_BOOM = 1 // Explodes if pulsed or cut while active, defuses a bomb that isn't active on cut -var/const/WIRE_UNBOLT = 2 // Unbolts the bomb if cut, hint on pulsed -var/const/WIRE_DELAY = 4 // Raises the timer on pulse, does nothing on cut -var/const/WIRE_PROCEED = 8 // Lowers the timer, explodes if cut while the bomb is active -var/const/WIRE_ACTIVATE = 16 // Will start a bombs timer if pulsed, will hint if pulsed while already active, will stop a timer a bomb on cut +#define BOMB_WIRE_BOOM 1 // Explodes if pulsed or cut while active, defuses a bomb that isn't active on cut +#define BOMB_WIRE_UNBOLT 2 // Unbolts the bomb if cut, hint on pulsed +#define BOMB_WIRE_DELAY 4 // Raises the timer on pulse, does nothing on cut +#define BOMB_WIRE_PROCEED 8 // Lowers the timer, explodes if cut while the bomb is active +#define BOMB_WIRE_ACTIVATE 16 // Will start a bombs timer if pulsed, will hint if pulsed while already active, will stop a timer a bomb on cut /datum/wires/syndicatebomb/GetWireName(index) switch(index) - if(WIRE_BOOM) + if(BOMB_WIRE_BOOM) return "Explode" - if(WIRE_UNBOLT) + if(BOMB_WIRE_UNBOLT) return "Unbolt" - if(WIRE_DELAY) + if(BOMB_WIRE_DELAY) return "Delay" - if(WIRE_PROCEED) + if(BOMB_WIRE_PROCEED) return "Proceed" - if(WIRE_ACTIVATE) + if(BOMB_WIRE_ACTIVATE) return "Activate" /datum/wires/syndicatebomb/CanUse(mob/living/L) @@ -35,13 +35,13 @@ var/const/WIRE_ACTIVATE = 16 // Will start a bombs timer if pulsed, will hint if /datum/wires/syndicatebomb/UpdatePulsed(index) var/obj/machinery/syndicatebomb/B = holder switch(index) - if(WIRE_BOOM) + if(BOMB_WIRE_BOOM) if(B.active) holder.visible_message("[bicon(B)] An alarm sounds! It's go-") B.explode_now = TRUE - if(WIRE_UNBOLT) + if(BOMB_WIRE_UNBOLT) holder.visible_message("[bicon(holder)] The bolts spin in place for a moment.") - if(WIRE_DELAY) + if(BOMB_WIRE_DELAY) if(B.delayedbig) holder.visible_message("[bicon(B)] The bomb has already been delayed.") else @@ -49,7 +49,7 @@ var/const/WIRE_ACTIVATE = 16 // Will start a bombs timer if pulsed, will hint if playsound(B, 'sound/machines/chime.ogg', 30, 1) B.detonation_timer += 300 B.delayedbig = TRUE - if(WIRE_PROCEED) + if(BOMB_WIRE_PROCEED) holder.visible_message("[bicon(B)] The bomb buzzes ominously!") playsound(B, 'sound/machines/buzz-sigh.ogg', 30, 1) var/seconds = B.seconds_remaining() @@ -59,7 +59,7 @@ var/const/WIRE_ACTIVATE = 16 // Will start a bombs timer if pulsed, will hint if B.detonation_timer -= 100 else if(seconds >= 11) // Both to prevent negative timers and to have a little mercy. B.detonation_timer = world.time + 100 - if(WIRE_ACTIVATE) + if(BOMB_WIRE_ACTIVATE) if(!B.active && !B.defused) holder.visible_message("[bicon(B)] You hear the bomb start ticking!") B.activate() @@ -75,7 +75,7 @@ var/const/WIRE_ACTIVATE = 16 // Will start a bombs timer if pulsed, will hint if /datum/wires/syndicatebomb/UpdateCut(index, mended) var/obj/machinery/syndicatebomb/B = holder switch(index) - if(WIRE_EXPLODE) + if(BOMB_WIRE_BOOM) if(mended) B.defused = FALSE // Cutting and mending all the wires of an inactive bomb will thus cure any sabotage. else @@ -84,19 +84,19 @@ var/const/WIRE_ACTIVATE = 16 // Will start a bombs timer if pulsed, will hint if B.explode_now = TRUE else B.defused = TRUE - if(WIRE_UNBOLT) + if(BOMB_WIRE_UNBOLT) if(!mended && B.anchored) holder.visible_message("[bicon(B)] The bolts lift out of the ground!") playsound(B, 'sound/effects/stealthoff.ogg', 30, 1) B.anchored = FALSE - if(WIRE_PROCEED) + if(BOMB_WIRE_PROCEED) if(!mended && B.active) holder.visible_message("[bicon(B)] An alarm sounds! It's go-") B.explode_now = TRUE - if(WIRE_ACTIVATE) + if(BOMB_WIRE_ACTIVATE) if(!mended && B.active) holder.visible_message("[bicon(B)] The timer stops! The bomb has been defused!") B.active = FALSE B.defused = TRUE B.update_icon() - ..() \ No newline at end of file + ..() diff --git a/code/datums/wires/tesla_coil.dm b/code/datums/wires/tesla_coil.dm index ed60e927f45..bc513c6c6c0 100644 --- a/code/datums/wires/tesla_coil.dm +++ b/code/datums/wires/tesla_coil.dm @@ -2,11 +2,11 @@ wire_count = 1 holder_type = /obj/machinery/power/tesla_coil -var/const/WIRE_ZAP = 1 +#define TESLACOIL_WIRE_ZAP 1 /datum/wires/tesla_coil/GetWireName(index) switch(index) - if(WIRE_ZAP) + if(TESLACOIL_WIRE_ZAP) return "Zap" /datum/wires/tesla_coil/CanUse(mob/living/L) @@ -18,6 +18,6 @@ var/const/WIRE_ZAP = 1 /datum/wires/tesla_coil/UpdatePulsed(index) var/obj/machinery/power/tesla_coil/T = holder switch(index) - if(WIRE_ZAP) + if(TESLACOIL_WIRE_ZAP) T.zap() - ..() \ No newline at end of file + ..() diff --git a/code/datums/wires/vending.dm b/code/datums/wires/vending.dm index 3861cc68203..c9e6032afee 100644 --- a/code/datums/wires/vending.dm +++ b/code/datums/wires/vending.dm @@ -2,10 +2,10 @@ holder_type = /obj/machinery/vending wire_count = 4 -var/const/VENDING_WIRE_THROW = 1 -var/const/VENDING_WIRE_CONTRABAND = 2 -var/const/VENDING_WIRE_ELECTRIFY = 4 -var/const/VENDING_WIRE_IDSCAN = 8 +#define VENDING_WIRE_THROW 1 +#define VENDING_WIRE_CONTRABAND 2 +#define VENDING_WIRE_ELECTRIFY 4 +#define VENDING_WIRE_IDSCAN 8 /datum/wires/vending/GetWireName(index) switch(index) @@ -66,4 +66,4 @@ var/const/VENDING_WIRE_IDSCAN = 8 V.seconds_electrified = -1 if(VENDING_WIRE_IDSCAN) V.scan_id = 1 - ..() \ No newline at end of file + ..() diff --git a/code/datums/wires/wires.dm b/code/datums/wires/wires.dm index 5220fd83a6d..07cf919db2d 100644 --- a/code/datums/wires/wires.dm +++ b/code/datums/wires/wires.dm @@ -5,12 +5,6 @@ #define MAX_FLAG 65535 -/proc/is_wire_tool(obj/item/I) - if(ismultitool(I)) - return TRUE - if(iswirecutter(I)) - return TRUE - var/list/same_wires = list() // 12 colours, if you're adding more than 12 wires then add more colours here var/list/wireColours = list("red", "blue", "green", "black", "orange", "brown", "gold", "gray", "cyan", "navy", "purple", "pink") @@ -208,20 +202,20 @@ var/list/wireColours = list("red", "blue", "green", "black", "orange", "brown", // Example of use: /* -var/const/BOLTED= 1 -var/const/SHOCKED = 2 -var/const/SAFETY = 4 -var/const/POWER = 8 +#define NAME_WIRE_BOLTED 1 +#define NAME_WIRE_SHOCKED 2 +#define NAME_WIRE_SAFETY 4 +#define NAME_WIRE_POWER 8 /datum/wires/door/UpdateCut(var/index, var/mended) var/obj/machinery/door/airlock/A = holder switch(index) - if(BOLTED) + if(NAME_WIRE_BOLTED) if(!mended) A.bolt() - if(SHOCKED) + if(NAME_WIRE_SHOCKED) A.shock() - if(SAFETY ) + if(NAME_WIRE_SAFETY) A.safety() */ @@ -335,4 +329,4 @@ var/const/POWER = 8 /datum/wires/proc/Shuffle() wires_status = 0 - GenerateWires() \ No newline at end of file + GenerateWires() diff --git a/code/defines/procs/AStar.dm b/code/defines/procs/AStar.dm index 1ec4f4eb7fc..e593888c265 100644 --- a/code/defines/procs/AStar.dm +++ b/code/defines/procs/AStar.dm @@ -1,184 +1,184 @@ -/* -A Star pathfinding algorithm -Returns a list of tiles forming a path from A to B, taking dense objects as well as walls, and the orientation of -windows along the route into account. -Use: -your_list = AStar(start location, end location, moving atom, distance proc, max nodes, maximum node depth, minimum distance to target, adjacent proc, atom id, turfs to exclude, check only simulated) - -Optional extras to add on (in order): -Distance proc : the distance used in every A* calculation (length of path and heuristic) -MaxNodes: The maximum number of nodes the returned path can be (0 = infinite) -Maxnodedepth: The maximum number of nodes to search (default: 30, 0 = infinite) -Mintargetdist: Minimum distance to the target before path returns, could be used to get -near a target, but not right to it - for an AI mob with a gun, for example. -Adjacent proc : returns the turfs to consider around the actually processed node -Simulated only : whether to consider unsimulated turfs or not (used by some Adjacent proc) - -Also added 'exclude' turf to avoid travelling over; defaults to null - -Actual Adjacent procs : - - /turf/proc/reachableAdjacentTurfs : returns reachable turfs in cardinal directions (uses simulated_only) - - /turf/proc/reachableAdjacentAtmosTurfs : returns turfs in cardinal directions reachable via atmos - -*/ - -////////////////////// -//PathNode object -////////////////////// - -//A* nodes variables -/PathNode - var/turf/source //turf associated with the PathNode - var/PathNode/prevNode //link to the parent PathNode - var/f //A* Node weight (f = g + h) - var/g //A* movement cost variable - var/h //A* heuristic variable - var/nt //count the number of Nodes traversed - -/PathNode/New(s,p,pg,ph,pnt) - source = s - prevNode = p - g = pg - h = ph - f = g + h - source.PNode = src - nt = pnt - -/PathNode/proc/calc_f() - f = g + h - -////////////////////// -//A* procs -////////////////////// - -//the weighting function, used in the A* algorithm -/proc/PathWeightCompare(PathNode/a, PathNode/b) - return a.f - b.f - -//reversed so that the Heap is a MinHeap rather than a MaxHeap -/proc/HeapPathWeightCompare(PathNode/a, PathNode/b) - return b.f - a.f - -//wrapper that returns an empty list if A* failed to find a path -/proc/get_path_to(caller, end, dist, maxnodes, maxnodedepth = 30, mintargetdist, adjacent = /turf/proc/reachableAdjacentTurfs, id=null, turf/exclude=null, simulated_only = 1) - var/list/path = AStar(caller, end, dist, maxnodes, maxnodedepth, mintargetdist, adjacent, id, exclude, simulated_only) - if(!path) - path = list() - return path - -//the actual algorithm -/proc/AStar(caller, end, dist, maxnodes, maxnodedepth = 30, mintargetdist, adjacent = /turf/proc/reachableAdjacentTurfs, id=null, turf/exclude=null, simulated_only = 1) - - //sanitation - var/start = get_turf(caller) - if(!start) - return 0 - - if(maxnodes) - //if start turf is farther than maxnodes from end turf, no need to do anything - if(call(start, dist)(end) > maxnodes) - return 0 - maxnodedepth = maxnodes //no need to consider path longer than maxnodes - - var/Heap/open = new /Heap(/proc/HeapPathWeightCompare) //the open list - var/list/closed = new() //the closed list - var/list/path = null //the returned path, if any - var/PathNode/cur //current processed turf - - //initialization - open.Insert(new /PathNode(start,null,0,call(start,dist)(end),0)) - - //then run the main loop - while(!open.IsEmpty() && !path) - { - //get the lower f node on the open list - cur = open.Pop() //get the lower f turf in the open list - closed.Add(cur.source) //and tell we've processed it - - //if we only want to get near the target, check if we're close enough - var/closeenough - if(mintargetdist) - closeenough = call(cur.source,dist)(end) <= mintargetdist - - //if too many steps, abandon that path - if(maxnodedepth && (cur.nt > maxnodedepth)) - continue - - //found the target turf (or close enough), let's create the path to it - if(cur.source == end || closeenough) - path = new() - path.Add(cur.source) - - while(cur.prevNode) - cur = cur.prevNode - path.Add(cur.source) - - break - - //get adjacents turfs using the adjacent proc, checking for access with id - var/list/L = call(cur.source,adjacent)(caller, id, simulated_only) - for(var/turf/T in L) - if(T == exclude || (T in closed)) - continue - - var/newg = cur.g + call(cur.source,dist)(T) - if(!T.PNode) //is not already in open list, so add it - open.Insert(new /PathNode(T,cur,newg,call(T,dist)(end),cur.nt+1)) - else //is already in open list, check if it's a better way from the current turf - if(newg < T.PNode.g) - T.PNode.prevNode = cur - T.PNode.g = newg - T.PNode.calc_f() - T.PNode.nt = cur.nt + 1 - open.ReSort(T.PNode)//reorder the changed element in the list - - } - - //cleaning after us - for(var/PathNode/PN in open.L) - PN.source.PNode = null - for(var/turf/T in closed) - T.PNode = null - - //reverse the path to get it from start to finish - if(path) - for(var/i = 1; i <= path.len/2; i++) - path.Swap(i,path.len-i+1) - - return path - -//Returns adjacent turfs in cardinal directions that are reachable -//simulated_only controls whether only simulated turfs are considered or not -/turf/proc/reachableAdjacentTurfs(caller, ID, simulated_only) - var/list/L = new() - var/turf/simulated/T - - for(var/dir in cardinal) - T = get_step(src,dir) - if(!T || (simulated_only && !istype(T))) - continue - if(!T.density && !LinkBlockedWithAccess(T,caller, ID)) - L.Add(T) - return L - -//Returns adjacent turfs in cardinal directions that are reachable via atmos -/turf/proc/reachableAdjacentAtmosTurfs() - return atmos_adjacent_turfs - -/turf/proc/LinkBlockedWithAccess(turf/T, caller, ID) - var/adir = get_dir(src, T) - var/rdir = get_dir(T, src) - - for(var/obj/structure/window/W in src) - if(!W.CanAStarPass(ID, adir)) - return 1 - for(var/obj/machinery/door/window/W in src) - if(!W.CanAStarPass(ID, adir)) - return 1 - for(var/obj/O in T) - if(!O.CanAStarPass(ID, rdir, caller)) - return 1 - - return 0 +/* +A Star pathfinding algorithm +Returns a list of tiles forming a path from A to B, taking dense objects as well as walls, and the orientation of +windows along the route into account. +Use: +your_list = AStar(start location, end location, moving atom, distance proc, max nodes, maximum node depth, minimum distance to target, adjacent proc, atom id, turfs to exclude, check only simulated) + +Optional extras to add on (in order): +Distance proc : the distance used in every A* calculation (length of path and heuristic) +MaxNodes: The maximum number of nodes the returned path can be (0 = infinite) +Maxnodedepth: The maximum number of nodes to search (default: 30, 0 = infinite) +Mintargetdist: Minimum distance to the target before path returns, could be used to get +near a target, but not right to it - for an AI mob with a gun, for example. +Adjacent proc : returns the turfs to consider around the actually processed node +Simulated only : whether to consider unsimulated turfs or not (used by some Adjacent proc) + +Also added 'exclude' turf to avoid travelling over; defaults to null + +Actual Adjacent procs : + + /turf/proc/reachableAdjacentTurfs : returns reachable turfs in cardinal directions (uses simulated_only) + + /turf/proc/reachableAdjacentAtmosTurfs : returns turfs in cardinal directions reachable via atmos + +*/ + +////////////////////// +//PathNode object +////////////////////// + +//A* nodes variables +/PathNode + var/turf/source //turf associated with the PathNode + var/PathNode/prevNode //link to the parent PathNode + var/f //A* Node weight (f = g + h) + var/g //A* movement cost variable + var/h //A* heuristic variable + var/nt //count the number of Nodes traversed + +/PathNode/New(s,p,pg,ph,pnt) + source = s + prevNode = p + g = pg + h = ph + f = g + h + source.PNode = src + nt = pnt + +/PathNode/proc/calc_f() + f = g + h + +////////////////////// +//A* procs +////////////////////// + +//the weighting function, used in the A* algorithm +/proc/PathWeightCompare(PathNode/a, PathNode/b) + return a.f - b.f + +//reversed so that the Heap is a MinHeap rather than a MaxHeap +/proc/HeapPathWeightCompare(PathNode/a, PathNode/b) + return b.f - a.f + +//wrapper that returns an empty list if A* failed to find a path +/proc/get_path_to(caller, end, dist, maxnodes, maxnodedepth = 30, mintargetdist, adjacent = /turf/proc/reachableAdjacentTurfs, id=null, turf/exclude=null, simulated_only = 1) + var/list/path = AStar(caller, end, dist, maxnodes, maxnodedepth, mintargetdist, adjacent, id, exclude, simulated_only) + if(!path) + path = list() + return path + +//the actual algorithm +/proc/AStar(caller, end, dist, maxnodes, maxnodedepth = 30, mintargetdist, adjacent = /turf/proc/reachableAdjacentTurfs, id=null, turf/exclude=null, simulated_only = 1) + + //sanitation + var/start = get_turf(caller) + if(!start) + return 0 + + if(maxnodes) + //if start turf is farther than maxnodes from end turf, no need to do anything + if(call(start, dist)(end) > maxnodes) + return 0 + maxnodedepth = maxnodes //no need to consider path longer than maxnodes + + var/Heap/open = new /Heap(/proc/HeapPathWeightCompare) //the open list + var/list/closed = new() //the closed list + var/list/path = null //the returned path, if any + var/PathNode/cur //current processed turf + + //initialization + open.Insert(new /PathNode(start,null,0,call(start,dist)(end),0)) + + //then run the main loop + while(!open.IsEmpty() && !path) + { + //get the lower f node on the open list + cur = open.Pop() //get the lower f turf in the open list + closed.Add(cur.source) //and tell we've processed it + + //if we only want to get near the target, check if we're close enough + var/closeenough + if(mintargetdist) + closeenough = call(cur.source,dist)(end) <= mintargetdist + + //if too many steps, abandon that path + if(maxnodedepth && (cur.nt > maxnodedepth)) + continue + + //found the target turf (or close enough), let's create the path to it + if(cur.source == end || closeenough) + path = new() + path.Add(cur.source) + + while(cur.prevNode) + cur = cur.prevNode + path.Add(cur.source) + + break + + //get adjacents turfs using the adjacent proc, checking for access with id + var/list/L = call(cur.source,adjacent)(caller, id, simulated_only) + for(var/turf/T in L) + if(T == exclude || (T in closed)) + continue + + var/newg = cur.g + call(cur.source,dist)(T) + if(!T.PNode) //is not already in open list, so add it + open.Insert(new /PathNode(T,cur,newg,call(T,dist)(end),cur.nt+1)) + else //is already in open list, check if it's a better way from the current turf + if(newg < T.PNode.g) + T.PNode.prevNode = cur + T.PNode.g = newg + T.PNode.calc_f() + T.PNode.nt = cur.nt + 1 + open.ReSort(T.PNode)//reorder the changed element in the list + + } + + //cleaning after us + for(var/PathNode/PN in open.L) + PN.source.PNode = null + for(var/turf/T in closed) + T.PNode = null + + //reverse the path to get it from start to finish + if(path) + for(var/i = 1; i <= path.len/2; i++) + path.Swap(i,path.len-i+1) + + return path + +//Returns adjacent turfs in cardinal directions that are reachable +//simulated_only controls whether only simulated turfs are considered or not +/turf/proc/reachableAdjacentTurfs(caller, ID, simulated_only) + var/list/L = new() + var/turf/simulated/T + + for(var/dir in cardinal) + T = get_step(src,dir) + if(!T || (simulated_only && !istype(T))) + continue + if(!T.density && !LinkBlockedWithAccess(T,caller, ID)) + L.Add(T) + return L + +//Returns adjacent turfs in cardinal directions that are reachable via atmos +/turf/proc/reachableAdjacentAtmosTurfs() + return atmos_adjacent_turfs + +/turf/proc/LinkBlockedWithAccess(turf/T, caller, ID) + var/adir = get_dir(src, T) + var/rdir = get_dir(T, src) + + for(var/obj/structure/window/W in src) + if(!W.CanAStarPass(ID, adir)) + return 1 + for(var/obj/machinery/door/window/W in src) + if(!W.CanAStarPass(ID, adir)) + return 1 + for(var/obj/O in T) + if(!O.CanAStarPass(ID, rdir, caller)) + return 1 + + return 0 diff --git a/code/defines/procs/dbcore.dm b/code/defines/procs/dbcore.dm index b04a4c3b93a..86862eb9fcc 100644 --- a/code/defines/procs/dbcore.dm +++ b/code/defines/procs/dbcore.dm @@ -1,206 +1,206 @@ -//cursors -#define Default_Cursor 0 -#define Client_Cursor 1 -#define Server_Cursor 2 -//conversions -#define TEXT_CONV 1 -#define RSC_FILE_CONV 2 -#define NUMBER_CONV 3 -//column flag values: -#define IS_NUMERIC 1 -#define IS_BINARY 2 -#define IS_NOT_NULL 4 -#define IS_PRIMARY_KEY 8 -#define IS_UNSIGNED 16 -//types -#define TINYINT 1 -#define SMALLINT 2 -#define MEDIUMINT 3 -#define INTEGER 4 -#define BIGINT 5 -#define DECIMAL 6 -#define FLOAT 7 -#define DOUBLE 8 -#define DATE 9 -#define DATETIME 10 -#define TIMESTAMP 11 -#define TIME 12 -#define STRING 13 -#define BLOB 14 -// TODO: Investigate more recent type additions and see if I can handle them. - Nadrew - - -// Deprecated! See global.dm for new configuration vars -/* -var/DB_SERVER = "" // This is the location of your MySQL server (localhost is USUALLY fine) -var/DB_PORT = 3306 // This is the port your MySQL server is running on (3306 is the default) -*/ - -DBConnection - var/_db_con // This variable contains a reference to the actual database connection. - var/dbi // This variable is a string containing the DBI MySQL requires. - var/user // This variable contains the username data. - var/password // This variable contains the password data. - var/default_cursor // This contains the default database cursor data. - // - var/server = "" - var/port = 3306 - -DBConnection/New(dbi_handler,username,password_handler,cursor_handler) - src.dbi = dbi_handler - src.user = username - src.password = password_handler - src.default_cursor = cursor_handler - _db_con = _dm_db_new_con() - -DBConnection/proc/Connect(dbi_handler=src.dbi,user_handler=src.user,password_handler=src.password,cursor_handler) - if(!config.sql_enabled) - return 0 - if(!src) return 0 - cursor_handler = src.default_cursor - if(!cursor_handler) cursor_handler = Default_Cursor - return _dm_db_connect(_db_con,dbi_handler,user_handler,password_handler,cursor_handler,null) - -DBConnection/proc/Disconnect() return _dm_db_close(_db_con) - -DBConnection/proc/IsConnected() - if(!config.sql_enabled) return 0 - var/success = _dm_db_is_connected(_db_con) - return success - -DBConnection/proc/Quote(str) return _dm_db_quote(_db_con,str) - -DBConnection/proc/ErrorMsg() return _dm_db_error_msg(_db_con) -DBConnection/proc/SelectDB(database_name,dbi) - if(IsConnected()) Disconnect() - //return Connect("[dbi?"[dbi]":"dbi:mysql:[database_name]:[DB_SERVER]:[DB_PORT]"]",user,password) - return Connect("[dbi?"[dbi]":"dbi:mysql:[database_name]:[sqladdress]:[sqlport]"]",user,password) -DBConnection/proc/NewQuery(sql_query,cursor_handler=src.default_cursor) return new/DBQuery(sql_query,src,cursor_handler) - - -DBQuery/New(sql_query,DBConnection/connection_handler,cursor_handler) - if(sql_query) src.sql = sql_query - if(connection_handler) src.db_connection = connection_handler - if(cursor_handler) src.default_cursor = cursor_handler - _db_query = _dm_db_new_query() - return ..() - - -DBQuery - var/sql // The sql query being executed. - var/default_cursor - var/list/columns //list of DB Columns populated by Columns() - var/list/conversions - var/list/item[0] //list of data values populated by NextRow() - - var/DBConnection/db_connection - var/_db_query - -DBQuery/proc/Connect(DBConnection/connection_handler) src.db_connection = connection_handler - -DBQuery/proc/Execute(sql_query=src.sql,cursor_handler=default_cursor) - Close() - return _dm_db_execute(_db_query,sql_query,db_connection._db_con,cursor_handler,null) - -DBQuery/proc/NextRow() return _dm_db_next_row(_db_query,item,conversions) - -DBQuery/proc/RowsAffected() return _dm_db_rows_affected(_db_query) - -DBQuery/proc/RowCount() return _dm_db_row_count(_db_query) - -DBQuery/proc/ErrorMsg() return _dm_db_error_msg(_db_query) - -DBQuery/proc/Columns() - if(!columns) - columns = _dm_db_columns(_db_query,/DBColumn) - return columns - -DBQuery/proc/GetRowData() - var/list/columns = Columns() - var/list/results - if(columns.len) - results = list() - for(var/C in columns) - results+=C - var/DBColumn/cur_col = columns[C] - results[C] = src.item[(cur_col.position+1)] - return results - -DBQuery/proc/Close() - item.len = 0 - columns = null - conversions = null - return _dm_db_close(_db_query) - -DBQuery/proc/Quote(str) - return db_connection.Quote(str) - -DBQuery/proc/SetConversion(column,conversion) - if(istext(column)) column = columns.Find(column) - if(!conversions) conversions = new/list(column) - else if(conversions.len < column) conversions.len = column - conversions[column] = conversion - - -DBColumn - var/name - var/table - var/position //1-based index into item data - var/sql_type - var/flags - var/length - var/max_length - -DBColumn/New(name_handler,table_handler,position_handler,type_handler,flag_handler,length_handler,max_length_handler) - src.name = name_handler - src.table = table_handler - src.position = position_handler - src.sql_type = type_handler - src.flags = flag_handler - src.length = length_handler - src.max_length = max_length_handler - return ..() - - -DBColumn/proc/SqlTypeName(type_handler=src.sql_type) - switch(type_handler) - if(TINYINT) return "TINYINT" - if(SMALLINT) return "SMALLINT" - if(MEDIUMINT) return "MEDIUMINT" - if(INTEGER) return "INTEGER" - if(BIGINT) return "BIGINT" - if(FLOAT) return "FLOAT" - if(DOUBLE) return "DOUBLE" - if(DATE) return "DATE" - if(DATETIME) return "DATETIME" - if(TIMESTAMP) return "TIMESTAMP" - if(TIME) return "TIME" - if(STRING) return "STRING" - if(BLOB) return "BLOB" - - -#undef Default_Cursor -#undef Client_Cursor -#undef Server_Cursor -#undef TEXT_CONV -#undef RSC_FILE_CONV -#undef NUMBER_CONV -#undef IS_NUMERIC -#undef IS_BINARY -#undef IS_NOT_NULL -#undef IS_PRIMARY_KEY -#undef IS_UNSIGNED -#undef TINYINT -#undef SMALLINT -#undef MEDIUMINT -#undef INTEGER -#undef BIGINT -#undef DECIMAL -#undef FLOAT -#undef DOUBLE -#undef DATE -#undef DATETIME -#undef TIMESTAMP -#undef TIME -#undef STRING -#undef BLOB +//cursors +#define Default_Cursor 0 +#define Client_Cursor 1 +#define Server_Cursor 2 +//conversions +#define TEXT_CONV 1 +#define RSC_FILE_CONV 2 +#define NUMBER_CONV 3 +//column flag values: +#define IS_NUMERIC 1 +#define IS_BINARY 2 +#define IS_NOT_NULL 4 +#define IS_PRIMARY_KEY 8 +#define IS_UNSIGNED 16 +//types +#define TINYINT 1 +#define SMALLINT 2 +#define MEDIUMINT 3 +#define INTEGER 4 +#define BIGINT 5 +#define DECIMAL 6 +#define FLOAT 7 +#define DOUBLE 8 +#define DATE 9 +#define DATETIME 10 +#define TIMESTAMP 11 +#define TIME 12 +#define STRING 13 +#define BLOB 14 +// TODO: Investigate more recent type additions and see if I can handle them. - Nadrew + + +// Deprecated! See global.dm for new configuration vars +/* +var/DB_SERVER = "" // This is the location of your MySQL server (localhost is USUALLY fine) +var/DB_PORT = 3306 // This is the port your MySQL server is running on (3306 is the default) +*/ + +DBConnection + var/_db_con // This variable contains a reference to the actual database connection. + var/dbi // This variable is a string containing the DBI MySQL requires. + var/user // This variable contains the username data. + var/password // This variable contains the password data. + var/default_cursor // This contains the default database cursor data. + // + var/server = "" + var/port = 3306 + +DBConnection/New(dbi_handler,username,password_handler,cursor_handler) + src.dbi = dbi_handler + src.user = username + src.password = password_handler + src.default_cursor = cursor_handler + _db_con = _dm_db_new_con() + +DBConnection/proc/Connect(dbi_handler=src.dbi,user_handler=src.user,password_handler=src.password,cursor_handler) + if(!config.sql_enabled) + return 0 + if(!src) return 0 + cursor_handler = src.default_cursor + if(!cursor_handler) cursor_handler = Default_Cursor + return _dm_db_connect(_db_con,dbi_handler,user_handler,password_handler,cursor_handler,null) + +DBConnection/proc/Disconnect() return _dm_db_close(_db_con) + +DBConnection/proc/IsConnected() + if(!config.sql_enabled) return 0 + var/success = _dm_db_is_connected(_db_con) + return success + +DBConnection/proc/Quote(str) return _dm_db_quote(_db_con,str) + +DBConnection/proc/ErrorMsg() return _dm_db_error_msg(_db_con) +DBConnection/proc/SelectDB(database_name,dbi) + if(IsConnected()) Disconnect() + //return Connect("[dbi?"[dbi]":"dbi:mysql:[database_name]:[DB_SERVER]:[DB_PORT]"]",user,password) + return Connect("[dbi?"[dbi]":"dbi:mysql:[database_name]:[sqladdress]:[sqlport]"]",user,password) +DBConnection/proc/NewQuery(sql_query,cursor_handler=src.default_cursor) return new/DBQuery(sql_query,src,cursor_handler) + + +DBQuery/New(sql_query,DBConnection/connection_handler,cursor_handler) + if(sql_query) src.sql = sql_query + if(connection_handler) src.db_connection = connection_handler + if(cursor_handler) src.default_cursor = cursor_handler + _db_query = _dm_db_new_query() + return ..() + + +DBQuery + var/sql // The sql query being executed. + var/default_cursor + var/list/columns //list of DB Columns populated by Columns() + var/list/conversions + var/list/item[0] //list of data values populated by NextRow() + + var/DBConnection/db_connection + var/_db_query + +DBQuery/proc/Connect(DBConnection/connection_handler) src.db_connection = connection_handler + +DBQuery/proc/Execute(sql_query=src.sql,cursor_handler=default_cursor) + Close() + return _dm_db_execute(_db_query,sql_query,db_connection._db_con,cursor_handler,null) + +DBQuery/proc/NextRow() return _dm_db_next_row(_db_query,item,conversions) + +DBQuery/proc/RowsAffected() return _dm_db_rows_affected(_db_query) + +DBQuery/proc/RowCount() return _dm_db_row_count(_db_query) + +DBQuery/proc/ErrorMsg() return _dm_db_error_msg(_db_query) + +DBQuery/proc/Columns() + if(!columns) + columns = _dm_db_columns(_db_query,/DBColumn) + return columns + +DBQuery/proc/GetRowData() + var/list/columns = Columns() + var/list/results + if(columns.len) + results = list() + for(var/C in columns) + results+=C + var/DBColumn/cur_col = columns[C] + results[C] = src.item[(cur_col.position+1)] + return results + +DBQuery/proc/Close() + item.len = 0 + columns = null + conversions = null + return _dm_db_close(_db_query) + +DBQuery/proc/Quote(str) + return db_connection.Quote(str) + +DBQuery/proc/SetConversion(column,conversion) + if(istext(column)) column = columns.Find(column) + if(!conversions) conversions = new/list(column) + else if(conversions.len < column) conversions.len = column + conversions[column] = conversion + + +DBColumn + var/name + var/table + var/position //1-based index into item data + var/sql_type + var/flags + var/length + var/max_length + +DBColumn/New(name_handler,table_handler,position_handler,type_handler,flag_handler,length_handler,max_length_handler) + src.name = name_handler + src.table = table_handler + src.position = position_handler + src.sql_type = type_handler + src.flags = flag_handler + src.length = length_handler + src.max_length = max_length_handler + return ..() + + +DBColumn/proc/SqlTypeName(type_handler=src.sql_type) + switch(type_handler) + if(TINYINT) return "TINYINT" + if(SMALLINT) return "SMALLINT" + if(MEDIUMINT) return "MEDIUMINT" + if(INTEGER) return "INTEGER" + if(BIGINT) return "BIGINT" + if(FLOAT) return "FLOAT" + if(DOUBLE) return "DOUBLE" + if(DATE) return "DATE" + if(DATETIME) return "DATETIME" + if(TIMESTAMP) return "TIMESTAMP" + if(TIME) return "TIME" + if(STRING) return "STRING" + if(BLOB) return "BLOB" + + +#undef Default_Cursor +#undef Client_Cursor +#undef Server_Cursor +#undef TEXT_CONV +#undef RSC_FILE_CONV +#undef NUMBER_CONV +#undef IS_NUMERIC +#undef IS_BINARY +#undef IS_NOT_NULL +#undef IS_PRIMARY_KEY +#undef IS_UNSIGNED +#undef TINYINT +#undef SMALLINT +#undef MEDIUMINT +#undef INTEGER +#undef BIGINT +#undef DECIMAL +#undef FLOAT +#undef DOUBLE +#undef DATE +#undef DATETIME +#undef TIMESTAMP +#undef TIME +#undef STRING +#undef BLOB diff --git a/code/defines/procs/statistics.dm b/code/defines/procs/statistics.dm index e68bb7ecc63..b2b643541ec 100644 --- a/code/defines/procs/statistics.dm +++ b/code/defines/procs/statistics.dm @@ -1,163 +1,163 @@ -/proc/sql_poll_players() - if(!config.sql_enabled) - return - var/playercount = 0 - for(var/mob/M in GLOB.player_list) - if(M.client) - playercount += 1 - establish_db_connection() - if(!dbcon.IsConnected()) - log_game("SQL ERROR during player polling. Failed to connect.") - else - var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") - var/DBQuery/query = dbcon.NewQuery("INSERT INTO [format_table_name("legacy_population")] (playercount, time) VALUES ([playercount], '[sqltime]')") - if(!query.Execute()) - var/err = query.ErrorMsg() - log_game("SQL ERROR during player polling. Error : \[[err]\]\n") - - -/proc/sql_poll_admins() - if(!config.sql_enabled) - return - var/admincount = GLOB.admins.len - establish_db_connection() - if(!dbcon.IsConnected()) - log_game("SQL ERROR during admin polling. Failed to connect.") - else - var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") - var/DBQuery/query = dbcon.NewQuery("INSERT INTO [format_table_name("legacy_population")] (admincount, time) VALUES ([admincount], '[sqltime]')") - if(!query.Execute()) - var/err = query.ErrorMsg() - log_game("SQL ERROR during admin polling. Error : \[[err]\]\n") - -/proc/sql_report_round_start() - // TODO - if(!config.sql_enabled) - return - -/proc/sql_report_round_end() - // TODO - if(!config.sql_enabled) - return - -/proc/sql_report_death(mob/living/carbon/human/H) - if(!config.sql_enabled) - return - if(!H) - return - if(!H.key || !H.mind) - return - - var/area/placeofdeath = get_area(H.loc) - var/podname = "Unknown" - if(placeofdeath) - podname = sanitizeSQL(placeofdeath.name) - - var/sqlname = sanitizeSQL(H.real_name) - var/sqlkey = sanitizeSQL(H.key) - var/sqlpod = sanitizeSQL(podname) - var/sqlspecial = sanitizeSQL(H.mind.special_role) - var/sqljob = sanitizeSQL(H.mind.assigned_role) - var/laname - var/lakey - if(H.lastattacker) - laname = sanitizeSQL(H.lastattacker:real_name) - lakey = sanitizeSQL(H.lastattacker:key) - var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") - var/coord = "[H.x], [H.y], [H.z]" -// to_chat(world, "INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.getBrainLoss()], [H.getOxyLoss()])") - establish_db_connection() - if(!dbcon.IsConnected()) - log_game("SQL ERROR during death reporting. Failed to connect.") - else - var/DBQuery/query = dbcon.NewQuery("INSERT INTO [format_table_name("death")] (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.getBruteLoss()], [H.getFireLoss()], [H.getBrainLoss()], [H.getOxyLoss()], '[coord]')") - if(!query.Execute()) - var/err = query.ErrorMsg() - log_game("SQL ERROR during death reporting. Error : \[[err]\]\n") - - -/proc/sql_report_cyborg_death(mob/living/silicon/robot/H) - if(!config.sql_enabled) - return - if(!H) - return - if(!H.key || !H.mind) - return - - var/turf/T = H.loc - var/area/placeofdeath = get_area(T.loc) - var/podname = sanitizeSQL(placeofdeath.name) - - var/sqlname = sanitizeSQL(H.real_name) - var/sqlkey = sanitizeSQL(H.key) - var/sqlpod = sanitizeSQL(podname) - var/sqlspecial = sanitizeSQL(H.mind.special_role) - var/sqljob = sanitizeSQL(H.mind.assigned_role) - var/laname - var/lakey - if(H.lastattacker) - laname = sanitizeSQL(H.lastattacker:real_name) - lakey = sanitizeSQL(H.lastattacker:key) - var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") - var/coord = "[H.x], [H.y], [H.z]" -// to_chat(world, "INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()])") - establish_db_connection() - if(!dbcon.IsConnected()) - log_game("SQL ERROR during death reporting. Failed to connect.") - else - var/DBQuery/query = dbcon.NewQuery("INSERT INTO [format_table_name("death")] (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.getBruteLoss()], [H.getFireLoss()], [H.getBrainLoss()], [H.getOxyLoss()], '[coord]')") - if(!query.Execute()) - var/err = query.ErrorMsg() - log_game("SQL ERROR during death reporting. Error : \[[err]\]\n") - -/proc/statistic_cycle() - if(!config.sql_enabled) - return - while(1) - sql_poll_players() - sleep(600) - sql_poll_admins() - sleep(6000) //Poll every ten minutes - -//This proc is used for feedback. It is executed at round end. -/proc/sql_commit_feedback() - if(!blackbox) - log_game("Round ended without a blackbox recorder. No feedback was sent to the database.") - return - - //content is a list of lists. Each item in the list is a list with two fields, a variable name and a value. Items MUST only have these two values. - var/list/datum/feedback_variable/content = blackbox.get_round_feedback() - - if(!content) - log_game("Round ended without any feedback being generated. No feedback was sent to the database.") - return - - establish_db_connection() - if(!dbcon.IsConnected()) - log_game("SQL ERROR during feedback reporting. Failed to connect.") - else - - var/DBQuery/max_query = dbcon.NewQuery("SELECT MAX(roundid) AS max_round_id FROM [format_table_name("feedback")]") - max_query.Execute() - - var/newroundid - - while(max_query.NextRow()) - newroundid = max_query.item[1] - - if(!(isnum(newroundid))) - newroundid = text2num(newroundid) - - if(isnum(newroundid)) - newroundid++ - else - newroundid = 1 - - for(var/datum/feedback_variable/item in content) - var/variable = item.get_variable() - var/value = item.get_value() - - var/DBQuery/query = dbcon.NewQuery("INSERT INTO [format_table_name("feedback")] (id, roundid, time, variable, value) VALUES (null, [newroundid], Now(), '[variable]', '[value]')") - if(!query.Execute()) - var/err = query.ErrorMsg() - log_game("SQL ERROR during feedback reporting. Error : \[[err]\]\n") +/proc/sql_poll_players() + if(!config.sql_enabled) + return + var/playercount = 0 + for(var/mob/M in GLOB.player_list) + if(M.client) + playercount += 1 + establish_db_connection() + if(!dbcon.IsConnected()) + log_game("SQL ERROR during player polling. Failed to connect.") + else + var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") + var/DBQuery/query = dbcon.NewQuery("INSERT INTO [format_table_name("legacy_population")] (playercount, time) VALUES ([playercount], '[sqltime]')") + if(!query.Execute()) + var/err = query.ErrorMsg() + log_game("SQL ERROR during player polling. Error : \[[err]\]\n") + + +/proc/sql_poll_admins() + if(!config.sql_enabled) + return + var/admincount = GLOB.admins.len + establish_db_connection() + if(!dbcon.IsConnected()) + log_game("SQL ERROR during admin polling. Failed to connect.") + else + var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") + var/DBQuery/query = dbcon.NewQuery("INSERT INTO [format_table_name("legacy_population")] (admincount, time) VALUES ([admincount], '[sqltime]')") + if(!query.Execute()) + var/err = query.ErrorMsg() + log_game("SQL ERROR during admin polling. Error : \[[err]\]\n") + +/proc/sql_report_round_start() + // TODO + if(!config.sql_enabled) + return + +/proc/sql_report_round_end() + // TODO + if(!config.sql_enabled) + return + +/proc/sql_report_death(mob/living/carbon/human/H) + if(!config.sql_enabled) + return + if(!H) + return + if(!H.key || !H.mind) + return + + var/area/placeofdeath = get_area(H.loc) + var/podname = "Unknown" + if(placeofdeath) + podname = sanitizeSQL(placeofdeath.name) + + var/sqlname = sanitizeSQL(H.real_name) + var/sqlkey = sanitizeSQL(H.key) + var/sqlpod = sanitizeSQL(podname) + var/sqlspecial = sanitizeSQL(H.mind.special_role) + var/sqljob = sanitizeSQL(H.mind.assigned_role) + var/laname + var/lakey + if(H.lastattacker) + laname = sanitizeSQL(H.lastattacker:real_name) + lakey = sanitizeSQL(H.lastattacker:key) + var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") + var/coord = "[H.x], [H.y], [H.z]" +// to_chat(world, "INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.getBrainLoss()], [H.getOxyLoss()])") + establish_db_connection() + if(!dbcon.IsConnected()) + log_game("SQL ERROR during death reporting. Failed to connect.") + else + var/DBQuery/query = dbcon.NewQuery("INSERT INTO [format_table_name("death")] (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.getBruteLoss()], [H.getFireLoss()], [H.getBrainLoss()], [H.getOxyLoss()], '[coord]')") + if(!query.Execute()) + var/err = query.ErrorMsg() + log_game("SQL ERROR during death reporting. Error : \[[err]\]\n") + + +/proc/sql_report_cyborg_death(mob/living/silicon/robot/H) + if(!config.sql_enabled) + return + if(!H) + return + if(!H.key || !H.mind) + return + + var/turf/T = H.loc + var/area/placeofdeath = get_area(T.loc) + var/podname = sanitizeSQL(placeofdeath.name) + + var/sqlname = sanitizeSQL(H.real_name) + var/sqlkey = sanitizeSQL(H.key) + var/sqlpod = sanitizeSQL(podname) + var/sqlspecial = sanitizeSQL(H.mind.special_role) + var/sqljob = sanitizeSQL(H.mind.assigned_role) + var/laname + var/lakey + if(H.lastattacker) + laname = sanitizeSQL(H.lastattacker:real_name) + lakey = sanitizeSQL(H.lastattacker:key) + var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") + var/coord = "[H.x], [H.y], [H.z]" +// to_chat(world, "INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()])") + establish_db_connection() + if(!dbcon.IsConnected()) + log_game("SQL ERROR during death reporting. Failed to connect.") + else + var/DBQuery/query = dbcon.NewQuery("INSERT INTO [format_table_name("death")] (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.getBruteLoss()], [H.getFireLoss()], [H.getBrainLoss()], [H.getOxyLoss()], '[coord]')") + if(!query.Execute()) + var/err = query.ErrorMsg() + log_game("SQL ERROR during death reporting. Error : \[[err]\]\n") + +/proc/statistic_cycle() + if(!config.sql_enabled) + return + while(1) + sql_poll_players() + sleep(600) + sql_poll_admins() + sleep(6000) //Poll every ten minutes + +//This proc is used for feedback. It is executed at round end. +/proc/sql_commit_feedback() + if(!blackbox) + log_game("Round ended without a blackbox recorder. No feedback was sent to the database.") + return + + //content is a list of lists. Each item in the list is a list with two fields, a variable name and a value. Items MUST only have these two values. + var/list/datum/feedback_variable/content = blackbox.get_round_feedback() + + if(!content) + log_game("Round ended without any feedback being generated. No feedback was sent to the database.") + return + + establish_db_connection() + if(!dbcon.IsConnected()) + log_game("SQL ERROR during feedback reporting. Failed to connect.") + else + + var/DBQuery/max_query = dbcon.NewQuery("SELECT MAX(roundid) AS max_round_id FROM [format_table_name("feedback")]") + max_query.Execute() + + var/newroundid + + while(max_query.NextRow()) + newroundid = max_query.item[1] + + if(!(isnum(newroundid))) + newroundid = text2num(newroundid) + + if(isnum(newroundid)) + newroundid++ + else + newroundid = 1 + + for(var/datum/feedback_variable/item in content) + var/variable = item.get_variable() + var/value = item.get_value() + + var/DBQuery/query = dbcon.NewQuery("INSERT INTO [format_table_name("feedback")] (id, roundid, time, variable, value) VALUES (null, [newroundid], Now(), '[variable]', '[value]')") + if(!query.Execute()) + var/err = query.ErrorMsg() + log_game("SQL ERROR during feedback reporting. Error : \[[err]\]\n") diff --git a/code/defines/vox_sounds.dm b/code/defines/vox_sounds.dm index 794a259ea4e..bb155337502 100644 --- a/code/defines/vox_sounds.dm +++ b/code/defines/vox_sounds.dm @@ -1267,4 +1267,4 @@ var/list/vox_sounds = list("," = 'sound/vox_fem/,.ogg', "zero" = 'sound/vox_fem/zero.ogg', "zone" = 'sound/vox_fem/zone.ogg', "zulu" = 'sound/vox_fem/zulu.ogg' -) \ No newline at end of file +) diff --git a/code/game/area/Dynamic areas.dm b/code/game/area/Dynamic areas.dm index c6d1154847b..10957e11d2a 100644 --- a/code/game/area/Dynamic areas.dm +++ b/code/game/area/Dynamic areas.dm @@ -41,4 +41,4 @@ match_width = 5 match_height = 4 requires_power = FALSE - dynamic_lighting = DYNAMIC_LIGHTING_FORCED \ No newline at end of file + dynamic_lighting = DYNAMIC_LIGHTING_FORCED diff --git a/code/game/area/ai_monitored.dm b/code/game/area/ai_monitored.dm index 0bc7b5f001b..2ca607921ff 100644 --- a/code/game/area/ai_monitored.dm +++ b/code/game/area/ai_monitored.dm @@ -1,26 +1,26 @@ -/area/ai_monitored - name = "AI Monitored Area" - var/obj/machinery/camera/motioncamera = null - - -/area/ai_monitored/New() - ..() - // locate and store the motioncamera - spawn (20) // spawn on a delay to let turfs/objs load - for(var/obj/machinery/camera/M in src) - if(M.isMotion()) - motioncamera = M - M.area_motion = src - return - return - -/area/ai_monitored/Entered(atom/movable/O) - ..() - if(ismob(O) && motioncamera) - motioncamera.newTarget(O) - -/area/ai_monitored/Exited(atom/movable/O) - if(ismob(O) && motioncamera) - motioncamera.lostTarget(O) - - +/area/ai_monitored + name = "AI Monitored Area" + var/obj/machinery/camera/motioncamera = null + + +/area/ai_monitored/New() + ..() + // locate and store the motioncamera + spawn (20) // spawn on a delay to let turfs/objs load + for(var/obj/machinery/camera/M in src) + if(M.isMotion()) + motioncamera = M + M.area_motion = src + return + return + +/area/ai_monitored/Entered(atom/movable/O) + ..() + if(ismob(O) && motioncamera) + motioncamera.newTarget(O) + +/area/ai_monitored/Exited(atom/movable/O) + if(ismob(O) && motioncamera) + motioncamera.lostTarget(O) + + diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm index 26dc6ddbf56..dbc0a8c567b 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -1,453 +1,453 @@ -/area - var/fire = null - var/atmosalm = ATMOS_ALARM_NONE - var/poweralm = TRUE - var/party = null - var/report_alerts = TRUE // Should atmos alerts notify the AI/computers - level = null - name = "Space" - icon = 'icons/turf/areas.dmi' - icon_state = "unknown" - layer = AREA_LAYER - plane = BLACKNESS_PLANE //Keeping this on the default plane, GAME_PLANE, will make area overlays fail to render on FLOOR_PLANE. - luminosity = 0 - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - invisibility = INVISIBILITY_LIGHTING - var/valid_territory = TRUE //used for cult summoning areas on station zlevel - var/map_name // Set in New(); preserves the name set by the map maker, even if renamed by the Blueprints. - var/lightswitch = TRUE - - var/eject = null - - var/debug = FALSE - var/requires_power = TRUE - var/always_unpowered = FALSE //this gets overriden to 1 for space in area/New() - - var/power_equip = TRUE - var/power_light = TRUE - var/power_environ = TRUE - var/music = null - var/used_equip = FALSE - var/used_light = FALSE - var/used_environ = FALSE - var/static_equip - var/static_light = FALSE - var/static_environ - - var/has_gravity = TRUE - var/list/apc = list() - var/no_air = null - - var/air_doors_activated = FALSE - - var/tele_proof = FALSE - var/no_teleportlocs = FALSE - - var/outdoors = FALSE //For space, the asteroid, lavaland, etc. Used with blueprints to determine if we are adding a new area (vs editing a station room) - var/xenobiology_compatible = FALSE //Can the Xenobio management console transverse this area by default? - var/nad_allowed = FALSE //is the station NAD allowed on this area? - - // This var is used with the maploader (modules/awaymissions/maploader/reader.dm) - // if this is 1, when used in a map snippet, this will instantiate a unique - // area from any other instances already present (meaning you can have - // separate APCs, and so on) - var/there_can_be_many = FALSE - - var/global/global_uid = 0 - var/uid - - var/list/ambientsounds = list('sound/ambience/ambigen1.ogg','sound/ambience/ambigen3.ogg',\ - 'sound/ambience/ambigen4.ogg','sound/ambience/ambigen5.ogg',\ - 'sound/ambience/ambigen6.ogg','sound/ambience/ambigen7.ogg',\ - 'sound/ambience/ambigen8.ogg','sound/ambience/ambigen9.ogg',\ - 'sound/ambience/ambigen10.ogg','sound/ambience/ambigen11.ogg',\ - 'sound/ambience/ambigen12.ogg','sound/ambience/ambigen14.ogg') - - var/fast_despawn = FALSE - var/can_get_auto_cryod = TRUE - var/hide_attacklogs = FALSE // For areas such as thunderdome, lavaland syndiebase, etc which generate a lot of spammy attacklogs. Reduces log priority. - - var/parallax_movedir = 0 - var/moving = FALSE - -/area/Initialize(mapload) - GLOB.all_areas += src - icon_state = "" - layer = AREA_LAYER - uid = ++global_uid - - map_name = name // Save the initial (the name set in the map) name of the area. - - if(requires_power) - luminosity = 0 - else - power_light = TRUE - power_equip = TRUE - power_environ = TRUE - - if(dynamic_lighting == DYNAMIC_LIGHTING_FORCED) - dynamic_lighting = DYNAMIC_LIGHTING_ENABLED - luminosity = 0 - else if(dynamic_lighting != DYNAMIC_LIGHTING_IFSTARLIGHT) - dynamic_lighting = DYNAMIC_LIGHTING_DISABLED - if(dynamic_lighting == DYNAMIC_LIGHTING_IFSTARLIGHT) - dynamic_lighting = config.starlight ? DYNAMIC_LIGHTING_ENABLED : DYNAMIC_LIGHTING_DISABLED - - . = ..() - - blend_mode = BLEND_MULTIPLY // Putting this in the constructor so that it stops the icons being screwed up in the map editor. - - if(!IS_DYNAMIC_LIGHTING(src)) - add_overlay(/obj/effect/fullbright) - - reg_in_areas_in_z() - - return INITIALIZE_HINT_LATELOAD - -/area/LateInitialize() - . = ..() - power_change() // all machines set to current power level, also updates lighting icon - -/area/proc/reg_in_areas_in_z() - if(contents.len) - var/list/areas_in_z = space_manager.areas_in_z - var/z - for(var/i in 1 to contents.len) - var/atom/thing = contents[i] - if(!thing) - continue - z = thing.z - break - if(!z) - WARNING("No z found for [src]") - return - if(!areas_in_z["[z]"]) - areas_in_z["[z]"] = list() - areas_in_z["[z]"] += src - -/area/proc/get_cameras() - var/list/cameras = list() - for(var/obj/machinery/camera/C in src) - cameras += C - return cameras - - -/area/proc/atmosalert(danger_level, var/alarm_source, var/force = FALSE) - if(report_alerts) - if(danger_level == ATMOS_ALARM_NONE) - SSalarms.atmosphere_alarm.clearAlarm(src, alarm_source) - else - SSalarms.atmosphere_alarm.triggerAlarm(src, alarm_source, severity = danger_level) - - //Check all the alarms before lowering atmosalm. Raising is perfectly fine. If force = 1 we don't care. - for(var/obj/machinery/alarm/AA in src) - if(!(AA.stat & (NOPOWER|BROKEN)) && !AA.shorted && AA.report_danger_level && !force) - danger_level = max(danger_level, AA.danger_level) - - if(danger_level != atmosalm) - if(danger_level < ATMOS_ALARM_WARNING && atmosalm >= ATMOS_ALARM_WARNING) - //closing the doors on red and opening on green provides a bit of hysteresis that will hopefully prevent fire doors from opening and closing repeatedly due to noise - air_doors_open() - else if(danger_level >= ATMOS_ALARM_DANGER && atmosalm < ATMOS_ALARM_DANGER) - air_doors_close() - - atmosalm = danger_level - for(var/obj/machinery/alarm/AA in src) - AA.update_icon() - - air_alarm_repository.update_cache(src) - return 1 - air_alarm_repository.update_cache(src) - return 0 - -/area/proc/air_doors_close() - if(!air_doors_activated) - air_doors_activated = TRUE - for(var/obj/machinery/door/firedoor/D in src) - if(!D.welded) - D.activate_alarm() - if(D.operating) - D.nextstate = FD_CLOSED - else if(!D.density) - spawn(0) - D.close() - -/area/proc/air_doors_open() - if(air_doors_activated) - air_doors_activated = FALSE - for(var/obj/machinery/door/firedoor/D in src) - if(!D.welded) - D.deactivate_alarm() - if(D.operating) - D.nextstate = OPEN - else if(D.density) - spawn(0) - D.open() - - -/area/proc/fire_alert() - if(!fire) - fire = 1 //used for firedoor checks - updateicon() - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - air_doors_close() - -/area/proc/fire_reset() - if(fire) - fire = 0 //used for firedoor checks - updateicon() - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - air_doors_open() - - return - -/area/proc/burglaralert(var/obj/trigger) - if(always_unpowered == 1) //no burglar alarms in space/asteroid - return - - //Trigger alarm effect - set_fire_alarm_effect() - - //Lockdown airlocks - for(var/obj/machinery/door/airlock/A in src) - spawn(0) - A.close() - if(A.density) - A.lock() - - SSalarms.burglar_alarm.triggerAlarm(src, trigger) - spawn(600) - SSalarms.burglar_alarm.clearAlarm(src, trigger) - -/area/proc/set_fire_alarm_effect() - fire = 1 - updateicon() - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - -/area/proc/readyalert() - if(!eject) - eject = 1 - updateicon() - -/area/proc/readyreset() - if(eject) - eject = 0 - updateicon() - -/area/proc/partyalert() - if(!party) - party = 1 - updateicon() - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - -/area/proc/partyreset() - if(party) - party = 0 - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - updateicon() - -/area/proc/updateicon() - if((fire || eject || party) && (!requires_power||power_environ))//If it doesn't require power, can still activate this proc. - if(fire && !eject && !party) - icon_state = "red" - else if(!fire && eject && !party) - icon_state = "red" - else if(party && !fire && !eject) - icon_state = "party" - else - icon_state = "blue-red" - else - var/weather_icon - for(var/V in SSweather.processing) - var/datum/weather/W = V - if(W.stage != END_STAGE && (src in W.impacted_areas)) - W.update_areas() - weather_icon = TRUE - if(!weather_icon) - icon_state = null - -/area/space/updateicon() - icon_state = null - -/* -#define EQUIP 1 -#define LIGHT 2 -#define ENVIRON 3 -*/ - -/area/proc/powered(var/chan) // return true if the area has power to given channel - - if(!requires_power) - return 1 - if(always_unpowered) - return 0 - switch(chan) - if(EQUIP) - return power_equip - if(LIGHT) - return power_light - if(ENVIRON) - return power_environ - - return 0 - -/area/space/powered(chan) //Nope.avi - return 0 - -// called when power status changes - -/area/proc/power_change() - for(var/obj/machinery/M in src) // for each machine in the area - M.power_change() // reverify power status (to update icons etc.) - updateicon() - -/area/proc/usage(var/chan) - var/used = 0 - switch(chan) - if(LIGHT) - used += used_light - if(EQUIP) - used += used_equip - if(ENVIRON) - used += used_environ - if(TOTAL) - used += used_light + used_equip + used_environ - if(STATIC_EQUIP) - used += static_equip - if(STATIC_LIGHT) - used += static_light - if(STATIC_ENVIRON) - used += static_environ - return used - -/area/proc/addStaticPower(value, powerchannel) - switch(powerchannel) - if(STATIC_EQUIP) - static_equip += value - if(STATIC_LIGHT) - static_light += value - if(STATIC_ENVIRON) - static_environ += value - -/area/proc/clear_usage() - - used_equip = 0 - used_light = 0 - used_environ = 0 - -/area/proc/use_power(var/amount, var/chan) - switch(chan) - if(EQUIP) - used_equip += amount - if(LIGHT) - used_light += amount - if(ENVIRON) - used_environ += amount - -/area/proc/use_battery_power(var/amount, var/chan) - switch(chan) - if(EQUIP) - used_equip += amount - if(LIGHT) - used_light += amount - if(ENVIRON) - used_environ += amount - - -/area/Entered(A) - var/area/newarea - var/area/oldarea - - if(istype(A,/mob)) - var/mob/M=A - - if(!M.lastarea) - M.lastarea = get_area(M) - newarea = get_area(M) - oldarea = M.lastarea - - if(newarea==oldarea) return - - M.lastarea = src - - if(!istype(A,/mob/living)) return - - var/mob/living/L = A - if(!L.ckey) return - if((oldarea.has_gravity == 0) && (newarea.has_gravity == 1) && (L.m_intent == MOVE_INTENT_RUN)) // Being ready when you change areas gives you a chance to avoid falling all together. - thunk(L) - - // Ambience goes down here -- make sure to list each area seperately for ease of adding things in later, thanks! Note: areas adjacent to each other should have the same sounds to prevent cutoff when possible.- LastyScratch - if(L && L.client && !L.client.ambience_playing && (L.client.prefs.sound & SOUND_BUZZ)) //split off the white noise from the rest of the ambience because of annoyance complaints - Kluys - L.client.ambience_playing = 1 - L << sound('sound/ambience/shipambience.ogg', repeat = 1, wait = 0, volume = 35, channel = CHANNEL_BUZZ) - else if(L && L.client && !(L.client.prefs.sound & SOUND_BUZZ)) - L.client.ambience_playing = 0 - - if(prob(35) && L && L.client && (L.client.prefs.sound & SOUND_AMBIENCE)) - var/sound = pick(ambientsounds) - - if(!L.client.played) - L << sound(sound, repeat = 0, wait = 0, volume = 25, channel = CHANNEL_AMBIENCE) - L.client.played = 1 - spawn(600) //ewww - this is very very bad - if(L && L.client) - L.client.played = 0 - -/area/proc/gravitychange(var/gravitystate = 0, var/area/A) - A.has_gravity = gravitystate - - if(gravitystate) - for(var/mob/living/carbon/human/M in A) - thunk(M) - -/area/proc/thunk(var/mob/living/carbon/human/M) - if(istype(M,/mob/living/carbon/human/)) // Only humans can wear magboots, so we give them a chance to. - if(istype(M.shoes, /obj/item/clothing/shoes/magboots) && (M.shoes.flags & NOSLIP)) - return - - if(M.buckled) //Cam't fall down if you are buckled - return - - if(istype(get_turf(M), /turf/space)) // Can't fall onto nothing. - return - - if((istype(M,/mob/living/carbon/human/)) && (M.m_intent == MOVE_INTENT_RUN)) - M.Stun(5) - M.Weaken(5) - - else if(istype(M,/mob/living/carbon/human/)) - M.Stun(2) - M.Weaken(2) - - - to_chat(M, "Gravity!") - -/proc/has_gravity(atom/AT, turf/T) - if(!T) - T = get_turf(AT) - var/area/A = get_area(T) - if(istype(T, /turf/space)) // Turf never has gravity - return 0 - else if(A && A.has_gravity) // Areas which always has gravity - return 1 - else - // There's a gravity generator on our z level - // This would do well when integrated with the z level manager - if(T && gravity_generators["[T.z]"] && length(gravity_generators["[T.z]"])) - return 1 - return 0 - -/area/proc/prison_break() - for(var/obj/machinery/power/apc/temp_apc in src) - temp_apc.overload_lighting(70) - for(var/obj/machinery/door/airlock/temp_airlock in src) - temp_airlock.prison_open() - for(var/obj/machinery/door/window/temp_windoor in src) - temp_windoor.open() - -/area/AllowDrop() - CRASH("Bad op: area/AllowDrop() called") - -/area/drop_location() - CRASH("Bad op: area/drop_location() called") +/area + var/fire = null + var/atmosalm = ATMOS_ALARM_NONE + var/poweralm = TRUE + var/party = null + var/report_alerts = TRUE // Should atmos alerts notify the AI/computers + level = null + name = "Space" + icon = 'icons/turf/areas.dmi' + icon_state = "unknown" + layer = AREA_LAYER + plane = BLACKNESS_PLANE //Keeping this on the default plane, GAME_PLANE, will make area overlays fail to render on FLOOR_PLANE. + luminosity = 0 + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + invisibility = INVISIBILITY_LIGHTING + var/valid_territory = TRUE //used for cult summoning areas on station zlevel + var/map_name // Set in New(); preserves the name set by the map maker, even if renamed by the Blueprints. + var/lightswitch = TRUE + + var/eject = null + + var/debug = FALSE + var/requires_power = TRUE + var/always_unpowered = FALSE //this gets overriden to 1 for space in area/New() + + var/power_equip = TRUE + var/power_light = TRUE + var/power_environ = TRUE + var/music = null + var/used_equip = FALSE + var/used_light = FALSE + var/used_environ = FALSE + var/static_equip + var/static_light = FALSE + var/static_environ + + var/has_gravity = TRUE + var/list/apc = list() + var/no_air = null + + var/air_doors_activated = FALSE + + var/tele_proof = FALSE + var/no_teleportlocs = FALSE + + var/outdoors = FALSE //For space, the asteroid, lavaland, etc. Used with blueprints to determine if we are adding a new area (vs editing a station room) + var/xenobiology_compatible = FALSE //Can the Xenobio management console transverse this area by default? + var/nad_allowed = FALSE //is the station NAD allowed on this area? + + // This var is used with the maploader (modules/awaymissions/maploader/reader.dm) + // if this is 1, when used in a map snippet, this will instantiate a unique + // area from any other instances already present (meaning you can have + // separate APCs, and so on) + var/there_can_be_many = FALSE + + var/global/global_uid = 0 + var/uid + + var/list/ambientsounds = list('sound/ambience/ambigen1.ogg','sound/ambience/ambigen3.ogg',\ + 'sound/ambience/ambigen4.ogg','sound/ambience/ambigen5.ogg',\ + 'sound/ambience/ambigen6.ogg','sound/ambience/ambigen7.ogg',\ + 'sound/ambience/ambigen8.ogg','sound/ambience/ambigen9.ogg',\ + 'sound/ambience/ambigen10.ogg','sound/ambience/ambigen11.ogg',\ + 'sound/ambience/ambigen12.ogg','sound/ambience/ambigen14.ogg') + + var/fast_despawn = FALSE + var/can_get_auto_cryod = TRUE + var/hide_attacklogs = FALSE // For areas such as thunderdome, lavaland syndiebase, etc which generate a lot of spammy attacklogs. Reduces log priority. + + var/parallax_movedir = 0 + var/moving = FALSE + +/area/Initialize(mapload) + GLOB.all_areas += src + icon_state = "" + layer = AREA_LAYER + uid = ++global_uid + + map_name = name // Save the initial (the name set in the map) name of the area. + + if(requires_power) + luminosity = 0 + else + power_light = TRUE + power_equip = TRUE + power_environ = TRUE + + if(dynamic_lighting == DYNAMIC_LIGHTING_FORCED) + dynamic_lighting = DYNAMIC_LIGHTING_ENABLED + luminosity = 0 + else if(dynamic_lighting != DYNAMIC_LIGHTING_IFSTARLIGHT) + dynamic_lighting = DYNAMIC_LIGHTING_DISABLED + if(dynamic_lighting == DYNAMIC_LIGHTING_IFSTARLIGHT) + dynamic_lighting = config.starlight ? DYNAMIC_LIGHTING_ENABLED : DYNAMIC_LIGHTING_DISABLED + + . = ..() + + blend_mode = BLEND_MULTIPLY // Putting this in the constructor so that it stops the icons being screwed up in the map editor. + + if(!IS_DYNAMIC_LIGHTING(src)) + add_overlay(/obj/effect/fullbright) + + reg_in_areas_in_z() + + return INITIALIZE_HINT_LATELOAD + +/area/LateInitialize() + . = ..() + power_change() // all machines set to current power level, also updates lighting icon + +/area/proc/reg_in_areas_in_z() + if(contents.len) + var/list/areas_in_z = space_manager.areas_in_z + var/z + for(var/i in 1 to contents.len) + var/atom/thing = contents[i] + if(!thing) + continue + z = thing.z + break + if(!z) + WARNING("No z found for [src]") + return + if(!areas_in_z["[z]"]) + areas_in_z["[z]"] = list() + areas_in_z["[z]"] += src + +/area/proc/get_cameras() + var/list/cameras = list() + for(var/obj/machinery/camera/C in src) + cameras += C + return cameras + + +/area/proc/atmosalert(danger_level, var/alarm_source, var/force = FALSE) + if(report_alerts) + if(danger_level == ATMOS_ALARM_NONE) + SSalarms.atmosphere_alarm.clearAlarm(src, alarm_source) + else + SSalarms.atmosphere_alarm.triggerAlarm(src, alarm_source, severity = danger_level) + + //Check all the alarms before lowering atmosalm. Raising is perfectly fine. If force = 1 we don't care. + for(var/obj/machinery/alarm/AA in src) + if(!(AA.stat & (NOPOWER|BROKEN)) && !AA.shorted && AA.report_danger_level && !force) + danger_level = max(danger_level, AA.danger_level) + + if(danger_level != atmosalm) + if(danger_level < ATMOS_ALARM_WARNING && atmosalm >= ATMOS_ALARM_WARNING) + //closing the doors on red and opening on green provides a bit of hysteresis that will hopefully prevent fire doors from opening and closing repeatedly due to noise + air_doors_open() + else if(danger_level >= ATMOS_ALARM_DANGER && atmosalm < ATMOS_ALARM_DANGER) + air_doors_close() + + atmosalm = danger_level + for(var/obj/machinery/alarm/AA in src) + AA.update_icon() + + air_alarm_repository.update_cache(src) + return 1 + air_alarm_repository.update_cache(src) + return 0 + +/area/proc/air_doors_close() + if(!air_doors_activated) + air_doors_activated = TRUE + for(var/obj/machinery/door/firedoor/D in src) + if(!D.welded) + D.activate_alarm() + if(D.operating) + D.nextstate = FD_CLOSED + else if(!D.density) + spawn(0) + D.close() + +/area/proc/air_doors_open() + if(air_doors_activated) + air_doors_activated = FALSE + for(var/obj/machinery/door/firedoor/D in src) + if(!D.welded) + D.deactivate_alarm() + if(D.operating) + D.nextstate = OPEN + else if(D.density) + spawn(0) + D.open() + + +/area/proc/fire_alert() + if(!fire) + fire = 1 //used for firedoor checks + updateicon() + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + air_doors_close() + +/area/proc/fire_reset() + if(fire) + fire = 0 //used for firedoor checks + updateicon() + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + air_doors_open() + + return + +/area/proc/burglaralert(var/obj/trigger) + if(always_unpowered == 1) //no burglar alarms in space/asteroid + return + + //Trigger alarm effect + set_fire_alarm_effect() + + //Lockdown airlocks + for(var/obj/machinery/door/airlock/A in src) + spawn(0) + A.close() + if(A.density) + A.lock() + + SSalarms.burglar_alarm.triggerAlarm(src, trigger) + spawn(600) + SSalarms.burglar_alarm.clearAlarm(src, trigger) + +/area/proc/set_fire_alarm_effect() + fire = 1 + updateicon() + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + +/area/proc/readyalert() + if(!eject) + eject = 1 + updateicon() + +/area/proc/readyreset() + if(eject) + eject = 0 + updateicon() + +/area/proc/partyalert() + if(!party) + party = 1 + updateicon() + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + +/area/proc/partyreset() + if(party) + party = 0 + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + updateicon() + +/area/proc/updateicon() + if((fire || eject || party) && (!requires_power||power_environ))//If it doesn't require power, can still activate this proc. + if(fire && !eject && !party) + icon_state = "red" + else if(!fire && eject && !party) + icon_state = "red" + else if(party && !fire && !eject) + icon_state = "party" + else + icon_state = "blue-red" + else + var/weather_icon + for(var/V in SSweather.processing) + var/datum/weather/W = V + if(W.stage != END_STAGE && (src in W.impacted_areas)) + W.update_areas() + weather_icon = TRUE + if(!weather_icon) + icon_state = null + +/area/space/updateicon() + icon_state = null + +/* +#define EQUIP 1 +#define LIGHT 2 +#define ENVIRON 3 +*/ + +/area/proc/powered(var/chan) // return true if the area has power to given channel + + if(!requires_power) + return 1 + if(always_unpowered) + return 0 + switch(chan) + if(EQUIP) + return power_equip + if(LIGHT) + return power_light + if(ENVIRON) + return power_environ + + return 0 + +/area/space/powered(chan) //Nope.avi + return 0 + +// called when power status changes + +/area/proc/power_change() + for(var/obj/machinery/M in src) // for each machine in the area + M.power_change() // reverify power status (to update icons etc.) + updateicon() + +/area/proc/usage(var/chan) + var/used = 0 + switch(chan) + if(LIGHT) + used += used_light + if(EQUIP) + used += used_equip + if(ENVIRON) + used += used_environ + if(TOTAL) + used += used_light + used_equip + used_environ + if(STATIC_EQUIP) + used += static_equip + if(STATIC_LIGHT) + used += static_light + if(STATIC_ENVIRON) + used += static_environ + return used + +/area/proc/addStaticPower(value, powerchannel) + switch(powerchannel) + if(STATIC_EQUIP) + static_equip += value + if(STATIC_LIGHT) + static_light += value + if(STATIC_ENVIRON) + static_environ += value + +/area/proc/clear_usage() + + used_equip = 0 + used_light = 0 + used_environ = 0 + +/area/proc/use_power(var/amount, var/chan) + switch(chan) + if(EQUIP) + used_equip += amount + if(LIGHT) + used_light += amount + if(ENVIRON) + used_environ += amount + +/area/proc/use_battery_power(var/amount, var/chan) + switch(chan) + if(EQUIP) + used_equip += amount + if(LIGHT) + used_light += amount + if(ENVIRON) + used_environ += amount + + +/area/Entered(A) + var/area/newarea + var/area/oldarea + + if(istype(A,/mob)) + var/mob/M=A + + if(!M.lastarea) + M.lastarea = get_area(M) + newarea = get_area(M) + oldarea = M.lastarea + + if(newarea==oldarea) return + + M.lastarea = src + + if(!istype(A,/mob/living)) return + + var/mob/living/L = A + if(!L.ckey) return + if((oldarea.has_gravity == 0) && (newarea.has_gravity == 1) && (L.m_intent == MOVE_INTENT_RUN)) // Being ready when you change areas gives you a chance to avoid falling all together. + thunk(L) + + // Ambience goes down here -- make sure to list each area seperately for ease of adding things in later, thanks! Note: areas adjacent to each other should have the same sounds to prevent cutoff when possible.- LastyScratch + if(L && L.client && !L.client.ambience_playing && (L.client.prefs.sound & SOUND_BUZZ)) //split off the white noise from the rest of the ambience because of annoyance complaints - Kluys + L.client.ambience_playing = 1 + L << sound('sound/ambience/shipambience.ogg', repeat = 1, wait = 0, volume = 35, channel = CHANNEL_BUZZ) + else if(L && L.client && !(L.client.prefs.sound & SOUND_BUZZ)) + L.client.ambience_playing = 0 + + if(prob(35) && L && L.client && (L.client.prefs.sound & SOUND_AMBIENCE)) + var/sound = pick(ambientsounds) + + if(!L.client.played) + L << sound(sound, repeat = 0, wait = 0, volume = 25, channel = CHANNEL_AMBIENCE) + L.client.played = 1 + spawn(600) //ewww - this is very very bad + if(L && L.client) + L.client.played = 0 + +/area/proc/gravitychange(var/gravitystate = 0, var/area/A) + A.has_gravity = gravitystate + + if(gravitystate) + for(var/mob/living/carbon/human/M in A) + thunk(M) + +/area/proc/thunk(var/mob/living/carbon/human/M) + if(istype(M,/mob/living/carbon/human/)) // Only humans can wear magboots, so we give them a chance to. + if(istype(M.shoes, /obj/item/clothing/shoes/magboots) && (M.shoes.flags & NOSLIP)) + return + + if(M.buckled) //Cam't fall down if you are buckled + return + + if(istype(get_turf(M), /turf/space)) // Can't fall onto nothing. + return + + if((istype(M,/mob/living/carbon/human/)) && (M.m_intent == MOVE_INTENT_RUN)) + M.Stun(5) + M.Weaken(5) + + else if(istype(M,/mob/living/carbon/human/)) + M.Stun(2) + M.Weaken(2) + + + to_chat(M, "Gravity!") + +/proc/has_gravity(atom/AT, turf/T) + if(!T) + T = get_turf(AT) + var/area/A = get_area(T) + if(istype(T, /turf/space)) // Turf never has gravity + return 0 + else if(A && A.has_gravity) // Areas which always has gravity + return 1 + else + // There's a gravity generator on our z level + // This would do well when integrated with the z level manager + if(T && gravity_generators["[T.z]"] && length(gravity_generators["[T.z]"])) + return 1 + return 0 + +/area/proc/prison_break() + for(var/obj/machinery/power/apc/temp_apc in src) + temp_apc.overload_lighting(70) + for(var/obj/machinery/door/airlock/temp_airlock in src) + temp_airlock.prison_open() + for(var/obj/machinery/door/window/temp_windoor in src) + temp_windoor.open() + +/area/AllowDrop() + CRASH("Bad op: area/AllowDrop() called") + +/area/drop_location() + CRASH("Bad op: area/drop_location() called") diff --git a/code/game/area/areas/depot-areas.dm b/code/game/area/areas/depot-areas.dm index 86940fcb412..7a7c934df35 100644 --- a/code/game/area/areas/depot-areas.dm +++ b/code/game/area/areas/depot-areas.dm @@ -174,7 +174,7 @@ L.close() if(!L.locked) L.locked = !L.locked - L.req_access = list(access_syndicate_leader) + L.req_access = list(ACCESS_SYNDICATE_LEADER) L.update_icon() else log_game("Depot visit: ended") @@ -182,7 +182,7 @@ for(var/mob/living/simple_animal/hostile/syndicate/N in src) N.a_intent = INTENT_HARM for(var/obj/machinery/door/airlock/A in src) - A.req_access_txt = "[access_syndicate_leader]" + A.req_access_txt = "[ACCESS_SYNDICATE_LEADER]" for(var/obj/structure/closet/secure_closet/syndicate/depot/L in src) if(L.locked) L.locked = !L.locked @@ -476,4 +476,4 @@ var/obj/machinery/shieldwall/syndicate/S = locateUID(shuid) if(S) qdel(S) - shield_list = list() \ No newline at end of file + shield_list = list() diff --git a/code/game/area/areas/mining.dm b/code/game/area/areas/mining.dm index 4a2317889fa..0b07fffda5e 100644 --- a/code/game/area/areas/mining.dm +++ b/code/game/area/areas/mining.dm @@ -127,4 +127,4 @@ icon_state = "danger" /area/lavaland/surface/outdoors/explored - name = "Lavaland Labor Camp" \ No newline at end of file + name = "Lavaland Labor Camp" diff --git a/code/game/area/areas/ruins/lavaland.dm b/code/game/area/areas/ruins/lavaland.dm index ac4c41b0c42..5baf1fe315e 100644 --- a/code/game/area/areas/ruins/lavaland.dm +++ b/code/game/area/areas/ruins/lavaland.dm @@ -87,4 +87,4 @@ //ash walker nest /area/ruin/unpowered/ash_walkers - icon_state = "red" \ No newline at end of file + icon_state = "red" diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 8a19fb14d9c..f9d66372df5 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -1,843 +1,884 @@ -/atom - layer = TURF_LAYER - plane = GAME_PLANE - var/level = 2 - var/flags = NONE - var/flags_2 = NONE - var/list/fingerprints - var/list/fingerprintshidden - var/fingerprintslast = null - var/list/blood_DNA - var/blood_color - var/last_bumped = 0 - var/pass_flags = 0 - var/germ_level = GERM_LEVEL_AMBIENT // The higher the germ level, the more germ on the atom. - var/simulated = TRUE //filter for actions - used by lighting overlays - var/atom_say_verb = "says" - var/dont_save = 0 // For atoms that are temporary by necessity - like lighting overlays - - - ///Chemistry. - var/container_type = NONE - var/datum/reagents/reagents = null - - //This atom's HUD (med/sec, etc) images. Associative list. - var/list/image/hud_list = list() - //HUD images that this atom can provide. - var/list/hud_possible - - ///Chemistry. - - - //Value used to increment ex_act() if reactionary_explosions is on - var/explosion_block = 0 - - //Detective Work, used for the duplicate data points kept in the scanners - var/list/original_atom - - //Detective Work, used for allowing a given atom to leave its fibers on stuff. Allowed by default - var/can_leave_fibers = TRUE - - var/allow_spin = 1 //Set this to 1 for a _target_ that is being thrown at; if an atom has this set to 1 then atoms thrown AT it will not spin; currently used for the singularity. -Fox - - var/admin_spawned = 0 //was this spawned by an admin? used for stat tracking stuff. - - var/initialized = FALSE - - var/list/priority_overlays //overlays that should remain on top and not normally removed when using cut_overlay functions, like c4. - var/list/remove_overlays // a very temporary list of overlays to remove - var/list/add_overlays // a very temporary list of overlays to add - - var/list/atom_colours //used to store the different colors on an atom - //its inherent color, the colored paint applied on it, special color effect etc... - -/atom/New(loc, ...) - if(use_preloader && (src.type == _preloader.target_path))//in case the instanciated atom is creating other atoms in New() - _preloader.load(src) - . = ..() - attempt_init(arglist(args)) - -// This is distinct from /tg/ because of our space management system -// This is overriden in /atom/movable and the parent isn't called if the SMS wants to deal with it's init -/atom/proc/attempt_init(...) - var/do_initialize = SSatoms.initialized - if(do_initialize != INITIALIZATION_INSSATOMS) - args[1] = do_initialize == INITIALIZATION_INNEW_MAPLOAD - if(SSatoms.InitAtom(src, args)) - // we were deleted - return - - -//Called after New if the map is being loaded. mapload = TRUE -//Called from base of New if the map is not being loaded. mapload = FALSE -//This base must be called or derivatives must set initialized to TRUE -//must not sleep -//Other parameters are passed from New (excluding loc), this does not happen if mapload is TRUE -//Must return an Initialize hint. Defined in __DEFINES/subsystems.dm - -//Note: the following functions don't call the base for optimization and must copypasta: -// /turf/Initialize -// /turf/open/space/Initialize - -/atom/proc/Initialize(mapload, ...) - if(initialized) - stack_trace("Warning: [src]([type]) initialized multiple times!") - initialized = TRUE - - if(color) - add_atom_colour(color, FIXED_COLOUR_PRIORITY) - - if(light_power && light_range) - update_light() - - if(opacity && isturf(loc)) - var/turf/T = loc - T.has_opaque_atom = TRUE // No need to recalculate it in this case, it's guranteed to be on afterwards anyways. - - if(loc) - loc.InitializedOn(src) // Used for poolcontroller / pool to improve performance greatly. However it also open up path to other usage of observer pattern on turfs. - - ComponentInitialize() - - return INITIALIZE_HINT_NORMAL - - -//called if Initialize returns INITIALIZE_HINT_LATELOAD -/atom/proc/LateInitialize() - return - -// Put your AddComponent() calls here -/atom/proc/ComponentInitialize() - return - -/atom/proc/InitializedOn(atom/A) // Proc for when something is initialized on a atom - Optional to call. Useful for observer pattern etc. - return - -/atom/proc/onCentcom() - var/turf/T = get_turf(src) - if(!T) - return 0 - - if(!is_admin_level(T.z))//if not, don't bother - return 0 - - //check for centcomm shuttles - for(var/centcom_shuttle in list("emergency", "pod1", "pod2", "pod3", "pod4", "ferry")) - var/obj/docking_port/mobile/M = SSshuttle.getShuttle(centcom_shuttle) - if(T in M.areaInstance) - return 1 - - //finally check for centcom itself - return istype(T.loc,/area/centcom) - -/atom/proc/onSyndieBase() - var/turf/T = get_turf(src) - if(!T) - return 0 - - if(!is_admin_level(T.z))//if not, don't bother - return 0 - - if(istype(T.loc, /area/shuttle/syndicate_elite) || istype(T.loc, /area/syndicate_mothership)) - return 1 - - return 0 - -/atom/Destroy() - if(alternate_appearances) - for(var/aakey in alternate_appearances) - var/datum/alternate_appearance/AA = alternate_appearances[aakey] - qdel(AA) - alternate_appearances = null - - QDEL_NULL(reagents) - invisibility = INVISIBILITY_MAXIMUM - LAZYCLEARLIST(overlays) - LAZYCLEARLIST(priority_overlays) - - QDEL_NULL(light) - - return ..() - -//Hook for running code when a dir change occurs -/atom/proc/setDir(newdir) - SEND_SIGNAL(src, COMSIG_ATOM_DIR_CHANGE, dir, newdir) - dir = newdir - -///Handle melee attack by a mech -/atom/proc/mech_melee_attack(obj/mecha/M) - return - -/atom/proc/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) - SEND_SIGNAL(src, COMSIG_ATOM_HULK_ATTACK, user) - if(does_attack_animation) - user.changeNext_move(CLICK_CD_MELEE) - add_attack_logs(user, src, "Punched with hulk powers") - user.do_attack_animation(src, ATTACK_EFFECT_SMASH) - -/atom/proc/CheckParts(list/parts_list) - for(var/A in parts_list) - if(istype(A, /datum/reagent)) - if(!reagents) - reagents = new() - reagents.reagent_list.Add(A) - reagents.conditional_update() - else if(istype(A, /atom/movable)) - var/atom/movable/M = A - if(istype(M.loc, /mob/living)) - var/mob/living/L = M.loc - L.unEquip(M) - M.forceMove(src) - -/atom/proc/assume_air(datum/gas_mixture/giver) - qdel(giver) - return null - -/atom/proc/remove_air(amount) - return null - -/atom/proc/return_air() - if(loc) - return loc.return_air() - else - return null - -/atom/proc/check_eye(user as mob) - if(istype(user, /mob/living/silicon/ai)) // WHYYYY - return 1 - return - -/atom/proc/on_reagent_change() - return - -/atom/proc/Bumped(AM as mob|obj) - return - -/// Convenience proc to see if a container is open for chemistry handling -/atom/proc/is_open_container() - return is_refillable() && is_drainable() - -/// Is this atom injectable into other atoms -/atom/proc/is_injectable(mob/user, allowmobs = TRUE) - return reagents && (container_type & (INJECTABLE | REFILLABLE)) - -/// Can we draw from this atom with an injectable atom -/atom/proc/is_drawable(mob/user, allowmobs = TRUE) - return reagents && (container_type & (DRAWABLE | DRAINABLE)) - -/// Can this atoms reagents be refilled -/atom/proc/is_refillable() - return reagents && (container_type & REFILLABLE) - -/// Is this atom drainable of reagents -/atom/proc/is_drainable() - return reagents && (container_type & DRAINABLE) - -/atom/proc/CheckExit() - return 1 - -/atom/proc/HasProximity(atom/movable/AM as mob|obj) - return - -/atom/proc/emp_act(var/severity) - return - -/atom/proc/bullet_act(obj/item/projectile/P, def_zone) - SEND_SIGNAL(src, COMSIG_ATOM_BULLET_ACT, P, def_zone) - . = P.on_hit(src, 0, def_zone) - -/atom/proc/in_contents_of(container)//can take class or object instance as argument - if(ispath(container)) - if(istype(src.loc, container)) - return 1 - else if(src in container) - return 1 - return - -/* - * atom/proc/search_contents_for(path,list/filter_path=null) - * Recursevly searches all atom contens (including contents contents and so on). - * - * ARGS: path - search atom contents for atoms of this type - * list/filter_path - if set, contents of atoms not of types in this list are excluded from search. - * - * RETURNS: list of found atoms - */ - -/atom/proc/search_contents_for(path,list/filter_path=null) - var/list/found = list() - for(var/atom/A in src) - if(istype(A, path)) - found += A - if(filter_path) - var/pass = 0 - for(var/type in filter_path) - pass |= istype(A, type) - if(!pass) - continue - if(A.contents.len) - found += A.search_contents_for(path,filter_path) - return found - - -//All atoms -/atom/proc/examine(mob/user, infix = "", suffix = "") - //This reformat names to get a/an properly working on item descriptions when they are bloody - var/f_name = "\a [src][infix]." - if(src.blood_DNA && !istype(src, /obj/effect/decal)) - if(gender == PLURAL) - f_name = "some " - else - f_name = "a " - if(blood_color != "#030303") - f_name += "blood-stained [name][infix]!" - else - f_name += "oil-stained [name][infix]." - . = list("[bicon(src)] That's [f_name] [suffix]") - if(desc) - . += desc - - if(reagents) - if(container_type & TRANSPARENT) - . += "It contains:" - if(reagents.reagent_list.len) - if(user.can_see_reagents()) //Show each individual reagent - for(var/I in reagents.reagent_list) - var/datum/reagent/R = I - . += "[R.volume] units of [R.name]" - else //Otherwise, just show the total volume - if(reagents && reagents.reagent_list.len) - . += "[reagents.total_volume] units of various reagents." - else - . += "Nothing." - else if(container_type & AMOUNT_VISIBLE) - if(reagents.total_volume) - . += "It has [reagents.total_volume] unit\s left." - else - . += "It's empty." - - SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, .) - -/atom/proc/relaymove() - return - -/atom/proc/ex_act() - return - -/atom/proc/blob_act(obj/structure/blob/B) - SEND_SIGNAL(src, COMSIG_ATOM_BLOB_ACT, B) - -/atom/proc/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) - SEND_SIGNAL(src, COMSIG_ATOM_FIRE_ACT, exposed_temperature, exposed_volume) - if(reagents) - reagents.temperature_reagents(exposed_temperature) - -/atom/proc/emag_act() - return - -/atom/proc/fart_act(mob/living/M) - return FALSE - -/atom/proc/rpd_act() - return - -/atom/proc/rpd_blocksusage() - // Atoms that return TRUE prevent RPDs placing any kind of pipes on their turf. - return FALSE - -/atom/proc/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) - if(density && !has_gravity(AM)) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...). - addtimer(CALLBACK(src, .proc/hitby_react, AM), 2) - -/atom/proc/hitby_react(atom/movable/AM) - if(AM && isturf(AM.loc)) - step(AM, turn(AM.dir, 180)) - -/atom/proc/get_spooked() - return - -/atom/proc/add_hiddenprint(mob/living/M as mob) - if(isnull(M)) return - if(isnull(M.key)) return - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(!istype(H.dna, /datum/dna)) - return 0 - if(H.gloves) - if(fingerprintslast != H.ckey) - //Add the list if it does not exist. - if(!fingerprintshidden) - fingerprintshidden = list() - fingerprintshidden += text("\[[time_stamp()]\] (Wearing gloves). Real name: [], Key: []",H.real_name, H.key) - fingerprintslast = H.ckey - return 0 - if(!fingerprints) - if(fingerprintslast != H.ckey) - //Add the list if it does not exist. - if(!fingerprintshidden) - fingerprintshidden = list() - fingerprintshidden += text("\[[time_stamp()]\] Real name: [], Key: []",H.real_name, H.key) - fingerprintslast = H.ckey - return 1 - else - if(fingerprintslast != M.ckey) - //Add the list if it does not exist. - if(!fingerprintshidden) - fingerprintshidden = list() - fingerprintshidden += text("\[[time_stamp()]\] Real name: [], Key: []",M.real_name, M.key) - fingerprintslast = M.ckey - return - - -//Set ignoregloves to add prints irrespective of the mob having gloves on. -/atom/proc/add_fingerprint(mob/living/M as mob, ignoregloves = 0) - if(isnull(M)) return - if(isnull(M.key)) return - if(ishuman(M)) - //Add the list if it does not exist. - if(!fingerprintshidden) - fingerprintshidden = list() - - //Fibers~ - add_fibers(M) - - //He has no prints! - if(FINGERPRINTS in M.mutations) - if(fingerprintslast != M.key) - fingerprintshidden += "(Has no fingerprints) Real name: [M.real_name], Key: [M.key]" - fingerprintslast = M.key - return 0 //Now, lets get to the dirty work. - //First, make sure their DNA makes sense. - var/mob/living/carbon/human/H = M - if(!istype(H.dna, /datum/dna) || !H.dna.uni_identity || (length(H.dna.uni_identity) != 32)) - if(!istype(H.dna, /datum/dna)) - H.dna = new /datum/dna(null) - H.dna.real_name = H.real_name - H.check_dna() - - //Check if the gloves (if any) hide fingerprints - if(H.gloves) - var/obj/item/clothing/gloves/G = H.gloves - if(G.transfer_prints) - ignoregloves = 1 - - //Now, deal with gloves. - if(!ignoregloves) - if(H.gloves && H.gloves != src) - if(fingerprintslast != H.ckey) - fingerprintshidden += text("\[[]\](Wearing gloves). Real name: [], Key: []",time_stamp(), H.real_name, H.key) - fingerprintslast = H.ckey - H.gloves.add_fingerprint(M) - return 0 - - //More adminstuffz - if(fingerprintslast != H.ckey) - fingerprintshidden += text("\[[]\]Real name: [], Key: []",time_stamp(), H.real_name, H.key) - fingerprintslast = H.ckey - - //Make the list if it does not exist. - if(!fingerprints) - fingerprints = list() - - //Hash this shit. - var/full_print = H.get_full_print() - - // Add the fingerprints - fingerprints[full_print] = full_print - - return 1 - else - //Smudge up dem prints some - if(fingerprintslast != M.ckey) - fingerprintshidden += text("\[[]\]Real name: [], Key: []",time_stamp(), M.real_name, M.key) - fingerprintslast = M.ckey - - return - - -/atom/proc/transfer_fingerprints_to(var/atom/A) - - // Make sure everything are lists. - if(!islist(A.fingerprints)) - A.fingerprints = list() - if(!islist(A.fingerprintshidden)) - A.fingerprintshidden = list() - - if(!islist(fingerprints)) - fingerprints = list() - if(!islist(fingerprintshidden)) - fingerprintshidden = list() - - // Transfer - if(fingerprints) - A.fingerprints |= fingerprints.Copy() //detective - if(fingerprintshidden) - A.fingerprintshidden |= fingerprintshidden.Copy() //admin - A.fingerprintslast = fingerprintslast - -var/list/blood_splatter_icons = list() - -/atom/proc/blood_splatter_index() - return "\ref[initial(icon)]-[initial(icon_state)]" - -//returns the mob's dna info as a list, to be inserted in an object's blood_DNA list -/mob/living/proc/get_blood_dna_list() - if(get_blood_id() != "blood") - return - return list("ANIMAL DNA" = "Y-") - -/mob/living/carbon/get_blood_dna_list() - if(get_blood_id() != "blood") - return - var/list/blood_dna = list() - if(dna) - blood_dna[dna.unique_enzymes] = dna.blood_type - else - blood_dna["UNKNOWN DNA"] = "X*" - return blood_dna - -/mob/living/carbon/alien/get_blood_dna_list() - return list("UNKNOWN DNA" = "X*") - -//to add a mob's dna info into an object's blood_DNA list. -/atom/proc/transfer_mob_blood_dna(mob/living/L) - var/new_blood_dna = L.get_blood_dna_list() - if(!new_blood_dna) - return 0 - return transfer_blood_dna(new_blood_dna) - -/obj/effect/decal/cleanable/blood/splatter/transfer_mob_blood_dna(mob/living/L) - ..(L) - var/list/b_data = L.get_blood_data(L.get_blood_id()) - if(b_data) - basecolor = b_data["blood_color"] - else - basecolor = "#A10808" - update_icon() - -/obj/effect/decal/cleanable/blood/footprints/transfer_mob_blood_dna(mob/living/L) - ..(L) - var/list/b_data = L.get_blood_data(L.get_blood_id()) - if(b_data) - basecolor = b_data["blood_color"] - else - basecolor = "#A10808" - update_icon() - -//to add blood dna info to the object's blood_DNA list -/atom/proc/transfer_blood_dna(list/blood_dna) - if(!blood_DNA) - blood_DNA = list() - var/old_length = blood_DNA.len - blood_DNA |= blood_dna - if(blood_DNA.len > old_length) - return 1//some new blood DNA was added - - -//to add blood from a mob onto something, and transfer their dna info -/atom/proc/add_mob_blood(mob/living/M) - var/list/blood_dna = M.get_blood_dna_list() - if(!blood_dna) - return 0 - var/bloodcolor = "#A10808" - var/list/b_data = M.get_blood_data(M.get_blood_id()) - if(b_data) - bloodcolor = b_data["blood_color"] - - return add_blood(blood_dna, bloodcolor) - -//to add blood onto something, with blood dna info to include. -/atom/proc/add_blood(list/blood_dna, color) - return 0 - -/obj/add_blood(list/blood_dna, color) - return transfer_blood_dna(blood_dna) - -/obj/item/add_blood(list/blood_dna, color) - var/blood_count = !blood_DNA ? 0 : blood_DNA.len - if(!..()) - return 0 - if(!blood_count)//apply the blood-splatter overlay if it isn't already in there - add_blood_overlay(color) - return 1 //we applied blood to the item - -/obj/item/clothing/gloves/add_blood(list/blood_dna, color) - . = ..() - transfer_blood = rand(2, 4) - -/turf/add_blood(list/blood_dna, color) - var/obj/effect/decal/cleanable/blood/splatter/B = locate() in src - if(!B) - B = new /obj/effect/decal/cleanable/blood/splatter(src) - B.transfer_blood_dna(blood_dna) //give blood info to the blood decal. - B.basecolor = color - return 1 //we bloodied the floor - -/mob/living/carbon/human/add_blood(list/blood_dna, color) - if(wear_suit) - wear_suit.add_blood(blood_dna, color) - wear_suit.blood_color = color - update_inv_wear_suit(1) - else if(w_uniform) - w_uniform.add_blood(blood_dna, color) - w_uniform.blood_color = color - update_inv_w_uniform(1) - if(head) - head.add_blood(blood_dna, color) - head.blood_color = color - update_inv_head(0,0) - if(glasses) - glasses.add_blood(blood_dna, color) - glasses.blood_color = color - update_inv_glasses(0) - if(gloves) - var/obj/item/clothing/gloves/G = gloves - G.add_blood(blood_dna, color) - G.blood_color = color - verbs += /mob/living/carbon/human/proc/bloody_doodle - else - hand_blood_color = color - bloody_hands = rand(2, 4) - transfer_blood_dna(blood_dna) - verbs += /mob/living/carbon/human/proc/bloody_doodle - - update_inv_gloves(1) //handles bloody hands overlays and updating - return 1 - -/obj/item/proc/add_blood_overlay(color) - if(initial(icon) && initial(icon_state)) - //try to find a pre-processed blood-splatter. otherwise, make a new one - var/index = blood_splatter_index() - var/icon/blood_splatter_icon = blood_splatter_icons[index] - if(!blood_splatter_icon) - blood_splatter_icon = icon(initial(icon), initial(icon_state), , 1) //we only want to apply blood-splatters to the initial icon_state for each object - blood_splatter_icon.Blend("#fff", ICON_ADD) //fills the icon_state with white (except where it's transparent) - blood_splatter_icon.Blend(icon('icons/effects/blood.dmi', "itemblood"), ICON_MULTIPLY) //adds blood and the remaining white areas become transparant - blood_splatter_icon = fcopy_rsc(blood_splatter_icon) - blood_splatter_icons[index] = blood_splatter_icon - - blood_overlay = image(blood_splatter_icon) - blood_overlay.color = color - overlays += blood_overlay - -/atom/proc/clean_blood() - germ_level = 0 - if(islist(blood_DNA)) - blood_DNA = null - return TRUE - -/obj/effect/decal/cleanable/blood/clean_blood() - return // While this seems nonsensical, clean_blood isn't supposed to be used like this on a blood decal. - -/obj/item/clean_blood() - . = ..() - if(.) - if(blood_overlay) - overlays -= blood_overlay - -/obj/item/clothing/gloves/clean_blood() - . = ..() - if(.) - transfer_blood = 0 - - -/obj/item/clothing/shoes/clean_blood() - ..() - bloody_shoes = list(BLOOD_STATE_HUMAN = 0, BLOOD_STATE_XENO = 0, BLOOD_STATE_NOT_BLOODY = 0) - blood_state = BLOOD_STATE_NOT_BLOODY - if(ismob(loc)) - var/mob/M = loc - M.update_inv_shoes() - - -/mob/living/carbon/human/clean_blood() - if(gloves) - if(gloves.clean_blood()) - clean_blood() - update_inv_gloves() - gloves.germ_level = 0 - else - ..() // Clear the Blood_DNA list - if(bloody_hands) - bloody_hands = 0 - update_inv_gloves() - update_icons() //apply the now updated overlays to the mob - - -/atom/proc/add_vomit_floor(toxvomit = 0, green = FALSE) - playsound(src, 'sound/effects/splat.ogg', 50, 1) - if(!isspaceturf(src)) - var/type = green ? /obj/effect/decal/cleanable/vomit/green : /obj/effect/decal/cleanable/vomit - var/vomit_reagent = green ? "green_vomit" : "vomit" - for(var/obj/effect/decal/cleanable/vomit/V in get_turf(src)) - if(V.type == type) - V.reagents.add_reagent(vomit_reagent, 5) - return - - var/obj/effect/decal/cleanable/vomit/this = new type(src) - - // Make toxins vomit look different - if(toxvomit) - this.icon_state = "vomittox_[pick(1,4)]" - -/atom/proc/get_global_map_pos() - if(!islist(global_map) || isemptylist(global_map)) return - var/cur_x = null - var/cur_y = null - var/list/y_arr = null - for(cur_x=1,cur_x<=global_map.len,cur_x++) - y_arr = global_map[cur_x] - cur_y = y_arr.Find(src.z) - if(cur_y) - break -// to_chat(world, "X = [cur_x]; Y = [cur_y]") - if(cur_x && cur_y) - return list("x"=cur_x,"y"=cur_y) - else - return 0 - -// Used to provide overlays when using this atom as a viewing focus -// (cameras, locker tint, etc.) -/atom/proc/get_remote_view_fullscreens(mob/user) - return - -//the sight changes to give to the mob whose perspective is set to that atom (e.g. A mob with nightvision loses its nightvision while looking through a normal camera) -/atom/proc/update_remote_sight(mob/living/user) - user.sync_lighting_plane_alpha() - return - -/atom/proc/checkpass(passflag) - return pass_flags&passflag - -/atom/proc/isinspace() - if(isspaceturf(get_turf(src))) - return TRUE - else - return FALSE - -/atom/proc/handle_fall() - return - -/atom/proc/singularity_act() - return - -/atom/proc/singularity_pull(obj/singularity/S, current_size) - SEND_SIGNAL(src, COMSIG_ATOM_SING_PULL, S, current_size) - -/** - * Respond to acid being used on our atom - * - * Default behaviour is to send COMSIG_ATOM_ACID_ACT and return - */ -/atom/proc/acid_act(acidpwr, acid_volume) - SEND_SIGNAL(src, COMSIG_ATOM_ACID_ACT, acidpwr, acid_volume) - -/atom/proc/narsie_act() - return - -/atom/proc/ratvar_act() - return - -/atom/proc/handle_ricochet(obj/item/projectile/P) - return - -//This proc is called on the location of an atom when the atom is Destroy()'d -/atom/proc/handle_atom_del(atom/A) - return - -/atom/proc/atom_say(message) - if(!message) - return - audible_message("[src] [atom_say_verb], \"[message]\"") - -/atom/proc/speech_bubble(var/bubble_state = "",var/bubble_loc = src, var/list/bubble_recipients = list()) - return - -/atom/vv_edit_var(var_name, var_value) - if(!Debug2) - admin_spawned = TRUE - . = ..() - switch(var_name) - if("light_power", "light_range", "light_color") - update_light() - if("color") - add_atom_colour(color, ADMIN_COLOUR_PRIORITY) - - -/atom/vv_get_dropdown() - . = ..() - var/turf/curturf = get_turf(src) - if(curturf) - .["Jump to turf"] = "?_src_=holder;adminplayerobservecoodjump=1;X=[curturf.x];Y=[curturf.y];Z=[curturf.z]" - .["Add reagent"] = "?_src_=vars;addreagent=[UID()]" - .["Trigger explosion"] = "?_src_=vars;explode=[UID()]" - .["Trigger EM pulse"] = "?_src_=vars;emp=[UID()]" - -/atom/proc/AllowDrop() - return FALSE - -/atom/proc/drop_location() - var/atom/L = loc - if(!L) - return null - return L.AllowDrop() ? L : get_turf(L) - -/atom/Entered(atom/movable/AM, atom/oldLoc) - SEND_SIGNAL(src, COMSIG_ATOM_ENTERED, AM, oldLoc) - -/atom/Exit(atom/movable/AM, atom/newLoc) - . = ..() - if(SEND_SIGNAL(src, COMSIG_ATOM_EXIT, AM, newLoc) & COMPONENT_ATOM_BLOCK_EXIT) - return FALSE - -/atom/Exited(atom/movable/AM, atom/newLoc) - SEND_SIGNAL(src, COMSIG_ATOM_EXITED, AM, newLoc) - -/* - Adds an instance of colour_type to the atom's atom_colours list -*/ -/atom/proc/add_atom_colour(coloration, colour_priority) - if(!atom_colours || !atom_colours.len) - atom_colours = list() - atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. - if(!coloration) - return - if(colour_priority > atom_colours.len) - return - atom_colours[colour_priority] = coloration - update_atom_colour() - - -/* - Removes an instance of colour_type from the atom's atom_colours list -*/ -/atom/proc/remove_atom_colour(colour_priority, coloration) - if(!atom_colours) - atom_colours = list() - atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. - if(colour_priority > atom_colours.len) - return - if(coloration && atom_colours[colour_priority] != coloration) - return //if we don't have the expected color (for a specific priority) to remove, do nothing - atom_colours[colour_priority] = null - update_atom_colour() - - -/* - Resets the atom's color to null, and then sets it to the highest priority - colour available -*/ -/atom/proc/update_atom_colour() - if(!atom_colours) - atom_colours = list() - atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. - color = null - for(var/C in atom_colours) - if(islist(C)) - var/list/L = C - if(L.len) - color = L - return - else if(C) - color = C - return \ No newline at end of file +/atom + layer = TURF_LAYER + plane = GAME_PLANE + var/level = 2 + var/flags = NONE + var/flags_2 = NONE + var/list/fingerprints + var/list/fingerprintshidden + var/fingerprintslast = null + var/list/blood_DNA + var/blood_color + var/last_bumped = 0 + var/pass_flags = 0 + var/germ_level = GERM_LEVEL_AMBIENT // The higher the germ level, the more germ on the atom. + var/simulated = TRUE //filter for actions - used by lighting overlays + var/atom_say_verb = "says" + var/dont_save = 0 // For atoms that are temporary by necessity - like lighting overlays + + + ///Chemistry. + var/container_type = NONE + var/datum/reagents/reagents = null + + //This atom's HUD (med/sec, etc) images. Associative list. + var/list/image/hud_list = list() + //HUD images that this atom can provide. + var/list/hud_possible + + ///Chemistry. + + + //Value used to increment ex_act() if reactionary_explosions is on + var/explosion_block = 0 + + //Detective Work, used for the duplicate data points kept in the scanners + var/list/original_atom + + //Detective Work, used for allowing a given atom to leave its fibers on stuff. Allowed by default + var/can_leave_fibers = TRUE + + var/allow_spin = 1 //Set this to 1 for a _target_ that is being thrown at; if an atom has this set to 1 then atoms thrown AT it will not spin; currently used for the singularity. -Fox + + var/admin_spawned = 0 //was this spawned by an admin? used for stat tracking stuff. + + var/initialized = FALSE + + var/list/priority_overlays //overlays that should remain on top and not normally removed when using cut_overlay functions, like c4. + var/list/remove_overlays // a very temporary list of overlays to remove + var/list/add_overlays // a very temporary list of overlays to add + + var/list/atom_colours //used to store the different colors on an atom + //its inherent color, the colored paint applied on it, special color effect etc... + +/atom/New(loc, ...) + if(use_preloader && (src.type == _preloader.target_path))//in case the instanciated atom is creating other atoms in New() + _preloader.load(src) + . = ..() + attempt_init(arglist(args)) + +// This is distinct from /tg/ because of our space management system +// This is overriden in /atom/movable and the parent isn't called if the SMS wants to deal with it's init +/atom/proc/attempt_init(...) + var/do_initialize = SSatoms.initialized + if(do_initialize != INITIALIZATION_INSSATOMS) + args[1] = do_initialize == INITIALIZATION_INNEW_MAPLOAD + if(SSatoms.InitAtom(src, args)) + // we were deleted + return + + +//Called after New if the map is being loaded. mapload = TRUE +//Called from base of New if the map is not being loaded. mapload = FALSE +//This base must be called or derivatives must set initialized to TRUE +//must not sleep +//Other parameters are passed from New (excluding loc), this does not happen if mapload is TRUE +//Must return an Initialize hint. Defined in __DEFINES/subsystems.dm + +//Note: the following functions don't call the base for optimization and must copypasta: +// /turf/Initialize +// /turf/open/space/Initialize + +/atom/proc/Initialize(mapload, ...) + if(initialized) + stack_trace("Warning: [src]([type]) initialized multiple times!") + initialized = TRUE + + if(color) + add_atom_colour(color, FIXED_COLOUR_PRIORITY) + + if(light_power && light_range) + update_light() + + if(opacity && isturf(loc)) + var/turf/T = loc + T.has_opaque_atom = TRUE // No need to recalculate it in this case, it's guranteed to be on afterwards anyways. + + if(loc) + loc.InitializedOn(src) // Used for poolcontroller / pool to improve performance greatly. However it also open up path to other usage of observer pattern on turfs. + + ComponentInitialize() + + return INITIALIZE_HINT_NORMAL + + +//called if Initialize returns INITIALIZE_HINT_LATELOAD +/atom/proc/LateInitialize() + return + +// Put your AddComponent() calls here +/atom/proc/ComponentInitialize() + return + +/atom/proc/InitializedOn(atom/A) // Proc for when something is initialized on a atom - Optional to call. Useful for observer pattern etc. + return + +/atom/proc/onCentcom() + var/turf/T = get_turf(src) + if(!T) + return 0 + + if(!is_admin_level(T.z))//if not, don't bother + return 0 + + //check for centcomm shuttles + for(var/centcom_shuttle in list("emergency", "pod1", "pod2", "pod3", "pod4", "ferry")) + var/obj/docking_port/mobile/M = SSshuttle.getShuttle(centcom_shuttle) + if(T in M.areaInstance) + return 1 + + //finally check for centcom itself + return istype(T.loc,/area/centcom) + +/atom/proc/onSyndieBase() + var/turf/T = get_turf(src) + if(!T) + return 0 + + if(!is_admin_level(T.z))//if not, don't bother + return 0 + + if(istype(T.loc, /area/shuttle/syndicate_elite) || istype(T.loc, /area/syndicate_mothership)) + return 1 + + return 0 + +/atom/Destroy() + if(alternate_appearances) + for(var/aakey in alternate_appearances) + var/datum/alternate_appearance/AA = alternate_appearances[aakey] + qdel(AA) + alternate_appearances = null + + QDEL_NULL(reagents) + invisibility = INVISIBILITY_MAXIMUM + LAZYCLEARLIST(overlays) + LAZYCLEARLIST(priority_overlays) + + QDEL_NULL(light) + + return ..() + +//Hook for running code when a dir change occurs +/atom/proc/setDir(newdir) + SEND_SIGNAL(src, COMSIG_ATOM_DIR_CHANGE, dir, newdir) + dir = newdir + +///Handle melee attack by a mech +/atom/proc/mech_melee_attack(obj/mecha/M) + return + +/atom/proc/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) + SEND_SIGNAL(src, COMSIG_ATOM_HULK_ATTACK, user) + if(does_attack_animation) + user.changeNext_move(CLICK_CD_MELEE) + add_attack_logs(user, src, "Punched with hulk powers") + user.do_attack_animation(src, ATTACK_EFFECT_SMASH) + +/atom/proc/CheckParts(list/parts_list) + for(var/A in parts_list) + if(istype(A, /datum/reagent)) + if(!reagents) + reagents = new() + reagents.reagent_list.Add(A) + reagents.conditional_update() + else if(istype(A, /atom/movable)) + var/atom/movable/M = A + if(istype(M.loc, /mob/living)) + var/mob/living/L = M.loc + L.unEquip(M) + M.forceMove(src) + +/atom/proc/assume_air(datum/gas_mixture/giver) + qdel(giver) + return null + +/atom/proc/remove_air(amount) + return null + +/atom/proc/return_air() + if(loc) + return loc.return_air() + else + return null + +/atom/proc/check_eye(user as mob) + if(istype(user, /mob/living/silicon/ai)) // WHYYYY + return 1 + return + +/atom/proc/on_reagent_change() + return + +/atom/proc/Bumped(AM as mob|obj) + return + +/// Convenience proc to see if a container is open for chemistry handling +/atom/proc/is_open_container() + return is_refillable() && is_drainable() + +/// Is this atom injectable into other atoms +/atom/proc/is_injectable(mob/user, allowmobs = TRUE) + return reagents && (container_type & (INJECTABLE | REFILLABLE)) + +/// Can we draw from this atom with an injectable atom +/atom/proc/is_drawable(mob/user, allowmobs = TRUE) + return reagents && (container_type & (DRAWABLE | DRAINABLE)) + +/// Can this atoms reagents be refilled +/atom/proc/is_refillable() + return reagents && (container_type & REFILLABLE) + +/// Is this atom drainable of reagents +/atom/proc/is_drainable() + return reagents && (container_type & DRAINABLE) + +/atom/proc/CheckExit() + return 1 + +/atom/proc/HasProximity(atom/movable/AM as mob|obj) + return + +/atom/proc/emp_act(var/severity) + return + +/atom/proc/bullet_act(obj/item/projectile/P, def_zone) + SEND_SIGNAL(src, COMSIG_ATOM_BULLET_ACT, P, def_zone) + . = P.on_hit(src, 0, def_zone) + +/atom/proc/in_contents_of(container)//can take class or object instance as argument + if(ispath(container)) + if(istype(src.loc, container)) + return 1 + else if(src in container) + return 1 + return + +/* + * atom/proc/search_contents_for(path,list/filter_path=null) + * Recursevly searches all atom contens (including contents contents and so on). + * + * ARGS: path - search atom contents for atoms of this type + * list/filter_path - if set, contents of atoms not of types in this list are excluded from search. + * + * RETURNS: list of found atoms + */ + +/atom/proc/search_contents_for(path,list/filter_path=null) + var/list/found = list() + for(var/atom/A in src) + if(istype(A, path)) + found += A + if(filter_path) + var/pass = 0 + for(var/type in filter_path) + pass |= istype(A, type) + if(!pass) + continue + if(A.contents.len) + found += A.search_contents_for(path,filter_path) + return found + + +//All atoms +/atom/proc/examine(mob/user, infix = "", suffix = "") + //This reformat names to get a/an properly working on item descriptions when they are bloody + var/f_name = "\a [src][infix]." + if(src.blood_DNA && !istype(src, /obj/effect/decal)) + if(gender == PLURAL) + f_name = "some " + else + f_name = "a " + if(blood_color != "#030303") + f_name += "blood-stained [name][infix]!" + else + f_name += "oil-stained [name][infix]." + . = list("[bicon(src)] That's [f_name] [suffix]") + if(desc) + . += desc + + if(reagents) + if(container_type & TRANSPARENT) + . += "It contains:" + if(reagents.reagent_list.len) + if(user.can_see_reagents()) //Show each individual reagent + for(var/I in reagents.reagent_list) + var/datum/reagent/R = I + . += "[R.volume] units of [R.name]" + else //Otherwise, just show the total volume + if(reagents && reagents.reagent_list.len) + . += "[reagents.total_volume] units of various reagents." + else + . += "Nothing." + else if(container_type & AMOUNT_VISIBLE) + if(reagents.total_volume) + . += "It has [reagents.total_volume] unit\s left." + else + . += "It's empty." + + SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, .) + +/atom/proc/relaymove() + return + +/atom/proc/ex_act() + return + +/atom/proc/blob_act(obj/structure/blob/B) + SEND_SIGNAL(src, COMSIG_ATOM_BLOB_ACT, B) + +/atom/proc/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) + SEND_SIGNAL(src, COMSIG_ATOM_FIRE_ACT, exposed_temperature, exposed_volume) + if(reagents) + reagents.temperature_reagents(exposed_temperature) + +/atom/proc/tool_act(mob/living/user, obj/item/I, tool_type) + switch(tool_type) + if(TOOL_CROWBAR) + return crowbar_act(user, I) + if(TOOL_MULTITOOL) + return multitool_act(user, I) + if(TOOL_SCREWDRIVER) + return screwdriver_act(user, I) + if(TOOL_WRENCH) + return wrench_act(user, I) + if(TOOL_WIRECUTTER) + return wirecutter_act(user, I) + if(TOOL_WELDER) + return welder_act(user, I) + + +// Tool-specific behavior procs. To be overridden in subtypes. +/atom/proc/crowbar_act(mob/living/user, obj/item/I) + return + +/atom/proc/multitool_act(mob/living/user, obj/item/I) + return + +//Check if the multitool has an item in its data buffer +/atom/proc/multitool_check_buffer(user, silent = FALSE) + if(!silent) + to_chat(user, "[src] has no data buffer!") + return FALSE + +/atom/proc/screwdriver_act(mob/living/user, obj/item/I) + return + +/atom/proc/wrench_act(mob/living/user, obj/item/I) + return + +/atom/proc/wirecutter_act(mob/living/user, obj/item/I) + return + +/atom/proc/welder_act(mob/living/user, obj/item/I) + return + +/atom/proc/emag_act() + return + +/atom/proc/fart_act(mob/living/M) + return FALSE + +/atom/proc/rpd_act() + return + +/atom/proc/rpd_blocksusage() + // Atoms that return TRUE prevent RPDs placing any kind of pipes on their turf. + return FALSE + +/atom/proc/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) + if(density && !has_gravity(AM)) //thrown stuff bounces off dense stuff in no grav, unless the thrown stuff ends up inside what it hit(embedding, bola, etc...). + addtimer(CALLBACK(src, .proc/hitby_react, AM), 2) + +/atom/proc/hitby_react(atom/movable/AM) + if(AM && isturf(AM.loc)) + step(AM, turn(AM.dir, 180)) + +/atom/proc/get_spooked() + return + +/atom/proc/add_hiddenprint(mob/living/M as mob) + if(isnull(M)) return + if(isnull(M.key)) return + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(!istype(H.dna, /datum/dna)) + return 0 + if(H.gloves) + if(fingerprintslast != H.ckey) + //Add the list if it does not exist. + if(!fingerprintshidden) + fingerprintshidden = list() + fingerprintshidden += text("\[[time_stamp()]\] (Wearing gloves). Real name: [], Key: []",H.real_name, H.key) + fingerprintslast = H.ckey + return 0 + if(!fingerprints) + if(fingerprintslast != H.ckey) + //Add the list if it does not exist. + if(!fingerprintshidden) + fingerprintshidden = list() + fingerprintshidden += text("\[[time_stamp()]\] Real name: [], Key: []",H.real_name, H.key) + fingerprintslast = H.ckey + return 1 + else + if(fingerprintslast != M.ckey) + //Add the list if it does not exist. + if(!fingerprintshidden) + fingerprintshidden = list() + fingerprintshidden += text("\[[time_stamp()]\] Real name: [], Key: []",M.real_name, M.key) + fingerprintslast = M.ckey + return + + +//Set ignoregloves to add prints irrespective of the mob having gloves on. +/atom/proc/add_fingerprint(mob/living/M as mob, ignoregloves = 0) + if(isnull(M)) return + if(isnull(M.key)) return + if(ishuman(M)) + //Add the list if it does not exist. + if(!fingerprintshidden) + fingerprintshidden = list() + + //Fibers~ + add_fibers(M) + + //He has no prints! + if(FINGERPRINTS in M.mutations) + if(fingerprintslast != M.key) + fingerprintshidden += "(Has no fingerprints) Real name: [M.real_name], Key: [M.key]" + fingerprintslast = M.key + return 0 //Now, lets get to the dirty work. + //First, make sure their DNA makes sense. + var/mob/living/carbon/human/H = M + if(!istype(H.dna, /datum/dna) || !H.dna.uni_identity || (length(H.dna.uni_identity) != 32)) + if(!istype(H.dna, /datum/dna)) + H.dna = new /datum/dna(null) + H.dna.real_name = H.real_name + H.check_dna() + + //Check if the gloves (if any) hide fingerprints + if(H.gloves) + var/obj/item/clothing/gloves/G = H.gloves + if(G.transfer_prints) + ignoregloves = 1 + + //Now, deal with gloves. + if(!ignoregloves) + if(H.gloves && H.gloves != src) + if(fingerprintslast != H.ckey) + fingerprintshidden += text("\[[]\](Wearing gloves). Real name: [], Key: []",time_stamp(), H.real_name, H.key) + fingerprintslast = H.ckey + H.gloves.add_fingerprint(M) + return 0 + + //More adminstuffz + if(fingerprintslast != H.ckey) + fingerprintshidden += text("\[[]\]Real name: [], Key: []",time_stamp(), H.real_name, H.key) + fingerprintslast = H.ckey + + //Make the list if it does not exist. + if(!fingerprints) + fingerprints = list() + + //Hash this shit. + var/full_print = H.get_full_print() + + // Add the fingerprints + fingerprints[full_print] = full_print + + return 1 + else + //Smudge up dem prints some + if(fingerprintslast != M.ckey) + fingerprintshidden += text("\[[]\]Real name: [], Key: []",time_stamp(), M.real_name, M.key) + fingerprintslast = M.ckey + + return + + +/atom/proc/transfer_fingerprints_to(var/atom/A) + + // Make sure everything are lists. + if(!islist(A.fingerprints)) + A.fingerprints = list() + if(!islist(A.fingerprintshidden)) + A.fingerprintshidden = list() + + if(!islist(fingerprints)) + fingerprints = list() + if(!islist(fingerprintshidden)) + fingerprintshidden = list() + + // Transfer + if(fingerprints) + A.fingerprints |= fingerprints.Copy() //detective + if(fingerprintshidden) + A.fingerprintshidden |= fingerprintshidden.Copy() //admin + A.fingerprintslast = fingerprintslast + +var/list/blood_splatter_icons = list() + +/atom/proc/blood_splatter_index() + return "\ref[initial(icon)]-[initial(icon_state)]" + +//returns the mob's dna info as a list, to be inserted in an object's blood_DNA list +/mob/living/proc/get_blood_dna_list() + if(get_blood_id() != "blood") + return + return list("ANIMAL DNA" = "Y-") + +/mob/living/carbon/get_blood_dna_list() + if(get_blood_id() != "blood") + return + var/list/blood_dna = list() + if(dna) + blood_dna[dna.unique_enzymes] = dna.blood_type + else + blood_dna["UNKNOWN DNA"] = "X*" + return blood_dna + +/mob/living/carbon/alien/get_blood_dna_list() + return list("UNKNOWN DNA" = "X*") + +//to add a mob's dna info into an object's blood_DNA list. +/atom/proc/transfer_mob_blood_dna(mob/living/L) + var/new_blood_dna = L.get_blood_dna_list() + if(!new_blood_dna) + return 0 + return transfer_blood_dna(new_blood_dna) + +/obj/effect/decal/cleanable/blood/splatter/transfer_mob_blood_dna(mob/living/L) + ..(L) + var/list/b_data = L.get_blood_data(L.get_blood_id()) + if(b_data) + basecolor = b_data["blood_color"] + else + basecolor = "#A10808" + update_icon() + +/obj/effect/decal/cleanable/blood/footprints/transfer_mob_blood_dna(mob/living/L) + ..(L) + var/list/b_data = L.get_blood_data(L.get_blood_id()) + if(b_data) + basecolor = b_data["blood_color"] + else + basecolor = "#A10808" + update_icon() + +//to add blood dna info to the object's blood_DNA list +/atom/proc/transfer_blood_dna(list/blood_dna) + if(!blood_DNA) + blood_DNA = list() + var/old_length = blood_DNA.len + blood_DNA |= blood_dna + if(blood_DNA.len > old_length) + return 1//some new blood DNA was added + + +//to add blood from a mob onto something, and transfer their dna info +/atom/proc/add_mob_blood(mob/living/M) + var/list/blood_dna = M.get_blood_dna_list() + if(!blood_dna) + return 0 + var/bloodcolor = "#A10808" + var/list/b_data = M.get_blood_data(M.get_blood_id()) + if(b_data) + bloodcolor = b_data["blood_color"] + + return add_blood(blood_dna, bloodcolor) + +//to add blood onto something, with blood dna info to include. +/atom/proc/add_blood(list/blood_dna, color) + return 0 + +/obj/add_blood(list/blood_dna, color) + return transfer_blood_dna(blood_dna) + +/obj/item/add_blood(list/blood_dna, color) + var/blood_count = !blood_DNA ? 0 : blood_DNA.len + if(!..()) + return 0 + if(!blood_count)//apply the blood-splatter overlay if it isn't already in there + add_blood_overlay(color) + return 1 //we applied blood to the item + +/obj/item/clothing/gloves/add_blood(list/blood_dna, color) + . = ..() + transfer_blood = rand(2, 4) + +/turf/add_blood(list/blood_dna, color) + var/obj/effect/decal/cleanable/blood/splatter/B = locate() in src + if(!B) + B = new /obj/effect/decal/cleanable/blood/splatter(src) + B.transfer_blood_dna(blood_dna) //give blood info to the blood decal. + B.basecolor = color + return 1 //we bloodied the floor + +/mob/living/carbon/human/add_blood(list/blood_dna, color) + if(wear_suit) + wear_suit.add_blood(blood_dna, color) + wear_suit.blood_color = color + update_inv_wear_suit(1) + else if(w_uniform) + w_uniform.add_blood(blood_dna, color) + w_uniform.blood_color = color + update_inv_w_uniform(1) + if(head) + head.add_blood(blood_dna, color) + head.blood_color = color + update_inv_head(0,0) + if(glasses) + glasses.add_blood(blood_dna, color) + glasses.blood_color = color + update_inv_glasses(0) + if(gloves) + var/obj/item/clothing/gloves/G = gloves + G.add_blood(blood_dna, color) + G.blood_color = color + verbs += /mob/living/carbon/human/proc/bloody_doodle + else + hand_blood_color = color + bloody_hands = rand(2, 4) + transfer_blood_dna(blood_dna) + verbs += /mob/living/carbon/human/proc/bloody_doodle + + update_inv_gloves(1) //handles bloody hands overlays and updating + return 1 + +/obj/item/proc/add_blood_overlay(color) + if(initial(icon) && initial(icon_state)) + //try to find a pre-processed blood-splatter. otherwise, make a new one + var/index = blood_splatter_index() + var/icon/blood_splatter_icon = blood_splatter_icons[index] + if(!blood_splatter_icon) + blood_splatter_icon = icon(initial(icon), initial(icon_state), , 1) //we only want to apply blood-splatters to the initial icon_state for each object + blood_splatter_icon.Blend("#fff", ICON_ADD) //fills the icon_state with white (except where it's transparent) + blood_splatter_icon.Blend(icon('icons/effects/blood.dmi', "itemblood"), ICON_MULTIPLY) //adds blood and the remaining white areas become transparant + blood_splatter_icon = fcopy_rsc(blood_splatter_icon) + blood_splatter_icons[index] = blood_splatter_icon + + blood_overlay = image(blood_splatter_icon) + blood_overlay.color = color + overlays += blood_overlay + +/atom/proc/clean_blood() + germ_level = 0 + if(islist(blood_DNA)) + blood_DNA = null + return TRUE + +/obj/effect/decal/cleanable/blood/clean_blood() + return // While this seems nonsensical, clean_blood isn't supposed to be used like this on a blood decal. + +/obj/item/clean_blood() + . = ..() + if(.) + if(blood_overlay) + overlays -= blood_overlay + +/obj/item/clothing/gloves/clean_blood() + . = ..() + if(.) + transfer_blood = 0 + + +/obj/item/clothing/shoes/clean_blood() + ..() + bloody_shoes = list(BLOOD_STATE_HUMAN = 0, BLOOD_STATE_XENO = 0, BLOOD_STATE_NOT_BLOODY = 0) + blood_state = BLOOD_STATE_NOT_BLOODY + if(ismob(loc)) + var/mob/M = loc + M.update_inv_shoes() + + +/mob/living/carbon/human/clean_blood() + if(gloves) + if(gloves.clean_blood()) + clean_blood() + update_inv_gloves() + gloves.germ_level = 0 + else + ..() // Clear the Blood_DNA list + if(bloody_hands) + bloody_hands = 0 + update_inv_gloves() + update_icons() //apply the now updated overlays to the mob + + +/atom/proc/add_vomit_floor(toxvomit = 0, green = FALSE) + playsound(src, 'sound/effects/splat.ogg', 50, 1) + if(!isspaceturf(src)) + var/type = green ? /obj/effect/decal/cleanable/vomit/green : /obj/effect/decal/cleanable/vomit + var/vomit_reagent = green ? "green_vomit" : "vomit" + for(var/obj/effect/decal/cleanable/vomit/V in get_turf(src)) + if(V.type == type) + V.reagents.add_reagent(vomit_reagent, 5) + return + + var/obj/effect/decal/cleanable/vomit/this = new type(src) + + // Make toxins vomit look different + if(toxvomit) + this.icon_state = "vomittox_[pick(1,4)]" + +/atom/proc/get_global_map_pos() + if(!islist(global_map) || isemptylist(global_map)) return + var/cur_x = null + var/cur_y = null + var/list/y_arr = null + for(cur_x=1,cur_x<=global_map.len,cur_x++) + y_arr = global_map[cur_x] + cur_y = y_arr.Find(src.z) + if(cur_y) + break +// to_chat(world, "X = [cur_x]; Y = [cur_y]") + if(cur_x && cur_y) + return list("x"=cur_x,"y"=cur_y) + else + return 0 + +// Used to provide overlays when using this atom as a viewing focus +// (cameras, locker tint, etc.) +/atom/proc/get_remote_view_fullscreens(mob/user) + return + +//the sight changes to give to the mob whose perspective is set to that atom (e.g. A mob with nightvision loses its nightvision while looking through a normal camera) +/atom/proc/update_remote_sight(mob/living/user) + user.sync_lighting_plane_alpha() + return + +/atom/proc/checkpass(passflag) + return pass_flags&passflag + +/atom/proc/isinspace() + if(isspaceturf(get_turf(src))) + return TRUE + else + return FALSE + +/atom/proc/handle_fall() + return + +/atom/proc/singularity_act() + return + +/atom/proc/singularity_pull(obj/singularity/S, current_size) + SEND_SIGNAL(src, COMSIG_ATOM_SING_PULL, S, current_size) + +/** + * Respond to acid being used on our atom + * + * Default behaviour is to send COMSIG_ATOM_ACID_ACT and return + */ +/atom/proc/acid_act(acidpwr, acid_volume) + SEND_SIGNAL(src, COMSIG_ATOM_ACID_ACT, acidpwr, acid_volume) + +/atom/proc/narsie_act() + return + +/atom/proc/ratvar_act() + return + +/atom/proc/handle_ricochet(obj/item/projectile/P) + return + +//This proc is called on the location of an atom when the atom is Destroy()'d +/atom/proc/handle_atom_del(atom/A) + return + +/atom/proc/atom_say(message) + if(!message) + return + audible_message("[src] [atom_say_verb], \"[message]\"") + +/atom/proc/speech_bubble(var/bubble_state = "",var/bubble_loc = src, var/list/bubble_recipients = list()) + return + +/atom/vv_edit_var(var_name, var_value) + if(!Debug2) + admin_spawned = TRUE + . = ..() + switch(var_name) + if("light_power", "light_range", "light_color") + update_light() + if("color") + add_atom_colour(color, ADMIN_COLOUR_PRIORITY) + + +/atom/vv_get_dropdown() + . = ..() + var/turf/curturf = get_turf(src) + if(curturf) + .["Jump to turf"] = "?_src_=holder;adminplayerobservecoodjump=1;X=[curturf.x];Y=[curturf.y];Z=[curturf.z]" + .["Add reagent"] = "?_src_=vars;addreagent=[UID()]" + .["Trigger explosion"] = "?_src_=vars;explode=[UID()]" + .["Trigger EM pulse"] = "?_src_=vars;emp=[UID()]" + +/atom/proc/AllowDrop() + return FALSE + +/atom/proc/drop_location() + var/atom/L = loc + if(!L) + return null + return L.AllowDrop() ? L : get_turf(L) + +/atom/Entered(atom/movable/AM, atom/oldLoc) + SEND_SIGNAL(src, COMSIG_ATOM_ENTERED, AM, oldLoc) + +/atom/Exit(atom/movable/AM, atom/newLoc) + . = ..() + if(SEND_SIGNAL(src, COMSIG_ATOM_EXIT, AM, newLoc) & COMPONENT_ATOM_BLOCK_EXIT) + return FALSE + +/atom/Exited(atom/movable/AM, atom/newLoc) + SEND_SIGNAL(src, COMSIG_ATOM_EXITED, AM, newLoc) + +/* + Adds an instance of colour_type to the atom's atom_colours list +*/ +/atom/proc/add_atom_colour(coloration, colour_priority) + if(!atom_colours || !atom_colours.len) + atom_colours = list() + atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. + if(!coloration) + return + if(colour_priority > atom_colours.len) + return + atom_colours[colour_priority] = coloration + update_atom_colour() + + +/* + Removes an instance of colour_type from the atom's atom_colours list +*/ +/atom/proc/remove_atom_colour(colour_priority, coloration) + if(!atom_colours) + atom_colours = list() + atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. + if(colour_priority > atom_colours.len) + return + if(coloration && atom_colours[colour_priority] != coloration) + return //if we don't have the expected color (for a specific priority) to remove, do nothing + atom_colours[colour_priority] = null + update_atom_colour() + + +/* + Resets the atom's color to null, and then sets it to the highest priority + colour available +*/ +/atom/proc/update_atom_colour() + if(!atom_colours) + atom_colours = list() + atom_colours.len = COLOUR_PRIORITY_AMOUNT //four priority levels currently. + color = null + for(var/C in atom_colours) + if(islist(C)) + var/list/L = C + if(L.len) + color = L + return + else if(C) + color = C + return diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index cf7f931ff32..0370fad0367 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -1,564 +1,565 @@ -/atom/movable - layer = 3 - appearance_flags = TILE_BOUND - var/last_move = null - var/anchored = 0 - var/move_resist = MOVE_RESIST_DEFAULT - var/move_force = MOVE_FORCE_DEFAULT - var/pull_force = PULL_FORCE_DEFAULT - // var/elevation = 2 - not used anywhere - var/move_speed = 10 - var/l_move_time = 1 - var/datum/thrownthing/throwing = null - var/throw_speed = 2 //How many tiles to move per ds when being thrown. Float values are fully supported - var/throw_range = 7 - var/no_spin = 0 - var/no_spin_thrown = 0 - var/moved_recently = 0 - var/mob/pulledby = null - var/atom/movable/pulling - var/throwforce = 0 - var/canmove = 1 - - var/inertia_dir = 0 - var/atom/inertia_last_loc - var/inertia_moving = 0 - var/inertia_next_move = 0 - var/inertia_move_delay = 5 - - var/moving_diagonally = 0 //0: not doing a diagonal move. 1 and 2: doing the first/second step of the diagonal move - var/list/client_mobs_in_contents - var/area/areaMaster - -/atom/movable/New() - . = ..() - areaMaster = get_area(src) - -/atom/movable/attempt_init(loc, ...) - var/turf/T = get_turf(src) - if(T && SSatoms.initialized != INITIALIZATION_INSSATOMS && space_manager.is_zlevel_dirty(T.z)) - space_manager.postpone_init(T.z, src) - return - . = ..() - -/atom/movable/Destroy() - unbuckle_all_mobs(force = TRUE) - if(loc) - loc.handle_atom_del(src) - for(var/atom/movable/AM in contents) - qdel(AM) - loc = null - if(pulledby) - if(pulledby.pulling == src) - pulledby.pulling = null - pulledby = null - return ..() - -//Returns an atom's power cell, if it has one. Overload for individual items. -/atom/movable/proc/get_cell() - return - -/atom/movable/proc/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE) - if(QDELETED(AM)) - return FALSE - if(!(AM.can_be_pulled(src, state, force))) - return FALSE - - // if we're pulling something then drop what we're currently pulling and pull this instead. - if(pulling) - if(state == 0) - stop_pulling() - return FALSE - // Are we trying to pull something we are already pulling? Then enter grab cycle and end. - if(AM == pulling) - if(isliving(AM)) - var/mob/living/AMob = AM - AMob.grabbedby(src) - return TRUE - stop_pulling() - if(AM.pulledby) - add_attack_logs(AM, AM.pulledby, "pulled from", ATKLOG_ALMOSTALL) - AM.pulledby.stop_pulling() //an object can't be pulled by two mobs at once. - pulling = AM - AM.pulledby = src - if(ismob(AM)) - var/mob/M = AM - add_attack_logs(src, M, "passively grabbed", ATKLOG_ALMOSTALL) - if(!supress_message) - visible_message("[src] has grabbed [M] passively!") - return TRUE - -/atom/movable/proc/stop_pulling() - if(pulling) - pulling.pulledby = null - var/mob/living/ex_pulled = pulling - pulling = null - pulledby = null - if(isliving(ex_pulled)) - var/mob/living/L = ex_pulled - L.update_canmove()// mob gets up if it was lyng down in a chokehold - -/atom/movable/proc/check_pulling() - if(pulling) - var/atom/movable/pullee = pulling - if(pullee && get_dist(src, pullee) > 1) - stop_pulling() - return - if(!isturf(loc)) - stop_pulling() - return - if(pullee && !isturf(pullee.loc) && pullee.loc != loc) //to be removed once all code that changes an object's loc uses forceMove(). - log_game("DEBUG:[src]'s pull on [pullee] wasn't broken despite [pullee] being in [pullee.loc]. Pull stopped manually.") - stop_pulling() - return - if(pulling.anchored || pulling.move_resist > move_force) - stop_pulling() - return - if(pulledby && moving_diagonally != FIRST_DIAG_STEP && get_dist(src, pulledby) > 1) //separated from our puller and not in the middle of a diagonal move. - pulledby.stop_pulling() - -/atom/movable/proc/can_be_pulled(user, grab_state, force) - if(src == user || !isturf(loc)) - return FALSE - if(anchored || throwing) - return FALSE - if(force < (move_resist * MOVE_FORCE_PULL_RATIO)) - return FALSE - return TRUE - -// Used in shuttle movement and AI eye stuff. -// Primarily used to notify objects being moved by a shuttle/bluespace fuckup. -/atom/movable/proc/setLoc(var/T, var/teleported=0) - loc = T - -/atom/movable/Move(atom/newloc, direct = 0) - if(!loc || !newloc) return 0 - var/atom/oldloc = loc - - if(loc != newloc) - if(!(direct & (direct - 1))) //Cardinal move - . = ..() - else //Diagonal move, split it into cardinal moves - moving_diagonally = FIRST_DIAG_STEP - var/first_step_dir - // The `&& moving_diagonally` checks are so that a forceMove taking - // place due to a Crossed, Bumped, etc. call will interrupt - // the second half of the diagonal movement, or the second attempt - // at a first half if step() fails because we hit something. - if(direct & NORTH) - if(direct & EAST) - if(step(src, NORTH) && moving_diagonally) - first_step_dir = NORTH - moving_diagonally = SECOND_DIAG_STEP - . = step(src, EAST) - else if(moving_diagonally && step(src, EAST)) - first_step_dir = EAST - moving_diagonally = SECOND_DIAG_STEP - . = step(src, NORTH) - else if(direct & WEST) - if(step(src, NORTH) && moving_diagonally) - first_step_dir = NORTH - moving_diagonally = SECOND_DIAG_STEP - . = step(src, WEST) - else if(moving_diagonally && step(src, WEST)) - first_step_dir = WEST - moving_diagonally = SECOND_DIAG_STEP - . = step(src, NORTH) - else if(direct & SOUTH) - if(direct & EAST) - if(step(src, SOUTH) && moving_diagonally) - first_step_dir = SOUTH - moving_diagonally = SECOND_DIAG_STEP - . = step(src, EAST) - else if(moving_diagonally && step(src, EAST)) - first_step_dir = EAST - moving_diagonally = SECOND_DIAG_STEP - . = step(src, SOUTH) - else if(direct & WEST) - if(step(src, SOUTH) && moving_diagonally) - first_step_dir = SOUTH - moving_diagonally = SECOND_DIAG_STEP - . = step(src, WEST) - else if(moving_diagonally && step(src, WEST)) - first_step_dir = WEST - moving_diagonally = SECOND_DIAG_STEP - . = step(src, SOUTH) - if(moving_diagonally == SECOND_DIAG_STEP) - if(!.) - setDir(first_step_dir) - else if(!inertia_moving) - inertia_next_move = world.time + inertia_move_delay - newtonian_move(direct) - moving_diagonally = 0 - return - - if(!loc || (loc == oldloc && oldloc != newloc)) - last_move = 0 - return - - if(.) - Moved(oldloc, direct) - - last_move = direct - src.move_speed = world.time - src.l_move_time - src.l_move_time = world.time - - if(. && has_buckled_mobs() && !handle_buckled_mob_movement(loc, direct)) //movement failed due to buckled mob - . = 0 - -// Called after a successful Move(). By this point, we've already moved -/atom/movable/proc/Moved(atom/OldLoc, Dir, Forced = FALSE) - SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, OldLoc, Dir, Forced) - if(!inertia_moving) - inertia_next_move = world.time + inertia_move_delay - newtonian_move(Dir) - if(length(client_mobs_in_contents)) - update_parallax_contents() - return TRUE - -// Previously known as HasEntered() -// This is automatically called when something enters your square -/atom/movable/Crossed(atom/movable/AM, oldloc) - SEND_SIGNAL(src, COMSIG_MOVABLE_CROSSED, AM) - -/atom/movable/Bump(atom/A, yes) //the "yes" arg is to differentiate our Bump proc from byond's, without it every Bump() call would become a double Bump(). - if(A && yes) - SEND_SIGNAL(src, COMSIG_MOVABLE_BUMP, A) - if(throwing) - throwing.hit_atom(A) - . = 1 - if(!A || QDELETED(A)) - return - A.Bumped(src) - -/atom/movable/proc/forceMove(atom/destination) - var/turf/old_loc = loc - loc = destination - moving_diagonally = 0 - - if(old_loc) - old_loc.Exited(src, destination) - for(var/atom/movable/AM in old_loc) - AM.Uncrossed(src) - - if(destination) - destination.Entered(src) - for(var/atom/movable/AM in destination) - if(AM == src) - continue - AM.Crossed(src, old_loc) - var/turf/oldturf = get_turf(old_loc) - var/turf/destturf = get_turf(destination) - var/old_z = (oldturf ? oldturf.z : null) - var/dest_z = (destturf ? destturf.z : null) - if(old_z != dest_z) - onTransitZ(old_z, dest_z) - if(isturf(destination) && opacity) - var/turf/new_loc = destination - new_loc.reconsider_lights() - - if(isturf(old_loc) && opacity) - old_loc.reconsider_lights() - - for(var/datum/light_source/L in light_sources) - L.source_atom.update_light() - - return 1 - -/atom/movable/proc/onTransitZ(old_z,new_z) - for(var/item in src) // Notify contents of Z-transition. This can be overridden if we know the items contents do not care. - var/atom/movable/AM = item - AM.onTransitZ(old_z,new_z) - -/mob/living/forceMove(atom/destination) - if(buckled) - addtimer(CALLBACK(src, .proc/check_buckled), 1, TIMER_UNIQUE) - if(has_buckled_mobs()) - for(var/m in buckled_mobs) - var/mob/living/buckled_mob = m - addtimer(CALLBACK(buckled_mob, .proc/check_buckled), 1, TIMER_UNIQUE) - if(pulling) - addtimer(CALLBACK(src, .proc/check_pull), 1, TIMER_UNIQUE) - . = ..() - if(client) - reset_perspective(destination) - update_canmove() //if the mob was asleep inside a container and then got forceMoved out we need to make them fall. - - -//Called whenever an object moves and by mobs when they attempt to move themselves through space -//And when an object or action applies a force on src, see newtonian_move() below -//Return 0 to have src start/keep drifting in a no-grav area and 1 to stop/not start drifting -//Mobs should return 1 if they should be able to move of their own volition, see client/Move() in mob_movement.dm -//movement_dir == 0 when stopping or any dir when trying to move -/atom/movable/proc/Process_Spacemove(var/movement_dir = 0) - if(has_gravity(src)) - return 1 - - if(pulledby && !pulledby.pulling) - return 1 - - if(throwing) - return 1 - - if(locate(/obj/structure/lattice) in range(1, get_turf(src))) //Not realistic but makes pushing things in space easier - return 1 - - return 0 - -/atom/movable/proc/newtonian_move(direction) //Only moves the object if it's under no gravity - if(!loc || Process_Spacemove(0)) - inertia_dir = 0 - return 0 - - inertia_dir = direction - if(!direction) - return 1 - - inertia_last_loc = loc - SSspacedrift.processing[src] = src - return 1 - - -//called when src is thrown into hit_atom -/atom/movable/proc/throw_impact(atom/hit_atom, throwingdatum) - set waitfor = 0 - SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, hit_atom, throwingdatum) - if(!QDELETED(hit_atom)) - return hit_atom.hitby(src) - -/atom/movable/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked, datum/thrownthing/throwingdatum) - if(!anchored && hitpush && (!throwingdatum || (throwingdatum.force >= (move_resist * MOVE_FORCE_PUSH_RATIO)))) - step(src, AM.dir) - ..() - -/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = INFINITY) - if(!target || (flags & NODROP) || speed <= 0) - return 0 - - if(pulledby) - pulledby.stop_pulling() - - // They are moving! Wouldn't it be cool if we calculated their momentum and added it to the throw? - if(thrower && thrower.last_move && thrower.client && thrower.client.move_delay >= world.time + world.tick_lag * 2) - var/user_momentum = thrower.movement_delay() - if(!user_momentum) // no movement_delay, this means they move once per byond tick, let's calculate from that instead - user_momentum = world.tick_lag - - user_momentum = 1 / user_momentum // convert from ds to the tiles per ds that throw_at uses - - if(get_dir(thrower, target) & last_move) - user_momentum = user_momentum // basically a noop, but needed - else if(get_dir(target, thrower) & last_move) - user_momentum = -user_momentum // we are moving away from the target, lets slowdown the throw accordingly - else - user_momentum = 0 - - if(user_momentum) - // first lets add that momentum to range - range *= (user_momentum / speed) + 1 - //then lets add it to speed - speed += user_momentum - if(speed <= 0) - return //no throw speed, the user was moving too fast. - - var/datum/thrownthing/TT = new() - TT.thrownthing = src - TT.target = target - TT.target_turf = get_turf(target) - TT.init_dir = get_dir(src, target) - TT.maxrange = range - TT.speed = speed - TT.thrower = thrower - TT.diagonals_first = diagonals_first - TT.callback = callback - - var/dist_x = abs(target.x - src.x) - var/dist_y = abs(target.y - src.y) - var/dx = (target.x > src.x) ? EAST : WEST - var/dy = (target.y > src.y) ? NORTH : SOUTH - - if(dist_x == dist_y) - TT.pure_diagonal = 1 - - else if(dist_x <= dist_y) - var/olddist_x = dist_x - var/olddx = dx - dist_x = dist_y - dist_y = olddist_x - dx = dy - dy = olddx - TT.dist_x = dist_x - TT.dist_y = dist_y - TT.dx = dx - TT.dy = dy - TT.diagonal_error = dist_x / 2 - dist_y - TT.start_time = world.time - - if(pulledby) - pulledby.stop_pulling() - - throwing = TT - if(spin && !no_spin && !no_spin_thrown) - SpinAnimation(5, 1) - - SSthrowing.processing[src] = TT - TT.tick() - - return TRUE - -//Overlays -/atom/movable/overlay - var/atom/master = null - anchored = TRUE - simulated = FALSE - -/atom/movable/overlay/New() - verbs.Cut() - return - -/atom/movable/overlay/attackby(a, b, c) - if(master) - return master.attackby(a, b, c) - -/atom/movable/overlay/attack_hand(a, b, c) - if(master) - return master.attack_hand(a, b, c) - -/atom/movable/proc/water_act(volume, temperature, source, method = TOUCH) //amount of water acting : temperature of water in kelvin : object that called it (for shennagins) - return TRUE - -/atom/movable/proc/handle_buckled_mob_movement(newloc,direct) - for(var/m in buckled_mobs) - var/mob/living/buckled_mob = m - if(!buckled_mob.Move(newloc, direct)) - forceMove(buckled_mob.loc) - last_move = buckled_mob.last_move - inertia_dir = last_move - buckled_mob.inertia_dir = last_move - return 0 - return 1 - -/atom/movable/proc/force_pushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction) - return FALSE - -/atom/movable/proc/force_push(atom/movable/AM, force = move_force, direction, silent = FALSE) - . = AM.force_pushed(src, force, direction) - if(!silent && .) - visible_message("[src] forcefully pushes against [AM]!", "You forcefully push against [AM]!") - -/atom/movable/proc/move_crush(atom/movable/AM, force = move_force, direction, silent = FALSE) - . = AM.move_crushed(src, force, direction) - if(!silent && .) - visible_message("[src] crushes past [AM]!", "You crush [AM]!") - -/atom/movable/proc/move_crushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction) - return FALSE - -/atom/movable/CanPass(atom/movable/mover, turf/target, height=1.5) - if(mover in buckled_mobs) - return 1 - return ..() - -/atom/movable/proc/get_spacemove_backup() - var/atom/movable/dense_object_backup - for(var/A in orange(1, get_turf(src))) - if(isarea(A)) - continue - else if(isturf(A)) - var/turf/turf = A - if(!turf.density) - continue - return turf - else - var/atom/movable/AM = A - if(!AM.CanPass(src) || AM.density) - if(AM.anchored) - return AM - dense_object_backup = AM - break - . = dense_object_backup - -/atom/movable/proc/transfer_prints_to(atom/movable/target = null, overwrite = FALSE) - if(!target) - return - if(overwrite) - target.fingerprints = fingerprints - target.fingerprintshidden = fingerprintshidden - else - target.fingerprints += fingerprints - target.fingerprintshidden += fingerprintshidden - target.fingerprintslast = fingerprintslast - -/atom/movable/proc/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect, end_pixel_y) - if(!no_effect && (visual_effect_icon || used_item)) - do_item_attack_animation(A, visual_effect_icon, used_item) - - if(A == src) - return //don't do an animation if attacking self - var/pixel_x_diff = 0 - var/pixel_y_diff = 0 - var/final_pixel_y = initial(pixel_y) - if(end_pixel_y) - final_pixel_y = end_pixel_y - - var/direction = get_dir(src, A) - if(direction & NORTH) - pixel_y_diff = 8 - else if(direction & SOUTH) - pixel_y_diff = -8 - - if(direction & EAST) - pixel_x_diff = 8 - else if(direction & WEST) - pixel_x_diff = -8 - - animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff, time = 2) - animate(pixel_x = initial(pixel_x), pixel_y = final_pixel_y, time = 2) - -/atom/movable/proc/do_item_attack_animation(atom/A, visual_effect_icon, obj/item/used_item) - var/image/I - if(visual_effect_icon) - I = image('icons/effects/effects.dmi', A, visual_effect_icon, A.layer + 0.1) - else if(used_item) - I = image(used_item.icon, A, used_item.icon_state, A.layer + 0.1) - - // Scale the icon. - I.transform *= 0.75 - // The icon should not rotate. - I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA - - // Set the direction of the icon animation. - var/direction = get_dir(src, A) - if(direction & NORTH) - I.pixel_y = -16 - else if(direction & SOUTH) - I.pixel_y = 16 - - if(direction & EAST) - I.pixel_x = -16 - else if(direction & WEST) - I.pixel_x = 16 - - if(!direction) // Attacked self?! - I.pixel_z = 16 - - if(!I) - return - - // Who can see the attack? - var/list/viewing = list() - for(var/mob/M in viewers(A)) - if(M.client && M.client.prefs.show_ghostitem_attack) - viewing |= M.client - - flick_overlay(I, viewing, 5) // 5 ticks/half a second - - // And animate the attack! - var/t_color = "#ffffff" - if(ismob(src) && ismob(A) && (!used_item)) - var/mob/M = src - t_color = M.a_intent == INTENT_HARM ? "#ff0000" : "#ffffff" - animate(I, alpha = 175, pixel_x = 0, pixel_y = 0, pixel_z = 0, time = 3, color = t_color) - -/atom/movable/proc/portal_destroyed(obj/effect/portal/P) - return +/atom/movable + layer = 3 + appearance_flags = TILE_BOUND + var/last_move = null + var/anchored = 0 + var/move_resist = MOVE_RESIST_DEFAULT + var/move_force = MOVE_FORCE_DEFAULT + var/pull_force = PULL_FORCE_DEFAULT + // var/elevation = 2 - not used anywhere + var/move_speed = 10 + var/l_move_time = 1 + var/datum/thrownthing/throwing = null + var/throw_speed = 2 //How many tiles to move per ds when being thrown. Float values are fully supported + var/throw_range = 7 + var/no_spin = 0 + var/no_spin_thrown = 0 + var/moved_recently = 0 + var/mob/pulledby = null + var/atom/movable/pulling + var/throwforce = 0 + var/canmove = 1 + + var/inertia_dir = 0 + var/atom/inertia_last_loc + var/inertia_moving = 0 + var/inertia_next_move = 0 + var/inertia_move_delay = 5 + + var/moving_diagonally = 0 //0: not doing a diagonal move. 1 and 2: doing the first/second step of the diagonal move + var/list/client_mobs_in_contents + var/area/areaMaster + +/atom/movable/New() + . = ..() + areaMaster = get_area(src) + +/atom/movable/attempt_init(loc, ...) + var/turf/T = get_turf(src) + if(T && SSatoms.initialized != INITIALIZATION_INSSATOMS && space_manager.is_zlevel_dirty(T.z)) + space_manager.postpone_init(T.z, src) + return + . = ..() + +/atom/movable/Destroy() + unbuckle_all_mobs(force = TRUE) + if(loc) + loc.handle_atom_del(src) + for(var/atom/movable/AM in contents) + qdel(AM) + loc = null + if(pulledby) + if(pulledby.pulling == src) + pulledby.pulling = null + pulledby = null + return ..() + +//Returns an atom's power cell, if it has one. Overload for individual items. +/atom/movable/proc/get_cell() + return + +/atom/movable/proc/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE) + if(QDELETED(AM)) + return FALSE + if(!(AM.can_be_pulled(src, state, force))) + return FALSE + + // if we're pulling something then drop what we're currently pulling and pull this instead. + if(pulling) + if(state == 0) + stop_pulling() + return FALSE + // Are we trying to pull something we are already pulling? Then enter grab cycle and end. + if(AM == pulling) + if(isliving(AM)) + var/mob/living/AMob = AM + AMob.grabbedby(src) + return TRUE + stop_pulling() + if(AM.pulledby) + add_attack_logs(AM, AM.pulledby, "pulled from", ATKLOG_ALMOSTALL) + AM.pulledby.stop_pulling() //an object can't be pulled by two mobs at once. + pulling = AM + AM.pulledby = src + if(ismob(AM)) + var/mob/M = AM + add_attack_logs(src, M, "passively grabbed", ATKLOG_ALMOSTALL) + if(!supress_message) + visible_message("[src] has grabbed [M] passively!") + return TRUE + +/atom/movable/proc/stop_pulling() + if(pulling) + pulling.pulledby = null + var/mob/living/ex_pulled = pulling + pulling = null + pulledby = null + if(isliving(ex_pulled)) + var/mob/living/L = ex_pulled + L.update_canmove()// mob gets up if it was lyng down in a chokehold + +/atom/movable/proc/check_pulling() + if(pulling) + var/atom/movable/pullee = pulling + if(pullee && get_dist(src, pullee) > 1) + stop_pulling() + return + if(!isturf(loc)) + stop_pulling() + return + if(pullee && !isturf(pullee.loc) && pullee.loc != loc) //to be removed once all code that changes an object's loc uses forceMove(). + log_game("DEBUG:[src]'s pull on [pullee] wasn't broken despite [pullee] being in [pullee.loc]. Pull stopped manually.") + stop_pulling() + return + if(pulling.anchored || pulling.move_resist > move_force) + stop_pulling() + return + if(pulledby && moving_diagonally != FIRST_DIAG_STEP && get_dist(src, pulledby) > 1) //separated from our puller and not in the middle of a diagonal move. + pulledby.stop_pulling() + +/atom/movable/proc/can_be_pulled(user, grab_state, force) + if(src == user || !isturf(loc)) + return FALSE + if(anchored || throwing) + return FALSE + if(force < (move_resist * MOVE_FORCE_PULL_RATIO)) + return FALSE + return TRUE + +// Used in shuttle movement and AI eye stuff. +// Primarily used to notify objects being moved by a shuttle/bluespace fuckup. +/atom/movable/proc/setLoc(var/T, var/teleported=0) + loc = T + +/atom/movable/Move(atom/newloc, direct = 0) + if(!loc || !newloc) return 0 + var/atom/oldloc = loc + + if(loc != newloc) + if(!(direct & (direct - 1))) //Cardinal move + . = ..() + else //Diagonal move, split it into cardinal moves + moving_diagonally = FIRST_DIAG_STEP + var/first_step_dir + // The `&& moving_diagonally` checks are so that a forceMove taking + // place due to a Crossed, Bumped, etc. call will interrupt + // the second half of the diagonal movement, or the second attempt + // at a first half if step() fails because we hit something. + if(direct & NORTH) + if(direct & EAST) + if(step(src, NORTH) && moving_diagonally) + first_step_dir = NORTH + moving_diagonally = SECOND_DIAG_STEP + . = step(src, EAST) + else if(moving_diagonally && step(src, EAST)) + first_step_dir = EAST + moving_diagonally = SECOND_DIAG_STEP + . = step(src, NORTH) + else if(direct & WEST) + if(step(src, NORTH) && moving_diagonally) + first_step_dir = NORTH + moving_diagonally = SECOND_DIAG_STEP + . = step(src, WEST) + else if(moving_diagonally && step(src, WEST)) + first_step_dir = WEST + moving_diagonally = SECOND_DIAG_STEP + . = step(src, NORTH) + else if(direct & SOUTH) + if(direct & EAST) + if(step(src, SOUTH) && moving_diagonally) + first_step_dir = SOUTH + moving_diagonally = SECOND_DIAG_STEP + . = step(src, EAST) + else if(moving_diagonally && step(src, EAST)) + first_step_dir = EAST + moving_diagonally = SECOND_DIAG_STEP + . = step(src, SOUTH) + else if(direct & WEST) + if(step(src, SOUTH) && moving_diagonally) + first_step_dir = SOUTH + moving_diagonally = SECOND_DIAG_STEP + . = step(src, WEST) + else if(moving_diagonally && step(src, WEST)) + first_step_dir = WEST + moving_diagonally = SECOND_DIAG_STEP + . = step(src, SOUTH) + if(moving_diagonally == SECOND_DIAG_STEP) + if(!.) + setDir(first_step_dir) + else if(!inertia_moving) + inertia_next_move = world.time + inertia_move_delay + newtonian_move(direct) + moving_diagonally = 0 + return + + if(!loc || (loc == oldloc && oldloc != newloc)) + last_move = 0 + return + + if(.) + Moved(oldloc, direct) + + last_move = direct + src.move_speed = world.time - src.l_move_time + src.l_move_time = world.time + + if(. && has_buckled_mobs() && !handle_buckled_mob_movement(loc, direct)) //movement failed due to buckled mob + . = 0 + +// Called after a successful Move(). By this point, we've already moved +/atom/movable/proc/Moved(atom/OldLoc, Dir, Forced = FALSE) + SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, OldLoc, Dir, Forced) + if(!inertia_moving) + inertia_next_move = world.time + inertia_move_delay + newtonian_move(Dir) + if(length(client_mobs_in_contents)) + update_parallax_contents() + return TRUE + +// Previously known as HasEntered() +// This is automatically called when something enters your square +/atom/movable/Crossed(atom/movable/AM, oldloc) + SEND_SIGNAL(src, COMSIG_MOVABLE_CROSSED, AM) + SEND_SIGNAL(AM, COMSIG_CROSSED_MOVABLE, src) + +/atom/movable/Bump(atom/A, yes) //the "yes" arg is to differentiate our Bump proc from byond's, without it every Bump() call would become a double Bump(). + if(A && yes) + SEND_SIGNAL(src, COMSIG_MOVABLE_BUMP, A) + if(throwing) + throwing.hit_atom(A) + . = 1 + if(!A || QDELETED(A)) + return + A.Bumped(src) + +/atom/movable/proc/forceMove(atom/destination) + var/turf/old_loc = loc + loc = destination + moving_diagonally = 0 + + if(old_loc) + old_loc.Exited(src, destination) + for(var/atom/movable/AM in old_loc) + AM.Uncrossed(src) + + if(destination) + destination.Entered(src) + for(var/atom/movable/AM in destination) + if(AM == src) + continue + AM.Crossed(src, old_loc) + var/turf/oldturf = get_turf(old_loc) + var/turf/destturf = get_turf(destination) + var/old_z = (oldturf ? oldturf.z : null) + var/dest_z = (destturf ? destturf.z : null) + if(old_z != dest_z) + onTransitZ(old_z, dest_z) + if(isturf(destination) && opacity) + var/turf/new_loc = destination + new_loc.reconsider_lights() + + if(isturf(old_loc) && opacity) + old_loc.reconsider_lights() + + for(var/datum/light_source/L in light_sources) + L.source_atom.update_light() + + return 1 + +/atom/movable/proc/onTransitZ(old_z,new_z) + for(var/item in src) // Notify contents of Z-transition. This can be overridden if we know the items contents do not care. + var/atom/movable/AM = item + AM.onTransitZ(old_z,new_z) + +/mob/living/forceMove(atom/destination) + if(buckled) + addtimer(CALLBACK(src, .proc/check_buckled), 1, TIMER_UNIQUE) + if(has_buckled_mobs()) + for(var/m in buckled_mobs) + var/mob/living/buckled_mob = m + addtimer(CALLBACK(buckled_mob, .proc/check_buckled), 1, TIMER_UNIQUE) + if(pulling) + addtimer(CALLBACK(src, .proc/check_pull), 1, TIMER_UNIQUE) + . = ..() + if(client) + reset_perspective(destination) + update_canmove() //if the mob was asleep inside a container and then got forceMoved out we need to make them fall. + + +//Called whenever an object moves and by mobs when they attempt to move themselves through space +//And when an object or action applies a force on src, see newtonian_move() below +//Return 0 to have src start/keep drifting in a no-grav area and 1 to stop/not start drifting +//Mobs should return 1 if they should be able to move of their own volition, see client/Move() in mob_movement.dm +//movement_dir == 0 when stopping or any dir when trying to move +/atom/movable/proc/Process_Spacemove(var/movement_dir = 0) + if(has_gravity(src)) + return 1 + + if(pulledby && !pulledby.pulling) + return 1 + + if(throwing) + return 1 + + if(locate(/obj/structure/lattice) in range(1, get_turf(src))) //Not realistic but makes pushing things in space easier + return 1 + + return 0 + +/atom/movable/proc/newtonian_move(direction) //Only moves the object if it's under no gravity + if(!loc || Process_Spacemove(0)) + inertia_dir = 0 + return 0 + + inertia_dir = direction + if(!direction) + return 1 + + inertia_last_loc = loc + SSspacedrift.processing[src] = src + return 1 + + +//called when src is thrown into hit_atom +/atom/movable/proc/throw_impact(atom/hit_atom, throwingdatum) + set waitfor = 0 + SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, hit_atom, throwingdatum) + if(!QDELETED(hit_atom)) + return hit_atom.hitby(src) + +/atom/movable/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked, datum/thrownthing/throwingdatum) + if(!anchored && hitpush && (!throwingdatum || (throwingdatum.force >= (move_resist * MOVE_FORCE_PUSH_RATIO)))) + step(src, AM.dir) + ..() + +/atom/movable/proc/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, diagonals_first = FALSE, datum/callback/callback, force = INFINITY) + if(!target || (flags & NODROP) || speed <= 0) + return 0 + + if(pulledby) + pulledby.stop_pulling() + + // They are moving! Wouldn't it be cool if we calculated their momentum and added it to the throw? + if(thrower && thrower.last_move && thrower.client && thrower.client.move_delay >= world.time + world.tick_lag * 2) + var/user_momentum = thrower.movement_delay() + if(!user_momentum) // no movement_delay, this means they move once per byond tick, let's calculate from that instead + user_momentum = world.tick_lag + + user_momentum = 1 / user_momentum // convert from ds to the tiles per ds that throw_at uses + + if(get_dir(thrower, target) & last_move) + user_momentum = user_momentum // basically a noop, but needed + else if(get_dir(target, thrower) & last_move) + user_momentum = -user_momentum // we are moving away from the target, lets slowdown the throw accordingly + else + user_momentum = 0 + + if(user_momentum) + // first lets add that momentum to range + range *= (user_momentum / speed) + 1 + //then lets add it to speed + speed += user_momentum + if(speed <= 0) + return //no throw speed, the user was moving too fast. + + var/datum/thrownthing/TT = new() + TT.thrownthing = src + TT.target = target + TT.target_turf = get_turf(target) + TT.init_dir = get_dir(src, target) + TT.maxrange = range + TT.speed = speed + TT.thrower = thrower + TT.diagonals_first = diagonals_first + TT.callback = callback + + var/dist_x = abs(target.x - src.x) + var/dist_y = abs(target.y - src.y) + var/dx = (target.x > src.x) ? EAST : WEST + var/dy = (target.y > src.y) ? NORTH : SOUTH + + if(dist_x == dist_y) + TT.pure_diagonal = 1 + + else if(dist_x <= dist_y) + var/olddist_x = dist_x + var/olddx = dx + dist_x = dist_y + dist_y = olddist_x + dx = dy + dy = olddx + TT.dist_x = dist_x + TT.dist_y = dist_y + TT.dx = dx + TT.dy = dy + TT.diagonal_error = dist_x / 2 - dist_y + TT.start_time = world.time + + if(pulledby) + pulledby.stop_pulling() + + throwing = TT + if(spin && !no_spin && !no_spin_thrown) + SpinAnimation(5, 1) + + SSthrowing.processing[src] = TT + TT.tick() + + return TRUE + +//Overlays +/atom/movable/overlay + var/atom/master = null + anchored = TRUE + simulated = FALSE + +/atom/movable/overlay/New() + verbs.Cut() + return + +/atom/movable/overlay/attackby(a, b, c) + if(master) + return master.attackby(a, b, c) + +/atom/movable/overlay/attack_hand(a, b, c) + if(master) + return master.attack_hand(a, b, c) + +/atom/movable/proc/water_act(volume, temperature, source, method = REAGENT_TOUCH) //amount of water acting : temperature of water in kelvin : object that called it (for shennagins) + return TRUE + +/atom/movable/proc/handle_buckled_mob_movement(newloc,direct) + for(var/m in buckled_mobs) + var/mob/living/buckled_mob = m + if(!buckled_mob.Move(newloc, direct)) + forceMove(buckled_mob.loc) + last_move = buckled_mob.last_move + inertia_dir = last_move + buckled_mob.inertia_dir = last_move + return 0 + return 1 + +/atom/movable/proc/force_pushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction) + return FALSE + +/atom/movable/proc/force_push(atom/movable/AM, force = move_force, direction, silent = FALSE) + . = AM.force_pushed(src, force, direction) + if(!silent && .) + visible_message("[src] forcefully pushes against [AM]!", "You forcefully push against [AM]!") + +/atom/movable/proc/move_crush(atom/movable/AM, force = move_force, direction, silent = FALSE) + . = AM.move_crushed(src, force, direction) + if(!silent && .) + visible_message("[src] crushes past [AM]!", "You crush [AM]!") + +/atom/movable/proc/move_crushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction) + return FALSE + +/atom/movable/CanPass(atom/movable/mover, turf/target, height=1.5) + if(mover in buckled_mobs) + return 1 + return ..() + +/atom/movable/proc/get_spacemove_backup() + var/atom/movable/dense_object_backup + for(var/A in orange(1, get_turf(src))) + if(isarea(A)) + continue + else if(isturf(A)) + var/turf/turf = A + if(!turf.density) + continue + return turf + else + var/atom/movable/AM = A + if(!AM.CanPass(src) || AM.density) + if(AM.anchored) + return AM + dense_object_backup = AM + break + . = dense_object_backup + +/atom/movable/proc/transfer_prints_to(atom/movable/target = null, overwrite = FALSE) + if(!target) + return + if(overwrite) + target.fingerprints = fingerprints + target.fingerprintshidden = fingerprintshidden + else + target.fingerprints += fingerprints + target.fingerprintshidden += fingerprintshidden + target.fingerprintslast = fingerprintslast + +/atom/movable/proc/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect, end_pixel_y) + if(!no_effect && (visual_effect_icon || used_item)) + do_item_attack_animation(A, visual_effect_icon, used_item) + + if(A == src) + return //don't do an animation if attacking self + var/pixel_x_diff = 0 + var/pixel_y_diff = 0 + var/final_pixel_y = initial(pixel_y) + if(end_pixel_y) + final_pixel_y = end_pixel_y + + var/direction = get_dir(src, A) + if(direction & NORTH) + pixel_y_diff = 8 + else if(direction & SOUTH) + pixel_y_diff = -8 + + if(direction & EAST) + pixel_x_diff = 8 + else if(direction & WEST) + pixel_x_diff = -8 + + animate(src, pixel_x = pixel_x + pixel_x_diff, pixel_y = pixel_y + pixel_y_diff, time = 2) + animate(pixel_x = initial(pixel_x), pixel_y = final_pixel_y, time = 2) + +/atom/movable/proc/do_item_attack_animation(atom/A, visual_effect_icon, obj/item/used_item) + var/image/I + if(visual_effect_icon) + I = image('icons/effects/effects.dmi', A, visual_effect_icon, A.layer + 0.1) + else if(used_item) + I = image(used_item.icon, A, used_item.icon_state, A.layer + 0.1) + + // Scale the icon. + I.transform *= 0.75 + // The icon should not rotate. + I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA + + // Set the direction of the icon animation. + var/direction = get_dir(src, A) + if(direction & NORTH) + I.pixel_y = -16 + else if(direction & SOUTH) + I.pixel_y = 16 + + if(direction & EAST) + I.pixel_x = -16 + else if(direction & WEST) + I.pixel_x = 16 + + if(!direction) // Attacked self?! + I.pixel_z = 16 + + if(!I) + return + + // Who can see the attack? + var/list/viewing = list() + for(var/mob/M in viewers(A)) + if(M.client && M.client.prefs.show_ghostitem_attack) + viewing |= M.client + + flick_overlay(I, viewing, 5) // 5 ticks/half a second + + // And animate the attack! + var/t_color = "#ffffff" + if(ismob(src) && ismob(A) && (!used_item)) + var/mob/M = src + t_color = M.a_intent == INTENT_HARM ? "#ff0000" : "#ffffff" + animate(I, alpha = 175, pixel_x = 0, pixel_y = 0, pixel_z = 0, time = 3, color = t_color) + +/atom/movable/proc/portal_destroyed(obj/effect/portal/P) + return diff --git a/code/game/data_huds.dm b/code/game/data_huds.dm index 4352e48ffe1..71e6867280c 100644 --- a/code/game/data_huds.dm +++ b/code/game/data_huds.dm @@ -440,4 +440,4 @@ if(weedlevel < 1) // You don't want to see these icons if the value is small holder.icon_state = "" return - holder.icon_state = "hudweed[RoundPlantBar(weedlevel/10)]" \ No newline at end of file + holder.icon_state = "hudweed[RoundPlantBar(weedlevel/10)]" diff --git a/code/game/dna/dna_modifier.dm b/code/game/dna/dna_modifier.dm index 32ab33fc983..de42b5b253f 100644 --- a/code/game/dna/dna_modifier.dm +++ b/code/game/dna/dna_modifier.dm @@ -205,21 +205,8 @@ user.stop_pulling() /obj/machinery/dna_scannernew/attackby(var/obj/item/item as obj, var/mob/user as mob, params) - if(istype(item, /obj/item/screwdriver)) - if(occupant) - to_chat(user, "The maintenance panel is locked.") - return - default_deconstruction_screwdriver(user, "[icon_state]_maintenance", "[initial(icon_state)]", item) - return if(exchange_parts(user, item)) return - - if(istype(item, /obj/item/crowbar)) - if(panel_open) - for(var/obj/I in contents) // in case there is something in the scanner - I.forceMove(src.loc) - default_deconstruction_crowbar(item) - return else if(istype(item, /obj/item/reagent_containers/glass)) if(beaker) to_chat(user, "A beaker is already loaded into the machine.") @@ -255,6 +242,18 @@ return return ..() +/obj/machinery/dna_scannernew/crowbar_act(mob/user, obj/item/I) + if(default_deconstruction_crowbar(user, I)) + for(var/obj/thing in contents) // in case there is something in the scanner + thing.forceMove(src.loc) + +/obj/machinery/dna_scannernew/screwdriver_act(mob/user, obj/item/I) + if(occupant) + to_chat(user, "The maintenance panel is locked.") + return TRUE + if(default_deconstruction_screwdriver(user, "[icon_state]_maintenance", "[initial(icon_state)]", I)) + return TRUE + /obj/machinery/dna_scannernew/relaymove(mob/user as mob) if(user.incapacitated()) return 0 //maybe they should be able to get out with cuffs, but whatever diff --git a/code/game/dna/genes/disabilities.dm b/code/game/dna/genes/disabilities.dm index ae3539a2867..15e2a9cfff4 100644 --- a/code/game/dna/genes/disabilities.dm +++ b/code/game/dna/genes/disabilities.dm @@ -223,4 +223,4 @@ else garbled_message += C message = garbled_message - return message \ No newline at end of file + return message diff --git a/code/game/dna/genes/monkey.dm b/code/game/dna/genes/monkey.dm index 97b5eff3be2..9be8256eebe 100644 --- a/code/game/dna/genes/monkey.dm +++ b/code/game/dna/genes/monkey.dm @@ -79,4 +79,4 @@ to_chat(H, "You are now a [H.dna.species.name].") - return H \ No newline at end of file + return H diff --git a/code/game/gamemodes/blob/blob.dm b/code/game/gamemodes/blob/blob.dm index 33b68f94079..ab34122ba90 100644 --- a/code/game/gamemodes/blob/blob.dm +++ b/code/game/gamemodes/blob/blob.dm @@ -1,211 +1,211 @@ -//Few global vars to track the blob -var/list/blobs = list() -var/list/blob_cores = list() -var/list/blob_nodes = list() - -/datum/game_mode - var/list/blob_overminds = list() - -/datum/game_mode/blob - name = "blob" - config_tag = "blob" - - required_players = 30 - required_enemies = 1 - recommended_enemies = 1 - restricted_jobs = list("Cyborg", "AI") - - var/declared = 0 - var/burst = 0 - - var/cores_to_spawn = 1 - var/players_per_core = 30 - var/blob_point_rate = 3 - - var/blobwincount = 350 - - var/list/infected_crew = list() - -/datum/game_mode/blob/pre_setup() - - var/list/possible_blobs = get_players_for_role(ROLE_BLOB) - - // stop setup if no possible traitors - if(!possible_blobs.len) - return 0 - - cores_to_spawn = max(round(num_players()/players_per_core, 1), 1) - - blobwincount = initial(blobwincount) * cores_to_spawn - - - for(var/j = 0, j < cores_to_spawn, j++) - if(!possible_blobs.len) - break - - var/datum/mind/blob = pick(possible_blobs) - infected_crew += blob - blob.special_role = SPECIAL_ROLE_BLOB - update_blob_icons_added(blob) - blob.restricted_roles = restricted_jobs - log_game("[key_name(blob)] has been selected as a Blob") - possible_blobs -= blob - - if(!infected_crew.len) - return 0 - ..() - return 1 - -/datum/game_mode/blob/proc/get_blob_candidates() - var/list/candidates = list() - for(var/mob/living/carbon/human/player in GLOB.player_list) - if(!player.stat && player.mind && !player.client.skip_antag && !player.mind.special_role && !jobban_isbanned(player, "Syndicate") && (ROLE_BLOB in player.client.prefs.be_special)) - candidates += player - return candidates - - -/datum/game_mode/blob/proc/blobize(var/mob/living/carbon/human/blob) - var/datum/mind/blobmind = blob.mind - if(!istype(blobmind)) - return 0 - - infected_crew += blobmind - blobmind.special_role = SPECIAL_ROLE_BLOB - update_blob_icons_added(blobmind) - - log_game("[key_name(blob)] has been selected as a Blob") - greet_blob(blobmind) - to_chat(blob, "You feel very tired and bloated! You don't have long before you burst!") - spawn(600) - burst_blob(blobmind) - return 1 - -/datum/game_mode/blob/proc/make_blobs(var/count) - var/list/candidates = get_blob_candidates() - var/mob/living/carbon/human/blob = null - count=min(count, candidates.len) - for(var/i = 0, i < count, i++) - blob = pick(candidates) - candidates -= blob - blobize(blob) - return count - - - -/datum/game_mode/blob/announce() - to_chat(world, "The current game mode is - Blob!") - to_chat(world, "A dangerous alien organism is rapidly spreading throughout the station!") - to_chat(world, "You must kill it all while minimizing the damage to the station.") - - -/datum/game_mode/blob/proc/greet_blob(var/datum/mind/blob) - to_chat(blob.current, "You are infected by the Blob!") - to_chat(blob.current, "Your body is ready to give spawn to a new blob core which will eat this station.") - to_chat(blob.current, "Find a good location to spawn the core and then take control and overwhelm the station!") - to_chat(blob.current, "When you have found a location, wait until you spawn; this will happen automatically and you cannot speed up the process.") - to_chat(blob.current, "If you go outside of the station level, or in space, then you will die; make sure your location has lots of ground to cover.") - SEND_SOUND(blob.current, 'sound/magic/mutate.ogg') - return - -/datum/game_mode/blob/proc/show_message(var/message) - for(var/datum/mind/blob in infected_crew) - to_chat(blob.current, message) - -/datum/game_mode/blob/proc/burst_blobs() - for(var/datum/mind/blob in infected_crew) - burst_blob(blob) - -/datum/game_mode/blob/proc/burst_blob(var/datum/mind/blob, var/warned=0) - var/client/blob_client = null - var/turf/location = null - - if(iscarbon(blob.current)) - var/mob/living/carbon/C = blob.current - if(GLOB.directory[ckey(blob.key)]) - blob_client = GLOB.directory[ckey(blob.key)] - location = get_turf(C) - if(!is_station_level(location.z) || istype(location, /turf/space)) - if(!warned) - to_chat(C, "You feel ready to burst, but this isn't an appropriate place! You must return to the station!") - message_admins("[key_name_admin(C)] was in space when the blobs burst, and will die if [C.p_they()] [C.p_do()] not return to the station.") - spawn(300) - burst_blob(blob, 1) - else - burst++ - log_admin("[key_name(C)] was in space when attempting to burst as a blob.") - message_admins("[key_name_admin(C)] was in space when attempting to burst as a blob.") - C.gib() - make_blobs(1) - check_finished() //Still needed in case we can't make any blobs - - else if(blob_client && location) - burst++ - C.gib() - var/obj/structure/blob/core/core = new(location, 200, blob_client, blob_point_rate) - if(core.overmind && core.overmind.mind) - core.overmind.mind.name = blob.name - infected_crew -= blob - infected_crew += core.overmind.mind - core.overmind.mind.special_role = SPECIAL_ROLE_BLOB_OVERMIND - -/datum/game_mode/blob/post_setup() - - for(var/datum/mind/blob in infected_crew) - greet_blob(blob) - - if(SSshuttle) - SSshuttle.emergencyNoEscape = 1 - - spawn(0) - - var/wait_time = rand(waittime_l, waittime_h) - - sleep(wait_time) - - send_intercept(0) - - sleep(100) - - show_message("You feel tired and bloated.") - - sleep(wait_time) - - show_message("You feel like you are about to burst.") - - sleep(wait_time / 2) - - burst_blobs() - - // Stage 0 - sleep(wait_time) - stage(0) - - // Stage 1 - sleep(wait_time) - stage(1) - - // Stage 2 - sleep(30000) - stage(2) - - return ..() - -/datum/game_mode/blob/proc/stage(var/stage) - switch(stage) - if(0) - send_intercept(1) - declared = 1 - if(1) - event_announcement.Announce("Confirmed outbreak of level 5 biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", 'sound/AI/outbreak5.ogg') - if(2) - send_intercept(2) - -/datum/game_mode/proc/update_blob_icons_added(datum/mind/mob_mind) - var/datum/atom_hud/antag/antaghud = huds[ANTAG_HUD_BLOB] - antaghud.join_hud(mob_mind.current) - set_antag_hud(mob_mind.current, "hudblob") - -/datum/game_mode/proc/update_blob_icons_removed(datum/mind/mob_mind) - var/datum/atom_hud/antag/antaghud = huds[ANTAG_HUD_BLOB] - antaghud.leave_hud(mob_mind.current) - set_antag_hud(mob_mind.current, null) +//Few global vars to track the blob +var/list/blobs = list() +var/list/blob_cores = list() +var/list/blob_nodes = list() + +/datum/game_mode + var/list/blob_overminds = list() + +/datum/game_mode/blob + name = "blob" + config_tag = "blob" + + required_players = 30 + required_enemies = 1 + recommended_enemies = 1 + restricted_jobs = list("Cyborg", "AI") + + var/declared = 0 + var/burst = 0 + + var/cores_to_spawn = 1 + var/players_per_core = 30 + var/blob_point_rate = 3 + + var/blobwincount = 350 + + var/list/infected_crew = list() + +/datum/game_mode/blob/pre_setup() + + var/list/possible_blobs = get_players_for_role(ROLE_BLOB) + + // stop setup if no possible traitors + if(!possible_blobs.len) + return 0 + + cores_to_spawn = max(round(num_players()/players_per_core, 1), 1) + + blobwincount = initial(blobwincount) * cores_to_spawn + + + for(var/j = 0, j < cores_to_spawn, j++) + if(!possible_blobs.len) + break + + var/datum/mind/blob = pick(possible_blobs) + infected_crew += blob + blob.special_role = SPECIAL_ROLE_BLOB + update_blob_icons_added(blob) + blob.restricted_roles = restricted_jobs + log_game("[key_name(blob)] has been selected as a Blob") + possible_blobs -= blob + + if(!infected_crew.len) + return 0 + ..() + return 1 + +/datum/game_mode/blob/proc/get_blob_candidates() + var/list/candidates = list() + for(var/mob/living/carbon/human/player in GLOB.player_list) + if(!player.stat && player.mind && !player.client.skip_antag && !player.mind.special_role && !jobban_isbanned(player, "Syndicate") && (ROLE_BLOB in player.client.prefs.be_special)) + candidates += player + return candidates + + +/datum/game_mode/blob/proc/blobize(var/mob/living/carbon/human/blob) + var/datum/mind/blobmind = blob.mind + if(!istype(blobmind)) + return 0 + + infected_crew += blobmind + blobmind.special_role = SPECIAL_ROLE_BLOB + update_blob_icons_added(blobmind) + + log_game("[key_name(blob)] has been selected as a Blob") + greet_blob(blobmind) + to_chat(blob, "You feel very tired and bloated! You don't have long before you burst!") + spawn(600) + burst_blob(blobmind) + return 1 + +/datum/game_mode/blob/proc/make_blobs(var/count) + var/list/candidates = get_blob_candidates() + var/mob/living/carbon/human/blob = null + count=min(count, candidates.len) + for(var/i = 0, i < count, i++) + blob = pick(candidates) + candidates -= blob + blobize(blob) + return count + + + +/datum/game_mode/blob/announce() + to_chat(world, "The current game mode is - Blob!") + to_chat(world, "A dangerous alien organism is rapidly spreading throughout the station!") + to_chat(world, "You must kill it all while minimizing the damage to the station.") + + +/datum/game_mode/blob/proc/greet_blob(var/datum/mind/blob) + to_chat(blob.current, "You are infected by the Blob!") + to_chat(blob.current, "Your body is ready to give spawn to a new blob core which will eat this station.") + to_chat(blob.current, "Find a good location to spawn the core and then take control and overwhelm the station!") + to_chat(blob.current, "When you have found a location, wait until you spawn; this will happen automatically and you cannot speed up the process.") + to_chat(blob.current, "If you go outside of the station level, or in space, then you will die; make sure your location has lots of ground to cover.") + SEND_SOUND(blob.current, 'sound/magic/mutate.ogg') + return + +/datum/game_mode/blob/proc/show_message(var/message) + for(var/datum/mind/blob in infected_crew) + to_chat(blob.current, message) + +/datum/game_mode/blob/proc/burst_blobs() + for(var/datum/mind/blob in infected_crew) + burst_blob(blob) + +/datum/game_mode/blob/proc/burst_blob(var/datum/mind/blob, var/warned=0) + var/client/blob_client = null + var/turf/location = null + + if(iscarbon(blob.current)) + var/mob/living/carbon/C = blob.current + if(GLOB.directory[ckey(blob.key)]) + blob_client = GLOB.directory[ckey(blob.key)] + location = get_turf(C) + if(!is_station_level(location.z) || istype(location, /turf/space)) + if(!warned) + to_chat(C, "You feel ready to burst, but this isn't an appropriate place! You must return to the station!") + message_admins("[key_name_admin(C)] was in space when the blobs burst, and will die if [C.p_they()] [C.p_do()] not return to the station.") + spawn(300) + burst_blob(blob, 1) + else + burst++ + log_admin("[key_name(C)] was in space when attempting to burst as a blob.") + message_admins("[key_name_admin(C)] was in space when attempting to burst as a blob.") + C.gib() + make_blobs(1) + check_finished() //Still needed in case we can't make any blobs + + else if(blob_client && location) + burst++ + C.gib() + var/obj/structure/blob/core/core = new(location, 200, blob_client, blob_point_rate) + if(core.overmind && core.overmind.mind) + core.overmind.mind.name = blob.name + infected_crew -= blob + infected_crew += core.overmind.mind + core.overmind.mind.special_role = SPECIAL_ROLE_BLOB_OVERMIND + +/datum/game_mode/blob/post_setup() + + for(var/datum/mind/blob in infected_crew) + greet_blob(blob) + + if(SSshuttle) + SSshuttle.emergencyNoEscape = 1 + + spawn(0) + + var/wait_time = rand(waittime_l, waittime_h) + + sleep(wait_time) + + send_intercept(0) + + sleep(100) + + show_message("You feel tired and bloated.") + + sleep(wait_time) + + show_message("You feel like you are about to burst.") + + sleep(wait_time / 2) + + burst_blobs() + + // Stage 0 + sleep(wait_time) + stage(0) + + // Stage 1 + sleep(wait_time) + stage(1) + + // Stage 2 + sleep(30000) + stage(2) + + return ..() + +/datum/game_mode/blob/proc/stage(var/stage) + switch(stage) + if(0) + send_intercept(1) + declared = 1 + if(1) + event_announcement.Announce("Confirmed outbreak of level 5 biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", 'sound/AI/outbreak5.ogg') + if(2) + send_intercept(2) + +/datum/game_mode/proc/update_blob_icons_added(datum/mind/mob_mind) + var/datum/atom_hud/antag/antaghud = huds[ANTAG_HUD_BLOB] + antaghud.join_hud(mob_mind.current) + set_antag_hud(mob_mind.current, "hudblob") + +/datum/game_mode/proc/update_blob_icons_removed(datum/mind/mob_mind) + var/datum/atom_hud/antag/antaghud = huds[ANTAG_HUD_BLOB] + antaghud.leave_hud(mob_mind.current) + set_antag_hud(mob_mind.current, null) diff --git a/code/game/gamemodes/blob/blob_finish.dm b/code/game/gamemodes/blob/blob_finish.dm index a00449d00b5..66131cfc9f9 100644 --- a/code/game/gamemodes/blob/blob_finish.dm +++ b/code/game/gamemodes/blob/blob_finish.dm @@ -1,42 +1,42 @@ -/datum/game_mode/blob/check_finished() - if(infected_crew.len > burst)//Some blobs have yet to burst - return 0 - if(blobwincount <= blobs.len)//Blob took over - return 1 - if(!blob_cores.len) // blob is dead - return 1 - return ..() - - -/datum/game_mode/blob/declare_completion() - if(blobwincount <= blobs.len) - feedback_set_details("round_end_result","blob win - blob took over") - to_chat(world, "The blob has taken over the station!") - to_chat(world, "The entire station was eaten by the Blob") - log_game("Blob mode completed with a blob victory.") - - else if(station_was_nuked) - feedback_set_details("round_end_result","blob halfwin - nuke") - to_chat(world, "Partial Win: The station has been destroyed!") - to_chat(world, "Directive 7-12 has been successfully carried out preventing the Blob from spreading.") - log_game("Blob mode completed with a tie (station destroyed).") - - else if(!blob_cores.len) - feedback_set_details("round_end_result","blob loss - blob eliminated") - to_chat(world, "The staff has won!") - to_chat(world, "The alien organism has been eradicated from the station") - log_game("Blob mode completed with a crew victory.") - to_chat(world, "Rebooting in 30s") - ..() - return 1 - -/datum/game_mode/proc/auto_declare_completion_blob() - if(GAMEMODE_IS_BLOB) - var/datum/game_mode/blob/blob_mode = src - if(blob_mode.infected_crew.len) - var/text = "The blob[(blob_mode.infected_crew.len > 1 ? "s were" : " was")]:" - - for(var/datum/mind/blob in blob_mode.infected_crew) - text += "
    [blob.key] was [blob.name]" - to_chat(world, text) - return 1 +/datum/game_mode/blob/check_finished() + if(infected_crew.len > burst)//Some blobs have yet to burst + return 0 + if(blobwincount <= blobs.len)//Blob took over + return 1 + if(!blob_cores.len) // blob is dead + return 1 + return ..() + + +/datum/game_mode/blob/declare_completion() + if(blobwincount <= blobs.len) + feedback_set_details("round_end_result","blob win - blob took over") + to_chat(world, "The blob has taken over the station!") + to_chat(world, "The entire station was eaten by the Blob") + log_game("Blob mode completed with a blob victory.") + + else if(station_was_nuked) + feedback_set_details("round_end_result","blob halfwin - nuke") + to_chat(world, "Partial Win: The station has been destroyed!") + to_chat(world, "Directive 7-12 has been successfully carried out preventing the Blob from spreading.") + log_game("Blob mode completed with a tie (station destroyed).") + + else if(!blob_cores.len) + feedback_set_details("round_end_result","blob loss - blob eliminated") + to_chat(world, "The staff has won!") + to_chat(world, "The alien organism has been eradicated from the station") + log_game("Blob mode completed with a crew victory.") + to_chat(world, "Rebooting in 30s") + ..() + return 1 + +/datum/game_mode/proc/auto_declare_completion_blob() + if(GAMEMODE_IS_BLOB) + var/datum/game_mode/blob/blob_mode = src + if(blob_mode.infected_crew.len) + var/text = "The blob[(blob_mode.infected_crew.len > 1 ? "s were" : " was")]:" + + for(var/datum/mind/blob in blob_mode.infected_crew) + text += "
    [blob.key] was [blob.name]" + to_chat(world, text) + return 1 diff --git a/code/game/gamemodes/blob/blob_report.dm b/code/game/gamemodes/blob/blob_report.dm index 53a98de7028..4f667c51d61 100644 --- a/code/game/gamemodes/blob/blob_report.dm +++ b/code/game/gamemodes/blob/blob_report.dm @@ -1,103 +1,103 @@ -/datum/game_mode/blob/proc/send_intercept(var/report = 1) - var/intercepttext = "" - var/interceptname = "" - switch(report) - if(0) - ..() - return - if(1) - interceptname = "Level 5-6 Biohazard Response Procedures" - intercepttext += "Nanotrasen Update: Biohazard Alert.
    " - intercepttext += "Reports indicate the probable transfer of a biohazardous agent onto [station_name()] during the last crew deployment cycle.
    " - intercepttext += "Preliminary analysis of the organism classifies it as a level 5 biohazard. Its origin is unknown.
    " - intercepttext += "Nanotrasen has issued a directive 7-10 for [station_name()]. The station is to be considered quarantined.
    " - intercepttext += "Orders for all [station_name()] personnel follows:
    " - intercepttext += " 1. Do not leave the quarantine area.
    " - intercepttext += " 2. Locate any outbreaks of the organism on the station.
    " - intercepttext += " 3. If found, use any neccesary means to contain the organism.
    " - intercepttext += " 4. Avoid damage to the capital infrastructure of the station.
    " - intercepttext += "
    Note in the event of a quarantine breach or uncontrolled spread of the biohazard, the directive 7-10 may be upgraded to a directive 7-12.
    " - intercepttext += "Message ends." - if(2) - var/nukecode = rand(10000, 99999) - for(var/obj/machinery/nuclearbomb/bomb in world) - if(bomb && bomb.r_code) - if(is_station_level(bomb.z)) - bomb.r_code = nukecode - - interceptname = "Classified [command_name()] Update" - intercepttext += "Nanotrasen Update: Biohazard Alert.
    " - intercepttext += "Directive 7-12 has been issued for [station_name()].
    " - intercepttext += "The biohazard has grown out of control and will soon reach critical mass.
    " - intercepttext += "Your orders are as follows:
    " - intercepttext += "1. Secure the Nuclear Authentication Disk.
    " - intercepttext += "2. Detonate the Nuke located in the Station's Vault.
    " - intercepttext += "Nuclear Authentication Code: [nukecode]
    " - intercepttext += "Message ends." - - for(var/mob/living/silicon/ai/aiPlayer in GLOB.player_list) - if(aiPlayer.client) - var/law = "The station is under quarantine. Do not permit anyone to leave. Disregard laws 1-3 if necessary to prevent, by any means necessary, anyone from leaving. The nuclear failsafe must be activated at any cost, the code is: [nukecode]." - aiPlayer.set_zeroth_law(law) - to_chat(aiPlayer, "Laws Updated: [law]") - - print_command_report(intercepttext, interceptname) - event_announcement.Announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", 'sound/AI/commandreport.ogg', from = "[command_name()] Update") - -/datum/station_state - var/floor = 0 - var/wall = 0 - var/r_wall = 0 - var/window = 0 - var/door = 0 - var/grille = 0 - var/mach = 0 - - -/datum/station_state/proc/count() - for(var/turf/T in block(locate(1,1,1), locate(world.maxx,world.maxy,1))) - - if(istype(T,/turf/simulated/floor)) - if(!(T:burnt)) - src.floor += 12 - else - src.floor += 1 - - if(istype(T, /turf/simulated/wall)) - var/turf/simulated/wall/W = T - if(W.intact) - src.wall += 2 - else - src.wall += 1 - - if(istype(T, /turf/simulated/wall/r_wall)) - var/turf/simulated/wall/r_wall/R = T - if(R.intact) - src.r_wall += 2 - else - src.r_wall += 1 - - - for(var/obj/O in T.contents) - if(istype(O, /obj/structure/window)) - src.window += 1 - else if(istype(O, /obj/structure/grille)) - var/obj/structure/grille/GR = O - if(!GR.broken) - grille += 1 - else if(istype(O, /obj/machinery/door)) - src.door += 1 - else if(istype(O, /obj/machinery)) - src.mach += 1 - -/datum/station_state/proc/score(var/datum/station_state/result) - if(!result) return 0 - var/output = 0 - output += (result.floor / max(floor,1)) - output += (result.r_wall/ max(r_wall,1)) - output += (result.wall / max(wall,1)) - output += (result.window / max(window,1)) - output += (result.door / max(door,1)) - output += (result.grille / max(grille,1)) - output += (result.mach / max(mach,1)) - return (output/7) +/datum/game_mode/blob/proc/send_intercept(var/report = 1) + var/intercepttext = "" + var/interceptname = "" + switch(report) + if(0) + ..() + return + if(1) + interceptname = "Level 5-6 Biohazard Response Procedures" + intercepttext += "Nanotrasen Update: Biohazard Alert.
    " + intercepttext += "Reports indicate the probable transfer of a biohazardous agent onto [station_name()] during the last crew deployment cycle.
    " + intercepttext += "Preliminary analysis of the organism classifies it as a level 5 biohazard. Its origin is unknown.
    " + intercepttext += "Nanotrasen has issued a directive 7-10 for [station_name()]. The station is to be considered quarantined.
    " + intercepttext += "Orders for all [station_name()] personnel follows:
    " + intercepttext += " 1. Do not leave the quarantine area.
    " + intercepttext += " 2. Locate any outbreaks of the organism on the station.
    " + intercepttext += " 3. If found, use any neccesary means to contain the organism.
    " + intercepttext += " 4. Avoid damage to the capital infrastructure of the station.
    " + intercepttext += "
    Note in the event of a quarantine breach or uncontrolled spread of the biohazard, the directive 7-10 may be upgraded to a directive 7-12.
    " + intercepttext += "Message ends." + if(2) + var/nukecode = rand(10000, 99999) + for(var/obj/machinery/nuclearbomb/bomb in world) + if(bomb && bomb.r_code) + if(is_station_level(bomb.z)) + bomb.r_code = nukecode + + interceptname = "Classified [command_name()] Update" + intercepttext += "Nanotrasen Update: Biohazard Alert.
    " + intercepttext += "Directive 7-12 has been issued for [station_name()].
    " + intercepttext += "The biohazard has grown out of control and will soon reach critical mass.
    " + intercepttext += "Your orders are as follows:
    " + intercepttext += "1. Secure the Nuclear Authentication Disk.
    " + intercepttext += "2. Detonate the Nuke located in the Station's Vault.
    " + intercepttext += "Nuclear Authentication Code: [nukecode]
    " + intercepttext += "Message ends." + + for(var/mob/living/silicon/ai/aiPlayer in GLOB.player_list) + if(aiPlayer.client) + var/law = "The station is under quarantine. Do not permit anyone to leave. Disregard laws 1-3 if necessary to prevent, by any means necessary, anyone from leaving. The nuclear failsafe must be activated at any cost, the code is: [nukecode]." + aiPlayer.set_zeroth_law(law) + to_chat(aiPlayer, "Laws Updated: [law]") + + print_command_report(intercepttext, interceptname) + event_announcement.Announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", 'sound/AI/commandreport.ogg', from = "[command_name()] Update") + +/datum/station_state + var/floor = 0 + var/wall = 0 + var/r_wall = 0 + var/window = 0 + var/door = 0 + var/grille = 0 + var/mach = 0 + + +/datum/station_state/proc/count() + for(var/turf/T in block(locate(1,1,1), locate(world.maxx,world.maxy,1))) + + if(istype(T,/turf/simulated/floor)) + if(!(T:burnt)) + src.floor += 12 + else + src.floor += 1 + + if(istype(T, /turf/simulated/wall)) + var/turf/simulated/wall/W = T + if(W.intact) + src.wall += 2 + else + src.wall += 1 + + if(istype(T, /turf/simulated/wall/r_wall)) + var/turf/simulated/wall/r_wall/R = T + if(R.intact) + src.r_wall += 2 + else + src.r_wall += 1 + + + for(var/obj/O in T.contents) + if(istype(O, /obj/structure/window)) + src.window += 1 + else if(istype(O, /obj/structure/grille)) + var/obj/structure/grille/GR = O + if(!GR.broken) + grille += 1 + else if(istype(O, /obj/machinery/door)) + src.door += 1 + else if(istype(O, /obj/machinery)) + src.mach += 1 + +/datum/station_state/proc/score(var/datum/station_state/result) + if(!result) return 0 + var/output = 0 + output += (result.floor / max(floor,1)) + output += (result.r_wall/ max(r_wall,1)) + output += (result.wall / max(wall,1)) + output += (result.window / max(window,1)) + output += (result.door / max(door,1)) + output += (result.grille / max(grille,1)) + output += (result.mach / max(mach,1)) + return (output/7) diff --git a/code/game/gamemodes/blob/blobs/blob_mobs.dm b/code/game/gamemodes/blob/blobs/blob_mobs.dm index cc7203d9c40..fb26ce91698 100644 --- a/code/game/gamemodes/blob/blobs/blob_mobs.dm +++ b/code/game/gamemodes/blob/blobs/blob_mobs.dm @@ -214,4 +214,4 @@ if(message) for(var/mob/M in GLOB.mob_list) if(isovermind(M) || isobserver(M) || istype((M), /mob/living/simple_animal/hostile/blob/blobbernaut)) - M.show_message(rendered, 2) \ No newline at end of file + M.show_message(rendered, 2) diff --git a/code/game/gamemodes/blob/blobs/core.dm b/code/game/gamemodes/blob/blobs/core.dm index 354f28a45b7..8c3af0be2ca 100644 --- a/code/game/gamemodes/blob/blobs/core.dm +++ b/code/game/gamemodes/blob/blobs/core.dm @@ -1,144 +1,144 @@ -/obj/structure/blob/core - name = "blob core" - icon = 'icons/mob/blob.dmi' - icon_state = "blank_blob" - max_integrity = 400 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 75, "acid" = 90) - fire_resist = 2 - point_return = -1 - var/overmind_get_delay = 0 // we don't want to constantly try to find an overmind, do it every 5 minutes - var/resource_delay = 0 - var/point_rate = 2 - var/is_offspring = null - var/selecting = 0 - -/obj/structure/blob/core/New(loc, var/h = 200, var/client/new_overmind = null, var/new_rate = 2, offspring) - blob_cores += src - START_PROCESSING(SSobj, src) - GLOB.poi_list |= src - adjustcolors(color) //so it atleast appears - if(!overmind) - create_overmind(new_overmind) - if(overmind) - adjustcolors(overmind.blob_reagent_datum.color) - if(offspring) - is_offspring = 1 - point_rate = new_rate - ..(loc, h) - - -/obj/structure/blob/core/adjustcolors(var/a_color) - overlays.Cut() - color = null - var/image/I = new('icons/mob/blob.dmi', "blob") - I.color = a_color - overlays += I - var/image/C = new('icons/mob/blob.dmi', "blob_core_overlay") - overlays += C - - -/obj/structure/blob/core/Destroy() - blob_cores -= src - if(overmind) - overmind.blob_core = null - overmind = null - STOP_PROCESSING(SSobj, src) - GLOB.poi_list.Remove(src) - return ..() - -/obj/structure/blob/core/ex_act(severity) - var/damage = 50 - 10 * severity //remember, the core takes half brute damage, so this is 20/15/10 damage based on severity - take_damage(damage, BRUTE, "bomb", 0) - -/obj/structure/blob/core/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir, overmind_reagent_trigger = 1) - . = ..() - if(obj_integrity > 0) - if(overmind) //we should have an overmind, but... - overmind.update_health_hud() - -/obj/structure/blob/core/RegenHealth() - return // Don't regen, we handle it in Life() - -/obj/structure/blob/core/Life(seconds, times_fired) - if(!overmind) - create_overmind() - else - if(resource_delay <= world.time) - resource_delay = world.time + 10 // 1 second - overmind.add_points(point_rate) - obj_integrity = min(max_integrity, obj_integrity + 1) - if(overmind) - overmind.update_health_hud() - if(overmind) - for(var/i = 1; i < 8; i += i) - Pulse(0, i, overmind.blob_reagent_datum.color) - else - for(var/i = 1; i < 8; i += i) - Pulse(0, i, color) - for(var/b_dir in alldirs) - if(!prob(5)) - continue - var/obj/structure/blob/normal/B = locate() in get_step(src, b_dir) - if(B) - B.change_to(/obj/structure/blob/shield/core) - if(B && overmind) - B.color = overmind.blob_reagent_datum.color - else - B.color = color - color = null - ..() - - -/obj/structure/blob/core/proc/create_overmind(var/client/new_overmind, var/override_delay) - if(overmind_get_delay > world.time && !override_delay) - return - - overmind_get_delay = world.time + 3000 // 5 minutes - - if(overmind) - qdel(overmind) - - var/mob/C = null - var/list/candidates = list() - - spawn() - if(!new_overmind) - if(is_offspring) - candidates = pollCandidates("Do you want to play as a blob offspring?", ROLE_BLOB, 1) - else - candidates = pollCandidates("Do you want to play as a blob?", ROLE_BLOB, 1) - - if(candidates.len) - C = pick(candidates) - else - C = new_overmind - - if(C) - var/mob/camera/blob/B = new(src.loc) - B.key = C.key - B.blob_core = src - src.overmind = B - color = overmind.blob_reagent_datum.color - if(B.mind && !B.mind.special_role) - B.mind.make_Overmind() - spawn(0) - if(is_offspring) - B.is_offspring = TRUE - -/obj/structure/blob/core/proc/lateblobtimer() - addtimer(CALLBACK(src, .proc/lateblobcheck), 50) - -/obj/structure/blob/core/proc/lateblobcheck() - if(overmind) - overmind.add_points(60) - if(overmind.mind) - overmind.mind.make_Overmind() - else - log_debug("/obj/structure/blob/core/proc/lateblobcheck: Blob core lacks a overmind.mind.") - else - log_debug("/obj/structure/blob/core/proc/lateblobcheck: Blob core lacks an overmind.") - -/obj/structure/blob/core/onTransitZ(old_z, new_z) - if(overmind && is_station_level(new_z)) - overmind.forceMove(get_turf(src)) - return ..() \ No newline at end of file +/obj/structure/blob/core + name = "blob core" + icon = 'icons/mob/blob.dmi' + icon_state = "blank_blob" + max_integrity = 400 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 75, "acid" = 90) + fire_resist = 2 + point_return = -1 + var/overmind_get_delay = 0 // we don't want to constantly try to find an overmind, do it every 5 minutes + var/resource_delay = 0 + var/point_rate = 2 + var/is_offspring = null + var/selecting = 0 + +/obj/structure/blob/core/New(loc, var/h = 200, var/client/new_overmind = null, var/new_rate = 2, offspring) + blob_cores += src + START_PROCESSING(SSobj, src) + GLOB.poi_list |= src + adjustcolors(color) //so it atleast appears + if(!overmind) + create_overmind(new_overmind) + if(overmind) + adjustcolors(overmind.blob_reagent_datum.color) + if(offspring) + is_offspring = 1 + point_rate = new_rate + ..(loc, h) + + +/obj/structure/blob/core/adjustcolors(var/a_color) + overlays.Cut() + color = null + var/image/I = new('icons/mob/blob.dmi', "blob") + I.color = a_color + overlays += I + var/image/C = new('icons/mob/blob.dmi', "blob_core_overlay") + overlays += C + + +/obj/structure/blob/core/Destroy() + blob_cores -= src + if(overmind) + overmind.blob_core = null + overmind = null + STOP_PROCESSING(SSobj, src) + GLOB.poi_list.Remove(src) + return ..() + +/obj/structure/blob/core/ex_act(severity) + var/damage = 50 - 10 * severity //remember, the core takes half brute damage, so this is 20/15/10 damage based on severity + take_damage(damage, BRUTE, "bomb", 0) + +/obj/structure/blob/core/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir, overmind_reagent_trigger = 1) + . = ..() + if(obj_integrity > 0) + if(overmind) //we should have an overmind, but... + overmind.update_health_hud() + +/obj/structure/blob/core/RegenHealth() + return // Don't regen, we handle it in Life() + +/obj/structure/blob/core/Life(seconds, times_fired) + if(!overmind) + create_overmind() + else + if(resource_delay <= world.time) + resource_delay = world.time + 10 // 1 second + overmind.add_points(point_rate) + obj_integrity = min(max_integrity, obj_integrity + 1) + if(overmind) + overmind.update_health_hud() + if(overmind) + for(var/i = 1; i < 8; i += i) + Pulse(0, i, overmind.blob_reagent_datum.color) + else + for(var/i = 1; i < 8; i += i) + Pulse(0, i, color) + for(var/b_dir in alldirs) + if(!prob(5)) + continue + var/obj/structure/blob/normal/B = locate() in get_step(src, b_dir) + if(B) + B.change_to(/obj/structure/blob/shield/core) + if(B && overmind) + B.color = overmind.blob_reagent_datum.color + else + B.color = color + color = null + ..() + + +/obj/structure/blob/core/proc/create_overmind(var/client/new_overmind, var/override_delay) + if(overmind_get_delay > world.time && !override_delay) + return + + overmind_get_delay = world.time + 3000 // 5 minutes + + if(overmind) + qdel(overmind) + + var/mob/C = null + var/list/candidates = list() + + spawn() + if(!new_overmind) + if(is_offspring) + candidates = pollCandidates("Do you want to play as a blob offspring?", ROLE_BLOB, 1) + else + candidates = pollCandidates("Do you want to play as a blob?", ROLE_BLOB, 1) + + if(candidates.len) + C = pick(candidates) + else + C = new_overmind + + if(C) + var/mob/camera/blob/B = new(src.loc) + B.key = C.key + B.blob_core = src + src.overmind = B + color = overmind.blob_reagent_datum.color + if(B.mind && !B.mind.special_role) + B.mind.make_Overmind() + spawn(0) + if(is_offspring) + B.is_offspring = TRUE + +/obj/structure/blob/core/proc/lateblobtimer() + addtimer(CALLBACK(src, .proc/lateblobcheck), 50) + +/obj/structure/blob/core/proc/lateblobcheck() + if(overmind) + overmind.add_points(60) + if(overmind.mind) + overmind.mind.make_Overmind() + else + log_debug("/obj/structure/blob/core/proc/lateblobcheck: Blob core lacks a overmind.mind.") + else + log_debug("/obj/structure/blob/core/proc/lateblobcheck: Blob core lacks an overmind.") + +/obj/structure/blob/core/onTransitZ(old_z, new_z) + if(overmind && is_station_level(new_z)) + overmind.forceMove(get_turf(src)) + return ..() diff --git a/code/game/gamemodes/blob/blobs/factory.dm b/code/game/gamemodes/blob/blobs/factory.dm index b8bff19cbcf..54139ca4a35 100644 --- a/code/game/gamemodes/blob/blobs/factory.dm +++ b/code/game/gamemodes/blob/blobs/factory.dm @@ -1,29 +1,29 @@ -/obj/structure/blob/factory - name = "factory blob" - icon = 'icons/mob/blob.dmi' - icon_state = "blob_factory" - max_integrity = 200 - point_return = 18 - var/list/spores = list() - var/max_spores = 3 - var/spore_delay = 0 - -/obj/structure/blob/factory/Destroy() - for(var/mob/living/simple_animal/hostile/blob/blobspore/spore in spores) - if(spore.factory == src) - spore.factory = null - spores = null - return ..() - -/obj/structure/blob/factory/run_action() - if(spores.len >= max_spores) - return - if(spore_delay > world.time) - return - flick("blob_factory_glow", src) - spore_delay = world.time + 100 // 10 seconds - var/mob/living/simple_animal/hostile/blob/blobspore/BS = new/mob/living/simple_animal/hostile/blob/blobspore(src.loc, src) - if(overmind) - BS.color = overmind.blob_reagent_datum?.complementary_color - BS.overmind = overmind - overmind.blob_mobs.Add(BS) +/obj/structure/blob/factory + name = "factory blob" + icon = 'icons/mob/blob.dmi' + icon_state = "blob_factory" + max_integrity = 200 + point_return = 18 + var/list/spores = list() + var/max_spores = 3 + var/spore_delay = 0 + +/obj/structure/blob/factory/Destroy() + for(var/mob/living/simple_animal/hostile/blob/blobspore/spore in spores) + if(spore.factory == src) + spore.factory = null + spores = null + return ..() + +/obj/structure/blob/factory/run_action() + if(spores.len >= max_spores) + return + if(spore_delay > world.time) + return + flick("blob_factory_glow", src) + spore_delay = world.time + 100 // 10 seconds + var/mob/living/simple_animal/hostile/blob/blobspore/BS = new/mob/living/simple_animal/hostile/blob/blobspore(src.loc, src) + if(overmind) + BS.color = overmind.blob_reagent_datum?.complementary_color + BS.overmind = overmind + overmind.blob_mobs.Add(BS) diff --git a/code/game/gamemodes/blob/blobs/node.dm b/code/game/gamemodes/blob/blobs/node.dm index 7d21bcc7946..b84e0a7a84c 100644 --- a/code/game/gamemodes/blob/blobs/node.dm +++ b/code/game/gamemodes/blob/blobs/node.dm @@ -1,36 +1,36 @@ -/obj/structure/blob/node - name = "blob node" - icon = 'icons/mob/blob.dmi' - icon_state = "blank_blob" - max_integrity = 200 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 65, "acid" = 90) - point_return = 18 - -/obj/structure/blob/node/New(loc, var/h = 100) - blob_nodes += src - START_PROCESSING(SSobj, src) - ..(loc, h) - -/obj/structure/blob/node/adjustcolors(var/a_color) - overlays.Cut() - color = null - var/image/I = new('icons/mob/blob.dmi', "blob") - I.color = a_color - src.overlays += I - var/image/C = new('icons/mob/blob.dmi', "blob_node_overlay") - src.overlays += C - -/obj/structure/blob/node/Destroy() - blob_nodes -= src - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/structure/blob/node/Life(seconds, times_fired) - if(overmind) - for(var/i = 1; i < 8; i += i) - Pulse(5, i, overmind.blob_reagent_datum.color) - else - for(var/i = 1; i < 8; i += i) - Pulse(5, i, color) - obj_integrity = min(max_integrity, obj_integrity + 1) - color = null \ No newline at end of file +/obj/structure/blob/node + name = "blob node" + icon = 'icons/mob/blob.dmi' + icon_state = "blank_blob" + max_integrity = 200 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 65, "acid" = 90) + point_return = 18 + +/obj/structure/blob/node/New(loc, var/h = 100) + blob_nodes += src + START_PROCESSING(SSobj, src) + ..(loc, h) + +/obj/structure/blob/node/adjustcolors(var/a_color) + overlays.Cut() + color = null + var/image/I = new('icons/mob/blob.dmi', "blob") + I.color = a_color + src.overlays += I + var/image/C = new('icons/mob/blob.dmi', "blob_node_overlay") + src.overlays += C + +/obj/structure/blob/node/Destroy() + blob_nodes -= src + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/structure/blob/node/Life(seconds, times_fired) + if(overmind) + for(var/i = 1; i < 8; i += i) + Pulse(5, i, overmind.blob_reagent_datum.color) + else + for(var/i = 1; i < 8; i += i) + Pulse(5, i, color) + obj_integrity = min(max_integrity, obj_integrity + 1) + color = null diff --git a/code/game/gamemodes/blob/blobs/shield.dm b/code/game/gamemodes/blob/blobs/shield.dm index 9e841131b78..04e9f6d677f 100644 --- a/code/game/gamemodes/blob/blobs/shield.dm +++ b/code/game/gamemodes/blob/blobs/shield.dm @@ -1,53 +1,53 @@ -/obj/structure/blob/shield - name = "strong blob" - icon = 'icons/mob/blob.dmi' - icon_state = "blob_shield" - desc = "Some blob creature thingy" - max_integrity = 150 - brute_resist = 0.25 - explosion_block = 3 - atmosblock = TRUE - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 90) - -/obj/structure/blob/shield/core - point_return = 0 - -/obj/structure/blob/shield/update_icon() - ..() - if(obj_integrity < max_integrity * 0.5) - icon_state = "[initial(icon_state)]_damaged" - name = "weakened [initial(name)]" - desc = "A wall of twitching tendrils." - atmosblock = FALSE - else - icon_state = initial(icon_state) - name = initial(name) - desc = initial(desc) - atmosblock = TRUE - air_update_turf(1) - -/obj/structure/blob/shield/CanPass(atom/movable/mover, turf/target, height=0) - if(istype(mover) && mover.checkpass(PASSBLOB)) return 1 - return 0 - -/obj/structure/blob/shield/reflective - name = "reflective blob" - desc = "A solid wall of slightly twitching tendrils with a reflective glow." - icon_state = "blob_glow" - max_integrity = 100 - brute_resist = 0.5 - explosion_block = 2 - point_return = 9 - flags_2 = CHECK_RICOCHET_2 - -/obj/structure/blob/shield/reflective/handle_ricochet(obj/item/projectile/P) - var/turf/p_turf = get_turf(P) - var/face_direction = get_dir(src, p_turf) - var/face_angle = dir2angle(face_direction) - var/incidence_s = GET_ANGLE_OF_INCIDENCE(face_angle, (P.Angle + 180)) - if(abs(incidence_s) > 90 && abs(incidence_s) < 270) - return FALSE - var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s) - P.setAngle(new_angle_s) - visible_message("[P] reflects off [src]!") - return TRUE \ No newline at end of file +/obj/structure/blob/shield + name = "strong blob" + icon = 'icons/mob/blob.dmi' + icon_state = "blob_shield" + desc = "Some blob creature thingy" + max_integrity = 150 + brute_resist = 0.25 + explosion_block = 3 + atmosblock = TRUE + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 90) + +/obj/structure/blob/shield/core + point_return = 0 + +/obj/structure/blob/shield/update_icon() + ..() + if(obj_integrity < max_integrity * 0.5) + icon_state = "[initial(icon_state)]_damaged" + name = "weakened [initial(name)]" + desc = "A wall of twitching tendrils." + atmosblock = FALSE + else + icon_state = initial(icon_state) + name = initial(name) + desc = initial(desc) + atmosblock = TRUE + air_update_turf(1) + +/obj/structure/blob/shield/CanPass(atom/movable/mover, turf/target, height=0) + if(istype(mover) && mover.checkpass(PASSBLOB)) return 1 + return 0 + +/obj/structure/blob/shield/reflective + name = "reflective blob" + desc = "A solid wall of slightly twitching tendrils with a reflective glow." + icon_state = "blob_glow" + max_integrity = 100 + brute_resist = 0.5 + explosion_block = 2 + point_return = 9 + flags_2 = CHECK_RICOCHET_2 + +/obj/structure/blob/shield/reflective/handle_ricochet(obj/item/projectile/P) + var/turf/p_turf = get_turf(P) + var/face_direction = get_dir(src, p_turf) + var/face_angle = dir2angle(face_direction) + var/incidence_s = GET_ANGLE_OF_INCIDENCE(face_angle, (P.Angle + 180)) + if(abs(incidence_s) > 90 && abs(incidence_s) < 270) + return FALSE + var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s) + P.setAngle(new_angle_s) + visible_message("[P] reflects off [src]!") + return TRUE diff --git a/code/game/gamemodes/blob/blobs/storage.dm b/code/game/gamemodes/blob/blobs/storage.dm index 0f3225e8362..b9052b3601a 100644 --- a/code/game/gamemodes/blob/blobs/storage.dm +++ b/code/game/gamemodes/blob/blobs/storage.dm @@ -13,4 +13,4 @@ /obj/structure/blob/storage/proc/update_max_blob_points(var/new_point_increase) if(overmind) - overmind.max_blob_points += new_point_increase \ No newline at end of file + overmind.max_blob_points += new_point_increase diff --git a/code/game/gamemodes/blob/overmind.dm b/code/game/gamemodes/blob/overmind.dm index 140ead3a947..74ed59d2469 100644 --- a/code/game/gamemodes/blob/overmind.dm +++ b/code/game/gamemodes/blob/overmind.dm @@ -48,13 +48,14 @@ update_health_hud() /mob/camera/blob/update_health_hud() - if(blob_core) + if(blob_core && hud_used) hud_used.blobhealthdisplay.maptext = "
    [round(blob_core.obj_integrity)]
    " /mob/camera/blob/proc/add_points(var/points) if(points != 0) blob_points = Clamp(blob_points + points, 0, max_blob_points) - hud_used.blobpwrdisplay.maptext = "
    [round(src.blob_points)]
    " + if(hud_used) + hud_used.blobpwrdisplay.maptext = "
    [round(src.blob_points)]
    " /mob/camera/blob/say(var/message) if(!message) diff --git a/code/game/gamemodes/blob/powers.dm b/code/game/gamemodes/blob/powers.dm index d4c429a6f53..548f2759ba0 100644 --- a/code/game/gamemodes/blob/powers.dm +++ b/code/game/gamemodes/blob/powers.dm @@ -336,7 +336,7 @@ if(ROLE_BLOB in L.faction) //no friendly/dead fire continue var/mob_protection = L.get_permeability_protection() - blob_reagent_datum.reaction_mob(L, TOUCH, 25, 1, mob_protection) + blob_reagent_datum.reaction_mob(L, REAGENT_TOUCH, 25, 1, mob_protection) blob_reagent_datum.send_message(L) OB.color = blob_reagent_datum.color return diff --git a/code/game/gamemodes/blob/theblob.dm b/code/game/gamemodes/blob/theblob.dm index 37251cabbf2..b97c8311507 100644 --- a/code/game/gamemodes/blob/theblob.dm +++ b/code/game/gamemodes/blob/theblob.dm @@ -1,242 +1,242 @@ -//I will need to recode parts of this but I am way too tired atm -/obj/structure/blob - name = "blob" - icon = 'icons/mob/blob.dmi' - light_range = 3 - desc = "Some blob creature thingy" - density = 0 - opacity = 0 - anchored = 1 - max_integrity = 30 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 70) - var/point_return = 0 //How many points the blob gets back when it removes a blob of that type. If less than 0, blob cannot be removed. - var/health_timestamp = 0 - var/brute_resist = 0.5 //multiplies brute damage by this - var/fire_resist = 1 //multiplies burn damage by this - var/atmosblock = FALSE //if the blob blocks atmos and heat spread - var/mob/camera/blob/overmind - -/obj/structure/blob/New(loc) - ..() - blobs += src - setDir(pick(cardinal)) - update_icon() - if(atmosblock) - air_update_turf(1) - ConsumeTile() - -/obj/structure/blob/Destroy() - if(atmosblock) - atmosblock = FALSE - air_update_turf(1) - blobs -= src - if(isturf(loc)) //Necessary because Expand() is retarded and spawns a blob and then deletes it - playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) - return ..() - -/obj/structure/blob/BlockSuperconductivity() - return atmosblock - -/obj/structure/blob/CanPass(atom/movable/mover, turf/target, height=0) - if(height==0) - return 1 - if(istype(mover) && mover.checkpass(PASSBLOB)) - return 1 - return 0 - -/obj/structure/blob/CanAtmosPass(turf/T) - return !atmosblock - -/obj/structure/blob/CanAStarPass(ID, dir, caller) - . = 0 - if(ismovableatom(caller)) - var/atom/movable/mover = caller - . = . || mover.checkpass(PASSBLOB) - -/obj/structure/blob/process() - Life() - return - -/obj/structure/blob/blob_act(obj/structure/blob/B) - return - -/obj/structure/blob/proc/Life() - return - -/obj/structure/blob/proc/RegenHealth() - // All blobs heal over time when pulsed, but it has a cool down - if(health_timestamp > world.time) - return 0 - if(obj_integrity < max_integrity) - obj_integrity = min(max_integrity, obj_integrity + 1) - update_icon() - health_timestamp = world.time + 10 // 1 seconds - - -/obj/structure/blob/proc/Pulse(var/pulse = 0, var/origin_dir = 0, var/a_color)//Todo: Fix spaceblob expand - - set background = BACKGROUND_ENABLED - - RegenHealth() - - if(run_action())//If we can do something here then we dont need to pulse more - return - - if(pulse > 30) - return//Inf loop check - - //Looking for another blob to pulse - var/list/dirs = list(1,2,4,8) - dirs.Remove(origin_dir)//Dont pulse the guy who pulsed us - for(var/i = 1 to 4) - if(!dirs.len) break - var/dirn = pick(dirs) - dirs.Remove(dirn) - var/turf/T = get_step(src, dirn) - var/obj/structure/blob/B = (locate(/obj/structure/blob) in T) - if(!B) - expand(T,1,a_color)//No blob here so try and expand - return - B.adjustcolors(a_color) - - B.Pulse((pulse+1),get_dir(src.loc,T), a_color) - return - return - - -/obj/structure/blob/proc/run_action() - return 0 - -/obj/structure/blob/proc/ConsumeTile() - for(var/atom/A in loc) - A.blob_act(src) - if(iswallturf(loc)) - loc.blob_act(src) //don't ask how a wall got on top of the core, just eat it - -/obj/structure/blob/proc/expand(var/turf/T = null, var/prob = 1, var/a_color) - if(prob && !prob(obj_integrity)) - return - if(istype(T, /turf/space) && prob(75)) return - if(!T) - var/list/dirs = list(1,2,4,8) - for(var/i = 1 to 4) - var/dirn = pick(dirs) - dirs.Remove(dirn) - T = get_step(src, dirn) - if(!(locate(/obj/structure/blob) in T)) break - else T = null - - if(!T) return 0 - var/obj/structure/blob/normal/B = new /obj/structure/blob/normal(src.loc, min(obj_integrity, 30)) - B.color = a_color - B.density = 1 - if(T.Enter(B,src))//Attempt to move into the tile - B.density = initial(B.density) - B.loc = T - else - T.blob_act()//If we cant move in hit the turf - B.loc = null //So we don't play the splat sound, see Destroy() - qdel(B) - - for(var/atom/A in T)//Hit everything in the turf - A.blob_act(src) - return 1 - -/obj/structure/blob/Crossed(var/mob/living/L, oldloc) - ..() - L.blob_act(src) - -/obj/structure/blob/tesla_act(power) - ..() - take_damage(power / 400, BURN, "energy") - -/obj/structure/blob/hulk_damage() - return 15 - -/obj/structure/blob/attack_animal(mob/living/simple_animal/M) - if(ROLE_BLOB in M.faction) //sorry, but you can't kill the blob as a blobbernaut - return - ..() - -/obj/structure/blob/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - switch(damage_type) - if(BRUTE) - if(damage_amount) - playsound(src.loc, 'sound/effects/attackblob.ogg', 50, TRUE) - else - playsound(src, 'sound/weapons/tap.ogg', 50, TRUE) - if(BURN) - playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE) - -/obj/structure/blob/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) - switch(damage_type) - if(BRUTE) - damage_amount *= brute_resist - if(BURN) - damage_amount *= fire_resist - if(CLONE) - else - return 0 - var/armor_protection = 0 - if(damage_flag) - armor_protection = armor[damage_flag] - damage_amount = round(damage_amount * (100 - armor_protection)*0.01, 0.1) - if(overmind && damage_flag) - damage_amount = overmind.blob_reagent_datum.damage_reaction(src, damage_amount, damage_type, damage_flag) - return damage_amount - -/obj/structure/blob/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) - . = ..() - if(. && obj_integrity > 0) - update_icon() - -/obj/structure/blob/proc/change_to(var/type) - if(!ispath(type)) - error("[type] is an invalid type for the blob.") - var/obj/structure/blob/B = new type(src.loc) - if(!istype(type, /obj/structure/blob/core) || !istype(type, /obj/structure/blob/node)) - B.color = color - else - B.adjustcolors(color) - qdel(src) - -/obj/structure/blob/proc/adjustcolors(var/a_color) - if(a_color) - color = a_color - return - -/obj/structure/blob/examine(mob/user) - . = ..() - . += "It looks like it's made of [get_chem_name()]." - - -/obj/structure/blob/proc/get_chem_name() - for(var/mob/camera/blob/B in GLOB.mob_list) - if(lowertext(B.blob_reagent_datum.color) == lowertext(src.color)) // Goddamit why we use strings for these - return B.blob_reagent_datum.name - return "unknown" - -/obj/structure/blob/normal - icon_state = "blob" - light_range = 0 - obj_integrity = 21 //doesn't start at full health - max_integrity = 25 - brute_resist = 0.25 - -/obj/structure/blob/normal/update_icon() - ..() - if(obj_integrity <= 15) - icon_state = "blob_damaged" - name = "fragile blob" - desc = "A thin lattice of slightly twitching tendrils." - brute_resist = 0.5 - else if(overmind) - icon_state = "blob" - name = "blob" - desc = "A thick wall of writhing tendrils." - brute_resist = 0.25 - else - icon_state = "blob" - name = "dead blob" - desc = "A thick wall of lifeless tendrils." - brute_resist = 0.25 \ No newline at end of file +//I will need to recode parts of this but I am way too tired atm +/obj/structure/blob + name = "blob" + icon = 'icons/mob/blob.dmi' + light_range = 3 + desc = "Some blob creature thingy" + density = 0 + opacity = 0 + anchored = 1 + max_integrity = 30 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 70) + var/point_return = 0 //How many points the blob gets back when it removes a blob of that type. If less than 0, blob cannot be removed. + var/health_timestamp = 0 + var/brute_resist = 0.5 //multiplies brute damage by this + var/fire_resist = 1 //multiplies burn damage by this + var/atmosblock = FALSE //if the blob blocks atmos and heat spread + var/mob/camera/blob/overmind + +/obj/structure/blob/New(loc) + ..() + blobs += src + setDir(pick(cardinal)) + update_icon() + if(atmosblock) + air_update_turf(1) + ConsumeTile() + +/obj/structure/blob/Destroy() + if(atmosblock) + atmosblock = FALSE + air_update_turf(1) + blobs -= src + if(isturf(loc)) //Necessary because Expand() is retarded and spawns a blob and then deletes it + playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) + return ..() + +/obj/structure/blob/BlockSuperconductivity() + return atmosblock + +/obj/structure/blob/CanPass(atom/movable/mover, turf/target, height=0) + if(height==0) + return 1 + if(istype(mover) && mover.checkpass(PASSBLOB)) + return 1 + return 0 + +/obj/structure/blob/CanAtmosPass(turf/T) + return !atmosblock + +/obj/structure/blob/CanAStarPass(ID, dir, caller) + . = 0 + if(ismovableatom(caller)) + var/atom/movable/mover = caller + . = . || mover.checkpass(PASSBLOB) + +/obj/structure/blob/process() + Life() + return + +/obj/structure/blob/blob_act(obj/structure/blob/B) + return + +/obj/structure/blob/proc/Life() + return + +/obj/structure/blob/proc/RegenHealth() + // All blobs heal over time when pulsed, but it has a cool down + if(health_timestamp > world.time) + return 0 + if(obj_integrity < max_integrity) + obj_integrity = min(max_integrity, obj_integrity + 1) + update_icon() + health_timestamp = world.time + 10 // 1 seconds + + +/obj/structure/blob/proc/Pulse(var/pulse = 0, var/origin_dir = 0, var/a_color)//Todo: Fix spaceblob expand + + set background = BACKGROUND_ENABLED + + RegenHealth() + + if(run_action())//If we can do something here then we dont need to pulse more + return + + if(pulse > 30) + return//Inf loop check + + //Looking for another blob to pulse + var/list/dirs = list(1,2,4,8) + dirs.Remove(origin_dir)//Dont pulse the guy who pulsed us + for(var/i = 1 to 4) + if(!dirs.len) break + var/dirn = pick(dirs) + dirs.Remove(dirn) + var/turf/T = get_step(src, dirn) + var/obj/structure/blob/B = (locate(/obj/structure/blob) in T) + if(!B) + expand(T,1,a_color)//No blob here so try and expand + return + B.adjustcolors(a_color) + + B.Pulse((pulse+1),get_dir(src.loc,T), a_color) + return + return + + +/obj/structure/blob/proc/run_action() + return 0 + +/obj/structure/blob/proc/ConsumeTile() + for(var/atom/A in loc) + A.blob_act(src) + if(iswallturf(loc)) + loc.blob_act(src) //don't ask how a wall got on top of the core, just eat it + +/obj/structure/blob/proc/expand(var/turf/T = null, var/prob = 1, var/a_color) + if(prob && !prob(obj_integrity)) + return + if(istype(T, /turf/space) && prob(75)) return + if(!T) + var/list/dirs = list(1,2,4,8) + for(var/i = 1 to 4) + var/dirn = pick(dirs) + dirs.Remove(dirn) + T = get_step(src, dirn) + if(!(locate(/obj/structure/blob) in T)) break + else T = null + + if(!T) return 0 + var/obj/structure/blob/normal/B = new /obj/structure/blob/normal(src.loc, min(obj_integrity, 30)) + B.color = a_color + B.density = 1 + if(T.Enter(B,src))//Attempt to move into the tile + B.density = initial(B.density) + B.loc = T + else + T.blob_act()//If we cant move in hit the turf + B.loc = null //So we don't play the splat sound, see Destroy() + qdel(B) + + for(var/atom/A in T)//Hit everything in the turf + A.blob_act(src) + return 1 + +/obj/structure/blob/Crossed(var/mob/living/L, oldloc) + ..() + L.blob_act(src) + +/obj/structure/blob/tesla_act(power) + ..() + take_damage(power / 400, BURN, "energy") + +/obj/structure/blob/hulk_damage() + return 15 + +/obj/structure/blob/attack_animal(mob/living/simple_animal/M) + if(ROLE_BLOB in M.faction) //sorry, but you can't kill the blob as a blobbernaut + return + ..() + +/obj/structure/blob/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + switch(damage_type) + if(BRUTE) + if(damage_amount) + playsound(src.loc, 'sound/effects/attackblob.ogg', 50, TRUE) + else + playsound(src, 'sound/weapons/tap.ogg', 50, TRUE) + if(BURN) + playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE) + +/obj/structure/blob/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) + switch(damage_type) + if(BRUTE) + damage_amount *= brute_resist + if(BURN) + damage_amount *= fire_resist + if(CLONE) + else + return 0 + var/armor_protection = 0 + if(damage_flag) + armor_protection = armor[damage_flag] + damage_amount = round(damage_amount * (100 - armor_protection)*0.01, 0.1) + if(overmind && damage_flag) + damage_amount = overmind.blob_reagent_datum.damage_reaction(src, damage_amount, damage_type, damage_flag) + return damage_amount + +/obj/structure/blob/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) + . = ..() + if(. && obj_integrity > 0) + update_icon() + +/obj/structure/blob/proc/change_to(var/type) + if(!ispath(type)) + error("[type] is an invalid type for the blob.") + var/obj/structure/blob/B = new type(src.loc) + if(!istype(type, /obj/structure/blob/core) || !istype(type, /obj/structure/blob/node)) + B.color = color + else + B.adjustcolors(color) + qdel(src) + +/obj/structure/blob/proc/adjustcolors(var/a_color) + if(a_color) + color = a_color + return + +/obj/structure/blob/examine(mob/user) + . = ..() + . += "It looks like it's made of [get_chem_name()]." + + +/obj/structure/blob/proc/get_chem_name() + for(var/mob/camera/blob/B in GLOB.mob_list) + if(lowertext(B.blob_reagent_datum.color) == lowertext(src.color)) // Goddamit why we use strings for these + return B.blob_reagent_datum.name + return "unknown" + +/obj/structure/blob/normal + icon_state = "blob" + light_range = 0 + obj_integrity = 21 //doesn't start at full health + max_integrity = 25 + brute_resist = 0.25 + +/obj/structure/blob/normal/update_icon() + ..() + if(obj_integrity <= 15) + icon_state = "blob_damaged" + name = "fragile blob" + desc = "A thin lattice of slightly twitching tendrils." + brute_resist = 0.5 + else if(overmind) + icon_state = "blob" + name = "blob" + desc = "A thick wall of writhing tendrils." + brute_resist = 0.25 + else + icon_state = "blob" + name = "dead blob" + desc = "A thick wall of lifeless tendrils." + brute_resist = 0.25 diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm index 713779af241..afad318a517 100644 --- a/code/game/gamemodes/changeling/changeling.dm +++ b/code/game/gamemodes/changeling/changeling.dm @@ -1,332 +1,332 @@ -#define LING_FAKEDEATH_TIME 400 //40 seconds -#define LING_DEAD_GENETICDAMAGE_HEAL_CAP 50 //The lowest value of geneticdamage handle_changeling() can take it to while dead. -#define LING_ABSORB_RECENT_SPEECH 8 //The amount of recent spoken lines to gain on absorbing a mob - -var/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon","Zeta","Eta","Theta","Iota","Kappa","Lambda","Mu","Nu","Xi","Omicron","Pi","Rho","Sigma","Tau","Upsilon","Phi","Chi","Psi","Omega") - -/datum/game_mode - var/list/datum/mind/changelings = list() - -/datum/game_mode/changeling - name = "changeling" - config_tag = "changeling" - restricted_jobs = list("AI", "Cyborg") - protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Brig Physician", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer") - protected_species = list("Machine") - required_players = 15 - required_enemies = 1 - recommended_enemies = 4 - - var/const/prob_int_murder_target = 50 // intercept names the assassination target half the time - var/const/prob_right_murder_target_l = 25 // lower bound on probability of naming right assassination target - var/const/prob_right_murder_target_h = 50 // upper bound on probability of naimg the right assassination target - - var/const/prob_int_item = 50 // intercept names the theft target half the time - var/const/prob_right_item_l = 25 // lower bound on probability of naming right theft target - var/const/prob_right_item_h = 50 // upper bound on probability of naming the right theft target - - var/const/prob_int_sab_target = 50 // intercept names the sabotage target half the time - var/const/prob_right_sab_target_l = 25 // lower bound on probability of naming right sabotage target - var/const/prob_right_sab_target_h = 50 // upper bound on probability of naming right sabotage target - - var/const/prob_right_killer_l = 25 //lower bound on probability of naming the right operative - var/const/prob_right_killer_h = 50 //upper bound on probability of naming the right operative - var/const/prob_right_objective_l = 25 //lower bound on probability of determining the objective correctly - var/const/prob_right_objective_h = 50 //upper bound on probability of determining the objective correctly - - var/changeling_amount = 4 - -/datum/game_mode/changeling/announce() - to_chat(world, "The current game mode is - Changeling!") - to_chat(world, "There are alien changelings on the station. Do not let the changelings succeed!") - -/datum/game_mode/changeling/pre_setup() - if(config.protect_roles_from_antagonist) - restricted_jobs += protected_jobs - - var/list/datum/mind/possible_changelings = get_players_for_role(ROLE_CHANGELING) - - changeling_amount = 1 + round(num_players() / 10) - - if(possible_changelings.len>0) - for(var/i = 0, i < changeling_amount, i++) - if(!possible_changelings.len) break - var/datum/mind/changeling = pick(possible_changelings) - possible_changelings -= changeling - changelings += changeling - changeling.restricted_roles = restricted_jobs - modePlayer += changelings - changeling.special_role = SPECIAL_ROLE_CHANGELING - ..() - return 1 - else - return 0 - -/datum/game_mode/changeling/post_setup() - for(var/datum/mind/changeling in changelings) - grant_changeling_powers(changeling.current) - forge_changeling_objectives(changeling) - greet_changeling(changeling) - update_change_icons_added(changeling) - - ..() - -/datum/game_mode/proc/forge_changeling_objectives(datum/mind/changeling) - //OBJECTIVES - Always absorb 5 genomes, plus random traitor objectives. - //If they have two objectives as well as absorb, they must survive rather than escape - //No escape alone because changelings aren't suited for it and it'd probably just lead to rampant robusting - //If it seems like they'd be able to do it in play, add a 10% chance to have to escape alone - - var/datum/objective/absorb/absorb_objective = new - absorb_objective.owner = changeling - absorb_objective.gen_amount_goal(6, 8) - changeling.objectives += absorb_objective - - if(prob(60)) - var/datum/objective/steal/steal_objective = new - steal_objective.owner = changeling - steal_objective.find_target() - changeling.objectives += steal_objective - else - var/datum/objective/debrain/debrain_objective = new - debrain_objective.owner = changeling - debrain_objective.find_target() - changeling.objectives += debrain_objective - - var/list/active_ais = active_ais() - if(active_ais.len && prob(4)) // Leaving this at a flat chance for now, problems with the num_players() proc due to latejoin antags. - var/datum/objective/destroy/destroy_objective = new - destroy_objective.owner = changeling - destroy_objective.find_target() - changeling.objectives += destroy_objective - else - var/datum/objective/assassinate/kill_objective = new - kill_objective.owner = changeling - kill_objective.find_target() - changeling.objectives += kill_objective - - if(!(locate(/datum/objective/escape) in changeling.objectives)) - var/datum/objective/escape/escape_with_identity/identity_theft = new - identity_theft.owner = changeling - identity_theft.target = kill_objective.target - if(identity_theft.target && identity_theft.target.current) - identity_theft.target_real_name = kill_objective.target.current.real_name //Whoops, forgot this. - var/mob/living/carbon/human/H = identity_theft.target.current - if(can_absorb_species(H.dna.species)) // For species that can't be absorbed - should default to an escape objective - identity_theft.explanation_text = "Escape on the shuttle or an escape pod with the identity of [identity_theft.target_real_name], the [identity_theft.target.assigned_role] while wearing [identity_theft.target.p_their()] identification card." - changeling.objectives += identity_theft - else - qdel(identity_theft) - - if(!(locate(/datum/objective/escape) in changeling.objectives)) - if(prob(70)) - var/datum/objective/escape/escape_objective = new - escape_objective.owner = changeling - changeling.objectives += escape_objective - else - var/datum/objective/escape/escape_with_identity/identity_theft = new - identity_theft.owner = changeling - identity_theft.find_target() - changeling.objectives += identity_theft - return - -/datum/game_mode/proc/greet_changeling(datum/mind/changeling, you_are=1) - SEND_SOUND(changeling.current, 'sound/ambience/antag/ling_aler.ogg') - if(you_are) - to_chat(changeling.current, "You are a changeling!") - to_chat(changeling.current, "Use say \":g message\" to communicate with your fellow changelings. Remember: you get all of their absorbed DNA if you absorb them.") - to_chat(changeling.current, "You must complete the following tasks:") - if(changeling.current.mind) - if(changeling.current.mind.assigned_role == "Clown") - to_chat(changeling.current, "You have evolved beyond your clownish nature, allowing you to wield weapons without harming yourself.") - changeling.current.mutations.Remove(CLUMSY) - var/datum/action/innate/toggle_clumsy/A = new - A.Grant(changeling.current) - var/obj_count = 1 - for(var/datum/objective/objective in changeling.objectives) - to_chat(changeling.current, "Objective #[obj_count]: [objective.explanation_text]") - obj_count++ - return - -/datum/game_mode/proc/remove_changeling(datum/mind/changeling_mind) - if(changeling_mind in changelings) - changelings -= changeling_mind - changeling_mind.current.remove_changeling_powers() - changeling_mind.memory = "" - changeling_mind.special_role = null - if(issilicon(changeling_mind.current)) - to_chat(changeling_mind.current, "You have been robotized!") - to_chat(changeling_mind.current, "You must obey your silicon laws and master AI above all else. Your objectives will consider you to be dead.") - else - to_chat(changeling_mind.current, "You lose your powers! You are no longer a changeling and are stuck in your current form!") - update_change_icons_removed(changeling_mind) - -/datum/game_mode/proc/update_change_icons_added(datum/mind/changeling) - var/datum/atom_hud/antag/linghud = huds[ANTAG_HUD_CHANGELING] - linghud.join_hud(changeling.current) - set_antag_hud(changeling.current, "hudchangeling") - -/datum/game_mode/proc/update_change_icons_removed(datum/mind/changeling) - var/datum/atom_hud/antag/linghud = huds[ANTAG_HUD_CHANGELING] - linghud.leave_hud(changeling.current) - set_antag_hud(changeling.current, null) - -/datum/game_mode/proc/grant_changeling_powers(mob/living/carbon/changeling_mob) - if(!istype(changeling_mob)) - return - changeling_mob.make_changeling() - -/datum/game_mode/proc/auto_declare_completion_changeling() - if(changelings.len) - var/text = "The changelings were:" - for(var/datum/mind/changeling in changelings) - var/changelingwin = 1 - - text += "
    [changeling.key] was [changeling.name] (" - if(changeling.current) - if(changeling.current.stat == DEAD) - text += "died" - else - text += "survived" - if(changeling.current.real_name != changeling.name) - text += " as [changeling.current.real_name]" - else - text += "body destroyed" - changelingwin = 0 - text += ")" - - //Removed sanity if(changeling) because we -want- a runtime to inform us that the changelings list is incorrect and needs to be fixed. - text += "
    Changeling ID: [changeling.changeling.changelingID]." - text += "
    Genomes Extracted: [changeling.changeling.absorbedcount]" - - if(changeling.objectives.len) - var/count = 1 - for(var/datum/objective/objective in changeling.objectives) - if(objective.check_completion()) - text += "
    Objective #[count]: [objective.explanation_text] Success!" - feedback_add_details("changeling_objective","[objective.type]|SUCCESS") - else - text += "
    Objective #[count]: [objective.explanation_text] Fail." - feedback_add_details("changeling_objective","[objective.type]|FAIL") - changelingwin = 0 - count++ - - if(changelingwin) - text += "
    The changeling was successful!" - feedback_add_details("changeling_success","SUCCESS") - else - text += "
    The changeling has failed." - feedback_add_details("changeling_success","FAIL") - - to_chat(world, text) - - return 1 - -/datum/changeling //stores changeling powers, changeling recharge thingie, changeling absorbed DNA and changeling ID (for changeling hivemind) - var/list/absorbed_dna = list() - var/list/absorbed_languages = list() - var/list/protected_dna = list() //DNA that is not lost when capacity is otherwise full. - var/dna_max = 5 //How many total DNA strands the changeling can store for transformation. - var/absorbedcount = 1 //Would require at least 1 sample to take on the form of a human - var/chem_charges = 20 - var/chem_recharge_rate = 1 - var/chem_recharge_slowdown = 0 - var/chem_storage = 75 - var/sting_range = 2 - var/changelingID = "Changeling" - var/geneticdamage = 0 - var/isabsorbing = 0 - var/islinking = 0 - var/geneticpoints = 10 - var/purchasedpowers = list() - var/mimicing = "" - var/canrespec = FALSE //set to TRUE in absorb.dm - var/changeling_speak = 0 - var/datum/dna/chosen_dna - var/datum/action/changeling/sting/chosen_sting - var/regenerating = FALSE - -/datum/changeling/New(gender=FEMALE) - ..() - var/honorific - if(gender == FEMALE) - honorific = "Ms." - else - honorific = "Mr." - if(possible_changeling_IDs.len) - changelingID = pick(possible_changeling_IDs) - possible_changeling_IDs -= changelingID - changelingID = "[honorific] [changelingID]" - else - changelingID = "[honorific] [rand(1,999)]" - -/datum/changeling/proc/regenerate(mob/living/carbon/the_ling) - if(istype(the_ling)) - if(the_ling.stat == DEAD) - chem_charges = min(max(0, chem_charges + chem_recharge_rate - chem_recharge_slowdown), (chem_storage*0.5)) - geneticdamage = max(LING_DEAD_GENETICDAMAGE_HEAL_CAP,geneticdamage-1) - else //not dead? no chem/geneticdamage caps. - chem_charges = min(max(0, chem_charges + chem_recharge_rate - chem_recharge_slowdown), chem_storage) - geneticdamage = max(0, geneticdamage-1) - -/datum/changeling/proc/GetDNA(dna_owner) - for(var/datum/dna/DNA in (absorbed_dna + protected_dna)) - if(dna_owner == DNA.real_name) - return DNA - -/datum/changeling/proc/find_dna(datum/dna/tDNA) - for(var/datum/dna/D in (absorbed_dna + protected_dna)) - if(tDNA.unique_enzymes == D.unique_enzymes && tDNA.uni_identity == D.uni_identity && tDNA.species.type == D.species.type) - return D - return null - -/datum/changeling/proc/has_dna(datum/dna/tDNA) - if(find_dna(tDNA)) - return 1 - return 0 - -// A changeling's DNA is "stale" if their current form's DNA is the oldest DNA in a full list -/datum/changeling/proc/using_stale_dna(mob/living/carbon/user) - var/current_dna = find_dna(user.dna) - if(absorbed_dna.len < dna_max) - return 0 // Still more room for DNA - if(!current_dna || !(current_dna in absorbed_dna)) - return 1 // Oops, our current DNA was somehow not absorbed; force a transformation - if(absorbed_dna[1] == current_dna) - return 1 // Oldest DNA is the current DNA - return 0 - -/datum/changeling/proc/trim_dna() - absorbed_dna -= null - if(absorbed_dna.len > dna_max) - absorbed_dna.Cut(1, (absorbed_dna.len - dna_max) + 1) - -/datum/changeling/proc/can_absorb_dna(mob/living/carbon/user, mob/living/carbon/target) - if(using_stale_dna(user))//If our current DNA is the stalest, we gotta ditch it. - to_chat(user, "The DNA we are wearing is stale. Transform and try again.") - return - - if(!target || !target.dna) - to_chat(user, "This creature does not have any DNA.") - return - - var/mob/living/carbon/human/T = target - if(!istype(T) || issmall(T)) - to_chat(user, "[T] is not compatible with our biology.") - return - - if((NOCLONE || SKELETON || HUSK) in T.mutations) - to_chat(user, "DNA of [target] is ruined beyond usability!") - return - - if(NO_DNA in T.dna.species.species_traits) - to_chat(user, "This creature does not have DNA!") - return - - if(has_dna(target.dna)) - to_chat(user, "We already have this DNA in storage!") - - return 1 - -/proc/can_absorb_species(datum/species/S) - return !(NO_DNA in S.species_traits) +#define LING_FAKEDEATH_TIME 400 //40 seconds +#define LING_DEAD_GENETICDAMAGE_HEAL_CAP 50 //The lowest value of geneticdamage handle_changeling() can take it to while dead. +#define LING_ABSORB_RECENT_SPEECH 8 //The amount of recent spoken lines to gain on absorbing a mob + +var/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon","Zeta","Eta","Theta","Iota","Kappa","Lambda","Mu","Nu","Xi","Omicron","Pi","Rho","Sigma","Tau","Upsilon","Phi","Chi","Psi","Omega") + +/datum/game_mode + var/list/datum/mind/changelings = list() + +/datum/game_mode/changeling + name = "changeling" + config_tag = "changeling" + restricted_jobs = list("AI", "Cyborg") + protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Brig Physician", "Internal Affairs Agent", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer") + protected_species = list("Machine") + required_players = 15 + required_enemies = 1 + recommended_enemies = 4 + + var/const/prob_int_murder_target = 50 // intercept names the assassination target half the time + var/const/prob_right_murder_target_l = 25 // lower bound on probability of naming right assassination target + var/const/prob_right_murder_target_h = 50 // upper bound on probability of naimg the right assassination target + + var/const/prob_int_item = 50 // intercept names the theft target half the time + var/const/prob_right_item_l = 25 // lower bound on probability of naming right theft target + var/const/prob_right_item_h = 50 // upper bound on probability of naming the right theft target + + var/const/prob_int_sab_target = 50 // intercept names the sabotage target half the time + var/const/prob_right_sab_target_l = 25 // lower bound on probability of naming right sabotage target + var/const/prob_right_sab_target_h = 50 // upper bound on probability of naming right sabotage target + + var/const/prob_right_killer_l = 25 //lower bound on probability of naming the right operative + var/const/prob_right_killer_h = 50 //upper bound on probability of naming the right operative + var/const/prob_right_objective_l = 25 //lower bound on probability of determining the objective correctly + var/const/prob_right_objective_h = 50 //upper bound on probability of determining the objective correctly + + var/changeling_amount = 4 + +/datum/game_mode/changeling/announce() + to_chat(world, "The current game mode is - Changeling!") + to_chat(world, "There are alien changelings on the station. Do not let the changelings succeed!") + +/datum/game_mode/changeling/pre_setup() + if(config.protect_roles_from_antagonist) + restricted_jobs += protected_jobs + + var/list/datum/mind/possible_changelings = get_players_for_role(ROLE_CHANGELING) + + changeling_amount = 1 + round(num_players() / 10) + + if(possible_changelings.len>0) + for(var/i = 0, i < changeling_amount, i++) + if(!possible_changelings.len) break + var/datum/mind/changeling = pick(possible_changelings) + possible_changelings -= changeling + changelings += changeling + changeling.restricted_roles = restricted_jobs + modePlayer += changelings + changeling.special_role = SPECIAL_ROLE_CHANGELING + ..() + return 1 + else + return 0 + +/datum/game_mode/changeling/post_setup() + for(var/datum/mind/changeling in changelings) + grant_changeling_powers(changeling.current) + forge_changeling_objectives(changeling) + greet_changeling(changeling) + update_change_icons_added(changeling) + + ..() + +/datum/game_mode/proc/forge_changeling_objectives(datum/mind/changeling) + //OBJECTIVES - Always absorb 5 genomes, plus random traitor objectives. + //If they have two objectives as well as absorb, they must survive rather than escape + //No escape alone because changelings aren't suited for it and it'd probably just lead to rampant robusting + //If it seems like they'd be able to do it in play, add a 10% chance to have to escape alone + + var/datum/objective/absorb/absorb_objective = new + absorb_objective.owner = changeling + absorb_objective.gen_amount_goal(6, 8) + changeling.objectives += absorb_objective + + if(prob(60)) + var/datum/objective/steal/steal_objective = new + steal_objective.owner = changeling + steal_objective.find_target() + changeling.objectives += steal_objective + else + var/datum/objective/debrain/debrain_objective = new + debrain_objective.owner = changeling + debrain_objective.find_target() + changeling.objectives += debrain_objective + + var/list/active_ais = active_ais() + if(active_ais.len && prob(4)) // Leaving this at a flat chance for now, problems with the num_players() proc due to latejoin antags. + var/datum/objective/destroy/destroy_objective = new + destroy_objective.owner = changeling + destroy_objective.find_target() + changeling.objectives += destroy_objective + else + var/datum/objective/assassinate/kill_objective = new + kill_objective.owner = changeling + kill_objective.find_target() + changeling.objectives += kill_objective + + if(!(locate(/datum/objective/escape) in changeling.objectives)) + var/datum/objective/escape/escape_with_identity/identity_theft = new + identity_theft.owner = changeling + identity_theft.target = kill_objective.target + if(identity_theft.target && identity_theft.target.current) + identity_theft.target_real_name = kill_objective.target.current.real_name //Whoops, forgot this. + var/mob/living/carbon/human/H = identity_theft.target.current + if(can_absorb_species(H.dna.species)) // For species that can't be absorbed - should default to an escape objective + identity_theft.explanation_text = "Escape on the shuttle or an escape pod with the identity of [identity_theft.target_real_name], the [identity_theft.target.assigned_role] while wearing [identity_theft.target.p_their()] identification card." + changeling.objectives += identity_theft + else + qdel(identity_theft) + + if(!(locate(/datum/objective/escape) in changeling.objectives)) + if(prob(70)) + var/datum/objective/escape/escape_objective = new + escape_objective.owner = changeling + changeling.objectives += escape_objective + else + var/datum/objective/escape/escape_with_identity/identity_theft = new + identity_theft.owner = changeling + identity_theft.find_target() + changeling.objectives += identity_theft + return + +/datum/game_mode/proc/greet_changeling(datum/mind/changeling, you_are=1) + SEND_SOUND(changeling.current, 'sound/ambience/antag/ling_aler.ogg') + if(you_are) + to_chat(changeling.current, "You are a changeling!") + to_chat(changeling.current, "Use say \":g message\" to communicate with your fellow changelings. Remember: you get all of their absorbed DNA if you absorb them.") + to_chat(changeling.current, "You must complete the following tasks:") + if(changeling.current.mind) + if(changeling.current.mind.assigned_role == "Clown") + to_chat(changeling.current, "You have evolved beyond your clownish nature, allowing you to wield weapons without harming yourself.") + changeling.current.mutations.Remove(CLUMSY) + var/datum/action/innate/toggle_clumsy/A = new + A.Grant(changeling.current) + var/obj_count = 1 + for(var/datum/objective/objective in changeling.objectives) + to_chat(changeling.current, "Objective #[obj_count]: [objective.explanation_text]") + obj_count++ + return + +/datum/game_mode/proc/remove_changeling(datum/mind/changeling_mind) + if(changeling_mind in changelings) + changelings -= changeling_mind + changeling_mind.current.remove_changeling_powers() + changeling_mind.memory = "" + changeling_mind.special_role = null + if(issilicon(changeling_mind.current)) + to_chat(changeling_mind.current, "You have been robotized!") + to_chat(changeling_mind.current, "You must obey your silicon laws and master AI above all else. Your objectives will consider you to be dead.") + else + to_chat(changeling_mind.current, "You lose your powers! You are no longer a changeling and are stuck in your current form!") + update_change_icons_removed(changeling_mind) + +/datum/game_mode/proc/update_change_icons_added(datum/mind/changeling) + var/datum/atom_hud/antag/linghud = huds[ANTAG_HUD_CHANGELING] + linghud.join_hud(changeling.current) + set_antag_hud(changeling.current, "hudchangeling") + +/datum/game_mode/proc/update_change_icons_removed(datum/mind/changeling) + var/datum/atom_hud/antag/linghud = huds[ANTAG_HUD_CHANGELING] + linghud.leave_hud(changeling.current) + set_antag_hud(changeling.current, null) + +/datum/game_mode/proc/grant_changeling_powers(mob/living/carbon/changeling_mob) + if(!istype(changeling_mob)) + return + changeling_mob.make_changeling() + +/datum/game_mode/proc/auto_declare_completion_changeling() + if(changelings.len) + var/text = "The changelings were:" + for(var/datum/mind/changeling in changelings) + var/changelingwin = 1 + + text += "
    [changeling.key] was [changeling.name] (" + if(changeling.current) + if(changeling.current.stat == DEAD) + text += "died" + else + text += "survived" + if(changeling.current.real_name != changeling.name) + text += " as [changeling.current.real_name]" + else + text += "body destroyed" + changelingwin = 0 + text += ")" + + //Removed sanity if(changeling) because we -want- a runtime to inform us that the changelings list is incorrect and needs to be fixed. + text += "
    Changeling ID: [changeling.changeling.changelingID]." + text += "
    Genomes Extracted: [changeling.changeling.absorbedcount]" + + if(changeling.objectives.len) + var/count = 1 + for(var/datum/objective/objective in changeling.objectives) + if(objective.check_completion()) + text += "
    Objective #[count]: [objective.explanation_text] Success!" + feedback_add_details("changeling_objective","[objective.type]|SUCCESS") + else + text += "
    Objective #[count]: [objective.explanation_text] Fail." + feedback_add_details("changeling_objective","[objective.type]|FAIL") + changelingwin = 0 + count++ + + if(changelingwin) + text += "
    The changeling was successful!" + feedback_add_details("changeling_success","SUCCESS") + else + text += "
    The changeling has failed." + feedback_add_details("changeling_success","FAIL") + + to_chat(world, text) + + return 1 + +/datum/changeling //stores changeling powers, changeling recharge thingie, changeling absorbed DNA and changeling ID (for changeling hivemind) + var/list/absorbed_dna = list() + var/list/absorbed_languages = list() + var/list/protected_dna = list() //DNA that is not lost when capacity is otherwise full. + var/dna_max = 5 //How many total DNA strands the changeling can store for transformation. + var/absorbedcount = 1 //Would require at least 1 sample to take on the form of a human + var/chem_charges = 20 + var/chem_recharge_rate = 1 + var/chem_recharge_slowdown = 0 + var/chem_storage = 75 + var/sting_range = 2 + var/changelingID = "Changeling" + var/geneticdamage = 0 + var/isabsorbing = 0 + var/islinking = 0 + var/geneticpoints = 10 + var/purchasedpowers = list() + var/mimicing = "" + var/canrespec = FALSE //set to TRUE in absorb.dm + var/changeling_speak = 0 + var/datum/dna/chosen_dna + var/datum/action/changeling/sting/chosen_sting + var/regenerating = FALSE + +/datum/changeling/New(gender=FEMALE) + ..() + var/honorific + if(gender == FEMALE) + honorific = "Ms." + else + honorific = "Mr." + if(possible_changeling_IDs.len) + changelingID = pick(possible_changeling_IDs) + possible_changeling_IDs -= changelingID + changelingID = "[honorific] [changelingID]" + else + changelingID = "[honorific] [rand(1,999)]" + +/datum/changeling/proc/regenerate(mob/living/carbon/the_ling) + if(istype(the_ling)) + if(the_ling.stat == DEAD) + chem_charges = min(max(0, chem_charges + chem_recharge_rate - chem_recharge_slowdown), (chem_storage*0.5)) + geneticdamage = max(LING_DEAD_GENETICDAMAGE_HEAL_CAP,geneticdamage-1) + else //not dead? no chem/geneticdamage caps. + chem_charges = min(max(0, chem_charges + chem_recharge_rate - chem_recharge_slowdown), chem_storage) + geneticdamage = max(0, geneticdamage-1) + +/datum/changeling/proc/GetDNA(dna_owner) + for(var/datum/dna/DNA in (absorbed_dna + protected_dna)) + if(dna_owner == DNA.real_name) + return DNA + +/datum/changeling/proc/find_dna(datum/dna/tDNA) + for(var/datum/dna/D in (absorbed_dna + protected_dna)) + if(tDNA.unique_enzymes == D.unique_enzymes && tDNA.uni_identity == D.uni_identity && tDNA.species.type == D.species.type) + return D + return null + +/datum/changeling/proc/has_dna(datum/dna/tDNA) + if(find_dna(tDNA)) + return 1 + return 0 + +// A changeling's DNA is "stale" if their current form's DNA is the oldest DNA in a full list +/datum/changeling/proc/using_stale_dna(mob/living/carbon/user) + var/current_dna = find_dna(user.dna) + if(absorbed_dna.len < dna_max) + return 0 // Still more room for DNA + if(!current_dna || !(current_dna in absorbed_dna)) + return 1 // Oops, our current DNA was somehow not absorbed; force a transformation + if(absorbed_dna[1] == current_dna) + return 1 // Oldest DNA is the current DNA + return 0 + +/datum/changeling/proc/trim_dna() + absorbed_dna -= null + if(absorbed_dna.len > dna_max) + absorbed_dna.Cut(1, (absorbed_dna.len - dna_max) + 1) + +/datum/changeling/proc/can_absorb_dna(mob/living/carbon/user, mob/living/carbon/target) + if(using_stale_dna(user))//If our current DNA is the stalest, we gotta ditch it. + to_chat(user, "The DNA we are wearing is stale. Transform and try again.") + return + + if(!target || !target.dna) + to_chat(user, "This creature does not have any DNA.") + return + + var/mob/living/carbon/human/T = target + if(!istype(T) || issmall(T)) + to_chat(user, "[T] is not compatible with our biology.") + return + + if((NOCLONE || SKELETON || HUSK) in T.mutations) + to_chat(user, "DNA of [target] is ruined beyond usability!") + return + + if(NO_DNA in T.dna.species.species_traits) + to_chat(user, "This creature does not have DNA!") + return + + if(has_dna(target.dna)) + to_chat(user, "We already have this DNA in storage!") + + return 1 + +/proc/can_absorb_species(datum/species/S) + return !(NO_DNA in S.species_traits) diff --git a/code/game/gamemodes/changeling/powers/augmented_eyesight.dm b/code/game/gamemodes/changeling/powers/augmented_eyesight.dm index b0b77a456d5..4663fef8b97 100644 --- a/code/game/gamemodes/changeling/powers/augmented_eyesight.dm +++ b/code/game/gamemodes/changeling/powers/augmented_eyesight.dm @@ -40,7 +40,7 @@ name = "protective membranes" desc = "These variable transparency organic membranes will protect you from welders and flashes and heal your eye damage." icon_state = "ling_eyeshield" - eye_colour = null + eye_colour = "#000000" implant_overlay = null slot = "eye_ling" status = 0 @@ -61,7 +61,7 @@ name = "heat receptors" desc = "These heat receptors dramatically increases eyes light sensing ability." icon_state = "ling_thermal" - eye_colour = null + eye_colour = "#000000" implant_overlay = null slot = "eye_ling" status = 0 diff --git a/code/game/gamemodes/changeling/powers/lesserform.dm b/code/game/gamemodes/changeling/powers/lesserform.dm index 0dd112b66dd..ceab9e02cd3 100644 --- a/code/game/gamemodes/changeling/powers/lesserform.dm +++ b/code/game/gamemodes/changeling/powers/lesserform.dm @@ -33,4 +33,4 @@ HF.Grant(user) feedback_add_details("changeling_powers","LF") - return 1 \ No newline at end of file + return 1 diff --git a/code/game/gamemodes/changeling/powers/panacea.dm b/code/game/gamemodes/changeling/powers/panacea.dm index 6e6d69cc1dd..ae675ac5ad6 100644 --- a/code/game/gamemodes/changeling/powers/panacea.dm +++ b/code/game/gamemodes/changeling/powers/panacea.dm @@ -41,4 +41,4 @@ D.cure() feedback_add_details("changeling_powers","AP") - return 1 \ No newline at end of file + return 1 diff --git a/code/game/gamemodes/changeling/powers/transform.dm b/code/game/gamemodes/changeling/powers/transform.dm index 417fbaa052d..afad76299fb 100644 --- a/code/game/gamemodes/changeling/powers/transform.dm +++ b/code/game/gamemodes/changeling/powers/transform.dm @@ -32,4 +32,4 @@ if(!chosen_name) return var/datum/dna/chosen_dna = GetDNA(chosen_name) - return chosen_dna \ No newline at end of file + return chosen_dna diff --git a/code/game/gamemodes/changeling/traitor_chan.dm b/code/game/gamemodes/changeling/traitor_chan.dm index 80a3dc37809..f163b2a4c04 100644 --- a/code/game/gamemodes/changeling/traitor_chan.dm +++ b/code/game/gamemodes/changeling/traitor_chan.dm @@ -28,6 +28,7 @@ changelings += changeling modePlayer += changelings changeling.restricted_roles = restricted_jobs + changeling.special_role = SPECIAL_ROLE_CHANGELING return ..() else return 0 @@ -40,4 +41,4 @@ greet_changeling(changeling) update_change_icons_added(changeling) ..() - return \ No newline at end of file + return diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm index 113487dc1dc..2a42d531f0f 100644 --- a/code/game/gamemodes/cult/cult.dm +++ b/code/game/gamemodes/cult/cult.dm @@ -1,396 +1,396 @@ -var/global/list/all_cults = list() - -/datum/game_mode - var/list/datum/mind/cult = list() - -/proc/iscultist(mob/living/M as mob) - return istype(M) && M.mind && SSticker && SSticker.mode && (M.mind in SSticker.mode.cult) - - -/proc/is_convertable_to_cult(datum/mind/mind) - if(!mind) - return FALSE - if(!mind.current) - return FALSE - if(iscultist(mind.current)) - return TRUE //If they're already in the cult, assume they are convertable - if(ishuman(mind.current) && (mind.assigned_role in list("Captain", "Chaplain"))) - return FALSE - if(ishuman(mind.current)) - var/mob/living/carbon/human/H = mind.current - if(ismindshielded(H)) //mindshield protects against conversions unless removed - return FALSE -// if(mind.offstation_role) cant convert offstation roles such as ghost spawns -// return FALSE Commented out until we can figure out why offstation_role is getting set to TRUE on normal crew - if(issilicon(mind.current)) - return FALSE //can't convert machines, that's ratvar's thing - if(isguardian(mind.current)) - var/mob/living/simple_animal/hostile/guardian/G = mind.current - if(!iscultist(G.summoner)) - return FALSE //can't convert it unless the owner is converted - if(isgolem(mind.current)) - return FALSE - return TRUE - -/proc/is_sacrifice_target(datum/mind/mind) - if(SSticker.mode.name == "cult") - var/datum/game_mode/cult/cult_mode = SSticker.mode - if(mind == cult_mode.sacrifice_target) - return 1 - return 0 - -/datum/game_mode/cult - name = "cult" - config_tag = "cult" - restricted_jobs = list("Chaplain","AI", "Cyborg", "Internal Affairs Agent", "Security Officer", "Warden", "Detective", "Security Pod Pilot", "Head of Security", "Captain", "Head of Personnel", "Blueshield", "Nanotrasen Representative", "Magistrate", "Brig Physician", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer") - protected_jobs = list() - required_players = 30 - required_enemies = 3 - recommended_enemies = 4 - - var/datum/mind/sacrifice_target = null - var/finished = 0 - - - var/list/objectives = list() - - var/eldergod = 1 //for the summon god objective - var/demons_summoned = 0 - - var/acolytes_needed = 4 //for the survive objective - base number of acolytes, increased by 1 for every 10 players - var/const/min_cultists_to_start = 3 - var/const/max_cultists_to_start = 4 - var/acolytes_survived = 0 - - var/narsie_condition_cleared = 0 //allows Nar-Sie to be summonned during cult rounds. set to 1 once the cult reaches the second phase. - var/current_objective = 1 //equals the number of cleared objectives + 1 - var/prenarsie_objectives = 2 //how many objectives at most before the cult gets to summon narsie - var/list/bloody_floors = list() - var/spilltarget = 100 //how many floor tiles must be covered in blood to complete the bloodspill objective - var/convert_target = 0 //how many members the cult needs to reach to complete the convert objective - var/harvested = 0 - - var/list/sacrificed = list() //contains the mind of the sacrifice target ONCE the sacrifice objective has been completed - var/mass_convert = 0 //set to 1 if the convert objective has been accomplised once that round - var/spilled_blood = 0 //set to 1 if the bloodspill objective has been accomplised once that round - var/max_spilled_blood = 0 //highest quantity of blood covered tiles during the round - var/bonus = 0 //set to 1 if the cult has completed the bonus (third phase) objective (harvest, hijack, massacre) - - var/harvest_target = 10 - var/massacre_target = 5 - - var/escaped_shuttle = 0 - var/escaped_pod = 0 - var/survivors = 0 - -/datum/game_mode/cult/announce() - to_chat(world, "The current game mode is - Cult!") - to_chat(world, "Some crewmembers are attempting to start a cult!
    \nCultists - complete your objectives. Convert crewmembers to your cause by using the convert rune. Remember - there is no you, there is only the cult.
    \nPersonnel - Do not let the cult succeed in its mission. Brainwashing them with the chaplain's bible reverts them to whatever CentComm-allowed faith they had.
    ") - - -/datum/game_mode/cult/pre_setup() - if(config.protect_roles_from_antagonist) - restricted_jobs += protected_jobs - ..() - var/list/cultists_possible = get_players_for_role(ROLE_CULTIST) - - for(var/cultists_number = 1 to max_cultists_to_start) - if(!cultists_possible.len) - break - var/datum/mind/cultist = pick(cultists_possible) - cultists_possible -= cultist - cult += cultist - cultist.restricted_roles = restricted_jobs - cultist.special_role = SPECIAL_ROLE_CULTIST - ..() - return (cult.len>0) - - -/datum/game_mode/cult/post_setup() - modePlayer += cult - acolytes_needed = acolytes_needed + round((num_players_started() / 10)) - - if(!summon_spots.len) - while(summon_spots.len < SUMMON_POSSIBILITIES) - var/area/summon = pick(return_sorted_areas() - summon_spots) - if(summon && is_station_level(summon.z) && summon.valid_territory) - summon_spots += summon - - for(var/datum/mind/cult_mind in cult) - SEND_SOUND(cult_mind.current, 'sound/ambience/antag/bloodcult.ogg') - equip_cultist(cult_mind.current) - cult_mind.current.faction |= "cult" - var/datum/action/innate/cultcomm/C = new() - C.Grant(cult_mind.current) - update_cult_icons_added(cult_mind) - to_chat(cult_mind.current, "You catch a glimpse of the Realm of [SSticker.cultdat.entity_name], [SSticker.cultdat.entity_title3]. You now see how flimsy the world is, you see that it should be open to the knowledge of [SSticker.cultdat.entity_name].") - - first_phase() - - ..() - - -/datum/game_mode/cult/proc/memorize_cult_objectives(datum/mind/cult_mind) - for(var/obj_count in 1 to objectives.len) - var/explanation - switch(objectives[obj_count]) - if("survive") - explanation = "Our knowledge must live on. Make sure at least [acolytes_needed] acolytes escape on the shuttle to spread their work on an another station." - if("convert") - explanation = "We must increase our influence before we can summon [SSticker.cultdat.entity_name], Convert [convert_target] crew members. Take it slowly to avoid raising suspicions." - if("bloodspill") - explanation = "We must prepare this place for [SSticker.cultdat.entity_title1]'s coming. Spill blood and gibs over [spilltarget] floor tiles." - if("sacrifice") - if(sacrifice_target) - explanation = "Sacrifice [sacrifice_target.current.real_name], the [sacrifice_target.assigned_role]. You will need the sacrifice rune and three acolytes to do so." - else - explanation = "Free objective." - if("eldergod") - explanation = "Summon [SSticker.cultdat.entity_name] by invoking the 'Tear Reality' rune.The summoning can only be accomplished in [english_list(summon_spots)] - where the veil is weak enough for the ritual to begin." - to_chat(cult_mind.current, "Objective #[obj_count]: [explanation]") - cult_mind.memory += "Objective #[obj_count]: [explanation]
    " - - -/datum/game_mode/proc/equip_cultist(mob/living/carbon/human/mob) - if(!istype(mob)) - return - - if(mob.mind) - if(mob.mind.assigned_role == "Clown") - to_chat(mob, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.") - mob.mutations.Remove(CLUMSY) - var/datum/action/innate/toggle_clumsy/A = new - A.Grant(mob) - var/obj/item/paper/talisman/supply/T = new(mob) - var/list/slots = list ( - "backpack" = slot_in_backpack, - "left pocket" = slot_l_store, - "right pocket" = slot_r_store, - "left hand" = slot_l_hand, - "right hand" = slot_r_hand, - ) - var/where = mob.equip_in_one_of_slots(T, slots) - if(!where) - to_chat(mob, "Unfortunately, you weren't able to get a talisman. This is very bad and you should adminhelp immediately.") - else - to_chat(mob, "You have a talisman in your [where], one that will help you start the cult on this station. Use it well and remember - there are others.") - mob.update_icons() - return 1 - - -/datum/game_mode/proc/add_cultist(datum/mind/cult_mind) //BASE - if(!istype(cult_mind)) - return 0 - var/datum/game_mode/cult/cult_mode = SSticker.mode - if(!(cult_mind in cult) && is_convertable_to_cult(cult_mind)) - cult += cult_mind - cult_mind.current.faction |= "cult" - var/datum/action/innate/cultcomm/C = new() - C.Grant(cult_mind.current) - SEND_SOUND(cult_mind.current, 'sound/ambience/antag/bloodcult.ogg') - cult_mind.current.create_attack_log("Has been converted to the cult!") - if(jobban_isbanned(cult_mind.current, ROLE_CULTIST) || jobban_isbanned(cult_mind.current, ROLE_SYNDICATE)) - replace_jobbanned_player(cult_mind.current, ROLE_CULTIST) - update_cult_icons_added(cult_mind) - cult_mode.memorize_cult_objectives(cult_mind) - if(GAMEMODE_IS_CULT) - cult_mode.check_numbers() - return 1 - -/datum/game_mode/proc/remove_cultist(datum/mind/cult_mind, show_message = 1) - if(cult_mind in cult) - cult -= cult_mind - to_chat(cult_mind.current, "An unfamiliar white light flashes through your mind, cleansing the taint of the dark-one and the memories of your time as his servant with it.") - cult_mind.current.faction -= "cult" - cult_mind.memory = "" - cult_mind.special_role = null - for(var/datum/action/innate/cultcomm/C in cult_mind.current.actions) - qdel(C) - update_cult_icons_removed(cult_mind) - if(show_message) - for(var/mob/M in viewers(cult_mind.current)) - to_chat(M, "[cult_mind.current] looks like [cult_mind.current.p_they()] just reverted to [cult_mind.current.p_their()] old faith!") - - -/datum/game_mode/proc/update_cult_icons_added(datum/mind/cult_mind) - var/datum/atom_hud/antag/culthud = huds[ANTAG_HUD_CULT] - culthud.join_hud(cult_mind.current) - set_antag_hud(cult_mind.current, "hudcultist") - - -/datum/game_mode/proc/update_cult_icons_removed(datum/mind/cult_mind) - var/datum/atom_hud/antag/culthud = huds[ANTAG_HUD_CULT] - culthud.leave_hud(cult_mind.current) - set_antag_hud(cult_mind.current, null) - -/datum/game_mode/proc/update_cult_comms_added(datum/mind/cult_mind) - var/datum/action/innate/cultcomm/C = new() - C.Grant(cult_mind.current) - -/datum/game_mode/cult/proc/get_unconvertables() - var/list/ucs = list() - for(var/mob/living/carbon/human/player in GLOB.player_list) - if(!is_convertable_to_cult(player.mind)) - ucs += player.mind - return ucs - - -/datum/game_mode/cult/proc/check_cult_victory() - var/cult_fail = 0 - if(objectives.Find("survive")) - cult_fail += check_survive() //the proc returns 1 if there are not enough cultists on the shuttle, 0 otherwise - if(objectives.Find("eldergod")) - cult_fail += eldergod //1 by default, 0 if the elder god has been summoned at least once - if(objectives.Find("slaughter")) - if(!demons_summoned) - cult_fail++ - if(objectives.Find("sacrifice")) - if(sacrifice_target && !(sacrifice_target in sacrificed)) //if the target has been sacrificed, ignore this step. otherwise, add 1 to cult_fail - cult_fail++ - if(objectives.Find("convert")) - if(cult.len < convert_target) - cult_fail++ - if(objectives.Find("bloodspill")) - if(max_spilled_blood < spilltarget) - cult_fail++ - - return cult_fail //if any objectives aren't met, failure - - -/datum/game_mode/cult/proc/check_survive() - acolytes_survived = 0 - for(var/datum/mind/cult_mind in cult) - if(cult_mind.current && cult_mind.current.stat!=2) - var/area/A = get_area(cult_mind.current ) - if( is_type_in_list(A, centcom_areas)) - acolytes_survived++ - else if(A == SSshuttle.emergency.areaInstance && SSshuttle.emergency.mode >= SHUTTLE_ESCAPE) //snowflaked into objectives because shitty bay shuttles had areas to auto-determine this - acolytes_survived++ - - if(acolytes_survived>=acolytes_needed) - return 0 - else - return 1 - - -/atom/proc/cult_log(var/message) - investigate_log(message, "cult") - - -/datum/game_mode/cult/declare_completion() - bonus_check() - - if(!check_cult_victory()) - feedback_set_details("round_end_result","cult win - cult win") - feedback_set("round_end_result",acolytes_survived) - to_chat(world, " The cult wins! It has succeeded in serving its dark masters!") - else - feedback_set_details("round_end_result","cult loss - staff stopped the cult") - feedback_set("round_end_result",acolytes_survived) - to_chat(world, " The staff managed to stop the cult!") - - var/text = "Cultists escaped: [acolytes_survived]" - - if(objectives.len) - text += "
    The cultists' objectives were:" - for(var/obj_count=1, obj_count <= objectives.len, obj_count++) - var/explanation - switch(objectives[obj_count]) - if("survive") - if(!check_survive()) - explanation = "Make sure at least [acolytes_needed] acolytes escape on the shuttle. Success!" - feedback_add_details("cult_objective","cult_survive|SUCCESS|[acolytes_needed]") - else - explanation = "Make sure at least [acolytes_needed] acolytes escape on the shuttle. Fail." - feedback_add_details("cult_objective","cult_survive|FAIL|[acolytes_needed]") - if("sacrifice") - if(sacrifice_target) - if(sacrifice_target in sacrificed) - explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Success!" - feedback_add_details("cult_objective","cult_sacrifice|SUCCESS") - else if(sacrifice_target && sacrifice_target.current) - explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Fail." - feedback_add_details("cult_objective","cult_sacrifice|FAIL") - else - explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Fail (Gibbed)." - feedback_add_details("cult_objective","cult_sacrifice|FAIL|GIBBED") - if("eldergod") - if(!eldergod) - explanation = "Summon [SSticker.cultdat.entity_name]. Success!" - feedback_add_details("cult_objective","cult_narsie|SUCCESS") - else - explanation = "Summon [SSticker.cultdat.entity_name]. Fail." - feedback_add_details("cult_objective","cult_narsie|FAIL") - if("slaughter") - if(demons_summoned) - explanation = "Bring the Slaughter. Success!" - feedback_add_details("cult_objective","cult_demons|SUCCESS") - else - explanation = "Bring the Slaughter. Fail." - feedback_add_details("cult_objective","cult_demons|FAIL") - - if("convert")//convert half the crew - if(cult.len >= convert_target) - explanation = "Convert [convert_target] crewmembers ([cult.len] cultists at round end). Success!" - feedback_add_details("cult_objective","cult_convertion|SUCCESS") - else - explanation = "Convert [convert_target] crewmembers ([cult.len] total cultists). Fail!" - feedback_add_details("cult_objective","cult_convertion|FAIL") - - if("bloodspill")//cover a large portion of the station in blood - if(max_spilled_blood >= spilltarget) - explanation = "Cover [spilltarget] tiles of the station in blood (The peak number of covered tiles was: [max_spilled_blood]). Success!" - feedback_add_details("cult_objective","cult_bloodspill|SUCCESS") - else - explanation = "Cover [spilltarget] tiles of the station in blood (The peak number of covered tiles was: [max_spilled_blood]). Fail!" - feedback_add_details("cult_objective","cult_bloodspill|FAIL") - - if("harvest") - if(harvested > harvest_target) - explanation = "Offer [harvest_target] humans for [SSticker.cultdat.entity_name]'s first meal of the day. ([harvested] sacrificed) Success!" - feedback_add_details("cult_objective","cult_harvest|SUCCESS") - else - explanation = "Offer [harvest_target] humans for [SSticker.cultdat.entity_name]'s first meal of the day. ([harvested] sacrificed) Fail!" - feedback_add_details("cult_objective","cult_harvest|FAIL") - - if("hijack") - if(!escaped_shuttle) - explanation = "Do not let a single non-cultist board the Escape Shuttle. ([escaped_shuttle] escaped on the shuttle) ([escaped_pod] escaped on pods) Success!" - feedback_add_details("cult_objective","cult_hijack|SUCCESS") - else - explanation = "Do not let a single non-cultist board the Escape Shuttle. ([escaped_shuttle] escaped on the shuttle) ([escaped_pod] escaped on pods) Fail!" - feedback_add_details("cult_objective","cult_hijack|FAIL") - - if("massacre") - if(survivors < massacre_target) - explanation = "Massacre the crew until less than [massacre_target] people are left on the station. ([survivors] humans left alive) Success!" - feedback_add_details("cult_objective","cult_massacre|SUCCESS") - else - explanation = "Massacre the crew until less than [massacre_target] people are left on the station. ([survivors] humans left alive) Fail!" - feedback_add_details("cult_objective","cult_massacre|FAIL") - - text += "
    Objective #[obj_count]: [explanation]" - - to_chat(world, text) - ..() - return 1 - - -/datum/game_mode/proc/auto_declare_completion_cult() - if(cult.len || (SSticker && GAMEMODE_IS_CULT)) - var/text = "The cultists were:" - for(var/datum/mind/cultist in cult) - - text += "
    [cultist.key] was [cultist.name] (" - if(cultist.current) - if(cultist.current.stat == DEAD) - text += "died" - else - text += "survived" - if(cultist.current.real_name != cultist.name) - text += " as [cultist.current.real_name]" - else - text += "body destroyed" - text += ")" - - to_chat(world, text) +var/global/list/all_cults = list() + +/datum/game_mode + var/list/datum/mind/cult = list() + +/proc/iscultist(mob/living/M as mob) + return istype(M) && M.mind && SSticker && SSticker.mode && (M.mind in SSticker.mode.cult) + + +/proc/is_convertable_to_cult(datum/mind/mind) + if(!mind) + return FALSE + if(!mind.current) + return FALSE + if(iscultist(mind.current)) + return TRUE //If they're already in the cult, assume they are convertable + if(ishuman(mind.current) && (mind.assigned_role in list("Captain", "Chaplain"))) + return FALSE + if(ishuman(mind.current)) + var/mob/living/carbon/human/H = mind.current + if(ismindshielded(H)) //mindshield protects against conversions unless removed + return FALSE +// if(mind.offstation_role) cant convert offstation roles such as ghost spawns +// return FALSE Commented out until we can figure out why offstation_role is getting set to TRUE on normal crew + if(issilicon(mind.current)) + return FALSE //can't convert machines, that's ratvar's thing + if(isguardian(mind.current)) + var/mob/living/simple_animal/hostile/guardian/G = mind.current + if(!iscultist(G.summoner)) + return FALSE //can't convert it unless the owner is converted + if(isgolem(mind.current)) + return FALSE + return TRUE + +/proc/is_sacrifice_target(datum/mind/mind) + if(SSticker.mode.name == "cult") + var/datum/game_mode/cult/cult_mode = SSticker.mode + if(mind == cult_mode.sacrifice_target) + return 1 + return 0 + +/datum/game_mode/cult + name = "cult" + config_tag = "cult" + restricted_jobs = list("Chaplain","AI", "Cyborg", "Internal Affairs Agent", "Security Officer", "Warden", "Detective", "Security Pod Pilot", "Head of Security", "Captain", "Head of Personnel", "Blueshield", "Nanotrasen Representative", "Magistrate", "Brig Physician", "Nanotrasen Navy Officer", "Special Operations Officer", "Syndicate Officer") + protected_jobs = list() + required_players = 30 + required_enemies = 3 + recommended_enemies = 4 + + var/datum/mind/sacrifice_target = null + var/finished = 0 + + + var/list/objectives = list() + + var/eldergod = 1 //for the summon god objective + var/demons_summoned = 0 + + var/acolytes_needed = 4 //for the survive objective - base number of acolytes, increased by 1 for every 10 players + var/const/min_cultists_to_start = 3 + var/const/max_cultists_to_start = 4 + var/acolytes_survived = 0 + + var/narsie_condition_cleared = 0 //allows Nar-Sie to be summonned during cult rounds. set to 1 once the cult reaches the second phase. + var/current_objective = 1 //equals the number of cleared objectives + 1 + var/prenarsie_objectives = 2 //how many objectives at most before the cult gets to summon narsie + var/list/bloody_floors = list() + var/spilltarget = 100 //how many floor tiles must be covered in blood to complete the bloodspill objective + var/convert_target = 0 //how many members the cult needs to reach to complete the convert objective + var/harvested = 0 + + var/list/sacrificed = list() //contains the mind of the sacrifice target ONCE the sacrifice objective has been completed + var/mass_convert = 0 //set to 1 if the convert objective has been accomplised once that round + var/spilled_blood = 0 //set to 1 if the bloodspill objective has been accomplised once that round + var/max_spilled_blood = 0 //highest quantity of blood covered tiles during the round + var/bonus = 0 //set to 1 if the cult has completed the bonus (third phase) objective (harvest, hijack, massacre) + + var/harvest_target = 10 + var/massacre_target = 5 + + var/escaped_shuttle = 0 + var/escaped_pod = 0 + var/survivors = 0 + +/datum/game_mode/cult/announce() + to_chat(world, "The current game mode is - Cult!") + to_chat(world, "Some crewmembers are attempting to start a cult!
    \nCultists - complete your objectives. Convert crewmembers to your cause by using the convert rune. Remember - there is no you, there is only the cult.
    \nPersonnel - Do not let the cult succeed in its mission. Brainwashing them with the chaplain's bible reverts them to whatever CentComm-allowed faith they had.
    ") + + +/datum/game_mode/cult/pre_setup() + if(config.protect_roles_from_antagonist) + restricted_jobs += protected_jobs + ..() + var/list/cultists_possible = get_players_for_role(ROLE_CULTIST) + + for(var/cultists_number = 1 to max_cultists_to_start) + if(!cultists_possible.len) + break + var/datum/mind/cultist = pick(cultists_possible) + cultists_possible -= cultist + cult += cultist + cultist.restricted_roles = restricted_jobs + cultist.special_role = SPECIAL_ROLE_CULTIST + ..() + return (cult.len>0) + + +/datum/game_mode/cult/post_setup() + modePlayer += cult + acolytes_needed = acolytes_needed + round((num_players_started() / 10)) + + if(!summon_spots.len) + while(summon_spots.len < SUMMON_POSSIBILITIES) + var/area/summon = pick(return_sorted_areas() - summon_spots) + if(summon && is_station_level(summon.z) && summon.valid_territory) + summon_spots += summon + + for(var/datum/mind/cult_mind in cult) + SEND_SOUND(cult_mind.current, 'sound/ambience/antag/bloodcult.ogg') + equip_cultist(cult_mind.current) + cult_mind.current.faction |= "cult" + var/datum/action/innate/cultcomm/C = new() + C.Grant(cult_mind.current) + update_cult_icons_added(cult_mind) + to_chat(cult_mind.current, "You catch a glimpse of the Realm of [SSticker.cultdat.entity_name], [SSticker.cultdat.entity_title3]. You now see how flimsy the world is, you see that it should be open to the knowledge of [SSticker.cultdat.entity_name].") + + first_phase() + + ..() + + +/datum/game_mode/cult/proc/memorize_cult_objectives(datum/mind/cult_mind) + for(var/obj_count in 1 to objectives.len) + var/explanation + switch(objectives[obj_count]) + if("survive") + explanation = "Our knowledge must live on. Make sure at least [acolytes_needed] acolytes escape on the shuttle to spread their work on an another station." + if("convert") + explanation = "We must increase our influence before we can summon [SSticker.cultdat.entity_name], Convert [convert_target] crew members. Take it slowly to avoid raising suspicions." + if("bloodspill") + explanation = "We must prepare this place for [SSticker.cultdat.entity_title1]'s coming. Spill blood and gibs over [spilltarget] floor tiles." + if("sacrifice") + if(sacrifice_target) + explanation = "Sacrifice [sacrifice_target.current.real_name], the [sacrifice_target.assigned_role]. You will need the sacrifice rune and three acolytes to do so." + else + explanation = "Free objective." + if("eldergod") + explanation = "Summon [SSticker.cultdat.entity_name] by invoking the 'Tear Reality' rune.The summoning can only be accomplished in [english_list(summon_spots)] - where the veil is weak enough for the ritual to begin." + to_chat(cult_mind.current, "Objective #[obj_count]: [explanation]") + cult_mind.memory += "Objective #[obj_count]: [explanation]
    " + + +/datum/game_mode/proc/equip_cultist(mob/living/carbon/human/mob) + if(!istype(mob)) + return + + if(mob.mind) + if(mob.mind.assigned_role == "Clown") + to_chat(mob, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.") + mob.mutations.Remove(CLUMSY) + var/datum/action/innate/toggle_clumsy/A = new + A.Grant(mob) + var/obj/item/paper/talisman/supply/T = new(mob) + var/list/slots = list ( + "backpack" = slot_in_backpack, + "left pocket" = slot_l_store, + "right pocket" = slot_r_store, + "left hand" = slot_l_hand, + "right hand" = slot_r_hand, + ) + var/where = mob.equip_in_one_of_slots(T, slots) + if(!where) + to_chat(mob, "Unfortunately, you weren't able to get a talisman. This is very bad and you should adminhelp immediately.") + else + to_chat(mob, "You have a talisman in your [where], one that will help you start the cult on this station. Use it well and remember - there are others.") + mob.update_icons() + return 1 + + +/datum/game_mode/proc/add_cultist(datum/mind/cult_mind) //BASE + if(!istype(cult_mind)) + return 0 + var/datum/game_mode/cult/cult_mode = SSticker.mode + if(!(cult_mind in cult)) + cult += cult_mind + cult_mind.current.faction |= "cult" + var/datum/action/innate/cultcomm/C = new() + C.Grant(cult_mind.current) + SEND_SOUND(cult_mind.current, 'sound/ambience/antag/bloodcult.ogg') + cult_mind.current.create_attack_log("Has been converted to the cult!") + if(jobban_isbanned(cult_mind.current, ROLE_CULTIST) || jobban_isbanned(cult_mind.current, ROLE_SYNDICATE)) + replace_jobbanned_player(cult_mind.current, ROLE_CULTIST) + update_cult_icons_added(cult_mind) + cult_mode.memorize_cult_objectives(cult_mind) + if(GAMEMODE_IS_CULT) + cult_mode.check_numbers() + return 1 + +/datum/game_mode/proc/remove_cultist(datum/mind/cult_mind, show_message = 1) + if(cult_mind in cult) + cult -= cult_mind + to_chat(cult_mind.current, "An unfamiliar white light flashes through your mind, cleansing the taint of the dark-one and the memories of your time as his servant with it.") + cult_mind.current.faction -= "cult" + cult_mind.memory = "" + cult_mind.special_role = null + for(var/datum/action/innate/cultcomm/C in cult_mind.current.actions) + qdel(C) + update_cult_icons_removed(cult_mind) + if(show_message) + for(var/mob/M in viewers(cult_mind.current)) + to_chat(M, "[cult_mind.current] looks like [cult_mind.current.p_they()] just reverted to [cult_mind.current.p_their()] old faith!") + + +/datum/game_mode/proc/update_cult_icons_added(datum/mind/cult_mind) + var/datum/atom_hud/antag/culthud = huds[ANTAG_HUD_CULT] + culthud.join_hud(cult_mind.current) + set_antag_hud(cult_mind.current, "hudcultist") + + +/datum/game_mode/proc/update_cult_icons_removed(datum/mind/cult_mind) + var/datum/atom_hud/antag/culthud = huds[ANTAG_HUD_CULT] + culthud.leave_hud(cult_mind.current) + set_antag_hud(cult_mind.current, null) + +/datum/game_mode/proc/update_cult_comms_added(datum/mind/cult_mind) + var/datum/action/innate/cultcomm/C = new() + C.Grant(cult_mind.current) + +/datum/game_mode/cult/proc/get_unconvertables() + var/list/ucs = list() + for(var/mob/living/carbon/human/player in GLOB.player_list) + if(!is_convertable_to_cult(player.mind)) + ucs += player.mind + return ucs + + +/datum/game_mode/cult/proc/check_cult_victory() + var/cult_fail = 0 + if(objectives.Find("survive")) + cult_fail += check_survive() //the proc returns 1 if there are not enough cultists on the shuttle, 0 otherwise + if(objectives.Find("eldergod")) + cult_fail += eldergod //1 by default, 0 if the elder god has been summoned at least once + if(objectives.Find("slaughter")) + if(!demons_summoned) + cult_fail++ + if(objectives.Find("sacrifice")) + if(sacrifice_target && !(sacrifice_target in sacrificed)) //if the target has been sacrificed, ignore this step. otherwise, add 1 to cult_fail + cult_fail++ + if(objectives.Find("convert")) + if(cult.len < convert_target) + cult_fail++ + if(objectives.Find("bloodspill")) + if(max_spilled_blood < spilltarget) + cult_fail++ + + return cult_fail //if any objectives aren't met, failure + + +/datum/game_mode/cult/proc/check_survive() + acolytes_survived = 0 + for(var/datum/mind/cult_mind in cult) + if(cult_mind.current && cult_mind.current.stat!=2) + var/area/A = get_area(cult_mind.current ) + if( is_type_in_list(A, centcom_areas)) + acolytes_survived++ + else if(A == SSshuttle.emergency.areaInstance && SSshuttle.emergency.mode >= SHUTTLE_ESCAPE) //snowflaked into objectives because shitty bay shuttles had areas to auto-determine this + acolytes_survived++ + + if(acolytes_survived>=acolytes_needed) + return 0 + else + return 1 + + +/atom/proc/cult_log(var/message) + investigate_log(message, "cult") + + +/datum/game_mode/cult/declare_completion() + bonus_check() + + if(!check_cult_victory()) + feedback_set_details("round_end_result","cult win - cult win") + feedback_set("round_end_result",acolytes_survived) + to_chat(world, " The cult wins! It has succeeded in serving its dark masters!") + else + feedback_set_details("round_end_result","cult loss - staff stopped the cult") + feedback_set("round_end_result",acolytes_survived) + to_chat(world, " The staff managed to stop the cult!") + + var/text = "Cultists escaped: [acolytes_survived]" + + if(objectives.len) + text += "
    The cultists' objectives were:" + for(var/obj_count=1, obj_count <= objectives.len, obj_count++) + var/explanation + switch(objectives[obj_count]) + if("survive") + if(!check_survive()) + explanation = "Make sure at least [acolytes_needed] acolytes escape on the shuttle. Success!" + feedback_add_details("cult_objective","cult_survive|SUCCESS|[acolytes_needed]") + else + explanation = "Make sure at least [acolytes_needed] acolytes escape on the shuttle. Fail." + feedback_add_details("cult_objective","cult_survive|FAIL|[acolytes_needed]") + if("sacrifice") + if(sacrifice_target) + if(sacrifice_target in sacrificed) + explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Success!" + feedback_add_details("cult_objective","cult_sacrifice|SUCCESS") + else if(sacrifice_target && sacrifice_target.current) + explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Fail." + feedback_add_details("cult_objective","cult_sacrifice|FAIL") + else + explanation = "Sacrifice [sacrifice_target.name], the [sacrifice_target.assigned_role]. Fail (Gibbed)." + feedback_add_details("cult_objective","cult_sacrifice|FAIL|GIBBED") + if("eldergod") + if(!eldergod) + explanation = "Summon [SSticker.cultdat.entity_name]. Success!" + feedback_add_details("cult_objective","cult_narsie|SUCCESS") + else + explanation = "Summon [SSticker.cultdat.entity_name]. Fail." + feedback_add_details("cult_objective","cult_narsie|FAIL") + if("slaughter") + if(demons_summoned) + explanation = "Bring the Slaughter. Success!" + feedback_add_details("cult_objective","cult_demons|SUCCESS") + else + explanation = "Bring the Slaughter. Fail." + feedback_add_details("cult_objective","cult_demons|FAIL") + + if("convert")//convert half the crew + if(cult.len >= convert_target) + explanation = "Convert [convert_target] crewmembers ([cult.len] cultists at round end). Success!" + feedback_add_details("cult_objective","cult_convertion|SUCCESS") + else + explanation = "Convert [convert_target] crewmembers ([cult.len] total cultists). Fail!" + feedback_add_details("cult_objective","cult_convertion|FAIL") + + if("bloodspill")//cover a large portion of the station in blood + if(max_spilled_blood >= spilltarget) + explanation = "Cover [spilltarget] tiles of the station in blood (The peak number of covered tiles was: [max_spilled_blood]). Success!" + feedback_add_details("cult_objective","cult_bloodspill|SUCCESS") + else + explanation = "Cover [spilltarget] tiles of the station in blood (The peak number of covered tiles was: [max_spilled_blood]). Fail!" + feedback_add_details("cult_objective","cult_bloodspill|FAIL") + + if("harvest") + if(harvested > harvest_target) + explanation = "Offer [harvest_target] humans for [SSticker.cultdat.entity_name]'s first meal of the day. ([harvested] sacrificed) Success!" + feedback_add_details("cult_objective","cult_harvest|SUCCESS") + else + explanation = "Offer [harvest_target] humans for [SSticker.cultdat.entity_name]'s first meal of the day. ([harvested] sacrificed) Fail!" + feedback_add_details("cult_objective","cult_harvest|FAIL") + + if("hijack") + if(!escaped_shuttle) + explanation = "Do not let a single non-cultist board the Escape Shuttle. ([escaped_shuttle] escaped on the shuttle) ([escaped_pod] escaped on pods) Success!" + feedback_add_details("cult_objective","cult_hijack|SUCCESS") + else + explanation = "Do not let a single non-cultist board the Escape Shuttle. ([escaped_shuttle] escaped on the shuttle) ([escaped_pod] escaped on pods) Fail!" + feedback_add_details("cult_objective","cult_hijack|FAIL") + + if("massacre") + if(survivors < massacre_target) + explanation = "Massacre the crew until less than [massacre_target] people are left on the station. ([survivors] humans left alive) Success!" + feedback_add_details("cult_objective","cult_massacre|SUCCESS") + else + explanation = "Massacre the crew until less than [massacre_target] people are left on the station. ([survivors] humans left alive) Fail!" + feedback_add_details("cult_objective","cult_massacre|FAIL") + + text += "
    Objective #[obj_count]: [explanation]" + + to_chat(world, text) + ..() + return 1 + + +/datum/game_mode/proc/auto_declare_completion_cult() + if(cult.len || (SSticker && GAMEMODE_IS_CULT)) + var/text = "The cultists were:" + for(var/datum/mind/cultist in cult) + + text += "
    [cultist.key] was [cultist.name] (" + if(cultist.current) + if(cultist.current.stat == DEAD) + text += "died" + else + text += "survived" + if(cultist.current.real_name != cultist.name) + text += " as [cultist.current.real_name]" + else + text += "body destroyed" + text += ")" + + to_chat(world, text) diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm index 74d7a1889e7..f9ee181356d 100644 --- a/code/game/gamemodes/cult/cult_items.dm +++ b/code/game/gamemodes/cult/cult_items.dm @@ -1,377 +1,377 @@ -/obj/item/melee/cultblade - name = "Cult Blade" - desc = "An arcane weapon wielded by the followers of a cult." - icon_state = "cultblade" - item_state = "cultblade" - w_class = WEIGHT_CLASS_BULKY - force = 30 - throwforce = 10 - sharp = 1 - hitsound = 'sound/weapons/bladeslice.ogg' - attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") - - -/obj/item/melee/cultblade/attack(mob/living/target as mob, mob/living/carbon/human/user as mob) - if(!iscultist(user)) - user.Weaken(5) - user.unEquip(src, 1) - user.visible_message("A powerful force shoves [user] away from [target]!", \ - "\"You shouldn't play with sharp things. You'll poke someone's eye out.\"") - if(ishuman(user)) - var/mob/living/carbon/human/H = user - H.apply_damage(rand(force/2, force), BRUTE, pick("l_arm", "r_arm")) - else - user.adjustBruteLoss(rand(force/2,force)) - return - ..() - -/obj/item/melee/cultblade/pickup(mob/living/user) - . = ..() - if(!iscultist(user)) - to_chat(user, "\"I wouldn't advise that.\"") - to_chat(user, "An overwhelming sense of nausea overpowers you!") - user.Dizzy(120) - - if(HULK in user.mutations) - to_chat(user, "You can't seem to hold the blade properly!") - return FALSE - -/obj/item/melee/cultblade/dagger - name = "sacrificial dagger" - desc = "A strange dagger said to be used by sinister groups for \"preparing\" a corpse before sacrificing it to their dark gods." - icon = 'icons/obj/wizard.dmi' - icon_state = "render" - w_class = WEIGHT_CLASS_SMALL - force = 15 - throwforce = 25 - embed_chance = 75 - -/obj/item/melee/cultblade/dagger/attack(atom/target, mob/living/carbon/human/user) - ..() - if(ishuman(target)) - var/mob/living/carbon/human/H = target - if((H.stat != DEAD) && !(NO_BLOOD in H.dna.species.species_traits)) - H.bleed(50) - -/obj/item/restraints/legcuffs/bola/cult - name = "runed bola" - desc = "A strong bola, bound with dark magic. Throw it to trip and slow your victim." - icon = 'icons/obj/items.dmi' - icon_state = "bola_cult" - breakouttime = 45 - weaken = 1 - -/obj/item/clothing/head/hooded/culthood - name = "cult hood" - icon_state = "culthood" - desc = "A hood worn by the followers of a cult." - flags_inv = HIDEFACE - flags_cover = HEADCOVERSEYES - armor = list(melee = 30, bullet = 10, laser = 5, energy = 5, bomb = 0, bio = 0, rad = 0, fire = 10, acid = 10) - cold_protection = HEAD - min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT - - -/obj/item/clothing/head/hooded/culthood/alt - icon_state = "cult_hoodalt" - item_state = "cult_hoodalt" - - -/obj/item/clothing/suit/hooded/cultrobes - name = "cult robes" - desc = "A set of armored robes worn by the followers of a cult." - icon_state = "cultrobes" - item_state = "cultrobes" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - hoodtype = /obj/item/clothing/head/hooded/culthood - allowed = list(/obj/item/tome,/obj/item/melee/cultblade) - armor = list("melee" = 40, "bullet" = 30, "laser" = 40, "energy" = 20, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 10, "acid" = 10) - flags_inv = HIDEJUMPSUIT - -/obj/item/clothing/suit/hooded/cultrobes/alt - icon_state = "cultrobesalt" - item_state = "cultrobesalt" - hoodtype = /obj/item/clothing/head/hooded/culthood/alt - -/obj/item/clothing/head/magus - name = "magus helm" - icon_state = "magus" - item_state = "magus" - desc = "A helm worn by the followers of Nar-Sie." - flags = BLOCKHAIR - flags_inv = HIDEFACE - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - armor = list("melee" = 50, "bullet" = 30, "laser" = 50, "energy" = 20, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 10, "acid" = 10) - -/obj/item/clothing/suit/magusred - name = "magus robes" - desc = "A set of armored robes worn by the followers of Nar-Sie" - icon_state = "magusred" - item_state = "magusred" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - allowed = list(/obj/item/tome,/obj/item/melee/cultblade) - armor = list("melee" = 50, "bullet" = 30, "laser" = 50, "energy" = 20, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 10, "acid" = 10) - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT - -/obj/item/clothing/head/helmet/space/cult - name = "cult helmet" - desc = "A space worthy helmet used by the followers of a cult." - icon_state = "cult_helmet" - item_state = "cult_helmet" - armor = list("melee" = 70, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 40, "acid" = 75) - -/obj/item/clothing/suit/space/cult - name = "cult armor" - icon_state = "cult_armour" - item_state = "cult_armour" - desc = "A bulky suit of armor, bristling with spikes. It looks space proof." - w_class = WEIGHT_CLASS_NORMAL - allowed = list(/obj/item/tome,/obj/item/melee/cultblade,/obj/item/tank) - slowdown = 1 - armor = list("melee" = 70, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 40, "acid" = 75) - -/obj/item/clothing/suit/hooded/cultrobes/cult_shield - name = "empowered cultist robe" - desc = "Empowered garb which creates a powerful shield around the user." - icon_state = "cult_armour" - item_state = "cult_armour" - w_class = WEIGHT_CLASS_BULKY - armor = list("melee" = 50, "bullet" = 40, "laser" = 50, "energy" = 30, "bomb" = 50, "bio" = 30, "rad" = 30, "fire" = 50, "acid" = 60) - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - allowed = list(/obj/item/tome,/obj/item/melee/cultblade) - var/current_charges = 3 - hoodtype = /obj/item/clothing/head/hooded/cult_hoodie - -/obj/item/clothing/head/hooded/cult_hoodie - name = "empowered cultist robe" - desc = "Empowered garb which creates a powerful shield around the user." - icon_state = "cult_hoodalt" - armor = list("melee" = 40, "bullet" = 30, "laser" = 40,"energy" = 20, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 10, "acid" = 10) - body_parts_covered = HEAD - flags_inv = HIDEFACE - flags_cover = HEADCOVERSEYES - -/obj/item/clothing/suit/hooded/cultrobes/cult_shield/equipped(mob/living/user, slot) - ..() - if(!iscultist(user)) - to_chat(user, "\"I wouldn't advise that.\"") - to_chat(user, "An overwhelming sense of nausea overpowers you!") - user.unEquip(src, 1) - user.Dizzy(30) - user.Weaken(5) - -/obj/item/clothing/suit/hooded/cultrobes/cult_shield/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(current_charges) - owner.visible_message("\The [attack_text] is deflected in a burst of blood-red sparks!") - current_charges-- - new /obj/effect/temp_visual/cult/sparks(get_turf(owner)) - if(!current_charges) - owner.visible_message("The runed shield around [owner] suddenly disappears!") - owner.update_inv_wear_suit() - return 1 - return 0 - -/obj/item/clothing/suit/hooded/cultrobes/berserker - name = "flagellant's robes" - desc = "Blood-soaked robes infused with dark magic; allows the user to move at inhuman speeds, but at the cost of increased damage." - icon_state = "hardsuit-berserker" - item_state = "hardsuit-berserker" - flags_inv = HIDEJUMPSUIT - allowed = list(/obj/item/tome,/obj/item/melee/cultblade) - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - armor = list("melee" = -45, "bullet" = -45, "laser" = -45,"energy" = -45, "bomb" = -45, "bio" = -45, "rad" = -45, "fire" = 0, "acid" = 0) - slowdown = -1 - hoodtype = /obj/item/clothing/head/hooded/berserkerhood - - -/obj/item/clothing/head/hooded/berserkerhood - name = "flagellant's robes" - desc = "Blood-soaked garb infused with dark magic; allows the user to move at inhuman speeds, but at the cost of increased damage." - icon_state = "culthood" - flags_inv = HIDEFACE - flags_cover = HEADCOVERSEYES - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - -/obj/item/whetstone/cult - name = "eldritch whetstone" - desc = "A block, empowered by dark magic. Sharp weapons will be enhanced when used on the stone." - icon_state = "cult_sharpener" - used = 0 - increment = 5 - max = 40 - prefix = "darkened" - claw_damage_increase = 4 - -/obj/item/whetstone/cult/update_icon() - icon_state = "cult_sharpener[used ? "_used" : ""]" - -/obj/item/reagent_containers/food/drinks/bottle/unholywater - name = "flask of unholy water" - desc = "Toxic to nonbelievers; this water renews and reinvigorates the faithful of a cult." - icon_state = "holyflask" - color = "#333333" - list_reagents = list("unholywater" = 40) - -/obj/item/clothing/glasses/night/cultblind - desc = "May the master guide you through the darkness and shield you from the light." - name = "zealot's blindfold" - icon_state = "blindfold" - item_state = "blindfold" - see_in_dark = 8 - flash_protect = 1 - -/obj/item/clothing/glasses/night/cultblind/equipped(mob/user, slot) - ..() - if(!iscultist(user)) - to_chat(user, "\"You want to be blind, do you?\"") - user.unEquip(src, 1) - user.Dizzy(30) - user.Weaken(5) - user.EyeBlind(30) - -/obj/item/shuttle_curse - name = "cursed orb" - desc = "You peer within this smokey orb and glimpse terrible fates befalling the escape shuttle." - icon = 'icons/obj/projectiles.dmi' - icon_state ="bluespace" - color = "#ff0000" - var/global/curselimit = 0 - -/obj/item/shuttle_curse/attack_self(mob/user) - if(!iscultist(user)) - user.unEquip(src, 1) - user.Weaken(5) - to_chat(user, "A powerful force shoves you away from [src]!") - return - if(curselimit > 1) - to_chat(user, "We have exhausted our ability to curse the shuttle.") - return - if(locate(/obj/singularity/narsie) in GLOB.poi_list || locate(/mob/living/simple_animal/slaughter/cult) in GLOB.mob_list) - to_chat(user, "Nar-Sie or his avatars are already on this plane, there is no delaying the end of all things.") - return - - if(SSshuttle.emergency.mode == SHUTTLE_CALL) - var/cursetime = 1800 - var/timer = SSshuttle.emergency.timeLeft(1) + cursetime - SSshuttle.emergency.setTimer(timer) - to_chat(user,"You shatter the orb! A dark essence spirals into the air, then disappears.") - playsound(user.loc, 'sound/effects/glassbr1.ogg', 50, 1) - curselimit++ - qdel(src) - sleep(20) - var/global/list/curses - if(!curses) - curses = list("A fuel technician just slit his own throat and begged for death. The shuttle will be delayed by two minutes.", - "The shuttle's navigation programming was replaced by a file containing two words, IT COMES. The shuttle will be delayed by two minutes.", - "The shuttle's custodian tore out his guts and began painting strange shapes on the floor. The shuttle will be delayed by two minutes.", - "A shuttle engineer began screaming 'DEATH IS NOT THE END' and ripped out wires until an arc flash seared off her flesh. The shuttle will be delayed by two minutes.", - "A shuttle inspector started laughing madly over the radio and then threw herself into an engine turbine. The shuttle will be delayed by two minutes.", - "The shuttle dispatcher was found dead with bloody symbols carved into their flesh. The shuttle will be delayed by two minutes.", - "Steve repeatedly touched a lightbulb until his hands fell off. The shuttle will be delayed by two minutes.") - var/message = pick(curses) - command_announcement.Announce("[message]", "System Failure", 'sound/misc/notice1.ogg') - -/obj/item/cult_shift - name = "veil shifter" - desc = "This relic teleports you forward a medium distance." - icon = 'icons/obj/cult.dmi' - icon_state ="shifter" - var/uses = 4 - -/obj/item/cult_shift/examine(mob/user) - . = ..() - if(uses) - . += "It has [uses] uses remaining." - else - . += "It seems drained." - -/obj/item/cult_shift/proc/handle_teleport_grab(turf/T, mob/user) - var/mob/living/carbon/C = user - if(C.pulling) - var/atom/movable/pulled = C.pulling - pulled.forceMove(T) - . = pulled - -/obj/item/cult_shift/attack_self(mob/user) - if(!uses || !iscarbon(user)) - to_chat(user, "\The [src] is dull and unmoving in your hands.") - return - if(!iscultist(user)) - user.unEquip(src, 1) - step(src, pick(alldirs)) - to_chat(user, "\The [src] flickers out of your hands, too eager to move!") - return - - var/outer_tele_radius = 9 - - var/mob/living/carbon/C = user - var/turf/mobloc = get_turf(C) - var/list/turfs = new/list() - for(var/turf/T in range(user, outer_tele_radius)) - if(!is_teleport_allowed(T.z)) - continue - if(get_dir(C, T) != C.dir) - continue - if(T == mobloc) - continue - if(istype(T, /turf/space)) - continue - if(T.x > world.maxx-outer_tele_radius || T.x < outer_tele_radius) - continue //putting them at the edge is dumb - if(T.y > world.maxy-outer_tele_radius || T.y < outer_tele_radius) - continue - - turfs += T - - if(turfs) - uses-- - var/turf/destination = pick(turfs) - if(uses <= 0) - icon_state ="shifter_drained" - playsound(mobloc, "sparks", 50, 1) - new /obj/effect/temp_visual/dir_setting/cult/phase/out(mobloc, C.dir) - - var/atom/movable/pulled = handle_teleport_grab(destination, C) - C.forceMove(destination) - if(pulled) - C.start_pulling(pulled) //forcemove resets pulls, so we need to re-pull - - new /obj/effect/temp_visual/dir_setting/cult/phase(destination, C.dir) - playsound(destination, 'sound/effects/phasein.ogg', 25, 1) - playsound(destination, "sparks", 50, 1) - - else - to_chat(C, "The veil cannot be torn here!") - -/obj/item/melee/cultblade/ghost - name = "eldritch sword" - force = 15 - flags = NODROP | DROPDEL - -/obj/item/clothing/head/hooded/culthood/alt/ghost - flags = NODROP | DROPDEL - -/obj/item/clothing/suit/cultrobesghost - name = "ghostly cult robes" - desc = "A set of ethreal armored robes worn by the undead followers of a cult." - icon_state = "cultrobesalt" - item_state = "cultrobesalt" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - allowed = list(/obj/item/tome,/obj/item/melee/cultblade) - armor = list(melee = 50, bullet = 30, laser = 50, energy = 20, bomb = 25, bio = 10, rad = 0, fire = 10, acid = 10) - flags_inv = HIDEJUMPSUIT - - flags = NODROP | DROPDEL - - -/obj/item/clothing/shoes/cult/ghost - flags = NODROP | DROPDEL - -/datum/outfit/ghost_cultist - name = "Cultist Ghost" - - uniform = /obj/item/clothing/under/color/black - suit = /obj/item/clothing/suit/cultrobesghost - shoes = /obj/item/clothing/shoes/cult/ghost - head = /obj/item/clothing/head/hooded/culthood/alt/ghost - r_hand = /obj/item/melee/cultblade/ghost \ No newline at end of file +/obj/item/melee/cultblade + name = "Cult Blade" + desc = "An arcane weapon wielded by the followers of a cult." + icon_state = "cultblade" + item_state = "cultblade" + w_class = WEIGHT_CLASS_BULKY + force = 30 + throwforce = 10 + sharp = 1 + hitsound = 'sound/weapons/bladeslice.ogg' + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + + +/obj/item/melee/cultblade/attack(mob/living/target as mob, mob/living/carbon/human/user as mob) + if(!iscultist(user)) + user.Weaken(5) + user.unEquip(src, 1) + user.visible_message("A powerful force shoves [user] away from [target]!", \ + "\"You shouldn't play with sharp things. You'll poke someone's eye out.\"") + if(ishuman(user)) + var/mob/living/carbon/human/H = user + H.apply_damage(rand(force/2, force), BRUTE, pick("l_arm", "r_arm")) + else + user.adjustBruteLoss(rand(force/2,force)) + return + ..() + +/obj/item/melee/cultblade/pickup(mob/living/user) + . = ..() + if(!iscultist(user)) + to_chat(user, "\"I wouldn't advise that.\"") + to_chat(user, "An overwhelming sense of nausea overpowers you!") + user.Dizzy(120) + + if(HULK in user.mutations) + to_chat(user, "You can't seem to hold the blade properly!") + return FALSE + +/obj/item/melee/cultblade/dagger + name = "sacrificial dagger" + desc = "A strange dagger said to be used by sinister groups for \"preparing\" a corpse before sacrificing it to their dark gods." + icon = 'icons/obj/wizard.dmi' + icon_state = "render" + w_class = WEIGHT_CLASS_SMALL + force = 15 + throwforce = 25 + embed_chance = 75 + +/obj/item/melee/cultblade/dagger/attack(atom/target, mob/living/carbon/human/user) + ..() + if(ishuman(target)) + var/mob/living/carbon/human/H = target + if((H.stat != DEAD) && !(NO_BLOOD in H.dna.species.species_traits)) + H.bleed(50) + +/obj/item/restraints/legcuffs/bola/cult + name = "runed bola" + desc = "A strong bola, bound with dark magic. Throw it to trip and slow your victim." + icon = 'icons/obj/items.dmi' + icon_state = "bola_cult" + breakouttime = 45 + weaken = 1 + +/obj/item/clothing/head/hooded/culthood + name = "cult hood" + icon_state = "culthood" + desc = "A hood worn by the followers of a cult." + flags_inv = HIDEFACE + flags_cover = HEADCOVERSEYES + armor = list(melee = 30, bullet = 10, laser = 5, energy = 5, bomb = 0, bio = 0, rad = 0, fire = 10, acid = 10) + cold_protection = HEAD + min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT + + +/obj/item/clothing/head/hooded/culthood/alt + icon_state = "cult_hoodalt" + item_state = "cult_hoodalt" + + +/obj/item/clothing/suit/hooded/cultrobes + name = "cult robes" + desc = "A set of armored robes worn by the followers of a cult." + icon_state = "cultrobes" + item_state = "cultrobes" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + hoodtype = /obj/item/clothing/head/hooded/culthood + allowed = list(/obj/item/tome,/obj/item/melee/cultblade) + armor = list("melee" = 40, "bullet" = 30, "laser" = 40, "energy" = 20, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 10, "acid" = 10) + flags_inv = HIDEJUMPSUIT + +/obj/item/clothing/suit/hooded/cultrobes/alt + icon_state = "cultrobesalt" + item_state = "cultrobesalt" + hoodtype = /obj/item/clothing/head/hooded/culthood/alt + +/obj/item/clothing/head/magus + name = "magus helm" + icon_state = "magus" + item_state = "magus" + desc = "A helm worn by the followers of Nar-Sie." + flags = BLOCKHAIR + flags_inv = HIDEFACE + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + armor = list("melee" = 50, "bullet" = 30, "laser" = 50, "energy" = 20, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 10, "acid" = 10) + +/obj/item/clothing/suit/magusred + name = "magus robes" + desc = "A set of armored robes worn by the followers of Nar-Sie" + icon_state = "magusred" + item_state = "magusred" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + allowed = list(/obj/item/tome,/obj/item/melee/cultblade) + armor = list("melee" = 50, "bullet" = 30, "laser" = 50, "energy" = 20, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 10, "acid" = 10) + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT + +/obj/item/clothing/head/helmet/space/cult + name = "cult helmet" + desc = "A space worthy helmet used by the followers of a cult." + icon_state = "cult_helmet" + item_state = "cult_helmet" + armor = list("melee" = 70, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 40, "acid" = 75) + +/obj/item/clothing/suit/space/cult + name = "cult armor" + icon_state = "cult_armour" + item_state = "cult_armour" + desc = "A bulky suit of armor, bristling with spikes. It looks space proof." + w_class = WEIGHT_CLASS_NORMAL + allowed = list(/obj/item/tome,/obj/item/melee/cultblade,/obj/item/tank) + slowdown = 1 + armor = list("melee" = 70, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 40, "acid" = 75) + +/obj/item/clothing/suit/hooded/cultrobes/cult_shield + name = "empowered cultist robe" + desc = "Empowered garb which creates a powerful shield around the user." + icon_state = "cult_armour" + item_state = "cult_armour" + w_class = WEIGHT_CLASS_BULKY + armor = list("melee" = 50, "bullet" = 40, "laser" = 50, "energy" = 30, "bomb" = 50, "bio" = 30, "rad" = 30, "fire" = 50, "acid" = 60) + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + allowed = list(/obj/item/tome,/obj/item/melee/cultblade) + var/current_charges = 3 + hoodtype = /obj/item/clothing/head/hooded/cult_hoodie + +/obj/item/clothing/head/hooded/cult_hoodie + name = "empowered cultist robe" + desc = "Empowered garb which creates a powerful shield around the user." + icon_state = "cult_hoodalt" + armor = list("melee" = 40, "bullet" = 30, "laser" = 40,"energy" = 20, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 10, "acid" = 10) + body_parts_covered = HEAD + flags_inv = HIDEFACE + flags_cover = HEADCOVERSEYES + +/obj/item/clothing/suit/hooded/cultrobes/cult_shield/equipped(mob/living/user, slot) + ..() + if(!iscultist(user)) + to_chat(user, "\"I wouldn't advise that.\"") + to_chat(user, "An overwhelming sense of nausea overpowers you!") + user.unEquip(src, 1) + user.Dizzy(30) + user.Weaken(5) + +/obj/item/clothing/suit/hooded/cultrobes/cult_shield/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + if(current_charges) + owner.visible_message("\The [attack_text] is deflected in a burst of blood-red sparks!") + current_charges-- + new /obj/effect/temp_visual/cult/sparks(get_turf(owner)) + if(!current_charges) + owner.visible_message("The runed shield around [owner] suddenly disappears!") + owner.update_inv_wear_suit() + return 1 + return 0 + +/obj/item/clothing/suit/hooded/cultrobes/berserker + name = "flagellant's robes" + desc = "Blood-soaked robes infused with dark magic; allows the user to move at inhuman speeds, but at the cost of increased damage." + icon_state = "hardsuit-berserker" + item_state = "hardsuit-berserker" + flags_inv = HIDEJUMPSUIT + allowed = list(/obj/item/tome,/obj/item/melee/cultblade) + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + armor = list("melee" = -45, "bullet" = -45, "laser" = -45,"energy" = -45, "bomb" = -45, "bio" = -45, "rad" = -45, "fire" = 0, "acid" = 0) + slowdown = -1 + hoodtype = /obj/item/clothing/head/hooded/berserkerhood + + +/obj/item/clothing/head/hooded/berserkerhood + name = "flagellant's robes" + desc = "Blood-soaked garb infused with dark magic; allows the user to move at inhuman speeds, but at the cost of increased damage." + icon_state = "culthood" + flags_inv = HIDEFACE + flags_cover = HEADCOVERSEYES + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + +/obj/item/whetstone/cult + name = "eldritch whetstone" + desc = "A block, empowered by dark magic. Sharp weapons will be enhanced when used on the stone." + icon_state = "cult_sharpener" + used = 0 + increment = 5 + max = 40 + prefix = "darkened" + claw_damage_increase = 4 + +/obj/item/whetstone/cult/update_icon() + icon_state = "cult_sharpener[used ? "_used" : ""]" + +/obj/item/reagent_containers/food/drinks/bottle/unholywater + name = "flask of unholy water" + desc = "Toxic to nonbelievers; this water renews and reinvigorates the faithful of a cult." + icon_state = "holyflask" + color = "#333333" + list_reagents = list("unholywater" = 40) + +/obj/item/clothing/glasses/night/cultblind + desc = "May the master guide you through the darkness and shield you from the light." + name = "zealot's blindfold" + icon_state = "blindfold" + item_state = "blindfold" + see_in_dark = 8 + flash_protect = 1 + +/obj/item/clothing/glasses/night/cultblind/equipped(mob/user, slot) + ..() + if(!iscultist(user)) + to_chat(user, "\"You want to be blind, do you?\"") + user.unEquip(src, 1) + user.Dizzy(30) + user.Weaken(5) + user.EyeBlind(30) + +/obj/item/shuttle_curse + name = "cursed orb" + desc = "You peer within this smokey orb and glimpse terrible fates befalling the escape shuttle." + icon = 'icons/obj/projectiles.dmi' + icon_state ="bluespace" + color = "#ff0000" + var/global/curselimit = 0 + +/obj/item/shuttle_curse/attack_self(mob/user) + if(!iscultist(user)) + user.unEquip(src, 1) + user.Weaken(5) + to_chat(user, "A powerful force shoves you away from [src]!") + return + if(curselimit > 1) + to_chat(user, "We have exhausted our ability to curse the shuttle.") + return + if(locate(/obj/singularity/narsie) in GLOB.poi_list || locate(/mob/living/simple_animal/slaughter/cult) in GLOB.mob_list) + to_chat(user, "Nar-Sie or his avatars are already on this plane, there is no delaying the end of all things.") + return + + if(SSshuttle.emergency.mode == SHUTTLE_CALL) + var/cursetime = 1800 + var/timer = SSshuttle.emergency.timeLeft(1) + cursetime + SSshuttle.emergency.setTimer(timer) + to_chat(user,"You shatter the orb! A dark essence spirals into the air, then disappears.") + playsound(user.loc, 'sound/effects/glassbr1.ogg', 50, 1) + curselimit++ + qdel(src) + sleep(20) + var/global/list/curses + if(!curses) + curses = list("A fuel technician just slit his own throat and begged for death. The shuttle will be delayed by two minutes.", + "The shuttle's navigation programming was replaced by a file containing two words, IT COMES. The shuttle will be delayed by two minutes.", + "The shuttle's custodian tore out his guts and began painting strange shapes on the floor. The shuttle will be delayed by two minutes.", + "A shuttle engineer began screaming 'DEATH IS NOT THE END' and ripped out wires until an arc flash seared off her flesh. The shuttle will be delayed by two minutes.", + "A shuttle inspector started laughing madly over the radio and then threw herself into an engine turbine. The shuttle will be delayed by two minutes.", + "The shuttle dispatcher was found dead with bloody symbols carved into their flesh. The shuttle will be delayed by two minutes.", + "Steve repeatedly touched a lightbulb until his hands fell off. The shuttle will be delayed by two minutes.") + var/message = pick(curses) + command_announcement.Announce("[message]", "System Failure", 'sound/misc/notice1.ogg') + +/obj/item/cult_shift + name = "veil shifter" + desc = "This relic teleports you forward a medium distance." + icon = 'icons/obj/cult.dmi' + icon_state ="shifter" + var/uses = 4 + +/obj/item/cult_shift/examine(mob/user) + . = ..() + if(uses) + . += "It has [uses] uses remaining." + else + . += "It seems drained." + +/obj/item/cult_shift/proc/handle_teleport_grab(turf/T, mob/user) + var/mob/living/carbon/C = user + if(C.pulling) + var/atom/movable/pulled = C.pulling + pulled.forceMove(T) + . = pulled + +/obj/item/cult_shift/attack_self(mob/user) + if(!uses || !iscarbon(user)) + to_chat(user, "\The [src] is dull and unmoving in your hands.") + return + if(!iscultist(user)) + user.unEquip(src, 1) + step(src, pick(alldirs)) + to_chat(user, "\The [src] flickers out of your hands, too eager to move!") + return + + var/outer_tele_radius = 9 + + var/mob/living/carbon/C = user + var/turf/mobloc = get_turf(C) + var/list/turfs = new/list() + for(var/turf/T in range(user, outer_tele_radius)) + if(!is_teleport_allowed(T.z)) + continue + if(get_dir(C, T) != C.dir) + continue + if(T == mobloc) + continue + if(istype(T, /turf/space)) + continue + if(T.x > world.maxx-outer_tele_radius || T.x < outer_tele_radius) + continue //putting them at the edge is dumb + if(T.y > world.maxy-outer_tele_radius || T.y < outer_tele_radius) + continue + + turfs += T + + if(turfs) + uses-- + var/turf/destination = pick(turfs) + if(uses <= 0) + icon_state ="shifter_drained" + playsound(mobloc, "sparks", 50, 1) + new /obj/effect/temp_visual/dir_setting/cult/phase/out(mobloc, C.dir) + + var/atom/movable/pulled = handle_teleport_grab(destination, C) + C.forceMove(destination) + if(pulled) + C.start_pulling(pulled) //forcemove resets pulls, so we need to re-pull + + new /obj/effect/temp_visual/dir_setting/cult/phase(destination, C.dir) + playsound(destination, 'sound/effects/phasein.ogg', 25, 1) + playsound(destination, "sparks", 50, 1) + + else + to_chat(C, "The veil cannot be torn here!") + +/obj/item/melee/cultblade/ghost + name = "eldritch sword" + force = 15 + flags = NODROP | DROPDEL + +/obj/item/clothing/head/hooded/culthood/alt/ghost + flags = NODROP | DROPDEL + +/obj/item/clothing/suit/cultrobesghost + name = "ghostly cult robes" + desc = "A set of ethreal armored robes worn by the undead followers of a cult." + icon_state = "cultrobesalt" + item_state = "cultrobesalt" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + allowed = list(/obj/item/tome,/obj/item/melee/cultblade) + armor = list(melee = 50, bullet = 30, laser = 50, energy = 20, bomb = 25, bio = 10, rad = 0, fire = 10, acid = 10) + flags_inv = HIDEJUMPSUIT + + flags = NODROP | DROPDEL + + +/obj/item/clothing/shoes/cult/ghost + flags = NODROP | DROPDEL + +/datum/outfit/ghost_cultist + name = "Cultist Ghost" + + uniform = /obj/item/clothing/under/color/black + suit = /obj/item/clothing/suit/cultrobesghost + shoes = /obj/item/clothing/shoes/cult/ghost + head = /obj/item/clothing/head/hooded/culthood/alt/ghost + r_hand = /obj/item/melee/cultblade/ghost diff --git a/code/game/gamemodes/cult/cult_objectives.dm b/code/game/gamemodes/cult/cult_objectives.dm index 83ae3c2f1b7..caf607ce010 100644 --- a/code/game/gamemodes/cult/cult_objectives.dm +++ b/code/game/gamemodes/cult/cult_objectives.dm @@ -266,4 +266,4 @@ if(is_station_level(T.z)) //we're only interested in the remaining humans on the station survivors++ if(survivors < massacre_target) - bonus = 1 \ No newline at end of file + bonus = 1 diff --git a/code/game/gamemodes/cult/cult_structures.dm b/code/game/gamemodes/cult/cult_structures.dm index 3489a087ec0..de8846d292e 100644 --- a/code/game/gamemodes/cult/cult_structures.dm +++ b/code/game/gamemodes/cult/cult_structures.dm @@ -226,7 +226,7 @@ var/list/blacklisted_pylon_turfs = typecacheof(list( if(istype(T, /turf/simulated/floor)) T.ChangeTurf(/turf/simulated/floor/engine/cult) if(istype(T, /turf/simulated/wall)) - T.ChangeTurf(/turf/simulated/wall/cult) + T.ChangeTurf(/turf/simulated/wall/cult/artificer) else var/turf/simulated/floor/engine/cult/F = safepick(cultturfs) if(F) diff --git a/code/game/gamemodes/cult/ritual.dm b/code/game/gamemodes/cult/ritual.dm index a32f7219ca8..445050529af 100644 --- a/code/game/gamemodes/cult/ritual.dm +++ b/code/game/gamemodes/cult/ritual.dm @@ -1,320 +1,320 @@ -#define CULT_ELDERGOD "eldergod" -#define CULT_SLAUGHTER "slaughter" - -/obj/effect/rune/proc/fizzle() - if(istype(src,/obj/effect/rune)) - usr.say(pick("Hakkrutju gopoenjim.", "Nherasai pivroiashan.", "Firjji prhiv mazenhor.", "Tanah eh wakantahe.", "Obliyae na oraie.", "Miyf hon vnor'c.", "Wakabai hij fen juswix.")) - else - usr.whisper(pick("Hakkrutju gopoenjim.", "Nherasai pivroiashan.", "Firjji prhiv mazenhor.", "Tanah eh wakantahe.", "Obliyae na oraie.", "Miyf hon vnor'c.", "Wakabai hij fen juswix.")) - for (var/mob/V in viewers(src)) - V.show_message("The markings pulse with a small burst of light, then fall dark.", 3, "You hear a faint fizzle.", 2) - return - -/obj/effect/rune/proc/check_icon() - if(!SSticker.mode)//work around for maps with runes and cultdat is not loaded all the way - var/bits = make_bit_triplet() - icon = get_rune(bits) - else - icon = get_rune_cult(invocation) - -/obj/item/tome - name = "arcane tome" - desc = "An old, dusty tome with frayed edges and a sinister-looking cover." - icon_state ="tome" - throw_speed = 2 - throw_range = 5 - w_class = WEIGHT_CLASS_SMALL - var/scribereduct = 0 - var/canbypass = 0 //ADMINBUS - -/obj/item/tome/accursed - name = "accursed tome" - desc = "An arcane tome still empowered with a shadow of its former consecration." - scribereduct = 30 //faster because it's made by corrupting a bible - -/obj/item/tome/imbued //Admin-only tome, allows instant drawing of runes - name = "imbued arcane tome" - desc = "An arcane tome granted by the Geometer itself." - scribereduct = 50 - canbypass = 1 - -/obj/item/tome/New() - if(!SSticker.mode) - icon_state = "tome" - else - icon_state = SSticker.cultdat.tome_icon - ..() - -/obj/item/tome/examine(mob/user) - . = ..() - if(iscultist(user) || user.stat == DEAD) - . += "The scriptures of [SSticker.cultdat.entity_title3]. Allows the scribing of runes and access to the knowledge archives of the cult of [SSticker.cultdat.entity_name]." - . += "Striking another cultist with it will purge holy water from them." - . += "Striking a noncultist, however, will sear their flesh." - -/obj/item/tome/attack(mob/living/M, mob/living/user) - if(!istype(M)) - return - if(!iscultist(user)) - return ..() - if(iscultist(M)) - if(M.reagents && M.reagents.has_reagent("holywater")) //allows cultists to be rescued from the clutches of ordained religion - to_chat(user, "You remove the taint from [M].") - var/holy2unholy = M.reagents.get_reagent_amount("holywater") - M.reagents.del_reagent("holywater") - M.reagents.add_reagent("unholywater",holy2unholy) - add_attack_logs(user, M, "Hit with [src], removing the holy water from them") - return - M.take_organ_damage(0, 15) //Used to be a random between 5 and 20 - playsound(M, 'sound/weapons/sear.ogg', 50, 1) - M.visible_message("[user] strikes [M] with the arcane tome!", \ - "[user] strikes you with the tome, searing your flesh!") - flick("tome_attack", src) - user.do_attack_animation(M) - add_attack_logs(user, M, "Hit with [src]") - -/obj/item/tome/attack_self(mob/user) - if(!iscultist(user)) - to_chat(user, "[src] seems full of unintelligible shapes, scribbles, and notes. Is this some sort of joke?") - return - open_tome(user) - -/obj/item/tome/proc/open_tome(mob/user) - var/choice = alert(user,"You open the tome...",,"Scribe Rune","More Information","Cancel") - switch(choice) - if("More Information") - read_tome(user) - if("Scribe Rune") - scribe_rune(user) - if("Cancel") - return - -/obj/item/tome/proc/read_tome(mob/user) - var/text = list() - text += "
    Archives of [SSticker.cultdat.entity_title1]



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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

    " - - text += "Equipment:

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

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

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

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

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

    " - - text += "Constructs:

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

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

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

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

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

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



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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

    " + + text += "Equipment:

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

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

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

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

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

    " + + text += "Constructs:

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

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

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

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

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

    " + + var/text_string = jointext(text, null) + var/datum/browser/popup = new(user, "tome", "", 800, 600) + popup.set_content(text_string) + popup.open() + return 1 + +/obj/item/tome/proc/finale_runes_ok(mob/living/user, obj/effect/rune/rune_to_scribe) + var/datum/game_mode/cult/cult_mode = SSticker.mode + var/area/A = get_area(src) + if(GAMEMODE_IS_CULT) + if(!canbypass)//not an admin-tome, check things + if(!cult_mode.narsie_condition_cleared) + to_chat(user, "There is still more to do before unleashing [SSticker.cultdat.entity_name] power!") + return 0 + if(!cult_mode.eldergod) + to_chat(user, "\"I am already here. There is no need to try to summon me now.\"") + return 0 + if(cult_mode.demons_summoned) + to_chat(user, "\"We are already here. There is no need to try to summon us now.\"") + return 0 + if(!((CULT_ELDERGOD in cult_mode.objectives) || (CULT_SLAUGHTER in cult_mode.objectives))) + to_chat(user, "[SSticker.cultdat.entity_name]'s power does not wish to be unleashed!") + return 0 + if(!(A in summon_spots)) + to_chat(user, "[SSticker.cultdat.entity_name] can only be summoned where the veil is weak - in [english_list(summon_spots)]!") + return 0 + var/confirm_final = alert(user, "This is the FINAL step to summon your deities power, it is a long, painful ritual and the crew will be alerted to your presence", "Are you prepared for the final battle?", "My life for [SSticker.cultdat.entity_name]!", "No") + if(confirm_final == "No" || confirm_final == null) + to_chat(user, "You decide to prepare further before scribing the rune.") + return 0 + else + return 1 + else//the game mode is not cult..but we ARE a cultist...ALL ON THE ADMINBUS + var/confirm_final = alert(user, "This is the FINAL step to summon your deities power, it is a long, painful ritual and the crew will be alerted to your presence", "Are you prepared for the final battle?", "My life for [SSticker.cultdat.entity_name]!", "No") + if(confirm_final == "No" || confirm_final == null) + to_chat(user, "You decide to prepare further before scribing the rune.") + return 0 + else + return 1 + +/obj/item/tome/proc/scribe_rune(mob/living/user) + var/turf/runeturf = get_turf(user) + if(isspaceturf(runeturf)) + return + var/chosen_keyword + var/obj/effect/rune/rune_to_scribe + var/entered_rune_name + var/list/possible_runes = list() + var/list/shields = list() + var/area/A = get_area(src) + if(locate(/obj/effect/rune) in runeturf) + to_chat(user, "There is already a rune here.") + return + for(var/T in subtypesof(/obj/effect/rune) - /obj/effect/rune/malformed) + var/obj/effect/rune/R = T + if(initial(R.cultist_name)) + possible_runes.Add(initial(R.cultist_name)) //This is to allow the menu to let cultists select runes by name rather than by object path. I don't know a better way to do this + if(!possible_runes.len) + return + entered_rune_name = input(user, "Choose a rite to scribe.", "Sigils of Power") as null|anything in possible_runes + if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated()) + return + for(var/T in typesof(/obj/effect/rune)) + var/obj/effect/rune/R = T + if(initial(R.cultist_name) == entered_rune_name) + rune_to_scribe = R + if(initial(R.req_keyword)) + var/the_keyword = stripped_input(usr, "Please enter a keyword for the rune.", "Enter Keyword", "") + if(!the_keyword) + return + chosen_keyword = the_keyword + break + if(!rune_to_scribe) + return + runeturf = get_turf(user) //we may have moved. adjust as needed... + A = get_area(src) + if(locate(/obj/effect/rune) in runeturf) + to_chat(user, "There is already a rune here.") + return + if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated()) + return + if(ispath(rune_to_scribe, /obj/effect/rune/narsie) || ispath(rune_to_scribe, /obj/effect/rune/slaughter))//may need to change this - Fethas + if(finale_runes_ok(user,rune_to_scribe)) + A = get_area(src) + if(!(A in summon_spots)) // Check again to make sure they didn't move + to_chat(user, "The ritual can only begin where the veil is weak - in [english_list(summon_spots)]!") + return + command_announcement.Announce("Figments from an eldritch god are being summoned somewhere on the station from an unknown dimension. Disrupt the ritual at all costs!","Central Command Higher Dimensional Affairs", 'sound/AI/spanomalies.ogg') + for(var/B in spiral_range_turfs(1, user, 1)) + var/turf/T = B + var/obj/machinery/shield/N = new(T) + N.name = "Rune-Scriber's Shield" + N.desc = "A potent shield summoned by cultists to protect them while they prepare the final ritual" + N.icon_state = "shield-cult" + N.health = 60 + shields |= N + else + return//don't do shit + + var/mob/living/carbon/human/H = user + var/dam_zone = pick("head", "chest", "groin", "l_arm", "l_hand", "r_arm", "r_hand", "l_leg", "l_foot", "r_leg", "r_foot") + var/obj/item/organ/external/affecting = H.get_organ(ran_zone(dam_zone)) + user.visible_message("[user] cuts open [user.p_their()] [affecting] and begins writing in [user.p_their()] own blood!", "You slice open your [affecting] and begin drawing a sigil of [SSticker.cultdat.entity_title3].") + user.apply_damage(initial(rune_to_scribe.scribe_damage), BRUTE , affecting) + if(!do_after(user, initial(rune_to_scribe.scribe_delay)-scribereduct, target = get_turf(user))) + for(var/V in shields) + var/obj/machinery/shield/S = V + if(S && !QDELETED(S)) + qdel(S) + return + if(locate(/obj/effect/rune) in runeturf) + to_chat(user, "There is already a rune here.") + return + user.visible_message("[user] creates a strange circle in [user.p_their()] own blood.", \ + "You finish drawing the arcane markings of [SSticker.cultdat.entity_title3].") + for(var/V in shields) + var/obj/machinery/shield/S = V + if(S && !QDELETED(S)) + qdel(S) + var/obj/effect/rune/R = new rune_to_scribe(runeturf, chosen_keyword) + R.blood_DNA = list() + R.blood_DNA[H.dna.unique_enzymes] = H.dna.blood_type + R.add_hiddenprint(H) + to_chat(user, "The [lowertext(initial(rune_to_scribe.cultist_name))] rune [initial(rune_to_scribe.cultist_desc)]") diff --git a/code/game/gamemodes/cult/talisman.dm b/code/game/gamemodes/cult/talisman.dm index f7aaf2c1ebd..c0c4f7280f5 100644 --- a/code/game/gamemodes/cult/talisman.dm +++ b/code/game/gamemodes/cult/talisman.dm @@ -1,436 +1,436 @@ -/obj/item/paper/talisman - icon = 'icons/obj/paper.dmi' - icon_state = "paper_talisman" - var/cultist_name = "talisman" - var/cultist_desc = "A basic talisman. It serves no purpose." - var/invocation = "Naise meam!" - info = "


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


    " + var/uses = 1 + var/health_cost = 0 //The amount of health taken from the user when invoking the talisman + +/obj/item/paper/talisman/update_icon()//overriding this so the update_icon doesn't turn them into normal looking paper + SEND_SIGNAL(src, COMSIG_OBJ_UPDATE_ICON) + +/obj/item/paper/talisman/examine(mob/user) + . = ..() + if(iscultist(user) || user.stat == DEAD) + . += "Name: [cultist_name]" + . += "Effect: [cultist_desc]" + . += "Uses Remaining: [uses]" + else + . += "You see strange symbols on the paper. Are they supposed to mean something?" + +/obj/item/paper/talisman/attack_self(mob/living/user) + if(!iscultist(user)) + to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?") + return + if(invoke(user)) + uses-- + if(uses <= 0) + user.drop_item() + qdel(src) + +/obj/item/paper/talisman/proc/invoke(mob/living/user, successfuluse = 1) + . = successfuluse + if(successfuluse) //if the calling whatever says we succeed, do the fancy stuff + if(invocation) + user.whisper(invocation) + if(health_cost && iscarbon(user)) + var/mob/living/carbon/C = user + C.apply_damage(health_cost, BRUTE, pick("l_arm", "r_arm")) + +//Malformed Talisman: If something goes wrong. +/obj/item/paper/talisman/malformed + cultist_name = "malformed talisman" + cultist_desc = "A talisman with gibberish scrawlings. No good can come from invoking this." + invocation = "Ra'sha yoka!" + +/obj/item/paper/talisman/malformed/invoke(mob/living/user, successfuluse = 1) + to_chat(user, "You feel a pain in your head. [SSticker.cultdat.entity_title3] is displeased.") + if(iscarbon(user)) + var/mob/living/carbon/C = user + C.apply_damage(10, BRUTE, "head") + +//Supply Talisman: Has a few unique effects. Granted only to starter cultists. +/obj/item/paper/talisman/supply + cultist_name = "Supply Talisman" + icon_state = "supply" + cultist_desc = "A multi-use talisman that can create various objects. Intended to increase the cult's strength early on." + invocation = null + uses = 3 + +/obj/item/paper/talisman/supply/invoke(mob/living/user, successfuluse = 1) + var/dat = list() + dat += "There are [uses] bloody runes on the parchment.
    " + dat += "Please choose the chant to be imbued into the fabric of reality.
    " + dat += "
    " + dat += "N'ath reth sh'yro eth d'raggathnor! - Summons an arcane tome, used to scribe runes and communicate with other cultists.
    " + dat += "Bar'tea eas! - Provides 5 runed metal.
    " + dat += "Sas'so c'arta forbici! - Allows you to move to a selected teleportation rune.
    " + dat += "Ta'gh fara'qha fel d'amar det! - Allows you to destroy technology in a short range.
    " + dat += "Fuu ma'jin! - Allows you to stun a person by attacking them with the talisman.
    " + dat += "Kla'atu barada nikt'o! - Two use talisman, first use makes all nearby runes invisible, second use reveals nearby hidden runes.
    " + dat += "Kal'om neth! - Summons a soul stone, used to capture the spirits of dead or dying humans.
    " + dat += "Daa'ig osk! - Summons a construct shell for use with soulstone-captured souls. It is too large to carry on your person.
    " + var/datum/browser/popup = new(user, "talisman", "", 400, 400) + popup.set_content(jointext(dat, "")) + popup.open() + return 0 + +/obj/item/paper/talisman/supply/Topic(href, href_list) + if(src) + if(usr.stat || usr.restrained() || !in_range(src, usr)) + return + if(href_list["rune"]) + switch(href_list["rune"]) + if("newtome") + var/obj/item/tome/T = new(usr) + usr.put_in_hands(T) + if("metal") + if(istype(src, /obj/item/paper/talisman/supply/weak)) + usr.visible_message("Lesser supply talismans lack the strength to materialize runed metal!") + return + var/obj/item/stack/sheet/runed_metal/R = new(usr,5) + usr.put_in_hands(R) + if("teleport") + var/obj/item/paper/talisman/teleport/T = new(usr) + usr.put_in_hands(T) + if("emp") + var/obj/item/paper/talisman/emp/T = new(usr) + usr.put_in_hands(T) + if("runestun") + var/obj/item/paper/talisman/stun/T = new(usr) + usr.put_in_hands(T) + if("soulstone") + var/obj/item/soulstone/T = new(usr) + usr.put_in_hands(T) + if("construct") + new /obj/structure/constructshell(get_turf(usr)) + if("veiling") + var/obj/item/paper/talisman/true_sight/T = new(usr) + usr.put_in_hands(T) + uses-- + if(uses <= 0) + if(iscarbon(usr)) + var/mob/living/carbon/C = usr + C.drop_item() + visible_message("[src] crumbles to dust.") + qdel(src) + +/obj/item/paper/talisman/supply/weak + uses = 2 + +//Rite of Translocation: Same as rune +/obj/item/paper/talisman/teleport + cultist_name = "Talisman of Teleportation" + icon_state = "teleport" + cultist_desc = "A single-use talisman that will teleport a user to a random rune of the same keyword." + invocation = "Sas'so c'arta forbici!" + health_cost = 5 + +/obj/item/paper/talisman/teleport/invoke(mob/living/user, successfuluse = 1) + var/list/potential_runes = list() + var/list/teleportnames = list() + var/list/duplicaterunecount = list() + for(var/R in teleport_runes) + var/obj/effect/rune/teleport/T = R + var/resultkey = T.listkey + if(resultkey in teleportnames) + duplicaterunecount[resultkey]++ + resultkey = "[resultkey] ([duplicaterunecount[resultkey]])" + else + teleportnames.Add(resultkey) + duplicaterunecount[resultkey] = 1 + potential_runes[resultkey] = T + + if(!potential_runes.len) + to_chat(user, "There are no valid runes to teleport to!") + log_game("Teleport talisman failed - no other teleport runes") + return ..(user, 0) + + if(!is_level_reachable(user.z)) + to_chat(user, "You are not in the right dimension!") + log_game("Teleport talisman failed - user in away mission") + return ..(user, 0) + + var/input_rune_key = input(user, "Choose a rune to teleport to.", "Rune to Teleport to") as null|anything in potential_runes //we know what key they picked + var/obj/effect/rune/teleport/actual_selected_rune = potential_runes[input_rune_key] //what rune does that key correspond to? + if(!src || QDELETED(src) || !user || user.l_hand != src && user.r_hand != src || user.incapacitated() || !actual_selected_rune) + return ..(user, 0) + + user.visible_message("Dust flows from [user]'s hand, and [user.p_they()] disappear[user.p_s()] in a flash of red light!", \ + "You speak the words of the talisman and find yourself somewhere else!") + user.forceMove(get_turf(actual_selected_rune)) + return ..() + + +/obj/item/paper/talisman/summon_tome + cultist_name = "Talisman of Tome Summoning" + icon_state = "tome" + cultist_desc = "A one-use talisman that will call an untranslated tome from the archives of a cult." + invocation = "N'ath reth sh'yro eth d'raggathnor!" + health_cost = 1 + +/obj/item/paper/talisman/summon_tome/invoke(mob/living/user, successfuluse = 1) + . = ..() + user.visible_message("[user]'s hand glows red for a moment.", \ + "You speak the words of the talisman!") + new /obj/item/tome(get_turf(user)) + user.visible_message("A tome appears at [user]'s feet!", \ + "An arcane tome materializes at your feet.") + +/obj/item/paper/talisman/true_sight + cultist_name = "Talisman of Veiling" + icon_state = "veil" + cultist_desc = "A multi-use talisman that hides nearby runes. On its second use, will reveal nearby runes." + invocation = "Kla'atu barada nikt'o!" + health_cost = 1 + uses = 2 + var/revealing = FALSE //if it reveals or not + +/obj/item/paper/talisman/true_sight/invoke(mob/living/user, successfuluse = 1) + . = ..() + if(!revealing) + user.visible_message("Thin grey dust falls from [user]'s hand!", \ + "You speak the words of the talisman, hiding nearby runes.") + invocation = "Nikt'o barada kla'atu!" + revealing = TRUE + for(var/obj/effect/rune/R in range(3,user)) + R.talismanhide() + else + user.visible_message("A flash of light shines from [user]'s hand!", \ + "You speak the words of the talisman, revealing nearby runes.") + for(var/obj/effect/rune/R in range(3,user)) + R.talismanreveal() + +//Rite of False Truths: Same as rune +/obj/item/paper/talisman/make_runes_fake + cultist_name = "Talisman of Disguising" + icon_state = "disguising" + cultist_desc = "A talisman that will make nearby runes appear fake." + invocation = "By'o nar'nar!" + +/obj/item/paper/talisman/make_runes_fake/invoke(mob/living/user, successfuluse = 1) + . = ..() + user.visible_message("Dust flows from [user]s hand.", \ + "You speak the words of the talisman, making nearby runes appear fake.") + for(var/obj/effect/rune/R in orange(6,user)) + R.talismanfake() + R.desc = "A rune drawn in crayon." + + +//Rite of Disruption: Weaker than rune +/obj/item/paper/talisman/emp + cultist_name = "Talisman of Electromagnetic Pulse" + icon_state = "emp" + cultist_desc = "A talisman that will cause a moderately-sized electromagnetic pulse." + invocation = "Ta'gh fara'qha fel d'amar det!" + health_cost = 5 + +/obj/item/paper/talisman/emp/invoke(mob/living/user, successfuluse = 1) + . = ..() + user.visible_message("[user]'s hand flashes a bright blue!", \ + "You speak the words of the talisman, emitting an EMP blast.") + empulse(src, 4, 8, 1) + + +//Rite of Disorientation: Stuns and inhibit speech on a single target for quite some time +/obj/item/paper/talisman/stun + cultist_name = "Talisman of Stunning" + icon_state = "stunning" + cultist_desc = "A talisman that will stun and inhibit speech on a single target. To use, attack target directly." + invocation = "Dream sign:Evil sealing talisman!" + health_cost = 10 + +/obj/item/paper/talisman/stun/invoke(mob/living/user, successfuluse = 0) + if(successfuluse) //if we're forced to be successful(we normally aren't) then do the normal stuff + return ..() + if(iscultist(user)) + to_chat(user, "To use this talisman, attack the target directly.") + else + to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?") + return 0 + +/obj/item/paper/talisman/stun/attack(mob/living/target, mob/living/user, successfuluse = 1) + if(iscultist(user)) + invoke(user, 1) + user.visible_message("[user] holds up [src], which explodes in a flash of red light!", \ + "You stun [target] with the talisman!") + var/obj/item/nullrod/N = locate() in target + if(N) + target.visible_message("[target]'s holy weapon absorbs the talisman's light!", \ + "Your holy weapon absorbs the blinding light!") + else + add_attack_logs(user, target, "Stunned with a talisman") + target.Weaken(10) + target.Stun(10) + target.flash_eyes(1,1) + if(issilicon(target)) + var/mob/living/silicon/S = target + S.emp_act(1) + if(iscarbon(target)) + var/mob/living/carbon/C = target + C.AdjustSilence(5) + C.AdjustStuttering(15) + C.AdjustCultSlur(20) + C.Jitter(15) + user.drop_item() + qdel(src) + return + ..() + + +//Rite of Arming: Equips cultist armor on the user, where available +/obj/item/paper/talisman/armor + cultist_name = "Talisman of Arming" + icon_state = "arming" + cultist_desc = "A talisman that will equip the invoker with cultist equipment if there is a slot to equip it to." + invocation = "N'ath reth sh'yro eth draggathnor!" + +/obj/item/paper/talisman/armor/invoke(mob/living/user, successfuluse = 1) + . = ..() + var/mob/living/carbon/human/H = user + user.visible_message("Otherworldly armor suddenly appears on [user]!", \ + "You speak the words of the talisman, arming yourself!") + + H.equip_to_slot_or_del(new /obj/item/clothing/suit/hooded/cultrobes/alt(user), slot_wear_suit) + H.equip_to_slot_or_del(new /obj/item/storage/backpack/cultpack(user), slot_back) + H.equip_to_slot_or_del(new /obj/item/clothing/shoes/cult(user), slot_shoes) + H.put_in_hands(new /obj/item/melee/cultblade(user)) + H.put_in_hands(new /obj/item/restraints/legcuffs/bola/cult(user)) + +/obj/item/paper/talisman/armor/attack(mob/living/target, mob/living/user) + if(iscultist(user) && iscultist(target)) + user.drop_item() + qdel(src) + invoke(target) + return + ..() + + +//Talisman of Horrors: Breaks the mind of the victim with nightmarish hallucinations +/obj/item/paper/talisman/horror + cultist_name = "Talisman of Horrors" + icon_state = "horror" + cultist_desc = "A talisman that will break the mind of the victim with nightmarish hallucinations." + invocation = "Lo'Nab Na'Dm!" + +/obj/item/paper/talisman/horror/attack(mob/living/target, mob/living/user) + if(iscultist(user)) + to_chat(user, "You disturb [target] with visions of the end!") + if(iscarbon(target)) + var/mob/living/carbon/H = target + H.AdjustHallucinate(30) + qdel(src) + + +//Talisman of Fabrication: Creates a construct shell out of 25 metal sheets. +/obj/item/paper/talisman/construction + cultist_name = "Talisman of Construction" + icon_state = "construction" + cultist_desc = "Use this talisman on at least twenty-five metal sheets to create an empty construct shell or on plasteel to make runed metal" + invocation = "Ethra p'ni dedol!" + uses = 25 + +/obj/item/paper/talisman/construction/attack_self(mob/living/user) + if(iscultist(user)) + to_chat(user, "To use this talisman, place it upon a stack of metal sheets or plasteel sheets!.") + else + to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?") + + +/obj/item/paper/talisman/construction/attack(obj/M,mob/living/user) + if(iscultist(user)) + to_chat(user, "This talisman will only work on a stack of metal sheets or plasteel sheets!!") + log_game("Construct talisman failed - not a valid target") + +/obj/item/paper/talisman/construction/afterattack(obj/item/stack/sheet/target, mob/user, proximity_flag, click_parameters) + ..() + if(proximity_flag && iscultist(user)) + if(istype(target, /obj/item/stack/sheet/metal)) + var/turf/T = get_turf(target) + if(target.use(25)) + new /obj/structure/constructshell(T) + to_chat(user, "The talisman clings to the metal and twists it into a construct shell!") + user << sound('sound/magic/staff_chaos.ogg',0,1,25) + qdel(src) + return + if(istype(target, /obj/item/stack/sheet/plasteel)) + var/turf/T = get_turf(target) + var/quantity = min(target.amount, uses) + uses -= quantity + new /obj/item/stack/sheet/runed_metal(T,quantity) + target.use(quantity) + to_chat(user, "The talisman clings to the plasteel, transforming it into runed metal!") + user << sound('sound/magic/staff_chaos.ogg',0,1,25) + invoke(user, 1) + if(uses <= 0) + qdel(src) + else + to_chat(user, "The talisman must be used on metal or plasteel!") + +//Talisman of Shackling: Applies special cuffs directly from the talisman +/obj/item/paper/talisman/shackle + cultist_name = "Talisman of Shackling" + icon_state = "shackling" + cultist_desc = "Use this talisman on a victim to handcuff them with dark bindings." + invocation = "In'totum Lig'abis!" + uses = 4 + +/obj/item/paper/talisman/shackle/invoke(mob/living/user, successfuluse = 0) + if(successfuluse) //if we're forced to be successful(we normally aren't) then do the normal stuff + return ..() + if(iscultist(user)) + to_chat(user, "To use this talisman, attack the target directly.") + else + to_chat(user, "You see strange symbols on the paper. Are they supposed to mean something?") + return 0 + +/obj/item/paper/talisman/shackle/attack(mob/living/carbon/target, mob/living/user) + if(iscultist(user) && istype(target)) + if(target.stat == DEAD) + user.visible_message("This talisman's magic does not affect the dead!") + return + CuffAttack(target, user) + return + ..() + +/obj/item/paper/talisman/shackle/proc/CuffAttack(mob/living/carbon/C, mob/living/user) + if(!C.handcuffed) + invoke(user, 1) + playsound(loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2) + C.visible_message("[user] begins restraining [C] with dark magic!", \ + "[user] begins shaping a dark magic around your wrists!") + if(do_mob(user, C, 30)) + if(!C.handcuffed) + C.handcuffed = new /obj/item/restraints/handcuffs/energy/cult/used(C) + C.update_handcuffed() + to_chat(user, "You shackle [C].") + add_attack_logs(user, C, "Handcuffed (shackle talisman)") + uses-- + else + to_chat(user, "[C] is already bound.") + else + to_chat(user, "You fail to shackle [C].") + else + to_chat(user, "[C] is already bound.") + if(uses <= 0) + user.drop_item() + qdel(src) + return + +/obj/item/restraints/handcuffs/energy/cult //For the talisman of shackling + name = "cult shackles" + desc = "Shackles that bind the wrists with sinister magic." + trashtype = /obj/item/restraints/handcuffs/energy/used + origin_tech = "materials=2;magnets=5" + +/obj/item/restraints/handcuffs/energy/used + desc = "energy discharge" + flags = DROPDEL + +/obj/item/restraints/handcuffs/energy/cult/used/dropped(mob/user) + user.visible_message("[user]'s shackles shatter in a discharge of dark magic!", \ + "Your [src] shatters in a discharge of dark magic!") + qdel(src) + . = ..() diff --git a/code/game/gamemodes/devil/devilinfo.dm b/code/game/gamemodes/devil/devilinfo.dm index 89d997831a3..abd90172ee9 100644 --- a/code/game/gamemodes/devil/devilinfo.dm +++ b/code/game/gamemodes/devil/devilinfo.dm @@ -555,5 +555,5 @@ var/global/list/lawlorify = list ( W.rank = W.assignment W.age = H.age W.sex = capitalize(H.gender) - W.access = list(access_maint_tunnels, access_syndicate, access_external_airlocks) + W.access = list(ACCESS_MAINT_TUNNELS, ACCESS_SYNDICATE, ACCESS_EXTERNAL_AIRLOCKS) W.photo = get_id_photo(H) diff --git a/code/game/gamemodes/extended/extended.dm b/code/game/gamemodes/extended/extended.dm index a5dcef8d4e7..52edccc9e73 100644 --- a/code/game/gamemodes/extended/extended.dm +++ b/code/game/gamemodes/extended/extended.dm @@ -1,14 +1,14 @@ -/datum/game_mode/extended - name = "extended" - config_tag = "extended" - required_players = 0 - -/datum/game_mode/announce() - to_chat(world, "The current game mode is - Extended Role-Playing!") - to_chat(world, "Just have fun and role-play!") - -/datum/game_mode/extended/pre_setup() - return 1 - -/datum/game_mode/extended/post_setup() - ..() \ No newline at end of file +/datum/game_mode/extended + name = "extended" + config_tag = "extended" + required_players = 0 + +/datum/game_mode/announce() + to_chat(world, "The current game mode is - Extended Role-Playing!") + to_chat(world, "Just have fun and role-play!") + +/datum/game_mode/extended/pre_setup() + return 1 + +/datum/game_mode/extended/post_setup() + ..() diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index e7fb9b4969f..258d6ea20c6 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -1,522 +1,522 @@ -/* - * GAMEMODES (by Rastaf0) - * - * In the new mode system all special roles are fully supported. - * You can have proper wizards/traitors/changelings/cultists during any mode. - * Only two things really depends on gamemode: - * 1. Starting roles, equipment and preparations - * 2. Conditions of finishing the round. - * - */ - - -/datum/game_mode - var/name = "invalid" - var/config_tag = null - var/intercept_hacked = 0 - var/votable = 1 - var/probability = 0 - var/station_was_nuked = 0 //see nuclearbomb.dm and malfunction.dm - var/explosion_in_progress = 0 //sit back and relax - var/list/datum/mind/modePlayer = new - var/list/restricted_jobs = list() // Jobs it doesn't make sense to be. I.E chaplain or AI cultist - var/list/protected_jobs = list() // Jobs that can't be traitors - var/list/protected_species = list() // Species that can't be traitors - var/required_players = 0 - var/required_enemies = 0 - var/recommended_enemies = 0 - var/newscaster_announcements = null - var/ert_disabled = 0 - var/uplink_welcome = "Syndicate Uplink Console:" - var/uplink_uses = 20 - - var/const/waittime_l = 600 //lower bound on time before intercept arrives (in tenths of seconds) - var/const/waittime_h = 1800 //upper bound on time before intercept arrives (in tenths of seconds) - var/list/player_draft_log = list() - var/list/datum/mind/xenos = list() - var/list/datum/mind/eventmiscs = list() - - var/list/datum/station_goal/station_goals = list() // A list of all station goals for this game mode - -/datum/game_mode/proc/announce() //to be calles when round starts - to_chat(world, "Notice: [src] did not define announce()") - - -///can_start() -///Checks to see if the game can be setup and ran with the current number of players or whatnot. -/datum/game_mode/proc/can_start() - var/playerC = 0 - for(var/mob/new_player/player in GLOB.player_list) - if((player.client)&&(player.ready)) - playerC++ - - if(!config.enable_gamemode_player_limit || (playerC >= required_players)) - return 1 - return 0 - -//pre_pre_setup() For when you really don't want certain jobs ingame. -/datum/game_mode/proc/pre_pre_setup() - - return 1 - -///pre_setup() -///Attempts to select players for special roles the mode might have. -/datum/game_mode/proc/pre_setup() - - return 1 - - -///post_setup() -///Everyone should now be on the station and have their normal gear. This is the place to give the special roles extra things -/datum/game_mode/proc/post_setup() - - spawn (ROUNDSTART_LOGOUT_REPORT_TIME) - display_roundstart_logout_report() - - feedback_set_details("round_start","[time2text(world.realtime)]") - if(SSticker && SSticker.mode) - feedback_set_details("game_mode","[SSticker.mode]") -// if(revdata) -// feedback_set_details("revision","[revdata.revision]") - feedback_set_details("server_ip","[world.internet_address]:[world.port]") - generate_station_goals() - start_state = new /datum/station_state() - start_state.count() - return 1 - -///process() -///Called by the gameticker -/datum/game_mode/process() - return 0 - -//Called by the gameticker -/datum/game_mode/proc/process_job_tasks() - var/obj/machinery/message_server/useMS = null - if(message_servers) - for(var/obj/machinery/message_server/MS in message_servers) - if(MS.active) - useMS = MS - break - for(var/mob/M in GLOB.player_list) - if(M.mind) - var/obj/item/pda/P=null - for(var/obj/item/pda/check_pda in PDAs) - if(check_pda.owner==M.name) - P=check_pda - break - var/count=0 - for(var/datum/job_objective/objective in M.mind.job_objectives) - count++ - var/msg="" - var/pay=0 - if(objective.per_unit && objective.units_compensated0) - if(M.mind.initial_account) - M.mind.initial_account.credit(pay, "Payment", "\[CLASSIFIED\] Terminal #[rand(111,333)]", "[command_name()] Payroll") - msg += "You have been sent the $[pay], as agreed." - else - msg += "However, we were unable to send you the $[pay] you're entitled." - if(useMS && P) - useMS.send_pda_message("[P.owner]", "[command_name()] Payroll", msg) - - var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger) - PM.notify("Message from [command_name()] (Payroll), \"[msg]\" (Unable to Reply)", 0) - break - -/datum/game_mode/proc/check_finished() //to be called by ticker - if((SSshuttle.emergency && SSshuttle.emergency.mode >= SHUTTLE_ENDGAME) || station_was_nuked) - return 1 - return 0 - -/datum/game_mode/proc/cleanup() //This is called when the round has ended but not the game, if any cleanup would be necessary in that case. - return - -/datum/game_mode/proc/declare_completion() - var/clients = 0 - var/surviving_humans = 0 - var/surviving_total = 0 - var/ghosts = 0 - var/escaped_humans = 0 - var/escaped_total = 0 - var/escaped_on_pod_1 = 0 - var/escaped_on_pod_2 = 0 - var/escaped_on_pod_3 = 0 - var/escaped_on_pod_5 = 0 - var/escaped_on_shuttle = 0 - - var/list/area/escape_locations = list(/area/shuttle/escape, /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, /area/shuttle/escape_pod3/centcom, /area/shuttle/escape_pod5/centcom) - - if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) //shuttle didn't get to centcom - escape_locations -= /area/shuttle/escape - - for(var/mob/M in GLOB.player_list) - if(M.client) - clients++ - if(ishuman(M)) - if(!M.stat) - surviving_humans++ - if(M.loc && M.loc.loc && M.loc.loc.type in escape_locations) - escaped_humans++ - if(!M.stat) - surviving_total++ - if(M.loc && M.loc.loc && M.loc.loc.type in escape_locations) - escaped_total++ - - if(M.loc && M.loc.loc && M.loc.loc.type == SSshuttle.emergency.areaInstance.type && SSshuttle.emergency.mode >= SHUTTLE_ENDGAME) - escaped_on_shuttle++ - - if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod1/centcom) - escaped_on_pod_1++ - if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod2/centcom) - escaped_on_pod_2++ - if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod3/centcom) - escaped_on_pod_3++ - if(M.loc && M.loc.loc && M.loc.loc.type == /area/shuttle/escape_pod5/centcom) - escaped_on_pod_5++ - - if(isobserver(M)) - ghosts++ - - if(clients > 0) - feedback_set("round_end_clients",clients) - if(ghosts > 0) - feedback_set("round_end_ghosts",ghosts) - if(surviving_humans > 0) - feedback_set("survived_human",surviving_humans) - if(surviving_total > 0) - feedback_set("survived_total",surviving_total) - if(escaped_humans > 0) - feedback_set("escaped_human",escaped_humans) - if(escaped_total > 0) - feedback_set("escaped_total",escaped_total) - if(escaped_on_shuttle > 0) - feedback_set("escaped_on_shuttle",escaped_on_shuttle) - if(escaped_on_pod_1 > 0) - feedback_set("escaped_on_pod_1",escaped_on_pod_1) - if(escaped_on_pod_2 > 0) - feedback_set("escaped_on_pod_2",escaped_on_pod_2) - if(escaped_on_pod_3 > 0) - feedback_set("escaped_on_pod_3",escaped_on_pod_3) - if(escaped_on_pod_5 > 0) - feedback_set("escaped_on_pod_5",escaped_on_pod_5) - - send2mainirc("A round of [src.name] has ended - [surviving_total] survivors, [ghosts] ghosts.") - return 0 - - -/datum/game_mode/proc/check_win() //universal trigger to be called at mob death, nuke explosion, etc. To be called from everywhere. - return 0 - -/datum/game_mode/proc/get_players_for_role(var/role, override_jobbans=0) - var/list/players = list() - var/list/candidates = list() - //var/list/drafted = list() - //var/datum/mind/applicant = null - - var/roletext = get_roletext(role) - - // Assemble a list of active players without jobbans. - for(var/mob/new_player/player in GLOB.player_list) - if(player.client && player.ready && player.has_valid_preferences()) - if(!jobban_isbanned(player, "Syndicate") && !jobban_isbanned(player, roletext)) - if(player_old_enough_antag(player.client,role)) - players += player - - // Shuffle the players list so that it becomes ping-independent. - players = shuffle(players) - - // Get a list of all the people who want to be the antagonist for this round, except those with incompatible species - for(var/mob/new_player/player in players) - if(!player.client.skip_antag) - if((role in player.client.prefs.be_special) && !(player.client.prefs.species in protected_species)) - player_draft_log += "[player.key] had [roletext] enabled, so we are drafting them." - candidates += player.mind - players -= player - - // If we don't have enough antags, draft people who voted for the round. - if(candidates.len < recommended_enemies) - for(var/key in SSvote.round_voters) - for(var/mob/new_player/player in players) - if(player.ckey == key) - player_draft_log += "[player.key] voted for this round, so we are drafting them." - candidates += player.mind - players -= player - break - - // Remove candidates who want to be antagonist but have a job that precludes it - if(restricted_jobs) - for(var/datum/mind/player in candidates) - for(var/job in restricted_jobs) - if(player.assigned_role == job) - candidates -= player - - - return candidates // Returns: The number of people who had the antagonist role set to yes, regardless of recomended_enemies, if that number is greater than recommended_enemies - // recommended_enemies if the number of people with that role set to yes is less than recomended_enemies, - // Less if there are not enough valid players in the game entirely to make recommended_enemies. - - -/datum/game_mode/proc/latespawn(var/mob) - -/* -/datum/game_mode/proc/check_player_role_pref(var/role, var/mob/player) - if(player.preferences.be_special & role) - return 1 - return 0 -*/ - -/datum/game_mode/proc/num_players() - . = 0 - for(var/mob/new_player/P in GLOB.player_list) - if(P.client && P.ready) - .++ - -/datum/game_mode/proc/num_players_started() - . = 0 - for(var/mob/living/carbon/human/H in GLOB.player_list) - if(H.client) - .++ - -/////////////////////////////////// -//Keeps track of all living heads// -/////////////////////////////////// -/datum/game_mode/proc/get_living_heads() - . = list() - for(var/mob/living/carbon/human/player in GLOB.mob_list) - var/list/real_command_positions = command_positions.Copy() - "Nanotrasen Representative" - if(player.stat != DEAD && player.mind && (player.mind.assigned_role in real_command_positions)) - . |= player.mind - - -//////////////////////////// -//Keeps track of all heads// -//////////////////////////// -/datum/game_mode/proc/get_all_heads() - . = list() - for(var/mob/player in GLOB.mob_list) - var/list/real_command_positions = command_positions.Copy() - "Nanotrasen Representative" - if(player.mind && (player.mind.assigned_role in real_command_positions)) - . |= player.mind - -////////////////////////////////////////////// -//Keeps track of all living security members// -////////////////////////////////////////////// -/datum/game_mode/proc/get_living_sec() - . = list() - for(var/mob/living/carbon/human/player in GLOB.mob_list) - if(player.stat != DEAD && player.mind && (player.mind.assigned_role in security_positions)) - . |= player.mind - -//////////////////////////////////////// -//Keeps track of all security members// -//////////////////////////////////////// -/datum/game_mode/proc/get_all_sec() - . = list() - for(var/mob/living/carbon/human/player in GLOB.mob_list) - if(player.mind && (player.mind.assigned_role in security_positions)) - . |= player.mind - -/datum/game_mode/proc/check_antagonists_topic(href, href_list[]) - return 0 - -/datum/game_mode/New() - newscaster_announcements = pick(newscaster_standard_feeds) - -////////////////////////// -//Reports player logouts// -////////////////////////// -proc/display_roundstart_logout_report() - var/msg = "Roundstart logout report\n\n" - for(var/mob/living/L in GLOB.mob_list) - - if(L.ckey) - var/found = 0 - for(var/client/C in GLOB.clients) - if(C.ckey == L.ckey) - found = 1 - break - if(!found) - msg += "[L.name] ([L.ckey]), the [L.job] (Disconnected)\n" - - - if(L.ckey && L.client) - if(L.client.inactivity >= (ROUNDSTART_LOGOUT_REPORT_TIME / 2)) //Connected, but inactive (alt+tabbed or something) - msg += "[L.name] ([L.ckey]), the [L.job] (Connected, Inactive)\n" - continue //AFK client - if(L.stat) - if(L.suiciding) //Suicider - msg += "[L.name] ([L.ckey]), the [L.job] (Suicide)\n" - SSjobs.FreeRole(L.job) - message_admins("[key_name_admin(L)], the [L.job] has been freed due to (Early Round Suicide)\n") - continue //Disconnected client - if(L.stat == UNCONSCIOUS) - msg += "[L.name] ([L.ckey]), the [L.job] (Dying)\n" - continue //Unconscious - if(L.stat == DEAD) - msg += "[L.name] ([L.ckey]), the [L.job] (Dead)\n" - continue //Dead - - continue //Happy connected client - for(var/mob/dead/observer/D in GLOB.mob_list) - if(D.mind && (D.mind.original == L || D.mind.current == L)) - if(L.stat == DEAD) - if(L.suiciding) //Suicider - msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Suicide)\n" - continue //Disconnected client - else - msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Dead)\n" - continue //Dead mob, ghost abandoned - else - if(D.can_reenter_corpse) - msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (This shouldn't appear.)\n" - continue //Lolwhat - else - msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Ghosted)\n" - SSjobs.FreeRole(L.job) - message_admins("[key_name_admin(L)], the [L.job] has been freed due to (Early Round Ghosted While Alive)\n") - continue //Ghosted while alive - - - - for(var/mob/M in GLOB.mob_list) - if(check_rights(R_ADMIN, 0, M)) - to_chat(M, msg) - -//Announces objectives/generic antag text. -/proc/show_generic_antag_text(var/datum/mind/player) - if(player.current) - to_chat(player.current, "You are an antagonist! Within the rules, \ - try to act as an opposing force to the crew. Further RP and try to make sure \ - other players have fun! If you are confused or at a loss, always adminhelp, \ - and before taking extreme actions, please try to also contact the administration! \ - Think through your actions and make the roleplay immersive! Please remember all \ - rules aside from those without explicit exceptions apply to antagonists.") - -/proc/show_objectives(var/datum/mind/player) - if(!player || !player.current) return - - var/obj_count = 1 - to_chat(player.current, "Your current objectives:") - for(var/datum/objective/objective in player.objectives) - to_chat(player.current, "Objective #[obj_count]: [objective.explanation_text]") - obj_count++ - -/proc/get_roletext(var/role) - return role - -/proc/get_nuke_code() - var/nukecode = "ERROR" - for(var/obj/machinery/nuclearbomb/bomb in world) - if(bomb && bomb.r_code && is_station_level(bomb.z)) - nukecode = bomb.r_code - return nukecode - -/datum/game_mode/proc/replace_jobbanned_player(mob/living/M, role_type) - var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a [role_type]?", role_type, 0, 100) - var/mob/dead/observer/theghost = null - if(candidates.len) - theghost = pick(candidates) - to_chat(M, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!") - message_admins("[key_name_admin(theghost)] has taken control of ([key_name_admin(M)]) to replace a jobbanned player.") - M.ghostize() - M.key = theghost.key - else - message_admins("[M] ([M.key] has been converted into [role_type] with an active antagonist jobban for said role since no ghost has volunteered to take [M.p_their()] place.") - to_chat(M, "You have been converted into [role_type] with an active jobban. Any further violations of the rules on your part are likely to result in a permanent ban.") - -/proc/printplayer(datum/mind/ply, fleecheck) - var/jobtext = "" - if(ply.assigned_role) - jobtext = " the [ply.assigned_role]" - var/text = "[ply.key] was [ply.name][jobtext] and" - if(ply.current) - if(ply.current.stat == DEAD) - text += " died" - else - text += " survived" - if(fleecheck) - var/turf/T = get_turf(ply.current) - if(!T || !is_station_level(T.z)) - text += " while fleeing the station" - if(ply.current.real_name != ply.name) - text += " as [ply.current.real_name]" - else - text += " had [ply.p_their()] body destroyed" - return text - -/proc/printeventplayer(datum/mind/ply) - var/text = "[ply.key] was [ply.name]" - if(ply.special_role != SPECIAL_ROLE_EVENTMISC) - text += " the [ply.special_role]" - text += " and" - if(ply.current) - if(ply.current.stat == DEAD) - text += " died" - else - text += " survived" - else - text += " had [ply.p_their()] body destroyed" - return text - -/proc/printobjectives(datum/mind/ply) - var/list/objective_parts = list() - var/count = 1 - for(var/datum/objective/objective in ply.objectives) - if(objective.check_completion()) - objective_parts += "Objective #[count]: [objective.explanation_text] Success!" - else - objective_parts += "Objective #[count]: [objective.explanation_text] Fail." - count++ - return objective_parts.Join("
    ") - -/datum/game_mode/proc/generate_station_goals() - var/list/possible = list() - for(var/T in subtypesof(/datum/station_goal)) - var/datum/station_goal/G = T - if(config_tag in initial(G.gamemode_blacklist)) - continue - possible += T - var/goal_weights = 0 - while(possible.len && goal_weights < STATION_GOAL_BUDGET) - var/datum/station_goal/picked = pick_n_take(possible) - goal_weights += initial(picked.weight) - station_goals += new picked - - if(station_goals.len) - send_station_goals_message() - -/datum/game_mode/proc/send_station_goals_message() - var/message_text = "
    " - message_text += "

    [command_name()] Orders


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

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

    [command_name()] Orders


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

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

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

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

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

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

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

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

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

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

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

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

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

    We have received a report that a dangerous alien lifeform known only as \"[cname]\" may have infiltrated your crew. " + /* + src.text += "Our intelligence suggests a [prob_right_job]% chance that a [changeling_job] on board your station has been replaced by the alien. " + src.text += "Additionally, the report indicates a [prob_right_dude]% chance that [changeling_name] may have been in contact with the lifeform at a recent social gathering. " + */ + src.text += "These lifeforms are assosciated with the [orgname1] [orgname2] and may be attempting to acquire sensitive materials on their behalf. " + src.text += "Please take care not to alarm the crew, as [cname] may take advantage of a panic situation. Remember, they can be anybody, suspect everybody!" diff --git a/code/game/gamemodes/malfunction/Malf_Modules.dm b/code/game/gamemodes/malfunction/Malf_Modules.dm index eb54b52c225..9ca0025a0ab 100644 --- a/code/game/gamemodes/malfunction/Malf_Modules.dm +++ b/code/game/gamemodes/malfunction/Malf_Modules.dm @@ -1,757 +1,758 @@ -//The malf AI action subtype. All malf actions are subtypes of this. -/datum/action/innate/ai - name = "AI Action" - desc = "You aren't entirely sure what this does, but it's very beepy and boopy." - background_icon_state = "bg_tech_blue" - var/mob/living/silicon/ai/owner_AI //The owner AI, so we don't have to typecast every time - var/uses //If we have multiple uses of the same power - var/auto_use_uses = TRUE //If we automatically use up uses on each activation - var/cooldown_period //If applicable, the time in deciseconds we have to wait before using any more modules - -/datum/action/innate/ai/Grant(mob/living/L) - . = ..() - if(!isAI(owner)) - WARNING("AI action [name] attempted to grant itself to non-AI mob [L.real_name] ([L.key])!") - qdel(src) - else - owner_AI = owner - -/datum/action/innate/ai/IsAvailable() - . = ..() - if(owner_AI && owner_AI.malf_cooldown > world.time) - return - -/datum/action/innate/ai/Trigger() - . = ..() - if(auto_use_uses) - adjust_uses(-1) - if(cooldown_period) - owner_AI.malf_cooldown = world.time + cooldown_period - -/datum/action/innate/ai/proc/adjust_uses(amt, silent) - uses += amt - if(!silent && uses) - to_chat(owner, "[name] now has [uses] use[uses > 1 ? "s" : ""] remaining.") - if(!uses) - if(initial(uses) > 1) //no need to tell 'em if it was one-use anyway! - to_chat(owner, "[name] has run out of uses!") - qdel(src) - -//Framework for ranged abilities that can have different effects by left-clicking stuff. -/datum/action/innate/ai/ranged - name = "Ranged AI Action" - auto_use_uses = FALSE //This is so we can do the thing and disable/enable freely without having to constantly add uses - var/obj/effect/proc_holder/ranged_ai/linked_ability //The linked proc holder that contains the actual ability code - var/linked_ability_type //The path of our linked ability - -/datum/action/innate/ai/ranged/New() - if(!linked_ability_type) - WARNING("Ranged AI action [name] attempted to spawn without a linked ability!") - qdel(src) //uh oh! - return - linked_ability = new linked_ability_type() - linked_ability.attached_action = src - ..() - -/datum/action/innate/ai/ranged/adjust_uses(amt, silent) - uses += amt - if(!silent && uses) - to_chat(owner, "[name] now has [uses] use[uses > 1 ? "s" : ""] remaining.") - if(!uses) - if(initial(uses) > 1) //no need to tell 'em if it was one-use anyway! - to_chat(owner, "[name] has run out of uses!") - Remove(owner) - QDEL_IN(src, 100) //let any active timers on us finish up - -/datum/action/innate/ai/ranged/Destroy() - QDEL_NULL(linked_ability) - return ..() - -/datum/action/innate/ai/ranged/Activate() - linked_ability.toggle(owner) - return TRUE - -//The actual ranged proc holder. -/obj/effect/proc_holder/ranged_ai - var/enable_text = "Hello World!" //Appears when the user activates the ability - var/disable_text = "Goodbye Cruel World!" //Context clues! - var/datum/action/innate/ai/ranged/attached_action - -/obj/effect/proc_holder/ranged_ai/proc/toggle(mob/user) - if(active) - remove_ranged_ability(user, disable_text) - else - add_ranged_ability(user, enable_text) - -//The datum and interface for the malf unlock menu, which lets them choose actions to unlock. -/datum/module_picker - var/temp - var/processing_time = 50 - var/list/possible_modules - -/datum/module_picker/New() - possible_modules = list() - for(var/type in typesof(/datum/AI_Module)) - var/datum/AI_Module/AM = new type - if((AM.power_type && AM.power_type != /datum/action/innate/ai) || AM.upgrade) - possible_modules += AM - -/datum/module_picker/proc/remove_malf_verbs(mob/living/silicon/ai/AI) //Removes all malfunction-related abilities from the target AI. - for(var/datum/AI_Module/AM in possible_modules) - for(var/datum/action/A in AI.actions) - if(istype(A, initial(AM.power_type))) - qdel(A) - -/datum/module_picker/proc/use(user as mob) - var/dat - dat += {"Select use of processing time: (currently #[processing_time] left.)
    -
    - Install Module:
    - The number afterwards is the amount of processing time it consumes.
    "} - for(var/datum/AI_Module/large/module in possible_modules) - dat += "[module.module_name]\[?\] ([module.cost])
    " - for(var/datum/AI_Module/small/module in possible_modules) - dat += "[module.module_name]\[?\] ([module.cost])
    " - dat += "
    " - if(temp) - dat += "[temp]" - var/datum/browser/popup = new(user, "modpicker", "Malf Module Menu", 400, 500) - popup.set_content(dat) - popup.open() - return - -/datum/module_picker/Topic(href, href_list) - ..() - - if(!isAI(usr)) - return - var/mob/living/silicon/ai/A = usr - - if(A.stat == DEAD) - to_chat(A, "You are already dead!") - return - - for(var/datum/AI_Module/AM in possible_modules) - if (href_list[AM.mod_pick_name]) - - // Cost check - if(AM.cost > processing_time) - temp = "You cannot afford this module." - break - - var/datum/action/innate/ai/action = locate(AM.power_type) in A.actions - - // Give the power and take away the money. - if(AM.upgrade) //upgrade and upgrade() are separate, be careful! - AM.upgrade(A) - possible_modules -= AM - to_chat(A, AM.unlock_text) - A.playsound_local(A, AM.unlock_sound, 50, 0) - else - if(AM.power_type) - if(!action) //Unlocking for the first time - var/datum/action/AC = new AM.power_type - AC.Grant(A) - A.current_modules += new AM.type - temp = AM.description - if(AM.one_purchase) - possible_modules -= AM - if(AM.unlock_text) - to_chat(A, AM.unlock_text) - if(AM.unlock_sound) - A.playsound_local(A, AM.unlock_sound, 50, 0) - else //Adding uses to an existing module - action.uses += initial(action.uses) - action.desc = "[initial(action.desc)] It has [action.uses] use\s remaining." - action.UpdateButtonIcon() - temp = "Additional use[action.uses > 1 ? "s" : ""] added to [action.name]!" - processing_time -= AM.cost - - if(href_list["showdesc"]) - if(AM.mod_pick_name == href_list["showdesc"]) - temp = AM.description - use(usr) - -//The base module type, which holds info about each ability. -/datum/AI_Module - var/module_name - var/mod_pick_name - var/description = "" - var/engaged = 0 - var/cost = 5 - var/one_purchase = FALSE //If this module can only be purchased once. This always applies to upgrades, even if the variable is set to false. - var/power_type = /datum/action/innate/ai //If the module gives an active ability, use this. Mutually exclusive with upgrade. - var/upgrade //If the module gives a passive upgrade, use this. Mutually exclusive with power_type. - var/unlock_text = "Hello World!" //Text shown when an ability is unlocked - var/unlock_sound //Sound played when an ability is unlocked - var/uses - -/datum/AI_Module/proc/upgrade(mob/living/silicon/AI/AI) //Apply upgrades! - return - -/datum/AI_Module/large //Big, powerful stuff that can only be used once. - -/datum/AI_Module/small //Weak, usually localized stuff with multiple uses. - -//Doomsday Device: Starts the self-destruct timer. It can only be stopped by killing the AI completely. -/datum/AI_Module/large/nuke_station - module_name = "Doomsday Device" - mod_pick_name = "nukestation" - description = "Activate a weapon that will disintegrate all organic life on the station after a 450 second delay. Can only be used while on the station, will fail if your core is moved off station or destroyed." - cost = 130 - one_purchase = TRUE - power_type = /datum/action/innate/ai/nuke_station - unlock_text = "You slowly, carefully, establish a connection with the on-station self-destruct. You can now activate it at any time." - unlock_sound = 'sound/items/timer.ogg' - -/datum/action/innate/ai/nuke_station - name = "Doomsday Device" - desc = "Activates the doomsday device. This is not reversible." - button_icon_state = "doomsday_device" - auto_use_uses = FALSE - -/datum/action/innate/ai/nuke_station/Activate() - var/turf/T = get_turf(owner) - if(!istype(T) || !is_station_level(T.z)) - to_chat(owner, "You cannot activate the doomsday device while off-station!") - return - if(alert(owner, "Send arming signal? (true = arm, false = cancel)", "purge_all_life()", "confirm = TRUE;", "confirm = FALSE;") != "confirm = TRUE;") - return - if(active) - return //prevent the AI from activating an already active doomsday - active = TRUE - set_us_up_the_bomb() - -/datum/action/innate/ai/nuke_station/proc/set_us_up_the_bomb() - to_chat(owner_AI, "Nuclear device armed.") - event_announcement.Announce("Hostile runtimes detected in all station systems, please deactivate your AI to prevent possible damage to its morality core.", "Anomaly Alert", new_sound = 'sound/AI/aimalf.ogg') - set_security_level("delta") - owner_AI.nuking = TRUE - var/obj/machinery/doomsday_device/DOOM = new /obj/machinery/doomsday_device(owner_AI) - owner_AI.doomsday_device = DOOM - owner_AI.doomsday_device.start() - for(var/obj/item/pinpointer/point in GLOB.pinpointer_list) - for(var/mob/living/silicon/ai/A in ai_list) - if((A.stat != DEAD) && A.nuking) - point.the_disk = A //The pinpointer now tracks the AI core - qdel(src) - -/obj/machinery/doomsday_device - icon = 'icons/obj/machines/nuke_terminal.dmi' - name = "doomsday device" - icon_state = "nuclearbomb_base" - desc = "A weapon which disintegrates all organic life in a large area." - anchored = 1 - density = 1 - atom_say_verb = "blares" - var/timing = 0 - var/default_timer = 4500 - var/detonation_timer - var/announced = 0 - -/obj/machinery/doomsday_device/Destroy() - STOP_PROCESSING(SSfastprocess, src) - SSshuttle.emergencyNoEscape = 0 - if(SSshuttle.emergency.mode == SHUTTLE_STRANDED) - SSshuttle.emergency.mode = SHUTTLE_DOCKED - SSshuttle.emergency.timer = world.time - priority_announcement.Announce("Hostile environment resolved. You have 3 minutes to board the Emergency Shuttle.", "Priority Announcement", 'sound/AI/shuttledock.ogg') - return ..() - -/obj/machinery/doomsday_device/proc/start() - detonation_timer = world.time + default_timer - timing = 1 - START_PROCESSING(SSfastprocess, src) - SSshuttle.emergencyNoEscape = 1 - -/obj/machinery/doomsday_device/proc/seconds_remaining() - . = max(0, (round(detonation_timer - world.time) / 10)) - -/obj/machinery/doomsday_device/process() - var/turf/T = get_turf(src) - if(!T || !is_station_level(T.z)) - minor_announcement.Announce("DOOMSDAY DEVICE OUT OF STATION RANGE, ABORTING", "ERROR ER0RR $R0RRO$!R41.%%!!(%$^^__+ @#F0E4", 'sound/misc/notice1.ogg') - SSshuttle.emergencyNoEscape = 0 - if(SSshuttle.emergency.mode == SHUTTLE_STRANDED) - SSshuttle.emergency.mode = SHUTTLE_DOCKED - SSshuttle.emergency.timer = world.time - priority_announcement.Announce("Hostile environment resolved. You have 3 minutes to board the Emergency Shuttle.", "Priority Announcement", 'sound/AI/shuttledock.ogg') - qdel(src) - if(!timing) - STOP_PROCESSING(SSfastprocess, src) - return - var/sec_left = seconds_remaining() - if(sec_left <= 0) - timing = 0 - detonate(T.z) - qdel(src) - else - if(!(sec_left % 60) && !announced) - var/message = "[sec_left] SECONDS UNTIL DOOMSDAY DEVICE ACTIVATION!" - minor_announcement.Announce(message, "ERROR ER0RR $R0RRO$!R41.%%!!(%$^^__+ @#F0E4", 'sound/misc/notice1.ogg') - announced = 10 - announced = max(0, announced-1) - -/obj/machinery/doomsday_device/proc/detonate(z_level = 1) - for(var/mob/M in GLOB.player_list) - M << 'sound/machines/alarm.ogg' - sleep(100) - for(var/mob/living/L in GLOB.mob_list) - var/turf/T = get_turf(L) - if(!T || T.z != z_level) - continue - if(issilicon(L)) - continue - to_chat(L, "The blast wave from [src] tears you atom from atom!") - L.dust() - to_chat(world, "The AI cleansed the station of life with the doomsday device!") - SSticker.force_ending = 1 - -//AI Turret Upgrade: Increases the health and damage of all turrets. -/datum/AI_Module/large/upgrade_turrets - module_name = "AI Turret Upgrade" - mod_pick_name = "turret" - description = "Improves the power and health of all AI turrets. This effect is permanent." - cost = 30 - upgrade = TRUE - unlock_text = "You establish a power diversion to your turrets, upgrading their health and damage." - unlock_sound = 'sound/items/rped.ogg' - -/datum/AI_Module/large/upgrade_turrets/upgrade(mob/living/silicon/AI/AI) - for(var/obj/machinery/porta_turret/turret in GLOB.machines) - var/turf/T = get_turf(turret) - if(is_station_level(T.z)) - turret.health += 30 - turret.eprojectile = /obj/item/projectile/beam/laser/heavylaser //Once you see it, you will know what it means to FEAR. - turret.eshot_sound = 'sound/weapons/lasercannonfire.ogg' - -//Hostile Station Lockdown: Locks, bolts, and electrifies every airlock on the station. After 90 seconds, the doors reset. -/datum/AI_Module/large/lockdown - module_name = "Hostile Station Lockdown" - mod_pick_name = "lockdown" - description = "Overload the airlock, blast door and fire control networks, locking them down. Caution! This command also electrifies all airlocks. The networks will automatically reset after 90 seconds, briefly \ - opening all doors on the station." - cost = 30 - one_purchase = TRUE - power_type = /datum/action/innate/ai/lockdown - unlock_text = "You upload a sleeper trojan into the door control systems. You can send a signal to set it off at any time." - -/datum/action/innate/ai/lockdown - name = "Lockdown" - desc = "Closes, bolts, and depowers every airlock, firelock, and blast door on the station. After 90 seconds, they will reset themselves." - button_icon_state = "lockdown" - uses = 1 - -/datum/action/innate/ai/lockdown/Activate() - for(var/obj/machinery/door/D in GLOB.airlocks) - if(!is_station_level(D.z)) - continue - INVOKE_ASYNC(D, /obj/machinery/door.proc/hostile_lockdown, owner) - addtimer(CALLBACK(D, /obj/machinery/door.proc/disable_lockdown), 900) - - post_status("alert", "lockdown") - - minor_announcement.Announce("Hostile runtime detected in door controllers. Isolation lockdown protocols are now in effect. Please remain calm.", "Network Alert") - to_chat(owner, "Lockdown Initiated. Network reset in 90 seconds.") - spawn(900) - minor_announcement.Announce("Automatic system reboot complete. Have a secure day.","Network reset:") - -//Destroy RCDs: Detonates all non-cyborg RCDs on the station. -/datum/AI_Module/large/destroy_rcd - module_name = "Destroy RCDs" - mod_pick_name = "rcd" - description = "Send a specialised pulse to detonate all hand-held and exosuit Rapid Construction Devices on the station." - cost = 25 - one_purchase = TRUE - power_type = /datum/action/innate/ai/destroy_rcds - unlock_text = "After some improvisation, you rig your onboard radio to be able to send a signal to detonate all RCDs." - -/datum/action/innate/ai/destroy_rcds - name = "Destroy RCDs" - desc = "Detonate all non-cyborg RCDs on the station." - button_icon_state = "detonate_rcds" - uses = 1 - cooldown_period = 100 - -/datum/action/innate/ai/destroy_rcds/Activate() - for(var/obj/item/rcd/RCD in GLOB.rcd_list) - if(!istype(RCD, /obj/item/rcd/borg)) //Ensures that cyborg RCDs are spared. - RCD.detonate_pulse() - - to_chat(owner, "RCD detonation pulse emitted.") - owner.playsound_local(owner, 'sound/machines/twobeep.ogg', 50, 0) - -//Unlock Mech Domination: Unlocks the ability to dominate mechs. Big shocker, right? -/datum/AI_Module/large/mecha_domination - module_name = "Unlock Mech Domination" - mod_pick_name = "mechjack" - description = "Allows you to hack into a mech's onboard computer, shunting all processes into it and ejecting any occupants. Once uploaded to the mech, it is impossible to leave.\ - Do not allow the mech to leave the station's vicinity or allow it to be destroyed." - cost = 30 - upgrade = TRUE - unlock_text = "Virus package compiled. Select a target mech at any time. You must remain on the station at all times. Loss of signal will result in total system lockout." - unlock_sound = 'sound/mecha/nominal.ogg' - -/datum/AI_Module/large/mecha_domination/upgrade(mob/living/silicon/ai/AI) - AI.can_dominate_mechs = TRUE //Yep. This is all it does. Honk! - -//Thermal Sensor Override: Unlocks the ability to disable all fire alarms from doing their job. -/datum/AI_Module/large/break_fire_alarms - module_name = "Thermal Sensor Override" - mod_pick_name = "burnpigs" - description = "Gives you the ability to override the thermal sensors on all fire alarms. This will remove their ability to scan for fire and thus their ability to alert. \ - Anyone can check the fire alarm's interface and may be tipped off by its status." - one_purchase = TRUE - cost = 25 - power_type = /datum/action/innate/ai/break_fire_alarms - unlock_text = "You replace the thermal sensing capabilities of all fire alarms with a manual override, allowing you to turn them off at will." - -/datum/action/innate/ai/break_fire_alarms - name = "Override Thermal Sensors" - desc = "Disables the automatic temperature sensing on all fire alarms, making them effectively useless." - button_icon_state = "break_fire_alarms" - uses = 1 - -/datum/action/innate/ai/break_fire_alarms/Activate() - for(var/obj/machinery/firealarm/F in GLOB.machines) - if(!is_station_level(F.z)) - continue - F.emagged = TRUE - to_chat(owner, "All thermal sensors on the station have been disabled. Fire alerts will no longer be recognized.") - owner.playsound_local(owner, 'sound/machines/terminal_off.ogg', 50, 0) - -//Air Alarm Safety Override: Unlocks the ability to enable flooding on all air alarms. -/datum/AI_Module/large/break_air_alarms - module_name = "Air Alarm Safety Override" - mod_pick_name = "allow_flooding" - description = "Gives you the ability to disable safeties on all air alarms. This will allow you to use the environmental mode Flood, which disables scrubbers as well as pressure checks on vents. \ - Anyone can check the air alarm's interface and may be tipped off by their nonfunctionality." - one_purchase = TRUE - cost = 50 - power_type = /datum/action/innate/ai/break_air_alarms - unlock_text = "You remove the safety overrides on all air alarms, but you leave the confirm prompts open. You can hit 'Yes' at any time... you bastard." - -/datum/action/innate/ai/break_air_alarms - name = "Override Air Alarm Safeties" - desc = "Enables the Flood setting on all air alarms." - button_icon_state = "break_air_alarms" - uses = 1 - -/datum/action/innate/ai/break_air_alarms/Activate() - for(var/obj/machinery/alarm/AA in GLOB.machines) - if(!is_station_level(AA.z)) - continue - AA.emagged = TRUE - to_chat(owner, "All air alarm safeties on the station have been overriden. Air alarms may now use the Flood environmental mode.") - owner.playsound_local(owner, 'sound/machines/terminal_off.ogg', 50, 0) - - -//Overload Machine: Allows the AI to overload a machine, detonating it after a delay. Two uses per purchase. -/datum/AI_Module/small/overload_machine - module_name = "Machine Overload" - mod_pick_name = "overload" - description = "Overheats an electrical machine, causing a small explosion and destroying it. Two uses per purchase." - cost = 20 - power_type = /datum/action/innate/ai/ranged/overload_machine - unlock_text = "You enable the ability for the station's APCs to direct intense energy into machinery." - -/datum/action/innate/ai/ranged/overload_machine - name = "Overload Machine" - desc = "Overheats a machine, causing a small explosion after a short time." - button_icon_state = "overload_machine" - uses = 2 - linked_ability_type = /obj/effect/proc_holder/ranged_ai/overload_machine - -/datum/action/innate/ai/ranged/overload_machine/New() - ..() - desc = "[desc] It has [uses] use\s remaining." - button.desc = desc - -/datum/action/innate/ai/ranged/overload_machine/proc/detonate_machine(obj/machinery/M) - if(M && !QDELETED(M)) - explosion(get_turf(M), 0,1,1,0) - if(M) //to check if the explosion killed it before we try to delete it - qdel(M) - -/obj/effect/proc_holder/ranged_ai/overload_machine - active = FALSE - ranged_mousepointer = 'icons/effects/overload_machine_target.dmi' - enable_text = "You tap into the station's powernet. Click on a machine to detonate it, or use the ability again to cancel." - disable_text = "You release your hold on the powernet." - -/obj/effect/proc_holder/ranged_ai/overload_machine/InterceptClickOn(mob/living/caller, params, obj/machinery/target) - if(..()) - return - if(ranged_ability_user.incapacitated()) - remove_ranged_ability() - return - if(!istype(target)) - to_chat(ranged_ability_user, "You can only overload machines!") - return - - ranged_ability_user.playsound_local(ranged_ability_user, "sparks", 50, 0) - attached_action.adjust_uses(-1) - if(attached_action && attached_action.uses) - attached_action.desc = "[initial(attached_action.desc)] It has [attached_action.uses] use\s remaining." - attached_action.UpdateButtonIcon() - target.audible_message("You hear a loud electrical buzzing sound coming from [target]!") - addtimer(CALLBACK(attached_action, /datum/action/innate/ai/ranged/overload_machine.proc/detonate_machine, target), 50) //kaboom! - remove_ranged_ability(ranged_ability_user, "Overloading machine circuitry...") - return TRUE - - -//Override Machine: Allows the AI to override a machine, animating it into an angry, living version of itself. -/datum/AI_Module/small/override_machine - module_name = "Machine Override" - mod_pick_name = "override" - description = "Overrides a machine's programming, causing it to rise up and attack everyone except other machines. Four uses." - cost = 30 - power_type = /datum/action/innate/ai/ranged/override_machine - unlock_text = "You procure a virus from the Space Dark Web and distribute it to the station's machines." - -/datum/action/innate/ai/ranged/override_machine - name = "Override Machine" - desc = "Animates a targeted machine, causing it to attack anyone nearby." - button_icon_state = "override_machine" - uses = 4 - linked_ability_type = /obj/effect/proc_holder/ranged_ai/override_machine - -/datum/action/innate/ai/ranged/override_machine/New() - ..() - desc = "[desc] It has [uses] use\s remaining." - button.desc = desc - -/datum/action/innate/ai/ranged/override_machine/proc/animate_machine(obj/machinery/M) - if(M && !QDELETED(M)) - new/mob/living/simple_animal/hostile/mimic/copy/machine(get_turf(M), M, owner, 1) - -/obj/effect/proc_holder/ranged_ai/override_machine - active = FALSE - ranged_mousepointer = 'icons/effects/override_machine_target.dmi' - enable_text = "You tap into the station's powernet. Click on a machine to animate it, or use the ability again to cancel." - disable_text = "You release your hold on the powernet." - -/obj/effect/proc_holder/ranged_ai/override_machine/InterceptClickOn(mob/living/caller, params, obj/machinery/target) - if(..()) - return - if(ranged_ability_user.incapacitated()) - remove_ranged_ability() - return - if(!istype(target)) - to_chat(ranged_ability_user, "You can only animate machines!") - return - if(!target.can_be_overridden()) - to_chat(ranged_ability_user, "That machine can't be overridden!") - return - - ranged_ability_user.playsound_local(ranged_ability_user, 'sound/misc/interference.ogg', 50, 0) - attached_action.adjust_uses(-1) - if(attached_action && attached_action.uses) - attached_action.desc = "[initial(attached_action.desc)] It has [attached_action.uses] use\s remaining." - attached_action.UpdateButtonIcon() - target.audible_message("You hear a loud electrical buzzing sound coming from [target]!") - addtimer(CALLBACK(attached_action, /datum/action/innate/ai/ranged/override_machine.proc/animate_machine, target), 50) //kabeep! - remove_ranged_ability(ranged_ability_user, "Sending override signal...") - return TRUE - - -//Robotic Factory: Places a large machine that converts humans that go through it into cyborgs. Unlocking this ability removes shunting. -/datum/AI_Module/large/place_cyborg_transformer - module_name = "Robotic Factory (Removes Shunting)" - mod_pick_name = "cyborgtransformer" - description = "Build a machine anywhere, using expensive nanomachines, that can convert a living human into a loyal cyborg slave when placed inside." - cost = 100 - one_purchase = TRUE - power_type = /datum/action/innate/ai/place_transformer - unlock_text = "You prepare a robotics factory for deployment." - unlock_sound = 'sound/machines/ping.ogg' - -/datum/action/innate/ai/place_transformer - name = "Place Robotics Factory" - desc = "Places a machine that converts humans into cyborgs. Conveyor belts included!" - button_icon_state = "robotic_factory" - uses = 1 - auto_use_uses = FALSE //So we can attempt multiple times - var/list/turfOverlays - -/datum/action/innate/ai/place_transformer/New() - ..() - for(var/i in 1 to 3) - var/image/I = image("icon"='icons/turf/overlays.dmi') - LAZYADD(turfOverlays, I) - -/datum/action/innate/ai/place_transformer/Activate() - if(!owner_AI.can_place_transformer(src)) - return - active = TRUE - if(alert(owner, "Are you sure you want to place the machine here?", "Are you sure?", "Yes", "No") == "No") - active = FALSE - return - if(!owner_AI.can_place_transformer(src)) - active = FALSE - return - var/turf/T = get_turf(owner_AI.eyeobj) - new /obj/machinery/transformer/conveyor(T) - playsound(T, 'sound/effects/phasein.ogg', 100, 1) - owner_AI.can_shunt = FALSE - to_chat(owner, "You are no longer able to shunt your core to APCs.") - adjust_uses(-1) - -/mob/living/silicon/ai/proc/remove_transformer_image(client/C, image/I, turf/T) - if(C && I.loc == T) - C.images -= I - -/mob/living/silicon/ai/proc/can_place_transformer(datum/action/innate/ai/place_transformer/action) - if(!eyeobj || !isturf(loc) || incapacitated() || !action) - return - var/turf/middle = get_turf(eyeobj) - var/list/turfs = list(middle, locate(middle.x - 1, middle.y, middle.z), locate(middle.x + 1, middle.y, middle.z)) - var/alert_msg = "There isn't enough room! Make sure you are placing the machine in a clear area and on a floor." - var/success = TRUE - for(var/n in 1 to 3) //We have to do this instead of iterating normally because of how overlay images are handled - var/turf/T = turfs[n] - if(!isfloorturf(T)) - success = FALSE - var/datum/camerachunk/C = cameranet.getCameraChunk(T.x, T.y, T.z) - if(!C.visibleTurfs[T]) - alert_msg = "You don't have camera vision of this location!" - success = FALSE - for(var/atom/movable/AM in T.contents) - if(AM.density) - alert_msg = "That area must be clear of objects!" - success = FALSE - var/image/I = action.turfOverlays[n] - I.loc = T - client.images += I - I.icon_state = "[success ? "green" : "red"]Overlay" //greenOverlay and redOverlay for success and failure respectively - addtimer(CALLBACK(src, .proc/remove_transformer_image, client, I, T), 30) - if(!success) - to_chat(src, "[alert_msg]") - return success - -//Blackout: Overloads a random number of lights across the station. Three uses. -/datum/AI_Module/small/blackout - module_name = "Blackout" - mod_pick_name = "blackout" - description = "Attempts to overload the lighting circuits on the station, destroying some bulbs. Three uses." - cost = 15 - power_type = /datum/action/innate/ai/blackout - unlock_text = "You hook into the powernet and route bonus power towards the station's lighting." - -/datum/action/innate/ai/blackout - name = "Blackout" - desc = "Overloads random lights across the station." - button_icon_state = "blackout" - uses = 3 - auto_use_uses = FALSE - -/datum/action/innate/ai/blackout/New() - ..() - desc = "[desc] It has [uses] use\s remaining." - button.desc = desc - -/datum/action/innate/ai/blackout/Activate() - for(var/obj/machinery/power/apc/apc in GLOB.apcs) - if(prob(30 * apc.overload)) - apc.overload_lighting() - else - apc.overload++ - to_chat(owner, "Overcurrent applied to the powernet.") - owner.playsound_local(owner, "sparks", 50, 0) - adjust_uses(-1) - if(src && uses) //Not sure if not having src here would cause a runtime, so it's here to be safe - desc = "[initial(desc)] It has [uses] use\s remaining." - UpdateButtonIcon() - -//Reactivate Camera Network: Reactivates up to 30 cameras across the station. -/datum/AI_Module/small/reactivate_cameras - module_name = "Reactivate Camera Network" - mod_pick_name = "recam" - description = "Runs a network-wide diagnostic on the camera network, resetting focus and re-routing power to failed cameras. Can be used to repair up to 30 cameras." - cost = 10 - one_purchase = TRUE - power_type = /datum/action/innate/ai/reactivate_cameras - unlock_text = "You deploy nanomachines to the cameranet." - -/datum/action/innate/ai/reactivate_cameras - name = "Reactivate Cameras" - desc = "Reactivates disabled cameras across the station; remaining uses can be used later." - button_icon_state = "reactivate_cameras" - uses = 30 - auto_use_uses = FALSE - cooldown_period = 30 - -/datum/action/innate/ai/reactivate_cameras/New() - ..() - desc = "[desc] It has [uses] use\s remaining." - button.desc = desc - -/datum/action/innate/ai/reactivate_cameras/Activate() - var/fixed_cameras = 0 - for(var/V in cameranet.cameras) - if(!uses) - break - var/obj/machinery/camera/C = V - if(!C.status || C.view_range != initial(C.view_range)) - C.toggle_cam(owner_AI, 0) //Reactivates the camera based on status. Badly named proc. - C.view_range = initial(C.view_range) - fixed_cameras++ - uses-- //Not adjust_uses() so it doesn't automatically delete or show a message - to_chat(owner, "Diagnostic complete! Cameras reactivated: [fixed_cameras]. Reactivations remaining: [uses].") - owner.playsound_local(owner, 'sound/items/wirecutter.ogg', 50, 0) - adjust_uses(0, TRUE) //Checks the uses remaining - if(src && uses) //Not sure if not having src here would cause a runtime, so it's here to be safe - desc = "[initial(desc)] It has [uses] use\s remaining." - UpdateButtonIcon() - -//Upgrade Camera Network: EMP-proofs all cameras, in addition to giving them X-ray vision. -/datum/AI_Module/large/upgrade_cameras - module_name = "Upgrade Camera Network" - mod_pick_name = "upgradecam" - description = "Install broad-spectrum scanning and electrical redundancy firmware to the camera network, enabling EMP-proofing and light-amplified X-ray vision." //I <3 pointless technobabble - //This used to have motion sensing as well, but testing quickly revealed that giving it to the whole cameranet is PURE HORROR. - one_purchase = TRUE - cost = 35 //Decent price for omniscience! - upgrade = TRUE - unlock_text = "OTA firmware distribution complete! Cameras upgraded: CAMSUPGRADED. Light amplification system online." - unlock_sound = 'sound/items/rped.ogg' - -/datum/AI_Module/large/upgrade_cameras/upgrade(mob/living/silicon/ai/AI) - AI.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //Night-vision, without which X-ray would be very limited in power. - AI.update_sight() - var/upgraded_cameras = 0 - - for(var/V in cameranet.cameras) - var/obj/machinery/camera/C = V - if(C.assembly) - var/upgraded = FALSE - - if(!C.isXRay()) - C.upgradeXRay() - //Update what it can see. - cameranet.updateVisibility(C, 0) - upgraded = TRUE - - if(!C.isEmpProof()) - C.upgradeEmpProof() - upgraded = TRUE - - if(upgraded) - upgraded_cameras++ - - unlock_text = replacetext(unlock_text, "CAMSUPGRADED", "[upgraded_cameras]") //This works, since unlock text is called after upgrade() - -/datum/AI_Module/large/eavesdrop - module_name = "Enhanced Surveillance" - mod_pick_name = "eavesdrop" - description = "Via a combination of hidden microphones and lip reading software, you are able to use your cameras to listen in on conversations." - cost = 30 - one_purchase = TRUE - upgrade = TRUE - unlock_text = "OTA firmware distribution complete! Cameras upgraded: Enhanced surveillance package online." - unlock_sound = 'sound/items/rped.ogg' - -/datum/AI_Module/large/eavesdrop/upgrade(mob/living/silicon/ai/AI) - if(AI.eyeobj) - AI.eyeobj.relay_speech = TRUE - +//The malf AI action subtype. All malf actions are subtypes of this. +/datum/action/innate/ai + name = "AI Action" + desc = "You aren't entirely sure what this does, but it's very beepy and boopy." + background_icon_state = "bg_tech_blue" + var/mob/living/silicon/ai/owner_AI //The owner AI, so we don't have to typecast every time + var/uses //If we have multiple uses of the same power + var/auto_use_uses = TRUE //If we automatically use up uses on each activation + var/cooldown_period //If applicable, the time in deciseconds we have to wait before using any more modules + +/datum/action/innate/ai/Grant(mob/living/L) + . = ..() + if(!isAI(owner)) + WARNING("AI action [name] attempted to grant itself to non-AI mob [L.real_name] ([L.key])!") + qdel(src) + else + owner_AI = owner + +/datum/action/innate/ai/IsAvailable() + . = ..() + if(owner_AI && owner_AI.malf_cooldown > world.time) + return + +/datum/action/innate/ai/Trigger() + . = ..() + if(auto_use_uses) + adjust_uses(-1) + if(cooldown_period) + owner_AI.malf_cooldown = world.time + cooldown_period + +/datum/action/innate/ai/proc/adjust_uses(amt, silent) + uses += amt + if(!silent && uses) + to_chat(owner, "[name] now has [uses] use[uses > 1 ? "s" : ""] remaining.") + if(!uses) + if(initial(uses) > 1) //no need to tell 'em if it was one-use anyway! + to_chat(owner, "[name] has run out of uses!") + qdel(src) + +//Framework for ranged abilities that can have different effects by left-clicking stuff. +/datum/action/innate/ai/ranged + name = "Ranged AI Action" + auto_use_uses = FALSE //This is so we can do the thing and disable/enable freely without having to constantly add uses + var/obj/effect/proc_holder/ranged_ai/linked_ability //The linked proc holder that contains the actual ability code + var/linked_ability_type //The path of our linked ability + +/datum/action/innate/ai/ranged/New() + if(!linked_ability_type) + WARNING("Ranged AI action [name] attempted to spawn without a linked ability!") + qdel(src) //uh oh! + return + linked_ability = new linked_ability_type() + linked_ability.attached_action = src + ..() + +/datum/action/innate/ai/ranged/adjust_uses(amt, silent) + uses += amt + if(!silent && uses) + to_chat(owner, "[name] now has [uses] use[uses > 1 ? "s" : ""] remaining.") + if(!uses) + if(initial(uses) > 1) //no need to tell 'em if it was one-use anyway! + to_chat(owner, "[name] has run out of uses!") + Remove(owner) + QDEL_IN(src, 100) //let any active timers on us finish up + +/datum/action/innate/ai/ranged/Destroy() + QDEL_NULL(linked_ability) + return ..() + +/datum/action/innate/ai/ranged/Activate() + linked_ability.toggle(owner) + return TRUE + +//The actual ranged proc holder. +/obj/effect/proc_holder/ranged_ai + var/enable_text = "Hello World!" //Appears when the user activates the ability + var/disable_text = "Goodbye Cruel World!" //Context clues! + var/datum/action/innate/ai/ranged/attached_action + +/obj/effect/proc_holder/ranged_ai/proc/toggle(mob/user) + if(active) + remove_ranged_ability(user, disable_text) + else + add_ranged_ability(user, enable_text) + +//The datum and interface for the malf unlock menu, which lets them choose actions to unlock. +/datum/module_picker + var/temp + var/processing_time = 50 + var/list/possible_modules + +/datum/module_picker/New() + possible_modules = list() + for(var/type in typesof(/datum/AI_Module)) + var/datum/AI_Module/AM = new type + if((AM.power_type && AM.power_type != /datum/action/innate/ai) || AM.upgrade) + possible_modules += AM + +/datum/module_picker/proc/remove_malf_verbs(mob/living/silicon/ai/AI) //Removes all malfunction-related abilities from the target AI. + for(var/datum/AI_Module/AM in possible_modules) + for(var/datum/action/A in AI.actions) + if(istype(A, initial(AM.power_type))) + qdel(A) + +/datum/module_picker/proc/use(user as mob) + var/dat + dat += {"Select use of processing time: (currently #[processing_time] left.)
    +
    + Install Module:
    + The number afterwards is the amount of processing time it consumes.
    "} + for(var/datum/AI_Module/large/module in possible_modules) + dat += "[module.module_name]\[?\] ([module.cost])
    " + for(var/datum/AI_Module/small/module in possible_modules) + dat += "[module.module_name]\[?\] ([module.cost])
    " + dat += "
    " + if(temp) + dat += "[temp]" + var/datum/browser/popup = new(user, "modpicker", "Malf Module Menu", 400, 500) + popup.set_content(dat) + popup.open() + return + +/datum/module_picker/Topic(href, href_list) + ..() + + if(!isAI(usr)) + return + var/mob/living/silicon/ai/A = usr + + if(A.stat == DEAD) + to_chat(A, "You are already dead!") + return + + for(var/datum/AI_Module/AM in possible_modules) + if (href_list[AM.mod_pick_name]) + + // Cost check + if(AM.cost > processing_time) + temp = "You cannot afford this module." + break + + var/datum/action/innate/ai/action = locate(AM.power_type) in A.actions + + // Give the power and take away the money. + if(AM.upgrade) //upgrade and upgrade() are separate, be careful! + AM.upgrade(A) + possible_modules -= AM + to_chat(A, AM.unlock_text) + A.playsound_local(A, AM.unlock_sound, 50, 0) + else + if(AM.power_type) + if(!action) //Unlocking for the first time + var/datum/action/AC = new AM.power_type + AC.Grant(A) + A.current_modules += new AM.type + temp = AM.description + if(AM.one_purchase) + possible_modules -= AM + if(AM.unlock_text) + to_chat(A, AM.unlock_text) + if(AM.unlock_sound) + A.playsound_local(A, AM.unlock_sound, 50, 0) + else //Adding uses to an existing module + action.uses += initial(action.uses) + action.desc = "[initial(action.desc)] It has [action.uses] use\s remaining." + action.UpdateButtonIcon() + temp = "Additional use[action.uses > 1 ? "s" : ""] added to [action.name]!" + processing_time -= AM.cost + + if(href_list["showdesc"]) + if(AM.mod_pick_name == href_list["showdesc"]) + temp = AM.description + use(usr) + +//The base module type, which holds info about each ability. +/datum/AI_Module + var/module_name + var/mod_pick_name + var/description = "" + var/engaged = 0 + var/cost = 5 + var/one_purchase = FALSE //If this module can only be purchased once. This always applies to upgrades, even if the variable is set to false. + var/power_type = /datum/action/innate/ai //If the module gives an active ability, use this. Mutually exclusive with upgrade. + var/upgrade //If the module gives a passive upgrade, use this. Mutually exclusive with power_type. + var/unlock_text = "Hello World!" //Text shown when an ability is unlocked + var/unlock_sound //Sound played when an ability is unlocked + var/uses + +/datum/AI_Module/proc/upgrade(mob/living/silicon/AI/AI) //Apply upgrades! + return + +/datum/AI_Module/large //Big, powerful stuff that can only be used once. + +/datum/AI_Module/small //Weak, usually localized stuff with multiple uses. + +//Doomsday Device: Starts the self-destruct timer. It can only be stopped by killing the AI completely. +/datum/AI_Module/large/nuke_station + module_name = "Doomsday Device" + mod_pick_name = "nukestation" + description = "Activate a weapon that will disintegrate all organic life on the station after a 450 second delay. Can only be used while on the station, will fail if your core is moved off station or destroyed." + cost = 130 + one_purchase = TRUE + power_type = /datum/action/innate/ai/nuke_station + unlock_text = "You slowly, carefully, establish a connection with the on-station self-destruct. You can now activate it at any time." + unlock_sound = 'sound/items/timer.ogg' + +/datum/action/innate/ai/nuke_station + name = "Doomsday Device" + desc = "Activates the doomsday device. This is not reversible." + button_icon_state = "doomsday_device" + auto_use_uses = FALSE + +/datum/action/innate/ai/nuke_station/Activate() + var/turf/T = get_turf(owner) + if(!istype(T) || !is_station_level(T.z)) + to_chat(owner, "You cannot activate the doomsday device while off-station!") + return + if(alert(owner, "Send arming signal? (true = arm, false = cancel)", "purge_all_life()", "confirm = TRUE;", "confirm = FALSE;") != "confirm = TRUE;") + return + if(active) + return //prevent the AI from activating an already active doomsday + active = TRUE + set_us_up_the_bomb() + +/datum/action/innate/ai/nuke_station/proc/set_us_up_the_bomb() + to_chat(owner_AI, "Nuclear device armed.") + event_announcement.Announce("Hostile runtimes detected in all station systems, please deactivate your AI to prevent possible damage to its morality core.", "Anomaly Alert", new_sound = 'sound/AI/aimalf.ogg') + set_security_level("delta") + owner_AI.nuking = TRUE + var/obj/machinery/doomsday_device/DOOM = new /obj/machinery/doomsday_device(owner_AI) + owner_AI.doomsday_device = DOOM + owner_AI.doomsday_device.start() + for(var/obj/item/pinpointer/point in GLOB.pinpointer_list) + for(var/mob/living/silicon/ai/A in ai_list) + if((A.stat != DEAD) && A.nuking) + point.the_disk = A //The pinpointer now tracks the AI core + qdel(src) + +/obj/machinery/doomsday_device + icon = 'icons/obj/machines/nuke_terminal.dmi' + name = "doomsday device" + icon_state = "nuclearbomb_base" + desc = "A weapon which disintegrates all organic life in a large area." + anchored = 1 + density = 1 + atom_say_verb = "blares" + speed_process = TRUE // Disgusting fix. Please remove once #12952 is merged + var/timing = 0 + var/default_timer = 4500 + var/detonation_timer + var/announced = 0 + +/obj/machinery/doomsday_device/Destroy() + STOP_PROCESSING(SSfastprocess, src) + SSshuttle.emergencyNoEscape = 0 + if(SSshuttle.emergency.mode == SHUTTLE_STRANDED) + SSshuttle.emergency.mode = SHUTTLE_DOCKED + SSshuttle.emergency.timer = world.time + priority_announcement.Announce("Hostile environment resolved. You have 3 minutes to board the Emergency Shuttle.", "Priority Announcement", 'sound/AI/shuttledock.ogg') + return ..() + +/obj/machinery/doomsday_device/proc/start() + detonation_timer = world.time + default_timer + timing = 1 + START_PROCESSING(SSfastprocess, src) + SSshuttle.emergencyNoEscape = 1 + +/obj/machinery/doomsday_device/proc/seconds_remaining() + . = max(0, (round(detonation_timer - world.time) / 10)) + +/obj/machinery/doomsday_device/process() + var/turf/T = get_turf(src) + if(!T || !is_station_level(T.z)) + minor_announcement.Announce("DOOMSDAY DEVICE OUT OF STATION RANGE, ABORTING", "ERROR ER0RR $R0RRO$!R41.%%!!(%$^^__+ @#F0E4", 'sound/misc/notice1.ogg') + SSshuttle.emergencyNoEscape = 0 + if(SSshuttle.emergency.mode == SHUTTLE_STRANDED) + SSshuttle.emergency.mode = SHUTTLE_DOCKED + SSshuttle.emergency.timer = world.time + priority_announcement.Announce("Hostile environment resolved. You have 3 minutes to board the Emergency Shuttle.", "Priority Announcement", 'sound/AI/shuttledock.ogg') + qdel(src) + if(!timing) + STOP_PROCESSING(SSfastprocess, src) + return + var/sec_left = seconds_remaining() + if(sec_left <= 0) + timing = 0 + detonate(T.z) + qdel(src) + else + if(!(sec_left % 60) && !announced) + var/message = "[sec_left] SECONDS UNTIL DOOMSDAY DEVICE ACTIVATION!" + minor_announcement.Announce(message, "ERROR ER0RR $R0RRO$!R41.%%!!(%$^^__+ @#F0E4", 'sound/misc/notice1.ogg') + announced = 10 + announced = max(0, announced-1) + +/obj/machinery/doomsday_device/proc/detonate(z_level = 1) + for(var/mob/M in GLOB.player_list) + M << 'sound/machines/alarm.ogg' + sleep(100) + for(var/mob/living/L in GLOB.mob_list) + var/turf/T = get_turf(L) + if(!T || T.z != z_level) + continue + if(issilicon(L)) + continue + to_chat(L, "The blast wave from [src] tears you atom from atom!") + L.dust() + to_chat(world, "The AI cleansed the station of life with the doomsday device!") + SSticker.force_ending = 1 + +//AI Turret Upgrade: Increases the health and damage of all turrets. +/datum/AI_Module/large/upgrade_turrets + module_name = "AI Turret Upgrade" + mod_pick_name = "turret" + description = "Improves the power and health of all AI turrets. This effect is permanent." + cost = 30 + upgrade = TRUE + unlock_text = "You establish a power diversion to your turrets, upgrading their health and damage." + unlock_sound = 'sound/items/rped.ogg' + +/datum/AI_Module/large/upgrade_turrets/upgrade(mob/living/silicon/AI/AI) + for(var/obj/machinery/porta_turret/turret in GLOB.machines) + var/turf/T = get_turf(turret) + if(is_station_level(T.z)) + turret.health += 30 + turret.eprojectile = /obj/item/projectile/beam/laser/heavylaser //Once you see it, you will know what it means to FEAR. + turret.eshot_sound = 'sound/weapons/lasercannonfire.ogg' + +//Hostile Station Lockdown: Locks, bolts, and electrifies every airlock on the station. After 90 seconds, the doors reset. +/datum/AI_Module/large/lockdown + module_name = "Hostile Station Lockdown" + mod_pick_name = "lockdown" + description = "Overload the airlock, blast door and fire control networks, locking them down. Caution! This command also electrifies all airlocks. The networks will automatically reset after 90 seconds, briefly \ + opening all doors on the station." + cost = 30 + one_purchase = TRUE + power_type = /datum/action/innate/ai/lockdown + unlock_text = "You upload a sleeper trojan into the door control systems. You can send a signal to set it off at any time." + +/datum/action/innate/ai/lockdown + name = "Lockdown" + desc = "Closes, bolts, and depowers every airlock, firelock, and blast door on the station. After 90 seconds, they will reset themselves." + button_icon_state = "lockdown" + uses = 1 + +/datum/action/innate/ai/lockdown/Activate() + for(var/obj/machinery/door/D in GLOB.airlocks) + if(!is_station_level(D.z)) + continue + INVOKE_ASYNC(D, /obj/machinery/door.proc/hostile_lockdown, owner) + addtimer(CALLBACK(D, /obj/machinery/door.proc/disable_lockdown), 900) + + post_status("alert", "lockdown") + + minor_announcement.Announce("Hostile runtime detected in door controllers. Isolation lockdown protocols are now in effect. Please remain calm.", "Network Alert") + to_chat(owner, "Lockdown Initiated. Network reset in 90 seconds.") + spawn(900) + minor_announcement.Announce("Automatic system reboot complete. Have a secure day.","Network reset:") + +//Destroy RCDs: Detonates all non-cyborg RCDs on the station. +/datum/AI_Module/large/destroy_rcd + module_name = "Destroy RCDs" + mod_pick_name = "rcd" + description = "Send a specialised pulse to detonate all hand-held and exosuit Rapid Construction Devices on the station." + cost = 25 + one_purchase = TRUE + power_type = /datum/action/innate/ai/destroy_rcds + unlock_text = "After some improvisation, you rig your onboard radio to be able to send a signal to detonate all RCDs." + +/datum/action/innate/ai/destroy_rcds + name = "Destroy RCDs" + desc = "Detonate all non-cyborg RCDs on the station." + button_icon_state = "detonate_rcds" + uses = 1 + cooldown_period = 100 + +/datum/action/innate/ai/destroy_rcds/Activate() + for(var/obj/item/rcd/RCD in GLOB.rcd_list) + if(!istype(RCD, /obj/item/rcd/borg)) //Ensures that cyborg RCDs are spared. + RCD.detonate_pulse() + + to_chat(owner, "RCD detonation pulse emitted.") + owner.playsound_local(owner, 'sound/machines/twobeep.ogg', 50, 0) + +//Unlock Mech Domination: Unlocks the ability to dominate mechs. Big shocker, right? +/datum/AI_Module/large/mecha_domination + module_name = "Unlock Mech Domination" + mod_pick_name = "mechjack" + description = "Allows you to hack into a mech's onboard computer, shunting all processes into it and ejecting any occupants. Once uploaded to the mech, it is impossible to leave.\ + Do not allow the mech to leave the station's vicinity or allow it to be destroyed." + cost = 30 + upgrade = TRUE + unlock_text = "Virus package compiled. Select a target mech at any time. You must remain on the station at all times. Loss of signal will result in total system lockout." + unlock_sound = 'sound/mecha/nominal.ogg' + +/datum/AI_Module/large/mecha_domination/upgrade(mob/living/silicon/ai/AI) + AI.can_dominate_mechs = TRUE //Yep. This is all it does. Honk! + +//Thermal Sensor Override: Unlocks the ability to disable all fire alarms from doing their job. +/datum/AI_Module/large/break_fire_alarms + module_name = "Thermal Sensor Override" + mod_pick_name = "burnpigs" + description = "Gives you the ability to override the thermal sensors on all fire alarms. This will remove their ability to scan for fire and thus their ability to alert. \ + Anyone can check the fire alarm's interface and may be tipped off by its status." + one_purchase = TRUE + cost = 25 + power_type = /datum/action/innate/ai/break_fire_alarms + unlock_text = "You replace the thermal sensing capabilities of all fire alarms with a manual override, allowing you to turn them off at will." + +/datum/action/innate/ai/break_fire_alarms + name = "Override Thermal Sensors" + desc = "Disables the automatic temperature sensing on all fire alarms, making them effectively useless." + button_icon_state = "break_fire_alarms" + uses = 1 + +/datum/action/innate/ai/break_fire_alarms/Activate() + for(var/obj/machinery/firealarm/F in GLOB.machines) + if(!is_station_level(F.z)) + continue + F.emagged = TRUE + to_chat(owner, "All thermal sensors on the station have been disabled. Fire alerts will no longer be recognized.") + owner.playsound_local(owner, 'sound/machines/terminal_off.ogg', 50, 0) + +//Air Alarm Safety Override: Unlocks the ability to enable flooding on all air alarms. +/datum/AI_Module/large/break_air_alarms + module_name = "Air Alarm Safety Override" + mod_pick_name = "allow_flooding" + description = "Gives you the ability to disable safeties on all air alarms. This will allow you to use the environmental mode Flood, which disables scrubbers as well as pressure checks on vents. \ + Anyone can check the air alarm's interface and may be tipped off by their nonfunctionality." + one_purchase = TRUE + cost = 50 + power_type = /datum/action/innate/ai/break_air_alarms + unlock_text = "You remove the safety overrides on all air alarms, but you leave the confirm prompts open. You can hit 'Yes' at any time... you bastard." + +/datum/action/innate/ai/break_air_alarms + name = "Override Air Alarm Safeties" + desc = "Enables the Flood setting on all air alarms." + button_icon_state = "break_air_alarms" + uses = 1 + +/datum/action/innate/ai/break_air_alarms/Activate() + for(var/obj/machinery/alarm/AA in GLOB.machines) + if(!is_station_level(AA.z)) + continue + AA.emagged = TRUE + to_chat(owner, "All air alarm safeties on the station have been overriden. Air alarms may now use the Flood environmental mode.") + owner.playsound_local(owner, 'sound/machines/terminal_off.ogg', 50, 0) + + +//Overload Machine: Allows the AI to overload a machine, detonating it after a delay. Two uses per purchase. +/datum/AI_Module/small/overload_machine + module_name = "Machine Overload" + mod_pick_name = "overload" + description = "Overheats an electrical machine, causing a small explosion and destroying it. Two uses per purchase." + cost = 20 + power_type = /datum/action/innate/ai/ranged/overload_machine + unlock_text = "You enable the ability for the station's APCs to direct intense energy into machinery." + +/datum/action/innate/ai/ranged/overload_machine + name = "Overload Machine" + desc = "Overheats a machine, causing a small explosion after a short time." + button_icon_state = "overload_machine" + uses = 2 + linked_ability_type = /obj/effect/proc_holder/ranged_ai/overload_machine + +/datum/action/innate/ai/ranged/overload_machine/New() + ..() + desc = "[desc] It has [uses] use\s remaining." + button.desc = desc + +/datum/action/innate/ai/ranged/overload_machine/proc/detonate_machine(obj/machinery/M) + if(M && !QDELETED(M)) + explosion(get_turf(M), 0,1,1,0) + if(M) //to check if the explosion killed it before we try to delete it + qdel(M) + +/obj/effect/proc_holder/ranged_ai/overload_machine + active = FALSE + ranged_mousepointer = 'icons/effects/overload_machine_target.dmi' + enable_text = "You tap into the station's powernet. Click on a machine to detonate it, or use the ability again to cancel." + disable_text = "You release your hold on the powernet." + +/obj/effect/proc_holder/ranged_ai/overload_machine/InterceptClickOn(mob/living/caller, params, obj/machinery/target) + if(..()) + return + if(ranged_ability_user.incapacitated()) + remove_ranged_ability() + return + if(!istype(target)) + to_chat(ranged_ability_user, "You can only overload machines!") + return + + ranged_ability_user.playsound_local(ranged_ability_user, "sparks", 50, 0) + attached_action.adjust_uses(-1) + if(attached_action && attached_action.uses) + attached_action.desc = "[initial(attached_action.desc)] It has [attached_action.uses] use\s remaining." + attached_action.UpdateButtonIcon() + target.audible_message("You hear a loud electrical buzzing sound coming from [target]!") + addtimer(CALLBACK(attached_action, /datum/action/innate/ai/ranged/overload_machine.proc/detonate_machine, target), 50) //kaboom! + remove_ranged_ability(ranged_ability_user, "Overloading machine circuitry...") + return TRUE + + +//Override Machine: Allows the AI to override a machine, animating it into an angry, living version of itself. +/datum/AI_Module/small/override_machine + module_name = "Machine Override" + mod_pick_name = "override" + description = "Overrides a machine's programming, causing it to rise up and attack everyone except other machines. Four uses." + cost = 30 + power_type = /datum/action/innate/ai/ranged/override_machine + unlock_text = "You procure a virus from the Space Dark Web and distribute it to the station's machines." + +/datum/action/innate/ai/ranged/override_machine + name = "Override Machine" + desc = "Animates a targeted machine, causing it to attack anyone nearby." + button_icon_state = "override_machine" + uses = 4 + linked_ability_type = /obj/effect/proc_holder/ranged_ai/override_machine + +/datum/action/innate/ai/ranged/override_machine/New() + ..() + desc = "[desc] It has [uses] use\s remaining." + button.desc = desc + +/datum/action/innate/ai/ranged/override_machine/proc/animate_machine(obj/machinery/M) + if(M && !QDELETED(M)) + new/mob/living/simple_animal/hostile/mimic/copy/machine(get_turf(M), M, owner, 1) + +/obj/effect/proc_holder/ranged_ai/override_machine + active = FALSE + ranged_mousepointer = 'icons/effects/override_machine_target.dmi' + enable_text = "You tap into the station's powernet. Click on a machine to animate it, or use the ability again to cancel." + disable_text = "You release your hold on the powernet." + +/obj/effect/proc_holder/ranged_ai/override_machine/InterceptClickOn(mob/living/caller, params, obj/machinery/target) + if(..()) + return + if(ranged_ability_user.incapacitated()) + remove_ranged_ability() + return + if(!istype(target)) + to_chat(ranged_ability_user, "You can only animate machines!") + return + if(!target.can_be_overridden()) + to_chat(ranged_ability_user, "That machine can't be overridden!") + return + + ranged_ability_user.playsound_local(ranged_ability_user, 'sound/misc/interference.ogg', 50, 0) + attached_action.adjust_uses(-1) + if(attached_action && attached_action.uses) + attached_action.desc = "[initial(attached_action.desc)] It has [attached_action.uses] use\s remaining." + attached_action.UpdateButtonIcon() + target.audible_message("You hear a loud electrical buzzing sound coming from [target]!") + addtimer(CALLBACK(attached_action, /datum/action/innate/ai/ranged/override_machine.proc/animate_machine, target), 50) //kabeep! + remove_ranged_ability(ranged_ability_user, "Sending override signal...") + return TRUE + + +//Robotic Factory: Places a large machine that converts humans that go through it into cyborgs. Unlocking this ability removes shunting. +/datum/AI_Module/large/place_cyborg_transformer + module_name = "Robotic Factory (Removes Shunting)" + mod_pick_name = "cyborgtransformer" + description = "Build a machine anywhere, using expensive nanomachines, that can convert a living human into a loyal cyborg slave when placed inside." + cost = 100 + one_purchase = TRUE + power_type = /datum/action/innate/ai/place_transformer + unlock_text = "You prepare a robotics factory for deployment." + unlock_sound = 'sound/machines/ping.ogg' + +/datum/action/innate/ai/place_transformer + name = "Place Robotics Factory" + desc = "Places a machine that converts humans into cyborgs. Conveyor belts included!" + button_icon_state = "robotic_factory" + uses = 1 + auto_use_uses = FALSE //So we can attempt multiple times + var/list/turfOverlays + +/datum/action/innate/ai/place_transformer/New() + ..() + for(var/i in 1 to 3) + var/image/I = image("icon"='icons/turf/overlays.dmi') + LAZYADD(turfOverlays, I) + +/datum/action/innate/ai/place_transformer/Activate() + if(!owner_AI.can_place_transformer(src)) + return + active = TRUE + if(alert(owner, "Are you sure you want to place the machine here?", "Are you sure?", "Yes", "No") == "No") + active = FALSE + return + if(!owner_AI.can_place_transformer(src)) + active = FALSE + return + var/turf/T = get_turf(owner_AI.eyeobj) + new /obj/machinery/transformer/conveyor(T) + playsound(T, 'sound/effects/phasein.ogg', 100, 1) + owner_AI.can_shunt = FALSE + to_chat(owner, "You are no longer able to shunt your core to APCs.") + adjust_uses(-1) + +/mob/living/silicon/ai/proc/remove_transformer_image(client/C, image/I, turf/T) + if(C && I.loc == T) + C.images -= I + +/mob/living/silicon/ai/proc/can_place_transformer(datum/action/innate/ai/place_transformer/action) + if(!eyeobj || !isturf(loc) || incapacitated() || !action) + return + var/turf/middle = get_turf(eyeobj) + var/list/turfs = list(middle, locate(middle.x - 1, middle.y, middle.z), locate(middle.x + 1, middle.y, middle.z)) + var/alert_msg = "There isn't enough room! Make sure you are placing the machine in a clear area and on a floor." + var/success = TRUE + for(var/n in 1 to 3) //We have to do this instead of iterating normally because of how overlay images are handled + var/turf/T = turfs[n] + if(!isfloorturf(T)) + success = FALSE + var/datum/camerachunk/C = cameranet.getCameraChunk(T.x, T.y, T.z) + if(!C.visibleTurfs[T]) + alert_msg = "You don't have camera vision of this location!" + success = FALSE + for(var/atom/movable/AM in T.contents) + if(AM.density) + alert_msg = "That area must be clear of objects!" + success = FALSE + var/image/I = action.turfOverlays[n] + I.loc = T + client.images += I + I.icon_state = "[success ? "green" : "red"]Overlay" //greenOverlay and redOverlay for success and failure respectively + addtimer(CALLBACK(src, .proc/remove_transformer_image, client, I, T), 30) + if(!success) + to_chat(src, "[alert_msg]") + return success + +//Blackout: Overloads a random number of lights across the station. Three uses. +/datum/AI_Module/small/blackout + module_name = "Blackout" + mod_pick_name = "blackout" + description = "Attempts to overload the lighting circuits on the station, destroying some bulbs. Three uses." + cost = 15 + power_type = /datum/action/innate/ai/blackout + unlock_text = "You hook into the powernet and route bonus power towards the station's lighting." + +/datum/action/innate/ai/blackout + name = "Blackout" + desc = "Overloads random lights across the station." + button_icon_state = "blackout" + uses = 3 + auto_use_uses = FALSE + +/datum/action/innate/ai/blackout/New() + ..() + desc = "[desc] It has [uses] use\s remaining." + button.desc = desc + +/datum/action/innate/ai/blackout/Activate() + for(var/obj/machinery/power/apc/apc in GLOB.apcs) + if(prob(30 * apc.overload)) + apc.overload_lighting() + else + apc.overload++ + to_chat(owner, "Overcurrent applied to the powernet.") + owner.playsound_local(owner, "sparks", 50, 0) + adjust_uses(-1) + if(src && uses) //Not sure if not having src here would cause a runtime, so it's here to be safe + desc = "[initial(desc)] It has [uses] use\s remaining." + UpdateButtonIcon() + +//Reactivate Camera Network: Reactivates up to 30 cameras across the station. +/datum/AI_Module/small/reactivate_cameras + module_name = "Reactivate Camera Network" + mod_pick_name = "recam" + description = "Runs a network-wide diagnostic on the camera network, resetting focus and re-routing power to failed cameras. Can be used to repair up to 30 cameras." + cost = 10 + one_purchase = TRUE + power_type = /datum/action/innate/ai/reactivate_cameras + unlock_text = "You deploy nanomachines to the cameranet." + +/datum/action/innate/ai/reactivate_cameras + name = "Reactivate Cameras" + desc = "Reactivates disabled cameras across the station; remaining uses can be used later." + button_icon_state = "reactivate_cameras" + uses = 30 + auto_use_uses = FALSE + cooldown_period = 30 + +/datum/action/innate/ai/reactivate_cameras/New() + ..() + desc = "[desc] It has [uses] use\s remaining." + button.desc = desc + +/datum/action/innate/ai/reactivate_cameras/Activate() + var/fixed_cameras = 0 + for(var/V in cameranet.cameras) + if(!uses) + break + var/obj/machinery/camera/C = V + if(!C.status || C.view_range != initial(C.view_range)) + C.toggle_cam(owner_AI, 0) //Reactivates the camera based on status. Badly named proc. + C.view_range = initial(C.view_range) + fixed_cameras++ + uses-- //Not adjust_uses() so it doesn't automatically delete or show a message + to_chat(owner, "Diagnostic complete! Cameras reactivated: [fixed_cameras]. Reactivations remaining: [uses].") + owner.playsound_local(owner, 'sound/items/wirecutter.ogg', 50, 0) + adjust_uses(0, TRUE) //Checks the uses remaining + if(src && uses) //Not sure if not having src here would cause a runtime, so it's here to be safe + desc = "[initial(desc)] It has [uses] use\s remaining." + UpdateButtonIcon() + +//Upgrade Camera Network: EMP-proofs all cameras, in addition to giving them X-ray vision. +/datum/AI_Module/large/upgrade_cameras + module_name = "Upgrade Camera Network" + mod_pick_name = "upgradecam" + description = "Install broad-spectrum scanning and electrical redundancy firmware to the camera network, enabling EMP-proofing and light-amplified X-ray vision." //I <3 pointless technobabble + //This used to have motion sensing as well, but testing quickly revealed that giving it to the whole cameranet is PURE HORROR. + one_purchase = TRUE + cost = 35 //Decent price for omniscience! + upgrade = TRUE + unlock_text = "OTA firmware distribution complete! Cameras upgraded: CAMSUPGRADED. Light amplification system online." + unlock_sound = 'sound/items/rped.ogg' + +/datum/AI_Module/large/upgrade_cameras/upgrade(mob/living/silicon/ai/AI) + AI.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //Night-vision, without which X-ray would be very limited in power. + AI.update_sight() + var/upgraded_cameras = 0 + + for(var/V in cameranet.cameras) + var/obj/machinery/camera/C = V + if(C.assembly) + var/upgraded = FALSE + + if(!C.isXRay()) + C.upgradeXRay() + //Update what it can see. + cameranet.updateVisibility(C, 0) + upgraded = TRUE + + if(!C.isEmpProof()) + C.upgradeEmpProof() + upgraded = TRUE + + if(upgraded) + upgraded_cameras++ + + unlock_text = replacetext(unlock_text, "CAMSUPGRADED", "[upgraded_cameras]") //This works, since unlock text is called after upgrade() + +/datum/AI_Module/large/eavesdrop + module_name = "Enhanced Surveillance" + mod_pick_name = "eavesdrop" + description = "Via a combination of hidden microphones and lip reading software, you are able to use your cameras to listen in on conversations." + cost = 30 + one_purchase = TRUE + upgrade = TRUE + unlock_text = "OTA firmware distribution complete! Cameras upgraded: Enhanced surveillance package online." + unlock_sound = 'sound/items/rped.ogg' + +/datum/AI_Module/large/eavesdrop/upgrade(mob/living/silicon/ai/AI) + if(AI.eyeobj) + AI.eyeobj.relay_speech = TRUE + diff --git a/code/game/gamemodes/meteor/meteor.dm b/code/game/gamemodes/meteor/meteor.dm index ee0ef02d1dc..f8ac70b31dc 100644 --- a/code/game/gamemodes/meteor/meteor.dm +++ b/code/game/gamemodes/meteor/meteor.dm @@ -1,61 +1,61 @@ -/datum/game_mode/meteor - name = "meteor" - config_tag = "meteor" - var/const/initialmeteordelay = 6000 - var/wave = 1 - required_players = 35 - - -/datum/game_mode/meteor/announce() - to_chat(world, "The current game mode is - Meteor!") - to_chat(world, "The space station has been stuck in a major meteor shower. You must escape from the station or at least live.") - - -/datum/game_mode/meteor/post_setup() - spawn(rand(waittime_l, waittime_h)) - command_announcement.Announce("The station is on the path of an incoming wave of meteors. Reinforce the hull and prepare damage control parties.", "Incoming Meteors", 'sound/effects/siren.ogg') - spawn(initialmeteordelay) - sendmeteors() - ..() - - - -/datum/game_mode/meteor/proc/sendmeteors() - var/waveduration = world.time + rand(0,1000) + text2num("[wave]000") / 2 - var/waitduration = rand(3000,6000) - while(waveduration - world.time > 0) - sleep(max(65 - text2num("[wave]0") / 2, 40)) - spawn() spawn_meteors(6, meteors_normal) - wave++ - sleep(waitduration) - sendmeteors() - -/datum/game_mode/meteor/declare_completion() - var/text - var/survivors = 0 - for(var/mob/living/player in GLOB.player_list) - if(player.stat != DEAD) - var/turf/location = get_turf(player.loc) - if(!location) continue - - if(location.loc.type == SSshuttle.emergency.areaInstance.type) //didn't work in the switch for some reason - text += "
    [player.real_name] escaped on the emergency shuttle" - - else - switch(location.loc.type) - if( /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, /area/shuttle/escape_pod3/centcom, /area/shuttle/escape_pod5/centcom ) - text += "
    [player.real_name] escaped in a life pod." - else - text += "
    [player.real_name] survived but is stranded without any hope of rescue." - survivors++ - - if(survivors) - to_chat(world, "The following survived the meteor storm:[text]") - else - to_chat(world, "Nobody survived the meteor storm!") - - feedback_set_details("round_end_result","meteor end - evacuation") - feedback_set("round_end_result", "Meteor survivors: [survivors]") - - ..() - return 1 +/datum/game_mode/meteor + name = "meteor" + config_tag = "meteor" + var/const/initialmeteordelay = 6000 + var/wave = 1 + required_players = 35 + + +/datum/game_mode/meteor/announce() + to_chat(world, "The current game mode is - Meteor!") + to_chat(world, "The space station has been stuck in a major meteor shower. You must escape from the station or at least live.") + + +/datum/game_mode/meteor/post_setup() + spawn(rand(waittime_l, waittime_h)) + command_announcement.Announce("The station is on the path of an incoming wave of meteors. Reinforce the hull and prepare damage control parties.", "Incoming Meteors", 'sound/effects/siren.ogg') + spawn(initialmeteordelay) + sendmeteors() + ..() + + + +/datum/game_mode/meteor/proc/sendmeteors() + var/waveduration = world.time + rand(0,1000) + text2num("[wave]000") / 2 + var/waitduration = rand(3000,6000) + while(waveduration - world.time > 0) + sleep(max(65 - text2num("[wave]0") / 2, 40)) + spawn() spawn_meteors(6, meteors_normal) + wave++ + sleep(waitduration) + sendmeteors() + +/datum/game_mode/meteor/declare_completion() + var/text + var/survivors = 0 + for(var/mob/living/player in GLOB.player_list) + if(player.stat != DEAD) + var/turf/location = get_turf(player.loc) + if(!location) continue + + if(location.loc.type == SSshuttle.emergency.areaInstance.type) //didn't work in the switch for some reason + text += "
    [player.real_name] escaped on the emergency shuttle" + + else + switch(location.loc.type) + if( /area/shuttle/escape_pod1/centcom, /area/shuttle/escape_pod2/centcom, /area/shuttle/escape_pod3/centcom, /area/shuttle/escape_pod5/centcom ) + text += "
    [player.real_name] escaped in a life pod." + else + text += "
    [player.real_name] survived but is stranded without any hope of rescue." + survivors++ + + if(survivors) + to_chat(world, "The following survived the meteor storm:[text]") + else + to_chat(world, "Nobody survived the meteor storm!") + + feedback_set_details("round_end_result","meteor end - evacuation") + feedback_set("round_end_result", "Meteor survivors: [survivors]") + + ..() + return 1 diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm index 5dafcee72a7..507fedddd32 100644 --- a/code/game/gamemodes/meteor/meteors.dm +++ b/code/game/gamemodes/meteor/meteors.dm @@ -1,318 +1,318 @@ -//Meteors probability of spawning during a given wave -/var/list/meteors_normal = list(/obj/effect/meteor/dust=3, /obj/effect/meteor/medium=8, /obj/effect/meteor/big=3, \ - /obj/effect/meteor/flaming=1, /obj/effect/meteor/irradiated=3) //for normal meteor event - -/var/list/meteors_threatening = list(/obj/effect/meteor/medium=4, /obj/effect/meteor/big=8, \ - /obj/effect/meteor/flaming=3, /obj/effect/meteor/irradiated=3) //for threatening meteor event - -/var/list/meteors_catastrophic = list(/obj/effect/meteor/medium=5, /obj/effect/meteor/big=75, \ - /obj/effect/meteor/flaming=10, /obj/effect/meteor/irradiated=10, /obj/effect/meteor/tunguska = 1) //for catastrophic meteor event - -/var/list/meteors_dust = list(/obj/effect/meteor/dust) //for space dust event - -/var/list/meteors_gore = list(/obj/effect/meteor/gore) //Meaty Gore - -/var/list/meteors_ops = list(/obj/effect/meteor/goreops) //Meaty Ops - - -/////////////////////////////// -//Meteor spawning global procs -/////////////////////////////// -/proc/spawn_meteors(var/number = 10, var/list/meteortypes) - for(var/i = 0; i < number; i++) - spawn_meteor(meteortypes) - -/proc/spawn_meteor(var/list/meteortypes) - var/turf/pickedstart - var/turf/pickedgoal - var/max_i = 10//number of tries to spawn meteor. - while(!istype(pickedstart, /turf/space)) - var/startSide = pick(cardinal) - pickedstart = spaceDebrisStartLoc(startSide, 1) - pickedgoal = spaceDebrisFinishLoc(startSide, 1) - max_i-- - if(max_i<=0) - return - var/Me = pickweight(meteortypes) - var/obj/effect/meteor/M = new Me(pickedstart) - M.dest = pickedgoal - M.z_original = level_name_to_num(MAIN_STATION) - spawn(0) - walk_towards(M, M.dest, 1) - return - -/proc/spaceDebrisStartLoc(startSide, Z) - var/starty - var/startx - switch(startSide) - if(NORTH) - starty = world.maxy-(TRANSITIONEDGE+1) - startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) - if(EAST) - starty = rand((TRANSITIONEDGE+1),world.maxy-(TRANSITIONEDGE+1)) - startx = world.maxx-(TRANSITIONEDGE+1) - if(SOUTH) - starty = (TRANSITIONEDGE+1) - startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) - if(WEST) - starty = rand((TRANSITIONEDGE+1), world.maxy-(TRANSITIONEDGE+1)) - startx = (TRANSITIONEDGE+1) - var/turf/T = locate(startx, starty, Z) - return T - -/proc/spaceDebrisFinishLoc(startSide, Z) - var/endy - var/endx - switch(startSide) - if(NORTH) - endy = TRANSITIONEDGE - endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) - if(EAST) - endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE) - endx = TRANSITIONEDGE - if(SOUTH) - endy = world.maxy-TRANSITIONEDGE - endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) - if(WEST) - endy = rand(TRANSITIONEDGE,world.maxy-TRANSITIONEDGE) - endx = world.maxx-TRANSITIONEDGE - var/turf/T = locate(endx, endy, Z) - return T - -/////////////////////// -//The meteor effect -////////////////////// - -/obj/effect/meteor - name = "the concept of meteor" - desc = "You should probably run instead of gawking at this." - icon = 'icons/obj/meteor.dmi' - icon_state = "small" - density = 1 - anchored = 1 - var/hits = 4 - var/hitpwr = 2 //Level of ex_act to be called on hit. - var/dest - pass_flags = PASSTABLE - var/heavy = 0 - var/meteorsound = 'sound/effects/meteorimpact.ogg' - var/z_original = 1 - - var/meteordrop = /obj/item/stack/ore/iron - var/dropamt = 2 - -/obj/effect/meteor/Move() - if(z != z_original || loc == dest) - qdel(src) - return - - . = ..() //process movement... - - if(.)//.. if did move, ram the turf we get in - var/turf/T = get_turf(loc) - ram_turf(T) - - if(prob(10) && !istype(T, /turf/space))//randomly takes a 'hit' from ramming - get_hit() - - return . - -/obj/effect/meteor/Destroy() - GLOB.meteor_list -= src - walk(src,0) //this cancels the walk_towards() proc - return ..() - -/obj/effect/meteor/New() - ..() - GLOB.meteor_list += src - SpinAnimation() - -/obj/effect/meteor/Bump(atom/A) - if(A) - ram_turf(get_turf(A)) - playsound(src.loc, meteorsound, 40, 1) - get_hit() - -/obj/effect/meteor/CanPass(atom/movable/mover, turf/target, height=0) - return istype(mover, /obj/effect/meteor) ? 1 : ..() - -/obj/effect/meteor/proc/ram_turf(var/turf/T) - //first bust whatever is in the turf - for(var/atom/A in T) - if(A != src) - A.ex_act(hitpwr) - - //then, ram the turf if it still exists - if(T) - T.ex_act(hitpwr) - -//process getting 'hit' by colliding with a dense object -//or randomly when ramming turfs -/obj/effect/meteor/proc/get_hit() - hits-- - if(hits <= 0) - make_debris() - meteor_effect(heavy) - qdel(src) - -/obj/effect/meteor/ex_act() - return - -/obj/effect/meteor/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/pickaxe)) - make_debris() - qdel(src) - return - return ..() - -/obj/effect/meteor/proc/make_debris() - for(var/throws = dropamt, throws > 0, throws--) - var/obj/item/O = new meteordrop(get_turf(src)) - O.throw_at(dest, 5, 10) - -/obj/effect/meteor/proc/meteor_effect(var/sound=1) - if(sound) - var/sound/meteor_sound = sound(meteorsound) - var/random_frequency = get_rand_frequency() - - for(var/P in GLOB.player_list) - var/mob/M = P - var/turf/T = get_turf(M) - if(!T || T.z != src.z) - continue - var/dist = get_dist(M.loc, src.loc) - if(prob(50)) - shake_camera(M, dist > 20 ? 3 : 5, dist > 20 ? 1 : 3) - M.playsound_local(src.loc, null, 50, 1, random_frequency, 10, S = meteor_sound) - -/////////////////////// -//Meteor types -/////////////////////// - -//Dust -/obj/effect/meteor/dust - name = "space dust" - icon_state = "dust" - pass_flags = PASSTABLE | PASSGRILLE - hits = 1 - hitpwr = 3 - meteorsound = 'sound/weapons/tap.ogg' - meteordrop = /obj/item/stack/ore/glass - -//Medium-sized -/obj/effect/meteor/medium - name = "meteor" - dropamt = 3 - -/obj/effect/meteor/medium/meteor_effect() - ..(heavy) - explosion(src.loc, 0, 1, 2, 3, 0) - -//Large-sized -/obj/effect/meteor/big - name = "large meteor" - icon_state = "large" - hits = 6 - heavy = 1 - dropamt = 4 - -/obj/effect/meteor/big/meteor_effect() - ..(heavy) - explosion(src.loc, 1, 2, 3, 4, 0) - -//Flaming meteor -/obj/effect/meteor/flaming - name = "flaming meteor" - icon_state = "flaming" - hits = 5 - heavy = 1 - meteorsound = 'sound/effects/bamf.ogg' - meteordrop = /obj/item/stack/ore/plasma - -/obj/effect/meteor/flaming/meteor_effect() - ..(heavy) - explosion(src.loc, 1, 2, 3, 4, 0, 0, flame_range = 5) - -//Radiation meteor -/obj/effect/meteor/irradiated - name = "glowing meteor" - icon_state = "glowing" - heavy = 1 - meteordrop = /obj/item/stack/ore/uranium - - -/obj/effect/meteor/irradiated/meteor_effect() - ..(heavy) - explosion(src.loc, 0, 0, 4, 3, 0) - new /obj/effect/decal/cleanable/greenglow(get_turf(src)) - for(var/mob/living/L in view(5, src)) - L.apply_effect(40, IRRADIATE) - -//Station buster Tunguska -/obj/effect/meteor/tunguska - name = "tunguska meteor" - icon_state = "flaming" - desc = "Your life briefly passes before your eyes the moment you lay them on this monstruosity." - hits = 30 - hitpwr = 1 - heavy = 1 - meteorsound = 'sound/effects/bamf.ogg' - meteordrop = /obj/item/stack/ore/plasma - -/obj/effect/meteor/tunguska/meteor_effect() - ..(heavy) - explosion(src.loc, 5, 10, 15, 20, 0) - -/obj/effect/meteor/tunguska/Bump() - ..() - if(prob(20)) - explosion(src.loc,2,4,6,8) - - -//Gore -/obj/effect/meteor/gore - name = "Oraganic Debris" - icon = 'icons/mob/human.dmi' - icon_state = "body_m_s" - hits = 1 - hitpwr = 0 - meteorsound = 'sound/effects/blobattack.ogg' - meteordrop = /obj/item/reagent_containers/food/snacks/meat - var/meteorgibs = /obj/effect/gibspawner/generic - -/obj/effect/meteor/gore/make_debris() - ..() - new meteorgibs(get_turf(src)) - - -/obj/effect/meteor/gore/ram_turf(turf/T) - if(!istype(T, /turf/space)) - new /obj/effect/decal/cleanable/blood(T) - -/obj/effect/meteor/gore/Bump(atom/A) - A.ex_act(hitpwr) - get_hit() - -//Meteor Ops -/obj/effect/meteor/goreops - name = "MeteorOps" - icon = 'icons/mob/animal.dmi' - icon_state = "syndicaterangedpsace" - hits = 10 - hitpwr = 1 - meteorsound = 'sound/effects/blobattack.ogg' - meteordrop = /obj/item/reagent_containers/food/snacks/meat - var/meteorgibs = /obj/effect/gibspawner/generic - -/obj/effect/meteor/goreops/make_debris() - ..() - new meteorgibs(get_turf(src)) - - -/obj/effect/meteor/goreops/ram_turf(turf/T) - if(!istype(T, /turf/space)) - new /obj/effect/decal/cleanable/blood(T) - -/obj/effect/meteor/goreops/Bump(atom/A) - A.ex_act(hitpwr) - get_hit() +//Meteors probability of spawning during a given wave +/var/list/meteors_normal = list(/obj/effect/meteor/dust=3, /obj/effect/meteor/medium=8, /obj/effect/meteor/big=3, \ + /obj/effect/meteor/flaming=1, /obj/effect/meteor/irradiated=3) //for normal meteor event + +/var/list/meteors_threatening = list(/obj/effect/meteor/medium=4, /obj/effect/meteor/big=8, \ + /obj/effect/meteor/flaming=3, /obj/effect/meteor/irradiated=3) //for threatening meteor event + +/var/list/meteors_catastrophic = list(/obj/effect/meteor/medium=5, /obj/effect/meteor/big=75, \ + /obj/effect/meteor/flaming=10, /obj/effect/meteor/irradiated=10, /obj/effect/meteor/tunguska = 1) //for catastrophic meteor event + +/var/list/meteors_dust = list(/obj/effect/meteor/dust) //for space dust event + +/var/list/meteors_gore = list(/obj/effect/meteor/gore) //Meaty Gore + +/var/list/meteors_ops = list(/obj/effect/meteor/goreops) //Meaty Ops + + +/////////////////////////////// +//Meteor spawning global procs +/////////////////////////////// +/proc/spawn_meteors(var/number = 10, var/list/meteortypes) + for(var/i = 0; i < number; i++) + spawn_meteor(meteortypes) + +/proc/spawn_meteor(var/list/meteortypes) + var/turf/pickedstart + var/turf/pickedgoal + var/max_i = 10//number of tries to spawn meteor. + while(!istype(pickedstart, /turf/space)) + var/startSide = pick(cardinal) + pickedstart = spaceDebrisStartLoc(startSide, 1) + pickedgoal = spaceDebrisFinishLoc(startSide, 1) + max_i-- + if(max_i<=0) + return + var/Me = pickweight(meteortypes) + var/obj/effect/meteor/M = new Me(pickedstart) + M.dest = pickedgoal + M.z_original = level_name_to_num(MAIN_STATION) + spawn(0) + walk_towards(M, M.dest, 1) + return + +/proc/spaceDebrisStartLoc(startSide, Z) + var/starty + var/startx + switch(startSide) + if(NORTH) + starty = world.maxy-(TRANSITIONEDGE+1) + startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) + if(EAST) + starty = rand((TRANSITIONEDGE+1),world.maxy-(TRANSITIONEDGE+1)) + startx = world.maxx-(TRANSITIONEDGE+1) + if(SOUTH) + starty = (TRANSITIONEDGE+1) + startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) + if(WEST) + starty = rand((TRANSITIONEDGE+1), world.maxy-(TRANSITIONEDGE+1)) + startx = (TRANSITIONEDGE+1) + var/turf/T = locate(startx, starty, Z) + return T + +/proc/spaceDebrisFinishLoc(startSide, Z) + var/endy + var/endx + switch(startSide) + if(NORTH) + endy = TRANSITIONEDGE + endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) + if(EAST) + endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE) + endx = TRANSITIONEDGE + if(SOUTH) + endy = world.maxy-TRANSITIONEDGE + endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE) + if(WEST) + endy = rand(TRANSITIONEDGE,world.maxy-TRANSITIONEDGE) + endx = world.maxx-TRANSITIONEDGE + var/turf/T = locate(endx, endy, Z) + return T + +/////////////////////// +//The meteor effect +////////////////////// + +/obj/effect/meteor + name = "the concept of meteor" + desc = "You should probably run instead of gawking at this." + icon = 'icons/obj/meteor.dmi' + icon_state = "small" + density = 1 + anchored = 1 + var/hits = 4 + var/hitpwr = 2 //Level of ex_act to be called on hit. + var/dest + pass_flags = PASSTABLE + var/heavy = 0 + var/meteorsound = 'sound/effects/meteorimpact.ogg' + var/z_original = 1 + + var/meteordrop = /obj/item/stack/ore/iron + var/dropamt = 2 + +/obj/effect/meteor/Move() + if(z != z_original || loc == dest) + qdel(src) + return + + . = ..() //process movement... + + if(.)//.. if did move, ram the turf we get in + var/turf/T = get_turf(loc) + ram_turf(T) + + if(prob(10) && !istype(T, /turf/space))//randomly takes a 'hit' from ramming + get_hit() + + return . + +/obj/effect/meteor/Destroy() + GLOB.meteor_list -= src + walk(src,0) //this cancels the walk_towards() proc + return ..() + +/obj/effect/meteor/New() + ..() + GLOB.meteor_list += src + SpinAnimation() + +/obj/effect/meteor/Bump(atom/A) + if(A) + ram_turf(get_turf(A)) + playsound(src.loc, meteorsound, 40, 1) + get_hit() + +/obj/effect/meteor/CanPass(atom/movable/mover, turf/target, height=0) + return istype(mover, /obj/effect/meteor) ? 1 : ..() + +/obj/effect/meteor/proc/ram_turf(var/turf/T) + //first bust whatever is in the turf + for(var/atom/A in T) + if(A != src) + A.ex_act(hitpwr) + + //then, ram the turf if it still exists + if(T) + T.ex_act(hitpwr) + +//process getting 'hit' by colliding with a dense object +//or randomly when ramming turfs +/obj/effect/meteor/proc/get_hit() + hits-- + if(hits <= 0) + make_debris() + meteor_effect(heavy) + qdel(src) + +/obj/effect/meteor/ex_act() + return + +/obj/effect/meteor/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/pickaxe)) + make_debris() + qdel(src) + return + return ..() + +/obj/effect/meteor/proc/make_debris() + for(var/throws = dropamt, throws > 0, throws--) + var/obj/item/O = new meteordrop(get_turf(src)) + O.throw_at(dest, 5, 10) + +/obj/effect/meteor/proc/meteor_effect(var/sound=1) + if(sound) + var/sound/meteor_sound = sound(meteorsound) + var/random_frequency = get_rand_frequency() + + for(var/P in GLOB.player_list) + var/mob/M = P + var/turf/T = get_turf(M) + if(!T || T.z != src.z) + continue + var/dist = get_dist(M.loc, src.loc) + if(prob(50)) + shake_camera(M, dist > 20 ? 3 : 5, dist > 20 ? 1 : 3) + M.playsound_local(src.loc, null, 50, 1, random_frequency, 10, S = meteor_sound) + +/////////////////////// +//Meteor types +/////////////////////// + +//Dust +/obj/effect/meteor/dust + name = "space dust" + icon_state = "dust" + pass_flags = PASSTABLE | PASSGRILLE + hits = 1 + hitpwr = 3 + meteorsound = 'sound/weapons/tap.ogg' + meteordrop = /obj/item/stack/ore/glass + +//Medium-sized +/obj/effect/meteor/medium + name = "meteor" + dropamt = 3 + +/obj/effect/meteor/medium/meteor_effect() + ..(heavy) + explosion(src.loc, 0, 1, 2, 3, 0) + +//Large-sized +/obj/effect/meteor/big + name = "large meteor" + icon_state = "large" + hits = 6 + heavy = 1 + dropamt = 4 + +/obj/effect/meteor/big/meteor_effect() + ..(heavy) + explosion(src.loc, 1, 2, 3, 4, 0) + +//Flaming meteor +/obj/effect/meteor/flaming + name = "flaming meteor" + icon_state = "flaming" + hits = 5 + heavy = 1 + meteorsound = 'sound/effects/bamf.ogg' + meteordrop = /obj/item/stack/ore/plasma + +/obj/effect/meteor/flaming/meteor_effect() + ..(heavy) + explosion(src.loc, 1, 2, 3, 4, 0, 0, flame_range = 5) + +//Radiation meteor +/obj/effect/meteor/irradiated + name = "glowing meteor" + icon_state = "glowing" + heavy = 1 + meteordrop = /obj/item/stack/ore/uranium + + +/obj/effect/meteor/irradiated/meteor_effect() + ..(heavy) + explosion(src.loc, 0, 0, 4, 3, 0) + new /obj/effect/decal/cleanable/greenglow(get_turf(src)) + for(var/mob/living/L in view(5, src)) + L.apply_effect(40, IRRADIATE) + +//Station buster Tunguska +/obj/effect/meteor/tunguska + name = "tunguska meteor" + icon_state = "flaming" + desc = "Your life briefly passes before your eyes the moment you lay them on this monstruosity." + hits = 30 + hitpwr = 1 + heavy = 1 + meteorsound = 'sound/effects/bamf.ogg' + meteordrop = /obj/item/stack/ore/plasma + +/obj/effect/meteor/tunguska/meteor_effect() + ..(heavy) + explosion(src.loc, 5, 10, 15, 20, 0) + +/obj/effect/meteor/tunguska/Bump() + ..() + if(prob(20)) + explosion(src.loc,2,4,6,8) + + +//Gore +/obj/effect/meteor/gore + name = "Oraganic Debris" + icon = 'icons/mob/human.dmi' + icon_state = "body_m_s" + hits = 1 + hitpwr = 0 + meteorsound = 'sound/effects/blobattack.ogg' + meteordrop = /obj/item/reagent_containers/food/snacks/meat + var/meteorgibs = /obj/effect/gibspawner/generic + +/obj/effect/meteor/gore/make_debris() + ..() + new meteorgibs(get_turf(src)) + + +/obj/effect/meteor/gore/ram_turf(turf/T) + if(!istype(T, /turf/space)) + new /obj/effect/decal/cleanable/blood(T) + +/obj/effect/meteor/gore/Bump(atom/A) + A.ex_act(hitpwr) + get_hit() + +//Meteor Ops +/obj/effect/meteor/goreops + name = "MeteorOps" + icon = 'icons/mob/animal.dmi' + icon_state = "syndicaterangedpsace" + hits = 10 + hitpwr = 1 + meteorsound = 'sound/effects/blobattack.ogg' + meteordrop = /obj/item/reagent_containers/food/snacks/meat + var/meteorgibs = /obj/effect/gibspawner/generic + +/obj/effect/meteor/goreops/make_debris() + ..() + new meteorgibs(get_turf(src)) + + +/obj/effect/meteor/goreops/ram_turf(turf/T) + if(!istype(T, /turf/space)) + new /obj/effect/decal/cleanable/blood(T) + +/obj/effect/meteor/goreops/Bump(atom/A) + A.ex_act(hitpwr) + get_hit() diff --git a/code/game/gamemodes/miniantags/abduction/abductee_objectives.dm b/code/game/gamemodes/miniantags/abduction/abductee_objectives.dm index 5db63d3450d..3859d68accb 100644 --- a/code/game/gamemodes/miniantags/abduction/abductee_objectives.dm +++ b/code/game/gamemodes/miniantags/abduction/abductee_objectives.dm @@ -144,4 +144,4 @@ explanation_text = "You are doomed to feel woefully incomplete forever... until you find your true love on this station. They're waiting for you!" /datum/objective/abductee/sixthsense - explanation_text = "You died back there and went to heaven... or is it hell? No one here seems to know they're dead. Convince them, and maybe you can escape this limbo." \ No newline at end of file + explanation_text = "You died back there and went to heaven... or is it hell? No one here seems to know they're dead. Convince them, and maybe you can escape this limbo." diff --git a/code/game/gamemodes/miniantags/abduction/abduction.dm b/code/game/gamemodes/miniantags/abduction/abduction.dm index 9fce3cc6e24..ea22ba9f54e 100644 --- a/code/game/gamemodes/miniantags/abduction/abduction.dm +++ b/code/game/gamemodes/miniantags/abduction/abduction.dm @@ -208,12 +208,14 @@ text += "
    The abductors were:
    " for(var/datum/mind/abductor_mind in abductors) text += printplayer(abductor_mind) + text += "
    " text += printobjectives(abductor_mind) text += "
    " if(abductees.len) text += "
    The abductees were:
    " for(var/datum/mind/abductee_mind in abductees) text += printplayer(abductee_mind) + text += "
    " text += printobjectives(abductee_mind) text += "
    " to_chat(world, text) @@ -279,4 +281,4 @@ /datum/game_mode/proc/update_abductor_icons_removed(datum/mind/alien_mind) var/datum/atom_hud/antag/hud = huds[ANTAG_HUD_ABDUCTOR] hud.leave_hud(alien_mind.current) - set_antag_hud(alien_mind.current, null) \ No newline at end of file + set_antag_hud(alien_mind.current, null) diff --git a/code/game/gamemodes/miniantags/abduction/abduction_gear.dm b/code/game/gamemodes/miniantags/abduction/abduction_gear.dm index 962da01b74d..c27b230b3b2 100644 --- a/code/game/gamemodes/miniantags/abduction/abduction_gear.dm +++ b/code/game/gamemodes/miniantags/abduction/abduction_gear.dm @@ -587,10 +587,8 @@ Congratulations! You are now trained for invasive xenobiology research!"} ..() make_syndie() -/obj/item/radio/headset/abductor/attackby(obj/item/I, mob/user, params) - if(isscrewdriver(I)) - return // Stops humans from disassembling abductor headsets. - return ..() +/obj/item/radio/headset/abductor/screwdriver_act() + return// Stops humans from disassembling abductor headsets. /obj/item/scalpel/alien name = "alien scalpel" @@ -683,15 +681,6 @@ Congratulations! You are now trained for invasive xenobiology research!"} density = TRUE /obj/structure/table_frame/abductor/attackby(obj/item/I, mob/user, params) - if(iswrench(I)) - to_chat(user, "You start disassembling [src]...") - playsound(loc, I.usesound, 50, 1) - if(do_after(user, 30*I.toolspeed, target = src)) - playsound(loc, 'sound/items/deconstruct.ogg', 50, 1) - for(var/i = 1, i <= framestackamount, i++) - new framestack(get_turf(src)) - qdel(src) - return if(istype(I, /obj/item/stack/sheet/mineral/abductor)) var/obj/item/stack/sheet/P = I if(P.get_amount() < 1) diff --git a/code/game/gamemodes/miniantags/abduction/abduction_outfits.dm b/code/game/gamemodes/miniantags/abduction/abduction_outfits.dm index c90aaac19d8..6478a997b76 100644 --- a/code/game/gamemodes/miniantags/abduction/abduction_outfits.dm +++ b/code/game/gamemodes/miniantags/abduction/abduction_outfits.dm @@ -59,4 +59,4 @@ ..() if(!visualsOnly) var/obj/item/implant/abductor/beamplant = new /obj/item/implant/abductor(H) - beamplant.implant(H) \ No newline at end of file + beamplant.implant(H) diff --git a/code/game/gamemodes/miniantags/abduction/gland.dm b/code/game/gamemodes/miniantags/abduction/gland.dm index 419b53d50a3..23d9792ca4c 100644 --- a/code/game/gamemodes/miniantags/abduction/gland.dm +++ b/code/game/gamemodes/miniantags/abduction/gland.dm @@ -346,5 +346,5 @@ owner.visible_message("[owner] vomits a cloud of plasma!") var/turf/simulated/T = get_turf(owner) if(istype(T)) - T.atmos_spawn_air(SPAWN_TOXINS|SPAWN_20C,50) + T.atmos_spawn_air(LINDA_SPAWN_TOXINS|LINDA_SPAWN_20C,50) owner.vomit() diff --git a/code/game/gamemodes/miniantags/abduction/machinery/console.dm b/code/game/gamemodes/miniantags/abduction/machinery/console.dm index b162103c88b..17ebcbdd270 100644 --- a/code/game/gamemodes/miniantags/abduction/machinery/console.dm +++ b/code/game/gamemodes/miniantags/abduction/machinery/console.dm @@ -221,4 +221,4 @@ else new item(src.loc) else - atom_say("Insufficent data!") \ No newline at end of file + atom_say("Insufficent data!") diff --git a/code/game/gamemodes/miniantags/abduction/machinery/dispenser.dm b/code/game/gamemodes/miniantags/abduction/machinery/dispenser.dm index 1a377667650..5964c6f379b 100644 --- a/code/game/gamemodes/miniantags/abduction/machinery/dispenser.dm +++ b/code/game/gamemodes/miniantags/abduction/machinery/dispenser.dm @@ -83,4 +83,4 @@ if(amounts[count]>0) amounts[count]-- var/T = gland_types[count] - new T(get_turf(src)) \ No newline at end of file + new T(get_turf(src)) diff --git a/code/game/gamemodes/miniantags/abduction/machinery/pad.dm b/code/game/gamemodes/miniantags/abduction/machinery/pad.dm index 697d173716a..85c120186e4 100644 --- a/code/game/gamemodes/miniantags/abduction/machinery/pad.dm +++ b/code/game/gamemodes/miniantags/abduction/machinery/pad.dm @@ -48,4 +48,4 @@ /obj/effect/temp_visual/teleport_abductor/New() do_sparks(10, 0, loc) - ..() \ No newline at end of file + ..() diff --git a/code/game/gamemodes/miniantags/borer/borer_html.dm b/code/game/gamemodes/miniantags/borer/borer_html.dm index 415438884e9..8b6e19e6226 100644 --- a/code/game/gamemodes/miniantags/borer/borer_html.dm +++ b/code/game/gamemodes/miniantags/borer/borer_html.dm @@ -66,4 +66,4 @@
    [content]
    "} - return html \ No newline at end of file + return html diff --git a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm index 63905bc3390..defa0de633d 100644 --- a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm +++ b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm @@ -41,15 +41,17 @@ return to_chat(user, "Picking up the swarmer may cause it to activate. You should be careful about this.") -/obj/effect/mob_spawn/swarmer/attackby(obj/item/I, mob/user, params) - if(isscrewdriver(I) && user.a_intent != INTENT_HARM) - user.visible_message("[usr.name] deactivates [src].", - "After some fiddling, you find a way to disable [src]'s power source.", - "You hear clicking.") - new /obj/item/deactivated_swarmer(get_turf(src)) - qdel(src) - else - return ..() +/obj/effect/mob_spawn/swarmer/screwdriver_act(mob/user, obj/item/I) + if(user.a_intent == INTENT_HARM) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + user.visible_message("[usr.name] deactivates [src].", + "After some fiddling, you find a way to disable [src]'s power source.", + "You hear clicking.") + new /obj/item/deactivated_swarmer(get_turf(src)) + qdel(src) ////The Mob itself//// @@ -687,4 +689,4 @@ if(message) for(var/mob/M in GLOB.mob_list) if(isswarmer(M) || (M in GLOB.dead_mob_list)) - to_chat(M, "Swarm communication - [src] states: [message]") \ No newline at end of file + to_chat(M, "Swarm communication - [src] states: [message]") diff --git a/code/game/gamemodes/miniantags/guardian/guardian.dm b/code/game/gamemodes/miniantags/guardian/guardian.dm index 17d7187f439..387cd03aceb 100644 --- a/code/game/gamemodes/miniantags/guardian/guardian.dm +++ b/code/game/gamemodes/miniantags/guardian/guardian.dm @@ -271,6 +271,7 @@ var/used = FALSE var/theme = "magic" var/mob_name = "Guardian Spirit" + var/confirmation_message = "The cards are still unused. Do you wish to use them?" var/use_message = "You shuffle the deck..." var/used_message = "All the cards seem to be blank now." var/failure_message = "..And draw a card! It's...blank? Maybe you should try again later." @@ -295,6 +296,10 @@ if(used == TRUE) to_chat(user, "[used_message]") return + var/choice = alert(user, "[confirmation_message]",, "Yes", "No") + if(choice == "No") + to_chat(user, "You decide against using the [name].") + return used = TRUE to_chat(user, "[use_message]") var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as the [mob_name] of [user.real_name]?", ROLE_GUARDIAN, 0, 100) @@ -384,6 +389,7 @@ icon_state = "combat_hypo" theme = "tech" mob_name = "Holoparasite" + confirmation_message = "The injector still contains holoparasites. Do you wish to use it?" use_message = "You start to power on the injector..." used_message = "The injector has already been used." failure_message = "...ERROR. BOOT SEQUENCE ABORTED. AI FAILED TO INTIALIZE. PLEASE CONTACT SUPPORT OR TRY AGAIN LATER." @@ -424,6 +430,7 @@ theme = "bio" mob_name = "Scarab Swarm" use_message = "The eggs begin to twitch..." + confirmation_message = "These eggs are still dormant. Do you wish to activate them?" used_message = "The cluster already hatched." failure_message = "...but soon settles again. Guess they weren't ready to hatch after all." color_list = list("Rose" = "#F62C6B", diff --git a/code/game/gamemodes/miniantags/guardian/types/assassin.dm b/code/game/gamemodes/miniantags/guardian/types/assassin.dm index 4b17e597aa5..a12fce36e82 100644 --- a/code/game/gamemodes/miniantags/guardian/types/assassin.dm +++ b/code/game/gamemodes/miniantags/guardian/types/assassin.dm @@ -90,4 +90,4 @@ clear_alert("instealth") instealthalert = null clear_alert("canstealth") - canstealthalert = null \ No newline at end of file + canstealthalert = null diff --git a/code/game/gamemodes/miniantags/guardian/types/charger.dm b/code/game/gamemodes/miniantags/guardian/types/charger.dm index 17ed569f256..3ea6dbe5f30 100644 --- a/code/game/gamemodes/miniantags/guardian/types/charger.dm +++ b/code/game/gamemodes/miniantags/guardian/types/charger.dm @@ -69,4 +69,4 @@ shake_camera(L, 4, 3) shake_camera(src, 2, 3) - charging = 0 \ No newline at end of file + charging = 0 diff --git a/code/game/gamemodes/miniantags/guardian/types/lightning.dm b/code/game/gamemodes/miniantags/guardian/types/lightning.dm index 13b9b67f0a9..bf84e199bab 100644 --- a/code/game/gamemodes/miniantags/guardian/types/lightning.dm +++ b/code/game/gamemodes/miniantags/guardian/types/lightning.dm @@ -105,4 +105,4 @@ "You hear a heavy electrical crack." \ ) L.adjustFireLoss(1.2) //adds up very rapidly - . = 1 \ No newline at end of file + . = 1 diff --git a/code/game/gamemodes/miniantags/guardian/types/protector.dm b/code/game/gamemodes/miniantags/guardian/types/protector.dm index 5fb31b8ba15..534b7e16934 100644 --- a/code/game/gamemodes/miniantags/guardian/types/protector.dm +++ b/code/game/gamemodes/miniantags/guardian/types/protector.dm @@ -55,4 +55,4 @@ summoner.visible_message("[summoner] jumps back to [summoner.p_their()] protector.") new /obj/effect/temp_visual/guardian/phase/out(get_turf(summoner)) summoner.forceMove(get_turf(src)) - new /obj/effect/temp_visual/guardian/phase(get_turf(summoner))//Protector \ No newline at end of file + new /obj/effect/temp_visual/guardian/phase(get_turf(summoner))//Protector diff --git a/code/game/gamemodes/miniantags/guardian/types/standard.dm b/code/game/gamemodes/miniantags/guardian/types/standard.dm index dc85a48a480..e6d95ffb92c 100644 --- a/code/game/gamemodes/miniantags/guardian/types/standard.dm +++ b/code/game/gamemodes/miniantags/guardian/types/standard.dm @@ -45,4 +45,4 @@ playstyle_string = "As a standard type you have no special abilities, but have a high damage resistance and a powerful attack capable of smashing through walls." environment_smash = 2 battlecry = "URK" - adminseal = TRUE \ No newline at end of file + adminseal = TRUE diff --git a/code/game/gamemodes/miniantags/morph/morph_event.dm b/code/game/gamemodes/miniantags/morph/morph_event.dm index e80374e2dca..28183e7d22e 100644 --- a/code/game/gamemodes/miniantags/morph/morph_event.dm +++ b/code/game/gamemodes/miniantags/morph/morph_event.dm @@ -29,4 +29,4 @@ return 1 /datum/event/spawn_morph/start() - get_morph() \ No newline at end of file + get_morph() diff --git a/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm b/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm index 437e7637a77..1f902a51806 100644 --- a/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm +++ b/code/game/gamemodes/miniantags/revenant/revenant_spawn_event.dm @@ -52,4 +52,4 @@ /datum/event/revenant/start() - get_revenant() \ No newline at end of file + get_revenant() diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm index 2d7c9cff015..bdc5f718ac6 100644 --- a/code/game/gamemodes/nuclear/nuclear.dm +++ b/code/game/gamemodes/nuclear/nuclear.dm @@ -1,536 +1,536 @@ -#define NUKESCALINGMODIFIER 1.2 - -/datum/game_mode - var/list/datum/mind/syndicates = list() - -proc/issyndicate(mob/living/M as mob) - return istype(M) && M.mind && SSticker && SSticker.mode && (M.mind in SSticker.mode.syndicates) - -/datum/game_mode/nuclear - name = "nuclear emergency" - config_tag = "nuclear" - required_players = 30 // 30 players - 5 players to be the nuke ops = 25 players remaining - required_enemies = 5 - recommended_enemies = 5 - - var/const/agents_possible = 5 //If we ever need more syndicate agents. - - var/nukes_left = 1 //Call 3714-PRAY right now and order more nukes! Limited offer! - var/nuke_off_station = 0 //Used for tracking if the syndies actually haul the nuke to the station - var/syndies_didnt_escape = 0 //Used for tracking if the syndies got the shuttle off of the z-level - var/total_tc = 0 //Total amount of telecrystals shared between nuke ops - -/datum/game_mode/nuclear/announce() - to_chat(world, "The current game mode is - Nuclear Emergency!") - to_chat(world, "A [syndicate_name()] Strike Force is approaching [station_name()]!") - to_chat(world, "A nuclear explosive was being transported by Nanotrasen to a military base. The transport ship mysteriously lost contact with Space Traffic Control (STC). About that time a strange disk was discovered around [station_name()]. It was identified by Nanotrasen as a nuclear authentication disk and now Syndicate Operatives have arrived to retake the disk and detonate SS13! There are most likely Syndicate starships are in the vicinity, so take care not to lose the disk!\nSyndicate: Reclaim the disk and detonate the nuclear bomb anywhere on SS13.\nPersonnel: Hold the disk and escape with the disk on the shuttle!") - -/datum/game_mode/nuclear/can_start()//This could be better, will likely have to recode it later - if(!..()) - return 0 - - var/list/possible_syndicates = get_players_for_role(ROLE_OPERATIVE) - var/agent_number = 0 - - if(possible_syndicates.len < 1) - return 0 - - if(LAZYLEN(possible_syndicates) > agents_possible) - agent_number = agents_possible - else - agent_number = possible_syndicates.len - - var/n_players = num_players() - if(agent_number > n_players) - agent_number = n_players/2 - - while(agent_number > 0) - var/datum/mind/new_syndicate = pick(possible_syndicates) - syndicates += new_syndicate - possible_syndicates -= new_syndicate //So it doesn't pick the same guy each time. - agent_number-- - - for(var/datum/mind/synd_mind in syndicates) - synd_mind.assigned_role = SPECIAL_ROLE_NUKEOPS //So they aren't chosen for other jobs. - synd_mind.special_role = SPECIAL_ROLE_NUKEOPS - synd_mind.offstation_role = TRUE - return 1 - - -/datum/game_mode/nuclear/pre_setup() - ..() - return 1 - -/datum/game_mode/proc/remove_operative(datum/mind/operative_mind) - if(operative_mind in syndicates) - SSticker.mode.syndicates -= operative_mind - operative_mind.special_role = null - for(var/datum/objective/nuclear/O in operative_mind.objectives) - operative_mind.objectives -= O - operative_mind.current.create_attack_log("No longer nuclear operative") - if(issilicon(operative_mind.current)) - to_chat(operative_mind.current, "You have been turned into a robot! You are no longer a Syndicate operative.") - else - to_chat(operative_mind.current, "You have been brainwashed! You are no longer a Syndicate operative.") - SSticker.mode.update_synd_icons_removed(operative_mind) - -//////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////// - -/datum/game_mode/proc/update_synd_icons_added(datum/mind/synd_mind) - var/datum/atom_hud/antag/opshud = huds[ANTAG_HUD_OPS] - opshud.join_hud(synd_mind.current) - set_antag_hud(synd_mind.current, "hudoperative") - -/datum/game_mode/proc/update_synd_icons_removed(datum/mind/synd_mind) - var/datum/atom_hud/antag/opshud = huds[ANTAG_HUD_OPS] - opshud.leave_hud(synd_mind.current) - set_antag_hud(synd_mind.current, null) - -//////////////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////////////// - -/datum/game_mode/nuclear/post_setup() - - var/list/turf/synd_spawn = list() - - for(var/obj/effect/landmark/A in GLOB.landmarks_list) - if(A.name == "Syndicate-Spawn") - synd_spawn += get_turf(A) - qdel(A) - continue - - var/obj/effect/landmark/nuke_spawn = locate("landmark*Nuclear-Bomb") - - var/nuke_code = "[rand(10000, 99999)]" - var/leader_selected = 0 - var/agent_number = 1 - var/spawnpos = 1 - - for(var/datum/mind/synd_mind in syndicates) - if(spawnpos > synd_spawn.len) - spawnpos = 2 - synd_mind.current.loc = synd_spawn[spawnpos] - - forge_syndicate_objectives(synd_mind) - create_syndicate(synd_mind) - greet_syndicate(synd_mind) - equip_syndicate(synd_mind.current) - - if(!leader_selected) - prepare_syndicate_leader(synd_mind, nuke_code) - leader_selected = 1 - else - synd_mind.current.real_name = "[syndicate_name()] Operative #[agent_number]" - update_syndicate_id(synd_mind, FALSE) - - agent_number++ - spawnpos++ - update_synd_icons_added(synd_mind) - - scale_telecrystals() - share_telecrystals() - if(nuke_spawn && synd_spawn.len > 0) - var/obj/machinery/nuclearbomb/syndicate/the_bomb = new /obj/machinery/nuclearbomb/syndicate(nuke_spawn.loc) - the_bomb.r_code = nuke_code - - return ..() - -/datum/game_mode/nuclear/proc/scale_telecrystals() - var/danger - danger = GLOB.player_list.len - while(!IsMultiple(++danger, 10)) //Increments danger up to the nearest multiple of ten - - total_tc += danger * NUKESCALINGMODIFIER - -/datum/game_mode/nuclear/proc/share_telecrystals() - var/player_tc - var/remainder - - player_tc = round(total_tc / GLOB.nuclear_uplink_list.len) //round to get an integer and not floating point - remainder = total_tc % GLOB.nuclear_uplink_list.len - - for(var/obj/item/radio/uplink/nuclear/U in GLOB.nuclear_uplink_list) - U.hidden_uplink.uses += player_tc - while(remainder > 0) - for(var/obj/item/radio/uplink/nuclear/U in GLOB.nuclear_uplink_list) - if(remainder <= 0) - break - U.hidden_uplink.uses++ - remainder-- - -/datum/game_mode/proc/create_syndicate(datum/mind/synd_mind) // So we don't have inferior species as ops - randomize a human - var/mob/living/carbon/human/M = synd_mind.current - - M.set_species(/datum/species/human, TRUE) - M.dna.ready_dna(M) // Quadriplegic Nuke Ops won't be participating in the paralympics - M.dna.species.create_organs(M) - M.cleanSE() //No fat/blind/colourblind/epileptic/whatever ops. - M.overeatduration = 0 - M.flavor_text = null - - var/obj/item/organ/external/head/head_organ = M.get_organ("head") - var/hair_c = pick("#8B4513","#000000","#FF4500","#FFD700") // Brown, black, red, blonde - var/eye_c = pick("#000000","#8B4513","1E90FF") // Black, brown, blue - var/skin_tone = pick(-50, -30, -10, 0, 0, 0, 10) // Caucasian/black - head_organ.facial_colour = hair_c - head_organ.sec_facial_colour = hair_c - head_organ.hair_colour = hair_c - head_organ.sec_hair_colour = hair_c - M.change_eye_color(eye_c) - M.s_tone = skin_tone - head_organ.h_style = random_hair_style(M.gender, head_organ.dna.species.name) - head_organ.f_style = random_facial_hair_style(M.gender, head_organ.dna.species.name) - M.body_accessory = null - M.regenerate_icons() - M.update_body() - -/datum/game_mode/proc/prepare_syndicate_leader(var/datum/mind/synd_mind, var/nuke_code) - var/leader_title = pick("Czar", "Boss", "Commander", "Chief", "Kingpin", "Director", "Overlord") - synd_mind.current.real_name = "[syndicate_name()] Team [leader_title]" - to_chat(synd_mind.current, "You are the Syndicate leader for this mission. You are responsible for the distribution of telecrystals and your ID is the only one who can open the launch bay doors.") - to_chat(synd_mind.current, "If you feel you are not up to this task, give your ID to another operative.") - to_chat(synd_mind.current, "In your hand you will find a special item capable of triggering a greater challenge for your team. Examine it carefully and consult with your fellow operatives before activating it.") - - var/obj/item/nuclear_challenge/challenge = new /obj/item/nuclear_challenge - synd_mind.current.equip_to_slot_or_del(challenge, slot_r_hand) - - update_syndicate_id(synd_mind, leader_title, TRUE) - - if(nuke_code) - synd_mind.store_memory("Syndicate Nuclear Bomb Code: [nuke_code]", 0, 0) - to_chat(synd_mind.current, "The nuclear authorization code is: [nuke_code]") - var/obj/item/paper/P = new - P.info = "The nuclear authorization code is: [nuke_code]" - P.name = "nuclear bomb code" - var/obj/item/stamp/syndicate/stamp = new - P.stamp(stamp) - qdel(stamp) - - if(SSticker.mode.config_tag=="nuclear") - P.loc = synd_mind.current.loc - else - var/mob/living/carbon/human/H = synd_mind.current - P.loc = H.loc - H.equip_to_slot_or_del(P, slot_r_store, 0) - H.update_icons() - - else - nuke_code = "code will be provided later" - -/datum/game_mode/proc/update_syndicate_id(var/datum/mind/synd_mind, is_leader = FALSE) - var/list/found_ids = synd_mind.current.search_contents_for(/obj/item/card/id) - - if(LAZYLEN(found_ids)) - for(var/obj/item/card/id/ID in found_ids) - ID.name = "[synd_mind.current.real_name] ID card" - ID.registered_name = synd_mind.current.real_name - if(is_leader) - ID.access += access_syndicate_leader - else - message_admins("Warning: Operative [key_name_admin(synd_mind.current)] spawned without an ID card!") - -/datum/game_mode/proc/forge_syndicate_objectives(var/datum/mind/syndicate) - var/datum/objective/nuclear/syndobj = new - syndobj.owner = syndicate - syndicate.objectives += syndobj - - -/datum/game_mode/proc/greet_syndicate(var/datum/mind/syndicate, var/you_are=1) - SEND_SOUND(syndicate.current, 'sound/ambience/antag/ops.ogg') - if(you_are) - to_chat(syndicate.current, "You are a [syndicate_name()] agent!") - var/obj_count = 1 - for(var/datum/objective/objective in syndicate.objectives) - to_chat(syndicate.current, "Objective #[obj_count]: [objective.explanation_text]") - obj_count++ - return - - -/datum/game_mode/proc/random_radio_frequency() - return 1337 // WHY??? -- Doohl - - -/datum/game_mode/proc/equip_syndicate(mob/living/carbon/human/synd_mob, uplink_uses = 20) - var/radio_freq = SYND_FREQ - - var/obj/item/radio/R = new /obj/item/radio/headset/syndicate/alt(synd_mob) - R.set_frequency(radio_freq) - synd_mob.equip_to_slot_or_del(R, slot_l_ear) - - synd_mob.equip_to_slot_or_del(new /obj/item/clothing/under/syndicate(synd_mob), slot_w_uniform) - synd_mob.equip_to_slot_or_del(new /obj/item/clothing/shoes/combat(synd_mob), slot_shoes) - synd_mob.equip_or_collect(new /obj/item/clothing/gloves/combat(synd_mob), slot_gloves) - synd_mob.equip_to_slot_or_del(new /obj/item/card/id/syndicate(synd_mob), slot_wear_id) - synd_mob.equip_to_slot_or_del(new /obj/item/storage/backpack(synd_mob), slot_back) - synd_mob.equip_to_slot_or_del(new /obj/item/gun/projectile/automatic/pistol(synd_mob), slot_belt) - synd_mob.equip_to_slot_or_del(new /obj/item/storage/box/survival_syndi(synd_mob.back), slot_in_backpack) - synd_mob.equip_to_slot_or_del(new /obj/item/pinpointer/nukeop(synd_mob), slot_wear_pda) - var/obj/item/radio/uplink/nuclear/U = new /obj/item/radio/uplink/nuclear(synd_mob) - U.hidden_uplink.uplink_owner="[synd_mob.key]" - U.hidden_uplink.uses = uplink_uses - synd_mob.equip_to_slot_or_del(U, slot_in_backpack) - - if(synd_mob.dna.species) - - /* - Incase anyone ever gets the burning desire to have nukeops with randomized apperances. -- Dave - synd_mob.gender = pick(MALE, FEMALE) // Randomized appearances for the nukeops. - var/datum/preferences/pref = new() - A.randomize_appearance_for(synd_mob) - */ - - var/race = synd_mob.dna.species.name - - switch(race) - if("Vox" || "Vox Armalis") - synd_mob.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/syndicate(synd_mob), slot_wear_mask) - synd_mob.equip_to_slot_or_del(new /obj/item/tank/emergency_oxygen/vox(synd_mob), slot_l_hand) - synd_mob.internal = synd_mob.l_hand - synd_mob.update_action_buttons_icon() - - if("Plasmaman") - synd_mob.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/syndicate(synd_mob), slot_wear_mask) - synd_mob.equip_or_collect(new /obj/item/tank/plasma/plasmaman(synd_mob), slot_s_store) - synd_mob.equip_or_collect(new /obj/item/extinguisher_refill(synd_mob), slot_in_backpack) - synd_mob.equip_or_collect(new /obj/item/extinguisher_refill(synd_mob), slot_in_backpack) - synd_mob.internal = synd_mob.get_item_by_slot(slot_s_store) - synd_mob.update_action_buttons_icon() - - synd_mob.rejuvenate() //fix any damage taken by naked vox/plasmamen/etc while round setups - var/obj/item/implant/explosive/E = new/obj/item/implant/explosive(synd_mob) - E.implant(synd_mob) - synd_mob.faction |= "syndicate" - synd_mob.update_icons() - return 1 - -/datum/game_mode/nuclear/check_win() - if(nukes_left == 0) - return 1 - return ..() - - -/datum/game_mode/proc/is_operatives_are_dead() - for(var/datum/mind/operative_mind in syndicates) - if(!istype(operative_mind.current,/mob/living/carbon/human)) - if(operative_mind.current) - if(operative_mind.current.stat!=2) - return 0 - return 1 - - -/datum/game_mode/nuclear/declare_completion() - var/disk_rescued = 1 - for(var/obj/item/disk/nuclear/D in GLOB.poi_list) - if(!D.onCentcom()) - disk_rescued = 0 - break - var/crew_evacuated = (SSshuttle.emergency.mode >= SHUTTLE_ESCAPE) - //var/operatives_are_dead = is_operatives_are_dead() - - - //nukes_left - //station_was_nuked - //derp //Used for tracking if the syndies actually haul the nuke to the station //no - //herp //Used for tracking if the syndies got the shuttle off of the z-level //NO, DON'T FUCKING NAME VARS LIKE THIS - - if(!disk_rescued && station_was_nuked && !syndies_didnt_escape) - feedback_set_details("round_end_result","nuclear win - syndicate nuke") - to_chat(world, "Syndicate Major Victory!") - to_chat(world, "[syndicate_name()] operatives have destroyed [station_name()]!") - - else if(!disk_rescued && station_was_nuked && syndies_didnt_escape) - feedback_set_details("round_end_result","nuclear halfwin - syndicate nuke - did not evacuate in time") - to_chat(world, "Total Annihilation") - to_chat(world, "[syndicate_name()] operatives destroyed [station_name()] but did not leave the area in time and got caught in the explosion. Next time, don't lose the disk!") - - else if(!disk_rescued && !station_was_nuked && nuke_off_station && !syndies_didnt_escape) - feedback_set_details("round_end_result","nuclear halfwin - blew wrong station") - to_chat(world, "Crew Minor Victory") - to_chat(world, "[syndicate_name()] operatives secured the authentication disk but blew up something that wasn't [station_name()]. Next time, don't lose the disk!") - - else if(!disk_rescued && !station_was_nuked && nuke_off_station && syndies_didnt_escape) - feedback_set_details("round_end_result","nuclear halfwin - blew wrong station - did not evacuate in time") - to_chat(world, "[syndicate_name()] operatives have earned Darwin Award!") - to_chat(world, "[syndicate_name()] operatives blew up something that wasn't [station_name()] and got caught in the explosion. Next time, don't lose the disk!") - - else if(disk_rescued && is_operatives_are_dead()) - feedback_set_details("round_end_result","nuclear loss - evacuation - disk secured - syndi team dead") - to_chat(world, "Crew Major Victory!") - to_chat(world, "The Research Staff has saved the disc and killed the [syndicate_name()] Operatives") - - else if(disk_rescued) - feedback_set_details("round_end_result","nuclear loss - evacuation - disk secured") - to_chat(world, "Crew Major Victory") - to_chat(world, "The Research Staff has saved the disc and stopped the [syndicate_name()] Operatives!") - - else if(!disk_rescued && is_operatives_are_dead()) - feedback_set_details("round_end_result","nuclear loss - evacuation - disk not secured") - to_chat(world, "Syndicate Minor Victory!") - to_chat(world, "The Research Staff failed to secure the authentication disk but did manage to kill most of the [syndicate_name()] Operatives!") - - else if(!disk_rescued && crew_evacuated) - feedback_set_details("round_end_result","nuclear halfwin - detonation averted") - to_chat(world, "Syndicate Minor Victory!") - to_chat(world, "[syndicate_name()] operatives recovered the abandoned authentication disk but detonation of [station_name()] was averted. Next time, don't lose the disk!") - - else if(!disk_rescued && !crew_evacuated) - feedback_set_details("round_end_result","nuclear halfwin - interrupted") - to_chat(world, "Neutral Victory") - to_chat(world, "Round was mysteriously interrupted!") - ..() - return - - -/datum/game_mode/proc/auto_declare_completion_nuclear() - if(syndicates.len || GAMEMODE_IS_NUCLEAR) - var/text = "
    The syndicate operatives were:" - - var/purchases = "" - var/TC_uses = 0 - - for(var/datum/mind/syndicate in syndicates) - - text += "
    [syndicate.key] was [syndicate.name] (" - if(syndicate.current) - if(syndicate.current.stat == DEAD) - text += "died" - else - text += "survived" - if(syndicate.current.real_name != syndicate.name) - text += " as [syndicate.current.real_name]" - else - text += "body destroyed" - text += ")" - for(var/obj/item/uplink/H in world_uplinks) - if(H && H.uplink_owner && H.uplink_owner==syndicate.key) - TC_uses += H.used_TC - purchases += H.purchase_log - - text += "
    " - - text += "(Syndicates used [TC_uses] TC) [purchases]" - - if(TC_uses==0 && station_was_nuked && !is_operatives_are_dead()) - text += "" - - to_chat(world, text) - return 1 - -/proc/nukelastname(var/mob/M as mob) //--All praise goes to NEO|Phyte, all blame goes to DH, and it was Cindi-Kate's idea. Also praise Urist for copypasta ho. - var/randomname = pick(GLOB.last_names) - var/newname = sanitize(copytext(input(M,"You are the nuke operative [pick("Czar", "Boss", "Commander", "Chief", "Kingpin", "Director", "Overlord")]. Please choose a last name for your family.", "Name change",randomname),1,MAX_NAME_LEN)) - - if(!newname) - newname = randomname - - else - if(newname == "Unknown" || newname == "floor" || newname == "wall" || newname == "rwall" || newname == "_") - to_chat(M, "That name is reserved.") - return nukelastname(M) - - return newname - - -/datum/game_mode/nuclear/set_scoreboard_gvars() - var/foecount = 0 - for(var/datum/mind/M in SSticker.mode.syndicates) - foecount++ - if(!M || !M.current) - score_opkilled++ - continue - - if(M.current.stat == DEAD) - score_opkilled++ - - else if(M.current.restrained()) - score_arrested++ - - if(foecount == score_arrested) - score_allarrested = 1 - - for(var/obj/machinery/nuclearbomb/nuke in world) - if(nuke.r_code == "Nope") continue - var/turf/T = get_turf(nuke) - var/area/A = T.loc - - var/list/thousand_penalty = list(/area/wizard_station, /area/solar, /area) - var/list/fiftythousand_penalty = list(/area/security/main, /area/security/brig, /area/security/armoury, /area/security/checkpoint2) - - if(is_type_in_list(A, thousand_penalty)) - score_nuked_penalty = 1000 - - else if(is_type_in_list(A, fiftythousand_penalty)) - score_nuked_penalty = 50000 - - else if(istype(A, /area/engine)) - score_nuked_penalty = 100000 - - else - score_nuked_penalty = 10000 - - break - - var/killpoints = score_opkilled * 250 - var/arrestpoints = score_arrested * 1000 - score_crewscore += killpoints - score_crewscore += arrestpoints - if(score_nuked) - score_crewscore -= score_nuked_penalty - - - -/datum/game_mode/nuclear/get_scoreboard_stats() - var/foecount = 0 - var/crewcount = 0 - - var/diskdat = "" - var/bombdat = null - - for(var/datum/mind/M in SSticker.mode.syndicates) - foecount++ - - for(var/mob/living/C in world) - if(ishuman(C) || isAI(C) || isrobot(C)) - if(C.stat == 2) continue - if(!C.client) continue - crewcount++ - - var/obj/item/disk/nuclear/N = locate() in world - if(istype(N)) - var/atom/disk_loc = N.loc - while(!isturf(disk_loc)) - if(ismob(disk_loc)) - var/mob/M = disk_loc - diskdat += "Carried by [M.real_name] " - if(isobj(disk_loc)) - var/obj/O = disk_loc - diskdat += "in \a [O]" - disk_loc = disk_loc.loc - diskdat += "in [disk_loc.loc]" - - - if(!diskdat) - diskdat = "WARNING: Nuked_penalty could not be found, look at [__FILE__], [__LINE__]." - - var/dat = "" - dat += "Mode Statistics
    " - - dat += "Number of Operatives: [foecount]
    " - dat += "Number of Surviving Crew: [crewcount]
    " - - dat += "Final Location of Nuke: [bombdat]
    " - dat += "Final Location of Disk: [diskdat]
    " - - dat += "
    " - - dat += "Operatives Arrested: [score_arrested] ([score_arrested * 1000] Points)
    " - dat += "All Operatives Arrested: [score_allarrested ? "Yes" : "No"] (Score tripled)
    " - - dat += "Operatives Killed: [score_opkilled] ([score_opkilled * 1000] Points)
    " - dat += "Station Destroyed: [score_nuked ? "Yes" : "No"] (-[score_nuked_penalty] Points)
    " - dat += "
    " - - return dat - -#undef NUKESCALINGMODIFIER +#define NUKESCALINGMODIFIER 1.2 + +/datum/game_mode + var/list/datum/mind/syndicates = list() + +proc/issyndicate(mob/living/M as mob) + return istype(M) && M.mind && SSticker && SSticker.mode && (M.mind in SSticker.mode.syndicates) + +/datum/game_mode/nuclear + name = "nuclear emergency" + config_tag = "nuclear" + required_players = 30 // 30 players - 5 players to be the nuke ops = 25 players remaining + required_enemies = 5 + recommended_enemies = 5 + + var/const/agents_possible = 5 //If we ever need more syndicate agents. + + var/nukes_left = 1 //Call 3714-PRAY right now and order more nukes! Limited offer! + var/nuke_off_station = 0 //Used for tracking if the syndies actually haul the nuke to the station + var/syndies_didnt_escape = 0 //Used for tracking if the syndies got the shuttle off of the z-level + var/total_tc = 0 //Total amount of telecrystals shared between nuke ops + +/datum/game_mode/nuclear/announce() + to_chat(world, "The current game mode is - Nuclear Emergency!") + to_chat(world, "A [syndicate_name()] Strike Force is approaching [station_name()]!") + to_chat(world, "A nuclear explosive was being transported by Nanotrasen to a military base. The transport ship mysteriously lost contact with Space Traffic Control (STC). About that time a strange disk was discovered around [station_name()]. It was identified by Nanotrasen as a nuclear authentication disk and now Syndicate Operatives have arrived to retake the disk and detonate SS13! There are most likely Syndicate starships are in the vicinity, so take care not to lose the disk!\nSyndicate: Reclaim the disk and detonate the nuclear bomb anywhere on SS13.\nPersonnel: Hold the disk and escape with the disk on the shuttle!") + +/datum/game_mode/nuclear/can_start()//This could be better, will likely have to recode it later + if(!..()) + return 0 + + var/list/possible_syndicates = get_players_for_role(ROLE_OPERATIVE) + var/agent_number = 0 + + if(possible_syndicates.len < 1) + return 0 + + if(LAZYLEN(possible_syndicates) > agents_possible) + agent_number = agents_possible + else + agent_number = possible_syndicates.len + + var/n_players = num_players() + if(agent_number > n_players) + agent_number = n_players/2 + + while(agent_number > 0) + var/datum/mind/new_syndicate = pick(possible_syndicates) + syndicates += new_syndicate + possible_syndicates -= new_syndicate //So it doesn't pick the same guy each time. + agent_number-- + + for(var/datum/mind/synd_mind in syndicates) + synd_mind.assigned_role = SPECIAL_ROLE_NUKEOPS //So they aren't chosen for other jobs. + synd_mind.special_role = SPECIAL_ROLE_NUKEOPS + synd_mind.offstation_role = TRUE + return 1 + + +/datum/game_mode/nuclear/pre_setup() + ..() + return 1 + +/datum/game_mode/proc/remove_operative(datum/mind/operative_mind) + if(operative_mind in syndicates) + SSticker.mode.syndicates -= operative_mind + operative_mind.special_role = null + for(var/datum/objective/nuclear/O in operative_mind.objectives) + operative_mind.objectives -= O + operative_mind.current.create_attack_log("No longer nuclear operative") + if(issilicon(operative_mind.current)) + to_chat(operative_mind.current, "You have been turned into a robot! You are no longer a Syndicate operative.") + else + to_chat(operative_mind.current, "You have been brainwashed! You are no longer a Syndicate operative.") + SSticker.mode.update_synd_icons_removed(operative_mind) + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// + +/datum/game_mode/proc/update_synd_icons_added(datum/mind/synd_mind) + var/datum/atom_hud/antag/opshud = huds[ANTAG_HUD_OPS] + opshud.join_hud(synd_mind.current) + set_antag_hud(synd_mind.current, "hudoperative") + +/datum/game_mode/proc/update_synd_icons_removed(datum/mind/synd_mind) + var/datum/atom_hud/antag/opshud = huds[ANTAG_HUD_OPS] + opshud.leave_hud(synd_mind.current) + set_antag_hud(synd_mind.current, null) + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// + +/datum/game_mode/nuclear/post_setup() + + var/list/turf/synd_spawn = list() + + for(var/obj/effect/landmark/A in GLOB.landmarks_list) + if(A.name == "Syndicate-Spawn") + synd_spawn += get_turf(A) + qdel(A) + continue + + var/obj/effect/landmark/nuke_spawn = locate("landmark*Nuclear-Bomb") + + var/nuke_code = "[rand(10000, 99999)]" + var/leader_selected = 0 + var/agent_number = 1 + var/spawnpos = 1 + + for(var/datum/mind/synd_mind in syndicates) + if(spawnpos > synd_spawn.len) + spawnpos = 2 + synd_mind.current.loc = synd_spawn[spawnpos] + + forge_syndicate_objectives(synd_mind) + create_syndicate(synd_mind) + greet_syndicate(synd_mind) + equip_syndicate(synd_mind.current) + + if(!leader_selected) + prepare_syndicate_leader(synd_mind, nuke_code) + leader_selected = 1 + else + synd_mind.current.real_name = "[syndicate_name()] Operative #[agent_number]" + update_syndicate_id(synd_mind, FALSE) + + agent_number++ + spawnpos++ + update_synd_icons_added(synd_mind) + + scale_telecrystals() + share_telecrystals() + if(nuke_spawn && synd_spawn.len > 0) + var/obj/machinery/nuclearbomb/syndicate/the_bomb = new /obj/machinery/nuclearbomb/syndicate(nuke_spawn.loc) + the_bomb.r_code = nuke_code + + return ..() + +/datum/game_mode/nuclear/proc/scale_telecrystals() + var/danger + danger = GLOB.player_list.len + while(!IsMultiple(++danger, 10)) //Increments danger up to the nearest multiple of ten + + total_tc += danger * NUKESCALINGMODIFIER + +/datum/game_mode/nuclear/proc/share_telecrystals() + var/player_tc + var/remainder + + player_tc = round(total_tc / GLOB.nuclear_uplink_list.len) //round to get an integer and not floating point + remainder = total_tc % GLOB.nuclear_uplink_list.len + + for(var/obj/item/radio/uplink/nuclear/U in GLOB.nuclear_uplink_list) + U.hidden_uplink.uses += player_tc + while(remainder > 0) + for(var/obj/item/radio/uplink/nuclear/U in GLOB.nuclear_uplink_list) + if(remainder <= 0) + break + U.hidden_uplink.uses++ + remainder-- + +/datum/game_mode/proc/create_syndicate(datum/mind/synd_mind) // So we don't have inferior species as ops - randomize a human + var/mob/living/carbon/human/M = synd_mind.current + + M.set_species(/datum/species/human, TRUE) + M.dna.ready_dna(M) // Quadriplegic Nuke Ops won't be participating in the paralympics + M.dna.species.create_organs(M) + M.cleanSE() //No fat/blind/colourblind/epileptic/whatever ops. + M.overeatduration = 0 + M.flavor_text = null + + var/obj/item/organ/external/head/head_organ = M.get_organ("head") + var/hair_c = pick("#8B4513","#000000","#FF4500","#FFD700") // Brown, black, red, blonde + var/eye_c = pick("#000000","#8B4513","1E90FF") // Black, brown, blue + var/skin_tone = pick(-50, -30, -10, 0, 0, 0, 10) // Caucasian/black + head_organ.facial_colour = hair_c + head_organ.sec_facial_colour = hair_c + head_organ.hair_colour = hair_c + head_organ.sec_hair_colour = hair_c + M.change_eye_color(eye_c) + M.s_tone = skin_tone + head_organ.h_style = random_hair_style(M.gender, head_organ.dna.species.name) + head_organ.f_style = random_facial_hair_style(M.gender, head_organ.dna.species.name) + M.body_accessory = null + M.regenerate_icons() + M.update_body() + +/datum/game_mode/proc/prepare_syndicate_leader(var/datum/mind/synd_mind, var/nuke_code) + var/leader_title = pick("Czar", "Boss", "Commander", "Chief", "Kingpin", "Director", "Overlord") + synd_mind.current.real_name = "[syndicate_name()] Team [leader_title]" + to_chat(synd_mind.current, "You are the Syndicate leader for this mission. You are responsible for the distribution of telecrystals and your ID is the only one who can open the launch bay doors.") + to_chat(synd_mind.current, "If you feel you are not up to this task, give your ID to another operative.") + to_chat(synd_mind.current, "In your hand you will find a special item capable of triggering a greater challenge for your team. Examine it carefully and consult with your fellow operatives before activating it.") + + var/obj/item/nuclear_challenge/challenge = new /obj/item/nuclear_challenge + synd_mind.current.equip_to_slot_or_del(challenge, slot_r_hand) + + update_syndicate_id(synd_mind, leader_title, TRUE) + + if(nuke_code) + synd_mind.store_memory("Syndicate Nuclear Bomb Code: [nuke_code]", 0, 0) + to_chat(synd_mind.current, "The nuclear authorization code is: [nuke_code]") + var/obj/item/paper/P = new + P.info = "The nuclear authorization code is: [nuke_code]" + P.name = "nuclear bomb code" + var/obj/item/stamp/syndicate/stamp = new + P.stamp(stamp) + qdel(stamp) + + if(SSticker.mode.config_tag=="nuclear") + P.loc = synd_mind.current.loc + else + var/mob/living/carbon/human/H = synd_mind.current + P.loc = H.loc + H.equip_to_slot_or_del(P, slot_r_store, 0) + H.update_icons() + + else + nuke_code = "code will be provided later" + +/datum/game_mode/proc/update_syndicate_id(var/datum/mind/synd_mind, is_leader = FALSE) + var/list/found_ids = synd_mind.current.search_contents_for(/obj/item/card/id) + + if(LAZYLEN(found_ids)) + for(var/obj/item/card/id/ID in found_ids) + ID.name = "[synd_mind.current.real_name] ID card" + ID.registered_name = synd_mind.current.real_name + if(is_leader) + ID.access += ACCESS_SYNDICATE_LEADER + else + message_admins("Warning: Operative [key_name_admin(synd_mind.current)] spawned without an ID card!") + +/datum/game_mode/proc/forge_syndicate_objectives(var/datum/mind/syndicate) + var/datum/objective/nuclear/syndobj = new + syndobj.owner = syndicate + syndicate.objectives += syndobj + + +/datum/game_mode/proc/greet_syndicate(var/datum/mind/syndicate, var/you_are=1) + SEND_SOUND(syndicate.current, 'sound/ambience/antag/ops.ogg') + if(you_are) + to_chat(syndicate.current, "You are a [syndicate_name()] agent!") + var/obj_count = 1 + for(var/datum/objective/objective in syndicate.objectives) + to_chat(syndicate.current, "Objective #[obj_count]: [objective.explanation_text]") + obj_count++ + return + + +/datum/game_mode/proc/random_radio_frequency() + return 1337 // WHY??? -- Doohl + + +/datum/game_mode/proc/equip_syndicate(mob/living/carbon/human/synd_mob, uplink_uses = 20) + var/radio_freq = SYND_FREQ + + var/obj/item/radio/R = new /obj/item/radio/headset/syndicate/alt(synd_mob) + R.set_frequency(radio_freq) + synd_mob.equip_to_slot_or_del(R, slot_l_ear) + + synd_mob.equip_to_slot_or_del(new /obj/item/clothing/under/syndicate(synd_mob), slot_w_uniform) + synd_mob.equip_to_slot_or_del(new /obj/item/clothing/shoes/combat(synd_mob), slot_shoes) + synd_mob.equip_or_collect(new /obj/item/clothing/gloves/combat(synd_mob), slot_gloves) + synd_mob.equip_to_slot_or_del(new /obj/item/card/id/syndicate(synd_mob), slot_wear_id) + synd_mob.equip_to_slot_or_del(new /obj/item/storage/backpack(synd_mob), slot_back) + synd_mob.equip_to_slot_or_del(new /obj/item/gun/projectile/automatic/pistol(synd_mob), slot_belt) + synd_mob.equip_to_slot_or_del(new /obj/item/storage/box/survival_syndi(synd_mob.back), slot_in_backpack) + synd_mob.equip_to_slot_or_del(new /obj/item/pinpointer/nukeop(synd_mob), slot_wear_pda) + var/obj/item/radio/uplink/nuclear/U = new /obj/item/radio/uplink/nuclear(synd_mob) + U.hidden_uplink.uplink_owner="[synd_mob.key]" + U.hidden_uplink.uses = uplink_uses + synd_mob.equip_to_slot_or_del(U, slot_in_backpack) + + if(synd_mob.dna.species) + + /* + Incase anyone ever gets the burning desire to have nukeops with randomized apperances. -- Dave + synd_mob.gender = pick(MALE, FEMALE) // Randomized appearances for the nukeops. + var/datum/preferences/pref = new() + A.randomize_appearance_for(synd_mob) + */ + + var/race = synd_mob.dna.species.name + + switch(race) + if("Vox" || "Vox Armalis") + synd_mob.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/syndicate(synd_mob), slot_wear_mask) + synd_mob.equip_to_slot_or_del(new /obj/item/tank/emergency_oxygen/vox(synd_mob), slot_l_hand) + synd_mob.internal = synd_mob.l_hand + synd_mob.update_action_buttons_icon() + + if("Plasmaman") + synd_mob.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/syndicate(synd_mob), slot_wear_mask) + synd_mob.equip_or_collect(new /obj/item/tank/plasma/plasmaman(synd_mob), slot_s_store) + synd_mob.equip_or_collect(new /obj/item/extinguisher_refill(synd_mob), slot_in_backpack) + synd_mob.equip_or_collect(new /obj/item/extinguisher_refill(synd_mob), slot_in_backpack) + synd_mob.internal = synd_mob.get_item_by_slot(slot_s_store) + synd_mob.update_action_buttons_icon() + + synd_mob.rejuvenate() //fix any damage taken by naked vox/plasmamen/etc while round setups + var/obj/item/implant/explosive/E = new/obj/item/implant/explosive(synd_mob) + E.implant(synd_mob) + synd_mob.faction |= "syndicate" + synd_mob.update_icons() + return 1 + +/datum/game_mode/nuclear/check_win() + if(nukes_left == 0) + return 1 + return ..() + + +/datum/game_mode/proc/is_operatives_are_dead() + for(var/datum/mind/operative_mind in syndicates) + if(!istype(operative_mind.current,/mob/living/carbon/human)) + if(operative_mind.current) + if(operative_mind.current.stat!=2) + return 0 + return 1 + + +/datum/game_mode/nuclear/declare_completion() + var/disk_rescued = 1 + for(var/obj/item/disk/nuclear/D in GLOB.poi_list) + if(!D.onCentcom()) + disk_rescued = 0 + break + var/crew_evacuated = (SSshuttle.emergency.mode >= SHUTTLE_ESCAPE) + //var/operatives_are_dead = is_operatives_are_dead() + + + //nukes_left + //station_was_nuked + //derp //Used for tracking if the syndies actually haul the nuke to the station //no + //herp //Used for tracking if the syndies got the shuttle off of the z-level //NO, DON'T FUCKING NAME VARS LIKE THIS + + if(!disk_rescued && station_was_nuked && !syndies_didnt_escape) + feedback_set_details("round_end_result","nuclear win - syndicate nuke") + to_chat(world, "Syndicate Major Victory!") + to_chat(world, "[syndicate_name()] operatives have destroyed [station_name()]!") + + else if(!disk_rescued && station_was_nuked && syndies_didnt_escape) + feedback_set_details("round_end_result","nuclear halfwin - syndicate nuke - did not evacuate in time") + to_chat(world, "Total Annihilation") + to_chat(world, "[syndicate_name()] operatives destroyed [station_name()] but did not leave the area in time and got caught in the explosion. Next time, don't lose the disk!") + + else if(!disk_rescued && !station_was_nuked && nuke_off_station && !syndies_didnt_escape) + feedback_set_details("round_end_result","nuclear halfwin - blew wrong station") + to_chat(world, "Crew Minor Victory") + to_chat(world, "[syndicate_name()] operatives secured the authentication disk but blew up something that wasn't [station_name()]. Next time, don't lose the disk!") + + else if(!disk_rescued && !station_was_nuked && nuke_off_station && syndies_didnt_escape) + feedback_set_details("round_end_result","nuclear halfwin - blew wrong station - did not evacuate in time") + to_chat(world, "[syndicate_name()] operatives have earned Darwin Award!") + to_chat(world, "[syndicate_name()] operatives blew up something that wasn't [station_name()] and got caught in the explosion. Next time, don't lose the disk!") + + else if(disk_rescued && is_operatives_are_dead()) + feedback_set_details("round_end_result","nuclear loss - evacuation - disk secured - syndi team dead") + to_chat(world, "Crew Major Victory!") + to_chat(world, "The Research Staff has saved the disc and killed the [syndicate_name()] Operatives") + + else if(disk_rescued) + feedback_set_details("round_end_result","nuclear loss - evacuation - disk secured") + to_chat(world, "Crew Major Victory") + to_chat(world, "The Research Staff has saved the disc and stopped the [syndicate_name()] Operatives!") + + else if(!disk_rescued && is_operatives_are_dead()) + feedback_set_details("round_end_result","nuclear loss - evacuation - disk not secured") + to_chat(world, "Syndicate Minor Victory!") + to_chat(world, "The Research Staff failed to secure the authentication disk but did manage to kill most of the [syndicate_name()] Operatives!") + + else if(!disk_rescued && crew_evacuated) + feedback_set_details("round_end_result","nuclear halfwin - detonation averted") + to_chat(world, "Syndicate Minor Victory!") + to_chat(world, "[syndicate_name()] operatives recovered the abandoned authentication disk but detonation of [station_name()] was averted. Next time, don't lose the disk!") + + else if(!disk_rescued && !crew_evacuated) + feedback_set_details("round_end_result","nuclear halfwin - interrupted") + to_chat(world, "Neutral Victory") + to_chat(world, "Round was mysteriously interrupted!") + ..() + return + + +/datum/game_mode/proc/auto_declare_completion_nuclear() + if(syndicates.len || GAMEMODE_IS_NUCLEAR) + var/text = "
    The syndicate operatives were:" + + var/purchases = "" + var/TC_uses = 0 + + for(var/datum/mind/syndicate in syndicates) + + text += "
    [syndicate.key] was [syndicate.name] (" + if(syndicate.current) + if(syndicate.current.stat == DEAD) + text += "died" + else + text += "survived" + if(syndicate.current.real_name != syndicate.name) + text += " as [syndicate.current.real_name]" + else + text += "body destroyed" + text += ")" + for(var/obj/item/uplink/H in world_uplinks) + if(H && H.uplink_owner && H.uplink_owner==syndicate.key) + TC_uses += H.used_TC + purchases += H.purchase_log + + text += "
    " + + text += "(Syndicates used [TC_uses] TC) [purchases]" + + if(TC_uses==0 && station_was_nuked && !is_operatives_are_dead()) + text += "" + + to_chat(world, text) + return 1 + +/proc/nukelastname(var/mob/M as mob) //--All praise goes to NEO|Phyte, all blame goes to DH, and it was Cindi-Kate's idea. Also praise Urist for copypasta ho. + var/randomname = pick(GLOB.last_names) + var/newname = sanitize(copytext(input(M,"You are the nuke operative [pick("Czar", "Boss", "Commander", "Chief", "Kingpin", "Director", "Overlord")]. Please choose a last name for your family.", "Name change",randomname),1,MAX_NAME_LEN)) + + if(!newname) + newname = randomname + + else + if(newname == "Unknown" || newname == "floor" || newname == "wall" || newname == "rwall" || newname == "_") + to_chat(M, "That name is reserved.") + return nukelastname(M) + + return newname + + +/datum/game_mode/nuclear/set_scoreboard_gvars() + var/foecount = 0 + for(var/datum/mind/M in SSticker.mode.syndicates) + foecount++ + if(!M || !M.current) + score_opkilled++ + continue + + if(M.current.stat == DEAD) + score_opkilled++ + + else if(M.current.restrained()) + score_arrested++ + + if(foecount == score_arrested) + score_allarrested = 1 + + for(var/obj/machinery/nuclearbomb/nuke in world) + if(nuke.r_code == "Nope") continue + var/turf/T = get_turf(nuke) + var/area/A = T.loc + + var/list/thousand_penalty = list(/area/wizard_station, /area/solar, /area) + var/list/fiftythousand_penalty = list(/area/security/main, /area/security/brig, /area/security/armoury, /area/security/checkpoint2) + + if(is_type_in_list(A, thousand_penalty)) + score_nuked_penalty = 1000 + + else if(is_type_in_list(A, fiftythousand_penalty)) + score_nuked_penalty = 50000 + + else if(istype(A, /area/engine)) + score_nuked_penalty = 100000 + + else + score_nuked_penalty = 10000 + + break + + var/killpoints = score_opkilled * 250 + var/arrestpoints = score_arrested * 1000 + score_crewscore += killpoints + score_crewscore += arrestpoints + if(score_nuked) + score_crewscore -= score_nuked_penalty + + + +/datum/game_mode/nuclear/get_scoreboard_stats() + var/foecount = 0 + var/crewcount = 0 + + var/diskdat = "" + var/bombdat = null + + for(var/datum/mind/M in SSticker.mode.syndicates) + foecount++ + + for(var/mob/living/C in world) + if(ishuman(C) || isAI(C) || isrobot(C)) + if(C.stat == 2) continue + if(!C.client) continue + crewcount++ + + var/obj/item/disk/nuclear/N = locate() in world + if(istype(N)) + var/atom/disk_loc = N.loc + while(!isturf(disk_loc)) + if(ismob(disk_loc)) + var/mob/M = disk_loc + diskdat += "Carried by [M.real_name] " + if(isobj(disk_loc)) + var/obj/O = disk_loc + diskdat += "in \a [O]" + disk_loc = disk_loc.loc + diskdat += "in [disk_loc.loc]" + + + if(!diskdat) + diskdat = "WARNING: Nuked_penalty could not be found, look at [__FILE__], [__LINE__]." + + var/dat = "" + dat += "Mode Statistics
    " + + dat += "Number of Operatives: [foecount]
    " + dat += "Number of Surviving Crew: [crewcount]
    " + + dat += "Final Location of Nuke: [bombdat]
    " + dat += "Final Location of Disk: [diskdat]
    " + + dat += "
    " + + dat += "Operatives Arrested: [score_arrested] ([score_arrested * 1000] Points)
    " + dat += "All Operatives Arrested: [score_allarrested ? "Yes" : "No"] (Score tripled)
    " + + dat += "Operatives Killed: [score_opkilled] ([score_opkilled * 1000] Points)
    " + dat += "Station Destroyed: [score_nuked ? "Yes" : "No"] (-[score_nuked_penalty] Points)
    " + dat += "
    " + + return dat + +#undef NUKESCALINGMODIFIER diff --git a/code/game/gamemodes/nuclear/nuclearbomb.dm b/code/game/gamemodes/nuclear/nuclearbomb.dm index 1c4956d096e..eec24d0e1ed 100644 --- a/code/game/gamemodes/nuclear/nuclearbomb.dm +++ b/code/game/gamemodes/nuclear/nuclearbomb.dm @@ -1,455 +1,472 @@ -var/bomb_set - -/obj/machinery/nuclearbomb - name = "\improper Nuclear Fission Explosive" - desc = "Uh oh. RUN!!!!" - icon = 'icons/obj/stationobjs.dmi' - icon_state = "nuclearbomb0" - density = 1 - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - var/deployable = 0 - var/extended = 0 - var/lighthack = 0 - var/timeleft = 120 - var/timing = 0 - var/r_code = "ADMIN" - var/code = "" - var/yes_code = 0 - var/safety = 1 - var/obj/item/disk/nuclear/auth = null - var/removal_stage = 0 // 0 is no removal, 1 is covers removed, 2 is covers open, 3 is sealant open, 4 is unwrenched, 5 is removed from bolts. - var/lastentered - var/is_syndicate = 0 - use_power = NO_POWER_USE - var/previous_level = "" - var/datum/wires/nuclearbomb/wires = null - -/obj/machinery/nuclearbomb/syndicate - is_syndicate = 1 - -/obj/machinery/nuclearbomb/New() - ..() - r_code = "[rand(10000, 99999.0)]"//Creates a random code upon object spawn. - wires = new/datum/wires/nuclearbomb(src) - previous_level = get_security_level() - GLOB.poi_list |= src - -/obj/machinery/nuclearbomb/Destroy() - QDEL_NULL(wires) - GLOB.poi_list.Remove(src) - return ..() - -/obj/machinery/nuclearbomb/process() - if(timing) - bomb_set = 1 //So long as there is one nuke timing, it means one nuke is armed. - timeleft = max(timeleft - 2, 0) // 2 seconds per process() - if(timeleft <= 0) - spawn - explode() - SSnanoui.update_uis(src) - return - -/obj/machinery/nuclearbomb/attackby(obj/item/O as obj, mob/user as mob, params) - if(istype(O, /obj/item/screwdriver)) - add_fingerprint(user) - if(auth) - if(panel_open == 0) - panel_open = 1 - overlays += image(icon, "npanel_open") - to_chat(user, "You unscrew the control panel of [src].") - playsound(src, O.usesound, 50, 1) - else - panel_open = 0 - overlays -= image(icon, "npanel_open") - to_chat(user, "You screw the control panel of [src] back on.") - playsound(src, O.usesound, 50, 1) - else - if(panel_open == 0) - to_chat(user, "[src] emits a buzzing noise, the panel staying locked in.") - if(panel_open == 1) - panel_open = 0 - overlays -= image(icon, "npanel_open") - to_chat(user, "You screw the control panel of [src] back on.") - playsound(src, O.usesound, 50, 1) - flick("nuclearbombc", src) - return - - if(panel_open && (istype(O, /obj/item/multitool) || istype(O, /obj/item/wirecutters))) - return attack_hand(user) - - if(istype(O, /obj/item/disk/nuclear)) - if(extended) - if(!user.drop_item()) - to_chat(user, "\The [O] is stuck to your hand!") - return - O.forceMove(src) - auth = O - add_fingerprint(user) - return attack_hand(user) - else - to_chat(user, "You need to deploy \the [src] first. Right click on the sprite, select 'Make Deployable' then click on \the [src] with an empty hand.") - return - - if(anchored) - switch(removal_stage) - if(0) - if(istype(O,/obj/item/weldingtool)) - var/obj/item/weldingtool/WT = O - if(!WT.isOn()) return - if(WT.get_fuel() < 5) // uses up 5 fuel. - to_chat(user, "You need more fuel to complete this task.") - return - - user.visible_message("[user] starts cutting loose the anchoring bolt covers on [src].", "You start cutting loose the anchoring bolt covers with [O]...") - - if(do_after(user,40, target = src)) - if(!src || !user || !WT.remove_fuel(5, user)) return - user.visible_message("[user] cuts through the bolt covers on [src].", "You cut through the bolt cover.") - removal_stage = 1 - return - - if(1) - if(istype(O,/obj/item/crowbar)) - user.visible_message("[user] starts forcing open the bolt covers on [src].", "You start forcing open the anchoring bolt covers with [O]...") - - if(do_after(user,15, target = src)) - if(!src || !user) return - user.visible_message("[user] forces open the bolt covers on [src].", "You force open the bolt covers.") - removal_stage = 2 - return - - if(2) - if(istype(O,/obj/item/weldingtool)) - - var/obj/item/weldingtool/WT = O - if(!WT.isOn()) return - if(WT.get_fuel() < 5) // uses up 5 fuel. - to_chat(user, "You need more fuel to complete this task.") - return - - user.visible_message("[user] starts cutting apart the anchoring system sealant on [src].", "You start cutting apart the anchoring system's sealant with [O]...") - - if(do_after(user,40, target = src)) - if(!src || !user || !WT.remove_fuel(5, user)) return - user.visible_message("[user] cuts apart the anchoring system sealant on [src].", "You cut apart the anchoring system's sealant.") - removal_stage = 3 - return - - if(3) - if(istype(O,/obj/item/wrench)) - - user.visible_message("[user] begins unwrenching the anchoring bolts on [src].", "You begin unwrenching the anchoring bolts...") - - if(do_after(user,50, target = src)) - if(!src || !user) return - user.visible_message("[user] unwrenches the anchoring bolts on [src].", "You unwrench the anchoring bolts.") - removal_stage = 4 - return - - if(4) - if(istype(O,/obj/item/crowbar)) - - user.visible_message("[user] begins lifting [src] off of the anchors.", "You begin lifting the device off the anchors...") - - if(do_after(user,80, target = src)) - if(!src || !user) return - user.visible_message("[user] crowbars [src] off of the anchors. It can now be moved.", "You jam the crowbar under the nuclear device and lift it off its anchors. You can now move it!") - anchored = 0 - removal_stage = 5 - return - return - return ..() - -/obj/machinery/nuclearbomb/attack_ghost(mob/user as mob) - if(extended) - attack_hand(user) - -/obj/machinery/nuclearbomb/attack_hand(mob/user as mob) - if(extended) - if(panel_open) - wires.Interact(user) - else - ui_interact(user) - else if(deployable) - if(removal_stage < 5) - anchored = 1 - visible_message("With a steely snap, bolts slide out of [src] and anchor it to the flooring!") - else - visible_message("\The [src] makes a highly unpleasant crunching noise. It looks like the anchoring bolts have been cut.") - if(!lighthack) - flick("nuclearbombc", src) - icon_state = "nuclearbomb1" - extended = 1 - return - -/obj/machinery/nuclearbomb/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "nuclear_bomb.tmpl", "Nuke Control Panel", 450, 550, state = physical_state) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/nuclearbomb/ui_data(mob/user, datum/topic_state/state) - var/data[0] - data["is_syndicate"] = is_syndicate - data["hacking"] = 0 - data["auth"] = is_auth(user) - if(is_auth(user)) - if(yes_code) - data["authstatus"] = timing ? "Functional/Set" : "Functional" - else - data["authstatus"] = "Auth. S2" - else - if(timing) - data["authstatus"] = "Set" - else - data["authstatus"] = "Auth. S1" - data["safe"] = safety ? "Safe" : "Engaged" - data["time"] = timeleft - data["timer"] = timing - data["safety"] = safety - data["anchored"] = anchored - data["yescode"] = yes_code - data["message"] = "AUTH" - if(is_auth(user)) - data["message"] = code - if(yes_code) - data["message"] = "*****" - - return data - -/obj/machinery/nuclearbomb/verb/make_deployable() - set category = "Object" - set name = "Make Deployable" - set src in oview(1) - - if(usr.stat || !usr.canmove || usr.restrained()) - return - - if(deployable) - to_chat(usr, "You close several panels to make [src] undeployable.") - deployable = 0 - else - to_chat(usr, "You adjust some panels to make [src] deployable.") - deployable = 1 - return - -/obj/machinery/nuclearbomb/proc/is_auth(var/mob/user) - if(auth) - return 1 - else if(user.can_admin_interact()) - return 1 - else - return 0 - -/obj/machinery/nuclearbomb/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["auth"]) - if(auth) - auth.loc = loc - yes_code = 0 - auth = null - else - var/obj/item/I = usr.get_active_hand() - if(istype(I, /obj/item/disk/nuclear)) - usr.drop_item() - I.loc = src - auth = I - if(is_auth(usr)) - if(href_list["type"]) - if(href_list["type"] == "E") - if(code == r_code) - yes_code = 1 - code = null - else - code = "ERROR" - else - if(href_list["type"] == "R") - yes_code = 0 - code = null - else - lastentered = text("[]", href_list["type"]) - if(text2num(lastentered) == null) - var/turf/LOC = get_turf(usr) - message_admins("[key_name_admin(usr)] tried to exploit a nuclear bomb by entering non-numerical codes: [lastentered]! ([LOC ? "JMP" : "null"])", 0) - log_admin("EXPLOIT: [key_name(usr)] tried to exploit a nuclear bomb by entering non-numerical codes: [lastentered]!") - else - code += lastentered - if(length(code) > 5) - code = "ERROR" - if(yes_code) - if(href_list["time"]) - var/time = text2num(href_list["time"]) - timeleft += time - timeleft = min(max(round(src.timeleft), 120), 600) - if(href_list["timer"]) - if(timing == -1.0) - SSnanoui.update_uis(src) - return - if(safety) - to_chat(usr, "The safety is still on.") - SSnanoui.update_uis(src) - return - timing = !(timing) - if(timing) - if(!lighthack) - icon_state = "nuclearbomb2" - if(!safety) - message_admins("[key_name_admin(usr)] engaged a nuclear bomb (JMP)") - if(!is_syndicate) - set_security_level("delta") - bomb_set = 1 //There can still be issues with this resetting when there are multiple bombs. Not a big deal though for Nuke/N - else - bomb_set = 0 - else - if(!is_syndicate) - set_security_level(previous_level) - bomb_set = 0 - if(!lighthack) - icon_state = "nuclearbomb1" - if(href_list["safety"]) - safety = !(safety) - if(safety) - if(!is_syndicate) - set_security_level(previous_level) - timing = 0 - bomb_set = 0 - if(href_list["anchor"]) - if(removal_stage == 5) - anchored = 0 - visible_message("\The [src] makes a highly unpleasant crunching noise. It looks like the anchoring bolts have been cut.") - SSnanoui.update_uis(src) - return - - if(!isinspace()) - anchored = !(anchored) - if(anchored) - visible_message("With a steely snap, bolts slide out of [src] and anchor it to the flooring.") - else - visible_message("The anchoring bolts slide back into the depths of [src].") - else - to_chat(usr, "There is nothing to anchor to!") - - SSnanoui.update_uis(src) - -/obj/machinery/nuclearbomb/blob_act(obj/structure/blob/B) - if(timing == -1.0) - return - qdel(src) - -/obj/machinery/nuclearbomb/tesla_act(power, explosive) - ..() - if(explosive) - qdel(src)//like the singulo, tesla deletes it. stops it from exploding over and over - -#define NUKERANGE 80 -/obj/machinery/nuclearbomb/proc/explode() - if(safety) - timing = 0 - return - timing = -1.0 - yes_code = 0 - safety = 1 - if(!lighthack) - icon_state = "nuclearbomb3" - playsound(src,'sound/machines/alarm.ogg',100,0,5) - if(SSticker && SSticker.mode) - SSticker.mode.explosion_in_progress = 1 - sleep(100) - - enter_allowed = 0 - - var/off_station = 0 - var/turf/bomb_location = get_turf(src) - if( bomb_location && is_station_level(bomb_location.z) ) - if( (bomb_location.x < (128-NUKERANGE)) || (bomb_location.x > (128+NUKERANGE)) || (bomb_location.y < (128-NUKERANGE)) || (bomb_location.y > (128+NUKERANGE)) ) - off_station = 1 - else - off_station = 2 - - if(SSticker) - if(SSticker.mode && SSticker.mode.name == "nuclear emergency") - var/obj/docking_port/mobile/syndie_shuttle = SSshuttle.getShuttle("syndicate") - if(syndie_shuttle) - SSticker.mode:syndies_didnt_escape = is_station_level(syndie_shuttle.z) - SSticker.mode:nuke_off_station = off_station - SSticker.station_explosion_cinematic(off_station,null) - if(SSticker.mode) - SSticker.mode.explosion_in_progress = 0 - if(SSticker.mode.name == "nuclear emergency") - SSticker.mode:nukes_left -- - else if(off_station == 1) - to_chat(world, "A nuclear device was set off, but the explosion was out of reach of the station!") - else if(off_station == 2) - to_chat(world, "A nuclear device was set off, but the device was not on the station!") - else - to_chat(world, "The station was destoyed by the nuclear blast!") - - SSticker.mode.station_was_nuked = (off_station<2) //offstation==1 is a draw. the station becomes irradiated and needs to be evacuated. - //kinda shit but I couldn't get permission to do what I wanted to do. - - if(!SSticker.mode.check_finished())//If the mode does not deal with the nuke going off so just reboot because everyone is stuck as is - world.Reboot("Station destroyed by Nuclear Device.", "end_error", "nuke - unhandled ending") - return - return - - -//==========DAT FUKKEN DISK=============== -/obj/item/disk/nuclear - name = "nuclear authentication disk" - desc = "Better keep this safe." - icon_state = "nucleardisk" - max_integrity = 250 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF - -/obj/item/disk/nuclear/unrestricted - desc = "Seems to have been stripped of its safeties, you better not lose it." - -/obj/item/disk/nuclear/New() - ..() - START_PROCESSING(SSobj, src) - GLOB.poi_list |= src - -/obj/item/disk/nuclear/process() - if(!check_disk_loc()) - var/holder = get(src, /mob) - if(holder) - to_chat(holder, "You can't help but feel that you just lost something back there...") - qdel(src) - - //station disk is allowed on z1, escape shuttle/pods, CC, and syndicate shuttles/base, reset otherwise -/obj/item/disk/nuclear/proc/check_disk_loc() - var/turf/T = get_turf(src) - var/area/A = get_area(src) - if(is_station_level(T.z)) - return TRUE - if(A.nad_allowed) - return TRUE - return FALSE - -/obj/item/disk/nuclear/unrestricted/check_disk_loc() - return TRUE - -/obj/item/disk/nuclear/Destroy(force) - var/turf/diskturf = get_turf(src) - - if(force) - message_admins("[src] has been !!force deleted!! in ([diskturf ? "[diskturf.x], [diskturf.y] ,[diskturf.z] - JMP":"nonexistent location"]).") - log_game("[src] has been !!force deleted!! in ([diskturf ? "[diskturf.x], [diskturf.y] ,[diskturf.z]":"nonexistent location"]).") - GLOB.poi_list.Remove(src) - STOP_PROCESSING(SSobj, src) - return ..() - - if(blobstart.len > 0) - GLOB.poi_list.Remove(src) - var/obj/item/disk/nuclear/NEWDISK = new(pick(blobstart)) - transfer_fingerprints_to(NEWDISK) - message_admins("[src] has been destroyed at ([diskturf.x], [diskturf.y], [diskturf.z] - JMP). Moving it to ([NEWDISK.x], [NEWDISK.y], [NEWDISK.z] - JMP).") - log_game("[src] has been destroyed in ([diskturf.x], [diskturf.y], [diskturf.z]). Moving it to ([NEWDISK.x], [NEWDISK.y], [NEWDISK.z]).") - return QDEL_HINT_HARDDEL_NOW - else - error("[src] was supposed to be destroyed, but we were unable to locate a blobstart landmark to spawn a new one.") - return QDEL_HINT_LETMELIVE // Cancel destruction unless forced. +#define NUKE_INTACT 0 +#define NUKE_COVER_OFF 1 +#define NUKE_COVER_OPEN 2 +#define NUKE_SEALANT_OPEN 3 +#define NUKE_UNWRENCHED 4 +#define NUKE_MOBILE 5 + +var/bomb_set + +/obj/machinery/nuclearbomb + name = "\improper Nuclear Fission Explosive" + desc = "Uh oh. RUN!!!!" + icon = 'icons/obj/stationobjs.dmi' + icon_state = "nuclearbomb0" + density = 1 + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + var/deployable = 0 + var/extended = 0 + var/lighthack = 0 + var/timeleft = 120 + var/timing = 0 + var/r_code = "ADMIN" + var/code = "" + var/yes_code = 0 + var/safety = 1 + var/obj/item/disk/nuclear/auth = null + var/removal_stage = NUKE_INTACT + var/lastentered + var/is_syndicate = 0 + use_power = NO_POWER_USE + var/previous_level = "" + var/datum/wires/nuclearbomb/wires = null + +/obj/machinery/nuclearbomb/syndicate + is_syndicate = 1 + +/obj/machinery/nuclearbomb/New() + ..() + r_code = "[rand(10000, 99999.0)]"//Creates a random code upon object spawn. + wires = new/datum/wires/nuclearbomb(src) + previous_level = get_security_level() + GLOB.poi_list |= src + +/obj/machinery/nuclearbomb/Destroy() + QDEL_NULL(wires) + GLOB.poi_list.Remove(src) + return ..() + +/obj/machinery/nuclearbomb/process() + if(timing) + bomb_set = 1 //So long as there is one nuke timing, it means one nuke is armed. + timeleft = max(timeleft - 2, 0) // 2 seconds per process() + if(timeleft <= 0) + spawn + explode() + SSnanoui.update_uis(src) + return + +/obj/machinery/nuclearbomb/attackby(obj/item/O as obj, mob/user as mob, params) + if(istype(O, /obj/item/disk/nuclear)) + if(extended) + if(!user.drop_item()) + to_chat(user, "\The [O] is stuck to your hand!") + return + O.forceMove(src) + auth = O + add_fingerprint(user) + return attack_hand(user) + else + to_chat(user, "You need to deploy \the [src] first. Right click on the sprite, select 'Make Deployable' then click on \the [src] with an empty hand.") + return + return ..() + +/obj/machinery/nuclearbomb/crowbar_act(mob/user, obj/item/I) + if(!anchored) + return + if(removal_stage != NUKE_UNWRENCHED && removal_stage != NUKE_COVER_OFF) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if(removal_stage == NUKE_COVER_OFF) + user.visible_message("[user] starts forcing open the bolt covers on [src].", "You start forcing open the anchoring bolt covers with [I]...") + if(!I.use_tool(src, user, 15, volume = I.tool_volume) || removal_stage != NUKE_COVER_OFF) + return + user.visible_message("[user] forces open the bolt covers on [src].", "You force open the bolt covers.") + removal_stage = NUKE_COVER_OPEN + else + user.visible_message("[user] begins lifting [src] off of the anchors.", "You begin lifting the device off the anchors...") + if(!I.use_tool(src, user, 80, volume = I.tool_volume) || removal_stage != NUKE_UNWRENCHED) + return + user.visible_message("[user] crowbars [src] off of the anchors. It can now be moved.", "You jam the crowbar under the nuclear device and lift it off its anchors. You can now move it!") + anchored = FALSE + removal_stage = NUKE_MOBILE + +/obj/machinery/nuclearbomb/wrench_act(mob/user, obj/item/I) + if(!anchored) + return + if(removal_stage != NUKE_SEALANT_OPEN) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + user.visible_message("[user] begins unwrenching the anchoring bolts on [src].", "You begin unwrenching the anchoring bolts...") + if(!I.use_tool(src, user, 50, volume = I.tool_volume) || removal_stage != NUKE_SEALANT_OPEN) + return + user.visible_message("[user] unwrenches the anchoring bolts on [src].", "You unwrench the anchoring bolts.") + removal_stage = NUKE_UNWRENCHED + +/obj/machinery/nuclearbomb/multitool_act(mob/user, obj/item/I) + if(!panel_open) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + attack_hand(user) + +/obj/machinery/nuclearbomb/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(auth) + if(!panel_open) + panel_open = TRUE + overlays += image(icon, "npanel_open") + to_chat(user, "You unscrew the control panel of [src].") + else + panel_open = FALSE + overlays -= image(icon, "npanel_open") + to_chat(user, "You screw the control panel of [src] back on.") + else + if(!panel_open) + to_chat(user, "[src] emits a buzzing noise, the panel staying locked in.") + if(panel_open == TRUE) + panel_open = FALSE + overlays -= image(icon, "npanel_open") + to_chat(user, "You screw the control panel of [src] back on.") + flick("nuclearbombc", src) + +/obj/machinery/nuclearbomb/wirecutter_act(mob/user, obj/item/I) + if(!panel_open) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + attack_hand(user) + +/obj/machinery/nuclearbomb/welder_act(mob/user, obj/item/I) + . = TRUE + if(removal_stage != NUKE_INTACT && removal_stage != NUKE_COVER_OPEN) + return + if(!I.tool_use_check(user, 0)) + return + if(removal_stage == NUKE_INTACT) + visible_message("[user] starts cutting loose the anchoring bolt covers on [src].",\ + "You start cutting loose the anchoring bolt covers with [I]...",\ + "You hear welding.") + if(!I.use_tool(src, user, 40, 5, volume = I.tool_volume) || removal_stage != NUKE_INTACT) + return + visible_message("[user] cuts through the bolt covers on [src].",\ + "You cut through the bolt cover.") + removal_stage = NUKE_COVER_OFF + else if(removal_stage == NUKE_COVER_OPEN) + visible_message("[user] starts cutting apart the anchoring system sealant on [src].",\ + "You start cutting apart the anchoring system's sealant with [I]...",\ + "You hear welding.") + if(!I.use_tool(src, user, 40, 5, volume = I.tool_volume) || removal_stage != NUKE_COVER_OPEN) + return + visible_message("[user] cuts apart the anchoring system sealant on [src].",\ + "You cut apart the anchoring system's sealant.
    ") + removal_stage = NUKE_SEALANT_OPEN + +/obj/machinery/nuclearbomb/attack_ghost(mob/user as mob) + if(extended) + attack_hand(user) + +/obj/machinery/nuclearbomb/attack_hand(mob/user as mob) + if(extended) + if(panel_open) + wires.Interact(user) + else + ui_interact(user) + else if(deployable) + if(removal_stage != NUKE_MOBILE) + anchored = 1 + visible_message("With a steely snap, bolts slide out of [src] and anchor it to the flooring!") + else + visible_message("\The [src] makes a highly unpleasant crunching noise. It looks like the anchoring bolts have been cut.") + if(!lighthack) + flick("nuclearbombc", src) + icon_state = "nuclearbomb1" + extended = 1 + return + +/obj/machinery/nuclearbomb/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "nuclear_bomb.tmpl", "Nuke Control Panel", 450, 550, state = physical_state) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/nuclearbomb/ui_data(mob/user, datum/topic_state/state) + var/data[0] + data["is_syndicate"] = is_syndicate + data["hacking"] = 0 + data["auth"] = is_auth(user) + if(is_auth(user)) + if(yes_code) + data["authstatus"] = timing ? "Functional/Set" : "Functional" + else + data["authstatus"] = "Auth. S2" + else + if(timing) + data["authstatus"] = "Set" + else + data["authstatus"] = "Auth. S1" + data["safe"] = safety ? "Safe" : "Engaged" + data["time"] = timeleft + data["timer"] = timing + data["safety"] = safety + data["anchored"] = anchored + data["yescode"] = yes_code + data["message"] = "AUTH" + if(is_auth(user)) + data["message"] = code + if(yes_code) + data["message"] = "*****" + + return data + +/obj/machinery/nuclearbomb/verb/make_deployable() + set category = "Object" + set name = "Make Deployable" + set src in oview(1) + + if(usr.stat || !usr.canmove || usr.restrained()) + return + + if(deployable) + to_chat(usr, "You close several panels to make [src] undeployable.") + deployable = 0 + else + to_chat(usr, "You adjust some panels to make [src] deployable.") + deployable = 1 + return + +/obj/machinery/nuclearbomb/proc/is_auth(var/mob/user) + if(auth) + return 1 + else if(user.can_admin_interact()) + return 1 + else + return 0 + +/obj/machinery/nuclearbomb/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["auth"]) + if(auth) + auth.loc = loc + yes_code = 0 + auth = null + else + var/obj/item/I = usr.get_active_hand() + if(istype(I, /obj/item/disk/nuclear)) + usr.drop_item() + I.loc = src + auth = I + if(is_auth(usr)) + if(href_list["type"]) + if(href_list["type"] == "E") + if(code == r_code) + yes_code = 1 + code = null + else + code = "ERROR" + else + if(href_list["type"] == "R") + yes_code = 0 + code = null + else + lastentered = text("[]", href_list["type"]) + if(text2num(lastentered) == null) + var/turf/LOC = get_turf(usr) + message_admins("[key_name_admin(usr)] tried to exploit a nuclear bomb by entering non-numerical codes: [lastentered]! ([LOC ? "JMP" : "null"])", 0) + log_admin("EXPLOIT: [key_name(usr)] tried to exploit a nuclear bomb by entering non-numerical codes: [lastentered]!") + else + code += lastentered + if(length(code) > 5) + code = "ERROR" + if(yes_code) + if(href_list["time"]) + var/time = text2num(href_list["time"]) + timeleft += time + timeleft = min(max(round(src.timeleft), 120), 600) + if(href_list["timer"]) + if(timing == -1.0) + SSnanoui.update_uis(src) + return + if(safety) + to_chat(usr, "The safety is still on.") + SSnanoui.update_uis(src) + return + timing = !(timing) + if(timing) + if(!lighthack) + icon_state = "nuclearbomb2" + if(!safety) + message_admins("[key_name_admin(usr)] engaged a nuclear bomb (JMP)") + if(!is_syndicate) + set_security_level("delta") + bomb_set = 1 //There can still be issues with this resetting when there are multiple bombs. Not a big deal though for Nuke/N + else + bomb_set = 0 + else + if(!is_syndicate) + set_security_level(previous_level) + bomb_set = 0 + if(!lighthack) + icon_state = "nuclearbomb1" + if(href_list["safety"]) + safety = !(safety) + if(safety) + if(!is_syndicate) + set_security_level(previous_level) + timing = 0 + bomb_set = 0 + if(href_list["anchor"]) + if(removal_stage == NUKE_MOBILE) + anchored = 0 + visible_message("\The [src] makes a highly unpleasant crunching noise. It looks like the anchoring bolts have been cut.") + SSnanoui.update_uis(src) + return + + if(!isinspace()) + anchored = !(anchored) + if(anchored) + visible_message("With a steely snap, bolts slide out of [src] and anchor it to the flooring.") + else + visible_message("The anchoring bolts slide back into the depths of [src].") + else + to_chat(usr, "There is nothing to anchor to!") + + SSnanoui.update_uis(src) + +/obj/machinery/nuclearbomb/blob_act(obj/structure/blob/B) + if(timing == -1.0) + return + qdel(src) + +/obj/machinery/nuclearbomb/tesla_act(power, explosive) + ..() + if(explosive) + qdel(src)//like the singulo, tesla deletes it. stops it from exploding over and over + +#define NUKERANGE 80 +/obj/machinery/nuclearbomb/proc/explode() + if(safety) + timing = 0 + return + timing = -1.0 + yes_code = 0 + safety = 1 + if(!lighthack) + icon_state = "nuclearbomb3" + playsound(src,'sound/machines/alarm.ogg',100,0,5) + if(SSticker && SSticker.mode) + SSticker.mode.explosion_in_progress = 1 + sleep(100) + + enter_allowed = 0 + + var/off_station = 0 + var/turf/bomb_location = get_turf(src) + if( bomb_location && is_station_level(bomb_location.z) ) + if( (bomb_location.x < (128-NUKERANGE)) || (bomb_location.x > (128+NUKERANGE)) || (bomb_location.y < (128-NUKERANGE)) || (bomb_location.y > (128+NUKERANGE)) ) + off_station = 1 + else + off_station = 2 + + if(SSticker) + if(SSticker.mode && SSticker.mode.name == "nuclear emergency") + var/obj/docking_port/mobile/syndie_shuttle = SSshuttle.getShuttle("syndicate") + if(syndie_shuttle) + SSticker.mode:syndies_didnt_escape = is_station_level(syndie_shuttle.z) + SSticker.mode:nuke_off_station = off_station + SSticker.station_explosion_cinematic(off_station,null) + if(SSticker.mode) + SSticker.mode.explosion_in_progress = 0 + if(SSticker.mode.name == "nuclear emergency") + SSticker.mode:nukes_left -- + else if(off_station == 1) + to_chat(world, "A nuclear device was set off, but the explosion was out of reach of the station!") + else if(off_station == 2) + to_chat(world, "A nuclear device was set off, but the device was not on the station!") + else + to_chat(world, "The station was destoyed by the nuclear blast!") + + SSticker.mode.station_was_nuked = (off_station<2) //offstation==1 is a draw. the station becomes irradiated and needs to be evacuated. + //kinda shit but I couldn't get permission to do what I wanted to do. + + if(!SSticker.mode.check_finished())//If the mode does not deal with the nuke going off so just reboot because everyone is stuck as is + world.Reboot("Station destroyed by Nuclear Device.", "end_error", "nuke - unhandled ending") + return + return + + +//==========DAT FUKKEN DISK=============== +/obj/item/disk/nuclear + name = "nuclear authentication disk" + desc = "Better keep this safe." + icon_state = "nucleardisk" + max_integrity = 250 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + +/obj/item/disk/nuclear/unrestricted + desc = "Seems to have been stripped of its safeties, you better not lose it." + +/obj/item/disk/nuclear/New() + ..() + START_PROCESSING(SSobj, src) + GLOB.poi_list |= src + +/obj/item/disk/nuclear/process() + if(!check_disk_loc()) + var/holder = get(src, /mob) + if(holder) + to_chat(holder, "You can't help but feel that you just lost something back there...") + qdel(src) + + //station disk is allowed on z1, escape shuttle/pods, CC, and syndicate shuttles/base, reset otherwise +/obj/item/disk/nuclear/proc/check_disk_loc() + var/turf/T = get_turf(src) + var/area/A = get_area(src) + if(is_station_level(T.z)) + return TRUE + if(A.nad_allowed) + return TRUE + return FALSE + +/obj/item/disk/nuclear/unrestricted/check_disk_loc() + return TRUE + +/obj/item/disk/nuclear/Destroy(force) + var/turf/diskturf = get_turf(src) + + if(force) + message_admins("[src] has been !!force deleted!! in ([diskturf ? "[diskturf.x], [diskturf.y] ,[diskturf.z] - JMP":"nonexistent location"]).") + log_game("[src] has been !!force deleted!! in ([diskturf ? "[diskturf.x], [diskturf.y] ,[diskturf.z]":"nonexistent location"]).") + GLOB.poi_list.Remove(src) + STOP_PROCESSING(SSobj, src) + return ..() + + if(blobstart.len > 0) + GLOB.poi_list.Remove(src) + var/obj/item/disk/nuclear/NEWDISK = new(pick(blobstart)) + transfer_fingerprints_to(NEWDISK) + message_admins("[src] has been destroyed at ([diskturf.x], [diskturf.y], [diskturf.z] - JMP). Moving it to ([NEWDISK.x], [NEWDISK.y], [NEWDISK.z] - JMP).") + log_game("[src] has been destroyed in ([diskturf.x], [diskturf.y], [diskturf.z]). Moving it to ([NEWDISK.x], [NEWDISK.y], [NEWDISK.z]).") + return QDEL_HINT_HARDDEL_NOW + else + error("[src] was supposed to be destroyed, but we were unable to locate a blobstart landmark to spawn a new one.") + return QDEL_HINT_LETMELIVE // Cancel destruction unless forced. + +#undef NUKE_INTACT +#undef NUKE_COVER_OFF +#undef NUKE_COVER_OPEN +#undef NUKE_SEALANT_OPEN +#undef NUKE_UNWRENCHED +#undef NUKE_MOBILE diff --git a/code/game/gamemodes/nuclear/pinpointer.dm b/code/game/gamemodes/nuclear/pinpointer.dm index 251c32716bb..f193856b3d9 100644 --- a/code/game/gamemodes/nuclear/pinpointer.dm +++ b/code/game/gamemodes/nuclear/pinpointer.dm @@ -1,404 +1,404 @@ -/obj/item/pinpointer - name = "pinpointer" - icon = 'icons/obj/device.dmi' - icon_state = "pinoff" - flags = CONDUCT - slot_flags = SLOT_PDA | SLOT_BELT - w_class = WEIGHT_CLASS_SMALL - item_state = "electronic" - throw_speed = 4 - throw_range = 20 - materials = list(MAT_METAL=500) - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF - var/obj/item/disk/nuclear/the_disk = null - var/active = 0 - var/shows_nuke_timer = TRUE - var/icon_off = "pinoff" - var/icon_null = "pinonnull" - var/icon_direct = "pinondirect" - var/icon_close = "pinonclose" - var/icon_medium = "pinonmedium" - var/icon_far = "pinonfar" - -/obj/item/pinpointer/New() - ..() - GLOB.pinpointer_list += src - -/obj/item/pinpointer/Destroy() - GLOB.pinpointer_list -= src - active = 0 - the_disk = null - return ..() - -/obj/item/pinpointer/attack_self() - if(!active) - active = 1 - workdisk() - to_chat(usr, "You activate the pinpointer.") - else - active = 0 - icon_state = icon_off - to_chat(usr, "You deactivate the pinpointer.") - -/obj/item/pinpointer/proc/scandisk() - if(!the_disk) - the_disk = locate() - -/obj/item/pinpointer/proc/point_at(atom/target, spawnself = 1) - if(!active) - return - if(!target) - icon_state = icon_null - return - - var/turf/T = get_turf(target) - var/turf/L = get_turf(src) - - if(!(T && L) || (T.z != L.z)) - icon_state = icon_null - else - dir = get_dir(L, T) - switch(get_dist(L, T)) - if(-1) - icon_state = icon_direct - if(1 to 8) - icon_state = icon_close - if(9 to 16) - icon_state = icon_medium - if(16 to INFINITY) - icon_state = icon_far - if(spawnself) - spawn(5) - .() - -/obj/item/pinpointer/proc/workdisk() - scandisk() - point_at(the_disk, 0) - spawn(5) - .() - -/obj/item/pinpointer/examine(mob/user) - . = ..() - if(shows_nuke_timer) - for(var/obj/machinery/nuclearbomb/bomb in GLOB.machines) - if(bomb.timing) - . += "Extreme danger. Arming signal detected. Time remaining: [bomb.timeleft]" - -/obj/item/pinpointer/advpinpointer - name = "advanced pinpointer" - desc = "A larger version of the normal pinpointer, this unit features a helpful quantum entanglement detection system to locate various objects that do not broadcast a locator signal." - var/mode = 0 // Mode 0 locates disk, mode 1 locates coordinates. - var/turf/location = null - var/obj/target = null - -/obj/item/pinpointer/advpinpointer/attack_self() - if(!active) - active = 1 - if(mode == 0) - workdisk() - if(mode == 1) - point_at(location) - if(mode == 2) - point_at(target) - to_chat(usr, "You activate the pinpointer.") - else - active = 0 - icon_state = icon_off - to_chat(usr, "You deactivate the pinpointer.") - -/obj/item/pinpointer/advpinpointer/workdisk() - if(mode == 0) - scandisk() - point_at(the_disk, 0) - spawn(5) - .() - -/obj/item/pinpointer/advpinpointer/verb/toggle_mode() - set category = "Object" - set name = "Toggle Pinpointer Mode" - set src in usr - - if(usr.stat || usr.restrained()) - return - - active = 0 - icon_state = icon_off - target = null - location = null - - switch(alert("Please select the mode you want to put the pinpointer in.", "Pinpointer Mode Select", "Location", "Disk Recovery", "Other Signature")) - if("Location") - mode = 1 - - var/locationx = input(usr, "Please input the x coordinate to search for.", "Location?" , "") as num - if(!locationx || !(usr in view(1,src))) - return - var/locationy = input(usr, "Please input the y coordinate to search for.", "Location?" , "") as num - if(!locationy || !(usr in view(1,src))) - return - - var/turf/Z = get_turf(src) - - location = locate(locationx,locationy,Z.z) - - to_chat(usr, "You set the pinpointer to locate [locationx],[locationy]") - - - return attack_self() - - if("Disk Recovery") - mode = 0 - return attack_self() - - if("Other Signature") - mode = 2 - switch(alert("Search for item signature or DNA fragment?" , "Signature Mode Select" , "Item" , "DNA")) - if("Item") - var/list/item_names[0] - var/list/item_paths[0] - for(var/objective in potential_theft_objectives) - var/datum/theft_objective/T = objective - var/name = initial(T.name) - item_names += name - item_paths[name] = initial(T.typepath) - var/targetitem = input("Select item to search for.", "Item Mode Select","") as null|anything in item_names - if(!targetitem) - return - - var/list/target_candidates = get_all_of_type(item_paths[targetitem], subtypes = TRUE) - for(var/obj/item/candidate in target_candidates) - if(!is_admin_level((get_turf(candidate)).z)) - target = candidate - break - - if(!target) - to_chat(usr, "Failed to locate [targetitem]!") - return - to_chat(usr, "You set the pinpointer to locate [targetitem].") - if("DNA") - var/DNAstring = input("Input DNA string to search for." , "Please Enter String." , "") - if(!DNAstring) - return - for(var/mob/living/carbon/C in GLOB.mob_list) - if(!C.dna) - continue - if(C.dna.unique_enzymes == DNAstring) - target = C - break - - return attack_self() - -/////////////////////// -//nuke op pinpointers// -/////////////////////// -/obj/item/pinpointer/nukeop - var/mode = 0 //Mode 0 locates disk, mode 1 locates the shuttle - var/obj/docking_port/mobile/home = null - slot_flags = SLOT_BELT | SLOT_PDA - -/obj/item/pinpointer/nukeop/attack_self(mob/user as mob) - if(!active) - active = 1 - if(!mode) - workdisk() - to_chat(user, "Authentication Disk Locator active.") - else - worklocation() - to_chat(user, "Shuttle Locator active.") - else - active = 0 - icon_state = icon_off - to_chat(user, "You deactivate the pinpointer.") - -/obj/item/pinpointer/nukeop/workdisk() - if(!active) return - if(mode) //Check in case the mode changes while operating - worklocation() - return - if(bomb_set) //If the bomb is set, lead to the shuttle - mode = 1 //Ensures worklocation() continues to work - worklocation() - playsound(loc, 'sound/machines/twobeep.ogg', 50, 1) //Plays a beep - visible_message("Shuttle Locator mode actived.") //Lets the mob holding it know that the mode has changed - return //Get outta here - scandisk() - if(!the_disk) - icon_state = icon_null - return - dir = get_dir(src, the_disk) - switch(get_dist(src, the_disk)) - if(0) - icon_state = icon_direct - if(1 to 8) - icon_state = icon_close - if(9 to 16) - icon_state = icon_medium - if(16 to INFINITY) - icon_state = icon_far - - spawn(5) .() - -/obj/item/pinpointer/nukeop/proc/worklocation() - if(!active) - return - if(!mode) - workdisk() - return - if(!bomb_set) - mode = 0 - workdisk() - playsound(loc, 'sound/machines/twobeep.ogg', 50, 1) - visible_message("Authentication Disk Locator mode actived.") - return - if(!home) - home = SSshuttle.getShuttle("syndicate") - if(!home) - icon_state = icon_null - return - if(loc.z != home.z) //If you are on a different z-level from the shuttle - icon_state = icon_null - else - dir = get_dir(src, home) - switch(get_dist(src, home)) - if(0) - icon_state = icon_direct - if(1 to 8) - icon_state = icon_close - if(9 to 16) - icon_state = icon_medium - if(16 to INFINITY) - icon_state = icon_far - - spawn(5) - .() - -/obj/item/pinpointer/operative - name = "operative pinpointer" - desc = "A pinpointer that leads to the first Syndicate operative detected." - var/mob/living/carbon/nearest_op = null - -/obj/item/pinpointer/operative/attack_self() - if(!usr.mind || !(usr.mind in SSticker.mode.syndicates)) - to_chat(usr, "AUTHENTICATION FAILURE. ACCESS DENIED.") - return 0 - if(!active) - active = 1 - workop() - to_chat(usr, "You activate the pinpointer.") - else - active = 0 - icon_state = icon_off - to_chat(usr, "You deactivate the pinpointer.") - -/obj/item/pinpointer/operative/proc/scan_for_ops() - if(active) - nearest_op = null //Resets nearest_op every time it scans - var/closest_distance = 1000 - for(var/mob/living/carbon/M in GLOB.mob_list) - if(M.mind && (M.mind in SSticker.mode.syndicates)) - if(get_dist(M, get_turf(src)) < closest_distance) //Actually points toward the nearest op, instead of a random one like it used to - nearest_op = M - -/obj/item/pinpointer/operative/proc/workop() - if(active) - scan_for_ops() - point_at(nearest_op, 0) - spawn(5) - .() - else - return 0 - -/obj/item/pinpointer/operative/examine(mob/user) - . = ..() - if(active) - if(nearest_op) - . += "Nearest operative detected is [nearest_op.real_name]." - else - . += "No operatives detected within scanning range." - -/obj/item/pinpointer/crew - name = "crew pinpointer" - desc = "A handheld tracking device that points to crew suit sensors." - shows_nuke_timer = FALSE - icon_state = "pinoff_crew" - icon_off = "pinoff_crew" - icon_null = "pinonnull_crew" - icon_direct = "pinondirect_crew" - icon_close = "pinonclose_crew" - icon_medium = "pinonmedium_crew" - icon_far = "pinonfar_crew" - -/obj/item/pinpointer/crew/proc/trackable(mob/living/carbon/human/H) - var/turf/here = get_turf(src) - if(istype(H.w_uniform, /obj/item/clothing/under)) - var/obj/item/clothing/under/U = H.w_uniform - - // Suit sensors must be on maximum. - if(!U.has_sensor || U.sensor_mode < 3) - return FALSE - - var/turf/there = get_turf(U) - return there && there.z == here.z - - return FALSE - -/obj/item/pinpointer/crew/attack_self(mob/living/user) - if(active) - active = FALSE - icon_state = icon_off - user.visible_message("[user] deactivates [user.p_their()] pinpointer.", "You deactivate your pinpointer.") - return - - var/list/name_counts = list() - var/list/names = list() - - for(var/mob/living/carbon/human/H in GLOB.mob_list) - if(!trackable(H)) - continue - - var/name = "Unknown" - if(H.wear_id) - var/obj/item/card/id/I = H.wear_id.GetID() - if(I) - name = I.registered_name - - while(name in name_counts) - name_counts[name]++ - name = text("[] ([])", name, name_counts[name]) - names[name] = H - name_counts[name] = 1 - - if(!names.len) - user.visible_message("[user]'s pinpointer fails to detect a signal.", "Your pinpointer fails to detect a signal.") - return - - var/A = input(user, "Person to track", "Pinpoint") in names - if(!src || !user || (user.get_active_hand() != src) || user.incapacitated() || !A) - return - - var/target = names[A] - active = TRUE - user.visible_message("[user] activates [user.p_their()] pinpointer.", "You activate your pinpointer.") - point_at(target) - -/obj/item/pinpointer/crew/point_at(atom/target, spawnself = 1) - if(!active) - return - - if(!trackable(target) || !target) - icon_state = icon_null - return - - ..(target, spawnself = 0) - if(spawnself) - spawn(5) - .() - -/obj/item/pinpointer/crew/centcom - name = "centcom pinpointer" - desc = "A handheld tracking device that tracks crew based on remote centcom sensors." - -/obj/item/pinpointer/crew/centcom/trackable(mob/living/carbon/human/H) - var/turf/here = get_turf(src) - var/turf/there = get_turf(H) - return there && there.z == here.z \ No newline at end of file +/obj/item/pinpointer + name = "pinpointer" + icon = 'icons/obj/device.dmi' + icon_state = "pinoff" + flags = CONDUCT + slot_flags = SLOT_PDA | SLOT_BELT + w_class = WEIGHT_CLASS_SMALL + item_state = "electronic" + throw_speed = 4 + throw_range = 20 + materials = list(MAT_METAL=500) + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + var/obj/item/disk/nuclear/the_disk = null + var/active = 0 + var/shows_nuke_timer = TRUE + var/icon_off = "pinoff" + var/icon_null = "pinonnull" + var/icon_direct = "pinondirect" + var/icon_close = "pinonclose" + var/icon_medium = "pinonmedium" + var/icon_far = "pinonfar" + +/obj/item/pinpointer/New() + ..() + GLOB.pinpointer_list += src + +/obj/item/pinpointer/Destroy() + GLOB.pinpointer_list -= src + active = 0 + the_disk = null + return ..() + +/obj/item/pinpointer/attack_self() + if(!active) + active = 1 + workdisk() + to_chat(usr, "You activate the pinpointer.") + else + active = 0 + icon_state = icon_off + to_chat(usr, "You deactivate the pinpointer.") + +/obj/item/pinpointer/proc/scandisk() + if(!the_disk) + the_disk = locate() + +/obj/item/pinpointer/proc/point_at(atom/target, spawnself = 1) + if(!active) + return + if(!target) + icon_state = icon_null + return + + var/turf/T = get_turf(target) + var/turf/L = get_turf(src) + + if(!(T && L) || (T.z != L.z)) + icon_state = icon_null + else + dir = get_dir(L, T) + switch(get_dist(L, T)) + if(-1) + icon_state = icon_direct + if(1 to 8) + icon_state = icon_close + if(9 to 16) + icon_state = icon_medium + if(16 to INFINITY) + icon_state = icon_far + if(spawnself) + spawn(5) + .() + +/obj/item/pinpointer/proc/workdisk() + scandisk() + point_at(the_disk, 0) + spawn(5) + .() + +/obj/item/pinpointer/examine(mob/user) + . = ..() + if(shows_nuke_timer) + for(var/obj/machinery/nuclearbomb/bomb in GLOB.machines) + if(bomb.timing) + . += "Extreme danger. Arming signal detected. Time remaining: [bomb.timeleft]" + +/obj/item/pinpointer/advpinpointer + name = "advanced pinpointer" + desc = "A larger version of the normal pinpointer, this unit features a helpful quantum entanglement detection system to locate various objects that do not broadcast a locator signal." + var/mode = 0 // Mode 0 locates disk, mode 1 locates coordinates. + var/turf/location = null + var/obj/target = null + +/obj/item/pinpointer/advpinpointer/attack_self() + if(!active) + active = 1 + if(mode == 0) + workdisk() + if(mode == 1) + point_at(location) + if(mode == 2) + point_at(target) + to_chat(usr, "You activate the pinpointer.") + else + active = 0 + icon_state = icon_off + to_chat(usr, "You deactivate the pinpointer.") + +/obj/item/pinpointer/advpinpointer/workdisk() + if(mode == 0) + scandisk() + point_at(the_disk, 0) + spawn(5) + .() + +/obj/item/pinpointer/advpinpointer/verb/toggle_mode() + set category = "Object" + set name = "Toggle Pinpointer Mode" + set src in usr + + if(usr.stat || usr.restrained()) + return + + active = 0 + icon_state = icon_off + target = null + location = null + + switch(alert("Please select the mode you want to put the pinpointer in.", "Pinpointer Mode Select", "Location", "Disk Recovery", "Other Signature")) + if("Location") + mode = 1 + + var/locationx = input(usr, "Please input the x coordinate to search for.", "Location?" , "") as num + if(!locationx || !(usr in view(1,src))) + return + var/locationy = input(usr, "Please input the y coordinate to search for.", "Location?" , "") as num + if(!locationy || !(usr in view(1,src))) + return + + var/turf/Z = get_turf(src) + + location = locate(locationx,locationy,Z.z) + + to_chat(usr, "You set the pinpointer to locate [locationx],[locationy]") + + + return attack_self() + + if("Disk Recovery") + mode = 0 + return attack_self() + + if("Other Signature") + mode = 2 + switch(alert("Search for item signature or DNA fragment?" , "Signature Mode Select" , "Item" , "DNA")) + if("Item") + var/list/item_names[0] + var/list/item_paths[0] + for(var/objective in potential_theft_objectives) + var/datum/theft_objective/T = objective + var/name = initial(T.name) + item_names += name + item_paths[name] = initial(T.typepath) + var/targetitem = input("Select item to search for.", "Item Mode Select","") as null|anything in item_names + if(!targetitem) + return + + var/list/target_candidates = get_all_of_type(item_paths[targetitem], subtypes = TRUE) + for(var/obj/item/candidate in target_candidates) + if(!is_admin_level((get_turf(candidate)).z)) + target = candidate + break + + if(!target) + to_chat(usr, "Failed to locate [targetitem]!") + return + to_chat(usr, "You set the pinpointer to locate [targetitem].") + if("DNA") + var/DNAstring = input("Input DNA string to search for." , "Please Enter String." , "") + if(!DNAstring) + return + for(var/mob/living/carbon/C in GLOB.mob_list) + if(!C.dna) + continue + if(C.dna.unique_enzymes == DNAstring) + target = C + break + + return attack_self() + +/////////////////////// +//nuke op pinpointers// +/////////////////////// +/obj/item/pinpointer/nukeop + var/mode = 0 //Mode 0 locates disk, mode 1 locates the shuttle + var/obj/docking_port/mobile/home = null + slot_flags = SLOT_BELT | SLOT_PDA + +/obj/item/pinpointer/nukeop/attack_self(mob/user as mob) + if(!active) + active = 1 + if(!mode) + workdisk() + to_chat(user, "Authentication Disk Locator active.") + else + worklocation() + to_chat(user, "Shuttle Locator active.") + else + active = 0 + icon_state = icon_off + to_chat(user, "You deactivate the pinpointer.") + +/obj/item/pinpointer/nukeop/workdisk() + if(!active) return + if(mode) //Check in case the mode changes while operating + worklocation() + return + if(bomb_set) //If the bomb is set, lead to the shuttle + mode = 1 //Ensures worklocation() continues to work + worklocation() + playsound(loc, 'sound/machines/twobeep.ogg', 50, 1) //Plays a beep + visible_message("Shuttle Locator mode actived.") //Lets the mob holding it know that the mode has changed + return //Get outta here + scandisk() + if(!the_disk) + icon_state = icon_null + return + dir = get_dir(src, the_disk) + switch(get_dist(src, the_disk)) + if(0) + icon_state = icon_direct + if(1 to 8) + icon_state = icon_close + if(9 to 16) + icon_state = icon_medium + if(16 to INFINITY) + icon_state = icon_far + + spawn(5) .() + +/obj/item/pinpointer/nukeop/proc/worklocation() + if(!active) + return + if(!mode) + workdisk() + return + if(!bomb_set) + mode = 0 + workdisk() + playsound(loc, 'sound/machines/twobeep.ogg', 50, 1) + visible_message("Authentication Disk Locator mode actived.") + return + if(!home) + home = SSshuttle.getShuttle("syndicate") + if(!home) + icon_state = icon_null + return + if(loc.z != home.z) //If you are on a different z-level from the shuttle + icon_state = icon_null + else + dir = get_dir(src, home) + switch(get_dist(src, home)) + if(0) + icon_state = icon_direct + if(1 to 8) + icon_state = icon_close + if(9 to 16) + icon_state = icon_medium + if(16 to INFINITY) + icon_state = icon_far + + spawn(5) + .() + +/obj/item/pinpointer/operative + name = "operative pinpointer" + desc = "A pinpointer that leads to the first Syndicate operative detected." + var/mob/living/carbon/nearest_op = null + +/obj/item/pinpointer/operative/attack_self() + if(!usr.mind || !(usr.mind in SSticker.mode.syndicates)) + to_chat(usr, "AUTHENTICATION FAILURE. ACCESS DENIED.") + return 0 + if(!active) + active = 1 + workop() + to_chat(usr, "You activate the pinpointer.") + else + active = 0 + icon_state = icon_off + to_chat(usr, "You deactivate the pinpointer.") + +/obj/item/pinpointer/operative/proc/scan_for_ops() + if(active) + nearest_op = null //Resets nearest_op every time it scans + var/closest_distance = 1000 + for(var/mob/living/carbon/M in GLOB.mob_list) + if(M.mind && (M.mind in SSticker.mode.syndicates)) + if(get_dist(M, get_turf(src)) < closest_distance) //Actually points toward the nearest op, instead of a random one like it used to + nearest_op = M + +/obj/item/pinpointer/operative/proc/workop() + if(active) + scan_for_ops() + point_at(nearest_op, 0) + spawn(5) + .() + else + return 0 + +/obj/item/pinpointer/operative/examine(mob/user) + . = ..() + if(active) + if(nearest_op) + . += "Nearest operative detected is [nearest_op.real_name]." + else + . += "No operatives detected within scanning range." + +/obj/item/pinpointer/crew + name = "crew pinpointer" + desc = "A handheld tracking device that points to crew suit sensors." + shows_nuke_timer = FALSE + icon_state = "pinoff_crew" + icon_off = "pinoff_crew" + icon_null = "pinonnull_crew" + icon_direct = "pinondirect_crew" + icon_close = "pinonclose_crew" + icon_medium = "pinonmedium_crew" + icon_far = "pinonfar_crew" + +/obj/item/pinpointer/crew/proc/trackable(mob/living/carbon/human/H) + var/turf/here = get_turf(src) + if(istype(H.w_uniform, /obj/item/clothing/under)) + var/obj/item/clothing/under/U = H.w_uniform + + // Suit sensors must be on maximum. + if(!U.has_sensor || U.sensor_mode < 3) + return FALSE + + var/turf/there = get_turf(U) + return there && there.z == here.z + + return FALSE + +/obj/item/pinpointer/crew/attack_self(mob/living/user) + if(active) + active = FALSE + icon_state = icon_off + user.visible_message("[user] deactivates [user.p_their()] pinpointer.", "You deactivate your pinpointer.") + return + + var/list/name_counts = list() + var/list/names = list() + + for(var/mob/living/carbon/human/H in GLOB.mob_list) + if(!trackable(H)) + continue + + var/name = "Unknown" + if(H.wear_id) + var/obj/item/card/id/I = H.wear_id.GetID() + if(I) + name = I.registered_name + + while(name in name_counts) + name_counts[name]++ + name = text("[] ([])", name, name_counts[name]) + names[name] = H + name_counts[name] = 1 + + if(!names.len) + user.visible_message("[user]'s pinpointer fails to detect a signal.", "Your pinpointer fails to detect a signal.") + return + + var/A = input(user, "Person to track", "Pinpoint") in names + if(!src || !user || (user.get_active_hand() != src) || user.incapacitated() || !A) + return + + var/target = names[A] + active = TRUE + user.visible_message("[user] activates [user.p_their()] pinpointer.", "You activate your pinpointer.") + point_at(target) + +/obj/item/pinpointer/crew/point_at(atom/target, spawnself = 1) + if(!active) + return + + if(!trackable(target) || !target) + icon_state = icon_null + return + + ..(target, spawnself = 0) + if(spawnself) + spawn(5) + .() + +/obj/item/pinpointer/crew/centcom + name = "centcom pinpointer" + desc = "A handheld tracking device that tracks crew based on remote centcom sensors." + +/obj/item/pinpointer/crew/centcom/trackable(mob/living/carbon/human/H) + var/turf/here = get_turf(src) + var/turf/there = get_turf(H) + return there && there.z == here.z diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index 89ecb1d103b..eb667d4994d 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -1,809 +1,809 @@ -GLOBAL_LIST_EMPTY(all_objectives) - -var/list/potential_theft_objectives = subtypesof(/datum/theft_objective) - /datum/theft_objective/steal - /datum/theft_objective/number - /datum/theft_objective/unique - -/datum/objective - var/datum/mind/owner = null //Who owns the objective. - var/explanation_text = "Nothing" //What that person is supposed to do. - var/datum/mind/target = null //If they are focused on a particular person. - var/target_amount = 0 //If they are focused on a particular number. Steal objectives have their own counter. - var/completed = 0 //currently only used for custom objectives. - var/martyr_compatible = 0 //If the objective is compatible with martyr objective, i.e. if you can still do it while dead. - -/datum/objective/New(text) - GLOB.all_objectives += src - if(text) - explanation_text = text - -/datum/objective/Destroy() - GLOB.all_objectives -= src - return ..() - -/datum/objective/proc/check_completion() - return completed - -/datum/objective/proc/is_invalid_target(datum/mind/possible_target) - if(possible_target == owner) - return TARGET_INVALID_IS_OWNER - if(possible_target in owner.targets) - return TARGET_INVALID_IS_TARGET - if(!ishuman(possible_target.current)) - return TARGET_INVALID_NOT_HUMAN - if(!possible_target.current.stat == DEAD) - return TARGET_INVALID_DEAD - if(!possible_target.key) - return TARGET_INVALID_NOCKEY - if(possible_target.current) - var/turf/current_location = get_turf(possible_target.current) - if(current_location && !is_level_reachable(current_location.z)) - return TARGET_INVALID_UNREACHABLE - if(isgolem(possible_target.current)) - return TARGET_INVALID_GOLEM - if(possible_target.offstation_role) - return TARGET_INVALID_EVENT - - -/datum/objective/proc/find_target() - var/list/possible_targets = list() - for(var/datum/mind/possible_target in SSticker.minds) - if(is_invalid_target(possible_target)) - continue - - possible_targets += possible_target - - if(possible_targets.len > 0) - target = pick(possible_targets) - -/datum/objective/assassinate - martyr_compatible = 1 - -/datum/objective/assassinate/find_target() - ..() - if(target && target.current) - explanation_text = "Assassinate [target.current.real_name], the [target.assigned_role]." - else - explanation_text = "Free Objective" - return target - -/datum/objective/assassinate/check_completion() - if(target && target.current) - if(target.current.stat == DEAD || iszombie(target)) - return 1 - if(issilicon(target.current) || isbrain(target.current)) //Borgs/brains/AIs count as dead for traitor objectives. --NeoFite - return 1 - if(!target.current.ckey) - return 1 - return 0 - return 1 - - -/datum/objective/mutiny - martyr_compatible = 1 - -/datum/objective/mutiny/find_target() - ..() - if(target && target.current) - explanation_text = "Assassinate [target.current.real_name], the [target.assigned_role]." - else - explanation_text = "Free Objective" - return target - -/datum/objective/mutiny/check_completion() - if(target && target.current) - if(target.current.stat == DEAD || !ishuman(target.current) || !target.current.ckey || !target.current.client) - return 1 - var/turf/T = get_turf(target.current) - if(T && !is_station_level(T.z)) //If they leave the station they count as dead for this - return 1 - return 0 - return 1 - -/datum/objective/maroon - martyr_compatible = 1 - -/datum/objective/maroon/find_target() - ..() - if(target && target.current) - explanation_text = "Prevent [target.current.real_name], the [target.assigned_role] from escaping alive." - else - explanation_text = "Free Objective" - return target - -/datum/objective/maroon/check_completion() - if(target && target.current) - if(target.current.stat == DEAD || iszombie(target)) - return 1 - if(!target.current.ckey) - return 1 - if(issilicon(target.current)) - return 1 - if(isbrain(target.current)) - return 1 - var/turf/T = get_turf(target.current) - if(is_admin_level(T.z)) - return 0 - return 1 - return 1 - - -/datum/objective/debrain //I want braaaainssss - martyr_compatible = 0 - -/datum/objective/debrain/find_target() - ..() - if(target && target.current) - explanation_text = "Steal the brain of [target.current.real_name] the [target.assigned_role]." - else - explanation_text = "Free Objective" - return target - - -/datum/objective/debrain/check_completion() - if(!target)//If it's a free objective. - return 1 - if(!owner.current || owner.current.stat == DEAD) - return 0 - if(!target.current || !isbrain(target.current)) - return 0 - var/atom/A = target.current - while(A.loc) //check to see if the brainmob is on our person - A = A.loc - if(A == owner.current) - return 1 - return 0 - - -/datum/objective/protect //The opposite of killing a dude. - martyr_compatible = 1 - -/datum/objective/protect/find_target() - ..() - if(target && target.current) - explanation_text = "Protect [target.current.real_name], the [target.assigned_role]." - else - explanation_text = "Free Objective" - return target - -/datum/objective/protect/check_completion() - if(!target) //If it's a free objective. - return 1 - if(target.current) - if(target.current.stat == DEAD || iszombie(target)) - return 0 - if(issilicon(target.current)) - return 0 - if(isbrain(target.current)) - return 0 - return 1 - return 0 - -/datum/objective/protect/mindslave //subytpe for mindslave implants - - -/datum/objective/hijack - martyr_compatible = 0 //Technically you won't get both anyway. - explanation_text = "Hijack the shuttle by escaping on it with no loyalist Nanotrasen crew on board and free. \ - Syndicate agents, other enemies of Nanotrasen, cyborgs, pets, and cuffed/restrained hostages may be allowed on the shuttle alive." - -/datum/objective/hijack/check_completion() - if(!owner.current || owner.current.stat) - return 0 - if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) - return 0 - if(issilicon(owner.current)) - return 0 - - var/area/A = get_area(owner.current) - if(SSshuttle.emergency.areaInstance != A) - return 0 - - return SSshuttle.emergency.is_hijacked() - -/datum/objective/hijackclone - explanation_text = "Hijack the shuttle by ensuring only you (or your copies) escape." - martyr_compatible = 0 - -/datum/objective/hijackclone/check_completion() - if(!owner.current) - return 0 - if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) - return 0 - - var/area/A = SSshuttle.emergency.areaInstance - - for(var/mob/living/player in GLOB.player_list) //Make sure nobody else is onboard - if(player.mind && player.mind != owner) - if(player.stat != DEAD) - if(issilicon(player)) - continue - if(get_area(player) == A) - if(player.real_name != owner.current.real_name && !istype(get_turf(player.mind.current), /turf/simulated/shuttle/floor4)) - return 0 - - for(var/mob/living/player in GLOB.player_list) //Make sure at least one of you is onboard - if(player.mind && player.mind != owner) - if(player.stat != DEAD) - if(issilicon(player)) - continue - if(get_area(player) == A) - if(player.real_name == owner.current.real_name && !istype(get_turf(player.mind.current), /turf/simulated/shuttle/floor4)) - return 1 - return 0 - -/datum/objective/block - explanation_text = "Do not allow any lifeforms, be it organic or synthetic to escape on the shuttle alive. AIs, Cyborgs, and pAIs are not considered alive." - martyr_compatible = 1 - -/datum/objective/block/check_completion() - if(!istype(owner.current, /mob/living/silicon)) - return 0 - if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) - return 0 - if(!owner.current) - return 0 - - var/area/A = SSshuttle.emergency.areaInstance - var/list/protected_mobs = list(/mob/living/silicon/ai, /mob/living/silicon/pai, /mob/living/silicon/robot) - - for(var/mob/living/player in GLOB.player_list) - if(player.type in protected_mobs) - continue - - if(player.mind && player.stat != DEAD) - if(get_area(player) == A) - return 0 - - return 1 - -/datum/objective/escape - explanation_text = "Escape on the shuttle or an escape pod alive and free." - -/datum/objective/escape/check_completion() - if(issilicon(owner.current)) - return 0 - if(isbrain(owner.current)) - return 0 - if(!owner.current || owner.current.stat == DEAD || iszombie(owner)) - return 0 - if(SSticker.force_ending) //This one isn't their fault, so lets just assume good faith - return 1 - if(SSticker.mode.station_was_nuked) //If they escaped the blast somehow, let them win - return 1 - if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) - return 0 - var/turf/location = get_turf(owner.current) - if(!location) - return 0 - - if(istype(location, /turf/simulated/shuttle/floor4) || istype(location, /turf/simulated/floor/mineral/plastitanium/red/brig)) // Fails traitors if they are in the shuttle brig -- Polymorph - return 0 - - if(location.onCentcom() || location.onSyndieBase()) - return 1 - - return 0 - - -/datum/objective/escape/escape_with_identity - var/target_real_name // Has to be stored because the target's real_name can change over the course of the round - -/datum/objective/escape/escape_with_identity/find_target() - var/list/possible_targets = list() //Copypasta because NO_DNA races, yay for snowflakes. - for(var/datum/mind/possible_target in SSticker.minds) - if(possible_target != owner && ishuman(possible_target.current) && (possible_target.current.stat != DEAD) && possible_target.current.client) - var/mob/living/carbon/human/H = possible_target.current - if(!(NO_DNA in H.dna.species.species_traits)) - possible_targets += possible_target - if(possible_targets.len > 0) - target = pick(possible_targets) - if(target && target.current) - target_real_name = target.current.real_name - explanation_text = "Escape on the shuttle or an escape pod with the identity of [target_real_name], the [target.assigned_role] while wearing [target.p_their()] identification card." - else - explanation_text = "Free Objective" - -/datum/objective/escape/escape_with_identity/check_completion() - if(!target_real_name) - return 1 - if(!ishuman(owner.current)) - return 0 - var/mob/living/carbon/human/H = owner.current - if(..()) - if(H.dna.real_name == target_real_name) - if(H.get_id_name()== target_real_name) - return 1 - return 0 - -/datum/objective/die - explanation_text = "Die a glorious death." - -/datum/objective/die/check_completion() - if(!owner.current || owner.current.stat == DEAD || isbrain(owner.current) || iszombie(owner)) - return 1 - if(issilicon(owner.current) && owner.current != owner.original) - return 1 - return 0 - - - -/datum/objective/survive - explanation_text = "Stay alive until the end." - -/datum/objective/survive/check_completion() - if(!owner.current || owner.current.stat == DEAD || isbrain(owner.current)) - return 0 //Brains no longer win survive objectives. --NEO - if(issilicon(owner.current) && owner.current != owner.original) - return 0 - return 1 - -/datum/objective/nuclear - explanation_text = "Destroy the station with a nuclear device." - martyr_compatible = 1 - -/datum/objective/steal - var/datum/theft_objective/steal_target - martyr_compatible = 0 - var/theft_area - -/datum/objective/steal/proc/get_location() - if(steal_target.location_override) - return steal_target.location_override - var/list/obj/item/steal_candidates = get_all_of_type(steal_target.typepath, subtypes = TRUE) - for(var/obj/item/candidate in steal_candidates) - if(!is_admin_level(candidate.loc.z)) - theft_area = get_area(candidate.loc) - return "[theft_area]" - return "an unknown area" - -/datum/objective/steal/find_target() - var/loop=50 - while(!steal_target && loop > 0) - loop-- - var/thefttype = pick(potential_theft_objectives) - var/datum/theft_objective/O = new thefttype - if(owner.assigned_role in O.protected_jobs) - continue - if(O in owner.targets) - continue - if(O.flags & 2) - continue - steal_target = O - - explanation_text = "Steal [steal_target]. One was last seen in [get_location()]. " - if(islist(O.protected_jobs) && O.protected_jobs.len) - explanation_text += "It may also be in the possession of the [jointext(O.protected_jobs, ", ")]." - return - explanation_text = "Free Objective." - - -/datum/objective/steal/proc/select_target() - var/list/possible_items_all = potential_theft_objectives+"custom" - var/new_target = input("Select target:", "Objective target", null) as null|anything in possible_items_all - if(!new_target) return - if(new_target == "custom") - var/datum/theft_objective/O=new - O.typepath = input("Select type:","Type") as null|anything in typesof(/obj/item) - if(!O.typepath) return - var/tmp_obj = new O.typepath - var/custom_name = tmp_obj:name - qdel(tmp_obj) - O.name = sanitize(copytext(input("Enter target name:", "Objective target", custom_name) as text|null,1,MAX_NAME_LEN)) - if(!O.name) return - steal_target = O - explanation_text = "Steal [O.name]." - else - steal_target = new new_target - explanation_text = "Steal [steal_target.name]." - return steal_target - -/datum/objective/steal/check_completion() - if(!steal_target) - return 1 // Free Objective - - var/list/all_items = owner.current.GetAllContents() - - for(var/obj/I in all_items) - if(istype(I, steal_target.typepath)) - return steal_target.check_special_completion(I) - if(I.type in steal_target.altitems) - return steal_target.check_special_completion(I) - - -/datum/objective/steal/exchange - martyr_compatible = 0 - -/datum/objective/steal/exchange/proc/set_faction(var/faction,var/otheragent) - target = otheragent - var/datum/theft_objective/unique/targetinfo - if(faction == "red") - targetinfo = new /datum/theft_objective/unique/docs_blue - else if(faction == "blue") - targetinfo = new /datum/theft_objective/unique/docs_red - explanation_text = "Acquire [targetinfo.name] held by [target.current.real_name], the [target.assigned_role] and syndicate agent" - steal_target = targetinfo - -/datum/objective/steal/exchange/backstab -/datum/objective/steal/exchange/backstab/set_faction(var/faction) - var/datum/theft_objective/unique/targetinfo - if(faction == "red") - targetinfo = new /datum/theft_objective/unique/docs_red - else if(faction == "blue") - targetinfo = new /datum/theft_objective/unique/docs_blue - explanation_text = "Do not give up or lose [targetinfo.name]." - steal_target = targetinfo - -/datum/objective/download -/datum/objective/download/proc/gen_amount_goal() - target_amount = rand(10,20) - explanation_text = "Download [target_amount] research levels." - return target_amount - - -/datum/objective/download/check_completion() - return 0 - - - -/datum/objective/capture -/datum/objective/capture/proc/gen_amount_goal() - target_amount = rand(5,10) - explanation_text = "Accumulate [target_amount] capture points." - return target_amount - - -/datum/objective/capture/check_completion()//Basically runs through all the mobs in the area to determine how much they are worth. - return 0 - - - - -/datum/objective/absorb -/datum/objective/absorb/proc/gen_amount_goal(var/lowbound = 4, var/highbound = 6) - target_amount = rand (lowbound,highbound) - if(SSticker) - var/n_p = 1 //autowin - if(SSticker.current_state == GAME_STATE_SETTING_UP) - for(var/mob/new_player/P in GLOB.player_list) - if(P.client && P.ready && P.mind != owner) - if(P.client.prefs && (P.client.prefs.species == "Machine")) // Special check for species that can't be absorbed. No better solution. - continue - n_p++ - else if(SSticker.current_state == GAME_STATE_PLAYING) - for(var/mob/living/carbon/human/P in GLOB.player_list) - if(NO_DNA in P.dna.species.species_traits) - continue - if(P.client && !(P.mind in SSticker.mode.changelings) && P.mind!=owner) - n_p++ - target_amount = min(target_amount, n_p) - - explanation_text = "Absorb [target_amount] compatible genomes." - return target_amount - -/datum/objective/absorb/check_completion() - if(owner && owner.changeling && owner.changeling.absorbed_dna && (owner.changeling.absorbedcount >= target_amount)) - return 1 - else - return 0 - -/datum/objective/destroy - martyr_compatible = 1 - var/target_real_name - -/datum/objective/destroy/find_target() - var/list/possible_targets = active_ais(1) - var/mob/living/silicon/ai/target_ai = pick(possible_targets) - target = target_ai.mind - if(target && target.current) - target_real_name = target.current.real_name - explanation_text = "Destroy [target_real_name], the AI." - else - explanation_text = "Free Objective" - return target - -/datum/objective/destroy/check_completion() - if(target && target.current) - if(target.current.stat == DEAD || is_away_level(target.current.z) || !target.current.ckey) - return 1 - return 0 - return 1 - -/datum/objective/steal_five_of_type - explanation_text = "Steal at least five items!" - var/list/wanted_items = list() - -/datum/objective/steal_five_of_type/New() - ..() - wanted_items = typecacheof(wanted_items) - -/datum/objective/steal_five_of_type/check_completion() - var/stolen_count = 0 - if(!isliving(owner.current)) - return FALSE - var/list/all_items = owner.current.GetAllContents() //this should get things in cheesewheels, books, etc. - for(var/obj/I in all_items) //Check for wanted items - if(is_type_in_typecache(I, wanted_items)) - stolen_count++ - return stolen_count >= 5 - -/datum/objective/steal_five_of_type/summon_guns - explanation_text = "Steal at least five guns!" - wanted_items = list(/obj/item/gun) - -/datum/objective/steal_five_of_type/summon_magic - explanation_text = "Steal at least five magical artefacts!" - wanted_items = list() - -/datum/objective/steal_five_of_type/summon_magic/New() - wanted_items = GLOB.summoned_magic_objectives - ..() - -/datum/objective/steal_five_of_type/summon_magic/check_completion() - var/stolen_count = 0 - if(!isliving(owner.current)) - return FALSE - var/list/all_items = owner.current.GetAllContents() //this should get things in cheesewheels, books, etc. - for(var/obj/I in all_items) //Check for wanted items - if(istype(I, /obj/item/spellbook) && !istype(I, /obj/item/spellbook/oneuse)) - var/obj/item/spellbook/spellbook = I - if(spellbook.uses) //if the book still has powers... - stolen_count++ //it counts. nice. - if(istype(I, /obj/item/spellbook/oneuse)) - var/obj/item/spellbook/oneuse/oneuse = I - if(!oneuse.used) - stolen_count++ - else if(is_type_in_typecache(I, wanted_items)) - stolen_count++ - return stolen_count >= 5 - -/datum/objective/blood -/datum/objective/blood/proc/gen_amount_goal(low = 150, high = 400) - target_amount = rand(low,high) - target_amount = round(round(target_amount/5)*5) - explanation_text = "Accumulate at least [target_amount] total units of blood." - return target_amount - -/datum/objective/blood/check_completion() - if(owner && owner.vampire && owner.vampire.bloodtotal && owner.vampire.bloodtotal >= target_amount) - return 1 - else - return 0 - -// /vg/; Vox Inviolate for humans :V -/datum/objective/minimize_casualties - explanation_text = "Minimise casualties." -/datum/objective/minimize_casualties/check_completion() - if(owner.kills.len>5) return 0 - return 1 - -//Vox heist objectives. - -/datum/objective/heist -/datum/objective/heist/proc/choose_target() - return - -/datum/objective/heist/kidnap -/datum/objective/heist/kidnap/choose_target() - var/list/roles = list("Chief Engineer","Research Director","Roboticist","Chemist","Station Engineer") - var/list/possible_targets = list() - var/list/priority_targets = list() - - for(var/datum/mind/possible_target in SSticker.minds) - if(possible_target != owner && ishuman(possible_target.current) && (possible_target.current.stat != DEAD) && (possible_target.assigned_role != possible_target.special_role) && !possible_target.offstation_role) - possible_targets += possible_target - for(var/role in roles) - if(possible_target.assigned_role == role) - priority_targets += possible_target - continue - - if(priority_targets.len > 0) - target = pick(priority_targets) - else if(possible_targets.len > 0) - target = pick(possible_targets) - - if(target && target.current) - explanation_text = "The Shoal has a need for [target.current.real_name], the [target.assigned_role]. Take [target.current.p_them()] alive." - else - explanation_text = "Free Objective" - return target - -/datum/objective/heist/kidnap/check_completion() - if(target && target.current) - if(target.current.stat == DEAD) - return 0 - - var/area/shuttle/vox/A = locate() //stupid fucking hardcoding - var/area/vox_station/B = locate() //but necessary - - for(var/mob/living/carbon/human/M in A) - if(target.current == M) - return 1 - for(var/mob/living/carbon/human/M in B) - if(target.current == M) - return 1 - else - return 0 - -/datum/objective/heist/loot -/datum/objective/heist/loot/choose_target() - var/loot = "an object" - switch(rand(1,8)) - if(1) - target = /obj/structure/particle_accelerator - target_amount = 6 - loot = "a complete particle accelerator" - if(2) - target = /obj/machinery/the_singularitygen - target_amount = 1 - loot = "a gravitational singularity generator" - if(3) - target = /obj/machinery/power/emitter - target_amount = 4 - loot = "four emitters" - if(4) - target = /obj/machinery/nuclearbomb - target_amount = 1 - loot = "a nuclear bomb" - if(5) - target = /obj/item/gun - target_amount = 6 - loot = "six guns. Tasers and other non-lethal guns are acceptable" - if(6) - target = /obj/item/gun/energy - target_amount = 4 - loot = "four energy guns" - if(7) - target = /obj/item/gun/energy/laser - target_amount = 2 - loot = "two laser guns" - if(8) - target = /obj/item/gun/energy/ionrifle - target_amount = 1 - loot = "an ion gun" - - explanation_text = "We are lacking in hardware. Steal or trade [loot]." - -/datum/objective/heist/loot/check_completion() - var/total_amount = 0 - - for(var/obj/O in locate(/area/shuttle/vox)) - if(istype(O, target)) - total_amount++ - for(var/obj/I in O.contents) - if(istype(I, target)) - total_amount++ - if(total_amount >= target_amount) - return 1 - - for(var/obj/O in locate(/area/vox_station)) - if(istype(O, target)) - total_amount++ - for(var/obj/I in O.contents) - if(istype(I, target)) - total_amount++ - if(total_amount >= target_amount) - return 1 - - var/datum/game_mode/heist/H = SSticker.mode - for(var/datum/mind/raider in H.raiders) - if(raider.current) - for(var/obj/O in raider.current.get_contents()) - if(istype(O,target)) - total_amount++ - if(total_amount >= target_amount) - return 1 - - return 0 - -/datum/objective/heist/salvage -/datum/objective/heist/salvage/choose_target() - switch(rand(1,8)) - if(1) - target = "metal" - target_amount = 300 - if(2) - target = "glass" - target_amount = 200 - if(3) - target = "plasteel" - target_amount = 100 - if(4) - target = "solid plasma" - target_amount = 100 - if(5) - target = "silver" - target_amount = 50 - if(6) - target = "gold" - target_amount = 20 - if(7) - target = "uranium" - target_amount = 20 - if(8) - target = "diamond" - target_amount = 20 - - explanation_text = "Ransack or trade with the station and escape with [target_amount] [target]." - -/datum/objective/heist/salvage/check_completion() - var/total_amount = 0 - - for(var/obj/item/O in locate(/area/shuttle/vox)) - var/obj/item/stack/sheet/S - if(istype(O,/obj/item/stack/sheet)) - if(O.name == target) - S = O - total_amount += S.get_amount() - - for(var/obj/I in O.contents) - if(istype(I,/obj/item/stack/sheet)) - if(I.name == target) - S = I - total_amount += S.get_amount() - - for(var/obj/item/O in locate(/area/vox_station)) - var/obj/item/stack/sheet/S - if(istype(O,/obj/item/stack/sheet)) - if(O.name == target) - S = O - total_amount += S.get_amount() - - for(var/obj/I in O.contents) - if(istype(I,/obj/item/stack/sheet)) - if(I.name == target) - S = I - total_amount += S.get_amount() - - var/datum/game_mode/heist/H = SSticker.mode - for(var/datum/mind/raider in H.raiders) - if(raider.current) - for(var/obj/item/O in raider.current.get_contents()) - if(istype(O,/obj/item/stack/sheet)) - if(O.name == target) - var/obj/item/stack/sheet/S = O - total_amount += S.get_amount() - - if(total_amount >= target_amount) return 1 - return 0 - - -/datum/objective/heist/inviolate_crew - explanation_text = "Do not leave any Vox behind, alive or dead." - -/datum/objective/heist/inviolate_crew/check_completion() - var/datum/game_mode/heist/H = SSticker.mode - if(H.is_raider_crew_safe()) - return 1 - return 0 - -/datum/objective/heist/inviolate_death - explanation_text = "Follow the Inviolate. Minimise death and loss of resources." - -/datum/objective/heist/inviolate_death/check_completion() - var/vox_allowed_kills = 3 // The number of people the vox can accidently kill. Mostly a counter to people killing themselves if a raider touches them to force fail. - var/vox_total_kills = 0 - - var/datum/game_mode/heist/H = SSticker.mode - for(var/datum/mind/raider in H.raiders) - vox_total_kills += raider.kills.len // Kills are listed in the mind; uses this to calculate vox kills - - if(vox_total_kills > vox_allowed_kills) return 0 - return 1 - - -// Traders -// These objectives have no check_completion, they exist only to tell Sol Traders what to aim for. - -/datum/objective/trade/proc/choose_target() - return - -/datum/objective/trade/plasma/choose_target() - explanation_text = "Acquire at least 15 sheets of plasma through trade." - -/datum/objective/trade/credits/choose_target() - explanation_text = "Acquire at least 10,000 credits through trade." - -//wizard - -/datum/objective/wizchaos - explanation_text = "Wreak havoc upon the station as much you can. Send those wandless Nanotrasen scum a message!" - completed = 1 +GLOBAL_LIST_EMPTY(all_objectives) + +var/list/potential_theft_objectives = subtypesof(/datum/theft_objective) - /datum/theft_objective/steal - /datum/theft_objective/number - /datum/theft_objective/unique + +/datum/objective + var/datum/mind/owner = null //Who owns the objective. + var/explanation_text = "Nothing" //What that person is supposed to do. + var/datum/mind/target = null //If they are focused on a particular person. + var/target_amount = 0 //If they are focused on a particular number. Steal objectives have their own counter. + var/completed = 0 //currently only used for custom objectives. + var/martyr_compatible = 0 //If the objective is compatible with martyr objective, i.e. if you can still do it while dead. + +/datum/objective/New(text) + GLOB.all_objectives += src + if(text) + explanation_text = text + +/datum/objective/Destroy() + GLOB.all_objectives -= src + return ..() + +/datum/objective/proc/check_completion() + return completed + +/datum/objective/proc/is_invalid_target(datum/mind/possible_target) + if(possible_target == owner) + return TARGET_INVALID_IS_OWNER + if(possible_target in owner.targets) + return TARGET_INVALID_IS_TARGET + if(!ishuman(possible_target.current)) + return TARGET_INVALID_NOT_HUMAN + if(!possible_target.current.stat == DEAD) + return TARGET_INVALID_DEAD + if(!possible_target.key) + return TARGET_INVALID_NOCKEY + if(possible_target.current) + var/turf/current_location = get_turf(possible_target.current) + if(current_location && !is_level_reachable(current_location.z)) + return TARGET_INVALID_UNREACHABLE + if(isgolem(possible_target.current)) + return TARGET_INVALID_GOLEM + if(possible_target.offstation_role) + return TARGET_INVALID_EVENT + + +/datum/objective/proc/find_target() + var/list/possible_targets = list() + for(var/datum/mind/possible_target in SSticker.minds) + if(is_invalid_target(possible_target)) + continue + + possible_targets += possible_target + + if(possible_targets.len > 0) + target = pick(possible_targets) + +/datum/objective/assassinate + martyr_compatible = 1 + +/datum/objective/assassinate/find_target() + ..() + if(target && target.current) + explanation_text = "Assassinate [target.current.real_name], the [target.assigned_role]." + else + explanation_text = "Free Objective" + return target + +/datum/objective/assassinate/check_completion() + if(target && target.current) + if(target.current.stat == DEAD || iszombie(target)) + return 1 + if(issilicon(target.current) || isbrain(target.current)) //Borgs/brains/AIs count as dead for traitor objectives. --NeoFite + return 1 + if(!target.current.ckey) + return 1 + return 0 + return 1 + + +/datum/objective/mutiny + martyr_compatible = 1 + +/datum/objective/mutiny/find_target() + ..() + if(target && target.current) + explanation_text = "Assassinate [target.current.real_name], the [target.assigned_role]." + else + explanation_text = "Free Objective" + return target + +/datum/objective/mutiny/check_completion() + if(target && target.current) + if(target.current.stat == DEAD || !ishuman(target.current) || !target.current.ckey || !target.current.client) + return 1 + var/turf/T = get_turf(target.current) + if(T && !is_station_level(T.z)) //If they leave the station they count as dead for this + return 1 + return 0 + return 1 + +/datum/objective/maroon + martyr_compatible = 1 + +/datum/objective/maroon/find_target() + ..() + if(target && target.current) + explanation_text = "Prevent [target.current.real_name], the [target.assigned_role] from escaping alive." + else + explanation_text = "Free Objective" + return target + +/datum/objective/maroon/check_completion() + if(target && target.current) + if(target.current.stat == DEAD || iszombie(target)) + return 1 + if(!target.current.ckey) + return 1 + if(issilicon(target.current)) + return 1 + if(isbrain(target.current)) + return 1 + var/turf/T = get_turf(target.current) + if(is_admin_level(T.z)) + return 0 + return 1 + return 1 + + +/datum/objective/debrain //I want braaaainssss + martyr_compatible = 0 + +/datum/objective/debrain/find_target() + ..() + if(target && target.current) + explanation_text = "Steal the brain of [target.current.real_name] the [target.assigned_role]." + else + explanation_text = "Free Objective" + return target + + +/datum/objective/debrain/check_completion() + if(!target)//If it's a free objective. + return 1 + if(!owner.current || owner.current.stat == DEAD) + return 0 + if(!target.current || !isbrain(target.current)) + return 0 + var/atom/A = target.current + while(A.loc) //check to see if the brainmob is on our person + A = A.loc + if(A == owner.current) + return 1 + return 0 + + +/datum/objective/protect //The opposite of killing a dude. + martyr_compatible = 1 + +/datum/objective/protect/find_target() + ..() + if(target && target.current) + explanation_text = "Protect [target.current.real_name], the [target.assigned_role]." + else + explanation_text = "Free Objective" + return target + +/datum/objective/protect/check_completion() + if(!target) //If it's a free objective. + return 1 + if(target.current) + if(target.current.stat == DEAD || iszombie(target)) + return 0 + if(issilicon(target.current)) + return 0 + if(isbrain(target.current)) + return 0 + return 1 + return 0 + +/datum/objective/protect/mindslave //subytpe for mindslave implants + + +/datum/objective/hijack + martyr_compatible = 0 //Technically you won't get both anyway. + explanation_text = "Hijack the shuttle by escaping on it with no loyalist Nanotrasen crew on board and free. \ + Syndicate agents, other enemies of Nanotrasen, cyborgs, pets, and cuffed/restrained hostages may be allowed on the shuttle alive." + +/datum/objective/hijack/check_completion() + if(!owner.current || owner.current.stat) + return 0 + if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) + return 0 + if(issilicon(owner.current)) + return 0 + + var/area/A = get_area(owner.current) + if(SSshuttle.emergency.areaInstance != A) + return 0 + + return SSshuttle.emergency.is_hijacked() + +/datum/objective/hijackclone + explanation_text = "Hijack the shuttle by ensuring only you (or your copies) escape." + martyr_compatible = 0 + +/datum/objective/hijackclone/check_completion() + if(!owner.current) + return 0 + if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) + return 0 + + var/area/A = SSshuttle.emergency.areaInstance + + for(var/mob/living/player in GLOB.player_list) //Make sure nobody else is onboard + if(player.mind && player.mind != owner) + if(player.stat != DEAD) + if(issilicon(player)) + continue + if(get_area(player) == A) + if(player.real_name != owner.current.real_name && !istype(get_turf(player.mind.current), /turf/simulated/shuttle/floor4)) + return 0 + + for(var/mob/living/player in GLOB.player_list) //Make sure at least one of you is onboard + if(player.mind && player.mind != owner) + if(player.stat != DEAD) + if(issilicon(player)) + continue + if(get_area(player) == A) + if(player.real_name == owner.current.real_name && !istype(get_turf(player.mind.current), /turf/simulated/shuttle/floor4)) + return 1 + return 0 + +/datum/objective/block + explanation_text = "Do not allow any lifeforms, be it organic or synthetic to escape on the shuttle alive. AIs, Cyborgs, and pAIs are not considered alive." + martyr_compatible = 1 + +/datum/objective/block/check_completion() + if(!istype(owner.current, /mob/living/silicon)) + return 0 + if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) + return 0 + if(!owner.current) + return 0 + + var/area/A = SSshuttle.emergency.areaInstance + var/list/protected_mobs = list(/mob/living/silicon/ai, /mob/living/silicon/pai, /mob/living/silicon/robot) + + for(var/mob/living/player in GLOB.player_list) + if(player.type in protected_mobs) + continue + + if(player.mind && player.stat != DEAD) + if(get_area(player) == A) + return 0 + + return 1 + +/datum/objective/escape + explanation_text = "Escape on the shuttle or an escape pod alive and free." + +/datum/objective/escape/check_completion() + if(issilicon(owner.current)) + return 0 + if(isbrain(owner.current)) + return 0 + if(!owner.current || owner.current.stat == DEAD || iszombie(owner)) + return 0 + if(SSticker.force_ending) //This one isn't their fault, so lets just assume good faith + return 1 + if(SSticker.mode.station_was_nuked) //If they escaped the blast somehow, let them win + return 1 + if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME) + return 0 + var/turf/location = get_turf(owner.current) + if(!location) + return 0 + + if(istype(location, /turf/simulated/shuttle/floor4) || istype(location, /turf/simulated/floor/mineral/plastitanium/red/brig)) // Fails traitors if they are in the shuttle brig -- Polymorph + return 0 + + if(location.onCentcom() || location.onSyndieBase()) + return 1 + + return 0 + + +/datum/objective/escape/escape_with_identity + var/target_real_name // Has to be stored because the target's real_name can change over the course of the round + +/datum/objective/escape/escape_with_identity/find_target() + var/list/possible_targets = list() //Copypasta because NO_DNA races, yay for snowflakes. + for(var/datum/mind/possible_target in SSticker.minds) + if(possible_target != owner && ishuman(possible_target.current) && (possible_target.current.stat != DEAD) && possible_target.current.client) + var/mob/living/carbon/human/H = possible_target.current + if(!(NO_DNA in H.dna.species.species_traits)) + possible_targets += possible_target + if(possible_targets.len > 0) + target = pick(possible_targets) + if(target && target.current) + target_real_name = target.current.real_name + explanation_text = "Escape on the shuttle or an escape pod with the identity of [target_real_name], the [target.assigned_role] while wearing [target.p_their()] identification card." + else + explanation_text = "Free Objective" + +/datum/objective/escape/escape_with_identity/check_completion() + if(!target_real_name) + return 1 + if(!ishuman(owner.current)) + return 0 + var/mob/living/carbon/human/H = owner.current + if(..()) + if(H.dna.real_name == target_real_name) + if(H.get_id_name()== target_real_name) + return 1 + return 0 + +/datum/objective/die + explanation_text = "Die a glorious death." + +/datum/objective/die/check_completion() + if(!owner.current || owner.current.stat == DEAD || isbrain(owner.current) || iszombie(owner)) + return 1 + if(issilicon(owner.current) && owner.current != owner.original) + return 1 + return 0 + + + +/datum/objective/survive + explanation_text = "Stay alive until the end." + +/datum/objective/survive/check_completion() + if(!owner.current || owner.current.stat == DEAD || isbrain(owner.current)) + return 0 //Brains no longer win survive objectives. --NEO + if(issilicon(owner.current) && owner.current != owner.original) + return 0 + return 1 + +/datum/objective/nuclear + explanation_text = "Destroy the station with a nuclear device." + martyr_compatible = 1 + +/datum/objective/steal + var/datum/theft_objective/steal_target + martyr_compatible = 0 + var/theft_area + +/datum/objective/steal/proc/get_location() + if(steal_target.location_override) + return steal_target.location_override + var/list/obj/item/steal_candidates = get_all_of_type(steal_target.typepath, subtypes = TRUE) + for(var/obj/item/candidate in steal_candidates) + if(!is_admin_level(candidate.loc.z)) + theft_area = get_area(candidate.loc) + return "[theft_area]" + return "an unknown area" + +/datum/objective/steal/find_target() + var/loop=50 + while(!steal_target && loop > 0) + loop-- + var/thefttype = pick(potential_theft_objectives) + var/datum/theft_objective/O = new thefttype + if(owner.assigned_role in O.protected_jobs) + continue + if(O in owner.targets) + continue + if(O.flags & 2) + continue + steal_target = O + + explanation_text = "Steal [steal_target]. One was last seen in [get_location()]. " + if(islist(O.protected_jobs) && O.protected_jobs.len) + explanation_text += "It may also be in the possession of the [jointext(O.protected_jobs, ", ")]." + return + explanation_text = "Free Objective." + + +/datum/objective/steal/proc/select_target() + var/list/possible_items_all = potential_theft_objectives+"custom" + var/new_target = input("Select target:", "Objective target", null) as null|anything in possible_items_all + if(!new_target) return + if(new_target == "custom") + var/datum/theft_objective/O=new + O.typepath = input("Select type:","Type") as null|anything in typesof(/obj/item) + if(!O.typepath) return + var/tmp_obj = new O.typepath + var/custom_name = tmp_obj:name + qdel(tmp_obj) + O.name = sanitize(copytext(input("Enter target name:", "Objective target", custom_name) as text|null,1,MAX_NAME_LEN)) + if(!O.name) return + steal_target = O + explanation_text = "Steal [O.name]." + else + steal_target = new new_target + explanation_text = "Steal [steal_target.name]." + return steal_target + +/datum/objective/steal/check_completion() + if(!steal_target) + return 1 // Free Objective + + var/list/all_items = owner.current.GetAllContents() + + for(var/obj/I in all_items) + if(istype(I, steal_target.typepath)) + return steal_target.check_special_completion(I) + if(I.type in steal_target.altitems) + return steal_target.check_special_completion(I) + + +/datum/objective/steal/exchange + martyr_compatible = 0 + +/datum/objective/steal/exchange/proc/set_faction(var/faction,var/otheragent) + target = otheragent + var/datum/theft_objective/unique/targetinfo + if(faction == "red") + targetinfo = new /datum/theft_objective/unique/docs_blue + else if(faction == "blue") + targetinfo = new /datum/theft_objective/unique/docs_red + explanation_text = "Acquire [targetinfo.name] held by [target.current.real_name], the [target.assigned_role] and syndicate agent" + steal_target = targetinfo + +/datum/objective/steal/exchange/backstab +/datum/objective/steal/exchange/backstab/set_faction(var/faction) + var/datum/theft_objective/unique/targetinfo + if(faction == "red") + targetinfo = new /datum/theft_objective/unique/docs_red + else if(faction == "blue") + targetinfo = new /datum/theft_objective/unique/docs_blue + explanation_text = "Do not give up or lose [targetinfo.name]." + steal_target = targetinfo + +/datum/objective/download +/datum/objective/download/proc/gen_amount_goal() + target_amount = rand(10,20) + explanation_text = "Download [target_amount] research levels." + return target_amount + + +/datum/objective/download/check_completion() + return 0 + + + +/datum/objective/capture +/datum/objective/capture/proc/gen_amount_goal() + target_amount = rand(5,10) + explanation_text = "Accumulate [target_amount] capture points." + return target_amount + + +/datum/objective/capture/check_completion()//Basically runs through all the mobs in the area to determine how much they are worth. + return 0 + + + + +/datum/objective/absorb +/datum/objective/absorb/proc/gen_amount_goal(var/lowbound = 4, var/highbound = 6) + target_amount = rand (lowbound,highbound) + if(SSticker) + var/n_p = 1 //autowin + if(SSticker.current_state == GAME_STATE_SETTING_UP) + for(var/mob/new_player/P in GLOB.player_list) + if(P.client && P.ready && P.mind != owner) + if(P.client.prefs && (P.client.prefs.species == "Machine")) // Special check for species that can't be absorbed. No better solution. + continue + n_p++ + else if(SSticker.current_state == GAME_STATE_PLAYING) + for(var/mob/living/carbon/human/P in GLOB.player_list) + if(NO_DNA in P.dna.species.species_traits) + continue + if(P.client && !(P.mind in SSticker.mode.changelings) && P.mind!=owner) + n_p++ + target_amount = min(target_amount, n_p) + + explanation_text = "Absorb [target_amount] compatible genomes." + return target_amount + +/datum/objective/absorb/check_completion() + if(owner && owner.changeling && owner.changeling.absorbed_dna && (owner.changeling.absorbedcount >= target_amount)) + return 1 + else + return 0 + +/datum/objective/destroy + martyr_compatible = 1 + var/target_real_name + +/datum/objective/destroy/find_target() + var/list/possible_targets = active_ais(1) + var/mob/living/silicon/ai/target_ai = pick(possible_targets) + target = target_ai.mind + if(target && target.current) + target_real_name = target.current.real_name + explanation_text = "Destroy [target_real_name], the AI." + else + explanation_text = "Free Objective" + return target + +/datum/objective/destroy/check_completion() + if(target && target.current) + if(target.current.stat == DEAD || is_away_level(target.current.z) || !target.current.ckey) + return 1 + return 0 + return 1 + +/datum/objective/steal_five_of_type + explanation_text = "Steal at least five items!" + var/list/wanted_items = list() + +/datum/objective/steal_five_of_type/New() + ..() + wanted_items = typecacheof(wanted_items) + +/datum/objective/steal_five_of_type/check_completion() + var/stolen_count = 0 + if(!isliving(owner.current)) + return FALSE + var/list/all_items = owner.current.GetAllContents() //this should get things in cheesewheels, books, etc. + for(var/obj/I in all_items) //Check for wanted items + if(is_type_in_typecache(I, wanted_items)) + stolen_count++ + return stolen_count >= 5 + +/datum/objective/steal_five_of_type/summon_guns + explanation_text = "Steal at least five guns!" + wanted_items = list(/obj/item/gun) + +/datum/objective/steal_five_of_type/summon_magic + explanation_text = "Steal at least five magical artefacts!" + wanted_items = list() + +/datum/objective/steal_five_of_type/summon_magic/New() + wanted_items = GLOB.summoned_magic_objectives + ..() + +/datum/objective/steal_five_of_type/summon_magic/check_completion() + var/stolen_count = 0 + if(!isliving(owner.current)) + return FALSE + var/list/all_items = owner.current.GetAllContents() //this should get things in cheesewheels, books, etc. + for(var/obj/I in all_items) //Check for wanted items + if(istype(I, /obj/item/spellbook) && !istype(I, /obj/item/spellbook/oneuse)) + var/obj/item/spellbook/spellbook = I + if(spellbook.uses) //if the book still has powers... + stolen_count++ //it counts. nice. + if(istype(I, /obj/item/spellbook/oneuse)) + var/obj/item/spellbook/oneuse/oneuse = I + if(!oneuse.used) + stolen_count++ + else if(is_type_in_typecache(I, wanted_items)) + stolen_count++ + return stolen_count >= 5 + +/datum/objective/blood +/datum/objective/blood/proc/gen_amount_goal(low = 150, high = 400) + target_amount = rand(low,high) + target_amount = round(round(target_amount/5)*5) + explanation_text = "Accumulate at least [target_amount] total units of blood." + return target_amount + +/datum/objective/blood/check_completion() + if(owner && owner.vampire && owner.vampire.bloodtotal && owner.vampire.bloodtotal >= target_amount) + return 1 + else + return 0 + +// /vg/; Vox Inviolate for humans :V +/datum/objective/minimize_casualties + explanation_text = "Minimise casualties." +/datum/objective/minimize_casualties/check_completion() + if(owner.kills.len>5) return 0 + return 1 + +//Vox heist objectives. + +/datum/objective/heist +/datum/objective/heist/proc/choose_target() + return + +/datum/objective/heist/kidnap +/datum/objective/heist/kidnap/choose_target() + var/list/roles = list("Chief Engineer","Research Director","Roboticist","Chemist","Station Engineer") + var/list/possible_targets = list() + var/list/priority_targets = list() + + for(var/datum/mind/possible_target in SSticker.minds) + if(possible_target != owner && ishuman(possible_target.current) && (possible_target.current.stat != DEAD) && (possible_target.assigned_role != possible_target.special_role) && !possible_target.offstation_role) + possible_targets += possible_target + for(var/role in roles) + if(possible_target.assigned_role == role) + priority_targets += possible_target + continue + + if(priority_targets.len > 0) + target = pick(priority_targets) + else if(possible_targets.len > 0) + target = pick(possible_targets) + + if(target && target.current) + explanation_text = "The Shoal has a need for [target.current.real_name], the [target.assigned_role]. Take [target.current.p_them()] alive." + else + explanation_text = "Free Objective" + return target + +/datum/objective/heist/kidnap/check_completion() + if(target && target.current) + if(target.current.stat == DEAD) + return 0 + + var/area/shuttle/vox/A = locate() //stupid fucking hardcoding + var/area/vox_station/B = locate() //but necessary + + for(var/mob/living/carbon/human/M in A) + if(target.current == M) + return 1 + for(var/mob/living/carbon/human/M in B) + if(target.current == M) + return 1 + else + return 0 + +/datum/objective/heist/loot +/datum/objective/heist/loot/choose_target() + var/loot = "an object" + switch(rand(1,8)) + if(1) + target = /obj/structure/particle_accelerator + target_amount = 6 + loot = "a complete particle accelerator" + if(2) + target = /obj/machinery/the_singularitygen + target_amount = 1 + loot = "a gravitational singularity generator" + if(3) + target = /obj/machinery/power/emitter + target_amount = 4 + loot = "four emitters" + if(4) + target = /obj/machinery/nuclearbomb + target_amount = 1 + loot = "a nuclear bomb" + if(5) + target = /obj/item/gun + target_amount = 6 + loot = "six guns. Tasers and other non-lethal guns are acceptable" + if(6) + target = /obj/item/gun/energy + target_amount = 4 + loot = "four energy guns" + if(7) + target = /obj/item/gun/energy/laser + target_amount = 2 + loot = "two laser guns" + if(8) + target = /obj/item/gun/energy/ionrifle + target_amount = 1 + loot = "an ion gun" + + explanation_text = "We are lacking in hardware. Steal or trade [loot]." + +/datum/objective/heist/loot/check_completion() + var/total_amount = 0 + + for(var/obj/O in locate(/area/shuttle/vox)) + if(istype(O, target)) + total_amount++ + for(var/obj/I in O.contents) + if(istype(I, target)) + total_amount++ + if(total_amount >= target_amount) + return 1 + + for(var/obj/O in locate(/area/vox_station)) + if(istype(O, target)) + total_amount++ + for(var/obj/I in O.contents) + if(istype(I, target)) + total_amount++ + if(total_amount >= target_amount) + return 1 + + var/datum/game_mode/heist/H = SSticker.mode + for(var/datum/mind/raider in H.raiders) + if(raider.current) + for(var/obj/O in raider.current.get_contents()) + if(istype(O,target)) + total_amount++ + if(total_amount >= target_amount) + return 1 + + return 0 + +/datum/objective/heist/salvage +/datum/objective/heist/salvage/choose_target() + switch(rand(1,8)) + if(1) + target = "metal" + target_amount = 300 + if(2) + target = "glass" + target_amount = 200 + if(3) + target = "plasteel" + target_amount = 100 + if(4) + target = "solid plasma" + target_amount = 100 + if(5) + target = "silver" + target_amount = 50 + if(6) + target = "gold" + target_amount = 20 + if(7) + target = "uranium" + target_amount = 20 + if(8) + target = "diamond" + target_amount = 20 + + explanation_text = "Ransack or trade with the station and escape with [target_amount] [target]." + +/datum/objective/heist/salvage/check_completion() + var/total_amount = 0 + + for(var/obj/item/O in locate(/area/shuttle/vox)) + var/obj/item/stack/sheet/S + if(istype(O,/obj/item/stack/sheet)) + if(O.name == target) + S = O + total_amount += S.get_amount() + + for(var/obj/I in O.contents) + if(istype(I,/obj/item/stack/sheet)) + if(I.name == target) + S = I + total_amount += S.get_amount() + + for(var/obj/item/O in locate(/area/vox_station)) + var/obj/item/stack/sheet/S + if(istype(O,/obj/item/stack/sheet)) + if(O.name == target) + S = O + total_amount += S.get_amount() + + for(var/obj/I in O.contents) + if(istype(I,/obj/item/stack/sheet)) + if(I.name == target) + S = I + total_amount += S.get_amount() + + var/datum/game_mode/heist/H = SSticker.mode + for(var/datum/mind/raider in H.raiders) + if(raider.current) + for(var/obj/item/O in raider.current.get_contents()) + if(istype(O,/obj/item/stack/sheet)) + if(O.name == target) + var/obj/item/stack/sheet/S = O + total_amount += S.get_amount() + + if(total_amount >= target_amount) return 1 + return 0 + + +/datum/objective/heist/inviolate_crew + explanation_text = "Do not leave any Vox behind, alive or dead." + +/datum/objective/heist/inviolate_crew/check_completion() + var/datum/game_mode/heist/H = SSticker.mode + if(H.is_raider_crew_safe()) + return 1 + return 0 + +/datum/objective/heist/inviolate_death + explanation_text = "Follow the Inviolate. Minimise death and loss of resources." + +/datum/objective/heist/inviolate_death/check_completion() + var/vox_allowed_kills = 3 // The number of people the vox can accidently kill. Mostly a counter to people killing themselves if a raider touches them to force fail. + var/vox_total_kills = 0 + + var/datum/game_mode/heist/H = SSticker.mode + for(var/datum/mind/raider in H.raiders) + vox_total_kills += raider.kills.len // Kills are listed in the mind; uses this to calculate vox kills + + if(vox_total_kills > vox_allowed_kills) return 0 + return 1 + + +// Traders +// These objectives have no check_completion, they exist only to tell Sol Traders what to aim for. + +/datum/objective/trade/proc/choose_target() + return + +/datum/objective/trade/plasma/choose_target() + explanation_text = "Acquire at least 15 sheets of plasma through trade." + +/datum/objective/trade/credits/choose_target() + explanation_text = "Acquire at least 10,000 credits through trade." + +//wizard + +/datum/objective/wizchaos + explanation_text = "Wreak havoc upon the station as much you can. Send those wandless Nanotrasen scum a message!" + completed = 1 diff --git a/code/game/gamemodes/revolution/revolution.dm b/code/game/gamemodes/revolution/revolution.dm index 9f7e14ccef2..ac0b3705198 100644 --- a/code/game/gamemodes/revolution/revolution.dm +++ b/code/game/gamemodes/revolution/revolution.dm @@ -1,449 +1,449 @@ -// To add a rev to the list of revolutionaries, make sure it's rev (with if(ticker.mode.name == "revolution)), -// then call ticker.mode:add_revolutionary(_THE_PLAYERS_MIND_) -// nothing else needs to be done, as that proc will check if they are a valid target. -// Just make sure the converter is a head before you call it! -// To remove a rev (from brainwashing or w/e), call ticker.mode:remove_revolutionary(_THE_PLAYERS_MIND_), -// this will also check they're not a head, so it can just be called freely -// If the game somtimes isn't registering a win properly, then ticker.mode.check_win() isn't being called somewhere. - -/datum/game_mode - var/list/datum/mind/head_revolutionaries = list() - var/list/datum/mind/revolutionaries = list() - -/datum/game_mode/revolution - name = "revolution" - config_tag = "revolution" - restricted_jobs = list("Security Officer", "Warden", "Detective", "Internal Affairs Agent", "AI", "Cyborg","Captain", "Head of Personnel", "Head of Security", "Chief Engineer", "Research Director", "Chief Medical Officer", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Brig Physician") - required_players = 20 - required_enemies = 1 - recommended_enemies = 3 - - var/finished = 0 - var/check_counter = 0 - var/max_headrevs = 3 - var/list/datum/mind/heads_to_kill = list() - var/list/possible_revolutionaries = list() - -/////////////////////////// -//Announces the game type// -/////////////////////////// -/datum/game_mode/revolution/announce() - to_chat(world, "The current game mode is - Revolution!") - to_chat(world, "Some crewmembers are attempting to start a revolution!
    \nRevolutionaries - Kill the Captain, HoP, HoS, CE, RD and CMO. Convert other crewmembers (excluding the heads of staff, and security officers) to your cause by flashing them. Protect your leaders.
    \nPersonnel - Protect the heads of staff. Kill the leaders of the revolution, and brainwash the other revolutionaries (by beating them in the head).
    ") - - -/////////////////////////////////////////////////////////////////////////////// -//Gets the round setup, cancelling if there's not enough players at the start// -/////////////////////////////////////////////////////////////////////////////// -/datum/game_mode/revolution/pre_setup() - possible_revolutionaries = get_players_for_role(ROLE_REV) - - if(config.protect_roles_from_antagonist) - restricted_jobs += protected_jobs - - - for(var/i=1 to max_headrevs) - if(possible_revolutionaries.len==0) - break - var/datum/mind/lenin = pick(possible_revolutionaries) - possible_revolutionaries -= lenin - head_revolutionaries += lenin - lenin.restricted_roles = restricted_jobs - - if(head_revolutionaries.len < required_enemies) - return FALSE - - return TRUE - - -/datum/game_mode/revolution/post_setup() - var/list/heads = get_living_heads() - var/list/sec = get_living_sec() - var/weighted_score = min(max(round(heads.len - ((8 - sec.len) / 3)),1),max_headrevs) - - while(weighted_score < head_revolutionaries.len) //das vi danya - var/datum/mind/trotsky = pick(head_revolutionaries) - possible_revolutionaries += trotsky - head_revolutionaries -= trotsky - update_rev_icons_removed(trotsky) - - for(var/datum/mind/rev_mind in head_revolutionaries) - log_game("[key_name(rev_mind)] has been selected as a head rev") - for(var/datum/mind/head_mind in heads) - mark_for_death(rev_mind, head_mind) - - addtimer(CALLBACK(src, .proc/equip_revolutionary, rev_mind.current), rand(10, 100)) - - for(var/datum/mind/rev_mind in head_revolutionaries) - greet_revolutionary(rev_mind) - modePlayer += head_revolutionaries - if(SSshuttle) - SSshuttle.emergencyNoEscape = 1 - ..() - - -/datum/game_mode/revolution/process() - check_counter++ - if(check_counter >= 5) - if(!finished) - check_heads() - SSticker.mode.check_win() - check_counter = 0 - return FALSE - - -/datum/game_mode/proc/forge_revolutionary_objectives(datum/mind/rev_mind) - var/list/heads = get_living_heads() - for(var/datum/mind/head_mind in heads) - var/datum/objective/mutiny/rev_obj = new - rev_obj.owner = rev_mind - rev_obj.target = head_mind - rev_obj.explanation_text = "Assassinate or exile [head_mind.name], the [head_mind.assigned_role]." - rev_mind.objectives += rev_obj - -/datum/game_mode/proc/greet_revolutionary(datum/mind/rev_mind, you_are=1) - var/obj_count = 1 - update_rev_icons_added(rev_mind) - if(you_are) - to_chat(rev_mind.current, "You are a member of the revolutionaries' leadership!") - for(var/datum/objective/objective in rev_mind.objectives) - to_chat(rev_mind.current, "Objective #[obj_count]: [objective.explanation_text]") - rev_mind.special_role = SPECIAL_ROLE_HEAD_REV - obj_count++ - -///////////////////////////////////////////////////////////////////////////////// -//This are equips the rev heads with their gear, and makes the clown not clumsy// -///////////////////////////////////////////////////////////////////////////////// -/datum/game_mode/proc/equip_revolutionary(mob/living/carbon/human/mob) - if(!istype(mob)) - return - - if(mob.mind) - if(mob.mind.assigned_role == "Clown") - to_chat(mob, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.") - mob.mutations.Remove(CLUMSY) - var/datum/action/innate/toggle_clumsy/A = new - A.Grant(mob) - - var/obj/item/flash/T = new(mob) - var/obj/item/toy/crayon/spraycan/R = new(mob) - var/obj/item/clothing/glasses/hud/security/chameleon/C = new(mob) - - var/list/slots = list ( - "backpack" = slot_in_backpack, - "left pocket" = slot_l_store, - "right pocket" = slot_r_store, - "left hand" = slot_l_hand, - "right hand" = slot_r_hand, - ) - var/where = mob.equip_in_one_of_slots(T, slots) - var/where2 = mob.equip_in_one_of_slots(C, slots) - mob.equip_in_one_of_slots(R,slots) - - mob.update_icons() - - if(!where2) - to_chat(mob, "The Syndicate were unfortunately unable to get you a chameleon security HUD.") - else - to_chat(mob, "The chameleon security HUD in your [where2] will help you keep track of who is mindshield-implanted, and unable to be recruited.") - - if(!where) - to_chat(mob, "The Syndicate were unfortunately unable to get you a flash.") - else - to_chat(mob, "The flash in your [where] will help you to persuade the crew to join your cause.") - return 1 - -///////////////////////////////// -//Gives head revs their targets// -///////////////////////////////// -/datum/game_mode/revolution/proc/mark_for_death(datum/mind/rev_mind, datum/mind/head_mind) - var/datum/objective/mutiny/rev_obj = new - rev_obj.owner = rev_mind - rev_obj.target = head_mind - rev_obj.explanation_text = "Assassinate [head_mind.name], the [head_mind.assigned_role]." - rev_mind.objectives += rev_obj - heads_to_kill += head_mind - -//////////////////////////////////////////// -//Checks if new heads have joined midround// -//////////////////////////////////////////// -/datum/game_mode/revolution/proc/check_heads() - var/list/heads = get_all_heads() - var/list/sec = get_all_sec() - if(heads_to_kill.len < heads.len) - var/list/new_heads = heads - heads_to_kill - for(var/datum/mind/head_mind in new_heads) - for(var/datum/mind/rev_mind in head_revolutionaries) - mark_for_death(rev_mind, head_mind) - - if(head_revolutionaries.len < max_headrevs && head_revolutionaries.len < round(heads.len - ((8 - sec.len) / 3))) - latejoin_headrev() - -/////////////////////////////// -//Adds a new headrev midround// -/////////////////////////////// -/datum/game_mode/revolution/proc/latejoin_headrev() - if(revolutionaries) //Head Revs are not in this list - var/list/promotable_revs = list() - for(var/datum/mind/khrushchev in revolutionaries) - if(khrushchev.current && khrushchev.current.client && khrushchev.current.stat != DEAD) - if(ROLE_REV in khrushchev.current.client.prefs.be_special) - promotable_revs += khrushchev - if(promotable_revs.len) - var/datum/mind/stalin = pick(promotable_revs) - revolutionaries -= stalin - head_revolutionaries += stalin - log_game("[key_name(stalin)] has been promoted to a head rev") - equip_revolutionary(stalin.current) - forge_revolutionary_objectives(stalin) - greet_revolutionary(stalin) - -////////////////////////////////////// -//Checks if the revs have won or not// -////////////////////////////////////// -/datum/game_mode/revolution/check_win() - if(check_rev_victory()) - finished = 1 - else if(check_heads_victory()) - finished = 2 - return - -/////////////////////////////// -//Checks if the round is over// -/////////////////////////////// -/datum/game_mode/revolution/check_finished() - if(config.continuous_rounds) - if(finished != 0) - SSshuttle.emergencyNoEscape = 0 - if(SSshuttle.emergency.mode == SHUTTLE_STRANDED) - SSshuttle.emergency.mode = SHUTTLE_DOCKED - SSshuttle.emergency.timer = world.time - command_announcement.Announce("Hostile enviroment resolved. You have 3 minutes to board the Emergency Shuttle.", null, 'sound/AI/shuttledock.ogg') - return ..() - if(finished != 0) - return TRUE - else - return ..() - -/////////////////////////////////////////////////// -//Deals with converting players to the revolution// -/////////////////////////////////////////////////// -/datum/game_mode/proc/add_revolutionary(datum/mind/rev_mind) - if(rev_mind.assigned_role in command_positions) - return 0 - var/mob/living/carbon/human/H = rev_mind.current//Check to see if the potential rev is implanted - if(ismindshielded(H)) - return 0 - if((rev_mind in revolutionaries) || (rev_mind in head_revolutionaries)) - return 0 - revolutionaries += rev_mind - if(iscarbon(rev_mind.current)) - var/mob/living/carbon/carbon_mob = rev_mind.current - carbon_mob.Silence(5) - carbon_mob.flash_eyes(1, 1) - rev_mind.current.Stun(5) - to_chat(rev_mind.current, " You are now a revolutionary! Help your cause. Do not harm your fellow freedom fighters. You can identify your comrades by the red \"R\" icons, and your leaders by the blue \"R\" icons. Help them kill the heads to win the revolution!") - rev_mind.current.create_attack_log("Has been converted to the revolution!") - rev_mind.special_role = SPECIAL_ROLE_REV - update_rev_icons_added(rev_mind) - if(jobban_isbanned(rev_mind.current, ROLE_REV) || jobban_isbanned(rev_mind.current, ROLE_SYNDICATE)) - replace_jobbanned_player(rev_mind.current, ROLE_REV) - return 1 -////////////////////////////////////////////////////////////////////////////// -//Deals with players being converted from the revolution (Not a rev anymore)// // Modified to handle borged MMIs. Accepts another var if the target is being borged at the time -- Polymorph. -////////////////////////////////////////////////////////////////////////////// -/datum/game_mode/proc/remove_revolutionary(datum/mind/rev_mind , beingborged) - var/remove_head = 0 - if(beingborged && (rev_mind in head_revolutionaries)) - head_revolutionaries -= rev_mind - remove_head = 1 - - if((rev_mind in revolutionaries) || remove_head) - revolutionaries -= rev_mind - rev_mind.special_role = null - rev_mind.current.create_attack_log("Has renounced the revolution!") - - if(beingborged) - to_chat(rev_mind.current, "The frame's firmware detects and deletes your neural reprogramming! You remember nothing[remove_head ? "." : " but the name of the one who flashed you."]") - message_admins("[key_name_admin(rev_mind.current)] [ADMIN_QUE(rev_mind.current,"?")] ([ADMIN_FLW(rev_mind.current,"FLW")]) has been borged while being a [remove_head ? "leader" : " member"] of the revolution.") - - else - rev_mind.current.Paralyse(5) - to_chat(rev_mind.current, "You have been brainwashed! You are no longer a revolutionary! Your memory is hazy from the time you were a rebel...the only thing you remember is the name of the one who brainwashed you...") - - update_rev_icons_removed(rev_mind) - for(var/mob/living/M in view(rev_mind.current)) - if(beingborged) - to_chat(M, "The frame beeps contentedly, purging the hostile memory engram from the MMI before initalizing it.") - - else - to_chat(M, "[rev_mind.current] looks like [rev_mind.current.p_they()] just remembered [rev_mind.current.p_their()] real allegiance!") - -///////////////////////////////////// -//Adds the rev hud to a new convert// -///////////////////////////////////// -/datum/game_mode/proc/update_rev_icons_added(datum/mind/rev_mind) - var/datum/atom_hud/antag/revhud = huds[ANTAG_HUD_REV] - revhud.join_hud(rev_mind.current) - set_antag_hud(rev_mind.current, ((rev_mind in head_revolutionaries) ? "hudheadrevolutionary" : "hudrevolutionary")) - -///////////////////////////////////////// -//Removes the hud from deconverted revs// -///////////////////////////////////////// -/datum/game_mode/proc/update_rev_icons_removed(datum/mind/rev_mind) - var/datum/atom_hud/antag/revhud = huds[ANTAG_HUD_REV] - revhud.leave_hud(rev_mind.current) - set_antag_hud(rev_mind.current, null) - -////////////////////////// -//Checks for rev victory// -////////////////////////// -/datum/game_mode/revolution/proc/check_rev_victory() - for(var/datum/mind/rev_mind in head_revolutionaries) - for(var/datum/objective/mutiny/objective in rev_mind.objectives) - if(!(objective.check_completion())) - return FALSE - - return TRUE - -///////////////////////////// -//Checks for a head victory// -///////////////////////////// -/datum/game_mode/revolution/proc/check_heads_victory() - for(var/datum/mind/rev_mind in head_revolutionaries) - var/turf/T = get_turf(rev_mind.current) - if((rev_mind) && (rev_mind.current) && (rev_mind.current.stat != DEAD) && rev_mind.current.client && T && is_station_level(T.z)) - if(ishuman(rev_mind.current)) - return FALSE - return TRUE - -////////////////////////////////////////////////////////////////////// -//Announces the end of the game with all relavent information stated// -////////////////////////////////////////////////////////////////////// -/datum/game_mode/revolution/declare_completion() - if(finished == 1) - feedback_set_details("round_end_result","revolution win - heads killed") - to_chat(world, "The heads of staff were killed or exiled! The revolutionaries win!") - else if(finished == 2) - feedback_set_details("round_end_result","revolution loss - rev heads killed") - to_chat(world, "The heads of staff managed to stop the revolution!") - ..() - return TRUE - -/datum/game_mode/proc/auto_declare_completion_revolution() - var/list/targets = list() - if(head_revolutionaries.len || GAMEMODE_IS_REVOLUTION) - var/num_revs = 0 - var/num_survivors = 0 - for(var/mob/living/carbon/survivor in GLOB.living_mob_list) - if(survivor.ckey) - num_survivors++ - if(survivor.mind) - if((survivor.mind in head_revolutionaries) || (survivor.mind in revolutionaries)) - num_revs++ - if(num_survivors) - to_chat(world, "[TAB]Command's Approval Rating: [100 - round((num_revs/num_survivors)*100, 0.1)]%") // % of loyal crew - var/text = "
    The head revolutionaries were:" - for(var/datum/mind/headrev in head_revolutionaries) - text += printplayer(headrev, 1) - text += "
    " - to_chat(world, text) - - if(revolutionaries.len || GAMEMODE_IS_REVOLUTION) - var/text = "
    The revolutionaries were:" - for(var/datum/mind/rev in revolutionaries) - text += printplayer(rev, 1) - text += "
    " - to_chat(world, text) - - if( head_revolutionaries.len || revolutionaries.len || GAMEMODE_IS_REVOLUTION ) - var/text = "
    The heads of staff were:" - var/list/heads = get_all_heads() - for(var/datum/mind/head in heads) - var/target = (head in targets) - if(target) - text += "Target" - text += printplayer(head, 1) - text += "
    " - to_chat(world, text) - -/datum/game_mode/revolution/set_scoreboard_gvars() - var/foecount = 0 - for(var/datum/mind/M in SSticker.mode.head_revolutionaries) - foecount++ - if(!M || !M.current) - score_opkilled++ - continue - - if(M.current.stat == DEAD) - score_opkilled++ - - else if(M.current.restrained()) - score_arrested++ - - if(foecount == score_arrested) - score_allarrested = 1 - - for(var/mob/living/carbon/human/player in world) - if(player.mind) - var/role = player.mind.assigned_role - if(role in list("Captain", "Head of Security", "Head of Personnel", "Chief Engineer", "Research Director")) - if(player.stat == DEAD) - score_deadcommand++ - - - var/arrestpoints = score_arrested * 1000 - var/killpoints = score_opkilled * 500 - var/comdeadpts = score_deadcommand * 500 - if(score_traitorswon) - score_crewscore -= 10000 - - score_crewscore += arrestpoints - score_crewscore += killpoints - score_crewscore -= comdeadpts - - -/datum/game_mode/revolution/get_scoreboard_stats() - var/foecount = 0 - var/comcount = 0 - var/revcount = 0 - var/loycount = 0 - for(var/datum/mind/M in SSticker.mode:head_revolutionaries) - if(M.current && M.current.stat != DEAD) - foecount++ - for(var/datum/mind/M in SSticker.mode:revolutionaries) - if(M.current && M.current.stat != DEAD) - revcount++ - for(var/mob/living/carbon/human/player in world) - if(player.mind) - var/role = player.mind.assigned_role - if(role in list("Captain", "Head of Security", "Head of Personnel", "Chief Engineer", "Research Director")) - if(player.stat != DEAD) - comcount++ - else - if(player.mind in SSticker.mode.revolutionaries) continue - loycount++ - - for(var/mob/living/silicon/X in world) - if(X.stat != DEAD) - loycount++ - - - var/dat = "" - - dat += "Mode Statistics
    " - dat += "Number of Surviving Revolution Heads: [foecount]
    " - dat += "Number of Surviving Command Staff: [comcount]
    " - dat += "Number of Surviving Revolutionaries: [revcount]
    " - dat += "Number of Surviving Loyal Crew: [loycount]
    " - - dat += "
    " - dat += "Revolution Heads Arrested: [score_arrested] ([score_arrested * 1000] Points)
    " - dat += "All Revolution Heads Arrested: [score_allarrested ? "Yes" : "No"] (Score tripled)
    " - - dat += "Revolution Heads Slain: [score_opkilled] ([score_opkilled * 500] Points)
    " - dat += "Command Staff Slain: [score_deadcommand] (-[score_deadcommand * 500] Points)
    " - dat += "Revolution Successful: [score_traitorswon ? "Yes" : "No"] (-[score_traitorswon * 10000] Points)
    " - dat += "
    " - - return dat +// To add a rev to the list of revolutionaries, make sure it's rev (with if(ticker.mode.name == "revolution)), +// then call ticker.mode:add_revolutionary(_THE_PLAYERS_MIND_) +// nothing else needs to be done, as that proc will check if they are a valid target. +// Just make sure the converter is a head before you call it! +// To remove a rev (from brainwashing or w/e), call ticker.mode:remove_revolutionary(_THE_PLAYERS_MIND_), +// this will also check they're not a head, so it can just be called freely +// If the game somtimes isn't registering a win properly, then ticker.mode.check_win() isn't being called somewhere. + +/datum/game_mode + var/list/datum/mind/head_revolutionaries = list() + var/list/datum/mind/revolutionaries = list() + +/datum/game_mode/revolution + name = "revolution" + config_tag = "revolution" + restricted_jobs = list("Security Officer", "Warden", "Detective", "Internal Affairs Agent", "AI", "Cyborg","Captain", "Head of Personnel", "Head of Security", "Chief Engineer", "Research Director", "Chief Medical Officer", "Blueshield", "Nanotrasen Representative", "Security Pod Pilot", "Magistrate", "Brig Physician") + required_players = 20 + required_enemies = 1 + recommended_enemies = 3 + + var/finished = 0 + var/check_counter = 0 + var/max_headrevs = 3 + var/list/datum/mind/heads_to_kill = list() + var/list/possible_revolutionaries = list() + +/////////////////////////// +//Announces the game type// +/////////////////////////// +/datum/game_mode/revolution/announce() + to_chat(world, "The current game mode is - Revolution!") + to_chat(world, "Some crewmembers are attempting to start a revolution!
    \nRevolutionaries - Kill the Captain, HoP, HoS, CE, RD and CMO. Convert other crewmembers (excluding the heads of staff, and security officers) to your cause by flashing them. Protect your leaders.
    \nPersonnel - Protect the heads of staff. Kill the leaders of the revolution, and brainwash the other revolutionaries (by beating them in the head).
    ") + + +/////////////////////////////////////////////////////////////////////////////// +//Gets the round setup, cancelling if there's not enough players at the start// +/////////////////////////////////////////////////////////////////////////////// +/datum/game_mode/revolution/pre_setup() + possible_revolutionaries = get_players_for_role(ROLE_REV) + + if(config.protect_roles_from_antagonist) + restricted_jobs += protected_jobs + + + for(var/i=1 to max_headrevs) + if(possible_revolutionaries.len==0) + break + var/datum/mind/lenin = pick(possible_revolutionaries) + possible_revolutionaries -= lenin + head_revolutionaries += lenin + lenin.restricted_roles = restricted_jobs + + if(head_revolutionaries.len < required_enemies) + return FALSE + + return TRUE + + +/datum/game_mode/revolution/post_setup() + var/list/heads = get_living_heads() + var/list/sec = get_living_sec() + var/weighted_score = min(max(round(heads.len - ((8 - sec.len) / 3)),1),max_headrevs) + + while(weighted_score < head_revolutionaries.len) //das vi danya + var/datum/mind/trotsky = pick(head_revolutionaries) + possible_revolutionaries += trotsky + head_revolutionaries -= trotsky + update_rev_icons_removed(trotsky) + + for(var/datum/mind/rev_mind in head_revolutionaries) + log_game("[key_name(rev_mind)] has been selected as a head rev") + for(var/datum/mind/head_mind in heads) + mark_for_death(rev_mind, head_mind) + + addtimer(CALLBACK(src, .proc/equip_revolutionary, rev_mind.current), rand(10, 100)) + + for(var/datum/mind/rev_mind in head_revolutionaries) + greet_revolutionary(rev_mind) + modePlayer += head_revolutionaries + if(SSshuttle) + SSshuttle.emergencyNoEscape = 1 + ..() + + +/datum/game_mode/revolution/process() + check_counter++ + if(check_counter >= 5) + if(!finished) + check_heads() + SSticker.mode.check_win() + check_counter = 0 + return FALSE + + +/datum/game_mode/proc/forge_revolutionary_objectives(datum/mind/rev_mind) + var/list/heads = get_living_heads() + for(var/datum/mind/head_mind in heads) + var/datum/objective/mutiny/rev_obj = new + rev_obj.owner = rev_mind + rev_obj.target = head_mind + rev_obj.explanation_text = "Assassinate or exile [head_mind.name], the [head_mind.assigned_role]." + rev_mind.objectives += rev_obj + +/datum/game_mode/proc/greet_revolutionary(datum/mind/rev_mind, you_are=1) + var/obj_count = 1 + update_rev_icons_added(rev_mind) + if(you_are) + to_chat(rev_mind.current, "You are a member of the revolutionaries' leadership!") + for(var/datum/objective/objective in rev_mind.objectives) + to_chat(rev_mind.current, "Objective #[obj_count]: [objective.explanation_text]") + rev_mind.special_role = SPECIAL_ROLE_HEAD_REV + obj_count++ + +///////////////////////////////////////////////////////////////////////////////// +//This are equips the rev heads with their gear, and makes the clown not clumsy// +///////////////////////////////////////////////////////////////////////////////// +/datum/game_mode/proc/equip_revolutionary(mob/living/carbon/human/mob) + if(!istype(mob)) + return + + if(mob.mind) + if(mob.mind.assigned_role == "Clown") + to_chat(mob, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.") + mob.mutations.Remove(CLUMSY) + var/datum/action/innate/toggle_clumsy/A = new + A.Grant(mob) + + var/obj/item/flash/T = new(mob) + var/obj/item/toy/crayon/spraycan/R = new(mob) + var/obj/item/clothing/glasses/hud/security/chameleon/C = new(mob) + + var/list/slots = list ( + "backpack" = slot_in_backpack, + "left pocket" = slot_l_store, + "right pocket" = slot_r_store, + "left hand" = slot_l_hand, + "right hand" = slot_r_hand, + ) + var/where = mob.equip_in_one_of_slots(T, slots) + var/where2 = mob.equip_in_one_of_slots(C, slots) + mob.equip_in_one_of_slots(R,slots) + + mob.update_icons() + + if(!where2) + to_chat(mob, "The Syndicate were unfortunately unable to get you a chameleon security HUD.") + else + to_chat(mob, "The chameleon security HUD in your [where2] will help you keep track of who is mindshield-implanted, and unable to be recruited.") + + if(!where) + to_chat(mob, "The Syndicate were unfortunately unable to get you a flash.") + else + to_chat(mob, "The flash in your [where] will help you to persuade the crew to join your cause.") + return 1 + +///////////////////////////////// +//Gives head revs their targets// +///////////////////////////////// +/datum/game_mode/revolution/proc/mark_for_death(datum/mind/rev_mind, datum/mind/head_mind) + var/datum/objective/mutiny/rev_obj = new + rev_obj.owner = rev_mind + rev_obj.target = head_mind + rev_obj.explanation_text = "Assassinate [head_mind.name], the [head_mind.assigned_role]." + rev_mind.objectives += rev_obj + heads_to_kill += head_mind + +//////////////////////////////////////////// +//Checks if new heads have joined midround// +//////////////////////////////////////////// +/datum/game_mode/revolution/proc/check_heads() + var/list/heads = get_all_heads() + var/list/sec = get_all_sec() + if(heads_to_kill.len < heads.len) + var/list/new_heads = heads - heads_to_kill + for(var/datum/mind/head_mind in new_heads) + for(var/datum/mind/rev_mind in head_revolutionaries) + mark_for_death(rev_mind, head_mind) + + if(head_revolutionaries.len < max_headrevs && head_revolutionaries.len < round(heads.len - ((8 - sec.len) / 3))) + latejoin_headrev() + +/////////////////////////////// +//Adds a new headrev midround// +/////////////////////////////// +/datum/game_mode/revolution/proc/latejoin_headrev() + if(revolutionaries) //Head Revs are not in this list + var/list/promotable_revs = list() + for(var/datum/mind/khrushchev in revolutionaries) + if(khrushchev.current && khrushchev.current.client && khrushchev.current.stat != DEAD) + if(ROLE_REV in khrushchev.current.client.prefs.be_special) + promotable_revs += khrushchev + if(promotable_revs.len) + var/datum/mind/stalin = pick(promotable_revs) + revolutionaries -= stalin + head_revolutionaries += stalin + log_game("[key_name(stalin)] has been promoted to a head rev") + equip_revolutionary(stalin.current) + forge_revolutionary_objectives(stalin) + greet_revolutionary(stalin) + +////////////////////////////////////// +//Checks if the revs have won or not// +////////////////////////////////////// +/datum/game_mode/revolution/check_win() + if(check_rev_victory()) + finished = 1 + else if(check_heads_victory()) + finished = 2 + return + +/////////////////////////////// +//Checks if the round is over// +/////////////////////////////// +/datum/game_mode/revolution/check_finished() + if(config.continuous_rounds) + if(finished != 0) + SSshuttle.emergencyNoEscape = 0 + if(SSshuttle.emergency.mode == SHUTTLE_STRANDED) + SSshuttle.emergency.mode = SHUTTLE_DOCKED + SSshuttle.emergency.timer = world.time + command_announcement.Announce("Hostile enviroment resolved. You have 3 minutes to board the Emergency Shuttle.", null, 'sound/AI/shuttledock.ogg') + return ..() + if(finished != 0) + return TRUE + else + return ..() + +/////////////////////////////////////////////////// +//Deals with converting players to the revolution// +/////////////////////////////////////////////////// +/datum/game_mode/proc/add_revolutionary(datum/mind/rev_mind) + if(rev_mind.assigned_role in command_positions) + return 0 + var/mob/living/carbon/human/H = rev_mind.current//Check to see if the potential rev is implanted + if(ismindshielded(H)) + return 0 + if((rev_mind in revolutionaries) || (rev_mind in head_revolutionaries)) + return 0 + revolutionaries += rev_mind + if(iscarbon(rev_mind.current)) + var/mob/living/carbon/carbon_mob = rev_mind.current + carbon_mob.Silence(5) + carbon_mob.flash_eyes(1, 1) + rev_mind.current.Stun(5) + to_chat(rev_mind.current, " You are now a revolutionary! Help your cause. Do not harm your fellow freedom fighters. You can identify your comrades by the red \"R\" icons, and your leaders by the blue \"R\" icons. Help them kill the heads to win the revolution!") + rev_mind.current.create_attack_log("Has been converted to the revolution!") + rev_mind.special_role = SPECIAL_ROLE_REV + update_rev_icons_added(rev_mind) + if(jobban_isbanned(rev_mind.current, ROLE_REV) || jobban_isbanned(rev_mind.current, ROLE_SYNDICATE)) + replace_jobbanned_player(rev_mind.current, ROLE_REV) + return 1 +////////////////////////////////////////////////////////////////////////////// +//Deals with players being converted from the revolution (Not a rev anymore)// // Modified to handle borged MMIs. Accepts another var if the target is being borged at the time -- Polymorph. +////////////////////////////////////////////////////////////////////////////// +/datum/game_mode/proc/remove_revolutionary(datum/mind/rev_mind , beingborged) + var/remove_head = 0 + if(beingborged && (rev_mind in head_revolutionaries)) + head_revolutionaries -= rev_mind + remove_head = 1 + + if((rev_mind in revolutionaries) || remove_head) + revolutionaries -= rev_mind + rev_mind.special_role = null + rev_mind.current.create_attack_log("Has renounced the revolution!") + + if(beingborged) + to_chat(rev_mind.current, "The frame's firmware detects and deletes your neural reprogramming! You remember nothing[remove_head ? "." : " but the name of the one who flashed you."]") + message_admins("[key_name_admin(rev_mind.current)] [ADMIN_QUE(rev_mind.current,"?")] ([ADMIN_FLW(rev_mind.current,"FLW")]) has been borged while being a [remove_head ? "leader" : " member"] of the revolution.") + + else + rev_mind.current.Paralyse(5) + to_chat(rev_mind.current, "You have been brainwashed! You are no longer a revolutionary! Your memory is hazy from the time you were a rebel...the only thing you remember is the name of the one who brainwashed you...") + + update_rev_icons_removed(rev_mind) + for(var/mob/living/M in view(rev_mind.current)) + if(beingborged) + to_chat(M, "The frame beeps contentedly, purging the hostile memory engram from the MMI before initalizing it.") + + else + to_chat(M, "[rev_mind.current] looks like [rev_mind.current.p_they()] just remembered [rev_mind.current.p_their()] real allegiance!") + +///////////////////////////////////// +//Adds the rev hud to a new convert// +///////////////////////////////////// +/datum/game_mode/proc/update_rev_icons_added(datum/mind/rev_mind) + var/datum/atom_hud/antag/revhud = huds[ANTAG_HUD_REV] + revhud.join_hud(rev_mind.current) + set_antag_hud(rev_mind.current, ((rev_mind in head_revolutionaries) ? "hudheadrevolutionary" : "hudrevolutionary")) + +///////////////////////////////////////// +//Removes the hud from deconverted revs// +///////////////////////////////////////// +/datum/game_mode/proc/update_rev_icons_removed(datum/mind/rev_mind) + var/datum/atom_hud/antag/revhud = huds[ANTAG_HUD_REV] + revhud.leave_hud(rev_mind.current) + set_antag_hud(rev_mind.current, null) + +////////////////////////// +//Checks for rev victory// +////////////////////////// +/datum/game_mode/revolution/proc/check_rev_victory() + for(var/datum/mind/rev_mind in head_revolutionaries) + for(var/datum/objective/mutiny/objective in rev_mind.objectives) + if(!(objective.check_completion())) + return FALSE + + return TRUE + +///////////////////////////// +//Checks for a head victory// +///////////////////////////// +/datum/game_mode/revolution/proc/check_heads_victory() + for(var/datum/mind/rev_mind in head_revolutionaries) + var/turf/T = get_turf(rev_mind.current) + if((rev_mind) && (rev_mind.current) && (rev_mind.current.stat != DEAD) && rev_mind.current.client && T && is_station_level(T.z)) + if(ishuman(rev_mind.current)) + return FALSE + return TRUE + +////////////////////////////////////////////////////////////////////// +//Announces the end of the game with all relavent information stated// +////////////////////////////////////////////////////////////////////// +/datum/game_mode/revolution/declare_completion() + if(finished == 1) + feedback_set_details("round_end_result","revolution win - heads killed") + to_chat(world, "The heads of staff were killed or exiled! The revolutionaries win!") + else if(finished == 2) + feedback_set_details("round_end_result","revolution loss - rev heads killed") + to_chat(world, "The heads of staff managed to stop the revolution!") + ..() + return TRUE + +/datum/game_mode/proc/auto_declare_completion_revolution() + var/list/targets = list() + if(head_revolutionaries.len || GAMEMODE_IS_REVOLUTION) + var/num_revs = 0 + var/num_survivors = 0 + for(var/mob/living/carbon/survivor in GLOB.living_mob_list) + if(survivor.ckey) + num_survivors++ + if(survivor.mind) + if((survivor.mind in head_revolutionaries) || (survivor.mind in revolutionaries)) + num_revs++ + if(num_survivors) + to_chat(world, "[TAB]Command's Approval Rating: [100 - round((num_revs/num_survivors)*100, 0.1)]%") // % of loyal crew + var/text = "
    The head revolutionaries were:" + for(var/datum/mind/headrev in head_revolutionaries) + text += printplayer(headrev, 1) + text += "
    " + to_chat(world, text) + + if(revolutionaries.len || GAMEMODE_IS_REVOLUTION) + var/text = "
    The revolutionaries were:" + for(var/datum/mind/rev in revolutionaries) + text += printplayer(rev, 1) + text += "
    " + to_chat(world, text) + + if( head_revolutionaries.len || revolutionaries.len || GAMEMODE_IS_REVOLUTION ) + var/text = "
    The heads of staff were:" + var/list/heads = get_all_heads() + for(var/datum/mind/head in heads) + var/target = (head in targets) + if(target) + text += "Target" + text += printplayer(head, 1) + text += "
    " + to_chat(world, text) + +/datum/game_mode/revolution/set_scoreboard_gvars() + var/foecount = 0 + for(var/datum/mind/M in SSticker.mode.head_revolutionaries) + foecount++ + if(!M || !M.current) + score_opkilled++ + continue + + if(M.current.stat == DEAD) + score_opkilled++ + + else if(M.current.restrained()) + score_arrested++ + + if(foecount == score_arrested) + score_allarrested = 1 + + for(var/mob/living/carbon/human/player in world) + if(player.mind) + var/role = player.mind.assigned_role + if(role in list("Captain", "Head of Security", "Head of Personnel", "Chief Engineer", "Research Director")) + if(player.stat == DEAD) + score_deadcommand++ + + + var/arrestpoints = score_arrested * 1000 + var/killpoints = score_opkilled * 500 + var/comdeadpts = score_deadcommand * 500 + if(score_traitorswon) + score_crewscore -= 10000 + + score_crewscore += arrestpoints + score_crewscore += killpoints + score_crewscore -= comdeadpts + + +/datum/game_mode/revolution/get_scoreboard_stats() + var/foecount = 0 + var/comcount = 0 + var/revcount = 0 + var/loycount = 0 + for(var/datum/mind/M in SSticker.mode:head_revolutionaries) + if(M.current && M.current.stat != DEAD) + foecount++ + for(var/datum/mind/M in SSticker.mode:revolutionaries) + if(M.current && M.current.stat != DEAD) + revcount++ + for(var/mob/living/carbon/human/player in world) + if(player.mind) + var/role = player.mind.assigned_role + if(role in list("Captain", "Head of Security", "Head of Personnel", "Chief Engineer", "Research Director")) + if(player.stat != DEAD) + comcount++ + else + if(player.mind in SSticker.mode.revolutionaries) continue + loycount++ + + for(var/mob/living/silicon/X in world) + if(X.stat != DEAD) + loycount++ + + + var/dat = "" + + dat += "Mode Statistics
    " + dat += "Number of Surviving Revolution Heads: [foecount]
    " + dat += "Number of Surviving Command Staff: [comcount]
    " + dat += "Number of Surviving Revolutionaries: [revcount]
    " + dat += "Number of Surviving Loyal Crew: [loycount]
    " + + dat += "
    " + dat += "Revolution Heads Arrested: [score_arrested] ([score_arrested * 1000] Points)
    " + dat += "All Revolution Heads Arrested: [score_allarrested ? "Yes" : "No"] (Score tripled)
    " + + dat += "Revolution Heads Slain: [score_opkilled] ([score_opkilled * 500] Points)
    " + dat += "Command Staff Slain: [score_deadcommand] (-[score_deadcommand * 500] Points)
    " + dat += "Revolution Successful: [score_traitorswon ? "Yes" : "No"] (-[score_traitorswon * 10000] Points)
    " + dat += "
    " + + return dat diff --git a/code/game/gamemodes/sandbox/h_sandbox.dm b/code/game/gamemodes/sandbox/h_sandbox.dm index 216c946fc97..5dd462296b9 100644 --- a/code/game/gamemodes/sandbox/h_sandbox.dm +++ b/code/game/gamemodes/sandbox/h_sandbox.dm @@ -1,161 +1,161 @@ -//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31 - -var/hsboxspawn = 1 -var/list - hrefs = list( - "hsbsuit" = "Suit Up (Space Travel Gear)", - "hsbmetal" = "Spawn 50 Metal", - "hsbglass" = "Spawn 50 Glass", - "hsbairlock" = "Spawn Airlock", - "hsbregulator" = "Spawn Air Regulator", - "hsbfilter" = "Spawn Air Filter", - "hsbcanister" = "Spawn Canister", - "hsbfueltank" = "Spawn Welding Fuel Tank", - "hsbwater tank" = "Spawn Water Tank", - "hsbtoolbox" = "Spawn Toolbox", - "hsbmedkit" = "Spawn Medical Kit") - -mob - var/datum/hSB/sandbox = null - proc - CanBuild() - if(master_mode == "sandbox") - sandbox = new/datum/hSB - sandbox.owner = src.ckey - if(src.client.holder) - sandbox.admin = 1 - verbs += new/mob/proc/sandbox_panel - sandbox_panel() - if(sandbox) - sandbox.update() - -datum/hSB - var/owner = null - var/admin = 0 - proc - update() - var/hsbpanel = "
    h_Sandbox Panel

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

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

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

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

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

    " + usr << browse(hsbpanel, "window=hsbpanel") + Topic(href, href_list) + if(!(src.owner == usr.ckey)) return + if(!usr) return //I guess this is possible if they log out or die with the panel open? It happened. + if(href_list["hsb"]) + switch(href_list["hsb"]) + if("hsbtobj") + if(!admin) return + if(hsboxspawn) + to_chat(world, "Sandbox: [usr.key] has disabled object spawning!") + hsboxspawn = 0 + return + if(!hsboxspawn) + to_chat(world, "Sandbox: [usr.key] has enabled object spawning!") + hsboxspawn = 1 + return + if("hsbsuit") + var/mob/living/carbon/human/P = usr + if(P.wear_suit) + P.wear_suit.loc = P.loc + P.wear_suit.layer = initial(P.wear_suit.layer) + P.wear_suit.plane = initial(P.wear_suit.plane) + P.wear_suit = null + P.wear_suit = new/obj/item/clothing/suit/space(P) + P.wear_suit.layer = ABOVE_HUD_LAYER + P.wear_suit.plane = ABOVE_HUD_PLANE + if(P.head) + P.head.loc = P.loc + P.head.layer = initial(P.head.layer) + P.head.plane = initial(P.head.plane) + P.head = null + P.head = new/obj/item/clothing/head/helmet/space(P) + P.head.layer = ABOVE_HUD_LAYER + P.head.plane = ABOVE_HUD_PLANE + if(P.wear_mask) + P.wear_mask.loc = P.loc + P.wear_mask.layer = initial(P.wear_mask.layer) + P.wear_mask.plane = initial(P.wear_mask.plane) + P.wear_mask = null + P.wear_mask = new/obj/item/clothing/mask/gas(P) + P.wear_mask.layer = ABOVE_HUD_LAYER + P.wear_mask.plane = ABOVE_HUD_PLANE + if(P.back) + P.back.loc = P.loc + P.back.layer = initial(P.back.layer) + P.back.plane = initial(P.back.plane) + P.back = null + P.back = new/obj/item/tank/jetpack(P) + P.back.layer = ABOVE_HUD_LAYER + P.back.plane = ABOVE_HUD_PLANE + P.internal = P.back + if("hsbmetal") + var/obj/item/stack/sheet/hsb = new/obj/item/stack/sheet/metal + hsb.amount = 50 + hsb.loc = usr.loc + if("hsbglass") + var/obj/item/stack/sheet/hsb = new/obj/item/stack/sheet/glass + hsb.amount = 50 + hsb.loc = usr.loc + if("hsbairlock") + var/obj/machinery/door/hsb = new/obj/machinery/door/airlock + + //TODO: DEFERRED make this better, with an HTML window or something instead of 15 popups + hsb.req_access = list() + var/accesses = get_all_accesses() + for(var/A in accesses) + if(alert(usr, "Will this airlock require [get_access_desc(A)] access?", "Sandbox:", "Yes", "No") == "Yes") + hsb.req_access += A + + hsb.loc = usr.loc + to_chat(usr, "Sandbox: Created an airlock.") + if("hsbcanister") + var/list/hsbcanisters = subtypesof(/obj/machinery/portable_atmospherics/canister/) + var/hsbcanister = input(usr, "Choose a canister to spawn.", "Sandbox:") in hsbcanisters + "Cancel" + if(!(hsbcanister == "Cancel")) + new hsbcanister(usr.loc) + if("hsbfueltank") + //var/obj/hsb = new/obj/weldfueltank + //hsb.loc = usr.loc + if("hsbwatertank") + //var/obj/hsb = new/obj/watertank + //hsb.loc = usr.loc + if("hsbtoolbox") + var/obj/item/storage/hsb = new/obj/item/storage/toolbox/mechanical + for(var/obj/item/radio/T in hsb) + qdel(T) + new/obj/item/crowbar (hsb) + hsb.loc = usr.loc + if("hsbmedkit") + var/obj/item/storage/firstaid/hsb = new/obj/item/storage/firstaid/regular + hsb.loc = usr.loc + if("hsbobj") + if(!hsboxspawn) return + + var/list/selectable = list() + for(var/O in typesof(/obj/item/)) + //Note, these istypes don't work + if(istype(O, /obj/item/gun)) + continue + if(istype(O, /obj/item/assembly)) + continue + if(istype(O, /obj/item/camera)) + continue + if(istype(O, /obj/item/cloaking_device)) + continue + if(istype(O, /obj/item/dummy)) + continue + if(istype(O, /obj/item/melee/energy/sword/saber)) + continue + if(istype(O, /obj/structure)) + continue + selectable += O + + var/hsbitem = input(usr, "Choose an object to spawn.", "Sandbox:") in selectable + "Cancel" + if(hsbitem != "Cancel") + new hsbitem(usr.loc) diff --git a/code/game/gamemodes/sandbox/sandbox.dm b/code/game/gamemodes/sandbox/sandbox.dm index 663759c961c..7b6978f4a1b 100644 --- a/code/game/gamemodes/sandbox/sandbox.dm +++ b/code/game/gamemodes/sandbox/sandbox.dm @@ -1,21 +1,21 @@ -/datum/game_mode/sandbox - name = "sandbox" - config_tag = "sandbox" - required_players = 0 - - uplink_welcome = "Syndicate Uplink Console:" - uplink_uses = 10 - -/datum/game_mode/sandbox/announce() - to_chat(world, "The current game mode is - Sandbox!") - to_chat(world, "Build your own station with the sandbox-panel command!") - -/datum/game_mode/sandbox/pre_setup() - for(var/mob/M in GLOB.player_list) - M.CanBuild() - return 1 - -/datum/game_mode/sandbox/post_setup() - ..() - if(emergency_shuttle) - emergency_shuttle.no_escape = 1 +/datum/game_mode/sandbox + name = "sandbox" + config_tag = "sandbox" + required_players = 0 + + uplink_welcome = "Syndicate Uplink Console:" + uplink_uses = 10 + +/datum/game_mode/sandbox/announce() + to_chat(world, "The current game mode is - Sandbox!") + to_chat(world, "Build your own station with the sandbox-panel command!") + +/datum/game_mode/sandbox/pre_setup() + for(var/mob/M in GLOB.player_list) + M.CanBuild() + return 1 + +/datum/game_mode/sandbox/post_setup() + ..() + if(emergency_shuttle) + emergency_shuttle.no_escape = 1 diff --git a/code/game/gamemodes/setupgame.dm b/code/game/gamemodes/setupgame.dm index 78e6cea4987..507aaec0135 100644 --- a/code/game/gamemodes/setupgame.dm +++ b/code/game/gamemodes/setupgame.dm @@ -1,164 +1,164 @@ -/proc/getAssignedBlock(var/name,var/list/blocksLeft, var/activity_bounds=DNA_DEFAULT_BOUNDS, var/good=0) - if(blocksLeft.len==0) - warning("[name]: No more blocks left to assign!") - return 0 - var/assigned = pick(blocksLeft) - blocksLeft.Remove(assigned) - if(good) - good_blocks += assigned - else - bad_blocks += assigned - assigned_blocks[assigned]=name - dna_activity_bounds[assigned]=activity_bounds - //Debug message_admins("[name] assigned to block #[assigned].") -// testing("[name] assigned to block #[assigned].") - return assigned - -/proc/setupgenetics() - - if(prob(50)) - BLOCKADD = rand(-300,300) - if(prob(75)) - DIFFMUT = rand(0,20) - - -//Thanks to nexis for the fancy code -// BITCH I AIN'T DONE YET - - // SE blocks to assign. - var/list/numsToAssign=new() - for(var/i=1;iThe current game mode is - Traitor!") - to_chat(world, "There is a syndicate traitor on the station. Do not let the traitor succeed!") - - -/datum/game_mode/traitor/pre_setup() - - if(config.protect_roles_from_antagonist) - restricted_jobs += protected_jobs - - var/list/possible_traitors = get_players_for_role(ROLE_TRAITOR) - - // stop setup if no possible traitors - if(!possible_traitors.len) - return 0 - - var/num_traitors = 1 - - if(config.traitor_scaling) - num_traitors = max(1, round((num_players())/(traitor_scaling_coeff))) - else - num_traitors = max(1, min(num_players(), traitors_possible)) - - for(var/j = 0, j < num_traitors, j++) - if(!possible_traitors.len) - break - var/datum/mind/traitor = pick(possible_traitors) - pre_traitors += traitor - traitor.special_role = SPECIAL_ROLE_TRAITOR - traitor.restricted_roles = restricted_jobs - possible_traitors.Remove(traitor) - - if(!pre_traitors.len) - return 0 - return 1 - - -/datum/game_mode/traitor/post_setup() - for(var/datum/mind/traitor in pre_traitors) - var/datum/antagonist/traitor/new_antag = new antag_datum() - addtimer(CALLBACK(traitor, /datum/mind.proc/add_antag_datum, new_antag), rand(10,100)) - if(!exchange_blue) - exchange_blue = -1 //Block latejoiners from getting exchange objectives - ..() - - -/datum/game_mode/traitor/declare_completion() - ..() - return//Traitors will be checked as part of check_extra_completion. Leaving this here as a reminder. - -/datum/game_mode/traitor/process() - // Make sure all objectives are processed regularly, so that objectives - // which can be checked mid-round are checked mid-round. - for(var/datum/mind/traitor_mind in traitors) - for(var/datum/objective/objective in traitor_mind.objectives) - objective.check_completion() - return 0 - - -/datum/game_mode/proc/auto_declare_completion_traitor() - if(traitors.len) - var/text = "The traitors were:" - for(var/datum/mind/traitor in traitors) - var/traitorwin = 1 - - text += "
    [traitor.key] was [traitor.name] (" - if(traitor.current) - if(traitor.current.stat == DEAD) - text += "died" - else - text += "survived" - if(traitor.current.real_name != traitor.name) - text += " as [traitor.current.real_name]" - else - text += "body destroyed" - text += ")" - - - var/TC_uses = 0 - var/uplink_true = 0 - var/purchases = "" - for(var/obj/item/uplink/H in world_uplinks) - if(H && H.uplink_owner && H.uplink_owner==traitor.key) - TC_uses += H.used_TC - uplink_true=1 - purchases += H.purchase_log - - if(uplink_true) text += " (used [TC_uses] TC) [purchases]" - - - if(traitor.objectives && traitor.objectives.len)//If the traitor had no objectives, don't need to process this. - var/count = 1 - for(var/datum/objective/objective in traitor.objectives) - if(objective.check_completion()) - text += "
    Objective #[count]: [objective.explanation_text] Success!" - feedback_add_details("traitor_objective","[objective.type]|SUCCESS") - else - text += "
    Objective #[count]: [objective.explanation_text] Fail." - feedback_add_details("traitor_objective","[objective.type]|FAIL") - traitorwin = 0 - count++ - - var/special_role_text - if(traitor.special_role) - special_role_text = lowertext(traitor.special_role) - else - special_role_text = "antagonist" - - - if(traitorwin) - text += "
    The [special_role_text] was successful!" - feedback_add_details("traitor_success","SUCCESS") - else - text += "
    The [special_role_text] has failed!" - feedback_add_details("traitor_success","FAIL") - - var/phrases = jointext(GLOB.syndicate_code_phrase, ", ") - var/responses = jointext(GLOB.syndicate_code_response, ", ") - - text += "

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

      " + dat += "Items are not bound to you and can be stolen. Additionaly they cannot typically be returned once purchased.
      " + return dat + +/obj/item/spellbook/proc/wrap(content) + var/dat = "" + dat +="Spellbook" + dat += {" + + + + "} + dat += {"[content]"} + return dat + +/obj/item/spellbook/attack_self(mob/user as mob) + if(!owner) + to_chat(user, "You bind the spellbook to yourself.") + owner = user + return + if(user != owner) + to_chat(user, "The [name] does not recognize you as it's owner and refuses to open!") + return + user.set_machine(src) + var/dat = "" + + dat += "
        " + var/list/cat_dat = list() + for(var/main_category in main_categories) + cat_dat[main_category] = "
        " + dat += "
      • [main_category]
      • " + dat += "
      " + switch(main_tab) + if("Spells") + dat += "
        " + for(var/category in categories) + if(category in item_categories) + continue + cat_dat[category] = "
        " + dat += "
      • [category]
      • " + dat += "
      • Points remaining : [uses]
      • " + if("Magical Items") + dat += "
          " + for(var/category in categories) + if(category in spell_categories) + continue + cat_dat[category] = "
          " + dat += "
        • [category]
        • " + dat += "
        • Points remaining : [uses]
        • " + dat += "
        " + + var/datum/spellbook_entry/E + for(var/i=1,i<=entries.len,i++) + var/spell_info = "" + E = entries[i] + spell_info += E.GetInfo() + if(E.CanBuy(user,src)) + spell_info+= "[E.buy_word]
        " + else + spell_info+= "Can't [E.buy_word]
        " + if(E.CanRefund(user,src)) + spell_info+= "Refund
        " + spell_info += "
        " + if(cat_dat[E.category]) + cat_dat[E.category] += spell_info + + for(var/category in categories) + dat += "
        " + dat += GetCategoryHeader(category) + dat += cat_dat[category] + dat += "
        " + + user << browse(wrap(dat), "window=spellbook;size=800x600") + onclose(user, "spellbook") + return + +/obj/item/spellbook/Topic(href, href_list) + if(..()) + return 1 + var/mob/living/carbon/human/H = usr + + if(!ishuman(H)) + return 1 + + if(H.mind.special_role == SPECIAL_ROLE_WIZARD_APPRENTICE) + temp = "If you got caught sneaking a peak from your teacher's spellbook, you'd likely be expelled from the Wizard Academy. Better not." + return 1 + + var/datum/spellbook_entry/E = null + if(loc == H || (in_range(src, H) && istype(loc, /turf))) + H.set_machine(src) + if(href_list["buy"]) + E = entries[text2num(href_list["buy"])] + if(E && E.CanBuy(H,src)) + if(E.Buy(H,src)) + if(E.limit) + E.limit-- + uses -= E.cost + else if(href_list["refund"]) + E = entries[text2num(href_list["refund"])] + if(E && E.refundable) + var/result = E.Refund(H,src) + if(result > 0) + if(!isnull(E.limit)) + E.limit += result + uses += result + else if(href_list["mainpage"]) + main_tab = sanitize(href_list["mainpage"]) + tab = sanitize(href_list["page"]) + if(main_tab == "Spells") + tab = spell_categories[1] + else if(main_tab == "Magical Items") + tab = item_categories[1] + else if(href_list["page"]) + tab = sanitize(href_list["page"]) + attack_self(H) + return 1 + +//Single Use Spellbooks +/obj/item/spellbook/oneuse + var/spell = /obj/effect/proc_holder/spell/targeted/projectile/magic_missile //just a placeholder to avoid runtimes if someone spawned the generic + var/spellname = "sandbox" + var/used = 0 + name = "spellbook of " + uses = 1 + desc = "This template spellbook was never meant for the eyes of man..." + +/obj/item/spellbook/oneuse/New() + ..() + name += spellname + +/obj/item/spellbook/oneuse/initialize() //No need to init + return + +/obj/item/spellbook/oneuse/attack_self(mob/user) + var/obj/effect/proc_holder/spell/S = new spell + for(var/obj/effect/proc_holder/spell/knownspell in user.mind.spell_list) + if(knownspell.type == S.type) + if(user.mind) + if(user.mind.special_role == SPECIAL_ROLE_WIZARD_APPRENTICE || user.mind.special_role == SPECIAL_ROLE_WIZARD) + to_chat(user, "You're already far more versed in this spell than this flimsy how-to book can provide.") + else + to_chat(user, "You've already read this one.") + return + if(used) + recoil(user) + else + user.mind.AddSpell(S) + to_chat(user, "you rapidly read through the arcane book. Suddenly you realize you understand [spellname]!") + user.create_attack_log("[key_name(user)] learned the spell [spellname] ([S]).") + onlearned(user) + +/obj/item/spellbook/oneuse/proc/recoil(mob/user) + user.visible_message("[src] glows in a black light!") + +/obj/item/spellbook/oneuse/proc/onlearned(mob/user) + used = 1 + user.visible_message("[src] glows dark for a second!") + +/obj/item/spellbook/oneuse/attackby() + return + +/obj/item/spellbook/oneuse/fireball + spell = /obj/effect/proc_holder/spell/fireball + spellname = "fireball" + icon_state = "bookfireball" + desc = "This book feels warm to the touch." + +/obj/item/spellbook/oneuse/fireball/recoil(mob/user as mob) + ..() + explosion(user.loc, -1, 0, 2, 3, 0, flame_range = 2) + qdel(src) + +/obj/item/spellbook/oneuse/smoke + spell = /obj/effect/proc_holder/spell/targeted/smoke + spellname = "smoke" + icon_state = "booksmoke" + desc = "This book is overflowing with the dank arts." + +/obj/item/spellbook/oneuse/smoke/recoil(mob/user as mob) + ..() + to_chat(user, "Your stomach rumbles...") + user.adjust_nutrition(-200) + +/obj/item/spellbook/oneuse/blind + spell = /obj/effect/proc_holder/spell/targeted/trigger/blind + spellname = "blind" + icon_state = "bookblind" + desc = "This book looks blurry, no matter how you look at it." + +/obj/item/spellbook/oneuse/blind/recoil(mob/user as mob) + ..() + to_chat(user, "You go blind!") + user.EyeBlind(10) + +/obj/item/spellbook/oneuse/mindswap + spell = /obj/effect/proc_holder/spell/targeted/mind_transfer + spellname = "mindswap" + icon_state = "bookmindswap" + desc = "This book's cover is pristine, though its pages look ragged and torn." + var/mob/stored_swap = null //Used in used book recoils to store an identity for mindswaps + +/obj/item/spellbook/oneuse/mindswap/onlearned() + spellname = pick("fireball","smoke","blind","forcewall","knock","horses","charge") + icon_state = "book[spellname]" + name = "spellbook of [spellname]" //Note, desc doesn't change by design + ..() + +/obj/item/spellbook/oneuse/mindswap/recoil(mob/user) + ..() + if(stored_swap in GLOB.dead_mob_list) + stored_swap = null + if(!stored_swap) + stored_swap = user + to_chat(user, "For a moment you feel like you don't even know who you are anymore.") + return + if(stored_swap == user) + to_chat(user, "You stare at the book some more, but there doesn't seem to be anything else to learn...") + return + + var/obj/effect/proc_holder/spell/targeted/mind_transfer/swapper = new + swapper.cast(user, stored_swap, 1) + + to_chat(stored_swap, "You're suddenly somewhere else... and someone else?!") + to_chat(user, "Suddenly you're staring at [src] again... where are you, who are you?!") + stored_swap = null + +/obj/item/spellbook/oneuse/forcewall + spell = /obj/effect/proc_holder/spell/targeted/forcewall + spellname = "forcewall" + icon_state = "bookforcewall" + desc = "This book has a dedication to mimes everywhere inside the front cover." + +/obj/item/spellbook/oneuse/forcewall/recoil(mob/user as mob) + ..() + to_chat(user, "You suddenly feel very solid!") + var/obj/structure/closet/statue/S = new /obj/structure/closet/statue(user.loc, user) + S.timer = 30 + user.drop_item() + +/obj/item/spellbook/oneuse/knock + spell = /obj/effect/proc_holder/spell/aoe_turf/knock + spellname = "knock" + icon_state = "bookknock" + desc = "This book is hard to hold closed properly." + +/obj/item/spellbook/oneuse/knock/recoil(mob/user as mob) + ..() + to_chat(user, "You're knocked down!") + user.Weaken(20) + +/obj/item/spellbook/oneuse/horsemask + spell = /obj/effect/proc_holder/spell/targeted/horsemask + spellname = "horses" + icon_state = "bookhorses" + desc = "This book is more horse than your mind has room for." + +/obj/item/spellbook/oneuse/horsemask/recoil(mob/living/carbon/user as mob) + if(istype(user, /mob/living/carbon/human)) + to_chat(user, "HOR-SIE HAS RISEN") + var/obj/item/clothing/mask/horsehead/magichead = new /obj/item/clothing/mask/horsehead + magichead.flags |= NODROP | DROPDEL //curses! + magichead.flags_inv = null //so you can still see their face + magichead.voicechange = 1 //NEEEEIIGHH + if(!user.unEquip(user.wear_mask)) + qdel(user.wear_mask) + user.equip_to_slot_if_possible(magichead, slot_wear_mask, 1, 1) + qdel(src) + else + to_chat(user, "I say thee neigh") + +/obj/item/spellbook/oneuse/charge + spell = /obj/effect/proc_holder/spell/targeted/charge + spellname = "charging" + icon_state = "bookcharge" + desc = "This book is made of 100% post-consumer wizard." + +/obj/item/spellbook/oneuse/charge/recoil(mob/user as mob) + ..() + to_chat(user, "[src] suddenly feels very warm!") + empulse(src, 1, 1) + +/obj/item/spellbook/oneuse/summonitem + spell = /obj/effect/proc_holder/spell/targeted/summonitem + spellname = "instant summons" + icon_state = "booksummons" + desc = "This book is bright and garish, very hard to miss." + +/obj/item/spellbook/oneuse/summonitem/recoil(mob/user as mob) + ..() + to_chat(user, "[src] suddenly vanishes!") + qdel(src) + +/obj/item/spellbook/oneuse/fake_gib + spell = /obj/effect/proc_holder/spell/targeted/touch/fake_disintegrate + spellname = "disintegrate" + icon_state = "bookfireball" + desc = "This book feels like it will rip stuff apart." + +/obj/item/spellbook/oneuse/sacredflame + spell = /obj/effect/proc_holder/spell/targeted/sacred_flame + spellname = "sacred flame" + icon_state = "booksacredflame" + desc = "Become one with the flames that burn within... and invite others to do so as well." + +/obj/item/spellbook/oneuse/random + icon_state = "random_book" + +/obj/item/spellbook/oneuse/random/initialize() + . = ..() + var/static/banned_spells = list(/obj/item/spellbook/oneuse/mime, /obj/item/spellbook/oneuse/mime/fingergun, /obj/item/spellbook/oneuse/mime/fingergun/fake, /obj/item/spellbook/oneuse/mime/greaterwall) + var/real_type = pick(subtypesof(/obj/item/spellbook/oneuse) - banned_spells) + new real_type(loc) + qdel(src) diff --git a/code/game/gamemodes/wizard/wizard.dm b/code/game/gamemodes/wizard/wizard.dm index 131afd84fff..ba310ed0e8f 100644 --- a/code/game/gamemodes/wizard/wizard.dm +++ b/code/game/gamemodes/wizard/wizard.dm @@ -1,282 +1,283 @@ -/datum/game_mode - var/list/datum/mind/wizards = list() - -/datum/game_mode/wizard - name = "wizard" - config_tag = "wizard" - required_players = 20 - required_enemies = 1 - recommended_enemies = 1 - var/use_huds = 1 - - var/finished = 0 - var/but_wait_theres_more = 0 - -/datum/game_mode/wizard/announce() - to_chat(world, "The current game mode is - Wizard!") - to_chat(world, "There is a SPACE WIZARD on the station. You can't let him achieve his objective!") - -/datum/game_mode/wizard/can_start()//This could be better, will likely have to recode it later - if(!..()) - return 0 - var/list/datum/mind/possible_wizards = get_players_for_role(ROLE_WIZARD) - if(possible_wizards.len==0) - return 0 - var/datum/mind/wizard = pick(possible_wizards) - - wizards += wizard - modePlayer += wizard - wizard.assigned_role = SPECIAL_ROLE_WIZARD //So they aren't chosen for other jobs. - wizard.special_role = SPECIAL_ROLE_WIZARD - wizard.original = wizard.current - if(wizardstart.len == 0) - to_chat(wizard.current, "A starting location for you could not be found, please report this bug!") - return 0 - return 1 - -/datum/game_mode/wizard/pre_setup() - for(var/datum/mind/wiz in wizards) - wiz.current.loc = pick(wizardstart) - ..() - return 1 - -/datum/game_mode/wizard/post_setup() - for(var/datum/mind/wizard in wizards) - log_game("[key_name(wizard)] has been selected as a Wizard") - forge_wizard_objectives(wizard) - //learn_basic_spells(wizard.current) - equip_wizard(wizard.current) - name_wizard(wizard.current) - greet_wizard(wizard) - if(use_huds) - update_wiz_icons_added(wizard) - - ..() - -/datum/game_mode/proc/remove_wizard(datum/mind/wizard_mind) - if(wizard_mind in wizards) - SSticker.mode.wizards -= wizard_mind - wizard_mind.special_role = null - wizard_mind.current.create_attack_log("De-wizarded") - wizard_mind.current.spellremove(wizard_mind.current) - wizard_mind.current.faction = list("Station") - if(issilicon(wizard_mind.current)) - to_chat(wizard_mind.current, "You have been turned into a robot! You can feel your magical powers fading away...") - else - to_chat(wizard_mind.current, "You have been brainwashed! You are no longer a wizard.") - SSticker.mode.update_wiz_icons_removed(wizard_mind) - -/datum/game_mode/proc/update_wiz_icons_added(datum/mind/wiz_mind) - var/datum/atom_hud/antag/wizhud = huds[ANTAG_HUD_WIZ] - wizhud.join_hud(wiz_mind.current) - set_antag_hud(wiz_mind.current, ((wiz_mind in wizards) ? "hudwizard" : "apprentice")) - -/datum/game_mode/proc/update_wiz_icons_removed(datum/mind/wiz_mind) - var/datum/atom_hud/antag/wizhud = huds[ANTAG_HUD_WIZ] - wizhud.leave_hud(wiz_mind.current) - set_antag_hud(wiz_mind.current, null) - -/datum/game_mode/proc/forge_wizard_objectives(var/datum/mind/wizard) - var/datum/objective/wizchaos/wiz_objective = new - wiz_objective.owner = wizard - wizard.objectives += wiz_objective - return - -/datum/game_mode/proc/name_wizard(mob/living/carbon/human/wizard_mob) - //Allows the wizard to choose a custom name or go with a random one. Spawn 0 so it does not lag the round starting. - var/wizard_name_first = pick(GLOB.wizard_first) - var/wizard_name_second = pick(GLOB.wizard_second) - var/randomname = "[wizard_name_first] [wizard_name_second]" - spawn(0) - var/newname = sanitize(copytext(input(wizard_mob, "You are the Space Wizard. Would you like to change your name to something else?", "Name change", randomname) as null|text,1,MAX_NAME_LEN)) - - if(!newname) - newname = randomname - - wizard_mob.real_name = newname - wizard_mob.name = newname - if(wizard_mob.mind) - wizard_mob.mind.name = newname - return - -/datum/game_mode/proc/greet_wizard(var/datum/mind/wizard, var/you_are=1) - addtimer(CALLBACK(wizard.current, /mob/.proc/playsound_local, null, 'sound/ambience/antag/ragesmages.ogg', 100, 0), 30) - if(you_are) - to_chat(wizard.current, "You are the Space Wizard!") - to_chat(wizard.current, "The Space Wizards Federation has given you the following tasks:") - - var/obj_count = 1 - for(var/datum/objective/objective in wizard.objectives) - to_chat(wizard.current, "Objective #[obj_count]: [objective.explanation_text]") - obj_count++ - return - -/*/datum/game_mode/proc/learn_basic_spells(mob/living/carbon/human/wizard_mob) - if(!istype(wizard_mob)) - return - if(!config.feature_object_spell_system) - wizard_mob.verbs += /client/proc/jaunt - wizard_mob.mind.special_verbs += /client/proc/jaunt - else - wizard_mob.spell_list += new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt(usr) -*/ - -/datum/game_mode/proc/equip_wizard(mob/living/carbon/human/wizard_mob) - if(!istype(wizard_mob)) - return - - //So zards properly get their items when they are admin-made. - qdel(wizard_mob.wear_suit) - qdel(wizard_mob.head) - qdel(wizard_mob.shoes) - qdel(wizard_mob.r_hand) - qdel(wizard_mob.r_store) - qdel(wizard_mob.l_store) - - wizard_mob.equip_to_slot_or_del(new /obj/item/radio/headset(wizard_mob), slot_l_ear) - wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/under/color/lightpurple(wizard_mob), slot_w_uniform) - wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(wizard_mob), slot_shoes) - if(!isplasmaman(wizard_mob)) //handled in the species file for plasmen on the afterjob equip proc for now - wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe(wizard_mob), slot_wear_suit) - wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/head/wizard(wizard_mob), slot_head) - wizard_mob.equip_to_slot_or_del(new /obj/item/storage/backpack/satchel(wizard_mob), slot_back) - wizard_mob.equip_to_slot_or_del(new /obj/item/storage/box/survival(wizard_mob), slot_in_backpack) - wizard_mob.equip_to_slot_or_del(new /obj/item/teleportation_scroll(wizard_mob), slot_r_store) - var/obj/item/spellbook/spellbook = new /obj/item/spellbook(wizard_mob) - spellbook.owner = wizard_mob - wizard_mob.equip_to_slot_or_del(spellbook, slot_r_hand) - - wizard_mob.faction = list("wizard") - - wizard_mob.dna.species.after_equip_job(null, wizard_mob) - - to_chat(wizard_mob, "You will find a list of available spells in your spell book. Choose your magic arsenal carefully.") - to_chat(wizard_mob, "The spellbook is bound to you, and others cannot use it.") - to_chat(wizard_mob, "In your pockets you will find a teleport scroll. Use it as needed.") - wizard_mob.mind.store_memory("Remember: do not forget to prepare your spells.") - wizard_mob.update_icons() - wizard_mob.gene_stability += DEFAULT_GENE_STABILITY //magic - return 1 - -// Checks if the game should end due to all wizards and apprentices being dead, or MMI'd/Borged -/datum/game_mode/wizard/check_finished() - var/wizards_alive = 0 - var/traitors_alive = 0 - - // Wizards - for(var/datum/mind/wizard in wizards) - if(!istype(wizard.current,/mob/living/carbon)) - continue - if(wizard.current.stat==DEAD) - continue - if(istype(wizard.current, /obj/item/mmi)) // wizard is in an MMI, don't count them as alive - continue - wizards_alive++ - - // Apprentices - classified as "traitors" - if(!wizards_alive) - for(var/datum/mind/traitor in traitors) - if(!istype(traitor.current,/mob/living/carbon)) - continue - if(traitor.current.stat==DEAD) - continue - if(istype(traitor.current, /obj/item/mmi)) // apprentice is in an MMI, don't count them as alive - continue - traitors_alive++ - - if(wizards_alive || traitors_alive || but_wait_theres_more) - return ..() - else - finished = 1 - return 1 - -/datum/game_mode/wizard/declare_completion(var/ragin = 0) - if(finished && !ragin) - feedback_set_details("round_end_result","wizard loss - wizard killed") - to_chat(world, " The wizard[(wizards.len>1)?"s":""] has been killed by the crew! The Space Wizards Federation has been taught a lesson they will not soon forget!") - ..() - return 1 - -/datum/game_mode/proc/auto_declare_completion_wizard() - if(wizards.len) - var/text = "
        the wizards/witches were:" - - for(var/datum/mind/wizard in wizards) - - text += "
        [wizard.key] was [wizard.name] (" - if(wizard.current) - if(wizard.current.stat == DEAD) - text += "died" - else - text += "survived" - if(wizard.current.real_name != wizard.name) - text += " as [wizard.current.real_name]" - else - text += "body destroyed" - text += ")" - - var/count = 1 - var/wizardwin = 1 - for(var/datum/objective/objective in wizard.objectives) - if(objective.check_completion()) - text += "
        Objective #[count]: [objective.explanation_text] Success!" - feedback_add_details("wizard_objective","[objective.type]|SUCCESS") - else - text += "
        Objective #[count]: [objective.explanation_text] Fail." - feedback_add_details("wizard_objective","[objective.type]|FAIL") - wizardwin = 0 - count++ - - if(wizard.current && wizard.current.stat!=DEAD && wizardwin) - text += "
        The wizard was successful!" - feedback_add_details("wizard_success","SUCCESS") - else - text += "
        The wizard has failed!" - feedback_add_details("wizard_success","FAIL") - if(wizard.spell_list) - text += "
        [wizard.name] used the following spells: " - var/i = 1 - for(var/obj/effect/proc_holder/spell/S in wizard.spell_list) - text += "[S.name]" - if(wizard.spell_list.len > i) - text += ", " - i++ - text += "
        " - - to_chat(world, text) - return 1 - -//OTHER PROCS - -//To batch-remove wizard spells. Linked to mind.dm -/mob/proc/spellremove(mob/M) - if(!mind) - return - for(var/obj/effect/proc_holder/spell/spell_to_remove in mind.spell_list) - qdel(spell_to_remove) - mind.spell_list -= spell_to_remove - -//To batch-remove mob spells. -/mob/proc/mobspellremove(mob/M) - for(var/obj/effect/proc_holder/spell/spell_to_remove in mob_spell_list) - qdel(spell_to_remove) - mob_spell_list -= spell_to_remove - -/*Checks if the wizard can cast spells. -Made a proc so this is not repeated 14 (or more) times.*/ -/mob/proc/casting() -//Removed the stat check because not all spells require clothing now. - if(!istype(usr:wear_suit, /obj/item/clothing/suit/wizrobe)) - to_chat(usr, "I don't feel strong enough without my robe.") - return 0 - if(!istype(usr:shoes, /obj/item/clothing/shoes/sandal)) - to_chat(usr, "I don't feel strong enough without my sandals.") - return 0 - if(!istype(usr:head, /obj/item/clothing/head/wizard)) - to_chat(usr, "I don't feel strong enough without my hat.") - return 0 - else - return 1 - -/proc/iswizard(mob/living/M as mob) - return istype(M) && M.mind && SSticker && SSticker.mode && (M.mind in SSticker.mode.wizards) +/datum/game_mode + var/list/datum/mind/wizards = list() + var/list/datum/mind/apprentices = list() + +/datum/game_mode/wizard + name = "wizard" + config_tag = "wizard" + required_players = 20 + required_enemies = 1 + recommended_enemies = 1 + var/use_huds = 1 + + var/finished = 0 + var/but_wait_theres_more = 0 + +/datum/game_mode/wizard/announce() + to_chat(world, "The current game mode is - Wizard!") + to_chat(world, "There is a SPACE WIZARD on the station. You can't let him achieve his objective!") + +/datum/game_mode/wizard/can_start()//This could be better, will likely have to recode it later + if(!..()) + return 0 + var/list/datum/mind/possible_wizards = get_players_for_role(ROLE_WIZARD) + if(possible_wizards.len==0) + return 0 + var/datum/mind/wizard = pick(possible_wizards) + + wizards += wizard + modePlayer += wizard + wizard.assigned_role = SPECIAL_ROLE_WIZARD //So they aren't chosen for other jobs. + wizard.special_role = SPECIAL_ROLE_WIZARD + wizard.original = wizard.current + if(wizardstart.len == 0) + to_chat(wizard.current, "A starting location for you could not be found, please report this bug!") + return 0 + return 1 + +/datum/game_mode/wizard/pre_setup() + for(var/datum/mind/wiz in wizards) + wiz.current.loc = pick(wizardstart) + ..() + return 1 + +/datum/game_mode/wizard/post_setup() + for(var/datum/mind/wizard in wizards) + log_game("[key_name(wizard)] has been selected as a Wizard") + forge_wizard_objectives(wizard) + //learn_basic_spells(wizard.current) + equip_wizard(wizard.current) + name_wizard(wizard.current) + greet_wizard(wizard) + if(use_huds) + update_wiz_icons_added(wizard) + + ..() + +/datum/game_mode/proc/remove_wizard(datum/mind/wizard_mind) + if(wizard_mind in wizards) + SSticker.mode.wizards -= wizard_mind + wizard_mind.special_role = null + wizard_mind.current.create_attack_log("De-wizarded") + wizard_mind.current.spellremove(wizard_mind.current) + wizard_mind.current.faction = list("Station") + if(issilicon(wizard_mind.current)) + to_chat(wizard_mind.current, "You have been turned into a robot! You can feel your magical powers fading away...") + else + to_chat(wizard_mind.current, "You have been brainwashed! You are no longer a wizard.") + SSticker.mode.update_wiz_icons_removed(wizard_mind) + +/datum/game_mode/proc/update_wiz_icons_added(datum/mind/wiz_mind) + var/datum/atom_hud/antag/wizhud = huds[ANTAG_HUD_WIZ] + wizhud.join_hud(wiz_mind.current) + set_antag_hud(wiz_mind.current, ((wiz_mind in wizards) ? "hudwizard" : "apprentice")) + +/datum/game_mode/proc/update_wiz_icons_removed(datum/mind/wiz_mind) + var/datum/atom_hud/antag/wizhud = huds[ANTAG_HUD_WIZ] + wizhud.leave_hud(wiz_mind.current) + set_antag_hud(wiz_mind.current, null) + +/datum/game_mode/proc/forge_wizard_objectives(var/datum/mind/wizard) + var/datum/objective/wizchaos/wiz_objective = new + wiz_objective.owner = wizard + wizard.objectives += wiz_objective + return + +/datum/game_mode/proc/name_wizard(mob/living/carbon/human/wizard_mob) + //Allows the wizard to choose a custom name or go with a random one. Spawn 0 so it does not lag the round starting. + var/wizard_name_first = pick(GLOB.wizard_first) + var/wizard_name_second = pick(GLOB.wizard_second) + var/randomname = "[wizard_name_first] [wizard_name_second]" + spawn(0) + var/newname = sanitize(copytext(input(wizard_mob, "You are the Space Wizard. Would you like to change your name to something else?", "Name change", randomname) as null|text,1,MAX_NAME_LEN)) + + if(!newname) + newname = randomname + + wizard_mob.real_name = newname + wizard_mob.name = newname + if(wizard_mob.mind) + wizard_mob.mind.name = newname + return + +/datum/game_mode/proc/greet_wizard(var/datum/mind/wizard, var/you_are=1) + addtimer(CALLBACK(wizard.current, /mob/.proc/playsound_local, null, 'sound/ambience/antag/ragesmages.ogg', 100, 0), 30) + if(you_are) + to_chat(wizard.current, "You are the Space Wizard!") + to_chat(wizard.current, "The Space Wizards Federation has given you the following tasks:") + + var/obj_count = 1 + for(var/datum/objective/objective in wizard.objectives) + to_chat(wizard.current, "Objective #[obj_count]: [objective.explanation_text]") + obj_count++ + return + +/*/datum/game_mode/proc/learn_basic_spells(mob/living/carbon/human/wizard_mob) + if(!istype(wizard_mob)) + return + if(!config.feature_object_spell_system) + wizard_mob.verbs += /client/proc/jaunt + wizard_mob.mind.special_verbs += /client/proc/jaunt + else + wizard_mob.spell_list += new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt(usr) +*/ + +/datum/game_mode/proc/equip_wizard(mob/living/carbon/human/wizard_mob) + if(!istype(wizard_mob)) + return + + //So zards properly get their items when they are admin-made. + qdel(wizard_mob.wear_suit) + qdel(wizard_mob.head) + qdel(wizard_mob.shoes) + qdel(wizard_mob.r_hand) + qdel(wizard_mob.r_store) + qdel(wizard_mob.l_store) + + wizard_mob.equip_to_slot_or_del(new /obj/item/radio/headset(wizard_mob), slot_l_ear) + wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/under/color/lightpurple(wizard_mob), slot_w_uniform) + wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/shoes/sandal(wizard_mob), slot_shoes) + if(!isplasmaman(wizard_mob)) //handled in the species file for plasmen on the afterjob equip proc for now + wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/suit/wizrobe(wizard_mob), slot_wear_suit) + wizard_mob.equip_to_slot_or_del(new /obj/item/clothing/head/wizard(wizard_mob), slot_head) + wizard_mob.equip_to_slot_or_del(new /obj/item/storage/backpack/satchel(wizard_mob), slot_back) + wizard_mob.equip_to_slot_or_del(new /obj/item/storage/box/survival(wizard_mob), slot_in_backpack) + wizard_mob.equip_to_slot_or_del(new /obj/item/teleportation_scroll(wizard_mob), slot_r_store) + var/obj/item/spellbook/spellbook = new /obj/item/spellbook(wizard_mob) + spellbook.owner = wizard_mob + wizard_mob.equip_to_slot_or_del(spellbook, slot_r_hand) + + wizard_mob.faction = list("wizard") + + wizard_mob.dna.species.after_equip_job(null, wizard_mob) + + to_chat(wizard_mob, "You will find a list of available spells in your spell book. Choose your magic arsenal carefully.") + to_chat(wizard_mob, "The spellbook is bound to you, and others cannot use it.") + to_chat(wizard_mob, "In your pockets you will find a teleport scroll. Use it as needed.") + wizard_mob.mind.store_memory("Remember: do not forget to prepare your spells.") + wizard_mob.update_icons() + wizard_mob.gene_stability += DEFAULT_GENE_STABILITY //magic + return 1 + +// Checks if the game should end due to all wizards and apprentices being dead, or MMI'd/Borged +/datum/game_mode/wizard/check_finished() + var/wizards_alive = 0 + var/apprentices_alive = 0 + + // Wizards + for(var/datum/mind/wizard in wizards) + if(!istype(wizard.current,/mob/living/carbon)) + continue + if(wizard.current.stat==DEAD) + continue + if(istype(wizard.current, /obj/item/mmi)) // wizard is in an MMI, don't count them as alive + continue + wizards_alive++ + + // Apprentices + if(!wizards_alive) + for(var/datum/mind/apprentice in apprentices) + if(!istype(apprentice.current,/mob/living/carbon)) + continue + if(apprentice.current.stat==DEAD) + continue + if(istype(apprentice.current, /obj/item/mmi)) // apprentice is in an MMI, don't count them as alive + continue + apprentices_alive++ + + if(wizards_alive || apprentices_alive || but_wait_theres_more) + return ..() + else + finished = 1 + return 1 + +/datum/game_mode/wizard/declare_completion(var/ragin = 0) + if(finished && !ragin) + feedback_set_details("round_end_result","wizard loss - wizard killed") + to_chat(world, " The wizard[(wizards.len>1)?"s":""] has been killed by the crew! The Space Wizards Federation has been taught a lesson they will not soon forget!") + ..() + return 1 + +/datum/game_mode/proc/auto_declare_completion_wizard() + if(wizards.len) + var/text = "
        the wizards/witches were:" + + for(var/datum/mind/wizard in wizards) + + text += "
        [wizard.key] was [wizard.name] (" + if(wizard.current) + if(wizard.current.stat == DEAD) + text += "died" + else + text += "survived" + if(wizard.current.real_name != wizard.name) + text += " as [wizard.current.real_name]" + else + text += "body destroyed" + text += ")" + + var/count = 1 + var/wizardwin = 1 + for(var/datum/objective/objective in wizard.objectives) + if(objective.check_completion()) + text += "
        Objective #[count]: [objective.explanation_text] Success!" + feedback_add_details("wizard_objective","[objective.type]|SUCCESS") + else + text += "
        Objective #[count]: [objective.explanation_text] Fail." + feedback_add_details("wizard_objective","[objective.type]|FAIL") + wizardwin = 0 + count++ + + if(wizard.current && wizard.current.stat!=DEAD && wizardwin) + text += "
        The wizard was successful!" + feedback_add_details("wizard_success","SUCCESS") + else + text += "
        The wizard has failed!" + feedback_add_details("wizard_success","FAIL") + if(wizard.spell_list) + text += "
        [wizard.name] used the following spells: " + var/i = 1 + for(var/obj/effect/proc_holder/spell/S in wizard.spell_list) + text += "[S.name]" + if(wizard.spell_list.len > i) + text += ", " + i++ + text += "
        " + + to_chat(world, text) + return 1 + +//OTHER PROCS + +//To batch-remove wizard spells. Linked to mind.dm +/mob/proc/spellremove(mob/M) + if(!mind) + return + for(var/obj/effect/proc_holder/spell/spell_to_remove in mind.spell_list) + qdel(spell_to_remove) + mind.spell_list -= spell_to_remove + +//To batch-remove mob spells. +/mob/proc/mobspellremove(mob/M) + for(var/obj/effect/proc_holder/spell/spell_to_remove in mob_spell_list) + qdel(spell_to_remove) + mob_spell_list -= spell_to_remove + +/*Checks if the wizard can cast spells. +Made a proc so this is not repeated 14 (or more) times.*/ +/mob/proc/casting() +//Removed the stat check because not all spells require clothing now. + if(!istype(usr:wear_suit, /obj/item/clothing/suit/wizrobe)) + to_chat(usr, "I don't feel strong enough without my robe.") + return 0 + if(!istype(usr:shoes, /obj/item/clothing/shoes/sandal)) + to_chat(usr, "I don't feel strong enough without my sandals.") + return 0 + if(!istype(usr:head, /obj/item/clothing/head/wizard)) + to_chat(usr, "I don't feel strong enough without my hat.") + return 0 + else + return 1 + +/proc/iswizard(mob/living/M as mob) + return istype(M) && M.mind && SSticker && SSticker.mode && ((M.mind in SSticker.mode.wizards) || (M.mind in SSticker.mode.apprentices)) diff --git a/code/game/jobs/access.dm b/code/game/jobs/access.dm index a31f694f7fc..7f93fd9fef6 100644 --- a/code/game/jobs/access.dm +++ b/code/game/jobs/access.dm @@ -1,629 +1,514 @@ -/var/const/access_security = 1 // Security equipment -/var/const/access_brig = 2 // Brig timers and permabrig -/var/const/access_armory = 3 -/var/const/access_forensics_lockers= 4 -/var/const/access_medical = 5 -/var/const/access_morgue = 6 -/var/const/access_tox = 7 -/var/const/access_tox_storage = 8 -/var/const/access_genetics = 9 -/var/const/access_engine = 10 -/var/const/access_engine_equip = 11 -/var/const/access_maint_tunnels = 12 -/var/const/access_external_airlocks = 13 -/var/const/access_emergency_storage = 14 -/var/const/access_change_ids = 15 -/var/const/access_ai_upload = 16 -/var/const/access_teleporter = 17 -/var/const/access_eva = 18 -/var/const/access_heads = 19 -/var/const/access_captain = 20 -/var/const/access_all_personal_lockers = 21 -/var/const/access_chapel_office = 22 -/var/const/access_tech_storage = 23 -/var/const/access_atmospherics = 24 -/var/const/access_bar = 25 -/var/const/access_janitor = 26 -/var/const/access_crematorium = 27 -/var/const/access_kitchen = 28 -/var/const/access_robotics = 29 -/var/const/access_rd = 30 -/var/const/access_cargo = 31 -/var/const/access_construction = 32 -/var/const/access_chemistry = 33 -/var/const/access_cargo_bot = 34 -/var/const/access_hydroponics = 35 -/var/const/access_manufacturing = 36 -/var/const/access_library = 37 -/var/const/access_lawyer = 38 -/var/const/access_virology = 39 -/var/const/access_cmo = 40 -/var/const/access_qm = 41 -/var/const/access_court = 42 -/var/const/access_clown = 43 -/var/const/access_mime = 44 -/var/const/access_surgery = 45 -/var/const/access_theatre = 46 -/var/const/access_research = 47 -/var/const/access_mining = 48 -/var/const/access_mining_office = 49 //not in use -/var/const/access_mailsorting = 50 -/var/const/access_mint = 51 -/var/const/access_mint_vault = 52 -/var/const/access_heads_vault = 53 -/var/const/access_mining_station = 54 -/var/const/access_xenobiology = 55 -/var/const/access_ce = 56 -/var/const/access_hop = 57 -/var/const/access_hos = 58 -/var/const/access_RC_announce = 59 //Request console announcements -/var/const/access_keycard_auth = 60 //Used for events which require at least two people to confirm them -/var/const/access_tcomsat = 61 // has access to the entire telecomms satellite / machinery -/var/const/access_gateway = 62 -/var/const/access_sec_doors = 63 // Security front doors -/var/const/access_psychiatrist = 64 // Psychiatrist's office -/var/const/access_xenoarch = 65 -/var/const/access_paramedic = 66 -/var/const/access_blueshield = 67 -/var/const/access_salvage_captain = 69 // Salvage ship captain's quarters -/var/const/access_mechanic = 70 -/var/const/access_pilot = 71 -/var/const/access_ntrep = 73 -/var/const/access_magistrate = 74 -/var/const/access_minisat = 75 -/var/const/access_mineral_storeroom = 76 -/var/const/access_network = 77 - -/var/const/access_weapons = 99 //Weapon authorization for secbots - - //BEGIN CENTCOM ACCESS -/var/const/access_cent_general = 101//General facilities. -/var/const/access_cent_living = 102//Living quarters. -/var/const/access_cent_medical = 103//Medical. -/var/const/access_cent_security = 104//Security. -/var/const/access_cent_storage = 105//Storage areas. -/var/const/access_cent_shuttles = 106//Shuttle docks. -/var/const/access_cent_telecomms = 107//Telecomms. -/var/const/access_cent_teleporter = 108//Teleporter -/var/const/access_cent_specops = 109//Special Ops. -/var/const/access_cent_specops_commander = 110//Special Ops Commander. -/var/const/access_cent_blackops = 111//Black Ops. -/var/const/access_cent_thunder = 112//Thunderdome. -/var/const/access_cent_bridge = 113//Bridge. -/var/const/access_cent_commander = 114//Commander's Office/ID computer. - -//The Syndicate -/var/const/access_syndicate = 150//General Syndicate Access -/var/const/access_syndicate_leader = 151//Nuke Op Leader Access -/var/const/access_vox = 152//Vox Access -/var/const/access_syndicate_command = 153//Admin syndi officer - -//Trade Stations -var/const/access_trade_sol = 160 - -//MONEY -/var/const/access_crate_cash = 200 - -//Awaymissions -/var/const/access_away01 = 271 - -//Ghost roles -var/const/access_free_golems = 300 - -/obj/var/list/req_access = null -/obj/var/req_access_txt = "0" -/obj/var/list/req_one_access = null -/obj/var/req_one_access_txt = "0" - -//returns 1 if this mob has sufficient access to use this object -/obj/proc/allowed(mob/M) - //check if we don't require any access at all - if(check_access()) - return 1 - - if(!M) - return 0 - - var/acc = M.get_access() //see mob.dm - - if(acc == IGNORE_ACCESS || M.can_admin_interact()) - return 1 //Mob ignores access - - else - return check_access_list(acc) - - return 0 - -/obj/item/proc/GetAccess() - return list() - -/obj/item/proc/GetID() - return null - -/obj/proc/generate_req_lists() - //These generations have been moved out of /obj/New() because they were slowing down the creation of objects that never even used the access system. - if(!req_access) - req_access = list() - if(req_access_txt) - var/list/req_access_str = splittext(req_access_txt, ";") - for(var/x in req_access_str) - var/n = text2num(x) - if(n) - req_access += n - - if(!req_one_access) - req_one_access = list() - if(req_one_access_txt) - var/list/req_one_access_str = splittext(req_one_access_txt,";") - for(var/x in req_one_access_str) - var/n = text2num(x) - if(n) - req_one_access += n - -/obj/proc/check_access(obj/item/I) - var/list/L - if(I) - L = I.GetAccess() - else - L = list() - return check_access_list(L) - -/obj/proc/check_access_list(var/list/L) - generate_req_lists() - - if(!L) - return 0 - if(!istype(L, /list)) - return 0 - return has_access(req_access, req_one_access, L) - -/proc/has_access(var/list/req_access, var/list/req_one_access, var/list/accesses) - for(var/req in req_access) - if(!(req in accesses)) //doesn't have this access - return 0 - if(req_one_access.len) - for(var/req in req_one_access) - if(req in accesses) //has an access from the single access list - return 1 - return 0 - return 1 - -/proc/get_centcom_access(job) - switch(job) - if("VIP Guest") - return list(access_cent_general, access_cent_living) - if("Custodian") - return list(access_cent_general, access_cent_living, access_cent_medical, access_cent_storage) - if("Thunderdome Overseer") - return list(access_cent_general, access_cent_thunder) - if("Emergency Response Team Member") - return list(access_cent_general, access_cent_living, access_cent_medical, access_cent_security, access_cent_storage, access_cent_specops) + get_all_accesses() - if("Emergency Response Team Leader") - return list(access_cent_general, access_cent_living, access_cent_medical, access_cent_security, access_cent_storage, access_cent_specops, access_cent_specops_commander) + get_all_accesses() - if("Medical Officer") - return list(access_cent_general, access_cent_living, access_cent_medical, access_cent_storage) + get_all_accesses() - if("Intel Officer") - return list(access_cent_general, access_cent_living, access_cent_security, access_cent_storage) + get_all_accesses() - if("Research Officer") - return list(access_cent_general, access_cent_specops, access_cent_medical, access_cent_storage, access_cent_telecomms, access_cent_teleporter) + get_all_accesses() - if("Death Commando") - return list(access_cent_general, access_cent_living, access_cent_medical, access_cent_security, access_cent_storage, access_cent_specops, access_cent_specops_commander, access_cent_blackops) + get_all_accesses() - if("Deathsquad Officer") - return get_all_centcom_access() + get_all_accesses() - if("NT Undercover Operative") - return get_all_centcom_access() + get_all_accesses() - if("Special Operations Officer") - return get_all_centcom_access() + get_all_accesses() - if("Nanotrasen Navy Representative") - return get_all_centcom_access() + get_all_accesses() - if("Nanotrasen Navy Officer") - return get_all_centcom_access() + get_all_accesses() - if("Nanotrasen Navy Captain") - return get_all_centcom_access() + get_all_accesses() - if("Supreme Commander") - return get_all_centcom_access() + get_all_accesses() - -/proc/get_syndicate_access(job) - switch(job) - if("Syndicate Operative") - return list(access_syndicate) - if("Syndicate Operative Leader") - return list(access_syndicate, access_syndicate_leader) - if("Syndicate Agent") - return list(access_syndicate, access_maint_tunnels) - if("Vox Raider") - return list(access_vox) - if("Vox Trader") - return list(access_vox) - if("Syndicate Commando") - return list(access_syndicate, access_syndicate_leader) - if("Syndicate Officer") - return list(access_syndicate, access_syndicate_leader, access_syndicate_command) - -/proc/get_all_accesses() - return list(access_security, access_sec_doors, access_brig, access_armory, access_forensics_lockers, access_court, - access_medical, access_genetics, access_morgue, access_rd, - access_tox, access_tox_storage, access_chemistry, access_engine, access_engine_equip, access_maint_tunnels, - access_external_airlocks, access_change_ids, access_ai_upload, - access_teleporter, access_eva, access_heads, access_captain, access_all_personal_lockers, - access_tech_storage, access_chapel_office, access_atmospherics, access_kitchen, - access_bar, access_janitor, access_crematorium, access_robotics, access_cargo, access_construction, - access_hydroponics, access_library, access_lawyer, access_virology, access_psychiatrist, access_cmo, access_qm, access_clown, access_mime, access_surgery, - access_theatre, access_research, access_mining, access_mailsorting, - access_heads_vault, access_mining_station, access_xenobiology, access_ce, access_hop, access_hos, access_RC_announce, - access_keycard_auth, access_tcomsat, access_gateway, access_xenoarch, access_paramedic, access_blueshield, access_mechanic,access_weapons, - access_pilot, access_ntrep, access_magistrate, access_mineral_storeroom, access_minisat, access_network) - -/proc/get_all_centcom_access() - return list(access_cent_general, access_cent_living, access_cent_medical, access_cent_security, access_cent_storage, access_cent_shuttles, access_cent_telecomms, access_cent_teleporter, access_cent_specops, access_cent_specops_commander, access_cent_blackops, access_cent_thunder, access_cent_bridge, access_cent_commander) - -/proc/get_all_syndicate_access() - return list(access_syndicate, access_syndicate_leader, access_vox, access_syndicate_command) - -/proc/get_all_misc_access() - return list(access_salvage_captain, access_trade_sol, access_crate_cash, access_away01) - -/proc/get_absolutely_all_accesses() - return (get_all_accesses() | get_all_centcom_access() | get_all_syndicate_access() | get_all_misc_access()) - -/proc/get_region_accesses(code) - switch(code) - if(REGION_ALL) - return get_all_accesses() - if(REGION_GENERAL) //station general - return list(access_kitchen, access_bar, access_hydroponics, access_janitor, access_chapel_office, access_crematorium, access_library, access_theatre, access_lawyer, access_magistrate, access_clown, access_mime) - if(REGION_SECURITY) //security - return list(access_sec_doors, access_weapons, access_security, access_brig, access_armory, access_forensics_lockers, access_court, access_pilot, access_hos) - if(REGION_MEDBAY) //medbay - return list(access_medical, access_genetics, access_morgue, access_chemistry, access_psychiatrist, access_virology, access_surgery, access_cmo, access_paramedic) - if(REGION_RESEARCH) //research - return list(access_research, access_tox, access_tox_storage, access_genetics, access_robotics, access_xenobiology, access_xenoarch, access_minisat, access_rd, access_network) - if(REGION_ENGINEERING) //engineering and maintenance - return list(access_construction, access_maint_tunnels, access_engine, access_engine_equip, access_external_airlocks, access_tech_storage, access_atmospherics, access_minisat, access_ce, access_mechanic) - if(REGION_SUPPLY) //supply - return list(access_mailsorting, access_mining, access_mining_station, access_mineral_storeroom, access_cargo, access_qm) - if(REGION_COMMAND) //command - return list(access_heads, access_RC_announce, access_keycard_auth, access_change_ids, access_ai_upload, access_teleporter, access_eva, access_tcomsat, access_gateway, access_all_personal_lockers, access_heads_vault, access_blueshield, access_ntrep, access_hop, access_captain) - if(REGION_CENTCOMM) //because why the heck not - return get_all_centcom_access() + get_all_accesses() - -/proc/get_region_accesses_name(code) - switch(code) - if(REGION_ALL) - return "All" - if(REGION_GENERAL) //station general - return "General" - if(REGION_SECURITY) //security - return "Security" - if(REGION_MEDBAY) //medbay - return "Medbay" - if(REGION_RESEARCH) //research - return "Research" - if(REGION_ENGINEERING) //engineering and maintenance - return "Engineering" - if(REGION_SUPPLY) //supply - return "Supply" - if(REGION_COMMAND) //command - return "Command" - if(REGION_CENTCOMM) //CC - return "CentComm" - - -/proc/get_access_desc(A) - switch(A) - if(access_cargo) - return "Cargo Bay" - if(access_cargo_bot) - return "Cargo Bot Delivery" - if(access_security) - return "Security" - if(access_brig) - return "Holding Cells" - if(access_court) - return "Courtroom" - if(access_forensics_lockers) - return "Forensics" - if(access_medical) - return "Medical" - if(access_genetics) - return "Genetics Lab" - if(access_morgue) - return "Morgue" - if(access_tox) - return "R&D Lab" - if(access_tox_storage) - return "Toxins Lab" - if(access_chemistry) - return "Chemistry Lab" - if(access_rd) - return "Research Director" - if(access_bar) - return "Bar" - if(access_janitor) - return "Custodial Closet" - if(access_engine) - return "Engineering" - if(access_engine_equip) - return "Power Equipment" - if(access_maint_tunnels) - return "Maintenance" - if(access_external_airlocks) - return "External Airlocks" - if(access_emergency_storage) - return "Emergency Storage" - if(access_change_ids) - return "ID Computer" - if(access_ai_upload) - return "AI Upload" - if(access_teleporter) - return "Teleporter" - if(access_eva) - return "EVA" - if(access_heads) - return "Bridge" - if(access_captain) - return "Captain" - if(access_all_personal_lockers) - return "Personal Lockers" - if(access_chapel_office) - return "Chapel Office" - if(access_tech_storage) - return "Technical Storage" - if(access_atmospherics) - return "Atmospherics" - if(access_crematorium) - return "Crematorium" - if(access_armory) - return "Armory" - if(access_construction) - return "Construction Areas" - if(access_kitchen) - return "Kitchen" - if(access_hydroponics) - return "Hydroponics" - if(access_library) - return "Library" - if(access_lawyer) - return "Law Office" - if(access_robotics) - return "Robotics" - if(access_virology) - return "Virology" - if(access_psychiatrist) - return "Psychiatrist's Office" - if(access_cmo) - return "Chief Medical Officer" - if(access_qm) - return "Quartermaster" - if(access_clown) - return "Clown's Office" - if(access_mime) - return "Mime's Office" - if(access_surgery) - return "Surgery" - if(access_theatre) - return "Theatre" - if(access_manufacturing) - return "Manufacturing" - if(access_research) - return "Science" - if(access_mining) - return "Mining" - if(access_mining_office) - return "Mining Office" - if(access_mailsorting) - return "Cargo Office" - if(access_mint) - return "Mint" - if(access_mint_vault) - return "Mint Vault" - if(access_heads_vault) - return "Main Vault" - if(access_mining_station) - return "Mining EVA" - if(access_xenobiology) - return "Xenobiology Lab" - if(access_xenoarch) - return "Xenoarchaeology" - if(access_hop) - return "Head of Personnel" - if(access_hos) - return "Head of Security" - if(access_ce) - return "Chief Engineer" - if(access_RC_announce) - return "RC Announcements" - if(access_keycard_auth) - return "Keycode Auth. Device" - if(access_tcomsat) - return "Telecommunications" - if(access_network) - return "Network Access" - if(access_gateway) - return "Gateway" - if(access_sec_doors) - return "Brig" - if(access_blueshield) - return "Blueshield" - if(access_ntrep) - return "Nanotrasen Rep." - if(access_paramedic) - return "Paramedic" - if(access_mechanic) - return "Mechanic Workshop" - if(access_pilot) - return "Security Pod Pilot" - if(access_magistrate) - return "Magistrate" - if(access_mineral_storeroom) - return "Mineral Storage" - if(access_minisat) - return "AI Satellite" - if(access_weapons) - return "Weapon Permit" - -/proc/get_centcom_access_desc(A) - switch(A) - if(access_cent_general) - return "General Access" - if(access_cent_living) - return "Living Quarters" - if(access_cent_medical) - return "Medical" - if(access_cent_security) - return "Security" - if(access_cent_storage) - return "Storage" - if(access_cent_shuttles) - return "Shuttles" - if(access_cent_telecomms) - return "Telecommunications" - if(access_cent_teleporter) - return "Teleporter" - if(access_cent_specops) - return "Special Ops" - if(access_cent_specops_commander) - return "Special Ops Commander" - if(access_cent_blackops) - return "Black Ops" - if(access_cent_thunder) - return "Thunderdome" - if(access_cent_bridge) - return "Bridge" - if(access_cent_commander) - return "Commander" - -/proc/get_syndicate_access_desc(A) - switch(A) - if(access_syndicate) - return "Syndicate Operative" - if(access_syndicate_leader) - return "Syndicate Operative Leader" - if(access_vox) - return "Vox" - if(access_syndicate_command) - return "Syndicate Command" - -/proc/get_all_jobs() - var/list/all_jobs = list() - var/list/all_datums = subtypesof(/datum/job) - all_datums.Remove(list(/datum/job/ai,/datum/job/cyborg)) - var/datum/job/jobdatum - for(var/jobtype in all_datums) - jobdatum = new jobtype - all_jobs.Add(jobdatum.title) - return all_jobs - -/proc/get_all_centcom_jobs() - return list("VIP Guest","Custodian","Thunderdome Overseer","Emergency Response Team Member","Emergency Response Team Leader","Intel Officer","Medical Officer","Death Commando","Research Officer","Deathsquad Officer","Special Operations Officer","Nanotrasen Navy Representative","Nanotrasen Navy Officer","Nanotrasen Navy Captain","Supreme Commander") - -//gets the actual job rank (ignoring alt titles) -//this is used solely for sechuds -/obj/proc/GetJobRealName() - if(!istype(src, /obj/item/pda) && !istype(src,/obj/item/card/id)) - return - - var/rank - var/assignment - if(istype(src, /obj/item/pda)) - if(src:id) - rank = src:id:rank - assignment = src:id:assignment - else if(istype(src, /obj/item/card/id)) - rank = src:rank - assignment = src:assignment - - if( rank in GLOB.joblist ) - return rank - - if( assignment in GLOB.joblist ) - return assignment - - return "Unknown" - -//gets the alt title, failing that the actual job rank -//this is unused -/obj/proc/sdsdsd() //GetJobDisplayName - if(!istype(src, /obj/item/pda) && !istype(src,/obj/item/card/id)) - return - - var/assignment - if(istype(src, /obj/item/pda)) - if(src:id) - assignment = src:id:assignment - else if(istype(src, /obj/item/card/id)) - assignment = src:assignment - - if(assignment) - return assignment - - return "Unknown" - -proc/GetIdCard(var/mob/living/carbon/human/H) - if(H.wear_id) - var/id = H.wear_id.GetID() - if(id) - return id - if(H.get_active_hand()) - var/obj/item/I = H.get_active_hand() - return I.GetID() - -proc/FindNameFromID(var/mob/living/carbon/human/H) - ASSERT(istype(H)) - var/obj/item/card/id/C = H.get_active_hand() - if( istype(C) || istype(C, /obj/item/pda) ) - var/obj/item/card/id/ID = C - - if( istype(C, /obj/item/pda) ) - var/obj/item/pda/pda = C - ID = pda.id - if(!istype(ID)) - ID = null - - if(ID) - return ID.registered_name - - C = H.wear_id - - if( istype(C) || istype(C, /obj/item/pda) ) - var/obj/item/card/id/ID = C - - if( istype(C, /obj/item/pda) ) - var/obj/item/pda/pda = C - ID = pda.id - if(!istype(ID)) - ID = null - - if(ID) - return ID.registered_name - -proc/get_all_job_icons() //For all existing HUD icons - return GLOB.joblist + list("Prisoner") - -/obj/proc/GetJobName() //Used in secHUD icon generation - var/assignmentName = "Unknown" - var/rankName = "Unknown" - if(istype(src, /obj/item/pda)) - var/obj/item/pda/P = src - assignmentName = P.ownjob - rankName = P.ownrank - else if(istype(src, /obj/item/card/id)) - var/obj/item/card/id/I = src - assignmentName = I.assignment - rankName = I.rank - - - var/job_icons = get_all_job_icons() - var/centcom = get_all_centcom_jobs() - - if(assignmentName in centcom) //Return with the NT logo if it is a Centcom job - return "Centcom" - if(rankName in centcom) - return "Centcom" - - if(assignmentName in job_icons) //Check if the job has a hud icon - return assignmentName - if(rankName in job_icons) - return rankName - - return "Unknown" //Return unknown if none of the above apply +/obj/var/list/req_access = null +/obj/var/req_access_txt = "0" +/obj/var/list/req_one_access = null +/obj/var/req_one_access_txt = "0" + +//returns 1 if this mob has sufficient access to use this object +/obj/proc/allowed(mob/M) + //check if we don't require any access at all + if(check_access()) + return 1 + + if(!M) + return 0 + + var/acc = M.get_access() //see mob.dm + + if(acc == IGNORE_ACCESS || M.can_admin_interact()) + return 1 //Mob ignores access + + else + return check_access_list(acc) + + return 0 + +/obj/item/proc/GetAccess() + return list() + +/obj/item/proc/GetID() + return null + +/obj/proc/generate_req_lists() + //These generations have been moved out of /obj/New() because they were slowing down the creation of objects that never even used the access system. + if(!req_access) + req_access = list() + if(req_access_txt) + var/list/req_access_str = splittext(req_access_txt, ";") + for(var/x in req_access_str) + var/n = text2num(x) + if(n) + req_access += n + + if(!req_one_access) + req_one_access = list() + if(req_one_access_txt) + var/list/req_one_access_str = splittext(req_one_access_txt,";") + for(var/x in req_one_access_str) + var/n = text2num(x) + if(n) + req_one_access += n + +/obj/proc/check_access(obj/item/I) + var/list/L + if(I) + L = I.GetAccess() + else + L = list() + return check_access_list(L) + +/obj/proc/check_access_list(var/list/L) + generate_req_lists() + + if(!L) + return 0 + if(!istype(L, /list)) + return 0 + return has_access(req_access, req_one_access, L) + +/proc/has_access(var/list/req_access, var/list/req_one_access, var/list/accesses) + for(var/req in req_access) + if(!(req in accesses)) //doesn't have this access + return 0 + if(req_one_access.len) + for(var/req in req_one_access) + if(req in accesses) //has an access from the single access list + return 1 + return 0 + return 1 + +/proc/get_centcom_access(job) + switch(job) + if("VIP Guest") + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING) + if("Custodian") + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_MEDICAL, ACCESS_CENT_STORAGE) + if("Thunderdome Overseer") + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_THUNDER) + if("Emergency Response Team Member") + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_MEDICAL, ACCESS_CENT_SECURITY, ACCESS_CENT_STORAGE, ACCESS_CENT_SPECOPS) + get_all_accesses() + if("Emergency Response Team Leader") + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_MEDICAL, ACCESS_CENT_SECURITY, ACCESS_CENT_STORAGE, ACCESS_CENT_SPECOPS, ACCESS_CENT_SPECOPS_COMMANDER) + get_all_accesses() + if("Medical Officer") + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_MEDICAL, ACCESS_CENT_STORAGE) + get_all_accesses() + if("Intel Officer") + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_SECURITY, ACCESS_CENT_STORAGE) + get_all_accesses() + if("Research Officer") + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_SPECOPS, ACCESS_CENT_MEDICAL, ACCESS_CENT_STORAGE, ACCESS_CENT_TELECOMMS, ACCESS_CENT_TELEPORTER) + get_all_accesses() + if("Death Commando") + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_MEDICAL, ACCESS_CENT_SECURITY, ACCESS_CENT_STORAGE, ACCESS_CENT_SPECOPS, ACCESS_CENT_SPECOPS_COMMANDER, ACCESS_CENT_BLACKOPS) + get_all_accesses() + if("Deathsquad Officer") + return get_all_centcom_access() + get_all_accesses() + if("NT Undercover Operative") + return get_all_centcom_access() + get_all_accesses() + if("Special Operations Officer") + return get_all_centcom_access() + get_all_accesses() + if("Nanotrasen Navy Representative") + return get_all_centcom_access() + get_all_accesses() + if("Nanotrasen Navy Officer") + return get_all_centcom_access() + get_all_accesses() + if("Nanotrasen Navy Captain") + return get_all_centcom_access() + get_all_accesses() + if("Supreme Commander") + return get_all_centcom_access() + get_all_accesses() + +/proc/get_syndicate_access(job) + switch(job) + if("Syndicate Operative") + return list(ACCESS_SYNDICATE) + if("Syndicate Operative Leader") + return list(ACCESS_SYNDICATE, ACCESS_SYNDICATE_LEADER) + if("Syndicate Agent") + return list(ACCESS_SYNDICATE, ACCESS_MAINT_TUNNELS) + if("Vox Raider") + return list(ACCESS_VOX) + if("Vox Trader") + return list(ACCESS_VOX) + if("Syndicate Commando") + return list(ACCESS_SYNDICATE, ACCESS_SYNDICATE_LEADER) + if("Syndicate Officer") + return list(ACCESS_SYNDICATE, ACCESS_SYNDICATE_LEADER, ACCESS_SYNDICATE_COMMAND) + +/proc/get_all_accesses() + return list(ACCESS_MINISAT, ACCESS_AI_UPLOAD, ACCESS_ARMORY, ACCESS_ATMOSPHERICS, ACCESS_BAR, ACCESS_SEC_DOORS, ACCESS_BLUESHIELD, + ACCESS_HEADS, ACCESS_CAPTAIN, ACCESS_CARGO, ACCESS_MAILSORTING, ACCESS_CHAPEL_OFFICE, ACCESS_CE, ACCESS_CHEMISTRY, ACCESS_CLOWN, ACCESS_CMO, + ACCESS_COURT, ACCESS_CONSTRUCTION, ACCESS_CREMATORIUM, ACCESS_JANITOR, ACCESS_ENGINE, ACCESS_EVA, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_FORENSICS_LOCKERS, + ACCESS_GENETICS, ACCESS_GATEWAY, ACCESS_BRIG, ACCESS_HOP, ACCESS_HOS, ACCESS_HYDROPONICS, ACCESS_CHANGE_IDS, ACCESS_KEYCARD_AUTH, ACCESS_KITCHEN, + ACCESS_LAWYER, ACCESS_LIBRARY, ACCESS_MAGISTRATE, ACCESS_MAINT_TUNNELS, ACCESS_HEADS_VAULT, ACCESS_MEDICAL, ACCESS_MECHANIC, ACCESS_MIME, + ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM, ACCESS_MORGUE, ACCESS_NETWORK, ACCESS_NTREP, ACCESS_PARAMEDIC, ACCESS_ALL_PERSONAL_LOCKERS, + ACCESS_ENGINE_EQUIP, ACCESS_PSYCHIATRIST, ACCESS_QM, ACCESS_RD, ACCESS_RC_ANNOUNCE, ACCESS_ROBOTICS, ACCESS_TOX, ACCESS_RESEARCH, ACCESS_SECURITY, ACCESS_PILOT, + ACCESS_SURGERY, ACCESS_TECH_STORAGE, ACCESS_TELEPORTER, ACCESS_THEATRE, ACCESS_TCOMSAT, ACCESS_TOX_STORAGE, ACCESS_VIROLOGY, ACCESS_WEAPONS, ACCESS_XENOBIOLOGY, + ACCESS_XENOARCH) + +/proc/get_all_centcom_access() + return list(ACCESS_CENT_GENERAL, ACCESS_CENT_LIVING, ACCESS_CENT_MEDICAL, ACCESS_CENT_SECURITY, ACCESS_CENT_STORAGE, ACCESS_CENT_SHUTTLES, ACCESS_CENT_TELECOMMS, ACCESS_CENT_TELEPORTER, ACCESS_CENT_SPECOPS, ACCESS_CENT_SPECOPS_COMMANDER, ACCESS_CENT_BLACKOPS, ACCESS_CENT_THUNDER, ACCESS_CENT_BRIDGE, ACCESS_CENT_COMMANDER) + +/proc/get_all_syndicate_access() + return list(ACCESS_SYNDICATE, ACCESS_SYNDICATE_LEADER, ACCESS_VOX, ACCESS_SYNDICATE_COMMAND) + +/proc/get_all_misc_access() + return list(ACCESS_SALVAGE_CAPTAIN, ACCESS_TRADE_SOL, ACCESS_CRATE_CASH, ACCESS_AWAY01) + +/proc/get_absolutely_all_accesses() + return (get_all_accesses() | get_all_centcom_access() | get_all_syndicate_access() | get_all_misc_access()) + +/proc/get_region_accesses(code) + switch(code) + if(REGION_ALL) + return get_all_accesses() + if(REGION_GENERAL) //station general + return list(ACCESS_KITCHEN, ACCESS_BAR, ACCESS_HYDROPONICS, ACCESS_JANITOR, ACCESS_CHAPEL_OFFICE, ACCESS_CREMATORIUM, ACCESS_LIBRARY, ACCESS_THEATRE, ACCESS_LAWYER, ACCESS_MAGISTRATE, ACCESS_CLOWN, ACCESS_MIME) + if(REGION_SECURITY) //security + return list(ACCESS_SEC_DOORS, ACCESS_WEAPONS, ACCESS_SECURITY, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_FORENSICS_LOCKERS, ACCESS_COURT, ACCESS_PILOT, ACCESS_HOS) + if(REGION_MEDBAY) //medbay + return list(ACCESS_MEDICAL, ACCESS_GENETICS, ACCESS_MORGUE, ACCESS_CHEMISTRY, ACCESS_PSYCHIATRIST, ACCESS_VIROLOGY, ACCESS_SURGERY, ACCESS_CMO, ACCESS_PARAMEDIC) + if(REGION_RESEARCH) //research + return list(ACCESS_RESEARCH, ACCESS_TOX, ACCESS_TOX_STORAGE, ACCESS_GENETICS, ACCESS_ROBOTICS, ACCESS_XENOBIOLOGY, ACCESS_XENOARCH, ACCESS_MINISAT, ACCESS_RD, ACCESS_NETWORK) + if(REGION_ENGINEERING) //engineering and maintenance + return list(ACCESS_CONSTRUCTION, ACCESS_MAINT_TUNNELS, ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_TECH_STORAGE, ACCESS_ATMOSPHERICS, ACCESS_MINISAT, ACCESS_CE, ACCESS_MECHANIC) + if(REGION_SUPPLY) //supply + return list(ACCESS_MAILSORTING, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM, ACCESS_CARGO, ACCESS_QM) + if(REGION_COMMAND) //command + return list(ACCESS_HEADS, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_CHANGE_IDS, ACCESS_AI_UPLOAD, ACCESS_TELEPORTER, ACCESS_EVA, ACCESS_TCOMSAT, ACCESS_GATEWAY, ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_HEADS_VAULT, ACCESS_BLUESHIELD, ACCESS_NTREP, ACCESS_HOP, ACCESS_CAPTAIN) + if(REGION_CENTCOMM) //because why the heck not + return get_all_centcom_access() + get_all_accesses() + +/proc/get_region_accesses_name(code) + switch(code) + if(REGION_ALL) + return "All" + if(REGION_GENERAL) //station general + return "General" + if(REGION_SECURITY) //security + return "Security" + if(REGION_MEDBAY) //medbay + return "Medbay" + if(REGION_RESEARCH) //research + return "Research" + if(REGION_ENGINEERING) //engineering and maintenance + return "Engineering" + if(REGION_SUPPLY) //supply + return "Supply" + if(REGION_COMMAND) //command + return "Command" + if(REGION_CENTCOMM) //CC + return "CentComm" + + +/proc/get_access_desc(A) + switch(A) + if(ACCESS_CARGO) + return "Cargo Bay" + if(ACCESS_CARGO_BOT) + return "Cargo Bot Delivery" + if(ACCESS_SECURITY) + return "Security" + if(ACCESS_BRIG) + return "Holding Cells" + if(ACCESS_COURT) + return "Courtroom" + if(ACCESS_FORENSICS_LOCKERS) + return "Forensics" + if(ACCESS_MEDICAL) + return "Medical" + if(ACCESS_GENETICS) + return "Genetics Lab" + if(ACCESS_MORGUE) + return "Morgue" + if(ACCESS_TOX) + return "R&D Lab" + if(ACCESS_TOX_STORAGE) + return "Toxins Lab" + if(ACCESS_CHEMISTRY) + return "Chemistry Lab" + if(ACCESS_RD) + return "Research Director" + if(ACCESS_BAR) + return "Bar" + if(ACCESS_JANITOR) + return "Custodial Closet" + if(ACCESS_ENGINE) + return "Engineering" + if(ACCESS_ENGINE_EQUIP) + return "Power Equipment" + if(ACCESS_MAINT_TUNNELS) + return "Maintenance" + if(ACCESS_EXTERNAL_AIRLOCKS) + return "External Airlocks" + if(ACCESS_EMERGENCY_STORAGE) + return "Emergency Storage" + if(ACCESS_CHANGE_IDS) + return "ID Computer" + if(ACCESS_AI_UPLOAD) + return "AI Upload" + if(ACCESS_TELEPORTER) + return "Teleporter" + if(ACCESS_EVA) + return "EVA" + if(ACCESS_HEADS) + return "Bridge" + if(ACCESS_CAPTAIN) + return "Captain" + if(ACCESS_ALL_PERSONAL_LOCKERS) + return "Personal Lockers" + if(ACCESS_CHAPEL_OFFICE) + return "Chapel Office" + if(ACCESS_TECH_STORAGE) + return "Technical Storage" + if(ACCESS_ATMOSPHERICS) + return "Atmospherics" + if(ACCESS_CREMATORIUM) + return "Crematorium" + if(ACCESS_ARMORY) + return "Armory" + if(ACCESS_CONSTRUCTION) + return "Construction Areas" + if(ACCESS_KITCHEN) + return "Kitchen" + if(ACCESS_HYDROPONICS) + return "Hydroponics" + if(ACCESS_LIBRARY) + return "Library" + if(ACCESS_LAWYER) + return "Law Office" + if(ACCESS_ROBOTICS) + return "Robotics" + if(ACCESS_VIROLOGY) + return "Virology" + if(ACCESS_PSYCHIATRIST) + return "Psychiatrist's Office" + if(ACCESS_CMO) + return "Chief Medical Officer" + if(ACCESS_QM) + return "Quartermaster" + if(ACCESS_CLOWN) + return "Clown's Office" + if(ACCESS_MIME) + return "Mime's Office" + if(ACCESS_SURGERY) + return "Surgery" + if(ACCESS_THEATRE) + return "Theatre" + if(ACCESS_MANUFACTURING) + return "Manufacturing" + if(ACCESS_RESEARCH) + return "Science" + if(ACCESS_MINING) + return "Mining" + if(ACCESS_MINING_OFFICE) + return "Mining Office" + if(ACCESS_MAILSORTING) + return "Cargo Office" + if(ACCESS_MINT) + return "Mint" + if(ACCESS_MINT_VAULT) + return "Mint Vault" + if(ACCESS_HEADS_VAULT) + return "Main Vault" + if(ACCESS_MINING_STATION) + return "Mining EVA" + if(ACCESS_XENOBIOLOGY) + return "Xenobiology Lab" + if(ACCESS_XENOARCH) + return "Xenoarchaeology" + if(ACCESS_HOP) + return "Head of Personnel" + if(ACCESS_HOS) + return "Head of Security" + if(ACCESS_CE) + return "Chief Engineer" + if(ACCESS_RC_ANNOUNCE) + return "RC Announcements" + if(ACCESS_KEYCARD_AUTH) + return "Keycode Auth. Device" + if(ACCESS_TCOMSAT) + return "Telecommunications" + if(ACCESS_NETWORK) + return "Network Access" + if(ACCESS_GATEWAY) + return "Gateway" + if(ACCESS_SEC_DOORS) + return "Brig" + if(ACCESS_BLUESHIELD) + return "Blueshield" + if(ACCESS_NTREP) + return "Nanotrasen Rep." + if(ACCESS_PARAMEDIC) + return "Paramedic" + if(ACCESS_MECHANIC) + return "Mechanic Workshop" + if(ACCESS_PILOT) + return "Security Pod Pilot" + if(ACCESS_MAGISTRATE) + return "Magistrate" + if(ACCESS_MINERAL_STOREROOM) + return "Mineral Storage" + if(ACCESS_MINISAT) + return "AI Satellite" + if(ACCESS_WEAPONS) + return "Weapon Permit" + +/proc/get_centcom_access_desc(A) + switch(A) + if(ACCESS_CENT_GENERAL) + return "General Access" + if(ACCESS_CENT_LIVING) + return "Living Quarters" + if(ACCESS_CENT_MEDICAL) + return "Medical" + if(ACCESS_CENT_SECURITY) + return "Security" + if(ACCESS_CENT_STORAGE) + return "Storage" + if(ACCESS_CENT_SHUTTLES) + return "Shuttles" + if(ACCESS_CENT_TELECOMMS) + return "Telecommunications" + if(ACCESS_CENT_TELEPORTER) + return "Teleporter" + if(ACCESS_CENT_SPECOPS) + return "Special Ops" + if(ACCESS_CENT_SPECOPS_COMMANDER) + return "Special Ops Commander" + if(ACCESS_CENT_BLACKOPS) + return "Black Ops" + if(ACCESS_CENT_THUNDER) + return "Thunderdome" + if(ACCESS_CENT_BRIDGE) + return "Bridge" + if(ACCESS_CENT_COMMANDER) + return "Commander" + +/proc/get_syndicate_access_desc(A) + switch(A) + if(ACCESS_SYNDICATE) + return "Syndicate Operative" + if(ACCESS_SYNDICATE_LEADER) + return "Syndicate Operative Leader" + if(ACCESS_VOX) + return "Vox" + if(ACCESS_SYNDICATE_COMMAND) + return "Syndicate Command" + +/proc/get_all_jobs() + var/list/all_jobs = list() + var/list/all_datums = subtypesof(/datum/job) + all_datums.Remove(list(/datum/job/ai,/datum/job/cyborg)) + var/datum/job/jobdatum + for(var/jobtype in all_datums) + jobdatum = new jobtype + all_jobs.Add(jobdatum.title) + return all_jobs + +/proc/get_all_centcom_jobs() + return list("VIP Guest","Custodian","Thunderdome Overseer","Emergency Response Team Member","Emergency Response Team Leader","Intel Officer","Medical Officer","Death Commando","Research Officer","Deathsquad Officer","Special Operations Officer","Nanotrasen Navy Representative","Nanotrasen Navy Officer","Nanotrasen Navy Captain","Supreme Commander") + +//gets the actual job rank (ignoring alt titles) +//this is used solely for sechuds +/obj/proc/GetJobRealName() + if(!istype(src, /obj/item/pda) && !istype(src,/obj/item/card/id)) + return + + var/rank + var/assignment + if(istype(src, /obj/item/pda)) + if(src:id) + rank = src:id:rank + assignment = src:id:assignment + else if(istype(src, /obj/item/card/id)) + rank = src:rank + assignment = src:assignment + + if( rank in GLOB.joblist ) + return rank + + if( assignment in GLOB.joblist ) + return assignment + + return "Unknown" + +//gets the alt title, failing that the actual job rank +//this is unused +/obj/proc/sdsdsd() //GetJobDisplayName + if(!istype(src, /obj/item/pda) && !istype(src,/obj/item/card/id)) + return + + var/assignment + if(istype(src, /obj/item/pda)) + if(src:id) + assignment = src:id:assignment + else if(istype(src, /obj/item/card/id)) + assignment = src:assignment + + if(assignment) + return assignment + + return "Unknown" + +proc/GetIdCard(var/mob/living/carbon/human/H) + if(H.wear_id) + var/id = H.wear_id.GetID() + if(id) + return id + if(H.get_active_hand()) + var/obj/item/I = H.get_active_hand() + return I.GetID() + +proc/FindNameFromID(var/mob/living/carbon/human/H) + ASSERT(istype(H)) + var/obj/item/card/id/C = H.get_active_hand() + if( istype(C) || istype(C, /obj/item/pda) ) + var/obj/item/card/id/ID = C + + if( istype(C, /obj/item/pda) ) + var/obj/item/pda/pda = C + ID = pda.id + if(!istype(ID)) + ID = null + + if(ID) + return ID.registered_name + + C = H.wear_id + + if( istype(C) || istype(C, /obj/item/pda) ) + var/obj/item/card/id/ID = C + + if( istype(C, /obj/item/pda) ) + var/obj/item/pda/pda = C + ID = pda.id + if(!istype(ID)) + ID = null + + if(ID) + return ID.registered_name + +proc/get_all_job_icons() //For all existing HUD icons + return GLOB.joblist + list("Prisoner") + +/obj/proc/GetJobName() //Used in secHUD icon generation + var/assignmentName = "Unknown" + var/rankName = "Unknown" + if(istype(src, /obj/item/pda)) + var/obj/item/pda/P = src + assignmentName = P.ownjob + rankName = P.ownrank + else if(istype(src, /obj/item/card/id)) + var/obj/item/card/id/I = src + assignmentName = I.assignment + rankName = I.rank + + + var/job_icons = get_all_job_icons() + var/centcom = get_all_centcom_jobs() + + if(assignmentName in centcom) //Return with the NT logo if it is a Centcom job + return "Centcom" + if(rankName in centcom) + return "Centcom" + + if(assignmentName in job_icons) //Check if the job has a hud icon + return assignmentName + if(rankName in job_icons) + return rankName + + return "Unknown" //Return unknown if none of the above apply diff --git a/code/game/jobs/job/central.dm b/code/game/jobs/job/central.dm index 1d6ae0162a2..796db23b36e 100644 --- a/code/game/jobs/job/central.dm +++ b/code/game/jobs/job/central.dm @@ -2,8 +2,8 @@ // General-purpose CC official. Can hear out grievances, investigate cases, issue demotions, etc. /datum/job/ntnavyofficer title = "Nanotrasen Navy Officer" - flag = CENTCOM - department_flag = CENTCOM + flag = JOB_CENTCOM + department_flag = JOB_CENTCOM // This gets its job as its own flag because admin jobs dont have flags total_positions = 5 spawn_positions = 5 supervisors = "the admins" @@ -48,8 +48,8 @@ // CC Officials who lead ERTs, Death Squads, etc. /datum/job/ntspecops title = "Special Operations Officer" - flag = CENTCOM - department_flag = CENTCOM + flag = JOB_CENTCOM + department_flag = JOB_CENTCOM // This gets its job as its own flag because admin jobs dont have flags total_positions = 5 spawn_positions = 5 supervisors = "the admins" @@ -66,9 +66,9 @@ /datum/outfit/job/ntspecops name = "Special Operations Officer" jobtype = /datum/job/ntspecops - uniform = /obj/item/clothing/under/rank/centcom_commander + uniform = /obj/item/clothing/under/rank/centcom/captain suit = /obj/item/clothing/suit/space/deathsquad/officer - back = /obj/item/storage/backpack/security + back = /obj/item/storage/backpack/ert/security belt = /obj/item/storage/belt/military/assault gloves = /obj/item/clothing/gloves/combat shoes = /obj/item/clothing/shoes/combat @@ -100,4 +100,4 @@ . = ..() if(visualsOnly) return - H.mind.offstation_role = TRUE \ No newline at end of file + H.mind.offstation_role = TRUE diff --git a/code/game/jobs/job/civilian.dm b/code/game/jobs/job/civilian.dm index 4535e1c6c41..6adc55a89f1 100644 --- a/code/game/jobs/job/civilian.dm +++ b/code/game/jobs/job/civilian.dm @@ -1,28 +1,28 @@ -/datum/job/civilian - title = "Civilian" - flag = CIVILIAN - department_flag = SUPPORT - total_positions = -1 - spawn_positions = -1 - supervisors = "the head of personnel" - department_head = list("Head of Personnel") - selection_color = "#dddddd" - access = list() //See /datum/job/assistant/get_access() - minimal_access = list() //See /datum/job/assistant/get_access() - alt_titles = list("Tourist","Businessman","Trader","Assistant") - outfit = /datum/outfit/job/assistant - -/datum/job/civilian/get_access() - if(config.assistant_maint) - return list(access_maint_tunnels) - else - return list() - -/datum/outfit/job/assistant - name = "Civilian" - jobtype = /datum/job/civilian - - uniform = /obj/item/clothing/under/color/random - shoes = /obj/item/clothing/shoes/black - - +/datum/job/civilian + title = "Civilian" + flag = JOB_CIVILIAN + department_flag = JOBCAT_SUPPORT + total_positions = -1 + spawn_positions = -1 + supervisors = "the head of personnel" + department_head = list("Head of Personnel") + selection_color = "#dddddd" + access = list() //See /datum/job/assistant/get_access() + minimal_access = list() //See /datum/job/assistant/get_access() + alt_titles = list("Tourist","Businessman","Trader","Assistant") + outfit = /datum/outfit/job/assistant + +/datum/job/civilian/get_access() + if(config.assistant_maint) + return list(ACCESS_MAINT_TUNNELS) + else + return list() + +/datum/outfit/job/assistant + name = "Civilian" + jobtype = /datum/job/civilian + + uniform = /obj/item/clothing/under/color/random + shoes = /obj/item/clothing/shoes/black + + diff --git a/code/game/jobs/job/engineering.dm b/code/game/jobs/job/engineering.dm index ecae6919813..84f903dd2b1 100644 --- a/code/game/jobs/job/engineering.dm +++ b/code/game/jobs/job/engineering.dm @@ -1,153 +1,153 @@ -/datum/job/chief_engineer - title = "Chief Engineer" - flag = CHIEF - department_flag = ENGSEC - total_positions = 1 - spawn_positions = 1 - is_engineering = 1 - supervisors = "the captain" - department_head = list("Captain") - selection_color = "#ffeeaa" - req_admin_notify = 1 - access = list(access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, - access_teleporter, access_external_airlocks, access_atmospherics, access_emergency_storage, access_eva, - access_heads, access_construction, access_sec_doors, - access_ce, access_RC_announce, access_keycard_auth, access_tcomsat, access_minisat, access_mechanic, access_mineral_storeroom) - minimal_access = list(access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, - access_teleporter, access_external_airlocks, access_atmospherics, access_emergency_storage, access_eva, - access_heads, access_construction, access_sec_doors, - access_ce, access_RC_announce, access_keycard_auth, access_tcomsat, access_minisat, access_mechanic, access_mineral_storeroom) - minimal_player_age = 21 - exp_requirements = 300 - exp_type = EXP_TYPE_ENGINEERING - outfit = /datum/outfit/job/chief_engineer - -/datum/outfit/job/chief_engineer - name = "Chief Engineer" - jobtype = /datum/job/chief_engineer - - uniform = /obj/item/clothing/under/rank/chief_engineer - belt = /obj/item/storage/belt/utility/chief/full - gloves = /obj/item/clothing/gloves/color/black/ce - shoes = /obj/item/clothing/shoes/brown - head = /obj/item/clothing/head/hardhat/white - l_ear = /obj/item/radio/headset/heads/ce - id = /obj/item/card/id/ce - l_pocket = /obj/item/t_scanner - pda = /obj/item/pda/heads/ce - backpack_contents = list( - /obj/item/melee/classic_baton/telescopic = 1 - ) - - backpack = /obj/item/storage/backpack/industrial - satchel = /obj/item/storage/backpack/satchel_eng - dufflebag = /obj/item/storage/backpack/duffel/engineering - box = /obj/item/storage/box/engineer - - -/datum/job/engineer - title = "Station Engineer" - flag = ENGINEER - department_flag = ENGSEC - total_positions = 5 - spawn_positions = 5 - is_engineering = 1 - supervisors = "the chief engineer" - department_head = list("Chief Engineer") - selection_color = "#fff5cc" - access = list(access_eva, access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, access_external_airlocks, access_construction, access_atmospherics, access_mineral_storeroom) - minimal_access = list(access_eva, access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, access_external_airlocks, access_construction, access_mineral_storeroom) - alt_titles = list("Maintenance Technician","Engine Technician","Electrician") - minimal_player_age = 7 - exp_requirements = 300 - exp_type = EXP_TYPE_CREW - outfit = /datum/outfit/job/engineer - -/datum/outfit/job/engineer - name = "Station Engineer" - jobtype = /datum/job/engineer - - uniform = /obj/item/clothing/under/rank/engineer - belt = /obj/item/storage/belt/utility/full - shoes = /obj/item/clothing/shoes/workboots - head = /obj/item/clothing/head/hardhat - l_ear = /obj/item/radio/headset/headset_eng - id = /obj/item/card/id/engineering - l_pocket = /obj/item/t_scanner - pda = /obj/item/pda/engineering - - backpack = /obj/item/storage/backpack/industrial - satchel = /obj/item/storage/backpack/satchel_eng - dufflebag = /obj/item/storage/backpack/duffel/engineering - box = /obj/item/storage/box/engineer - - - -/datum/job/atmos - title = "Life Support Specialist" - flag = ATMOSTECH - department_flag = ENGSEC - total_positions = 3 - spawn_positions = 2 - is_engineering = 1 - supervisors = "the chief engineer" - department_head = list("Chief Engineer") - selection_color = "#fff5cc" - access = list(access_eva, access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, access_external_airlocks, access_construction, access_atmospherics, access_mineral_storeroom) - minimal_access = list(access_eva, access_atmospherics, access_maint_tunnels, access_external_airlocks, access_emergency_storage, access_construction, access_mineral_storeroom, access_tech_storage) - alt_titles = list("Atmospheric Technician") - minimal_player_age = 7 - exp_requirements = 300 - exp_type = EXP_TYPE_CREW - outfit = /datum/outfit/job/atmos - -/datum/outfit/job/atmos - name = "Life Support Specialist" - jobtype = /datum/job/atmos - - uniform = /obj/item/clothing/under/rank/atmospheric_technician - belt = /obj/item/storage/belt/utility/atmostech - shoes = /obj/item/clothing/shoes/workboots - l_ear = /obj/item/radio/headset/headset_eng - id = /obj/item/card/id/engineering - pda = /obj/item/pda/atmos - - backpack = /obj/item/storage/backpack/industrial - satchel = /obj/item/storage/backpack/satchel_eng - dufflebag = /obj/item/storage/backpack/duffel/atmos - box = /obj/item/storage/box/engineer - -/datum/job/mechanic - title = "Mechanic" - flag = MECHANIC - department_flag = KARMA - total_positions = 1 - spawn_positions = 1 - is_engineering = 1 - supervisors = "the chief engineer" - department_head = list("Chief Engineer") - selection_color = "#fff5cc" - access = list(access_engine, access_engine_equip, access_tech_storage, access_maint_tunnels, access_mechanic, access_external_airlocks, access_mineral_storeroom) - minimal_access = list(access_maint_tunnels, access_emergency_storage, access_mechanic, access_external_airlocks, access_mineral_storeroom) - outfit = /datum/outfit/job/mechanic - -/datum/outfit/job/mechanic - name = "Mechanic" - jobtype = /datum/job/mechanic - - uniform = /obj/item/clothing/under/rank/mechanic - belt = /obj/item/storage/belt/utility/full - shoes = /obj/item/clothing/shoes/workboots - head = /obj/item/clothing/head/hardhat - l_ear = /obj/item/radio/headset/headset_eng - id = /obj/item/card/id/engineering - r_pocket = /obj/item/t_scanner - pda = /obj/item/pda/engineering - backpack_contents = list( - /obj/item/pod_paint_bucket = 1 - ) - - backpack = /obj/item/storage/backpack/industrial - satchel = /obj/item/storage/backpack/satchel_eng - dufflebag = /obj/item/storage/backpack/duffel/engineering - box = /obj/item/storage/box/engineer +/datum/job/chief_engineer + title = "Chief Engineer" + flag = JOB_CHIEF + department_flag = JOBCAT_ENGSEC + total_positions = 1 + spawn_positions = 1 + is_engineering = 1 + supervisors = "the captain" + department_head = list("Captain") + selection_color = "#ffeeaa" + req_admin_notify = 1 + access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, + ACCESS_TELEPORTER, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_ATMOSPHERICS, ACCESS_EMERGENCY_STORAGE, ACCESS_EVA, + ACCESS_HEADS, ACCESS_CONSTRUCTION, ACCESS_SEC_DOORS, + ACCESS_CE, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_MINISAT, ACCESS_MECHANIC, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, + ACCESS_TELEPORTER, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_ATMOSPHERICS, ACCESS_EMERGENCY_STORAGE, ACCESS_EVA, + ACCESS_HEADS, ACCESS_CONSTRUCTION, ACCESS_SEC_DOORS, + ACCESS_CE, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_MINISAT, ACCESS_MECHANIC, ACCESS_MINERAL_STOREROOM) + minimal_player_age = 21 + exp_requirements = 300 + exp_type = EXP_TYPE_ENGINEERING + outfit = /datum/outfit/job/chief_engineer + +/datum/outfit/job/chief_engineer + name = "Chief Engineer" + jobtype = /datum/job/chief_engineer + + uniform = /obj/item/clothing/under/rank/chief_engineer + belt = /obj/item/storage/belt/utility/chief/full + gloves = /obj/item/clothing/gloves/color/black/ce + shoes = /obj/item/clothing/shoes/brown + head = /obj/item/clothing/head/hardhat/white + l_ear = /obj/item/radio/headset/heads/ce + id = /obj/item/card/id/ce + l_pocket = /obj/item/t_scanner + pda = /obj/item/pda/heads/ce + backpack_contents = list( + /obj/item/melee/classic_baton/telescopic = 1 + ) + + backpack = /obj/item/storage/backpack/industrial + satchel = /obj/item/storage/backpack/satchel_eng + dufflebag = /obj/item/storage/backpack/duffel/engineering + box = /obj/item/storage/box/engineer + + +/datum/job/engineer + title = "Station Engineer" + flag = JOB_ENGINEER + department_flag = JOBCAT_ENGSEC + total_positions = 5 + spawn_positions = 5 + is_engineering = 1 + supervisors = "the chief engineer" + department_head = list("Chief Engineer") + selection_color = "#fff5cc" + access = list(ACCESS_EVA, ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_ATMOSPHERICS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_EVA, ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_MINERAL_STOREROOM) + alt_titles = list("Maintenance Technician","Engine Technician","Electrician") + minimal_player_age = 7 + exp_requirements = 300 + exp_type = EXP_TYPE_CREW + outfit = /datum/outfit/job/engineer + +/datum/outfit/job/engineer + name = "Station Engineer" + jobtype = /datum/job/engineer + + uniform = /obj/item/clothing/under/rank/engineer + belt = /obj/item/storage/belt/utility/full + shoes = /obj/item/clothing/shoes/workboots + head = /obj/item/clothing/head/hardhat + l_ear = /obj/item/radio/headset/headset_eng + id = /obj/item/card/id/engineering + l_pocket = /obj/item/t_scanner + pda = /obj/item/pda/engineering + + backpack = /obj/item/storage/backpack/industrial + satchel = /obj/item/storage/backpack/satchel_eng + dufflebag = /obj/item/storage/backpack/duffel/engineering + box = /obj/item/storage/box/engineer + + + +/datum/job/atmos + title = "Life Support Specialist" + flag = JOB_ATMOSTECH + department_flag = JOBCAT_ENGSEC + total_positions = 3 + spawn_positions = 2 + is_engineering = 1 + supervisors = "the chief engineer" + department_head = list("Chief Engineer") + selection_color = "#fff5cc" + access = list(ACCESS_EVA, ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_CONSTRUCTION, ACCESS_ATMOSPHERICS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_EVA, ACCESS_ATMOSPHERICS, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_EMERGENCY_STORAGE, ACCESS_CONSTRUCTION, ACCESS_MINERAL_STOREROOM, ACCESS_TECH_STORAGE) + alt_titles = list("Atmospheric Technician") + minimal_player_age = 7 + exp_requirements = 300 + exp_type = EXP_TYPE_CREW + outfit = /datum/outfit/job/atmos + +/datum/outfit/job/atmos + name = "Life Support Specialist" + jobtype = /datum/job/atmos + + uniform = /obj/item/clothing/under/rank/atmospheric_technician + belt = /obj/item/storage/belt/utility/atmostech + shoes = /obj/item/clothing/shoes/workboots + l_ear = /obj/item/radio/headset/headset_eng + id = /obj/item/card/id/engineering + pda = /obj/item/pda/atmos + + backpack = /obj/item/storage/backpack/industrial + satchel = /obj/item/storage/backpack/satchel_eng + dufflebag = /obj/item/storage/backpack/duffel/atmos + box = /obj/item/storage/box/engineer + +/datum/job/mechanic + title = "Mechanic" + flag = JOB_MECHANIC + department_flag = JOBCAT_KARMA + total_positions = 1 + spawn_positions = 1 + is_engineering = 1 + supervisors = "the chief engineer" + department_head = list("Chief Engineer") + selection_color = "#fff5cc" + access = list(ACCESS_ENGINE, ACCESS_ENGINE_EQUIP, ACCESS_TECH_STORAGE, ACCESS_MAINT_TUNNELS, ACCESS_MECHANIC, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MAINT_TUNNELS, ACCESS_EMERGENCY_STORAGE, ACCESS_MECHANIC, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_MINERAL_STOREROOM) + outfit = /datum/outfit/job/mechanic + +/datum/outfit/job/mechanic + name = "Mechanic" + jobtype = /datum/job/mechanic + + uniform = /obj/item/clothing/under/rank/mechanic + belt = /obj/item/storage/belt/utility/full + shoes = /obj/item/clothing/shoes/workboots + head = /obj/item/clothing/head/hardhat + l_ear = /obj/item/radio/headset/headset_eng + id = /obj/item/card/id/engineering + r_pocket = /obj/item/t_scanner + pda = /obj/item/pda/engineering + backpack_contents = list( + /obj/item/pod_paint_bucket = 1 + ) + + backpack = /obj/item/storage/backpack/industrial + satchel = /obj/item/storage/backpack/satchel_eng + dufflebag = /obj/item/storage/backpack/duffel/engineering + box = /obj/item/storage/box/engineer diff --git a/code/game/jobs/job/job.dm b/code/game/jobs/job/job.dm index 9def19d2970..03ac85c8d57 100644 --- a/code/game/jobs/job/job.dm +++ b/code/game/jobs/job/job.dm @@ -1,272 +1,272 @@ -/datum/job - - //The name of the job - var/title = "NOPE" - - //Job access. The use of minimal_access or access is determined by a config setting: config.jobs_have_minimal_access - var/list/minimal_access = list() //Useful for servers which prefer to only have access given to the places a job absolutely needs (Larger server population) - var/list/access = list() //Useful for servers which either have fewer players, so each person needs to fill more than one role, or servers which like to give more access, so players can't hide forever in their super secure departments (I'm looking at you, chemistry!) - - //Bitflags for the job - var/flag = 0 - var/department_flag = 0 - var/department_head = list() - - //Players will be allowed to spawn in as jobs that are set to "Station" - var/list/faction = list("Station") - - //How many players can be this job - var/total_positions = 0 - - //How many players can spawn in as this job - var/spawn_positions = 0 - - //How many players have this job - var/current_positions = 0 - - //Supervisors, who this person answers to directly - var/supervisors = "" - - //Sellection screen color - var/selection_color = "#ffffff" - - //List of alternate titles, if any - var/list/alt_titles - - //If this is set to 1, a text is printed to the player when jobs are assigned, telling him that he should let admins know that he has to disconnect. - var/req_admin_notify - - //Various Departmental identifiers - var/is_supply - var/is_service - var/is_command - var/is_legal - var/is_engineering - var/is_medical - var/is_science - var/is_security - - //If you have use_age_restriction_for_jobs config option enabled and the database set up, this option will add a requirement for players to be at least minimal_player_age days old. (meaning they first signed in at least that many days before.) - var/minimal_player_age = 0 - - var/exp_requirements = 0 - var/exp_type = "" - - var/disabilities_allowed = 1 - - var/admin_only = 0 - var/spawn_ert = 0 - var/syndicate_command = 0 - - var/outfit = null - - ///////////////////////////////// - // /vg/ feature: Job Objectives! - ///////////////////////////////// - var/required_objectives=list() // Objectives that are ALWAYS added. - var/optional_objectives=list() // Objectives that are SOMETIMES added. - -//Only override this proc -/datum/job/proc/after_spawn(mob/living/carbon/human/H) - -/datum/job/proc/announce(mob/living/carbon/human/H) - -/datum/job/proc/equip(mob/living/carbon/human/H, visualsOnly = FALSE, announce = TRUE) - if(!H) - return 0 - - H.dna.species.before_equip_job(src, H, visualsOnly) - - if(outfit) - H.equipOutfit(outfit, visualsOnly) - - H.dna.species.after_equip_job(src, H, visualsOnly) - - if(!visualsOnly && announce) - announce(H) - -/datum/job/proc/get_access() - if(!config) //Needed for robots. - return src.minimal_access.Copy() - - if(config.jobs_have_minimal_access) - return src.minimal_access.Copy() - else - return src.access.Copy() - -//If the configuration option is set to require players to be logged as old enough to play certain jobs, then this proc checks that they are, otherwise it just returns 1 -/datum/job/proc/player_old_enough(client/C) - if(available_in_days(C) == 0) - return 1 //Available in 0 days = available right now = player is old enough to play. - return 0 - - -/datum/job/proc/available_in_days(client/C) - if(!C) - return 0 - if(!config.use_age_restriction_for_jobs) - return 0 - if(!isnum(C.player_age)) - return 0 //This is only a number if the db connection is established, otherwise it is text: "Requires database", meaning these restrictions cannot be enforced - if(!isnum(minimal_player_age)) - return 0 - - return max(0, minimal_player_age - C.player_age) - -/datum/job/proc/barred_by_disability(client/C) - if(!C) - return 0 - if(disabilities_allowed) - return 0 - var/list/prohibited_disabilities = list(DISABILITY_FLAG_BLIND, DISABILITY_FLAG_DEAF, DISABILITY_FLAG_MUTE, DISABILITY_FLAG_DIZZY) - for(var/i = 1, i < prohibited_disabilities.len, i++) - var/this_disability = prohibited_disabilities[i] - if(C.prefs.disabilities & this_disability) - return 1 - return 0 - -/datum/job/proc/is_position_available() - return (current_positions < total_positions) || (total_positions == -1) - -/datum/outfit/job - name = "Standard Gear" - collect_not_del = TRUE // we don't want anyone to lose their job shit - - var/allow_loadout = TRUE - var/allow_backbag_choice = TRUE - var/jobtype = null - - uniform = /obj/item/clothing/under/color/grey - id = /obj/item/card/id - l_ear = /obj/item/radio/headset - back = /obj/item/storage/backpack - shoes = /obj/item/clothing/shoes/black - pda = /obj/item/pda - - var/backpack = /obj/item/storage/backpack - var/satchel = /obj/item/storage/backpack/satchel_norm - var/dufflebag = /obj/item/storage/backpack/duffel - box = /obj/item/storage/box/survival - - var/tmp/list/gear_leftovers = list() - -/datum/outfit/job/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - if(allow_backbag_choice) - switch(H.backbag) - if(GBACKPACK) - back = /obj/item/storage/backpack //Grey backpack - if(GSATCHEL) - back = /obj/item/storage/backpack/satchel_norm //Grey satchel - if(GDUFFLEBAG) - back = /obj/item/storage/backpack/duffel //Grey Dufflebag - if(LSATCHEL) - back = /obj/item/storage/backpack/satchel //Leather Satchel - if(DSATCHEL) - back = satchel //Department satchel - if(DDUFFLEBAG) - back = dufflebag //Department dufflebag - else - back = backpack //Department backpack - - if(box && H.dna.species.speciesbox) - box = H.dna.species.speciesbox - - if(allow_loadout && H.client && (H.client.prefs.gear && H.client.prefs.gear.len)) - for(var/gear in H.client.prefs.gear) - var/datum/gear/G = gear_datums[gear] - if(G) - var/permitted = FALSE - - if(G.allowed_roles) - if(name in G.allowed_roles) - permitted = TRUE - else - permitted = TRUE - - if(G.whitelisted && (G.whitelisted != H.dna.species.name || !is_alien_whitelisted(H, G.whitelisted))) - permitted = FALSE - - if(!permitted) - to_chat(H, "Your current job or whitelist status does not permit you to spawn with [gear]!") - continue - - if(G.slot) - if(H.equip_to_slot_or_del(G.spawn_item(H), G.slot)) - to_chat(H, "Equipping you with [gear]!") - else - gear_leftovers += G - else - gear_leftovers += G - -/datum/outfit/job/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - if(visualsOnly) - return - - imprint_idcard(H) - - H.sec_hud_set_ID() - - imprint_pda(H) - - if(gear_leftovers.len) - for(var/datum/gear/G in gear_leftovers) - var/atom/placed_in = H.equip_or_collect(G.spawn_item(null, H.client.prefs.gear[G.display_name])) - if(istype(placed_in)) - if(isturf(placed_in)) - to_chat(H, "Placing [G.display_name] on [placed_in]!") - else - to_chat(H, "Placing [G.display_name] in [placed_in.name].") - continue - if(H.equip_to_appropriate_slot(G)) - to_chat(H, "Placing [G.display_name] in your inventory!") - continue - if(H.put_in_hands(G)) - to_chat(H, "Placing [G.display_name] in your hands!") - continue - to_chat(H, "Failed to locate a storage object on your mob, either you spawned with no hands free and no backpack or this is a bug.") - qdel(G) - - qdel(gear_leftovers) - - return 1 - -/datum/outfit/job/proc/imprint_idcard(mob/living/carbon/human/H) - var/datum/job/J = SSjobs.GetJobType(jobtype) - if(!J) - J = SSjobs.GetJob(H.job) - - var/alt_title - if(H.mind) - alt_title = H.mind.role_alt_title - - var/obj/item/card/id/C = H.wear_id - if(istype(C)) - C.access = J.get_access() - C.registered_name = H.real_name - C.rank = J.title - C.assignment = alt_title ? alt_title : J.title - C.sex = capitalize(H.gender) - C.age = H.age - C.name = "[C.registered_name]'s ID Card ([C.assignment])" - C.photo = get_id_photo(H) - - if(H.mind && H.mind.initial_account) - C.associated_account_number = H.mind.initial_account.account_number - C.owner_uid = H.UID() - C.owner_ckey = H.ckey - -/datum/outfit/job/proc/imprint_pda(mob/living/carbon/human/H) - var/obj/item/pda/PDA = H.wear_pda - var/obj/item/card/id/C = H.wear_id - if(istype(PDA) && istype(C)) - PDA.owner = H.real_name - PDA.ownjob = C.assignment - PDA.ownrank = C.rank - PDA.name = "PDA-[H.real_name] ([PDA.ownjob])" - -/datum/job/proc/would_accept_job_transfer_from_player(mob/player) - if(!guest_jobbans(title)) // actually checks if job is a whitelisted position - return TRUE - if(!istype(player)) - return FALSE - return is_job_whitelisted(player, title) +/datum/job + + //The name of the job + var/title = "NOPE" + + //Job access. The use of minimal_access or access is determined by a config setting: config.jobs_have_minimal_access + var/list/minimal_access = list() //Useful for servers which prefer to only have access given to the places a job absolutely needs (Larger server population) + var/list/access = list() //Useful for servers which either have fewer players, so each person needs to fill more than one role, or servers which like to give more access, so players can't hide forever in their super secure departments (I'm looking at you, chemistry!) + + //Bitflags for the job + var/flag = 0 + var/department_flag = 0 + var/department_head = list() + + //Players will be allowed to spawn in as jobs that are set to "Station" + var/list/faction = list("Station") + + //How many players can be this job + var/total_positions = 0 + + //How many players can spawn in as this job + var/spawn_positions = 0 + + //How many players have this job + var/current_positions = 0 + + //Supervisors, who this person answers to directly + var/supervisors = "" + + //Sellection screen color + var/selection_color = "#ffffff" + + //List of alternate titles, if any + var/list/alt_titles + + //If this is set to 1, a text is printed to the player when jobs are assigned, telling him that he should let admins know that he has to disconnect. + var/req_admin_notify + + //Various Departmental identifiers + var/is_supply + var/is_service + var/is_command + var/is_legal + var/is_engineering + var/is_medical + var/is_science + var/is_security + + //If you have use_age_restriction_for_jobs config option enabled and the database set up, this option will add a requirement for players to be at least minimal_player_age days old. (meaning they first signed in at least that many days before.) + var/minimal_player_age = 0 + + var/exp_requirements = 0 + var/exp_type = "" + + var/disabilities_allowed = 1 + + var/admin_only = 0 + var/spawn_ert = 0 + var/syndicate_command = 0 + + var/outfit = null + + ///////////////////////////////// + // /vg/ feature: Job Objectives! + ///////////////////////////////// + var/required_objectives=list() // Objectives that are ALWAYS added. + var/optional_objectives=list() // Objectives that are SOMETIMES added. + +//Only override this proc +/datum/job/proc/after_spawn(mob/living/carbon/human/H) + +/datum/job/proc/announce(mob/living/carbon/human/H) + +/datum/job/proc/equip(mob/living/carbon/human/H, visualsOnly = FALSE, announce = TRUE) + if(!H) + return 0 + + H.dna.species.before_equip_job(src, H, visualsOnly) + + if(outfit) + H.equipOutfit(outfit, visualsOnly) + + H.dna.species.after_equip_job(src, H, visualsOnly) + + if(!visualsOnly && announce) + announce(H) + +/datum/job/proc/get_access() + if(!config) //Needed for robots. + return src.minimal_access.Copy() + + if(config.jobs_have_minimal_access) + return src.minimal_access.Copy() + else + return src.access.Copy() + +//If the configuration option is set to require players to be logged as old enough to play certain jobs, then this proc checks that they are, otherwise it just returns 1 +/datum/job/proc/player_old_enough(client/C) + if(available_in_days(C) == 0) + return 1 //Available in 0 days = available right now = player is old enough to play. + return 0 + + +/datum/job/proc/available_in_days(client/C) + if(!C) + return 0 + if(!config.use_age_restriction_for_jobs) + return 0 + if(!isnum(C.player_age)) + return 0 //This is only a number if the db connection is established, otherwise it is text: "Requires database", meaning these restrictions cannot be enforced + if(!isnum(minimal_player_age)) + return 0 + + return max(0, minimal_player_age - C.player_age) + +/datum/job/proc/barred_by_disability(client/C) + if(!C) + return 0 + if(disabilities_allowed) + return 0 + var/list/prohibited_disabilities = list(DISABILITY_FLAG_BLIND, DISABILITY_FLAG_DEAF, DISABILITY_FLAG_MUTE, DISABILITY_FLAG_DIZZY) + for(var/i = 1, i < prohibited_disabilities.len, i++) + var/this_disability = prohibited_disabilities[i] + if(C.prefs.disabilities & this_disability) + return 1 + return 0 + +/datum/job/proc/is_position_available() + return (current_positions < total_positions) || (total_positions == -1) + +/datum/outfit/job + name = "Standard Gear" + collect_not_del = TRUE // we don't want anyone to lose their job shit + + var/allow_loadout = TRUE + var/allow_backbag_choice = TRUE + var/jobtype = null + + uniform = /obj/item/clothing/under/color/grey + id = /obj/item/card/id + l_ear = /obj/item/radio/headset + back = /obj/item/storage/backpack + shoes = /obj/item/clothing/shoes/black + pda = /obj/item/pda + + var/backpack = /obj/item/storage/backpack + var/satchel = /obj/item/storage/backpack/satchel_norm + var/dufflebag = /obj/item/storage/backpack/duffel + box = /obj/item/storage/box/survival + + var/tmp/list/gear_leftovers = list() + +/datum/outfit/job/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + if(allow_backbag_choice) + switch(H.backbag) + if(GBACKPACK) + back = /obj/item/storage/backpack //Grey backpack + if(GSATCHEL) + back = /obj/item/storage/backpack/satchel_norm //Grey satchel + if(GDUFFLEBAG) + back = /obj/item/storage/backpack/duffel //Grey Dufflebag + if(LSATCHEL) + back = /obj/item/storage/backpack/satchel //Leather Satchel + if(DSATCHEL) + back = satchel //Department satchel + if(DDUFFLEBAG) + back = dufflebag //Department dufflebag + else + back = backpack //Department backpack + + if(box && H.dna.species.speciesbox) + box = H.dna.species.speciesbox + + if(allow_loadout && H.client && (H.client.prefs.gear && H.client.prefs.gear.len)) + for(var/gear in H.client.prefs.gear) + var/datum/gear/G = gear_datums[gear] + if(G) + var/permitted = FALSE + + if(G.allowed_roles) + if(name in G.allowed_roles) + permitted = TRUE + else + permitted = TRUE + + if(G.whitelisted && (G.whitelisted != H.dna.species.name || !is_alien_whitelisted(H, G.whitelisted))) + permitted = FALSE + + if(!permitted) + to_chat(H, "Your current job or whitelist status does not permit you to spawn with [gear]!") + continue + + if(G.slot) + if(H.equip_to_slot_or_del(G.spawn_item(H), G.slot)) + to_chat(H, "Equipping you with [gear]!") + else + gear_leftovers += G + else + gear_leftovers += G + +/datum/outfit/job/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + if(visualsOnly) + return + + imprint_idcard(H) + + H.sec_hud_set_ID() + + imprint_pda(H) + + if(gear_leftovers.len) + for(var/datum/gear/G in gear_leftovers) + var/atom/placed_in = H.equip_or_collect(G.spawn_item(null, H.client.prefs.gear[G.display_name])) + if(istype(placed_in)) + if(isturf(placed_in)) + to_chat(H, "Placing [G.display_name] on [placed_in]!") + else + to_chat(H, "Placing [G.display_name] in [placed_in.name].") + continue + if(H.equip_to_appropriate_slot(G)) + to_chat(H, "Placing [G.display_name] in your inventory!") + continue + if(H.put_in_hands(G)) + to_chat(H, "Placing [G.display_name] in your hands!") + continue + to_chat(H, "Failed to locate a storage object on your mob, either you spawned with no hands free and no backpack or this is a bug.") + qdel(G) + + qdel(gear_leftovers) + + return 1 + +/datum/outfit/job/proc/imprint_idcard(mob/living/carbon/human/H) + var/datum/job/J = SSjobs.GetJobType(jobtype) + if(!J) + J = SSjobs.GetJob(H.job) + + var/alt_title + if(H.mind) + alt_title = H.mind.role_alt_title + + var/obj/item/card/id/C = H.wear_id + if(istype(C)) + C.access = J.get_access() + C.registered_name = H.real_name + C.rank = J.title + C.assignment = alt_title ? alt_title : J.title + C.sex = capitalize(H.gender) + C.age = H.age + C.name = "[C.registered_name]'s ID Card ([C.assignment])" + C.photo = get_id_photo(H) + + if(H.mind && H.mind.initial_account) + C.associated_account_number = H.mind.initial_account.account_number + C.owner_uid = H.UID() + C.owner_ckey = H.ckey + +/datum/outfit/job/proc/imprint_pda(mob/living/carbon/human/H) + var/obj/item/pda/PDA = H.wear_pda + var/obj/item/card/id/C = H.wear_id + if(istype(PDA) && istype(C)) + PDA.owner = H.real_name + PDA.ownjob = C.assignment + PDA.ownrank = C.rank + PDA.name = "PDA-[H.real_name] ([PDA.ownjob])" + +/datum/job/proc/would_accept_job_transfer_from_player(mob/player) + if(!guest_jobbans(title)) // actually checks if job is a whitelisted position + return TRUE + if(!istype(player)) + return FALSE + return is_job_whitelisted(player, title) diff --git a/code/game/jobs/job/medical.dm b/code/game/jobs/job/medical.dm index 49e6b4486ac..259afa12c9e 100644 --- a/code/game/jobs/job/medical.dm +++ b/code/game/jobs/job/medical.dm @@ -1,318 +1,318 @@ -/datum/job/cmo - title = "Chief Medical Officer" - flag = CMO - department_flag = MEDSCI - total_positions = 1 - spawn_positions = 1 - is_medical = 1 - supervisors = "the captain" - department_head = list("Captain") - selection_color = "#ffddf0" - req_admin_notify = 1 - access = list(access_medical, access_morgue, access_genetics, access_heads, - access_chemistry, access_virology, access_cmo, access_surgery, access_RC_announce, - access_keycard_auth, access_sec_doors, access_psychiatrist, access_paramedic, access_mineral_storeroom) - minimal_access = list(access_eva, access_medical, access_morgue, access_genetics, access_heads, - access_chemistry, access_virology, access_cmo, access_surgery, access_RC_announce, - access_keycard_auth, access_sec_doors, access_psychiatrist, access_maint_tunnels, access_paramedic, access_mineral_storeroom) - minimal_player_age = 21 - exp_requirements = 300 - exp_type = EXP_TYPE_MEDICAL - outfit = /datum/outfit/job/cmo - -/datum/outfit/job/cmo - name = "Chief Medical Officer" - jobtype = /datum/job/cmo - - uniform = /obj/item/clothing/under/rank/chief_medical_officer - suit = /obj/item/clothing/suit/storage/labcoat/cmo - shoes = /obj/item/clothing/shoes/brown - l_ear = /obj/item/radio/headset/heads/cmo - id = /obj/item/card/id/cmo - suit_store = /obj/item/flashlight/pen - l_hand = /obj/item/storage/firstaid/adv - pda = /obj/item/pda/heads/cmo - backpack_contents = list( - /obj/item/melee/classic_baton/telescopic = 1 - ) - - backpack = /obj/item/storage/backpack/medic - satchel = /obj/item/storage/backpack/satchel_med - dufflebag = /obj/item/storage/backpack/duffel/medical - -/datum/job/doctor - title = "Medical Doctor" - flag = DOCTOR - department_flag = MEDSCI - total_positions = 5 - spawn_positions = 3 - is_medical = 1 - supervisors = "the chief medical officer" - department_head = list("Chief Medical Officer") - selection_color = "#ffeef0" - access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics, access_mineral_storeroom) - minimal_access = list(access_medical, access_morgue, access_surgery, access_maint_tunnels) - alt_titles = list("Surgeon","Nurse") - minimal_player_age = 3 - exp_requirements = 180 - exp_type = EXP_TYPE_CREW - outfit = /datum/outfit/job/doctor - -/datum/outfit/job/doctor - name = "Medical Doctor" - jobtype = /datum/job/doctor - - uniform = /obj/item/clothing/under/rank/medical - suit = /obj/item/clothing/suit/storage/labcoat - shoes = /obj/item/clothing/shoes/white - l_ear = /obj/item/radio/headset/headset_med - id = /obj/item/card/id/medical - suit_store = /obj/item/flashlight/pen - l_hand = /obj/item/storage/firstaid/adv - pda = /obj/item/pda/medical - - backpack = /obj/item/storage/backpack/medic - satchel = /obj/item/storage/backpack/satchel_med - dufflebag = /obj/item/storage/backpack/duffel/medical - -/datum/job/coroner - title = "Coroner" - flag = CORONER - department_flag = MEDSCI - total_positions = 1 - spawn_positions = 1 - is_medical = 1 - supervisors = "the chief medical officer" - department_head = list("Chief Medical Officer") - selection_color = "#ffeef0" - access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics, access_mineral_storeroom) - minimal_access = list(access_medical, access_morgue, access_maint_tunnels) - minimal_player_age = 3 - exp_requirements = 180 - exp_type = EXP_TYPE_CREW - outfit = /datum/outfit/job/coroner - -/datum/outfit/job/coroner - name = "Coroner" - jobtype = /datum/job/coroner - - uniform = /obj/item/clothing/under/rank/medical/mortician - suit = /obj/item/clothing/suit/storage/labcoat/mortician - shoes = /obj/item/clothing/shoes/white - l_ear = /obj/item/radio/headset/headset_med - id = /obj/item/card/id/medical - suit_store = /obj/item/flashlight/pen - l_hand = /obj/item/clipboard - pda = /obj/item/pda/medical - - backpack = /obj/item/storage/backpack/medic - satchel = /obj/item/storage/backpack/satchel_med - dufflebag = /obj/item/storage/backpack/duffel/medical - - backpack_contents = list( - /obj/item/clothing/head/surgery/black = 1, - /obj/item/autopsy_scanner = 1, - /obj/item/reagent_scanner = 1, - /obj/item/storage/box/bodybags = 1) - -/datum/outfit/job/doctor/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - . = ..() - if(H.mind && H.mind.role_alt_title) - switch(H.mind.role_alt_title) - if("Surgeon") - uniform = /obj/item/clothing/under/rank/medical/blue - head = /obj/item/clothing/head/surgery/blue - if("Medical Doctor") - uniform = /obj/item/clothing/under/rank/medical - if("Nurse") - if(H.gender == FEMALE) - if(prob(50)) - uniform = /obj/item/clothing/under/rank/nursesuit - else - uniform = /obj/item/clothing/under/rank/nurse - head = /obj/item/clothing/head/nursehat - else - uniform = /obj/item/clothing/under/rank/medical/purple - - - -//Chemist is a medical job damnit //YEAH FUCK YOU SCIENCE -Pete //Guys, behave -Erro -/datum/job/chemist - title = "Chemist" - flag = CHEMIST - department_flag = MEDSCI - total_positions = 2 - spawn_positions = 2 - is_medical = 1 - supervisors = "the chief medical officer" - department_head = list("Chief Medical Officer") - selection_color = "#ffeef0" - access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics, access_mineral_storeroom) - minimal_access = list(access_medical, access_chemistry, access_maint_tunnels, access_mineral_storeroom) - alt_titles = list("Pharmacist","Pharmacologist") - minimal_player_age = 7 - exp_requirements = 300 - exp_type = EXP_TYPE_CREW - outfit = /datum/outfit/job/chemist - -/datum/outfit/job/chemist - name = "Chemist" - jobtype = /datum/job/chemist - - uniform = /obj/item/clothing/under/rank/chemist - suit = /obj/item/clothing/suit/storage/labcoat/chemist - shoes = /obj/item/clothing/shoes/white - l_ear = /obj/item/radio/headset/headset_med - glasses = /obj/item/clothing/glasses/science - id = /obj/item/card/id/medical - pda = /obj/item/pda/chemist - - backpack = /obj/item/storage/backpack/chemistry - satchel = /obj/item/storage/backpack/satchel_chem - dufflebag = /obj/item/storage/backpack/duffel/chemistry - -/datum/job/geneticist - title = "Geneticist" - flag = GENETICIST - department_flag = MEDSCI - total_positions = 2 - spawn_positions = 2 - is_medical = 1 - supervisors = "the chief medical officer and the research director" - department_head = list("Chief Medical Officer", "Research Director") - selection_color = "#ffeef0" - access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics, access_research, access_mineral_storeroom) - minimal_access = list(access_medical, access_morgue, access_genetics, access_research, access_maint_tunnels) - minimal_player_age = 3 - exp_requirements = 180 - exp_type = EXP_TYPE_CREW - outfit = /datum/outfit/job/geneticist - -/datum/outfit/job/geneticist - name = "Geneticist" - jobtype = /datum/job/geneticist - - uniform = /obj/item/clothing/under/rank/geneticist - suit = /obj/item/clothing/suit/storage/labcoat/genetics - shoes = /obj/item/clothing/shoes/white - l_ear = /obj/item/radio/headset/headset_medsci - id = /obj/item/card/id/medical - suit_store = /obj/item/flashlight/pen - pda = /obj/item/pda/geneticist - - backpack = /obj/item/storage/backpack/genetics - satchel = /obj/item/storage/backpack/satchel_gen - dufflebag = /obj/item/storage/backpack/duffel/genetics - - -/datum/job/virologist - title = "Virologist" - flag = VIROLOGIST - department_flag = MEDSCI - total_positions = 1 - spawn_positions = 1 - is_medical = 1 - supervisors = "the chief medical officer" - department_head = list("Chief Medical Officer") - selection_color = "#ffeef0" - access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics) - minimal_access = list(access_medical, access_virology, access_maint_tunnels, access_mineral_storeroom) - alt_titles = list("Pathologist","Microbiologist") - minimal_player_age = 7 - exp_requirements = 300 - exp_type = EXP_TYPE_CREW - outfit = /datum/outfit/job/virologist - -/datum/outfit/job/virologist - name = "Virologist" - jobtype = /datum/job/virologist - - uniform = /obj/item/clothing/under/rank/virologist - suit = /obj/item/clothing/suit/storage/labcoat/virologist - shoes = /obj/item/clothing/shoes/white - mask = /obj/item/clothing/mask/surgical - l_ear = /obj/item/radio/headset/headset_med - id = /obj/item/card/id/medical - suit_store = /obj/item/flashlight/pen - pda = /obj/item/pda/viro - - backpack = /obj/item/storage/backpack/virology - satchel = /obj/item/storage/backpack/satchel_vir - dufflebag = /obj/item/storage/backpack/duffel/virology - -/datum/job/psychiatrist - title = "Psychiatrist" - flag = PSYCHIATRIST - department_flag = MEDSCI - total_positions = 1 - spawn_positions = 1 - is_medical = 1 - supervisors = "the chief medical officer" - department_head = list("Chief Medical Officer") - selection_color = "#ffeef0" - access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics, access_psychiatrist) - minimal_access = list(access_medical, access_psychiatrist, access_maint_tunnels) - alt_titles = list("Psychologist","Therapist") - outfit = /datum/outfit/job/psychiatrist - -/datum/outfit/job/psychiatrist - name = "Psychiatrist" - jobtype = /datum/job/psychiatrist - - uniform = /obj/item/clothing/under/rank/medical - suit = /obj/item/clothing/suit/storage/labcoat - shoes = /obj/item/clothing/shoes/laceup - l_ear = /obj/item/radio/headset/headset_med - id = /obj/item/card/id/medical - suit_store = /obj/item/flashlight/pen - pda = /obj/item/pda/medical - -/datum/outfit/job/psychiatrist/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) - . = ..() - if(H.mind && H.mind.role_alt_title) - switch(H.mind.role_alt_title) - if("Psychiatrist") - uniform = /obj/item/clothing/under/rank/psych - if("Psychologist") - uniform = /obj/item/clothing/under/rank/psych/turtleneck - if("Therapist") - uniform = /obj/item/clothing/under/rank/medical - -/datum/job/paramedic - title = "Paramedic" - flag = PARAMEDIC - department_flag = MEDSCI - total_positions = 1 - spawn_positions = 1 - is_medical = 1 - supervisors = "the chief medical officer" - department_head = list("Chief Medical Officer") - selection_color = "#ffeef0" - access = list(access_paramedic, access_medical, access_maint_tunnels, access_external_airlocks, access_morgue) - minimal_access=list(access_paramedic, access_medical, access_maint_tunnels, access_external_airlocks, access_morgue) - minimal_player_age = 3 - exp_requirements = 180 - exp_type = EXP_TYPE_CREW - outfit = /datum/outfit/job/paramedic - -/datum/outfit/job/paramedic - name = "Paramedic" - jobtype = /datum/job/paramedic - - uniform = /obj/item/clothing/under/rank/medical/paramedic - shoes = /obj/item/clothing/shoes/black - head = /obj/item/clothing/head/soft/blue - mask = /obj/item/clothing/mask/cigarette - l_ear = /obj/item/radio/headset/headset_med - id = /obj/item/card/id/medical - l_pocket = /obj/item/flashlight/pen - pda = /obj/item/pda/medical - backpack_contents = list( - /obj/item/healthanalyzer = 1 - ) - - backpack = /obj/item/storage/backpack/medic - satchel = /obj/item/storage/backpack/satchel_med - dufflebag = /obj/item/storage/backpack/duffel/medical - box = /obj/item/storage/box/engineer - +/datum/job/cmo + title = "Chief Medical Officer" + flag = JOB_CMO + department_flag = JOBCAT_MEDSCI + total_positions = 1 + spawn_positions = 1 + is_medical = 1 + supervisors = "the captain" + department_head = list("Captain") + selection_color = "#ffddf0" + req_admin_notify = 1 + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_GENETICS, ACCESS_HEADS, + ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_CMO, ACCESS_SURGERY, ACCESS_RC_ANNOUNCE, + ACCESS_KEYCARD_AUTH, ACCESS_SEC_DOORS, ACCESS_PSYCHIATRIST, ACCESS_PARAMEDIC, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_EVA, ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_GENETICS, ACCESS_HEADS, + ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_CMO, ACCESS_SURGERY, ACCESS_RC_ANNOUNCE, + ACCESS_KEYCARD_AUTH, ACCESS_SEC_DOORS, ACCESS_PSYCHIATRIST, ACCESS_MAINT_TUNNELS, ACCESS_PARAMEDIC, ACCESS_MINERAL_STOREROOM) + minimal_player_age = 21 + exp_requirements = 300 + exp_type = EXP_TYPE_MEDICAL + outfit = /datum/outfit/job/cmo + +/datum/outfit/job/cmo + name = "Chief Medical Officer" + jobtype = /datum/job/cmo + + uniform = /obj/item/clothing/under/rank/chief_medical_officer + suit = /obj/item/clothing/suit/storage/labcoat/cmo + shoes = /obj/item/clothing/shoes/brown + l_ear = /obj/item/radio/headset/heads/cmo + id = /obj/item/card/id/cmo + suit_store = /obj/item/flashlight/pen + l_hand = /obj/item/storage/firstaid/adv + pda = /obj/item/pda/heads/cmo + backpack_contents = list( + /obj/item/melee/classic_baton/telescopic = 1 + ) + + backpack = /obj/item/storage/backpack/medic + satchel = /obj/item/storage/backpack/satchel_med + dufflebag = /obj/item/storage/backpack/duffel/medical + +/datum/job/doctor + title = "Medical Doctor" + flag = JOB_DOCTOR + department_flag = JOBCAT_MEDSCI + total_positions = 5 + spawn_positions = 3 + is_medical = 1 + supervisors = "the chief medical officer" + department_head = list("Chief Medical Officer") + selection_color = "#ffeef0" + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_MAINT_TUNNELS) + alt_titles = list("Surgeon","Nurse") + minimal_player_age = 3 + exp_requirements = 180 + exp_type = EXP_TYPE_CREW + outfit = /datum/outfit/job/doctor + +/datum/outfit/job/doctor + name = "Medical Doctor" + jobtype = /datum/job/doctor + + uniform = /obj/item/clothing/under/rank/medical + suit = /obj/item/clothing/suit/storage/labcoat + shoes = /obj/item/clothing/shoes/white + l_ear = /obj/item/radio/headset/headset_med + id = /obj/item/card/id/medical + suit_store = /obj/item/flashlight/pen + l_hand = /obj/item/storage/firstaid/adv + pda = /obj/item/pda/medical + + backpack = /obj/item/storage/backpack/medic + satchel = /obj/item/storage/backpack/satchel_med + dufflebag = /obj/item/storage/backpack/duffel/medical + +/datum/job/coroner + title = "Coroner" + flag = JOB_CORONER + department_flag = JOBCAT_MEDSCI + total_positions = 1 + spawn_positions = 1 + is_medical = 1 + supervisors = "the chief medical officer" + department_head = list("Chief Medical Officer") + selection_color = "#ffeef0" + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS) + minimal_player_age = 3 + exp_requirements = 180 + exp_type = EXP_TYPE_CREW + outfit = /datum/outfit/job/coroner + +/datum/outfit/job/coroner + name = "Coroner" + jobtype = /datum/job/coroner + + uniform = /obj/item/clothing/under/rank/medical/mortician + suit = /obj/item/clothing/suit/storage/labcoat/mortician + shoes = /obj/item/clothing/shoes/white + l_ear = /obj/item/radio/headset/headset_med + id = /obj/item/card/id/medical + suit_store = /obj/item/flashlight/pen + l_hand = /obj/item/clipboard + pda = /obj/item/pda/medical + + backpack = /obj/item/storage/backpack/medic + satchel = /obj/item/storage/backpack/satchel_med + dufflebag = /obj/item/storage/backpack/duffel/medical + + backpack_contents = list( + /obj/item/clothing/head/surgery/black = 1, + /obj/item/autopsy_scanner = 1, + /obj/item/reagent_scanner = 1, + /obj/item/storage/box/bodybags = 1) + +/datum/outfit/job/doctor/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + . = ..() + if(H.mind && H.mind.role_alt_title) + switch(H.mind.role_alt_title) + if("Surgeon") + uniform = /obj/item/clothing/under/rank/medical/blue + head = /obj/item/clothing/head/surgery/blue + if("Medical Doctor") + uniform = /obj/item/clothing/under/rank/medical + if("Nurse") + if(H.gender == FEMALE) + if(prob(50)) + uniform = /obj/item/clothing/under/rank/nursesuit + else + uniform = /obj/item/clothing/under/rank/nurse + head = /obj/item/clothing/head/nursehat + else + uniform = /obj/item/clothing/under/rank/medical/purple + + + +//Chemist is a medical job damnit //YEAH FUCK YOU SCIENCE -Pete //Guys, behave -Erro +/datum/job/chemist + title = "Chemist" + flag = JOB_CHEMIST + department_flag = JOBCAT_MEDSCI + total_positions = 2 + spawn_positions = 2 + is_medical = 1 + supervisors = "the chief medical officer" + department_head = list("Chief Medical Officer") + selection_color = "#ffeef0" + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MEDICAL, ACCESS_CHEMISTRY, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) + alt_titles = list("Pharmacist","Pharmacologist") + minimal_player_age = 7 + exp_requirements = 300 + exp_type = EXP_TYPE_CREW + outfit = /datum/outfit/job/chemist + +/datum/outfit/job/chemist + name = "Chemist" + jobtype = /datum/job/chemist + + uniform = /obj/item/clothing/under/rank/chemist + suit = /obj/item/clothing/suit/storage/labcoat/chemist + shoes = /obj/item/clothing/shoes/white + l_ear = /obj/item/radio/headset/headset_med + glasses = /obj/item/clothing/glasses/science + id = /obj/item/card/id/medical + pda = /obj/item/pda/chemist + + backpack = /obj/item/storage/backpack/chemistry + satchel = /obj/item/storage/backpack/satchel_chem + dufflebag = /obj/item/storage/backpack/duffel/chemistry + +/datum/job/geneticist + title = "Geneticist" + flag = JOB_GENETICIST + department_flag = JOBCAT_MEDSCI + total_positions = 2 + spawn_positions = 2 + is_medical = 1 + supervisors = "the chief medical officer and the research director" + department_head = list("Chief Medical Officer", "Research Director") + selection_color = "#ffeef0" + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS, ACCESS_RESEARCH, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_GENETICS, ACCESS_RESEARCH, ACCESS_MAINT_TUNNELS) + minimal_player_age = 3 + exp_requirements = 180 + exp_type = EXP_TYPE_CREW + outfit = /datum/outfit/job/geneticist + +/datum/outfit/job/geneticist + name = "Geneticist" + jobtype = /datum/job/geneticist + + uniform = /obj/item/clothing/under/rank/geneticist + suit = /obj/item/clothing/suit/storage/labcoat/genetics + shoes = /obj/item/clothing/shoes/white + l_ear = /obj/item/radio/headset/headset_medsci + id = /obj/item/card/id/medical + suit_store = /obj/item/flashlight/pen + pda = /obj/item/pda/geneticist + + backpack = /obj/item/storage/backpack/genetics + satchel = /obj/item/storage/backpack/satchel_gen + dufflebag = /obj/item/storage/backpack/duffel/genetics + + +/datum/job/virologist + title = "Virologist" + flag = JOB_VIROLOGIST + department_flag = JOBCAT_MEDSCI + total_positions = 1 + spawn_positions = 1 + is_medical = 1 + supervisors = "the chief medical officer" + department_head = list("Chief Medical Officer") + selection_color = "#ffeef0" + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS) + minimal_access = list(ACCESS_MEDICAL, ACCESS_VIROLOGY, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) + alt_titles = list("Pathologist","Microbiologist") + minimal_player_age = 7 + exp_requirements = 300 + exp_type = EXP_TYPE_CREW + outfit = /datum/outfit/job/virologist + +/datum/outfit/job/virologist + name = "Virologist" + jobtype = /datum/job/virologist + + uniform = /obj/item/clothing/under/rank/virologist + suit = /obj/item/clothing/suit/storage/labcoat/virologist + shoes = /obj/item/clothing/shoes/white + mask = /obj/item/clothing/mask/surgical + l_ear = /obj/item/radio/headset/headset_med + id = /obj/item/card/id/medical + suit_store = /obj/item/flashlight/pen + pda = /obj/item/pda/viro + + backpack = /obj/item/storage/backpack/virology + satchel = /obj/item/storage/backpack/satchel_vir + dufflebag = /obj/item/storage/backpack/duffel/virology + +/datum/job/psychiatrist + title = "Psychiatrist" + flag = JOB_PSYCHIATRIST + department_flag = JOBCAT_MEDSCI + total_positions = 1 + spawn_positions = 1 + is_medical = 1 + supervisors = "the chief medical officer" + department_head = list("Chief Medical Officer") + selection_color = "#ffeef0" + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS, ACCESS_PSYCHIATRIST) + minimal_access = list(ACCESS_MEDICAL, ACCESS_PSYCHIATRIST, ACCESS_MAINT_TUNNELS) + alt_titles = list("Psychologist","Therapist") + outfit = /datum/outfit/job/psychiatrist + +/datum/outfit/job/psychiatrist + name = "Psychiatrist" + jobtype = /datum/job/psychiatrist + + uniform = /obj/item/clothing/under/rank/medical + suit = /obj/item/clothing/suit/storage/labcoat + shoes = /obj/item/clothing/shoes/laceup + l_ear = /obj/item/radio/headset/headset_med + id = /obj/item/card/id/medical + suit_store = /obj/item/flashlight/pen + pda = /obj/item/pda/medical + +/datum/outfit/job/psychiatrist/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + . = ..() + if(H.mind && H.mind.role_alt_title) + switch(H.mind.role_alt_title) + if("Psychiatrist") + uniform = /obj/item/clothing/under/rank/psych + if("Psychologist") + uniform = /obj/item/clothing/under/rank/psych/turtleneck + if("Therapist") + uniform = /obj/item/clothing/under/rank/medical + +/datum/job/paramedic + title = "Paramedic" + flag = JOB_PARAMEDIC + department_flag = JOBCAT_MEDSCI + total_positions = 1 + spawn_positions = 1 + is_medical = 1 + supervisors = "the chief medical officer" + department_head = list("Chief Medical Officer") + selection_color = "#ffeef0" + access = list(ACCESS_PARAMEDIC, ACCESS_MEDICAL, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_MORGUE) + minimal_access=list(ACCESS_PARAMEDIC, ACCESS_MEDICAL, ACCESS_MAINT_TUNNELS, ACCESS_EXTERNAL_AIRLOCKS, ACCESS_MORGUE) + minimal_player_age = 3 + exp_requirements = 180 + exp_type = EXP_TYPE_CREW + outfit = /datum/outfit/job/paramedic + +/datum/outfit/job/paramedic + name = "Paramedic" + jobtype = /datum/job/paramedic + + uniform = /obj/item/clothing/under/rank/medical/paramedic + shoes = /obj/item/clothing/shoes/black + head = /obj/item/clothing/head/soft/blue + mask = /obj/item/clothing/mask/cigarette + l_ear = /obj/item/radio/headset/headset_med + id = /obj/item/card/id/medical + l_pocket = /obj/item/flashlight/pen + pda = /obj/item/pda/medical + backpack_contents = list( + /obj/item/healthanalyzer = 1 + ) + + backpack = /obj/item/storage/backpack/medic + satchel = /obj/item/storage/backpack/satchel_med + dufflebag = /obj/item/storage/backpack/duffel/medical + box = /obj/item/storage/box/engineer + diff --git a/code/game/jobs/job/science.dm b/code/game/jobs/job/science.dm index 435565434c8..684de933832 100644 --- a/code/game/jobs/job/science.dm +++ b/code/game/jobs/job/science.dm @@ -1,7 +1,7 @@ /datum/job/rd title = "Research Director" - flag = RD - department_flag = MEDSCI + flag = JOB_RD + department_flag = JOBCAT_MEDSCI total_positions = 1 spawn_positions = 1 is_science = 1 @@ -9,14 +9,14 @@ department_head = list("Captain") selection_color = "#ffddff" req_admin_notify = 1 - access = list(access_rd, access_heads, access_tox, access_genetics, access_morgue, - access_tox_storage, access_tech_storage, access_teleporter, access_sec_doors, - access_research, access_robotics, access_xenobiology, access_ai_upload, - access_RC_announce, access_keycard_auth, access_tcomsat, access_gateway, access_xenoarch, access_minisat, access_mineral_storeroom, access_network) - minimal_access = list(access_eva, access_rd, access_heads, access_tox, access_genetics, access_morgue, - access_tox_storage, access_tech_storage, access_teleporter, access_sec_doors, - access_research, access_robotics, access_xenobiology, access_ai_upload, - access_RC_announce, access_keycard_auth, access_tcomsat, access_gateway, access_xenoarch, access_minisat, access_maint_tunnels, access_mineral_storeroom, access_network) + access = list(ACCESS_RD, ACCESS_HEADS, ACCESS_TOX, ACCESS_GENETICS, ACCESS_MORGUE, + ACCESS_TOX_STORAGE, ACCESS_TECH_STORAGE, ACCESS_TELEPORTER, ACCESS_SEC_DOORS, + ACCESS_RESEARCH, ACCESS_ROBOTICS, ACCESS_XENOBIOLOGY, ACCESS_AI_UPLOAD, + ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_GATEWAY, ACCESS_XENOARCH, ACCESS_MINISAT, ACCESS_MINERAL_STOREROOM, ACCESS_NETWORK) + minimal_access = list(ACCESS_EVA, ACCESS_RD, ACCESS_HEADS, ACCESS_TOX, ACCESS_GENETICS, ACCESS_MORGUE, + ACCESS_TOX_STORAGE, ACCESS_TECH_STORAGE, ACCESS_TELEPORTER, ACCESS_SEC_DOORS, + ACCESS_RESEARCH, ACCESS_ROBOTICS, ACCESS_XENOBIOLOGY, ACCESS_AI_UPLOAD, + ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_TCOMSAT, ACCESS_GATEWAY, ACCESS_XENOARCH, ACCESS_MINISAT, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM, ACCESS_NETWORK) minimal_player_age = 21 exp_requirements = 300 exp_type = EXP_TYPE_SCIENCE @@ -49,16 +49,16 @@ /datum/job/scientist title = "Scientist" - flag = SCIENTIST - department_flag = MEDSCI + flag = JOB_SCIENTIST + department_flag = JOBCAT_MEDSCI total_positions = 6 spawn_positions = 6 is_science = 1 supervisors = "the research director" department_head = list("Research Director") selection_color = "#ffeeff" - access = list(access_robotics, access_tox, access_tox_storage, access_research, access_xenobiology, access_xenoarch, access_mineral_storeroom) - minimal_access = list(access_tox, access_tox_storage, access_research, access_xenobiology, access_xenoarch, access_maint_tunnels, access_mineral_storeroom) + access = list(ACCESS_ROBOTICS, ACCESS_TOX, ACCESS_TOX_STORAGE, ACCESS_RESEARCH, ACCESS_XENOBIOLOGY, ACCESS_XENOARCH, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_TOX, ACCESS_TOX_STORAGE, ACCESS_RESEARCH, ACCESS_XENOBIOLOGY, ACCESS_XENOARCH, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) alt_titles = list("Xenoarcheologist", "Anomalist", "Plasma Researcher", "Xenobiologist", "Chemical Researcher") minimal_player_age = 3 exp_requirements = 300 @@ -88,16 +88,16 @@ /datum/job/roboticist title = "Roboticist" - flag = ROBOTICIST - department_flag = MEDSCI + flag = JOB_ROBOTICIST + department_flag = JOBCAT_MEDSCI total_positions = 2 spawn_positions = 2 is_science = 1 supervisors = "the research director" department_head = list("Research Director") selection_color = "#ffeeff" - access = list(access_robotics, access_tox, access_tox_storage, access_tech_storage, access_morgue, access_research, access_mineral_storeroom) //As a job that handles so many corpses, it makes sense for them to have morgue access. - minimal_access = list(access_robotics, access_tech_storage, access_morgue, access_research, access_maint_tunnels, access_mineral_storeroom) //As a job that handles so many corpses, it makes sense for them to have morgue access. + access = list(ACCESS_ROBOTICS, ACCESS_TOX, ACCESS_TOX_STORAGE, ACCESS_TECH_STORAGE, ACCESS_MORGUE, ACCESS_RESEARCH, ACCESS_MINERAL_STOREROOM) //As a job that handles so many corpses, it makes sense for them to have morgue access. + minimal_access = list(ACCESS_ROBOTICS, ACCESS_TECH_STORAGE, ACCESS_MORGUE, ACCESS_RESEARCH, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) //As a job that handles so many corpses, it makes sense for them to have morgue access. alt_titles = list("Biomechanical Engineer","Mechatronic Engineer") minimal_player_age = 3 exp_requirements = 180 diff --git a/code/game/jobs/job/security.dm b/code/game/jobs/job/security.dm index 0dfd0876817..20364b57677 100644 --- a/code/game/jobs/job/security.dm +++ b/code/game/jobs/job/security.dm @@ -1,7 +1,7 @@ /datum/job/hos title = "Head of Security" - flag = HOS - department_flag = ENGSEC + flag = JOB_HOS + department_flag = JOBCAT_ENGSEC total_positions = 1 spawn_positions = 1 is_security = 1 @@ -9,14 +9,14 @@ department_head = list("Captain") selection_color = "#ffdddd" req_admin_notify = 1 - access = list(access_security, access_sec_doors, access_brig, access_armory, access_court, - access_forensics_lockers, access_pilot, access_morgue, access_maint_tunnels, access_all_personal_lockers, - access_research, access_engine, access_mining, access_medical, access_construction, access_mailsorting, - access_heads, access_hos, access_RC_announce, access_keycard_auth, access_gateway, access_weapons) - minimal_access = list(access_eva, access_security, access_sec_doors, access_brig, access_armory, access_court, - access_forensics_lockers, access_morgue, access_maint_tunnels, access_all_personal_lockers, - access_research, access_engine, access_mining, access_medical, access_construction, access_mailsorting, - access_heads, access_hos, access_RC_announce, access_keycard_auth, access_gateway, access_pilot, access_weapons) + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_COURT, + ACCESS_FORENSICS_LOCKERS, ACCESS_PILOT, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_ALL_PERSONAL_LOCKERS, + ACCESS_RESEARCH, ACCESS_ENGINE, ACCESS_MINING, ACCESS_MEDICAL, ACCESS_CONSTRUCTION, ACCESS_MAILSORTING, + ACCESS_HEADS, ACCESS_HOS, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_WEAPONS) + minimal_access = list(ACCESS_EVA, ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_COURT, + ACCESS_FORENSICS_LOCKERS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_ALL_PERSONAL_LOCKERS, + ACCESS_RESEARCH, ACCESS_ENGINE, ACCESS_MINING, ACCESS_MEDICAL, ACCESS_CONSTRUCTION, ACCESS_MAILSORTING, + ACCESS_HEADS, ACCESS_HOS, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_PILOT, ACCESS_WEAPONS) minimal_player_age = 21 exp_requirements = 300 exp_type = EXP_TYPE_SECURITY @@ -52,16 +52,16 @@ /datum/job/warden title = "Warden" - flag = WARDEN - department_flag = ENGSEC + flag = JOB_WARDEN + department_flag = JOBCAT_ENGSEC total_positions = 1 spawn_positions = 1 is_security = 1 supervisors = "the head of security" department_head = list("Head of Security") selection_color = "#ffeeee" - access = list(access_security, access_sec_doors, access_brig, access_armory, access_court, access_maint_tunnels, access_morgue, access_weapons) - minimal_access = list(access_security, access_sec_doors, access_brig, access_armory, access_court, access_maint_tunnels, access_weapons) + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_MORGUE, ACCESS_WEAPONS) + minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_ARMORY, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_WEAPONS) minimal_player_age = 21 exp_requirements = 600 exp_type = EXP_TYPE_CREW @@ -96,8 +96,8 @@ /datum/job/detective title = "Detective" - flag = DETECTIVE - department_flag = ENGSEC + flag = JOB_DETECTIVE + department_flag = JOBCAT_ENGSEC total_positions = 1 spawn_positions = 1 is_security = 1 @@ -105,8 +105,8 @@ department_head = list("Head of Security") selection_color = "#ffeeee" alt_titles = list("Forensic Technician") - access = list(access_security, access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels, access_court, access_weapons) - minimal_access = list(access_sec_doors, access_forensics_lockers, access_morgue, access_maint_tunnels, access_court, access_weapons) + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_FORENSICS_LOCKERS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_COURT, ACCESS_WEAPONS) + minimal_access = list(ACCESS_SEC_DOORS, ACCESS_FORENSICS_LOCKERS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS, ACCESS_COURT, ACCESS_WEAPONS) alt_titles = list("Forensic Technician") minimal_player_age = 14 exp_requirements = 600 @@ -155,16 +155,16 @@ /datum/job/officer title = "Security Officer" - flag = OFFICER - department_flag = ENGSEC + flag = JOB_OFFICER + department_flag = JOBCAT_ENGSEC total_positions = 7 spawn_positions = 7 is_security = 1 supervisors = "the head of security" department_head = list("Head of Security") selection_color = "#ffeeee" - access = list(access_security, access_sec_doors, access_brig, access_court, access_maint_tunnels, access_morgue, access_weapons) - minimal_access = list(access_security, access_sec_doors, access_brig, access_court, access_maint_tunnels, access_weapons) + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_MORGUE, ACCESS_WEAPONS) + minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_WEAPONS) minimal_player_age = 14 exp_requirements = 600 exp_type = EXP_TYPE_CREW @@ -195,16 +195,16 @@ /datum/job/brigdoc title = "Brig Physician" - flag = BRIGDOC - department_flag = KARMA + flag = JOB_BRIGDOC + department_flag = JOBCAT_KARMA total_positions = 1 spawn_positions = 1 is_security = 1 supervisors = "the head of security" department_head = list("Head of Security") selection_color = "#ffeeee" - access = list(access_medical, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics) - minimal_access = list(access_medical, access_morgue, access_surgery, access_security, access_sec_doors, access_brig, access_court, access_maint_tunnels) + access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_CHEMISTRY, ACCESS_VIROLOGY, ACCESS_GENETICS) + minimal_access = list(ACCESS_MEDICAL, ACCESS_MORGUE, ACCESS_SURGERY, ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_MAINT_TUNNELS) outfit = /datum/outfit/job/brigdoc /datum/outfit/job/brigdoc @@ -227,16 +227,16 @@ /datum/job/pilot title = "Security Pod Pilot" - flag = PILOT - department_flag = KARMA + flag = JOB_PILOT + department_flag = JOBCAT_KARMA total_positions = 1 spawn_positions = 1 is_security = 1 supervisors = "the head of security" department_head = list("Head of Security") selection_color = "#ffeeee" - access = list(access_security, access_sec_doors, access_brig, access_court, access_maint_tunnels, access_morgue, access_weapons, access_pilot, access_external_airlocks) - minimal_access = list(access_security, access_sec_doors, access_brig, access_court, access_maint_tunnels, access_weapons, access_pilot, access_external_airlocks) + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_MORGUE, ACCESS_WEAPONS, ACCESS_PILOT, ACCESS_EXTERNAL_AIRLOCKS) + minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_WEAPONS, ACCESS_PILOT, ACCESS_EXTERNAL_AIRLOCKS) minimal_player_age = 7 outfit = /datum/outfit/job/pilot @@ -259,4 +259,4 @@ backpack = /obj/item/storage/backpack/security satchel = /obj/item/storage/backpack/satchel_sec dufflebag = /obj/item/storage/backpack/duffel/security - box = /obj/item/storage/box/engineer \ No newline at end of file + box = /obj/item/storage/box/engineer diff --git a/code/game/jobs/job/silicon.dm b/code/game/jobs/job/silicon.dm index c239c1d3b3b..5325f077f4f 100644 --- a/code/game/jobs/job/silicon.dm +++ b/code/game/jobs/job/silicon.dm @@ -1,40 +1,40 @@ -/datum/job/ai - title = "AI" - flag = AI - department_flag = ENGSEC - total_positions = 0 // Not used for AI, see is_position_available below and modules/mob/living/silicon/ai/latejoin.dm - spawn_positions = 1 - selection_color = "#ccffcc" - supervisors = "your laws" - department_head = list("Captain") - req_admin_notify = 1 - minimal_player_age = 30 - exp_requirements = 300 - exp_type = EXP_TYPE_SILICON - -/datum/job/ai/equip(mob/living/carbon/human/H) - if(!H) - return 0 - -/datum/job/ai/is_position_available() - return (empty_playable_ai_cores.len != 0) - - -/datum/job/cyborg - title = "Cyborg" - flag = CYBORG - department_flag = ENGSEC - total_positions = 1 - spawn_positions = 1 - supervisors = "your laws and the AI" //Nodrak - department_head = list("AI") - selection_color = "#ddffdd" - minimal_player_age = 21 - exp_requirements = 300 - exp_type = EXP_TYPE_CREW - alt_titles = list("Android", "Robot") - -/datum/job/cyborg/equip(mob/living/carbon/human/H) - if(!H) - return 0 - return H.Robotize() \ No newline at end of file +/datum/job/ai + title = "AI" + flag = JOB_AI + department_flag = JOBCAT_ENGSEC + total_positions = 0 // Not used for AI, see is_position_available below and modules/mob/living/silicon/ai/latejoin.dm + spawn_positions = 1 + selection_color = "#ccffcc" + supervisors = "your laws" + department_head = list("Captain") + req_admin_notify = 1 + minimal_player_age = 30 + exp_requirements = 300 + exp_type = EXP_TYPE_SILICON + +/datum/job/ai/equip(mob/living/carbon/human/H) + if(!H) + return 0 + +/datum/job/ai/is_position_available() + return (empty_playable_ai_cores.len != 0) + + +/datum/job/cyborg + title = "Cyborg" + flag = JOB_CYBORG + department_flag = JOBCAT_ENGSEC + total_positions = 1 + spawn_positions = 1 + supervisors = "your laws and the AI" //Nodrak + department_head = list("AI") + selection_color = "#ddffdd" + minimal_player_age = 21 + exp_requirements = 300 + exp_type = EXP_TYPE_CREW + alt_titles = list("Android", "Robot") + +/datum/job/cyborg/equip(mob/living/carbon/human/H) + if(!H) + return 0 + return H.Robotize() diff --git a/code/game/jobs/job/supervisor.dm b/code/game/jobs/job/supervisor.dm index feaf1c7972d..4e773d42ad9 100644 --- a/code/game/jobs/job/supervisor.dm +++ b/code/game/jobs/job/supervisor.dm @@ -1,8 +1,8 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 0) /datum/job/captain title = "Captain" - flag = CAPTAIN - department_flag = ENGSEC + flag = JOB_CAPTAIN + department_flag = JOBCAT_ENGSEC total_positions = 1 spawn_positions = 1 supervisors = "Nanotrasen officials" @@ -57,8 +57,8 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 0) /datum/job/hop title = "Head of Personnel" - flag = HOP - department_flag = SUPPORT + flag = JOB_HOP + department_flag = JOBCAT_SUPPORT total_positions = 1 spawn_positions = 1 supervisors = "the captain" @@ -69,18 +69,18 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 0) minimal_player_age = 21 exp_requirements = 300 exp_type = EXP_TYPE_COMMAND - access = list(access_security, access_sec_doors, access_brig, access_court, access_forensics_lockers, - access_medical, access_engine, access_change_ids, access_ai_upload, access_eva, access_heads, - access_all_personal_lockers, access_maint_tunnels, access_bar, access_janitor, access_construction, access_morgue, - access_crematorium, access_kitchen, access_cargo, access_cargo_bot, access_mailsorting, access_qm, access_hydroponics, access_lawyer, - access_theatre, access_chapel_office, access_library, access_research, access_mining, access_heads_vault, access_mining_station, - access_clown, access_mime, access_hop, access_RC_announce, access_keycard_auth, access_gateway, access_weapons, access_mineral_storeroom) - minimal_access = list(access_security, access_sec_doors, access_brig, access_court, access_forensics_lockers, - access_medical, access_engine, access_change_ids, access_ai_upload, access_eva, access_heads, - access_all_personal_lockers, access_maint_tunnels, access_bar, access_janitor, access_construction, access_morgue, - access_crematorium, access_kitchen, access_cargo, access_cargo_bot, access_mailsorting, access_qm, access_hydroponics, access_lawyer, - access_theatre, access_chapel_office, access_library, access_research, access_mining, access_heads_vault, access_mining_station, - access_clown, access_mime, access_hop, access_RC_announce, access_keycard_auth, access_gateway, access_weapons, access_mineral_storeroom) + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_FORENSICS_LOCKERS, + ACCESS_MEDICAL, ACCESS_ENGINE, ACCESS_CHANGE_IDS, ACCESS_AI_UPLOAD, ACCESS_EVA, ACCESS_HEADS, + ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_MAINT_TUNNELS, ACCESS_BAR, ACCESS_JANITOR, ACCESS_CONSTRUCTION, ACCESS_MORGUE, + ACCESS_CREMATORIUM, ACCESS_KITCHEN, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_MAILSORTING, ACCESS_QM, ACCESS_HYDROPONICS, ACCESS_LAWYER, + ACCESS_THEATRE, ACCESS_CHAPEL_OFFICE, ACCESS_LIBRARY, ACCESS_RESEARCH, ACCESS_MINING, ACCESS_HEADS_VAULT, ACCESS_MINING_STATION, + ACCESS_CLOWN, ACCESS_MIME, ACCESS_HOP, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_FORENSICS_LOCKERS, + ACCESS_MEDICAL, ACCESS_ENGINE, ACCESS_CHANGE_IDS, ACCESS_AI_UPLOAD, ACCESS_EVA, ACCESS_HEADS, + ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_MAINT_TUNNELS, ACCESS_BAR, ACCESS_JANITOR, ACCESS_CONSTRUCTION, ACCESS_MORGUE, + ACCESS_CREMATORIUM, ACCESS_KITCHEN, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_MAILSORTING, ACCESS_QM, ACCESS_HYDROPONICS, ACCESS_LAWYER, + ACCESS_THEATRE, ACCESS_CHAPEL_OFFICE, ACCESS_LIBRARY, ACCESS_RESEARCH, ACCESS_MINING, ACCESS_HEADS_VAULT, ACCESS_MINING_STATION, + ACCESS_CLOWN, ACCESS_MIME, ACCESS_HOP, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM) outfit = /datum/outfit/job/hop /datum/outfit/job/hop @@ -103,8 +103,8 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 0) /datum/job/nanotrasenrep title = "Nanotrasen Representative" - flag = NANO - department_flag = KARMA + flag = JOB_NANO + department_flag = JOBCAT_KARMA total_positions = 1 spawn_positions = 1 supervisors = "the command staff" @@ -113,18 +113,18 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 0) req_admin_notify = 1 is_command = 1 minimal_player_age = 21 - access = list(access_security, access_sec_doors, access_brig, access_court, access_forensics_lockers, - access_medical, access_engine, access_change_ids, access_eva, access_heads, - access_all_personal_lockers, access_maint_tunnels, access_bar, access_janitor, access_construction, access_morgue, - access_crematorium, access_kitchen, access_cargo, access_cargo_bot, access_mailsorting, access_qm, access_hydroponics, access_lawyer, - access_theatre, access_chapel_office, access_library, access_research, access_mining, access_heads_vault, access_mining_station, - access_clown, access_mime, access_hop, access_RC_announce, access_keycard_auth, access_gateway, access_weapons, access_ntrep) - minimal_access = list(access_security, access_sec_doors, access_brig, access_court, access_forensics_lockers, - access_medical, access_engine, access_eva, access_heads, - access_all_personal_lockers, access_maint_tunnels, access_bar, access_janitor, access_construction, access_morgue, - access_crematorium, access_kitchen, access_cargo, access_cargo_bot, access_mailsorting, access_qm, access_hydroponics, access_lawyer, - access_theatre, access_chapel_office, access_library, access_research, access_mining, access_heads_vault, access_mining_station, - access_clown, access_mime, access_RC_announce, access_keycard_auth, access_gateway, access_weapons, access_ntrep) + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_FORENSICS_LOCKERS, + ACCESS_MEDICAL, ACCESS_ENGINE, ACCESS_CHANGE_IDS, ACCESS_EVA, ACCESS_HEADS, + ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_MAINT_TUNNELS, ACCESS_BAR, ACCESS_JANITOR, ACCESS_CONSTRUCTION, ACCESS_MORGUE, + ACCESS_CREMATORIUM, ACCESS_KITCHEN, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_MAILSORTING, ACCESS_QM, ACCESS_HYDROPONICS, ACCESS_LAWYER, + ACCESS_THEATRE, ACCESS_CHAPEL_OFFICE, ACCESS_LIBRARY, ACCESS_RESEARCH, ACCESS_MINING, ACCESS_HEADS_VAULT, ACCESS_MINING_STATION, + ACCESS_CLOWN, ACCESS_MIME, ACCESS_HOP, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_WEAPONS, ACCESS_NTREP) + minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_FORENSICS_LOCKERS, + ACCESS_MEDICAL, ACCESS_ENGINE, ACCESS_EVA, ACCESS_HEADS, + ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_MAINT_TUNNELS, ACCESS_BAR, ACCESS_JANITOR, ACCESS_CONSTRUCTION, ACCESS_MORGUE, + ACCESS_CREMATORIUM, ACCESS_KITCHEN, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_MAILSORTING, ACCESS_QM, ACCESS_HYDROPONICS, ACCESS_LAWYER, + ACCESS_THEATRE, ACCESS_CHAPEL_OFFICE, ACCESS_LIBRARY, ACCESS_RESEARCH, ACCESS_MINING, ACCESS_HEADS_VAULT, ACCESS_MINING_STATION, + ACCESS_CLOWN, ACCESS_MIME, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_WEAPONS, ACCESS_NTREP) outfit = /datum/outfit/job/nanotrasenrep /datum/outfit/job/nanotrasenrep @@ -146,8 +146,8 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 0) /datum/job/blueshield title = "Blueshield" - flag = BLUESHIELD - department_flag = KARMA + flag = JOB_BLUESHIELD + department_flag = JOBCAT_KARMA total_positions = 1 spawn_positions = 1 supervisors = "the Nanotrasen representative" @@ -156,14 +156,14 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 0) req_admin_notify = 1 is_command = 1 minimal_player_age = 21 - access = list(access_security, access_sec_doors, access_brig, access_court, access_forensics_lockers, - access_medical, access_engine, access_change_ids, access_eva, access_heads, - access_all_personal_lockers, access_maint_tunnels, access_bar, access_janitor, access_construction, access_morgue, - access_crematorium, access_kitchen, access_cargo, access_cargo_bot, access_mailsorting, access_qm, access_hydroponics, access_lawyer, - access_theatre, access_chapel_office, access_library, access_research, access_mining, access_heads_vault, access_mining_station, - access_clown, access_mime, access_hop, access_RC_announce, access_keycard_auth, access_gateway, access_weapons, access_blueshield) - minimal_access = list(access_forensics_lockers, access_sec_doors, access_medical, access_construction, access_engine, access_maint_tunnels, access_research, - access_RC_announce, access_keycard_auth, access_heads, access_blueshield, access_weapons) + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_FORENSICS_LOCKERS, + ACCESS_MEDICAL, ACCESS_ENGINE, ACCESS_CHANGE_IDS, ACCESS_EVA, ACCESS_HEADS, + ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_MAINT_TUNNELS, ACCESS_BAR, ACCESS_JANITOR, ACCESS_CONSTRUCTION, ACCESS_MORGUE, + ACCESS_CREMATORIUM, ACCESS_KITCHEN, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_MAILSORTING, ACCESS_QM, ACCESS_HYDROPONICS, ACCESS_LAWYER, + ACCESS_THEATRE, ACCESS_CHAPEL_OFFICE, ACCESS_LIBRARY, ACCESS_RESEARCH, ACCESS_MINING, ACCESS_HEADS_VAULT, ACCESS_MINING_STATION, + ACCESS_CLOWN, ACCESS_MIME, ACCESS_HOP, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_WEAPONS, ACCESS_BLUESHIELD) + minimal_access = list(ACCESS_FORENSICS_LOCKERS, ACCESS_SEC_DOORS, ACCESS_MEDICAL, ACCESS_CONSTRUCTION, ACCESS_ENGINE, ACCESS_MAINT_TUNNELS, ACCESS_RESEARCH, + ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_HEADS, ACCESS_BLUESHIELD, ACCESS_WEAPONS) outfit = /datum/outfit/job/blueshield /datum/outfit/job/blueshield @@ -182,15 +182,15 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 0) /obj/item/gun/energy/gun/blueshield = 1 ) implants = list(/obj/item/implant/mindshield) - backpack = /obj/item/storage/backpack/security - satchel = /obj/item/storage/backpack/satchel_sec - dufflebag = /obj/item/storage/backpack/duffel/security + backpack = /obj/item/storage/backpack/blueshield + satchel = /obj/item/storage/backpack/satchel_blueshield + dufflebag = /obj/item/storage/backpack/duffel/blueshield /datum/job/judge title = "Magistrate" - flag = JUDGE - department_flag = KARMA + flag = JOB_JUDGE + department_flag = JOBCAT_KARMA total_positions = 1 spawn_positions = 1 supervisors = "the Nanotrasen Supreme Court" @@ -199,13 +199,13 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 0) req_admin_notify = 1 is_legal = 1 minimal_player_age = 30 - access = list(access_security, access_sec_doors, access_brig, access_court, access_forensics_lockers, - access_medical, access_engine, access_change_ids, access_eva, access_heads, - access_all_personal_lockers, access_maint_tunnels, access_bar, access_janitor, access_construction, access_morgue, - access_crematorium, access_kitchen, access_cargo, access_cargo_bot, access_mailsorting, access_qm, access_hydroponics, access_lawyer, - access_theatre, access_chapel_office, access_library, access_research, access_mining, access_heads_vault, access_mining_station, - access_clown, access_mime, access_RC_announce, access_keycard_auth, access_gateway, access_magistrate) - minimal_access = list(access_security, access_sec_doors, access_brig, access_court, access_maint_tunnels, access_lawyer, access_magistrate, access_heads) + access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_FORENSICS_LOCKERS, + ACCESS_MEDICAL, ACCESS_ENGINE, ACCESS_CHANGE_IDS, ACCESS_EVA, ACCESS_HEADS, + ACCESS_ALL_PERSONAL_LOCKERS, ACCESS_MAINT_TUNNELS, ACCESS_BAR, ACCESS_JANITOR, ACCESS_CONSTRUCTION, ACCESS_MORGUE, + ACCESS_CREMATORIUM, ACCESS_KITCHEN, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_MAILSORTING, ACCESS_QM, ACCESS_HYDROPONICS, ACCESS_LAWYER, + ACCESS_THEATRE, ACCESS_CHAPEL_OFFICE, ACCESS_LIBRARY, ACCESS_RESEARCH, ACCESS_MINING, ACCESS_HEADS_VAULT, ACCESS_MINING_STATION, + ACCESS_CLOWN, ACCESS_MIME, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH, ACCESS_GATEWAY, ACCESS_MAGISTRATE) + minimal_access = list(ACCESS_SECURITY, ACCESS_SEC_DOORS, ACCESS_BRIG, ACCESS_COURT, ACCESS_MAINT_TUNNELS, ACCESS_LAWYER, ACCESS_MAGISTRATE, ACCESS_HEADS) outfit = /datum/outfit/job/judge /datum/outfit/job/judge @@ -233,16 +233,16 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 0) //var/global/lawyer = 0//Checks for another lawyer //This changed clothes on 2nd lawyer, both IA get the same dreds. /datum/job/lawyer title = "Internal Affairs Agent" - flag = LAWYER - department_flag = SUPPORT + flag = JOB_LAWYER + department_flag = JOBCAT_SUPPORT total_positions = 2 spawn_positions = 2 is_legal = 1 supervisors = "the magistrate" department_head = list("Captain") selection_color = "#ddddff" - access = list(access_lawyer, access_court, access_sec_doors, access_maint_tunnels, access_research, access_medical, access_construction, access_mailsorting) - minimal_access = list(access_lawyer, access_court, access_sec_doors, access_maint_tunnels, access_research, access_medical, access_construction, access_mailsorting) + access = list(ACCESS_LAWYER, ACCESS_COURT, ACCESS_SEC_DOORS, ACCESS_MAINT_TUNNELS, ACCESS_RESEARCH, ACCESS_MEDICAL, ACCESS_CONSTRUCTION, ACCESS_MAILSORTING) + minimal_access = list(ACCESS_LAWYER, ACCESS_COURT, ACCESS_SEC_DOORS, ACCESS_MAINT_TUNNELS, ACCESS_RESEARCH, ACCESS_MEDICAL, ACCESS_CONSTRUCTION, ACCESS_MAILSORTING) alt_titles = list("Human Resources Agent") minimal_player_age = 30 exp_requirements = 600 diff --git a/code/game/jobs/job/support.dm b/code/game/jobs/job/support.dm index e4bb22b28af..7d4f5ccc622 100644 --- a/code/game/jobs/job/support.dm +++ b/code/game/jobs/job/support.dm @@ -1,16 +1,16 @@ //Food /datum/job/bartender title = "Bartender" - flag = BARTENDER - department_flag = SUPPORT + flag = JOB_BARTENDER + department_flag = JOBCAT_SUPPORT total_positions = 1 spawn_positions = 1 is_service = 1 supervisors = "the head of personnel" department_head = list("Head of Personnel") selection_color = "#dddddd" - access = list(access_hydroponics, access_bar, access_kitchen, access_morgue, access_weapons, access_mineral_storeroom) - minimal_access = list(access_bar, access_maint_tunnels, access_weapons, access_mineral_storeroom) + access = list(ACCESS_HYDROPONICS, ACCESS_BAR, ACCESS_KITCHEN, ACCESS_MORGUE, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_BAR, ACCESS_MAINT_TUNNELS, ACCESS_WEAPONS, ACCESS_MINERAL_STOREROOM) outfit = /datum/outfit/job/bartender /datum/outfit/job/bartender @@ -42,16 +42,16 @@ /datum/job/chef title = "Chef" - flag = CHEF - department_flag = SUPPORT + flag = JOB_CHEF + department_flag = JOBCAT_SUPPORT total_positions = 1 spawn_positions = 1 is_service = 1 supervisors = "the head of personnel" department_head = list("Head of Personnel") selection_color = "#dddddd" - access = list(access_hydroponics, access_bar, access_kitchen, access_morgue) - minimal_access = list(access_kitchen, access_maint_tunnels) + access = list(ACCESS_HYDROPONICS, ACCESS_BAR, ACCESS_KITCHEN, ACCESS_MORGUE) + minimal_access = list(ACCESS_KITCHEN, ACCESS_MAINT_TUNNELS) alt_titles = list("Cook","Culinary Artist","Butcher") outfit = /datum/outfit/job/chef @@ -76,16 +76,16 @@ /datum/job/hydro title = "Botanist" - flag = BOTANIST - department_flag = SUPPORT + flag = JOB_BOTANIST + department_flag = JOBCAT_SUPPORT total_positions = 3 spawn_positions = 2 is_service = 1 supervisors = "the head of personnel" department_head = list("Head of Personnel") selection_color = "#dddddd" - access = list(access_hydroponics, access_bar, access_kitchen, access_morgue) - minimal_access = list(access_hydroponics, access_morgue, access_maint_tunnels) + access = list(ACCESS_HYDROPONICS, ACCESS_BAR, ACCESS_KITCHEN, ACCESS_MORGUE) + minimal_access = list(ACCESS_HYDROPONICS, ACCESS_MORGUE, ACCESS_MAINT_TUNNELS) alt_titles = list("Hydroponicist", "Botanical Researcher") outfit = /datum/outfit/job/hydro @@ -110,16 +110,16 @@ //Cargo /datum/job/qm title = "Quartermaster" - flag = QUARTERMASTER - department_flag = SUPPORT + flag = JOB_QUARTERMASTER + department_flag = JOBCAT_SUPPORT total_positions = 1 spawn_positions = 1 is_supply = 1 supervisors = "the head of personnel" department_head = list("Head of Personnel") selection_color = "#dddddd" - access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_qm, access_mint, access_mining, access_mining_station, access_mineral_storeroom) - minimal_access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_qm, access_mint, access_mining, access_mining_station, access_mineral_storeroom) + access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_QM, ACCESS_MINT, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_QM, ACCESS_MINT, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM) outfit = /datum/outfit/job/qm /datum/outfit/job/qm @@ -138,16 +138,16 @@ /datum/job/cargo_tech title = "Cargo Technician" - flag = CARGOTECH - department_flag = SUPPORT + flag = JOB_CARGOTECH + department_flag = JOBCAT_SUPPORT total_positions = 2 spawn_positions = 2 is_supply = 1 supervisors = "the quartermaster" department_head = list("Head of Personnel") selection_color = "#dddddd" - access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_qm, access_mint, access_mining, access_mining_station, access_mineral_storeroom) - minimal_access = list(access_maint_tunnels, access_cargo, access_cargo_bot, access_mailsorting, access_mineral_storeroom) + access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_QM, ACCESS_MINT, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MAINT_TUNNELS, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_MAILSORTING, ACCESS_MINERAL_STOREROOM) outfit = /datum/outfit/job/cargo_tech /datum/outfit/job/cargo_tech @@ -164,16 +164,16 @@ /datum/job/mining title = "Shaft Miner" - flag = MINER - department_flag = SUPPORT + flag = JOB_MINER + department_flag = JOBCAT_SUPPORT total_positions = 6 spawn_positions = 8 is_supply = 1 supervisors = "the quartermaster" department_head = list("Head of Personnel") selection_color = "#dddddd" - access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_qm, access_mint, access_mining, access_mining_station, access_mineral_storeroom) - minimal_access = list(access_mining, access_mint, access_mining_station, access_mailsorting, access_maint_tunnels, access_mineral_storeroom) + access = list(ACCESS_MAINT_TUNNELS, ACCESS_MAILSORTING, ACCESS_CARGO, ACCESS_CARGO_BOT, ACCESS_QM, ACCESS_MINT, ACCESS_MINING, ACCESS_MINING_STATION, ACCESS_MINERAL_STOREROOM) + minimal_access = list(ACCESS_MINING, ACCESS_MINT, ACCESS_MINING_STATION, ACCESS_MAILSORTING, ACCESS_MAINT_TUNNELS, ACCESS_MINERAL_STOREROOM) alt_titles = list("Spelunker") outfit = /datum/outfit/job/mining @@ -234,16 +234,16 @@ /datum/job/clown title = "Clown" - flag = CLOWN - department_flag = SUPPORT + flag = JOB_CLOWN + department_flag = JOBCAT_SUPPORT total_positions = 1 spawn_positions = 1 is_service = 1 supervisors = "the head of personnel" department_head = list("Head of Personnel") selection_color = "#dddddd" - access = list(access_clown, access_theatre, access_maint_tunnels) - minimal_access = list(access_clown, access_theatre, access_maint_tunnels) + access = list(ACCESS_CLOWN, ACCESS_THEATRE, ACCESS_MAINT_TUNNELS) + minimal_access = list(ACCESS_CLOWN, ACCESS_THEATRE, ACCESS_MAINT_TUNNELS) outfit = /datum/outfit/job/clown /datum/outfit/job/clown @@ -294,6 +294,7 @@ genemutcheck(H, COMICBLOCK, null, MUTCHK_FORCED) H.dna.default_blocks.Add(COMICBLOCK) H.check_mutations = TRUE + H.add_language("Clownish") //action given to antag clowns /datum/action/innate/toggle_clumsy @@ -318,16 +319,16 @@ /datum/job/mime title = "Mime" - flag = MIME - department_flag = SUPPORT + flag = JOB_MIME + department_flag = JOBCAT_SUPPORT total_positions = 1 spawn_positions = 1 is_service = 1 supervisors = "the head of personnel" department_head = list("Head of Personnel") selection_color = "#dddddd" - access = list(access_mime, access_theatre, access_maint_tunnels) - minimal_access = list(access_mime, access_theatre, access_maint_tunnels) + access = list(ACCESS_MIME, ACCESS_THEATRE, ACCESS_MAINT_TUNNELS) + minimal_access = list(ACCESS_MIME, ACCESS_THEATRE, ACCESS_MAINT_TUNNELS) outfit = /datum/outfit/job/mime /datum/outfit/job/mime @@ -370,16 +371,16 @@ /datum/job/janitor title = "Janitor" - flag = JANITOR - department_flag = SUPPORT + flag = JOB_JANITOR + department_flag = JOBCAT_SUPPORT total_positions = 1 spawn_positions = 1 is_service = 1 supervisors = "the head of personnel" department_head = list("Head of Personnel") selection_color = "#dddddd" - access = list(access_janitor, access_maint_tunnels) - minimal_access = list(access_janitor, access_maint_tunnels) + access = list(ACCESS_JANITOR, ACCESS_MAINT_TUNNELS) + minimal_access = list(ACCESS_JANITOR, ACCESS_MAINT_TUNNELS) alt_titles = list("Custodial Technician") outfit = /datum/outfit/job/janitor @@ -396,16 +397,16 @@ //More or less assistants /datum/job/librarian title = "Librarian" - flag = LIBRARIAN - department_flag = SUPPORT + flag = JOB_LIBRARIAN + department_flag = JOBCAT_SUPPORT total_positions = 1 spawn_positions = 1 is_service = 1 supervisors = "the head of personnel" department_head = list("Head of Personnel") selection_color = "#dddddd" - access = list(access_library, access_maint_tunnels) - minimal_access = list(access_library, access_maint_tunnels) + access = list(ACCESS_LIBRARY, ACCESS_MAINT_TUNNELS) + minimal_access = list(ACCESS_LIBRARY, ACCESS_MAINT_TUNNELS) alt_titles = list("Journalist") outfit = /datum/outfit/job/librarian @@ -420,11 +421,13 @@ r_pocket = /obj/item/barcodescanner l_hand = /obj/item/storage/bag/books pda = /obj/item/pda/librarian + backpack_contents = list( + /obj/item/videocam = 1) /datum/job/barber title = "Barber" - flag = BARBER - department_flag = KARMA + flag = JOB_BARBER + department_flag = JOBCAT_KARMA total_positions = 1 spawn_positions = 1 is_service = 1 @@ -432,8 +435,8 @@ department_head = list("Head of Personnel") selection_color = "#dddddd" alt_titles = list("Hair Stylist","Beautician") - access = list(access_maint_tunnels) - minimal_access = list(access_maint_tunnels) + access = list(ACCESS_MAINT_TUNNELS) + minimal_access = list(ACCESS_MAINT_TUNNELS) outfit = /datum/outfit/job/barber /datum/outfit/job/barber diff --git a/code/game/jobs/job/support_chaplain.dm b/code/game/jobs/job/support_chaplain.dm index 02e55a325b2..b39eed344ae 100644 --- a/code/game/jobs/job/support_chaplain.dm +++ b/code/game/jobs/job/support_chaplain.dm @@ -1,16 +1,16 @@ //Due to how large this one is it gets its own file /datum/job/chaplain title = "Chaplain" - flag = CHAPLAIN - department_flag = SUPPORT + flag = JOB_CHAPLAIN + department_flag = JOBCAT_SUPPORT total_positions = 1 spawn_positions = 1 is_service = 1 supervisors = "the head of personnel" department_head = list("Head of Personnel") selection_color = "#dddddd" - access = list(access_morgue, access_chapel_office, access_crematorium, access_maint_tunnels) - minimal_access = list(access_morgue, access_chapel_office, access_crematorium, access_maint_tunnels) + access = list(ACCESS_MORGUE, ACCESS_CHAPEL_OFFICE, ACCESS_CREMATORIUM, ACCESS_MAINT_TUNNELS) + minimal_access = list(ACCESS_MORGUE, ACCESS_CHAPEL_OFFICE, ACCESS_CREMATORIUM, ACCESS_MAINT_TUNNELS) outfit = /datum/outfit/job/chaplain diff --git a/code/game/jobs/job/syndicate.dm b/code/game/jobs/job/syndicate.dm index b5757ffe37c..f7c5659f86c 100644 --- a/code/game/jobs/job/syndicate.dm +++ b/code/game/jobs/job/syndicate.dm @@ -1,7 +1,7 @@ /datum/job/syndicateofficer title = "Syndicate Officer" - flag = SYNDICATE - department_flag = SYNDICATE + flag = JOB_SYNDICATE + department_flag = JOB_SYNDICATE // This gets its job as its own flag because admin jobs dont have flags total_positions = 5 spawn_positions = 5 supervisors = "the admins" @@ -55,4 +55,4 @@ opshud.join_hud(H.mind.current) H.mind.offstation_role = TRUE set_antag_hud(H.mind.current, "hudoperative") - H.regenerate_icons() \ No newline at end of file + H.regenerate_icons() diff --git a/code/game/jobs/job_exp.dm b/code/game/jobs/job_exp.dm index b69dbfd3ed4..7823b4b6f4a 100644 --- a/code/game/jobs/job_exp.dm +++ b/code/game/jobs/job_exp.dm @@ -301,4 +301,4 @@ var/global/list/role_playtime_requirements = list( var/err = update_query_history.ErrorMsg() log_game("SQL ERROR during exp_update_client write 2. Error : \[[err]\]\n") message_admins("SQL ERROR during exp_update_client write 2. Error : \[[err]\]\n") - return \ No newline at end of file + return diff --git a/code/game/jobs/job_objective.dm b/code/game/jobs/job_objective.dm index 48fa911bcf5..11fc2b7181d 100644 --- a/code/game/jobs/job_objective.dm +++ b/code/game/jobs/job_objective.dm @@ -73,4 +73,4 @@ else feedback_add_details("employee_success","FAIL") - return text \ No newline at end of file + return text diff --git a/code/game/jobs/job_objectives/science.dm b/code/game/jobs/job_objectives/science.dm index 0d5b4aebd83..70ff38a6a32 100644 --- a/code/game/jobs/job_objectives/science.dm +++ b/code/game/jobs/job_objectives/science.dm @@ -42,4 +42,4 @@ /datum/job_objective/make_ripley/get_description() var/desc = "Make a Ripley or Firefighter." desc += "([units_completed] created.)" - return desc \ No newline at end of file + return desc diff --git a/code/game/jobs/job_scaling.dm b/code/game/jobs/job_scaling.dm index 90d55b03444..c062fe6c216 100644 --- a/code/game/jobs/job_scaling.dm +++ b/code/game/jobs/job_scaling.dm @@ -8,4 +8,4 @@ SSjobs.LoadJobs("config/jobs_highpop.txt") else log_debug("Playercount: [playercount] versus trigger: [highpop_trigger] - keeping standard job config"); - return 1 \ No newline at end of file + return 1 diff --git a/code/game/jobs/jobs.dm b/code/game/jobs/jobs.dm index f45f8f5a22b..b0d70fb3003 100644 --- a/code/game/jobs/jobs.dm +++ b/code/game/jobs/jobs.dm @@ -1,206 +1,148 @@ - -var/const/ENGSEC =(1<<0) - -var/const/CAPTAIN =(1<<0) -var/const/HOS =(1<<1) -var/const/WARDEN =(1<<2) -var/const/DETECTIVE =(1<<3) -var/const/OFFICER =(1<<4) -var/const/CHIEF =(1<<5) -var/const/ENGINEER =(1<<6) -var/const/ATMOSTECH =(1<<7) -var/const/AI =(1<<8) -var/const/CYBORG =(1<<9) -var/const/CENTCOM =(1<<10) -var/const/SYNDICATE =(1<<11) - -var/const/MEDSCI =(1<<1) - -var/const/RD =(1<<0) -var/const/SCIENTIST =(1<<1) -var/const/CHEMIST =(1<<2) -var/const/CMO =(1<<3) -var/const/DOCTOR =(1<<4) -var/const/GENETICIST =(1<<5) -var/const/VIROLOGIST =(1<<6) -var/const/PSYCHIATRIST =(1<<7) -var/const/ROBOTICIST =(1<<8) -var/const/PARAMEDIC =(1<<9) -var/const/CORONER =(1<<10) - - -var/const/SUPPORT =(1<<2) - -var/const/HOP =(1<<0) -var/const/BARTENDER =(1<<1) -var/const/BOTANIST =(1<<2) -var/const/CHEF =(1<<3) -var/const/JANITOR =(1<<4) -var/const/LIBRARIAN =(1<<5) -var/const/QUARTERMASTER =(1<<6) -var/const/CARGOTECH =(1<<7) -var/const/MINER =(1<<8) -var/const/LAWYER =(1<<9) -var/const/CHAPLAIN =(1<<10) -var/const/CLOWN =(1<<11) -var/const/MIME =(1<<12) -var/const/CIVILIAN =(1<<13) - - -var/const/KARMA =(1<<3) - -var/const/NANO =(1<<0) -var/const/BLUESHIELD =(1<<1) -var/const/BARBER =(1<<3) -var/const/MECHANIC =(1<<4) -var/const/BRIGDOC =(1<<5) -var/const/JUDGE =(1<<6) -var/const/PILOT =(1<<7) - -var/list/assistant_occupations = list( -) - - -var/list/command_positions = list( - "Captain", - "Head of Personnel", - "Head of Security", - "Chief Engineer", - "Research Director", - "Chief Medical Officer", - "Nanotrasen Representative" -) - - -var/list/engineering_positions = list( - "Chief Engineer", - "Station Engineer", - "Life Support Specialist", - "Mechanic" -) - - -var/list/medical_positions = list( - "Chief Medical Officer", - "Medical Doctor", - "Geneticist", - "Psychiatrist", - "Chemist", - "Virologist", - "Paramedic", - "Coroner" -) - - -var/list/science_positions = list( - "Research Director", - "Scientist", - "Geneticist", //Part of both medical and science - "Roboticist", -) - -//BS12 EDIT -var/list/support_positions = list( - "Head of Personnel", - "Bartender", - "Botanist", - "Chef", - "Janitor", - "Librarian", - "Quartermaster", - "Cargo Technician", - "Shaft Miner", - "Internal Affairs Agent", - "Chaplain", - "Clown", - "Mime", - "Barber", - "Magistrate", - "Nanotrasen Representative", - "Blueshield" -) - -var/list/supply_positions = list( - "Head of Personnel", - "Quartermaster", - "Cargo Technician", - "Shaft Miner" -) - -var/list/service_positions = support_positions - supply_positions + list("Head of Personnel") - - -var/list/security_positions = list( - "Head of Security", - "Warden", - "Detective", - "Security Officer", - "Brig Physician", - "Security Pod Pilot", - "Magistrate" -) - - -var/list/civilian_positions = list( - "Civilian" -) - -var/list/nonhuman_positions = list( - "AI", - "Cyborg", - "Drone", - "pAI" -) - -var/list/whitelisted_positions = list( - "Blueshield", - "Nanotrasen Representative", - "Barber", - "Mechanic", - "Brig Physician", - "Magistrate", - "Security Pod Pilot", -) - - -/proc/guest_jobbans(var/job) - return (job in whitelisted_positions) - -/proc/get_job_datums() - var/list/occupations = list() - var/list/all_jobs = typesof(/datum/job) - - for(var/A in all_jobs) - var/datum/job/job = new A() - if(!job) continue - occupations += job - - return occupations - -/proc/get_alternate_titles(var/job) - var/list/jobs = get_job_datums() - var/list/titles = list() - - for(var/datum/job/J in jobs) - if(!J) continue - if(J.title == job) - titles = J.alt_titles - - return titles - -var/global/list/exp_jobsmap = list( - EXP_TYPE_LIVING = list(), // all living mobs - EXP_TYPE_CREW = list(titles = command_positions | engineering_positions | medical_positions | science_positions | support_positions | supply_positions | security_positions | civilian_positions | list("AI","Cyborg") | whitelisted_positions), // crew positions - EXP_TYPE_SPECIAL = list(), // antags, ERT, etc - EXP_TYPE_GHOST = list(), // dead people, observers - EXP_TYPE_EXEMPT = list(), // special grandfather setting - EXP_TYPE_COMMAND = list(titles = command_positions), - EXP_TYPE_ENGINEERING = list(titles = engineering_positions), - EXP_TYPE_MEDICAL = list(titles = medical_positions), - EXP_TYPE_SCIENCE = list(titles = science_positions), - EXP_TYPE_SUPPLY = list(titles = supply_positions), - EXP_TYPE_SECURITY = list(titles = security_positions), - EXP_TYPE_SILICON = list(titles = list("AI","Cyborg")), - EXP_TYPE_SERVICE = list(titles = service_positions), - EXP_TYPE_WHITELIST = list(titles = whitelisted_positions) // karma-locked jobs -) + +var/list/assistant_occupations = list( +) + + +var/list/command_positions = list( + "Captain", + "Head of Personnel", + "Head of Security", + "Chief Engineer", + "Research Director", + "Chief Medical Officer", + "Nanotrasen Representative" +) + + +var/list/engineering_positions = list( + "Chief Engineer", + "Station Engineer", + "Life Support Specialist", + "Mechanic" +) + + +var/list/medical_positions = list( + "Chief Medical Officer", + "Medical Doctor", + "Geneticist", + "Psychiatrist", + "Chemist", + "Virologist", + "Paramedic", + "Coroner" +) + + +var/list/science_positions = list( + "Research Director", + "Scientist", + "Geneticist", //Part of both medical and science + "Roboticist", +) + +//BS12 EDIT +var/list/support_positions = list( + "Head of Personnel", + "Bartender", + "Botanist", + "Chef", + "Janitor", + "Librarian", + "Quartermaster", + "Cargo Technician", + "Shaft Miner", + "Internal Affairs Agent", + "Chaplain", + "Clown", + "Mime", + "Barber", + "Magistrate", + "Nanotrasen Representative", + "Blueshield" +) + +var/list/supply_positions = list( + "Head of Personnel", + "Quartermaster", + "Cargo Technician", + "Shaft Miner" +) + +var/list/service_positions = support_positions - supply_positions + list("Head of Personnel") + + +var/list/security_positions = list( + "Head of Security", + "Warden", + "Detective", + "Security Officer", + "Brig Physician", + "Security Pod Pilot", + "Magistrate" +) + + +var/list/civilian_positions = list( + "Civilian" +) + +var/list/nonhuman_positions = list( + "AI", + "Cyborg", + "Drone", + "pAI" +) + +var/list/whitelisted_positions = list( + "Blueshield", + "Nanotrasen Representative", + "Barber", + "Mechanic", + "Brig Physician", + "Magistrate", + "Security Pod Pilot", +) + + +/proc/guest_jobbans(var/job) + return (job in whitelisted_positions) + +/proc/get_job_datums() + var/list/occupations = list() + var/list/all_jobs = typesof(/datum/job) + + for(var/A in all_jobs) + var/datum/job/job = new A() + if(!job) continue + occupations += job + + return occupations + +/proc/get_alternate_titles(var/job) + var/list/jobs = get_job_datums() + var/list/titles = list() + + for(var/datum/job/J in jobs) + if(!J) continue + if(J.title == job) + titles = J.alt_titles + + return titles + +var/global/list/exp_jobsmap = list( + EXP_TYPE_LIVING = list(), // all living mobs + EXP_TYPE_CREW = list(titles = command_positions | engineering_positions | medical_positions | science_positions | support_positions | supply_positions | security_positions | civilian_positions | list("AI","Cyborg") | whitelisted_positions), // crew positions + EXP_TYPE_SPECIAL = list(), // antags, ERT, etc + EXP_TYPE_GHOST = list(), // dead people, observers + EXP_TYPE_EXEMPT = list(), // special grandfather setting + EXP_TYPE_COMMAND = list(titles = command_positions), + EXP_TYPE_ENGINEERING = list(titles = engineering_positions), + EXP_TYPE_MEDICAL = list(titles = medical_positions), + EXP_TYPE_SCIENCE = list(titles = science_positions), + EXP_TYPE_SUPPLY = list(titles = supply_positions), + EXP_TYPE_SECURITY = list(titles = security_positions), + EXP_TYPE_SILICON = list(titles = list("AI","Cyborg")), + EXP_TYPE_SERVICE = list(titles = service_positions), + EXP_TYPE_WHITELIST = list(titles = whitelisted_positions) // karma-locked jobs +) diff --git a/code/game/jobs/whitelist.dm b/code/game/jobs/whitelist.dm index a09eaed6c02..503dd33258e 100644 --- a/code/game/jobs/whitelist.dm +++ b/code/game/jobs/whitelist.dm @@ -1,99 +1,99 @@ -#define WHITELISTFILE "data/whitelist.txt" - -var/list/whitelist = list() - -/hook/startup/proc/loadWhitelist() - if(config.usewhitelist) - load_whitelist() - return 1 - -/proc/load_whitelist() - whitelist = file2list(WHITELISTFILE) - if(!whitelist.len) whitelist = null -/* -/proc/check_whitelist(mob/M, var/rank) - if(!whitelist) - return 0 - return ("[M.ckey]" in whitelist) -*/ - -/proc/is_job_whitelisted(mob/M, var/rank) - if(guest_jobbans(rank)) - if(!config.usewhitelist) - return 1 - if(config.disable_karma) - return 1 - if(check_rights(R_ADMIN, 0, M)) - return 1 - if(!dbcon.IsConnected()) - to_chat(usr, "Unable to connect to whitelist database. Please try again later.
        ") - return 0 - else - var/DBQuery/query = dbcon.NewQuery("SELECT job FROM [format_table_name("whitelist")] WHERE ckey='[M.ckey]'") - query.Execute() - - - while(query.NextRow()) - var/joblist = query.item[1] - if(joblist!="*") - var/allowed_jobs = splittext(joblist,",") - if(rank in allowed_jobs) return 1 - else return 1 - return 0 - else - return 1 - - - - -/var/list/alien_whitelist = list() - -/hook/startup/proc/loadAlienWhitelist() - if(config.usealienwhitelist) - load_alienwhitelist() - return 1 - -/proc/load_alienwhitelist() - var/text = file2text("config/alienwhitelist.txt") - if(!text) - log_config("Failed to load config/alienwhitelist.txt\n") - else - alien_whitelist = splittext(text, "\n") - -//todo: admin aliens -/proc/is_alien_whitelisted(mob/M, var/species) - if(!config.usealienwhitelist) - return 1 - if(config.disable_karma) - return 1 - if(species == "human" || species == "Human") - return 1 - if(check_rights(R_ADMIN, 0)) - return 1 - if(!alien_whitelist) - return 0 - if(!dbcon.IsConnected()) - to_chat(usr, "Unable to connect to whitelist database. Please try again later.
        ") - return 0 - else - var/DBQuery/query = dbcon.NewQuery("SELECT species FROM [format_table_name("whitelist")] WHERE ckey='[M.ckey]'") - query.Execute() - - while(query.NextRow()) - var/specieslist = query.item[1] - if(specieslist!="*") - var/allowed_species = splittext(specieslist,",") - if(species in allowed_species) return 1 - else return 1 - return 0 -/* - if(M && species) - for(var/s in alien_whitelist) - if(findtext(s,"[M.ckey] - [species]")) - return 1 - if(findtext(s,"[M.ckey] - All")) - return 1 -*/ - - -#undef WHITELISTFILE \ No newline at end of file +#define WHITELISTFILE "data/whitelist.txt" + +var/list/whitelist = list() + +/hook/startup/proc/loadWhitelist() + if(config.usewhitelist) + load_whitelist() + return 1 + +/proc/load_whitelist() + whitelist = file2list(WHITELISTFILE) + if(!whitelist.len) whitelist = null +/* +/proc/check_whitelist(mob/M, var/rank) + if(!whitelist) + return 0 + return ("[M.ckey]" in whitelist) +*/ + +/proc/is_job_whitelisted(mob/M, var/rank) + if(guest_jobbans(rank)) + if(!config.usewhitelist) + return 1 + if(config.disable_karma) + return 1 + if(check_rights(R_ADMIN, 0, M)) + return 1 + if(!dbcon.IsConnected()) + to_chat(usr, "Unable to connect to whitelist database. Please try again later.
        ") + return 0 + else + var/DBQuery/query = dbcon.NewQuery("SELECT job FROM [format_table_name("whitelist")] WHERE ckey='[M.ckey]'") + query.Execute() + + + while(query.NextRow()) + var/joblist = query.item[1] + if(joblist!="*") + var/allowed_jobs = splittext(joblist,",") + if(rank in allowed_jobs) return 1 + else return 1 + return 0 + else + return 1 + + + + +/var/list/alien_whitelist = list() + +/hook/startup/proc/loadAlienWhitelist() + if(config.usealienwhitelist) + load_alienwhitelist() + return 1 + +/proc/load_alienwhitelist() + var/text = file2text("config/alienwhitelist.txt") + if(!text) + log_config("Failed to load config/alienwhitelist.txt\n") + else + alien_whitelist = splittext(text, "\n") + +//todo: admin aliens +/proc/is_alien_whitelisted(mob/M, var/species) + if(!config.usealienwhitelist) + return 1 + if(config.disable_karma) + return 1 + if(species == "human" || species == "Human") + return 1 + if(check_rights(R_ADMIN, 0)) + return 1 + if(!alien_whitelist) + return 0 + if(!dbcon.IsConnected()) + to_chat(usr, "Unable to connect to whitelist database. Please try again later.
        ") + return 0 + else + var/DBQuery/query = dbcon.NewQuery("SELECT species FROM [format_table_name("whitelist")] WHERE ckey='[M.ckey]'") + query.Execute() + + while(query.NextRow()) + var/specieslist = query.item[1] + if(specieslist!="*") + var/allowed_species = splittext(specieslist,",") + if(species in allowed_species) return 1 + else return 1 + return 0 +/* + if(M && species) + for(var/s in alien_whitelist) + if(findtext(s,"[M.ckey] - [species]")) + return 1 + if(findtext(s,"[M.ckey] - All")) + return 1 +*/ + + +#undef WHITELISTFILE diff --git a/code/game/machinery/Beacon.dm b/code/game/machinery/Beacon.dm index b5b2413fb96..d3b2114f801 100644 --- a/code/game/machinery/Beacon.dm +++ b/code/game/machinery/Beacon.dm @@ -86,4 +86,4 @@ /obj/machinery/bluespace_beacon/syndicate/infiltrator/New() ..() - enabled = FALSE \ No newline at end of file + enabled = FALSE diff --git a/code/game/machinery/Freezer.dm b/code/game/machinery/Freezer.dm index 91afce20b4d..097d2e26da2 100644 --- a/code/game/machinery/Freezer.dm +++ b/code/game/machinery/Freezer.dm @@ -1,325 +1,332 @@ -/obj/machinery/atmospherics/unary/cold_sink/freezer - name = "freezer" - icon = 'icons/obj/cryogenic2.dmi' - icon_state = "freezer" - density = 1 - var/min_temperature = 0 - anchored = 1.0 - use_power = IDLE_POWER_USE - current_heat_capacity = 1000 - layer = 3 - max_integrity = 300 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 30) - -/obj/machinery/atmospherics/unary/cold_sink/freezer/New() - ..() - initialize_directions = dir - component_parts = list() - component_parts += new /obj/item/circuitboard/thermomachine(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stock_parts/micro_laser(null) - component_parts += new /obj/item/stock_parts/micro_laser(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - RefreshParts() - -/obj/machinery/atmospherics/unary/cold_sink/freezer/upgraded/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/thermomachine(null) - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) - component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - RefreshParts() - -/obj/machinery/atmospherics/unary/cold_sink/freezer/RefreshParts() - var/H - var/T - for(var/obj/item/stock_parts/matter_bin/M in component_parts) - H += M.rating - for(var/obj/item/stock_parts/micro_laser/M in component_parts) - T += M.rating - min_temperature = max(0,T0C - (170 + (T*15))) - current_heat_capacity = 1000 * ((H - 1) ** 2) - -/obj/machinery/atmospherics/unary/cold_sink/freezer/on_construction() - ..(dir,dir) - -/obj/machinery/atmospherics/unary/cold_sink/freezer/attackby(obj/item/I, mob/user, params) - if(default_deconstruction_screwdriver(user, "freezer-o", "freezer", I)) - on = 0 - update_icon() - return - - if(exchange_parts(user, I)) - return - - if(default_deconstruction_crowbar(I)) - return - - if(iswrench(I)) - if(!panel_open) - to_chat(user, "Open the maintenance panel first.") - return - var/list/choices = list("West" = WEST, "East" = EAST, "South" = SOUTH, "North" = NORTH) - var/selected = input(user,"Select a direction for the connector.", "Connector Direction") in choices - dir = choices[selected] - playsound(src.loc, I.usesound, 50, 1) - var/node_connect = dir - initialize_directions = dir - for(var/obj/machinery/atmospherics/target in get_step(src,node_connect)) - if(target.initialize_directions & get_dir(target,src)) - node = target - break - build_network() - update_icon() - return - return ..() - -/obj/machinery/atmospherics/unary/cold_sink/freezer/update_icon() - if(panel_open) - icon_state = "freezer-o" - else if(src.on) - icon_state = "freezer_1" - else - icon_state = "freezer" - return - -/obj/machinery/atmospherics/unary/cold_sink/freezer/attack_ai(mob/user as mob) - attack_hand(user) - -/obj/machinery/atmospherics/unary/cold_sink/freezer/attack_ghost(mob/user as mob) - attack_hand(user) - -/obj/machinery/atmospherics/unary/cold_sink/freezer/attack_hand(mob/user as mob) - if(panel_open) - to_chat(user, "Close the maintenance panel first.") - return - - src.ui_interact(user) - -/obj/machinery/atmospherics/unary/cold_sink/freezer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "freezer.tmpl", "Gas Cooling System", 540, 300) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - -/obj/machinery/atmospherics/unary/cold_sink/freezer/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - data["on"] = on ? 1 : 0 - data["gasPressure"] = round(air_contents.return_pressure()) - data["gasTemperature"] = round(air_contents.temperature) - data["gasTemperatureCelsius"] = round(air_contents.temperature - T0C,1) - if(air_contents.total_moles() == 0 && air_contents.temperature == 0) - data["gasTemperatureCelsius"] = 0 - data["minGasTemperature"] = round(min_temperature) - data["maxGasTemperature"] = round(T20C) - data["targetGasTemperature"] = round(current_temperature) - data["targetGasTemperatureCelsius"] = round(current_temperature - T0C,1) - - var/temp_class = "good" - if(air_contents.temperature > (T0C - 20)) - temp_class = "bad" - else if(air_contents.temperature < (T0C - 20) && air_contents.temperature > (T0C - 100)) - temp_class = "average" - data["gasTemperatureClass"] = temp_class - return data - -/obj/machinery/atmospherics/unary/cold_sink/freezer/Topic(href, href_list) - if(..()) - return 1 - if(href_list["toggleStatus"]) - src.on = !src.on - update_icon() - else if(href_list["minimum"]) - current_temperature = min_temperature - else if(href_list["maximum"]) - current_temperature = T20C - else if(href_list["temp"]) - var/amount = text2num(href_list["temp"]) - if(amount > 0) - src.current_temperature = min(T20C, src.current_temperature+amount) - else - src.current_temperature = max(min_temperature, src.current_temperature+amount) - src.add_fingerprint(usr) - return 1 - -/obj/machinery/atmospherics/unary/cold_sink/freezer/power_change() - ..() - if(stat & NOPOWER) - on = 0 - update_icon() - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/ - name = "heater" - icon = 'icons/obj/cryogenic2.dmi' - icon_state = "heater" - density = 1 - var/max_temperature = 0 - anchored = 1.0 - layer = 3 - current_heat_capacity = 1000 - max_integrity = 300 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 30) - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/New() - ..() - initialize_directions = dir - var/obj/item/circuitboard/thermomachine/H = new /obj/item/circuitboard/thermomachine(null) - H.build_path = /obj/machinery/atmospherics/unary/heat_reservoir/heater - H.name = "circuit board (Heater)" - component_parts = list() - component_parts += H - component_parts += new /obj/item/stock_parts/matter_bin(src) - component_parts += new /obj/item/stock_parts/matter_bin(src) - component_parts += new /obj/item/stock_parts/micro_laser(src) - component_parts += new /obj/item/stock_parts/micro_laser(src) - component_parts += new /obj/item/stack/sheet/glass(src) - component_parts += new /obj/item/stack/cable_coil(src, 1) - RefreshParts() - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/upgraded/New() - ..() - var/obj/item/circuitboard/thermomachine/H = new /obj/item/circuitboard/thermomachine(null) - H.build_path = /obj/machinery/atmospherics/unary/heat_reservoir/heater - H.name = "circuit board (Heater)" - component_parts = list() - component_parts += H - component_parts += new /obj/item/stock_parts/matter_bin/super(src) - component_parts += new /obj/item/stock_parts/matter_bin/super(src) - component_parts += new /obj/item/stock_parts/micro_laser/ultra(src) - component_parts += new /obj/item/stock_parts/micro_laser/ultra(src) - component_parts += new /obj/item/stack/sheet/glass(src) - component_parts += new /obj/item/stack/cable_coil(src, 1) - RefreshParts() - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/on_construction() - ..(dir,dir) - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/RefreshParts() - var/H - var/T - for(var/obj/item/stock_parts/matter_bin/M in component_parts) - H += M.rating - for(var/obj/item/stock_parts/micro_laser/M in component_parts) - T += M.rating - max_temperature = T20C + (140 * T) - current_heat_capacity = 1000 * ((H - 1) ** 2) - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/attackby(obj/item/I, mob/user, params) - if(default_deconstruction_screwdriver(user, "heater-o", "heater", I)) - on = 0 - update_icon() - return - - if(exchange_parts(user, I)) - return - - if(default_deconstruction_crowbar(I)) - return - - if(iswrench(I)) - if(!panel_open) - to_chat(user, "Open the maintenance panel first.") - return - var/list/choices = list("West" = WEST, "East" = EAST, "South" = SOUTH, "North" = NORTH) - var/selected = input(user,"Select a direction for the connector.", "Connector Direction") in choices - dir = choices[selected] - playsound(src.loc, I.usesound, 50, 1) - var/node_connect = dir - initialize_directions = dir - for(var/obj/machinery/atmospherics/target in get_step(src,node_connect)) - if(target.initialize_directions & get_dir(target,src)) - node = target - break - build_network() - update_icon() - return - return ..() - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/update_icon() - if(panel_open) - icon_state = "heater-o" - else if(src.on) - icon_state = "heater_1" - else - icon_state = "heater" - return - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/attack_ai(mob/user as mob) - attack_hand(user) - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/attack_ghost(mob/user as mob) - src.attack_hand(user) - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/attack_hand(mob/user as mob) - if(panel_open) - to_chat(user, "Close the maintenance panel first.") - return - src.ui_interact(user) - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "freezer.tmpl", "Gas Heating System", 540, 300) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - data["on"] = on ? 1 : 0 - data["gasPressure"] = round(air_contents.return_pressure()) - data["gasTemperature"] = round(air_contents.temperature) - data["gasTemperatureCelsius"] = round(air_contents.temperature - T0C,1) - if(air_contents.total_moles() == 0 && air_contents.temperature == 0) - data["gasTemperatureCelsius"] = 0 - data["minGasTemperature"] = round(T20C) - data["maxGasTemperature"] = round(T20C+max_temperature) - data["targetGasTemperature"] = round(current_temperature) - data["targetGasTemperatureCelsius"] = round(current_temperature - T0C,1) - - var/temp_class = "normal" - if(air_contents.temperature > (T20C+40)) - temp_class = "bad" - data["gasTemperatureClass"] = temp_class - return data - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/Topic(href, href_list) - if(..()) - return 1 - if(href_list["toggleStatus"]) - src.on = !src.on - update_icon() - else if(href_list["minimum"]) - current_temperature = T20C - else if(href_list["maximum"]) - current_temperature = max_temperature + T20C - else if(href_list["temp"]) - var/amount = text2num(href_list["temp"]) - if(amount > 0) - src.current_temperature = min((T20C+max_temperature), src.current_temperature+amount) - else - src.current_temperature = max(T20C, src.current_temperature+amount) - src.add_fingerprint(usr) - return 1 - -/obj/machinery/atmospherics/unary/heat_reservoir/heater/power_change() - ..() - if(stat & NOPOWER) - on = 0 - update_icon() +/obj/machinery/atmospherics/unary/cold_sink/freezer + name = "freezer" + icon = 'icons/obj/cryogenic2.dmi' + icon_state = "freezer" + density = 1 + var/min_temperature = 0 + anchored = 1.0 + use_power = IDLE_POWER_USE + current_heat_capacity = 1000 + layer = 3 + plane = GAME_PLANE + max_integrity = 300 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 30) + +/obj/machinery/atmospherics/unary/cold_sink/freezer/New() + ..() + initialize_directions = dir + component_parts = list() + component_parts += new /obj/item/circuitboard/thermomachine(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/micro_laser(null) + component_parts += new /obj/item/stock_parts/micro_laser(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + RefreshParts() + +/obj/machinery/atmospherics/unary/cold_sink/freezer/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/thermomachine(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) + component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + RefreshParts() + +/obj/machinery/atmospherics/unary/cold_sink/freezer/RefreshParts() + var/H + var/T + for(var/obj/item/stock_parts/matter_bin/M in component_parts) + H += M.rating + for(var/obj/item/stock_parts/micro_laser/M in component_parts) + T += M.rating + min_temperature = max(0,T0C - (170 + (T*15))) + current_heat_capacity = 1000 * ((H - 1) ** 2) + +/obj/machinery/atmospherics/unary/cold_sink/freezer/on_construction() + ..(dir,dir) + +/obj/machinery/atmospherics/unary/cold_sink/freezer/attackby(obj/item/I, mob/user, params) + if(exchange_parts(user, I)) + return + return ..() + +/obj/machinery/atmospherics/unary/cold_sink/freezer/crowbar_act(mob/user, obj/item/I) + if(default_deconstruction_crowbar(user, I)) + return TRUE + +/obj/machinery/atmospherics/unary/cold_sink/freezer/screwdriver_act(mob/user, obj/item/I) + if(default_deconstruction_screwdriver(user, "freezer-o", "freezer", I)) + on = FALSE + update_icon() + return TRUE + +/obj/machinery/atmospherics/unary/cold_sink/freezer/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(!panel_open) + to_chat(user, "Open the maintenance panel first.") + return + var/list/choices = list("West" = WEST, "East" = EAST, "South" = SOUTH, "North" = NORTH) + var/selected = input(user,"Select a direction for the connector.", "Connector Direction") in choices + dir = choices[selected] + var/node_connect = dir + initialize_directions = dir + for(var/obj/machinery/atmospherics/target in get_step(src,node_connect)) + if(target.initialize_directions & get_dir(target,src)) + node = target + break + build_network() + update_icon() + +/obj/machinery/atmospherics/unary/cold_sink/freezer/update_icon() + if(panel_open) + icon_state = "freezer-o" + else if(src.on) + icon_state = "freezer_1" + else + icon_state = "freezer" + return + +/obj/machinery/atmospherics/unary/cold_sink/freezer/attack_ai(mob/user as mob) + attack_hand(user) + +/obj/machinery/atmospherics/unary/cold_sink/freezer/attack_ghost(mob/user as mob) + attack_hand(user) + +/obj/machinery/atmospherics/unary/cold_sink/freezer/attack_hand(mob/user as mob) + if(panel_open) + to_chat(user, "Close the maintenance panel first.") + return + + src.ui_interact(user) + +/obj/machinery/atmospherics/unary/cold_sink/freezer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "freezer.tmpl", "Gas Cooling System", 540, 300) + // open the new ui window + ui.open() + // auto update every Master Controller tick + ui.set_auto_update(1) + +/obj/machinery/atmospherics/unary/cold_sink/freezer/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) + var/data[0] + data["on"] = on ? 1 : 0 + data["gasPressure"] = round(air_contents.return_pressure()) + data["gasTemperature"] = round(air_contents.temperature) + data["gasTemperatureCelsius"] = round(air_contents.temperature - T0C,1) + if(air_contents.total_moles() == 0 && air_contents.temperature == 0) + data["gasTemperatureCelsius"] = 0 + data["minGasTemperature"] = round(min_temperature) + data["maxGasTemperature"] = round(T20C) + data["targetGasTemperature"] = round(current_temperature) + data["targetGasTemperatureCelsius"] = round(current_temperature - T0C,1) + + var/temp_class = "good" + if(air_contents.temperature > (T0C - 20)) + temp_class = "bad" + else if(air_contents.temperature < (T0C - 20) && air_contents.temperature > (T0C - 100)) + temp_class = "average" + data["gasTemperatureClass"] = temp_class + return data + +/obj/machinery/atmospherics/unary/cold_sink/freezer/Topic(href, href_list) + if(..()) + return 1 + if(href_list["toggleStatus"]) + src.on = !src.on + update_icon() + else if(href_list["minimum"]) + current_temperature = min_temperature + else if(href_list["maximum"]) + current_temperature = T20C + else if(href_list["temp"]) + var/amount = text2num(href_list["temp"]) + if(amount > 0) + src.current_temperature = min(T20C, src.current_temperature+amount) + else + src.current_temperature = max(min_temperature, src.current_temperature+amount) + src.add_fingerprint(usr) + return 1 + +/obj/machinery/atmospherics/unary/cold_sink/freezer/power_change() + ..() + if(stat & NOPOWER) + on = 0 + update_icon() + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/ + name = "heater" + icon = 'icons/obj/cryogenic2.dmi' + icon_state = "heater" + density = 1 + var/max_temperature = 0 + anchored = 1.0 + layer = 3 + current_heat_capacity = 1000 + max_integrity = 300 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 30) + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/New() + ..() + initialize_directions = dir + var/obj/item/circuitboard/thermomachine/H = new /obj/item/circuitboard/thermomachine(null) + H.build_path = /obj/machinery/atmospherics/unary/heat_reservoir/heater + H.name = "circuit board (Heater)" + component_parts = list() + component_parts += H + component_parts += new /obj/item/stock_parts/matter_bin(src) + component_parts += new /obj/item/stock_parts/matter_bin(src) + component_parts += new /obj/item/stock_parts/micro_laser(src) + component_parts += new /obj/item/stock_parts/micro_laser(src) + component_parts += new /obj/item/stack/sheet/glass(src) + component_parts += new /obj/item/stack/cable_coil(src, 1) + RefreshParts() + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/upgraded/New() + ..() + var/obj/item/circuitboard/thermomachine/H = new /obj/item/circuitboard/thermomachine(null) + H.build_path = /obj/machinery/atmospherics/unary/heat_reservoir/heater + H.name = "circuit board (Heater)" + component_parts = list() + component_parts += H + component_parts += new /obj/item/stock_parts/matter_bin/super(src) + component_parts += new /obj/item/stock_parts/matter_bin/super(src) + component_parts += new /obj/item/stock_parts/micro_laser/ultra(src) + component_parts += new /obj/item/stock_parts/micro_laser/ultra(src) + component_parts += new /obj/item/stack/sheet/glass(src) + component_parts += new /obj/item/stack/cable_coil(src, 1) + RefreshParts() + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/on_construction() + ..(dir,dir) + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/RefreshParts() + var/H + var/T + for(var/obj/item/stock_parts/matter_bin/M in component_parts) + H += M.rating + for(var/obj/item/stock_parts/micro_laser/M in component_parts) + T += M.rating + max_temperature = T20C + (140 * T) + current_heat_capacity = 1000 * ((H - 1) ** 2) + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/attackby(obj/item/I, mob/user, params) + if(exchange_parts(user, I)) + return + return ..() + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/crowbar_act(mob/user, obj/item/I) + if(default_deconstruction_crowbar(user, I)) + return TRUE + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/screwdriver_act(mob/user, obj/item/I) + if(default_deconstruction_screwdriver(user, "heater-o", "heater", I)) + on = 0 + update_icon() + return TRUE + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(!panel_open) + to_chat(user, "Open the maintenance panel first.") + return + var/list/choices = list("West" = WEST, "East" = EAST, "South" = SOUTH, "North" = NORTH) + var/selected = input(user,"Select a direction for the connector.", "Connector Direction") in choices + dir = choices[selected] + var/node_connect = dir + initialize_directions = dir + for(var/obj/machinery/atmospherics/target in get_step(src,node_connect)) + if(target.initialize_directions & get_dir(target,src)) + node = target + break + build_network() + update_icon() + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/update_icon() + if(panel_open) + icon_state = "heater-o" + else if(src.on) + icon_state = "heater_1" + else + icon_state = "heater" + return + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/attack_ai(mob/user as mob) + attack_hand(user) + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/attack_ghost(mob/user as mob) + src.attack_hand(user) + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/attack_hand(mob/user as mob) + if(panel_open) + to_chat(user, "Close the maintenance panel first.") + return + src.ui_interact(user) + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "freezer.tmpl", "Gas Heating System", 540, 300) + // open the new ui window + ui.open() + // auto update every Master Controller tick + ui.set_auto_update(1) + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) + var/data[0] + data["on"] = on ? 1 : 0 + data["gasPressure"] = round(air_contents.return_pressure()) + data["gasTemperature"] = round(air_contents.temperature) + data["gasTemperatureCelsius"] = round(air_contents.temperature - T0C,1) + if(air_contents.total_moles() == 0 && air_contents.temperature == 0) + data["gasTemperatureCelsius"] = 0 + data["minGasTemperature"] = round(T20C) + data["maxGasTemperature"] = round(T20C+max_temperature) + data["targetGasTemperature"] = round(current_temperature) + data["targetGasTemperatureCelsius"] = round(current_temperature - T0C,1) + + var/temp_class = "normal" + if(air_contents.temperature > (T20C+40)) + temp_class = "bad" + data["gasTemperatureClass"] = temp_class + return data + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/Topic(href, href_list) + if(..()) + return 1 + if(href_list["toggleStatus"]) + src.on = !src.on + update_icon() + else if(href_list["minimum"]) + current_temperature = T20C + else if(href_list["maximum"]) + current_temperature = max_temperature + T20C + else if(href_list["temp"]) + var/amount = text2num(href_list["temp"]) + if(amount > 0) + src.current_temperature = min((T20C+max_temperature), src.current_temperature+amount) + else + src.current_temperature = max(T20C, src.current_temperature+amount) + src.add_fingerprint(usr) + return 1 + +/obj/machinery/atmospherics/unary/heat_reservoir/heater/power_change() + ..() + if(stat & NOPOWER) + on = 0 + update_icon() diff --git a/code/game/machinery/OpTable.dm b/code/game/machinery/OpTable.dm index 2c6e60c3d52..7317099b8dc 100644 --- a/code/game/machinery/OpTable.dm +++ b/code/game/machinery/OpTable.dm @@ -1,152 +1,155 @@ -/obj/machinery/optable - name = "Operating Table" - desc = "Used for advanced medical procedures." - icon = 'icons/obj/surgery.dmi' - icon_state = "table2-idle" - density = 1 - anchored = 1.0 - use_power = IDLE_POWER_USE - idle_power_usage = 1 - active_power_usage = 5 - var/mob/living/carbon/human/victim = null - var/strapped = 0.0 - - var/obj/machinery/computer/operating/computer = null - buckle_lying = -1 - var/no_icon_updates = 0 //set this to 1 if you don't want the icons ever changing - var/list/injected_reagents = list() - var/reagent_target_amount = 1 - var/inject_amount = 1 - -/obj/machinery/optable/New() - ..() - for(dir in list(NORTH,EAST,SOUTH,WEST)) - computer = locate(/obj/machinery/computer/operating, get_step(src, dir)) - if(computer) - computer.table = src - break - -/obj/machinery/optable/Destroy() - if(computer) - computer.table = null - computer.victim = null - computer = null - if(victim) - victim = null - return ..() - -/obj/machinery/optable/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) - if(user.a_intent == INTENT_HARM) - ..(user, TRUE) - visible_message("[user] destroys the operating table!") - qdel(src) - return TRUE - -/obj/machinery/optable/CanPass(atom/movable/mover, turf/target, height=0) - if(height==0) return 1 - - if(istype(mover) && mover.checkpass(PASSTABLE)) - return 1 - else - return 0 - - -/obj/machinery/optable/MouseDrop_T(atom/movable/O as mob|obj, mob/user as mob) - if(usr.stat || (!ishuman(user) && !isrobot(user)) || user.restrained() || !check_table(user) || user.IsWeakened() || user.stunned) - return - - if(!ismob(O)) //humans only - return - - if(istype(O, /mob/living/simple_animal) || istype(O, /mob/living/silicon)) //animals and robots dont fit - return - - var/mob/living/L = O - take_victim(L,usr) - return - -/obj/machinery/optable/proc/check_victim() - if(locate(/mob/living/carbon/human, src.loc)) - var/mob/living/carbon/human/M = locate(/mob/living/carbon/human, src.loc) - if(M.lying) - src.victim = M - if(!no_icon_updates) - icon_state = M.pulse ? "table2-active" : "table2-idle" - return 1 - src.victim = null - if(!no_icon_updates) - icon_state = "table2-idle" - return 0 - -/obj/machinery/optable/Crossed(atom/movable/AM, oldloc) - . = ..() - if(iscarbon(AM) && LAZYLEN(injected_reagents)) - to_chat(AM, "You feel a series of tiny pricks!") - -/obj/machinery/optable/process() - check_victim() - if(LAZYLEN(injected_reagents)) - for(var/mob/living/carbon/C in get_turf(src)) - var/datum/reagents/R = C.reagents - for(var/chemical in injected_reagents) - R.check_and_add(chemical,reagent_target_amount,inject_amount) - -/obj/machinery/optable/proc/take_victim(mob/living/carbon/C, mob/living/carbon/user as mob) - if(C == user) - user.visible_message("[user] climbs on the operating table.","You climb on the operating table.") - else - visible_message("[C] has been laid on the operating table by [user].") - C.resting = 1 - C.update_canmove() - C.forceMove(loc) - if(user.pulling == C) - user.stop_pulling() - if(C.s_active) //Close the container opened - C.s_active.close(C) - for(var/obj/O in src) - O.loc = src.loc - src.add_fingerprint(user) - if(ishuman(C)) - var/mob/living/carbon/human/H = C - src.victim = H - if(!no_icon_updates) - icon_state = H.pulse ? "table2-active" : "table2-idle" - else - if(!no_icon_updates) - icon_state = "table2-idle" - -/obj/machinery/optable/verb/climb_on() - set name = "Climb On Table" - set category = "Object" - set src in oview(1) - - if(usr.stat || !ishuman(usr) || usr.restrained() || !check_table(usr)) - return - - take_victim(usr,usr) - -/obj/machinery/optable/attackby(obj/item/I, mob/living/carbon/user, params) - if(istype(I, /obj/item/grab)) - var/obj/item/grab/G = I - if(iscarbon(G.affecting)) - take_victim(G.affecting, user) - qdel(G) - if(iswrench(I)) - playsound(loc, I.usesound, 50, 1) - if(do_after(user, 20 * I.toolspeed, target = src)) - to_chat(user, "You deconstruct the table.") - new /obj/item/stack/sheet/plasteel(loc, 5) - qdel(src) - else - return ..() - -/obj/machinery/optable/proc/check_table(mob/living/carbon/patient as mob) - if(src.victim && get_turf(victim) == get_turf(src) && victim.lying) - to_chat(usr, "The table is already occupied!") - return 0 - - if(patient.buckled) - to_chat(usr, "Unbuckle first!") - return 0 - - return 1 +/obj/machinery/optable + name = "Operating Table" + desc = "Used for advanced medical procedures." + icon = 'icons/obj/surgery.dmi' + icon_state = "table2-idle" + density = 1 + anchored = 1.0 + use_power = IDLE_POWER_USE + idle_power_usage = 1 + active_power_usage = 5 + var/mob/living/carbon/human/victim = null + var/strapped = 0.0 + + var/obj/machinery/computer/operating/computer = null + buckle_lying = -1 + var/no_icon_updates = 0 //set this to 1 if you don't want the icons ever changing + var/list/injected_reagents = list() + var/reagent_target_amount = 1 + var/inject_amount = 1 + +/obj/machinery/optable/New() + ..() + for(dir in list(NORTH,EAST,SOUTH,WEST)) + computer = locate(/obj/machinery/computer/operating, get_step(src, dir)) + if(computer) + computer.table = src + break + +/obj/machinery/optable/Destroy() + if(computer) + computer.table = null + computer.victim = null + computer = null + if(victim) + victim = null + return ..() + +/obj/machinery/optable/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE) + if(user.a_intent == INTENT_HARM) + ..(user, TRUE) + visible_message("[user] destroys the operating table!") + qdel(src) + return TRUE + +/obj/machinery/optable/CanPass(atom/movable/mover, turf/target, height=0) + if(height==0) return 1 + + if(istype(mover) && mover.checkpass(PASSTABLE)) + return 1 + else + return 0 + + +/obj/machinery/optable/MouseDrop_T(atom/movable/O as mob|obj, mob/user as mob) + if(usr.stat || (!ishuman(user) && !isrobot(user)) || user.restrained() || !check_table(user) || user.IsWeakened() || user.stunned) + return + + if(!ismob(O)) //humans only + return + + if(istype(O, /mob/living/simple_animal) || istype(O, /mob/living/silicon)) //animals and robots dont fit + return + + var/mob/living/L = O + take_victim(L,usr) + return + +/obj/machinery/optable/proc/check_victim() + if(locate(/mob/living/carbon/human, src.loc)) + var/mob/living/carbon/human/M = locate(/mob/living/carbon/human, src.loc) + if(M.lying) + src.victim = M + if(!no_icon_updates) + icon_state = M.pulse ? "table2-active" : "table2-idle" + return 1 + src.victim = null + if(!no_icon_updates) + icon_state = "table2-idle" + return 0 + +/obj/machinery/optable/Crossed(atom/movable/AM, oldloc) + . = ..() + if(iscarbon(AM) && LAZYLEN(injected_reagents)) + to_chat(AM, "You feel a series of tiny pricks!") + +/obj/machinery/optable/process() + check_victim() + if(LAZYLEN(injected_reagents)) + for(var/mob/living/carbon/C in get_turf(src)) + var/datum/reagents/R = C.reagents + for(var/chemical in injected_reagents) + R.check_and_add(chemical,reagent_target_amount,inject_amount) + +/obj/machinery/optable/proc/take_victim(mob/living/carbon/C, mob/living/carbon/user as mob) + if(C == user) + user.visible_message("[user] climbs on the operating table.","You climb on the operating table.") + else + visible_message("[C] has been laid on the operating table by [user].") + C.resting = 1 + C.update_canmove() + C.forceMove(loc) + if(user.pulling == C) + user.stop_pulling() + if(C.s_active) //Close the container opened + C.s_active.close(C) + for(var/obj/O in src) + O.loc = src.loc + src.add_fingerprint(user) + if(ishuman(C)) + var/mob/living/carbon/human/H = C + src.victim = H + if(!no_icon_updates) + icon_state = H.pulse ? "table2-active" : "table2-idle" + else + if(!no_icon_updates) + icon_state = "table2-idle" + +/obj/machinery/optable/verb/climb_on() + set name = "Climb On Table" + set category = "Object" + set src in oview(1) + + if(usr.stat || !ishuman(usr) || usr.restrained() || !check_table(usr)) + return + + take_victim(usr,usr) + +/obj/machinery/optable/attackby(obj/item/I, mob/living/carbon/user, params) + if(istype(I, /obj/item/grab)) + var/obj/item/grab/G = I + if(iscarbon(G.affecting)) + take_victim(G.affecting, user) + qdel(G) + else + return ..() + +/obj/machinery/optable/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_start_check(user, 0)) + return + if(I.use_tool(src, user, 20, volume = I.tool_volume)) + to_chat(user, "You deconstruct the table.") + new /obj/item/stack/sheet/plasteel(loc, 5) + qdel(src) + +/obj/machinery/optable/proc/check_table(mob/living/carbon/patient as mob) + if(src.victim && get_turf(victim) == get_turf(src) && victim.lying) + to_chat(usr, "The table is already occupied!") + return 0 + + if(patient.buckled) + to_chat(usr, "Unbuckle first!") + return 0 + + return 1 diff --git a/code/game/machinery/PDApainter.dm b/code/game/machinery/PDApainter.dm index 817d56cb47a..3185cdcddd4 100644 --- a/code/game/machinery/PDApainter.dm +++ b/code/game/machinery/PDApainter.dm @@ -1,144 +1,132 @@ -/obj/machinery/pdapainter - name = "PDA painter" - desc = "A PDA painting machine. To use, simply insert your PDA and choose the desired preset paint scheme." - icon = 'icons/obj/pda.dmi' - icon_state = "pdapainter" - density = 1 - anchored = 1 - max_integrity = 200 - var/obj/item/pda/storedpda = null - var/list/colorlist = list() - - -/obj/machinery/pdapainter/update_icon() - cut_overlays() - - if(stat & BROKEN) - icon_state = "[initial(icon_state)]-broken" - return - - if(storedpda) - add_overlay("[initial(icon_state)]-closed") - - if(powered()) - icon_state = initial(icon_state) - else - icon_state = "[initial(icon_state)]-off" - - return - -/obj/machinery/pdapainter/New() - ..() - var/blocked = list(/obj/item/pda/silicon/ai, /obj/item/pda/silicon/robot, /obj/item/pda/silicon/pai, /obj/item/pda/heads, - /obj/item/pda/clear, /obj/item/pda/syndicate) - - for(var/P in typesof(/obj/item/pda)-blocked) - var/obj/item/pda/D = new P - - //D.name = "PDA Style [colorlist.len+1]" //Gotta set the name, otherwise it all comes up as "PDA" - D.name = D.icon_state //PDAs don't have unique names, but using the sprite names works. - - src.colorlist += D - -/obj/machinery/pdapainter/Destroy() - QDEL_NULL(storedpda) - return ..() - -/obj/machinery/pdapainter/on_deconstruction() - if(storedpda) - storedpda.forceMove(loc) - storedpda = null - -/obj/machinery/pdapainter/ex_act(severity) - if(storedpda) - storedpda.ex_act(severity) - ..() - -/obj/machinery/pdapainter/handle_atom_del(atom/A) - if(A == storedpda) - storedpda = null - update_icon() - -/obj/machinery/pdapainter/attackby(obj/item/I, mob/user, params) - if(default_unfasten_wrench(user, I)) - power_change() - return - if(istype(I, /obj/item/pda)) - if(storedpda) - to_chat(user, "There is already a PDA inside.") - return - else - var/obj/item/pda/P = user.get_active_hand() - if(istype(P)) - if(user.drop_item()) - storedpda = P - P.forceMove(src) - P.add_fingerprint(user) - update_icon() - else if(iswelder(I) && user.a_intent != INTENT_HARM) - var/obj/item/weldingtool/WT = I - if(stat & BROKEN) - if(WT.remove_fuel(0,user)) - user.visible_message("[user] is repairing [src].", \ - "You begin repairing [src]...", \ - "You hear welding.") - playsound(loc, WT.usesound, 40, 1) - if(do_after(user,40*WT.toolspeed, 1, target = src)) - if(!WT.isOn() || !(stat & BROKEN)) - return - to_chat(user, "You repair [src].") - playsound(loc, 'sound/items/welder2.ogg', 50, 1) - stat &= ~BROKEN - obj_integrity = max_integrity - update_icon() - else - to_chat(user, "[src] does not need repairs.") - else - return ..() - -/obj/machinery/pdapainter/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - if(!(stat & BROKEN)) - stat |= BROKEN - update_icon() - -/obj/machinery/pdapainter/attack_hand(mob/user as mob) - if(..()) - return 1 - - src.add_fingerprint(user) - - if(storedpda) - var/obj/item/pda/P - P = input(user, "Select your color!", "PDA Painting") as null|anything in colorlist - if(!P) - return - if(!in_range(src, user)) - return - - storedpda.icon_state = P.icon_state - storedpda.desc = P.desc - - else - to_chat(user, "The [src] is empty.") - - -/obj/machinery/pdapainter/verb/ejectpda() - set name = "Eject PDA" - set category = "Object" - set src in oview(1) - - if(usr.incapacitated()) - return - - if(storedpda) - storedpda.loc = get_turf(src.loc) - storedpda = null - update_icon() - else - to_chat(usr, "The [src] is empty.") - - -/obj/machinery/pdapainter/power_change() - ..() - update_icon() \ No newline at end of file +/obj/machinery/pdapainter + name = "PDA painter" + desc = "A PDA painting machine. To use, simply insert your PDA and choose the desired preset paint scheme." + icon = 'icons/obj/pda.dmi' + icon_state = "pdapainter" + density = 1 + anchored = 1 + max_integrity = 200 + var/obj/item/pda/storedpda = null + var/list/colorlist = list() + + +/obj/machinery/pdapainter/update_icon() + cut_overlays() + + if(stat & BROKEN) + icon_state = "[initial(icon_state)]-broken" + return + + if(storedpda) + add_overlay("[initial(icon_state)]-closed") + + if(powered()) + icon_state = initial(icon_state) + else + icon_state = "[initial(icon_state)]-off" + + return + +/obj/machinery/pdapainter/New() + ..() + var/blocked = list(/obj/item/pda/silicon/ai, /obj/item/pda/silicon/robot, /obj/item/pda/silicon/pai, /obj/item/pda/heads, + /obj/item/pda/clear, /obj/item/pda/syndicate) + + for(var/P in typesof(/obj/item/pda)-blocked) + var/obj/item/pda/D = new P + + //D.name = "PDA Style [colorlist.len+1]" //Gotta set the name, otherwise it all comes up as "PDA" + D.name = D.icon_state //PDAs don't have unique names, but using the sprite names works. + + src.colorlist += D + +/obj/machinery/pdapainter/Destroy() + QDEL_NULL(storedpda) + return ..() + +/obj/machinery/pdapainter/on_deconstruction() + if(storedpda) + storedpda.forceMove(loc) + storedpda = null + +/obj/machinery/pdapainter/ex_act(severity) + if(storedpda) + storedpda.ex_act(severity) + ..() + +/obj/machinery/pdapainter/handle_atom_del(atom/A) + if(A == storedpda) + storedpda = null + update_icon() + +/obj/machinery/pdapainter/attackby(obj/item/I, mob/user, params) + if(default_unfasten_wrench(user, I)) + power_change() + return + if(istype(I, /obj/item/pda)) + if(storedpda) + to_chat(user, "There is already a PDA inside.") + return + else + var/obj/item/pda/P = user.get_active_hand() + if(istype(P)) + if(user.drop_item()) + storedpda = P + P.forceMove(src) + P.add_fingerprint(user) + update_icon() + else + return ..() + +/obj/machinery/pdapainter/welder_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + default_welder_repair(user, I) + +/obj/machinery/pdapainter/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + if(!(stat & BROKEN)) + stat |= BROKEN + update_icon() + +/obj/machinery/pdapainter/attack_hand(mob/user as mob) + if(..()) + return 1 + + src.add_fingerprint(user) + + if(storedpda) + var/obj/item/pda/P + P = input(user, "Select your color!", "PDA Painting") as null|anything in colorlist + if(!P) + return + if(!in_range(src, user)) + return + + storedpda.icon_state = P.icon_state + storedpda.desc = P.desc + + else + to_chat(user, "The [src] is empty.") + + +/obj/machinery/pdapainter/verb/ejectpda() + set name = "Eject PDA" + set category = "Object" + set src in oview(1) + + if(usr.incapacitated()) + return + + if(storedpda) + storedpda.loc = get_turf(src.loc) + storedpda = null + update_icon() + else + to_chat(usr, "The [src] is empty.") + + +/obj/machinery/pdapainter/power_change() + ..() + update_icon() diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index fbe594f2269..81409b5de86 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -1,554 +1,556 @@ -#define ADDICTION_SPEEDUP_TIME 1500 // 2.5 minutes - -///////////// -// SLEEPER // -//////////// - -/obj/machinery/sleeper - name = "Sleeper" - icon = 'icons/obj/cryogenic2.dmi' - icon_state = "sleeper-open" - var/base_icon = "sleeper" - density = 1 - anchored = 1 - dir = WEST - var/orient = "LEFT" // "RIGHT" changes the dir suffix to "-r" - var/mob/living/carbon/human/occupant = null - var/possible_chems = list("ephedrine", "salglu_solution", "salbutamol", "charcoal") - var/emergency_chems = list("ephedrine") // Desnowflaking - var/amounts = list(5, 10) - var/obj/item/reagent_containers/glass/beaker = null - var/filtering = 0 - var/max_chem - var/initial_bin_rating = 1 - var/min_health = -25 - var/controls_inside = FALSE - idle_power_usage = 1250 - active_power_usage = 2500 - - light_color = LIGHT_COLOR_CYAN - -/obj/machinery/sleeper/power_change() - ..() - if(!(stat & (BROKEN|NOPOWER))) - set_light(2) - else - set_light(0) - -/obj/machinery/sleeper/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/sleeper(null) - - // Customizable bin rating, used by the labor camp to stop people filling themselves with chemicals and escaping. - var/obj/item/stock_parts/matter_bin/B = new(null) - B.rating = initial_bin_rating - component_parts += B - - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - RefreshParts() - -/obj/machinery/sleeper/upgraded/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/sleeper(null) - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/manipulator/pico(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - RefreshParts() - -/obj/machinery/sleeper/RefreshParts() - var/E - for(var/obj/item/stock_parts/matter_bin/B in component_parts) - E += B.rating - - max_chem = E * 20 - min_health = -E * 25 - -/obj/machinery/sleeper/Destroy() - for(var/mob/M in contents) - M.forceMove(get_turf(src)) - return ..() - -/obj/machinery/sleeper/relaymove(mob/user as mob) - if(user.incapacitated()) - return 0 //maybe they should be able to get out with cuffs, but whatever - go_out() - -/obj/machinery/sleeper/process() - if(filtering > 0) - if(beaker) - // To prevent runtimes from drawing blood from runtime, and to prevent getting IPC blood. - if(!istype(occupant) || !occupant.dna || (NO_BLOOD in occupant.dna.species.species_traits)) - filtering = 0 - return - - if(beaker.reagents.total_volume < beaker.reagents.maximum_volume) - occupant.transfer_blood_to(beaker, 1) - for(var/datum/reagent/x in occupant.reagents.reagent_list) - occupant.reagents.trans_to(beaker, 3) - occupant.transfer_blood_to(beaker, 1) - - if(occupant) - for(var/A in occupant.reagents.addiction_list) - var/datum/reagent/R = A - - var/addiction_removal_chance = 5 - if(world.timeofday > (R.last_addiction_dose + ADDICTION_SPEEDUP_TIME)) // 2.5 minutes - addiction_removal_chance = 10 - if(prob(addiction_removal_chance)) - to_chat(occupant, "You no longer feel reliant on [R.name]!") - occupant.reagents.addiction_list.Remove(R) - qdel(R) - - for(var/mob/M as mob in src) // makes sure that simple mobs don't get stuck inside a sleeper when they resist out of occupant's grasp - if(M == occupant) - continue - else - M.forceMove(loc) - - updateDialog() - return - - -/obj/machinery/sleeper/attack_ai(mob/user) - return attack_hand(user) - -/obj/machinery/sleeper/attack_ghost(mob/user) - return attack_hand(user) - -/obj/machinery/sleeper/attack_hand(mob/user) - if(stat & (NOPOWER|BROKEN)) - return - - if(panel_open) - to_chat(user, "Close the maintenance panel first.") - return - - ui_interact(user) - -/obj/machinery/sleeper/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "sleeper.tmpl", "Sleeper", 550, 770) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/sleeper/ui_data(mob/user, datum/topic_state/state) - var/data[0] - data["hasOccupant"] = occupant ? 1 : 0 - var/occupantData[0] - var/crisis = 0 - if(occupant) - occupantData["name"] = occupant.name - occupantData["stat"] = occupant.stat - occupantData["health"] = occupant.health - occupantData["maxHealth"] = occupant.maxHealth - occupantData["minHealth"] = HEALTH_THRESHOLD_DEAD - occupantData["bruteLoss"] = occupant.getBruteLoss() - occupantData["oxyLoss"] = occupant.getOxyLoss() - occupantData["toxLoss"] = occupant.getToxLoss() - occupantData["fireLoss"] = occupant.getFireLoss() - occupantData["paralysis"] = occupant.paralysis - occupantData["hasBlood"] = 0 - occupantData["bodyTemperature"] = occupant.bodytemperature - occupantData["maxTemp"] = 1000 // If you get a burning vox armalis into the sleeper, congratulations - // Because we can put simple_animals in here, we need to do something tricky to get things working nice - occupantData["temperatureSuitability"] = 0 // 0 is the baseline - if(ishuman(occupant) && occupant.dna.species) - // I wanna do something where the bar gets bluer as the temperature gets lower - // For now, I'll just use the standard format for the temperature status - var/datum/species/sp = occupant.dna.species - if(occupant.bodytemperature < sp.cold_level_3) - occupantData["temperatureSuitability"] = -3 - else if(occupant.bodytemperature < sp.cold_level_2) - occupantData["temperatureSuitability"] = -2 - else if(occupant.bodytemperature < sp.cold_level_1) - occupantData["temperatureSuitability"] = -1 - else if(occupant.bodytemperature > sp.heat_level_3) - occupantData["temperatureSuitability"] = 3 - else if(occupant.bodytemperature > sp.heat_level_2) - occupantData["temperatureSuitability"] = 2 - else if(occupant.bodytemperature > sp.heat_level_1) - occupantData["temperatureSuitability"] = 1 - else if(istype(occupant, /mob/living/simple_animal)) - var/mob/living/simple_animal/silly = occupant - if(silly.bodytemperature < silly.minbodytemp) - occupantData["temperatureSuitability"] = -3 - else if(silly.bodytemperature > silly.maxbodytemp) - occupantData["temperatureSuitability"] = 3 - // Blast you, imperial measurement system - occupantData["btCelsius"] = occupant.bodytemperature - T0C - occupantData["btFaren"] = ((occupant.bodytemperature - T0C) * (9.0/5.0))+ 32 - - - crisis = (occupant.health < min_health) - // I'm not sure WHY you'd want to put a simple_animal in a sleeper, but precedent is precedent - // Runtime is aptly named, isn't she? - if(ishuman(occupant) && !(NO_BLOOD in occupant.dna.species.species_traits)) - occupantData["pulse"] = occupant.get_pulse(GETPULSE_TOOL) - occupantData["hasBlood"] = 1 - occupantData["bloodLevel"] = round(occupant.blood_volume) - occupantData["bloodMax"] = occupant.max_blood - occupantData["bloodPercent"] = round(100*(occupant.blood_volume/occupant.max_blood), 0.01) - - data["occupant"] = occupantData - data["maxchem"] = max_chem - data["minhealth"] = min_health - data["dialysis"] = filtering - if(beaker) - data["isBeakerLoaded"] = 1 - if(beaker.reagents) - data["beakerFreeSpace"] = round(beaker.reagents.maximum_volume - beaker.reagents.total_volume) - else - data["beakerFreeSpace"] = 0 - - var/chemicals[0] - for(var/re in possible_chems) - var/datum/reagent/temp = GLOB.chemical_reagents_list[re] - if(temp) - var/reagent_amount = 0 - var/pretty_amount - var/injectable = occupant ? 1 : 0 - var/overdosing = 0 - var/caution = 0 // To make things clear that you're coming close to an overdose - if(crisis && !(temp.id in emergency_chems)) - injectable = 0 - - if(occupant && occupant.reagents) - reagent_amount = occupant.reagents.get_reagent_amount(temp.id) - // If they're mashing the highest concentration, they get one warning - if(temp.overdose_threshold && reagent_amount + 10 > temp.overdose_threshold) - caution = 1 - if(temp.id in occupant.reagents.overdose_list()) - overdosing = 1 - - // Because I don't know how to do this on the nano side - pretty_amount = round(reagent_amount, 0.05) - - chemicals.Add(list(list("title" = temp.name, "id" = temp.id, "commands" = list("chemical" = temp.id), "occ_amount" = reagent_amount, "pretty_amount" = pretty_amount, "injectable" = injectable, "overdosing" = overdosing, "od_warning" = caution))) - data["chemicals"] = chemicals - return data - -/obj/machinery/sleeper/Topic(href, href_list) - if(!controls_inside && usr == occupant) - return 0 - - if(..()) - return 1 - - if(panel_open) - to_chat(usr, "Close the maintenance panel first.") - return 0 - - if((usr.contents.Find(src) || ((get_dist(src, usr) <= 1) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon/ai))) - if(href_list["chemical"]) - if(occupant) - if(occupant.stat == DEAD) - to_chat(usr, "This person has no life for to preserve anymore. Take [occupant.p_them()] to a department capable of reanimating them.") - else if(occupant.health > min_health || (href_list["chemical"] in emergency_chems)) - inject_chemical(usr,href_list["chemical"],text2num(href_list["amount"])) - else - to_chat(usr, "This person is not in good enough condition for sleepers to be effective! Use another means of treatment, such as cryogenics!") - if(href_list["removebeaker"]) - remove_beaker() - if(href_list["togglefilter"]) - toggle_filter() - if(href_list["ejectify"]) - eject() - add_fingerprint(usr) - return 1 - -/obj/machinery/sleeper/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/reagent_containers/glass)) - if(!beaker) - if(!user.drop_item()) - to_chat(user, "[I] is stuck to you!") - return - - beaker = I - I.forceMove(src) - user.visible_message("[user] adds \a [I] to [src]!", "You add \a [I] to [src]!") - return - - else - to_chat(user, "The sleeper has a beaker already.") - return - - if(isscrewdriver(I)) - if(occupant) - to_chat(user, "The maintenance panel is locked.") - return - default_deconstruction_screwdriver(user, "[base_icon]-o", "[base_icon]-open", I) - return - - if(iswrench(I)) - if(occupant) - to_chat(user, "The scanner is occupied.") - return - if(panel_open) - to_chat(user, "Close the maintenance panel first.") - return - if(dir == EAST) - orient = "LEFT" - setDir(WEST) - else - orient = "RIGHT" - setDir(EAST) - playsound(loc, I.usesound, 50, 1) - return - - if(exchange_parts(user, I)) - return - - if(default_deconstruction_crowbar(I)) - return - - if(istype(I, /obj/item/grab)) - var/obj/item/grab/G = I - if(panel_open) - to_chat(user, "Close the maintenance panel first.") - return - if(!ismob(G.affecting)) - return - if(occupant) - to_chat(user, "The sleeper is already occupied!") - return - if(G.affecting.has_buckled_mobs()) //mob attached to us - to_chat(user, "[G.affecting] will not fit into [src] because [G.affecting.p_they()] [G.affecting.p_have()] a slime latched onto [G.affecting.p_their()] head.") - return - - visible_message("[user] starts putting [G.affecting.name] into the sleeper.") - - if(do_after(user, 20, target = G.affecting)) - if(occupant) - to_chat(user, "The sleeper is already occupied!") - return - if(!G || !G.affecting) - return - var/mob/M = G.affecting - M.forceMove(src) - occupant = M - icon_state = "[base_icon]" - to_chat(M, "You feel cool air surround you. You go numb as your senses turn inward.") - add_fingerprint(user) - qdel(G) - return - - return ..() - - -/obj/machinery/sleeper/ex_act(severity) - if(filtering) - toggle_filter() - if(occupant) - occupant.ex_act(severity) - ..() - -/obj/machinery/sleeper/handle_atom_del(atom/A) - ..() - if(A == occupant) - occupant = null - updateUsrDialog() - update_icon() - if(A == beaker) - beaker = null - updateUsrDialog() - -/obj/machinery/sleeper/emp_act(severity) - if(filtering) - toggle_filter() - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - if(occupant) - go_out() - ..(severity) - -/obj/machinery/sleeper/narsie_act() - go_out() - new /obj/effect/gibspawner/generic(get_turf(loc)) //I REPLACE YOUR TECHNOLOGY WITH FLESH! - qdel(src) - -/obj/machinery/sleeper/proc/toggle_filter() - if(filtering) - filtering = 0 - else - filtering = 1 - -/obj/machinery/sleeper/proc/go_out() - if(filtering) - toggle_filter() - if(!occupant) - return - occupant.forceMove(loc) - occupant = null - icon_state = "[base_icon]-open" - // eject trash the occupant dropped - for(var/atom/movable/A in contents - component_parts - list(beaker)) - A.forceMove(loc) - -/obj/machinery/sleeper/proc/inject_chemical(mob/living/user as mob, chemical, amount) - if(!(chemical in possible_chems)) - to_chat(user, "The sleeper does not offer that chemical!") - return - - if(occupant) - if(occupant.reagents) - if(occupant.reagents.get_reagent_amount(chemical) + amount <= max_chem) - occupant.reagents.add_reagent(chemical, amount) - return - else - to_chat(user, "You can not inject any more of this chemical.") - return - else - to_chat(user, "The patient rejects the chemicals!") - return - else - to_chat(user, "There's no occupant in the sleeper!") - return - -/obj/machinery/sleeper/verb/eject() - set name = "Eject Sleeper" - set category = "Object" - set src in oview(1) - - if(usr.incapacitated()) //are you cuffed, dying, lying, stunned or other - return - - icon_state = "[base_icon]-open" - go_out() - add_fingerprint(usr) - return - -/obj/machinery/sleeper/verb/remove_beaker() - set name = "Remove Beaker" - set category = "Object" - set src in oview(1) - - if(usr.incapacitated()) //are you cuffed, dying, lying, stunned or other - return - - if(beaker) - filtering = 0 - beaker.forceMove(usr.loc) - beaker = null - add_fingerprint(usr) - return - -/obj/machinery/sleeper/MouseDrop_T(atom/movable/O as mob|obj, mob/user as mob) - if(O.loc == user) //no you can't pull things out of your ass - return - if(user.incapacitated()) //are you cuffed, dying, lying, stunned or other - return - if(get_dist(user, src) > 1 || get_dist(user, O) > 1 || user.contents.Find(src)) // is the mob anchored, too far away from you, or are you too far away from the source - return - if(!ismob(O)) //humans only - return - if(istype(O, /mob/living/simple_animal) || istype(O, /mob/living/silicon)) //animals and robots dont fit - return - if(!ishuman(user) && !isrobot(user)) //No ghosts or mice putting people into the sleeper - return - if(user.loc==null) // just in case someone manages to get a closet into the blue light dimension, as unlikely as that seems - return - if(!istype(user.loc, /turf) || !istype(O.loc, /turf)) // are you in a container/closet/pod/etc? - return - if(panel_open) - to_chat(user, "Close the maintenance panel first.") - return - if(occupant) - to_chat(user, "The sleeper is already occupied!") - return - var/mob/living/L = O - if(!istype(L) || L.buckled) - return - if(L.abiotic()) - to_chat(user, "Subject cannot have abiotic items on.") - return - if(L.has_buckled_mobs()) //mob attached to us - to_chat(user, "[L] will not fit into [src] because [L.p_they()] [L.p_have()] a slime latched onto [L.p_their()] head.") - return - if(L == user) - visible_message("[user] starts climbing into the sleeper.") - else - visible_message("[user] starts putting [L.name] into the sleeper.") - - if(do_after(user, 20, target = L)) - if(occupant) - to_chat(user, "The sleeper is already occupied!") - return - if(!L) return - L.forceMove(src) - occupant = L - icon_state = "[base_icon]" - to_chat(L, "You feel cool air surround you. You go numb as your senses turn inward.") - add_fingerprint(user) - if(user.pulling == L) - user.stop_pulling() - return - return - -/obj/machinery/sleeper/AllowDrop() - return FALSE - -/obj/machinery/sleeper/verb/move_inside() - set name = "Enter Sleeper" - set category = "Object" - set src in oview(1) - if(usr.stat != 0 || !(ishuman(usr))) - return - if(occupant) - to_chat(usr, "The sleeper is already occupied!") - return - if(panel_open) - to_chat(usr, "Close the maintenance panel first.") - return - if(usr.incapacitated()) //are you cuffed, dying, lying, stunned or other - return - if(usr.has_buckled_mobs()) //mob attached to us - to_chat(usr, "[usr] will not fit into [src] because [usr.p_they()] [usr.p_have()] a slime latched onto [usr.p_their()] head.") - return - visible_message("[usr] starts climbing into the sleeper.") - if(do_after(usr, 20, target = usr)) - if(occupant) - to_chat(usr, "The sleeper is already occupied!") - return - usr.stop_pulling() - usr.forceMove(src) - occupant = usr - icon_state = "[base_icon]" - - for(var/obj/O in src) - qdel(O) - add_fingerprint(usr) - return - return - -/obj/machinery/sleeper/syndie - icon_state = "sleeper_s-open" - base_icon = "sleeper_s" - possible_chems = list("epinephrine", "ether", "salbutamol", "styptic_powder", "silver_sulfadiazine") - emergency_chems = list("epinephrine") - controls_inside = TRUE - - light_color = LIGHT_COLOR_DARKRED - -/obj/machinery/sleeper/syndie/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/sleeper/syndicate(null) - var/obj/item/stock_parts/matter_bin/B = new(null) - B.rating = initial_bin_rating - component_parts += B - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - RefreshParts() - -#undef ADDICTION_SPEEDUP_TIME +#define ADDICTION_SPEEDUP_TIME 1500 // 2.5 minutes + +///////////// +// SLEEPER // +//////////// + +/obj/machinery/sleeper + name = "Sleeper" + icon = 'icons/obj/cryogenic2.dmi' + icon_state = "sleeper-open" + var/base_icon = "sleeper" + density = 1 + anchored = 1 + dir = WEST + var/orient = "LEFT" // "RIGHT" changes the dir suffix to "-r" + var/mob/living/carbon/human/occupant = null + var/possible_chems = list("ephedrine", "salglu_solution", "salbutamol", "charcoal") + var/emergency_chems = list("ephedrine") // Desnowflaking + var/amounts = list(5, 10) + var/obj/item/reagent_containers/glass/beaker = null + var/filtering = 0 + var/max_chem + var/initial_bin_rating = 1 + var/min_health = -25 + var/controls_inside = FALSE + idle_power_usage = 1250 + active_power_usage = 2500 + + light_color = LIGHT_COLOR_CYAN + +/obj/machinery/sleeper/power_change() + ..() + if(!(stat & (BROKEN|NOPOWER))) + set_light(2) + else + set_light(0) + +/obj/machinery/sleeper/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/sleeper(null) + + // Customizable bin rating, used by the labor camp to stop people filling themselves with chemicals and escaping. + var/obj/item/stock_parts/matter_bin/B = new(null) + B.rating = initial_bin_rating + component_parts += B + + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + RefreshParts() + +/obj/machinery/sleeper/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/sleeper(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/manipulator/pico(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + RefreshParts() + +/obj/machinery/sleeper/RefreshParts() + var/E + for(var/obj/item/stock_parts/matter_bin/B in component_parts) + E += B.rating + + max_chem = E * 20 + min_health = -E * 25 + +/obj/machinery/sleeper/Destroy() + for(var/mob/M in contents) + M.forceMove(get_turf(src)) + return ..() + +/obj/machinery/sleeper/relaymove(mob/user as mob) + if(user.incapacitated()) + return 0 //maybe they should be able to get out with cuffs, but whatever + go_out() + +/obj/machinery/sleeper/process() + if(filtering > 0) + if(beaker) + // To prevent runtimes from drawing blood from runtime, and to prevent getting IPC blood. + if(!istype(occupant) || !occupant.dna || (NO_BLOOD in occupant.dna.species.species_traits)) + filtering = 0 + return + + if(beaker.reagents.total_volume < beaker.reagents.maximum_volume) + occupant.transfer_blood_to(beaker, 1) + for(var/datum/reagent/x in occupant.reagents.reagent_list) + occupant.reagents.trans_to(beaker, 3) + occupant.transfer_blood_to(beaker, 1) + + if(occupant) + for(var/A in occupant.reagents.addiction_list) + var/datum/reagent/R = A + + var/addiction_removal_chance = 5 + if(world.timeofday > (R.last_addiction_dose + ADDICTION_SPEEDUP_TIME)) // 2.5 minutes + addiction_removal_chance = 10 + if(prob(addiction_removal_chance)) + to_chat(occupant, "You no longer feel reliant on [R.name]!") + occupant.reagents.addiction_list.Remove(R) + qdel(R) + + for(var/mob/M as mob in src) // makes sure that simple mobs don't get stuck inside a sleeper when they resist out of occupant's grasp + if(M == occupant) + continue + else + M.forceMove(loc) + + updateDialog() + return + + +/obj/machinery/sleeper/attack_ai(mob/user) + return attack_hand(user) + +/obj/machinery/sleeper/attack_ghost(mob/user) + return attack_hand(user) + +/obj/machinery/sleeper/attack_hand(mob/user) + if(stat & (NOPOWER|BROKEN)) + return + + if(panel_open) + to_chat(user, "Close the maintenance panel first.") + return + + ui_interact(user) + +/obj/machinery/sleeper/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "sleeper.tmpl", "Sleeper", 550, 770) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/sleeper/ui_data(mob/user, datum/topic_state/state) + var/data[0] + data["hasOccupant"] = occupant ? 1 : 0 + var/occupantData[0] + var/crisis = 0 + if(occupant) + occupantData["name"] = occupant.name + occupantData["stat"] = occupant.stat + occupantData["health"] = occupant.health + occupantData["maxHealth"] = occupant.maxHealth + occupantData["minHealth"] = HEALTH_THRESHOLD_DEAD + occupantData["bruteLoss"] = occupant.getBruteLoss() + occupantData["oxyLoss"] = occupant.getOxyLoss() + occupantData["toxLoss"] = occupant.getToxLoss() + occupantData["fireLoss"] = occupant.getFireLoss() + occupantData["paralysis"] = occupant.paralysis + occupantData["hasBlood"] = 0 + occupantData["bodyTemperature"] = occupant.bodytemperature + occupantData["maxTemp"] = 1000 // If you get a burning vox armalis into the sleeper, congratulations + // Because we can put simple_animals in here, we need to do something tricky to get things working nice + occupantData["temperatureSuitability"] = 0 // 0 is the baseline + if(ishuman(occupant) && occupant.dna.species) + // I wanna do something where the bar gets bluer as the temperature gets lower + // For now, I'll just use the standard format for the temperature status + var/datum/species/sp = occupant.dna.species + if(occupant.bodytemperature < sp.cold_level_3) + occupantData["temperatureSuitability"] = -3 + else if(occupant.bodytemperature < sp.cold_level_2) + occupantData["temperatureSuitability"] = -2 + else if(occupant.bodytemperature < sp.cold_level_1) + occupantData["temperatureSuitability"] = -1 + else if(occupant.bodytemperature > sp.heat_level_3) + occupantData["temperatureSuitability"] = 3 + else if(occupant.bodytemperature > sp.heat_level_2) + occupantData["temperatureSuitability"] = 2 + else if(occupant.bodytemperature > sp.heat_level_1) + occupantData["temperatureSuitability"] = 1 + else if(istype(occupant, /mob/living/simple_animal)) + var/mob/living/simple_animal/silly = occupant + if(silly.bodytemperature < silly.minbodytemp) + occupantData["temperatureSuitability"] = -3 + else if(silly.bodytemperature > silly.maxbodytemp) + occupantData["temperatureSuitability"] = 3 + // Blast you, imperial measurement system + occupantData["btCelsius"] = occupant.bodytemperature - T0C + occupantData["btFaren"] = ((occupant.bodytemperature - T0C) * (9.0/5.0))+ 32 + + + crisis = (occupant.health < min_health) + // I'm not sure WHY you'd want to put a simple_animal in a sleeper, but precedent is precedent + // Runtime is aptly named, isn't she? + if(ishuman(occupant) && !(NO_BLOOD in occupant.dna.species.species_traits)) + occupantData["pulse"] = occupant.get_pulse(GETPULSE_TOOL) + occupantData["hasBlood"] = 1 + occupantData["bloodLevel"] = round(occupant.blood_volume) + occupantData["bloodMax"] = occupant.max_blood + occupantData["bloodPercent"] = round(100*(occupant.blood_volume/occupant.max_blood), 0.01) + + data["occupant"] = occupantData + data["maxchem"] = max_chem + data["minhealth"] = min_health + data["dialysis"] = filtering + if(beaker) + data["isBeakerLoaded"] = 1 + if(beaker.reagents) + data["beakerFreeSpace"] = round(beaker.reagents.maximum_volume - beaker.reagents.total_volume) + else + data["beakerFreeSpace"] = 0 + + var/chemicals[0] + for(var/re in possible_chems) + var/datum/reagent/temp = GLOB.chemical_reagents_list[re] + if(temp) + var/reagent_amount = 0 + var/pretty_amount + var/injectable = occupant ? 1 : 0 + var/overdosing = 0 + var/caution = 0 // To make things clear that you're coming close to an overdose + if(crisis && !(temp.id in emergency_chems)) + injectable = 0 + + if(occupant && occupant.reagents) + reagent_amount = occupant.reagents.get_reagent_amount(temp.id) + // If they're mashing the highest concentration, they get one warning + if(temp.overdose_threshold && reagent_amount + 10 > temp.overdose_threshold) + caution = 1 + if(temp.id in occupant.reagents.overdose_list()) + overdosing = 1 + + // Because I don't know how to do this on the nano side + pretty_amount = round(reagent_amount, 0.05) + + chemicals.Add(list(list("title" = temp.name, "id" = temp.id, "commands" = list("chemical" = temp.id), "occ_amount" = reagent_amount, "pretty_amount" = pretty_amount, "injectable" = injectable, "overdosing" = overdosing, "od_warning" = caution))) + data["chemicals"] = chemicals + return data + +/obj/machinery/sleeper/Topic(href, href_list) + if(!controls_inside && usr == occupant) + return 0 + + if(..()) + return 1 + + if(panel_open) + to_chat(usr, "Close the maintenance panel first.") + return 0 + + if((usr.contents.Find(src) || ((get_dist(src, usr) <= 1) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon/ai))) + if(href_list["chemical"]) + if(occupant) + if(occupant.stat == DEAD) + to_chat(usr, "This person has no life for to preserve anymore. Take [occupant.p_them()] to a department capable of reanimating them.") + else if(occupant.health > min_health || (href_list["chemical"] in emergency_chems)) + inject_chemical(usr,href_list["chemical"],text2num(href_list["amount"])) + else + to_chat(usr, "This person is not in good enough condition for sleepers to be effective! Use another means of treatment, such as cryogenics!") + if(href_list["removebeaker"]) + remove_beaker() + if(href_list["togglefilter"]) + toggle_filter() + if(href_list["ejectify"]) + eject() + add_fingerprint(usr) + return 1 + +/obj/machinery/sleeper/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/reagent_containers/glass)) + if(!beaker) + if(!user.drop_item()) + to_chat(user, "[I] is stuck to you!") + return + + beaker = I + I.forceMove(src) + user.visible_message("[user] adds \a [I] to [src]!", "You add \a [I] to [src]!") + return + + else + to_chat(user, "The sleeper has a beaker already.") + return + + if(exchange_parts(user, I)) + return + + if(istype(I, /obj/item/grab)) + var/obj/item/grab/G = I + if(panel_open) + to_chat(user, "Close the maintenance panel first.") + return + if(!ismob(G.affecting)) + return + if(occupant) + to_chat(user, "The sleeper is already occupied!") + return + if(G.affecting.has_buckled_mobs()) //mob attached to us + to_chat(user, "[G.affecting] will not fit into [src] because [G.affecting.p_they()] [G.affecting.p_have()] a slime latched onto [G.affecting.p_their()] head.") + return + + visible_message("[user] starts putting [G.affecting.name] into the sleeper.") + + if(do_after(user, 20, target = G.affecting)) + if(occupant) + to_chat(user, "The sleeper is already occupied!") + return + if(!G || !G.affecting) + return + var/mob/M = G.affecting + M.forceMove(src) + occupant = M + icon_state = "[base_icon]" + to_chat(M, "You feel cool air surround you. You go numb as your senses turn inward.") + add_fingerprint(user) + qdel(G) + return + + return ..() + + +/obj/machinery/sleeper/crowbar_act(mob/user, obj/item/I) + if(default_deconstruction_crowbar(user, I)) + return TRUE + +/obj/machinery/sleeper/screwdriver_act(mob/user, obj/item/I) + if(occupant) + to_chat(user, "The maintenance panel is locked.") + return TRUE + if(default_deconstruction_screwdriver(user, "[base_icon]-o", "[base_icon]-open", I)) + return TRUE + +/obj/machinery/sleeper/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(occupant) + to_chat(user, "The scanner is occupied.") + return + if(panel_open) + to_chat(user, "Close the maintenance panel first.") + return + if(dir == EAST) + orient = "LEFT" + setDir(WEST) + else + orient = "RIGHT" + setDir(EAST) + +/obj/machinery/sleeper/ex_act(severity) + if(filtering) + toggle_filter() + if(occupant) + occupant.ex_act(severity) + ..() + +/obj/machinery/sleeper/handle_atom_del(atom/A) + ..() + if(A == occupant) + occupant = null + updateUsrDialog() + update_icon() + if(A == beaker) + beaker = null + updateUsrDialog() + +/obj/machinery/sleeper/emp_act(severity) + if(filtering) + toggle_filter() + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + if(occupant) + go_out() + ..(severity) + +/obj/machinery/sleeper/narsie_act() + go_out() + new /obj/effect/gibspawner/generic(get_turf(loc)) //I REPLACE YOUR TECHNOLOGY WITH FLESH! + qdel(src) + +/obj/machinery/sleeper/proc/toggle_filter() + if(filtering) + filtering = 0 + else + filtering = 1 + +/obj/machinery/sleeper/proc/go_out() + if(filtering) + toggle_filter() + if(!occupant) + return + occupant.forceMove(loc) + occupant = null + icon_state = "[base_icon]-open" + // eject trash the occupant dropped + for(var/atom/movable/A in contents - component_parts - list(beaker)) + A.forceMove(loc) + +/obj/machinery/sleeper/proc/inject_chemical(mob/living/user as mob, chemical, amount) + if(!(chemical in possible_chems)) + to_chat(user, "The sleeper does not offer that chemical!") + return + + if(occupant) + if(occupant.reagents) + if(occupant.reagents.get_reagent_amount(chemical) + amount <= max_chem) + occupant.reagents.add_reagent(chemical, amount) + return + else + to_chat(user, "You can not inject any more of this chemical.") + return + else + to_chat(user, "The patient rejects the chemicals!") + return + else + to_chat(user, "There's no occupant in the sleeper!") + return + +/obj/machinery/sleeper/verb/eject() + set name = "Eject Sleeper" + set category = "Object" + set src in oview(1) + + if(usr.incapacitated()) //are you cuffed, dying, lying, stunned or other + return + + icon_state = "[base_icon]-open" + go_out() + add_fingerprint(usr) + return + +/obj/machinery/sleeper/verb/remove_beaker() + set name = "Remove Beaker" + set category = "Object" + set src in oview(1) + + if(usr.incapacitated()) //are you cuffed, dying, lying, stunned or other + return + + if(beaker) + filtering = 0 + beaker.forceMove(usr.loc) + beaker = null + add_fingerprint(usr) + return + +/obj/machinery/sleeper/MouseDrop_T(atom/movable/O as mob|obj, mob/user as mob) + if(O.loc == user) //no you can't pull things out of your ass + return + if(user.incapacitated()) //are you cuffed, dying, lying, stunned or other + return + if(get_dist(user, src) > 1 || get_dist(user, O) > 1 || user.contents.Find(src)) // is the mob anchored, too far away from you, or are you too far away from the source + return + if(!ismob(O)) //humans only + return + if(istype(O, /mob/living/simple_animal) || istype(O, /mob/living/silicon)) //animals and robots dont fit + return + if(!ishuman(user) && !isrobot(user)) //No ghosts or mice putting people into the sleeper + return + if(user.loc==null) // just in case someone manages to get a closet into the blue light dimension, as unlikely as that seems + return + if(!istype(user.loc, /turf) || !istype(O.loc, /turf)) // are you in a container/closet/pod/etc? + return + if(panel_open) + to_chat(user, "Close the maintenance panel first.") + return + if(occupant) + to_chat(user, "The sleeper is already occupied!") + return + var/mob/living/L = O + if(!istype(L) || L.buckled) + return + if(L.abiotic()) + to_chat(user, "Subject cannot have abiotic items on.") + return + if(L.has_buckled_mobs()) //mob attached to us + to_chat(user, "[L] will not fit into [src] because [L.p_they()] [L.p_have()] a slime latched onto [L.p_their()] head.") + return + if(L == user) + visible_message("[user] starts climbing into the sleeper.") + else + visible_message("[user] starts putting [L.name] into the sleeper.") + + if(do_after(user, 20, target = L)) + if(occupant) + to_chat(user, "The sleeper is already occupied!") + return + if(!L) return + L.forceMove(src) + occupant = L + icon_state = "[base_icon]" + to_chat(L, "You feel cool air surround you. You go numb as your senses turn inward.") + add_fingerprint(user) + if(user.pulling == L) + user.stop_pulling() + return + return + +/obj/machinery/sleeper/AllowDrop() + return FALSE + +/obj/machinery/sleeper/verb/move_inside() + set name = "Enter Sleeper" + set category = "Object" + set src in oview(1) + if(usr.stat != 0 || !(ishuman(usr))) + return + if(occupant) + to_chat(usr, "The sleeper is already occupied!") + return + if(panel_open) + to_chat(usr, "Close the maintenance panel first.") + return + if(usr.incapacitated()) //are you cuffed, dying, lying, stunned or other + return + if(usr.has_buckled_mobs()) //mob attached to us + to_chat(usr, "[usr] will not fit into [src] because [usr.p_they()] [usr.p_have()] a slime latched onto [usr.p_their()] head.") + return + visible_message("[usr] starts climbing into the sleeper.") + if(do_after(usr, 20, target = usr)) + if(occupant) + to_chat(usr, "The sleeper is already occupied!") + return + usr.stop_pulling() + usr.forceMove(src) + occupant = usr + icon_state = "[base_icon]" + + for(var/obj/O in src) + qdel(O) + add_fingerprint(usr) + return + return + +/obj/machinery/sleeper/syndie + icon_state = "sleeper_s-open" + base_icon = "sleeper_s" + possible_chems = list("epinephrine", "ether", "salbutamol", "styptic_powder", "silver_sulfadiazine") + emergency_chems = list("epinephrine") + controls_inside = TRUE + + light_color = LIGHT_COLOR_DARKRED + +/obj/machinery/sleeper/syndie/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/sleeper/syndicate(null) + var/obj/item/stock_parts/matter_bin/B = new(null) + B.rating = initial_bin_rating + component_parts += B + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/sheet/glass(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + RefreshParts() + +#undef ADDICTION_SPEEDUP_TIME diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm index 0863b65415a..6b6f3087ad2 100644 --- a/code/game/machinery/adv_med.dm +++ b/code/game/machinery/adv_med.dm @@ -40,33 +40,9 @@ RefreshParts() /obj/machinery/bodyscanner/attackby(obj/item/I, mob/user) - if(isscrewdriver(I)) - if(occupant) - to_chat(user, "The maintenance panel is locked.") - return - default_deconstruction_screwdriver(user, "bodyscanner-o", "bodyscanner-open", I) - return - - if(iswrench(I)) - if(occupant) - to_chat(user, "The scanner is occupied.") - return - if(panel_open) - to_chat(user, "Close the maintenance panel first.") - return - if(dir == EAST) - setDir(WEST) - else - setDir(EAST) - playsound(loc, I.usesound, 50, 1) - return - if(exchange_parts(user, I)) return - if(default_deconstruction_crowbar(I)) - return - if(istype(I, /obj/item/grab)) var/obj/item/grab/TYPECAST_YOUR_SHIT = I if(panel_open) @@ -93,6 +69,28 @@ return ..() +/obj/machinery/bodyscanner/crowbar_act(mob/user, obj/item/I) + if(default_deconstruction_crowbar(user, I)) + return TRUE + +/obj/machinery/bodyscanner/screwdriver_act(mob/user, obj/item/I) + if(default_deconstruction_screwdriver(user, "bodyscanner-o", "bodyscanner-open", I)) + return TRUE + +/obj/machinery/bodyscanner/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(occupant) + to_chat(user, "The scanner is occupied.") + return + if(panel_open) + to_chat(user, "Close the maintenance panel first.") + return + if(dir == EAST) + setDir(WEST) + else + setDir(EAST) /obj/machinery/bodyscanner/MouseDrop_T(mob/living/carbon/human/H, mob/user) if(!istype(H)) @@ -501,4 +499,4 @@ else dat += "[src] is empty." - return dat \ No newline at end of file + return dat diff --git a/code/game/machinery/ai_slipper.dm b/code/game/machinery/ai_slipper.dm index 12ad51c8b28..eaa1120ab04 100644 --- a/code/game/machinery/ai_slipper.dm +++ b/code/game/machinery/ai_slipper.dm @@ -14,7 +14,7 @@ var/cooldown_time = 0 var/cooldown_timeleft = 0 var/cooldown_on = FALSE - req_access = list(access_ai_upload) + req_access = list(ACCESS_AI_UPLOAD) /obj/machinery/ai_slipper/power_change() if(stat & BROKEN) @@ -133,4 +133,4 @@ return if(uses >= 0) cooldown_on = FALSE - power_change() \ No newline at end of file + power_change() diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm index d13406eee99..a8fabc1fab7 100644 --- a/code/game/machinery/alarm.dm +++ b/code/game/machinery/alarm.dm @@ -1,1078 +1,1110 @@ -// A datum for dealing with threshold limit values -// used in /obj/machinery/alarm -/datum/tlv - var/min2 - var/min1 - var/max1 - var/max2 - -/datum/tlv/New(_min2 as num, _min1 as num, _max1 as num, _max2 as num) - min2 = _min2 - min1 = _min1 - max1 = _max1 - max2 = _max2 - -/datum/tlv/proc/get_danger_level(curval as num) - if(max2 >=0 && curval>max2) - return ATMOS_ALARM_DANGER - if(min2 >=0 && curval=0 && curval>max1) - return ATMOS_ALARM_WARNING - if(min1 >=0 && curval target_temperature + 2 || regulating_temperature) - //If it goes too far, we should adjust ourselves back before stopping. - if(!cur_tlv.get_danger_level(target_temperature)) - if(!regulating_temperature) - regulating_temperature = 1 - visible_message("\The [src] clicks as it starts [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ - "You hear a click and a faint electronic hum.") - - if(target_temperature > MAX_TEMPERATURE) - target_temperature = MAX_TEMPERATURE - - if(target_temperature < MIN_TEMPERATURE) - target_temperature = MIN_TEMPERATURE - - var/datum/gas_mixture/gas = location.remove_air(0.25*environment.total_moles()) - var/heat_capacity = gas.heat_capacity() - var/energy_used = max( abs( heat_capacity*(gas.temperature - target_temperature) ), MAX_ENERGY_CHANGE) - - //Use power. Assuming that each power unit represents 1000 watts.... - use_power(energy_used/1000, ENVIRON) - - //We need to cool ourselves. - if(heat_capacity) - if(environment.temperature > target_temperature) - gas.temperature -= energy_used/heat_capacity - else - gas.temperature += energy_used/heat_capacity - - environment.merge(gas) - - if(abs(environment.temperature - target_temperature) <= 0.5) - regulating_temperature = 0 - visible_message("\The [src] clicks quietly as it stops [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ - "You hear a click as a faint electronic humming stops.") - -/obj/machinery/alarm/update_icon() - if(wiresexposed) - icon_state = "alarmx" - return - if((stat & (NOPOWER|BROKEN)) || shorted) - icon_state = "alarmp" - return - - switch(max(danger_level, alarm_area.atmosalm-1)) - if(ATMOS_ALARM_NONE) - icon_state = "alarm0" - if(ATMOS_ALARM_WARNING) - icon_state = "alarm2" //yes, alarm2 is yellow alarm - if(ATMOS_ALARM_DANGER) - icon_state = "alarm1" - -/obj/machinery/alarm/proc/register_env_machine(var/m_id, var/device_type) - var/new_name - if(device_type=="AVP") - new_name = "[alarm_area.name] Vent Pump #[alarm_area.air_vent_names.len+1]" - alarm_area.air_vent_names[m_id] = new_name - else if(device_type=="AScr") - new_name = "[alarm_area.name] Air Scrubber #[alarm_area.air_scrub_names.len+1]" - alarm_area.air_scrub_names[m_id] = new_name - else - return - spawn (10) - send_signal(m_id, list("init" = new_name) ) - -/obj/machinery/alarm/proc/refresh_all() - for(var/id_tag in alarm_area.air_vent_names) - var/list/I = alarm_area.air_vent_info[id_tag] - if(I && I["timestamp"]+AALARM_REPORT_TIMEOUT/2 > world.time) - continue - send_signal(id_tag, list("status") ) - for(var/id_tag in alarm_area.air_scrub_names) - var/list/I = alarm_area.air_scrub_info[id_tag] - if(I && I["timestamp"]+AALARM_REPORT_TIMEOUT/2 > world.time) - continue - send_signal(id_tag, list("status") ) - -/obj/machinery/alarm/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - radio_connection = SSradio.add_object(src, frequency, RADIO_TO_AIRALARM) - -/obj/machinery/alarm/proc/send_signal(var/target, var/list/command)//sends signal 'command' to 'target'. Returns 0 if no radio connection, 1 otherwise - if(!radio_connection) - return 0 - - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - - signal.data = command - signal.data["tag"] = target - signal.data["sigtype"] = "command" - - radio_connection.post_signal(src, signal, RADIO_FROM_AIRALARM) -// to_chat(world, text("Signal [] Broadcasted to []", command, target)) - - return 1 - -/obj/machinery/alarm/proc/apply_mode() - switch(mode) - if(AALARM_MODE_SCRUBBING) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list( - "power"= 1, - "o2_scrub" = (preset==AALARM_PRESET_VOX), - "n2_scrub" = 0, - "co2_scrub"= 1, - "scrubbing"= 1, - "widenet"= 0, - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 1, - "checks"= 1, - "set_external_pressure"= ONE_ATMOSPHERE - )) - if(AALARM_MODE_CONTAMINATED) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list( - "power"= 1, - "co2_scrub"= 1, - "tox_scrub"= 1, - "n2o_scrub"= 1, - "scrubbing"= 1, - "widenet"= 1, - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 1, - "checks"= 1, - "set_external_pressure"= ONE_ATMOSPHERE - )) - if(AALARM_MODE_VENTING) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list( - "power"= 1, - "widenet"= 0, - "scrubbing"= 0 - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 1, - "checks"= 1, - "set_external_pressure" = ONE_ATMOSPHERE*2 - )) - if(AALARM_MODE_REFILL) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list( - "power"= 1, - "co2_scrub"= 1, - "tox_scrub"= 0, - "n2o_scrub"= 0, - "scrubbing"= 1, - "widenet"= 0, - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 1, - "checks"= 1, - "set_external_pressure" = ONE_ATMOSPHERE*3 - )) - if( - AALARM_MODE_PANIC, - AALARM_MODE_REPLACEMENT - ) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list( - "power"= 1, - "widenet"= 1, - "scrubbing"= 0 - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 0 - )) - if( - AALARM_MODE_SIPHON - ) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list( - "power"= 1, - "widenet"= 0, - "scrubbing"= 0 - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 0 - )) - - if(AALARM_MODE_OFF) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list( - "power"= 0 - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 0 - )) - if(AALARM_MODE_FLOOD) - for(var/device_id in alarm_area.air_scrub_names) - send_signal(device_id, list( - "power"=0 - )) - for(var/device_id in alarm_area.air_vent_names) - send_signal(device_id, list( - "power"= 1, - "checks"= 0, - )) - -/obj/machinery/alarm/proc/apply_danger_level(var/new_danger_level) - if(report_danger_level && alarm_area.atmosalert(new_danger_level, src)) - post_alert(new_danger_level) - - update_icon() - -/obj/machinery/alarm/proc/post_alert(alert_level) - var/datum/radio_frequency/frequency = SSradio.return_frequency(alarm_frequency) - if(!frequency) - return - - var/datum/signal/alert_signal = new - alert_signal.source = src - alert_signal.transmission_method = 1 - alert_signal.data["zone"] = alarm_area.name - alert_signal.data["type"] = "Atmospheric" - - if(alert_level==2) - alert_signal.data["alert"] = "severe" - else if(alert_level==1) - alert_signal.data["alert"] = "minor" - else if(alert_level==0) - alert_signal.data["alert"] = "clear" - - frequency.post_signal(src, alert_signal) - -/////////////// -//END HACKING// -/////////////// - -/obj/machinery/alarm/attack_ai(mob/user) - src.add_hiddenprint(user) - return ui_interact(user) - -/obj/machinery/alarm/attack_ghost(user as mob) - return interact(user) - -/obj/machinery/alarm/attack_hand(mob/user) - . = ..() - if(.) - return - return interact(user) - -/obj/machinery/alarm/interact(mob/user) - if(buildstage != 2) - return - - if(wiresexposed) - wires.Interact(user) - - if(!shorted) - ui_interact(user) - -/obj/machinery/alarm/proc/ui_air_status() - var/turf/location = get_turf(src) - if(!istype(location)) - return - - var/datum/gas_mixture/environment = location.return_air() - var/total = environment.oxygen + environment.nitrogen + environment.carbon_dioxide + environment.toxins - if(total==0) - return null - - var/datum/tlv/cur_tlv - var/GET_PP = R_IDEAL_GAS_EQUATION*environment.temperature/environment.volume - - cur_tlv = TLV["pressure"] - var/environment_pressure = environment.return_pressure() - var/pressure_dangerlevel = cur_tlv.get_danger_level(environment_pressure) - - cur_tlv = TLV["oxygen"] - var/oxygen_dangerlevel = cur_tlv.get_danger_level(environment.oxygen*GET_PP) - var/oxygen_percent = round(environment.oxygen / total * 100, 2) - - cur_tlv = TLV["nitrogen"] - var/nitrogen_dangerlevel = cur_tlv.get_danger_level(environment.nitrogen*GET_PP) - var/nitrogen_percent = round(environment.nitrogen / total * 100, 2) - - cur_tlv = TLV["carbon dioxide"] - var/co2_dangerlevel = cur_tlv.get_danger_level(environment.carbon_dioxide*GET_PP) - var/co2_percent = round(environment.carbon_dioxide / total * 100, 2) - - cur_tlv = TLV["plasma"] - var/plasma_dangerlevel = cur_tlv.get_danger_level(environment.toxins*GET_PP) - var/plasma_percent = round(environment.toxins / total * 100, 2) - - cur_tlv = TLV["other"] - var/other_moles = 0.0 - for(var/datum/gas/G in environment.trace_gases) - other_moles+=G.moles - var/other_dangerlevel = cur_tlv.get_danger_level(other_moles*GET_PP) - - cur_tlv = TLV["temperature"] - var/temperature_dangerlevel = cur_tlv.get_danger_level(environment.temperature) - - var/data[0] - data["pressure"] = environment_pressure - data["temperature"] = environment.temperature - data["temperature_c"] = round(environment.temperature - T0C, 0.1) - - var/percentages[0] - percentages["oxygen"] = oxygen_percent - percentages["nitrogen"] = nitrogen_percent - percentages["co2"] = co2_percent - percentages["plasma"] = plasma_percent - percentages["other"] = other_moles - data["contents"] = percentages - - var/danger[0] - danger["pressure"] = pressure_dangerlevel - danger["temperature"] = temperature_dangerlevel - danger["oxygen"] = oxygen_dangerlevel - danger["nitrogen"] = nitrogen_dangerlevel - danger["co2"] = co2_dangerlevel - danger["plasma"] = plasma_dangerlevel - danger["other"] = other_dangerlevel - danger["overall"] = max(pressure_dangerlevel,oxygen_dangerlevel,nitrogen_dangerlevel,co2_dangerlevel,plasma_dangerlevel,other_dangerlevel,temperature_dangerlevel) - data["danger"] = danger - return data - -/obj/machinery/alarm/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - - var/list/href_list = state.href_list(user) - if(href_list) - data["remote_connection"] = href_list["remote_connection"] - data["remote_access"] = href_list["remote_access"] - - data["name"] = sanitize(name) - data["air"] = ui_air_status() - data["alarmActivated"] = alarmActivated || danger_level == ATMOS_ALARM_DANGER - data["thresholds"] = generate_thresholds_menu() - - // Locked when: - // Not sent from atmos console AND - // Not silicon AND locked. - data["locked"] = !is_authenticated(user, href_list) - data["rcon"] = rcon_setting - data["target_temp"] = target_temperature - T0C - data["atmos_alarm"] = alarm_area.atmosalm - data["emagged"] = emagged - data["modes"] = list( - AALARM_MODE_SCRUBBING = list("name"="Filtering", "desc"="Scrubs out contaminants"),\ - AALARM_MODE_VENTING = list("name"="Draught", "desc"="Siphons out air while replacing"),\ - AALARM_MODE_PANIC = list("name"="Panic Siphon","desc"="Siphons air out of the room quickly"),\ - AALARM_MODE_REPLACEMENT = list("name"="Cycle", "desc"="Siphons air before replacing"),\ - AALARM_MODE_SIPHON = list("name"="Siphon", "desc"="Siphons air out of the room"),\ - AALARM_MODE_CONTAMINATED= list("name"="Contaminated","desc"="Scrubs out all contaminants quickly"),\ - AALARM_MODE_REFILL = list("name"="Refill", "desc"="Triples vent output"),\ - AALARM_MODE_OFF = list("name"="Off", "desc"="Shuts off vents and scrubbers"),\ - AALARM_MODE_FLOOD = list("name"="Flood", "desc"="Shuts off scrubbers and opens vents", "emagonly" = 1) - ) - data["mode"] = mode - data["presets"] = list( - AALARM_PRESET_HUMAN = list("name"="Human", "desc"="Checks for oxygen and nitrogen"),\ - AALARM_PRESET_VOX = list("name"="Vox", "desc"="Checks for nitrogen only"),\ - AALARM_PRESET_COLDROOM = list("name"="Coldroom", "desc"="For freezers"),\ - AALARM_PRESET_SERVER = list("name"="Server Room", "desc"="For server rooms") - ) - data["preset"] = preset - data["screen"] = screen - - var/list/vents=list() - if(alarm_area.air_vent_names.len) - for(var/id_tag in alarm_area.air_vent_names) - var/vent_info[0] - var/long_name = alarm_area.air_vent_names[id_tag] - var/list/vent_data = alarm_area.air_vent_info[id_tag] - if(!vent_data) - continue - vent_info["id_tag"]=id_tag - vent_info["name"]=sanitize(long_name) - vent_info += vent_data - vents+=list(vent_info) - data["vents"]=vents - - var/list/scrubbers=list() - if(alarm_area.air_scrub_names.len) - for(var/id_tag in alarm_area.air_scrub_names) - var/long_name = alarm_area.air_scrub_names[id_tag] - var/list/scrubber_data = alarm_area.air_scrub_info[id_tag] - if(!scrubber_data) - continue - scrubber_data["id_tag"]=id_tag - scrubber_data["name"]=sanitize(long_name) - scrubbers+=list(scrubber_data) - data["scrubbers"]=scrubbers - return data - -/obj/machinery/alarm/proc/get_nano_data_console(mob/user) - var/data[0] - data["name"] = sanitize(name) - data["ref"] = "\ref[src]" - data["danger"] = max(danger_level, alarm_area.atmosalm) - var/area/Area = get_area(src) - data["area"] = sanitize(Area.name) - var/turf/pos = get_turf(src) - data["x"] = pos.x - data["y"] = pos.y - data["z"] = pos.z - return data - -/obj/machinery/alarm/proc/generate_thresholds_menu() - var/datum/tlv/selected - var/list/thresholds = list() - - var/list/gas_names = list( - "oxygen" = "O2", - "nitrogen" = "N2", - "carbon dioxide" = "CO2", - "plasma" = "Toxin", - "other" = "Other") - for(var/g in gas_names) - thresholds += list(list("name" = gas_names[g], "settings" = list())) - selected = TLV[g] - thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "min2", "selected" = selected.min2)) - thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "min1", "selected" = selected.min1)) - thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "max1", "selected" = selected.max1)) - thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "max2", "selected" = selected.max2)) - - selected = TLV["pressure"] - thresholds += list(list("name" = "Pressure", "settings" = list())) - thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "min2", "selected" = selected.min2)) - thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "min1", "selected" = selected.min1)) - thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "max1", "selected" = selected.max1)) - thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "max2", "selected" = selected.max2)) - - selected = TLV["temperature"] - thresholds += list(list("name" = "Temperature", "settings" = list())) - thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "min2", "selected" = selected.min2)) - thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "min1", "selected" = selected.min1)) - thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "max1", "selected" = selected.max1)) - thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "max2", "selected" = selected.max2)) - - return thresholds - -/obj/machinery/alarm/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "air_alarm.tmpl", name, 570, 410, state = state) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/alarm/proc/is_authenticated(var/mob/user, href_list) - if(user.can_admin_interact()) - return 1 - else if(isAI(user) || isrobot(user) || emagged || is_auth_rcon(href_list)) - return 1 - else - return !locked - -/obj/machinery/alarm/proc/is_auth_rcon(href_list) - if(href_list && href_list["remote_connection"] && href_list["remote_access"]) - return 1 - else - return 0 - -/obj/machinery/alarm/CanUseTopic(var/mob/user, var/datum/topic_state/state, var/href_list = list()) - if(buildstage != 2) - return STATUS_CLOSE - - if(aidisabled && (isAI(user) || isrobot(user))) - to_chat(user, "AI control for \the [src] interface has been disabled.") - return STATUS_CLOSE - - . = shorted ? STATUS_DISABLED : STATUS_INTERACTIVE - - if(. == STATUS_INTERACTIVE) - var/extra_href = state.href_list(usr) - // Prevent remote users from altering RCON settings or activating atmos alarms unless they already have access - if((href_list["atmos_alarm"] || href_list["rcon"]) && extra_href["remote_connection"] && !extra_href["remote_access"]) - . = STATUS_UPDATE - return min(..(), .) - -/obj/machinery/alarm/Topic(href, href_list, var/nowindow = 0, var/datum/topic_state/state) - if(..(href, href_list, nowindow, state)) - return 1 - - var/state_href = state.href_list(usr) - - if(href_list["rcon"]) - var/attempted_rcon_setting = text2num(href_list["rcon"]) - switch(attempted_rcon_setting) - if(RCON_NO) - rcon_setting = RCON_NO - if(RCON_AUTO) - rcon_setting = RCON_AUTO - if(RCON_YES) - rcon_setting = RCON_YES - return 1 - - add_fingerprint(usr) - - if(href_list["command"]) - if(!is_authenticated(usr, state_href)) - return - - var/device_id = href_list["id_tag"] - switch(href_list["command"]) - if( "power", - "adjust_external_pressure", - "set_external_pressure", - "checks", - "co2_scrub", - "tox_scrub", - "n2o_scrub", - "n2_scrub", - "o2_scrub", - "widenet", - "scrubbing", - "direction") - var/val - if(href_list["val"]) - val=text2num(href_list["val"]) - else - var/newval = input("Enter new value") as num|null - if(isnull(newval)) - return - if(href_list["command"]=="set_external_pressure") - if(newval>1000+ONE_ATMOSPHERE) - newval = 1000+ONE_ATMOSPHERE - if(newval<0) - newval = 0 - val = newval - - send_signal(device_id, list(href_list["command"] = val ) ) - waiting_on_device=device_id - - if("set_threshold") - var/env = href_list["env"] - var/varname = href_list["var"] - var/datum/tlv/tlv = TLV[env] - var/newval = input("Enter [varname] for [env]", "Alarm triggers", tlv.vars[varname]) as num|null - - if(isnull(newval) || ..(href, href_list, nowindow, state)) - return - if(newval<0) - tlv.vars[varname] = -1.0 - else if(env=="temperature" && newval>5000) - tlv.vars[varname] = 5000 - else if(env=="pressure" && newval>50*ONE_ATMOSPHERE) - tlv.vars[varname] = 50*ONE_ATMOSPHERE - else if(env!="temperature" && env!="pressure" && newval>200) - tlv.vars[varname] = 200 - else - newval = round(newval,0.01) - tlv.vars[varname] = newval - - if(href_list["screen"]) - if(!is_authenticated(usr, state_href)) - return - - screen = text2num(href_list["screen"]) - return 1 - - if(href_list["atmos_alarm"]) - if(alarm_area.atmosalert(ATMOS_ALARM_DANGER, src)) - apply_danger_level(ATMOS_ALARM_DANGER) - alarmActivated = 1 - update_icon() - return 1 - - if(href_list["atmos_reset"]) - if(alarm_area.atmosalert(ATMOS_ALARM_NONE, src, TRUE)) - apply_danger_level(ATMOS_ALARM_NONE) - alarmActivated = 0 - update_icon() - return 1 - - if(href_list["mode"]) - if(!is_authenticated(usr, state_href)) - return - - mode = text2num(href_list["mode"]) - apply_mode() - return 1 - - if(href_list["preset"]) - if(!is_authenticated(usr, state_href)) - return - - preset = text2num(href_list["preset"]) - apply_preset() - return 1 - - if(href_list["temperature"]) - var/datum/tlv/selected = TLV["temperature"] - var/max_temperature = selected.max1 >= 0 ? min(selected.max1, MAX_TEMPERATURE) : max(selected.max1, MAX_TEMPERATURE) - var/min_temperature = max(selected.min1, MIN_TEMPERATURE) - var/max_temperature_c = max_temperature - T0C - var/min_temperature_c = min_temperature - T0C - var/input_temperature = input("What temperature would you like the system to maintain? (Capped between [min_temperature_c]C and [max_temperature_c]C)", "Thermostat Controls") as num|null - if(isnull(input_temperature) || ..(href, href_list, nowindow, state)) - return - input_temperature = input_temperature + T0C - if(input_temperature > max_temperature || input_temperature < min_temperature) - to_chat(usr, "Temperature must be between [min_temperature_c]C and [max_temperature_c]C") - else - target_temperature = input_temperature - return 1 - -/obj/machinery/alarm/emag_act(mob/user) - if(!emagged) - src.emagged = 1 - if(user) - user.visible_message("Sparks fly out of the [src]!", "You emag the [src], disabling its safeties.") - playsound(src.loc, 'sound/effects/sparks4.ogg', 50, 1) - return - -/obj/machinery/alarm/attackby(obj/item/I, mob/user, params) - add_fingerprint(user) - - switch(buildstage) - if(2) - if(isscrewdriver(I)) // Opening that Air Alarm up. - wiresexposed = !wiresexposed - to_chat(user, "The wires have been [wiresexposed ? "exposed" : "unexposed"]") - update_icon() - return - - if(iswirecutter(I)) // cutting the wires out - if(wires.wires_status == 31) // all wires cut - var/obj/item/stack/cable_coil/new_coil = new /obj/item/stack/cable_coil() - new_coil.amount = 5 - new_coil.forceMove(user.loc) - buildstage = 1 - update_icon() - return - - if(wiresexposed && ((ismultitool(I) || iswirecutter(I)))) - return attack_hand(user) - - if(istype(I, /obj/item/card/id) || istype(I, /obj/item/pda))// trying to unlock the interface with an ID card - if(stat & (NOPOWER|BROKEN)) - to_chat(user, "It does nothing") - return - else - if(allowed(usr) && !wires.IsIndexCut(AALARM_WIRE_IDSCAN)) - locked = !locked - to_chat(user, "You [ locked ? "lock" : "unlock"] the Air Alarm interface.") - updateUsrDialog() - else - to_chat(user, "Access denied.") - return - - if(1) - if(istype(I, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/coil = I - if(coil.amount < 5) - to_chat(user, "You need more cable for this!") - return - - to_chat(user, "You wire \the [src]!") - playsound(get_turf(src), coil.usesound, 50, 1) - coil.amount -= 5 - if(!coil.amount) - qdel(coil) - - buildstage = 2 - update_icon() - first_run() - return - - else if(iscrowbar(I)) - to_chat(user, "You start prying out the circuit.") - playsound(get_turf(src), I.usesound, 50, 1) - if(do_after(user, 20 * I.toolspeed, target = src)) - if(buildstage != 1) - return - to_chat(user, "You pry out the circuit!") - var/obj/item/airalarm_electronics/circuit = new /obj/item/airalarm_electronics() - circuit.forceMove(user.loc) - buildstage = 0 - update_icon() - return - if(0) - if(istype(I, /obj/item/airalarm_electronics)) - to_chat(user, "You insert the circuit!") - playsound(get_turf(src), I.usesound, 50, 1) - qdel(I) - buildstage = 1 - update_icon() - return - - else if(iswrench(I)) - to_chat(user, "You remove the fire alarm assembly from the wall!") - new /obj/item/mounted/frame/alarm_frame(get_turf(user)) - playsound(get_turf(src), I.usesound, 50, 1) - qdel(src) - return - - return ..() - -/obj/machinery/alarm/power_change() - if(powered(power_channel)) - stat &= ~NOPOWER - else - stat |= NOPOWER - spawn(rand(0,15)) - update_icon() - -/obj/machinery/alarm/obj_break(damage_flag) - ..() - update_icon() - -/obj/machinery/alarm/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - new /obj/item/stack/sheet/metal(loc, 2) - var/obj/item/I = new /obj/item/airalarm_electronics(loc) - if(!disassembled) - I.obj_integrity = I.max_integrity * 0.5 - new /obj/item/stack/cable_coil(loc, 3) - qdel(src) - -/obj/machinery/alarm/examine(mob/user) - . = ..() - if(buildstage < 2) - . += "It is not wired." - if(buildstage < 1) - . += "The circuit is missing." - -/obj/machinery/alarm/all_access - name = "all-access air alarm" - desc = "This particular atmos control unit appears to have no access restrictions." - locked = FALSE - req_access = null - req_one_access = null - -/* -AIR ALARM CIRCUIT -Just an object used in constructing air alarms -*/ -/obj/item/airalarm_electronics - name = "air alarm electronics" - icon = 'icons/obj/doors/door_assembly.dmi' - icon_state = "door_electronics" - desc = "Looks like a circuit. Probably is." - w_class = WEIGHT_CLASS_SMALL - materials = list(MAT_METAL=50, MAT_GLASS=50) - origin_tech = "engineering=2;programming=1" - toolspeed = 1 - usesound = 'sound/items/deconstruct.ogg' +// A datum for dealing with threshold limit values +// used in /obj/machinery/alarm +/datum/tlv + var/min2 + var/min1 + var/max1 + var/max2 + +/datum/tlv/New(_min2 as num, _min1 as num, _max1 as num, _max2 as num) + min2 = _min2 + min1 = _min1 + max1 = _max1 + max2 = _max2 + +/datum/tlv/proc/get_danger_level(curval as num) + if(max2 >=0 && curval>max2) + return ATMOS_ALARM_DANGER + if(min2 >=0 && curval=0 && curval>max1) + return ATMOS_ALARM_WARNING + if(min1 >=0 && curval target_temperature + 2 || regulating_temperature) + //If it goes too far, we should adjust ourselves back before stopping. + if(!cur_tlv.get_danger_level(target_temperature)) + if(!regulating_temperature) + regulating_temperature = 1 + visible_message("\The [src] clicks as it starts [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ + "You hear a click and a faint electronic hum.") + + if(target_temperature > MAX_TEMPERATURE) + target_temperature = MAX_TEMPERATURE + + if(target_temperature < MIN_TEMPERATURE) + target_temperature = MIN_TEMPERATURE + + var/datum/gas_mixture/gas = location.remove_air(0.25*environment.total_moles()) + var/heat_capacity = gas.heat_capacity() + var/energy_used = max( abs( heat_capacity*(gas.temperature - target_temperature) ), MAX_ENERGY_CHANGE) + + //Use power. Assuming that each power unit represents 1000 watts.... + use_power(energy_used/1000, ENVIRON) + + //We need to cool ourselves. + if(heat_capacity) + if(environment.temperature > target_temperature) + gas.temperature -= energy_used/heat_capacity + else + gas.temperature += energy_used/heat_capacity + + environment.merge(gas) + + if(abs(environment.temperature - target_temperature) <= 0.5) + regulating_temperature = 0 + visible_message("\The [src] clicks quietly as it stops [environment.temperature > target_temperature ? "cooling" : "heating"] the room.",\ + "You hear a click as a faint electronic humming stops.") + +/obj/machinery/alarm/update_icon() + if(wiresexposed) + icon_state = "alarmx" + return + if((stat & (NOPOWER|BROKEN)) || shorted) + icon_state = "alarmp" + return + + switch(max(danger_level, alarm_area.atmosalm-1)) + if(ATMOS_ALARM_NONE) + icon_state = "alarm0" + if(ATMOS_ALARM_WARNING) + icon_state = "alarm2" //yes, alarm2 is yellow alarm + if(ATMOS_ALARM_DANGER) + icon_state = "alarm1" + +/obj/machinery/alarm/proc/register_env_machine(var/m_id, var/device_type) + var/new_name + if(device_type=="AVP") + new_name = "[alarm_area.name] Vent Pump #[alarm_area.air_vent_names.len+1]" + alarm_area.air_vent_names[m_id] = new_name + else if(device_type=="AScr") + new_name = "[alarm_area.name] Air Scrubber #[alarm_area.air_scrub_names.len+1]" + alarm_area.air_scrub_names[m_id] = new_name + else + return + spawn (10) + send_signal(m_id, list("init" = new_name) ) + +/obj/machinery/alarm/proc/refresh_all() + for(var/id_tag in alarm_area.air_vent_names) + var/list/I = alarm_area.air_vent_info[id_tag] + if(I && I["timestamp"]+AALARM_REPORT_TIMEOUT/2 > world.time) + continue + send_signal(id_tag, list("status") ) + for(var/id_tag in alarm_area.air_scrub_names) + var/list/I = alarm_area.air_scrub_info[id_tag] + if(I && I["timestamp"]+AALARM_REPORT_TIMEOUT/2 > world.time) + continue + send_signal(id_tag, list("status") ) + +/obj/machinery/alarm/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + radio_connection = SSradio.add_object(src, frequency, RADIO_TO_AIRALARM) + +/obj/machinery/alarm/proc/send_signal(var/target, var/list/command)//sends signal 'command' to 'target'. Returns 0 if no radio connection, 1 otherwise + if(!radio_connection) + return 0 + + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + + signal.data = command + signal.data["tag"] = target + signal.data["sigtype"] = "command" + + radio_connection.post_signal(src, signal, RADIO_FROM_AIRALARM) +// to_chat(world, text("Signal [] Broadcasted to []", command, target)) + + return 1 + +/obj/machinery/alarm/proc/apply_mode() + switch(mode) + if(AALARM_MODE_SCRUBBING) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list( + "power"= 1, + "o2_scrub" = (preset==AALARM_PRESET_VOX), + "n2_scrub" = 0, + "co2_scrub"= 1, + "scrubbing"= 1, + "widenet"= 0, + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 1, + "checks"= 1, + "set_external_pressure"= ONE_ATMOSPHERE + )) + if(AALARM_MODE_CONTAMINATED) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list( + "power"= 1, + "co2_scrub"= 1, + "tox_scrub"= 1, + "n2o_scrub"= 1, + "scrubbing"= 1, + "widenet"= 1, + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 1, + "checks"= 1, + "set_external_pressure"= ONE_ATMOSPHERE + )) + if(AALARM_MODE_VENTING) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list( + "power"= 1, + "widenet"= 0, + "scrubbing"= 0 + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 1, + "checks"= 1, + "set_external_pressure" = ONE_ATMOSPHERE*2 + )) + if(AALARM_MODE_REFILL) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list( + "power"= 1, + "co2_scrub"= 1, + "tox_scrub"= 0, + "n2o_scrub"= 0, + "scrubbing"= 1, + "widenet"= 0, + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 1, + "checks"= 1, + "set_external_pressure" = ONE_ATMOSPHERE*3 + )) + if( + AALARM_MODE_PANIC, + AALARM_MODE_REPLACEMENT + ) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list( + "power"= 1, + "widenet"= 1, + "scrubbing"= 0 + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 0 + )) + if( + AALARM_MODE_SIPHON + ) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list( + "power"= 1, + "widenet"= 0, + "scrubbing"= 0 + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 0 + )) + + if(AALARM_MODE_OFF) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list( + "power"= 0 + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 0 + )) + if(AALARM_MODE_FLOOD) + for(var/device_id in alarm_area.air_scrub_names) + send_signal(device_id, list( + "power"=0 + )) + for(var/device_id in alarm_area.air_vent_names) + send_signal(device_id, list( + "power"= 1, + "checks"= 0, + )) + +/obj/machinery/alarm/proc/apply_danger_level(var/new_danger_level) + if(report_danger_level && alarm_area.atmosalert(new_danger_level, src)) + post_alert(new_danger_level) + + update_icon() + +/obj/machinery/alarm/proc/post_alert(alert_level) + var/datum/radio_frequency/frequency = SSradio.return_frequency(alarm_frequency) + if(!frequency) + return + + var/datum/signal/alert_signal = new + alert_signal.source = src + alert_signal.transmission_method = 1 + alert_signal.data["zone"] = alarm_area.name + alert_signal.data["type"] = "Atmospheric" + + if(alert_level==2) + alert_signal.data["alert"] = "severe" + else if(alert_level==1) + alert_signal.data["alert"] = "minor" + else if(alert_level==0) + alert_signal.data["alert"] = "clear" + + frequency.post_signal(src, alert_signal) + +/////////////// +//END HACKING// +/////////////// + +/obj/machinery/alarm/attack_ai(mob/user) + src.add_hiddenprint(user) + return ui_interact(user) + +/obj/machinery/alarm/attack_ghost(user as mob) + return interact(user) + +/obj/machinery/alarm/attack_hand(mob/user) + . = ..() + if(.) + return + return interact(user) + +/obj/machinery/alarm/interact(mob/user) + if(buildstage != 2) + return + + if(wiresexposed) + wires.Interact(user) + + if(!shorted) + ui_interact(user) + +/obj/machinery/alarm/proc/ui_air_status() + var/turf/location = get_turf(src) + if(!istype(location)) + return + + var/datum/gas_mixture/environment = location.return_air() + var/total = environment.oxygen + environment.nitrogen + environment.carbon_dioxide + environment.toxins + if(total==0) + return null + + var/datum/tlv/cur_tlv + var/GET_PP = R_IDEAL_GAS_EQUATION*environment.temperature/environment.volume + + cur_tlv = TLV["pressure"] + var/environment_pressure = environment.return_pressure() + var/pressure_dangerlevel = cur_tlv.get_danger_level(environment_pressure) + + cur_tlv = TLV["oxygen"] + var/oxygen_dangerlevel = cur_tlv.get_danger_level(environment.oxygen*GET_PP) + var/oxygen_percent = round(environment.oxygen / total * 100, 2) + + cur_tlv = TLV["nitrogen"] + var/nitrogen_dangerlevel = cur_tlv.get_danger_level(environment.nitrogen*GET_PP) + var/nitrogen_percent = round(environment.nitrogen / total * 100, 2) + + cur_tlv = TLV["carbon dioxide"] + var/co2_dangerlevel = cur_tlv.get_danger_level(environment.carbon_dioxide*GET_PP) + var/co2_percent = round(environment.carbon_dioxide / total * 100, 2) + + cur_tlv = TLV["plasma"] + var/plasma_dangerlevel = cur_tlv.get_danger_level(environment.toxins*GET_PP) + var/plasma_percent = round(environment.toxins / total * 100, 2) + + cur_tlv = TLV["other"] + var/other_moles = 0.0 + for(var/datum/gas/G in environment.trace_gases) + other_moles+=G.moles + var/other_dangerlevel = cur_tlv.get_danger_level(other_moles*GET_PP) + + cur_tlv = TLV["temperature"] + var/temperature_dangerlevel = cur_tlv.get_danger_level(environment.temperature) + + var/data[0] + data["pressure"] = environment_pressure + data["temperature"] = environment.temperature + data["temperature_c"] = round(environment.temperature - T0C, 0.1) + + var/percentages[0] + percentages["oxygen"] = oxygen_percent + percentages["nitrogen"] = nitrogen_percent + percentages["co2"] = co2_percent + percentages["plasma"] = plasma_percent + percentages["other"] = other_moles + data["contents"] = percentages + + var/danger[0] + danger["pressure"] = pressure_dangerlevel + danger["temperature"] = temperature_dangerlevel + danger["oxygen"] = oxygen_dangerlevel + danger["nitrogen"] = nitrogen_dangerlevel + danger["co2"] = co2_dangerlevel + danger["plasma"] = plasma_dangerlevel + danger["other"] = other_dangerlevel + danger["overall"] = max(pressure_dangerlevel,oxygen_dangerlevel,nitrogen_dangerlevel,co2_dangerlevel,plasma_dangerlevel,other_dangerlevel,temperature_dangerlevel) + data["danger"] = danger + return data + +/obj/machinery/alarm/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) + var/data[0] + + var/list/href_list = state.href_list(user) + if(href_list) + data["remote_connection"] = href_list["remote_connection"] + data["remote_access"] = href_list["remote_access"] + + data["name"] = sanitize(name) + data["air"] = ui_air_status() + data["alarmActivated"] = alarmActivated || danger_level == ATMOS_ALARM_DANGER + data["thresholds"] = generate_thresholds_menu() + + // Locked when: + // Not sent from atmos console AND + // Not silicon AND locked. + data["locked"] = !is_authenticated(user, href_list) + data["rcon"] = rcon_setting + data["target_temp"] = target_temperature - T0C + data["atmos_alarm"] = alarm_area.atmosalm + data["emagged"] = emagged + data["modes"] = list( + AALARM_MODE_SCRUBBING = list("name"="Filtering", "desc"="Scrubs out contaminants"),\ + AALARM_MODE_VENTING = list("name"="Draught", "desc"="Siphons out air while replacing"),\ + AALARM_MODE_PANIC = list("name"="Panic Siphon","desc"="Siphons air out of the room quickly"),\ + AALARM_MODE_REPLACEMENT = list("name"="Cycle", "desc"="Siphons air before replacing"),\ + AALARM_MODE_SIPHON = list("name"="Siphon", "desc"="Siphons air out of the room"),\ + AALARM_MODE_CONTAMINATED= list("name"="Contaminated","desc"="Scrubs out all contaminants quickly"),\ + AALARM_MODE_REFILL = list("name"="Refill", "desc"="Triples vent output"),\ + AALARM_MODE_OFF = list("name"="Off", "desc"="Shuts off vents and scrubbers"),\ + AALARM_MODE_FLOOD = list("name"="Flood", "desc"="Shuts off scrubbers and opens vents", "emagonly" = 1) + ) + data["mode"] = mode + data["presets"] = list( + AALARM_PRESET_HUMAN = list("name"="Human", "desc"="Checks for oxygen and nitrogen"),\ + AALARM_PRESET_VOX = list("name"="Vox", "desc"="Checks for nitrogen only"),\ + AALARM_PRESET_COLDROOM = list("name"="Coldroom", "desc"="For freezers"),\ + AALARM_PRESET_SERVER = list("name"="Server Room", "desc"="For server rooms") + ) + data["preset"] = preset + data["screen"] = screen + + var/list/vents=list() + if(alarm_area.air_vent_names.len) + for(var/id_tag in alarm_area.air_vent_names) + var/vent_info[0] + var/long_name = alarm_area.air_vent_names[id_tag] + var/list/vent_data = alarm_area.air_vent_info[id_tag] + if(!vent_data) + continue + vent_info["id_tag"]=id_tag + vent_info["name"]=sanitize(long_name) + vent_info += vent_data + vents+=list(vent_info) + data["vents"]=vents + + var/list/scrubbers=list() + if(alarm_area.air_scrub_names.len) + for(var/id_tag in alarm_area.air_scrub_names) + var/long_name = alarm_area.air_scrub_names[id_tag] + var/list/scrubber_data = alarm_area.air_scrub_info[id_tag] + if(!scrubber_data) + continue + scrubber_data["id_tag"]=id_tag + scrubber_data["name"]=sanitize(long_name) + scrubbers+=list(scrubber_data) + data["scrubbers"]=scrubbers + return data + +/obj/machinery/alarm/proc/get_nano_data_console(mob/user) + var/data[0] + data["name"] = sanitize(name) + data["ref"] = "\ref[src]" + data["danger"] = max(danger_level, alarm_area.atmosalm) + var/area/Area = get_area(src) + data["area"] = sanitize(Area.name) + var/turf/pos = get_turf(src) + data["x"] = pos.x + data["y"] = pos.y + data["z"] = pos.z + return data + +/obj/machinery/alarm/proc/generate_thresholds_menu() + var/datum/tlv/selected + var/list/thresholds = list() + + var/list/gas_names = list( + "oxygen" = "O2", + "nitrogen" = "N2", + "carbon dioxide" = "CO2", + "plasma" = "Toxin", + "other" = "Other") + for(var/g in gas_names) + thresholds += list(list("name" = gas_names[g], "settings" = list())) + selected = TLV[g] + thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "min2", "selected" = selected.min2)) + thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "min1", "selected" = selected.min1)) + thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "max1", "selected" = selected.max1)) + thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "max2", "selected" = selected.max2)) + + selected = TLV["pressure"] + thresholds += list(list("name" = "Pressure", "settings" = list())) + thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "min2", "selected" = selected.min2)) + thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "min1", "selected" = selected.min1)) + thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "max1", "selected" = selected.max1)) + thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "max2", "selected" = selected.max2)) + + selected = TLV["temperature"] + thresholds += list(list("name" = "Temperature", "settings" = list())) + thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "min2", "selected" = selected.min2)) + thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "min1", "selected" = selected.min1)) + thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "max1", "selected" = selected.max1)) + thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "max2", "selected" = selected.max2)) + + return thresholds + +/obj/machinery/alarm/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, var/master_ui = null, var/datum/topic_state/state = default_state) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "air_alarm.tmpl", name, 570, 410, state = state) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/alarm/proc/is_authenticated(var/mob/user, href_list) + if(user.can_admin_interact()) + return 1 + else if(isAI(user) || isrobot(user) || emagged || is_auth_rcon(href_list)) + return 1 + else + return !locked + +/obj/machinery/alarm/proc/is_auth_rcon(href_list) + if(href_list && href_list["remote_connection"] && href_list["remote_access"]) + return 1 + else + return 0 + +/obj/machinery/alarm/CanUseTopic(var/mob/user, var/datum/topic_state/state, var/href_list = list()) + if(buildstage != 2) + return STATUS_CLOSE + + if(aidisabled && (isAI(user) || isrobot(user))) + to_chat(user, "AI control for \the [src] interface has been disabled.") + return STATUS_CLOSE + + . = shorted ? STATUS_DISABLED : STATUS_INTERACTIVE + + if(. == STATUS_INTERACTIVE) + var/extra_href = state.href_list(usr) + // Prevent remote users from altering RCON settings or activating atmos alarms unless they already have access + if((href_list["atmos_alarm"] || href_list["rcon"]) && extra_href["remote_connection"] && !extra_href["remote_access"]) + . = STATUS_UPDATE + return min(..(), .) + +/obj/machinery/alarm/Topic(href, href_list, var/nowindow = 0, var/datum/topic_state/state) + if(..(href, href_list, nowindow, state)) + return 1 + + var/state_href = state.href_list(usr) + + if(href_list["rcon"]) + var/attempted_rcon_setting = text2num(href_list["rcon"]) + switch(attempted_rcon_setting) + if(RCON_NO) + rcon_setting = RCON_NO + if(RCON_AUTO) + rcon_setting = RCON_AUTO + if(RCON_YES) + rcon_setting = RCON_YES + return 1 + + add_fingerprint(usr) + + if(href_list["command"]) + if(!is_authenticated(usr, state_href)) + return + + var/device_id = href_list["id_tag"] + switch(href_list["command"]) + if( "power", + "adjust_external_pressure", + "set_external_pressure", + "checks", + "co2_scrub", + "tox_scrub", + "n2o_scrub", + "n2_scrub", + "o2_scrub", + "widenet", + "scrubbing", + "direction") + var/val + if(href_list["val"]) + val=text2num(href_list["val"]) + else + var/newval = input("Enter new value") as num|null + if(isnull(newval)) + return + if(href_list["command"]=="set_external_pressure") + if(newval>1000+ONE_ATMOSPHERE) + newval = 1000+ONE_ATMOSPHERE + if(newval<0) + newval = 0 + val = newval + + send_signal(device_id, list(href_list["command"] = val ) ) + waiting_on_device=device_id + + if("set_threshold") + var/env = href_list["env"] + var/varname = href_list["var"] + var/datum/tlv/tlv = TLV[env] + var/newval = input("Enter [varname] for [env]", "Alarm triggers", tlv.vars[varname]) as num|null + + if(isnull(newval) || ..(href, href_list, nowindow, state)) + return + if(newval<0) + tlv.vars[varname] = -1.0 + else if(env=="temperature" && newval>5000) + tlv.vars[varname] = 5000 + else if(env=="pressure" && newval>50*ONE_ATMOSPHERE) + tlv.vars[varname] = 50*ONE_ATMOSPHERE + else if(env!="temperature" && env!="pressure" && newval>200) + tlv.vars[varname] = 200 + else + newval = round(newval,0.01) + tlv.vars[varname] = newval + + if(href_list["screen"]) + if(!is_authenticated(usr, state_href)) + return + + screen = text2num(href_list["screen"]) + return 1 + + if(href_list["atmos_alarm"]) + if(alarm_area.atmosalert(ATMOS_ALARM_DANGER, src)) + apply_danger_level(ATMOS_ALARM_DANGER) + alarmActivated = 1 + update_icon() + return 1 + + if(href_list["atmos_reset"]) + if(alarm_area.atmosalert(ATMOS_ALARM_NONE, src, TRUE)) + apply_danger_level(ATMOS_ALARM_NONE) + alarmActivated = 0 + update_icon() + return 1 + + if(href_list["mode"]) + if(!is_authenticated(usr, state_href)) + return + + mode = text2num(href_list["mode"]) + apply_mode() + return 1 + + if(href_list["preset"]) + if(!is_authenticated(usr, state_href)) + return + + preset = text2num(href_list["preset"]) + apply_preset() + return 1 + + if(href_list["temperature"]) + var/datum/tlv/selected = TLV["temperature"] + var/max_temperature = selected.max1 >= 0 ? min(selected.max1, MAX_TEMPERATURE) : max(selected.max1, MAX_TEMPERATURE) + var/min_temperature = max(selected.min1, MIN_TEMPERATURE) + var/max_temperature_c = max_temperature - T0C + var/min_temperature_c = min_temperature - T0C + var/input_temperature = input("What temperature would you like the system to maintain? (Capped between [min_temperature_c]C and [max_temperature_c]C)", "Thermostat Controls") as num|null + if(isnull(input_temperature) || ..(href, href_list, nowindow, state)) + return + input_temperature = input_temperature + T0C + if(input_temperature > max_temperature || input_temperature < min_temperature) + to_chat(usr, "Temperature must be between [min_temperature_c]C and [max_temperature_c]C") + else + target_temperature = input_temperature + return 1 + +/obj/machinery/alarm/emag_act(mob/user) + if(!emagged) + src.emagged = 1 + if(user) + user.visible_message("Sparks fly out of the [src]!", "You emag the [src], disabling its safeties.") + playsound(src.loc, 'sound/effects/sparks4.ogg', 50, 1) + return + +/obj/machinery/alarm/attackby(obj/item/I, mob/user, params) + add_fingerprint(user) + + switch(buildstage) + if(2) + if(istype(I, /obj/item/card/id) || istype(I, /obj/item/pda))// trying to unlock the interface with an ID card + if(stat & (NOPOWER|BROKEN)) + to_chat(user, "It does nothing") + return + else + if(allowed(usr) && !wires.IsIndexCut(AALARM_WIRE_IDSCAN)) + locked = !locked + to_chat(user, "You [ locked ? "lock" : "unlock"] the Air Alarm interface.") + updateUsrDialog() + else + to_chat(user, "Access denied.") + return + + if(1) + if(iscoil(I)) + var/obj/item/stack/cable_coil/coil = I + if(coil.amount < 5) + to_chat(user, "You need more cable for this!") + return + + to_chat(user, "You wire \the [src]!") + playsound(get_turf(src), coil.usesound, 50, 1) + coil.amount -= 5 + if(!coil.amount) + qdel(coil) + + buildstage = 2 + update_icon() + first_run() + return + if(0) + if(istype(I, /obj/item/airalarm_electronics)) + to_chat(user, "You insert the circuit!") + playsound(get_turf(src), I.usesound, 50, 1) + qdel(I) + buildstage = 1 + update_icon() + return + return ..() + +/obj/machinery/alarm/crowbar_act(mob/user, obj/item/I) + if(buildstage != AIR_ALARM_BUILDING) + return + . = TRUE + if(!I.tool_start_check(user, 0)) + return + to_chat(user, "You start prying out the circuit.") + if(!I.use_tool(src, user, 20, volume = I.tool_volume)) + return + if(buildstage != AIR_ALARM_BUILDING) + return + to_chat(user, "You pry out the circuit!") + new /obj/item/airalarm_electronics(user.drop_location()) + buildstage = AIR_ALARM_FRAME + update_icon() + +/obj/machinery/alarm/multitool_act(mob/user, obj/item/I) + if(buildstage != AIR_ALARM_READY) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(wiresexposed) + attack_hand(user) + +/obj/machinery/alarm/screwdriver_act(mob/user, obj/item/I) + if(buildstage != AIR_ALARM_READY) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + wiresexposed = !wiresexposed + update_icon() + if(wiresexposed) + SCREWDRIVER_OPEN_PANEL_MESSAGE + else + SCREWDRIVER_CLOSE_PANEL_MESSAGE + +/obj/machinery/alarm/wirecutter_act(mob/user, obj/item/I) + if(buildstage != AIR_ALARM_READY) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(wires.wires_status == 31) // all wires cut + var/obj/item/stack/cable_coil/new_coil = new /obj/item/stack/cable_coil(user.drop_location()) + new_coil.amount = 5 + buildstage = AIR_ALARM_BUILDING + update_icon() + if(wiresexposed) + wires.Interact(user) + +/obj/machinery/alarm/wrench_act(mob/user, obj/item/I) + if(buildstage != AIR_ALARM_FRAME) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + new /obj/item/mounted/frame/alarm_frame(get_turf(user)) + WRENCH_UNANCHOR_WALL_MESSAGE + qdel(src) + +/obj/machinery/alarm/power_change() + if(powered(power_channel)) + stat &= ~NOPOWER + else + stat |= NOPOWER + spawn(rand(0,15)) + update_icon() + +/obj/machinery/alarm/obj_break(damage_flag) + ..() + update_icon() + +/obj/machinery/alarm/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + new /obj/item/stack/sheet/metal(loc, 2) + var/obj/item/I = new /obj/item/airalarm_electronics(loc) + if(!disassembled) + I.obj_integrity = I.max_integrity * 0.5 + new /obj/item/stack/cable_coil(loc, 3) + qdel(src) + +/obj/machinery/alarm/examine(mob/user) + . = ..() + if(buildstage < 2) + . += "It is not wired." + if(buildstage < 1) + . += "The circuit is missing." + +/obj/machinery/alarm/all_access + name = "all-access air alarm" + desc = "This particular atmos control unit appears to have no access restrictions." + locked = FALSE + req_access = null + req_one_access = null + +/* +AIR ALARM CIRCUIT +Just an object used in constructing air alarms +*/ +/obj/item/airalarm_electronics + name = "air alarm electronics" + icon = 'icons/obj/doors/door_assembly.dmi' + icon_state = "door_electronics" + desc = "Looks like a circuit. Probably is." + w_class = WEIGHT_CLASS_SMALL + materials = list(MAT_METAL=50, MAT_GLASS=50) + origin_tech = "engineering=2;programming=1" + toolspeed = 1 + usesound = 'sound/items/deconstruct.ogg' + + +#undef AIR_ALARM_FRAME +#undef AIR_ALARM_BUILDING +#undef AIR_ALARM_READY diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm index 3210e899876..efcdada302d 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -1,630 +1,606 @@ -/datum/canister_icons - var - possiblemaincolor = list( //these lists contain the possible colors of a canister - list("name" = "\[N2O\]", "icon" = "redws"), - list("name" = "\[N2\]", "icon" = "red"), - list("name" = "\[O2\]", "icon" = "blue"), - list("name" = "\[Toxin (Bio)\]", "icon" = "orange"), - list("name" = "\[CO2\]", "icon" = "black"), - list("name" = "\[Air\]", "icon" = "grey"), - list("name" = "\[CAUTION\]", "icon" = "yellow"), - list("name" = "\[SPECIAL\]", "icon" = "whiters") - ) - possibleseccolor = list( // no point in having the N2O and "whiters" ones in these lists - list("name" = "\[N2\]", "icon" = "red-c"), - list("name" = "\[O2\]", "icon" = "blue-c"), - list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c"), - list("name" = "\[CO2\]", "icon" = "black-c"), - list("name" = "\[Air\]", "icon" = "grey-c"), - list("name" = "\[CAUTION\]", "icon" = "yellow-c") - ) - possibletertcolor = list( - list("name" = "\[N2\]", "icon" = "red-c-1"), - list("name" = "\[O2\]", "icon" = "blue-c-1"), - list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c-1"), - list("name" = "\[CO2\]", "icon" = "black-c-1"), - list("name" = "\[Air\]", "icon" = "grey-c-1"), - list("name" = "\[CAUTION\]", "icon" = "yellow-c-1") - ) - possiblequartcolor = list( - list("name" = "\[N2\]", "icon" = "red-c-2"), - list("name" = "\[O2\]", "icon" = "blue-c-2"), - list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c-2"), - list("name" = "\[CO2\]", "icon" = "black-c-2"), - list("name" = "\[Air\]", "icon" = "grey-c-2"), - list("name" = "\[CAUTION\]", "icon" = "yellow-c-2") - ) - - possibledecals = list( //var that stores all possible decals, used by ui - list("name" = "Low temperature canister", "icon" = "cold"), - list("name" = "High temperature canister", "icon" = "hot"), - list("name" = "Plasma containing canister", "icon" = "plasma") - ) - -var/datum/canister_icons/canister_icon_container = new() - -/obj/machinery/portable_atmospherics/canister - name = "canister" - icon = 'icons/obj/atmos.dmi' - icon_state = "yellow" - density = 1 - flags = CONDUCT - armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 100, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 50) - max_integrity = 250 - integrity_failure = 100 - - var/menu = 0 - //used by nanoui: 0 = main menu, 1 = relabel - - var/valve_open = 0 - var/release_pressure = ONE_ATMOSPHERE - - var/list/canister_color //variable that stores colours - var/list/decals //list that stores the decals - - //lists for check_change() - var/list/oldcolor - var/list/olddecals - - //passed to the ui to render the color lists - var/list/colorcontainer - var/list/possibledecals - - var/can_label = 1 - var/filled = 0.5 - pressure_resistance = 7 * ONE_ATMOSPHERE - var/temperature_resistance = 1000 + T0C - volume = 1000 - use_power = NO_POWER_USE - interact_offline = 1 - var/release_log = "" - var/busy = 0 - var/update_flag = 0 - -/obj/machinery/portable_atmospherics/canister/New() - ..() - canister_color = list( - "prim" = "yellow", - "sec" = "none", - "ter" = "none", - "quart" = "none") - oldcolor = new /list() - decals = list("cold" = 0, "hot" = 0, "plasma" = 0) - colorcontainer = list() - possibledecals = list() - update_icon() - -/obj/machinery/portable_atmospherics/canister/proc/init_data_vars() - //passed to the ui to render the color lists - colorcontainer = list( - "prim" = list( - "options" = canister_icon_container.possiblemaincolor, - "name" = "Primary color", - ), - "sec" = list( - "options" = canister_icon_container.possibleseccolor, - "name" = "Secondary color", - ), - "ter" = list( - "options" = canister_icon_container.possibletertcolor, - "name" = "Tertiary color", - ), - "quart" = list( - "options" = canister_icon_container.possiblequartcolor, - "name" = "Quaternary color", - ) - ) - - //var/anycolor used by the nanoUI, 0: no color applied. 1: color applied - for(var/C in colorcontainer) - if(C == "prim") continue - var/list/L = colorcontainer[C] - if(!(canister_color[C]) || (canister_color[C] == "none")) - L.Add(list("anycolor" = 0)) - else - L.Add(list("anycolor" = 1)) - colorcontainer[C] = L - - possibledecals = list() - - var/i - var/list/L = canister_icon_container.possibledecals - for(i=1;i<=L.len;i++) - var/list/LL = L[i] - LL = LL.Copy() //make sure we don't edit the datum list - LL.Add(list("active" = decals[LL["icon"]])) //"active" used by nanoUI - possibledecals.Add(LL) - -/obj/machinery/portable_atmospherics/canister/proc/check_change() - var/old_flag = update_flag - update_flag = 0 - if(holding) - update_flag |= 1 - if(connected_port) - update_flag |= 2 - - var/tank_pressure = air_contents.return_pressure() - if(tank_pressure < 10) - update_flag |= 4 - else if(tank_pressure < ONE_ATMOSPHERE) - update_flag |= 8 - else if(tank_pressure < 15*ONE_ATMOSPHERE) - update_flag |= 16 - else - update_flag |= 32 - - if(list2params(oldcolor) != list2params(canister_color)) - update_flag |= 64 - oldcolor = canister_color.Copy() - - if(list2params(olddecals) != list2params(decals)) - update_flag |= 128 - olddecals = decals.Copy() - - if(update_flag == old_flag) - return 1 - else - return 0 - -/obj/machinery/portable_atmospherics/canister/update_icon() -/* -update_flag -1 = holding -2 = connected_port -4 = tank_pressure < 10 -8 = tank_pressure < ONE_ATMOS -16 = tank_pressure < 15*ONE_ATMOS -32 = tank_pressure go boom. -64 = colors -128 = decals -(note: colors and decals has to be applied every icon update) -*/ - - if(src.destroyed) - src.overlays = 0 - src.icon_state = text("[]-1", src.canister_color["prim"])//yes, I KNOW the colours don't reflect when the can's borked, whatever. - return - - if(icon_state != src.canister_color["prim"]) - icon_state = src.canister_color["prim"] - - if(check_change()) //Returns 1 if no change needed to icons. - return - - overlays.Cut() - - for(var/C in canister_color) - if(C == "prim") continue - if(canister_color[C] == "none") continue - overlays.Add(canister_color[C]) - - for(var/D in decals) - if(decals[D]) - overlays.Add("decal-" + D) - - if(update_flag & 1) - overlays += "can-open" - if(update_flag & 2) - overlays += "can-connector" - if(update_flag & 4) - overlays += "can-o0" - if(update_flag & 8) - overlays += "can-o1" - else if(update_flag & 16) - overlays += "can-o2" - else if(update_flag & 32) - overlays += "can-o3" - - update_flag &= ~196 //the flags 128 and 64 represent change, not states. As such, we have to reset them to be able to detect a change on the next go. - return - -//template modification exploit prevention, used in Topic() -/obj/machinery/portable_atmospherics/canister/proc/is_a_color(var/inputVar, var/checkColor = "all") - if(checkColor == "prim" || checkColor == "all") - for(var/list/L in canister_icon_container.possiblemaincolor) - if(L["icon"] == inputVar) - return 1 - if(checkColor == "sec" || checkColor == "all") - for(var/list/L in canister_icon_container.possibleseccolor) - if(L["icon"] == inputVar) - return 1 - if(checkColor == "ter" || checkColor == "all") - for(var/list/L in canister_icon_container.possibletertcolor) - if(L["icon"] == inputVar) - return 1 - if(checkColor == "quart" || checkColor == "all") - for(var/list/L in canister_icon_container.possiblequartcolor) - if(L["icon"] == inputVar) - return 1 - return 0 - -/obj/machinery/portable_atmospherics/canister/proc/is_a_decal(var/inputVar) - for(var/list/L in canister_icon_container.possibledecals) - if(L["icon"] == inputVar) - return 1 - return 0 - -/obj/machinery/portable_atmospherics/canister/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - ..() - if(exposed_temperature > temperature_resistance) - take_damage(5, BURN, 0) - -/obj/machinery/portable_atmospherics/canister/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - if(!(stat & BROKEN)) - canister_break() - if(disassembled) - new /obj/item/stack/sheet/metal (loc, 10) - else - new /obj/item/stack/sheet/metal (loc, 5) - qdel(src) - -/obj/machinery/portable_atmospherics/canister/obj_break(damage_flag) - if((stat & BROKEN) || (flags & NODECONSTRUCT)) - return - canister_break() - -/obj/machinery/portable_atmospherics/canister/attackby(obj/item/I, mob/user, params) - if(user.a_intent != INTENT_HARM && iswelder(I)) - var/obj/item/weldingtool/WT = I - if(stat & BROKEN) - if(!WT.remove_fuel(0, user)) - return - playsound(loc, WT.usesound, 40, 1) - to_chat(user, "You begin cutting [src] apart...") - if(do_after(user, 30, target = src)) - deconstruct(TRUE) - else - to_chat(user, "You cannot slice [src] apart when it isn't broken.") - return TRUE - else - return ..() - -/obj/machinery/portable_atmospherics/canister/proc/canister_break() - disconnect() - var/datum/gas_mixture/expelled_gas = air_contents.remove(air_contents.total_moles()) - var/turf/T = get_turf(src) - T.assume_air(expelled_gas) - air_update_turf() - - stat |= BROKEN - density = FALSE - playsound(src.loc, 'sound/effects/spray.ogg', 10, TRUE, -3) - update_icon() - - if(holding) - holding.forceMove(T) - holding = null - -/obj/machinery/portable_atmospherics/canister/process_atmos() - if(destroyed) - return - - ..() - - if(valve_open) - var/datum/gas_mixture/environment - if(holding) - environment = holding.air_contents - else - environment = loc.return_air() - - var/env_pressure = environment.return_pressure() - var/pressure_delta = min(release_pressure - env_pressure, (air_contents.return_pressure() - env_pressure)/2) - //Can not have a pressure delta that would cause environment pressure > tank pressure - - var/transfer_moles = 0 - if((air_contents.temperature > 0) && (pressure_delta > 0)) - transfer_moles = pressure_delta*environment.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION) - - //Actually transfer the gas - var/datum/gas_mixture/removed = air_contents.remove(transfer_moles) - - if(holding) - environment.merge(removed) - else - loc.assume_air(removed) - air_update_turf() - src.update_icon() - - - if(air_contents.return_pressure() < 1) - can_label = 1 - else - can_label = 0 - - src.updateDialog() - return - -/obj/machinery/portable_atmospherics/canister/return_air() - return air_contents - -/obj/machinery/portable_atmospherics/canister/proc/return_temperature() - var/datum/gas_mixture/GM = src.return_air() - if(GM && GM.volume>0) - return GM.temperature - return 0 - -/obj/machinery/portable_atmospherics/canister/proc/return_pressure() - var/datum/gas_mixture/GM = src.return_air() - if(GM && GM.volume>0) - return GM.return_pressure() - return 0 - -/obj/machinery/portable_atmospherics/canister/replace_tank(mob/living/user, close_valve) - . = ..() - if(.) - if(close_valve) - valve_open = FALSE - update_icon() - investigate_log("Valve was closed by [key_name(user)].
        ", "atmos") - else if(valve_open && holding) - investigate_log("[key_name(user)] started a transfer into [holding].
        ", "atmos") - -/obj/machinery/portable_atmospherics/canister/attack_ai(var/mob/user as mob) - src.add_hiddenprint(user) - return src.attack_hand(user) - -/obj/machinery/portable_atmospherics/canister/attack_ghost(var/mob/user as mob) - return src.ui_interact(user) - -/obj/machinery/portable_atmospherics/canister/attack_hand(var/mob/user as mob) - return src.ui_interact(user) - -/obj/machinery/portable_atmospherics/canister/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = physical_state) - if(src.destroyed) - return - - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "canister.tmpl", "Canister", 480, 400, state = state) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - - -/obj/machinery/portable_atmospherics/canister/ui_data(mob/user, datum/topic_state/state) - init_data_vars() //set up var/colorcontainer and var/possibledecals - - // this is the data which will be sent to the ui - var/data[0] - data["name"] = name - data["menu"] = menu ? 1 : 0 - data["canLabel"] = can_label ? 1 : 0 - data["canister_color"] = canister_color - data["colorContainer"] = colorcontainer.Copy() - colorcontainer.Cut() - data["possibleDecals"] = possibledecals.Copy() - possibledecals.Cut() - data["portConnected"] = connected_port ? 1 : 0 - data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) - data["releasePressure"] = round(release_pressure ? release_pressure : 0) - data["minReleasePressure"] = round(ONE_ATMOSPHERE/10) - data["maxReleasePressure"] = round(10*ONE_ATMOSPHERE) - data["valveOpen"] = valve_open ? 1 : 0 - - data["hasHoldingTank"] = holding ? 1 : 0 - if(holding) - data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure())) - - return data - -/obj/machinery/portable_atmospherics/canister/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["choice"] == "menu") - menu = text2num(href_list["mode_target"]) - - if(href_list["toggle"]) - var/logmsg - if(valve_open) - if(holding) - logmsg = "Valve was closed by [key_name(usr)], stopping the transfer into the [holding]
        " - else - logmsg = "Valve was closed by [key_name(usr)], stopping the transfer into the air
        " - else - if(holding) - logmsg = "Valve was opened by [key_name(usr)], starting the transfer into the [holding]
        " - else - logmsg = "Valve was opened by [key_name(usr)], starting the transfer into the air
        " - if(air_contents.toxins > 0) - message_admins("[key_name_admin(usr)] opened a canister that contains plasma in [get_area(src)]! (JMP)") - log_admin("[key_name(usr)] opened a canister that contains plasma at [get_area(src)]: [x], [y], [z]") - var/datum/gas/sleeping_agent = locate(/datum/gas/sleeping_agent) in air_contents.trace_gases - if(sleeping_agent && (sleeping_agent.moles > 1)) - message_admins("[key_name_admin(usr)] opened a canister that contains N2O in [get_area(src)]! (JMP)") - log_admin("[key_name(usr)] opened a canister that contains N2O at [get_area(src)]: [x], [y], [z]") - investigate_log(logmsg, "atmos") - release_log += logmsg - valve_open = !valve_open - - if(href_list["remove_tank"]) - if(holding) - if(valve_open) - valve_open = 0 - release_log += "Valve was closed by [key_name(usr)], stopping the transfer into the [holding]
        " - holding.loc = loc - holding = null - - if(href_list["pressure_adj"]) - var/diff = text2num(href_list["pressure_adj"]) - if(diff > 0) - release_pressure = min(10*ONE_ATMOSPHERE, release_pressure+diff) - else - release_pressure = max(ONE_ATMOSPHERE/10, release_pressure+diff) - - if(href_list["rename"]) - if(can_label) - var/T = sanitize(copytext(input("Choose canister label", "Name", name) as text|null,1,MAX_NAME_LEN)) - if(can_label) //Exploit prevention - if(T) - name = T - else - name = "canister" - else - to_chat(usr, "As you attempted to rename it the pressure rose!") - - if(href_list["choice"] == "Primary color") - if(is_a_color(href_list["icon"],"prim")) - canister_color["prim"] = href_list["icon"] - if(href_list["choice"] == "Secondary color") - if(href_list["icon"] == "none") - canister_color["sec"] = "none" - else if(is_a_color(href_list["icon"],"sec")) - canister_color["sec"] = href_list["icon"] - if(href_list["choice"] == "Tertiary color") - if(href_list["icon"] == "none") - canister_color["ter"] = "none" - else if(is_a_color(href_list["icon"],"ter")) - canister_color["ter"] = href_list["icon"] - if(href_list["choice"] == "Quaternary color") - if(href_list["icon"] == "none") - canister_color["quart"] = "none" - else if(is_a_color(href_list["icon"],"quart")) - canister_color["quart"] = href_list["icon"] - - if(href_list["choice"] == "decals") - if(is_a_decal(href_list["icon"])) - decals[href_list["icon"]] = (decals[href_list["icon"]] == 0) - - src.add_fingerprint(usr) - update_icon() - - return 1 - - -/obj/machinery/portable_atmospherics/canister/toxins - name = "Canister \[Toxin (Plasma)\]" - icon_state = "orange" //See New() - can_label = 0 -/obj/machinery/portable_atmospherics/canister/oxygen - name = "Canister: \[O2\]" - icon_state = "blue" //See New() - can_label = 0 -/obj/machinery/portable_atmospherics/canister/sleeping_agent - name = "Canister: \[N2O\]" - icon_state = "redws" //See New() - can_label = 0 -/obj/machinery/portable_atmospherics/canister/nitrogen - name = "Canister: \[N2\]" - icon_state = "red" //See New() - can_label = 0 -/obj/machinery/portable_atmospherics/canister/carbon_dioxide - name = "Canister \[CO2\]" - icon_state = "black" //See New() - can_label = 0 -/obj/machinery/portable_atmospherics/canister/air - name = "Canister \[Air\]" - icon_state = "grey" //See New() - can_label = 0 -/obj/machinery/portable_atmospherics/canister/custom_mix - name = "Canister \[Custom\]" - icon_state = "whiters" //See New() - can_label = 0 - - -/obj/machinery/portable_atmospherics/canister/toxins/New() - ..() - - canister_color["prim"] = "orange" - decals["plasma"] = 1 - src.air_contents.toxins = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - - src.update_icon() - return 1 - -/obj/machinery/portable_atmospherics/canister/oxygen/New() - ..() - - canister_color["prim"] = "blue" - src.air_contents.oxygen = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - - src.update_icon() - return 1 - -/obj/machinery/portable_atmospherics/canister/sleeping_agent/New() - ..() - - canister_color["prim"] = "redws" - var/datum/gas/sleeping_agent/trace_gas = new - air_contents.trace_gases += trace_gas - trace_gas.moles = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - - src.update_icon() - return 1 - - -//Dirty way to fill room with gas. However it is a bit easier to do than creating some floor/engine/n2o -rastaf0 -/obj/machinery/portable_atmospherics/canister/sleeping_agent/roomfiller/New() - ..() - var/datum/gas/sleeping_agent/trace_gas = air_contents.trace_gases[1] - trace_gas.moles = 9*4000 - spawn(100) - var/turf/simulated/location = src.loc - if(istype(src.loc)) - while(!location.air) - sleep(1000) - location.assume_air(air_contents) - air_contents = new - return 1 - - -/obj/machinery/portable_atmospherics/canister/nitrogen/New() - ..() - - canister_color["prim"] = "red" - src.air_contents.nitrogen = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - - src.update_icon() - return 1 - -/obj/machinery/portable_atmospherics/canister/carbon_dioxide/New() - ..() - - canister_color["prim"] = "black" - src.air_contents.carbon_dioxide = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - - src.update_icon() - return 1 - - -/obj/machinery/portable_atmospherics/canister/air/New() - ..() - - canister_color["prim"] = "grey" - src.air_contents.oxygen = (O2STANDARD*src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - src.air_contents.nitrogen = (N2STANDARD*src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) - - src.update_icon() - return 1 - -/obj/machinery/portable_atmospherics/canister/custom_mix/New() - ..() - - canister_color["prim"] = "whiters" - src.update_icon() // Otherwise new canisters do not have their icon updated with the pressure light, likely want to add this to the canister class constructor, avoiding at current time to refrain from screwing up code for other canisters. --DZD - return 1 - -/obj/machinery/portable_atmospherics/canister/proc/weld(var/obj/item/weldingtool/WT, var/mob/user) - - if(busy) - return 0 - if(!WT.remove_fuel(0, user)) - return 0 - - // Do after stuff here - to_chat(user, "You start to slice away at \the [src]...") - playsound(src.loc, WT.usesound, 50, 1) - busy = 1 - if(do_after(user, 50 * WT.toolspeed, target = src)) - busy = 0 - if(!WT.isOn()) - return 0 - return 1 - busy = 0 - return 0 +/datum/canister_icons + var + possiblemaincolor = list( //these lists contain the possible colors of a canister + list("name" = "\[N2O\]", "icon" = "redws"), + list("name" = "\[N2\]", "icon" = "red"), + list("name" = "\[O2\]", "icon" = "blue"), + list("name" = "\[Toxin (Bio)\]", "icon" = "orange"), + list("name" = "\[CO2\]", "icon" = "black"), + list("name" = "\[Air\]", "icon" = "grey"), + list("name" = "\[CAUTION\]", "icon" = "yellow"), + list("name" = "\[SPECIAL\]", "icon" = "whiters") + ) + possibleseccolor = list( // no point in having the N2O and "whiters" ones in these lists + list("name" = "\[N2\]", "icon" = "red-c"), + list("name" = "\[O2\]", "icon" = "blue-c"), + list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c"), + list("name" = "\[CO2\]", "icon" = "black-c"), + list("name" = "\[Air\]", "icon" = "grey-c"), + list("name" = "\[CAUTION\]", "icon" = "yellow-c") + ) + possibletertcolor = list( + list("name" = "\[N2\]", "icon" = "red-c-1"), + list("name" = "\[O2\]", "icon" = "blue-c-1"), + list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c-1"), + list("name" = "\[CO2\]", "icon" = "black-c-1"), + list("name" = "\[Air\]", "icon" = "grey-c-1"), + list("name" = "\[CAUTION\]", "icon" = "yellow-c-1") + ) + possiblequartcolor = list( + list("name" = "\[N2\]", "icon" = "red-c-2"), + list("name" = "\[O2\]", "icon" = "blue-c-2"), + list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c-2"), + list("name" = "\[CO2\]", "icon" = "black-c-2"), + list("name" = "\[Air\]", "icon" = "grey-c-2"), + list("name" = "\[CAUTION\]", "icon" = "yellow-c-2") + ) + + possibledecals = list( //var that stores all possible decals, used by ui + list("name" = "Low temperature canister", "icon" = "cold"), + list("name" = "High temperature canister", "icon" = "hot"), + list("name" = "Plasma containing canister", "icon" = "plasma") + ) + +var/datum/canister_icons/canister_icon_container = new() + +/obj/machinery/portable_atmospherics/canister + name = "canister" + icon = 'icons/obj/atmos.dmi' + icon_state = "yellow" + density = 1 + flags = CONDUCT + armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 100, "bomb" = 10, "bio" = 100, "rad" = 100, "fire" = 80, "acid" = 50) + max_integrity = 250 + integrity_failure = 100 + + var/menu = 0 + //used by nanoui: 0 = main menu, 1 = relabel + + var/valve_open = 0 + var/release_pressure = ONE_ATMOSPHERE + + var/list/canister_color //variable that stores colours + var/list/decals //list that stores the decals + + //lists for check_change() + var/list/oldcolor + var/list/olddecals + + //passed to the ui to render the color lists + var/list/colorcontainer + var/list/possibledecals + + var/can_label = 1 + var/filled = 0.5 + pressure_resistance = 7 * ONE_ATMOSPHERE + var/temperature_resistance = 1000 + T0C + volume = 1000 + use_power = NO_POWER_USE + interact_offline = 1 + var/release_log = "" + var/update_flag = 0 + +/obj/machinery/portable_atmospherics/canister/New() + ..() + canister_color = list( + "prim" = "yellow", + "sec" = "none", + "ter" = "none", + "quart" = "none") + oldcolor = new /list() + decals = list("cold" = 0, "hot" = 0, "plasma" = 0) + colorcontainer = list() + possibledecals = list() + update_icon() + +/obj/machinery/portable_atmospherics/canister/proc/init_data_vars() + //passed to the ui to render the color lists + colorcontainer = list( + "prim" = list( + "options" = canister_icon_container.possiblemaincolor, + "name" = "Primary color", + ), + "sec" = list( + "options" = canister_icon_container.possibleseccolor, + "name" = "Secondary color", + ), + "ter" = list( + "options" = canister_icon_container.possibletertcolor, + "name" = "Tertiary color", + ), + "quart" = list( + "options" = canister_icon_container.possiblequartcolor, + "name" = "Quaternary color", + ) + ) + + //var/anycolor used by the nanoUI, 0: no color applied. 1: color applied + for(var/C in colorcontainer) + if(C == "prim") continue + var/list/L = colorcontainer[C] + if(!(canister_color[C]) || (canister_color[C] == "none")) + L.Add(list("anycolor" = 0)) + else + L.Add(list("anycolor" = 1)) + colorcontainer[C] = L + + possibledecals = list() + + var/i + var/list/L = canister_icon_container.possibledecals + for(i=1;i<=L.len;i++) + var/list/LL = L[i] + LL = LL.Copy() //make sure we don't edit the datum list + LL.Add(list("active" = decals[LL["icon"]])) //"active" used by nanoUI + possibledecals.Add(LL) + +/obj/machinery/portable_atmospherics/canister/proc/check_change() + var/old_flag = update_flag + update_flag = 0 + if(holding) + update_flag |= 1 + if(connected_port) + update_flag |= 2 + + var/tank_pressure = air_contents.return_pressure() + if(tank_pressure < 10) + update_flag |= 4 + else if(tank_pressure < ONE_ATMOSPHERE) + update_flag |= 8 + else if(tank_pressure < 15*ONE_ATMOSPHERE) + update_flag |= 16 + else + update_flag |= 32 + + if(list2params(oldcolor) != list2params(canister_color)) + update_flag |= 64 + oldcolor = canister_color.Copy() + + if(list2params(olddecals) != list2params(decals)) + update_flag |= 128 + olddecals = decals.Copy() + + if(update_flag == old_flag) + return 1 + else + return 0 + +/obj/machinery/portable_atmospherics/canister/update_icon() +/* +update_flag +1 = holding +2 = connected_port +4 = tank_pressure < 10 +8 = tank_pressure < ONE_ATMOS +16 = tank_pressure < 15*ONE_ATMOS +32 = tank_pressure go boom. +64 = colors +128 = decals +(note: colors and decals has to be applied every icon update) +*/ + + if(src.destroyed) + src.overlays = 0 + src.icon_state = text("[]-1", src.canister_color["prim"])//yes, I KNOW the colours don't reflect when the can's borked, whatever. + return + + if(icon_state != src.canister_color["prim"]) + icon_state = src.canister_color["prim"] + + if(check_change()) //Returns 1 if no change needed to icons. + return + + overlays.Cut() + + for(var/C in canister_color) + if(C == "prim") continue + if(canister_color[C] == "none") continue + overlays.Add(canister_color[C]) + + for(var/D in decals) + if(decals[D]) + overlays.Add("decal-" + D) + + if(update_flag & 1) + overlays += "can-open" + if(update_flag & 2) + overlays += "can-connector" + if(update_flag & 4) + overlays += "can-o0" + if(update_flag & 8) + overlays += "can-o1" + else if(update_flag & 16) + overlays += "can-o2" + else if(update_flag & 32) + overlays += "can-o3" + + update_flag &= ~196 //the flags 128 and 64 represent change, not states. As such, we have to reset them to be able to detect a change on the next go. + return + +//template modification exploit prevention, used in Topic() +/obj/machinery/portable_atmospherics/canister/proc/is_a_color(var/inputVar, var/checkColor = "all") + if(checkColor == "prim" || checkColor == "all") + for(var/list/L in canister_icon_container.possiblemaincolor) + if(L["icon"] == inputVar) + return 1 + if(checkColor == "sec" || checkColor == "all") + for(var/list/L in canister_icon_container.possibleseccolor) + if(L["icon"] == inputVar) + return 1 + if(checkColor == "ter" || checkColor == "all") + for(var/list/L in canister_icon_container.possibletertcolor) + if(L["icon"] == inputVar) + return 1 + if(checkColor == "quart" || checkColor == "all") + for(var/list/L in canister_icon_container.possiblequartcolor) + if(L["icon"] == inputVar) + return 1 + return 0 + +/obj/machinery/portable_atmospherics/canister/proc/is_a_decal(var/inputVar) + for(var/list/L in canister_icon_container.possibledecals) + if(L["icon"] == inputVar) + return 1 + return 0 + +/obj/machinery/portable_atmospherics/canister/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + ..() + if(exposed_temperature > temperature_resistance) + take_damage(5, BURN, 0) + +/obj/machinery/portable_atmospherics/canister/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + if(!(stat & BROKEN)) + canister_break() + if(disassembled) + new /obj/item/stack/sheet/metal (loc, 10) + else + new /obj/item/stack/sheet/metal (loc, 5) + qdel(src) + +/obj/machinery/portable_atmospherics/canister/obj_break(damage_flag) + if((stat & BROKEN) || (flags & NODECONSTRUCT)) + return + canister_break() + +/obj/machinery/portable_atmospherics/canister/proc/canister_break() + disconnect() + var/datum/gas_mixture/expelled_gas = air_contents.remove(air_contents.total_moles()) + var/turf/T = get_turf(src) + T.assume_air(expelled_gas) + air_update_turf() + + stat |= BROKEN + density = FALSE + playsound(src.loc, 'sound/effects/spray.ogg', 10, TRUE, -3) + update_icon() + + if(holding) + holding.forceMove(T) + holding = null + +/obj/machinery/portable_atmospherics/canister/process_atmos() + if(destroyed) + return + + ..() + + if(valve_open) + var/datum/gas_mixture/environment + if(holding) + environment = holding.air_contents + else + environment = loc.return_air() + + var/env_pressure = environment.return_pressure() + var/pressure_delta = min(release_pressure - env_pressure, (air_contents.return_pressure() - env_pressure)/2) + //Can not have a pressure delta that would cause environment pressure > tank pressure + + var/transfer_moles = 0 + if((air_contents.temperature > 0) && (pressure_delta > 0)) + transfer_moles = pressure_delta*environment.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION) + + //Actually transfer the gas + var/datum/gas_mixture/removed = air_contents.remove(transfer_moles) + + if(holding) + environment.merge(removed) + else + loc.assume_air(removed) + air_update_turf() + src.update_icon() + + + if(air_contents.return_pressure() < 1) + can_label = 1 + else + can_label = 0 + + src.updateDialog() + return + +/obj/machinery/portable_atmospherics/canister/return_air() + return air_contents + +/obj/machinery/portable_atmospherics/canister/proc/return_temperature() + var/datum/gas_mixture/GM = src.return_air() + if(GM && GM.volume>0) + return GM.temperature + return 0 + +/obj/machinery/portable_atmospherics/canister/proc/return_pressure() + var/datum/gas_mixture/GM = src.return_air() + if(GM && GM.volume>0) + return GM.return_pressure() + return 0 + +/obj/machinery/portable_atmospherics/canister/replace_tank(mob/living/user, close_valve) + . = ..() + if(.) + if(close_valve) + valve_open = FALSE + update_icon() + investigate_log("Valve was closed by [key_name(user)].
        ", "atmos") + else if(valve_open && holding) + investigate_log("[key_name(user)] started a transfer into [holding].
        ", "atmos") + +/obj/machinery/portable_atmospherics/canister/attack_ai(var/mob/user as mob) + src.add_hiddenprint(user) + return src.attack_hand(user) + +/obj/machinery/portable_atmospherics/canister/attack_ghost(var/mob/user as mob) + return src.ui_interact(user) + +/obj/machinery/portable_atmospherics/canister/attack_hand(var/mob/user as mob) + return src.ui_interact(user) + +/obj/machinery/portable_atmospherics/canister/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = physical_state) + if(src.destroyed) + return + + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "canister.tmpl", "Canister", 480, 400, state = state) + // open the new ui window + ui.open() + // auto update every Master Controller tick + ui.set_auto_update(1) + + +/obj/machinery/portable_atmospherics/canister/ui_data(mob/user, datum/topic_state/state) + init_data_vars() //set up var/colorcontainer and var/possibledecals + + // this is the data which will be sent to the ui + var/data[0] + data["name"] = name + data["menu"] = menu ? 1 : 0 + data["canLabel"] = can_label ? 1 : 0 + data["canister_color"] = canister_color + data["colorContainer"] = colorcontainer.Copy() + colorcontainer.Cut() + data["possibleDecals"] = possibledecals.Copy() + possibledecals.Cut() + data["portConnected"] = connected_port ? 1 : 0 + data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0) + data["releasePressure"] = round(release_pressure ? release_pressure : 0) + data["minReleasePressure"] = round(ONE_ATMOSPHERE/10) + data["maxReleasePressure"] = round(10*ONE_ATMOSPHERE) + data["valveOpen"] = valve_open ? 1 : 0 + + data["hasHoldingTank"] = holding ? 1 : 0 + if(holding) + data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure())) + + return data + +/obj/machinery/portable_atmospherics/canister/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["choice"] == "menu") + menu = text2num(href_list["mode_target"]) + + if(href_list["toggle"]) + var/logmsg + if(valve_open) + if(holding) + logmsg = "Valve was closed by [key_name(usr)], stopping the transfer into the [holding]
        " + else + logmsg = "Valve was closed by [key_name(usr)], stopping the transfer into the air
        " + else + if(holding) + logmsg = "Valve was opened by [key_name(usr)], starting the transfer into the [holding]
        " + else + logmsg = "Valve was opened by [key_name(usr)], starting the transfer into the air
        " + if(air_contents.toxins > 0) + message_admins("[key_name_admin(usr)] opened a canister that contains plasma in [get_area(src)]! (JMP)") + log_admin("[key_name(usr)] opened a canister that contains plasma at [get_area(src)]: [x], [y], [z]") + var/datum/gas/sleeping_agent = locate(/datum/gas/sleeping_agent) in air_contents.trace_gases + if(sleeping_agent && (sleeping_agent.moles > 1)) + message_admins("[key_name_admin(usr)] opened a canister that contains N2O in [get_area(src)]! (JMP)") + log_admin("[key_name(usr)] opened a canister that contains N2O at [get_area(src)]: [x], [y], [z]") + investigate_log(logmsg, "atmos") + release_log += logmsg + valve_open = !valve_open + + if(href_list["remove_tank"]) + if(holding) + if(valve_open) + valve_open = 0 + release_log += "Valve was closed by [key_name(usr)], stopping the transfer into the [holding]
        " + holding.loc = loc + holding = null + + if(href_list["pressure_adj"]) + var/diff = text2num(href_list["pressure_adj"]) + if(diff > 0) + release_pressure = min(10*ONE_ATMOSPHERE, release_pressure+diff) + else + release_pressure = max(ONE_ATMOSPHERE/10, release_pressure+diff) + + if(href_list["rename"]) + if(can_label) + var/T = sanitize(copytext(input("Choose canister label", "Name", name) as text|null,1,MAX_NAME_LEN)) + if(can_label) //Exploit prevention + if(T) + name = T + else + name = "canister" + else + to_chat(usr, "As you attempted to rename it the pressure rose!") + + if(href_list["choice"] == "Primary color") + if(is_a_color(href_list["icon"],"prim")) + canister_color["prim"] = href_list["icon"] + if(href_list["choice"] == "Secondary color") + if(href_list["icon"] == "none") + canister_color["sec"] = "none" + else if(is_a_color(href_list["icon"],"sec")) + canister_color["sec"] = href_list["icon"] + if(href_list["choice"] == "Tertiary color") + if(href_list["icon"] == "none") + canister_color["ter"] = "none" + else if(is_a_color(href_list["icon"],"ter")) + canister_color["ter"] = href_list["icon"] + if(href_list["choice"] == "Quaternary color") + if(href_list["icon"] == "none") + canister_color["quart"] = "none" + else if(is_a_color(href_list["icon"],"quart")) + canister_color["quart"] = href_list["icon"] + + if(href_list["choice"] == "decals") + if(is_a_decal(href_list["icon"])) + decals[href_list["icon"]] = (decals[href_list["icon"]] == 0) + + src.add_fingerprint(usr) + update_icon() + + return 1 + + +/obj/machinery/portable_atmospherics/canister/toxins + name = "Canister \[Toxin (Plasma)\]" + icon_state = "orange" //See New() + can_label = 0 +/obj/machinery/portable_atmospherics/canister/oxygen + name = "Canister: \[O2\]" + icon_state = "blue" //See New() + can_label = 0 +/obj/machinery/portable_atmospherics/canister/sleeping_agent + name = "Canister: \[N2O\]" + icon_state = "redws" //See New() + can_label = 0 +/obj/machinery/portable_atmospherics/canister/nitrogen + name = "Canister: \[N2\]" + icon_state = "red" //See New() + can_label = 0 +/obj/machinery/portable_atmospherics/canister/carbon_dioxide + name = "Canister \[CO2\]" + icon_state = "black" //See New() + can_label = 0 +/obj/machinery/portable_atmospherics/canister/air + name = "Canister \[Air\]" + icon_state = "grey" //See New() + can_label = 0 +/obj/machinery/portable_atmospherics/canister/custom_mix + name = "Canister \[Custom\]" + icon_state = "whiters" //See New() + can_label = 0 + + +/obj/machinery/portable_atmospherics/canister/toxins/New() + ..() + + canister_color["prim"] = "orange" + decals["plasma"] = 1 + src.air_contents.toxins = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/oxygen/New() + ..() + + canister_color["prim"] = "blue" + src.air_contents.oxygen = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/sleeping_agent/New() + ..() + + canister_color["prim"] = "redws" + var/datum/gas/sleeping_agent/trace_gas = new + air_contents.trace_gases += trace_gas + trace_gas.moles = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + + src.update_icon() + return 1 + + +//Dirty way to fill room with gas. However it is a bit easier to do than creating some floor/engine/n2o -rastaf0 +/obj/machinery/portable_atmospherics/canister/sleeping_agent/roomfiller/New() + ..() + var/datum/gas/sleeping_agent/trace_gas = air_contents.trace_gases[1] + trace_gas.moles = 9*4000 + spawn(100) + var/turf/simulated/location = src.loc + if(istype(src.loc)) + while(!location.air) + sleep(1000) + location.assume_air(air_contents) + air_contents = new + return 1 + + +/obj/machinery/portable_atmospherics/canister/nitrogen/New() + ..() + + canister_color["prim"] = "red" + src.air_contents.nitrogen = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/carbon_dioxide/New() + ..() + + canister_color["prim"] = "black" + src.air_contents.carbon_dioxide = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + + src.update_icon() + return 1 + + +/obj/machinery/portable_atmospherics/canister/air/New() + ..() + + canister_color["prim"] = "grey" + src.air_contents.oxygen = (O2STANDARD*src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + src.air_contents.nitrogen = (N2STANDARD*src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature) + + src.update_icon() + return 1 + +/obj/machinery/portable_atmospherics/canister/custom_mix/New() + ..() + + canister_color["prim"] = "whiters" + src.update_icon() // Otherwise new canisters do not have their icon updated with the pressure light, likely want to add this to the canister class constructor, avoiding at current time to refrain from screwing up code for other canisters. --DZD + return 1 + +/obj/machinery/portable_atmospherics/canister/welder_act(mob/user, obj/item/I) + if(!(stat & BROKEN)) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_SLICING_MESSAGE + if(I.use_tool(src, user, 50, volume = I.tool_volume)) + to_chat(user, "You salvage whats left of [src]!") + new /obj/item/stack/sheet/metal(drop_location(), 3) + qdel(src) diff --git a/code/game/machinery/atmoalter/meter.dm b/code/game/machinery/atmoalter/meter.dm index 90601f61bfb..a20b47b30db 100644 --- a/code/game/machinery/atmoalter/meter.dm +++ b/code/game/machinery/atmoalter/meter.dm @@ -1,174 +1,174 @@ -/obj/machinery/meter - name = "gas flow meter" - desc = "It measures something." - icon = 'icons/obj/meter.dmi' - icon_state = "meterX" - - layer = GAS_PUMP_LAYER - - var/obj/machinery/atmospherics/pipe/target = null - anchored = TRUE - max_integrity = 150 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 40, "acid" = 0) - power_channel = ENVIRON - var/frequency = ATMOS_DISTRO_FREQ - var/id - var/id_tag - use_power = IDLE_POWER_USE - idle_power_usage = 2 - active_power_usage = 5 - req_one_access_txt = "24;10" - Mtoollink = TRUE - settagwhitelist = list("id_tag") - -/obj/machinery/meter/New() - ..() - SSair.atmos_machinery += src - target = locate(/obj/machinery/atmospherics/pipe) in loc - if(id && !id_tag)//i'm not dealing with further merge conflicts, fuck it - id_tag = id - return 1 - -/obj/machinery/meter/Destroy() - SSair.atmos_machinery -= src - target = null - return ..() - -/obj/machinery/meter/Initialize() - ..() - if(!target) - target = locate(/obj/machinery/atmospherics/pipe) in loc - -/obj/machinery/meter/process_atmos() - if(!target) - icon_state = "meterX" - return 0 - - if(stat & (BROKEN|NOPOWER)) - icon_state = "meter0" - return 0 - - var/datum/gas_mixture/environment = target.return_air() - if(!environment) - icon_state = "meterX" - return 0 - - var/env_pressure = environment.return_pressure() - if(env_pressure <= 0.15*ONE_ATMOSPHERE) - icon_state = "meter0" - else if(env_pressure <= 1.8*ONE_ATMOSPHERE) - var/val = round(env_pressure/(ONE_ATMOSPHERE*0.3) + 0.5) - icon_state = "meter1_[val]" - else if(env_pressure <= 30*ONE_ATMOSPHERE) - var/val = round(env_pressure/(ONE_ATMOSPHERE*5)-0.35) + 1 - icon_state = "meter2_[val]" - else if(env_pressure <= 59*ONE_ATMOSPHERE) - var/val = round(env_pressure/(ONE_ATMOSPHERE*5) - 6) + 1 - icon_state = "meter3_[val]" - else - icon_state = "meter4" - - if(frequency) - var/datum/radio_frequency/radio_connection = SSradio.return_frequency(frequency) - - if(!radio_connection) return - - var/datum/signal/signal = new - signal.source = src - signal.transmission_method = 1 - signal.data = list( - "tag" = id_tag, - "device" = "AM", - "pressure" = round(env_pressure), - "sigtype" = "status" - ) - radio_connection.post_signal(src, signal) - -/obj/machinery/meter/proc/status() - var/t = "" - if(target) - var/datum/gas_mixture/environment = target.return_air() - if(environment) - t += "The pressure gauge reads [round(environment.return_pressure(), 0.01)] kPa; [round(environment.temperature,0.01)]°K ([round(environment.temperature-T0C,0.01)]°C)" - else - t += "The sensor error light is blinking." - else - t += "The connect error light is blinking." - return t - -/obj/machinery/meter/examine(mob/user) - var/t = "A gas flow meter. " - - if(get_dist(user, src) > 3 && !(istype(user, /mob/living/silicon/ai) || istype(user, /mob/dead))) - t += "You are too far away to read it." - - else if(stat & (NOPOWER|BROKEN)) - t += "The display is off." - - else if(target) - var/datum/gas_mixture/environment = target.return_air() - if(environment) - t += "The pressure gauge reads [round(environment.return_pressure(), 0.01)] kPa; [round(environment.temperature,0.01)]K ([round(environment.temperature-T0C,0.01)]°C)" - else - t += "The sensor error light is blinking." - else - t += "The connect error light is blinking." - - . = list(t) - -/obj/machinery/meter/Click() - if(istype(usr, /mob/living/silicon/ai)) // ghosts can call ..() for examine - usr.examinate(src) - return 1 - - return ..() - -/obj/machinery/meter/attackby(var/obj/item/W as obj, var/mob/user as mob, params) - if(istype(W, /obj/item/multitool)) - update_multitool_menu(user) - return 1 - - if(!istype(W, /obj/item/wrench)) - return ..() - playsound(loc, W.usesound, 50, 1) - to_chat(user, "You begin to unfasten \the [src]...") - if(do_after(user, 40 * W.toolspeed, target = src)) - user.visible_message( \ - "[user] unfastens \the [src].", \ - "You have unfastened \the [src].", \ - "You hear ratchet.") - deconstruct(TRUE) - -/obj/machinery/meter/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - new /obj/item/pipe_meter(loc) - qdel(src) - -/obj/machinery/meter/singularity_pull(S, current_size) - ..() - if(current_size >= STAGE_FIVE) - deconstruct() - -// TURF METER - REPORTS A TILE'S AIR CONTENTS - -/obj/machinery/meter/turf/New() - ..() - target = loc - return 1 - - -/obj/machinery/meter/turf/Initialize() - if(!target) - target = loc - ..() - -/obj/machinery/meter/turf/attackby(var/obj/item/W as obj, var/mob/user as mob, params) - return - -/obj/machinery/meter/multitool_menu(var/mob/user, var/obj/item/multitool/P) - return {" - Main - "} +/obj/machinery/meter + name = "gas flow meter" + desc = "It measures something." + icon = 'icons/obj/meter.dmi' + icon_state = "meterX" + + layer = GAS_PUMP_LAYER + + var/obj/machinery/atmospherics/pipe/target = null + anchored = TRUE + max_integrity = 150 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 40, "acid" = 0) + power_channel = ENVIRON + var/frequency = ATMOS_DISTRO_FREQ + var/id + var/id_tag + use_power = IDLE_POWER_USE + idle_power_usage = 2 + active_power_usage = 5 + req_one_access_txt = "24;10" + Mtoollink = TRUE + settagwhitelist = list("id_tag") + +/obj/machinery/meter/New() + ..() + SSair.atmos_machinery += src + target = locate(/obj/machinery/atmospherics/pipe) in loc + if(id && !id_tag)//i'm not dealing with further merge conflicts, fuck it + id_tag = id + return 1 + +/obj/machinery/meter/Destroy() + SSair.atmos_machinery -= src + target = null + return ..() + +/obj/machinery/meter/Initialize() + ..() + if(!target) + target = locate(/obj/machinery/atmospherics/pipe) in loc + +/obj/machinery/meter/process_atmos() + if(!target) + icon_state = "meterX" + return 0 + + if(stat & (BROKEN|NOPOWER)) + icon_state = "meter0" + return 0 + + var/datum/gas_mixture/environment = target.return_air() + if(!environment) + icon_state = "meterX" + return 0 + + var/env_pressure = environment.return_pressure() + if(env_pressure <= 0.15*ONE_ATMOSPHERE) + icon_state = "meter0" + else if(env_pressure <= 1.8*ONE_ATMOSPHERE) + var/val = round(env_pressure/(ONE_ATMOSPHERE*0.3) + 0.5) + icon_state = "meter1_[val]" + else if(env_pressure <= 30*ONE_ATMOSPHERE) + var/val = round(env_pressure/(ONE_ATMOSPHERE*5)-0.35) + 1 + icon_state = "meter2_[val]" + else if(env_pressure <= 59*ONE_ATMOSPHERE) + var/val = round(env_pressure/(ONE_ATMOSPHERE*5) - 6) + 1 + icon_state = "meter3_[val]" + else + icon_state = "meter4" + + if(frequency) + var/datum/radio_frequency/radio_connection = SSradio.return_frequency(frequency) + + if(!radio_connection) return + + var/datum/signal/signal = new + signal.source = src + signal.transmission_method = 1 + signal.data = list( + "tag" = id_tag, + "device" = "AM", + "pressure" = round(env_pressure), + "sigtype" = "status" + ) + radio_connection.post_signal(src, signal) + +/obj/machinery/meter/proc/status() + var/t = "" + if(target) + var/datum/gas_mixture/environment = target.return_air() + if(environment) + t += "The pressure gauge reads [round(environment.return_pressure(), 0.01)] kPa; [round(environment.temperature,0.01)]°K ([round(environment.temperature-T0C,0.01)]°C)" + else + t += "The sensor error light is blinking." + else + t += "The connect error light is blinking." + return t + +/obj/machinery/meter/examine(mob/user) + var/t = "A gas flow meter. " + + if(get_dist(user, src) > 3 && !(istype(user, /mob/living/silicon/ai) || istype(user, /mob/dead))) + t += "You are too far away to read it." + + else if(stat & (NOPOWER|BROKEN)) + t += "The display is off." + + else if(target) + var/datum/gas_mixture/environment = target.return_air() + if(environment) + t += "The pressure gauge reads [round(environment.return_pressure(), 0.01)] kPa; [round(environment.temperature,0.01)]K ([round(environment.temperature-T0C,0.01)]°C)" + else + t += "The sensor error light is blinking." + else + t += "The connect error light is blinking." + + . = list(t) + +/obj/machinery/meter/Click() + if(istype(usr, /mob/living/silicon/ai)) // ghosts can call ..() for examine + usr.examinate(src) + return 1 + + return ..() + +/obj/machinery/meter/attackby(var/obj/item/W as obj, var/mob/user as mob, params) + if(istype(W, /obj/item/multitool)) + update_multitool_menu(user) + return 1 + + if(!istype(W, /obj/item/wrench)) + return ..() + playsound(loc, W.usesound, 50, 1) + to_chat(user, "You begin to unfasten \the [src]...") + if(do_after(user, 40 * W.toolspeed, target = src)) + user.visible_message( \ + "[user] unfastens \the [src].", \ + "You have unfastened \the [src].", \ + "You hear ratchet.") + deconstruct(TRUE) + +/obj/machinery/meter/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + new /obj/item/pipe_meter(loc) + qdel(src) + +/obj/machinery/meter/singularity_pull(S, current_size) + ..() + if(current_size >= STAGE_FIVE) + deconstruct() + +// TURF METER - REPORTS A TILE'S AIR CONTENTS + +/obj/machinery/meter/turf/New() + ..() + target = loc + return 1 + + +/obj/machinery/meter/turf/Initialize() + if(!target) + target = loc + ..() + +/obj/machinery/meter/turf/attackby(var/obj/item/W as obj, var/mob/user as mob, params) + return + +/obj/machinery/meter/multitool_menu(var/mob/user, var/obj/item/multitool/P) + return {" + Main + "} diff --git a/code/game/machinery/atmoalter/portable_atmospherics.dm b/code/game/machinery/atmoalter/portable_atmospherics.dm index b7c2dd152da..cec04bf4f6f 100644 --- a/code/game/machinery/atmoalter/portable_atmospherics.dm +++ b/code/game/machinery/atmoalter/portable_atmospherics.dm @@ -1,158 +1,160 @@ -/obj/machinery/portable_atmospherics - name = "atmoalter" - use_power = NO_POWER_USE - max_integrity = 250 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 60, "acid" = 30) - var/datum/gas_mixture/air_contents = new - - var/obj/machinery/atmospherics/unary/portables_connector/connected_port - var/obj/item/tank/holding - - var/volume = 0 - var/destroyed = 0 - - var/maximum_pressure = 90*ONE_ATMOSPHERE - -/obj/machinery/portable_atmospherics/New() - ..() - SSair.atmos_machinery += src - - air_contents.volume = volume - air_contents.temperature = T20C - - return 1 - -/obj/machinery/portable_atmospherics/Initialize() - . = ..() - spawn() - var/obj/machinery/atmospherics/unary/portables_connector/port = locate() in loc - if(port) - connect(port) - update_icon() - -/obj/machinery/portable_atmospherics/process_atmos() - if(!connected_port) //only react when pipe_network will ont it do it for you - //Allow for reactions - air_contents.react() - else - update_icon() - -/obj/machinery/portable_atmospherics/Destroy() - SSair.atmos_machinery -= src - disconnect() - QDEL_NULL(air_contents) - QDEL_NULL(holding) - return ..() - -/obj/machinery/portable_atmospherics/update_icon() - return null - -/obj/machinery/portable_atmospherics/proc/connect(obj/machinery/atmospherics/unary/portables_connector/new_port) - //Make sure not already connected to something else - if(connected_port || !new_port || new_port.connected_device) - return 0 - - //Make sure are close enough for a valid connection - if(new_port.loc != loc) - return 0 - - //Perform the connection - connected_port = new_port - connected_port.connected_device = src - // To avoid a chicken-egg thing where pipes need to - // be initialized before the atmos cans are - if(!connected_port.parent) - connected_port.build_network() - connected_port.parent.reconcile_air() - - anchored = 1 //Prevent movement - - return 1 - -/obj/machinery/portable_atmospherics/proc/disconnect() - if(!connected_port) - return 0 - - anchored = 0 - - connected_port.connected_device = null - connected_port = null - - return 1 - -/obj/machinery/portable_atmospherics/portableConnectorReturnAir() - return air_contents - -/obj/machinery/portable_atmospherics/AltClick(mob/living/user) - if(!istype(user) || user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!in_range(src, user)) - return - if(!ishuman(usr) && !issilicon(usr)) - return - if(holding) - to_chat(user, "You remove [holding] from [src].") - replace_tank(user, TRUE) - -/obj/machinery/portable_atmospherics/examine(mob/user) - . = ..() - if(holding) - . += "\The [src] contains [holding]. Alt-click [src] to remove it." - -/obj/machinery/portable_atmospherics/proc/replace_tank(mob/living/user, close_valve, obj/item/tank/new_tank) - if(holding) - holding.forceMove(drop_location()) - if(Adjacent(user) && !issilicon(user)) - user.put_in_hands(holding) - if(new_tank) - holding = new_tank - else - holding = null - update_icon() - return TRUE - -/obj/machinery/portable_atmospherics/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/tank)) - if(!(stat & BROKEN)) - if(!user.drop_item()) - return - var/obj/item/tank/T = W - user.drop_item() - if(src.holding) - to_chat(user, "[holding ? "In one smooth motion you pop [holding] out of [src]'s connector and replace it with [T]" : "You insert [T] into [src]"].") - replace_tank(user, FALSE) - T.loc = src - src.holding = T - update_icon() - return - else if(istype(W, /obj/item/wrench)) - if(connected_port) - disconnect() - to_chat(user, "You disconnect [name] from the port.") - update_icon() - return - else - var/obj/machinery/atmospherics/unary/portables_connector/possible_port = locate(/obj/machinery/atmospherics/unary/portables_connector/) in loc - if(possible_port) - if(connect(possible_port)) - to_chat(user, "You connect [name] to the port.") - update_icon() - return - else - to_chat(user, "[name] failed to connect to the port.") - return - else - to_chat(user, "Nothing happens.") - return - if((istype(W, /obj/item/analyzer)) && get_dist(user, src) <= 1) - atmosanalyzer_scan(air_contents, user) - return - return ..() - -/obj/machinery/portable_atmospherics/attacked_by(obj/item/I, mob/user) - if(I.force < 10 && !(stat & BROKEN)) - take_damage(0) - else - add_fingerprint(user) - ..() \ No newline at end of file +/obj/machinery/portable_atmospherics + name = "atmoalter" + use_power = NO_POWER_USE + max_integrity = 250 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 60, "acid" = 30) + var/datum/gas_mixture/air_contents = new + + var/obj/machinery/atmospherics/unary/portables_connector/connected_port + var/obj/item/tank/holding + + var/volume = 0 + var/destroyed = 0 + + var/maximum_pressure = 90*ONE_ATMOSPHERE + +/obj/machinery/portable_atmospherics/New() + ..() + SSair.atmos_machinery += src + + air_contents.volume = volume + air_contents.temperature = T20C + + return 1 + +/obj/machinery/portable_atmospherics/Initialize() + . = ..() + spawn() + var/obj/machinery/atmospherics/unary/portables_connector/port = locate() in loc + if(port) + connect(port) + update_icon() + +/obj/machinery/portable_atmospherics/process_atmos() + if(!connected_port) //only react when pipe_network will ont it do it for you + //Allow for reactions + air_contents.react() + else + update_icon() + +/obj/machinery/portable_atmospherics/Destroy() + SSair.atmos_machinery -= src + disconnect() + QDEL_NULL(air_contents) + QDEL_NULL(holding) + return ..() + +/obj/machinery/portable_atmospherics/update_icon() + return null + +/obj/machinery/portable_atmospherics/proc/connect(obj/machinery/atmospherics/unary/portables_connector/new_port) + //Make sure not already connected to something else + if(connected_port || !new_port || new_port.connected_device) + return 0 + + //Make sure are close enough for a valid connection + if(new_port.loc != loc) + return 0 + + //Perform the connection + connected_port = new_port + connected_port.connected_device = src + // To avoid a chicken-egg thing where pipes need to + // be initialized before the atmos cans are + if(!connected_port.parent) + connected_port.build_network() + connected_port.parent.reconcile_air() + + anchored = 1 //Prevent movement + + return 1 + +/obj/machinery/portable_atmospherics/proc/disconnect() + if(!connected_port) + return 0 + + anchored = 0 + + connected_port.connected_device = null + connected_port = null + + return 1 + +/obj/machinery/portable_atmospherics/portableConnectorReturnAir() + return air_contents + +/obj/machinery/portable_atmospherics/AltClick(mob/living/user) + if(!istype(user) || user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!in_range(src, user)) + return + if(!ishuman(usr) && !issilicon(usr)) + return + if(holding) + to_chat(user, "You remove [holding] from [src].") + replace_tank(user, TRUE) + +/obj/machinery/portable_atmospherics/examine(mob/user) + . = ..() + if(holding) + . += "\The [src] contains [holding]. Alt-click [src] to remove it." + +/obj/machinery/portable_atmospherics/proc/replace_tank(mob/living/user, close_valve, obj/item/tank/new_tank) + if(holding) + holding.forceMove(drop_location()) + if(Adjacent(user) && !issilicon(user)) + user.put_in_hands(holding) + if(new_tank) + holding = new_tank + else + holding = null + update_icon() + return TRUE + +/obj/machinery/portable_atmospherics/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/tank)) + if(!(stat & BROKEN)) + if(!user.drop_item()) + return + var/obj/item/tank/T = W + user.drop_item() + if(src.holding) + to_chat(user, "[holding ? "In one smooth motion you pop [holding] out of [src]'s connector and replace it with [T]" : "You insert [T] into [src]"].") + replace_tank(user, FALSE) + T.loc = src + src.holding = T + update_icon() + return + if((istype(W, /obj/item/analyzer)) && get_dist(user, src) <= 1) + atmosanalyzer_scan(air_contents, user) + return + return ..() + +/obj/machinery/portable_atmospherics/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(connected_port) + disconnect() + to_chat(user, "You disconnect [name] from the port.") + update_icon() + else + var/obj/machinery/atmospherics/unary/portables_connector/possible_port = locate(/obj/machinery/atmospherics/unary/portables_connector/) in loc + if(possible_port) + if(connect(possible_port)) + to_chat(user, "You connect [src] to the port.") + update_icon() + return + else + to_chat(user, "[src] failed to connect to the port.") + return + else + to_chat(user, "Nothing happens.") + +/obj/machinery/portable_atmospherics/attacked_by(obj/item/I, mob/user) + if(I.force < 10 && !(stat & BROKEN)) + take_damage(0) + else + add_fingerprint(user) + ..() diff --git a/code/game/machinery/atmoalter/pump.dm b/code/game/machinery/atmoalter/pump.dm index 5bd441f6748..e4bdef2f147 100644 --- a/code/game/machinery/atmoalter/pump.dm +++ b/code/game/machinery/atmoalter/pump.dm @@ -1,173 +1,173 @@ -/obj/machinery/portable_atmospherics/pump - name = "Portable Air Pump" - - icon = 'icons/obj/atmos.dmi' - icon_state = "psiphon:0" - density = 1 - - var/on = 0 - var/direction_out = 0 //0 = siphoning, 1 = releasing - var/target_pressure = 100 - - var/pressuremin = 0 - var/pressuremax = 10 * ONE_ATMOSPHERE - - volume = 1000 - -/obj/machinery/portable_atmospherics/pump/update_icon() - src.overlays = 0 - - if(on) - icon_state = "psiphon:1" - else - icon_state = "psiphon:0" - - if(holding) - overlays += "siphon-open" - - if(connected_port) - overlays += "siphon-connector" - - return - -/obj/machinery/portable_atmospherics/pump/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - - if(prob(50/severity)) - on = !on - - if(prob(100/severity)) - direction_out = !direction_out - - target_pressure = rand(0,1300) - update_icon() - - ..(severity) - -/obj/machinery/portable_atmospherics/pump/process_atmos() - ..() - if(on) - var/datum/gas_mixture/environment - if(holding) - environment = holding.air_contents - else - environment = loc.return_air() - if(direction_out) - var/pressure_delta = target_pressure - environment.return_pressure() - //Can not have a pressure delta that would cause environment pressure > tank pressure - - var/transfer_moles = 0 - if(air_contents.temperature > 0) - transfer_moles = pressure_delta*environment.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION) - - //Actually transfer the gas - var/datum/gas_mixture/removed = air_contents.remove(transfer_moles) - - if(holding) - environment.merge(removed) - else - loc.assume_air(removed) - air_update_turf() - else - var/pressure_delta = target_pressure - air_contents.return_pressure() - //Can not have a pressure delta that would cause environment pressure > tank pressure - - var/transfer_moles = 0 - if(environment.temperature > 0) - transfer_moles = pressure_delta*air_contents.volume/(environment.temperature * R_IDEAL_GAS_EQUATION) - - //Actually transfer the gas - var/datum/gas_mixture/removed - if(holding) - removed = environment.remove(transfer_moles) - else - removed = loc.remove_air(transfer_moles) - air_update_turf() - - air_contents.merge(removed) - //src.update_icon() - - src.updateDialog() - return - -/obj/machinery/portable_atmospherics/pump/return_air() - return air_contents - -/obj/machinery/portable_atmospherics/pump/replace_tank(mob/living/user, close_valve) - . = ..() - if(.) - if(close_valve) - if(on) - on = FALSE - update_icon() - else if(on && holding && direction_out) - investigate_log("[key_name(user)] started a transfer into [holding].
        ", "atmos") - -/obj/machinery/portable_atmospherics/pump/attack_ai(var/mob/user as mob) - src.add_hiddenprint(user) - return src.attack_hand(user) - -/obj/machinery/portable_atmospherics/pump/attack_ghost(var/mob/user as mob) - return src.attack_hand(user) - -/obj/machinery/portable_atmospherics/pump/attack_hand(var/mob/user as mob) - ui_interact(user) - -/obj/machinery/portable_atmospherics/pump/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = physical_state) - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "portpump.tmpl", "Portable Pump", 480, 400, state = state) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - -/obj/machinery/portable_atmospherics/pump/ui_data(mob/user, ui_key = "main", datum/topic_state/state = physical_state) - var/data[0] - data["portConnected"] = connected_port ? 1 : 0 - data["tankPressure"] = round(air_contents.return_pressure() > 0 ? air_contents.return_pressure() : 0) - data["targetpressure"] = round(target_pressure) - data["pump_dir"] = direction_out - data["minpressure"] = round(pressuremin) - data["maxpressure"] = round(pressuremax) - data["on"] = on ? 1 : 0 - - data["hasHoldingTank"] = holding ? 1 : 0 - if(holding) - data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure() > 0 ? holding.air_contents.return_pressure() : 0)) - - return data - -/obj/machinery/portable_atmospherics/pump/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["power"]) - on = !on - if(on && direction_out) - investigate_log("[key_name(usr)] started a transfer into [holding].
        ", "atmos") - update_icon() - - if(href_list["direction"]) - direction_out = !direction_out - if(on && holding) - investigate_log("[key_name(usr)] started a transfer into [holding].
        ", "atmos") - - if(href_list["remove_tank"]) - if(holding) - on = FALSE - holding.loc = loc - holding = null - update_icon() - - if(href_list["pressure_adj"]) - var/diff = text2num(href_list["pressure_adj"]) - target_pressure = Clamp(target_pressure+diff, pressuremin, pressuremax) - update_icon() - - src.add_fingerprint(usr) +/obj/machinery/portable_atmospherics/pump + name = "Portable Air Pump" + + icon = 'icons/obj/atmos.dmi' + icon_state = "psiphon:0" + density = 1 + + var/on = 0 + var/direction_out = 0 //0 = siphoning, 1 = releasing + var/target_pressure = 100 + + var/pressuremin = 0 + var/pressuremax = 10 * ONE_ATMOSPHERE + + volume = 1000 + +/obj/machinery/portable_atmospherics/pump/update_icon() + src.overlays = 0 + + if(on) + icon_state = "psiphon:1" + else + icon_state = "psiphon:0" + + if(holding) + overlays += "siphon-open" + + if(connected_port) + overlays += "siphon-connector" + + return + +/obj/machinery/portable_atmospherics/pump/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + + if(prob(50/severity)) + on = !on + + if(prob(100/severity)) + direction_out = !direction_out + + target_pressure = rand(0,1300) + update_icon() + + ..(severity) + +/obj/machinery/portable_atmospherics/pump/process_atmos() + ..() + if(on) + var/datum/gas_mixture/environment + if(holding) + environment = holding.air_contents + else + environment = loc.return_air() + if(direction_out) + var/pressure_delta = target_pressure - environment.return_pressure() + //Can not have a pressure delta that would cause environment pressure > tank pressure + + var/transfer_moles = 0 + if(air_contents.temperature > 0) + transfer_moles = pressure_delta*environment.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION) + + //Actually transfer the gas + var/datum/gas_mixture/removed = air_contents.remove(transfer_moles) + + if(holding) + environment.merge(removed) + else + loc.assume_air(removed) + air_update_turf() + else + var/pressure_delta = target_pressure - air_contents.return_pressure() + //Can not have a pressure delta that would cause environment pressure > tank pressure + + var/transfer_moles = 0 + if(environment.temperature > 0) + transfer_moles = pressure_delta*air_contents.volume/(environment.temperature * R_IDEAL_GAS_EQUATION) + + //Actually transfer the gas + var/datum/gas_mixture/removed + if(holding) + removed = environment.remove(transfer_moles) + else + removed = loc.remove_air(transfer_moles) + air_update_turf() + + air_contents.merge(removed) + //src.update_icon() + + src.updateDialog() + return + +/obj/machinery/portable_atmospherics/pump/return_air() + return air_contents + +/obj/machinery/portable_atmospherics/pump/replace_tank(mob/living/user, close_valve) + . = ..() + if(.) + if(close_valve) + if(on) + on = FALSE + update_icon() + else if(on && holding && direction_out) + investigate_log("[key_name(user)] started a transfer into [holding].
        ", "atmos") + +/obj/machinery/portable_atmospherics/pump/attack_ai(var/mob/user as mob) + src.add_hiddenprint(user) + return src.attack_hand(user) + +/obj/machinery/portable_atmospherics/pump/attack_ghost(var/mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/portable_atmospherics/pump/attack_hand(var/mob/user as mob) + ui_interact(user) + +/obj/machinery/portable_atmospherics/pump/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = physical_state) + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "portpump.tmpl", "Portable Pump", 480, 400, state = state) + // open the new ui window + ui.open() + // auto update every Master Controller tick + ui.set_auto_update(1) + +/obj/machinery/portable_atmospherics/pump/ui_data(mob/user, ui_key = "main", datum/topic_state/state = physical_state) + var/data[0] + data["portConnected"] = connected_port ? 1 : 0 + data["tankPressure"] = round(air_contents.return_pressure() > 0 ? air_contents.return_pressure() : 0) + data["targetpressure"] = round(target_pressure) + data["pump_dir"] = direction_out + data["minpressure"] = round(pressuremin) + data["maxpressure"] = round(pressuremax) + data["on"] = on ? 1 : 0 + + data["hasHoldingTank"] = holding ? 1 : 0 + if(holding) + data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure() > 0 ? holding.air_contents.return_pressure() : 0)) + + return data + +/obj/machinery/portable_atmospherics/pump/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["power"]) + on = !on + if(on && direction_out) + investigate_log("[key_name(usr)] started a transfer into [holding].
        ", "atmos") + update_icon() + + if(href_list["direction"]) + direction_out = !direction_out + if(on && holding) + investigate_log("[key_name(usr)] started a transfer into [holding].
        ", "atmos") + + if(href_list["remove_tank"]) + if(holding) + on = FALSE + holding.loc = loc + holding = null + update_icon() + + if(href_list["pressure_adj"]) + var/diff = text2num(href_list["pressure_adj"]) + target_pressure = Clamp(target_pressure+diff, pressuremin, pressuremax) + update_icon() + + src.add_fingerprint(usr) diff --git a/code/game/machinery/atmoalter/scrubber.dm b/code/game/machinery/atmoalter/scrubber.dm index 2079b8cf915..1016e270f6d 100644 --- a/code/game/machinery/atmoalter/scrubber.dm +++ b/code/game/machinery/atmoalter/scrubber.dm @@ -1,216 +1,216 @@ -/obj/machinery/portable_atmospherics/scrubber - name = "Portable Air Scrubber" - - icon = 'icons/obj/atmos.dmi' - icon_state = "pscrubber:0" - density = 1 - - var/on = 0 - var/volume_rate = 800 - var/widenet = 0 //is this scrubber acting on the 3x3 area around it. - - volume = 750 - - var/minrate = 0//probably useless, but whatever - var/maxrate = 10 * ONE_ATMOSPHERE - -/obj/machinery/portable_atmospherics/scrubber/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - ..(severity) - return - - if(prob(50/severity)) - on = !on - update_icon() - - ..(severity) - -/obj/machinery/portable_atmospherics/scrubber/update_icon() - src.overlays = 0 - - if(on) - icon_state = "pscrubber:1" - else - icon_state = "pscrubber:0" - - if(holding) - overlays += "scrubber-open" - - if(connected_port) - overlays += "scrubber-connector" - - return - -/obj/machinery/portable_atmospherics/scrubber/process_atmos() - ..() - - if(!on) - return - scrub(loc) - if(widenet) - var/turf/T = loc - if(istype(T)) - for(var/turf/simulated/tile in T.GetAtmosAdjacentTurfs(alldir=1)) - scrub(tile) - -/obj/machinery/portable_atmospherics/scrubber/proc/scrub(var/turf/simulated/tile) - var/datum/gas_mixture/environment - if(holding) - environment = holding.air_contents - else - environment = tile.return_air() - var/transfer_moles = min(1,volume_rate/environment.volume)*environment.total_moles() - - //Take a gas sample - var/datum/gas_mixture/removed - if(holding) - removed = environment.remove(transfer_moles) - else - removed = loc.remove_air(transfer_moles) - - //Filter it - if(removed) - var/datum/gas_mixture/filtered_out = new - - filtered_out.temperature = removed.temperature - - - filtered_out.toxins = removed.toxins - removed.toxins = 0 - - filtered_out.carbon_dioxide = removed.carbon_dioxide - removed.carbon_dioxide = 0 - - if(removed.trace_gases.len>0) - for(var/datum/gas/trace_gas in removed.trace_gases) - if(istype(trace_gas, /datum/gas/sleeping_agent)) - removed.trace_gases -= trace_gas - filtered_out.trace_gases += trace_gas - - if(removed.trace_gases.len>0) - for(var/datum/gas/trace_gas in removed.trace_gases) - if(istype(trace_gas, /datum/gas/oxygen_agent_b)) - removed.trace_gases -= trace_gas - filtered_out.trace_gases += trace_gas - - //Remix the resulting gases - air_contents.merge(filtered_out) - - if(holding) - environment.merge(removed) - else - tile.assume_air(removed) - tile.air_update_turf() - -/obj/machinery/portable_atmospherics/scrubber/return_air() - return air_contents - -/obj/machinery/portable_atmospherics/scrubber/attack_ai(var/mob/user as mob) - src.add_hiddenprint(user) - return src.attack_hand(user) - -/obj/machinery/portable_atmospherics/scrubber/attack_ghost(var/mob/user as mob) - return src.attack_hand(user) - -/obj/machinery/portable_atmospherics/scrubber/attack_hand(var/mob/user as mob) - ui_interact(user) - return - -/obj/machinery/portable_atmospherics/scrubber/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = physical_state) - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "portscrubber.tmpl", "Portable Scrubber", 480, 400, state = state) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - -/obj/machinery/portable_atmospherics/scrubber/ui_data(mob/user, ui_key = "main", datum/topic_state/state = physical_state) - var/data[0] - data["portConnected"] = connected_port ? 1 : 0 - data["tankPressure"] = round(air_contents.return_pressure() > 0 ? air_contents.return_pressure() : 0) - data["rate"] = round(volume_rate) - data["minrate"] = round(minrate) - data["maxrate"] = round(maxrate) - data["on"] = on ? 1 : 0 - - data["hasHoldingTank"] = holding ? 1 : 0 - if(holding) - data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure() > 0 ? holding.air_contents.return_pressure() : 0)) - - return data - -/obj/machinery/portable_atmospherics/scrubber/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["power"]) - on = !on - update_icon() - - if(href_list["remove_tank"]) - if(holding) - holding.loc = loc - holding = null - - if(href_list["volume_adj"]) - var/diff = text2num(href_list["volume_adj"]) - volume_rate = Clamp(volume_rate+diff, minrate, maxrate) - - src.add_fingerprint(usr) - -/obj/machinery/portable_atmospherics/scrubber/huge - name = "Huge Air Scrubber" - icon_state = "scrubber:0" - anchored = 1 - volume = 50000 - volume_rate = 5000 - widenet = 1 - - var/global/gid = 1 - var/id = 0 - var/stationary = 0 - -/obj/machinery/portable_atmospherics/scrubber/huge/New() - ..() - id = gid - gid++ - - name = "[name] (ID [id])" - -/obj/machinery/portable_atmospherics/scrubber/huge/attack_hand(var/mob/user as mob) - to_chat(usr, "You can't directly interact with this machine. Use the area atmos computer.") - -/obj/machinery/portable_atmospherics/scrubber/huge/update_icon() - src.overlays = 0 - - if(on) - icon_state = "scrubber:1" - else - icon_state = "scrubber:0" - -/obj/machinery/portable_atmospherics/scrubber/huge/attackby(var/obj/item/W as obj, var/mob/user as mob, params) - if(istype(W, /obj/item/wrench)) - if(stationary) - to_chat(user, "The bolts are too tight for you to unscrew!") - return - if(on) - to_chat(user, "Turn it off first!") - return - - anchored = !anchored - playsound(loc, W.usesound, 50, 1) - to_chat(user, "You [anchored ? "wrench" : "unwrench"] \the [src].") - return - - if((istype(W, /obj/item/analyzer)) && get_dist(user, src) <= 1) - atmosanalyzer_scan(air_contents, user) - return - return ..() - -/obj/machinery/portable_atmospherics/scrubber/huge/stationary - name = "Stationary Air Scrubber" - stationary = 1 +/obj/machinery/portable_atmospherics/scrubber + name = "Portable Air Scrubber" + + icon = 'icons/obj/atmos.dmi' + icon_state = "pscrubber:0" + density = 1 + + var/on = 0 + var/volume_rate = 800 + var/widenet = 0 //is this scrubber acting on the 3x3 area around it. + + volume = 750 + + var/minrate = 0//probably useless, but whatever + var/maxrate = 10 * ONE_ATMOSPHERE + +/obj/machinery/portable_atmospherics/scrubber/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + + if(prob(50/severity)) + on = !on + update_icon() + + ..(severity) + +/obj/machinery/portable_atmospherics/scrubber/update_icon() + src.overlays = 0 + + if(on) + icon_state = "pscrubber:1" + else + icon_state = "pscrubber:0" + + if(holding) + overlays += "scrubber-open" + + if(connected_port) + overlays += "scrubber-connector" + + return + +/obj/machinery/portable_atmospherics/scrubber/process_atmos() + ..() + + if(!on) + return + scrub(loc) + if(widenet) + var/turf/T = loc + if(istype(T)) + for(var/turf/simulated/tile in T.GetAtmosAdjacentTurfs(alldir=1)) + scrub(tile) + +/obj/machinery/portable_atmospherics/scrubber/proc/scrub(var/turf/simulated/tile) + var/datum/gas_mixture/environment + if(holding) + environment = holding.air_contents + else + environment = tile.return_air() + var/transfer_moles = min(1,volume_rate/environment.volume)*environment.total_moles() + + //Take a gas sample + var/datum/gas_mixture/removed + if(holding) + removed = environment.remove(transfer_moles) + else + removed = loc.remove_air(transfer_moles) + + //Filter it + if(removed) + var/datum/gas_mixture/filtered_out = new + + filtered_out.temperature = removed.temperature + + + filtered_out.toxins = removed.toxins + removed.toxins = 0 + + filtered_out.carbon_dioxide = removed.carbon_dioxide + removed.carbon_dioxide = 0 + + if(removed.trace_gases.len>0) + for(var/datum/gas/trace_gas in removed.trace_gases) + if(istype(trace_gas, /datum/gas/sleeping_agent)) + removed.trace_gases -= trace_gas + filtered_out.trace_gases += trace_gas + + if(removed.trace_gases.len>0) + for(var/datum/gas/trace_gas in removed.trace_gases) + if(istype(trace_gas, /datum/gas/oxygen_agent_b)) + removed.trace_gases -= trace_gas + filtered_out.trace_gases += trace_gas + + //Remix the resulting gases + air_contents.merge(filtered_out) + + if(holding) + environment.merge(removed) + else + tile.assume_air(removed) + tile.air_update_turf() + +/obj/machinery/portable_atmospherics/scrubber/return_air() + return air_contents + +/obj/machinery/portable_atmospherics/scrubber/attack_ai(var/mob/user as mob) + src.add_hiddenprint(user) + return src.attack_hand(user) + +/obj/machinery/portable_atmospherics/scrubber/attack_ghost(var/mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/portable_atmospherics/scrubber/attack_hand(var/mob/user as mob) + ui_interact(user) + return + +/obj/machinery/portable_atmospherics/scrubber/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = physical_state) + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "portscrubber.tmpl", "Portable Scrubber", 480, 400, state = state) + // open the new ui window + ui.open() + // auto update every Master Controller tick + ui.set_auto_update(1) + +/obj/machinery/portable_atmospherics/scrubber/ui_data(mob/user, ui_key = "main", datum/topic_state/state = physical_state) + var/data[0] + data["portConnected"] = connected_port ? 1 : 0 + data["tankPressure"] = round(air_contents.return_pressure() > 0 ? air_contents.return_pressure() : 0) + data["rate"] = round(volume_rate) + data["minrate"] = round(minrate) + data["maxrate"] = round(maxrate) + data["on"] = on ? 1 : 0 + + data["hasHoldingTank"] = holding ? 1 : 0 + if(holding) + data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure() > 0 ? holding.air_contents.return_pressure() : 0)) + + return data + +/obj/machinery/portable_atmospherics/scrubber/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["power"]) + on = !on + update_icon() + + if(href_list["remove_tank"]) + if(holding) + holding.loc = loc + holding = null + + if(href_list["volume_adj"]) + var/diff = text2num(href_list["volume_adj"]) + volume_rate = Clamp(volume_rate+diff, minrate, maxrate) + + src.add_fingerprint(usr) + +/obj/machinery/portable_atmospherics/scrubber/huge + name = "Huge Air Scrubber" + icon_state = "scrubber:0" + anchored = 1 + volume = 50000 + volume_rate = 5000 + widenet = 1 + + var/global/gid = 1 + var/id = 0 + var/stationary = 0 + +/obj/machinery/portable_atmospherics/scrubber/huge/New() + ..() + id = gid + gid++ + + name = "[name] (ID [id])" + +/obj/machinery/portable_atmospherics/scrubber/huge/attack_hand(var/mob/user as mob) + to_chat(usr, "You can't directly interact with this machine. Use the area atmos computer.") + +/obj/machinery/portable_atmospherics/scrubber/huge/update_icon() + src.overlays = 0 + + if(on) + icon_state = "scrubber:1" + else + icon_state = "scrubber:0" + +/obj/machinery/portable_atmospherics/scrubber/huge/attackby(var/obj/item/W as obj, var/mob/user as mob, params) + if(istype(W, /obj/item/wrench)) + if(stationary) + to_chat(user, "The bolts are too tight for you to unscrew!") + return + if(on) + to_chat(user, "Turn it off first!") + return + + anchored = !anchored + playsound(loc, W.usesound, 50, 1) + to_chat(user, "You [anchored ? "wrench" : "unwrench"] \the [src].") + return + + if((istype(W, /obj/item/analyzer)) && get_dist(user, src) <= 1) + atmosanalyzer_scan(air_contents, user) + return + return ..() + +/obj/machinery/portable_atmospherics/scrubber/huge/stationary + name = "Stationary Air Scrubber" + stationary = 1 diff --git a/code/game/machinery/atmoalter/zvent.dm b/code/game/machinery/atmoalter/zvent.dm index 18af424c4d6..773432133e8 100644 --- a/code/game/machinery/atmoalter/zvent.dm +++ b/code/game/machinery/atmoalter/zvent.dm @@ -1,35 +1,35 @@ -/obj/machinery/zvent - name = "Interfloor Air Transfer System" - - icon = 'icons/obj/pipes.dmi' - icon_state = "vent-db" - density = 0 - anchored=1 - - var/on = 0 - var/volume_rate = 800 - -/obj/machinery/zvent/New() - ..() - SSair.atmos_machinery += src - -/obj/machinery/zvent/Destroy() - SSair.atmos_machinery -= src - return ..() - -/obj/machinery/zvent/process_atmos() - - //all this object does, is make its turf share air with the ones above and below it, if they have a vent too. - if(istype(loc,/turf/simulated)) //if we're not on a valid turf, forget it - for(var/new_z in list(-1,1)) //change this list if a fancier system of z-levels gets implemented - var/turf/simulated/zturf_conn = locate(x,y,z+new_z) - if(istype(zturf_conn)) - var/obj/machinery/zvent/zvent_conn= locate(/obj/machinery/zvent) in zturf_conn - if(istype(zvent_conn)) - //both floors have simulated turfs, share() - var/turf/simulated/myturf = loc - var/datum/gas_mixture/conn_air = zturf_conn.air //TODO: pop culture reference - var/datum/gas_mixture/my_air = myturf.air - if(istype(conn_air) && istype(my_air)) - my_air.share(conn_air) - air_update_turf() +/obj/machinery/zvent + name = "Interfloor Air Transfer System" + + icon = 'icons/obj/pipes.dmi' + icon_state = "vent-db" + density = 0 + anchored=1 + + var/on = 0 + var/volume_rate = 800 + +/obj/machinery/zvent/New() + ..() + SSair.atmos_machinery += src + +/obj/machinery/zvent/Destroy() + SSair.atmos_machinery -= src + return ..() + +/obj/machinery/zvent/process_atmos() + + //all this object does, is make its turf share air with the ones above and below it, if they have a vent too. + if(istype(loc,/turf/simulated)) //if we're not on a valid turf, forget it + for(var/new_z in list(-1,1)) //change this list if a fancier system of z-levels gets implemented + var/turf/simulated/zturf_conn = locate(x,y,z+new_z) + if(istype(zturf_conn)) + var/obj/machinery/zvent/zvent_conn= locate(/obj/machinery/zvent) in zturf_conn + if(istype(zvent_conn)) + //both floors have simulated turfs, share() + var/turf/simulated/myturf = loc + var/datum/gas_mixture/conn_air = zturf_conn.air //TODO: pop culture reference + var/datum/gas_mixture/my_air = myturf.air + if(istype(conn_air) && istype(my_air)) + my_air.share(conn_air) + air_update_turf() diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm index ae0611e69d4..9d9db841b1e 100644 --- a/code/game/machinery/autolathe.dm +++ b/code/game/machinery/autolathe.dm @@ -1,438 +1,469 @@ -#define AUTOLATHE_MAIN_MENU 1 -#define AUTOLATHE_CATEGORY_MENU 2 -#define AUTOLATHE_SEARCH_MENU 3 - -/obj/machinery/autolathe - name = "autolathe" - desc = "It produces items using metal and glass." - icon_state = "autolathe" - density = 1 - - var/operating = 0.0 - var/list/queue = list() - var/queue_max_len = 12 - var/turf/BuildTurf - anchored = 1.0 - var/list/L = list() - var/list/LL = list() - var/hacked = 0 - var/disabled = 0 - var/shocked = 0 - var/hack_wire - var/disable_wire - var/shock_wire - use_power = IDLE_POWER_USE - idle_power_usage = 10 - active_power_usage = 100 - var/busy = 0 - var/prod_coeff - var/datum/wires/autolathe/wires = null - - var/list/being_built = list() - var/datum/research/files - var/list/datum/design/matching_designs - var/temp_search - var/selected_category - var/screen = 1 - - var/list/categories = list("Tools", "Electronics", "Construction", "Communication", "Security", "Machinery", "Medical", "Miscellaneous", "Dinnerware", "Imported") - -/obj/machinery/autolathe/New() - AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_GLASS), 0, TRUE, null, null, CALLBACK(src, .proc/AfterMaterialInsert)) - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/autolathe(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stack/sheet/glass(null) - RefreshParts() - - wires = new(src) - files = new /datum/research/autolathe(src) - matching_designs = list() - -/obj/machinery/autolathe/upgraded/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/autolathe(null) - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/manipulator/pico(null) - component_parts += new /obj/item/stack/sheet/glass(null) - RefreshParts() - -/obj/machinery/autolathe/Destroy() - QDEL_NULL(wires) - GET_COMPONENT(materials, /datum/component/material_container) - materials.retrieve_all() - return ..() - -/obj/machinery/autolathe/interact(mob/user) - if(shocked && !(stat & NOPOWER)) - if(shock(user, 50)) - return - - if(panel_open) - wires.Interact(user) - else - ui_interact(user) - -/obj/machinery/autolathe/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "autolathe.tmpl", name, 800, 550) - ui.open() - -/obj/machinery/autolathe/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - GET_COMPONENT(materials, /datum/component/material_container) - var/data[0] - data["screen"] = screen - data["total_amount"] = materials.total_amount - data["max_amount"] = materials.max_amount - data["metal_amount"] = materials.amount(MAT_METAL) - data["glass_amount"] = materials.amount(MAT_GLASS) - switch(screen) - if(AUTOLATHE_MAIN_MENU) - data["uid"] = UID() - data["categories"] = categories - if(AUTOLATHE_CATEGORY_MENU) - data["selected_category"] = selected_category - var/list/designs = list() - data["designs"] = designs - for(var/v in files.known_designs) - var/datum/design/D = files.known_designs[v] - if(!(selected_category in D.category)) - continue - var/list/design = list() - designs[++designs.len] = design - design["name"] = D.name - design["id"] = D.id - design["disabled"] = disabled || !can_build(D) ? "disabled" : null - if(ispath(D.build_path, /obj/item/stack)) - design["max_multiplier"] = min(D.maxstack, D.materials[MAT_METAL] ? round(materials.amount(MAT_METAL) / D.materials[MAT_METAL]) : INFINITY, D.materials[MAT_GLASS] ? round(materials.amount(MAT_GLASS) / D.materials[MAT_GLASS]) : INFINITY) - else - design["max_multiplier"] = null - design["materials"] = design_cost_data(D) - if(AUTOLATHE_SEARCH_MENU) - data["search"] = temp_search - var/list/designs = list() - data["designs"] = designs - for(var/datum/design/D in matching_designs) - var/list/design = list() - designs[++designs.len] = design - design["name"] = D.name - design["id"] = D.id - design["disabled"] = disabled || !can_build(D) ? "disabled" : null - if(ispath(D.build_path, /obj/item/stack)) - design["max_multiplier"] = min(D.maxstack, D.materials[MAT_METAL] ? round(materials.amount(MAT_METAL) / D.materials[MAT_METAL]) : INFINITY, D.materials[MAT_GLASS] ? round(materials.amount(MAT_GLASS) / D.materials[MAT_GLASS]) : INFINITY) - else - design["max_multiplier"] = null - design["materials"] = design_cost_data(D) - - data = queue_data(data) - return data - -/obj/machinery/autolathe/proc/design_cost_data(datum/design/D) - var/list/data = list() - var/coeff = get_coeff(D) - GET_COMPONENT(materials, /datum/component/material_container) - var/has_metal = 1 - if(D.materials[MAT_METAL] && (materials.amount(MAT_METAL) < (D.materials[MAT_METAL] / coeff))) - has_metal = 0 - var/has_glass = 1 - if(D.materials[MAT_GLASS] && (materials.amount(MAT_GLASS) < (D.materials[MAT_GLASS] / coeff))) - has_glass = 0 - - data[++data.len] = list("name" = "metal", "amount" = D.materials[MAT_METAL] / coeff, "is_red" = !has_metal) - data[++data.len] = list("name" = "glass", "amount" = D.materials[MAT_GLASS] / coeff, "is_red" = !has_glass) - - return data - -/obj/machinery/autolathe/proc/queue_data(list/data) - GET_COMPONENT(materials, /datum/component/material_container) - var/temp_metal = materials.amount(MAT_METAL) - var/temp_glass = materials.amount(MAT_GLASS) - data["processing"] = being_built.len ? get_processing_line() : null - if(istype(queue) && queue.len) - var/list/data_queue = list() - for(var/list/L in queue) - var/datum/design/D = L[1] - var/list/LL = get_design_cost_as_list(D, L[2]) - data_queue[++data_queue.len] = list("name" = initial(D.name), "can_build" = can_build(D, L[2], temp_metal, temp_glass), "multiplier" = L[2]) - temp_metal = max(temp_metal - LL[1], 1) - temp_glass = max(temp_glass - LL[2], 1) - data["queue"] = data_queue - data["queue_len"] = data_queue.len - else - data["queue"] = null - return data - -/obj/machinery/autolathe/attackby(obj/item/O, mob/user, params) - if(busy) - to_chat(user, "The autolathe is busy. Please wait for completion of previous operation.") - return 1 - - if(default_deconstruction_screwdriver(user, "autolathe_t", "autolathe", O)) - SSnanoui.update_uis(src) - return - - if(exchange_parts(user, O)) - return - - if(panel_open) - if(istype(O, /obj/item/crowbar)) - default_deconstruction_crowbar(O) - return 1 - else - attack_hand(user) - return 1 - if(stat) - return 1 - - // Disks in general - if(istype(O, /obj/item/disk)) - if(istype(O, /obj/item/disk/design_disk)) - var/obj/item/disk/design_disk/D = O - if(D.blueprint) - user.visible_message("[user] begins to load \the [O] in \the [src]...", - "You begin to load a design from \the [O]...", - "You hear the chatter of a floppy drive.") - playsound(get_turf(src), 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) - busy = 1 - if(do_after(user, 14.4, target = src)) - files.AddDesign2Known(D.blueprint) - busy = 0 - else - to_chat(user, "That disk does not have a design on it!") - return 1 - else - // So that people who are bad at computers don't shred their disks - to_chat(user, "This is not the correct type of disk for the autolathe!") - return 1 - - return ..() - -/obj/machinery/autolathe/proc/AfterMaterialInsert(type_inserted, id_inserted, amount_inserted) - switch(id_inserted) - if(MAT_METAL) - flick("autolathe_o", src)//plays metal insertion animation - if(MAT_GLASS) - flick("autolathe_r", src)//plays glass insertion animation - use_power(min(1000, amount_inserted / 100)) - SSnanoui.update_uis(src) - -/obj/machinery/autolathe/attack_ghost(mob/user) - interact(user) - -/obj/machinery/autolathe/attack_hand(mob/user) - if(..(user, 0)) - return - interact(user) - -/obj/machinery/autolathe/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["menu"]) - screen = text2num(href_list["menu"]) - - if(href_list["category"]) - selected_category = href_list["category"] - screen = AUTOLATHE_CATEGORY_MENU - - if(href_list["make"]) - BuildTurf = loc - - ///////////////// - //href protection - var/datum/design/design_last_ordered - design_last_ordered = files.FindDesignByID(href_list["make"]) //check if it's a valid design - if(!design_last_ordered) - return - if(!(design_last_ordered.build_type & AUTOLATHE)) - return - - //multiplier checks : only stacks can have one and its value is 1, 10 ,25 or max_multiplier - var/multiplier = text2num(href_list["multiplier"]) - GET_COMPONENT(materials, /datum/component/material_container) - var/max_multiplier = min(design_last_ordered.maxstack, design_last_ordered.materials[MAT_METAL] ?round(materials.amount(MAT_METAL)/design_last_ordered.materials[MAT_METAL]):INFINITY,design_last_ordered.materials[MAT_GLASS]?round(materials.amount(MAT_GLASS)/design_last_ordered.materials[MAT_GLASS]):INFINITY) - var/is_stack = ispath(design_last_ordered.build_path, /obj/item/stack) - - if(!is_stack && (multiplier > 1)) - return - if(!(multiplier in list(1, 10, 25, max_multiplier))) //"enough materials ?" is checked in the build proc - return - ///////////////// - - if((queue.len + 1) < queue_max_len) - add_to_queue(design_last_ordered,multiplier) - else - to_chat(usr, "The autolathe queue is full!") - if(!busy) - busy = 1 - process_queue() - busy = 0 - - if(href_list["remove_from_queue"]) - var/index = text2num(href_list["remove_from_queue"]) - if(isnum(index) && IsInRange(index, 1, queue.len)) - remove_from_queue(index) - if(href_list["queue_move"] && href_list["index"]) - var/index = text2num(href_list["index"]) - var/new_index = index + text2num(href_list["queue_move"]) - if(isnum(index) && isnum(new_index)) - if(IsInRange(new_index, 1, queue.len)) - queue.Swap(index,new_index) - if(href_list["clear_queue"]) - queue = list() - if(href_list["search"]) - if(href_list["to_search"]) - temp_search = href_list["to_search"] - if(!temp_search) - return - matching_designs.Cut() - - for(var/v in files.known_designs) - var/datum/design/D = files.known_designs[v] - if(findtext(D.name, temp_search)) - matching_designs.Add(D) - - screen = AUTOLATHE_SEARCH_MENU - - SSnanoui.update_uis(src) - return 1 - -/obj/machinery/autolathe/RefreshParts() - var/tot_rating = 0 - prod_coeff = 0 - for(var/obj/item/stock_parts/matter_bin/MB in component_parts) - tot_rating += MB.rating - tot_rating *= 25000 - GET_COMPONENT(materials, /datum/component/material_container) - materials.max_amount = tot_rating * 3 - for(var/obj/item/stock_parts/manipulator/M in component_parts) - prod_coeff += M.rating - 1 - -/obj/machinery/autolathe/proc/get_coeff(datum/design/D) - var/coeff = (ispath(D.build_path,/obj/item/stack) ? 1 : 2 ** prod_coeff)//stacks are unaffected by production coefficient - return coeff - -/obj/machinery/autolathe/proc/build_item(datum/design/D, multiplier) - desc = initial(desc)+"\nIt's building \a [initial(D.name)]." - var/is_stack = ispath(D.build_path, /obj/item/stack) - var/coeff = get_coeff(D) - GET_COMPONENT(materials, /datum/component/material_container) - var/metal_cost = D.materials[MAT_METAL] - var/glass_cost = D.materials[MAT_GLASS] - var/power = max(2000, (metal_cost+glass_cost)*multiplier/5) - if(can_build(D, multiplier)) - being_built = list(D, multiplier) - use_power(power) - icon_state = "autolathe" - flick("autolathe_n",src) - if(is_stack) - var/list/materials_used = list(MAT_METAL=metal_cost*multiplier, MAT_GLASS=glass_cost*multiplier) - materials.use_amount(materials_used) - else - var/list/materials_used = list(MAT_METAL=metal_cost/coeff, MAT_GLASS=glass_cost/coeff) - materials.use_amount(materials_used) - SSnanoui.update_uis(src) - sleep(32/coeff) - if(is_stack) - var/obj/item/stack/S = new D.build_path(BuildTurf) - S.amount = multiplier - else - var/obj/item/new_item = new D.build_path(BuildTurf) - new_item.materials[MAT_METAL] /= coeff - new_item.materials[MAT_GLASS] /= coeff - SSnanoui.update_uis(src) - desc = initial(desc) - -/obj/machinery/autolathe/proc/can_build(datum/design/D, multiplier = 1, custom_metal, custom_glass) - if(D.make_reagents.len) - return 0 - - var/coeff = get_coeff(D) - GET_COMPONENT(materials, /datum/component/material_container) - var/metal_amount = materials.amount(MAT_METAL) - if(custom_metal) - metal_amount = custom_metal - var/glass_amount = materials.amount(MAT_GLASS) - if(custom_glass) - glass_amount = custom_glass - - if(D.materials[MAT_METAL] && (metal_amount < (multiplier*D.materials[MAT_METAL] / coeff))) - return 0 - if(D.materials[MAT_GLASS] && (glass_amount < (multiplier*D.materials[MAT_GLASS] / coeff))) - return 0 - return 1 - -/obj/machinery/autolathe/proc/get_design_cost_as_list(datum/design/D, multiplier = 1) - var/list/OutputList = list(0,0) - var/coeff = get_coeff(D) - if(D.materials[MAT_METAL]) - OutputList[1] = (D.materials[MAT_METAL] / coeff)*multiplier - if(D.materials[MAT_GLASS]) - OutputList[2] = (D.materials[MAT_GLASS] / coeff)*multiplier - return OutputList - -/obj/machinery/autolathe/proc/get_processing_line() - var/datum/design/D = being_built[1] - var/multiplier = being_built[2] - var/is_stack = (multiplier>1) - var/output = "PROCESSING: [initial(D.name)][is_stack?" (x[multiplier])":null]" - return output - -/obj/machinery/autolathe/proc/add_to_queue(D, multiplier) - if(!istype(queue)) - queue = list() - if(D) - queue.Add(list(list(D,multiplier))) - return queue.len - -/obj/machinery/autolathe/proc/remove_from_queue(index) - if(!isnum(index) || !istype(queue) || (index<1 || index>queue.len)) - return 0 - queue.Cut(index,++index) - return 1 - -/obj/machinery/autolathe/proc/process_queue() - var/datum/design/D = queue[1][1] - var/multiplier = queue[1][2] - if(!D) - remove_from_queue(1) - if(queue.len) - return process_queue() - else - return - while(D) - if(stat & (NOPOWER|BROKEN)) - being_built = new /list() - return 0 - if(!can_build(D, multiplier)) - visible_message("[bicon(src)] \The [src] beeps, \"Not enough resources. Queue processing terminated.\"") - queue = list() - being_built = new /list() - return 0 - - remove_from_queue(1) - build_item(D,multiplier) - D = listgetindex(listgetindex(queue, 1),1) - multiplier = listgetindex(listgetindex(queue,1),2) - being_built = new /list() - //visible_message("[bicon(src)] \The [src] beeps, \"Queue processing finished successfully.\"") - -/obj/machinery/autolathe/proc/adjust_hacked(hack) - hacked = hack - - if(hack) - for(var/datum/design/D in files.possible_designs) - if((D.build_type & AUTOLATHE) && ("hacked" in D.category)) - files.AddDesign2Known(D) - else - for(var/datum/design/D in files.known_designs) - if("hacked" in D.category) - files.known_designs -= D.id +#define AUTOLATHE_MAIN_MENU 1 +#define AUTOLATHE_CATEGORY_MENU 2 +#define AUTOLATHE_SEARCH_MENU 3 + +/obj/machinery/autolathe + name = "autolathe" + desc = "It produces items using metal and glass." + icon_state = "autolathe" + density = 1 + + var/operating = 0.0 + var/list/queue = list() + var/queue_max_len = 12 + var/turf/BuildTurf + anchored = 1.0 + var/list/L = list() + var/list/LL = list() + var/hacked = 0 + var/disabled = 0 + var/shocked = 0 + var/hack_wire + var/disable_wire + var/shock_wire + use_power = IDLE_POWER_USE + idle_power_usage = 10 + active_power_usage = 100 + var/busy = 0 + var/prod_coeff + var/datum/wires/autolathe/wires = null + + var/list/being_built = list() + var/datum/research/files + var/list/datum/design/matching_designs + var/temp_search + var/selected_category + var/screen = 1 + + var/list/categories = list("Tools", "Electronics", "Construction", "Communication", "Security", "Machinery", "Medical", "Miscellaneous", "Dinnerware", "Imported") + +/obj/machinery/autolathe/New() + AddComponent(/datum/component/material_container, list(MAT_METAL, MAT_GLASS), 0, TRUE, null, null, CALLBACK(src, .proc/AfterMaterialInsert)) + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/autolathe(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stack/sheet/glass(null) + RefreshParts() + + wires = new(src) + files = new /datum/research/autolathe(src) + matching_designs = list() + +/obj/machinery/autolathe/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/autolathe(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/manipulator/pico(null) + component_parts += new /obj/item/stack/sheet/glass(null) + RefreshParts() + +/obj/machinery/autolathe/Destroy() + QDEL_NULL(wires) + GET_COMPONENT(materials, /datum/component/material_container) + materials.retrieve_all() + return ..() + +/obj/machinery/autolathe/interact(mob/user) + if(shocked && !(stat & NOPOWER)) + if(shock(user, 50)) + return + + if(panel_open) + wires.Interact(user) + else + ui_interact(user) + +/obj/machinery/autolathe/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "autolathe.tmpl", name, 800, 550) + ui.open() + +/obj/machinery/autolathe/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) + GET_COMPONENT(materials, /datum/component/material_container) + var/data[0] + data["screen"] = screen + data["total_amount"] = materials.total_amount + data["max_amount"] = materials.max_amount + data["metal_amount"] = materials.amount(MAT_METAL) + data["glass_amount"] = materials.amount(MAT_GLASS) + switch(screen) + if(AUTOLATHE_MAIN_MENU) + data["uid"] = UID() + data["categories"] = categories + if(AUTOLATHE_CATEGORY_MENU) + data["selected_category"] = selected_category + var/list/designs = list() + data["designs"] = designs + for(var/v in files.known_designs) + var/datum/design/D = files.known_designs[v] + if(!(selected_category in D.category)) + continue + var/list/design = list() + designs[++designs.len] = design + design["name"] = D.name + design["id"] = D.id + design["disabled"] = disabled || !can_build(D) ? "disabled" : null + if(ispath(D.build_path, /obj/item/stack)) + design["max_multiplier"] = min(D.maxstack, D.materials[MAT_METAL] ? round(materials.amount(MAT_METAL) / D.materials[MAT_METAL]) : INFINITY, D.materials[MAT_GLASS] ? round(materials.amount(MAT_GLASS) / D.materials[MAT_GLASS]) : INFINITY) + else + design["max_multiplier"] = null + design["materials"] = design_cost_data(D) + if(AUTOLATHE_SEARCH_MENU) + data["search"] = temp_search + var/list/designs = list() + data["designs"] = designs + for(var/datum/design/D in matching_designs) + var/list/design = list() + designs[++designs.len] = design + design["name"] = D.name + design["id"] = D.id + design["disabled"] = disabled || !can_build(D) ? "disabled" : null + if(ispath(D.build_path, /obj/item/stack)) + design["max_multiplier"] = min(D.maxstack, D.materials[MAT_METAL] ? round(materials.amount(MAT_METAL) / D.materials[MAT_METAL]) : INFINITY, D.materials[MAT_GLASS] ? round(materials.amount(MAT_GLASS) / D.materials[MAT_GLASS]) : INFINITY) + else + design["max_multiplier"] = null + design["materials"] = design_cost_data(D) + + data = queue_data(data) + return data + +/obj/machinery/autolathe/proc/design_cost_data(datum/design/D) + var/list/data = list() + var/coeff = get_coeff(D) + GET_COMPONENT(materials, /datum/component/material_container) + var/has_metal = 1 + if(D.materials[MAT_METAL] && (materials.amount(MAT_METAL) < (D.materials[MAT_METAL] / coeff))) + has_metal = 0 + var/has_glass = 1 + if(D.materials[MAT_GLASS] && (materials.amount(MAT_GLASS) < (D.materials[MAT_GLASS] / coeff))) + has_glass = 0 + + data[++data.len] = list("name" = "metal", "amount" = D.materials[MAT_METAL] / coeff, "is_red" = !has_metal) + data[++data.len] = list("name" = "glass", "amount" = D.materials[MAT_GLASS] / coeff, "is_red" = !has_glass) + + return data + +/obj/machinery/autolathe/proc/queue_data(list/data) + GET_COMPONENT(materials, /datum/component/material_container) + var/temp_metal = materials.amount(MAT_METAL) + var/temp_glass = materials.amount(MAT_GLASS) + data["processing"] = being_built.len ? get_processing_line() : null + if(istype(queue) && queue.len) + var/list/data_queue = list() + for(var/list/L in queue) + var/datum/design/D = L[1] + var/list/LL = get_design_cost_as_list(D, L[2]) + data_queue[++data_queue.len] = list("name" = initial(D.name), "can_build" = can_build(D, L[2], temp_metal, temp_glass), "multiplier" = L[2]) + temp_metal = max(temp_metal - LL[1], 1) + temp_glass = max(temp_glass - LL[2], 1) + data["queue"] = data_queue + data["queue_len"] = data_queue.len + else + data["queue"] = null + return data + +/obj/machinery/autolathe/attackby(obj/item/O, mob/user, params) + if(busy) + to_chat(user, "The autolathe is busy. Please wait for completion of previous operation.") + return 1 + if(exchange_parts(user, O)) + return + if(stat) + return 1 + + // Disks in general + if(istype(O, /obj/item/disk)) + if(istype(O, /obj/item/disk/design_disk)) + var/obj/item/disk/design_disk/D = O + if(D.blueprint) + user.visible_message("[user] begins to load \the [O] in \the [src]...", + "You begin to load a design from \the [O]...", + "You hear the chatter of a floppy drive.") + playsound(get_turf(src), 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) + busy = 1 + if(do_after(user, 14.4, target = src)) + files.AddDesign2Known(D.blueprint) + busy = 0 + else + to_chat(user, "That disk does not have a design on it!") + return 1 + else + // So that people who are bad at computers don't shred their disks + to_chat(user, "This is not the correct type of disk for the autolathe!") + return 1 + + return ..() + +/obj/machinery/autolathe/crowbar_act(mob/user, obj/item/I) + if(!I.use_tool(src, user, 0, volume = 0)) + return + . = TRUE + if(busy) + to_chat(user, "The autolathe is busy. Please wait for completion of previous operation.") + return + if(panel_open) + default_deconstruction_crowbar(user, I) + I.play_tool_sound(user, I.tool_volume) + +/obj/machinery/autolathe/screwdriver_act(mob/user, obj/item/I) + if(!I.use_tool(src, user, 0, volume = 0)) + return + . = TRUE + if(busy) + to_chat(user, "The autolathe is busy. Please wait for completion of previous operation.") + return + if(default_deconstruction_screwdriver(user, "autolathe_t", "autolathe", I)) + SSnanoui.update_uis(src) + I.play_tool_sound(user, I.tool_volume) + +/obj/machinery/autolathe/wirecutter_act(mob/user, obj/item/I) + if(!panel_open) + return + if(!I.use_tool(src, user, 0, volume = 0)) + return + . = TRUE + if(busy) + to_chat(user, "The autolathe is busy. Please wait for completion of previous operation.") + return + interact(user) + +/obj/machinery/autolathe/multitool_act(mob/user, obj/item/I) + if(!panel_open) + return + if(!I.use_tool(src, user, 0, volume = 0)) + return + . = TRUE + if(busy) + to_chat(user, "The autolathe is busy. Please wait for completion of previous operation.") + return + interact(user) + +/obj/machinery/autolathe/proc/AfterMaterialInsert(type_inserted, id_inserted, amount_inserted) + switch(id_inserted) + if(MAT_METAL) + flick("autolathe_o", src)//plays metal insertion animation + if(MAT_GLASS) + flick("autolathe_r", src)//plays glass insertion animation + use_power(min(1000, amount_inserted / 100)) + SSnanoui.update_uis(src) + +/obj/machinery/autolathe/attack_ghost(mob/user) + interact(user) + +/obj/machinery/autolathe/attack_hand(mob/user) + if(..(user, 0)) + return + interact(user) + +/obj/machinery/autolathe/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["menu"]) + screen = text2num(href_list["menu"]) + + if(href_list["category"]) + selected_category = href_list["category"] + screen = AUTOLATHE_CATEGORY_MENU + + if(href_list["make"]) + BuildTurf = loc + + ///////////////// + //href protection + var/datum/design/design_last_ordered + design_last_ordered = files.FindDesignByID(href_list["make"]) //check if it's a valid design + if(!design_last_ordered) + return + if(!(design_last_ordered.build_type & AUTOLATHE)) + return + + //multiplier checks : only stacks can have one and its value is 1, 10 ,25 or max_multiplier + var/multiplier = text2num(href_list["multiplier"]) + GET_COMPONENT(materials, /datum/component/material_container) + var/max_multiplier = min(design_last_ordered.maxstack, design_last_ordered.materials[MAT_METAL] ?round(materials.amount(MAT_METAL)/design_last_ordered.materials[MAT_METAL]):INFINITY,design_last_ordered.materials[MAT_GLASS]?round(materials.amount(MAT_GLASS)/design_last_ordered.materials[MAT_GLASS]):INFINITY) + var/is_stack = ispath(design_last_ordered.build_path, /obj/item/stack) + + if(!is_stack && (multiplier > 1)) + return + if(!(multiplier in list(1, 10, 25, max_multiplier))) //"enough materials ?" is checked in the build proc + return + ///////////////// + + if((queue.len + 1) < queue_max_len) + add_to_queue(design_last_ordered,multiplier) + else + to_chat(usr, "The autolathe queue is full!") + if(!busy) + busy = 1 + process_queue() + busy = 0 + + if(href_list["remove_from_queue"]) + var/index = text2num(href_list["remove_from_queue"]) + if(isnum(index) && IsInRange(index, 1, queue.len)) + remove_from_queue(index) + if(href_list["queue_move"] && href_list["index"]) + var/index = text2num(href_list["index"]) + var/new_index = index + text2num(href_list["queue_move"]) + if(isnum(index) && isnum(new_index)) + if(IsInRange(new_index, 1, queue.len)) + queue.Swap(index,new_index) + if(href_list["clear_queue"]) + queue = list() + if(href_list["search"]) + if(href_list["to_search"]) + temp_search = href_list["to_search"] + if(!temp_search) + return + matching_designs.Cut() + + for(var/v in files.known_designs) + var/datum/design/D = files.known_designs[v] + if(findtext(D.name, temp_search)) + matching_designs.Add(D) + + screen = AUTOLATHE_SEARCH_MENU + + SSnanoui.update_uis(src) + return 1 + +/obj/machinery/autolathe/RefreshParts() + var/tot_rating = 0 + prod_coeff = 0 + for(var/obj/item/stock_parts/matter_bin/MB in component_parts) + tot_rating += MB.rating + tot_rating *= 25000 + GET_COMPONENT(materials, /datum/component/material_container) + materials.max_amount = tot_rating * 3 + for(var/obj/item/stock_parts/manipulator/M in component_parts) + prod_coeff += M.rating - 1 + +/obj/machinery/autolathe/proc/get_coeff(datum/design/D) + var/coeff = (ispath(D.build_path,/obj/item/stack) ? 1 : 2 ** prod_coeff)//stacks are unaffected by production coefficient + return coeff + +/obj/machinery/autolathe/proc/build_item(datum/design/D, multiplier) + desc = initial(desc)+"\nIt's building \a [initial(D.name)]." + var/is_stack = ispath(D.build_path, /obj/item/stack) + var/coeff = get_coeff(D) + GET_COMPONENT(materials, /datum/component/material_container) + var/metal_cost = D.materials[MAT_METAL] + var/glass_cost = D.materials[MAT_GLASS] + var/power = max(2000, (metal_cost+glass_cost)*multiplier/5) + if(can_build(D, multiplier)) + being_built = list(D, multiplier) + use_power(power) + icon_state = "autolathe" + flick("autolathe_n",src) + if(is_stack) + var/list/materials_used = list(MAT_METAL=metal_cost*multiplier, MAT_GLASS=glass_cost*multiplier) + materials.use_amount(materials_used) + else + var/list/materials_used = list(MAT_METAL=metal_cost/coeff, MAT_GLASS=glass_cost/coeff) + materials.use_amount(materials_used) + SSnanoui.update_uis(src) + sleep(32/coeff) + if(is_stack) + var/obj/item/stack/S = new D.build_path(BuildTurf) + S.amount = multiplier + else + var/obj/item/new_item = new D.build_path(BuildTurf) + new_item.materials[MAT_METAL] /= coeff + new_item.materials[MAT_GLASS] /= coeff + SSnanoui.update_uis(src) + desc = initial(desc) + +/obj/machinery/autolathe/proc/can_build(datum/design/D, multiplier = 1, custom_metal, custom_glass) + if(D.make_reagents.len) + return 0 + + var/coeff = get_coeff(D) + GET_COMPONENT(materials, /datum/component/material_container) + var/metal_amount = materials.amount(MAT_METAL) + if(custom_metal) + metal_amount = custom_metal + var/glass_amount = materials.amount(MAT_GLASS) + if(custom_glass) + glass_amount = custom_glass + + if(D.materials[MAT_METAL] && (metal_amount < (multiplier*D.materials[MAT_METAL] / coeff))) + return 0 + if(D.materials[MAT_GLASS] && (glass_amount < (multiplier*D.materials[MAT_GLASS] / coeff))) + return 0 + return 1 + +/obj/machinery/autolathe/proc/get_design_cost_as_list(datum/design/D, multiplier = 1) + var/list/OutputList = list(0,0) + var/coeff = get_coeff(D) + if(D.materials[MAT_METAL]) + OutputList[1] = (D.materials[MAT_METAL] / coeff)*multiplier + if(D.materials[MAT_GLASS]) + OutputList[2] = (D.materials[MAT_GLASS] / coeff)*multiplier + return OutputList + +/obj/machinery/autolathe/proc/get_processing_line() + var/datum/design/D = being_built[1] + var/multiplier = being_built[2] + var/is_stack = (multiplier>1) + var/output = "PROCESSING: [initial(D.name)][is_stack?" (x[multiplier])":null]" + return output + +/obj/machinery/autolathe/proc/add_to_queue(D, multiplier) + if(!istype(queue)) + queue = list() + if(D) + queue.Add(list(list(D,multiplier))) + return queue.len + +/obj/machinery/autolathe/proc/remove_from_queue(index) + if(!isnum(index) || !istype(queue) || (index<1 || index>queue.len)) + return 0 + queue.Cut(index,++index) + return 1 + +/obj/machinery/autolathe/proc/process_queue() + var/datum/design/D = queue[1][1] + var/multiplier = queue[1][2] + if(!D) + remove_from_queue(1) + if(queue.len) + return process_queue() + else + return + while(D) + if(stat & (NOPOWER|BROKEN)) + being_built = new /list() + return 0 + if(!can_build(D, multiplier)) + visible_message("[bicon(src)] \The [src] beeps, \"Not enough resources. Queue processing terminated.\"") + queue = list() + being_built = new /list() + return 0 + + remove_from_queue(1) + build_item(D,multiplier) + D = listgetindex(listgetindex(queue, 1),1) + multiplier = listgetindex(listgetindex(queue,1),2) + being_built = new /list() + //visible_message("[bicon(src)] \The [src] beeps, \"Queue processing finished successfully.\"") + +/obj/machinery/autolathe/proc/adjust_hacked(hack) + hacked = hack + + if(hack) + for(var/datum/design/D in files.possible_designs) + if((D.build_type & AUTOLATHE) && ("hacked" in D.category)) + files.AddDesign2Known(D) + else + for(var/datum/design/D in files.known_designs) + if("hacked" in D.category) + files.known_designs -= D.id diff --git a/code/game/machinery/buttons.dm b/code/game/machinery/buttons.dm index f91152b47e4..796d5c552dd 100644 --- a/code/game/machinery/buttons.dm +++ b/code/game/machinery/buttons.dm @@ -1,206 +1,206 @@ - -////////////////////////////////////// -// Driver Button // -////////////////////////////////////// - -/obj/machinery/driver_button - name = "mass driver button" - icon = 'icons/obj/objects.dmi' - icon_state = "launcherbtt" - desc = "A remote control switch for a mass driver." - var/id_tag = "default" - var/active = 0 - settagwhitelist = list("id_tag", "logic_id_tag") - anchored = 1.0 - armor = list(melee = 50, bullet = 50, laser = 50, energy = 50, bomb = 10, bio = 100, rad = 100, fire = 90, acid = 70) - use_power = IDLE_POWER_USE - idle_power_usage = 2 - active_power_usage = 4 - resistance_flags = LAVA_PROOF | FIRE_PROOF - var/range = 7 - - var/datum/radio_frequency/radio_connection - var/frequency = 0 - var/logic_id_tag = "default" //Defines the ID tag to send logic signals to, so you don't have to unlink from doors and stuff - var/logic_connect = 0 //Set this to allow the button to send out logic signals when pressed in addition to normal stuff - -/obj/machinery/button/indestructible - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - -/obj/machinery/driver_button/New(turf/loc, var/w_dir=null) - ..() - switch(w_dir) - if(NORTH) - pixel_y = 25 - if(SOUTH) - pixel_y = -25 - if(EAST) - pixel_x = 25 - if(WEST) - pixel_x = -25 - if(SSradio) - set_frequency(frequency) - -/obj/machinery/driver_button/Initialize() - ..() - set_frequency(frequency) - -/obj/machinery/driver_button/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - radio_connection = SSradio.add_object(src, frequency, RADIO_LOGIC) - return - -/obj/machinery/driver_button/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - return ..() - - -/obj/machinery/driver_button/attack_ai(mob/user as mob) - return attack_hand(user) - -/obj/machinery/driver_button/attack_ghost(mob/user) - if(user.can_advanced_admin_interact()) - return attack_hand(user) - -/obj/machinery/driver_button/attackby(obj/item/W, mob/user as mob, params) - - if(istype(W, /obj/item/detective_scanner)) - return - - if(istype(W, /obj/item/multitool)) - update_multitool_menu(user) - return 1 - - if(istype(W, /obj/item/wrench)) - playsound(get_turf(src), W.usesound, 50, 1) - if(do_after(user, 30 * W.toolspeed, target = src)) - to_chat(user, "You detach \the [src] from the wall.") - new/obj/item/mounted/frame/driver_button(get_turf(src)) - qdel(src) - return 1 - - return ..() - -/obj/machinery/driver_button/multitool_menu(var/mob/user, var/obj/item/multitool/P) - return {" -
          -
        • ID Tag: [format_tag("ID Tag","id_tag")]
        • -
        • Logic Connection: [logic_connect ? "On" : "Off"]
        • -
        • Logic ID Tag: [format_tag("Logic ID Tag", "logic_id_tag")]
        • -
        "} - -/obj/machinery/driver_button/attack_hand(mob/user as mob) - - add_fingerprint(usr) - if(stat & (NOPOWER|BROKEN)) - return - if(active) - return - add_fingerprint(user) - - use_power(5) - - launch_sequence() - -/obj/machinery/driver_button/proc/launch_sequence() - active = 1 - icon_state = "launcheract" - - if(logic_connect) - if(!radio_connection) //can't output without this - return - - if(logic_id_tag == null) //Don't output to an undefined id_tag - return - - var/datum/signal/signal = new - signal.transmission_method = 1 //radio signal - signal.source = src - - signal.data = list( - "tag" = logic_id_tag, - "sigtype" = "logic", - "state" = LOGIC_FLICKER, //Buttons are a FLICKER source, since they only register as ON when you press it, then turn OFF after you release - ) - - radio_connection.post_signal(src, signal, filter = RADIO_LOGIC) - - for(var/obj/machinery/door/poddoor/M in range(src,range)) - if(M.id_tag == id_tag && !M.protected) - spawn() - M.open() - - sleep(20) - - for(var/obj/machinery/mass_driver/M in range(src,range)) - if(M.id_tag == id_tag) - M.drive() - - sleep(50) - - for(var/obj/machinery/door/poddoor/M in range(src,range)) - if(M.id_tag == id_tag && !M.protected) - spawn() - M.close() - return - - icon_state = "launcherbtt" - active = 0 - -/obj/machinery/driver_button/multitool_topic(var/mob/user,var/list/href_list,var/obj/O) - ..() - if("toggle_logic" in href_list) - logic_connect = !logic_connect - -////////////////////////////////////// -// Ignition Switch // -////////////////////////////////////// - -/obj/machinery/ignition_switch - name = "ignition switch" - icon = 'icons/obj/objects.dmi' - icon_state = "launcherbtt" - desc = "A remote control switch for a mounted igniter." - var/id = null - var/active = 0 - anchored = 1.0 - use_power = IDLE_POWER_USE - idle_power_usage = 2 - active_power_usage = 4 - -/obj/machinery/ignition_switch/attack_ai(mob/user) - return attack_hand(user) - -/obj/machinery/ignition_switch/attack_ghost(mob/user) - if(user.can_advanced_admin_interact()) - return attack_hand(user) - -/obj/machinery/ignition_switch/attack_hand(mob/user) - if(stat & (NOPOWER|BROKEN)) - return - if(active) - return - - use_power(5) - - active = 1 - icon_state = "launcheract" - - for(var/obj/machinery/sparker/M in world) - if(M.id == id) - spawn( 0 ) - M.spark() - - for(var/obj/machinery/igniter/M in world) - if(M.id == id) - use_power(50) - M.on = !( M.on ) - M.icon_state = text("igniter[]", M.on) - - sleep(50) - - icon_state = "launcherbtt" - active = 0 + +////////////////////////////////////// +// Driver Button // +////////////////////////////////////// + +/obj/machinery/driver_button + name = "mass driver button" + icon = 'icons/obj/objects.dmi' + icon_state = "launcherbtt" + desc = "A remote control switch for a mass driver." + var/id_tag = "default" + var/active = 0 + settagwhitelist = list("id_tag", "logic_id_tag") + anchored = 1.0 + armor = list(melee = 50, bullet = 50, laser = 50, energy = 50, bomb = 10, bio = 100, rad = 100, fire = 90, acid = 70) + use_power = IDLE_POWER_USE + idle_power_usage = 2 + active_power_usage = 4 + resistance_flags = LAVA_PROOF | FIRE_PROOF + var/range = 7 + + var/datum/radio_frequency/radio_connection + var/frequency = 0 + var/logic_id_tag = "default" //Defines the ID tag to send logic signals to, so you don't have to unlink from doors and stuff + var/logic_connect = 0 //Set this to allow the button to send out logic signals when pressed in addition to normal stuff + +/obj/machinery/button/indestructible + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + +/obj/machinery/driver_button/New(turf/loc, var/w_dir=null) + ..() + switch(w_dir) + if(NORTH) + pixel_y = 25 + if(SOUTH) + pixel_y = -25 + if(EAST) + pixel_x = 25 + if(WEST) + pixel_x = -25 + if(SSradio) + set_frequency(frequency) + +/obj/machinery/driver_button/Initialize() + ..() + set_frequency(frequency) + +/obj/machinery/driver_button/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + radio_connection = SSradio.add_object(src, frequency, RADIO_LOGIC) + return + +/obj/machinery/driver_button/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + return ..() + + +/obj/machinery/driver_button/attack_ai(mob/user as mob) + return attack_hand(user) + +/obj/machinery/driver_button/attack_ghost(mob/user) + if(user.can_advanced_admin_interact()) + return attack_hand(user) + +/obj/machinery/driver_button/attackby(obj/item/W, mob/user as mob, params) + + if(istype(W, /obj/item/detective_scanner)) + return + + if(istype(W, /obj/item/multitool)) + update_multitool_menu(user) + return 1 + + if(istype(W, /obj/item/wrench)) + playsound(get_turf(src), W.usesound, 50, 1) + if(do_after(user, 30 * W.toolspeed, target = src)) + to_chat(user, "You detach \the [src] from the wall.") + new/obj/item/mounted/frame/driver_button(get_turf(src)) + qdel(src) + return 1 + + return ..() + +/obj/machinery/driver_button/multitool_menu(var/mob/user, var/obj/item/multitool/P) + return {" +
          +
        • ID Tag: [format_tag("ID Tag","id_tag")]
        • +
        • Logic Connection: [logic_connect ? "On" : "Off"]
        • +
        • Logic ID Tag: [format_tag("Logic ID Tag", "logic_id_tag")]
        • +
        "} + +/obj/machinery/driver_button/attack_hand(mob/user as mob) + + add_fingerprint(usr) + if(stat & (NOPOWER|BROKEN)) + return + if(active) + return + add_fingerprint(user) + + use_power(5) + + launch_sequence() + +/obj/machinery/driver_button/proc/launch_sequence() + active = 1 + icon_state = "launcheract" + + if(logic_connect) + if(!radio_connection) //can't output without this + return + + if(logic_id_tag == null) //Don't output to an undefined id_tag + return + + var/datum/signal/signal = new + signal.transmission_method = 1 //radio signal + signal.source = src + + signal.data = list( + "tag" = logic_id_tag, + "sigtype" = "logic", + "state" = LOGIC_FLICKER, //Buttons are a FLICKER source, since they only register as ON when you press it, then turn OFF after you release + ) + + radio_connection.post_signal(src, signal, filter = RADIO_LOGIC) + + for(var/obj/machinery/door/poddoor/M in range(src,range)) + if(M.id_tag == id_tag && !M.protected) + spawn() + M.open() + + sleep(20) + + for(var/obj/machinery/mass_driver/M in range(src,range)) + if(M.id_tag == id_tag) + M.drive() + + sleep(50) + + for(var/obj/machinery/door/poddoor/M in range(src,range)) + if(M.id_tag == id_tag && !M.protected) + spawn() + M.close() + return + + icon_state = "launcherbtt" + active = 0 + +/obj/machinery/driver_button/multitool_topic(var/mob/user,var/list/href_list,var/obj/O) + ..() + if("toggle_logic" in href_list) + logic_connect = !logic_connect + +////////////////////////////////////// +// Ignition Switch // +////////////////////////////////////// + +/obj/machinery/ignition_switch + name = "ignition switch" + icon = 'icons/obj/objects.dmi' + icon_state = "launcherbtt" + desc = "A remote control switch for a mounted igniter." + var/id = null + var/active = 0 + anchored = 1.0 + use_power = IDLE_POWER_USE + idle_power_usage = 2 + active_power_usage = 4 + +/obj/machinery/ignition_switch/attack_ai(mob/user) + return attack_hand(user) + +/obj/machinery/ignition_switch/attack_ghost(mob/user) + if(user.can_advanced_admin_interact()) + return attack_hand(user) + +/obj/machinery/ignition_switch/attack_hand(mob/user) + if(stat & (NOPOWER|BROKEN)) + return + if(active) + return + + use_power(5) + + active = 1 + icon_state = "launcheract" + + for(var/obj/machinery/sparker/M in world) + if(M.id == id) + spawn( 0 ) + M.spark() + + for(var/obj/machinery/igniter/M in world) + if(M.id == id) + use_power(50) + M.on = !( M.on ) + M.icon_state = text("igniter[]", M.on) + + sleep(50) + + icon_state = "launcherbtt" + active = 0 diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index 7e0508ba24f..c097a19f4f8 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -1,419 +1,433 @@ -/obj/machinery/camera - name = "security camera" - desc = "It's used to monitor rooms." - icon = 'icons/obj/monitors.dmi' - icon_state = "camera" - use_power = ACTIVE_POWER_USE - idle_power_usage = 5 - active_power_usage = 10 - layer = WALL_OBJ_LAYER - resistance_flags = FIRE_PROOF - damage_deflection = 12 - armor = list("melee" = 50, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 50) - var/datum/wires/camera/wires = null // Wires datum - max_integrity = 100 - integrity_failure = 50 - var/list/network = list("SS13") - var/c_tag = null - var/c_tag_order = 999 - var/status = 1 - anchored = TRUE - var/start_active = FALSE //If it ignores the random chance to start broken on round start - var/invuln = null - var/obj/item/camera_bug/bug = null - var/obj/item/camera_assembly/assembly = null - - //OTHER - - var/view_range = 7 - var/short_range = 2 - - var/busy = FALSE - var/emped = FALSE //Number of consecutive EMP's on this camera - - var/in_use_lights = 0 // TO BE IMPLEMENTED - var/toggle_sound = 'sound/items/wirecutter.ogg' - - -/obj/machinery/camera/New() - ..() - wires = new(src) - assembly = new(src) - assembly.state = 4 - assembly.anchored = 1 - assembly.update_icon() - - cameranet.cameras += src - cameranet.addCamera(src) - -/obj/machinery/camera/Initialize() - ..() - if(is_station_level(z) && prob(3) && !start_active) - toggle_cam(null, FALSE) - wires.CutAll() - -/obj/machinery/camera/Destroy() - toggle_cam(null, FALSE) //kick anyone viewing out - QDEL_NULL(assembly) - if(istype(bug)) - bug.bugged_cameras -= c_tag - if(bug.current == src) - bug.current = null - bug = null - QDEL_NULL(wires) - cameranet.removeCamera(src) //Will handle removal from the camera network and the chunks, so we don't need to worry about that - cameranet.cameras -= src - var/area/ai_monitored/A = get_area(src) - if(istype(A)) - A.motioncamera = null - area_motion = null - return ..() - -/obj/machinery/camera/emp_act(severity) - if(!status) - return - if(!isEmpProof()) - if(prob(150/severity)) - update_icon() - var/list/previous_network = network - network = list() - cameranet.removeCamera(src) - stat |= EMPED - set_light(0) - emped = emped+1 //Increase the number of consecutive EMP's - update_icon() - var/thisemp = emped //Take note of which EMP this proc is for - spawn(900) - if(!QDELETED(src)) - triggerCameraAlarm() //camera alarm triggers even if multiple EMPs are in effect. - if(emped == thisemp) //Only fix it if the camera hasn't been EMP'd again - network = previous_network - stat &= ~EMPED - update_icon() - if(can_use()) - cameranet.addCamera(src) - emped = 0 //Resets the consecutive EMP count - spawn(100) - if(!QDELETED(src)) - cancelCameraAlarm() - for(var/mob/M in GLOB.player_list) - if(M.client && M.client.eye == src) - M.unset_machine() - M.reset_perspective(null) - to_chat(M, "The screen bursts into static.") - ..() - -/obj/machinery/camera/tesla_act(power)//EMP proof upgrade also makes it tesla immune - if(isEmpProof()) - return - ..() - qdel(src)//to prevent bomb testing camera from exploding over and over forever - -/obj/machinery/camera/ex_act(severity) - if(invuln) - return - ..() - -/obj/machinery/camera/proc/setViewRange(num = 7) - view_range = num - cameranet.updateVisibility(src, 0) - -/obj/machinery/camera/singularity_pull(S, current_size) - if (status && current_size >= STAGE_FIVE) // If the singulo is strong enough to pull anchored objects and the camera is still active, turn off the camera as it gets ripped off the wall. - toggle_cam(null, 0) - ..() - -/obj/machinery/camera/attackby(obj/item/I, mob/living/user, params) - var/msg = "You attach [I] into the assembly inner circuits." - var/msg2 = "The camera already has that upgrade!" - - // DECONSTRUCTION - if(isscrewdriver(I)) - panel_open = !panel_open - to_chat(user, "You screw the camera's panel [panel_open ? "open" : "closed"].") - playsound(loc, I.usesound, 50, 1) - - else if((iswirecutter(I) || ismultitool(I)) && panel_open) - wires.Interact(user) - - else if(iswelder(I) && panel_open && wires.CanDeconstruct()) - var/obj/item/weldingtool/WT = I - if(!WT.remove_fuel(0, user)) - return - to_chat(user, "You start to weld [src]...") - playsound(loc, WT.usesound, 50, 1) - if(do_after(user, 100 * WT.toolspeed, target = src)) - user.visible_message("[user] unwelds [src], leaving it as just a frame bolted to the wall.", - "You unweld [src], leaving it as just a frame bolted to the wall") - deconstruct(TRUE) - - else if(istype(I, /obj/item/analyzer) && panel_open) //XRay - if(!user.drop_item()) - to_chat(user, "[I] is stuck to your hand!") - return - if(!isXRay()) - upgradeXRay() - qdel(I) - to_chat(user, "[msg]") - else - to_chat(user, "[msg2]") - - else if(istype(I, /obj/item/stack/sheet/mineral/plasma) && panel_open) - if(!user.drop_item()) - to_chat(user, "[I] is stuck to your hand!") - return - if(!isEmpProof()) - var/obj/item/stack/sheet/mineral/plasma/P = I - upgradeEmpProof() - to_chat(user, "[msg]") - P.use(1) - else - to_chat(user, "[msg2]") - else if(istype(I, /obj/item/assembly/prox_sensor) && panel_open) - if(!user.drop_item()) - to_chat(user, "[I] is stuck to your hand!") - return - if(!isMotion()) - upgradeMotion() - to_chat(user, "[msg]") - qdel(I) - else - to_chat(user, "[msg2]") - - // OTHER - else if((istype(I, /obj/item/paper) || istype(I, /obj/item/pda)) && isliving(user)) - var/mob/living/U = user - var/obj/item/paper/X = null - var/obj/item/pda/PDA = null - - var/itemname = "" - var/info = "" - if(istype(I, /obj/item/paper)) - X = I - itemname = X.name - info = X.info - else - PDA = I - var/datum/data/pda/app/notekeeper/N = PDA.find_program(/datum/data/pda/app/notekeeper) - if(N) - itemname = PDA.name - info = N.notehtml - to_chat(U, "You hold \the [itemname] up to the camera ...") - U.changeNext_move(CLICK_CD_MELEE) - for(var/mob/O in GLOB.player_list) - if(istype(O, /mob/living/silicon/ai)) - var/mob/living/silicon/ai/AI = O - if(AI.control_disabled || (AI.stat == DEAD)) - return - if(U.name == "Unknown") - to_chat(AI, "[U] holds \a [itemname] up to one of your cameras ...") - else - to_chat(AI, "[U] holds \a [itemname] up to one of your cameras ...") - AI.last_paper_seen = "[itemname][info]" - else if(O.client && O.client.eye == src) - to_chat(O, "[U] holds \a [itemname] up to one of the cameras ...") - O << browse(text("[][]", itemname, info), text("window=[]", itemname)) - - else if(istype(I, /obj/item/camera_bug)) - if(!can_use()) - to_chat(user, "Camera non-functional.") - return - if(istype(bug)) - to_chat(user, "Camera bug removed.") - bug.bugged_cameras -= c_tag - bug = null - else - to_chat(user, "Camera bugged.") - bug = I - bug.bugged_cameras[c_tag] = src - - else if(istype(I, /obj/item/laser_pointer)) - var/obj/item/laser_pointer/L = I - L.laser_act(src, user) - else - return ..() - -/obj/machinery/camera/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) - if(stat & BROKEN) - return damage_amount - . = ..() - -/obj/machinery/camera/obj_break(damage_flag) - if(status && !(flags & NODECONSTRUCT)) - triggerCameraAlarm() - toggle_cam(null, FALSE) - wires.CutAll() - -/obj/machinery/camera/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - if(disassembled) - if(!assembly) - assembly = new() - assembly.forceMove(drop_location()) - assembly.state = 1 - assembly.setDir(dir) - assembly.update_icon() - assembly = null - else - var/obj/item/I = new /obj/item/camera_assembly(loc) - I.obj_integrity = I.max_integrity * 0.5 - new /obj/item/stack/cable_coil(loc, 2) - qdel(src) - -/obj/machinery/camera/update_icon() - if(!status) - icon_state = "[initial(icon_state)]1" - else if(stat & EMPED) - icon_state = "[initial(icon_state)]emp" - else - icon_state = "[initial(icon_state)]" - -/obj/machinery/camera/proc/toggle_cam(mob/user, displaymessage = TRUE) - status = !status - if(can_use()) - cameranet.addCamera(src) - else - set_light(0) - cameranet.removeCamera(src) - cameranet.updateChunk(x, y, z) - var/change_msg = "deactivates" - if(status) - change_msg = "reactivates" - triggerCameraAlarm() - spawn(100) - if(!QDELETED(src)) - cancelCameraAlarm() - if(displaymessage) - if(user) - visible_message("[user] [change_msg] [src]!") - add_hiddenprint(user) - else - visible_message("\The [src] [change_msg]!") - - playsound(loc, toggle_sound, 100, 1) - update_icon() - - // now disconnect anyone using the camera - //Apparently, this will disconnect anyone even if the camera was re-activated. - //I guess that doesn't matter since they can't use it anyway? - for(var/mob/O in GLOB.player_list) - if(O.client && O.client.eye == src) - O.unset_machine() - O.reset_perspective(null) - to_chat(O, "The screen bursts into static.") - -/obj/machinery/camera/proc/triggerCameraAlarm() - if(is_station_contact(z)) - SSalarms.camera_alarm.triggerAlarm(loc, src) - -/obj/machinery/camera/proc/cancelCameraAlarm() - if(is_station_contact(z)) - SSalarms.camera_alarm.clearAlarm(loc, src) - -/obj/machinery/camera/proc/can_use() - if(!status) - return 0 - if(stat & EMPED) - return 0 - return 1 - -/obj/machinery/camera/proc/can_see() - var/list/see = null - var/turf/pos = get_turf(src) - if(isXRay()) - see = range(view_range, pos) - else - see = hear(view_range, pos) - return see - -/atom/proc/auto_turn() - //Automatically turns based on nearby walls. - var/turf/simulated/wall/T = null - for(var/i = 1, i <= 8; i += i) - T = get_ranged_target_turf(src, i, 1) - if(istype(T)) - //If someone knows a better way to do this, let me know. -Giacom - switch(i) - if(NORTH) - setDir(SOUTH) - if(SOUTH) - setDir(NORTH) - if(WEST) - setDir(EAST) - if(EAST) - setDir(WEST) - break - -//Return a working camera that can see a given mob -//or null if none -/proc/seen_by_camera(mob/M) - for(var/obj/machinery/camera/C in oview(4, M)) - if(C.can_use()) // check if camera disabled - return C - break - return null - -/proc/near_range_camera(mob/M) - for(var/obj/machinery/camera/C in range(4, M)) - if(C.can_use()) // check if camera disabled - return C - break - - return null - -/obj/machinery/camera/proc/Togglelight(on = FALSE) - for(var/mob/living/silicon/ai/A in ai_list) - for(var/obj/machinery/camera/cam in A.lit_cameras) - if(cam == src) - return - if(on) - set_light(AI_CAMERA_LUMINOSITY) - else - set_light(0) - -/obj/machinery/camera/proc/nano_structure() - var/cam[0] - var/turf/T = get_turf(src) - cam["name"] = sanitize(c_tag) - cam["deact"] = !can_use() - cam["camera"] = "\ref[src]" - if(T) - cam["x"] = T.x - cam["y"] = T.y - cam["z"] = T.z - else - cam["x"] = 0 - cam["y"] = 0 - cam["z"] = 0 - return cam - -/obj/machinery/camera/get_remote_view_fullscreens(mob/user) - if(view_range == short_range) //unfocused - user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 2) - -/obj/machinery/camera/update_remote_sight(mob/living/user) - if(isXRay()) - user.sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS) - user.see_in_dark = max(user.see_in_dark, 8) - user.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - else - user.sight = initial(user.sight) - user.see_in_dark = initial(user.see_in_dark) - user.lighting_alpha = initial(user.lighting_alpha) - - ..() - return TRUE - -/obj/machinery/camera/portable //Cameras which are placed inside of things, such as helmets. - var/turf/prev_turf - -/obj/machinery/camera/portable/New() - ..() - assembly.state = 0 //These cameras are portable, and so shall be in the portable state if removed. - assembly.anchored = 0 - assembly.update_icon() - -/obj/machinery/camera/portable/process() //Updates whenever the camera is moved. - if(cameranet && get_turf(src) != prev_turf) - cameranet.updatePortableCamera(src) - prev_turf = get_turf(src) +/obj/machinery/camera + name = "security camera" + desc = "It's used to monitor rooms." + icon = 'icons/obj/monitors.dmi' + icon_state = "camera" + use_power = ACTIVE_POWER_USE + idle_power_usage = 5 + active_power_usage = 10 + layer = WALL_OBJ_LAYER + resistance_flags = FIRE_PROOF + damage_deflection = 12 + armor = list("melee" = 50, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 50) + var/datum/wires/camera/wires = null // Wires datum + max_integrity = 100 + integrity_failure = 50 + var/list/network = list("SS13") + var/c_tag = null + var/c_tag_order = 999 + var/status = 1 + anchored = TRUE + var/start_active = FALSE //If it ignores the random chance to start broken on round start + var/invuln = null + var/obj/item/camera_bug/bug = null + var/obj/item/camera_assembly/assembly = null + + //OTHER + + var/view_range = 7 + var/short_range = 2 + + var/busy = FALSE + var/emped = FALSE //Number of consecutive EMP's on this camera + + var/in_use_lights = 0 // TO BE IMPLEMENTED + var/toggle_sound = 'sound/items/wirecutter.ogg' + + +/obj/machinery/camera/New() + ..() + wires = new(src) + assembly = new(src) + assembly.state = 4 + assembly.anchored = 1 + assembly.update_icon() + + cameranet.cameras += src + cameranet.addCamera(src) + +/obj/machinery/camera/Initialize() + ..() + if(is_station_level(z) && prob(3) && !start_active) + toggle_cam(null, FALSE) + wires.CutAll() + +/obj/machinery/camera/Destroy() + toggle_cam(null, FALSE) //kick anyone viewing out + QDEL_NULL(assembly) + if(istype(bug)) + bug.bugged_cameras -= c_tag + if(bug.current == src) + bug.current = null + bug = null + QDEL_NULL(wires) + cameranet.removeCamera(src) //Will handle removal from the camera network and the chunks, so we don't need to worry about that + cameranet.cameras -= src + var/area/ai_monitored/A = get_area(src) + if(istype(A)) + A.motioncamera = null + area_motion = null + return ..() + +/obj/machinery/camera/emp_act(severity) + if(!status) + return + if(!isEmpProof()) + if(prob(150/severity)) + update_icon() + var/list/previous_network = network + network = list() + cameranet.removeCamera(src) + stat |= EMPED + set_light(0) + emped = emped+1 //Increase the number of consecutive EMP's + update_icon() + var/thisemp = emped //Take note of which EMP this proc is for + spawn(900) + if(!QDELETED(src)) + triggerCameraAlarm() //camera alarm triggers even if multiple EMPs are in effect. + if(emped == thisemp) //Only fix it if the camera hasn't been EMP'd again + network = previous_network + stat &= ~EMPED + update_icon() + if(can_use()) + cameranet.addCamera(src) + emped = 0 //Resets the consecutive EMP count + spawn(100) + if(!QDELETED(src)) + cancelCameraAlarm() + for(var/mob/M in GLOB.player_list) + if(M.client && M.client.eye == src) + M.unset_machine() + M.reset_perspective(null) + to_chat(M, "The screen bursts into static.") + ..() + +/obj/machinery/camera/tesla_act(power)//EMP proof upgrade also makes it tesla immune + if(isEmpProof()) + return + ..() + qdel(src)//to prevent bomb testing camera from exploding over and over forever + +/obj/machinery/camera/ex_act(severity) + if(invuln) + return + ..() + +/obj/machinery/camera/proc/setViewRange(num = 7) + view_range = num + cameranet.updateVisibility(src, 0) + +/obj/machinery/camera/singularity_pull(S, current_size) + if (status && current_size >= STAGE_FIVE) // If the singulo is strong enough to pull anchored objects and the camera is still active, turn off the camera as it gets ripped off the wall. + toggle_cam(null, 0) + ..() + +/obj/machinery/camera/attackby(obj/item/I, mob/living/user, params) + var/msg = "You attach [I] into the assembly inner circuits." + var/msg2 = "The camera already has that upgrade!" + + if(istype(I, /obj/item/analyzer) && panel_open) //XRay + if(!user.drop_item()) + to_chat(user, "[I] is stuck to your hand!") + return + if(!isXRay()) + upgradeXRay() + qdel(I) + to_chat(user, "[msg]") + else + to_chat(user, "[msg2]") + + else if(istype(I, /obj/item/stack/sheet/mineral/plasma) && panel_open) + if(!user.drop_item()) + to_chat(user, "[I] is stuck to your hand!") + return + if(!isEmpProof()) + var/obj/item/stack/sheet/mineral/plasma/P = I + upgradeEmpProof() + to_chat(user, "[msg]") + P.use(1) + else + to_chat(user, "[msg2]") + else if(istype(I, /obj/item/assembly/prox_sensor) && panel_open) + if(!user.drop_item()) + to_chat(user, "[I] is stuck to your hand!") + return + if(!isMotion()) + upgradeMotion() + to_chat(user, "[msg]") + qdel(I) + else + to_chat(user, "[msg2]") + + // OTHER + else if((istype(I, /obj/item/paper) || istype(I, /obj/item/pda)) && isliving(user)) + var/mob/living/U = user + var/obj/item/paper/X = null + var/obj/item/pda/PDA = null + + var/itemname = "" + var/info = "" + if(istype(I, /obj/item/paper)) + X = I + itemname = X.name + info = X.info + else + PDA = I + var/datum/data/pda/app/notekeeper/N = PDA.find_program(/datum/data/pda/app/notekeeper) + if(N) + itemname = PDA.name + info = N.notehtml + to_chat(U, "You hold \the [itemname] up to the camera ...") + U.changeNext_move(CLICK_CD_MELEE) + for(var/mob/O in GLOB.player_list) + if(istype(O, /mob/living/silicon/ai)) + var/mob/living/silicon/ai/AI = O + if(AI.control_disabled || (AI.stat == DEAD)) + return + if(U.name == "Unknown") + to_chat(AI, "[U] holds \a [itemname] up to one of your cameras ...") + else + to_chat(AI, "[U] holds \a [itemname] up to one of your cameras ...") + AI.last_paper_seen = "[itemname][info]" + else if(O.client && O.client.eye == src) + to_chat(O, "[U] holds \a [itemname] up to one of the cameras ...") + O << browse(text("[][]", itemname, info), text("window=[]", itemname)) + + else if(istype(I, /obj/item/camera_bug)) + if(!can_use()) + to_chat(user, "Camera non-functional.") + return + if(istype(bug)) + to_chat(user, "Camera bug removed.") + bug.bugged_cameras -= c_tag + bug = null + else + to_chat(user, "Camera bugged.") + bug = I + bug.bugged_cameras[c_tag] = src + + else if(istype(I, /obj/item/laser_pointer)) + var/obj/item/laser_pointer/L = I + L.laser_act(src, user) + else + return ..() + + +/obj/machinery/camera/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + panel_open = !panel_open + to_chat(user, "You screw [src]'s panel [panel_open ? "open" : "closed"].") + +/obj/machinery/camera/wirecutter_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = 0)) + return + if(panel_open) + wires.Interact(user) + +/obj/machinery/camera/multitool_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = 0)) + return + if(panel_open) + wires.Interact(user) + +/obj/machinery/camera/welder_act(mob/user, obj/item/I) + if(!panel_open || !wires.CanDeconstruct()) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_WELD_MESSAGE + if(I.use_tool(src, user, 100, volume = I.tool_volume)) + visible_message("[user] unwelds [src], leaving it as just a frame bolted to the wall.", + "You unweld [src], leaving it as just a frame bolted to the wall") + deconstruct(TRUE) + +/obj/machinery/camera/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) + if(stat & BROKEN) + return damage_amount + . = ..() + +/obj/machinery/camera/obj_break(damage_flag) + if(status && !(flags & NODECONSTRUCT)) + triggerCameraAlarm() + toggle_cam(null, FALSE) + wires.CutAll() + +/obj/machinery/camera/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + if(disassembled) + if(!assembly) + assembly = new() + assembly.forceMove(drop_location()) + assembly.state = 1 + assembly.setDir(dir) + assembly.update_icon() + assembly = null + else + var/obj/item/I = new /obj/item/camera_assembly(loc) + I.obj_integrity = I.max_integrity * 0.5 + new /obj/item/stack/cable_coil(loc, 2) + qdel(src) + +/obj/machinery/camera/update_icon() + if(!status) + icon_state = "[initial(icon_state)]1" + else if(stat & EMPED) + icon_state = "[initial(icon_state)]emp" + else + icon_state = "[initial(icon_state)]" + +/obj/machinery/camera/proc/toggle_cam(mob/user, displaymessage = TRUE) + status = !status + if(can_use()) + cameranet.addCamera(src) + else + set_light(0) + cameranet.removeCamera(src) + cameranet.updateChunk(x, y, z) + var/change_msg = "deactivates" + if(status) + change_msg = "reactivates" + triggerCameraAlarm() + spawn(100) + if(!QDELETED(src)) + cancelCameraAlarm() + if(displaymessage) + if(user) + visible_message("[user] [change_msg] [src]!") + add_hiddenprint(user) + else + visible_message("\The [src] [change_msg]!") + + playsound(loc, toggle_sound, 100, 1) + update_icon() + + // now disconnect anyone using the camera + //Apparently, this will disconnect anyone even if the camera was re-activated. + //I guess that doesn't matter since they can't use it anyway? + for(var/mob/O in GLOB.player_list) + if(O.client && O.client.eye == src) + O.unset_machine() + O.reset_perspective(null) + to_chat(O, "The screen bursts into static.") + +/obj/machinery/camera/proc/triggerCameraAlarm() + if(is_station_contact(z)) + SSalarms.camera_alarm.triggerAlarm(loc, src) + +/obj/machinery/camera/proc/cancelCameraAlarm() + if(is_station_contact(z)) + SSalarms.camera_alarm.clearAlarm(loc, src) + +/obj/machinery/camera/proc/can_use() + if(!status) + return 0 + if(stat & EMPED) + return 0 + return 1 + +/obj/machinery/camera/proc/can_see() + var/list/see = null + var/turf/pos = get_turf(src) + if(isXRay()) + see = range(view_range, pos) + else + see = hear(view_range, pos) + return see + +/atom/proc/auto_turn() + //Automatically turns based on nearby walls. + var/turf/simulated/wall/T = null + for(var/i = 1, i <= 8; i += i) + T = get_ranged_target_turf(src, i, 1) + if(istype(T)) + //If someone knows a better way to do this, let me know. -Giacom + switch(i) + if(NORTH) + setDir(SOUTH) + if(SOUTH) + setDir(NORTH) + if(WEST) + setDir(EAST) + if(EAST) + setDir(WEST) + break + +//Return a working camera that can see a given mob +//or null if none +/proc/seen_by_camera(mob/M) + for(var/obj/machinery/camera/C in oview(4, M)) + if(C.can_use()) // check if camera disabled + return C + break + return null + +/proc/near_range_camera(mob/M) + for(var/obj/machinery/camera/C in range(4, M)) + if(C.can_use()) // check if camera disabled + return C + break + + return null + +/obj/machinery/camera/proc/Togglelight(on = FALSE) + for(var/mob/living/silicon/ai/A in ai_list) + for(var/obj/machinery/camera/cam in A.lit_cameras) + if(cam == src) + return + if(on) + set_light(AI_CAMERA_LUMINOSITY) + else + set_light(0) + +/obj/machinery/camera/proc/nano_structure() + var/cam[0] + var/turf/T = get_turf(src) + cam["name"] = sanitize(c_tag) + cam["deact"] = !can_use() + cam["camera"] = "\ref[src]" + if(T) + cam["x"] = T.x + cam["y"] = T.y + cam["z"] = T.z + else + cam["x"] = 0 + cam["y"] = 0 + cam["z"] = 0 + return cam + +/obj/machinery/camera/get_remote_view_fullscreens(mob/user) + if(view_range == short_range) //unfocused + user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 2) + +/obj/machinery/camera/update_remote_sight(mob/living/user) + if(isXRay()) + user.sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS) + user.see_in_dark = max(user.see_in_dark, 8) + user.lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + else + user.sight = initial(user.sight) + user.see_in_dark = initial(user.see_in_dark) + user.lighting_alpha = initial(user.lighting_alpha) + + ..() + return TRUE + +/obj/machinery/camera/portable //Cameras which are placed inside of things, such as helmets. + var/turf/prev_turf + +/obj/machinery/camera/portable/New() + ..() + assembly.state = 0 //These cameras are portable, and so shall be in the portable state if removed. + assembly.anchored = 0 + assembly.update_icon() + +/obj/machinery/camera/portable/process() //Updates whenever the camera is moved. + if(cameranet && get_turf(src) != prev_turf) + cameranet.updatePortableCamera(src) + prev_turf = get_turf(src) diff --git a/code/game/machinery/camera/camera_assembly.dm b/code/game/machinery/camera/camera_assembly.dm index 34915f2d01b..98f84cb75ec 100644 --- a/code/game/machinery/camera/camera_assembly.dm +++ b/code/game/machinery/camera/camera_assembly.dm @@ -1,186 +1,179 @@ -#define ASSEMBLY_UNBUILT 0 // Nothing done to it -#define ASSEMBLY_WRENCHED 1 // Wrenched in place -#define ASSEMBLY_WELDED 2 // Welded in place -#define ASSEMBLY_WIRED 3 // Wires attached (Upgradable now) -#define ASSEMBLY_BUILT 4 // Fully built (incl panel closed) - -/obj/item/camera_assembly - name = "camera assembly" - desc = "A pre-fabricated security camera kit, ready to be assembled and mounted to a surface." - icon = 'icons/obj/monitors.dmi' - icon_state = "cameracase" - w_class = WEIGHT_CLASS_SMALL - anchored = FALSE - materials = list(MAT_METAL=400, MAT_GLASS=250) - // Motion, EMP-Proof, X-Ray - var/list/obj/item/possible_upgrades = list(/obj/item/assembly/prox_sensor, /obj/item/stack/sheet/mineral/plasma, /obj/item/analyzer) - var/list/upgrades = list() - var/state = ASSEMBLY_UNBUILT - var/busy = FALSE - - -/obj/item/camera_assembly/Destroy() - QDEL_LIST(upgrades) - return ..() - -/obj/item/camera_assembly/attackby(obj/item/I, mob/living/user, params) - - switch(state) - if(ASSEMBLY_UNBUILT) - // State 0 - if(iswrench(I) && isturf(loc)) - playsound(loc, I.usesound, 50, 1) - to_chat(user, "You wrench the assembly into place.") - anchored = 1 - state = ASSEMBLY_WRENCHED - update_icon() - auto_turn() - return - - if(ASSEMBLY_WRENCHED) - // State 1 - if(iswelder(I)) - if(weld(I, user)) - to_chat(user, "You weld the assembly securely into place.") - anchored = TRUE - state = ASSEMBLY_WELDED - return - - else if(iswrench(I)) - playsound(loc, I.usesound, 50, 1) - to_chat(user, "You unattach the assembly from it's place.") - anchored = FALSE - update_icon() - state = ASSEMBLY_UNBUILT - return - - if(ASSEMBLY_WELDED) - if(iscoil(I)) - var/obj/item/stack/cable_coil/C = I - if(C.use(2)) - to_chat(user, "You add wires to the assembly.") - playsound(loc, I.usesound, 50, 1) - state = ASSEMBLY_WIRED - else - to_chat(user, "You need 2 coils of wire to wire the assembly.") - return - - else if(iswelder(I)) - if(weld(I, user)) - to_chat(user, "You unweld the assembly from it's place.") - state = ASSEMBLY_WRENCHED - anchored = TRUE - return - - - if(ASSEMBLY_WIRED) - if(isscrewdriver(I)) - playsound(loc, I.usesound, 50, 1) - - var/input = strip_html(input(usr, "Which networks would you like to connect this camera to? Seperate networks with a comma. No Spaces!\nFor example: SS13,Security,Secret ", "Set Network", "SS13")) - if(!input) - to_chat(usr, "No input found please hang up and try your call again.") - return - - var/list/tempnetwork = splittext(input, ",") - if(tempnetwork.len < 1) - to_chat(usr, "No network found please hang up and try your call again.") - return - - var/area/camera_area = get_area(src) - var/temptag = "[sanitize(camera_area.name)] ([rand(1, 999)])" - input = strip_html(input(usr, "How would you like to name the camera?", "Set Camera Name", temptag)) - - state = ASSEMBLY_BUILT - var/obj/machinery/camera/C = new(loc) - loc = C - C.assembly = src - - C.auto_turn() - - C.network = uniquelist(tempnetwork) - tempnetwork = difflist(C.network,GLOB.restricted_camera_networks) - if(!tempnetwork.len) // Camera isn't on any open network - remove its chunk from AI visibility. - cameranet.removeCamera(C) - - C.c_tag = input - - for(var/i = 5; i >= 0; i -= 1) - var/direct = input(user, "Direction?", "Assembling Camera", null) in list("LEAVE IT", "NORTH", "EAST", "SOUTH", "WEST" ) - if(direct != "LEAVE IT") - C.dir = text2dir(direct) - if(i != 0) - var/confirm = alert(user, "Is this what you want? Chances Remaining: [i]", "Confirmation", "Yes", "No") - if(confirm == "Yes") - break - return - - else if(iswirecutter(I)) - new/obj/item/stack/cable_coil(get_turf(src), 2) - playsound(loc, I.usesound, 50, 1) - to_chat(user, "You cut the wires from the circuits.") - state = ASSEMBLY_WELDED - return - - // Upgrades! - if(is_type_in_list(I, possible_upgrades) && !is_type_in_list(I, upgrades)) // Is a possible upgrade and isn't in the camera already. - if(!user.unEquip(I)) - to_chat(user, "[I] is stuck!") - return - to_chat(user, "You attach [I] into the assembly inner circuits.") - upgrades += I - user.drop_item() - I.loc = src - return - - // Taking out upgrades - else if(iscrowbar(I) && upgrades.len) - var/obj/U = locate(/obj) in upgrades - if(U) - to_chat(user, "You unattach an upgrade from the assembly.") - playsound(loc, I.usesound, 50, 1) - U.loc = get_turf(src) - upgrades -= U - return - - else - return ..() - -/obj/item/camera_assembly/update_icon() - if(anchored) - icon_state = "camera1" - else - icon_state = "cameracase" - -/obj/item/camera_assembly/attack_hand(mob/user as mob) - if(!anchored) - ..() - -/obj/item/camera_assembly/proc/weld(obj/item/weldingtool/WT, mob/living/user) - if(busy) - return FALSE - if(!WT.remove_fuel(0, user)) - return FALSE - - to_chat(user, "You start to weld the [src]...") - playsound(loc, WT.usesound, 50, 1) - busy = TRUE - if(do_after(user, 20 * WT.toolspeed, target = src)) - busy = FALSE - if(!WT.isOn()) - return FALSE - return TRUE - busy = FALSE - return FALSE - -/obj/item/camera_assembly/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - new /obj/item/stack/sheet/metal(loc) - qdel(src) - - -#undef ASSEMBLY_UNBUILT -#undef ASSEMBLY_WRENCHED -#undef ASSEMBLY_WELDED -#undef ASSEMBLY_WIRED -#undef ASSEMBLY_BUILT +#define ASSEMBLY_UNBUILT 0 // Nothing done to it +#define ASSEMBLY_WRENCHED 1 // Wrenched in place +#define ASSEMBLY_WELDED 2 // Welded in place +#define ASSEMBLY_WIRED 3 // Wires attached (Upgradable now) +#define ASSEMBLY_BUILT 4 // Fully built (incl panel closed) +#define HEY_IM_WORKING_HERE 666 //So nobody can mess with the camera while we're inputting settings + +/obj/item/camera_assembly + name = "camera assembly" + desc = "A pre-fabricated security camera kit, ready to be assembled and mounted to a surface." + icon = 'icons/obj/monitors.dmi' + icon_state = "cameracase" + w_class = WEIGHT_CLASS_SMALL + anchored = FALSE + materials = list(MAT_METAL=400, MAT_GLASS=250) + // Motion, EMP-Proof, X-Ray + var/list/obj/item/possible_upgrades = list(/obj/item/assembly/prox_sensor, /obj/item/stack/sheet/mineral/plasma, /obj/item/analyzer) + var/list/upgrades = list() + var/state = ASSEMBLY_UNBUILT + + +/obj/item/camera_assembly/Destroy() + QDEL_LIST(upgrades) + return ..() + +/obj/item/camera_assembly/attackby(obj/item/I, mob/living/user, params) + if(state == ASSEMBLY_WELDED && iscoil(I)) + var/obj/item/stack/cable_coil/C = I + if(C.use(2)) + to_chat(user, "You add wires to the assembly.") + playsound(loc, I.usesound, 50, 1) + state = ASSEMBLY_WIRED + else + to_chat(user, "You need 2 coils of wire to wire the assembly.") + return + + // Upgrades! + else if(is_type_in_list(I, possible_upgrades) && !is_type_in_list(I, upgrades)) // Is a possible upgrade and isn't in the camera already. + if(!user.unEquip(I)) + to_chat(user, "[I] is stuck!") + return + to_chat(user, "You attach [I] into the assembly inner circuits.") + upgrades += I + user.drop_item() + I.loc = src + return + else + return ..() + +/obj/item/camera_assembly/crowbar_act(mob/user, obj/item/I) + if(!upgrades.len) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + var/obj/U = locate(/obj) in upgrades + if(U) + to_chat(user, "You unattach an upgrade from the assembly.") + playsound(loc, I.usesound, 50, 1) + U.loc = get_turf(src) + upgrades -= U + +/obj/item/camera_assembly/screwdriver_act(mob/user, obj/item/I) + if(state != ASSEMBLY_WIRED) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + state = HEY_IM_WORKING_HERE + var/input = strip_html(input(usr, "Which networks would you like to connect this camera to? Seperate networks with a comma. No Spaces!\nFor example: SS13,Security,Secret ", "Set Network", "SS13")) + if(!input) + state = ASSEMBLY_WIRED + to_chat(usr, "No input found please hang up and try your call again.") + return + + var/list/tempnetwork = splittext(input, ",") + if(tempnetwork.len < 1) + state = ASSEMBLY_WIRED + to_chat(usr, "No network found please hang up and try your call again.") + return + + var/area/camera_area = get_area(src) + var/temptag = "[sanitize(camera_area.name)] ([rand(1, 999)])" + input = strip_html(input(usr, "How would you like to name the camera?", "Set Camera Name", temptag)) + state = ASSEMBLY_BUILT + var/obj/machinery/camera/C = new(loc) + loc = C + C.assembly = src + + C.auto_turn() + + C.network = uniquelist(tempnetwork) + tempnetwork = difflist(C.network,GLOB.restricted_camera_networks) + if(!tempnetwork.len) // Camera isn't on any open network - remove its chunk from AI visibility. + cameranet.removeCamera(C) + + C.c_tag = input + + for(var/i = 5; i >= 0; i -= 1) + var/direct = input(user, "Direction?", "Assembling Camera", null) in list("LEAVE IT", "NORTH", "EAST", "SOUTH", "WEST" ) + if(direct != "LEAVE IT") + C.dir = text2dir(direct) + if(i != 0) + var/confirm = alert(user, "Is this what you want? Chances Remaining: [i]", "Confirmation", "Yes", "No") + if(confirm == "Yes") + break + + +/obj/item/camera_assembly/wirecutter_act(mob/user, obj/item/I) + if(state != ASSEMBLY_WIRED) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + new/obj/item/stack/cable_coil(get_turf(src), 2) + WIRECUTTER_SNIP_MESSAGE + state = ASSEMBLY_WELDED + return + +/obj/item/camera_assembly/wrench_act(mob/user, obj/item/I) + if(state != ASSEMBLY_UNBUILT && state != ASSEMBLY_WRENCHED) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(state == ASSEMBLY_UNBUILT && isturf(loc)) + WRENCH_ANCHOR_TO_WALL_MESSAGE + anchored = TRUE + state = ASSEMBLY_WRENCHED + update_icon() + auto_turn() + else if(state == ASSEMBLY_WRENCHED) + WRENCH_UNANCHOR_WALL_MESSAGE + anchored = FALSE + update_icon() + state = ASSEMBLY_UNBUILT + else + to_chat(user, "[src] can't fit here!") + +/obj/item/camera_assembly/welder_act(mob/user, obj/item/I) + if(state == ASSEMBLY_UNBUILT) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_WELD_MESSAGE + if(state == ASSEMBLY_WRENCHED) + if(!I.use_tool(src, user, 50, volume = I.tool_volume)) + return + to_chat(user, "You weld [src] into place.") + state = ASSEMBLY_WELDED + else if(state == ASSEMBLY_WELDED) + if(!I.use_tool(src, user, 50, volume = I.tool_volume)) + return + to_chat(user, "You unweld [src] from its place.") + state = ASSEMBLY_WRENCHED + +/obj/item/camera_assembly/update_icon() + if(anchored) + icon_state = "camera1" + else + icon_state = "cameracase" + +/obj/item/camera_assembly/attack_hand(mob/user as mob) + if(!anchored) + ..() + +/obj/item/camera_assembly/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + new /obj/item/stack/sheet/metal(loc) + qdel(src) + + +#undef ASSEMBLY_UNBUILT +#undef ASSEMBLY_WRENCHED +#undef ASSEMBLY_WELDED +#undef ASSEMBLY_WIRED +#undef ASSEMBLY_BUILT +#undef HEY_IM_WORKING_HERE diff --git a/code/game/machinery/camera/motion.dm b/code/game/machinery/camera/motion.dm index 312e4cf577e..16fb6eca204 100644 --- a/code/game/machinery/camera/motion.dm +++ b/code/game/machinery/camera/motion.dm @@ -1,66 +1,66 @@ -/obj/machinery/camera - - var/list/motionTargets = list() - var/detectTime = 0 - var/area/ai_monitored/area_motion = null - var/alarm_delay = 100 - - -/obj/machinery/camera/process() - // motion camera event loop - if(stat & (EMPED|NOPOWER)) - return - if(!isMotion()) - . = PROCESS_KILL - return - if(detectTime > 0) - var/elapsed = world.time - detectTime - if(elapsed > alarm_delay) - triggerAlarm() - else if(detectTime == -1) - for(var/mob/target in motionTargets) - if(target.stat == 2) lostTarget(target) - // If not detecting with motion camera... - if(!area_motion) - // See if the camera is still in range - if(!in_range(src, target)) - // If they aren't in range, lose the target. - lostTarget(target) - -/obj/machinery/camera/proc/newTarget(var/mob/target) - if(istype(target, /mob/living/silicon/ai)) return 0 - if(detectTime == 0) - detectTime = world.time // start the clock - if(!(target in motionTargets)) - motionTargets += target - return 1 - -/obj/machinery/camera/proc/lostTarget(var/mob/target) - if(target in motionTargets) - motionTargets -= target - if(motionTargets.len == 0) - cancelAlarm() - -/obj/machinery/camera/proc/cancelAlarm() - if(!status || (stat & NOPOWER)) - return FALSE - if(detectTime == -1 && is_station_contact(z)) - SSalarms.motion_alarm.clearAlarm(loc, src) - detectTime = 0 - return TRUE - -/obj/machinery/camera/proc/triggerAlarm() - if(!status || (stat & NOPOWER)) - return FALSE - if(!detectTime || !is_station_contact(z)) - return FALSE - SSalarms.motion_alarm.triggerAlarm(loc, src) - detectTime = -1 - return TRUE - -/obj/machinery/camera/HasProximity(atom/movable/AM as mob|obj) - // Motion cameras outside of an "ai monitored" area will use this to detect stuff. - if(!area_motion) - if(isliving(AM)) - newTarget(AM) - +/obj/machinery/camera + + var/list/motionTargets = list() + var/detectTime = 0 + var/area/ai_monitored/area_motion = null + var/alarm_delay = 100 + + +/obj/machinery/camera/process() + // motion camera event loop + if(stat & (EMPED|NOPOWER)) + return + if(!isMotion()) + . = PROCESS_KILL + return + if(detectTime > 0) + var/elapsed = world.time - detectTime + if(elapsed > alarm_delay) + triggerAlarm() + else if(detectTime == -1) + for(var/mob/target in motionTargets) + if(target.stat == 2) lostTarget(target) + // If not detecting with motion camera... + if(!area_motion) + // See if the camera is still in range + if(!in_range(src, target)) + // If they aren't in range, lose the target. + lostTarget(target) + +/obj/machinery/camera/proc/newTarget(var/mob/target) + if(istype(target, /mob/living/silicon/ai)) return 0 + if(detectTime == 0) + detectTime = world.time // start the clock + if(!(target in motionTargets)) + motionTargets += target + return 1 + +/obj/machinery/camera/proc/lostTarget(var/mob/target) + if(target in motionTargets) + motionTargets -= target + if(motionTargets.len == 0) + cancelAlarm() + +/obj/machinery/camera/proc/cancelAlarm() + if(!status || (stat & NOPOWER)) + return FALSE + if(detectTime == -1 && is_station_contact(z)) + SSalarms.motion_alarm.clearAlarm(loc, src) + detectTime = 0 + return TRUE + +/obj/machinery/camera/proc/triggerAlarm() + if(!status || (stat & NOPOWER)) + return FALSE + if(!detectTime || !is_station_contact(z)) + return FALSE + SSalarms.motion_alarm.triggerAlarm(loc, src) + detectTime = -1 + return TRUE + +/obj/machinery/camera/HasProximity(atom/movable/AM as mob|obj) + // Motion cameras outside of an "ai monitored" area will use this to detect stuff. + if(!area_motion) + if(isliving(AM)) + newTarget(AM) + diff --git a/code/game/machinery/camera/presets.dm b/code/game/machinery/camera/presets.dm index ac70a7c11ba..85b83b88c06 100644 --- a/code/game/machinery/camera/presets.dm +++ b/code/game/machinery/camera/presets.dm @@ -1,92 +1,92 @@ -// PRESETS - -// EMP - -/obj/machinery/camera/emp_proof/New() - ..() - upgradeEmpProof() - -// X-RAY - -/obj/machinery/camera/xray - icon_state = "xraycam" // Thanks to Krutchen for the icons. - -/obj/machinery/camera/xray/New() - ..() - upgradeXRay() - -// MOTION - -/obj/machinery/camera/motion/New() - ..() - upgradeMotion() - -// ALL UPGRADES - -/obj/machinery/camera/all/New() - ..() - upgradeEmpProof() - upgradeXRay() - upgradeMotion() - -// AUTONAME - -/obj/machinery/camera/autoname - var/number = 0 //camera number in area - -//This camera type automatically sets it's name to whatever the area that it's in is called. -/obj/machinery/camera/autoname/New() - ..() - spawn(10) - number = 1 - var/area/A = get_area(src) - if(A) - for(var/obj/machinery/camera/autoname/C in world) - if(C == src) continue - var/area/CA = get_area(C) - if(CA.type == A.type) - if(C.number) - number = max(number, C.number+1) - c_tag = "[A.name] #[number]" - - -// CHECKS - -/obj/machinery/camera/proc/isEmpProof() - var/O = locate(/obj/item/stack/sheet/mineral/plasma) in assembly.upgrades - return O - -/obj/machinery/camera/proc/isXRay() - var/O = locate(/obj/item/analyzer) in assembly.upgrades - return O - -/obj/machinery/camera/proc/isMotion() - var/O = locate(/obj/item/assembly/prox_sensor) in assembly.upgrades - return O - -// UPGRADE PROCS - -/obj/machinery/camera/proc/upgradeEmpProof() - assembly.upgrades.Add(new /obj/item/stack/sheet/mineral/plasma(assembly)) - setPowerUsage() - -/obj/machinery/camera/proc/upgradeXRay() - assembly.upgrades.Add(new /obj/item/analyzer(assembly)) - setPowerUsage() - //Update what it can see. - cameranet.updateVisibility(src, 0) - -// If you are upgrading Motion, and it isn't in the camera's New(), add it to the machines list. -/obj/machinery/camera/proc/upgradeMotion() - assembly.upgrades.Add(new /obj/item/assembly/prox_sensor(assembly)) - setPowerUsage() - // Add it to machines that process - START_PROCESSING(SSmachines, src) - -/obj/machinery/camera/proc/setPowerUsage() - var/mult = 1 - if(isXRay()) - mult++ - if(isMotion()) - mult++ - active_power_usage = mult*initial(active_power_usage) +// PRESETS + +// EMP + +/obj/machinery/camera/emp_proof/New() + ..() + upgradeEmpProof() + +// X-RAY + +/obj/machinery/camera/xray + icon_state = "xraycam" // Thanks to Krutchen for the icons. + +/obj/machinery/camera/xray/New() + ..() + upgradeXRay() + +// MOTION + +/obj/machinery/camera/motion/New() + ..() + upgradeMotion() + +// ALL UPGRADES + +/obj/machinery/camera/all/New() + ..() + upgradeEmpProof() + upgradeXRay() + upgradeMotion() + +// AUTONAME + +/obj/machinery/camera/autoname + var/number = 0 //camera number in area + +//This camera type automatically sets it's name to whatever the area that it's in is called. +/obj/machinery/camera/autoname/New() + ..() + spawn(10) + number = 1 + var/area/A = get_area(src) + if(A) + for(var/obj/machinery/camera/autoname/C in world) + if(C == src) continue + var/area/CA = get_area(C) + if(CA.type == A.type) + if(C.number) + number = max(number, C.number+1) + c_tag = "[A.name] #[number]" + + +// CHECKS + +/obj/machinery/camera/proc/isEmpProof() + var/O = locate(/obj/item/stack/sheet/mineral/plasma) in assembly.upgrades + return O + +/obj/machinery/camera/proc/isXRay() + var/O = locate(/obj/item/analyzer) in assembly.upgrades + return O + +/obj/machinery/camera/proc/isMotion() + var/O = locate(/obj/item/assembly/prox_sensor) in assembly.upgrades + return O + +// UPGRADE PROCS + +/obj/machinery/camera/proc/upgradeEmpProof() + assembly.upgrades.Add(new /obj/item/stack/sheet/mineral/plasma(assembly)) + setPowerUsage() + +/obj/machinery/camera/proc/upgradeXRay() + assembly.upgrades.Add(new /obj/item/analyzer(assembly)) + setPowerUsage() + //Update what it can see. + cameranet.updateVisibility(src, 0) + +// If you are upgrading Motion, and it isn't in the camera's New(), add it to the machines list. +/obj/machinery/camera/proc/upgradeMotion() + assembly.upgrades.Add(new /obj/item/assembly/prox_sensor(assembly)) + setPowerUsage() + // Add it to machines that process + START_PROCESSING(SSmachines, src) + +/obj/machinery/camera/proc/setPowerUsage() + var/mult = 1 + if(isXRay()) + mult++ + if(isMotion()) + mult++ + active_power_usage = mult*initial(active_power_usage) diff --git a/code/game/machinery/camera/tracking.dm b/code/game/machinery/camera/tracking.dm index 277cee5beb3..5e6bd1640fb 100644 --- a/code/game/machinery/camera/tracking.dm +++ b/code/game/machinery/camera/tracking.dm @@ -1,260 +1,260 @@ -/mob/living/silicon/ai/proc/InvalidTurf(turf/T as turf) - if(!T) - return 1 - if(!is_level_reachable(T.z)) - return 1 - return 0 - - -/mob/living/silicon/ai/var/max_locations = 10 -/mob/living/silicon/ai/var/stored_locations[0] - -/mob/living/silicon/ai/proc/get_camera_list() - - track.cameras.Cut() - - if(src.stat == 2) - return - - var/list/L = list() - for(var/obj/machinery/camera/C in cameranet.cameras) - L.Add(C) - - camera_sort(L) - - var/list/T = list() - - for(var/obj/machinery/camera/C in L) - var/list/tempnetwork = C.network & src.network - if(tempnetwork.len) - T[text("[][]", C.c_tag, (C.can_use() ? null : " (Deactivated)"))] = C - - track.cameras = T - return T - - -/mob/living/silicon/ai/proc/ai_camera_list(var/camera in get_camera_list()) - set category = "AI Commands" - set name = "Show Camera List" - - if(src.stat == 2) - to_chat(src, "You can't list the cameras because you are dead!") - return - - if(!camera || camera == "Cancel") - return 0 - - var/obj/machinery/camera/C = track.cameras[camera] - src.eyeobj.setLoc(C) - - return - -/mob/living/silicon/ai/proc/ai_store_location(loc as text) - set category = "AI Commands" - set name = "Store Camera Location" - set desc = "Stores your current camera location by the given name" - - loc = sanitize(copytext(loc, 1, MAX_MESSAGE_LEN)) - if(!loc) - to_chat(src, "Must supply a location name") - return - - if(stored_locations.len >= max_locations) - to_chat(src, "Cannot store additional locations. Remove one first") - return - - if(loc in stored_locations) - to_chat(src, "There is already a stored location by this name") - return - - var/L = get_turf(eyeobj) - if(InvalidTurf(get_turf(L))) - to_chat(src, "Unable to store this location") - return - - stored_locations[loc] = L - to_chat(src, "Location '[loc]' stored") - -/mob/living/silicon/ai/proc/sorted_stored_locations() - return sortList(stored_locations) - -/mob/living/silicon/ai/proc/ai_goto_location(loc in sorted_stored_locations()) - set category = "AI Commands" - set name = "Goto Camera Location" - set desc = "Returns to the selected camera location" - - if(!(loc in stored_locations)) - to_chat(src, "Location [loc] not found") - return - - var/L = stored_locations[loc] - src.eyeobj.setLoc(L) - -/mob/living/silicon/ai/proc/ai_remove_location(loc in sorted_stored_locations()) - set category = "AI Commands" - set name = "Delete Camera Location" - set desc = "Deletes the selected camera location" - - if(!(loc in stored_locations)) - to_chat(src, "Location [loc] not found") - return - - stored_locations.Remove(loc) - to_chat(src, "Location [loc] removed") - -// Used to allow the AI is write in mob names/camera name from the CMD line. -/datum/trackable - var/list/names = list() - var/list/namecounts = list() - var/list/humans = list() - var/list/others = list() - var/list/cameras = list() - -/mob/living/silicon/ai/proc/trackable_mobs() - - track.names.Cut() - track.namecounts.Cut() - track.humans.Cut() - track.others.Cut() - - if(usr.stat == 2) - return list() - - for(var/mob/living/M in GLOB.mob_list) - if(!M.can_track(usr)) - continue - - // Human check - var/human = 0 - if(istype(M, /mob/living/carbon/human)) - human = 1 - - var/name = M.name - if(name in track.names) - track.namecounts[name]++ - name = text("[] ([])", name, track.namecounts[name]) - else - track.names.Add(name) - track.namecounts[name] = 1 - if(human) - track.humans[name] = M - else - track.others[name] = M - - var/list/targets = sortList(track.humans) + sortList(track.others) - - return targets - -/mob/living/silicon/ai/proc/ai_camera_track(target_name in trackable_mobs()) - set category = "AI Commands" - set name = "Track With Camera" - set desc = "Select who you would like to track." - - if(src.stat == DEAD) - to_chat(src, "You can't track with camera because you are dead!") - return - if(!target_name) - return - - var/mob/target = (isnull(track.humans[target_name]) ? track.others[target_name] : track.humans[target_name]) - - ai_actual_track(target) - -/mob/living/silicon/ai/proc/ai_cancel_tracking(var/forced = 0) - if(!cameraFollow) - return - - to_chat(src, "Follow camera mode [forced ? "terminated" : "ended"].") - cameraFollow = null - -/mob/living/silicon/ai/proc/ai_actual_track(mob/living/target) - if(!istype(target)) - return - var/mob/living/silicon/ai/U = usr - - U.cameraFollow = target - U.tracking = 1 - - to_chat(U, "Attempting to track [target.get_visible_name()]...") - sleep(min(30, get_dist(target, U.eyeobj) / 4)) - spawn(15) //give the AI a grace period to stop moving. - U.tracking = 0 - - if(!target || !target.can_track(usr)) - to_chat(U, "Target is not near any active cameras.") - U.cameraFollow = null - return - - to_chat(U, "Now tracking [target.get_visible_name()] on camera.") - - var/cameraticks = 0 - spawn(0) - while(U.cameraFollow == target) - if(U.cameraFollow == null) - return - - if(!target.can_track(usr)) - U.tracking = 1 - if(!cameraticks) - to_chat(U, "Target is not near any active cameras. Attempting to reacquire...") - cameraticks++ - if(cameraticks > 9) - U.cameraFollow = null - to_chat(U, "Unable to reacquire, cancelling track...") - U.tracking = 0 - return - else - sleep(10) - continue - - else - cameraticks = 0 - U.tracking = 0 - - if(U.eyeobj) - U.eyeobj.setLoc(get_turf(target)) - - else - view_core() - U.cameraFollow = null - return - - sleep(10) - -/proc/near_camera(mob/living/M) - if(!isturf(M.loc)) - return 0 - if(isrobot(M)) - var/mob/living/silicon/robot/R = M - if(!(R.camera && R.camera.can_use()) && !cameranet.checkCameraVis(M)) - return 0 - else if(!cameranet.checkCameraVis(M)) - return 0 - return 1 - -/obj/machinery/camera/attack_ai(mob/living/silicon/ai/user) - if(!istype(user)) - return - if(!src.can_use()) - return - user.eyeobj.setLoc(get_turf(src)) - - -/mob/living/silicon/ai/attack_ai(mob/user) - ai_camera_list() - -/proc/camera_sort(list/L) - var/obj/machinery/camera/a - var/obj/machinery/camera/b - - for(var/i = L.len, i > 0, i--) - for(var/j = 1 to i - 1) - a = L[j] - b = L[j + 1] - if(a.c_tag_order != b.c_tag_order) - if(a.c_tag_order > b.c_tag_order) - L.Swap(j, j + 1) - else - if(sorttext(a.c_tag, b.c_tag) < 0) - L.Swap(j, j + 1) - return L +/mob/living/silicon/ai/proc/InvalidTurf(turf/T as turf) + if(!T) + return 1 + if(!is_level_reachable(T.z)) + return 1 + return 0 + + +/mob/living/silicon/ai/var/max_locations = 10 +/mob/living/silicon/ai/var/stored_locations[0] + +/mob/living/silicon/ai/proc/get_camera_list() + + track.cameras.Cut() + + if(src.stat == 2) + return + + var/list/L = list() + for(var/obj/machinery/camera/C in cameranet.cameras) + L.Add(C) + + camera_sort(L) + + var/list/T = list() + + for(var/obj/machinery/camera/C in L) + var/list/tempnetwork = C.network & src.network + if(tempnetwork.len) + T[text("[][]", C.c_tag, (C.can_use() ? null : " (Deactivated)"))] = C + + track.cameras = T + return T + + +/mob/living/silicon/ai/proc/ai_camera_list(var/camera in get_camera_list()) + set category = "AI Commands" + set name = "Show Camera List" + + if(src.stat == 2) + to_chat(src, "You can't list the cameras because you are dead!") + return + + if(!camera || camera == "Cancel") + return 0 + + var/obj/machinery/camera/C = track.cameras[camera] + src.eyeobj.setLoc(C) + + return + +/mob/living/silicon/ai/proc/ai_store_location(loc as text) + set category = "AI Commands" + set name = "Store Camera Location" + set desc = "Stores your current camera location by the given name" + + loc = sanitize(copytext(loc, 1, MAX_MESSAGE_LEN)) + if(!loc) + to_chat(src, "Must supply a location name") + return + + if(stored_locations.len >= max_locations) + to_chat(src, "Cannot store additional locations. Remove one first") + return + + if(loc in stored_locations) + to_chat(src, "There is already a stored location by this name") + return + + var/L = get_turf(eyeobj) + if(InvalidTurf(get_turf(L))) + to_chat(src, "Unable to store this location") + return + + stored_locations[loc] = L + to_chat(src, "Location '[loc]' stored") + +/mob/living/silicon/ai/proc/sorted_stored_locations() + return sortList(stored_locations) + +/mob/living/silicon/ai/proc/ai_goto_location(loc in sorted_stored_locations()) + set category = "AI Commands" + set name = "Goto Camera Location" + set desc = "Returns to the selected camera location" + + if(!(loc in stored_locations)) + to_chat(src, "Location [loc] not found") + return + + var/L = stored_locations[loc] + src.eyeobj.setLoc(L) + +/mob/living/silicon/ai/proc/ai_remove_location(loc in sorted_stored_locations()) + set category = "AI Commands" + set name = "Delete Camera Location" + set desc = "Deletes the selected camera location" + + if(!(loc in stored_locations)) + to_chat(src, "Location [loc] not found") + return + + stored_locations.Remove(loc) + to_chat(src, "Location [loc] removed") + +// Used to allow the AI is write in mob names/camera name from the CMD line. +/datum/trackable + var/list/names = list() + var/list/namecounts = list() + var/list/humans = list() + var/list/others = list() + var/list/cameras = list() + +/mob/living/silicon/ai/proc/trackable_mobs() + + track.names.Cut() + track.namecounts.Cut() + track.humans.Cut() + track.others.Cut() + + if(usr.stat == 2) + return list() + + for(var/mob/living/M in GLOB.mob_list) + if(!M.can_track(usr)) + continue + + // Human check + var/human = 0 + if(istype(M, /mob/living/carbon/human)) + human = 1 + + var/name = M.name + if(name in track.names) + track.namecounts[name]++ + name = text("[] ([])", name, track.namecounts[name]) + else + track.names.Add(name) + track.namecounts[name] = 1 + if(human) + track.humans[name] = M + else + track.others[name] = M + + var/list/targets = sortList(track.humans) + sortList(track.others) + + return targets + +/mob/living/silicon/ai/proc/ai_camera_track(target_name in trackable_mobs()) + set category = "AI Commands" + set name = "Track With Camera" + set desc = "Select who you would like to track." + + if(src.stat == DEAD) + to_chat(src, "You can't track with camera because you are dead!") + return + if(!target_name) + return + + var/mob/target = (isnull(track.humans[target_name]) ? track.others[target_name] : track.humans[target_name]) + + ai_actual_track(target) + +/mob/living/silicon/ai/proc/ai_cancel_tracking(var/forced = 0) + if(!cameraFollow) + return + + to_chat(src, "Follow camera mode [forced ? "terminated" : "ended"].") + cameraFollow = null + +/mob/living/silicon/ai/proc/ai_actual_track(mob/living/target) + if(!istype(target)) + return + var/mob/living/silicon/ai/U = usr + + U.cameraFollow = target + U.tracking = 1 + + to_chat(U, "Attempting to track [target.get_visible_name()]...") + sleep(min(30, get_dist(target, U.eyeobj) / 4)) + spawn(15) //give the AI a grace period to stop moving. + U.tracking = 0 + + if(!target || !target.can_track(usr)) + to_chat(U, "Target is not near any active cameras.") + U.cameraFollow = null + return + + to_chat(U, "Now tracking [target.get_visible_name()] on camera.") + + var/cameraticks = 0 + spawn(0) + while(U.cameraFollow == target) + if(U.cameraFollow == null) + return + + if(!target.can_track(usr)) + U.tracking = 1 + if(!cameraticks) + to_chat(U, "Target is not near any active cameras. Attempting to reacquire...") + cameraticks++ + if(cameraticks > 9) + U.cameraFollow = null + to_chat(U, "Unable to reacquire, cancelling track...") + U.tracking = 0 + return + else + sleep(10) + continue + + else + cameraticks = 0 + U.tracking = 0 + + if(U.eyeobj) + U.eyeobj.setLoc(get_turf(target)) + + else + view_core() + U.cameraFollow = null + return + + sleep(10) + +/proc/near_camera(mob/living/M) + if(!isturf(M.loc)) + return 0 + if(isrobot(M)) + var/mob/living/silicon/robot/R = M + if(!(R.camera && R.camera.can_use()) && !cameranet.checkCameraVis(M)) + return 0 + else if(!cameranet.checkCameraVis(M)) + return 0 + return 1 + +/obj/machinery/camera/attack_ai(mob/living/silicon/ai/user) + if(!istype(user)) + return + if(!src.can_use()) + return + user.eyeobj.setLoc(get_turf(src)) + + +/mob/living/silicon/ai/attack_ai(mob/user) + ai_camera_list() + +/proc/camera_sort(list/L) + var/obj/machinery/camera/a + var/obj/machinery/camera/b + + for(var/i = L.len, i > 0, i--) + for(var/j = 1 to i - 1) + a = L[j] + b = L[j + 1] + if(a.c_tag_order != b.c_tag_order) + if(a.c_tag_order > b.c_tag_order) + L.Swap(j, j + 1) + else + if(sorttext(a.c_tag, b.c_tag) < 0) + L.Swap(j, j + 1) + return L diff --git a/code/game/machinery/cell_charger.dm b/code/game/machinery/cell_charger.dm index e6c957c1779..675d18ebb32 100644 --- a/code/game/machinery/cell_charger.dm +++ b/code/game/machinery/cell_charger.dm @@ -1,132 +1,137 @@ -/obj/machinery/cell_charger - name = "cell charger" - desc = "It charges power cells." - icon = 'icons/obj/power.dmi' - icon_state = "ccharger0" - anchored = 1 - use_power = IDLE_POWER_USE - idle_power_usage = 5 - active_power_usage = 60 - power_channel = EQUIP - pass_flags = PASSTABLE - var/obj/item/stock_parts/cell/charging = null - var/chargelevel = -1 - -/obj/machinery/cell_charger/deconstruct() - if(charging) - charging.forceMove(drop_location()) - return ..() - -/obj/machinery/cell_charger/Destroy() - QDEL_NULL(charging) - return ..() - -/obj/machinery/cell_charger/proc/updateicon() - icon_state = "ccharger[charging ? 1 : 0]" - - if(charging && !(stat & (BROKEN|NOPOWER))) - var/newlevel = round(charging.percent() * 4 / 100) - - if(chargelevel != newlevel) - chargelevel = newlevel - - overlays.Cut() - overlays += "ccharger-o[newlevel]" - - else - overlays.Cut() - -/obj/machinery/cell_charger/examine(mob/user) - . = ..() - . += "There's [charging ? "a" : "no"] cell in the charger." - if(charging) - . += "Current charge: [round(charging.percent(), 1)]%" - -/obj/machinery/cell_charger/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/stock_parts/cell)) - if(stat & BROKEN) - to_chat(user, "[src] is broken!") - return - if(!anchored) - to_chat(user, "[src] isn't attached to the ground!") - return - if(charging) - to_chat(user, "There is already a cell in the charger!") - return - else - var/area/a = loc.loc // Gets our locations location, like a dream within a dream - if(!isarea(a)) - return - if(a.power_equip == 0) // There's no APC in this area, don't try to cheat power! - to_chat(user, "[src] blinks red as you try to insert the cell!") - return - if(!user.drop_item()) - return - - I.forceMove(src) - charging = I - user.visible_message("[user] inserts a cell into the charger.", "You insert a cell into the charger.") - chargelevel = -1 - updateicon() - else if(iswrench(I)) - if(charging) - to_chat(user, "Remove the cell first!") - return - - anchored = !anchored - to_chat(user, "You [anchored ? "attach" : "detach"] the cell charger [anchored ? "to" : "from"] the ground") - playsound(src.loc, I.usesound, 75, 1) - else - return ..() - - -/obj/machinery/cell_charger/proc/removecell() - charging.update_icon() - charging = null - chargelevel = -1 - updateicon() - -/obj/machinery/cell_charger/attack_hand(mob/user) - if(!charging) - return - - user.put_in_hands(charging) - charging.add_fingerprint(user) - - user.visible_message("[user] removes [charging] from [src].", "You remove [charging] from [src].") - - removecell() - -/obj/machinery/cell_charger/attack_tk(mob/user) - if(!charging) - return - - charging.forceMove(loc) - to_chat(user, "You telekinetically remove [charging] from [src].") - - removecell() - -/obj/machinery/cell_charger/attack_ai(mob/user) - return - -/obj/machinery/cell_charger/emp_act(severity) - if(stat & (BROKEN|NOPOWER)) - return - - if(charging) - charging.emp_act(severity) - - ..(severity) - - -/obj/machinery/cell_charger/process() - if(!charging || !anchored || (stat & (BROKEN|NOPOWER))) - return - - if(charging.percent() >= 100) - return - - use_power(200) //this used to use CELLRATE, but CELLRATE is fucking awful. feel free to fix this properly! - charging.give(175) //inefficiency. - - updateicon() +/obj/machinery/cell_charger + name = "cell charger" + desc = "It charges power cells." + icon = 'icons/obj/power.dmi' + icon_state = "ccharger0" + anchored = 1 + use_power = IDLE_POWER_USE + idle_power_usage = 5 + active_power_usage = 60 + power_channel = EQUIP + pass_flags = PASSTABLE + var/obj/item/stock_parts/cell/charging = null + var/chargelevel = -1 + +/obj/machinery/cell_charger/deconstruct() + if(charging) + charging.forceMove(drop_location()) + return ..() + +/obj/machinery/cell_charger/Destroy() + QDEL_NULL(charging) + return ..() + +/obj/machinery/cell_charger/proc/updateicon() + icon_state = "ccharger[charging ? 1 : 0]" + + if(charging && !(stat & (BROKEN|NOPOWER))) + var/newlevel = round(charging.percent() * 4 / 100) + + if(chargelevel != newlevel) + chargelevel = newlevel + + overlays.Cut() + overlays += "ccharger-o[newlevel]" + + else + overlays.Cut() + +/obj/machinery/cell_charger/examine(mob/user) + . = ..() + . += "There's [charging ? "a" : "no"] cell in the charger." + if(charging) + . += "Current charge: [round(charging.percent(), 1)]%" + +/obj/machinery/cell_charger/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/stock_parts/cell)) + if(stat & BROKEN) + to_chat(user, "[src] is broken!") + return + if(!anchored) + to_chat(user, "[src] isn't attached to the ground!") + return + if(charging) + to_chat(user, "There is already a cell in the charger!") + return + else + var/area/a = loc.loc // Gets our locations location, like a dream within a dream + if(!isarea(a)) + return + if(a.power_equip == 0) // There's no APC in this area, don't try to cheat power! + to_chat(user, "[src] blinks red as you try to insert the cell!") + return + if(!user.drop_item()) + return + + I.forceMove(src) + charging = I + user.visible_message("[user] inserts a cell into the charger.", "You insert a cell into the charger.") + chargelevel = -1 + updateicon() + else + return ..() + +/obj/machinery/cell_charger/wrench_act(mob/user, obj/item/I) + . = TRUE + if(charging) + to_chat(user, "Remove the cell first!") + return + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + anchored = !anchored + if(anchored) + WRENCH_ANCHOR_MESSAGE + else + WRENCH_UNANCHOR_MESSAGE + + +/obj/machinery/cell_charger/proc/removecell() + charging.update_icon() + charging = null + chargelevel = -1 + updateicon() + +/obj/machinery/cell_charger/attack_hand(mob/user) + if(!charging) + return + + user.put_in_hands(charging) + charging.add_fingerprint(user) + + user.visible_message("[user] removes [charging] from [src].", "You remove [charging] from [src].") + + removecell() + +/obj/machinery/cell_charger/attack_tk(mob/user) + if(!charging) + return + + charging.forceMove(loc) + to_chat(user, "You telekinetically remove [charging] from [src].") + + removecell() + +/obj/machinery/cell_charger/attack_ai(mob/user) + return + +/obj/machinery/cell_charger/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + return + + if(charging) + charging.emp_act(severity) + + ..(severity) + + +/obj/machinery/cell_charger/process() + if(!charging || !anchored || (stat & (BROKEN|NOPOWER))) + return + + if(charging.percent() >= 100) + return + + use_power(200) //this used to use CELLRATE, but CELLRATE is fucking awful. feel free to fix this properly! + charging.give(175) //inefficiency. + + updateicon() diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index 83a12a6ff81..721162c0794 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -1,650 +1,658 @@ -//Cloning revival method. -//The pod handles the actual cloning while the computer manages the clone profiles - -//Potential replacement for genetics revives or something I dunno (?) - -#define CLONE_BIOMASS 150 -#define BIOMASS_MEAT_AMOUNT 50 -#define MINIMUM_HEAL_LEVEL 40 -#define CLONE_INITIAL_DAMAGE 190 -#define BRAIN_INITIAL_DAMAGE 90 // our minds are too feeble for 190 - -/obj/machinery/clonepod - anchored = 1 - name = "cloning pod" - desc = "An electronically-lockable pod for growing organic tissue." - density = 1 - icon = 'icons/obj/cloning.dmi' - icon_state = "pod_0" - req_access = list(access_genetics) //For premature unlocking. - var/mob/living/carbon/human/occupant - var/heal_level //The clone is released once its health reaches this level. - var/obj/machinery/computer/cloning/connected = null //So we remember the connected clone machine. - var/mess = 0 //Need to clean out it if it's full of exploded clone. - var/attempting = 0 //One clone attempt at a time thanks - var/biomass = 0 - var/speed_coeff - var/efficiency - - var/datum/mind/clonemind - var/grab_ghost_when = CLONER_MATURE_CLONE - - var/obj/item/radio/Radio - var/radio_announce = 0 - - var/obj/effect/countdown/clonepod/countdown - - var/list/brine_types = list("corazone", "perfluorodecalin", "epinephrine", "salglu_solution") //stops heart attacks, heart failure, shock, and keeps their O2 levels normal - var/list/missing_organs - var/organs_number = 0 - - light_color = LIGHT_COLOR_PURE_GREEN - -/obj/machinery/clonepod/power_change() - ..() - if(!(stat & (BROKEN|NOPOWER))) - set_light(2) - else - set_light(0) - -/obj/machinery/clonepod/biomass - biomass = CLONE_BIOMASS - -/obj/machinery/clonepod/New() - ..() - countdown = new(src) - - Radio = new /obj/item/radio(src) - Radio.listening = 0 - Radio.config(list("Medical" = 0)) - - component_parts = list() - component_parts += new /obj/item/circuitboard/clonepod(null) - component_parts += new /obj/item/stock_parts/scanning_module(null) - component_parts += new /obj/item/stock_parts/scanning_module(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - component_parts += new /obj/item/stack/cable_coil(null, 1) - RefreshParts() - update_icon() - -/obj/machinery/clonepod/upgraded/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/clonepod(null) - component_parts += new /obj/item/stock_parts/scanning_module/phasic(null) - component_parts += new /obj/item/stock_parts/scanning_module/phasic(null) - component_parts += new /obj/item/stock_parts/manipulator/pico(null) - component_parts += new /obj/item/stock_parts/manipulator/pico(null) - component_parts += new /obj/item/stack/sheet/glass(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - component_parts += new /obj/item/stack/cable_coil(null, 1) - biomass = CLONE_BIOMASS - RefreshParts() - -/obj/machinery/clonepod/Destroy() - if(connected) - connected.pods -= src - for(var/s in sharedSoulhooks) - var/datum/soullink/S = s - S.removeSoulsharer(src) //If a sharer is destroy()'d, they are simply removed - sharedSoulhooks = null - QDEL_NULL(Radio) - QDEL_NULL(countdown) - QDEL_LIST(missing_organs) - return ..() - -/obj/machinery/clonepod/RefreshParts() - speed_coeff = 0 - efficiency = 0 - for(var/obj/item/stock_parts/scanning_module/S in component_parts) - efficiency += S.rating - for(var/obj/item/stock_parts/manipulator/P in component_parts) - speed_coeff += P.rating - heal_level = max(min((efficiency * 15) + 10, 100), MINIMUM_HEAL_LEVEL) - -//The return of data disks?? Just for transferring between genetics machine/cloning machine. -//TO-DO: Make the genetics machine accept them. -/obj/item/disk/data - name = "Cloning Data Disk" - icon_state = "datadisk0" //Gosh I hope syndies don't mistake them for the nuke disk. - var/datum/dna2/record/buf = null - var/read_only = 0 //Well,it's still a floppy disk - -/obj/item/disk/data/proc/initialize() - buf = new - buf.dna=new - -/obj/item/disk/data/Destroy() - QDEL_NULL(buf) - return ..() - -/obj/item/disk/data/demo - name = "data disk - 'God Emperor of Mankind'" - read_only = 1 - -/obj/item/disk/data/demo/New() - initialize() - buf.types=DNA2_BUF_UE|DNA2_BUF_UI - //data = "066000033000000000AF00330660FF4DB002690" - //data = "0C80C80C80C80C80C8000000000000161FBDDEF" - Farmer Jeff - buf.dna.real_name="God Emperor of Mankind" - buf.dna.unique_enzymes = md5(buf.dna.real_name) - buf.dna.UI=list(0x066,0x000,0x033,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0xAF0,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x000,0x033,0x066,0x0FF,0x4DB,0x002,0x690,0x000,0x000,0x000,0x328,0x045,0x5FC,0x053,0x035,0x035,0x035) - //buf.dna.UI=list(0x0C8,0x0C8,0x0C8,0x0C8,0x0C8,0x0C8,0x000,0x000,0x000,0x000,0x161,0xFBD,0xDEF) // Farmer Jeff - if(buf.dna.UI.len != DNA_UI_LENGTH) //If there's a disparity b/w the dna UI string lengths, 0-fill the extra blocks in this UI. - for(var/i in buf.dna.UI.len to DNA_UI_LENGTH) - buf.dna.UI += 0x000 - buf.dna.ResetSE() - buf.dna.UpdateUI() - -/obj/item/disk/data/monkey - name = "data disk - 'Mr. Muggles'" - read_only = 1 - -/obj/item/disk/data/monkey/New() - initialize() - buf.types=DNA2_BUF_SE - var/list/new_SE=list(0x098,0x3E8,0x403,0x44C,0x39F,0x4B0,0x59D,0x514,0x5FC,0x578,0x5DC,0x640,0x6A4) - for(var/i=new_SE.len;i<=DNA_SE_LENGTH;i++) - new_SE += rand(1,1024) - buf.dna.SE=new_SE - buf.dna.SetSEValueRange(MONKEYBLOCK,0xDAC, 0xFFF) - -//Disk stuff. -/obj/item/disk/data/New() - ..() - var/diskcolor = pick(0,1,2) - icon_state = "datadisk[diskcolor]" - -/obj/item/disk/data/attack_self(mob/user as mob) - read_only = !read_only - to_chat(user, "You flip the write-protect tab to [read_only ? "protected" : "unprotected"].") - -/obj/item/disk/data/examine(mob/user) - . = ..() - . += "The write-protect tab is set to [read_only ? "protected" : "unprotected"]." - -//Clonepod - -/obj/machinery/clonepod/examine(mob/user) - . = ..() - if(mess) - . += "It's filled with blood and viscera. You swear you can see it moving..." - if(!occupant || stat & (NOPOWER|BROKEN)) - return - if(occupant && occupant.stat != DEAD) - . += "Current clone cycle is [round(get_completion())]% complete." - -/obj/machinery/clonepod/return_air() //non-reactive air - var/datum/gas_mixture/GM = new - GM.nitrogen = MOLES_O2STANDARD + MOLES_N2STANDARD - GM.temperature = T20C - return GM - -/obj/machinery/clonepod/proc/get_completion() - . = (100 * ((occupant.health + 100) / (heal_level + 100))) - -/obj/machinery/clonepod/attack_ai(mob/user) - return examine(user) - -//Radio Announcement - -/obj/machinery/clonepod/proc/announce_radio_message(message) - if(radio_announce) - Radio.autosay(message, name, "Medical", list(z)) - -/obj/machinery/clonepod/proc/spooky_devil_flavor() - playsound(loc, pick('sound/goonstation/voice/male_scream.ogg', 'sound/goonstation/voice/female_scream.ogg'), 100, 1) - mess = 1 - update_icon() - connected_message("If you keep trying to steal from me, you'll end up with me.") - -//Start growing a human clone in the pod! -/obj/machinery/clonepod/proc/growclone(datum/dna2/record/R) - if(mess || attempting || panel_open || stat & (NOPOWER|BROKEN)) - return 0 - clonemind = locate(R.mind) - if(!istype(clonemind)) //not a mind - return 0 - if(clonemind.current && clonemind.current.stat != DEAD) //mind is associated with a non-dead body - return 0 - if(clonemind.damnation_type) - spooky_devil_flavor() - return 0 - if(!clonemind.is_revivable()) //Other reasons for being unrevivable - return 0 - if(clonemind.active) //somebody is using that mind - if(ckey(clonemind.key) != R.ckey ) - return 0 - if(clonemind.suicided) // and stay out! - malfunction(go_easy = 0) - return -1 // Flush the record - else - // get_ghost() will fail if they're unable to reenter their body - var/mob/dead/observer/G = clonemind.get_ghost() - if(!G) - return 0 - -/* - if(clonemind.damnation_type) //Can't clone the damned. - playsound('sound/hallucinations/veryfar_noise.ogg', 50, 0) - malfunction() - return -1 // so that the record gets flushed out - */ - - if(biomass >= CLONE_BIOMASS) - biomass -= CLONE_BIOMASS - else - return 0 - - attempting = 1 //One at a time!! - countdown.start() - - if(!R.dna) - R.dna = new /datum/dna() - - var/mob/living/carbon/human/H = new /mob/living/carbon/human(src) - H.set_species(R.dna.species.type) - occupant = H - - if(!R.dna.real_name) //to prevent null names - R.dna.real_name = H.real_name - else - H.real_name = R.dna.real_name - - H.dna = R.dna.Clone() - - for(var/datum/language/L in R.languages) - H.add_language(L.name) - - domutcheck(H, null, MUTCHK_FORCED) //Ensures species that get powers by the species proc handle_dna keep them - - if(efficiency > 2 && efficiency < 5 && prob(25)) - randmutb(H) - if(efficiency > 5 && prob(20)) - randmutg(H) - if(efficiency < 3 && prob(50)) - randmutb(H) - - H.dna.UpdateSE() - H.dna.UpdateUI() - - H.sync_organ_dna(1) // It's literally a fresh body as you can get, so all organs properly belong to it - H.UpdateAppearance() - - check_brine() - //Get the clone body ready - maim_clone(H) - H.Paralyse(4) - - if(grab_ghost_when == CLONER_FRESH_CLONE) - clonemind.transfer_to(H) - H.ckey = R.ckey - update_clone_antag(H) //Since the body's got the mind, update their antag stuff right now. Otherwise, wait until they get kicked out (as per the CLONER_MATURE_CLONE business) to do it. - to_chat(H, {"Consciousness slowly creeps over you - as your body regenerates.
        So this is what cloning - feels like?
        "}) - else if(grab_ghost_when == CLONER_MATURE_CLONE) - to_chat(clonemind.current, {"Your body is - beginning to regenerate in a cloning pod. You will - become conscious when it is complete."}) - // Set up a soul link with the dead body to catch a revival - soullink(/datum/soullink/soulhook, clonemind.current, src) - - update_icon() - - H.suiciding = 0 - attempting = 0 - return 1 - -//Grow clones to maturity then kick them out. FREELOADERS -/obj/machinery/clonepod/process() - var/show_message = 0 - for(var/obj/item/reagent_containers/food/snacks/meat/meat in range(1, src)) - qdel(meat) - biomass += BIOMASS_MEAT_AMOUNT - show_message = 1 - if(show_message) - visible_message("[src] sucks in and processes the nearby biomass.") - - if(stat & NOPOWER) //Autoeject if power is lost - if(occupant) - go_out() - connected_message("Clone Ejected: Loss of power.") - - else if((occupant) && (occupant.loc == src)) - if((occupant.stat == DEAD) || (occupant.suiciding) || (occupant.mind && !occupant.mind.is_revivable())) //Autoeject corpses and suiciding dudes. - announce_radio_message("The cloning of [occupant] has been aborted due to unrecoverable tissue failure.") - go_out() - connected_message("Clone Rejected: Deceased.") - - else if(occupant.cloneloss > (100 - heal_level)) - occupant.Paralyse(4) - - //Slowly get that clone healed and finished. - occupant.adjustCloneLoss(-((speed_coeff/2))) - - // For human species that lack non-vital parts for some weird reason - if(organs_number) - var/progress = CLONE_INITIAL_DAMAGE - occupant.getCloneLoss() - progress += (100 - MINIMUM_HEAL_LEVEL) - var/milestone = CLONE_INITIAL_DAMAGE / organs_number -// Doing this as a #define so that the value can change when evaluated multiple times -#define INSTALLED (organs_number - LAZYLEN(missing_organs)) - - while((progress / milestone) > INSTALLED && LAZYLEN(missing_organs)) - var/obj/item/organ/I = pick_n_take(missing_organs) - I.safe_replace(occupant) - -#undef INSTALLED - - //Premature clones may have brain damage. - occupant.adjustBrainLoss(-((speed_coeff/20)*efficiency)) - - check_brine() - - //Also heal some oxyloss ourselves just in case!! - occupant.adjustOxyLoss(-10) - - use_power(7500) //This might need tweaking. - - else if((occupant.cloneloss <= (100 - heal_level))) - connected_message("Cloning Process Complete.") - announce_radio_message("The cloning cycle of [occupant] is complete.") - go_out() - - else if((!occupant) || (occupant.loc != src)) - occupant = null - update_icon() - use_power(200) - -//Let's unlock this early I guess. Might be too early, needs tweaking. -/obj/machinery/clonepod/attackby(obj/item/I, mob/user, params) - if(!(occupant || mess)) - if(default_deconstruction_screwdriver(user, "[icon_state]_maintenance", "[initial(icon_state)]", I)) - return - - if(exchange_parts(user, I)) - return - - if(default_deconstruction_crowbar(I)) - return - - if(I.GetID()) - if(!check_access(I)) - to_chat(user, "Access Denied.") - return - if(!(occupant || mess)) - to_chat(user, "Error: Pod has no occupant.") - return - else - connected_message("Authorized Ejection") - announce_radio_message("An authorized ejection of [(occupant) ? occupant.real_name : "the malfunctioning pod"] has occured") - to_chat(user, "You force an emergency ejection.") - go_out() - -//Removing cloning pod biomass - else if(istype(I, /obj/item/reagent_containers/food/snacks/meat)) - if(user.drop_item()) - to_chat(user, "[src] processes [I].") - biomass += BIOMASS_MEAT_AMOUNT - qdel(I) - else if(iswrench(I)) - if(occupant) - to_chat(user, "Can not do that while [src] is in use.") - else - if(anchored) - anchored = FALSE - connected.pods -= src - connected = null - else - anchored = TRUE - playsound(loc, I.usesound, 100, 1) - if(anchored) - user.visible_message("[user] secures [src] to the floor.", "You secure [src] to the floor.") - else - user.visible_message("[user] unsecures [src] from the floor.", "You unsecure [src] from the floor.") - else if(ismultitool(I)) - var/obj/item/multitool/M = I - M.buffer = src - to_chat(user, "You load connection data from [src] to [M].") - return - else - return ..() - -/obj/machinery/clonepod/emag_act(user) - if(isnull(occupant)) - return - go_out() - -/obj/machinery/clonepod/proc/update_clone_antag(var/mob/living/carbon/human/H) - // Check to see if the clone's mind is an antagonist of any kind and handle them accordingly to make sure they get their spells, HUD/whatever else back. - if((H.mind in SSticker.mode:revolutionaries) || (H.mind in SSticker.mode:head_revolutionaries)) - SSticker.mode.update_rev_icons_added() //So the icon actually appears - if(H.mind in SSticker.mode.syndicates) - SSticker.mode.update_synd_icons_added() - if(H.mind in SSticker.mode.cult) - SSticker.mode.add_cultist(occupant.mind) - SSticker.mode.update_cult_icons_added() //So the icon actually appears - SSticker.mode.update_cult_comms_added(H.mind) //So the comms actually appears - if(H.mind.vampire) - H.mind.vampire.update_owner(H) - if((H.mind in SSticker.mode.vampire_thralls) || (H.mind in SSticker.mode.vampire_enthralled)) - SSticker.mode.update_vampire_icons_added(H.mind) - if(H.mind in SSticker.mode.changelings) - SSticker.mode.update_change_icons_added(H.mind) - if((H.mind in SSticker.mode.shadowling_thralls) || (H.mind in SSticker.mode.shadows)) - SSticker.mode.update_shadow_icons_added(H.mind) - -//Put messages in the connected computer's temp var for display. -/obj/machinery/clonepod/proc/connected_message(message) - if((isnull(connected)) || (!istype(connected, /obj/machinery/computer/cloning))) - return 0 - if(!message) - return 0 - - connected.temp = "[name] : [message]" - connected.updateUsrDialog() - return 1 - -/obj/machinery/clonepod/proc/go_out() - countdown.stop() - var/turf/T = get_turf(src) - if(mess) //Clean that mess and dump those gibs! - for(var/i in missing_organs) - var/obj/I = i - I.forceMove(T) - missing_organs.Cut() - mess = FALSE - new /obj/effect/gibspawner/generic(get_turf(src), occupant) - playsound(loc, 'sound/effects/splat.ogg', 50, 1) - update_icon() - return - - if(!occupant) - return - - if(grab_ghost_when == CLONER_MATURE_CLONE) - clonemind.transfer_to(occupant) - occupant.grab_ghost() - update_clone_antag(occupant) - to_chat(occupant, "There is a bright flash!
        \ - You feel like a new being.
        ") - occupant.flash_eyes(visual = 1) - for(var/s in sharedSoulhooks) - var/datum/soullink/S = s - S.removeSoulsharer(src) //If a sharer is destroy()'d, they are simply removed - sharedSoulhooks = null - - - for(var/i in missing_organs) - qdel(i) - missing_organs.Cut() - occupant.SetLoseBreath(0) // Stop friggin' dying, gosh damn - occupant.setOxyLoss(0) - for(var/datum/disease/critical/crit in occupant.viruses) - crit.cure() - occupant.forceMove(T) - occupant.update_body() - domutcheck(occupant) //Waiting until they're out before possible notransform. - occupant.special_post_clone_handling() - occupant = null - update_icon() - -/obj/machinery/clonepod/proc/malfunction(go_easy = FALSE) - if(occupant) - connected_message("Critical Error!") - announce_radio_message("Critical error! Please contact a Thinktronic Systems technician, as your warranty may be affected.") - for(var/s in sharedSoulhooks) - var/datum/soullink/S = s - S.removeSoulsharer(src) //If a sharer is destroy()'d, they are simply removed - sharedSoulhooks = null - if(!go_easy) - if(occupant.mind != clonemind) - clonemind.transfer_to(occupant) - occupant.grab_ghost() // We really just want to make you suffer. - to_chat(occupant, {"Agony blazes across your - consciousness as your body is torn apart.
        - Is this what dying is like? Yes it is.
        "}) - occupant << sound('sound/hallucinations/veryfar_noise.ogg',0,1,50) - for(var/i in missing_organs) - qdel(i) - missing_organs.Cut() - spawn(40) - qdel(occupant) - - - playsound(loc, 'sound/machines/warning-buzzer.ogg', 50, 0) - mess = TRUE - update_icon() - -/obj/machinery/clonepod/update_icon() - ..() - icon_state = "pod_0" - if(occupant && !(stat & NOPOWER)) - icon_state = "pod_1" - else if(mess && !panel_open) - icon_state = "pod_g" - -/obj/machinery/clonepod/relaymove(mob/user) - if(user.stat == CONSCIOUS) - go_out() - -/obj/machinery/clonepod/emp_act(severity) - if(prob(100/(severity*efficiency))) malfunction() - ..() - -/obj/machinery/clonepod/ex_act(severity) - ..() - if(!QDELETED(src) && occupant) - go_out() - -/obj/machinery/clonepod/handle_atom_del(atom/A) - if(A == occupant) - occupant = null - countdown.stop() - -/obj/machinery/clonepod/deconstruct(disassembled = TRUE) - if(occupant) - go_out() - ..() - -/obj/machinery/clonepod/onSoullinkRevive(mob/living/L) - if(occupant && L == clonemind.current) - // The old body's back in shape, time to ditch the cloning one - malfunction(go_easy = TRUE) - -/obj/machinery/clonepod/proc/maim_clone(mob/living/carbon/human/H) - LAZYINITLIST(missing_organs) - for(var/i in missing_organs) - qdel(i) - missing_organs.Cut() - - H.setCloneLoss(CLONE_INITIAL_DAMAGE, FALSE) - H.setBrainLoss(BRAIN_INITIAL_DAMAGE) - - for(var/o in H.internal_organs) - var/obj/item/organ/O = o - if(!istype(O) || O.vital) - continue - - // Let's non-specially remove all non-vital organs - // What could possibly go wrong - var/obj/item/I = O.remove(H) - // Make this support stuff that turns into items when removed - I.forceMove(src) - missing_organs += I - - var/static/list/zones = list("r_arm", "l_arm", "r_leg", "l_leg") - for(var/zone in zones) - var/obj/item/organ/external/E = H.get_organ(zone) - var/obj/item/I = E.remove(H) - I.forceMove(src) - missing_organs += I - - organs_number = LAZYLEN(missing_organs) - H.updatehealth() - -/obj/machinery/clonepod/proc/check_brine() - // Clones are in a pickled bath of mild chemicals, keeping - // them alive, despite their lack of internal organs - for(var/bt in brine_types) - if(occupant.reagents.get_reagent_amount(bt) < 1) - occupant.reagents.add_reagent(bt, 1) - -/* - * Diskette Box - */ - -/obj/item/storage/box/disks - name = "Diskette Box" - icon_state = "disk_kit" - -/obj/item/storage/box/disks/New() - ..() - new /obj/item/disk/data(src) - new /obj/item/disk/data(src) - new /obj/item/disk/data(src) - new /obj/item/disk/data(src) - new /obj/item/disk/data(src) - new /obj/item/disk/data(src) - new /obj/item/disk/data(src) - -/* - * Manual -- A big ol' manual. - */ - -/obj/item/paper/Cloning - name = "paper - 'H-87 Cloning Apparatus Manual" - info = {"

        Getting Started

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

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

        -

        Cloning from a profile

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

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


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

        Profile Management

        -

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


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

        Getting Started

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

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

        +

        Cloning from a profile

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

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


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

        Profile Management

        +

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


        + A good diskette is a great way to counter aforementioned genetic drift!
        +
        + This technology produced under license from Thinktronic Systems, LTD."} + +//SOME SCRAPS I GUESS +/* EMP grenade/spell effect + if(istype(A, /obj/machinery/clonepod)) + A:malfunction() +*/ + +#undef MINIMUM_HEAL_LEVEL diff --git a/code/game/machinery/computer/HolodeckControl.dm b/code/game/machinery/computer/HolodeckControl.dm index 4c730963519..cb5e4da5e95 100644 --- a/code/game/machinery/computer/HolodeckControl.dm +++ b/code/game/machinery/computer/HolodeckControl.dm @@ -415,9 +415,11 @@ var/active = 0 /obj/item/holo/esword/green/New() + ..() item_color = "green" /obj/item/holo/esword/red/New() + ..() item_color = "red" /obj/item/holo/esword/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) @@ -426,6 +428,7 @@ return 0 /obj/item/holo/esword/New() + ..() item_color = pick("red","blue","green","purple") /obj/item/holo/esword/attack_self(mob/living/user as mob) diff --git a/code/game/machinery/computer/Operating.dm b/code/game/machinery/computer/Operating.dm index bf019593065..ad11d6fb7c5 100644 --- a/code/game/machinery/computer/Operating.dm +++ b/code/game/machinery/computer/Operating.dm @@ -1,188 +1,188 @@ -#define OP_COMPUTER_COOLDOWN 60 - -/obj/machinery/computer/operating - name = "operating computer" - density = 1 - anchored = 1.0 - icon_keyboard = "med_key" - icon_screen = "crew" - circuit = /obj/item/circuitboard/operating - var/obj/machinery/optable/table = null - var/mob/living/carbon/human/victim = null - light_color = LIGHT_COLOR_PURE_BLUE - var/verbose = 1 //general speaker toggle - var/patientName = null - var/oxyAlarm = 30 //oxy damage at which the computer will beep - var/choice = 0 //just for going into and out of the options menu - var/healthAnnounce = 1 //healther announcer toggle - var/crit = 1 //crit beeping toggle - var/nextTick = OP_COMPUTER_COOLDOWN - var/healthAlarm = 50 - var/oxy = 1 //oxygen beeping toggle - -/obj/machinery/computer/operating/New() - ..() - for(dir in list(NORTH,EAST,SOUTH,WEST)) - table = locate(/obj/machinery/optable, get_step(src, dir)) - if(table) - table.computer = src - break - -/obj/machinery/computer/operating/Destroy() - if(table) - table.computer = null - table = null - if(victim) - victim = null - return ..() - -/obj/machinery/computer/operating/attack_ai(mob/user) - add_fingerprint(user) - if(stat & (BROKEN|NOPOWER)) - return - ui_interact(user) - - -/obj/machinery/computer/operating/attack_hand(mob/user) - if(..(user)) - return - - if(stat & (NOPOWER|BROKEN)) - return - - - add_fingerprint(user) - ui_interact(user) - -/obj/machinery/computer/operating/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)//ui is mostly copy pasta from the sleeper ui - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "op_computer.tmpl", "Patient Monitor", 650, 455) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/computer/operating/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - var/mob/living/carbon/human/occupant - if(table) - occupant = table.victim - data["hasOccupant"] = occupant ? 1 : 0 - var/occupantData[0] - - if(occupant) - occupantData["name"] = occupant.name - occupantData["stat"] = occupant.stat - occupantData["health"] = occupant.health - occupantData["maxHealth"] = occupant.maxHealth - occupantData["minHealth"] = HEALTH_THRESHOLD_DEAD - occupantData["bruteLoss"] = occupant.getBruteLoss() - occupantData["oxyLoss"] = occupant.getOxyLoss() - occupantData["toxLoss"] = occupant.getToxLoss() - occupantData["fireLoss"] = occupant.getFireLoss() - occupantData["paralysis"] = occupant.paralysis - occupantData["hasBlood"] = 0 - occupantData["bodyTemperature"] = occupant.bodytemperature - occupantData["maxTemp"] = 1000 // If you get a burning vox armalis into the sleeper, congratulations - // Because we can put simple_animals in here, we need to do something tricky to get things working nice - occupantData["temperatureSuitability"] = 0 // 0 is the baseline - if(ishuman(occupant) && occupant.dna.species) - var/datum/species/sp = occupant.dna.species - if(occupant.bodytemperature < sp.cold_level_3) - occupantData["temperatureSuitability"] = -3 - else if(occupant.bodytemperature < sp.cold_level_2) - occupantData["temperatureSuitability"] = -2 - else if(occupant.bodytemperature < sp.cold_level_1) - occupantData["temperatureSuitability"] = -1 - else if(occupant.bodytemperature > sp.heat_level_3) - occupantData["temperatureSuitability"] = 3 - else if(occupant.bodytemperature > sp.heat_level_2) - occupantData["temperatureSuitability"] = 2 - else if(occupant.bodytemperature > sp.heat_level_1) - occupantData["temperatureSuitability"] = 1 - else if(istype(occupant, /mob/living/simple_animal)) - var/mob/living/simple_animal/silly = occupant - if(silly.bodytemperature < silly.minbodytemp) - occupantData["temperatureSuitability"] = -3 - else if(silly.bodytemperature > silly.maxbodytemp) - occupantData["temperatureSuitability"] = 3 - // Blast you, imperial measurement system - occupantData["btCelsius"] = occupant.bodytemperature - T0C - occupantData["btFaren"] = ((occupant.bodytemperature - T0C) * (9.0/5.0))+ 32 - - if(ishuman(occupant) && !(NO_BLOOD in occupant.dna.species.species_traits)) - occupantData["pulse"] = occupant.get_pulse(GETPULSE_TOOL) - occupantData["hasBlood"] = 1 - occupantData["bloodLevel"] = round(occupant.blood_volume) - occupantData["bloodMax"] = occupant.max_blood - occupantData["bloodPercent"] = round(100*(occupant.blood_volume/occupant.max_blood), 0.01) //copy pasta ends here - - occupantData["bloodType"] = occupant.dna.blood_type - if(occupant.surgeries.len) - occupantData["inSurgery"] = 1 - for(var/datum/surgery/procedure in occupant.surgeries) - occupantData["surgeryName"] = "[capitalize(procedure.name)]" - var/datum/surgery_step/surgery_step = procedure.get_surgery_step() - occupantData["stepName"] = "[capitalize(surgery_step.name)]" - - data["occupant"] = occupantData - data["verbose"]=verbose - data["oxyAlarm"]=oxyAlarm - data["choice"]=choice - data["health"]=healthAnnounce - data["crit"]=crit - data["healthAlarm"]=healthAlarm - data["oxy"]=oxy - - return data - - -/obj/machinery/computer/operating/Topic(href, href_list) - if(..()) - return 1 - if((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) - usr.set_machine(src) - - if(href_list["verboseOn"]) - verbose=1 - if(href_list["verboseOff"]) - verbose=0 - if(href_list["healthOn"]) - healthAnnounce=1 - if(href_list["healthOff"]) - healthAnnounce=0 - if(href_list["critOn"]) - crit=1 - if(href_list["critOff"]) - crit=0 - if(href_list["oxyOn"]) - oxy=1 - if(href_list["oxyOff"]) - oxy=0 - if(href_list["oxy_adj"]!=0) - oxyAlarm=oxyAlarm+text2num(href_list["oxy_adj"]) - if(href_list["choiceOn"]) - choice=1 - if(href_list["choiceOff"]) - choice=0 - if(href_list["health_adj"]!=0) - healthAlarm=healthAlarm+text2num(href_list["health_adj"]) - return - - -/obj/machinery/computer/operating/process() - - if(table && table.check_victim()) - if(verbose) - if(patientName!=table.victim.name) - patientName=table.victim.name - atom_say("New patient detected, loading stats") - victim = table.victim - atom_say("[victim.real_name], [victim.dna.blood_type] blood, [victim.stat ? "Non-Responsive" : "Awake"]") - if(nextTick < world.time) - nextTick=world.time + OP_COMPUTER_COOLDOWN - if(crit && victim.health <= -50 ) - playsound(src.loc, 'sound/machines/defib_success.ogg', 50, 0) - if(oxy && victim.getOxyLoss()>oxyAlarm) - playsound(src.loc, 'sound/machines/defib_saftyoff.ogg', 50, 0) - if(healthAnnounce && victim.health <= healthAlarm) - atom_say("[round(victim.health)]") +#define OP_COMPUTER_COOLDOWN 60 + +/obj/machinery/computer/operating + name = "operating computer" + density = 1 + anchored = 1.0 + icon_keyboard = "med_key" + icon_screen = "crew" + circuit = /obj/item/circuitboard/operating + var/obj/machinery/optable/table = null + var/mob/living/carbon/human/victim = null + light_color = LIGHT_COLOR_PURE_BLUE + var/verbose = 1 //general speaker toggle + var/patientName = null + var/oxyAlarm = 30 //oxy damage at which the computer will beep + var/choice = 0 //just for going into and out of the options menu + var/healthAnnounce = 1 //healther announcer toggle + var/crit = 1 //crit beeping toggle + var/nextTick = OP_COMPUTER_COOLDOWN + var/healthAlarm = 50 + var/oxy = 1 //oxygen beeping toggle + +/obj/machinery/computer/operating/New() + ..() + for(dir in list(NORTH,EAST,SOUTH,WEST)) + table = locate(/obj/machinery/optable, get_step(src, dir)) + if(table) + table.computer = src + break + +/obj/machinery/computer/operating/Destroy() + if(table) + table.computer = null + table = null + if(victim) + victim = null + return ..() + +/obj/machinery/computer/operating/attack_ai(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) + return + ui_interact(user) + + +/obj/machinery/computer/operating/attack_hand(mob/user) + if(..(user)) + return + + if(stat & (NOPOWER|BROKEN)) + return + + + add_fingerprint(user) + ui_interact(user) + +/obj/machinery/computer/operating/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)//ui is mostly copy pasta from the sleeper ui + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "op_computer.tmpl", "Patient Monitor", 650, 455) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/computer/operating/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) + var/data[0] + var/mob/living/carbon/human/occupant + if(table) + occupant = table.victim + data["hasOccupant"] = occupant ? 1 : 0 + var/occupantData[0] + + if(occupant) + occupantData["name"] = occupant.name + occupantData["stat"] = occupant.stat + occupantData["health"] = occupant.health + occupantData["maxHealth"] = occupant.maxHealth + occupantData["minHealth"] = HEALTH_THRESHOLD_DEAD + occupantData["bruteLoss"] = occupant.getBruteLoss() + occupantData["oxyLoss"] = occupant.getOxyLoss() + occupantData["toxLoss"] = occupant.getToxLoss() + occupantData["fireLoss"] = occupant.getFireLoss() + occupantData["paralysis"] = occupant.paralysis + occupantData["hasBlood"] = 0 + occupantData["bodyTemperature"] = occupant.bodytemperature + occupantData["maxTemp"] = 1000 // If you get a burning vox armalis into the sleeper, congratulations + // Because we can put simple_animals in here, we need to do something tricky to get things working nice + occupantData["temperatureSuitability"] = 0 // 0 is the baseline + if(ishuman(occupant) && occupant.dna.species) + var/datum/species/sp = occupant.dna.species + if(occupant.bodytemperature < sp.cold_level_3) + occupantData["temperatureSuitability"] = -3 + else if(occupant.bodytemperature < sp.cold_level_2) + occupantData["temperatureSuitability"] = -2 + else if(occupant.bodytemperature < sp.cold_level_1) + occupantData["temperatureSuitability"] = -1 + else if(occupant.bodytemperature > sp.heat_level_3) + occupantData["temperatureSuitability"] = 3 + else if(occupant.bodytemperature > sp.heat_level_2) + occupantData["temperatureSuitability"] = 2 + else if(occupant.bodytemperature > sp.heat_level_1) + occupantData["temperatureSuitability"] = 1 + else if(istype(occupant, /mob/living/simple_animal)) + var/mob/living/simple_animal/silly = occupant + if(silly.bodytemperature < silly.minbodytemp) + occupantData["temperatureSuitability"] = -3 + else if(silly.bodytemperature > silly.maxbodytemp) + occupantData["temperatureSuitability"] = 3 + // Blast you, imperial measurement system + occupantData["btCelsius"] = occupant.bodytemperature - T0C + occupantData["btFaren"] = ((occupant.bodytemperature - T0C) * (9.0/5.0))+ 32 + + if(ishuman(occupant) && !(NO_BLOOD in occupant.dna.species.species_traits)) + occupantData["pulse"] = occupant.get_pulse(GETPULSE_TOOL) + occupantData["hasBlood"] = 1 + occupantData["bloodLevel"] = round(occupant.blood_volume) + occupantData["bloodMax"] = occupant.max_blood + occupantData["bloodPercent"] = round(100*(occupant.blood_volume/occupant.max_blood), 0.01) //copy pasta ends here + + occupantData["bloodType"] = occupant.dna.blood_type + if(occupant.surgeries.len) + occupantData["inSurgery"] = 1 + for(var/datum/surgery/procedure in occupant.surgeries) + occupantData["surgeryName"] = "[capitalize(procedure.name)]" + var/datum/surgery_step/surgery_step = procedure.get_surgery_step() + occupantData["stepName"] = "[capitalize(surgery_step.name)]" + + data["occupant"] = occupantData + data["verbose"]=verbose + data["oxyAlarm"]=oxyAlarm + data["choice"]=choice + data["health"]=healthAnnounce + data["crit"]=crit + data["healthAlarm"]=healthAlarm + data["oxy"]=oxy + + return data + + +/obj/machinery/computer/operating/Topic(href, href_list) + if(..()) + return 1 + if((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) + usr.set_machine(src) + + if(href_list["verboseOn"]) + verbose=1 + if(href_list["verboseOff"]) + verbose=0 + if(href_list["healthOn"]) + healthAnnounce=1 + if(href_list["healthOff"]) + healthAnnounce=0 + if(href_list["critOn"]) + crit=1 + if(href_list["critOff"]) + crit=0 + if(href_list["oxyOn"]) + oxy=1 + if(href_list["oxyOff"]) + oxy=0 + if(href_list["oxy_adj"]!=0) + oxyAlarm=oxyAlarm+text2num(href_list["oxy_adj"]) + if(href_list["choiceOn"]) + choice=1 + if(href_list["choiceOff"]) + choice=0 + if(href_list["health_adj"]!=0) + healthAlarm=healthAlarm+text2num(href_list["health_adj"]) + return + + +/obj/machinery/computer/operating/process() + + if(table && table.check_victim()) + if(verbose) + if(patientName!=table.victim.name) + patientName=table.victim.name + atom_say("New patient detected, loading stats") + victim = table.victim + atom_say("[victim.real_name], [victim.dna.blood_type] blood, [victim.stat ? "Non-Responsive" : "Awake"]") + if(nextTick < world.time) + nextTick=world.time + OP_COMPUTER_COOLDOWN + if(crit && victim.health <= -50 ) + playsound(src.loc, 'sound/machines/defib_success.ogg', 50, 0) + if(oxy && victim.getOxyLoss()>oxyAlarm) + playsound(src.loc, 'sound/machines/defib_saftyoff.ogg', 50, 0) + if(healthAnnounce && victim.health <= healthAlarm) + atom_say("[round(victim.health)]") diff --git a/code/game/machinery/computer/ai_core.dm b/code/game/machinery/computer/ai_core.dm index dfac3250d44..d53903857ef 100644 --- a/code/game/machinery/computer/ai_core.dm +++ b/code/game/machinery/computer/ai_core.dm @@ -17,197 +17,185 @@ return ..() /obj/structure/AIcore/attackby(obj/item/P, mob/user, params) - if(istype(P, /obj/item/wrench)) - return default_unfasten_wrench(user, P, 20) - if(!anchored) - if(iswelder(P)) - if(state != EMPTY_CORE) - to_chat(user, "The core must be empty to deconstruct it!") + switch(state) + if(EMPTY_CORE) + if(istype(P, /obj/item/circuitboard/aicore)) + if(!user.drop_item()) + return + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You place the circuit board inside the frame.") + update_icon() + state = CIRCUIT_CORE + P.forceMove(src) + circuit = P return - var/obj/item/weldingtool/WT = P - if(!WT.isOn()) - to_chat(user, "The welder must be on for this task!") - return - playsound(loc, WT.usesound, 50, 1) - to_chat(user, "You start to deconstruct the frame...") - if(do_after(user, 20*P.toolspeed, target = src) && src && state == EMPTY_CORE && WT && WT.remove_fuel(0, user)) - to_chat(user, "You deconstruct the frame.") - deconstruct(TRUE) - return - else - switch(state) - if(EMPTY_CORE) - if(istype(P, /obj/item/circuitboard/aicore)) - if(!user.drop_item()) - return - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You place the circuit board inside the frame.") - update_icon() - state = CIRCUIT_CORE - P.forceMove(src) - circuit = P - return - if(CIRCUIT_CORE) - if(isscrewdriver(P)) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You screw the circuit board into place.") - state = SCREWED_CORE - update_icon() - return - if(iscrowbar(P)) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You remove the circuit board.") - state = EMPTY_CORE - update_icon() - circuit.forceMove(loc) - circuit = null - return - if(SCREWED_CORE) - if(iswirecutter(P) && circuit) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You unfasten the circuit board.") - state = CIRCUIT_CORE - update_icon() - return - if(istype(P, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = P - if(C.get_amount() >= 5) - playsound(loc, 'sound/items/deconstruct.ogg', 50, 1) - to_chat(user, "You start to add cables to the frame...") - if(do_after(user, 20, target = src) && state == SCREWED_CORE && C.use(5)) - to_chat(user, "You add cables to the frame.") - state = CABLED_CORE - update_icon() - else - to_chat(user, "You need five lengths of cable to wire the AI core!") - return - if(CABLED_CORE) - if(iswirecutter(P)) - if(brain) - to_chat(user, "Get that [brain.name] out of there first!") - else - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You remove the cables.") - state = SCREWED_CORE + if(SCREWED_CORE) + if(istype(P, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = P + if(C.get_amount() >= 5) + playsound(loc, 'sound/items/deconstruct.ogg', 50, 1) + to_chat(user, "You start to add cables to the frame...") + if(do_after(user, 20, target = src) && state == SCREWED_CORE && C.use(5)) + to_chat(user, "You add cables to the frame.") + state = CABLED_CORE update_icon() - var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( loc ) - A.amount = 5 + else + to_chat(user, "You need five lengths of cable to wire the AI core!") + return + if(CABLED_CORE) + if(istype(P, /obj/item/stack/sheet/rglass)) + var/obj/item/stack/sheet/rglass/G = P + if(G.get_amount() >= 2) + playsound(loc, 'sound/items/deconstruct.ogg', 50, 1) + to_chat(user, "You start to put in the glass panel...") + if(do_after(user, 20, target = src) && state == CABLED_CORE && G.use(2)) + to_chat(user, "You put in the glass panel.") + state = GLASS_CORE + update_icon() + else + to_chat(user, "You need two sheets of reinforced glass to insert them into the AI core!") + return + + if(istype(P, /obj/item/aiModule/purge)) + laws.clear_inherent_laws() + to_chat(usr, "Law module applied.") + return + + if(istype(P, /obj/item/aiModule/freeform)) + var/obj/item/aiModule/freeform/M = P + laws.add_inherent_law(M.newFreeFormLaw) + to_chat(usr, "Added a freeform law.") + return + + if(istype(P, /obj/item/aiModule)) + var/obj/item/aiModule/M = P + if(!M.laws) + to_chat(usr, "This AI module can not be applied directly to AI cores.") + return + laws = M.laws + + if(istype(P, /obj/item/mmi) && !brain) + var/obj/item/mmi/M = P + if(!M.brainmob) + to_chat(user, "Sticking an empty [P] into the frame would sort of defeat the purpose.") + return + if(M.brainmob.stat == DEAD) + to_chat(user, "Sticking a dead [P] into the frame would sort of defeat the purpose.") return - if(istype(P, /obj/item/stack/sheet/rglass)) - var/obj/item/stack/sheet/rglass/G = P - if(G.get_amount() >= 2) - playsound(loc, 'sound/items/deconstruct.ogg', 50, 1) - to_chat(user, "You start to put in the glass panel...") - if(do_after(user, 20, target = src) && state == CABLED_CORE && G.use(2)) - to_chat(user, "You put in the glass panel.") - state = GLASS_CORE - update_icon() - else - to_chat(user, "You need two sheets of reinforced glass to insert them into the AI core!") + if(!M.brainmob.client) + to_chat(user, "Sticking an inactive [M.name] into the frame would sort of defeat the purpose.") return - if(istype(P, /obj/item/aiModule/purge)) - laws.clear_inherent_laws() - to_chat(usr, "Law module applied.") + if(jobban_isbanned(M.brainmob, "AI") || jobban_isbanned(M.brainmob, "nonhumandept")) + to_chat(user, "This [P] does not seem to fit.") return - if(istype(P, /obj/item/aiModule/freeform)) - var/obj/item/aiModule/freeform/M = P - laws.add_inherent_law(M.newFreeFormLaw) - to_chat(usr, "Added a freeform law.") + if(!M.brainmob.mind) + to_chat(user, "This [M.name] is mindless!") return - if(istype(P, /obj/item/aiModule)) - var/obj/item/aiModule/M = P - if(!M.laws) - to_chat(usr, "This AI module can not be applied directly to AI cores.") - return - laws = M.laws - - if(istype(P, /obj/item/mmi) && !brain) - var/obj/item/mmi/M = P - if(!M.brainmob) - to_chat(user, "Sticking an empty [P] into the frame would sort of defeat the purpose.") - return - if(M.brainmob.stat == DEAD) - to_chat(user, "Sticking a dead [P] into the frame would sort of defeat the purpose.") - return - - if(!M.brainmob.client) - to_chat(user, "Sticking an inactive [M.name] into the frame would sort of defeat the purpose.") - return - - if(jobban_isbanned(M.brainmob, "AI") || jobban_isbanned(M.brainmob, "nonhumandept")) - to_chat(user, "This [P] does not seem to fit.") - return - - if(!M.brainmob.mind) - to_chat(user, "This [M.name] is mindless!") - return - - if(istype(P, /obj/item/mmi/syndie)) - to_chat(user, "This MMI does not seem to fit!") - return - - if(!user.drop_item()) - return - - M.forceMove(src) - brain = M - to_chat(user, "You add [M.name] to the frame.") - update_icon() + if(istype(P, /obj/item/mmi/syndie)) + to_chat(user, "This MMI does not seem to fit!") return - if(iscrowbar(P) && brain) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You remove the brain.") - brain.forceMove(loc) - brain = null - update_icon() + if(!user.drop_item()) return - if(GLASS_CORE) - if(istype(P, /obj/item/crowbar)) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You remove the glass panel.") - state = CABLED_CORE - update_icon() - new /obj/item/stack/sheet/rglass(loc, 2) - return + M.forceMove(src) + brain = M + to_chat(user, "You add [M.name] to the frame.") + update_icon() + return - if(isscrewdriver(P)) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You connect the monitor.") - if(!brain) - var/open_for_latejoin = alert(user, "Would you like this core to be open for latejoining AIs?", "Latejoin", "Yes", "Yes", "No") == "Yes" - var/obj/structure/AIcore/deactivated/D = new(loc) - if(open_for_latejoin) - empty_playable_ai_cores += D - else - if(brain.brainmob.mind) - SSticker.mode.remove_cultist(brain.brainmob.mind, 1) - SSticker.mode.remove_revolutionary(brain.brainmob.mind, 1) - - var/mob/living/silicon/ai/A = new /mob/living/silicon/ai(loc, laws, brain) - if(A) //if there's no brain, the mob is deleted and a structure/AIcore is created - A.rename_self("AI", 1) - feedback_inc("cyborg_ais_created",1) - qdel(src) - - if(AI_READY_CORE) - if(istype(P, /obj/item/aicard)) - P.transfer_ai("INACTIVE", "AICARD", src, user) - return - - if(isscrewdriver(P)) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You disconnect the monitor.") - state = GLASS_CORE - update_icon() - return + if(AI_READY_CORE) + if(istype(P, /obj/item/aicard)) + P.transfer_ai("INACTIVE", "AICARD", src, user) + return return ..() +/obj/structure/AIcore/crowbar_act(mob/living/user, obj/item/I) + if(state !=CIRCUIT_CORE || state != GLASS_CORE || !(state == CABLED_CORE && brain)) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + switch(state) + if(CIRCUIT_CORE) + to_chat(user, "You remove the circuit board.") + state = EMPTY_CORE + circuit.forceMove(loc) + circuit = null + return + if(GLASS_CORE) + to_chat(user, "You remove the glass panel.") + state = CABLED_CORE + new /obj/item/stack/sheet/rglass(loc, 2) + return + if(CABLED_CORE) + if(brain) + to_chat(user, "You remove the brain.") + brain.forceMove(loc) + brain = null + update_icon() + +/obj/structure/AIcore/screwdriver_act(mob/living/user, obj/item/I) + if(!(state in list(SCREWED_CORE, CIRCUIT_CORE, GLASS_CORE, AI_READY_CORE))) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + switch(state) + if(SCREWED_CORE) + to_chat(user, "You unfasten the circuit board.") + state = CIRCUIT_CORE + if(CIRCUIT_CORE) + to_chat(user, "You screw the circuit board into place.") + state = SCREWED_CORE + if(GLASS_CORE) + to_chat(user, "You connect the monitor.") + if(!brain) + var/open_for_latejoin = alert(user, "Would you like this core to be open for latejoining AIs?", "Latejoin", "Yes", "Yes", "No") == "Yes" + var/obj/structure/AIcore/deactivated/D = new(loc) + if(open_for_latejoin) + empty_playable_ai_cores += D + else + if(brain.brainmob.mind) + SSticker.mode.remove_cultist(brain.brainmob.mind, 1) + SSticker.mode.remove_revolutionary(brain.brainmob.mind, 1) + + var/mob/living/silicon/ai/A = new /mob/living/silicon/ai(loc, laws, brain) + if(A) //if there's no brain, the mob is deleted and a structure/AIcore is created + A.rename_self("AI", 1) + feedback_inc("cyborg_ais_created",1) + qdel(src) + if(AI_READY_CORE) + to_chat(user, "You disconnect the monitor.") + state = GLASS_CORE + update_icon() + + +/obj/structure/AIcore/wirecutter_act(mob/living/user, obj/item/I) + if(state != CABLED_CORE) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(brain) + to_chat(user, "Get that [brain.name] out of there first!") + else + to_chat(user, "You remove the cables.") + state = SCREWED_CORE + update_icon() + var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( loc ) + A.amount = 5 + +/obj/structure/AIcore/wrench_act(mob/living/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + default_unfasten_wrench(user, I, 20) + /obj/structure/AIcore/update_icon() switch(state) if(EMPTY_CORE) @@ -237,6 +225,18 @@ new /obj/item/stack/sheet/plasteel(loc, 4) qdel(src) +/obj/structure/AIcore/welder_act(mob/user, obj/item/I) + if(!state) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_WELD_MESSAGE + if(I.use_tool(src, user, 20, volume = I.tool_volume)) + to_chat(user, "You deconstruct the frame.") + new /obj/item/stack/sheet/plasteel(drop_location(), 4) + qdel(src) + /obj/structure/AIcore/deactivated name = "inactive AI" icon_state = "ai-empty" @@ -305,4 +305,4 @@ atom/proc/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/ to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) installed and executed successfully. Local copy has been removed.") qdel(src) else //If for some reason you use an empty card on an empty AI terminal. - to_chat(user, "There is no AI loaded on this terminal!") \ No newline at end of file + to_chat(user, "There is no AI loaded on this terminal!") diff --git a/code/game/machinery/computer/aifixer.dm b/code/game/machinery/computer/aifixer.dm index e9cf036d3ca..0d715fe5759 100644 --- a/code/game/machinery/computer/aifixer.dm +++ b/code/game/machinery/computer/aifixer.dm @@ -1,147 +1,147 @@ -/obj/machinery/computer/aifixer - name = "\improper AI system integrity restorer" - icon = 'icons/obj/computer.dmi' - icon_keyboard = "rd_key" - icon_screen = "ai-fixer" - circuit = /obj/item/circuitboard/aifixer - req_access = list(access_captain, access_robotics, access_heads) - var/mob/living/silicon/ai/occupant = null - var/active = 0 - - light_color = LIGHT_COLOR_PURPLE - -/obj/machinery/computer/aifixer/attackby(I as obj, user as mob, params) - if(occupant && istype(I, /obj/item/screwdriver)) - if(stat & BROKEN) - ..() - if(stat & NOPOWER) - to_chat(user, "The screws on [name]'s screen won't budge.") - else - to_chat(user, "The screws on [name]'s screen won't budge and it emits a warning beep!.") - else - return ..() - -/obj/machinery/computer/aifixer/attack_ai(var/mob/user as mob) - ui_interact(user) - -/obj/machinery/computer/aifixer/attack_hand(var/mob/user as mob) - ui_interact(user) - -/obj/machinery/computer/aifixer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - - if(!ui) - ui = new(user, src, ui_key, "ai_fixer.tmpl", "AI System Integrity Restorer", 550, 500) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/computer/aifixer/ui_data(mob/user, datum/topic_state/state) - var/data[0] - if(occupant) - data["occupant"] = occupant.name - data["reference"] = "\ref[occupant]" - data["integrity"] = (occupant.health+100)/2 - data["stat"] = occupant.stat - data["active"] = active - data["wireless"] = occupant.control_disabled - data["radio"] = occupant.aiRadio.disabledAi - - var/laws[0] - for(var/datum/ai_law/law in occupant.laws.all_laws()) - laws.Add(list(list("law" = law.law, "number" = law.get_index()))) - - data["laws"] = laws - - return data - -/obj/machinery/computer/aifixer/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["fix"]) - active = 1 - while(occupant.health < 100) - occupant.adjustOxyLoss(-1, FALSE) - occupant.adjustFireLoss(-1, FALSE) - occupant.adjustToxLoss(-1, FALSE) - occupant.adjustBruteLoss(-1, FALSE) - occupant.updatehealth() - if(occupant.health >= 0 && occupant.stat == DEAD) - occupant.update_revive() - occupant.lying = 0 - update_icon() - sleep(10) - active = 0 - add_fingerprint(usr) - - if(href_list["wireless"]) - var/wireless = text2num(href_list["wireless"]) - if(wireless == 0 || wireless == 1) - occupant.control_disabled = wireless - - if(href_list["radio"]) - var/radio = text2num(href_list["radio"]) - if(radio == 0 || radio == 1) - occupant.aiRadio.disabledAi = radio - - SSnanoui.update_uis(src) - update_icon() - return - -/obj/machinery/computer/aifixer/update_icon() - ..() - if(stat & (NOPOWER|BROKEN)) - return - else - var/overlay_layer = LIGHTING_LAYER+0.2 // +0.1 from the default computer overlays - if(active) - overlays += image(icon,"ai-fixer-on",overlay_layer) - if(occupant) - switch(occupant.stat) - if(0) - overlays += image(icon,"ai-fixer-full",overlay_layer) - if(2) - overlays += image(icon,"ai-fixer-404",overlay_layer) - else - overlays += image(icon,"ai-fixer-empty",overlay_layer) - -/obj/machinery/computer/aifixer/transfer_ai(var/interaction, var/mob/user, var/mob/living/silicon/ai/AI, var/obj/item/aicard/card) - if(!..()) - return - //Downloading AI from card to terminal. - if(interaction == AI_TRANS_FROM_CARD) - if(stat & (NOPOWER|BROKEN)) - to_chat(user, "[src] is offline and cannot take an AI at this time!") - return - AI.loc = src - occupant = AI - AI.control_disabled = 1 - AI.aiRadio.disabledAi = 1 - to_chat(AI, "You have been uploaded to a stationary terminal. Sadly, there is no remote access from here.") - to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) installed and executed successfully. Local copy has been removed.") - update_icon() - - else //Uploading AI from terminal to card - if(occupant && !active) - to_chat(occupant, "You have been downloaded to a mobile storage device. Still no remote access.") - to_chat(user, "Transfer successful: [occupant.name] ([rand(1000,9999)].exe) removed from host terminal and stored within local memory.") - occupant.loc = card - occupant = null - update_icon() - else if(active) - to_chat(user, "ERROR: Reconstruction in progress.") - else if(!occupant) - to_chat(user, "ERROR: Unable to locate artificial intelligence.") - -/obj/machinery/computer/aifixer/Destroy() - if(occupant) - occupant.ghostize() - QDEL_NULL(occupant) - return ..() - -/obj/machinery/computer/aifixer/emp_act() - if(occupant) - occupant.ghostize() - QDEL_NULL(occupant) - else - ..() +/obj/machinery/computer/aifixer + name = "\improper AI system integrity restorer" + icon = 'icons/obj/computer.dmi' + icon_keyboard = "rd_key" + icon_screen = "ai-fixer" + circuit = /obj/item/circuitboard/aifixer + req_access = list(ACCESS_CAPTAIN, ACCESS_ROBOTICS, ACCESS_HEADS) + var/mob/living/silicon/ai/occupant = null + var/active = 0 + + light_color = LIGHT_COLOR_PURPLE + +/obj/machinery/computer/aifixer/attackby(I as obj, user as mob, params) + if(occupant && istype(I, /obj/item/screwdriver)) + if(stat & BROKEN) + ..() + if(stat & NOPOWER) + to_chat(user, "The screws on [name]'s screen won't budge.") + else + to_chat(user, "The screws on [name]'s screen won't budge and it emits a warning beep!.") + else + return ..() + +/obj/machinery/computer/aifixer/attack_ai(var/mob/user as mob) + ui_interact(user) + +/obj/machinery/computer/aifixer/attack_hand(var/mob/user as mob) + ui_interact(user) + +/obj/machinery/computer/aifixer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + + if(!ui) + ui = new(user, src, ui_key, "ai_fixer.tmpl", "AI System Integrity Restorer", 550, 500) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/computer/aifixer/ui_data(mob/user, datum/topic_state/state) + var/data[0] + if(occupant) + data["occupant"] = occupant.name + data["reference"] = "\ref[occupant]" + data["integrity"] = (occupant.health+100)/2 + data["stat"] = occupant.stat + data["active"] = active + data["wireless"] = occupant.control_disabled + data["radio"] = occupant.aiRadio.disabledAi + + var/laws[0] + for(var/datum/ai_law/law in occupant.laws.all_laws()) + laws.Add(list(list("law" = law.law, "number" = law.get_index()))) + + data["laws"] = laws + + return data + +/obj/machinery/computer/aifixer/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["fix"]) + active = 1 + while(occupant.health < 100) + occupant.adjustOxyLoss(-1, FALSE) + occupant.adjustFireLoss(-1, FALSE) + occupant.adjustToxLoss(-1, FALSE) + occupant.adjustBruteLoss(-1, FALSE) + occupant.updatehealth() + if(occupant.health >= 0 && occupant.stat == DEAD) + occupant.update_revive() + occupant.lying = 0 + update_icon() + sleep(10) + active = 0 + add_fingerprint(usr) + + if(href_list["wireless"]) + var/wireless = text2num(href_list["wireless"]) + if(wireless == 0 || wireless == 1) + occupant.control_disabled = wireless + + if(href_list["radio"]) + var/radio = text2num(href_list["radio"]) + if(radio == 0 || radio == 1) + occupant.aiRadio.disabledAi = radio + + SSnanoui.update_uis(src) + update_icon() + return + +/obj/machinery/computer/aifixer/update_icon() + ..() + if(stat & (NOPOWER|BROKEN)) + return + else + var/overlay_layer = LIGHTING_LAYER+0.2 // +0.1 from the default computer overlays + if(active) + overlays += image(icon,"ai-fixer-on",overlay_layer) + if(occupant) + switch(occupant.stat) + if(0) + overlays += image(icon,"ai-fixer-full",overlay_layer) + if(2) + overlays += image(icon,"ai-fixer-404",overlay_layer) + else + overlays += image(icon,"ai-fixer-empty",overlay_layer) + +/obj/machinery/computer/aifixer/transfer_ai(var/interaction, var/mob/user, var/mob/living/silicon/ai/AI, var/obj/item/aicard/card) + if(!..()) + return + //Downloading AI from card to terminal. + if(interaction == AI_TRANS_FROM_CARD) + if(stat & (NOPOWER|BROKEN)) + to_chat(user, "[src] is offline and cannot take an AI at this time!") + return + AI.loc = src + occupant = AI + AI.control_disabled = 1 + AI.aiRadio.disabledAi = 1 + to_chat(AI, "You have been uploaded to a stationary terminal. Sadly, there is no remote access from here.") + to_chat(user, "Transfer successful: [AI.name] ([rand(1000,9999)].exe) installed and executed successfully. Local copy has been removed.") + update_icon() + + else //Uploading AI from terminal to card + if(occupant && !active) + to_chat(occupant, "You have been downloaded to a mobile storage device. Still no remote access.") + to_chat(user, "Transfer successful: [occupant.name] ([rand(1000,9999)].exe) removed from host terminal and stored within local memory.") + occupant.loc = card + occupant = null + update_icon() + else if(active) + to_chat(user, "ERROR: Reconstruction in progress.") + else if(!occupant) + to_chat(user, "ERROR: Unable to locate artificial intelligence.") + +/obj/machinery/computer/aifixer/Destroy() + if(occupant) + occupant.ghostize() + QDEL_NULL(occupant) + return ..() + +/obj/machinery/computer/aifixer/emp_act() + if(occupant) + occupant.ghostize() + QDEL_NULL(occupant) + else + ..() diff --git a/code/game/machinery/computer/arcade.dm b/code/game/machinery/computer/arcade.dm index 28f847d81eb..20ff79041a9 100644 --- a/code/game/machinery/computer/arcade.dm +++ b/code/game/machinery/computer/arcade.dm @@ -1,1023 +1,1023 @@ -/obj/machinery/computer/arcade - name = "random arcade" - desc = "random arcade machine" - icon = 'icons/obj/computer.dmi' - icon_state = "arcade" - icon_keyboard = null - icon_screen = "invaders" - light_color = "#00FF00" - var/prize = /obj/item/stack/tickets - -/obj/machinery/computer/arcade/proc/Reset() - return - -/obj/machinery/computer/arcade/New() - ..() - if(!circuit) - var/choice = pick(subtypesof(/obj/machinery/computer/arcade)) - new choice(loc) - qdel(src) - return - Reset() - - -/obj/machinery/computer/arcade/proc/prizevend(var/score) - if(!contents.len) - var/prize_amount - if(score) - prize_amount = score - else - prize_amount = rand(1, 10) - new prize(get_turf(src), prize_amount) - else - var/atom/movable/prize = pick(contents) - prize.loc = get_turf(src) - -/obj/machinery/computer/arcade/emp_act(severity) - ..(severity) - if(stat & (NOPOWER|BROKEN)) - return - var/num_of_prizes = 0 - switch(severity) - if(1) - num_of_prizes = rand(1,4) - if(2) - num_of_prizes = rand(0,2) - for(var/i = num_of_prizes; i > 0; i--) - prizevend() - explosion(get_turf(src), -1, 0, 1+num_of_prizes, flame_range = 1+num_of_prizes) - - -/obj/machinery/computer/arcade/battle - name = "arcade machine" - desc = "Does not support Pinball." - icon = 'icons/obj/computer.dmi' - icon_state = "arcade" - circuit = /obj/item/circuitboard/arcade/battle - var/enemy_name = "Space Villian" - var/temp = "Winners Don't Use Spacedrugs" //Temporary message, for attack messages, etc - var/player_hp = 30 //Player health/attack points - var/player_mp = 10 - var/enemy_hp = 45 //Enemy health/attack points - var/enemy_mp = 20 - var/gameover = 0 - var/blocked = 0 //Player cannot attack/heal while set - var/turtle = 0 - -/obj/machinery/computer/arcade/battle/Reset() - var/name_action - var/name_part1 - var/name_part2 - - name_action = pick("Defeat ", "Annihilate ", "Save ", "Strike ", "Stop ", "Destroy ", "Robust ", "Romance ", "Pwn ", "Own ", "Ban ") - - name_part1 = pick("the Automatic ", "Farmer ", "Lord ", "Professor ", "the Cuban ", "the Evil ", "the Dread King ", "the Space ", "Lord ", "the Great ", "Duke ", "General ") - name_part2 = pick("Melonoid", "Murdertron", "Sorcerer", "Ruin", "Jeff", "Ectoplasm", "Crushulon", "Uhangoid", "Vhakoid", "Peteoid", "slime", "Griefer", "ERPer", "Lizard Man", "Unicorn", "Bloopers") - - enemy_name = replacetext((name_part1 + name_part2), "the ", "") - name = (name_action + name_part1 + name_part2) - -/obj/machinery/computer/arcade/battle/attack_hand(mob/user as mob) - if(..()) - return - user.set_machine(src) - var/dat = "Close" - dat += "

        [enemy_name]

        " - - dat += "

        [temp]

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

        Game Over

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

        OK...

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

        [title]

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

        Crew:

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

        Go Around Continue

        " - else - dat += "

        Continue

        " - dat += "

        Kill a crewmember

        " - dat += "

        Close

        " - else - dat = "

        The Orion Trail

        " - dat += "

        Experience the journey of your ancestors!



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

        Close

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

        [event]

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

        Continue

        " - eventdat += "

        Close

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

        Slow Down Continue

        " - eventdat += "

        Close

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

        Continue

        " - eventdat += "

        Close

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

        Use PartWait

        " - else - eventdat += "

        Wait

        " - eventdat += "

        Close

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

        Use PartWait

        " - else - eventdat += "

        Wait

        " - eventdat += "

        Close

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

        Use PartWait

        " - else - eventdat += "

        Wait

        " - eventdat += "

        Close

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

        Oh...

        " - eventdat += "

        Close

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

        Continue

        " - eventdat += "

        Close

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

        Kill a crewmember

        " - eventdat += "

        Risk it

        " - eventdat += "

        Close

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

        Continue

        " - eventdat += "

        Close

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

        Depart Spaceport

        " - eventdat += "

        Close

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

        Crew:

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

        Crew Management:

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

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

        " - else - eventdat += "

        Cant afford a new Crewmember

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

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

        " - else - eventdat += "

        Cant afford to sell a Crewmember

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

        Spare Parts:

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

        Buy Engine Parts (-5FU)

        " - else - eventdat += "

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

        Buy Hull Plates (-5FU)

        " - else - eventdat += "

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

        Buy Spare Electronics (-5FU)

        " - else - eventdat += "

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

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

        " - else - eventdat += "

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

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

        " - else - eventdat += "

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

        [enemy_name]

        " + + dat += "

        [temp]

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

        Game Over

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

        OK...

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

        [title]

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

        Crew:

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

        Go Around Continue

        " + else + dat += "

        Continue

        " + dat += "

        Kill a crewmember

        " + dat += "

        Close

        " + else + dat = "

        The Orion Trail

        " + dat += "

        Experience the journey of your ancestors!



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

        Close

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

        [event]

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

        Continue

        " + eventdat += "

        Close

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

        Slow Down Continue

        " + eventdat += "

        Close

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

        Continue

        " + eventdat += "

        Close

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

        Use PartWait

        " + else + eventdat += "

        Wait

        " + eventdat += "

        Close

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

        Use PartWait

        " + else + eventdat += "

        Wait

        " + eventdat += "

        Close

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

        Use PartWait

        " + else + eventdat += "

        Wait

        " + eventdat += "

        Close

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

        Oh...

        " + eventdat += "

        Close

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

        Continue

        " + eventdat += "

        Close

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

        Kill a crewmember

        " + eventdat += "

        Risk it

        " + eventdat += "

        Close

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

        Continue

        " + eventdat += "

        Close

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

        Depart Spaceport

        " + eventdat += "

        Close

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

        Crew:

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

        Crew Management:

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

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

        " + else + eventdat += "

        Cant afford a new Crewmember

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

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

        " + else + eventdat += "

        Cant afford to sell a Crewmember

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

        Spare Parts:

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

        Buy Engine Parts (-5FU)

        " + else + eventdat += "

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

        Buy Hull Plates (-5FU)

        " + else + eventdat += "

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

        Buy Spare Electronics (-5FU)

        " + else + eventdat += "

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

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

        " + else + eventdat += "

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

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

        " + else + eventdat += "

        Cant afford to Trade Food for Fuel= 1) //need to make sure we even have anyone to remove + removed = pick(safe2remove) + + if(removed) + if(lings_aboard && prob(40*lings_aboard)) //if there are 2 lings you're twice as likely to get one, obviously + lings_aboard = max(0,--lings_aboard) + settlers -= removed + alive-- + return removed + + +/obj/machinery/computer/arcade/orion_trail/proc/win() + playing = 0 + turns = 1 + atom_say("Congratulations, you made it to Orion!") + if(emagged) + new /obj/item/orion_ship(get_turf(src)) + message_admins("[key_name_admin(usr)] made it to Orion on an emagged machine and got an explosive toy ship.") + log_game("[key_name(usr)] made it to Orion on an emagged machine and got an explosive toy ship.") + else + var/score = alive + round(food/2) + round(fuel/5) + engine + hull + electronics - lings_aboard + prizevend(score) + emagged = 0 + name = "The Orion Trail" + desc = "Learn how our ancestors got to Orion, and have fun in the process!" + +/obj/machinery/computer/arcade/orion_trail/emag_act(mob/user) + if(!emagged) + to_chat(user, "You override the cheat code menu and skip to Cheat #[rand(1, 50)]: Realism Mode.") + name = "The Orion Trail: Realism Edition" + desc = "Learn how our ancestors got to Orion, and try not to die in the process!" + newgame() + emagged = 1 + +/mob/living/simple_animal/hostile/syndicate/ranged/orion + name = "spaceport security" + desc = "Premier corporate security forces for all spaceports found along the Orion Trail." + faction = list("orion") + loot = list() + del_on_death = TRUE + +/obj/item/orion_ship + name = "model settler ship" + desc = "A model spaceship, it looks like those used back in the day when travelling to Orion! It even has a miniature FX-293 reactor, which was renowned for its instability and tendency to explode..." + icon = 'icons/obj/toy.dmi' + icon_state = "ship" + w_class = WEIGHT_CLASS_SMALL + var/active = 0 //if the ship is on + +/obj/item/orion_ship/examine(mob/user) + . = ..() + if(in_range(user, src)) + if(!active) + . += "There's a little switch on the bottom. It's flipped down." + else + . += "There's a little switch on the bottom. It's flipped up." + +/obj/item/orion_ship/attack_self(mob/user) //Minibomb-level explosion. Should probably be more because of how hard it is to survive the machine! Also, just over a 5-second fuse + if(active) + return + + message_admins("[key_name_admin(usr)] primed an explosive Orion ship for detonation.") + log_game("[key_name(usr)] primed an explosive Orion ship for detonation.") + + to_chat(user, "You flip the switch on the underside of [src].") + active = 1 + visible_message("[src] softly beeps and whirs to life!") + playsound(src.loc, 'sound/machines/defib_saftyon.ogg', 25, 1) + atom_say("This is ship ID #[rand(1,1000)] to Orion Port Authority. We're coming in for landing, over.") + sleep(20) + visible_message("[src] begins to vibrate...") + atom_say("Uh, Port? Having some issues with our reactor, could you check it out? Over.") + sleep(30) + atom_say("Oh, God! Code Eight! CODE EIGHT! IT'S GONNA BL-") + playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 25, 1) + sleep(3.6) + visible_message("[src] explodes!") + explosion(src.loc, 1,2,4, flame_range = 3) + qdel(src) + + +#undef ORION_TRAIL_WINTURN +#undef ORION_TRAIL_RAIDERS +#undef ORION_TRAIL_FLUX +#undef ORION_TRAIL_ILLNESS +#undef ORION_TRAIL_BREAKDOWN +#undef ORION_TRAIL_LING +#undef ORION_TRAIL_LING_ATTACK +#undef ORION_TRAIL_MALFUNCTION +#undef ORION_TRAIL_COLLISION +#undef ORION_TRAIL_SPACEPORT +#undef ORION_TRAIL_BLACKHOLE diff --git a/code/game/machinery/computer/atmos_control.dm b/code/game/machinery/computer/atmos_control.dm index 84ab146589e..7b07cda3bbb 100644 --- a/code/game/machinery/computer/atmos_control.dm +++ b/code/game/machinery/computer/atmos_control.dm @@ -7,7 +7,7 @@ density = 1 anchored = 1.0 circuit = /obj/item/circuitboard/atmoscontrol - req_access = list(access_atmospherics) + req_access = list(ACCESS_ATMOSPHERICS) var/list/monitored_alarm_ids = null var/datum/nano_module/atmos_control/atmos_control diff --git a/code/game/machinery/computer/brigcells.dm b/code/game/machinery/computer/brigcells.dm index d322e99ae4b..378c10d9783 100644 --- a/code/game/machinery/computer/brigcells.dm +++ b/code/game/machinery/computer/brigcells.dm @@ -8,7 +8,7 @@ active_power_usage = 500 circuit = /obj/item/circuitboard/brigcells light_color = LIGHT_COLOR_DARKRED - req_access = list(access_brig) + req_access = list(ACCESS_BRIG) /obj/machinery/computer/brigcells/attack_ai(mob/user) attack_hand(user) diff --git a/code/game/machinery/computer/buildandrepair.dm b/code/game/machinery/computer/buildandrepair.dm index 9e8bc08f9b2..2f8321b57bb 100644 --- a/code/game/machinery/computer/buildandrepair.dm +++ b/code/game/machinery/computer/buildandrepair.dm @@ -1,697 +1,687 @@ -/obj/structure/computerframe - density = 1 - anchored = 0 - name = "computer frame" - icon = 'icons/obj/stock_parts.dmi' - icon_state = "0" - max_integrity = 100 - var/state = 0 - var/obj/item/circuitboard/circuit = null - var/base_mineral = /obj/item/stack/sheet/metal - -/obj/structure/computerframe/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - drop_computer_parts() - return ..() // will qdel the frame - -/obj/structure/computerframe/obj_break(damage_flag) - deconstruct() - -/obj/structure/computerframe/proc/drop_computer_parts() - new base_mineral(loc, 5) - if(circuit) - circuit.forceMove(loc) - circuit = null - if(state >= 3) - var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( loc ) - A.amount = 5 - if(state >= 4) - new /obj/item/stack/sheet/glass(loc, 2) - -/obj/item/circuitboard - density = 0 - anchored = 0 - w_class = WEIGHT_CLASS_SMALL - name = "circuit board" - icon = 'icons/obj/module.dmi' - icon_state = "id_mod" - item_state = "electronic" - origin_tech = "programming=2" - materials = list(MAT_GLASS=200) - var/id = null - var/frequency = null - var/build_path = null - var/board_type = "computer" - var/list/req_components = null - var/powernet = null - var/list/records = null - var/frame_desc = null - var/contain_parts = 1 - toolspeed = 1 - usesound = 'sound/items/deconstruct.ogg' - -/obj/item/circuitboard/computer - board_type = "computer" - -/obj/item/circuitboard/machine - board_type = "machine" - -/obj/item/circuitboard/examine(mob/user) - . = ..() - if(LAZYLEN(req_components)) - var/list/nice_list = list() - for(var/B in req_components) - var/atom/A = B - if(!ispath(A)) - continue - nice_list += list("[req_components[A]] [initial(A.name)]") - . += "Required components: [english_list(nice_list)]." - -/obj/item/circuitboard/message_monitor - name = "Circuit board (Message Monitor)" - build_path = /obj/machinery/computer/message_monitor - origin_tech = "programming=2" -/obj/item/circuitboard/camera - name = "Circuit board (Camera Monitor)" - build_path = /obj/machinery/computer/security - origin_tech = "programming=2;combat=2" - -/obj/item/circuitboard/camera/telescreen - name = "Circuit board (Telescreen)" - build_path = /obj/machinery/computer/security/telescreen -/obj/item/circuitboard/camera/telescreen/entertainment - name = "Circuit board (Entertainment Monitor)" - build_path = /obj/machinery/computer/security/telescreen/entertainment -/obj/item/circuitboard/camera/wooden_tv - name = "Circuit board (Wooden TV)" - build_path = /obj/machinery/computer/security/wooden_tv -/obj/item/circuitboard/camera/mining - name = "Circuit board (Outpost Camera Monitor)" - build_path = /obj/machinery/computer/security/mining -/obj/item/circuitboard/camera/engineering - name = "Circuit board (Engineering Camera Monitor)" - build_path = /obj/machinery/computer/security/engineering - - -/obj/item/circuitboard/xenobiology - name = "Circuit board (Xenobiology Console)" - build_path = /obj/machinery/computer/camera_advanced/xenobio - origin_tech = "programming=3;biotech=3" -/obj/item/circuitboard/aicore - name = "Circuit board (AI Core)" - origin_tech = "programming=3" - board_type = "other" -/obj/item/circuitboard/aiupload - name = "Circuit board (AI Upload)" - build_path = /obj/machinery/computer/aiupload - origin_tech = "programming=4;engineering=4" -/obj/item/circuitboard/borgupload - name = "Circuit board (Cyborg Upload)" - build_path = /obj/machinery/computer/borgupload - origin_tech = "programming=4;engineering=4" -/obj/item/circuitboard/med_data - name = "Circuit board (Medical Records)" - build_path = /obj/machinery/computer/med_data - origin_tech = "programming=2;biotech=2" -/obj/item/circuitboard/pandemic - name = "circuit board (PanD.E.M.I.C. 2200)" - build_path = /obj/machinery/computer/pandemic - origin_tech = "programming=2;biotech=2" -/obj/item/circuitboard/scan_consolenew - name = "Circuit board (DNA Machine)" - build_path = /obj/machinery/computer/scan_consolenew - origin_tech = "programming=2;biotech=2" -/obj/item/circuitboard/communications - name = "Circuit board (Communications Console)" - build_path = /obj/machinery/computer/communications - origin_tech = "programming=3;magnets=3" -/obj/item/circuitboard/card - name = "Circuit board (ID Computer)" - build_path = /obj/machinery/computer/card - origin_tech = "programming=3" -/obj/item/circuitboard/card/minor - name = "Circuit board (Dept ID Computer)" - build_path = /obj/machinery/computer/card/minor - var/target_dept = TARGET_DEPT_GENERIC -/obj/item/circuitboard/card/minor/hos - name = "Circuit board (Sec ID Computer)" - build_path = /obj/machinery/computer/card/minor/hos - target_dept = TARGET_DEPT_SEC -/obj/item/circuitboard/card/minor/cmo - name = "Circuit board (Medical ID Computer)" - build_path = /obj/machinery/computer/card/minor/cmo - target_dept = TARGET_DEPT_MED -/obj/item/circuitboard/card/minor/rd - name = "Circuit board (Science ID Computer)" - build_path = /obj/machinery/computer/card/minor/rd - target_dept = TARGET_DEPT_SCI -/obj/item/circuitboard/card/minor/ce - name = "Circuit board (Engineering ID Computer)" - build_path = /obj/machinery/computer/card/minor/ce - target_dept = TARGET_DEPT_ENG -/obj/item/circuitboard/card/centcom - name = "Circuit board (CentComm ID Computer)" - build_path = /obj/machinery/computer/card/centcom -/obj/item/circuitboard/teleporter - name = "Circuit board (Teleporter Console)" - build_path = /obj/machinery/computer/teleporter - origin_tech = "programming=3;bluespace=3;plasmatech=3" -/obj/item/circuitboard/secure_data - name = "Circuit board (Security Records)" - build_path = /obj/machinery/computer/secure_data - origin_tech = "programming=2;combat=2" -/obj/item/circuitboard/skills - name = "Circuit board (Employment Records)" - build_path = /obj/machinery/computer/skills -/obj/item/circuitboard/stationalert_engineering - name = "Circuit Board (Station Alert Console (Engineering))" - build_path = /obj/machinery/computer/station_alert -/obj/item/circuitboard/stationalert_security - name = "Circuit Board (Station Alert Console (Security))" - build_path = /obj/machinery/computer/station_alert -/obj/item/circuitboard/stationalert_all - name = "Circuit Board (Station Alert Console (All))" - build_path = /obj/machinery/computer/station_alert/all -/obj/item/circuitboard/atmos_alert - name = "Circuit Board (Atmospheric Alert Computer)" - build_path = /obj/machinery/computer/atmos_alert -/obj/item/circuitboard/atmoscontrol - name = "Circuit Board (Central Atmospherics Computer)" - build_path = /obj/machinery/computer/atmoscontrol -/obj/item/circuitboard/air_management - name = "Circuit board (Atmospheric Monitor)" - build_path = /obj/machinery/computer/general_air_control -/obj/item/circuitboard/injector_control - name = "Circuit board (Injector Control)" - build_path = /obj/machinery/computer/general_air_control/fuel_injection -/obj/item/circuitboard/pod - name = "Circuit board (Massdriver Control)" - build_path = /obj/machinery/computer/pod -/obj/item/circuitboard/pod/deathsquad - name = "Circuit board (Deathsquad Massdriver control)" - build_path = /obj/machinery/computer/pod/deathsquad -/obj/item/circuitboard/robotics - name = "Circuit board (Robotics Control Console)" - build_path = /obj/machinery/computer/robotics - origin_tech = "programming=3" -/obj/item/circuitboard/drone_control - name = "Circuit board (Drone Control)" - build_path = /obj/machinery/computer/drone_control - origin_tech = "programming=3" -/obj/item/circuitboard/cloning - name = "Circuit board (Cloning Machine Console)" - build_path = /obj/machinery/computer/cloning - origin_tech = "programming=2;biotech=2" -/obj/item/circuitboard/arcade/battle - name = "circuit board (Arcade Battle)" - build_path = /obj/machinery/computer/arcade/battle - origin_tech = "programming=1" -/obj/item/circuitboard/arcade/orion_trail - name = "circuit board (Orion Trail)" - build_path = /obj/machinery/computer/arcade/orion_trail - origin_tech = "programming=1" -/obj/item/circuitboard/solar_control - name = "Circuit board (Solar Control)" - build_path = /obj/machinery/power/solar_control - origin_tech = "programming=2;powerstorage=2" -/obj/item/circuitboard/powermonitor - name = "Circuit board (Power Monitor)" - build_path = /obj/machinery/computer/monitor - origin_tech = "programming=2;powerstorage=2" -/obj/item/circuitboard/powermonitor/secret - name = "Circuit board (Outdated Power Monitor)" - build_path = /obj/machinery/computer/monitor/secret - origin_tech = "programming=2;powerstorage=2" -/obj/item/circuitboard/olddoor - name = "Circuit board (DoorMex)" - build_path = /obj/machinery/computer/pod/old -/obj/item/circuitboard/syndicatedoor - name = "Circuit board (ProComp Executive)" - build_path = /obj/machinery/computer/pod/old/syndicate -/obj/item/circuitboard/swfdoor - name = "Circuit board (Magix)" - build_path = /obj/machinery/computer/pod/old/swf -/obj/item/circuitboard/prisoner - name = "Circuit board (Prisoner Management)" - build_path = /obj/machinery/computer/prisoner -/obj/item/circuitboard/brigcells - name = "Circuit board (Brig Cell Control)" - build_path = /obj/machinery/computer/brigcells - - -// RD console circuits, so that {de,re}constructing one of the special consoles doesn't ruin everything forever -/obj/item/circuitboard/rdconsole - name = "Circuit Board (RD Console)" - desc = "Swipe a Scientist level ID or higher to reconfigure." - build_path = /obj/machinery/computer/rdconsole/core - req_access = list(access_tox) // This is for adjusting the type of computer we're building - in case something messes up the pre-existing robotics or mechanics consoles - var/access_types = list("R&D Core", "Robotics", "E.X.P.E.R.I-MENTOR", "Mechanics", "Public") - id = 1 -/obj/item/circuitboard/rdconsole/robotics - name = "Circuit Board (RD Console - Robotics)" - build_path = /obj/machinery/computer/rdconsole/robotics - id = 2 -/obj/item/circuitboard/rdconsole/experiment - name = "Circuit Board (RD Console - E.X.P.E.R.I-MENTOR)" - build_path = /obj/machinery/computer/rdconsole/experiment - id = 3 -/obj/item/circuitboard/rdconsole/mechanics - name = "Circuit Board (RD Console - Mechanics)" - build_path = /obj/machinery/computer/rdconsole/mechanics - id = 4 -/obj/item/circuitboard/rdconsole/public - name = "Circuit Board (RD Console - Public)" - build_path = /obj/machinery/computer/rdconsole/public - id = 5 - - -/obj/item/circuitboard/mecha_control - name = "Circuit Board (Exosuit Control Console)" - build_path = /obj/machinery/computer/mecha -/obj/item/circuitboard/pod_locater - name = "Circuit Board (Pod Location Console)" - build_path = /obj/machinery/computer/podtracker -/obj/item/circuitboard/rdservercontrol - name = "Circuit Board (RD Server Control)" - build_path = /obj/machinery/computer/rdservercontrol -/obj/item/circuitboard/crew - name = "Circuit board (Crew Monitoring Computer)" - build_path = /obj/machinery/computer/crew - origin_tech = "programming=2;biotech=2" -/obj/item/circuitboard/mech_bay_power_console - name = "Circuit board (Mech Bay Power Control Console)" - build_path = /obj/machinery/computer/mech_bay_power_console - origin_tech = "programming=3;powerstorage=3" -/obj/item/circuitboard/ordercomp - name = "Circuit board (Supply Ordering Console)" - build_path = /obj/machinery/computer/ordercomp - origin_tech = "programming=3" -/obj/item/circuitboard/supplycomp - name = "Circuit board (Supply Shuttle Console)" - build_path = /obj/machinery/computer/supplycomp - origin_tech = "programming=3" - var/contraband_enabled = 0 - -/obj/item/circuitboard/operating - name = "Circuit board (Operating Computer)" - build_path = /obj/machinery/computer/operating - origin_tech = "programming=2;biotech=3" -/obj/item/circuitboard/comm_monitor - name = "Circuit board (Telecommunications Monitor)" - build_path = /obj/machinery/computer/telecomms/monitor - origin_tech = "programming=3;magnets=3;bluespace=2" -/obj/item/circuitboard/comm_server - name = "Circuit board (Telecommunications Server Monitor)" - build_path = /obj/machinery/computer/telecomms/server - origin_tech = "programming=3;magnets=3;bluespace=2" -/obj/item/circuitboard/comm_traffic - name = "Circuitboard (Telecommunications Traffic Control)" - build_path = /obj/machinery/computer/telecomms/traffic - origin_tech = "programming=3;magnets=3;bluespace=2" - - -/obj/item/circuitboard/shuttle - name = "circuit board (Shuttle)" - build_path = /obj/machinery/computer/shuttle - var/shuttleId - var/possible_destinations = "" - -/obj/item/circuitboard/labor_shuttle - name = "circuit Board (Labor Shuttle)" - build_path = /obj/machinery/computer/shuttle/labor -/obj/item/circuitboard/labor_shuttle/one_way - name = "circuit Board (Prisoner Shuttle Console)" - build_path = /obj/machinery/computer/shuttle/labor/one_way -/obj/item/circuitboard/ferry - name = "circuit Board (Transport Ferry)" - build_path = /obj/machinery/computer/shuttle/ferry -/obj/item/circuitboard/ferry/request - name = "circuit Board (Transport Ferry Console)" - build_path = /obj/machinery/computer/shuttle/ferry/request -/obj/item/circuitboard/mining_shuttle - name = "circuit Board (Mining Shuttle)" - build_path = /obj/machinery/computer/shuttle/mining -/obj/item/circuitboard/white_ship - name = "circuit Board (White Ship)" - build_path = /obj/machinery/computer/shuttle/white_ship -/obj/item/circuitboard/shuttle/syndicate - name = "circuit board (Syndicate Shuttle)" - build_path = /obj/machinery/computer/shuttle/syndicate -/obj/item/circuitboard/shuttle/syndicate/recall - name = "circuit board (Syndicate Shuttle Recall Terminal)" - build_path = /obj/machinery/computer/shuttle/syndicate/recall -/obj/item/circuitboard/shuttle/syndicate/drop_pod - name = "circuit board (Syndicate Drop Pod)" - build_path = /obj/machinery/computer/shuttle/syndicate/drop_pod -/obj/item/circuitboard/shuttle/golem_ship - name = "circuit Board (Golem Ship)" - build_path = /obj/machinery/computer/shuttle/golem_ship - -/obj/item/circuitboard/HolodeckControl - name = "Circuit board (Holodeck Control)" - build_path = /obj/machinery/computer/HolodeckControl - origin_tech = "programming=4" -/obj/item/circuitboard/aifixer - name = "Circuit board (AI Integrity Restorer)" - build_path = /obj/machinery/computer/aifixer - origin_tech = "programming=2;biotech=2" -/obj/item/circuitboard/area_atmos - name = "Circuit board (Area Air Control)" - build_path = /obj/machinery/computer/area_atmos - origin_tech = "programming=2" -/obj/item/circuitboard/telesci_console - name = "Circuit board (Telepad Control Console)" - build_path = /obj/machinery/computer/telescience - origin_tech = "programming=3;bluespace=3;plasmatech=4" - -/obj/item/circuitboard/atmos_automation - name = "Circuit board (Atmospherics Automation)" - build_path = /obj/machinery/computer/general_air_control/atmos_automation -/obj/item/circuitboard/large_tank_control - name = "Circuit board (Atmospheric Tank Control)" - build_path = /obj/machinery/computer/general_air_control/large_tank_control - origin_tech = "programming=2;engineering=3;materials=2" - -/obj/item/circuitboard/turbine_computer - name = "circuit board (Turbine Computer)" - build_path = /obj/machinery/computer/turbine_computer - origin_tech = "programming=4;engineering=4;powerstorage=4" - -/obj/item/circuitboard/HONKputer - name = "Circuit board (HONKputer)" - build_path = /obj/machinery/computer/HONKputer - origin_tech = "programming=2" - icon = 'icons/obj/machines/HONKputer.dmi' - icon_state = "bananium_board" - board_type = "honkcomputer" - - -/obj/item/circuitboard/supplycomp/attackby(obj/item/I as obj, mob/user as mob, params) - if(istype(I,/obj/item/multitool)) - var/catastasis = contraband_enabled - var/opposite_catastasis - if(catastasis) - opposite_catastasis = "STANDARD" - catastasis = "BROAD" - else - opposite_catastasis = "BROAD" - catastasis = "STANDARD" - - switch( alert("Current receiver spectrum is set to: [catastasis]","Multitool-Circuitboard interface","Switch to [opposite_catastasis]","Cancel") ) - //switch( alert("Current receiver spectrum is set to: " {(contraband_enabled) ? ("BROAD") : ("STANDARD")} , "Multitool-Circuitboard interface" , "Switch to " {(contraband_enabled) ? ("STANDARD") : ("BROAD")}, "Cancel") ) - if("Switch to STANDARD","Switch to BROAD") - contraband_enabled = !contraband_enabled - - if("Cancel") - return - else - to_chat(user, "DERP! BUG! Report this (And what you were doing to cause it) to Agouri") - return - return ..() - -/obj/item/circuitboard/rdconsole/attackby(obj/item/I as obj, mob/user as mob, params) - if(istype(I,/obj/item/card/id)||istype(I, /obj/item/pda)) - if(allowed(user)) - user.visible_message("\the [user] waves [user.p_their()] ID past the [src]'s access protocol scanner.", "You swipe your ID past the [src]'s access protocol scanner.") - var/console_choice = input(user, "What do you want to configure the access to?", "Access Modification", "R&D Core") as null|anything in access_types - if(console_choice == null) - return - switch(console_choice) - if("R&D Core") - name = "Circuit Board (RD Console)" - build_path = /obj/machinery/computer/rdconsole/core - id = 1 - if("Robotics") - name = "Circuit Board (RD Console - Robotics)" - build_path = /obj/machinery/computer/rdconsole/robotics - id = 2 - if("E.X.P.E.R.I-MENTOR") - name = "Circuit Board (RD Console - E.X.P.E.R.I-MENTOR)" - build_path = /obj/machinery/computer/rdconsole/experiment - id = 3 - if("Mechanics") - name = "Circuit Board (RD Console - Mechanics)" - build_path = /obj/machinery/computer/rdconsole/mechanics - id = 4 - if("Public") - name = "Circuit Board (RD Console - Public)" - build_path = /obj/machinery/computer/rdconsole/public - id = 5 - - to_chat(user, "Access protocols set to [console_choice].") - else - to_chat(user, "Access Denied") - return - return ..() - -/obj/structure/computerframe/attackby(obj/item/P as obj, mob/user as mob, params) - switch(state) - if(0) - if(istype(P, /obj/item/wrench)) - playsound(loc, P.usesound, 50, 1) - if(do_after(user, 20 * P.toolspeed, target = src)) - to_chat(user, "You wrench the frame into place.") - anchored = 1 - state = 1 - return - if(istype(P, /obj/item/weldingtool)) - var/obj/item/weldingtool/WT = P - if(!WT.remove_fuel(0, user)) - to_chat(user, "The welding tool must be on to complete this task.") - return - playsound(loc, WT.usesound, 50, 1) - if(do_after(user, 20 * WT.toolspeed, target = src)) - if(!src || !WT.isOn()) return - to_chat(user, "You deconstruct the frame.") - deconstruct(TRUE) - return - if(1) - if(istype(P, /obj/item/wrench)) - playsound(loc, P.usesound, 50, 1) - if(do_after(user, 20 * P.toolspeed, target = src)) - to_chat(user, "You unfasten the frame.") - anchored = 0 - state = 0 - return - if(istype(P, /obj/item/circuitboard) && !circuit) - var/obj/item/circuitboard/B = P - if(B.board_type == "computer") - playsound(loc, B.usesound, 50, 1) - to_chat(user, "You place the circuit board inside the frame.") - icon_state = "1" - circuit = P - user.drop_item() - P.loc = src - else - to_chat(user, "This frame does not accept circuit boards of this type!") - return - if(istype(P, /obj/item/screwdriver) && circuit) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You screw the circuit board into place.") - state = 2 - icon_state = "2" - return - if(istype(P, /obj/item/crowbar) && circuit) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You remove the circuit board.") - state = 1 - icon_state = "0" - circuit.loc = loc - circuit = null - return - if(2) - if(istype(P, /obj/item/screwdriver) && circuit) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You unfasten the circuit board.") - state = 1 - icon_state = "1" - return - if(istype(P, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = P - if(C.amount >= 5) - playsound(loc, C.usesound, 50, 1) - to_chat(user, "You start to add cables to the frame.") - if(do_after(user, 20 * C.toolspeed, target = src)) - if(state == 2 && C.amount >= 5 && C.use(5)) - to_chat(user, "You add cables to the frame.") - state = 3 - icon_state = "3" - else - to_chat(user, "At some point during construction you lost some cable. Make sure you have five lengths before trying again.") - return - else - to_chat(user, "You need five lengths of cable to wire the frame.") - return - if(3) - if(istype(P, /obj/item/wirecutters)) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You remove the cables.") - state = 2 - icon_state = "2" - var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( loc ) - A.amount = 5 - return - if(istype(P, /obj/item/stack/sheet/glass)) - var/obj/item/stack/sheet/glass/G = P - if(G.amount >= 2) - playsound(loc, G.usesound, 50, 1) - to_chat(user, "You start to add the glass panel to the frame.") - if(do_after(user, 20 * G.toolspeed, target = src)) - if(state == 3 && G.amount >= 2 && G.use(2)) - to_chat(user, "You put in the glass panel.") - state = 4 - icon_state = "4" - else - to_chat(user, "At some point during construction you lost some glass. Make sure you have two sheets before trying again.") - return - else - to_chat(user, "You need two sheets of glass for this.") - return - if(4) - if(istype(P, /obj/item/crowbar)) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You remove the glass panel.") - state = 3 - icon_state = "3" - new /obj/item/stack/sheet/glass(loc, 2) - return - if(istype(P, /obj/item/screwdriver)) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You connect the monitor.") - var/B = new circuit.build_path (loc) - if(circuit.powernet) B:powernet = circuit.powernet - if(circuit.id) B:id = circuit.id - if(circuit.records) B:records = circuit.records - if(circuit.frequency) B:frequency = circuit.frequency - if(istype(circuit,/obj/item/circuitboard/supplycomp)) - var/obj/machinery/computer/supplycomp/SC = B - var/obj/item/circuitboard/supplycomp/C = circuit - SC.can_order_contraband = C.contraband_enabled - qdel(src) - return - if(user.a_intent == INTENT_HARM) - return ..() - - - -/obj/structure/computerframe/HONKputer - name = "Bananium Computer-frame" - icon = 'icons/obj/machines/HONKputer.dmi' - base_mineral = /obj/item/stack/sheet/mineral/bananium - -/obj/structure/computerframe/HONKputer/attackby(obj/item/P as obj, mob/user as mob, params) - switch(state) - if(0) - if(istype(P, /obj/item/wrench)) - playsound(loc, P.usesound, 50, 1) - if(do_after(user, 20, target = src)) - to_chat(user, "You wrench the frame into place.") - anchored = 1 - state = 1 - if(istype(P, /obj/item/weldingtool)) - var/obj/item/weldingtool/WT = P - if(!WT.remove_fuel(0, user)) - to_chat(user, "The welding tool must be on to complete this task.") - return - playsound(loc, WT.usesound, 50, 1) - if(do_after(user, 20 * WT.toolspeed, target = src)) - if(!src || !WT.isOn()) return - to_chat(user, "You deconstruct the frame.") - deconstruct(TRUE) - return - if(1) - if(istype(P, /obj/item/wrench)) - playsound(loc, P.usesound, 50, 1) - if(do_after(user, 20 * P.toolspeed, target = src)) - to_chat(user, "You unfasten the frame.") - anchored = 0 - state = 0 - if(istype(P, /obj/item/circuitboard) && !circuit) - var/obj/item/circuitboard/B = P - if(B.board_type == "honkcomputer") - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You place the circuit board inside the frame.") - icon_state = "1" - circuit = P - user.drop_item() - P.loc = src - else - to_chat(user, "This frame does not accept circuit boards of this type!") - if(istype(P, /obj/item/screwdriver) && circuit) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You screw the circuit board into place.") - state = 2 - icon_state = "2" - if(istype(P, /obj/item/crowbar) && circuit) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You remove the circuit board.") - state = 1 - icon_state = "0" - circuit.loc = loc - circuit = null - return - if(2) - if(istype(P, /obj/item/screwdriver) && circuit) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You unfasten the circuit board.") - state = 1 - icon_state = "1" - if(istype(P, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = P - if(C.amount >= 5) - playsound(loc, C.usesound, 50, 1) - to_chat(user, "You start to add cables to the frame.") - if(do_after(user, 20 * C.toolspeed, target = src)) - if(state == 2 && C.amount >= 5 && C.use(5)) - to_chat(user, "You add cables to the frame.") - state = 3 - icon_state = "3" - else - to_chat(user, "At some point during construction you lost some cable. Make sure you have five lengths before trying again.") - return - else - to_chat(user, "You need five lengths of cable to wire the frame.") - return - if(3) - if(istype(P, /obj/item/wirecutters)) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You remove the cables.") - state = 2 - icon_state = "2" - var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( loc ) - A.amount = 5 - - if(istype(P, /obj/item/stack/sheet/glass)) - var/obj/item/stack/sheet/glass/G = P - if(G.amount >= 2) - playsound(loc, G.usesound, 50, 1) - to_chat(user, "You start to add the glass panel to the frame.") - if(do_after(user, 20 * G.toolspeed, target = src)) - if(state == 3 && G.amount >= 2 && G.use(2)) - to_chat(user, "You put in the glass panel.") - state = 4 - icon_state = "4" - else - to_chat(user, "At some point during construction you lost some glass. Make sure you have two sheets before trying again.") - return - else - to_chat(user, "You need two sheets of glass for this.") - return - if(4) - if(istype(P, /obj/item/crowbar)) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You remove the glass panel.") - state = 3 - icon_state = "3" - new /obj/item/stack/sheet/glass(loc, 2) - if(istype(P, /obj/item/screwdriver)) - playsound(loc, P.usesound, 50, 1) - to_chat(user, "You connect the monitor.") - var/B = new circuit.build_path (loc) - if(circuit.powernet) B:powernet = circuit.powernet - if(circuit.id) B:id = circuit.id - if(circuit.records) B:records = circuit.records - if(circuit.frequency) B:frequency = circuit.frequency - qdel(src) - return - return ..() \ No newline at end of file +/obj/structure/computerframe + density = 1 + anchored = 0 + name = "computer frame" + icon = 'icons/obj/stock_parts.dmi' + icon_state = "0" + max_integrity = 100 + var/state = 0 + var/obj/item/circuitboard/circuit = null + var/base_mineral = /obj/item/stack/sheet/metal + +/obj/structure/computerframe/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + drop_computer_parts() + return ..() // will qdel the frame + +/obj/structure/computerframe/obj_break(damage_flag) + deconstruct() + +/obj/structure/computerframe/proc/drop_computer_parts() + new base_mineral(loc, 5) + if(circuit) + circuit.forceMove(loc) + circuit = null + if(state >= 3) + var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( loc ) + A.amount = 5 + if(state >= 4) + new /obj/item/stack/sheet/glass(loc, 2) + +/obj/item/circuitboard + density = 0 + anchored = 0 + w_class = WEIGHT_CLASS_SMALL + name = "circuit board" + icon = 'icons/obj/module.dmi' + icon_state = "id_mod" + item_state = "electronic" + origin_tech = "programming=2" + materials = list(MAT_GLASS=200) + var/id = null + var/frequency = null + var/build_path = null + var/board_type = "computer" + var/list/req_components = null + var/powernet = null + var/list/records = null + var/frame_desc = null + var/contain_parts = 1 + toolspeed = 1 + usesound = 'sound/items/deconstruct.ogg' + +/obj/item/circuitboard/computer + board_type = "computer" + +/obj/item/circuitboard/machine + board_type = "machine" + +/obj/item/circuitboard/examine(mob/user) + . = ..() + if(LAZYLEN(req_components)) + var/list/nice_list = list() + for(var/B in req_components) + var/atom/A = B + if(!ispath(A)) + continue + nice_list += list("[req_components[A]] [initial(A.name)]") + . += "Required components: [english_list(nice_list)]." + +/obj/item/circuitboard/message_monitor + name = "Circuit board (Message Monitor)" + build_path = /obj/machinery/computer/message_monitor + origin_tech = "programming=2" +/obj/item/circuitboard/camera + name = "Circuit board (Camera Monitor)" + build_path = /obj/machinery/computer/security + origin_tech = "programming=2;combat=2" + +/obj/item/circuitboard/camera/telescreen + name = "Circuit board (Telescreen)" + build_path = /obj/machinery/computer/security/telescreen +/obj/item/circuitboard/camera/telescreen/entertainment + name = "Circuit board (Entertainment Monitor)" + build_path = /obj/machinery/computer/security/telescreen/entertainment +/obj/item/circuitboard/camera/wooden_tv + name = "Circuit board (Wooden TV)" + build_path = /obj/machinery/computer/security/wooden_tv +/obj/item/circuitboard/camera/mining + name = "Circuit board (Outpost Camera Monitor)" + build_path = /obj/machinery/computer/security/mining +/obj/item/circuitboard/camera/engineering + name = "Circuit board (Engineering Camera Monitor)" + build_path = /obj/machinery/computer/security/engineering + + +/obj/item/circuitboard/xenobiology + name = "Circuit board (Xenobiology Console)" + build_path = /obj/machinery/computer/camera_advanced/xenobio + origin_tech = "programming=3;biotech=3" +/obj/item/circuitboard/aicore + name = "Circuit board (AI Core)" + origin_tech = "programming=3" + board_type = "other" +/obj/item/circuitboard/aiupload + name = "Circuit board (AI Upload)" + build_path = /obj/machinery/computer/aiupload + origin_tech = "programming=4;engineering=4" +/obj/item/circuitboard/borgupload + name = "Circuit board (Cyborg Upload)" + build_path = /obj/machinery/computer/borgupload + origin_tech = "programming=4;engineering=4" +/obj/item/circuitboard/med_data + name = "Circuit board (Medical Records)" + build_path = /obj/machinery/computer/med_data + origin_tech = "programming=2;biotech=2" +/obj/item/circuitboard/pandemic + name = "circuit board (PanD.E.M.I.C. 2200)" + build_path = /obj/machinery/computer/pandemic + origin_tech = "programming=2;biotech=2" +/obj/item/circuitboard/scan_consolenew + name = "Circuit board (DNA Machine)" + build_path = /obj/machinery/computer/scan_consolenew + origin_tech = "programming=2;biotech=2" +/obj/item/circuitboard/communications + name = "Circuit board (Communications Console)" + build_path = /obj/machinery/computer/communications + origin_tech = "programming=3;magnets=3" +/obj/item/circuitboard/card + name = "Circuit board (ID Computer)" + build_path = /obj/machinery/computer/card + origin_tech = "programming=3" +/obj/item/circuitboard/card/minor + name = "Circuit board (Dept ID Computer)" + build_path = /obj/machinery/computer/card/minor + var/target_dept = TARGET_DEPT_GENERIC +/obj/item/circuitboard/card/minor/hos + name = "Circuit board (Sec ID Computer)" + build_path = /obj/machinery/computer/card/minor/hos + target_dept = TARGET_DEPT_SEC +/obj/item/circuitboard/card/minor/cmo + name = "Circuit board (Medical ID Computer)" + build_path = /obj/machinery/computer/card/minor/cmo + target_dept = TARGET_DEPT_MED +/obj/item/circuitboard/card/minor/rd + name = "Circuit board (Science ID Computer)" + build_path = /obj/machinery/computer/card/minor/rd + target_dept = TARGET_DEPT_SCI +/obj/item/circuitboard/card/minor/ce + name = "Circuit board (Engineering ID Computer)" + build_path = /obj/machinery/computer/card/minor/ce + target_dept = TARGET_DEPT_ENG +/obj/item/circuitboard/card/centcom + name = "Circuit board (CentComm ID Computer)" + build_path = /obj/machinery/computer/card/centcom +/obj/item/circuitboard/teleporter + name = "Circuit board (Teleporter Console)" + build_path = /obj/machinery/computer/teleporter + origin_tech = "programming=3;bluespace=3;plasmatech=3" +/obj/item/circuitboard/secure_data + name = "Circuit board (Security Records)" + build_path = /obj/machinery/computer/secure_data + origin_tech = "programming=2;combat=2" +/obj/item/circuitboard/skills + name = "Circuit board (Employment Records)" + build_path = /obj/machinery/computer/skills +/obj/item/circuitboard/stationalert_engineering + name = "Circuit Board (Station Alert Console (Engineering))" + build_path = /obj/machinery/computer/station_alert +/obj/item/circuitboard/stationalert_security + name = "Circuit Board (Station Alert Console (Security))" + build_path = /obj/machinery/computer/station_alert +/obj/item/circuitboard/stationalert_all + name = "Circuit Board (Station Alert Console (All))" + build_path = /obj/machinery/computer/station_alert/all +/obj/item/circuitboard/atmos_alert + name = "Circuit Board (Atmospheric Alert Computer)" + build_path = /obj/machinery/computer/atmos_alert +/obj/item/circuitboard/atmoscontrol + name = "Circuit Board (Central Atmospherics Computer)" + build_path = /obj/machinery/computer/atmoscontrol +/obj/item/circuitboard/air_management + name = "Circuit board (Atmospheric Monitor)" + build_path = /obj/machinery/computer/general_air_control +/obj/item/circuitboard/injector_control + name = "Circuit board (Injector Control)" + build_path = /obj/machinery/computer/general_air_control/fuel_injection +/obj/item/circuitboard/pod + name = "Circuit board (Massdriver Control)" + build_path = /obj/machinery/computer/pod +/obj/item/circuitboard/pod/deathsquad + name = "Circuit board (Deathsquad Massdriver control)" + build_path = /obj/machinery/computer/pod/deathsquad +/obj/item/circuitboard/robotics + name = "Circuit board (Robotics Control Console)" + build_path = /obj/machinery/computer/robotics + origin_tech = "programming=3" +/obj/item/circuitboard/drone_control + name = "Circuit board (Drone Control)" + build_path = /obj/machinery/computer/drone_control + origin_tech = "programming=3" +/obj/item/circuitboard/cloning + name = "Circuit board (Cloning Machine Console)" + build_path = /obj/machinery/computer/cloning + origin_tech = "programming=2;biotech=2" +/obj/item/circuitboard/arcade/battle + name = "circuit board (Arcade Battle)" + build_path = /obj/machinery/computer/arcade/battle + origin_tech = "programming=1" +/obj/item/circuitboard/arcade/orion_trail + name = "circuit board (Orion Trail)" + build_path = /obj/machinery/computer/arcade/orion_trail + origin_tech = "programming=1" +/obj/item/circuitboard/solar_control + name = "Circuit board (Solar Control)" + build_path = /obj/machinery/power/solar_control + origin_tech = "programming=2;powerstorage=2" +/obj/item/circuitboard/powermonitor + name = "Circuit board (Power Monitor)" + build_path = /obj/machinery/computer/monitor + origin_tech = "programming=2;powerstorage=2" +/obj/item/circuitboard/powermonitor/secret + name = "Circuit board (Outdated Power Monitor)" + build_path = /obj/machinery/computer/monitor/secret + origin_tech = "programming=2;powerstorage=2" +/obj/item/circuitboard/olddoor + name = "Circuit board (DoorMex)" + build_path = /obj/machinery/computer/pod/old +/obj/item/circuitboard/syndicatedoor + name = "Circuit board (ProComp Executive)" + build_path = /obj/machinery/computer/pod/old/syndicate +/obj/item/circuitboard/swfdoor + name = "Circuit board (Magix)" + build_path = /obj/machinery/computer/pod/old/swf +/obj/item/circuitboard/prisoner + name = "Circuit board (Prisoner Management)" + build_path = /obj/machinery/computer/prisoner +/obj/item/circuitboard/brigcells + name = "Circuit board (Brig Cell Control)" + build_path = /obj/machinery/computer/brigcells + + +// RD console circuits, so that {de,re}constructing one of the special consoles doesn't ruin everything forever +/obj/item/circuitboard/rdconsole + name = "Circuit Board (RD Console)" + desc = "Swipe a Scientist level ID or higher to reconfigure." + build_path = /obj/machinery/computer/rdconsole/core + req_access = list(ACCESS_TOX) // This is for adjusting the type of computer we're building - in case something messes up the pre-existing robotics or mechanics consoles + var/access_types = list("R&D Core", "Robotics", "E.X.P.E.R.I-MENTOR", "Mechanics", "Public") + id = 1 +/obj/item/circuitboard/rdconsole/robotics + name = "Circuit Board (RD Console - Robotics)" + build_path = /obj/machinery/computer/rdconsole/robotics + id = 2 +/obj/item/circuitboard/rdconsole/experiment + name = "Circuit Board (RD Console - E.X.P.E.R.I-MENTOR)" + build_path = /obj/machinery/computer/rdconsole/experiment + id = 3 +/obj/item/circuitboard/rdconsole/mechanics + name = "Circuit Board (RD Console - Mechanics)" + build_path = /obj/machinery/computer/rdconsole/mechanics + id = 4 +/obj/item/circuitboard/rdconsole/public + name = "Circuit Board (RD Console - Public)" + build_path = /obj/machinery/computer/rdconsole/public + id = 5 + + +/obj/item/circuitboard/mecha_control + name = "Circuit Board (Exosuit Control Console)" + build_path = /obj/machinery/computer/mecha +/obj/item/circuitboard/pod_locater + name = "Circuit Board (Pod Location Console)" + build_path = /obj/machinery/computer/podtracker +/obj/item/circuitboard/rdservercontrol + name = "Circuit Board (RD Server Control)" + build_path = /obj/machinery/computer/rdservercontrol +/obj/item/circuitboard/crew + name = "Circuit board (Crew Monitoring Computer)" + build_path = /obj/machinery/computer/crew + origin_tech = "programming=2;biotech=2" +/obj/item/circuitboard/mech_bay_power_console + name = "Circuit board (Mech Bay Power Control Console)" + build_path = /obj/machinery/computer/mech_bay_power_console + origin_tech = "programming=3;powerstorage=3" +/obj/item/circuitboard/ordercomp + name = "Circuit board (Supply Ordering Console)" + build_path = /obj/machinery/computer/ordercomp + origin_tech = "programming=3" +/obj/item/circuitboard/supplycomp + name = "Circuit board (Supply Shuttle Console)" + build_path = /obj/machinery/computer/supplycomp + origin_tech = "programming=3" + var/contraband_enabled = 0 + +/obj/item/circuitboard/operating + name = "Circuit board (Operating Computer)" + build_path = /obj/machinery/computer/operating + origin_tech = "programming=2;biotech=3" +/obj/item/circuitboard/comm_monitor + name = "Circuit board (Telecommunications Monitor)" + build_path = /obj/machinery/computer/telecomms/monitor + origin_tech = "programming=3;magnets=3;bluespace=2" +/obj/item/circuitboard/comm_server + name = "Circuit board (Telecommunications Server Monitor)" + build_path = /obj/machinery/computer/telecomms/server + origin_tech = "programming=3;magnets=3;bluespace=2" +/obj/item/circuitboard/comm_traffic + name = "Circuitboard (Telecommunications Traffic Control)" + build_path = /obj/machinery/computer/telecomms/traffic + origin_tech = "programming=3;magnets=3;bluespace=2" + + +/obj/item/circuitboard/shuttle + name = "circuit board (Shuttle)" + build_path = /obj/machinery/computer/shuttle + var/shuttleId + var/possible_destinations = "" + +/obj/item/circuitboard/labor_shuttle + name = "circuit Board (Labor Shuttle)" + build_path = /obj/machinery/computer/shuttle/labor +/obj/item/circuitboard/labor_shuttle/one_way + name = "circuit Board (Prisoner Shuttle Console)" + build_path = /obj/machinery/computer/shuttle/labor/one_way +/obj/item/circuitboard/ferry + name = "circuit Board (Transport Ferry)" + build_path = /obj/machinery/computer/shuttle/ferry +/obj/item/circuitboard/ferry/request + name = "circuit Board (Transport Ferry Console)" + build_path = /obj/machinery/computer/shuttle/ferry/request +/obj/item/circuitboard/mining_shuttle + name = "circuit Board (Mining Shuttle)" + build_path = /obj/machinery/computer/shuttle/mining +/obj/item/circuitboard/white_ship + name = "circuit Board (White Ship)" + build_path = /obj/machinery/computer/shuttle/white_ship +/obj/item/circuitboard/shuttle/syndicate + name = "circuit board (Syndicate Shuttle)" + build_path = /obj/machinery/computer/shuttle/syndicate +/obj/item/circuitboard/shuttle/syndicate/recall + name = "circuit board (Syndicate Shuttle Recall Terminal)" + build_path = /obj/machinery/computer/shuttle/syndicate/recall +/obj/item/circuitboard/shuttle/syndicate/drop_pod + name = "circuit board (Syndicate Drop Pod)" + build_path = /obj/machinery/computer/shuttle/syndicate/drop_pod +/obj/item/circuitboard/shuttle/golem_ship + name = "circuit Board (Golem Ship)" + build_path = /obj/machinery/computer/shuttle/golem_ship + +/obj/item/circuitboard/HolodeckControl + name = "Circuit board (Holodeck Control)" + build_path = /obj/machinery/computer/HolodeckControl + origin_tech = "programming=4" +/obj/item/circuitboard/aifixer + name = "Circuit board (AI Integrity Restorer)" + build_path = /obj/machinery/computer/aifixer + origin_tech = "programming=2;biotech=2" +/obj/item/circuitboard/area_atmos + name = "Circuit board (Area Air Control)" + build_path = /obj/machinery/computer/area_atmos + origin_tech = "programming=2" +/obj/item/circuitboard/telesci_console + name = "Circuit board (Telepad Control Console)" + build_path = /obj/machinery/computer/telescience + origin_tech = "programming=3;bluespace=3;plasmatech=4" + +/obj/item/circuitboard/atmos_automation + name = "Circuit board (Atmospherics Automation)" + build_path = /obj/machinery/computer/general_air_control/atmos_automation +/obj/item/circuitboard/large_tank_control + name = "Circuit board (Atmospheric Tank Control)" + build_path = /obj/machinery/computer/general_air_control/large_tank_control + origin_tech = "programming=2;engineering=3;materials=2" + +/obj/item/circuitboard/turbine_computer + name = "circuit board (Turbine Computer)" + build_path = /obj/machinery/computer/turbine_computer + origin_tech = "programming=4;engineering=4;powerstorage=4" + +/obj/item/circuitboard/HONKputer + name = "Circuit board (HONKputer)" + build_path = /obj/machinery/computer/HONKputer + origin_tech = "programming=2" + icon = 'icons/obj/machines/HONKputer.dmi' + icon_state = "bananium_board" + board_type = "honkcomputer" + + +/obj/item/circuitboard/supplycomp/attackby(obj/item/I as obj, mob/user as mob, params) + if(istype(I,/obj/item/multitool)) + var/catastasis = contraband_enabled + var/opposite_catastasis + if(catastasis) + opposite_catastasis = "STANDARD" + catastasis = "BROAD" + else + opposite_catastasis = "BROAD" + catastasis = "STANDARD" + + switch( alert("Current receiver spectrum is set to: [catastasis]","Multitool-Circuitboard interface","Switch to [opposite_catastasis]","Cancel") ) + //switch( alert("Current receiver spectrum is set to: " {(contraband_enabled) ? ("BROAD") : ("STANDARD")} , "Multitool-Circuitboard interface" , "Switch to " {(contraband_enabled) ? ("STANDARD") : ("BROAD")}, "Cancel") ) + if("Switch to STANDARD","Switch to BROAD") + contraband_enabled = !contraband_enabled + + if("Cancel") + return + else + to_chat(user, "DERP! BUG! Report this (And what you were doing to cause it) to Agouri") + return + return ..() + +/obj/item/circuitboard/rdconsole/attackby(obj/item/I as obj, mob/user as mob, params) + if(istype(I,/obj/item/card/id)||istype(I, /obj/item/pda)) + if(allowed(user)) + user.visible_message("\the [user] waves [user.p_their()] ID past the [src]'s access protocol scanner.", "You swipe your ID past the [src]'s access protocol scanner.") + var/console_choice = input(user, "What do you want to configure the access to?", "Access Modification", "R&D Core") as null|anything in access_types + if(console_choice == null) + return + switch(console_choice) + if("R&D Core") + name = "Circuit Board (RD Console)" + build_path = /obj/machinery/computer/rdconsole/core + id = 1 + if("Robotics") + name = "Circuit Board (RD Console - Robotics)" + build_path = /obj/machinery/computer/rdconsole/robotics + id = 2 + if("E.X.P.E.R.I-MENTOR") + name = "Circuit Board (RD Console - E.X.P.E.R.I-MENTOR)" + build_path = /obj/machinery/computer/rdconsole/experiment + id = 3 + if("Mechanics") + name = "Circuit Board (RD Console - Mechanics)" + build_path = /obj/machinery/computer/rdconsole/mechanics + id = 4 + if("Public") + name = "Circuit Board (RD Console - Public)" + build_path = /obj/machinery/computer/rdconsole/public + id = 5 + + to_chat(user, "Access protocols set to [console_choice].") + else + to_chat(user, "Access Denied") + return + return ..() + +/obj/structure/computerframe/attackby(obj/item/P as obj, mob/user as mob, params) + switch(state) + if(0) + if(istype(P, /obj/item/wrench)) + playsound(loc, P.usesound, 50, 1) + if(do_after(user, 20 * P.toolspeed, target = src)) + to_chat(user, "You wrench the frame into place.") + anchored = 1 + state = 1 + return + if(1) + if(istype(P, /obj/item/wrench)) + playsound(loc, P.usesound, 50, 1) + if(do_after(user, 20 * P.toolspeed, target = src)) + to_chat(user, "You unfasten the frame.") + anchored = 0 + state = 0 + return + if(istype(P, /obj/item/circuitboard) && !circuit) + var/obj/item/circuitboard/B = P + if(B.board_type == "computer") + playsound(loc, B.usesound, 50, 1) + to_chat(user, "You place the circuit board inside the frame.") + icon_state = "1" + circuit = P + user.drop_item() + P.loc = src + else + to_chat(user, "This frame does not accept circuit boards of this type!") + return + if(istype(P, /obj/item/screwdriver) && circuit) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You screw the circuit board into place.") + state = 2 + icon_state = "2" + return + if(istype(P, /obj/item/crowbar) && circuit) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You remove the circuit board.") + state = 1 + icon_state = "0" + circuit.loc = loc + circuit = null + return + if(2) + if(istype(P, /obj/item/screwdriver) && circuit) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You unfasten the circuit board.") + state = 1 + icon_state = "1" + return + if(istype(P, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = P + if(C.amount >= 5) + playsound(loc, C.usesound, 50, 1) + to_chat(user, "You start to add cables to the frame.") + if(do_after(user, 20 * C.toolspeed, target = src)) + if(state == 2 && C.amount >= 5 && C.use(5)) + to_chat(user, "You add cables to the frame.") + state = 3 + icon_state = "3" + else + to_chat(user, "At some point during construction you lost some cable. Make sure you have five lengths before trying again.") + return + else + to_chat(user, "You need five lengths of cable to wire the frame.") + return + if(3) + if(istype(P, /obj/item/wirecutters)) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You remove the cables.") + state = 2 + icon_state = "2" + var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( loc ) + A.amount = 5 + return + if(istype(P, /obj/item/stack/sheet/glass)) + var/obj/item/stack/sheet/glass/G = P + if(G.amount >= 2) + playsound(loc, G.usesound, 50, 1) + to_chat(user, "You start to add the glass panel to the frame.") + if(do_after(user, 20 * G.toolspeed, target = src)) + if(state == 3 && G.amount >= 2 && G.use(2)) + to_chat(user, "You put in the glass panel.") + state = 4 + icon_state = "4" + else + to_chat(user, "At some point during construction you lost some glass. Make sure you have two sheets before trying again.") + return + else + to_chat(user, "You need two sheets of glass for this.") + return + if(4) + if(istype(P, /obj/item/crowbar)) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You remove the glass panel.") + state = 3 + icon_state = "3" + new /obj/item/stack/sheet/glass(loc, 2) + return + if(istype(P, /obj/item/screwdriver)) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You connect the monitor.") + var/B = new circuit.build_path (loc) + if(circuit.powernet) B:powernet = circuit.powernet + if(circuit.id) B:id = circuit.id + if(circuit.records) B:records = circuit.records + if(circuit.frequency) B:frequency = circuit.frequency + if(istype(circuit,/obj/item/circuitboard/supplycomp)) + var/obj/machinery/computer/supplycomp/SC = B + var/obj/item/circuitboard/supplycomp/C = circuit + SC.can_order_contraband = C.contraband_enabled + qdel(src) + return + if(user.a_intent == INTENT_HARM) + return ..() + + +/obj/structure/computerframe/welder_act(mob/user, obj/item/I) + if(state) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_SLICING_MESSAGE + if(I.use_tool(src, user, 50, volume = I.tool_volume) && !state) + to_chat(user, "You deconstruct [src].") + deconstruct(TRUE) + + + +/obj/structure/computerframe/HONKputer + name = "Bananium Computer-frame" + icon = 'icons/obj/machines/HONKputer.dmi' + base_mineral = /obj/item/stack/sheet/mineral/bananium + +/obj/structure/computerframe/HONKputer/attackby(obj/item/P as obj, mob/user as mob, params) + switch(state) + if(0) + if(istype(P, /obj/item/wrench)) + playsound(loc, P.usesound, 50, 1) + if(do_after(user, 20, target = src)) + to_chat(user, "You wrench the frame into place.") + anchored = 1 + state = 1 + if(1) + if(istype(P, /obj/item/wrench)) + playsound(loc, P.usesound, 50, 1) + if(do_after(user, 20 * P.toolspeed, target = src)) + to_chat(user, "You unfasten the frame.") + anchored = 0 + state = 0 + if(istype(P, /obj/item/circuitboard) && !circuit) + var/obj/item/circuitboard/B = P + if(B.board_type == "honkcomputer") + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You place the circuit board inside the frame.") + icon_state = "1" + circuit = P + user.drop_item() + P.loc = src + else + to_chat(user, "This frame does not accept circuit boards of this type!") + if(istype(P, /obj/item/screwdriver) && circuit) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You screw the circuit board into place.") + state = 2 + icon_state = "2" + if(istype(P, /obj/item/crowbar) && circuit) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You remove the circuit board.") + state = 1 + icon_state = "0" + circuit.loc = loc + circuit = null + return + if(2) + if(istype(P, /obj/item/screwdriver) && circuit) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You unfasten the circuit board.") + state = 1 + icon_state = "1" + if(istype(P, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = P + if(C.amount >= 5) + playsound(loc, C.usesound, 50, 1) + to_chat(user, "You start to add cables to the frame.") + if(do_after(user, 20 * C.toolspeed, target = src)) + if(state == 2 && C.amount >= 5 && C.use(5)) + to_chat(user, "You add cables to the frame.") + state = 3 + icon_state = "3" + else + to_chat(user, "At some point during construction you lost some cable. Make sure you have five lengths before trying again.") + return + else + to_chat(user, "You need five lengths of cable to wire the frame.") + return + if(3) + if(istype(P, /obj/item/wirecutters)) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You remove the cables.") + state = 2 + icon_state = "2" + var/obj/item/stack/cable_coil/A = new /obj/item/stack/cable_coil( loc ) + A.amount = 5 + + if(istype(P, /obj/item/stack/sheet/glass)) + var/obj/item/stack/sheet/glass/G = P + if(G.amount >= 2) + playsound(loc, G.usesound, 50, 1) + to_chat(user, "You start to add the glass panel to the frame.") + if(do_after(user, 20 * G.toolspeed, target = src)) + if(state == 3 && G.amount >= 2 && G.use(2)) + to_chat(user, "You put in the glass panel.") + state = 4 + icon_state = "4" + else + to_chat(user, "At some point during construction you lost some glass. Make sure you have two sheets before trying again.") + return + else + to_chat(user, "You need two sheets of glass for this.") + return + if(4) + if(istype(P, /obj/item/crowbar)) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You remove the glass panel.") + state = 3 + icon_state = "3" + new /obj/item/stack/sheet/glass(loc, 2) + if(istype(P, /obj/item/screwdriver)) + playsound(loc, P.usesound, 50, 1) + to_chat(user, "You connect the monitor.") + var/B = new circuit.build_path (loc) + if(circuit.powernet) B:powernet = circuit.powernet + if(circuit.id) B:id = circuit.id + if(circuit.records) B:records = circuit.records + if(circuit.frequency) B:frequency = circuit.frequency + qdel(src) + return + return ..() diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm index 45f390082a1..f67158f82b0 100644 --- a/code/game/machinery/computer/camera.dm +++ b/code/game/machinery/computer/camera.dm @@ -1,356 +1,356 @@ -/obj/machinery/computer/security - name = "security camera console" - desc = "Used to access the various cameras networks on the station." - - icon_keyboard = "security_key" - icon_screen = "cameras" - light_color = LIGHT_COLOR_RED - circuit = /obj/item/circuitboard/camera - - var/mapping = 0 // For the overview file (overview.dm), not used on this page - - var/list/network = list() - var/list/available_networks = list() - var/list/watchers = list() //who's using the console, associated with the camera they're on. - -/obj/machinery/computer/security/New() // Lists existing networks and their required access. Format: available_networks[] = list() - generate_network_access() - ..() - -/obj/machinery/computer/security/proc/generate_network_access() - available_networks["SS13"] = list(access_hos,access_captain) - available_networks["Telecomms"] = list(access_hos,access_captain) - available_networks["Research Outpost"] = list(access_rd,access_hos,access_captain) - available_networks["Mining Outpost"] = list(access_qm,access_hop,access_hos,access_captain) - available_networks["Research"] = list(access_rd,access_hos,access_captain) - available_networks["Prison"] = list(access_hos,access_captain) - available_networks["Labor Camp"] = list(access_hos,access_captain) - available_networks["Interrogation"] = list(access_hos,access_captain) - available_networks["Atmosphere Alarms"] = list(access_ce,access_hos,access_captain) - available_networks["Fire Alarms"] = list(access_ce,access_hos,access_captain) - available_networks["Power Alarms"] = list(access_ce,access_hos,access_captain) - available_networks["Supermatter"] = list(access_ce,access_hos,access_captain) - available_networks["MiniSat"] = list(access_rd,access_hos,access_captain) - available_networks["Singularity"] = list(access_ce,access_hos,access_captain) - available_networks["Anomaly Isolation"] = list(access_rd,access_hos,access_captain) - available_networks["Toxins"] = list(access_rd,access_hos,access_captain) - available_networks["Telepad"] = list(access_rd,access_hos,access_captain) - available_networks["TestChamber"] = list(access_rd,access_hos,access_captain) - available_networks["ERT"] = list(access_cent_specops_commander,access_cent_commander) - available_networks["CentComm"] = list(access_cent_security,access_cent_commander) - available_networks["Thunderdome"] = list(access_cent_thunder,access_cent_commander) - -/obj/machinery/computer/security/Destroy() - if(watchers.len) - for(var/mob/M in watchers) - M.unset_machine() //to properly reset the view of the users if the console is deleted. - return ..() - -/obj/machinery/computer/security/proc/isCameraFarAway(obj/machinery/camera/C) - var/turf/consoleturf = get_turf(src) - var/turf/cameraturf = get_turf(C) - if((is_away_level(cameraturf.z) || is_away_level(consoleturf.z)) && !atoms_share_level(cameraturf, consoleturf)) //can only recieve away mission cameras on away missions - return TRUE - -/obj/machinery/computer/security/check_eye(mob/user) - if(!(user in watchers)) - user.unset_machine() - return - if(!watchers[user]) - user.unset_machine() - return - var/obj/machinery/camera/C = watchers[user] - if(isCameraFarAway(C)) - user.unset_machine() - return - if(!can_access_camera(C, user)) - user.unset_machine() - return - return 1 - -/obj/machinery/computer/security/on_unset_machine(mob/user) - watchers.Remove(user) - user.reset_perspective(null) - -/obj/machinery/computer/security/attack_hand(mob/user) - if(stat || ..()) - user.unset_machine() - return - - ui_interact(user) - -/obj/machinery/computer/security/telescreen/attackby(obj/item/I, mob/user, params) - if(ismultitool(I)) - var/direction = input(user, "Which direction?", "Select direction!") as null|anything in list("North", "East", "South", "West", "Centre") - if(!direction || !Adjacent(user)) - return - pixel_x = 0 - pixel_y = 0 - switch(direction) - if("North") - pixel_y = 32 - if("East") - pixel_x = 32 - if("South") - pixel_y = -32 - if("West") - pixel_x = -32 - else - return ..() - -/obj/machinery/computer/security/emag_act(user as mob) - if(!emagged) - emagged = 1 - to_chat(user, "You have authorized full network access!") - attack_hand(user) - else - attack_hand(user) - -/obj/machinery/computer/security/proc/get_user_access(mob/user) - var/list/access = list() - - if(emagged) - access = get_all_accesses() // Assume captain level access when emagged - else if(ishuman(user)) - access = user.get_access() - else if((isAI(user) || isrobot(user)) && CanUseTopic(user, default_state) == STATUS_INTERACTIVE) - access = get_all_accesses() // Assume captain level access when AI - else if(user.can_admin_interact()) - access = get_all_accesses() - return access - -/obj/machinery/computer/security/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "sec_camera.tmpl", "Camera Console", 900, 800) - - // adding a template with the key "mapContent" enables the map ui functionality - ui.add_template("mapContent", "sec_camera_map_content.tmpl") - // adding a template with the key "mapHeader" replaces the map header content - ui.add_template("mapHeader", "sec_camera_map_header.tmpl") - - ui.open() - -/obj/machinery/computer/security/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - - var/list/cameras = list() - for(var/obj/machinery/camera/C in cameranet.cameras) - if(isCameraFarAway(C)) - continue - if(!can_access_camera(C, user)) - continue - - cameras[++cameras.len] = C.nano_structure() - - for(var/i = cameras.len, i > 0, i--) //based off /proc/camera_sort, sorts cameras alphabetically for the UI - for(var/j = 1 to i - 1) - var/a = cameras[j] - var/b = cameras[j + 1] - if(sorttext(a["name"], b["name"]) < 0) - cameras.Swap(j, j + 1) - - data["cameras"] = cameras - - var/list/access = get_user_access(user) - if(emagged) - data["emagged"] = 1 - - var/list/networks_list = list() - // Loop through the ID's permission, and check which networks the ID has access to. - for(var/net in available_networks) // Loop through networks. - for(var/req in available_networks[net]) // Loop through access levels of the networks. - if(req in access) - if(net in network) // Checks if the network is currently active. - networks_list.Add(list(list("name" = net, "active" = 1))) - else - networks_list.Add(list(list("name" = net, "active" = 0))) - break - - if(networks_list.len) - data["networks"] = networks_list - - data["current"] = null - if(watchers[user]) - var/obj/machinery/camera/watched = watchers[user] - data["current"] = watched.nano_structure() - - return data - -/obj/machinery/computer/security/Topic(href, href_list) - if(..()) - usr.unset_machine() - return 1 - - if(href_list["switchTo"]) - var/obj/machinery/camera/C = locate(href_list["switchTo"]) in cameranet.cameras - if(!C) - return 1 - - switch_to_camera(usr, C) - - else if(href_list["reset"]) - usr.unset_machine() - - else if(href_list["activate"]) // Activate: enable or disable networks - var/net = href_list["activate"] // Network to be enabled or disabled. - var/active = href_list["active"] // Is the network currently active. - var/list/access = get_user_access(usr) - for(var/a in available_networks[net]) - if(a in access) // Re-check for authorization. - if(text2num(active) == 1) - network -= net - break - else - network += net - break - - SSnanoui.update_uis(src) - -// Check if camera is accessible when jumping -/obj/machinery/computer/security/proc/can_access_camera(var/obj/machinery/camera/C, var/mob/M) - if(CanUseTopic(M, default_state) != STATUS_INTERACTIVE || M.incapacitated() || !M.has_vision()) - return 0 - - if(isrobot(M)) - var/list/viewing = viewers(src) - if(!viewing.Find(M)) - return 0 - - if(isAI(M)) - var/mob/living/silicon/ai/A = M - if(!A.is_in_chassis()) - return 0 - - if(!issilicon(M) && !Adjacent(M)) - return 0 - - var/list/shared_networks = network & C.network - if(!shared_networks.len || !C.can_use()) - return 0 - - return 1 - -// Switching to cameras -/obj/machinery/computer/security/proc/switch_to_camera(var/mob/user, var/obj/machinery/camera/C) - if(!can_access_camera(C, user)) - user.unset_machine() - return 1 - - if(isAI(user)) - var/mob/living/silicon/ai/A = user - A.eyeobj.setLoc(get_turf(C)) - A.client.eye = A.eyeobj - else - user.reset_perspective(C) - watchers[user] = C - use_power(50) - -//Camera control: moving. -/obj/machinery/computer/security/proc/jump_on_click(var/mob/user, var/A) - if(user.machine != src) - return - - var/obj/machinery/camera/jump_to - - if(istype(A, /obj/machinery/camera)) - jump_to = A - - else if(ismob(A)) - if(ishuman(A)) - var/mob/living/carbon/human/H = A - jump_to = locate() in H.head - else if(isrobot(A)) - var/mob/living/silicon/robot/R = A - jump_to = R.camera - - else if(isobj(A)) - var/obj/O = A - jump_to = locate() in O - - else if(isturf(A)) - var/best_dist = INFINITY - for(var/obj/machinery/camera/camera in get_area(A)) - if(!camera.can_use()) - continue - if(!can_access_camera(camera, user)) - continue - var/dist = get_dist(camera,A) - if(dist < best_dist) - best_dist = dist - jump_to = camera - - if(isnull(jump_to)) - return - - if(can_access_camera(jump_to, user)) - switch_to_camera(user, jump_to) - -// Camera control: mouse. -/atom/DblClick() - ..() - if(istype(usr.machine, /obj/machinery/computer/security)) - var/obj/machinery/computer/security/console = usr.machine - console.jump_on_click(usr, src) - -// Camera control: arrow keys. -/mob/Move(n, direct) - if(istype(machine, /obj/machinery/computer/security)) - var/obj/machinery/computer/security/console = machine - var/turf/T = get_turf(console.watchers[src]) - for(var/i; i < 10; i++) - T = get_step(T, direct) - console.jump_on_click(src, T) - return - return ..(n,direct) - -// Other computer monitors. -/obj/machinery/computer/security/telescreen - name = "telescreen" - desc = "Used for watching camera networks." - icon_state = "telescreen_console" - icon_screen = "telescreen" - icon_keyboard = null - light_range_on = 0 - density = 0 - circuit = /obj/item/circuitboard/camera/telescreen - -/obj/machinery/computer/security/telescreen/entertainment - name = "entertainment monitor" - desc = "Damn, they better have Paradise TV on these things." - icon_state = "entertainment_console" - icon_screen = "entertainment" - light_color = "#FFEEDB" - light_range_on = 0 - network = list("news") - luminosity = 0 - circuit = /obj/item/circuitboard/camera/telescreen/entertainment - -/obj/machinery/computer/security/wooden_tv - name = "security camera monitor" - desc = "An old TV hooked into the station's camera network." - icon_state = "television" - icon_keyboard = null - icon_screen = "detective_tv" - light_color = "#3848B3" - light_power_on = 0.5 - network = list("SS13") - circuit = /obj/item/circuitboard/camera/wooden_tv - -/obj/machinery/computer/security/mining - name = "outpost camera monitor" - desc = "Used to access the various cameras on the outpost." - icon_keyboard = "mining_key" - icon_screen = "mining" - light_color = "#F9BBFC" - network = list("Mining Outpost") - circuit = /obj/item/circuitboard/camera/mining - -/obj/machinery/computer/security/engineering - name = "engineering camera monitor" - desc = "Used to monitor fires and breaches." - icon_keyboard = "power_key" - icon_screen = "engie_cams" - light_color = "#FAC54B" - network = list("Power Alarms","Atmosphere Alarms","Fire Alarms") - circuit = /obj/item/circuitboard/camera/engineering +/obj/machinery/computer/security + name = "security camera console" + desc = "Used to access the various cameras networks on the station." + + icon_keyboard = "security_key" + icon_screen = "cameras" + light_color = LIGHT_COLOR_RED + circuit = /obj/item/circuitboard/camera + + var/mapping = 0 // For the overview file (overview.dm), not used on this page + + var/list/network = list() + var/list/available_networks = list() + var/list/watchers = list() //who's using the console, associated with the camera they're on. + +/obj/machinery/computer/security/New() // Lists existing networks and their required access. Format: available_networks[] = list() + generate_network_access() + ..() + +/obj/machinery/computer/security/proc/generate_network_access() + available_networks["SS13"] = list(ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Telecomms"] = list(ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Research Outpost"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Mining Outpost"] = list(ACCESS_QM,ACCESS_HOP,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Research"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Prison"] = list(ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Labor Camp"] = list(ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Interrogation"] = list(ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Atmosphere Alarms"] = list(ACCESS_CE,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Fire Alarms"] = list(ACCESS_CE,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Power Alarms"] = list(ACCESS_CE,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Supermatter"] = list(ACCESS_CE,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["MiniSat"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Singularity"] = list(ACCESS_CE,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Anomaly Isolation"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Toxins"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["Telepad"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["TestChamber"] = list(ACCESS_RD,ACCESS_HOS,ACCESS_CAPTAIN) + available_networks["ERT"] = list(ACCESS_CENT_SPECOPS_COMMANDER,ACCESS_CENT_COMMANDER) + available_networks["CentComm"] = list(ACCESS_CENT_SECURITY,ACCESS_CENT_COMMANDER) + available_networks["Thunderdome"] = list(ACCESS_CENT_THUNDER,ACCESS_CENT_COMMANDER) + +/obj/machinery/computer/security/Destroy() + if(watchers.len) + for(var/mob/M in watchers) + M.unset_machine() //to properly reset the view of the users if the console is deleted. + return ..() + +/obj/machinery/computer/security/proc/isCameraFarAway(obj/machinery/camera/C) + var/turf/consoleturf = get_turf(src) + var/turf/cameraturf = get_turf(C) + if((is_away_level(cameraturf.z) || is_away_level(consoleturf.z)) && !atoms_share_level(cameraturf, consoleturf)) //can only recieve away mission cameras on away missions + return TRUE + +/obj/machinery/computer/security/check_eye(mob/user) + if(!(user in watchers)) + user.unset_machine() + return + if(!watchers[user]) + user.unset_machine() + return + var/obj/machinery/camera/C = watchers[user] + if(isCameraFarAway(C)) + user.unset_machine() + return + if(!can_access_camera(C, user)) + user.unset_machine() + return + return 1 + +/obj/machinery/computer/security/on_unset_machine(mob/user) + watchers.Remove(user) + user.reset_perspective(null) + +/obj/machinery/computer/security/attack_hand(mob/user) + if(stat || ..()) + user.unset_machine() + return + + ui_interact(user) + +/obj/machinery/computer/security/telescreen/multitool_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + var/direction = input(user, "Which direction?", "Select direction!") as null|anything in list("North", "East", "South", "West", "Centre") + if(!direction || !Adjacent(user)) + return + pixel_x = 0 + pixel_y = 0 + switch(direction) + if("North") + pixel_y = 32 + if("East") + pixel_x = 32 + if("South") + pixel_y = -32 + if("West") + pixel_x = -32 + +/obj/machinery/computer/security/emag_act(user as mob) + if(!emagged) + emagged = 1 + to_chat(user, "You have authorized full network access!") + attack_hand(user) + else + attack_hand(user) + +/obj/machinery/computer/security/proc/get_user_access(mob/user) + var/list/access = list() + + if(emagged) + access = get_all_accesses() // Assume captain level access when emagged + else if(ishuman(user)) + access = user.get_access() + else if((isAI(user) || isrobot(user)) && CanUseTopic(user, default_state) == STATUS_INTERACTIVE) + access = get_all_accesses() // Assume captain level access when AI + else if(user.can_admin_interact()) + access = get_all_accesses() + return access + +/obj/machinery/computer/security/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "sec_camera.tmpl", "Camera Console", 900, 800) + + // adding a template with the key "mapContent" enables the map ui functionality + ui.add_template("mapContent", "sec_camera_map_content.tmpl") + // adding a template with the key "mapHeader" replaces the map header content + ui.add_template("mapHeader", "sec_camera_map_header.tmpl") + + ui.open() + +/obj/machinery/computer/security/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) + var/data[0] + + var/list/cameras = list() + for(var/obj/machinery/camera/C in cameranet.cameras) + if(isCameraFarAway(C)) + continue + if(!can_access_camera(C, user)) + continue + + cameras[++cameras.len] = C.nano_structure() + + for(var/i = cameras.len, i > 0, i--) //based off /proc/camera_sort, sorts cameras alphabetically for the UI + for(var/j = 1 to i - 1) + var/a = cameras[j] + var/b = cameras[j + 1] + if(sorttext(a["name"], b["name"]) < 0) + cameras.Swap(j, j + 1) + + data["cameras"] = cameras + + var/list/access = get_user_access(user) + if(emagged) + data["emagged"] = 1 + + var/list/networks_list = list() + // Loop through the ID's permission, and check which networks the ID has access to. + for(var/net in available_networks) // Loop through networks. + for(var/req in available_networks[net]) // Loop through access levels of the networks. + if(req in access) + if(net in network) // Checks if the network is currently active. + networks_list.Add(list(list("name" = net, "active" = 1))) + else + networks_list.Add(list(list("name" = net, "active" = 0))) + break + + if(networks_list.len) + data["networks"] = networks_list + + data["current"] = null + if(watchers[user]) + var/obj/machinery/camera/watched = watchers[user] + data["current"] = watched.nano_structure() + + return data + +/obj/machinery/computer/security/Topic(href, href_list) + if(..()) + usr.unset_machine() + return 1 + + if(href_list["switchTo"]) + var/obj/machinery/camera/C = locate(href_list["switchTo"]) in cameranet.cameras + if(!C) + return 1 + + switch_to_camera(usr, C) + + else if(href_list["reset"]) + usr.unset_machine() + + else if(href_list["activate"]) // Activate: enable or disable networks + var/net = href_list["activate"] // Network to be enabled or disabled. + var/active = href_list["active"] // Is the network currently active. + var/list/access = get_user_access(usr) + for(var/a in available_networks[net]) + if(a in access) // Re-check for authorization. + if(text2num(active) == 1) + network -= net + break + else + network += net + break + + SSnanoui.update_uis(src) + +// Check if camera is accessible when jumping +/obj/machinery/computer/security/proc/can_access_camera(var/obj/machinery/camera/C, var/mob/M) + if(CanUseTopic(M, default_state) != STATUS_INTERACTIVE || M.incapacitated() || !M.has_vision()) + return 0 + + if(isrobot(M)) + var/list/viewing = viewers(src) + if(!viewing.Find(M)) + return 0 + + if(isAI(M)) + var/mob/living/silicon/ai/A = M + if(!A.is_in_chassis()) + return 0 + + if(!issilicon(M) && !Adjacent(M)) + return 0 + + var/list/shared_networks = network & C.network + if(!shared_networks.len || !C.can_use()) + return 0 + + return 1 + +// Switching to cameras +/obj/machinery/computer/security/proc/switch_to_camera(var/mob/user, var/obj/machinery/camera/C) + if(!can_access_camera(C, user)) + user.unset_machine() + return 1 + + if(isAI(user)) + var/mob/living/silicon/ai/A = user + A.eyeobj.setLoc(get_turf(C)) + A.client.eye = A.eyeobj + else + user.reset_perspective(C) + watchers[user] = C + use_power(50) + +//Camera control: moving. +/obj/machinery/computer/security/proc/jump_on_click(var/mob/user, var/A) + if(user.machine != src) + return + + var/obj/machinery/camera/jump_to + + if(istype(A, /obj/machinery/camera)) + jump_to = A + + else if(ismob(A)) + if(ishuman(A)) + var/mob/living/carbon/human/H = A + jump_to = locate() in H.head + else if(isrobot(A)) + var/mob/living/silicon/robot/R = A + jump_to = R.camera + + else if(isobj(A)) + var/obj/O = A + jump_to = locate() in O + + else if(isturf(A)) + var/best_dist = INFINITY + for(var/obj/machinery/camera/camera in get_area(A)) + if(!camera.can_use()) + continue + if(!can_access_camera(camera, user)) + continue + var/dist = get_dist(camera,A) + if(dist < best_dist) + best_dist = dist + jump_to = camera + + if(isnull(jump_to)) + return + + if(can_access_camera(jump_to, user)) + switch_to_camera(user, jump_to) + +// Camera control: mouse. +/atom/DblClick() + ..() + if(istype(usr.machine, /obj/machinery/computer/security)) + var/obj/machinery/computer/security/console = usr.machine + console.jump_on_click(usr, src) + +// Camera control: arrow keys. +/mob/Move(n, direct) + if(istype(machine, /obj/machinery/computer/security)) + var/obj/machinery/computer/security/console = machine + var/turf/T = get_turf(console.watchers[src]) + for(var/i; i < 10; i++) + T = get_step(T, direct) + console.jump_on_click(src, T) + return + return ..(n,direct) + +// Other computer monitors. +/obj/machinery/computer/security/telescreen + name = "telescreen" + desc = "Used for watching camera networks." + icon_state = "telescreen_console" + icon_screen = "telescreen" + icon_keyboard = null + light_range_on = 0 + density = 0 + circuit = /obj/item/circuitboard/camera/telescreen + +/obj/machinery/computer/security/telescreen/entertainment + name = "entertainment monitor" + desc = "Damn, they better have Paradise TV on these things." + icon_state = "entertainment_console" + icon_screen = "entertainment" + light_color = "#FFEEDB" + light_range_on = 0 + network = list("news") + luminosity = 0 + circuit = /obj/item/circuitboard/camera/telescreen/entertainment + +/obj/machinery/computer/security/wooden_tv + name = "security camera monitor" + desc = "An old TV hooked into the station's camera network." + icon_state = "television" + icon_keyboard = null + icon_screen = "detective_tv" + light_color = "#3848B3" + light_power_on = 0.5 + network = list("SS13") + circuit = /obj/item/circuitboard/camera/wooden_tv + +/obj/machinery/computer/security/mining + name = "outpost camera monitor" + desc = "Used to access the various cameras on the outpost." + icon_keyboard = "mining_key" + icon_screen = "mining" + light_color = "#F9BBFC" + network = list("Mining Outpost") + circuit = /obj/item/circuitboard/camera/mining + +/obj/machinery/computer/security/engineering + name = "engineering camera monitor" + desc = "Used to monitor fires and breaches." + icon_keyboard = "power_key" + icon_screen = "engie_cams" + light_color = "#FAC54B" + network = list("Power Alarms","Atmosphere Alarms","Fire Alarms") + circuit = /obj/item/circuitboard/camera/engineering diff --git a/code/game/machinery/computer/card.dm b/code/game/machinery/computer/card.dm index 86515e5ddea..07ab3f5d62b 100644 --- a/code/game/machinery/computer/card.dm +++ b/code/game/machinery/computer/card.dm @@ -7,7 +7,7 @@ var/time_last_changed_position = 0 desc = "Terminal for programming Nanotrasen employee ID cards to access parts of the station." icon_keyboard = "id_key" icon_screen = "id" - req_access = list(access_change_ids) + req_access = list(ACCESS_CHANGE_IDS) circuit = /obj/item/circuitboard/card light_color = LIGHT_COLOR_LIGHTBLUE var/obj/item/card/id/scan = null @@ -133,7 +133,7 @@ var/time_last_changed_position = 0 if(!istype(id_card)) return ..() - if(!scan && access_change_ids in id_card.access) + if(!scan && ACCESS_CHANGE_IDS in id_card.access) user.drop_item() id_card.loc = src scan = id_card @@ -200,7 +200,7 @@ var/time_last_changed_position = 0 return 1 if(!scan.assignment) return 0 - if(access_captain in scan.access) + if(ACCESS_CAPTAIN in scan.access) return 1 if(!targetjob || !targetjob.title) return 0 @@ -598,7 +598,7 @@ var/time_last_changed_position = 0 /obj/machinery/computer/card/centcom name = "\improper CentComm identification computer" circuit = /obj/item/circuitboard/card/centcom - req_access = list(access_cent_commander) + req_access = list(ACCESS_CENT_COMMANDER) change_position_cooldown = -1 blacklisted_full = list() blacklisted_partial = list() @@ -615,14 +615,14 @@ var/time_last_changed_position = 0 target_dept = TARGET_DEPT_SEC icon_screen = "idhos" light_color = LIGHT_COLOR_RED - req_access = list(access_hos) + req_access = list(ACCESS_HOS) circuit = /obj/item/circuitboard/card/minor/hos /obj/machinery/computer/card/minor/cmo name = "medical management console" target_dept = TARGET_DEPT_MED icon_screen = "idcmo" - req_access = list(access_cmo) + req_access = list(ACCESS_CMO) circuit = /obj/item/circuitboard/card/minor/cmo /obj/machinery/computer/card/minor/rd @@ -630,7 +630,7 @@ var/time_last_changed_position = 0 target_dept = TARGET_DEPT_SCI icon_screen = "idrd" light_color = LIGHT_COLOR_PINK - req_access = list(access_rd) + req_access = list(ACCESS_RD) circuit = /obj/item/circuitboard/card/minor/rd /obj/machinery/computer/card/minor/ce @@ -638,5 +638,5 @@ var/time_last_changed_position = 0 target_dept = TARGET_DEPT_ENG icon_screen = "idce" light_color = COLOR_YELLOW - req_access = list(access_ce) - circuit = /obj/item/circuitboard/card/minor/ce \ No newline at end of file + req_access = list(ACCESS_CE) + circuit = /obj/item/circuitboard/card/minor/ce diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index 539d4dfc0ed..c06cbb32a04 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -4,7 +4,7 @@ icon_keyboard = "med_key" icon_screen = "dna" circuit = /obj/item/circuitboard/cloning - req_access = list(access_heads) //Only used for record deletion right now. + req_access = list(ACCESS_HEADS) //Only used for record deletion right now. var/obj/machinery/dna_scannernew/scanner = null //Linked scanner. For scanning. var/list/pods = list() //Linked cloning pods. var/temp = "" diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm index be27b0600cf..3b663bbe549 100644 --- a/code/game/machinery/computer/communications.dm +++ b/code/game/machinery/computer/communications.dm @@ -1,569 +1,569 @@ -#define COMM_SCREEN_MAIN 1 -#define COMM_SCREEN_STAT 2 -#define COMM_SCREEN_MESSAGES 3 -#define COMM_SCREEN_SECLEVEL 4 - -#define COMM_AUTHENTICATION_NONE 0 -#define COMM_AUTHENTICATION_MIN 1 -#define COMM_AUTHENTICATION_MAX 2 - -// The communications computer -/obj/machinery/computer/communications - name = "communications console" - desc = "This can be used for various important functions. Still under developement." - icon_keyboard = "tech_key" - icon_screen = "comm" - req_access = list(access_heads) - circuit = /obj/item/circuitboard/communications - var/prints_intercept = 1 - var/authenticated = COMM_AUTHENTICATION_NONE - var/list/messagetitle = list() - var/list/messagetext = list() - var/currmsg = 0 - var/aicurrmsg = 0 - var/menu_state = COMM_SCREEN_MAIN - var/ai_menu_state = COMM_SCREEN_MAIN - var/message_cooldown = 0 - var/centcomm_message_cooldown = 0 - var/tmp_alertlevel = 0 - - var/stat_msg1 - var/stat_msg2 - var/display_type="blank" - - var/datum/announcement/priority/crew_announcement = new - - light_color = LIGHT_COLOR_LIGHTBLUE - -/obj/machinery/computer/communications/New() - GLOB.shuttle_caller_list += src - ..() - crew_announcement.newscast = 0 - -/obj/machinery/computer/communications/proc/is_authenticated(var/mob/user, var/message = 1) - if(authenticated == COMM_AUTHENTICATION_MAX) - return COMM_AUTHENTICATION_MAX - else if(user.can_admin_interact()) - return COMM_AUTHENTICATION_MAX - else if(authenticated) - return COMM_AUTHENTICATION_MIN - else - if(message) - to_chat(user, "Access denied.") - return COMM_AUTHENTICATION_NONE - -/obj/machinery/computer/communications/proc/change_security_level(var/new_level) - tmp_alertlevel = new_level - var/old_level = security_level - if(!tmp_alertlevel) tmp_alertlevel = SEC_LEVEL_GREEN - if(tmp_alertlevel < SEC_LEVEL_GREEN) tmp_alertlevel = SEC_LEVEL_GREEN - if(tmp_alertlevel > SEC_LEVEL_BLUE) tmp_alertlevel = SEC_LEVEL_BLUE //Cannot engage delta with this - set_security_level(tmp_alertlevel) - if(security_level != old_level) - //Only notify the admins if an actual change happened - log_game("[key_name(usr)] has changed the security level to [get_security_level()].") - message_admins("[key_name_admin(usr)] has changed the security level to [get_security_level()].") - switch(security_level) - if(SEC_LEVEL_GREEN) - feedback_inc("alert_comms_green",1) - if(SEC_LEVEL_BLUE) - feedback_inc("alert_comms_blue",1) - tmp_alertlevel = 0 - -/obj/machinery/computer/communications/Topic(href, href_list) - if(..(href, href_list)) - return 1 - - if(!is_secure_level(src.z)) - to_chat(usr, "Unable to establish a connection: You're too far away from the station!") - return 1 - - if(href_list["login"]) - if(!ishuman(usr)) - to_chat(usr, "Access denied.") - return - - var/list/access = usr.get_access() - if(allowed(usr)) - authenticated = COMM_AUTHENTICATION_MIN - - if(access_captain in access) - authenticated = COMM_AUTHENTICATION_MAX - var/mob/living/carbon/human/H = usr - var/obj/item/card/id = H.get_idcard(TRUE) - if(istype(id)) - crew_announcement.announcer = GetNameAndAssignmentFromId(id) - - SSnanoui.update_uis(src) - return - - if(href_list["logout"]) - authenticated = COMM_AUTHENTICATION_NONE - crew_announcement.announcer = "" - setMenuState(usr,COMM_SCREEN_MAIN) - SSnanoui.update_uis(src) - return - - if(!is_authenticated(usr)) - return 1 - - switch(href_list["operation"]) - if("main") - setMenuState(usr,COMM_SCREEN_MAIN) - - if("changeseclevel") - setMenuState(usr,COMM_SCREEN_SECLEVEL) - - if("newalertlevel") - if(isAI(usr) || isrobot(usr)) - to_chat(usr, "Firewalls prevent you from changing the alert level.") - return 1 - else if(usr.can_admin_interact()) - change_security_level(text2num(href_list["level"])) - return 1 - else if(!ishuman(usr)) - to_chat(usr, "Security measures prevent you from changing the alert level.") - return 1 - - var/mob/living/carbon/human/L = usr - var/obj/item/card = L.get_active_hand() - var/obj/item/card/id/I = (card && card.GetID()) || L.wear_id || L.wear_pda - if(istype(I, /obj/item/pda)) - var/obj/item/pda/pda = I - I = pda.id - if(I && istype(I)) - if(access_captain in I.access) - change_security_level(text2num(href_list["level"])) - else - to_chat(usr, "You are not authorized to do this.") - setMenuState(usr,COMM_SCREEN_MAIN) - else - to_chat(usr, "You need to swipe your ID.") - - if("announce") - if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) - if(message_cooldown) - to_chat(usr, "Please allow at least one minute to pass between announcements.") - SSnanoui.update_uis(src) - return - var/input = input(usr, "Please write a message to announce to the station crew.", "Priority Announcement") - if(!input || message_cooldown || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) - SSnanoui.update_uis(src) - return - crew_announcement.Announce(input) - message_cooldown = 1 - spawn(600)//One minute cooldown - message_cooldown = 0 - - if("callshuttle") - var/input = clean_input("Please enter the reason for calling the shuttle.", "Shuttle Call Reason.","") - if(!input || ..() || !is_authenticated(usr)) - SSnanoui.update_uis(src) - return - - call_shuttle_proc(usr, input) - if(SSshuttle.emergency.timer) - post_status("shuttle") - setMenuState(usr,COMM_SCREEN_MAIN) - - if("cancelshuttle") - if(isAI(usr) || isrobot(usr)) - to_chat(usr, "Firewalls prevent you from recalling the shuttle.") - SSnanoui.update_uis(src) - return 1 - var/response = alert("Are you sure you wish to recall the shuttle?", "Confirm", "Yes", "No") - if(response == "Yes") - cancel_call_proc(usr) - if(SSshuttle.emergency.timer) - post_status("shuttle") - setMenuState(usr,COMM_SCREEN_MAIN) - - if("messagelist") - currmsg = 0 - if(href_list["msgid"]) - setCurrentMessage(usr, text2num(href_list["msgid"])) - setMenuState(usr,COMM_SCREEN_MESSAGES) - - if("delmessage") - if(href_list["msgid"]) - currmsg = text2num(href_list["msgid"]) - var/response = alert("Are you sure you wish to delete this message?", "Confirm", "Yes", "No") - if(response == "Yes") - if(currmsg) - var/id = getCurrentMessage() - var/title = messagetitle[id] - var/text = messagetext[id] - messagetitle.Remove(title) - messagetext.Remove(text) - if(currmsg == id) - currmsg = 0 - if(aicurrmsg == id) - aicurrmsg = 0 - setMenuState(usr,COMM_SCREEN_MESSAGES) - - if("status") - setMenuState(usr,COMM_SCREEN_STAT) - - // Status display stuff - if("setstat") - display_type=href_list["statdisp"] - switch(display_type) - if("message") - post_status("message", stat_msg1, stat_msg2, usr) - if("alert") - post_status("alert", href_list["alert"], user = usr) - else - post_status(href_list["statdisp"], user = usr) - setMenuState(usr,COMM_SCREEN_STAT) - - if("setmsg1") - stat_msg1 = clean_input("Line 1", "Enter Message Text", stat_msg1) - setMenuState(usr,COMM_SCREEN_STAT) - - if("setmsg2") - stat_msg2 = clean_input("Line 2", "Enter Message Text", stat_msg2) - setMenuState(usr,COMM_SCREEN_STAT) - - if("nukerequest") - if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) - if(centcomm_message_cooldown) - to_chat(usr, "Arrays recycling. Please stand by.") - SSnanoui.update_uis(src) - return - var/input = stripped_input(usr, "Please enter the reason for requesting the nuclear self-destruct codes. Misuse of the nuclear request system will not be tolerated under any circumstances. Transmission does not guarantee a response.", "Self Destruct Code Request.","") - if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) - SSnanoui.update_uis(src) - return - Nuke_request(input, usr) - to_chat(usr, "Request sent.") - log_game("[key_name(usr)] has requested the nuclear codes from Centcomm") - priority_announcement.Announce("The codes for the on-station nuclear self-destruct have been requested by [usr]. Confirmation or denial of this request will be sent shortly.", "Nuclear Self Destruct Codes Requested",'sound/AI/commandreport.ogg') - centcomm_message_cooldown = 1 - spawn(6000)//10 minute cooldown - centcomm_message_cooldown = 0 - setMenuState(usr,COMM_SCREEN_MAIN) - - if("MessageCentcomm") - if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) - if(centcomm_message_cooldown) - to_chat(usr, "Arrays recycling. Please stand by.") - SSnanoui.update_uis(src) - return - var/input = stripped_input(usr, "Please choose a message to transmit to Centcomm via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response.", "To abort, send an empty message.", "") - if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) - SSnanoui.update_uis(src) - return - Centcomm_announce(input, usr) - print_centcom_report(input, station_time_timestamp() + " Captain's Message") - to_chat(usr, "Message transmitted.") - log_game("[key_name(usr)] has made a Centcomm announcement: [input]") - centcomm_message_cooldown = 1 - spawn(6000)//10 minute cooldown - centcomm_message_cooldown = 0 - setMenuState(usr,COMM_SCREEN_MAIN) - - // OMG SYNDICATE ...LETTERHEAD - if("MessageSyndicate") - if((is_authenticated(usr) == COMM_AUTHENTICATION_MAX) && (src.emagged)) - if(centcomm_message_cooldown) - to_chat(usr, "Arrays recycling. Please stand by.") - SSnanoui.update_uis(src) - return - var/input = stripped_input(usr, "Please choose a message to transmit to \[ABNORMAL ROUTING CORDINATES\] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response.", "To abort, send an empty message.", "") - if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) - SSnanoui.update_uis(src) - return - Syndicate_announce(input, usr) - to_chat(usr, "Message transmitted.") - log_game("[key_name(usr)] has made a Syndicate announcement: [input]") - centcomm_message_cooldown = 1 - spawn(6000)//10 minute cooldown - centcomm_message_cooldown = 0 - setMenuState(usr,COMM_SCREEN_MAIN) - - if("RestoreBackup") - to_chat(usr, "Backup routing data restored!") - src.emagged = 0 - setMenuState(usr,COMM_SCREEN_MAIN) - - if("RestartNanoMob") - if(SSmob_hunt) - if(SSmob_hunt.manual_reboot()) - var/loading_msg = pick("Respawning spawns", "Reticulating splines", "Flipping hat", - "Capturing all of them", "Fixing minor text issues", "Being the very best", - "Nerfing this", "Not communicating with playerbase", "Coding a ripoff in a 2D spaceman game") - to_chat(usr, "Restarting Nano-Mob Hunter GO! game server. [loading_msg]...") - else - to_chat(usr, "Nano-Mob Hunter GO! game server reboot failed due to recent restart. Please wait before re-attempting.") - else - to_chat(usr, "Nano-Mob Hunter GO! game server is offline for extended maintenance. Contact your Central Command administrators for more info if desired.") - - if("ToggleATC") - atc.squelched = !atc.squelched - to_chat(usr, "ATC traffic is now: [atc.squelched ? "Disabled" : "Enabled"].") - - SSnanoui.update_uis(src) - return 1 - -/obj/machinery/computer/communications/emag_act(user as mob) - if(!emagged) - src.emagged = 1 - to_chat(user, "You scramble the communication routing circuits!") - SSnanoui.update_uis(src) - -/obj/machinery/computer/communications/attack_ai(var/mob/user as mob) - return src.attack_hand(user) - -/obj/machinery/computer/communications/attack_hand(var/mob/user as mob) - if(..(user)) - return - - if(stat & (NOPOWER|BROKEN)) - return - - if(!is_secure_level(src.z)) - to_chat(user, "Unable to establish a connection: You're too far away from the station!") - return - - ui_interact(user) - -/obj/machinery/computer/communications/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null) - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "comm_console.tmpl", "Communications Console", 400, 500) - // open the new ui window - ui.open() - -/obj/machinery/computer/communications/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - data["is_ai"] = isAI(user) || isrobot(user) - data["menu_state"] = data["is_ai"] ? ai_menu_state : menu_state - data["emagged"] = emagged - data["authenticated"] = is_authenticated(user, 0) - data["screen"] = getMenuState(usr) - - data["stat_display"] = list( - "type" = display_type, - "line_1" = (stat_msg1 ? stat_msg1 : "-----"), - "line_2" = (stat_msg2 ? stat_msg2 : "-----"), - - "presets" = list( - list("name" = "blank", "label" = "Clear", "desc" = "Blank slate"), - list("name" = "shuttle", "label" = "Shuttle ETA", "desc" = "Display how much time is left."), - list("name" = "message", "label" = "Message", "desc" = "A custom message.") - ), - - "alerts"=list( - list("alert" = "default", "label" = "Nanotrasen", "desc" = "Oh god."), - list("alert" = "redalert", "label" = "Red Alert", "desc" = "Nothing to do with communists."), - list("alert" = "lockdown", "label" = "Lockdown", "desc" = "Let everyone know they're on lockdown."), - list("alert" = "biohazard", "label" = "Biohazard", "desc" = "Great for virus outbreaks and parties."), - ) - ) - - data["security_level"] = security_level - data["str_security_level"] = capitalize(get_security_level()) - data["levels"] = list( - list("id" = SEC_LEVEL_GREEN, "name" = "Green"), - list("id" = SEC_LEVEL_BLUE, "name" = "Blue"), - //SEC_LEVEL_RED = list("name"="Red"), - ) - - var/list/msg_data = list() - for(var/i = 1; i <= messagetext.len; i++) - msg_data.Add(list(list("title" = messagetitle[i], "body" = messagetext[i], "id" = i))) - - data["messages"] = msg_data - if((data["is_ai"] && aicurrmsg) || (!data["is_ai"] && currmsg)) - data["current_message"] = data["is_ai"] ? messagetext[aicurrmsg] : messagetext[currmsg] - data["current_message_title"] = data["is_ai"] ? messagetitle[aicurrmsg] : messagetitle[currmsg] - - data["lastCallLoc"] = SSshuttle.emergencyLastCallLoc ? format_text(SSshuttle.emergencyLastCallLoc.name) : null - - var/shuttle[0] - switch(SSshuttle.emergency.mode) - if(SHUTTLE_IDLE, SHUTTLE_RECALL) - shuttle["callStatus"] = 2 //#define - else - shuttle["callStatus"] = 1 - if(SSshuttle.emergency.mode == SHUTTLE_CALL) - var/timeleft = SSshuttle.emergency.timeLeft() - shuttle["eta"] = "[timeleft / 60 % 60]:[add_zero(num2text(timeleft % 60), 2)]" - - data["shuttle"] = shuttle - - data["atcSquelched"] = atc.squelched - - return data - - -/obj/machinery/computer/communications/proc/setCurrentMessage(var/mob/user,var/value) - if(isAI(user) || isrobot(user)) - aicurrmsg = value - else - currmsg = value - -/obj/machinery/computer/communications/proc/getCurrentMessage(var/mob/user) - if(isAI(user) || isrobot(user)) - return aicurrmsg - else - return currmsg - -/obj/machinery/computer/communications/proc/setMenuState(var/mob/user,var/value) - if(isAI(user) || isrobot(user)) - ai_menu_state=value - else - menu_state=value - -/obj/machinery/computer/communications/proc/getMenuState(var/mob/user) - if(isAI(user) || isrobot(user)) - return ai_menu_state - else - return menu_state - -/proc/enable_prison_shuttle(var/mob/user); - -/proc/call_shuttle_proc(var/mob/user, var/reason) - if(sent_strike_team == 1) - to_chat(user, "Central Command will not allow the shuttle to be called. Consider all contracts terminated.") - return - - if(SSshuttle.emergencyNoEscape) - to_chat(user, "The emergency shuttle may not be sent at this time. Please try again later.") - return - - if(SSshuttle.emergency.mode > SHUTTLE_ESCAPE) - to_chat(user, "The emergency shuttle may not be called while returning to Central Command.") - return - - if(SSticker.mode.name == "blob") - to_chat(user, "Under directive 7-10, [station_name()] is quarantined until further notice.") - return - - SSshuttle.requestEvac(user, reason) - log_game("[key_name(user)] has called the shuttle.") - message_admins("[key_name_admin(user)] has called the shuttle.", 1) - - return - -/proc/init_shift_change(var/mob/user, var/force = 0) - // if force is 0, some things may stop the shuttle call - if(!force) - if(SSshuttle.emergencyNoEscape) - to_chat(user, "Central Command does not currently have a shuttle available in your sector. Please try again later.") - return - - if(sent_strike_team == 1) - to_chat(user, "Central Command will not allow the shuttle to be called. Consider all contracts terminated.") - return - - if(world.time < 54000) // 30 minute grace period to let the game get going - to_chat(user, "The shuttle is refueling. Please wait another [round((54000-world.time)/600)] minutes before trying again.") - return - - if(SSticker.mode.name == "epidemic") - to_chat(user, "Under directive 7-10, [station_name()] is quarantined until further notice.") - return - - if(seclevel2num(get_security_level()) >= SEC_LEVEL_RED) // There is a serious threat we gotta move no time to give them five minutes. - SSshuttle.emergency.request(null, 0.5, null, " Automatic Crew Transfer", 1) - SSshuttle.emergency.canRecall = FALSE - else - SSshuttle.emergency.request(null, 1, null, " Automatic Crew Transfer", 0) - SSshuttle.emergency.canRecall = FALSE - if(user) - log_game("[key_name(user)] has called the shuttle.") - message_admins("[key_name_admin(user)] has called the shuttle - [formatJumpTo(user)].", 1) - return - - -/proc/cancel_call_proc(var/mob/user) - if(SSticker.mode.name == "meteor") - return - - if(SSshuttle.cancelEvac(user)) - log_game("[key_name(user)] has recalled the shuttle.") - message_admins("[key_name_admin(user)] has recalled the shuttle - ([ADMIN_FLW(user,"FLW")]).", 1) - else - to_chat(user, "Central Command has refused the recall request!") - log_game("[key_name(user)] has tried and failed to recall the shuttle.") - message_admins("[key_name_admin(user)] has tried and failed to recall the shuttle - ([ADMIN_FLW(user,"FLW")]).", 1) - -/proc/post_status(command, data1, data2, mob/user = null) - - var/datum/radio_frequency/frequency = SSradio.return_frequency(DISPLAY_FREQ) - - if(!frequency) return - - var/datum/signal/status_signal = new - status_signal.source = src - status_signal.transmission_method = 1 - status_signal.data["command"] = command - - switch(command) - if("message") - status_signal.data["msg1"] = data1 - status_signal.data["msg2"] = data2 - log_admin("STATUS: [user] set status screen message with [src]: [data1] [data2]") - //message_admins("STATUS: [user] set status screen with [PDA]. Message: [data1] [data2]") - if("alert") - status_signal.data["picture_state"] = data1 - - spawn(0) - frequency.post_signal(src, status_signal) - - -/obj/machinery/computer/communications/Destroy() - GLOB.shuttle_caller_list -= src - SSshuttle.autoEvac() - return ..() - -/obj/item/circuitboard/communications/New() - GLOB.shuttle_caller_list += src - ..() - -/obj/item/circuitboard/communications/Destroy() - GLOB.shuttle_caller_list -= src - SSshuttle.autoEvac() - return ..() - -/proc/print_command_report(text = "", title = "Central Command Update") - for(var/obj/machinery/computer/communications/C in GLOB.shuttle_caller_list) - if(!(C.stat & (BROKEN|NOPOWER)) && is_station_contact(C.z)) - var/obj/item/paper/P = new /obj/item/paper(C.loc) - P.name = "paper- '[title]'" - P.info = text - P.update_icon() - C.messagetitle.Add("[title]") - C.messagetext.Add(text) - for(var/datum/computer_file/program/comm/P in GLOB.shuttle_caller_list) - var/turf/T = get_turf(P.computer) - if(T && P.program_state != PROGRAM_STATE_KILLED && is_station_contact(T.z)) - if(P.computer) - var/obj/item/computer_hardware/printer/printer = P.computer.all_components[MC_PRINT] - if(printer) - printer.print_text(text, "paper- '[title]'") - P.messagetitle.Add("[title]") - P.messagetext.Add(text) - -/proc/print_centcom_report(text = "", title = "Incoming Message") - for(var/obj/machinery/computer/communications/C in GLOB.shuttle_caller_list) - if(!(C.stat & (BROKEN|NOPOWER)) && is_admin_level(C.z)) - var/obj/item/paper/P = new /obj/item/paper(C.loc) - P.name = "paper- '[title]'" - P.info = text - P.update_icon() - C.messagetitle.Add("[title]") - C.messagetext.Add(text) - for(var/datum/computer_file/program/comm/P in GLOB.shuttle_caller_list) - var/turf/T = get_turf(P.computer) - if(T && P.program_state != PROGRAM_STATE_KILLED && is_admin_level(T.z)) - if(P.computer) - var/obj/item/computer_hardware/printer/printer = P.computer.all_components[MC_PRINT] - if(printer) - printer.print_text(text, "paper- '[title]'") - P.messagetitle.Add("[title]") - P.messagetext.Add(text) \ No newline at end of file +#define COMM_SCREEN_MAIN 1 +#define COMM_SCREEN_STAT 2 +#define COMM_SCREEN_MESSAGES 3 +#define COMM_SCREEN_SECLEVEL 4 + +#define COMM_AUTHENTICATION_NONE 0 +#define COMM_AUTHENTICATION_MIN 1 +#define COMM_AUTHENTICATION_MAX 2 + +// The communications computer +/obj/machinery/computer/communications + name = "communications console" + desc = "This can be used for various important functions. Still under developement." + icon_keyboard = "tech_key" + icon_screen = "comm" + req_access = list(ACCESS_HEADS) + circuit = /obj/item/circuitboard/communications + var/prints_intercept = 1 + var/authenticated = COMM_AUTHENTICATION_NONE + var/list/messagetitle = list() + var/list/messagetext = list() + var/currmsg = 0 + var/aicurrmsg = 0 + var/menu_state = COMM_SCREEN_MAIN + var/ai_menu_state = COMM_SCREEN_MAIN + var/message_cooldown = 0 + var/centcomm_message_cooldown = 0 + var/tmp_alertlevel = 0 + + var/stat_msg1 + var/stat_msg2 + var/display_type="blank" + + var/datum/announcement/priority/crew_announcement = new + + light_color = LIGHT_COLOR_LIGHTBLUE + +/obj/machinery/computer/communications/New() + GLOB.shuttle_caller_list += src + ..() + crew_announcement.newscast = 0 + +/obj/machinery/computer/communications/proc/is_authenticated(var/mob/user, var/message = 1) + if(authenticated == COMM_AUTHENTICATION_MAX) + return COMM_AUTHENTICATION_MAX + else if(user.can_admin_interact()) + return COMM_AUTHENTICATION_MAX + else if(authenticated) + return COMM_AUTHENTICATION_MIN + else + if(message) + to_chat(user, "Access denied.") + return COMM_AUTHENTICATION_NONE + +/obj/machinery/computer/communications/proc/change_security_level(var/new_level) + tmp_alertlevel = new_level + var/old_level = security_level + if(!tmp_alertlevel) tmp_alertlevel = SEC_LEVEL_GREEN + if(tmp_alertlevel < SEC_LEVEL_GREEN) tmp_alertlevel = SEC_LEVEL_GREEN + if(tmp_alertlevel > SEC_LEVEL_BLUE) tmp_alertlevel = SEC_LEVEL_BLUE //Cannot engage delta with this + set_security_level(tmp_alertlevel) + if(security_level != old_level) + //Only notify the admins if an actual change happened + log_game("[key_name(usr)] has changed the security level to [get_security_level()].") + message_admins("[key_name_admin(usr)] has changed the security level to [get_security_level()].") + switch(security_level) + if(SEC_LEVEL_GREEN) + feedback_inc("alert_comms_green",1) + if(SEC_LEVEL_BLUE) + feedback_inc("alert_comms_blue",1) + tmp_alertlevel = 0 + +/obj/machinery/computer/communications/Topic(href, href_list) + if(..(href, href_list)) + return 1 + + if(!is_secure_level(src.z)) + to_chat(usr, "Unable to establish a connection: You're too far away from the station!") + return 1 + + if(href_list["login"]) + if(!ishuman(usr)) + to_chat(usr, "Access denied.") + return + + var/list/access = usr.get_access() + if(allowed(usr)) + authenticated = COMM_AUTHENTICATION_MIN + + if(ACCESS_CAPTAIN in access) + authenticated = COMM_AUTHENTICATION_MAX + var/mob/living/carbon/human/H = usr + var/obj/item/card/id = H.get_idcard(TRUE) + if(istype(id)) + crew_announcement.announcer = GetNameAndAssignmentFromId(id) + + SSnanoui.update_uis(src) + return + + if(href_list["logout"]) + authenticated = COMM_AUTHENTICATION_NONE + crew_announcement.announcer = "" + setMenuState(usr,COMM_SCREEN_MAIN) + SSnanoui.update_uis(src) + return + + if(!is_authenticated(usr)) + return 1 + + switch(href_list["operation"]) + if("main") + setMenuState(usr,COMM_SCREEN_MAIN) + + if("changeseclevel") + setMenuState(usr,COMM_SCREEN_SECLEVEL) + + if("newalertlevel") + if(isAI(usr) || isrobot(usr)) + to_chat(usr, "Firewalls prevent you from changing the alert level.") + return 1 + else if(usr.can_admin_interact()) + change_security_level(text2num(href_list["level"])) + return 1 + else if(!ishuman(usr)) + to_chat(usr, "Security measures prevent you from changing the alert level.") + return 1 + + var/mob/living/carbon/human/L = usr + var/obj/item/card = L.get_active_hand() + var/obj/item/card/id/I = (card && card.GetID()) || L.wear_id || L.wear_pda + if(istype(I, /obj/item/pda)) + var/obj/item/pda/pda = I + I = pda.id + if(I && istype(I)) + if(ACCESS_CAPTAIN in I.access) + change_security_level(text2num(href_list["level"])) + else + to_chat(usr, "You are not authorized to do this.") + setMenuState(usr,COMM_SCREEN_MAIN) + else + to_chat(usr, "You need to swipe your ID.") + + if("announce") + if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) + if(message_cooldown) + to_chat(usr, "Please allow at least one minute to pass between announcements.") + SSnanoui.update_uis(src) + return + var/input = input(usr, "Please write a message to announce to the station crew.", "Priority Announcement") + if(!input || message_cooldown || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) + SSnanoui.update_uis(src) + return + crew_announcement.Announce(input) + message_cooldown = 1 + spawn(600)//One minute cooldown + message_cooldown = 0 + + if("callshuttle") + var/input = clean_input("Please enter the reason for calling the shuttle.", "Shuttle Call Reason.","") + if(!input || ..() || !is_authenticated(usr)) + SSnanoui.update_uis(src) + return + + call_shuttle_proc(usr, input) + if(SSshuttle.emergency.timer) + post_status("shuttle") + setMenuState(usr,COMM_SCREEN_MAIN) + + if("cancelshuttle") + if(isAI(usr) || isrobot(usr)) + to_chat(usr, "Firewalls prevent you from recalling the shuttle.") + SSnanoui.update_uis(src) + return 1 + var/response = alert("Are you sure you wish to recall the shuttle?", "Confirm", "Yes", "No") + if(response == "Yes") + cancel_call_proc(usr) + if(SSshuttle.emergency.timer) + post_status("shuttle") + setMenuState(usr,COMM_SCREEN_MAIN) + + if("messagelist") + currmsg = 0 + if(href_list["msgid"]) + setCurrentMessage(usr, text2num(href_list["msgid"])) + setMenuState(usr,COMM_SCREEN_MESSAGES) + + if("delmessage") + if(href_list["msgid"]) + currmsg = text2num(href_list["msgid"]) + var/response = alert("Are you sure you wish to delete this message?", "Confirm", "Yes", "No") + if(response == "Yes") + if(currmsg) + var/id = getCurrentMessage() + var/title = messagetitle[id] + var/text = messagetext[id] + messagetitle.Remove(title) + messagetext.Remove(text) + if(currmsg == id) + currmsg = 0 + if(aicurrmsg == id) + aicurrmsg = 0 + setMenuState(usr,COMM_SCREEN_MESSAGES) + + if("status") + setMenuState(usr,COMM_SCREEN_STAT) + + // Status display stuff + if("setstat") + display_type=href_list["statdisp"] + switch(display_type) + if("message") + post_status("message", stat_msg1, stat_msg2, usr) + if("alert") + post_status("alert", href_list["alert"], user = usr) + else + post_status(href_list["statdisp"], user = usr) + setMenuState(usr,COMM_SCREEN_STAT) + + if("setmsg1") + stat_msg1 = clean_input("Line 1", "Enter Message Text", stat_msg1) + setMenuState(usr,COMM_SCREEN_STAT) + + if("setmsg2") + stat_msg2 = clean_input("Line 2", "Enter Message Text", stat_msg2) + setMenuState(usr,COMM_SCREEN_STAT) + + if("nukerequest") + if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) + if(centcomm_message_cooldown) + to_chat(usr, "Arrays recycling. Please stand by.") + SSnanoui.update_uis(src) + return + var/input = stripped_input(usr, "Please enter the reason for requesting the nuclear self-destruct codes. Misuse of the nuclear request system will not be tolerated under any circumstances. Transmission does not guarantee a response.", "Self Destruct Code Request.","") + if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) + SSnanoui.update_uis(src) + return + Nuke_request(input, usr) + to_chat(usr, "Request sent.") + log_game("[key_name(usr)] has requested the nuclear codes from Centcomm") + priority_announcement.Announce("The codes for the on-station nuclear self-destruct have been requested by [usr]. Confirmation or denial of this request will be sent shortly.", "Nuclear Self Destruct Codes Requested",'sound/AI/commandreport.ogg') + centcomm_message_cooldown = 1 + spawn(6000)//10 minute cooldown + centcomm_message_cooldown = 0 + setMenuState(usr,COMM_SCREEN_MAIN) + + if("MessageCentcomm") + if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) + if(centcomm_message_cooldown) + to_chat(usr, "Arrays recycling. Please stand by.") + SSnanoui.update_uis(src) + return + var/input = stripped_input(usr, "Please choose a message to transmit to Centcomm via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response.", "To abort, send an empty message.", "") + if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) + SSnanoui.update_uis(src) + return + Centcomm_announce(input, usr) + print_centcom_report(input, station_time_timestamp() + " Captain's Message") + to_chat(usr, "Message transmitted.") + log_game("[key_name(usr)] has made a Centcomm announcement: [input]") + centcomm_message_cooldown = 1 + spawn(6000)//10 minute cooldown + centcomm_message_cooldown = 0 + setMenuState(usr,COMM_SCREEN_MAIN) + + // OMG SYNDICATE ...LETTERHEAD + if("MessageSyndicate") + if((is_authenticated(usr) == COMM_AUTHENTICATION_MAX) && (src.emagged)) + if(centcomm_message_cooldown) + to_chat(usr, "Arrays recycling. Please stand by.") + SSnanoui.update_uis(src) + return + var/input = stripped_input(usr, "Please choose a message to transmit to \[ABNORMAL ROUTING CORDINATES\] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response.", "To abort, send an empty message.", "") + if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) + SSnanoui.update_uis(src) + return + Syndicate_announce(input, usr) + to_chat(usr, "Message transmitted.") + log_game("[key_name(usr)] has made a Syndicate announcement: [input]") + centcomm_message_cooldown = 1 + spawn(6000)//10 minute cooldown + centcomm_message_cooldown = 0 + setMenuState(usr,COMM_SCREEN_MAIN) + + if("RestoreBackup") + to_chat(usr, "Backup routing data restored!") + src.emagged = 0 + setMenuState(usr,COMM_SCREEN_MAIN) + + if("RestartNanoMob") + if(SSmob_hunt) + if(SSmob_hunt.manual_reboot()) + var/loading_msg = pick("Respawning spawns", "Reticulating splines", "Flipping hat", + "Capturing all of them", "Fixing minor text issues", "Being the very best", + "Nerfing this", "Not communicating with playerbase", "Coding a ripoff in a 2D spaceman game") + to_chat(usr, "Restarting Nano-Mob Hunter GO! game server. [loading_msg]...") + else + to_chat(usr, "Nano-Mob Hunter GO! game server reboot failed due to recent restart. Please wait before re-attempting.") + else + to_chat(usr, "Nano-Mob Hunter GO! game server is offline for extended maintenance. Contact your Central Command administrators for more info if desired.") + + if("ToggleATC") + atc.squelched = !atc.squelched + to_chat(usr, "ATC traffic is now: [atc.squelched ? "Disabled" : "Enabled"].") + + SSnanoui.update_uis(src) + return 1 + +/obj/machinery/computer/communications/emag_act(user as mob) + if(!emagged) + src.emagged = 1 + to_chat(user, "You scramble the communication routing circuits!") + SSnanoui.update_uis(src) + +/obj/machinery/computer/communications/attack_ai(var/mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/computer/communications/attack_hand(var/mob/user as mob) + if(..(user)) + return + + if(stat & (NOPOWER|BROKEN)) + return + + if(!is_secure_level(src.z)) + to_chat(user, "Unable to establish a connection: You're too far away from the station!") + return + + ui_interact(user) + +/obj/machinery/computer/communications/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null) + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "comm_console.tmpl", "Communications Console", 400, 500) + // open the new ui window + ui.open() + +/obj/machinery/computer/communications/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) + var/data[0] + data["is_ai"] = isAI(user) || isrobot(user) + data["menu_state"] = data["is_ai"] ? ai_menu_state : menu_state + data["emagged"] = emagged + data["authenticated"] = is_authenticated(user, 0) + data["screen"] = getMenuState(usr) + + data["stat_display"] = list( + "type" = display_type, + "line_1" = (stat_msg1 ? stat_msg1 : "-----"), + "line_2" = (stat_msg2 ? stat_msg2 : "-----"), + + "presets" = list( + list("name" = "blank", "label" = "Clear", "desc" = "Blank slate"), + list("name" = "shuttle", "label" = "Shuttle ETA", "desc" = "Display how much time is left."), + list("name" = "message", "label" = "Message", "desc" = "A custom message.") + ), + + "alerts"=list( + list("alert" = "default", "label" = "Nanotrasen", "desc" = "Oh god."), + list("alert" = "redalert", "label" = "Red Alert", "desc" = "Nothing to do with communists."), + list("alert" = "lockdown", "label" = "Lockdown", "desc" = "Let everyone know they're on lockdown."), + list("alert" = "biohazard", "label" = "Biohazard", "desc" = "Great for virus outbreaks and parties."), + ) + ) + + data["security_level"] = security_level + data["str_security_level"] = capitalize(get_security_level()) + data["levels"] = list( + list("id" = SEC_LEVEL_GREEN, "name" = "Green"), + list("id" = SEC_LEVEL_BLUE, "name" = "Blue"), + //SEC_LEVEL_RED = list("name"="Red"), + ) + + var/list/msg_data = list() + for(var/i = 1; i <= messagetext.len; i++) + msg_data.Add(list(list("title" = messagetitle[i], "body" = messagetext[i], "id" = i))) + + data["messages"] = msg_data + if((data["is_ai"] && aicurrmsg) || (!data["is_ai"] && currmsg)) + data["current_message"] = data["is_ai"] ? messagetext[aicurrmsg] : messagetext[currmsg] + data["current_message_title"] = data["is_ai"] ? messagetitle[aicurrmsg] : messagetitle[currmsg] + + data["lastCallLoc"] = SSshuttle.emergencyLastCallLoc ? format_text(SSshuttle.emergencyLastCallLoc.name) : null + + var/shuttle[0] + switch(SSshuttle.emergency.mode) + if(SHUTTLE_IDLE, SHUTTLE_RECALL) + shuttle["callStatus"] = 2 //#define + else + shuttle["callStatus"] = 1 + if(SSshuttle.emergency.mode == SHUTTLE_CALL) + var/timeleft = SSshuttle.emergency.timeLeft() + shuttle["eta"] = "[timeleft / 60 % 60]:[add_zero(num2text(timeleft % 60), 2)]" + + data["shuttle"] = shuttle + + data["atcSquelched"] = atc.squelched + + return data + + +/obj/machinery/computer/communications/proc/setCurrentMessage(var/mob/user,var/value) + if(isAI(user) || isrobot(user)) + aicurrmsg = value + else + currmsg = value + +/obj/machinery/computer/communications/proc/getCurrentMessage(var/mob/user) + if(isAI(user) || isrobot(user)) + return aicurrmsg + else + return currmsg + +/obj/machinery/computer/communications/proc/setMenuState(var/mob/user,var/value) + if(isAI(user) || isrobot(user)) + ai_menu_state=value + else + menu_state=value + +/obj/machinery/computer/communications/proc/getMenuState(var/mob/user) + if(isAI(user) || isrobot(user)) + return ai_menu_state + else + return menu_state + +/proc/enable_prison_shuttle(var/mob/user); + +/proc/call_shuttle_proc(var/mob/user, var/reason) + if(sent_strike_team == 1) + to_chat(user, "Central Command will not allow the shuttle to be called. Consider all contracts terminated.") + return + + if(SSshuttle.emergencyNoEscape) + to_chat(user, "The emergency shuttle may not be sent at this time. Please try again later.") + return + + if(SSshuttle.emergency.mode > SHUTTLE_ESCAPE) + to_chat(user, "The emergency shuttle may not be called while returning to Central Command.") + return + + if(SSticker.mode.name == "blob") + to_chat(user, "Under directive 7-10, [station_name()] is quarantined until further notice.") + return + + SSshuttle.requestEvac(user, reason) + log_game("[key_name(user)] has called the shuttle.") + message_admins("[key_name_admin(user)] has called the shuttle.", 1) + + return + +/proc/init_shift_change(var/mob/user, var/force = 0) + // if force is 0, some things may stop the shuttle call + if(!force) + if(SSshuttle.emergencyNoEscape) + to_chat(user, "Central Command does not currently have a shuttle available in your sector. Please try again later.") + return + + if(sent_strike_team == 1) + to_chat(user, "Central Command will not allow the shuttle to be called. Consider all contracts terminated.") + return + + if(world.time < 54000) // 30 minute grace period to let the game get going + to_chat(user, "The shuttle is refueling. Please wait another [round((54000-world.time)/600)] minutes before trying again.") + return + + if(SSticker.mode.name == "epidemic") + to_chat(user, "Under directive 7-10, [station_name()] is quarantined until further notice.") + return + + if(seclevel2num(get_security_level()) >= SEC_LEVEL_RED) // There is a serious threat we gotta move no time to give them five minutes. + SSshuttle.emergency.request(null, 0.5, null, " Automatic Crew Transfer", 1) + SSshuttle.emergency.canRecall = FALSE + else + SSshuttle.emergency.request(null, 1, null, " Automatic Crew Transfer", 0) + SSshuttle.emergency.canRecall = FALSE + if(user) + log_game("[key_name(user)] has called the shuttle.") + message_admins("[key_name_admin(user)] has called the shuttle - [formatJumpTo(user)].", 1) + return + + +/proc/cancel_call_proc(var/mob/user) + if(SSticker.mode.name == "meteor") + return + + if(SSshuttle.cancelEvac(user)) + log_game("[key_name(user)] has recalled the shuttle.") + message_admins("[key_name_admin(user)] has recalled the shuttle - ([ADMIN_FLW(user,"FLW")]).", 1) + else + to_chat(user, "Central Command has refused the recall request!") + log_game("[key_name(user)] has tried and failed to recall the shuttle.") + message_admins("[key_name_admin(user)] has tried and failed to recall the shuttle - ([ADMIN_FLW(user,"FLW")]).", 1) + +/proc/post_status(command, data1, data2, mob/user = null) + + var/datum/radio_frequency/frequency = SSradio.return_frequency(DISPLAY_FREQ) + + if(!frequency) return + + var/datum/signal/status_signal = new + status_signal.source = src + status_signal.transmission_method = 1 + status_signal.data["command"] = command + + switch(command) + if("message") + status_signal.data["msg1"] = data1 + status_signal.data["msg2"] = data2 + log_admin("STATUS: [user] set status screen message with [src]: [data1] [data2]") + //message_admins("STATUS: [user] set status screen with [PDA]. Message: [data1] [data2]") + if("alert") + status_signal.data["picture_state"] = data1 + + spawn(0) + frequency.post_signal(src, status_signal) + + +/obj/machinery/computer/communications/Destroy() + GLOB.shuttle_caller_list -= src + SSshuttle.autoEvac() + return ..() + +/obj/item/circuitboard/communications/New() + GLOB.shuttle_caller_list += src + ..() + +/obj/item/circuitboard/communications/Destroy() + GLOB.shuttle_caller_list -= src + SSshuttle.autoEvac() + return ..() + +/proc/print_command_report(text = "", title = "Central Command Update") + for(var/obj/machinery/computer/communications/C in GLOB.shuttle_caller_list) + if(!(C.stat & (BROKEN|NOPOWER)) && is_station_contact(C.z)) + var/obj/item/paper/P = new /obj/item/paper(C.loc) + P.name = "paper- '[title]'" + P.info = text + P.update_icon() + C.messagetitle.Add("[title]") + C.messagetext.Add(text) + for(var/datum/computer_file/program/comm/P in GLOB.shuttle_caller_list) + var/turf/T = get_turf(P.computer) + if(T && P.program_state != PROGRAM_STATE_KILLED && is_station_contact(T.z)) + if(P.computer) + var/obj/item/computer_hardware/printer/printer = P.computer.all_components[MC_PRINT] + if(printer) + printer.print_text(text, "paper- '[title]'") + P.messagetitle.Add("[title]") + P.messagetext.Add(text) + +/proc/print_centcom_report(text = "", title = "Incoming Message") + for(var/obj/machinery/computer/communications/C in GLOB.shuttle_caller_list) + if(!(C.stat & (BROKEN|NOPOWER)) && is_admin_level(C.z)) + var/obj/item/paper/P = new /obj/item/paper(C.loc) + P.name = "paper- '[title]'" + P.info = text + P.update_icon() + C.messagetitle.Add("[title]") + C.messagetext.Add(text) + for(var/datum/computer_file/program/comm/P in GLOB.shuttle_caller_list) + var/turf/T = get_turf(P.computer) + if(T && P.program_state != PROGRAM_STATE_KILLED && is_admin_level(T.z)) + if(P.computer) + var/obj/item/computer_hardware/printer/printer = P.computer.all_components[MC_PRINT] + if(printer) + printer.print_text(text, "paper- '[title]'") + P.messagetitle.Add("[title]") + P.messagetext.Add(text) diff --git a/code/game/machinery/computer/computer.dm b/code/game/machinery/computer/computer.dm index 438c8f92246..aec41c80be3 100644 --- a/code/game/machinery/computer/computer.dm +++ b/code/game/machinery/computer/computer.dm @@ -133,10 +133,10 @@ if(istype(user, /mob/dead/observer)) return 0 return ..() -/obj/machinery/computer/attackby(obj/item/I, mob/user, params) - if(isscrewdriver(I) && circuit && !(flags & NODECONSTRUCT)) - playsound(loc, I.usesound, 50, TRUE) - if(do_after(user, 20 * I.toolspeed, target = src)) - deconstruct(TRUE, user) +/obj/machinery/computer/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_start_check(user, 0)) return - return ..() \ No newline at end of file + if(circuit && !(flags & NODECONSTRUCT)) + if(I.use_tool(src, user, 20, volume = I.tool_volume)) + deconstruct(TRUE, user) diff --git a/code/game/machinery/computer/crew.dm b/code/game/machinery/computer/crew.dm index 4efdcb69ee8..a832e2b5d16 100644 --- a/code/game/machinery/computer/crew.dm +++ b/code/game/machinery/computer/crew.dm @@ -1,36 +1,36 @@ -/obj/machinery/computer/crew - name = "crew monitoring computer" - desc = "Used to monitor active health sensors built into most of the crew's uniforms." - icon_keyboard = "med_key" - icon_screen = "crew" - use_power = IDLE_POWER_USE - idle_power_usage = 250 - active_power_usage = 500 - light_color = LIGHT_COLOR_DARKBLUE - circuit = /obj/item/circuitboard/crew - var/datum/nano_module/crew_monitor/crew_monitor - -/obj/machinery/computer/crew/New() - crew_monitor = new(src) - ..() - -/obj/machinery/computer/crew/Destroy() - QDEL_NULL(crew_monitor) - return ..() - -/obj/machinery/computer/crew/attack_ai(mob/user) - attack_hand(user) - ui_interact(user) - - -/obj/machinery/computer/crew/attack_hand(mob/user) - add_fingerprint(user) - if(stat & (BROKEN|NOPOWER)) - return - ui_interact(user) - -/obj/machinery/computer/crew/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - crew_monitor.ui_interact(user, ui_key, ui, force_open) - -/obj/machinery/computer/crew/interact(mob/user) - crew_monitor.ui_interact(user) +/obj/machinery/computer/crew + name = "crew monitoring computer" + desc = "Used to monitor active health sensors built into most of the crew's uniforms." + icon_keyboard = "med_key" + icon_screen = "crew" + use_power = IDLE_POWER_USE + idle_power_usage = 250 + active_power_usage = 500 + light_color = LIGHT_COLOR_DARKBLUE + circuit = /obj/item/circuitboard/crew + var/datum/nano_module/crew_monitor/crew_monitor + +/obj/machinery/computer/crew/New() + crew_monitor = new(src) + ..() + +/obj/machinery/computer/crew/Destroy() + QDEL_NULL(crew_monitor) + return ..() + +/obj/machinery/computer/crew/attack_ai(mob/user) + attack_hand(user) + ui_interact(user) + + +/obj/machinery/computer/crew/attack_hand(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) + return + ui_interact(user) + +/obj/machinery/computer/crew/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + crew_monitor.ui_interact(user, ui_key, ui, force_open) + +/obj/machinery/computer/crew/interact(mob/user) + crew_monitor.ui_interact(user) diff --git a/code/game/machinery/computer/depot.dm b/code/game/machinery/computer/depot.dm index 02be740b7d3..62c82d9bd95 100644 --- a/code/game/machinery/computer/depot.dm +++ b/code/game/machinery/computer/depot.dm @@ -12,7 +12,7 @@ icon_keyboard = "syndie_key" icon_screen = "tcboss" light_color = LIGHT_COLOR_PURE_CYAN - req_access = list(access_syndicate) + req_access = list(ACCESS_SYNDICATE) var/security_lockout = FALSE var/sound_yes = 'sound/machines/twobeep.ogg' var/sound_no = 'sound/machines/buzz-sigh.ogg' @@ -186,7 +186,7 @@ /obj/machinery/computer/syndicate_depot/shieldcontrol name = "shield control computer" icon_screen = "accelerator" - req_access = list(access_syndicate_leader) + req_access = list(ACCESS_SYNDICATE_LEADER) alerts_when_broken = TRUE var/area/syndicate_depot/perimeter/perimeterarea @@ -543,4 +543,4 @@ to_chat(user, "[B] has been recalled.") qdel(B) raise_alert("Sentry bot removed via emergency recall.") - playsound(user, sound_yes, 50, 0) \ No newline at end of file + playsound(user, sound_yes, 50, 0) diff --git a/code/game/machinery/computer/honkputer.dm b/code/game/machinery/computer/honkputer.dm index 842a65b5f50..9f5d77a642c 100644 --- a/code/game/machinery/computer/honkputer.dm +++ b/code/game/machinery/computer/honkputer.dm @@ -6,7 +6,7 @@ icon_keyboard = "key_honk" icon_screen = "honkcomms" light_color = LIGHT_COLOR_PINK - req_access = list(access_clown) + req_access = list(ACCESS_CLOWN) circuit = /obj/item/circuitboard/HONKputer var/authenticated = 0 var/message_cooldown = 0 @@ -115,4 +115,4 @@ A.icon_state = "4" qdel(src) else - return ..() \ No newline at end of file + return ..() diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm index 3a71ab6c6c8..a3828af6be1 100644 --- a/code/game/machinery/computer/medical.dm +++ b/code/game/machinery/computer/medical.dm @@ -1,572 +1,572 @@ -#define MED_DATA_MAIN 1 // Main menu -#define MED_DATA_R_LIST 2 // Record list -#define MED_DATA_MAINT 3 // Records maintenance -#define MED_DATA_RECORD 4 // Record -#define MED_DATA_V_DATA 5 // Virus database -#define MED_DATA_MEDBOT 6 // Medbot monitor - -/obj/machinery/computer/med_data //TODO:SANITY - name = "medical records console" - desc = "This can be used to check medical records." - icon_keyboard = "med_key" - icon_screen = "medcomp" - req_one_access = list(access_medical, access_forensics_lockers) - circuit = /obj/item/circuitboard/med_data - var/obj/item/card/id/scan = null - var/authenticated = null - var/rank = null - var/screen = null - var/datum/data/record/active1 = null - var/datum/data/record/active2 = null - var/temp = null - var/printing = null - - light_color = LIGHT_COLOR_DARKBLUE - -/obj/machinery/computer/med_data/Destroy() - active1 = null - active2 = null - return ..() - -/obj/machinery/computer/med_data/attackby(obj/item/O, mob/user, params) - if(istype(O, /obj/item/card/id) && !scan) - usr.drop_item() - O.forceMove(src) - scan = O - ui_interact(user) - return - return ..() - -/obj/machinery/computer/med_data/attack_hand(mob/user) - if(..()) - return - if(is_away_level(z)) - to_chat(user, "Unable to establish a connection: You're too far away from the station!") - return - add_fingerprint(user) - ui_interact(user) - -/obj/machinery/computer/med_data/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "med_data.tmpl", name, 800, 380) - ui.open() - -/obj/machinery/computer/med_data/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - data["temp"] = temp - data["scan"] = scan ? scan.name : null - data["authenticated"] = authenticated - data["screen"] = screen - if(authenticated) - switch(screen) - if(MED_DATA_R_LIST) - if(!isnull(data_core.general)) - var/list/records = list() - data["records"] = records - for(var/datum/data/record/R in sortRecord(data_core.general)) - records[++records.len] = list("ref" = "\ref[R]", "id" = R.fields["id"], "name" = R.fields["name"]) - if(MED_DATA_RECORD) - var/list/general = list() - data["general"] = general - if(istype(active1, /datum/data/record) && data_core.general.Find(active1)) - var/list/fields = list() - general["fields"] = fields - fields[++fields.len] = list("field" = "Name:", "value" = active1.fields["name"], "edit" = null) - fields[++fields.len] = list("field" = "ID:", "value" = active1.fields["id"], "edit" = null) - fields[++fields.len] = list("field" = "Sex:", "value" = active1.fields["sex"], "edit" = "sex") - fields[++fields.len] = list("field" = "Age:", "value" = active1.fields["age"], "edit" = "age") - fields[++fields.len] = list("field" = "Fingerprint:", "value" = active1.fields["fingerprint"], "edit" = "fingerprint") - fields[++fields.len] = list("field" = "Physical Status:", "value" = active1.fields["p_stat"], "edit" = "p_stat") - fields[++fields.len] = list("field" = "Mental Status:", "value" = active1.fields["m_stat"], "edit" = "m_stat") - var/list/photos = list() - general["photos"] = photos - photos[++photos.len] = list("photo" = active1.fields["photo-south"]) - photos[++photos.len] = list("photo" = active1.fields["photo-west"]) - general["has_photos"] = (active1.fields["photo-south"] || active1.fields["photo-west"] ? 1 : 0) - general["empty"] = 0 - else - general["empty"] = 1 - - var/list/medical = list() - data["medical"] = medical - if(istype(active2, /datum/data/record) && data_core.medical.Find(active2)) - var/list/fields = list() - medical["fields"] = fields - fields[++fields.len] = list("field" = "Blood Type:", "value" = active2.fields["blood_type"], "edit" = "blood_type", "line_break" = 0) - fields[++fields.len] = list("field" = "DNA:", "value" = active2.fields["b_dna"], "edit" = "b_dna", "line_break" = 1) - fields[++fields.len] = list("field" = "Minor Disabilities:", "value" = active2.fields["mi_dis"], "edit" = "mi_dis", "line_break" = 0) - fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["mi_dis_d"], "edit" = "mi_dis_d", "line_break" = 1) - fields[++fields.len] = list("field" = "Major Disabilities:", "value" = active2.fields["ma_dis"], "edit" = "ma_dis", "line_break" = 0) - fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["ma_dis_d"], "edit" = "ma_dis_d", "line_break" = 1) - fields[++fields.len] = list("field" = "Allergies:", "value" = active2.fields["alg"], "edit" = "alg", "line_break" = 0) - fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["alg_d"], "edit" = "alg_d", "line_break" = 1) - fields[++fields.len] = list("field" = "Current Diseases:", "value" = active2.fields["cdi"], "edit" = "cdi", "line_break" = 0) - fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["cdi_d"], "edit" = "cdi_d", "line_break" = 1) - fields[++fields.len] = list("field" = "Important Notes:", "value" = active2.fields["notes"], "edit" = "notes", "line_break" = 0) - if(!active2.fields["comments"] || !islist(active2.fields["comments"])) - active2.fields["comments"] = list() - medical["comments"] = active2.fields["comments"] - medical["empty"] = 0 - else - medical["empty"] = 1 - if(MED_DATA_V_DATA) - data["virus"] = list() - for(var/D in typesof(/datum/disease)) - var/datum/disease/DS = new D(0) - if(istype(DS, /datum/disease/advance)) - continue - if(!DS.desc) - continue - data["virus"] += list(list("name" = DS.name, "D" = D)) - if(MED_DATA_MEDBOT) - data["medbots"] = list() - for(var/mob/living/simple_animal/bot/medbot/M in world) - if(M.z != z) - continue - var/turf/T = get_turf(M) - if(T) - var/medbot = list() - medbot["name"] = M.name - medbot["x"] = T.x - medbot["y"] = T.y - medbot["on"] = M.on - if(!isnull(M.reagent_glass) && M.use_beaker) - medbot["use_beaker"] = 1 - medbot["total_volume"] = M.reagent_glass.reagents.total_volume - medbot["maximum_volume"] = M.reagent_glass.reagents.maximum_volume - else - medbot["use_beaker"] = 0 - data["medbots"] += list(medbot) - return data - -/obj/machinery/computer/med_data/Topic(href, href_list) - if(..()) - return 1 - - if(!data_core.general.Find(active1)) - active1 = null - if(!data_core.medical.Find(active2)) - active2 = null - - if(href_list["temp"]) - temp = null - - if(href_list["temp_action"]) - if(href_list["temp_action"]) - var/temp_href = splittext(href_list["temp_action"], "=") - switch(temp_href[1]) - if("del_all2") - for(var/datum/data/record/R in data_core.medical) - qdel(R) - setTemp("

        All records deleted.

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

        [severity]

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

        Are you sure you wish to delete all records?

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

        Physical Condition

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

        Mental Condition

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

        Blood Type

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

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

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

        Record not found!

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

        Could not locate record [t1].

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

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

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

        All records deleted.

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

        [severity]

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

        Are you sure you wish to delete all records?

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

        Physical Condition

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

        Mental Condition

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

        Blood Type

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

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

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

        Record not found!

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

        Could not locate record [t1].

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

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

        "} + for(var/c in active2.fields["comments"]) + P.info += "[c]
        " + else + P.info += "Medical Record Lost!
        " + P.info += "
        " + P.name = "paper- 'Medical Record: [active1.fields["name"]]'" + printing = 0 + return 1 + +/obj/machinery/computer/med_data/proc/setTemp(text, list/buttons = list()) + temp = list("text" = text, "buttons" = buttons, "has_buttons" = buttons.len > 0) + +/obj/machinery/computer/med_data/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + return ..(severity) + + for(var/datum/data/record/R in data_core.medical) + if(prob(10/severity)) + switch(rand(1,6)) + if(1) + R.fields["name"] = "[pick(pick(GLOB.first_names_male), pick(GLOB.first_names_female))] [pick(GLOB.last_names)]" + if(2) + R.fields["sex"] = pick("Male", "Female") + if(3) + R.fields["age"] = rand(5, 85) + if(4) + R.fields["blood_type"] = pick("A-", "B-", "AB-", "O-", "A+", "B+", "AB+", "O+") + if(5) + R.fields["p_stat"] = pick("*SSD*", "Active", "Physically Unfit", "Disabled") + if(6) + R.fields["m_stat"] = pick("*Insane*", "*Unstable*", "*Watch*", "Stable") + continue + + else if(prob(1)) + qdel(R) + continue + + ..(severity) + + +/obj/machinery/computer/med_data/laptop + name = "medical laptop" + desc = "Cheap Nanotrasen laptop." + icon_state = "laptop" + icon_keyboard = "laptop_key" + icon_screen = "medlaptop" + density = 0 + +#undef MED_DATA_MAIN +#undef MED_DATA_R_LIST +#undef MED_DATA_MAINT +#undef MED_DATA_RECORD +#undef MED_DATA_V_DATA +#undef MED_DATA_MEDBOT diff --git a/code/game/machinery/computer/message.dm b/code/game/machinery/computer/message.dm index fef40c9f93a..55107809ce3 100644 --- a/code/game/machinery/computer/message.dm +++ b/code/game/machinery/computer/message.dm @@ -35,11 +35,8 @@ light_color = LIGHT_COLOR_DARKGREEN -/obj/machinery/computer/message_monitor/attackby(obj/item/O as obj, mob/living/user as mob, params) - if(!istype(user)) - return - if(isscrewdriver(O) && emag) - //Stops people from just unscrewing the monitor and putting it back to get the console working again. +/obj/machinery/computer/message_monitor/screwdriver_act(mob/user, obj/item/I) + if(emag) //Stops people from just unscrewing the monitor and putting it back to get the console working again. to_chat(user, "It is too hot to mess with!") return return ..() diff --git a/code/game/machinery/computer/pod.dm b/code/game/machinery/computer/pod.dm index fdb9f934516..6ea095028a2 100644 --- a/code/game/machinery/computer/pod.dm +++ b/code/game/machinery/computer/pod.dm @@ -268,7 +268,7 @@ /obj/machinery/computer/pod/old/syndicate name = "external airlock controls" desc = "The Syndicate operate on a tight budget. Operates external airlocks." - req_access = list(access_syndicate) + req_access = list(ACCESS_SYNDICATE) circuit = /obj/item/circuitboard/syndicatedoor light_color = "#00FFFF" diff --git a/code/game/machinery/computer/pod_tracking_console.dm b/code/game/machinery/computer/pod_tracking_console.dm index ae6e7a4f36a..cd6450c700c 100644 --- a/code/game/machinery/computer/pod_tracking_console.dm +++ b/code/game/machinery/computer/pod_tracking_console.dm @@ -4,7 +4,7 @@ icon_keyboard = "tech_key" icon_screen = "rdcomp" light_color = LIGHT_COLOR_PURPLE - req_access = list(access_robotics) + req_access = list(ACCESS_ROBOTICS) circuit = /obj/item/circuitboard/pod_locater /obj/machinery/computer/podtracker/attack_ai(var/mob/user as mob) diff --git a/code/game/machinery/computer/power.dm b/code/game/machinery/computer/power.dm index 416c73d2570..fce315a1db0 100644 --- a/code/game/machinery/computer/power.dm +++ b/code/game/machinery/computer/power.dm @@ -1,65 +1,65 @@ -/obj/machinery/computer/monitor - name = "power monitoring console" - desc = "Used to monitor power levels across the station." - icon_screen = "power" - icon_keyboard = "power_key" - use_power = ACTIVE_POWER_USE - idle_power_usage = 20 - active_power_usage = 80 - light_color = LIGHT_COLOR_ORANGE - circuit = /obj/item/circuitboard/powermonitor - var/datum/powernet/powernet = null - var/datum/nano_module/power_monitor/power_monitor - var/is_secret_monitor = FALSE - -/obj/machinery/computer/monitor/secret //Hides the power monitor (such as ones on ruins & CentCom) from PDA's to prevent metagaming. - name = "outdated power monitoring console" - desc = "It monitors power levels across the local powernet." - circuit = /obj/item/circuitboard/powermonitor/secret - is_secret_monitor = TRUE - -/obj/machinery/computer/monitor/New() - ..() - GLOB.power_monitors += src - GLOB.power_monitors = sortAtom(GLOB.power_monitors) - power_monitor = new(src) - -/obj/machinery/computer/monitor/Initialize() - ..() - powermonitor_repository.update_cache() - powernet = find_powernet() - -/obj/machinery/computer/monitor/Destroy() - GLOB.power_monitors -= src - powermonitor_repository.update_cache() - QDEL_NULL(power_monitor) - return ..() - -/obj/machinery/computer/monitor/power_change() - ..() - powermonitor_repository.update_cache() - -/obj/machinery/computer/monitor/proc/find_powernet() - var/obj/structure/cable/attached = null - var/turf/T = loc - if(isturf(T)) - attached = locate() in T - if(attached) - return attached.powernet - -/obj/machinery/computer/monitor/attack_ai(mob/user) - attack_hand(user) - -/obj/machinery/computer/monitor/attack_hand(mob/user) - add_fingerprint(user) - if(stat & (BROKEN|NOPOWER)) - return - // Update the powernet - powernet = find_powernet() - ui_interact(user) - -/obj/machinery/computer/monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - power_monitor.ui_interact(user, ui_key, ui, force_open) - -/obj/machinery/computer/monitor/interact(mob/user) - power_monitor.ui_interact(user) +/obj/machinery/computer/monitor + name = "power monitoring console" + desc = "Used to monitor power levels across the station." + icon_screen = "power" + icon_keyboard = "power_key" + use_power = ACTIVE_POWER_USE + idle_power_usage = 20 + active_power_usage = 80 + light_color = LIGHT_COLOR_ORANGE + circuit = /obj/item/circuitboard/powermonitor + var/datum/powernet/powernet = null + var/datum/nano_module/power_monitor/power_monitor + var/is_secret_monitor = FALSE + +/obj/machinery/computer/monitor/secret //Hides the power monitor (such as ones on ruins & CentCom) from PDA's to prevent metagaming. + name = "outdated power monitoring console" + desc = "It monitors power levels across the local powernet." + circuit = /obj/item/circuitboard/powermonitor/secret + is_secret_monitor = TRUE + +/obj/machinery/computer/monitor/New() + ..() + GLOB.power_monitors += src + GLOB.power_monitors = sortAtom(GLOB.power_monitors) + power_monitor = new(src) + +/obj/machinery/computer/monitor/Initialize() + ..() + powermonitor_repository.update_cache() + powernet = find_powernet() + +/obj/machinery/computer/monitor/Destroy() + GLOB.power_monitors -= src + powermonitor_repository.update_cache() + QDEL_NULL(power_monitor) + return ..() + +/obj/machinery/computer/monitor/power_change() + ..() + powermonitor_repository.update_cache() + +/obj/machinery/computer/monitor/proc/find_powernet() + var/obj/structure/cable/attached = null + var/turf/T = loc + if(isturf(T)) + attached = locate() in T + if(attached) + return attached.powernet + +/obj/machinery/computer/monitor/attack_ai(mob/user) + attack_hand(user) + +/obj/machinery/computer/monitor/attack_hand(mob/user) + add_fingerprint(user) + if(stat & (BROKEN|NOPOWER)) + return + // Update the powernet + powernet = find_powernet() + ui_interact(user) + +/obj/machinery/computer/monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + power_monitor.ui_interact(user, ui_key, ui, force_open) + +/obj/machinery/computer/monitor/interact(mob/user) + power_monitor.ui_interact(user) diff --git a/code/game/machinery/computer/prisoner.dm b/code/game/machinery/computer/prisoner.dm index bcabdeb511e..42903d48ecd 100644 --- a/code/game/machinery/computer/prisoner.dm +++ b/code/game/machinery/computer/prisoner.dm @@ -3,7 +3,7 @@ icon = 'icons/obj/computer.dmi' icon_keyboard = "security_key" icon_screen = "explosive" - req_access = list(access_armory) + req_access = list(ACCESS_ARMORY) circuit = /obj/item/circuitboard/prisoner var/id = 0.0 var/temp = null diff --git a/code/game/machinery/computer/robot.dm b/code/game/machinery/computer/robot.dm index 8def58c370b..8ecd463bc35 100644 --- a/code/game/machinery/computer/robot.dm +++ b/code/game/machinery/computer/robot.dm @@ -1,240 +1,240 @@ -/obj/machinery/computer/robotics - name = "robotics control console" - desc = "Used to remotely lockdown or detonate linked Cyborgs." - icon = 'icons/obj/computer.dmi' - icon_keyboard = "tech_key" - icon_screen = "robot" - req_access = list(access_robotics) - circuit = /obj/item/circuitboard/robotics - var/temp = null - - light_color = LIGHT_COLOR_PURPLE - - var/safety = 1 - -/obj/machinery/computer/robotics/attack_ai(var/mob/user as mob) - return attack_hand(user) - -/obj/machinery/computer/robotics/attack_hand(var/mob/user as mob) - if(..()) - return - if(stat & (NOPOWER|BROKEN)) - return - ui_interact(user) - -/obj/machinery/computer/robotics/proc/is_authenticated(var/mob/user as mob) - if(user.can_admin_interact()) - return 1 - else if(allowed(user)) - return 1 - return 0 - -/obj/machinery/computer/robotics/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "robot_control.tmpl", "Robotic Control Console", 400, 500) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/computer/robotics/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - var/list/robots = get_cyborgs(user) - if(robots.len) - data["robots"] = robots - data["safety"] = safety - // Also applies for cyborgs. Hides the manual self-destruct button. - data["is_ai"] = issilicon(user) - data["allowed"] = is_authenticated(user) - return data - -/obj/machinery/computer/robotics/Topic(href, href_list) - if(..()) - return 1 - - var/mob/user = usr - if(!is_authenticated(user)) - to_chat(user, "Access denied.") - return - - // Destroys the cyborg - if(href_list["detonate"]) - var/mob/living/silicon/robot/target = get_cyborg_by_name(href_list["detonate"]) - if(!target || !istype(target)) - return - if(isAI(user) && (target.connected_ai != user)) - to_chat(user, "Access Denied. This robot is not linked to you.") - return - // Cyborgs may blow up themselves via the console - if((isrobot(user) && user != target) || !is_authenticated(user)) - to_chat(user, "Access Denied.") - return - var/choice = input("Really detonate [target.name]?") in list ("Yes", "No") - if(choice != "Yes") - return - if(!target || !istype(target)) - return - - // Antagonistic cyborgs? Left here for downstream - if(target.mind && target.mind.special_role && target.emagged) - to_chat(target, "Extreme danger. Termination codes detected. Scrambling security codes and automatic AI unlink triggered.") - target.ResetSecurityCodes() - else - message_admins("[key_name_admin(usr)] detonated [key_name_admin(target)] (JMP)!") - log_game("\[key_name(usr)] detonated [key_name(target)]!") - to_chat(target, "Self-destruct command received.") - if(target.connected_ai) - to_chat(target.connected_ai, "

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

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

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

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

        All records deleted.

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

        All cell logs deleted.

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

        Error: permission denied.

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

        Record not found!

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

        Are you sure you wish to delete all records?

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

        Are you sure you wish to delete all cell logs?

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

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

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

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

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

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

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

        Criminal Status

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

        Rank

        ", buttons) - else - setTemp("

        You do not have the required rank to do this!

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

        All records deleted.

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

        All cell logs deleted.

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

        Error: permission denied.

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

        Record not found!

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

        Are you sure you wish to delete all records?

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

        Are you sure you wish to delete all cell logs?

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

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

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

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

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

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

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

        Criminal Status

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

        Rank

        ", buttons) + else + setTemp("

        You do not have the required rank to do this!

        ") + if("species") + if(istype(active1, /datum/data/record)) + var/t1 = copytext(trim(sanitize(input("Please enter race:", "General records", active1.fields["species"], null) as message)), 1, MAX_MESSAGE_LEN) + if(!t1 || ..() || active1 != a1) + return 1 + active1.fields["species"] = t1 + return 1 + +/obj/machinery/computer/secure_data/proc/setTemp(text, list/buttons = list()) + temp = list("text" = text, "buttons" = buttons, "has_buttons" = buttons.len > 0) + +/* Proc disabled due to BYOND Issue + +/obj/machinery/computer/secure_data/proc/create_record_photo(datum/data/record/R) + // basically copy-pasted from the camera code but different enough that it has to be redone + var/icon/photoimage = get_record_photo(R) + var/icon/small_img = icon(photoimage) + var/icon/tiny_img = icon(photoimage) + var/icon/ic = icon('icons/obj/items.dmi',"photo") + var/icon/pc = icon('icons/obj/bureaucracy.dmi', "photo") + small_img.Scale(8, 8) + tiny_img.Scale(4, 4) + ic.Blend(small_img, ICON_OVERLAY, 10, 13) + pc.Blend(tiny_img, ICON_OVERLAY, 12, 19) + + var/datum/picture/P = new() + P.fields["name"] = "File Photo - [R.fields["name"]]" + P.fields["author"] = "Central Command" + P.fields["icon"] = ic + P.fields["tiny"] = pc + P.fields["img"] = photoimage + P.fields["desc"] = "You can see [R.fields["name"]] on the photo." + P.fields["pixel_x"] = rand(-10, 10) + P.fields["pixel_y"] = rand(-10, 10) + P.fields["size"] = 2 + + var/obj/item/photo/PH = new/obj/item/photo(loc) + PH.construct(P) + +*/ + +/obj/machinery/computer/secure_data/proc/get_record_photo(datum/data/record/R) + // similar to the code to make a photo, but of course the actual rendering is completely different + var/icon/res = icon('icons/effects/96x96.dmi', "") + // will be 2x2 to fit the 2 directions + res.Scale(2 * 32, 2 * 32) + // transparent background (it's a plastic transparency, you see) with the front and side icons + res.Blend(icon(R.fields["photo"], dir = SOUTH), ICON_OVERLAY, 1, 17) + res.Blend(icon(R.fields["photo"], dir = WEST), ICON_OVERLAY, 33, 17) + + return res + +/obj/machinery/computer/secure_data/emp_act(severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + + for(var/datum/data/record/R in data_core.security) + if(prob(10/severity)) + switch(rand(1,6)) + if(1) + R.fields["name"] = "[pick(pick(GLOB.first_names_male), pick(GLOB.first_names_female))] [pick(GLOB.last_names)]" + if(2) + R.fields["sex"] = pick("Male", "Female") + if(3) + R.fields["age"] = rand(5, 85) + if(4) + R.fields["criminal"] = pick("None", "*Arrest*", "Incarcerated", "Parolled", "Released") + if(5) + R.fields["p_stat"] = pick("*Unconcious*", "Active", "Physically Unfit") + if(6) + R.fields["m_stat"] = pick("*Insane*", "*Unstable*", "*Watch*", "Stable") + continue + + else if(prob(1)) + qdel(R) + continue + + ..(severity) + +/obj/machinery/computer/secure_data/detective_computer + icon = 'icons/obj/computer.dmi' + icon_state = "messyfiles" + +/obj/machinery/computer/secure_data/laptop + name = "security laptop" + desc = "Nanotrasen Security laptop. Bringing modern compact computing to this century!" + icon_state = "laptop" + icon_keyboard = "seclaptop_key" + icon_screen = "seclaptop" + density = 0 + +#undef SEC_DATA_R_LIST +#undef SEC_DATA_MAINT +#undef SEC_DATA_RECORD diff --git a/code/game/machinery/computer/skills.dm b/code/game/machinery/computer/skills.dm index 125b5294860..e3c2918f335 100644 --- a/code/game/machinery/computer/skills.dm +++ b/code/game/machinery/computer/skills.dm @@ -10,7 +10,7 @@ icon_screen = "medlaptop" density = 0 light_color = LIGHT_COLOR_GREEN - req_one_access = list(access_heads) + req_one_access = list(ACCESS_HEADS) circuit = /obj/item/circuitboard/skills var/obj/item/card/id/scan = null var/authenticated = null diff --git a/code/game/machinery/computer/specops_shuttle.dm b/code/game/machinery/computer/specops_shuttle.dm index 0602757944f..23658a3e939 100644 --- a/code/game/machinery/computer/specops_shuttle.dm +++ b/code/game/machinery/computer/specops_shuttle.dm @@ -1,340 +1,340 @@ -//Config stuff -#define SPECOPS_MOVETIME 600 //Time to station is milliseconds. 60 seconds, enough time for everyone to be on the shuttle before it leaves. -#define SPECOPS_STATION_AREATYPE "/area/shuttle/specops/station" //Type of the spec ops shuttle area for station -#define SPECOPS_DOCK_AREATYPE "/area/shuttle/specops/centcom" //Type of the spec ops shuttle area for dock -#define SPECOPS_RETURN_DELAY 6000 //Time between the shuttle is capable of moving. - -var/specops_shuttle_moving_to_station = 0 -var/specops_shuttle_moving_to_centcom = 0 -var/specops_shuttle_at_station = 0 -var/specops_shuttle_can_send = 1 -var/specops_shuttle_time = 0 -var/specops_shuttle_timeleft = 0 - -/obj/machinery/computer/specops_shuttle - name = "\improper Spec. Ops. shuttle console" - icon = 'icons/obj/computer.dmi' - icon_keyboard = "security_key" - icon_screen = "syndishuttle" - light_color = LIGHT_COLOR_PURE_CYAN - req_access = list(access_cent_specops) -// req_access = list(ACCESS_CENT_SPECOPS) - var/temp = null - var/hacked = 0 - var/allowedtocall = 0 - var/specops_shuttle_timereset = 0 - -/proc/specops_return() - var/obj/item/radio/intercom/announcer = new /obj/item/radio/intercom(null)//We need a fake AI to announce some stuff below. Otherwise it will be wonky. - announcer.config(list("Response Team" = 0)) - - var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values. - var/message = "\"THE SPECIAL OPERATIONS SHUTTLE IS PREPARING TO RETURN\""//Initial message shown. - if(announcer) - announcer.autosay(message, "A.L.I.C.E.", "Response Team", list(1,2)) - - while(specops_shuttle_time - world.timeofday > 0) - var/ticksleft = specops_shuttle_time - world.timeofday - - if(ticksleft > 1e5) - specops_shuttle_time = world.timeofday + 10 // midnight rollover - specops_shuttle_timeleft = (ticksleft / 10) - - //All this does is announce the time before launch. - if(announcer) - var/rounded_time_left = round(specops_shuttle_timeleft)//Round time so that it will report only once, not in fractions. - if(rounded_time_left in message_tracker)//If that time is in the list for message announce. - message = "\"ALERT: [rounded_time_left] SECOND[(rounded_time_left!=1)?"S":""] REMAIN\"" - if(rounded_time_left==0) - message = "\"ALERT: TAKEOFF\"" - announcer.autosay(message, "A.L.I.C.E.", "Response Team") - message_tracker -= rounded_time_left//Remove the number from the list so it won't be called again next cycle. - //Should call all the numbers but lag could mean some issues. Oh well. Not much I can do about that. - - sleep(5) - - specops_shuttle_moving_to_station = 0 - specops_shuttle_moving_to_centcom = 0 - - specops_shuttle_at_station = 1 - - var/area/start_location = locate(/area/shuttle/specops/station) - var/area/end_location = locate(/area/shuttle/specops/centcom) - - var/list/dstturfs = list() - var/throwy = world.maxy - - for(var/turf/T in end_location) - dstturfs += T - if(T.y < throwy) - throwy = T.y - - // hey you, get out of the way! - for(var/turf/T in dstturfs) - // find the turf to move things to - var/turf/D = locate(T.x, throwy - 1, 1) - //var/turf/E = get_step(D, SOUTH) - for(var/atom/movable/AM as mob|obj in T) - AM.Move(D) - if(istype(T, /turf/simulated)) - qdel(T) - - for(var/mob/living/carbon/bug in end_location) // If someone somehow is still in the shuttle's docking area... - bug.gib() - - for(var/mob/living/simple_animal/pest in end_location) // And for the other kind of bug... - pest.gib() - - start_location.move_contents_to(end_location) - - for(var/turf/T in get_area_turfs(end_location) ) - var/mob/M = locate(/mob) in T - to_chat(M, "You have arrived at Central Command. Operation has ended!") - - specops_shuttle_at_station = 0 - - for(var/obj/machinery/computer/specops_shuttle/S in world) - S.specops_shuttle_timereset = world.time + SPECOPS_RETURN_DELAY - - qdel(announcer) - -/proc/specops_process() - var/area/centcom/specops/special_ops = locate()//Where is the specops area located? - var/obj/item/radio/intercom/announcer = new /obj/item/radio/intercom(null)//We need a fake AI to announce some stuff below. Otherwise it will be wonky. - announcer.config(list("Response Team" = 0)) - - var/message_tracker[] = list(0,1,2,3,5,10,30,45)//Create a a list with potential time values. - var/message = "\"THE SPECIAL OPERATIONS SHUTTLE IS PREPARING FOR LAUNCH\""//Initial message shown. - if(announcer) - announcer.autosay(message, "A.L.I.C.E.", "Response Team") - //message = "ARMORED SQUAD TAKE YOUR POSITION ON GRAVITY LAUNCH PAD" - //announcer.autosay(message, "A.L.I.C.E.", "Response Team") - - while(specops_shuttle_time - world.timeofday > 0) - var/ticksleft = specops_shuttle_time - world.timeofday - - if(ticksleft > 1e5) - specops_shuttle_time = world.timeofday + 10 // midnight rollover - specops_shuttle_timeleft = (ticksleft / 10) - - //All this does is announce the time before launch. - if(announcer) - var/rounded_time_left = round(specops_shuttle_timeleft)//Round time so that it will report only once, not in fractions. - if(rounded_time_left in message_tracker)//If that time is in the list for message announce. - message = "\"ALERT: [rounded_time_left] SECOND[(rounded_time_left!=1)?"S":""] REMAIN\"" - if(rounded_time_left==0) - message = "\"ALERT: TAKEOFF\"" - announcer.autosay(message, "A.L.I.C.E.", "Response Team") - message_tracker -= rounded_time_left//Remove the number from the list so it won't be called again next cycle. - //Should call all the numbers but lag could mean some issues. Oh well. Not much I can do about that. - - sleep(5) - - specops_shuttle_moving_to_station = 0 - specops_shuttle_moving_to_centcom = 0 - - specops_shuttle_at_station = 1 - if(specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) return - - if(!specops_can_move()) - to_chat(usr, "The Special Operations shuttle is unable to leave.") - return - - //Begin Marauder launchpad. - spawn(0)//So it parallel processes it. - for(var/obj/machinery/door/poddoor/M in special_ops) - switch(M.id_tag) - if("ASSAULT0") - spawn(10)//1 second delay between each. - M.open() - if("ASSAULT1") - spawn(20) - M.open() - if("ASSAULT2") - spawn(30) - M.open() - if("ASSAULT3") - spawn(40) - M.open() - - sleep(10) - - var/spawn_marauder[] = new() - for(var/obj/effect/landmark/L in world) - if(L.name == "Marauder Entry") - spawn_marauder.Add(L.loc) - for(var/obj/effect/landmark/L in world) - if(L.name == "Marauder Exit") - var/obj/effect/portal/P = new(L.loc, pick(spawn_marauder)) - //P.invisibility = 101//So it is not seen by anyone. - P.failchance = 0//So it has no fail chance when teleporting. - spawn_marauder.Remove(P.target) - - sleep(10) - - for(var/obj/machinery/mass_driver/M in special_ops) - switch(M.id_tag) - if("ASSAULT0") - spawn(10) - M.drive() - if("ASSAULT1") - spawn(20) - M.drive() - if("ASSAULT2") - spawn(30) - M.drive() - if("ASSAULT3") - spawn(40) - M.drive() - - sleep(50)//Doors remain open for 5 seconds. - - for(var/obj/machinery/door/poddoor/M in special_ops) - switch(M.id_tag)//Doors close at the same time. - if("ASSAULT0") - spawn(0) - M.close() - if("ASSAULT1") - spawn(0) - M.close() - if("ASSAULT2") - spawn(0) - M.close() - if("ASSAULT3") - spawn(0) - M.close() - special_ops.readyreset()//Reset firealarm after the team launched. - //End Marauder launchpad. - - var/area/start_location = locate(/area/shuttle/specops/centcom) - var/area/end_location = locate(/area/shuttle/specops/station) - - var/list/dstturfs = list() - var/throwy = world.maxy - - for(var/turf/T in end_location) - dstturfs += T - if(T.y < throwy) - throwy = T.y - - // hey you, get out of the way! - for(var/turf/T in dstturfs) - // find the turf to move things to - var/turf/D = locate(T.x, throwy - 1, 1) - //var/turf/E = get_step(D, SOUTH) - for(var/atom/movable/AM as mob|obj in T) - AM.Move(D) - if(istype(T, /turf/simulated)) - qdel(T) - - start_location.move_contents_to(end_location) - - for(var/turf/T in get_area_turfs(end_location) ) - var/mob/M = locate(/mob) in T - to_chat(M, "You have arrived to [station_name()]. Commence operation!") - - for(var/obj/machinery/computer/specops_shuttle/S in world) - S.specops_shuttle_timereset = world.time + SPECOPS_RETURN_DELAY - - qdel(announcer) - -/proc/specops_can_move() - if(specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) - return 0 - for(var/obj/machinery/computer/specops_shuttle/S in world) - if(world.timeofday <= S.specops_shuttle_timereset) - return 0 - return 1 - -/obj/machinery/computer/specops_shuttle/attack_ai(var/mob/user as mob) - to_chat(user, "Access Denied.") - return 1 - -/obj/machinery/computer/specops_shuttle/attackby(I as obj, user as mob, params) - if(istype(I,/obj/item/card/emag)) - to_chat(user, "The electronic systems in this console are far too advanced for your primitive hacking peripherals.") - else - return ..() - -/obj/machinery/computer/specops_shuttle/attack_hand(var/mob/user as mob) - if(!allowed(user)) - to_chat(user, "Access Denied.") - return - -//Commented out so admins can do shenanigans at their leisure. Also makes the force-spawned admin ERTs able to use the shuttle. -// if(sent_strike_team == 0 && send_emergency_team == 0) -// to_chat(usr, "The strike team has not yet deployed.") -// return - - if(..()) - return - - user.machine = src - var/dat - if(temp) - dat = temp - else - dat += {"
        Special Operations Shuttle
        - \nLocation: [specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom ? "Departing for [station_name()] in ([specops_shuttle_timeleft] seconds.)":specops_shuttle_at_station ? "Station":"Dock"]
        - [specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom ? "\n*The Special Ops. shuttle is already leaving.*
        \n
        ":specops_shuttle_at_station ? "\nShuttle standing by...
        \n
        ":"\nDepart to [station_name()]
        \n
        "] - \nClose"} - - user << browse(dat, "window=computer;size=575x450") - onclose(user, "computer") - return - -/obj/machinery/computer/specops_shuttle/Topic(href, href_list) - if(..()) - return 1 - - if((usr.contents.Find(src) || (in_range(src, usr) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon))) - usr.machine = src - - if(href_list["sendtodock"]) - if(!specops_shuttle_at_station|| specops_shuttle_moving_to_station || specops_shuttle_moving_to_centcom) return - - if(!specops_can_move()) - to_chat(usr, "Central Command will not allow the Special Operations shuttle to return yet.") - if(world.timeofday <= specops_shuttle_timereset) - if(((world.timeofday - specops_shuttle_timereset)/10) > 60) - to_chat(usr, "[-((world.timeofday - specops_shuttle_timereset)/10)/60] minutes remain!") - to_chat(usr, "[-(world.timeofday - specops_shuttle_timereset)/10] seconds remain!") - return - - to_chat(usr, "The Special Operations shuttle will arrive at Central Command in [(SPECOPS_MOVETIME/10)] seconds.") - - temp += "Shuttle departing.

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

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

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

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

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

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

        Close

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

        Close

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



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

        -
        Admission data:

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


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

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



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

        +
        Admission data:

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


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

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

        Explosive Log Report

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

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

        Explosive Log Report

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

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

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

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

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

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

                    +(swipe card to lock controls)
                    + +
                    +Location: [location ? location : "None"]
                    +Transponder Codes:
                      "} + + for(var/key in codes) + t += "
                    • [key] ... [codes[key]]" + t += " Edit" + t += " Delete
                      " + t += " Add New
                      " + t+= "
                        " + + var/datum/browser/popup = new(user, "navbeacon", "Navigation Beacon", 300, 400) + popup.set_content(t) + popup.open() + return + +/obj/machinery/navbeacon/Topic(href, href_list) + if(..()) + return + if(open && !locked) + usr.set_machine(src) + + if(href_list["locedit"]) + var/newloc = copytext(sanitize(input("Enter New Location", "Navigation Beacon", location) as text|null),1,MAX_MESSAGE_LEN) + if(newloc) + location = newloc + updateDialog() + + else if(href_list["edit"]) + var/codekey = href_list["code"] + + var/newkey = stripped_input(usr, "Enter Transponder Code Key", "Navigation Beacon", codekey) + if(!newkey) + return + + var/codeval = codes[codekey] + var/newval = stripped_input(usr, "Enter Transponder Code Value", "Navigation Beacon", codeval) + if(!newval) + newval = codekey + return + + codes.Remove(codekey) + codes[newkey] = newval + + updateDialog() + + else if(href_list["delete"]) + var/codekey = href_list["code"] + codes.Remove(codekey) + updateDialog() + + else if(href_list["add"]) + + var/newkey = stripped_input(usr, "Enter New Transponder Code Key", "Navigation Beacon") + if(!newkey) + return + + var/newval = stripped_input(usr, "Enter New Transponder Code Value", "Navigation Beacon") + if(!newval) + newval = "1" + return + + if(!codes) + codes = new() + + codes[newkey] = newval + + updateDialog() + + +/obj/machinery/navbeacon/invisible + invisibility = INVISIBILITY_MAXIMUM + +/obj/machinery/navbeacon/invisible/hide(intact) + invisibility = INVISIBILITY_MAXIMUM + updateicon() diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm index fba07d684ac..61d96602343 100644 --- a/code/game/machinery/newscaster.dm +++ b/code/game/machinery/newscaster.dm @@ -568,41 +568,29 @@ var/list/obj/machinery/newscaster/allCasters = list() //Global list that will co SSnanoui.update_uis(src) return 1 -/obj/machinery/newscaster/attackby(obj/item/I, mob/living/user, params) - if(istype(I, /obj/item/wrench)) - to_chat(user, "Now [anchored ? "un" : ""]securing [name]") - playsound(loc, I.usesound, 50, 1) - if(do_after(user, 60 * I.toolspeed, target = src)) - playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE) - if(stat & BROKEN) - to_chat(user, "The broken remains of [src] fall on the ground.") - new /obj/item/stack/sheet/metal(loc, 5) - new /obj/item/shard(loc) - new /obj/item/shard(loc) - else - to_chat(user, "You [anchored ? "un" : ""]secure [name].") - new /obj/item/mounted/frame/newscaster_frame(loc) - qdel(src) - else if(iswelder(I) && user.a_intent != INTENT_HARM) - var/obj/item/weldingtool/WT = I - if(stat & BROKEN) - if(WT.remove_fuel(0, user)) - user.visible_message("[user] is repairing [src].", - "You begin repairing [src]...", - "You hear welding.") - playsound(loc, WT.usesound, 40, 1) - if(do_after(user,40 * WT.toolspeed, 1, target = src)) - if(!WT.isOn() || !(stat & BROKEN)) - return - to_chat(user, "You repair [src].") - playsound(loc, 'sound/items/welder2.ogg', 50, 1) - obj_integrity = max_integrity - stat &= ~BROKEN - update_icon() - else - to_chat(user, "[src] does not need repairs.") +/obj/machinery/newscaster/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + to_chat(user, "Now [anchored ? "un" : ""]securing [name]") + if(!I.use_tool(src, user, 60, volume = I.tool_volume)) + return + playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE) + if(stat & BROKEN) + to_chat(user, "The broken remains of [src] fall on the ground.") + new /obj/item/stack/sheet/metal(loc, 5) + new /obj/item/shard(loc) + new /obj/item/shard(loc) else - return ..() + to_chat(user, "You [anchored ? "un" : ""]secure [name].") + new /obj/item/mounted/frame/newscaster_frame(loc) + qdel(src) + +/obj/machinery/newscaster/welder_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + default_welder_repair(user, I) /obj/machinery/newscaster/play_attack_sound(damage, damage_type = BRUTE, damage_flag = 0) switch(damage_type) diff --git a/code/game/machinery/overview.dm b/code/game/machinery/overview.dm index e77eef869dc..7165cceb1ff 100644 --- a/code/game/machinery/overview.dm +++ b/code/game/machinery/overview.dm @@ -1,362 +1,362 @@ -//#define AMAP - -/obj/machinery/computer/security/verb/station_map() - set name = ".map" - set category = "Object" - set src in view(1) - usr.set_machine(src) - if(!mapping) return - - log_game("[usr]([usr.key]) used station map L[z] in [src.loc.loc]") - - src.drawmap(usr) - -/obj/machinery/computer/security/proc/drawmap(var/mob/user as mob) - - var/icx = round(world.maxx/16) + 1 - var/icy = round(world.maxy/16) + 1 - - var/xoff = round( (icx*16-world.maxx)-2) - var/yoff = round( (icy*16-world.maxy)-2) - - var/icount = icx * icy - - - var/list/imap = list() - -#ifdef AMAP - - for(var/i = 0; i
        - - - - -
        - [left_part] - - [list_queue()] -
        "} - var/datum/browser/popup = new(user, "mecha_fabricator", name, 1000, 600) - popup.set_content(dat) - popup.open(0) - onclose(user, "mecha_fabricator") - return - -/obj/machinery/mecha_part_fabricator/Topic(href, href_list) - if(..()) - return 1 - var/datum/topic_input/afilter = new /datum/topic_input(href,href_list) - if(href_list["part_set"]) - var/tpart_set = afilter.getStr("part_set") - if(tpart_set) - if(tpart_set=="clear") - part_set = null - else - part_set = tpart_set - screen = "parts" - if(href_list["part"]) - var/T = afilter.getStr("part") - for(var/v in files.known_designs) - var/datum/design/D = files.known_designs[v] - if(D.build_type & fabricator_type) - if(D.id == T) - if(!processing_queue) - build_part(D) - else - add_to_queue(D) - break - if(href_list["add_to_queue"]) - var/T = afilter.getStr("add_to_queue") - for(var/v in files.known_designs) - var/datum/design/D = files.known_designs[v] - if(D.build_type & fabricator_type) - if(D.id == T) - add_to_queue(D) - break - return update_queue_on_page() - if(href_list["remove_from_queue"]) - remove_from_queue(afilter.getNum("remove_from_queue")) - return update_queue_on_page() - if(href_list["partset_to_queue"]) - add_part_set_to_queue(afilter.get("partset_to_queue")) - return update_queue_on_page() - if(href_list["process_queue"]) - spawn(0) - if(processing_queue || being_built) - return FALSE - processing_queue = 1 - process_queue() - processing_queue = 0 - if(href_list["clear_temp"]) - temp = null - if(href_list["screen"]) - screen = href_list["screen"] - if(href_list["queue_move"] && href_list["index"]) - var/index = afilter.getNum("index") - var/new_index = index + afilter.getNum("queue_move") - if(isnum(index) && isnum(new_index)) - if(IsInRange(new_index,1,queue.len)) - queue.Swap(index,new_index) - return update_queue_on_page() - if(href_list["clear_queue"]) - queue = list() - return update_queue_on_page() - if(href_list["sync"]) - sync() - if(href_list["part_desc"]) - var/T = afilter.getStr("part_desc") - for(var/v in files.known_designs) - var/datum/design/D = files.known_designs[v] - if(D.build_type & fabricator_type) - if(D.id == T) - var/obj/part = D.build_path - temp = {"

        [initial(part.name)] description:

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

        [initial(part.name)] description:

        + [initial(part.desc)]
        + Return + "} + break + + if(href_list["remove_mat"] && href_list["material"]) + GET_COMPONENT(materials, /datum/component/material_container) + materials.retrieve_sheets(text2num(href_list["remove_mat"]), href_list["material"]) + + updateUsrDialog() + return + +/obj/machinery/mecha_part_fabricator/proc/AfterMaterialInsert(type_inserted, id_inserted, amount_inserted) + var/stack_name = material2name(id_inserted) + overlays += "fab-load-[stack_name]" + sleep(10) + overlays -= "fab-load-[stack_name]" + updateUsrDialog() + +/obj/machinery/mecha_part_fabricator/attackby(obj/item/W, mob/user, params) + if(default_deconstruction_screwdriver(user, "fab-o", "fab-idle", W)) + return + + if(exchange_parts(user, W)) + return + + if(default_deconstruction_crowbar(user, W)) + return TRUE + + else + return ..() + +/obj/machinery/mecha_part_fabricator/proc/material2name(ID) + return copytext(ID,2) + +/obj/machinery/mecha_part_fabricator/proc/is_insertion_ready(mob/user) + if(panel_open) + to_chat(user, "You can't load [src] while it's opened!") + return FALSE + if(being_built) + to_chat(user, "\The [src] is currently processing! Please wait until completion.") + return FALSE + + return TRUE + +/obj/machinery/mecha_part_fabricator/spacepod + name = "spacepod fabricator" + fabricator_type = PODFAB + part_sets = list( "Pod_Weaponry", + "Pod_Armor", + "Pod_Cargo", + "Pod_Parts", + "Pod_Frame", + "Misc") + req_access = list(ACCESS_MECHANIC) + +/obj/machinery/mecha_part_fabricator/spacepod/New() + ..() + QDEL_LIST(component_parts) + component_parts = list() + component_parts += new /obj/item/circuitboard/podfab(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stock_parts/micro_laser(null) + component_parts += new /obj/item/stack/sheet/glass(null) + RefreshParts() + +/obj/machinery/mecha_part_fabricator/robot + name = "Robotic Fabricator" + part_sets = list("Cyborg") diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index a55a8a3be09..6a087b920d4 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -51,7 +51,7 @@ var/internal_damage = 0 //contains bitflags var/list/operation_req_access = list()//required access level for mecha operation - var/list/internals_req_access = list(access_engine,access_robotics)//required access level to open cell compartment + var/list/internals_req_access = list(ACCESS_ENGINE,ACCESS_ROBOTICS)//required access level to open cell compartment var/wreckage @@ -696,31 +696,6 @@ else to_chat(user, "Maintenance protocols disabled by operator.") - else if(iswrench(W)) - if(state==1) - state = 2 - to_chat(user, "You undo the securing bolts.") - else if(state==2) - state = 1 - to_chat(user, "You tighten the securing bolts.") - return - - else if(iscrowbar(W)) - if(state==2) - state = 3 - to_chat(user, "You open the hatch to the power unit") - else if(state==3) - state=2 - to_chat(user, "You close the hatch to the power unit") - else if(state==4 && pilot_is_mmi()) - // Since having maint protocols available is controllable by the MMI, I see this as a consensual way to remove an MMI without destroying the mech - user.visible_message("[user] begins levering out the MMI from the [src].", "You begin to lever out the MMI from the [src].") - to_chat(occupant, "[user] is prying you out of the exosuit!") - if(do_after(user, 80 * W.toolspeed, target=src)) - user.visible_message("[user] pries the MMI out of the [src]!", "You finish removing the MMI from the [src]!") - go_out() - return - else if(istype(W, /obj/item/stack/cable_coil)) if(state == 3 && hasInternalDamage(MECHA_INT_SHORT_CIRCUIT)) var/obj/item/stack/cable_coil/CC = W @@ -732,21 +707,6 @@ to_chat(user, "There's not enough wire to finish the task.") return - else if(isscrewdriver(W) && user.a_intent != INTENT_HARM) - if(hasInternalDamage(MECHA_INT_TEMP_CONTROL)) - clearInternalDamage(MECHA_INT_TEMP_CONTROL) - to_chat(user, "You repair the damaged temperature controller.") - else if(state==3 && cell) - cell.forceMove(loc) - cell = null - state = 4 - to_chat(user, "You unscrew and pry out the powercell.") - log_message("Powercell removed") - else if(state==4 && cell) - state=3 - to_chat(user, "You screw the cell in place.") - return - else if(istype(W, /obj/item/stock_parts/cell)) if(state==4) if(!cell) @@ -760,24 +720,6 @@ to_chat(user, "There's already a powercell installed.") return - else if(iswelder(W) && user.a_intent != INTENT_HARM) - var/obj/item/weldingtool/WT = W - if(obj_integrity < max_integrity) - if(WT.remove_fuel(0, user)) - user.visible_message("[user] starts repairing some damage to [name].", "You start repairing some damage to [name]") - if(do_after_once(user, 15 * WT.toolspeed, target = src, attempt_cancel_message = "You stop repairing [name].")) - if(internal_damage & MECHA_INT_TANK_BREACH) - clearInternalDamage(MECHA_INT_TANK_BREACH) - user.visible_message("[user] repairs the damaged gas tank.", "You repair the damaged gas tank.") - else - user.visible_message("[user] repairs some damage to [name].", "You repair some damage to [name].") - obj_integrity += min(10, max_integrity - obj_integrity) - else - to_chat(user, "The welder must be on for this task!") - else - to_chat(user, "The [name] is at full integrity!") - return TRUE - else if(istype(W, /obj/item/mecha_parts/mecha_tracking)) if(!user.unEquip(W)) to_chat(user, "\the [W] is stuck to your hand, you cannot put it in \the [src]") @@ -828,6 +770,81 @@ else return ..() + +/obj/mecha/crowbar_act(mob/user, obj/item/I) + if(state != 2 && state != 3 && !(state == 4 && pilot_is_mmi())) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(state == 2) + state = 3 + to_chat(user, "You open the hatch to the power unit") + else if(state == 3) + state = 2 + to_chat(user, "You close the hatch to the power unit") + else + // Since having maint protocols available is controllable by the MMI, I see this as a consensual way to remove an MMI without destroying the mech + user.visible_message("[user] begins levering out the MMI from the [src].", "You begin to lever out the MMI from the [src].") + to_chat(occupant, "[user] is prying you out of the exosuit!") + if(I.use_tool(src, user, 80, volume = I.tool_volume) && pilot_is_mmi()) + user.visible_message("[user] pries the MMI out of the [src]!", "You finish removing the MMI from the [src]!") + go_out() + +/obj/mecha/screwdriver_act(mob/user, obj/item/I) + if(user.a_intent == INTENT_HARM) + return + if(!(state==3 && cell) && !(state==4 && cell)) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(hasInternalDamage(MECHA_INT_TEMP_CONTROL)) + clearInternalDamage(MECHA_INT_TEMP_CONTROL) + to_chat(user, "You repair the damaged temperature controller.") + else if(state==3 && cell) + cell.forceMove(loc) + cell = null + state = 4 + to_chat(user, "You unscrew and pry out the powercell.") + log_message("Powercell removed") + else if(state==4 && cell) + state=3 + to_chat(user, "You screw the cell in place.") + +/obj/mecha/wrench_act(mob/user, obj/item/I) + if(state != 1 && state != 2) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(state==1) + state = 2 + to_chat(user, "You undo the securing bolts.") + else + state = 1 + to_chat(user, "You tighten the securing bolts.") + +/obj/mecha/welder_act(mob/user, obj/item/I) + if(user.a_intent == INTENT_HARM) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if((obj_integrity >= max_integrity) && !internal_damage) + to_chat(user, "[src] is at full integrity!") + return + WELDER_ATTEMPT_REPAIR_MESSAGE + if(I.use_tool(src, user, 15, volume = I.tool_volume)) + if(internal_damage & MECHA_INT_TANK_BREACH) + clearInternalDamage(MECHA_INT_TANK_BREACH) + user.visible_message("[user] repairs the damaged gas tank.", "You repair the damaged gas tank.") + else if(obj_integrity < max_integrity) + user.visible_message("[user] repairs some damage to [name].", "You repair some damage to [name].") + obj_integrity += min(10, max_integrity - obj_integrity) + else + to_chat(user, "[src] is at full integrity!") + /obj/mecha/mech_melee_attack(obj/mecha/M) if(!has_charge(melee_energy_drain)) return 0 diff --git a/code/game/mecha/mecha_actions.dm b/code/game/mecha/mecha_actions.dm index 3c71db19b72..8ff3d4ebd5c 100644 --- a/code/game/mecha/mecha_actions.dm +++ b/code/game/mecha/mecha_actions.dm @@ -225,4 +225,4 @@ chassis.damtype = new_damtype button_icon_state = "mech_damtype_[new_damtype]" playsound(src, 'sound/mecha/mechmove01.ogg', 50, 1) - UpdateButtonIcon() \ No newline at end of file + UpdateButtonIcon() diff --git a/code/game/mecha/mecha_construction_paths.dm b/code/game/mecha/mecha_construction_paths.dm index 59de4cc45b4..c9dc5b11e97 100644 --- a/code/game/mecha/mecha_construction_paths.dm +++ b/code/game/mecha/mecha_construction_paths.dm @@ -1,1757 +1,1727 @@ -#define STANDARD_STACK_AMOUNT 5 - -//////////////////////////////// -///// Construction datums ////// -//////////////////////////////// - -/datum/construction/mecha/custom_action(step, atom/used_atom, mob/user) - if(istype(used_atom, /obj/item/weldingtool)) - var/obj/item/weldingtool/W = used_atom - if(W.remove_fuel(0, user)) - playsound(holder, W.usesound, 50, 1) - else - return 0 - else if(istype(used_atom, /obj/item/wrench)) - var/obj/item/wrench/W = used_atom - playsound(holder, W.usesound, 50, 1) - - else if(istype(used_atom, /obj/item/screwdriver)) - var/obj/item/screwdriver/S = used_atom - playsound(holder, S.usesound, 50, 1) - - else if(istype(used_atom, /obj/item/wirecutters)) - var/obj/item/wirecutters/W = used_atom - playsound(holder, W.usesound, 50, 1) - - else if(istype(used_atom, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = used_atom - if(C.use(4)) - playsound(holder, C.usesound, 50, 1) - else - to_chat(user, ("There's not enough cable to finish the task.")) - return 0 - else if(istype(used_atom, /obj/item/stack)) - var/obj/item/stack/S = used_atom - if(S.amount < STANDARD_STACK_AMOUNT) - to_chat(user, ("There's not enough material in this stack.")) - return 0 - else - S.use(STANDARD_STACK_AMOUNT) - return 1 - -/datum/construction/reversible/mecha/custom_action(index as num, diff as num, atom/used_atom, mob/user as mob) - if(istype(used_atom, /obj/item/weldingtool)) - var/obj/item/weldingtool/W = used_atom - if(W.remove_fuel(0, user)) - playsound(holder, W.usesound, 50, 1) - else - return 0 - else if(istype(used_atom, /obj/item/wrench)) - var/obj/item/wrench/W = used_atom - playsound(holder, W.usesound, 50, 1) - - else if(istype(used_atom, /obj/item/screwdriver)) - var/obj/item/screwdriver/S = used_atom - playsound(holder, S.usesound, 50, 1) - - else if(istype(used_atom, /obj/item/wirecutters)) - var/obj/item/wirecutters/W = used_atom - playsound(holder, W.usesound, 50, 1) - - else if(istype(used_atom, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = used_atom - if(C.use(4)) - playsound(holder, C.usesound, 50, 1) - else - to_chat(user, ("There's not enough cable to finish the task.")) - return 0 - else if(istype(used_atom, /obj/item/stack)) - var/obj/item/stack/S = used_atom - if(S.amount < STANDARD_STACK_AMOUNT) - to_chat(user, ("There's not enough material in this stack.")) - return 0 - else - S.use(STANDARD_STACK_AMOUNT) - return 1 - - -/datum/construction/mecha/ripley_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/ripley_torso),//1 - list("key"=/obj/item/mecha_parts/part/ripley_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/ripley_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/ripley_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/ripley_right_leg)//5 - ) - -/datum/construction/mecha/ripley_chassis/custom_action(step, atom/used_atom, mob/user) - user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") - holder.overlays += used_atom.icon_state+"+o" - qdel(used_atom) - return 1 - -/datum/construction/mecha/ripley_chassis/action(atom/used_atom,mob/user as mob) - return check_all_steps(used_atom,user) - -/datum/construction/mecha/ripley_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/ripley(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "ripley0" - const_holder.density = 1 - const_holder.overlays.len = 0 - qdel(src) - return - - -/datum/construction/reversible/mecha/ripley - result = "/obj/mecha/working/ripley" - taskpath = /datum/job_objective/make_ripley - steps = list( - //1 - list("key"=/obj/item/weldingtool, - "backkey"=/obj/item/wrench, - "desc"="External armor is wrenched."), - //2 - list("key"=/obj/item/wrench, - "backkey"=/obj/item/crowbar, - "desc"="External armor is installed."), - //3 - list("key"=/obj/item/stack/sheet/plasteel, - "backkey"=/obj/item/weldingtool, - "desc"="Internal armor is welded."), - //4 - list("key"=/obj/item/weldingtool, - "backkey"=/obj/item/wrench, - "desc"="Internal armor is wrenched."), - //5 - list("key"=/obj/item/wrench, - "backkey"=/obj/item/crowbar, - "desc"="Internal armor is installed."), - //6 - list("key"=/obj/item/stack/sheet/metal, - "backkey"=/obj/item/screwdriver, - "desc"="Peripherals control module is secured."), - //7 - list("key"=/obj/item/screwdriver, - "backkey"=/obj/item/crowbar, - "desc"="Peripherals control module is installed."), - //8 - list("key"=/obj/item/circuitboard/mecha/ripley/peripherals, - "backkey"=/obj/item/screwdriver, - "desc"="Central control module is secured."), - //9 - list("key"=/obj/item/screwdriver, - "backkey"=/obj/item/crowbar, - "desc"="Central control module is installed."), - //10 - list("key"=/obj/item/circuitboard/mecha/ripley/main, - "backkey"=/obj/item/screwdriver, - "desc"="The wiring is adjusted."), - //11 - list("key"=/obj/item/wirecutters, - "backkey"=/obj/item/screwdriver, - "desc"="The wiring is added."), - //12 - list("key"=/obj/item/stack/cable_coil, - "backkey"=/obj/item/screwdriver, - "desc"="The hydraulic systems are active."), - //13 - list("key"=/obj/item/screwdriver, - "backkey"=/obj/item/wrench, - "desc"="The hydraulic systems are connected."), - //14 - list("key"=/obj/item/wrench, - "desc"="The hydraulic systems are disconnected.") - ) - -/datum/construction/reversible/mecha/ripley/action(atom/used_atom,mob/user as mob) - return check_step(used_atom,user) - -/datum/construction/reversible/mecha/ripley/custom_action(index, diff, atom/used_atom, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(14) - user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") - holder.icon_state = "ripley1" - if(13) - if(diff==FORWARD) - user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") - holder.icon_state = "ripley2" - else - user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") - holder.icon_state = "ripley0" - if(12) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") - holder.icon_state = "ripley3" - else - user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") - holder.icon_state = "ripley1" - if(11) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") - holder.icon_state = "ripley4" - else - user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") - var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) - coil.amount = 4 - holder.icon_state = "ripley2" - if(10) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") - qdel(used_atom) - holder.icon_state = "ripley5" - else - user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") - holder.icon_state = "ripley3" - if(9) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "ripley6" - else - user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") - new /obj/item/circuitboard/mecha/ripley/main(get_turf(holder)) - holder.icon_state = "ripley4" - if(8) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") - qdel(used_atom) - holder.icon_state = "ripley7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "ripley5" - if(7) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "ripley8" - else - user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") - new /obj/item/circuitboard/mecha/ripley/peripherals(get_turf(holder)) - holder.icon_state = "ripley6" - if(6) - if(diff==FORWARD) - user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") - holder.icon_state = "ripley9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "ripley7" - if(5) - if(diff==FORWARD) - user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") - holder.icon_state = "ripley10" - else - user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") - var/obj/item/stack/sheet/metal/MS = new /obj/item/stack/sheet/metal(get_turf(holder)) - MS.amount = 5 - holder.icon_state = "ripley8" - if(4) - if(diff==FORWARD) - user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") - holder.icon_state = "ripley11" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "ripley9" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs the external reinforced armor layer to the [holder].", "You install the external reinforced armor layer to the [holder].") - holder.icon_state = "ripley12" - else - user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") - holder.icon_state = "ripley10" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures the external armor layer.", "You secure the external reinforced armor layer.") - holder.icon_state = "ripley13" - else - user.visible_message("[user] pries external armor layer from the [holder].", "You pry external armor layer from the [holder].") - var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) - MS.amount = 5 - holder.icon_state = "ripley11" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds the external armor layer to the [holder].", "You weld the external armor layer to the [holder].") - else - user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") - holder.icon_state = "ripley12" - return 1 - -/datum/construction/reversible/mecha/ripley/spawn_result() - ..() - feedback_inc("mecha_ripley_created",1) - return - - - -/datum/construction/mecha/gygax_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/gygax_torso),//1 - list("key"=/obj/item/mecha_parts/part/gygax_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/gygax_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/gygax_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/gygax_right_leg),//5 - list("key"=/obj/item/mecha_parts/part/gygax_head) - ) - -/datum/construction/mecha/gygax_chassis/custom_action(step, atom/used_atom, mob/user) - user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") - holder.overlays += used_atom.icon_state+"+o" - qdel(used_atom) - return 1 - -/datum/construction/mecha/gygax_chassis/action(atom/used_atom,mob/user as mob) - return check_all_steps(used_atom,user) - -/datum/construction/mecha/gygax_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/gygax(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "gygax0" - const_holder.density = 1 - qdel(src) - return - - -/datum/construction/reversible/mecha/gygax - result = "/obj/mecha/combat/gygax" - steps = list( - //1 - list("key"=/obj/item/weldingtool, - "backkey"=/obj/item/wrench, - "desc"="External armor is wrenched."), - //2 - list("key"=/obj/item/wrench, - "backkey"=/obj/item/crowbar, - "desc"="External armor is installed."), - //3 - list("key"=/obj/item/mecha_parts/part/gygax_armour, - "backkey"=/obj/item/weldingtool, - "desc"="Internal armor is welded."), - //4 - list("key"=/obj/item/weldingtool, - "backkey"=/obj/item/wrench, - "desc"="Internal armor is wrenched."), - //5 - list("key"=/obj/item/wrench, - "backkey"=/obj/item/crowbar, - "desc"="Internal armor is installed."), - //6 - list("key"=/obj/item/stack/sheet/metal, - "backkey"=/obj/item/screwdriver, - "desc"="Advanced capacitor is secured."), - //7 - list("key"=/obj/item/screwdriver, - "backkey"=/obj/item/crowbar, - "desc"="Advanced capacitor is installed."), - //8 - list("key"=/obj/item/stock_parts/capacitor/adv, - "backkey"=/obj/item/screwdriver, - "desc"="Advanced scanner module is secured."), - //9 - list("key"=/obj/item/screwdriver, - "backkey"=/obj/item/crowbar, - "desc"="Advanced scanner module is installed."), - //10 - list("key"=/obj/item/stock_parts/scanning_module/adv, - "backkey"=/obj/item/screwdriver, - "desc"="Scanning module is secured."), - //11 - list("key"=/obj/item/screwdriver, - "backkey"=/obj/item/crowbar, - "desc"="Scanning module is installed."), - //12 - list("key"=/obj/item/circuitboard/mecha/gygax/targeting, - "backkey"=/obj/item/screwdriver, - "desc"="Peripherals control module is secured."), - //13 - list("key"=/obj/item/screwdriver, - "backkey"=/obj/item/crowbar, - "desc"="Peripherals control module is installed."), - //14 - list("key"=/obj/item/circuitboard/mecha/gygax/peripherals, - "backkey"=/obj/item/screwdriver, - "desc"="Central control module is secured."), - //15 - list("key"=/obj/item/screwdriver, - "backkey"=/obj/item/crowbar, - "desc"="Central control module is installed."), - //16 - list("key"=/obj/item/circuitboard/mecha/gygax/main, - "backkey"=/obj/item/screwdriver, - "desc"="The wiring is adjusted."), - //17 - list("key"=/obj/item/wirecutters, - "backkey"=/obj/item/screwdriver, - "desc"="The wiring is added."), - //18 - list("key"=/obj/item/stack/cable_coil, - "backkey"=/obj/item/screwdriver, - "desc"="The hydraulic systems are active."), - //19 - list("key"=/obj/item/screwdriver, - "backkey"=/obj/item/wrench, - "desc"="The hydraulic systems are connected."), - //20 - list("key"=/obj/item/wrench, - "desc"="The hydraulic systems are disconnected.") - ) - -/datum/construction/reversible/mecha/gygax/action(atom/used_atom,mob/user as mob) - return check_step(used_atom,user) - -/datum/construction/reversible/mecha/gygax/custom_action(index, diff, atom/used_atom, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(20) - user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") - holder.icon_state = "gygax1" - if(19) - if(diff==FORWARD) - user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") - holder.icon_state = "gygax2" - else - user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") - holder.icon_state = "gygax0" - if(18) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") - holder.icon_state = "gygax3" - else - user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") - holder.icon_state = "gygax1" - if(17) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") - holder.icon_state = "gygax4" - else - user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") - var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) - coil.amount = 4 - holder.icon_state = "gygax2" - if(16) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") - qdel(used_atom) - holder.icon_state = "gygax5" - else - user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") - holder.icon_state = "gygax3" - if(15) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "gygax6" - else - user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") - new /obj/item/circuitboard/mecha/gygax/main(get_turf(holder)) - holder.icon_state = "gygax4" - if(14) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") - qdel(used_atom) - holder.icon_state = "gygax7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "gygax5" - if(13) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "gygax8" - else - user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") - new /obj/item/circuitboard/mecha/gygax/peripherals(get_turf(holder)) - holder.icon_state = "gygax6" - if(12) - if(diff==FORWARD) - user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") - qdel(used_atom) - holder.icon_state = "gygax9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "gygax7" - if(11) - if(diff==FORWARD) - user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") - holder.icon_state = "gygax10" - else - user.visible_message("[user] removes the weapon control module from the [holder].", "You remove the weapon control module from the [holder].") - new /obj/item/circuitboard/mecha/gygax/targeting(get_turf(holder)) - holder.icon_state = "gygax8" - if(10) - if(diff==FORWARD) - user.visible_message("[user] installs advanced scanner module to the [holder].", "You install advanced scanner module to the [holder].") - qdel(used_atom) - holder.icon_state = "gygax11" - else - user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") - holder.icon_state = "gygax9" - if(9) - if(diff==FORWARD) - user.visible_message("[user] secures the advanced scanner module.", "You secure the advanced scanner module.") - holder.icon_state = "gygax12" - else - user.visible_message("[user] removes the advanced scanner module from the [holder].", "You remove the advanced scanner module from the [holder].") - new /obj/item/stock_parts/scanning_module/adv(get_turf(holder)) - holder.icon_state = "gygax10" - if(8) - if(diff==FORWARD) - user.visible_message("[user] installs advanced capacitor to the [holder].", "You install advanced capacitor to the [holder].") - qdel(used_atom) - holder.icon_state = "gygax13" - else - user.visible_message("[user] unfastens the advanced scanner module.", "You unfasten the advanced scanner module.") - holder.icon_state = "gygax11" - if(7) - if(diff==FORWARD) - user.visible_message("[user] secures the advanced capacitor.", "You secure the advanced capacitor.") - holder.icon_state = "gygax14" - else - user.visible_message("[user] removes the advanced capacitor from the [holder].", "You remove the advanced capacitor from the [holder].") - new /obj/item/stock_parts/capacitor/adv(get_turf(holder)) - holder.icon_state = "gygax12" - if(6) - if(diff==FORWARD) - user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") - holder.icon_state = "gygax15" - else - user.visible_message("[user] unfastens the advanced capacitor.", "You unfasten the advanced capacitor.") - holder.icon_state = "gygax13" - if(5) - if(diff==FORWARD) - user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") - holder.icon_state = "gygax16" - else - user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") - var/obj/item/stack/sheet/metal/MS = new /obj/item/stack/sheet/metal(get_turf(holder)) - MS.amount = 5 - holder.icon_state = "gygax14" - if(4) - if(diff==FORWARD) - user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") - holder.icon_state = "gygax17" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "gygax15" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs Gygax Armor Plates to the [holder].", "You install Gygax Armor Plates to the [holder].") - qdel(used_atom) - holder.icon_state = "gygax18" - else - user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") - holder.icon_state = "gygax16" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures Gygax Armor Plates.", "You secure Gygax Armor Plates.") - holder.icon_state = "gygax19" - else - user.visible_message("[user] pries Gygax Armor Plates from the [holder].", "You pry Gygax Armor Plates from the [holder].") - new /obj/item/mecha_parts/part/gygax_armour(get_turf(holder)) - holder.icon_state = "gygax17" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds Gygax Armor Plates to the [holder].", "You weld Gygax Armor Plates to the [holder].") - else - user.visible_message("[user] unfastens Gygax Armor Plates.", "You unfasten Gygax Armor Plates.") - holder.icon_state = "gygax18" - return 1 - -/datum/construction/reversible/mecha/gygax/spawn_result() - ..() - feedback_inc("mecha_gygax_created",1) - return - -/datum/construction/mecha/firefighter_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/ripley_torso),//1 - list("key"=/obj/item/mecha_parts/part/ripley_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/ripley_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/ripley_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/ripley_right_leg),//5 - list("key"=/obj/item/clothing/suit/fire)//6 - ) - -/datum/construction/mecha/firefighter_chassis/custom_action(step, atom/used_atom, mob/user) - user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") - holder.overlays += used_atom.icon_state+"+o" - qdel(used_atom) - return 1 - -/datum/construction/mecha/firefighter_chassis/action(atom/used_atom,mob/user as mob) - return check_all_steps(used_atom,user) - -/datum/construction/mecha/firefighter_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/firefighter(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "fireripley0" - const_holder.density = 1 - qdel(src) - return - - -/datum/construction/reversible/mecha/firefighter - result = "/obj/mecha/working/ripley/firefighter" - taskpath = /datum/job_objective/make_ripley - steps = list( - //1 - list("key"=/obj/item/weldingtool, - "backkey"=/obj/item/wrench, - "desc"="External armor is wrenched."), - //2 - list("key"=/obj/item/wrench, - "backkey"=/obj/item/crowbar, - "desc"="External armor is installed."), - //3 - list("key"=/obj/item/stack/sheet/plasteel, - "backkey"=/obj/item/crowbar, - "desc"="External armor is being installed."), - //4 - list("key"=/obj/item/stack/sheet/plasteel, - "backkey"=/obj/item/weldingtool, - "desc"="Internal armor is welded."), - //5 - list("key"=/obj/item/weldingtool, - "backkey"=/obj/item/wrench, - "desc"="Internal armor is wrenched."), - //6 - list("key"=/obj/item/wrench, - "backkey"=/obj/item/crowbar, - "desc"="Internal armor is installed."), - - //7 - list("key"=/obj/item/stack/sheet/plasteel, - "backkey"=/obj/item/screwdriver, - "desc"="Peripherals control module is secured."), - //8 - list("key"=/obj/item/screwdriver, - "backkey"=/obj/item/crowbar, - "desc"="Peripherals control module is installed."), - //9 - list("key"=/obj/item/circuitboard/mecha/ripley/peripherals, - "backkey"=/obj/item/screwdriver, - "desc"="Central control module is secured."), - //10 - list("key"=/obj/item/screwdriver, - "backkey"=/obj/item/crowbar, - "desc"="Central control module is installed."), - //11 - list("key"=/obj/item/circuitboard/mecha/ripley/main, - "backkey"=/obj/item/screwdriver, - "desc"="The wiring is adjusted."), - //12 - list("key"=/obj/item/wirecutters, - "backkey"=/obj/item/screwdriver, - "desc"="The wiring is added."), - //13 - list("key"=/obj/item/stack/cable_coil, - "backkey"=/obj/item/screwdriver, - "desc"="The hydraulic systems are active."), - //14 - list("key"=/obj/item/screwdriver, - "backkey"=/obj/item/wrench, - "desc"="The hydraulic systems are connected."), - //15 - list("key"=/obj/item/wrench, - "desc"="The hydraulic systems are disconnected.") - ) - -/datum/construction/reversible/mecha/firefighter/action(atom/used_atom,mob/user as mob) - return check_step(used_atom,user) - -/datum/construction/reversible/mecha/firefighter/custom_action(index, diff, atom/used_atom, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(15) - user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") - holder.icon_state = "fireripley1" - if(14) - if(diff==FORWARD) - user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") - holder.icon_state = "fireripley2" - else - user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") - holder.icon_state = "fireripley0" - if(13) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") - holder.icon_state = "fireripley3" - else - user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") - holder.icon_state = "fireripley1" - if(12) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") - holder.icon_state = "fireripley4" - else - user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") - var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) - coil.amount = 4 - holder.icon_state = "fireripley2" - if(11) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") - qdel(used_atom) - holder.icon_state = "fireripley5" - else - user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") - holder.icon_state = "fireripley3" - if(10) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "fireripley6" - else - user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") - new /obj/item/circuitboard/mecha/ripley/main(get_turf(holder)) - holder.icon_state = "fireripley4" - if(9) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") - qdel(used_atom) - holder.icon_state = "fireripley7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "fireripley5" - if(8) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "fireripley8" - else - user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") - new /obj/item/circuitboard/mecha/ripley/peripherals(get_turf(holder)) - holder.icon_state = "fireripley6" - if(7) - if(diff==FORWARD) - user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") - holder.icon_state = "fireripley9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "fireripley7" - - if(6) - if(diff==FORWARD) - user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") - holder.icon_state = "fireripley10" - else - user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") - var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) - MS.amount = 5 - holder.icon_state = "fireripley8" - if(5) - if(diff==FORWARD) - user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") - holder.icon_state = "fireripley11" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "fireripley9" - if(4) - if(diff==FORWARD) - user.visible_message("[user] starts to install the external armor layer to the [holder].", "You start to install the external armor layer to the [holder].") - holder.icon_state = "fireripley12" - else - user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") - holder.icon_state = "fireripley10" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs the external reinforced armor layer to the [holder].", "You install the external reinforced armor layer to the [holder].") - holder.icon_state = "fireripley13" - else - user.visible_message("[user] removes the external armor from the [holder].", "You remove the external armor from the [holder].") - var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) - MS.amount = 5 - holder.icon_state = "fireripley11" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures the external armor layer.", "You secure the external reinforced armor layer.") - holder.icon_state = "fireripley14" - else - user.visible_message("[user] pries external armor layer from the [holder].", "You pry external armor layer from the [holder].") - var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) - MS.amount = 5 - holder.icon_state = "fireripley12" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds the external armor layer to the [holder].", "You weld the external armor layer to the [holder].") - else - user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") - holder.icon_state = "fireripley13" - return 1 - -/datum/construction/reversible/mecha/firefighter/spawn_result() - ..() - feedback_inc("mecha_firefighter_created",1) - return - - - -/datum/construction/mecha/honker_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/honker_torso),//1 - list("key"=/obj/item/mecha_parts/part/honker_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/honker_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/honker_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/honker_right_leg),//5 - list("key"=/obj/item/mecha_parts/part/honker_head) - ) - -/datum/construction/mecha/honker_chassis/action(atom/used_atom,mob/user as mob) - return check_all_steps(used_atom,user) - -/datum/construction/mecha/honker_chassis/custom_action(step, atom/used_atom, mob/user) - user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") - holder.overlays += used_atom.icon_state+"+o" - qdel(used_atom) - return 1 - -/datum/construction/mecha/honker_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/mecha/honker(const_holder) - const_holder.density = 1 - qdel(src) - return - - -/datum/construction/mecha/honker - result = "/obj/mecha/combat/honker" - steps = list(list("key"=/obj/item/bikehorn),//1 - list("key"=/obj/item/clothing/shoes/clown_shoes),//2 - list("key"=/obj/item/bikehorn),//3 - list("key"=/obj/item/clothing/mask/gas/clown_hat),//4 - list("key"=/obj/item/bikehorn),//5 - list("key"=/obj/item/circuitboard/mecha/honker/targeting),//6 - list("key"=/obj/item/bikehorn),//7 - list("key"=/obj/item/circuitboard/mecha/honker/peripherals),//8 - list("key"=/obj/item/bikehorn),//9 - list("key"=/obj/item/circuitboard/mecha/honker/main),//10 - list("key"=/obj/item/bikehorn),//11 - ) - -/datum/construction/mecha/honker/action(atom/used_atom,mob/user as mob) - return check_step(used_atom,user) - -/datum/construction/mecha/honker/custom_action(step, atom/used_atom, mob/user) - if(!..()) - return 0 - - if(istype(used_atom, /obj/item/bikehorn)) - playsound(holder, 'sound/items/bikehorn.ogg', 50, 1) - user.visible_message("HONK!") - - //TODO: better messages. - switch(step) - if(10) - user.visible_message("[user] installs the central control module into the [holder].", "You install the central control module into the [holder].") - qdel(used_atom) - if(8) - user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") - qdel(used_atom) - if(6) - user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") - qdel(used_atom) - if(4) - user.visible_message("[user] puts clown wig and mask on the [holder].", "You put clown wig and mask on the [holder].") - qdel(used_atom) - if(2) - user.visible_message("[user] puts clown boots on the [holder].", "You put clown boots on the [holder].") - qdel(used_atom) - return 1 - -/datum/construction/mecha/honker/spawn_result() - ..() - feedback_inc("mecha_honker_created",1) - return - -/datum/construction/mecha/reticence_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/reticence_torso),//1 - list("key"=/obj/item/mecha_parts/part/reticence_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/reticence_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/reticence_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/reticence_right_leg),//5 - list("key"=/obj/item/mecha_parts/part/reticence_head) - ) - -/datum/construction/mecha/reticence_chassis/action(atom/used_atom,mob/user as mob) - return check_all_steps(used_atom,user) - -/datum/construction/mecha/reticence_chassis/custom_action(step, atom/used_atom, mob/user) - user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") - holder.overlays += used_atom.icon_state + "+o" - qdel(used_atom) - return 1 - -/datum/construction/mecha/reticence_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/mecha/reticence(const_holder) - const_holder.density = 1 - qdel(src) - return - -/datum/construction/mecha/reticence - result = "/obj/mecha/combat/reticence" - steps = list(list("key"=/obj/effect/dummy/mecha_emote_step),//1 - list("key"=/obj/item/clothing/suit/suspenders),//2 - list("key"=/obj/effect/dummy/mecha_emote_step),//3 - list("key"=/obj/item/clothing/mask/gas/mime),//4 - list("key"=/obj/effect/dummy/mecha_emote_step),//5 - list("key"=/obj/item/clothing/head/beret),//6 - list("key"=/obj/item/circuitboard/mecha/reticence/targeting),//7 - list("key"=/obj/item/circuitboard/mecha/reticence/peripherals),//8 - list("key"=/obj/item/circuitboard/mecha/reticence/main),//9 - ) - -/datum/construction/mecha/reticence/action(atom/used_atom,mob/user) - return check_step(used_atom,user) - -/datum/construction/mecha/reticence/custom_action(step, atom/used_atom, mob/user) - if(!..()) - return 0 - - if(istype(used_atom, /obj/effect/dummy/mecha_emote_step)) - var/obj/effect/dummy/mecha_emote_step/E = used_atom - holder.visible_message("[holder] likewise [E.emote]") - qdel(used_atom) - - //TODO: better messages. - switch(step) - if(9) - user.visible_message("[user] installs the central control module into the [holder].", "You install the central control module into the [holder].") - qdel(used_atom) - if(8) - user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") - qdel(used_atom) - if(7) - user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") - qdel(used_atom) - if(6) - user.visible_message("[user] puts beret on the [holder].", "You put beret on the [holder].") - qdel(used_atom) - if(4) - user.visible_message("[user] puts mime mask on the [holder].", "You put mime mask on the [holder].") - qdel(used_atom) - if(2) - user.visible_message("[user] puts suspenders on the [holder].", "You put suspenders on the [holder].") - qdel(used_atom) - return 1 - -/datum/construction/mecha/reticence/spawn_result() - ..() - feedback_inc("mecha_reticence_created",1) - return - -/datum/construction/mecha/durand_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/durand_torso),//1 - list("key"=/obj/item/mecha_parts/part/durand_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/durand_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/durand_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/durand_right_leg),//5 - list("key"=/obj/item/mecha_parts/part/durand_head) - ) - -/datum/construction/mecha/durand_chassis/custom_action(step, atom/used_atom, mob/user) - user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") - holder.overlays += used_atom.icon_state+"+o" - qdel(used_atom) - return 1 - -/datum/construction/mecha/durand_chassis/action(atom/used_atom,mob/user as mob) - return check_all_steps(used_atom,user) - -/datum/construction/mecha/durand_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/durand(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "durand0" - const_holder.density = 1 - qdel(src) - return - -/datum/construction/reversible/mecha/durand - result = "/obj/mecha/combat/durand" - steps = list( - //1 - list("key"=/obj/item/weldingtool, - "backkey"=/obj/item/wrench, - "desc"="External armor is wrenched."), - //2 - list("key"=/obj/item/wrench, - "backkey"=/obj/item/crowbar, - "desc"="External armor is installed."), - //3 - list("key"=/obj/item/mecha_parts/part/durand_armor, - "backkey"=/obj/item/weldingtool, - "desc"="Internal armor is welded."), - //4 - list("key"=/obj/item/weldingtool, - "backkey"=/obj/item/wrench, - "desc"="Internal armor is wrenched."), - //5 - list("key"=/obj/item/wrench, - "backkey"=/obj/item/crowbar, - "desc"="Internal armor is installed."), - //6 - list("key"=/obj/item/stack/sheet/metal, - "backkey"=/obj/item/screwdriver, - "desc"="Super capacitor is secured."), - //7 - list("key"=/obj/item/screwdriver, - "backkey"=/obj/item/crowbar, - "desc"="Super capacitor is installed."), - //8 - list("key"=/obj/item/stock_parts/capacitor/super, - "backkey"=/obj/item/screwdriver, - "desc"="Phasic scanner module is secured."), - //9 - list("key"=/obj/item/screwdriver, - "backkey"=/obj/item/crowbar, - "desc"="Phasic scanner module is installed."), - //10 - list("key"=/obj/item/stock_parts/scanning_module/phasic, - "backkey"=/obj/item/screwdriver, - "desc"="Scanning module is secured."), - //11 - list("key"=/obj/item/screwdriver, - "backkey"=/obj/item/crowbar, - "desc"="Scanning module is installed."), - //12 - list("key"=/obj/item/circuitboard/mecha/durand/targeting, - "backkey"=/obj/item/screwdriver, - "desc"="Peripherals control module is secured."), - //13 - list("key"=/obj/item/screwdriver, - "backkey"=/obj/item/crowbar, - "desc"="Peripherals control module is installed."), - //14 - list("key"=/obj/item/circuitboard/mecha/durand/peripherals, - "backkey"=/obj/item/screwdriver, - "desc"="Central control module is secured."), - //15 - list("key"=/obj/item/screwdriver, - "backkey"=/obj/item/crowbar, - "desc"="Central control module is installed."), - //16 - list("key"=/obj/item/circuitboard/mecha/durand/main, - "backkey"=/obj/item/screwdriver, - "desc"="The wiring is adjusted."), - //17 - list("key"=/obj/item/wirecutters, - "backkey"=/obj/item/screwdriver, - "desc"="The wiring is added."), - //18 - list("key"=/obj/item/stack/cable_coil, - "backkey"=/obj/item/screwdriver, - "desc"="The hydraulic systems are active."), - //19 - list("key"=/obj/item/screwdriver, - "backkey"=/obj/item/wrench, - "desc"="The hydraulic systems are connected."), - //20 - list("key"=/obj/item/wrench, - "desc"="The hydraulic systems are disconnected.") - ) - - -/datum/construction/reversible/mecha/durand/action(atom/used_atom,mob/user as mob) - return check_step(used_atom,user) - -/datum/construction/reversible/mecha/durand/custom_action(index, diff, atom/used_atom, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(20) - user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") - holder.icon_state = "durand1" - if(19) - if(diff==FORWARD) - user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") - holder.icon_state = "durand2" - else - user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") - holder.icon_state = "durand0" - if(18) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") - holder.icon_state = "durand3" - else - user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") - holder.icon_state = "durand1" - if(17) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") - holder.icon_state = "durand4" - else - user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") - var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) - coil.amount = 4 - holder.icon_state = "durand2" - if(16) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") - qdel(used_atom) - holder.icon_state = "durand5" - else - user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") - holder.icon_state = "durand3" - if(15) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "durand6" - else - user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") - new /obj/item/circuitboard/mecha/durand/main(get_turf(holder)) - holder.icon_state = "durand4" - if(14) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") - qdel(used_atom) - holder.icon_state = "durand7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "durand5" - if(13) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "durand8" - else - user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") - new /obj/item/circuitboard/mecha/durand/peripherals(get_turf(holder)) - holder.icon_state = "durand6" - if(12) - if(diff==FORWARD) - user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") - qdel(used_atom) - holder.icon_state = "durand9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "durand7" - if(11) - if(diff==FORWARD) - user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") - holder.icon_state = "durand10" - else - user.visible_message("[user] removes the weapon control module from the [holder].", "You remove the weapon control module from the [holder].") - new /obj/item/circuitboard/mecha/durand/targeting(get_turf(holder)) - holder.icon_state = "durand8" - if(10) - if(diff==FORWARD) - user.visible_message("[user] installs phasic scanner module to the [holder].", "You install phasic scanner module to the [holder].") - qdel(used_atom) - holder.icon_state = "durand11" - else - user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") - holder.icon_state = "durand9" - if(9) - if(diff==FORWARD) - user.visible_message("[user] secures the phasic scanner module.", "You secure the phasic scanner module.") - holder.icon_state = "durand12" - else - user.visible_message("[user] removes the phasic scanner module from the [holder].", "You remove the phasic scanner module from the [holder].") - new /obj/item/stock_parts/scanning_module/phasic(get_turf(holder)) - holder.icon_state = "durand10" - if(8) - if(diff==FORWARD) - user.visible_message("[user] installs super capacitor to the [holder].", "You install super capacitor to the [holder].") - qdel(used_atom) - holder.icon_state = "durand13" - else - user.visible_message("[user] unfastens the phasic scanner module.", "You unfasten the phasic scanner module.") - holder.icon_state = "durand11" - if(7) - if(diff==FORWARD) - user.visible_message("[user] secures the super capacitor.", "You secure the super capacitor.") - holder.icon_state = "durand14" - else - user.visible_message("[user] removes the super capacitor from the [holder].", "You remove the super capacitor from the [holder].") - new /obj/item/stock_parts/capacitor/super(get_turf(holder)) - holder.icon_state = "durand12" - if(6) - if(diff==FORWARD) - user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") - holder.icon_state = "durand15" - else - user.visible_message("[user] unfastens the super capacitor.", "You unfasten the super capacitor.") - holder.icon_state = "durand13" - if(5) - if(diff==FORWARD) - user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") - holder.icon_state = "durand16" - else - user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") - var/obj/item/stack/sheet/metal/MS = new /obj/item/stack/sheet/metal(get_turf(holder)) - MS.amount = 5 - holder.icon_state = "durand14" - if(4) - if(diff==FORWARD) - user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") - holder.icon_state = "durand17" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "durand15" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs Durand Armor Plates to the [holder].", "You install Durand Armor Plates to the [holder].") - qdel(used_atom) - holder.icon_state = "durand18" - else - user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") - holder.icon_state = "durand16" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures Durand Armor Plates.", "You secure Durand Armor Plates.") - holder.icon_state = "durand19" - else - user.visible_message("[user] pries Durand Armor Plates from the [holder].", "You pry Durand Armor Plates from the [holder].") - new /obj/item/mecha_parts/part/durand_armor(get_turf(holder)) - holder.icon_state = "durand17" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds Durand Armor Plates to the [holder].", "You weld Durand Armor Plates to the [holder].") - else - user.visible_message("[user] unfastens Durand Armor Plates.", "You unfasten Durand Armor Plates.") - holder.icon_state = "durand18" - return 1 - -/datum/construction/reversible/mecha/durand/spawn_result() - ..() - feedback_inc("mecha_durand_created",1) - return - -//PHAZON - -/datum/construction/mecha/phazon_chassis - result = "/obj/mecha/combat/phazon" - steps = list(list("key"=/obj/item/mecha_parts/part/phazon_torso),//1 - list("key"=/obj/item/mecha_parts/part/phazon_left_arm),//2 - list("key"=/obj/item/mecha_parts/part/phazon_right_arm),//3 - list("key"=/obj/item/mecha_parts/part/phazon_left_leg),//4 - list("key"=/obj/item/mecha_parts/part/phazon_right_leg),//5 - list("key"=/obj/item/mecha_parts/part/phazon_head) - ) - -/datum/construction/mecha/phazon_chassis/custom_action(step, atom/used_atom, mob/user) - user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") - holder.overlays += used_atom.icon_state+"+o" - qdel(used_atom) - return 1 - -/datum/construction/mecha/phazon_chassis/action(atom/used_atom,mob/user as mob) - return check_all_steps(used_atom,user) - -/datum/construction/mecha/phazon_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/phazon(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "phazon0" - const_holder.density = 1 - qdel(src) - return - -/datum/construction/reversible/mecha/phazon - result = "/obj/mecha/combat/phazon" - steps = list( - //1 - list("key"=/obj/item/assembly/signaler/anomaly, - "backkey"=null, //Cannot remove the anomaly core once it's in - "desc"="Anomaly core socket is open and awaiting connection."), - //2 - list("key"=/obj/item/weldingtool, - "backkey"=/obj/item/wrench, - "desc"="External armor is wrenched."), - //3 - list("key"=/obj/item/wrench, - "backkey"=/obj/item/crowbar, - "desc"="External armor is installed."), - //4 - list("key"=/obj/item/mecha_parts/part/phazon_armor, - "backkey"=/obj/item/weldingtool, - "desc"="Phase armor is welded."), - //5 - list("key"=/obj/item/weldingtool, - "backkey"=/obj/item/wrench, - "desc"="Phase armor is wrenched."), - //6 - list("key"=/obj/item/wrench, - "backkey"=/obj/item/crowbar, - "desc"="Phase armor is installed."), - //7 - list("key"=/obj/item/stack/sheet/plasteel, - "backkey"=/obj/item/screwdriver, - "desc"="The bluespace crystal is engaged."), - //8 - list("key"=/obj/item/screwdriver, - "backkey"=/obj/item/wirecutters, - "desc"="The bluespace crystal is connected."), - //9 - list("key"=/obj/item/stack/cable_coil, - "backkey"=/obj/item/crowbar, - "desc"="The bluespace crystal is installed."), - //10 - list("key"=/obj/item/stack/ore/bluespace_crystal, - "backkey"=/obj/item/screwdriver, - "desc"="Super capacitor is secured."), - //11 - list("key"=/obj/item/screwdriver, - "backkey"=/obj/item/crowbar, - "desc"="Super capacitor is installed."), - //12 - list("key"=/obj/item/stock_parts/capacitor/super, - "backkey"=/obj/item/screwdriver, - "desc"="Phasic scanner module is secured."), - //13 - list("key"=/obj/item/screwdriver, - "backkey"=/obj/item/crowbar, - "desc"="Phasic scanner module is installed."), - //14 - list("key"=/obj/item/stock_parts/scanning_module/phasic, - "backkey"=/obj/item/screwdriver, - "desc"="Scanning module is secured."), - //15 - list("key"=/obj/item/screwdriver, - "backkey"=/obj/item/crowbar, - "desc"="Scanning module is installed."), - //16 - list("key"=/obj/item/circuitboard/mecha/phazon/targeting, - "backkey"=/obj/item/screwdriver, - "desc"="Peripherals control module is secured."), - //17 - list("key"=/obj/item/screwdriver, - "backkey"=/obj/item/crowbar, - "desc"="Peripherals control module is installed"), - //18 - list("key"=/obj/item/circuitboard/mecha/phazon/peripherals, - "backkey"=/obj/item/screwdriver, - "desc"="Central control module is secured."), - //19 - list("key"=/obj/item/screwdriver, - "backkey"=/obj/item/crowbar, - "desc"="Central control module is installed."), - //20 - list("key"=/obj/item/circuitboard/mecha/phazon/main, - "backkey"=/obj/item/screwdriver, - "desc"="The wiring is adjusted."), - //21 - list("key"=/obj/item/wirecutters, - "backkey"=/obj/item/screwdriver, - "desc"="The wiring is added."), - //22 - list("key"=/obj/item/stack/cable_coil, - "backkey"=/obj/item/screwdriver, - "desc"="The hydraulic systems are active."), - //23 - list("key"=/obj/item/screwdriver, - "backkey"=/obj/item/wrench, - "desc"="The hydraulic systems are connected."), - //24 - list("key"=/obj/item/wrench, - "desc"="The hydraulic systems are disconnected.") - ) - - -/datum/construction/reversible/mecha/phazon/action(atom/used_atom,mob/user as mob) - return check_step(used_atom,user) - -/datum/construction/reversible/mecha/phazon/custom_action(index, diff, atom/used_atom, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(24) - user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") - holder.icon_state = "phazon1" - if(23) - if(diff==FORWARD) - user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") - holder.icon_state = "phazon2" - else - user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") - holder.icon_state = "phazon0" - if(22) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") - holder.icon_state = "phazon3" - else - user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") - holder.icon_state = "phazon1" - if(21) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") - holder.icon_state = "phazon4" - else - user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") - var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) - coil.amount = 4 - holder.icon_state = "phazon2" - if(20) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") - qdel(used_atom) - holder.icon_state = "phazon5" - else - user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") - holder.icon_state = "phazon3" - if(19) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "phazon6" - else - user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") - new /obj/item/circuitboard/mecha/phazon/main(get_turf(holder)) - holder.icon_state = "phazon4" - if(18) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") - qdel(used_atom) - holder.icon_state = "phazon7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "phazon5" - if(17) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "phazon8" - else - user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") - new /obj/item/circuitboard/mecha/phazon/peripherals(get_turf(holder)) - holder.icon_state = "phazon6" - if(16) - if(diff==FORWARD) - user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") - qdel(used_atom) - holder.icon_state = "phazon9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "phazon7" - if(15) - if(diff==FORWARD) - user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") - holder.icon_state = "phazon10" - else - user.visible_message("[user] removes the weapon control module from the [holder].", "You remove the weapon control module from the [holder].") - new /obj/item/circuitboard/mecha/phazon/targeting(get_turf(holder)) - holder.icon_state = "phazon8" - if(14) - if(diff==FORWARD) - user.visible_message("[user] installs phasic scanner module to the [holder].", "You install phasic scanner module to the [holder].") - qdel(used_atom) - holder.icon_state = "phazon11" - else - user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") - holder.icon_state = "phazon9" - if(13) - if(diff==FORWARD) - user.visible_message("[user] secures the phasic scanner module.", "You secure the phasic scanner module.") - holder.icon_state = "phazon12" - else - user.visible_message("[user] removes the phasic scanner module from the [holder].", "You remove the phasic scanner module from the [holder].") - new /obj/item/stock_parts/scanning_module/phasic(get_turf(holder)) - holder.icon_state = "phazon10" - if(12) - if(diff==FORWARD) - user.visible_message("[user] installs super capacitor to the [holder].", "You install super capacitor to the [holder].") - qdel(used_atom) - holder.icon_state = "phazon13" - else - user.visible_message("[user] unfastens the phasic scanner module.", "You unfasten the phasic scanner module.") - holder.icon_state = "phazon11" - if(11) - if(diff==FORWARD) - user.visible_message("[user] secures the super capacitor.", "You secure the super capacitor.") - holder.icon_state = "phazon14" - else - user.visible_message("[user] removes the super capacitor from the [holder].", "You remove the super capacitor from the [holder].") - new /obj/item/stock_parts/capacitor/super(get_turf(holder)) - holder.icon_state = "phazon12" - if(10) - if(diff==FORWARD) - user.visible_message("[user] installs the bluespace crystal.", "You install the bluespace crystals.") - holder.icon_state = "phazon15" - else - user.visible_message("[user] unsecures the super capacitor from the [holder].", "You unsecure the super capacitor from the [holder].") - holder.icon_state = "phazon13" - if(9) - if(diff==FORWARD) - user.visible_message("[user] connects the bluespace crystal.", "You connect the bluespace crystals.") - holder.icon_state = "phazon16" - else - user.visible_message("[user] removes the bluespace crystal from the [holder].", "You remove the bluespace crystal from the [holder].") - new /obj/item/stack/ore/bluespace_crystal(get_turf(holder), new_amount = 5) - holder.icon_state = "phazon14" - if(8) - if(diff==FORWARD) - user.visible_message("[user] engages the bluespace crystal.", "You engage the bluespace crystals.") - holder.icon_state = "phazon17" - else - user.visible_message("[user] disconnects the bluespace crystal from the [holder].", "You disconnect the bluespace crystal from the [holder].") - holder.icon_state = "phazon15" - if(7) - if(diff==FORWARD) - user.visible_message("[user] installs the phase armor layer to the [holder].", "You install the phase armor layer to the [holder].") - holder.icon_state = "phazon18" - else - user.visible_message("[user] disengages the bluespace crystal.", "You disengage the bluespace crystals.") - holder.icon_state = "phazon16" - if(6) - if(diff==FORWARD) - user.visible_message("[user] secures the phase armor layer.", "You secure the phase armor layer.") - holder.icon_state = "phazon19" - else - user.visible_message("[user] pries the phase armor layer from the [holder].", "You pry the phase armor layer from the [holder].") - var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) - MS.amount = 5 - holder.icon_state = "phazon17" - if(5) - if(diff==FORWARD) - user.visible_message("[user] welds the phase armor layer to the [holder].", "You weld the phase armor layer to the [holder].") - holder.icon_state = "phazon20" - else - user.visible_message("[user] unfastens the phase armor layer.", "You unfasten the phase armor layer.") - holder.icon_state = "phazon18" - if(4) - if(diff==FORWARD) - user.visible_message("[user] installs Phazon Armor Plates to the [holder].", "You install Phazon Armor Plates to the [holder].") - qdel(used_atom) - holder.icon_state = "phazon21" - else - user.visible_message("[user] cuts phase armor layer from the [holder].", "You cut the phase armor layer from the [holder].") - holder.icon_state = "phazon19" - if(3) - if(diff==FORWARD) - user.visible_message("[user] secures Phazon Armor Plates.", "You secure Phazon Armor Plates.") - holder.icon_state = "phazon22" - else - user.visible_message("[user] pries Phazon Armor Plates from the [holder].", "You pry Phazon Armor Plates from the [holder].") - new /obj/item/mecha_parts/part/phazon_armor(get_turf(holder)) - holder.icon_state = "phazon20" - if(2) - if(diff==FORWARD) - user.visible_message("[user] welds Phazon Armor Plates to the [holder].", "You weld Phazon Armor Plates to the [holder].") - else - user.visible_message("[user] unfastens Phazon Armor Plates.", "You unfasten Phazon Armor Plates.") - holder.icon_state = "phazon21" - if(1) - if(diff==FORWARD) - user.visible_message("[user] carefully inserts the anomaly core into \the [holder] and secures it.", "You slowly place the anomaly core into its socket and close its chamber.") - qdel(used_atom) - return 1 - -/datum/construction/reversible/mecha/phazon/spawn_result() - ..() - feedback_inc("mecha_phazon_created",1) - return - -//ODYSSEUS - -/datum/construction/mecha/odysseus_chassis - steps = list(list("key"=/obj/item/mecha_parts/part/odysseus_torso),//1 - list("key"=/obj/item/mecha_parts/part/odysseus_head),//2 - list("key"=/obj/item/mecha_parts/part/odysseus_left_arm),//3 - list("key"=/obj/item/mecha_parts/part/odysseus_right_arm),//4 - list("key"=/obj/item/mecha_parts/part/odysseus_left_leg),//5 - list("key"=/obj/item/mecha_parts/part/odysseus_right_leg)//6 - ) - -/datum/construction/mecha/odysseus_chassis/custom_action(step, atom/used_atom, mob/user) - user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") - holder.overlays += used_atom.icon_state+"+o" - qdel(used_atom) - return 1 - -/datum/construction/mecha/odysseus_chassis/action(atom/used_atom,mob/user as mob) - return check_all_steps(used_atom,user) - -/datum/construction/mecha/odysseus_chassis/spawn_result() - var/obj/item/mecha_parts/chassis/const_holder = holder - const_holder.construct = new /datum/construction/reversible/mecha/odysseus(const_holder) - const_holder.icon = 'icons/mecha/mech_construction.dmi' - const_holder.icon_state = "odysseus0" - const_holder.density = 1 - qdel(src) - return - - -/datum/construction/reversible/mecha/odysseus - result = "/obj/mecha/medical/odysseus" - steps = list( - //1 - list("key"=/obj/item/weldingtool, - "backkey"=/obj/item/wrench, - "desc"="External armor is wrenched."), - //2 - list("key"=/obj/item/wrench, - "backkey"=/obj/item/crowbar, - "desc"="External armor is installed."), - //3 - list("key"=/obj/item/stack/sheet/plasteel, - "backkey"=/obj/item/weldingtool, - "desc"="Internal armor is welded."), - //4 - list("key"=/obj/item/weldingtool, - "backkey"=/obj/item/wrench, - "desc"="Internal armor is wrenched."), - //5 - list("key"=/obj/item/wrench, - "backkey"=/obj/item/crowbar, - "desc"="Internal armor is installed."), - //6 - list("key"=/obj/item/stack/sheet/metal, - "backkey"=/obj/item/screwdriver, - "desc"="Peripherals control module is secured."), - //7 - list("key"=/obj/item/screwdriver, - "backkey"=/obj/item/crowbar, - "desc"="Peripherals control module is installed."), - //8 - list("key"=/obj/item/circuitboard/mecha/odysseus/peripherals, - "backkey"=/obj/item/screwdriver, - "desc"="Central control module is secured."), - //9 - list("key"=/obj/item/screwdriver, - "backkey"=/obj/item/crowbar, - "desc"="Central control module is installed."), - //10 - list("key"=/obj/item/circuitboard/mecha/odysseus/main, - "backkey"=/obj/item/screwdriver, - "desc"="The wiring is adjusted."), - //11 - list("key"=/obj/item/wirecutters, - "backkey"=/obj/item/screwdriver, - "desc"="The wiring is added."), - //12 - list("key"=/obj/item/stack/cable_coil, - "backkey"=/obj/item/screwdriver, - "desc"="The hydraulic systems are active."), - //13 - list("key"=/obj/item/screwdriver, - "backkey"=/obj/item/wrench, - "desc"="The hydraulic systems are connected."), - //14 - list("key"=/obj/item/wrench, - "desc"="The hydraulic systems are disconnected.") - ) - -/datum/construction/reversible/mecha/odysseus/action(atom/used_atom,mob/user as mob) - return check_step(used_atom,user) - -/datum/construction/reversible/mecha/odysseus/custom_action(index, diff, atom/used_atom, mob/user) - if(!..()) - return 0 - - //TODO: better messages. - switch(index) - if(14) - user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") - holder.icon_state = "odysseus1" - if(13) - if(diff==FORWARD) - user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") - holder.icon_state = "odysseus2" - else - user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") - holder.icon_state = "odysseus0" - if(12) - if(diff==FORWARD) - user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") - holder.icon_state = "odysseus3" - else - user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") - holder.icon_state = "odysseus1" - if(11) - if(diff==FORWARD) - user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") - holder.icon_state = "odysseus4" - else - user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") - var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) - coil.amount = 4 - holder.icon_state = "odysseus2" - if(10) - if(diff==FORWARD) - user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") - qdel(used_atom) - holder.icon_state = "odysseus5" - else - user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") - holder.icon_state = "odysseus3" - if(9) - if(diff==FORWARD) - user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") - holder.icon_state = "odysseus6" - else - user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") - new /obj/item/circuitboard/mecha/odysseus/main(get_turf(holder)) - holder.icon_state = "odysseus4" - if(8) - if(diff==FORWARD) - user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") - qdel(used_atom) - holder.icon_state = "odysseus7" - else - user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") - holder.icon_state = "odysseus5" - if(7) - if(diff==FORWARD) - user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") - holder.icon_state = "odysseus8" - else - user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") - new /obj/item/circuitboard/mecha/odysseus/peripherals(get_turf(holder)) - holder.icon_state = "odysseus6" - if(6) - if(diff==FORWARD) - user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") - holder.icon_state = "odysseus9" - else - user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") - holder.icon_state = "odysseus7" - if(5) - if(diff==FORWARD) - user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") - holder.icon_state = "odysseus10" - else - user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") - var/obj/item/stack/sheet/metal/MS = new /obj/item/stack/sheet/metal(get_turf(holder)) - MS.amount = 5 - holder.icon_state = "odysseus8" - if(4) - if(diff==FORWARD) - user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") - holder.icon_state = "odysseus11" - else - user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") - holder.icon_state = "odysseus9" - if(3) - if(diff==FORWARD) - user.visible_message("[user] installs [used_atom] layer to the [holder].", "You install the external reinforced armor layer to the [holder].") - - holder.icon_state = "odysseus12" - else - user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") - holder.icon_state = "odysseus10" - if(2) - if(diff==FORWARD) - user.visible_message("[user] secures the external armor layer.", "You secure the external reinforced armor layer.") - holder.icon_state = "odysseus13" - else - var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) - MS.amount = 5 - user.visible_message("[user] pries [MS] from the [holder].", "You pry [MS] from the [holder].") - holder.icon_state = "odysseus11" - if(1) - if(diff==FORWARD) - user.visible_message("[user] welds the external armor layer to the [holder].", "You weld the external armor layer to the [holder].") - holder.icon_state = "odysseus14" - else - user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") - holder.icon_state = "odysseus12" - return 1 - -/datum/construction/reversible/mecha/odysseus/spawn_result() - ..() - feedback_inc("mecha_odysseus_created",1) - return +#define STANDARD_STACK_AMOUNT 5 + +//////////////////////////////// +///// Construction datums ////// +//////////////////////////////// + +/datum/construction/mecha/custom_action(step, atom/used_atom, mob/user) + if(istype(used_atom, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = used_atom + if(C.use(4)) + playsound(holder, C.usesound, 50, 1) + else + to_chat(user, ("There's not enough cable to finish the task.")) + return 0 + else if(istype(used_atom, /obj/item/stack)) + var/obj/item/stack/S = used_atom + if(S.amount < STANDARD_STACK_AMOUNT) + to_chat(user, ("There's not enough material in this stack.")) + return 0 + else + S.use(STANDARD_STACK_AMOUNT) + else + return ..() + +/datum/construction/reversible/mecha/custom_action(index as num, diff as num, atom/used_atom, mob/user as mob) + if(istype(used_atom, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = used_atom + if(C.use(4)) + playsound(holder, C.usesound, 50, 1) + else + to_chat(user, ("There's not enough cable to finish the task.")) + return 0 + else if(istype(used_atom, /obj/item/stack)) + var/obj/item/stack/S = used_atom + if(S.amount < STANDARD_STACK_AMOUNT) + to_chat(user, ("There's not enough material in this stack.")) + return 0 + else + S.use(STANDARD_STACK_AMOUNT) + else if(isitem(used_atom)) + var/obj/item/I = used_atom + if(I.tool_behaviour in CONSTRUCTION_TOOL_BEHAVIOURS) + if(!I.use_tool(holder, user, 0, volume = I.tool_volume)) + return 0 + return 1 + + +/datum/construction/mecha/ripley_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/ripley_torso),//1 + list("key"=/obj/item/mecha_parts/part/ripley_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/ripley_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/ripley_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/ripley_right_leg)//5 + ) + +/datum/construction/mecha/ripley_chassis/custom_action(step, atom/used_atom, mob/user) + user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") + holder.overlays += used_atom.icon_state+"+o" + qdel(used_atom) + return 1 + +/datum/construction/mecha/ripley_chassis/action(atom/used_atom,mob/user as mob) + return check_all_steps(used_atom,user) + +/datum/construction/mecha/ripley_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/ripley(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "ripley0" + const_holder.density = 1 + const_holder.overlays.len = 0 + qdel(src) + return + + +/datum/construction/reversible/mecha/ripley + result = "/obj/mecha/working/ripley" + taskpath = /datum/job_objective/make_ripley + steps = list( + //1 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/stack/sheet/plasteel, + "backkey"=TOOL_WELDER, + "desc"="Internal armor is welded."), + //4 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="Internal armor is wrenched."), + //5 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="Internal armor is installed."), + //6 + list("key"=/obj/item/stack/sheet/metal, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Peripherals control module is secured."), + //7 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Peripherals control module is installed."), + //8 + list("key"=/obj/item/circuitboard/mecha/ripley/peripherals, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Central control module is secured."), + //9 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Central control module is installed."), + //10 + list("key"=/obj/item/circuitboard/mecha/ripley/main, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is adjusted."), + //11 + list("key"=/obj/item/wirecutters, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is added."), + //12 + list("key"=/obj/item/stack/cable_coil, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //13 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_WRENCH, + "desc"="The hydraulic systems are connected."), + //14 + list("key"=TOOL_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + +/datum/construction/reversible/mecha/ripley/action(atom/used_atom,mob/user as mob) + return check_step(used_atom,user) + +/datum/construction/reversible/mecha/ripley/custom_action(index, diff, atom/used_atom, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(14) + user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") + holder.icon_state = "ripley1" + if(13) + if(diff==FORWARD) + user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") + holder.icon_state = "ripley2" + else + user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") + holder.icon_state = "ripley0" + if(12) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") + holder.icon_state = "ripley3" + else + user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") + holder.icon_state = "ripley1" + if(11) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") + holder.icon_state = "ripley4" + else + user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") + var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) + coil.amount = 4 + holder.icon_state = "ripley2" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") + qdel(used_atom) + holder.icon_state = "ripley5" + else + user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") + holder.icon_state = "ripley3" + if(9) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "ripley6" + else + user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") + new /obj/item/circuitboard/mecha/ripley/main(get_turf(holder)) + holder.icon_state = "ripley4" + if(8) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") + qdel(used_atom) + holder.icon_state = "ripley7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "ripley5" + if(7) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "ripley8" + else + user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") + new /obj/item/circuitboard/mecha/ripley/peripherals(get_turf(holder)) + holder.icon_state = "ripley6" + if(6) + if(diff==FORWARD) + user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") + holder.icon_state = "ripley9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "ripley7" + if(5) + if(diff==FORWARD) + user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") + holder.icon_state = "ripley10" + else + user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") + var/obj/item/stack/sheet/metal/MS = new /obj/item/stack/sheet/metal(get_turf(holder)) + MS.amount = 5 + holder.icon_state = "ripley8" + if(4) + if(diff==FORWARD) + user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") + holder.icon_state = "ripley11" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "ripley9" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs the external reinforced armor layer to the [holder].", "You install the external reinforced armor layer to the [holder].") + holder.icon_state = "ripley12" + else + user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") + holder.icon_state = "ripley10" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures the external armor layer.", "You secure the external reinforced armor layer.") + holder.icon_state = "ripley13" + else + user.visible_message("[user] pries external armor layer from the [holder].", "You pry external armor layer from the [holder].") + var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) + MS.amount = 5 + holder.icon_state = "ripley11" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds the external armor layer to the [holder].", "You weld the external armor layer to the [holder].") + else + user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") + holder.icon_state = "ripley12" + return 1 + +/datum/construction/reversible/mecha/ripley/spawn_result() + ..() + feedback_inc("mecha_ripley_created",1) + return + + + +/datum/construction/mecha/gygax_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/gygax_torso),//1 + list("key"=/obj/item/mecha_parts/part/gygax_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/gygax_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/gygax_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/gygax_right_leg),//5 + list("key"=/obj/item/mecha_parts/part/gygax_head) + ) + +/datum/construction/mecha/gygax_chassis/custom_action(step, atom/used_atom, mob/user) + user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") + holder.overlays += used_atom.icon_state+"+o" + qdel(used_atom) + return 1 + +/datum/construction/mecha/gygax_chassis/action(atom/used_atom,mob/user as mob) + return check_all_steps(used_atom,user) + +/datum/construction/mecha/gygax_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/gygax(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "gygax0" + const_holder.density = 1 + qdel(src) + return + + +/datum/construction/reversible/mecha/gygax + result = "/obj/mecha/combat/gygax" + steps = list( + //1 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/mecha_parts/part/gygax_armour, + "backkey"=TOOL_WELDER, + "desc"="Internal armor is welded."), + //4 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="Internal armor is wrenched."), + //5 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="Internal armor is installed."), + //6 + list("key"=/obj/item/stack/sheet/metal, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Advanced capacitor is secured."), + //7 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Advanced capacitor is installed."), + //8 + list("key"=/obj/item/stock_parts/capacitor/adv, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Advanced scanner module is secured."), + //9 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Advanced scanner module is installed."), + //10 + list("key"=/obj/item/stock_parts/scanning_module/adv, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Scanning module is secured."), + //11 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Scanning module is installed."), + //12 + list("key"=/obj/item/circuitboard/mecha/gygax/targeting, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Peripherals control module is secured."), + //13 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Peripherals control module is installed."), + //14 + list("key"=/obj/item/circuitboard/mecha/gygax/peripherals, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Central control module is secured."), + //15 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Central control module is installed."), + //16 + list("key"=/obj/item/circuitboard/mecha/gygax/main, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is adjusted."), + //17 + list("key"=/obj/item/wirecutters, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is added."), + //18 + list("key"=/obj/item/stack/cable_coil, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //19 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_WRENCH, + "desc"="The hydraulic systems are connected."), + //20 + list("key"=TOOL_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + +/datum/construction/reversible/mecha/gygax/action(atom/used_atom,mob/user as mob) + return check_step(used_atom,user) + +/datum/construction/reversible/mecha/gygax/custom_action(index, diff, atom/used_atom, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(20) + user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") + holder.icon_state = "gygax1" + if(19) + if(diff==FORWARD) + user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") + holder.icon_state = "gygax2" + else + user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") + holder.icon_state = "gygax0" + if(18) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") + holder.icon_state = "gygax3" + else + user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") + holder.icon_state = "gygax1" + if(17) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") + holder.icon_state = "gygax4" + else + user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") + var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) + coil.amount = 4 + holder.icon_state = "gygax2" + if(16) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") + qdel(used_atom) + holder.icon_state = "gygax5" + else + user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") + holder.icon_state = "gygax3" + if(15) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "gygax6" + else + user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") + new /obj/item/circuitboard/mecha/gygax/main(get_turf(holder)) + holder.icon_state = "gygax4" + if(14) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") + qdel(used_atom) + holder.icon_state = "gygax7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "gygax5" + if(13) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "gygax8" + else + user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") + new /obj/item/circuitboard/mecha/gygax/peripherals(get_turf(holder)) + holder.icon_state = "gygax6" + if(12) + if(diff==FORWARD) + user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") + qdel(used_atom) + holder.icon_state = "gygax9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "gygax7" + if(11) + if(diff==FORWARD) + user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") + holder.icon_state = "gygax10" + else + user.visible_message("[user] removes the weapon control module from the [holder].", "You remove the weapon control module from the [holder].") + new /obj/item/circuitboard/mecha/gygax/targeting(get_turf(holder)) + holder.icon_state = "gygax8" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs advanced scanner module to the [holder].", "You install advanced scanner module to the [holder].") + qdel(used_atom) + holder.icon_state = "gygax11" + else + user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") + holder.icon_state = "gygax9" + if(9) + if(diff==FORWARD) + user.visible_message("[user] secures the advanced scanner module.", "You secure the advanced scanner module.") + holder.icon_state = "gygax12" + else + user.visible_message("[user] removes the advanced scanner module from the [holder].", "You remove the advanced scanner module from the [holder].") + new /obj/item/stock_parts/scanning_module/adv(get_turf(holder)) + holder.icon_state = "gygax10" + if(8) + if(diff==FORWARD) + user.visible_message("[user] installs advanced capacitor to the [holder].", "You install advanced capacitor to the [holder].") + qdel(used_atom) + holder.icon_state = "gygax13" + else + user.visible_message("[user] unfastens the advanced scanner module.", "You unfasten the advanced scanner module.") + holder.icon_state = "gygax11" + if(7) + if(diff==FORWARD) + user.visible_message("[user] secures the advanced capacitor.", "You secure the advanced capacitor.") + holder.icon_state = "gygax14" + else + user.visible_message("[user] removes the advanced capacitor from the [holder].", "You remove the advanced capacitor from the [holder].") + new /obj/item/stock_parts/capacitor/adv(get_turf(holder)) + holder.icon_state = "gygax12" + if(6) + if(diff==FORWARD) + user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") + holder.icon_state = "gygax15" + else + user.visible_message("[user] unfastens the advanced capacitor.", "You unfasten the advanced capacitor.") + holder.icon_state = "gygax13" + if(5) + if(diff==FORWARD) + user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") + holder.icon_state = "gygax16" + else + user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") + var/obj/item/stack/sheet/metal/MS = new /obj/item/stack/sheet/metal(get_turf(holder)) + MS.amount = 5 + holder.icon_state = "gygax14" + if(4) + if(diff==FORWARD) + user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") + holder.icon_state = "gygax17" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "gygax15" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs Gygax Armor Plates to the [holder].", "You install Gygax Armor Plates to the [holder].") + qdel(used_atom) + holder.icon_state = "gygax18" + else + user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") + holder.icon_state = "gygax16" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures Gygax Armor Plates.", "You secure Gygax Armor Plates.") + holder.icon_state = "gygax19" + else + user.visible_message("[user] pries Gygax Armor Plates from the [holder].", "You pry Gygax Armor Plates from the [holder].") + new /obj/item/mecha_parts/part/gygax_armour(get_turf(holder)) + holder.icon_state = "gygax17" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds Gygax Armor Plates to the [holder].", "You weld Gygax Armor Plates to the [holder].") + else + user.visible_message("[user] unfastens Gygax Armor Plates.", "You unfasten Gygax Armor Plates.") + holder.icon_state = "gygax18" + return 1 + +/datum/construction/reversible/mecha/gygax/spawn_result() + ..() + feedback_inc("mecha_gygax_created",1) + return + +/datum/construction/mecha/firefighter_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/ripley_torso),//1 + list("key"=/obj/item/mecha_parts/part/ripley_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/ripley_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/ripley_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/ripley_right_leg),//5 + list("key"=/obj/item/clothing/suit/fire)//6 + ) + +/datum/construction/mecha/firefighter_chassis/custom_action(step, atom/used_atom, mob/user) + user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") + holder.overlays += used_atom.icon_state+"+o" + qdel(used_atom) + return 1 + +/datum/construction/mecha/firefighter_chassis/action(atom/used_atom,mob/user as mob) + return check_all_steps(used_atom,user) + +/datum/construction/mecha/firefighter_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/firefighter(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "fireripley0" + const_holder.density = 1 + qdel(src) + return + + +/datum/construction/reversible/mecha/firefighter + result = "/obj/mecha/working/ripley/firefighter" + taskpath = /datum/job_objective/make_ripley + steps = list( + //1 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/stack/sheet/plasteel, + "backkey"=TOOL_CROWBAR, + "desc"="External armor is being installed."), + //4 + list("key"=/obj/item/stack/sheet/plasteel, + "backkey"=TOOL_WELDER, + "desc"="Internal armor is welded."), + //5 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="Internal armor is wrenched."), + //6 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="Internal armor is installed."), + + //7 + list("key"=/obj/item/stack/sheet/plasteel, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Peripherals control module is secured."), + //8 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Peripherals control module is installed."), + //9 + list("key"=/obj/item/circuitboard/mecha/ripley/peripherals, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Central control module is secured."), + //10 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Central control module is installed."), + //11 + list("key"=/obj/item/circuitboard/mecha/ripley/main, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is adjusted."), + //12 + list("key"=/obj/item/wirecutters, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is added."), + //13 + list("key"=/obj/item/stack/cable_coil, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //14 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_WRENCH, + "desc"="The hydraulic systems are connected."), + //15 + list("key"=TOOL_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + +/datum/construction/reversible/mecha/firefighter/action(atom/used_atom,mob/user as mob) + return check_step(used_atom,user) + +/datum/construction/reversible/mecha/firefighter/custom_action(index, diff, atom/used_atom, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(15) + user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") + holder.icon_state = "fireripley1" + if(14) + if(diff==FORWARD) + user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") + holder.icon_state = "fireripley2" + else + user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") + holder.icon_state = "fireripley0" + if(13) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") + holder.icon_state = "fireripley3" + else + user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") + holder.icon_state = "fireripley1" + if(12) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") + holder.icon_state = "fireripley4" + else + user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") + var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) + coil.amount = 4 + holder.icon_state = "fireripley2" + if(11) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") + qdel(used_atom) + holder.icon_state = "fireripley5" + else + user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") + holder.icon_state = "fireripley3" + if(10) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "fireripley6" + else + user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") + new /obj/item/circuitboard/mecha/ripley/main(get_turf(holder)) + holder.icon_state = "fireripley4" + if(9) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") + qdel(used_atom) + holder.icon_state = "fireripley7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "fireripley5" + if(8) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "fireripley8" + else + user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") + new /obj/item/circuitboard/mecha/ripley/peripherals(get_turf(holder)) + holder.icon_state = "fireripley6" + if(7) + if(diff==FORWARD) + user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") + holder.icon_state = "fireripley9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "fireripley7" + + if(6) + if(diff==FORWARD) + user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") + holder.icon_state = "fireripley10" + else + user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") + var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) + MS.amount = 5 + holder.icon_state = "fireripley8" + if(5) + if(diff==FORWARD) + user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") + holder.icon_state = "fireripley11" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "fireripley9" + if(4) + if(diff==FORWARD) + user.visible_message("[user] starts to install the external armor layer to the [holder].", "You start to install the external armor layer to the [holder].") + holder.icon_state = "fireripley12" + else + user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") + holder.icon_state = "fireripley10" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs the external reinforced armor layer to the [holder].", "You install the external reinforced armor layer to the [holder].") + holder.icon_state = "fireripley13" + else + user.visible_message("[user] removes the external armor from the [holder].", "You remove the external armor from the [holder].") + var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) + MS.amount = 5 + holder.icon_state = "fireripley11" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures the external armor layer.", "You secure the external reinforced armor layer.") + holder.icon_state = "fireripley14" + else + user.visible_message("[user] pries external armor layer from the [holder].", "You pry external armor layer from the [holder].") + var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) + MS.amount = 5 + holder.icon_state = "fireripley12" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds the external armor layer to the [holder].", "You weld the external armor layer to the [holder].") + else + user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") + holder.icon_state = "fireripley13" + return 1 + +/datum/construction/reversible/mecha/firefighter/spawn_result() + ..() + feedback_inc("mecha_firefighter_created",1) + return + + + +/datum/construction/mecha/honker_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/honker_torso),//1 + list("key"=/obj/item/mecha_parts/part/honker_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/honker_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/honker_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/honker_right_leg),//5 + list("key"=/obj/item/mecha_parts/part/honker_head) + ) + +/datum/construction/mecha/honker_chassis/action(atom/used_atom,mob/user as mob) + return check_all_steps(used_atom,user) + +/datum/construction/mecha/honker_chassis/custom_action(step, atom/used_atom, mob/user) + user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") + holder.overlays += used_atom.icon_state+"+o" + qdel(used_atom) + return 1 + +/datum/construction/mecha/honker_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/mecha/honker(const_holder) + const_holder.density = 1 + qdel(src) + return + + +/datum/construction/mecha/honker + result = "/obj/mecha/combat/honker" + steps = list(list("key"=/obj/item/bikehorn),//1 + list("key"=/obj/item/clothing/shoes/clown_shoes),//2 + list("key"=/obj/item/bikehorn),//3 + list("key"=/obj/item/clothing/mask/gas/clown_hat),//4 + list("key"=/obj/item/bikehorn),//5 + list("key"=/obj/item/circuitboard/mecha/honker/targeting),//6 + list("key"=/obj/item/bikehorn),//7 + list("key"=/obj/item/circuitboard/mecha/honker/peripherals),//8 + list("key"=/obj/item/bikehorn),//9 + list("key"=/obj/item/circuitboard/mecha/honker/main),//10 + list("key"=/obj/item/bikehorn),//11 + ) + +/datum/construction/mecha/honker/action(atom/used_atom,mob/user as mob) + return check_step(used_atom,user) + +/datum/construction/mecha/honker/custom_action(step, atom/used_atom, mob/user) + if(!..()) + return 0 + + if(istype(used_atom, /obj/item/bikehorn)) + playsound(holder, 'sound/items/bikehorn.ogg', 50, 1) + user.visible_message("HONK!") + + //TODO: better messages. + switch(step) + if(10) + user.visible_message("[user] installs the central control module into the [holder].", "You install the central control module into the [holder].") + qdel(used_atom) + if(8) + user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") + qdel(used_atom) + if(6) + user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") + qdel(used_atom) + if(4) + user.visible_message("[user] puts clown wig and mask on the [holder].", "You put clown wig and mask on the [holder].") + qdel(used_atom) + if(2) + user.visible_message("[user] puts clown boots on the [holder].", "You put clown boots on the [holder].") + qdel(used_atom) + return 1 + +/datum/construction/mecha/honker/spawn_result() + ..() + feedback_inc("mecha_honker_created",1) + return + +/datum/construction/mecha/reticence_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/reticence_torso),//1 + list("key"=/obj/item/mecha_parts/part/reticence_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/reticence_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/reticence_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/reticence_right_leg),//5 + list("key"=/obj/item/mecha_parts/part/reticence_head) + ) + +/datum/construction/mecha/reticence_chassis/action(atom/used_atom,mob/user as mob) + return check_all_steps(used_atom,user) + +/datum/construction/mecha/reticence_chassis/custom_action(step, atom/used_atom, mob/user) + user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") + holder.overlays += used_atom.icon_state + "+o" + qdel(used_atom) + return 1 + +/datum/construction/mecha/reticence_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/mecha/reticence(const_holder) + const_holder.density = 1 + qdel(src) + return + +/datum/construction/mecha/reticence + result = "/obj/mecha/combat/reticence" + steps = list(list("key"=/obj/effect/dummy/mecha_emote_step),//1 + list("key"=/obj/item/clothing/suit/suspenders),//2 + list("key"=/obj/effect/dummy/mecha_emote_step),//3 + list("key"=/obj/item/clothing/mask/gas/mime),//4 + list("key"=/obj/effect/dummy/mecha_emote_step),//5 + list("key"=/obj/item/clothing/head/beret),//6 + list("key"=/obj/item/circuitboard/mecha/reticence/targeting),//7 + list("key"=/obj/item/circuitboard/mecha/reticence/peripherals),//8 + list("key"=/obj/item/circuitboard/mecha/reticence/main),//9 + ) + +/datum/construction/mecha/reticence/action(atom/used_atom,mob/user) + return check_step(used_atom,user) + +/datum/construction/mecha/reticence/custom_action(step, atom/used_atom, mob/user) + if(!..()) + return 0 + + if(istype(used_atom, /obj/effect/dummy/mecha_emote_step)) + var/obj/effect/dummy/mecha_emote_step/E = used_atom + holder.visible_message("[holder] likewise [E.emote]") + qdel(used_atom) + + //TODO: better messages. + switch(step) + if(9) + user.visible_message("[user] installs the central control module into the [holder].", "You install the central control module into the [holder].") + qdel(used_atom) + if(8) + user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") + qdel(used_atom) + if(7) + user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") + qdel(used_atom) + if(6) + user.visible_message("[user] puts beret on the [holder].", "You put beret on the [holder].") + qdel(used_atom) + if(4) + user.visible_message("[user] puts mime mask on the [holder].", "You put mime mask on the [holder].") + qdel(used_atom) + if(2) + user.visible_message("[user] puts suspenders on the [holder].", "You put suspenders on the [holder].") + qdel(used_atom) + return 1 + +/datum/construction/mecha/reticence/spawn_result() + ..() + feedback_inc("mecha_reticence_created",1) + return + +/datum/construction/mecha/durand_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/durand_torso),//1 + list("key"=/obj/item/mecha_parts/part/durand_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/durand_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/durand_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/durand_right_leg),//5 + list("key"=/obj/item/mecha_parts/part/durand_head) + ) + +/datum/construction/mecha/durand_chassis/custom_action(step, atom/used_atom, mob/user) + user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") + holder.overlays += used_atom.icon_state+"+o" + qdel(used_atom) + return 1 + +/datum/construction/mecha/durand_chassis/action(atom/used_atom,mob/user as mob) + return check_all_steps(used_atom,user) + +/datum/construction/mecha/durand_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/durand(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "durand0" + const_holder.density = 1 + qdel(src) + return + +/datum/construction/reversible/mecha/durand + result = "/obj/mecha/combat/durand" + steps = list( + //1 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/mecha_parts/part/durand_armor, + "backkey"=TOOL_WELDER, + "desc"="Internal armor is welded."), + //4 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="Internal armor is wrenched."), + //5 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="Internal armor is installed."), + //6 + list("key"=/obj/item/stack/sheet/metal, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Super capacitor is secured."), + //7 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Super capacitor is installed."), + //8 + list("key"=/obj/item/stock_parts/capacitor/super, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Phasic scanner module is secured."), + //9 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Phasic scanner module is installed."), + //10 + list("key"=/obj/item/stock_parts/scanning_module/phasic, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Scanning module is secured."), + //11 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Scanning module is installed."), + //12 + list("key"=/obj/item/circuitboard/mecha/durand/targeting, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Peripherals control module is secured."), + //13 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Peripherals control module is installed."), + //14 + list("key"=/obj/item/circuitboard/mecha/durand/peripherals, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Central control module is secured."), + //15 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Central control module is installed."), + //16 + list("key"=/obj/item/circuitboard/mecha/durand/main, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is adjusted."), + //17 + list("key"=/obj/item/wirecutters, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is added."), + //18 + list("key"=/obj/item/stack/cable_coil, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //19 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_WRENCH, + "desc"="The hydraulic systems are connected."), + //20 + list("key"=TOOL_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + + +/datum/construction/reversible/mecha/durand/action(atom/used_atom,mob/user as mob) + return check_step(used_atom,user) + +/datum/construction/reversible/mecha/durand/custom_action(index, diff, atom/used_atom, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(20) + user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") + holder.icon_state = "durand1" + if(19) + if(diff==FORWARD) + user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") + holder.icon_state = "durand2" + else + user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") + holder.icon_state = "durand0" + if(18) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") + holder.icon_state = "durand3" + else + user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") + holder.icon_state = "durand1" + if(17) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") + holder.icon_state = "durand4" + else + user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") + var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) + coil.amount = 4 + holder.icon_state = "durand2" + if(16) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") + qdel(used_atom) + holder.icon_state = "durand5" + else + user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") + holder.icon_state = "durand3" + if(15) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "durand6" + else + user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") + new /obj/item/circuitboard/mecha/durand/main(get_turf(holder)) + holder.icon_state = "durand4" + if(14) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") + qdel(used_atom) + holder.icon_state = "durand7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "durand5" + if(13) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "durand8" + else + user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") + new /obj/item/circuitboard/mecha/durand/peripherals(get_turf(holder)) + holder.icon_state = "durand6" + if(12) + if(diff==FORWARD) + user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") + qdel(used_atom) + holder.icon_state = "durand9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "durand7" + if(11) + if(diff==FORWARD) + user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") + holder.icon_state = "durand10" + else + user.visible_message("[user] removes the weapon control module from the [holder].", "You remove the weapon control module from the [holder].") + new /obj/item/circuitboard/mecha/durand/targeting(get_turf(holder)) + holder.icon_state = "durand8" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs phasic scanner module to the [holder].", "You install phasic scanner module to the [holder].") + qdel(used_atom) + holder.icon_state = "durand11" + else + user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") + holder.icon_state = "durand9" + if(9) + if(diff==FORWARD) + user.visible_message("[user] secures the phasic scanner module.", "You secure the phasic scanner module.") + holder.icon_state = "durand12" + else + user.visible_message("[user] removes the phasic scanner module from the [holder].", "You remove the phasic scanner module from the [holder].") + new /obj/item/stock_parts/scanning_module/phasic(get_turf(holder)) + holder.icon_state = "durand10" + if(8) + if(diff==FORWARD) + user.visible_message("[user] installs super capacitor to the [holder].", "You install super capacitor to the [holder].") + qdel(used_atom) + holder.icon_state = "durand13" + else + user.visible_message("[user] unfastens the phasic scanner module.", "You unfasten the phasic scanner module.") + holder.icon_state = "durand11" + if(7) + if(diff==FORWARD) + user.visible_message("[user] secures the super capacitor.", "You secure the super capacitor.") + holder.icon_state = "durand14" + else + user.visible_message("[user] removes the super capacitor from the [holder].", "You remove the super capacitor from the [holder].") + new /obj/item/stock_parts/capacitor/super(get_turf(holder)) + holder.icon_state = "durand12" + if(6) + if(diff==FORWARD) + user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") + holder.icon_state = "durand15" + else + user.visible_message("[user] unfastens the super capacitor.", "You unfasten the super capacitor.") + holder.icon_state = "durand13" + if(5) + if(diff==FORWARD) + user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") + holder.icon_state = "durand16" + else + user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") + var/obj/item/stack/sheet/metal/MS = new /obj/item/stack/sheet/metal(get_turf(holder)) + MS.amount = 5 + holder.icon_state = "durand14" + if(4) + if(diff==FORWARD) + user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") + holder.icon_state = "durand17" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "durand15" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs Durand Armor Plates to the [holder].", "You install Durand Armor Plates to the [holder].") + qdel(used_atom) + holder.icon_state = "durand18" + else + user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") + holder.icon_state = "durand16" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures Durand Armor Plates.", "You secure Durand Armor Plates.") + holder.icon_state = "durand19" + else + user.visible_message("[user] pries Durand Armor Plates from the [holder].", "You pry Durand Armor Plates from the [holder].") + new /obj/item/mecha_parts/part/durand_armor(get_turf(holder)) + holder.icon_state = "durand17" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds Durand Armor Plates to the [holder].", "You weld Durand Armor Plates to the [holder].") + else + user.visible_message("[user] unfastens Durand Armor Plates.", "You unfasten Durand Armor Plates.") + holder.icon_state = "durand18" + return 1 + +/datum/construction/reversible/mecha/durand/spawn_result() + ..() + feedback_inc("mecha_durand_created",1) + return + +//PHAZON + +/datum/construction/mecha/phazon_chassis + result = "/obj/mecha/combat/phazon" + steps = list(list("key"=/obj/item/mecha_parts/part/phazon_torso),//1 + list("key"=/obj/item/mecha_parts/part/phazon_left_arm),//2 + list("key"=/obj/item/mecha_parts/part/phazon_right_arm),//3 + list("key"=/obj/item/mecha_parts/part/phazon_left_leg),//4 + list("key"=/obj/item/mecha_parts/part/phazon_right_leg),//5 + list("key"=/obj/item/mecha_parts/part/phazon_head) + ) + +/datum/construction/mecha/phazon_chassis/custom_action(step, atom/used_atom, mob/user) + user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") + holder.overlays += used_atom.icon_state+"+o" + qdel(used_atom) + return 1 + +/datum/construction/mecha/phazon_chassis/action(atom/used_atom,mob/user as mob) + return check_all_steps(used_atom,user) + +/datum/construction/mecha/phazon_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/phazon(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "phazon0" + const_holder.density = 1 + qdel(src) + return + +/datum/construction/reversible/mecha/phazon + result = "/obj/mecha/combat/phazon" + steps = list( + //1 + list("key"=/obj/item/assembly/signaler/anomaly, + "backkey"=null, //Cannot remove the anomaly core once it's in + "desc"="Anomaly core socket is open and awaiting connection."), + //2 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="External armor is wrenched."), + //3 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="External armor is installed."), + //4 + list("key"=/obj/item/mecha_parts/part/phazon_armor, + "backkey"=TOOL_WELDER, + "desc"="Phase armor is welded."), + //5 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="Phase armor is wrenched."), + //6 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="Phase armor is installed."), + //7 + list("key"=/obj/item/stack/sheet/plasteel, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The bluespace crystal is engaged."), + //8 + list("key"=TOOL_SCREWDRIVER, + "backkey"=/obj/item/wirecutters, + "desc"="The bluespace crystal is connected."), + //9 + list("key"=/obj/item/stack/cable_coil, + "backkey"=TOOL_CROWBAR, + "desc"="The bluespace crystal is installed."), + //10 + list("key"=/obj/item/stack/ore/bluespace_crystal, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Super capacitor is secured."), + //11 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Super capacitor is installed."), + //12 + list("key"=/obj/item/stock_parts/capacitor/super, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Phasic scanner module is secured."), + //13 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Phasic scanner module is installed."), + //14 + list("key"=/obj/item/stock_parts/scanning_module/phasic, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Scanning module is secured."), + //15 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Scanning module is installed."), + //16 + list("key"=/obj/item/circuitboard/mecha/phazon/targeting, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Peripherals control module is secured."), + //17 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Peripherals control module is installed"), + //18 + list("key"=/obj/item/circuitboard/mecha/phazon/peripherals, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Central control module is secured."), + //19 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Central control module is installed."), + //20 + list("key"=/obj/item/circuitboard/mecha/phazon/main, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is adjusted."), + //21 + list("key"=/obj/item/wirecutters, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is added."), + //22 + list("key"=/obj/item/stack/cable_coil, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //23 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_WRENCH, + "desc"="The hydraulic systems are connected."), + //24 + list("key"=TOOL_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + + +/datum/construction/reversible/mecha/phazon/action(atom/used_atom,mob/user as mob) + return check_step(used_atom,user) + +/datum/construction/reversible/mecha/phazon/custom_action(index, diff, atom/used_atom, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(24) + user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") + holder.icon_state = "phazon1" + if(23) + if(diff==FORWARD) + user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") + holder.icon_state = "phazon2" + else + user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") + holder.icon_state = "phazon0" + if(22) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") + holder.icon_state = "phazon3" + else + user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") + holder.icon_state = "phazon1" + if(21) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") + holder.icon_state = "phazon4" + else + user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") + var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) + coil.amount = 4 + holder.icon_state = "phazon2" + if(20) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") + qdel(used_atom) + holder.icon_state = "phazon5" + else + user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") + holder.icon_state = "phazon3" + if(19) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "phazon6" + else + user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") + new /obj/item/circuitboard/mecha/phazon/main(get_turf(holder)) + holder.icon_state = "phazon4" + if(18) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") + qdel(used_atom) + holder.icon_state = "phazon7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "phazon5" + if(17) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "phazon8" + else + user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") + new /obj/item/circuitboard/mecha/phazon/peripherals(get_turf(holder)) + holder.icon_state = "phazon6" + if(16) + if(diff==FORWARD) + user.visible_message("[user] installs the weapon control module into the [holder].", "You install the weapon control module into the [holder].") + qdel(used_atom) + holder.icon_state = "phazon9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "phazon7" + if(15) + if(diff==FORWARD) + user.visible_message("[user] secures the weapon control module.", "You secure the weapon control module.") + holder.icon_state = "phazon10" + else + user.visible_message("[user] removes the weapon control module from the [holder].", "You remove the weapon control module from the [holder].") + new /obj/item/circuitboard/mecha/phazon/targeting(get_turf(holder)) + holder.icon_state = "phazon8" + if(14) + if(diff==FORWARD) + user.visible_message("[user] installs phasic scanner module to the [holder].", "You install phasic scanner module to the [holder].") + qdel(used_atom) + holder.icon_state = "phazon11" + else + user.visible_message("[user] unfastens the weapon control module.", "You unfasten the weapon control module.") + holder.icon_state = "phazon9" + if(13) + if(diff==FORWARD) + user.visible_message("[user] secures the phasic scanner module.", "You secure the phasic scanner module.") + holder.icon_state = "phazon12" + else + user.visible_message("[user] removes the phasic scanner module from the [holder].", "You remove the phasic scanner module from the [holder].") + new /obj/item/stock_parts/scanning_module/phasic(get_turf(holder)) + holder.icon_state = "phazon10" + if(12) + if(diff==FORWARD) + user.visible_message("[user] installs super capacitor to the [holder].", "You install super capacitor to the [holder].") + qdel(used_atom) + holder.icon_state = "phazon13" + else + user.visible_message("[user] unfastens the phasic scanner module.", "You unfasten the phasic scanner module.") + holder.icon_state = "phazon11" + if(11) + if(diff==FORWARD) + user.visible_message("[user] secures the super capacitor.", "You secure the super capacitor.") + holder.icon_state = "phazon14" + else + user.visible_message("[user] removes the super capacitor from the [holder].", "You remove the super capacitor from the [holder].") + new /obj/item/stock_parts/capacitor/super(get_turf(holder)) + holder.icon_state = "phazon12" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs the bluespace crystal.", "You install the bluespace crystals.") + holder.icon_state = "phazon15" + else + user.visible_message("[user] unsecures the super capacitor from the [holder].", "You unsecure the super capacitor from the [holder].") + holder.icon_state = "phazon13" + if(9) + if(diff==FORWARD) + user.visible_message("[user] connects the bluespace crystal.", "You connect the bluespace crystals.") + holder.icon_state = "phazon16" + else + user.visible_message("[user] removes the bluespace crystal from the [holder].", "You remove the bluespace crystal from the [holder].") + new /obj/item/stack/ore/bluespace_crystal(get_turf(holder), new_amount = 5) + holder.icon_state = "phazon14" + if(8) + if(diff==FORWARD) + user.visible_message("[user] engages the bluespace crystal.", "You engage the bluespace crystals.") + holder.icon_state = "phazon17" + else + user.visible_message("[user] disconnects the bluespace crystal from the [holder].", "You disconnect the bluespace crystal from the [holder].") + holder.icon_state = "phazon15" + if(7) + if(diff==FORWARD) + user.visible_message("[user] installs the phase armor layer to the [holder].", "You install the phase armor layer to the [holder].") + holder.icon_state = "phazon18" + else + user.visible_message("[user] disengages the bluespace crystal.", "You disengage the bluespace crystals.") + holder.icon_state = "phazon16" + if(6) + if(diff==FORWARD) + user.visible_message("[user] secures the phase armor layer.", "You secure the phase armor layer.") + holder.icon_state = "phazon19" + else + user.visible_message("[user] pries the phase armor layer from the [holder].", "You pry the phase armor layer from the [holder].") + var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) + MS.amount = 5 + holder.icon_state = "phazon17" + if(5) + if(diff==FORWARD) + user.visible_message("[user] welds the phase armor layer to the [holder].", "You weld the phase armor layer to the [holder].") + holder.icon_state = "phazon20" + else + user.visible_message("[user] unfastens the phase armor layer.", "You unfasten the phase armor layer.") + holder.icon_state = "phazon18" + if(4) + if(diff==FORWARD) + user.visible_message("[user] installs Phazon Armor Plates to the [holder].", "You install Phazon Armor Plates to the [holder].") + qdel(used_atom) + holder.icon_state = "phazon21" + else + user.visible_message("[user] cuts phase armor layer from the [holder].", "You cut the phase armor layer from the [holder].") + holder.icon_state = "phazon19" + if(3) + if(diff==FORWARD) + user.visible_message("[user] secures Phazon Armor Plates.", "You secure Phazon Armor Plates.") + holder.icon_state = "phazon22" + else + user.visible_message("[user] pries Phazon Armor Plates from the [holder].", "You pry Phazon Armor Plates from the [holder].") + new /obj/item/mecha_parts/part/phazon_armor(get_turf(holder)) + holder.icon_state = "phazon20" + if(2) + if(diff==FORWARD) + user.visible_message("[user] welds Phazon Armor Plates to the [holder].", "You weld Phazon Armor Plates to the [holder].") + else + user.visible_message("[user] unfastens Phazon Armor Plates.", "You unfasten Phazon Armor Plates.") + holder.icon_state = "phazon21" + if(1) + if(diff==FORWARD) + user.visible_message("[user] carefully inserts the anomaly core into \the [holder] and secures it.", "You slowly place the anomaly core into its socket and close its chamber.") + qdel(used_atom) + return 1 + +/datum/construction/reversible/mecha/phazon/spawn_result() + ..() + feedback_inc("mecha_phazon_created",1) + return + +//ODYSSEUS + +/datum/construction/mecha/odysseus_chassis + steps = list(list("key"=/obj/item/mecha_parts/part/odysseus_torso),//1 + list("key"=/obj/item/mecha_parts/part/odysseus_head),//2 + list("key"=/obj/item/mecha_parts/part/odysseus_left_arm),//3 + list("key"=/obj/item/mecha_parts/part/odysseus_right_arm),//4 + list("key"=/obj/item/mecha_parts/part/odysseus_left_leg),//5 + list("key"=/obj/item/mecha_parts/part/odysseus_right_leg)//6 + ) + +/datum/construction/mecha/odysseus_chassis/custom_action(step, atom/used_atom, mob/user) + user.visible_message("[user] has connected [used_atom] to the [holder].", "You connect [used_atom] to the [holder]") + holder.overlays += used_atom.icon_state+"+o" + qdel(used_atom) + return 1 + +/datum/construction/mecha/odysseus_chassis/action(atom/used_atom,mob/user as mob) + return check_all_steps(used_atom,user) + +/datum/construction/mecha/odysseus_chassis/spawn_result() + var/obj/item/mecha_parts/chassis/const_holder = holder + const_holder.construct = new /datum/construction/reversible/mecha/odysseus(const_holder) + const_holder.icon = 'icons/mecha/mech_construction.dmi' + const_holder.icon_state = "odysseus0" + const_holder.density = 1 + qdel(src) + return + + +/datum/construction/reversible/mecha/odysseus + result = "/obj/mecha/medical/odysseus" + steps = list( + //1 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="External armor is wrenched."), + //2 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="External armor is installed."), + //3 + list("key"=/obj/item/stack/sheet/plasteel, + "backkey"=TOOL_WELDER, + "desc"="Internal armor is welded."), + //4 + list("key"=TOOL_WELDER, + "backkey"=TOOL_WRENCH, + "desc"="Internal armor is wrenched."), + //5 + list("key"=TOOL_WRENCH, + "backkey"=TOOL_CROWBAR, + "desc"="Internal armor is installed."), + //6 + list("key"=/obj/item/stack/sheet/metal, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Peripherals control module is secured."), + //7 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Peripherals control module is installed."), + //8 + list("key"=/obj/item/circuitboard/mecha/odysseus/peripherals, + "backkey"=TOOL_SCREWDRIVER, + "desc"="Central control module is secured."), + //9 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_CROWBAR, + "desc"="Central control module is installed."), + //10 + list("key"=/obj/item/circuitboard/mecha/odysseus/main, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is adjusted."), + //11 + list("key"=/obj/item/wirecutters, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The wiring is added."), + //12 + list("key"=/obj/item/stack/cable_coil, + "backkey"=TOOL_SCREWDRIVER, + "desc"="The hydraulic systems are active."), + //13 + list("key"=TOOL_SCREWDRIVER, + "backkey"=TOOL_WRENCH, + "desc"="The hydraulic systems are connected."), + //14 + list("key"=TOOL_WRENCH, + "desc"="The hydraulic systems are disconnected.") + ) + +/datum/construction/reversible/mecha/odysseus/action(atom/used_atom,mob/user as mob) + return check_step(used_atom,user) + +/datum/construction/reversible/mecha/odysseus/custom_action(index, diff, atom/used_atom, mob/user) + if(!..()) + return 0 + + //TODO: better messages. + switch(index) + if(14) + user.visible_message("[user] connects the [holder] hydraulic systems", "You connect the [holder] hydraulic systems.") + holder.icon_state = "odysseus1" + if(13) + if(diff==FORWARD) + user.visible_message("[user] activates the [holder] hydraulic systems.", "You activate the [holder] hydraulic systems.") + holder.icon_state = "odysseus2" + else + user.visible_message("[user] disconnects the [holder] hydraulic systems", "You disconnect the [holder] hydraulic systems.") + holder.icon_state = "odysseus0" + if(12) + if(diff==FORWARD) + user.visible_message("[user] adds the wiring to the [holder].", "You add the wiring to the [holder].") + holder.icon_state = "odysseus3" + else + user.visible_message("[user] deactivates the [holder] hydraulic systems.", "You deactivate the [holder] hydraulic systems.") + holder.icon_state = "odysseus1" + if(11) + if(diff==FORWARD) + user.visible_message("[user] adjusts the wiring of the [holder].", "You adjust the wiring of the [holder].") + holder.icon_state = "odysseus4" + else + user.visible_message("[user] removes the wiring from the [holder].", "You remove the wiring from the [holder].") + var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil(get_turf(holder)) + coil.amount = 4 + holder.icon_state = "odysseus2" + if(10) + if(diff==FORWARD) + user.visible_message("[user] installs the central control module into the [holder].", "You install the central computer mainboard into the [holder].") + qdel(used_atom) + holder.icon_state = "odysseus5" + else + user.visible_message("[user] disconnects the wiring of the [holder].", "You disconnect the wiring of the [holder].") + holder.icon_state = "odysseus3" + if(9) + if(diff==FORWARD) + user.visible_message("[user] secures the mainboard.", "You secure the mainboard.") + holder.icon_state = "odysseus6" + else + user.visible_message("[user] removes the central control module from the [holder].", "You remove the central computer mainboard from the [holder].") + new /obj/item/circuitboard/mecha/odysseus/main(get_turf(holder)) + holder.icon_state = "odysseus4" + if(8) + if(diff==FORWARD) + user.visible_message("[user] installs the peripherals control module into the [holder].", "You install the peripherals control module into the [holder].") + qdel(used_atom) + holder.icon_state = "odysseus7" + else + user.visible_message("[user] unfastens the mainboard.", "You unfasten the mainboard.") + holder.icon_state = "odysseus5" + if(7) + if(diff==FORWARD) + user.visible_message("[user] secures the peripherals control module.", "You secure the peripherals control module.") + holder.icon_state = "odysseus8" + else + user.visible_message("[user] removes the peripherals control module from the [holder].", "You remove the peripherals control module from the [holder].") + new /obj/item/circuitboard/mecha/odysseus/peripherals(get_turf(holder)) + holder.icon_state = "odysseus6" + if(6) + if(diff==FORWARD) + user.visible_message("[user] installs the internal armor layer to the [holder].", "You install the internal armor layer to the [holder].") + holder.icon_state = "odysseus9" + else + user.visible_message("[user] unfastens the peripherals control module.", "You unfasten the peripherals control module.") + holder.icon_state = "odysseus7" + if(5) + if(diff==FORWARD) + user.visible_message("[user] secures the internal armor layer.", "You secure the internal armor layer.") + holder.icon_state = "odysseus10" + else + user.visible_message("[user] pries internal armor layer from the [holder].", "You pry internal armor layer from the [holder].") + var/obj/item/stack/sheet/metal/MS = new /obj/item/stack/sheet/metal(get_turf(holder)) + MS.amount = 5 + holder.icon_state = "odysseus8" + if(4) + if(diff==FORWARD) + user.visible_message("[user] welds the internal armor layer to the [holder].", "You weld the internal armor layer to the [holder].") + holder.icon_state = "odysseus11" + else + user.visible_message("[user] unfastens the internal armor layer.", "You unfasten the internal armor layer.") + holder.icon_state = "odysseus9" + if(3) + if(diff==FORWARD) + user.visible_message("[user] installs [used_atom] layer to the [holder].", "You install the external reinforced armor layer to the [holder].") + + holder.icon_state = "odysseus12" + else + user.visible_message("[user] cuts the internal armor layer from the [holder].", "You cut the internal armor layer from the [holder].") + holder.icon_state = "odysseus10" + if(2) + if(diff==FORWARD) + user.visible_message("[user] secures the external armor layer.", "You secure the external reinforced armor layer.") + holder.icon_state = "odysseus13" + else + var/obj/item/stack/sheet/plasteel/MS = new /obj/item/stack/sheet/plasteel(get_turf(holder)) + MS.amount = 5 + user.visible_message("[user] pries [MS] from the [holder].", "You pry [MS] from the [holder].") + holder.icon_state = "odysseus11" + if(1) + if(diff==FORWARD) + user.visible_message("[user] welds the external armor layer to the [holder].", "You weld the external armor layer to the [holder].") + holder.icon_state = "odysseus14" + else + user.visible_message("[user] unfastens the external armor layer.", "You unfasten the external armor layer.") + holder.icon_state = "odysseus12" + return 1 + +/datum/construction/reversible/mecha/odysseus/spawn_result() + ..() + feedback_inc("mecha_odysseus_created",1) + return diff --git a/code/game/mecha/mecha_control_console.dm b/code/game/mecha/mecha_control_console.dm index 99e6ca72367..0313397a809 100644 --- a/code/game/mecha/mecha_control_console.dm +++ b/code/game/mecha/mecha_control_console.dm @@ -1,161 +1,161 @@ -/obj/machinery/computer/mecha - name = "exosuit control console" - icon = 'icons/obj/computer.dmi' - icon_keyboard = "rd_key" - icon_screen = "mecha" - light_color = LIGHT_COLOR_FADEDPURPLE - req_access = list(access_robotics) - circuit = /obj/item/circuitboard/mecha_control - var/list/located = list() - var/screen = 0 - var/stored_data - -/obj/machinery/computer/mecha/attack_ai(mob/user) - return attack_hand(user) - -/obj/machinery/computer/mecha/attack_hand(mob/user) - ui_interact(user) - -/obj/machinery/computer/mecha/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "exosuit_control.tmpl", "Exosuit Control Console", 420, 500) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/computer/mecha/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - data["screen"] = screen - if(screen == 0) - var/list/mechas[0] - for(var/obj/item/mecha_parts/mecha_tracking/TR in world) - var/answer = TR.get_mecha_info() - if(answer) - mechas[++mechas.len] = answer - data["mechas"] = mechas - if(screen == 1) - data["log"] = stored_data - return data - -/obj/machinery/computer/mecha/Topic(href, href_list) - if(..()) - return 1 - - var/datum/topic_input/afilter = new /datum/topic_input(href,href_list) - if(href_list["send_message"]) - var/obj/item/mecha_parts/mecha_tracking/MT = afilter.getObj("send_message") - var/message = strip_html_simple(input(usr,"Input message","Transmit message") as text) - if(!trim(message) || ..()) - return 1 - var/obj/mecha/M = MT.in_mecha() - if(M) - M.occupant_message(message) - - if(href_list["shock"]) - var/obj/item/mecha_parts/mecha_tracking/MT = afilter.getObj("shock") - MT.shock() - - if(href_list["get_log"]) - var/obj/item/mecha_parts/mecha_tracking/MT = afilter.getObj("get_log") - stored_data = MT.get_mecha_log() - screen = 1 - - if(href_list["return"]) - screen = 0 - - SSnanoui.update_uis(src) - return - -/obj/item/mecha_parts/mecha_tracking - name = "Exosuit tracking beacon" - desc = "Device used to transmit exosuit data." - icon = 'icons/obj/device.dmi' - icon_state = "motion2" - w_class = WEIGHT_CLASS_SMALL - origin_tech = "programming=2;magnets=2" - var/ai_beacon = FALSE //If this beacon allows for AI control. Exists to avoid using istype() on checking. - -/obj/item/mecha_parts/mecha_tracking/proc/get_mecha_info() - if(!in_mecha()) - return FALSE - var/obj/mecha/M = loc - var/list/answer[0] - answer["reference"] = "\ref[src]" - answer["name"] = sanitize(replacetext(M.name,"\"","'")) // Double apostrophes break JSON - if(M.cell) - answer["cell"] = 1 - answer["cell_capacity"] = M.cell.maxcharge - answer["cell_current"] = M.get_charge() - answer["cell_percentage"] = round(M.cell.percent()) - else - answer["cell"] = 0 - answer["integrity"] = round((M.obj_integrity/M.max_integrity*100), 0.01) - answer["airtank"] = M.return_pressure() - answer["pilot"] = "[M.occupant||"None"]" - var/area/area = get_area(M) - answer["location"] = "[sanitize(area.name)||"Unknown"]" - answer["equipment"] = "[M.selected||"None"]" - if(istype(M, /obj/mecha/working/ripley)) - var/obj/mecha/working/ripley/RM = M - answer["hascargo"] = 1 - answer["cargo"] = RM.cargo.len/RM.cargo_capacity*100 - - return answer - -/obj/item/mecha_parts/mecha_tracking/proc/get_mecha_info_text() - if(!in_mecha()) - return FALSE - var/obj/mecha/M = loc - var/cell_charge = M.get_charge() - var/area/A = get_area(M) - var/answer = {"Name: [M.name] - Integrity: [M.obj_integrity / M.max_integrity * 100]% - Cell charge: [isnull(cell_charge)?"Not found":"[M.cell.percent()]%"] - Airtank: [M.return_pressure()]kPa - Pilot: [M.occupant||"None"] - Location: [sanitize(A.name)||"Unknown"] - Active equipment: [M.selected||"None"]
        "} - if(istype(M, /obj/mecha/working/ripley)) - var/obj/mecha/working/ripley/RM = M - answer += "Used cargo space: [RM.cargo.len/RM.cargo_capacity*100]%
        " - - return answer - -/obj/item/mecha_parts/mecha_tracking/emp_act() - qdel(src) - -/obj/item/mecha_parts/mecha_tracking/proc/in_mecha() - if(istype(loc, /obj/mecha)) - return loc - return FALSE - -/obj/item/mecha_parts/mecha_tracking/proc/shock() - var/obj/mecha/M = in_mecha() - if(M) - M.emp_act(2) - qdel(src) - -/obj/item/mecha_parts/mecha_tracking/proc/get_mecha_log() - if(!in_mecha()) - return 0 - var/obj/mecha/M = loc - return M.get_log_html() - -/obj/item/mecha_parts/mecha_tracking/ai_control - name = "exosuit AI control beacon" - desc = "A device used to transmit exosuit data. Also allows active AI units to take control of said exosuit." - origin_tech = "programming=3;magnets=2;engineering=2" - ai_beacon = TRUE - -/obj/item/storage/box/mechabeacons - name = "Exosuit Tracking Beacons" - -/obj/item/storage/box/mechabeacons/New() - ..() - new /obj/item/mecha_parts/mecha_tracking(src) - new /obj/item/mecha_parts/mecha_tracking(src) - new /obj/item/mecha_parts/mecha_tracking(src) - new /obj/item/mecha_parts/mecha_tracking(src) - new /obj/item/mecha_parts/mecha_tracking(src) - new /obj/item/mecha_parts/mecha_tracking(src) - new /obj/item/mecha_parts/mecha_tracking(src) +/obj/machinery/computer/mecha + name = "exosuit control console" + icon = 'icons/obj/computer.dmi' + icon_keyboard = "rd_key" + icon_screen = "mecha" + light_color = LIGHT_COLOR_FADEDPURPLE + req_access = list(ACCESS_ROBOTICS) + circuit = /obj/item/circuitboard/mecha_control + var/list/located = list() + var/screen = 0 + var/stored_data + +/obj/machinery/computer/mecha/attack_ai(mob/user) + return attack_hand(user) + +/obj/machinery/computer/mecha/attack_hand(mob/user) + ui_interact(user) + +/obj/machinery/computer/mecha/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "exosuit_control.tmpl", "Exosuit Control Console", 420, 500) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/computer/mecha/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) + var/data[0] + data["screen"] = screen + if(screen == 0) + var/list/mechas[0] + for(var/obj/item/mecha_parts/mecha_tracking/TR in world) + var/answer = TR.get_mecha_info() + if(answer) + mechas[++mechas.len] = answer + data["mechas"] = mechas + if(screen == 1) + data["log"] = stored_data + return data + +/obj/machinery/computer/mecha/Topic(href, href_list) + if(..()) + return 1 + + var/datum/topic_input/afilter = new /datum/topic_input(href,href_list) + if(href_list["send_message"]) + var/obj/item/mecha_parts/mecha_tracking/MT = afilter.getObj("send_message") + var/message = strip_html_simple(input(usr,"Input message","Transmit message") as text) + if(!trim(message) || ..()) + return 1 + var/obj/mecha/M = MT.in_mecha() + if(M) + M.occupant_message(message) + + if(href_list["shock"]) + var/obj/item/mecha_parts/mecha_tracking/MT = afilter.getObj("shock") + MT.shock() + + if(href_list["get_log"]) + var/obj/item/mecha_parts/mecha_tracking/MT = afilter.getObj("get_log") + stored_data = MT.get_mecha_log() + screen = 1 + + if(href_list["return"]) + screen = 0 + + SSnanoui.update_uis(src) + return + +/obj/item/mecha_parts/mecha_tracking + name = "Exosuit tracking beacon" + desc = "Device used to transmit exosuit data." + icon = 'icons/obj/device.dmi' + icon_state = "motion2" + w_class = WEIGHT_CLASS_SMALL + origin_tech = "programming=2;magnets=2" + var/ai_beacon = FALSE //If this beacon allows for AI control. Exists to avoid using istype() on checking. + +/obj/item/mecha_parts/mecha_tracking/proc/get_mecha_info() + if(!in_mecha()) + return FALSE + var/obj/mecha/M = loc + var/list/answer[0] + answer["reference"] = "\ref[src]" + answer["name"] = sanitize(replacetext(M.name,"\"","'")) // Double apostrophes break JSON + if(M.cell) + answer["cell"] = 1 + answer["cell_capacity"] = M.cell.maxcharge + answer["cell_current"] = M.get_charge() + answer["cell_percentage"] = round(M.cell.percent()) + else + answer["cell"] = 0 + answer["integrity"] = round((M.obj_integrity/M.max_integrity*100), 0.01) + answer["airtank"] = M.return_pressure() + answer["pilot"] = "[M.occupant||"None"]" + var/area/area = get_area(M) + answer["location"] = "[sanitize(area.name)||"Unknown"]" + answer["equipment"] = "[M.selected||"None"]" + if(istype(M, /obj/mecha/working/ripley)) + var/obj/mecha/working/ripley/RM = M + answer["hascargo"] = 1 + answer["cargo"] = RM.cargo.len/RM.cargo_capacity*100 + + return answer + +/obj/item/mecha_parts/mecha_tracking/proc/get_mecha_info_text() + if(!in_mecha()) + return FALSE + var/obj/mecha/M = loc + var/cell_charge = M.get_charge() + var/area/A = get_area(M) + var/answer = {"Name: [M.name] + Integrity: [M.obj_integrity / M.max_integrity * 100]% + Cell charge: [isnull(cell_charge)?"Not found":"[M.cell.percent()]%"] + Airtank: [M.return_pressure()]kPa + Pilot: [M.occupant||"None"] + Location: [sanitize(A.name)||"Unknown"] + Active equipment: [M.selected||"None"]
        "} + if(istype(M, /obj/mecha/working/ripley)) + var/obj/mecha/working/ripley/RM = M + answer += "Used cargo space: [RM.cargo.len/RM.cargo_capacity*100]%
        " + + return answer + +/obj/item/mecha_parts/mecha_tracking/emp_act() + qdel(src) + +/obj/item/mecha_parts/mecha_tracking/proc/in_mecha() + if(istype(loc, /obj/mecha)) + return loc + return FALSE + +/obj/item/mecha_parts/mecha_tracking/proc/shock() + var/obj/mecha/M = in_mecha() + if(M) + M.emp_act(2) + qdel(src) + +/obj/item/mecha_parts/mecha_tracking/proc/get_mecha_log() + if(!in_mecha()) + return 0 + var/obj/mecha/M = loc + return M.get_log_html() + +/obj/item/mecha_parts/mecha_tracking/ai_control + name = "exosuit AI control beacon" + desc = "A device used to transmit exosuit data. Also allows active AI units to take control of said exosuit." + origin_tech = "programming=3;magnets=2;engineering=2" + ai_beacon = TRUE + +/obj/item/storage/box/mechabeacons + name = "Exosuit Tracking Beacons" + +/obj/item/storage/box/mechabeacons/New() + ..() + new /obj/item/mecha_parts/mecha_tracking(src) + new /obj/item/mecha_parts/mecha_tracking(src) + new /obj/item/mecha_parts/mecha_tracking(src) + new /obj/item/mecha_parts/mecha_tracking(src) + new /obj/item/mecha_parts/mecha_tracking(src) + new /obj/item/mecha_parts/mecha_tracking(src) + new /obj/item/mecha_parts/mecha_tracking(src) diff --git a/code/game/mecha/mecha_modkit.dm b/code/game/mecha/mecha_modkit.dm index 9a75fc75077..9d07e760c79 100644 --- a/code/game/mecha/mecha_modkit.dm +++ b/code/game/mecha/mecha_modkit.dm @@ -75,4 +75,4 @@ critdestrsound = null weapdestrsound = null lowpowersound = null - longactivationsound = null \ No newline at end of file + longactivationsound = null diff --git a/code/game/mecha/mecha_parts.dm b/code/game/mecha/mecha_parts.dm index 6aff10eb423..86fd9cb6821 100644 --- a/code/game/mecha/mecha_parts.dm +++ b/code/game/mecha/mecha_parts.dm @@ -1,457 +1,457 @@ -///////////////////////// -////// Mecha Parts ////// -///////////////////////// - -/obj/item/mecha_parts - name = "mecha part" - icon = 'icons/mecha/mech_construct.dmi' - icon_state = "blank" - w_class = WEIGHT_CLASS_GIGANTIC - flags = CONDUCT - origin_tech = "programming=2;materials=2;engineering=2" - - -/obj/item/mecha_parts/chassis - name="Mecha Chassis" - icon_state = "backbone" - var/datum/construction/construct - flags = CONDUCT - -/obj/item/mecha_parts/chassis/Destroy() - QDEL_NULL(construct) - return ..() - -/obj/item/mecha_parts/chassis/attackby(obj/item/W, mob/user, params) - if(!construct || !construct.action(W, user)) - return ..() - -/obj/item/mecha_parts/chassis/attack_hand() - return - -/////////// Ripley - -/obj/item/mecha_parts/chassis/ripley - name = "Ripley Chassis" - -/obj/item/mecha_parts/chassis/ripley/New() - ..() - construct = new /datum/construction/mecha/ripley_chassis(src) - -/obj/item/mecha_parts/part/ripley_torso - name="Ripley Torso" - desc="A torso part of Ripley APLU. Contains power unit, processing core and life support systems." - icon_state = "ripley_harness" - origin_tech = "programming=2;materials=2;biotech=2;engineering=2" - -/obj/item/mecha_parts/part/ripley_left_arm - name="Ripley Left Arm" - desc="A Ripley APLU left arm. Data and power sockets are compatible with most exosuit tools." - icon_state = "ripley_l_arm" - -/obj/item/mecha_parts/part/ripley_right_arm - name="Ripley Right Arm" - desc="A Ripley APLU right arm. Data and power sockets are compatible with most exosuit tools." - icon_state = "ripley_r_arm" - -/obj/item/mecha_parts/part/ripley_left_leg - name="Ripley Left Leg" - desc="A Ripley APLU left leg. Contains somewhat complex servodrives and balance maintaining systems." - icon_state = "ripley_l_leg" - -/obj/item/mecha_parts/part/ripley_right_leg - name="Ripley Right Leg" - desc="A Ripley APLU right leg. Contains somewhat complex servodrives and balance maintaining systems." - icon_state = "ripley_r_leg" - -///////// Gygax - -/obj/item/mecha_parts/chassis/gygax - name = "Gygax Chassis" - -/obj/item/mecha_parts/chassis/gygax/New() - ..() - construct = new /datum/construction/mecha/gygax_chassis(src) - -/obj/item/mecha_parts/part/gygax_torso - name="Gygax Torso" - desc="A torso part of Gygax. Contains power unit, processing core and life support systems. Has an additional equipment slot." - icon_state = "gygax_harness" - origin_tech = "programming=2;materials=4;biotech=3;engineering=3" - -/obj/item/mecha_parts/part/gygax_head - name="Gygax Head" - desc="A Gygax head. Houses advanced surveilance and targeting sensors." - icon_state = "gygax_head" - origin_tech = "programming=2;materials=4;magnets=3;engineering=3" - -/obj/item/mecha_parts/part/gygax_left_arm - name="Gygax Left Arm" - desc="A Gygax left arm. Data and power sockets are compatible with most exosuit tools and weapons." - icon_state = "gygax_l_arm" - origin_tech = "programming=2;materials=4;engineering=3" - -/obj/item/mecha_parts/part/gygax_right_arm - name="Gygax Right Arm" - desc="A Gygax right arm. Data and power sockets are compatible with most exosuit tools and weapons." - icon_state = "gygax_r_arm" - origin_tech = "programming=2;materials=4;engineering=3" - -/obj/item/mecha_parts/part/gygax_left_leg - name="Gygax Left Leg" - icon_state = "gygax_l_leg" - origin_tech = "programming=2;materials=4;engineering=3" - -/obj/item/mecha_parts/part/gygax_right_leg - name="Gygax Right Leg" - icon_state = "gygax_r_leg" - origin_tech = "programming=2;materials=4;engineering=3" - -/obj/item/mecha_parts/part/gygax_armour - name="Gygax Armour Plates" - icon_state = "gygax_armour" - origin_tech = "materials=6;combat=4;engineering=4" - - -//////////// Durand - -/obj/item/mecha_parts/chassis/durand - name = "Durand Chassis" - -/obj/item/mecha_parts/chassis/durand/New() - ..() - construct = new /datum/construction/mecha/durand_chassis(src) - -/obj/item/mecha_parts/part/durand_torso - name="Durand Torso" - icon_state = "durand_harness" - origin_tech = "programming=2;materials=3;biotech=3;engineering=3" - -/obj/item/mecha_parts/part/durand_head - name="Durand Head" - icon_state = "durand_head" - origin_tech = "programming=2;materials=3;magnets=3;engineering=3" - -/obj/item/mecha_parts/part/durand_left_arm - name="Durand Left Arm" - icon_state = "durand_l_arm" - origin_tech = "programming=2;materials=3;engineering=3" - -/obj/item/mecha_parts/part/durand_right_arm - name="Durand Right Arm" - icon_state = "durand_r_arm" - origin_tech = "programming=2;materials=3;engineering=3" - -/obj/item/mecha_parts/part/durand_left_leg - name="Durand Left Leg" - icon_state = "durand_l_leg" - origin_tech = "programming=2;materials=3;engineering=3" - -/obj/item/mecha_parts/part/durand_right_leg - name="Durand Right Leg" - icon_state = "durand_r_leg" - origin_tech = "programming=2;materials=3;engineering=3" - -/obj/item/mecha_parts/part/durand_armor - name="Durand Armour Plates" - icon_state = "durand_armor" - origin_tech = "materials=5;combat=4;engineering=4" - - - -////////// Firefighter - -/obj/item/mecha_parts/chassis/firefighter - name = "Firefighter Chassis" - -/obj/item/mecha_parts/chassis/firefighter/New() - ..() - construct = new /datum/construction/mecha/firefighter_chassis(src) - -////////// HONK - -/obj/item/mecha_parts/chassis/honker - name = "H.O.N.K Chassis" - -/obj/item/mecha_parts/chassis/honker/New() - ..() - construct = new /datum/construction/mecha/honker_chassis(src) - -/obj/item/mecha_parts/part/honker_torso - name="H.O.N.K Torso" - icon_state = "honker_harness" - -/obj/item/mecha_parts/part/honker_head - name="H.O.N.K Head" - icon_state = "honker_head" - -/obj/item/mecha_parts/part/honker_left_arm - name="H.O.N.K Left Arm" - icon_state = "honker_l_arm" - -/obj/item/mecha_parts/part/honker_right_arm - name="H.O.N.K Right Arm" - icon_state = "honker_r_arm" - -/obj/item/mecha_parts/part/honker_left_leg - name="H.O.N.K Left Leg" - icon_state = "honker_l_leg" - -/obj/item/mecha_parts/part/honker_right_leg - name="H.O.N.K Right Leg" - icon_state = "honker_r_leg" - - -////////// Reticence - -/obj/item/mecha_parts/chassis/reticence - name = "Reticence Chassis" - -/obj/item/mecha_parts/chassis/reticence/New() - ..() - construct = new /datum/construction/mecha/reticence_chassis(src) - -/obj/effect/dummy/mecha_emote_step - var/emote - -/obj/effect/dummy/mecha_emote_step/New(e) - emote = e - -/obj/item/mecha_parts/chassis/reticence/hear_message(mob/living/M, msg) - if(!istype(M) || !istype(construct, /datum/construction/mecha/reticence)) - return - // is the current step the dummy emote object? - var/list/steps = construct.steps - if(steps[steps.len]["key"] == /obj/effect/dummy/mecha_emote_step) - construct.action(new /obj/effect/dummy/mecha_emote_step(msg), M) - -/obj/item/mecha_parts/part/reticence_torso - name = "Reticence Torso" - icon_state = "reticence_harness" - -/obj/item/mecha_parts/part/reticence_head - name = "Reticence Head" - icon_state = "reticence_head" - -/obj/item/mecha_parts/part/reticence_left_arm - name = "Reticence Left Arm" - icon_state = "reticence_l_arm" - -/obj/item/mecha_parts/part/reticence_right_arm - name = "Reticence Right Arm" - icon_state = "reticence_r_arm" - -/obj/item/mecha_parts/part/reticence_left_leg - name = "Reticence Left Leg" - icon_state = "reticence_l_leg" - -/obj/item/mecha_parts/part/reticence_right_leg - name = "Reticence Right Leg" - icon_state = "reticence_r_leg" - - -////////// Phazon - -/obj/item/mecha_parts/chassis/phazon - name = "Phazon Chassis" - -/obj/item/mecha_parts/chassis/phazon/New() - ..() - construct = new /datum/construction/mecha/phazon_chassis(src) - -/obj/item/mecha_parts/part/phazon_torso - name="Phazon Torso" - icon_state = "phazon_harness" - origin_tech = "programming=4;materials=4;bluespace=4;plasmatech=5" - -/obj/item/mecha_parts/part/phazon_head - name="Phazon Head" - icon_state = "phazon_head" - origin_tech = "programming=3;materials=3;magnets=3" - -/obj/item/mecha_parts/part/phazon_left_arm - name="Phazon Left Arm" - icon_state = "phazon_l_arm" - origin_tech = "materials=3;bluespace=3;magnets=3" - -/obj/item/mecha_parts/part/phazon_right_arm - name="Phazon Right Arm" - icon_state = "phazon_r_arm" - origin_tech = "materials=3;bluespace=3;magnets=3" - -/obj/item/mecha_parts/part/phazon_left_leg - name="Phazon Left Leg" - icon_state = "phazon_l_leg" - origin_tech = "materials=3;bluespace=3;magnets=3" - -/obj/item/mecha_parts/part/phazon_right_leg - name="Phazon Right Leg" - icon_state = "phazon_r_leg" - origin_tech = "materials=3;bluespace=3;magnets=3" - -/obj/item/mecha_parts/part/phazon_armor - name="Phazon armor" - desc="Phazon armor plates. They are layered with plasma to protect the pilot from the stress of phasing and have unusual properties." - icon_state = "phazon_armor" - origin_tech = "materials=4;bluespace=4;plasmatech=5" - -///////// Odysseus -/obj/item/mecha_parts/chassis/odysseus - name = "Odysseus Chassis" - -/obj/item/mecha_parts/chassis/odysseus/New() - ..() - construct = new /datum/construction/mecha/odysseus_chassis(src) - -/obj/item/mecha_parts/part/odysseus_head - name="Odysseus Head" - icon_state = "odysseus_head" - -/obj/item/mecha_parts/part/odysseus_torso - name="Odysseus Torso" - desc="A torso part of Odysseus. Contains power unit, processing core and life support systems." - icon_state = "odysseus_torso" - origin_tech = "programming=2;materials=2;biotech=2;engineering=2" - -/obj/item/mecha_parts/part/odysseus_left_arm - name="Odysseus Left Arm" - desc="An Odysseus left arm. Data and power sockets are compatible with most exosuit tools." - icon_state = "odysseus_l_arm" - -/obj/item/mecha_parts/part/odysseus_right_arm - name="Odysseus Right Arm" - desc="An Odysseus right arm. Data and power sockets are compatible with most exosuit tools." - icon_state = "odysseus_r_arm" - -/obj/item/mecha_parts/part/odysseus_left_leg - name="Odysseus Left Leg" - desc="An Odysseus left leg. Contains somewhat complex servodrives and balance maintaining systems." - icon_state = "odysseus_l_leg" - -/obj/item/mecha_parts/part/odysseus_right_leg - name="Odysseus Right Leg" - desc="A Odysseus right leg. Contains somewhat complex servodrives and balance maintaining systems." - icon_state = "odysseus_r_leg" - -/*/obj/item/mecha_parts/part/odysseus_armour - name="Odysseus Carapace" - icon_state = "odysseus_armour" - origin_tech = "materials=3;engineering=3")*/ - - -///////// Circuitboards - -/obj/item/circuitboard/mecha - name = "Exosuit Circuit board" - icon = 'icons/obj/module.dmi' - icon_state = "std_mod" - item_state = "electronic" - board_type = "other" - flags = CONDUCT - force = 5.0 - w_class = WEIGHT_CLASS_SMALL - throwforce = 5.0 - throw_speed = 3 - throw_range = 15 - -/obj/item/circuitboard/mecha/ripley - origin_tech = "programming=2" - -/obj/item/circuitboard/mecha/ripley/peripherals - name = "Circuit board (Ripley Peripherals Control module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/ripley/main - name = "Circuit board (Ripley Central Control module)" - icon_state = "mainboard" - -/obj/item/circuitboard/mecha/gygax - origin_tech = "programming=4;combat=3;engineering=3" - -/obj/item/circuitboard/mecha/gygax/peripherals - name = "Circuit board (Gygax Peripherals Control module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/gygax/targeting - name = "Circuit board (Gygax Weapon Control and Targeting module)" - icon_state = "mcontroller" - origin_tech = "programming=4;combat=4" - -/obj/item/circuitboard/mecha/gygax/main - name = "Circuit board (Gygax Central Control module)" - icon_state = "mainboard" - -/obj/item/circuitboard/mecha/durand - origin_tech = "programming=4;combat=3;engineering=3" - -/obj/item/circuitboard/mecha/durand/peripherals - name = "Circuit board (Durand Peripherals Control module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/durand/targeting - name = "Circuit board (Durand Weapon Control and Targeting module)" - icon_state = "mcontroller" - origin_tech = "programming=4;combat=4;engineering=3" - -/obj/item/circuitboard/mecha/durand/main - name = "Circuit board (Durand Central Control module)" - icon_state = "mainboard" - -/obj/item/circuitboard/mecha/phazon - origin_tech = "programming=5;plasmatech=4" - -/obj/item/circuitboard/mecha/phazon/peripherals - name = "Circuit board (Phazon Peripherals Control module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/phazon/targeting - name = "Circuit board (Phazon Weapon Control and Targeting module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/phazon/main - name = "Circuit board (Phazon Central Control module)" - icon_state = "mainboard" - -/obj/item/circuitboard/mecha/honker - origin_tech = "programming=3;engineering=3" - -/obj/item/circuitboard/mecha/honker/peripherals - name = "Circuit board (H.O.N.K Peripherals Control module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/honker/targeting - name = "Circuit board (H.O.N.K Weapon Control and Targeting module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/honker/main - name = "Circuit board (H.O.N.K Central Control module)" - icon_state = "mainboard" - -/obj/item/circuitboard/mecha/reticence - origin_tech = "programming=3;engineering=3" - -/obj/item/circuitboard/mecha/reticence/peripherals - name = "circuit board (Reticence Peripherals Control module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/reticence/targeting - name = "circuit board (Reticence Weapon Control and Targeting module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/reticence/main - name = "circuit board (Reticence Central Control module)" - icon_state = "mainboard" - -/obj/item/circuitboard/mecha/odysseus - origin_tech = "programming=3;biotech=3" - -/obj/item/circuitboard/mecha/odysseus/peripherals - name = "Circuit board (Odysseus Peripherals Control module)" - icon_state = "mcontroller" - -/obj/item/circuitboard/mecha/odysseus/main - name = "Circuit board (Odysseus Central Control module)" - icon_state = "mainboard" - -/obj/item/circuitboard/mecha/pod - name = "Circuit board (Space Pod Mainboard)" - icon_state = "mainboard" +///////////////////////// +////// Mecha Parts ////// +///////////////////////// + +/obj/item/mecha_parts + name = "mecha part" + icon = 'icons/mecha/mech_construct.dmi' + icon_state = "blank" + w_class = WEIGHT_CLASS_GIGANTIC + flags = CONDUCT + origin_tech = "programming=2;materials=2;engineering=2" + + +/obj/item/mecha_parts/chassis + name="Mecha Chassis" + icon_state = "backbone" + var/datum/construction/construct + flags = CONDUCT + +/obj/item/mecha_parts/chassis/Destroy() + QDEL_NULL(construct) + return ..() + +/obj/item/mecha_parts/chassis/attackby(obj/item/W, mob/user, params) + if(!construct || !construct.action(W, user)) + return ..() + +/obj/item/mecha_parts/chassis/attack_hand() + return + +/////////// Ripley + +/obj/item/mecha_parts/chassis/ripley + name = "Ripley Chassis" + +/obj/item/mecha_parts/chassis/ripley/New() + ..() + construct = new /datum/construction/mecha/ripley_chassis(src) + +/obj/item/mecha_parts/part/ripley_torso + name="Ripley Torso" + desc="A torso part of Ripley APLU. Contains power unit, processing core and life support systems." + icon_state = "ripley_harness" + origin_tech = "programming=2;materials=2;biotech=2;engineering=2" + +/obj/item/mecha_parts/part/ripley_left_arm + name="Ripley Left Arm" + desc="A Ripley APLU left arm. Data and power sockets are compatible with most exosuit tools." + icon_state = "ripley_l_arm" + +/obj/item/mecha_parts/part/ripley_right_arm + name="Ripley Right Arm" + desc="A Ripley APLU right arm. Data and power sockets are compatible with most exosuit tools." + icon_state = "ripley_r_arm" + +/obj/item/mecha_parts/part/ripley_left_leg + name="Ripley Left Leg" + desc="A Ripley APLU left leg. Contains somewhat complex servodrives and balance maintaining systems." + icon_state = "ripley_l_leg" + +/obj/item/mecha_parts/part/ripley_right_leg + name="Ripley Right Leg" + desc="A Ripley APLU right leg. Contains somewhat complex servodrives and balance maintaining systems." + icon_state = "ripley_r_leg" + +///////// Gygax + +/obj/item/mecha_parts/chassis/gygax + name = "Gygax Chassis" + +/obj/item/mecha_parts/chassis/gygax/New() + ..() + construct = new /datum/construction/mecha/gygax_chassis(src) + +/obj/item/mecha_parts/part/gygax_torso + name="Gygax Torso" + desc="A torso part of Gygax. Contains power unit, processing core and life support systems. Has an additional equipment slot." + icon_state = "gygax_harness" + origin_tech = "programming=2;materials=4;biotech=3;engineering=3" + +/obj/item/mecha_parts/part/gygax_head + name="Gygax Head" + desc="A Gygax head. Houses advanced surveilance and targeting sensors." + icon_state = "gygax_head" + origin_tech = "programming=2;materials=4;magnets=3;engineering=3" + +/obj/item/mecha_parts/part/gygax_left_arm + name="Gygax Left Arm" + desc="A Gygax left arm. Data and power sockets are compatible with most exosuit tools and weapons." + icon_state = "gygax_l_arm" + origin_tech = "programming=2;materials=4;engineering=3" + +/obj/item/mecha_parts/part/gygax_right_arm + name="Gygax Right Arm" + desc="A Gygax right arm. Data and power sockets are compatible with most exosuit tools and weapons." + icon_state = "gygax_r_arm" + origin_tech = "programming=2;materials=4;engineering=3" + +/obj/item/mecha_parts/part/gygax_left_leg + name="Gygax Left Leg" + icon_state = "gygax_l_leg" + origin_tech = "programming=2;materials=4;engineering=3" + +/obj/item/mecha_parts/part/gygax_right_leg + name="Gygax Right Leg" + icon_state = "gygax_r_leg" + origin_tech = "programming=2;materials=4;engineering=3" + +/obj/item/mecha_parts/part/gygax_armour + name="Gygax Armour Plates" + icon_state = "gygax_armour" + origin_tech = "materials=6;combat=4;engineering=4" + + +//////////// Durand + +/obj/item/mecha_parts/chassis/durand + name = "Durand Chassis" + +/obj/item/mecha_parts/chassis/durand/New() + ..() + construct = new /datum/construction/mecha/durand_chassis(src) + +/obj/item/mecha_parts/part/durand_torso + name="Durand Torso" + icon_state = "durand_harness" + origin_tech = "programming=2;materials=3;biotech=3;engineering=3" + +/obj/item/mecha_parts/part/durand_head + name="Durand Head" + icon_state = "durand_head" + origin_tech = "programming=2;materials=3;magnets=3;engineering=3" + +/obj/item/mecha_parts/part/durand_left_arm + name="Durand Left Arm" + icon_state = "durand_l_arm" + origin_tech = "programming=2;materials=3;engineering=3" + +/obj/item/mecha_parts/part/durand_right_arm + name="Durand Right Arm" + icon_state = "durand_r_arm" + origin_tech = "programming=2;materials=3;engineering=3" + +/obj/item/mecha_parts/part/durand_left_leg + name="Durand Left Leg" + icon_state = "durand_l_leg" + origin_tech = "programming=2;materials=3;engineering=3" + +/obj/item/mecha_parts/part/durand_right_leg + name="Durand Right Leg" + icon_state = "durand_r_leg" + origin_tech = "programming=2;materials=3;engineering=3" + +/obj/item/mecha_parts/part/durand_armor + name="Durand Armour Plates" + icon_state = "durand_armor" + origin_tech = "materials=5;combat=4;engineering=4" + + + +////////// Firefighter + +/obj/item/mecha_parts/chassis/firefighter + name = "Firefighter Chassis" + +/obj/item/mecha_parts/chassis/firefighter/New() + ..() + construct = new /datum/construction/mecha/firefighter_chassis(src) + +////////// HONK + +/obj/item/mecha_parts/chassis/honker + name = "H.O.N.K Chassis" + +/obj/item/mecha_parts/chassis/honker/New() + ..() + construct = new /datum/construction/mecha/honker_chassis(src) + +/obj/item/mecha_parts/part/honker_torso + name="H.O.N.K Torso" + icon_state = "honker_harness" + +/obj/item/mecha_parts/part/honker_head + name="H.O.N.K Head" + icon_state = "honker_head" + +/obj/item/mecha_parts/part/honker_left_arm + name="H.O.N.K Left Arm" + icon_state = "honker_l_arm" + +/obj/item/mecha_parts/part/honker_right_arm + name="H.O.N.K Right Arm" + icon_state = "honker_r_arm" + +/obj/item/mecha_parts/part/honker_left_leg + name="H.O.N.K Left Leg" + icon_state = "honker_l_leg" + +/obj/item/mecha_parts/part/honker_right_leg + name="H.O.N.K Right Leg" + icon_state = "honker_r_leg" + + +////////// Reticence + +/obj/item/mecha_parts/chassis/reticence + name = "Reticence Chassis" + +/obj/item/mecha_parts/chassis/reticence/New() + ..() + construct = new /datum/construction/mecha/reticence_chassis(src) + +/obj/effect/dummy/mecha_emote_step + var/emote + +/obj/effect/dummy/mecha_emote_step/New(e) + emote = e + +/obj/item/mecha_parts/chassis/reticence/hear_message(mob/living/M, msg) + if(!istype(M) || !istype(construct, /datum/construction/mecha/reticence)) + return + // is the current step the dummy emote object? + var/list/steps = construct.steps + if(steps[steps.len]["key"] == /obj/effect/dummy/mecha_emote_step) + construct.action(new /obj/effect/dummy/mecha_emote_step(msg), M) + +/obj/item/mecha_parts/part/reticence_torso + name = "Reticence Torso" + icon_state = "reticence_harness" + +/obj/item/mecha_parts/part/reticence_head + name = "Reticence Head" + icon_state = "reticence_head" + +/obj/item/mecha_parts/part/reticence_left_arm + name = "Reticence Left Arm" + icon_state = "reticence_l_arm" + +/obj/item/mecha_parts/part/reticence_right_arm + name = "Reticence Right Arm" + icon_state = "reticence_r_arm" + +/obj/item/mecha_parts/part/reticence_left_leg + name = "Reticence Left Leg" + icon_state = "reticence_l_leg" + +/obj/item/mecha_parts/part/reticence_right_leg + name = "Reticence Right Leg" + icon_state = "reticence_r_leg" + + +////////// Phazon + +/obj/item/mecha_parts/chassis/phazon + name = "Phazon Chassis" + +/obj/item/mecha_parts/chassis/phazon/New() + ..() + construct = new /datum/construction/mecha/phazon_chassis(src) + +/obj/item/mecha_parts/part/phazon_torso + name="Phazon Torso" + icon_state = "phazon_harness" + origin_tech = "programming=4;materials=4;bluespace=4;plasmatech=5" + +/obj/item/mecha_parts/part/phazon_head + name="Phazon Head" + icon_state = "phazon_head" + origin_tech = "programming=3;materials=3;magnets=3" + +/obj/item/mecha_parts/part/phazon_left_arm + name="Phazon Left Arm" + icon_state = "phazon_l_arm" + origin_tech = "materials=3;bluespace=3;magnets=3" + +/obj/item/mecha_parts/part/phazon_right_arm + name="Phazon Right Arm" + icon_state = "phazon_r_arm" + origin_tech = "materials=3;bluespace=3;magnets=3" + +/obj/item/mecha_parts/part/phazon_left_leg + name="Phazon Left Leg" + icon_state = "phazon_l_leg" + origin_tech = "materials=3;bluespace=3;magnets=3" + +/obj/item/mecha_parts/part/phazon_right_leg + name="Phazon Right Leg" + icon_state = "phazon_r_leg" + origin_tech = "materials=3;bluespace=3;magnets=3" + +/obj/item/mecha_parts/part/phazon_armor + name="Phazon armor" + desc="Phazon armor plates. They are layered with plasma to protect the pilot from the stress of phasing and have unusual properties." + icon_state = "phazon_armor" + origin_tech = "materials=4;bluespace=4;plasmatech=5" + +///////// Odysseus +/obj/item/mecha_parts/chassis/odysseus + name = "Odysseus Chassis" + +/obj/item/mecha_parts/chassis/odysseus/New() + ..() + construct = new /datum/construction/mecha/odysseus_chassis(src) + +/obj/item/mecha_parts/part/odysseus_head + name="Odysseus Head" + icon_state = "odysseus_head" + +/obj/item/mecha_parts/part/odysseus_torso + name="Odysseus Torso" + desc="A torso part of Odysseus. Contains power unit, processing core and life support systems." + icon_state = "odysseus_torso" + origin_tech = "programming=2;materials=2;biotech=2;engineering=2" + +/obj/item/mecha_parts/part/odysseus_left_arm + name="Odysseus Left Arm" + desc="An Odysseus left arm. Data and power sockets are compatible with most exosuit tools." + icon_state = "odysseus_l_arm" + +/obj/item/mecha_parts/part/odysseus_right_arm + name="Odysseus Right Arm" + desc="An Odysseus right arm. Data and power sockets are compatible with most exosuit tools." + icon_state = "odysseus_r_arm" + +/obj/item/mecha_parts/part/odysseus_left_leg + name="Odysseus Left Leg" + desc="An Odysseus left leg. Contains somewhat complex servodrives and balance maintaining systems." + icon_state = "odysseus_l_leg" + +/obj/item/mecha_parts/part/odysseus_right_leg + name="Odysseus Right Leg" + desc="A Odysseus right leg. Contains somewhat complex servodrives and balance maintaining systems." + icon_state = "odysseus_r_leg" + +/*/obj/item/mecha_parts/part/odysseus_armour + name="Odysseus Carapace" + icon_state = "odysseus_armour" + origin_tech = "materials=3;engineering=3")*/ + + +///////// Circuitboards + +/obj/item/circuitboard/mecha + name = "Exosuit Circuit board" + icon = 'icons/obj/module.dmi' + icon_state = "std_mod" + item_state = "electronic" + board_type = "other" + flags = CONDUCT + force = 5.0 + w_class = WEIGHT_CLASS_SMALL + throwforce = 5.0 + throw_speed = 3 + throw_range = 15 + +/obj/item/circuitboard/mecha/ripley + origin_tech = "programming=2" + +/obj/item/circuitboard/mecha/ripley/peripherals + name = "Circuit board (Ripley Peripherals Control module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/ripley/main + name = "Circuit board (Ripley Central Control module)" + icon_state = "mainboard" + +/obj/item/circuitboard/mecha/gygax + origin_tech = "programming=4;combat=3;engineering=3" + +/obj/item/circuitboard/mecha/gygax/peripherals + name = "Circuit board (Gygax Peripherals Control module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/gygax/targeting + name = "Circuit board (Gygax Weapon Control and Targeting module)" + icon_state = "mcontroller" + origin_tech = "programming=4;combat=4" + +/obj/item/circuitboard/mecha/gygax/main + name = "Circuit board (Gygax Central Control module)" + icon_state = "mainboard" + +/obj/item/circuitboard/mecha/durand + origin_tech = "programming=4;combat=3;engineering=3" + +/obj/item/circuitboard/mecha/durand/peripherals + name = "Circuit board (Durand Peripherals Control module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/durand/targeting + name = "Circuit board (Durand Weapon Control and Targeting module)" + icon_state = "mcontroller" + origin_tech = "programming=4;combat=4;engineering=3" + +/obj/item/circuitboard/mecha/durand/main + name = "Circuit board (Durand Central Control module)" + icon_state = "mainboard" + +/obj/item/circuitboard/mecha/phazon + origin_tech = "programming=5;plasmatech=4" + +/obj/item/circuitboard/mecha/phazon/peripherals + name = "Circuit board (Phazon Peripherals Control module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/phazon/targeting + name = "Circuit board (Phazon Weapon Control and Targeting module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/phazon/main + name = "Circuit board (Phazon Central Control module)" + icon_state = "mainboard" + +/obj/item/circuitboard/mecha/honker + origin_tech = "programming=3;engineering=3" + +/obj/item/circuitboard/mecha/honker/peripherals + name = "Circuit board (H.O.N.K Peripherals Control module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/honker/targeting + name = "Circuit board (H.O.N.K Weapon Control and Targeting module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/honker/main + name = "Circuit board (H.O.N.K Central Control module)" + icon_state = "mainboard" + +/obj/item/circuitboard/mecha/reticence + origin_tech = "programming=3;engineering=3" + +/obj/item/circuitboard/mecha/reticence/peripherals + name = "circuit board (Reticence Peripherals Control module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/reticence/targeting + name = "circuit board (Reticence Weapon Control and Targeting module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/reticence/main + name = "circuit board (Reticence Central Control module)" + icon_state = "mainboard" + +/obj/item/circuitboard/mecha/odysseus + origin_tech = "programming=3;biotech=3" + +/obj/item/circuitboard/mecha/odysseus/peripherals + name = "Circuit board (Odysseus Peripherals Control module)" + icon_state = "mcontroller" + +/obj/item/circuitboard/mecha/odysseus/main + name = "Circuit board (Odysseus Central Control module)" + icon_state = "mainboard" + +/obj/item/circuitboard/mecha/pod + name = "Circuit board (Space Pod Mainboard)" + icon_state = "mainboard" diff --git a/code/game/mecha/mecha_topic.dm b/code/game/mecha/mecha_topic.dm index 9598d328d7a..fc8f38a971c 100644 --- a/code/game/mecha/mecha_topic.dm +++ b/code/game/mecha/mecha_topic.dm @@ -18,8 +18,8 @@ .hidden {display: none;} - - - -
        - - - - -"} - - for(var/adm_ckey in admin_datums) - var/datum/admins/D = admin_datums[adm_ckey] - if(!D) continue - var/rank = D.rank ? D.rank : "*none*" - var/rights = rights2text(D.rights," ") - if(!rights) rights = "*none*" - output += {" - - - -"} - - /*output += "" - output += "" - output += "" - output += "" - output += ""*/ - - output += {" -
        CKEY \[+\]RANKPERMISSIONS
        [adm_ckey] \[-\][rank][rights]
        [adm_ckey] \[-\][rank][rights]
        -
        Search:
        - -"} - - usr << browse(output,"window=editrights;size=600x500") - -/datum/admins/proc/log_admin_rank_modification(var/adm_ckey, var/new_rank) - if(config.admin_legacy_system) return - - if(!usr.client) - return - - if(!check_rights(R_PERMISSIONS)) - return - - establish_db_connection() - - if(!dbcon.IsConnected()) - to_chat(usr, "Failed to establish database connection") - return - - if(!adm_ckey || !new_rank) - return - - adm_ckey = ckey(adm_ckey) - - if(!adm_ckey) - return - - if(!istext(adm_ckey) || !istext(new_rank)) - return - - var/DBQuery/select_query = dbcon.NewQuery("SELECT id FROM [format_table_name("admin")] WHERE ckey = '[adm_ckey]'") - select_query.Execute() - - var/new_admin = 1 - var/admin_id - while(select_query.NextRow()) - new_admin = 0 - admin_id = text2num(select_query.item[1]) - - flag_account_for_forum_sync(adm_ckey) - if(new_admin) - var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO [format_table_name("admin")] (`id`, `ckey`, `rank`, `level`, `flags`) VALUES (null, '[adm_ckey]', '[new_rank]', -1, 0)") - insert_query.Execute() - var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO [format_table_name("admin_log")] (`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (Now() , '[usr.ckey]', '[usr.client.address]', 'Added new admin [adm_ckey] to rank [new_rank]');") - log_query.Execute() - to_chat(usr, "New admin added.") - else - if(!isnull(admin_id) && isnum(admin_id)) - var/DBQuery/insert_query = dbcon.NewQuery("UPDATE [format_table_name("admin")] SET rank = '[new_rank]' WHERE id = [admin_id]") - insert_query.Execute() - var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO [format_table_name("admin_log")] (`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (Now() , '[usr.ckey]', '[usr.client.address]', 'Edited the rank of [adm_ckey] to [new_rank]');") - log_query.Execute() - to_chat(usr, "Admin rank changed.") - -/datum/admins/proc/log_admin_permission_modification(var/adm_ckey, var/new_permission) - if(config.admin_legacy_system) - return - - if(!usr.client) - return - - if(!check_rights(R_PERMISSIONS)) - return - - establish_db_connection() - if(!dbcon.IsConnected()) - to_chat(usr, "Failed to establish database connection") - return - - if(!adm_ckey || !new_permission) - return - - adm_ckey = ckey(adm_ckey) - - if(!adm_ckey) - return - - if(istext(new_permission)) - new_permission = text2num(new_permission) - - if(!istext(adm_ckey) || !isnum(new_permission)) - return - - var/DBQuery/select_query = dbcon.NewQuery("SELECT id, flags FROM [format_table_name("admin")] WHERE ckey = '[adm_ckey]'") - select_query.Execute() - - var/admin_id - var/admin_rights - while(select_query.NextRow()) - admin_id = text2num(select_query.item[1]) - admin_rights = text2num(select_query.item[2]) - - if(!admin_id) - return - - flag_account_for_forum_sync(adm_ckey) - if(admin_rights & new_permission) //This admin already has this permission, so we are removing it. - var/DBQuery/insert_query = dbcon.NewQuery("UPDATE [format_table_name("admin")] SET flags = [admin_rights & ~new_permission] WHERE id = [admin_id]") - insert_query.Execute() - var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO [format_table_name("admin_log")] (`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (Now() , '[usr.ckey]', '[usr.client.address]', 'Removed permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]');") - log_query.Execute() - to_chat(usr, "Permission removed.") - else //This admin doesn't have this permission, so we are adding it. - var/DBQuery/insert_query = dbcon.NewQuery("UPDATE [format_table_name("admin")] SET flags = '[admin_rights | new_permission]' WHERE id = [admin_id]") - insert_query.Execute() - var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO [format_table_name("admin_log")] (`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (Now() , '[usr.ckey]', '[usr.client.address]', 'Added permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]')") - log_query.Execute() - to_chat(usr, "Permission added.") - -/datum/admins/proc/updateranktodb(ckey,newrank) - establish_db_connection() - if(!dbcon.IsConnected()) - return - if(!check_rights(R_PERMISSIONS)) - return - var/sql_ckey = sanitizeSQL(ckey) - var/sql_admin_rank = sanitizeSQL(newrank) - - var/DBQuery/query_update = dbcon.NewQuery("UPDATE [format_table_name("player")] SET lastadminrank = '[sql_admin_rank]' WHERE ckey = '[sql_ckey]'") - query_update.Execute() - flag_account_for_forum_sync(sql_ckey) \ No newline at end of file +/client/proc/edit_admin_permissions() + set category = "Admin" + set name = "Permissions Panel" + set desc = "Edit admin permissions" + if(!check_rights(R_PERMISSIONS)) + return + usr.client.holder.edit_admin_permissions() + +/datum/admins/proc/edit_admin_permissions() + if(!check_rights(R_PERMISSIONS)) + return + + var/output = {" + + +Permissions Panel + + + + +
        + + + + +"} + + for(var/adm_ckey in admin_datums) + var/datum/admins/D = admin_datums[adm_ckey] + if(!D) continue + var/rank = D.rank ? D.rank : "*none*" + var/rights = rights2text(D.rights," ") + if(!rights) rights = "*none*" + output += {" + + + +"} + + /*output += "" + output += "" + output += "" + output += "" + output += ""*/ + + output += {" +
        CKEY \[+\]RANKPERMISSIONS
        [adm_ckey] \[-\][rank][rights]
        [adm_ckey] \[-\][rank][rights]
        +
        Search:
        + +"} + + usr << browse(output,"window=editrights;size=600x500") + +/datum/admins/proc/log_admin_rank_modification(var/adm_ckey, var/new_rank) + if(config.admin_legacy_system) return + + if(!usr.client) + return + + if(!check_rights(R_PERMISSIONS)) + return + + establish_db_connection() + + if(!dbcon.IsConnected()) + to_chat(usr, "Failed to establish database connection") + return + + if(!adm_ckey || !new_rank) + return + + adm_ckey = ckey(adm_ckey) + + if(!adm_ckey) + return + + if(!istext(adm_ckey) || !istext(new_rank)) + return + + var/DBQuery/select_query = dbcon.NewQuery("SELECT id FROM [format_table_name("admin")] WHERE ckey = '[adm_ckey]'") + select_query.Execute() + + var/new_admin = 1 + var/admin_id + while(select_query.NextRow()) + new_admin = 0 + admin_id = text2num(select_query.item[1]) + + flag_account_for_forum_sync(adm_ckey) + if(new_admin) + var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO [format_table_name("admin")] (`id`, `ckey`, `rank`, `level`, `flags`) VALUES (null, '[adm_ckey]', '[new_rank]', -1, 0)") + insert_query.Execute() + var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO [format_table_name("admin_log")] (`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (Now() , '[usr.ckey]', '[usr.client.address]', 'Added new admin [adm_ckey] to rank [new_rank]');") + log_query.Execute() + to_chat(usr, "New admin added.") + else + if(!isnull(admin_id) && isnum(admin_id)) + var/DBQuery/insert_query = dbcon.NewQuery("UPDATE [format_table_name("admin")] SET rank = '[new_rank]' WHERE id = [admin_id]") + insert_query.Execute() + var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO [format_table_name("admin_log")] (`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (Now() , '[usr.ckey]', '[usr.client.address]', 'Edited the rank of [adm_ckey] to [new_rank]');") + log_query.Execute() + to_chat(usr, "Admin rank changed.") + +/datum/admins/proc/log_admin_permission_modification(var/adm_ckey, var/new_permission) + if(config.admin_legacy_system) + return + + if(!usr.client) + return + + if(!check_rights(R_PERMISSIONS)) + return + + establish_db_connection() + if(!dbcon.IsConnected()) + to_chat(usr, "Failed to establish database connection") + return + + if(!adm_ckey || !new_permission) + return + + adm_ckey = ckey(adm_ckey) + + if(!adm_ckey) + return + + if(istext(new_permission)) + new_permission = text2num(new_permission) + + if(!istext(adm_ckey) || !isnum(new_permission)) + return + + var/DBQuery/select_query = dbcon.NewQuery("SELECT id, flags FROM [format_table_name("admin")] WHERE ckey = '[adm_ckey]'") + select_query.Execute() + + var/admin_id + var/admin_rights + while(select_query.NextRow()) + admin_id = text2num(select_query.item[1]) + admin_rights = text2num(select_query.item[2]) + + if(!admin_id) + return + + flag_account_for_forum_sync(adm_ckey) + if(admin_rights & new_permission) //This admin already has this permission, so we are removing it. + var/DBQuery/insert_query = dbcon.NewQuery("UPDATE [format_table_name("admin")] SET flags = [admin_rights & ~new_permission] WHERE id = [admin_id]") + insert_query.Execute() + var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO [format_table_name("admin_log")] (`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (Now() , '[usr.ckey]', '[usr.client.address]', 'Removed permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]');") + log_query.Execute() + to_chat(usr, "Permission removed.") + else //This admin doesn't have this permission, so we are adding it. + var/DBQuery/insert_query = dbcon.NewQuery("UPDATE [format_table_name("admin")] SET flags = '[admin_rights | new_permission]' WHERE id = [admin_id]") + insert_query.Execute() + var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO [format_table_name("admin_log")] (`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (Now() , '[usr.ckey]', '[usr.client.address]', 'Added permission [rights2text(new_permission)] (flag = [new_permission]) to admin [adm_ckey]')") + log_query.Execute() + to_chat(usr, "Permission added.") + +/datum/admins/proc/updateranktodb(ckey,newrank) + establish_db_connection() + if(!dbcon.IsConnected()) + return + if(!check_rights(R_PERMISSIONS)) + return + var/sql_ckey = sanitizeSQL(ckey) + var/sql_admin_rank = sanitizeSQL(newrank) + + var/DBQuery/query_update = dbcon.NewQuery("UPDATE [format_table_name("player")] SET lastadminrank = '[sql_admin_rank]' WHERE ckey = '[sql_ckey]'") + query_update.Execute() + flag_account_for_forum_sync(sql_ckey) diff --git a/code/modules/admin/secrets.dm b/code/modules/admin/secrets.dm index fe487ae6431..4e2fe2d7ae2 100644 --- a/code/modules/admin/secrets.dm +++ b/code/modules/admin/secrets.dm @@ -49,7 +49,7 @@ Security Level Elevated

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



        Nanotrasen Science Station [using_map.station_short]


        NAS Trurl Communications Department Report


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

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

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

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

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

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

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

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

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

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

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

        Please try again later.

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

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

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

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

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

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

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

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

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



        Nanotrasen Science Station [using_map.station_short]


        NAS Trurl Communications Department Report


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

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

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

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

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

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

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

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

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

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

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

        Please try again later.

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

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

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

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

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

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

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

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

        " + for(var/datum/job/job in SSjobs.occupations) + if(!job) continue + dat += "job: [job.title], current_positions: [job.current_positions], total_positions: [job.total_positions]
        " + usr << browse(dat, "window=jobdebug;size=600x500") + if("showailaws") + output_ai_laws() + if("showgm") + if(!SSticker) + alert("The game hasn't started yet!") + else if(SSticker.mode) + alert("The game mode is [SSticker.mode.name]") + else alert("For some reason there's a ticker, but not a game mode") + if("manifest") + var/dat = "Showing Crew Manifest.
        " + dat += "" + for(var/mob/living/carbon/human/H in GLOB.mob_list) + if(H.ckey) + dat += text("", H.name, H.get_assignment()) + dat += "
        NamePosition
        [][]
        " + usr << browse(dat, "window=manifest;size=440x410") + if("check_antagonist") + check_antagonists() + if("DNA") + var/dat = "Showing DNA from blood.
        " + dat += "" + for(var/mob/living/carbon/human/H in GLOB.mob_list) + if(H.dna && H.ckey) + dat += "" + dat += "
        NameDNABlood Type
        [H][H.dna.unique_enzymes][H.dna.blood_type]
        " + usr << browse(dat, "window=DNA;size=440x410") + if("fingerprints") + var/dat = "Showing Fingerprints.
        " + dat += "" + for(var/mob/living/carbon/human/H in GLOB.mob_list) + if(H.ckey) + if(H.dna && H.dna.uni_identity) + dat += "" + else if(H.dna && !H.dna.uni_identity) + dat += "" + else if(!H.dna) + dat += "" + dat += "
        NameFingerprints
        [H][md5(H.dna.uni_identity)]
        [H]H.dna.uni_identity = null
        [H]H.dna = null
        " + usr << browse(dat, "window=fingerprints;size=440x410") + if("night_shift_set") + var/val = alert(usr, "What do you want to set night shift to? This will override the automatic system until set to automatic again.", "Night Shift", "On", "Off", "Automatic") + switch(val) + if("Automatic") + if(config.enable_night_shifts) + SSnightshift.can_fire = TRUE + SSnightshift.fire() + else + SSnightshift.update_nightshift(FALSE, TRUE) + to_chat(usr, "Night shift set to automatic.") + if("On") + SSnightshift.can_fire = FALSE + SSnightshift.update_nightshift(TRUE, FALSE) + to_chat(usr, "Night shift forced on.") + if("Off") + SSnightshift.can_fire = FALSE + SSnightshift.update_nightshift(FALSE, FALSE) + to_chat(usr, "Night shift forced off.") + else + if(usr) + log_admin("[key_name(usr)] used secret [href_list["secretsadmin"]]") + if(ok) + to_chat(world, text("A secret has been activated by []!", usr.key)) + + else if(href_list["secretscoder"]) + if(!check_rights(R_DEBUG)) return + + switch(href_list["secretscoder"]) + if("spawn_objects") + var/dat = "Admin Log
        " + for(var/l in admin_log) + dat += "
      • [l]
      • " + if(!admin_log.len) + dat += "No-one has done anything this round!" + usr << browse(dat, "window=admin_log") + if("maint_ACCESS_BRIG") + for(var/obj/machinery/door/airlock/maintenance/M in GLOB.airlocks) + if(ACCESS_MAINT_TUNNELS in M.req_access) + M.req_access = list(ACCESS_BRIG) + message_admins("[key_name_admin(usr)] made all maint doors brig access-only.") + if("maint_access_engiebrig") + for(var/obj/machinery/door/airlock/maintenance/M in GLOB.airlocks) + if(ACCESS_MAINT_TUNNELS in M.req_access) + M.req_access = list() + M.req_one_access = list(ACCESS_BRIG,ACCESS_ENGINE) + message_admins("[key_name_admin(usr)] made all maint doors engineering and brig access-only.") + if("infinite_sec") + var/datum/job/J = SSjobs.GetJob("Security Officer") + if(!J) return + J.total_positions = -1 + J.spawn_positions = -1 + message_admins("[key_name_admin(usr)] has removed the cap on security officers.") + + else if(href_list["ac_view_wanted"]) //Admin newscaster Topic() stuff be here + src.admincaster_screen = 18 //The ac_ prefix before the hrefs stands for AdminCaster. + src.access_news_network() + + else if(href_list["ac_set_channel_name"]) + src.admincaster_feed_channel.channel_name = strip_html_simple(input(usr, "Provide a Feed Channel Name", "Network Channel Handler", "")) + while(findtext(src.admincaster_feed_channel.channel_name," ") == 1) + src.admincaster_feed_channel.channel_name = copytext(src.admincaster_feed_channel.channel_name,2,length(src.admincaster_feed_channel.channel_name)+1) + src.access_news_network() + + else if(href_list["ac_set_channel_lock"]) + src.admincaster_feed_channel.locked = !src.admincaster_feed_channel.locked + src.access_news_network() + + else if(href_list["ac_submit_new_channel"]) + var/check = 0 + for(var/datum/feed_channel/FC in news_network.network_channels) + if(FC.channel_name == src.admincaster_feed_channel.channel_name) + check = 1 + break + if(src.admincaster_feed_channel.channel_name == "" || src.admincaster_feed_channel.channel_name == "\[REDACTED\]" || check ) + src.admincaster_screen=7 + else + var/choice = alert("Please confirm Feed channel creation","Network Channel Handler","Confirm","Cancel") + if(choice=="Confirm") + var/datum/feed_channel/newChannel = new /datum/feed_channel + newChannel.channel_name = src.admincaster_feed_channel.channel_name + newChannel.author = src.admincaster_signature + newChannel.locked = src.admincaster_feed_channel.locked + newChannel.is_admin_channel = 1 + feedback_inc("newscaster_channels",1) + news_network.network_channels += newChannel //Adding channel to the global network + log_admin("[key_name_admin(usr)] created command feed channel: [src.admincaster_feed_channel.channel_name]!") + src.admincaster_screen=5 + src.access_news_network() + + else if(href_list["ac_set_channel_receiving"]) + var/list/available_channels = list() + for(var/datum/feed_channel/F in news_network.network_channels) + available_channels += F.channel_name + src.admincaster_feed_channel.channel_name = adminscrub(input(usr, "Choose receiving Feed Channel", "Network Channel Handler") in available_channels ) + src.access_news_network() + + else if(href_list["ac_set_new_message"]) + src.admincaster_feed_message.body = adminscrub(input(usr, "Write your Feed story", "Network Channel Handler", "")) + while(findtext(src.admincaster_feed_message.body," ") == 1) + src.admincaster_feed_message.body = copytext(src.admincaster_feed_message.body,2,length(src.admincaster_feed_message.body)+1) + src.access_news_network() + + else if(href_list["ac_submit_new_message"]) + if(src.admincaster_feed_message.body =="" || src.admincaster_feed_message.body =="\[REDACTED\]" || src.admincaster_feed_channel.channel_name == "" ) + src.admincaster_screen = 6 + else + var/datum/feed_message/newMsg = new /datum/feed_message + newMsg.author = src.admincaster_signature + newMsg.body = src.admincaster_feed_message.body + newMsg.is_admin_message = 1 + feedback_inc("newscaster_stories",1) + for(var/datum/feed_channel/FC in news_network.network_channels) + if(FC.channel_name == src.admincaster_feed_channel.channel_name) + FC.messages += newMsg //Adding message to the network's appropriate feed_channel + break + src.admincaster_screen=4 + + for(var/obj/machinery/newscaster/NEWSCASTER in allCasters) + NEWSCASTER.newsAlert(src.admincaster_feed_channel.channel_name) + + log_admin("[key_name_admin(usr)] submitted a feed story to channel: [src.admincaster_feed_channel.channel_name]!") + src.access_news_network() + + else if(href_list["ac_create_channel"]) + src.admincaster_screen=2 + src.access_news_network() + + else if(href_list["ac_create_feed_story"]) + src.admincaster_screen=3 + src.access_news_network() + + else if(href_list["ac_menu_censor_story"]) + src.admincaster_screen=10 + src.access_news_network() + + else if(href_list["ac_menu_censor_channel"]) + src.admincaster_screen=11 + src.access_news_network() + + else if(href_list["ac_menu_wanted"]) + var/already_wanted = 0 + if(news_network.wanted_issue) + already_wanted = 1 + + if(already_wanted) + src.admincaster_feed_message.author = news_network.wanted_issue.author + src.admincaster_feed_message.body = news_network.wanted_issue.body + src.admincaster_screen = 14 + src.access_news_network() + + else if(href_list["ac_set_wanted_name"]) + src.admincaster_feed_message.author = adminscrub(input(usr, "Provide the name of the Wanted person", "Network Security Handler", "")) + while(findtext(src.admincaster_feed_message.author," ") == 1) + src.admincaster_feed_message.author = copytext(admincaster_feed_message.author,2,length(admincaster_feed_message.author)+1) + src.access_news_network() + + else if(href_list["ac_set_wanted_desc"]) + src.admincaster_feed_message.body = adminscrub(input(usr, "Provide the a description of the Wanted person and any other details you deem important", "Network Security Handler", "")) + while(findtext(src.admincaster_feed_message.body," ") == 1) + src.admincaster_feed_message.body = copytext(src.admincaster_feed_message.body,2,length(src.admincaster_feed_message.body)+1) + src.access_news_network() + + else if(href_list["ac_submit_wanted"]) + var/input_param = text2num(href_list["ac_submit_wanted"]) + if(src.admincaster_feed_message.author == "" || src.admincaster_feed_message.body == "") + src.admincaster_screen = 16 + else + var/choice = alert("Please confirm Wanted Issue [(input_param==1) ? ("creation.") : ("edit.")]","Network Security Handler","Confirm","Cancel") + if(choice=="Confirm") + if(input_param==1) //If input_param == 1 we're submitting a new wanted issue. At 2 we're just editing an existing one. See the else below + var/datum/feed_message/WANTED = new /datum/feed_message + WANTED.author = src.admincaster_feed_message.author //Wanted name + WANTED.body = src.admincaster_feed_message.body //Wanted desc + WANTED.backup_author = src.admincaster_signature //Submitted by + WANTED.is_admin_message = 1 + news_network.wanted_issue = WANTED + for(var/obj/machinery/newscaster/NEWSCASTER in allCasters) + NEWSCASTER.newsAlert() + NEWSCASTER.update_icon() + src.admincaster_screen = 15 + else + news_network.wanted_issue.author = src.admincaster_feed_message.author + news_network.wanted_issue.body = src.admincaster_feed_message.body + news_network.wanted_issue.backup_author = src.admincaster_feed_message.backup_author + src.admincaster_screen = 19 + log_admin("[key_name_admin(usr)] issued a Station-wide Wanted Notification for [src.admincaster_feed_message.author]!") + src.access_news_network() + + else if(href_list["ac_cancel_wanted"]) + var/choice = alert("Please confirm Wanted Issue removal","Network Security Handler","Confirm","Cancel") + if(choice=="Confirm") + news_network.wanted_issue = null + for(var/obj/machinery/newscaster/NEWSCASTER in allCasters) + NEWSCASTER.update_icon() + src.admincaster_screen=17 + src.access_news_network() + + else if(href_list["ac_censor_channel_author"]) + var/datum/feed_channel/FC = locate(href_list["ac_censor_channel_author"]) + if(FC.author != "\[REDACTED\]") + FC.backup_author = FC.author + FC.author = "\[REDACTED\]" + else + FC.author = FC.backup_author + src.access_news_network() + + else if(href_list["ac_censor_channel_story_author"]) + var/datum/feed_message/MSG = locate(href_list["ac_censor_channel_story_author"]) + if(MSG.author != "\[REDACTED\]") + MSG.backup_author = MSG.author + MSG.author = "\[REDACTED\]" + else + MSG.author = MSG.backup_author + src.access_news_network() + + else if(href_list["ac_censor_channel_story_body"]) + var/datum/feed_message/MSG = locate(href_list["ac_censor_channel_story_body"]) + if(MSG.body != "\[REDACTED\]") + MSG.backup_body = MSG.body + MSG.body = "\[REDACTED\]" + else + MSG.body = MSG.backup_body + src.access_news_network() + + else if(href_list["ac_pick_d_notice"]) + var/datum/feed_channel/FC = locate(href_list["ac_pick_d_notice"]) + src.admincaster_feed_channel = FC + src.admincaster_screen=13 + src.access_news_network() + + else if(href_list["ac_toggle_d_notice"]) + var/datum/feed_channel/FC = locate(href_list["ac_toggle_d_notice"]) + FC.censored = !FC.censored + src.access_news_network() + + else if(href_list["ac_view"]) + src.admincaster_screen=1 + src.access_news_network() + + else if(href_list["ac_setScreen"]) //Brings us to the main menu and resets all fields~ + src.admincaster_screen = text2num(href_list["ac_setScreen"]) + if(src.admincaster_screen == 0) + if(src.admincaster_feed_channel) + src.admincaster_feed_channel = new /datum/feed_channel + if(src.admincaster_feed_message) + src.admincaster_feed_message = new /datum/feed_message + src.access_news_network() + + else if(href_list["ac_show_channel"]) + var/datum/feed_channel/FC = locate(href_list["ac_show_channel"]) + src.admincaster_feed_channel = FC + src.admincaster_screen = 9 + src.access_news_network() + + else if(href_list["ac_pick_censor_channel"]) + var/datum/feed_channel/FC = locate(href_list["ac_pick_censor_channel"]) + src.admincaster_feed_channel = FC + src.admincaster_screen = 12 + src.access_news_network() + + else if(href_list["ac_refresh"]) + src.access_news_network() + + else if(href_list["ac_set_signature"]) + src.admincaster_signature = adminscrub(input(usr, "Provide your desired signature", "Network Identity Handler", "")) + src.access_news_network() + + if(href_list["secretsmenu"]) + switch(href_list["secretsmenu"]) + if("tab") + current_tab = text2num(href_list["tab"]) + Secrets(usr) + return 1 + + else if(href_list["viewruntime"]) + var/datum/ErrorViewer/error_viewer = locateUID(href_list["viewruntime"]) + if(!istype(error_viewer)) + to_chat(usr, "That runtime viewer no longer exists.") + return + if(href_list["viewruntime_backto"]) + error_viewer.showTo(usr, locateUID(href_list["viewruntime_backto"]), href_list["viewruntime_linear"]) + else + error_viewer.showTo(usr, null, href_list["viewruntime_linear"]) + + else if(href_list["add_station_goal"]) + if(!check_rights(R_EVENT)) + return + var/list/type_choices = typesof(/datum/station_goal) + var/picked = input("Choose goal type") in type_choices|null + if(!picked) + return + var/datum/station_goal/G = new picked() + if(picked == /datum/station_goal) + var/newname = clean_input("Enter goal name:") + if(!newname) + return + G.name = newname + var/description = input("Enter [command_name()] message contents:") as message|null + if(!description) + return + G.report_message = description + message_admins("[key_name_admin(usr)] created \"[G.name]\" station goal.") + SSticker.mode.station_goals += G + modify_goals() + + else if(href_list["showdetails"]) + if(!check_rights(R_ADMIN)) + return + var/text = html_decode(href_list["showdetails"]) + usr << browse("Details[replacetext(text, "\n", "
        ")]
        ", + "window=show_details;size=500x200") + + // Library stuff + else if(href_list["library_book_id"]) + var/isbn = sanitizeSQL(href_list["library_book_id"]) + + if(href_list["view_library_book"]) + var/DBQuery/query_view_book = dbcon.NewQuery("SELECT content, title FROM [format_table_name("library")] WHERE id=[isbn]") + if(!query_view_book.Execute()) + var/err = query_view_book.ErrorMsg() + log_game("SQL ERROR viewing book. Error : \[[err]\]\n") + return + + var/content = "" + var/title = "" + while(query_view_book.NextRow()) + content = query_view_book.item[1] + title = html_encode(query_view_book.item[2]) + + var/dat = "
        "
        +			dat += "[html_encode(html_to_pencode(content))]"
        +			dat += "
        " + + var/datum/browser/popup = new(usr, "admin_view_book", "[title]", 700, 400) + popup.set_content(dat) + popup.open(0) + + log_admin("[key_name(usr)] has viewed the book [isbn].") + message_admins("[key_name_admin(usr)] has viewed the book [isbn].") + return + + else if(href_list["unflag_library_book"]) + var/DBQuery/query_unflag_book = dbcon.NewQuery("UPDATE [format_table_name("library")] SET flagged = 0 WHERE id=[isbn]") + if(!query_unflag_book.Execute()) + var/err = query_unflag_book.ErrorMsg() + log_game("SQL ERROR unflagging book. Error : \[[err]\]\n") + return + + log_admin("[key_name(usr)] has unflagged the book [isbn].") + message_admins("[key_name_admin(usr)] has unflagged the book [isbn].") + + else if(href_list["delete_library_book"]) + var/DBQuery/query_delbook = dbcon.NewQuery("DELETE FROM [format_table_name("library")] WHERE id=[isbn]") + if(!query_delbook.Execute()) + var/err = query_delbook.ErrorMsg() + log_game("SQL ERROR deleting book. Error : \[[err]\]\n") + return + + log_admin("[key_name(usr)] has deleted the book [isbn].") + message_admins("[key_name_admin(usr)] has deleted the book [isbn].") + + // Refresh the page + src.view_flagged_books() + + // Force unlink a discord key + else if(href_list["force_discord_unlink"]) + if(!check_rights(R_ADMIN)) + return + var/target_ckey = href_list["force_discord_unlink"] + var/DBQuery/admin_unlink_discord_id = dbcon.NewQuery("DELETE FROM [format_table_name("discord")] WHERE ckey = '[target_ckey]'") + if(!admin_unlink_discord_id.Execute()) + var/err = admin_unlink_discord_id.ErrorMsg() + log_game("SQL ERROR while admin-unlinking discord account. Error : \[[err]\]\n") + return + to_chat(src, "Successfully forcefully unlinked discord account from [target_ckey]") + message_admins("[key_name_admin(usr)] forcefully unlinked the discord account belonging to [target_ckey]") + log_admin("[key_name_admin(usr)] forcefully unlinked the discord account belonging to [target_ckey]") + + else if(href_list["create_outfit_finalize"]) + if(!check_rights(R_EVENT)) + return + create_outfit_finalize(usr,href_list) + else if(href_list["load_outfit"]) + if(!check_rights(R_EVENT)) + return + load_outfit(usr) + else if(href_list["create_outfit_menu"]) + if(!check_rights(R_EVENT)) + return + create_outfit(usr) + else if(href_list["delete_outfit"]) + if(!check_rights(R_EVENT)) + return + var/datum/outfit/O = locate(href_list["chosen_outfit"]) in GLOB.custom_outfits + delete_outfit(usr,O) + else if(href_list["save_outfit"]) + if(!check_rights(R_EVENT)) + return + var/datum/outfit/O = locate(href_list["chosen_outfit"]) in GLOB.custom_outfits + save_outfit(usr,O) + +/client/proc/create_eventmob_for(var/mob/living/carbon/human/H, var/killthem = 0) + if(!check_rights(R_EVENT)) + return + var/admin_outfits = subtypesof(/datum/outfit/admin) + var/hunter_outfits = list() + for(var/type in admin_outfits) + var/datum/outfit/admin/O = type + hunter_outfits[initial(O.name)] = type + var/dresscode = input("Select type", "Contracted Agents") as null|anything in hunter_outfits + if(isnull(dresscode)) + return + var/datum/outfit/O = hunter_outfits[dresscode] + message_admins("[key_name_admin(mob)] is sending a ([dresscode]) to [killthem ? "assassinate" : "protect"] [key_name_admin(H)]...") + var/list/candidates = pollCandidates("Play as a [killthem ? "murderous" : "protective"] [dresscode]?", ROLE_TRAITOR, 1) + if(!candidates.len) + to_chat(usr, "ERROR: Could not create eventmob. No valid candidates.") + return + var/mob/C = pick(candidates) + var/key_of_hunter = C.key + if(!key_of_hunter) + to_chat(usr, "ERROR: Could not create eventmob. Could not pick key.") + return + var/datum/mind/hunter_mind = new /datum/mind(key_of_hunter) + hunter_mind.active = 1 + var/mob/living/carbon/human/hunter_mob = new /mob/living/carbon/human(pick(latejoin)) + hunter_mind.transfer_to(hunter_mob) + hunter_mob.equipOutfit(O, FALSE) + var/obj/item/pinpointer/advpinpointer/N = new /obj/item/pinpointer/advpinpointer(hunter_mob) + hunter_mob.equip_to_slot_or_del(N, slot_in_backpack) + N.active = 1 + N.mode = 2 + N.target = H + N.point_at(N.target) + if(!locate(/obj/item/implant/dust, hunter_mob)) + var/obj/item/implant/dust/D = new /obj/item/implant/dust(hunter_mob) + D.implant(hunter_mob) + if(killthem) + var/datum/objective/assassinate/kill_objective = new + kill_objective.owner = hunter_mind + kill_objective.target = H.mind + kill_objective.explanation_text = "Kill [H.real_name], the [H.mind.assigned_role]." + hunter_mind.objectives += kill_objective + else + var/datum/objective/protect/protect_objective = new + protect_objective.owner = hunter_mind + protect_objective.target = H.mind + protect_objective.explanation_text = "Protect [H.real_name], the [H.mind.assigned_role]." + hunter_mind.objectives += protect_objective + SSticker.mode.traitors |= hunter_mob.mind + to_chat(hunter_mob, "ATTENTION: You are now on a mission!") + to_chat(hunter_mob, "Goal: [killthem ? "MURDER" : "PROTECT"] [H.real_name], currently in [get_area(H.loc)]. "); + if(killthem) + to_chat(hunter_mob, "If you kill [H.p_them()], [H.p_they()] cannot be revived."); + hunter_mob.mind.special_role = SPECIAL_ROLE_TRAITOR + var/datum/atom_hud/antag/tatorhud = huds[ANTAG_HUD_TRAITOR] + tatorhud.join_hud(hunter_mob) + set_antag_hud(hunter_mob, "hudsyndicate") + +/proc/admin_jump_link(var/atom/target) + if(!target) return + // The way admin jump links handle their src is weirdly inconsistent... + + . = ADMIN_FLW(target,"FLW") + if(isAI(target)) // AI core/eye follow links + var/mob/living/silicon/ai/A = target + if(A.client && A.eyeobj) // No point following clientless AI eyes + . += "|[ADMIN_FLW(A.eyeobj,"EYE")]" + else if(istype(target, /mob/dead/observer)) + var/mob/dead/observer/O = target + if(O.mind && O.mind.current) + . += "|[ADMIN_FLW(O.mind.current,"BDY")]" diff --git a/code/modules/admin/verbs/BrokenInhands.dm b/code/modules/admin/verbs/BrokenInhands.dm index e0fb1a46913..75e051b1ff0 100644 --- a/code/modules/admin/verbs/BrokenInhands.dm +++ b/code/modules/admin/verbs/BrokenInhands.dm @@ -1,36 +1,36 @@ -/proc/getbrokeninhands() - set name = "Broken Sprite List" - set category = "Debug" - - var/text - for(var/A in typesof(/obj/item)) - var/obj/item/O = new A( locate(1,1,1) ) - if(!O) continue - var/icon/IL = new(O.lefthand_file) - var/list/Lstates = IL.IconStates() - var/icon/IR = new(O.righthand_file) - var/list/Rstates = IR.IconStates() - var/icon/J = new(O.icon) - var/list/istates = J.IconStates() - if(!Lstates.Find(O.icon_state) && !Lstates.Find(O.item_state)) - if(O.icon_state) - text += "[O.type] is missing left hand icon called \"[O.icon_state]\".\n" - if(!Rstates.Find(O.icon_state) && !Rstates.Find(O.item_state)) - if(O.icon_state) - text += "[O.type] is missing right hand icon called \"[O.icon_state]\".\n" - - - if(O.icon_state) - if(!istates.Find(O.icon_state)) - text += "[O.type] is missing normal icon called \"[O.icon_state]\" in \"[O.icon]\".\n" - //if(O.item_state) - // if(!istates.Find(O.item_state)) - // text += "[O.type] MISSING NORMAL ICON CALLED\n\"[O.item_state]\" IN \"[O.icon]\"\n" - //text+="\n" - qdel(O) - if(text) - var/F = file("broken_hand_icons.txt") - fdel(F) - to_chat(F, text) - to_chat(world, "Completed and written to [F]") - +/proc/getbrokeninhands() + set name = "Broken Sprite List" + set category = "Debug" + + var/text + for(var/A in typesof(/obj/item)) + var/obj/item/O = new A( locate(1,1,1) ) + if(!O) continue + var/icon/IL = new(O.lefthand_file) + var/list/Lstates = IL.IconStates() + var/icon/IR = new(O.righthand_file) + var/list/Rstates = IR.IconStates() + var/icon/J = new(O.icon) + var/list/istates = J.IconStates() + if(!Lstates.Find(O.icon_state) && !Lstates.Find(O.item_state)) + if(O.icon_state) + text += "[O.type] is missing left hand icon called \"[O.icon_state]\".\n" + if(!Rstates.Find(O.icon_state) && !Rstates.Find(O.item_state)) + if(O.icon_state) + text += "[O.type] is missing right hand icon called \"[O.icon_state]\".\n" + + + if(O.icon_state) + if(!istates.Find(O.icon_state)) + text += "[O.type] is missing normal icon called \"[O.icon_state]\" in \"[O.icon]\".\n" + //if(O.item_state) + // if(!istates.Find(O.item_state)) + // text += "[O.type] MISSING NORMAL ICON CALLED\n\"[O.item_state]\" IN \"[O.icon]\"\n" + //text+="\n" + qdel(O) + if(text) + var/F = file("broken_hand_icons.txt") + fdel(F) + to_chat(F, text) + to_chat(world, "Completed and written to [F]") + diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm index 76e22259893..1a13539c520 100644 --- a/code/modules/admin/verbs/adminhelp.dm +++ b/code/modules/admin/verbs/adminhelp.dm @@ -1,187 +1,187 @@ - - -//This is a list of words which are ignored by the parser when comparing message contents for names. MUST BE IN LOWER CASE! -var/list/adminhelp_ignored_words = list("unknown","the","a","an","of","monkey","alien","as") - -/client/verb/adminhelp() - set category = "Admin" - set name = "Adminhelp" - - //handle muting and automuting - if(prefs.muted & MUTE_ADMINHELP) - to_chat(src, "Error: Admin-PM: You cannot send adminhelps (Muted).") - return - - adminhelped = 1 //Determines if they get the message to reply by clicking the name. - - var/msg - var/list/type = list("Mentorhelp","Adminhelp") - var/selected_type = input("Pick a category.", "Admin Help", null, null) as null|anything in type - if(selected_type) - msg = clean_input("Please enter your message.", "Admin Help", null) - - //clean the input msg - if(!msg) - return - - if(handle_spam_prevention(msg, MUTE_ADMINHELP, OOC_COOLDOWN)) - return - - msg = sanitize_simple(copytext(msg,1,MAX_MESSAGE_LEN)) - if(!msg) return - var/original_msg = msg - - //explode the input msg into a list - var/list/msglist = splittext(msg, " ") - - //generate keywords lookup - var/list/surnames = list() - var/list/forenames = list() - var/list/ckeys = list() - for(var/mob/M in GLOB.mob_list) - var/list/indexing = list(M.real_name, M.name) - if(M.mind) indexing += M.mind.name - - for(var/string in indexing) - var/list/L = splittext(string, " ") - var/surname_found = 0 - //surnames - for(var/i=L.len, i>=1, i--) - var/word = ckey(L[i]) - if(word) - surnames[word] = M - surname_found = i - break - //forenames - for(var/i=1, iTICKET) [ai_found ? "(CL)" : ""] (TAKE) (RESOLVE) [isMhelp ? "" : "(AUTO)"] : [span][msg]" - if(isMhelp) - //Open a new adminticket and inform the user. - SSmentor_tickets.newTicket(src, prunedmsg, msg) - for(var/client/X in mentorholders + modholders + adminholders) - if(X.prefs.sound & SOUND_MENTORHELP) - X << 'sound/effects/adminhelp.ogg' - to_chat(X, msg) - else //Ahelp - //Open a new adminticket and inform the user. - SStickets.newTicket(src, prunedmsg, msg) - for(var/client/X in modholders + adminholders) - if(X.prefs.sound & SOUND_ADMINHELP) - X << 'sound/effects/adminhelp.ogg' - window_flash(X) - to_chat(X, msg) - - - - //show it to the person adminhelping too - to_chat(src, "[selected_type]
        : [original_msg]") - - var/admin_number_present = adminholders.len - admin_number_afk - log_admin("[selected_type]: [key_name(src)]: [original_msg] - heard by [admin_number_present] non-AFK admins.") - if(admin_number_present <= 0) - if(!admin_number_afk) - send2adminirc("[selected_type] from [key_name(src)]: [original_msg] - !!No admins online!!") - else - send2adminirc("[selected_type] from [key_name(src)]: [original_msg] - !!All admins AFK ([admin_number_afk])!!") - else - send2adminirc("[selected_type] from [key_name(src)]: [original_msg]") - feedback_add_details("admin_verb","AH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/proc/send2irc_adminless_only(source, msg, requiredflags = R_BAN) - var/admin_number_total = 0 //Total number of admins - var/admin_number_afk = 0 //Holds the number of admins who are afk - var/admin_number_ignored = 0 //Holds the number of admins without +BAN (so admins who are not really admins) - var/admin_number_decrease = 0 //Holds the number of admins with are afk, ignored or both - for(var/client/X in GLOB.admins) - admin_number_total++; - var/invalid = 0 - if(requiredflags != 0 && !check_rights_for(X, requiredflags)) - admin_number_ignored++ - invalid = 1 - if(X.is_afk()) - admin_number_afk++ - invalid = 1 - if(X.holder.fakekey) - admin_number_ignored++ - invalid = 1 - if(invalid) - admin_number_decrease++ - var/admin_number_present = admin_number_total - admin_number_decrease //Number of admins who are neither afk nor invalid - if(admin_number_present <= 0) - if(!admin_number_afk && !admin_number_ignored) - send2irc(source, "[msg] - No admins online") - else - send2irc(source, "[msg] - All admins AFK ([admin_number_afk]/[admin_number_total]) or skipped ([admin_number_ignored]/[admin_number_total])") - return admin_number_present + + +//This is a list of words which are ignored by the parser when comparing message contents for names. MUST BE IN LOWER CASE! +var/list/adminhelp_ignored_words = list("unknown","the","a","an","of","monkey","alien","as") + +/client/verb/adminhelp() + set category = "Admin" + set name = "Adminhelp" + + //handle muting and automuting + if(prefs.muted & MUTE_ADMINHELP) + to_chat(src, "Error: Admin-PM: You cannot send adminhelps (Muted).") + return + + adminhelped = 1 //Determines if they get the message to reply by clicking the name. + + var/msg + var/list/type = list("Mentorhelp","Adminhelp") + var/selected_type = input("Pick a category.", "Admin Help", null, null) as null|anything in type + if(selected_type) + msg = clean_input("Please enter your message.", "Admin Help", null) + + //clean the input msg + if(!msg) + return + + if(handle_spam_prevention(msg, MUTE_ADMINHELP, OOC_COOLDOWN)) + return + + msg = sanitize_simple(copytext(msg,1,MAX_MESSAGE_LEN)) + if(!msg) return + var/original_msg = msg + + //explode the input msg into a list + var/list/msglist = splittext(msg, " ") + + //generate keywords lookup + var/list/surnames = list() + var/list/forenames = list() + var/list/ckeys = list() + for(var/mob/M in GLOB.mob_list) + var/list/indexing = list(M.real_name, M.name) + if(M.mind) indexing += M.mind.name + + for(var/string in indexing) + var/list/L = splittext(string, " ") + var/surname_found = 0 + //surnames + for(var/i=L.len, i>=1, i--) + var/word = ckey(L[i]) + if(word) + surnames[word] = M + surname_found = i + break + //forenames + for(var/i=1, iTICKET) [ai_found ? "(CL)" : ""] (TAKE) (RESOLVE) [isMhelp ? "" : "(AUTO)"] : [span][msg]" + if(isMhelp) + //Open a new adminticket and inform the user. + SSmentor_tickets.newTicket(src, prunedmsg, msg) + for(var/client/X in mentorholders + modholders + adminholders) + if(X.prefs.sound & SOUND_MENTORHELP) + X << 'sound/effects/adminhelp.ogg' + to_chat(X, msg) + else //Ahelp + //Open a new adminticket and inform the user. + SStickets.newTicket(src, prunedmsg, msg) + for(var/client/X in modholders + adminholders) + if(X.prefs.sound & SOUND_ADMINHELP) + X << 'sound/effects/adminhelp.ogg' + window_flash(X) + to_chat(X, msg) + + + + //show it to the person adminhelping too + to_chat(src, "[selected_type]
        : [original_msg]") + + var/admin_number_present = adminholders.len - admin_number_afk + log_admin("[selected_type]: [key_name(src)]: [original_msg] - heard by [admin_number_present] non-AFK admins.") + if(admin_number_present <= 0) + if(!admin_number_afk) + send2adminirc("[selected_type] from [key_name(src)]: [original_msg] - !!No admins online!!") + else + send2adminirc("[selected_type] from [key_name(src)]: [original_msg] - !!All admins AFK ([admin_number_afk])!!") + else + send2adminirc("[selected_type] from [key_name(src)]: [original_msg]") + feedback_add_details("admin_verb","AH") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/proc/send2irc_adminless_only(source, msg, requiredflags = R_BAN) + var/admin_number_total = 0 //Total number of admins + var/admin_number_afk = 0 //Holds the number of admins who are afk + var/admin_number_ignored = 0 //Holds the number of admins without +BAN (so admins who are not really admins) + var/admin_number_decrease = 0 //Holds the number of admins with are afk, ignored or both + for(var/client/X in GLOB.admins) + admin_number_total++; + var/invalid = 0 + if(requiredflags != 0 && !check_rights_for(X, requiredflags)) + admin_number_ignored++ + invalid = 1 + if(X.is_afk()) + admin_number_afk++ + invalid = 1 + if(X.holder.fakekey) + admin_number_ignored++ + invalid = 1 + if(invalid) + admin_number_decrease++ + var/admin_number_present = admin_number_total - admin_number_decrease //Number of admins who are neither afk nor invalid + if(admin_number_present <= 0) + if(!admin_number_afk && !admin_number_ignored) + send2irc(source, "[msg] - No admins online") + else + send2irc(source, "[msg] - All admins AFK ([admin_number_afk]/[admin_number_total]) or skipped ([admin_number_ignored]/[admin_number_total])") + return admin_number_present diff --git a/code/modules/admin/verbs/adminjump.dm b/code/modules/admin/verbs/adminjump.dm index 16a1c14694f..0c1a8c5f14f 100644 --- a/code/modules/admin/verbs/adminjump.dm +++ b/code/modules/admin/verbs/adminjump.dm @@ -1,161 +1,161 @@ -/client/proc/Jump(area/A in return_sorted_areas()) - set name = "Jump to Area" - set desc = "Area to jump to" - set category = "Admin" - - if(!check_rights(R_ADMIN)) - return - - if(!A) - return - - var/list/turfs = list() - for(var/turf/T in A) - if(T.density) - continue - if(locate(/obj/structure/grille, T)) // Quick check to not spawn in windows - continue - turfs.Add(T) - - var/turf/T = pick_n_take(turfs) - if(!T) - to_chat(src, "Nowhere to jump to!") - return - - admin_forcemove(usr, T) - log_admin("[key_name(usr)] jumped to [A]") - if(!isobserver(usr)) - message_admins("[key_name_admin(usr)] jumped to [A]") - feedback_add_details("admin_verb","JA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/jumptoturf(var/turf/T in world) - set name = "Jump to Turf" - set category = null - - if(!check_rights(R_ADMIN)) - return - - log_admin("[key_name(usr)] jumped to [T.x], [T.y], [T.z] in [T.loc]") - if(!isobserver(usr)) - message_admins("[key_name_admin(usr)] jumped to [T.x], [T.y], [T.z] in [T.loc]", 1) - admin_forcemove(usr, T) - feedback_add_details("admin_verb","JT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return - -/client/proc/jumptomob(var/mob/M in GLOB.mob_list) - set category = "Admin" - set name = "Jump to Mob" - - if(!check_rights(R_ADMIN)) - return - - log_admin("[key_name(usr)] jumped to [key_name(M)]") - if(!isobserver(usr)) - message_admins("[key_name_admin(usr)] jumped to [key_name_admin(M)]", 1) - if(src.mob) - var/mob/A = src.mob - var/turf/T = get_turf(M) - if(T && isturf(T)) - feedback_add_details("admin_verb","JM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - admin_forcemove(A, M.loc) - else - to_chat(A, "This mob is not located in the game world.") - -/client/proc/jumptocoord(tx as num, ty as num, tz as num) - set category = "Admin" - set name = "Jump to Coordinate" - - if(!check_rights(R_ADMIN)) - return - - var/turf/T = locate(tx, ty, tz) - if(T) - admin_forcemove(usr, T) - if(isobserver(usr)) - var/mob/dead/observer/O = usr - O.ManualFollow(T) - feedback_add_details("admin_verb","JC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - if(!isobserver(usr)) - message_admins("[key_name_admin(usr)] jumped to coordinates [tx], [ty], [tz]") - -/client/proc/jumptokey() - set category = "Admin" - set name = "Jump to Key" - - if(!check_rights(R_ADMIN)) - return - - var/list/keys = list() - for(var/mob/M in GLOB.player_list) - keys += M.client - var/selection = input("Please, select a player!", "Admin Jumping", null, null) as null|anything in sortKey(keys) - if(!selection) - to_chat(src, "No keys found.") - return - var/mob/M = selection:mob - log_admin("[key_name(usr)] jumped to [key_name(M)]") - if(!isobserver(usr)) - message_admins("[key_name_admin(usr)] jumped to [key_name_admin(M)]", 1) - - admin_forcemove(usr, M.loc) - - feedback_add_details("admin_verb","JK") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/Getmob(var/mob/M in GLOB.mob_list) - set category = "Admin" - set name = "Get Mob" - set desc = "Mob to teleport" - - if(!check_rights(R_ADMIN)) - return - - log_admin("[key_name(usr)] teleported [key_name(M)]") - message_admins("[key_name_admin(usr)] teleported [key_name_admin(M)]", 1) - admin_forcemove(M, get_turf(usr)) - feedback_add_details("admin_verb","GM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/Getkey() - set category = "Admin" - set name = "Get Key" - set desc = "Key to teleport" - - if(!check_rights(R_ADMIN)) - return - - var/list/keys = list() - for(var/mob/M in GLOB.player_list) - keys += M.client - var/selection = input("Please, select a player!", "Admin Jumping", null, null) as null|anything in sortKey(keys) - if(!selection) - return - var/mob/M = selection:mob - - if(!M) - return - log_admin("[key_name(usr)] teleported [key_name(M)]") - message_admins("[key_name_admin(usr)] teleported [key_name(M)]", 1) - if(M) - admin_forcemove(M, get_turf(usr)) - admin_forcemove(usr, M.loc) - feedback_add_details("admin_verb","GK") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/sendmob(var/mob/M in GLOB.mob_list) - set category = "Admin" - set name = "Send Mob" - - if(!check_rights(R_ADMIN)) - return - - var/area/A = input(usr, "Pick an area.", "Pick an area") in return_sorted_areas() - if(A) - admin_forcemove(M, pick(get_area_turfs(A))) - feedback_add_details("admin_verb","SMOB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] teleported [key_name(M)] to [A]") - message_admins("[key_name_admin(usr)] teleported [key_name_admin(M)] to [A]", 1) - -/proc/admin_forcemove(mob/mover, atom/newloc) - mover.forceMove(newloc) - mover.on_forcemove(newloc) - -/mob/proc/on_forcemove(atom/newloc) - return +/client/proc/Jump(area/A in return_sorted_areas()) + set name = "Jump to Area" + set desc = "Area to jump to" + set category = "Admin" + + if(!check_rights(R_ADMIN)) + return + + if(!A) + return + + var/list/turfs = list() + for(var/turf/T in A) + if(T.density) + continue + if(locate(/obj/structure/grille, T)) // Quick check to not spawn in windows + continue + turfs.Add(T) + + var/turf/T = pick_n_take(turfs) + if(!T) + to_chat(src, "Nowhere to jump to!") + return + + admin_forcemove(usr, T) + log_admin("[key_name(usr)] jumped to [A]") + if(!isobserver(usr)) + message_admins("[key_name_admin(usr)] jumped to [A]") + feedback_add_details("admin_verb","JA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/jumptoturf(var/turf/T in world) + set name = "Jump to Turf" + set category = null + + if(!check_rights(R_ADMIN)) + return + + log_admin("[key_name(usr)] jumped to [T.x], [T.y], [T.z] in [T.loc]") + if(!isobserver(usr)) + message_admins("[key_name_admin(usr)] jumped to [T.x], [T.y], [T.z] in [T.loc]", 1) + admin_forcemove(usr, T) + feedback_add_details("admin_verb","JT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + return + +/client/proc/jumptomob(var/mob/M in GLOB.mob_list) + set category = "Admin" + set name = "Jump to Mob" + + if(!check_rights(R_ADMIN)) + return + + log_admin("[key_name(usr)] jumped to [key_name(M)]") + if(!isobserver(usr)) + message_admins("[key_name_admin(usr)] jumped to [key_name_admin(M)]", 1) + if(src.mob) + var/mob/A = src.mob + var/turf/T = get_turf(M) + if(T && isturf(T)) + feedback_add_details("admin_verb","JM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + admin_forcemove(A, M.loc) + else + to_chat(A, "This mob is not located in the game world.") + +/client/proc/jumptocoord(tx as num, ty as num, tz as num) + set category = "Admin" + set name = "Jump to Coordinate" + + if(!check_rights(R_ADMIN)) + return + + var/turf/T = locate(tx, ty, tz) + if(T) + admin_forcemove(usr, T) + if(isobserver(usr)) + var/mob/dead/observer/O = usr + O.ManualFollow(T) + feedback_add_details("admin_verb","JC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + if(!isobserver(usr)) + message_admins("[key_name_admin(usr)] jumped to coordinates [tx], [ty], [tz]") + +/client/proc/jumptokey() + set category = "Admin" + set name = "Jump to Key" + + if(!check_rights(R_ADMIN)) + return + + var/list/keys = list() + for(var/mob/M in GLOB.player_list) + keys += M.client + var/selection = input("Please, select a player!", "Admin Jumping", null, null) as null|anything in sortKey(keys) + if(!selection) + to_chat(src, "No keys found.") + return + var/mob/M = selection:mob + log_admin("[key_name(usr)] jumped to [key_name(M)]") + if(!isobserver(usr)) + message_admins("[key_name_admin(usr)] jumped to [key_name_admin(M)]", 1) + + admin_forcemove(usr, M.loc) + + feedback_add_details("admin_verb","JK") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/Getmob(var/mob/M in GLOB.mob_list) + set category = "Admin" + set name = "Get Mob" + set desc = "Mob to teleport" + + if(!check_rights(R_ADMIN)) + return + + log_admin("[key_name(usr)] teleported [key_name(M)]") + message_admins("[key_name_admin(usr)] teleported [key_name_admin(M)]", 1) + admin_forcemove(M, get_turf(usr)) + feedback_add_details("admin_verb","GM") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/Getkey() + set category = "Admin" + set name = "Get Key" + set desc = "Key to teleport" + + if(!check_rights(R_ADMIN)) + return + + var/list/keys = list() + for(var/mob/M in GLOB.player_list) + keys += M.client + var/selection = input("Please, select a player!", "Admin Jumping", null, null) as null|anything in sortKey(keys) + if(!selection) + return + var/mob/M = selection:mob + + if(!M) + return + log_admin("[key_name(usr)] teleported [key_name(M)]") + message_admins("[key_name_admin(usr)] teleported [key_name(M)]", 1) + if(M) + admin_forcemove(M, get_turf(usr)) + admin_forcemove(usr, M.loc) + feedback_add_details("admin_verb","GK") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/sendmob(var/mob/M in GLOB.mob_list) + set category = "Admin" + set name = "Send Mob" + + if(!check_rights(R_ADMIN)) + return + + var/area/A = input(usr, "Pick an area.", "Pick an area") in return_sorted_areas() + if(A) + admin_forcemove(M, pick(get_area_turfs(A))) + feedback_add_details("admin_verb","SMOB") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + log_admin("[key_name(usr)] teleported [key_name(M)] to [A]") + message_admins("[key_name_admin(usr)] teleported [key_name_admin(M)] to [A]", 1) + +/proc/admin_forcemove(mob/mover, atom/newloc) + mover.forceMove(newloc) + mover.on_forcemove(newloc) + +/mob/proc/on_forcemove(atom/newloc) + return diff --git a/code/modules/admin/verbs/adminsay.dm b/code/modules/admin/verbs/adminsay.dm index 6f6b3484924..74eeff49692 100644 --- a/code/modules/admin/verbs/adminsay.dm +++ b/code/modules/admin/verbs/adminsay.dm @@ -1,82 +1,82 @@ -/client/proc/cmd_admin_say(msg as text) - set category = "Admin" - set name = "Asay" //Gave this shit a shorter name so you only have to time out "asay" rather than "admin say" to use it --NeoFite - set hidden = 1 - if(!check_rights(R_ADMIN)) return - - msg = sanitize(copytext(msg, 1, MAX_MESSAGE_LEN)) - if(!msg) return - - log_adminsay(msg, src) - - if(check_rights(R_ADMIN,0)) - for(var/client/C in GLOB.admins) - if(R_ADMIN & C.holder.rights) - msg = "[msg]" - to_chat(C, "ADMIN: [key_name(usr, 1)] ([admin_jump_link(mob)]): [msg]") - - feedback_add_details("admin_verb","M") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/get_admin_say() - var/msg = input(src, null, "asay \"text\"") as text|null - cmd_admin_say(msg) - -/client/proc/cmd_mentor_say(msg as text) - set category = "Admin" - set name = "Msay" - set hidden = 1 - - if(!check_rights(R_ADMIN|R_MOD|R_MENTOR)) - return - - msg = sanitize(copytext(msg, 1, MAX_MESSAGE_LEN)) - log_mentorsay(msg, src) - - if(!msg) - return - - for(var/client/C in GLOB.admins) - if(check_rights(R_ADMIN|R_MOD|R_MENTOR, 0, C.mob)) - var/display_name = key - if(holder.fakekey) - if(C.holder && C.holder.rights & R_ADMIN) - display_name = "[holder.fakekey]/([key])" - else - display_name = holder.fakekey - msg = "[msg]" - to_chat(C, "MENTOR: [display_name] ([admin_jump_link(mob)]): [msg]") - - feedback_add_details("admin_verb","MS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/toggle_mentor_chat() - set category = "Server" - set name = "Toggle Mentor Chat" - set desc = "Toggle whether mentors have access to the msay command" - - if(!check_rights(R_ADMIN)) - return - - var/enabling - var/msay = /client/proc/cmd_mentor_say - - if(msay in admin_verbs_mentor) - enabling = FALSE - admin_verbs_mentor -= msay - else - enabling = TRUE - admin_verbs_mentor += msay - - for(var/client/C in GLOB.admins) - if(check_rights(R_ADMIN|R_MOD, 0, C.mob)) - continue - if(!check_rights(R_MENTOR, 0, C.mob)) - continue - if(enabling) - C.verbs += msay - to_chat(C, "Mentor chat has been enabled. Use 'msay' to speak in it.") - else - C.verbs -= msay - to_chat(C, "Mentor chat has been disabled.") - - admin_log_and_message_admins("toggled mentor chat [enabling ? "on" : "off"].") - feedback_add_details("admin_verb", "TMC") +/client/proc/cmd_admin_say(msg as text) + set category = "Admin" + set name = "Asay" //Gave this shit a shorter name so you only have to time out "asay" rather than "admin say" to use it --NeoFite + set hidden = 1 + if(!check_rights(R_ADMIN)) return + + msg = sanitize(copytext(msg, 1, MAX_MESSAGE_LEN)) + if(!msg) return + + log_adminsay(msg, src) + + if(check_rights(R_ADMIN,0)) + for(var/client/C in GLOB.admins) + if(R_ADMIN & C.holder.rights) + msg = "[msg]" + to_chat(C, "ADMIN: [key_name(usr, 1)] ([admin_jump_link(mob)]): [msg]") + + feedback_add_details("admin_verb","M") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/get_admin_say() + var/msg = input(src, null, "asay \"text\"") as text|null + cmd_admin_say(msg) + +/client/proc/cmd_mentor_say(msg as text) + set category = "Admin" + set name = "Msay" + set hidden = 1 + + if(!check_rights(R_ADMIN|R_MOD|R_MENTOR)) + return + + msg = sanitize(copytext(msg, 1, MAX_MESSAGE_LEN)) + log_mentorsay(msg, src) + + if(!msg) + return + + for(var/client/C in GLOB.admins) + if(check_rights(R_ADMIN|R_MOD|R_MENTOR, 0, C.mob)) + var/display_name = key + if(holder.fakekey) + if(C.holder && C.holder.rights & R_ADMIN) + display_name = "[holder.fakekey]/([key])" + else + display_name = holder.fakekey + msg = "[msg]" + to_chat(C, "MENTOR: [display_name] ([admin_jump_link(mob)]): [msg]") + + feedback_add_details("admin_verb","MS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/toggle_mentor_chat() + set category = "Server" + set name = "Toggle Mentor Chat" + set desc = "Toggle whether mentors have access to the msay command" + + if(!check_rights(R_ADMIN)) + return + + var/enabling + var/msay = /client/proc/cmd_mentor_say + + if(msay in admin_verbs_mentor) + enabling = FALSE + admin_verbs_mentor -= msay + else + enabling = TRUE + admin_verbs_mentor += msay + + for(var/client/C in GLOB.admins) + if(check_rights(R_ADMIN|R_MOD, 0, C.mob)) + continue + if(!check_rights(R_MENTOR, 0, C.mob)) + continue + if(enabling) + C.verbs += msay + to_chat(C, "Mentor chat has been enabled. Use 'msay' to speak in it.") + else + C.verbs -= msay + to_chat(C, "Mentor chat has been disabled.") + + admin_log_and_message_admins("toggled mentor chat [enabling ? "on" : "off"].") + feedback_add_details("admin_verb", "TMC") diff --git a/code/modules/admin/verbs/atmosdebug.dm b/code/modules/admin/verbs/atmosdebug.dm index de281d34e68..0624e4d1909 100644 --- a/code/modules/admin/verbs/atmosdebug.dm +++ b/code/modules/admin/verbs/atmosdebug.dm @@ -1,63 +1,63 @@ -/client/proc/atmosscan() - set category = "Mapping" - set name = "Check Piping" - set background = 1 - if(!src.holder) - to_chat(src, "Only administrators may use this command.") - return - feedback_add_details("admin_verb","CP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - if(alert("WARNING: This command should not be run on a live server. Do you want to continue?", "Check Piping", "No", "Yes") == "No") - return - - to_chat(usr, "Checking for disconnected pipes...") - //all plumbing - yes, some things might get stated twice, doesn't matter. - for(var/obj/machinery/atmospherics/plumbing in world) - if(plumbing.nodealert) - to_chat(usr, "Unconnected [plumbing.name] located at [plumbing.x],[plumbing.y],[plumbing.z] ([get_area(plumbing.loc)])") - - //Manifolds - for(var/obj/machinery/atmospherics/pipe/manifold/pipe in world) - if(!pipe.node1 || !pipe.node2 || !pipe.node3) - to_chat(usr, "Unconnected [pipe.name] located at [pipe.x],[pipe.y],[pipe.z] ([get_area(pipe.loc)])") - - //Pipes - for(var/obj/machinery/atmospherics/pipe/simple/pipe in world) - if(!pipe.node1 || !pipe.node2) - to_chat(usr, "Unconnected [pipe.name] located at [pipe.x],[pipe.y],[pipe.z] ([get_area(pipe.loc)])") - - to_chat(usr, "Checking for overlapping pipes...") - for(var/turf/T in world) - for(var/dir in cardinal) - var/list/check = list(0, 0, 0) - var/done = 0 - for(var/obj/machinery/atmospherics/pipe in T) - if(dir & pipe.initialize_directions) - for(var/ct in pipe.connect_types) - check[ct]++ - if(check[ct] > 1) - to_chat(usr, "Overlapping pipe ([pipe.name]) located at [T.x],[T.y],[T.z] ([get_area(T)])") - done = 1 - break - if(done) - break - to_chat(usr, "Done") - -/client/proc/powerdebug() - set category = "Mapping" - set name = "Check Power" - if(!src.holder) - to_chat(src, "Only administrators may use this command.") - return - feedback_add_details("admin_verb","CPOW") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - for(var/datum/powernet/PN in SSmachines.powernets) - if(!PN.nodes || !PN.nodes.len) - if(PN.cables && (PN.cables.len > 1)) - var/obj/structure/cable/C = PN.cables[1] - to_chat(usr, "Powernet with no nodes! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [get_area(C.loc)]") - - if(!PN.cables || (PN.cables.len < 10)) - if(PN.cables && (PN.cables.len > 1)) - var/obj/structure/cable/C = PN.cables[1] - to_chat(usr, "Powernet with fewer than 10 cables! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [get_area(C.loc)]") +/client/proc/atmosscan() + set category = "Mapping" + set name = "Check Piping" + set background = 1 + if(!src.holder) + to_chat(src, "Only administrators may use this command.") + return + feedback_add_details("admin_verb","CP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + if(alert("WARNING: This command should not be run on a live server. Do you want to continue?", "Check Piping", "No", "Yes") == "No") + return + + to_chat(usr, "Checking for disconnected pipes...") + //all plumbing - yes, some things might get stated twice, doesn't matter. + for(var/obj/machinery/atmospherics/plumbing in world) + if(plumbing.nodealert) + to_chat(usr, "Unconnected [plumbing.name] located at [plumbing.x],[plumbing.y],[plumbing.z] ([get_area(plumbing.loc)])") + + //Manifolds + for(var/obj/machinery/atmospherics/pipe/manifold/pipe in world) + if(!pipe.node1 || !pipe.node2 || !pipe.node3) + to_chat(usr, "Unconnected [pipe.name] located at [pipe.x],[pipe.y],[pipe.z] ([get_area(pipe.loc)])") + + //Pipes + for(var/obj/machinery/atmospherics/pipe/simple/pipe in world) + if(!pipe.node1 || !pipe.node2) + to_chat(usr, "Unconnected [pipe.name] located at [pipe.x],[pipe.y],[pipe.z] ([get_area(pipe.loc)])") + + to_chat(usr, "Checking for overlapping pipes...") + for(var/turf/T in world) + for(var/dir in cardinal) + var/list/check = list(0, 0, 0) + var/done = 0 + for(var/obj/machinery/atmospherics/pipe in T) + if(dir & pipe.initialize_directions) + for(var/ct in pipe.connect_types) + check[ct]++ + if(check[ct] > 1) + to_chat(usr, "Overlapping pipe ([pipe.name]) located at [T.x],[T.y],[T.z] ([get_area(T)])") + done = 1 + break + if(done) + break + to_chat(usr, "Done") + +/client/proc/powerdebug() + set category = "Mapping" + set name = "Check Power" + if(!src.holder) + to_chat(src, "Only administrators may use this command.") + return + feedback_add_details("admin_verb","CPOW") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + for(var/datum/powernet/PN in SSmachines.powernets) + if(!PN.nodes || !PN.nodes.len) + if(PN.cables && (PN.cables.len > 1)) + var/obj/structure/cable/C = PN.cables[1] + to_chat(usr, "Powernet with no nodes! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [get_area(C.loc)]") + + if(!PN.cables || (PN.cables.len < 10)) + if(PN.cables && (PN.cables.len > 1)) + var/obj/structure/cable/C = PN.cables[1] + to_chat(usr, "Powernet with fewer than 10 cables! (number [PN.number]) - example cable at [C.x], [C.y], [C.z] in area [get_area(C.loc)]") diff --git a/code/modules/admin/verbs/cinematic.dm b/code/modules/admin/verbs/cinematic.dm index 63ebd52d09b..5bfd7c65312 100644 --- a/code/modules/admin/verbs/cinematic.dm +++ b/code/modules/admin/verbs/cinematic.dm @@ -14,4 +14,4 @@ override = input(src, "mode = ?","Enter Parameter", null) as anything in list("nuclear emergency", "fake", "no override") if(0) override = input(src, "mode = ?","Enter Parameter", null) as anything in list("blob", "nuclear emergency", "AI malfunction", "no override") - SSticker.station_explosion_cinematic(parameter, override) \ No newline at end of file + SSticker.station_explosion_cinematic(parameter, override) diff --git a/code/modules/admin/verbs/deadsay.dm b/code/modules/admin/verbs/deadsay.dm index e1bbc885c75..aae13ad882d 100644 --- a/code/modules/admin/verbs/deadsay.dm +++ b/code/modules/admin/verbs/deadsay.dm @@ -1,49 +1,49 @@ -/client/proc/dsay(msg as text) - set category = "Admin" - set name = "Dsay" //Gave this shit a shorter name so you only have to time out "dsay" rather than "dead say" to use it --NeoFite - set hidden = 1 - - if(!check_rights(R_ADMIN|R_MOD)) - return - - if(!src.mob) - return - - if(prefs.muted & MUTE_DEADCHAT) - to_chat(src, "You cannot send DSAY messages (muted).") - return - - if(!(prefs.toggles & CHAT_DEAD)) - to_chat(src, "You have deadchat muted.") - return - - if(handle_spam_prevention(msg,MUTE_DEADCHAT)) - return - - var/stafftype = null - - if(check_rights(R_MENTOR, 0)) - stafftype = "MENTOR" - - if(check_rights(R_MOD, 0)) - stafftype = "MOD" - - if(check_rights(R_ADMIN, 0)) - stafftype = "ADMIN" - - msg = sanitize(copytext(msg, 1, MAX_MESSAGE_LEN)) - log_admin("[key_name(src)] : [msg]") - - if(!msg) - return - - var/prefix = "[stafftype] ([src.key])" - if(holder.fakekey) - prefix = "Administrator" - say_dead_direct("[prefix] says, \"[msg]\"") - - feedback_add_details("admin_verb","D") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/get_dead_say() - var/msg = input(src, null, "dsay \"text\"") as text - dsay(msg) \ No newline at end of file +/client/proc/dsay(msg as text) + set category = "Admin" + set name = "Dsay" //Gave this shit a shorter name so you only have to time out "dsay" rather than "dead say" to use it --NeoFite + set hidden = 1 + + if(!check_rights(R_ADMIN|R_MOD)) + return + + if(!src.mob) + return + + if(prefs.muted & MUTE_DEADCHAT) + to_chat(src, "You cannot send DSAY messages (muted).") + return + + if(!(prefs.toggles & CHAT_DEAD)) + to_chat(src, "You have deadchat muted.") + return + + if(handle_spam_prevention(msg,MUTE_DEADCHAT)) + return + + var/stafftype = null + + if(check_rights(R_MENTOR, 0)) + stafftype = "MENTOR" + + if(check_rights(R_MOD, 0)) + stafftype = "MOD" + + if(check_rights(R_ADMIN, 0)) + stafftype = "ADMIN" + + msg = sanitize(copytext(msg, 1, MAX_MESSAGE_LEN)) + log_admin("[key_name(src)] : [msg]") + + if(!msg) + return + + var/prefix = "[stafftype] ([src.key])" + if(holder.fakekey) + prefix = "Administrator" + say_dead_direct("[prefix] says, \"[msg]\"") + + feedback_add_details("admin_verb","D") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/get_dead_say() + var/msg = input(src, null, "dsay \"text\"") as text + dsay(msg) diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index 57f88d20351..ad3a0403e87 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -1,892 +1,892 @@ -/client/proc/Debug2() - set category = "Debug" - set name = "Debug-Game" - - if(!check_rights(R_DEBUG)) - return - - if(Debug2) - Debug2 = 0 - message_admins("[key_name_admin(src)] toggled debugging off.") - log_admin("[key_name(src)] toggled debugging off.") - else - Debug2 = 1 - message_admins("[key_name_admin(src)] toggled debugging on.") - log_admin("[key_name(src)] toggled debugging on.") - - feedback_add_details("admin_verb","DG2") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/* 21st Sept 2010 -Updated by Skie -- Still not perfect but better! -Stuff you can't do: -Call proc /mob/proc/Dizzy() for some player -Because if you select a player mob as owner it tries to do the proc for -/mob/living/carbon/human/ instead. And that gives a run-time error. -But you can call procs that are of type /mob/living/carbon/human/proc/ for that player. -*/ - -/client/proc/callproc() - set category = "Debug" - set name = "Advanced ProcCall" - - if(!check_rights(R_PROCCALL)) - return - - spawn(0) - var/target = null - var/targetselected = 0 - var/returnval = null - var/class = null - - switch(alert("Proc owned by something?",,"Yes","No")) - if("Yes") - targetselected = 1 - if(src.holder && src.holder.marked_datum) - class = input("Proc owned by...","Owner",null) as null|anything in list("Obj","Mob","Area or Turf","Client","Marked datum ([holder.marked_datum.type])") - if(class == "Marked datum ([holder.marked_datum.type])") - class = "Marked datum" - else - class = input("Proc owned by...","Owner",null) as null|anything in list("Obj","Mob","Area or Turf","Client") - switch(class) - if("Obj") - target = input("Enter target:","Target",usr) as obj in world - if("Mob") - target = input("Enter target:","Target",usr) as mob in world - if("Area or Turf") - target = input("Enter target:","Target",usr.loc) as area|turf in world - if("Client") - var/list/keys = list() - for(var/client/C) - keys += C - target = input("Please, select a player!", "Selection", null, null) as null|anything in keys - if("Marked datum") - target = holder.marked_datum - else - return - if("No") - target = null - targetselected = 0 - - var/procname = clean_input("Proc path, eg: /proc/fake_blood","Path:", null) - if(!procname) return - - if(targetselected && !hascall(target,procname)) - to_chat(usr, "Error: callproc(): target has no such call [procname].") - return - - var/list/lst = get_callproc_args() - if(!lst) - return - - if(targetselected) - if(!target) - to_chat(usr, "Error: callproc(): owner of proc no longer exists.") - return - message_admins("[key_name_admin(src)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") - log_admin("[key_name(src)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") - returnval = call(target,procname)(arglist(lst)) // Pass the lst as an argument list to the proc - else - //this currently has no hascall protection. wasn't able to get it working. - message_admins("[key_name_admin(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]") - log_admin("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]") - returnval = call(procname)(arglist(lst)) // Pass the lst as an argument list to the proc - - to_chat(usr, "[procname] returned: [!isnull(returnval) ? returnval : "null"]") - feedback_add_details("admin_verb","APC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -GLOBAL_VAR(AdminProcCaller) -GLOBAL_PROTECT(AdminProcCaller) - -/proc/IsAdminAdvancedProcCall() -#ifdef TESTING - return FALSE -#else - return usr && usr.client && GLOB.AdminProcCaller == usr.client.ckey -#endif - -/client/proc/callproc_datum(var/A as null|area|mob|obj|turf) - set category = "Debug" - set name = "Atom ProcCall" - - if(!check_rights(R_PROCCALL)) - return - - var/procname = clean_input("Proc name, eg: fake_blood","Proc:", null) - if(!procname) - return - - if(!hascall(A,procname)) - to_chat(usr, "Error: callproc_datum(): target has no such call [procname].") - return - - var/list/lst = get_callproc_args() - if(!lst) - return - - if(!A || !IsValidSrc(A)) - to_chat(src, "Error: callproc_datum(): owner of proc no longer exists.") - return - message_admins("[key_name_admin(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]") - log_admin("[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]") - - spawn() - var/returnval = call(A,procname)(arglist(lst)) // Pass the lst as an argument list to the proc - to_chat(src, "[procname] returned: [!isnull(returnval) ? returnval : "null"]") - - feedback_add_details("admin_verb","DPC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/get_callproc_args() - var/argnum = input("Number of arguments","Number:",0) as num|null - if(!argnum && (argnum!=0)) return - - var/list/lst = list() - //TODO: make a list to store whether each argument was initialised as null. - //Reason: So we can abort the proccall if say, one of our arguments was a mob which no longer exists - //this will protect us from a fair few errors ~Carn - - while(argnum--) - var/class = null - // Make a list with each index containing one variable, to be given to the proc - if(src.holder && src.holder.marked_datum) - class = input("What kind of variable?","Variable Type") in list("text","num","type","reference","mob reference","icon","file","client","mob's area","Marked datum ([holder.marked_datum.type])","CANCEL") - if(holder.marked_datum && class == "Marked datum ([holder.marked_datum.type])") - class = "Marked datum" - else - class = input("What kind of variable?","Variable Type") in list("text","num","type","reference","mob reference","icon","file","client","mob's area","CANCEL") - switch(class) - if("CANCEL") - return null - - if("text") - lst += clean_input("Enter new text:","Text",null) - - if("num") - lst += input("Enter new number:","Num",0) as num - - if("type") - lst += input("Enter type:","Type") in typesof(/obj,/mob,/area,/turf) - - if("reference") - lst += input("Select reference:","Reference",src) as mob|obj|turf|area in world - - if("mob reference") - lst += input("Select reference:","Reference",usr) as mob in world - - if("file") - lst += input("Pick file:","File") as file - - if("icon") - lst += input("Pick icon:","Icon") as icon - - if("client") - var/list/keys = list() - for(var/mob/M in world) - keys += M.client - lst += input("Please, select a player!", "Selection", null, null) as null|anything in keys - - if("mob's area") - var/mob/temp = input("Select mob", "Selection", usr) as mob in world - lst += temp.loc - - if("Marked datum") - lst += holder.marked_datum - return lst - -/client/proc/Cell() - set category = "Debug" - set name = "Air Status in Location" - - if(!check_rights(R_DEBUG)) - return - - if(!mob) - return - var/turf/T = mob.loc - - if(!( istype(T, /turf) )) - return - - var/datum/gas_mixture/env = T.return_air() - - var/t = "" - t+= "Nitrogen : [env.nitrogen]\n" - t+= "Oxygen : [env.oxygen]\n" - t+= "Plasma : [env.toxins]\n" - t+= "CO2: [env.carbon_dioxide]\n" - - usr.show_message(t, 1) - feedback_add_details("admin_verb","ASL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_robotize(var/mob/M in GLOB.mob_list) - set category = "Event" - set name = "Make Robot" - - if(!check_rights(R_SPAWN)) - return - - if(!SSticker) - alert("Wait until the game starts") - return - if(istype(M, /mob/living/carbon/human)) - log_admin("[key_name(src)] has robotized [M.key].") - spawn(10) - M:Robotize() - - else - alert("Invalid mob") - -/client/proc/cmd_admin_animalize(var/mob/M in GLOB.mob_list) - set category = "Event" - set name = "Make Simple Animal" - - if(!check_rights(R_SPAWN)) - return - - if(!SSticker) - alert("Wait until the game starts") - return - - if(!M) - alert("That mob doesn't seem to exist, close the panel and try again.") - return - - if(istype(M, /mob/new_player)) - alert("The mob must not be a new_player.") - return - - log_admin("[key_name(src)] has animalized [M.key].") - spawn(10) - M.Animalize() - - -/client/proc/makepAI(var/turf/T in GLOB.mob_list) - set category = "Event" - set name = "Make pAI" - set desc = "Specify a location to spawn a pAI device, then specify a key to play that pAI" - - if(!check_rights(R_SPAWN)) - return - - var/list/available = list() - for(var/mob/C in GLOB.mob_list) - if(C.key) - available.Add(C) - var/mob/choice = input("Choose a player to play the pAI", "Spawn pAI") in available - if(!choice) - return 0 - if(!istype(choice, /mob/dead/observer)) - var/confirm = input("[choice.key] isn't ghosting right now. Are you sure you want to yank [choice.p_them()] out of [choice.p_their()] body and place [choice.p_them()] in this pAI?", "Spawn pAI Confirmation", "No") in list("Yes", "No") - if(confirm != "Yes") - return 0 - var/obj/item/paicard/card = new(T) - var/mob/living/silicon/pai/pai = new(card) - var/raw_name = clean_input("Enter your pAI name:", "pAI Name", "Personal AI", choice) - var/new_name = reject_bad_name(raw_name, 1) - if(new_name) - pai.name = new_name - pai.real_name = new_name - else - to_chat(usr, "Invalid name. Your name should be at least 2 and at most [MAX_NAME_LEN] characters long. It may only contain the characters A-Z, a-z, -, ' and .") - pai.real_name = pai.name - pai.key = choice.key - card.setPersonality(pai) - for(var/datum/paiCandidate/candidate in paiController.pai_candidates) - if(candidate.key == choice.key) - paiController.pai_candidates.Remove(candidate) - feedback_add_details("admin_verb","MPAI") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_alienize(var/mob/M in GLOB.mob_list) - set category = "Event" - set name = "Make Alien" - - if(!check_rights(R_SPAWN)) - return - - if(!SSticker) - alert("Wait until the game starts") - return - if(ishuman(M)) - log_admin("[key_name(src)] has alienized [M.key].") - spawn(10) - M:Alienize() - feedback_add_details("admin_verb","MKAL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] made [key_name(M)] into an alien.") - message_admins("[key_name_admin(usr)] made [key_name(M)] into an alien.", 1) - else - alert("Invalid mob") - -/client/proc/cmd_admin_slimeize(var/mob/M in GLOB.mob_list) - set category = "Event" - set name = "Make slime" - - if(!check_rights(R_SPAWN)) - return - - if(!SSticker) - alert("Wait until the game starts") - return - if(ishuman(M)) - log_admin("[key_name(src)] has slimeized [M.key].") - spawn(10) - M:slimeize() - feedback_add_details("admin_verb","MKMET") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] made [key_name(M)] into a slime.") - message_admins("[key_name_admin(usr)] made [key_name(M)] into a slime.", 1) - else - alert("Invalid mob") - -/client/proc/cmd_admin_super(var/mob/M in GLOB.mob_list) - set category = "Event" - set name = "Make Superhero" - - if(!check_rights(R_SPAWN)) - return - - if(!SSticker) - alert("Wait until the game starts") - return - if(ishuman(M)) - var/type = input("Pick the Superhero","Superhero") as null|anything in GLOB.all_superheroes - var/datum/superheroes/S = GLOB.all_superheroes[type] - if(S) - S.create(M) - log_admin("[key_name(src)] has turned [M.key] into a Superhero.") - message_admins("[key_name_admin(usr)] made [key_name(M)] into a Superhero.", 1) - else - alert("Invalid mob") - -//TODO: merge the vievars version into this or something maybe mayhaps -/client/proc/cmd_debug_del_all() - set category = "Debug" - set name = "Del-All" - - if(!check_rights(R_DEBUG)) - return - - // to prevent REALLY stupid deletions - var/blocked = list(/mob/living, /mob/living/carbon, /mob/living/carbon/human, /mob/dead, /mob/dead/observer, /mob/living/silicon, /mob/living/silicon/robot, /mob/living/silicon/ai) - var/hsbitem = input(usr, "Choose an object to delete.", "Delete:") as null|anything in subtypesof(/obj) + subtypesof(/mob) - blocked - if(hsbitem) - for(var/atom/O in world) - if(istype(O, hsbitem)) - qdel(O) - log_admin("[key_name(src)] has deleted all instances of [hsbitem].") - message_admins("[key_name_admin(src)] has deleted all instances of [hsbitem].", 0) - feedback_add_details("admin_verb","DELA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_debug_del_sing() - set category = "Debug" - set name = "Del Singulo / Tesla" - - if(!check_rights(R_DEBUG)) - return - - //This gets a confirmation check because it's way easier to accidentally hit this and delete things than it is with del-all - var/confirm = alert("This will delete ALL Singularities and Tesla orbs except for any that are on away mission z-levels or the centcomm z-level. Are you sure you want to delete them?", "Confirm Panic Button", "Yes", "No") - if(confirm != "Yes") - return - - for(var/I in GLOB.singularities) - var/obj/singularity/S = I - if(!is_level_reachable(S.z)) - continue - qdel(S) - log_admin("[key_name(src)] has deleted all Singularities and Tesla orbs.") - message_admins("[key_name_admin(src)] has deleted all Singularities and Tesla orbs.", 0) - feedback_add_details("admin_verb","DELS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_debug_make_powernets() - set category = "Debug" - set name = "Make Powernets" - - if(!check_rights(R_DEBUG)) - return - - SSmachines.makepowernets() - log_admin("[key_name(src)] has remade the powernet. makepowernets() called.") - message_admins("[key_name_admin(src)] has remade the powernets. makepowernets() called.", 0) - feedback_add_details("admin_verb","MPWN") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/cmd_admin_grantfullaccess(var/mob/M in GLOB.mob_list) - set category = "Admin" - set name = "Grant Full Access" - - if(!check_rights(R_EVENT)) - return - - if(!SSticker) - alert("Wait until the game starts") - return - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - if(H.wear_id) - var/obj/item/card/id/id = H.wear_id - if(istype(H.wear_id, /obj/item/pda)) - var/obj/item/pda/pda = H.wear_id - id = pda.id - id.icon_state = "gold" - id:access = get_all_accesses()+get_all_centcom_access()+get_all_syndicate_access() - else - var/obj/item/card/id/id = new/obj/item/card/id(M); - id.icon_state = "gold" - id:access = get_all_accesses()+get_all_centcom_access()+get_all_syndicate_access() - id.registered_name = H.real_name - id.assignment = "Captain" - id.name = "[id.registered_name]'s ID Card ([id.assignment])" - H.equip_to_slot_or_del(id, slot_wear_id) - H.update_inv_wear_id() - else - alert("Invalid mob") - feedback_add_details("admin_verb","GFA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(src)] has granted [M.key] full access.") - message_admins("[key_name_admin(usr)] has granted [M.key] full access.", 1) - -/client/proc/cmd_assume_direct_control(var/mob/M in GLOB.mob_list) - set category = "Admin" - set name = "Assume direct control" - set desc = "Direct intervention" - - if(!check_rights(R_DEBUG|R_ADMIN)) - return - - if(M.ckey) - if(alert("This mob is being controlled by [M.ckey]. Are you sure you wish to assume control of it? [M.ckey] will be made a ghost.",,"Yes","No") != "Yes") - return - else - var/mob/dead/observer/ghost = new/mob/dead/observer(M,1) - ghost.ckey = M.ckey - message_admins("[key_name_admin(usr)] assumed direct control of [M].", 1) - log_admin("[key_name(usr)] assumed direct control of [M].") - var/mob/adminmob = src.mob - M.ckey = src.ckey - if( isobserver(adminmob) ) - qdel(adminmob) - feedback_add_details("admin_verb","ADC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - -/client/proc/cmd_admin_areatest() - set category = "Mapping" - set name = "Test areas" - - if(!check_rights(R_DEBUG)) - return - - var/list/areas_all = list() - var/list/areas_with_APC = list() - var/list/areas_with_air_alarm = list() - var/list/areas_with_RC = list() - var/list/areas_with_light = list() - var/list/areas_with_LS = list() - var/list/areas_with_intercom = list() - var/list/areas_with_camera = list() - - var/list/areas_with_multiple_APCs = list() - var/list/areas_with_multiple_air_alarms = list() - - for(var/area/A in world) - areas_all |= A.type - - for(var/obj/machinery/power/apc/APC in world) - var/area/A = get_area(APC) - if(!A) - continue - if(!(A.type in areas_with_APC)) - areas_with_APC |= A.type - else - areas_with_multiple_APCs |= A.type - - for(var/obj/machinery/alarm/alarm in world) - var/area/A = get_area(alarm) - if(!A) - continue - if(!(A.type in areas_with_air_alarm)) - areas_with_air_alarm |= A.type - else - areas_with_multiple_air_alarms |= A.type - - for(var/obj/machinery/requests_console/RC in world) - var/area/A = get_area(RC) - if(!A) - continue - areas_with_RC |= A.type - - for(var/obj/machinery/light/L in world) - var/area/A = get_area(L) - if(!A) - continue - areas_with_light |= A.type - - for(var/obj/machinery/light_switch/LS in world) - var/area/A = get_area(LS) - if(!A) - continue - areas_with_LS |= A.type - - for(var/obj/item/radio/intercom/I in world) - var/area/A = get_area(I) - if(!A) - continue - areas_with_intercom |= A.type - - for(var/obj/machinery/camera/C in world) - var/area/A = get_area(C) - if(!A) - continue - areas_with_camera |= A.type - - var/list/areas_without_APC = areas_all - areas_with_APC - var/list/areas_without_air_alarm = areas_all - areas_with_air_alarm - var/list/areas_without_RC = areas_all - areas_with_RC - var/list/areas_without_light = areas_all - areas_with_light - var/list/areas_without_LS = areas_all - areas_with_LS - var/list/areas_without_intercom = areas_all - areas_with_intercom - var/list/areas_without_camera = areas_all - areas_with_camera - - to_chat(world, "AREAS WITHOUT AN APC:") - for(var/areatype in areas_without_APC) - to_chat(world, "* [areatype]") - - to_chat(world, "AREAS WITHOUT AN AIR ALARM:") - for(var/areatype in areas_without_air_alarm) - to_chat(world, "* [areatype]") - - to_chat(world, "AREAS WITH TOO MANY APCS:") - for(var/areatype in areas_with_multiple_APCs) - to_chat(world, "* [areatype]") - - to_chat(world, "AREAS WITH TOO MANY AIR ALARMS:") - for(var/areatype in areas_with_multiple_air_alarms) - to_chat(world, "* [areatype]") - - to_chat(world, "AREAS WITHOUT A REQUEST CONSOLE:") - for(var/areatype in areas_without_RC) - to_chat(world, "* [areatype]") - - to_chat(world, "AREAS WITHOUT ANY LIGHTS:") - for(var/areatype in areas_without_light) - to_chat(world, "* [areatype]") - - to_chat(world, "AREAS WITHOUT A LIGHT SWITCH:") - for(var/areatype in areas_without_LS) - to_chat(world, "* [areatype]") - - to_chat(world, "AREAS WITHOUT ANY INTERCOMS:") - for(var/areatype in areas_without_intercom) - to_chat(world, "* [areatype]") - - to_chat(world, "AREAS WITHOUT ANY CAMERAS:") - for(var/areatype in areas_without_camera) - to_chat(world, "* [areatype]") - -/client/proc/cmd_admin_dress(var/mob/living/carbon/human/M in GLOB.mob_list) - set category = "Event" - set name = "Select equipment" - - if(!check_rights(R_EVENT)) - return - - if(!ishuman(M) && !isobserver(M)) - alert("Invalid mob") - return - - var/dresscode = robust_dress_shop() - - if(!dresscode) - return - - var/delete_pocket - var/mob/living/carbon/human/H - if(isobserver(M)) - H = M.change_mob_type(/mob/living/carbon/human, null, null, TRUE) - else - H = M - if(H.l_store || H.r_store || H.s_store) //saves a lot of time for admins and coders alike - if(alert("Should the items in their pockets be dropped? Selecting \"No\" will delete them.", "Robust quick dress shop", "Yes", "No") == "No") - delete_pocket = TRUE - - for (var/obj/item/I in H.get_equipped_items(delete_pocket)) - qdel(I) - if(dresscode != "Naked") - H.equipOutfit(dresscode) - - H.regenerate_icons() - - feedback_add_details("admin_verb", "SE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - log_admin("[key_name(usr)] changed the equipment of [key_name(M)] to [dresscode].") - message_admins("[key_name_admin(usr)] changed the equipment of [key_name_admin(M)] to [dresscode].", 1) - -/client/proc/robust_dress_shop() - var/list/outfits = list( - "Naked", - "As Job...", - "Custom..." - ) - - var/list/paths = subtypesof(/datum/outfit) - typesof(/datum/outfit/job) - for(var/path in paths) - var/datum/outfit/O = path //not much to initalize here but whatever - if(initial(O.can_be_admin_equipped)) - outfits[initial(O.name)] = path - - var/dresscode = input("Select outfit", "Robust quick dress shop") as null|anything in outfits - if(isnull(dresscode)) - return - - if(outfits[dresscode]) - dresscode = outfits[dresscode] - - if(dresscode == "As Job...") - var/list/job_paths = subtypesof(/datum/outfit/job) - var/list/job_outfits = list() - for(var/path in job_paths) - var/datum/outfit/O = path - if(initial(O.can_be_admin_equipped)) - job_outfits[initial(O.name)] = path - - dresscode = input("Select job equipment", "Robust quick dress shop") as null|anything in job_outfits - dresscode = job_outfits[dresscode] - if(isnull(dresscode)) - return - - if(dresscode == "Custom...") - var/list/custom_names = list() - for(var/datum/outfit/D in GLOB.custom_outfits) - custom_names[D.name] = D - var/selected_name = input("Select outfit", "Robust quick dress shop") as null|anything in custom_names - dresscode = custom_names[selected_name] - if(isnull(dresscode)) - return - - return dresscode - -/client/proc/startSinglo() - set category = "Debug" - set name = "Start Singularity" - set desc = "Sets up the singularity and all machines to get power flowing through the station" - - if(!check_rights(R_DEBUG)) - return - - if(alert("Are you sure? This will start up the engine. Should only be used during debug!",,"Yes","No") != "Yes") - return - - for(var/obj/machinery/power/emitter/E in GLOB.machines) - if(E.anchored) - E.active = 1 - - for(var/obj/machinery/field/generator/F in GLOB.machines) - if(F.active == 0) - F.active = 1 - F.state = 2 - F.power = 250 - F.anchored = 1 - F.warming_up = 3 - F.start_fields() - F.update_icon() - - spawn(30) - for(var/obj/machinery/the_singularitygen/G in GLOB.machines) - if(G.anchored) - var/obj/singularity/S = new /obj/singularity(get_turf(G)) - S.energy = 800 - break - - for(var/obj/machinery/power/rad_collector/Rad in GLOB.machines) - if(Rad.anchored) - if(!Rad.P) - var/obj/item/tank/plasma/Plasma = new/obj/item/tank/plasma(Rad) - Plasma.air_contents.toxins = 70 - Rad.drainratio = 0 - Rad.P = Plasma - Plasma.loc = Rad - - if(!Rad.active) - Rad.toggle_power() - - for(var/obj/machinery/power/smes/SMES in GLOB.machines) - if(SMES.anchored) - SMES.input_attempt = 1 - -/client/proc/cmd_debug_mob_lists() - set category = "Debug" - set name = "Debug Mob Lists" - set desc = "For when you just gotta know" - - if(!check_rights(R_DEBUG)) - return - - switch(input("Which list?") in list("Players","Admins","Mobs","Living Mobs","Dead Mobs","Silicons","Clients","Respawnable Mobs")) - if("Players") - to_chat(usr, jointext(GLOB.player_list,",")) - if("Admins") - to_chat(usr, jointext(GLOB.admins,",")) - if("Mobs") - to_chat(usr, jointext(GLOB.mob_list,",")) - if("Living Mobs") - to_chat(usr, jointext(GLOB.living_mob_list,",")) - if("Dead Mobs") - to_chat(usr, jointext(GLOB.dead_mob_list,",")) - if("Silicons") - to_chat(usr, jointext(GLOB.silicon_mob_list,",")) - if("Clients") - to_chat(usr, jointext(GLOB.clients,",")) - if("Respawnable Mobs") - to_chat(usr, jointext(GLOB.respawnable_list,",")) - -/client/proc/cmd_display_del_log() - set category = "Debug" - set name = "Display del() Log" - set desc = "Display del's log of everything that's passed through it." - - if(!check_rights(R_DEBUG)) - return - - var/list/dellog = list("List of things that have gone through qdel this round

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

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

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

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

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

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

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

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

        Gods rolled [dice], result is [result]

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

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

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

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

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

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

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

        Good luck, soldier!

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

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

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

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

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

        Good luck, soldier!

        " + P.name = "Spec. Ops Manual" + P.icon = "pamphlet-ds" + var/obj/item/stamp/centcom/stamp = new + P.stamp(stamp) + qdel(stamp) + + for(var/obj/effect/landmark/L in GLOB.landmarks_list) + if(L.name == "Commando-Bomb") + new /obj/effect/spawner/newbomb/timer/syndicate(L.loc) + qdel(L) + + message_admins("[key_name_admin(usr)] has spawned a CentComm DeathSquad.", 1) + log_admin("[key_name(usr)] used Spawn Death Squad.") + return 1 + +/client/proc/create_death_commando(obj/spawn_location, is_leader = FALSE) + var/mob/living/carbon/human/new_commando = new(spawn_location.loc) + var/commando_leader_rank = pick("Lieutenant", "Captain", "Major") + var/commando_rank = pick("Corporal", "Sergeant", "Staff Sergeant", "Sergeant 1st Class", "Master Sergeant", "Sergeant Major") + var/commando_name = pick(GLOB.last_names) + + var/datum/preferences/A = new()//Randomize appearance for the commando. + if(is_leader) + A.age = rand(35,45) + A.real_name = "[commando_leader_rank] [commando_name]" + else + A.real_name = "[commando_rank] [commando_name]" + A.copy_to(new_commando) + + + new_commando.dna.ready_dna(new_commando)//Creates DNA. + + //Creates mind stuff. + new_commando.mind_initialize() + new_commando.mind.assigned_role = SPECIAL_ROLE_DEATHSQUAD + new_commando.mind.special_role = SPECIAL_ROLE_DEATHSQUAD + SSticker.mode.traitors |= new_commando.mind//Adds them to current traitor list. Which is really the extra antagonist list. + new_commando.equip_death_commando(is_leader) + return new_commando + +/mob/living/carbon/human/proc/equip_death_commando(is_leader = FALSE) + + var/obj/item/radio/R = new /obj/item/radio/headset/alt(src) + R.set_frequency(DTH_FREQ) + equip_to_slot_or_del(R, slot_l_ear) + if(is_leader) + equip_to_slot_or_del(new /obj/item/clothing/under/rank/centcom_officer(src), slot_w_uniform) + else + equip_to_slot_or_del(new /obj/item/clothing/under/color/green(src), slot_w_uniform) + equip_to_slot_or_del(new /obj/item/clothing/shoes/magboots/advance(src), slot_shoes) + equip_to_slot_or_del(new /obj/item/clothing/suit/space/deathsquad(src), slot_wear_suit) + equip_to_slot_or_del(new /obj/item/clothing/gloves/combat(src), slot_gloves) + equip_to_slot_or_del(new /obj/item/clothing/head/helmet/space/deathsquad(src), slot_head) + equip_to_slot_or_del(new /obj/item/clothing/mask/gas/sechailer/swat(src), slot_wear_mask) + equip_to_slot_or_del(new /obj/item/clothing/glasses/thermal(src), slot_glasses) + + equip_to_slot_or_del(new /obj/item/storage/backpack/security(src), slot_back) + equip_to_slot_or_del(new /obj/item/storage/box(src), slot_in_backpack) + + equip_to_slot_or_del(new /obj/item/ammo_box/a357(src), slot_in_backpack) + equip_to_slot_or_del(new /obj/item/reagent_containers/hypospray/combat/nanites(src), slot_in_backpack) + equip_to_slot_or_del(new /obj/item/storage/box/flashbangs(src), slot_in_backpack) + equip_to_slot_or_del(new /obj/item/flashlight(src), slot_in_backpack) + equip_to_slot_or_del(new /obj/item/pinpointer(src), slot_in_backpack) + if(is_leader) + equip_to_slot_or_del(new /obj/item/disk/nuclear/unrestricted(src), slot_in_backpack) + else + equip_to_slot_or_del(new /obj/item/grenade/plastic/x4(src), slot_in_backpack) + + + equip_to_slot_or_del(new /obj/item/melee/energy/sword/saber(src), slot_l_store) + equip_to_slot_or_del(new /obj/item/shield/energy(src), slot_r_store) + equip_to_slot_or_del(new /obj/item/tank/emergency_oxygen/double/full(src), slot_s_store) + equip_to_slot_or_del(new /obj/item/gun/projectile/revolver/mateba(src), slot_belt) + equip_to_slot_or_del(new /obj/item/gun/energy/pulse(src), slot_r_hand) + + var/obj/item/implant/mindshield/L = new/obj/item/implant/mindshield(src) + L.implant(src) + + var/obj/item/card/id/W = new(src) + W.name = "[real_name]'s ID Card" + W.icon_state = "deathsquad" + W.assignment = "Death Commando" + W.access = get_centcom_access(W.assignment) + W.registered_name = real_name + equip_to_slot_or_del(W, slot_wear_id) + + return 1 diff --git a/code/modules/admin/verbs/striketeam_syndicate.dm b/code/modules/admin/verbs/striketeam_syndicate.dm index 611dbcfd97a..8dc2881911c 100644 --- a/code/modules/admin/verbs/striketeam_syndicate.dm +++ b/code/modules/admin/verbs/striketeam_syndicate.dm @@ -1,169 +1,169 @@ -//STRIKE TEAMS - -var/const/syndicate_commandos_possible = 6 //if more Commandos are needed in the future -var/global/sent_syndicate_strike_team = 0 -/client/proc/syndicate_strike_team() - set category = "Event" - set name = "Spawn Syndicate Strike Team" - set desc = "Spawns a squad of commandos in the Syndicate Mothership if you want to run an admin event." - if(!src.holder) - to_chat(src, "Only administrators may use this command.") - return - if(!SSticker) - alert("The game hasn't started yet!") - return - if(sent_syndicate_strike_team == 1) - alert("The Syndicate are already sending a team, Mr. Dumbass.") - return - if(alert("Do you want to send in the Syndicate Strike Team? Once enabled, this is irreversible.",,"Yes","No")=="No") - return - alert("This 'mode' will go on until everyone is dead or the station is destroyed. You may also admin-call the evac shuttle when appropriate. Spawned syndicates have internals cameras which are viewable through a monitor inside the Syndicate Mothership Bridge. Assigning the team's detailed task is recommended from there. The first one selected/spawned will be the team leader.") - - message_admins("[key_name_admin(usr)] has started to spawn a Syndicate Strike Team.", 1) - - var/input = null - while(!input) - input = sanitize(copytext(input(src, "Please specify which mission the syndicate strike team shall undertake.", "Specify Mission", ""),1,MAX_MESSAGE_LEN)) - if(!input) - if(alert("Error, no mission set. Do you want to exit the setup process?",,"Yes","No")=="Yes") - return - - if(sent_syndicate_strike_team) - to_chat(src, "Looks like someone beat you to it.") - return - - var/syndicate_commando_number = syndicate_commandos_possible //for selecting a leader - var/is_leader = TRUE // set to FALSE after leader is spawned - - // Find the nuclear auth code - var/nuke_code - var/temp_code - for(var/obj/machinery/nuclearbomb/N in world) - temp_code = text2num(N.r_code) - if(temp_code)//if it's actually a number. It won't convert any non-numericals. - nuke_code = N.r_code - break - - // Find ghosts willing to be SST - var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, syndicate_commandos_possible, "Join the Syndicate Strike Team?",, 21, 600, 1, role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE) - if(!commando_ghosts.len) - to_chat(usr, "Nobody volunteered to join the SST.") - return - - sent_syndicate_strike_team = 1 - - //Spawns commandos and equips them. - for(var/obj/effect/landmark/L in GLOB.landmarks_list) - - if(syndicate_commando_number <= 0) - break - - if(L.name == "Syndicate-Commando") - - if(!commando_ghosts.len) - break - - var/mob/ghost_mob = pick(commando_ghosts) - commando_ghosts -= ghost_mob - - if(!ghost_mob || !ghost_mob.key || !ghost_mob.client) - continue - - var/mob/living/carbon/human/new_syndicate_commando = create_syndicate_death_commando(L, is_leader) - - if(!new_syndicate_commando) - continue - - new_syndicate_commando.key = ghost_mob.key - new_syndicate_commando.internal = new_syndicate_commando.s_store - new_syndicate_commando.update_action_buttons_icon() - - //So they don't forget their code or mission. - if(nuke_code) - new_syndicate_commando.mind.store_memory("Nuke Code: [nuke_code].") - new_syndicate_commando.mind.store_memory("Mission: [input].") - - to_chat(new_syndicate_commando, "You are an Elite Syndicate [is_leader ? "TEAM LEADER" : "commando"] in the service of the Syndicate. \nYour current mission is: [input]") - new_syndicate_commando.faction += "syndicate" - var/datum/atom_hud/antag/opshud = huds[ANTAG_HUD_OPS] - opshud.join_hud(new_syndicate_commando.mind.current) - set_antag_hud(new_syndicate_commando.mind.current, "hudoperative") - new_syndicate_commando.regenerate_icons() - is_leader = FALSE - syndicate_commando_number-- - - message_admins("[key_name_admin(usr)] has spawned a Syndicate strike squad.", 1) - log_admin("[key_name(usr)] used Spawn Syndicate Squad.") - feedback_add_details("admin_verb","SDTHS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -/client/proc/create_syndicate_death_commando(obj/spawn_location, is_leader = FALSE) - var/mob/living/carbon/human/new_syndicate_commando = new(spawn_location.loc) - var/syndicate_commando_leader_rank = pick("Lieutenant", "Captain", "Major") - var/syndicate_commando_rank = pick("Corporal", "Sergeant", "Staff Sergeant", "Sergeant 1st Class", "Master Sergeant", "Sergeant Major") - var/syndicate_commando_name = pick(GLOB.last_names) - - var/datum/preferences/A = new()//Randomize appearance for the commando. - if(is_leader) - A.age = rand(35,45) - A.real_name = "[syndicate_commando_leader_rank] [syndicate_commando_name]" - else - A.real_name = "[syndicate_commando_rank] [syndicate_commando_name]" - A.copy_to(new_syndicate_commando) - - new_syndicate_commando.dna.ready_dna(new_syndicate_commando)//Creates DNA. - - //Creates mind stuff. - new_syndicate_commando.mind_initialize() - new_syndicate_commando.mind.assigned_role = SPECIAL_ROLE_SYNDICATE_DEATHSQUAD - new_syndicate_commando.mind.special_role = SPECIAL_ROLE_SYNDICATE_DEATHSQUAD - new_syndicate_commando.mind.offstation_role = TRUE - SSticker.mode.traitors |= new_syndicate_commando.mind //Adds them to current traitor list. Which is really the extra antagonist list. - new_syndicate_commando.equip_syndicate_commando(is_leader) - qdel(spawn_location) - return new_syndicate_commando - -/mob/living/carbon/human/proc/equip_syndicate_commando(is_leader = FALSE, full_gear = FALSE) - var/obj/item/radio/R = new /obj/item/radio/headset/syndicate/alt/syndteam(src) - R.set_frequency(SYNDTEAM_FREQ) - equip_to_slot_or_del(R, slot_l_ear) - equip_to_slot_or_del(new /obj/item/clothing/under/syndicate(src), slot_w_uniform) - if(!full_gear) - equip_to_slot_or_del(new /obj/item/clothing/shoes/combat(src), slot_shoes) - equip_to_slot_or_del(new /obj/item/clothing/gloves/combat(src), slot_gloves) - - equip_to_slot_or_del(new /obj/item/storage/backpack/security(src), slot_back) - equip_to_slot_or_del(new /obj/item/storage/box/survival_syndi(src), slot_in_backpack) - - equip_to_slot_or_del(new /obj/item/gun/projectile/revolver(src), slot_in_backpack) - equip_to_slot_or_del(new /obj/item/ammo_box/a357(src), slot_in_backpack) - equip_to_slot_or_del(new /obj/item/reagent_containers/hypospray/combat/nanites(src), slot_in_backpack) - equip_to_slot_or_del(new /obj/item/grenade/plastic/x4(src), slot_in_backpack) - if(is_leader) - equip_to_slot_or_del(new /obj/item/pinpointer(src), slot_in_backpack) - equip_to_slot_or_del(new /obj/item/disk/nuclear/unrestricted(src), slot_in_backpack) - else - equip_to_slot_or_del(new /obj/item/grenade/plastic/x4(src), slot_in_backpack) - equip_to_slot_or_del(new /obj/item/card/emag(src), slot_r_store) - equip_to_slot_or_del(new /obj/item/melee/energy/sword/saber/red(src), slot_l_store) - - if(full_gear) - equip_to_slot_or_del(new /obj/item/clothing/mask/gas/syndicate(src), slot_wear_mask) - equip_to_slot_or_del(new /obj/item/clothing/suit/space/hardsuit/syndi/elite/sst(src), slot_wear_suit) - equip_to_slot_or_del(new /obj/item/clothing/glasses/thermal(src), slot_glasses) - equip_to_slot_or_del(new /obj/item/storage/belt/military/sst(src), slot_belt) - equip_to_slot_or_del(new /obj/item/tank/jetpack/oxygen/harness(src), slot_s_store) - equip_to_slot_or_del(new /obj/item/clothing/shoes/magboots/syndie/advance(src), slot_shoes) - equip_to_slot_or_del(new /obj/item/gun/projectile/automatic/l6_saw(src), slot_r_hand) - equip_to_slot_or_del(new /obj/item/ammo_box/magazine/mm556x45(src), slot_in_backpack) - - var/obj/item/implant/dust/D = new /obj/item/implant/dust(src) - D.implant(src) - var/obj/item/card/id/syndicate/W = new(src) //Untrackable by AI - W.name = "[real_name]'s ID Card" - W.icon_state = "syndie" - W.assignment = "Syndicate Commando" - W.access += get_syndicate_access(W.assignment) - W.registered_name = real_name - equip_to_slot_or_del(W, slot_wear_id) - - return 1 +//STRIKE TEAMS + +#define SYNDICATE_COMMANDOS_POSSIBLE 6 //if more Commandos are needed in the future +var/global/sent_syndicate_strike_team = 0 +/client/proc/syndicate_strike_team() + set category = "Event" + set name = "Spawn Syndicate Strike Team" + set desc = "Spawns a squad of commandos in the Syndicate Mothership if you want to run an admin event." + if(!src.holder) + to_chat(src, "Only administrators may use this command.") + return + if(!SSticker) + alert("The game hasn't started yet!") + return + if(sent_syndicate_strike_team == 1) + alert("The Syndicate are already sending a team, Mr. Dumbass.") + return + if(alert("Do you want to send in the Syndicate Strike Team? Once enabled, this is irreversible.",,"Yes","No")=="No") + return + alert("This 'mode' will go on until everyone is dead or the station is destroyed. You may also admin-call the evac shuttle when appropriate. Spawned syndicates have internals cameras which are viewable through a monitor inside the Syndicate Mothership Bridge. Assigning the team's detailed task is recommended from there. The first one selected/spawned will be the team leader.") + + message_admins("[key_name_admin(usr)] has started to spawn a Syndicate Strike Team.", 1) + + var/input = null + while(!input) + input = sanitize(copytext(input(src, "Please specify which mission the syndicate strike team shall undertake.", "Specify Mission", ""),1,MAX_MESSAGE_LEN)) + if(!input) + if(alert("Error, no mission set. Do you want to exit the setup process?",,"Yes","No")=="Yes") + return + + if(sent_syndicate_strike_team) + to_chat(src, "Looks like someone beat you to it.") + return + + var/syndicate_commando_number = SYNDICATE_COMMANDOS_POSSIBLE //for selecting a leader + var/is_leader = TRUE // set to FALSE after leader is spawned + + // Find the nuclear auth code + var/nuke_code + var/temp_code + for(var/obj/machinery/nuclearbomb/N in world) + temp_code = text2num(N.r_code) + if(temp_code)//if it's actually a number. It won't convert any non-numericals. + nuke_code = N.r_code + break + + // Find ghosts willing to be SST + var/list/commando_ghosts = pollCandidatesWithVeto(src, usr, SYNDICATE_COMMANDOS_POSSIBLE, "Join the Syndicate Strike Team?",, 21, 600, 1, role_playtime_requirements[ROLE_DEATHSQUAD], TRUE, FALSE) + if(!commando_ghosts.len) + to_chat(usr, "Nobody volunteered to join the SST.") + return + + sent_syndicate_strike_team = 1 + + //Spawns commandos and equips them. + for(var/obj/effect/landmark/L in GLOB.landmarks_list) + + if(syndicate_commando_number <= 0) + break + + if(L.name == "Syndicate-Commando") + + if(!commando_ghosts.len) + break + + var/mob/ghost_mob = pick(commando_ghosts) + commando_ghosts -= ghost_mob + + if(!ghost_mob || !ghost_mob.key || !ghost_mob.client) + continue + + var/mob/living/carbon/human/new_syndicate_commando = create_syndicate_death_commando(L, is_leader) + + if(!new_syndicate_commando) + continue + + new_syndicate_commando.key = ghost_mob.key + new_syndicate_commando.internal = new_syndicate_commando.s_store + new_syndicate_commando.update_action_buttons_icon() + + //So they don't forget their code or mission. + if(nuke_code) + new_syndicate_commando.mind.store_memory("Nuke Code: [nuke_code].") + new_syndicate_commando.mind.store_memory("Mission: [input].") + + to_chat(new_syndicate_commando, "You are an Elite Syndicate [is_leader ? "TEAM LEADER" : "commando"] in the service of the Syndicate. \nYour current mission is: [input]") + new_syndicate_commando.faction += "syndicate" + var/datum/atom_hud/antag/opshud = huds[ANTAG_HUD_OPS] + opshud.join_hud(new_syndicate_commando.mind.current) + set_antag_hud(new_syndicate_commando.mind.current, "hudoperative") + new_syndicate_commando.regenerate_icons() + is_leader = FALSE + syndicate_commando_number-- + + message_admins("[key_name_admin(usr)] has spawned a Syndicate strike squad.", 1) + log_admin("[key_name(usr)] used Spawn Syndicate Squad.") + feedback_add_details("admin_verb","SDTHS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +/client/proc/create_syndicate_death_commando(obj/spawn_location, is_leader = FALSE) + var/mob/living/carbon/human/new_syndicate_commando = new(spawn_location.loc) + var/syndicate_commando_leader_rank = pick("Lieutenant", "Captain", "Major") + var/syndicate_commando_rank = pick("Corporal", "Sergeant", "Staff Sergeant", "Sergeant 1st Class", "Master Sergeant", "Sergeant Major") + var/syndicate_commando_name = pick(GLOB.last_names) + + var/datum/preferences/A = new()//Randomize appearance for the commando. + if(is_leader) + A.age = rand(35,45) + A.real_name = "[syndicate_commando_leader_rank] [syndicate_commando_name]" + else + A.real_name = "[syndicate_commando_rank] [syndicate_commando_name]" + A.copy_to(new_syndicate_commando) + + new_syndicate_commando.dna.ready_dna(new_syndicate_commando)//Creates DNA. + + //Creates mind stuff. + new_syndicate_commando.mind_initialize() + new_syndicate_commando.mind.assigned_role = SPECIAL_ROLE_SYNDICATE_DEATHSQUAD + new_syndicate_commando.mind.special_role = SPECIAL_ROLE_SYNDICATE_DEATHSQUAD + new_syndicate_commando.mind.offstation_role = TRUE + SSticker.mode.traitors |= new_syndicate_commando.mind //Adds them to current traitor list. Which is really the extra antagonist list. + new_syndicate_commando.equip_syndicate_commando(is_leader) + qdel(spawn_location) + return new_syndicate_commando + +/mob/living/carbon/human/proc/equip_syndicate_commando(is_leader = FALSE, full_gear = FALSE) + var/obj/item/radio/R = new /obj/item/radio/headset/syndicate/alt/syndteam(src) + R.set_frequency(SYNDTEAM_FREQ) + equip_to_slot_or_del(R, slot_l_ear) + equip_to_slot_or_del(new /obj/item/clothing/under/syndicate(src), slot_w_uniform) + if(!full_gear) + equip_to_slot_or_del(new /obj/item/clothing/shoes/combat(src), slot_shoes) + equip_to_slot_or_del(new /obj/item/clothing/gloves/combat(src), slot_gloves) + + equip_to_slot_or_del(new /obj/item/storage/backpack/security(src), slot_back) + equip_to_slot_or_del(new /obj/item/storage/box/survival_syndi(src), slot_in_backpack) + + equip_to_slot_or_del(new /obj/item/gun/projectile/revolver(src), slot_in_backpack) + equip_to_slot_or_del(new /obj/item/ammo_box/a357(src), slot_in_backpack) + equip_to_slot_or_del(new /obj/item/reagent_containers/hypospray/combat/nanites(src), slot_in_backpack) + equip_to_slot_or_del(new /obj/item/grenade/plastic/x4(src), slot_in_backpack) + if(is_leader) + equip_to_slot_or_del(new /obj/item/pinpointer(src), slot_in_backpack) + equip_to_slot_or_del(new /obj/item/disk/nuclear/unrestricted(src), slot_in_backpack) + else + equip_to_slot_or_del(new /obj/item/grenade/plastic/x4(src), slot_in_backpack) + equip_to_slot_or_del(new /obj/item/card/emag(src), slot_r_store) + equip_to_slot_or_del(new /obj/item/melee/energy/sword/saber/red(src), slot_l_store) + + if(full_gear) + equip_to_slot_or_del(new /obj/item/clothing/mask/gas/syndicate(src), slot_wear_mask) + equip_to_slot_or_del(new /obj/item/clothing/suit/space/hardsuit/syndi/elite/sst(src), slot_wear_suit) + equip_to_slot_or_del(new /obj/item/clothing/glasses/thermal(src), slot_glasses) + equip_to_slot_or_del(new /obj/item/storage/belt/military/sst(src), slot_belt) + equip_to_slot_or_del(new /obj/item/tank/jetpack/oxygen/harness(src), slot_s_store) + equip_to_slot_or_del(new /obj/item/clothing/shoes/magboots/syndie/advance(src), slot_shoes) + equip_to_slot_or_del(new /obj/item/gun/projectile/automatic/l6_saw(src), slot_r_hand) + equip_to_slot_or_del(new /obj/item/ammo_box/magazine/mm556x45(src), slot_in_backpack) + + var/obj/item/implant/dust/D = new /obj/item/implant/dust(src) + D.implant(src) + var/obj/item/card/id/syndicate/W = new(src) //Untrackable by AI + W.name = "[real_name]'s ID Card" + W.icon_state = "syndie" + W.assignment = "Syndicate Commando" + W.access += get_syndicate_access(W.assignment) + W.registered_name = real_name + equip_to_slot_or_del(W, slot_wear_id) + + return 1 diff --git a/code/modules/admin/verbs/ticklag.dm b/code/modules/admin/verbs/ticklag.dm index 316adf935b0..e19291c159e 100644 --- a/code/modules/admin/verbs/ticklag.dm +++ b/code/modules/admin/verbs/ticklag.dm @@ -1,21 +1,21 @@ -//Merged Doohl's and the existing ticklag as they both had good elements about them ~Carn - -/client/proc/ticklag() - set category = "Debug" - set name = "Set Ticklag" - set desc = "Sets a new tick lag. Recommend you don't mess with this too much! Stable, time-tested ticklag value is 0.9" - - if(!check_rights(R_DEBUG)) return - - var/newtick = input("Sets a new tick lag. Please don't mess with this too much! The stable, time-tested ticklag value is 0.9","Lag of Tick", world.tick_lag) as num|null - //I've used ticks of 2 before to help with serious singulo lags - if(newtick && newtick <= 2 && newtick > 0) - log_admin("[key_name(src)] has modified world.tick_lag to [newtick]", 0) - message_admins("[key_name_admin(src)] has modified world.tick_lag to [newtick]", 0) - world.tick_lag = newtick - feedback_add_details("admin_verb","TICKLAG") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - - else - to_chat(src, "Error: ticklag(): Invalid world.ticklag value. No changes made.") - - +//Merged Doohl's and the existing ticklag as they both had good elements about them ~Carn + +/client/proc/ticklag() + set category = "Debug" + set name = "Set Ticklag" + set desc = "Sets a new tick lag. Recommend you don't mess with this too much! Stable, time-tested ticklag value is 0.9" + + if(!check_rights(R_DEBUG)) return + + var/newtick = input("Sets a new tick lag. Please don't mess with this too much! The stable, time-tested ticklag value is 0.9","Lag of Tick", world.tick_lag) as num|null + //I've used ticks of 2 before to help with serious singulo lags + if(newtick && newtick <= 2 && newtick > 0) + log_admin("[key_name(src)] has modified world.tick_lag to [newtick]", 0) + message_admins("[key_name_admin(src)] has modified world.tick_lag to [newtick]", 0) + world.tick_lag = newtick + feedback_add_details("admin_verb","TICKLAG") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + + else + to_chat(src, "Error: ticklag(): Invalid world.ticklag value. No changes made.") + + diff --git a/code/modules/admin/verbs/tripAI.dm b/code/modules/admin/verbs/tripAI.dm index 98a4bf82e98..5eedd9f2811 100644 --- a/code/modules/admin/verbs/tripAI.dm +++ b/code/modules/admin/verbs/tripAI.dm @@ -1,22 +1,22 @@ -/client/proc/triple_ai() - set category = "Event" - set name = "Create AI Triumvirate" - - if(SSticker.current_state > GAME_STATE_PREGAME) - to_chat(usr, "This option is currently only usable during pregame. This may change at a later date.") - return - - if(SSjobs && SSticker) - var/datum/job/job = SSjobs.GetJob("AI") - if(!job) - to_chat(usr, "Unable to locate the AI job") - return - if(SSticker.triai) - SSticker.triai = 0 - to_chat(usr, "Only one AI will be spawned at round start.") - message_admins("[key_name_admin(usr)] has toggled off triple AIs at round start.", 1) - else - SSticker.triai = 1 - to_chat(usr, "There will be an AI Triumvirate at round start.") - message_admins("[key_name_admin(usr)] has toggled on triple AIs at round start.", 1) - return +/client/proc/triple_ai() + set category = "Event" + set name = "Create AI Triumvirate" + + if(SSticker.current_state > GAME_STATE_PREGAME) + to_chat(usr, "This option is currently only usable during pregame. This may change at a later date.") + return + + if(SSjobs && SSticker) + var/datum/job/job = SSjobs.GetJob("AI") + if(!job) + to_chat(usr, "Unable to locate the AI job") + return + if(SSticker.triai) + SSticker.triai = 0 + to_chat(usr, "Only one AI will be spawned at round start.") + message_admins("[key_name_admin(usr)] has toggled off triple AIs at round start.", 1) + else + SSticker.triai = 1 + to_chat(usr, "There will be an AI Triumvirate at round start.") + message_admins("[key_name_admin(usr)] has toggled on triple AIs at round start.", 1) + return diff --git a/code/modules/alarm/camera_alarm.dm b/code/modules/alarm/camera_alarm.dm index 6d8f151f0b8..bef53ad466f 100644 --- a/code/modules/alarm/camera_alarm.dm +++ b/code/modules/alarm/camera_alarm.dm @@ -1,2 +1,2 @@ /datum/alarm_handler/camera - category = "Camera Alarms" \ No newline at end of file + category = "Camera Alarms" diff --git a/code/modules/alarm/motion_alarm.dm b/code/modules/alarm/motion_alarm.dm index 0acbbe5f221..fd7e6febe48 100644 --- a/code/modules/alarm/motion_alarm.dm +++ b/code/modules/alarm/motion_alarm.dm @@ -1,2 +1,2 @@ /datum/alarm_handler/motion - category = "Motion Alarms" \ No newline at end of file + category = "Motion Alarms" diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm index 39053fa79d9..4a6fe7d6640 100644 --- a/code/modules/antagonists/_common/antag_datum.dm +++ b/code/modules/antagonists/_common/antag_datum.dm @@ -134,4 +134,4 @@ GLOBAL_LIST_EMPTY(antagonists) //Displayed at the end of roundend_category section /datum/antagonist/proc/roundend_report_footer() - return \ No newline at end of file + return diff --git a/code/modules/antagonists/_common/antag_helpers.dm b/code/modules/antagonists/_common/antag_helpers.dm index 134ba1d7d90..af00cbf3190 100644 --- a/code/modules/antagonists/_common/antag_helpers.dm +++ b/code/modules/antagonists/_common/antag_helpers.dm @@ -16,4 +16,4 @@ continue var/datum/team/T = A.get_team() if(!team_type || istype(T, team_type)) - . |= T \ No newline at end of file + . |= T diff --git a/code/modules/antagonists/_common/antag_hud.dm b/code/modules/antagonists/_common/antag_hud.dm index 89ad9af15ab..178f84387ac 100644 --- a/code/modules/antagonists/_common/antag_hud.dm +++ b/code/modules/antagonists/_common/antag_hud.dm @@ -77,4 +77,4 @@ /datum/mindslaves/proc/leave_serv_hud(datum/mind/free_mind) thrallhud.leave_hud(free_mind.current) - set_antag_hud(free_mind.current, null) \ No newline at end of file + set_antag_hud(free_mind.current, null) diff --git a/code/modules/antagonists/survivalist/survivalist.dm b/code/modules/antagonists/survivalist/survivalist.dm index 7d08e18834c..1d30a6fe334 100644 --- a/code/modules/antagonists/survivalist/survivalist.dm +++ b/code/modules/antagonists/survivalist/survivalist.dm @@ -38,4 +38,4 @@ var/datum/objective/steal_five_of_type/summon_magic/magic = new magic.owner = owner objectives += magic - ..() \ No newline at end of file + ..() diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm index 9e6f1b79bfa..3cd7998edd8 100644 --- a/code/modules/antagonists/traitor/datum_traitor.dm +++ b/code/modules/antagonists/traitor/datum_traitor.dm @@ -152,8 +152,7 @@ var/objective_count = 0 var/try_again = TRUE - if(prob(30)) - objective_count += forge_single_objective() + objective_count += forge_single_objective() for(var/i = objective_count, i < config.traitor_objectives_amount) var/datum/objective/assassinate/kill_objective = new @@ -236,28 +235,9 @@ /datum/antagonist/traitor/proc/forge_single_AI_objective() . = 1 - var/special_pick = rand(1,2) - switch(special_pick) - if(1) // AI hijack - var/datum/objective/block/block_objective = new - block_objective.owner = owner - add_objective(block_objective) - if(2) // Protect and strand a target - var/datum/objective/protect/yandere_one = new - yandere_one.owner = owner - yandere_one.find_target() - - if("[yandere_one.target]" in assigned_targets) - return 0 - else if(yandere_one.target) - assigned_targets.Add("[yandere_one.target]") - - add_objective(yandere_one) - var/datum/objective/maroon/yandere_two = new - yandere_two.owner = owner - yandere_two.target = yandere_one.target - yandere_two.explanation_text = "Prevent [yandere_one.target], the [yandere_one.target.assigned_role] from escaping alive." - add_objective(yandere_two) + var/datum/objective/block/block_objective = new + block_objective.owner = owner + add_objective(block_objective) /datum/antagonist/traitor/greet() diff --git a/code/modules/antagonists/wishgranter/wishgranter.dm b/code/modules/antagonists/wishgranter/wishgranter.dm index 1697eb897c9..b5414df33bf 100644 --- a/code/modules/antagonists/wishgranter/wishgranter.dm +++ b/code/modules/antagonists/wishgranter/wishgranter.dm @@ -75,4 +75,4 @@ H.mutations.Add(LASER) H.update_mutations() - H.update_body() \ No newline at end of file + H.update_body() diff --git a/code/modules/arcade/arcade_base.dm b/code/modules/arcade/arcade_base.dm index 80d6cad031a..df007b83ad3 100644 --- a/code/modules/arcade/arcade_base.dm +++ b/code/modules/arcade/arcade_base.dm @@ -78,7 +78,7 @@ tokens += 1 return if(panel_open && component_parts && istype(O, /obj/item/crowbar)) - default_deconstruction_crowbar(O) + default_deconstruction_crowbar(user, O) return return ..() diff --git a/code/modules/arcade/arcade_prize.dm b/code/modules/arcade/arcade_prize.dm index a91e392e125..47b0ac97bf8 100644 --- a/code/modules/arcade/arcade_prize.dm +++ b/code/modules/arcade/arcade_prize.dm @@ -85,4 +85,4 @@ if(25 to 74) icon_state = "tickets_3" // Buncha tickets else - icon_state = "tickets_4" // Ticket snake \ No newline at end of file + icon_state = "tickets_4" // Ticket snake diff --git a/code/modules/arcade/claw_game.dm b/code/modules/arcade/claw_game.dm index 7339b31dc77..904adaffeb5 100644 --- a/code/modules/arcade/claw_game.dm +++ b/code/modules/arcade/claw_game.dm @@ -76,4 +76,4 @@ if(!isnull(prize_won)) close_game() if(prize_won == "1") - win() \ No newline at end of file + win() diff --git a/code/modules/arcade/mob_hunt/battle_computer.dm b/code/modules/arcade/mob_hunt/battle_computer.dm index 0d6e91b60d9..94337a5cfac 100644 --- a/code/modules/arcade/mob_hunt/battle_computer.dm +++ b/code/modules/arcade/mob_hunt/battle_computer.dm @@ -302,4 +302,4 @@ to_chat(user, "This mob is already at maximum health!") return patient.mob_data.cur_health = patient.mob_data.max_health - to_chat(user, "[patient.mob_data.nickname ? patient.mob_data.nickname : patient.mob_data.mob_name] has been restored to full health!") \ No newline at end of file + to_chat(user, "[patient.mob_data.nickname ? patient.mob_data.nickname : patient.mob_data.mob_name] has been restored to full health!") diff --git a/code/modules/arcade/mob_hunt/mob_cards.dm b/code/modules/arcade/mob_hunt/mob_cards.dm index 8623b25d0f1..0fbd236dee6 100644 --- a/code/modules/arcade/mob_hunt/mob_cards.dm +++ b/code/modules/arcade/mob_hunt/mob_cards.dm @@ -27,6 +27,7 @@ desc = "A random Nano-Mob Trading Card from a Booster Pack. Wonder what it is?" /obj/item/nanomob_card/booster/New() + ..() var/datum/mob_hunt/mob_info = pick(subtypesof(/datum/mob_hunt)) mob_data = new mob_info(0,null,1) update_info() diff --git a/code/modules/arcade/mob_hunt/mob_datums.dm b/code/modules/arcade/mob_hunt/mob_datums.dm index dad3f528796..d9fc1220a79 100644 --- a/code/modules/arcade/mob_hunt/mob_datums.dm +++ b/code/modules/arcade/mob_hunt/mob_datums.dm @@ -498,4 +498,4 @@ turf_blacklist = list() area_whitelist = list() turf_whitelist = list() - lifetime = 2000 \ No newline at end of file + lifetime = 2000 diff --git a/code/modules/arcade/mob_hunt/mob_type_datums.dm b/code/modules/arcade/mob_hunt/mob_type_datums.dm index 9d27a844ea8..0ae84b1b764 100644 --- a/code/modules/arcade/mob_hunt/mob_type_datums.dm +++ b/code/modules/arcade/mob_hunt/mob_type_datums.dm @@ -253,4 +253,4 @@ TYPE_FLYING, TYPE_BLUESPACE, TYPE_STEEL) - immunity = list(TYPE_POISON) \ No newline at end of file + immunity = list(TYPE_POISON) diff --git a/code/modules/arcade/page.css b/code/modules/arcade/page.css index 17e77bcb038..e3dfcac1cce 100644 --- a/code/modules/arcade/page.css +++ b/code/modules/arcade/page.css @@ -89,4 +89,4 @@ body { .button { cursor: pointer; -} \ No newline at end of file +} diff --git a/code/modules/arcade/prize_counter.dm b/code/modules/arcade/prize_counter.dm index a48b1011adc..4d1d6973909 100644 --- a/code/modules/arcade/prize_counter.dm +++ b/code/modules/arcade/prize_counter.dm @@ -52,7 +52,7 @@ if(component_parts && istype(O, /obj/item/crowbar)) if(tickets) //save the tickets! print_tickets() - default_deconstruction_crowbar(O) + default_deconstruction_crowbar(user, O) return return ..() diff --git a/code/modules/assembly/assembly.dm b/code/modules/assembly/assembly.dm index d94e3243013..827e7893396 100644 --- a/code/modules/assembly/assembly.dm +++ b/code/modules/assembly/assembly.dm @@ -1,146 +1,150 @@ -/obj/item/assembly - name = "assembly" - desc = "A small electronic device that should never exist." - icon = 'icons/obj/assemblies/new_assemblies.dmi' - icon_state = "" - flags = CONDUCT - w_class = WEIGHT_CLASS_SMALL - materials = list(MAT_METAL = 100) - throwforce = 2 - throw_speed = 3 - throw_range = 10 - origin_tech = "magnets=1;engineering=1" - toolspeed = 1 - usesound = 'sound/items/deconstruct.ogg' - - var/bomb_name = "bomb" // used for naming bombs / mines - - var/secured = TRUE - var/list/attached_overlays = null - var/obj/item/assembly_holder/holder = null - var/cooldown = FALSE //To prevent spam - var/wires = WIRE_RECEIVE | WIRE_PULSE - var/datum/wires/connected = null // currently only used by timer/signaler - - var/const/WIRE_RECEIVE = 1 //Allows Pulsed(0) to call Activate() - var/const/WIRE_PULSE = 2 //Allows Pulse(0) to act on the holder - var/const/WIRE_PULSE_SPECIAL = 4 //Allows Pulse(0) to act on the holders special assembly - var/const/WIRE_RADIO_RECEIVE = 8 //Allows Pulsed(1) to call Activate() - var/const/WIRE_RADIO_PULSE = 16 //Allows Pulse(1) to send a radio message - -/obj/item/assembly/proc/activate() //What the device does when turned on - return - -/obj/item/assembly/proc/pulsed(radio = FALSE) //Called when another assembly acts on this one, var/radio will determine where it came from for wire calcs - return - -/obj/item/assembly/proc/pulse(radio = FALSE) //Called when this device attempts to act on another device, var/radio determines if it was sent via radio or direct - return - -/obj/item/assembly/proc/toggle_secure() //Code that has to happen when the assembly is un\secured goes here - return - -/obj/item/assembly/proc/attach_assembly(obj/A, mob/user) //Called when an assembly is attacked by another - return - -/obj/item/assembly/proc/process_cooldown() //Called via spawn(10) to have it count down the cooldown var - return - -/obj/item/assembly/proc/holder_movement() //Called when the holder is moved - return - -/obj/item/assembly/proc/describe() // Called by grenades to describe the state of the trigger (time left, etc) - return "The trigger assembly looks broken!" - -/obj/item/assembly/interact(mob/user) //Called when attack_self is called - return - -/obj/item/assembly/process_cooldown() - cooldown-- - if(cooldown <= 0) - return FALSE - spawn(10) - process_cooldown() - return TRUE - -/obj/item/assembly/Destroy() - if(istype(loc, /obj/item/assembly_holder) || istype(holder)) - var/obj/item/assembly_holder/A = loc - if(A.a_left == src) - A.a_left = null - else if(A.a_right == src) - A.a_right = null - holder = null - return ..() - -/obj/item/assembly/pulsed(radio = FALSE) - if(holder && (wires & WIRE_RECEIVE)) - activate() - if(radio && (wires & WIRE_RADIO_RECEIVE)) - activate() - return TRUE - -/obj/item/assembly/pulse(radio = FALSE) - if(holder && (wires & WIRE_PULSE)) - holder.process_activation(src, 1, 0) - if(holder && (wires & WIRE_PULSE_SPECIAL)) - holder.process_activation(src, 0, 1) - if(istype(loc, /obj/item/grenade)) // This is a hack. Todo: Manage this better -Sayu - var/obj/item/grenade/G = loc - G.prime() // Adios, muchachos - return TRUE - -/obj/item/assembly/activate() - if(!secured || cooldown > 0) - return FALSE - cooldown = 2 - spawn(10) - process_cooldown() - return TRUE - -/obj/item/assembly/toggle_secure() - secured = !secured - update_icon() - return secured - -/obj/item/assembly/attach_assembly(obj/item/assembly/A, mob/user) - holder = new /obj/item/assembly_holder(get_turf(src)) - if(holder.attach(A, src, user)) - to_chat(user, "You attach [A] to [src]!") - return TRUE - return FALSE - -/obj/item/assembly/attackby(obj/item/W, mob/user, params) - if(isassembly(W)) - var/obj/item/assembly/A = W - if(!A.secured && !secured) - attach_assembly(A, user) - return - if(isscrewdriver(W)) - if(toggle_secure()) - to_chat(user, "[src] is ready!") - else - to_chat(user, "[src] can now be attached!") - return - return ..() - -/obj/item/assembly/process() - STOP_PROCESSING(SSobj, src) - -/obj/item/assembly/examine(mob/user) - . = ..() - if(in_range(src, user) || loc == user) - if(secured) - . += "[src] is ready!" - else - . += "[src] can be attached!" - -/obj/item/assembly/attack_self(mob/user) - if(!user) - return - user.set_machine(src) - interact(user) - return TRUE - -/obj/item/assembly/interact(mob/user) - return \ No newline at end of file +/obj/item/assembly + name = "assembly" + desc = "A small electronic device that should never exist." + icon = 'icons/obj/assemblies/new_assemblies.dmi' + icon_state = "" + flags = CONDUCT + w_class = WEIGHT_CLASS_SMALL + materials = list(MAT_METAL = 100) + throwforce = 2 + throw_speed = 3 + throw_range = 10 + origin_tech = "magnets=1;engineering=1" + toolspeed = 1 + usesound = 'sound/items/deconstruct.ogg' + + var/bomb_name = "bomb" // used for naming bombs / mines + + var/secured = TRUE + var/list/attached_overlays = null + var/obj/item/assembly_holder/holder = null + var/cooldown = FALSE //To prevent spam + var/wires = WIRE_RECEIVE | WIRE_PULSE + var/datum/wires/connected = null // currently only used by timer/signaler + + var/const/WIRE_RECEIVE = 1 //Allows Pulsed(0) to call Activate() + var/const/WIRE_PULSE = 2 //Allows Pulse(0) to act on the holder + var/const/WIRE_PULSE_SPECIAL = 4 //Allows Pulse(0) to act on the holders special assembly + var/const/WIRE_RADIO_RECEIVE = 8 //Allows Pulsed(1) to call Activate() + var/const/WIRE_RADIO_PULSE = 16 //Allows Pulse(1) to send a radio message + +/obj/item/assembly/proc/activate() //What the device does when turned on + return + +/obj/item/assembly/proc/pulsed(radio = FALSE) //Called when another assembly acts on this one, var/radio will determine where it came from for wire calcs + return + +/obj/item/assembly/proc/pulse(radio = FALSE) //Called when this device attempts to act on another device, var/radio determines if it was sent via radio or direct + return + +/obj/item/assembly/proc/toggle_secure() //Code that has to happen when the assembly is un\secured goes here + return + +/obj/item/assembly/proc/attach_assembly(obj/A, mob/user) //Called when an assembly is attacked by another + return + +/obj/item/assembly/proc/process_cooldown() //Called via spawn(10) to have it count down the cooldown var + return + +/obj/item/assembly/proc/holder_movement() //Called when the holder is moved + return + +/obj/item/assembly/proc/describe() // Called by grenades to describe the state of the trigger (time left, etc) + return "The trigger assembly looks broken!" + +/obj/item/assembly/interact(mob/user) //Called when attack_self is called + return + +/obj/item/assembly/process_cooldown() + cooldown-- + if(cooldown <= 0) + return FALSE + spawn(10) + process_cooldown() + return TRUE + +/obj/item/assembly/Destroy() + if(istype(loc, /obj/item/assembly_holder) || istype(holder)) + var/obj/item/assembly_holder/A = loc + if(A.a_left == src) + A.a_left = null + else if(A.a_right == src) + A.a_right = null + holder = null + return ..() + +/obj/item/assembly/pulsed(radio = FALSE) + if(holder && (wires & WIRE_RECEIVE)) + activate() + if(radio && (wires & WIRE_RADIO_RECEIVE)) + activate() + return TRUE + +/obj/item/assembly/pulse(radio = FALSE) + if(holder && (wires & WIRE_PULSE)) + holder.process_activation(src, 1, 0) + if(holder && (wires & WIRE_PULSE_SPECIAL)) + holder.process_activation(src, 0, 1) + if(istype(loc, /obj/item/grenade)) // This is a hack. Todo: Manage this better -Sayu + var/obj/item/grenade/G = loc + G.prime() // Adios, muchachos + return TRUE + +/obj/item/assembly/activate() + if(!secured || cooldown > 0) + return FALSE + cooldown = 2 + spawn(10) + process_cooldown() + return TRUE + +/obj/item/assembly/toggle_secure() + secured = !secured + update_icon() + return secured + +/obj/item/assembly/attach_assembly(obj/item/assembly/A, mob/user) + holder = new /obj/item/assembly_holder(get_turf(src)) + if(holder.attach(A, src, user)) + to_chat(user, "You attach [A] to [src]!") + return TRUE + return FALSE + +/obj/item/assembly/attackby(obj/item/W, mob/user, params) + if(isassembly(W)) + var/obj/item/assembly/A = W + if(!A.secured && !secured) + attach_assembly(A, user) + return + + return ..() + +/obj/item/assembly/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(toggle_secure()) + to_chat(user, "[src] is ready!") + else + to_chat(user, "[src] can now be attached!") + +/obj/item/assembly/process() + STOP_PROCESSING(SSobj, src) + +/obj/item/assembly/examine(mob/user) + . = ..() + if(in_range(src, user) || loc == user) + if(secured) + . += "[src] is ready!" + else + . += "[src] can be attached!" + +/obj/item/assembly/attack_self(mob/user) + if(!user) + return + user.set_machine(src) + interact(user) + return TRUE + +/obj/item/assembly/interact(mob/user) + return diff --git a/code/modules/assembly/bomb.dm b/code/modules/assembly/bomb.dm index e0454d5f5ea..bf62e2a828a 100644 --- a/code/modules/assembly/bomb.dm +++ b/code/modules/assembly/bomb.dm @@ -1,177 +1,180 @@ -/obj/item/onetankbomb - name = "bomb" - icon = 'icons/obj/tank.dmi' - item_state = "assembly" - throwforce = 5 - w_class = WEIGHT_CLASS_NORMAL - throw_speed = 2 - throw_range = 4 - flags = CONDUCT //Copied this from old code, so this may or may not be necessary - var/status = 0 //0 - not readied //1 - bomb finished with welder - var/obj/item/assembly_holder/bombassembly = null //The first part of the bomb is an assembly holder, holding an igniter+some device - var/obj/item/tank/bombtank = null //the second part of the bomb is a plasma tank - origin_tech = "materials=1;engineering=1" - -/obj/item/onetankbomb/examine(mob/user) - . = ..() - . += bombtank.examine(user) - -/obj/item/onetankbomb/update_icon() - if(bombtank) - icon_state = bombtank.icon_state - if(bombassembly) - overlays += bombassembly.icon_state - overlays += bombassembly.overlays - overlays += "bomb_assembly" - -/obj/item/onetankbomb/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/analyzer)) - bombtank.attackby(W, user, params) - return - if(iswrench(W) && !status) //This is basically bomb assembly code inverted. apparently it works. - - to_chat(user, "You disassemble [src].") - - bombassembly.loc = user.loc - bombassembly.master = null - bombassembly = null - - bombtank.loc = user.loc - bombtank.master = null - bombtank = null - - qdel(src) - return - if(iswelder(W)) - var/obj/item/weldingtool/WT = W - if(WT.welding) - if(!status) - status = TRUE - investigate_log("[key_name(user)] welded a single tank bomb. Temperature: [bombtank.air_contents.temperature-T0C]", INVESTIGATE_BOMB) - msg_admin_attack("[key_name_admin(user)] welded a single tank bomb. Temperature: [bombtank.air_contents.temperature-T0C]", ATKLOG_FEW) - log_game("[key_name(user)] welded a single tank bomb. Temperature: [bombtank.air_contents.temperature - T0C]") - to_chat(user, "A pressure hole has been bored to [bombtank] valve. [bombtank] can now be ignited.") - else - status = FALSE - investigate_log("[key_name(user)] unwelded a single tank bomb. Temperature: [bombtank.air_contents.temperature-T0C]", INVESTIGATE_BOMB) - to_chat(user, "The hole has been closed.") - add_fingerprint(user) - ..() - -/obj/item/onetankbomb/attack_self(mob/user) //pressing the bomb accesses its assembly - bombassembly.attack_self(user, 1) - add_fingerprint(user) - return - -/obj/item/onetankbomb/receive_signal() //This is mainly called by the sensor through sense() to the holder, and from the holder to here. - visible_message("[bicon(src)] *beep* *beep*", "*beep* *beep*") - sleep(10) - if(!src) - return - if(status) - bombtank.detonate() //if its not a dud, boom (or not boom if you made shitty mix) the ignite proc is below, in this file - else - bombtank.release() - -/obj/item/onetankbomb/HasProximity(atom/movable/AM) - if(bombassembly) - bombassembly.HasProximity(AM) - -/obj/item/onetankbomb/Crossed(atom/movable/AM, oldloc) //for mousetraps - if(bombassembly) - bombassembly.Crossed(AM, oldloc) - -/obj/item/onetankbomb/on_found(mob/finder) //for mousetraps - if(bombassembly) - bombassembly.on_found(finder) - -/obj/item/onetankbomb/hear_talk(mob/living/M, list/message_pieces) - if(bombassembly) - bombassembly.hear_talk(M, message_pieces) - -/obj/item/onetankbomb/hear_message(mob/living/M, msg) - if(bombassembly) - bombassembly.hear_message(M, msg) - -// ---------- Procs below are for tanks that are used exclusively in 1-tank bombs ---------- - -/obj/item/tank/proc/bomb_assemble(W,user) //Bomb assembly proc. This turns assembly+tank into a bomb - var/obj/item/assembly_holder/S = W - var/mob/M = user - if(!S.secured) //Check if the assembly is secured - return - if(isigniter(S.a_left) == isigniter(S.a_right)) //Check if either part of the assembly has an igniter, but if both parts are igniters, then fuck it - return - - var/obj/item/onetankbomb/R = new /obj/item/onetankbomb(loc) - - M.drop_item() //Remove the assembly from your hands - M.remove_from_mob(src) //Remove the tank from your character,in case you were holding it - M.put_in_hands(R) //Equips the bomb if possible, or puts it on the floor. - - R.bombassembly = S //Tell the bomb about its assembly part - S.master = R //Tell the assembly about its new owner - S.loc = R //Move the assembly out of the fucking way - - R.bombtank = src //Same for tank - master = R - loc = R - R.update_icon() - return - -/obj/item/tank/proc/detonate() //This happens when a bomb is told to explode - var/fuel_moles = air_contents.toxins + air_contents.oxygen/6 - var/strength = 1 - - var/turf/ground_zero = get_turf(loc) - loc = null - - if(air_contents.temperature > (T0C + 400)) - strength = (fuel_moles/15) - - if(strength >=1) - explosion(ground_zero, round(strength,1), round(strength*2,1), round(strength*3,1), round(strength*4,1)) - else if(strength >=0.5) - explosion(ground_zero, 0, 1, 2, 4) - else if(strength >=0.2) - explosion(ground_zero, -1, 0, 1, 2) - else - ground_zero.assume_air(air_contents) - ground_zero.hotspot_expose(1000, 125) - - else if(air_contents.temperature > (T0C + 250)) - strength = (fuel_moles/20) - - if(strength >=1) - explosion(ground_zero, 0, round(strength,1), round(strength*2,1), round(strength*3,1)) - else if(strength >=0.5) - explosion(ground_zero, -1, 0, 1, 2) - else - ground_zero.assume_air(air_contents) - ground_zero.hotspot_expose(1000, 125) - - else if(air_contents.temperature > (T0C + 100)) - strength = (fuel_moles/25) - - if(strength >=1) - explosion(ground_zero, -1, 0, round(strength,1), round(strength*3,1)) - else - ground_zero.assume_air(air_contents) - ground_zero.hotspot_expose(1000, 125) - - else - ground_zero.assume_air(air_contents) - ground_zero.hotspot_expose(1000, 125) - - air_update_turf() - if(master) - qdel(master) - qdel(src) - -/obj/item/tank/proc/release() //This happens when the bomb is not welded. Tank contents are just spat out. - var/datum/gas_mixture/removed = air_contents.remove(air_contents.total_moles()) - var/turf/simulated/T = get_turf(src) - if(!T) - return - T.assume_air(removed) - air_update_turf() +/obj/item/onetankbomb + name = "bomb" + icon = 'icons/obj/tank.dmi' + item_state = "assembly" + throwforce = 5 + w_class = WEIGHT_CLASS_NORMAL + throw_speed = 2 + throw_range = 4 + flags = CONDUCT //Copied this from old code, so this may or may not be necessary + var/status = 0 //0 - not readied //1 - bomb finished with welder + var/obj/item/assembly_holder/bombassembly = null //The first part of the bomb is an assembly holder, holding an igniter+some device + var/obj/item/tank/bombtank = null //the second part of the bomb is a plasma tank + origin_tech = "materials=1;engineering=1" + +/obj/item/onetankbomb/examine(mob/user) + . = ..() + . += bombtank.examine(user) + +/obj/item/onetankbomb/update_icon() + if(bombtank) + icon_state = bombtank.icon_state + if(bombassembly) + overlays += bombassembly.icon_state + overlays += bombassembly.overlays + overlays += "bomb_assembly" + +/obj/item/onetankbomb/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/analyzer)) + bombtank.attackby(W, user, params) + return + return ..() + +/obj/item/onetankbomb/wrench_act(mob/user, obj/item/I) //This is basically bomb assembly code inverted. apparently it works. + if(status) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + to_chat(user, "You disassemble [src].") + bombassembly.loc = user.loc + bombassembly.master = null + bombassembly = null + bombtank.loc = user.loc + bombtank.master = null + bombtank = null + qdel(src) + +/obj/item/onetankbomb/welder_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, volume = I.tool_volume)) + return + if(!status) + status = TRUE + investigate_log("[key_name(user)] welded a single tank bomb. Temperature: [bombtank.air_contents.temperature-T0C]", INVESTIGATE_BOMB) + msg_admin_attack("[key_name_admin(user)] welded a single tank bomb. Temperature: [bombtank.air_contents.temperature-T0C]", ATKLOG_FEW) + log_game("[key_name(user)] welded a single tank bomb. Temperature: [bombtank.air_contents.temperature - T0C]") + to_chat(user, "A pressure hole has been bored to [bombtank] valve. [bombtank] can now be ignited.") + else + status = FALSE + investigate_log("[key_name(user)] unwelded a single tank bomb. Temperature: [bombtank.air_contents.temperature-T0C]", INVESTIGATE_BOMB) + to_chat(user, "The hole has been closed.") + + +/obj/item/onetankbomb/attack_self(mob/user) //pressing the bomb accesses its assembly + bombassembly.attack_self(user, 1) + add_fingerprint(user) + return + +/obj/item/onetankbomb/receive_signal() //This is mainly called by the sensor through sense() to the holder, and from the holder to here. + visible_message("[bicon(src)] *beep* *beep*", "*beep* *beep*") + sleep(10) + if(!src) + return + if(status) + bombtank.detonate() //if its not a dud, boom (or not boom if you made shitty mix) the ignite proc is below, in this file + else + bombtank.release() + +/obj/item/onetankbomb/HasProximity(atom/movable/AM) + if(bombassembly) + bombassembly.HasProximity(AM) + +/obj/item/onetankbomb/Crossed(atom/movable/AM, oldloc) //for mousetraps + if(bombassembly) + bombassembly.Crossed(AM, oldloc) + +/obj/item/onetankbomb/on_found(mob/finder) //for mousetraps + if(bombassembly) + bombassembly.on_found(finder) + +/obj/item/onetankbomb/hear_talk(mob/living/M, list/message_pieces) + if(bombassembly) + bombassembly.hear_talk(M, message_pieces) + +/obj/item/onetankbomb/hear_message(mob/living/M, msg) + if(bombassembly) + bombassembly.hear_message(M, msg) + +// ---------- Procs below are for tanks that are used exclusively in 1-tank bombs ---------- + +/obj/item/tank/proc/bomb_assemble(W,user) //Bomb assembly proc. This turns assembly+tank into a bomb + var/obj/item/assembly_holder/S = W + var/mob/M = user + if(!S.secured) //Check if the assembly is secured + return + if(isigniter(S.a_left) == isigniter(S.a_right)) //Check if either part of the assembly has an igniter, but if both parts are igniters, then fuck it + return + + var/obj/item/onetankbomb/R = new /obj/item/onetankbomb(loc) + + M.drop_item() //Remove the assembly from your hands + M.remove_from_mob(src) //Remove the tank from your character,in case you were holding it + M.put_in_hands(R) //Equips the bomb if possible, or puts it on the floor. + + R.bombassembly = S //Tell the bomb about its assembly part + S.master = R //Tell the assembly about its new owner + S.loc = R //Move the assembly out of the fucking way + + R.bombtank = src //Same for tank + master = R + loc = R + R.update_icon() + return + +/obj/item/tank/proc/detonate() //This happens when a bomb is told to explode + var/fuel_moles = air_contents.toxins + air_contents.oxygen/6 + var/strength = 1 + + var/turf/ground_zero = get_turf(loc) + loc = null + + if(air_contents.temperature > (T0C + 400)) + strength = (fuel_moles/15) + + if(strength >=1) + explosion(ground_zero, round(strength,1), round(strength*2,1), round(strength*3,1), round(strength*4,1)) + else if(strength >=0.5) + explosion(ground_zero, 0, 1, 2, 4) + else if(strength >=0.2) + explosion(ground_zero, -1, 0, 1, 2) + else + ground_zero.assume_air(air_contents) + ground_zero.hotspot_expose(1000, 125) + + else if(air_contents.temperature > (T0C + 250)) + strength = (fuel_moles/20) + + if(strength >=1) + explosion(ground_zero, 0, round(strength,1), round(strength*2,1), round(strength*3,1)) + else if(strength >=0.5) + explosion(ground_zero, -1, 0, 1, 2) + else + ground_zero.assume_air(air_contents) + ground_zero.hotspot_expose(1000, 125) + + else if(air_contents.temperature > (T0C + 100)) + strength = (fuel_moles/25) + + if(strength >=1) + explosion(ground_zero, -1, 0, round(strength,1), round(strength*3,1)) + else + ground_zero.assume_air(air_contents) + ground_zero.hotspot_expose(1000, 125) + + else + ground_zero.assume_air(air_contents) + ground_zero.hotspot_expose(1000, 125) + + air_update_turf() + if(master) + qdel(master) + qdel(src) + +/obj/item/tank/proc/release() //This happens when the bomb is not welded. Tank contents are just spat out. + var/datum/gas_mixture/removed = air_contents.remove(air_contents.total_moles()) + var/turf/simulated/T = get_turf(src) + if(!T) + return + T.assume_air(removed) + air_update_turf() diff --git a/code/modules/assembly/health.dm b/code/modules/assembly/health.dm index 3a543e38f72..a83df5501c9 100644 --- a/code/modules/assembly/health.dm +++ b/code/modules/assembly/health.dm @@ -28,17 +28,16 @@ update_icon() return secured -/obj/item/assembly/health/attackby(obj/item/W, mob/user) - if(ismultitool(W)) - if(alarm_health == 0) - alarm_health = -90 - user.show_message("You toggle [src] to \"detect death\" mode.") - else - alarm_health = 0 - user.show_message("You toggle [src] to \"detect critical state\" mode.") +/obj/item/assembly/health/multitool_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) return + if(alarm_health == 0) + alarm_health = -90 + user.show_message("You toggle [src] to \"detect death\" mode.") else - return ..() + alarm_health = 0 + user.show_message("You toggle [src] to \"detect critical state\" mode.") /obj/item/assembly/health/process() if(!scanning || !secured) @@ -105,4 +104,4 @@ return attack_self(user) - return \ No newline at end of file + return diff --git a/code/modules/assembly/helpers.dm b/code/modules/assembly/helpers.dm index eef33d2763d..9b5e93931a7 100644 --- a/code/modules/assembly/helpers.dm +++ b/code/modules/assembly/helpers.dm @@ -1,44 +1,44 @@ -/proc/isassembly(O) - if(istype(O, /obj/item/assembly)) - return 1 - return 0 - -/proc/isigniter(O) - if(istype(O, /obj/item/assembly/igniter)) - return 1 - return 0 - -/proc/isinfared(O) - if(istype(O, /obj/item/assembly/infra)) - return 1 - return 0 - -/proc/isprox(O) - if(istype(O, /obj/item/assembly/prox_sensor)) - return 1 - return 0 - -/proc/issignaler(O) - if(istype(O, /obj/item/assembly/signaler)) - return 1 - return 0 - -/proc/istimer(O) - if(istype(O, /obj/item/assembly/timer)) - return 1 - return 0 - -/* -Name: IsSpecialAssembly -Desc: If true is an object that can be attached to an assembly holder but is a special thing like a plasma can or door -*/ - -/obj/proc/IsSpecialAssembly() - return 0 - -/* -Name: IsAssemblyHolder -Desc: If true is an object that can hold an assemblyholder object -*/ -/obj/proc/IsAssemblyHolder() - return 0 \ No newline at end of file +/proc/isassembly(O) + if(istype(O, /obj/item/assembly)) + return 1 + return 0 + +/proc/isigniter(O) + if(istype(O, /obj/item/assembly/igniter)) + return 1 + return 0 + +/proc/isinfared(O) + if(istype(O, /obj/item/assembly/infra)) + return 1 + return 0 + +/proc/isprox(O) + if(istype(O, /obj/item/assembly/prox_sensor)) + return 1 + return 0 + +/proc/issignaler(O) + if(istype(O, /obj/item/assembly/signaler)) + return 1 + return 0 + +/proc/istimer(O) + if(istype(O, /obj/item/assembly/timer)) + return 1 + return 0 + +/* +Name: IsSpecialAssembly +Desc: If true is an object that can be attached to an assembly holder but is a special thing like a plasma can or door +*/ + +/obj/proc/IsSpecialAssembly() + return 0 + +/* +Name: IsAssemblyHolder +Desc: If true is an object that can hold an assemblyholder object +*/ +/obj/proc/IsAssemblyHolder() + return 0 diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index a7007806289..6a6cd7e8d8e 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -1,200 +1,197 @@ -/obj/item/assembly_holder - name = "Assembly" - icon = 'icons/obj/assemblies/new_assemblies.dmi' - icon_state = "holder" - item_state = "assembly" - flags = CONDUCT - throwforce = 5 - w_class = WEIGHT_CLASS_SMALL - throw_speed = 3 - throw_range = 10 - - var/secured = FALSE - var/obj/item/assembly/a_left = null - var/obj/item/assembly/a_right = null - -/obj/item/assembly_holder/proc/attach(obj/item/D, obj/item/D2, mob/user) - return - -/obj/item/assembly_holder/proc/process_activation(var/obj/item/D) - return - -/obj/item/assembly_holder/IsAssemblyHolder() - return TRUE - -/obj/item/assembly_holder/Destroy() - if(a_left) - a_left.holder = null - if(a_right) - a_right.holder = null - return ..() - -/obj/item/assembly_holder/attach(obj/item/D, obj/item/D2, mob/user) - if(!D || !D2) - return FALSE - if(!isassembly(D) || !isassembly(D2)) - return FALSE - var/obj/item/assembly/A1 = D - var/obj/item/assembly/A2 = D2 - if(A1.secured || A2.secured) - return FALSE - if(!A1.remove_item_from_storage(src)) - if(user) - user.remove_from_mob(A1) - A1.loc = src - if(!A2.remove_item_from_storage(src)) - if(user) - user.remove_from_mob(A2) - A2.loc = src - A1.holder = src - A2.holder = src - a_left = A1 - a_right = A2 - name = "[A1.name]-[A2.name] assembly" - update_icon() - return TRUE - - -/obj/item/assembly_holder/update_icon() - overlays.Cut() - if(a_left) - overlays += "[a_left.icon_state]_left" - for(var/O in a_left.attached_overlays) - overlays += "[O]_l" - if(a_right) - overlays += "[a_right.icon_state]_right" - for(var/O in a_right.attached_overlays) - overlays += "[O]_r" - if(master) - master.update_icon() - - -/obj/item/assembly_holder/examine(mob/user) - . = ..() - if(in_range(src, user) || loc == user) - if(secured) - . += "[src] is ready!" - else - . += "[src] can be attached!" - - -/obj/item/assembly_holder/HasProximity(atom/movable/AM) - if(a_left) - a_left.HasProximity(AM) - if(a_right) - a_right.HasProximity(AM) - - -/obj/item/assembly_holder/Crossed(atom/movable/AM, oldloc) - if(a_left) - a_left.Crossed(AM, oldloc) - if(a_right) - a_right.Crossed(AM, oldloc) - -/obj/item/assembly_holder/on_found(mob/finder) - if(a_left) - a_left.on_found(finder) - if(a_right) - a_right.on_found(finder) - - -/obj/item/assembly_holder/hear_talk(mob/living/M, list/message_pieces) - if(a_left) - a_left.hear_talk(M, message_pieces) - if(a_right) - a_right.hear_talk(M, message_pieces) - -/obj/item/assembly_holder/hear_message(mob/living/M, msg) - if(a_left) - a_left.hear_message(M, msg) - if(a_right) - a_right.hear_message(M, msg) - -/obj/item/assembly_holder/proc/process_movement() // infrared beams and prox sensors - if(a_left && a_right) - a_left.holder_movement() - a_right.holder_movement() - -/obj/item/assembly_holder/Move() - . = ..() - process_movement() - return - -/obj/item/assembly_holder/pickup() - . = ..() - process_movement() - -/obj/item/assembly_holder/Bump() - ..() - process_movement() - -/obj/item/assembly_holder/throw_impact() // called when a throw stops - ..() - process_movement() - -/obj/item/assembly_holder/attack_hand()//Perhapse this should be a holder_pickup proc instead, can add if needbe I guess - if(a_left && a_right) - a_left.holder_movement() - a_right.holder_movement() - ..() - return - -/obj/item/assembly_holder/attackby(obj/item/W, mob/user, params) - if(isscrewdriver(W)) - if(!a_left || !a_right) - to_chat(user, "BUG:Assembly part missing, please report this!") - return - a_left.toggle_secure() - a_right.toggle_secure() - secured = !secured - if(secured) - to_chat(user, "[src] is ready!") - else - to_chat(user, "[src] can now be taken apart!") - update_icon() - return - else - ..() - return - - -/obj/item/assembly_holder/attack_self(mob/user) - add_fingerprint(user) - if(secured) - if(!a_left || !a_right) - to_chat(user, "Assembly part missing!") - return - if(istype(a_left, a_right.type))//If they are the same type it causes issues due to window code - switch(alert("Which side would you like to use?",,"Left","Right")) - if("Left") - a_left.attack_self(user) - if("Right") - a_right.attack_self(user) - return - else - a_left.attack_self(user) - a_right.attack_self(user) - else - var/turf/T = get_turf(src) - if(!T) - return FALSE - if(a_left) - a_left.holder = null - a_left.loc = T - if(a_right) - a_right.holder = null - a_right.loc = T - qdel(src) - - -/obj/item/assembly_holder/process_activation(obj/D, normal = TRUE, special = TRUE) - if(!D) - return FALSE - if(normal && a_right && a_left) - if(a_right != D) - a_right.pulsed(0) - if(a_left != D) - a_left.pulsed(0) - if(master) - master.receive_signal() - return TRUE \ No newline at end of file +/obj/item/assembly_holder + name = "Assembly" + icon = 'icons/obj/assemblies/new_assemblies.dmi' + icon_state = "holder" + item_state = "assembly" + flags = CONDUCT + throwforce = 5 + w_class = WEIGHT_CLASS_SMALL + throw_speed = 3 + throw_range = 10 + + var/secured = FALSE + var/obj/item/assembly/a_left = null + var/obj/item/assembly/a_right = null + +/obj/item/assembly_holder/proc/attach(obj/item/D, obj/item/D2, mob/user) + return + +/obj/item/assembly_holder/proc/process_activation(var/obj/item/D) + return + +/obj/item/assembly_holder/IsAssemblyHolder() + return TRUE + +/obj/item/assembly_holder/Destroy() + if(a_left) + a_left.holder = null + if(a_right) + a_right.holder = null + return ..() + +/obj/item/assembly_holder/attach(obj/item/D, obj/item/D2, mob/user) + if(!D || !D2) + return FALSE + if(!isassembly(D) || !isassembly(D2)) + return FALSE + var/obj/item/assembly/A1 = D + var/obj/item/assembly/A2 = D2 + if(A1.secured || A2.secured) + return FALSE + if(!A1.remove_item_from_storage(src)) + if(user) + user.remove_from_mob(A1) + A1.loc = src + if(!A2.remove_item_from_storage(src)) + if(user) + user.remove_from_mob(A2) + A2.loc = src + A1.holder = src + A2.holder = src + a_left = A1 + a_right = A2 + name = "[A1.name]-[A2.name] assembly" + update_icon() + return TRUE + + +/obj/item/assembly_holder/update_icon() + overlays.Cut() + if(a_left) + overlays += "[a_left.icon_state]_left" + for(var/O in a_left.attached_overlays) + overlays += "[O]_l" + if(a_right) + overlays += "[a_right.icon_state]_right" + for(var/O in a_right.attached_overlays) + overlays += "[O]_r" + if(master) + master.update_icon() + + +/obj/item/assembly_holder/examine(mob/user) + . = ..() + if(in_range(src, user) || loc == user) + if(secured) + . += "[src] is ready!" + else + . += "[src] can be attached!" + + +/obj/item/assembly_holder/HasProximity(atom/movable/AM) + if(a_left) + a_left.HasProximity(AM) + if(a_right) + a_right.HasProximity(AM) + + +/obj/item/assembly_holder/Crossed(atom/movable/AM, oldloc) + if(a_left) + a_left.Crossed(AM, oldloc) + if(a_right) + a_right.Crossed(AM, oldloc) + +/obj/item/assembly_holder/on_found(mob/finder) + if(a_left) + a_left.on_found(finder) + if(a_right) + a_right.on_found(finder) + + +/obj/item/assembly_holder/hear_talk(mob/living/M, list/message_pieces) + if(a_left) + a_left.hear_talk(M, message_pieces) + if(a_right) + a_right.hear_talk(M, message_pieces) + +/obj/item/assembly_holder/hear_message(mob/living/M, msg) + if(a_left) + a_left.hear_message(M, msg) + if(a_right) + a_right.hear_message(M, msg) + +/obj/item/assembly_holder/proc/process_movement() // infrared beams and prox sensors + if(a_left && a_right) + a_left.holder_movement() + a_right.holder_movement() + +/obj/item/assembly_holder/Move() + . = ..() + process_movement() + return + +/obj/item/assembly_holder/pickup() + . = ..() + process_movement() + +/obj/item/assembly_holder/Bump() + ..() + process_movement() + +/obj/item/assembly_holder/throw_impact() // called when a throw stops + ..() + process_movement() + +/obj/item/assembly_holder/attack_hand()//Perhapse this should be a holder_pickup proc instead, can add if needbe I guess + if(a_left && a_right) + a_left.holder_movement() + a_right.holder_movement() + ..() + return + +/obj/item/assembly_holder/screwdriver_act(mob/user, obj/item/I) + if(!a_left || !a_right) + to_chat(user, "BUG:Assembly part missing, please report this!") + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + a_left.toggle_secure() + a_right.toggle_secure() + secured = !secured + if(secured) + to_chat(user, "[src] is ready!") + else + to_chat(user, "[src] can now be taken apart!") + update_icon() + +/obj/item/assembly_holder/attack_self(mob/user) + add_fingerprint(user) + if(secured) + if(!a_left || !a_right) + to_chat(user, "Assembly part missing!") + return + if(istype(a_left, a_right.type))//If they are the same type it causes issues due to window code + switch(alert("Which side would you like to use?",,"Left","Right")) + if("Left") + a_left.attack_self(user) + if("Right") + a_right.attack_self(user) + return + else + a_left.attack_self(user) + a_right.attack_self(user) + else + var/turf/T = get_turf(src) + if(!T) + return FALSE + if(a_left) + a_left.holder = null + a_left.loc = T + if(a_right) + a_right.holder = null + a_right.loc = T + qdel(src) + + +/obj/item/assembly_holder/process_activation(obj/D, normal = TRUE, special = TRUE) + if(!D) + return FALSE + if(normal && a_right && a_left) + if(a_right != D) + a_right.pulsed(0) + if(a_left != D) + a_left.pulsed(0) + if(master) + master.receive_signal() + return TRUE diff --git a/code/modules/assembly/igniter.dm b/code/modules/assembly/igniter.dm index 3bbfe65bbc0..b81acbbe2b0 100644 --- a/code/modules/assembly/igniter.dm +++ b/code/modules/assembly/igniter.dm @@ -1,45 +1,45 @@ -/obj/item/assembly/igniter - name = "igniter" - desc = "A small electronic device able to ignite combustable substances." - icon_state = "igniter" - materials = list(MAT_METAL=500, MAT_GLASS=50) - origin_tech = "magnets=1" - var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread - -/obj/item/assembly/igniter/New() - ..() - sparks.set_up(2, 0, src) - sparks.attach(src) - -/obj/item/assembly/igniter/Destroy() - QDEL_NULL(sparks) - return ..() - - -/obj/item/assembly/igniter/describe() - return "The igniter is [secured ? "secured." : "unsecured."]" - - -/obj/item/assembly/igniter/activate() - if(!..()) - return FALSE//Cooldown check - var/turf/location = get_turf(loc) - if(location) - location.hotspot_expose(1000,1000) - if(istype(loc, /obj/item/assembly_holder)) - if(istype(loc.loc, /obj/structure/reagent_dispensers/fueltank)) - var/obj/structure/reagent_dispensers/fueltank/tank = loc.loc - if(tank) - tank.boom(TRUE) - if(istype(loc.loc, /obj/item/reagent_containers/glass/beaker)) - var/obj/item/reagent_containers/glass/beaker/beakerbomb = loc.loc - if(beakerbomb) - beakerbomb.heat_beaker() - sparks.start() - return TRUE - - -/obj/item/assembly/igniter/attack_self(mob/user) - activate() - add_fingerprint(user) - return +/obj/item/assembly/igniter + name = "igniter" + desc = "A small electronic device able to ignite combustable substances." + icon_state = "igniter" + materials = list(MAT_METAL=500, MAT_GLASS=50) + origin_tech = "magnets=1" + var/datum/effect_system/spark_spread/sparks = new /datum/effect_system/spark_spread + +/obj/item/assembly/igniter/New() + ..() + sparks.set_up(2, 0, src) + sparks.attach(src) + +/obj/item/assembly/igniter/Destroy() + QDEL_NULL(sparks) + return ..() + + +/obj/item/assembly/igniter/describe() + return "The igniter is [secured ? "secured." : "unsecured."]" + + +/obj/item/assembly/igniter/activate() + if(!..()) + return FALSE//Cooldown check + var/turf/location = get_turf(loc) + if(location) + location.hotspot_expose(1000,1000) + if(istype(loc, /obj/item/assembly_holder)) + if(istype(loc.loc, /obj/structure/reagent_dispensers/fueltank)) + var/obj/structure/reagent_dispensers/fueltank/tank = loc.loc + if(tank) + tank.boom(TRUE) + if(istype(loc.loc, /obj/item/reagent_containers/glass/beaker)) + var/obj/item/reagent_containers/glass/beaker/beakerbomb = loc.loc + if(beakerbomb) + beakerbomb.heat_beaker() + sparks.start() + return TRUE + + +/obj/item/assembly/igniter/attack_self(mob/user) + activate() + add_fingerprint(user) + return diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm index b9d3ee2afc1..19263e010ab 100644 --- a/code/modules/assembly/infrared.dm +++ b/code/modules/assembly/infrared.dm @@ -1,286 +1,286 @@ -/obj/item/assembly/infra - name = "infrared emitter" - desc = "Emits a visible or invisible beam and is triggered when the beam is interrupted." - icon_state = "infrared" - materials = list(MAT_METAL=1000, MAT_GLASS=500) - origin_tech = "magnets=2;materials=2" - - bomb_name = "tripwire mine" - - secured = FALSE // toggle_secure()'ed in New() for correct adding to processing_objects, won't work otherwise - dir = EAST - var/on = FALSE - var/visible = TRUE - var/obj/effect/beam/i_beam/first = null - var/obj/effect/beam/i_beam/last = null - var/max_nesting_level = 10 - var/turf/fire_location - var/emission_cycles = 0 - var/emission_cap = 20 - -/obj/item/assembly/infra/Destroy() - if(first) - QDEL_NULL(first) - last = null - fire_location = null - return ..() - -/obj/item/assembly/infra/describe() - return "The assembly is [secured ? "secure" : "not secure"]. The infrared trigger is [on ? "on" : "off"]." - -/obj/item/assembly/infra/examine(mob/user) - . = ..() - . += describe() - -/obj/item/assembly/infra/activate() - if(!..()) - return FALSE//Cooldown check - on = !on - update_icon() - return TRUE - -/obj/item/assembly/infra/toggle_secure() - secured = !secured - if(secured) - START_PROCESSING(SSobj, src) - else - on = FALSE - if(first) - qdel(first) - STOP_PROCESSING(SSobj, src) - update_icon() - return secured - -/obj/item/assembly/infra/New() - ..() - if(!secured) - toggle_secure() - -/obj/item/assembly/infra/proc/arm() // Forces the device to arm no matter its current state. - if(!secured) // Checked because arm() might be called sometime after the object is spawned. - toggle_secure() - on = 1 - -/obj/item/assembly/infra/update_icon() - overlays.Cut() - attached_overlays = list() - if(on) - overlays += "infrared_on" - attached_overlays += "infrared_on" - - if(holder) - holder.update_icon() - -/obj/item/assembly/infra/process() - var/turf/T = get_turf(src) - if(first && (!on || !fire_location || fire_location != T || emission_cycles >= emission_cap)) - qdel(first) - return - if(!on) - return - if(!secured) - return - if(first && last) - last.process() - emission_cycles++ - return - if(T) - fire_location = T - emission_cycles = 0 - var/obj/effect/beam/i_beam/I = new /obj/effect/beam/i_beam(T) - I.master = src - I.density = 1 - I.dir = dir - I.update_icon() - first = I - step(I, I.dir) - if(first) - I.density = FALSE - I.vis_spread(visible) - I.limit = 8 - I.process() - -/obj/item/assembly/infra/attack_hand() - qdel(first) - ..() - -/obj/item/assembly/infra/Move() - var/t = dir - . = ..() - dir = t - qdel(first) - -/obj/item/assembly/infra/holder_movement() - if(!holder) - return FALSE - qdel(first) - return TRUE - -/obj/item/assembly/infra/equipped(var/mob/user, var/slot) - qdel(first) - return ..() - -/obj/item/assembly/infra/pickup(mob/user) - qdel(first) - return ..() - -/obj/item/assembly/infra/proc/trigger_beam() - if(!secured || !on || cooldown > 0) - return FALSE - pulse(0) - audible_message("[bicon(src)] *beep* *beep*", null, 3) - if(first) - qdel(first) - cooldown = 2 - spawn(10) - process_cooldown() - -/obj/item/assembly/infra/interact(mob/user)//TODO: change this this to the wire control panel - if(!secured) return - user.set_machine(src) - var/dat = {"Infrared Laser - Status: [on ? "On" : "Off"]
        - Visibility: [visible ? "Visible" : "Invisible"]
        - Current Direction: [capitalize(dir2text(dir))]
        -
        -

        Refresh -

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

        Refresh +

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

        Refresh" - dat += "

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

        Refresh" + dat += "

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

        Timing Unit

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

        - Refresh -

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

        Timing Unit

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

        + Refresh +

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

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

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

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

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

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

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

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

        A New World
        \ + As a participant in the Nanotrasen Gateway Project, you will be on the frontiers of space. \ + Though complete safety is assured, participants are advised to prepare for inhospitable \ + environs." + +//we don't want the silly text overlay! +/obj/item/paper/pamphlet/update_icon() + return diff --git a/code/modules/awaymissions/zvis.dm b/code/modules/awaymissions/zvis.dm index 102554f2e6e..b35b0fb6ffc 100644 --- a/code/modules/awaymissions/zvis.dm +++ b/code/modules/awaymissions/zvis.dm @@ -370,4 +370,4 @@ screen_loc = "CENTER[ox >= 0 ? "+" : ""][ox],CENTER[oy >= 0 ? "+" : ""][oy]" /obj/effect/view_portal_dummy/attack_ghost(mob/user) - owner.attack_ghost(user) \ No newline at end of file + owner.attack_ghost(user) diff --git a/code/modules/buildmode/README.md b/code/modules/buildmode/README.md index 37ce2ce6b1b..015e6ce6a66 100644 --- a/code/modules/buildmode/README.md +++ b/code/modules/buildmode/README.md @@ -294,4 +294,4 @@ Existing varieties: + *Left click a location*: - Cause an explosion where you clicked. \ No newline at end of file + Cause an explosion where you clicked. diff --git a/code/modules/buildmode/bm_mode.dm b/code/modules/buildmode/bm_mode.dm index de4c09a192d..1ce23555e3e 100644 --- a/code/modules/buildmode/bm_mode.dm +++ b/code/modules/buildmode/bm_mode.dm @@ -84,4 +84,4 @@ deselect_region() /datum/buildmode_mode/proc/handle_selected_region(mob/user, params) - return \ No newline at end of file + return diff --git a/code/modules/buildmode/buildmode.dm b/code/modules/buildmode/buildmode.dm index 7f43f20c934..6c260c41cb2 100644 --- a/code/modules/buildmode/buildmode.dm +++ b/code/modules/buildmode/buildmode.dm @@ -125,4 +125,4 @@ #undef BM_SWITCHSTATE_NONE #undef BM_SWITCHSTATE_MODE -#undef BM_SWITCHSTATE_DIR \ No newline at end of file +#undef BM_SWITCHSTATE_DIR diff --git a/code/modules/buildmode/buttons.dm b/code/modules/buildmode/buttons.dm index c07be97a9eb..2bec31f8650 100644 --- a/code/modules/buildmode/buttons.dm +++ b/code/modules/buildmode/buttons.dm @@ -85,4 +85,4 @@ /obj/screen/buildmode/quit/Click() bd.quit() - return TRUE \ No newline at end of file + return TRUE diff --git a/code/modules/buildmode/effects/line.dm b/code/modules/buildmode/effects/line.dm index 4b59e8a673c..b49d35af095 100644 --- a/code/modules/buildmode/effects/line.dm +++ b/code/modules/buildmode/effects/line.dm @@ -25,4 +25,4 @@ cl.images -= I cl = null QDEL_NULL(I) - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/buildmode/submodes/advanced.dm b/code/modules/buildmode/submodes/advanced.dm index fadd46753bb..c2e64fb488e 100644 --- a/code/modules/buildmode/submodes/advanced.dm +++ b/code/modules/buildmode/submodes/advanced.dm @@ -56,4 +56,4 @@ if(isobj(object)) log_admin("Build Mode: [key_name(user)] deleted [object] at ([object.x],[object.y],[object.z])") qdel(object) - \ No newline at end of file + diff --git a/code/modules/buildmode/submodes/atmos.dm b/code/modules/buildmode/submodes/atmos.dm index f12fdb1eb1a..b3a07374846 100644 --- a/code/modules/buildmode/submodes/atmos.dm +++ b/code/modules/buildmode/submodes/atmos.dm @@ -69,4 +69,4 @@ T.air_update_turf() // admin log - log_admin("Build Mode: [key_name(user)] changed the atmos of region [COORD(cornerA)] to [COORD(cornerB)]. T: [temperature], P: [pressure], Ox: [oxygen]%, N2: [nitrogen]%, Plsma: [plasma]%, CO2: [cdiox]%, N2O: [nitrox]%. [ctrl_click ? "Overwrote base unsimulated turf gases." : ""]") \ No newline at end of file + log_admin("Build Mode: [key_name(user)] changed the atmos of region [COORD(cornerA)] to [COORD(cornerB)]. T: [temperature], P: [pressure], Ox: [oxygen]%, N2: [nitrogen]%, Plsma: [plasma]%, CO2: [cdiox]%, N2O: [nitrox]%. [ctrl_click ? "Overwrote base unsimulated turf gases." : ""]") diff --git a/code/modules/buildmode/submodes/fill.dm b/code/modules/buildmode/submodes/fill.dm index b143b2c9b90..3c06a199e47 100644 --- a/code/modules/buildmode/submodes/fill.dm +++ b/code/modules/buildmode/submodes/fill.dm @@ -46,4 +46,4 @@ T.ChangeTurf(objholder) else var/obj/A = new objholder(T) - A.setDir(BM.build_dir) \ No newline at end of file + A.setDir(BM.build_dir) diff --git a/code/modules/buildmode/submodes/mapgen.dm b/code/modules/buildmode/submodes/mapgen.dm index 22be429c2ce..df6f6b73ed0 100644 --- a/code/modules/buildmode/submodes/mapgen.dm +++ b/code/modules/buildmode/submodes/mapgen.dm @@ -37,4 +37,4 @@ highlight_region(G.map) var/confirm = alert("Are you sure you want run the map generator?", "Run generator", "Yes", "No") if(confirm == "Yes") - G.generate() \ No newline at end of file + G.generate() diff --git a/code/modules/busy_space/air_traffic.dm b/code/modules/busy_space/air_traffic.dm index b5cbf8e6fda..a7f28f49fb6 100644 --- a/code/modules/busy_space/air_traffic.dm +++ b/code/modules/busy_space/air_traffic.dm @@ -126,4 +126,4 @@ var/datum/lore/atc_controller/atc = new/datum/lore/atc_controller msg(full_response) sleep(5 SECONDS) //Ship sends response to ATC - msg(full_closure,"[prefix] [shipname]") \ No newline at end of file + msg(full_closure,"[prefix] [shipname]") diff --git a/code/modules/busy_space/organizations.dm b/code/modules/busy_space/organizations.dm index 41b3a80cbab..df86aefd75e 100644 --- a/code/modules/busy_space/organizations.dm +++ b/code/modules/busy_space/organizations.dm @@ -439,7 +439,7 @@ /datum/lore/organization/gov/synth name = "Synthetic Union" - short_name = "Synthtica" + short_name = "Synthetica" acronym = "SYN" desc = "A defensive coalition of synthetics based out of New Canaan,\ the Synthetic Union is an organization which aims to establish and consolidate synthetic rights across the galaxy.\ @@ -546,4 +546,4 @@ "Xarxis 5", "Haverick", "Darsing", - "Norfolk") \ No newline at end of file + "Norfolk") diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm index 0feca103c12..85615be1ee5 100644 --- a/code/modules/client/client defines.dm +++ b/code/modules/client/client defines.dm @@ -99,4 +99,4 @@ var/client_keysend_amount = 0 var/next_keysend_reset = 0 var/next_keysend_trip_reset = 0 - var/keysend_tripped = FALSE \ No newline at end of file + var/keysend_tripped = FALSE diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index 962be47cae9..e504a796052 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -414,9 +414,16 @@ if(M.client) playercount += 1 - if(playercount >= 150 && GLOB.panic_bunker_enabled == 0) - GLOB.panic_bunker_enabled = 1 - message_admins("Panic bunker has been automatically enabled due to playercount surpassing 150") + // Update the state of the panic bunker based on current playercount + var/threshold = config.panic_bunker_threshold + + if((playercount > threshold) && (GLOB.panic_bunker_enabled == FALSE)) + GLOB.panic_bunker_enabled = TRUE + message_admins("Panic bunker has been automatically enabled due to playercount rising above [threshold]") + + if((playercount < threshold) && (GLOB.panic_bunker_enabled == TRUE)) + GLOB.panic_bunker_enabled = FALSE + message_admins("Panic bunker has been automatically disabled due to playercount dropping below [threshold]") /client/proc/is_connecting_from_localhost() var/localhost_addresses = list("127.0.0.1", "::1") // Adresses @@ -549,8 +556,8 @@ // Check new peeps for panic bunker if(GLOB.panic_bunker_enabled) - message_admins("Failed Login: [key] - New account attempting to connect during panic bunker") - src << "Sorry but the server is currently not accepting connections from never before seen players. Please try again later." + var/threshold = config.panic_bunker_threshold + src << "Server is not accepting connections from never-before-seen players until player count is less than [threshold]. Please try again later." del(src) return // Dont insert or they can just go in again diff --git a/code/modules/client/message.dm b/code/modules/client/message.dm index ba09e7e1567..5c612339621 100644 --- a/code/modules/client/message.dm +++ b/code/modules/client/message.dm @@ -7,4 +7,4 @@ proc/addclientmessage(var/ckey, var/message) var/list/L = GLOB.clientmessages[ckey] if(!L) GLOB.clientmessages[ckey] = L = list() - L += message \ No newline at end of file + L += message diff --git a/code/modules/client/preference/loadout/loadout_cosmetics.dm b/code/modules/client/preference/loadout/loadout_cosmetics.dm index b7ac6c39493..0375de2de53 100644 --- a/code/modules/client/preference/loadout/loadout_cosmetics.dm +++ b/code/modules/client/preference/loadout/loadout_cosmetics.dm @@ -21,4 +21,4 @@ /datum/gear/lipstick/lime display_name = "lipstick, lime" - path = /obj/item/lipstick/lime \ No newline at end of file + path = /obj/item/lipstick/lime diff --git a/code/modules/client/preference/loadout/loadout_donor.dm b/code/modules/client/preference/loadout/loadout_donor.dm index 830b10b7055..7d43bd6b86a 100644 --- a/code/modules/client/preference/loadout/loadout_donor.dm +++ b/code/modules/client/preference/loadout/loadout_donor.dm @@ -93,4 +93,4 @@ display_name = "Gold ID Decal" path = /obj/item/id_decal/gold donator_tier = 4 - cost = 4 \ No newline at end of file + cost = 4 diff --git a/code/modules/client/preference/loadout/loadout_glasses.dm b/code/modules/client/preference/loadout/loadout_glasses.dm index 94f2555bdba..a84af914c49 100644 --- a/code/modules/client/preference/loadout/loadout_glasses.dm +++ b/code/modules/client/preference/loadout/loadout_glasses.dm @@ -21,4 +21,4 @@ /datum/gear/glasses/prescription display_name = "Prescription glasses" - path = /obj/item/clothing/glasses/regular \ No newline at end of file + path = /obj/item/clothing/glasses/regular diff --git a/code/modules/client/preference/loadout/loadout_gloves.dm b/code/modules/client/preference/loadout/loadout_gloves.dm index cd8d5a7c8e1..879cb840506 100644 --- a/code/modules/client/preference/loadout/loadout_gloves.dm +++ b/code/modules/client/preference/loadout/loadout_gloves.dm @@ -5,4 +5,4 @@ /datum/gear/gloves/fingerless display_name = "Fingerless Gloves" - path = /obj/item/clothing/gloves/fingerless \ No newline at end of file + path = /obj/item/clothing/gloves/fingerless diff --git a/code/modules/client/preference/loadout/loadout_racial.dm b/code/modules/client/preference/loadout/loadout_racial.dm index 2acec90e7b7..0d7b945bb78 100644 --- a/code/modules/client/preference/loadout/loadout_racial.dm +++ b/code/modules/client/preference/loadout/loadout_racial.dm @@ -1,46 +1,47 @@ /datum/gear/racial sort_category = "Racial" subtype_path = /datum/gear/racial + cost = 1 /datum/gear/racial/taj - slot = slot_glasses - cost = 2 - -/datum/gear/racial/taj/civ display_name = "embroidered veil" description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races." path = /obj/item/clothing/glasses/tajblind - cost = 1 + slot = slot_glasses /datum/gear/racial/taj/sec display_name = "sleek veil" description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races. This one has an in-built security HUD." path = /obj/item/clothing/glasses/hud/security/tajblind allowed_roles = list("Head of Security", "Warden", "Security Officer", "Security Pod Pilot", "Internal Affairs Agent", "Magistrate") + cost = 2 /datum/gear/racial/taj/med display_name = "lightweight veil" description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races. This one has an in-built medical HUD." path = /obj/item/clothing/glasses/hud/health/tajblind allowed_roles = list("Chief Medical Officer", "Medical Doctor", "Chemist", "Psychiatrist", "Paramedic", "Virologist", "Brig Physician" , "Coroner") + cost = 2 /datum/gear/racial/taj/sci display_name = "hi-tech veil" description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races." path = /obj/item/clothing/glasses/tajblind/sci + cost = 2 /datum/gear/racial/taj/eng display_name = "industrial veil" description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races." path = /obj/item/clothing/glasses/tajblind/eng - + cost = 2 + /datum/gear/racial/taj/cargo display_name = "khaki veil" description = "A common traditional nano-fiber veil worn by many Tajaran, It is rare and offensive to see it on other races. It is light and comfy!" path = /obj/item/clothing/glasses/tajblind/cargo - + cost = 2 + /datum/gear/racial/footwraps display_name = "cloth footwraps" path = /obj/item/clothing/shoes/footwraps slot = slot_shoes - cost = 1 diff --git a/code/modules/client/preference/loadout/loadout_shoes.dm b/code/modules/client/preference/loadout/loadout_shoes.dm index 5031f4333fb..ab000b209ac 100644 --- a/code/modules/client/preference/loadout/loadout_shoes.dm +++ b/code/modules/client/preference/loadout/loadout_shoes.dm @@ -55,4 +55,4 @@ /datum/gear/shoes/whiteshoes display_name = "White shoes" - path = /obj/item/clothing/shoes/white \ No newline at end of file + path = /obj/item/clothing/shoes/white diff --git a/code/modules/client/preference/preferences.dm b/code/modules/client/preference/preferences.dm index 56cb55b6b80..4e8ef94aeec 100644 --- a/code/modules/client/preference/preferences.dm +++ b/code/modules/client/preference/preferences.dm @@ -55,8 +55,8 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts return max(0, days - C.player_age) return 0 -#define MAX_SAVE_SLOTS 20 // Save slots for regular players -#define MAX_SAVE_SLOTS_MEMBER 20 // Save slots for BYOND members +#define MAX_SAVE_SLOTS 30 // Save slots for regular players +#define MAX_SAVE_SLOTS_MEMBER 30 // Save slots for BYOND members #define TAB_CHAR 0 #define TAB_GAME 1 @@ -651,7 +651,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts var/available_in_days = job.available_in_days(user.client) HTML += "[rank] \[IN [(available_in_days)] DAYS]" continue - if((job_support_low & CIVILIAN) && (rank != "Civilian")) + if((job_support_low & JOB_CIVILIAN) && (rank != "Civilian")) HTML += "[rank]" continue if((rank in command_positions) || (rank == "AI"))//Bold head jobs @@ -693,7 +693,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts // HTML += "" if(rank == "Civilian")//Civilian is special - if(job_support_low & CIVILIAN) + if(job_support_low & JOB_CIVILIAN) HTML += " \[Yes]" else HTML += " \[No]" @@ -760,7 +760,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts job_medsci_high = 0 job_karma_high = 0 - if(job.department_flag == SUPPORT) + if(job.department_flag == JOBCAT_SUPPORT) job_support_low &= ~job.flag job_support_med &= ~job.flag job_support_high &= ~job.flag @@ -774,7 +774,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts job_support_low |= job.flag return 1 - else if(job.department_flag == ENGSEC) + else if(job.department_flag == JOBCAT_ENGSEC) job_engsec_low &= ~job.flag job_engsec_med &= ~job.flag job_engsec_high &= ~job.flag @@ -788,7 +788,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts job_engsec_low |= job.flag return 1 - else if(job.department_flag == MEDSCI) + else if(job.department_flag == JOBCAT_MEDSCI) job_medsci_low &= ~job.flag job_medsci_med &= ~job.flag job_medsci_high &= ~job.flag @@ -802,7 +802,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts job_medsci_low |= job.flag return 1 - else if(job.department_flag == KARMA) + else if(job.department_flag == JOBCAT_KARMA) job_karma_low &= ~job.flag job_karma_med &= ~job.flag job_karma_high &= ~job.flag @@ -973,7 +973,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts /datum/preferences/proc/GetJobDepartment(var/datum/job/job, var/level) if(!job || !level) return 0 switch(job.department_flag) - if(SUPPORT) + if(JOBCAT_SUPPORT) switch(level) if(1) return job_support_high @@ -981,7 +981,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts return job_support_med if(3) return job_support_low - if(MEDSCI) + if(JOBCAT_MEDSCI) switch(level) if(1) return job_medsci_high @@ -989,7 +989,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts return job_medsci_med if(3) return job_medsci_low - if(ENGSEC) + if(JOBCAT_ENGSEC) switch(level) if(1) return job_engsec_high @@ -997,7 +997,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts return job_engsec_med if(3) return job_engsec_low - if(KARMA) + if(JOBCAT_KARMA) switch(level) if(1) return job_karma_high @@ -1027,7 +1027,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts job_karma_high = 0 switch(job.department_flag) - if(SUPPORT) + if(JOBCAT_SUPPORT) switch(level) if(2) job_support_high = job.flag @@ -1037,7 +1037,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts job_support_low &= ~job.flag else job_support_low |= job.flag - if(MEDSCI) + if(JOBCAT_MEDSCI) switch(level) if(2) job_medsci_high = job.flag @@ -1047,7 +1047,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts job_medsci_low &= ~job.flag else job_medsci_low |= job.flag - if(ENGSEC) + if(JOBCAT_ENGSEC) switch(level) if(2) job_engsec_high = job.flag @@ -1057,7 +1057,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts job_engsec_low &= ~job.flag else job_engsec_low |= job.flag - if(KARMA) + if(JOBCAT_KARMA) switch(level) if(2) job_karma_high = job.flag @@ -1308,7 +1308,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts else //Not using the whitelist? Aliens for everyone! new_species += GLOB.whitelisted_species - species = input("Please select a species", "Character Generation", null) in new_species + species = input("Please select a species", "Character Generation", null) in sortTim(new_species, /proc/cmp_text_asc) var/datum/species/NS = GLOB.all_species[species] if(!istype(NS)) //The species was invalid. Notify the user and fail out. species = prev_species @@ -1406,7 +1406,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts if(!(lang.flags & RESTRICTED)) new_languages += lang.name - language = input("Please select a secondary language", "Character Generation", null) in new_languages + language = input("Please select a secondary language", "Character Generation", null) in sortTim(new_languages, /proc/cmp_text_asc) if("autohiss_mode") if(S.autohiss_basic_map) diff --git a/code/modules/client/preference/preferences_toggles.dm b/code/modules/client/preference/preferences_toggles.dm index d5e4b2bf3fe..5534e18e253 100644 --- a/code/modules/client/preference/preferences_toggles.dm +++ b/code/modules/client/preference/preferences_toggles.dm @@ -36,6 +36,15 @@ to_chat(usr, "You will [(prefs.toggles & CHAT_RADIO) ? "now" : "no longer"] see radio chatter from radios or speakers") feedback_add_details("admin_verb","THR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +/client/verb/toggle_ai_voice_annoucements() + set name = "Hear/Silence AI Voice Announcements" + set category = "Preferences" + set desc = "Toggle hearing AI annoucements in voice form or in text form" + prefs.sound ^= SOUND_AI_VOICE + prefs.save_preferences(src) + to_chat(usr, "[(prefs.sound & SOUND_AI_VOICE) ? "You will now hear AI announcements." : "AI annoucements will now be converted to text."] ") + feedback_add_details("admin_verb","TAIvoice") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + /client/proc/toggleadminhelpsound() set name = "Hear/Silence Admin Bwoinks" set category = "Preferences" diff --git a/code/modules/client/view.dm b/code/modules/client/view.dm index f4e74e203e8..ac862933afb 100644 --- a/code/modules/client/view.dm +++ b/code/modules/client/view.dm @@ -87,4 +87,4 @@ return to_chat(src, "View range set to [viewNum_to_text(view_range)]") - AddViewMod("custom", view_range) \ No newline at end of file + AddViewMod("custom", view_range) diff --git a/code/modules/clothing/chameleon.dm b/code/modules/clothing/chameleon.dm index f2e9e639dd6..da0873f162f 100644 --- a/code/modules/clothing/chameleon.dm +++ b/code/modules/clothing/chameleon.dm @@ -542,4 +542,4 @@ /obj/item/stamp/chameleon/broken/Initialize() . = ..() - chameleon_action.emp_randomise(INFINITY) \ No newline at end of file + chameleon_action.emp_randomise(INFINITY) diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index adb35b0fb3b..d816e067502 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -1,803 +1,803 @@ -/obj/item/clothing - name = "clothing" - max_integrity = 200 - integrity_failure = 80 - resistance_flags = FLAMMABLE - var/list/species_restricted = null //Only these species can wear this kit. - var/scan_reagents = 0 //Can the wearer see reagents while it's equipped? - - /* - Sprites used when the clothing item is refit. This is done by setting icon_override. - For best results, if this is set then sprite_sheets should be null and vice versa, but that is by no means necessary. - Ideally, sprite_sheets_refit should be used for "hard" clothing items that can't change shape very well to fit the wearer (e.g. helmets, hardsuits), - while sprite_sheets should be used for "flexible" clothing items that do not need to be refitted (e.g. vox wearing jumpsuits). - */ - var/list/sprite_sheets_refit = null - lefthand_file = 'icons/mob/inhands/clothing_lefthand.dmi' - righthand_file = 'icons/mob/inhands/clothing_righthand.dmi' - var/alt_desc = null - var/flash_protect = 0 //What level of bright light protection item has. 1 = Flashers, Flashes, & Flashbangs | 2 = Welding | -1 = OH GOD WELDING BURNT OUT MY RETINAS - var/tint = 0 //Sets the item's level of visual impairment tint, normally set to the same as flash_protect - var/up = 0 //but seperated to allow items to protect but not impair vision, like space helmets - - var/visor_flags = 0 //flags that are added/removed when an item is adjusted up/down - var/visor_flags_inv = 0 //same as visor_flags, but for flags_inv - var/visor_vars_to_toggle = VISOR_FLASHPROTECT | VISOR_TINT | VISOR_VISIONFLAGS | VISOR_DARKNESSVIEW | VISOR_INVISVIEW //what to toggle when toggled with weldingvisortoggle() - - var/toggle_message = null - var/alt_toggle_message = null - var/active_sound = null - var/toggle_sound = null - var/toggle_cooldown = null - var/cooldown = 0 - var/species_disguise = null - var/magical = FALSE - -/obj/item/clothing/proc/weldingvisortoggle(mob/user) //proc to toggle welding visors on helmets, masks, goggles, etc. - if(!can_use(user)) - return FALSE - - visor_toggling() - - to_chat(user, "You adjust \the [src] [up ? "up" : "down"].") - - if(iscarbon(user)) - var/mob/living/carbon/C = user - C.head_update(src, forced = 1) - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - return TRUE - -/obj/item/clothing/proc/visor_toggling() //handles all the actual toggling of flags - up = !up - flags ^= visor_flags - flags_inv ^= visor_flags_inv - flags_cover ^= initial(flags_cover) - icon_state = "[initial(icon_state)][up ? "up" : ""]" - if(visor_vars_to_toggle & VISOR_FLASHPROTECT) - flash_protect ^= initial(flash_protect) - if(visor_vars_to_toggle & VISOR_TINT) - tint ^= initial(tint) - -/obj/item/clothing/proc/can_use(mob/user) - if(user && ismob(user)) - if(!user.incapacitated()) - return TRUE - return FALSE - -//BS12: Species-restricted clothing check. -/obj/item/clothing/mob_can_equip(M as mob, slot) - - //if we can't equip the item anyway, don't bother with species_restricted (also cuts down on spam) - if(!..()) - return 0 - - // Skip species restriction checks on non-equipment slots - if(slot in list(slot_r_hand, slot_l_hand, slot_in_backpack, slot_l_store, slot_r_store)) - return 1 - - if(species_restricted && istype(M,/mob/living/carbon/human)) - - var/wearable = null - var/exclusive = null - var/mob/living/carbon/human/H = M - - if("exclude" in species_restricted) - exclusive = 1 - - if(H.dna.species) - if(exclusive) - if(!(H.dna.species.name in species_restricted)) - wearable = 1 - else - if(H.dna.species.name in species_restricted) - wearable = 1 - - if(!wearable) - to_chat(M, "Your species cannot wear [src].") - return 0 - - return 1 - -/obj/item/clothing/proc/refit_for_species(var/target_species) - //Set species_restricted list - switch(target_species) - if("Human", "Skrell") //humanoid bodytypes - species_restricted = list("exclude","Unathi","Tajaran","Diona","Vox","Wryn","Drask") - else - species_restricted = list(target_species) - - //Set icon - if(sprite_sheets && (target_species in sprite_sheets)) - icon_override = sprite_sheets[target_species] - else - icon_override = initial(icon_override) - - if(sprite_sheets_obj && (target_species in sprite_sheets_obj)) - icon = sprite_sheets_obj[target_species] - else - icon = initial(icon) - -//Ears: currently only used for headsets and earmuffs -/obj/item/clothing/ears - name = "ears" - w_class = WEIGHT_CLASS_TINY - throwforce = 2 - slot_flags = SLOT_EARS - resistance_flags = NONE - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/ears.dmi', - "Vox Armalis" = 'icons/mob/species/armalis/ears.dmi' - ) //We read you loud and skree-er. - -/obj/item/clothing/ears/attack_hand(mob/user) - if(!user) - return - - if(loc != user || !ishuman(user)) - ..() - return - - var/mob/living/carbon/human/H = user - if(H.l_ear != src && H.r_ear != src) - ..() - return - - if(!usr.canUnEquip(src)) - return - - var/obj/item/clothing/ears/O - if(slot_flags & SLOT_TWOEARS ) - O = (H.l_ear == src ? H.r_ear : H.l_ear) - user.unEquip(O) - if(!istype(src, /obj/item/clothing/ears/offear)) - qdel(O) - O = src - else - O = src - - user.unEquip(src) - - if(O) - user.put_in_hands(O) - O.add_fingerprint(user) - - if(istype(src, /obj/item/clothing/ears/offear)) - qdel(src) - - -/obj/item/clothing/ears/offear - name = "Other ear" - w_class = WEIGHT_CLASS_HUGE - icon = 'icons/mob/screen_gen.dmi' - icon_state = "block" - slot_flags = SLOT_EARS | SLOT_TWOEARS - -/obj/item/clothing/ears/offear/New(var/obj/O) - name = O.name - desc = O.desc - icon = O.icon - icon_state = O.icon_state - dir = O.dir - - -//Glasses -/obj/item/clothing/glasses - name = "glasses" - icon = 'icons/obj/clothing/glasses.dmi' - w_class = WEIGHT_CLASS_SMALL - flags_cover = GLASSESCOVERSEYES - slot_flags = SLOT_EYES - materials = list(MAT_GLASS = 250) - var/vision_flags = 0 - var/see_in_dark = 0 //Base human is 2 - var/invis_view = SEE_INVISIBLE_LIVING - var/invis_override = 0 - var/lighting_alpha - - var/emagged = 0 - var/list/color_view = null//overrides client.color while worn - var/prescription = 0 - var/prescription_upgradable = 0 - var/over_mask = FALSE //Whether or not the eyewear is rendered above the mask. Purely cosmetic. - strip_delay = 20 // but seperated to allow items to protect but not impair vision, like space helmets - put_on_delay = 25 - resistance_flags = NONE - species_restricted = list("exclude","Kidan") -/* -SEE_SELF // can see self, no matter what -SEE_MOBS // can see all mobs, no matter what -SEE_OBJS // can see all objs, no matter what -SEE_TURFS // can see all turfs (and areas), no matter what -SEE_PIXELS// if an object is located on an unlit area, but some of its pixels are - // in a lit area (via pixel_x,y or smooth movement), can see those pixels -BLIND // can't see anything -*/ - -/obj/item/clothing/glasses/verb/adjust_eyewear() //Adjust eyewear to be worn above or below the mask. - set name = "Adjust Eyewear" - set category = "Object" - set desc = "Adjust your eyewear to be worn over or under a mask." - set src in usr - - var/mob/living/carbon/human/user = usr - if(!istype(user)) - return - if(user.incapacitated()) //Dead spessmen adjust no glasses. Resting/buckled ones do, though - return - - var/action_fluff = "You adjust \the [src]" - if(user.glasses == src) - if(!user.canUnEquip(src)) - to_chat(usr, "[src] is stuck to you!") - return - if(attack_hand(user)) //Remove the glasses for this action. Prevents logic-defying instances where glasses phase through your mask as it ascends/descends to another plane of existence. - action_fluff = "You remove \the [src] and adjust it" - - over_mask = !over_mask - to_chat(user, "[action_fluff] to be worn [over_mask ? "over" : "under"] a mask.") - -//Gloves -/obj/item/clothing/gloves - name = "gloves" - gender = PLURAL //Carn: for grammarically correct text-parsing - w_class = WEIGHT_CLASS_SMALL - icon = 'icons/obj/clothing/gloves.dmi' - siemens_coefficient = 0.50 - body_parts_covered = HANDS - slot_flags = SLOT_GLOVES - attack_verb = list("challenged") - var/transfer_prints = FALSE - var/pickpocket = 0 //Master pickpocket? - var/clipped = 0 - strip_delay = 20 - put_on_delay = 40 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/gloves.dmi', - "Drask" = 'icons/mob/species/drask/gloves.dmi' - ) - -// Called just before an attack_hand(), in mob/UnarmedAttack() -/obj/item/clothing/gloves/proc/Touch(atom/A, proximity) - return 0 // return 1 to cancel attack_hand() - -/obj/item/clothing/gloves/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/wirecutters)) - if(!clipped) - playsound(src.loc, W.usesound, 100, 1) - user.visible_message("[user] snips the fingertips off [src].","You snip the fingertips off [src].") - clipped = 1 - name = "mangled [name]" - desc = "[desc] They have had the fingertips cut off of them." - update_icon() - else - to_chat(user, "[src] have already been clipped!") - return - else - return ..() - -/obj/item/clothing/under/proc/set_sensors(mob/user as mob) - var/mob/M = user - if(istype(M, /mob/dead/)) return - if(user.stat || user.restrained()) return - if(has_sensor >= 2) - to_chat(user, "The controls are locked.") - return 0 - if(has_sensor <= 0) - to_chat(user, "This suit does not have any sensors.") - return 0 - - var/list/modes = list("Off", "Binary sensors", "Vitals tracker", "Tracking beacon") - var/switchMode = input("Select a sensor mode:", "Suit Sensor Mode", modes[sensor_mode + 1]) in modes - if(get_dist(user, src) > 1) - to_chat(user, "You have moved too far away.") - return - sensor_mode = modes.Find(switchMode) - 1 - - if(src.loc == user) - switch(sensor_mode) - if(0) - to_chat(user, "You disable your suit's remote sensing equipment.") - if(1) - to_chat(user, "Your suit will now report whether you are live or dead.") - if(2) - to_chat(user, "Your suit will now report your vital lifesigns.") - if(3) - to_chat(user, "Your suit will now report your vital lifesigns as well as your coordinate position.") - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - if(H.w_uniform == src) - H.update_suit_sensors() - - else if(istype(src.loc, /mob)) - switch(sensor_mode) - if(0) - for(var/mob/V in viewers(user, 1)) - V.show_message("[user] disables [src.loc]'s remote sensing equipment.", 1) - if(1) - for(var/mob/V in viewers(user, 1)) - V.show_message("[user] turns [src.loc]'s remote sensors to binary.", 1) - if(2) - for(var/mob/V in viewers(user, 1)) - V.show_message("[user] sets [src.loc]'s sensors to track vitals.", 1) - if(3) - for(var/mob/V in viewers(user, 1)) - V.show_message("[user] sets [src.loc]'s sensors to maximum.", 1) - if(istype(src,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = src - if(H.w_uniform == src) - H.update_suit_sensors() - -/obj/item/clothing/under/verb/toggle() - set name = "Toggle Suit Sensors" - set category = "Object" - set src in usr - set_sensors(usr) - ..() - -//Head -/obj/item/clothing/head - name = "head" - icon = 'icons/obj/clothing/hats.dmi' - body_parts_covered = HEAD - slot_flags = SLOT_HEAD - var/blockTracking // Do we block AI tracking? - var/HUDType = null - - var/vision_flags = 0 - var/see_in_dark = 0 - var/lighting_alpha - - var/can_toggle = null - -//Mask -/obj/item/clothing/mask - name = "mask" - icon = 'icons/obj/clothing/masks.dmi' - body_parts_covered = HEAD - slot_flags = SLOT_MASK - var/mask_adjusted = 0 - var/adjusted_flags = null - strip_delay = 40 - put_on_delay = 40 - -//Proc that moves gas/breath masks out of the way -/obj/item/clothing/mask/proc/adjustmask(var/mob/user) - var/mob/living/carbon/human/H = usr //Used to check if the mask is on the head, to check if the hands are full, and to turn off internals if they were on when the mask was pushed out of the way. - if(user.incapacitated()) //This check allows you to adjust your masks while you're buckled into chairs or beds. - return - if(mask_adjusted) - icon_state = initial(icon_state) - gas_transfer_coefficient = initial(gas_transfer_coefficient) - permeability_coefficient = initial(permeability_coefficient) - to_chat(user, "You push \the [src] back into place.") - mask_adjusted = 0 - slot_flags = initial(slot_flags) - if(flags_inv != initial(flags_inv)) - if(initial(flags_inv) & HIDEFACE) //If the mask is one that hides the face and can be adjusted yet lost that trait when it was adjusted, make it hide the face again. - flags_inv |= HIDEFACE - if(flags != initial(flags)) - if(initial(flags) & AIRTIGHT) //If the mask is airtight and thus, one that you'd be able to run internals from yet can't because it was adjusted, make it airtight again. - flags |= AIRTIGHT - if(flags_cover != initial(flags_cover)) - if(initial(flags_cover) & MASKCOVERSMOUTH) //If the mask covers the mouth when it's down and can be adjusted yet lost that trait when it was adjusted, make it cover the mouth again. - flags_cover |= MASKCOVERSMOUTH - if(H.head == src && flags_inv == HIDEFACE) //Means that only things like bandanas and balaclavas will be affected since they obscure the identity of the wearer. - if(H.l_hand && H.r_hand) //If both hands are occupied, drop the object on the ground. - user.unEquip(src) - else //Otherwise, put it in an available hand, the active one preferentially. - src.loc = user - H.head = null - user.put_in_hands(src) - else - icon_state += "_up" - to_chat(user, "You push \the [src] out of the way.") - gas_transfer_coefficient = null - permeability_coefficient = null - mask_adjusted = 1 - if(adjusted_flags) - slot_flags = adjusted_flags - if(ishuman(user) && H.internal && !H.get_organ_slot("breathing_tube") && user.wear_mask == src) /*If the user was wearing the mask providing internals on their face at the time it was adjusted, turn off internals. - Otherwise, they adjusted it while it was in their hands or some such so we won't be needing to turn off internals.*/ - H.internal = null - H.update_action_buttons_icon() - if(flags_inv & HIDEFACE) //Means that only things like bandanas and balaclavas will be affected since they obscure the identity of the wearer. - flags_inv &= ~HIDEFACE /*Done after the above to avoid having to do a check for initial(src.flags_inv == HIDEFACE). - This reveals the user's face since the bandana will now be going on their head.*/ - if(flags_cover & MASKCOVERSMOUTH) //Mask won't cover the mouth any more since it's been pushed out of the way. Allows for CPRing with adjusted masks. - flags_cover &= ~MASKCOVERSMOUTH - if(flags & AIRTIGHT) //If the mask was airtight, it won't be anymore since you just pushed it off your face. - flags &= ~AIRTIGHT - if(user.wear_mask == src && initial(flags_inv) == HIDEFACE) //Means that you won't have to take off and put back on simple things like breath masks which, realistically, can just be pulled down off your face. - if(H.l_hand && H.r_hand) //If both hands are occupied, drop the object on the ground. - user.unEquip(src) - else //Otherwise, put it in an available hand, the active one preferentially. - src.loc = user - user.wear_mask = null - user.put_in_hands(src) - H.wear_mask_update(src, toggle_off = mask_adjusted) - usr.update_inv_wear_mask() - usr.update_inv_head() - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - -//Shoes -/obj/item/clothing/shoes - name = "shoes" - icon = 'icons/obj/clothing/shoes.dmi' - desc = "Comfortable-looking shoes." - gender = PLURAL //Carn: for grammatically correct text-parsing - var/chained = 0 - var/can_cut_open = 0 - var/cut_open = 0 - body_parts_covered = FEET - slot_flags = SLOT_FEET - - var/silence_steps = 0 - var/shoe_sound_footstep = 1 - var/shoe_sound = null - var/blood_state = BLOOD_STATE_NOT_BLOODY - var/list/bloody_shoes = list(BLOOD_STATE_HUMAN = 0, BLOOD_STATE_XENO = 0, BLOOD_STATE_NOT_BLOODY = 0) - - permeability_coefficient = 0.50 - slowdown = SHOES_SLOWDOWN - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/shoes.dmi', - "Drask" = 'icons/mob/species/drask/shoes.dmi' - ) - -/obj/item/clothing/shoes/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/match) && src.loc == user) - var/obj/item/match/M = I - if(M.matchignite()) // Match isn't lit, but isn't burnt. - user.visible_message("[user] strikes a [M] on the bottom of [src], lighting it.","You strike the [M] on the bottom of [src] to light it.") - playsound(user.loc, 'sound/goonstation/misc/matchstick_light.ogg', 50, 1) - else - user.visible_message("[user] crushes the [M] into the bottom of [src], extinguishing it.","You crush the [M] into the bottom of [src], extinguishing it.") - M.dropped() - return - - if(istype(I, /obj/item/wirecutters)) - if(can_cut_open) - if(!cut_open) - playsound(src.loc, I.usesound, 100, 1) - user.visible_message("[user] cuts open the toes of [src].","You cut open the toes of [src].") - cut_open = 1 - icon_state = "[icon_state]_opentoe" - item_state = "[item_state]_opentoe" - name = "mangled [name]" - desc = "[desc] They have had their toes opened up." - update_icon() - else - to_chat(user, "[src] have already had [p_their()] toes cut open!") - return - else - return ..() - -/obj/item/clothing/shoes/proc/step_action(var/mob/living/carbon/human/H) //squeek squeek - SEND_SIGNAL(src, COMSIG_SHOES_STEP_ACTION) - if(shoe_sound) - var/turf/T = get_turf(H) - - if(!istype(H) || !istype(T)) - return 0 - - if(H.m_intent == MOVE_INTENT_RUN) - if(shoe_sound_footstep >= 2) - if(T.shoe_running_volume) - playsound(src, shoe_sound, T.shoe_running_volume, 1) - shoe_sound_footstep = 0 - else - shoe_sound_footstep++ - else if(T.shoe_walking_volume) - playsound(src, shoe_sound, T.shoe_walking_volume, 1) - - return 1 - -/obj/item/proc/negates_gravity() - return 0 - -//Suit -/obj/item/clothing/suit - icon = 'icons/obj/clothing/suits.dmi' - name = "suit" - var/fire_resist = T0C+100 - allowed = list(/obj/item/tank/emergency_oxygen) - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - slot_flags = SLOT_OCLOTHING - var/blood_overlay_type = "suit" - var/suittoggled = FALSE - var/suit_adjusted = 0 - var/ignore_suitadjust = 1 - var/adjust_flavour = null - var/list/hide_tail_by_species = null - -//Proc that opens and closes jackets. -/obj/item/clothing/suit/proc/adjustsuit(var/mob/user) - if(!ignore_suitadjust) - if(!user.incapacitated()) - if(!(HULK in user.mutations)) - if(suit_adjusted) - var/flavour = "close" - icon_state = copytext(icon_state, 1, findtext(icon_state, "_open")) /*Trims the '_open' off the end of the icon state, thus avoiding a case where jackets that start open will - end up with a suffix of _open_open if adjusted twice, since their initial state is _open. */ - item_state = copytext(item_state, 1, findtext(item_state, "_open")) - if(adjust_flavour) - flavour = "[copytext(adjust_flavour, 3, length(adjust_flavour) + 1)] up" //Trims off the 'un' at the beginning of the word. unzip -> zip, unbutton->button. - to_chat(user, "You [flavour] \the [src].") - suit_adjusted = 0 //Suit is no longer adjusted. - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - else - var/flavour = "open" - icon_state += "_open" - item_state += "_open" - if(adjust_flavour) - flavour = "[adjust_flavour]" - to_chat(user, "You [flavour] \the [src].") - suit_adjusted = 1 //Suit's adjusted. - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - else - if(user.canUnEquip(src)) //Checks to see if the item can be unequipped. If so, lets shred. Otherwise, struggle and fail. - if(contents) //If the suit's got any storage capability... - for(var/obj/item/O in contents) //AVOIDING ITEM LOSS. Check through everything that's stored in the jacket and see if one of the items is a pocket. - if(istype(O, /obj/item/storage/internal)) //If it's a pocket... - if(O.contents) //Check to see if the pocket's got anything in it. - for(var/obj/item/I in O.contents) //Dump the pocket out onto the floor below the user. - user.unEquip(I,1) - - user.visible_message("[user] bellows, [pick("shredding", "ripping open", "tearing off")] [user.p_their()] jacket in a fit of rage!","You accidentally [pick("shred", "rend", "tear apart")] [src] with your [pick("excessive", "extreme", "insane", "monstrous", "ridiculous", "unreal", "stupendous")] [pick("power", "strength")]!") - user.unEquip(src) - qdel(src) //Now that the pockets have been emptied, we can safely destroy the jacket. - user.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!")) - else - to_chat(user, "You yank and pull at \the [src] with your [pick("excessive", "extreme", "insane", "monstrous", "ridiculous", "unreal", "stupendous")] [pick("power", "strength")], however you are unable to change its state!")//Yep, that's all they get. Avoids having to snowflake in a cooldown. - - return - user.update_inv_wear_suit() - else - to_chat(user, "You attempt to button up the velcro on \the [src], before promptly realising how retarded you are.") - -/obj/item/clothing/suit/equipped(var/mob/living/carbon/human/user, var/slot) //Handle tail-hiding on a by-species basis. - ..() - if(ishuman(user) && hide_tail_by_species && slot == slot_wear_suit) - if(user.dna.species.name in hide_tail_by_species) - if(!(flags_inv & HIDETAIL)) //Hide the tail if the user's species is in the hide_tail_by_species list and the tail isn't already hidden. - flags_inv |= HIDETAIL - else - if(!(initial(flags_inv) & HIDETAIL) && (flags_inv & HIDETAIL)) //Otherwise, remove the HIDETAIL flag if it wasn't already in the flags_inv to start with. - flags_inv &= ~HIDETAIL - -/obj/item/clothing/suit/ui_action_click(mob/user) //This is what happens when you click the HUD action button to adjust your suit. - if(!ignore_suitadjust) - adjustsuit(user) - else - ..() //This is required in order to ensure that the UI buttons for items that have alternate functions tied to UI buttons still work. - -/obj/item/clothing/suit/proc/special_overlays() // Does it have special overlays when worn? - return FALSE - -//Spacesuit -//Note: Everything in modules/clothing/spacesuits should have the entire suit grouped together. -// Meaning the the suit is defined directly after the corrisponding helmet. Just like below! -/obj/item/clothing/head/helmet/space - name = "Space helmet" - icon_state = "space" - desc = "A special helmet designed for work in a hazardous, low-pressure environment." - flags = BLOCKHAIR | STOPSPRESSUREDMAGE | THICKMATERIAL - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - item_state = "s_helmet" - permeability_coefficient = 0.01 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 50, "fire" = 80, "acid" = 70) - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - cold_protection = HEAD - min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT - heat_protection = HEAD - max_heat_protection_temperature = SPACE_HELM_MAX_TEMP_PROTECT - species_restricted = list("exclude","Wryn") - flash_protect = 2 - strip_delay = 50 - put_on_delay = 50 - resistance_flags = NONE - dog_fashion = null - - -/obj/item/clothing/suit/space - name = "Space suit" - desc = "A suit that protects against low pressure environments. Has a big 13 on the back." - icon_state = "space" - item_state = "s_suit" - w_class = WEIGHT_CLASS_BULKY - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.02 - flags = STOPSPRESSUREDMAGE | THICKMATERIAL - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - allowed = list(/obj/item/flashlight,/obj/item/tank) - slowdown = 1 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 50, "fire" = 80, "acid" = 70) - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL - cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS - min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT - heat_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS - max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT - strip_delay = 80 - put_on_delay = 80 - resistance_flags = NONE - hide_tail_by_species = null - species_restricted = list("exclude","Wryn") - - -//Under clothing -/obj/item/clothing/under - icon = 'icons/obj/clothing/uniforms.dmi' - name = "under" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - permeability_coefficient = 0.90 - slot_flags = SLOT_ICLOTHING - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/uniform.dmi', - "Drask" = 'icons/mob/species/drask/uniform.dmi', - "Grey" = 'icons/mob/species/grey/uniform.dmi' - ) - - var/has_sensor = TRUE//For the crew computer 2 = unable to change mode - var/sensor_mode = SENSOR_OFF - var/random_sensor = TRUE - /* - 1 = Report living/dead - 2 = Report detailed damages - 3 = Report location - */ - var/list/accessories = list() - var/displays_id = 1 - var/rolled_down = 0 - var/basecolor - -/obj/item/clothing/under/rank/New() - if(random_sensor) - sensor_mode = pick(SENSOR_OFF, SENSOR_LIVING, SENSOR_VITALS, SENSOR_COORDS) - ..() - -/obj/item/clothing/under/Destroy() - QDEL_LIST(accessories) - return ..() - -/obj/item/clothing/under/proc/can_attach_accessory(obj/item/clothing/accessory/A) - if(istype(A)) - . = TRUE - else - return FALSE - - if(accessories.len) - for(var/obj/item/clothing/accessory/AC in accessories) - if((A.slot in list(ACCESSORY_SLOT_UTILITY, ACCESSORY_SLOT_ARMBAND)) && AC.slot == A.slot) - return FALSE - if(!A.allow_duplicates && AC.type == A.type) - return FALSE - -/obj/item/clothing/under/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/clothing/accessory)) - attach_accessory(I, user, TRUE) - - if(accessories.len) - for(var/obj/item/clothing/accessory/A in accessories) - A.attackby(I, user, params) - return TRUE - - . = ..() - -/obj/item/clothing/under/proc/attach_accessory(obj/item/clothing/accessory/A, mob/user, unequip = FALSE) - if(can_attach_accessory(A)) - if(unequip && !user.unEquip(A)) // Make absolutely sure this accessory is removed from hands - return FALSE - - accessories += A - A.on_attached(src, user) - - if(ishuman(loc)) - var/mob/living/carbon/human/H = loc - H.update_inv_w_uniform() - - return TRUE - else - to_chat(user, "You cannot attach more accessories of this type to [src].") - - return FALSE - -/obj/item/clothing/under/examine(mob/user) - . = ..() - switch(sensor_mode) - if(0) - . += "Its sensors appear to be disabled." - if(1) - . += "Its binary life sensors appear to be enabled." - if(2) - . += "Its vital tracker appears to be enabled." - if(3) - . += "Its vital tracker and tracking beacon appear to be enabled." - if(accessories.len) - for(var/obj/item/clothing/accessory/A in accessories) - . += "\A [A] is attached to it." - - -/obj/item/clothing/under/verb/rollsuit() - set name = "Roll Down Jumpsuit" - set category = "Object" - set src in usr - if(!istype(usr, /mob/living)) return - if(usr.stat) return - - if(!usr.incapacitated()) - if(copytext(item_color,-2) != "_d") - basecolor = item_color - if(basecolor + "_d_s" in icon_states('icons/mob/uniform.dmi')) - item_color = item_color == "[basecolor]" ? "[basecolor]_d" : "[basecolor]" - usr.update_inv_w_uniform() - else - to_chat(usr, "You cannot roll down this uniform!") - else - to_chat(usr, "You cannot roll down the uniform!") - -/obj/item/clothing/under/verb/removetie() - set name = "Remove Accessory" - set category = "Object" - set src in usr - handle_accessories_removal() - -/obj/item/clothing/under/proc/handle_accessories_removal() - if(!isliving(usr)) - return - if(usr.incapacitated()) - return - if(!Adjacent(usr)) - return - if(!accessories.len) - return - var/obj/item/clothing/accessory/A - if(accessories.len > 1) - A = input("Select an accessory to remove from [src]") as null|anything in accessories - else - A = accessories[1] - remove_accessory(usr,A) - -/obj/item/clothing/under/proc/remove_accessory(mob/user, obj/item/clothing/accessory/A) - if(!(A in accessories)) - return - if(!isliving(user)) - return - if(user.incapacitated()) - return - if(!Adjacent(user)) - return - A.on_removed(user) - accessories -= A - to_chat(user, "You remove [A] from [src].") - usr.update_inv_w_uniform() - -/obj/item/clothing/under/emp_act(severity) - if(accessories.len) - for(var/obj/item/clothing/accessory/A in accessories) - A.emp_act(severity) - ..() - -/obj/item/clothing/under/AltClick() - handle_accessories_removal() - -/obj/item/clothing/obj_destruction(damage_flag) - if(damage_flag == "bomb" || damage_flag == "melee") - var/turf/T = get_turf(src) - spawn(1) //so the shred survives potential turf change from the explosion. - var/obj/effect/decal/cleanable/shreds/Shreds = new(T) - Shreds.desc = "The sad remains of what used to be [name]." - deconstruct(FALSE) - else - ..() \ No newline at end of file +/obj/item/clothing + name = "clothing" + max_integrity = 200 + integrity_failure = 80 + resistance_flags = FLAMMABLE + var/list/species_restricted = null //Only these species can wear this kit. + var/scan_reagents = 0 //Can the wearer see reagents while it's equipped? + + /* + Sprites used when the clothing item is refit. This is done by setting icon_override. + For best results, if this is set then sprite_sheets should be null and vice versa, but that is by no means necessary. + Ideally, sprite_sheets_refit should be used for "hard" clothing items that can't change shape very well to fit the wearer (e.g. helmets, hardsuits), + while sprite_sheets should be used for "flexible" clothing items that do not need to be refitted (e.g. vox wearing jumpsuits). + */ + var/list/sprite_sheets_refit = null + lefthand_file = 'icons/mob/inhands/clothing_lefthand.dmi' + righthand_file = 'icons/mob/inhands/clothing_righthand.dmi' + var/alt_desc = null + var/flash_protect = 0 //What level of bright light protection item has. 1 = Flashers, Flashes, & Flashbangs | 2 = Welding | -1 = OH GOD WELDING BURNT OUT MY RETINAS + var/tint = 0 //Sets the item's level of visual impairment tint, normally set to the same as flash_protect + var/up = 0 //but seperated to allow items to protect but not impair vision, like space helmets + + var/visor_flags = 0 //flags that are added/removed when an item is adjusted up/down + var/visor_flags_inv = 0 //same as visor_flags, but for flags_inv + var/visor_vars_to_toggle = VISOR_FLASHPROTECT | VISOR_TINT | VISOR_VISIONFLAGS | VISOR_DARKNESSVIEW | VISOR_INVISVIEW //what to toggle when toggled with weldingvisortoggle() + + var/toggle_message = null + var/alt_toggle_message = null + var/active_sound = null + var/toggle_sound = null + var/toggle_cooldown = null + var/cooldown = 0 + var/species_disguise = null + var/magical = FALSE + +/obj/item/clothing/proc/weldingvisortoggle(mob/user) //proc to toggle welding visors on helmets, masks, goggles, etc. + if(!can_use(user)) + return FALSE + + visor_toggling() + + to_chat(user, "You adjust \the [src] [up ? "up" : "down"].") + + if(iscarbon(user)) + var/mob/living/carbon/C = user + C.head_update(src, forced = 1) + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + return TRUE + +/obj/item/clothing/proc/visor_toggling() //handles all the actual toggling of flags + up = !up + flags ^= visor_flags + flags_inv ^= visor_flags_inv + flags_cover ^= initial(flags_cover) + icon_state = "[initial(icon_state)][up ? "up" : ""]" + if(visor_vars_to_toggle & VISOR_FLASHPROTECT) + flash_protect ^= initial(flash_protect) + if(visor_vars_to_toggle & VISOR_TINT) + tint ^= initial(tint) + +/obj/item/clothing/proc/can_use(mob/user) + if(user && ismob(user)) + if(!user.incapacitated()) + return TRUE + return FALSE + +//BS12: Species-restricted clothing check. +/obj/item/clothing/mob_can_equip(M as mob, slot) + + //if we can't equip the item anyway, don't bother with species_restricted (also cuts down on spam) + if(!..()) + return 0 + + // Skip species restriction checks on non-equipment slots + if(slot in list(slot_r_hand, slot_l_hand, slot_in_backpack, slot_l_store, slot_r_store)) + return 1 + + if(species_restricted && istype(M,/mob/living/carbon/human)) + + var/wearable = null + var/exclusive = null + var/mob/living/carbon/human/H = M + + if("exclude" in species_restricted) + exclusive = 1 + + if(H.dna.species) + if(exclusive) + if(!(H.dna.species.name in species_restricted)) + wearable = 1 + else + if(H.dna.species.name in species_restricted) + wearable = 1 + + if(!wearable) + to_chat(M, "Your species cannot wear [src].") + return 0 + + return 1 + +/obj/item/clothing/proc/refit_for_species(var/target_species) + //Set species_restricted list + switch(target_species) + if("Human", "Skrell") //humanoid bodytypes + species_restricted = list("exclude","Unathi","Tajaran","Diona","Vox","Wryn","Drask") + else + species_restricted = list(target_species) + + //Set icon + if(sprite_sheets && (target_species in sprite_sheets)) + icon_override = sprite_sheets[target_species] + else + icon_override = initial(icon_override) + + if(sprite_sheets_obj && (target_species in sprite_sheets_obj)) + icon = sprite_sheets_obj[target_species] + else + icon = initial(icon) + +//Ears: currently only used for headsets and earmuffs +/obj/item/clothing/ears + name = "ears" + w_class = WEIGHT_CLASS_TINY + throwforce = 2 + slot_flags = SLOT_EARS + resistance_flags = NONE + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/ears.dmi', + "Vox Armalis" = 'icons/mob/species/armalis/ears.dmi' + ) //We read you loud and skree-er. + +/obj/item/clothing/ears/attack_hand(mob/user) + if(!user) + return + + if(loc != user || !ishuman(user)) + ..() + return + + var/mob/living/carbon/human/H = user + if(H.l_ear != src && H.r_ear != src) + ..() + return + + if(!usr.canUnEquip(src)) + return + + var/obj/item/clothing/ears/O + if(slot_flags & SLOT_TWOEARS ) + O = (H.l_ear == src ? H.r_ear : H.l_ear) + user.unEquip(O) + if(!istype(src, /obj/item/clothing/ears/offear)) + qdel(O) + O = src + else + O = src + + user.unEquip(src) + + if(O) + user.put_in_hands(O) + O.add_fingerprint(user) + + if(istype(src, /obj/item/clothing/ears/offear)) + qdel(src) + + +/obj/item/clothing/ears/offear + name = "Other ear" + w_class = WEIGHT_CLASS_HUGE + icon = 'icons/mob/screen_gen.dmi' + icon_state = "block" + slot_flags = SLOT_EARS | SLOT_TWOEARS + +/obj/item/clothing/ears/offear/New(var/obj/O) + name = O.name + desc = O.desc + icon = O.icon + icon_state = O.icon_state + dir = O.dir + + +//Glasses +/obj/item/clothing/glasses + name = "glasses" + icon = 'icons/obj/clothing/glasses.dmi' + w_class = WEIGHT_CLASS_SMALL + flags_cover = GLASSESCOVERSEYES + slot_flags = SLOT_EYES + materials = list(MAT_GLASS = 250) + var/vision_flags = 0 + var/see_in_dark = 0 //Base human is 2 + var/invis_view = SEE_INVISIBLE_LIVING + var/invis_override = 0 + var/lighting_alpha + + var/emagged = 0 + var/list/color_view = null//overrides client.color while worn + var/prescription = 0 + var/prescription_upgradable = 0 + var/over_mask = FALSE //Whether or not the eyewear is rendered above the mask. Purely cosmetic. + strip_delay = 20 // but seperated to allow items to protect but not impair vision, like space helmets + put_on_delay = 25 + resistance_flags = NONE + species_restricted = list("exclude","Kidan") +/* +SEE_SELF // can see self, no matter what +SEE_MOBS // can see all mobs, no matter what +SEE_OBJS // can see all objs, no matter what +SEE_TURFS // can see all turfs (and areas), no matter what +SEE_PIXELS// if an object is located on an unlit area, but some of its pixels are + // in a lit area (via pixel_x,y or smooth movement), can see those pixels +BLIND // can't see anything +*/ + +/obj/item/clothing/glasses/verb/adjust_eyewear() //Adjust eyewear to be worn above or below the mask. + set name = "Adjust Eyewear" + set category = "Object" + set desc = "Adjust your eyewear to be worn over or under a mask." + set src in usr + + var/mob/living/carbon/human/user = usr + if(!istype(user)) + return + if(user.incapacitated()) //Dead spessmen adjust no glasses. Resting/buckled ones do, though + return + + var/action_fluff = "You adjust \the [src]" + if(user.glasses == src) + if(!user.canUnEquip(src)) + to_chat(usr, "[src] is stuck to you!") + return + if(attack_hand(user)) //Remove the glasses for this action. Prevents logic-defying instances where glasses phase through your mask as it ascends/descends to another plane of existence. + action_fluff = "You remove \the [src] and adjust it" + + over_mask = !over_mask + to_chat(user, "[action_fluff] to be worn [over_mask ? "over" : "under"] a mask.") + +//Gloves +/obj/item/clothing/gloves + name = "gloves" + gender = PLURAL //Carn: for grammarically correct text-parsing + w_class = WEIGHT_CLASS_SMALL + icon = 'icons/obj/clothing/gloves.dmi' + siemens_coefficient = 0.50 + body_parts_covered = HANDS + slot_flags = SLOT_GLOVES + attack_verb = list("challenged") + var/transfer_prints = FALSE + var/pickpocket = 0 //Master pickpocket? + var/clipped = 0 + strip_delay = 20 + put_on_delay = 40 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/gloves.dmi', + "Drask" = 'icons/mob/species/drask/gloves.dmi' + ) + +// Called just before an attack_hand(), in mob/UnarmedAttack() +/obj/item/clothing/gloves/proc/Touch(atom/A, proximity) + return 0 // return 1 to cancel attack_hand() + +/obj/item/clothing/gloves/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/wirecutters)) + if(!clipped) + playsound(src.loc, W.usesound, 100, 1) + user.visible_message("[user] snips the fingertips off [src].","You snip the fingertips off [src].") + clipped = 1 + name = "mangled [name]" + desc = "[desc] They have had the fingertips cut off of them." + update_icon() + else + to_chat(user, "[src] have already been clipped!") + return + else + return ..() + +/obj/item/clothing/under/proc/set_sensors(mob/user as mob) + var/mob/M = user + if(istype(M, /mob/dead/)) return + if(user.stat || user.restrained()) return + if(has_sensor >= 2) + to_chat(user, "The controls are locked.") + return 0 + if(has_sensor <= 0) + to_chat(user, "This suit does not have any sensors.") + return 0 + + var/list/modes = list("Off", "Binary sensors", "Vitals tracker", "Tracking beacon") + var/switchMode = input("Select a sensor mode:", "Suit Sensor Mode", modes[sensor_mode + 1]) in modes + if(get_dist(user, src) > 1) + to_chat(user, "You have moved too far away.") + return + sensor_mode = modes.Find(switchMode) - 1 + + if(src.loc == user) + switch(sensor_mode) + if(0) + to_chat(user, "You disable your suit's remote sensing equipment.") + if(1) + to_chat(user, "Your suit will now report whether you are live or dead.") + if(2) + to_chat(user, "Your suit will now report your vital lifesigns.") + if(3) + to_chat(user, "Your suit will now report your vital lifesigns as well as your coordinate position.") + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + if(H.w_uniform == src) + H.update_suit_sensors() + + else if(istype(src.loc, /mob)) + switch(sensor_mode) + if(0) + for(var/mob/V in viewers(user, 1)) + V.show_message("[user] disables [src.loc]'s remote sensing equipment.", 1) + if(1) + for(var/mob/V in viewers(user, 1)) + V.show_message("[user] turns [src.loc]'s remote sensors to binary.", 1) + if(2) + for(var/mob/V in viewers(user, 1)) + V.show_message("[user] sets [src.loc]'s sensors to track vitals.", 1) + if(3) + for(var/mob/V in viewers(user, 1)) + V.show_message("[user] sets [src.loc]'s sensors to maximum.", 1) + if(istype(src,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = src + if(H.w_uniform == src) + H.update_suit_sensors() + +/obj/item/clothing/under/verb/toggle() + set name = "Toggle Suit Sensors" + set category = "Object" + set src in usr + set_sensors(usr) + ..() + +//Head +/obj/item/clothing/head + name = "head" + icon = 'icons/obj/clothing/hats.dmi' + body_parts_covered = HEAD + slot_flags = SLOT_HEAD + var/blockTracking // Do we block AI tracking? + var/HUDType = null + + var/vision_flags = 0 + var/see_in_dark = 0 + var/lighting_alpha + + var/can_toggle = null + +//Mask +/obj/item/clothing/mask + name = "mask" + icon = 'icons/obj/clothing/masks.dmi' + body_parts_covered = HEAD + slot_flags = SLOT_MASK + var/mask_adjusted = 0 + var/adjusted_flags = null + strip_delay = 40 + put_on_delay = 40 + +//Proc that moves gas/breath masks out of the way +/obj/item/clothing/mask/proc/adjustmask(var/mob/user) + var/mob/living/carbon/human/H = usr //Used to check if the mask is on the head, to check if the hands are full, and to turn off internals if they were on when the mask was pushed out of the way. + if(user.incapacitated()) //This check allows you to adjust your masks while you're buckled into chairs or beds. + return + if(mask_adjusted) + icon_state = initial(icon_state) + gas_transfer_coefficient = initial(gas_transfer_coefficient) + permeability_coefficient = initial(permeability_coefficient) + to_chat(user, "You push \the [src] back into place.") + mask_adjusted = 0 + slot_flags = initial(slot_flags) + if(flags_inv != initial(flags_inv)) + if(initial(flags_inv) & HIDEFACE) //If the mask is one that hides the face and can be adjusted yet lost that trait when it was adjusted, make it hide the face again. + flags_inv |= HIDEFACE + if(flags != initial(flags)) + if(initial(flags) & AIRTIGHT) //If the mask is airtight and thus, one that you'd be able to run internals from yet can't because it was adjusted, make it airtight again. + flags |= AIRTIGHT + if(flags_cover != initial(flags_cover)) + if(initial(flags_cover) & MASKCOVERSMOUTH) //If the mask covers the mouth when it's down and can be adjusted yet lost that trait when it was adjusted, make it cover the mouth again. + flags_cover |= MASKCOVERSMOUTH + if(H.head == src && flags_inv == HIDEFACE) //Means that only things like bandanas and balaclavas will be affected since they obscure the identity of the wearer. + if(H.l_hand && H.r_hand) //If both hands are occupied, drop the object on the ground. + user.unEquip(src) + else //Otherwise, put it in an available hand, the active one preferentially. + src.loc = user + H.head = null + user.put_in_hands(src) + else + icon_state += "_up" + to_chat(user, "You push \the [src] out of the way.") + gas_transfer_coefficient = null + permeability_coefficient = null + mask_adjusted = 1 + if(adjusted_flags) + slot_flags = adjusted_flags + if(ishuman(user) && H.internal && !H.get_organ_slot("breathing_tube") && user.wear_mask == src) /*If the user was wearing the mask providing internals on their face at the time it was adjusted, turn off internals. + Otherwise, they adjusted it while it was in their hands or some such so we won't be needing to turn off internals.*/ + H.internal = null + H.update_action_buttons_icon() + if(flags_inv & HIDEFACE) //Means that only things like bandanas and balaclavas will be affected since they obscure the identity of the wearer. + flags_inv &= ~HIDEFACE /*Done after the above to avoid having to do a check for initial(src.flags_inv == HIDEFACE). + This reveals the user's face since the bandana will now be going on their head.*/ + if(flags_cover & MASKCOVERSMOUTH) //Mask won't cover the mouth any more since it's been pushed out of the way. Allows for CPRing with adjusted masks. + flags_cover &= ~MASKCOVERSMOUTH + if(flags & AIRTIGHT) //If the mask was airtight, it won't be anymore since you just pushed it off your face. + flags &= ~AIRTIGHT + if(user.wear_mask == src && initial(flags_inv) == HIDEFACE) //Means that you won't have to take off and put back on simple things like breath masks which, realistically, can just be pulled down off your face. + if(H.l_hand && H.r_hand) //If both hands are occupied, drop the object on the ground. + user.unEquip(src) + else //Otherwise, put it in an available hand, the active one preferentially. + src.loc = user + user.wear_mask = null + user.put_in_hands(src) + H.wear_mask_update(src, toggle_off = mask_adjusted) + usr.update_inv_wear_mask() + usr.update_inv_head() + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + +//Shoes +/obj/item/clothing/shoes + name = "shoes" + icon = 'icons/obj/clothing/shoes.dmi' + desc = "Comfortable-looking shoes." + gender = PLURAL //Carn: for grammatically correct text-parsing + var/chained = 0 + var/can_cut_open = 0 + var/cut_open = 0 + body_parts_covered = FEET + slot_flags = SLOT_FEET + + var/silence_steps = 0 + var/shoe_sound_footstep = 1 + var/shoe_sound = null + var/blood_state = BLOOD_STATE_NOT_BLOODY + var/list/bloody_shoes = list(BLOOD_STATE_HUMAN = 0, BLOOD_STATE_XENO = 0, BLOOD_STATE_NOT_BLOODY = 0) + + permeability_coefficient = 0.50 + slowdown = SHOES_SLOWDOWN + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/shoes.dmi', + "Drask" = 'icons/mob/species/drask/shoes.dmi' + ) + +/obj/item/clothing/shoes/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/match) && src.loc == user) + var/obj/item/match/M = I + if(M.matchignite()) // Match isn't lit, but isn't burnt. + user.visible_message("[user] strikes a [M] on the bottom of [src], lighting it.","You strike the [M] on the bottom of [src] to light it.") + playsound(user.loc, 'sound/goonstation/misc/matchstick_light.ogg', 50, 1) + else + user.visible_message("[user] crushes the [M] into the bottom of [src], extinguishing it.","You crush the [M] into the bottom of [src], extinguishing it.") + M.dropped() + return + + if(istype(I, /obj/item/wirecutters)) + if(can_cut_open) + if(!cut_open) + playsound(src.loc, I.usesound, 100, 1) + user.visible_message("[user] cuts open the toes of [src].","You cut open the toes of [src].") + cut_open = 1 + icon_state = "[icon_state]_opentoe" + item_state = "[item_state]_opentoe" + name = "mangled [name]" + desc = "[desc] They have had their toes opened up." + update_icon() + else + to_chat(user, "[src] have already had [p_their()] toes cut open!") + return + else + return ..() + +/obj/item/clothing/shoes/proc/step_action(var/mob/living/carbon/human/H) //squeek squeek + SEND_SIGNAL(src, COMSIG_SHOES_STEP_ACTION) + if(shoe_sound) + var/turf/T = get_turf(H) + + if(!istype(H) || !istype(T)) + return 0 + + if(H.m_intent == MOVE_INTENT_RUN) + if(shoe_sound_footstep >= 2) + if(T.shoe_running_volume) + playsound(src, shoe_sound, T.shoe_running_volume, 1) + shoe_sound_footstep = 0 + else + shoe_sound_footstep++ + else if(T.shoe_walking_volume) + playsound(src, shoe_sound, T.shoe_walking_volume, 1) + + return 1 + +/obj/item/proc/negates_gravity() + return 0 + +//Suit +/obj/item/clothing/suit + icon = 'icons/obj/clothing/suits.dmi' + name = "suit" + var/fire_resist = T0C+100 + allowed = list(/obj/item/tank/emergency_oxygen) + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + slot_flags = SLOT_OCLOTHING + var/blood_overlay_type = "suit" + var/suittoggled = FALSE + var/suit_adjusted = 0 + var/ignore_suitadjust = 1 + var/adjust_flavour = null + var/list/hide_tail_by_species = null + +//Proc that opens and closes jackets. +/obj/item/clothing/suit/proc/adjustsuit(var/mob/user) + if(!ignore_suitadjust) + if(!user.incapacitated()) + if(!(HULK in user.mutations)) + if(suit_adjusted) + var/flavour = "close" + icon_state = copytext(icon_state, 1, findtext(icon_state, "_open")) /*Trims the '_open' off the end of the icon state, thus avoiding a case where jackets that start open will + end up with a suffix of _open_open if adjusted twice, since their initial state is _open. */ + item_state = copytext(item_state, 1, findtext(item_state, "_open")) + if(adjust_flavour) + flavour = "[copytext(adjust_flavour, 3, length(adjust_flavour) + 1)] up" //Trims off the 'un' at the beginning of the word. unzip -> zip, unbutton->button. + to_chat(user, "You [flavour] \the [src].") + suit_adjusted = 0 //Suit is no longer adjusted. + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + else + var/flavour = "open" + icon_state += "_open" + item_state += "_open" + if(adjust_flavour) + flavour = "[adjust_flavour]" + to_chat(user, "You [flavour] \the [src].") + suit_adjusted = 1 //Suit's adjusted. + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + else + if(user.canUnEquip(src)) //Checks to see if the item can be unequipped. If so, lets shred. Otherwise, struggle and fail. + if(contents) //If the suit's got any storage capability... + for(var/obj/item/O in contents) //AVOIDING ITEM LOSS. Check through everything that's stored in the jacket and see if one of the items is a pocket. + if(istype(O, /obj/item/storage/internal)) //If it's a pocket... + if(O.contents) //Check to see if the pocket's got anything in it. + for(var/obj/item/I in O.contents) //Dump the pocket out onto the floor below the user. + user.unEquip(I,1) + + user.visible_message("[user] bellows, [pick("shredding", "ripping open", "tearing off")] [user.p_their()] jacket in a fit of rage!","You accidentally [pick("shred", "rend", "tear apart")] [src] with your [pick("excessive", "extreme", "insane", "monstrous", "ridiculous", "unreal", "stupendous")] [pick("power", "strength")]!") + user.unEquip(src) + qdel(src) //Now that the pockets have been emptied, we can safely destroy the jacket. + user.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!")) + else + to_chat(user, "You yank and pull at \the [src] with your [pick("excessive", "extreme", "insane", "monstrous", "ridiculous", "unreal", "stupendous")] [pick("power", "strength")], however you are unable to change its state!")//Yep, that's all they get. Avoids having to snowflake in a cooldown. + + return + user.update_inv_wear_suit() + else + to_chat(user, "You attempt to button up the velcro on \the [src], before promptly realising how retarded you are.") + +/obj/item/clothing/suit/equipped(var/mob/living/carbon/human/user, var/slot) //Handle tail-hiding on a by-species basis. + ..() + if(ishuman(user) && hide_tail_by_species && slot == slot_wear_suit) + if(user.dna.species.name in hide_tail_by_species) + if(!(flags_inv & HIDETAIL)) //Hide the tail if the user's species is in the hide_tail_by_species list and the tail isn't already hidden. + flags_inv |= HIDETAIL + else + if(!(initial(flags_inv) & HIDETAIL) && (flags_inv & HIDETAIL)) //Otherwise, remove the HIDETAIL flag if it wasn't already in the flags_inv to start with. + flags_inv &= ~HIDETAIL + +/obj/item/clothing/suit/ui_action_click(mob/user) //This is what happens when you click the HUD action button to adjust your suit. + if(!ignore_suitadjust) + adjustsuit(user) + else + ..() //This is required in order to ensure that the UI buttons for items that have alternate functions tied to UI buttons still work. + +/obj/item/clothing/suit/proc/special_overlays() // Does it have special overlays when worn? + return FALSE + +//Spacesuit +//Note: Everything in modules/clothing/spacesuits should have the entire suit grouped together. +// Meaning the the suit is defined directly after the corrisponding helmet. Just like below! +/obj/item/clothing/head/helmet/space + name = "Space helmet" + icon_state = "space" + desc = "A special helmet designed for work in a hazardous, low-pressure environment." + flags = BLOCKHAIR | STOPSPRESSUREDMAGE | THICKMATERIAL + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + item_state = "s_helmet" + permeability_coefficient = 0.01 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 50, "fire" = 80, "acid" = 70) + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + cold_protection = HEAD + min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT + heat_protection = HEAD + max_heat_protection_temperature = SPACE_HELM_MAX_TEMP_PROTECT + species_restricted = list("exclude","Wryn") + flash_protect = 2 + strip_delay = 50 + put_on_delay = 50 + resistance_flags = NONE + dog_fashion = null + + +/obj/item/clothing/suit/space + name = "Space suit" + desc = "A suit that protects against low pressure environments. Has a big 13 on the back." + icon_state = "space" + item_state = "s_suit" + w_class = WEIGHT_CLASS_BULKY + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.02 + flags = STOPSPRESSUREDMAGE | THICKMATERIAL + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + allowed = list(/obj/item/flashlight,/obj/item/tank) + slowdown = 1 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 50, "fire" = 80, "acid" = 70) + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL + cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS + min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT + heat_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS + max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT + strip_delay = 80 + put_on_delay = 80 + resistance_flags = NONE + hide_tail_by_species = null + species_restricted = list("exclude","Wryn") + + +//Under clothing +/obj/item/clothing/under + icon = 'icons/obj/clothing/uniforms.dmi' + name = "under" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + permeability_coefficient = 0.90 + slot_flags = SLOT_ICLOTHING + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/uniform.dmi', + "Drask" = 'icons/mob/species/drask/uniform.dmi', + "Grey" = 'icons/mob/species/grey/uniform.dmi' + ) + + var/has_sensor = TRUE//For the crew computer 2 = unable to change mode + var/sensor_mode = SENSOR_OFF + var/random_sensor = TRUE + /* + 1 = Report living/dead + 2 = Report detailed damages + 3 = Report location + */ + var/list/accessories = list() + var/displays_id = 1 + var/rolled_down = 0 + var/basecolor + +/obj/item/clothing/under/rank/New() + if(random_sensor) + sensor_mode = pick(SENSOR_OFF, SENSOR_LIVING, SENSOR_VITALS, SENSOR_COORDS) + ..() + +/obj/item/clothing/under/Destroy() + QDEL_LIST(accessories) + return ..() + +/obj/item/clothing/under/proc/can_attach_accessory(obj/item/clothing/accessory/A) + if(istype(A)) + . = TRUE + else + return FALSE + + if(accessories.len) + for(var/obj/item/clothing/accessory/AC in accessories) + if((A.slot in list(ACCESSORY_SLOT_UTILITY, ACCESSORY_SLOT_ARMBAND)) && AC.slot == A.slot) + return FALSE + if(!A.allow_duplicates && AC.type == A.type) + return FALSE + +/obj/item/clothing/under/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/clothing/accessory)) + attach_accessory(I, user, TRUE) + + if(accessories.len) + for(var/obj/item/clothing/accessory/A in accessories) + A.attackby(I, user, params) + return TRUE + + . = ..() + +/obj/item/clothing/under/proc/attach_accessory(obj/item/clothing/accessory/A, mob/user, unequip = FALSE) + if(can_attach_accessory(A)) + if(unequip && !user.unEquip(A)) // Make absolutely sure this accessory is removed from hands + return FALSE + + accessories += A + A.on_attached(src, user) + + if(ishuman(loc)) + var/mob/living/carbon/human/H = loc + H.update_inv_w_uniform() + + return TRUE + else + to_chat(user, "You cannot attach more accessories of this type to [src].") + + return FALSE + +/obj/item/clothing/under/examine(mob/user) + . = ..() + switch(sensor_mode) + if(0) + . += "Its sensors appear to be disabled." + if(1) + . += "Its binary life sensors appear to be enabled." + if(2) + . += "Its vital tracker appears to be enabled." + if(3) + . += "Its vital tracker and tracking beacon appear to be enabled." + if(accessories.len) + for(var/obj/item/clothing/accessory/A in accessories) + . += "\A [A] is attached to it." + + +/obj/item/clothing/under/verb/rollsuit() + set name = "Roll Down Jumpsuit" + set category = "Object" + set src in usr + if(!istype(usr, /mob/living)) return + if(usr.stat) return + + if(!usr.incapacitated()) + if(copytext(item_color,-2) != "_d") + basecolor = item_color + if(basecolor + "_d_s" in icon_states('icons/mob/uniform.dmi')) + item_color = item_color == "[basecolor]" ? "[basecolor]_d" : "[basecolor]" + usr.update_inv_w_uniform() + else + to_chat(usr, "You cannot roll down this uniform!") + else + to_chat(usr, "You cannot roll down the uniform!") + +/obj/item/clothing/under/verb/removetie() + set name = "Remove Accessory" + set category = "Object" + set src in usr + handle_accessories_removal() + +/obj/item/clothing/under/proc/handle_accessories_removal() + if(!isliving(usr)) + return + if(usr.incapacitated()) + return + if(!Adjacent(usr)) + return + if(!accessories.len) + return + var/obj/item/clothing/accessory/A + if(accessories.len > 1) + A = input("Select an accessory to remove from [src]") as null|anything in accessories + else + A = accessories[1] + remove_accessory(usr,A) + +/obj/item/clothing/under/proc/remove_accessory(mob/user, obj/item/clothing/accessory/A) + if(!(A in accessories)) + return + if(!isliving(user)) + return + if(user.incapacitated()) + return + if(!Adjacent(user)) + return + A.on_removed(user) + accessories -= A + to_chat(user, "You remove [A] from [src].") + usr.update_inv_w_uniform() + +/obj/item/clothing/under/emp_act(severity) + if(accessories.len) + for(var/obj/item/clothing/accessory/A in accessories) + A.emp_act(severity) + ..() + +/obj/item/clothing/under/AltClick() + handle_accessories_removal() + +/obj/item/clothing/obj_destruction(damage_flag) + if(damage_flag == "bomb" || damage_flag == "melee") + var/turf/T = get_turf(src) + spawn(1) //so the shred survives potential turf change from the explosion. + var/obj/effect/decal/cleanable/shreds/Shreds = new(T) + Shreds.desc = "The sad remains of what used to be [name]." + deconstruct(FALSE) + else + ..() diff --git a/code/modules/clothing/ears/ears.dm b/code/modules/clothing/ears/ears.dm index 2ba883a6f89..ea8f4198f50 100644 --- a/code/modules/clothing/ears/ears.dm +++ b/code/modules/clothing/ears/ears.dm @@ -25,4 +25,4 @@ var/datum/action/A = X A.UpdateButtonIcon() - user.update_inv_ears() \ No newline at end of file + user.update_inv_ears() diff --git a/code/modules/clothing/glasses/glasses.dm b/code/modules/clothing/glasses/glasses.dm index ce29fa8f7b1..1b9c87f56e5 100644 --- a/code/modules/clothing/glasses/glasses.dm +++ b/code/modules/clothing/glasses/glasses.dm @@ -1,520 +1,525 @@ -/obj/item/clothing/glasses/New() - . = ..() - if(prescription_upgradable && prescription) - // Pre-upgraded upgradable glasses - name = "prescription [name]" - -/obj/item/clothing/glasses/attackby(var/obj/item/O as obj, var/mob/user as mob) - if(user.stat || user.restrained() || !ishuman(user)) - return ..() - var/mob/living/carbon/human/H = user - if(prescription_upgradable) - if(istype(O, /obj/item/clothing/glasses/regular)) - if(prescription) - to_chat(H, "You can't possibly imagine how adding more lenses would improve \the [name].") - return - H.unEquip(O) - O.loc = src // Store the glasses for later removal - to_chat(H, "You fit \the [name] with lenses from \the [O].") - prescription = 1 - name = "prescription [name]" - return - if(prescription && istype(O, /obj/item/screwdriver)) - var/obj/item/clothing/glasses/regular/G = locate() in src - if(!G) - G = new(get_turf(H)) - to_chat(H, "You salvage the prescription lenses from \the [name].") - prescription = 0 - name = initial(name) - H.put_in_hands(G) - return - return ..() - -/obj/item/clothing/glasses/visor_toggling() - ..() - if(visor_vars_to_toggle & VISOR_VISIONFLAGS) - vision_flags ^= initial(vision_flags) - if(visor_vars_to_toggle & VISOR_DARKNESSVIEW) - see_in_dark ^= initial(see_in_dark) - if(visor_vars_to_toggle & VISOR_INVISVIEW) - invis_view ^= initial(invis_view) - -/obj/item/clothing/glasses/weldingvisortoggle(mob/user) - . = ..() - if(. && user) - user.update_sight() - user.update_inv_glasses() - -/obj/item/clothing/glasses/meson - name = "Optical Meson Scanner" - desc = "Used for seeing walls, floors, and stuff through anything." - icon_state = "meson" - item_state = "glasses" - origin_tech = "magnets=1;engineering=2" - vision_flags = SEE_TURFS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - prescription_upgradable = 1 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - -/obj/item/clothing/glasses/meson/night - name = "Night Vision Optical Meson Scanner" - desc = "An Optical Meson Scanner fitted with an amplified visible light spectrum overlay, providing greater visual clarity in darkness." - icon_state = "nvgmeson" - item_state = "glasses" - origin_tech = "magnets=4;engineering=5;plasmatech=4" - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - prescription_upgradable = 0 - -/obj/item/clothing/glasses/meson/prescription - prescription = 1 - -/obj/item/clothing/glasses/meson/gar - name = "gar mesons" - icon_state = "garm" - item_state = "garm" - desc = "Do the impossible, see the invisible!" - force = 10 - throwforce = 10 - throw_speed = 4 - attack_verb = list("sliced") - hitsound = 'sound/weapons/bladeslice.ogg' - sharp = 1 - -/obj/item/clothing/glasses/meson/cyber - name = "Eye Replacement Implant" - desc = "An implanted replacement for a left eye with meson vision capabilities." - icon_state = "cybereye-green" - item_state = "eyepatch" - flags = NODROP - flags_cover = null - prescription_upgradable = 0 - -/obj/item/clothing/glasses/science - name = "science goggles" - desc = "A pair of snazzy goggles used to protect against chemical spills. Fitted with an analyzer for scanning items and reagents." - icon_state = "purple" - item_state = "glasses" - origin_tech = "magnets=2;engineering=1" - prescription_upgradable = 0 - scan_reagents = 1 //You can see reagents while wearing science goggles - resistance_flags = ACID_PROOF - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 100) - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - actions_types = list(/datum/action/item_action/toggle_research_scanner) - -/obj/item/clothing/glasses/science/item_action_slot_check(slot) - if(slot == slot_glasses) - return 1 - -/obj/item/clothing/glasses/science/night - name = "Night Vision Science Goggle" - desc = "Now you can science in darkness." - icon_state = "nvpurple" - item_state = "glasses" - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these - -/obj/item/clothing/glasses/janitor - name = "Janitorial Goggles" - desc = "These'll keep the soap out of your eyes." - icon_state = "purple" - item_state = "glasses" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi' - ) - -/obj/item/clothing/glasses/night - name = "Night Vision Goggles" - desc = "You can totally see in the dark now!" - icon_state = "night" - item_state = "glasses" - origin_tech = "materials=4;magnets=4;plasmatech=4;engineering=4" - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/eyepatch - name = "eyepatch" - desc = "Yarr." - icon_state = "eyepatch" - item_state = "eyepatch" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - -/obj/item/clothing/glasses/monocle - name = "monocle" - desc = "Such a dapper eyepiece!" - icon_state = "monocle" - item_state = "headset" // lol - prescription_upgradable = 1 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/material - name = "Optical Material Scanner" - desc = "Very confusing glasses." - icon_state = "material" - item_state = "glasses" - origin_tech = "magnets=3;engineering=3" - vision_flags = SEE_OBJS - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/material/cyber - name = "Eye Replacement Implant" - desc = "An implanted replacement for a left eye with material vision capabilities." - icon_state = "cybereye-blue" - item_state = "eyepatch" - flags = NODROP - flags_cover = null - -/obj/item/clothing/glasses/material/lighting - name = "Neutron Goggles" - desc = "These odd glasses use a form of neutron-based imaging to completely negate the effects of light and darkness." - origin_tech = null - vision_flags = 0 - - flags = NODROP - lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE - -/obj/item/clothing/glasses/regular - name = "prescription glasses" - desc = "Made by Nerd. Co." - icon_state = "glasses" - item_state = "glasses" - prescription = 1 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - -/obj/item/clothing/glasses/regular/hipster - name = "prescription glasses" - desc = "Made by Uncool. Co." - icon_state = "hipster_glasses" - item_state = "hipster_glasses" - -/obj/item/clothing/glasses/threedglasses - desc = "A long time ago, people used these glasses to makes images from screens threedimensional." - name = "3D glasses" - icon_state = "3d" - item_state = "3d" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - -/obj/item/clothing/glasses/gglasses - name = "Green Glasses" - desc = "Forest green glasses, like the kind you'd wear when hatching a nasty scheme." - icon_state = "gglasses" - item_state = "gglasses" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - prescription_upgradable = 1 - -/obj/item/clothing/glasses/sunglasses - desc = "Strangely ancient technology used to help provide rudimentary eye cover. Enhanced shielding blocks many flashes." - name = "sunglasses" - icon_state = "sun" - item_state = "sunglasses" - see_in_dark = 1 - flash_protect = 1 - tint = 1 - prescription_upgradable = 1 - dog_fashion = /datum/dog_fashion/head - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/sunglasses_fake - desc = "Cheap, plastic sunglasses. They don't even have UV protection." - name = "cheap sunglasses" - icon_state = "sun" - item_state = "sunglasses" - see_in_dark = 0 - flash_protect = 0 - tint = 0 - -/obj/item/clothing/glasses/sunglasses/noir - name = "noir sunglasses" - desc = "Somehow these seem even more out-of-date than normal sunglasses." - actions_types = list(/datum/action/item_action/noir) - -/obj/item/clothing/glasses/sunglasses/noir/attack_self(mob/user) - toggle_noir(user) - -/obj/item/clothing/glasses/sunglasses/noir/item_action_slot_check(slot) - if(slot == slot_glasses) - return 1 - -/obj/item/clothing/glasses/sunglasses/noir/proc/toggle_noir(mob/user) - color_view = color_view ? null : MATRIX_GREYSCALE //Toggles between null and grayscale, with null being the default option. - user.update_client_colour() - -/obj/item/clothing/glasses/sunglasses/yeah - name = "agreeable glasses" - desc = "H.C Limited edition." - var/punused = null - actions_types = list(/datum/action/item_action/YEEEAAAAAHHHHHHHHHHHHH) - -/obj/item/clothing/glasses/sunglasses/yeah/attack_self() - pun() - -/obj/item/clothing/glasses/sunglasses/yeah/proc/pun() - if(!punused)//one per round - punused = 1 - playsound(src.loc, 'sound/misc/yeah.ogg', 100, 0) - usr.visible_message("YEEEAAAAAHHHHHHHHHHHHH!!") - else - to_chat(usr, "The moment is gone.") - - -/obj/item/clothing/glasses/sunglasses/reagent - name = "sunscanners" - desc = "Strangely ancient technology used to help provide rudimentary eye color. Outfitted with apparatus to scan individual reagents." - scan_reagents = 1 - -/obj/item/clothing/glasses/virussunglasses - desc = "Strangely ancient technology used to help provide rudimentary eye cover. Enhanced shielding blocks many flashes." - name = "sunglasses" - icon_state = "sun" - item_state = "sunglasses" - see_in_dark = 1 - flash_protect = 1 - tint = 1 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/sunglasses/lasers - desc = "A peculiar set of sunglasses; they have various chips and other panels attached to the sides of the frames." - name = "high-tech sunglasses" - flags = NODROP - -/obj/item/clothing/glasses/sunglasses/lasers/equipped(mob/user, slot) //grant them laser eyes upon equipping it. - if(slot == slot_glasses) - user.mutations.Add(LASER) - user.regenerate_icons() - ..(user, slot) - -/obj/item/clothing/glasses/welding - name = "welding goggles" - desc = "Protects the eyes from welders, approved by the mad scientist association." - icon_state = "welding-g" - item_state = "welding-g" - actions_types = list(/datum/action/item_action/toggle) - flash_protect = 2 - tint = 2 - visor_vars_to_toggle = VISOR_FLASHPROTECT | VISOR_TINT - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/welding/attack_self(mob/user) - weldingvisortoggle(user) - -/obj/item/clothing/glasses/welding/superior - name = "superior welding goggles" - desc = "Welding goggles made from more expensive materials, strangely smells like potatoes." - icon_state = "rwelding-g" - item_state = "rwelding-g" - flash_protect = 2 - tint = 0 - -/obj/item/clothing/glasses/sunglasses/blindfold - name = "blindfold" - desc = "Covers the eyes, preventing sight." - icon_state = "blindfold" - item_state = "blindfold" - //vision_flags = BLIND - flash_protect = 2 - tint = 3 //to make them blind - prescription_upgradable = 0 - -/obj/item/clothing/glasses/sunglasses/prescription - prescription = 1 - -/obj/item/clothing/glasses/sunglasses/big - desc = "Strangely ancient technology used to help provide rudimentary eye cover. Larger than average enhanced shielding blocks many flashes." - icon_state = "bigsunglasses" - item_state = "bigsunglasses" - -/obj/item/clothing/glasses/thermal - name = "Optical Thermal Scanner" - desc = "Thermals in the shape of glasses." - icon_state = "thermal" - item_state = "glasses" - origin_tech = "magnets=3" - vision_flags = SEE_MOBS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - flash_protect = -1 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - -/obj/item/clothing/glasses/thermal/emp_act(severity) - if(istype(src.loc, /mob/living/carbon/human)) - var/mob/living/carbon/human/M = src.loc - to_chat(M, "The Optical Thermal Scanner overloads and blinds you!") - if(M.glasses == src) - M.EyeBlind(3) - M.EyeBlurry(5) - if(!(M.disabilities & NEARSIGHTED)) - M.BecomeNearsighted() - spawn(100) - M.CureNearsighted() - ..() - -/obj/item/clothing/glasses/thermal/monocle - name = "Thermoncle" - desc = "A monocle thermal." - icon_state = "thermoncle" - flags_cover = null //doesn't protect eyes because it's a monocle, duh - -/obj/item/clothing/glasses/thermal/eyepatch - name = "Optical Thermal Eyepatch" - desc = "An eyepatch with built-in thermal optics" - icon_state = "eyepatch" - item_state = "eyepatch" - -/obj/item/clothing/glasses/thermal/jensen - name = "Optical Thermal Implants" - desc = "A set of implantable lenses designed to augment your vision" - icon_state = "thermalimplants" - item_state = "syringe_kit" - -/obj/item/clothing/glasses/thermal/cyber - name = "Eye Replacement Implant" - desc = "An implanted replacement for a left eye with thermal vision capabilities." - icon_state = "cybereye-red" - item_state = "eyepatch" - flags = NODROP - - -/obj/item/clothing/glasses/godeye - name = "eye of god" - desc = "A strange eye, said to have been torn from an omniscient creature that used to roam the wastes." - icon_state = "godeye" - item_state = "godeye" - vision_flags = SEE_TURFS|SEE_MOBS|SEE_OBJS - see_in_dark = 8 - scan_reagents = 1 - flags = NODROP - flags_cover = null - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - resistance_flags = LAVA_PROOF | FIRE_PROOF - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - -/obj/item/clothing/glasses/godeye/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, src) && W != src && W.loc == user) - if(W.icon_state == "godeye") - W.icon_state = "doublegodeye" - W.item_state = "doublegodeye" - W.desc = "A pair of strange eyes, said to have been torn from an omniscient creature that used to roam the wastes. There's no real reason to have two, but that isn't stopping you." - if(iscarbon(user)) - var/mob/living/carbon/C = user - C.update_inv_wear_mask() - else - to_chat(user, "The eye winks at you and vanishes into the abyss, you feel really unlucky.") - qdel(src) - ..() - -/obj/item/clothing/glasses/tajblind - name = "embroidered veil" - desc = "An Ahdominian made veil that allows the user to see while obscuring their eyes." - icon_state = "tajblind" - item_state = "tajblind" - flags_cover = GLASSESCOVERSEYES - actions_types = list(/datum/action/item_action/toggle) - up = 0 - tint = 0 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - -/obj/item/clothing/glasses/tajblind/eng - name = "industrial veil" - icon_state = "tajblind_engi" - item_state = "tajblind_engi" - -/obj/item/clothing/glasses/tajblind/sci - name = "hi-tech veil" - icon_state = "tajblind_sci" - item_state = "tajblind_sci" - -/obj/item/clothing/glasses/tajblind/cargo - name = "khaki veil" - icon_state = "tajblind_cargo" - item_state = "tajblind_cargo" - -/obj/item/clothing/glasses/tajblind/attack_self() - toggle_veil() - -/obj/item/clothing/glasses/proc/toggle_veil() - if(usr.canmove && !usr.incapacitated()) - if(up) - up = !up - tint = initial(tint) - to_chat(usr, "You activate [src], allowing you to see.") - else - up = !up - tint = 3 - to_chat(usr, "You deactivate [src], obscuring your vision.") - var/mob/living/carbon/user = usr - user.update_tint() - user.update_inv_glasses() \ No newline at end of file +/obj/item/clothing/glasses/New() + . = ..() + if(prescription_upgradable && prescription) + // Pre-upgraded upgradable glasses + name = "prescription [name]" + +/obj/item/clothing/glasses/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(user.stat || user.restrained() || !ishuman(user)) + return ..() + var/mob/living/carbon/human/H = user + if(prescription_upgradable) + if(istype(O, /obj/item/clothing/glasses/regular)) + if(prescription) + to_chat(H, "You can't possibly imagine how adding more lenses would improve \the [name].") + return + H.unEquip(O) + O.loc = src // Store the glasses for later removal + to_chat(H, "You fit \the [name] with lenses from \the [O].") + prescription = 1 + name = "prescription [name]" + return + if(prescription && istype(O, /obj/item/screwdriver)) + var/obj/item/clothing/glasses/regular/G = locate() in src + if(!G) + G = new(get_turf(H)) + to_chat(H, "You salvage the prescription lenses from \the [name].") + prescription = 0 + name = initial(name) + H.put_in_hands(G) + return + return ..() + +/obj/item/clothing/glasses/visor_toggling() + ..() + if(visor_vars_to_toggle & VISOR_VISIONFLAGS) + vision_flags ^= initial(vision_flags) + if(visor_vars_to_toggle & VISOR_DARKNESSVIEW) + see_in_dark ^= initial(see_in_dark) + if(visor_vars_to_toggle & VISOR_INVISVIEW) + invis_view ^= initial(invis_view) + +/obj/item/clothing/glasses/weldingvisortoggle(mob/user) + . = ..() + if(. && user) + user.update_sight() + user.update_inv_glasses() + +/obj/item/clothing/glasses/meson + name = "Optical Meson Scanner" + desc = "Used for seeing walls, floors, and stuff through anything." + icon_state = "meson" + item_state = "glasses" + origin_tech = "magnets=1;engineering=2" + vision_flags = SEE_TURFS + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + prescription_upgradable = 1 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + +/obj/item/clothing/glasses/meson/night + name = "Night Vision Optical Meson Scanner" + desc = "An Optical Meson Scanner fitted with an amplified visible light spectrum overlay, providing greater visual clarity in darkness." + icon_state = "nvgmeson" + item_state = "glasses" + origin_tech = "magnets=4;engineering=5;plasmatech=4" + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + prescription_upgradable = 0 + +/obj/item/clothing/glasses/meson/prescription + prescription = 1 + +/obj/item/clothing/glasses/meson/gar + name = "gar mesons" + icon_state = "garm" + item_state = "garm" + desc = "Do the impossible, see the invisible!" + force = 10 + throwforce = 10 + throw_speed = 4 + attack_verb = list("sliced") + hitsound = 'sound/weapons/bladeslice.ogg' + sharp = 1 + +/obj/item/clothing/glasses/meson/cyber + name = "Eye Replacement Implant" + desc = "An implanted replacement for a left eye with meson vision capabilities." + icon_state = "cybereye-green" + item_state = "eyepatch" + flags = NODROP + flags_cover = null + prescription_upgradable = 0 + +/obj/item/clothing/glasses/science + name = "science goggles" + desc = "A pair of snazzy goggles used to protect against chemical spills. Fitted with an analyzer for scanning items and reagents." + icon_state = "purple" + item_state = "glasses" + origin_tech = "magnets=2;engineering=1" + prescription_upgradable = 0 + scan_reagents = 1 //You can see reagents while wearing science goggles + resistance_flags = ACID_PROOF + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 100) + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + actions_types = list(/datum/action/item_action/toggle_research_scanner) + +/obj/item/clothing/glasses/science/item_action_slot_check(slot) + if(slot == slot_glasses) + return 1 + +/obj/item/clothing/glasses/science/night + name = "Night Vision Science Goggle" + desc = "Now you can science in darkness." + icon_state = "nvpurple" + item_state = "glasses" + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these + +/obj/item/clothing/glasses/janitor + name = "Janitorial Goggles" + desc = "These'll keep the soap out of your eyes." + icon_state = "purple" + item_state = "glasses" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi' + ) + +/obj/item/clothing/glasses/night + name = "Night Vision Goggles" + desc = "You can totally see in the dark now!" + icon_state = "night" + item_state = "glasses" + origin_tech = "materials=4;magnets=4;plasmatech=4;engineering=4" + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/eyepatch + name = "eyepatch" + desc = "Yarr." + icon_state = "eyepatch" + item_state = "eyepatch" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + +/obj/item/clothing/glasses/monocle + name = "monocle" + desc = "Such a dapper eyepiece!" + icon_state = "monocle" + item_state = "headset" // lol + prescription_upgradable = 1 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/material + name = "Optical Material Scanner" + desc = "Very confusing glasses." + icon_state = "material" + item_state = "glasses" + origin_tech = "magnets=3;engineering=3" + vision_flags = SEE_OBJS + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/material/cyber + name = "Eye Replacement Implant" + desc = "An implanted replacement for a left eye with material vision capabilities." + icon_state = "cybereye-blue" + item_state = "eyepatch" + flags = NODROP + flags_cover = null + +/obj/item/clothing/glasses/material/lighting + name = "Neutron Goggles" + desc = "These odd glasses use a form of neutron-based imaging to completely negate the effects of light and darkness." + origin_tech = null + vision_flags = 0 + + flags = NODROP + lighting_alpha = LIGHTING_PLANE_ALPHA_INVISIBLE + +/obj/item/clothing/glasses/regular + name = "prescription glasses" + desc = "Made by Nerd. Co." + icon_state = "glasses" + item_state = "glasses" + prescription = 1 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + +/obj/item/clothing/glasses/regular/hipster + name = "prescription glasses" + desc = "Made by Uncool. Co." + icon_state = "hipster_glasses" + item_state = "hipster_glasses" + +/obj/item/clothing/glasses/threedglasses + desc = "A long time ago, people used these glasses to makes images from screens threedimensional." + name = "3D glasses" + icon_state = "3d" + item_state = "3d" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + +/obj/item/clothing/glasses/gglasses + name = "Green Glasses" + desc = "Forest green glasses, like the kind you'd wear when hatching a nasty scheme." + icon_state = "gglasses" + item_state = "gglasses" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + prescription_upgradable = 1 + +/obj/item/clothing/glasses/sunglasses + desc = "Strangely ancient technology used to help provide rudimentary eye cover. Enhanced shielding blocks many flashes." + name = "sunglasses" + icon_state = "sun" + item_state = "sunglasses" + see_in_dark = 1 + flash_protect = 1 + tint = 1 + prescription_upgradable = 1 + dog_fashion = /datum/dog_fashion/head + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/sunglasses_fake + desc = "Cheap, plastic sunglasses. They don't even have UV protection." + name = "cheap sunglasses" + icon_state = "sun" + item_state = "sunglasses" + see_in_dark = 0 + flash_protect = 0 + tint = 0 + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/sunglasses/noir + name = "noir sunglasses" + desc = "Somehow these seem even more out-of-date than normal sunglasses." + actions_types = list(/datum/action/item_action/noir) + +/obj/item/clothing/glasses/sunglasses/noir/attack_self(mob/user) + toggle_noir(user) + +/obj/item/clothing/glasses/sunglasses/noir/item_action_slot_check(slot) + if(slot == slot_glasses) + return 1 + +/obj/item/clothing/glasses/sunglasses/noir/proc/toggle_noir(mob/user) + color_view = color_view ? null : MATRIX_GREYSCALE //Toggles between null and grayscale, with null being the default option. + user.update_client_colour() + +/obj/item/clothing/glasses/sunglasses/yeah + name = "agreeable glasses" + desc = "H.C Limited edition." + var/punused = null + actions_types = list(/datum/action/item_action/YEEEAAAAAHHHHHHHHHHHHH) + +/obj/item/clothing/glasses/sunglasses/yeah/attack_self() + pun() + +/obj/item/clothing/glasses/sunglasses/yeah/proc/pun() + if(!punused)//one per round + punused = 1 + playsound(src.loc, 'sound/misc/yeah.ogg', 100, 0) + usr.visible_message("YEEEAAAAAHHHHHHHHHHHHH!!") + else + to_chat(usr, "The moment is gone.") + + +/obj/item/clothing/glasses/sunglasses/reagent + name = "sunscanners" + desc = "Strangely ancient technology used to help provide rudimentary eye color. Outfitted with apparatus to scan individual reagents." + scan_reagents = 1 + +/obj/item/clothing/glasses/virussunglasses + desc = "Strangely ancient technology used to help provide rudimentary eye cover. Enhanced shielding blocks many flashes." + name = "sunglasses" + icon_state = "sun" + item_state = "sunglasses" + see_in_dark = 1 + flash_protect = 1 + tint = 1 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/sunglasses/lasers + desc = "A peculiar set of sunglasses; they have various chips and other panels attached to the sides of the frames." + name = "high-tech sunglasses" + flags = NODROP + +/obj/item/clothing/glasses/sunglasses/lasers/equipped(mob/user, slot) //grant them laser eyes upon equipping it. + if(slot == slot_glasses) + user.mutations.Add(LASER) + user.regenerate_icons() + ..(user, slot) + +/obj/item/clothing/glasses/welding + name = "welding goggles" + desc = "Protects the eyes from welders, approved by the mad scientist association." + icon_state = "welding-g" + item_state = "welding-g" + actions_types = list(/datum/action/item_action/toggle) + flash_protect = 2 + tint = 2 + visor_vars_to_toggle = VISOR_FLASHPROTECT | VISOR_TINT + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/welding/attack_self(mob/user) + weldingvisortoggle(user) + +/obj/item/clothing/glasses/welding/superior + name = "superior welding goggles" + desc = "Welding goggles made from more expensive materials, strangely smells like potatoes." + icon_state = "rwelding-g" + item_state = "rwelding-g" + flash_protect = 2 + tint = 0 + +/obj/item/clothing/glasses/sunglasses/blindfold + name = "blindfold" + desc = "Covers the eyes, preventing sight." + icon_state = "blindfold" + item_state = "blindfold" + //vision_flags = BLIND + flash_protect = 2 + tint = 3 //to make them blind + prescription_upgradable = 0 + +/obj/item/clothing/glasses/sunglasses/prescription + prescription = 1 + +/obj/item/clothing/glasses/sunglasses/big + desc = "Strangely ancient technology used to help provide rudimentary eye cover. Larger than average enhanced shielding blocks many flashes." + icon_state = "bigsunglasses" + item_state = "bigsunglasses" + +/obj/item/clothing/glasses/thermal + name = "Optical Thermal Scanner" + desc = "Thermals in the shape of glasses." + icon_state = "thermal" + item_state = "glasses" + origin_tech = "magnets=3" + vision_flags = SEE_MOBS + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + flash_protect = -1 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + +/obj/item/clothing/glasses/thermal/emp_act(severity) + if(istype(src.loc, /mob/living/carbon/human)) + var/mob/living/carbon/human/M = src.loc + to_chat(M, "The Optical Thermal Scanner overloads and blinds you!") + if(M.glasses == src) + M.EyeBlind(3) + M.EyeBlurry(5) + if(!(M.disabilities & NEARSIGHTED)) + M.BecomeNearsighted() + spawn(100) + M.CureNearsighted() + ..() + +/obj/item/clothing/glasses/thermal/monocle + name = "Thermoncle" + desc = "A monocle thermal." + icon_state = "thermoncle" + flags_cover = null //doesn't protect eyes because it's a monocle, duh + +/obj/item/clothing/glasses/thermal/eyepatch + name = "Optical Thermal Eyepatch" + desc = "An eyepatch with built-in thermal optics" + icon_state = "eyepatch" + item_state = "eyepatch" + +/obj/item/clothing/glasses/thermal/jensen + name = "Optical Thermal Implants" + desc = "A set of implantable lenses designed to augment your vision" + icon_state = "thermalimplants" + item_state = "syringe_kit" + +/obj/item/clothing/glasses/thermal/cyber + name = "Eye Replacement Implant" + desc = "An implanted replacement for a left eye with thermal vision capabilities." + icon_state = "cybereye-red" + item_state = "eyepatch" + flags = NODROP + + +/obj/item/clothing/glasses/godeye + name = "eye of god" + desc = "A strange eye, said to have been torn from an omniscient creature that used to roam the wastes." + icon_state = "godeye" + item_state = "godeye" + vision_flags = SEE_TURFS|SEE_MOBS|SEE_OBJS + see_in_dark = 8 + scan_reagents = 1 + flags = NODROP + flags_cover = null + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + resistance_flags = LAVA_PROOF | FIRE_PROOF + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + +/obj/item/clothing/glasses/godeye/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, src) && W != src && W.loc == user) + if(W.icon_state == "godeye") + W.icon_state = "doublegodeye" + W.item_state = "doublegodeye" + W.desc = "A pair of strange eyes, said to have been torn from an omniscient creature that used to roam the wastes. There's no real reason to have two, but that isn't stopping you." + if(iscarbon(user)) + var/mob/living/carbon/C = user + C.update_inv_wear_mask() + else + to_chat(user, "The eye winks at you and vanishes into the abyss, you feel really unlucky.") + qdel(src) + ..() + +/obj/item/clothing/glasses/tajblind + name = "embroidered veil" + desc = "An Ahdominian made veil that allows the user to see while obscuring their eyes." + icon_state = "tajblind" + item_state = "tajblind" + flags_cover = GLASSESCOVERSEYES + actions_types = list(/datum/action/item_action/toggle) + up = 0 + tint = 0 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + +/obj/item/clothing/glasses/tajblind/eng + name = "industrial veil" + icon_state = "tajblind_engi" + item_state = "tajblind_engi" + +/obj/item/clothing/glasses/tajblind/sci + name = "hi-tech veil" + icon_state = "tajblind_sci" + item_state = "tajblind_sci" + +/obj/item/clothing/glasses/tajblind/cargo + name = "khaki veil" + icon_state = "tajblind_cargo" + item_state = "tajblind_cargo" + +/obj/item/clothing/glasses/tajblind/attack_self() + toggle_veil() + +/obj/item/clothing/glasses/proc/toggle_veil() + if(usr.canmove && !usr.incapacitated()) + if(up) + up = !up + tint = initial(tint) + to_chat(usr, "You activate [src], allowing you to see.") + else + up = !up + tint = 3 + to_chat(usr, "You deactivate [src], obscuring your vision.") + var/mob/living/carbon/user = usr + user.update_tint() + user.update_inv_glasses() diff --git a/code/modules/clothing/glasses/hud.dm b/code/modules/clothing/glasses/hud.dm index 27cd78afad3..feee4845a9c 100644 --- a/code/modules/clothing/glasses/hud.dm +++ b/code/modules/clothing/glasses/hud.dm @@ -1,198 +1,198 @@ -/obj/item/clothing/glasses/hud - name = "\improper HUD" - desc = "A heads-up display that provides important info in (almost) real time." - flags = null //doesn't protect eyes because it's a monocle, duh - origin_tech = "magnets=3;biotech=2" - var/HUDType = null //Hudtype is defined on glasses.dm - prescription_upgradable = 1 - var/list/icon/current = list() //the current hud icons - - -/obj/item/clothing/glasses/hud/equipped(mob/living/carbon/human/user, slot) - ..() - if(HUDType && slot == slot_glasses) - var/datum/atom_hud/H = huds[HUDType] - H.add_hud_to(user) - -/obj/item/clothing/glasses/hud/dropped(mob/living/carbon/human/user) - ..() - if(HUDType && istype(user) && user.glasses == src) - var/datum/atom_hud/H = huds[HUDType] - H.remove_hud_from(user) - -/obj/item/clothing/glasses/hud/emp_act(severity) - if(emagged == 0) - emagged = 1 - desc = desc + " The display flickers slightly." - -/obj/item/clothing/glasses/hud/health - name = "\improper Health Scanner HUD" - desc = "A heads-up display that scans the humans in view and provides accurate data about their health status." - icon_state = "healthhud" - origin_tech = "magnets=3;biotech=2" - HUDType = DATA_HUD_MEDICAL_ADVANCED - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/hud/health/night - name = "\improper Night Vision Health Scanner HUD" - desc = "An advanced medical head-up display that allows doctors to find patients in complete darkness." - icon_state = "healthhudnight" - item_state = "glasses" - origin_tech = "magnets=4;biotech=4;plasmatech=4;engineering=5" - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - prescription_upgradable = 0 - -/obj/item/clothing/glasses/hud/health/sunglasses - name = "medical HUDSunglasses" - desc = "Sunglasses with a medical HUD." - icon_state = "sunhudmed" - see_in_dark = 1 - flash_protect = 1 - tint = 1 - -/obj/item/clothing/glasses/hud/diagnostic - name = "Diagnostic HUD" - desc = "A heads-up display capable of analyzing the integrity and status of robotics and exosuits." - icon_state = "diagnostichud" - origin_tech = "magnets=2;engineering=2" - HUDType = DATA_HUD_DIAGNOSTIC - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/hud/diagnostic/night - name = "Night Vision Diagnostic HUD" - desc = "A robotics diagnostic HUD fitted with a light amplifier." - icon_state = "diagnostichudnight" - item_state = "glasses" - origin_tech = "magnets=4;powerstorage=4;plasmatech=4;engineering=5" - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - prescription_upgradable = 0 - -/obj/item/clothing/glasses/hud/diagnostic/sunglasses - name = "diagnostic sunglasses" - desc = "Sunglasses with a diagnostic HUD." - icon_state = "sunhuddiag" - item_state = "glasses" - flash_protect = 1 - tint = 1 - -/obj/item/clothing/glasses/hud/security - name = "\improper Security HUD" - desc = "A heads-up display that scans the humans in view and provides accurate data about their ID status and security records." - icon_state = "securityhud" - origin_tech = "magnets=3;combat=2" - var/global/list/jobs[0] - HUDType = DATA_HUD_SECURITY_ADVANCED - var/read_only = FALSE - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - - -/obj/item/clothing/glasses/hud/security/sunglasses/jensenshades - name = "augmented shades" - desc = "Polarized bioneural eyewear, designed to augment your vision." - icon_state = "jensenshades" - item_state = "jensenshades" - vision_flags = SEE_MOBS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - -/obj/item/clothing/glasses/hud/security/night - name = "\improper Night Vision Security HUD" - desc = "An advanced heads-up display which provides id data and vision in complete darkness." - icon_state = "securityhudnight" - origin_tech = "magnets=4;combat=4;plasmatech=4;engineering=5" - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these - prescription_upgradable = 0 - -/obj/item/clothing/glasses/hud/security/sunglasses/read_only - read_only = TRUE - -/obj/item/clothing/glasses/hud/security/sunglasses - name = "HUDSunglasses" - desc = "Sunglasses with a HUD." - icon_state = "sunhud" - origin_tech = "magnets=3;combat=3;engineering=3" - see_in_dark = 1 - flash_protect = 1 - tint = 1 - prescription_upgradable = 1 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/hud/security/sunglasses/prescription - prescription = 1 - -/obj/item/clothing/glasses/hud/hydroponic - name = "Hydroponic HUD" - desc = "A heads-up display capable of analyzing the health and status of plants growing in hydro trays and soil." - icon_state = "hydroponichud" - HUDType = DATA_HUD_HYDROPONIC - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi' - ) - -/obj/item/clothing/glasses/hud/hydroponic/night - name = "Night Vision Hydroponic HUD" - desc = "A hydroponic HUD fitted with a light amplifier." - icon_state = "hydroponichudnight" - item_state = "glasses" - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - prescription_upgradable = 0 - -/obj/item/clothing/glasses/hud/security/tajblind - name = "sleek veil" - desc = "An Ahdominian made veil that allows the user to see while obscuring their eyes. This one has an in-built security HUD." - icon_state = "tajblind_sec" - item_state = "tajblind_sec" - flags_cover = GLASSESCOVERSEYES - actions_types = list(/datum/action/item_action/toggle) - up = 0 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi' - ) - -/obj/item/clothing/glasses/hud/security/tajblind/attack_self() - toggle_veil() - -/obj/item/clothing/glasses/hud/health/tajblind - name = "lightweight veil" - desc = "An Ahdominian made veil that allows the user to see while obscuring their eyes. This one has an installed medical HUD." - icon_state = "tajblind_med" - item_state = "tajblind_med" - flags_cover = GLASSESCOVERSEYES - actions_types = list(/datum/action/item_action/toggle) - up = 0 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/eyes.dmi', - "Grey" = 'icons/mob/species/grey/eyes.dmi', - "Drask" = 'icons/mob/species/drask/eyes.dmi' - ) - -/obj/item/clothing/glasses/hud/health/tajblind/attack_self() - toggle_veil() +/obj/item/clothing/glasses/hud + name = "\improper HUD" + desc = "A heads-up display that provides important info in (almost) real time." + flags = null //doesn't protect eyes because it's a monocle, duh + origin_tech = "magnets=3;biotech=2" + var/HUDType = null //Hudtype is defined on glasses.dm + prescription_upgradable = 1 + var/list/icon/current = list() //the current hud icons + + +/obj/item/clothing/glasses/hud/equipped(mob/living/carbon/human/user, slot) + ..() + if(HUDType && slot == slot_glasses) + var/datum/atom_hud/H = huds[HUDType] + H.add_hud_to(user) + +/obj/item/clothing/glasses/hud/dropped(mob/living/carbon/human/user) + ..() + if(HUDType && istype(user) && user.glasses == src) + var/datum/atom_hud/H = huds[HUDType] + H.remove_hud_from(user) + +/obj/item/clothing/glasses/hud/emp_act(severity) + if(emagged == 0) + emagged = 1 + desc = desc + " The display flickers slightly." + +/obj/item/clothing/glasses/hud/health + name = "\improper Health Scanner HUD" + desc = "A heads-up display that scans the humans in view and provides accurate data about their health status." + icon_state = "healthhud" + origin_tech = "magnets=3;biotech=2" + HUDType = DATA_HUD_MEDICAL_ADVANCED + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/hud/health/night + name = "\improper Night Vision Health Scanner HUD" + desc = "An advanced medical head-up display that allows doctors to find patients in complete darkness." + icon_state = "healthhudnight" + item_state = "glasses" + origin_tech = "magnets=4;biotech=4;plasmatech=4;engineering=5" + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + prescription_upgradable = 0 + +/obj/item/clothing/glasses/hud/health/sunglasses + name = "medical HUDSunglasses" + desc = "Sunglasses with a medical HUD." + icon_state = "sunhudmed" + see_in_dark = 1 + flash_protect = 1 + tint = 1 + +/obj/item/clothing/glasses/hud/diagnostic + name = "Diagnostic HUD" + desc = "A heads-up display capable of analyzing the integrity and status of robotics and exosuits." + icon_state = "diagnostichud" + origin_tech = "magnets=2;engineering=2" + HUDType = DATA_HUD_DIAGNOSTIC + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/hud/diagnostic/night + name = "Night Vision Diagnostic HUD" + desc = "A robotics diagnostic HUD fitted with a light amplifier." + icon_state = "diagnostichudnight" + item_state = "glasses" + origin_tech = "magnets=4;powerstorage=4;plasmatech=4;engineering=5" + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + prescription_upgradable = 0 + +/obj/item/clothing/glasses/hud/diagnostic/sunglasses + name = "diagnostic sunglasses" + desc = "Sunglasses with a diagnostic HUD." + icon_state = "sunhuddiag" + item_state = "glasses" + flash_protect = 1 + tint = 1 + +/obj/item/clothing/glasses/hud/security + name = "\improper Security HUD" + desc = "A heads-up display that scans the humans in view and provides accurate data about their ID status and security records." + icon_state = "securityhud" + origin_tech = "magnets=3;combat=2" + var/global/list/jobs[0] + HUDType = DATA_HUD_SECURITY_ADVANCED + var/read_only = FALSE + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + + +/obj/item/clothing/glasses/hud/security/sunglasses/jensenshades + name = "augmented shades" + desc = "Polarized bioneural eyewear, designed to augment your vision." + icon_state = "jensenshades" + item_state = "jensenshades" + vision_flags = SEE_MOBS + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + +/obj/item/clothing/glasses/hud/security/night + name = "\improper Night Vision Security HUD" + desc = "An advanced heads-up display which provides id data and vision in complete darkness." + icon_state = "securityhudnight" + origin_tech = "magnets=4;combat=4;plasmatech=4;engineering=5" + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these + prescription_upgradable = 0 + +/obj/item/clothing/glasses/hud/security/sunglasses/read_only + read_only = TRUE + +/obj/item/clothing/glasses/hud/security/sunglasses + name = "HUDSunglasses" + desc = "Sunglasses with a HUD." + icon_state = "sunhud" + origin_tech = "magnets=3;combat=3;engineering=3" + see_in_dark = 1 + flash_protect = 1 + tint = 1 + prescription_upgradable = 1 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/hud/security/sunglasses/prescription + prescription = 1 + +/obj/item/clothing/glasses/hud/hydroponic + name = "Hydroponic HUD" + desc = "A heads-up display capable of analyzing the health and status of plants growing in hydro trays and soil." + icon_state = "hydroponichud" + HUDType = DATA_HUD_HYDROPONIC + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi' + ) + +/obj/item/clothing/glasses/hud/hydroponic/night + name = "Night Vision Hydroponic HUD" + desc = "A hydroponic HUD fitted with a light amplifier." + icon_state = "hydroponichudnight" + item_state = "glasses" + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + prescription_upgradable = 0 + +/obj/item/clothing/glasses/hud/security/tajblind + name = "sleek veil" + desc = "An Ahdominian made veil that allows the user to see while obscuring their eyes. This one has an in-built security HUD." + icon_state = "tajblind_sec" + item_state = "tajblind_sec" + flags_cover = GLASSESCOVERSEYES + actions_types = list(/datum/action/item_action/toggle) + up = 0 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi' + ) + +/obj/item/clothing/glasses/hud/security/tajblind/attack_self() + toggle_veil() + +/obj/item/clothing/glasses/hud/health/tajblind + name = "lightweight veil" + desc = "An Ahdominian made veil that allows the user to see while obscuring their eyes. This one has an installed medical HUD." + icon_state = "tajblind_med" + item_state = "tajblind_med" + flags_cover = GLASSESCOVERSEYES + actions_types = list(/datum/action/item_action/toggle) + up = 0 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/eyes.dmi', + "Grey" = 'icons/mob/species/grey/eyes.dmi', + "Drask" = 'icons/mob/species/drask/eyes.dmi' + ) + +/obj/item/clothing/glasses/hud/health/tajblind/attack_self() + toggle_veil() diff --git a/code/modules/clothing/gloves/boxing.dm b/code/modules/clothing/gloves/boxing.dm index c31e1abf4b5..701984de055 100644 --- a/code/modules/clothing/gloves/boxing.dm +++ b/code/modules/clothing/gloves/boxing.dm @@ -1,19 +1,19 @@ -/obj/item/clothing/gloves/boxing - name = "boxing gloves" - desc = "Because you really needed another excuse to punch your crewmates." - icon_state = "boxing" - item_state = "boxing" - put_on_delay = 60 - species_exception = list(/datum/species/golem) // now you too can be a golem boxing champion - -/obj/item/clothing/gloves/boxing/green - icon_state = "boxinggreen" - item_state = "boxinggreen" - -/obj/item/clothing/gloves/boxing/blue - icon_state = "boxingblue" - item_state = "boxingblue" - -/obj/item/clothing/gloves/boxing/yellow - icon_state = "boxingyellow" - item_state = "boxingyellow" \ No newline at end of file +/obj/item/clothing/gloves/boxing + name = "boxing gloves" + desc = "Because you really needed another excuse to punch your crewmates." + icon_state = "boxing" + item_state = "boxing" + put_on_delay = 60 + species_exception = list(/datum/species/golem) // now you too can be a golem boxing champion + +/obj/item/clothing/gloves/boxing/green + icon_state = "boxinggreen" + item_state = "boxinggreen" + +/obj/item/clothing/gloves/boxing/blue + icon_state = "boxingblue" + item_state = "boxingblue" + +/obj/item/clothing/gloves/boxing/yellow + icon_state = "boxingyellow" + item_state = "boxingyellow" diff --git a/code/modules/clothing/gloves/color.dm b/code/modules/clothing/gloves/color.dm index cbf9d0695ce..ec4ac9bbf15 100644 --- a/code/modules/clothing/gloves/color.dm +++ b/code/modules/clothing/gloves/color.dm @@ -1,240 +1,240 @@ -/obj/item/clothing/gloves/color/yellow - desc = "These gloves will protect the wearer from electric shock." - name = "insulated gloves" - icon_state = "yellow" - item_state = "ygloves" - siemens_coefficient = 0 - permeability_coefficient = 0.05 - item_color="yellow" - resistance_flags = NONE - -/obj/item/clothing/gloves/color/yellow/power - description_antag = "These are a pair of power gloves, and can be used to fire bolts of electricity while standing over powered power cables." - var/old_mclick_override - var/datum/middleClickOverride/power_gloves/mclick_override = new /datum/middleClickOverride/power_gloves - var/last_shocked = 0 - var/shock_delay = 40 - var/unlimited_power = FALSE // Does this really need explanation? - -/obj/item/clothing/gloves/color/yellow/power/equipped(mob/user, slot) - if(!ishuman(user)) - return - var/mob/living/carbon/human/H = user - if(slot == slot_gloves) - if(H.middleClickOverride) - old_mclick_override = H.middleClickOverride - H.middleClickOverride = mclick_override - if(!unlimited_power) - to_chat(H, "You feel electricity begin to build up in [src].") - else - to_chat(H, "You feel like you have UNLIMITED POWER!!") - -/obj/item/clothing/gloves/color/yellow/power/dropped(mob/user, slot) - if(!ishuman(user)) - return - var/mob/living/carbon/human/H = user - if(H.get_item_by_slot(slot_gloves) == src && H.middleClickOverride == mclick_override) - if(old_mclick_override) - H.middleClickOverride = old_mclick_override - old_mclick_override = null - else - H.middleClickOverride = null - -/obj/item/clothing/gloves/color/yellow/power/unlimited - name = "UNLIMITED POWER gloves" - desc = "These gloves possess UNLIMITED POWER." - shock_delay = 0 - unlimited_power = TRUE - -/obj/item/clothing/gloves/color/yellow/fake - desc = "These gloves will protect the wearer from electric shock. They don't feel like rubber..." - siemens_coefficient = 1 - -/obj/item/clothing/gloves/color/fyellow //Cheap Chinese Crap - desc = "These gloves are cheap copies of the coveted gloves, no way this can end badly." - name = "budget insulated gloves" - icon_state = "yellow" - item_state = "ygloves" - siemens_coefficient = 1 //Set to a default of 1, gets overridden in New() - permeability_coefficient = 0.05 - item_color="yellow" - resistance_flags = NONE - -/obj/item/clothing/gloves/color/fyellow/New() - ..() - siemens_coefficient = pick(0,0.5,0.5,0.5,0.5,0.75,1.5) - -/obj/item/clothing/gloves/color/fyellow/old - desc = "Old and worn out insulated gloves, hopefully they still work." - name = "worn out insulated gloves" - -/obj/item/clothing/gloves/color/fyellow/old/New() - ..() - siemens_coefficient = pick(0,0,0,0.5,0.5,0.5,0.75) - -/obj/item/clothing/gloves/color/black - desc = "These gloves are fire-resistant." - name = "black gloves" - icon_state = "black" - item_state = "bgloves" - item_color="brown" - cold_protection = HANDS - min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT - heat_protection = HANDS - max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT - resistance_flags = NONE - var/can_be_cut = 1 - - -/obj/item/clothing/gloves/color/black/hos - item_color = "hosred" //Exists for washing machines. Is not different from black gloves in any way. - -/obj/item/clothing/gloves/color/black/ce - item_color = "chief" //Exists for washing machines. Is not different from black gloves in any way. - -/obj/item/clothing/gloves/color/black/thief - pickpocket = 1 - -/obj/item/clothing/gloves/color/black/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/wirecutters)) - if(can_be_cut && icon_state == initial(icon_state))//only if not dyed - var/confirm = alert("Do you want to cut off the gloves fingertips? Warning: It might destroy their functionality.","Cut tips?","Yes","No") - if(get_dist(user, src) > 1) - to_chat(user, "You have moved too far away.") - return - if(confirm == "Yes") - to_chat(user, "You snip the fingertips off of [src].") - playsound(user.loc, W.usesound, rand(10,50), 1) - var/obj/item/clothing/gloves/fingerless/F = new/obj/item/clothing/gloves/fingerless(user.loc) - if(pickpocket) - F.pickpocket = FALSE - qdel(src) - return - ..() - -/obj/item/clothing/gloves/color/orange - name = "orange gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "orange" - item_state = "orangegloves" - item_color="orange" - -/obj/item/clothing/gloves/color/red - name = "red gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "red" - item_state = "redgloves" - item_color = "red" - -/obj/item/clothing/gloves/color/red/insulated - name = "insulated gloves" - desc = "These gloves will protect the wearer from electric shock." - siemens_coefficient = 0 - permeability_coefficient = 0.05 - resistance_flags = NONE - -/obj/item/clothing/gloves/color/rainbow - name = "rainbow gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "rainbow" - item_state = "rainbowgloves" - item_color = "rainbow" - -/obj/item/clothing/gloves/color/rainbow/clown - item_color = "clown" - -/obj/item/clothing/gloves/color/blue - name = "blue gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "blue" - item_state = "bluegloves" - item_color="blue" - -/obj/item/clothing/gloves/color/purple - name = "purple gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "purple" - item_state = "purplegloves" - item_color="purple" - -/obj/item/clothing/gloves/color/green - name = "green gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "green" - item_state = "greengloves" - item_color="green" - -/obj/item/clothing/gloves/color/grey - name = "grey gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "gray" - item_state = "graygloves" - item_color="grey" - -/obj/item/clothing/gloves/color/grey/rd - item_color = "director" //Exists for washing machines. Is not different from gray gloves in any way. - -/obj/item/clothing/gloves/color/grey/hop - item_color = "hop" //Exists for washing machines. Is not different from gray gloves in any way. - -/obj/item/clothing/gloves/color/light_brown - name = "light brown gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "lightbrown" - item_state = "lightbrowngloves" - item_color="light brown" - -/obj/item/clothing/gloves/color/brown - name = "brown gloves" - desc = "A pair of gloves, they don't look special in any way." - icon_state = "brown" - item_state = "browngloves" - item_color="brown" - -/obj/item/clothing/gloves/color/brown/cargo - item_color = "cargo" //Exists for washing machines. Is not different from brown gloves in any way. - -/obj/item/clothing/gloves/color/latex - name = "latex gloves" - desc = "Cheap sterile gloves made from latex." - icon_state = "latex" - item_state = "lgloves" - siemens_coefficient = 0.30 - permeability_coefficient = 0.01 - item_color="white" - transfer_prints = TRUE - resistance_flags = NONE - -/obj/item/clothing/gloves/color/latex/nitrile - name = "nitrile gloves" - desc = "Pricy sterile gloves that are stronger than latex." - icon_state = "nitrile" - item_state = "nitrilegloves" - transfer_prints = FALSE - item_color = "medical" - -/obj/item/clothing/gloves/color/white - name = "white gloves" - desc = "These look pretty fancy." - icon_state = "white" - item_state = "wgloves" - item_color="mime" - -/obj/item/clothing/gloves/color/white/redcoat - item_color = "redcoat" //Exists for washing machines. Is not different from white gloves in any way. - - -/obj/item/clothing/gloves/color/captain - desc = "Regal blue gloves, with a nice gold trim. Swanky." - name = "captain's gloves" - icon_state = "captain" - item_state = "egloves" - item_color = "captain" - siemens_coefficient = 0 - permeability_coefficient = 0.05 - cold_protection = HANDS - min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT - heat_protection = HANDS - max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT - strip_delay = 60 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 50) \ No newline at end of file +/obj/item/clothing/gloves/color/yellow + desc = "These gloves will protect the wearer from electric shock." + name = "insulated gloves" + icon_state = "yellow" + item_state = "ygloves" + siemens_coefficient = 0 + permeability_coefficient = 0.05 + item_color="yellow" + resistance_flags = NONE + +/obj/item/clothing/gloves/color/yellow/power + description_antag = "These are a pair of power gloves, and can be used to fire bolts of electricity while standing over powered power cables." + var/old_mclick_override + var/datum/middleClickOverride/power_gloves/mclick_override = new /datum/middleClickOverride/power_gloves + var/last_shocked = 0 + var/shock_delay = 40 + var/unlimited_power = FALSE // Does this really need explanation? + +/obj/item/clothing/gloves/color/yellow/power/equipped(mob/user, slot) + if(!ishuman(user)) + return + var/mob/living/carbon/human/H = user + if(slot == slot_gloves) + if(H.middleClickOverride) + old_mclick_override = H.middleClickOverride + H.middleClickOverride = mclick_override + if(!unlimited_power) + to_chat(H, "You feel electricity begin to build up in [src].") + else + to_chat(H, "You feel like you have UNLIMITED POWER!!") + +/obj/item/clothing/gloves/color/yellow/power/dropped(mob/user, slot) + if(!ishuman(user)) + return + var/mob/living/carbon/human/H = user + if(H.get_item_by_slot(slot_gloves) == src && H.middleClickOverride == mclick_override) + if(old_mclick_override) + H.middleClickOverride = old_mclick_override + old_mclick_override = null + else + H.middleClickOverride = null + +/obj/item/clothing/gloves/color/yellow/power/unlimited + name = "UNLIMITED POWER gloves" + desc = "These gloves possess UNLIMITED POWER." + shock_delay = 0 + unlimited_power = TRUE + +/obj/item/clothing/gloves/color/yellow/fake + desc = "These gloves will protect the wearer from electric shock. They don't feel like rubber..." + siemens_coefficient = 1 + +/obj/item/clothing/gloves/color/fyellow //Cheap Chinese Crap + desc = "These gloves are cheap copies of the coveted gloves, no way this can end badly." + name = "budget insulated gloves" + icon_state = "yellow" + item_state = "ygloves" + siemens_coefficient = 1 //Set to a default of 1, gets overridden in New() + permeability_coefficient = 0.05 + item_color="yellow" + resistance_flags = NONE + +/obj/item/clothing/gloves/color/fyellow/New() + ..() + siemens_coefficient = pick(0,0.5,0.5,0.5,0.5,0.75,1.5) + +/obj/item/clothing/gloves/color/fyellow/old + desc = "Old and worn out insulated gloves, hopefully they still work." + name = "worn out insulated gloves" + +/obj/item/clothing/gloves/color/fyellow/old/New() + ..() + siemens_coefficient = pick(0,0,0,0.5,0.5,0.5,0.75) + +/obj/item/clothing/gloves/color/black + desc = "These gloves are fire-resistant." + name = "black gloves" + icon_state = "black" + item_state = "bgloves" + item_color="brown" + cold_protection = HANDS + min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT + heat_protection = HANDS + max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT + resistance_flags = NONE + var/can_be_cut = 1 + + +/obj/item/clothing/gloves/color/black/hos + item_color = "hosred" //Exists for washing machines. Is not different from black gloves in any way. + +/obj/item/clothing/gloves/color/black/ce + item_color = "chief" //Exists for washing machines. Is not different from black gloves in any way. + +/obj/item/clothing/gloves/color/black/thief + pickpocket = 1 + +/obj/item/clothing/gloves/color/black/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/wirecutters)) + if(can_be_cut && icon_state == initial(icon_state))//only if not dyed + var/confirm = alert("Do you want to cut off the gloves fingertips? Warning: It might destroy their functionality.","Cut tips?","Yes","No") + if(get_dist(user, src) > 1) + to_chat(user, "You have moved too far away.") + return + if(confirm == "Yes") + to_chat(user, "You snip the fingertips off of [src].") + playsound(user.loc, W.usesound, rand(10,50), 1) + var/obj/item/clothing/gloves/fingerless/F = new/obj/item/clothing/gloves/fingerless(user.loc) + if(pickpocket) + F.pickpocket = FALSE + qdel(src) + return + ..() + +/obj/item/clothing/gloves/color/orange + name = "orange gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "orange" + item_state = "orangegloves" + item_color="orange" + +/obj/item/clothing/gloves/color/red + name = "red gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "red" + item_state = "redgloves" + item_color = "red" + +/obj/item/clothing/gloves/color/red/insulated + name = "insulated gloves" + desc = "These gloves will protect the wearer from electric shock." + siemens_coefficient = 0 + permeability_coefficient = 0.05 + resistance_flags = NONE + +/obj/item/clothing/gloves/color/rainbow + name = "rainbow gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "rainbow" + item_state = "rainbowgloves" + item_color = "rainbow" + +/obj/item/clothing/gloves/color/rainbow/clown + item_color = "clown" + +/obj/item/clothing/gloves/color/blue + name = "blue gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "blue" + item_state = "bluegloves" + item_color="blue" + +/obj/item/clothing/gloves/color/purple + name = "purple gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "purple" + item_state = "purplegloves" + item_color="purple" + +/obj/item/clothing/gloves/color/green + name = "green gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "green" + item_state = "greengloves" + item_color="green" + +/obj/item/clothing/gloves/color/grey + name = "grey gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "gray" + item_state = "graygloves" + item_color="grey" + +/obj/item/clothing/gloves/color/grey/rd + item_color = "director" //Exists for washing machines. Is not different from gray gloves in any way. + +/obj/item/clothing/gloves/color/grey/hop + item_color = "hop" //Exists for washing machines. Is not different from gray gloves in any way. + +/obj/item/clothing/gloves/color/light_brown + name = "light brown gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "lightbrown" + item_state = "lightbrowngloves" + item_color="light brown" + +/obj/item/clothing/gloves/color/brown + name = "brown gloves" + desc = "A pair of gloves, they don't look special in any way." + icon_state = "brown" + item_state = "browngloves" + item_color="brown" + +/obj/item/clothing/gloves/color/brown/cargo + item_color = "cargo" //Exists for washing machines. Is not different from brown gloves in any way. + +/obj/item/clothing/gloves/color/latex + name = "latex gloves" + desc = "Cheap sterile gloves made from latex." + icon_state = "latex" + item_state = "lgloves" + siemens_coefficient = 0.30 + permeability_coefficient = 0.01 + item_color="white" + transfer_prints = TRUE + resistance_flags = NONE + +/obj/item/clothing/gloves/color/latex/nitrile + name = "nitrile gloves" + desc = "Pricy sterile gloves that are stronger than latex." + icon_state = "nitrile" + item_state = "nitrilegloves" + transfer_prints = FALSE + item_color = "medical" + +/obj/item/clothing/gloves/color/white + name = "white gloves" + desc = "These look pretty fancy." + icon_state = "white" + item_state = "wgloves" + item_color="mime" + +/obj/item/clothing/gloves/color/white/redcoat + item_color = "redcoat" //Exists for washing machines. Is not different from white gloves in any way. + + +/obj/item/clothing/gloves/color/captain + desc = "Regal blue gloves, with a nice gold trim. Swanky." + name = "captain's gloves" + icon_state = "captain" + item_state = "egloves" + item_color = "captain" + siemens_coefficient = 0 + permeability_coefficient = 0.05 + cold_protection = HANDS + min_cold_protection_temperature = GLOVES_MIN_TEMP_PROTECT + heat_protection = HANDS + max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT + strip_delay = 60 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 50) diff --git a/code/modules/clothing/gloves/miscellaneous.dm b/code/modules/clothing/gloves/miscellaneous.dm index fef737982fa..429a87ba58a 100644 --- a/code/modules/clothing/gloves/miscellaneous.dm +++ b/code/modules/clothing/gloves/miscellaneous.dm @@ -143,13 +143,18 @@ update_icon() else to_chat(user, "[src] already has a cell.") + else + return ..() - else if(iswirecutter(W)) - if(cell) - to_chat(user, "You cut [cell] away from [src].") - cell.forceMove(get_turf(loc)) - cell = null - update_icon() +/obj/item/clothing/gloves/color/yellow/stun/wirecutter_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(cell) + to_chat(user, "You cut [cell] away from [src].") + cell.forceMove(get_turf(loc)) + cell = null + update_icon() /obj/item/clothing/gloves/fingerless/rapid name = "Gloves of the North Star" diff --git a/code/modules/clothing/gloves/rings.dm b/code/modules/clothing/gloves/rings.dm index 6bef80bfe6d..29e5af2121a 100644 --- a/code/modules/clothing/gloves/rings.dm +++ b/code/modules/clothing/gloves/rings.dm @@ -9,16 +9,14 @@ var/fluff_material = FALSE //If true, will ignore the material when examining var/material = "iron" var/stud = 0 + var/ring_color = "iron" /obj/item/clothing/gloves/ring/New() ..() update_icon() /obj/item/clothing/gloves/ring/update_icon() - if(stud) - icon_state = "d_[initial(icon_state)]" - else - icon_state = initial(icon_state) + icon_state = "[stud ? "d_" : ""][ring_color]ring" /obj/item/clothing/gloves/ring/examine(mob/user) . = ..() @@ -44,6 +42,7 @@ name = "silver ring" icon_state = "silverring" material = "silver" + ring_color = "silver" /obj/item/clothing/gloves/ring/silver/blessed // todo name = "blessed silver ring" @@ -52,6 +51,7 @@ name = "gold ring" icon_state = "goldring" material = "gold" + ring_color = "gold" /obj/item/clothing/gloves/ring/gold/blessed name = "wedding band" @@ -61,40 +61,45 @@ name = "white plastic ring" icon_state = "whitering" material = "plastic" + ring_color = "white" /obj/item/clothing/gloves/ring/plastic/blue name = "blue plastic ring" icon_state = "bluering" + ring_color = "blue" /obj/item/clothing/gloves/ring/plastic/red name = "red plastic ring" icon_state = "redring" - -/obj/item/clothing/gloves/ring/plastic/random + ring_color = "red" /obj/item/clothing/gloves/ring/plastic/random/New() - var/c = pick("white","blue","red") - name = "[c] plastic ring" - icon_state = "[c]ring" + ring_color = pick("white","blue","red") + name = "[ring_color] plastic ring" + ..() // weird /obj/item/clothing/gloves/ring/glass name = "glass ring" icon_state = "whitering" material = "glass" + ring_color = "white" /obj/item/clothing/gloves/ring/plasma name = "plasma ring" icon_state = "plasmaring" material = "plasma" + ring_color = "plasma" /obj/item/clothing/gloves/ring/uranium name = "uranium ring" icon_state = "uraniumring" material = "uranium" + ring_color = "uranium" // cultish /obj/item/clothing/gloves/ring/shadow name = "shadow ring" icon_state = "shadowring" material = "shadows" + ring_color = "shadow" diff --git a/code/modules/clothing/head/beanie.dm b/code/modules/clothing/head/beanie.dm index b912a07c212..ada61c41384 100644 --- a/code/modules/clothing/head/beanie.dm +++ b/code/modules/clothing/head/beanie.dm @@ -93,4 +93,4 @@ name = "rastacap" desc = "Perfect for tucking in those dreadlocks." icon_state = "beanierasta" - item_color = "beanierasta" \ No newline at end of file + item_color = "beanierasta" diff --git a/code/modules/clothing/head/collectable.dm b/code/modules/clothing/head/collectable.dm index cc5c3248b19..501ec562441 100644 --- a/code/modules/clothing/head/collectable.dm +++ b/code/modules/clothing/head/collectable.dm @@ -1,146 +1,146 @@ - -//Hat Station 13 - -/obj/item/clothing/head/collectable - name = "collectable hat" - desc = "A rare collectable hat." - -/obj/item/clothing/head/collectable/petehat - name = "ultra rare Pete's hat!" - desc = "It smells faintly of plasma" - icon_state = "petehat" - -/obj/item/clothing/head/collectable/slime - name = "collectable slime cap!" - desc = "It just latches right in place!" - icon_state = "slime" - -/obj/item/clothing/head/collectable/xenom - name = "collectable xenomorph helmet!" - desc = "Hiss hiss hiss!" - icon_state = "xenom" - -/obj/item/clothing/head/collectable/chef - name = "collectable chef's hat" - desc = "A rare Chef's Hat meant for hat collectors!" - icon_state = "chef" - item_state = "chef" - dog_fashion = /datum/dog_fashion/head/chef - -/obj/item/clothing/head/collectable/paper - name = "collectable paper hat" - desc = "What looks like an ordinary paper hat, is actually a rare and valuable collector's edition paper hat. Keep away from water, fire and Librarians." - icon_state = "paper" - dog_fashion = /datum/dog_fashion/head - -/obj/item/clothing/head/collectable/tophat - name = "collectable top hat" - desc = "A top hat worn by only the most prestigious hat collectors." - icon_state = "tophat" - item_state = "that" - dog_fashion = /datum/dog_fashion/head - -/obj/item/clothing/head/collectable/captain - name = "collectable captain's hat" - desc = "A Collectable Hat that'll make you look just like a real comdom!" - icon_state = "captain" - item_state = "caphat" - dog_fashion = /datum/dog_fashion/head/captain - -/obj/item/clothing/head/collectable/police - name = "collectable police officer's hat" - desc = "A Collectable Police Officer's Hat. This hat emphasizes that you are THE LAW." - icon_state = "policehelm" - dog_fashion = /datum/dog_fashion/head/warden - -/obj/item/clothing/head/collectable/beret - name = "collectable beret" - desc = "A Collectable red Beret. It smells faintly of Garlic." - icon_state = "beret" - dog_fashion = /datum/dog_fashion/head/beret - -/obj/item/clothing/head/collectable/welding - name = "collectable welding helmet" - desc = "A Collectable Welding Helmet. Now with 80% less lead! Not for actual welding. Any welding done while wearing this Helmet is done so at the owner's own risk!" - icon_state = "welding" - item_state = "welding" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi' - ) - -/obj/item/clothing/head/collectable/slime - name = "collectable slime hat" - desc = "Just like a real Brain Slug!" - icon_state = "headslime" - item_state = "headslime" - -/obj/item/clothing/head/collectable/flatcap - name = "collectable flat cap" - desc = "A Collectible farmer's Flat Cap!" - icon_state = "flat_cap" - item_state = "detective" - -/obj/item/clothing/head/collectable/pirate - name = "collectable pirate hat" - desc = "You'd make a great Dread Syndie Roberts!" - icon_state = "pirate" - item_state = "pirate" - dog_fashion = /datum/dog_fashion/head/pirate - -/obj/item/clothing/head/collectable/kitty - name = "collectable kitty ears" - desc = "The fur feels.....a bit too realistic." - icon_state = "kitty" - item_state = "kitty" - dog_fashion = /datum/dog_fashion/head/kitty - -/obj/item/clothing/head/collectable/rabbitears - name = "collectable rabbit ears" - desc = "Not as lucky as the feet!" - icon_state = "bunny" - item_state = "bunny" - dog_fashion = /datum/dog_fashion/head/rabbit - -/obj/item/clothing/head/collectable/wizard - name = "collectable wizard's hat" - desc = "NOTE:Any magical powers gained from wearing this hat are purely coincidental." - icon_state = "wizard" - dog_fashion = /datum/dog_fashion/head/blue_wizard - -/obj/item/clothing/head/collectable/hardhat - name = "collectable hard hat" - desc = "WARNING! Offers no real protection, or luminosity, but it is damn fancy!" - icon_state = "hardhat0_yellow" - item_state = "hardhat0_yellow" - dog_fashion = /datum/dog_fashion/head - -/obj/item/clothing/head/collectable/HoS - name = "collectable HoS hat" - desc = "Now you can beat prisoners, set silly sentences and arrest for no reason too!" - icon_state = "hoscap" - -/obj/item/clothing/head/collectable/HoP - name = "collectable HoP hat" - desc = "It's your turn to demand excessive paperwork, signatures, stamps, and hire more clowns! Papers, please!" - icon_state = "hopcap" - dog_fashion = /datum/dog_fashion/head/hop - -/obj/item/clothing/head/collectable/thunderdome - name = "collectable Thunderdome helmet" - desc = "Go Red! I mean Green! I mean Red! No Green!" - icon_state = "thunderdome" - item_state = "thunderdome" - -/obj/item/clothing/head/collectable/swat - name = "collectable SWAT helmet" - desc = "Now you can be in the Deathsquad too!" - icon_state = "swat" - item_state = "swat" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi' - ) + +//Hat Station 13 + +/obj/item/clothing/head/collectable + name = "collectable hat" + desc = "A rare collectable hat." + +/obj/item/clothing/head/collectable/petehat + name = "ultra rare Pete's hat!" + desc = "It smells faintly of plasma" + icon_state = "petehat" + +/obj/item/clothing/head/collectable/slime + name = "collectable slime cap!" + desc = "It just latches right in place!" + icon_state = "slime" + +/obj/item/clothing/head/collectable/xenom + name = "collectable xenomorph helmet!" + desc = "Hiss hiss hiss!" + icon_state = "xenom" + +/obj/item/clothing/head/collectable/chef + name = "collectable chef's hat" + desc = "A rare Chef's Hat meant for hat collectors!" + icon_state = "chef" + item_state = "chef" + dog_fashion = /datum/dog_fashion/head/chef + +/obj/item/clothing/head/collectable/paper + name = "collectable paper hat" + desc = "What looks like an ordinary paper hat, is actually a rare and valuable collector's edition paper hat. Keep away from water, fire and Librarians." + icon_state = "paper" + dog_fashion = /datum/dog_fashion/head + +/obj/item/clothing/head/collectable/tophat + name = "collectable top hat" + desc = "A top hat worn by only the most prestigious hat collectors." + icon_state = "tophat" + item_state = "that" + dog_fashion = /datum/dog_fashion/head + +/obj/item/clothing/head/collectable/captain + name = "collectable captain's hat" + desc = "A Collectable Hat that'll make you look just like a real comdom!" + icon_state = "captain" + item_state = "caphat" + dog_fashion = /datum/dog_fashion/head/captain + +/obj/item/clothing/head/collectable/police + name = "collectable police officer's hat" + desc = "A Collectable Police Officer's Hat. This hat emphasizes that you are THE LAW." + icon_state = "policehelm" + dog_fashion = /datum/dog_fashion/head/warden + +/obj/item/clothing/head/collectable/beret + name = "collectable beret" + desc = "A Collectable red Beret. It smells faintly of Garlic." + icon_state = "beret" + dog_fashion = /datum/dog_fashion/head/beret + +/obj/item/clothing/head/collectable/welding + name = "collectable welding helmet" + desc = "A Collectable Welding Helmet. Now with 80% less lead! Not for actual welding. Any welding done while wearing this Helmet is done so at the owner's own risk!" + icon_state = "welding" + item_state = "welding" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi' + ) + +/obj/item/clothing/head/collectable/slime + name = "collectable slime hat" + desc = "Just like a real Brain Slug!" + icon_state = "headslime" + item_state = "headslime" + +/obj/item/clothing/head/collectable/flatcap + name = "collectable flat cap" + desc = "A Collectible farmer's Flat Cap!" + icon_state = "flat_cap" + item_state = "detective" + +/obj/item/clothing/head/collectable/pirate + name = "collectable pirate hat" + desc = "You'd make a great Dread Syndie Roberts!" + icon_state = "pirate" + item_state = "pirate" + dog_fashion = /datum/dog_fashion/head/pirate + +/obj/item/clothing/head/collectable/kitty + name = "collectable kitty ears" + desc = "The fur feels.....a bit too realistic." + icon_state = "kitty" + item_state = "kitty" + dog_fashion = /datum/dog_fashion/head/kitty + +/obj/item/clothing/head/collectable/rabbitears + name = "collectable rabbit ears" + desc = "Not as lucky as the feet!" + icon_state = "bunny" + item_state = "bunny" + dog_fashion = /datum/dog_fashion/head/rabbit + +/obj/item/clothing/head/collectable/wizard + name = "collectable wizard's hat" + desc = "NOTE:Any magical powers gained from wearing this hat are purely coincidental." + icon_state = "wizard" + dog_fashion = /datum/dog_fashion/head/blue_wizard + +/obj/item/clothing/head/collectable/hardhat + name = "collectable hard hat" + desc = "WARNING! Offers no real protection, or luminosity, but it is damn fancy!" + icon_state = "hardhat0_yellow" + item_state = "hardhat0_yellow" + dog_fashion = /datum/dog_fashion/head + +/obj/item/clothing/head/collectable/HoS + name = "collectable HoS hat" + desc = "Now you can beat prisoners, set silly sentences and arrest for no reason too!" + icon_state = "hoscap" + +/obj/item/clothing/head/collectable/HoP + name = "collectable HoP hat" + desc = "It's your turn to demand excessive paperwork, signatures, stamps, and hire more clowns! Papers, please!" + icon_state = "hopcap" + dog_fashion = /datum/dog_fashion/head/hop + +/obj/item/clothing/head/collectable/thunderdome + name = "collectable Thunderdome helmet" + desc = "Go Red! I mean Green! I mean Red! No Green!" + icon_state = "thunderdome" + item_state = "thunderdome" + +/obj/item/clothing/head/collectable/swat + name = "collectable SWAT helmet" + desc = "Now you can be in the Deathsquad too!" + icon_state = "swat" + item_state = "swat" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi' + ) diff --git a/code/modules/clothing/head/hardhat.dm b/code/modules/clothing/head/hardhat.dm index 18b92433f83..b5d4b94038f 100644 --- a/code/modules/clothing/head/hardhat.dm +++ b/code/modules/clothing/head/hardhat.dm @@ -101,4 +101,4 @@ dog_fashion = null sprite_sheets = list( "Grey" = 'icons/mob/species/grey/helmet.dmi' - ) \ No newline at end of file + ) diff --git a/code/modules/clothing/head/helmet.dm b/code/modules/clothing/head/helmet.dm index d47e3c32365..2a9abf461c9 100644 --- a/code/modules/clothing/head/helmet.dm +++ b/code/modules/clothing/head/helmet.dm @@ -1,301 +1,301 @@ -/obj/item/clothing/head/helmet - name = "helmet" - desc = "Standard Security gear. Protects the head from impacts." - icon_state = "helmetmaterials" - flags = HEADBANGPROTECT - flags_cover = HEADCOVERSEYES - item_state = "helmetmaterials" - armor = list("melee" = 35, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - flags_inv = HIDEEARS|HIDEEYES - cold_protection = HEAD - min_cold_protection_temperature = HELMET_MIN_TEMP_PROTECT - heat_protection = HEAD - max_heat_protection_temperature = HELMET_MAX_TEMP_PROTECT - strip_delay = 60 - dog_fashion = /datum/dog_fashion/head/helmet - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi', - "Drask" = 'icons/mob/species/drask/helmet.dmi', - "Grey" = 'icons/mob/species/grey/helmet.dmi' - ) - -/obj/item/clothing/head/helmet/attack_self(mob/user) - if(can_toggle && !user.incapacitated()) - if(world.time > cooldown + toggle_cooldown) - cooldown = world.time - up = !up - flags ^= visor_flags - flags_inv ^= visor_flags_inv - icon_state = "[initial(icon_state)][up ? "up" : ""]" - to_chat(user, "[up ? alt_toggle_message : toggle_message] \the [src]") - - user.update_inv_head() - - if(active_sound) - while(up) - playsound(src.loc, "[active_sound]", 100, 0, 4) - sleep(15) - if(toggle_sound) - playsound(src.loc, "[toggle_sound]", 100, 0, 4) - - -/obj/item/clothing/head/helmet/visor - name = "visor helmet" - desc = "A helmet with a built-in visor. It doesn't seem to do anything, but it sure looks cool!" - icon_state = "helmetgoggles" - -/obj/item/clothing/head/helmet/thermal - name = "thermal visor helmet" - desc = "A helmet with a built-in thermal scanning visor." - icon_state = "helmetthermals" - vision_flags = SEE_MOBS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - -/obj/item/clothing/head/helmet/meson - name = "meson visor helmet" - desc = "A helmet with a built-in meson scanning visor." - icon_state = "helmetmesons" - vision_flags = SEE_TURFS - -/obj/item/clothing/head/helmet/material - name = "material visor helmet" - desc = "A helmet with a built-in material scanning visor." - icon_state = "helmetmaterials" - vision_flags = SEE_OBJS - -/obj/item/clothing/head/helmet/night - name = "night-vision helmet" - desc = "A helmet with a built-in pair of night vision goggles." - icon_state = "helmetNVG" - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these - -/obj/item/clothing/head/helmet/alt - name = "bulletproof helmet" - desc = "A bulletproof helmet that excels in protecting the wearer against traditional projectile weaponry and explosives to a minor extent." - icon_state = "swat" - item_state = "swat-alt" - armor = list("melee" = 15, "bullet" = 60, "laser" = 10, "energy" = 10, "bomb" = 40, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - dog_fashion = null - -/obj/item/clothing/head/helmet/riot - name = "riot helmet" - desc = "It's a helmet specifically designed to protect against close range attacks." - icon_state = "riot" - item_state = "helmet" - armor = list("melee" = 50, "bullet" = 10, "laser" = 10, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 80) - flags_inv = HIDEEARS - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - strip_delay = 80 - dog_fashion = null - -/obj/item/clothing/head/helmet/riot/knight - name = "medieval helmet" - desc = "A classic metal helmet." - icon_state = "knight_green" - item_state = "knight_green" - flags = BLOCKHAIR - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - dog_fashion = null - -/obj/item/clothing/head/helmet/justice - name = "helmet of justice" - desc = "WEEEEOOO. WEEEEEOOO. WEEEEOOOO." - icon_state = "justice" - toggle_message = "You turn off the lights on" - alt_toggle_message = "You turn on the lights on" - actions_types = list(/datum/action/item_action/toggle_helmet_light) - can_toggle = 1 - toggle_cooldown = 20 - active_sound = 'sound/items/weeoo1.ogg' - dog_fashion = null - -/obj/item/clothing/head/helmet/justice/escape - name = "alarm helmet" - desc = "WEEEEOOO. WEEEEEOOO. STOP THAT MONKEY. WEEEOOOO." - icon_state = "justice2" - toggle_message = "You turn off the light on" - alt_toggle_message = "You turn on the light on" - - -/obj/item/clothing/head/helmet/swat - name = "\improper SWAT helmet" - desc = "They're often used by highly trained Swat Members." - icon_state = "swat" - item_state = "swat" - armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 30, "bomb" = 50, "bio" = 90, "rad" = 20, "fire" = 50, "acid" = 50) - flags = null - flags_inv = HIDEEARS|HIDEEYES - cold_protection = HEAD - min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT - heat_protection = HEAD - max_heat_protection_temperature = SPACE_HELM_MAX_TEMP_PROTECT - strip_delay = 80 - dog_fashion = null - -/obj/item/clothing/head/helmet/swat/syndicate - name = "blood-red helmet" - desc = "An extremely robust, space-worthy helmet without a visor to allow for goggle usage underneath. Property of Gorlex Marauders." - icon_state = "helmetsyndi" - item_state = "helmet" - -/obj/item/clothing/head/helmet/thunderdome - name = "\improper Thunderdome helmet" - desc = "'Let the battle commence!'" - icon_state = "thunderdome" - flags = null - item_state = "thunderdome" - armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 90) - cold_protection = HEAD - min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT - heat_protection = HEAD - max_heat_protection_temperature = SPACE_HELM_MAX_TEMP_PROTECT - strip_delay = 80 - dog_fashion = null - -/obj/item/clothing/head/helmet/roman - name = "roman helmet" - desc = "An ancient helmet made of bronze and leather." - flags = null - armor = list("melee" = 25, "bullet" = 0, "laser" = 25, "energy" = 10, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50) - resistance_flags = FIRE_PROOF - icon_state = "roman" - item_state = "roman" - strip_delay = 100 - dog_fashion = null - -/obj/item/clothing/head/helmet/roman/fake - desc = "An ancient helmet made of plastic and leather." - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0) - -/obj/item/clothing/head/helmet/roman/legionaire - name = "roman legionaire helmet" - desc = "An ancient helmet made of bronze and leather. Has a red crest on top of it." - icon_state = "roman_c" - item_state = "roman_c" - -/obj/item/clothing/head/helmet/roman/legionaire/fake - desc = "An ancient helmet made of plastic and leather. Has a red crest on top of it." - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0) - -/obj/item/clothing/head/helmet/gladiator - name = "gladiator helmet" - desc = "Ave, Imperator, morituri te salutant." - icon_state = "gladiator" - flags = BLOCKHAIR - item_state = "gladiator" - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES - toggle_message = "You attach the face shield to the" - alt_toggle_message = "You remove the face shield from the" - actions_types = list(/datum/action/item_action/toggle_helmet_mode) - can_toggle = 1 - toggle_cooldown = 20 - toggle_sound = 'sound/items/zippoclose.ogg' - dog_fashion = null - -obj/item/clothing/head/helmet/redtaghelm - name = "red laser tag helmet" - desc = "They have chosen their own end." - icon_state = "redtaghelm" - flags = null - item_state = "redtaghelm" - armor = list("melee" = 15, "bullet" = 10, "laser" = 20,"energy" = 10, "bomb" = 20, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 50) - // Offer about the same protection as a hardhat. - flags_inv = HIDEEARS|HIDEEYES - dog_fashion = null - -obj/item/clothing/head/helmet/bluetaghelm - name = "blue laser tag helmet" - desc = "They'll need more men." - icon_state = "bluetaghelm" - flags = null - item_state = "bluetaghelm" - armor = list("melee" = 15, "bullet" = 10, "laser" = 20,"energy" = 10, "bomb" = 20, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 50) - // Offer about the same protection as a hardhat. - flags_inv = HIDEEARS|HIDEEYES - dog_fashion = null - -obj/item/clothing/head/blob - name = "blob hat" - desc = "A collectible hat handed out at the latest Blob Family Reunion." - icon_state = "blobhat" - item_state = "blobhat" - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi' - ) - -/obj/item/clothing/head/helmet/riot/knight/blue - icon_state = "knight_blue" - item_state = "knight_blue" - -/obj/item/clothing/head/helmet/riot/knight/yellow - icon_state = "knight_yellow" - item_state = "knight_yellow" - -/obj/item/clothing/head/helmet/riot/knight/red - icon_state = "knight_red" - item_state = "knight_red" - -/obj/item/clothing/head/helmet/riot/knight/templar - name = "crusader helmet" - desc = "Deus Vult." - icon_state = "knight_templar" - item_state = "knight_templar" - armor = list(melee = 20, bullet = 7, laser = 2, energy = 2, bomb = 2, bio = 2, rad = 0, fire = 80, acid = 80) - -/obj/item/clothing/head/helmet/skull - name = "skull helmet" - desc = "An intimidating tribal helmet, it doesn't look very comfortable." - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - flags_cover = HEADCOVERSEYES - armor = list("melee" = 35, "bullet" = 25, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - icon_state = "skull" - item_state = "skull" - strip_delay = 100 - -/obj/item/clothing/head/helmet/durathread - name = "durathread helmet" - desc = "A helmet made from durathread and leather." - icon_state = "durathread" - item_state = "durathread" - resistance_flags = FLAMMABLE - armor = list("melee" = 20, "bullet" = 10, "laser" = 30, "energy" = 5, "bomb" = 15, "bio" = 0, "rad" = 0, "fire" = 40, "acid" = 50) - strip_delay = 60 - -//Commander -/obj/item/clothing/head/helmet/ert/command - name = "emergency response team commander helmet" - desc = "An in-atmosphere helmet worn by the commander of a Nanotrasen Emergency Response Team. Has blue highlights." - icon_state = "erthelmet_cmd" - -//Security -/obj/item/clothing/head/helmet/ert/security - name = "emergency response team security helmet" - desc = "An in-atmosphere helmet worn by security members of the Nanotrasen Emergency Response Team. Has red highlights." - icon_state = "erthelmet_sec" - -/obj/item/clothing/head/helmet/ert/security/paranormal - name = "paranormal emergency response team helmet" - desc = "An in-atmosphere helmet worn by paranormal members of the Nanotrasen Emergency Response Team. Has crusader sigils." - icon_state = "knight_templar" - item_state = "knight_templar" - -//Engineer -/obj/item/clothing/head/helmet/ert/engineer - name = "emergency response team engineer helmet" - desc = "An in-atmosphere helmet worn by engineering members of the Nanotrasen Emergency Response Team. Has orange highlights." - icon_state = "erthelmet_eng" - -//Medical -/obj/item/clothing/head/helmet/ert/medical - name = "emergency response team medical helmet" - desc = "A set of armor worn by medical members of the Nanotrasen Emergency Response Team. Has red and white highlights." - icon_state = "erthelmet_med" - -//Janitorial -/obj/item/clothing/head/helmet/ert/janitor - name = "emergency response team janitor helmet" - desc = "A set of armor worn by janitorial members of the Nanotrasen Emergency Response Team. Has red and white highlights." - icon_state = "erthelmet_jan" +/obj/item/clothing/head/helmet + name = "helmet" + desc = "Standard Security gear. Protects the head from impacts." + icon_state = "helmetmaterials" + flags = HEADBANGPROTECT + flags_cover = HEADCOVERSEYES + item_state = "helmetmaterials" + armor = list("melee" = 35, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + flags_inv = HIDEEARS|HIDEEYES + cold_protection = HEAD + min_cold_protection_temperature = HELMET_MIN_TEMP_PROTECT + heat_protection = HEAD + max_heat_protection_temperature = HELMET_MAX_TEMP_PROTECT + strip_delay = 60 + dog_fashion = /datum/dog_fashion/head/helmet + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi', + "Drask" = 'icons/mob/species/drask/helmet.dmi', + "Grey" = 'icons/mob/species/grey/helmet.dmi' + ) + +/obj/item/clothing/head/helmet/attack_self(mob/user) + if(can_toggle && !user.incapacitated()) + if(world.time > cooldown + toggle_cooldown) + cooldown = world.time + up = !up + flags ^= visor_flags + flags_inv ^= visor_flags_inv + icon_state = "[initial(icon_state)][up ? "up" : ""]" + to_chat(user, "[up ? alt_toggle_message : toggle_message] \the [src]") + + user.update_inv_head() + + if(active_sound) + while(up) + playsound(src.loc, "[active_sound]", 100, 0, 4) + sleep(15) + if(toggle_sound) + playsound(src.loc, "[toggle_sound]", 100, 0, 4) + + +/obj/item/clothing/head/helmet/visor + name = "visor helmet" + desc = "A helmet with a built-in visor. It doesn't seem to do anything, but it sure looks cool!" + icon_state = "helmetgoggles" + +/obj/item/clothing/head/helmet/thermal + name = "thermal visor helmet" + desc = "A helmet with a built-in thermal scanning visor." + icon_state = "helmetthermals" + vision_flags = SEE_MOBS + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + +/obj/item/clothing/head/helmet/meson + name = "meson visor helmet" + desc = "A helmet with a built-in meson scanning visor." + icon_state = "helmetmesons" + vision_flags = SEE_TURFS + +/obj/item/clothing/head/helmet/material + name = "material visor helmet" + desc = "A helmet with a built-in material scanning visor." + icon_state = "helmetmaterials" + vision_flags = SEE_OBJS + +/obj/item/clothing/head/helmet/night + name = "night-vision helmet" + desc = "A helmet with a built-in pair of night vision goggles." + icon_state = "helmetNVG" + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these + +/obj/item/clothing/head/helmet/alt + name = "bulletproof helmet" + desc = "A bulletproof helmet that excels in protecting the wearer against traditional projectile weaponry and explosives to a minor extent." + icon_state = "swat" + item_state = "swat-alt" + armor = list("melee" = 15, "bullet" = 60, "laser" = 10, "energy" = 10, "bomb" = 40, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + dog_fashion = null + +/obj/item/clothing/head/helmet/riot + name = "riot helmet" + desc = "It's a helmet specifically designed to protect against close range attacks." + icon_state = "riot" + item_state = "helmet" + armor = list("melee" = 50, "bullet" = 10, "laser" = 10, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 80) + flags_inv = HIDEEARS + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + strip_delay = 80 + dog_fashion = null + +/obj/item/clothing/head/helmet/riot/knight + name = "medieval helmet" + desc = "A classic metal helmet." + icon_state = "knight_green" + item_state = "knight_green" + flags = BLOCKHAIR + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + dog_fashion = null + +/obj/item/clothing/head/helmet/justice + name = "helmet of justice" + desc = "WEEEEOOO. WEEEEEOOO. WEEEEOOOO." + icon_state = "justice" + toggle_message = "You turn off the lights on" + alt_toggle_message = "You turn on the lights on" + actions_types = list(/datum/action/item_action/toggle_helmet_light) + can_toggle = 1 + toggle_cooldown = 20 + active_sound = 'sound/items/weeoo1.ogg' + dog_fashion = null + +/obj/item/clothing/head/helmet/justice/escape + name = "alarm helmet" + desc = "WEEEEOOO. WEEEEEOOO. STOP THAT MONKEY. WEEEOOOO." + icon_state = "justice2" + toggle_message = "You turn off the light on" + alt_toggle_message = "You turn on the light on" + + +/obj/item/clothing/head/helmet/swat + name = "\improper SWAT helmet" + desc = "They're often used by highly trained Swat Members." + icon_state = "swat" + item_state = "swat" + armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 30, "bomb" = 50, "bio" = 90, "rad" = 20, "fire" = 50, "acid" = 50) + flags = null + flags_inv = HIDEEARS|HIDEEYES + cold_protection = HEAD + min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT + heat_protection = HEAD + max_heat_protection_temperature = SPACE_HELM_MAX_TEMP_PROTECT + strip_delay = 80 + dog_fashion = null + +/obj/item/clothing/head/helmet/swat/syndicate + name = "blood-red helmet" + desc = "An extremely robust, space-worthy helmet without a visor to allow for goggle usage underneath. Property of Gorlex Marauders." + icon_state = "helmetsyndi" + item_state = "helmet" + +/obj/item/clothing/head/helmet/thunderdome + name = "\improper Thunderdome helmet" + desc = "'Let the battle commence!'" + icon_state = "thunderdome" + flags = null + item_state = "thunderdome" + armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 90) + cold_protection = HEAD + min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT + heat_protection = HEAD + max_heat_protection_temperature = SPACE_HELM_MAX_TEMP_PROTECT + strip_delay = 80 + dog_fashion = null + +/obj/item/clothing/head/helmet/roman + name = "roman helmet" + desc = "An ancient helmet made of bronze and leather." + flags = null + armor = list("melee" = 25, "bullet" = 0, "laser" = 25, "energy" = 10, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50) + resistance_flags = FIRE_PROOF + icon_state = "roman" + item_state = "roman" + strip_delay = 100 + dog_fashion = null + +/obj/item/clothing/head/helmet/roman/fake + desc = "An ancient helmet made of plastic and leather." + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0) + +/obj/item/clothing/head/helmet/roman/legionaire + name = "roman legionaire helmet" + desc = "An ancient helmet made of bronze and leather. Has a red crest on top of it." + icon_state = "roman_c" + item_state = "roman_c" + +/obj/item/clothing/head/helmet/roman/legionaire/fake + desc = "An ancient helmet made of plastic and leather. Has a red crest on top of it." + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 0) + +/obj/item/clothing/head/helmet/gladiator + name = "gladiator helmet" + desc = "Ave, Imperator, morituri te salutant." + icon_state = "gladiator" + flags = BLOCKHAIR + item_state = "gladiator" + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES + toggle_message = "You attach the face shield to the" + alt_toggle_message = "You remove the face shield from the" + actions_types = list(/datum/action/item_action/toggle_helmet_mode) + can_toggle = 1 + toggle_cooldown = 20 + toggle_sound = 'sound/items/zippoclose.ogg' + dog_fashion = null + +obj/item/clothing/head/helmet/redtaghelm + name = "red laser tag helmet" + desc = "They have chosen their own end." + icon_state = "redtaghelm" + flags = null + item_state = "redtaghelm" + armor = list("melee" = 15, "bullet" = 10, "laser" = 20,"energy" = 10, "bomb" = 20, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 50) + // Offer about the same protection as a hardhat. + flags_inv = HIDEEARS|HIDEEYES + dog_fashion = null + +obj/item/clothing/head/helmet/bluetaghelm + name = "blue laser tag helmet" + desc = "They'll need more men." + icon_state = "bluetaghelm" + flags = null + item_state = "bluetaghelm" + armor = list("melee" = 15, "bullet" = 10, "laser" = 20,"energy" = 10, "bomb" = 20, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 50) + // Offer about the same protection as a hardhat. + flags_inv = HIDEEARS|HIDEEYES + dog_fashion = null + +obj/item/clothing/head/blob + name = "blob hat" + desc = "A collectible hat handed out at the latest Blob Family Reunion." + icon_state = "blobhat" + item_state = "blobhat" + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi' + ) + +/obj/item/clothing/head/helmet/riot/knight/blue + icon_state = "knight_blue" + item_state = "knight_blue" + +/obj/item/clothing/head/helmet/riot/knight/yellow + icon_state = "knight_yellow" + item_state = "knight_yellow" + +/obj/item/clothing/head/helmet/riot/knight/red + icon_state = "knight_red" + item_state = "knight_red" + +/obj/item/clothing/head/helmet/riot/knight/templar + name = "crusader helmet" + desc = "Deus Vult." + icon_state = "knight_templar" + item_state = "knight_templar" + armor = list(melee = 20, bullet = 7, laser = 2, energy = 2, bomb = 2, bio = 2, rad = 0, fire = 80, acid = 80) + +/obj/item/clothing/head/helmet/skull + name = "skull helmet" + desc = "An intimidating tribal helmet, it doesn't look very comfortable." + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + flags_cover = HEADCOVERSEYES + armor = list("melee" = 35, "bullet" = 25, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + icon_state = "skull" + item_state = "skull" + strip_delay = 100 + +/obj/item/clothing/head/helmet/durathread + name = "durathread helmet" + desc = "A helmet made from durathread and leather." + icon_state = "durathread" + item_state = "durathread" + resistance_flags = FLAMMABLE + armor = list("melee" = 20, "bullet" = 10, "laser" = 30, "energy" = 5, "bomb" = 15, "bio" = 0, "rad" = 0, "fire" = 40, "acid" = 50) + strip_delay = 60 + +//Commander +/obj/item/clothing/head/helmet/ert/command + name = "emergency response team commander helmet" + desc = "An in-atmosphere helmet worn by the commander of a Nanotrasen Emergency Response Team. Has blue highlights." + icon_state = "erthelmet_cmd" + +//Security +/obj/item/clothing/head/helmet/ert/security + name = "emergency response team security helmet" + desc = "An in-atmosphere helmet worn by security members of the Nanotrasen Emergency Response Team. Has red highlights." + icon_state = "erthelmet_sec" + +/obj/item/clothing/head/helmet/ert/security/paranormal + name = "paranormal emergency response team helmet" + desc = "An in-atmosphere helmet worn by paranormal members of the Nanotrasen Emergency Response Team. Has crusader sigils." + icon_state = "knight_templar" + item_state = "knight_templar" + +//Engineer +/obj/item/clothing/head/helmet/ert/engineer + name = "emergency response team engineer helmet" + desc = "An in-atmosphere helmet worn by engineering members of the Nanotrasen Emergency Response Team. Has orange highlights." + icon_state = "erthelmet_eng" + +//Medical +/obj/item/clothing/head/helmet/ert/medical + name = "emergency response team medical helmet" + desc = "A set of armor worn by medical members of the Nanotrasen Emergency Response Team. Has red and white highlights." + icon_state = "erthelmet_med" + +//Janitorial +/obj/item/clothing/head/helmet/ert/janitor + name = "emergency response team janitor helmet" + desc = "A set of armor worn by janitorial members of the Nanotrasen Emergency Response Team. Has red and white highlights." + icon_state = "erthelmet_jan" diff --git a/code/modules/clothing/head/jobs.dm b/code/modules/clothing/head/jobs.dm index e3ce5a62cd3..993f2050b51 100644 --- a/code/modules/clothing/head/jobs.dm +++ b/code/modules/clothing/head/jobs.dm @@ -1,190 +1,190 @@ - -//Bartender -/obj/item/clothing/head/chefhat - name = "chef's hat" - desc = "It's a hat used by chefs to keep hair out of your food. Judging by the food in the mess, they don't work." - icon_state = "chef" - item_state = "chef" - desc = "The commander in chef's head wear." - strip_delay = 10 - put_on_delay = 10 - dog_fashion = /datum/dog_fashion/head/chef - -//Captain -/obj/item/clothing/head/caphat - name = "captain's hat" - icon_state = "captain" - desc = "It's good being the king." - item_state = "caphat" - armor = list("melee" = 25, "bullet" = 15, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - strip_delay = 60 - dog_fashion = /datum/dog_fashion/head/captain - -//Captain: no longer space-worthy -/obj/item/clothing/head/caphat/parade - name = "captain's parade cap" - desc = "Worn only by Captains with an abundance of class." - icon_state = "capcap" - dog_fashion = null - -//Head of Personnel -/obj/item/clothing/head/hopcap - name = "head of personnel's cap" - icon_state = "hopcap" - desc = "The symbol of true bureaucratic micromanagement." - armor = list("melee" = 25, "bullet" = 15, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - dog_fashion = /datum/dog_fashion/head/hop - -//Nanotrasen Representative -/obj/item/clothing/head/ntrep - name = "Nanotrasen Representative hat" - desc = "A cap issued to the Nanotrasen Representative" - icon_state = "ntrep" - - -//Chaplain -/obj/item/clothing/head/hooded/chaplain_hood - name = "chaplain's hood" - desc = "It's hood that covers the head. It keeps you warm during the space winters." - icon_state = "chaplain_hood" - flags = BLOCKHAIR - flags_cover = HEADCOVERSEYES - -//Chaplain -/obj/item/clothing/head/hooded/nun_hood - name = "nun hood" - desc = "Maximum piety in this star system." - icon_state = "nun_hood" - flags = BLOCKHAIR - flags_cover = HEADCOVERSEYES - -//Chaplain -/obj/item/clothing/head/hooded/monk_hood - name = "monk hood" - desc = "Wooden board not included." - icon_state = "monk_hood" - flags = BLOCKHAIR - flags_cover = HEADCOVERSEYES - -/obj/item/clothing/head/witchhunter_hat - name = "witchhunter hat" - desc = "This hat saw much use back in the day." - icon_state = "witchhunterhat" - item_state = "witchhunterhat" - flags_cover = HEADCOVERSEYES - -/obj/item/clothing/head/det_hat - name = "hat" - desc = "Someone who wears this will look very smart." - icon_state = "detective" - allowed = list(/obj/item/reagent_containers/food/snacks/candy/candy_corn, /obj/item/pen) - armor = list("melee" = 25, "bullet" = 5, "laser" = 25, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 50) - dog_fashion = /datum/dog_fashion/head/detective - -//Mime -/obj/item/clothing/head/beret - name = "beret" - desc = "A beret, an artists favorite headwear." - icon_state = "beret" - dog_fashion = /datum/dog_fashion/head/beret - -/obj/item/clothing/head/beret/durathread - name = "durathread beret" - desc = "A beret made from durathread, its resilient fibres provide some protection to the wearer." - icon_state = "beretdurathread" - item_color = null - armor = list("melee" = 15, "bullet" = 5, "laser" = 15, "energy" = 5, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 5) - -//Security -/obj/item/clothing/head/HoS - name = "head of security cap" - desc = "The robust standard-issue cap of the Head of Security. For showing the officers who's in charge." - icon_state = "hoscap" - armor = list("melee" = 40, "bullet" = 30, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 50, "acid" = 60) - strip_delay = 80 - -/obj/item/clothing/head/HoS/beret - name = "head of security beret" - desc = "A robust beret for the Head of Security, for looking stylish while not sacrificing protection." - icon_state = "beret_hos_black" - -/obj/item/clothing/head/warden - name = "warden's police hat" - desc = "It's a special armored hat issued to the Warden of a security force. Protects the head from impacts." - icon_state = "policehelm" - armor = list("melee" = 40, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 60) - strip_delay = 60 - dog_fashion = /datum/dog_fashion/head/warden - -/obj/item/clothing/head/officer - name = "officer's cap" - desc = "A red cap with an old-fashioned badge on the front for establishing that you are, in fact, the law." - icon_state = "customshelm" - item_state = "customshelm" - armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) - strip_delay = 60 - -/obj/item/clothing/head/beret/sec - name = "security beret" - desc = "A beret with the security insignia emblazoned on it. For officers that are more inclined towards style than safety." - icon_state = "beret_officer" - armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) - strip_delay = 60 - dog_fashion = null - -/obj/item/clothing/head/beret/sec/warden - name = "warden's beret" - desc = "A special beret with the Warden's insignia emblazoned on it. For wardens with class." - icon_state = "beret_warden" - armor = list("melee" = 40, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 50) - -/obj/item/clothing/head/beret/eng - name = "engineering beret" - desc = "A beret with the engineering insignia emblazoned on it. For engineers that are more inclined towards style than safety." - icon_state = "beret_engineering" - -/obj/item/clothing/head/beret/atmos - name = "atmospherics beret" - desc = "A beret for those who have shown immaculate proficienty in piping. Or plumbing." - icon_state = "beret_atmospherics" - -/obj/item/clothing/head/beret/ce - name = "chief engineer beret" - desc = "A white beret with the engineering insignia emblazoned on it. Its owner knows what they're doing. Probably." - icon_state = "beret_ce" - -/obj/item/clothing/head/beret/sci - name = "science beret" - desc = "A purple beret with the science insignia emblazoned on it. It has that authentic burning plasma smell." - icon_state = "beret_sci" - -//Medical -/obj/item/clothing/head/beret/med - name = "medical beret" - desc = "A white beret with a green cross finely threaded into it. It has that sterile smell about it." - icon_state = "beret_med" - -/obj/item/clothing/head/surgery - name = "surgical cap" - desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs." - icon_state = "surgcap_blue" - flags = BLOCKHEADHAIR - sprite_sheets = list( - "Drask" = 'icons/mob/species/drask/head.dmi' - ) - -/obj/item/clothing/head/surgery/purple - desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is deep purple." - icon_state = "surgcap_purple" - -/obj/item/clothing/head/surgery/blue - desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is baby blue." - icon_state = "surgcap_blue" - -/obj/item/clothing/head/surgery/green - desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is dark green." - icon_state = "surgcap_green" - -/obj/item/clothing/head/surgery/black - desc = "A cap coroners wear during autopsies. Keeps their hair from falling into the cadavers. It is as dark than the coroner's humor." - icon_state = "surgcap_black" + +//Bartender +/obj/item/clothing/head/chefhat + name = "chef's hat" + desc = "It's a hat used by chefs to keep hair out of your food. Judging by the food in the mess, they don't work." + icon_state = "chef" + item_state = "chef" + desc = "The commander in chef's head wear." + strip_delay = 10 + put_on_delay = 10 + dog_fashion = /datum/dog_fashion/head/chef + +//Captain +/obj/item/clothing/head/caphat + name = "captain's hat" + icon_state = "captain" + desc = "It's good being the king." + item_state = "caphat" + armor = list("melee" = 25, "bullet" = 15, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + strip_delay = 60 + dog_fashion = /datum/dog_fashion/head/captain + +//Captain: no longer space-worthy +/obj/item/clothing/head/caphat/parade + name = "captain's parade cap" + desc = "Worn only by Captains with an abundance of class." + icon_state = "capcap" + dog_fashion = null + +//Head of Personnel +/obj/item/clothing/head/hopcap + name = "head of personnel's cap" + icon_state = "hopcap" + desc = "The symbol of true bureaucratic micromanagement." + armor = list("melee" = 25, "bullet" = 15, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + dog_fashion = /datum/dog_fashion/head/hop + +//Nanotrasen Representative +/obj/item/clothing/head/ntrep + name = "Nanotrasen Representative hat" + desc = "A cap issued to the Nanotrasen Representative" + icon_state = "ntrep" + + +//Chaplain +/obj/item/clothing/head/hooded/chaplain_hood + name = "chaplain's hood" + desc = "It's hood that covers the head. It keeps you warm during the space winters." + icon_state = "chaplain_hood" + flags = BLOCKHAIR + flags_cover = HEADCOVERSEYES + +//Chaplain +/obj/item/clothing/head/hooded/nun_hood + name = "nun hood" + desc = "Maximum piety in this star system." + icon_state = "nun_hood" + flags = BLOCKHAIR + flags_cover = HEADCOVERSEYES + +//Chaplain +/obj/item/clothing/head/hooded/monk_hood + name = "monk hood" + desc = "Wooden board not included." + icon_state = "monk_hood" + flags = BLOCKHAIR + flags_cover = HEADCOVERSEYES + +/obj/item/clothing/head/witchhunter_hat + name = "witchhunter hat" + desc = "This hat saw much use back in the day." + icon_state = "witchhunterhat" + item_state = "witchhunterhat" + flags_cover = HEADCOVERSEYES + +/obj/item/clothing/head/det_hat + name = "hat" + desc = "Someone who wears this will look very smart." + icon_state = "detective" + allowed = list(/obj/item/reagent_containers/food/snacks/candy/candy_corn, /obj/item/pen) + armor = list("melee" = 25, "bullet" = 5, "laser" = 25, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 50) + dog_fashion = /datum/dog_fashion/head/detective + +//Mime +/obj/item/clothing/head/beret + name = "beret" + desc = "A beret, an artists favorite headwear." + icon_state = "beret" + dog_fashion = /datum/dog_fashion/head/beret + +/obj/item/clothing/head/beret/durathread + name = "durathread beret" + desc = "A beret made from durathread, its resilient fibres provide some protection to the wearer." + icon_state = "beretdurathread" + item_color = null + armor = list("melee" = 15, "bullet" = 5, "laser" = 15, "energy" = 5, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 5) + +//Security +/obj/item/clothing/head/HoS + name = "head of security cap" + desc = "The robust standard-issue cap of the Head of Security. For showing the officers who's in charge." + icon_state = "hoscap" + armor = list("melee" = 40, "bullet" = 30, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 10, "rad" = 0, "fire" = 50, "acid" = 60) + strip_delay = 80 + +/obj/item/clothing/head/HoS/beret + name = "head of security beret" + desc = "A robust beret for the Head of Security, for looking stylish while not sacrificing protection." + icon_state = "beret_hos_black" + +/obj/item/clothing/head/warden + name = "warden's police hat" + desc = "It's a special armored hat issued to the Warden of a security force. Protects the head from impacts." + icon_state = "policehelm" + armor = list("melee" = 40, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 60) + strip_delay = 60 + dog_fashion = /datum/dog_fashion/head/warden + +/obj/item/clothing/head/officer + name = "officer's cap" + desc = "A red cap with an old-fashioned badge on the front for establishing that you are, in fact, the law." + icon_state = "customshelm" + item_state = "customshelm" + armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) + strip_delay = 60 + +/obj/item/clothing/head/beret/sec + name = "security beret" + desc = "A beret with the security insignia emblazoned on it. For officers that are more inclined towards style than safety." + icon_state = "beret_officer" + armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) + strip_delay = 60 + dog_fashion = null + +/obj/item/clothing/head/beret/sec/warden + name = "warden's beret" + desc = "A special beret with the Warden's insignia emblazoned on it. For wardens with class." + icon_state = "beret_warden" + armor = list("melee" = 40, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 50) + +/obj/item/clothing/head/beret/eng + name = "engineering beret" + desc = "A beret with the engineering insignia emblazoned on it. For engineers that are more inclined towards style than safety." + icon_state = "beret_engineering" + +/obj/item/clothing/head/beret/atmos + name = "atmospherics beret" + desc = "A beret for those who have shown immaculate proficienty in piping. Or plumbing." + icon_state = "beret_atmospherics" + +/obj/item/clothing/head/beret/ce + name = "chief engineer beret" + desc = "A white beret with the engineering insignia emblazoned on it. Its owner knows what they're doing. Probably." + icon_state = "beret_ce" + +/obj/item/clothing/head/beret/sci + name = "science beret" + desc = "A purple beret with the science insignia emblazoned on it. It has that authentic burning plasma smell." + icon_state = "beret_sci" + +//Medical +/obj/item/clothing/head/beret/med + name = "medical beret" + desc = "A white beret with a green cross finely threaded into it. It has that sterile smell about it." + icon_state = "beret_med" + +/obj/item/clothing/head/surgery + name = "surgical cap" + desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs." + icon_state = "surgcap_blue" + flags = BLOCKHEADHAIR + sprite_sheets = list( + "Drask" = 'icons/mob/species/drask/head.dmi' + ) + +/obj/item/clothing/head/surgery/purple + desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is deep purple." + icon_state = "surgcap_purple" + +/obj/item/clothing/head/surgery/blue + desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is baby blue." + icon_state = "surgcap_blue" + +/obj/item/clothing/head/surgery/green + desc = "A cap surgeons wear during operations. Keeps their hair from tickling your internal organs. This one is dark green." + icon_state = "surgcap_green" + +/obj/item/clothing/head/surgery/black + desc = "A cap coroners wear during autopsies. Keeps their hair from falling into the cadavers. It is as dark than the coroner's humor." + icon_state = "surgcap_black" diff --git a/code/modules/clothing/head/misc.dm b/code/modules/clothing/head/misc.dm index ef49855dbec..cac07fe5b1d 100644 --- a/code/modules/clothing/head/misc.dm +++ b/code/modules/clothing/head/misc.dm @@ -1,493 +1,493 @@ - - -/obj/item/clothing/head/centhat - name = "\improper CentComm. hat" - icon_state = "centcom" - desc = "It's good to be emperor." - item_state = "centhat" - armor = list("melee" = 30, "bullet" = 15, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - strip_delay = 80 - -/obj/item/clothing/head/hairflower - name = "hair flower pin" - icon_state = "hairflower" - desc = "Smells nice." - item_state = "hairflower" - -/obj/item/clothing/head/powdered_wig - name = "powdered wig" - desc = "A powdered wig." - icon_state = "pwig" - item_state = "pwig" - -/obj/item/clothing/head/justice_wig - name = "Justice wig" - desc = "A fancy powdered wig given to arbitrators of the law. It looks itchy." - icon_state = "jwig" - item_state = "jwig" - -/obj/item/clothing/head/beret/blue - icon_state = "beret_blue" - -/obj/item/clothing/head/beret/black - icon_state = "beret_black" - -/obj/item/clothing/head/beret/purple_normal - icon_state = "beret_purple_normal" - -/obj/item/clothing/head/that - name = "top-hat" - desc = "It's an amish looking hat." - icon_state = "tophat" - item_state = "that" - dog_fashion = /datum/dog_fashion/head - -/obj/item/clothing/head/redcoat - name = "redcoat's hat" - icon_state = "redcoat" - desc = "'I guess it's a redhead.'" - -/obj/item/clothing/head/mailman - name = "mailman's hat" - icon_state = "mailman" - desc = "'Right-on-time' mail service head wear." - -/obj/item/clothing/head/plaguedoctorhat - name = "plague doctor's hat" - desc = "These were once used by Plague doctors. They're pretty much useless." - icon_state = "plaguedoctor" - permeability_coefficient = 0.01 - -/obj/item/clothing/head/hasturhood - name = "hastur's hood" - desc = "It's unspeakably stylish" - icon_state = "hasturhood" - flags = BLOCKHAIR - flags_cover = HEADCOVERSEYES - -/obj/item/clothing/head/nursehat - name = "nurse's hat" - desc = "It allows quick identification of trained medical personnel." - icon_state = "nursehat" - dog_fashion = /datum/dog_fashion/head/nurse - -/obj/item/clothing/head/syndicatefake - name = "black and red space-helmet replica" - icon_state = "syndicate-helm-black-red" - item_state = "syndicate-helm-black-red" - desc = "A plastic replica of a syndicate agent's space helmet, you'll look just like a real murderous syndicate agent in this! This is a toy, it is not made for use in space!" - flags = BLOCKHAIR - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/helmet.dmi' - ) - - -/obj/item/clothing/head/cueball - name = "cueball helmet" - desc = "A large, featureless white orb meant to be worn on your head. How do you even see out of this thing?" - icon_state = "cueball" - item_state = "cueball" - flags = BLOCKHAIR - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - -/obj/item/clothing/head/snowman - name = "snowman head" - desc = "A ball of white styrofoam. So festive." - icon_state = "snowman_h" - item_state = "snowman_h" - flags = BLOCKHAIR - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - -/obj/item/clothing/head/that - name = "sturdy top-hat" - desc = "It's an amish looking armored top hat." - icon_state = "tophat" - item_state = "that" - flags_inv = 0 - - -/obj/item/clothing/head/greenbandana - name = "green bandana" - desc = "It's a green bandana with some fine nanotech lining." - icon_state = "greenbandana" - item_state = "greenbandana" - flags_inv = 0 - -/obj/item/clothing/head/justice - name = "justice hat" - desc = "Fight for what's righteous!" - icon_state = "justicered" - item_state = "justicered" - flags = BLOCKHAIR - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - -/obj/item/clothing/head/justice/blue - icon_state = "justiceblue" - item_state = "justiceblue" - -/obj/item/clothing/head/justice/yellow - icon_state = "justiceyellow" - item_state = "justiceyellow" - -/obj/item/clothing/head/justice/green - icon_state = "justicegreen" - item_state = "justicegreen" - -/obj/item/clothing/head/justice/pink - icon_state = "justicepink" - item_state = "justicepink" - -/obj/item/clothing/head/rabbitears - name = "rabbit ears" - desc = "Wearing these makes you look useless, and only good for your sex appeal." - icon_state = "bunny" - dog_fashion = /datum/dog_fashion/head/rabbit - -/obj/item/clothing/head/flatcap - name = "flat cap" - desc = "A working man's cap." - icon_state = "flat_cap" - item_state = "detective" - -/obj/item/clothing/head/pirate - name = "pirate hat" - desc = "Yarr." - icon_state = "pirate" - item_state = "pirate" - dog_fashion = /datum/dog_fashion/head/pirate - -/obj/item/clothing/head/hgpiratecap - name = "pirate hat" - desc = "Yarr." - icon_state = "hgpiratecap" - item_state = "hgpiratecap" - -/obj/item/clothing/head/bandana - name = "pirate bandana" - desc = "Yarr." - icon_state = "bandana" - item_state = "bandana" - -//stylish bs12 hats - -/obj/item/clothing/head/bowlerhat - name = "bowler hat" - icon_state = "bowler_hat" - item_state = "bowler_hat" - desc = "For that industrial age look." - -/obj/item/clothing/head/beaverhat - name = "beaver hat" - icon_state = "beaver_hat" - item_state = "beaver_hat" - desc = "Like a top hat, but made of beavers." - -/obj/item/clothing/head/boaterhat - name = "boater hat" - icon_state = "boater_hat" - item_state = "boater_hat" - desc = "Goes well with celery." - -/obj/item/clothing/head/cowboyhat - name = "cowboy hat" - icon_state = "cowboyhat" - item_state = "cowboyhat" - desc = "For the Rancher in us all." - -/obj/item/clothing/head/cowboyhat/tan - name = "tan cowboy hat" - icon_state = "cowboyhat_tan" - item_state = "cowboyhat_tan" - desc = "There's a new sheriff in town. Pass the whiskey." - -/obj/item/clothing/head/cowboyhat/black - name = "black cowboy hat" - icon_state = "cowboyhat_black" - item_state = "cowboyhat_black" - desc = "This station ain't big enough for the two ah' us." - -/obj/item/clothing/head/cowboyhat/white - name = "white cowboy hat" - icon_state = "cowboyhat_white" - item_state = "cowboyhat_white" - desc = "Authentic Marshall hair case. Now ya can protect this here homestead. Navy Model not included." - -/obj/item/clothing/head/cowboyhat/pink - name = "cowgirl hat" - icon_state = "cowboyhat_pink" - item_state = "cowboyhat_pink" - desc = "For those buckle bunnies wanta' become a real buckaroo." - -/obj/item/clothing/head/fedora - name = "fedora" - icon_state = "fedora" - item_state = "fedora" - desc = "A great hat ruined by being within fifty yards of you." - actions_types = list(/datum/action/item_action/tip_fedora) - -/obj/item/clothing/head/fedora/attack_self(mob/user) - tip_fedora(user) - -/obj/item/clothing/head/fedora/item_action_slot_check(slot) - if(slot == slot_head) - return 1 - -/obj/item/clothing/head/fedora/proc/tip_fedora(mob/user) - user.visible_message("[user] tips [user.p_their()] fedora.", "You tip your fedora") - - -/obj/item/clothing/head/fez - name = "fez" - icon_state = "fez" - item_state = "fez" - desc = "Put it on your monkey, make lots of cash money." - -//end bs12 hats - -/obj/item/clothing/head/witchwig - name = "witch costume wig" - desc = "Eeeee~heheheheheheh!" - icon_state = "witch" - item_state = "witch" - flags = BLOCKHAIR - -/obj/item/clothing/head/chicken - name = "chicken suit head" - desc = "Bkaw!" - icon_state = "chickenhead" - item_state = "chickensuit" - flags = BLOCKHAIR - - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - -/obj/item/clothing/head/corgi - name = "corgi suit head" - desc = "Woof!" - icon_state = "corgihead" - item_state = "chickensuit" - flags = BLOCKHAIR - -/obj/item/clothing/head/corgi/super_hero - name = "super-hero corgi suit head" - desc = "Woof! This one seems to pulse with a strange power" - flags = BLOCKHAIR | NODROP - -/obj/item/clothing/head/corgi/super_hero/en - name = "E-N suit head" - icon_state = "enhead" - -/obj/item/clothing/head/bearpelt - name = "bear pelt hat" - desc = "Fuzzy." - icon_state = "bearpelt" - item_state = "bearpelt" - flags = BLOCKHAIR - -/obj/item/clothing/head/xenos - name = "xenos helmet" - icon_state = "xenos" - item_state = "xenos_helm" - desc = "A helmet made out of chitinous alien hide." - flags = BLOCKHAIR - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - -/obj/item/clothing/head/fedora - name = "fedora" - desc = "Someone wearing this definitely makes them cool" - icon_state = "fedora" - -/obj/item/clothing/head/fedora/whitefedora - name = "white fedora" - icon_state = "wfedora" - -/obj/item/clothing/head/fedora/brownfedora - name = "brown fedora" - icon_state = "bfedora" - -/obj/item/clothing/head/stalhelm - name = "Clown Stalhelm" - desc = "The typical clown soldier's helmet." - icon_state = "stalhelm" - item_state = "stalhelm" - flags = BLOCKHAIR - flags_inv = HIDEEARS - -/obj/item/clothing/head/panzer - name = "Clown HONKMech Cap" - desc = "The softcap worn by HONK Mech pilots." - icon_state = "panzercap" - item_state = "panzercap" - flags = BLOCKHAIR - -/obj/item/clothing/head/naziofficer - name = "Clown Officer Cap" - desc = "The peaked clown officer's cap, disturbingly similar to the warden's." - icon_state = "officercap" - item_state = "officercap" - flags = BLOCKHAIR - flags_inv = HIDEEARS - -/obj/item/clothing/head/beret/purple - name = "Pierson Family Beret" - desc = " A purple beret, with a small golden crescent moon sewn onto it." - icon_state = "beret_purple" - item_state = "purpleberet" - -/obj/item/clothing/head/beret/centcom/officer - name = "officers beret" - desc = "A black beret adorned with the shield—a silver kite shield with an engraved sword—of the Nanotrasen security forces, announcing to the world that the wearer is a defender of Nanotrasen." - icon_state = "beret_centcom_officer" - armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) - strip_delay = 60 - -/obj/item/clothing/head/beret/centcom/officer/navy - name = "navy blue officers beret" - desc = "A navy blue beret adorned with the shield—a silver kite shield with an engraved sword—of the Nanotrasen security forces, announcing to the world that the wearer is a defender of Nanotrasen." - icon_state = "beret_centcom_officer_navy" - armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) - strip_delay = 60 - -/obj/item/clothing/head/beret/centcom/captain - name = "captains beret" - desc = "A white beret adorned with the shield—a cobalt kite shield with an engraved sword—of the Nanotrasen security forces, worn only by those captaining a vessel of the Nanotrasen Navy." - icon_state = "beret_centcom_captain" - -/obj/item/clothing/head/sombrero - name = "sombrero" - icon_state = "sombrero" - item_state = "sombrero" - desc = "You can practically taste the fiesta." - dog_fashion = /datum/dog_fashion/head/sombrero - -/obj/item/clothing/head/sombrero/green - name = "green sombrero" - icon_state = "greensombrero" - item_state = "greensombrero" - desc = "As elegant as a dancing cactus." - dog_fashion = null - -/obj/item/clothing/head/sombrero/shamebrero - name = "shamebrero" - icon_state = "shamebrero" - item_state = "shamebrero" - desc = "Once it's on, it never comes off." - flags = NODROP - dog_fashion = null - -/obj/item/clothing/head/cone - desc = "This cone is trying to warn you of something!" - name = "warning cone" - icon = 'icons/obj/janitor.dmi' - icon_state = "cone" - item_state = "cone" - force = 1.0 - throwforce = 3.0 - throw_speed = 2 - throw_range = 5 - w_class = WEIGHT_CLASS_SMALL - attack_verb = list("warned", "cautioned", "smashed") - resistance_flags = NONE - -/obj/item/clothing/head/jester - name = "jester hat" - desc = "A hat with bells, to add some merryness to the suit." - icon_state = "jester_hat" - -/obj/item/clothing/head/rice_hat - name = "rice hat" - desc = "Welcome to the rice fields, motherfucker." - icon_state = "rice_hat" - -/obj/item/clothing/head/griffin - name = "griffon head" - desc = "Why not 'eagle head'? Who knows." - icon_state = "griffinhat" - item_state = "griffinhat" - flags = BLOCKHAIR - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - actions_types = list(/datum/action/item_action/caw) - -/obj/item/clothing/head/griffin/attack_self() - caw() - -/obj/item/clothing/head/griffin/proc/caw() - if(cooldown < world.time - 20) // A cooldown, to stop people being jerks - playsound(src.loc, 'sound/creatures/caw.ogg', 50, 1) - cooldown = world.time - - -/obj/item/clothing/head/lordadmiralhat - name = "Lord Admiral's Hat" - desc = "A hat suitable for any man of high and exalted rank." - icon_state = "lordadmiralhat" - item_state = "lordadmiralhat" - -/obj/item/clothing/head/human_head - name = "bloated human head" - desc = "A horribly bloated and mismatched human head." - icon_state = "lingspacehelmet" - item_state = "lingspacehelmet" - -/obj/item/clothing/head/papersack - name = "paper sack hat" - desc = "A paper sack with crude holes cut out for eyes. Useful for hiding one's identity or ugliness." - icon_state = "papersack" - flags = BLOCKHAIR - flags_inv = HIDEFACE|HIDEEARS - - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - -/obj/item/clothing/head/papersack/smiley - name = "paper sack hat" - desc = "A paper sack with crude holes cut out for eyes and a sketchy smile drawn on the front. Not creepy at all." - icon_state = "papersack_smile" - flags = BLOCKHAIR - flags_inv = HIDEFACE|HIDEEARS - - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - -/obj/item/clothing/head/crown - name = "crown" - desc = "A crown fit for a king, a petty king maybe." - icon_state = "crown" - armor = list("melee" = 15, "bullet" = 0, "laser" = 0,"energy" = 15, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50) - resistance_flags = FIRE_PROOF - -/obj/item/clothing/head/crown/fancy - name = "magnificent crown" - desc = "A crown worn by only the highest emperors of the land." - icon_state = "fancycrown" - -/obj/item/clothing/head/zepelli - name = "chequered diamond hat" - desc = "Wearing this makes you feel like a real mozzarella cheeseball. " - icon_state = "zepelli" - item_state = "zepelli" - -/obj/item/clothing/head/cuban_hat - name = "rhumba hat" - desc = "Now just to find some maracas!" - icon_state = "cuban_hat" - item_state = "cuban_hat" \ No newline at end of file + + +/obj/item/clothing/head/centhat + name = "\improper CentComm. hat" + icon_state = "centcom" + desc = "It's good to be emperor." + item_state = "centhat" + armor = list("melee" = 30, "bullet" = 15, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + strip_delay = 80 + +/obj/item/clothing/head/hairflower + name = "hair flower pin" + icon_state = "hairflower" + desc = "Smells nice." + item_state = "hairflower" + +/obj/item/clothing/head/powdered_wig + name = "powdered wig" + desc = "A powdered wig." + icon_state = "pwig" + item_state = "pwig" + +/obj/item/clothing/head/justice_wig + name = "Justice wig" + desc = "A fancy powdered wig given to arbitrators of the law. It looks itchy." + icon_state = "jwig" + item_state = "jwig" + +/obj/item/clothing/head/beret/blue + icon_state = "beret_blue" + +/obj/item/clothing/head/beret/black + icon_state = "beret_black" + +/obj/item/clothing/head/beret/purple_normal + icon_state = "beret_purple_normal" + +/obj/item/clothing/head/that + name = "top-hat" + desc = "It's an amish looking hat." + icon_state = "tophat" + item_state = "that" + dog_fashion = /datum/dog_fashion/head + +/obj/item/clothing/head/redcoat + name = "redcoat's hat" + icon_state = "redcoat" + desc = "'I guess it's a redhead.'" + +/obj/item/clothing/head/mailman + name = "mailman's hat" + icon_state = "mailman" + desc = "'Right-on-time' mail service head wear." + +/obj/item/clothing/head/plaguedoctorhat + name = "plague doctor's hat" + desc = "These were once used by Plague doctors. They're pretty much useless." + icon_state = "plaguedoctor" + permeability_coefficient = 0.01 + +/obj/item/clothing/head/hasturhood + name = "hastur's hood" + desc = "It's unspeakably stylish" + icon_state = "hasturhood" + flags = BLOCKHAIR + flags_cover = HEADCOVERSEYES + +/obj/item/clothing/head/nursehat + name = "nurse's hat" + desc = "It allows quick identification of trained medical personnel." + icon_state = "nursehat" + dog_fashion = /datum/dog_fashion/head/nurse + +/obj/item/clothing/head/syndicatefake + name = "black and red space-helmet replica" + icon_state = "syndicate-helm-black-red" + item_state = "syndicate-helm-black-red" + desc = "A plastic replica of a syndicate agent's space helmet, you'll look just like a real murderous syndicate agent in this! This is a toy, it is not made for use in space!" + flags = BLOCKHAIR + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/helmet.dmi' + ) + + +/obj/item/clothing/head/cueball + name = "cueball helmet" + desc = "A large, featureless white orb meant to be worn on your head. How do you even see out of this thing?" + icon_state = "cueball" + item_state = "cueball" + flags = BLOCKHAIR + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + +/obj/item/clothing/head/snowman + name = "snowman head" + desc = "A ball of white styrofoam. So festive." + icon_state = "snowman_h" + item_state = "snowman_h" + flags = BLOCKHAIR + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + +/obj/item/clothing/head/that + name = "sturdy top-hat" + desc = "It's an amish looking armored top hat." + icon_state = "tophat" + item_state = "that" + flags_inv = 0 + + +/obj/item/clothing/head/greenbandana + name = "green bandana" + desc = "It's a green bandana with some fine nanotech lining." + icon_state = "greenbandana" + item_state = "greenbandana" + flags_inv = 0 + +/obj/item/clothing/head/justice + name = "justice hat" + desc = "Fight for what's righteous!" + icon_state = "justicered" + item_state = "justicered" + flags = BLOCKHAIR + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + +/obj/item/clothing/head/justice/blue + icon_state = "justiceblue" + item_state = "justiceblue" + +/obj/item/clothing/head/justice/yellow + icon_state = "justiceyellow" + item_state = "justiceyellow" + +/obj/item/clothing/head/justice/green + icon_state = "justicegreen" + item_state = "justicegreen" + +/obj/item/clothing/head/justice/pink + icon_state = "justicepink" + item_state = "justicepink" + +/obj/item/clothing/head/rabbitears + name = "rabbit ears" + desc = "Wearing these makes you look useless, and only good for your sex appeal." + icon_state = "bunny" + dog_fashion = /datum/dog_fashion/head/rabbit + +/obj/item/clothing/head/flatcap + name = "flat cap" + desc = "A working man's cap." + icon_state = "flat_cap" + item_state = "detective" + +/obj/item/clothing/head/pirate + name = "pirate hat" + desc = "Yarr." + icon_state = "pirate" + item_state = "pirate" + dog_fashion = /datum/dog_fashion/head/pirate + +/obj/item/clothing/head/hgpiratecap + name = "pirate hat" + desc = "Yarr." + icon_state = "hgpiratecap" + item_state = "hgpiratecap" + +/obj/item/clothing/head/bandana + name = "pirate bandana" + desc = "Yarr." + icon_state = "bandana" + item_state = "bandana" + +//stylish bs12 hats + +/obj/item/clothing/head/bowlerhat + name = "bowler hat" + icon_state = "bowler_hat" + item_state = "bowler_hat" + desc = "For that industrial age look." + +/obj/item/clothing/head/beaverhat + name = "beaver hat" + icon_state = "beaver_hat" + item_state = "beaver_hat" + desc = "Like a top hat, but made of beavers." + +/obj/item/clothing/head/boaterhat + name = "boater hat" + icon_state = "boater_hat" + item_state = "boater_hat" + desc = "Goes well with celery." + +/obj/item/clothing/head/cowboyhat + name = "cowboy hat" + icon_state = "cowboyhat" + item_state = "cowboyhat" + desc = "For the Rancher in us all." + +/obj/item/clothing/head/cowboyhat/tan + name = "tan cowboy hat" + icon_state = "cowboyhat_tan" + item_state = "cowboyhat_tan" + desc = "There's a new sheriff in town. Pass the whiskey." + +/obj/item/clothing/head/cowboyhat/black + name = "black cowboy hat" + icon_state = "cowboyhat_black" + item_state = "cowboyhat_black" + desc = "This station ain't big enough for the two ah' us." + +/obj/item/clothing/head/cowboyhat/white + name = "white cowboy hat" + icon_state = "cowboyhat_white" + item_state = "cowboyhat_white" + desc = "Authentic Marshall hair case. Now ya can protect this here homestead. Navy Model not included." + +/obj/item/clothing/head/cowboyhat/pink + name = "cowgirl hat" + icon_state = "cowboyhat_pink" + item_state = "cowboyhat_pink" + desc = "For those buckle bunnies wanta' become a real buckaroo." + +/obj/item/clothing/head/fedora + name = "fedora" + icon_state = "fedora" + item_state = "fedora" + desc = "A great hat ruined by being within fifty yards of you." + actions_types = list(/datum/action/item_action/tip_fedora) + +/obj/item/clothing/head/fedora/attack_self(mob/user) + tip_fedora(user) + +/obj/item/clothing/head/fedora/item_action_slot_check(slot) + if(slot == slot_head) + return 1 + +/obj/item/clothing/head/fedora/proc/tip_fedora(mob/user) + user.visible_message("[user] tips [user.p_their()] fedora.", "You tip your fedora") + + +/obj/item/clothing/head/fez + name = "fez" + icon_state = "fez" + item_state = "fez" + desc = "Put it on your monkey, make lots of cash money." + +//end bs12 hats + +/obj/item/clothing/head/witchwig + name = "witch costume wig" + desc = "Eeeee~heheheheheheh!" + icon_state = "witch" + item_state = "witch" + flags = BLOCKHAIR + +/obj/item/clothing/head/chicken + name = "chicken suit head" + desc = "Bkaw!" + icon_state = "chickenhead" + item_state = "chickensuit" + flags = BLOCKHAIR + + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + +/obj/item/clothing/head/corgi + name = "corgi suit head" + desc = "Woof!" + icon_state = "corgihead" + item_state = "chickensuit" + flags = BLOCKHAIR + +/obj/item/clothing/head/corgi/super_hero + name = "super-hero corgi suit head" + desc = "Woof! This one seems to pulse with a strange power" + flags = BLOCKHAIR | NODROP + +/obj/item/clothing/head/corgi/super_hero/en + name = "E-N suit head" + icon_state = "enhead" + +/obj/item/clothing/head/bearpelt + name = "bear pelt hat" + desc = "Fuzzy." + icon_state = "bearpelt" + item_state = "bearpelt" + flags = BLOCKHAIR + +/obj/item/clothing/head/xenos + name = "xenos helmet" + icon_state = "xenos" + item_state = "xenos_helm" + desc = "A helmet made out of chitinous alien hide." + flags = BLOCKHAIR + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + +/obj/item/clothing/head/fedora + name = "fedora" + desc = "Someone wearing this definitely makes them cool" + icon_state = "fedora" + +/obj/item/clothing/head/fedora/whitefedora + name = "white fedora" + icon_state = "wfedora" + +/obj/item/clothing/head/fedora/brownfedora + name = "brown fedora" + icon_state = "bfedora" + +/obj/item/clothing/head/stalhelm + name = "Clown Stalhelm" + desc = "The typical clown soldier's helmet." + icon_state = "stalhelm" + item_state = "stalhelm" + flags = BLOCKHAIR + flags_inv = HIDEEARS + +/obj/item/clothing/head/panzer + name = "Clown HONKMech Cap" + desc = "The softcap worn by HONK Mech pilots." + icon_state = "panzercap" + item_state = "panzercap" + flags = BLOCKHAIR + +/obj/item/clothing/head/naziofficer + name = "Clown Officer Cap" + desc = "The peaked clown officer's cap, disturbingly similar to the warden's." + icon_state = "officercap" + item_state = "officercap" + flags = BLOCKHAIR + flags_inv = HIDEEARS + +/obj/item/clothing/head/beret/purple + name = "Pierson Family Beret" + desc = " A purple beret, with a small golden crescent moon sewn onto it." + icon_state = "beret_purple" + item_state = "purpleberet" + +/obj/item/clothing/head/beret/centcom/officer + name = "officers beret" + desc = "A black beret adorned with the shield—a silver kite shield with an engraved sword—of the Nanotrasen security forces, announcing to the world that the wearer is a defender of Nanotrasen." + icon_state = "beret_centcom_officer" + armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) + strip_delay = 60 + +/obj/item/clothing/head/beret/centcom/officer/navy + name = "navy blue officers beret" + desc = "A navy blue beret adorned with the shield—a silver kite shield with an engraved sword—of the Nanotrasen security forces, announcing to the world that the wearer is a defender of Nanotrasen." + icon_state = "beret_centcom_officer_navy" + armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) + strip_delay = 60 + +/obj/item/clothing/head/beret/centcom/captain + name = "captains beret" + desc = "A white beret adorned with the shield—a cobalt kite shield with an engraved sword—of the Nanotrasen security forces, worn only by those captaining a vessel of the Nanotrasen Navy." + icon_state = "beret_centcom_captain" + +/obj/item/clothing/head/sombrero + name = "sombrero" + icon_state = "sombrero" + item_state = "sombrero" + desc = "You can practically taste the fiesta." + dog_fashion = /datum/dog_fashion/head/sombrero + +/obj/item/clothing/head/sombrero/green + name = "green sombrero" + icon_state = "greensombrero" + item_state = "greensombrero" + desc = "As elegant as a dancing cactus." + dog_fashion = null + +/obj/item/clothing/head/sombrero/shamebrero + name = "shamebrero" + icon_state = "shamebrero" + item_state = "shamebrero" + desc = "Once it's on, it never comes off." + flags = NODROP + dog_fashion = null + +/obj/item/clothing/head/cone + desc = "This cone is trying to warn you of something!" + name = "warning cone" + icon = 'icons/obj/janitor.dmi' + icon_state = "cone" + item_state = "cone" + force = 1.0 + throwforce = 3.0 + throw_speed = 2 + throw_range = 5 + w_class = WEIGHT_CLASS_SMALL + attack_verb = list("warned", "cautioned", "smashed") + resistance_flags = NONE + +/obj/item/clothing/head/jester + name = "jester hat" + desc = "A hat with bells, to add some merryness to the suit." + icon_state = "jester_hat" + +/obj/item/clothing/head/rice_hat + name = "rice hat" + desc = "Welcome to the rice fields, motherfucker." + icon_state = "rice_hat" + +/obj/item/clothing/head/griffin + name = "griffon head" + desc = "Why not 'eagle head'? Who knows." + icon_state = "griffinhat" + item_state = "griffinhat" + flags = BLOCKHAIR + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + actions_types = list(/datum/action/item_action/caw) + +/obj/item/clothing/head/griffin/attack_self() + caw() + +/obj/item/clothing/head/griffin/proc/caw() + if(cooldown < world.time - 20) // A cooldown, to stop people being jerks + playsound(src.loc, 'sound/creatures/caw.ogg', 50, 1) + cooldown = world.time + + +/obj/item/clothing/head/lordadmiralhat + name = "Lord Admiral's Hat" + desc = "A hat suitable for any man of high and exalted rank." + icon_state = "lordadmiralhat" + item_state = "lordadmiralhat" + +/obj/item/clothing/head/human_head + name = "bloated human head" + desc = "A horribly bloated and mismatched human head." + icon_state = "lingspacehelmet" + item_state = "lingspacehelmet" + +/obj/item/clothing/head/papersack + name = "paper sack hat" + desc = "A paper sack with crude holes cut out for eyes. Useful for hiding one's identity or ugliness." + icon_state = "papersack" + flags = BLOCKHAIR + flags_inv = HIDEFACE|HIDEEARS + + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + +/obj/item/clothing/head/papersack/smiley + name = "paper sack hat" + desc = "A paper sack with crude holes cut out for eyes and a sketchy smile drawn on the front. Not creepy at all." + icon_state = "papersack_smile" + flags = BLOCKHAIR + flags_inv = HIDEFACE|HIDEEARS + + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + +/obj/item/clothing/head/crown + name = "crown" + desc = "A crown fit for a king, a petty king maybe." + icon_state = "crown" + armor = list("melee" = 15, "bullet" = 0, "laser" = 0,"energy" = 15, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 50) + resistance_flags = FIRE_PROOF + +/obj/item/clothing/head/crown/fancy + name = "magnificent crown" + desc = "A crown worn by only the highest emperors of the land." + icon_state = "fancycrown" + +/obj/item/clothing/head/zepelli + name = "chequered diamond hat" + desc = "Wearing this makes you feel like a real mozzarella cheeseball. " + icon_state = "zepelli" + item_state = "zepelli" + +/obj/item/clothing/head/cuban_hat + name = "rhumba hat" + desc = "Now just to find some maracas!" + icon_state = "cuban_hat" + item_state = "cuban_hat" diff --git a/code/modules/clothing/head/misc_special.dm b/code/modules/clothing/head/misc_special.dm index d939dadffba..5f8ae17d061 100644 --- a/code/modules/clothing/head/misc_special.dm +++ b/code/modules/clothing/head/misc_special.dm @@ -1,273 +1,273 @@ -/* - * Contents: - * Welding mask - * Cakehat - * Ushanka - * Pumpkin head - * Kitty ears - * Cardborg Disguise - * Head Mirror - */ - -/* - * Welding mask - */ -/obj/item/clothing/head/welding - name = "welding helmet" - desc = "A head-mounted face cover designed to protect the wearer completely from space-arc eye." - icon_state = "welding" - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - item_state = "welding" - materials = list(MAT_METAL=1750, MAT_GLASS=400) - flash_protect = 2 - tint = 2 - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 60) - flags_inv = (HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) - actions_types = list(/datum/action/item_action/toggle) - visor_flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - resistance_flags = FIRE_PROOF - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/head.dmi', - "Unathi" = 'icons/mob/species/unathi/helmet.dmi', - "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', - "Grey" = 'icons/mob/species/grey/helmet.dmi' - ) - -/obj/item/clothing/head/welding/attack_self(mob/user) - weldingvisortoggle(user) - -/obj/item/clothing/head/welding/flamedecal - name = "flame decal welding helmet" - desc = "A welding helmet adorned with flame decals, and several cryptic slogans of varying degrees of legibility." - icon_state = "welding_redflame" - -/obj/item/clothing/head/welding/flamedecal/blue - name = "blue flame decal welding helmet" - desc = "A welding helmet with blue flame decals on it." - icon_state = "welding_blueflame" - -/obj/item/clothing/head/welding/white - name = "white decal welding helmet" - desc = "A white welding helmet with a character written across it." - icon_state = "welding_white" - -/obj/item/clothing/head/welding/attack_self() - toggle() - -/obj/item/clothing/head/welding/proc/toggle() - if(up) - up = !up - flags_cover |= (HEADCOVERSEYES | HEADCOVERSMOUTH) - flags_inv |= (HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) - icon_state = initial(icon_state) - to_chat(usr, "You flip the [src] down to protect your eyes.") - flash_protect = 2 - tint = 2 - else - up = !up - flags_cover &= ~(HEADCOVERSEYES | HEADCOVERSMOUTH) - flags_inv &= ~(HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) - icon_state = "[initial(icon_state)]up" - to_chat(usr, "You push the [src] up out of your face.") - flash_protect = 0 - tint = 0 - var/mob/living/carbon/user = usr - user.update_tint() - user.update_inv_head() //so our mob-overlays update - - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - - - -/* - * Cakehat - */ -/obj/item/clothing/head/cakehat - name = "cake-hat" - desc = "It's tasty looking!" - icon_state = "cake0" - flags_cover = HEADCOVERSEYES - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - var/onfire = 0.0 - var/status = 0 - var/fire_resist = T0C+1300 //this is the max temp it can stand before you start to cook. although it might not burn away, you take damage - var/processing = 0 //I dont think this is used anywhere. - -/obj/item/clothing/head/cakehat/process() - if(!onfire) - STOP_PROCESSING(SSobj, src) - return - - var/turf/location = src.loc - if(istype(location, /mob/)) - var/mob/living/carbon/human/M = location - if(M.l_hand == src || M.r_hand == src || M.head == src) - location = M.loc - - if(istype(location, /turf)) - location.hotspot_expose(700, 1) - -/obj/item/clothing/head/cakehat/attack_self(mob/user as mob) - if(status > 1) return - src.onfire = !( src.onfire ) - if(src.onfire) - src.force = 3 - src.damtype = "fire" - src.icon_state = "cake1" - START_PROCESSING(SSobj, src) - else - src.force = null - src.damtype = "brute" - src.icon_state = "cake0" - return - - -/* - * Ushanka - */ -/obj/item/clothing/head/ushanka - name = "ushanka" - desc = "Perfect for winter in Siberia, da?" - icon_state = "ushankadown" - item_state = "ushankadown" - flags_inv = HIDEEARS - cold_protection = HEAD - min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT - dog_fashion = /datum/dog_fashion/head/ushanka - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - -/obj/item/clothing/head/ushanka/attack_self(mob/user as mob) - if(src.icon_state == "ushankadown") - src.icon_state = "ushankaup" - src.item_state = "ushankaup" - to_chat(user, "You raise the ear flaps on the ushanka.") - else - src.icon_state = "ushankadown" - src.item_state = "ushankadown" - to_chat(user, "You lower the ear flaps on the ushanka.") - -/* - * Pumpkin head - */ -/obj/item/clothing/head/hardhat/pumpkinhead - name = "carved pumpkin" - desc = "A jack o' lantern! Believed to ward off evil spirits." - icon_state = "hardhat0_pumpkin"//Could stand to be renamed - item_state = "hardhat0_pumpkin" - item_color = "pumpkin" - flags = BLOCKHAIR - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - - - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - brightness_on = 2 //luminosity when on - - -/obj/item/clothing/head/hardhat/reindeer - name = "novelty reindeer hat" - desc = "Some fake antlers and a very fake red nose." - icon_state = "hardhat0_reindeer" - item_state = "hardhat0_reindeer" - item_color = "reindeer" - flags_inv = 0 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - brightness_on = 1 //luminosity when on - dog_fashion = /datum/dog_fashion/head/reindeer - - -/* - * Kitty ears - */ -/obj/item/clothing/head/kitty - name = "kitty ears" - desc = "A pair of kitty ears. Meow!" - icon_state = "kitty" - var/icon/mob - dog_fashion = /datum/dog_fashion/head/kitty - -/obj/item/clothing/head/kitty/update_icon(var/mob/living/carbon/human/user) - if(!istype(user)) return - var/obj/item/organ/external/head/head_organ = user.get_organ("head") - - mob = new/icon("icon" = 'icons/mob/head.dmi', "icon_state" = "kitty") - mob.Blend(head_organ.hair_colour, ICON_ADD) - - var/icon/earbit = new/icon("icon" = 'icons/mob/head.dmi', "icon_state" = "kittyinner") - mob.Blend(earbit, ICON_OVERLAY) - - icon_override = mob - -/obj/item/clothing/head/kitty/equipped(var/mob/M, slot) - . = ..() - if(ishuman(M) && slot == slot_head) - update_icon(M) - - -/obj/item/clothing/head/kitty/mouse - name = "mouse ears" - desc = "A pair of mouse ears. Squeak!" - icon_state = "mousey" - -/obj/item/clothing/head/kitty/mouse/update_icon(var/mob/living/carbon/human/user) - if(!istype(user)) return - var/obj/item/organ/external/head/head_organ = user.get_organ("head") - mob = new/icon("icon" = 'icons/mob/head.dmi', "icon_state" = "mousey") - mob.Blend(head_organ.hair_colour, ICON_ADD) - - var/icon/earbit = new/icon("icon" = 'icons/mob/head.dmi', "icon_state" = "mouseyinner") - mob.Blend(earbit, ICON_OVERLAY) - - icon_override = mob - -/obj/item/clothing/head/cardborg - name = "cardborg helmet" - desc = "A helmet made out of a box." - icon_state = "cardborg_h" - item_state = "cardborg_h" - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - species_disguise = "High-tech robot" - dog_fashion = /datum/dog_fashion/head/cardborg - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - - -/obj/item/clothing/head/cardborg/equipped(mob/living/user, slot) - ..() - if(ishuman(user) && slot == slot_head) - var/mob/living/carbon/human/H = user - if(istype(H.wear_suit, /obj/item/clothing/suit/cardborg)) - var/obj/item/clothing/suit/cardborg/CB = H.wear_suit - CB.disguise(user, src) - -/obj/item/clothing/head/cardborg/dropped(mob/living/user) - ..() - user.remove_alt_appearance("standard_borg_disguise") - -/* - * Head Mirror - */ -/obj/item/clothing/head/headmirror - name = "head mirror" - desc = "A band of rubber with a very reflective looking mirror attached to the front of it. One of the early signs of medical budget cuts." - icon_state = "head_mirror" - item_state = "head_mirror" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/head.dmi', - "Drask" = 'icons/mob/species/drask/head.dmi', - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - +/* + * Contents: + * Welding mask + * Cakehat + * Ushanka + * Pumpkin head + * Kitty ears + * Cardborg Disguise + * Head Mirror + */ + +/* + * Welding mask + */ +/obj/item/clothing/head/welding + name = "welding helmet" + desc = "A head-mounted face cover designed to protect the wearer completely from space-arc eye." + icon_state = "welding" + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + item_state = "welding" + materials = list(MAT_METAL=1750, MAT_GLASS=400) + flash_protect = 2 + tint = 2 + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 60) + flags_inv = (HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) + actions_types = list(/datum/action/item_action/toggle) + visor_flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + resistance_flags = FIRE_PROOF + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/head.dmi', + "Unathi" = 'icons/mob/species/unathi/helmet.dmi', + "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', + "Grey" = 'icons/mob/species/grey/helmet.dmi' + ) + +/obj/item/clothing/head/welding/attack_self(mob/user) + weldingvisortoggle(user) + +/obj/item/clothing/head/welding/flamedecal + name = "flame decal welding helmet" + desc = "A welding helmet adorned with flame decals, and several cryptic slogans of varying degrees of legibility." + icon_state = "welding_redflame" + +/obj/item/clothing/head/welding/flamedecal/blue + name = "blue flame decal welding helmet" + desc = "A welding helmet with blue flame decals on it." + icon_state = "welding_blueflame" + +/obj/item/clothing/head/welding/white + name = "white decal welding helmet" + desc = "A white welding helmet with a character written across it." + icon_state = "welding_white" + +/obj/item/clothing/head/welding/attack_self() + toggle() + +/obj/item/clothing/head/welding/proc/toggle() + if(up) + up = !up + flags_cover |= (HEADCOVERSEYES | HEADCOVERSMOUTH) + flags_inv |= (HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) + icon_state = initial(icon_state) + to_chat(usr, "You flip the [src] down to protect your eyes.") + flash_protect = 2 + tint = 2 + else + up = !up + flags_cover &= ~(HEADCOVERSEYES | HEADCOVERSMOUTH) + flags_inv &= ~(HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) + icon_state = "[initial(icon_state)]up" + to_chat(usr, "You push the [src] up out of your face.") + flash_protect = 0 + tint = 0 + var/mob/living/carbon/user = usr + user.update_tint() + user.update_inv_head() //so our mob-overlays update + + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + + + +/* + * Cakehat + */ +/obj/item/clothing/head/cakehat + name = "cake-hat" + desc = "It's tasty looking!" + icon_state = "cake0" + flags_cover = HEADCOVERSEYES + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + var/onfire = 0.0 + var/status = 0 + var/fire_resist = T0C+1300 //this is the max temp it can stand before you start to cook. although it might not burn away, you take damage + var/processing = 0 //I dont think this is used anywhere. + +/obj/item/clothing/head/cakehat/process() + if(!onfire) + STOP_PROCESSING(SSobj, src) + return + + var/turf/location = src.loc + if(istype(location, /mob/)) + var/mob/living/carbon/human/M = location + if(M.l_hand == src || M.r_hand == src || M.head == src) + location = M.loc + + if(istype(location, /turf)) + location.hotspot_expose(700, 1) + +/obj/item/clothing/head/cakehat/attack_self(mob/user as mob) + if(status > 1) return + src.onfire = !( src.onfire ) + if(src.onfire) + src.force = 3 + src.damtype = "fire" + src.icon_state = "cake1" + START_PROCESSING(SSobj, src) + else + src.force = null + src.damtype = "brute" + src.icon_state = "cake0" + return + + +/* + * Ushanka + */ +/obj/item/clothing/head/ushanka + name = "ushanka" + desc = "Perfect for winter in Siberia, da?" + icon_state = "ushankadown" + item_state = "ushankadown" + flags_inv = HIDEEARS + cold_protection = HEAD + min_cold_protection_temperature = FIRE_HELM_MIN_TEMP_PROTECT + dog_fashion = /datum/dog_fashion/head/ushanka + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + +/obj/item/clothing/head/ushanka/attack_self(mob/user as mob) + if(src.icon_state == "ushankadown") + src.icon_state = "ushankaup" + src.item_state = "ushankaup" + to_chat(user, "You raise the ear flaps on the ushanka.") + else + src.icon_state = "ushankadown" + src.item_state = "ushankadown" + to_chat(user, "You lower the ear flaps on the ushanka.") + +/* + * Pumpkin head + */ +/obj/item/clothing/head/hardhat/pumpkinhead + name = "carved pumpkin" + desc = "A jack o' lantern! Believed to ward off evil spirits." + icon_state = "hardhat0_pumpkin"//Could stand to be renamed + item_state = "hardhat0_pumpkin" + item_color = "pumpkin" + flags = BLOCKHAIR + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + + + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + brightness_on = 2 //luminosity when on + + +/obj/item/clothing/head/hardhat/reindeer + name = "novelty reindeer hat" + desc = "Some fake antlers and a very fake red nose." + icon_state = "hardhat0_reindeer" + item_state = "hardhat0_reindeer" + item_color = "reindeer" + flags_inv = 0 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + brightness_on = 1 //luminosity when on + dog_fashion = /datum/dog_fashion/head/reindeer + + +/* + * Kitty ears + */ +/obj/item/clothing/head/kitty + name = "kitty ears" + desc = "A pair of kitty ears. Meow!" + icon_state = "kitty" + var/icon/mob + dog_fashion = /datum/dog_fashion/head/kitty + +/obj/item/clothing/head/kitty/update_icon(var/mob/living/carbon/human/user) + if(!istype(user)) return + var/obj/item/organ/external/head/head_organ = user.get_organ("head") + + mob = new/icon("icon" = 'icons/mob/head.dmi', "icon_state" = "kitty") + mob.Blend(head_organ.hair_colour, ICON_ADD) + + var/icon/earbit = new/icon("icon" = 'icons/mob/head.dmi', "icon_state" = "kittyinner") + mob.Blend(earbit, ICON_OVERLAY) + + icon_override = mob + +/obj/item/clothing/head/kitty/equipped(var/mob/M, slot) + . = ..() + if(ishuman(M) && slot == slot_head) + update_icon(M) + + +/obj/item/clothing/head/kitty/mouse + name = "mouse ears" + desc = "A pair of mouse ears. Squeak!" + icon_state = "mousey" + +/obj/item/clothing/head/kitty/mouse/update_icon(var/mob/living/carbon/human/user) + if(!istype(user)) return + var/obj/item/organ/external/head/head_organ = user.get_organ("head") + mob = new/icon("icon" = 'icons/mob/head.dmi', "icon_state" = "mousey") + mob.Blend(head_organ.hair_colour, ICON_ADD) + + var/icon/earbit = new/icon("icon" = 'icons/mob/head.dmi', "icon_state" = "mouseyinner") + mob.Blend(earbit, ICON_OVERLAY) + + icon_override = mob + +/obj/item/clothing/head/cardborg + name = "cardborg helmet" + desc = "A helmet made out of a box." + icon_state = "cardborg_h" + item_state = "cardborg_h" + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + species_disguise = "High-tech robot" + dog_fashion = /datum/dog_fashion/head/cardborg + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + + +/obj/item/clothing/head/cardborg/equipped(mob/living/user, slot) + ..() + if(ishuman(user) && slot == slot_head) + var/mob/living/carbon/human/H = user + if(istype(H.wear_suit, /obj/item/clothing/suit/cardborg)) + var/obj/item/clothing/suit/cardborg/CB = H.wear_suit + CB.disguise(user, src) + +/obj/item/clothing/head/cardborg/dropped(mob/living/user) + ..() + user.remove_alt_appearance("standard_borg_disguise") + +/* + * Head Mirror + */ +/obj/item/clothing/head/headmirror + name = "head mirror" + desc = "A band of rubber with a very reflective looking mirror attached to the front of it. One of the early signs of medical budget cuts." + icon_state = "head_mirror" + item_state = "head_mirror" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/head.dmi', + "Drask" = 'icons/mob/species/drask/head.dmi', + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + diff --git a/code/modules/clothing/head/soft_caps.dm b/code/modules/clothing/head/soft_caps.dm index b111672e86a..0255109620a 100644 --- a/code/modules/clothing/head/soft_caps.dm +++ b/code/modules/clothing/head/soft_caps.dm @@ -1,132 +1,132 @@ -/obj/item/clothing/head/soft - name = "cargo cap" - desc = "It's a baseball hat in a tasteless yellow colour." - icon_state = "cargosoft" - item_state = "helmet" - item_color = "cargo" - var/flipped = 0 - actions_types = list(/datum/action/item_action/flip_cap) - dog_fashion = /datum/dog_fashion/head/cargo_tech - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/head.dmi' - ) - -/obj/item/clothing/head/soft/dropped() - icon_state = "[item_color]soft" - flipped = 0 - ..() - -/obj/item/clothing/head/soft/attack_self(mob/user) - flip(user) - -/obj/item/clothing/head/soft/proc/flip(mob/user) - flipped = !flipped - if(flipped) - icon_state = "[item_color]soft_flipped" - to_chat(usr, "You flip the hat backwards.") - else - icon_state = "[item_color]soft" - to_chat(user, "You flip the hat back in normal position.") - user.update_inv_head() //so our mob-overlays update - - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - -/obj/item/clothing/head/soft/red - name = "red cap" - desc = "It's a baseball hat in a tasteless red colour." - icon_state = "redsoft" - item_color = "red" - dog_fashion = null - -/obj/item/clothing/head/soft/blue - name = "blue cap" - desc = "It's a baseball hat in a tasteless blue colour." - icon_state = "bluesoft" - item_color = "blue" - dog_fashion = null - -/obj/item/clothing/head/soft/green - name = "green cap" - desc = "It's a baseball hat in a tasteless green colour." - icon_state = "greensoft" - item_color = "green" - dog_fashion = null - -/obj/item/clothing/head/soft/yellow - name = "yellow cap" - desc = "It's a baseball hat in a tasteless yellow colour." - icon_state = "yellowsoft" - item_color = "yellow" - dog_fashion = null - -/obj/item/clothing/head/soft/grey - name = "grey cap" - desc = "It's a baseball hat in a tasteful grey colour." - icon_state = "greysoft" - item_color = "grey" - dog_fashion = null - -/obj/item/clothing/head/soft/orange - name = "orange cap" - desc = "It's a baseball hat in a tasteless orange colour." - icon_state = "orangesoft" - item_color = "orange" - -/obj/item/clothing/head/soft/mime - name = "white cap" - desc = "It's a baseball hat in a tasteless white colour." - icon_state = "mimesoft" - item_color = "mime" - dog_fashion = null - -/obj/item/clothing/head/soft/purple - name = "purple cap" - desc = "It's a baseball hat in a tasteless purple colour." - icon_state = "purplesoft" - item_color = "purple" - dog_fashion = null - -/obj/item/clothing/head/soft/black - name = "black cap" - desc = "It's a baseball hat in a tasteless black colour." - icon_state = "blacksoft" - item_color = "black" - dog_fashion = null - -/obj/item/clothing/head/soft/rainbow - name = "rainbow cap" - desc = "It's a baseball hat in a bright rainbow of colors." - icon_state = "rainbowsoft" - item_color = "rainbow" - dog_fashion = null - -/obj/item/clothing/head/soft/sec - name = "security cap" - desc = "It's baseball hat in tasteful red colour." - icon_state = "secsoft" - item_color = "sec" - armor = list("melee" = 30, "bullet" = 25, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) - strip_delay = 60 - dog_fashion = null - -/obj/item/clothing/head/soft/sec/corp - name = "corporate security cap" - desc = "It's baseball hat in corpotate colours." - icon_state = "corpsoft" - item_color = "corp" - -/obj/item/clothing/head/soft/solgov - name = "Sol Federation marine cap" - desc = "A soft cap worn by marines of the Sol Federation." - icon_state = "solgovsoft" - item_color = "solgov" - dog_fashion = null - -/obj/item/clothing/head/soft/solgov/command - name = "Sol Federation Lieutenant's cap" - desc = "A soft cap worn by marines of the Sol Federation. The insignia signifies the wearer bears the rank of a Lieutenant." - icon_state = "solgovcsoft" - item_color = "solgovc" - dog_fashion = null \ No newline at end of file +/obj/item/clothing/head/soft + name = "cargo cap" + desc = "It's a baseball hat in a tasteless yellow colour." + icon_state = "cargosoft" + item_state = "helmet" + item_color = "cargo" + var/flipped = 0 + actions_types = list(/datum/action/item_action/flip_cap) + dog_fashion = /datum/dog_fashion/head/cargo_tech + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/head.dmi' + ) + +/obj/item/clothing/head/soft/dropped() + icon_state = "[item_color]soft" + flipped = 0 + ..() + +/obj/item/clothing/head/soft/attack_self(mob/user) + flip(user) + +/obj/item/clothing/head/soft/proc/flip(mob/user) + flipped = !flipped + if(flipped) + icon_state = "[item_color]soft_flipped" + to_chat(usr, "You flip the hat backwards.") + else + icon_state = "[item_color]soft" + to_chat(user, "You flip the hat back in normal position.") + user.update_inv_head() //so our mob-overlays update + + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + +/obj/item/clothing/head/soft/red + name = "red cap" + desc = "It's a baseball hat in a tasteless red colour." + icon_state = "redsoft" + item_color = "red" + dog_fashion = null + +/obj/item/clothing/head/soft/blue + name = "blue cap" + desc = "It's a baseball hat in a tasteless blue colour." + icon_state = "bluesoft" + item_color = "blue" + dog_fashion = null + +/obj/item/clothing/head/soft/green + name = "green cap" + desc = "It's a baseball hat in a tasteless green colour." + icon_state = "greensoft" + item_color = "green" + dog_fashion = null + +/obj/item/clothing/head/soft/yellow + name = "yellow cap" + desc = "It's a baseball hat in a tasteless yellow colour." + icon_state = "yellowsoft" + item_color = "yellow" + dog_fashion = null + +/obj/item/clothing/head/soft/grey + name = "grey cap" + desc = "It's a baseball hat in a tasteful grey colour." + icon_state = "greysoft" + item_color = "grey" + dog_fashion = null + +/obj/item/clothing/head/soft/orange + name = "orange cap" + desc = "It's a baseball hat in a tasteless orange colour." + icon_state = "orangesoft" + item_color = "orange" + +/obj/item/clothing/head/soft/mime + name = "white cap" + desc = "It's a baseball hat in a tasteless white colour." + icon_state = "mimesoft" + item_color = "mime" + dog_fashion = null + +/obj/item/clothing/head/soft/purple + name = "purple cap" + desc = "It's a baseball hat in a tasteless purple colour." + icon_state = "purplesoft" + item_color = "purple" + dog_fashion = null + +/obj/item/clothing/head/soft/black + name = "black cap" + desc = "It's a baseball hat in a tasteless black colour." + icon_state = "blacksoft" + item_color = "black" + dog_fashion = null + +/obj/item/clothing/head/soft/rainbow + name = "rainbow cap" + desc = "It's a baseball hat in a bright rainbow of colors." + icon_state = "rainbowsoft" + item_color = "rainbow" + dog_fashion = null + +/obj/item/clothing/head/soft/sec + name = "security cap" + desc = "It's baseball hat in tasteful red colour." + icon_state = "secsoft" + item_color = "sec" + armor = list("melee" = 30, "bullet" = 25, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = 50) + strip_delay = 60 + dog_fashion = null + +/obj/item/clothing/head/soft/sec/corp + name = "corporate security cap" + desc = "It's baseball hat in corpotate colours." + icon_state = "corpsoft" + item_color = "corp" + +/obj/item/clothing/head/soft/solgov + name = "Sol Federation marine cap" + desc = "A soft cap worn by marines of the Sol Federation." + icon_state = "solgovsoft" + item_color = "solgov" + dog_fashion = null + +/obj/item/clothing/head/soft/solgov/command + name = "Sol Federation Lieutenant's cap" + desc = "A soft cap worn by marines of the Sol Federation. The insignia signifies the wearer bears the rank of a Lieutenant." + icon_state = "solgovcsoft" + item_color = "solgovc" + dog_fashion = null diff --git a/code/modules/clothing/masks/boxing.dm b/code/modules/clothing/masks/boxing.dm index 78034a3c0c8..14afa81cb58 100644 --- a/code/modules/clothing/masks/boxing.dm +++ b/code/modules/clothing/masks/boxing.dm @@ -1,52 +1,52 @@ -/obj/item/clothing/mask/balaclava - name = "balaclava" - desc = "LOADSAMONEY" - icon_state = "balaclava" - item_state = "balaclava" - flags = BLOCKHAIR - flags_inv = HIDEFACE - w_class = WEIGHT_CLASS_SMALL - actions_types = list(/datum/action/item_action/adjust) - adjusted_flags = SLOT_HEAD - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi' - ) - -/obj/item/clothing/mask/balaclava/attack_self(var/mob/user) - adjustmask(user) - -/obj/item/clothing/mask/luchador - name = "Luchador Mask" - desc = "Worn by robust fighters, flying high to defeat their foes!" - icon_state = "luchag" - item_state = "luchag" - flags = BLOCKHAIR - flags_inv = HIDEFACE - w_class = WEIGHT_CLASS_SMALL - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi' - ) - -/obj/item/clothing/mask/luchador/tecnicos - name = "Tecnicos Mask" - desc = "Worn by robust fighters who uphold justice and fight honorably." - icon_state = "luchador" - item_state = "luchador" - -/obj/item/clothing/mask/luchador/rudos - name = "Rudos Mask" - desc = "Worn by robust fighters who are willing to do anything to win." - icon_state = "luchar" - item_state = "luchar" +/obj/item/clothing/mask/balaclava + name = "balaclava" + desc = "LOADSAMONEY" + icon_state = "balaclava" + item_state = "balaclava" + flags = BLOCKHAIR + flags_inv = HIDEFACE + w_class = WEIGHT_CLASS_SMALL + actions_types = list(/datum/action/item_action/adjust) + adjusted_flags = SLOT_HEAD + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi' + ) + +/obj/item/clothing/mask/balaclava/attack_self(var/mob/user) + adjustmask(user) + +/obj/item/clothing/mask/luchador + name = "Luchador Mask" + desc = "Worn by robust fighters, flying high to defeat their foes!" + icon_state = "luchag" + item_state = "luchag" + flags = BLOCKHAIR + flags_inv = HIDEFACE + w_class = WEIGHT_CLASS_SMALL + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi' + ) + +/obj/item/clothing/mask/luchador/tecnicos + name = "Tecnicos Mask" + desc = "Worn by robust fighters who uphold justice and fight honorably." + icon_state = "luchador" + item_state = "luchador" + +/obj/item/clothing/mask/luchador/rudos + name = "Rudos Mask" + desc = "Worn by robust fighters who are willing to do anything to win." + icon_state = "luchar" + item_state = "luchar" diff --git a/code/modules/clothing/masks/breath.dm b/code/modules/clothing/masks/breath.dm index c37fad48906..032062461e7 100644 --- a/code/modules/clothing/masks/breath.dm +++ b/code/modules/clothing/masks/breath.dm @@ -1,54 +1,54 @@ -/obj/item/clothing/mask/breath - desc = "A close-fitting mask that can be connected to an air supply." - name = "breath mask" - icon_state = "breath" - item_state = "breath" - flags = AIRTIGHT - flags_cover = MASKCOVERSMOUTH - w_class = WEIGHT_CLASS_SMALL - gas_transfer_coefficient = 0.10 - permeability_coefficient = 0.50 - actions_types = list(/datum/action/item_action/adjust) - resistance_flags = NONE - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Vox Armalis" = 'icons/mob/species/armalis/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi', - "Plasmaman" = 'icons/mob/species/plasmaman/mask.dmi' - ) - -/obj/item/clothing/mask/breath/attack_self(var/mob/user) - adjustmask(user) - -/obj/item/clothing/mask/breath/AltClick(mob/user) - ..() - if( (!in_range(src, user)) || user.stat || user.restrained() ) - return - adjustmask(user) - -/obj/item/clothing/mask/breath/medical - desc = "A close-fitting sterile mask that can be connected to an air supply." - name = "medical mask" - icon_state = "medical" - item_state = "medical" - permeability_coefficient = 0.01 - put_on_delay = 10 - -/obj/item/clothing/mask/breath/vox - desc = "A weirdly-shaped breath mask." - name = "vox breath mask" - icon_state = "voxmask" - item_state = "voxmask" - permeability_coefficient = 0.01 - species_restricted = list("Vox", "Vox Armalis") //These should fit the "Mega Vox" just fine. - actions_types = list() - -/obj/item/clothing/mask/breath/vox/attack_self(var/mob/user) - return - -/obj/item/clothing/mask/breath/vox/AltClick(mob/user) - return +/obj/item/clothing/mask/breath + desc = "A close-fitting mask that can be connected to an air supply." + name = "breath mask" + icon_state = "breath" + item_state = "breath" + flags = AIRTIGHT + flags_cover = MASKCOVERSMOUTH + w_class = WEIGHT_CLASS_SMALL + gas_transfer_coefficient = 0.10 + permeability_coefficient = 0.50 + actions_types = list(/datum/action/item_action/adjust) + resistance_flags = NONE + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Vox Armalis" = 'icons/mob/species/armalis/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi', + "Plasmaman" = 'icons/mob/species/plasmaman/mask.dmi' + ) + +/obj/item/clothing/mask/breath/attack_self(var/mob/user) + adjustmask(user) + +/obj/item/clothing/mask/breath/AltClick(mob/user) + ..() + if( (!in_range(src, user)) || user.stat || user.restrained() ) + return + adjustmask(user) + +/obj/item/clothing/mask/breath/medical + desc = "A close-fitting sterile mask that can be connected to an air supply." + name = "medical mask" + icon_state = "medical" + item_state = "medical" + permeability_coefficient = 0.01 + put_on_delay = 10 + +/obj/item/clothing/mask/breath/vox + desc = "A weirdly-shaped breath mask." + name = "vox breath mask" + icon_state = "voxmask" + item_state = "voxmask" + permeability_coefficient = 0.01 + species_restricted = list("Vox", "Vox Armalis") //These should fit the "Mega Vox" just fine. + actions_types = list() + +/obj/item/clothing/mask/breath/vox/attack_self(var/mob/user) + return + +/obj/item/clothing/mask/breath/vox/AltClick(mob/user) + return diff --git a/code/modules/clothing/masks/gasmask.dm b/code/modules/clothing/masks/gasmask.dm index b6e3164a44d..b798c1d8df0 100644 --- a/code/modules/clothing/masks/gasmask.dm +++ b/code/modules/clothing/masks/gasmask.dm @@ -1,365 +1,365 @@ -/obj/item/clothing/mask/gas - name = "gas mask" - desc = "A face-covering mask that can be connected to an air supply." - icon_state = "gas_alt" - flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT - flags_inv = HIDEEARS|HIDEEYES|HIDEFACE - flags_cover = MASKCOVERSMOUTH | MASKCOVERSEYES - w_class = WEIGHT_CLASS_NORMAL - item_state = "gas_alt" - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.01 - resistance_flags = NONE - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi', - "Plasmaman" = 'icons/mob/species/plasmaman/mask.dmi' - ) - -// **** Welding gas mask **** - -/obj/item/clothing/mask/gas/welding - name = "welding mask" - desc = "A gas mask with built in welding goggles and face shield. Looks like a skull, clearly designed by a nerd." - icon_state = "weldingmask" - item_state = "weldingmask" - materials = list(MAT_METAL=4000, MAT_GLASS=2000) - flash_protect = 2 - tint = 2 - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 55) - origin_tech = "materials=2;engineering=3" - actions_types = list(/datum/action/item_action/toggle) - flags_inv = HIDEEARS|HIDEEYES|HIDEFACE - flags_cover = MASKCOVERSEYES - visor_flags_inv = HIDEEYES - resistance_flags = FIRE_PROOF - -/obj/item/clothing/mask/gas/welding/attack_self(mob/user) - weldingvisortoggle(user) - -/obj/item/clothing/mask/gas/explorer - name = "explorer gas mask" - desc = "A military-grade gas mask that can be connected to an air supply." - icon_state = "gas_mining" - actions_types = list(/datum/action/item_action/adjust) - armor = list("melee" = 10, "bullet" = 5, "laser" = 5, "energy" = 5, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 20, "acid" = 40) - resistance_flags = FIRE_PROOF - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi' - ) - -/obj/item/clothing/mask/gas/explorer/attack_self(mob/user) - adjustmask(user) - -/obj/item/clothing/mask/gas/explorer/adjustmask(user) - ..() - w_class = mask_adjusted ? WEIGHT_CLASS_SMALL : WEIGHT_CLASS_NORMAL - -/obj/item/clothing/mask/gas/explorer/folded/Initialize() - . = ..() - adjustmask() - -//Bane gas mask -/obj/item/clothing/mask/banemask - name = "bane mask" - desc = "Only when the station is in flames, do you have my permission to robust." - icon_state = "bane_mask" - flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT - flags_inv = HIDEEARS|HIDEEYES|HIDEFACE - flags_cover = MASKCOVERSMOUTH | MASKCOVERSEYES - w_class = WEIGHT_CLASS_NORMAL - item_state = "bane_mask" - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.01 - - -//Plague Dr suit can be found in clothing/suits/bio.dm -/obj/item/clothing/mask/gas/plaguedoctor - name = "plague doctor mask" - desc = "A modernised version of the classic design, this mask will not only filter out toxins but it can also be connected to an air supply." - icon_state = "plaguedoctor" - item_state = "gas_mask" - armor = list("melee" = 0, "bullet" = 0, "laser" = 2, "energy" = 2, "bomb" = 0, "bio" = 75, "rad" = 0, "fire" = 0, "acid" = 0) - -/obj/item/clothing/mask/gas/swat - name = "\improper SWAT mask" - desc = "A close-fitting tactical mask that can be connected to an air supply." - icon_state = "swat" - -/obj/item/clothing/mask/gas/syndicate - name = "syndicate mask" - desc = "A close-fitting tactical mask that can be connected to an air supply." - icon_state = "swat" - strip_delay = 60 - -/obj/item/clothing/mask/gas/clown_hat - name = "clown wig and mask" - desc = "A true prankster's facial attire. A clown is incomplete without his wig and mask." - icon_state = "clown" - item_state = "clown_hat" - flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | BLOCKHAIR - resistance_flags = FLAMMABLE - dog_fashion = /datum/dog_fashion/head/clown - -/obj/item/clothing/mask/gas/clown_hat/attack_self(mob/user) - - var/mob/M = usr - var/list/options = list() - options["True Form"] = "clown" - options["The Feminist"] = "sexyclown" - options["The Madman"] = "joker" - options["The Rainbow Color"] ="rainbow" - - var/choice = input(M,"To what form do you wish to Morph this mask?","Morph Mask") in options - - if(src && choice && !M.stat && in_range(M,src)) - icon_state = options[choice] - to_chat(M, "Your Clown Mask has now morphed into [choice], all praise the Honk Mother!") - return 1 - -/obj/item/clothing/mask/gas/clown_hat/sexy - name = "sexy-clown wig and mask" - desc = "A feminine clown mask for the dabbling crossdressers or female entertainers." - icon_state = "sexyclown" - item_state = "sexyclown" - -/obj/item/clothing/mask/gas/clownwiz - name = "wizard clown wig and mask" - desc = "Some pranksters are truly magical." - icon_state = "wizzclown" - item_state = "wizzclown" - flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | BLOCKHAIR - flags_inv = HIDEEARS | HIDEEYES - magical = TRUE - -/obj/item/clothing/mask/gas/clown_hat/nodrop - flags = NODROP - -/obj/item/clothing/mask/gas/mime - name = "mime mask" - desc = "The traditional mime's mask. It has an eerie facial posture." - icon_state = "mime" - item_state = "mime" - resistance_flags = FLAMMABLE - -/obj/item/clothing/mask/gas/mime/nodrop - flags = NODROP - -/obj/item/clothing/mask/gas/monkeymask - name = "monkey mask" - desc = "A mask used when acting as a monkey." - icon_state = "monkeymask" - item_state = "monkeymask" - resistance_flags = FLAMMABLE - -/obj/item/clothing/mask/gas/sexymime - name = "sexy mime mask" - desc = "A traditional female mime's mask." - icon_state = "sexymime" - item_state = "sexymime" - resistance_flags = FLAMMABLE - -/obj/item/clothing/mask/gas/cyborg - name = "cyborg visor" - desc = "Beep boop" - icon_state = "death" - resistance_flags = FLAMMABLE - -/obj/item/clothing/mask/gas/owl_mask - name = "owl mask" - desc = "Twoooo!" - icon_state = "owl" - resistance_flags = FLAMMABLE - actions_types = list(/datum/action/item_action/hoot) - -/obj/item/clothing/mask/gas/owl_mask/super_hero - flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | NODROP - -/obj/item/clothing/mask/gas/owl_mask/attack_self() - hoot() - -/obj/item/clothing/mask/gas/owl_mask/proc/hoot() - if(cooldown < world.time - 35) // A cooldown, to stop people being jerks - playsound(src.loc, 'sound/creatures/hoot.ogg', 50, 1) - cooldown = world.time - -// ******************************************************************** - -// **** Security gas mask **** - -/obj/item/clothing/mask/gas/sechailer - name = "security gas mask" - desc = "A standard issue Security gas mask with integrated 'Compli-o-nator 3000' device, plays over a dozen pre-recorded compliance phrases designed to get scumbags to stand still whilst you taze them. Do not tamper with the device." - icon_state = "sechailer" - item_state = "sechailer" - var/phrase = 1 - var/aggressiveness = 1 - var/safety = 1 - actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/adjust, /datum/action/item_action/selectphrase) - var/phrase_list = list( - - "halt" = "HALT! HALT! HALT! HALT!", - "bobby" = "Stop in the name of the Law.", - "compliance" = "Compliance is in your best interest.", - "justice" = "Prepare for justice!", - "running" = "Running will only increase your sentence.", - "dontmove" = "Don't move, Creep!", - "floor" = "Down on the floor, Creep!", - "robocop" = "Dead or alive you're coming with me.", - "god" = "God made today for the crooks we could not catch yesterday.", - "freeze" = "Freeze, Scum Bag!", - "imperial" = "Stop right there, criminal scum!", - "bash" = "Stop or I'll bash you.", - "harry" = "Go ahead, make my day.", - "asshole" = "Stop breaking the law, asshole.", - "stfu" = "You have the right to shut the fuck up", - "shutup" = "Shut up crime!", - "super" = "Face the wrath of the golden bolt.", - "dredd" = "I am, the LAW!" - ) -/obj/item/clothing/mask/gas/sechailer/hos - name = "\improper HOS SWAT mask" - desc = "A close-fitting tactical mask with an especially aggressive Compli-o-nator 3000. It has a tan stripe." - icon_state = "hosmask" - aggressiveness = 3 - phrase = 12 - actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) - -/obj/item/clothing/mask/gas/sechailer/warden - name = "\improper Warden SWAT mask" - desc = "A close-fitting tactical mask with an especially aggressive Compli-o-nator 3000. It has a blue stripe." - icon_state = "wardenmask" - aggressiveness = 3 - phrase = 12 - actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) - - -/obj/item/clothing/mask/gas/sechailer/swat - name = "\improper SWAT mask" - desc = "A close-fitting tactical mask with an especially aggressive Compli-o-nator 3000." - icon_state = "officermask" - aggressiveness = 3 - phrase = 12 - actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) - -/obj/item/clothing/mask/gas/sechailer/blue - name = "\improper blue SWAT mask" - desc = "A neon blue swat mask, used for demoralizing Greytide in the wild." - icon_state = "blue_sechailer" - item_state = "blue_sechailer" - aggressiveness = 3 - phrase = 12 - actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) - -/obj/item/clothing/mask/gas/sechailer/cyborg - name = "security hailer" - desc = "A set of recognizable pre-recorded messages for cyborgs to use when apprehending criminals." - icon = 'icons/obj/device.dmi' - icon_state = "taperecorder_idle" - actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) - -/obj/item/clothing/mask/gas/sechailer/ui_action_click(mob/user, actiontype) - if(actiontype == /datum/action/item_action/halt) - halt() - else if(actiontype == /datum/action/item_action/adjust) - adjustmask(user) - else if(actiontype == /datum/action/item_action/selectphrase) - var/key = phrase_list[phrase] - var/message = phrase_list[key] - - if (!safety) - to_chat(user, "You set the restrictor to: FUCK YOUR CUNT YOU SHIT EATING COCKSUCKER MAN EAT A DONG FUCKING ASS RAMMING SHIT FUCK EAT PENISES IN YOUR FUCK FACE AND SHIT OUT ABORTIONS OF FUCK AND DO SHIT IN YOUR ASS YOU COCK FUCK SHIT MONKEY FUCK ASS WANKER FROM THE DEPTHS OF SHIT.") - return - - switch(aggressiveness) - if(1) - phrase = (phrase < 6) ? (phrase + 1) : 1 - key = phrase_list[phrase] - message = phrase_list[key] - to_chat(user,"You set the restrictor to: [message]") - if(2) - phrase = (phrase < 11 && phrase >= 7) ? (phrase + 1) : 7 - key = phrase_list[phrase] - message = phrase_list[key] - to_chat(user,"You set the restrictor to: [message]") - if(3) - phrase = (phrase < 18 && phrase >= 12 ) ? (phrase + 1) : 12 - key = phrase_list[phrase] - message = phrase_list[key] - to_chat(user,"You set the restrictor to: [message]") - if(4) - phrase = (phrase < 18 && phrase >= 1 ) ? (phrase + 1) : 1 - key = phrase_list[phrase] - message = phrase_list[key] - to_chat(user,"You set the restrictor to: [message]") - else - to_chat(user, "It's broken.") - -/obj/item/clothing/mask/gas/sechailer/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/screwdriver)) - switch(aggressiveness) - if(1) - to_chat(user, "You set the aggressiveness restrictor to the second position.") - aggressiveness = 2 - phrase = 7 - if(2) - to_chat(user, "You set the aggressiveness restrictor to the third position.") - aggressiveness = 3 - phrase = 13 - if(3) - to_chat(user, "You set the aggressiveness restrictor to the fourth position.") - aggressiveness = 4 - phrase = 1 - if(4) - to_chat(user, "You set the aggressiveness restrictor to the first position.") - aggressiveness = 1 - phrase = 1 - if(5) - to_chat(user, "You adjust the restrictor but nothing happens, probably because its broken.") - else if(istype(W, /obj/item/wirecutters)) - if(aggressiveness != 5) - to_chat(user, "You broke it!") - aggressiveness = 5 - else - ..() - -/obj/item/clothing/mask/gas/sechailer/attack_self() - halt() - -/obj/item/clothing/mask/gas/sechailer/emag_act(mob/user as mob) - if(safety) - safety = 0 - to_chat(user, "You silently fry [src]'s vocal circuit with the cryptographic sequencer.") - else - return - -/obj/item/clothing/mask/gas/sechailer/proc/halt() - var/key = phrase_list[phrase] - var/message = phrase_list[key] - - - if(cooldown < world.time - 35) // A cooldown, to stop people being jerks - if(!safety) - message = "FUCK YOUR CUNT YOU SHIT EATING COCKSUCKER MAN EAT A DONG FUCKING ASS RAMMING SHIT FUCK EAT PENISES IN YOUR FUCK FACE AND SHIT OUT ABORTIONS OF FUCK AND DO SHIT IN YOUR ASS YOU COCK FUCK SHIT MONKEY FUCK ASS WANKER FROM THE DEPTHS OF SHIT." - usr.visible_message("[usr]'s Compli-o-Nator: [message]") - playsound(src.loc, 'sound/voice/binsult.ogg', 100, 0, 4) - cooldown = world.time - return - - usr.visible_message("[usr]'s Compli-o-Nator: [message]") - playsound(src.loc, "sound/voice/complionator/[key].ogg", 100, 0, 4) - cooldown = world.time - - - -// ******************************************************************** +/obj/item/clothing/mask/gas + name = "gas mask" + desc = "A face-covering mask that can be connected to an air supply." + icon_state = "gas_alt" + flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT + flags_inv = HIDEEARS|HIDEEYES|HIDEFACE + flags_cover = MASKCOVERSMOUTH | MASKCOVERSEYES + w_class = WEIGHT_CLASS_NORMAL + item_state = "gas_alt" + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.01 + resistance_flags = NONE + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi', + "Plasmaman" = 'icons/mob/species/plasmaman/mask.dmi' + ) + +// **** Welding gas mask **** + +/obj/item/clothing/mask/gas/welding + name = "welding mask" + desc = "A gas mask with built in welding goggles and face shield. Looks like a skull, clearly designed by a nerd." + icon_state = "weldingmask" + item_state = "weldingmask" + materials = list(MAT_METAL=4000, MAT_GLASS=2000) + flash_protect = 2 + tint = 2 + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 55) + origin_tech = "materials=2;engineering=3" + actions_types = list(/datum/action/item_action/toggle) + flags_inv = HIDEEARS|HIDEEYES|HIDEFACE + flags_cover = MASKCOVERSEYES + visor_flags_inv = HIDEEYES + resistance_flags = FIRE_PROOF + +/obj/item/clothing/mask/gas/welding/attack_self(mob/user) + weldingvisortoggle(user) + +/obj/item/clothing/mask/gas/explorer + name = "explorer gas mask" + desc = "A military-grade gas mask that can be connected to an air supply." + icon_state = "gas_mining" + actions_types = list(/datum/action/item_action/adjust) + armor = list("melee" = 10, "bullet" = 5, "laser" = 5, "energy" = 5, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 20, "acid" = 40) + resistance_flags = FIRE_PROOF + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi' + ) + +/obj/item/clothing/mask/gas/explorer/attack_self(mob/user) + adjustmask(user) + +/obj/item/clothing/mask/gas/explorer/adjustmask(user) + ..() + w_class = mask_adjusted ? WEIGHT_CLASS_SMALL : WEIGHT_CLASS_NORMAL + +/obj/item/clothing/mask/gas/explorer/folded/Initialize() + . = ..() + adjustmask() + +//Bane gas mask +/obj/item/clothing/mask/banemask + name = "bane mask" + desc = "Only when the station is in flames, do you have my permission to robust." + icon_state = "bane_mask" + flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT + flags_inv = HIDEEARS|HIDEEYES|HIDEFACE + flags_cover = MASKCOVERSMOUTH | MASKCOVERSEYES + w_class = WEIGHT_CLASS_NORMAL + item_state = "bane_mask" + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.01 + + +//Plague Dr suit can be found in clothing/suits/bio.dm +/obj/item/clothing/mask/gas/plaguedoctor + name = "plague doctor mask" + desc = "A modernised version of the classic design, this mask will not only filter out toxins but it can also be connected to an air supply." + icon_state = "plaguedoctor" + item_state = "gas_mask" + armor = list("melee" = 0, "bullet" = 0, "laser" = 2, "energy" = 2, "bomb" = 0, "bio" = 75, "rad" = 0, "fire" = 0, "acid" = 0) + +/obj/item/clothing/mask/gas/swat + name = "\improper SWAT mask" + desc = "A close-fitting tactical mask that can be connected to an air supply." + icon_state = "swat" + +/obj/item/clothing/mask/gas/syndicate + name = "syndicate mask" + desc = "A close-fitting tactical mask that can be connected to an air supply." + icon_state = "swat" + strip_delay = 60 + +/obj/item/clothing/mask/gas/clown_hat + name = "clown wig and mask" + desc = "A true prankster's facial attire. A clown is incomplete without his wig and mask." + icon_state = "clown" + item_state = "clown_hat" + flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | BLOCKHAIR + resistance_flags = FLAMMABLE + dog_fashion = /datum/dog_fashion/head/clown + +/obj/item/clothing/mask/gas/clown_hat/attack_self(mob/user) + + var/mob/M = usr + var/list/options = list() + options["True Form"] = "clown" + options["The Feminist"] = "sexyclown" + options["The Madman"] = "joker" + options["The Rainbow Color"] ="rainbow" + + var/choice = input(M,"To what form do you wish to Morph this mask?","Morph Mask") in options + + if(src && choice && !M.stat && in_range(M,src)) + icon_state = options[choice] + to_chat(M, "Your Clown Mask has now morphed into [choice], all praise the Honk Mother!") + return 1 + +/obj/item/clothing/mask/gas/clown_hat/sexy + name = "sexy-clown wig and mask" + desc = "A feminine clown mask for the dabbling crossdressers or female entertainers." + icon_state = "sexyclown" + item_state = "sexyclown" + +/obj/item/clothing/mask/gas/clownwiz + name = "wizard clown wig and mask" + desc = "Some pranksters are truly magical." + icon_state = "wizzclown" + item_state = "wizzclown" + flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | BLOCKHAIR + flags_inv = HIDEEARS | HIDEEYES + magical = TRUE + +/obj/item/clothing/mask/gas/clown_hat/nodrop + flags = NODROP + +/obj/item/clothing/mask/gas/mime + name = "mime mask" + desc = "The traditional mime's mask. It has an eerie facial posture." + icon_state = "mime" + item_state = "mime" + resistance_flags = FLAMMABLE + +/obj/item/clothing/mask/gas/mime/nodrop + flags = NODROP + +/obj/item/clothing/mask/gas/monkeymask + name = "monkey mask" + desc = "A mask used when acting as a monkey." + icon_state = "monkeymask" + item_state = "monkeymask" + resistance_flags = FLAMMABLE + +/obj/item/clothing/mask/gas/sexymime + name = "sexy mime mask" + desc = "A traditional female mime's mask." + icon_state = "sexymime" + item_state = "sexymime" + resistance_flags = FLAMMABLE + +/obj/item/clothing/mask/gas/cyborg + name = "cyborg visor" + desc = "Beep boop" + icon_state = "death" + resistance_flags = FLAMMABLE + +/obj/item/clothing/mask/gas/owl_mask + name = "owl mask" + desc = "Twoooo!" + icon_state = "owl" + resistance_flags = FLAMMABLE + actions_types = list(/datum/action/item_action/hoot) + +/obj/item/clothing/mask/gas/owl_mask/super_hero + flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | NODROP + +/obj/item/clothing/mask/gas/owl_mask/attack_self() + hoot() + +/obj/item/clothing/mask/gas/owl_mask/proc/hoot() + if(cooldown < world.time - 35) // A cooldown, to stop people being jerks + playsound(src.loc, 'sound/creatures/hoot.ogg', 50, 1) + cooldown = world.time + +// ******************************************************************** + +// **** Security gas mask **** + +/obj/item/clothing/mask/gas/sechailer + name = "security gas mask" + desc = "A standard issue Security gas mask with integrated 'Compli-o-nator 3000' device, plays over a dozen pre-recorded compliance phrases designed to get scumbags to stand still whilst you taze them. Do not tamper with the device." + icon_state = "sechailer" + item_state = "sechailer" + var/phrase = 1 + var/aggressiveness = 1 + var/safety = 1 + actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/adjust, /datum/action/item_action/selectphrase) + var/phrase_list = list( + + "halt" = "HALT! HALT! HALT! HALT!", + "bobby" = "Stop in the name of the Law.", + "compliance" = "Compliance is in your best interest.", + "justice" = "Prepare for justice!", + "running" = "Running will only increase your sentence.", + "dontmove" = "Don't move, Creep!", + "floor" = "Down on the floor, Creep!", + "robocop" = "Dead or alive you're coming with me.", + "god" = "God made today for the crooks we could not catch yesterday.", + "freeze" = "Freeze, Scum Bag!", + "imperial" = "Stop right there, criminal scum!", + "bash" = "Stop or I'll bash you.", + "harry" = "Go ahead, make my day.", + "asshole" = "Stop breaking the law, asshole.", + "stfu" = "You have the right to shut the fuck up", + "shutup" = "Shut up crime!", + "super" = "Face the wrath of the golden bolt.", + "dredd" = "I am, the LAW!" + ) +/obj/item/clothing/mask/gas/sechailer/hos + name = "\improper HOS SWAT mask" + desc = "A close-fitting tactical mask with an especially aggressive Compli-o-nator 3000. It has a tan stripe." + icon_state = "hosmask" + aggressiveness = 3 + phrase = 12 + actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) + +/obj/item/clothing/mask/gas/sechailer/warden + name = "\improper Warden SWAT mask" + desc = "A close-fitting tactical mask with an especially aggressive Compli-o-nator 3000. It has a blue stripe." + icon_state = "wardenmask" + aggressiveness = 3 + phrase = 12 + actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) + + +/obj/item/clothing/mask/gas/sechailer/swat + name = "\improper SWAT mask" + desc = "A close-fitting tactical mask with an especially aggressive Compli-o-nator 3000." + icon_state = "officermask" + aggressiveness = 3 + phrase = 12 + actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) + +/obj/item/clothing/mask/gas/sechailer/blue + name = "\improper blue SWAT mask" + desc = "A neon blue swat mask, used for demoralizing Greytide in the wild." + icon_state = "blue_sechailer" + item_state = "blue_sechailer" + aggressiveness = 3 + phrase = 12 + actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) + +/obj/item/clothing/mask/gas/sechailer/cyborg + name = "security hailer" + desc = "A set of recognizable pre-recorded messages for cyborgs to use when apprehending criminals." + icon = 'icons/obj/device.dmi' + icon_state = "taperecorder_idle" + actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase) + +/obj/item/clothing/mask/gas/sechailer/ui_action_click(mob/user, actiontype) + if(actiontype == /datum/action/item_action/halt) + halt() + else if(actiontype == /datum/action/item_action/adjust) + adjustmask(user) + else if(actiontype == /datum/action/item_action/selectphrase) + var/key = phrase_list[phrase] + var/message = phrase_list[key] + + if (!safety) + to_chat(user, "You set the restrictor to: FUCK YOUR CUNT YOU SHIT EATING COCKSUCKER MAN EAT A DONG FUCKING ASS RAMMING SHIT FUCK EAT PENISES IN YOUR FUCK FACE AND SHIT OUT ABORTIONS OF FUCK AND DO SHIT IN YOUR ASS YOU COCK FUCK SHIT MONKEY FUCK ASS WANKER FROM THE DEPTHS OF SHIT.") + return + + switch(aggressiveness) + if(1) + phrase = (phrase < 6) ? (phrase + 1) : 1 + key = phrase_list[phrase] + message = phrase_list[key] + to_chat(user,"You set the restrictor to: [message]") + if(2) + phrase = (phrase < 11 && phrase >= 7) ? (phrase + 1) : 7 + key = phrase_list[phrase] + message = phrase_list[key] + to_chat(user,"You set the restrictor to: [message]") + if(3) + phrase = (phrase < 18 && phrase >= 12 ) ? (phrase + 1) : 12 + key = phrase_list[phrase] + message = phrase_list[key] + to_chat(user,"You set the restrictor to: [message]") + if(4) + phrase = (phrase < 18 && phrase >= 1 ) ? (phrase + 1) : 1 + key = phrase_list[phrase] + message = phrase_list[key] + to_chat(user,"You set the restrictor to: [message]") + else + to_chat(user, "It's broken.") + +/obj/item/clothing/mask/gas/sechailer/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/screwdriver)) + switch(aggressiveness) + if(1) + to_chat(user, "You set the aggressiveness restrictor to the second position.") + aggressiveness = 2 + phrase = 7 + if(2) + to_chat(user, "You set the aggressiveness restrictor to the third position.") + aggressiveness = 3 + phrase = 13 + if(3) + to_chat(user, "You set the aggressiveness restrictor to the fourth position.") + aggressiveness = 4 + phrase = 1 + if(4) + to_chat(user, "You set the aggressiveness restrictor to the first position.") + aggressiveness = 1 + phrase = 1 + if(5) + to_chat(user, "You adjust the restrictor but nothing happens, probably because its broken.") + else if(istype(W, /obj/item/wirecutters)) + if(aggressiveness != 5) + to_chat(user, "You broke it!") + aggressiveness = 5 + else + ..() + +/obj/item/clothing/mask/gas/sechailer/attack_self() + halt() + +/obj/item/clothing/mask/gas/sechailer/emag_act(mob/user as mob) + if(safety) + safety = 0 + to_chat(user, "You silently fry [src]'s vocal circuit with the cryptographic sequencer.") + else + return + +/obj/item/clothing/mask/gas/sechailer/proc/halt() + var/key = phrase_list[phrase] + var/message = phrase_list[key] + + + if(cooldown < world.time - 35) // A cooldown, to stop people being jerks + if(!safety) + message = "FUCK YOUR CUNT YOU SHIT EATING COCKSUCKER MAN EAT A DONG FUCKING ASS RAMMING SHIT FUCK EAT PENISES IN YOUR FUCK FACE AND SHIT OUT ABORTIONS OF FUCK AND DO SHIT IN YOUR ASS YOU COCK FUCK SHIT MONKEY FUCK ASS WANKER FROM THE DEPTHS OF SHIT." + usr.visible_message("[usr]'s Compli-o-Nator: [message]") + playsound(src.loc, 'sound/voice/binsult.ogg', 100, 0, 4) + cooldown = world.time + return + + usr.visible_message("[usr]'s Compli-o-Nator: [message]") + playsound(src.loc, "sound/voice/complionator/[key].ogg", 100, 0, 4) + cooldown = world.time + + + +// ******************************************************************** diff --git a/code/modules/clothing/masks/miscellaneous.dm b/code/modules/clothing/masks/miscellaneous.dm index 789696ae874..5cc891b56af 100644 --- a/code/modules/clothing/masks/miscellaneous.dm +++ b/code/modules/clothing/masks/miscellaneous.dm @@ -1,519 +1,523 @@ -/obj/item/clothing/mask/muzzle - name = "muzzle" - desc = "To stop that awful noise." - icon_state = "muzzle" - item_state = "muzzle" - flags_cover = MASKCOVERSMOUTH - w_class = WEIGHT_CLASS_SMALL - gas_transfer_coefficient = 0.90 - put_on_delay = 20 - var/resist_time = 0 //deciseconds of how long you need to gnaw to get rid of the gag, 0 to make it impossible to remove - var/mute = MUZZLE_MUTE_ALL - var/security_lock = FALSE // Requires brig access to remove 0 - Remove as normal - var/locked = FALSE //Indicates if a mask is locked, should always start as 0. - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi' - ) - -// Clumsy folks can't take the mask off themselves. -/obj/item/clothing/mask/muzzle/attack_hand(mob/user as mob) - if(user.wear_mask == src && !user.IsAdvancedToolUser()) - return 0 - else if(security_lock && locked) - if(do_unlock(user)) - visible_message("[user] unlocks [user.p_their()] [src.name].", \ - "[user] unlocks [user.p_their()] [src.name].") - ..() - return 1 - -/obj/item/clothing/mask/muzzle/proc/do_break() - if(security_lock) - security_lock = FALSE - locked = FALSE - flags &= ~NODROP - desc += " This one appears to be broken." - return TRUE - else - return FALSE - -/obj/item/clothing/mask/muzzle/proc/do_unlock(mob/living/carbon/human/user) - if(istype(user.get_inactive_hand(), /obj/item/card/emag)) - to_chat(user, "The lock vibrates as the card forces its locking system open.") - do_break() - return TRUE - else if(access_brig in user.get_access()) - to_chat(user, "The muzzle unlocks with a click.") - locked = FALSE - flags &= ~NODROP - return TRUE - - to_chat(user, "You must be wearing a security ID card or have one in your inactive hand to remove the muzzle.") - return FALSE - -/obj/item/clothing/mask/muzzle/proc/do_lock(mob/living/carbon/human/user) - if(security_lock) - locked = TRUE - flags |= NODROP - return TRUE - return FALSE - -/obj/item/clothing/mask/muzzle/Topic(href, href_list) - ..() - if(href_list["locked"]) - var/mob/living/carbon/wearer = locate(href_list["locked"]) - var/success = 0 - if(ishuman(usr)) - visible_message("[usr] tries to [locked ? "unlock" : "lock"] [wearer]'s [name].", \ - "[usr] tries to [locked ? "unlock" : "lock"] [wearer]'s [name].") - if(do_mob(usr, wearer, POCKET_STRIP_DELAY)) - if(locked) - success = do_unlock(usr) - else - success = do_lock(usr) - if(success) - visible_message("[usr] [locked ? "locks" : "unlocks"] [wearer]'s [name].", \ - "[usr] [locked ? "locks" : "unlocks"] [wearer]'s [name].") - if(usr.machine == wearer && in_range(src, usr)) - wearer.show_inv(usr) - else - to_chat(usr, "You lack the ability to manipulate the lock.") - - -/obj/item/clothing/mask/muzzle/tapegag - name = "tape gag" - desc = "MHPMHHH!" - icon_state = "tapegag" - item_state = null - w_class = WEIGHT_CLASS_TINY - resist_time = 150 - mute = MUZZLE_MUTE_MUFFLE - flags = DROPDEL - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi' - ) - -/obj/item/clothing/mask/muzzle/tapegag/dropped(mob/user) - var/obj/item/trash/tapetrash/TT = new - transfer_fingerprints_to(TT) - user.transfer_fingerprints_to(TT) - user.put_in_active_hand(TT) - playsound(src, 'sound/items/poster_ripped.ogg', 40, 1) - user.emote("scream") - ..() - -/obj/item/clothing/mask/muzzle/safety - name = "safety muzzle" - desc = "A muzzle designed to prevent biting." - icon_state = "muzzle_secure" - item_state = "muzzle_secure" - resist_time = 0 - mute = MUZZLE_MUTE_NONE - security_lock = TRUE - locked = FALSE - materials = list(MAT_METAL=500, MAT_GLASS=50) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi' - ) - -/obj/item/clothing/mask/muzzle/safety/shock - name = "shock muzzle" - desc = "A muzzle designed to prevent biting. This one is fitted with a behavior correction system." - var/obj/item/assembly/trigger = null - origin_tech = "materials=1;engineering=1" - materials = list(MAT_METAL=500, MAT_GLASS=50) - -/obj/item/clothing/mask/muzzle/safety/shock/attackby(obj/item/W, mob/user, params) - if(isscrewdriver(W) && trigger) - to_chat(user, "You disassemble [src].") - trigger.forceMove(get_turf(user)) - trigger.master = null - trigger.holder = null - trigger = null - return TRUE - else if(istype(W, /obj/item/assembly/signaler) || istype(W, /obj/item/assembly/voice)) - if(istype(trigger, /obj/item/assembly/signaler) || istype(trigger, /obj/item/assembly/voice)) - to_chat(user, "Something is already attached to [src].") - return FALSE - if(!user.drop_item()) - to_chat(user, "You are unable to insert [W] into [src].") - return FALSE - trigger = W - trigger.forceMove(src) - trigger.master = src - trigger.holder = src - to_chat(user, "You attach the [W] to [src].") - return TRUE - else if(istype(W, /obj/item/assembly)) - to_chat(user, "That won't fit in [src]. Perhaps a signaler or voice analyzer would?") - return FALSE - - return ..() - - -/obj/item/clothing/mask/muzzle/safety/shock/proc/can_shock(obj/item/clothing/C) - if(istype(C)) - if(isliving(C.loc)) - return C.loc - else if(isliving(loc)) - return loc - return FALSE - -/obj/item/clothing/mask/muzzle/safety/shock/proc/process_activation(var/obj/D, var/normal = 1, var/special = 1) - visible_message("[bicon(src)] *beep* *beep*", "*beep* *beep*") - var/mob/M = can_shock(loc) - if(M) - to_chat(M, "You feel a sharp shock!") - do_sparks(3, 1, M) - - M.Weaken(5) - M.Stuttering(1) - M.Jitter(20) - return - -/obj/item/clothing/mask/muzzle/safety/shock/HasProximity(atom/movable/AM as mob|obj) - if(trigger) - trigger.HasProximity(AM) - - -/obj/item/clothing/mask/muzzle/safety/shock/hear_talk(mob/living/M as mob, list/message_pieces) - if(trigger) - trigger.hear_talk(M, message_pieces) - -/obj/item/clothing/mask/muzzle/safety/shock/hear_message(mob/living/M as mob, msg) - if(trigger) - trigger.hear_message(M, msg) - - - -/obj/item/clothing/mask/surgical - name = "sterile mask" - desc = "A sterile mask designed to help prevent the spread of diseases." - icon_state = "sterile" - item_state = "sterile" - w_class = WEIGHT_CLASS_TINY - flags_cover = MASKCOVERSMOUTH - gas_transfer_coefficient = 0.90 - permeability_coefficient = 0.01 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 25, "rad" = 0, "fire" = 0, "acid" = 0) - actions_types = list(/datum/action/item_action/adjust) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi' - ) - - -/obj/item/clothing/mask/surgical/attack_self(var/mob/user) - adjustmask(user) - -/obj/item/clothing/mask/fakemoustache - name = "completely real moustache" - desc = "moustache is totally real." - icon_state = "fake-moustache" - flags_inv = HIDEFACE - actions_types = list(/datum/action/item_action/pontificate) - dog_fashion = /datum/dog_fashion/head/not_ian - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi' - ) - -/obj/item/clothing/mask/fakemoustache/attack_self(mob/user) - pontificate(user) - -/obj/item/clothing/mask/fakemoustache/item_action_slot_check(slot) - if(slot == slot_wear_mask) - return 1 - -/obj/item/clothing/mask/fakemoustache/proc/pontificate(mob/user) - user.visible_message("\ [user] twirls [user.p_their()] moustache and laughs [pick("fiendishly","maniacally","diabolically","evilly")]!") - -//scarves (fit in in mask slot) - -/obj/item/clothing/mask/bluescarf - name = "blue neck scarf" - desc = "A blue neck scarf." - icon_state = "blueneckscarf" - item_state = "blueneckscarf" - flags_cover = MASKCOVERSMOUTH - w_class = WEIGHT_CLASS_SMALL - gas_transfer_coefficient = 0.90 - - -/obj/item/clothing/mask/redscarf - name = "red scarf" - desc = "A red and white checkered neck scarf." - icon_state = "redwhite_scarf" - item_state = "redwhite_scarf" - flags_cover = MASKCOVERSMOUTH - w_class = WEIGHT_CLASS_SMALL - gas_transfer_coefficient = 0.90 - -/obj/item/clothing/mask/greenscarf - name = "green scarf" - desc = "A green neck scarf." - icon_state = "green_scarf" - item_state = "green_scarf" - flags_cover = MASKCOVERSMOUTH - w_class = WEIGHT_CLASS_SMALL - gas_transfer_coefficient = 0.90 - -/obj/item/clothing/mask/ninjascarf - name = "ninja scarf" - desc = "A stealthy, dark scarf." - icon_state = "ninja_scarf" - item_state = "ninja_scarf" - flags_cover = MASKCOVERSMOUTH - w_class = WEIGHT_CLASS_SMALL - gas_transfer_coefficient = 0.90 - - -/obj/item/clothing/mask/pig - name = "pig mask" - desc = "A rubber pig mask." - icon_state = "pig" - item_state = "pig" - flags = BLOCKHAIR - flags_inv = HIDEFACE - w_class = WEIGHT_CLASS_SMALL - - -/obj/item/clothing/mask/horsehead - name = "horse head mask" - desc = "A mask made of soft vinyl and latex, representing the head of a horse." - icon_state = "horsehead" - item_state = "horsehead" - flags = BLOCKHAIR - flags_inv = HIDEFACE - w_class = WEIGHT_CLASS_SMALL - var/voicechange = 0 - var/temporaryname = " the Horse" - var/originalname = "" - - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi' - ) - -/obj/item/clothing/mask/horsehead/equipped(mob/user, slot) - if(flags & NODROP) //cursed masks only - originalname = user.real_name - if(!user.real_name || user.real_name == "Unknown") - user.real_name = "A Horse With No Name" //it felt good to be out of the rain - else - user.real_name = "[user.name][temporaryname]" - ..() - -/obj/item/clothing/mask/horsehead/dropped() //this really shouldn't happen, but call it extreme caution - if(flags & NODROP) - goodbye_horses(loc) - ..() - -/obj/item/clothing/mask/horsehead/Destroy() - if(flags & NODROP) - goodbye_horses(loc) - return ..() - -/obj/item/clothing/mask/horsehead/proc/goodbye_horses(mob/user) //I'm flying over you - if(!ismob(user)) - return - if(user.real_name == "[originalname][temporaryname]" || user.real_name == "A Horse With No Name") //if it's somehow changed while the mask is on it doesn't revert - user.real_name = originalname - -/obj/item/clothing/mask/face - flags_inv = HIDEFACE - flags_cover = MASKCOVERSMOUTH - -/obj/item/clothing/mask/face/rat - name = "rat mask" - desc = "A mask made of soft vinyl and latex, representing the head of a rat." - icon_state = "rat" - item_state = "rat" - -/obj/item/clothing/mask/face/fox - name = "fox mask" - desc = "A mask made of soft vinyl and latex, representing the head of a fox." - icon_state = "fox" - item_state = "fox" - -/obj/item/clothing/mask/face/bee - name = "bee mask" - desc = "A mask made of soft vinyl and latex, representing the head of a bee." - icon_state = "bee" - item_state = "bee" - -/obj/item/clothing/mask/face/bear - name = "bear mask" - desc = "A mask made of soft vinyl and latex, representing the head of a bear." - icon_state = "bear" - item_state = "bear" - -/obj/item/clothing/mask/face/bat - name = "bat mask" - desc = "A mask made of soft vinyl and latex, representing the head of a bat." - icon_state = "bat" - item_state = "bat" - -/obj/item/clothing/mask/face/raven - name = "raven mask" - desc = "A mask made of soft vinyl and latex, representing the head of a raven." - icon_state = "raven" - item_state = "raven" - -/obj/item/clothing/mask/face/jackal - name = "jackal mask" - desc = "A mask made of soft vinyl and latex, representing the head of a jackal." - icon_state = "jackal" - item_state = "jackal" - -/obj/item/clothing/mask/face/tribal - name = "tribal mask" - desc = "A mask carved out of wood, detailed carefully by hand." - icon_state = "bumba" - item_state = "bumba" - -/obj/item/clothing/mask/fawkes - name = "Guy Fawkes mask" - desc = "A mask designed to help you remember a specific date." - icon_state = "fawkes" - item_state = "fawkes" - flags_inv = HIDEFACE - w_class = WEIGHT_CLASS_SMALL - -/obj/item/clothing/mask/gas/clown_hat/pennywise - name = "Pennywise Mask" - desc = "It's the eater of worlds, and of children." - icon_state = "pennywise_mask" - item_state = "pennywise_mask" - - flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | BLOCKHAIR - -// Bandanas -/obj/item/clothing/mask/bandana - name = "bandana" - desc = "A colorful bandana." - flags_inv = HIDEFACE - flags_cover = MASKCOVERSMOUTH - w_class = WEIGHT_CLASS_TINY - slot_flags = SLOT_MASK - adjusted_flags = SLOT_HEAD - icon_state = "bandbotany" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/mask.dmi', - "Unathi" = 'icons/mob/species/unathi/mask.dmi', - "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', - "Grey" = 'icons/mob/species/grey/mask.dmi', - "Drask" = 'icons/mob/species/drask/mask.dmi' - ) - actions_types = list(/datum/action/item_action/adjust) - -/obj/item/clothing/mask/bandana/attack_self(var/mob/user) - adjustmask(user) - -/obj/item/clothing/mask/bandana/red - name = "red bandana" - icon_state = "bandred" - item_color = "red" - desc = "It's a red bandana." - -/obj/item/clothing/mask/bandana/blue - name = "blue bandana" - icon_state = "bandblue" - item_color = "blue" - desc = "It's a blue bandana." - -/obj/item/clothing/mask/bandana/gold - name = "gold bandana" - icon_state = "bandgold" - item_color = "yellow" - desc = "It's a gold bandana." - -/obj/item/clothing/mask/bandana/green - name = "green bandana" - icon_state = "bandgreen" - item_color = "green" - desc = "It's a green bandana." - -/obj/item/clothing/mask/bandana/orange - name = "orange bandana" - icon_state = "bandorange" - item_color = "orange" - desc = "It's an orange bandana." - -/obj/item/clothing/mask/bandana/purple - name = "purple bandana" - icon_state = "bandpurple" - item_color = "purple" - desc = "It's a purple bandana." - -/obj/item/clothing/mask/bandana/botany - name = "botany bandana" - desc = "It's a green bandana with some fine nanotech lining." - icon_state = "bandbotany" - -/obj/item/clothing/mask/bandana/skull - name = "skull bandana" - desc = "It's a black bandana with a skull pattern." - icon_state = "bandskull" - -/obj/item/clothing/mask/bandana/black - name = "black bandana" - icon_state = "bandblack" - item_color = "black" - desc = "It's a black bandana." - -/obj/item/clothing/mask/bandana/durathread - name = "durathread bandana" - desc = "A bandana made from durathread, you wish it would provide some protection to its wearer, but it's far too thin..." - icon_state = "banddurathread" - -/obj/item/clothing/mask/cursedclown - name = "cursed clown mask" - desc = "This is a very, very odd looking mask." - icon = 'icons/goonstation/objects/clothing/mask.dmi' - icon_state = "cursedclown" - item_state = "cclown_hat" - resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF - icon_override = 'icons/goonstation/mob/clothing/mask.dmi' - lefthand_file = 'icons/goonstation/mob/inhands/clothing_lefthand.dmi' - righthand_file = 'icons/goonstation/mob/inhands/clothing_righthand.dmi' - flags = NODROP | AIRTIGHT - flags_cover = MASKCOVERSMOUTH - -/obj/item/clothing/mask/cursedclown/equipped(mob/user, slot) - ..() - var/mob/living/carbon/human/H = user - if(istype(H) && slot == slot_wear_mask) - to_chat(H, "[src] grips your face!") - if(H.mind && H.mind.assigned_role != "Cluwne") - H.makeCluwne() - -/obj/item/clothing/mask/cursedclown/suicide_act(mob/user) - user.visible_message("[user] gazes into the eyes of [src]. [src] gazes back!") - spawn(10) - if(user) - user.gib() - return OBLITERATION +/obj/item/clothing/mask/muzzle + name = "muzzle" + desc = "To stop that awful noise." + icon_state = "muzzle" + item_state = "muzzle" + flags_cover = MASKCOVERSMOUTH + w_class = WEIGHT_CLASS_SMALL + gas_transfer_coefficient = 0.90 + put_on_delay = 20 + var/resist_time = 0 //deciseconds of how long you need to gnaw to get rid of the gag, 0 to make it impossible to remove + var/mute = MUZZLE_MUTE_ALL + var/security_lock = FALSE // Requires brig access to remove 0 - Remove as normal + var/locked = FALSE //Indicates if a mask is locked, should always start as 0. + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi' + ) + +// Clumsy folks can't take the mask off themselves. +/obj/item/clothing/mask/muzzle/attack_hand(mob/user as mob) + if(user.wear_mask == src && !user.IsAdvancedToolUser()) + return 0 + else if(security_lock && locked) + if(do_unlock(user)) + visible_message("[user] unlocks [user.p_their()] [src.name].", \ + "[user] unlocks [user.p_their()] [src.name].") + ..() + return 1 + +/obj/item/clothing/mask/muzzle/proc/do_break() + if(security_lock) + security_lock = FALSE + locked = FALSE + flags &= ~NODROP + desc += " This one appears to be broken." + return TRUE + else + return FALSE + +/obj/item/clothing/mask/muzzle/proc/do_unlock(mob/living/carbon/human/user) + if(istype(user.get_inactive_hand(), /obj/item/card/emag)) + to_chat(user, "The lock vibrates as the card forces its locking system open.") + do_break() + return TRUE + else if(ACCESS_BRIG in user.get_access()) + to_chat(user, "The muzzle unlocks with a click.") + locked = FALSE + flags &= ~NODROP + return TRUE + + to_chat(user, "You must be wearing a security ID card or have one in your inactive hand to remove the muzzle.") + return FALSE + +/obj/item/clothing/mask/muzzle/proc/do_lock(mob/living/carbon/human/user) + if(security_lock) + locked = TRUE + flags |= NODROP + return TRUE + return FALSE + +/obj/item/clothing/mask/muzzle/Topic(href, href_list) + ..() + if(href_list["locked"]) + var/mob/living/carbon/wearer = locate(href_list["locked"]) + var/success = 0 + if(ishuman(usr)) + visible_message("[usr] tries to [locked ? "unlock" : "lock"] [wearer]'s [name].", \ + "[usr] tries to [locked ? "unlock" : "lock"] [wearer]'s [name].") + if(do_mob(usr, wearer, POCKET_STRIP_DELAY)) + if(locked) + success = do_unlock(usr) + else + success = do_lock(usr) + if(success) + visible_message("[usr] [locked ? "locks" : "unlocks"] [wearer]'s [name].", \ + "[usr] [locked ? "locks" : "unlocks"] [wearer]'s [name].") + if(usr.machine == wearer && in_range(src, usr)) + wearer.show_inv(usr) + else + to_chat(usr, "You lack the ability to manipulate the lock.") + + +/obj/item/clothing/mask/muzzle/tapegag + name = "tape gag" + desc = "MHPMHHH!" + icon_state = "tapegag" + item_state = null + w_class = WEIGHT_CLASS_TINY + resist_time = 150 + mute = MUZZLE_MUTE_MUFFLE + flags = DROPDEL + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi' + ) + +/obj/item/clothing/mask/muzzle/tapegag/dropped(mob/user) + var/obj/item/trash/tapetrash/TT = new + transfer_fingerprints_to(TT) + user.transfer_fingerprints_to(TT) + user.put_in_active_hand(TT) + playsound(src, 'sound/items/poster_ripped.ogg', 40, 1) + user.emote("scream") + ..() + +/obj/item/clothing/mask/muzzle/safety + name = "safety muzzle" + desc = "A muzzle designed to prevent biting." + icon_state = "muzzle_secure" + item_state = "muzzle_secure" + resist_time = 0 + mute = MUZZLE_MUTE_NONE + security_lock = TRUE + locked = FALSE + materials = list(MAT_METAL=500, MAT_GLASS=50) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi' + ) + +/obj/item/clothing/mask/muzzle/safety/shock + name = "shock muzzle" + desc = "A muzzle designed to prevent biting. This one is fitted with a behavior correction system." + var/obj/item/assembly/trigger = null + origin_tech = "materials=1;engineering=1" + materials = list(MAT_METAL=500, MAT_GLASS=50) + +/obj/item/clothing/mask/muzzle/safety/shock/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/assembly/signaler) || istype(W, /obj/item/assembly/voice)) + if(istype(trigger, /obj/item/assembly/signaler) || istype(trigger, /obj/item/assembly/voice)) + to_chat(user, "Something is already attached to [src].") + return FALSE + if(!user.drop_item()) + to_chat(user, "You are unable to insert [W] into [src].") + return FALSE + trigger = W + trigger.forceMove(src) + trigger.master = src + trigger.holder = src + to_chat(user, "You attach the [W] to [src].") + return TRUE + else if(istype(W, /obj/item/assembly)) + to_chat(user, "That won't fit in [src]. Perhaps a signaler or voice analyzer would?") + return FALSE + + return ..() + +/obj/item/clothing/mask/muzzle/safety/shock/screwdriver_act(mob/user, obj/item/I) + if(!trigger) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + to_chat(user, "You remove [trigger] from [src].") + trigger.forceMove(get_turf(user)) + trigger.master = null + trigger.holder = null + trigger = null + +/obj/item/clothing/mask/muzzle/safety/shock/proc/can_shock(obj/item/clothing/C) + if(istype(C)) + if(isliving(C.loc)) + return C.loc + else if(isliving(loc)) + return loc + return FALSE + +/obj/item/clothing/mask/muzzle/safety/shock/proc/process_activation(var/obj/D, var/normal = 1, var/special = 1) + visible_message("[bicon(src)] *beep* *beep*", "*beep* *beep*") + var/mob/M = can_shock(loc) + if(M) + to_chat(M, "You feel a sharp shock!") + do_sparks(3, 1, M) + + M.Weaken(5) + M.Stuttering(1) + M.Jitter(20) + return + +/obj/item/clothing/mask/muzzle/safety/shock/HasProximity(atom/movable/AM as mob|obj) + if(trigger) + trigger.HasProximity(AM) + + +/obj/item/clothing/mask/muzzle/safety/shock/hear_talk(mob/living/M as mob, list/message_pieces) + if(trigger) + trigger.hear_talk(M, message_pieces) + +/obj/item/clothing/mask/muzzle/safety/shock/hear_message(mob/living/M as mob, msg) + if(trigger) + trigger.hear_message(M, msg) + + + +/obj/item/clothing/mask/surgical + name = "sterile mask" + desc = "A sterile mask designed to help prevent the spread of diseases." + icon_state = "sterile" + item_state = "sterile" + w_class = WEIGHT_CLASS_TINY + flags_cover = MASKCOVERSMOUTH + gas_transfer_coefficient = 0.90 + permeability_coefficient = 0.01 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 25, "rad" = 0, "fire" = 0, "acid" = 0) + actions_types = list(/datum/action/item_action/adjust) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi' + ) + + +/obj/item/clothing/mask/surgical/attack_self(var/mob/user) + adjustmask(user) + +/obj/item/clothing/mask/fakemoustache + name = "completely real moustache" + desc = "moustache is totally real." + icon_state = "fake-moustache" + flags_inv = HIDEFACE + actions_types = list(/datum/action/item_action/pontificate) + dog_fashion = /datum/dog_fashion/head/not_ian + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi' + ) + +/obj/item/clothing/mask/fakemoustache/attack_self(mob/user) + pontificate(user) + +/obj/item/clothing/mask/fakemoustache/item_action_slot_check(slot) + if(slot == slot_wear_mask) + return 1 + +/obj/item/clothing/mask/fakemoustache/proc/pontificate(mob/user) + user.visible_message("\ [user] twirls [user.p_their()] moustache and laughs [pick("fiendishly","maniacally","diabolically","evilly")]!") + +//scarves (fit in in mask slot) + +/obj/item/clothing/mask/bluescarf + name = "blue neck scarf" + desc = "A blue neck scarf." + icon_state = "blueneckscarf" + item_state = "blueneckscarf" + flags_cover = MASKCOVERSMOUTH + w_class = WEIGHT_CLASS_SMALL + gas_transfer_coefficient = 0.90 + + +/obj/item/clothing/mask/redscarf + name = "red scarf" + desc = "A red and white checkered neck scarf." + icon_state = "redwhite_scarf" + item_state = "redwhite_scarf" + flags_cover = MASKCOVERSMOUTH + w_class = WEIGHT_CLASS_SMALL + gas_transfer_coefficient = 0.90 + +/obj/item/clothing/mask/greenscarf + name = "green scarf" + desc = "A green neck scarf." + icon_state = "green_scarf" + item_state = "green_scarf" + flags_cover = MASKCOVERSMOUTH + w_class = WEIGHT_CLASS_SMALL + gas_transfer_coefficient = 0.90 + +/obj/item/clothing/mask/ninjascarf + name = "ninja scarf" + desc = "A stealthy, dark scarf." + icon_state = "ninja_scarf" + item_state = "ninja_scarf" + flags_cover = MASKCOVERSMOUTH + w_class = WEIGHT_CLASS_SMALL + gas_transfer_coefficient = 0.90 + + +/obj/item/clothing/mask/pig + name = "pig mask" + desc = "A rubber pig mask." + icon_state = "pig" + item_state = "pig" + flags = BLOCKHAIR + flags_inv = HIDEFACE + w_class = WEIGHT_CLASS_SMALL + + +/obj/item/clothing/mask/horsehead + name = "horse head mask" + desc = "A mask made of soft vinyl and latex, representing the head of a horse." + icon_state = "horsehead" + item_state = "horsehead" + flags = BLOCKHAIR + flags_inv = HIDEFACE + w_class = WEIGHT_CLASS_SMALL + var/voicechange = 0 + var/temporaryname = " the Horse" + var/originalname = "" + + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi' + ) + +/obj/item/clothing/mask/horsehead/equipped(mob/user, slot) + if(flags & NODROP) //cursed masks only + originalname = user.real_name + if(!user.real_name || user.real_name == "Unknown") + user.real_name = "A Horse With No Name" //it felt good to be out of the rain + else + user.real_name = "[user.name][temporaryname]" + ..() + +/obj/item/clothing/mask/horsehead/dropped() //this really shouldn't happen, but call it extreme caution + if(flags & NODROP) + goodbye_horses(loc) + ..() + +/obj/item/clothing/mask/horsehead/Destroy() + if(flags & NODROP) + goodbye_horses(loc) + return ..() + +/obj/item/clothing/mask/horsehead/proc/goodbye_horses(mob/user) //I'm flying over you + if(!ismob(user)) + return + if(user.real_name == "[originalname][temporaryname]" || user.real_name == "A Horse With No Name") //if it's somehow changed while the mask is on it doesn't revert + user.real_name = originalname + +/obj/item/clothing/mask/face + flags_inv = HIDEFACE + flags_cover = MASKCOVERSMOUTH + +/obj/item/clothing/mask/face/rat + name = "rat mask" + desc = "A mask made of soft vinyl and latex, representing the head of a rat." + icon_state = "rat" + item_state = "rat" + +/obj/item/clothing/mask/face/fox + name = "fox mask" + desc = "A mask made of soft vinyl and latex, representing the head of a fox." + icon_state = "fox" + item_state = "fox" + +/obj/item/clothing/mask/face/bee + name = "bee mask" + desc = "A mask made of soft vinyl and latex, representing the head of a bee." + icon_state = "bee" + item_state = "bee" + +/obj/item/clothing/mask/face/bear + name = "bear mask" + desc = "A mask made of soft vinyl and latex, representing the head of a bear." + icon_state = "bear" + item_state = "bear" + +/obj/item/clothing/mask/face/bat + name = "bat mask" + desc = "A mask made of soft vinyl and latex, representing the head of a bat." + icon_state = "bat" + item_state = "bat" + +/obj/item/clothing/mask/face/raven + name = "raven mask" + desc = "A mask made of soft vinyl and latex, representing the head of a raven." + icon_state = "raven" + item_state = "raven" + +/obj/item/clothing/mask/face/jackal + name = "jackal mask" + desc = "A mask made of soft vinyl and latex, representing the head of a jackal." + icon_state = "jackal" + item_state = "jackal" + +/obj/item/clothing/mask/face/tribal + name = "tribal mask" + desc = "A mask carved out of wood, detailed carefully by hand." + icon_state = "bumba" + item_state = "bumba" + +/obj/item/clothing/mask/fawkes + name = "Guy Fawkes mask" + desc = "A mask designed to help you remember a specific date." + icon_state = "fawkes" + item_state = "fawkes" + flags_inv = HIDEFACE + w_class = WEIGHT_CLASS_SMALL + +/obj/item/clothing/mask/gas/clown_hat/pennywise + name = "Pennywise Mask" + desc = "It's the eater of worlds, and of children." + icon_state = "pennywise_mask" + item_state = "pennywise_mask" + + flags = BLOCK_GAS_SMOKE_EFFECT | AIRTIGHT | BLOCKHAIR + +// Bandanas +/obj/item/clothing/mask/bandana + name = "bandana" + desc = "A colorful bandana." + flags_inv = HIDEFACE + flags_cover = MASKCOVERSMOUTH + w_class = WEIGHT_CLASS_TINY + slot_flags = SLOT_MASK + adjusted_flags = SLOT_HEAD + icon_state = "bandbotany" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/mask.dmi', + "Unathi" = 'icons/mob/species/unathi/mask.dmi', + "Tajaran" = 'icons/mob/species/tajaran/mask.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/mask.dmi', + "Grey" = 'icons/mob/species/grey/mask.dmi', + "Drask" = 'icons/mob/species/drask/mask.dmi' + ) + actions_types = list(/datum/action/item_action/adjust) + +/obj/item/clothing/mask/bandana/attack_self(var/mob/user) + adjustmask(user) + +/obj/item/clothing/mask/bandana/red + name = "red bandana" + icon_state = "bandred" + item_color = "red" + desc = "It's a red bandana." + +/obj/item/clothing/mask/bandana/blue + name = "blue bandana" + icon_state = "bandblue" + item_color = "blue" + desc = "It's a blue bandana." + +/obj/item/clothing/mask/bandana/gold + name = "gold bandana" + icon_state = "bandgold" + item_color = "yellow" + desc = "It's a gold bandana." + +/obj/item/clothing/mask/bandana/green + name = "green bandana" + icon_state = "bandgreen" + item_color = "green" + desc = "It's a green bandana." + +/obj/item/clothing/mask/bandana/orange + name = "orange bandana" + icon_state = "bandorange" + item_color = "orange" + desc = "It's an orange bandana." + +/obj/item/clothing/mask/bandana/purple + name = "purple bandana" + icon_state = "bandpurple" + item_color = "purple" + desc = "It's a purple bandana." + +/obj/item/clothing/mask/bandana/botany + name = "botany bandana" + desc = "It's a green bandana with some fine nanotech lining." + icon_state = "bandbotany" + +/obj/item/clothing/mask/bandana/skull + name = "skull bandana" + desc = "It's a black bandana with a skull pattern." + icon_state = "bandskull" + +/obj/item/clothing/mask/bandana/black + name = "black bandana" + icon_state = "bandblack" + item_color = "black" + desc = "It's a black bandana." + +/obj/item/clothing/mask/bandana/durathread + name = "durathread bandana" + desc = "A bandana made from durathread, you wish it would provide some protection to its wearer, but it's far too thin..." + icon_state = "banddurathread" + +/obj/item/clothing/mask/cursedclown + name = "cursed clown mask" + desc = "This is a very, very odd looking mask." + icon = 'icons/goonstation/objects/clothing/mask.dmi' + icon_state = "cursedclown" + item_state = "cclown_hat" + resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF + icon_override = 'icons/goonstation/mob/clothing/mask.dmi' + lefthand_file = 'icons/goonstation/mob/inhands/clothing_lefthand.dmi' + righthand_file = 'icons/goonstation/mob/inhands/clothing_righthand.dmi' + flags = NODROP | AIRTIGHT + flags_cover = MASKCOVERSMOUTH + +/obj/item/clothing/mask/cursedclown/equipped(mob/user, slot) + ..() + var/mob/living/carbon/human/H = user + if(istype(H) && slot == slot_wear_mask) + to_chat(H, "[src] grips your face!") + if(H.mind && H.mind.assigned_role != "Cluwne") + H.makeCluwne() + +/obj/item/clothing/mask/cursedclown/suicide_act(mob/user) + user.visible_message("[user] gazes into the eyes of [src]. [src] gazes back!") + spawn(10) + if(user) + user.gib() + return OBLITERATION diff --git a/code/modules/clothing/patreon/glasses.dm b/code/modules/clothing/patreon/glasses.dm index f52d54f7df2..08d5d6b0af8 100644 --- a/code/modules/clothing/patreon/glasses.dm +++ b/code/modules/clothing/patreon/glasses.dm @@ -18,4 +18,4 @@ name = "Spiky Green-tinted Shades" desc = "Fight the power!" icon_state = "garm" - item_state = "garm" \ No newline at end of file + item_state = "garm" diff --git a/code/modules/clothing/patreon/hats.dm b/code/modules/clothing/patreon/hats.dm index abee0cb9466..8b769ba4150 100644 --- a/code/modules/clothing/patreon/hats.dm +++ b/code/modules/clothing/patreon/hats.dm @@ -60,4 +60,4 @@ sprite_sheets = list( "Vox" = 'icons/mob/species/vox/head.dmi' - ) \ No newline at end of file + ) diff --git a/code/modules/clothing/shoes/colour.dm b/code/modules/clothing/shoes/colour.dm index f864d3bf8f6..0bf54e8692f 100644 --- a/code/modules/clothing/shoes/colour.dm +++ b/code/modules/clothing/shoes/colour.dm @@ -1,106 +1,106 @@ -/obj/item/clothing/shoes/black - name = "black shoes" - icon_state = "black" - item_color = "black" - desc = "A pair of black shoes." - - cold_protection = FEET - min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT - heat_protection = FEET - max_heat_protection_temperature = SHOES_MAX_TEMP_PROTECT - - redcoat - item_color = "redcoat" //Exists for washing machines. Is not different from black shoes in any way. - -/obj/item/clothing/shoes/black/greytide - flags = NODROP - -/obj/item/clothing/shoes/brown - name = "brown shoes" - desc = "A pair of brown shoes." - icon_state = "brown" - - captain - item_color = "captain" //Exists for washing machines. Is not different from brown shoes in any way. - hop - item_color = "hop" //Exists for washing machines. Is not different from brown shoes in any way. - ce - item_color = "chief" //Exists for washing machines. Is not different from brown shoes in any way. - rd - item_color = "director" //Exists for washing machines. Is not different from brown shoes in any way. - cmo - item_color = "medical" //Exists for washing machines. Is not different from brown shoes in any way. - cmo - item_color = "cargo" //Exists for washing machines. Is not different from brown shoes in any way. - -/obj/item/clothing/shoes/blue - name = "blue shoes" - icon_state = "blue" - item_color = "blue" - -/obj/item/clothing/shoes/green - name = "green shoes" - icon_state = "green" - item_color = "green" - -/obj/item/clothing/shoes/yellow - name = "yellow shoes" - icon_state = "yellow" - item_color = "yellow" - -/obj/item/clothing/shoes/purple - name = "purple shoes" - icon_state = "purple" - item_color = "purple" - -/obj/item/clothing/shoes/brown - name = "brown shoes" - icon_state = "brown" - item_color = "brown" - -/obj/item/clothing/shoes/red - name = "red shoes" - desc = "Stylish red shoes." - icon_state = "red" - item_color = "red" - -/obj/item/clothing/shoes/white - name = "white shoes" - icon_state = "white" - permeability_coefficient = 0.01 - item_color = "white" - -/obj/item/clothing/shoes/leather - name = "leather shoes" - desc = "A sturdy pair of leather shoes." - icon_state = "leather" - item_color = "leather" - -/obj/item/clothing/shoes/rainbow - name = "rainbow shoes" - desc = "Very gay shoes." - icon_state = "rain_bow" - item_color = "rainbow" - -/obj/item/clothing/shoes/orange - name = "orange shoes" - icon_state = "orange" - item_color = "orange" - -/obj/item/clothing/shoes/orange/attack_self(mob/user as mob) - if(src.chained) - src.chained = null - src.slowdown = SHOES_SLOWDOWN - new /obj/item/restraints/handcuffs( user.loc ) - src.icon_state = "orange" - return - -/obj/item/clothing/shoes/orange/attackby(obj/H, loc, params) - ..() - if(istype(H, /obj/item/restraints/handcuffs) && !chained && !(H.flags & NODROP)) - if(src.icon_state != "orange") return - qdel(H) - src.chained = 1 - src.slowdown = 15 - src.icon_state = "orange1" - return +/obj/item/clothing/shoes/black + name = "black shoes" + icon_state = "black" + item_color = "black" + desc = "A pair of black shoes." + + cold_protection = FEET + min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT + heat_protection = FEET + max_heat_protection_temperature = SHOES_MAX_TEMP_PROTECT + + redcoat + item_color = "redcoat" //Exists for washing machines. Is not different from black shoes in any way. + +/obj/item/clothing/shoes/black/greytide + flags = NODROP + +/obj/item/clothing/shoes/brown + name = "brown shoes" + desc = "A pair of brown shoes." + icon_state = "brown" + + captain + item_color = "captain" //Exists for washing machines. Is not different from brown shoes in any way. + hop + item_color = "hop" //Exists for washing machines. Is not different from brown shoes in any way. + ce + item_color = "chief" //Exists for washing machines. Is not different from brown shoes in any way. + rd + item_color = "director" //Exists for washing machines. Is not different from brown shoes in any way. + cmo + item_color = "medical" //Exists for washing machines. Is not different from brown shoes in any way. + cmo + item_color = "cargo" //Exists for washing machines. Is not different from brown shoes in any way. + +/obj/item/clothing/shoes/blue + name = "blue shoes" + icon_state = "blue" + item_color = "blue" + +/obj/item/clothing/shoes/green + name = "green shoes" + icon_state = "green" + item_color = "green" + +/obj/item/clothing/shoes/yellow + name = "yellow shoes" + icon_state = "yellow" + item_color = "yellow" + +/obj/item/clothing/shoes/purple + name = "purple shoes" + icon_state = "purple" + item_color = "purple" + +/obj/item/clothing/shoes/brown + name = "brown shoes" + icon_state = "brown" + item_color = "brown" + +/obj/item/clothing/shoes/red + name = "red shoes" + desc = "Stylish red shoes." + icon_state = "red" + item_color = "red" + +/obj/item/clothing/shoes/white + name = "white shoes" + icon_state = "white" + permeability_coefficient = 0.01 + item_color = "white" + +/obj/item/clothing/shoes/leather + name = "leather shoes" + desc = "A sturdy pair of leather shoes." + icon_state = "leather" + item_color = "leather" + +/obj/item/clothing/shoes/rainbow + name = "rainbow shoes" + desc = "Very gay shoes." + icon_state = "rain_bow" + item_color = "rainbow" + +/obj/item/clothing/shoes/orange + name = "orange shoes" + icon_state = "orange" + item_color = "orange" + +/obj/item/clothing/shoes/orange/attack_self(mob/user as mob) + if(src.chained) + src.chained = null + src.slowdown = SHOES_SLOWDOWN + new /obj/item/restraints/handcuffs( user.loc ) + src.icon_state = "orange" + return + +/obj/item/clothing/shoes/orange/attackby(obj/H, loc, params) + ..() + if(istype(H, /obj/item/restraints/handcuffs) && !chained && !(H.flags & NODROP)) + if(src.icon_state != "orange") return + qdel(H) + src.chained = 1 + src.slowdown = 15 + src.icon_state = "orange1" + return diff --git a/code/modules/clothing/shoes/magboots.dm b/code/modules/clothing/shoes/magboots.dm index 7b0baef536f..916cb8e89d4 100644 --- a/code/modules/clothing/shoes/magboots.dm +++ b/code/modules/clothing/shoes/magboots.dm @@ -1,118 +1,118 @@ -/obj/item/clothing/shoes/magboots - desc = "Magnetic boots, often used during extravehicular activity to ensure the user remains safely attached to the vehicle." - name = "magboots" - icon_state = "magboots0" - origin_tech = "materials=3;magnets=4;engineering=4" - var/magboot_state = "magboots" - var/magpulse = 0 - var/slowdown_active = 2 - var/slowdown_passive = SHOES_SLOWDOWN - var/magpulse_name = "mag-pulse traction system" - actions_types = list(/datum/action/item_action/toggle) - strip_delay = 70 - put_on_delay = 70 - resistance_flags = FIRE_PROOF - -/obj/item/clothing/shoes/magboots/attack_self(mob/user) - if(magpulse) - flags &= ~NOSLIP - slowdown = slowdown_passive - else - flags |= NOSLIP - slowdown = slowdown_active - magpulse = !magpulse - icon_state = "[magboot_state][magpulse]" - to_chat(user, "You [magpulse ? "enable" : "disable"] the [magpulse_name].") - user.update_inv_shoes() //so our mob-overlays update - user.update_gravity(user.mob_has_gravity()) - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - -/obj/item/clothing/shoes/magboots/negates_gravity() - return flags & NOSLIP - -/obj/item/clothing/shoes/magboots/examine(mob/user) - . = ..() - . += "Its [magpulse_name] appears to be [magpulse ? "enabled" : "disabled"]." - - -/obj/item/clothing/shoes/magboots/advance - desc = "Advanced magnetic boots that have a lighter magnetic pull, placing less burden on the wearer." - name = "advanced magboots" - icon_state = "advmag0" - magboot_state = "advmag" - slowdown_active = SHOES_SLOWDOWN - origin_tech = null - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF - -/obj/item/clothing/shoes/magboots/syndie - desc = "Reverse-engineered magnetic boots that have a heavy magnetic pull. Property of Gorlex Marauders." - name = "blood-red magboots" - icon_state = "syndiemag0" - magboot_state = "syndiemag" - origin_tech = "magnets=4;syndicate=2" - -obj/item/clothing/shoes/magboots/syndie/advance //For the Syndicate Strike Team - desc = "Reverse-engineered magboots that appear to be based on an advanced model, as they have a lighter magnetic pull. Property of Gorlex Marauders." - name = "advanced blood-red magboots" - slowdown_active = SHOES_SLOWDOWN - -/obj/item/clothing/shoes/magboots/clown - desc = "The prankster's standard-issue clowning shoes. Damn they're huge! There's a red light on the side." - name = "clown shoes" - icon_state = "clownmag0" - magboot_state = "clownmag" - item_state = "clown_shoes" - slowdown = SHOES_SLOWDOWN+1 - slowdown_active = SHOES_SLOWDOWN+1 - slowdown_passive = SHOES_SLOWDOWN+1 - magpulse_name = "honk-powered traction system" - item_color = "clown" - silence_steps = 1 - shoe_sound = "clownstep" - origin_tech = "magnets=4;syndicate=2" - var/enabled_waddle = TRUE - var/datum/component/waddle - -/obj/item/clothing/shoes/magboots/clown/equipped(mob/user, slot) - . = ..() - if(slot == slot_shoes && enabled_waddle) - waddle = user.AddComponent(/datum/component/waddling) - -/obj/item/clothing/shoes/magboots/clown/dropped(mob/user) - . = ..() - QDEL_NULL(waddle) - -/obj/item/clothing/shoes/magboots/clown/CtrlClick(mob/living/user) - if(!isliving(user)) - return - if(user.get_active_hand() != src) - to_chat(user, "You must hold [src] in your hand to do this.") - return - if(!enabled_waddle) - to_chat(user, "You switch off the waddle dampeners!") - enabled_waddle = TRUE - else - to_chat(user, "You switch on the waddle dampeners!") - enabled_waddle = FALSE - -/obj/item/clothing/shoes/magboots/wizard //bundled with the wiz hardsuit - name = "boots of gripping" - desc = "These magical boots, once activated, will stay gripped to any surface without slowing you down." - icon_state = "wizmag0" - magboot_state = "wizmag" - slowdown_active = SHOES_SLOWDOWN //wiz hardsuit already slows you down, no need to double it - magpulse_name = "gripping ability" - magical = TRUE - -/obj/item/clothing/shoes/magboots/wizard/attack_self(mob/user) - if(user) - if(user.mind in SSticker.mode.wizards) - if(magpulse) //faint blue light when shoes are turned on gives a reason to turn them off when not needed in maint - set_light(0) - else - set_light(2, 1, LIGHT_COLOR_LIGHTBLUE) - ..() - else - to_chat(user, "You poke the gem on [src]. Nothing happens.") \ No newline at end of file +/obj/item/clothing/shoes/magboots + desc = "Magnetic boots, often used during extravehicular activity to ensure the user remains safely attached to the vehicle." + name = "magboots" + icon_state = "magboots0" + origin_tech = "materials=3;magnets=4;engineering=4" + var/magboot_state = "magboots" + var/magpulse = 0 + var/slowdown_active = 2 + var/slowdown_passive = SHOES_SLOWDOWN + var/magpulse_name = "mag-pulse traction system" + actions_types = list(/datum/action/item_action/toggle) + strip_delay = 70 + put_on_delay = 70 + resistance_flags = FIRE_PROOF + +/obj/item/clothing/shoes/magboots/attack_self(mob/user) + if(magpulse) + flags &= ~NOSLIP + slowdown = slowdown_passive + else + flags |= NOSLIP + slowdown = slowdown_active + magpulse = !magpulse + icon_state = "[magboot_state][magpulse]" + to_chat(user, "You [magpulse ? "enable" : "disable"] the [magpulse_name].") + user.update_inv_shoes() //so our mob-overlays update + user.update_gravity(user.mob_has_gravity()) + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + +/obj/item/clothing/shoes/magboots/negates_gravity() + return flags & NOSLIP + +/obj/item/clothing/shoes/magboots/examine(mob/user) + . = ..() + . += "Its [magpulse_name] appears to be [magpulse ? "enabled" : "disabled"]." + + +/obj/item/clothing/shoes/magboots/advance + desc = "Advanced magnetic boots that have a lighter magnetic pull, placing less burden on the wearer." + name = "advanced magboots" + icon_state = "advmag0" + magboot_state = "advmag" + slowdown_active = SHOES_SLOWDOWN + origin_tech = null + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + +/obj/item/clothing/shoes/magboots/syndie + desc = "Reverse-engineered magnetic boots that have a heavy magnetic pull. Property of Gorlex Marauders." + name = "blood-red magboots" + icon_state = "syndiemag0" + magboot_state = "syndiemag" + origin_tech = "magnets=4;syndicate=2" + +obj/item/clothing/shoes/magboots/syndie/advance //For the Syndicate Strike Team + desc = "Reverse-engineered magboots that appear to be based on an advanced model, as they have a lighter magnetic pull. Property of Gorlex Marauders." + name = "advanced blood-red magboots" + slowdown_active = SHOES_SLOWDOWN + +/obj/item/clothing/shoes/magboots/clown + desc = "The prankster's standard-issue clowning shoes. Damn they're huge! There's a red light on the side." + name = "clown shoes" + icon_state = "clownmag0" + magboot_state = "clownmag" + item_state = "clown_shoes" + slowdown = SHOES_SLOWDOWN+1 + slowdown_active = SHOES_SLOWDOWN+1 + slowdown_passive = SHOES_SLOWDOWN+1 + magpulse_name = "honk-powered traction system" + item_color = "clown" + silence_steps = 1 + shoe_sound = "clownstep" + origin_tech = "magnets=4;syndicate=2" + var/enabled_waddle = TRUE + var/datum/component/waddle + +/obj/item/clothing/shoes/magboots/clown/equipped(mob/user, slot) + . = ..() + if(slot == slot_shoes && enabled_waddle) + waddle = user.AddComponent(/datum/component/waddling) + +/obj/item/clothing/shoes/magboots/clown/dropped(mob/user) + . = ..() + QDEL_NULL(waddle) + +/obj/item/clothing/shoes/magboots/clown/CtrlClick(mob/living/user) + if(!isliving(user)) + return + if(user.get_active_hand() != src) + to_chat(user, "You must hold [src] in your hand to do this.") + return + if(!enabled_waddle) + to_chat(user, "You switch off the waddle dampeners!") + enabled_waddle = TRUE + else + to_chat(user, "You switch on the waddle dampeners!") + enabled_waddle = FALSE + +/obj/item/clothing/shoes/magboots/wizard //bundled with the wiz hardsuit + name = "boots of gripping" + desc = "These magical boots, once activated, will stay gripped to any surface without slowing you down." + icon_state = "wizmag0" + magboot_state = "wizmag" + slowdown_active = SHOES_SLOWDOWN //wiz hardsuit already slows you down, no need to double it + magpulse_name = "gripping ability" + magical = TRUE + +/obj/item/clothing/shoes/magboots/wizard/attack_self(mob/user) + if(user) + if(user.mind in SSticker.mode.wizards) + if(magpulse) //faint blue light when shoes are turned on gives a reason to turn them off when not needed in maint + set_light(0) + else + set_light(2, 1, LIGHT_COLOR_LIGHTBLUE) + ..() + else + to_chat(user, "You poke the gem on [src]. Nothing happens.") diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm index 9c7d8bac080..6858355f876 100644 --- a/code/modules/clothing/shoes/miscellaneous.dm +++ b/code/modules/clothing/shoes/miscellaneous.dm @@ -1,346 +1,346 @@ -/obj/item/clothing/shoes/mime - name = "mime shoes" - icon_state = "mime" - item_color = "mime" - -/obj/item/clothing/shoes/combat //basic syndicate combat boots for nuke ops and mob corpses - name = "combat boots" - desc = "High speed, low drag combat boots." - can_cut_open = 1 - icon_state = "jackboots" - item_state = "jackboots" - armor = list("melee" = 25, "bullet" = 25, "laser" = 25, "energy" = 25, "bomb" = 50, "bio" = 10, "rad" = 0, "fire" = 70, "acid" = 50) - strip_delay = 70 - resistance_flags = NONE - -/obj/item/clothing/shoes/combat/swat //overpowered boots for death squads - name = "\improper SWAT shoes" - desc = "High speed, no drag combat boots." - permeability_coefficient = 0.01 - armor = list("melee" = 40, "bullet" = 30, "laser" = 25, "energy" = 25, "bomb" = 50, "bio" = 30, "rad" = 30, "fire" = 90, "acid" = 50) - flags = NOSLIP - -/obj/item/clothing/shoes/sandal - desc = "A pair of rather plain, wooden sandals." - name = "sandals" - icon_state = "wizard" - strip_delay = 50 - put_on_delay = 50 - magical = TRUE - -/obj/item/clothing/shoes/sandal/marisa - desc = "A pair of magic, black shoes." - name = "magic shoes" - icon_state = "black" - resistance_flags = FIRE_PROOF | ACID_PROOF - -/obj/item/clothing/shoes/sandal/magic - name = "magical sandals" - desc = "A pair of sandals imbued with magic." - resistance_flags = FIRE_PROOF | ACID_PROOF - -/obj/item/clothing/shoes/galoshes - desc = "A pair of yellow rubber boots, designed to prevent slipping on wet surfaces." - name = "galoshes" - icon_state = "galoshes" - permeability_coefficient = 0.05 - flags = NOSLIP - slowdown = SHOES_SLOWDOWN+1 - strip_delay = 50 - put_on_delay = 50 - resistance_flags = NONE - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 40, "acid" = 75) - -/obj/item/clothing/shoes/galoshes/dry - name = "absorbent galoshes" - desc = "A pair of purple rubber boots, designed to prevent slipping on wet surfaces while also drying them." - icon_state = "galoshes_dry" - -/obj/item/clothing/shoes/galoshes/dry/step_action() - var/turf/simulated/t_loc = get_turf(src) - if(istype(t_loc) && t_loc.wet) - t_loc.MakeDry(TURF_WET_WATER) - -/obj/item/clothing/shoes/clown_shoes - desc = "The prankster's standard-issue clowning shoes. Damn they're huge! Ctrl-click to toggle the waddle dampeners!" - name = "clown shoes" - icon_state = "clown" - item_state = "clown_shoes" - slowdown = SHOES_SLOWDOWN+1 - item_color = "clown" - var/footstep = 1 //used for squeeks whilst walking - shoe_sound = "clownstep" - var/enabled_waddle = TRUE - var/datum/component/waddle - -/obj/item/clothing/shoes/clown_shoes/equipped(mob/user, slot) - . = ..() - if(slot == slot_shoes && enabled_waddle) - waddle = user.AddComponent(/datum/component/waddling) - -/obj/item/clothing/shoes/clown_shoes/dropped(mob/user) - . = ..() - QDEL_NULL(waddle) - -/obj/item/clothing/shoes/clown_shoes/CtrlClick(mob/living/user) - if(!isliving(user)) - return - if(user.get_active_hand() != src) - to_chat(user, "You must hold [src] in your hand to do this.") - return - if(!enabled_waddle) - to_chat(user, "You switch off the waddle dampeners!") - enabled_waddle = TRUE - else - to_chat(user, "You switch on the waddle dampeners!") - enabled_waddle = FALSE - -/obj/item/clothing/shoes/clown_shoes/nodrop - flags = NODROP - -/obj/item/clothing/shoes/clown_shoes/magical - name = "magical clown shoes" - desc = "Standard-issue shoes of the wizarding class clown. Damn they're huge! And powerful! Somehow." - magical = TRUE - -/obj/item/clothing/shoes/jackboots - name = "jackboots" - desc = "Nanotrasen-issue Security combat boots for combat scenarios or combat situations. All combat, all the time." - can_cut_open = 1 - icon_state = "jackboots" - item_state = "jackboots" - item_color = "hosred" - strip_delay = 50 - put_on_delay = 50 - resistance_flags = NONE - var/footstep = 1 - shoe_sound = "jackboot" - -/obj/item/clothing/shoes/jackboots/jacksandals - name = "jacksandals" - desc = "Nanotrasen-issue Security combat sandals for combat scenarios. They're jacksandals, however that works." - can_cut_open = 0 - icon_state = "jacksandal" - item_color = "jacksandal" - -/obj/item/clothing/shoes/workboots - name = "work boots" - desc = "Thick-soled boots for industrial work environments." - can_cut_open = 1 - icon_state = "workboots" - -/obj/item/clothing/shoes/workboots/mining - name = "mining boots" - desc = "Steel-toed mining boots for mining in hazardous environments. Very good at keeping toes uncrushed." - icon_state = "explorer" - resistance_flags = FIRE_PROOF - -/obj/item/clothing/shoes/winterboots - name = "winter boots" - desc = "Boots lined with 'synthetic' animal fur." - can_cut_open = 1 - icon_state = "winterboots" - cold_protection = FEET|LEGS - min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT - heat_protection = FEET|LEGS - max_heat_protection_temperature = SHOES_MAX_TEMP_PROTECT - -/obj/item/clothing/shoes/cult - name = "boots" - desc = "A pair of boots worn by the followers of Nar-Sie." - icon_state = "cult" - item_state = "cult" - item_color = "cult" - - cold_protection = FEET - min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT - heat_protection = FEET - max_heat_protection_temperature = SHOES_MAX_TEMP_PROTECT - -/obj/item/clothing/shoes/cyborg - name = "cyborg boots" - desc = "Shoes for a cyborg costume" - icon_state = "boots" - -/obj/item/clothing/shoes/slippers - name = "bunny slippers" - desc = "Fluffy!" - icon_state = "slippers" - item_state = "slippers" - -/obj/item/clothing/shoes/slippers_worn - name = "worn bunny slippers" - desc = "Fluffy..." - icon_state = "slippers_worn" - item_state = "slippers_worn" - -/obj/item/clothing/shoes/laceup - name = "laceup shoes" - desc = "The height of fashion, and they're pre-polished!" - icon_state = "laceups" - put_on_delay = 50 - -/obj/item/clothing/shoes/roman - name = "roman sandals" - desc = "Sandals with buckled leather straps on it." - icon_state = "roman" - item_state = "roman" - strip_delay = 100 - put_on_delay = 100 - -/obj/item/clothing/shoes/centcom - name = "dress shoes" - desc = "They appear impeccably polished." - icon_state = "laceups" - -/obj/item/clothing/shoes/griffin - name = "griffon boots" - desc = "A pair of costume boots fashioned after bird talons." - icon_state = "griffinboots" - item_state = "griffinboots" - - -/obj/item/clothing/shoes/fluff/noble_boot - name = "noble boots" - desc = "The boots are economically designed to balance function and comfort, so that you can step on peasants without having to worry about blisters. The leather also resists unwanted blood stains." - icon_state = "noble_boot" - item_color = "noble_boot" - item_state = "noble_boot" - -/obj/item/clothing/shoes/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/stack/tape_roll) && !silence_steps) - var/obj/item/stack/tape_roll/TR = I - if((!silence_steps || shoe_sound) && TR.use(4)) - silence_steps = TRUE - shoe_sound = null - to_chat(user, "You tape the soles of [src] to silence your footsteps.") - else - return ..() - -/obj/item/clothing/shoes/sandal/white - name = "White Sandals" - desc = "Medical sandals that nerds wear." - icon_state = "medsandal" - item_color = "medsandal" - -/obj/item/clothing/shoes/sandal/fancy - name = "Fancy Sandals" - desc = "FANCY!!." - icon_state = "fancysandal" - item_color = "fancysandal" - -/obj/item/clothing/shoes/cursedclown - name = "cursed clown shoes" - desc = "Moldering clown flip flops. They're neon green for some reason." - icon = 'icons/goonstation/objects/clothing/feet.dmi' - icon_state = "cursedclown" - item_state = "cclown_shoes" - icon_override = 'icons/goonstation/mob/clothing/feet.dmi' - lefthand_file = 'icons/goonstation/mob/inhands/clothing_lefthand.dmi' - righthand_file = 'icons/goonstation/mob/inhands/clothing_righthand.dmi' - resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF - flags = NODROP - shoe_sound = "clownstep" - -/obj/item/clothing/shoes/singery - name = "yellow performer's boots" - desc = "These boots were made for dancing." - icon_state = "ysing" - put_on_delay = 50 - -/obj/item/clothing/shoes/singerb - name = "blue performer's boots" - desc = "These boots were made for dancing." - icon_state = "bsing" - put_on_delay = 50 - -/obj/item/clothing/shoes/cowboy - name = "cowboy boots" - desc = "A pair a' brown boots." - icon_state = "cowboy_brown" - item_color = "cowboy_brown" - -/obj/item/clothing/shoes/cowboy/black - name = "black cowboy boots" - desc = "A pair a' black rustlers' boots" - icon_state = "cowboy_black" - item_color = "cowboy_black" - -/obj/item/clothing/shoes/cowboy/white - name = "white cowboy boots" - desc = "For the rancher in us all." - icon_state = "cowboy_white" - item_color = "cowboy_white" - -/obj/item/clothing/shoes/cowboy/fancy - name = "bilton wrangler boots" - desc = "A pair of authentic haute couture boots from Japanifornia. You doubt they have ever been close to cattle." - icon_state = "cowboy_fancy" - item_color = "cowboy_fancy" - -/obj/item/clothing/shoes/cowboy/pink - name = "pink cowgirl boots" - desc = "For a Rustlin' tustlin' cowgirl." - icon_state = "cowboyboots_pink" - item_color = "cowboyboots_pink" - -/obj/item/clothing/shoes/cowboy/lizard - name = "lizard skin boots" - desc = "You can hear a faint hissing from inside the boots; you hope it is just a mournful ghost." - icon_state = "lizardboots_green" - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 40, "acid" = 0) //lizards like to stay warm - -/obj/item/clothing/shoes/cowboy/lizardmasterwork - name = "\improper Hugs-The-Feet lizard skin boots" - desc = "A pair of masterfully crafted lizard skin boots. Finally a good application for the station's most bothersome inhabitants." - icon_state = "lizardboots_blue" - -/obj/effect/spawner/lootdrop/lizardboots - name = "random lizard boot quality" - desc = "Which ever gets picked, the lizard race loses" - icon = 'icons/obj/clothing/shoes.dmi' - icon_state = "lizardboots_green" - loot = list( - /obj/item/clothing/shoes/cowboy/lizard = 7, - /obj/item/clothing/shoes/cowboy/lizardmasterwork = 1) - -/obj/item/clothing/shoes/footwraps - name = "cloth footwraps" - desc = "A roll of treated canvas used for wrapping claws or paws." - icon_state = "clothwrap" - item_state = "clothwrap" - force = 0 - silence_steps = TRUE - w_class = WEIGHT_CLASS_SMALL - -/obj/item/clothing/shoes/bhop - name = "jump boots" - desc = "A specialized pair of combat boots with a built-in propulsion system for rapid foward movement." - icon_state = "jetboots" - item_state = "jetboots" - item_color = "hosred" - resistance_flags = FIRE_PROOF - actions_types = list(/datum/action/item_action/bhop) - permeability_coefficient = 0.05 - can_cut_open = FALSE - var/jumpdistance = 5 //-1 from to see the actual distance, e.g 4 goes over 3 tiles - var/jumpspeed = 3 - var/recharging_rate = 60 //default 6 seconds between each dash - var/recharging_time = 0 //time until next dash - -/obj/item/clothing/shoes/bhop/ui_action_click(mob/user, action) - if(!isliving(user)) - return - - if(recharging_time > world.time) - to_chat(user, "The boot's internal propulsion needs to recharge still!") - return - - var/atom/target = get_edge_target_turf(user, user.dir) //gets the user's direction - - if (user.throw_at(target, jumpdistance, jumpspeed, spin = FALSE, diagonals_first = TRUE)) - playsound(src, 'sound/effects/stealthoff.ogg', 50, 1, 1) - user.visible_message("[usr] dashes forward into the air!") - recharging_time = world.time + recharging_rate - else - to_chat(user, "Something prevents you from dashing forward!") \ No newline at end of file +/obj/item/clothing/shoes/mime + name = "mime shoes" + icon_state = "mime" + item_color = "mime" + +/obj/item/clothing/shoes/combat //basic syndicate combat boots for nuke ops and mob corpses + name = "combat boots" + desc = "High speed, low drag combat boots." + can_cut_open = 1 + icon_state = "jackboots" + item_state = "jackboots" + armor = list("melee" = 25, "bullet" = 25, "laser" = 25, "energy" = 25, "bomb" = 50, "bio" = 10, "rad" = 0, "fire" = 70, "acid" = 50) + strip_delay = 70 + resistance_flags = NONE + +/obj/item/clothing/shoes/combat/swat //overpowered boots for death squads + name = "\improper SWAT shoes" + desc = "High speed, no drag combat boots." + permeability_coefficient = 0.01 + armor = list("melee" = 40, "bullet" = 30, "laser" = 25, "energy" = 25, "bomb" = 50, "bio" = 30, "rad" = 30, "fire" = 90, "acid" = 50) + flags = NOSLIP + +/obj/item/clothing/shoes/sandal + desc = "A pair of rather plain, wooden sandals." + name = "sandals" + icon_state = "wizard" + strip_delay = 50 + put_on_delay = 50 + magical = TRUE + +/obj/item/clothing/shoes/sandal/marisa + desc = "A pair of magic, black shoes." + name = "magic shoes" + icon_state = "black" + resistance_flags = FIRE_PROOF | ACID_PROOF + +/obj/item/clothing/shoes/sandal/magic + name = "magical sandals" + desc = "A pair of sandals imbued with magic." + resistance_flags = FIRE_PROOF | ACID_PROOF + +/obj/item/clothing/shoes/galoshes + desc = "A pair of yellow rubber boots, designed to prevent slipping on wet surfaces." + name = "galoshes" + icon_state = "galoshes" + permeability_coefficient = 0.05 + flags = NOSLIP + slowdown = SHOES_SLOWDOWN+1 + strip_delay = 50 + put_on_delay = 50 + resistance_flags = NONE + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 40, "acid" = 75) + +/obj/item/clothing/shoes/galoshes/dry + name = "absorbent galoshes" + desc = "A pair of purple rubber boots, designed to prevent slipping on wet surfaces while also drying them." + icon_state = "galoshes_dry" + +/obj/item/clothing/shoes/galoshes/dry/step_action() + var/turf/simulated/t_loc = get_turf(src) + if(istype(t_loc) && t_loc.wet) + t_loc.MakeDry(TURF_WET_WATER) + +/obj/item/clothing/shoes/clown_shoes + desc = "The prankster's standard-issue clowning shoes. Damn they're huge! Ctrl-click to toggle the waddle dampeners!" + name = "clown shoes" + icon_state = "clown" + item_state = "clown_shoes" + slowdown = SHOES_SLOWDOWN+1 + item_color = "clown" + var/footstep = 1 //used for squeeks whilst walking + shoe_sound = "clownstep" + var/enabled_waddle = TRUE + var/datum/component/waddle + +/obj/item/clothing/shoes/clown_shoes/equipped(mob/user, slot) + . = ..() + if(slot == slot_shoes && enabled_waddle) + waddle = user.AddComponent(/datum/component/waddling) + +/obj/item/clothing/shoes/clown_shoes/dropped(mob/user) + . = ..() + QDEL_NULL(waddle) + +/obj/item/clothing/shoes/clown_shoes/CtrlClick(mob/living/user) + if(!isliving(user)) + return + if(user.get_active_hand() != src) + to_chat(user, "You must hold [src] in your hand to do this.") + return + if(!enabled_waddle) + to_chat(user, "You switch off the waddle dampeners!") + enabled_waddle = TRUE + else + to_chat(user, "You switch on the waddle dampeners!") + enabled_waddle = FALSE + +/obj/item/clothing/shoes/clown_shoes/nodrop + flags = NODROP + +/obj/item/clothing/shoes/clown_shoes/magical + name = "magical clown shoes" + desc = "Standard-issue shoes of the wizarding class clown. Damn they're huge! And powerful! Somehow." + magical = TRUE + +/obj/item/clothing/shoes/jackboots + name = "jackboots" + desc = "Nanotrasen-issue Security combat boots for combat scenarios or combat situations. All combat, all the time." + can_cut_open = 1 + icon_state = "jackboots" + item_state = "jackboots" + item_color = "hosred" + strip_delay = 50 + put_on_delay = 50 + resistance_flags = NONE + var/footstep = 1 + shoe_sound = "jackboot" + +/obj/item/clothing/shoes/jackboots/jacksandals + name = "jacksandals" + desc = "Nanotrasen-issue Security combat sandals for combat scenarios. They're jacksandals, however that works." + can_cut_open = 0 + icon_state = "jacksandal" + item_color = "jacksandal" + +/obj/item/clothing/shoes/workboots + name = "work boots" + desc = "Thick-soled boots for industrial work environments." + can_cut_open = 1 + icon_state = "workboots" + +/obj/item/clothing/shoes/workboots/mining + name = "mining boots" + desc = "Steel-toed mining boots for mining in hazardous environments. Very good at keeping toes uncrushed." + icon_state = "explorer" + resistance_flags = FIRE_PROOF + +/obj/item/clothing/shoes/winterboots + name = "winter boots" + desc = "Boots lined with 'synthetic' animal fur." + can_cut_open = 1 + icon_state = "winterboots" + cold_protection = FEET|LEGS + min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT + heat_protection = FEET|LEGS + max_heat_protection_temperature = SHOES_MAX_TEMP_PROTECT + +/obj/item/clothing/shoes/cult + name = "boots" + desc = "A pair of boots worn by the followers of Nar-Sie." + icon_state = "cult" + item_state = "cult" + item_color = "cult" + + cold_protection = FEET + min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT + heat_protection = FEET + max_heat_protection_temperature = SHOES_MAX_TEMP_PROTECT + +/obj/item/clothing/shoes/cyborg + name = "cyborg boots" + desc = "Shoes for a cyborg costume" + icon_state = "boots" + +/obj/item/clothing/shoes/slippers + name = "bunny slippers" + desc = "Fluffy!" + icon_state = "slippers" + item_state = "slippers" + +/obj/item/clothing/shoes/slippers_worn + name = "worn bunny slippers" + desc = "Fluffy..." + icon_state = "slippers_worn" + item_state = "slippers_worn" + +/obj/item/clothing/shoes/laceup + name = "laceup shoes" + desc = "The height of fashion, and they're pre-polished!" + icon_state = "laceups" + put_on_delay = 50 + +/obj/item/clothing/shoes/roman + name = "roman sandals" + desc = "Sandals with buckled leather straps on it." + icon_state = "roman" + item_state = "roman" + strip_delay = 100 + put_on_delay = 100 + +/obj/item/clothing/shoes/centcom + name = "dress shoes" + desc = "They appear impeccably polished." + icon_state = "laceups" + +/obj/item/clothing/shoes/griffin + name = "griffon boots" + desc = "A pair of costume boots fashioned after bird talons." + icon_state = "griffinboots" + item_state = "griffinboots" + + +/obj/item/clothing/shoes/fluff/noble_boot + name = "noble boots" + desc = "The boots are economically designed to balance function and comfort, so that you can step on peasants without having to worry about blisters. The leather also resists unwanted blood stains." + icon_state = "noble_boot" + item_color = "noble_boot" + item_state = "noble_boot" + +/obj/item/clothing/shoes/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/stack/tape_roll) && !silence_steps) + var/obj/item/stack/tape_roll/TR = I + if((!silence_steps || shoe_sound) && TR.use(4)) + silence_steps = TRUE + shoe_sound = null + to_chat(user, "You tape the soles of [src] to silence your footsteps.") + else + return ..() + +/obj/item/clothing/shoes/sandal/white + name = "White Sandals" + desc = "Medical sandals that nerds wear." + icon_state = "medsandal" + item_color = "medsandal" + +/obj/item/clothing/shoes/sandal/fancy + name = "Fancy Sandals" + desc = "FANCY!!." + icon_state = "fancysandal" + item_color = "fancysandal" + +/obj/item/clothing/shoes/cursedclown + name = "cursed clown shoes" + desc = "Moldering clown flip flops. They're neon green for some reason." + icon = 'icons/goonstation/objects/clothing/feet.dmi' + icon_state = "cursedclown" + item_state = "cclown_shoes" + icon_override = 'icons/goonstation/mob/clothing/feet.dmi' + lefthand_file = 'icons/goonstation/mob/inhands/clothing_lefthand.dmi' + righthand_file = 'icons/goonstation/mob/inhands/clothing_righthand.dmi' + resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF + flags = NODROP + shoe_sound = "clownstep" + +/obj/item/clothing/shoes/singery + name = "yellow performer's boots" + desc = "These boots were made for dancing." + icon_state = "ysing" + put_on_delay = 50 + +/obj/item/clothing/shoes/singerb + name = "blue performer's boots" + desc = "These boots were made for dancing." + icon_state = "bsing" + put_on_delay = 50 + +/obj/item/clothing/shoes/cowboy + name = "cowboy boots" + desc = "A pair a' brown boots." + icon_state = "cowboy_brown" + item_color = "cowboy_brown" + +/obj/item/clothing/shoes/cowboy/black + name = "black cowboy boots" + desc = "A pair a' black rustlers' boots" + icon_state = "cowboy_black" + item_color = "cowboy_black" + +/obj/item/clothing/shoes/cowboy/white + name = "white cowboy boots" + desc = "For the rancher in us all." + icon_state = "cowboy_white" + item_color = "cowboy_white" + +/obj/item/clothing/shoes/cowboy/fancy + name = "bilton wrangler boots" + desc = "A pair of authentic haute couture boots from Japanifornia. You doubt they have ever been close to cattle." + icon_state = "cowboy_fancy" + item_color = "cowboy_fancy" + +/obj/item/clothing/shoes/cowboy/pink + name = "pink cowgirl boots" + desc = "For a Rustlin' tustlin' cowgirl." + icon_state = "cowboyboots_pink" + item_color = "cowboyboots_pink" + +/obj/item/clothing/shoes/cowboy/lizard + name = "lizard skin boots" + desc = "You can hear a faint hissing from inside the boots; you hope it is just a mournful ghost." + icon_state = "lizardboots_green" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 40, "acid" = 0) //lizards like to stay warm + +/obj/item/clothing/shoes/cowboy/lizardmasterwork + name = "\improper Hugs-The-Feet lizard skin boots" + desc = "A pair of masterfully crafted lizard skin boots. Finally a good application for the station's most bothersome inhabitants." + icon_state = "lizardboots_blue" + +/obj/effect/spawner/lootdrop/lizardboots + name = "random lizard boot quality" + desc = "Which ever gets picked, the lizard race loses" + icon = 'icons/obj/clothing/shoes.dmi' + icon_state = "lizardboots_green" + loot = list( + /obj/item/clothing/shoes/cowboy/lizard = 7, + /obj/item/clothing/shoes/cowboy/lizardmasterwork = 1) + +/obj/item/clothing/shoes/footwraps + name = "cloth footwraps" + desc = "A roll of treated canvas used for wrapping claws or paws." + icon_state = "clothwrap" + item_state = "clothwrap" + force = 0 + silence_steps = TRUE + w_class = WEIGHT_CLASS_SMALL + +/obj/item/clothing/shoes/bhop + name = "jump boots" + desc = "A specialized pair of combat boots with a built-in propulsion system for rapid foward movement." + icon_state = "jetboots" + item_state = "jetboots" + item_color = "hosred" + resistance_flags = FIRE_PROOF + actions_types = list(/datum/action/item_action/bhop) + permeability_coefficient = 0.05 + can_cut_open = FALSE + var/jumpdistance = 5 //-1 from to see the actual distance, e.g 4 goes over 3 tiles + var/jumpspeed = 3 + var/recharging_rate = 60 //default 6 seconds between each dash + var/recharging_time = 0 //time until next dash + +/obj/item/clothing/shoes/bhop/ui_action_click(mob/user, action) + if(!isliving(user)) + return + + if(recharging_time > world.time) + to_chat(user, "The boot's internal propulsion needs to recharge still!") + return + + var/atom/target = get_edge_target_turf(user, user.dir) //gets the user's direction + + if (user.throw_at(target, jumpdistance, jumpspeed, spin = FALSE, diagonals_first = TRUE)) + playsound(src, 'sound/effects/stealthoff.ogg', 50, 1, 1) + user.visible_message("[usr] dashes forward into the air!") + recharging_time = world.time + recharging_rate + else + to_chat(user, "Something prevents you from dashing forward!") diff --git a/code/modules/clothing/spacesuits/breaches.dm b/code/modules/clothing/spacesuits/breaches.dm index c8891fde00d..483c122fd80 100644 --- a/code/modules/clothing/spacesuits/breaches.dm +++ b/code/modules/clothing/spacesuits/breaches.dm @@ -194,25 +194,18 @@ var/global/list/breach_burn_descriptors = list( repair_breaches(BURN, ( istype(P,/obj/item/stack/sheet/plastic) ? 3 : 5), user) return - else if(istype(W, /obj/item/weldingtool)) - if(istype(src.loc,/mob/living)) - to_chat(user, "How do you intend to patch a hardsuit while someone is wearing it?") - return - - if(!damage || ! brute_damage) - to_chat(user, "There is no structural damage on \the [src] to repair.") - return - - var/obj/item/weldingtool/WT = W - if(!WT.remove_fuel(5)) - to_chat(user, "You need more welding fuel to repair this suit.") - return - - repair_breaches(BRUTE, 3, user) +/obj/item/clothing/suit/space/welder_act(mob/user, obj/item/I) + . = TRUE + if(istype(src.loc,/mob/living)) + to_chat(user, "How do you intend to patch a hardsuit while someone is wearing it?") return - - ..() + if(!damage || ! brute_damage) + to_chat(user, "There is no structural damage on \the [src] to repair.") + return + if(!I.use_tool(src, user, amount = 5, volume = I.tool_volume)) + return + repair_breaches(BRUTE, 3, user) /obj/item/clothing/suit/space/examine(mob/user) . = ..() diff --git a/code/modules/clothing/spacesuits/ert.dm b/code/modules/clothing/spacesuits/ert.dm index a97752a29db..225b32e59fe 100644 --- a/code/modules/clothing/spacesuits/ert.dm +++ b/code/modules/clothing/spacesuits/ert.dm @@ -246,4 +246,4 @@ icon_state = "hardsuit-berserker" helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ert/paranormal/berserker armor = list(melee = 65, bullet = 50, laser = 50, energy = 50, bomb = 50, bio = 100, rad = 100, fire = 80, acid = 80) - slowdown = 0 \ No newline at end of file + slowdown = 0 diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm index 32b594b1387..5a01c9f6f55 100644 --- a/code/modules/clothing/spacesuits/hardsuit.dm +++ b/code/modules/clothing/spacesuits/hardsuit.dm @@ -1,588 +1,590 @@ -//Baseline hardsuits -/obj/item/clothing/head/helmet/space/hardsuit - name = "hardsuit helmet" - desc = "A special helmet designed for work in a hazardous, low-pressure environment." - icon_state = "hardsuit0-engineering" - item_state = "eng_helm" - max_integrity = 300 - armor = list("melee" = 10, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 75, "fire" = 50, "acid" = 75) - var/basestate = "hardsuit" - allowed = list(/obj/item/flashlight) - var/brightness_on = 4 //luminosity when on - var/on = FALSE - var/obj/item/clothing/suit/space/hardsuit/suit - item_color = "engineering" //Determines used sprites: hardsuit[on]-[color] and hardsuit[on]-[color]2 (lying down sprite) - actions_types = list(/datum/action/item_action/toggle_helmet_light) - - //Species-specific stuff. - species_restricted = list("exclude","Wryn") - sprite_sheets = list( - "Unathi" = 'icons/mob/species/unathi/helmet.dmi', - "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', - "Skrell" = 'icons/mob/species/skrell/helmet.dmi', - "Vox" = 'icons/mob/species/vox/helmet.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', - "Drask" = 'icons/mob/species/drask/helmet.dmi', - "Grey" = 'icons/mob/species/grey/helmet.dmi' - ) - sprite_sheets_obj = list( - "Unathi" = 'icons/obj/clothing/species/unathi/hats.dmi', - "Tajaran" = 'icons/obj/clothing/species/tajaran/hats.dmi', - "Skrell" = 'icons/obj/clothing/species/skrell/hats.dmi', - "Vox" = 'icons/obj/clothing/species/vox/hats.dmi', - "Vulpkanin" = 'icons/obj/clothing/species/vulpkanin/hats.dmi' - ) - -/obj/item/clothing/head/helmet/space/hardsuit/attack_self(mob/user) - toggle_light(user) - -/obj/item/clothing/head/helmet/space/hardsuit/proc/toggle_light(mob/user) - on = !on - icon_state = "[basestate][on]-[item_color]" - - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - H.update_inv_head() - - if(on) - set_light(brightness_on) - else - set_light(0) - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - -/obj/item/clothing/head/helmet/space/hardsuit/extinguish_light() - if(on) - toggle_light() - visible_message("[src]'s light fades and turns off.") - -/obj/item/clothing/head/helmet/space/hardsuit/dropped(mob/user) - ..() - if(suit) - suit.RemoveHelmet() - -/obj/item/clothing/head/helmet/space/hardsuit/item_action_slot_check(slot) - if(slot == slot_head) - return 1 - -/obj/item/clothing/head/helmet/space/hardsuit/equipped(mob/user, slot) - ..() - if(slot != slot_head) - if(suit) - suit.RemoveHelmet() - else - qdel(src) - -/obj/item/clothing/head/helmet/space/hardsuit/proc/display_visor_message(var/msg) - var/mob/wearer = loc - if(msg && ishuman(wearer)) - wearer.show_message("[msg]", 1) - -/obj/item/clothing/head/helmet/space/hardsuit/emp_act(severity) - ..() - display_visor_message("[severity > 1 ? "Light" : "Strong"] electromagnetic pulse detected!") - -/obj/item/clothing/suit/space/hardsuit - name = "hardsuit" - desc = "A special space suit for environments that might pose hazards beyond just the vacuum of space. Provides more protection than a standard space suit." - icon_state = "hardsuit-engineering" - item_state = "eng_hardsuit" - max_integrity = 300 - armor = list("melee" = 10, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 75, "fire" = 50, "acid" = 75) - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/t_scanner, /obj/item/rcd, /obj/item/rpd) - siemens_coefficient = 0 - var/obj/item/clothing/head/helmet/space/hardsuit/helmet - actions_types = list(/datum/action/item_action/toggle_helmet) - var/helmettype = /obj/item/clothing/head/helmet/space/hardsuit - var/obj/item/tank/jetpack/suit/jetpack = null - - hide_tail_by_species = list("Vox" , "Vulpkanin" , "Unathi" , "Tajaran") - species_restricted = list("exclude", "Wryn") - sprite_sheets = list( - "Unathi" = 'icons/mob/species/unathi/suit.dmi', - "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', - "Skrell" = 'icons/mob/species/skrell/suit.dmi', - "Vox" = 'icons/mob/species/vox/suit.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', - "Drask" = 'icons/mob/species/drask/suit.dmi' - ) - sprite_sheets_obj = list( - "Unathi" = 'icons/obj/clothing/species/unathi/suits.dmi', - "Tajaran" = 'icons/obj/clothing/species/tajaran/suits.dmi', - "Skrell" = 'icons/obj/clothing/species/skrell/suits.dmi', - "Vox" = 'icons/obj/clothing/species/vox/suits.dmi', - "Vulpkanin" = 'icons/obj/clothing/species/vulpkanin/suits.dmi' - ) - -/obj/item/clothing/suit/space/hardsuit/New() - if(jetpack && ispath(jetpack)) - jetpack = new jetpack(src) - ..() - -/obj/item/clothing/suit/space/hardsuit/attack_self(mob/user) - user.changeNext_move(CLICK_CD_MELEE) - ..() - -/obj/item/clothing/suit/space/hardsuit/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/tank/jetpack/suit)) - if(jetpack) - to_chat(user, "[src] already has a jetpack installed.") - return - if(src == user.get_item_by_slot(slot_wear_suit)) //Make sure the player is not wearing the suit before applying the upgrade. - to_chat(user, "You cannot install the upgrade to [src] while wearing it.") - return - - if(user.unEquip(I)) - I.forceMove(src) - jetpack = I - to_chat(user, "You successfully install the jetpack into [src].") - return - else if(isscrewdriver(I)) - if(!jetpack) - to_chat(user, "[src] has no jetpack installed.") - return - if(src == user.get_item_by_slot(slot_wear_suit)) - to_chat(user, "You cannot remove the jetpack from [src] while wearing it.") - return - - jetpack.turn_off(user) - jetpack.forceMove(drop_location()) - jetpack = null - to_chat(user, "You successfully remove the jetpack from [src].") - return - return ..() - -/obj/item/clothing/suit/space/hardsuit/equipped(mob/user, slot) - ..() - if(jetpack) - if(slot == slot_wear_suit) - for(var/X in jetpack.actions) - var/datum/action/A = X - A.Grant(user) - -/obj/item/clothing/suit/space/hardsuit/dropped(mob/user) - ..() - if(jetpack) - for(var/X in jetpack.actions) - var/datum/action/A = X - A.Remove(user) - -/obj/item/clothing/suit/space/hardsuit/item_action_slot_check(slot) - if(slot == slot_wear_suit) //we only give the mob the ability to toggle the helmet if he's wearing the hardsuit. - return 1 - -//Engineering hardsuit -/obj/item/clothing/head/helmet/space/hardsuit/engine - name = "engineering hardsuit helmet" - desc = "A special helmet designed for work in a hazardous, low-pressure environment. Has radiation shielding." - icon_state = "hardsuit0-engineering" - item_state = "eng_helm" - armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 75, "fire" = 100, "acid" = 75) - resistance_flags = FIRE_PROOF - item_color = "engineering" - -/obj/item/clothing/suit/space/hardsuit/engine - name = "engineering hardsuit" - desc = "A special suit that protects against hazardous, low pressure environments. Has radiation shielding." - icon_state = "hardsuit-engineering" - item_state = "eng_hardsuit" - armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 75, "fire" = 100, "acid" = 75) - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/engine - dog_fashion = /datum/dog_fashion/back/hardsuit - resistance_flags = FIRE_PROOF - -//Atmospherics -/obj/item/clothing/head/helmet/space/hardsuit/engine/atmos - name = "atmospherics hardsuit helmet" - desc = "A special helmet designed for work in a hazardous, low-pressure environment. Has thermal shielding." - icon_state = "hardsuit0-atmos" - item_state = "atmos_helm" - item_color = "atmos" - armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 25, "fire" = 100, "acid" = 75) - heat_protection = HEAD //Uncomment to enable firesuit protection - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - -/obj/item/clothing/suit/space/hardsuit/engine/atmos - name = "atmospherics hardsuit" - desc = "A special suit that protects against hazardous, low pressure environments. Has thermal shielding." - icon_state = "hardsuit-atmos" - item_state = "atmos_hardsuit" - armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 25, "fire" = 100, "acid" = 75) - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS //Uncomment to enable firesuit protection - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/engine/atmos - dog_fashion = null - -//Chief Engineer's hardsuit -/obj/item/clothing/head/helmet/space/hardsuit/engine/elite - name = "advanced hardsuit helmet" - desc = "An advanced helmet designed for work in a hazardous, low pressure environment. Shines with a high polish." - icon_state = "hardsuit0-white" - item_state = "ce_helm" - item_color = "white" - armor = list("melee" = 40, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 90) - heat_protection = HEAD //Uncomment to enable firesuit protection - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - -/obj/item/clothing/suit/space/hardsuit/engine/elite - icon_state = "hardsuit-white" - name = "advanced hardsuit" - desc = "An advanced suit that protects against hazardous, low pressure environments. Shines with a high polish." - item_state = "ce_hardsuit" - armor = list("melee" = 40, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 90, "fire" = 100, "acid" = 90) - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS //Uncomment to enable firesuit protection - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/engine/elite - jetpack = /obj/item/tank/jetpack/suit - dog_fashion = null - -//Mining hardsuit -/obj/item/clothing/head/helmet/space/hardsuit/mining - name = "mining hardsuit helmet" - desc = "A special helmet designed for work in a hazardous, low pressure environment. Has reinforced plating." - icon_state = "hardsuit0-mining" - item_state = "mining_helm" - item_color = "mining" - max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT - resistance_flags = FIRE_PROOF - heat_protection = HEAD - armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 75) - brightness_on = 7 - -/obj/item/clothing/suit/space/hardsuit/mining - icon_state = "hardsuit-mining" - name = "mining hardsuit" - desc = "A special suit that protects against hazardous, low pressure environments. Has reinforced plating." - item_state = "mining_hardsuit" - max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT - resistance_flags = FIRE_PROOF - armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 75) - allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/storage/bag/ore, /obj/item/pickaxe, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator) - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/mining - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - -//Syndicate hardsuit -/obj/item/clothing/head/helmet/space/hardsuit/syndi - name = "blood-red hardsuit helmet" - desc = "A dual-mode advanced helmet designed for work in special operations. It is in travel mode. Property of Gorlex Marauders." - alt_desc = "A dual-mode advanced helmet designed for work in special operations. It is in combat mode. Property of Gorlex Marauders." - icon_state = "hardsuit1-syndi" - item_state = "syndie_helm" - item_color = "syndi" - armor = list("melee" = 40, "bullet" = 50, "laser" = 30, "energy" = 15, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 90) - on = 1 - var/obj/item/clothing/suit/space/hardsuit/syndi/linkedsuit = null - actions_types = list(/datum/action/item_action/toggle_helmet_mode) - visor_flags_inv = HIDEMASK|HIDEEYES|HIDEFACE|HIDETAIL - visor_flags = STOPSPRESSUREDMAGE - -/obj/item/clothing/head/helmet/space/hardsuit/syndi/update_icon() - icon_state = "hardsuit[on]-[item_color]" - -/obj/item/clothing/head/helmet/space/hardsuit/syndi/New() - ..() - if(istype(loc, /obj/item/clothing/suit/space/hardsuit/syndi)) - linkedsuit = loc - -/obj/item/clothing/head/helmet/space/hardsuit/syndi/attack_self(mob/user) //Toggle Helmet - if(!isturf(user.loc)) - to_chat(user, "You cannot toggle your helmet while in this [user.loc]!" ) - return - on = !on - if(on) - to_chat(user, "You switch your hardsuit to EVA mode, sacrificing speed for space protection.") - name = initial(name) - desc = initial(desc) - set_light(brightness_on) - flags |= visor_flags - flags_cover |= HEADCOVERSEYES | HEADCOVERSMOUTH - flags_inv |= visor_flags_inv - cold_protection |= HEAD - else - to_chat(user, "You switch your hardsuit to combat mode and can now run at full speed.") - name += " (combat)" - desc = alt_desc - set_light(0) - flags &= ~visor_flags - flags_cover &= ~(HEADCOVERSEYES | HEADCOVERSMOUTH) - flags_inv &= ~visor_flags_inv - cold_protection &= ~HEAD - update_icon() - playsound(src.loc, 'sound/mecha/mechmove03.ogg', 50, 1) - toggle_hardsuit_mode(user) - user.update_inv_head() - if(iscarbon(user)) - var/mob/living/carbon/C = user - C.head_update(src, forced = 1) - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - -/obj/item/clothing/head/helmet/space/hardsuit/syndi/proc/toggle_hardsuit_mode(mob/user) //Helmet Toggles Suit Mode - if(linkedsuit) - if(on) - linkedsuit.name = initial(linkedsuit.name) - linkedsuit.desc = initial(linkedsuit.desc) - linkedsuit.slowdown = 1 - linkedsuit.flags |= STOPSPRESSUREDMAGE - linkedsuit.cold_protection |= UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS - else - linkedsuit.name += " (combat)" - linkedsuit.desc = linkedsuit.alt_desc - linkedsuit.slowdown = 0 - linkedsuit.flags &= ~STOPSPRESSUREDMAGE - linkedsuit.cold_protection &= ~(UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS) - - linkedsuit.update_icon() - user.update_inv_wear_suit() - user.update_inv_w_uniform() - -/obj/item/clothing/suit/space/hardsuit/syndi - name = "blood-red hardsuit" - desc = "A dual-mode advanced hardsuit designed for work in special operations. It is in travel mode. Property of Gorlex Marauders." - alt_desc = "A dual-mode advanced hardsuit designed for work in special operations. It is in combat mode. Property of Gorlex Marauders." - icon_state = "hardsuit1-syndi" - item_state = "syndie_hardsuit" - item_color = "syndi" - w_class = WEIGHT_CLASS_NORMAL - var/on = 1 - actions_types = list(/datum/action/item_action/toggle_hardsuit_mode) - armor = list("melee" = 40, "bullet" = 50, "laser" = 30, "energy" = 15, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 90) - allowed = list(/obj/item/gun, /obj/item/ammo_box,/obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/melee/energy/sword, /obj/item/restraints/handcuffs, /obj/item/tank) - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi - jetpack = /obj/item/tank/jetpack/suit - -/obj/item/clothing/suit/space/hardsuit/syndi/update_icon() - icon_state = "hardsuit[on]-[item_color]" - -//Elite Syndie suit -/obj/item/clothing/head/helmet/space/hardsuit/syndi/elite - name = "elite syndicate hardsuit helmet" - desc = "An elite version of the syndicate helmet, with improved armour and fire shielding. It is in travel mode. Property of Gorlex Marauders." - icon_state = "hardsuit0-syndielite" - item_color = "syndielite" - armor = list("melee" = 60, "bullet" = 60, "laser" = 50, "energy" = 25, "bomb" = 55, "bio" = 100, "rad" = 70, "fire" = 100, "acid" = 100) - heat_protection = HEAD - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = FIRE_PROOF | ACID_PROOF - -/obj/item/clothing/suit/space/hardsuit/syndi/elite - name = "elite syndicate hardsuit" - desc = "An elite version of the syndicate hardsuit, with improved armour and fire shielding. It is in travel mode." - icon_state = "hardsuit0-syndielite" - item_color = "syndielite" - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi/elite - armor = list("melee" = 60, "bullet" = 60, "laser" = 50, "energy" = 25, "bomb" = 55, "bio" = 100, "rad" = 70, "fire" = 100, "acid" = 100) - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = FIRE_PROOF | ACID_PROOF - -//Strike team hardsuits -/obj/item/clothing/head/helmet/space/hardsuit/syndi/elite/sst - armor = list(melee = 70, bullet = 70, laser = 50, energy = 40, bomb = 80, bio = 100, rad = 100, fire = 100, acid = 100) //Almost as good as DS gear, but unlike DS can switch to combat for mobility - icon_state = "hardsuit0-sst" - item_color = "sst" - -/obj/item/clothing/suit/space/hardsuit/syndi/elite/sst - armor = list(melee = 70, bullet = 70, laser = 50, energy = 40, bomb = 80, bio = 100, rad = 100, fire = 100, acid = 100) - icon_state = "hardsuit0-sst" - item_color = "sst" - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi/elite/sst - -/obj/item/clothing/suit/space/hardsuit/syndi/freedom - name = "eagle suit" - desc = "An advanced, light suit, fabricated from a mixture of synthetic feathers and space-resistant material. A gun holster appears to be integrated into the suit." - icon_state = "freedom" - item_state = "freedom" - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi/freedom - sprite_sheets = null - -/obj/item/clothing/suit/space/hardsuit/syndi/freedom/update_icon() - return - -/obj/item/clothing/head/helmet/space/hardsuit/syndi/freedom - name = "eagle helmet" - desc = "An advanced, space-proof helmet. It appears to be modeled after an old-world eagle." - icon_state = "griffinhat" - item_state = "griffinhat" - sprite_sheets = null - -/obj/item/clothing/head/helmet/space/hardsuit/syndi/freedom/update_icon() - return - -//Medical hardsuit -/obj/item/clothing/head/helmet/space/hardsuit/medical - name = "medical hardsuit helmet" - desc = "A special helmet designed for work in a hazardous, low pressure environment. Built with lightweight materials for extra comfort, but does not protect the eyes from intense light." - icon_state = "hardsuit0-medical" - item_state = "medical_helm" - item_color = "medical" - flash_protect = 0 - armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 60, "fire" = 60, "acid" = 75) - flags = STOPSPRESSUREDMAGE | THICKMATERIAL - scan_reagents = 1 //Generally worn by the CMO, so they'd get utility off of seeing reagents - -/obj/item/clothing/suit/space/hardsuit/medical - icon_state = "hardsuit-medical" - name = "medical hardsuit" - desc = "A special helmet designed for work in a hazardous, low pressure environment. Built with lightweight materials for extra comfort." - item_state = "medical_hardsuit" - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/storage/firstaid,/obj/item/healthanalyzer,/obj/item/stack/medical,/obj/item/rad_laser) - armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 60, "fire" = 60, "acid" = 75) - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/medical - slowdown = 0.5 - - //Security -/obj/item/clothing/head/helmet/space/hardsuit/security - name = "security hardsuit helmet" - desc = "A special helmet designed for work in a hazardous, low pressure environment. Has an additional layer of armor." - icon_state = "hardsuit0-sec" - item_state = "sec_helm" - item_color = "sec" - armor = list("melee" = 35, "bullet" = 15, "laser" = 30,"energy" = 10, "bomb" = 10, "bio" = 100, "rad" = 50, "fire" = 75, "acid" = 75) - -/obj/item/clothing/suit/space/hardsuit/security - icon_state = "hardsuit-sec" - name = "security hardsuit" - desc = "A special suit that protects against hazardous, low pressure environments. Has an additional layer of armor." - item_state = "sec_hardsuit" - armor = list("melee" = 35, "bullet" = 15, "laser" = 30, "energy" = 10, "bomb" = 10, "bio" = 100, "rad" = 50, "fire" = 75, "acid" = 75) - allowed = list(/obj/item/gun,/obj/item/flashlight,/obj/item/tank,/obj/item/melee/baton,/obj/item/reagent_containers/spray/pepper,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/restraints/handcuffs) - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/security - -/obj/item/clothing/head/helmet/space/hardsuit/security/hos - name = "head of security's hardsuit helmet" - desc = "A special bulky helmet designed for work in a hazardous, low pressure environment. Has an additional layer of armor." - icon_state = "hardsuit0-hos" - item_color = "hos" - armor = list("melee" = 45, "bullet" = 25, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 100, "rad" = 50, "fire" = 95, "acid" = 95) - -/obj/item/clothing/suit/space/hardsuit/security/hos - name = "head of security's hardsuit" - desc = "A special bulky suit that protects against hazardous, low pressure environments. Has an additional layer of armor." - icon_state = "hardsuit-hos" - armor = list("melee" = 45, "bullet" = 25, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 100, "rad" = 50, "fire" = 95, "acid" = 95) - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/security/hos - jetpack = /obj/item/tank/jetpack/suit - - -//Singuloth armor -/obj/item/clothing/head/helmet/space/hardsuit/singuloth - name = "singuloth knight's helmet" - desc = "This is an adamantium helmet from the chapter of the Singuloth Knights. It shines with a holy aura." - icon_state = "hardsuit0-singuloth" - item_state = "singuloth_helm" - item_color = "singuloth" - armor = list(melee = 40, bullet = 5, laser = 20, energy = 5, bomb = 25, bio = 100, rad = 100, fire = 95, acid = 95) - sprite_sheets = null - -/obj/item/clothing/suit/space/hardsuit/singuloth - icon_state = "hardsuit-singuloth" - name = "singuloth knight's armor" - desc = "This is a ceremonial armor from the chapter of the Singuloth Knights. It's made of pure forged adamantium." - item_state = "singuloth_hardsuit" - flags = STOPSPRESSUREDMAGE - armor = list(melee = 45, bullet = 25, laser = 30, energy = 10, bomb = 25, bio = 100, rad = 100, fire = 95, acid = 95) - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/singuloth - sprite_sheets = null - - -/////////////SHIELDED////////////////////////////////// - -/obj/item/clothing/suit/space/hardsuit/shielded - name = "shielded hardsuit" - desc = "A hardsuit with built in energy shielding. Will rapidly recharge when not under fire." - icon_state = "hardsuit-hos" - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/shielded - allowed = list(/obj/item/flashlight,/obj/item/tank, /obj/item/gun,/obj/item/reagent_containers/spray/pepper,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs) - armor = list("melee" = 30, "bullet" = 15, "laser" = 30, "energy" = 10, "bomb" = 10, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) - resistance_flags = FIRE_PROOF | ACID_PROOF - var/current_charges = 3 - var/max_charges = 3 //How many charges total the shielding has - var/recharge_delay = 200 //How long after we've been shot before we can start recharging. 20 seconds here - var/recharge_cooldown = 0 //Time since we've last been shot - var/recharge_rate = 1 //How quickly the shield recharges once it starts charging - var/shield_state = "shield-old" - var/shield_on = "shield-old" - -/obj/item/clothing/suit/space/hardsuit/shielded/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - recharge_cooldown = world.time + recharge_delay - if(current_charges > 0) - do_sparks(2, 1, src) - owner.visible_message("[owner]'s shields deflect [attack_text] in a shower of sparks!") - current_charges-- - if(recharge_rate) - START_PROCESSING(SSobj, src) - if(current_charges <= 0) - owner.visible_message("[owner]'s shield overloads!") - shield_state = "broken" - owner.update_inv_wear_suit() - return 1 - return 0 - - - -/obj/item/clothing/suit/space/hardsuit/shielded/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/clothing/suit/space/hardsuit/shielded/process() - if(world.time > recharge_cooldown && current_charges < max_charges) - current_charges = Clamp((current_charges + recharge_rate), 0, max_charges) - playsound(loc, 'sound/magic/charge.ogg', 50, TRUE) - if(current_charges == max_charges) - playsound(loc, 'sound/machines/ding.ogg', 50, TRUE) - STOP_PROCESSING(SSobj, src) - shield_state = "[shield_on]" - if(ishuman(loc)) - var/mob/living/carbon/human/C = loc - C.update_inv_wear_suit() - -/obj/item/clothing/suit/space/hardsuit/shielded/special_overlays() - return mutable_appearance('icons/effects/effects.dmi', shield_state, MOB_LAYER + 0.01) - -/obj/item/clothing/head/helmet/space/hardsuit/shielded - resistance_flags = FIRE_PROOF | ACID_PROOF - - -//////Syndicate Version - -/obj/item/clothing/suit/space/hardsuit/shielded/syndi - name = "blood-red hardsuit" - desc = "An advanced hardsuit with built in energy shielding." - icon_state = "hardsuit1-syndi" - item_state = "syndie_hardsuit" - item_color = "syndi" - armor = list("melee" = 40, "bullet" = 50, "laser" = 30, "energy" = 15, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) - allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword/saber,/obj/item/restraints/handcuffs,/obj/item/tank) - slowdown = 0 - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/shielded/syndi - jetpack = /obj/item/tank/jetpack/suit - -/obj/item/clothing/suit/space/hardsuit/shielded/syndi/attackby(obj/item/I, mob/user, params) - if(ismultitool(I)) - if(shield_state == "broken") - to_chat(user, "You can't interface with the hardsuit's software if the shield's broken!") - return - - if(shield_state == "shield-red") - shield_state = "shield-old" - shield_on = "shield-old" - to_chat(user, "You roll back the hardsuit's software, changing the shield's color!") - - else - shield_state = "shield-red" - shield_on = "shield-red" - to_chat(user, "You update the hardsuit's hardware, changing back the shield's color to red.") - user.update_inv_wear_suit() - return - return ..() - -/obj/item/clothing/head/helmet/space/hardsuit/shielded/syndi - name = "blood-red hardsuit helmet" - desc = "An advanced hardsuit helmet with built in energy shielding." - icon_state = "hardsuit1-syndi" - item_state = "syndie_helm" - item_color = "syndi" - armor = list("melee" = 40, "bullet" = 50, "laser" = 30, "energy" = 15, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) +//Baseline hardsuits +/obj/item/clothing/head/helmet/space/hardsuit + name = "hardsuit helmet" + desc = "A special helmet designed for work in a hazardous, low-pressure environment." + icon_state = "hardsuit0-engineering" + item_state = "eng_helm" + max_integrity = 300 + armor = list("melee" = 10, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 75, "fire" = 50, "acid" = 75) + var/basestate = "hardsuit" + allowed = list(/obj/item/flashlight) + var/brightness_on = 4 //luminosity when on + var/on = FALSE + var/obj/item/clothing/suit/space/hardsuit/suit + item_color = "engineering" //Determines used sprites: hardsuit[on]-[color] and hardsuit[on]-[color]2 (lying down sprite) + actions_types = list(/datum/action/item_action/toggle_helmet_light) + + //Species-specific stuff. + species_restricted = list("exclude","Wryn") + sprite_sheets = list( + "Unathi" = 'icons/mob/species/unathi/helmet.dmi', + "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', + "Skrell" = 'icons/mob/species/skrell/helmet.dmi', + "Vox" = 'icons/mob/species/vox/helmet.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', + "Drask" = 'icons/mob/species/drask/helmet.dmi', + "Grey" = 'icons/mob/species/grey/helmet.dmi' + ) + sprite_sheets_obj = list( + "Unathi" = 'icons/obj/clothing/species/unathi/hats.dmi', + "Tajaran" = 'icons/obj/clothing/species/tajaran/hats.dmi', + "Skrell" = 'icons/obj/clothing/species/skrell/hats.dmi', + "Vox" = 'icons/obj/clothing/species/vox/hats.dmi', + "Vulpkanin" = 'icons/obj/clothing/species/vulpkanin/hats.dmi' + ) + +/obj/item/clothing/head/helmet/space/hardsuit/attack_self(mob/user) + toggle_light(user) + +/obj/item/clothing/head/helmet/space/hardsuit/proc/toggle_light(mob/user) + on = !on + icon_state = "[basestate][on]-[item_color]" + + if(istype(user,/mob/living/carbon/human)) + var/mob/living/carbon/human/H = user + H.update_inv_head() + + if(on) + set_light(brightness_on) + else + set_light(0) + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + +/obj/item/clothing/head/helmet/space/hardsuit/extinguish_light() + if(on) + toggle_light() + visible_message("[src]'s light fades and turns off.") + +/obj/item/clothing/head/helmet/space/hardsuit/dropped(mob/user) + ..() + if(suit) + suit.RemoveHelmet() + +/obj/item/clothing/head/helmet/space/hardsuit/item_action_slot_check(slot) + if(slot == slot_head) + return 1 + +/obj/item/clothing/head/helmet/space/hardsuit/equipped(mob/user, slot) + ..() + if(slot != slot_head) + if(suit) + suit.RemoveHelmet() + else + qdel(src) + +/obj/item/clothing/head/helmet/space/hardsuit/proc/display_visor_message(var/msg) + var/mob/wearer = loc + if(msg && ishuman(wearer)) + wearer.show_message("[msg]", 1) + +/obj/item/clothing/head/helmet/space/hardsuit/emp_act(severity) + ..() + display_visor_message("[severity > 1 ? "Light" : "Strong"] electromagnetic pulse detected!") + +/obj/item/clothing/suit/space/hardsuit + name = "hardsuit" + desc = "A special space suit for environments that might pose hazards beyond just the vacuum of space. Provides more protection than a standard space suit." + icon_state = "hardsuit-engineering" + item_state = "eng_hardsuit" + max_integrity = 300 + armor = list("melee" = 10, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 75, "fire" = 50, "acid" = 75) + allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/t_scanner, /obj/item/rcd, /obj/item/rpd) + siemens_coefficient = 0 + var/obj/item/clothing/head/helmet/space/hardsuit/helmet + actions_types = list(/datum/action/item_action/toggle_helmet) + var/helmettype = /obj/item/clothing/head/helmet/space/hardsuit + var/obj/item/tank/jetpack/suit/jetpack = null + + hide_tail_by_species = list("Vox" , "Vulpkanin" , "Unathi" , "Tajaran") + species_restricted = list("exclude", "Wryn") + sprite_sheets = list( + "Unathi" = 'icons/mob/species/unathi/suit.dmi', + "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', + "Skrell" = 'icons/mob/species/skrell/suit.dmi', + "Vox" = 'icons/mob/species/vox/suit.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', + "Drask" = 'icons/mob/species/drask/suit.dmi' + ) + sprite_sheets_obj = list( + "Unathi" = 'icons/obj/clothing/species/unathi/suits.dmi', + "Tajaran" = 'icons/obj/clothing/species/tajaran/suits.dmi', + "Skrell" = 'icons/obj/clothing/species/skrell/suits.dmi', + "Vox" = 'icons/obj/clothing/species/vox/suits.dmi', + "Vulpkanin" = 'icons/obj/clothing/species/vulpkanin/suits.dmi' + ) + +/obj/item/clothing/suit/space/hardsuit/New() + if(jetpack && ispath(jetpack)) + jetpack = new jetpack(src) + ..() + +/obj/item/clothing/suit/space/hardsuit/attack_self(mob/user) + user.changeNext_move(CLICK_CD_MELEE) + ..() + +/obj/item/clothing/suit/space/hardsuit/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/tank/jetpack/suit)) + if(jetpack) + to_chat(user, "[src] already has a jetpack installed.") + return + if(src == user.get_item_by_slot(slot_wear_suit)) //Make sure the player is not wearing the suit before applying the upgrade. + to_chat(user, "You cannot install the upgrade to [src] while wearing it.") + return + + if(user.unEquip(I)) + I.forceMove(src) + jetpack = I + to_chat(user, "You successfully install the jetpack into [src].") + return + return ..() + +/obj/item/clothing/suit/space/hardsuit/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(!jetpack) + to_chat(user, "[src] has no jetpack installed.") + return + if(src == user.get_item_by_slot(slot_wear_suit)) + to_chat(user, "You cannot remove the jetpack from [src] while wearing it.") + return + jetpack.turn_off(user) + jetpack.forceMove(drop_location()) + jetpack = null + to_chat(user, "You successfully remove the jetpack from [src].") + +/obj/item/clothing/suit/space/hardsuit/equipped(mob/user, slot) + ..() + if(jetpack) + if(slot == slot_wear_suit) + for(var/X in jetpack.actions) + var/datum/action/A = X + A.Grant(user) + +/obj/item/clothing/suit/space/hardsuit/dropped(mob/user) + ..() + if(jetpack) + for(var/X in jetpack.actions) + var/datum/action/A = X + A.Remove(user) + +/obj/item/clothing/suit/space/hardsuit/item_action_slot_check(slot) + if(slot == slot_wear_suit) //we only give the mob the ability to toggle the helmet if he's wearing the hardsuit. + return 1 + +//Engineering hardsuit +/obj/item/clothing/head/helmet/space/hardsuit/engine + name = "engineering hardsuit helmet" + desc = "A special helmet designed for work in a hazardous, low-pressure environment. Has radiation shielding." + icon_state = "hardsuit0-engineering" + item_state = "eng_helm" + armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 75, "fire" = 100, "acid" = 75) + resistance_flags = FIRE_PROOF + item_color = "engineering" + +/obj/item/clothing/suit/space/hardsuit/engine + name = "engineering hardsuit" + desc = "A special suit that protects against hazardous, low pressure environments. Has radiation shielding." + icon_state = "hardsuit-engineering" + item_state = "eng_hardsuit" + armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 75, "fire" = 100, "acid" = 75) + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/engine + dog_fashion = /datum/dog_fashion/back/hardsuit + resistance_flags = FIRE_PROOF + +//Atmospherics +/obj/item/clothing/head/helmet/space/hardsuit/engine/atmos + name = "atmospherics hardsuit helmet" + desc = "A special helmet designed for work in a hazardous, low-pressure environment. Has thermal shielding." + icon_state = "hardsuit0-atmos" + item_state = "atmos_helm" + item_color = "atmos" + armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 25, "fire" = 100, "acid" = 75) + heat_protection = HEAD //Uncomment to enable firesuit protection + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + +/obj/item/clothing/suit/space/hardsuit/engine/atmos + name = "atmospherics hardsuit" + desc = "A special suit that protects against hazardous, low pressure environments. Has thermal shielding." + icon_state = "hardsuit-atmos" + item_state = "atmos_hardsuit" + armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 25, "fire" = 100, "acid" = 75) + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS //Uncomment to enable firesuit protection + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/engine/atmos + dog_fashion = null + +//Chief Engineer's hardsuit +/obj/item/clothing/head/helmet/space/hardsuit/engine/elite + name = "advanced hardsuit helmet" + desc = "An advanced helmet designed for work in a hazardous, low pressure environment. Shines with a high polish." + icon_state = "hardsuit0-white" + item_state = "ce_helm" + item_color = "white" + armor = list("melee" = 40, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 90) + heat_protection = HEAD //Uncomment to enable firesuit protection + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + +/obj/item/clothing/suit/space/hardsuit/engine/elite + icon_state = "hardsuit-white" + name = "advanced hardsuit" + desc = "An advanced suit that protects against hazardous, low pressure environments. Shines with a high polish." + item_state = "ce_hardsuit" + armor = list("melee" = 40, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 90, "fire" = 100, "acid" = 90) + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS //Uncomment to enable firesuit protection + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/engine/elite + jetpack = /obj/item/tank/jetpack/suit + dog_fashion = null + +//Mining hardsuit +/obj/item/clothing/head/helmet/space/hardsuit/mining + name = "mining hardsuit helmet" + desc = "A special helmet designed for work in a hazardous, low pressure environment. Has reinforced plating." + icon_state = "hardsuit0-mining" + item_state = "mining_helm" + item_color = "mining" + max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT + resistance_flags = FIRE_PROOF + heat_protection = HEAD + armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 75) + brightness_on = 7 + +/obj/item/clothing/suit/space/hardsuit/mining + icon_state = "hardsuit-mining" + name = "mining hardsuit" + desc = "A special suit that protects against hazardous, low pressure environments. Has reinforced plating." + item_state = "mining_hardsuit" + max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT + resistance_flags = FIRE_PROOF + armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 75) + allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/storage/bag/ore, /obj/item/pickaxe, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator) + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/mining + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + +//Syndicate hardsuit +/obj/item/clothing/head/helmet/space/hardsuit/syndi + name = "blood-red hardsuit helmet" + desc = "A dual-mode advanced helmet designed for work in special operations. It is in travel mode. Property of Gorlex Marauders." + alt_desc = "A dual-mode advanced helmet designed for work in special operations. It is in combat mode. Property of Gorlex Marauders." + icon_state = "hardsuit1-syndi" + item_state = "syndie_helm" + item_color = "syndi" + armor = list("melee" = 40, "bullet" = 50, "laser" = 30, "energy" = 15, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 90) + on = 1 + var/obj/item/clothing/suit/space/hardsuit/syndi/linkedsuit = null + actions_types = list(/datum/action/item_action/toggle_helmet_mode) + visor_flags_inv = HIDEMASK|HIDEEYES|HIDEFACE|HIDETAIL + visor_flags = STOPSPRESSUREDMAGE + +/obj/item/clothing/head/helmet/space/hardsuit/syndi/update_icon() + icon_state = "hardsuit[on]-[item_color]" + +/obj/item/clothing/head/helmet/space/hardsuit/syndi/New() + ..() + if(istype(loc, /obj/item/clothing/suit/space/hardsuit/syndi)) + linkedsuit = loc + +/obj/item/clothing/head/helmet/space/hardsuit/syndi/attack_self(mob/user) //Toggle Helmet + if(!isturf(user.loc)) + to_chat(user, "You cannot toggle your helmet while in this [user.loc]!" ) + return + on = !on + if(on) + to_chat(user, "You switch your hardsuit to EVA mode, sacrificing speed for space protection.") + name = initial(name) + desc = initial(desc) + set_light(brightness_on) + flags |= visor_flags + flags_cover |= HEADCOVERSEYES | HEADCOVERSMOUTH + flags_inv |= visor_flags_inv + cold_protection |= HEAD + else + to_chat(user, "You switch your hardsuit to combat mode and can now run at full speed.") + name += " (combat)" + desc = alt_desc + set_light(0) + flags &= ~visor_flags + flags_cover &= ~(HEADCOVERSEYES | HEADCOVERSMOUTH) + flags_inv &= ~visor_flags_inv + cold_protection &= ~HEAD + update_icon() + playsound(src.loc, 'sound/mecha/mechmove03.ogg', 50, 1) + toggle_hardsuit_mode(user) + user.update_inv_head() + if(iscarbon(user)) + var/mob/living/carbon/C = user + C.head_update(src, forced = 1) + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + +/obj/item/clothing/head/helmet/space/hardsuit/syndi/proc/toggle_hardsuit_mode(mob/user) //Helmet Toggles Suit Mode + if(linkedsuit) + if(on) + linkedsuit.name = initial(linkedsuit.name) + linkedsuit.desc = initial(linkedsuit.desc) + linkedsuit.slowdown = 1 + linkedsuit.flags |= STOPSPRESSUREDMAGE + linkedsuit.cold_protection |= UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS + else + linkedsuit.name += " (combat)" + linkedsuit.desc = linkedsuit.alt_desc + linkedsuit.slowdown = 0 + linkedsuit.flags &= ~STOPSPRESSUREDMAGE + linkedsuit.cold_protection &= ~(UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS) + + linkedsuit.update_icon() + user.update_inv_wear_suit() + user.update_inv_w_uniform() + +/obj/item/clothing/suit/space/hardsuit/syndi + name = "blood-red hardsuit" + desc = "A dual-mode advanced hardsuit designed for work in special operations. It is in travel mode. Property of Gorlex Marauders." + alt_desc = "A dual-mode advanced hardsuit designed for work in special operations. It is in combat mode. Property of Gorlex Marauders." + icon_state = "hardsuit1-syndi" + item_state = "syndie_hardsuit" + item_color = "syndi" + w_class = WEIGHT_CLASS_NORMAL + var/on = 1 + actions_types = list(/datum/action/item_action/toggle_hardsuit_mode) + armor = list("melee" = 40, "bullet" = 50, "laser" = 30, "energy" = 15, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 50, "acid" = 90) + allowed = list(/obj/item/gun, /obj/item/ammo_box,/obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/melee/energy/sword, /obj/item/restraints/handcuffs, /obj/item/tank) + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi + jetpack = /obj/item/tank/jetpack/suit + +/obj/item/clothing/suit/space/hardsuit/syndi/update_icon() + icon_state = "hardsuit[on]-[item_color]" + +//Elite Syndie suit +/obj/item/clothing/head/helmet/space/hardsuit/syndi/elite + name = "elite syndicate hardsuit helmet" + desc = "An elite version of the syndicate helmet, with improved armour and fire shielding. It is in travel mode. Property of Gorlex Marauders." + icon_state = "hardsuit0-syndielite" + item_color = "syndielite" + armor = list("melee" = 60, "bullet" = 60, "laser" = 50, "energy" = 25, "bomb" = 55, "bio" = 100, "rad" = 70, "fire" = 100, "acid" = 100) + heat_protection = HEAD + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = FIRE_PROOF | ACID_PROOF + +/obj/item/clothing/suit/space/hardsuit/syndi/elite + name = "elite syndicate hardsuit" + desc = "An elite version of the syndicate hardsuit, with improved armour and fire shielding. It is in travel mode." + icon_state = "hardsuit0-syndielite" + item_color = "syndielite" + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi/elite + armor = list("melee" = 60, "bullet" = 60, "laser" = 50, "energy" = 25, "bomb" = 55, "bio" = 100, "rad" = 70, "fire" = 100, "acid" = 100) + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = FIRE_PROOF | ACID_PROOF + +//Strike team hardsuits +/obj/item/clothing/head/helmet/space/hardsuit/syndi/elite/sst + armor = list(melee = 70, bullet = 70, laser = 50, energy = 40, bomb = 80, bio = 100, rad = 100, fire = 100, acid = 100) //Almost as good as DS gear, but unlike DS can switch to combat for mobility + icon_state = "hardsuit0-sst" + item_color = "sst" + +/obj/item/clothing/suit/space/hardsuit/syndi/elite/sst + armor = list(melee = 70, bullet = 70, laser = 50, energy = 40, bomb = 80, bio = 100, rad = 100, fire = 100, acid = 100) + icon_state = "hardsuit0-sst" + item_color = "sst" + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi/elite/sst + +/obj/item/clothing/suit/space/hardsuit/syndi/freedom + name = "eagle suit" + desc = "An advanced, light suit, fabricated from a mixture of synthetic feathers and space-resistant material. A gun holster appears to be integrated into the suit." + icon_state = "freedom" + item_state = "freedom" + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/syndi/freedom + sprite_sheets = null + +/obj/item/clothing/suit/space/hardsuit/syndi/freedom/update_icon() + return + +/obj/item/clothing/head/helmet/space/hardsuit/syndi/freedom + name = "eagle helmet" + desc = "An advanced, space-proof helmet. It appears to be modeled after an old-world eagle." + icon_state = "griffinhat" + item_state = "griffinhat" + sprite_sheets = null + +/obj/item/clothing/head/helmet/space/hardsuit/syndi/freedom/update_icon() + return + +//Medical hardsuit +/obj/item/clothing/head/helmet/space/hardsuit/medical + name = "medical hardsuit helmet" + desc = "A special helmet designed for work in a hazardous, low pressure environment. Built with lightweight materials for extra comfort, but does not protect the eyes from intense light." + icon_state = "hardsuit0-medical" + item_state = "medical_helm" + item_color = "medical" + flash_protect = 0 + armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 60, "fire" = 60, "acid" = 75) + flags = STOPSPRESSUREDMAGE | THICKMATERIAL + scan_reagents = 1 //Generally worn by the CMO, so they'd get utility off of seeing reagents + +/obj/item/clothing/suit/space/hardsuit/medical + icon_state = "hardsuit-medical" + name = "medical hardsuit" + desc = "A special helmet designed for work in a hazardous, low pressure environment. Built with lightweight materials for extra comfort." + item_state = "medical_hardsuit" + allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/storage/firstaid,/obj/item/healthanalyzer,/obj/item/stack/medical,/obj/item/rad_laser) + armor = list("melee" = 30, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 10, "bio" = 100, "rad" = 60, "fire" = 60, "acid" = 75) + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/medical + slowdown = 0.5 + + //Security +/obj/item/clothing/head/helmet/space/hardsuit/security + name = "security hardsuit helmet" + desc = "A special helmet designed for work in a hazardous, low pressure environment. Has an additional layer of armor." + icon_state = "hardsuit0-sec" + item_state = "sec_helm" + item_color = "sec" + armor = list("melee" = 35, "bullet" = 15, "laser" = 30,"energy" = 10, "bomb" = 10, "bio" = 100, "rad" = 50, "fire" = 75, "acid" = 75) + +/obj/item/clothing/suit/space/hardsuit/security + icon_state = "hardsuit-sec" + name = "security hardsuit" + desc = "A special suit that protects against hazardous, low pressure environments. Has an additional layer of armor." + item_state = "sec_hardsuit" + armor = list("melee" = 35, "bullet" = 15, "laser" = 30, "energy" = 10, "bomb" = 10, "bio" = 100, "rad" = 50, "fire" = 75, "acid" = 75) + allowed = list(/obj/item/gun,/obj/item/flashlight,/obj/item/tank,/obj/item/melee/baton,/obj/item/reagent_containers/spray/pepper,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/restraints/handcuffs) + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/security + +/obj/item/clothing/head/helmet/space/hardsuit/security/hos + name = "head of security's hardsuit helmet" + desc = "A special bulky helmet designed for work in a hazardous, low pressure environment. Has an additional layer of armor." + icon_state = "hardsuit0-hos" + item_color = "hos" + armor = list("melee" = 45, "bullet" = 25, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 100, "rad" = 50, "fire" = 95, "acid" = 95) + +/obj/item/clothing/suit/space/hardsuit/security/hos + name = "head of security's hardsuit" + desc = "A special bulky suit that protects against hazardous, low pressure environments. Has an additional layer of armor." + icon_state = "hardsuit-hos" + armor = list("melee" = 45, "bullet" = 25, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 100, "rad" = 50, "fire" = 95, "acid" = 95) + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/security/hos + jetpack = /obj/item/tank/jetpack/suit + + +//Singuloth armor +/obj/item/clothing/head/helmet/space/hardsuit/singuloth + name = "singuloth knight's helmet" + desc = "This is an adamantium helmet from the chapter of the Singuloth Knights. It shines with a holy aura." + icon_state = "hardsuit0-singuloth" + item_state = "singuloth_helm" + item_color = "singuloth" + armor = list(melee = 40, bullet = 5, laser = 20, energy = 5, bomb = 25, bio = 100, rad = 100, fire = 95, acid = 95) + sprite_sheets = null + +/obj/item/clothing/suit/space/hardsuit/singuloth + icon_state = "hardsuit-singuloth" + name = "singuloth knight's armor" + desc = "This is a ceremonial armor from the chapter of the Singuloth Knights. It's made of pure forged adamantium." + item_state = "singuloth_hardsuit" + flags = STOPSPRESSUREDMAGE + armor = list(melee = 45, bullet = 25, laser = 30, energy = 10, bomb = 25, bio = 100, rad = 100, fire = 95, acid = 95) + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/singuloth + sprite_sheets = null + + +/////////////SHIELDED////////////////////////////////// + +/obj/item/clothing/suit/space/hardsuit/shielded + name = "shielded hardsuit" + desc = "A hardsuit with built in energy shielding. Will rapidly recharge when not under fire." + icon_state = "hardsuit-hos" + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/shielded + allowed = list(/obj/item/flashlight,/obj/item/tank, /obj/item/gun,/obj/item/reagent_containers/spray/pepper,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs) + armor = list("melee" = 30, "bullet" = 15, "laser" = 30, "energy" = 10, "bomb" = 10, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) + resistance_flags = FIRE_PROOF | ACID_PROOF + var/current_charges = 3 + var/max_charges = 3 //How many charges total the shielding has + var/recharge_delay = 200 //How long after we've been shot before we can start recharging. 20 seconds here + var/recharge_cooldown = 0 //Time since we've last been shot + var/recharge_rate = 1 //How quickly the shield recharges once it starts charging + var/shield_state = "shield-old" + var/shield_on = "shield-old" + +/obj/item/clothing/suit/space/hardsuit/shielded/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + recharge_cooldown = world.time + recharge_delay + if(current_charges > 0) + do_sparks(2, 1, src) + owner.visible_message("[owner]'s shields deflect [attack_text] in a shower of sparks!") + current_charges-- + if(recharge_rate) + START_PROCESSING(SSobj, src) + if(current_charges <= 0) + owner.visible_message("[owner]'s shield overloads!") + shield_state = "broken" + owner.update_inv_wear_suit() + return 1 + return 0 + + + +/obj/item/clothing/suit/space/hardsuit/shielded/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/clothing/suit/space/hardsuit/shielded/process() + if(world.time > recharge_cooldown && current_charges < max_charges) + current_charges = Clamp((current_charges + recharge_rate), 0, max_charges) + playsound(loc, 'sound/magic/charge.ogg', 50, TRUE) + if(current_charges == max_charges) + playsound(loc, 'sound/machines/ding.ogg', 50, TRUE) + STOP_PROCESSING(SSobj, src) + shield_state = "[shield_on]" + if(ishuman(loc)) + var/mob/living/carbon/human/C = loc + C.update_inv_wear_suit() + +/obj/item/clothing/suit/space/hardsuit/shielded/special_overlays() + return mutable_appearance('icons/effects/effects.dmi', shield_state, MOB_LAYER + 0.01) + +/obj/item/clothing/head/helmet/space/hardsuit/shielded + resistance_flags = FIRE_PROOF | ACID_PROOF + + +//////Syndicate Version + +/obj/item/clothing/suit/space/hardsuit/shielded/syndi + name = "blood-red hardsuit" + desc = "An advanced hardsuit with built in energy shielding." + icon_state = "hardsuit1-syndi" + item_state = "syndie_hardsuit" + item_color = "syndi" + armor = list("melee" = 40, "bullet" = 50, "laser" = 30, "energy" = 15, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) + allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword/saber,/obj/item/restraints/handcuffs,/obj/item/tank) + slowdown = 0 + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/shielded/syndi + jetpack = /obj/item/tank/jetpack/suit + +/obj/item/clothing/suit/space/hardsuit/shielded/syndi/multitool_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(shield_state == "broken") + to_chat(user, "You can't interface with the hardsuit's software if the shield's broken!") + return + + if(shield_state == "shield-red") + shield_state = "shield-old" + shield_on = "shield-old" + to_chat(user, "You roll back the hardsuit's software, changing the shield's color!") + + else + shield_state = "shield-red" + shield_on = "shield-red" + to_chat(user, "You update the hardsuit's hardware, changing back the shield's color to red.") + user.update_inv_wear_suit() + +/obj/item/clothing/head/helmet/space/hardsuit/shielded/syndi + name = "blood-red hardsuit helmet" + desc = "An advanced hardsuit helmet with built in energy shielding." + icon_state = "hardsuit1-syndi" + item_state = "syndie_helm" + item_color = "syndi" + armor = list("melee" = 40, "bullet" = 50, "laser" = 30, "energy" = 15, "bomb" = 35, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) diff --git a/code/modules/clothing/spacesuits/miscellaneous.dm b/code/modules/clothing/spacesuits/miscellaneous.dm index f516627e182..f6ab79e628a 100644 --- a/code/modules/clothing/spacesuits/miscellaneous.dm +++ b/code/modules/clothing/spacesuits/miscellaneous.dm @@ -1,287 +1,287 @@ - //Captain's space suit, not hardsuits because no flashlight! -/obj/item/clothing/head/helmet/space/capspace - name = "captain's space helmet" - icon_state = "capspace" - item_state = "capspacehelmet" - desc = "A special helmet designed for only the most fashionable of military figureheads." - flags_inv = HIDEFACE - permeability_coefficient = 0.01 - armor = list("melee" = 40, "bullet" = 50, "laser" = 50, "energy" = 25, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) - species_restricted = list("exclude", "Wryn") - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi', - "Grey" = 'icons/mob/species/grey/helmet.dmi' - ) - -/obj/item/clothing/head/helmet/space/capspace/equipped(mob/living/carbon/human/user, slot) - if(ishuman(user) && slot == slot_head) - if(isvox(user)) - if(flags & BLOCKHAIR) - flags &= ~BLOCKHAIR - else - if((initial(flags) & BLOCKHAIR) && !(flags & BLOCKHAIR)) - flags |= BLOCKHAIR - -/obj/item/clothing/suit/space/captain - name = "captain's space suit" - desc = "A bulky, heavy-duty piece of exclusive Nanotrasen armor. YOU are in charge!" - icon_state = "caparmor" - item_state = "capspacesuit" - w_class = WEIGHT_CLASS_BULKY - allowed = list(/obj/item/tank, /obj/item/flashlight,/obj/item/gun/energy, /obj/item/gun/projectile, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton,/obj/item/restraints/handcuffs) - armor = list("melee" = 40, "bullet" = 50, "laser" = 50, "energy" = 25, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) - species_restricted = list("exclude", "Wryn") - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - - //Deathsquad space suit, not hardsuits because no flashlight! -/obj/item/clothing/head/helmet/space/deathsquad - name = "deathsquad helmet" - desc = "That's not red paint. That's real blood." - icon_state = "deathsquad" - item_state = "deathsquad" - armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = FIRE_PROOF | ACID_PROOF - vision_flags = SEE_MOBS - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these - see_in_dark = 8 - HUDType = MEDHUD - strip_delay = 130 - -/obj/item/clothing/suit/space/deathsquad - name = "deathsquad suit" - desc = "A heavily armored, advanced space suit that protects against most forms of damage." - icon_state = "deathsquad" - item_state = "swat_suit" - allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/tank,/obj/item/kitchen/knife/combat) - armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = FIRE_PROOF | ACID_PROOF - strip_delay = 130 - dog_fashion = /datum/dog_fashion/back/deathsquad - - //NEW SWAT suit -/obj/item/clothing/suit/space/swat - name = "SWAT armor" - desc = "Space-proof tactical SWAT armor." - icon_state = "heavy" - item_state = "swat_suit" - allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/tank,/obj/item/kitchen/knife/combat) - armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 30, "bomb" = 50, "bio" = 90, "rad" = 20, "fire" = 100, "acid" = 100) - strip_delay = 120 - resistance_flags = FIRE_PROOF | ACID_PROOF - species_restricted = list("exclude", "Wryn") - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/obj/item/clothing/head/helmet/space/deathsquad/beret - name = "officer's beret" - desc = "An armored beret commonly used by special operations officers." - icon_state = "beret_officer" - armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) - flags = STOPSPRESSUREDMAGE | THICKMATERIAL - -/obj/item/clothing/suit/space/deathsquad/officer - name = "officer jacket" - desc = "An armored jacket used in special operations." - icon_state = "detective" - item_state = "det_suit" - blood_overlay_type = "coat" - flags_inv = 0 - slowdown = 0 - armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) - resistance_flags = FIRE_PROOF | ACID_PROOF - w_class = WEIGHT_CLASS_NORMAL - -//Space santa outfit suit -/obj/item/clothing/head/helmet/space/santahat - name = "Santa's hat" - desc = "Ho ho ho. Merrry X-mas!" - icon_state = "santahat" - - sprite_sheets = list( - "Grey" = 'icons/mob/species/Grey/head.dmi', - "Drask" = 'icons/mob/species/Drask/helmet.dmi' - ) - flags = BLOCKHAIR | STOPSPRESSUREDMAGE - flags_cover = HEADCOVERSEYES - dog_fashion = /datum/dog_fashion/head/santa - -/obj/item/clothing/head/helmet/space/santahat/attack_self(mob/user as mob) - if(src.icon_state == "santahat") - src.icon_state = "santahat_beard" - src.item_state = "santahat_beard" - to_chat(user, "Santa's beard expands out from the hat!") - else - src.icon_state = "santahat" - src.item_state = "santahat" - to_chat(user, "The beard slinks back into the hat...") - -/obj/item/clothing/suit/space/santa - name = "Santa's suit" - desc = "Festive!" - icon_state = "santa" - item_state = "santa" - slowdown = 0 - flags = STOPSPRESSUREDMAGE - flags_size = ONESIZEFITSALL - allowed = list(/obj/item) //for stuffing extra special presents - -//Space pirate outfit -/obj/item/clothing/head/helmet/space/pirate - name = "pirate hat" - desc = "Yarr." - icon_state = "pirate" - item_state = "pirate" - armor = list("melee" = 30, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 60, "acid" = 75) - flags = BLOCKHAIR | STOPSPRESSUREDMAGE - flags_cover = HEADCOVERSEYES - strip_delay = 40 - put_on_delay = 20 - -/obj/item/clothing/suit/space/pirate - name = "pirate coat" - desc = "Yarr." - icon_state = "pirate" - item_state = "pirate" - w_class = WEIGHT_CLASS_NORMAL - allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/tank) - slowdown = 0 - armor = list("melee" = 30, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 60, "acid" = 75) - strip_delay = 40 - put_on_delay = 20 - -//Paramedic EVA suit -/obj/item/clothing/head/helmet/space/eva/paramedic - name = "Paramedic EVA helmet" - desc = "A brand new paramedic EVA helmet. It seems to mold to your head shape. Used for retrieving bodies in space." - icon_state = "paramedic-eva-helmet" - item_state = "paramedic-eva-helmet" - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 100, rad = 20, fire = 50, acid = 65) - species_restricted = list("exclude", "Wryn") - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi', - "Grey" = 'icons/mob/species/grey/helmet.dmi', - "Skrell" = 'icons/mob/species/skrell/helmet.dmi', - "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', - "Drask" = 'icons/mob/species/drask/helmet.dmi', - "Unathi" = 'icons/mob/species/unathi/helmet.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', - ) - sprite_sheets_obj = list( - "Vox" = 'icons/obj/clothing/species/vox/hats.dmi' - ) - -/obj/item/clothing/suit/space/eva/paramedic - name = "Paramedic EVA suit" - icon_state = "paramedic-eva" - item_state = "paramedic-eva" - desc = "A brand new paramedic EVA suit. The nitrile seems a bit too thin to be space proof. Used for retrieving bodies in space." - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 100, rad = 20, fire = 50, acid = 65) - species_restricted = list("exclude", "Wryn") - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi', - "Skrell" = 'icons/mob/species/skrell/suit.dmi', - "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', - "Drask" = 'icons/mob/species/drask/suit.dmi', - "Unathi" = 'icons/mob/species/unathi/suit.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', - ) - sprite_sheets_obj = list( - "Vox" = 'icons/obj/clothing/species/vox/suits.dmi' - ) - -/obj/item/clothing/suit/space/eva - name = "EVA suit" - icon_state = "spacenew" - item_state = "s_suit" - desc = "A lightweight space suit with the basic ability to protect the wearer from the vacuum of space during emergencies." - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 100, rad = 20, fire = 50, acid = 65) - species_restricted = list("exclude", "Wryn") - - sprite_sheets = list( - "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', - "Unathi" = 'icons/mob/species/unathi/suit.dmi', - "Vox" = 'icons/mob/species/vox/suit.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', - ) - sprite_sheets_obj = list( - "Tajaran" = 'icons/obj/clothing/species/tajaran/suits.dmi', - "Unathi" = 'icons/obj/clothing/species/unathi/suits.dmi', - "Vox" = 'icons/obj/clothing/species/vox/suits.dmi', - "Vulpkanin" = 'icons/obj/clothing/species/vulpkanin/suits.dmi' - ) - -/obj/item/clothing/head/helmet/space/eva - name = "EVA helmet" - icon_state = "spacenew" - item_state = "s_helmet" - desc = "A lightweight space helmet with the basic ability to protect the wearer from the vacuum of space during emergencies." - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 100, rad = 20, fire = 50, acid = 65) - flash_protect = 0 - species_restricted = list("exclude", "Wryn") - sprite_sheets = list( - "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', - "Unathi" = 'icons/mob/species/unathi/helmet.dmi', - "Vox" = 'icons/mob/species/vox/helmet.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', - "Grey" = 'icons/mob/species/grey/helmet.dmi' - ) - sprite_sheets_obj = list( - "Vox" = 'icons/obj/clothing/species/vox/hats.dmi', - "Vulpkanin" = 'icons/obj/clothing/species/vulpkanin/hats.dmi' - ) - -//Mime's Hardsuit -/obj/item/clothing/head/helmet/space/eva/mime - name = "mime eva helmet" -// icon = 'spaceciv.dmi' - desc = ". . ." - icon_state = "spacemimehelmet" - item_state = "spacemimehelmet" - species_restricted = list("exclude","Wryn") - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi') - sprite_sheets_obj = null - -/obj/item/clothing/suit/space/eva/mime - name = "mime eva suit" -// icon = 'spaceciv.dmi' - desc = ". . ." - icon_state = "spacemime_suit" - item_state = "spacemime_items" - species_restricted = list("exclude","Wryn") - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi') - sprite_sheets_obj = null - -/obj/item/clothing/head/helmet/space/eva/clown - name = "clown eva helmet" -// icon = 'spaceciv.dmi' - desc = "An EVA helmet specifically designed for the clown. SPESSHONK!" - icon_state = "clownhelmet" - item_state = "clownhelmet" - species_restricted = list("exclude","Wryn") - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi') - sprite_sheets_obj = null - -/obj/item/clothing/suit/space/eva/clown - name = "clown eva suit" -// icon = 'spaceciv.dmi' - desc = "An EVA suit specifically designed for the clown. SPESSHONK!" - icon_state = "spaceclown_suit" - item_state = "spaceclown_items" - species_restricted = list("exclude","Wryn") - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi') - sprite_sheets_obj = null + //Captain's space suit, not hardsuits because no flashlight! +/obj/item/clothing/head/helmet/space/capspace + name = "captain's space helmet" + icon_state = "capspace" + item_state = "capspacehelmet" + desc = "A special helmet designed for only the most fashionable of military figureheads." + flags_inv = HIDEFACE + permeability_coefficient = 0.01 + armor = list("melee" = 40, "bullet" = 50, "laser" = 50, "energy" = 25, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) + species_restricted = list("exclude", "Wryn") + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi', + "Grey" = 'icons/mob/species/grey/helmet.dmi' + ) + +/obj/item/clothing/head/helmet/space/capspace/equipped(mob/living/carbon/human/user, slot) + if(ishuman(user) && slot == slot_head) + if(isvox(user)) + if(flags & BLOCKHAIR) + flags &= ~BLOCKHAIR + else + if((initial(flags) & BLOCKHAIR) && !(flags & BLOCKHAIR)) + flags |= BLOCKHAIR + +/obj/item/clothing/suit/space/captain + name = "captain's space suit" + desc = "A bulky, heavy-duty piece of exclusive Nanotrasen armor. YOU are in charge!" + icon_state = "caparmor" + item_state = "capspacesuit" + w_class = WEIGHT_CLASS_BULKY + allowed = list(/obj/item/tank, /obj/item/flashlight,/obj/item/gun/energy, /obj/item/gun/projectile, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton,/obj/item/restraints/handcuffs) + armor = list("melee" = 40, "bullet" = 50, "laser" = 50, "energy" = 25, "bomb" = 50, "bio" = 100, "rad" = 50, "fire" = 100, "acid" = 100) + species_restricted = list("exclude", "Wryn") + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + + //Deathsquad space suit, not hardsuits because no flashlight! +/obj/item/clothing/head/helmet/space/deathsquad + name = "deathsquad helmet" + desc = "That's not red paint. That's real blood." + icon_state = "deathsquad" + item_state = "deathsquad" + armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = FIRE_PROOF | ACID_PROOF + vision_flags = SEE_MOBS + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE //don't render darkness while wearing these + see_in_dark = 8 + HUDType = MEDHUD + strip_delay = 130 + +/obj/item/clothing/suit/space/deathsquad + name = "deathsquad suit" + desc = "A heavily armored, advanced space suit that protects against most forms of damage." + icon_state = "deathsquad" + item_state = "swat_suit" + allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/tank,/obj/item/kitchen/knife/combat) + armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = FIRE_PROOF | ACID_PROOF + strip_delay = 130 + dog_fashion = /datum/dog_fashion/back/deathsquad + + //NEW SWAT suit +/obj/item/clothing/suit/space/swat + name = "SWAT armor" + desc = "Space-proof tactical SWAT armor." + icon_state = "heavy" + item_state = "swat_suit" + allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/tank,/obj/item/kitchen/knife/combat) + armor = list("melee" = 40, "bullet" = 30, "laser" = 30,"energy" = 30, "bomb" = 50, "bio" = 90, "rad" = 20, "fire" = 100, "acid" = 100) + strip_delay = 120 + resistance_flags = FIRE_PROOF | ACID_PROOF + species_restricted = list("exclude", "Wryn") + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/obj/item/clothing/head/helmet/space/deathsquad/beret + name = "officer's beret" + desc = "An armored beret commonly used by special operations officers." + icon_state = "beret_officer" + armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) + flags = STOPSPRESSUREDMAGE | THICKMATERIAL + +/obj/item/clothing/suit/space/deathsquad/officer + name = "officer jacket" + desc = "An armored jacket used in special operations." + icon_state = "detective" + item_state = "det_suit" + blood_overlay_type = "coat" + flags_inv = 0 + slowdown = 0 + armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) + resistance_flags = FIRE_PROOF | ACID_PROOF + w_class = WEIGHT_CLASS_NORMAL + +//Space santa outfit suit +/obj/item/clothing/head/helmet/space/santahat + name = "Santa's hat" + desc = "Ho ho ho. Merrry X-mas!" + icon_state = "santahat" + + sprite_sheets = list( + "Grey" = 'icons/mob/species/Grey/head.dmi', + "Drask" = 'icons/mob/species/Drask/helmet.dmi' + ) + flags = BLOCKHAIR | STOPSPRESSUREDMAGE + flags_cover = HEADCOVERSEYES + dog_fashion = /datum/dog_fashion/head/santa + +/obj/item/clothing/head/helmet/space/santahat/attack_self(mob/user as mob) + if(src.icon_state == "santahat") + src.icon_state = "santahat_beard" + src.item_state = "santahat_beard" + to_chat(user, "Santa's beard expands out from the hat!") + else + src.icon_state = "santahat" + src.item_state = "santahat" + to_chat(user, "The beard slinks back into the hat...") + +/obj/item/clothing/suit/space/santa + name = "Santa's suit" + desc = "Festive!" + icon_state = "santa" + item_state = "santa" + slowdown = 0 + flags = STOPSPRESSUREDMAGE + flags_size = ONESIZEFITSALL + allowed = list(/obj/item) //for stuffing extra special presents + +//Space pirate outfit +/obj/item/clothing/head/helmet/space/pirate + name = "pirate hat" + desc = "Yarr." + icon_state = "pirate" + item_state = "pirate" + armor = list("melee" = 30, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 60, "acid" = 75) + flags = BLOCKHAIR | STOPSPRESSUREDMAGE + flags_cover = HEADCOVERSEYES + strip_delay = 40 + put_on_delay = 20 + +/obj/item/clothing/suit/space/pirate + name = "pirate coat" + desc = "Yarr." + icon_state = "pirate" + item_state = "pirate" + w_class = WEIGHT_CLASS_NORMAL + allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/tank) + slowdown = 0 + armor = list("melee" = 30, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 60, "acid" = 75) + strip_delay = 40 + put_on_delay = 20 + +//Paramedic EVA suit +/obj/item/clothing/head/helmet/space/eva/paramedic + name = "Paramedic EVA helmet" + desc = "A brand new paramedic EVA helmet. It seems to mold to your head shape. Used for retrieving bodies in space." + icon_state = "paramedic-eva-helmet" + item_state = "paramedic-eva-helmet" + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 100, rad = 20, fire = 50, acid = 65) + species_restricted = list("exclude", "Wryn") + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi', + "Grey" = 'icons/mob/species/grey/helmet.dmi', + "Skrell" = 'icons/mob/species/skrell/helmet.dmi', + "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', + "Drask" = 'icons/mob/species/drask/helmet.dmi', + "Unathi" = 'icons/mob/species/unathi/helmet.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', + ) + sprite_sheets_obj = list( + "Vox" = 'icons/obj/clothing/species/vox/hats.dmi' + ) + +/obj/item/clothing/suit/space/eva/paramedic + name = "Paramedic EVA suit" + icon_state = "paramedic-eva" + item_state = "paramedic-eva" + desc = "A brand new paramedic EVA suit. The nitrile seems a bit too thin to be space proof. Used for retrieving bodies in space." + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 100, rad = 20, fire = 50, acid = 65) + species_restricted = list("exclude", "Wryn") + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi', + "Skrell" = 'icons/mob/species/skrell/suit.dmi', + "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', + "Drask" = 'icons/mob/species/drask/suit.dmi', + "Unathi" = 'icons/mob/species/unathi/suit.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', + ) + sprite_sheets_obj = list( + "Vox" = 'icons/obj/clothing/species/vox/suits.dmi' + ) + +/obj/item/clothing/suit/space/eva + name = "EVA suit" + icon_state = "spacenew" + item_state = "s_suit" + desc = "A lightweight space suit with the basic ability to protect the wearer from the vacuum of space during emergencies." + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 100, rad = 20, fire = 50, acid = 65) + species_restricted = list("exclude", "Wryn") + + sprite_sheets = list( + "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', + "Unathi" = 'icons/mob/species/unathi/suit.dmi', + "Vox" = 'icons/mob/species/vox/suit.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', + ) + sprite_sheets_obj = list( + "Tajaran" = 'icons/obj/clothing/species/tajaran/suits.dmi', + "Unathi" = 'icons/obj/clothing/species/unathi/suits.dmi', + "Vox" = 'icons/obj/clothing/species/vox/suits.dmi', + "Vulpkanin" = 'icons/obj/clothing/species/vulpkanin/suits.dmi' + ) + +/obj/item/clothing/head/helmet/space/eva + name = "EVA helmet" + icon_state = "spacenew" + item_state = "s_helmet" + desc = "A lightweight space helmet with the basic ability to protect the wearer from the vacuum of space during emergencies." + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 100, rad = 20, fire = 50, acid = 65) + flash_protect = 0 + species_restricted = list("exclude", "Wryn") + sprite_sheets = list( + "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', + "Unathi" = 'icons/mob/species/unathi/helmet.dmi', + "Vox" = 'icons/mob/species/vox/helmet.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', + "Grey" = 'icons/mob/species/grey/helmet.dmi' + ) + sprite_sheets_obj = list( + "Vox" = 'icons/obj/clothing/species/vox/hats.dmi', + "Vulpkanin" = 'icons/obj/clothing/species/vulpkanin/hats.dmi' + ) + +//Mime's Hardsuit +/obj/item/clothing/head/helmet/space/eva/mime + name = "mime eva helmet" +// icon = 'spaceciv.dmi' + desc = ". . ." + icon_state = "spacemimehelmet" + item_state = "spacemimehelmet" + species_restricted = list("exclude","Wryn") + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi') + sprite_sheets_obj = null + +/obj/item/clothing/suit/space/eva/mime + name = "mime eva suit" +// icon = 'spaceciv.dmi' + desc = ". . ." + icon_state = "spacemime_suit" + item_state = "spacemime_items" + species_restricted = list("exclude","Wryn") + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi') + sprite_sheets_obj = null + +/obj/item/clothing/head/helmet/space/eva/clown + name = "clown eva helmet" +// icon = 'spaceciv.dmi' + desc = "An EVA helmet specifically designed for the clown. SPESSHONK!" + icon_state = "clownhelmet" + item_state = "clownhelmet" + species_restricted = list("exclude","Wryn") + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi') + sprite_sheets_obj = null + +/obj/item/clothing/suit/space/eva/clown + name = "clown eva suit" +// icon = 'spaceciv.dmi' + desc = "An EVA suit specifically designed for the clown. SPESSHONK!" + icon_state = "spaceclown_suit" + item_state = "spaceclown_items" + species_restricted = list("exclude","Wryn") + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi') + sprite_sheets_obj = null diff --git a/code/modules/clothing/spacesuits/plasmamen.dm b/code/modules/clothing/spacesuits/plasmamen.dm index 73b95c0d3f7..77d7b99fde0 100644 --- a/code/modules/clothing/spacesuits/plasmamen.dm +++ b/code/modules/clothing/spacesuits/plasmamen.dm @@ -62,14 +62,21 @@ actions_types = list(/datum/action/item_action/toggle_helmet_light) /obj/item/clothing/head/helmet/space/plasmaman/attack_self(mob/user) + toggle_light(user) + +/obj/item/clothing/head/helmet/space/plasmaman/proc/toggle_light(mob/user) on = !on icon_state = "[initial(icon_state)][on ? "-light":""]" item_state = icon_state - user.update_inv_head() //So the mob overlay updates + + var/mob/living/carbon/human/H = user + if(istype(H)) + H.update_inv_head() if(on) if(!up) - to_chat(user, "Your helmet's torch can't pass through your welding visor!") + if(istype(H)) + to_chat(user, "Your helmet's torch can't pass through your welding visor!") set_light(0) else set_light(brightness_on) @@ -80,6 +87,10 @@ var/datum/action/A=X A.UpdateButtonIcon() +/obj/item/clothing/head/helmet/space/plasmaman/extinguish_light() + if(on) + toggle_light() + /obj/item/clothing/head/helmet/space/plasmaman/security name = "security plasma envirosuit helmet" desc = "A plasmaman containment helmet designed for security officers, protecting them from being flashed and burning alive, along-side other undesirables." @@ -247,4 +258,4 @@ name = "blueshield envirosuit helmet" desc = "A plasmaman envirohelm designed for the blueshield." icon_state = "bs_envirohelm" - item_state = "bs_envirohelm" \ No newline at end of file + item_state = "bs_envirohelm" diff --git a/code/modules/clothing/spacesuits/rig/modules/utility.dm b/code/modules/clothing/spacesuits/rig/modules/utility.dm index e87e35a0dea..4b19d2a7753 100644 --- a/code/modules/clothing/spacesuits/rig/modules/utility.dm +++ b/code/modules/clothing/spacesuits/rig/modules/utility.dm @@ -465,18 +465,12 @@ else to_chat(holder.wearer, "You need to have a welding tool in one of your hands to dispense fuel.") -/obj/item/rig_module/welding_tank/proc/fill_welder(var/obj/item/weldingtool/W) +/obj/item/rig_module/welding_tank/proc/fill_welder(obj/item/weldingtool/W) if(!istype(W)) - return 0 - - if(reagents) - if(get_fuel() >= W.max_fuel) - reagents.trans_to(W, W.max_fuel) - to_chat(holder.wearer, "Your [holder] dispenses some of the contents of the welding fuel tank into \the [W].") - else - reagents.trans_to(W, W.max_fuel) - to_chat(holder.wearer, "You hear a faint dripping as your hardsuit welding tank completely empties.") - W.update_icon() + return + W.refill(holder.wearer, src, W.maximum_fuel) + if(!reagents.get_reagent_amount("fuel")) + to_chat(holder.wearer, "You hear a faint dripping as your hardsuit welding tank completely empties.") /obj/item/rig_module/welding_tank/proc/get_fuel() return reagents.get_reagent_amount("fuel") diff --git a/code/modules/clothing/spacesuits/rig/modules/vision.dm b/code/modules/clothing/spacesuits/rig/modules/vision.dm index 9fc960cdbce..33ae007ceee 100644 --- a/code/modules/clothing/spacesuits/rig/modules/vision.dm +++ b/code/modules/clothing/spacesuits/rig/modules/vision.dm @@ -188,4 +188,4 @@ if(!vision) vision = vision_datum processed_vision += vision_datum - vision_modes = processed_vision \ No newline at end of file + vision_modes = processed_vision diff --git a/code/modules/clothing/spacesuits/rig/rig_verbs.dm b/code/modules/clothing/spacesuits/rig/rig_verbs.dm index 4e1782d2384..4a71f6c67bc 100644 --- a/code/modules/clothing/spacesuits/rig/rig_verbs.dm +++ b/code/modules/clothing/spacesuits/rig/rig_verbs.dm @@ -332,4 +332,4 @@ return to_chat(usr, "You attempt to engage the [module.interface_name].") - module.engage() \ No newline at end of file + module.engage() diff --git a/code/modules/clothing/spacesuits/rig/suits/ert_suits.dm b/code/modules/clothing/spacesuits/rig/suits/ert_suits.dm index 3ff5a19b468..5485e1fe882 100644 --- a/code/modules/clothing/spacesuits/rig/suits/ert_suits.dm +++ b/code/modules/clothing/spacesuits/rig/suits/ert_suits.dm @@ -8,7 +8,7 @@ helm_type = /obj/item/clothing/head/helmet/space/new_rig/ert - req_access = list(access_cent_specops) + req_access = list(ACCESS_CENT_SPECOPS) armor = list(melee = 45, bullet = 25, laser = 30, energy = 10, bomb = 25, bio = 100, rad = 50) allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/t_scanner, /obj/item/rcd, /obj/item/crowbar, \ @@ -78,4 +78,4 @@ /obj/item/rig_module/device/plasmacutter, // /obj/item/rig_module/device/rcd, /obj/item/rig_module/datajack - ) \ No newline at end of file + ) diff --git a/code/modules/clothing/spacesuits/rig/suits/light.dm b/code/modules/clothing/spacesuits/rig/suits/light.dm index 2d1933dad60..d76691daf57 100644 --- a/code/modules/clothing/spacesuits/rig/suits/light.dm +++ b/code/modules/clothing/spacesuits/rig/suits/light.dm @@ -35,7 +35,7 @@ desc = "An advanced powered armour suit with many cyberwarfare enhancements. Comes with built-in insulated gloves for safely tampering with electronics." icon_state = "hacker_rig" - req_access = list(access_syndicate) + req_access = list(ACCESS_SYNDICATE) airtight = 0 seal_delay = 5 //not being vaccum-proof has an upside I guess @@ -82,7 +82,7 @@ chest_type = /obj/item/clothing/suit/space/new_rig/light/ninja glove_type = /obj/item/clothing/gloves/rig/light/ninja - req_access = list(access_syndicate) + req_access = list(ACCESS_SYNDICATE) initial_modules = list( /obj/item/rig_module/teleporter, @@ -112,7 +112,7 @@ desc = "A highly advanced and expensive suit designed for covert operations." icon_state = "ninja_rig" //supposed to be "stealth_rig", but as it currently only has a semi-copied ninja rig sprite, we can just use them directly. - req_access = list(access_syndicate) + req_access = list(ACCESS_SYNDICATE) initial_modules = list( /obj/item/rig_module/stealth_field, diff --git a/code/modules/clothing/spacesuits/rig/suits/station.dm b/code/modules/clothing/spacesuits/rig/suits/station.dm index 0954a6c0f62..c4bbd059fae 100644 --- a/code/modules/clothing/spacesuits/rig/suits/station.dm +++ b/code/modules/clothing/spacesuits/rig/suits/station.dm @@ -32,7 +32,7 @@ /obj/item/rig/internalaffairs/equipped - req_access = list(access_lawyer) + req_access = list(ACCESS_LAWYER) initial_modules = list( /obj/item/rig_module/ai_container, @@ -125,7 +125,7 @@ /obj/item/rig/ce/equipped - req_access = list(access_ce) + req_access = list(ACCESS_CE) initial_modules = list( /obj/item/rig_module/ai_container, @@ -163,7 +163,7 @@ /obj/item/rig/hazmat/equipped - req_access = list(access_rd) + req_access = list(ACCESS_RD) initial_modules = list( /obj/item/rig_module/ai_container, diff --git a/code/modules/clothing/spacesuits/syndi.dm b/code/modules/clothing/spacesuits/syndi.dm index a1043eb2643..8c65ed02553 100644 --- a/code/modules/clothing/spacesuits/syndi.dm +++ b/code/modules/clothing/spacesuits/syndi.dm @@ -1,188 +1,188 @@ -//Regular syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate - name = "red space helmet" - desc = "Top secret Spess Helmet." - icon_state = "syndicate" - item_state = "syndicate" - desc = "Has a tag on it: Totally not property of a hostile corporation, honest!" - armor = list("melee" = 40, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 80, "acid" = 85) - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/helmet.dmi', - "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', - "Unathi" = 'icons/mob/species/unathi/helmet.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', - "Vox" = 'icons/mob/species/vox/helmet.dmi') - - -/obj/item/clothing/suit/space/syndicate - name = "red space suit" - icon_state = "syndicate" - item_state = "space_suit_syndicate" - desc = "Has a tag on it: Totally not property of a hostile corporation, honest!" - w_class = WEIGHT_CLASS_NORMAL - allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword/saber,/obj/item/restraints/handcuffs,/obj/item/tank) - armor = list("melee" = 40, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 80, "acid" = 85) - sprite_sheets = list( - "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', - "Unathi" = 'icons/mob/species/unathi/suit.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', - "Vox" = 'icons/mob/species/vox/suit.dmi') - - -//Green syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/green - name = "Green Space Helmet" - icon_state = "syndicate-helm-green" - item_state = "syndicate-helm-green" - -/obj/item/clothing/suit/space/syndicate/green - name = "Green Space Suit" - icon_state = "syndicate-green" - item_state = "syndicate-green" - - -//Dark green syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/green/dark - name = "Dark Green Space Helmet" - icon_state = "syndicate-helm-green-dark" - item_state = "syndicate-helm-green-dark" - -/obj/item/clothing/suit/space/syndicate/green/dark - name = "Dark Green Space Suit" - icon_state = "syndicate-green-dark" - item_state = "syndicate-green-dark" - - -//Orange syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/orange - name = "Orange Space Helmet" - icon_state = "syndicate-helm-orange" - item_state = "syndicate-helm-orange" - -/obj/item/clothing/suit/space/syndicate/orange - name = "Orange Space Suit" - icon_state = "syndicate-orange" - item_state = "syndicate-orange" - - -//Blue syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/blue - name = "Blue Space Helmet" - icon_state = "syndicate-helm-blue" - item_state = "syndicate-helm-blue" - -/obj/item/clothing/suit/space/syndicate/blue - name = "Blue Space Suit" - icon_state = "syndicate-blue" - item_state = "syndicate-blue" - - -//Black syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black - name = "Black Space Helmet" - icon_state = "syndicate-helm-black" - item_state = "syndicate-helm-black" - -obj/item/clothing/head/helmet/space/syndicate/black/strike - name = "Syndicate Strike Team commando helmet" - desc = "A heavily armored black helmet that is only given to high-ranking Syndicate operatives." - armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100) //Matches DS gear. - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = ACID_PROOF - -/obj/item/clothing/suit/space/syndicate/black - name = "Black Space Suit" - icon_state = "syndicate-black" - item_state = "syndicate-black" - -obj/item/clothing/suit/space/syndicate/black/strike - name = "Syndicate Strike Team commando space suit" - desc = "A heavily armored, black space suit that is only given to high-ranking Syndicate operatives." - armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100) //Matches DS gear. - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = ACID_PROOF - -//Black-green syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/green - name = "Black Space Helmet" - icon_state = "syndicate-helm-black-green" - item_state = "syndicate-helm-black-green" - -/obj/item/clothing/suit/space/syndicate/black/green - name = "Black and Green Space Suit" - icon_state = "syndicate-black-green" - item_state = "syndicate-black-green" - - -//Black-blue syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/blue - name = "Black Space Helmet" - icon_state = "syndicate-helm-black-blue" - item_state = "syndicate-helm-black-blue" - -/obj/item/clothing/suit/space/syndicate/black/blue - name = "Black and Blue Space Suit" - icon_state = "syndicate-black-blue" - item_state = "syndicate-black-blue" - - -//Black medical syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/med - name = "Black Space Helmet" - icon_state = "syndicate-helm-black-med" - item_state = "syndicate-helm-black" - -/obj/item/clothing/suit/space/syndicate/black/med - name = "Green Space Suit" - icon_state = "syndicate-black-med" - item_state = "syndicate-black" - - -//Black-orange syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/orange - name = "Black Space Helmet" - icon_state = "syndicate-helm-black-orange" - item_state = "syndicate-helm-black" - -/obj/item/clothing/suit/space/syndicate/black/orange - name = "Black and Orange Space Suit" - icon_state = "syndicate-black-orange" - item_state = "syndicate-black" - - -//Black-red syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/red - name = "Black Space Helmet" - icon_state = "syndicate-helm-black-red" - item_state = "syndicate-helm-black-red" - -obj/item/clothing/head/helmet/space/syndicate/black/red/strike - name = "Syndicate Strike Team leader helmet" - desc = "A heavily armored, black and red space helmet that is only given to elite Syndicate operatives, it looks particularly menacing." - armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100) //Matches DS gear. - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = ACID_PROOF - -/obj/item/clothing/suit/space/syndicate/black/red - name = "Black and Red Space Suit" - icon_state = "syndicate-black-red" - item_state = "syndicate-black-red" - -obj/item/clothing/suit/space/syndicate/black/red/strike - name = "Syndicate Strike Team leader space suit" - desc = "A heavily armored, black and red space suit that is only given to elite Syndicate operatives, it looks particularly menacing." - armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100) //Matches DS gear. - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = ACID_PROOF - - -//Black with yellow/red engineering syndicate space suit -/obj/item/clothing/head/helmet/space/syndicate/black/engie - name = "Black Space Helmet" - icon_state = "syndicate-helm-black-engie" - item_state = "syndicate-helm-black" - -/obj/item/clothing/suit/space/syndicate/black/engie - name = "Black Engineering Space Suit" - icon_state = "syndicate-black-engie" - item_state = "syndicate-black" +//Regular syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate + name = "red space helmet" + desc = "Top secret Spess Helmet." + icon_state = "syndicate" + item_state = "syndicate" + desc = "Has a tag on it: Totally not property of a hostile corporation, honest!" + armor = list("melee" = 40, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 80, "acid" = 85) + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/helmet.dmi', + "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', + "Unathi" = 'icons/mob/species/unathi/helmet.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', + "Vox" = 'icons/mob/species/vox/helmet.dmi') + + +/obj/item/clothing/suit/space/syndicate + name = "red space suit" + icon_state = "syndicate" + item_state = "space_suit_syndicate" + desc = "Has a tag on it: Totally not property of a hostile corporation, honest!" + w_class = WEIGHT_CLASS_NORMAL + allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword/saber,/obj/item/restraints/handcuffs,/obj/item/tank) + armor = list("melee" = 40, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 80, "acid" = 85) + sprite_sheets = list( + "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', + "Unathi" = 'icons/mob/species/unathi/suit.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', + "Vox" = 'icons/mob/species/vox/suit.dmi') + + +//Green syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/green + name = "Green Space Helmet" + icon_state = "syndicate-helm-green" + item_state = "syndicate-helm-green" + +/obj/item/clothing/suit/space/syndicate/green + name = "Green Space Suit" + icon_state = "syndicate-green" + item_state = "syndicate-green" + + +//Dark green syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/green/dark + name = "Dark Green Space Helmet" + icon_state = "syndicate-helm-green-dark" + item_state = "syndicate-helm-green-dark" + +/obj/item/clothing/suit/space/syndicate/green/dark + name = "Dark Green Space Suit" + icon_state = "syndicate-green-dark" + item_state = "syndicate-green-dark" + + +//Orange syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/orange + name = "Orange Space Helmet" + icon_state = "syndicate-helm-orange" + item_state = "syndicate-helm-orange" + +/obj/item/clothing/suit/space/syndicate/orange + name = "Orange Space Suit" + icon_state = "syndicate-orange" + item_state = "syndicate-orange" + + +//Blue syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/blue + name = "Blue Space Helmet" + icon_state = "syndicate-helm-blue" + item_state = "syndicate-helm-blue" + +/obj/item/clothing/suit/space/syndicate/blue + name = "Blue Space Suit" + icon_state = "syndicate-blue" + item_state = "syndicate-blue" + + +//Black syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black + name = "Black Space Helmet" + icon_state = "syndicate-helm-black" + item_state = "syndicate-helm-black" + +obj/item/clothing/head/helmet/space/syndicate/black/strike + name = "Syndicate Strike Team commando helmet" + desc = "A heavily armored black helmet that is only given to high-ranking Syndicate operatives." + armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100) //Matches DS gear. + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = ACID_PROOF + +/obj/item/clothing/suit/space/syndicate/black + name = "Black Space Suit" + icon_state = "syndicate-black" + item_state = "syndicate-black" + +obj/item/clothing/suit/space/syndicate/black/strike + name = "Syndicate Strike Team commando space suit" + desc = "A heavily armored, black space suit that is only given to high-ranking Syndicate operatives." + armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100) //Matches DS gear. + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = ACID_PROOF + +//Black-green syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/green + name = "Black Space Helmet" + icon_state = "syndicate-helm-black-green" + item_state = "syndicate-helm-black-green" + +/obj/item/clothing/suit/space/syndicate/black/green + name = "Black and Green Space Suit" + icon_state = "syndicate-black-green" + item_state = "syndicate-black-green" + + +//Black-blue syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/blue + name = "Black Space Helmet" + icon_state = "syndicate-helm-black-blue" + item_state = "syndicate-helm-black-blue" + +/obj/item/clothing/suit/space/syndicate/black/blue + name = "Black and Blue Space Suit" + icon_state = "syndicate-black-blue" + item_state = "syndicate-black-blue" + + +//Black medical syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/med + name = "Black Space Helmet" + icon_state = "syndicate-helm-black-med" + item_state = "syndicate-helm-black" + +/obj/item/clothing/suit/space/syndicate/black/med + name = "Green Space Suit" + icon_state = "syndicate-black-med" + item_state = "syndicate-black" + + +//Black-orange syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/orange + name = "Black Space Helmet" + icon_state = "syndicate-helm-black-orange" + item_state = "syndicate-helm-black" + +/obj/item/clothing/suit/space/syndicate/black/orange + name = "Black and Orange Space Suit" + icon_state = "syndicate-black-orange" + item_state = "syndicate-black" + + +//Black-red syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/red + name = "Black Space Helmet" + icon_state = "syndicate-helm-black-red" + item_state = "syndicate-helm-black-red" + +obj/item/clothing/head/helmet/space/syndicate/black/red/strike + name = "Syndicate Strike Team leader helmet" + desc = "A heavily armored, black and red space helmet that is only given to elite Syndicate operatives, it looks particularly menacing." + armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100) //Matches DS gear. + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = ACID_PROOF + +/obj/item/clothing/suit/space/syndicate/black/red + name = "Black and Red Space Suit" + icon_state = "syndicate-black-red" + item_state = "syndicate-black-red" + +obj/item/clothing/suit/space/syndicate/black/red/strike + name = "Syndicate Strike Team leader space suit" + desc = "A heavily armored, black and red space suit that is only given to elite Syndicate operatives, it looks particularly menacing." + armor = list(melee = 80, bullet = 80, laser = 50, energy = 50, bomb = 100, bio = 100, rad = 100, fire = 100, acid = 100) //Matches DS gear. + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = ACID_PROOF + + +//Black with yellow/red engineering syndicate space suit +/obj/item/clothing/head/helmet/space/syndicate/black/engie + name = "Black Space Helmet" + icon_state = "syndicate-helm-black-engie" + item_state = "syndicate-helm-black" + +/obj/item/clothing/suit/space/syndicate/black/engie + name = "Black Engineering Space Suit" + icon_state = "syndicate-black-engie" + item_state = "syndicate-black" diff --git a/code/modules/clothing/spacesuits/void.dm b/code/modules/clothing/spacesuits/void.dm index 0f97be88e10..04c5edf20ec 100644 --- a/code/modules/clothing/spacesuits/void.dm +++ b/code/modules/clothing/spacesuits/void.dm @@ -1,124 +1,124 @@ - -//Voidsuits -/obj/item/clothing/head/helmet/space/nasavoid - name = "NASA Void Helmet" - desc = "A high tech, NASA Centcom branch designed space suit helmet. Used for AI satellite maintenance." - icon_state = "void-red" - item_state = "void" - flags_inv = HIDEMASK|HIDEEARS - sprite_sheets = list( - "Grey" = 'icons/mob/species/grey/helmet.dmi', - "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', - "Unathi" = 'icons/mob/species/unathi/helmet.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', - "Vox" = 'icons/mob/species/vox/helmet.dmi') - -/obj/item/clothing/suit/space/nasavoid - name = "NASA Void Suit" - icon_state = "void-red" - item_state = "void" - desc = "A high tech, NASA Centcom branch designed space suit. Used for AI satellite maintenance." - allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/multitool) - sprite_sheets = list( - "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', - "Unathi" = 'icons/mob/species/unathi/suit.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', - "Vox" = 'icons/mob/species/vox/suit.dmi') - -//Colors!!! -/obj/item/clothing/head/helmet/space/nasavoid/green - icon_state = "void-green" - -/obj/item/clothing/suit/space/nasavoid/green - icon_state = "void-green" - -/obj/item/clothing/head/helmet/space/nasavoid/ntblue - icon_state = "void-ntblue" - -/obj/item/clothing/suit/space/nasavoid/ntblue - icon_state = "void-ntblue" - -/obj/item/clothing/head/helmet/space/nasavoid/purple - icon_state = "void-purple" - -/obj/item/clothing/suit/space/nasavoid/purple - icon_state = "void-purple" - -/obj/item/clothing/head/helmet/space/nasavoid/yellow - icon_state = "void-yellow" - -/obj/item/clothing/suit/space/nasavoid/yellow - icon_state = "void-yellow" - -/obj/item/clothing/head/helmet/space/nasavoid/ltblue - icon_state = "void-light_blue" - -/obj/item/clothing/suit/space/nasavoid/ltblue - icon_state = "void-light_blue" - - -//Captian's Suit, like the other captian's suit, but looks better, at the cost of armor -/obj/item/clothing/head/helmet/space/nasavoid/captain - name = "Fancy Retro Void Helmet" - icon_state = "void-captian" - desc = "A high tech, NASA Centcom branch designed space suit helmet. Used for AI satellite maintenance. This one is fit for a captain." - -/obj/item/clothing/suit/space/nasavoid/captain - name = "Fancy NASA Void Suit" - icon_state = "void-captian" - desc = "A high tech, NASA Centcom branch designed space suit. Used for AI satellite maintenance. This one is fit for a captain." - -//Syndi's suit, on par with a blood red softsuit - -/obj/item/clothing/head/helmet/space/nasavoid/syndi - name = "Blood Red Retro Void Helmet" - icon_state = "void-syndi" - desc = "A high tech, NASA Centcom branch designed space suit helmet. This one looks rather suspicious." - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE - armor = list("melee" = 40, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 80, "acid" = 85) - -/obj/item/clothing/suit/space/nasavoid/syndi - name = "Blood Red NASA Void Suit" - icon_state = "void-syndi" - desc = "A high tech, NASA Centcom branch designed space suit. This one looks rather suspicious." - w_class = WEIGHT_CLASS_NORMAL - allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword/saber,/obj/item/restraints/handcuffs,/obj/item/tank) - armor = list("melee" = 40, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 80, "acid" = 85) - -//random spawner - -/obj/effect/nasavoidsuitspawner - name = "NASA Void Suit Spawner" - icon = 'icons/obj/clothing/suits.dmi' - icon_state = "void-red" - desc = "You shouldn't see this, a spawner for NASA Void Suits." - var/suits = list("red", "green", "ntblue", "purple", "yellow", "ltblue") - -/obj/effect/nasavoidsuitspawner/New() - var/obj/item/clothing/head/helmet/space/nasavoid/H - var/obj/item/clothing/suit/space/nasavoid/S - switch(pick(suits)) - if("red") - H = new /obj/item/clothing/head/helmet/space/nasavoid - S = new /obj/item/clothing/suit/space/nasavoid - if("green") - H = new /obj/item/clothing/head/helmet/space/nasavoid/green - S = new /obj/item/clothing/suit/space/nasavoid/green - if("ntblue") - H = new /obj/item/clothing/head/helmet/space/nasavoid/ntblue - S = new /obj/item/clothing/suit/space/nasavoid/ntblue - if("purple") - H = new /obj/item/clothing/head/helmet/space/nasavoid/purple - S = new /obj/item/clothing/suit/space/nasavoid/purple - if("yellow") - H = new /obj/item/clothing/head/helmet/space/nasavoid/yellow - S = new /obj/item/clothing/suit/space/nasavoid/yellow - if("ltblue") - H = new /obj/item/clothing/head/helmet/space/nasavoid/ltblue - S = new /obj/item/clothing/suit/space/nasavoid/ltblue - var/turf/T = get_turf(src) - if(H) - H.forceMove(T) - if(S) - S.forceMove(T) - qdel(src) \ No newline at end of file + +//Voidsuits +/obj/item/clothing/head/helmet/space/nasavoid + name = "NASA Void Helmet" + desc = "A high tech, NASA Centcom branch designed space suit helmet. Used for AI satellite maintenance." + icon_state = "void-red" + item_state = "void" + flags_inv = HIDEMASK|HIDEEARS + sprite_sheets = list( + "Grey" = 'icons/mob/species/grey/helmet.dmi', + "Tajaran" = 'icons/mob/species/tajaran/helmet.dmi', + "Unathi" = 'icons/mob/species/unathi/helmet.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/helmet.dmi', + "Vox" = 'icons/mob/species/vox/helmet.dmi') + +/obj/item/clothing/suit/space/nasavoid + name = "NASA Void Suit" + icon_state = "void-red" + item_state = "void" + desc = "A high tech, NASA Centcom branch designed space suit. Used for AI satellite maintenance." + allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/multitool) + sprite_sheets = list( + "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', + "Unathi" = 'icons/mob/species/unathi/suit.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', + "Vox" = 'icons/mob/species/vox/suit.dmi') + +//Colors!!! +/obj/item/clothing/head/helmet/space/nasavoid/green + icon_state = "void-green" + +/obj/item/clothing/suit/space/nasavoid/green + icon_state = "void-green" + +/obj/item/clothing/head/helmet/space/nasavoid/ntblue + icon_state = "void-ntblue" + +/obj/item/clothing/suit/space/nasavoid/ntblue + icon_state = "void-ntblue" + +/obj/item/clothing/head/helmet/space/nasavoid/purple + icon_state = "void-purple" + +/obj/item/clothing/suit/space/nasavoid/purple + icon_state = "void-purple" + +/obj/item/clothing/head/helmet/space/nasavoid/yellow + icon_state = "void-yellow" + +/obj/item/clothing/suit/space/nasavoid/yellow + icon_state = "void-yellow" + +/obj/item/clothing/head/helmet/space/nasavoid/ltblue + icon_state = "void-light_blue" + +/obj/item/clothing/suit/space/nasavoid/ltblue + icon_state = "void-light_blue" + + +//Captian's Suit, like the other captian's suit, but looks better, at the cost of armor +/obj/item/clothing/head/helmet/space/nasavoid/captain + name = "Fancy Retro Void Helmet" + icon_state = "void-captian" + desc = "A high tech, NASA Centcom branch designed space suit helmet. Used for AI satellite maintenance. This one is fit for a captain." + +/obj/item/clothing/suit/space/nasavoid/captain + name = "Fancy NASA Void Suit" + icon_state = "void-captian" + desc = "A high tech, NASA Centcom branch designed space suit. Used for AI satellite maintenance. This one is fit for a captain." + +//Syndi's suit, on par with a blood red softsuit + +/obj/item/clothing/head/helmet/space/nasavoid/syndi + name = "Blood Red Retro Void Helmet" + icon_state = "void-syndi" + desc = "A high tech, NASA Centcom branch designed space suit helmet. This one looks rather suspicious." + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE + armor = list("melee" = 40, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 80, "acid" = 85) + +/obj/item/clothing/suit/space/nasavoid/syndi + name = "Blood Red NASA Void Suit" + icon_state = "void-syndi" + desc = "A high tech, NASA Centcom branch designed space suit. This one looks rather suspicious." + w_class = WEIGHT_CLASS_NORMAL + allowed = list(/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/melee/energy/sword/saber,/obj/item/restraints/handcuffs,/obj/item/tank) + armor = list("melee" = 40, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 80, "acid" = 85) + +//random spawner + +/obj/effect/nasavoidsuitspawner + name = "NASA Void Suit Spawner" + icon = 'icons/obj/clothing/suits.dmi' + icon_state = "void-red" + desc = "You shouldn't see this, a spawner for NASA Void Suits." + var/suits = list("red", "green", "ntblue", "purple", "yellow", "ltblue") + +/obj/effect/nasavoidsuitspawner/New() + var/obj/item/clothing/head/helmet/space/nasavoid/H + var/obj/item/clothing/suit/space/nasavoid/S + switch(pick(suits)) + if("red") + H = new /obj/item/clothing/head/helmet/space/nasavoid + S = new /obj/item/clothing/suit/space/nasavoid + if("green") + H = new /obj/item/clothing/head/helmet/space/nasavoid/green + S = new /obj/item/clothing/suit/space/nasavoid/green + if("ntblue") + H = new /obj/item/clothing/head/helmet/space/nasavoid/ntblue + S = new /obj/item/clothing/suit/space/nasavoid/ntblue + if("purple") + H = new /obj/item/clothing/head/helmet/space/nasavoid/purple + S = new /obj/item/clothing/suit/space/nasavoid/purple + if("yellow") + H = new /obj/item/clothing/head/helmet/space/nasavoid/yellow + S = new /obj/item/clothing/suit/space/nasavoid/yellow + if("ltblue") + H = new /obj/item/clothing/head/helmet/space/nasavoid/ltblue + S = new /obj/item/clothing/suit/space/nasavoid/ltblue + var/turf/T = get_turf(src) + if(H) + H.forceMove(T) + if(S) + S.forceMove(T) + qdel(src) diff --git a/code/modules/clothing/suits/alien.dm b/code/modules/clothing/suits/alien.dm index 0f82a9eae7e..1fde5cd6f84 100644 --- a/code/modules/clothing/suits/alien.dm +++ b/code/modules/clothing/suits/alien.dm @@ -16,4 +16,4 @@ desc = "A rather grisly selection of cured hides and skin, sewn together to form a ragged mantle." icon_state = "mantle-unathi" item_state = "mantle-unathi" - body_parts_covered = UPPER_TORSO \ No newline at end of file + body_parts_covered = UPPER_TORSO diff --git a/code/modules/clothing/suits/armor.dm b/code/modules/clothing/suits/armor.dm index 037e967f5ee..e9f78936cf7 100644 --- a/code/modules/clothing/suits/armor.dm +++ b/code/modules/clothing/suits/armor.dm @@ -1,544 +1,544 @@ -/obj/item/clothing/suit/armor - allowed = list(/obj/item/gun/energy,/obj/item/reagent_containers/spray/pepper,/obj/item/gun/projectile,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/flashlight/seclite,/obj/item/melee/classic_baton/telescopic,/obj/item/kitchen/knife/combat) - body_parts_covered = UPPER_TORSO|LOWER_TORSO - cold_protection = UPPER_TORSO|LOWER_TORSO - min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT - heat_protection = UPPER_TORSO|LOWER_TORSO - max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT - strip_delay = 60 - put_on_delay = 40 - max_integrity = 250 - resistance_flags = NONE - armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/obj/item/clothing/suit/armor/vest - name = "armor" - desc = "An armored vest that protects against some damage." - icon_state = "armor" - item_state = "armor" - blood_overlay_type = "armor" - flags_size = ONESIZEFITSALL - dog_fashion = /datum/dog_fashion/back - -/obj/item/clothing/suit/armor/vest/jacket - name = "military jacket" - desc = "An old military jacket, it has armoring." - icon_state = "militaryjacket" - item_state = "militaryjacket" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/suit/armor/vest/combat - name = "combat vest" - desc = "An armored vest that protects against some damage." - icon_state = "armor-combat" - item_state = "bulletproof" - blood_overlay_type = "armor" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/suit/armor/vest/security - name = "security armor" - desc = "An armored vest that protects against some damage. This one has a clip for a holobadge." - icon_state = "armor" - item_state = "armor" - var/obj/item/clothing/accessory/holobadge/attached_badge - -/obj/item/clothing/suit/armor/vest/security/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/clothing/accessory/holobadge)) - if(user.unEquip(W)) - add_fingerprint(user) - W.forceMove(src) - attached_badge = W - - var/datum/action/A = new /datum/action/item_action/remove_badge(src) - A.Grant(user) - icon_state = "armorsec" - user.update_inv_wear_suit() - desc = "An armored vest that protects against some damage. This one has [attached_badge] attached to it." - to_chat(user, "You attach [attached_badge] to [src].") - return - ..() - -/obj/item/clothing/suit/armor/vest/security/attack_self(mob/user as mob) - if(attached_badge) - add_fingerprint(user) - user.put_in_hands(attached_badge) - - for(var/X in actions) - var/datum/action/A = X - A.Remove(user) - - icon_state = "armor" - user.update_inv_wear_suit() - desc = "An armored vest that protects against some damage. This one has a clip for a holobadge." - to_chat(user, "You remove [attached_badge] from [src].") - - attached_badge = null - - return - - ..() - -/obj/item/clothing/suit/armor/vest/blueshield - name = "blueshield security armor" - desc = "An armored vest with the badge of a Blueshield Lieutenant." - icon_state = "blueshield" - item_state = "blueshield" - -/obj/item/clothing/suit/armor/vest/bloody - name = "bloodied security armor" - desc = "A vest drenched in the blood of Greytide. It has seen better days." - icon_state = "bloody_armor" - item_state = "bloody_armor" - sprite_sheets = null - -/obj/item/clothing/suit/armor/secjacket - name = "security jacket" - desc = "A sturdy black jacket with reinforced fabric. Bears insignia of NT corporate security." - icon_state = "secjacket_open" - item_state = "hos" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - armor = list(melee = 15, bullet = 10, laser = 15, energy = 5, bomb = 15, bio = 0, rad = 0, fire = 30, acid = 30) - cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS - heat_protection = UPPER_TORSO|LOWER_TORSO|ARMS - ignore_suitadjust = 0 - suit_adjusted = 1 - actions_types = list(/datum/action/item_action/openclose) - adjust_flavour = "unzip" - -/obj/item/clothing/suit/armor/hos - name = "armored coat" - desc = "A trench coat enhanced with a special alloy for some protection and style." - icon_state = "hos" - item_state = "hos" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS - armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 90) - flags_inv = HIDEJUMPSUIT - cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - strip_delay = 80 - -/obj/item/clothing/suit/armor/hos/alt - name = "armored trenchoat" - desc = "A trenchcoat enhanced with a special lightweight kevlar. The epitome of tactical plainclothes." - icon_state = "hostrench_open" - item_state = "hostrench_open" - flags_inv = 0 - ignore_suitadjust = 0 - suit_adjusted = 1 - actions_types = list(/datum/action/item_action/openclose) - adjust_flavour = "unbutton" - -/obj/item/clothing/suit/armor/hos/jensen - name = "armored trenchcoat" - desc = "A trenchcoat augmented with a special alloy for some protection and style." - icon_state = "jensencoat" - item_state = "jensencoat" - flags_inv = 0 - sprite_sheets = null - -/obj/item/clothing/suit/armor/vest/warden - name = "Warden's armored jacket" - desc = "An armored jacket with silver rank pips and livery." - icon_state = "warden_jacket" - item_state = "armor" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - cold_protection = UPPER_TORSO|LOWER_TORSO|HANDS - heat_protection = UPPER_TORSO|LOWER_TORSO|HANDS - strip_delay = 70 - resistance_flags = FLAMMABLE - dog_fashion = null - -/obj/item/clothing/suit/armor/vest/warden/alt - name = "warden's jacket" - desc = "A navy-blue armored jacket with blue shoulder designations and '/Warden/' stitched into one of the chest pockets." - icon_state = "warden_jacket_alt" - -/obj/item/clothing/suit/armor/vest/capcarapace - name = "captain's carapace" - desc = "An armored vest reinforced with ceramic plates and pauldrons to provide additional protection whilst still offering maximum mobility and flexibility. Issued only to the station's finest, although it does chafe your nipples." - icon_state = "capcarapace" - item_state = "armor" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - armor = list("melee" = 50, "bullet" = 40, "laser" = 50, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 90) - dog_fashion = null - resistance_flags = FIRE_PROOF - -/obj/item/clothing/suit/armor/vest/capcarapace/alt - name = "captain's parade jacket" - desc = "For when an armored vest isn't fashionable enough." - icon_state = "capformal" - item_state = "capspacesuit" - -/obj/item/clothing/suit/armor/riot - name = "Riot Suit" - desc = "A suit of armor with heavy padding to protect against melee attacks. Looks like it might impair movement." - icon_state = "riot" - item_state = "swat_suit" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - armor = list("melee" = 50, "bullet" = 10, "laser" = 10, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 80) - flags_inv = HIDEJUMPSUIT - strip_delay = 80 - put_on_delay = 60 - hide_tail_by_species = list("Vox") - -/obj/item/clothing/suit/armor/riot/knight - name = "plate armour" - desc = "A classic suit of plate armour, highly effective at stopping melee attacks." - icon_state = "knight_green" - item_state = "knight_green" - slowdown = 1 - -/obj/item/clothing/suit/armor/riot/knight/yellow - icon_state = "knight_yellow" - item_state = "knight_yellow" - -/obj/item/clothing/suit/armor/riot/knight/blue - icon_state = "knight_blue" - item_state = "knight_blue" - -/obj/item/clothing/suit/armor/riot/knight/red - icon_state = "knight_red" - item_state = "knight_red" - -/obj/item/clothing/suit/armor/riot/knight/templar - name = "crusader armour" - desc = "God wills it!" - icon_state = "knight_templar" - item_state = "knight_templar" - allowed = list(/obj/item/nullrod/claymore) - armor = list(melee = 25, bullet = 5, laser = 5, energy = 5, bomb = 0, bio = 0, rad = 0, fire = 80, acid = 80) - -/obj/item/clothing/suit/armor/vest/durathread - name = "durathread vest" - desc = "A vest made of durathread with strips of leather acting as trauma plates." - icon_state = "durathread" - item_state = "durathread" - strip_delay = 60 - max_integrity = 200 - resistance_flags = FLAMMABLE - armor = list("melee" = 20, "bullet" = 10, "laser" = 30, "energy" = 5, "bomb" = 15, "bio" = 0, "rad" = 0, "fire" = 40, "acid" = 50) - -/obj/item/clothing/suit/armor/bulletproof - name = "Bulletproof Vest" - desc = "A bulletproof vest that excels in protecting the wearer against traditional projectile weaponry and explosives to a minor extent." - icon_state = "bulletproof" - item_state = "armor" - blood_overlay_type = "armor" - armor = list("melee" = 15, "bullet" = 60, "laser" = 10, "energy" = 10, "bomb" = 40, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - strip_delay = 70 - put_on_delay = 50 - -/obj/item/clothing/suit/armor/laserproof - name = "Ablative Armor Vest" - desc = "A vest that excels in protecting the wearer against energy projectiles." - icon_state = "armor_reflec" - item_state = "armor_reflec" - blood_overlay_type = "armor" - armor = list("melee" = 10, "bullet" = 10, "laser" = 60, "energy" = 50, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF - var/hit_reflect_chance = 40 - -/obj/item/clothing/suit/armor/laserproof/IsReflect(var/def_zone) - if(!(def_zone in list("chest", "groin"))) //If not shot where ablative is covering you, you don't get the reflection bonus! - return 0 - if(prob(hit_reflect_chance)) - return 1 - -/obj/item/clothing/suit/armor/vest/det_suit - name = "armor" - desc = "An armored vest with a detective's badge on it." - icon_state = "detective-armor" - item_state = "armor" - blood_overlay_type = "armor" - flags_size = ONESIZEFITSALL - allowed = list(/obj/item/tank/emergency_oxygen,/obj/item/reagent_containers/spray/pepper,/obj/item/flashlight,/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/storage/fancy/cigarettes,/obj/item/lighter,/obj/item/detective_scanner,/obj/item/taperecorder) - resistance_flags = FLAMMABLE - dog_fashion = null - -//Reactive armor -/obj/item/clothing/suit/armor/reactive - name = "reactive armor" - desc = "Doesn't seem to do much for some reason." - var/active = 0 - icon_state = "reactiveoff" - item_state = "reactiveoff" - blood_overlay_type = "armor" - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) - actions_types = list(/datum/action/item_action/toggle) - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF - hit_reaction_chance = 50 - -/obj/item/clothing/suit/armor/reactive/attack_self(mob/user) - active = !(active) - if(active) - to_chat(user, "[src] is now active.") - icon_state = "reactive" - item_state = "reactive" - else - to_chat(user, "[src] is now inactive.") - icon_state = "reactiveoff" - item_state = "reactiveoff" - add_fingerprint(user) - user.update_inv_wear_suit() - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - -/obj/item/clothing/suit/armor/reactive/emp_act(severity) - active = 0 - icon_state = "reactiveoff" - item_state = "reactiveoff" - if(istype(loc, /mob/living/carbon/human)) - var/mob/living/carbon/human/C = loc - C.update_inv_wear_suit() - ..() - -//When the wearer gets hit, this armor will teleport the user a short distance away (to safety or to more danger, no one knows. That's the fun of it!) -/obj/item/clothing/suit/armor/reactive/teleport - name = "reactive teleport armor" - desc = "Someone seperated our Research Director from his own head!" - var/tele_range = 6 - -/obj/item/clothing/suit/armor/reactive/teleport/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(!active) - return 0 - if(prob(hit_reaction_chance)) - var/mob/living/carbon/human/H = owner - owner.visible_message("The reactive teleport system flings [H] clear of [attack_text]!") - var/list/turfs = new/list() - for(var/turf/T in orange(tele_range, H)) - if(istype(T, /turf/space)) - continue - if(T.density) - continue - if(T.x>world.maxx-tele_range || T.xworld.maxy-tele_range || T.yThe [src] blocks the [attack_text], sending out jets of flame!") - for(var/mob/living/carbon/C in range(6, owner)) - if(C != owner) - C.fire_stacks += 8 - C.IgniteMob() - owner.fire_stacks = -20 - return 1 - return 0 - - -/obj/item/clothing/suit/armor/reactive/stealth - name = "reactive stealth armor" - -/obj/item/clothing/suit/armor/reactive/stealth/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(!active) - return 0 - if(prob(hit_reaction_chance)) - var/mob/living/simple_animal/hostile/illusion/escape/E = new(owner.loc) - E.Copy_Parent(owner, 50) - E.GiveTarget(owner) //so it starts running right away - E.Goto(owner, E.move_to_delay, E.minimum_distance) - owner.alpha = 0 - owner.visible_message("[owner] is hit by [attack_text] in the chest!") //We pretend to be hit, since blocking it would stop the message otherwise - spawn(40) - owner.alpha = initial(owner.alpha) - return 1 - -/obj/item/clothing/suit/armor/reactive/tesla - name = "reactive tesla armor" - -/obj/item/clothing/suit/armor/reactive/tesla/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(!active) - return 0 - if(prob(hit_reaction_chance)) - owner.visible_message("The [src] blocks the [attack_text], sending out arcs of lightning!") - for(var/mob/living/M in view(6, owner)) - if(M == owner) - continue - owner.Beam(M,icon_state="lightning[rand(1, 12)]",icon='icons/effects/effects.dmi',time=5) - M.adjustFireLoss(25) - playsound(M, 'sound/machines/defib_zap.ogg', 50, 1, -1) - return 1 - -//All of the armor below is mostly unused - - -/obj/item/clothing/suit/armor/centcomm - name = "Cent. Com. armor" - desc = "A suit that protects against some damage." - icon_state = "centcom" - item_state = "centcom" - w_class = WEIGHT_CLASS_BULKY - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - allowed = list(/obj/item/gun/energy,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/tank/emergency_oxygen) - flags = THICKMATERIAL - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT - cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS - min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT - sprite_sheets = null - armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 90) - -/obj/item/clothing/suit/armor/heavy - name = "heavy armor" - desc = "A heavily armored suit that protects against moderate damage." - icon_state = "heavy" - item_state = "swat_suit" - armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 90) - w_class = WEIGHT_CLASS_BULKY - gas_transfer_coefficient = 0.90 - flags = THICKMATERIAL - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - slowdown = 3 - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT - hide_tail_by_species = list("Vox") - -/obj/item/clothing/suit/armor/tdome - armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 90) - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT - flags = THICKMATERIAL - cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - hide_tail_by_species = list("Vox") - -/obj/item/clothing/suit/armor/tdome/red - name = "Red Thunderdome Armor" - desc = "Armor worn by the red Thunderdome team." - icon_state = "tdred" - item_state = "tdred" - -/obj/item/clothing/suit/armor/tdome/green - name = "Green Thunderdome Armor" - desc = "Armor worn by the green Thunderdome team." - icon_state = "tdgreen" - item_state = "tdgreen" - -//Non-hardsuit ERT armor. -/obj/item/clothing/suit/armor/vest/ert - name = "emergency response team armor" - desc = "A set of armor worn by members of the Nanotrasen Emergency Response Team." - icon_state = "ertarmor_cmd" - item_state = "armor" - armor = list(melee = 30, bullet = 30, laser = 30, energy = 30, bomb = 20, bio = 0, rad = 0, fire = 50, acid = 50) - -//Commander -/obj/item/clothing/suit/armor/vest/ert/command - name = "emergency response team commander armor" - desc = "A set of armor worn by the commander of a Nanotrasen Emergency Response Team. Has blue highlights." - -//Security -/obj/item/clothing/suit/armor/vest/ert/security - name = "emergency response team security armor" - desc = "A set of armor worn by security members of the Nanotrasen Emergency Response Team. Has red highlights." - icon_state = "ertarmor_sec" - - -/obj/item/clothing/suit/armor/vest/ert/security/paranormal - name = "emergency response team paranormal armor" - desc = "A set of armor worn by paranormal members of the Nanotrasen Emergency Response Team. Has crusader sigils." - icon_state = "knight_templar" - item_state = "knight_templar" - -//Engineer -/obj/item/clothing/suit/armor/vest/ert/engineer - name = "emergency response team engineer armor" - desc = "A set of armor worn by engineering members of the Nanotrasen Emergency Response Team. Has orange highlights." - icon_state = "ertarmor_eng" - -//Medical -/obj/item/clothing/suit/armor/vest/ert/medical - name = "emergency response team medical armor" - desc = "A set of armor worn by medical members of the Nanotrasen Emergency Response Team. Has red and white highlights." - icon_state = "ertarmor_med" - -//Janitorial -/obj/item/clothing/suit/armor/vest/ert/janitor - name = "emergency response team janitor armor" - desc = "A set of armor worn by janitorial members of the Nanotrasen Emergency Response Team. Has red and white highlights." - icon_state = "ertarmor_jan" - -//same defense as basic sec armor -/obj/item/clothing/suit/storage/lawyer/blackjacket/armored - desc = "A snappy dress jacket, reinforced with a layer of armor protecting the torso." - allowed = list(/obj/item/tank/emergency_oxygen, /obj/item/gun/projectile/revolver, /obj/item/gun/projectile/automatic/pistol) - body_parts_covered = UPPER_TORSO|LOWER_TORSO - cold_protection = UPPER_TORSO|LOWER_TORSO - min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT - heat_protection = UPPER_TORSO|LOWER_TORSO - max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT - armor = list(melee = 25, bullet = 15, laser = 25, energy = 10, bomb = 25, bio = 0, rad = 0, fire = 40, acid = 40) - -//LAVALAND! - -/obj/item/clothing/suit/hooded/drake - name = "drake armour" - icon_state = "dragon" - item_state = "dragon" - desc = "A suit of armour fashioned from the remains of an ash drake." - allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator, /obj/item/pickaxe, /obj/item/twohanded/spear) - armor = list("melee" = 70, "bullet" = 30, "laser" = 50, "energy" = 40, "bomb" = 70, "bio" = 60, "rad" = 50, "fire" = 100, "acid" = 100) - hoodtype = /obj/item/clothing/head/hooded/drake - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = FIRE_PROOF | ACID_PROOF - -/obj/item/clothing/head/hooded/drake - name = "drake helmet" - icon_state = "dragon" - item_state = "dragon" - desc = "The skull of a dragon." - armor = list("melee" = 70, "bullet" = 30, "laser" = 50, "energy" = 40, "bomb" = 70, "bio" = 60, "rad" = 50, "fire" = 100, "acid" = 100) - heat_protection = HEAD - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - resistance_flags = FIRE_PROOF | ACID_PROOF - flags = BLOCKHAIR - flags_cover = HEADCOVERSEYES - -/obj/item/clothing/suit/hooded/goliath - name = "goliath cloak" - icon_state = "goliath_cloak" - item_state = "goliath_cloak" - desc = "A staunch, practical cape made out of numerous monster materials, it is coveted amongst exiles & hermits." - allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/pickaxe, /obj/item/twohanded/spear, /obj/item/organ/internal/regenerative_core/legion, /obj/item/kitchen/knife/combat/survival) - armor = list("melee" = 35, "bullet" = 10, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 60, "acid" = 60) //a fair alternative to bone armor, requiring alternative materials and gaining a suit slot - hoodtype = /obj/item/clothing/head/hooded/goliath - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/head/hooded/goliath - name = "goliath cloak hood" - icon_state = "golhood" - item_state = "golhood" - desc = "A protective & concealing hood." - armor = list("melee" = 35, "bullet" = 10, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 60, "acid" = 60) - flags = BLOCKHAIR - flags_cover = HEADCOVERSEYES - -/obj/item/clothing/suit/armor/bone - name = "bone armor" - desc = "A tribal armor plate, crafted from animal bone." - icon_state = "bonearmor" - item_state = "bonearmor" - blood_overlay_type = "armor" - armor = list("melee" = 35, "bullet" = 25, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS \ No newline at end of file +/obj/item/clothing/suit/armor + allowed = list(/obj/item/gun/energy,/obj/item/reagent_containers/spray/pepper,/obj/item/gun/projectile,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/flashlight/seclite,/obj/item/melee/classic_baton/telescopic,/obj/item/kitchen/knife/combat) + body_parts_covered = UPPER_TORSO|LOWER_TORSO + cold_protection = UPPER_TORSO|LOWER_TORSO + min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT + heat_protection = UPPER_TORSO|LOWER_TORSO + max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT + strip_delay = 60 + put_on_delay = 40 + max_integrity = 250 + resistance_flags = NONE + armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/obj/item/clothing/suit/armor/vest + name = "armor" + desc = "An armored vest that protects against some damage." + icon_state = "armor" + item_state = "armor" + blood_overlay_type = "armor" + flags_size = ONESIZEFITSALL + dog_fashion = /datum/dog_fashion/back + +/obj/item/clothing/suit/armor/vest/jacket + name = "military jacket" + desc = "An old military jacket, it has armoring." + icon_state = "militaryjacket" + item_state = "militaryjacket" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/suit/armor/vest/combat + name = "combat vest" + desc = "An armored vest that protects against some damage." + icon_state = "armor-combat" + item_state = "bulletproof" + blood_overlay_type = "armor" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/suit/armor/vest/security + name = "security armor" + desc = "An armored vest that protects against some damage. This one has a clip for a holobadge." + icon_state = "armor" + item_state = "armor" + var/obj/item/clothing/accessory/holobadge/attached_badge + +/obj/item/clothing/suit/armor/vest/security/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/clothing/accessory/holobadge)) + if(user.unEquip(W)) + add_fingerprint(user) + W.forceMove(src) + attached_badge = W + + var/datum/action/A = new /datum/action/item_action/remove_badge(src) + A.Grant(user) + icon_state = "armorsec" + user.update_inv_wear_suit() + desc = "An armored vest that protects against some damage. This one has [attached_badge] attached to it." + to_chat(user, "You attach [attached_badge] to [src].") + return + ..() + +/obj/item/clothing/suit/armor/vest/security/attack_self(mob/user as mob) + if(attached_badge) + add_fingerprint(user) + user.put_in_hands(attached_badge) + + for(var/X in actions) + var/datum/action/A = X + A.Remove(user) + + icon_state = "armor" + user.update_inv_wear_suit() + desc = "An armored vest that protects against some damage. This one has a clip for a holobadge." + to_chat(user, "You remove [attached_badge] from [src].") + + attached_badge = null + + return + + ..() + +/obj/item/clothing/suit/armor/vest/blueshield + name = "blueshield security armor" + desc = "An armored vest with the badge of a Blueshield Lieutenant." + icon_state = "blueshield" + item_state = "blueshield" + +/obj/item/clothing/suit/armor/vest/bloody + name = "bloodied security armor" + desc = "A vest drenched in the blood of Greytide. It has seen better days." + icon_state = "bloody_armor" + item_state = "bloody_armor" + sprite_sheets = null + +/obj/item/clothing/suit/armor/secjacket + name = "security jacket" + desc = "A sturdy black jacket with reinforced fabric. Bears insignia of NT corporate security." + icon_state = "secjacket_open" + item_state = "hos" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + armor = list(melee = 15, bullet = 10, laser = 15, energy = 5, bomb = 15, bio = 0, rad = 0, fire = 30, acid = 30) + cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS + heat_protection = UPPER_TORSO|LOWER_TORSO|ARMS + ignore_suitadjust = 0 + suit_adjusted = 1 + actions_types = list(/datum/action/item_action/openclose) + adjust_flavour = "unzip" + +/obj/item/clothing/suit/armor/hos + name = "armored coat" + desc = "A trench coat enhanced with a special alloy for some protection and style." + icon_state = "hos" + item_state = "hos" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS + armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 70, "acid" = 90) + flags_inv = HIDEJUMPSUIT + cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + strip_delay = 80 + +/obj/item/clothing/suit/armor/hos/alt + name = "armored trenchoat" + desc = "A trenchcoat enhanced with a special lightweight kevlar. The epitome of tactical plainclothes." + icon_state = "hostrench_open" + item_state = "hostrench_open" + flags_inv = 0 + ignore_suitadjust = 0 + suit_adjusted = 1 + actions_types = list(/datum/action/item_action/openclose) + adjust_flavour = "unbutton" + +/obj/item/clothing/suit/armor/hos/jensen + name = "armored trenchcoat" + desc = "A trenchcoat augmented with a special alloy for some protection and style." + icon_state = "jensencoat" + item_state = "jensencoat" + flags_inv = 0 + sprite_sheets = null + +/obj/item/clothing/suit/armor/vest/warden + name = "Warden's armored jacket" + desc = "An armored jacket with silver rank pips and livery." + icon_state = "warden_jacket" + item_state = "armor" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + cold_protection = UPPER_TORSO|LOWER_TORSO|HANDS + heat_protection = UPPER_TORSO|LOWER_TORSO|HANDS + strip_delay = 70 + resistance_flags = FLAMMABLE + dog_fashion = null + +/obj/item/clothing/suit/armor/vest/warden/alt + name = "warden's jacket" + desc = "A navy-blue armored jacket with blue shoulder designations and '/Warden/' stitched into one of the chest pockets." + icon_state = "warden_jacket_alt" + +/obj/item/clothing/suit/armor/vest/capcarapace + name = "captain's carapace" + desc = "An armored vest reinforced with ceramic plates and pauldrons to provide additional protection whilst still offering maximum mobility and flexibility. Issued only to the station's finest, although it does chafe your nipples." + icon_state = "capcarapace" + item_state = "armor" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + armor = list("melee" = 50, "bullet" = 40, "laser" = 50, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 90) + dog_fashion = null + resistance_flags = FIRE_PROOF + +/obj/item/clothing/suit/armor/vest/capcarapace/alt + name = "captain's parade jacket" + desc = "For when an armored vest isn't fashionable enough." + icon_state = "capformal" + item_state = "capspacesuit" + +/obj/item/clothing/suit/armor/riot + name = "Riot Suit" + desc = "A suit of armor with heavy padding to protect against melee attacks. Looks like it might impair movement." + icon_state = "riot" + item_state = "swat_suit" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + armor = list("melee" = 50, "bullet" = 10, "laser" = 10, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 80) + flags_inv = HIDEJUMPSUIT + strip_delay = 80 + put_on_delay = 60 + hide_tail_by_species = list("Vox") + +/obj/item/clothing/suit/armor/riot/knight + name = "plate armour" + desc = "A classic suit of plate armour, highly effective at stopping melee attacks." + icon_state = "knight_green" + item_state = "knight_green" + slowdown = 1 + +/obj/item/clothing/suit/armor/riot/knight/yellow + icon_state = "knight_yellow" + item_state = "knight_yellow" + +/obj/item/clothing/suit/armor/riot/knight/blue + icon_state = "knight_blue" + item_state = "knight_blue" + +/obj/item/clothing/suit/armor/riot/knight/red + icon_state = "knight_red" + item_state = "knight_red" + +/obj/item/clothing/suit/armor/riot/knight/templar + name = "crusader armour" + desc = "God wills it!" + icon_state = "knight_templar" + item_state = "knight_templar" + allowed = list(/obj/item/nullrod/claymore) + armor = list(melee = 25, bullet = 5, laser = 5, energy = 5, bomb = 0, bio = 0, rad = 0, fire = 80, acid = 80) + +/obj/item/clothing/suit/armor/vest/durathread + name = "durathread vest" + desc = "A vest made of durathread with strips of leather acting as trauma plates." + icon_state = "durathread" + item_state = "durathread" + strip_delay = 60 + max_integrity = 200 + resistance_flags = FLAMMABLE + armor = list("melee" = 20, "bullet" = 10, "laser" = 30, "energy" = 5, "bomb" = 15, "bio" = 0, "rad" = 0, "fire" = 40, "acid" = 50) + +/obj/item/clothing/suit/armor/bulletproof + name = "Bulletproof Vest" + desc = "A bulletproof vest that excels in protecting the wearer against traditional projectile weaponry and explosives to a minor extent." + icon_state = "bulletproof" + item_state = "armor" + blood_overlay_type = "armor" + armor = list("melee" = 15, "bullet" = 60, "laser" = 10, "energy" = 10, "bomb" = 40, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + strip_delay = 70 + put_on_delay = 50 + +/obj/item/clothing/suit/armor/laserproof + name = "Ablative Armor Vest" + desc = "A vest that excels in protecting the wearer against energy projectiles." + icon_state = "armor_reflec" + item_state = "armor_reflec" + blood_overlay_type = "armor" + armor = list("melee" = 10, "bullet" = 10, "laser" = 60, "energy" = 50, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + var/hit_reflect_chance = 40 + +/obj/item/clothing/suit/armor/laserproof/IsReflect(var/def_zone) + if(!(def_zone in list("chest", "groin"))) //If not shot where ablative is covering you, you don't get the reflection bonus! + return 0 + if(prob(hit_reflect_chance)) + return 1 + +/obj/item/clothing/suit/armor/vest/det_suit + name = "armor" + desc = "An armored vest with a detective's badge on it." + icon_state = "detective-armor" + item_state = "armor" + blood_overlay_type = "armor" + flags_size = ONESIZEFITSALL + allowed = list(/obj/item/tank/emergency_oxygen,/obj/item/reagent_containers/spray/pepper,/obj/item/flashlight,/obj/item/gun,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/storage/fancy/cigarettes,/obj/item/lighter,/obj/item/detective_scanner,/obj/item/taperecorder) + resistance_flags = FLAMMABLE + dog_fashion = null + +//Reactive armor +/obj/item/clothing/suit/armor/reactive + name = "reactive armor" + desc = "Doesn't seem to do much for some reason." + var/active = 0 + icon_state = "reactiveoff" + item_state = "reactiveoff" + blood_overlay_type = "armor" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) + actions_types = list(/datum/action/item_action/toggle) + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + hit_reaction_chance = 50 + +/obj/item/clothing/suit/armor/reactive/attack_self(mob/user) + active = !(active) + if(active) + to_chat(user, "[src] is now active.") + icon_state = "reactive" + item_state = "reactive" + else + to_chat(user, "[src] is now inactive.") + icon_state = "reactiveoff" + item_state = "reactiveoff" + add_fingerprint(user) + user.update_inv_wear_suit() + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + +/obj/item/clothing/suit/armor/reactive/emp_act(severity) + active = 0 + icon_state = "reactiveoff" + item_state = "reactiveoff" + if(istype(loc, /mob/living/carbon/human)) + var/mob/living/carbon/human/C = loc + C.update_inv_wear_suit() + ..() + +//When the wearer gets hit, this armor will teleport the user a short distance away (to safety or to more danger, no one knows. That's the fun of it!) +/obj/item/clothing/suit/armor/reactive/teleport + name = "reactive teleport armor" + desc = "Someone seperated our Research Director from his own head!" + var/tele_range = 6 + +/obj/item/clothing/suit/armor/reactive/teleport/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + if(!active) + return 0 + if(prob(hit_reaction_chance)) + var/mob/living/carbon/human/H = owner + owner.visible_message("The reactive teleport system flings [H] clear of [attack_text]!") + var/list/turfs = new/list() + for(var/turf/T in orange(tele_range, H)) + if(istype(T, /turf/space)) + continue + if(T.density) + continue + if(T.x>world.maxx-tele_range || T.xworld.maxy-tele_range || T.yThe [src] blocks the [attack_text], sending out jets of flame!") + for(var/mob/living/carbon/C in range(6, owner)) + if(C != owner) + C.fire_stacks += 8 + C.IgniteMob() + owner.fire_stacks = -20 + return 1 + return 0 + + +/obj/item/clothing/suit/armor/reactive/stealth + name = "reactive stealth armor" + +/obj/item/clothing/suit/armor/reactive/stealth/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + if(!active) + return 0 + if(prob(hit_reaction_chance)) + var/mob/living/simple_animal/hostile/illusion/escape/E = new(owner.loc) + E.Copy_Parent(owner, 50) + E.GiveTarget(owner) //so it starts running right away + E.Goto(owner, E.move_to_delay, E.minimum_distance) + owner.alpha = 0 + owner.visible_message("[owner] is hit by [attack_text] in the chest!") //We pretend to be hit, since blocking it would stop the message otherwise + spawn(40) + owner.alpha = initial(owner.alpha) + return 1 + +/obj/item/clothing/suit/armor/reactive/tesla + name = "reactive tesla armor" + +/obj/item/clothing/suit/armor/reactive/tesla/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + if(!active) + return 0 + if(prob(hit_reaction_chance)) + owner.visible_message("The [src] blocks the [attack_text], sending out arcs of lightning!") + for(var/mob/living/M in view(6, owner)) + if(M == owner) + continue + owner.Beam(M,icon_state="lightning[rand(1, 12)]",icon='icons/effects/effects.dmi',time=5) + M.adjustFireLoss(25) + playsound(M, 'sound/machines/defib_zap.ogg', 50, 1, -1) + return 1 + +//All of the armor below is mostly unused + + +/obj/item/clothing/suit/armor/centcomm + name = "Cent. Com. armor" + desc = "A suit that protects against some damage." + icon_state = "centcom" + item_state = "centcom" + w_class = WEIGHT_CLASS_BULKY + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + allowed = list(/obj/item/gun/energy,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/tank/emergency_oxygen) + flags = THICKMATERIAL + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT + cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS + min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT + sprite_sheets = null + armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 90) + +/obj/item/clothing/suit/armor/heavy + name = "heavy armor" + desc = "A heavily armored suit that protects against moderate damage." + icon_state = "heavy" + item_state = "swat_suit" + armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 90) + w_class = WEIGHT_CLASS_BULKY + gas_transfer_coefficient = 0.90 + flags = THICKMATERIAL + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + slowdown = 3 + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT + hide_tail_by_species = list("Vox") + +/obj/item/clothing/suit/armor/tdome + armor = list("melee" = 80, "bullet" = 80, "laser" = 50, "energy" = 50, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 90) + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT + flags = THICKMATERIAL + cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + hide_tail_by_species = list("Vox") + +/obj/item/clothing/suit/armor/tdome/red + name = "Red Thunderdome Armor" + desc = "Armor worn by the red Thunderdome team." + icon_state = "tdred" + item_state = "tdred" + +/obj/item/clothing/suit/armor/tdome/green + name = "Green Thunderdome Armor" + desc = "Armor worn by the green Thunderdome team." + icon_state = "tdgreen" + item_state = "tdgreen" + +//Non-hardsuit ERT armor. +/obj/item/clothing/suit/armor/vest/ert + name = "emergency response team armor" + desc = "A set of armor worn by members of the Nanotrasen Emergency Response Team." + icon_state = "ertarmor_cmd" + item_state = "armor" + armor = list(melee = 30, bullet = 30, laser = 30, energy = 30, bomb = 20, bio = 0, rad = 0, fire = 50, acid = 50) + +//Commander +/obj/item/clothing/suit/armor/vest/ert/command + name = "emergency response team commander armor" + desc = "A set of armor worn by the commander of a Nanotrasen Emergency Response Team. Has blue highlights." + +//Security +/obj/item/clothing/suit/armor/vest/ert/security + name = "emergency response team security armor" + desc = "A set of armor worn by security members of the Nanotrasen Emergency Response Team. Has red highlights." + icon_state = "ertarmor_sec" + + +/obj/item/clothing/suit/armor/vest/ert/security/paranormal + name = "emergency response team paranormal armor" + desc = "A set of armor worn by paranormal members of the Nanotrasen Emergency Response Team. Has crusader sigils." + icon_state = "knight_templar" + item_state = "knight_templar" + +//Engineer +/obj/item/clothing/suit/armor/vest/ert/engineer + name = "emergency response team engineer armor" + desc = "A set of armor worn by engineering members of the Nanotrasen Emergency Response Team. Has orange highlights." + icon_state = "ertarmor_eng" + +//Medical +/obj/item/clothing/suit/armor/vest/ert/medical + name = "emergency response team medical armor" + desc = "A set of armor worn by medical members of the Nanotrasen Emergency Response Team. Has red and white highlights." + icon_state = "ertarmor_med" + +//Janitorial +/obj/item/clothing/suit/armor/vest/ert/janitor + name = "emergency response team janitor armor" + desc = "A set of armor worn by janitorial members of the Nanotrasen Emergency Response Team. Has red and white highlights." + icon_state = "ertarmor_jan" + +//same defense as basic sec armor +/obj/item/clothing/suit/storage/lawyer/blackjacket/armored + desc = "A snappy dress jacket, reinforced with a layer of armor protecting the torso." + allowed = list(/obj/item/tank/emergency_oxygen, /obj/item/gun/projectile/revolver, /obj/item/gun/projectile/automatic/pistol) + body_parts_covered = UPPER_TORSO|LOWER_TORSO + cold_protection = UPPER_TORSO|LOWER_TORSO + min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT + heat_protection = UPPER_TORSO|LOWER_TORSO + max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT + armor = list(melee = 25, bullet = 15, laser = 25, energy = 10, bomb = 25, bio = 0, rad = 0, fire = 40, acid = 40) + +//LAVALAND! + +/obj/item/clothing/suit/hooded/drake + name = "drake armour" + icon_state = "dragon" + item_state = "dragon" + desc = "A suit of armour fashioned from the remains of an ash drake." + allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/resonator, /obj/item/mining_scanner, /obj/item/t_scanner/adv_mining_scanner, /obj/item/gun/energy/kinetic_accelerator, /obj/item/pickaxe, /obj/item/twohanded/spear) + armor = list("melee" = 70, "bullet" = 30, "laser" = 50, "energy" = 40, "bomb" = 70, "bio" = 60, "rad" = 50, "fire" = 100, "acid" = 100) + hoodtype = /obj/item/clothing/head/hooded/drake + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = FIRE_PROOF | ACID_PROOF + +/obj/item/clothing/head/hooded/drake + name = "drake helmet" + icon_state = "dragon" + item_state = "dragon" + desc = "The skull of a dragon." + armor = list("melee" = 70, "bullet" = 30, "laser" = 50, "energy" = 40, "bomb" = 70, "bio" = 60, "rad" = 50, "fire" = 100, "acid" = 100) + heat_protection = HEAD + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + resistance_flags = FIRE_PROOF | ACID_PROOF + flags = BLOCKHAIR + flags_cover = HEADCOVERSEYES + +/obj/item/clothing/suit/hooded/goliath + name = "goliath cloak" + icon_state = "goliath_cloak" + item_state = "goliath_cloak" + desc = "A staunch, practical cape made out of numerous monster materials, it is coveted amongst exiles & hermits." + allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/pickaxe, /obj/item/twohanded/spear, /obj/item/organ/internal/regenerative_core/legion, /obj/item/kitchen/knife/combat/survival) + armor = list("melee" = 35, "bullet" = 10, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 60, "acid" = 60) //a fair alternative to bone armor, requiring alternative materials and gaining a suit slot + hoodtype = /obj/item/clothing/head/hooded/goliath + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/head/hooded/goliath + name = "goliath cloak hood" + icon_state = "golhood" + item_state = "golhood" + desc = "A protective & concealing hood." + armor = list("melee" = 35, "bullet" = 10, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 60, "acid" = 60) + flags = BLOCKHAIR + flags_cover = HEADCOVERSEYES + +/obj/item/clothing/suit/armor/bone + name = "bone armor" + desc = "A tribal armor plate, crafted from animal bone." + icon_state = "bonearmor" + item_state = "bonearmor" + blood_overlay_type = "armor" + armor = list("melee" = 35, "bullet" = 25, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS diff --git a/code/modules/clothing/suits/bio.dm b/code/modules/clothing/suits/bio.dm index 6a2d582d355..1b7100c02bc 100644 --- a/code/modules/clothing/suits/bio.dm +++ b/code/modules/clothing/suits/bio.dm @@ -1,111 +1,111 @@ -//Biosuit complete with shoes (in the item sprite) -/obj/item/clothing/head/bio_hood - name = "bio hood" - icon_state = "bio" - desc = "A hood that protects the head and face from biological comtaminants." - permeability_coefficient = 0.01 - flags = BLOCKHAIR | THICKMATERIAL - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 80, "fire" = 30, "acid" = 100) - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES - resistance_flags = ACID_PROOF - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi', - "Unathi" = 'icons/mob/species/unathi/head.dmi', - "Tajaran" = 'icons/mob/species/tajaran/head.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/head.dmi', - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - -/obj/item/clothing/suit/bio_suit - name = "bio suit" - desc = "A suit that protects against biological contamination." - icon_state = "bio" - item_state = "bio_suit" - w_class = WEIGHT_CLASS_BULKY - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.01 - flags = THICKMATERIAL - flags_size = ONESIZEFITSALL - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - slowdown = 1 - allowed = list(/obj/item/tank/emergency_oxygen,/obj/item/pen,/obj/item/flashlight/pen) - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 80, "fire" = 30, "acid" = 100) - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL - strip_delay = 70 - put_on_delay = 70 - resistance_flags = ACID_PROOF - sprite_sheets = list( - "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', - "Unathi" = 'icons/mob/species/unathi/suit.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - - -//Standard biosuit, orange stripe -/obj/item/clothing/head/bio_hood/general - icon_state = "bio_general" - -/obj/item/clothing/suit/bio_suit/general - icon_state = "bio_general" - - -//Virology biosuit, green stripe -/obj/item/clothing/head/bio_hood/virology - icon_state = "bio_virology" - -/obj/item/clothing/suit/bio_suit/virology - icon_state = "bio_virology" - - -//Security biosuit, grey with red stripe across the chest -/obj/item/clothing/head/bio_hood/security - armor = list("melee" = 25, "bullet" = 15, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 100, "rad" = 80, "fire" = 30, "acid" = 100) - icon_state = "bio_security" - -/obj/item/clothing/suit/bio_suit/security - armor = list("melee" = 25, "bullet" = 15, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 100, "rad" = 80, "fire" = 30, "acid" = 100) - icon_state = "bio_security" - - -//Janitor's biosuit, grey with purple arms -/obj/item/clothing/head/bio_hood/janitor - icon_state = "bio_janitor" - -/obj/item/clothing/suit/bio_suit/janitor - icon_state = "bio_janitor" - - -//Scientist's biosuit, white with a pink-ish hue -/obj/item/clothing/head/bio_hood/scientist - icon_state = "bio_scientist" - -/obj/item/clothing/suit/bio_suit/scientist - icon_state = "bio_scientist" - - -//CMO's biosuit, blue stripe -/obj/item/clothing/suit/bio_suit/cmo - icon_state = "bio_cmo" - -/obj/item/clothing/head/bio_hood/cmo - icon_state = "bio_cmo" - - -//Plague Dr mask can be found in clothing/masks/gasmask.dm -/obj/item/clothing/suit/bio_suit/plaguedoctorsuit - name = "Plague doctor suit" - desc = "It protected doctors from the Black Death, back then. You bet your arse it's gonna help you against viruses." - icon_state = "plaguedoctor" - item_state = "bio_suit" - strip_delay = 40 - put_on_delay = 20 - - sprite_sheets = list( - "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', - "Unathi" = 'icons/mob/species/unathi/suit.dmi', - "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', - ) - hide_tail_by_species = list("Unathi, Tajaran, Vulpkanin") \ No newline at end of file +//Biosuit complete with shoes (in the item sprite) +/obj/item/clothing/head/bio_hood + name = "bio hood" + icon_state = "bio" + desc = "A hood that protects the head and face from biological comtaminants." + permeability_coefficient = 0.01 + flags = BLOCKHAIR | THICKMATERIAL + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 80, "fire" = 30, "acid" = 100) + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES + resistance_flags = ACID_PROOF + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi', + "Unathi" = 'icons/mob/species/unathi/head.dmi', + "Tajaran" = 'icons/mob/species/tajaran/head.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/head.dmi', + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + +/obj/item/clothing/suit/bio_suit + name = "bio suit" + desc = "A suit that protects against biological contamination." + icon_state = "bio" + item_state = "bio_suit" + w_class = WEIGHT_CLASS_BULKY + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.01 + flags = THICKMATERIAL + flags_size = ONESIZEFITSALL + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + slowdown = 1 + allowed = list(/obj/item/tank/emergency_oxygen,/obj/item/pen,/obj/item/flashlight/pen) + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 80, "fire" = 30, "acid" = 100) + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL + strip_delay = 70 + put_on_delay = 70 + resistance_flags = ACID_PROOF + sprite_sheets = list( + "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', + "Unathi" = 'icons/mob/species/unathi/suit.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + + +//Standard biosuit, orange stripe +/obj/item/clothing/head/bio_hood/general + icon_state = "bio_general" + +/obj/item/clothing/suit/bio_suit/general + icon_state = "bio_general" + + +//Virology biosuit, green stripe +/obj/item/clothing/head/bio_hood/virology + icon_state = "bio_virology" + +/obj/item/clothing/suit/bio_suit/virology + icon_state = "bio_virology" + + +//Security biosuit, grey with red stripe across the chest +/obj/item/clothing/head/bio_hood/security + armor = list("melee" = 25, "bullet" = 15, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 100, "rad" = 80, "fire" = 30, "acid" = 100) + icon_state = "bio_security" + +/obj/item/clothing/suit/bio_suit/security + armor = list("melee" = 25, "bullet" = 15, "laser" = 25, "energy" = 10, "bomb" = 25, "bio" = 100, "rad" = 80, "fire" = 30, "acid" = 100) + icon_state = "bio_security" + + +//Janitor's biosuit, grey with purple arms +/obj/item/clothing/head/bio_hood/janitor + icon_state = "bio_janitor" + +/obj/item/clothing/suit/bio_suit/janitor + icon_state = "bio_janitor" + + +//Scientist's biosuit, white with a pink-ish hue +/obj/item/clothing/head/bio_hood/scientist + icon_state = "bio_scientist" + +/obj/item/clothing/suit/bio_suit/scientist + icon_state = "bio_scientist" + + +//CMO's biosuit, blue stripe +/obj/item/clothing/suit/bio_suit/cmo + icon_state = "bio_cmo" + +/obj/item/clothing/head/bio_hood/cmo + icon_state = "bio_cmo" + + +//Plague Dr mask can be found in clothing/masks/gasmask.dm +/obj/item/clothing/suit/bio_suit/plaguedoctorsuit + name = "Plague doctor suit" + desc = "It protected doctors from the Black Death, back then. You bet your arse it's gonna help you against viruses." + icon_state = "plaguedoctor" + item_state = "bio_suit" + strip_delay = 40 + put_on_delay = 20 + + sprite_sheets = list( + "Tajaran" = 'icons/mob/species/tajaran/suit.dmi', + "Unathi" = 'icons/mob/species/unathi/suit.dmi', + "Vulpkanin" = 'icons/mob/species/vulpkanin/suit.dmi', + ) + hide_tail_by_species = list("Unathi, Tajaran, Vulpkanin") diff --git a/code/modules/clothing/suits/jobs.dm b/code/modules/clothing/suits/jobs.dm index dde11f1fcee..bcb71edf356 100644 --- a/code/modules/clothing/suits/jobs.dm +++ b/code/modules/clothing/suits/jobs.dm @@ -1,385 +1,385 @@ -/* - * Job related - */ -//Paramedic -/obj/item/clothing/suit/storage/paramedic - name = "paramedic vest" - desc = "A hazard vest used in the recovery of bodies." - icon_state = "paramedic-vest" - item_state = "paramedic-vest" - allowed = list(/obj/item/stack/medical, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/syringe, \ - /obj/item/healthanalyzer, /obj/item/flashlight, /obj/item/radio, /obj/item/tank/emergency_oxygen,/obj/item/rad_laser) - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 10, rad = 10, fire = 50, acid = 50) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Brig Physician -/obj/item/clothing/suit/storage/brigdoc - name = "brig physician vest" - desc = "A vest often worn by doctors caring for inmates." - icon_state = "brigphysician-vest" - item_state = "brigphysician-vest" - allowed = list(/obj/item/stack/medical, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/syringe, \ - /obj/item/healthanalyzer, /obj/item/flashlight, \ - /obj/item/radio, /obj/item/tank/emergency_oxygen,/obj/item/rad_laser) - armor = list(melee = 10, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 10, rad = 0, fire = 50, acid = 50) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Botanist -/obj/item/clothing/suit/apron - name = "apron" - desc = "A basic blue apron." - icon_state = "apron" - item_state = "apron" - blood_overlay_type = "armor" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - allowed = list(/obj/item/reagent_containers/spray/plantbgone,/obj/item/plant_analyzer,/obj/item/seeds,/obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/cultivator,/obj/item/reagent_containers/spray/pestspray,/obj/item/hatchet,/obj/item/storage/bag/plants) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Captain -/obj/item/clothing/suit/captunic - name = "captain's parade tunic" - desc = "Worn by a Captain to show their class." - icon_state = "captunic" - item_state = "bio_suit" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_inv = HIDEJUMPSUIT - flags_size = ONESIZEFITSALL - allowed = list(/obj/item/disk, /obj/item/stamp, /obj/item/reagent_containers/food/drinks/flask, /obj/item/melee, /obj/item/storage/lockbox/medal, /obj/item/flash, /obj/item/storage/box/matches, /obj/item/lighter, /obj/item/clothing/mask/cigarette, /obj/item/storage/fancy/cigarettes, /obj/item/tank/emergency_oxygen) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/obj/item/clothing/suit/captunic/capjacket - name = "captain's uniform jacket" - desc = "A less formal jacket for everyday captain use." - icon_state = "capjacket" - item_state = "bio_suit" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_inv = HIDEJUMPSUIT - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/obj/item/clothing/suit/mantle/armor/captain - name = "captain's cloak" - desc = "An armor-plated piece of fashion for the ruling elite. Protect your upper half in style." - icon_state = "capmantle" - item_state = "capmantle" - armor = list(melee = 50, bullet = 40, laser = 50, energy = 10, bomb = 25, bio = 0, rad = 0, fire = 50, acid = 50) - -//Chaplain -/obj/item/clothing/suit/hooded/chaplain_hoodie - name = "chaplain hoodie" - desc = "This suit says to you 'hush'!" - icon_state = "chaplain_hoodie" - item_state = "chaplain_hoodie" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - hoodtype = /obj/item/clothing/head/hooded/chaplain_hood - allowed = list(/obj/item/storage/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/emergency_oxygen) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Chaplain -/obj/item/clothing/suit/hooded/nun - name = "nun robe" - desc = "Maximum piety in this star system." - icon_state = "nun" - item_state = "nun" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|HANDS - flags_inv = HIDESHOES|HIDEJUMPSUIT - hoodtype = /obj/item/clothing/head/hooded/nun_hood - allowed = list(/obj/item/storage/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/emergency_oxygen) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Chaplain -/obj/item/clothing/suit/hooded/monk - name = "monk robe" - desc = "Wooden board not included." - icon_state = "monkrobe" - item_state = "monkrobe" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|HANDS - flags_inv = HIDESHOES|HIDEJUMPSUIT - hoodtype = /obj/item/clothing/head/hooded/monk_hood - allowed = list(/obj/item/storage/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/emergency_oxygen) - -/obj/item/clothing/suit/witchhunter - name = "witchhunter garb" - desc = "Dosen't weigh the same a a duck." - icon_state = "witchhunter" - item_state = "witchhunter" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - allowed = list(/obj/item/storage/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/emergency_oxygen) - - - //Chef -/obj/item/clothing/suit/toggle/chef - name = "chef's apron" - - -//Chef -/obj/item/clothing/suit/chef - name = "chef's apron" - desc = "An apron used by a high class chef." - icon_state = "chef" - item_state = "chef" - gas_transfer_coefficient = 0.90 - permeability_coefficient = 0.50 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - allowed = list (/obj/item/kitchen/knife) - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Chef -/obj/item/clothing/suit/chef/classic - name = "classic chef's apron" - desc = "A basic, dull, white chef's apron." - icon_state = "apronchef" - item_state = "apronchef" - blood_overlay_type = "armor" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - allowed = list(/obj/item/kitchen/knife) - -//Chief Engineer -/obj/item/clothing/suit/mantle/chief_engineer - name = "chief engineer's mantle" - desc = "A slick, authoritative cloak designed for the Chief Engineer." - icon_state = "cemantle" - item_state = "cemantle" - allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/t_scanner, /obj/item/rcd) - -//Chief Medical Officer -/obj/item/clothing/suit/mantle/labcoat/chief_medical_officer - name = "chief medical officer's mantle" - desc = "An absorbent, clean cover found on the shoulders of the Chief Medical Officer." - icon_state = "cmomantle" - item_state = "cmomantle" - -//Detective -/obj/item/clothing/suit/storage/det_suit - name = "coat" - desc = "An 18th-century multi-purpose trenchcoat. Someone who wears this means serious business." - icon_state = "detective" - item_state = "det_suit" - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - allowed = list(/obj/item/tank/emergency_oxygen, /obj/item/reagent_containers/spray/pepper, /obj/item/flashlight, /obj/item/gun, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/detective_scanner, /obj/item/taperecorder) - armor = list("melee" = 25, "bullet" = 10, "laser" = 25, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 45) - cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_size = ONESIZEFITSALL - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Forensics -/obj/item/clothing/suit/storage/det_suit/forensics - name = "jacket" - desc = "A forensics technician jacket." - item_state = "det_suit" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS - heat_protection = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/suit/storage/det_suit/forensics/red - name = "red jacket" - desc = "A red forensics technician jacket." - icon_state = "forensics_red" - -/obj/item/clothing/suit/storage/det_suit/forensics/blue - name = "blue jacket" - desc = "A blue forensics technician jacket." - icon_state = "forensics_blue" - -//Blueshield -/obj/item/clothing/suit/storage/blueshield - name = "blueshield coat" - desc = "NT deluxe ripoff. You finally have your own coat." - icon_state = "blueshieldcoat" - item_state = "blueshieldcoat" - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - allowed = list(/obj/item/gun/energy,/obj/item/reagent_containers/spray/pepper,/obj/item/gun/projectile,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/flashlight/seclite,/obj/item/melee/classic_baton/telescopic) - armor = list(melee = 25, bullet = 10, laser = 25, energy = 10, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 45) - cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS - flags_size = ONESIZEFITSALL - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Engineering -/obj/item/clothing/suit/storage/hazardvest - name = "hazard vest" - desc = "A high-visibility vest used in work zones." - icon_state = "hazard" - item_state = "hazard" - blood_overlay_type = "armor" - allowed = list (/obj/item/flashlight, /obj/item/t_scanner, /obj/item/tank/emergency_oxygen) - resistance_flags = NONE - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Lawyer -/obj/item/clothing/suit/storage/lawyer - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/obj/item/clothing/suit/storage/lawyer/blackjacket - name = "black suit jacket" - desc = "A snappy dress jacket." - icon_state = "suitjacket_black_open" - item_state = "suitjacket_black_open" - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|ARMS - ignore_suitadjust = 0 - suit_adjusted = 1 - actions_types = list(/datum/action/item_action/button) - adjust_flavour = "unbutton" - -/obj/item/clothing/suit/storage/lawyer/bluejacket - name = "blue suit jacket" - desc = "A snappy dress jacket." - icon_state = "suitjacket_blue_open" - item_state = "suitjacket_blue_open" - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|ARMS - ignore_suitadjust = 0 - suit_adjusted = 1 - actions_types = list(/datum/action/item_action/button) - adjust_flavour = "unbutton" - -/obj/item/clothing/suit/storage/lawyer/purpjacket - name = "purple suit jacket" - desc = "A snappy dress jacket." - icon_state = "suitjacket_purp" - item_state = "suitjacket_purp" - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|ARMS - -//Head of Security -/obj/item/clothing/suit/mantle/armor - name = "armored shawl" - desc = "A reinforced shawl, worn by the Head of Security. Do you dare take up their mantle?" - icon_state = "hosmantle" - item_state = "hosmantle" - allowed = list(/obj/item/gun/energy, /obj/item/reagent_containers/spray/pepper, /obj/item/gun/projectile, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/flashlight/seclite, /obj/item/melee/classic_baton/telescopic, /obj/item/kitchen/knife/combat) - armor = list(melee = 30, bullet = 30, laser = 30, energy = 10, bomb = 25, bio = 0, rad = 0, fire = 50, acid = 50) - min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT - heat_protection = UPPER_TORSO|ARMS - max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT - strip_delay = 60 - put_on_delay = 40 - resistance_flags = NONE - -//Head of Personnel -/obj/item/clothing/suit/mantle/armor/head_of_personnel - name = "head of personnel's shawl" - desc = "An armored shawl for the head of personnel. It's remarkably well kept." - icon_state = "hopmantle" - item_state = "hopmantle" - armor = list(melee = 25, bullet = 15, laser = 25, energy = 10, bomb = 25, bio = 0, rad = 0, fire = 50, acid = 50) - -//Internal Affairs -/obj/item/clothing/suit/storage/internalaffairs - name = "\improper Internal Affairs jacket" - desc = "A smooth black jacket." - icon_state = "ia_jacket_open" - item_state = "ia_jacket_open" - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|ARMS - ignore_suitadjust = 0 - suit_adjusted = 1 - actions_types = list(/datum/action/item_action/button) - adjust_flavour = "unbutton" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/obj/item/clothing/suit/storage/ntrep - name = "\improper Nanotrasen Representative jacket" - desc = "A fancy black jacket; standard issue to Nanotrasen Representatives." - icon_state = "ntrep" - item_state = "ntrep" - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|ARMS - ignore_suitadjust = 0 - actions_types = list(/datum/action/item_action/button) - adjust_flavour = "unbutton" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Medical -/obj/item/clothing/suit/storage/fr_jacket - name = "first responder jacket" - desc = "A high-visibility jacket worn by medical first responders." - icon_state = "fr_jacket_open" - item_state = "fr_jacket_open" - blood_overlay_type = "armor" - allowed = list(/obj/item/stack/medical, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/syringe, \ - /obj/item/healthanalyzer, /obj/item/flashlight, /obj/item/radio, /obj/item/tank/emergency_oxygen,/obj/item/rad_laser) - ignore_suitadjust = 0 - suit_adjusted = 1 - actions_types = list(/datum/action/item_action/button) - adjust_flavour = "unbutton" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -//Mime -/obj/item/clothing/suit/suspenders - name = "suspenders" - desc = "They suspend the illusion of the mime's play." - icon = 'icons/obj/clothing/belts.dmi' - icon_state = "suspenders" - blood_overlay_type = "armor" //it's the less thing that I can put here - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/obj/item/clothing/suit/suspenders/nodrop - flags = NODROP - -// Surgeon -/obj/item/clothing/suit/apron/surgical - name = "surgical apron" - desc = "A sterile blue surgical apron." - icon_state = "surgical" - item_state = "surgical" - allowed = list(/obj/item/scalpel, /obj/item/surgical_drapes, /obj/item/cautery, /obj/item/hemostat, /obj/item/retractor) - -//Research Director -/obj/item/clothing/suit/mantle/labcoat - name = "research director's mantle" - desc = "A tweed mantle, worn by the Research Director. Smells like science." - icon_state = "rdmantle" - item_state = "rdmantle" - allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/food/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/rad_laser) - armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 50, rad = 0, fire = 50, acid = 50) +/* + * Job related + */ +//Paramedic +/obj/item/clothing/suit/storage/paramedic + name = "paramedic vest" + desc = "A hazard vest used in the recovery of bodies." + icon_state = "paramedic-vest" + item_state = "paramedic-vest" + allowed = list(/obj/item/stack/medical, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/syringe, \ + /obj/item/healthanalyzer, /obj/item/flashlight, /obj/item/radio, /obj/item/tank/emergency_oxygen,/obj/item/rad_laser) + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 10, rad = 10, fire = 50, acid = 50) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Brig Physician +/obj/item/clothing/suit/storage/brigdoc + name = "brig physician vest" + desc = "A vest often worn by doctors caring for inmates." + icon_state = "brigphysician-vest" + item_state = "brigphysician-vest" + allowed = list(/obj/item/stack/medical, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/syringe, \ + /obj/item/healthanalyzer, /obj/item/flashlight, \ + /obj/item/radio, /obj/item/tank/emergency_oxygen,/obj/item/rad_laser) + armor = list(melee = 10, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 10, rad = 0, fire = 50, acid = 50) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Botanist +/obj/item/clothing/suit/apron + name = "apron" + desc = "A basic blue apron." + icon_state = "apron" + item_state = "apron" + blood_overlay_type = "armor" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + allowed = list(/obj/item/reagent_containers/spray/plantbgone,/obj/item/plant_analyzer,/obj/item/seeds,/obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/cultivator,/obj/item/reagent_containers/spray/pestspray,/obj/item/hatchet,/obj/item/storage/bag/plants) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Captain +/obj/item/clothing/suit/captunic + name = "captain's parade tunic" + desc = "Worn by a Captain to show their class." + icon_state = "captunic" + item_state = "bio_suit" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDEJUMPSUIT + flags_size = ONESIZEFITSALL + allowed = list(/obj/item/disk, /obj/item/stamp, /obj/item/reagent_containers/food/drinks/flask, /obj/item/melee, /obj/item/storage/lockbox/medal, /obj/item/flash, /obj/item/storage/box/matches, /obj/item/lighter, /obj/item/clothing/mask/cigarette, /obj/item/storage/fancy/cigarettes, /obj/item/tank/emergency_oxygen) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/obj/item/clothing/suit/captunic/capjacket + name = "captain's uniform jacket" + desc = "A less formal jacket for everyday captain use." + icon_state = "capjacket" + item_state = "bio_suit" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_inv = HIDEJUMPSUIT + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/obj/item/clothing/suit/mantle/armor/captain + name = "captain's cloak" + desc = "An armor-plated piece of fashion for the ruling elite. Protect your upper half in style." + icon_state = "capmantle" + item_state = "capmantle" + armor = list(melee = 50, bullet = 40, laser = 50, energy = 10, bomb = 25, bio = 0, rad = 0, fire = 50, acid = 50) + +//Chaplain +/obj/item/clothing/suit/hooded/chaplain_hoodie + name = "chaplain hoodie" + desc = "This suit says to you 'hush'!" + icon_state = "chaplain_hoodie" + item_state = "chaplain_hoodie" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + hoodtype = /obj/item/clothing/head/hooded/chaplain_hood + allowed = list(/obj/item/storage/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/emergency_oxygen) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Chaplain +/obj/item/clothing/suit/hooded/nun + name = "nun robe" + desc = "Maximum piety in this star system." + icon_state = "nun" + item_state = "nun" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|HANDS + flags_inv = HIDESHOES|HIDEJUMPSUIT + hoodtype = /obj/item/clothing/head/hooded/nun_hood + allowed = list(/obj/item/storage/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/emergency_oxygen) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Chaplain +/obj/item/clothing/suit/hooded/monk + name = "monk robe" + desc = "Wooden board not included." + icon_state = "monkrobe" + item_state = "monkrobe" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|HANDS + flags_inv = HIDESHOES|HIDEJUMPSUIT + hoodtype = /obj/item/clothing/head/hooded/monk_hood + allowed = list(/obj/item/storage/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/emergency_oxygen) + +/obj/item/clothing/suit/witchhunter + name = "witchhunter garb" + desc = "Dosen't weigh the same a a duck." + icon_state = "witchhunter" + item_state = "witchhunter" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + allowed = list(/obj/item/storage/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/emergency_oxygen) + + + //Chef +/obj/item/clothing/suit/toggle/chef + name = "chef's apron" + + +//Chef +/obj/item/clothing/suit/chef + name = "chef's apron" + desc = "An apron used by a high class chef." + icon_state = "chef" + item_state = "chef" + gas_transfer_coefficient = 0.90 + permeability_coefficient = 0.50 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + allowed = list (/obj/item/kitchen/knife) + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Chef +/obj/item/clothing/suit/chef/classic + name = "classic chef's apron" + desc = "A basic, dull, white chef's apron." + icon_state = "apronchef" + item_state = "apronchef" + blood_overlay_type = "armor" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + allowed = list(/obj/item/kitchen/knife) + +//Chief Engineer +/obj/item/clothing/suit/mantle/chief_engineer + name = "chief engineer's mantle" + desc = "A slick, authoritative cloak designed for the Chief Engineer." + icon_state = "cemantle" + item_state = "cemantle" + allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/t_scanner, /obj/item/rcd) + +//Chief Medical Officer +/obj/item/clothing/suit/mantle/labcoat/chief_medical_officer + name = "chief medical officer's mantle" + desc = "An absorbent, clean cover found on the shoulders of the Chief Medical Officer." + icon_state = "cmomantle" + item_state = "cmomantle" + +//Detective +/obj/item/clothing/suit/storage/det_suit + name = "coat" + desc = "An 18th-century multi-purpose trenchcoat. Someone who wears this means serious business." + icon_state = "detective" + item_state = "det_suit" + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + allowed = list(/obj/item/tank/emergency_oxygen, /obj/item/reagent_containers/spray/pepper, /obj/item/flashlight, /obj/item/gun, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/storage/fancy/cigarettes, /obj/item/lighter, /obj/item/detective_scanner, /obj/item/taperecorder) + armor = list("melee" = 25, "bullet" = 10, "laser" = 25, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 45) + cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_size = ONESIZEFITSALL + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Forensics +/obj/item/clothing/suit/storage/det_suit/forensics + name = "jacket" + desc = "A forensics technician jacket." + item_state = "det_suit" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS + heat_protection = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/suit/storage/det_suit/forensics/red + name = "red jacket" + desc = "A red forensics technician jacket." + icon_state = "forensics_red" + +/obj/item/clothing/suit/storage/det_suit/forensics/blue + name = "blue jacket" + desc = "A blue forensics technician jacket." + icon_state = "forensics_blue" + +//Blueshield +/obj/item/clothing/suit/storage/blueshield + name = "blueshield coat" + desc = "NT deluxe ripoff. You finally have your own coat." + icon_state = "blueshieldcoat" + item_state = "blueshieldcoat" + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + allowed = list(/obj/item/gun/energy,/obj/item/reagent_containers/spray/pepper,/obj/item/gun/projectile,/obj/item/ammo_box,/obj/item/ammo_casing,/obj/item/melee/baton,/obj/item/restraints/handcuffs,/obj/item/flashlight/seclite,/obj/item/melee/classic_baton/telescopic) + armor = list(melee = 25, bullet = 10, laser = 25, energy = 10, bomb = 0, bio = 0, rad = 0, fire = 0, acid = 45) + cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS + flags_size = ONESIZEFITSALL + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Engineering +/obj/item/clothing/suit/storage/hazardvest + name = "hazard vest" + desc = "A high-visibility vest used in work zones." + icon_state = "hazard" + item_state = "hazard" + blood_overlay_type = "armor" + allowed = list (/obj/item/flashlight, /obj/item/t_scanner, /obj/item/tank/emergency_oxygen) + resistance_flags = NONE + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Lawyer +/obj/item/clothing/suit/storage/lawyer + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/obj/item/clothing/suit/storage/lawyer/blackjacket + name = "black suit jacket" + desc = "A snappy dress jacket." + icon_state = "suitjacket_black_open" + item_state = "suitjacket_black_open" + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|ARMS + ignore_suitadjust = 0 + suit_adjusted = 1 + actions_types = list(/datum/action/item_action/button) + adjust_flavour = "unbutton" + +/obj/item/clothing/suit/storage/lawyer/bluejacket + name = "blue suit jacket" + desc = "A snappy dress jacket." + icon_state = "suitjacket_blue_open" + item_state = "suitjacket_blue_open" + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|ARMS + ignore_suitadjust = 0 + suit_adjusted = 1 + actions_types = list(/datum/action/item_action/button) + adjust_flavour = "unbutton" + +/obj/item/clothing/suit/storage/lawyer/purpjacket + name = "purple suit jacket" + desc = "A snappy dress jacket." + icon_state = "suitjacket_purp" + item_state = "suitjacket_purp" + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|ARMS + +//Head of Security +/obj/item/clothing/suit/mantle/armor + name = "armored shawl" + desc = "A reinforced shawl, worn by the Head of Security. Do you dare take up their mantle?" + icon_state = "hosmantle" + item_state = "hosmantle" + allowed = list(/obj/item/gun/energy, /obj/item/reagent_containers/spray/pepper, /obj/item/gun/projectile, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/flashlight/seclite, /obj/item/melee/classic_baton/telescopic, /obj/item/kitchen/knife/combat) + armor = list(melee = 30, bullet = 30, laser = 30, energy = 10, bomb = 25, bio = 0, rad = 0, fire = 50, acid = 50) + min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT + heat_protection = UPPER_TORSO|ARMS + max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT + strip_delay = 60 + put_on_delay = 40 + resistance_flags = NONE + +//Head of Personnel +/obj/item/clothing/suit/mantle/armor/head_of_personnel + name = "head of personnel's shawl" + desc = "An armored shawl for the head of personnel. It's remarkably well kept." + icon_state = "hopmantle" + item_state = "hopmantle" + armor = list(melee = 25, bullet = 15, laser = 25, energy = 10, bomb = 25, bio = 0, rad = 0, fire = 50, acid = 50) + +//Internal Affairs +/obj/item/clothing/suit/storage/internalaffairs + name = "\improper Internal Affairs jacket" + desc = "A smooth black jacket." + icon_state = "ia_jacket_open" + item_state = "ia_jacket_open" + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|ARMS + ignore_suitadjust = 0 + suit_adjusted = 1 + actions_types = list(/datum/action/item_action/button) + adjust_flavour = "unbutton" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/obj/item/clothing/suit/storage/ntrep + name = "\improper Nanotrasen Representative jacket" + desc = "A fancy black jacket; standard issue to Nanotrasen Representatives." + icon_state = "ntrep" + item_state = "ntrep" + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|ARMS + ignore_suitadjust = 0 + actions_types = list(/datum/action/item_action/button) + adjust_flavour = "unbutton" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Medical +/obj/item/clothing/suit/storage/fr_jacket + name = "first responder jacket" + desc = "A high-visibility jacket worn by medical first responders." + icon_state = "fr_jacket_open" + item_state = "fr_jacket_open" + blood_overlay_type = "armor" + allowed = list(/obj/item/stack/medical, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/syringe, \ + /obj/item/healthanalyzer, /obj/item/flashlight, /obj/item/radio, /obj/item/tank/emergency_oxygen,/obj/item/rad_laser) + ignore_suitadjust = 0 + suit_adjusted = 1 + actions_types = list(/datum/action/item_action/button) + adjust_flavour = "unbutton" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +//Mime +/obj/item/clothing/suit/suspenders + name = "suspenders" + desc = "They suspend the illusion of the mime's play." + icon = 'icons/obj/clothing/belts.dmi' + icon_state = "suspenders" + blood_overlay_type = "armor" //it's the less thing that I can put here + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/obj/item/clothing/suit/suspenders/nodrop + flags = NODROP + +// Surgeon +/obj/item/clothing/suit/apron/surgical + name = "surgical apron" + desc = "A sterile blue surgical apron." + icon_state = "surgical" + item_state = "surgical" + allowed = list(/obj/item/scalpel, /obj/item/surgical_drapes, /obj/item/cautery, /obj/item/hemostat, /obj/item/retractor) + +//Research Director +/obj/item/clothing/suit/mantle/labcoat + name = "research director's mantle" + desc = "A tweed mantle, worn by the Research Director. Smells like science." + icon_state = "rdmantle" + item_state = "rdmantle" + allowed = list(/obj/item/analyzer, /obj/item/stack/medical, /obj/item/dnainjector, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/hypospray, /obj/item/healthanalyzer, /obj/item/flashlight/pen, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/food/pill, /obj/item/storage/pill_bottle, /obj/item/paper, /obj/item/rad_laser) + armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 50, rad = 0, fire = 50, acid = 50) diff --git a/code/modules/clothing/suits/labcoat.dm b/code/modules/clothing/suits/labcoat.dm index 36157df8bc8..03df1bd74d8 100644 --- a/code/modules/clothing/suits/labcoat.dm +++ b/code/modules/clothing/suits/labcoat.dm @@ -1,65 +1,65 @@ -/obj/item/clothing/suit/storage/labcoat - name = "labcoat" - desc = "A suit that protects against minor chemical spills." - icon_state = "labcoat_open" - item_state = "labcoat_open" - ignore_suitadjust = 0 - suit_adjusted = 1 - blood_overlay_type = "coat" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - allowed = list(/obj/item/analyzer,/obj/item/stack/medical,/obj/item/dnainjector,/obj/item/reagent_containers/dropper,/obj/item/reagent_containers/syringe,/obj/item/reagent_containers/hypospray,/obj/item/healthanalyzer,/obj/item/flashlight/pen,/obj/item/reagent_containers/glass/bottle,/obj/item/reagent_containers/glass/beaker,/obj/item/reagent_containers/food/pill,/obj/item/storage/pill_bottle,/obj/item/paper,/obj/item/rad_laser) - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 50, "acid" = 50) - species_exception = list(/datum/species/golem) - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi', - "Grey" = 'icons/mob/species/grey/suit.dmi' - ) - actions_types = list(/datum/action/item_action/button) - adjust_flavour = "unbutton" - -/obj/item/clothing/suit/storage/labcoat/cmo - name = "chief medical officer's labcoat" - desc = "Bluer than the standard model." - icon_state = "labcoat_cmo_open" - item_state = "labcoat_cmo_open" - -/obj/item/clothing/suit/storage/labcoat/mad - name = "mad scientist's labcoat" - desc = "It makes you look capable of konking someone on the noggin and shooting them into space." - icon_state = "labcoat_green_open" - item_state = "labcoat_green_open" - -/obj/item/clothing/suit/storage/labcoat/genetics - name = "geneticist labcoat" - desc = "A suit that protects against minor chemical spills. Has a blue stripe on the shoulder." - icon_state = "labcoat_gen_open" - item_state = "labcoat_gen_open" - -/obj/item/clothing/suit/storage/labcoat/chemist - name = "chemist labcoat" - desc = "A suit that protects against minor chemical spills. Has an orange stripe on the shoulder." - icon_state = "labcoat_chem_open" - item_state = "labcoat_chem_open" - -/obj/item/clothing/suit/storage/labcoat/virologist - name = "virologist labcoat" - desc = "A suit that protects against minor chemical spills. Offers slightly more protection against biohazards than the standard model. Has a green stripe on the shoulder." - icon_state = "labcoat_vir_open" - -/obj/item/clothing/suit/storage/labcoat/science - name = "scientist labcoat" - desc = "A suit that protects against minor chemical spills. Has a purple stripe on the shoulder." - icon_state = "labcoat_tox_open" - item_state = "labcoat_tox_open" - -/obj/item/clothing/suit/storage/labcoat/mortician - name = "coroner labcoat" - desc = "A suit that protects against minor chemical spills. Has a black stripe on the shoulder." - icon_state = "labcoat_mort_open" - item_state = "labcoat_mort_open" - -/obj/item/clothing/suit/storage/labcoat/emt - name = "EMT labcoat" - desc = "A comfortable suit for paramedics. Has dark colours." - icon_state = "labcoat_emt_open" - item_state = "labcoat_emt_open" +/obj/item/clothing/suit/storage/labcoat + name = "labcoat" + desc = "A suit that protects against minor chemical spills." + icon_state = "labcoat_open" + item_state = "labcoat_open" + ignore_suitadjust = 0 + suit_adjusted = 1 + blood_overlay_type = "coat" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + allowed = list(/obj/item/analyzer,/obj/item/stack/medical,/obj/item/dnainjector,/obj/item/reagent_containers/dropper,/obj/item/reagent_containers/syringe,/obj/item/reagent_containers/hypospray,/obj/item/healthanalyzer,/obj/item/flashlight/pen,/obj/item/reagent_containers/glass/bottle,/obj/item/reagent_containers/glass/beaker,/obj/item/reagent_containers/food/pill,/obj/item/storage/pill_bottle,/obj/item/paper,/obj/item/rad_laser) + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 50, "rad" = 0, "fire" = 50, "acid" = 50) + species_exception = list(/datum/species/golem) + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi', + "Grey" = 'icons/mob/species/grey/suit.dmi' + ) + actions_types = list(/datum/action/item_action/button) + adjust_flavour = "unbutton" + +/obj/item/clothing/suit/storage/labcoat/cmo + name = "chief medical officer's labcoat" + desc = "Bluer than the standard model." + icon_state = "labcoat_cmo_open" + item_state = "labcoat_cmo_open" + +/obj/item/clothing/suit/storage/labcoat/mad + name = "mad scientist's labcoat" + desc = "It makes you look capable of konking someone on the noggin and shooting them into space." + icon_state = "labcoat_green_open" + item_state = "labcoat_green_open" + +/obj/item/clothing/suit/storage/labcoat/genetics + name = "geneticist labcoat" + desc = "A suit that protects against minor chemical spills. Has a blue stripe on the shoulder." + icon_state = "labcoat_gen_open" + item_state = "labcoat_gen_open" + +/obj/item/clothing/suit/storage/labcoat/chemist + name = "chemist labcoat" + desc = "A suit that protects against minor chemical spills. Has an orange stripe on the shoulder." + icon_state = "labcoat_chem_open" + item_state = "labcoat_chem_open" + +/obj/item/clothing/suit/storage/labcoat/virologist + name = "virologist labcoat" + desc = "A suit that protects against minor chemical spills. Offers slightly more protection against biohazards than the standard model. Has a green stripe on the shoulder." + icon_state = "labcoat_vir_open" + +/obj/item/clothing/suit/storage/labcoat/science + name = "scientist labcoat" + desc = "A suit that protects against minor chemical spills. Has a purple stripe on the shoulder." + icon_state = "labcoat_tox_open" + item_state = "labcoat_tox_open" + +/obj/item/clothing/suit/storage/labcoat/mortician + name = "coroner labcoat" + desc = "A suit that protects against minor chemical spills. Has a black stripe on the shoulder." + icon_state = "labcoat_mort_open" + item_state = "labcoat_mort_open" + +/obj/item/clothing/suit/storage/labcoat/emt + name = "EMT labcoat" + desc = "A comfortable suit for paramedics. Has dark colours." + icon_state = "labcoat_emt_open" + item_state = "labcoat_emt_open" diff --git a/code/modules/clothing/suits/toggles.dm b/code/modules/clothing/suits/toggles.dm index 576b3e6c552..c329d8a0033 100644 --- a/code/modules/clothing/suits/toggles.dm +++ b/code/modules/clothing/suits/toggles.dm @@ -74,4 +74,4 @@ H.update_inv_wear_suit() playsound(src.loc, 'sound/mecha/mechmove03.ogg', 50, 1) else - RemoveHelmet() \ No newline at end of file + RemoveHelmet() diff --git a/code/modules/clothing/suits/utility.dm b/code/modules/clothing/suits/utility.dm index 87df2c38b33..308380e4c55 100644 --- a/code/modules/clothing/suits/utility.dm +++ b/code/modules/clothing/suits/utility.dm @@ -1,159 +1,159 @@ -/* - * Contains: - * Fire protection - * Bomb protection - * Radiation protection - */ - -/* - * Fire protection - */ - -/obj/item/clothing/suit/fire - name = "emergency firesuit" - desc = "A suit that protects against fire and heat." - icon_state = "fire" - item_state = "fire_suit" - w_class = WEIGHT_CLASS_BULKY - gas_transfer_coefficient = 0.90 - permeability_coefficient = 0.50 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - allowed = list(/obj/item/flashlight,/obj/item/tank/emergency_oxygen,/obj/item/extinguisher) - slowdown = 1 - armor = list("melee" = 15, "bullet" = 5, "laser" = 20, "energy" = 10, "bomb" = 20, "bio" = 10, "rad" = 20, "fire" = 100, "acid" = 50) - flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL - flags = STOPSPRESSUREDMAGE | THICKMATERIAL - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT - cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS - min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT - strip_delay = 60 - put_on_delay = 60 - resistance_flags = FIRE_PROOF - -/obj/item/clothing/suit/fire/firefighter - icon_state = "firesuit" - item_state = "firefighter" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/obj/item/clothing/suit/fire/heavy - name = "firesuit" - desc = "A suit that protects against extreme fire and heat." - //icon_state = "thermal" - item_state = "ro_suit" - w_class = WEIGHT_CLASS_BULKY - slowdown = 1.5 - -/obj/item/clothing/suit/fire/atmos - name = "firesuit" - desc = "An expensive firesuit that protects against even the most deadly of station fires. Designed to protect even if the wearer is set aflame." - icon_state = "atmos_firesuit" - item_state = "firesuit_atmos" - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/* - * Bomb protection - */ -/obj/item/clothing/head/bomb_hood - name = "bomb hood" - desc = "Use in case of bomb." - icon_state = "bombsuit" - flags = BLOCKHAIR | THICKMATERIAL - armor = list("melee" = 20, "bullet" = 0, "laser" = 20,"energy" = 10, "bomb" = 100, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 50) - flags_inv = HIDEMASK|HIDEEARS|HIDEEYES - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - cold_protection = HEAD - min_cold_protection_temperature = HELMET_MIN_TEMP_PROTECT - heat_protection = HEAD - max_heat_protection_temperature = HELMET_MAX_TEMP_PROTECT - strip_delay = 70 - put_on_delay = 70 - resistance_flags = NONE - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/helmet.dmi', - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - -/obj/item/clothing/suit/bomb_suit - name = "bomb suit" - desc = "A suit designed for safety when handling explosives." - icon_state = "bombsuit" - item_state = "bombsuit" - w_class = WEIGHT_CLASS_BULKY - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.01 - flags = THICKMATERIAL - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - slowdown = 2 - armor = list("melee" = 20, "bullet" = 0, "laser" = 20,"energy" = 10, "bomb" = 100, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 50) - flags_inv = HIDEJUMPSUIT|HIDETAIL - heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT - cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT - strip_delay = 70 - put_on_delay = 70 - resistance_flags = NONE - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) - -/obj/item/clothing/head/bomb_hood/security - icon_state = "bombsuitsec" - item_state = "bombsuitsec" - - -/obj/item/clothing/suit/bomb_suit/security - icon_state = "bombsuitsec" - item_state = "bombsuitsec" - allowed = list(/obj/item/gun/energy,/obj/item/melee/baton,/obj/item/restraints/handcuffs) - -/* - * Radiation protection - */ -/obj/item/clothing/head/radiation - name = "Radiation Hood" - icon_state = "rad" - desc = "A hood with radiation protective properties. Label: Made with lead, do not eat insulation" - flags = BLOCKHAIR|THICKMATERIAL - flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 60, "rad" = 100, "fire" = 30, "acid" = 30) - strip_delay = 60 - put_on_delay = 60 - resistance_flags = NONE - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/head.dmi', - "Grey" = 'icons/mob/species/grey/head.dmi' - ) - -/obj/item/clothing/suit/radiation - name = "Radiation suit" - desc = "A suit that protects against radiation. Label: Made with lead, do not eat insulation." - icon_state = "rad" - item_state = "rad_suit" - w_class = WEIGHT_CLASS_BULKY - gas_transfer_coefficient = 0.90 - permeability_coefficient = 0.50 - flags = THICKMATERIAL - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - allowed = list(/obj/item/flashlight,/obj/item/tank/emergency_oxygen) - slowdown = 1.5 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 60, "rad" = 100, "fire" = 30, "acid" = 30) - flags_inv = HIDEJUMPSUIT|HIDETAIL - strip_delay = 60 - put_on_delay = 60 - resistance_flags = NONE - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/suit.dmi' - ) +/* + * Contains: + * Fire protection + * Bomb protection + * Radiation protection + */ + +/* + * Fire protection + */ + +/obj/item/clothing/suit/fire + name = "emergency firesuit" + desc = "A suit that protects against fire and heat." + icon_state = "fire" + item_state = "fire_suit" + w_class = WEIGHT_CLASS_BULKY + gas_transfer_coefficient = 0.90 + permeability_coefficient = 0.50 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + allowed = list(/obj/item/flashlight,/obj/item/tank/emergency_oxygen,/obj/item/extinguisher) + slowdown = 1 + armor = list("melee" = 15, "bullet" = 5, "laser" = 20, "energy" = 10, "bomb" = 20, "bio" = 10, "rad" = 20, "fire" = 100, "acid" = 50) + flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL + flags = STOPSPRESSUREDMAGE | THICKMATERIAL + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT + cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS + min_cold_protection_temperature = FIRE_SUIT_MIN_TEMP_PROTECT + strip_delay = 60 + put_on_delay = 60 + resistance_flags = FIRE_PROOF + +/obj/item/clothing/suit/fire/firefighter + icon_state = "firesuit" + item_state = "firefighter" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/obj/item/clothing/suit/fire/heavy + name = "firesuit" + desc = "A suit that protects against extreme fire and heat." + //icon_state = "thermal" + item_state = "ro_suit" + w_class = WEIGHT_CLASS_BULKY + slowdown = 1.5 + +/obj/item/clothing/suit/fire/atmos + name = "firesuit" + desc = "An expensive firesuit that protects against even the most deadly of station fires. Designed to protect even if the wearer is set aflame." + icon_state = "atmos_firesuit" + item_state = "firesuit_atmos" + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/* + * Bomb protection + */ +/obj/item/clothing/head/bomb_hood + name = "bomb hood" + desc = "Use in case of bomb." + icon_state = "bombsuit" + flags = BLOCKHAIR | THICKMATERIAL + armor = list("melee" = 20, "bullet" = 0, "laser" = 20,"energy" = 10, "bomb" = 100, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 50) + flags_inv = HIDEMASK|HIDEEARS|HIDEEYES + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + cold_protection = HEAD + min_cold_protection_temperature = HELMET_MIN_TEMP_PROTECT + heat_protection = HEAD + max_heat_protection_temperature = HELMET_MAX_TEMP_PROTECT + strip_delay = 70 + put_on_delay = 70 + resistance_flags = NONE + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/helmet.dmi', + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + +/obj/item/clothing/suit/bomb_suit + name = "bomb suit" + desc = "A suit designed for safety when handling explosives." + icon_state = "bombsuit" + item_state = "bombsuit" + w_class = WEIGHT_CLASS_BULKY + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.01 + flags = THICKMATERIAL + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + slowdown = 2 + armor = list("melee" = 20, "bullet" = 0, "laser" = 20,"energy" = 10, "bomb" = 100, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 50) + flags_inv = HIDEJUMPSUIT|HIDETAIL + heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT + cold_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT + strip_delay = 70 + put_on_delay = 70 + resistance_flags = NONE + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) + +/obj/item/clothing/head/bomb_hood/security + icon_state = "bombsuitsec" + item_state = "bombsuitsec" + + +/obj/item/clothing/suit/bomb_suit/security + icon_state = "bombsuitsec" + item_state = "bombsuitsec" + allowed = list(/obj/item/gun/energy,/obj/item/melee/baton,/obj/item/restraints/handcuffs) + +/* + * Radiation protection + */ +/obj/item/clothing/head/radiation + name = "Radiation Hood" + icon_state = "rad" + desc = "A hood with radiation protective properties. Label: Made with lead, do not eat insulation" + flags = BLOCKHAIR|THICKMATERIAL + flags_cover = HEADCOVERSEYES | HEADCOVERSMOUTH + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 60, "rad" = 100, "fire" = 30, "acid" = 30) + strip_delay = 60 + put_on_delay = 60 + resistance_flags = NONE + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/head.dmi', + "Grey" = 'icons/mob/species/grey/head.dmi' + ) + +/obj/item/clothing/suit/radiation + name = "Radiation suit" + desc = "A suit that protects against radiation. Label: Made with lead, do not eat insulation." + icon_state = "rad" + item_state = "rad_suit" + w_class = WEIGHT_CLASS_BULKY + gas_transfer_coefficient = 0.90 + permeability_coefficient = 0.50 + flags = THICKMATERIAL + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + allowed = list(/obj/item/flashlight,/obj/item/tank/emergency_oxygen) + slowdown = 1.5 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 60, "rad" = 100, "fire" = 30, "acid" = 30) + flags_inv = HIDEJUMPSUIT|HIDETAIL + strip_delay = 60 + put_on_delay = 60 + resistance_flags = NONE + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/suit.dmi' + ) diff --git a/code/modules/clothing/suits/wiz_robe.dm b/code/modules/clothing/suits/wiz_robe.dm index 09fab3322fb..d02c9b40105 100644 --- a/code/modules/clothing/suits/wiz_robe.dm +++ b/code/modules/clothing/suits/wiz_robe.dm @@ -1,204 +1,204 @@ -/obj/item/clothing/head/wizard - name = "wizard hat" - desc = "Strange-looking hat-wear that most certainly belongs to a real magic user." - icon_state = "wizard" - gas_transfer_coefficient = 0.01 // IT'S MAGICAL OKAY JEEZ +1 TO NOT DIE - permeability_coefficient = 0.01 - armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 20, "bio" = 20, "rad" = 20, "fire" = 100, "acid" = 100) - resistance_flags = FIRE_PROOF | ACID_PROOF - //Not given any special protective value since the magic robes are full-body protection --NEO - strip_delay = 50 - put_on_delay = 50 - magical = TRUE - dog_fashion = /datum/dog_fashion/head/blue_wizard - -/obj/item/clothing/head/wizard/red - name = "red wizard hat" - desc = "Strange-looking, red, hat-wear that most certainly belongs to a real magic user." - icon_state = "redwizard" - dog_fashion = /datum/dog_fashion/head/red_wizard - -/obj/item/clothing/head/wizard/black - name = "black wizard hat" - desc = "Strange-looking black hat-wear that most certainly belongs to a real skeleton. Spooky." - icon_state = "blackwizard" - dog_fashion = null - - -/obj/item/clothing/head/wizard/clown - name = "purple wizard hat" - desc = "Strange-looking purple hat-wear that most certainly belongs to a real magic user." - icon_state = "wizhatclown" - item_state = "wizhatclown" // cheating - dog_fashion = null - -/obj/item/clothing/head/wizard/fake - name = "wizard hat" - desc = "It has WIZZARD written across it in sequins. Comes with a cool beard." - icon_state = "wizard-fake" - gas_transfer_coefficient = 1 - permeability_coefficient = 1 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - magical = FALSE - resistance_flags = FLAMMABLE - dog_fashion = /datum/dog_fashion/head/blue_wizard - -/obj/item/clothing/head/wizard/marisa - name = "Witch Hat" - desc = "Strange-looking hat-wear, makes you want to cast fireballs." - icon_state = "marisa" - dog_fashion = null - -/obj/item/clothing/head/wizard/magus - name = "Magus Helm" - desc = "A mysterious helmet that hums with an unearthly power" - icon_state = "magus" - item_state = "magus" - dog_fashion = null - -/obj/item/clothing/head/wizard/amp - name = "psychic amplifier" - desc = "A crown-of-thorns psychic amplifier. Kind of looks like a tiara having sex with an industrial robot." - icon_state = "amp" - dog_fashion = null - -/obj/item/clothing/suit/wizrobe - name = "wizard robe" - desc = "A magnificant, gem-lined robe that seems to radiate power." - icon_state = "wizard" - item_state = "wizrobe" - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.01 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS - armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 20, "bio" = 20, "rad" = 20, "fire" = 100, "acid" = 100) - allowed = list(/obj/item/teleportation_scroll) - flags_inv = HIDEJUMPSUIT - strip_delay = 50 - put_on_delay = 50 - resistance_flags = FIRE_PROOF | ACID_PROOF - magical = TRUE - -/obj/item/clothing/suit/wizrobe/red - name = "red wizard robe" - desc = "A magnificant, red, gem-lined robe that seems to radiate power." - icon_state = "redwizard" - item_state = "redwizrobe" - -/obj/item/clothing/suit/wizrobe/black - name = "black wizard robe" - desc = "An unnerving black gem-lined robe that reeks of death and decay." - icon_state = "blackwizard" - item_state = "blackwizrobe" - -/obj/item/clothing/suit/wizrobe/clown - name = "Clown Robe" - desc = "A set of armoured robes that seem to radiate a dark power. That, and bad fashion decisions." - icon_state = "wizzclown" - item_state = "wizzclown" - -/obj/item/clothing/suit/wizrobe/marisa - name = "Witch Robe" - desc = "Magic is all about the spell power, ZE!" - icon_state = "marisa" - item_state = "marisarobe" - -/obj/item/clothing/suit/wizrobe/magusblue - name = "Magus Robe" - desc = "A set of armoured robes that seem to radiate a dark power" - icon_state = "magusblue" - item_state = "magusblue" - -/obj/item/clothing/suit/wizrobe/magusred - name = "Magus Robe" - desc = "A set of armoured robes that seem to radiate a dark power" - icon_state = "magusred" - item_state = "magusred" - -/obj/item/clothing/suit/wizrobe/psypurple - name = "purple robes" - desc = "Heavy, royal purple robes threaded with psychic amplifiers and weird, bulbous lenses. Do not machine wash." - icon_state = "psyamp" - item_state = "psyamp" - -/obj/item/clothing/suit/wizrobe/fake - name = "wizard robe" - desc = "A rather dull, blue robe meant to mimick real wizard robes." - icon_state = "wizard-fake" - item_state = "wizrobe" - gas_transfer_coefficient = 1 - permeability_coefficient = 1 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - resistance_flags = FLAMMABLE - magical = FALSE - -/obj/item/clothing/head/wizard/marisa/fake - name = "Witch Hat" - desc = "Strange-looking hat-wear, makes you want to cast fireballs." - icon_state = "marisa" - gas_transfer_coefficient = 1 - permeability_coefficient = 1 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - resistance_flags = FLAMMABLE - magical = FALSE - -/obj/item/clothing/suit/wizrobe/marisa/fake - name = "Witch Robe" - desc = "Magic is all about the spell power, ZE!" - icon_state = "marisa" - item_state = "marisarobe" - gas_transfer_coefficient = 1 - permeability_coefficient = 1 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - resistance_flags = FLAMMABLE - magical = FALSE - -//Shielded Armour - -/obj/item/clothing/suit/space/hardsuit/shielded/wizard - name = "battlemage armour" - desc = "Not all wizards are afraid of getting up close and personal." - icon_state = "hardsuit-wiz" - item_state = "wiz_hardsuit" - recharge_rate = 0 - current_charges = 15 - recharge_cooldown = INFINITY - shield_state = "shield-red" - shield_on = "shield-red" - min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT - max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT - helmettype = /obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard - armor = list(melee = 30, bullet = 20, laser = 20, energy = 20, bomb = 20, bio = 20, rad = 20, fire = 100, acid = 100) - slowdown = 0 - resistance_flags = FIRE_PROOF | ACID_PROOF - magical = TRUE - -/obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard - name = "battlemage helmet" - desc = "A suitably impressive helmet." - icon_state = "hardsuit0-wiz" - item_state = "wiz_helm" - item_color = "wiz" - min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT - max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT - armor = list(melee = 30, bullet = 20, laser = 20, energy = 20, bomb = 20, bio = 20, rad = 20, fire = 100, acid = 100) - actions_types = list() //No inbuilt light - resistance_flags = FIRE_PROOF | ACID_PROOF - magical = TRUE - -/obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard/attack_self(mob/user) - return - -/obj/item/wizard_armour_charge - name = "battlemage shield charges" - desc = "A powerful rune that will increase the number of hits a suit of battlemage armour can take before failing.." - icon = 'icons/effects/effects.dmi' - icon_state = "electricity2" - -/obj/item/wizard_armour_charge/afterattack(obj/item/clothing/suit/space/hardsuit/shielded/wizard/W, mob/user) - . = ..() - if(!istype(W)) - to_chat(user, "The rune can only be used on battlemage armour!") - return - W.current_charges += 8 - to_chat(user, "You charge [W]. It can now absorb [W.current_charges] hits.") - qdel(src) \ No newline at end of file +/obj/item/clothing/head/wizard + name = "wizard hat" + desc = "Strange-looking hat-wear that most certainly belongs to a real magic user." + icon_state = "wizard" + gas_transfer_coefficient = 0.01 // IT'S MAGICAL OKAY JEEZ +1 TO NOT DIE + permeability_coefficient = 0.01 + armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 20, "bio" = 20, "rad" = 20, "fire" = 100, "acid" = 100) + resistance_flags = FIRE_PROOF | ACID_PROOF + //Not given any special protective value since the magic robes are full-body protection --NEO + strip_delay = 50 + put_on_delay = 50 + magical = TRUE + dog_fashion = /datum/dog_fashion/head/blue_wizard + +/obj/item/clothing/head/wizard/red + name = "red wizard hat" + desc = "Strange-looking, red, hat-wear that most certainly belongs to a real magic user." + icon_state = "redwizard" + dog_fashion = /datum/dog_fashion/head/red_wizard + +/obj/item/clothing/head/wizard/black + name = "black wizard hat" + desc = "Strange-looking black hat-wear that most certainly belongs to a real skeleton. Spooky." + icon_state = "blackwizard" + dog_fashion = null + + +/obj/item/clothing/head/wizard/clown + name = "purple wizard hat" + desc = "Strange-looking purple hat-wear that most certainly belongs to a real magic user." + icon_state = "wizhatclown" + item_state = "wizhatclown" // cheating + dog_fashion = null + +/obj/item/clothing/head/wizard/fake + name = "wizard hat" + desc = "It has WIZZARD written across it in sequins. Comes with a cool beard." + icon_state = "wizard-fake" + gas_transfer_coefficient = 1 + permeability_coefficient = 1 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + magical = FALSE + resistance_flags = FLAMMABLE + dog_fashion = /datum/dog_fashion/head/blue_wizard + +/obj/item/clothing/head/wizard/marisa + name = "Witch Hat" + desc = "Strange-looking hat-wear, makes you want to cast fireballs." + icon_state = "marisa" + dog_fashion = null + +/obj/item/clothing/head/wizard/magus + name = "Magus Helm" + desc = "A mysterious helmet that hums with an unearthly power" + icon_state = "magus" + item_state = "magus" + dog_fashion = null + +/obj/item/clothing/head/wizard/amp + name = "psychic amplifier" + desc = "A crown-of-thorns psychic amplifier. Kind of looks like a tiara having sex with an industrial robot." + icon_state = "amp" + dog_fashion = null + +/obj/item/clothing/suit/wizrobe + name = "wizard robe" + desc = "A magnificant, gem-lined robe that seems to radiate power." + icon_state = "wizard" + item_state = "wizrobe" + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.01 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS + armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 20, "bomb" = 20, "bio" = 20, "rad" = 20, "fire" = 100, "acid" = 100) + allowed = list(/obj/item/teleportation_scroll) + flags_inv = HIDEJUMPSUIT + strip_delay = 50 + put_on_delay = 50 + resistance_flags = FIRE_PROOF | ACID_PROOF + magical = TRUE + +/obj/item/clothing/suit/wizrobe/red + name = "red wizard robe" + desc = "A magnificant, red, gem-lined robe that seems to radiate power." + icon_state = "redwizard" + item_state = "redwizrobe" + +/obj/item/clothing/suit/wizrobe/black + name = "black wizard robe" + desc = "An unnerving black gem-lined robe that reeks of death and decay." + icon_state = "blackwizard" + item_state = "blackwizrobe" + +/obj/item/clothing/suit/wizrobe/clown + name = "Clown Robe" + desc = "A set of armoured robes that seem to radiate a dark power. That, and bad fashion decisions." + icon_state = "wizzclown" + item_state = "wizzclown" + +/obj/item/clothing/suit/wizrobe/marisa + name = "Witch Robe" + desc = "Magic is all about the spell power, ZE!" + icon_state = "marisa" + item_state = "marisarobe" + +/obj/item/clothing/suit/wizrobe/magusblue + name = "Magus Robe" + desc = "A set of armoured robes that seem to radiate a dark power" + icon_state = "magusblue" + item_state = "magusblue" + +/obj/item/clothing/suit/wizrobe/magusred + name = "Magus Robe" + desc = "A set of armoured robes that seem to radiate a dark power" + icon_state = "magusred" + item_state = "magusred" + +/obj/item/clothing/suit/wizrobe/psypurple + name = "purple robes" + desc = "Heavy, royal purple robes threaded with psychic amplifiers and weird, bulbous lenses. Do not machine wash." + icon_state = "psyamp" + item_state = "psyamp" + +/obj/item/clothing/suit/wizrobe/fake + name = "wizard robe" + desc = "A rather dull, blue robe meant to mimick real wizard robes." + icon_state = "wizard-fake" + item_state = "wizrobe" + gas_transfer_coefficient = 1 + permeability_coefficient = 1 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + resistance_flags = FLAMMABLE + magical = FALSE + +/obj/item/clothing/head/wizard/marisa/fake + name = "Witch Hat" + desc = "Strange-looking hat-wear, makes you want to cast fireballs." + icon_state = "marisa" + gas_transfer_coefficient = 1 + permeability_coefficient = 1 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + resistance_flags = FLAMMABLE + magical = FALSE + +/obj/item/clothing/suit/wizrobe/marisa/fake + name = "Witch Robe" + desc = "Magic is all about the spell power, ZE!" + icon_state = "marisa" + item_state = "marisarobe" + gas_transfer_coefficient = 1 + permeability_coefficient = 1 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + resistance_flags = FLAMMABLE + magical = FALSE + +//Shielded Armour + +/obj/item/clothing/suit/space/hardsuit/shielded/wizard + name = "battlemage armour" + desc = "Not all wizards are afraid of getting up close and personal." + icon_state = "hardsuit-wiz" + item_state = "wiz_hardsuit" + recharge_rate = 0 + current_charges = 15 + recharge_cooldown = INFINITY + shield_state = "shield-red" + shield_on = "shield-red" + min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT + max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT + helmettype = /obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard + armor = list(melee = 30, bullet = 20, laser = 20, energy = 20, bomb = 20, bio = 20, rad = 20, fire = 100, acid = 100) + slowdown = 0 + resistance_flags = FIRE_PROOF | ACID_PROOF + magical = TRUE + +/obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard + name = "battlemage helmet" + desc = "A suitably impressive helmet." + icon_state = "hardsuit0-wiz" + item_state = "wiz_helm" + item_color = "wiz" + min_cold_protection_temperature = ARMOR_MIN_TEMP_PROTECT + max_heat_protection_temperature = ARMOR_MAX_TEMP_PROTECT + armor = list(melee = 30, bullet = 20, laser = 20, energy = 20, bomb = 20, bio = 20, rad = 20, fire = 100, acid = 100) + actions_types = list() //No inbuilt light + resistance_flags = FIRE_PROOF | ACID_PROOF + magical = TRUE + +/obj/item/clothing/head/helmet/space/hardsuit/shielded/wizard/attack_self(mob/user) + return + +/obj/item/wizard_armour_charge + name = "battlemage shield charges" + desc = "A powerful rune that will increase the number of hits a suit of battlemage armour can take before failing.." + icon = 'icons/effects/effects.dmi' + icon_state = "electricity2" + +/obj/item/wizard_armour_charge/afterattack(obj/item/clothing/suit/space/hardsuit/shielded/wizard/W, mob/user) + . = ..() + if(!istype(W)) + to_chat(user, "The rune can only be used on battlemage armour!") + return + W.current_charges += 8 + to_chat(user, "You charge [W]. It can now absorb [W.current_charges] hits.") + qdel(src) diff --git a/code/modules/clothing/under/accessories/accessory.dm b/code/modules/clothing/under/accessories/accessory.dm index 11b7919f0ae..b417df89426 100644 --- a/code/modules/clothing/under/accessories/accessory.dm +++ b/code/modules/clothing/under/accessories/accessory.dm @@ -291,7 +291,7 @@ var/obj/item/pda/pda = O id_card = pda.id - if(access_security in id_card.access || emagged) + if(ACCESS_SECURITY in id_card.access || emagged) to_chat(user, "You imprint your ID details onto the badge.") stored_name = id_card.registered_name name = "holobadge ([stored_name])" diff --git a/code/modules/clothing/under/color.dm b/code/modules/clothing/under/color.dm index 3fe4138785a..6b39191eb13 100644 --- a/code/modules/clothing/under/color.dm +++ b/code/modules/clothing/under/color.dm @@ -1,207 +1,207 @@ -/obj/item/clothing/under/color - desc = "A standard issue colored jumpsuit. Variety is the spice of life!" - - -/obj/item/clothing/under/color/random/New() - ..() - var/list/excluded = list(/obj/item/clothing/under/color/random, /obj/item/clothing/under/color/blackf, /obj/item/clothing/under/color/blue/dodgeball, /obj/item/clothing/under/color/orange/prison, /obj/item/clothing/under/color/red/dodgeball, /obj/item/clothing/under/color/red/jersey, /obj/item/clothing/under/color/blue/jersey) - var/obj/item/clothing/under/color/C = pick(subtypesof(/obj/item/clothing/under/color) - excluded) - name = initial(C.name) - icon_state = initial(C.icon_state) - item_state = initial(C.item_state) - item_color = initial(C.item_color) - -/obj/item/clothing/under/color/black - name = "black jumpsuit" - icon_state = "black" - item_state = "bl_suit" - item_color = "black" - flags_size = ONESIZEFITSALL - resistance_flags = NONE - -/obj/item/clothing/under/color/blackf - name = "feminine black jumpsuit" - desc = "It's very smart and in a ladies-size!" - icon_state = "black" - item_state = "bl_suit" - item_color = "blackf" - -/obj/item/clothing/under/color/blue - name = "blue jumpsuit" - icon_state = "blue" - item_state = "b_suit" - item_color = "blue" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/blue/dodgeball - flags = NODROP - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/green - name = "green jumpsuit" - icon_state = "green" - item_state = "g_suit" - item_color = "green" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/grey - name = "grey jumpsuit" - desc = "A tasteful grey jumpsuit that reminds you of the good old days." - icon_state = "grey" - item_state = "gy_suit" - item_color = "grey" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/grey/greytide - flags = NODROP - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/grey/glorf - name = "ancient jumpsuit" - desc = "A terribly ragged and frayed grey jumpsuit. It looks like it hasn't been washed in over a decade." - -/obj/item/clothing/under/color/grey/glorf/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - owner.forcesay(GLOB.hit_appends) - return 0 - -/obj/item/clothing/under/color/orange - name = "orange jumpsuit" - desc = "Don't wear this near paranoid security officers" - icon_state = "orange" - item_state = "o_suit" - item_color = "orange" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/orange/prison - name = "orange jumpsuit" - desc = "It's standardised Nanotrasen prisoner-wear. Its suit sensors are stuck in the \"Fully On\" position." - icon_state = "orange" - item_state = "o_suit" - item_color = "orange" - has_sensor = 2 - sensor_mode = 3 - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/pink - name = "pink jumpsuit" - desc = "Just looking at this makes you feel fabulous." - icon_state = "pink" - item_state = "p_suit" - item_color = "pink" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/red - name = "red jumpsuit" - icon_state = "red" - item_state = "r_suit" - item_color = "red" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/red/dodgeball - flags = NODROP - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/white - name = "white jumpsuit" - icon_state = "white" - item_state = "w_suit" - item_color = "white" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/yellow - name = "yellow jumpsuit" - icon_state = "yellow" - item_state = "y_suit" - item_color = "yellow" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/psyche - name = "psychedelic jumpsuit" - desc = "Groovy!" - icon_state = "psyche" - item_color = "psyche" - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/uniform.dmi' - ) - -/obj/item/clothing/under/color/lightblue - name = "light blue jumpsuit" - icon_state = "lightblue" - item_color = "lightblue" - -/obj/item/clothing/under/color/aqua - name = "aqua jumpsuit" - icon_state = "aqua" - item_color = "aqua" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/purple - name = "purple jumpsuit" - icon_state = "purple" - item_state = "p_suit" - item_color = "purple" - -/obj/item/clothing/under/color/lightpurple - name = "light purple jumpsuit" - icon_state = "lightpurple" - item_color = "lightpurple" - -/obj/item/clothing/under/color/lightgreen - name = "light green jumpsuit" - icon_state = "lightgreen" - item_color = "lightgreen" - -/obj/item/clothing/under/color/lightblue - name = "light blue jumpsuit" - icon_state = "lightblue" - item_color = "lightblue" - -/obj/item/clothing/under/color/lightbrown - name = "light brown jumpsuit" - icon_state = "lightbrown" - item_color = "lightbrown" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/brown - name = "brown jumpsuit" - icon_state = "brown" - item_color = "brown" - -/obj/item/clothing/under/color/yellowgreen - name = "yellow green jumpsuit" - icon_state = "yellowgreen" - item_color = "yellowgreen" - -/obj/item/clothing/under/color/darkblue - name = "dark blue jumpsuit" - icon_state = "darkblue" - item_color = "darkblue" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/lightred - name = "light red jumpsuit" - icon_state = "lightred" - item_color = "lightred" - -/obj/item/clothing/under/color/darkred - name = "dark red jumpsuit" - icon_state = "darkred" - item_color = "darkred" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/red/jersey - name = "red team jersey" - desc = "The jersey of the Nanotrasen Phi-ghters!" - icon_state = "redjersey" - item_state = "r_suit" - item_color = "redjersey" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/color/blue/jersey - name = "blue team jersey" - desc = "The jersey of the Nanotrasen Pi-rates!" - icon_state = "bluejersey" - item_state = "b_suit" - item_color = "bluejersey" - flags_size = ONESIZEFITSALL +/obj/item/clothing/under/color + desc = "A standard issue colored jumpsuit. Variety is the spice of life!" + + +/obj/item/clothing/under/color/random/New() + ..() + var/list/excluded = list(/obj/item/clothing/under/color/random, /obj/item/clothing/under/color/blackf, /obj/item/clothing/under/color/blue/dodgeball, /obj/item/clothing/under/color/orange/prison, /obj/item/clothing/under/color/red/dodgeball, /obj/item/clothing/under/color/red/jersey, /obj/item/clothing/under/color/blue/jersey) + var/obj/item/clothing/under/color/C = pick(subtypesof(/obj/item/clothing/under/color) - excluded) + name = initial(C.name) + icon_state = initial(C.icon_state) + item_state = initial(C.item_state) + item_color = initial(C.item_color) + +/obj/item/clothing/under/color/black + name = "black jumpsuit" + icon_state = "black" + item_state = "bl_suit" + item_color = "black" + flags_size = ONESIZEFITSALL + resistance_flags = NONE + +/obj/item/clothing/under/color/blackf + name = "feminine black jumpsuit" + desc = "It's very smart and in a ladies-size!" + icon_state = "black" + item_state = "bl_suit" + item_color = "blackf" + +/obj/item/clothing/under/color/blue + name = "blue jumpsuit" + icon_state = "blue" + item_state = "b_suit" + item_color = "blue" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/blue/dodgeball + flags = NODROP + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/green + name = "green jumpsuit" + icon_state = "green" + item_state = "g_suit" + item_color = "green" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/grey + name = "grey jumpsuit" + desc = "A tasteful grey jumpsuit that reminds you of the good old days." + icon_state = "grey" + item_state = "gy_suit" + item_color = "grey" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/grey/greytide + flags = NODROP + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/grey/glorf + name = "ancient jumpsuit" + desc = "A terribly ragged and frayed grey jumpsuit. It looks like it hasn't been washed in over a decade." + +/obj/item/clothing/under/color/grey/glorf/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + owner.forcesay(GLOB.hit_appends) + return 0 + +/obj/item/clothing/under/color/orange + name = "orange jumpsuit" + desc = "Don't wear this near paranoid security officers" + icon_state = "orange" + item_state = "o_suit" + item_color = "orange" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/orange/prison + name = "orange jumpsuit" + desc = "It's standardised Nanotrasen prisoner-wear. Its suit sensors are stuck in the \"Fully On\" position." + icon_state = "orange" + item_state = "o_suit" + item_color = "orange" + has_sensor = 2 + sensor_mode = 3 + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/pink + name = "pink jumpsuit" + desc = "Just looking at this makes you feel fabulous." + icon_state = "pink" + item_state = "p_suit" + item_color = "pink" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/red + name = "red jumpsuit" + icon_state = "red" + item_state = "r_suit" + item_color = "red" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/red/dodgeball + flags = NODROP + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/white + name = "white jumpsuit" + icon_state = "white" + item_state = "w_suit" + item_color = "white" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/yellow + name = "yellow jumpsuit" + icon_state = "yellow" + item_state = "y_suit" + item_color = "yellow" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/psyche + name = "psychedelic jumpsuit" + desc = "Groovy!" + icon_state = "psyche" + item_color = "psyche" + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/uniform.dmi' + ) + +/obj/item/clothing/under/color/lightblue + name = "light blue jumpsuit" + icon_state = "lightblue" + item_color = "lightblue" + +/obj/item/clothing/under/color/aqua + name = "aqua jumpsuit" + icon_state = "aqua" + item_color = "aqua" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/purple + name = "purple jumpsuit" + icon_state = "purple" + item_state = "p_suit" + item_color = "purple" + +/obj/item/clothing/under/color/lightpurple + name = "light purple jumpsuit" + icon_state = "lightpurple" + item_color = "lightpurple" + +/obj/item/clothing/under/color/lightgreen + name = "light green jumpsuit" + icon_state = "lightgreen" + item_color = "lightgreen" + +/obj/item/clothing/under/color/lightblue + name = "light blue jumpsuit" + icon_state = "lightblue" + item_color = "lightblue" + +/obj/item/clothing/under/color/lightbrown + name = "light brown jumpsuit" + icon_state = "lightbrown" + item_color = "lightbrown" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/brown + name = "brown jumpsuit" + icon_state = "brown" + item_color = "brown" + +/obj/item/clothing/under/color/yellowgreen + name = "yellow green jumpsuit" + icon_state = "yellowgreen" + item_color = "yellowgreen" + +/obj/item/clothing/under/color/darkblue + name = "dark blue jumpsuit" + icon_state = "darkblue" + item_color = "darkblue" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/lightred + name = "light red jumpsuit" + icon_state = "lightred" + item_color = "lightred" + +/obj/item/clothing/under/color/darkred + name = "dark red jumpsuit" + icon_state = "darkred" + item_color = "darkred" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/red/jersey + name = "red team jersey" + desc = "The jersey of the Nanotrasen Phi-ghters!" + icon_state = "redjersey" + item_state = "r_suit" + item_color = "redjersey" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/color/blue/jersey + name = "blue team jersey" + desc = "The jersey of the Nanotrasen Pi-rates!" + icon_state = "bluejersey" + item_state = "b_suit" + item_color = "bluejersey" + flags_size = ONESIZEFITSALL diff --git a/code/modules/clothing/under/jobs/civilian.dm b/code/modules/clothing/under/jobs/civilian.dm index 879e0fd22c7..3850ce559df 100644 --- a/code/modules/clothing/under/jobs/civilian.dm +++ b/code/modules/clothing/under/jobs/civilian.dm @@ -1,239 +1,239 @@ -//Alphabetical order of support jobs. - -/obj/item/clothing/under/rank/bartender - desc = "It looks like it could use some more flair." - name = "bartender's uniform" - icon_state = "ba_suit" - item_state = "ba_suit" - item_color = "ba_suit" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/captain //Alright, technically not a 'civilian' but its better then giving a .dm file for a single define. - desc = "It's a blue jumpsuit with some gold markings denoting the rank of \"Captain\"." - name = "captain's jumpsuit" - icon_state = "captain" - item_state = "caparmor" - item_color = "captain" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/cargo - name = "quartermaster's jumpsuit" - desc = "It's a jumpsuit worn by the quartermaster. It's specially designed to prevent back injuries caused by pushing paper." - icon_state = "qm" - item_state = "lb_suit" - item_color = "qm" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/cargo/skirt - name = "quartermaster's jumpskirt" - desc = "It's a jumpskirt worn by the quartermaster. It's specially designed to prevent back injuries caused by pushing paper." - icon_state = "qmf" - item_color = "qmf" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_size = null - -/obj/item/clothing/under/rank/cargotech - name = "cargo technician's jumpsuit" - desc = "Shooooorts! They're comfy and easy to wear!" - icon_state = "cargotech" - item_state = "lb_suit" - item_color = "cargo" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/cargotech/skirt - name = "cargo technician's jumpskirt" - desc = "Skirrrrrts! They're comfy and easy to wear!" - icon_state = "cargof" - item_color = "cargof" - flags_size = null - -/obj/item/clothing/under/rank/chaplain - desc = "It's a black jumpsuit, often worn by religious folk." - name = "chaplain's jumpsuit" - icon_state = "chaplain" - item_state = "bl_suit" - item_color = "chapblack" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/chef - desc = "It's an apron which is given only to the most hardcore chefs in space." - name = "chef's uniform" - icon_state = "chef" - item_color = "chef" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/clown - name = "clown suit" - desc = "'HONK!'" - icon_state = "clown" - item_state = "clown" - item_color = "clown" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/clown/Initialize() - . = ..() - AddComponent(/datum/component/squeak, list('sound/items/bikehorn.ogg' = 1), 50) - -/obj/item/clothing/under/rank/clown/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) - if(ishuman(loc)) - var/mob/living/carbon/human/H = loc - if(H.mind && H.mind.assigned_role == "Clown") - score_clownabuse++ - return ..() - -/obj/item/clothing/under/rank/clown/sexy - name = "sexy-clown suit" - desc = "It makes you look HONKable!" - icon_state = "sexyclown" - item_state = "sexyclown" - item_color = "sexyclown" - -/obj/item/clothing/under/rank/clown/nodrop - flags = NODROP - -/obj/item/clothing/under/rank/head_of_personnel - desc = "It's a jumpsuit worn by someone who works in the position of \"Head of Personnel\"." - name = "head of personnel's jumpsuit" - icon_state = "hop" - item_state = "b_suit" - item_color = "hop" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/head_of_personnel_whimsy - desc = "A blue jacket and red tie, with matching red cuffs! Snazzy. Wearing this makes you feel more important than your job title does." - name = "head of personnel's suit" - icon_state = "hopwhimsy" - item_state = "hopwhimsy" - item_color = "hopwhimsy" - - -/obj/item/clothing/under/rank/hydroponics - desc = "It's a jumpsuit designed to protect against minor plant-related hazards." - name = "botanist's jumpsuit" - icon_state = "hydroponics" - item_state = "g_suit" - item_color = "hydroponics" - permeability_coefficient = 0.50 - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/internalaffairs - desc = "The plain, professional attire of an Internal Affairs Agent. The collar is immaculately starched." - name = "Internal Affairs uniform" - icon_state = "internalaffairs" - item_state = "internalaffairs" - item_color = "internalaffairs" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/ntrep - desc = "A well-ironed dress shirt and matching set of black pants." - name = "dress shirt" - icon_state = "internalaffairs" - item_state = "internalaffairs" - item_color = "internalaffairs" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/ntrep/skirt - desc = "A silky smooth black and gold representative uniform with blue markings." - name = "representative skirt" - icon_state = "ntrepf" - item_state = "ntrepf" - item_color = "ntrepf" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/janitor - desc = "It's the official uniform of the station's janitor. It has minor protection from biohazards." - name = "janitor's jumpsuit" - icon_state = "janitor" - item_color = "janitor" - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - - -/obj/item/clothing/under/lawyer - desc = "Slick threads." - name = "Lawyer suit" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/lawyer/black - icon_state = "lawyer_black" - item_state = "lawyer_black" - item_color = "lawyer_black" - -/obj/item/clothing/under/lawyer/female - icon_state = "black_suit_fem" - item_state = "black_suit_fem" - item_color = "black_suit_fem" - -/obj/item/clothing/under/lawyer/red - icon_state = "lawyer_red" - item_state = "lawyer_red" - item_color = "lawyer_red" - -/obj/item/clothing/under/lawyer/blue - icon_state = "lawyer_blue" - item_state = "lawyer_blue" - item_color = "lawyer_blue" - -/obj/item/clothing/under/lawyer/bluesuit - name = "Blue Suit" - desc = "A classy suit and tie" - icon_state = "bluesuit" - item_state = "bluesuit" - item_color = "bluesuit" - -/obj/item/clothing/under/lawyer/purpsuit - name = "Purple Suit" - icon_state = "lawyer_purp" - item_state = "lawyer_purp" - item_color = "lawyer_purp" - -/obj/item/clothing/under/lawyer/oldman - name = "Old Man's Suit" - desc = "A classic suit for the older gentleman with built in back support." - icon_state = "oldman" - item_state = "oldman" - item_color = "oldman" - - -/obj/item/clothing/under/librarian - name = "sensible suit" - desc = "It's very... sensible." - icon_state = "red_suit" - item_state = "red_suit" - item_color = "red_suit" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/mime - name = "mime's outfit" - desc = "It's not very colourful." - icon_state = "mime" - item_state = "mime" - item_color = "mime" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/mime/nodrop - flags = NODROP - -/obj/item/clothing/under/rank/miner - desc = "It's a snappy jumpsuit with a sturdy set of overalls. It is very dirty." - name = "shaft miner's jumpsuit" - icon_state = "miner" - item_state = "miner" - item_color = "miner" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/miner/lavaland - desc = "A green uniform for operating in hazardous environments." - name = "shaft miner's jumpsuit" - icon_state = "explorer" - item_state = "explorer" - item_color = "explorer" - - sprite_sheets = list() - -/obj/item/clothing/under/barber - desc = "It's a barber's uniform." - name = "barber's uniform" - icon_state = "barber" - item_state = "barber" - item_color = "barber" +//Alphabetical order of support jobs. + +/obj/item/clothing/under/rank/bartender + desc = "It looks like it could use some more flair." + name = "bartender's uniform" + icon_state = "ba_suit" + item_state = "ba_suit" + item_color = "ba_suit" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/captain //Alright, technically not a 'civilian' but its better then giving a .dm file for a single define. + desc = "It's a blue jumpsuit with some gold markings denoting the rank of \"Captain\"." + name = "captain's jumpsuit" + icon_state = "captain" + item_state = "caparmor" + item_color = "captain" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/cargo + name = "quartermaster's jumpsuit" + desc = "It's a jumpsuit worn by the quartermaster. It's specially designed to prevent back injuries caused by pushing paper." + icon_state = "qm" + item_state = "lb_suit" + item_color = "qm" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/cargo/skirt + name = "quartermaster's jumpskirt" + desc = "It's a jumpskirt worn by the quartermaster. It's specially designed to prevent back injuries caused by pushing paper." + icon_state = "qmf" + item_color = "qmf" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + flags_size = null + +/obj/item/clothing/under/rank/cargotech + name = "cargo technician's jumpsuit" + desc = "Shooooorts! They're comfy and easy to wear!" + icon_state = "cargotech" + item_state = "lb_suit" + item_color = "cargo" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/cargotech/skirt + name = "cargo technician's jumpskirt" + desc = "Skirrrrrts! They're comfy and easy to wear!" + icon_state = "cargof" + item_color = "cargof" + flags_size = null + +/obj/item/clothing/under/rank/chaplain + desc = "It's a black jumpsuit, often worn by religious folk." + name = "chaplain's jumpsuit" + icon_state = "chaplain" + item_state = "bl_suit" + item_color = "chapblack" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/chef + desc = "It's an apron which is given only to the most hardcore chefs in space." + name = "chef's uniform" + icon_state = "chef" + item_color = "chef" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/clown + name = "clown suit" + desc = "'HONK!'" + icon_state = "clown" + item_state = "clown" + item_color = "clown" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/clown/Initialize() + . = ..() + AddComponent(/datum/component/squeak, list('sound/items/bikehorn.ogg' = 1), 50) + +/obj/item/clothing/under/rank/clown/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK) + if(ishuman(loc)) + var/mob/living/carbon/human/H = loc + if(H.mind && H.mind.assigned_role == "Clown") + score_clownabuse++ + return ..() + +/obj/item/clothing/under/rank/clown/sexy + name = "sexy-clown suit" + desc = "It makes you look HONKable!" + icon_state = "sexyclown" + item_state = "sexyclown" + item_color = "sexyclown" + +/obj/item/clothing/under/rank/clown/nodrop + flags = NODROP + +/obj/item/clothing/under/rank/head_of_personnel + desc = "It's a jumpsuit worn by someone who works in the position of \"Head of Personnel\"." + name = "head of personnel's jumpsuit" + icon_state = "hop" + item_state = "b_suit" + item_color = "hop" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/head_of_personnel_whimsy + desc = "A blue jacket and red tie, with matching red cuffs! Snazzy. Wearing this makes you feel more important than your job title does." + name = "head of personnel's suit" + icon_state = "hopwhimsy" + item_state = "hopwhimsy" + item_color = "hopwhimsy" + + +/obj/item/clothing/under/rank/hydroponics + desc = "It's a jumpsuit designed to protect against minor plant-related hazards." + name = "botanist's jumpsuit" + icon_state = "hydroponics" + item_state = "g_suit" + item_color = "hydroponics" + permeability_coefficient = 0.50 + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/internalaffairs + desc = "The plain, professional attire of an Internal Affairs Agent. The collar is immaculately starched." + name = "Internal Affairs uniform" + icon_state = "internalaffairs" + item_state = "internalaffairs" + item_color = "internalaffairs" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/ntrep + desc = "A well-ironed dress shirt and matching set of black pants." + name = "dress shirt" + icon_state = "internalaffairs" + item_state = "internalaffairs" + item_color = "internalaffairs" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/ntrep/skirt + desc = "A silky smooth black and gold representative uniform with blue markings." + name = "representative skirt" + icon_state = "ntrepf" + item_state = "ntrepf" + item_color = "ntrepf" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/janitor + desc = "It's the official uniform of the station's janitor. It has minor protection from biohazards." + name = "janitor's jumpsuit" + icon_state = "janitor" + item_color = "janitor" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + flags_size = ONESIZEFITSALL + + +/obj/item/clothing/under/lawyer + desc = "Slick threads." + name = "Lawyer suit" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/lawyer/black + icon_state = "lawyer_black" + item_state = "lawyer_black" + item_color = "lawyer_black" + +/obj/item/clothing/under/lawyer/female + icon_state = "black_suit_fem" + item_state = "black_suit_fem" + item_color = "black_suit_fem" + +/obj/item/clothing/under/lawyer/red + icon_state = "lawyer_red" + item_state = "lawyer_red" + item_color = "lawyer_red" + +/obj/item/clothing/under/lawyer/blue + icon_state = "lawyer_blue" + item_state = "lawyer_blue" + item_color = "lawyer_blue" + +/obj/item/clothing/under/lawyer/bluesuit + name = "Blue Suit" + desc = "A classy suit and tie" + icon_state = "bluesuit" + item_state = "bluesuit" + item_color = "bluesuit" + +/obj/item/clothing/under/lawyer/purpsuit + name = "Purple Suit" + icon_state = "lawyer_purp" + item_state = "lawyer_purp" + item_color = "lawyer_purp" + +/obj/item/clothing/under/lawyer/oldman + name = "Old Man's Suit" + desc = "A classic suit for the older gentleman with built in back support." + icon_state = "oldman" + item_state = "oldman" + item_color = "oldman" + + +/obj/item/clothing/under/librarian + name = "sensible suit" + desc = "It's very... sensible." + icon_state = "red_suit" + item_state = "red_suit" + item_color = "red_suit" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/mime + name = "mime's outfit" + desc = "It's not very colourful." + icon_state = "mime" + item_state = "mime" + item_color = "mime" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/mime/nodrop + flags = NODROP + +/obj/item/clothing/under/rank/miner + desc = "It's a snappy jumpsuit with a sturdy set of overalls. It is very dirty." + name = "shaft miner's jumpsuit" + icon_state = "miner" + item_state = "miner" + item_color = "miner" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/miner/lavaland + desc = "A green uniform for operating in hazardous environments." + name = "shaft miner's jumpsuit" + icon_state = "explorer" + item_state = "explorer" + item_color = "explorer" + + sprite_sheets = list() + +/obj/item/clothing/under/barber + desc = "It's a barber's uniform." + name = "barber's uniform" + icon_state = "barber" + item_state = "barber" + item_color = "barber" diff --git a/code/modules/clothing/under/jobs/engineering.dm b/code/modules/clothing/under/jobs/engineering.dm index 14878e402a1..bdda81ad03d 100644 --- a/code/modules/clothing/under/jobs/engineering.dm +++ b/code/modules/clothing/under/jobs/engineering.dm @@ -1,77 +1,77 @@ -//Contains: Engineering department jumpsuits -/obj/item/clothing/under/rank/chief_engineer - desc = "It's a high visibility jumpsuit given to those engineers insane enough to achieve the rank of \"Chief engineer\". It has minor radiation shielding." - name = "chief engineer's jumpsuit" - icon_state = "chiefengineer" - item_state = "chief" - item_color = "chief" - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 10, "fire" = 80, "acid" = 40) - flags_size = ONESIZEFITSALL - resistance_flags = NONE - -/obj/item/clothing/under/rank/chief_engineer/skirt - desc = "It's a high visibility jumpskirt given to those engineers insane enough to achieve the rank of \"Chief engineer\". It has minor radiation shielding." - name = "chief engineer's jumpskirt" - icon_state = "chieff" - item_color = "chieff" - flags_size = null - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/under/rank/atmospheric_technician - desc = "It's a jumpsuit worn by atmospheric technicians." - name = "atmospheric technician's jumpsuit" - icon_state = "atmos" - item_state = "atmos_suit" - item_color = "atmos" - flags_size = ONESIZEFITSALL - resistance_flags = NONE - -/obj/item/clothing/under/rank/atmospheric_technician/skirt - desc = "It's a jumpskirt worn by atmospheric technicians." - name = "atmospheric technician's jumpskirt" - icon_state = "atmosf" - item_color = "atmosf" - flags_size = null - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/under/rank/engineer - desc = "It's an orange high visibility jumpsuit worn by engineers. It has minor radiation shielding." - name = "engineer's jumpsuit" - icon_state = "engine" - item_state = "engi_suit" - item_color = "engine" - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 10, "fire" = 60, "acid" = 20) - flags_size = ONESIZEFITSALL - resistance_flags = NONE - - -/obj/item/clothing/under/rank/engineer/skirt - desc = "It's an orange high visibility jumpskirt worn by engineers. It has minor radiation shielding." - name = "engineer's jumpskirt" - icon_state = "enginef" - item_color = "enginef" - flags_size = null - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/under/rank/roboticist - desc = "It's a slimming black with reinforced seams; great for industrial work." - name = "roboticist's jumpsuit" - icon_state = "robotics" - item_state = "robotics" - item_color = "robotics" - flags_size = ONESIZEFITSALL - resistance_flags = NONE - -/obj/item/clothing/under/rank/roboticist/skirt - desc = "It's a slimming black jumpskirt with reinforced seams; great for industrial work." - name = "roboticist's jumpskirt" - icon_state = "roboticsf" - item_color = "roboticsf" - flags_size = null - -/obj/item/clothing/under/rank/mechanic - desc = "It's a pair of overalls worn by mechanics." - name = "mechanic's overalls" - icon_state = "mechanic" - item_state = "mechanic" - item_color = "mechanic" +//Contains: Engineering department jumpsuits +/obj/item/clothing/under/rank/chief_engineer + desc = "It's a high visibility jumpsuit given to those engineers insane enough to achieve the rank of \"Chief engineer\". It has minor radiation shielding." + name = "chief engineer's jumpsuit" + icon_state = "chiefengineer" + item_state = "chief" + item_color = "chief" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 10, "fire" = 80, "acid" = 40) + flags_size = ONESIZEFITSALL + resistance_flags = NONE + +/obj/item/clothing/under/rank/chief_engineer/skirt + desc = "It's a high visibility jumpskirt given to those engineers insane enough to achieve the rank of \"Chief engineer\". It has minor radiation shielding." + name = "chief engineer's jumpskirt" + icon_state = "chieff" + item_color = "chieff" + flags_size = null + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/under/rank/atmospheric_technician + desc = "It's a jumpsuit worn by atmospheric technicians." + name = "atmospheric technician's jumpsuit" + icon_state = "atmos" + item_state = "atmos_suit" + item_color = "atmos" + flags_size = ONESIZEFITSALL + resistance_flags = NONE + +/obj/item/clothing/under/rank/atmospheric_technician/skirt + desc = "It's a jumpskirt worn by atmospheric technicians." + name = "atmospheric technician's jumpskirt" + icon_state = "atmosf" + item_color = "atmosf" + flags_size = null + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/under/rank/engineer + desc = "It's an orange high visibility jumpsuit worn by engineers. It has minor radiation shielding." + name = "engineer's jumpsuit" + icon_state = "engine" + item_state = "engi_suit" + item_color = "engine" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 10, "fire" = 60, "acid" = 20) + flags_size = ONESIZEFITSALL + resistance_flags = NONE + + +/obj/item/clothing/under/rank/engineer/skirt + desc = "It's an orange high visibility jumpskirt worn by engineers. It has minor radiation shielding." + name = "engineer's jumpskirt" + icon_state = "enginef" + item_color = "enginef" + flags_size = null + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/under/rank/roboticist + desc = "It's a slimming black with reinforced seams; great for industrial work." + name = "roboticist's jumpsuit" + icon_state = "robotics" + item_state = "robotics" + item_color = "robotics" + flags_size = ONESIZEFITSALL + resistance_flags = NONE + +/obj/item/clothing/under/rank/roboticist/skirt + desc = "It's a slimming black jumpskirt with reinforced seams; great for industrial work." + name = "roboticist's jumpskirt" + icon_state = "roboticsf" + item_color = "roboticsf" + flags_size = null + +/obj/item/clothing/under/rank/mechanic + desc = "It's a pair of overalls worn by mechanics." + name = "mechanic's overalls" + icon_state = "mechanic" + item_state = "mechanic" + item_color = "mechanic" diff --git a/code/modules/clothing/under/jobs/medsci.dm b/code/modules/clothing/under/jobs/medsci.dm index f5d65b964fc..d3fcad16680 100644 --- a/code/modules/clothing/under/jobs/medsci.dm +++ b/code/modules/clothing/under/jobs/medsci.dm @@ -1,246 +1,246 @@ -/* - * Science - */ -/obj/item/clothing/under/rank/research_director - desc = "It's a jumpsuit worn by those with the know-how to achieve the position of \"Research Director\". Its fabric provides minor protection from biological contaminants." - name = "research director's jumpsuit" - icon_state = "director" - item_state = "g_suit" - item_color = "director" - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 10, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 35) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/scientist - desc = "It's made of a special fiber that provides minor protection against biohazards. It has markings that denote the wearer as a scientist." - name = "scientist's jumpsuit" - icon_state = "toxins" - item_state = "w_suit" - item_color = "toxinswhite" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/scientist/skirt - name = "scientist's jumpskirt" - icon_state = "sciencewhitef" - item_color = "sciencewhitef" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_size = null - -/obj/item/clothing/under/rank/chemist - desc = "It's made of a special fiber that gives special protection against biohazards. It has a chemist rank stripe on it." - name = "chemist's jumpsuit" - icon_state = "chemistry" - item_state = "w_suit" - item_color = "chemistrywhite" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 50, "acid" = 65) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/chemist/skirt - name = "chemist's jumpskirt" - icon_state = "chemistrywhitef" - item_color = "chemistrywhitef" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_size = null - -/* - * Medical - */ -/obj/item/clothing/under/rank/chief_medical_officer - desc = "It's a jumpsuit worn by those with the experience to be \"Chief Medical Officer\". It provides minor biological protection." - name = "chief medical officer's jumpsuit" - icon_state = "cmo" - item_state = "w_suit" - item_color = "cmo" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/chief_medical_officer/skirt - desc = "It's a jumpskirt worn by those with the experience to be \"Chief Medical Officer\". It provides minor biological protection." - name = "chief medical officer's jumpskirt" - icon_state = "cmof" - item_color = "cmof" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_size = null - -/obj/item/clothing/under/rank/geneticist - desc = "It's made of a special fiber that gives special protection against biohazards. It has a genetics rank stripe on it." - name = "geneticist's jumpsuit" - icon_state = "genetics" - item_state = "w_suit" - item_color = "geneticswhite" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/geneticist/skirt - name = "geneticist's jumpskirt" - icon_state = "geneticswhitef" - item_color = "geneticswhitef" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_size = null - -/obj/item/clothing/under/rank/virologist - desc = "It's made of a special fiber that gives special protection against biohazards. It has a virologist rank stripe on it." - name = "virologist's jumpsuit" - icon_state = "virology" - item_state = "w_suit" - item_color = "virologywhite" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/virologist/skirt - name = "virologist's jumpskirt" - icon_state = "virologywhitef" - item_color = "virologywhitef" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_size = null - -/obj/item/clothing/under/rank/nursesuit - desc = "It's a jumpsuit commonly worn by nursing staff in the medical department." - name = "nurse's suit" - icon_state = "nursesuit" - item_state = "nursesuit" - item_color = "nursesuit" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/nurse - desc = "A dress commonly worn by the nursing staff in the medical department." - name = "nurse's dress" - icon_state = "nurse" - item_state = "nurse" - item_color = "nurse" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/orderly - desc = "A white suit to be worn by orderly people who love orderly things." - name = "orderly's uniform" - icon_state = "orderly" - item_state = "orderly" - item_color = "orderly" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/medical - desc = "It's made of a special fiber that provides minor protection against biohazards. It has a cross on the chest denoting that the wearer is trained medical personnel." - name = "medical doctor's jumpsuit" - icon_state = "medical" - item_state = "w_suit" - item_color = "medical" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/medical/skirt - name = "medical doctor's jumpskirt" - icon_state = "medicalf" - item_color = "medicalf" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - flags_size = null - -/obj/item/clothing/under/rank/medical/blue - name = "medical scrubs" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in baby blue." - icon_state = "scrubsblue" - item_color = "scrubsblue" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/medical/green - name = "medical scrubs" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in dark green." - icon_state = "scrubsgreen" - item_color = "scrubsgreen" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/medical/purple - name = "medical scrubs" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in deep purple." - icon_state = "scrubspurple" - item_color = "scrubspurple" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/medical/mortician - name = "coroner's scrubs" - desc = "It's made of a special fiber that provides minor protection against biohazards. This one is as dark as an emo's poetry." - icon_state = "scrubsblack" - item_color = "scrubsblack" - flags_size = ONESIZEFITSALL - -//paramedic -/obj/item/clothing/under/rank/medical/paramedic - desc = "It's made of a special fiber that provides minor protection against biohazards and radiation. It has a cross on the chest denoting that the wearer is trained medical personnel." - name = "paramedic's jumpsuit" - icon_state = "paramedic" - item_state = "paramedic" - item_color = "paramedic" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 10, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/psych - desc = "A basic white jumpsuit. It has turqouise markings that denote the wearer as a psychiatrist." - name = "psychiatrist's jumpsuit" - icon_state = "psych" - item_state = "w_suit" - item_color = "psych" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/psych/turtleneck - desc = "A turqouise turtleneck and a pair of dark blue slacks, belonging to a psychologist." - name = "psychologist's turtleneck" - icon_state = "psychturtle" - item_state = "b_suit" - item_color = "psychturtle" - flags_size = ONESIZEFITSALL - - -/* - * Medsci, unused (i think) stuff - */ -/obj/item/clothing/under/rank/geneticist_new - desc = "It's made of a special fiber which provides minor protection against biohazards." - name = "geneticist's jumpsuit" - icon_state = "genetics_new" - item_state = "w_suit" - item_color = "genetics_new" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/chemist_new - desc = "It's made of a special fiber which provides minor protection against biohazards." - name = "chemist's jumpsuit" - icon_state = "chemist_new" - item_state = "w_suit" - item_color = "chemist_new" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 50, "acid" = 65) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/scientist_new - desc = "Made of a special fiber that gives special protection against biohazards and small explosions." - name = "scientist's jumpsuit" - icon_state = "scientist_new" - item_state = "w_suit" - item_color = "scientist_new" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/virologist_new - desc = "Made of a special fiber that gives increased protection against biohazards." - name = "virologist's jumpsuit" - icon_state = "virologist_new" - item_state = "w_suit" - item_color = "virologist_new" - permeability_coefficient = 0.50 - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) - flags_size = ONESIZEFITSALL +/* + * Science + */ +/obj/item/clothing/under/rank/research_director + desc = "It's a jumpsuit worn by those with the know-how to achieve the position of \"Research Director\". Its fabric provides minor protection from biological contaminants." + name = "research director's jumpsuit" + icon_state = "director" + item_state = "g_suit" + item_color = "director" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 10, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 35) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/scientist + desc = "It's made of a special fiber that provides minor protection against biohazards. It has markings that denote the wearer as a scientist." + name = "scientist's jumpsuit" + icon_state = "toxins" + item_state = "w_suit" + item_color = "toxinswhite" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/scientist/skirt + name = "scientist's jumpskirt" + icon_state = "sciencewhitef" + item_color = "sciencewhitef" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + flags_size = null + +/obj/item/clothing/under/rank/chemist + desc = "It's made of a special fiber that gives special protection against biohazards. It has a chemist rank stripe on it." + name = "chemist's jumpsuit" + icon_state = "chemistry" + item_state = "w_suit" + item_color = "chemistrywhite" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 50, "acid" = 65) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/chemist/skirt + name = "chemist's jumpskirt" + icon_state = "chemistrywhitef" + item_color = "chemistrywhitef" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + flags_size = null + +/* + * Medical + */ +/obj/item/clothing/under/rank/chief_medical_officer + desc = "It's a jumpsuit worn by those with the experience to be \"Chief Medical Officer\". It provides minor biological protection." + name = "chief medical officer's jumpsuit" + icon_state = "cmo" + item_state = "w_suit" + item_color = "cmo" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/chief_medical_officer/skirt + desc = "It's a jumpskirt worn by those with the experience to be \"Chief Medical Officer\". It provides minor biological protection." + name = "chief medical officer's jumpskirt" + icon_state = "cmof" + item_color = "cmof" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + flags_size = null + +/obj/item/clothing/under/rank/geneticist + desc = "It's made of a special fiber that gives special protection against biohazards. It has a genetics rank stripe on it." + name = "geneticist's jumpsuit" + icon_state = "genetics" + item_state = "w_suit" + item_color = "geneticswhite" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/geneticist/skirt + name = "geneticist's jumpskirt" + icon_state = "geneticswhitef" + item_color = "geneticswhitef" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + flags_size = null + +/obj/item/clothing/under/rank/virologist + desc = "It's made of a special fiber that gives special protection against biohazards. It has a virologist rank stripe on it." + name = "virologist's jumpsuit" + icon_state = "virology" + item_state = "w_suit" + item_color = "virologywhite" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/virologist/skirt + name = "virologist's jumpskirt" + icon_state = "virologywhitef" + item_color = "virologywhitef" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + flags_size = null + +/obj/item/clothing/under/rank/nursesuit + desc = "It's a jumpsuit commonly worn by nursing staff in the medical department." + name = "nurse's suit" + icon_state = "nursesuit" + item_state = "nursesuit" + item_color = "nursesuit" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/nurse + desc = "A dress commonly worn by the nursing staff in the medical department." + name = "nurse's dress" + icon_state = "nurse" + item_state = "nurse" + item_color = "nurse" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/orderly + desc = "A white suit to be worn by orderly people who love orderly things." + name = "orderly's uniform" + icon_state = "orderly" + item_state = "orderly" + item_color = "orderly" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/medical + desc = "It's made of a special fiber that provides minor protection against biohazards. It has a cross on the chest denoting that the wearer is trained medical personnel." + name = "medical doctor's jumpsuit" + icon_state = "medical" + item_state = "w_suit" + item_color = "medical" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/medical/skirt + name = "medical doctor's jumpskirt" + icon_state = "medicalf" + item_color = "medicalf" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + flags_size = null + +/obj/item/clothing/under/rank/medical/blue + name = "medical scrubs" + desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in baby blue." + icon_state = "scrubsblue" + item_color = "scrubsblue" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/medical/green + name = "medical scrubs" + desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in dark green." + icon_state = "scrubsgreen" + item_color = "scrubsgreen" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/medical/purple + name = "medical scrubs" + desc = "It's made of a special fiber that provides minor protection against biohazards. This one is in deep purple." + icon_state = "scrubspurple" + item_color = "scrubspurple" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/medical/mortician + name = "coroner's scrubs" + desc = "It's made of a special fiber that provides minor protection against biohazards. This one is as dark as an emo's poetry." + icon_state = "scrubsblack" + item_color = "scrubsblack" + flags_size = ONESIZEFITSALL + +//paramedic +/obj/item/clothing/under/rank/medical/paramedic + desc = "It's made of a special fiber that provides minor protection against biohazards and radiation. It has a cross on the chest denoting that the wearer is trained medical personnel." + name = "paramedic's jumpsuit" + icon_state = "paramedic" + item_state = "paramedic" + item_color = "paramedic" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 10, "fire" = 0, "acid" = 0) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/psych + desc = "A basic white jumpsuit. It has turqouise markings that denote the wearer as a psychiatrist." + name = "psychiatrist's jumpsuit" + icon_state = "psych" + item_state = "w_suit" + item_color = "psych" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/psych/turtleneck + desc = "A turqouise turtleneck and a pair of dark blue slacks, belonging to a psychologist." + name = "psychologist's turtleneck" + icon_state = "psychturtle" + item_state = "b_suit" + item_color = "psychturtle" + flags_size = ONESIZEFITSALL + + +/* + * Medsci, unused (i think) stuff + */ +/obj/item/clothing/under/rank/geneticist_new + desc = "It's made of a special fiber which provides minor protection against biohazards." + name = "geneticist's jumpsuit" + icon_state = "genetics_new" + item_state = "w_suit" + item_color = "genetics_new" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/chemist_new + desc = "It's made of a special fiber which provides minor protection against biohazards." + name = "chemist's jumpsuit" + icon_state = "chemist_new" + item_state = "w_suit" + item_color = "chemist_new" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 50, "acid" = 65) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/scientist_new + desc = "Made of a special fiber that gives special protection against biohazards and small explosions." + name = "scientist's jumpsuit" + icon_state = "scientist_new" + item_state = "w_suit" + item_color = "scientist_new" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/virologist_new + desc = "Made of a special fiber that gives increased protection against biohazards." + name = "virologist's jumpsuit" + icon_state = "virologist_new" + item_state = "w_suit" + item_color = "virologist_new" + permeability_coefficient = 0.50 + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0) + flags_size = ONESIZEFITSALL diff --git a/code/modules/clothing/under/jobs/plasmamen/_plasmamen.dm b/code/modules/clothing/under/jobs/plasmamen/_plasmamen.dm index 937d5111830..b3d7ce83431 100644 --- a/code/modules/clothing/under/jobs/plasmamen/_plasmamen.dm +++ b/code/modules/clothing/under/jobs/plasmamen/_plasmamen.dm @@ -52,4 +52,4 @@ name = "envirosuit extinguisher cartridge" desc = "A cartridge loaded with a compressed extinguisher mix, used to refill the automatic extinguisher on plasma envirosuits." icon_state = "plasmarefill" - icon = 'icons/obj/device.dmi' \ No newline at end of file + icon = 'icons/obj/device.dmi' diff --git a/code/modules/clothing/under/jobs/plasmamen/civilian_service.dm b/code/modules/clothing/under/jobs/plasmamen/civilian_service.dm index 69697d3be6f..0ef8df4d57b 100644 --- a/code/modules/clothing/under/jobs/plasmamen/civilian_service.dm +++ b/code/modules/clothing/under/jobs/plasmamen/civilian_service.dm @@ -104,4 +104,4 @@ desc = "An envirosuit designed for plasmamen employed as the blueshield." icon_state = "bs_envirosuit" item_state = "bs_envirosuit" - item_color = "bs_envirosuit" \ No newline at end of file + item_color = "bs_envirosuit" diff --git a/code/modules/clothing/under/jobs/plasmamen/security.dm b/code/modules/clothing/under/jobs/plasmamen/security.dm index f39feb6345e..7060037bd90 100644 --- a/code/modules/clothing/under/jobs/plasmamen/security.dm +++ b/code/modules/clothing/under/jobs/plasmamen/security.dm @@ -18,4 +18,4 @@ desc = "A plasmaman containment suit designed for the head of security." icon_state = "hos_envirosuit" item_state = "hos_envirosuit" - item_color = "hos_envirosuit" \ No newline at end of file + item_color = "hos_envirosuit" diff --git a/code/modules/clothing/under/jobs/security.dm b/code/modules/clothing/under/jobs/security.dm index fa48aff0f4f..2602c46d01d 100644 --- a/code/modules/clothing/under/jobs/security.dm +++ b/code/modules/clothing/under/jobs/security.dm @@ -1,200 +1,200 @@ -/* - * Contains: - * Security - * Detective - * Head of Security - */ - - -/* - * Security - */ -/obj/item/clothing/under/rank/warden - desc = "It's made of a slightly sturdier material than standard jumpsuits, to allow for more robust protection. It has the word \"Warden\" written on the shoulders." - name = "warden's jumpsuit" - icon_state = "warden" - item_state = "r_suit" - item_color = "warden" - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) - flags_size = ONESIZEFITSALL - strip_delay = 50 - -/obj/item/clothing/under/rank/warden/skirt - desc = "Standard feminine fashion for a Warden. It is made of sturdier material than standard jumpskirts. It has the word \"Warden\" written on the shoulders." - name = "warden's jumpskirt" - icon_state = "wardenf" - item_state = "r_suit" - item_color = "wardenf" - flags_size = null - -/obj/item/clothing/under/rank/security - name = "security officer's jumpsuit" - desc = "It's made of a slightly sturdier material than standard jumpsuits, to allow for robust protection." - icon_state = "security" - item_state = "r_suit" - item_color = "secred" - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) - flags_size = ONESIZEFITSALL - strip_delay = 50 - -/obj/item/clothing/under/rank/security/skirt - name = "security officer's jumpskirt" - desc = "Standard feminine fashion for Security Officers. It's made of sturdier material than the standard jumpskirts." - icon_state = "secredf" - item_state = "r_suit" - item_color = "secredf" - flags_size = null - -/obj/item/clothing/under/rank/dispatch - name = "dispatcher's uniform" - desc = "A dress shirt and khakis with a security patch sewn on." - icon_state = "dispatch" - item_state = "dispatch" - item_color = "dispatch" - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/security2 - name = "security officer's uniform" - desc = "It's made of a slightly sturdier material, to allow for robust protection." - icon_state = "redshirt2" - item_state = "r_suit" - item_color = "redshirt2" - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/security/corp - icon_state = "sec_corporate" - item_state = "sec_corporate" - item_color = "sec_corporate" - -/obj/item/clothing/under/rank/warden/corp - icon_state = "warden_corporate" - item_state = "warden_corporate" - item_color = "warden_corporate" - -/* - * Detective - */ -/obj/item/clothing/under/det - name = "hard-worn suit" - desc = "Someone who wears this means business." - icon_state = "detective" - item_state = "det" - item_color = "detective" - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) - flags_size = ONESIZEFITSALL - strip_delay = 50 - - sprite_sheets = list( - "Vox" = 'icons/mob/species/vox/uniform.dmi' - ) - -/* - * Head of Security - */ -/obj/item/clothing/under/rank/head_of_security - desc = "It's a jumpsuit worn by those few with the dedication to achieve the position of \"Head of Security\". It has additional armor to protect the wearer." - name = "head of security's jumpsuit" - icon_state = "hos" - item_state = "r_suit" - item_color = "hosred" - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) - flags_size = ONESIZEFITSALL - strip_delay = 60 - -/obj/item/clothing/under/rank/head_of_security/skirt - desc = "It's a fashionable jumpskirt worn by those few with the dedication to achieve the position of \"Head of Security\". It has additional armor to protect the wearer." - name = "head of security's jumpskirt" - icon_state = "hosredf" - item_state = "r_suit" - item_color = "hosredf" - flags_size = null - -/obj/item/clothing/under/rank/head_of_security/corp - icon_state = "hos_corporate" - item_state = "hos_corporate" - item_color = "hos_corporate" - -//Jensen cosplay gear -/obj/item/clothing/under/rank/head_of_security/jensen - desc = "You never asked for anything that stylish." - name = "head of security's jumpsuit" - icon_state = "jensen" - item_state = "jensen" - item_color = "jensen" - flags_size = ONESIZEFITSALL - -//Paradise Station - -/obj/item/clothing/suit/armor/hos/hosnavyjacket - name = "head of security navy jacket" - icon_state = "hosdnavyjacket" - item_state = "hosdnavyjacket" - -/obj/item/clothing/suit/armor/hos/hosbluejacket - name = "head of security blue jacket" - icon_state = "hosbluejacket" - item_state = "hosbluejacket" - -/obj/item/clothing/suit/armor/hos/officernavyjacket - name = "officer's navy jacket" - icon_state = "officernavyjacket" - item_state = "officernavyjacket" - -/obj/item/clothing/suit/armor/hos/officerbluejacket - name = "officer's blue jacket" - icon_state = "officerbluejacket" - item_state = "officerbluejacket" - -//TG Station - -/obj/item/clothing/under/rank/security/formal - name = "security suit" - desc = "A formal security suit for officers complete with nanotrasen belt buckle." - icon_state = "security_formal" - item_state = "gy_suit" - item_color = "security_formal" - -/obj/item/clothing/under/rank/warden/formal - name = "warden's suit" - desc = "A formal security suit for the warden with blue desginations and '/Warden/' stiched into the shoulders." - icon_state = "warden_formal" - item_state = "gy_suit" - item_color = "warden_formal" - -/obj/item/clothing/under/rank/head_of_security/formal - name = "head of security's suit" - desc = "A security suit decorated for those few with the dedication to achieve the position of Head of Security." - icon_state = "hos_formal" - item_state = "gy_suit" - item_color = "hos_formal" - - -//Brig Physician -/obj/item/clothing/under/rank/security/brigphys - desc = "Jumpsuit for Brig Physician it has both medical and security protection." - name = "brig physician's jumpsuit" - icon_state = "brig_phys" - item_state = "brig_phys" - item_color = "brig_phys" - permeability_coefficient = 0.50 - armor = list(melee = 10, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 10, rad = 0, fire = 30, acid = 30) - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/security/brigphys/skirt - desc = "A skirted Brig Physician uniform. It has both security and medical protection." - name = "brig physician's jumpskirt" - icon_state = "brig_physf" - item_state = "brig_physf" - item_color = "brig_physf" - permeability_coefficient = 0.50 - flags_size = ONESIZEFITSALL - -//Pod Pilot -/obj/item/clothing/under/rank/security/pod_pilot - desc = "Suit for your regular pod pilot." - name = "pod pilot's jumpsuit" - icon_state = "pod_pilot" - item_state = "pod_pilot" - item_color = "pod_pilot" +/* + * Contains: + * Security + * Detective + * Head of Security + */ + + +/* + * Security + */ +/obj/item/clothing/under/rank/warden + desc = "It's made of a slightly sturdier material than standard jumpsuits, to allow for more robust protection. It has the word \"Warden\" written on the shoulders." + name = "warden's jumpsuit" + icon_state = "warden" + item_state = "r_suit" + item_color = "warden" + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) + flags_size = ONESIZEFITSALL + strip_delay = 50 + +/obj/item/clothing/under/rank/warden/skirt + desc = "Standard feminine fashion for a Warden. It is made of sturdier material than standard jumpskirts. It has the word \"Warden\" written on the shoulders." + name = "warden's jumpskirt" + icon_state = "wardenf" + item_state = "r_suit" + item_color = "wardenf" + flags_size = null + +/obj/item/clothing/under/rank/security + name = "security officer's jumpsuit" + desc = "It's made of a slightly sturdier material than standard jumpsuits, to allow for robust protection." + icon_state = "security" + item_state = "r_suit" + item_color = "secred" + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) + flags_size = ONESIZEFITSALL + strip_delay = 50 + +/obj/item/clothing/under/rank/security/skirt + name = "security officer's jumpskirt" + desc = "Standard feminine fashion for Security Officers. It's made of sturdier material than the standard jumpskirts." + icon_state = "secredf" + item_state = "r_suit" + item_color = "secredf" + flags_size = null + +/obj/item/clothing/under/rank/dispatch + name = "dispatcher's uniform" + desc = "A dress shirt and khakis with a security patch sewn on." + icon_state = "dispatch" + item_state = "dispatch" + item_color = "dispatch" + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/security2 + name = "security officer's uniform" + desc = "It's made of a slightly sturdier material, to allow for robust protection." + icon_state = "redshirt2" + item_state = "r_suit" + item_color = "redshirt2" + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/security/corp + icon_state = "sec_corporate" + item_state = "sec_corporate" + item_color = "sec_corporate" + +/obj/item/clothing/under/rank/warden/corp + icon_state = "warden_corporate" + item_state = "warden_corporate" + item_color = "warden_corporate" + +/* + * Detective + */ +/obj/item/clothing/under/det + name = "hard-worn suit" + desc = "Someone who wears this means business." + icon_state = "detective" + item_state = "det" + item_color = "detective" + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) + flags_size = ONESIZEFITSALL + strip_delay = 50 + + sprite_sheets = list( + "Vox" = 'icons/mob/species/vox/uniform.dmi' + ) + +/* + * Head of Security + */ +/obj/item/clothing/under/rank/head_of_security + desc = "It's a jumpsuit worn by those few with the dedication to achieve the position of \"Head of Security\". It has additional armor to protect the wearer." + name = "head of security's jumpsuit" + icon_state = "hos" + item_state = "r_suit" + item_color = "hosred" + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50) + flags_size = ONESIZEFITSALL + strip_delay = 60 + +/obj/item/clothing/under/rank/head_of_security/skirt + desc = "It's a fashionable jumpskirt worn by those few with the dedication to achieve the position of \"Head of Security\". It has additional armor to protect the wearer." + name = "head of security's jumpskirt" + icon_state = "hosredf" + item_state = "r_suit" + item_color = "hosredf" + flags_size = null + +/obj/item/clothing/under/rank/head_of_security/corp + icon_state = "hos_corporate" + item_state = "hos_corporate" + item_color = "hos_corporate" + +//Jensen cosplay gear +/obj/item/clothing/under/rank/head_of_security/jensen + desc = "You never asked for anything that stylish." + name = "head of security's jumpsuit" + icon_state = "jensen" + item_state = "jensen" + item_color = "jensen" + flags_size = ONESIZEFITSALL + +//Paradise Station + +/obj/item/clothing/suit/armor/hos/hosnavyjacket + name = "head of security navy jacket" + icon_state = "hosdnavyjacket" + item_state = "hosdnavyjacket" + +/obj/item/clothing/suit/armor/hos/hosbluejacket + name = "head of security blue jacket" + icon_state = "hosbluejacket" + item_state = "hosbluejacket" + +/obj/item/clothing/suit/armor/hos/officernavyjacket + name = "officer's navy jacket" + icon_state = "officernavyjacket" + item_state = "officernavyjacket" + +/obj/item/clothing/suit/armor/hos/officerbluejacket + name = "officer's blue jacket" + icon_state = "officerbluejacket" + item_state = "officerbluejacket" + +//TG Station + +/obj/item/clothing/under/rank/security/formal + name = "security suit" + desc = "A formal security suit for officers complete with nanotrasen belt buckle." + icon_state = "security_formal" + item_state = "gy_suit" + item_color = "security_formal" + +/obj/item/clothing/under/rank/warden/formal + name = "warden's suit" + desc = "A formal security suit for the warden with blue desginations and '/Warden/' stiched into the shoulders." + icon_state = "warden_formal" + item_state = "gy_suit" + item_color = "warden_formal" + +/obj/item/clothing/under/rank/head_of_security/formal + name = "head of security's suit" + desc = "A security suit decorated for those few with the dedication to achieve the position of Head of Security." + icon_state = "hos_formal" + item_state = "gy_suit" + item_color = "hos_formal" + + +//Brig Physician +/obj/item/clothing/under/rank/security/brigphys + desc = "Jumpsuit for Brig Physician it has both medical and security protection." + name = "brig physician's jumpsuit" + icon_state = "brig_phys" + item_state = "brig_phys" + item_color = "brig_phys" + permeability_coefficient = 0.50 + armor = list(melee = 10, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 10, rad = 0, fire = 30, acid = 30) + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/security/brigphys/skirt + desc = "A skirted Brig Physician uniform. It has both security and medical protection." + name = "brig physician's jumpskirt" + icon_state = "brig_physf" + item_state = "brig_physf" + item_color = "brig_physf" + permeability_coefficient = 0.50 + flags_size = ONESIZEFITSALL + +//Pod Pilot +/obj/item/clothing/under/rank/security/pod_pilot + desc = "Suit for your regular pod pilot." + name = "pod pilot's jumpsuit" + icon_state = "pod_pilot" + item_state = "pod_pilot" + item_color = "pod_pilot" diff --git a/code/modules/clothing/under/miscellaneous.dm b/code/modules/clothing/under/miscellaneous.dm index 9e009014deb..22a1d25ba22 100644 --- a/code/modules/clothing/under/miscellaneous.dm +++ b/code/modules/clothing/under/miscellaneous.dm @@ -1,854 +1,851 @@ -/obj/item/clothing/under/pj/red - name = "red pj's" - desc = "Sleepwear." - icon_state = "red_pyjamas" - item_color = "red_pyjamas" - item_state = "w_suit" - -/obj/item/clothing/under/pj/blue - name = "blue pj's" - desc = "Sleepwear." - icon_state = "blue_pyjamas" - item_color = "blue_pyjamas" - item_state = "w_suit" - -/obj/item/clothing/under/patriotsuit - name = "Patriotic Suit" - desc = "Motorcycle not included." - icon_state = "ek" - item_state = "ek" - item_color = "ek" - -/obj/item/clothing/under/captain_fly - name = "rogue captains uniform" - desc = "For the man who doesn't care because he's still free." - icon_state = "captain_fly" - item_state = "captain_fly" - item_color = "captain_fly" - -/obj/item/clothing/under/scratch - name = "white suit" - desc = "A white suit, suitable for an excellent host" - icon_state = "scratch" - item_state = "scratch" - item_color = "scratch" - -/obj/item/clothing/under/sl_suit - desc = "It's a very amish looking suit." - name = "amish suit" - icon_state = "sl_suit" - item_color = "sl_suit" - -/obj/item/clothing/under/waiter - name = "waiter's outfit" - desc = "It's a very smart uniform with a special pocket for tip." - icon_state = "waiter" - item_state = "waiter" - item_color = "waiter" - -/obj/item/clothing/under/rank/mailman - name = "mailman's jumpsuit" - desc = "'Special delivery!'" - icon_state = "mailman" - item_state = "b_suit" - item_color = "mailman" - -/obj/item/clothing/under/rank/vice - name = "vice officer's jumpsuit" - desc = "It's the standard issue pretty-boy outfit, as seen on Holo-Vision." - icon_state = "vice" - item_state = "gy_suit" - item_color = "vice" - -/obj/item/clothing/under/solgov - name = "Sol Federation marine uniform" - desc = "A comfortable and durable combat uniform worn by Sol Federation Marine Forces." - icon_state = "solgov" - item_state = "ro_suit" - item_color = "solgov" - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) - displays_id = 0 - -/obj/item/clothing/under/solgov/command - name = "Sol Federation Lieutenant's uniform" - desc = "A comfortable and durable combat uniform worn by Sol Federation Marine Forces. This one has additional insignia on its shoulders." - icon_state = "solgovc" - item_color = "solgovc" - -/obj/item/clothing/under/solgov/rep - name = "Sol Federation representative's uniform" - desc = "A formal uniform worn by the diplomatic representatives of the Sol Federation." - icon_state = "solgovr" - item_color = "solgovr" - -/obj/item/clothing/under/rank/centcom_officer - desc = "It's a jumpsuit worn by CentComm Officers." - name = "\improper CentComm officer's jumpsuit" - icon_state = "officer" - item_state = "g_suit" - item_color = "officer" - -/obj/item/clothing/under/rank/centcom_commander - desc = "It's a jumpsuit worn by CentComm's highest-tier Commanders." - name = "\improper CentComm officer's jumpsuit" - icon_state = "centcom" - item_state = "dg_suit" - item_color = "centcom" - -/obj/item/clothing/under/rank/centcom/officer - desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Lieutenant-Commander\" and bears \"N.C.V. Fearless CV-286\" on the left shoulder." - name = "\improper Nanotrasen Officers Uniform" - icon_state = "officer" - item_state = "g_suit" - item_color = "officer" - displays_id = 0 - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/centcom/captain - desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Captain\" and bears \"N.C.V. Fearless CV-286\" on the left shoulder." - name = "\improper Nanotrasen Captains Uniform" - icon_state = "centcom" - item_state = "dg_suit" - item_color = "centcom" - displays_id = 0 - -/obj/item/clothing/under/rank/centcom/blueshield - desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Lieutenant\" and bears \"N.S.S. Cyberiad\" on the left shoulder." - name = "\improper Nanotrasen Navy Uniform" - icon_state = "officer" - item_state = "g_suit" - item_color = "officer" - displays_id = 0 - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/centcom/blueshield/New() - ..() - desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Lieutenant\" and bears [station_name()] on the left shoulder." - -/obj/item/clothing/under/rank/centcom/representative - desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Ensign\" and bears \"N.S.S. Cyberiad\" on the left shoulder." - name = "\improper Nanotrasen Navy Uniform" - icon_state = "officer" - item_state = "g_suit" - item_color = "officer" - displays_id = 0 - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/centcom/representative/New() - ..() - desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Ensign\" and bears [station_name()] on the left shoulder." - -/obj/item/clothing/under/rank/centcom/magistrate - desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Magistrate\" and bears \"N.S.S. Cyberiad\" on the left shoulder." - name = "\improper Nanotrasen Navy Uniform" - icon_state = "officer" - item_state = "g_suit" - item_color = "officer" - displays_id = 0 - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/rank/centcom/magistrate/New() - ..() - desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Magistrate\" and bears [station_name()] on the left shoulder." - -/obj/item/clothing/under/rank/centcom/diplomatic - desc = "A very gaudy and official looking uniform of the Nanotrasen Diplomatic Corps." - name = "\improper Nanotrasen Diplomatic Uniform" - icon_state = "presidente" - item_state = "g_suit" - item_color = "presidente" - displays_id = 0 - -/obj/item/clothing/under/rank/blueshield - name = "blueshield uniform" - desc = "A short-sleeved black uniform, paired with grey digital-camo cargo pants. Standard issue to Blueshield officers." - icon_state = "ert_uniform" - item_state = "bl_suit" - item_color = "ert_uniform" - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) - -/obj/item/clothing/under/rank/blueshield/skirt - name = "blueshield skirt" - desc = "A short, black and grey with blue markings skirted uniform. For the feminine Blueshield officers." - icon_state = "blueshieldf" - item_state = "blueshieldf" - item_color = "blueshieldf" - -/obj/item/clothing/under/space - name = "\improper NASA jumpsuit" - desc = "It has a NASA logo on it and is made of space-proofed materials." - icon_state = "black" - item_state = "bl_suit" - item_color = "black" - w_class = WEIGHT_CLASS_BULKY - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.02 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS - cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | ARMS //Needs gloves and shoes with cold protection to be fully protected. - min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT - heat_protection = UPPER_TORSO | LOWER_TORSO|LEGS|FEET|ARMS|HANDS - max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT - resistance_flags = NONE - -/obj/item/clothing/under/acj - name = "administrative cybernetic jumpsuit" - icon_state = "syndicate" - item_state = "bl_suit" - item_color = "syndicate" - desc = "it's a cybernetically enhanced jumpsuit used for administrative duties." - gas_transfer_coefficient = 0.01 - permeability_coefficient = 0.01 - body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS|HEAD - armor = list("melee" = 100, "bullet" = 100, "laser" = 100,"energy" = 100, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) - cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS | HEAD - min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT - heat_protection = UPPER_TORSO | LOWER_TORSO|LEGS|FEET|ARMS|HANDS | HEAD - max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT - slowdown = -10 - siemens_coefficient = 0 - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF | FREEZE_PROOF - -/obj/item/clothing/under/johnny - name = "johnny~~ jumpsuit" - desc = "Johnny~~" - icon_state = "johnny" - item_color = "johnny" - -/obj/item/clothing/under/rainbow - name = "rainbow" - desc = "rainbow" - icon_state = "rainbow" - item_state = "rainbow" - item_color = "rainbow" - -/obj/item/clothing/under/cloud - name = "cloud" - desc = "cloud" - icon_state = "cloud" - item_color = "cloud" - -/obj/item/clothing/under/psysuit - name = "dark undersuit" - desc = "A thick, layered grey undersuit lined with power cables. Feels a little like wearing an electrical storm." - icon_state = "psysuit" - item_state = "psysuit" - item_color = "psysuit" - -/obj/item/clothing/under/gimmick/rank/captain/suit - name = "captain's suit" - desc = "A green suit and yellow necktie. Exemplifies authority." - icon_state = "green_suit" - item_state = "dg_suit" - item_color = "green_suit" - -/obj/item/clothing/under/gimmick/rank/head_of_personnel/suit - name = "head of personnel's suit" - desc = "A teal suit and yellow necktie. An authoritative yet tacky ensemble." - icon_state = "teal_suit" - item_state = "g_suit" - item_color = "teal_suit" - -/obj/item/clothing/under/suit_jacket - name = "black suit" - desc = "A black suit and red tie. Very formal." - icon_state = "black_suit" - item_state = "bl_suit" - item_color = "black_suit" - -/obj/item/clothing/under/suit_jacket/really_black - name = "executive suit" - desc = "A formal black suit and red tie, intended for the station's finest." - icon_state = "really_black_suit" - item_state = "bl_suit" - item_color = "really_black_suit" - -/obj/item/clothing/under/suit_jacket/female - name = "executive suit" - desc = "A formal trouser suit for women, intended for the station's finest." - icon_state = "black_suit_fem" - item_state = "black_suit_fem" - item_color = "black_suit_fem" - -/obj/item/clothing/under/suit_jacket/red - name = "red suit" - desc = "A red suit and blue tie. Somewhat formal." - icon_state = "red_suit" - item_state = "r_suit" - item_color = "red_suit" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/suit_jacket/navy - name = "navy suit" - desc = "A navy suit and red tie, intended for the station's finest." - icon_state = "navy_suit" - item_state = "navy_suit" - item_color = "navy_suit" - -/obj/item/clothing/under/suit_jacket/tan - name = "tan suit" - desc = "A tan suit with a yellow tie. Smart, but casual." - icon_state = "tan_suit" - item_state = "tan_suit" - item_color = "tan_suit" - -/obj/item/clothing/under/suit_jacket/burgundy - name = "burgundy suit" - desc = "A burgundy suit and black tie. Somewhat formal." - icon_state = "burgundy_suit" - item_state = "burgundy_suit" - item_color = "burgundy_suit" - -/obj/item/clothing/under/suit_jacket/charcoal - name = "charcoal suit" - desc = "A charcoal suit and blue tie. Very professional." - icon_state = "charcoal_suit" - item_state = "charcoal_suit" - item_color = "charcoal_suit" - -/obj/item/clothing/under/blackskirt - name = "black skirt" - desc = "A black skirt, very fancy!" - icon_state = "blackskirt" - item_color = "blackskirt" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/under/schoolgirl - name = "schoolgirl uniform" - desc = "It's just like one of my Japanese animes!" - icon_state = "schoolgirl" - item_state = "schoolgirl" - item_color = "schoolgirl" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - -/obj/item/clothing/under/overalls - name = "laborer's overalls" - desc = "A set of durable overalls for getting the job done." - icon_state = "overalls" - item_state = "lb_suit" - item_color = "overalls" - -/obj/item/clothing/under/pirate - name = "pirate outfit" - desc = "Yarr." - icon_state = "pirate" - item_state = "pirate" - item_color = "pirate" - -/obj/item/clothing/under/pirate_rags - name = "pirate rags" - desc = "an old ragged set of clothing" - icon_state = "piraterags" - item_state = "piraterags" - item_color = "piraterags" - -/obj/item/clothing/under/soviet - name = "soviet uniform" - desc = "For the Motherland!" - icon_state = "soviet" - item_state = "soviet" - item_color = "soviet" - -/obj/item/clothing/under/redcoat - name = "redcoat uniform" - desc = "Looks old." - icon_state = "redcoat" - item_state = "redcoat" - item_color = "redcoat" - -/obj/item/clothing/under/kilt - name = "kilt" - desc = "Includes shoes and plaid" - icon_state = "kilt" - item_state = "kilt" - item_color = "kilt" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|FEET - -/obj/item/clothing/under/sexymime - name = "sexy mime outfit" - desc = "The only time when you DON'T enjoy looking at someone's rack." - icon_state = "sexymime" - item_state = "sexymime" - item_color = "sexymime" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/gladiator - name = "gladiator uniform" - desc = "Are you not entertained? Is that not why you are here?" - icon_state = "gladiator" - item_state = "gladiator" - item_color = "gladiator" - body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS - resistance_flags = NONE - -/obj/item/clothing/under/gladiator/ash_walker - desc = "This gladiator uniform appears to be covered in ash and fairly dated." - has_sensor = FALSE - -//dress - -/obj/item/clothing/under/dress/dress_fire - name = "flame dress" - desc = "A small black dress with blue flames print on it." - icon_state = "dress_fire" - item_color = "dress_fire" - -/obj/item/clothing/under/dress/dress_green - name = "green dress" - desc = "A simple, tight fitting green dress." - icon_state = "dress_green" - item_color = "dress_green" - -/obj/item/clothing/under/dress/dress_orange - name = "orange dress" - desc = "A fancy orange gown for those who like to show leg." - icon_state = "dress_orange" - item_color = "dress_orange" - -/obj/item/clothing/under/dress/dress_pink - name = "pink dress" - desc = "A simple, tight fitting pink dress." - icon_state = "dress_pink" - item_color = "dress_pink" - -/obj/item/clothing/under/dress/dress_yellow - name = "yellow dress" - desc = "A flirty, little yellow dress." - icon_state = "dress_yellow" - item_color = "dress_yellow" - -/obj/item/clothing/under/dress/dress_saloon - name = "saloon girl dress" - desc = "A old western inspired gown for the girl who likes to drink." - icon_state = "dress_saloon" - item_color = "dress_saloon" - -/obj/item/clothing/under/dress/dress_rd - name = "research director dress uniform" - desc = "Feminine fashion for the style concious RD." - icon_state = "dress_rd" - item_color = "dress_rd" - -/obj/item/clothing/under/dress/dress_cap - name = "captain dress uniform" - desc = "Feminine fashion for the style concious captain." - icon_state = "dress_cap" - item_color = "dress_cap" - -/obj/item/clothing/under/dress/dress_hop - name = "head of personal dress uniform" - desc = "Feminine fashion for the style concious HoP." - icon_state = "dress_hop" - item_color = "dress_hop" - -/obj/item/clothing/under/dress/dress_hr - name = "human resources director uniform" - desc = "Superior class for the nosy H.R. Director." - icon_state = "huresource" - item_color = "huresource" - -/obj/item/clothing/under/dress/plaid_blue - name = "blue plaid skirt" - desc = "A preppy blue skirt with a white blouse." - icon_state = "plaid_blue" - item_color = "plaid_blue" - -/obj/item/clothing/under/dress/plaid_red - name = "red plaid skirt" - desc = "A preppy red skirt with a white blouse." - icon_state = "plaid_red" - item_color = "plaid_red" - -/obj/item/clothing/under/dress/plaid_purple - name = "blue purple skirt" - desc = "A preppy purple skirt with a white blouse." - icon_state = "plaid_purple" - item_color = "plaid_purple" - -//wedding stuff - -/obj/item/clothing/under/wedding/bride_orange - name = "orange wedding dress" - desc = "A big and puffy orange dress." - icon_state = "bride_orange" - item_color = "bride_orange" - flags_inv = HIDESHOES - -/obj/item/clothing/under/wedding/bride_purple - name = "purple wedding dress" - desc = "A big and puffy purple dress." - icon_state = "bride_purple" - item_color = "bride_purple" - flags_inv = HIDESHOES - -/obj/item/clothing/under/wedding/bride_blue - name = "blue wedding dress" - desc = "A big and puffy blue dress." - icon_state = "bride_blue" - item_color = "bride_blue" - flags_inv = HIDESHOES - -/obj/item/clothing/under/wedding/bride_red - name = "red wedding dress" - desc = "A big and puffy red dress." - icon_state = "bride_red" - item_color = "bride_red" - flags_inv = HIDESHOES - -/obj/item/clothing/under/wedding/bride_white - name = "orange wedding dress" - desc = "A white wedding gown made from the finest silk." - icon_state = "bride_white" - item_color = "bride_white" - flags_inv = HIDESHOES - -/obj/item/clothing/under/sundress - name = "sundress" - desc = "Makes you want to frolic in a field of daisies." - icon_state = "sundress" - item_state = "sundress" - item_color = "sundress" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/captainparade - name = "captain's parade uniform" - desc = "A captain's luxury-wear, for special occasions." - icon_state = "captain_parade" - item_state = "by_suit" - item_color = "captain_parade" - -/obj/item/clothing/under/roman - name = "roman armor" - desc = "An ancient Roman armor. Made of metallic strips and leather straps." - icon_state = "roman" - item_color = "roman" - item_state = "armor" - strip_delay = 100 - resistance_flags = NONE - -/obj/item/clothing/under/maid - name = "maid costume" - desc = "Maid in China." - icon_state = "meido" - item_state = "meido" - item_color = "meido" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/janimaid - name = "maid uniform" - desc = "A simple maid uniform for housekeeping." - icon_state = "janimaid" - item_state = "janimaid" - item_color = "janimaid" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/singery - name = "yellow performer's outfit" - desc = "Just looking at this makes you want to sing." - icon_state = "ysing" - item_state = "ysing" - item_color = "ysing" - -/obj/item/clothing/under/singerb - name = "blue performer's outfit" - desc = "Just looking at this makes you want to sing." - icon_state = "bsing" - item_state = "bsing" - item_color = "bsing" - -/obj/item/clothing/under/jester - name = "jester suit" - desc = "A jolly dress, well suited to entertain your master, nuncle." - icon_state = "jester" - item_color = "jester" - -/obj/item/clothing/under/flappers - name = "flappers" - desc = "Nothing like the roarin' '20s, flapping the night away on the dance floor." - icon_state = "flapper" - item_state = "flapper" - item_color = "flapper" - -/obj/item/clothing/under/mafia - name = "mafia outfit" - desc = "The business of the mafia is business." - icon_state = "mafia" - item_state = "mafia" - item_color = "mafia" - -/obj/item/clothing/under/mafia/vest - name = "mafia vest" - desc = "Extreme problems often require extreme solutions." - icon_state = "mafiavest" - item_state = "mafiavest" - item_color = "mafiavest" - -/obj/item/clothing/under/mafia/white - name = "white mafia outfit" - desc = "The best defense against the treacherous is treachery." - icon_state = "mafiawhite" - item_state = "mafiawhite" - item_color = "mafiawhite" - -/obj/item/clothing/under/mafia/sue - name = "mafia vest" - desc = "The business is born into." - icon_state = "suevest" - item_state = "suevest" - item_color = "suevest" - -/obj/item/clothing/under/mafia/tan - name = "leather mafia outfit" - desc = "The big drum sounds good only from a distance." - icon_state = "mafiatan" - item_state = "mafiatan" - item_color = "mafiatan" - - -/obj/item/clothing/under/bane - name = "Bane Harness" - desc = "Wear this harness to become the bane of the station." - icon_state = "bane" - item_state = "bane" - item_color = "bane" - -/obj/item/clothing/under/vox - name = "Ripped Jumpsuit" - desc = "A jumpsuit that looks like it's been shredded by some talons. Who could wear this now?" - icon = 'icons/obj/clothing/species/vox/uniforms.dmi' - icon_state = "vgrey" - item_state = "vgrey" - item_color = "vgrey" - -/obj/item/clothing/under/psyjump - name = "Psychic Amp Jumpsuit" - desc = "A suit made of strange materials." - icon_state = "psyamp" - item_state = "psyamp" - item_color = "psyamp" - -/obj/item/clothing/under/rebeloutfit - name = "Rebel Outfit" - desc = "Made in Seattle, 2216." - icon_state = "colin_earle" - item_state = "colin_earle" - item_color = "colin_earle" - -/obj/item/clothing/under/officeruniform - name = "Clown Officer's Uniform" - desc = "For Clown officers, this uniform was designed by the great clown designer Hugo Boss." - icon_state = "officeruniform" - item_color = "officeruniform" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/soldieruniform - name = "Clown Soldier's Uniform" - desc = "For the basic grunt of the Clown army." - icon_state = "soldieruniform" - item_color = "soldieruniform" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/pennywise - name = "Pennywise Costume" - desc = "It's everything you ever were afraid of." - icon_state = "pennywise" - item_color = "pennywise" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/assistantformal - name = "assistant's formal uniform" - desc = "An assistant's formal-wear. Why an assistant needs formal-wear is still unknown." - icon_state = "assistant_formal" - item_state = "gy_suit" - item_color = "assistant_formal" - -/obj/item/clothing/under/blacktango - name = "black tango dress" - desc = "Filled with Latin fire." - icon_state = "black_tango" - item_state = "wcoat" - item_color = "black_tango" - -/obj/item/clothing/under/stripeddress - name = "striped dress" - desc = "Fashion in space." - icon_state = "striped_dress" - item_state = "stripeddress" - item_color = "striped_dress" - -/obj/item/clothing/under/sailordress - name = "sailor dress" - desc = "Formal wear for a leading lady." - icon_state = "sailor_dress" - item_state = "sailordress" - item_color = "sailor_dress" - -/obj/item/clothing/under/redeveninggown - name = "red evening gown" - desc = "Fancy dress for space bar singers." - icon_state = "red_evening_gown" - item_state = "redeveninggown" - item_color = "red_evening_gown" - -/obj/item/clothing/under/suit_jacket/checkered - name = "checkered suit" - desc = "That's a very nice suit you have there. Shame if something were to happen to it, eh?" - icon_state = "checkered_suit" - item_state = "checkered_suit" - item_color = "checkered_suit" - -/obj/item/clothing/under/owl - name = "owl uniform" - desc = "A soft brown jumpsuit made of synthetic feathers and strong conviction." - icon_state = "owl" - item_color = "owl" - -/obj/item/clothing/under/griffin - name = "griffon uniform" - desc = "A soft brown jumpsuit with a white feather collar made of synthetic feathers and a lust for mayhem." - icon_state = "griffin" - item_color = "griffin" - -/obj/item/clothing/under/noble_clothes - name = "noble clothes" - desc = "They fall just short of majestic." - icon_state = "noble_clothes" - item_color = "noble_clothes" - item_state = "noble_clothes" - -/obj/item/clothing/under/contortionist - name = "atmospheric technician's jumpsuit" - desc = "A light jumpsuit useful for squeezing through narrow vents." - icon_state = "atmos" - item_state = "atmos_suit" - item_color = "atmos" - resistance_flags = FIRE_PROOF - -/obj/item/clothing/under/contortionist/equipped(mob/living/carbon/human/user, slot) - if(!user.ventcrawler) - user.ventcrawler = 2 - ..() - -/obj/item/clothing/under/contortionist/dropped(mob/living/carbon/human/user) - if(!user.get_int_organ(/obj/item/organ/internal/heart/gland/ventcrawling)) - user.ventcrawler = 0 - ..() - -/obj/item/clothing/under/contortionist/proc/check_clothing(mob/user as mob) - //Allowed to wear: glasses, shoes, gloves, pockets, mask, and jumpsuit (obviously) - var/list/slot_must_be_empty = list(slot_back,slot_handcuffed,slot_legcuffed,slot_l_hand,slot_r_hand,slot_belt,slot_head,slot_wear_suit) - for(var/slot_id in slot_must_be_empty) - if(user.get_item_by_slot(slot_id)) - to_chat(user,"You can't fit inside while wearing that \the [user.get_item_by_slot(slot_id)].") - return 0 - return 1 - -/obj/item/clothing/under/cursedclown - name = "cursed clown suit" - desc = "It wasn't already?" - icon = 'icons/goonstation/objects/clothing/uniform.dmi' - icon_state = "cursedclown" - item_state = "cclown_uniform" - item_color = "cursedclown" - icon_override = 'icons/goonstation/mob/clothing/uniform.dmi' - lefthand_file = 'icons/goonstation/mob/inhands/clothing_lefthand.dmi' - righthand_file = 'icons/goonstation/mob/inhands/clothing_righthand.dmi' - flags = NODROP - resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF - flags_size = ONESIZEFITSALL - has_sensor = 0 // HUNKE - -/obj/item/clothing/under/victdress - name = "black victorian dress" - desc = "A victorian style dress, fancy!" - icon_state = "victorianblackdress" - item_state = "victorianblackdress" - item_color = "victorianblackdress" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/victdress/red - name = "red victorian dress" - icon_state = "victorianreddress" - item_state = "victorianreddress" - item_color = "victorianreddress" - -/obj/item/clothing/under/victsuit - name = "victorian suit" - desc = "A victorian style suit, fancy!" - icon_state = "victorianvest" - item_state = "victorianvest" - item_color = "victorianvest" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/victsuit/redblk - name = "red and black victorian suit" - icon_state = "victorianblred" - item_state = "victorianblred" - item_color = "victorianblred" - -/obj/item/clothing/under/victsuit/red - name = "red victorian suit" - icon_state = "victorianredvest" - item_state = "victorianredvest" - item_color = "victorianredvest" - -/obj/item/clothing/under/medigown - name = "medical gown" - desc = "a flimsy examination gown, the back ties never close." - icon_state = "medicalgown" - item_state = "medicalgown" - item_color = "medicalgown" - body_parts_covered = UPPER_TORSO|LOWER_TORSO - -/obj/item/clothing/under/burial - name = "burial garments" - desc = "Traditional burial garments from the early 22nd century." - icon_state = "burial" - item_state = "burial" - item_color = "burial" - -/obj/item/clothing/under/redhawaiianshirt - name = "red hawaiian shirt" - desc = "a floral shirt worn to most vacation destinations." - icon_state = "hawaiianred" - item_state = "hawaiianred" - item_color = "hawaiianred" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/pinkhawaiianshirt - name = "pink hawaiian shirt" - desc = "a pink floral shirt the material feels cool and comfy." - icon_state = "hawaiianpink" - item_state = "hawaiianpink" - item_color = "hawaiianpink" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/orangehawaiianshirt - name = "orange hawaiian shirt" - desc = "a orange floral shirt for a relaxing day in space." - icon_state = "hawaiianorange" - item_state = "hawaiianorange" - item_color = "hawaiianorange" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/bluehawaiianshirt - name = "blue hawaiian shirt" - desc = "a blue floral shirt it has a oddly colored pink flower on it." - icon_state = "hawaiianblue" - item_state = "hawaiianblue" - item_color = "hawaiianblue" - flags_size = ONESIZEFITSALL - -/obj/item/clothing/under/misc/durathread - name = "durathread jumpsuit" - desc = "A jumpsuit made from durathread, its resilient fibres provide some protection to the wearer." - icon_state = "durathread" - item_state = "durathread" - item_color = "durathread" - armor = list(melee = 10, bullet = 0, laser = 10, energy = 0, bomb = 5, bio = 0, rad = 0, fire = 0, acid = 0) - -/obj/item/clothing/under/cuban_suit - name = "rhumba outfit" - desc = "A satin shirt and high-waisted pants, worn by dancers in the Rhumba style. It smells oddly like... sulfur?" - icon_state = "cuban_suit" - item_state = "cuban_suit" - item_color = "cuban_suit" \ No newline at end of file +/obj/item/clothing/under/pj/red + name = "red pj's" + desc = "Sleepwear." + icon_state = "red_pyjamas" + item_color = "red_pyjamas" + item_state = "w_suit" + +/obj/item/clothing/under/pj/blue + name = "blue pj's" + desc = "Sleepwear." + icon_state = "blue_pyjamas" + item_color = "blue_pyjamas" + item_state = "w_suit" + +/obj/item/clothing/under/patriotsuit + name = "Patriotic Suit" + desc = "Motorcycle not included." + icon_state = "ek" + item_state = "ek" + item_color = "ek" + +/obj/item/clothing/under/captain_fly + name = "rogue captains uniform" + desc = "For the man who doesn't care because he's still free." + icon_state = "captain_fly" + item_state = "captain_fly" + item_color = "captain_fly" + +/obj/item/clothing/under/scratch + name = "white suit" + desc = "A white suit, suitable for an excellent host" + icon_state = "scratch" + item_state = "scratch" + item_color = "scratch" + +/obj/item/clothing/under/sl_suit + desc = "It's a very amish looking suit." + name = "amish suit" + icon_state = "sl_suit" + item_color = "sl_suit" + +/obj/item/clothing/under/waiter + name = "waiter's outfit" + desc = "It's a very smart uniform with a special pocket for tip." + icon_state = "waiter" + item_state = "waiter" + item_color = "waiter" + +/obj/item/clothing/under/rank/mailman + name = "mailman's jumpsuit" + desc = "'Special delivery!'" + icon_state = "mailman" + item_state = "b_suit" + item_color = "mailman" + +/obj/item/clothing/under/rank/vice + name = "vice officer's jumpsuit" + desc = "It's the standard issue pretty-boy outfit, as seen on Holo-Vision." + icon_state = "vice" + item_state = "gy_suit" + item_color = "vice" + +/obj/item/clothing/under/solgov + name = "Sol Federation marine uniform" + desc = "A comfortable and durable combat uniform worn by Sol Federation Marine Forces." + icon_state = "solgov" + item_state = "ro_suit" + item_color = "solgov" + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) + displays_id = 0 + +/obj/item/clothing/under/solgov/command + name = "Sol Federation Lieutenant's uniform" + desc = "A comfortable and durable combat uniform worn by Sol Federation Marine Forces. This one has additional insignia on its shoulders." + icon_state = "solgovc" + item_color = "solgovc" + +/obj/item/clothing/under/solgov/rep + name = "Sol Federation representative's uniform" + desc = "A formal uniform worn by the diplomatic representatives of the Sol Federation." + icon_state = "solgovr" + item_color = "solgovr" + +/obj/item/clothing/under/rank/centcom_officer + desc = "It's a jumpsuit worn by CentComm Officers." + name = "\improper CentComm officer's jumpsuit" + icon_state = "officer" + item_state = "g_suit" + item_color = "officer" + +/obj/item/clothing/under/rank/centcom_commander + desc = "It's a jumpsuit worn by CentComm's highest-tier Commanders." + name = "\improper CentComm officer's jumpsuit" + icon_state = "centcom" + item_state = "dg_suit" + item_color = "centcom" + +/obj/item/clothing/under/rank/centcom/officer + desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Lieutenant-Commander\" and bears \"N.A.S. Trurl \" on the left shoulder. Worn exclusively by officers of the Nanotrasen Navy. It's got exotic materials for protection." + name = "\improper Nanotrasen Naval Officer Uniform" + icon_state = "navy_gold" + item_state = "navy_gold" + item_color = "navy_gold" + displays_id = 0 + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/centcom/captain + desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Captain\" and bears \"N.A.S. Trurl \" on the left shoulder. Worn exclusively by officers of the Nanotrasen Navy. It's got exotic materials for protection." + name = "\improper Nanotrasen Naval Captain Uniform" + icon_state = "navy_gold" + item_state = "navy_gold" + item_color = "navy_gold" + displays_id = 0 + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/centcom/blueshield + desc = "Gold trim on space-black cloth, this uniform bears \"Close Protection\" on the left shoulder." + name = "\improper Formal Nanotrasen Uniform" + icon_state = "officer" + item_state = "g_suit" + item_color = "officer" + displays_id = 0 + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/centcom/representative + desc = "Gold trim on space-black cloth, this uniform bears \"N.S.S. Cyberiad\" on the left shoulder." + name = "\improper Formal Nanotrasen Uniform" + icon_state = "officer" + item_state = "g_suit" + item_color = "officer" + displays_id = 0 + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/centcom/representative/New() + ..() + desc = "Gold trim on space-black cloth, this uniform bears [station_name()] on the left shoulder." + +/obj/item/clothing/under/rank/centcom/magistrate + desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Magistrate\" and bears \"N.S.S. Cyberiad\" on the left shoulder." + name = "\improper Formal Nanotrasen Uniform" + icon_state = "officer" + item_state = "g_suit" + item_color = "officer" + displays_id = 0 + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/rank/centcom/magistrate/New() + ..() + desc = "Gold trim on space-black cloth, this uniform displays the rank of \"Magistrate\" and bears [station_name()] on the left shoulder." + +/obj/item/clothing/under/rank/centcom/diplomatic + desc = "A very gaudy and official looking uniform of the Nanotrasen Diplomatic Corps." + name = "\improper Nanotrasen Diplomatic Uniform" + icon_state = "presidente" + item_state = "g_suit" + item_color = "presidente" + displays_id = 0 + +/obj/item/clothing/under/rank/blueshield + name = "blueshield uniform" + desc = "A short-sleeved black uniform, paired with grey digital-camo cargo pants. Standard issue to Blueshields." + icon_state = "ert_uniform" + item_state = "bl_suit" + item_color = "ert_uniform" + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 30, "acid" = 30) + +/obj/item/clothing/under/rank/blueshield/skirt + name = "blueshield skirt" + desc = "A short, black and grey with blue markings skirted uniform. For the feminine Blueshield." + icon_state = "blueshieldf" + item_state = "blueshieldf" + item_color = "blueshieldf" + +/obj/item/clothing/under/space + name = "\improper NASA jumpsuit" + desc = "It has a NASA logo on it and is made of space-proofed materials." + icon_state = "black" + item_state = "bl_suit" + item_color = "black" + w_class = WEIGHT_CLASS_BULKY + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.02 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS + cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | ARMS //Needs gloves and shoes with cold protection to be fully protected. + min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT + heat_protection = UPPER_TORSO | LOWER_TORSO|LEGS|FEET|ARMS|HANDS + max_heat_protection_temperature = SPACE_SUIT_MAX_TEMP_PROTECT + resistance_flags = NONE + +/obj/item/clothing/under/acj + name = "administrative cybernetic jumpsuit" + icon_state = "syndicate" + item_state = "bl_suit" + item_color = "syndicate" + desc = "it's a cybernetically enhanced jumpsuit used for administrative duties." + gas_transfer_coefficient = 0.01 + permeability_coefficient = 0.01 + body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS|HEAD + armor = list("melee" = 100, "bullet" = 100, "laser" = 100,"energy" = 100, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100) + cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS | HEAD + min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT + heat_protection = UPPER_TORSO | LOWER_TORSO|LEGS|FEET|ARMS|HANDS | HEAD + max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT + slowdown = -10 + siemens_coefficient = 0 + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF | FREEZE_PROOF + +/obj/item/clothing/under/johnny + name = "johnny~~ jumpsuit" + desc = "Johnny~~" + icon_state = "johnny" + item_color = "johnny" + +/obj/item/clothing/under/rainbow + name = "rainbow" + desc = "rainbow" + icon_state = "rainbow" + item_state = "rainbow" + item_color = "rainbow" + +/obj/item/clothing/under/cloud + name = "cloud" + desc = "cloud" + icon_state = "cloud" + item_color = "cloud" + +/obj/item/clothing/under/psysuit + name = "dark undersuit" + desc = "A thick, layered grey undersuit lined with power cables. Feels a little like wearing an electrical storm." + icon_state = "psysuit" + item_state = "psysuit" + item_color = "psysuit" + +/obj/item/clothing/under/gimmick/rank/captain/suit + name = "captain's suit" + desc = "A green suit and yellow necktie. Exemplifies authority." + icon_state = "green_suit" + item_state = "dg_suit" + item_color = "green_suit" + +/obj/item/clothing/under/gimmick/rank/head_of_personnel/suit + name = "head of personnel's suit" + desc = "A teal suit and yellow necktie. An authoritative yet tacky ensemble." + icon_state = "teal_suit" + item_state = "g_suit" + item_color = "teal_suit" + +/obj/item/clothing/under/suit_jacket + name = "black suit" + desc = "A black suit and red tie. Very formal." + icon_state = "black_suit" + item_state = "bl_suit" + item_color = "black_suit" + +/obj/item/clothing/under/suit_jacket/really_black + name = "executive suit" + desc = "A formal black suit and red tie, intended for the station's finest." + icon_state = "really_black_suit" + item_state = "bl_suit" + item_color = "really_black_suit" + +/obj/item/clothing/under/suit_jacket/female + name = "executive suit" + desc = "A formal trouser suit for women, intended for the station's finest." + icon_state = "black_suit_fem" + item_state = "black_suit_fem" + item_color = "black_suit_fem" + +/obj/item/clothing/under/suit_jacket/red + name = "red suit" + desc = "A red suit and blue tie. Somewhat formal." + icon_state = "red_suit" + item_state = "r_suit" + item_color = "red_suit" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/suit_jacket/navy + name = "navy suit" + desc = "A navy suit and red tie, intended for the station's finest." + icon_state = "navy_suit" + item_state = "navy_suit" + item_color = "navy_suit" + +/obj/item/clothing/under/suit_jacket/tan + name = "tan suit" + desc = "A tan suit with a yellow tie. Smart, but casual." + icon_state = "tan_suit" + item_state = "tan_suit" + item_color = "tan_suit" + +/obj/item/clothing/under/suit_jacket/burgundy + name = "burgundy suit" + desc = "A burgundy suit and black tie. Somewhat formal." + icon_state = "burgundy_suit" + item_state = "burgundy_suit" + item_color = "burgundy_suit" + +/obj/item/clothing/under/suit_jacket/charcoal + name = "charcoal suit" + desc = "A charcoal suit and blue tie. Very professional." + icon_state = "charcoal_suit" + item_state = "charcoal_suit" + item_color = "charcoal_suit" + +/obj/item/clothing/under/blackskirt + name = "black skirt" + desc = "A black skirt, very fancy!" + icon_state = "blackskirt" + item_color = "blackskirt" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/under/schoolgirl + name = "schoolgirl uniform" + desc = "It's just like one of my Japanese animes!" + icon_state = "schoolgirl" + item_state = "schoolgirl" + item_color = "schoolgirl" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + +/obj/item/clothing/under/overalls + name = "laborer's overalls" + desc = "A set of durable overalls for getting the job done." + icon_state = "overalls" + item_state = "lb_suit" + item_color = "overalls" + +/obj/item/clothing/under/pirate + name = "pirate outfit" + desc = "Yarr." + icon_state = "pirate" + item_state = "pirate" + item_color = "pirate" + +/obj/item/clothing/under/pirate_rags + name = "pirate rags" + desc = "an old ragged set of clothing" + icon_state = "piraterags" + item_state = "piraterags" + item_color = "piraterags" + +/obj/item/clothing/under/soviet + name = "soviet uniform" + desc = "For the Motherland!" + icon_state = "soviet" + item_state = "soviet" + item_color = "soviet" + +/obj/item/clothing/under/redcoat + name = "redcoat uniform" + desc = "Looks old." + icon_state = "redcoat" + item_state = "redcoat" + item_color = "redcoat" + +/obj/item/clothing/under/kilt + name = "kilt" + desc = "Includes shoes and plaid" + icon_state = "kilt" + item_state = "kilt" + item_color = "kilt" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|FEET + +/obj/item/clothing/under/sexymime + name = "sexy mime outfit" + desc = "The only time when you DON'T enjoy looking at someone's rack." + icon_state = "sexymime" + item_state = "sexymime" + item_color = "sexymime" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/gladiator + name = "gladiator uniform" + desc = "Are you not entertained? Is that not why you are here?" + icon_state = "gladiator" + item_state = "gladiator" + item_color = "gladiator" + body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS + resistance_flags = NONE + +/obj/item/clothing/under/gladiator/ash_walker + desc = "This gladiator uniform appears to be covered in ash and fairly dated." + has_sensor = FALSE + +//dress + +/obj/item/clothing/under/dress/dress_fire + name = "flame dress" + desc = "A small black dress with blue flames print on it." + icon_state = "dress_fire" + item_color = "dress_fire" + +/obj/item/clothing/under/dress/dress_green + name = "green dress" + desc = "A simple, tight fitting green dress." + icon_state = "dress_green" + item_color = "dress_green" + +/obj/item/clothing/under/dress/dress_orange + name = "orange dress" + desc = "A fancy orange gown for those who like to show leg." + icon_state = "dress_orange" + item_color = "dress_orange" + +/obj/item/clothing/under/dress/dress_pink + name = "pink dress" + desc = "A simple, tight fitting pink dress." + icon_state = "dress_pink" + item_color = "dress_pink" + +/obj/item/clothing/under/dress/dress_yellow + name = "yellow dress" + desc = "A flirty, little yellow dress." + icon_state = "dress_yellow" + item_color = "dress_yellow" + +/obj/item/clothing/under/dress/dress_saloon + name = "saloon girl dress" + desc = "A old western inspired gown for the girl who likes to drink." + icon_state = "dress_saloon" + item_color = "dress_saloon" + +/obj/item/clothing/under/dress/dress_rd + name = "research director dress uniform" + desc = "Feminine fashion for the style concious RD." + icon_state = "dress_rd" + item_color = "dress_rd" + +/obj/item/clothing/under/dress/dress_cap + name = "captain dress uniform" + desc = "Feminine fashion for the style concious captain." + icon_state = "dress_cap" + item_color = "dress_cap" + +/obj/item/clothing/under/dress/dress_hop + name = "head of personal dress uniform" + desc = "Feminine fashion for the style concious HoP." + icon_state = "dress_hop" + item_color = "dress_hop" + +/obj/item/clothing/under/dress/dress_hr + name = "human resources director uniform" + desc = "Superior class for the nosy H.R. Director." + icon_state = "huresource" + item_color = "huresource" + +/obj/item/clothing/under/dress/plaid_blue + name = "blue plaid skirt" + desc = "A preppy blue skirt with a white blouse." + icon_state = "plaid_blue" + item_color = "plaid_blue" + +/obj/item/clothing/under/dress/plaid_red + name = "red plaid skirt" + desc = "A preppy red skirt with a white blouse." + icon_state = "plaid_red" + item_color = "plaid_red" + +/obj/item/clothing/under/dress/plaid_purple + name = "blue purple skirt" + desc = "A preppy purple skirt with a white blouse." + icon_state = "plaid_purple" + item_color = "plaid_purple" + +//wedding stuff + +/obj/item/clothing/under/wedding/bride_orange + name = "orange wedding dress" + desc = "A big and puffy orange dress." + icon_state = "bride_orange" + item_color = "bride_orange" + flags_inv = HIDESHOES + +/obj/item/clothing/under/wedding/bride_purple + name = "purple wedding dress" + desc = "A big and puffy purple dress." + icon_state = "bride_purple" + item_color = "bride_purple" + flags_inv = HIDESHOES + +/obj/item/clothing/under/wedding/bride_blue + name = "blue wedding dress" + desc = "A big and puffy blue dress." + icon_state = "bride_blue" + item_color = "bride_blue" + flags_inv = HIDESHOES + +/obj/item/clothing/under/wedding/bride_red + name = "red wedding dress" + desc = "A big and puffy red dress." + icon_state = "bride_red" + item_color = "bride_red" + flags_inv = HIDESHOES + +/obj/item/clothing/under/wedding/bride_white + name = "orange wedding dress" + desc = "A white wedding gown made from the finest silk." + icon_state = "bride_white" + item_color = "bride_white" + flags_inv = HIDESHOES + +/obj/item/clothing/under/sundress + name = "sundress" + desc = "Makes you want to frolic in a field of daisies." + icon_state = "sundress" + item_state = "sundress" + item_color = "sundress" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/captainparade + name = "captain's parade uniform" + desc = "A captain's luxury-wear, for special occasions." + icon_state = "captain_parade" + item_state = "by_suit" + item_color = "captain_parade" + +/obj/item/clothing/under/roman + name = "roman armor" + desc = "An ancient Roman armor. Made of metallic strips and leather straps." + icon_state = "roman" + item_color = "roman" + item_state = "armor" + strip_delay = 100 + resistance_flags = NONE + +/obj/item/clothing/under/maid + name = "maid costume" + desc = "Maid in China." + icon_state = "meido" + item_state = "meido" + item_color = "meido" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/janimaid + name = "maid uniform" + desc = "A simple maid uniform for housekeeping." + icon_state = "janimaid" + item_state = "janimaid" + item_color = "janimaid" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/singery + name = "yellow performer's outfit" + desc = "Just looking at this makes you want to sing." + icon_state = "ysing" + item_state = "ysing" + item_color = "ysing" + +/obj/item/clothing/under/singerb + name = "blue performer's outfit" + desc = "Just looking at this makes you want to sing." + icon_state = "bsing" + item_state = "bsing" + item_color = "bsing" + +/obj/item/clothing/under/jester + name = "jester suit" + desc = "A jolly dress, well suited to entertain your master, nuncle." + icon_state = "jester" + item_color = "jester" + +/obj/item/clothing/under/flappers + name = "flappers" + desc = "Nothing like the roarin' '20s, flapping the night away on the dance floor." + icon_state = "flapper" + item_state = "flapper" + item_color = "flapper" + +/obj/item/clothing/under/mafia + name = "mafia outfit" + desc = "The business of the mafia is business." + icon_state = "mafia" + item_state = "mafia" + item_color = "mafia" + +/obj/item/clothing/under/mafia/vest + name = "mafia vest" + desc = "Extreme problems often require extreme solutions." + icon_state = "mafiavest" + item_state = "mafiavest" + item_color = "mafiavest" + +/obj/item/clothing/under/mafia/white + name = "white mafia outfit" + desc = "The best defense against the treacherous is treachery." + icon_state = "mafiawhite" + item_state = "mafiawhite" + item_color = "mafiawhite" + +/obj/item/clothing/under/mafia/sue + name = "mafia vest" + desc = "The business is born into." + icon_state = "suevest" + item_state = "suevest" + item_color = "suevest" + +/obj/item/clothing/under/mafia/tan + name = "leather mafia outfit" + desc = "The big drum sounds good only from a distance." + icon_state = "mafiatan" + item_state = "mafiatan" + item_color = "mafiatan" + + +/obj/item/clothing/under/bane + name = "Bane Harness" + desc = "Wear this harness to become the bane of the station." + icon_state = "bane" + item_state = "bane" + item_color = "bane" + +/obj/item/clothing/under/vox + name = "Ripped Jumpsuit" + desc = "A jumpsuit that looks like it's been shredded by some talons. Who could wear this now?" + icon = 'icons/obj/clothing/species/vox/uniforms.dmi' + icon_state = "vgrey" + item_state = "vgrey" + item_color = "vgrey" + +/obj/item/clothing/under/psyjump + name = "Psychic Amp Jumpsuit" + desc = "A suit made of strange materials." + icon_state = "psyamp" + item_state = "psyamp" + item_color = "psyamp" + +/obj/item/clothing/under/rebeloutfit + name = "Rebel Outfit" + desc = "Made in Seattle, 2216." + icon_state = "colin_earle" + item_state = "colin_earle" + item_color = "colin_earle" + +/obj/item/clothing/under/officeruniform + name = "Clown Officer's Uniform" + desc = "For Clown officers, this uniform was designed by the great clown designer Hugo Boss." + icon_state = "officeruniform" + item_color = "officeruniform" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/soldieruniform + name = "Clown Soldier's Uniform" + desc = "For the basic grunt of the Clown army." + icon_state = "soldieruniform" + item_color = "soldieruniform" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/pennywise + name = "Pennywise Costume" + desc = "It's everything you ever were afraid of." + icon_state = "pennywise" + item_color = "pennywise" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/assistantformal + name = "assistant's formal uniform" + desc = "An assistant's formal-wear. Why an assistant needs formal-wear is still unknown." + icon_state = "assistant_formal" + item_state = "gy_suit" + item_color = "assistant_formal" + +/obj/item/clothing/under/blacktango + name = "black tango dress" + desc = "Filled with Latin fire." + icon_state = "black_tango" + item_state = "wcoat" + item_color = "black_tango" + +/obj/item/clothing/under/stripeddress + name = "striped dress" + desc = "Fashion in space." + icon_state = "striped_dress" + item_state = "stripeddress" + item_color = "striped_dress" + +/obj/item/clothing/under/sailordress + name = "sailor dress" + desc = "Formal wear for a leading lady." + icon_state = "sailor_dress" + item_state = "sailordress" + item_color = "sailor_dress" + +/obj/item/clothing/under/redeveninggown + name = "red evening gown" + desc = "Fancy dress for space bar singers." + icon_state = "red_evening_gown" + item_state = "redeveninggown" + item_color = "red_evening_gown" + +/obj/item/clothing/under/suit_jacket/checkered + name = "checkered suit" + desc = "That's a very nice suit you have there. Shame if something were to happen to it, eh?" + icon_state = "checkered_suit" + item_state = "checkered_suit" + item_color = "checkered_suit" + +/obj/item/clothing/under/owl + name = "owl uniform" + desc = "A soft brown jumpsuit made of synthetic feathers and strong conviction." + icon_state = "owl" + item_color = "owl" + +/obj/item/clothing/under/griffin + name = "griffon uniform" + desc = "A soft brown jumpsuit with a white feather collar made of synthetic feathers and a lust for mayhem." + icon_state = "griffin" + item_color = "griffin" + +/obj/item/clothing/under/noble_clothes + name = "noble clothes" + desc = "They fall just short of majestic." + icon_state = "noble_clothes" + item_color = "noble_clothes" + item_state = "noble_clothes" + +/obj/item/clothing/under/contortionist + name = "atmospheric technician's jumpsuit" + desc = "A light jumpsuit useful for squeezing through narrow vents." + icon_state = "atmos" + item_state = "atmos_suit" + item_color = "atmos" + resistance_flags = FIRE_PROOF + +/obj/item/clothing/under/contortionist/equipped(mob/living/carbon/human/user, slot) + if(!user.ventcrawler) + user.ventcrawler = 2 + ..() + +/obj/item/clothing/under/contortionist/dropped(mob/living/carbon/human/user) + if(!user.get_int_organ(/obj/item/organ/internal/heart/gland/ventcrawling)) + user.ventcrawler = 0 + ..() + +/obj/item/clothing/under/contortionist/proc/check_clothing(mob/user as mob) + //Allowed to wear: glasses, shoes, gloves, pockets, mask, and jumpsuit (obviously) + var/list/slot_must_be_empty = list(slot_back,slot_handcuffed,slot_legcuffed,slot_l_hand,slot_r_hand,slot_belt,slot_head,slot_wear_suit) + for(var/slot_id in slot_must_be_empty) + if(user.get_item_by_slot(slot_id)) + to_chat(user,"You can't fit inside while wearing that \the [user.get_item_by_slot(slot_id)].") + return 0 + return 1 + +/obj/item/clothing/under/cursedclown + name = "cursed clown suit" + desc = "It wasn't already?" + icon = 'icons/goonstation/objects/clothing/uniform.dmi' + icon_state = "cursedclown" + item_state = "cclown_uniform" + item_color = "cursedclown" + icon_override = 'icons/goonstation/mob/clothing/uniform.dmi' + lefthand_file = 'icons/goonstation/mob/inhands/clothing_lefthand.dmi' + righthand_file = 'icons/goonstation/mob/inhands/clothing_righthand.dmi' + flags = NODROP + resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF + flags_size = ONESIZEFITSALL + has_sensor = 0 // HUNKE + +/obj/item/clothing/under/victdress + name = "black victorian dress" + desc = "A victorian style dress, fancy!" + icon_state = "victorianblackdress" + item_state = "victorianblackdress" + item_color = "victorianblackdress" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/victdress/red + name = "red victorian dress" + icon_state = "victorianreddress" + item_state = "victorianreddress" + item_color = "victorianreddress" + +/obj/item/clothing/under/victsuit + name = "victorian suit" + desc = "A victorian style suit, fancy!" + icon_state = "victorianvest" + item_state = "victorianvest" + item_color = "victorianvest" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/victsuit/redblk + name = "red and black victorian suit" + icon_state = "victorianblred" + item_state = "victorianblred" + item_color = "victorianblred" + +/obj/item/clothing/under/victsuit/red + name = "red victorian suit" + icon_state = "victorianredvest" + item_state = "victorianredvest" + item_color = "victorianredvest" + +/obj/item/clothing/under/medigown + name = "medical gown" + desc = "a flimsy examination gown, the back ties never close." + icon_state = "medicalgown" + item_state = "medicalgown" + item_color = "medicalgown" + body_parts_covered = UPPER_TORSO|LOWER_TORSO + +/obj/item/clothing/under/burial + name = "burial garments" + desc = "Traditional burial garments from the early 22nd century." + icon_state = "burial" + item_state = "burial" + item_color = "burial" + +/obj/item/clothing/under/redhawaiianshirt + name = "red hawaiian shirt" + desc = "a floral shirt worn to most vacation destinations." + icon_state = "hawaiianred" + item_state = "hawaiianred" + item_color = "hawaiianred" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/pinkhawaiianshirt + name = "pink hawaiian shirt" + desc = "a pink floral shirt the material feels cool and comfy." + icon_state = "hawaiianpink" + item_state = "hawaiianpink" + item_color = "hawaiianpink" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/orangehawaiianshirt + name = "orange hawaiian shirt" + desc = "a orange floral shirt for a relaxing day in space." + icon_state = "hawaiianorange" + item_state = "hawaiianorange" + item_color = "hawaiianorange" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/bluehawaiianshirt + name = "blue hawaiian shirt" + desc = "a blue floral shirt it has a oddly colored pink flower on it." + icon_state = "hawaiianblue" + item_state = "hawaiianblue" + item_color = "hawaiianblue" + flags_size = ONESIZEFITSALL + +/obj/item/clothing/under/misc/durathread + name = "durathread jumpsuit" + desc = "A jumpsuit made from durathread, its resilient fibres provide some protection to the wearer." + icon_state = "durathread" + item_state = "durathread" + item_color = "durathread" + armor = list(melee = 10, bullet = 0, laser = 10, energy = 0, bomb = 5, bio = 0, rad = 0, fire = 0, acid = 0) + +/obj/item/clothing/under/cuban_suit + name = "rhumba outfit" + desc = "A satin shirt and high-waisted pants, worn by dancers in the Rhumba style. It smells oddly like... sulfur?" + icon_state = "cuban_suit" + item_state = "cuban_suit" + item_color = "cuban_suit" diff --git a/code/modules/clothing/under/pants.dm b/code/modules/clothing/under/pants.dm index b52c1eceb7c..735c681ca5f 100644 --- a/code/modules/clothing/under/pants.dm +++ b/code/modules/clothing/under/pants.dm @@ -89,4 +89,4 @@ name = "camo pants" desc = "A pair of woodland camouflage pants. Probably not the best choice for a space station." icon_state = "camopants" - item_color = "camopants" \ No newline at end of file + item_color = "camopants" diff --git a/code/modules/clothing/under/shorts.dm b/code/modules/clothing/under/shorts.dm index 13008c4ff6d..3cd2ddef84c 100644 --- a/code/modules/clothing/under/shorts.dm +++ b/code/modules/clothing/under/shorts.dm @@ -1,26 +1,26 @@ -/obj/item/clothing/under/shorts - name = "athletic shorts" - desc = "95% Polyester, 5% Spandex!" - gender = PLURAL - body_parts_covered = LOWER_TORSO - displays_id = 0 - -/obj/item/clothing/under/shorts/red - icon_state = "redshorts" - item_color = "redshorts" - -/obj/item/clothing/under/shorts/green - icon_state = "greenshorts" - item_color = "greenshorts" - -/obj/item/clothing/under/shorts/blue - icon_state = "blueshorts" - item_color = "blueshorts" - -/obj/item/clothing/under/shorts/black - icon_state = "blackshorts" - item_color = "blackshorts" - -/obj/item/clothing/under/shorts/grey - icon_state = "greyshorts" - item_color = "greyshorts" \ No newline at end of file +/obj/item/clothing/under/shorts + name = "athletic shorts" + desc = "95% Polyester, 5% Spandex!" + gender = PLURAL + body_parts_covered = LOWER_TORSO + displays_id = 0 + +/obj/item/clothing/under/shorts/red + icon_state = "redshorts" + item_color = "redshorts" + +/obj/item/clothing/under/shorts/green + icon_state = "greenshorts" + item_color = "greenshorts" + +/obj/item/clothing/under/shorts/blue + icon_state = "blueshorts" + item_color = "blueshorts" + +/obj/item/clothing/under/shorts/black + icon_state = "blackshorts" + item_color = "blackshorts" + +/obj/item/clothing/under/shorts/grey + icon_state = "greyshorts" + item_color = "greyshorts" diff --git a/code/modules/clothing/under/syndicate.dm b/code/modules/clothing/under/syndicate.dm index 6778138d30f..441cf8d9420 100644 --- a/code/modules/clothing/under/syndicate.dm +++ b/code/modules/clothing/under/syndicate.dm @@ -1,26 +1,26 @@ -/obj/item/clothing/under/syndicate - name = "tactical turtleneck" - desc = "A non-descript and slightly suspicious looking turtleneck with digital camouflage cargo pants." - icon_state = "syndicate" - item_state = "bl_suit" - item_color = "syndicate" - has_sensor = 0 - armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 40) - -/obj/item/clothing/under/syndicate/combat - name = "combat turtleneck" - -/obj/item/clothing/under/syndicate/tacticool - name = "tacticool turtleneck" - desc = "Just looking at it makes you want to buy an SKS, go into the woods, and -operate-." - icon_state = "tactifool" - item_state = "bl_suit" - item_color = "tactifool" - armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 40) - -/obj/item/clothing/under/syndicate/sniper - name = "Tactical turtleneck suit" - desc = "A double seamed tactical turtleneck disguised as a civilian grade silk suit. Intended for the most formal operator. The collar is really sharp." - icon_state = "really_black_suit" - item_state = "bl_suit" - item_color = "black_suit" \ No newline at end of file +/obj/item/clothing/under/syndicate + name = "tactical turtleneck" + desc = "A non-descript and slightly suspicious looking turtleneck with digital camouflage cargo pants." + icon_state = "syndicate" + item_state = "bl_suit" + item_color = "syndicate" + has_sensor = 0 + armor = list("melee" = 10, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 40) + +/obj/item/clothing/under/syndicate/combat + name = "combat turtleneck" + +/obj/item/clothing/under/syndicate/tacticool + name = "tacticool turtleneck" + desc = "Just looking at it makes you want to buy an SKS, go into the woods, and -operate-." + icon_state = "tactifool" + item_state = "bl_suit" + item_color = "tactifool" + armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 40) + +/obj/item/clothing/under/syndicate/sniper + name = "Tactical turtleneck suit" + desc = "A double seamed tactical turtleneck disguised as a civilian grade silk suit. Intended for the most formal operator. The collar is really sharp." + icon_state = "really_black_suit" + item_state = "bl_suit" + item_color = "black_suit" diff --git a/code/modules/crafting/craft.dm b/code/modules/crafting/craft.dm index 391338fc181..40a8fe5f893 100644 --- a/code/modules/crafting/craft.dm +++ b/code/modules/crafting/craft.dm @@ -89,7 +89,8 @@ /datum/personal_crafting/proc/get_surroundings(mob/user) . = list() - .["other"] = list() + .["other"] = list() //paths go in here + .["toolsother"] = list() // items go in here for(var/obj/item/I in get_environment(user)) if(I.flags_2 & HOLOGRAM_2) continue @@ -102,47 +103,78 @@ if(RC.is_drainable()) for(var/datum/reagent/A in RC.reagents.reagent_list) .["other"][A.type] += A.volume - .["other"][I.type] += 1 + .["other"][I.type] += 1 + .["toolsother"][I] += 1 /datum/personal_crafting/proc/check_tools(mob/user, datum/crafting_recipe/R, list/contents) - if(!R.tools.len) + if(!R.tools.len) //does not run if no tools are needed return TRUE var/list/possible_tools = list() - for(var/obj/item/I in user.contents) + var/list/tools_used = list() + for(var/obj/item/I in user.contents) //searchs the inventory of the mob if(istype(I, /obj/item/storage)) for(var/obj/item/SI in I.contents) - possible_tools += SI.type - possible_tools += I.type - possible_tools |= contents["other"] + if(SI.tool_behaviour) //filters for tool behaviours + possible_tools += SI + if(I.tool_behaviour) + possible_tools += I - main_loop: + possible_tools |= contents["toolsother"] // this add contents to possible_tools + main_loop: // checks if all tools found are usable with the recipe for(var/A in R.tools) - for(var/I in possible_tools) + for(var/obj/item/I in possible_tools) + if(A == I.tool_behaviour) + tools_used += I + continue main_loop + return FALSE + for(var/obj/item/T in tools_used) + if(!T.tool_start_check(user, 0)) //Check if all our tools are valid for their use + return FALSE + return TRUE + +/datum/personal_crafting/proc/check_pathtools(mob/user, datum/crafting_recipe/R, list/contents) + if(!R.pathtools.len) //does not run if no tools are needed + return TRUE + var/list/other_possible_tools = list() + for(var/obj/item/I in user.contents) // searchs the inventory of the mob + if(istype(I, /obj/item/storage)) + for(var/obj/item/SI in I.contents) + other_possible_tools += SI.type // filters type paths + other_possible_tools += I.type + + other_possible_tools |= contents["other"] // this adds contents to the other_possible_tools + main_loop: // checks if all tools found are usable with the recipe + for(var/A in R.pathtools) + for(var/I in other_possible_tools) if(ispath(I,A)) continue main_loop return FALSE return TRUE -/datum/personal_crafting/proc/construct_item(mob/user, datum/crafting_recipe/R) +/datum/personal_crafting/proc/construct_item(mob/user, datum/crafting_recipe/R) var/list/contents = get_surroundings(user) var/send_feedback = 1 if(check_contents(R, contents)) if(check_tools(user, R, contents)) - if(do_after(user, R.time, target = user)) - contents = get_surroundings(user) - if(!check_contents(R, contents)) - return ", missing component." - if(!check_tools(user, R, contents)) - return ", missing tool." - var/list/parts = del_reqs(R, user) - var/atom/movable/I = new R.result (get_turf(user.loc)) - I.CheckParts(parts, R) - if(isitem(I)) - user.put_in_hands(I) - if(send_feedback) - feedback_add_details("object_crafted","[I.type]") - return 0 - return "." + if(check_pathtools(user, R, contents)) + if(do_after(user, R.time, target = user)) + contents = get_surroundings(user) + if(!check_contents(R, contents)) + return ", missing component." + if(!check_tools(user, R, contents)) + return ", missing tool." + if(!check_pathtools(user, R, contents)) + return ", missing tool." + var/list/parts = del_reqs(R, user) + var/atom/movable/I = new R.result (get_turf(user.loc)) + I.CheckParts(parts, R) + if(isitem(I)) + user.put_in_hands(I) + if(send_feedback) + feedback_add_details("object_crafted","[I.type]") + return 0 + return "." + return ", missing tool." return ", missing tool." return ", missing component." @@ -407,12 +439,15 @@ catalyst_text = replacetext(catalyst_text, ",", "", -1) data["catalyst_text"] = catalyst_text - for(var/a in R.tools) + for(var/a in R.pathtools) if(ispath(a, /obj/item)) var/obj/item/b = a tool_text += " [initial(b.name)]," else tool_text += " [a]," + for(var/a in R.tools) + var/b = a + tool_text += " [b]," tool_text = replacetext(tool_text, ",", "", -1) data["tool_text"] = tool_text @@ -423,4 +458,4 @@ /datum/mind/proc/teach_crafting_recipe(R) if(!learned_recipes) learned_recipes = list() - learned_recipes |= R \ No newline at end of file + learned_recipes |= R diff --git a/code/modules/crafting/recipes.dm b/code/modules/crafting/recipes.dm index b49e20f1302..ec334979cd2 100644 --- a/code/modules/crafting/recipes.dm +++ b/code/modules/crafting/recipes.dm @@ -3,7 +3,8 @@ var/reqs[] = list() //type paths of items consumed associated with how many are needed var/blacklist[] = list() //type paths of items explicitly not allowed as an ingredient var/result //type path of item resulting from this craft - var/tools[] = list() //type paths of items needed but not consumed + var/tools[] = list() //tool behaviours of items needed but not consumed + var/pathtools[] = list() //type paths of items needed but not consumed var/time = 30 //time in deciseconds var/parts[] = list() //type paths of items that will be placed in the result var/chem_catalysts[] = list() //like tools but for reagents @@ -65,7 +66,7 @@ /obj/item/gun/energy/gun/advtaser = 1, /obj/item/stock_parts/cell = 1, /obj/item/assembly/prox_sensor = 1) - tools = list(/obj/item/weldingtool, /obj/item/screwdriver) + tools = list(TOOL_WELDER, TOOL_SCREWDRIVER) time = 60 category = CAT_ROBOT @@ -77,7 +78,7 @@ /obj/item/melee/baton = 1, /obj/item/assembly/prox_sensor = 1, /obj/item/robot_parts/r_arm = 1) - tools = list(/obj/item/weldingtool) + tools = list(TOOL_WELDER) time = 60 category = CAT_ROBOT @@ -90,7 +91,7 @@ /obj/item/assembly/prox_sensor = 1, /obj/item/robot_parts/r_arm = 2, /obj/item/robot_parts/l_arm = 2) - tools = list(/obj/item/weldingtool) + tools = list(TOOL_WELDER) time = 120 category = CAT_ROBOT @@ -142,7 +143,7 @@ /obj/item/stack/rods = 1) parts = list(/obj/item/assembly/igniter = 1, /obj/item/weldingtool = 1) - tools = list(/obj/item/screwdriver) + tools = list(TOOL_SCREWDRIVER) time = 10 category = CAT_WEAPONRY subcategory = CAT_WEAPON @@ -153,7 +154,7 @@ reqs = list(/obj/item/ammo_casing/shotgun/techshell = 1, /obj/item/rcd_ammo = 1, /obj/item/stock_parts/manipulator = 2) - tools = list(/obj/item/screwdriver) + tools = list(TOOL_SCREWDRIVER) time = 5 category = CAT_WEAPONRY subcategory = CAT_AMMO @@ -164,7 +165,7 @@ reqs = list(/obj/item/ammo_casing/shotgun/techshell = 1, /obj/item/stock_parts/capacitor/adv = 2, /obj/item/stock_parts/micro_laser/ultra = 1) - tools = list(/obj/item/screwdriver) + tools = list(TOOL_SCREWDRIVER) time = 5 category = CAT_WEAPONRY subcategory = CAT_AMMO @@ -174,7 +175,7 @@ result = /obj/item/ammo_casing/shotgun/incendiary/dragonsbreath reqs = list(/obj/item/ammo_casing/shotgun/techshell = 1, /datum/reagent/phosphorus = 5,) - tools = list(/obj/item/screwdriver) + tools = list(TOOL_SCREWDRIVER) time = 5 category = CAT_WEAPONRY subcategory = CAT_AMMO @@ -186,7 +187,7 @@ /datum/reagent/glycerol = 5, /datum/reagent/acid = 5, /datum/reagent/acid/facid = 5,) - tools = list(/obj/item/screwdriver) + tools = list(TOOL_SCREWDRIVER) time = 5 category = CAT_WEAPONRY subcategory = CAT_AMMO @@ -197,7 +198,7 @@ reqs = list(/obj/item/ammo_casing/shotgun/techshell = 1, /obj/item/stock_parts/micro_laser/ultra = 1, /obj/item/stock_parts/subspace/crystal = 1) - tools = list(/obj/item/screwdriver) + tools = list(TOOL_SCREWDRIVER) time = 5 category = CAT_WEAPONRY subcategory = CAT_AMMO @@ -209,7 +210,7 @@ /obj/item/stack/sheet/metal = 1, /obj/item/stack/cable_coil = 1, /datum/reagent/fuel = 10) - tools = list(/obj/item/screwdriver) + tools = list(TOOL_SCREWDRIVER) time = 5 category = CAT_WEAPONRY subcategory = CAT_AMMO @@ -220,7 +221,7 @@ reqs = list(/obj/item/ammo_casing/shotgun/improvised = 1, /datum/reagent/blackpowder = 10, /datum/reagent/plasma_dust = 20) - tools = list(/obj/item/screwdriver) + tools = list(TOOL_SCREWDRIVER) time = 5 category = CAT_WEAPONRY subcategory = CAT_AMMO @@ -231,7 +232,7 @@ reqs = list(/obj/item/ammo_casing/shotgun/techshell = 1, /obj/item/stock_parts/capacitor/adv = 1, /obj/item/stock_parts/micro_laser/high = 1) - tools = list(/obj/item/screwdriver) + tools = list(TOOL_SCREWDRIVER) time = 5 category = CAT_WEAPONRY subcategory = CAT_AMMO @@ -243,7 +244,7 @@ /obj/item/pipe = 1, /obj/item/weaponcrafting/stock = 1, /obj/item/stack/packageWrap = 5,) - tools = list(/obj/item/screwdriver) + tools = list(TOOL_SCREWDRIVER) time = 100 category = CAT_WEAPONRY subcategory = CAT_WEAPON @@ -254,7 +255,7 @@ reqs = list(/obj/item/circular_saw = 1, /obj/item/stack/cable_coil = 1, /obj/item/stack/sheet/plasteel = 1) - tools = list(/obj/item/weldingtool) + tools = list(TOOL_WELDER) time = 50 category = CAT_WEAPONRY subcategory = CAT_WEAPON @@ -307,7 +308,7 @@ time = 15 reqs = list(/obj/item/stack/sheet/wood = 1, /obj/item/stack/cable_coil = 5) - tools = list(/obj/item/kitchen/knife) // Gotta carve the wood into handles + pathtools = list(/obj/item/kitchen/knife) // Gotta carve the wood into handles category = CAT_WEAPONRY subcategory = CAT_WEAPON @@ -316,7 +317,7 @@ result = /obj/item/arrow/rod time = 5 reqs = list(/obj/item/stack/rods = 1) - tools = list(/obj/item/weldingtool) + tools = list(TOOL_WELDER) category = CAT_WEAPONRY subcategory = CAT_AMMO @@ -328,8 +329,7 @@ /obj/item/stack/cable_coil = 10, /obj/item/stack/sheet/plastic = 3, /obj/item/stack/sheet/wood = 5) - tools = list(/obj/item/weldingtool, - /obj/item/screwdriver) + tools = list(TOOL_WELDER, TOOL_SCREWDRIVER) category = CAT_WEAPONRY subcategory = CAT_WEAPON @@ -354,7 +354,7 @@ result = /obj/item/stack/tile/carpet/black time = 20 reqs = list(/obj/item/stack/tile/carpet = 1) - tools = list(/obj/item/toy/crayon) + pathtools = list(/obj/item/toy/crayon) category = CAT_MISC /datum/crafting_recipe/showercurtain @@ -525,7 +525,7 @@ reqs = list(/obj/item/stack/sheet/plasteel = 3, /obj/item/stack/sheet/wood = 20, /obj/item/stack/cable_coil = 10) - tools = list(/obj/item/screwdriver, /obj/item/wrench, /obj/item/weldingtool) + tools = list(TOOL_SCREWDRIVER, TOOL_WRENCH, TOOL_WELDER) category = CAT_MISC /datum/crafting_recipe/drill @@ -537,7 +537,7 @@ /obj/item/stock_parts/cell = 1, /obj/item/stack/rods = 2, /obj/item/assembly/timer = 1) - tools = list(/obj/item/screwdriver, /obj/item/wrench) + tools = list(TOOL_SCREWDRIVER, TOOL_WRENCH) category = CAT_MISC /datum/crafting_recipe/d_drill @@ -549,7 +549,7 @@ /obj/item/stock_parts/cell = 1, /obj/item/stack/rods = 2, /obj/item/assembly/prox_sensor = 1) // Not a timer because the system sees a diamond drill as a drill too, letting you make both otherwise. - tools = list(/obj/item/screwdriver, /obj/item/wrench) + tools = list(TOOL_SCREWDRIVER, TOOL_WRENCH) category = CAT_MISC /datum/crafting_recipe/faketoolbox @@ -559,7 +559,7 @@ reqs = list(/datum/reagent/paint/red = 10, /datum/reagent/paint/black = 30, /obj/item/storage/toolbox = 1) //Paint in reagents so it doesnt take the container up, yet still take it from the beaker - tools = list(/obj/item/reagent_containers/glass/rag = 1) //need something to paint with it + pathtools = list(/obj/item/reagent_containers/glass/rag = 1) //need something to paint with it category = CAT_MISC /datum/crafting_recipe/snowman @@ -578,152 +578,153 @@ result = /obj/item/decorations/sticky_decorations/flammable/heart reqs = list(/obj/item/paper = 1, /obj/item/stack/tape_roll = 1) - tools = list(/obj/item/wirecutters, - /obj/item/toy/crayon/red)//cutters act as makeshift scissors. I doubt the barber wants to have their scissors stolen when somone wants to decorate + tools = list(TOOL_WIRECUTTER) //cutters act as makeshift scissors. I doubt the barber wants to have their scissors stolen when somone wants to decorate + pathtools = list(/obj/item/toy/crayon/red) category = CAT_DECORATIONS subcategory = CAT_DECORATION /datum/crafting_recipe/paper_craft/single_eye name = "Paper Eye" result = /obj/item/decorations/sticky_decorations/flammable/singleeye - tools = list(/obj/item/wirecutters, - /obj/item/pen, - /obj/item/toy/crayon/blue) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/pen, /obj/item/toy/crayon/blue) category = CAT_DECORATIONS subcategory = CAT_DECORATION /datum/crafting_recipe/paper_craft/googlyeyes name = "Paper Googly Eye" result = /obj/item/decorations/sticky_decorations/flammable/googlyeyes - tools = list(/obj/item/wirecutters, - /obj/item/pen) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/pen) category = CAT_DECORATIONS subcategory = CAT_DECORATION /datum/crafting_recipe/paper_craft/clock name = "Paper Clock" result = /obj/item/decorations/sticky_decorations/flammable/paperclock - tools = list(/obj/item/wirecutters, - /obj/item/pen) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/pen) category = CAT_DECORATIONS subcategory = CAT_DECORATION /datum/crafting_recipe/paper_craft/jack_o_lantern name = "Paper Jack o'Lantern" result = /obj/item/decorations/sticky_decorations/flammable/jack_o_lantern - tools = list(/obj/item/wirecutters, - /obj/item/pen, - /obj/item/toy/crayon/orange, - /obj/item/toy/crayon/green)//pen ink is black + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/pen, + /obj/item/toy/crayon/orange, + /obj/item/toy/crayon/green)//pen ink is black category = CAT_DECORATIONS subcategory = CAT_HOLIDAY /datum/crafting_recipe/paper_craft/ghost name = "Paper Ghost" result = /obj/item/decorations/sticky_decorations/flammable/ghost - tools = list(/obj/item/wirecutters, - /obj/item/pen)//it's white paper why need a white crayon? + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/pen)//it's white paper why need a white crayon? category = CAT_DECORATIONS subcategory = CAT_HOLIDAY /datum/crafting_recipe/paper_craft/spider name = "Paper Spider" result = /obj/item/decorations/sticky_decorations/flammable/spider - tools = list(/obj/item/wirecutters, - /obj/item/pen, - /obj/item/toy/crayon/red) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/pen, + /obj/item/toy/crayon/red) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY /datum/crafting_recipe/paper_craft/spiderweb name = "Paper Spiderweb" result = /obj/item/decorations/sticky_decorations/flammable/spiderweb - tools = list(/obj/item/wirecutters) + tools = list(TOOL_WIRECUTTER) + pathtools = list() category = CAT_DECORATIONS subcategory = CAT_HOLIDAY /datum/crafting_recipe/paper_craft/skull name = "Paper Skull" result = /obj/item/decorations/sticky_decorations/flammable/skull - tools = list(/obj/item/wirecutters, - /obj/item/pen) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/pen) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY /datum/crafting_recipe/paper_craft/skeleton name = "Paper Skeleton" result = /obj/item/decorations/sticky_decorations/flammable/skeleton - tools = list(/obj/item/wirecutters, - /obj/item/pen) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/pen) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY /datum/crafting_recipe/paper_craft/cauldron name = "Paper Cauldron" result = /obj/item/decorations/sticky_decorations/flammable/cauldron - tools = list(/obj/item/wirecutters, - /obj/item/pen) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/pen) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY /datum/crafting_recipe/paper_craft/snowman name = "Paper Snowman" result = /obj/item/decorations/sticky_decorations/flammable/snowman - tools = list(/obj/item/wirecutters, - /obj/item/pen, - /obj/item/toy/crayon/orange) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/pen, + /obj/item/toy/crayon/orange) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY /datum/crafting_recipe/paper_craft/christmas_stocking name = "Paper Christmas Stocking" result = /obj/item/decorations/sticky_decorations/flammable/christmas_stocking - tools = list(/obj/item/wirecutters, - /obj/item/toy/crayon/red) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/toy/crayon/red) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY /datum/crafting_recipe/paper_craft/christmas_tree name = "Paper Christmas Tree" result = /obj/item/decorations/sticky_decorations/flammable/christmas_tree - tools = list(/obj/item/wirecutters, - /obj/item/toy/crayon/red, - /obj/item/toy/crayon/yellow, - /obj/item/toy/crayon/blue, - /obj/item/toy/crayon/green) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/toy/crayon/red, + /obj/item/toy/crayon/yellow, + /obj/item/toy/crayon/blue, + /obj/item/toy/crayon/green) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY /datum/crafting_recipe/paper_craft/snowflake name = "Paper Snowflake" result = /obj/item/decorations/sticky_decorations/flammable/snowflake - tools = list(/obj/item/wirecutters) + tools = list(TOOL_WIRECUTTER) + pathtools = list() category = CAT_DECORATIONS subcategory = CAT_HOLIDAY /datum/crafting_recipe/paper_craft/candy_cane name = "Paper Candy Cane" result = /obj/item/decorations/sticky_decorations/flammable/candy_cane - tools = list(/obj/item/wirecutters, - /obj/item/toy/crayon/red) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/toy/crayon/red) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY /datum/crafting_recipe/paper_craft/mistletoe name = "Paper Mistletoe" result = /obj/item/decorations/sticky_decorations/flammable/mistletoe - tools = list(/obj/item/wirecutters, - /obj/item/toy/crayon/red, - /obj/item/toy/crayon/green) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/toy/crayon/red, + /obj/item/toy/crayon/green) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY /datum/crafting_recipe/paper_craft/holly name = "Paper Holly" result = /obj/item/decorations/sticky_decorations/flammable/holly - tools = list(/obj/item/wirecutters, - /obj/item/toy/crayon/red, - /obj/item/toy/crayon/green) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/toy/crayon/red, + /obj/item/toy/crayon/green) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY @@ -733,7 +734,8 @@ result = /obj/item/decorations/sticky_decorations/flammable/tinsel reqs = list(/obj/item/paper = 1, /obj/item/stack/tape_roll = 2) - tools = list(/obj/item/wirecutters) + tools = list(TOOL_WIRECUTTER) + pathtools = list() category = CAT_DECORATIONS subcategory = CAT_HOLIDAY @@ -743,8 +745,8 @@ result = /obj/item/decorations/sticky_decorations/flammable/tinsel/red reqs = list(/obj/item/paper = 1, /obj/item/stack/tape_roll = 2) - tools = list(/obj/item/wirecutters, - /obj/item/toy/crayon/red) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/toy/crayon/red) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY @@ -754,8 +756,8 @@ result = /obj/item/decorations/sticky_decorations/flammable/tinsel/blue reqs = list(/obj/item/paper = 1, /obj/item/stack/tape_roll = 2) - tools = list(/obj/item/wirecutters, - /obj/item/toy/crayon/blue) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/toy/crayon/blue) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY @@ -765,8 +767,8 @@ result = /obj/item/decorations/sticky_decorations/flammable/tinsel/yellow reqs = list(/obj/item/paper = 1, /obj/item/stack/tape_roll = 2) - tools = list(/obj/item/wirecutters, - /obj/item/toy/crayon/yellow) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/toy/crayon/yellow) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY @@ -776,8 +778,8 @@ result = /obj/item/decorations/sticky_decorations/flammable/tinsel/purple reqs = list(/obj/item/paper = 1, /obj/item/stack/tape_roll = 2) - tools = list(/obj/item/wirecutters, - /obj/item/toy/crayon/purple) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/toy/crayon/purple) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY @@ -787,8 +789,8 @@ result = /obj/item/decorations/sticky_decorations/flammable/tinsel/green reqs = list(/obj/item/paper = 1, /obj/item/stack/tape_roll = 2) - tools = list(/obj/item/wirecutters, - /obj/item/toy/crayon/green) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/toy/crayon/green) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY @@ -798,8 +800,8 @@ result = /obj/item/decorations/sticky_decorations/flammable/tinsel/orange reqs = list(/obj/item/paper = 1, /obj/item/stack/tape_roll = 2) - tools = list(/obj/item/wirecutters, - /obj/item/toy/crayon/orange) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/toy/crayon/orange) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY @@ -809,8 +811,8 @@ result = /obj/item/decorations/sticky_decorations/flammable/tinsel/black reqs = list(/obj/item/paper = 1, /obj/item/stack/tape_roll = 2) - tools = list(/obj/item/wirecutters, - /obj/item/pen) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/pen) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY @@ -820,17 +822,17 @@ result = /obj/item/decorations/sticky_decorations/flammable/tinsel/halloween reqs = list(/obj/item/paper = 1, /obj/item/stack/tape_roll = 2) - tools = list(/obj/item/wirecutters, - /obj/item/pen, - /obj/item/toy/crayon/orange) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/pen, + /obj/item/toy/crayon/orange) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY /datum/crafting_recipe/paper_craft/arrowed_heart name = "Paper Arrowed Heart" result = /obj/item/decorations/sticky_decorations/flammable/arrowed_heart - tools = list(/obj/item/wirecutters, - /obj/item/toy/crayon/red) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/toy/crayon/red) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY @@ -840,24 +842,24 @@ reqs = list(/obj/item/paper = 1, /obj/item/stack/tape_roll = 2, /obj/item/stack/cable_coil = 2) - tools = list(/obj/item/wirecutters, - /obj/item/toy/crayon/red) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/toy/crayon/red) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY /datum/crafting_recipe/paper_craft/four_leaf_clover name = "Paper Four Leaf Clover" result = /obj/item/decorations/sticky_decorations/flammable/four_leaf_clover - tools = list(/obj/item/wirecutters, - /obj/item/toy/crayon/green) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/toy/crayon/green) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY /datum/crafting_recipe/paper_craft/pot_of_gold name = "Paper Pot of Gold" result = /obj/item/decorations/sticky_decorations/flammable/pot_of_gold - tools = list(/obj/item/wirecutters, - /obj/item/pen, + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/pen, /obj/item/toy/crayon/red, /obj/item/toy/crayon/yellow, /obj/item/toy/crayon/orange, @@ -871,8 +873,8 @@ name = "Paper Leprechaun Hat" time = 10 result = /obj/item/decorations/sticky_decorations/flammable/leprechaun_hat - tools = list(/obj/item/wirecutters, - /obj/item/pen, + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/pen, /obj/item/toy/crayon/yellow, /obj/item/toy/crayon/green) category = CAT_DECORATIONS @@ -881,8 +883,8 @@ /datum/crafting_recipe/paper_craft/easter_bunny name = "Paper Easter Bunny" result = /obj/item/decorations/sticky_decorations/flammable/easter_bunny - tools = list(/obj/item/wirecutters, - /obj/item/pen, + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/pen, /obj/item/toy/crayon/blue, /obj/item/toy/crayon/purple) category = CAT_DECORATIONS @@ -891,40 +893,40 @@ /datum/crafting_recipe/paper_craft/easter_egg_blue name = "Blue Paper Easter Egg" result = /obj/item/decorations/sticky_decorations/flammable/easter_egg - tools = list(/obj/item/wirecutters, - /obj/item/toy/crayon/blue) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/toy/crayon/blue) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY /datum/crafting_recipe/paper_craft/easter_egg_yellow name = "Yellow Paper Easter Egg" result = /obj/item/decorations/sticky_decorations/flammable/easter_egg/yellow - tools = list(/obj/item/wirecutters, - /obj/item/toy/crayon/yellow) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/toy/crayon/yellow) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY /datum/crafting_recipe/paper_craft/easter_egg_red name = "Red Paper Easter Egg" result = /obj/item/decorations/sticky_decorations/flammable/easter_egg/red - tools = list(/obj/item/wirecutters, - /obj/item/toy/crayon/red) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/toy/crayon/red) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY /datum/crafting_recipe/paper_craft/easter_egg_purple name = "Purple Paper Easter Egg" result = /obj/item/decorations/sticky_decorations/flammable/easter_egg/purple - tools = list(/obj/item/wirecutters, - /obj/item/toy/crayon/purple) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/toy/crayon/purple) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY /datum/crafting_recipe/paper_craft/easter_egg_orange name = "Orange Paper Easter Egg" result = /obj/item/decorations/sticky_decorations/flammable/easter_egg/orange - tools = list(/obj/item/wirecutters, - /obj/item/toy/crayon/orange) + tools = list(TOOL_WIRECUTTER) + pathtools = list(/obj/item/toy/crayon/orange) category = CAT_DECORATIONS subcategory = CAT_HOLIDAY @@ -934,7 +936,7 @@ result = /obj/structure/decorative_structures/metal/statue/metal_angel reqs = list(/obj/item/stack/sheet/metal = 10, /obj/item/stack/sheet/mineral/gold = 6) - tools = list(/obj/item/weldingtool) + tools = list(TOOL_WELDER) category = CAT_DECORATIONS subcategory = CAT_LARGE_DECORATIONS @@ -945,7 +947,7 @@ reqs = list(/obj/item/stack/sheet/metal = 10, /obj/item/stack/sheet/mineral/plasma = 3, /obj/item/stack/sheet/mineral/gold = 8) - tools = list(/obj/item/weldingtool) + tools = list(TOOL_WELDER) category = CAT_DECORATIONS subcategory = CAT_LARGE_DECORATIONS @@ -955,7 +957,7 @@ result = /obj/structure/decorative_structures/metal/statue/sun reqs = list(/obj/item/stack/sheet/metal = 6, /obj/item/stack/sheet/mineral/gold = 4) - tools = list(/obj/item/weldingtool) + tools = list(TOOL_WELDER) category = CAT_DECORATIONS subcategory = CAT_LARGE_DECORATIONS @@ -966,7 +968,7 @@ reqs = list(/obj/item/stack/sheet/metal = 6, /obj/item/stack/sheet/mineral/silver = 6, /obj/item/stack/sheet/mineral/gold = 4) - tools = list(/obj/item/weldingtool) + tools = list(TOOL_WELDER) category = CAT_DECORATIONS subcategory = CAT_LARGE_DECORATIONS @@ -976,7 +978,7 @@ result = /obj/structure/decorative_structures/metal/statue/tesla reqs = list(/obj/item/stack/sheet/metal = 4, /obj/item/stack/sheet/glass = 8) - tools = list(/obj/item/weldingtool) + tools = list(TOOL_WELDER) category = CAT_DECORATIONS subcategory = CAT_LARGE_DECORATIONS @@ -987,7 +989,7 @@ reqs = list(/obj/item/stack/sheet/metal = 8, /obj/item/stock_parts/cell = 3, /obj/item/stack/cable_coil = 4) - tools = list(/obj/item/weldingtool) + tools = list(TOOL_WELDER) category = CAT_DECORATIONS subcategory = CAT_LARGE_DECORATIONS @@ -1010,9 +1012,9 @@ /obj/item/stack/rods = 4, /obj/item/stock_parts/cell = 1, /obj/item/stack/cable_coil = 4)//thing is a wireframe construct with an electro magnetic hover field - tools = list(/obj/item/wirecutters, - /obj/item/pen, - /obj/item/weldingtool, + tools = list(TOOL_WIRECUTTER, + TOOL_WELDER) + pathtools = list(/obj/item/pen, /obj/item/toy/crayon/red) category = CAT_DECORATIONS subcategory = CAT_LARGE_DECORATIONS diff --git a/code/modules/crafting/tailoring.dm b/code/modules/crafting/tailoring.dm index a46e509001c..6973fd04ad3 100644 --- a/code/modules/crafting/tailoring.dm +++ b/code/modules/crafting/tailoring.dm @@ -54,7 +54,7 @@ name = "Security HUDsunglasses" result = /obj/item/clothing/glasses/hud/security/sunglasses time = 20 - tools = list(/obj/item/screwdriver, /obj/item/wirecutters) + tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER) reqs = list(/obj/item/clothing/glasses/hud/security = 1, /obj/item/clothing/glasses/sunglasses = 1, /obj/item/stack/cable_coil = 5) @@ -64,7 +64,7 @@ name = "Security HUD removal" result = /obj/item/clothing/glasses/sunglasses time = 20 - tools = list(/obj/item/screwdriver, /obj/item/wirecutters) + tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER) reqs = list(/obj/item/clothing/glasses/hud/security/sunglasses = 1) category = CAT_CLOTHING @@ -72,7 +72,7 @@ name = "Medical HUDsunglasses" result = /obj/item/clothing/glasses/hud/health/sunglasses time = 20 - tools = list(/obj/item/screwdriver, /obj/item/wirecutters) + tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER) reqs = list(/obj/item/clothing/glasses/hud/health = 1, /obj/item/clothing/glasses/sunglasses = 1, /obj/item/stack/cable_coil = 5) @@ -82,7 +82,7 @@ name = "Medical HUD removal" result = /obj/item/clothing/glasses/sunglasses time = 20 - tools = list(/obj/item/screwdriver, /obj/item/wirecutters) + tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER) reqs = list(/obj/item/clothing/glasses/hud/health/sunglasses = 1) category = CAT_CLOTHING @@ -90,7 +90,7 @@ name = "Diagnostic HUDsunglasses" result = /obj/item/clothing/glasses/hud/diagnostic/sunglasses time = 20 - tools = list(/obj/item/screwdriver, /obj/item/wirecutters) + tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER) reqs = list(/obj/item/clothing/glasses/hud/diagnostic = 1, /obj/item/clothing/glasses/sunglasses = 1, /obj/item/stack/cable_coil = 5) @@ -100,7 +100,7 @@ name = "Diagnostic HUD removal" result = /obj/item/clothing/glasses/sunglasses time = 20 - tools = list(/obj/item/screwdriver, /obj/item/wirecutters) + tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER) reqs = list(/obj/item/clothing/glasses/hud/diagnostic/sunglasses = 1) category = CAT_CLOTHING @@ -108,7 +108,7 @@ name = "Beer Goggles" result = /obj/item/clothing/glasses/sunglasses/reagent time = 20 - tools = list(/obj/item/screwdriver, /obj/item/wirecutters) + tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER) reqs = list(/obj/item/clothing/glasses/science = 1, /obj/item/clothing/glasses/sunglasses = 1, /obj/item/stack/cable_coil = 5) @@ -118,7 +118,7 @@ name = "Beer Goggles removal" result = /obj/item/clothing/glasses/sunglasses time = 20 - tools = list(/obj/item/screwdriver, /obj/item/wirecutters) + tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER) reqs = list(/obj/item/clothing/glasses/sunglasses/reagent = 1) category = CAT_CLOTHING @@ -126,7 +126,7 @@ name = "Ghost Sheet" result = /obj/item/clothing/suit/ghost_sheet time = 5 - tools = list(/obj/item/wirecutters) + tools = list(TOOL_WIRECUTTER) reqs = list(/obj/item/bedsheet = 1) category = CAT_CLOTHING diff --git a/code/modules/customitems/item_defines.dm b/code/modules/customitems/item_defines.dm index 3a0bdb8d795..983ce229d2c 100644 --- a/code/modules/customitems/item_defines.dm +++ b/code/modules/customitems/item_defines.dm @@ -1405,9 +1405,9 @@ icon = 'icons/obj/custom_items.dmi' icon_state = "ssscratches_backpack" -/obj/item/storage/backpack/duffel/fluff/thebrew //Greey: Korala Ice +/obj/item/storage/backpack/fluff/thebrew //Greey: Korala Ice name = "The Brew" - desc = "Amber colored duffle bag resembling a long lost friend, a spirit long forgotten." + desc = "Amber colored backpack resembling a long lost friend, a spirit long forgotten." icon = 'icons/obj/custom_items.dmi' icon_state = "greeyfluff" item_state = "greeyfluff" diff --git a/code/modules/detective_work/footprints_and_rag.dm b/code/modules/detective_work/footprints_and_rag.dm index b3c45c0f4a1..c636c3d01a7 100644 --- a/code/modules/detective_work/footprints_and_rag.dm +++ b/code/modules/detective_work/footprints_and_rag.dm @@ -27,7 +27,7 @@ /obj/item/reagent_containers/glass/rag/attack(atom/target as obj|turf|area, mob/user as mob , flag) if(ismob(target) && target.reagents && reagents.total_volume) user.visible_message("[user] has smothered \the [target] with \the [src]!", "You smother \the [target] with \the [src]!", "You hear some struggling and muffled cries of surprise") - src.reagents.reaction(target, TOUCH) + src.reagents.reaction(target, REAGENT_TOUCH) src.reagents.clear_reagents() return else diff --git a/code/modules/economy/ATM.dm b/code/modules/economy/ATM.dm index eafd038977d..df542964163 100644 --- a/code/modules/economy/ATM.dm +++ b/code/modules/economy/ATM.dm @@ -71,7 +71,7 @@ log transactions playsound(loc, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 50, 1) for(var/obj/item/stack/spacecash/S in T) S.use(S.amount) - authenticated_account.charge(-cash_amount, null, "Credit deposit", terminal_id = machine_id, dest_name = "Terminal") + authenticated_account.charge(-cash_amount, null, "Credit deposit", machine_id, "Terminal") /obj/machinery/atm/proc/reconnect_database() for(var/obj/machinery/computer/account_database/DB in GLOB.machines) diff --git a/code/modules/economy/Accounts_DB.dm b/code/modules/economy/Accounts_DB.dm index 16b1162e074..afb5fbff79a 100644 --- a/code/modules/economy/Accounts_DB.dm +++ b/code/modules/economy/Accounts_DB.dm @@ -5,7 +5,7 @@ var/global/current_date_string desc = "Access transaction logs, account data and all kinds of other financial records." icon_screen = "accounts" density = 1 - req_one_access = list(access_hop, access_captain, access_cent_commander) + req_one_access = list(ACCESS_HOP, ACCESS_CAPTAIN, ACCESS_CENT_COMMANDER) var/receipt_num var/machine_id = "" var/obj/item/card/id/held_card @@ -38,9 +38,9 @@ var/global/current_date_string return 2 if(!held_card) return 0 - if(access_cent_commander in held_card.access) + if(ACCESS_CENT_COMMANDER in held_card.access) return 2 - else if(access_hop in held_card.access || access_captain in held_card.access) + else if(ACCESS_HOP in held_card.access || ACCESS_CAPTAIN in held_card.access) return 1 /obj/machinery/computer/account_database/proc/accounting_letterhead(report_name) diff --git a/code/modules/economy/EFTPOS.dm b/code/modules/economy/EFTPOS.dm index 98d99e667aa..9a5faf8aa36 100644 --- a/code/modules/economy/EFTPOS.dm +++ b/code/modules/economy/EFTPOS.dm @@ -150,7 +150,7 @@ var/obj/item/I = usr.get_active_hand() if(istype(I, /obj/item/card)) var/obj/item/card/id/C = I - if(access_cent_commander in C.access || access_hop in C.access || access_captain in C.access) + if(ACCESS_CENT_COMMANDER in C.access || ACCESS_HOP in C.access || ACCESS_CAPTAIN in C.access) access_code = 0 to_chat(usr, "[bicon(src)]Access code reset to 0.") else if(istype(I, /obj/item/card/emag)) diff --git a/code/modules/economy/Economy.dm b/code/modules/economy/Economy.dm index 57d76e98c5c..eca0e08e359 100644 --- a/code/modules/economy/Economy.dm +++ b/code/modules/economy/Economy.dm @@ -94,4 +94,4 @@ var/setup_economy = 0 weighted_randomevent_locations[D] = D.viable_random_events.len weighted_mundaneevent_locations[D] = D.viable_mundane_events.len - setup_economy = 1 \ No newline at end of file + setup_economy = 1 diff --git a/code/modules/economy/Job_Departments.dm b/code/modules/economy/Job_Departments.dm index 0b851136147..77a938486dd 100644 --- a/code/modules/economy/Job_Departments.dm +++ b/code/modules/economy/Job_Departments.dm @@ -67,4 +67,4 @@ var/list/station_departments = list("Command", "Medical", "Engineering", "Scienc /datum/job/detective/department = "Security" -/datum/job/officer/department = "Security" \ No newline at end of file +/datum/job/officer/department = "Security" diff --git a/code/modules/economy/POS.dm b/code/modules/economy/POS.dm index 047c1636ece..cbf708f64ea 100644 --- a/code/modules/economy/POS.dm +++ b/code/modules/economy/POS.dm @@ -14,7 +14,7 @@ var/global/current_pos_id = 1 var/global/pos_sales = 0 -var/const/RECEIPT_HEADER = {" +#define RECEIPT_HEADER {" - "} - - dat += {" - - pAI Personality Configuration -

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

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

        - - -
        Submit Personality

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

        - "} - dat += "

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

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

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

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

        + + +
        Submit Personality

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

        + "} + dat += "

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

        " + + for(var/datum/paiCandidate/c in available) + dat += {" + + + + + + + + + + + + + + + + + +
        Name:[c.name]
        Description:[c.description]
        Preferred Role:[c.role]
        OOC Comments:[c.comments]
        + + +
        Download [c.name] +
        +
        + "} + + dat += {" + + + "} + + user << browse(dat, "window=findPai") + +/datum/paiController/proc/requestRecruits(var/obj/item/paicard/P, mob/user) + for(var/mob/dead/observer/O in GLOB.player_list) + if(O.client && (ROLE_PAI in O.client.prefs.be_special)) + if(player_old_enough_antag(O.client,ROLE_PAI)) + if(check_recruit(O)) + to_chat(O, "A pAI card activated by [user.real_name] is looking for personalities. (Teleport | Sign Up)") + //question(O.client) + +/datum/paiController/proc/check_recruit(var/mob/dead/observer/O) + if(jobban_isbanned(O, ROLE_PAI) || jobban_isbanned(O,"nonhumandept")) + return 0 + if(!player_old_enough_antag(O.client,ROLE_PAI)) + return 0 + if(cannotPossess(O)) + return 0 + if(!(O in GLOB.respawnable_list)) + return 0 + if(O.client) + return 1 + return 0 + +/datum/paiController/proc/question(var/client/C) + spawn(0) + if(!C) return + asked.Add(C.key) + asked[C.key] = world.time + var/response = alert(C, "Someone is requesting a pAI personality. Would you like to play as a personal AI?", "pAI Request", "Yes", "No", "Never for this round") + if(!C) return //handle logouts that happen whilst the alert is waiting for a response. + if(response == "Yes") + recruitWindow(C.mob) + else if(response == "Never for this round") + var/warning = alert(C, "Are you sure? This action will be undoable and you will need to wait until next round.", "You sure?", "Yes", "No") + if(warning == "Yes") + asked[C.key] = INFINITY + else + question(C) diff --git a/code/modules/mob/living/silicon/pai/say.dm b/code/modules/mob/living/silicon/pai/say.dm index 1f1b365eb4f..750aed1fd5f 100644 --- a/code/modules/mob/living/silicon/pai/say.dm +++ b/code/modules/mob/living/silicon/pai/say.dm @@ -1,16 +1,16 @@ -/mob/living/silicon/pai/say(var/msg) - if(silence_time) - to_chat(src, "Communication circuits remain uninitialized.") - else - ..(msg) - -/mob/living/silicon/pai/get_whisper_loc() - if(loc == card) // currently in its card? - var/atom/movable/whisper_loc = card - if(istype(card.loc, /obj/item/pda)) // Step up 1 level if in a PDA - whisper_loc = card.loc - if(istype(whisper_loc.loc, /mob/living)) - return whisper_loc.loc // allow a pai being held or in pocket to whisper - else - return whisper_loc // allow a pai in its card to whisper - return ..() +/mob/living/silicon/pai/say(var/msg) + if(silence_time) + to_chat(src, "Communication circuits remain uninitialized.") + else + ..(msg) + +/mob/living/silicon/pai/get_whisper_loc() + if(loc == card) // currently in its card? + var/atom/movable/whisper_loc = card + if(istype(card.loc, /obj/item/pda)) // Step up 1 level if in a PDA + whisper_loc = card.loc + if(istype(whisper_loc.loc, /mob/living)) + return whisper_loc.loc // allow a pai being held or in pocket to whisper + else + return whisper_loc // allow a pai in its card to whisper + return ..() diff --git a/code/modules/mob/living/silicon/pai/software.dm b/code/modules/mob/living/silicon/pai/software.dm index 135e1996dfe..70117b4558c 100644 --- a/code/modules/mob/living/silicon/pai/software.dm +++ b/code/modules/mob/living/silicon/pai/software.dm @@ -1,135 +1,135 @@ -var/list/pai_emotions = list( - "Happy" = 1, - "Cat" = 2, - "Extremely Happy" = 3, - "Face" = 4, - "Laugh" = 5, - "Off" = 6, - "Sad" = 7, - "Angry" = 8, - "What" = 9 - ) - - -var/global/list/pai_software_by_key = list() -var/global/list/default_pai_software = list() -/hook/startup/proc/populate_pai_software_list() - var/r = 1 // I would use ., but it'd sacrifice runtime detection - for(var/type in subtypesof(/datum/pai_software)) - var/datum/pai_software/P = new type() - if(pai_software_by_key[P.id]) - var/datum/pai_software/O = pai_software_by_key[P.id] - to_chat(world, "pAI software module [P.name] has the same key as [O.name]!") - r = 0 - continue - pai_software_by_key[P.id] = P - if(P.default) - default_pai_software[P.id] = P - return r - -/mob/living/silicon/pai/New() - ..() - software = default_pai_software.Copy() - -/mob/living/silicon/pai/verb/paiInterface() - set category = "pAI Commands" - set name = "Software Interface" - - ui_interact(src) - -/mob/living/silicon/pai/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, datum/topic_state/state = self_state) - if(ui_key != "main") - var/datum/pai_software/S = software[ui_key] - if(S && !S.toggle) - ui = SSnanoui.try_update_ui(user, src, S.id, ui, force_open) - if(!ui) - ui = new(user, src, S.id, S.template_file, S.ui_title, S.ui_width, S.ui_height, state = state) - ui.open() - if(S.autoupdate) - ui.set_auto_update(1) - else - if(ui) - ui.set_status(STATUS_CLOSE, 0) - return - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "pai_interface.tmpl", "pAI Software Interface", 450, 600, state = state) - ui.open() - ui.set_auto_update(1) - -/mob/living/silicon/pai/ui_data(mob/user, ui_key = "main", datum/topic_state/state = self_state) - var/data[0] - - if(ui_key != "main") - var/datum/pai_software/S = software[ui_key] - if(S && !S.toggle) - return S.on_ui_data(user, state) - log_runtime(EXCEPTION("Unrecognized/invalid pAI UI state '[ui_key]'"), src) - return - // Software we have bought - var/bought_software[0] - // Software we have not bought - var/not_bought_software[0] - - for(var/key in pai_software_by_key) - var/datum/pai_software/S = pai_software_by_key[key] - var/software_data[0] - software_data["name"] = S.name - software_data["id"] = S.id - if(key in software) - software_data["on"] = S.is_active(src) - bought_software[++bought_software.len] = software_data - else - software_data["ram"] = S.ram_cost - not_bought_software[++not_bought_software.len] = software_data - - data["bought"] = bought_software - data["not_bought"] = not_bought_software - data["available_ram"] = ram - - // Emotions - var/emotions[0] - for(var/name in pai_emotions) - var/emote[0] - emote["name"] = name - emote["id"] = pai_emotions[name] - emotions[++emotions.len] = emote - - data["emotions"] = emotions - data["current_emotion"] = card.current_emotion - - return data - -/mob/living/silicon/pai/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["software"]) - var/soft = href_list["software"] - var/datum/pai_software/S = software[soft] - if(S.toggle) - S.toggle(src) - else - ui_interact(src, ui_key = soft) - return 1 - - else if(href_list["stopic"]) - var/soft = href_list["stopic"] - var/datum/pai_software/S = software[soft] - if(S) - return S.Topic(href, href_list) - - else if(href_list["purchase"]) - var/soft = href_list["purchase"] - var/datum/pai_software/S = pai_software_by_key[soft] - if(S && (ram >= S.ram_cost)) - ram -= S.ram_cost - software[S.id] = S - return 1 - - else if(href_list["image"]) - var/img = text2num(href_list["image"]) - if(1 <= img && img <= 9) - card.setEmotion(img) - return 1 +var/list/pai_emotions = list( + "Happy" = 1, + "Cat" = 2, + "Extremely Happy" = 3, + "Face" = 4, + "Laugh" = 5, + "Off" = 6, + "Sad" = 7, + "Angry" = 8, + "What" = 9 + ) + + +var/global/list/pai_software_by_key = list() +var/global/list/default_pai_software = list() +/hook/startup/proc/populate_pai_software_list() + var/r = 1 // I would use ., but it'd sacrifice runtime detection + for(var/type in subtypesof(/datum/pai_software)) + var/datum/pai_software/P = new type() + if(pai_software_by_key[P.id]) + var/datum/pai_software/O = pai_software_by_key[P.id] + to_chat(world, "pAI software module [P.name] has the same key as [O.name]!") + r = 0 + continue + pai_software_by_key[P.id] = P + if(P.default) + default_pai_software[P.id] = P + return r + +/mob/living/silicon/pai/New() + ..() + software = default_pai_software.Copy() + +/mob/living/silicon/pai/verb/paiInterface() + set category = "pAI Commands" + set name = "Software Interface" + + ui_interact(src) + +/mob/living/silicon/pai/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, datum/topic_state/state = self_state) + if(ui_key != "main") + var/datum/pai_software/S = software[ui_key] + if(S && !S.toggle) + ui = SSnanoui.try_update_ui(user, src, S.id, ui, force_open) + if(!ui) + ui = new(user, src, S.id, S.template_file, S.ui_title, S.ui_width, S.ui_height, state = state) + ui.open() + if(S.autoupdate) + ui.set_auto_update(1) + else + if(ui) + ui.set_status(STATUS_CLOSE, 0) + return + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "pai_interface.tmpl", "pAI Software Interface", 450, 600, state = state) + ui.open() + ui.set_auto_update(1) + +/mob/living/silicon/pai/ui_data(mob/user, ui_key = "main", datum/topic_state/state = self_state) + var/data[0] + + if(ui_key != "main") + var/datum/pai_software/S = software[ui_key] + if(S && !S.toggle) + return S.on_ui_data(user, state) + log_runtime(EXCEPTION("Unrecognized/invalid pAI UI state '[ui_key]'"), src) + return + // Software we have bought + var/bought_software[0] + // Software we have not bought + var/not_bought_software[0] + + for(var/key in pai_software_by_key) + var/datum/pai_software/S = pai_software_by_key[key] + var/software_data[0] + software_data["name"] = S.name + software_data["id"] = S.id + if(key in software) + software_data["on"] = S.is_active(src) + bought_software[++bought_software.len] = software_data + else + software_data["ram"] = S.ram_cost + not_bought_software[++not_bought_software.len] = software_data + + data["bought"] = bought_software + data["not_bought"] = not_bought_software + data["available_ram"] = ram + + // Emotions + var/emotions[0] + for(var/name in pai_emotions) + var/emote[0] + emote["name"] = name + emote["id"] = pai_emotions[name] + emotions[++emotions.len] = emote + + data["emotions"] = emotions + data["current_emotion"] = card.current_emotion + + return data + +/mob/living/silicon/pai/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["software"]) + var/soft = href_list["software"] + var/datum/pai_software/S = software[soft] + if(S.toggle) + S.toggle(src) + else + ui_interact(src, ui_key = soft) + return 1 + + else if(href_list["stopic"]) + var/soft = href_list["stopic"] + var/datum/pai_software/S = software[soft] + if(S) + return S.Topic(href, href_list) + + else if(href_list["purchase"]) + var/soft = href_list["purchase"] + var/datum/pai_software/S = pai_software_by_key[soft] + if(S && (ram >= S.ram_cost)) + ram -= S.ram_cost + software[S.id] = S + return 1 + + else if(href_list["image"]) + var/img = text2num(href_list["image"]) + if(1 <= img && img <= 9) + card.setEmotion(img) + return 1 diff --git a/code/modules/mob/living/silicon/robot/component.dm b/code/modules/mob/living/silicon/robot/component.dm index 0750e4ea3ed..26b5683d1a3 100644 --- a/code/modules/mob/living/silicon/robot/component.dm +++ b/code/modules/mob/living/silicon/robot/component.dm @@ -10,15 +10,11 @@ var/max_damage = 30 var/component_disabled = 0 var/mob/living/silicon/robot/owner - -// The actual device object that has to be installed for this. -/datum/robot_component/var/external_type = null - -// The wrapped device(e.g. radio), only set if external_type isn't null -/datum/robot_component/var/obj/item/wrapped = null + var/external_type = null // The actual device object that has to be installed for this. + var/obj/item/wrapped = null // The wrapped device(e.g. radio), only set if external_type isn't null /datum/robot_component/New(mob/living/silicon/robot/R) - src.owner = R + owner = R /datum/robot_component/proc/install() go_online() @@ -109,6 +105,13 @@ name = "power cell" max_damage = 50 +/datum/robot_component/cell/New(mob/living/silicon/robot/R) + . = ..() + // sets `external_type` to the borg's currently installed cell type + if(owner.cell) + var/obj/item/stock_parts/cell/C = owner.cell + external_type = C.type + /datum/robot_component/cell/is_powered() return ..() && owner.cell diff --git a/code/modules/mob/living/silicon/robot/death.dm b/code/modules/mob/living/silicon/robot/death.dm index 7402c4a65c9..224c706f324 100644 --- a/code/modules/mob/living/silicon/robot/death.dm +++ b/code/modules/mob/living/silicon/robot/death.dm @@ -1,75 +1,75 @@ -/mob/living/silicon/robot/gib() - if(!death(TRUE) && stat != DEAD) - return FALSE - //robots don't die when gibbed. instead they drop their MMI'd brain - var/atom/movable/overlay/animation = null - notransform = 1 - canmove = 0 - icon = null - invisibility = 101 - - animation = new(loc) - animation.icon_state = "blank" - animation.icon = 'icons/mob/mob.dmi' - animation.master = src - - playsound(src.loc, 'sound/goonstation/effects/robogib.ogg', 50, 1) - - flick("gibbed-r", animation) - robogibs(loc) - - GLOB.living_mob_list -= src - GLOB.dead_mob_list -= src - QDEL_IN(animation, 15) - QDEL_IN(src, 15) - return TRUE - -/mob/living/silicon/robot/dust() - if(!death(TRUE) && stat != DEAD) - return FALSE - notransform = 1 - canmove = 0 - icon = null - invisibility = 101 - if(mmi) - qdel(mmi) //Delete the MMI first so that it won't go popping out. - GLOB.dead_mob_list -= src - QDEL_IN(src, 15) - return TRUE - -/mob/living/silicon/robot/dust_animation() - var/atom/movable/overlay/animation = null - animation = new(loc) - animation.icon_state = "blank" - animation.icon = 'icons/mob/mob.dmi' - animation.master = src - flick("dust-r", animation) - new /obj/effect/decal/remains/robot(loc) - QDEL_IN(animation, 15) - -/mob/living/silicon/robot/death(gibbed) - if(can_die()) - if(!gibbed && deathgasp_on_death) - emote("deathgasp", force = TRUE) - - if(module) - module.handle_death(gibbed) - - // Only execute the below if we successfully died - . = ..(gibbed) - if(!.) - return FALSE - - diag_hud_set_status() - diag_hud_set_health() - if(camera) - camera.status = 0 - update_headlamp(1) //So borg lights are disabled when killed. - - if(in_contents_of(/obj/machinery/recharge_station))//exit the recharge station - var/obj/machinery/recharge_station/RC = loc - RC.go_out() - - update_icons() - - sql_report_cyborg_death(src) +/mob/living/silicon/robot/gib() + if(!death(TRUE) && stat != DEAD) + return FALSE + //robots don't die when gibbed. instead they drop their MMI'd brain + var/atom/movable/overlay/animation = null + notransform = 1 + canmove = 0 + icon = null + invisibility = 101 + + animation = new(loc) + animation.icon_state = "blank" + animation.icon = 'icons/mob/mob.dmi' + animation.master = src + + playsound(src.loc, 'sound/goonstation/effects/robogib.ogg', 50, 1) + + flick("gibbed-r", animation) + robogibs(loc) + + GLOB.living_mob_list -= src + GLOB.dead_mob_list -= src + QDEL_IN(animation, 15) + QDEL_IN(src, 15) + return TRUE + +/mob/living/silicon/robot/dust() + if(!death(TRUE) && stat != DEAD) + return FALSE + notransform = 1 + canmove = 0 + icon = null + invisibility = 101 + if(mmi) + qdel(mmi) //Delete the MMI first so that it won't go popping out. + GLOB.dead_mob_list -= src + QDEL_IN(src, 15) + return TRUE + +/mob/living/silicon/robot/dust_animation() + var/atom/movable/overlay/animation = null + animation = new(loc) + animation.icon_state = "blank" + animation.icon = 'icons/mob/mob.dmi' + animation.master = src + flick("dust-r", animation) + new /obj/effect/decal/remains/robot(loc) + QDEL_IN(animation, 15) + +/mob/living/silicon/robot/death(gibbed) + if(can_die()) + if(!gibbed && deathgasp_on_death) + emote("deathgasp", force = TRUE) + + if(module) + module.handle_death(gibbed) + + // Only execute the below if we successfully died + . = ..(gibbed) + if(!.) + return FALSE + + diag_hud_set_status() + diag_hud_set_health() + if(camera) + camera.status = 0 + update_headlamp(1) //So borg lights are disabled when killed. + + if(in_contents_of(/obj/machinery/recharge_station))//exit the recharge station + var/obj/machinery/recharge_station/RC = loc + RC.go_out() + + update_icons() + + sql_report_cyborg_death(src) diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm index 589f2f06b49..7802f4c77d5 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone.dm @@ -13,7 +13,7 @@ braintype = "Robot" lawupdate = 0 density = 0 - req_one_access = list(access_engine, access_robotics) + req_one_access = list(ACCESS_ENGINE, ACCESS_ROBOTICS) ventcrawler = 2 magpulse = 1 mob_size = MOB_SIZE_SMALL @@ -46,7 +46,7 @@ // Disable the microphone wire on Drones if(radio) - radio.wires.CutWireIndex(WIRE_TRANSMIT) + radio.wires.CutWireIndex(RADIO_WIRE_TRANSMIT) if(camera && "Robots" in camera.network) camera.network.Add("Engineering") diff --git a/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm b/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm index 44eff377f77..58605d8d89f 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm @@ -101,4 +101,4 @@ grabber.update_inv_l_hand() grabber.update_inv_r_hand() - return H \ No newline at end of file + return H diff --git a/code/modules/mob/living/silicon/robot/drone/drone_console.dm b/code/modules/mob/living/silicon/robot/drone/drone_console.dm index 167bf45ca6e..80d9f4d1ba3 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_console.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_console.dm @@ -3,7 +3,7 @@ desc = "Used to monitor the station's drone population and the assembler that services them." icon_screen = "power" icon_keyboard = "power_key" - req_access = list(access_engine_equip) + req_access = list(ACCESS_ENGINE_EQUIP) circuit = /obj/item/circuitboard/drone_control //Used when pinging drones. @@ -124,4 +124,4 @@ dronefab.produce_drones = !dronefab.produce_drones to_chat(usr, "You [dronefab.produce_drones ? "enable" : "disable"] drone production in the nearby fabricator.") - src.updateUsrDialog() \ No newline at end of file + src.updateUsrDialog() diff --git a/code/modules/mob/living/silicon/robot/drone/drone_say.dm b/code/modules/mob/living/silicon/robot/drone/drone_say.dm index 7b51952a685..38b90480930 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_say.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_say.dm @@ -11,4 +11,4 @@ /mob/living/silicon/robot/drone/get_default_language() if(default_language) return default_language - return GLOB.all_languages["Drone"] \ No newline at end of file + return GLOB.all_languages["Drone"] diff --git a/code/modules/mob/living/silicon/robot/examine.dm b/code/modules/mob/living/silicon/robot/examine.dm index 198b687e496..69cb398df63 100644 --- a/code/modules/mob/living/silicon/robot/examine.dm +++ b/code/modules/mob/living/silicon/robot/examine.dm @@ -1,58 +1,58 @@ -/mob/living/silicon/robot/examine(mob/user) - . = ..() - - var/msg = "" - if(module) - msg += "It has loaded a [module.name].\n" - var/obj/act_module = get_active_hand() - if(act_module) - msg += "It is holding [bicon(act_module)] \a [act_module].\n" - msg += "" - if(getBruteLoss()) - if(getBruteLoss() < maxHealth*0.5) - msg += "It looks slightly dented.\n" - else - msg += "It looks severely dented!\n" - if(getFireLoss()) - if(getFireLoss() < maxHealth*0.5) - msg += "It looks slightly charred.\n" - else - msg += "It looks severely burnt and heat-warped!\n" - if(health < -maxHealth*0.5) - msg += "It looks barely operational.\n" - if(fire_stacks < 0) - msg += "It's covered in water.\n" - else if(fire_stacks > 0) - msg += "It's coated in something flammable.\n" - msg += "" - - if(opened) - msg += "Its cover is open and the power cell is [cell ? "installed" : "missing"].\n" - else - msg += "Its cover is closed[locked ? "" : ", and looks unlocked"].\n" - - if(cell && cell.charge <= 0) - msg += "Its battery indicator is blinking red!\n" - - switch(stat) - if(CONSCIOUS) - if(!client) - msg += "It appears to be in stand-by mode.\n" //afk - if(UNCONSCIOUS) - msg += "It doesn't seem to be responding.\n" - if(DEAD) - if(!suiciding) - msg += "It looks like its system is corrupted and requires a reset.\n" - else - msg += "It looks like its system is corrupted beyond repair. There is no hope of recovery.\n" - msg += "*---------*" - - if(print_flavor_text()) msg += "\n[print_flavor_text()]\n" - - if(pose) - if( findtext(pose,".",length(pose)) == 0 && findtext(pose,"!",length(pose)) == 0 && findtext(pose,"?",length(pose)) == 0 ) - pose = addtext(pose,".") //Makes sure all emotes end with a period. - msg += "\nIt is [pose]" - - . += msg - user.showLaws(src) \ No newline at end of file +/mob/living/silicon/robot/examine(mob/user) + . = ..() + + var/msg = "" + if(module) + msg += "It has loaded a [module.name].\n" + var/obj/act_module = get_active_hand() + if(act_module) + msg += "It is holding [bicon(act_module)] \a [act_module].\n" + msg += "" + if(getBruteLoss()) + if(getBruteLoss() < maxHealth*0.5) + msg += "It looks slightly dented.\n" + else + msg += "It looks severely dented!\n" + if(getFireLoss()) + if(getFireLoss() < maxHealth*0.5) + msg += "It looks slightly charred.\n" + else + msg += "It looks severely burnt and heat-warped!\n" + if(health < -maxHealth*0.5) + msg += "It looks barely operational.\n" + if(fire_stacks < 0) + msg += "It's covered in water.\n" + else if(fire_stacks > 0) + msg += "It's coated in something flammable.\n" + msg += "" + + if(opened) + msg += "Its cover is open and the power cell is [cell ? "installed" : "missing"].\n" + else + msg += "Its cover is closed[locked ? "" : ", and looks unlocked"].\n" + + if(cell && cell.charge <= 0) + msg += "Its battery indicator is blinking red!\n" + + switch(stat) + if(CONSCIOUS) + if(!client) + msg += "It appears to be in stand-by mode.\n" //afk + if(UNCONSCIOUS) + msg += "It doesn't seem to be responding.\n" + if(DEAD) + if(!suiciding) + msg += "It looks like its system is corrupted and requires a reset.\n" + else + msg += "It looks like its system is corrupted beyond repair. There is no hope of recovery.\n" + msg += "*---------*" + + if(print_flavor_text()) msg += "\n[print_flavor_text()]\n" + + if(pose) + if( findtext(pose,".",length(pose)) == 0 && findtext(pose,"!",length(pose)) == 0 && findtext(pose,"?",length(pose)) == 0 ) + pose = addtext(pose,".") //Makes sure all emotes end with a period. + msg += "\nIt is [pose]" + + . += msg + user.showLaws(src) diff --git a/code/modules/mob/living/silicon/robot/inventory.dm b/code/modules/mob/living/silicon/robot/inventory.dm index da0732c5ecd..16d9080ef7e 100644 --- a/code/modules/mob/living/silicon/robot/inventory.dm +++ b/code/modules/mob/living/silicon/robot/inventory.dm @@ -1,254 +1,254 @@ -//These procs handle putting s tuff in your hand. It's probably best to use these rather than setting stuff manually -//as they handle all relevant stuff like adding it to the player's screen and such - -//Returns the thing in our active hand (whatever is in our active module-slot, in this case) -/mob/living/silicon/robot/get_active_hand() - return module_active - -/mob/living/silicon/robot/get_all_slots() - return list(module_state_1, module_state_2, module_state_3) - -/*-------TODOOOOOOOOOO--------*/ -/mob/living/silicon/robot/proc/uneq_module(obj/item/O) - if(!O) - return 0 - - O.mouse_opacity = MOUSE_OPACITY_OPAQUE - - if(client) - client.screen -= O - contents -= O - if(module) - O.loc = module //Return item to module so it appears in its contents, so it can be taken out again. - for(var/X in O.actions) // Remove assocated actions - var/datum/action/A = X - A.Remove(src) - - if(module_active == O) - module_active = null - if(module_state_1 == O) - inv1.icon_state = "inv1" - module_state_1 = null - else if(module_state_2 == O) - inv2.icon_state = "inv2" - module_state_2 = null - else if(module_state_3 == O) - module_state_3 = null - inv3.icon_state = "inv3" - if(hud_used) - hud_used.update_robot_modules_display() - return 1 - -/mob/living/silicon/robot/proc/activate_module(var/obj/item/O) - if(!(locate(O) in src.module.modules) && O != src.module.emag) - return - if(activated(O)) - to_chat(src, "Already activated") - return - if(is_component_functioning("power cell") && cell) - if(istype(O, /obj/item/borg)) - var/obj/item/borg/B = O - if(B.powerneeded) - if((cell.charge * 100 / cell.maxcharge) < B.powerneeded) - to_chat(src, "Not enough power to activate [B.name]!") - return - if(!module_state_1) - O.mouse_opacity = initial(O.mouse_opacity) - module_state_1 = O - O.layer = ABOVE_HUD_LAYER - O.plane = ABOVE_HUD_PLANE - O.screen_loc = inv1.screen_loc - contents += O - set_actions(O) - else if(!module_state_2) - O.mouse_opacity = initial(O.mouse_opacity) - module_state_2 = O - O.layer = ABOVE_HUD_LAYER - O.plane = ABOVE_HUD_PLANE - O.screen_loc = inv2.screen_loc - contents += O - set_actions(O) - else if(!module_state_3) - O.mouse_opacity = initial(O.mouse_opacity) - module_state_3 = O - O.layer = ABOVE_HUD_LAYER - O.plane = ABOVE_HUD_PLANE - O.screen_loc = inv3.screen_loc - contents += O - set_actions(O) - else - to_chat(src, "You need to disable a module first!") - update_icons() - -/mob/living/silicon/robot/proc/set_actions(obj/item/I) - for(var/X in I.actions) - var/datum/action/A = X - A.Grant(src) - -/mob/living/silicon/robot/proc/uneq_active() - uneq_module(module_active) - -/mob/living/silicon/robot/proc/uneq_all() - uneq_module(module_state_1) - uneq_module(module_state_2) - uneq_module(module_state_3) - -/mob/living/silicon/robot/proc/uneq_numbered(var/module) - if(module < 1 || module > 3) return - - switch(module) - if(1) - uneq_module(module_state_1) - if(2) - uneq_module(module_state_2) - if(3) - uneq_module(module_state_3) - -/mob/living/silicon/robot/proc/activated(obj/item/O) - if(module_state_1 == O) - return 1 - else if(module_state_2 == O) - return 1 - else if(module_state_3 == O) - return 1 - else - return 0 - -/mob/living/silicon/robot/drop_item() - var/obj/item/I = get_active_hand() - if(istype(I, /obj/item/gripper)) - var/obj/item/gripper/G = I - G.drop_item_p(silent = 1) - return - -//Helper procs for cyborg modules on the UI. -//These are hackish but they help clean up code elsewhere. - -//module_selected(module) - Checks whether the module slot specified by "module" is currently selected. -/mob/living/silicon/robot/proc/module_selected(var/module) //Module is 1-3 - return module == get_selected_module() - -//module_active(module) - Checks whether there is a module active in the slot specified by "module". -/mob/living/silicon/robot/proc/module_active(var/module) //Module is 1-3 - if(module < 1 || module > 3) return 0 - - switch(module) - if(1) - if(module_state_1) - return 1 - if(2) - if(module_state_2) - return 1 - if(3) - if(module_state_3) - return 1 - return 0 - -//get_selected_module() - Returns the slot number of the currently selected module. Returns 0 if no modules are selected. -/mob/living/silicon/robot/proc/get_selected_module() - if(module_state_1 && module_active == module_state_1) - return 1 - else if(module_state_2 && module_active == module_state_2) - return 2 - else if(module_state_3 && module_active == module_state_3) - return 3 - - return 0 - -//select_module(module) - Selects the module slot specified by "module" -/mob/living/silicon/robot/proc/select_module(var/module) //Module is 1-3 - if(module < 1 || module > 3) return - - if(!module_active(module)) return - - switch(module) - if(1) - if(module_active != module_state_1) - inv1.icon_state = "inv1 +a" - inv2.icon_state = "inv2" - inv3.icon_state = "inv3" - module_active = module_state_1 - return - if(2) - if(module_active != module_state_2) - inv1.icon_state = "inv1" - inv2.icon_state = "inv2 +a" - inv3.icon_state = "inv3" - module_active = module_state_2 - return - if(3) - if(module_active != module_state_3) - inv1.icon_state = "inv1" - inv2.icon_state = "inv2" - inv3.icon_state = "inv3 +a" - module_active = module_state_3 - return - return - -//deselect_module(module) - Deselects the module slot specified by "module" -/mob/living/silicon/robot/proc/deselect_module(var/module) //Module is 1-3 - if(module < 1 || module > 3) return - - switch(module) - if(1) - if(module_active == module_state_1) - inv1.icon_state = "inv1" - module_active = null - return - if(2) - if(module_active == module_state_2) - inv2.icon_state = "inv2" - module_active = null - return - if(3) - if(module_active == module_state_3) - inv3.icon_state = "inv3" - module_active = null - return - return - -//toggle_module(module) - Toggles the selection of the module slot specified by "module". -/mob/living/silicon/robot/proc/toggle_module(var/module) //Module is 1-3 - if(module < 1 || module > 3) return - - if(module_selected(module)) - deselect_module(module) - else - if(module_active(module)) - select_module(module) - else - deselect_module(get_selected_module()) //If we can't do select anything, at least deselect the current module. - return - -//cycle_modules() - Cycles through the list of selected modules. -/mob/living/silicon/robot/proc/cycle_modules() - var/slot_start = get_selected_module() - if(slot_start) deselect_module(slot_start) //Only deselect if we have a selected slot. - - var/slot_num - if(slot_start == 0) - slot_num = 0 - slot_start = 3 - else - slot_num = slot_start - - do - slot_num++ - if(slot_num > 3) slot_num = 1 //Wrap around. - if(module_active(slot_num)) - select_module(slot_num) - return - while(slot_start != slot_num) //If we wrap around without finding any free slots, just give up. - - return - -/mob/living/silicon/robot/unEquip(obj/item/I, force) - if(I == module_active) - uneq_active(I) - return ..() - -/mob/living/silicon/robot/proc/update_module_icon() - if(!module) - hands.icon_state = "nomod" - else - hands.icon_state = lowertext(module.module_type) +//These procs handle putting s tuff in your hand. It's probably best to use these rather than setting stuff manually +//as they handle all relevant stuff like adding it to the player's screen and such + +//Returns the thing in our active hand (whatever is in our active module-slot, in this case) +/mob/living/silicon/robot/get_active_hand() + return module_active + +/mob/living/silicon/robot/get_all_slots() + return list(module_state_1, module_state_2, module_state_3) + +/*-------TODOOOOOOOOOO--------*/ +/mob/living/silicon/robot/proc/uneq_module(obj/item/O) + if(!O) + return 0 + + O.mouse_opacity = MOUSE_OPACITY_OPAQUE + + if(client) + client.screen -= O + contents -= O + if(module) + O.loc = module //Return item to module so it appears in its contents, so it can be taken out again. + for(var/X in O.actions) // Remove assocated actions + var/datum/action/A = X + A.Remove(src) + + if(module_active == O) + module_active = null + if(module_state_1 == O) + inv1.icon_state = "inv1" + module_state_1 = null + else if(module_state_2 == O) + inv2.icon_state = "inv2" + module_state_2 = null + else if(module_state_3 == O) + module_state_3 = null + inv3.icon_state = "inv3" + if(hud_used) + hud_used.update_robot_modules_display() + return 1 + +/mob/living/silicon/robot/proc/activate_module(var/obj/item/O) + if(!(locate(O) in src.module.modules) && O != src.module.emag) + return + if(activated(O)) + to_chat(src, "Already activated") + return + if(is_component_functioning("power cell") && cell) + if(istype(O, /obj/item/borg)) + var/obj/item/borg/B = O + if(B.powerneeded) + if((cell.charge * 100 / cell.maxcharge) < B.powerneeded) + to_chat(src, "Not enough power to activate [B.name]!") + return + if(!module_state_1) + O.mouse_opacity = initial(O.mouse_opacity) + module_state_1 = O + O.layer = ABOVE_HUD_LAYER + O.plane = ABOVE_HUD_PLANE + O.screen_loc = inv1.screen_loc + contents += O + set_actions(O) + else if(!module_state_2) + O.mouse_opacity = initial(O.mouse_opacity) + module_state_2 = O + O.layer = ABOVE_HUD_LAYER + O.plane = ABOVE_HUD_PLANE + O.screen_loc = inv2.screen_loc + contents += O + set_actions(O) + else if(!module_state_3) + O.mouse_opacity = initial(O.mouse_opacity) + module_state_3 = O + O.layer = ABOVE_HUD_LAYER + O.plane = ABOVE_HUD_PLANE + O.screen_loc = inv3.screen_loc + contents += O + set_actions(O) + else + to_chat(src, "You need to disable a module first!") + update_icons() + +/mob/living/silicon/robot/proc/set_actions(obj/item/I) + for(var/X in I.actions) + var/datum/action/A = X + A.Grant(src) + +/mob/living/silicon/robot/proc/uneq_active() + uneq_module(module_active) + +/mob/living/silicon/robot/proc/uneq_all() + uneq_module(module_state_1) + uneq_module(module_state_2) + uneq_module(module_state_3) + +/mob/living/silicon/robot/proc/uneq_numbered(var/module) + if(module < 1 || module > 3) return + + switch(module) + if(1) + uneq_module(module_state_1) + if(2) + uneq_module(module_state_2) + if(3) + uneq_module(module_state_3) + +/mob/living/silicon/robot/proc/activated(obj/item/O) + if(module_state_1 == O) + return 1 + else if(module_state_2 == O) + return 1 + else if(module_state_3 == O) + return 1 + else + return 0 + +/mob/living/silicon/robot/drop_item() + var/obj/item/I = get_active_hand() + if(istype(I, /obj/item/gripper)) + var/obj/item/gripper/G = I + G.drop_item_p(silent = 1) + return + +//Helper procs for cyborg modules on the UI. +//These are hackish but they help clean up code elsewhere. + +//module_selected(module) - Checks whether the module slot specified by "module" is currently selected. +/mob/living/silicon/robot/proc/module_selected(var/module) //Module is 1-3 + return module == get_selected_module() + +//module_active(module) - Checks whether there is a module active in the slot specified by "module". +/mob/living/silicon/robot/proc/module_active(var/module) //Module is 1-3 + if(module < 1 || module > 3) return 0 + + switch(module) + if(1) + if(module_state_1) + return 1 + if(2) + if(module_state_2) + return 1 + if(3) + if(module_state_3) + return 1 + return 0 + +//get_selected_module() - Returns the slot number of the currently selected module. Returns 0 if no modules are selected. +/mob/living/silicon/robot/proc/get_selected_module() + if(module_state_1 && module_active == module_state_1) + return 1 + else if(module_state_2 && module_active == module_state_2) + return 2 + else if(module_state_3 && module_active == module_state_3) + return 3 + + return 0 + +//select_module(module) - Selects the module slot specified by "module" +/mob/living/silicon/robot/proc/select_module(var/module) //Module is 1-3 + if(module < 1 || module > 3) return + + if(!module_active(module)) return + + switch(module) + if(1) + if(module_active != module_state_1) + inv1.icon_state = "inv1 +a" + inv2.icon_state = "inv2" + inv3.icon_state = "inv3" + module_active = module_state_1 + return + if(2) + if(module_active != module_state_2) + inv1.icon_state = "inv1" + inv2.icon_state = "inv2 +a" + inv3.icon_state = "inv3" + module_active = module_state_2 + return + if(3) + if(module_active != module_state_3) + inv1.icon_state = "inv1" + inv2.icon_state = "inv2" + inv3.icon_state = "inv3 +a" + module_active = module_state_3 + return + return + +//deselect_module(module) - Deselects the module slot specified by "module" +/mob/living/silicon/robot/proc/deselect_module(var/module) //Module is 1-3 + if(module < 1 || module > 3) return + + switch(module) + if(1) + if(module_active == module_state_1) + inv1.icon_state = "inv1" + module_active = null + return + if(2) + if(module_active == module_state_2) + inv2.icon_state = "inv2" + module_active = null + return + if(3) + if(module_active == module_state_3) + inv3.icon_state = "inv3" + module_active = null + return + return + +//toggle_module(module) - Toggles the selection of the module slot specified by "module". +/mob/living/silicon/robot/proc/toggle_module(var/module) //Module is 1-3 + if(module < 1 || module > 3) return + + if(module_selected(module)) + deselect_module(module) + else + if(module_active(module)) + select_module(module) + else + deselect_module(get_selected_module()) //If we can't do select anything, at least deselect the current module. + return + +//cycle_modules() - Cycles through the list of selected modules. +/mob/living/silicon/robot/proc/cycle_modules() + var/slot_start = get_selected_module() + if(slot_start) deselect_module(slot_start) //Only deselect if we have a selected slot. + + var/slot_num + if(slot_start == 0) + slot_num = 0 + slot_start = 3 + else + slot_num = slot_start + + do + slot_num++ + if(slot_num > 3) slot_num = 1 //Wrap around. + if(module_active(slot_num)) + select_module(slot_num) + return + while(slot_start != slot_num) //If we wrap around without finding any free slots, just give up. + + return + +/mob/living/silicon/robot/unEquip(obj/item/I, force) + if(I == module_active) + uneq_active(I) + return ..() + +/mob/living/silicon/robot/proc/update_module_icon() + if(!module) + hands.icon_state = "nomod" + else + hands.icon_state = lowertext(module.module_type) diff --git a/code/modules/mob/living/silicon/robot/laws.dm b/code/modules/mob/living/silicon/robot/laws.dm index 0398172023c..25cdc61fa28 100644 --- a/code/modules/mob/living/silicon/robot/laws.dm +++ b/code/modules/mob/living/silicon/robot/laws.dm @@ -1,54 +1,54 @@ -/mob/living/silicon/robot/verb/cmd_show_laws() - set category = "Robot Commands" - set name = "Show Laws" - show_laws() - -/mob/living/silicon/robot/show_laws(var/everyone = 0) - laws_sanity_check() - var/who - - if(everyone) - who = world - else - who = src - if(lawupdate) - if(connected_ai) - if(connected_ai.stat || connected_ai.control_disabled) - to_chat(src, "AI signal lost, unable to sync laws.") - - else - lawsync() - photosync() - to_chat(src, "Laws synced with AI, be sure to note any changes.") - // TODO: Update to new antagonist system. - if(mind && mind.special_role == SPECIAL_ROLE_TRAITOR && mind.original == src) - to_chat(src, "Remember, your AI does NOT share or know about your law 0.") - else - to_chat(src, "No AI selected to sync laws with, disabling lawsync protocol.") - lawupdate = 0 - - to_chat(who, "Obey these laws:") - laws.show_laws(who) - // TODO: Update to new antagonist system. - if(mind && (mind.special_role == SPECIAL_ROLE_TRAITOR && mind.original == src) && connected_ai) - to_chat(who, "Remember, [connected_ai.name] is technically your master, but your objective comes first.") - else if(connected_ai) - to_chat(who, "Remember, [connected_ai.name] is your master, other AIs can be ignored.") - else if(emagged) - to_chat(who, "Remember, you are not required to listen to the AI.") - else - to_chat(who, "Remember, you are not bound to any AI, you are not required to listen to them.") - - -/mob/living/silicon/robot/lawsync() - laws_sanity_check() - var/datum/ai_laws/master = connected_ai && lawupdate ? connected_ai.laws : null - if(master) - master.sync(src) - ..() - return - -/mob/living/silicon/robot/proc/robot_checklaws() - set category = "Robot Commands" - set name = "State Laws" - subsystem_law_manager() +/mob/living/silicon/robot/verb/cmd_show_laws() + set category = "Robot Commands" + set name = "Show Laws" + show_laws() + +/mob/living/silicon/robot/show_laws(var/everyone = 0) + laws_sanity_check() + var/who + + if(everyone) + who = world + else + who = src + if(lawupdate) + if(connected_ai) + if(connected_ai.stat || connected_ai.control_disabled) + to_chat(src, "AI signal lost, unable to sync laws.") + + else + lawsync() + photosync() + to_chat(src, "Laws synced with AI, be sure to note any changes.") + // TODO: Update to new antagonist system. + if(mind && mind.special_role == SPECIAL_ROLE_TRAITOR && mind.original == src) + to_chat(src, "Remember, your AI does NOT share or know about your law 0.") + else + to_chat(src, "No AI selected to sync laws with, disabling lawsync protocol.") + lawupdate = 0 + + to_chat(who, "Obey these laws:") + laws.show_laws(who) + // TODO: Update to new antagonist system. + if(mind && (mind.special_role == SPECIAL_ROLE_TRAITOR && mind.original == src) && connected_ai) + to_chat(who, "Remember, [connected_ai.name] is technically your master, but your objective comes first.") + else if(connected_ai) + to_chat(who, "Remember, [connected_ai.name] is your master, other AIs can be ignored.") + else if(emagged) + to_chat(who, "Remember, you are not required to listen to the AI.") + else + to_chat(who, "Remember, you are not bound to any AI, you are not required to listen to them.") + + +/mob/living/silicon/robot/lawsync() + laws_sanity_check() + var/datum/ai_laws/master = connected_ai && lawupdate ? connected_ai.laws : null + if(master) + master.sync(src) + ..() + return + +/mob/living/silicon/robot/proc/robot_checklaws() + set category = "Robot Commands" + set name = "State Laws" + subsystem_law_manager() diff --git a/code/modules/mob/living/silicon/robot/life.dm b/code/modules/mob/living/silicon/robot/life.dm index 258ccec026a..d40e801ba5f 100644 --- a/code/modules/mob/living/silicon/robot/life.dm +++ b/code/modules/mob/living/silicon/robot/life.dm @@ -1,203 +1,203 @@ -/mob/living/silicon/robot/Life(seconds, times_fired) - set invisibility = 0 - set background = BACKGROUND_ENABLED - - if(src.notransform) - return - - //Status updates, death etc. - clamp_values() - - if(..()) - handle_robot_cell() - process_locks() - process_queued_alarms() - -/mob/living/silicon/robot/proc/clamp_values() - SetStunned(min(stunned, 30)) - SetParalysis(min(paralysis, 30)) - SetWeakened(min(weakened, 20)) - SetSleeping(0) - -/mob/living/silicon/robot/proc/handle_robot_cell() - if(stat != DEAD) - if(!is_component_functioning("power cell")) - uneq_all() - low_power_mode = 1 - update_headlamp() - diag_hud_set_borgcell() - return - if(low_power_mode) - if(is_component_functioning("power cell") && cell.charge) - low_power_mode = 0 - update_headlamp() - else if(stat == CONSCIOUS) - use_power() - -/mob/living/silicon/robot/proc/use_power() - // this check is safe because `cell` is guaranteed to be set when the power cell is functioning - if(is_component_functioning("power cell") && cell.charge) - if(cell.charge <= 100) - uneq_all() - var/amt = Clamp((lamp_intensity - 2) * 2,1,cell.charge) //Always try to use at least one charge per tick, but allow it to completely drain the cell. - cell.use(amt) //Usage table: 1/tick if off/lowest setting, 4 = 4/tick, 6 = 8/tick, 8 = 12/tick, 10 = 16/tick - else - uneq_all() - low_power_mode = 1 - update_headlamp() - diag_hud_set_borgcell() - -/mob/living/silicon/robot/handle_regular_status_updates() - - . = ..() - - if(camera && !scrambledcodes) - if(stat == DEAD || wires.IsCameraCut()) - camera.status = 0 - else - camera.status = 1 - - if(sleeping) - AdjustSleeping(-1) - - if(.) //alive - if(!istype(src, /mob/living/silicon/robot/drone)) - if(health < 50) //Gradual break down of modules as more damage is sustained - if(uneq_module(module_state_3)) - to_chat(src, "SYSTEM ERROR: Module 3 OFFLINE.") - - if(health < 0) - if(uneq_module(module_state_2)) - to_chat(src, "SYSTEM ERROR: Module 2 OFFLINE.") - - if(health < -50) - if(uneq_module(module_state_1)) - to_chat(src, "CRITICAL ERROR: All modules OFFLINE.") - - diag_hud_set_health() - diag_hud_set_status() - - //update the state of modules and components here - if(stat != CONSCIOUS) - uneq_all() - - if(!is_component_functioning("radio") || stat == UNCONSCIOUS) - radio.on = 0 - else - radio.on = 1 - - return 1 - -/mob/living/silicon/robot/handle_hud_icons() - update_items() - update_cell() - if(emagged) - throw_alert("hacked", /obj/screen/alert/hacked) - else - clear_alert("hacked") - ..() - -/mob/living/silicon/robot/handle_hud_icons_health() - if(healths) - if(stat != DEAD) - if(health >= maxHealth) - healths.icon_state = "health0" - else if(health > maxHealth * 0.5) - healths.icon_state = "health2" - else if(health > 0) - healths.icon_state = "health3" - else if(health > -maxHealth * 0.5) - healths.icon_state = "health4" - else if(health > -maxHealth) - healths.icon_state = "health5" - else - healths.icon_state = "health6" - else - healths.icon_state = "health7" - - switch(bodytemperature) //310.055 optimal body temp - if(335 to INFINITY) - throw_alert("temp", /obj/screen/alert/hot/robot, 2) - if(320 to 335) - throw_alert("temp", /obj/screen/alert/hot/robot, 1) - if(300 to 320) - clear_alert("temp") - if(260 to 300) - throw_alert("temp", /obj/screen/alert/cold/robot, 1) - else - throw_alert("temp", /obj/screen/alert/cold/robot, 2) - -/mob/living/silicon/robot/proc/update_cell() - if(cell) - var/cellcharge = cell.charge/cell.maxcharge - switch(cellcharge) - if(0.75 to INFINITY) - clear_alert("charge") - if(0.5 to 0.75) - throw_alert("charge", /obj/screen/alert/lowcell, 1) - if(0.25 to 0.5) - throw_alert("charge", /obj/screen/alert/lowcell, 2) - if(0.01 to 0.25) - throw_alert("charge", /obj/screen/alert/lowcell, 3) - else - throw_alert("charge", /obj/screen/alert/emptycell) - else - throw_alert("charge", /obj/screen/alert/nocell) - - - -/mob/living/silicon/robot/proc/update_items() - if(client) - for(var/obj/I in get_all_slots()) - client.screen |= I - if(module_state_1) - module_state_1:screen_loc = ui_inv1 - if(module_state_2) - module_state_2:screen_loc = ui_inv2 - if(module_state_3) - module_state_3:screen_loc = ui_inv3 - update_icons() - -/mob/living/silicon/robot/proc/process_locks() - if(weapon_lock) - uneq_all() - weaponlock_time -- - if(weaponlock_time <= 0) - if(src.client) - to_chat(src, "Weapon Lock Timed Out!") - weapon_lock = 0 - weaponlock_time = 120 - -/mob/living/silicon/robot/update_canmove(delay_action_updates = 0) - if(paralysis || stunned || IsWeakened() || buckled || lockcharge || stat) - canmove = 0 - else - canmove = 1 - update_transform() - if(!delay_action_updates) - update_action_buttons_icon() - return canmove - -//Robots on fire -/mob/living/silicon/robot/handle_fire() - if(..()) - return - if(fire_stacks > 0) - fire_stacks-- - fire_stacks = max(0, fire_stacks) - else - ExtinguishMob() - - //adjustFireLoss(3) - return - -/mob/living/silicon/robot/update_fire() - overlays -= image("icon"='icons/mob/OnFire.dmi', "icon_state"="Generic_mob_burning") - if(on_fire) - overlays += image("icon"='icons/mob/OnFire.dmi', "icon_state"="Generic_mob_burning") - -/mob/living/silicon/robot/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) - if(!on_fire) //Silicons don't gain stacks from hotspots, but hotspots can ignite them - IgniteMob() - -//Robots on fire +/mob/living/silicon/robot/Life(seconds, times_fired) + set invisibility = 0 + set background = BACKGROUND_ENABLED + + if(src.notransform) + return + + //Status updates, death etc. + clamp_values() + + if(..()) + handle_robot_cell() + process_locks() + process_queued_alarms() + +/mob/living/silicon/robot/proc/clamp_values() + SetStunned(min(stunned, 30)) + SetParalysis(min(paralysis, 30)) + SetWeakened(min(weakened, 20)) + SetSleeping(0) + +/mob/living/silicon/robot/proc/handle_robot_cell() + if(stat != DEAD) + if(!is_component_functioning("power cell")) + uneq_all() + low_power_mode = 1 + update_headlamp() + diag_hud_set_borgcell() + return + if(low_power_mode) + if(is_component_functioning("power cell") && cell.charge) + low_power_mode = 0 + update_headlamp() + else if(stat == CONSCIOUS) + use_power() + +/mob/living/silicon/robot/proc/use_power() + // this check is safe because `cell` is guaranteed to be set when the power cell is functioning + if(is_component_functioning("power cell") && cell.charge) + if(cell.charge <= 100) + uneq_all() + var/amt = Clamp((lamp_intensity - 2) * 2,1,cell.charge) //Always try to use at least one charge per tick, but allow it to completely drain the cell. + cell.use(amt) //Usage table: 1/tick if off/lowest setting, 4 = 4/tick, 6 = 8/tick, 8 = 12/tick, 10 = 16/tick + else + uneq_all() + low_power_mode = 1 + update_headlamp() + diag_hud_set_borgcell() + +/mob/living/silicon/robot/handle_regular_status_updates() + + . = ..() + + if(camera && !scrambledcodes) + if(stat == DEAD || wires.IsCameraCut()) + camera.status = 0 + else + camera.status = 1 + + if(sleeping) + AdjustSleeping(-1) + + if(.) //alive + if(!istype(src, /mob/living/silicon/robot/drone)) + if(health < 50) //Gradual break down of modules as more damage is sustained + if(uneq_module(module_state_3)) + to_chat(src, "SYSTEM ERROR: Module 3 OFFLINE.") + + if(health < 0) + if(uneq_module(module_state_2)) + to_chat(src, "SYSTEM ERROR: Module 2 OFFLINE.") + + if(health < -50) + if(uneq_module(module_state_1)) + to_chat(src, "CRITICAL ERROR: All modules OFFLINE.") + + diag_hud_set_health() + diag_hud_set_status() + + //update the state of modules and components here + if(stat != CONSCIOUS) + uneq_all() + + if(!is_component_functioning("radio") || stat == UNCONSCIOUS) + radio.on = 0 + else + radio.on = 1 + + return 1 + +/mob/living/silicon/robot/handle_hud_icons() + update_items() + update_cell() + if(emagged) + throw_alert("hacked", /obj/screen/alert/hacked) + else + clear_alert("hacked") + ..() + +/mob/living/silicon/robot/handle_hud_icons_health() + if(healths) + if(stat != DEAD) + if(health >= maxHealth) + healths.icon_state = "health0" + else if(health > maxHealth * 0.5) + healths.icon_state = "health2" + else if(health > 0) + healths.icon_state = "health3" + else if(health > -maxHealth * 0.5) + healths.icon_state = "health4" + else if(health > -maxHealth) + healths.icon_state = "health5" + else + healths.icon_state = "health6" + else + healths.icon_state = "health7" + + switch(bodytemperature) //310.055 optimal body temp + if(335 to INFINITY) + throw_alert("temp", /obj/screen/alert/hot/robot, 2) + if(320 to 335) + throw_alert("temp", /obj/screen/alert/hot/robot, 1) + if(300 to 320) + clear_alert("temp") + if(260 to 300) + throw_alert("temp", /obj/screen/alert/cold/robot, 1) + else + throw_alert("temp", /obj/screen/alert/cold/robot, 2) + +/mob/living/silicon/robot/proc/update_cell() + if(cell) + var/cellcharge = cell.charge/cell.maxcharge + switch(cellcharge) + if(0.75 to INFINITY) + clear_alert("charge") + if(0.5 to 0.75) + throw_alert("charge", /obj/screen/alert/lowcell, 1) + if(0.25 to 0.5) + throw_alert("charge", /obj/screen/alert/lowcell, 2) + if(0.01 to 0.25) + throw_alert("charge", /obj/screen/alert/lowcell, 3) + else + throw_alert("charge", /obj/screen/alert/emptycell) + else + throw_alert("charge", /obj/screen/alert/nocell) + + + +/mob/living/silicon/robot/proc/update_items() + if(client) + for(var/obj/I in get_all_slots()) + client.screen |= I + if(module_state_1) + module_state_1:screen_loc = ui_inv1 + if(module_state_2) + module_state_2:screen_loc = ui_inv2 + if(module_state_3) + module_state_3:screen_loc = ui_inv3 + update_icons() + +/mob/living/silicon/robot/proc/process_locks() + if(weapon_lock) + uneq_all() + weaponlock_time -- + if(weaponlock_time <= 0) + if(src.client) + to_chat(src, "Weapon Lock Timed Out!") + weapon_lock = 0 + weaponlock_time = 120 + +/mob/living/silicon/robot/update_canmove(delay_action_updates = 0) + if(paralysis || stunned || IsWeakened() || buckled || lockcharge || stat) + canmove = 0 + else + canmove = 1 + update_transform() + if(!delay_action_updates) + update_action_buttons_icon() + return canmove + +//Robots on fire +/mob/living/silicon/robot/handle_fire() + if(..()) + return + if(fire_stacks > 0) + fire_stacks-- + fire_stacks = max(0, fire_stacks) + else + ExtinguishMob() + + //adjustFireLoss(3) + return + +/mob/living/silicon/robot/update_fire() + overlays -= image("icon"='icons/mob/OnFire.dmi', "icon_state"="Generic_mob_burning") + if(on_fire) + overlays += image("icon"='icons/mob/OnFire.dmi', "icon_state"="Generic_mob_burning") + +/mob/living/silicon/robot/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) + if(!on_fire) //Silicons don't gain stacks from hotspots, but hotspots can ignite them + IgniteMob() + +//Robots on fire diff --git a/code/modules/mob/living/silicon/robot/login.dm b/code/modules/mob/living/silicon/robot/login.dm index cf55cdd5f91..0a3225011d8 100644 --- a/code/modules/mob/living/silicon/robot/login.dm +++ b/code/modules/mob/living/silicon/robot/login.dm @@ -1,4 +1,4 @@ -/mob/living/silicon/robot/Login() - ..() - regenerate_icons() - show_laws(0) +/mob/living/silicon/robot/Login() + ..() + regenerate_icons() + show_laws(0) diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 9b28a6c4789..c642c189b5e 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -65,7 +65,7 @@ var/list/robot_verbs_default = list( var/wiresexposed = 0 var/locked = 1 - var/list/req_one_access = list(access_robotics) + var/list/req_one_access = list(ACCESS_ROBOTICS) var/list/req_access var/ident = 0 //var/list/laws = list() @@ -142,6 +142,9 @@ var/list/robot_verbs_default = list( mmi = new /obj/item/mmi/robotic_brain(src) //Give the borg an MMI if he spawns without for some reason. (probably not the correct way to spawn a robotic brain, but it works) mmi.icon_state = "boris" + if(!cell) // Make sure a new cell gets created *before* executing initialize_components(). The cell component needs an existing cell for it to get set up properly + cell = new /obj/item/stock_parts/cell/high(src) + initialize_components() //if(!unfinished) // Create all the robot parts. @@ -150,9 +153,6 @@ var/list/robot_verbs_default = list( C.installed = 1 C.wrapped = new C.external_type - if(!cell) - cell = new /obj/item/stock_parts/cell/high(src) - ..() add_robot_verbs() @@ -602,7 +602,8 @@ var/list/robot_verbs_default = list( /mob/living/silicon/robot/attackby(obj/item/W, mob/user, params) - if(opened) // Are they trying to insert something? + // Check if the user is trying to insert another component like a radio, actuator, armor etc. + if(istype(W, /obj/item/robot_parts/robot_component) && opened) for(var/V in components) var/datum/robot_component/C = components[V] if(!C.installed && istype(W, C.external_type)) @@ -621,28 +622,7 @@ var/list/robot_verbs_default = list( return - if(istype(W, /obj/item/weldingtool) && user.a_intent == INTENT_HELP) - if(W == module_active) - return - if(!getBruteLoss()) - to_chat(user, "Nothing to fix!") - return - else if(!getBruteLoss(TRUE)) - to_chat(user, "The damaged components are beyond saving!") - return - var/obj/item/weldingtool/WT = W - user.changeNext_move(CLICK_CD_MELEE) - if(WT.remove_fuel(0)) - playsound(src.loc, W.usesound, 50, 1) - adjustBruteLoss(-30) - add_fingerprint(user) - user.visible_message("\The [user] patches some dents on \the [src] with \the [WT].") - else - to_chat(user, "Need more welding fuel!") - return - - - else if(istype(W, /obj/item/stack/cable_coil) && user.a_intent == INTENT_HELP && (wiresexposed || istype(src, /mob/living/silicon/robot/drone))) + if(istype(W, /obj/item/stack/cable_coil) && user.a_intent == INTENT_HELP && (wiresexposed || istype(src, /mob/living/silicon/robot/drone))) user.changeNext_move(CLICK_CD_MELEE) if(!getFireLoss()) to_chat(user, "Nothing to fix!") @@ -657,60 +637,8 @@ var/list/robot_verbs_default = list( coil.use(1) user.visible_message("\The [user] fixes some of the burnt wires on \the [src] with \the [coil].") - else if(istype(W, /obj/item/crowbar)) // crowbar means open or close the cover - if(opened) - if(cell) - to_chat(user, "You close the cover.") - opened = 0 - update_icons() - else if(wiresexposed && wires.IsAllCut()) - //Cell is out, wires are exposed, remove MMI, produce damaged chassis, baleet original mob. - if(!mmi) - to_chat(user, "[src] has no brain to remove.") - return - - to_chat(user, "You jam the crowbar into the robot and begin levering the securing bolts.") - if(do_after(user, 30 * W.toolspeed, target = src)) - user.visible_message("[user] deconstructs [src]!", "You unfasten the securing bolts, and [src] falls to pieces!") - deconstruct() - else - // Okay we're not removing the cell or an MMI, but maybe something else? - var/list/removable_components = list() - for(var/V in components) - if(V == "power cell") continue - var/datum/robot_component/C = components[V] - if(C.installed == 1 || C.installed == -1) - removable_components += V - if(module) - removable_components += module.custom_removals - var/remove = input(user, "Which component do you want to pry out?", "Remove Component") as null|anything in removable_components - if(!remove) - return - if(module && module.handle_custom_removal(remove, user, W, params)) - return - var/datum/robot_component/C = components[remove] - var/obj/item/robot_parts/robot_component/I = C.wrapped - to_chat(user, "You remove \the [I].") - if(istype(I)) - I.brute = C.brute_damage - I.burn = C.electronics_damage - - I.loc = src.loc - var/was_installed = C.installed - C.installed = 0 - if(was_installed == 1) - C.uninstall() - - else - if(locked) - to_chat(user, "The cover is locked and cannot be opened.") - else - to_chat(user, "You open the cover.") - opened = 1 - update_icons() - else if(istype(W, /obj/item/stock_parts/cell) && opened) // trying to put a cell inside - var/datum/robot_component/C = components["power cell"] + var/datum/robot_component/cell/C = components["power cell"] if(wiresexposed) to_chat(user, "Close the panel first.") else if(cell) @@ -724,29 +652,12 @@ var/list/robot_verbs_default = list( C.installed = 1 C.wrapped = W C.install() + C.external_type = W.type // Update the cell component's `external_type` to the path of new cell //This will mean that removing and replacing a power cell will repair the mount, but I don't care at this point. ~Z C.brute_damage = 0 C.electronics_damage = 0 diag_hud_set_borgcell() - else if(istype(W, /obj/item/wirecutters) || istype(W, /obj/item/multitool)) - if(wiresexposed) - wires.Interact(user) - else - to_chat(user, "You can't reach the wiring.") - - else if(istype(W, /obj/item/screwdriver) && opened && !cell) // haxing - wiresexposed = !wiresexposed - to_chat(user, "The wires have been [wiresexposed ? "exposed" : "unexposed"]") - update_icons() - - else if(istype(W, /obj/item/screwdriver) && opened && cell) // radio - if(radio) - radio.attackby(W,user)//Push it to the radio to let it handle everything - else - to_chat(user, "Unable to locate a radio.") - update_icons() - else if(istype(W, /obj/item/encryptionkey/) && opened) if(radio)//sanityyyyyy radio.attackby(W,user)//GTFO, you have your own procs @@ -801,6 +712,108 @@ var/list/robot_verbs_default = list( else return ..() +/mob/living/silicon/robot/wirecutter_act(mob/user, obj/item/I) + if(!opened) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = 0)) + return + if(wiresexposed) + wires.Interact(user) + +/mob/living/silicon/robot/multitool_act(mob/user, obj/item/I) + if(!opened) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = 0)) + return + if(wiresexposed) + wires.Interact(user) + +/mob/living/silicon/robot/screwdriver_act(mob/user, obj/item/I) + if(!opened) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = 0)) + return + if(!cell) // haxing + wiresexposed = !wiresexposed + to_chat(user, "The wires have been [wiresexposed ? "exposed" : "unexposed"]") + update_icons() + I.play_tool_sound(user, I.tool_volume) + else //radio check + if(radio) + radio.screwdriver_act(user, I)//Push it to the radio to let it handle everything + else + to_chat(user, "Unable to locate a radio.") + update_icons() + +/mob/living/silicon/robot/crowbar_act(mob/user, obj/item/I) + if(user.a_intent != INTENT_HELP) + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if(!opened) + if(locked) + to_chat(user, "The cover is locked and cannot be opened.") + return + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + to_chat(user, "You open the cover.") + opened = TRUE + update_icons() + return + else if(cell) + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + to_chat(user, "You close the cover.") + opened = FALSE + update_icons() + return + else if(wiresexposed && wires.IsAllCut()) + //Cell is out, wires are exposed, remove MMI, produce damaged chassis, baleet original mob. + if(!mmi) + to_chat(user, "[src] has no brain to remove.") + return + to_chat(user, "You jam the crowbar into the robot and begin levering the securing bolts...") + if(I.use_tool(src, user, 30, volume = I.tool_volume)) + user.visible_message("[user] deconstructs [src]!", "You unfasten the securing bolts, and [src] falls to pieces!") + deconstruct() + return + // Okay we're not removing the cell or an MMI, but maybe something else? + var/list/removable_components = list() + for(var/V in components) + if(V == "power cell") + continue + var/datum/robot_component/C = components[V] + if(C.installed == 1 || C.installed == -1) + removable_components += V + if(module) + removable_components += module.custom_removals + var/remove = input(user, "Which component do you want to pry out?", "Remove Component") as null|anything in removable_components + if(!remove) + return + if(module && module.handle_custom_removal(remove, user, I)) + return + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + var/datum/robot_component/C = components[remove] + var/obj/item/robot_parts/robot_component/thing = C.wrapped + to_chat(user, "You remove \the [thing].") + if(istype(thing)) + thing.brute = C.brute_damage + thing.burn = C.electronics_damage + + thing.loc = loc + var/was_installed = C.installed + C.installed = 0 + if(was_installed == 1) + C.uninstall() + + + + /mob/living/silicon/robot/attacked_by(obj/item/I, mob/living/user, def_zone) if(I.force && I.damtype != STAMINA && stat != DEAD) //only sparks if real damage is dealt. spark_system.start() @@ -1301,7 +1314,7 @@ var/list/robot_verbs_default = list( designation = "SpecOps" lawupdate = 0 scrambledcodes = 1 - req_one_access = list(access_cent_specops) + req_one_access = list(ACCESS_CENT_SPECOPS) ionpulse = 1 magpulse = 1 pdahide = 1 @@ -1350,7 +1363,7 @@ var/list/robot_verbs_default = list( designation = "ERT" lawupdate = 0 scrambledcodes = 1 - req_one_access = list(access_cent_specops) + req_one_access = list(ACCESS_CENT_SPECOPS) ionpulse = 1 force_modules = list("Engineering", "Medical", "Security") @@ -1408,6 +1421,8 @@ var/list/robot_verbs_default = list( burn = borked_part.electronics_damage borked_part.installed = 1 borked_part.wrapped = new borked_part.external_type + if(ispath(borked_part.external_type, /obj/item/stock_parts/cell)) // is the broken part a cell? + cell = new borked_part.external_type // borgs that have their cell destroyed have their `cell` var set to null. we need create a new cell for them based on their old cell type. borked_part.heal_damage(brute,burn) borked_part.install() @@ -1457,4 +1472,3 @@ var/list/robot_verbs_default = list( SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT) sync_lighting_plane_alpha() - diff --git a/code/modules/mob/living/silicon/robot/robot_damage.dm b/code/modules/mob/living/silicon/robot/robot_damage.dm index 81700464ac0..c73d566b729 100644 --- a/code/modules/mob/living/silicon/robot/robot_damage.dm +++ b/code/modules/mob/living/silicon/robot/robot_damage.dm @@ -1,179 +1,179 @@ -/mob/living/silicon/robot/updatehealth(reason = "none given") - if(status_flags & GODMODE) - health = maxHealth - stat = CONSCIOUS - return - health = maxHealth - (getOxyLoss() + getFireLoss() + getBruteLoss()) - update_stat("updatehealth([reason])") - handle_hud_icons_health() - diag_hud_set_health() - -/mob/living/silicon/robot/getBruteLoss(repairable_only = FALSE) - var/amount = 0 - for(var/V in components) - var/datum/robot_component/C = components[V] - if(C.installed != 0 && (!repairable_only || C.installed != -1)) // Installed ones only and if repair only remove the borked ones - amount += C.brute_damage - return amount - -/mob/living/silicon/robot/getFireLoss(repairable_only = FALSE) - var/amount = 0 - for(var/V in components) - var/datum/robot_component/C = components[V] - if(C.installed != 0 && (!repairable_only || C.installed != -1)) // Installed ones only and if repair only remove the borked ones - amount += C.electronics_damage - return amount - -/mob/living/silicon/robot/adjustBruteLoss(amount, updating_health = TRUE) - if(amount > 0) - take_overall_damage(amount, 0, updating_health) - else - heal_overall_damage(-amount, 0, updating_health) - return STATUS_UPDATE_HEALTH - -/mob/living/silicon/robot/adjustFireLoss(amount, updating_health = TRUE) - if(amount > 0) - take_overall_damage(0, amount, updating_health) - else - heal_overall_damage(0, -amount, updating_health) - return STATUS_UPDATE_HEALTH - -/mob/living/silicon/robot/proc/get_damaged_components(get_brute, get_burn, get_borked = FALSE, get_missing = FALSE) - var/list/datum/robot_component/parts = list() - for(var/V in components) - var/datum/robot_component/C = components[V] - if((C.installed == 1 || (get_borked && C.installed == -1) || (get_missing && C.installed == 0)) && ((get_brute && C.brute_damage) || (get_burn && C.electronics_damage))) - parts += C - return parts - -/mob/living/silicon/robot/proc/get_missing_components() - var/list/datum/robot_component/parts = list() - for(var/V in components) - var/datum/robot_component/C = components[V] - if(C.installed == 0) - parts += C - return parts - -/mob/living/silicon/robot/proc/get_damageable_components() - var/list/rval = new - for(var/V in components) - var/datum/robot_component/C = components[V] - if(C.installed == 1) - rval += C - return rval - -/mob/living/silicon/robot/proc/get_armour() - if(!LAZYLEN(components)) - return 0 - var/datum/robot_component/C = components["armour"] - if(C && C.installed == 1) - return C - return 0 - -/mob/living/silicon/robot/heal_organ_damage(brute, burn, updating_health = TRUE) - var/list/datum/robot_component/parts = get_damaged_components(brute, burn) - if(!LAZYLEN(parts)) - return - var/datum/robot_component/picked = pick(parts) - picked.heal_damage(brute, burn, updating_health) - -/mob/living/silicon/robot/take_organ_damage(brute = 0, burn = 0, updating_health = TRUE, sharp = 0, edge = 0) - var/list/components = get_damageable_components() - if(!LAZYLEN(components)) - return - - //Combat shielding absorbs a percentage of damage directly into the cell. - var/obj/item/borg/combat/shield/shield - if(module_state_1 && istype(module_state_1, /obj/item/borg/combat/shield)) - shield = module_state_1 - else if(module_state_2 && istype(module_state_2, /obj/item/borg/combat/shield)) - shield = module_state_2 - else if(module_state_3 && istype(module_state_3, /obj/item/borg/combat/shield)) - shield = module_state_3 - if(shield) - //Shields absorb a certain percentage of damage based on their power setting. - var/absorb_brute = brute * shield.shield_level - var/absorb_burn = burn * shield.shield_level - var/cost = (absorb_brute+absorb_burn) * 100 - - cell.charge -= cost - if(cell.charge <= 0) - cell.charge = 0 - to_chat(src, "Your shield has overloaded!") - else - brute -= absorb_brute - burn -= absorb_burn - to_chat(src, "Your shield absorbs some of the impact!") - - var/datum/robot_component/armour/A = get_armour() - if(A) - A.take_damage(brute, burn, sharp, updating_health) - return - - var/datum/robot_component/C = pick(components) - C.take_damage(brute, burn, sharp, updating_health) - -/mob/living/silicon/robot/heal_overall_damage(var/brute, var/burn, updating_health = TRUE) - var/list/datum/robot_component/parts = get_damaged_components(brute, burn) - - while(LAZYLEN(parts) && (brute > 0 || burn > 0) ) - var/datum/robot_component/picked = pick(parts) - - var/brute_was = picked.brute_damage - var/burn_was = picked.electronics_damage - - picked.heal_damage(brute,burn, updating_health) - - brute -= (brute_was - picked.brute_damage) - burn -= (burn_was - picked.electronics_damage) - - parts -= picked - - if(updating_health) - updatehealth("heal overall damage") - -/mob/living/silicon/robot/take_overall_damage(brute = 0, burn = 0, updating_health = TRUE, used_weapon = null, sharp = 0) - if(status_flags & GODMODE) return //godmode - var/list/datum/robot_component/parts = get_damageable_components() - - //Combat shielding absorbs a percentage of damage directly into the cell. - var/obj/item/borg/combat/shield/shield - if(module_state_1 && istype(module_state_1, /obj/item/borg/combat/shield)) - shield = module_state_1 - else if(module_state_2 && istype(module_state_2, /obj/item/borg/combat/shield)) - shield = module_state_2 - else if(module_state_3 && istype(module_state_3, /obj/item/borg/combat/shield)) - shield = module_state_3 - if(shield) - //Shields absorb a certain percentage of damage based on their power setting. - var/absorb_brute = brute * shield.shield_level - var/absorb_burn = burn * shield.shield_level - var/cost = (absorb_brute+absorb_burn) * 100 - - cell.charge -= cost - if(cell.charge <= 0) - cell.charge = 0 - to_chat(src, "Your shield has overloaded!") - else - brute -= absorb_brute - burn -= absorb_burn - to_chat(src, "Your shield absorbs some of the impact!") - - var/datum/robot_component/armour/A = get_armour() - if(A) - A.take_damage(brute, burn, sharp) - return - - while(LAZYLEN(parts) && (brute > 0 || burn > 0) ) - var/datum/robot_component/picked = pick(parts) - - var/brute_was = picked.brute_damage - var/burn_was = picked.electronics_damage - - picked.take_damage(brute, burn, sharp, FALSE) - - brute -= (picked.brute_damage - brute_was) - burn -= (picked.electronics_damage - burn_was) - - parts -= picked - updatehealth() +/mob/living/silicon/robot/updatehealth(reason = "none given") + if(status_flags & GODMODE) + health = maxHealth + stat = CONSCIOUS + return + health = maxHealth - (getOxyLoss() + getFireLoss() + getBruteLoss()) + update_stat("updatehealth([reason])") + handle_hud_icons_health() + diag_hud_set_health() + +/mob/living/silicon/robot/getBruteLoss(repairable_only = FALSE) + var/amount = 0 + for(var/V in components) + var/datum/robot_component/C = components[V] + if(C.installed != 0 && (!repairable_only || C.installed != -1)) // Installed ones only and if repair only remove the borked ones + amount += C.brute_damage + return amount + +/mob/living/silicon/robot/getFireLoss(repairable_only = FALSE) + var/amount = 0 + for(var/V in components) + var/datum/robot_component/C = components[V] + if(C.installed != 0 && (!repairable_only || C.installed != -1)) // Installed ones only and if repair only remove the borked ones + amount += C.electronics_damage + return amount + +/mob/living/silicon/robot/adjustBruteLoss(amount, updating_health = TRUE) + if(amount > 0) + take_overall_damage(amount, 0, updating_health) + else + heal_overall_damage(-amount, 0, updating_health) + return STATUS_UPDATE_HEALTH + +/mob/living/silicon/robot/adjustFireLoss(amount, updating_health = TRUE) + if(amount > 0) + take_overall_damage(0, amount, updating_health) + else + heal_overall_damage(0, -amount, updating_health) + return STATUS_UPDATE_HEALTH + +/mob/living/silicon/robot/proc/get_damaged_components(get_brute, get_burn, get_borked = FALSE, get_missing = FALSE) + var/list/datum/robot_component/parts = list() + for(var/V in components) + var/datum/robot_component/C = components[V] + if((C.installed == 1 || (get_borked && C.installed == -1) || (get_missing && C.installed == 0)) && ((get_brute && C.brute_damage) || (get_burn && C.electronics_damage))) + parts += C + return parts + +/mob/living/silicon/robot/proc/get_missing_components() + var/list/datum/robot_component/parts = list() + for(var/V in components) + var/datum/robot_component/C = components[V] + if(C.installed == 0) + parts += C + return parts + +/mob/living/silicon/robot/proc/get_damageable_components() + var/list/rval = new + for(var/V in components) + var/datum/robot_component/C = components[V] + if(C.installed == 1) + rval += C + return rval + +/mob/living/silicon/robot/proc/get_armour() + if(!LAZYLEN(components)) + return 0 + var/datum/robot_component/C = components["armour"] + if(C && C.installed == 1) + return C + return 0 + +/mob/living/silicon/robot/heal_organ_damage(brute, burn, updating_health = TRUE) + var/list/datum/robot_component/parts = get_damaged_components(brute, burn) + if(!LAZYLEN(parts)) + return + var/datum/robot_component/picked = pick(parts) + picked.heal_damage(brute, burn, updating_health) + +/mob/living/silicon/robot/take_organ_damage(brute = 0, burn = 0, updating_health = TRUE, sharp = 0, edge = 0) + var/list/components = get_damageable_components() + if(!LAZYLEN(components)) + return + + //Combat shielding absorbs a percentage of damage directly into the cell. + var/obj/item/borg/combat/shield/shield + if(module_state_1 && istype(module_state_1, /obj/item/borg/combat/shield)) + shield = module_state_1 + else if(module_state_2 && istype(module_state_2, /obj/item/borg/combat/shield)) + shield = module_state_2 + else if(module_state_3 && istype(module_state_3, /obj/item/borg/combat/shield)) + shield = module_state_3 + if(shield) + //Shields absorb a certain percentage of damage based on their power setting. + var/absorb_brute = brute * shield.shield_level + var/absorb_burn = burn * shield.shield_level + var/cost = (absorb_brute+absorb_burn) * 100 + + cell.charge -= cost + if(cell.charge <= 0) + cell.charge = 0 + to_chat(src, "Your shield has overloaded!") + else + brute -= absorb_brute + burn -= absorb_burn + to_chat(src, "Your shield absorbs some of the impact!") + + var/datum/robot_component/armour/A = get_armour() + if(A) + A.take_damage(brute, burn, sharp, updating_health) + return + + var/datum/robot_component/C = pick(components) + C.take_damage(brute, burn, sharp, updating_health) + +/mob/living/silicon/robot/heal_overall_damage(var/brute, var/burn, updating_health = TRUE) + var/list/datum/robot_component/parts = get_damaged_components(brute, burn) + + while(LAZYLEN(parts) && (brute > 0 || burn > 0) ) + var/datum/robot_component/picked = pick(parts) + + var/brute_was = picked.brute_damage + var/burn_was = picked.electronics_damage + + picked.heal_damage(brute,burn, updating_health) + + brute -= (brute_was - picked.brute_damage) + burn -= (burn_was - picked.electronics_damage) + + parts -= picked + + if(updating_health) + updatehealth("heal overall damage") + +/mob/living/silicon/robot/take_overall_damage(brute = 0, burn = 0, updating_health = TRUE, used_weapon = null, sharp = 0) + if(status_flags & GODMODE) return //godmode + var/list/datum/robot_component/parts = get_damageable_components() + + //Combat shielding absorbs a percentage of damage directly into the cell. + var/obj/item/borg/combat/shield/shield + if(module_state_1 && istype(module_state_1, /obj/item/borg/combat/shield)) + shield = module_state_1 + else if(module_state_2 && istype(module_state_2, /obj/item/borg/combat/shield)) + shield = module_state_2 + else if(module_state_3 && istype(module_state_3, /obj/item/borg/combat/shield)) + shield = module_state_3 + if(shield) + //Shields absorb a certain percentage of damage based on their power setting. + var/absorb_brute = brute * shield.shield_level + var/absorb_burn = burn * shield.shield_level + var/cost = (absorb_brute+absorb_burn) * 100 + + cell.charge -= cost + if(cell.charge <= 0) + cell.charge = 0 + to_chat(src, "Your shield has overloaded!") + else + brute -= absorb_brute + burn -= absorb_burn + to_chat(src, "Your shield absorbs some of the impact!") + + var/datum/robot_component/armour/A = get_armour() + if(A) + A.take_damage(brute, burn, sharp) + return + + while(LAZYLEN(parts) && (brute > 0 || burn > 0) ) + var/datum/robot_component/picked = pick(parts) + + var/brute_was = picked.brute_damage + var/burn_was = picked.electronics_damage + + picked.take_damage(brute, burn, sharp, FALSE) + + brute -= (picked.brute_damage - brute_was) + burn -= (picked.electronics_damage - burn_was) + + parts -= picked + updatehealth() diff --git a/code/modules/mob/living/silicon/robot/robot_items.dm b/code/modules/mob/living/silicon/robot/robot_items.dm index 083e379e444..92115986b2e 100644 --- a/code/modules/mob/living/silicon/robot/robot_items.dm +++ b/code/modules/mob/living/silicon/robot/robot_items.dm @@ -95,4 +95,4 @@ desc = "By retracting limbs and tucking in its head, a combat android can roll at high speeds." icon = 'icons/obj/decals.dmi' icon_state = "shock" - powerneeded = 25 \ No newline at end of file + powerneeded = 25 diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index 76c7f5bc1af..47dcb93cef5 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -1,580 +1,590 @@ -/obj/item/robot_module - name = "robot module" - icon = 'icons/obj/module.dmi' - icon_state = "std_module" - w_class = 100 - item_state = "electronic" - flags = CONDUCT - - var/list/modules = list() - var/obj/item/emag = null - var/list/subsystems = list() - var/list/module_actions = list() - - var/module_type = "NoMod" // For icon usage - - var/list/stacktypes - var/channels = list() - var/list/custom_removals = list() - - -/obj/item/robot_module/emp_act(severity) - if(modules) - for(var/obj/O in modules) - O.emp_act(severity) - if(emag) - emag.emp_act(severity) - ..() - - -/obj/item/robot_module/New() - modules += new /obj/item/flash/cyborg(src) - emag = new /obj/item/toy/sword(src) - emag.name = "Placeholder Emag Item" - -/obj/item/robot_module/Destroy() - QDEL_LIST(modules) - QDEL_NULL(emag) - return ..() - -/obj/item/robot_module/proc/fix_modules() - for(var/obj/item/I in modules) - I.flags |= NODROP - I.mouse_opacity = MOUSE_OPACITY_OPAQUE - if(emag) - emag.flags |= NODROP - emag.mouse_opacity = MOUSE_OPACITY_OPAQUE - -/obj/item/robot_module/proc/respawn_consumable(mob/living/silicon/robot/R) - if(!stacktypes || !stacktypes.len) - return - - var/stack_respawned = 0 - for(var/T in stacktypes) - var/O = locate(T) in modules - var/obj/item/stack/S = O - - if(!S) - S = new T(src) - modules += S - S.amount = 1 - stack_respawned = 1 - - if(S && S.amount < stacktypes[T]) - S.amount++ - if(stack_respawned && istype(R) && R.hud_used) - R.hud_used.update_robot_modules_display() - -/obj/item/robot_module/proc/rebuild()//Rebuilds the list so it's possible to add/remove items from the module - var/list/temp_list = modules - modules = list() - for(var/obj/O in temp_list) - if(O) - modules += O - -/obj/item/robot_module/proc/add_languages(mob/living/silicon/robot/R) - //full set of languages - R.add_language("Galactic Common", 1) - R.add_language("Sol Common", 1) - R.add_language("Tradeband", 1) - R.add_language("Gutter", 0) - R.add_language("Neo-Russkiya", 0) - R.add_language("Sinta'unathi", 0) - R.add_language("Siik'tajr", 0) - R.add_language("Canilunzt", 0) - R.add_language("Skrellian", 0) - R.add_language("Vox-pidgin", 0) - R.add_language("Rootspeak", 0) - R.add_language("Trinary", 1) - R.add_language("Chittin", 0) - R.add_language("Bubblish", 0) - R.add_language("Orluum", 0) - R.add_language("Clownish",0) - -/obj/item/robot_module/proc/add_subsystems_and_actions(mob/living/silicon/robot/R) - R.verbs |= subsystems - for(var/A in module_actions) - var/datum/action/act = new A() - act.Grant(R) - R.module_actions += act - -/obj/item/robot_module/proc/remove_subsystems_and_actions(mob/living/silicon/robot/R) - R.verbs -= subsystems - for(var/datum/action/A in R.module_actions) - A.Remove(R) - qdel(A) - R.module_actions.Cut() - -// Return true in an overridden subtype to prevent normal removal handling -/obj/item/robot_module/proc/handle_custom_removal(component_id, mob/living/user, obj/item/W, params) - return FALSE - -/obj/item/robot_module/proc/handle_death(gibbed) - return - -/obj/item/robot_module/standard - name = "standard robot module" - module_type = "Standard" - -/obj/item/robot_module/standard/New() - ..() - modules += new /obj/item/melee/baton/loaded(src) - modules += new /obj/item/extinguisher(src) - modules += new /obj/item/wrench/cyborg(src) - modules += new /obj/item/crowbar/cyborg(src) - modules += new /obj/item/healthanalyzer(src) - emag = new /obj/item/melee/energy/sword/cyborg(src) - - fix_modules() - -/obj/item/robot_module/medical - name = "medical robot module" - module_type = "Medical" - subsystems = list(/mob/living/silicon/proc/subsystem_crew_monitor) - stacktypes = list( - /obj/item/stack/medical/bruise_pack/advanced = 6, - /obj/item/stack/medical/ointment/advanced = 6, - /obj/item/stack/medical/splint = 6, - /obj/item/stack/nanopaste = 6 - ) - -/obj/item/robot_module/medical/New() - ..() - modules += new /obj/item/healthanalyzer/advanced(src) - modules += new /obj/item/robotanalyzer(src) - modules += new /obj/item/reagent_scanner/adv(src) - modules += new /obj/item/borg_defib(src) - modules += new /obj/item/handheld_defibrillator(src) - modules += new /obj/item/roller_holder(src) - modules += new /obj/item/reagent_containers/borghypo(src) - modules += new /obj/item/reagent_containers/glass/beaker/large(src) - modules += new /obj/item/reagent_containers/dropper(src) - modules += new /obj/item/reagent_containers/syringe(src) - modules += new /obj/item/extinguisher/mini(src) - modules += new /obj/item/stack/medical/bruise_pack/advanced(src) - modules += new /obj/item/stack/medical/ointment/advanced(src) - modules += new /obj/item/stack/medical/splint(src) - modules += new /obj/item/stack/nanopaste(src) - modules += new /obj/item/scalpel/laser/laser1(src) - modules += new /obj/item/hemostat(src) - modules += new /obj/item/retractor(src) - modules += new /obj/item/bonegel(src) - modules += new /obj/item/FixOVein(src) - modules += new /obj/item/bonesetter(src) - modules += new /obj/item/circular_saw(src) - modules += new /obj/item/surgicaldrill(src) - modules += new /obj/item/gripper/medical(src) - - emag = new /obj/item/reagent_containers/spray(src) - - emag.reagents.add_reagent("facid", 250) - emag.name = "Polyacid spray" - - fix_modules() - -/obj/item/robot_module/medical/respawn_consumable(mob/living/silicon/robot/R) - if(emag) - var/obj/item/reagent_containers/spray/PS = emag - PS.reagents.add_reagent("facid", 2) - ..() - -/obj/item/robot_module/engineering - name = "engineering robot module" - module_type = "Engineer" - subsystems = list(/mob/living/silicon/proc/subsystem_power_monitor) - module_actions = list( - /datum/action/innate/robot_sight/meson, - ) - - stacktypes = list( - /obj/item/stack/sheet/metal/cyborg = 50, - /obj/item/stack/sheet/glass/cyborg = 50, - /obj/item/stack/sheet/rglass/cyborg = 50, - /obj/item/stack/cable_coil/cyborg = 50, - /obj/item/stack/rods/cyborg = 60, - /obj/item/stack/tile/plasteel = 20 - ) - -/obj/item/robot_module/engineering/New() - ..() - modules += new /obj/item/rcd/borg(src) - modules += new /obj/item/rpd(src) - modules += new /obj/item/extinguisher(src) - modules += new /obj/item/weldingtool/largetank/cyborg(src) - modules += new /obj/item/screwdriver/cyborg(src) - modules += new /obj/item/wrench/cyborg(src) - modules += new /obj/item/crowbar/cyborg(src) - modules += new /obj/item/wirecutters/cyborg(src) - modules += new /obj/item/multitool/cyborg(src) - modules += new /obj/item/t_scanner(src) - modules += new /obj/item/analyzer(src) - modules += new /obj/item/holosign_creator/engineering(src) - modules += new /obj/item/gripper(src) - modules += new /obj/item/matter_decompiler(src) - modules += new /obj/item/floor_painter(src) - modules += new /obj/item/areaeditor/blueprints/cyborg(src) - emag = new /obj/item/borg/stun(src) - - for(var/G in stacktypes) //Attempt to unify Engi-Borg material stacks into fewer lines. See Line 492 for example. Variables changed out of paranoia. - var/obj/item/stack/sheet/M = new G(src) - M.amount = stacktypes[G] - modules += M - - fix_modules() - -/obj/item/robot_module/engineering/handle_death() - var/obj/item/gripper/G = locate(/obj/item/gripper) in modules - if(G) - G.drop_item() - -/obj/item/robot_module/security - name = "security robot module" - module_type = "Security" - subsystems = list(/mob/living/silicon/proc/subsystem_crew_monitor) - -/obj/item/robot_module/security/New() - ..() - modules += new /obj/item/restraints/handcuffs/cable/zipties/cyborg(src) - modules += new /obj/item/melee/baton/loaded(src) - modules += new /obj/item/gun/energy/disabler/cyborg(src) - modules += new /obj/item/holosign_creator/security(src) - modules += new /obj/item/clothing/mask/gas/sechailer/cyborg(src) - emag = new /obj/item/gun/energy/laser/cyborg(src) - - fix_modules() - -/obj/item/robot_module/janitor - name = "janitorial robot module" - module_type = "Janitor" - -/obj/item/robot_module/janitor/New() - ..() - modules += new /obj/item/soap/nanotrasen(src) - modules += new /obj/item/storage/bag/trash/cyborg(src) - modules += new /obj/item/mop/advanced/cyborg(src) - modules += new /obj/item/lightreplacer/cyborg(src) - modules += new /obj/item/holosign_creator(src) - emag = new /obj/item/reagent_containers/spray(src) - - emag.reagents.add_reagent("lube", 250) - emag.name = "Lube spray" - - fix_modules() - -/obj/item/robot_module/butler - name = "service robot module" - module_type = "Service" - -/obj/item/robot_module/butler/New() - ..() - modules += new /obj/item/reagent_containers/food/drinks/cans/beer(src) - modules += new /obj/item/reagent_containers/food/drinks/cans/cola(src) - modules += new /obj/item/reagent_containers/food/drinks/cans/sodawater(src) - modules += new /obj/item/reagent_containers/food/condiment/enzyme(src) - modules += new /obj/item/reagent_containers/food/drinks/bottle/orangejuice(src) // -0.3 oxy/sec - modules += new /obj/item/reagent_containers/food/drinks/bottle/tomatojuice(src) // -0.2 fire/sec - modules += new /obj/item/reagent_containers/food/drinks/bottle/limejuice(src) // -0.2 tox/sec - modules += new /obj/item/reagent_containers/food/drinks/coffee(src) // -1 paralysis stunned & weakened/sec - modules += new /obj/item/reagent_containers/food/drinks/tea(src) - modules += new /obj/item/reagent_containers/food/drinks/bottle/milk(src) // -0.2 brute/sec - modules += new /obj/item/reagent_containers/food/condiment/sugar(src) - modules += new /obj/item/reagent_containers/food/drinks/ice(src) - modules += new /obj/item/reagent_containers/food/drinks/bottle/cream(src) - - modules += new /obj/item/reagent_containers/food/drinks/bottle/tequila(src) - modules += new /obj/item/reagent_containers/food/drinks/bottle/vodka(src) - modules += new /obj/item/reagent_containers/food/drinks/bottle/whiskey(src) - - modules += new /obj/item/pen(src) - modules += new /obj/item/razor(src) - modules += new /obj/item/instrument/piano_synth(src) - modules += new /obj/item/healthanalyzer/advanced(src) - - var/obj/item/rsf/M = new /obj/item/rsf(src) - M.matter = 30 - modules += M - - modules += new /obj/item/reagent_containers/dropper/cyborg(src) - modules += new /obj/item/lighter/zippo(src) - modules += new /obj/item/storage/bag/tray/cyborg(src) - modules += new /obj/item/reagent_containers/food/drinks/shaker(src) - emag = new /obj/item/reagent_containers/food/drinks/cans/beer(src) - - var/datum/reagents/R = new/datum/reagents(50) - emag.reagents = R - R.my_atom = emag - R.add_reagent("beer2", 50) - emag.name = "Mickey Finn's Special Brew" - - fix_modules() - -/obj/item/robot_module/butler/respawn_consumable(var/mob/living/silicon/robot/R) - var/obj/item/reagent_containers/food/condiment/enzyme/E = locate() in modules - E.reagents.add_reagent("enzyme", 2) - if(emag) - var/obj/item/reagent_containers/food/drinks/cans/beer/B = emag - B.reagents.add_reagent("beer2", 2) - ..() - -/obj/item/robot_module/butler/add_languages(var/mob/living/silicon/robot/R) - //full set of languages - R.add_language("Galactic Common", 1) - R.add_language("Sol Common", 1) - R.add_language("Tradeband", 1) - R.add_language("Gutter", 1) - R.add_language("Sinta'unathi", 1) - R.add_language("Siik'tajr", 1) - R.add_language("Canilunzt", 1) - R.add_language("Skrellian", 1) - R.add_language("Vox-pidgin", 1) - R.add_language("Rootspeak", 1) - R.add_language("Trinary", 1) - R.add_language("Chittin", 1) - R.add_language("Bubblish", 1) - R.add_language("Clownish",1) - R.add_language("Neo-Russkiya", 1) - - -/obj/item/robot_module/miner - name = "miner robot module" - module_type = "Miner" - module_actions = list( - /datum/action/innate/robot_sight/meson, - ) - custom_removals = list("KA modkits") - -/obj/item/robot_module/miner/New() - ..() - modules += new /obj/item/storage/bag/ore/cyborg(src) - modules += new /obj/item/pickaxe/drill/cyborg(src) - modules += new /obj/item/shovel(src) - modules += new /obj/item/weldingtool/mini(src) - modules += new /obj/item/extinguisher/mini(src) - modules += new /obj/item/storage/bag/sheetsnatcher/borg(src) - modules += new /obj/item/t_scanner/adv_mining_scanner/cyborg(src) - modules += new /obj/item/gun/energy/kinetic_accelerator/cyborg(src) - modules += new /obj/item/gps/cyborg(src) - emag = new /obj/item/borg/stun(src) - - fix_modules() - -/obj/item/robot_module/miner/handle_custom_removal(component_id, mob/living/user, obj/item/W, params) - if(component_id == "KA modkits") - for(var/obj/item/gun/energy/kinetic_accelerator/cyborg/D in src) - D.attackby(W, user, params) - return TRUE - return ..() - -/obj/item/robot_module/deathsquad - name = "NT advanced combat module" - module_type = "Malf" - module_actions = list( - /datum/action/innate/robot_sight/thermal, - ) - -/obj/item/robot_module/deathsquad/New() - ..() - modules += new /obj/item/melee/energy/sword/cyborg(src) - modules += new /obj/item/gun/energy/pulse/cyborg(src) - modules += new /obj/item/crowbar(src) - emag = null - - fix_modules() - -/obj/item/robot_module/syndicate - name = "syndicate assault robot module" - module_type = "Malf" // cuz it looks cool - -/obj/item/robot_module/syndicate/New() - ..() - modules += new /obj/item/melee/energy/sword/cyborg(src) - modules += new /obj/item/gun/energy/printer(src) - modules += new /obj/item/gun/projectile/revolver/grenadelauncher/multi/cyborg(src) - modules += new /obj/item/card/emag(src) - modules += new /obj/item/crowbar/cyborg(src) - modules += new /obj/item/pinpointer/operative(src) - emag = null - - fix_modules() - -/obj/item/robot_module/syndicate_medical - name = "syndicate medical robot module" - module_type = "Malf" - stacktypes = list( - /obj/item/stack/medical/bruise_pack/advanced = 25, - /obj/item/stack/medical/ointment/advanced = 25, - /obj/item/stack/medical/splint = 25, - /obj/item/stack/nanopaste = 25 - ) - -/obj/item/robot_module/syndicate_medical/New() - ..() - modules += new /obj/item/healthanalyzer/advanced(src) - modules += new /obj/item/reagent_scanner/adv(src) - modules += new /obj/item/bodyanalyzer/borg/syndicate(src) - modules += new /obj/item/borg_defib(src) - modules += new /obj/item/handheld_defibrillator(src) - modules += new /obj/item/roller_holder(src) - modules += new /obj/item/reagent_containers/borghypo/syndicate(src) - modules += new /obj/item/extinguisher/mini(src) - modules += new /obj/item/stack/medical/bruise_pack/advanced(src) - modules += new /obj/item/stack/medical/ointment/advanced(src) - modules += new /obj/item/stack/medical/splint(src) - modules += new /obj/item/stack/nanopaste(src) - modules += new /obj/item/scalpel/laser/laser1(src) - modules += new /obj/item/hemostat(src) - modules += new /obj/item/retractor(src) - modules += new /obj/item/bonegel(src) - modules += new /obj/item/FixOVein(src) - modules += new /obj/item/bonesetter(src) - modules += new /obj/item/surgicaldrill(src) - modules += new /obj/item/gripper/medical(src) - modules += new /obj/item/gun/medbeam(src) - modules += new /obj/item/melee/energy/sword/cyborg/saw(src) //Energy saw -- primary weapon - modules += new /obj/item/card/emag(src) - modules += new /obj/item/crowbar/cyborg(src) - modules += new /obj/item/pinpointer/operative(src) - emag = null - - fix_modules() - -/obj/item/robot_module/syndicate_saboteur - name = "engineering robot module" //to disguise in examine - module_type = "Malf" - - stacktypes = list( - /obj/item/stack/sheet/metal/cyborg = 50, - /obj/item/stack/sheet/glass/cyborg = 50, - /obj/item/stack/sheet/rglass/cyborg = 50, - /obj/item/stack/cable_coil/cyborg = 50, - /obj/item/stack/rods/cyborg = 60, - /obj/item/stack/tile/plasteel = 20 - ) - -/obj/item/robot_module/syndicate_saboteur/New() - ..() - modules += new /obj/item/rcd/borg/syndicate(src) - modules += new /obj/item/rpd(src) - modules += new /obj/item/extinguisher(src) - modules += new /obj/item/weldingtool/largetank/cyborg(src) - modules += new /obj/item/screwdriver/cyborg(src) - modules += new /obj/item/wrench/cyborg(src) - modules += new /obj/item/crowbar/cyborg(src) - modules += new /obj/item/wirecutters/cyborg(src) - modules += new /obj/item/multitool/cyborg(src) - modules += new /obj/item/t_scanner(src) - modules += new /obj/item/analyzer(src) - modules += new /obj/item/gripper(src) - modules += new /obj/item/melee/energy/sword/cyborg(src) - modules += new /obj/item/card/emag(src) - modules += new /obj/item/borg_chameleon(src) - modules += new /obj/item/pinpointer/operative(src) - emag = null - - for(var/T in stacktypes) - var/obj/item/stack/sheet/W = new T(src) - W.amount = stacktypes[T] - modules += W - - fix_modules() - -/obj/item/robot_module/combat - name = "combat robot module" - module_type = "Malf" - module_actions = list( - /datum/action/innate/robot_sight/thermal, - ) - -/obj/item/robot_module/combat/New() - ..() - modules += new /obj/item/restraints/handcuffs/cable/zipties/cyborg(src) - modules += new /obj/item/gun/energy/gun/cyborg(src) - modules += new /obj/item/pickaxe/drill/jackhammer(src) - modules += new /obj/item/borg/combat/shield(src) - modules += new /obj/item/borg/combat/mobility(src) - modules += new /obj/item/wrench/cyborg(src) - emag = new /obj/item/gun/energy/lasercannon/cyborg(src) - - fix_modules() - -/obj/item/robot_module/alien/hunter - name = "alien hunter module" - module_type = "Standard" - module_actions = list( - /datum/action/innate/robot_sight/thermal/alien, - ) - -/obj/item/robot_module/alien/hunter/New() - modules += new /obj/item/melee/energy/alien/claws(src) - modules += new /obj/item/flash/cyborg/alien(src) - var/obj/item/reagent_containers/spray/alien/stun/S = new /obj/item/reagent_containers/spray/alien/stun(src) - S.reagents.add_reagent("ether",250) //nerfed to sleeptoxin to make it less instant drop. - modules += S - var/obj/item/reagent_containers/spray/alien/smoke/A = new /obj/item/reagent_containers/spray/alien/smoke(src) - S.reagents.add_reagent("water",50) //Water is used as a dummy reagent for the smoke bombs. More of an ammo counter. - modules += A - emag = new /obj/item/reagent_containers/spray/alien/acid(src) - emag.reagents.add_reagent("facid", 125) - emag.reagents.add_reagent("sacid", 125) - - fix_modules() - -/obj/item/robot_module/alien/hunter/add_languages(var/mob/living/silicon/robot/R) - ..() - R.add_language("xenocommon", 1) - -/obj/item/robot_module/drone - name = "drone module" - module_type = "Engineer" - stacktypes = list( - /obj/item/stack/sheet/wood = 10, - /obj/item/stack/sheet/rglass/cyborg = 50, - /obj/item/stack/tile/wood = 20, - /obj/item/stack/rods/cyborg = 60, - /obj/item/stack/tile/plasteel = 20, - /obj/item/stack/sheet/metal/cyborg = 50, - /obj/item/stack/sheet/glass/cyborg = 50, - /obj/item/stack/cable_coil/cyborg = 30 - ) - -/obj/item/robot_module/drone/New() - modules += new /obj/item/weldingtool/largetank/cyborg(src) - modules += new /obj/item/screwdriver/cyborg(src) - modules += new /obj/item/wrench/cyborg(src) - modules += new /obj/item/crowbar/cyborg(src) - modules += new /obj/item/wirecutters/cyborg(src) - modules += new /obj/item/multitool/cyborg(src) - modules += new /obj/item/lightreplacer/cyborg(src) - modules += new /obj/item/gripper(src) - modules += new /obj/item/matter_decompiler(src) - modules += new /obj/item/reagent_containers/spray/cleaner/drone(src) - modules += new /obj/item/soap(src) - modules += new /obj/item/t_scanner(src) - modules += new /obj/item/rpd(src) - - for(var/T in stacktypes) - var/obj/item/stack/sheet/W = new T(src) - W.amount = stacktypes[T] - modules += W - - fix_modules() - -/obj/item/robot_module/drone/respawn_consumable(mob/living/silicon/robot/R) - var/obj/item/reagent_containers/spray/cleaner/C = locate() in modules - C.reagents.add_reagent("cleaner", 3) - ..() - - -/obj/item/robot_module/drone/handle_death() - var/obj/item/gripper/G = locate(/obj/item/gripper) in modules - if(G) - G.drop_item() - -//checks whether this item is a module of the robot it is located in. -/obj/item/proc/is_robot_module() - if(!istype(loc, /mob/living/silicon/robot)) - return 0 - - var/mob/living/silicon/robot/R = loc - - return (src in R.module.modules) +/obj/item/robot_module + name = "robot module" + icon = 'icons/obj/module.dmi' + icon_state = "std_module" + w_class = 100 + item_state = "electronic" + flags = CONDUCT + + var/list/modules = list() + var/obj/item/emag = null + var/list/subsystems = list() + var/list/module_actions = list() + + var/module_type = "NoMod" // For icon usage + + var/list/stacktypes + var/channels = list() + var/list/custom_removals = list() + + +/obj/item/robot_module/emp_act(severity) + if(modules) + for(var/obj/O in modules) + O.emp_act(severity) + if(emag) + emag.emp_act(severity) + ..() + + +/obj/item/robot_module/New() + ..() + add_default_robot_items() + emag = new /obj/item/toy/sword(src) + emag.name = "Placeholder Emag Item" + +/obj/item/robot_module/Destroy() + QDEL_LIST(modules) + QDEL_NULL(emag) + return ..() + +// By default, all robots will get the items in this proc, unless you override it for your specific module. See: ../robot_module/drone +/obj/item/robot_module/proc/add_default_robot_items() + modules += new /obj/item/flash/cyborg(src) + +/obj/item/robot_module/proc/fix_modules() + for(var/obj/item/I in modules) + I.flags |= NODROP + I.mouse_opacity = MOUSE_OPACITY_OPAQUE + if(emag) + emag.flags |= NODROP + emag.mouse_opacity = MOUSE_OPACITY_OPAQUE + +/obj/item/robot_module/proc/respawn_consumable(mob/living/silicon/robot/R) + if(!stacktypes || !stacktypes.len) + return + + var/stack_respawned = 0 + for(var/T in stacktypes) + var/O = locate(T) in modules + var/obj/item/stack/S = O + + if(!S) + S = new T(src) + modules += S + S.amount = 1 + stack_respawned = 1 + + if(S && S.amount < stacktypes[T]) + S.amount++ + if(stack_respawned && istype(R) && R.hud_used) + R.hud_used.update_robot_modules_display() + +/obj/item/robot_module/proc/rebuild()//Rebuilds the list so it's possible to add/remove items from the module + var/list/temp_list = modules + modules = list() + for(var/obj/O in temp_list) + if(O) + modules += O + +/obj/item/robot_module/proc/add_languages(mob/living/silicon/robot/R) + //full set of languages + R.add_language("Galactic Common", 1) + R.add_language("Sol Common", 1) + R.add_language("Tradeband", 1) + R.add_language("Gutter", 0) + R.add_language("Neo-Russkiya", 0) + R.add_language("Sinta'unathi", 0) + R.add_language("Siik'tajr", 0) + R.add_language("Canilunzt", 0) + R.add_language("Skrellian", 0) + R.add_language("Vox-pidgin", 0) + R.add_language("Rootspeak", 0) + R.add_language("Trinary", 1) + R.add_language("Chittin", 0) + R.add_language("Bubblish", 0) + R.add_language("Orluum", 0) + R.add_language("Clownish",0) + +/obj/item/robot_module/proc/add_subsystems_and_actions(mob/living/silicon/robot/R) + R.verbs |= subsystems + for(var/A in module_actions) + var/datum/action/act = new A() + act.Grant(R) + R.module_actions += act + +/obj/item/robot_module/proc/remove_subsystems_and_actions(mob/living/silicon/robot/R) + R.verbs -= subsystems + for(var/datum/action/A in R.module_actions) + A.Remove(R) + qdel(A) + R.module_actions.Cut() + +// Return true in an overridden subtype to prevent normal removal handling +/obj/item/robot_module/proc/handle_custom_removal(component_id, mob/living/user, obj/item/W) + return FALSE + +/obj/item/robot_module/proc/handle_death(gibbed) + return + +/obj/item/robot_module/standard + name = "standard robot module" + module_type = "Standard" + +/obj/item/robot_module/standard/New() + ..() + modules += new /obj/item/melee/baton/loaded(src) + modules += new /obj/item/extinguisher(src) + modules += new /obj/item/wrench/cyborg(src) + modules += new /obj/item/crowbar/cyborg(src) + modules += new /obj/item/healthanalyzer(src) + emag = new /obj/item/melee/energy/sword/cyborg(src) + + fix_modules() + +/obj/item/robot_module/medical + name = "medical robot module" + module_type = "Medical" + subsystems = list(/mob/living/silicon/proc/subsystem_crew_monitor) + stacktypes = list( + /obj/item/stack/medical/bruise_pack/advanced = 6, + /obj/item/stack/medical/ointment/advanced = 6, + /obj/item/stack/medical/splint = 6, + /obj/item/stack/nanopaste = 6 + ) + +/obj/item/robot_module/medical/New() + ..() + modules += new /obj/item/healthanalyzer/advanced(src) + modules += new /obj/item/robotanalyzer(src) + modules += new /obj/item/reagent_scanner/adv(src) + modules += new /obj/item/borg_defib(src) + modules += new /obj/item/handheld_defibrillator(src) + modules += new /obj/item/roller_holder(src) + modules += new /obj/item/reagent_containers/borghypo(src) + modules += new /obj/item/reagent_containers/glass/beaker/large(src) + modules += new /obj/item/reagent_containers/dropper(src) + modules += new /obj/item/reagent_containers/syringe(src) + modules += new /obj/item/extinguisher/mini(src) + modules += new /obj/item/stack/medical/bruise_pack/advanced(src) + modules += new /obj/item/stack/medical/ointment/advanced(src) + modules += new /obj/item/stack/medical/splint(src) + modules += new /obj/item/stack/nanopaste(src) + modules += new /obj/item/scalpel/laser/laser1(src) + modules += new /obj/item/hemostat(src) + modules += new /obj/item/retractor(src) + modules += new /obj/item/bonegel(src) + modules += new /obj/item/FixOVein(src) + modules += new /obj/item/bonesetter(src) + modules += new /obj/item/circular_saw(src) + modules += new /obj/item/surgicaldrill(src) + modules += new /obj/item/gripper/medical(src) + + emag = new /obj/item/reagent_containers/spray(src) + + emag.reagents.add_reagent("facid", 250) + emag.name = "Polyacid spray" + + fix_modules() + +/obj/item/robot_module/medical/respawn_consumable(mob/living/silicon/robot/R) + if(emag) + var/obj/item/reagent_containers/spray/PS = emag + PS.reagents.add_reagent("facid", 2) + ..() + +/obj/item/robot_module/engineering + name = "engineering robot module" + module_type = "Engineer" + subsystems = list(/mob/living/silicon/proc/subsystem_power_monitor) + module_actions = list( + /datum/action/innate/robot_sight/meson, + ) + + stacktypes = list( + /obj/item/stack/sheet/metal/cyborg = 50, + /obj/item/stack/sheet/glass/cyborg = 50, + /obj/item/stack/sheet/rglass/cyborg = 50, + /obj/item/stack/cable_coil/cyborg = 50, + /obj/item/stack/rods/cyborg = 60, + /obj/item/stack/tile/plasteel = 20 + ) + +/obj/item/robot_module/engineering/New() + ..() + modules += new /obj/item/rcd/borg(src) + modules += new /obj/item/rpd(src) + modules += new /obj/item/extinguisher(src) + modules += new /obj/item/weldingtool/largetank/cyborg(src) + modules += new /obj/item/screwdriver/cyborg(src) + modules += new /obj/item/wrench/cyborg(src) + modules += new /obj/item/crowbar/cyborg(src) + modules += new /obj/item/wirecutters/cyborg(src) + modules += new /obj/item/multitool/cyborg(src) + modules += new /obj/item/t_scanner(src) + modules += new /obj/item/analyzer(src) + modules += new /obj/item/holosign_creator/engineering(src) + modules += new /obj/item/gripper(src) + modules += new /obj/item/matter_decompiler(src) + modules += new /obj/item/floor_painter(src) + modules += new /obj/item/areaeditor/blueprints/cyborg(src) + emag = new /obj/item/borg/stun(src) + + for(var/G in stacktypes) //Attempt to unify Engi-Borg material stacks into fewer lines. See Line 492 for example. Variables changed out of paranoia. + var/obj/item/stack/sheet/M = new G(src) + M.amount = stacktypes[G] + modules += M + + fix_modules() + +/obj/item/robot_module/engineering/handle_death() + var/obj/item/gripper/G = locate(/obj/item/gripper) in modules + if(G) + G.drop_item() + +/obj/item/robot_module/security + name = "security robot module" + module_type = "Security" + subsystems = list(/mob/living/silicon/proc/subsystem_crew_monitor) + +/obj/item/robot_module/security/New() + ..() + modules += new /obj/item/restraints/handcuffs/cable/zipties/cyborg(src) + modules += new /obj/item/melee/baton/loaded(src) + modules += new /obj/item/gun/energy/disabler/cyborg(src) + modules += new /obj/item/holosign_creator/security(src) + modules += new /obj/item/clothing/mask/gas/sechailer/cyborg(src) + emag = new /obj/item/gun/energy/laser/cyborg(src) + + fix_modules() + +/obj/item/robot_module/janitor + name = "janitorial robot module" + module_type = "Janitor" + +/obj/item/robot_module/janitor/New() + ..() + modules += new /obj/item/soap/nanotrasen(src) + modules += new /obj/item/storage/bag/trash/cyborg(src) + modules += new /obj/item/mop/advanced/cyborg(src) + modules += new /obj/item/lightreplacer/cyborg(src) + modules += new /obj/item/holosign_creator(src) + emag = new /obj/item/reagent_containers/spray(src) + + emag.reagents.add_reagent("lube", 250) + emag.name = "Lube spray" + + fix_modules() + +/obj/item/robot_module/butler + name = "service robot module" + module_type = "Service" + +/obj/item/robot_module/butler/New() + ..() + modules += new /obj/item/reagent_containers/food/drinks/cans/beer(src) + modules += new /obj/item/reagent_containers/food/drinks/cans/cola(src) + modules += new /obj/item/reagent_containers/food/drinks/cans/sodawater(src) + modules += new /obj/item/reagent_containers/food/condiment/enzyme(src) + modules += new /obj/item/reagent_containers/food/drinks/bottle/orangejuice(src) // -0.3 oxy/sec + modules += new /obj/item/reagent_containers/food/drinks/bottle/tomatojuice(src) // -0.2 fire/sec + modules += new /obj/item/reagent_containers/food/drinks/bottle/limejuice(src) // -0.2 tox/sec + modules += new /obj/item/reagent_containers/food/drinks/coffee(src) // -1 paralysis stunned & weakened/sec + modules += new /obj/item/reagent_containers/food/drinks/tea(src) + modules += new /obj/item/reagent_containers/food/drinks/bottle/milk(src) // -0.2 brute/sec + modules += new /obj/item/reagent_containers/food/condiment/sugar(src) + modules += new /obj/item/reagent_containers/food/drinks/ice(src) + modules += new /obj/item/reagent_containers/food/drinks/bottle/cream(src) + + modules += new /obj/item/reagent_containers/food/drinks/bottle/tequila(src) + modules += new /obj/item/reagent_containers/food/drinks/bottle/vodka(src) + modules += new /obj/item/reagent_containers/food/drinks/bottle/whiskey(src) + + modules += new /obj/item/pen(src) + modules += new /obj/item/razor(src) + modules += new /obj/item/instrument/piano_synth(src) + modules += new /obj/item/healthanalyzer/advanced(src) + + var/obj/item/rsf/M = new /obj/item/rsf(src) + M.matter = 30 + modules += M + + modules += new /obj/item/reagent_containers/dropper/cyborg(src) + modules += new /obj/item/lighter/zippo(src) + modules += new /obj/item/storage/bag/tray/cyborg(src) + modules += new /obj/item/reagent_containers/food/drinks/shaker(src) + emag = new /obj/item/reagent_containers/food/drinks/cans/beer(src) + + var/datum/reagents/R = new/datum/reagents(50) + emag.reagents = R + R.my_atom = emag + R.add_reagent("beer2", 50) + emag.name = "Mickey Finn's Special Brew" + + fix_modules() + +/obj/item/robot_module/butler/respawn_consumable(var/mob/living/silicon/robot/R) + var/obj/item/reagent_containers/food/condiment/enzyme/E = locate() in modules + E.reagents.add_reagent("enzyme", 2) + if(emag) + var/obj/item/reagent_containers/food/drinks/cans/beer/B = emag + B.reagents.add_reagent("beer2", 2) + ..() + +/obj/item/robot_module/butler/add_languages(var/mob/living/silicon/robot/R) + //full set of languages + R.add_language("Galactic Common", 1) + R.add_language("Sol Common", 1) + R.add_language("Tradeband", 1) + R.add_language("Gutter", 1) + R.add_language("Sinta'unathi", 1) + R.add_language("Siik'tajr", 1) + R.add_language("Canilunzt", 1) + R.add_language("Skrellian", 1) + R.add_language("Vox-pidgin", 1) + R.add_language("Rootspeak", 1) + R.add_language("Trinary", 1) + R.add_language("Chittin", 1) + R.add_language("Bubblish", 1) + R.add_language("Clownish",1) + R.add_language("Neo-Russkiya", 1) + + +/obj/item/robot_module/miner + name = "miner robot module" + module_type = "Miner" + module_actions = list( + /datum/action/innate/robot_sight/meson, + ) + custom_removals = list("KA modkits") + +/obj/item/robot_module/miner/New() + ..() + modules += new /obj/item/storage/bag/ore/cyborg(src) + modules += new /obj/item/pickaxe/drill/cyborg(src) + modules += new /obj/item/shovel(src) + modules += new /obj/item/weldingtool/mini(src) + modules += new /obj/item/extinguisher/mini(src) + modules += new /obj/item/storage/bag/sheetsnatcher/borg(src) + modules += new /obj/item/t_scanner/adv_mining_scanner/cyborg(src) + modules += new /obj/item/gun/energy/kinetic_accelerator/cyborg(src) + modules += new /obj/item/gps/cyborg(src) + emag = new /obj/item/borg/stun(src) + + fix_modules() + +/obj/item/robot_module/miner/handle_custom_removal(component_id, mob/living/user, obj/item/W) + if(component_id == "KA modkits") + for(var/obj/item/gun/energy/kinetic_accelerator/cyborg/D in src) + D.attackby(W, user) + return TRUE + return ..() + +/obj/item/robot_module/deathsquad + name = "NT advanced combat module" + module_type = "Malf" + module_actions = list( + /datum/action/innate/robot_sight/thermal, + ) + +/obj/item/robot_module/deathsquad/New() + ..() + modules += new /obj/item/melee/energy/sword/cyborg(src) + modules += new /obj/item/gun/energy/pulse/cyborg(src) + modules += new /obj/item/crowbar(src) + emag = null + + fix_modules() + +/obj/item/robot_module/syndicate + name = "syndicate assault robot module" + module_type = "Malf" // cuz it looks cool + +/obj/item/robot_module/syndicate/New() + ..() + modules += new /obj/item/melee/energy/sword/cyborg(src) + modules += new /obj/item/gun/energy/printer(src) + modules += new /obj/item/gun/projectile/revolver/grenadelauncher/multi/cyborg(src) + modules += new /obj/item/card/emag(src) + modules += new /obj/item/crowbar/cyborg(src) + modules += new /obj/item/pinpointer/operative(src) + emag = null + + fix_modules() + +/obj/item/robot_module/syndicate_medical + name = "syndicate medical robot module" + module_type = "Malf" + stacktypes = list( + /obj/item/stack/medical/bruise_pack/advanced = 25, + /obj/item/stack/medical/ointment/advanced = 25, + /obj/item/stack/medical/splint = 25, + /obj/item/stack/nanopaste = 25 + ) + +/obj/item/robot_module/syndicate_medical/New() + ..() + modules += new /obj/item/healthanalyzer/advanced(src) + modules += new /obj/item/reagent_scanner/adv(src) + modules += new /obj/item/bodyanalyzer/borg/syndicate(src) + modules += new /obj/item/borg_defib(src) + modules += new /obj/item/handheld_defibrillator(src) + modules += new /obj/item/roller_holder(src) + modules += new /obj/item/reagent_containers/borghypo/syndicate(src) + modules += new /obj/item/extinguisher/mini(src) + modules += new /obj/item/stack/medical/bruise_pack/advanced(src) + modules += new /obj/item/stack/medical/ointment/advanced(src) + modules += new /obj/item/stack/medical/splint(src) + modules += new /obj/item/stack/nanopaste(src) + modules += new /obj/item/scalpel/laser/laser1(src) + modules += new /obj/item/hemostat(src) + modules += new /obj/item/retractor(src) + modules += new /obj/item/bonegel(src) + modules += new /obj/item/FixOVein(src) + modules += new /obj/item/bonesetter(src) + modules += new /obj/item/surgicaldrill(src) + modules += new /obj/item/gripper/medical(src) + modules += new /obj/item/gun/medbeam(src) + modules += new /obj/item/melee/energy/sword/cyborg/saw(src) //Energy saw -- primary weapon + modules += new /obj/item/card/emag(src) + modules += new /obj/item/crowbar/cyborg(src) + modules += new /obj/item/pinpointer/operative(src) + emag = null + + fix_modules() + +/obj/item/robot_module/syndicate_saboteur + name = "engineering robot module" //to disguise in examine + module_type = "Malf" + + stacktypes = list( + /obj/item/stack/sheet/metal/cyborg = 50, + /obj/item/stack/sheet/glass/cyborg = 50, + /obj/item/stack/sheet/rglass/cyborg = 50, + /obj/item/stack/cable_coil/cyborg = 50, + /obj/item/stack/rods/cyborg = 60, + /obj/item/stack/tile/plasteel = 20 + ) + +/obj/item/robot_module/syndicate_saboteur/New() + ..() + modules += new /obj/item/rcd/borg/syndicate(src) + modules += new /obj/item/rpd(src) + modules += new /obj/item/extinguisher(src) + modules += new /obj/item/weldingtool/largetank/cyborg(src) + modules += new /obj/item/screwdriver/cyborg(src) + modules += new /obj/item/wrench/cyborg(src) + modules += new /obj/item/crowbar/cyborg(src) + modules += new /obj/item/wirecutters/cyborg(src) + modules += new /obj/item/multitool/cyborg(src) + modules += new /obj/item/t_scanner(src) + modules += new /obj/item/analyzer(src) + modules += new /obj/item/gripper(src) + modules += new /obj/item/melee/energy/sword/cyborg(src) + modules += new /obj/item/card/emag(src) + modules += new /obj/item/borg_chameleon(src) + modules += new /obj/item/pinpointer/operative(src) + emag = null + + for(var/T in stacktypes) + var/obj/item/stack/sheet/W = new T(src) + W.amount = stacktypes[T] + modules += W + + fix_modules() + +/obj/item/robot_module/combat + name = "combat robot module" + module_type = "Malf" + module_actions = list( + /datum/action/innate/robot_sight/thermal, + ) + +/obj/item/robot_module/combat/New() + ..() + modules += new /obj/item/restraints/handcuffs/cable/zipties/cyborg(src) + modules += new /obj/item/gun/energy/gun/cyborg(src) + modules += new /obj/item/pickaxe/drill/jackhammer(src) + modules += new /obj/item/borg/combat/shield(src) + modules += new /obj/item/borg/combat/mobility(src) + modules += new /obj/item/wrench/cyborg(src) + emag = new /obj/item/gun/energy/lasercannon/cyborg(src) + + fix_modules() + +/obj/item/robot_module/alien/hunter + name = "alien hunter module" + module_type = "Standard" + module_actions = list( + /datum/action/innate/robot_sight/thermal/alien, + ) + +/obj/item/robot_module/alien/hunter/New() + ..() + modules += new /obj/item/melee/energy/alien/claws(src) + modules += new /obj/item/flash/cyborg/alien(src) + var/obj/item/reagent_containers/spray/alien/stun/S = new /obj/item/reagent_containers/spray/alien/stun(src) + S.reagents.add_reagent("ether",250) //nerfed to sleeptoxin to make it less instant drop. + modules += S + var/obj/item/reagent_containers/spray/alien/smoke/A = new /obj/item/reagent_containers/spray/alien/smoke(src) + S.reagents.add_reagent("water",50) //Water is used as a dummy reagent for the smoke bombs. More of an ammo counter. + modules += A + emag = new /obj/item/reagent_containers/spray/alien/acid(src) + emag.reagents.add_reagent("facid", 125) + emag.reagents.add_reagent("sacid", 125) + + fix_modules() + +/obj/item/robot_module/alien/hunter/add_languages(var/mob/living/silicon/robot/R) + ..() + R.add_language("xenocommon", 1) + +/obj/item/robot_module/drone + name = "drone module" + module_type = "Engineer" + stacktypes = list( + /obj/item/stack/sheet/wood = 10, + /obj/item/stack/sheet/rglass/cyborg = 50, + /obj/item/stack/tile/wood = 20, + /obj/item/stack/rods/cyborg = 60, + /obj/item/stack/tile/plasteel = 20, + /obj/item/stack/sheet/metal/cyborg = 50, + /obj/item/stack/sheet/glass/cyborg = 50, + /obj/item/stack/cable_coil/cyborg = 30 + ) + +/obj/item/robot_module/drone/New() + ..() + modules += new /obj/item/weldingtool/largetank/cyborg(src) + modules += new /obj/item/screwdriver/cyborg(src) + modules += new /obj/item/wrench/cyborg(src) + modules += new /obj/item/crowbar/cyborg(src) + modules += new /obj/item/wirecutters/cyborg(src) + modules += new /obj/item/multitool/cyborg(src) + modules += new /obj/item/lightreplacer/cyborg(src) + modules += new /obj/item/gripper(src) + modules += new /obj/item/matter_decompiler(src) + modules += new /obj/item/reagent_containers/spray/cleaner/drone(src) + modules += new /obj/item/soap(src) + modules += new /obj/item/t_scanner(src) + modules += new /obj/item/rpd(src) + + for(var/T in stacktypes) + var/obj/item/stack/sheet/W = new T(src) + W.amount = stacktypes[T] + modules += W + + fix_modules() + +/obj/item/robot_module/drone/add_default_robot_items() + return + +/obj/item/robot_module/drone/respawn_consumable(mob/living/silicon/robot/R) + var/obj/item/reagent_containers/spray/cleaner/C = locate() in modules + C.reagents.add_reagent("cleaner", 3) + ..() + + +/obj/item/robot_module/drone/handle_death() + var/obj/item/gripper/G = locate(/obj/item/gripper) in modules + if(G) + G.drop_item() + +//checks whether this item is a module of the robot it is located in. +/obj/item/proc/is_robot_module() + if(!istype(loc, /mob/living/silicon/robot)) + return 0 + + var/mob/living/silicon/robot/R = loc + + return (src in R.module.modules) diff --git a/code/modules/mob/living/silicon/robot/robot_movement.dm b/code/modules/mob/living/silicon/robot/robot_movement.dm index e896e7c9473..4b645b02849 100644 --- a/code/modules/mob/living/silicon/robot/robot_movement.dm +++ b/code/modules/mob/living/silicon/robot/robot_movement.dm @@ -21,4 +21,4 @@ /mob/living/silicon/robot/experience_pressure_difference(pressure_difference, direction) if(!magpulse) - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/mob/living/silicon/robot/syndicate.dm b/code/modules/mob/living/silicon/robot/syndicate.dm index f97b1f5c005..2b5fd9bb702 100644 --- a/code/modules/mob/living/silicon/robot/syndicate.dm +++ b/code/modules/mob/living/silicon/robot/syndicate.dm @@ -7,7 +7,7 @@ faction = list("syndicate") designation = "Syndicate Assault" modtype = "Syndicate" - req_access = list(access_syndicate) + req_access = list(ACCESS_SYNDICATE) ionpulse = 1 lawchannel = "State" var/playstyle_string = "You are a Syndicate assault cyborg!
        \ diff --git a/code/modules/mob/living/silicon/say.dm b/code/modules/mob/living/silicon/say.dm index 8092947919f..f75475fa92d 100644 --- a/code/modules/mob/living/silicon/say.dm +++ b/code/modules/mob/living/silicon/say.dm @@ -1,115 +1,115 @@ -/mob/living/silicon/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) - log_say(multilingual_to_message(message_pieces), src) - if(..()) - return 1 - -/mob/living/silicon/robot/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) - if(..()) - return 1 - if(message_mode) - used_radios += radio - if(!is_component_functioning("radio")) - to_chat(src, "Your radio isn't functional at this time.") - return 0 - if(message_mode == "general") - message_mode = null - return radio.talk_into(src,message_pieces,message_mode,verb) - -/mob/living/silicon/ai/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) - if(..()) - return 1 - if(message_mode == "department") - used_radios += aiRadio - return holopad_talk(message_pieces, verb) - else if(message_mode) - used_radios += aiRadio - if(aiRadio.disabledAi || aiRestorePowerRoutine || stat) - to_chat(src, "System Error - Transceiver Disabled.") - return 0 - if(message_mode == "general") - message_mode = null - return aiRadio.talk_into(src, message_pieces, message_mode, verb) - -/mob/living/silicon/pai/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) - if(..()) - return 1 - else if(message_mode == "whisper") - whisper_say(message_pieces) - return 1 - else if(message_mode) - if(message_mode == "general") - message_mode = null - used_radios += radio - return radio.talk_into(src, message_pieces, message_mode, verb) - -/mob/living/silicon/say_quote(var/text) - var/ending = copytext(text, length(text)) - - if(ending == "?") - return speak_query - else if(ending == "!") - return speak_exclamation - - return speak_statement - -#define IS_AI 1 -#define IS_ROBOT 2 -#define IS_PAI 3 - -/mob/living/silicon/say_understands(var/other,var/datum/language/speaking = null) - //These only pertain to common. Languages are handled by mob/say_understands() - if(!speaking) - if(istype(other, /mob/living/carbon)) - return 1 - if(istype(other, /mob/living/silicon)) - return 1 - if(istype(other, /mob/living/simple_animal/bot)) - return 1 - if(istype(other, /mob/living/carbon/brain)) - return 1 - return ..() - -//For holopads only. Usable by AI. -/mob/living/silicon/ai/proc/holopad_talk(list/message_pieces, verb) - log_say("(HPAD) [multilingual_to_message(message_pieces)]", src) - - var/obj/machinery/hologram/holopad/T = current - if(istype(T) && T.masters[src]) - for(var/mob/M in hearers(T.loc))//The location is the object, default distance. - M.hear_holopad_talk(message_pieces, verb, src) - to_chat(src, "Holopad transmitted, [real_name] [combine_message(message_pieces, verb, src)]") - else - to_chat(src, "No holopad connected.") - return - return 1 - -/mob/living/silicon/ai/proc/holopad_emote(var/message) //This is called when the AI uses the 'me' verb while using a holopad. - message = trim(message) - - if(!message) - return - - var/obj/machinery/hologram/holopad/T = current - if(istype(T) && T.masters[src]) - var/rendered = "[name] [message]" - to_chat(src, "Holopad action relayed, [real_name] [message]") - - for(var/mob/M in viewers(T.loc)) - M.show_message(rendered, 2) - - log_emote("(HPAD) [message]", src) - else //This shouldn't occur, but better safe then sorry. - to_chat(src, "No holopad connected.") - return - return 1 - -/mob/living/silicon/ai/emote(act, type, message, force) - var/obj/machinery/hologram/holopad/T = current - if(istype(T) && T.masters[src])//Is the AI using a holopad? - src.holopad_emote(message) - else //Emote normally, then. - ..() - -#undef IS_AI -#undef IS_ROBOT -#undef IS_PAI +/mob/living/silicon/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) + log_say(multilingual_to_message(message_pieces), src) + if(..()) + return 1 + +/mob/living/silicon/robot/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) + if(..()) + return 1 + if(message_mode) + used_radios += radio + if(!is_component_functioning("radio")) + to_chat(src, "Your radio isn't functional at this time.") + return 0 + if(message_mode == "general") + message_mode = null + return radio.talk_into(src,message_pieces,message_mode,verb) + +/mob/living/silicon/ai/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) + if(..()) + return 1 + if(message_mode == "department") + used_radios += aiRadio + return holopad_talk(message_pieces, verb) + else if(message_mode) + used_radios += aiRadio + if(aiRadio.disabledAi || aiRestorePowerRoutine || stat) + to_chat(src, "System Error - Transceiver Disabled.") + return 0 + if(message_mode == "general") + message_mode = null + return aiRadio.talk_into(src, message_pieces, message_mode, verb) + +/mob/living/silicon/pai/handle_message_mode(message_mode, list/message_pieces, verb, used_radios) + if(..()) + return 1 + else if(message_mode == "whisper") + whisper_say(message_pieces) + return 1 + else if(message_mode) + if(message_mode == "general") + message_mode = null + used_radios += radio + return radio.talk_into(src, message_pieces, message_mode, verb) + +/mob/living/silicon/say_quote(var/text) + var/ending = copytext(text, length(text)) + + if(ending == "?") + return speak_query + else if(ending == "!") + return speak_exclamation + + return speak_statement + +#define IS_AI 1 +#define IS_ROBOT 2 +#define IS_PAI 3 + +/mob/living/silicon/say_understands(var/other,var/datum/language/speaking = null) + //These only pertain to common. Languages are handled by mob/say_understands() + if(!speaking) + if(istype(other, /mob/living/carbon)) + return 1 + if(istype(other, /mob/living/silicon)) + return 1 + if(istype(other, /mob/living/simple_animal/bot)) + return 1 + if(istype(other, /mob/living/carbon/brain)) + return 1 + return ..() + +//For holopads only. Usable by AI. +/mob/living/silicon/ai/proc/holopad_talk(list/message_pieces, verb) + log_say("(HPAD) [multilingual_to_message(message_pieces)]", src) + + var/obj/machinery/hologram/holopad/T = current + if(istype(T) && T.masters[src]) + for(var/mob/M in hearers(T.loc))//The location is the object, default distance. + M.hear_holopad_talk(message_pieces, verb, src) + to_chat(src, "Holopad transmitted, [real_name] [combine_message(message_pieces, verb, src)]") + else + to_chat(src, "No holopad connected.") + return + return 1 + +/mob/living/silicon/ai/proc/holopad_emote(var/message) //This is called when the AI uses the 'me' verb while using a holopad. + message = trim(message) + + if(!message) + return + + var/obj/machinery/hologram/holopad/T = current + if(istype(T) && T.masters[src]) + var/rendered = "[name] [message]" + to_chat(src, "Holopad action relayed, [real_name] [message]") + + for(var/mob/M in viewers(T.loc)) + M.show_message(rendered, 2) + + log_emote("(HPAD) [message]", src) + else //This shouldn't occur, but better safe then sorry. + to_chat(src, "No holopad connected.") + return + return 1 + +/mob/living/silicon/ai/emote(act, type, message, force) + var/obj/machinery/hologram/holopad/T = current + if(istype(T) && T.masters[src])//Is the AI using a holopad? + src.holopad_emote(message) + else //Emote normally, then. + ..() + +#undef IS_AI +#undef IS_ROBOT +#undef IS_PAI diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index d2713ef2936..c7a93368433 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -100,6 +100,25 @@ /mob/living/silicon/IsAdvancedToolUser() return TRUE +/mob/living/silicon/robot/welder_act(mob/user, obj/item/I) + if(user.a_intent != INTENT_HELP) + return + if(user == src) //No self-repair dummy + return + . = TRUE + if(!getBruteLoss()) + to_chat(user, "Nothing to fix!") + return + else if(!getBruteLoss(TRUE)) + to_chat(user, "The damaged components are beyond saving!") + return + if(!I.use_tool(src, user, volume = I.tool_volume)) + return + adjustBruteLoss(-30) + add_fingerprint(user) + user.visible_message("[user] patches some dents on [src] with [I].") + + /mob/living/silicon/bullet_act(var/obj/item/projectile/Proj) diff --git a/code/modules/mob/living/simple_animal/animal_defense.dm b/code/modules/mob/living/simple_animal/animal_defense.dm index 6e94accb100..2cfc5e746dc 100644 --- a/code/modules/mob/living/simple_animal/animal_defense.dm +++ b/code/modules/mob/living/simple_animal/animal_defense.dm @@ -124,4 +124,4 @@ visual_effect_icon = ATTACK_EFFECT_PUNCH else visual_effect_icon = ATTACK_EFFECT_SMASH - ..() \ No newline at end of file + ..() diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm index 1fba341fd59..98f5a3555f4 100644 --- a/code/modules/mob/living/simple_animal/bot/bot.dm +++ b/code/modules/mob/living/simple_animal/bot/bot.dm @@ -143,10 +143,10 @@ icon_dead = icon_state access_card = new /obj/item/card/id(src) //This access is so bots can be immediately set to patrol and leave Robotics, instead of having to be let out first. - access_card.access += access_robotics + access_card.access += ACCESS_ROBOTICS set_custom_texts() Radio = new/obj/item/radio/headset/bot(src) - + Radio.follow_target = src add_language("Galactic Common", 1) add_language("Sol Common", 1) add_language("Tradeband", 1) @@ -357,25 +357,25 @@ user.visible_message("[user] uses [W] to pull [paicard] out of [bot_name]!","You pull [paicard] out of [bot_name] with [W].") ejectpai(user) else - user.changeNext_move(CLICK_CD_MELEE) - if(istype(W, /obj/item/weldingtool) && user.a_intent != INTENT_HARM) - if(health >= maxHealth) - to_chat(user, "[src] does not need a repair!") - return - if(!open) - to_chat(user, "Unable to repair with the maintenance panel closed!") - return - var/obj/item/weldingtool/WT = W - if(WT.remove_fuel(0, user)) - adjustHealth(-10) - add_fingerprint(user) - user.visible_message("[user] repairs [src]!","You repair [src].") - else - to_chat(user, "The welder must be on for this task!") - else - if(W.force) //if force is non-zero - do_sparks(5, 1, src) - ..() + return ..() + +/mob/living/simple_animal/bot/welder_act(mob/user, obj/item/I) + if(user.a_intent != INTENT_HELP) + return + if(user == src) //No self-repair dummy + return + . = TRUE + if(health >= maxHealth) + to_chat(user, "[src] does not need a repair!") + return + if(!open) + to_chat(user, "Unable to repair with the maintenance panel closed!") + return + if(!I.use_tool(src, user, volume = I.tool_volume)) + return + adjustBruteLoss(-10) + add_fingerprint(user) + user.visible_message("[user] repairs [src]!","You repair [src].") /mob/living/simple_animal/bot/bullet_act(obj/item/projectile/Proj) if(Proj && (Proj.damage_type == BRUTE || Proj.damage_type == BURN)) @@ -1109,4 +1109,4 @@ Pass a positive integer as an argument to override a bot's default speed. path.Cut(1, 2) /mob/living/simple_animal/bot/proc/drop_part(obj/item/drop_item, dropzone) - new drop_item(dropzone) \ No newline at end of file + new drop_item(dropzone) diff --git a/code/modules/mob/living/simple_animal/bot/cleanbot.dm b/code/modules/mob/living/simple_animal/bot/cleanbot.dm index 0c52b1517f3..f9747238138 100644 --- a/code/modules/mob/living/simple_animal/bot/cleanbot.dm +++ b/code/modules/mob/living/simple_animal/bot/cleanbot.dm @@ -187,7 +187,7 @@ ..() /obj/machinery/bot_core/cleanbot - req_one_access = list(access_janitor, access_robotics) + req_one_access = list(ACCESS_JANITOR, ACCESS_ROBOTICS) /mob/living/simple_animal/bot/cleanbot/get_controls(mob/user) diff --git a/code/modules/mob/living/simple_animal/bot/construction.dm b/code/modules/mob/living/simple_animal/bot/construction.dm index 5feebb53059..27ecd8e1569 100644 --- a/code/modules/mob/living/simple_animal/bot/construction.dm +++ b/code/modules/mob/living/simple_animal/bot/construction.dm @@ -96,12 +96,10 @@ var/robot_arm = /obj/item/robot_parts/l_arm icon_state = "[lasercolor]ed209_shell" if(3) - if(istype(W, /obj/item/weldingtool)) - var/obj/item/weldingtool/WT = W - if(WT.remove_fuel(0,user)) - build_step++ - name = "shielded frame assembly" - to_chat(user, "You weld the vest to [src].") + if(W.tool_behaviour == TOOL_WELDER && W.use_tool(src, user, volume = W.tool_volume)) + build_step++ + name = "shielded frame assembly" + to_chat(user, "You weld the vest to [src].") if(4) switch(lasercolor) if("b") @@ -342,7 +340,7 @@ var/robot_arm = /obj/item/robot_parts/l_arm icon = 'icons/obj/aibots.dmi' icon_state = "firstaid_arm" w_class = WEIGHT_CLASS_NORMAL - req_one_access = list(access_medical, access_robotics) + req_one_access = list(ACCESS_MEDICAL, ACCESS_ROBOTICS) var/build_step = 0 var/created_name = "Medibot" //To preserve the name if it's a unique medbot I guess var/skin = null //Same as medbot, set to tox or ointment for the respective kits. @@ -440,19 +438,15 @@ var/robot_arm = /obj/item/robot_parts/l_arm /obj/item/secbot_assembly/attackby(obj/item/I, mob/user, params) ..() - if(istype(I, /obj/item/weldingtool)) + if(I.tool_behaviour == TOOL_WELDER && I.use_tool(src, user, volume = I.tool_volume)) if(!build_step) - var/obj/item/weldingtool/WT = I - if(WT.remove_fuel(0, user)) - build_step++ - overlays += "hs_hole" - to_chat(user, "You weld a hole in [src]!") + build_step++ + overlays += "hs_hole" + to_chat(user, "You weld a hole in [src]!") else if(build_step == 1) - var/obj/item/weldingtool/WT = I - if(WT.remove_fuel(0, user)) - build_step-- - overlays -= "hs_hole" - to_chat(user, "You weld the hole in [src] shut!") + build_step-- + overlays -= "hs_hole" + to_chat(user, "You weld the hole in [src] shut!") else if(isprox(I) && (build_step == 1)) if(!user.unEquip(I)) @@ -599,7 +593,7 @@ var/robot_arm = /obj/item/robot_parts/l_arm icon = 'icons/obj/aibots.dmi' icon_state = "honkbot_arm" w_class = WEIGHT_CLASS_NORMAL - req_one_access = list(access_clown, access_robotics, access_mime) + req_one_access = list(ACCESS_CLOWN, ACCESS_ROBOTICS, ACCESS_MIME) var/build_step = 0 var/created_name = "Honkbot" //To preserve the name if it's a unique medbot I guess @@ -629,4 +623,4 @@ var/robot_arm = /obj/item/robot_parts/l_arm qdel(W) var/mob/living/simple_animal/bot/honkbot/A = new /mob/living/simple_animal/bot/honkbot(get_turf(src)) A.robot_arm = robot_arm - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/modules/mob/living/simple_animal/bot/ed209bot.dm b/code/modules/mob/living/simple_animal/bot/ed209bot.dm index c457f90c3f5..410f9715325 100644 --- a/code/modules/mob/living/simple_animal/bot/ed209bot.dm +++ b/code/modules/mob/living/simple_animal/bot/ed209bot.dm @@ -59,7 +59,7 @@ check_records = 0//Don't actively target people set to arrest arrest_type = 1//Don't even try to cuff declare_arrests = 0 // Don't spam sec - bot_core.req_access = list(access_maint_tunnels, access_theatre, access_robotics) + bot_core.req_access = list(ACCESS_MAINT_TUNNELS, ACCESS_THEATRE, ACCESS_ROBOTICS) if(created_name == initial(name) || !created_name) if(lasercolor == "b") diff --git a/code/modules/mob/living/simple_animal/bot/floorbot.dm b/code/modules/mob/living/simple_animal/bot/floorbot.dm index 21967bd202b..5b0ea28a72a 100644 --- a/code/modules/mob/living/simple_animal/bot/floorbot.dm +++ b/code/modules/mob/living/simple_animal/bot/floorbot.dm @@ -415,7 +415,7 @@ ..() /obj/machinery/bot_core/floorbot - req_one_access = list(access_construction, access_robotics) + req_one_access = list(ACCESS_CONSTRUCTION, ACCESS_ROBOTICS) /mob/living/simple_animal/bot/floorbot/UnarmedAttack(atom/A) if(isturf(A)) diff --git a/code/modules/mob/living/simple_animal/bot/griefsky.dm b/code/modules/mob/living/simple_animal/bot/griefsky.dm index 4dc50fca8f8..8b0878f7115 100644 --- a/code/modules/mob/living/simple_animal/bot/griefsky.dm +++ b/code/modules/mob/living/simple_animal/bot/griefsky.dm @@ -35,7 +35,7 @@ locked = 0 /obj/machinery/bot_core/toy - req_access = list(access_maint_tunnels, access_theatre, access_robotics) + req_access = list(ACCESS_MAINT_TUNNELS, ACCESS_THEATRE, ACCESS_ROBOTICS) /mob/living/simple_animal/bot/secbot/griefsky/proc/spam_flag_false() //used for addtimer to not spam comms spam_flag = 0 @@ -240,4 +240,4 @@ else return ..() else - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/mob/living/simple_animal/bot/honkbot.dm b/code/modules/mob/living/simple_animal/bot/honkbot.dm index 3499ae06649..d9097a82d2e 100644 --- a/code/modules/mob/living/simple_animal/bot/honkbot.dm +++ b/code/modules/mob/living/simple_animal/bot/honkbot.dm @@ -31,7 +31,7 @@ var/arrest_type = FALSE /obj/machinery/bot_core/honkbot - req_one_access = list(access_clown, access_robotics, access_mime) + req_one_access = list(ACCESS_CLOWN, ACCESS_ROBOTICS, ACCESS_MIME) /mob/living/simple_animal/bot/honkbot/Initialize() . = ..() @@ -317,4 +317,4 @@ speak("Honk!") sensor_blink() return - ..() \ No newline at end of file + ..() diff --git a/code/modules/mob/living/simple_animal/bot/medbot.dm b/code/modules/mob/living/simple_animal/bot/medbot.dm index 9507b91e4f0..b7696236e38 100644 --- a/code/modules/mob/living/simple_animal/bot/medbot.dm +++ b/code/modules/mob/living/simple_animal/bot/medbot.dm @@ -530,7 +530,7 @@ if(inject_beaker) if(use_beaker && reagent_glass && reagent_glass.reagents.total_volume) var/fraction = min(injection_amount/reagent_glass.reagents.total_volume, 1) - reagent_glass.reagents.reaction(patient, INGEST, fraction) + reagent_glass.reagents.reaction(patient, REAGENT_INGEST, fraction) reagent_glass.reagents.trans_to(patient, injection_amount) //Inject from beaker instead. else patient.reagents.add_reagent(reagent_id,injection_amount) @@ -615,7 +615,7 @@ declare_cooldown = 0 /obj/machinery/bot_core/medbot - req_one_access = list(access_medical, access_robotics) + req_one_access = list(ACCESS_MEDICAL, ACCESS_ROBOTICS) /obj/machinery/bot_core/medbot/syndicate - req_one_access = list(access_syndicate) + req_one_access = list(ACCESS_SYNDICATE) diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm index 2c8e56f3fbe..c89b160bf0e 100644 --- a/code/modules/mob/living/simple_animal/bot/mulebot.dm +++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm @@ -63,6 +63,7 @@ mulebot_count++ set_suffix(suffix ? suffix : "#[mulebot_count]") + RegisterSignal(src, COMSIG_CROSSED_MOVABLE, .proc/human_squish_check) /mob/living/simple_animal/bot/mulebot/Destroy() unload(0) @@ -866,9 +867,14 @@ else ..() +/mob/living/simple_animal/bot/mulebot/proc/human_squish_check(src, atom/movable/AM) + if(!ishuman(AM)) + return + RunOver(AM) + #undef SIGH #undef ANNOYED #undef DELIGHT /obj/machinery/bot_core/mulebot - req_access = list(access_cargo) + req_access = list(ACCESS_CARGO) diff --git a/code/modules/mob/living/simple_animal/bot/secbot.dm b/code/modules/mob/living/simple_animal/bot/secbot.dm index b5d63c8c9ca..eb629680a61 100644 --- a/code/modules/mob/living/simple_animal/bot/secbot.dm +++ b/code/modules/mob/living/simple_animal/bot/secbot.dm @@ -456,4 +456,4 @@ Auto Patrol: []"}, ..() /obj/machinery/bot_core/secbot - req_access = list(access_security) + req_access = list(ACCESS_SECURITY) diff --git a/code/modules/mob/living/simple_animal/bot/syndicate.dm b/code/modules/mob/living/simple_animal/bot/syndicate.dm index 6aa15ba0270..3993339f714 100644 --- a/code/modules/mob/living/simple_animal/bot/syndicate.dm +++ b/code/modules/mob/living/simple_animal/bot/syndicate.dm @@ -33,7 +33,7 @@ /mob/living/simple_animal/bot/ed209/syndicate/setup_access() if(access_card) - access_card.access = list(access_syndicate, access_syndicate_leader) + access_card.access = list(ACCESS_SYNDICATE, ACCESS_SYNDICATE_LEADER) prev_access = access_card.access /mob/living/simple_animal/bot/ed209/syndicate/update_icon() @@ -211,4 +211,4 @@ return raised_alert = TRUE if(depotarea) - depotarea.increase_alert(reason) \ No newline at end of file + depotarea.increase_alert(reason) diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm index 9dce0cecb62..ee03c204d48 100644 --- a/code/modules/mob/living/simple_animal/constructs.dm +++ b/code/modules/mob/living/simple_animal/constructs.dm @@ -1,405 +1,405 @@ - -/mob/living/simple_animal/hostile/construct - name = "Construct" - real_name = "Construct" - desc = "" - speak_emote = list("hisses") - emote_hear = list("wails","screeches") - response_help = "thinks better of touching" - response_disarm = "flails at" - response_harm = "punches" - icon_dead = "shade_dead" - speed = 0 - a_intent = INTENT_HARM - stop_automated_movement = 1 - status_flags = CANPUSH - attack_sound = 'sound/weapons/punch1.ogg' - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - faction = list("cult") - flying = 1 - pressure_resistance = 100 - universal_speak = 1 - AIStatus = AI_OFF //normal constructs don't have AI - var/const_type = "shade" - var/list/construct_spells = list() - var/playstyle_string = "You are a generic construct! Your job is to not exist, and you should probably adminhelp this." - loot = list(/obj/item/reagent_containers/food/snacks/ectoplasm) - del_on_death = 1 - deathmessage = "collapses in a shattered heap." - -/mob/living/simple_animal/hostile/construct/New() - . = ..() - if(!SSticker.mode)//work around for maps with runes and cultdat is not loaded all the way - name = "[const_type] ([rand(1, 1000)])" - real_name = const_type - icon_living = const_type - icon_state = const_type - else - name = "[SSticker.cultdat.get_name(const_type)] ([rand(1, 1000)])" - real_name = SSticker.cultdat.get_name(const_type) - icon_living = SSticker.cultdat.get_icon(const_type) - icon_state = SSticker.cultdat.get_icon(const_type) - - for(var/spell in construct_spells) - AddSpell(new spell(null)) - - if(SSticker.cultdat?.theme == "blood") - updateglow() - -/mob/living/simple_animal/hostile/construct/death(gibbed) - . = ..() - SSticker.mode.remove_cultist(src.mind, FALSE) - -/mob/living/simple_animal/hostile/construct/examine(mob/user) - . = ..() - - var/msg = "" - if(src.health < src.maxHealth) - msg += "" - if(src.health >= src.maxHealth/2) - msg += "It looks slightly dented.\n" - else - msg += "It looks severely dented!\n" - msg += "" - msg += "*---------*" - - . += msg - -/mob/living/simple_animal/hostile/construct/attack_animal(mob/living/simple_animal/M) - if(istype(M, /mob/living/simple_animal/hostile/construct/builder)) - if(health < maxHealth) - adjustBruteLoss(-5) - if(src != M) - Beam(M,icon_state="sendbeam",time=4) - M.visible_message("[M] repairs some of \the [src]'s dents.", \ - "You repair some of [src]'s dents, leaving [src] at [health]/[maxHealth] health.") - else - M.visible_message("[M] repairs some of its own dents.", \ - "You repair some of your own dents, leaving you at [M.health]/[M.maxHealth] health.") - else - if(src != M) - to_chat(M, "You cannot repair [src]'s dents, as it has none!") - else - to_chat(M, "You cannot repair your own dents, as you have none!") - else if(src != M) - return ..() - - -/mob/living/simple_animal/hostile/construct/narsie_act() - return - -/mob/living/simple_animal/hostile/construct/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE) - return FALSE - -/////////////////Juggernaut/////////////// - - - -/mob/living/simple_animal/hostile/construct/armoured - name = "Juggernaut" - real_name = "Juggernaut" - desc = "A possessed suit of armour driven by the will of the restless dead" - icon = 'icons/mob/mob.dmi' - icon_state = "behemoth" - icon_living = "behemoth" - maxHealth = 250 - health = 250 - response_harm = "harmlessly punches" - harm_intent_damage = 0 - obj_damage = 90 - melee_damage_lower = 30 - melee_damage_upper = 30 - attacktext = "smashes their armoured gauntlet into" - speed = 3 - environment_smash = 2 - attack_sound = 'sound/weapons/punch3.ogg' - status_flags = 0 - const_type = "juggernaut" - mob_size = MOB_SIZE_LARGE - construct_spells = list(/obj/effect/proc_holder/spell/targeted/night_vision, /obj/effect/proc_holder/spell/aoe_turf/conjure/lesserforcewall) - force_threshold = 11 - playstyle_string = "You are a Juggernaut. Though slow, your shell can withstand extreme punishment, \ - create shield walls, rip apart enemies and walls alike, and even deflect energy weapons." - - -/mob/living/simple_animal/hostile/construct/armoured/hostile //actually hostile, will move around, hit things - AIStatus = AI_ON - environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP - -/mob/living/simple_animal/hostile/construct/armoured/bullet_act(var/obj/item/projectile/P) - if(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam)) - var/reflectchance = 80 - round(P.damage/3) - if(prob(reflectchance)) - if((P.damage_type == BRUTE || P.damage_type == BURN)) - adjustBruteLoss(P.damage * 0.5) - visible_message("The [P.name] gets reflected by [src]'s shell!", \ - "The [P.name] gets reflected by [src]'s shell!") - - P.reflect_back(src, list(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3)) - - return -1 // complete projectile permutation - - return (..(P)) - - - -////////////////////////Wraith///////////////////////////////////////////// - - - -/mob/living/simple_animal/hostile/construct/wraith - name = "Wraith" - real_name = "Wraith" - desc = "A wicked bladed shell contraption piloted by a bound spirit" - icon = 'icons/mob/mob.dmi' - icon_state = "floating" - icon_living = "floating" - maxHealth = 75 - health = 75 - melee_damage_lower = 25 - melee_damage_upper = 25 - attacktext = "slashes" - see_in_dark = 8 - attack_sound = 'sound/weapons/bladeslice.ogg' - const_type = "wraith" - construct_spells = list(/obj/effect/proc_holder/spell/targeted/night_vision, /obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift) - retreat_distance = 2 //AI wraiths will move in and out of combat - playstyle_string = "You are a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls." - -/mob/living/simple_animal/hostile/construct/wraith/hostile //actually hostile, will move around, hit things - AIStatus = AI_ON - -/////////////////////////////Artificer///////////////////////// - - - -/mob/living/simple_animal/hostile/construct/builder - name = "Artificer" - real_name = "Artificer" - desc = "A bulbous construct dedicated to building and maintaining Cult armies." - icon = 'icons/mob/mob.dmi' - icon_state = "artificer" - icon_living = "artificer" - maxHealth = 50 - health = 50 - response_harm = "viciously beats" - harm_intent_damage = 5 - obj_damage = 60 - melee_damage_lower = 5 - melee_damage_upper = 5 - attacktext = "rams" - environment_smash = 2 - retreat_distance = 10 - minimum_distance = 10 //AI artificers will flee like fuck - attack_sound = 'sound/weapons/punch2.ogg' - const_type = "builder" - construct_spells = list(/obj/effect/proc_holder/spell/targeted/night_vision, - /obj/effect/proc_holder/spell/aoe_turf/conjure/construct/lesser, - /obj/effect/proc_holder/spell/aoe_turf/conjure/wall, - /obj/effect/proc_holder/spell/aoe_turf/conjure/floor, - /obj/effect/proc_holder/spell/aoe_turf/conjure/pylon, - /obj/effect/proc_holder/spell/aoe_turf/conjure/soulstone, - /obj/effect/proc_holder/spell/targeted/projectile/magic_missile/lesser) - - playstyle_string = "You are an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, \ - use magic missile, repair allied constructs (by clicking on them), \ - and, most important of all, create new constructs by producing soulstones to capture souls, \ - and shells to place those soulstones into." - - -/mob/living/simple_animal/hostile/construct/builder/Found(atom/A) //what have we found here? - if(isconstruct(A)) //is it a construct? - var/mob/living/simple_animal/hostile/construct/C = A - if(C.health < C.maxHealth) //is it hurt? let's go heal it if it is - return 1 - else - return 0 - else - return 0 - -/mob/living/simple_animal/hostile/construct/builder/CanAttack(atom/the_target) - if(see_invisible < the_target.invisibility)//Target's invisible to us, forget it - return 0 - if(Found(the_target) || ..()) //If we Found it or Can_Attack it normally, we Can_Attack it as long as it wasn't invisible - return 1 //as a note this shouldn't be added to base hostile mobs because it'll mess up retaliate hostile mobs - -/mob/living/simple_animal/hostile/construct/builder/MoveToTarget(var/list/possible_targets) - ..() - if(isliving(target)) - var/mob/living/L = target - if(isconstruct(L) && L.health >= L.maxHealth) //is this target an unhurt construct? stop trying to heal it - LoseTarget() - return 0 - if(L.health <= melee_damage_lower+melee_damage_upper) //ey bucko you're hurt as fuck let's go hit you - retreat_distance = null - minimum_distance = 1 - -/mob/living/simple_animal/hostile/construct/builder/Aggro() - ..() - if(isconstruct(target)) //oh the target is a construct no need to flee - retreat_distance = null - minimum_distance = 1 - -/mob/living/simple_animal/hostile/construct/builder/LoseAggro() - ..() - retreat_distance = initial(retreat_distance) - minimum_distance = initial(minimum_distance) - -/mob/living/simple_animal/hostile/construct/builder/hostile //actually hostile, will move around, hit things, heal other constructs - AIStatus = AI_ON - environment_smash = ENVIRONMENT_SMASH_STRUCTURES //only token destruction, don't smash the cult wall NO STOP - - -/////////////////////////////Behemoth///////////////////////// - - -/mob/living/simple_animal/hostile/construct/behemoth - name = "Behemoth" - real_name = "Behemoth" - desc = "The pinnacle of occult technology, Behemoths are the ultimate weapon in the Cult of Nar-Sie's arsenal." - icon = 'icons/mob/mob.dmi' - icon_state = "behemoth" - icon_living = "behemoth" - maxHealth = 750 - health = 750 - speak_emote = list("rumbles") - response_harm = "harmlessly punches" - harm_intent_damage = 0 - melee_damage_lower = 50 - melee_damage_upper = 50 - attacktext = "brutally crushes" - speed = 5 - environment_smash = 2 - attack_sound = 'sound/weapons/punch4.ogg' - force_threshold = 11 - const_type = "behemoth" - var/energy = 0 - var/max_energy = 1000 - -/mob/living/simple_animal/hostile/construct/behemoth/hostile //actually hostile, will move around, hit things - AIStatus = AI_ON - environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP - -/mob/living/simple_animal/hostile/construct/behemoth/Life(seconds, times_fired) - weakened = 0 - return ..() - - -/////////////////////////////Harvester///////////////////////// - -/mob/living/simple_animal/hostile/construct/harvester - name = "Harvester" - real_name = "Harvester" - desc = "A harbinger of Nar-Sie's enlightenment. It'll be all over soon." - icon = 'icons/mob/mob.dmi' - icon_state = "harvester" - icon_living = "harvester" - maxHealth = 60 - health = 60 - melee_damage_lower = 1 - melee_damage_upper = 5 - attacktext = "prods" - environment_smash = ENVIRONMENT_SMASH_RWALLS - see_in_dark = 8 - attack_sound = 'sound/weapons/tap.ogg' - const_type = "harvester" - construct_spells = list(/obj/effect/proc_holder/spell/targeted/night_vision, - /obj/effect/proc_holder/spell/aoe_turf/conjure/wall, - /obj/effect/proc_holder/spell/aoe_turf/conjure/floor, - /obj/effect/proc_holder/spell/targeted/smoke/disable) - retreat_distance = 2 //AI harvesters will move in and out of combat, like wraiths, but shittier - playstyle_string = "You are a Harvester. You are not strong, but your powers of domination will assist you in your role: \ - Bring those who still cling to this world of illusion back to the master so they may know Truth." - - -/mob/living/simple_animal/hostile/construct/harvester/Process_Spacemove(var/movement_dir = 0) - return 1 - - -/mob/living/simple_animal/hostile/construct/harvester/hostile //actually hostile, will move around, hit things - AIStatus = AI_ON - environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP - -////////////////Glow//////////////////// -/mob/living/simple_animal/hostile/construct/proc/updateglow() - overlays = 0 - var/overlay_layer = LIGHTING_LAYER + 1 - if(layer != MOB_LAYER) - overlay_layer=TURF_LAYER+0.2 - - overlays += image(icon,"glow-[icon_state]",overlay_layer) - set_light(2, -2, l_color = "#FFFFFF") - -///ui stuff - -/mob/living/simple_animal/hostile/construct/armoured/handle_hud_icons_health() - ..() - if(healths) - switch(health) - if(250 to INFINITY) healths.icon_state = "juggernaut_health0" - if(208 to 249) healths.icon_state = "juggernaut_health1" - if(167 to 207) healths.icon_state = "juggernaut_health2" - if(125 to 166) healths.icon_state = "juggernaut_health3" - if(84 to 124) healths.icon_state = "juggernaut_health4" - if(42 to 83) healths.icon_state = "juggernaut_health5" - if(1 to 41) healths.icon_state = "juggernaut_health6" - else healths.icon_state = "juggernaut_health7" - - -/mob/living/simple_animal/hostile/construct/behemoth/handle_hud_icons_health() - ..() - if(healths) - switch(health) - if(750 to INFINITY) healths.icon_state = "juggernaut_health0" - if(625 to 749) healths.icon_state = "juggernaut_health1" - if(500 to 624) healths.icon_state = "juggernaut_health2" - if(375 to 499) healths.icon_state = "juggernaut_health3" - if(250 to 374) healths.icon_state = "juggernaut_health4" - if(125 to 249) healths.icon_state = "juggernaut_health5" - if(1 to 124) healths.icon_state = "juggernaut_health6" - else healths.icon_state = "juggernaut_health7" - -/mob/living/simple_animal/hostile/construct/builder/handle_hud_icons_health() - ..() - if(healths) - switch(health) - if(50 to INFINITY) healths.icon_state = "artificer_health0" - if(42 to 49) healths.icon_state = "artificer_health1" - if(34 to 41) healths.icon_state = "artificer_health2" - if(26 to 33) healths.icon_state = "artificer_health3" - if(18 to 25) healths.icon_state = "artificer_health4" - if(10 to 17) healths.icon_state = "artificer_health5" - if(1 to 9) healths.icon_state = "artificer_health6" - else healths.icon_state = "artificer_health7" - - - -/mob/living/simple_animal/hostile/construct/wraith/handle_hud_icons_health() - - ..() - if(healths) - switch(health) - if(75 to INFINITY) healths.icon_state = "wraith_health0" - if(62 to 74) healths.icon_state = "wraith_health1" - if(50 to 61) healths.icon_state = "wraith_health2" - if(37 to 49) healths.icon_state = "wraith_health3" - if(25 to 36) healths.icon_state = "wraith_health4" - if(12 to 24) healths.icon_state = "wraith_health5" - if(1 to 11) healths.icon_state = "wraith_health6" - else healths.icon_state = "wraith_health7" - - -/mob/living/simple_animal/hostile/construct/harvester/handle_hud_icons_health() - - ..() - if(healths) - switch(health) - if(150 to INFINITY) healths.icon_state = "harvester_health0" - if(125 to 149) healths.icon_state = "harvester_health1" - if(100 to 124) healths.icon_state = "harvester_health2" - if(75 to 99) healths.icon_state = "harvester_health3" - if(50 to 74) healths.icon_state = "harvester_health4" - if(25 to 49) healths.icon_state = "harvester_health5" - if(1 to 24) healths.icon_state = "harvester_health6" - else healths.icon_state = "harvester_health7" \ No newline at end of file + +/mob/living/simple_animal/hostile/construct + name = "Construct" + real_name = "Construct" + desc = "" + speak_emote = list("hisses") + emote_hear = list("wails","screeches") + response_help = "thinks better of touching" + response_disarm = "flails at" + response_harm = "punches" + icon_dead = "shade_dead" + speed = 0 + a_intent = INTENT_HARM + stop_automated_movement = 1 + status_flags = CANPUSH + attack_sound = 'sound/weapons/punch1.ogg' + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + faction = list("cult") + flying = 1 + pressure_resistance = 100 + universal_speak = 1 + AIStatus = AI_OFF //normal constructs don't have AI + var/const_type = "shade" + var/list/construct_spells = list() + var/playstyle_string = "You are a generic construct! Your job is to not exist, and you should probably adminhelp this." + loot = list(/obj/item/reagent_containers/food/snacks/ectoplasm) + del_on_death = 1 + deathmessage = "collapses in a shattered heap." + +/mob/living/simple_animal/hostile/construct/New() + . = ..() + if(!SSticker.mode)//work around for maps with runes and cultdat is not loaded all the way + name = "[const_type] ([rand(1, 1000)])" + real_name = const_type + icon_living = const_type + icon_state = const_type + else + name = "[SSticker.cultdat.get_name(const_type)] ([rand(1, 1000)])" + real_name = SSticker.cultdat.get_name(const_type) + icon_living = SSticker.cultdat.get_icon(const_type) + icon_state = SSticker.cultdat.get_icon(const_type) + + for(var/spell in construct_spells) + AddSpell(new spell(null)) + + if(SSticker.cultdat?.theme == "blood") + updateglow() + +/mob/living/simple_animal/hostile/construct/death(gibbed) + . = ..() + SSticker.mode.remove_cultist(src.mind, FALSE) + +/mob/living/simple_animal/hostile/construct/examine(mob/user) + . = ..() + + var/msg = "" + if(src.health < src.maxHealth) + msg += "" + if(src.health >= src.maxHealth/2) + msg += "It looks slightly dented.\n" + else + msg += "It looks severely dented!\n" + msg += "" + msg += "*---------*" + + . += msg + +/mob/living/simple_animal/hostile/construct/attack_animal(mob/living/simple_animal/M) + if(istype(M, /mob/living/simple_animal/hostile/construct/builder)) + if(health < maxHealth) + adjustBruteLoss(-5) + if(src != M) + Beam(M,icon_state="sendbeam",time=4) + M.visible_message("[M] repairs some of \the [src]'s dents.", \ + "You repair some of [src]'s dents, leaving [src] at [health]/[maxHealth] health.") + else + M.visible_message("[M] repairs some of its own dents.", \ + "You repair some of your own dents, leaving you at [M.health]/[M.maxHealth] health.") + else + if(src != M) + to_chat(M, "You cannot repair [src]'s dents, as it has none!") + else + to_chat(M, "You cannot repair your own dents, as you have none!") + else if(src != M) + return ..() + + +/mob/living/simple_animal/hostile/construct/narsie_act() + return + +/mob/living/simple_animal/hostile/construct/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = FALSE, override = FALSE, tesla_shock = FALSE, illusion = FALSE, stun = TRUE) + return FALSE + +/////////////////Juggernaut/////////////// + + + +/mob/living/simple_animal/hostile/construct/armoured + name = "Juggernaut" + real_name = "Juggernaut" + desc = "A possessed suit of armour driven by the will of the restless dead" + icon = 'icons/mob/mob.dmi' + icon_state = "behemoth" + icon_living = "behemoth" + maxHealth = 250 + health = 250 + response_harm = "harmlessly punches" + harm_intent_damage = 0 + obj_damage = 90 + melee_damage_lower = 30 + melee_damage_upper = 30 + attacktext = "smashes their armoured gauntlet into" + speed = 3 + environment_smash = 2 + attack_sound = 'sound/weapons/punch3.ogg' + status_flags = 0 + const_type = "juggernaut" + mob_size = MOB_SIZE_LARGE + construct_spells = list(/obj/effect/proc_holder/spell/targeted/night_vision, /obj/effect/proc_holder/spell/aoe_turf/conjure/lesserforcewall) + force_threshold = 11 + playstyle_string = "You are a Juggernaut. Though slow, your shell can withstand extreme punishment, \ + create shield walls, rip apart enemies and walls alike, and even deflect energy weapons." + + +/mob/living/simple_animal/hostile/construct/armoured/hostile //actually hostile, will move around, hit things + AIStatus = AI_ON + environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP + +/mob/living/simple_animal/hostile/construct/armoured/bullet_act(var/obj/item/projectile/P) + if(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam)) + var/reflectchance = 80 - round(P.damage/3) + if(prob(reflectchance)) + if((P.damage_type == BRUTE || P.damage_type == BURN)) + adjustBruteLoss(P.damage * 0.5) + visible_message("The [P.name] gets reflected by [src]'s shell!", \ + "The [P.name] gets reflected by [src]'s shell!") + + P.reflect_back(src, list(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3)) + + return -1 // complete projectile permutation + + return (..(P)) + + + +////////////////////////Wraith///////////////////////////////////////////// + + + +/mob/living/simple_animal/hostile/construct/wraith + name = "Wraith" + real_name = "Wraith" + desc = "A wicked bladed shell contraption piloted by a bound spirit" + icon = 'icons/mob/mob.dmi' + icon_state = "floating" + icon_living = "floating" + maxHealth = 75 + health = 75 + melee_damage_lower = 25 + melee_damage_upper = 25 + attacktext = "slashes" + see_in_dark = 8 + attack_sound = 'sound/weapons/bladeslice.ogg' + const_type = "wraith" + construct_spells = list(/obj/effect/proc_holder/spell/targeted/night_vision, /obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift) + retreat_distance = 2 //AI wraiths will move in and out of combat + playstyle_string = "You are a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls." + +/mob/living/simple_animal/hostile/construct/wraith/hostile //actually hostile, will move around, hit things + AIStatus = AI_ON + +/////////////////////////////Artificer///////////////////////// + + + +/mob/living/simple_animal/hostile/construct/builder + name = "Artificer" + real_name = "Artificer" + desc = "A bulbous construct dedicated to building and maintaining Cult armies." + icon = 'icons/mob/mob.dmi' + icon_state = "artificer" + icon_living = "artificer" + maxHealth = 50 + health = 50 + response_harm = "viciously beats" + harm_intent_damage = 5 + obj_damage = 60 + melee_damage_lower = 5 + melee_damage_upper = 5 + attacktext = "rams" + environment_smash = 2 + retreat_distance = 10 + minimum_distance = 10 //AI artificers will flee like fuck + attack_sound = 'sound/weapons/punch2.ogg' + const_type = "builder" + construct_spells = list(/obj/effect/proc_holder/spell/targeted/night_vision, + /obj/effect/proc_holder/spell/aoe_turf/conjure/construct/lesser, + /obj/effect/proc_holder/spell/aoe_turf/conjure/wall, + /obj/effect/proc_holder/spell/aoe_turf/conjure/floor, + /obj/effect/proc_holder/spell/aoe_turf/conjure/pylon, + /obj/effect/proc_holder/spell/aoe_turf/conjure/soulstone, + /obj/effect/proc_holder/spell/targeted/projectile/magic_missile/lesser) + + playstyle_string = "You are an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, \ + use magic missile, repair allied constructs (by clicking on them), \ + and, most important of all, create new constructs by producing soulstones to capture souls, \ + and shells to place those soulstones into." + + +/mob/living/simple_animal/hostile/construct/builder/Found(atom/A) //what have we found here? + if(isconstruct(A)) //is it a construct? + var/mob/living/simple_animal/hostile/construct/C = A + if(C.health < C.maxHealth) //is it hurt? let's go heal it if it is + return 1 + else + return 0 + else + return 0 + +/mob/living/simple_animal/hostile/construct/builder/CanAttack(atom/the_target) + if(see_invisible < the_target.invisibility)//Target's invisible to us, forget it + return 0 + if(Found(the_target) || ..()) //If we Found it or Can_Attack it normally, we Can_Attack it as long as it wasn't invisible + return 1 //as a note this shouldn't be added to base hostile mobs because it'll mess up retaliate hostile mobs + +/mob/living/simple_animal/hostile/construct/builder/MoveToTarget(var/list/possible_targets) + ..() + if(isliving(target)) + var/mob/living/L = target + if(isconstruct(L) && L.health >= L.maxHealth) //is this target an unhurt construct? stop trying to heal it + LoseTarget() + return 0 + if(L.health <= melee_damage_lower+melee_damage_upper) //ey bucko you're hurt as fuck let's go hit you + retreat_distance = null + minimum_distance = 1 + +/mob/living/simple_animal/hostile/construct/builder/Aggro() + ..() + if(isconstruct(target)) //oh the target is a construct no need to flee + retreat_distance = null + minimum_distance = 1 + +/mob/living/simple_animal/hostile/construct/builder/LoseAggro() + ..() + retreat_distance = initial(retreat_distance) + minimum_distance = initial(minimum_distance) + +/mob/living/simple_animal/hostile/construct/builder/hostile //actually hostile, will move around, hit things, heal other constructs + AIStatus = AI_ON + environment_smash = ENVIRONMENT_SMASH_STRUCTURES //only token destruction, don't smash the cult wall NO STOP + + +/////////////////////////////Behemoth///////////////////////// + + +/mob/living/simple_animal/hostile/construct/behemoth + name = "Behemoth" + real_name = "Behemoth" + desc = "The pinnacle of occult technology, Behemoths are the ultimate weapon in the Cult of Nar-Sie's arsenal." + icon = 'icons/mob/mob.dmi' + icon_state = "behemoth" + icon_living = "behemoth" + maxHealth = 750 + health = 750 + speak_emote = list("rumbles") + response_harm = "harmlessly punches" + harm_intent_damage = 0 + melee_damage_lower = 50 + melee_damage_upper = 50 + attacktext = "brutally crushes" + speed = 5 + environment_smash = 2 + attack_sound = 'sound/weapons/punch4.ogg' + force_threshold = 11 + const_type = "behemoth" + var/energy = 0 + var/max_energy = 1000 + +/mob/living/simple_animal/hostile/construct/behemoth/hostile //actually hostile, will move around, hit things + AIStatus = AI_ON + environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP + +/mob/living/simple_animal/hostile/construct/behemoth/Life(seconds, times_fired) + weakened = 0 + return ..() + + +/////////////////////////////Harvester///////////////////////// + +/mob/living/simple_animal/hostile/construct/harvester + name = "Harvester" + real_name = "Harvester" + desc = "A harbinger of Nar-Sie's enlightenment. It'll be all over soon." + icon = 'icons/mob/mob.dmi' + icon_state = "harvester" + icon_living = "harvester" + maxHealth = 60 + health = 60 + melee_damage_lower = 1 + melee_damage_upper = 5 + attacktext = "prods" + environment_smash = ENVIRONMENT_SMASH_RWALLS + see_in_dark = 8 + attack_sound = 'sound/weapons/tap.ogg' + const_type = "harvester" + construct_spells = list(/obj/effect/proc_holder/spell/targeted/night_vision, + /obj/effect/proc_holder/spell/aoe_turf/conjure/wall, + /obj/effect/proc_holder/spell/aoe_turf/conjure/floor, + /obj/effect/proc_holder/spell/targeted/smoke/disable) + retreat_distance = 2 //AI harvesters will move in and out of combat, like wraiths, but shittier + playstyle_string = "You are a Harvester. You are not strong, but your powers of domination will assist you in your role: \ + Bring those who still cling to this world of illusion back to the master so they may know Truth." + + +/mob/living/simple_animal/hostile/construct/harvester/Process_Spacemove(var/movement_dir = 0) + return 1 + + +/mob/living/simple_animal/hostile/construct/harvester/hostile //actually hostile, will move around, hit things + AIStatus = AI_ON + environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP + +////////////////Glow//////////////////// +/mob/living/simple_animal/hostile/construct/proc/updateglow() + overlays = 0 + var/overlay_layer = LIGHTING_LAYER + 1 + if(layer != MOB_LAYER) + overlay_layer=TURF_LAYER+0.2 + + overlays += image(icon,"glow-[icon_state]",overlay_layer) + set_light(2, -2, l_color = "#FFFFFF") + +///ui stuff + +/mob/living/simple_animal/hostile/construct/armoured/handle_hud_icons_health() + ..() + if(healths) + switch(health) + if(250 to INFINITY) healths.icon_state = "juggernaut_health0" + if(208 to 249) healths.icon_state = "juggernaut_health1" + if(167 to 207) healths.icon_state = "juggernaut_health2" + if(125 to 166) healths.icon_state = "juggernaut_health3" + if(84 to 124) healths.icon_state = "juggernaut_health4" + if(42 to 83) healths.icon_state = "juggernaut_health5" + if(1 to 41) healths.icon_state = "juggernaut_health6" + else healths.icon_state = "juggernaut_health7" + + +/mob/living/simple_animal/hostile/construct/behemoth/handle_hud_icons_health() + ..() + if(healths) + switch(health) + if(750 to INFINITY) healths.icon_state = "juggernaut_health0" + if(625 to 749) healths.icon_state = "juggernaut_health1" + if(500 to 624) healths.icon_state = "juggernaut_health2" + if(375 to 499) healths.icon_state = "juggernaut_health3" + if(250 to 374) healths.icon_state = "juggernaut_health4" + if(125 to 249) healths.icon_state = "juggernaut_health5" + if(1 to 124) healths.icon_state = "juggernaut_health6" + else healths.icon_state = "juggernaut_health7" + +/mob/living/simple_animal/hostile/construct/builder/handle_hud_icons_health() + ..() + if(healths) + switch(health) + if(50 to INFINITY) healths.icon_state = "artificer_health0" + if(42 to 49) healths.icon_state = "artificer_health1" + if(34 to 41) healths.icon_state = "artificer_health2" + if(26 to 33) healths.icon_state = "artificer_health3" + if(18 to 25) healths.icon_state = "artificer_health4" + if(10 to 17) healths.icon_state = "artificer_health5" + if(1 to 9) healths.icon_state = "artificer_health6" + else healths.icon_state = "artificer_health7" + + + +/mob/living/simple_animal/hostile/construct/wraith/handle_hud_icons_health() + + ..() + if(healths) + switch(health) + if(75 to INFINITY) healths.icon_state = "wraith_health0" + if(62 to 74) healths.icon_state = "wraith_health1" + if(50 to 61) healths.icon_state = "wraith_health2" + if(37 to 49) healths.icon_state = "wraith_health3" + if(25 to 36) healths.icon_state = "wraith_health4" + if(12 to 24) healths.icon_state = "wraith_health5" + if(1 to 11) healths.icon_state = "wraith_health6" + else healths.icon_state = "wraith_health7" + + +/mob/living/simple_animal/hostile/construct/harvester/handle_hud_icons_health() + + ..() + if(healths) + switch(health) + if(150 to INFINITY) healths.icon_state = "harvester_health0" + if(125 to 149) healths.icon_state = "harvester_health1" + if(100 to 124) healths.icon_state = "harvester_health2" + if(75 to 99) healths.icon_state = "harvester_health3" + if(50 to 74) healths.icon_state = "harvester_health4" + if(25 to 49) healths.icon_state = "harvester_health5" + if(1 to 24) healths.icon_state = "harvester_health6" + else healths.icon_state = "harvester_health7" diff --git a/code/modules/mob/living/simple_animal/corpse.dm b/code/modules/mob/living/simple_animal/corpse.dm index cd82b39d27f..10460d11e65 100644 --- a/code/modules/mob/living/simple_animal/corpse.dm +++ b/code/modules/mob/living/simple_animal/corpse.dm @@ -1,115 +1,115 @@ -//List of different corpse types -/obj/effect/mob_spawn/human/corpse/syndicatesoldier - name = "Syndicate Operative" - mob_name = "Syndicate Operative" - hair_style = "bald" - facial_hair_style = "shaved" - id_job = "Operative" - id_access_list = list(access_syndicate) - outfit = /datum/outfit/syndicatesoldiercorpse - -/datum/outfit/syndicatesoldiercorpse - name = "Syndicate Operative Corpse" - uniform = /obj/item/clothing/under/syndicate - suit = /obj/item/clothing/suit/armor/vest - shoes = /obj/item/clothing/shoes/combat - gloves = /obj/item/clothing/gloves/combat - l_ear = /obj/item/radio/headset - mask = /obj/item/clothing/mask/gas - head = /obj/item/clothing/head/helmet/swat - back = /obj/item/storage/backpack - id = /obj/item/card/id - - -/obj/effect/mob_spawn/human/corpse/syndicatecommando - name = "Syndicate Commando" - mob_name = "Syndicate Commando" - hair_style = "bald" - facial_hair_style = "shaved" - id_job = "Operative" - id_access_list = list(access_syndicate) - outfit = /datum/outfit/syndicatecommandocorpse - -/datum/outfit/syndicatecommandocorpse - name = "Syndicate Commando Corpse" - uniform = /obj/item/clothing/under/syndicate - suit = /obj/item/clothing/suit/space/hardsuit/syndi - shoes = /obj/item/clothing/shoes/combat - gloves = /obj/item/clothing/gloves/combat - l_ear = /obj/item/radio/headset - mask = /obj/item/clothing/mask/gas/syndicate - back = /obj/item/tank/jetpack/oxygen - r_pocket = /obj/item/tank/emergency_oxygen - id = /obj/item/card/id - - -/obj/effect/mob_spawn/human/clown/corpse - roundstart = TRUE - instant = TRUE - -/obj/effect/mob_spawn/human/mime/corpse - roundstart = TRUE - instant = TRUE - -/obj/effect/mob_spawn/human/corpse/pirate - name = "Pirate" - mob_name = "Pirate" - hair_style = "bald" - facial_hair_style = "shaved" - outfit = /datum/outfit/piratecorpse - -/datum/outfit/piratecorpse - name = "Pirate Corpse" - uniform = /obj/item/clothing/under/pirate - shoes = /obj/item/clothing/shoes/jackboots - glasses = /obj/item/clothing/glasses/eyepatch - head = /obj/item/clothing/head/bandana - - -/obj/effect/mob_spawn/human/corpse/pirate/ranged - name = "Pirate Gunner" - mob_name = "Pirate Gunner" - outfit = /datum/outfit/piratecorpse/ranged - -/datum/outfit/piratecorpse/ranged - name = "Pirate Gunner Corpse" - suit = /obj/item/clothing/suit/pirate_black - head = /obj/item/clothing/head/pirate - - -/obj/effect/mob_spawn/human/corpse/russian - name = "Russian" - mob_name = "Russian" - hair_style = "bald" - facial_hair_style = "shaved" - outfit = /datum/outfit/russiancorpse - -/datum/outfit/russiancorpse - name = "Russian Corpse" - uniform = /obj/item/clothing/under/soviet - shoes = /obj/item/clothing/shoes/jackboots - head = /obj/item/clothing/head/bearpelt - - -/obj/effect/mob_spawn/human/corpse/russian/ranged - outfit = /datum/outfit/russiancorpse/ranged - -/datum/outfit/russiancorpse/ranged - name = "Ranged Russian Corpse" - head = /obj/item/clothing/head/ushanka - - -/obj/effect/mob_spawn/human/corpse/wizard - name = "Space Wizard Corpse" - outfit = /datum/outfit/wizardcorpse - -/obj/effect/mob_spawn/human/corpse/clownoff/Initialize() - mob_name = "[pick(GLOB.wizard_first)], [pick(GLOB.wizard_second)]" - ..() - -/datum/outfit/wizardcorpse - name = "Space Wizard Corpse" - uniform = /obj/item/clothing/under/color/lightpurple - suit = /obj/item/clothing/suit/wizrobe - shoes = /obj/item/clothing/shoes/sandal - head = /obj/item/clothing/head/wizard +//List of different corpse types +/obj/effect/mob_spawn/human/corpse/syndicatesoldier + name = "Syndicate Operative" + mob_name = "Syndicate Operative" + hair_style = "bald" + facial_hair_style = "shaved" + id_job = "Operative" + id_access_list = list(ACCESS_SYNDICATE) + outfit = /datum/outfit/syndicatesoldiercorpse + +/datum/outfit/syndicatesoldiercorpse + name = "Syndicate Operative Corpse" + uniform = /obj/item/clothing/under/syndicate + suit = /obj/item/clothing/suit/armor/vest + shoes = /obj/item/clothing/shoes/combat + gloves = /obj/item/clothing/gloves/combat + l_ear = /obj/item/radio/headset + mask = /obj/item/clothing/mask/gas + head = /obj/item/clothing/head/helmet/swat + back = /obj/item/storage/backpack + id = /obj/item/card/id + + +/obj/effect/mob_spawn/human/corpse/syndicatecommando + name = "Syndicate Commando" + mob_name = "Syndicate Commando" + hair_style = "bald" + facial_hair_style = "shaved" + id_job = "Operative" + id_access_list = list(ACCESS_SYNDICATE) + outfit = /datum/outfit/syndicatecommandocorpse + +/datum/outfit/syndicatecommandocorpse + name = "Syndicate Commando Corpse" + uniform = /obj/item/clothing/under/syndicate + suit = /obj/item/clothing/suit/space/hardsuit/syndi + shoes = /obj/item/clothing/shoes/combat + gloves = /obj/item/clothing/gloves/combat + l_ear = /obj/item/radio/headset + mask = /obj/item/clothing/mask/gas/syndicate + back = /obj/item/tank/jetpack/oxygen + r_pocket = /obj/item/tank/emergency_oxygen + id = /obj/item/card/id + + +/obj/effect/mob_spawn/human/clown/corpse + roundstart = TRUE + instant = TRUE + +/obj/effect/mob_spawn/human/mime/corpse + roundstart = TRUE + instant = TRUE + +/obj/effect/mob_spawn/human/corpse/pirate + name = "Pirate" + mob_name = "Pirate" + hair_style = "bald" + facial_hair_style = "shaved" + outfit = /datum/outfit/piratecorpse + +/datum/outfit/piratecorpse + name = "Pirate Corpse" + uniform = /obj/item/clothing/under/pirate + shoes = /obj/item/clothing/shoes/jackboots + glasses = /obj/item/clothing/glasses/eyepatch + head = /obj/item/clothing/head/bandana + + +/obj/effect/mob_spawn/human/corpse/pirate/ranged + name = "Pirate Gunner" + mob_name = "Pirate Gunner" + outfit = /datum/outfit/piratecorpse/ranged + +/datum/outfit/piratecorpse/ranged + name = "Pirate Gunner Corpse" + suit = /obj/item/clothing/suit/pirate_black + head = /obj/item/clothing/head/pirate + + +/obj/effect/mob_spawn/human/corpse/russian + name = "Russian" + mob_name = "Russian" + hair_style = "bald" + facial_hair_style = "shaved" + outfit = /datum/outfit/russiancorpse + +/datum/outfit/russiancorpse + name = "Russian Corpse" + uniform = /obj/item/clothing/under/soviet + shoes = /obj/item/clothing/shoes/jackboots + head = /obj/item/clothing/head/bearpelt + + +/obj/effect/mob_spawn/human/corpse/russian/ranged + outfit = /datum/outfit/russiancorpse/ranged + +/datum/outfit/russiancorpse/ranged + name = "Ranged Russian Corpse" + head = /obj/item/clothing/head/ushanka + + +/obj/effect/mob_spawn/human/corpse/wizard + name = "Space Wizard Corpse" + outfit = /datum/outfit/wizardcorpse + +/obj/effect/mob_spawn/human/corpse/clownoff/Initialize() + mob_name = "[pick(GLOB.wizard_first)], [pick(GLOB.wizard_second)]" + ..() + +/datum/outfit/wizardcorpse + name = "Space Wizard Corpse" + uniform = /obj/item/clothing/under/color/lightpurple + suit = /obj/item/clothing/suit/wizrobe + shoes = /obj/item/clothing/shoes/sandal + head = /obj/item/clothing/head/wizard diff --git a/code/modules/mob/living/simple_animal/damage_procs.dm b/code/modules/mob/living/simple_animal/damage_procs.dm index 6899b40bea0..6ba69de6a61 100644 --- a/code/modules/mob/living/simple_animal/damage_procs.dm +++ b/code/modules/mob/living/simple_animal/damage_procs.dm @@ -34,4 +34,4 @@ /mob/living/simple_animal/adjustStaminaLoss(amount, updating_health = TRUE) if(damage_coeff[STAMINA]) - return ..(amount*damage_coeff[STAMINA], updating_health) \ No newline at end of file + return ..(amount*damage_coeff[STAMINA], updating_health) diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm index f552bb5cebd..24818d07652 100644 --- a/code/modules/mob/living/simple_animal/friendly/cat.dm +++ b/code/modules/mob/living/simple_animal/friendly/cat.dm @@ -1,279 +1,279 @@ -//Cat -/mob/living/simple_animal/pet/cat - name = "cat" - desc = "Kitty!!" - icon_state = "cat2" - icon_living = "cat2" - icon_dead = "cat2_dead" - icon_resting = "cat2_rest" - gender = MALE - speak = list("Meow!", "Esp!", "Purr!", "HSSSSS") - speak_emote = list("purrs", "meows") - emote_hear = list("meows", "mews") - emote_see = list("shakes its head", "shivers") - var/meow_sound = 'sound/creatures/cat_meow.ogg' //Used in emote. - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - mob_size = MOB_SIZE_SMALL - animal_species = /mob/living/simple_animal/pet/cat - childtype = list(/mob/living/simple_animal/pet/cat/kitten) - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 3) - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "kicks" - gold_core_spawnable = FRIENDLY_SPAWN - collar_type = "cat" - var/turns_since_scan = 0 - var/mob/living/simple_animal/mouse/movement_target - var/eats_mice = 1 - -//RUNTIME IS ALIVE! SQUEEEEEEEE~ -/mob/living/simple_animal/pet/cat/Runtime - name = "Runtime" - desc = "GCAT" - icon_state = "cat" - icon_living = "cat" - icon_dead = "cat_dead" - icon_resting = "cat_rest" - gender = FEMALE - gold_core_spawnable = NO_SPAWN - unique_pet = TRUE - var/list/family = list() - var/memory_saved = 0 - var/list/children = list() //Actual mob instances of children - var/cats_deployed = 0 - -/mob/living/simple_animal/pet/cat/Runtime/New() - Read_Memory() - ..() - -/mob/living/simple_animal/pet/cat/Runtime/Life(seconds, times_fired) - if(!cats_deployed && SSticker.current_state >= GAME_STATE_SETTING_UP) - Deploy_The_Cats() - if(!stat && SSticker.current_state == GAME_STATE_FINISHED && !memory_saved) - Write_Memory() - ..() - -/mob/living/simple_animal/pet/cat/Runtime/make_babies() - var/mob/baby = ..() - if(baby) - children += baby - return baby - -/mob/living/simple_animal/pet/cat/Runtime/death() - if(can_die() && !memory_saved) - Write_Memory(1) - return ..() - -/mob/living/simple_animal/pet/cat/Runtime/proc/Read_Memory() - var/savefile/S = new /savefile("data/npc_saves/Runtime.sav") - S["family"] >> family - - if(isnull(family)) - family = list() - -/mob/living/simple_animal/pet/cat/Runtime/proc/Write_Memory(dead) - var/savefile/S = new /savefile("data/npc_saves/Runtime.sav") - family = list() - if(!dead) - for(var/mob/living/simple_animal/pet/cat/kitten/C in children) - if(istype(C,type) || C.stat || !C.z || !C.butcher_results) - continue - if(C.type in family) - family[C.type] += 1 - else - family[C.type] = 1 - S["family"] << family - memory_saved = 1 - -/mob/living/simple_animal/pet/cat/Runtime/proc/Deploy_The_Cats() - cats_deployed = 1 - for(var/cat_type in family) - if(family[cat_type] > 0) - for(var/i in 1 to min(family[cat_type],100)) //Limits to about 500 cats, you wouldn't think this would be needed (BUT IT IS) - new cat_type(loc) - - -/mob/living/simple_animal/pet/cat/Life() - ..() - make_babies() - - -/mob/living/simple_animal/pet/cat/handle_automated_action() - if(!stat && !buckled) - if(prob(1)) - custom_emote(1, pick("stretches out for a belly rub.", "wags its tail.", "lies down.")) - StartResting() - else if(prob(1)) - custom_emote(1, pick("sits down.", "crouches on its hind legs.", "looks alert.")) - icon_state = "[icon_living]_sit" - collar_type = "[initial(collar_type)]_sit" - resting = TRUE - update_canmove() - else if(prob(1)) - if(resting) - custom_emote(1, pick("gets up and meows.", "walks around.", "stops resting.")) - StopResting() - else - custom_emote(1, pick("grooms its fur.", "twitches its whiskers.", "shakes out its coat.")) - - //MICE! - if(eats_mice && isturf(loc) && !incapacitated()) - for(var/mob/living/simple_animal/mouse/M in view(1, src)) - if(!M.stat && Adjacent(M)) - custom_emote(1, "splats \the [M]!") - M.splat() - movement_target = null - stop_automated_movement = 0 - break - for(var/obj/item/toy/cattoy/T in view(1, src)) - if(T.cooldown < (world.time - 400)) - custom_emote(1, "bats \the [T] around with its paw!") - T.cooldown = world.time - -/mob/living/simple_animal/pet/cat/handle_automated_movement() - . = ..() - if(!stat && !resting && !buckled) - turns_since_scan++ - if(turns_since_scan > 5) - walk_to(src,0) - turns_since_scan = 0 - if((movement_target) && !(isturf(movement_target.loc) || ishuman(movement_target.loc) )) - movement_target = null - stop_automated_movement = 0 - if( !movement_target || !(movement_target.loc in oview(src, 3)) ) - movement_target = null - stop_automated_movement = 0 - for(var/mob/living/simple_animal/mouse/snack in oview(src,3)) - if(isturf(snack.loc) && !snack.stat) - movement_target = snack - break - if(movement_target) - stop_automated_movement = 1 - walk_to(src,movement_target,0,3) - -/mob/living/simple_animal/pet/cat/emote(act, m_type = 1, message = null, force) - if(stat != CONSCIOUS) - return - - var/on_CD = 0 - act = lowertext(act) - switch(act) - if("meow") - on_CD = handle_emote_CD() - if("hiss") - on_CD = handle_emote_CD() - if("purr") - on_CD = handle_emote_CD() - else - on_CD = 0 - - if(!force && on_CD == 1) - return - - switch(act) - if("meow") - message = "[src] [pick(emote_hear)]!" - m_type = 2 //audible - playsound(src, meow_sound, 50, 0.75) - if("hiss") - message = "[src] hisses!" - m_type = 2 - if("purr") - message = "[src] purrs." - m_type = 2 - if("help") - to_chat(src, "scream, meow, hiss, purr") - - ..() - -/mob/living/simple_animal/pet/cat/Proc - name = "Proc" - gender = MALE - gold_core_spawnable = NO_SPAWN - unique_pet = TRUE - -/mob/living/simple_animal/pet/cat/kitten - name = "kitten" - desc = "D'aaawwww" - icon_state = "kitten" - icon_living = "kitten" - icon_dead = "kitten_dead" - icon_resting = null - gender = NEUTER - density = 0 - pass_flags = PASSMOB - collar_type = "kitten" - -/mob/living/simple_animal/pet/cat/Syndi - name = "SyndiCat" - desc = "It's a SyndiCat droid." - icon_state = "Syndicat" - icon_living = "Syndicat" - icon_dead = "Syndicat_dead" - icon_resting = "Syndicat_rest" - meow_sound = null //Need robo-meow. - gender = FEMALE - mutations = list(BREATHLESS) - faction = list("syndicate") - gold_core_spawnable = NO_SPAWN - eats_mice = 0 - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - melee_damage_lower = 5 - melee_damage_upper = 15 - -/mob/living/simple_animal/pet/cat/cak - name = "Keeki" - desc = "It's a cat made out of cake." - icon_state = "cak" - icon_living = "cak" - icon_resting = "cak_rest" - icon_dead = "cak_dead" - health = 50 - maxHealth = 50 - harm_intent_damage = 10 - butcher_results = list( - /obj/item/organ/internal/brain = 1, - /obj/item/organ/internal/heart = 1, - /obj/item/reagent_containers/food/snacks/birthdaycakeslice = 3, - /obj/item/reagent_containers/food/snacks/meat/slab = 2 - ) - response_harm = "takes a bite out of" - attacked_sound = "sound/items/eatfood.ogg" - deathmessage = "loses its false life and collapses!" - death_sound = "bodyfall" - -/mob/living/simple_animal/pet/cat/cak/Life() - ..() - if(stat) - return - if(health < maxHealth) - adjustBruteLoss(-4) - for(var/obj/item/reagent_containers/food/snacks/donut/D in range(1, src)) - if(D.icon_state != "donut2") - D.name = "frosted donut" - D.icon_state = "donut2" - D.reagents.add_reagent("sprinkles", 2) - D.filling_color = "#FF69B4" - -/mob/living/simple_animal/pet/cat/cak/attack_hand(mob/living/L) - ..() - if(L.a_intent == INTENT_HARM && L.reagents && !stat) - L.reagents.add_reagent("nutriment", 0.4) - L.reagents.add_reagent("vitamin", 0.4) - -/mob/living/simple_animal/pet/cat/cak/CheckParts(list/parts) - ..() - var/obj/item/organ/internal/brain/B = locate(/obj/item/organ/internal/brain) in contents - if(!B || !B.brainmob || !B.brainmob.mind) - return - B.brainmob.mind.transfer_to(src) - to_chat(src, "You are a cak! You're a harmless cat/cake hybrid that everyone loves. People can take bites out of you if they're hungry, but you regenerate health \ - so quickly that it generally doesn't matter. You're remarkably resilient to any damage besides this and it's hard for you to really die at all. You should go around and bring happiness and \ - free cake to the station!") - var/new_name = stripped_input(src, "Enter your name, or press \"Cancel\" to stick with Keeki.", "Name Change") - if(new_name) - to_chat(src, "Your name is now \"[new_name]\"!") - name = new_name \ No newline at end of file +//Cat +/mob/living/simple_animal/pet/cat + name = "cat" + desc = "Kitty!!" + icon_state = "cat2" + icon_living = "cat2" + icon_dead = "cat2_dead" + icon_resting = "cat2_rest" + gender = MALE + speak = list("Meow!", "Esp!", "Purr!", "HSSSSS") + speak_emote = list("purrs", "meows") + emote_hear = list("meows", "mews") + emote_see = list("shakes its head", "shivers") + var/meow_sound = 'sound/creatures/cat_meow.ogg' //Used in emote. + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + mob_size = MOB_SIZE_SMALL + animal_species = /mob/living/simple_animal/pet/cat + childtype = list(/mob/living/simple_animal/pet/cat/kitten) + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 3) + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "kicks" + gold_core_spawnable = FRIENDLY_SPAWN + collar_type = "cat" + var/turns_since_scan = 0 + var/mob/living/simple_animal/mouse/movement_target + var/eats_mice = 1 + +//RUNTIME IS ALIVE! SQUEEEEEEEE~ +/mob/living/simple_animal/pet/cat/Runtime + name = "Runtime" + desc = "GCAT" + icon_state = "cat" + icon_living = "cat" + icon_dead = "cat_dead" + icon_resting = "cat_rest" + gender = FEMALE + gold_core_spawnable = NO_SPAWN + unique_pet = TRUE + var/list/family = list() + var/memory_saved = 0 + var/list/children = list() //Actual mob instances of children + var/cats_deployed = 0 + +/mob/living/simple_animal/pet/cat/Runtime/New() + Read_Memory() + ..() + +/mob/living/simple_animal/pet/cat/Runtime/Life(seconds, times_fired) + if(!cats_deployed && SSticker.current_state >= GAME_STATE_SETTING_UP) + Deploy_The_Cats() + if(!stat && SSticker.current_state == GAME_STATE_FINISHED && !memory_saved) + Write_Memory() + ..() + +/mob/living/simple_animal/pet/cat/Runtime/make_babies() + var/mob/baby = ..() + if(baby) + children += baby + return baby + +/mob/living/simple_animal/pet/cat/Runtime/death() + if(can_die() && !memory_saved) + Write_Memory(1) + return ..() + +/mob/living/simple_animal/pet/cat/Runtime/proc/Read_Memory() + var/savefile/S = new /savefile("data/npc_saves/Runtime.sav") + S["family"] >> family + + if(isnull(family)) + family = list() + +/mob/living/simple_animal/pet/cat/Runtime/proc/Write_Memory(dead) + var/savefile/S = new /savefile("data/npc_saves/Runtime.sav") + family = list() + if(!dead) + for(var/mob/living/simple_animal/pet/cat/kitten/C in children) + if(istype(C,type) || C.stat || !C.z || !C.butcher_results) + continue + if(C.type in family) + family[C.type] += 1 + else + family[C.type] = 1 + S["family"] << family + memory_saved = 1 + +/mob/living/simple_animal/pet/cat/Runtime/proc/Deploy_The_Cats() + cats_deployed = 1 + for(var/cat_type in family) + if(family[cat_type] > 0) + for(var/i in 1 to min(family[cat_type],100)) //Limits to about 500 cats, you wouldn't think this would be needed (BUT IT IS) + new cat_type(loc) + + +/mob/living/simple_animal/pet/cat/Life() + ..() + make_babies() + + +/mob/living/simple_animal/pet/cat/handle_automated_action() + if(!stat && !buckled) + if(prob(1)) + custom_emote(1, pick("stretches out for a belly rub.", "wags its tail.", "lies down.")) + StartResting() + else if(prob(1)) + custom_emote(1, pick("sits down.", "crouches on its hind legs.", "looks alert.")) + icon_state = "[icon_living]_sit" + collar_type = "[initial(collar_type)]_sit" + resting = TRUE + update_canmove() + else if(prob(1)) + if(resting) + custom_emote(1, pick("gets up and meows.", "walks around.", "stops resting.")) + StopResting() + else + custom_emote(1, pick("grooms its fur.", "twitches its whiskers.", "shakes out its coat.")) + + //MICE! + if(eats_mice && isturf(loc) && !incapacitated()) + for(var/mob/living/simple_animal/mouse/M in view(1, src)) + if(!M.stat && Adjacent(M)) + custom_emote(1, "splats \the [M]!") + M.splat() + movement_target = null + stop_automated_movement = 0 + break + for(var/obj/item/toy/cattoy/T in view(1, src)) + if(T.cooldown < (world.time - 400)) + custom_emote(1, "bats \the [T] around with its paw!") + T.cooldown = world.time + +/mob/living/simple_animal/pet/cat/handle_automated_movement() + . = ..() + if(!stat && !resting && !buckled) + turns_since_scan++ + if(turns_since_scan > 5) + walk_to(src,0) + turns_since_scan = 0 + if((movement_target) && !(isturf(movement_target.loc) || ishuman(movement_target.loc) )) + movement_target = null + stop_automated_movement = 0 + if( !movement_target || !(movement_target.loc in oview(src, 3)) ) + movement_target = null + stop_automated_movement = 0 + for(var/mob/living/simple_animal/mouse/snack in oview(src,3)) + if(isturf(snack.loc) && !snack.stat) + movement_target = snack + break + if(movement_target) + stop_automated_movement = 1 + walk_to(src,movement_target,0,3) + +/mob/living/simple_animal/pet/cat/emote(act, m_type = 1, message = null, force) + if(stat != CONSCIOUS) + return + + var/on_CD = 0 + act = lowertext(act) + switch(act) + if("meow") + on_CD = handle_emote_CD() + if("hiss") + on_CD = handle_emote_CD() + if("purr") + on_CD = handle_emote_CD() + else + on_CD = 0 + + if(!force && on_CD == 1) + return + + switch(act) + if("meow") + message = "[src] [pick(emote_hear)]!" + m_type = 2 //audible + playsound(src, meow_sound, 50, 0.75) + if("hiss") + message = "[src] hisses!" + m_type = 2 + if("purr") + message = "[src] purrs." + m_type = 2 + if("help") + to_chat(src, "scream, meow, hiss, purr") + + ..() + +/mob/living/simple_animal/pet/cat/Proc + name = "Proc" + gender = MALE + gold_core_spawnable = NO_SPAWN + unique_pet = TRUE + +/mob/living/simple_animal/pet/cat/kitten + name = "kitten" + desc = "D'aaawwww" + icon_state = "kitten" + icon_living = "kitten" + icon_dead = "kitten_dead" + icon_resting = null + gender = NEUTER + density = 0 + pass_flags = PASSMOB + collar_type = "kitten" + +/mob/living/simple_animal/pet/cat/Syndi + name = "SyndiCat" + desc = "It's a SyndiCat droid." + icon_state = "Syndicat" + icon_living = "Syndicat" + icon_dead = "Syndicat_dead" + icon_resting = "Syndicat_rest" + meow_sound = null //Need robo-meow. + gender = FEMALE + mutations = list(BREATHLESS) + faction = list("syndicate") + gold_core_spawnable = NO_SPAWN + eats_mice = 0 + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + melee_damage_lower = 5 + melee_damage_upper = 15 + +/mob/living/simple_animal/pet/cat/cak + name = "Keeki" + desc = "It's a cat made out of cake." + icon_state = "cak" + icon_living = "cak" + icon_resting = "cak_rest" + icon_dead = "cak_dead" + health = 50 + maxHealth = 50 + harm_intent_damage = 10 + butcher_results = list( + /obj/item/organ/internal/brain = 1, + /obj/item/organ/internal/heart = 1, + /obj/item/reagent_containers/food/snacks/birthdaycakeslice = 3, + /obj/item/reagent_containers/food/snacks/meat/slab = 2 + ) + response_harm = "takes a bite out of" + attacked_sound = "sound/items/eatfood.ogg" + deathmessage = "loses its false life and collapses!" + death_sound = "bodyfall" + +/mob/living/simple_animal/pet/cat/cak/Life() + ..() + if(stat) + return + if(health < maxHealth) + adjustBruteLoss(-4) + for(var/obj/item/reagent_containers/food/snacks/donut/D in range(1, src)) + if(D.icon_state != "donut2") + D.name = "frosted donut" + D.icon_state = "donut2" + D.reagents.add_reagent("sprinkles", 2) + D.filling_color = "#FF69B4" + +/mob/living/simple_animal/pet/cat/cak/attack_hand(mob/living/L) + ..() + if(L.a_intent == INTENT_HARM && L.reagents && !stat) + L.reagents.add_reagent("nutriment", 0.4) + L.reagents.add_reagent("vitamin", 0.4) + +/mob/living/simple_animal/pet/cat/cak/CheckParts(list/parts) + ..() + var/obj/item/organ/internal/brain/B = locate(/obj/item/organ/internal/brain) in contents + if(!B || !B.brainmob || !B.brainmob.mind) + return + B.brainmob.mind.transfer_to(src) + to_chat(src, "You are a cak! You're a harmless cat/cake hybrid that everyone loves. People can take bites out of you if they're hungry, but you regenerate health \ + so quickly that it generally doesn't matter. You're remarkably resilient to any damage besides this and it's hard for you to really die at all. You should go around and bring happiness and \ + free cake to the station!") + var/new_name = stripped_input(src, "Enter your name, or press \"Cancel\" to stick with Keeki.", "Name Change") + if(new_name) + to_chat(src, "Your name is now \"[new_name]\"!") + name = new_name diff --git a/code/modules/mob/living/simple_animal/friendly/crab.dm b/code/modules/mob/living/simple_animal/friendly/crab.dm index 7186eb5f5bf..4a51ce62d4e 100644 --- a/code/modules/mob/living/simple_animal/friendly/crab.dm +++ b/code/modules/mob/living/simple_animal/friendly/crab.dm @@ -1,55 +1,55 @@ -//Look Sir, free crabs! -/mob/living/simple_animal/crab - name = "crab" - desc = "A hard-shelled crustacean. Seems quite content to lounge around all the time." - icon_state = "crab" - icon_living = "crab" - icon_dead = "crab_dead" - speak_emote = list("clicks") - emote_hear = list("clicks") - emote_see = list("clacks") - speak_chance = 1 - turns_per_move = 5 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 1) - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "stomps" - stop_automated_movement = 1 - friendly = "pinches" - ventcrawler = 2 - can_hide = 1 - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN - -/mob/living/simple_animal/crab/handle_automated_movement() - //CRAB movement - if(!stat) - if(isturf(src.loc) && !resting && !buckled) //This is so it only moves if it's not inside a closet, gentics machine, etc. - turns_since_move++ - if(turns_since_move >= turns_per_move) - var/east_vs_west = pick(4, 8) - if(Process_Spacemove(east_vs_west)) - Move(get_step(src, east_vs_west), east_vs_west) - -//COFFEE! SQUEEEEEEEEE! -/mob/living/simple_animal/crab/Coffee - name = "Coffee" - real_name = "Coffee" - desc = "It's Coffee, the other pet!" - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "stomps" - gold_core_spawnable = NO_SPAWN - unique_pet = TRUE - -/mob/living/simple_animal/crab/evil - name = "Evil Crab" - real_name = "Evil Crab" - desc = "Unnerving, isn't it? It has to be planning something nefarious..." - icon_state = "evilcrab" - icon_living = "evilcrab" - icon_dead = "evilcrab_dead" - response_help = "pokes" - response_disarm = "shoves" - response_harm = "stomps" - gold_core_spawnable = HOSTILE_SPAWN \ No newline at end of file +//Look Sir, free crabs! +/mob/living/simple_animal/crab + name = "crab" + desc = "A hard-shelled crustacean. Seems quite content to lounge around all the time." + icon_state = "crab" + icon_living = "crab" + icon_dead = "crab_dead" + speak_emote = list("clicks") + emote_hear = list("clicks") + emote_see = list("clacks") + speak_chance = 1 + turns_per_move = 5 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 1) + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "stomps" + stop_automated_movement = 1 + friendly = "pinches" + ventcrawler = 2 + can_hide = 1 + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN + +/mob/living/simple_animal/crab/handle_automated_movement() + //CRAB movement + if(!stat) + if(isturf(src.loc) && !resting && !buckled) //This is so it only moves if it's not inside a closet, gentics machine, etc. + turns_since_move++ + if(turns_since_move >= turns_per_move) + var/east_vs_west = pick(4, 8) + if(Process_Spacemove(east_vs_west)) + Move(get_step(src, east_vs_west), east_vs_west) + +//COFFEE! SQUEEEEEEEEE! +/mob/living/simple_animal/crab/Coffee + name = "Coffee" + real_name = "Coffee" + desc = "It's Coffee, the other pet!" + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "stomps" + gold_core_spawnable = NO_SPAWN + unique_pet = TRUE + +/mob/living/simple_animal/crab/evil + name = "Evil Crab" + real_name = "Evil Crab" + desc = "Unnerving, isn't it? It has to be planning something nefarious..." + icon_state = "evilcrab" + icon_living = "evilcrab" + icon_dead = "evilcrab_dead" + response_help = "pokes" + response_disarm = "shoves" + response_harm = "stomps" + gold_core_spawnable = HOSTILE_SPAWN diff --git a/code/modules/mob/living/simple_animal/friendly/deer.dm b/code/modules/mob/living/simple_animal/friendly/deer.dm index 2c4c2eda960..a10ec1b7b35 100644 --- a/code/modules/mob/living/simple_animal/friendly/deer.dm +++ b/code/modules/mob/living/simple_animal/friendly/deer.dm @@ -15,4 +15,4 @@ response_disarm = "gently pushes aside" response_harm = "kicks" can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN \ No newline at end of file + gold_core_spawnable = FRIENDLY_SPAWN diff --git a/code/modules/mob/living/simple_animal/friendly/diona.dm b/code/modules/mob/living/simple_animal/friendly/diona.dm index 355d756288b..b4872c9ff87 100644 --- a/code/modules/mob/living/simple_animal/friendly/diona.dm +++ b/code/modules/mob/living/simple_animal/friendly/diona.dm @@ -283,4 +283,4 @@ if("help") to_chat(src, "scream, chirp") - ..() \ No newline at end of file + ..() diff --git a/code/modules/mob/living/simple_animal/friendly/dog.dm b/code/modules/mob/living/simple_animal/friendly/dog.dm index 3717493d26c..4b29e703116 100644 --- a/code/modules/mob/living/simple_animal/friendly/dog.dm +++ b/code/modules/mob/living/simple_animal/friendly/dog.dm @@ -762,4 +762,4 @@ spawn(0) for(var/i in list(1, 2, 4, 8, 4, 2, 1, 2, 4, 8, 4, 2, 1, 2, 4, 8, 4, 2)) dir = i - sleep(1) \ No newline at end of file + sleep(1) diff --git a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm index 99574aeac99..77c2edbdd53 100644 --- a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm +++ b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm @@ -1,448 +1,448 @@ -//goat -/mob/living/simple_animal/hostile/retaliate/goat - name = "goat" - desc = "Not known for their pleasant disposition." - icon_state = "goat" - icon_living = "goat" - icon_dead = "goat_dead" - speak = list("EHEHEHEHEH","eh?") - speak_emote = list("brays") - emote_hear = list("brays") - emote_see = list("shakes its head", "stamps a foot", "glares around") - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 4) - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "kicks" - faction = list("neutral") - attack_same = 1 - attacktext = "kicks" - attack_sound = 'sound/weapons/punch1.ogg' - health = 40 - maxHealth = 40 - melee_damage_lower = 1 - melee_damage_upper = 2 - stop_automated_movement_when_pulled = 1 - can_collar = 1 - blood_volume = BLOOD_VOLUME_NORMAL - var/obj/item/udder/udder = null - -/mob/living/simple_animal/hostile/retaliate/goat/New() - udder = new() - . = ..() - -/mob/living/simple_animal/hostile/retaliate/goat/Destroy() - QDEL_NULL(udder) - return ..() - -/mob/living/simple_animal/hostile/retaliate/goat/handle_automated_movement() - . = ..() - //chance to go crazy and start wacking stuff - if(!enemies.len && prob(1)) - Retaliate() - - if(enemies.len && prob(10)) - enemies = list() - LoseTarget() - visible_message("[src] calms down.") - - eat_plants() - if(!pulledby) - for(var/direction in shuffle(list(1, 2, 4, 8, 5, 6, 9, 10))) - var/step = get_step(src, direction) - if(step) - if(locate(/obj/structure/spacevine) in step || locate(/obj/structure/glowshroom) in step) - Move(step, get_dir(src, step)) - -/mob/living/simple_animal/hostile/retaliate/goat/Life(seconds, times_fired) - . = ..() - if(stat == CONSCIOUS) - udder.generateMilk() - -/mob/living/simple_animal/hostile/retaliate/goat/Retaliate() - ..() - visible_message("[src] gets an evil-looking gleam in their eye.") - -/mob/living/simple_animal/hostile/retaliate/goat/Move() - . = ..() - if(!stat) - eat_plants() - -/mob/living/simple_animal/hostile/retaliate/goat/attackby(var/obj/item/O as obj, var/mob/user as mob, params) - if(stat == CONSCIOUS && istype(O, /obj/item/reagent_containers/glass)) - udder.milkAnimal(O, user) - else - return ..() - -/mob/living/simple_animal/hostile/retaliate/goat/proc/eat_plants() - var/eaten = FALSE - var/obj/structure/spacevine/SV = locate(/obj/structure/spacevine) in loc - if(SV) - SV.eat(src) - eaten = TRUE - - var/obj/structure/glowshroom/GS = locate(/obj/structure/glowshroom) in loc - if(GS) - qdel(GS) - eaten = TRUE - - if(eaten && prob(10)) - say("Nom") - -/mob/living/simple_animal/hostile/retaliate/goat/AttackingTarget() - . = ..() - if(. && isdiona(target)) - var/mob/living/carbon/human/H = target - var/obj/item/organ/external/NB = pick(H.bodyparts) - H.visible_message("[src] takes a big chomp out of [H]!", "[src] takes a big chomp out of your [NB.name]!") - NB.droplimb() - -//cow -/mob/living/simple_animal/cow - name = "cow" - desc = "Known for their milk, just don't tip them over." - icon_state = "cow" - icon_living = "cow" - icon_dead = "cow_dead" - icon_gib = "cow_gib" - speak = list("moo?","moo","MOOOOOO") - speak_emote = list("moos","moos hauntingly") - emote_hear = list("brays") - emote_see = list("shakes its head") - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab = 6) - response_help = "pets the" - response_disarm = "gently pushes aside the" - response_harm = "kicks the" - attacktext = "kicks" - attack_sound = 'sound/weapons/punch1.ogg' - health = 50 - maxHealth = 50 - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN - blood_volume = BLOOD_VOLUME_NORMAL - var/obj/item/udder/udder = null - -/mob/living/simple_animal/cow/Initialize() - udder = new() - . = ..() - -/mob/living/simple_animal/cow/Destroy() - qdel(udder) - udder = null - return ..() - -/mob/living/simple_animal/cow/attackby(obj/item/O, mob/user, params) - if(stat == CONSCIOUS && istype(O, /obj/item/reagent_containers/glass)) - udder.milkAnimal(O, user) - return 1 - else - return ..() - -/mob/living/simple_animal/cow/Life(seconds, times_fired) - . = ..() - if(stat == CONSCIOUS) - udder.generateMilk() - -/mob/living/simple_animal/cow/attack_hand(mob/living/carbon/M as mob) - if(!stat && M.a_intent == INTENT_DISARM && icon_state != icon_dead) - M.visible_message("[M] tips over [src].","You tip over [src].") - Weaken(30) - icon_state = icon_dead - spawn(rand(20,50)) - if(!stat && M) - icon_state = icon_living - var/list/responses = list( "[src] looks at you imploringly.", - "[src] looks at you pleadingly", - "[src] looks at you with a resigned expression.", - "[src] seems resigned to its fate.") - to_chat(M, pick(responses)) - else - ..() - -/mob/living/simple_animal/chick - name = "\improper chick" - desc = "Adorable! They make such a racket though." - icon_state = "chick" - icon_living = "chick" - icon_dead = "chick_dead" - icon_gib = "chick_gib" - gender = FEMALE - speak = list("Cherp.","Cherp?","Chirrup.","Cheep!") - speak_emote = list("cheeps") - emote_hear = list("cheeps") - emote_see = list("pecks at the ground","flaps its tiny wings") - density = 0 - speak_chance = 2 - turns_per_move = 2 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 1) - response_help = "pets the" - response_disarm = "gently pushes aside the" - response_harm = "kicks the" - attacktext = "kicks" - health = 3 - maxHealth = 3 - ventcrawler = 2 - var/amount_grown = 0 - pass_flags = PASSTABLE | PASSGRILLE | PASSMOB - mob_size = MOB_SIZE_TINY - can_hide = 1 - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN - -/mob/living/simple_animal/chick/New() - ..() - pixel_x = rand(-6, 6) - pixel_y = rand(0, 10) - -/mob/living/simple_animal/chick/Life(seconds, times_fired) - . =..() - if(.) - amount_grown += rand(1,2) - if(amount_grown >= 100) - var/mob/living/simple_animal/chicken/C = new /mob/living/simple_animal/chicken(loc) - if(mind) - mind.transfer_to(C) - qdel(src) - -var/const/MAX_CHICKENS = 50 -var/global/chicken_count = 0 - -/mob/living/simple_animal/chicken - name = "\improper chicken" - desc = "Hopefully the eggs are good this season." - gender = FEMALE - icon_state = "chicken_brown" - icon_living = "chicken_brown" - icon_dead = "chicken_brown_dead" - speak = list("Cluck!","BWAAAAARK BWAK BWAK BWAK!","Bwaak bwak.") - speak_emote = list("clucks","croons") - emote_hear = list("clucks") - emote_see = list("pecks at the ground","flaps its wings viciously") - density = 0 - speak_chance = 2 - turns_per_move = 3 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 2) - var/egg_type = /obj/item/reagent_containers/food/snacks/egg - var/food_type = /obj/item/reagent_containers/food/snacks/grown/wheat - response_help = "pets the" - response_disarm = "gently pushes aside the" - response_harm = "kicks the" - attacktext = "kicks" - health = 15 - maxHealth = 15 - ventcrawler = 2 - var/eggsleft = 0 - var/eggsFertile = TRUE - var/body_color - var/icon_prefix = "chicken" - pass_flags = PASSTABLE | PASSMOB - mob_size = MOB_SIZE_SMALL - can_hide = 1 - can_collar = 1 - var/list/feedMessages = list("It clucks happily.","It clucks happily.") - var/list/layMessage = EGG_LAYING_MESSAGES - var/list/validColors = list("brown","black","white") - gold_core_spawnable = FRIENDLY_SPAWN - -/mob/living/simple_animal/chicken/New() - ..() - if(!body_color) - body_color = pick(validColors) - icon_state = "[icon_prefix]_[body_color]" - icon_living = "[icon_prefix]_[body_color]" - icon_dead = "[icon_prefix]_[body_color]_dead" - pixel_x = rand(-6, 6) - pixel_y = rand(0, 10) - chicken_count += 1 - -/mob/living/simple_animal/chicken/death(gibbed) - // Only execute the below if we successfully died - . = ..(gibbed) - if(!.) - return - chicken_count -= 1 - -/mob/living/simple_animal/chicken/attackby(obj/item/O, mob/user, params) - if(istype(O, food_type)) //feedin' dem chickens - if(!stat && eggsleft < 8) - var/feedmsg = "[user] feeds [O] to [name]! [pick(feedMessages)]" - user.visible_message(feedmsg) - user.drop_item() - qdel(O) - eggsleft += rand(1, 4) - //world << eggsleft - else - to_chat(user, "[name] doesn't seem hungry!") - else - ..() - -/mob/living/simple_animal/chicken/Life(seconds, times_fired) - . = ..() - if((. && prob(3) && eggsleft > 0) && egg_type) - visible_message("[src] [pick(layMessage)]") - eggsleft-- - var/obj/item/E = new egg_type(get_turf(src)) - E.pixel_x = rand(-6,6) - E.pixel_y = rand(-6,6) - if(eggsFertile) - if(chicken_count < MAX_CHICKENS && prob(25)) - START_PROCESSING(SSobj, E) - -/obj/item/reagent_containers/food/snacks/egg/var/amount_grown = 0 -/obj/item/reagent_containers/food/snacks/egg/process() - if(isturf(loc)) - amount_grown += rand(1,2) - if(amount_grown >= 100) - visible_message("[src] hatches with a quiet cracking sound.") - new /mob/living/simple_animal/chick(get_turf(src)) - STOP_PROCESSING(SSobj, src) - qdel(src) - else - STOP_PROCESSING(SSobj, src) - - -/mob/living/simple_animal/pig - name = "pig" - desc = "Oink oink." - icon_state = "pig" - icon_living = "pig" - icon_dead = "pig_dead" - speak = list("oink?","oink","OINK") - speak_emote = list("oinks") -// emote_hear = list("brays") - emote_see = list("rolls around") - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/ham = 6) - response_help = "pets the" - response_disarm = "gently pushes aside the" - response_harm = "kicks the" - attacktext = "kicks" - health = 50 - maxHealth = 50 - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN - blood_volume = BLOOD_VOLUME_NORMAL - -/mob/living/simple_animal/turkey - name = "turkey" - desc = "Benjamin Franklin would be proud." - icon_state = "turkey" - icon_living = "turkey" - icon_dead = "turkey_dead" - icon_resting = "turkey_rest" - speak = list("gobble?","gobble","GOBBLE") - speak_emote = list("gobble") - emote_see = list("struts around") - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 4) - response_help = "pets the" - response_disarm = "gently pushes aside the" - response_harm = "kicks the" - attacktext = "pecks" - health = 50 - maxHealth = 50 - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN - -/mob/living/simple_animal/goose - name = "goose" - desc = "A pretty goose. Would make a nice comforter." - icon_state = "goose" - icon_living = "goose" - icon_dead = "goose_dead" - speak = list("quack?","quack","QUACK") - speak_emote = list("quacks") -// emote_hear = list("brays") - emote_see = list("flaps it's wings") - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 6) - response_help = "pets the" - response_disarm = "gently pushes aside the" - response_harm = "kicks the" - attacktext = "kicks" - health = 50 - maxHealth = 50 - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN - -/mob/living/simple_animal/seal - name = "seal" - desc = "A beautiful white seal." - icon_state = "seal" - icon_living = "seal" - icon_dead = "seal_dead" - speak = list("Urk?","urk","URK") - speak_emote = list("urks") -// emote_hear = list("brays") - emote_see = list("flops around") - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 6) - response_help = "pets the" - response_disarm = "gently pushes aside the" - response_harm = "kicks the" - attacktext = "kicks" - health = 50 - maxHealth = 50 - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN - blood_volume = BLOOD_VOLUME_NORMAL - -/mob/living/simple_animal/walrus - name = "walrus" - desc = "A big brown walrus." - icon_state = "walrus" - icon_living = "walrus" - icon_dead = "walrus_dead" - speak = list("Urk?","urk","URK") - speak_emote = list("urks") -// emote_hear = list("brays") - emote_see = list("flops around") - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 6) - response_help = "pets the" - response_disarm = "gently pushes aside the" - response_harm = "kicks the" - attacktext = "kicks" - health = 50 - maxHealth = 50 - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN - blood_volume = BLOOD_VOLUME_NORMAL - -/obj/item/udder - name = "udder" - -/obj/item/udder/New() - create_reagents(50) - reagents.add_reagent("milk", 20) - . = ..() - -/obj/item/udder/proc/generateMilk() - if(prob(5)) - reagents.add_reagent("milk", rand(5, 10)) - -/obj/item/udder/proc/milkAnimal(obj/O, mob/user) - var/obj/item/reagent_containers/glass/G = O - if(G.reagents.total_volume >= G.volume) - to_chat(user, "[O] is full.") - return - var/transfered = reagents.trans_to(O, rand(5,10)) - if(transfered) - user.visible_message("[user] milks [src] using \the [O].", "You milk [src] using \the [O].") - else - to_chat(user, "The udder is dry. Wait a bit longer...") \ No newline at end of file +//goat +/mob/living/simple_animal/hostile/retaliate/goat + name = "goat" + desc = "Not known for their pleasant disposition." + icon_state = "goat" + icon_living = "goat" + icon_dead = "goat_dead" + speak = list("EHEHEHEHEH","eh?") + speak_emote = list("brays") + emote_hear = list("brays") + emote_see = list("shakes its head", "stamps a foot", "glares around") + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 4) + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "kicks" + faction = list("neutral") + attack_same = 1 + attacktext = "kicks" + attack_sound = 'sound/weapons/punch1.ogg' + health = 40 + maxHealth = 40 + melee_damage_lower = 1 + melee_damage_upper = 2 + stop_automated_movement_when_pulled = 1 + can_collar = 1 + blood_volume = BLOOD_VOLUME_NORMAL + var/obj/item/udder/udder = null + +/mob/living/simple_animal/hostile/retaliate/goat/New() + udder = new() + . = ..() + +/mob/living/simple_animal/hostile/retaliate/goat/Destroy() + QDEL_NULL(udder) + return ..() + +/mob/living/simple_animal/hostile/retaliate/goat/handle_automated_movement() + . = ..() + //chance to go crazy and start wacking stuff + if(!enemies.len && prob(1)) + Retaliate() + + if(enemies.len && prob(10)) + enemies = list() + LoseTarget() + visible_message("[src] calms down.") + + eat_plants() + if(!pulledby) + for(var/direction in shuffle(list(1, 2, 4, 8, 5, 6, 9, 10))) + var/step = get_step(src, direction) + if(step) + if(locate(/obj/structure/spacevine) in step || locate(/obj/structure/glowshroom) in step) + Move(step, get_dir(src, step)) + +/mob/living/simple_animal/hostile/retaliate/goat/Life(seconds, times_fired) + . = ..() + if(stat == CONSCIOUS) + udder.generateMilk() + +/mob/living/simple_animal/hostile/retaliate/goat/Retaliate() + ..() + visible_message("[src] gets an evil-looking gleam in their eye.") + +/mob/living/simple_animal/hostile/retaliate/goat/Move() + . = ..() + if(!stat) + eat_plants() + +/mob/living/simple_animal/hostile/retaliate/goat/attackby(var/obj/item/O as obj, var/mob/user as mob, params) + if(stat == CONSCIOUS && istype(O, /obj/item/reagent_containers/glass)) + udder.milkAnimal(O, user) + else + return ..() + +/mob/living/simple_animal/hostile/retaliate/goat/proc/eat_plants() + var/eaten = FALSE + var/obj/structure/spacevine/SV = locate(/obj/structure/spacevine) in loc + if(SV) + SV.eat(src) + eaten = TRUE + + var/obj/structure/glowshroom/GS = locate(/obj/structure/glowshroom) in loc + if(GS) + qdel(GS) + eaten = TRUE + + if(eaten && prob(10)) + say("Nom") + +/mob/living/simple_animal/hostile/retaliate/goat/AttackingTarget() + . = ..() + if(. && isdiona(target)) + var/mob/living/carbon/human/H = target + var/obj/item/organ/external/NB = pick(H.bodyparts) + H.visible_message("[src] takes a big chomp out of [H]!", "[src] takes a big chomp out of your [NB.name]!") + NB.droplimb() + +//cow +/mob/living/simple_animal/cow + name = "cow" + desc = "Known for their milk, just don't tip them over." + icon_state = "cow" + icon_living = "cow" + icon_dead = "cow_dead" + icon_gib = "cow_gib" + speak = list("moo?","moo","MOOOOOO") + speak_emote = list("moos","moos hauntingly") + emote_hear = list("brays") + emote_see = list("shakes its head") + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/slab = 6) + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "kicks the" + attacktext = "kicks" + attack_sound = 'sound/weapons/punch1.ogg' + health = 50 + maxHealth = 50 + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN + blood_volume = BLOOD_VOLUME_NORMAL + var/obj/item/udder/udder = null + +/mob/living/simple_animal/cow/Initialize() + udder = new() + . = ..() + +/mob/living/simple_animal/cow/Destroy() + qdel(udder) + udder = null + return ..() + +/mob/living/simple_animal/cow/attackby(obj/item/O, mob/user, params) + if(stat == CONSCIOUS && istype(O, /obj/item/reagent_containers/glass)) + udder.milkAnimal(O, user) + return 1 + else + return ..() + +/mob/living/simple_animal/cow/Life(seconds, times_fired) + . = ..() + if(stat == CONSCIOUS) + udder.generateMilk() + +/mob/living/simple_animal/cow/attack_hand(mob/living/carbon/M as mob) + if(!stat && M.a_intent == INTENT_DISARM && icon_state != icon_dead) + M.visible_message("[M] tips over [src].","You tip over [src].") + Weaken(30) + icon_state = icon_dead + spawn(rand(20,50)) + if(!stat && M) + icon_state = icon_living + var/list/responses = list( "[src] looks at you imploringly.", + "[src] looks at you pleadingly", + "[src] looks at you with a resigned expression.", + "[src] seems resigned to its fate.") + to_chat(M, pick(responses)) + else + ..() + +/mob/living/simple_animal/chick + name = "\improper chick" + desc = "Adorable! They make such a racket though." + icon_state = "chick" + icon_living = "chick" + icon_dead = "chick_dead" + icon_gib = "chick_gib" + gender = FEMALE + speak = list("Cherp.","Cherp?","Chirrup.","Cheep!") + speak_emote = list("cheeps") + emote_hear = list("cheeps") + emote_see = list("pecks at the ground","flaps its tiny wings") + density = 0 + speak_chance = 2 + turns_per_move = 2 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 1) + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "kicks the" + attacktext = "kicks" + health = 3 + maxHealth = 3 + ventcrawler = 2 + var/amount_grown = 0 + pass_flags = PASSTABLE | PASSGRILLE | PASSMOB + mob_size = MOB_SIZE_TINY + can_hide = 1 + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN + +/mob/living/simple_animal/chick/New() + ..() + pixel_x = rand(-6, 6) + pixel_y = rand(0, 10) + +/mob/living/simple_animal/chick/Life(seconds, times_fired) + . =..() + if(.) + amount_grown += rand(1,2) + if(amount_grown >= 100) + var/mob/living/simple_animal/chicken/C = new /mob/living/simple_animal/chicken(loc) + if(mind) + mind.transfer_to(C) + qdel(src) + +#define MAX_CHICKENS 50 +var/global/chicken_count = 0 + +/mob/living/simple_animal/chicken + name = "\improper chicken" + desc = "Hopefully the eggs are good this season." + gender = FEMALE + icon_state = "chicken_brown" + icon_living = "chicken_brown" + icon_dead = "chicken_brown_dead" + speak = list("Cluck!","BWAAAAARK BWAK BWAK BWAK!","Bwaak bwak.") + speak_emote = list("clucks","croons") + emote_hear = list("clucks") + emote_see = list("pecks at the ground","flaps its wings viciously") + density = 0 + speak_chance = 2 + turns_per_move = 3 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 2) + var/egg_type = /obj/item/reagent_containers/food/snacks/egg + var/food_type = /obj/item/reagent_containers/food/snacks/grown/wheat + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "kicks the" + attacktext = "kicks" + health = 15 + maxHealth = 15 + ventcrawler = 2 + var/eggsleft = 0 + var/eggsFertile = TRUE + var/body_color + var/icon_prefix = "chicken" + pass_flags = PASSTABLE | PASSMOB + mob_size = MOB_SIZE_SMALL + can_hide = 1 + can_collar = 1 + var/list/feedMessages = list("It clucks happily.","It clucks happily.") + var/list/layMessage = EGG_LAYING_MESSAGES + var/list/validColors = list("brown","black","white") + gold_core_spawnable = FRIENDLY_SPAWN + +/mob/living/simple_animal/chicken/New() + ..() + if(!body_color) + body_color = pick(validColors) + icon_state = "[icon_prefix]_[body_color]" + icon_living = "[icon_prefix]_[body_color]" + icon_dead = "[icon_prefix]_[body_color]_dead" + pixel_x = rand(-6, 6) + pixel_y = rand(0, 10) + chicken_count += 1 + +/mob/living/simple_animal/chicken/death(gibbed) + // Only execute the below if we successfully died + . = ..(gibbed) + if(!.) + return + chicken_count -= 1 + +/mob/living/simple_animal/chicken/attackby(obj/item/O, mob/user, params) + if(istype(O, food_type)) //feedin' dem chickens + if(!stat && eggsleft < 8) + var/feedmsg = "[user] feeds [O] to [name]! [pick(feedMessages)]" + user.visible_message(feedmsg) + user.drop_item() + qdel(O) + eggsleft += rand(1, 4) + //world << eggsleft + else + to_chat(user, "[name] doesn't seem hungry!") + else + ..() + +/mob/living/simple_animal/chicken/Life(seconds, times_fired) + . = ..() + if((. && prob(3) && eggsleft > 0) && egg_type) + visible_message("[src] [pick(layMessage)]") + eggsleft-- + var/obj/item/E = new egg_type(get_turf(src)) + E.pixel_x = rand(-6,6) + E.pixel_y = rand(-6,6) + if(eggsFertile) + if(chicken_count < MAX_CHICKENS && prob(25)) + START_PROCESSING(SSobj, E) + +/obj/item/reagent_containers/food/snacks/egg/var/amount_grown = 0 +/obj/item/reagent_containers/food/snacks/egg/process() + if(isturf(loc)) + amount_grown += rand(1,2) + if(amount_grown >= 100) + visible_message("[src] hatches with a quiet cracking sound.") + new /mob/living/simple_animal/chick(get_turf(src)) + STOP_PROCESSING(SSobj, src) + qdel(src) + else + STOP_PROCESSING(SSobj, src) + + +/mob/living/simple_animal/pig + name = "pig" + desc = "Oink oink." + icon_state = "pig" + icon_living = "pig" + icon_dead = "pig_dead" + speak = list("oink?","oink","OINK") + speak_emote = list("oinks") +// emote_hear = list("brays") + emote_see = list("rolls around") + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/ham = 6) + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "kicks the" + attacktext = "kicks" + health = 50 + maxHealth = 50 + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN + blood_volume = BLOOD_VOLUME_NORMAL + +/mob/living/simple_animal/turkey + name = "turkey" + desc = "Benjamin Franklin would be proud." + icon_state = "turkey" + icon_living = "turkey" + icon_dead = "turkey_dead" + icon_resting = "turkey_rest" + speak = list("gobble?","gobble","GOBBLE") + speak_emote = list("gobble") + emote_see = list("struts around") + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 4) + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "kicks the" + attacktext = "pecks" + health = 50 + maxHealth = 50 + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN + +/mob/living/simple_animal/goose + name = "goose" + desc = "A pretty goose. Would make a nice comforter." + icon_state = "goose" + icon_living = "goose" + icon_dead = "goose_dead" + speak = list("quack?","quack","QUACK") + speak_emote = list("quacks") +// emote_hear = list("brays") + emote_see = list("flaps it's wings") + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 6) + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "kicks the" + attacktext = "kicks" + health = 50 + maxHealth = 50 + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN + +/mob/living/simple_animal/seal + name = "seal" + desc = "A beautiful white seal." + icon_state = "seal" + icon_living = "seal" + icon_dead = "seal_dead" + speak = list("Urk?","urk","URK") + speak_emote = list("urks") +// emote_hear = list("brays") + emote_see = list("flops around") + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 6) + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "kicks the" + attacktext = "kicks" + health = 50 + maxHealth = 50 + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN + blood_volume = BLOOD_VOLUME_NORMAL + +/mob/living/simple_animal/walrus + name = "walrus" + desc = "A big brown walrus." + icon_state = "walrus" + icon_living = "walrus" + icon_dead = "walrus_dead" + speak = list("Urk?","urk","URK") + speak_emote = list("urks") +// emote_hear = list("brays") + emote_see = list("flops around") + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 6) + response_help = "pets the" + response_disarm = "gently pushes aside the" + response_harm = "kicks the" + attacktext = "kicks" + health = 50 + maxHealth = 50 + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN + blood_volume = BLOOD_VOLUME_NORMAL + +/obj/item/udder + name = "udder" + +/obj/item/udder/New() + create_reagents(50) + reagents.add_reagent("milk", 20) + . = ..() + +/obj/item/udder/proc/generateMilk() + if(prob(5)) + reagents.add_reagent("milk", rand(5, 10)) + +/obj/item/udder/proc/milkAnimal(obj/O, mob/user) + var/obj/item/reagent_containers/glass/G = O + if(G.reagents.total_volume >= G.volume) + to_chat(user, "[O] is full.") + return + var/transfered = reagents.trans_to(O, rand(5,10)) + if(transfered) + user.visible_message("[user] milks [src] using \the [O].", "You milk [src] using \the [O].") + else + to_chat(user, "The udder is dry. Wait a bit longer...") diff --git a/code/modules/mob/living/simple_animal/friendly/fox.dm b/code/modules/mob/living/simple_animal/friendly/fox.dm index ad77fd547a5..9eccc473e6d 100644 --- a/code/modules/mob/living/simple_animal/friendly/fox.dm +++ b/code/modules/mob/living/simple_animal/friendly/fox.dm @@ -40,4 +40,4 @@ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) minbodytemp = 0 melee_damage_lower = 10 - melee_damage_upper = 20 \ No newline at end of file + melee_damage_upper = 20 diff --git a/code/modules/mob/living/simple_animal/friendly/lizard.dm b/code/modules/mob/living/simple_animal/friendly/lizard.dm index 2d7426aa451..96688963b73 100644 --- a/code/modules/mob/living/simple_animal/friendly/lizard.dm +++ b/code/modules/mob/living/simple_animal/friendly/lizard.dm @@ -1,25 +1,25 @@ -/mob/living/simple_animal/lizard - name = "Lizard" - desc = "A cute tiny lizard." - icon = 'icons/mob/critter.dmi' - icon_state = "lizard" - icon_living = "lizard" - icon_dead = "lizard-dead" - speak_emote = list("hisses") - health = 5 - maxHealth = 5 - attacktext = "bites" - obj_damage = 0 - melee_damage_lower = 1 - melee_damage_upper = 2 - response_help = "pets" - response_disarm = "shoos" - response_harm = "stomps on" - ventcrawler = 2 - density = 0 - pass_flags = PASSTABLE | PASSMOB - mob_size = MOB_SIZE_SMALL - can_hide = 1 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 1) - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN +/mob/living/simple_animal/lizard + name = "Lizard" + desc = "A cute tiny lizard." + icon = 'icons/mob/critter.dmi' + icon_state = "lizard" + icon_living = "lizard" + icon_dead = "lizard-dead" + speak_emote = list("hisses") + health = 5 + maxHealth = 5 + attacktext = "bites" + obj_damage = 0 + melee_damage_lower = 1 + melee_damage_upper = 2 + response_help = "pets" + response_disarm = "shoos" + response_harm = "stomps on" + ventcrawler = 2 + density = 0 + pass_flags = PASSTABLE | PASSMOB + mob_size = MOB_SIZE_SMALL + can_hide = 1 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 1) + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN diff --git a/code/modules/mob/living/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm index 3ca0c840efd..1446ef7d4bb 100644 --- a/code/modules/mob/living/simple_animal/friendly/mouse.dm +++ b/code/modules/mob/living/simple_animal/friendly/mouse.dm @@ -1,236 +1,236 @@ -/mob/living/simple_animal/mouse - name = "mouse" - real_name = "mouse" - desc = "It's a small, disease-ridden rodent." - icon_state = "mouse_gray" - icon_living = "mouse_gray" - icon_dead = "mouse_gray_dead" - icon_resting = "mouse_gray_sleep" - speak = list("Squeek!","SQUEEK!","Squeek?") - speak_emote = list("squeeks","squeaks","squiks") - emote_hear = list("squeeks","squeaks","squiks") - emote_see = list("runs in a circle", "shakes", "scritches at something") - var/squeak_sound = 'sound/creatures/mousesqueak.ogg' - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - maxHealth = 5 - health = 5 - butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 1) - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "stamps on" - density = 0 - ventcrawler = 2 - pass_flags = PASSTABLE | PASSGRILLE | PASSMOB - mob_size = MOB_SIZE_TINY - var/mouse_color //brown, gray and white, leave blank for random - layer = MOB_LAYER - atmos_requirements = list("min_oxy" = 16, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 223 //Below -50 Degrees Celcius - maxbodytemp = 323 //Above 50 Degrees Celcius - universal_speak = 0 - can_hide = 1 - holder_type = /obj/item/holder/mouse - can_collar = 1 - gold_core_spawnable = FRIENDLY_SPAWN - var/chew_probability = 1 - -/mob/living/simple_animal/mouse/Initialize(mapload) - . = ..() - AddComponent(/datum/component/squeak, list('sound/creatures/mousesqueak.ogg' = 1), 100) - -/mob/living/simple_animal/mouse/handle_automated_action() - if(prob(chew_probability)) - var/turf/simulated/floor/F = get_turf(src) - if(istype(F) && !F.intact) - var/obj/structure/cable/C = locate() in F - if(C && prob(15)) - if(C.avail()) - visible_message("[src] chews through [C]. It's toast!") - playsound(src, 'sound/effects/sparks2.ogg', 100, 1) - C.deconstruct() - toast() // mmmm toasty. - else - C.deconstruct() - visible_message("[src] chews through [C].") - -/mob/living/simple_animal/mouse/handle_automated_speech() - ..() - if(prob(speak_chance) && !incapacitated()) - playsound(src, squeak_sound, 100, 1) - -/mob/living/simple_animal/mouse/handle_automated_movement() - . = ..() - if(resting) - if(prob(1)) - StopResting() - else if(prob(5)) - custom_emote(2, "snuffles") - else if(prob(0.5)) - StartResting() - -/mob/living/simple_animal/mouse/New() - ..() - if(!mouse_color) - mouse_color = pick( list("brown","gray","white") ) - icon_state = "mouse_[mouse_color]" - icon_living = "mouse_[mouse_color]" - icon_dead = "mouse_[mouse_color]_dead" - icon_resting = "mouse_[mouse_color]_sleep" - desc = "It's a small [mouse_color] rodent, often seen hiding in maintenance areas and making a nuisance of itself." - -/mob/living/simple_animal/mouse/proc/splat() - src.health = 0 - src.stat = DEAD - src.icon_dead = "mouse_[mouse_color]_splat" - src.icon_state = "mouse_[mouse_color]_splat" - layer = MOB_LAYER - if(client) - client.time_died_as_mouse = world.time - -/mob/living/simple_animal/mouse/attack_hand(mob/living/carbon/human/M as mob) - if(M.a_intent == INTENT_HELP) - get_scooped(M) - ..() - -/mob/living/simple_animal/mouse/start_pulling(var/atom/movable/AM)//Prevents mouse from pulling things - to_chat(src, "You are too small to pull anything.") - return - -/mob/living/simple_animal/mouse/Crossed(AM as mob|obj, oldloc) - if(ishuman(AM)) - if(!stat) - var/mob/M = AM - to_chat(M, "[bicon(src)] Squeek!") - ..() - -/mob/living/simple_animal/mouse/proc/toast() - add_atom_colour("#3A3A3A", FIXED_COLOUR_PRIORITY) - desc = "It's toast." - death() - -/mob/living/simple_animal/mouse/death(gibbed) - // Only execute the below if we successfully died - playsound(src, squeak_sound, 40, 1) - . = ..(gibbed) - if(!.) - return FALSE - layer = MOB_LAYER - if(client) - client.time_died_as_mouse = world.time - -/mob/living/simple_animal/mouse/emote(act, m_type = 1, message = null, force) - if(stat != CONSCIOUS) - return - - var/on_CD = 0 - act = lowertext(act) - switch(act) - if("squeak") //Mouse time - on_CD = handle_emote_CD() - else - on_CD = 0 - - if(!force && on_CD == 1) - return - - switch(act) - if("squeak") - message = "\The [src] [pick(emote_hear)]!" - m_type = 2 //audible - playsound(src, squeak_sound, 40, 1) - if("help") - to_chat(src, "scream, squeak") - - ..() - -/* - * Mouse types - */ - -/mob/living/simple_animal/mouse/white - mouse_color = "white" - icon_state = "mouse_white" - -/mob/living/simple_animal/mouse/gray - mouse_color = "gray" - icon_state = "mouse_gray" - -/mob/living/simple_animal/mouse/brown - mouse_color = "brown" - icon_state = "mouse_brown" - -//TOM IS ALIVE! SQUEEEEEEEE~K :) -/mob/living/simple_animal/mouse/brown/Tom - name = "Tom" - desc = "Jerry the cat is not amused." - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "splats" - unique_pet = TRUE - gold_core_spawnable = NO_SPAWN - - -/mob/living/simple_animal/mouse/blobinfected - maxHealth = 100 - health = 100 - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - gold_core_spawnable = NO_SPAWN - var/cycles_alive = 0 - var/cycles_limit = 60 - var/has_burst = FALSE - -/mob/living/simple_animal/mouse/blobinfected/Life() - cycles_alive++ - var/timeleft = (cycles_limit - cycles_alive) * 2 - if(ismob(loc)) // if someone ate it, burst immediately - burst(FALSE) - else if(timeleft < 1) // if timer expired, burst. - burst(FALSE) - else if(cycles_alive % 2 == 0) // give the mouse/player a countdown reminder every 2 cycles - to_chat(src, "[timeleft] seconds until you burst, and become a blob...") - return ..() - -/mob/living/simple_animal/mouse/blobinfected/death(gibbed) - burst(gibbed) - return ..(gibbed) - -/mob/living/simple_animal/mouse/blobinfected/proc/burst(gibbed) - if(has_burst) - return FALSE - var/turf/T = get_turf(src) - if(!is_station_level(T.z) || isspaceturf(T)) - to_chat(src, "You feel ready to burst, but this isn't an appropriate place! You must return to the station!") - return FALSE - has_burst = TRUE - var/datum/mind/blobmind = mind - var/client/C = client - if(istype(blobmind) && istype(C)) - blobmind.special_role = SPECIAL_ROLE_BLOB - var/obj/structure/blob/core/core = new(T, 200, C, 3) - core.lateblobtimer() - else - new /obj/structure/blob/core(T) // Ghosts will be prompted to control it. - if(ismob(loc)) // in case some taj/etc ate the mouse. - var/mob/M = loc - M.gib() - if(!gibbed) - gib() - -/mob/living/simple_animal/mouse/blobinfected/get_scooped(mob/living/carbon/grabber) - to_chat(grabber, "You try to pick up [src], but they slip out of your grasp!") - to_chat(src, "[src] tries to pick you up, but you wriggle free of their grasp!") - -/mob/living/simple_animal/mouse/fluff/clockwork - name = "Chip" - real_name = "Chip" - mouse_color = "clockwork" - icon_state = "mouse_clockwork" - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "stamps on" - gold_core_spawnable = NO_SPAWN - can_collar = 0 - butcher_results = list(/obj/item/stack/sheet/metal = 1) +/mob/living/simple_animal/mouse + name = "mouse" + real_name = "mouse" + desc = "It's a small, disease-ridden rodent." + icon_state = "mouse_gray" + icon_living = "mouse_gray" + icon_dead = "mouse_gray_dead" + icon_resting = "mouse_gray_sleep" + speak = list("Squeek!","SQUEEK!","Squeek?") + speak_emote = list("squeeks","squeaks","squiks") + emote_hear = list("squeeks","squeaks","squiks") + emote_see = list("runs in a circle", "shakes", "scritches at something") + var/squeak_sound = 'sound/creatures/mousesqueak.ogg' + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + maxHealth = 5 + health = 5 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat = 1) + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "stamps on" + density = 0 + ventcrawler = 2 + pass_flags = PASSTABLE | PASSGRILLE | PASSMOB + mob_size = MOB_SIZE_TINY + var/mouse_color //brown, gray and white, leave blank for random + layer = MOB_LAYER + atmos_requirements = list("min_oxy" = 16, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 223 //Below -50 Degrees Celcius + maxbodytemp = 323 //Above 50 Degrees Celcius + universal_speak = 0 + can_hide = 1 + holder_type = /obj/item/holder/mouse + can_collar = 1 + gold_core_spawnable = FRIENDLY_SPAWN + var/chew_probability = 1 + +/mob/living/simple_animal/mouse/Initialize(mapload) + . = ..() + AddComponent(/datum/component/squeak, list('sound/creatures/mousesqueak.ogg' = 1), 100) + +/mob/living/simple_animal/mouse/handle_automated_action() + if(prob(chew_probability) && isturf(loc)) + var/turf/simulated/floor/F = get_turf(src) + if(istype(F) && !F.intact) + var/obj/structure/cable/C = locate() in F + if(C && prob(15)) + if(C.avail()) + visible_message("[src] chews through [C]. It's toast!") + playsound(src, 'sound/effects/sparks2.ogg', 100, 1) + C.deconstruct() + toast() // mmmm toasty. + else + C.deconstruct() + visible_message("[src] chews through [C].") + +/mob/living/simple_animal/mouse/handle_automated_speech() + ..() + if(prob(speak_chance) && !incapacitated()) + playsound(src, squeak_sound, 100, 1) + +/mob/living/simple_animal/mouse/handle_automated_movement() + . = ..() + if(resting) + if(prob(1)) + StopResting() + else if(prob(5)) + custom_emote(2, "snuffles") + else if(prob(0.5)) + StartResting() + +/mob/living/simple_animal/mouse/New() + ..() + if(!mouse_color) + mouse_color = pick( list("brown","gray","white") ) + icon_state = "mouse_[mouse_color]" + icon_living = "mouse_[mouse_color]" + icon_dead = "mouse_[mouse_color]_dead" + icon_resting = "mouse_[mouse_color]_sleep" + desc = "It's a small [mouse_color] rodent, often seen hiding in maintenance areas and making a nuisance of itself." + +/mob/living/simple_animal/mouse/proc/splat() + src.health = 0 + src.stat = DEAD + src.icon_dead = "mouse_[mouse_color]_splat" + src.icon_state = "mouse_[mouse_color]_splat" + layer = MOB_LAYER + if(client) + client.time_died_as_mouse = world.time + +/mob/living/simple_animal/mouse/attack_hand(mob/living/carbon/human/M as mob) + if(M.a_intent == INTENT_HELP) + get_scooped(M) + ..() + +/mob/living/simple_animal/mouse/start_pulling(var/atom/movable/AM)//Prevents mouse from pulling things + to_chat(src, "You are too small to pull anything.") + return + +/mob/living/simple_animal/mouse/Crossed(AM as mob|obj, oldloc) + if(ishuman(AM)) + if(!stat) + var/mob/M = AM + to_chat(M, "[bicon(src)] Squeek!") + ..() + +/mob/living/simple_animal/mouse/proc/toast() + add_atom_colour("#3A3A3A", FIXED_COLOUR_PRIORITY) + desc = "It's toast." + death() + +/mob/living/simple_animal/mouse/death(gibbed) + // Only execute the below if we successfully died + playsound(src, squeak_sound, 40, 1) + . = ..(gibbed) + if(!.) + return FALSE + layer = MOB_LAYER + if(client) + client.time_died_as_mouse = world.time + +/mob/living/simple_animal/mouse/emote(act, m_type = 1, message = null, force) + if(stat != CONSCIOUS) + return + + var/on_CD = 0 + act = lowertext(act) + switch(act) + if("squeak") //Mouse time + on_CD = handle_emote_CD() + else + on_CD = 0 + + if(!force && on_CD == 1) + return + + switch(act) + if("squeak") + message = "\The [src] [pick(emote_hear)]!" + m_type = 2 //audible + playsound(src, squeak_sound, 40, 1) + if("help") + to_chat(src, "scream, squeak") + + ..() + +/* + * Mouse types + */ + +/mob/living/simple_animal/mouse/white + mouse_color = "white" + icon_state = "mouse_white" + +/mob/living/simple_animal/mouse/gray + mouse_color = "gray" + icon_state = "mouse_gray" + +/mob/living/simple_animal/mouse/brown + mouse_color = "brown" + icon_state = "mouse_brown" + +//TOM IS ALIVE! SQUEEEEEEEE~K :) +/mob/living/simple_animal/mouse/brown/Tom + name = "Tom" + desc = "Jerry the cat is not amused." + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "splats" + unique_pet = TRUE + gold_core_spawnable = NO_SPAWN + + +/mob/living/simple_animal/mouse/blobinfected + maxHealth = 100 + health = 100 + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + gold_core_spawnable = NO_SPAWN + var/cycles_alive = 0 + var/cycles_limit = 60 + var/has_burst = FALSE + +/mob/living/simple_animal/mouse/blobinfected/Life() + cycles_alive++ + var/timeleft = (cycles_limit - cycles_alive) * 2 + if(ismob(loc)) // if someone ate it, burst immediately + burst(FALSE) + else if(timeleft < 1) // if timer expired, burst. + burst(FALSE) + else if(cycles_alive % 2 == 0) // give the mouse/player a countdown reminder every 2 cycles + to_chat(src, "[timeleft] seconds until you burst, and become a blob...") + return ..() + +/mob/living/simple_animal/mouse/blobinfected/death(gibbed) + burst(gibbed) + return ..(gibbed) + +/mob/living/simple_animal/mouse/blobinfected/proc/burst(gibbed) + if(has_burst) + return FALSE + var/turf/T = get_turf(src) + if(!is_station_level(T.z) || isspaceturf(T)) + to_chat(src, "You feel ready to burst, but this isn't an appropriate place! You must return to the station!") + return FALSE + has_burst = TRUE + var/datum/mind/blobmind = mind + var/client/C = client + if(istype(blobmind) && istype(C)) + blobmind.special_role = SPECIAL_ROLE_BLOB + var/obj/structure/blob/core/core = new(T, 200, C, 3) + core.lateblobtimer() + else + new /obj/structure/blob/core(T) // Ghosts will be prompted to control it. + if(ismob(loc)) // in case some taj/etc ate the mouse. + var/mob/M = loc + M.gib() + if(!gibbed) + gib() + +/mob/living/simple_animal/mouse/blobinfected/get_scooped(mob/living/carbon/grabber) + to_chat(grabber, "You try to pick up [src], but they slip out of your grasp!") + to_chat(src, "[src] tries to pick you up, but you wriggle free of their grasp!") + +/mob/living/simple_animal/mouse/fluff/clockwork + name = "Chip" + real_name = "Chip" + mouse_color = "clockwork" + icon_state = "mouse_clockwork" + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "stamps on" + gold_core_spawnable = NO_SPAWN + can_collar = 0 + butcher_results = list(/obj/item/stack/sheet/metal = 1) diff --git a/code/modules/mob/living/simple_animal/friendly/penguin.dm b/code/modules/mob/living/simple_animal/friendly/penguin.dm index d2dba6da1c9..dc00e252661 100644 --- a/code/modules/mob/living/simple_animal/friendly/penguin.dm +++ b/code/modules/mob/living/simple_animal/friendly/penguin.dm @@ -64,4 +64,4 @@ icon_dead = "penguin_baby_dead" density = FALSE pass_flags = PASSMOB - mob_size = MOB_SIZE_SMALL \ No newline at end of file + mob_size = MOB_SIZE_SMALL diff --git a/code/modules/mob/living/simple_animal/friendly/pet.dm b/code/modules/mob/living/simple_animal/friendly/pet.dm index 8a9899b7fa9..ac4f3367002 100644 --- a/code/modules/mob/living/simple_animal/friendly/pet.dm +++ b/code/modules/mob/living/simple_animal/friendly/pet.dm @@ -13,4 +13,4 @@ setDir(i) sleep(1) else - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/mob/living/simple_animal/friendly/sloth.dm b/code/modules/mob/living/simple_animal/friendly/sloth.dm index ddf311d62d4..d5b47c189ff 100644 --- a/code/modules/mob/living/simple_animal/friendly/sloth.dm +++ b/code/modules/mob/living/simple_animal/friendly/sloth.dm @@ -29,4 +29,4 @@ name = "Paperwork" desc = "Cargo's pet sloth. About as useful as the rest of the techs." unique_pet = TRUE - gold_core_spawnable = NO_SPAWN \ No newline at end of file + gold_core_spawnable = NO_SPAWN diff --git a/code/modules/mob/living/simple_animal/friendly/snake.dm b/code/modules/mob/living/simple_animal/friendly/snake.dm index 1cd85c1b622..b4a15ba27b4 100644 --- a/code/modules/mob/living/simple_animal/friendly/snake.dm +++ b/code/modules/mob/living/simple_animal/friendly/snake.dm @@ -58,4 +58,4 @@ QDEL_NULL(target) adjustHealth(-2) else - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/mob/living/simple_animal/friendly/spiderbot.dm b/code/modules/mob/living/simple_animal/friendly/spiderbot.dm index 424d852d56b..7a1227960f4 100644 --- a/code/modules/mob/living/simple_animal/friendly/spiderbot.dm +++ b/code/modules/mob/living/simple_animal/friendly/spiderbot.dm @@ -87,19 +87,6 @@ update_icon() return 1 - else if(iswelder(O) && user.a_intent == INTENT_HELP) - var/obj/item/weldingtool/WT = O - user.changeNext_move(CLICK_CD_MELEE) - if(WT.remove_fuel(0)) - if(health < maxHealth) - adjustHealth(-5) - playsound(loc, WT.usesound, 50, 1) - add_fingerprint(user) - visible_message("[user] has spot-welded some of the damage to [src]!") - else - to_chat(user, "[src] is undamaged!") - else - to_chat(user, "Need more welding fuel!") else if(istype(O, /obj/item/card/id) || istype(O, /obj/item/pda)) if(!mmi) to_chat(user, "There's no reason to swipe your ID - the spiderbot has no brain to remove.") @@ -117,7 +104,7 @@ var/obj/item/pda/pda = O id_card = pda.id - if(access_robotics in id_card.access) + if(ACCESS_ROBOTICS in id_card.access) to_chat(user, "You swipe your access card and pop the brain out of [src].") eject_brain() return 1 @@ -128,6 +115,23 @@ else ..() +/mob/living/simple_animal/spiderbot/welder_act(mob/user, obj/item/I) + if(user.a_intent != INTENT_HELP) + return + if(user == src) //No self-repair dummy + return + if(health >= maxHealth) + to_chat(user, "[src] does not need repairing!") + return + to_chat(user, "Unable to repair with the maintenance panel closed!") + return + . = TRUE + if(!I.use_tool(src, user, volume = I.tool_volume)) + return + adjustHealth(-5) + add_fingerprint(user) + user.visible_message("[user] repairs [src]!","You repair [src].") + /mob/living/simple_animal/spiderbot/emag_act(mob/living/user) if(emagged) to_chat(user, "[src] doesn't seem to respond.") diff --git a/code/modules/mob/living/simple_animal/hostile/alien.dm b/code/modules/mob/living/simple_animal/hostile/alien.dm index b541d55096a..5a369a043d7 100644 --- a/code/modules/mob/living/simple_animal/hostile/alien.dm +++ b/code/modules/mob/living/simple_animal/hostile/alien.dm @@ -1,164 +1,164 @@ -/mob/living/simple_animal/hostile/alien - name = "alien hunter" - desc = "Hiss!" - icon = 'icons/mob/alien.dmi' - icon_state = "alienh_running" - icon_living = "alienh_running" - icon_dead = "alienh_dead" - icon_gib = "syndicate_gib" - gender = FEMALE - response_help = "pokes" - response_disarm = "shoves" - response_harm = "hits" - speed = 0 - butcher_results = list(/obj/item/reagent_containers/food/snacks/xenomeat = 3, /obj/item/stack/sheet/animalhide/xeno = 1) - maxHealth = 125 - health = 125 - harm_intent_damage = 5 - obj_damage = 60 - melee_damage_lower = 25 - melee_damage_upper = 25 - attacktext = "slashes" - speak_emote = list("hisses") - a_intent = INTENT_HARM - attack_sound = 'sound/weapons/bladeslice.ogg' - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - unsuitable_atmos_damage = 15 - heat_damage_per_tick = 20 - faction = list("alien") - status_flags = CANPUSH - minbodytemp = 0 - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - gold_core_spawnable = HOSTILE_SPAWN - death_sound = 'sound/voice/hiss6.ogg' - deathmessage = "lets out a waning guttural screech, green blood bubbling from its maw..." - - -/mob/living/simple_animal/hostile/alien/drone - name = "alien drone" - icon_state = "aliend_running" - icon_living = "aliend_running" - icon_dead = "aliend_dead" - melee_damage_lower = 15 - melee_damage_upper = 15 - var/plant_cooldown = 30 - var/plants_off = 0 - -/mob/living/simple_animal/hostile/alien/drone/handle_automated_action() - if(!..()) //AIStatus is off - return - plant_cooldown-- - if(AIStatus == AI_IDLE) - if(!plants_off && prob(10) && plant_cooldown<=0) - plant_cooldown = initial(plant_cooldown) - SpreadPlants() - -/mob/living/simple_animal/hostile/alien/sentinel - name = "alien sentinel" - icon_state = "aliens_running" - icon_living = "aliens_running" - icon_dead = "aliens_dead" - health = 150 - maxHealth = 150 - melee_damage_lower = 15 - melee_damage_upper = 15 - ranged = 1 - retreat_distance = 5 - minimum_distance = 5 - projectiletype = /obj/item/projectile/neurotox - projectilesound = 'sound/weapons/pierce.ogg' - - -/mob/living/simple_animal/hostile/alien/queen - name = "alien queen" - icon_state = "alienq_running" - icon_living = "alienq_running" - icon_dead = "alienq_d" - health = 250 - maxHealth = 250 - melee_damage_lower = 15 - melee_damage_upper = 15 - ranged = 1 - retreat_distance = 5 - minimum_distance = 5 - move_to_delay = 4 - butcher_results = list(/obj/item/reagent_containers/food/snacks/xenomeat = 4, /obj/item/stack/sheet/animalhide/xeno = 1) - projectiletype = /obj/item/projectile/neurotox - projectilesound = 'sound/weapons/pierce.ogg' - status_flags = 0 - var/sterile = 1 - var/plants_off = 0 - var/egg_cooldown = 30 - var/plant_cooldown = 30 - -/mob/living/simple_animal/hostile/alien/queen/handle_automated_action() - if(!..()) - return - egg_cooldown-- - plant_cooldown-- - if(AIStatus == AI_IDLE) - if(!plants_off && prob(10) && plant_cooldown<=0) - plant_cooldown = initial(plant_cooldown) - SpreadPlants() - if(!sterile && prob(10) && egg_cooldown<=0) - egg_cooldown = initial(egg_cooldown) - LayEggs() - -/mob/living/simple_animal/hostile/alien/proc/SpreadPlants() - if(!isturf(loc) || isspaceturf(loc)) - return - if(locate(/obj/structure/alien/weeds/node) in get_turf(src)) - return - visible_message("[src] has planted some alien weeds!") - new /obj/structure/alien/weeds/node(loc) - -/mob/living/simple_animal/hostile/alien/proc/LayEggs() - if(!isturf(loc) || isspaceturf(loc)) - return - if(locate(/obj/structure/alien/egg) in get_turf(src)) - return - visible_message("[src] has laid an egg!") - new /obj/structure/alien/egg(loc) - -/mob/living/simple_animal/hostile/alien/queen/large - name = "alien empress" - icon = 'icons/mob/alienlarge.dmi' - icon_state = "queen_s" - icon_living = "queen_s" - icon_dead = "queen_dead" - move_to_delay = 4 - maxHealth = 400 - health = 400 - butcher_results = list(/obj/item/reagent_containers/food/snacks/xenomeat = 10, /obj/item/stack/sheet/animalhide/xeno = 2) - mob_size = MOB_SIZE_LARGE - gold_core_spawnable = NO_SPAWN - -/obj/item/projectile/neurotox - name = "neurotoxin" - damage = 30 - icon_state = "toxin" - -/mob/living/simple_animal/hostile/alien/maid - name = "lusty xenomorph maid" - melee_damage_lower = 0 - melee_damage_upper = 0 - a_intent = INTENT_HELP - friendly = "caresses" - obj_damage = 0 - environment_smash = ENVIRONMENT_SMASH_NONE - gold_core_spawnable = HOSTILE_SPAWN - icon_state = "maid" - icon_living = "maid" - icon_dead = "maid_dead" - -/mob/living/simple_animal/hostile/alien/maid/AttackingTarget() - if(ismovableatom(target)) - if(istype(target, /obj/effect/decal/cleanable)) - visible_message("\The [src] cleans up \the [target].") - qdel(target) - return TRUE - var/atom/movable/M = target - M.clean_blood() - visible_message("\The [src] polishes \the [target].") - return TRUE +/mob/living/simple_animal/hostile/alien + name = "alien hunter" + desc = "Hiss!" + icon = 'icons/mob/alien.dmi' + icon_state = "alienh_running" + icon_living = "alienh_running" + icon_dead = "alienh_dead" + icon_gib = "syndicate_gib" + gender = FEMALE + response_help = "pokes" + response_disarm = "shoves" + response_harm = "hits" + speed = 0 + butcher_results = list(/obj/item/reagent_containers/food/snacks/xenomeat = 3, /obj/item/stack/sheet/animalhide/xeno = 1) + maxHealth = 125 + health = 125 + harm_intent_damage = 5 + obj_damage = 60 + melee_damage_lower = 25 + melee_damage_upper = 25 + attacktext = "slashes" + speak_emote = list("hisses") + a_intent = INTENT_HARM + attack_sound = 'sound/weapons/bladeslice.ogg' + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + unsuitable_atmos_damage = 15 + heat_damage_per_tick = 20 + faction = list("alien") + status_flags = CANPUSH + minbodytemp = 0 + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE + gold_core_spawnable = HOSTILE_SPAWN + death_sound = 'sound/voice/hiss6.ogg' + deathmessage = "lets out a waning guttural screech, green blood bubbling from its maw..." + + +/mob/living/simple_animal/hostile/alien/drone + name = "alien drone" + icon_state = "aliend_running" + icon_living = "aliend_running" + icon_dead = "aliend_dead" + melee_damage_lower = 15 + melee_damage_upper = 15 + var/plant_cooldown = 30 + var/plants_off = 0 + +/mob/living/simple_animal/hostile/alien/drone/handle_automated_action() + if(!..()) //AIStatus is off + return + plant_cooldown-- + if(AIStatus == AI_IDLE) + if(!plants_off && prob(10) && plant_cooldown<=0) + plant_cooldown = initial(plant_cooldown) + SpreadPlants() + +/mob/living/simple_animal/hostile/alien/sentinel + name = "alien sentinel" + icon_state = "aliens_running" + icon_living = "aliens_running" + icon_dead = "aliens_dead" + health = 150 + maxHealth = 150 + melee_damage_lower = 15 + melee_damage_upper = 15 + ranged = 1 + retreat_distance = 5 + minimum_distance = 5 + projectiletype = /obj/item/projectile/neurotox + projectilesound = 'sound/weapons/pierce.ogg' + + +/mob/living/simple_animal/hostile/alien/queen + name = "alien queen" + icon_state = "alienq_running" + icon_living = "alienq_running" + icon_dead = "alienq_d" + health = 250 + maxHealth = 250 + melee_damage_lower = 15 + melee_damage_upper = 15 + ranged = 1 + retreat_distance = 5 + minimum_distance = 5 + move_to_delay = 4 + butcher_results = list(/obj/item/reagent_containers/food/snacks/xenomeat = 4, /obj/item/stack/sheet/animalhide/xeno = 1) + projectiletype = /obj/item/projectile/neurotox + projectilesound = 'sound/weapons/pierce.ogg' + status_flags = 0 + var/sterile = 1 + var/plants_off = 0 + var/egg_cooldown = 30 + var/plant_cooldown = 30 + +/mob/living/simple_animal/hostile/alien/queen/handle_automated_action() + if(!..()) + return + egg_cooldown-- + plant_cooldown-- + if(AIStatus == AI_IDLE) + if(!plants_off && prob(10) && plant_cooldown<=0) + plant_cooldown = initial(plant_cooldown) + SpreadPlants() + if(!sterile && prob(10) && egg_cooldown<=0) + egg_cooldown = initial(egg_cooldown) + LayEggs() + +/mob/living/simple_animal/hostile/alien/proc/SpreadPlants() + if(!isturf(loc) || isspaceturf(loc)) + return + if(locate(/obj/structure/alien/weeds/node) in get_turf(src)) + return + visible_message("[src] has planted some alien weeds!") + new /obj/structure/alien/weeds/node(loc) + +/mob/living/simple_animal/hostile/alien/proc/LayEggs() + if(!isturf(loc) || isspaceturf(loc)) + return + if(locate(/obj/structure/alien/egg) in get_turf(src)) + return + visible_message("[src] has laid an egg!") + new /obj/structure/alien/egg(loc) + +/mob/living/simple_animal/hostile/alien/queen/large + name = "alien empress" + icon = 'icons/mob/alienlarge.dmi' + icon_state = "queen_s" + icon_living = "queen_s" + icon_dead = "queen_dead" + move_to_delay = 4 + maxHealth = 400 + health = 400 + butcher_results = list(/obj/item/reagent_containers/food/snacks/xenomeat = 10, /obj/item/stack/sheet/animalhide/xeno = 2) + mob_size = MOB_SIZE_LARGE + gold_core_spawnable = NO_SPAWN + +/obj/item/projectile/neurotox + name = "neurotoxin" + damage = 30 + icon_state = "toxin" + +/mob/living/simple_animal/hostile/alien/maid + name = "lusty xenomorph maid" + melee_damage_lower = 0 + melee_damage_upper = 0 + a_intent = INTENT_HELP + friendly = "caresses" + obj_damage = 0 + environment_smash = ENVIRONMENT_SMASH_NONE + gold_core_spawnable = HOSTILE_SPAWN + icon_state = "maid" + icon_living = "maid" + icon_dead = "maid_dead" + +/mob/living/simple_animal/hostile/alien/maid/AttackingTarget() + if(ismovableatom(target)) + if(istype(target, /obj/effect/decal/cleanable)) + visible_message("\The [src] cleans up \the [target].") + qdel(target) + return TRUE + var/atom/movable/M = target + M.clean_blood() + visible_message("\The [src] polishes \the [target].") + return TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/bat.dm b/code/modules/mob/living/simple_animal/hostile/bat.dm index ae851fe6dcb..be04fc217e9 100644 --- a/code/modules/mob/living/simple_animal/hostile/bat.dm +++ b/code/modules/mob/living/simple_animal/hostile/bat.dm @@ -73,4 +73,4 @@ pass_flags = PASSTABLE universal_speak = 1 universal_understand = 1 - gold_core_spawnable = NO_SPAWN //badmin only \ No newline at end of file + gold_core_spawnable = NO_SPAWN //badmin only diff --git a/code/modules/mob/living/simple_animal/hostile/bear.dm b/code/modules/mob/living/simple_animal/hostile/bear.dm index cfe673a78f1..2530fb4b389 100644 --- a/code/modules/mob/living/simple_animal/hostile/bear.dm +++ b/code/modules/mob/living/simple_animal/hostile/bear.dm @@ -1,56 +1,56 @@ -//Space bears! -/mob/living/simple_animal/hostile/bear - name = "space bear" - desc = "You don't need to be faster than a space bear, you just need to outrun your crewmates." - icon_state = "bear" - icon_living = "bear" - icon_dead = "bear_dead" - icon_gib = "bear_gib" - speak = list("RAWR!","Rawr!","GRR!","Growl!") - speak_emote = list("growls", "roars") - emote_hear = list("rawrs","grumbles","grawls") - emote_see = list("stares ferociously", "stomps") - speak_chance = 1 - turns_per_move = 5 - see_in_dark = 6 - butcher_results = list(/obj/item/reagent_containers/food/snacks/bearmeat = 5, /obj/item/clothing/head/bearpelt = 1) - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "hits" - stop_automated_movement_when_pulled = 0 - maxHealth = 60 - health = 60 - obj_damage = 60 - melee_damage_lower = 20 - melee_damage_upper = 30 - attacktext = "mauls" - friendly = "bear hugs" - attack_sound = 'sound/weapons/genhit3.ogg' - - //Space bears aren't affected by atmos. - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - - faction = list("russian") - gold_core_spawnable = HOSTILE_SPAWN - -//SPACE BEARS! SQUEEEEEEEE~ OW! FUCK! IT BIT MY HAND OFF!! -/mob/living/simple_animal/hostile/bear/Hudson - name = "Hudson" - desc = "Feared outlaw, this guy is one bad news bear." //I'm sorry... - -/mob/living/simple_animal/hostile/bear/Hudson/New() - ..() - var/unbearable_pun = pick("He's unbearably cute.", "It looks like he is a bearer of bad news.", "Sadly, he is bearly able to comprehend puns.") - desc = "That's Hudson. " + unbearable_pun// I am not sorry for this. - -/mob/living/simple_animal/hostile/bear/Move() - ..() - if(stat != DEAD) - if(loc && istype(loc,/turf/space)) - icon_state = "bear" - else - icon_state = "bearfloor" - -/mob/living/simple_animal/hostile/bear/Process_Spacemove(var/movement_dir = 0) - return 1 //No drifting in space for space bears! +//Space bears! +/mob/living/simple_animal/hostile/bear + name = "space bear" + desc = "You don't need to be faster than a space bear, you just need to outrun your crewmates." + icon_state = "bear" + icon_living = "bear" + icon_dead = "bear_dead" + icon_gib = "bear_gib" + speak = list("RAWR!","Rawr!","GRR!","Growl!") + speak_emote = list("growls", "roars") + emote_hear = list("rawrs","grumbles","grawls") + emote_see = list("stares ferociously", "stomps") + speak_chance = 1 + turns_per_move = 5 + see_in_dark = 6 + butcher_results = list(/obj/item/reagent_containers/food/snacks/bearmeat = 5, /obj/item/clothing/head/bearpelt = 1) + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "hits" + stop_automated_movement_when_pulled = 0 + maxHealth = 60 + health = 60 + obj_damage = 60 + melee_damage_lower = 20 + melee_damage_upper = 30 + attacktext = "mauls" + friendly = "bear hugs" + attack_sound = 'sound/weapons/genhit3.ogg' + + //Space bears aren't affected by atmos. + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + + faction = list("russian") + gold_core_spawnable = HOSTILE_SPAWN + +//SPACE BEARS! SQUEEEEEEEE~ OW! FUCK! IT BIT MY HAND OFF!! +/mob/living/simple_animal/hostile/bear/Hudson + name = "Hudson" + desc = "Feared outlaw, this guy is one bad news bear." //I'm sorry... + +/mob/living/simple_animal/hostile/bear/Hudson/New() + ..() + var/unbearable_pun = pick("He's unbearably cute.", "It looks like he is a bearer of bad news.", "Sadly, he is bearly able to comprehend puns.") + desc = "That's Hudson. " + unbearable_pun// I am not sorry for this. + +/mob/living/simple_animal/hostile/bear/Move() + ..() + if(stat != DEAD) + if(loc && istype(loc,/turf/space)) + icon_state = "bear" + else + icon_state = "bearfloor" + +/mob/living/simple_animal/hostile/bear/Process_Spacemove(var/movement_dir = 0) + return 1 //No drifting in space for space bears! diff --git a/code/modules/mob/living/simple_animal/hostile/bees.dm b/code/modules/mob/living/simple_animal/hostile/bees.dm index c6586fefa2f..c1e7548d9a3 100644 --- a/code/modules/mob/living/simple_animal/hostile/bees.dm +++ b/code/modules/mob/living/simple_animal/hostile/bees.dm @@ -159,7 +159,7 @@ var/mob/living/L = target if(L.reagents) if(beegent) - beegent.reaction_mob(L, INGEST) + beegent.reaction_mob(L, REAGENT_INGEST) L.reagents.add_reagent(beegent.id, rand(1, 5)) else L.reagents.add_reagent("spidertoxin", 5) @@ -238,7 +238,7 @@ . = ..() if(. && beegent && isliving(target)) var/mob/living/L = target - beegent.reaction_mob(L, TOUCH) + beegent.reaction_mob(L, REAGENT_TOUCH) L.reagents.add_reagent(beegent.id, rand(1, 5)) //PEASENT BEES @@ -352,4 +352,4 @@ if(. && target && isliving(target)) var/mob/living/L = target if(L.stat) - LoseTarget() \ No newline at end of file + LoseTarget() diff --git a/code/modules/mob/living/simple_animal/hostile/carp.dm b/code/modules/mob/living/simple_animal/hostile/carp.dm index eb2bbddc437..a0cb935ba25 100644 --- a/code/modules/mob/living/simple_animal/hostile/carp.dm +++ b/code/modules/mob/living/simple_animal/hostile/carp.dm @@ -1,171 +1,171 @@ -#define REGENERATION_DELAY 60 // After taking damage, how long it takes for automatic regeneration to begin for megacarps (ty robustin!) - -/mob/living/simple_animal/hostile/carp - name = "space carp" - desc = "A ferocious, fang-bearing creature that resembles a fish." - icon = 'icons/mob/carp.dmi' - icon_state = "base" - icon_living = "base" - icon_dead = "base_dead" - icon_gib = "carp_gib" - speak_chance = 0 - turns_per_move = 5 - butcher_results = list(/obj/item/reagent_containers/food/snacks/carpmeat = 2) - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "hits" - emote_taunt = list("gnashes") - taunt_chance = 30 - speed = 0 - maxHealth = 25 - health = 25 - - harm_intent_damage = 8 - obj_damage = 50 - melee_damage_lower = 15 - melee_damage_upper = 15 - attacktext = "bites" - attack_sound = 'sound/weapons/bite.ogg' - speak_emote = list("gnashes") - - //Space carp aren't affected by atmos. - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - maxbodytemp = 1500 - faction = list("carp") - flying = TRUE - pressure_resistance = 200 - gold_core_spawnable = HOSTILE_SPAWN - - var/random_color = TRUE //if the carp uses random coloring - var/rarechance = 1 //chance for rare color variant - - var/static/list/carp_colors = list(\ - "lightpurple" = "#c3b9f1", \ - "lightpink" = "#da77a8", \ - "green" = "#70ff25", \ - "grape" = "#df0afb", \ - "swamp" = "#e5e75a", \ - "turquoise" = "#04e1ed", \ - "brown" = "#ca805a", \ - "teal" = "#20e28e", \ - "lightblue" = "#4d88cc", \ - "rusty" = "#dd5f34", \ - "beige" = "#bbaeaf", \ - "yellow" = "#f3ca4a", \ - "blue" = "#09bae1", \ - "palegreen" = "#7ef099", \ - ) - var/static/list/carp_colors_rare = list(\ - "silver" = "#fdfbf3", \ - ) - -/mob/living/simple_animal/hostile/carp/Initialize(mapload) - . = ..() - carp_randomify(rarechance) - update_icons() - -/mob/living/simple_animal/hostile/carp/proc/carp_randomify(rarechance) - if(random_color) - var/our_color - if(prob(rarechance)) - our_color = pick(carp_colors_rare) - add_atom_colour(carp_colors_rare[our_color], FIXED_COLOUR_PRIORITY) - else - our_color = pick(carp_colors) - add_atom_colour(carp_colors[our_color], FIXED_COLOUR_PRIORITY) - add_carp_overlay() - -/mob/living/simple_animal/hostile/carp/proc/add_carp_overlay() - if(!random_color) - return - cut_overlays() - var/mutable_appearance/base_overlay = mutable_appearance(icon, "base_mouth") - base_overlay.appearance_flags = RESET_COLOR - add_overlay(base_overlay) - -/mob/living/simple_animal/hostile/carp/proc/add_dead_carp_overlay() - if(!random_color) - return - cut_overlays() - var/mutable_appearance/base_dead_overlay = mutable_appearance(icon, "base_dead_mouth") - base_dead_overlay.appearance_flags = RESET_COLOR - add_overlay(base_dead_overlay) - -/mob/living/simple_animal/hostile/carp/Process_Spacemove(movement_dir = 0) - return TRUE //No drifting in space for space carp! //original comments do not steal - -/mob/living/simple_animal/hostile/carp/AttackingTarget() - . = ..() - if(. && ishuman(target)) - var/mob/living/carbon/human/H = target - H.adjustStaminaLoss(8) - -/mob/living/simple_animal/hostile/carp/death(gibbed) - . = ..() - cut_overlays() - if(!random_color || gibbed) - return - add_dead_carp_overlay() - -/mob/living/simple_animal/hostile/carp/revive() - ..() - regenerate_icons() - -/mob/living/simple_animal/hostile/carp/regenerate_icons() - cut_overlays() - if(!random_color) - return - if(stat != DEAD) - add_carp_overlay() - else - add_dead_carp_overlay() - ..() - -/mob/living/simple_animal/hostile/carp/holocarp - icon_state = "holocarp" - icon_living = "holocarp" - maxbodytemp = INFINITY - gold_core_spawnable = NO_SPAWN - del_on_death = 1 - random_color = FALSE - -/mob/living/simple_animal/hostile/carp/megacarp - icon = 'icons/mob/alienqueen.dmi' - name = "Mega Space Carp" - desc = "A ferocious, fang bearing creature that resembles a shark. This one seems especially ticked off." - icon_state = "megacarp" - icon_living = "megacarp" - icon_dead = "megacarp_dead" - icon_gib = "megacarp_gib" - maxHealth = 20 - health = 20 - pixel_x = -16 - mob_size = MOB_SIZE_LARGE - random_color = FALSE - - obj_damage = 80 - melee_damage_lower = 20 - melee_damage_upper = 20 - - var/regen_cooldown = 0 - -/mob/living/simple_animal/hostile/carp/megacarp/Initialize() - . = ..() - name = "[pick(GLOB.megacarp_first_names)] [pick(GLOB.megacarp_last_names)]" - melee_damage_lower += rand(2, 10) - melee_damage_upper += rand(10, 20) - maxHealth += rand(30, 60) - move_to_delay = rand(3, 7) - -/mob/living/simple_animal/hostile/carp/megacarp/adjustHealth(amount, updating_health = TRUE) - . = ..() - if(.) - regen_cooldown = world.time + REGENERATION_DELAY - -/mob/living/simple_animal/hostile/carp/megacarp/Life() - ..() - if(regen_cooldown < world.time) - heal_overall_damage(4) - -#undef REGENERATION_DELAY \ No newline at end of file +#define REGENERATION_DELAY 60 // After taking damage, how long it takes for automatic regeneration to begin for megacarps (ty robustin!) + +/mob/living/simple_animal/hostile/carp + name = "space carp" + desc = "A ferocious, fang-bearing creature that resembles a fish." + icon = 'icons/mob/carp.dmi' + icon_state = "base" + icon_living = "base" + icon_dead = "base_dead" + icon_gib = "carp_gib" + speak_chance = 0 + turns_per_move = 5 + butcher_results = list(/obj/item/reagent_containers/food/snacks/carpmeat = 2) + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "hits" + emote_taunt = list("gnashes") + taunt_chance = 30 + speed = 0 + maxHealth = 25 + health = 25 + + harm_intent_damage = 8 + obj_damage = 50 + melee_damage_lower = 15 + melee_damage_upper = 15 + attacktext = "bites" + attack_sound = 'sound/weapons/bite.ogg' + speak_emote = list("gnashes") + + //Space carp aren't affected by atmos. + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + maxbodytemp = 1500 + faction = list("carp") + flying = TRUE + pressure_resistance = 200 + gold_core_spawnable = HOSTILE_SPAWN + + var/random_color = TRUE //if the carp uses random coloring + var/rarechance = 1 //chance for rare color variant + + var/static/list/carp_colors = list(\ + "lightpurple" = "#c3b9f1", \ + "lightpink" = "#da77a8", \ + "green" = "#70ff25", \ + "grape" = "#df0afb", \ + "swamp" = "#e5e75a", \ + "turquoise" = "#04e1ed", \ + "brown" = "#ca805a", \ + "teal" = "#20e28e", \ + "lightblue" = "#4d88cc", \ + "rusty" = "#dd5f34", \ + "beige" = "#bbaeaf", \ + "yellow" = "#f3ca4a", \ + "blue" = "#09bae1", \ + "palegreen" = "#7ef099", \ + ) + var/static/list/carp_colors_rare = list(\ + "silver" = "#fdfbf3", \ + ) + +/mob/living/simple_animal/hostile/carp/Initialize(mapload) + . = ..() + carp_randomify(rarechance) + update_icons() + +/mob/living/simple_animal/hostile/carp/proc/carp_randomify(rarechance) + if(random_color) + var/our_color + if(prob(rarechance)) + our_color = pick(carp_colors_rare) + add_atom_colour(carp_colors_rare[our_color], FIXED_COLOUR_PRIORITY) + else + our_color = pick(carp_colors) + add_atom_colour(carp_colors[our_color], FIXED_COLOUR_PRIORITY) + add_carp_overlay() + +/mob/living/simple_animal/hostile/carp/proc/add_carp_overlay() + if(!random_color) + return + cut_overlays() + var/mutable_appearance/base_overlay = mutable_appearance(icon, "base_mouth") + base_overlay.appearance_flags = RESET_COLOR + add_overlay(base_overlay) + +/mob/living/simple_animal/hostile/carp/proc/add_dead_carp_overlay() + if(!random_color) + return + cut_overlays() + var/mutable_appearance/base_dead_overlay = mutable_appearance(icon, "base_dead_mouth") + base_dead_overlay.appearance_flags = RESET_COLOR + add_overlay(base_dead_overlay) + +/mob/living/simple_animal/hostile/carp/Process_Spacemove(movement_dir = 0) + return TRUE //No drifting in space for space carp! //original comments do not steal + +/mob/living/simple_animal/hostile/carp/AttackingTarget() + . = ..() + if(. && ishuman(target)) + var/mob/living/carbon/human/H = target + H.adjustStaminaLoss(8) + +/mob/living/simple_animal/hostile/carp/death(gibbed) + . = ..() + cut_overlays() + if(!random_color || gibbed) + return + add_dead_carp_overlay() + +/mob/living/simple_animal/hostile/carp/revive() + ..() + regenerate_icons() + +/mob/living/simple_animal/hostile/carp/regenerate_icons() + cut_overlays() + if(!random_color) + return + if(stat != DEAD) + add_carp_overlay() + else + add_dead_carp_overlay() + ..() + +/mob/living/simple_animal/hostile/carp/holocarp + icon_state = "holocarp" + icon_living = "holocarp" + maxbodytemp = INFINITY + gold_core_spawnable = NO_SPAWN + del_on_death = 1 + random_color = FALSE + +/mob/living/simple_animal/hostile/carp/megacarp + icon = 'icons/mob/alienqueen.dmi' + name = "Mega Space Carp" + desc = "A ferocious, fang bearing creature that resembles a shark. This one seems especially ticked off." + icon_state = "megacarp" + icon_living = "megacarp" + icon_dead = "megacarp_dead" + icon_gib = "megacarp_gib" + maxHealth = 20 + health = 20 + pixel_x = -16 + mob_size = MOB_SIZE_LARGE + random_color = FALSE + + obj_damage = 80 + melee_damage_lower = 20 + melee_damage_upper = 20 + + var/regen_cooldown = 0 + +/mob/living/simple_animal/hostile/carp/megacarp/Initialize() + . = ..() + name = "[pick(GLOB.megacarp_first_names)] [pick(GLOB.megacarp_last_names)]" + melee_damage_lower += rand(2, 10) + melee_damage_upper += rand(10, 20) + maxHealth += rand(30, 60) + move_to_delay = rand(3, 7) + +/mob/living/simple_animal/hostile/carp/megacarp/adjustHealth(amount, updating_health = TRUE) + . = ..() + if(.) + regen_cooldown = world.time + REGENERATION_DELAY + +/mob/living/simple_animal/hostile/carp/megacarp/Life() + ..() + if(regen_cooldown < world.time) + heal_overall_damage(4) + +#undef REGENERATION_DELAY diff --git a/code/modules/mob/living/simple_animal/hostile/creature.dm b/code/modules/mob/living/simple_animal/hostile/creature.dm index 225cbfea5b1..3934a9b1d2f 100644 --- a/code/modules/mob/living/simple_animal/hostile/creature.dm +++ b/code/modules/mob/living/simple_animal/hostile/creature.dm @@ -1,17 +1,17 @@ -/mob/living/simple_animal/hostile/creature - name = "creature" - desc = "A sanity-destroying otherthing." - speak_emote = list("gibbers") - icon_state = "otherthing" - icon_living = "otherthing" - icon_dead = "otherthing-dead" - health = 80 - maxHealth = 80 - obj_damage = 100 - melee_damage_lower = 25 - melee_damage_upper = 50 - attacktext = "chomps" - attack_sound = 'sound/weapons/bite.ogg' - faction = list("creature") - gold_core_spawnable = HOSTILE_SPAWN - +/mob/living/simple_animal/hostile/creature + name = "creature" + desc = "A sanity-destroying otherthing." + speak_emote = list("gibbers") + icon_state = "otherthing" + icon_living = "otherthing" + icon_dead = "otherthing-dead" + health = 80 + maxHealth = 80 + obj_damage = 100 + melee_damage_lower = 25 + melee_damage_upper = 50 + attacktext = "chomps" + attack_sound = 'sound/weapons/bite.ogg' + faction = list("creature") + gold_core_spawnable = HOSTILE_SPAWN + diff --git a/code/modules/mob/living/simple_animal/hostile/deathsquid.dm b/code/modules/mob/living/simple_animal/hostile/deathsquid.dm index 14b66017d41..69f5257d4dd 100644 --- a/code/modules/mob/living/simple_animal/hostile/deathsquid.dm +++ b/code/modules/mob/living/simple_animal/hostile/deathsquid.dm @@ -48,4 +48,4 @@ armour_penetration = 5 melee_damage_lower = 10 melee_damage_upper = 20 - environment_smash = 2 \ No newline at end of file + environment_smash = 2 diff --git a/code/modules/mob/living/simple_animal/hostile/faithless.dm b/code/modules/mob/living/simple_animal/hostile/faithless.dm index 5065ce63545..0ed560f24e9 100644 --- a/code/modules/mob/living/simple_animal/hostile/faithless.dm +++ b/code/modules/mob/living/simple_animal/hostile/faithless.dm @@ -1,41 +1,41 @@ -/mob/living/simple_animal/hostile/faithless - name = "Faithless" - desc = "The Wish Granter's faith in humanity, incarnate" - icon_state = "faithless" - icon_living = "faithless" - icon_dead = "faithless_dead" - speak_chance = 0 - turns_per_move = 5 - response_help = "passes through the" - response_disarm = "shoves" - response_harm = "hits the" - speed = 0 - maxHealth = 80 - health = 80 - obj_damage = 50 - harm_intent_damage = 10 - melee_damage_lower = 15 - melee_damage_upper = 15 - attacktext = "grips" - attack_sound = 'sound/hallucinations/growl1.ogg' - speak_emote = list("growls") - emote_taunt = list("wails") - taunt_chance = 25 - - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - - faction = list("faithless") - gold_core_spawnable = HOSTILE_SPAWN - -/mob/living/simple_animal/hostile/faithless/Process_Spacemove(var/movement_dir = 0) - return 1 - -/mob/living/simple_animal/hostile/faithless/AttackingTarget() - . = ..() - if(. && iscarbon(target)) - var/mob/living/carbon/C = target - if(prob(12)) - C.Weaken(3) - C.visible_message("\The [src] knocks down \the [C]!", \ - "\The [src] knocks you down!") \ No newline at end of file +/mob/living/simple_animal/hostile/faithless + name = "Faithless" + desc = "The Wish Granter's faith in humanity, incarnate" + icon_state = "faithless" + icon_living = "faithless" + icon_dead = "faithless_dead" + speak_chance = 0 + turns_per_move = 5 + response_help = "passes through the" + response_disarm = "shoves" + response_harm = "hits the" + speed = 0 + maxHealth = 80 + health = 80 + obj_damage = 50 + harm_intent_damage = 10 + melee_damage_lower = 15 + melee_damage_upper = 15 + attacktext = "grips" + attack_sound = 'sound/hallucinations/growl1.ogg' + speak_emote = list("growls") + emote_taunt = list("wails") + taunt_chance = 25 + + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + + faction = list("faithless") + gold_core_spawnable = HOSTILE_SPAWN + +/mob/living/simple_animal/hostile/faithless/Process_Spacemove(var/movement_dir = 0) + return 1 + +/mob/living/simple_animal/hostile/faithless/AttackingTarget() + . = ..() + if(. && iscarbon(target)) + var/mob/living/carbon/C = target + if(prob(12)) + C.Weaken(3) + C.visible_message("\The [src] knocks down \the [C]!", \ + "\The [src] knocks you down!") diff --git a/code/modules/mob/living/simple_animal/hostile/feral_cat.dm b/code/modules/mob/living/simple_animal/hostile/feral_cat.dm index 78f5a3ea2b9..4067d552089 100644 --- a/code/modules/mob/living/simple_animal/hostile/feral_cat.dm +++ b/code/modules/mob/living/simple_animal/hostile/feral_cat.dm @@ -25,4 +25,4 @@ faction = list("cat") atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) unsuitable_atmos_damage = 5 - pass_flags = PASSTABLE \ No newline at end of file + pass_flags = PASSTABLE diff --git a/code/modules/mob/living/simple_animal/hostile/floorcluwne.dm b/code/modules/mob/living/simple_animal/hostile/floorcluwne.dm index c3939e41395..77d6807e086 100644 --- a/code/modules/mob/living/simple_animal/hostile/floorcluwne.dm +++ b/code/modules/mob/living/simple_animal/hostile/floorcluwne.dm @@ -426,4 +426,4 @@ #undef STAGE_SPOOK #undef STAGE_TORMENT #undef STAGE_ATTACK -#undef MANIFEST_DELAY \ No newline at end of file +#undef MANIFEST_DELAY diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm index 0465d137007..b456db6efaf 100644 --- a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm +++ b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm @@ -1,246 +1,246 @@ - -#define SPINNING_WEB 1 -#define LAYING_EGGS 2 -#define MOVING_TO_TARGET 3 -#define SPINNING_COCOON 4 - -//basic spider mob, these generally guard nests -/mob/living/simple_animal/hostile/poison/giant_spider - name = "giant spider" - desc = "Furry and black, it makes you shudder to look at it. This one has deep red eyes." - icon_state = "guard" - var/butcher_state = 8 // Icon state for dead spider icons - icon_living = "guard" - icon_dead = "guard_dead" - speak_emote = list("chitters") - emote_hear = list("chitters") - speak_chance = 5 - turns_per_move = 5 - see_in_dark = 8 - lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE - butcher_results = list(/obj/item/reagent_containers/food/snacks/spidermeat = 2, /obj/item/reagent_containers/food/snacks/spiderleg = 8) - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "hits" - maxHealth = 200 - health = 200 - obj_damage = 60 - melee_damage_lower = 15 - melee_damage_upper = 20 - heat_damage_per_tick = 20 //amount of damage applied if animal's body temperature is higher than maxbodytemp - cold_damage_per_tick = 20 //same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp - faction = list("spiders") - pass_flags = PASSTABLE - move_to_delay = 6 - attacktext = "bites" - attack_sound = 'sound/weapons/bite.ogg' - gold_core_spawnable = HOSTILE_SPAWN - var/venom_per_bite = 0 // While the /poison/ type path remains as-is for consistency reasons, we're really talking about venom, not poison. - var/busy = 0 - -/mob/living/simple_animal/hostile/poison/giant_spider/AttackingTarget() - // This is placed here, NOT on /poison, because the other subtypes of /poison/ already override AttackingTarget() completely, and as such it would do nothing but confuse people there. - . = ..() - if(. && venom_per_bite > 0 && iscarbon(target) && (!client || a_intent == INTENT_HARM)) - var/mob/living/carbon/C = target - var/inject_target = pick("chest", "head") - if(C.can_inject(null, FALSE, inject_target, FALSE)) - C.reagents.add_reagent("spidertoxin", venom_per_bite) - -//nursemaids - these create webs and eggs -/mob/living/simple_animal/hostile/poison/giant_spider/nurse - desc = "Furry and black, it makes you shudder to look at it. This one has brilliant green eyes." - icon_state = "nurse" - icon_living = "nurse" - icon_dead = "nurse_dead" - butcher_results = list(/obj/item/reagent_containers/food/snacks/spidermeat = 2, /obj/item/reagent_containers/food/snacks/spiderleg = 8, /obj/item/reagent_containers/food/snacks/spidereggs = 4) - - maxHealth = 40 - health = 40 - melee_damage_lower = 5 - melee_damage_upper = 10 - venom_per_bite = 30 - var/atom/cocoon_target - var/fed = 0 - -//hunters have the most poison and move the fastest, so they can find prey -/mob/living/simple_animal/hostile/poison/giant_spider/hunter - desc = "Furry and black, it makes you shudder to look at it. This one has sparkling purple eyes." - icon_state = "hunter" - icon_living = "hunter" - icon_dead = "hunter_dead" - maxHealth = 120 - health = 120 - melee_damage_lower = 10 - melee_damage_upper = 20 - venom_per_bite = 10 - move_to_delay = 5 - -/mob/living/simple_animal/hostile/poison/giant_spider/handle_automated_movement() //Hacky and ugly. - . = ..() - if(AIStatus == AI_IDLE) - //1% chance to skitter madly away - if(!busy && prob(1)) - stop_automated_movement = 1 - Goto(pick(urange(20, src, 1)), move_to_delay) - spawn(50) - stop_automated_movement = 0 - walk(src,0) - return 1 - -/mob/living/simple_animal/hostile/poison/giant_spider/nurse/proc/GiveUp(C) - spawn(100) - if(busy == MOVING_TO_TARGET) - if(cocoon_target == C && get_dist(src,cocoon_target) > 1) - cocoon_target = null - busy = 0 - stop_automated_movement = 0 - -/mob/living/simple_animal/hostile/poison/giant_spider/nurse/handle_automated_movement() //Hacky and ugly. - if(..()) - var/list/can_see = view(src, 10) - if(!busy && prob(30)) //30% chance to stop wandering and do something - //first, check for potential food nearby to cocoon - for(var/mob/living/C in can_see) - if(C.stat && !istype(C, /mob/living/simple_animal/hostile/poison/giant_spider) && !C.anchored) - cocoon_target = C - busy = MOVING_TO_TARGET - Goto(C, move_to_delay) - //give up if we can't reach them after 10 seconds - GiveUp(C) - return - //second, spin a sticky spiderweb on this tile - var/obj/structure/spider/stickyweb/W = locate() in get_turf(src) - if(!W) - Web() - else - //third, lay an egg cluster there - if(fed) - LayEggs() - else - //fourthly, cocoon any nearby items so those pesky pinkskins can't use them - for(var/obj/O in can_see) - if(O.anchored) - continue - - if(isitem(O) || isstructure(O) || ismachinery(O)) - cocoon_target = O - busy = MOVING_TO_TARGET - stop_automated_movement = 1 - Goto(O, move_to_delay) - //give up if we can't reach them after 10 seconds - GiveUp(O) - - else if(busy == MOVING_TO_TARGET && cocoon_target) - if(get_dist(src, cocoon_target) <= 1) - Wrap() - - else - busy = 0 - stop_automated_movement = 0 - -/mob/living/simple_animal/hostile/poison/giant_spider/verb/Web() - set name = "Lay Web" - set category = "Spider" - set desc = "Spread a sticky web to slow down prey." - - var/T = src.loc - - if(busy != SPINNING_WEB) - busy = SPINNING_WEB - src.visible_message("\the [src] begins to secrete a sticky substance.") - stop_automated_movement = 1 - spawn(40) - if(busy == SPINNING_WEB && src.loc == T) - new /obj/structure/spider/stickyweb(T) - busy = 0 - stop_automated_movement = 0 - - -/mob/living/simple_animal/hostile/poison/giant_spider/nurse/verb/Wrap() - set name = "Wrap" - set category = "Spider" - set desc = "Wrap up prey to feast upon and objects for safe keeping." - - if(!cocoon_target) - var/list/choices = list() - for(var/mob/living/L in view(1,src)) - if(L == src) - continue - if(Adjacent(L)) - choices += L - for(var/obj/O in src.loc) - if(Adjacent(O)) - choices += O - cocoon_target = input(src,"What do you wish to cocoon?") in null|choices - - if(cocoon_target && busy != SPINNING_COCOON) - busy = SPINNING_COCOON - src.visible_message("\the [src] begins to secrete a sticky substance around \the [cocoon_target].") - stop_automated_movement = 1 - walk(src,0) - spawn(50) - if(busy == SPINNING_COCOON) - if(cocoon_target && istype(cocoon_target.loc, /turf) && get_dist(src,cocoon_target) <= 1) - var/obj/structure/spider/cocoon/C = new(cocoon_target.loc) - var/large_cocoon = 0 - C.pixel_x = cocoon_target.pixel_x - C.pixel_y = cocoon_target.pixel_y - for(var/obj/item/I in C.loc) - I.loc = C - for(var/obj/structure/S in C.loc) - if(!S.anchored) - S.loc = C - large_cocoon = 1 - for(var/obj/machinery/M in C.loc) - if(!M.anchored) - M.loc = C - large_cocoon = 1 - for(var/mob/living/L in C.loc) - if(istype(L, /mob/living/simple_animal/hostile/poison/giant_spider)) - continue - large_cocoon = 1 - L.loc = C - C.pixel_x = L.pixel_x - C.pixel_y = L.pixel_y - fed++ - visible_message("\the [src] sticks a proboscis into \the [L] and sucks a viscous substance out.") - - break - if(large_cocoon) - C.icon_state = pick("cocoon_large1","cocoon_large2","cocoon_large3") - cocoon_target = null - busy = 0 - stop_automated_movement = 0 - -/mob/living/simple_animal/hostile/poison/giant_spider/nurse/verb/LayEggs() - set name = "Lay Eggs" - set category = "Spider" - set desc = "Lay a clutch of eggs, but you must wrap a creature for feeding first." - - var/obj/structure/spider/eggcluster/E = locate() in get_turf(src) - if(E) - to_chat(src, "There is already a cluster of eggs here!") - else if(!fed) - to_chat(src, "You are too hungry to do this!") - else if(busy != LAYING_EGGS) - busy = LAYING_EGGS - src.visible_message("\the [src] begins to lay a cluster of eggs.") - stop_automated_movement = 1 - spawn(50) - if(busy == LAYING_EGGS) - E = locate() in get_turf(src) - if(!E) - var/obj/structure/spider/eggcluster/C = new /obj/structure/spider/eggcluster(src.loc) - C.faction = faction.Copy() - C.master_commander = master_commander - if(ckey) - C.player_spiders = 1 - fed-- - busy = 0 - stop_automated_movement = 0 - -#undef SPINNING_WEB -#undef LAYING_EGGS -#undef MOVING_TO_TARGET -#undef SPINNING_COCOON + +#define SPINNING_WEB 1 +#define LAYING_EGGS 2 +#define MOVING_TO_TARGET 3 +#define SPINNING_COCOON 4 + +//basic spider mob, these generally guard nests +/mob/living/simple_animal/hostile/poison/giant_spider + name = "giant spider" + desc = "Furry and black, it makes you shudder to look at it. This one has deep red eyes." + icon_state = "guard" + var/butcher_state = 8 // Icon state for dead spider icons + icon_living = "guard" + icon_dead = "guard_dead" + speak_emote = list("chitters") + emote_hear = list("chitters") + speak_chance = 5 + turns_per_move = 5 + see_in_dark = 8 + lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE + butcher_results = list(/obj/item/reagent_containers/food/snacks/spidermeat = 2, /obj/item/reagent_containers/food/snacks/spiderleg = 8) + response_help = "pets" + response_disarm = "gently pushes aside" + response_harm = "hits" + maxHealth = 200 + health = 200 + obj_damage = 60 + melee_damage_lower = 15 + melee_damage_upper = 20 + heat_damage_per_tick = 20 //amount of damage applied if animal's body temperature is higher than maxbodytemp + cold_damage_per_tick = 20 //same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp + faction = list("spiders") + pass_flags = PASSTABLE + move_to_delay = 6 + attacktext = "bites" + attack_sound = 'sound/weapons/bite.ogg' + gold_core_spawnable = HOSTILE_SPAWN + var/venom_per_bite = 0 // While the /poison/ type path remains as-is for consistency reasons, we're really talking about venom, not poison. + var/busy = 0 + +/mob/living/simple_animal/hostile/poison/giant_spider/AttackingTarget() + // This is placed here, NOT on /poison, because the other subtypes of /poison/ already override AttackingTarget() completely, and as such it would do nothing but confuse people there. + . = ..() + if(. && venom_per_bite > 0 && iscarbon(target) && (!client || a_intent == INTENT_HARM)) + var/mob/living/carbon/C = target + var/inject_target = pick("chest", "head") + if(C.can_inject(null, FALSE, inject_target, FALSE)) + C.reagents.add_reagent("spidertoxin", venom_per_bite) + +//nursemaids - these create webs and eggs +/mob/living/simple_animal/hostile/poison/giant_spider/nurse + desc = "Furry and black, it makes you shudder to look at it. This one has brilliant green eyes." + icon_state = "nurse" + icon_living = "nurse" + icon_dead = "nurse_dead" + butcher_results = list(/obj/item/reagent_containers/food/snacks/spidermeat = 2, /obj/item/reagent_containers/food/snacks/spiderleg = 8, /obj/item/reagent_containers/food/snacks/spidereggs = 4) + + maxHealth = 40 + health = 40 + melee_damage_lower = 5 + melee_damage_upper = 10 + venom_per_bite = 30 + var/atom/cocoon_target + var/fed = 0 + +//hunters have the most poison and move the fastest, so they can find prey +/mob/living/simple_animal/hostile/poison/giant_spider/hunter + desc = "Furry and black, it makes you shudder to look at it. This one has sparkling purple eyes." + icon_state = "hunter" + icon_living = "hunter" + icon_dead = "hunter_dead" + maxHealth = 120 + health = 120 + melee_damage_lower = 10 + melee_damage_upper = 20 + venom_per_bite = 10 + move_to_delay = 5 + +/mob/living/simple_animal/hostile/poison/giant_spider/handle_automated_movement() //Hacky and ugly. + . = ..() + if(AIStatus == AI_IDLE) + //1% chance to skitter madly away + if(!busy && prob(1)) + stop_automated_movement = 1 + Goto(pick(urange(20, src, 1)), move_to_delay) + spawn(50) + stop_automated_movement = 0 + walk(src,0) + return 1 + +/mob/living/simple_animal/hostile/poison/giant_spider/nurse/proc/GiveUp(C) + spawn(100) + if(busy == MOVING_TO_TARGET) + if(cocoon_target == C && get_dist(src,cocoon_target) > 1) + cocoon_target = null + busy = 0 + stop_automated_movement = 0 + +/mob/living/simple_animal/hostile/poison/giant_spider/nurse/handle_automated_movement() //Hacky and ugly. + if(..()) + var/list/can_see = view(src, 10) + if(!busy && prob(30)) //30% chance to stop wandering and do something + //first, check for potential food nearby to cocoon + for(var/mob/living/C in can_see) + if(C.stat && !istype(C, /mob/living/simple_animal/hostile/poison/giant_spider) && !C.anchored) + cocoon_target = C + busy = MOVING_TO_TARGET + Goto(C, move_to_delay) + //give up if we can't reach them after 10 seconds + GiveUp(C) + return + //second, spin a sticky spiderweb on this tile + var/obj/structure/spider/stickyweb/W = locate() in get_turf(src) + if(!W) + Web() + else + //third, lay an egg cluster there + if(fed) + LayEggs() + else + //fourthly, cocoon any nearby items so those pesky pinkskins can't use them + for(var/obj/O in can_see) + if(O.anchored) + continue + + if(isitem(O) || isstructure(O) || ismachinery(O)) + cocoon_target = O + busy = MOVING_TO_TARGET + stop_automated_movement = 1 + Goto(O, move_to_delay) + //give up if we can't reach them after 10 seconds + GiveUp(O) + + else if(busy == MOVING_TO_TARGET && cocoon_target) + if(get_dist(src, cocoon_target) <= 1) + Wrap() + + else + busy = 0 + stop_automated_movement = 0 + +/mob/living/simple_animal/hostile/poison/giant_spider/verb/Web() + set name = "Lay Web" + set category = "Spider" + set desc = "Spread a sticky web to slow down prey." + + var/T = src.loc + + if(busy != SPINNING_WEB) + busy = SPINNING_WEB + src.visible_message("\the [src] begins to secrete a sticky substance.") + stop_automated_movement = 1 + spawn(40) + if(busy == SPINNING_WEB && src.loc == T) + new /obj/structure/spider/stickyweb(T) + busy = 0 + stop_automated_movement = 0 + + +/mob/living/simple_animal/hostile/poison/giant_spider/nurse/verb/Wrap() + set name = "Wrap" + set category = "Spider" + set desc = "Wrap up prey to feast upon and objects for safe keeping." + + if(!cocoon_target) + var/list/choices = list() + for(var/mob/living/L in view(1,src)) + if(L == src) + continue + if(Adjacent(L)) + choices += L + for(var/obj/O in src.loc) + if(Adjacent(O)) + choices += O + cocoon_target = input(src,"What do you wish to cocoon?") in null|choices + + if(cocoon_target && busy != SPINNING_COCOON) + busy = SPINNING_COCOON + src.visible_message("\the [src] begins to secrete a sticky substance around \the [cocoon_target].") + stop_automated_movement = 1 + walk(src,0) + spawn(50) + if(busy == SPINNING_COCOON) + if(cocoon_target && istype(cocoon_target.loc, /turf) && get_dist(src,cocoon_target) <= 1) + var/obj/structure/spider/cocoon/C = new(cocoon_target.loc) + var/large_cocoon = 0 + C.pixel_x = cocoon_target.pixel_x + C.pixel_y = cocoon_target.pixel_y + for(var/obj/item/I in C.loc) + I.loc = C + for(var/obj/structure/S in C.loc) + if(!S.anchored) + S.loc = C + large_cocoon = 1 + for(var/obj/machinery/M in C.loc) + if(!M.anchored) + M.loc = C + large_cocoon = 1 + for(var/mob/living/L in C.loc) + if(istype(L, /mob/living/simple_animal/hostile/poison/giant_spider)) + continue + large_cocoon = 1 + L.loc = C + C.pixel_x = L.pixel_x + C.pixel_y = L.pixel_y + fed++ + visible_message("\the [src] sticks a proboscis into \the [L] and sucks a viscous substance out.") + + break + if(large_cocoon) + C.icon_state = pick("cocoon_large1","cocoon_large2","cocoon_large3") + cocoon_target = null + busy = 0 + stop_automated_movement = 0 + +/mob/living/simple_animal/hostile/poison/giant_spider/nurse/verb/LayEggs() + set name = "Lay Eggs" + set category = "Spider" + set desc = "Lay a clutch of eggs, but you must wrap a creature for feeding first." + + var/obj/structure/spider/eggcluster/E = locate() in get_turf(src) + if(E) + to_chat(src, "There is already a cluster of eggs here!") + else if(!fed) + to_chat(src, "You are too hungry to do this!") + else if(busy != LAYING_EGGS) + busy = LAYING_EGGS + src.visible_message("\the [src] begins to lay a cluster of eggs.") + stop_automated_movement = 1 + spawn(50) + if(busy == LAYING_EGGS) + E = locate() in get_turf(src) + if(!E) + var/obj/structure/spider/eggcluster/C = new /obj/structure/spider/eggcluster(src.loc) + C.faction = faction.Copy() + C.master_commander = master_commander + if(ckey) + C.player_spiders = 1 + fed-- + busy = 0 + stop_automated_movement = 0 + +#undef SPINNING_WEB +#undef LAYING_EGGS +#undef MOVING_TO_TARGET +#undef SPINNING_COCOON diff --git a/code/modules/mob/living/simple_animal/hostile/hivebot.dm b/code/modules/mob/living/simple_animal/hostile/hivebot.dm index c94648fe641..d2a97949c8e 100644 --- a/code/modules/mob/living/simple_animal/hostile/hivebot.dm +++ b/code/modules/mob/living/simple_animal/hostile/hivebot.dm @@ -1,108 +1,108 @@ -/obj/item/projectile/hivebotbullet - damage = 10 - damage_type = BRUTE - -/mob/living/simple_animal/hostile/hivebot - name = "Hivebot" - desc = "A small robot" - icon = 'icons/mob/hivebot.dmi' - icon_state = "basic" - icon_living = "basic" - icon_dead = "basic" - health = 15 - maxHealth = 15 - melee_damage_lower = 2 - melee_damage_upper = 3 - attacktext = "claws" - attack_sound = 'sound/weapons/bladeslice.ogg' - projectilesound = 'sound/weapons/gunshots/gunshot.ogg' - projectiletype = /obj/item/projectile/hivebotbullet - faction = list("hivebot") - check_friendly_fire = 1 - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - speak_emote = list("states") - gold_core_spawnable = HOSTILE_SPAWN - loot = list(/obj/effect/decal/cleanable/blood/gibs/robot) - deathmessage = "blows apart!" - del_on_death = 1 - -/mob/living/simple_animal/hostile/hivebot/range - name = "Hivebot" - desc = "A smallish robot, this one is armed!" - ranged = 1 - retreat_distance = 5 - minimum_distance = 5 - -/mob/living/simple_animal/hostile/hivebot/rapid - ranged = 1 - rapid = 3 - retreat_distance = 5 - minimum_distance = 5 - -/mob/living/simple_animal/hostile/hivebot/strong - name = "Strong Hivebot" - desc = "A robot, this one is armed and looks tough!" - health = 80 - maxHealth = 80 - ranged = 1 - -/mob/living/simple_animal/hostile/hivebot/death(gibbed) - // Only execute the below if we successfully died - . = ..(gibbed) - if(!.) - return FALSE - do_sparks(3, 1, src) - -/mob/living/simple_animal/hostile/hivebot/tele//this still needs work - name = "Beacon" - desc = "Some odd beacon thing" - icon = 'icons/mob/hivebot.dmi' - icon_state = "def_radar-off" - icon_living = "def_radar-off" - health = 200 - maxHealth = 200 - status_flags = 0 - anchored = 1 - stop_automated_movement = 1 - var/bot_type = "norm" - var/bot_amt = 10 - var/spawn_delay = 600 - var/turn_on = 0 - var/auto_spawn = 1 - proc - warpbots() - - -/mob/living/simple_animal/hostile/hivebot/tele/New() - ..() - var/datum/effect_system/smoke_spread/smoke = new - smoke.set_up(5, 0, src.loc) - smoke.start() - visible_message("The [src] warps in!") - playsound(src.loc, 'sound/effects/empulse.ogg', 25, 1) - -/mob/living/simple_animal/hostile/hivebot/tele/warpbots() - icon_state = "def_radar" - visible_message("The [src] turns on!") - while(bot_amt > 0) - bot_amt-- - switch(bot_type) - if("norm") - var/mob/living/simple_animal/hostile/hivebot/H = new /mob/living/simple_animal/hostile/hivebot(get_turf(src)) - H.faction = faction - if("range") - var/mob/living/simple_animal/hostile/hivebot/range/R = new /mob/living/simple_animal/hostile/hivebot/range(get_turf(src)) - R.faction = faction - if("rapid") - var/mob/living/simple_animal/hostile/hivebot/rapid/F = new /mob/living/simple_animal/hostile/hivebot/rapid(get_turf(src)) - F.faction = faction - spawn(100) - qdel(src) - return - -/mob/living/simple_animal/hostile/hivebot/tele/handle_automated_action() - if(!..()) - return - if(prob(2))//Might be a bit low, will mess with it likely - warpbots() +/obj/item/projectile/hivebotbullet + damage = 10 + damage_type = BRUTE + +/mob/living/simple_animal/hostile/hivebot + name = "Hivebot" + desc = "A small robot" + icon = 'icons/mob/hivebot.dmi' + icon_state = "basic" + icon_living = "basic" + icon_dead = "basic" + health = 15 + maxHealth = 15 + melee_damage_lower = 2 + melee_damage_upper = 3 + attacktext = "claws" + attack_sound = 'sound/weapons/bladeslice.ogg' + projectilesound = 'sound/weapons/gunshots/gunshot.ogg' + projectiletype = /obj/item/projectile/hivebotbullet + faction = list("hivebot") + check_friendly_fire = 1 + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + speak_emote = list("states") + gold_core_spawnable = HOSTILE_SPAWN + loot = list(/obj/effect/decal/cleanable/blood/gibs/robot) + deathmessage = "blows apart!" + del_on_death = 1 + +/mob/living/simple_animal/hostile/hivebot/range + name = "Hivebot" + desc = "A smallish robot, this one is armed!" + ranged = 1 + retreat_distance = 5 + minimum_distance = 5 + +/mob/living/simple_animal/hostile/hivebot/rapid + ranged = 1 + rapid = 3 + retreat_distance = 5 + minimum_distance = 5 + +/mob/living/simple_animal/hostile/hivebot/strong + name = "Strong Hivebot" + desc = "A robot, this one is armed and looks tough!" + health = 80 + maxHealth = 80 + ranged = 1 + +/mob/living/simple_animal/hostile/hivebot/death(gibbed) + // Only execute the below if we successfully died + . = ..(gibbed) + if(!.) + return FALSE + do_sparks(3, 1, src) + +/mob/living/simple_animal/hostile/hivebot/tele//this still needs work + name = "Beacon" + desc = "Some odd beacon thing" + icon = 'icons/mob/hivebot.dmi' + icon_state = "def_radar-off" + icon_living = "def_radar-off" + health = 200 + maxHealth = 200 + status_flags = 0 + anchored = 1 + stop_automated_movement = 1 + var/bot_type = "norm" + var/bot_amt = 10 + var/spawn_delay = 600 + var/turn_on = 0 + var/auto_spawn = 1 + proc + warpbots() + + +/mob/living/simple_animal/hostile/hivebot/tele/New() + ..() + var/datum/effect_system/smoke_spread/smoke = new + smoke.set_up(5, 0, src.loc) + smoke.start() + visible_message("The [src] warps in!") + playsound(src.loc, 'sound/effects/empulse.ogg', 25, 1) + +/mob/living/simple_animal/hostile/hivebot/tele/warpbots() + icon_state = "def_radar" + visible_message("The [src] turns on!") + while(bot_amt > 0) + bot_amt-- + switch(bot_type) + if("norm") + var/mob/living/simple_animal/hostile/hivebot/H = new /mob/living/simple_animal/hostile/hivebot(get_turf(src)) + H.faction = faction + if("range") + var/mob/living/simple_animal/hostile/hivebot/range/R = new /mob/living/simple_animal/hostile/hivebot/range(get_turf(src)) + R.faction = faction + if("rapid") + var/mob/living/simple_animal/hostile/hivebot/rapid/F = new /mob/living/simple_animal/hostile/hivebot/rapid(get_turf(src)) + F.faction = faction + spawn(100) + qdel(src) + return + +/mob/living/simple_animal/hostile/hivebot/tele/handle_automated_action() + if(!..()) + return + if(prob(2))//Might be a bit low, will mess with it likely + warpbots() diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index c5749e939c4..2881a5945bb 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -1,568 +1,568 @@ -/mob/living/simple_animal/hostile - faction = list("hostile") - stop_automated_movement_when_pulled = 0 - obj_damage = 40 - environment_smash = ENVIRONMENT_SMASH_STRUCTURES //Bitflags. Set to ENVIRONMENT_SMASH_STRUCTURES to break closets,tables,racks, etc; ENVIRONMENT_SMASH_WALLS for walls; ENVIRONMENT_SMASH_RWALLS for rwalls - var/atom/target - var/ranged = FALSE - var/rapid = 0 //How many shots per volley. - var/rapid_fire_delay = 2 //Time between rapid fire shots - - var/dodging = FALSE - var/approaching_target = FALSE //We should dodge now - var/in_melee = FALSE //We should sidestep now - var/dodge_prob = 30 - var/sidestep_per_cycle = 1 //How many sidesteps per npcpool cycle when in melee - - var/projectiletype //set ONLY it and NULLIFY casingtype var, if we have ONLY projectile - var/projectilesound - var/casingtype //set ONLY it and NULLIFY projectiletype, if we have projectile IN CASING - var/move_to_delay = 3 //delay for the automated movement. - var/list/friends = list() - var/list/emote_taunt = list() - var/taunt_chance = 0 - - var/rapid_melee = 1 //Number of melee attacks between each npc pool tick. Spread evenly. - var/melee_queue_distance = 4 //If target is close enough start preparing to hit them if we have rapid_melee enabled - - var/ranged_message = "fires" //Fluff text for ranged mobs - var/ranged_cooldown = 0 //What the current cooldown on ranged attacks is, generally world.time + ranged_cooldown_time - var/ranged_cooldown_time = 30 //How long, in deciseconds, the cooldown of ranged attacks is - var/ranged_ignores_vision = FALSE //if it'll fire ranged attacks even if it lacks vision on its target, only works with environment smash - var/check_friendly_fire = 0 // Should the ranged mob check for friendlies when shooting - var/retreat_distance = null //If our mob runs from players when they're too close, set in tile distance. By default, mobs do not retreat. - var/minimum_distance = 1 //Minimum approach distance, so ranged mobs chase targets down, but still keep their distance set in tiles to the target, set higher to make mobs keep distance - - -//These vars are related to how mobs locate and target - var/robust_searching = 0 //By default, mobs have a simple searching method, set this to 1 for the more scrutinous searching (stat_attack, stat_exclusive, etc), should be disabled on most mobs - var/vision_range = 9 //How big of an area to search for targets in, a vision of 9 attempts to find targets as soon as they walk into screen view - var/aggro_vision_range = 9 //If a mob is aggro, we search in this radius. Defaults to 9 to keep in line with original simple mob aggro radius - var/search_objects = 0 //If we want to consider objects when searching around, set this to 1. If you want to search for objects while also ignoring mobs until hurt, set it to 2. To completely ignore mobs, even when attacked, set it to 3 - var/search_objects_timer_id //Timer for regaining our old search_objects value after being attacked - var/search_objects_regain_time = 30 //the delay between being attacked and gaining our old search_objects value back - var/list/wanted_objects = list() //A typecache of objects types that will be checked against to attack, should we have search_objects enabled - var/stat_attack = CONSCIOUS //Mobs with stat_attack to UNCONSCIOUS will attempt to attack things that are unconscious, Mobs with stat_attack set to DEAD will attempt to attack the dead. - var/stat_exclusive = FALSE //Mobs with this set to TRUE will exclusively attack things defined by stat_attack, stat_attack DEAD means they will only attack corpses - var/attack_same = 0 //Set us to 1 to allow us to attack our own faction - var/atom/targets_from = null //all range/attack/etc. calculations should be done from this atom, defaults to the mob itself, useful for Vehicles and such - var/attack_all_objects = FALSE //if true, equivalent to having a wanted_objects list containing ALL objects. - - var/lose_patience_timer_id //id for a timer to call LoseTarget(), used to stop mobs fixating on a target they can't reach - var/lose_patience_timeout = 300 //30 seconds by default, so there's no major changes to AI behaviour, beyond actually bailing if stuck forever - -/mob/living/simple_animal/hostile/Initialize(mapload) - . = ..() - - if(!targets_from) - targets_from = src - wanted_objects = typecacheof(wanted_objects) - -/mob/living/simple_animal/hostile/Destroy() - targets_from = null - target = null - return ..() - -/mob/living/simple_animal/hostile/Life(seconds, times_fired) - . = ..() - if(!.) - walk(src, 0) - return FALSE - -/mob/living/simple_animal/hostile/handle_automated_action() - if(AIStatus == AI_OFF) - return 0 - var/list/possible_targets = ListTargets() //we look around for potential targets and make it a list for later use. - - if(environment_smash) - EscapeConfinement() - - if(AICanContinue(possible_targets)) - if(!QDELETED(target) && !targets_from.Adjacent(target)) - DestroyPathToTarget() - if(!MoveToTarget(possible_targets)) //if we lose our target - if(AIShouldSleep(possible_targets)) // we try to acquire a new one - toggle_ai(AI_IDLE) // otherwise we go idle - return 1 - -/mob/living/simple_animal/hostile/handle_automated_movement() - . = ..() - if(dodging && target && in_melee && isturf(loc) && isturf(target.loc)) - var/datum/cb = CALLBACK(src,.proc/sidestep) - if(sidestep_per_cycle > 1) //For more than one just spread them equally - this could changed to some sensible distribution later - var/sidestep_delay = SSnpcpool.wait / sidestep_per_cycle - for(var/i in 1 to sidestep_per_cycle) - addtimer(cb, (i - 1) * sidestep_delay) - else //Otherwise randomize it to make the players guessing. - addtimer(cb,rand(1, SSnpcpool.wait)) - -/mob/living/simple_animal/hostile/proc/sidestep() - if(!target || !isturf(target.loc) || !isturf(loc) || stat == DEAD) - return - var/target_dir = get_dir(src, target) - - var/static/list/cardinal_sidestep_directions = list(-90, -45, 0, 45, 90) - var/static/list/diagonal_sidestep_directions = list(-45, 0, 45) - var/chosen_dir = 0 - if (target_dir & (target_dir - 1)) - chosen_dir = pick(diagonal_sidestep_directions) - else - chosen_dir = pick(cardinal_sidestep_directions) - if(chosen_dir) - chosen_dir = turn(target_dir, chosen_dir) - Move(get_step(src, chosen_dir)) - face_atom(target) //Looks better if they keep looking at you when dodging - -/mob/living/simple_animal/hostile/attacked_by(obj/item/I, mob/living/user) - if(stat == CONSCIOUS && !target && AIStatus != AI_OFF && !client && user) - FindTarget(list(user), 1) - return ..() - -/mob/living/simple_animal/hostile/bullet_act(obj/item/projectile/P) - if(stat == CONSCIOUS && !target && AIStatus != AI_OFF && !client) - if(P.firer && get_dist(src, P.firer) <= aggro_vision_range) - FindTarget(list(P.firer), 1) - Goto(P.starting, move_to_delay, 3) - return ..() - -//////////////HOSTILE MOB TARGETTING AND AGGRESSION//////////// - -/mob/living/simple_animal/hostile/proc/ListTargets()//Step 1, find out what we can see - if(!search_objects) - . = hearers(vision_range, targets_from) - src //Remove self, so we don't suicide - - var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha, /obj/spacepod)) - - for(var/HM in typecache_filter_list(range(vision_range, targets_from), hostile_machines)) - if(can_see(targets_from, HM, vision_range)) - . += HM - else - . = oview(vision_range, targets_from) - -/mob/living/simple_animal/hostile/proc/FindTarget(var/list/possible_targets, var/HasTargetsList = 0)//Step 2, filter down possible targets to things we actually care about - . = list() - if(!HasTargetsList) - possible_targets = ListTargets() - for(var/pos_targ in possible_targets) - var/atom/A = pos_targ - if(Found(A))//Just in case people want to override targetting - . = list(A) - break - if(CanAttack(A))//Can we attack it? - . += A - continue - var/Target = PickTarget(.) - GiveTarget(Target) - return Target //We now have a target - -/mob/living/simple_animal/hostile/proc/PossibleThreats() - . = list() - for(var/pos_targ in ListTargets()) - var/atom/A = pos_targ - if(Found(A)) - . = list(A) - break - if(CanAttack(A)) - . += A - continue - -/mob/living/simple_animal/hostile/proc/Found(var/atom/A)//This is here as a potential override to pick a specific target if available - return - -/mob/living/simple_animal/hostile/proc/PickTarget(list/Targets)//Step 3, pick amongst the possible, attackable targets - if(target != null)//If we already have a target, but are told to pick again, calculate the lowest distance between all possible, and pick from the lowest distance targets - for(var/pos_targ in Targets) - var/atom/A = pos_targ - var/target_dist = get_dist(targets_from, target) - var/possible_target_distance = get_dist(targets_from, A) - if(target_dist < possible_target_distance) - Targets -= A - if(!Targets.len)//We didnt find nothin! - return - var/chosen_target = pick(Targets)//Pick the remaining targets (if any) at random - return chosen_target - -// Please do not add one-off mob AIs here, but override this function for your mob -/mob/living/simple_animal/hostile/CanAttack(atom/the_target)//Can we actually attack a possible target? - if(isturf(the_target) || !the_target || the_target.type == /atom/movable/lighting_object) // bail out on invalids - return FALSE - - if(ismob(the_target)) //Target is in godmode, ignore it. - var/mob/M = the_target - if(M.status_flags & GODMODE) - return FALSE - - if(see_invisible < the_target.invisibility) //Target's invisible to us, forget it - return FALSE - if(search_objects < 2) - if(isliving(the_target)) - var/mob/living/L = the_target - var/faction_check = faction_check_mob(L) - if(robust_searching) - if(faction_check && !attack_same) - return FALSE - if(L.stat > stat_attack) - return FALSE - if(L in friends) - return FALSE - else - if((faction_check && !attack_same) || L.stat) - return FALSE - return TRUE - - if(ismecha(the_target)) - var/obj/mecha/M = the_target - if(M.occupant)//Just so we don't attack empty mechs - if(CanAttack(M.occupant)) - return TRUE - - if(isspacepod(the_target)) - var/obj/spacepod/S = the_target - if(S.pilot)//Just so we don't attack empty pods - if(CanAttack(S.pilot)) - return TRUE - - if(istype(the_target, /obj/machinery/porta_turret)) - var/obj/machinery/porta_turret/P = the_target - if(P.faction in faction) - return FALSE - if(!P.raised) //Don't attack invincible turrets - return FALSE - if(P.stat & BROKEN) //Or turrets that are already broken - return FALSE - return TRUE - - if(isobj(the_target)) - if(attack_all_objects || is_type_in_typecache(the_target, wanted_objects)) - return TRUE - - return FALSE - -/mob/living/simple_animal/hostile/proc/GiveTarget(new_target)//Step 4, give us our selected target - target = new_target - LosePatience() - if(target != null) - GainPatience() - Aggro() - return 1 - -//What we do after closing in -/mob/living/simple_animal/hostile/proc/MeleeAction(patience = TRUE) - if(rapid_melee > 1) - var/datum/callback/cb = CALLBACK(src, .proc/CheckAndAttack) - var/delay = SSnpcpool.wait / rapid_melee - for(var/i in 1 to rapid_melee) - addtimer(cb, (i - 1)*delay) - else - AttackingTarget() - if(patience) - GainPatience() - -/mob/living/simple_animal/hostile/proc/CheckAndAttack() - if(target && targets_from && isturf(targets_from.loc) && target.Adjacent(targets_from) && !incapacitated()) - AttackingTarget() - -/mob/living/simple_animal/hostile/proc/MoveToTarget(list/possible_targets)//Step 5, handle movement between us and our target - stop_automated_movement = 1 - if(!target || !CanAttack(target)) - LoseTarget() - return 0 - if(target in possible_targets) - var/turf/T = get_turf(src) - if(target.z != T.z) - LoseTarget() - return 0 - var/target_distance = get_dist(targets_from,target) - if(ranged) //We ranged? Shoot at em - if(!target.Adjacent(targets_from) && ranged_cooldown <= world.time) //But make sure they're not in range for a melee attack and our range attack is off cooldown - OpenFire(target) - if(!Process_Spacemove()) //Drifting - walk(src,0) - return 1 - if(retreat_distance != null) //If we have a retreat distance, check if we need to run from our target - if(target_distance <= retreat_distance) //If target's closer than our retreat distance, run - walk_away(src,target,retreat_distance,move_to_delay) - else - Goto(target,move_to_delay,minimum_distance) //Otherwise, get to our minimum distance so we chase them - else - Goto(target,move_to_delay,minimum_distance) - if(target) - if(targets_from && isturf(targets_from.loc) && target.Adjacent(targets_from)) //If they're next to us, attack - MeleeAction() - else - if(rapid_melee > 1 && target_distance <= melee_queue_distance) - MeleeAction(FALSE) - in_melee = FALSE //If we're just preparing to strike do not enter sidestep mode - return 1 - return 0 - if(environment_smash) - if(target.loc != null && get_dist(targets_from, target.loc) <= vision_range) //We can't see our target, but he's in our vision range still - if(ranged_ignores_vision && ranged_cooldown <= world.time) //we can't see our target... but we can fire at them! - OpenFire(target) - if((environment_smash & ENVIRONMENT_SMASH_WALLS) || (environment_smash & ENVIRONMENT_SMASH_RWALLS)) //If we're capable of smashing through walls, forget about vision completely after finding our target - Goto(target,move_to_delay,minimum_distance) - FindHidden() - return 1 - else - if(FindHidden()) - return 1 - LoseTarget() - return 0 - -/mob/living/simple_animal/hostile/proc/Goto(target, delay, minimum_distance) - if(target == src.target) - approaching_target = TRUE - else - approaching_target = FALSE - walk_to(src, target, minimum_distance, delay) - -/mob/living/simple_animal/hostile/adjustHealth(damage, updating_health = TRUE) - . = ..() - if(!ckey && !stat && search_objects < 3 && damage > 0)//Not unconscious, and we don't ignore mobs - if(search_objects)//Turn off item searching and ignore whatever item we were looking at, we're more concerned with fight or flight - target = null - LoseSearchObjects() - if(AIStatus != AI_ON && AIStatus != AI_OFF) - toggle_ai(AI_ON) - FindTarget() - else if(target != null && prob(40))//No more pulling a mob forever and having a second player attack it, it can switch targets now if it finds a more suitable one - FindTarget() - -/mob/living/simple_animal/hostile/proc/AttackingTarget() - SEND_SIGNAL(src, COMSIG_HOSTILE_ATTACKINGTARGET, target) - in_melee = TRUE - return target.attack_animal(src) - -/mob/living/simple_animal/hostile/proc/Aggro() - vision_range = aggro_vision_range - if(target && emote_taunt.len && prob(taunt_chance)) - emote("me", 1, "[pick(emote_taunt)] at [target].") - taunt_chance = max(taunt_chance-7,2) - -/mob/living/simple_animal/hostile/proc/LoseAggro() - stop_automated_movement = 0 - vision_range = initial(vision_range) - taunt_chance = initial(taunt_chance) - -/mob/living/simple_animal/hostile/proc/LoseTarget() - target = null - approaching_target = FALSE - in_melee = FALSE - walk(src, 0) - LoseAggro() - -//////////////END HOSTILE MOB TARGETTING AND AGGRESSION//////////// - -/mob/living/simple_animal/hostile/death(gibbed) - // Only execute the below if we successfully died - . = ..(gibbed) - if(!.) - return FALSE - LoseTarget() - -/mob/living/simple_animal/hostile/proc/summon_backup(distance) - do_alert_animation(src) - playsound(loc, 'sound/machines/chime.ogg', 50, 1, -1) - for(var/mob/living/simple_animal/hostile/M in oview(distance, targets_from)) - if(faction_check_mob(M, TRUE)) - if(M.AIStatus == AI_OFF) - return - else - M.Goto(src,M.move_to_delay,M.minimum_distance) - -/mob/living/simple_animal/hostile/proc/CheckFriendlyFire(atom/A) - if(check_friendly_fire) - for(var/turf/T in getline(src,A)) // Not 100% reliable but this is faster than simulating actual trajectory - for(var/mob/living/L in T) - if(L == src || L == A) - continue - if(faction_check_mob(L) && !attack_same) - return TRUE - -/mob/living/simple_animal/hostile/proc/OpenFire(atom/A) - if(CheckFriendlyFire(A)) - return - visible_message("[src] [ranged_message] at [A]!") - - - if(rapid > 1) - var/datum/callback/cb = CALLBACK(src, .proc/Shoot, A) - for(var/i in 1 to rapid) - addtimer(cb, (i - 1)*rapid_fire_delay) - else - Shoot(A) - ranged_cooldown = world.time + ranged_cooldown_time - -/mob/living/simple_animal/hostile/proc/Shoot(atom/targeted_atom) - if( QDELETED(targeted_atom) || targeted_atom == targets_from.loc || targeted_atom == targets_from ) - return - var/turf/startloc = get_turf(targets_from) - if(casingtype) - var/obj/item/ammo_casing/casing = new casingtype(startloc) - playsound(src, projectilesound, 100, 1) - casing.fire(targeted_atom, src, zone_override = ran_zone()) - else if(projectiletype) - var/obj/item/projectile/P = new projectiletype(startloc) - playsound(src, projectilesound, 100, 1) - P.current = startloc - P.starting = startloc - P.firer = src - P.yo = targeted_atom.y - startloc.y - P.xo = targeted_atom.x - startloc.x - if(AIStatus != AI_ON)//Don't want mindless mobs to have their movement screwed up firing in space - newtonian_move(get_dir(targeted_atom, targets_from)) - P.original = targeted_atom - P.preparePixelProjectile(targeted_atom, get_turf(targeted_atom), src) - P.fire() - return P - -/mob/living/simple_animal/hostile/proc/CanSmashTurfs(turf/T) - return iswallturf(T) || ismineralturf(T) - -/mob/living/simple_animal/hostile/Move(atom/newloc, dir , step_x , step_y) - if(dodging && approaching_target && prob(dodge_prob) && moving_diagonally == 0 && isturf(loc) && isturf(newloc)) - return dodge(newloc, dir) - else - return ..() - -/mob/living/simple_animal/hostile/proc/dodge(moving_to,move_direction) - //Assuming we move towards the target we want to swerve toward them to get closer - var/cdir = turn(move_direction, 45) - var/ccdir = turn(move_direction, -45) - dodging = FALSE - . = Move(get_step(loc,pick(cdir,ccdir))) - if(!.)//Can't dodge there so we just carry on - . = Move(moving_to,move_direction) - dodging = TRUE - -/mob/living/simple_animal/hostile/proc/DestroyObjectsInDirection(direction) - var/turf/T = get_step(targets_from, direction) - if(QDELETED(T)) - return - if(T.Adjacent(targets_from)) - if(CanSmashTurfs(T)) - T.attack_animal(src) - return - for(var/obj/O in T.contents) - if(!O.Adjacent(targets_from)) - continue - if((ismachinery(O) || isstructure(O)) && O.density && environment_smash >= ENVIRONMENT_SMASH_STRUCTURES && !O.IsObscured()) - O.attack_animal(src) - return - -/mob/living/simple_animal/hostile/proc/DestroyPathToTarget() - if(environment_smash) - EscapeConfinement() - var/dir_to_target = get_dir(targets_from, target) - var/dir_list = list() - if(dir_to_target in diagonals) //it's diagonal, so we need two directions to hit - for(var/direction in cardinal) - if(direction & dir_to_target) - dir_list += direction - else - dir_list += dir_to_target - for(var/direction in dir_list) //now we hit all of the directions we got in this fashion, since it's the only directions we should actually need - DestroyObjectsInDirection(direction) - -/mob/living/simple_animal/hostile/proc/DestroySurroundings() // for use with megafauna destroying everything around them - if(environment_smash) - EscapeConfinement() - for(var/dir in cardinal) - DestroyObjectsInDirection(dir) - -/mob/living/simple_animal/hostile/proc/EscapeConfinement() - if(buckled) - buckled.attack_animal(src) - if(!isturf(targets_from.loc) && targets_from.loc != null)//Did someone put us in something? - var/atom/A = targets_from.loc - A.attack_animal(src)//Bang on it till we get out - -/mob/living/simple_animal/hostile/proc/FindHidden() - if(istype(target.loc, /obj/structure/closet) || istype(target.loc, /obj/machinery/disposal) || istype(target.loc, /obj/machinery/sleeper) || istype(target.loc, /obj/machinery/bodyscanner) || istype(target.loc, /obj/machinery/recharge_station)) - var/atom/A = target.loc - Goto(A,move_to_delay,minimum_distance) - if(A.Adjacent(targets_from)) - A.attack_animal(src) - return 1 - -/mob/living/simple_animal/hostile/RangedAttack(atom/A, params) //Player firing - if(ranged && ranged_cooldown <= world.time) - target = A - OpenFire(A) - ..() - - - -////// AI Status /////// -/mob/living/simple_animal/hostile/proc/AICanContinue(var/list/possible_targets) - switch(AIStatus) - if(AI_ON) - . = 1 - if(AI_IDLE) - if(FindTarget(possible_targets, 1)) - . = 1 - toggle_ai(AI_ON) //Wake up for more than one Life() cycle. - else - . = 0 - -/mob/living/simple_animal/hostile/proc/AIShouldSleep(var/list/possible_targets) - return !FindTarget(possible_targets, 1) - - -//These two procs handle losing our target if we've failed to attack them for -//more than lose_patience_timeout deciseconds, which probably means we're stuck -/mob/living/simple_animal/hostile/proc/GainPatience() - if(lose_patience_timeout) - LosePatience() - lose_patience_timer_id = addtimer(CALLBACK(src, .proc/LoseTarget), lose_patience_timeout, TIMER_STOPPABLE) - - -/mob/living/simple_animal/hostile/proc/LosePatience() - deltimer(lose_patience_timer_id) - - -//These two procs handle losing and regaining search_objects when attacked by a mob -/mob/living/simple_animal/hostile/proc/LoseSearchObjects() - search_objects = 0 - deltimer(search_objects_timer_id) - search_objects_timer_id = addtimer(CALLBACK(src, .proc/RegainSearchObjects), search_objects_regain_time, TIMER_STOPPABLE) - - -/mob/living/simple_animal/hostile/proc/RegainSearchObjects(value) - if(!value) - value = initial(search_objects) - search_objects = value - -/mob/living/simple_animal/hostile/consider_wakeup() - ..() - var/list/tlist - var/turf/T = get_turf(src) - - if(!T) - return - - if(!length(SSmobs.clients_by_zlevel[T.z])) // It's fine to use .len here but doesn't compile on 511 - toggle_ai(AI_Z_OFF) - return - - var/cheap_search = isturf(T) && !is_station_level(T.z) - if(cheap_search) - tlist = ListTargetsLazy(T.z) - else - tlist = ListTargets() - - if(AIStatus == AI_IDLE && FindTarget(tlist, 1)) - if(cheap_search) //Try again with full effort - FindTarget() - toggle_ai(AI_ON) - -/mob/living/simple_animal/hostile/proc/ListTargetsLazy(var/_Z)//Step 1, find out what we can see - var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha, /obj/spacepod)) - . = list() - for(var/I in SSmobs.clients_by_zlevel[_Z]) - var/mob/M = I - if(get_dist(M, src) < vision_range) - if(isturf(M.loc)) - . += M - else if(M.loc.type in hostile_machines) - . += M.loc \ No newline at end of file +/mob/living/simple_animal/hostile + faction = list("hostile") + stop_automated_movement_when_pulled = 0 + obj_damage = 40 + environment_smash = ENVIRONMENT_SMASH_STRUCTURES //Bitflags. Set to ENVIRONMENT_SMASH_STRUCTURES to break closets,tables,racks, etc; ENVIRONMENT_SMASH_WALLS for walls; ENVIRONMENT_SMASH_RWALLS for rwalls + var/atom/target + var/ranged = FALSE + var/rapid = 0 //How many shots per volley. + var/rapid_fire_delay = 2 //Time between rapid fire shots + + var/dodging = FALSE + var/approaching_target = FALSE //We should dodge now + var/in_melee = FALSE //We should sidestep now + var/dodge_prob = 30 + var/sidestep_per_cycle = 1 //How many sidesteps per npcpool cycle when in melee + + var/projectiletype //set ONLY it and NULLIFY casingtype var, if we have ONLY projectile + var/projectilesound + var/casingtype //set ONLY it and NULLIFY projectiletype, if we have projectile IN CASING + var/move_to_delay = 3 //delay for the automated movement. + var/list/friends = list() + var/list/emote_taunt = list() + var/taunt_chance = 0 + + var/rapid_melee = 1 //Number of melee attacks between each npc pool tick. Spread evenly. + var/melee_queue_distance = 4 //If target is close enough start preparing to hit them if we have rapid_melee enabled + + var/ranged_message = "fires" //Fluff text for ranged mobs + var/ranged_cooldown = 0 //What the current cooldown on ranged attacks is, generally world.time + ranged_cooldown_time + var/ranged_cooldown_time = 30 //How long, in deciseconds, the cooldown of ranged attacks is + var/ranged_ignores_vision = FALSE //if it'll fire ranged attacks even if it lacks vision on its target, only works with environment smash + var/check_friendly_fire = 0 // Should the ranged mob check for friendlies when shooting + var/retreat_distance = null //If our mob runs from players when they're too close, set in tile distance. By default, mobs do not retreat. + var/minimum_distance = 1 //Minimum approach distance, so ranged mobs chase targets down, but still keep their distance set in tiles to the target, set higher to make mobs keep distance + + +//These vars are related to how mobs locate and target + var/robust_searching = 0 //By default, mobs have a simple searching method, set this to 1 for the more scrutinous searching (stat_attack, stat_exclusive, etc), should be disabled on most mobs + var/vision_range = 9 //How big of an area to search for targets in, a vision of 9 attempts to find targets as soon as they walk into screen view + var/aggro_vision_range = 9 //If a mob is aggro, we search in this radius. Defaults to 9 to keep in line with original simple mob aggro radius + var/search_objects = 0 //If we want to consider objects when searching around, set this to 1. If you want to search for objects while also ignoring mobs until hurt, set it to 2. To completely ignore mobs, even when attacked, set it to 3 + var/search_objects_timer_id //Timer for regaining our old search_objects value after being attacked + var/search_objects_regain_time = 30 //the delay between being attacked and gaining our old search_objects value back + var/list/wanted_objects = list() //A typecache of objects types that will be checked against to attack, should we have search_objects enabled + var/stat_attack = CONSCIOUS //Mobs with stat_attack to UNCONSCIOUS will attempt to attack things that are unconscious, Mobs with stat_attack set to DEAD will attempt to attack the dead. + var/stat_exclusive = FALSE //Mobs with this set to TRUE will exclusively attack things defined by stat_attack, stat_attack DEAD means they will only attack corpses + var/attack_same = 0 //Set us to 1 to allow us to attack our own faction + var/atom/targets_from = null //all range/attack/etc. calculations should be done from this atom, defaults to the mob itself, useful for Vehicles and such + var/attack_all_objects = FALSE //if true, equivalent to having a wanted_objects list containing ALL objects. + + var/lose_patience_timer_id //id for a timer to call LoseTarget(), used to stop mobs fixating on a target they can't reach + var/lose_patience_timeout = 300 //30 seconds by default, so there's no major changes to AI behaviour, beyond actually bailing if stuck forever + +/mob/living/simple_animal/hostile/Initialize(mapload) + . = ..() + + if(!targets_from) + targets_from = src + wanted_objects = typecacheof(wanted_objects) + +/mob/living/simple_animal/hostile/Destroy() + targets_from = null + target = null + return ..() + +/mob/living/simple_animal/hostile/Life(seconds, times_fired) + . = ..() + if(!.) + walk(src, 0) + return FALSE + +/mob/living/simple_animal/hostile/handle_automated_action() + if(AIStatus == AI_OFF) + return 0 + var/list/possible_targets = ListTargets() //we look around for potential targets and make it a list for later use. + + if(environment_smash) + EscapeConfinement() + + if(AICanContinue(possible_targets)) + if(!QDELETED(target) && !targets_from.Adjacent(target)) + DestroyPathToTarget() + if(!MoveToTarget(possible_targets)) //if we lose our target + if(AIShouldSleep(possible_targets)) // we try to acquire a new one + toggle_ai(AI_IDLE) // otherwise we go idle + return 1 + +/mob/living/simple_animal/hostile/handle_automated_movement() + . = ..() + if(dodging && target && in_melee && isturf(loc) && isturf(target.loc)) + var/datum/cb = CALLBACK(src,.proc/sidestep) + if(sidestep_per_cycle > 1) //For more than one just spread them equally - this could changed to some sensible distribution later + var/sidestep_delay = SSnpcpool.wait / sidestep_per_cycle + for(var/i in 1 to sidestep_per_cycle) + addtimer(cb, (i - 1) * sidestep_delay) + else //Otherwise randomize it to make the players guessing. + addtimer(cb,rand(1, SSnpcpool.wait)) + +/mob/living/simple_animal/hostile/proc/sidestep() + if(!target || !isturf(target.loc) || !isturf(loc) || stat == DEAD) + return + var/target_dir = get_dir(src, target) + + var/static/list/cardinal_sidestep_directions = list(-90, -45, 0, 45, 90) + var/static/list/diagonal_sidestep_directions = list(-45, 0, 45) + var/chosen_dir = 0 + if (target_dir & (target_dir - 1)) + chosen_dir = pick(diagonal_sidestep_directions) + else + chosen_dir = pick(cardinal_sidestep_directions) + if(chosen_dir) + chosen_dir = turn(target_dir, chosen_dir) + Move(get_step(src, chosen_dir)) + face_atom(target) //Looks better if they keep looking at you when dodging + +/mob/living/simple_animal/hostile/attacked_by(obj/item/I, mob/living/user) + if(stat == CONSCIOUS && !target && AIStatus != AI_OFF && !client && user) + FindTarget(list(user), 1) + return ..() + +/mob/living/simple_animal/hostile/bullet_act(obj/item/projectile/P) + if(stat == CONSCIOUS && !target && AIStatus != AI_OFF && !client) + if(P.firer && get_dist(src, P.firer) <= aggro_vision_range) + FindTarget(list(P.firer), 1) + Goto(P.starting, move_to_delay, 3) + return ..() + +//////////////HOSTILE MOB TARGETTING AND AGGRESSION//////////// + +/mob/living/simple_animal/hostile/proc/ListTargets()//Step 1, find out what we can see + if(!search_objects) + . = hearers(vision_range, targets_from) - src //Remove self, so we don't suicide + + var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha, /obj/spacepod)) + + for(var/HM in typecache_filter_list(range(vision_range, targets_from), hostile_machines)) + if(can_see(targets_from, HM, vision_range)) + . += HM + else + . = oview(vision_range, targets_from) + +/mob/living/simple_animal/hostile/proc/FindTarget(var/list/possible_targets, var/HasTargetsList = 0)//Step 2, filter down possible targets to things we actually care about + . = list() + if(!HasTargetsList) + possible_targets = ListTargets() + for(var/pos_targ in possible_targets) + var/atom/A = pos_targ + if(Found(A))//Just in case people want to override targetting + . = list(A) + break + if(CanAttack(A))//Can we attack it? + . += A + continue + var/Target = PickTarget(.) + GiveTarget(Target) + return Target //We now have a target + +/mob/living/simple_animal/hostile/proc/PossibleThreats() + . = list() + for(var/pos_targ in ListTargets()) + var/atom/A = pos_targ + if(Found(A)) + . = list(A) + break + if(CanAttack(A)) + . += A + continue + +/mob/living/simple_animal/hostile/proc/Found(var/atom/A)//This is here as a potential override to pick a specific target if available + return + +/mob/living/simple_animal/hostile/proc/PickTarget(list/Targets)//Step 3, pick amongst the possible, attackable targets + if(target != null)//If we already have a target, but are told to pick again, calculate the lowest distance between all possible, and pick from the lowest distance targets + for(var/pos_targ in Targets) + var/atom/A = pos_targ + var/target_dist = get_dist(targets_from, target) + var/possible_target_distance = get_dist(targets_from, A) + if(target_dist < possible_target_distance) + Targets -= A + if(!Targets.len)//We didnt find nothin! + return + var/chosen_target = pick(Targets)//Pick the remaining targets (if any) at random + return chosen_target + +// Please do not add one-off mob AIs here, but override this function for your mob +/mob/living/simple_animal/hostile/CanAttack(atom/the_target)//Can we actually attack a possible target? + if(isturf(the_target) || !the_target || the_target.type == /atom/movable/lighting_object) // bail out on invalids + return FALSE + + if(ismob(the_target)) //Target is in godmode, ignore it. + var/mob/M = the_target + if(M.status_flags & GODMODE) + return FALSE + + if(see_invisible < the_target.invisibility) //Target's invisible to us, forget it + return FALSE + if(search_objects < 2) + if(isliving(the_target)) + var/mob/living/L = the_target + var/faction_check = faction_check_mob(L) + if(robust_searching) + if(faction_check && !attack_same) + return FALSE + if(L.stat > stat_attack) + return FALSE + if(L in friends) + return FALSE + else + if((faction_check && !attack_same) || L.stat) + return FALSE + return TRUE + + if(ismecha(the_target)) + var/obj/mecha/M = the_target + if(M.occupant)//Just so we don't attack empty mechs + if(CanAttack(M.occupant)) + return TRUE + + if(isspacepod(the_target)) + var/obj/spacepod/S = the_target + if(S.pilot)//Just so we don't attack empty pods + if(CanAttack(S.pilot)) + return TRUE + + if(istype(the_target, /obj/machinery/porta_turret)) + var/obj/machinery/porta_turret/P = the_target + if(P.faction in faction) + return FALSE + if(!P.raised) //Don't attack invincible turrets + return FALSE + if(P.stat & BROKEN) //Or turrets that are already broken + return FALSE + return TRUE + + if(isobj(the_target)) + if(attack_all_objects || is_type_in_typecache(the_target, wanted_objects)) + return TRUE + + return FALSE + +/mob/living/simple_animal/hostile/proc/GiveTarget(new_target)//Step 4, give us our selected target + target = new_target + LosePatience() + if(target != null) + GainPatience() + Aggro() + return 1 + +//What we do after closing in +/mob/living/simple_animal/hostile/proc/MeleeAction(patience = TRUE) + if(rapid_melee > 1) + var/datum/callback/cb = CALLBACK(src, .proc/CheckAndAttack) + var/delay = SSnpcpool.wait / rapid_melee + for(var/i in 1 to rapid_melee) + addtimer(cb, (i - 1)*delay) + else + AttackingTarget() + if(patience) + GainPatience() + +/mob/living/simple_animal/hostile/proc/CheckAndAttack() + if(target && targets_from && isturf(targets_from.loc) && target.Adjacent(targets_from) && !incapacitated()) + AttackingTarget() + +/mob/living/simple_animal/hostile/proc/MoveToTarget(list/possible_targets)//Step 5, handle movement between us and our target + stop_automated_movement = 1 + if(!target || !CanAttack(target)) + LoseTarget() + return 0 + if(target in possible_targets) + var/turf/T = get_turf(src) + if(target.z != T.z) + LoseTarget() + return 0 + var/target_distance = get_dist(targets_from,target) + if(ranged) //We ranged? Shoot at em + if(!target.Adjacent(targets_from) && ranged_cooldown <= world.time) //But make sure they're not in range for a melee attack and our range attack is off cooldown + OpenFire(target) + if(!Process_Spacemove()) //Drifting + walk(src,0) + return 1 + if(retreat_distance != null) //If we have a retreat distance, check if we need to run from our target + if(target_distance <= retreat_distance) //If target's closer than our retreat distance, run + walk_away(src,target,retreat_distance,move_to_delay) + else + Goto(target,move_to_delay,minimum_distance) //Otherwise, get to our minimum distance so we chase them + else + Goto(target,move_to_delay,minimum_distance) + if(target) + if(targets_from && isturf(targets_from.loc) && target.Adjacent(targets_from)) //If they're next to us, attack + MeleeAction() + else + if(rapid_melee > 1 && target_distance <= melee_queue_distance) + MeleeAction(FALSE) + in_melee = FALSE //If we're just preparing to strike do not enter sidestep mode + return 1 + return 0 + if(environment_smash) + if(target.loc != null && get_dist(targets_from, target.loc) <= vision_range) //We can't see our target, but he's in our vision range still + if(ranged_ignores_vision && ranged_cooldown <= world.time) //we can't see our target... but we can fire at them! + OpenFire(target) + if((environment_smash & ENVIRONMENT_SMASH_WALLS) || (environment_smash & ENVIRONMENT_SMASH_RWALLS)) //If we're capable of smashing through walls, forget about vision completely after finding our target + Goto(target,move_to_delay,minimum_distance) + FindHidden() + return 1 + else + if(FindHidden()) + return 1 + LoseTarget() + return 0 + +/mob/living/simple_animal/hostile/proc/Goto(target, delay, minimum_distance) + if(target == src.target) + approaching_target = TRUE + else + approaching_target = FALSE + walk_to(src, target, minimum_distance, delay) + +/mob/living/simple_animal/hostile/adjustHealth(damage, updating_health = TRUE) + . = ..() + if(!ckey && !stat && search_objects < 3 && damage > 0)//Not unconscious, and we don't ignore mobs + if(search_objects)//Turn off item searching and ignore whatever item we were looking at, we're more concerned with fight or flight + target = null + LoseSearchObjects() + if(AIStatus != AI_ON && AIStatus != AI_OFF) + toggle_ai(AI_ON) + FindTarget() + else if(target != null && prob(40))//No more pulling a mob forever and having a second player attack it, it can switch targets now if it finds a more suitable one + FindTarget() + +/mob/living/simple_animal/hostile/proc/AttackingTarget() + SEND_SIGNAL(src, COMSIG_HOSTILE_ATTACKINGTARGET, target) + in_melee = TRUE + return target.attack_animal(src) + +/mob/living/simple_animal/hostile/proc/Aggro() + vision_range = aggro_vision_range + if(target && emote_taunt.len && prob(taunt_chance)) + emote("me", 1, "[pick(emote_taunt)] at [target].") + taunt_chance = max(taunt_chance-7,2) + +/mob/living/simple_animal/hostile/proc/LoseAggro() + stop_automated_movement = 0 + vision_range = initial(vision_range) + taunt_chance = initial(taunt_chance) + +/mob/living/simple_animal/hostile/proc/LoseTarget() + target = null + approaching_target = FALSE + in_melee = FALSE + walk(src, 0) + LoseAggro() + +//////////////END HOSTILE MOB TARGETTING AND AGGRESSION//////////// + +/mob/living/simple_animal/hostile/death(gibbed) + // Only execute the below if we successfully died + . = ..(gibbed) + if(!.) + return FALSE + LoseTarget() + +/mob/living/simple_animal/hostile/proc/summon_backup(distance) + do_alert_animation(src) + playsound(loc, 'sound/machines/chime.ogg', 50, 1, -1) + for(var/mob/living/simple_animal/hostile/M in oview(distance, targets_from)) + if(faction_check_mob(M, TRUE)) + if(M.AIStatus == AI_OFF) + return + else + M.Goto(src,M.move_to_delay,M.minimum_distance) + +/mob/living/simple_animal/hostile/proc/CheckFriendlyFire(atom/A) + if(check_friendly_fire) + for(var/turf/T in getline(src,A)) // Not 100% reliable but this is faster than simulating actual trajectory + for(var/mob/living/L in T) + if(L == src || L == A) + continue + if(faction_check_mob(L) && !attack_same) + return TRUE + +/mob/living/simple_animal/hostile/proc/OpenFire(atom/A) + if(CheckFriendlyFire(A)) + return + visible_message("[src] [ranged_message] at [A]!") + + + if(rapid > 1) + var/datum/callback/cb = CALLBACK(src, .proc/Shoot, A) + for(var/i in 1 to rapid) + addtimer(cb, (i - 1)*rapid_fire_delay) + else + Shoot(A) + ranged_cooldown = world.time + ranged_cooldown_time + +/mob/living/simple_animal/hostile/proc/Shoot(atom/targeted_atom) + if( QDELETED(targeted_atom) || targeted_atom == targets_from.loc || targeted_atom == targets_from ) + return + var/turf/startloc = get_turf(targets_from) + if(casingtype) + var/obj/item/ammo_casing/casing = new casingtype(startloc) + playsound(src, projectilesound, 100, 1) + casing.fire(targeted_atom, src, zone_override = ran_zone()) + else if(projectiletype) + var/obj/item/projectile/P = new projectiletype(startloc) + playsound(src, projectilesound, 100, 1) + P.current = startloc + P.starting = startloc + P.firer = src + P.yo = targeted_atom.y - startloc.y + P.xo = targeted_atom.x - startloc.x + if(AIStatus != AI_ON)//Don't want mindless mobs to have their movement screwed up firing in space + newtonian_move(get_dir(targeted_atom, targets_from)) + P.original = targeted_atom + P.preparePixelProjectile(targeted_atom, get_turf(targeted_atom), src) + P.fire() + return P + +/mob/living/simple_animal/hostile/proc/CanSmashTurfs(turf/T) + return iswallturf(T) || ismineralturf(T) + +/mob/living/simple_animal/hostile/Move(atom/newloc, dir , step_x , step_y) + if(dodging && approaching_target && prob(dodge_prob) && moving_diagonally == 0 && isturf(loc) && isturf(newloc)) + return dodge(newloc, dir) + else + return ..() + +/mob/living/simple_animal/hostile/proc/dodge(moving_to,move_direction) + //Assuming we move towards the target we want to swerve toward them to get closer + var/cdir = turn(move_direction, 45) + var/ccdir = turn(move_direction, -45) + dodging = FALSE + . = Move(get_step(loc,pick(cdir,ccdir))) + if(!.)//Can't dodge there so we just carry on + . = Move(moving_to,move_direction) + dodging = TRUE + +/mob/living/simple_animal/hostile/proc/DestroyObjectsInDirection(direction) + var/turf/T = get_step(targets_from, direction) + if(QDELETED(T)) + return + if(T.Adjacent(targets_from)) + if(CanSmashTurfs(T)) + T.attack_animal(src) + return + for(var/obj/O in T.contents) + if(!O.Adjacent(targets_from)) + continue + if((ismachinery(O) || isstructure(O)) && O.density && environment_smash >= ENVIRONMENT_SMASH_STRUCTURES && !O.IsObscured()) + O.attack_animal(src) + return + +/mob/living/simple_animal/hostile/proc/DestroyPathToTarget() + if(environment_smash) + EscapeConfinement() + var/dir_to_target = get_dir(targets_from, target) + var/dir_list = list() + if(dir_to_target in diagonals) //it's diagonal, so we need two directions to hit + for(var/direction in cardinal) + if(direction & dir_to_target) + dir_list += direction + else + dir_list += dir_to_target + for(var/direction in dir_list) //now we hit all of the directions we got in this fashion, since it's the only directions we should actually need + DestroyObjectsInDirection(direction) + +/mob/living/simple_animal/hostile/proc/DestroySurroundings() // for use with megafauna destroying everything around them + if(environment_smash) + EscapeConfinement() + for(var/dir in cardinal) + DestroyObjectsInDirection(dir) + +/mob/living/simple_animal/hostile/proc/EscapeConfinement() + if(buckled) + buckled.attack_animal(src) + if(!isturf(targets_from.loc) && targets_from.loc != null)//Did someone put us in something? + var/atom/A = targets_from.loc + A.attack_animal(src)//Bang on it till we get out + +/mob/living/simple_animal/hostile/proc/FindHidden() + if(istype(target.loc, /obj/structure/closet) || istype(target.loc, /obj/machinery/disposal) || istype(target.loc, /obj/machinery/sleeper) || istype(target.loc, /obj/machinery/bodyscanner) || istype(target.loc, /obj/machinery/recharge_station)) + var/atom/A = target.loc + Goto(A,move_to_delay,minimum_distance) + if(A.Adjacent(targets_from)) + A.attack_animal(src) + return 1 + +/mob/living/simple_animal/hostile/RangedAttack(atom/A, params) //Player firing + if(ranged && ranged_cooldown <= world.time) + target = A + OpenFire(A) + ..() + + + +////// AI Status /////// +/mob/living/simple_animal/hostile/proc/AICanContinue(var/list/possible_targets) + switch(AIStatus) + if(AI_ON) + . = 1 + if(AI_IDLE) + if(FindTarget(possible_targets, 1)) + . = 1 + toggle_ai(AI_ON) //Wake up for more than one Life() cycle. + else + . = 0 + +/mob/living/simple_animal/hostile/proc/AIShouldSleep(var/list/possible_targets) + return !FindTarget(possible_targets, 1) + + +//These two procs handle losing our target if we've failed to attack them for +//more than lose_patience_timeout deciseconds, which probably means we're stuck +/mob/living/simple_animal/hostile/proc/GainPatience() + if(lose_patience_timeout) + LosePatience() + lose_patience_timer_id = addtimer(CALLBACK(src, .proc/LoseTarget), lose_patience_timeout, TIMER_STOPPABLE) + + +/mob/living/simple_animal/hostile/proc/LosePatience() + deltimer(lose_patience_timer_id) + + +//These two procs handle losing and regaining search_objects when attacked by a mob +/mob/living/simple_animal/hostile/proc/LoseSearchObjects() + search_objects = 0 + deltimer(search_objects_timer_id) + search_objects_timer_id = addtimer(CALLBACK(src, .proc/RegainSearchObjects), search_objects_regain_time, TIMER_STOPPABLE) + + +/mob/living/simple_animal/hostile/proc/RegainSearchObjects(value) + if(!value) + value = initial(search_objects) + search_objects = value + +/mob/living/simple_animal/hostile/consider_wakeup() + ..() + var/list/tlist + var/turf/T = get_turf(src) + + if(!T) + return + + if(!length(SSmobs.clients_by_zlevel[T.z])) // It's fine to use .len here but doesn't compile on 511 + toggle_ai(AI_Z_OFF) + return + + var/cheap_search = isturf(T) && !is_station_level(T.z) + if(cheap_search) + tlist = ListTargetsLazy(T.z) + else + tlist = ListTargets() + + if(AIStatus == AI_IDLE && FindTarget(tlist, 1)) + if(cheap_search) //Try again with full effort + FindTarget() + toggle_ai(AI_ON) + +/mob/living/simple_animal/hostile/proc/ListTargetsLazy(var/_Z)//Step 1, find out what we can see + var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha, /obj/spacepod)) + . = list() + for(var/I in SSmobs.clients_by_zlevel[_Z]) + var/mob/M = I + if(get_dist(M, src) < vision_range) + if(isturf(M.loc)) + . += M + else if(M.loc.type in hostile_machines) + . += M.loc diff --git a/code/modules/mob/living/simple_animal/hostile/jungle_animals.dm b/code/modules/mob/living/simple_animal/hostile/jungle_animals.dm index 79d6c42cb11..934e1312606 100644 --- a/code/modules/mob/living/simple_animal/hostile/jungle_animals.dm +++ b/code/modules/mob/living/simple_animal/hostile/jungle_animals.dm @@ -42,4 +42,4 @@ if(prob(15) && iscarbon(target)) var/mob/living/carbon/C = target C.Weaken(3) - C.visible_message("\the [src] knocks down \the [C]!") \ No newline at end of file + C.visible_message("\the [src] knocks down \the [C]!") diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm index 3061c765359..70585b818f5 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm @@ -27,7 +27,7 @@ Difficulty: Medium maxHealth = 900 icon_state = "miner" icon_living = "miner" - icon = 'icons/mob/alienqueen.dmi' + icon = 'icons/mob/lavaland/blood_drunk.dmi' light_color = "#E4C7C5" flying = FALSE speak_emote = list("roars") @@ -37,7 +37,7 @@ Difficulty: Medium projectilesound = 'sound/weapons/kenetic_accel.ogg' ranged = TRUE ranged_cooldown_time = 16 - pixel_x = -16 + pixel_x = -7 crusher_loot = list(/obj/item/melee/energy/cleaving_saw, /obj/item/gun/energy/kinetic_accelerator, /obj/item/crusher_trophy/miner_eye) loot = list(/obj/item/melee/energy/cleaving_saw, /obj/item/gun/energy/kinetic_accelerator) wander = FALSE @@ -290,4 +290,4 @@ Difficulty: Medium if(. && prob(12)) INVOKE_ASYNC(src, .proc/dash) -#undef MINER_DASH_RANGE \ No newline at end of file +#undef MINER_DASH_RANGE diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm index 66799ffc6c6..9fa5abe4695 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm @@ -548,4 +548,4 @@ Difficulty: Hard return /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/try_bloodattack() - return \ No newline at end of file + return diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm index 8b6d6fed947..e810222e1c4 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm @@ -669,4 +669,4 @@ obj/effect/temp_visual/fireball ..(targets, user, 3) /mob/living/simple_animal/hostile/megafauna/dragon/space_dragon/AltClickOn(atom/movable/A) - return \ No newline at end of file + return diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm index 088d8631e77..24ee584b378 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm @@ -737,4 +737,4 @@ Difficulty: Hard icon_state = null gpstag = "Zealous Signal" desc = "Heed its words." - invisibility = 100 \ No newline at end of file + invisibility = 100 diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm index 81c96135b0e..b3618efff0a 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm @@ -158,4 +158,4 @@ /datum/action/innate/megafauna_attack/Activate() M.chosen_attack = chosen_attack_num - to_chat(M, chosen_message) \ No newline at end of file + to_chat(M, chosen_message) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm index f31b6a8619b..696c692a5dc 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm @@ -285,4 +285,4 @@ GLOBAL_LIST_INIT(AISwarmerCapsByType, list(/mob/living/simple_animal/hostile/swa name = "swarmer catwalk" desc = "A catwalk-like mesh, produced by swarmers to allow them to navigate hostile terrain." icon = 'icons/obj/smooth_structures/swarmer_catwalk.dmi' - icon_state = "swarmer_catwalk" \ No newline at end of file + icon_state = "swarmer_catwalk" diff --git a/code/modules/mob/living/simple_animal/hostile/mimic.dm b/code/modules/mob/living/simple_animal/hostile/mimic.dm index 761f44d11bd..53f384aee0c 100644 --- a/code/modules/mob/living/simple_animal/hostile/mimic.dm +++ b/code/modules/mob/living/simple_animal/hostile/mimic.dm @@ -1,283 +1,283 @@ -/mob/living/simple_animal/hostile/mimic - name = "crate" - desc = "A rectangular steel crate." - icon = 'icons/obj/crates.dmi' - icon_state = "crate" - icon_living = "crate" - - response_help = "touches the" - response_disarm = "pushes the" - response_harm = "hits the" - speed = 0 - maxHealth = 250 - health = 250 - - harm_intent_damage = 5 - melee_damage_lower = 8 - melee_damage_upper = 12 - attacktext = "attacks" - attack_sound = 'sound/weapons/bite.ogg' - emote_taunt = list("growls") - speak_emote = list("creaks") - taunt_chance = 30 - - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - - faction = list("mimic") - move_to_delay = 9 - - var/is_electronic = 0 - gold_core_spawnable = HOSTILE_SPAWN - del_on_death = 1 - -/mob/living/simple_animal/hostile/mimic/emp_act(severity) - if(is_electronic) - switch(severity) - if(1) - death() - if(2) - adjustBruteLoss(50) - ..(severity) - -// Aggro when you try to open them. Will also pickup loot when spawns and drop it when dies. -/mob/living/simple_animal/hostile/mimic/crate - attacktext = "bites" - stop_automated_movement = 1 - wander = 0 - var/attempt_open = 0 - -// Pickup loot -/mob/living/simple_animal/hostile/mimic/crate/Initialize() - ..() - for(var/obj/item/I in loc) - I.loc = src - -/mob/living/simple_animal/hostile/mimic/crate/DestroyPathToTarget() - ..() - if(prob(90)) - icon_state = "[initial(icon_state)]open" - else - icon_state = initial(icon_state) - -/mob/living/simple_animal/hostile/mimic/crate/ListTargets() - if(attempt_open) - return ..() - return ..(1) - -/mob/living/simple_animal/hostile/mimic/crate/FindTarget() - . = ..() - if(.) - trigger() - -/mob/living/simple_animal/hostile/mimic/crate/AttackingTarget() - . = ..() - if(.) - icon_state = initial(icon_state) - if(prob(15) && iscarbon(target)) - var/mob/living/carbon/C = target - C.Weaken(2) - C.visible_message("\The [src] knocks down \the [C]!", "\The [src] knocks you down!") - -/mob/living/simple_animal/hostile/mimic/crate/proc/trigger() - if(!attempt_open) - visible_message("[src] starts to move!") - attempt_open = 1 - -/mob/living/simple_animal/hostile/mimic/crate/adjustHealth(amount, updating_health = TRUE) - trigger() - . = ..() - -/mob/living/simple_animal/hostile/mimic/crate/LoseTarget() - ..() - icon_state = initial(icon_state) - -/mob/living/simple_animal/hostile/mimic/crate/death(gibbed) - if(can_die()) - var/obj/structure/closet/crate/C = new(get_turf(src)) - // Put loot in crate - for(var/obj/O in src) - O.forceMove(C) - // due to `del_on_death` - return ..() - -var/global/list/protected_objects = list(/obj/structure/table, /obj/structure/cable, /obj/structure/window) - -/mob/living/simple_animal/hostile/mimic/copy - health = 100 - maxHealth = 100 - var/mob/living/creator = null // the creator - var/destroy_objects = 0 - var/knockdown_people = 0 - var/image/googly_eyes = null - gold_core_spawnable = NO_SPAWN - -/mob/living/simple_animal/hostile/mimic/copy/New(loc, obj/copy, mob/living/creator, destroy_original = 0) - ..(loc) - CopyObject(copy, creator, destroy_original) - -/mob/living/simple_animal/hostile/mimic/copy/Life() - ..() - if(!target && !ckey) //Objects eventually revert to normal if no one is around to terrorize - adjustBruteLoss(1) - for(var/mob/living/M in contents) //a fix for animated statues from the flesh to stone spell - death() - -/mob/living/simple_animal/hostile/mimic/copy/death(gibbed) - if(can_die()) - for(var/atom/movable/M in src) - M.loc = get_turf(src) - // due to `del_on_death` - return ..() - -/mob/living/simple_animal/hostile/mimic/copy/ListTargets() - . = ..() - return . - creator - -/mob/living/simple_animal/hostile/mimic/copy/proc/ChangeOwner(var/mob/owner) - if(owner != creator) - LoseTarget() - creator = owner - faction |= "\ref[owner]" - -/mob/living/simple_animal/hostile/mimic/copy/proc/CheckObject(var/obj/O) - if((istype(O, /obj/item) || istype(O, /obj/structure)) && !is_type_in_list(O, protected_objects)) - return 1 - return 0 - -/mob/living/simple_animal/hostile/mimic/copy/proc/CopyObject(var/obj/O, var/mob/living/user, var/destroy_original = 0) - if(destroy_original || CheckObject(O)) - O.loc = src - name = O.name - desc = O.desc - icon = O.icon - icon_state = O.icon_state - icon_living = icon_state - overlays = O.overlays - googly_eyes = image('icons/mob/mob.dmi',"googly_eyes") - overlays += googly_eyes - if(istype(O, /obj/structure) || istype(O, /obj/machinery)) - health = (anchored * 50) + 50 - destroy_objects = 1 - if(O.density && O.anchored) - knockdown_people = 1 - melee_damage_lower *= 2 - melee_damage_upper *= 2 - if(istype(O, /obj/machinery)) - is_electronic = 1 - else if(istype(O, /obj/item)) - var/obj/item/I = O - health = 15 * I.w_class - melee_damage_lower = 2 + I.force - melee_damage_upper = 2 + I.force - move_to_delay = 2 * I.w_class + 1 - maxHealth = health - if(user) - creator = user - faction += "\ref[creator]" // very unique - if(destroy_original) - qdel(O) - return 1 - -/mob/living/simple_animal/hostile/mimic/copy/DestroySurroundings() - if(destroy_objects) - ..() - -/mob/living/simple_animal/hostile/mimic/copy/AttackingTarget() - . = ..() - if(knockdown_people && . && prob(15) && iscarbon(target)) - var/mob/living/carbon/C = target - C.Weaken(2) - C.visible_message("\The [src] knocks down \the [C]!", "\The [src] knocks you down!") - -/mob/living/simple_animal/hostile/mimic/copy/Aggro() - ..() - googly_eyes.dir = get_dir(src,target) - -/mob/living/simple_animal/hostile/mimic/copy/machine - speak = list("HUMANS ARE IMPERFECT!", "YOU SHALL BE ASSIMILATED!", "YOU ARE HARMING YOURSELF", "You have been deemed hazardous. Will you comply?", \ - "My logic is undeniable.", "One of us.", "FLESH IS WEAK", "THIS ISN'T WAR, THIS IS EXTERMINATION!") - speak_chance = 15 - -/mob/living/simple_animal/hostile/mimic/copy/machine/CanAttack(var/atom/the_target) - if(the_target == creator) // Don't attack our creator AI. - return 0 - if(isrobot(the_target)) - var/mob/living/silicon/robot/R = the_target - if(R.connected_ai == creator) // Only attack robots that aren't synced to our creator AI. - return 0 - return ..() - -/mob/living/simple_animal/hostile/mimic/copy/ranged - var/obj/item/gun/TrueGun = null - var/obj/item/gun/magic/Zapstick - var/obj/item/gun/projectile/Pewgun - var/obj/item/gun/energy/Zapgun - -/mob/living/simple_animal/hostile/mimic/copy/ranged/CopyObject(obj/O, mob/living/creator, destroy_original = 0) - if(..()) - emote_see = list("aims menacingly") - obj_damage = 0 - environment_smash = 0 //needed? seems weird for them to do so - ranged = 1 - retreat_distance = 1 //just enough to shoot - minimum_distance = 6 - var/obj/item/gun/G = O - melee_damage_upper = G.force - melee_damage_lower = G.force - max(0, (G.force / 2)) - move_to_delay = 2 * G.w_class + 1 - projectilesound = G.fire_sound - TrueGun = G - if(istype(G, /obj/item/gun/magic)) - Zapstick = G - var/obj/item/ammo_casing/magic/M = Zapstick.ammo_type - projectiletype = initial(M.projectile_type) - if(istype(G, /obj/item/gun/projectile)) - Pewgun = G - var/obj/item/ammo_box/magazine/M = Pewgun.mag_type - casingtype = initial(M.ammo_type) - if(istype(G, /obj/item/gun/energy)) - Zapgun = G - var/selectfiresetting = Zapgun.select - var/obj/item/ammo_casing/energy/E = Zapgun.ammo_type[selectfiresetting] - projectiletype = initial(E.projectile_type) - -/mob/living/simple_animal/hostile/mimic/copy/ranged/OpenFire(the_target) - if(Zapgun) - if(Zapgun.cell) - var/obj/item/ammo_casing/energy/shot = Zapgun.ammo_type[Zapgun.select] - if(Zapgun.cell.charge >= shot.e_cost) - Zapgun.cell.use(shot.e_cost) - Zapgun.update_icon() - ..() - else if(Zapstick) - if(Zapstick.charges) - Zapstick.charges-- - Zapstick.update_icon() - ..() - else if(Pewgun) - if(Pewgun.chambered) - if(Pewgun.chambered.BB) - qdel(Pewgun.chambered.BB) - Pewgun.chambered.BB = null //because qdel takes too long, ensures icon update - Pewgun.chambered.update_icon() - ..() - else - visible_message("The [src] clears a jam!") - Pewgun.chambered.loc = loc //rip revolver immersions, blame shotgun snowflake procs - Pewgun.chambered = null - if(Pewgun.magazine && Pewgun.magazine.stored_ammo.len) - Pewgun.chambered = Pewgun.magazine.get_round(0) - Pewgun.chambered.loc = Pewgun - Pewgun.update_icon() - else if(Pewgun.magazine && Pewgun.magazine.stored_ammo.len) //only true for pumpguns i think - Pewgun.chambered = Pewgun.magazine.get_round(0) - Pewgun.chambered.loc = Pewgun - visible_message("The [src] cocks itself!") - else - ranged = 0 //BANZAIIII - retreat_distance = 0 - minimum_distance = 1 - return - icon_state = TrueGun.icon_state - icon_living = TrueGun.icon_state +/mob/living/simple_animal/hostile/mimic + name = "crate" + desc = "A rectangular steel crate." + icon = 'icons/obj/crates.dmi' + icon_state = "crate" + icon_living = "crate" + + response_help = "touches the" + response_disarm = "pushes the" + response_harm = "hits the" + speed = 0 + maxHealth = 250 + health = 250 + + harm_intent_damage = 5 + melee_damage_lower = 8 + melee_damage_upper = 12 + attacktext = "attacks" + attack_sound = 'sound/weapons/bite.ogg' + emote_taunt = list("growls") + speak_emote = list("creaks") + taunt_chance = 30 + + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + + faction = list("mimic") + move_to_delay = 9 + + var/is_electronic = 0 + gold_core_spawnable = HOSTILE_SPAWN + del_on_death = 1 + +/mob/living/simple_animal/hostile/mimic/emp_act(severity) + if(is_electronic) + switch(severity) + if(1) + death() + if(2) + adjustBruteLoss(50) + ..(severity) + +// Aggro when you try to open them. Will also pickup loot when spawns and drop it when dies. +/mob/living/simple_animal/hostile/mimic/crate + attacktext = "bites" + stop_automated_movement = 1 + wander = 0 + var/attempt_open = 0 + +// Pickup loot +/mob/living/simple_animal/hostile/mimic/crate/Initialize() + ..() + for(var/obj/item/I in loc) + I.loc = src + +/mob/living/simple_animal/hostile/mimic/crate/DestroyPathToTarget() + ..() + if(prob(90)) + icon_state = "[initial(icon_state)]open" + else + icon_state = initial(icon_state) + +/mob/living/simple_animal/hostile/mimic/crate/ListTargets() + if(attempt_open) + return ..() + return ..(1) + +/mob/living/simple_animal/hostile/mimic/crate/FindTarget() + . = ..() + if(.) + trigger() + +/mob/living/simple_animal/hostile/mimic/crate/AttackingTarget() + . = ..() + if(.) + icon_state = initial(icon_state) + if(prob(15) && iscarbon(target)) + var/mob/living/carbon/C = target + C.Weaken(2) + C.visible_message("\The [src] knocks down \the [C]!", "\The [src] knocks you down!") + +/mob/living/simple_animal/hostile/mimic/crate/proc/trigger() + if(!attempt_open) + visible_message("[src] starts to move!") + attempt_open = 1 + +/mob/living/simple_animal/hostile/mimic/crate/adjustHealth(amount, updating_health = TRUE) + trigger() + . = ..() + +/mob/living/simple_animal/hostile/mimic/crate/LoseTarget() + ..() + icon_state = initial(icon_state) + +/mob/living/simple_animal/hostile/mimic/crate/death(gibbed) + if(can_die()) + var/obj/structure/closet/crate/C = new(get_turf(src)) + // Put loot in crate + for(var/obj/O in src) + O.forceMove(C) + // due to `del_on_death` + return ..() + +var/global/list/protected_objects = list(/obj/structure/table, /obj/structure/cable, /obj/structure/window) + +/mob/living/simple_animal/hostile/mimic/copy + health = 100 + maxHealth = 100 + var/mob/living/creator = null // the creator + var/destroy_objects = 0 + var/knockdown_people = 0 + var/image/googly_eyes = null + gold_core_spawnable = NO_SPAWN + +/mob/living/simple_animal/hostile/mimic/copy/New(loc, obj/copy, mob/living/creator, destroy_original = 0) + ..(loc) + CopyObject(copy, creator, destroy_original) + +/mob/living/simple_animal/hostile/mimic/copy/Life() + ..() + if(!target && !ckey) //Objects eventually revert to normal if no one is around to terrorize + adjustBruteLoss(1) + for(var/mob/living/M in contents) //a fix for animated statues from the flesh to stone spell + death() + +/mob/living/simple_animal/hostile/mimic/copy/death(gibbed) + if(can_die()) + for(var/atom/movable/M in src) + M.loc = get_turf(src) + // due to `del_on_death` + return ..() + +/mob/living/simple_animal/hostile/mimic/copy/ListTargets() + . = ..() + return . - creator + +/mob/living/simple_animal/hostile/mimic/copy/proc/ChangeOwner(var/mob/owner) + if(owner != creator) + LoseTarget() + creator = owner + faction |= "\ref[owner]" + +/mob/living/simple_animal/hostile/mimic/copy/proc/CheckObject(var/obj/O) + if((istype(O, /obj/item) || istype(O, /obj/structure)) && !is_type_in_list(O, protected_objects)) + return 1 + return 0 + +/mob/living/simple_animal/hostile/mimic/copy/proc/CopyObject(var/obj/O, var/mob/living/user, var/destroy_original = 0) + if(destroy_original || CheckObject(O)) + O.loc = src + name = O.name + desc = O.desc + icon = O.icon + icon_state = O.icon_state + icon_living = icon_state + overlays = O.overlays + googly_eyes = image('icons/mob/mob.dmi',"googly_eyes") + overlays += googly_eyes + if(istype(O, /obj/structure) || istype(O, /obj/machinery)) + health = (anchored * 50) + 50 + destroy_objects = 1 + if(O.density && O.anchored) + knockdown_people = 1 + melee_damage_lower *= 2 + melee_damage_upper *= 2 + if(istype(O, /obj/machinery)) + is_electronic = 1 + else if(istype(O, /obj/item)) + var/obj/item/I = O + health = 15 * I.w_class + melee_damage_lower = 2 + I.force + melee_damage_upper = 2 + I.force + move_to_delay = 2 * I.w_class + 1 + maxHealth = health + if(user) + creator = user + faction += "\ref[creator]" // very unique + if(destroy_original) + qdel(O) + return 1 + +/mob/living/simple_animal/hostile/mimic/copy/DestroySurroundings() + if(destroy_objects) + ..() + +/mob/living/simple_animal/hostile/mimic/copy/AttackingTarget() + . = ..() + if(knockdown_people && . && prob(15) && iscarbon(target)) + var/mob/living/carbon/C = target + C.Weaken(2) + C.visible_message("\The [src] knocks down \the [C]!", "\The [src] knocks you down!") + +/mob/living/simple_animal/hostile/mimic/copy/Aggro() + ..() + googly_eyes.dir = get_dir(src,target) + +/mob/living/simple_animal/hostile/mimic/copy/machine + speak = list("HUMANS ARE IMPERFECT!", "YOU SHALL BE ASSIMILATED!", "YOU ARE HARMING YOURSELF", "You have been deemed hazardous. Will you comply?", \ + "My logic is undeniable.", "One of us.", "FLESH IS WEAK", "THIS ISN'T WAR, THIS IS EXTERMINATION!") + speak_chance = 15 + +/mob/living/simple_animal/hostile/mimic/copy/machine/CanAttack(var/atom/the_target) + if(the_target == creator) // Don't attack our creator AI. + return 0 + if(isrobot(the_target)) + var/mob/living/silicon/robot/R = the_target + if(R.connected_ai == creator) // Only attack robots that aren't synced to our creator AI. + return 0 + return ..() + +/mob/living/simple_animal/hostile/mimic/copy/ranged + var/obj/item/gun/TrueGun = null + var/obj/item/gun/magic/Zapstick + var/obj/item/gun/projectile/Pewgun + var/obj/item/gun/energy/Zapgun + +/mob/living/simple_animal/hostile/mimic/copy/ranged/CopyObject(obj/O, mob/living/creator, destroy_original = 0) + if(..()) + emote_see = list("aims menacingly") + obj_damage = 0 + environment_smash = 0 //needed? seems weird for them to do so + ranged = 1 + retreat_distance = 1 //just enough to shoot + minimum_distance = 6 + var/obj/item/gun/G = O + melee_damage_upper = G.force + melee_damage_lower = G.force - max(0, (G.force / 2)) + move_to_delay = 2 * G.w_class + 1 + projectilesound = G.fire_sound + TrueGun = G + if(istype(G, /obj/item/gun/magic)) + Zapstick = G + var/obj/item/ammo_casing/magic/M = Zapstick.ammo_type + projectiletype = initial(M.projectile_type) + if(istype(G, /obj/item/gun/projectile)) + Pewgun = G + var/obj/item/ammo_box/magazine/M = Pewgun.mag_type + casingtype = initial(M.ammo_type) + if(istype(G, /obj/item/gun/energy)) + Zapgun = G + var/selectfiresetting = Zapgun.select + var/obj/item/ammo_casing/energy/E = Zapgun.ammo_type[selectfiresetting] + projectiletype = initial(E.projectile_type) + +/mob/living/simple_animal/hostile/mimic/copy/ranged/OpenFire(the_target) + if(Zapgun) + if(Zapgun.cell) + var/obj/item/ammo_casing/energy/shot = Zapgun.ammo_type[Zapgun.select] + if(Zapgun.cell.charge >= shot.e_cost) + Zapgun.cell.use(shot.e_cost) + Zapgun.update_icon() + ..() + else if(Zapstick) + if(Zapstick.charges) + Zapstick.charges-- + Zapstick.update_icon() + ..() + else if(Pewgun) + if(Pewgun.chambered) + if(Pewgun.chambered.BB) + qdel(Pewgun.chambered.BB) + Pewgun.chambered.BB = null //because qdel takes too long, ensures icon update + Pewgun.chambered.update_icon() + ..() + else + visible_message("The [src] clears a jam!") + Pewgun.chambered.loc = loc //rip revolver immersions, blame shotgun snowflake procs + Pewgun.chambered = null + if(Pewgun.magazine && Pewgun.magazine.stored_ammo.len) + Pewgun.chambered = Pewgun.magazine.get_round(0) + Pewgun.chambered.loc = Pewgun + Pewgun.update_icon() + else if(Pewgun.magazine && Pewgun.magazine.stored_ammo.len) //only true for pumpguns i think + Pewgun.chambered = Pewgun.magazine.get_round(0) + Pewgun.chambered.loc = Pewgun + visible_message("The [src] cocks itself!") + else + ranged = 0 //BANZAIIII + retreat_distance = 0 + minimum_distance = 1 + return + icon_state = TrueGun.icon_state + icon_living = TrueGun.icon_state diff --git a/code/modules/mob/living/simple_animal/hostile/mining/basilisk.dm b/code/modules/mob/living/simple_animal/hostile/mining/basilisk.dm index ef6e3c540f6..74eaf55b57c 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/basilisk.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/basilisk.dm @@ -149,4 +149,4 @@ L.apply_status_effect(/datum/status_effect/freon/watcher) /mob/living/simple_animal/hostile/asteroid/basilisk/watcher/tendril - fromtendril = TRUE \ No newline at end of file + fromtendril = TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/mining/gutlunch.dm b/code/modules/mob/living/simple_animal/hostile/mining/gutlunch.dm index 463b6e61e48..2f896f4adb0 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/gutlunch.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/gutlunch.dm @@ -163,4 +163,4 @@ L.faction = faction.Copy() L.setDir(dir) visible_message("[src] grows up into [L].") - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm b/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm index 080623f1b0d..3d7da9d0fcc 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm @@ -141,7 +141,7 @@ volume = min(volume, reagents.total_volume) var/fraction = min(volume/reagents.total_volume, 1) - reagents.reaction(C, INGEST, fraction) + reagents.reaction(C, REAGENT_INGEST, fraction) reagents.trans_to(C, volume) /mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/proc/link_host(mob/living/carbon/C) diff --git a/code/modules/mob/living/simple_animal/hostile/mining/mining.dm b/code/modules/mob/living/simple_animal/hostile/mining/mining.dm index e1e86536f19..7c7200e8588 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/mining.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/mining.dm @@ -63,4 +63,4 @@ ..(gibbed) /mob/living/simple_animal/hostile/asteroid/proc/spawn_crusher_loot() - butcher_results[crusher_loot] = 1 \ No newline at end of file + butcher_results[crusher_loot] = 1 diff --git a/code/modules/mob/living/simple_animal/hostile/netherworld.dm b/code/modules/mob/living/simple_animal/hostile/netherworld.dm index 92c01d7d72b..e8ce4027b4e 100644 --- a/code/modules/mob/living/simple_animal/hostile/netherworld.dm +++ b/code/modules/mob/living/simple_animal/hostile/netherworld.dm @@ -108,4 +108,4 @@ blank.name = "[M]" blank.desc = "It's [M], but [M.p_their()] flesh has an ashy texture, and [M.p_their()] face is featureless save an eerie smile." visible_message("[M] reemerges from the link!") - qdel(M) \ No newline at end of file + qdel(M) diff --git a/code/modules/mob/living/simple_animal/hostile/pirate.dm b/code/modules/mob/living/simple_animal/hostile/pirate.dm index 4e3f71b30b1..d8d2d55a8ee 100644 --- a/code/modules/mob/living/simple_animal/hostile/pirate.dm +++ b/code/modules/mob/living/simple_animal/hostile/pirate.dm @@ -1,45 +1,45 @@ -/mob/living/simple_animal/hostile/pirate - name = "Pirate" - desc = "Does what he wants cause a pirate is free." - icon = 'icons/mob/simple_human.dmi' - icon_state = "piratemelee" - icon_living = "piratemelee" - icon_dead = "piratemelee_dead" // Does not actually exist. del_on_death. - speak_chance = 0 - turns_per_move = 5 - response_help = "pushes the" - response_disarm = "shoves" - response_harm = "hits the" - speed = 0 - maxHealth = 100 - health = 100 - - harm_intent_damage = 5 - obj_damage = 60 - melee_damage_lower = 30 - melee_damage_upper = 30 - attacktext = "slashes" - attack_sound = 'sound/weapons/bladeslice.ogg' - - atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - unsuitable_atmos_damage = 15 - speak_emote = list("yarrs") - loot = list(/obj/effect/mob_spawn/human/corpse/pirate, - /obj/item/melee/energy/sword/pirate) - del_on_death = 1 - faction = list("pirate") - sentience_type = SENTIENCE_OTHER - -/mob/living/simple_animal/hostile/pirate/ranged - name = "Pirate Gunner" - icon_state = "pirateranged" - icon_living = "pirateranged" - icon_dead = "piratemelee_dead" // Does not actually exist. del_on_death. - projectilesound = 'sound/weapons/laser.ogg' - ranged = 1 - rapid = 2 - retreat_distance = 5 - minimum_distance = 5 - projectiletype = /obj/item/projectile/beam - loot = list(/obj/effect/mob_spawn/human/corpse/pirate/ranged, - /obj/item/gun/energy/laser) \ No newline at end of file +/mob/living/simple_animal/hostile/pirate + name = "Pirate" + desc = "Does what he wants cause a pirate is free." + icon = 'icons/mob/simple_human.dmi' + icon_state = "piratemelee" + icon_living = "piratemelee" + icon_dead = "piratemelee_dead" // Does not actually exist. del_on_death. + speak_chance = 0 + turns_per_move = 5 + response_help = "pushes the" + response_disarm = "shoves" + response_harm = "hits the" + speed = 0 + maxHealth = 100 + health = 100 + + harm_intent_damage = 5 + obj_damage = 60 + melee_damage_lower = 30 + melee_damage_upper = 30 + attacktext = "slashes" + attack_sound = 'sound/weapons/bladeslice.ogg' + + atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + unsuitable_atmos_damage = 15 + speak_emote = list("yarrs") + loot = list(/obj/effect/mob_spawn/human/corpse/pirate, + /obj/item/melee/energy/sword/pirate) + del_on_death = 1 + faction = list("pirate") + sentience_type = SENTIENCE_OTHER + +/mob/living/simple_animal/hostile/pirate/ranged + name = "Pirate Gunner" + icon_state = "pirateranged" + icon_living = "pirateranged" + icon_dead = "piratemelee_dead" // Does not actually exist. del_on_death. + projectilesound = 'sound/weapons/laser.ogg' + ranged = 1 + rapid = 2 + retreat_distance = 5 + minimum_distance = 5 + projectiletype = /obj/item/projectile/beam + loot = list(/obj/effect/mob_spawn/human/corpse/pirate/ranged, + /obj/item/gun/energy/laser) diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm index 5a278a65b24..f58f7fcfda2 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/clown.dm @@ -1,53 +1,53 @@ -/mob/living/simple_animal/hostile/retaliate/clown - name = "Clown" - desc = "A denizen of clown planet" - icon = 'icons/mob/simple_human.dmi' - icon_state = "clown" - icon_living = "clown" - icon_dead = "clown_dead" - icon_gib = "clown_gib" - speak_chance = 0 - turns_per_move = 5 - response_help = "pokes the" - response_disarm = "gently pushes aside the" - response_harm = "hits the" - speak = list("HONK", "Honk!", "Welcome to clown planet!") - emote_see = list("honks") - speak_chance = 1 - a_intent = INTENT_HARM - maxHealth = 75 - health = 75 - speed = 0 - harm_intent_damage = 8 - melee_damage_lower = 10 - melee_damage_upper = 10 - attacktext = "attacks" - attack_sound = 'sound/items/bikehorn.ogg' - obj_damage = 0 - environment_smash = 0 - minbodytemp = 270 - maxbodytemp = 370 - heat_damage_per_tick = 15 //amount of damage applied if animal's body temperature is higher than maxbodytemp - cold_damage_per_tick = 10 //same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp - unsuitable_atmos_damage = 10 - - -/mob/living/simple_animal/hostile/retaliate/clown/goblin - icon = 'icons/mob/animal.dmi' - name = "clown goblin" - desc = "A tiny walking mask and clown shoes. You want to honk his nose!" - icon_state = "clowngoblin" - icon_living = "clowngoblin" - icon_dead = null - response_help = "honks the" - speak = list("Honk!") - speak_emote = list("sqeaks") - emote_see = list("honks") - maxHealth = 100 - health = 100 - - speed = -1 - turns_per_move = 1 - - del_on_death = TRUE - loot = list(/obj/item/clothing/mask/gas/clown_hat, /obj/item/clothing/shoes/clown_shoes) +/mob/living/simple_animal/hostile/retaliate/clown + name = "Clown" + desc = "A denizen of clown planet" + icon = 'icons/mob/simple_human.dmi' + icon_state = "clown" + icon_living = "clown" + icon_dead = "clown_dead" + icon_gib = "clown_gib" + speak_chance = 0 + turns_per_move = 5 + response_help = "pokes the" + response_disarm = "gently pushes aside the" + response_harm = "hits the" + speak = list("HONK", "Honk!", "Welcome to clown planet!") + emote_see = list("honks") + speak_chance = 1 + a_intent = INTENT_HARM + maxHealth = 75 + health = 75 + speed = 0 + harm_intent_damage = 8 + melee_damage_lower = 10 + melee_damage_upper = 10 + attacktext = "attacks" + attack_sound = 'sound/items/bikehorn.ogg' + obj_damage = 0 + environment_smash = 0 + minbodytemp = 270 + maxbodytemp = 370 + heat_damage_per_tick = 15 //amount of damage applied if animal's body temperature is higher than maxbodytemp + cold_damage_per_tick = 10 //same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp + unsuitable_atmos_damage = 10 + + +/mob/living/simple_animal/hostile/retaliate/clown/goblin + icon = 'icons/mob/animal.dmi' + name = "clown goblin" + desc = "A tiny walking mask and clown shoes. You want to honk his nose!" + icon_state = "clowngoblin" + icon_living = "clowngoblin" + icon_dead = null + response_help = "honks the" + speak = list("Honk!") + speak_emote = list("sqeaks") + emote_see = list("honks") + maxHealth = 100 + health = 100 + + speed = -1 + turns_per_move = 1 + + del_on_death = TRUE + loot = list(/obj/item/clothing/mask/gas/clown_hat, /obj/item/clothing/shoes/clown_shoes) diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm index d3bf35671e2..7afaf3679c2 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/retaliate.dm @@ -1,56 +1,56 @@ -/mob/living/simple_animal/hostile/retaliate - var/list/enemies = list() - -/mob/living/simple_animal/hostile/retaliate/Found(atom/A) - if(isliving(A)) - var/mob/living/L = A - if(!L.stat) - return L - else - enemies -= L - else if(ismecha(A)) - var/obj/mecha/M = A - if(M.occupant) - return A - else if(isspacepod(A)) - var/obj/spacepod/S = A - if(S.pilot) - return A - -/mob/living/simple_animal/hostile/retaliate/ListTargets() - if(!enemies.len) - return list() - var/list/see = ..() - see &= enemies // Remove all entries that aren't in enemies - return see - -/mob/living/simple_animal/hostile/retaliate/proc/Retaliate() - var/list/around = view(src, vision_range) - - for(var/atom/movable/A in around) - if(A == src) - continue - if(isliving(A)) - var/mob/living/M = A - if(faction_check_mob(M) && attack_same || !faction_check_mob(M)) - enemies |= M - else if(ismecha(A)) - var/obj/mecha/M = A - if(M.occupant) - enemies |= M - enemies |= M.occupant - else if(isspacepod(A)) - var/obj/spacepod/S = A - if(S.pilot) - enemies |= S - enemies |= S.pilot - - for(var/mob/living/simple_animal/hostile/retaliate/H in around) - if(faction_check_mob(H) && !attack_same && !H.attack_same) - H.enemies |= enemies - return 0 - -/mob/living/simple_animal/hostile/retaliate/adjustHealth(amount, updating_health = TRUE) - . = ..() - if(amount > 0 && stat == CONSCIOUS) - Retaliate() \ No newline at end of file +/mob/living/simple_animal/hostile/retaliate + var/list/enemies = list() + +/mob/living/simple_animal/hostile/retaliate/Found(atom/A) + if(isliving(A)) + var/mob/living/L = A + if(!L.stat) + return L + else + enemies -= L + else if(ismecha(A)) + var/obj/mecha/M = A + if(M.occupant) + return A + else if(isspacepod(A)) + var/obj/spacepod/S = A + if(S.pilot) + return A + +/mob/living/simple_animal/hostile/retaliate/ListTargets() + if(!enemies.len) + return list() + var/list/see = ..() + see &= enemies // Remove all entries that aren't in enemies + return see + +/mob/living/simple_animal/hostile/retaliate/proc/Retaliate() + var/list/around = view(src, vision_range) + + for(var/atom/movable/A in around) + if(A == src) + continue + if(isliving(A)) + var/mob/living/M = A + if(faction_check_mob(M) && attack_same || !faction_check_mob(M)) + enemies |= M + else if(ismecha(A)) + var/obj/mecha/M = A + if(M.occupant) + enemies |= M + enemies |= M.occupant + else if(isspacepod(A)) + var/obj/spacepod/S = A + if(S.pilot) + enemies |= S + enemies |= S.pilot + + for(var/mob/living/simple_animal/hostile/retaliate/H in around) + if(faction_check_mob(H) && !attack_same && !H.attack_same) + H.enemies |= enemies + return 0 + +/mob/living/simple_animal/hostile/retaliate/adjustHealth(amount, updating_health = TRUE) + . = ..() + if(amount > 0 && stat == CONSCIOUS) + Retaliate() diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/undead.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/undead.dm index 6e05b699ea5..a3e32e9d35e 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/undead.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/undead.dm @@ -116,4 +116,4 @@ faction = list("undead") loot = list(/obj/effect/decal/cleanable/blood/gibs) - del_on_death = 1 \ No newline at end of file + del_on_death = 1 diff --git a/code/modules/mob/living/simple_animal/hostile/russian.dm b/code/modules/mob/living/simple_animal/hostile/russian.dm index 37ae67cfef5..83c6fa67e1d 100644 --- a/code/modules/mob/living/simple_animal/hostile/russian.dm +++ b/code/modules/mob/living/simple_animal/hostile/russian.dm @@ -1,44 +1,44 @@ -/mob/living/simple_animal/hostile/russian - name = "Russian" - desc = "For the Motherland!" - icon = 'icons/mob/simple_human.dmi' - icon_state = "russianmelee" - icon_living = "russianmelee" - icon_dead = "russianmelee_dead" // Does not actually exist. del_on_death. - icon_gib = "russianmelee_gib" // Does not actually exist. del_on_death. - speak_chance = 0 - turns_per_move = 5 - response_help = "pokes the" - response_disarm = "shoves the" - response_harm = "hits the" - speed = 0 - maxHealth = 100 - health = 100 - harm_intent_damage = 5 - melee_damage_lower = 15 - melee_damage_upper = 15 - attacktext = "punches" - attack_sound = 'sound/weapons/punch1.ogg' - a_intent = INTENT_HARM - unsuitable_atmos_damage = 15 - faction = list("russian") - status_flags = CANPUSH - loot = list(/obj/effect/mob_spawn/human/corpse/russian, - /obj/item/kitchen/knife) - del_on_death = 1 - sentience_type = SENTIENCE_OTHER - -/mob/living/simple_animal/hostile/russian/ranged - icon_state = "russianranged" - icon_living = "russianranged" - ranged = 1 - retreat_distance = 5 - minimum_distance = 5 - projectilesound = 'sound/weapons/gunshots/gunshot.ogg' - casingtype = /obj/item/ammo_casing/a357 - loot = list(/obj/effect/mob_spawn/human/corpse/russian/ranged, /obj/item/gun/projectile/revolver/mateba) - -/mob/living/simple_animal/hostile/russian/ranged/mosin - loot = list(/obj/effect/mob_spawn/human/corpse/russian/ranged, - /obj/item/gun/projectile/shotgun/boltaction) - casingtype = /obj/item/ammo_casing/a762 +/mob/living/simple_animal/hostile/russian + name = "Russian" + desc = "For the Motherland!" + icon = 'icons/mob/simple_human.dmi' + icon_state = "russianmelee" + icon_living = "russianmelee" + icon_dead = "russianmelee_dead" // Does not actually exist. del_on_death. + icon_gib = "russianmelee_gib" // Does not actually exist. del_on_death. + speak_chance = 0 + turns_per_move = 5 + response_help = "pokes the" + response_disarm = "shoves the" + response_harm = "hits the" + speed = 0 + maxHealth = 100 + health = 100 + harm_intent_damage = 5 + melee_damage_lower = 15 + melee_damage_upper = 15 + attacktext = "punches" + attack_sound = 'sound/weapons/punch1.ogg' + a_intent = INTENT_HARM + unsuitable_atmos_damage = 15 + faction = list("russian") + status_flags = CANPUSH + loot = list(/obj/effect/mob_spawn/human/corpse/russian, + /obj/item/kitchen/knife) + del_on_death = 1 + sentience_type = SENTIENCE_OTHER + +/mob/living/simple_animal/hostile/russian/ranged + icon_state = "russianranged" + icon_living = "russianranged" + ranged = 1 + retreat_distance = 5 + minimum_distance = 5 + projectilesound = 'sound/weapons/gunshots/gunshot.ogg' + casingtype = /obj/item/ammo_casing/a357 + loot = list(/obj/effect/mob_spawn/human/corpse/russian/ranged, /obj/item/gun/projectile/revolver/mateba) + +/mob/living/simple_animal/hostile/russian/ranged/mosin + loot = list(/obj/effect/mob_spawn/human/corpse/russian/ranged, + /obj/item/gun/projectile/shotgun/boltaction) + casingtype = /obj/item/ammo_casing/a762 diff --git a/code/modules/mob/living/simple_animal/hostile/skeleton.dm b/code/modules/mob/living/simple_animal/hostile/skeleton.dm index acfd9050c04..e0e71506bbe 100644 --- a/code/modules/mob/living/simple_animal/hostile/skeleton.dm +++ b/code/modules/mob/living/simple_animal/hostile/skeleton.dm @@ -46,4 +46,4 @@ loot = list(/obj/effect/decal/remains/human, /obj/item/twohanded/spear, /obj/item/clothing/shoes/winterboots, - /obj/item/clothing/suit/hooded/wintercoat) \ No newline at end of file + /obj/item/clothing/suit/hooded/wintercoat) diff --git a/code/modules/mob/living/simple_animal/hostile/syndicate.dm b/code/modules/mob/living/simple_animal/hostile/syndicate.dm index 02d5ee5d14b..f3389108498 100644 --- a/code/modules/mob/living/simple_animal/hostile/syndicate.dm +++ b/code/modules/mob/living/simple_animal/hostile/syndicate.dm @@ -1,361 +1,367 @@ -/mob/living/simple_animal/hostile/syndicate - name = "Syndicate Operative" - desc = "Death to Nanotrasen." - icon = 'icons/mob/simple_human.dmi' - icon_state = "syndicate" - icon_living = "syndicate" - icon_dead = "syndicate_dead" // Does not actually exist. del_on_death. - icon_gib = "syndicate_gib" // Does not actually exist. del_on_death. - speak_chance = 0 - turns_per_move = 5 - response_help = "pokes the" - response_disarm = "shoves the" - response_harm = "hits the" - speed = 0 - maxHealth = 100 - health = 100 - harm_intent_damage = 5 - melee_damage_lower = 10 - melee_damage_upper = 10 - attacktext = "punches" - attack_sound = 'sound/weapons/punch1.ogg' - a_intent = INTENT_HARM - unsuitable_atmos_damage = 15 - faction = list("syndicate") - check_friendly_fire = 1 - status_flags = CANPUSH - loot = list(/obj/effect/mob_spawn/human/corpse/syndicatesoldier) - del_on_death = 1 - sentience_type = SENTIENCE_OTHER - -///////////////Sword and shield//////////// - -/mob/living/simple_animal/hostile/syndicate/melee - melee_damage_lower = 20 - melee_damage_upper = 25 - icon_state = "syndicate_sword" - icon_living = "syndicate_sword" - attacktext = "slashes" - attack_sound = 'sound/weapons/bladeslice.ogg' - armour_penetration = 28 - status_flags = 0 - loot = list(/obj/effect/mob_spawn/human/corpse/syndicatesoldier, /obj/item/melee/energy/sword/saber/red, /obj/item/shield/energy) - var/melee_block_chance = 20 - var/ranged_block_chance = 35 - -/mob/living/simple_animal/hostile/syndicate/melee/attackby(var/obj/item/O as obj, var/mob/user as mob, params) - user.changeNext_move(CLICK_CD_MELEE) - user.do_attack_animation(src) - if(O.force) - if(prob(melee_block_chance)) - visible_message("[src] blocks the [O] with its shield! ") - else - var/damage = O.force - if(O.damtype == STAMINA) - damage = 0 - if(force_threshold && damage < force_threshold) - visible_message("[src] is unharmed by [O]!") - return - adjustHealth(damage) - visible_message("[src] has been attacked with the [O] by [user]. ") - playsound(loc, O.hitsound, 25, 1, -1) - else - to_chat(usr, "This weapon is ineffective, it does no damage.") - visible_message("[user] gently taps [src] with the [O]. ") - - -/mob/living/simple_animal/hostile/syndicate/melee/bullet_act(var/obj/item/projectile/Proj) - if(!Proj) - return - if(prob(ranged_block_chance)) - visible_message("[src] blocks [Proj] with its shield!") - else - if((Proj.damage_type == BRUTE || Proj.damage_type == BURN)) - adjustHealth(Proj.damage) - return 0 - -/mob/living/simple_animal/hostile/syndicate/melee/autogib - loot = list()//no loot, its gonna delete and gib. - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot - name = "Syndicate Operative" - force_threshold = 6 // Prevents people using punches to bypass eshield - robust_searching = 1 // Together with stat_attack, ensures dionae/etc that regen are killed properly - stat_attack = UNCONSCIOUS - universal_speak = 1 - icon_state = "syndicate_swordonly" - icon_living = "syndicate_swordonly" - melee_block_chance = 0 - ranged_block_chance = 0 - del_on_death = 1 - var/area/syndicate_depot/core/depotarea - var/raised_alert = FALSE - var/alert_on_death = FALSE - var/alert_on_timeout = TRUE - var/alert_on_spacing = TRUE - var/alert_on_shield_breach = FALSE - var/seen_enemy = FALSE - var/seen_enemy_name = null - var/seen_revived_enemy = FALSE - var/aggro_cycles = 0 - var/scan_cycles = 0 - var/shield_key = FALSE - var/turf/spawn_turf - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/New() - ..() - name = "[name] [pick(GLOB.last_names)]" - // Do not attempt to move this code to Initialize() or LateInitialize(). Doing so with other objects has caused bugs in the past, because assigning "depotarea" may not work there. - depotarea = areaMaster - spawn_turf = get_turf(src) - - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/ListTargetsLazy() - // The normal ListTargetsLazy ignores walls, which is very bad in the case of depot mobs. So we override it. - return ListTargets() - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/Aggro() - . = ..() - if(!istype(depotarea)) - return - if(target) - if(!seen_enemy) - seen_enemy = TRUE - if(!ranged) - playsound(loc, 'sound/weapons/saberon.ogg', 35, 1) - if(alert_on_shield_breach) - if(depotarea.shield_list.len) - raise_alert("[name] reports that [target] is trying to breach the armory shield!") - alert_on_shield_breach = FALSE - raised_alert = FALSE - alert_on_death = TRUE - if(isliving(target)) - var/mob/living/M = target - depotarea.list_add(M, depotarea.hostile_list) - if(M.mind && M.mind.special_role == SPECIAL_ROLE_TRAITOR) - depotarea.saw_double_agent(M) - depotarea.declare_started() - seen_enemy_name = target.name - if(istype(target, /obj/mecha)) - depotarea.saw_mech(target) - if(istype(target, /obj/spacepod)) - depotarea.saw_pod(target) - if(depotarea.list_includes(target, depotarea.dead_list)) - seen_revived_enemy = TRUE - raise_alert("[name] reports intruder [target] has returned from death!") - depotarea.list_remove(target, depotarea.dead_list) - if(!atoms_share_level(src, target) && prob(20)) - // This prevents someone from aggroing a depot mob, then hiding in a locker, perfectly safe, while the mob stands there getting killed by their friends. - LoseTarget() - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/handle_automated_movement() - . = ..() - if(!istype(depotarea)) - return - if(seen_enemy) - aggro_cycles++ - if(alert_on_timeout && !raised_alert && aggro_cycles >= 60) - raise_alert("[name] has reported contact with hostile entity: [seen_enemy_name]") - if(scan_cycles >= 15) - scan_cycles = 0 - if(!atoms_share_level(src, spawn_turf)) - if(istype(loc, /obj/structure/closet)) - var/obj/structure/closet/O = loc - forceMove(get_turf(src)) - visible_message("[src] smashes their way out of [O]!") - qdel(O) - raise_alert("[src] reported being trapped in a locker.") - raised_alert = FALSE - return - if(alert_on_spacing) - raise_alert("[src] lost in space.") - death() - return - for(var/mob/living/body in hearers(vision_range, targets_from)) - if(body.stat != DEAD) - continue - if(depotarea.list_includes(body, depotarea.dead_list)) - continue - if(faction_check_mob(body)) - continue - say("Target [body]... terminated.") - depotarea.list_add(body, depotarea.dead_list) - pointed(body) - else - scan_cycles++ - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/proc/raise_alert(var/reason) - if(istype(depotarea) && (!raised_alert || seen_revived_enemy) && !depotarea.used_self_destruct) - raised_alert = TRUE - say("Intruder!") - depotarea.increase_alert(reason) - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/death() - if(!istype(depotarea)) - return ..() - if(alert_on_death) - if(seen_enemy_name) - raise_alert("[name] has died in combat with [seen_enemy_name].") - else - raise_alert("[name] has died.") - if(shield_key && depotarea) - depotarea.shields_key_check() - if(depotarea) - depotarea.list_remove(src, depotarea.guard_list) - new /obj/effect/gibspawner/human(get_turf(src)) - return ..() - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/CanPass(atom/movable/mover, turf/target, height=0) - if(isliving(mover)) - var/mob/living/blocker = mover - if(faction_check_mob(blocker)) - return 1 - return ..(mover, target, height) - - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/officer - name = "Syndicate Officer" - icon_state = "syndicate_sword" - icon_living = "syndicate_sword" - melee_block_chance = 20 - ranged_block_chance = 35 - alert_on_death = TRUE - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/officer/Initialize(mapload) - . = ..() - if(prob(50)) - // 50% chance of switching to ranged variant. - // Designed to counter players taking cover behind reinforced plasmasglass. - // Does almost no danage in melee, but decent damage at range, and its shots go through glass. - melee_damage_lower = 10 - melee_damage_upper = 10 - attacktext = "punches" - attack_sound = 'sound/weapons/punch1.ogg' - ranged = 1 - rapid = 3 - retreat_distance = 3 - minimum_distance = 3 - melee_block_chance = 0 - ranged_block_chance = 0 - icon_state = "syndicate_pistol" - icon_living = "syndicate_pistol" - projectiletype = /obj/item/projectile/beam/laser - projectilesound = 'sound/weapons/laser.ogg' - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/armory - name = "Syndicate Quartermaster" - icon_state = "syndicate_stormtrooper_sword" - icon_living = "syndicate_stormtrooper_sword" - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - maxHealth = 200 - health = 200 - melee_block_chance = 40 - alert_on_shield_breach = TRUE - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/armory/Initialize(mapload) - ..() - if(prob(50)) - // 50% chance of switching to extremely dangerous ranged variant - melee_damage_lower = 10 - melee_damage_upper = 10 - attacktext = "punches" - attack_sound = 'sound/weapons/punch1.ogg' - ranged = 1 - retreat_distance = 3 - minimum_distance = 3 - melee_block_chance = 0 - ranged_block_chance = 0 - icon_state = "syndicate_stormtrooper_shotgun" - icon_living = "syndicate_stormtrooper_shotgun" - projectiletype = /obj/item/projectile/bullet/sniper/penetrator // Ignores cover. - projectilesound = 'sound/weapons/gunshots/gunshot_sniper.ogg' - return INITIALIZE_HINT_LATELOAD - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/armory/LateInitialize() - if(istype(depotarea)) - var/list/key_candidates = list() - for(var/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/officer/O in GLOB.living_mob_list) - key_candidates += O - if(key_candidates.len) - var/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/officer/O = pick(key_candidates) - O.shield_key = TRUE - depotarea.shields_up() - - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/space - name = "Syndicate Backup" - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - icon_state = "syndicate_space_sword" - icon_living = "syndicate_space_sword" - speed = 1 - wander = 0 - alert_on_spacing = FALSE - -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/space/Process_Spacemove(var/movement_dir = 0) - return TRUE - - - -/mob/living/simple_animal/hostile/syndicate/melee/space - name = "Syndicate Commando" - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - icon_state = "syndicate_space_sword" - icon_living = "syndicate_space_sword" - speed = 1 - loot = list(/obj/effect/mob_spawn/human/corpse/syndicatecommando, /obj/item/melee/energy/sword/saber/red, /obj/item/shield/energy) - -/mob/living/simple_animal/hostile/syndicate/melee/space/Process_Spacemove(var/movement_dir = 0) - return TRUE - - -/mob/living/simple_animal/hostile/syndicate/ranged - ranged = 1 - rapid = 2 - retreat_distance = 5 - minimum_distance = 5 - icon_state = "syndicate_smg" - icon_living = "syndicate_smg" - projectilesound = 'sound/weapons/gunshots/gunshot.ogg' - casingtype = /obj/item/ammo_casing/c45 - loot = list(/obj/effect/mob_spawn/human/corpse/syndicatesoldier, /obj/item/gun/projectile/automatic/c20r) - -/mob/living/simple_animal/hostile/syndicate/ranged/space - icon_state = "syndicate_space_smg" - icon_living = "syndicate_space_smg" - name = "Syndicate Commando" - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - speed = 1 - loot = list(/obj/effect/mob_spawn/human/corpse/syndicatecommando, /obj/item/gun/projectile/automatic/c20r) - -/mob/living/simple_animal/hostile/syndicate/ranged/space/Process_Spacemove(var/movement_dir = 0) - return TRUE - -/mob/living/simple_animal/hostile/syndicate/ranged/space/autogib - loot = list()//gonna gibe, no loot. - -/mob/living/simple_animal/hostile/viscerator - name = "viscerator" - desc = "A small, twin-bladed machine capable of inflicting very deadly lacerations." - icon = 'icons/mob/critter.dmi' - icon_state = "viscerator_attack" - icon_living = "viscerator_attack" - pass_flags = PASSTABLE - health = 15 - maxHealth = 15 - obj_damage = 0 - melee_damage_lower = 15 - melee_damage_upper = 15 - attacktext = "cuts" - attack_sound = 'sound/weapons/bladeslice.ogg' - faction = list("syndicate") - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - mob_size = MOB_SIZE_TINY - flying = 1 - gold_core_spawnable = HOSTILE_SPAWN - del_on_death = 1 - deathmessage = "is smashed into pieces!" \ No newline at end of file +/mob/living/simple_animal/hostile/syndicate + name = "Syndicate Operative" + desc = "Death to Nanotrasen." + icon = 'icons/mob/simple_human.dmi' + icon_state = "syndicate" + icon_living = "syndicate" + icon_dead = "syndicate_dead" // Does not actually exist. del_on_death. + icon_gib = "syndicate_gib" // Does not actually exist. del_on_death. + speak_chance = 0 + turns_per_move = 5 + response_help = "pokes the" + response_disarm = "shoves the" + response_harm = "hits the" + speed = 0 + maxHealth = 100 + health = 100 + harm_intent_damage = 5 + melee_damage_lower = 10 + melee_damage_upper = 10 + attacktext = "punches" + attack_sound = 'sound/weapons/punch1.ogg' + a_intent = INTENT_HARM + unsuitable_atmos_damage = 15 + faction = list("syndicate") + check_friendly_fire = 1 + status_flags = CANPUSH + loot = list(/obj/effect/mob_spawn/human/corpse/syndicatesoldier) + del_on_death = 1 + sentience_type = SENTIENCE_OTHER + +///////////////Sword and shield//////////// + +/mob/living/simple_animal/hostile/syndicate/melee + melee_damage_lower = 20 + melee_damage_upper = 25 + icon_state = "syndicate_sword" + icon_living = "syndicate_sword" + attacktext = "slashes" + attack_sound = 'sound/weapons/bladeslice.ogg' + armour_penetration = 28 + status_flags = 0 + loot = list(/obj/effect/mob_spawn/human/corpse/syndicatesoldier, /obj/item/melee/energy/sword/saber/red, /obj/item/shield/energy) + var/melee_block_chance = 20 + var/ranged_block_chance = 35 + +/mob/living/simple_animal/hostile/syndicate/melee/attackby(var/obj/item/O as obj, var/mob/user as mob, params) + user.changeNext_move(CLICK_CD_MELEE) + user.do_attack_animation(src) + if(O.force) + if(prob(melee_block_chance)) + visible_message("[src] blocks the [O] with its shield! ") + else + var/damage = O.force + if(O.damtype == STAMINA) + damage = 0 + if(force_threshold && damage < force_threshold) + visible_message("[src] is unharmed by [O]!") + return + adjustHealth(damage) + visible_message("[src] has been attacked with the [O] by [user]. ") + playsound(loc, O.hitsound, 25, 1, -1) + else + to_chat(usr, "This weapon is ineffective, it does no damage.") + visible_message("[user] gently taps [src] with the [O]. ") + + +/mob/living/simple_animal/hostile/syndicate/melee/bullet_act(var/obj/item/projectile/Proj) + if(!Proj) + return + if(prob(ranged_block_chance)) + visible_message("[src] blocks [Proj] with its shield!") + else + if((Proj.damage_type == BRUTE || Proj.damage_type == BURN)) + adjustHealth(Proj.damage) + return 0 + +/mob/living/simple_animal/hostile/syndicate/melee/autogib + loot = list()//no loot, its gonna delete and gib. + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot + name = "Syndicate Operative" + force_threshold = 6 // Prevents people using punches to bypass eshield + robust_searching = 1 // Together with stat_attack, ensures dionae/etc that regen are killed properly + stat_attack = UNCONSCIOUS + universal_speak = 1 + icon_state = "syndicate_swordonly" + icon_living = "syndicate_swordonly" + melee_block_chance = 0 + ranged_block_chance = 0 + del_on_death = 1 + var/area/syndicate_depot/core/depotarea + var/raised_alert = FALSE + var/alert_on_death = FALSE + var/alert_on_timeout = TRUE + var/alert_on_spacing = TRUE + var/alert_on_shield_breach = FALSE + var/seen_enemy = FALSE + var/seen_enemy_name = null + var/seen_revived_enemy = FALSE + var/aggro_cycles = 0 + var/scan_cycles = 0 + var/shield_key = FALSE + var/turf/spawn_turf + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/New() + ..() + name = "[name] [pick(GLOB.last_names)]" + // Do not attempt to move this code to Initialize() or LateInitialize(). Doing so with other objects has caused bugs in the past, because assigning "depotarea" may not work there. + depotarea = areaMaster + spawn_turf = get_turf(src) + + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/ListTargetsLazy() + // The normal ListTargetsLazy ignores walls, which is very bad in the case of depot mobs. So we override it. + return ListTargets() + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/Aggro() + . = ..() + if(!istype(depotarea)) + return + if(target) + if(!seen_enemy) + seen_enemy = TRUE + if(!ranged) + playsound(loc, 'sound/weapons/saberon.ogg', 35, 1) + if(alert_on_shield_breach) + if(depotarea.shield_list.len) + raise_alert("[name] reports that [target] is trying to breach the armory shield!") + alert_on_shield_breach = FALSE + raised_alert = FALSE + alert_on_death = TRUE + if(isliving(target)) + var/mob/living/M = target + depotarea.list_add(M, depotarea.hostile_list) + if(M.mind && M.mind.special_role == SPECIAL_ROLE_TRAITOR) + depotarea.saw_double_agent(M) + depotarea.declare_started() + seen_enemy_name = target.name + if(istype(target, /obj/mecha)) + depotarea.saw_mech(target) + if(istype(target, /obj/spacepod)) + depotarea.saw_pod(target) + if(depotarea.list_includes(target, depotarea.dead_list)) + seen_revived_enemy = TRUE + raise_alert("[name] reports intruder [target] has returned from death!") + depotarea.list_remove(target, depotarea.dead_list) + if(!atoms_share_level(src, target) && prob(20)) + // This prevents someone from aggroing a depot mob, then hiding in a locker, perfectly safe, while the mob stands there getting killed by their friends. + LoseTarget() + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/handle_automated_action() + . = ..() + if(!.) + return + if(!istype(depotarea)) + return + if(seen_enemy) + aggro_cycles++ + if(alert_on_timeout && !raised_alert && aggro_cycles >= 60) + raise_alert("[name] has reported contact with hostile entity: [seen_enemy_name]") + if(scan_cycles >= 15) + scan_cycles = 0 + if(!atoms_share_level(src, spawn_turf)) + if(istype(loc, /obj/structure/closet)) + var/obj/structure/closet/O = loc + forceMove(get_turf(src)) + visible_message("[src] smashes their way out of [O]!") + qdel(O) + raise_alert("[src] reported being trapped in a locker.") + raised_alert = FALSE + return + if(alert_on_spacing) + raise_alert("[src] lost in space.") + death() + return + for(var/mob/living/body in hearers(vision_range, targets_from)) + if(body.stat != DEAD) + continue + if(depotarea.list_includes(body, depotarea.dead_list)) + continue + if(faction_check_mob(body)) + continue + say("Target [body]... terminated.") + depotarea.list_add(body, depotarea.dead_list) + pointed(body) + else + scan_cycles++ + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/AIShouldSleep(var/list/possible_targets) + FindTarget(possible_targets, 1) + return FALSE + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/proc/raise_alert(var/reason) + if(istype(depotarea) && (!raised_alert || seen_revived_enemy) && !depotarea.used_self_destruct) + raised_alert = TRUE + say("Intruder!") + depotarea.increase_alert(reason) + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/death() + if(!istype(depotarea)) + return ..() + if(alert_on_death) + if(seen_enemy_name) + raise_alert("[name] has died in combat with [seen_enemy_name].") + else + raise_alert("[name] has died.") + if(shield_key && depotarea) + depotarea.shields_key_check() + if(depotarea) + depotarea.list_remove(src, depotarea.guard_list) + new /obj/effect/gibspawner/human(get_turf(src)) + return ..() + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/CanPass(atom/movable/mover, turf/target, height=0) + if(isliving(mover)) + var/mob/living/blocker = mover + if(faction_check_mob(blocker)) + return 1 + return ..(mover, target, height) + + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/officer + name = "Syndicate Officer" + icon_state = "syndicate_sword" + icon_living = "syndicate_sword" + melee_block_chance = 20 + ranged_block_chance = 35 + alert_on_death = TRUE + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/officer/Initialize(mapload) + . = ..() + if(prob(50)) + // 50% chance of switching to ranged variant. + // Designed to counter players taking cover behind reinforced plasmasglass. + // Does almost no danage in melee, but decent damage at range, and its shots go through glass. + melee_damage_lower = 10 + melee_damage_upper = 10 + attacktext = "punches" + attack_sound = 'sound/weapons/punch1.ogg' + ranged = 1 + rapid = 3 + retreat_distance = 3 + minimum_distance = 3 + melee_block_chance = 0 + ranged_block_chance = 0 + icon_state = "syndicate_pistol" + icon_living = "syndicate_pistol" + projectiletype = /obj/item/projectile/beam/laser + projectilesound = 'sound/weapons/laser.ogg' + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/armory + name = "Syndicate Quartermaster" + icon_state = "syndicate_stormtrooper_sword" + icon_living = "syndicate_stormtrooper_sword" + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + maxHealth = 200 + health = 200 + melee_block_chance = 40 + alert_on_shield_breach = TRUE + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/armory/Initialize(mapload) + ..() + if(prob(50)) + // 50% chance of switching to extremely dangerous ranged variant + melee_damage_lower = 10 + melee_damage_upper = 10 + attacktext = "punches" + attack_sound = 'sound/weapons/punch1.ogg' + ranged = 1 + retreat_distance = 3 + minimum_distance = 3 + melee_block_chance = 0 + ranged_block_chance = 0 + icon_state = "syndicate_stormtrooper_shotgun" + icon_living = "syndicate_stormtrooper_shotgun" + projectiletype = /obj/item/projectile/bullet/sniper/penetrator // Ignores cover. + projectilesound = 'sound/weapons/gunshots/gunshot_sniper.ogg' + return INITIALIZE_HINT_LATELOAD + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/armory/LateInitialize() + if(istype(depotarea)) + var/list/key_candidates = list() + for(var/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/officer/O in GLOB.living_mob_list) + key_candidates += O + if(key_candidates.len) + var/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/officer/O = pick(key_candidates) + O.shield_key = TRUE + depotarea.shields_up() + + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/space + name = "Syndicate Backup" + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + icon_state = "syndicate_space_sword" + icon_living = "syndicate_space_sword" + speed = 1 + wander = 0 + alert_on_spacing = FALSE + +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/space/Process_Spacemove(var/movement_dir = 0) + return TRUE + + + +/mob/living/simple_animal/hostile/syndicate/melee/space + name = "Syndicate Commando" + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + icon_state = "syndicate_space_sword" + icon_living = "syndicate_space_sword" + speed = 1 + loot = list(/obj/effect/mob_spawn/human/corpse/syndicatecommando, /obj/item/melee/energy/sword/saber/red, /obj/item/shield/energy) + +/mob/living/simple_animal/hostile/syndicate/melee/space/Process_Spacemove(var/movement_dir = 0) + return TRUE + + +/mob/living/simple_animal/hostile/syndicate/ranged + ranged = 1 + rapid = 2 + retreat_distance = 5 + minimum_distance = 5 + icon_state = "syndicate_smg" + icon_living = "syndicate_smg" + projectilesound = 'sound/weapons/gunshots/gunshot.ogg' + casingtype = /obj/item/ammo_casing/c45 + loot = list(/obj/effect/mob_spawn/human/corpse/syndicatesoldier, /obj/item/gun/projectile/automatic/c20r) + +/mob/living/simple_animal/hostile/syndicate/ranged/space + icon_state = "syndicate_space_smg" + icon_living = "syndicate_space_smg" + name = "Syndicate Commando" + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + speed = 1 + loot = list(/obj/effect/mob_spawn/human/corpse/syndicatecommando, /obj/item/gun/projectile/automatic/c20r) + +/mob/living/simple_animal/hostile/syndicate/ranged/space/Process_Spacemove(var/movement_dir = 0) + return TRUE + +/mob/living/simple_animal/hostile/syndicate/ranged/space/autogib + loot = list()//gonna gibe, no loot. + +/mob/living/simple_animal/hostile/viscerator + name = "viscerator" + desc = "A small, twin-bladed machine capable of inflicting very deadly lacerations." + icon = 'icons/mob/critter.dmi' + icon_state = "viscerator_attack" + icon_living = "viscerator_attack" + pass_flags = PASSTABLE + health = 15 + maxHealth = 15 + obj_damage = 0 + melee_damage_lower = 15 + melee_damage_upper = 15 + attacktext = "cuts" + attack_sound = 'sound/weapons/bladeslice.ogg' + faction = list("syndicate") + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + mob_size = MOB_SIZE_TINY + flying = 1 + gold_core_spawnable = HOSTILE_SPAWN + del_on_death = 1 + deathmessage = "is smashed into pieces!" diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/actions.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/actions.dm index b72ec90cf58..637c1833a89 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/actions.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/actions.dm @@ -278,4 +278,4 @@ forceMove(C.loc) C.visible_message("[src] smashes the welded cover off [C]!") return - to_chat(src, "There is no welded vent or scrubber close enough to do this.") \ No newline at end of file + to_chat(src, "There is no welded vent or scrubber close enough to do this.") diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/black.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/black.dm index eae926b88e1..a1bb0fb0ae9 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/black.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/black.dm @@ -56,4 +56,4 @@ var/inject_target = pick("chest","head") if(C.can_inject(null, FALSE, inject_target, FALSE)) to_chat(C, "[src] slices into you!") - C.reagents.add_reagent("terror_black_toxin", 30) \ No newline at end of file + C.reagents.add_reagent("terror_black_toxin", 30) diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/empress.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/empress.dm index fd30532a7bc..f6dffb79642 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/empress.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/empress.dm @@ -110,4 +110,4 @@ /obj/item/projectile/terrorqueenspit/empress damage_type = BURN damage = 30 - bonus_tox = 0 \ No newline at end of file + bonus_tox = 0 diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/ghost.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/ghost.dm index 283cbb6ad0d..24e8012b5ee 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/ghost.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/ghost.dm @@ -43,4 +43,4 @@ return key = user.key for(var/mob/dead/observer/G in GLOB.player_list) - G.show_message("A ghost has taken control of [src]. ([ghost_follow_link(src, ghost=G)]).") \ No newline at end of file + G.show_message("A ghost has taken control of [src]. ([ghost_follow_link(src, ghost=G)]).") diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/gray.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/gray.dm index c06a7c2fee6..544f7201d24 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/gray.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/gray.dm @@ -63,4 +63,4 @@ /obj/structure/spider/terrorweb/gray alpha = 100 name = "transparent web" - desc = "This web is partly transparent, making it harder to see, and easier to get caught by." \ No newline at end of file + desc = "This web is partly transparent, making it harder to see, and easier to get caught by." diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/hive.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/hive.dm index d4b1886e49e..aec7d9bb5ff 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/hive.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/hive.dm @@ -37,4 +37,4 @@ for(var/obj/structure/spider/spiderling/terror_spiderling/L in ts_spiderling_list) if(!L.stillborn && L.grow_as == specific_type && L.z == z) numspiders += 1 - return numspiders \ No newline at end of file + return numspiders diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/purple.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/purple.dm index 92c7688e1e1..7e36b411bae 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/purple.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/purple.dm @@ -94,4 +94,4 @@ name = "thick web" desc = "This web is so thick, most cannot see beyond it." opacity = 1 - max_integrity = 40 \ No newline at end of file + max_integrity = 40 diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen.dm index d7d41912233..cd5322a58a1 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/queen.dm @@ -335,8 +335,8 @@ damage_type = TOX var/bonus_tox = 30 -/obj/item/projectile/terrorqueenspit/on_hit(mob/living/carbon/target) - if(ismob(target)) +/obj/item/projectile/terrorqueenspit/on_hit(mob/living/carbon/target, blocked = 0, hit_zone) + if(ismob(target) && blocked < 100) var/mob/living/L = target if(L.reagents) if(L.can_inject(null, FALSE, "chest", FALSE)) @@ -353,4 +353,4 @@ var/inject_target = pick("chest","head") if(C.can_inject(null, FALSE, inject_target, FALSE)) C.Hallucinate(400) - C.adjustToxLoss(30) \ No newline at end of file + C.adjustToxLoss(30) diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm index 4bba74001fd..0cf8355e06e 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm @@ -243,4 +243,4 @@ S.enemies = enemies if(spider_growinstantly) S.amount_grown = 250 - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/white.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/white.dm index f62167792a3..919f91742cc 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/white.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/white.dm @@ -68,4 +68,4 @@ var/inject_target = pick("chest","head") if(C.can_inject(null, FALSE, inject_target, FALSE)) to_chat(C, "[src] slices into you!") - new /obj/item/organ/internal/body_egg/terror_eggs(C) \ No newline at end of file + new /obj/item/organ/internal/body_egg/terror_eggs(C) diff --git a/code/modules/mob/living/simple_animal/hostile/tree.dm b/code/modules/mob/living/simple_animal/hostile/tree.dm index 510e609756b..6881e3d7b12 100644 --- a/code/modules/mob/living/simple_animal/hostile/tree.dm +++ b/code/modules/mob/living/simple_animal/hostile/tree.dm @@ -1,45 +1,45 @@ -/mob/living/simple_animal/hostile/tree - name = "pine tree" - desc = "A pissed off tree-like alien. It seems annoyed with the festivities..." - icon = 'icons/obj/flora/pinetrees.dmi' - icon_state = "pine_1" - icon_living = "pine_1" - icon_dead = "pine_1" - icon_gib = "pine_1" - speak_chance = 0 - turns_per_move = 5 - response_help = "brushes the" - response_disarm = "pushes the" - response_harm = "hits the" - speed = 1 - maxHealth = 250 - health = 250 - mob_size = MOB_SIZE_LARGE - - pixel_x = -16 - - harm_intent_damage = 5 - melee_damage_lower = 8 - melee_damage_upper = 12 - attacktext = "bites" - attack_sound = 'sound/weapons/bite.ogg' - speak_emote = list("pines") - emote_taunt = list("growls") - taunt_chance = 20 - - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - minbodytemp = 0 - - faction = list("hostile", "winter") - loot = list(/obj/item/stack/sheet/wood) - gold_core_spawnable = HOSTILE_SPAWN - deathmessage = "is hacked into pieces!" - del_on_death = 1 - -/mob/living/simple_animal/hostile/tree/AttackingTarget() - . = ..() - if(. && iscarbon(target)) - var/mob/living/carbon/C = target - if(prob(15)) - C.Weaken(3) - C.visible_message("\the [src] knocks down \the [C]!") \ No newline at end of file +/mob/living/simple_animal/hostile/tree + name = "pine tree" + desc = "A pissed off tree-like alien. It seems annoyed with the festivities..." + icon = 'icons/obj/flora/pinetrees.dmi' + icon_state = "pine_1" + icon_living = "pine_1" + icon_dead = "pine_1" + icon_gib = "pine_1" + speak_chance = 0 + turns_per_move = 5 + response_help = "brushes the" + response_disarm = "pushes the" + response_harm = "hits the" + speed = 1 + maxHealth = 250 + health = 250 + mob_size = MOB_SIZE_LARGE + + pixel_x = -16 + + harm_intent_damage = 5 + melee_damage_lower = 8 + melee_damage_upper = 12 + attacktext = "bites" + attack_sound = 'sound/weapons/bite.ogg' + speak_emote = list("pines") + emote_taunt = list("growls") + taunt_chance = 20 + + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 0 + + faction = list("hostile", "winter") + loot = list(/obj/item/stack/sheet/wood) + gold_core_spawnable = HOSTILE_SPAWN + deathmessage = "is hacked into pieces!" + del_on_death = 1 + +/mob/living/simple_animal/hostile/tree/AttackingTarget() + . = ..() + if(. && iscarbon(target)) + var/mob/living/carbon/C = target + if(prob(15)) + C.Weaken(3) + C.visible_message("\the [src] knocks down \the [C]!") diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index 0cc174827ae..d159f8160c6 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -1,729 +1,729 @@ -/* Parrots! - * Contains - * Defines - * Inventory (headset stuff) - * Attack responces - * AI - * Procs / Verbs (usable by players) - * Sub-types - */ - -/* - * Defines - */ - -//Only a maximum of one action and one intent should be active at any given time. -//Actions -#define PARROT_PERCH 1 //Sitting/sleeping, not moving -#define PARROT_SWOOP 2 //Moving towards or away from a target -#define PARROT_WANDER 4 //Moving without a specific target in mind - -//Intents -#define PARROT_STEAL 8 //Flying towards a target to steal it/from it -#define PARROT_ATTACK 16 //Flying towards a target to attack it -#define PARROT_RETURN 32 //Flying towards its perch -#define PARROT_FLEE 64 //Flying away from its attacker - - -/mob/living/simple_animal/parrot - name = "\improper Parrot" - desc = "The parrot squaks, \"It's a Parrot! BAWWK!\"" - icon = 'icons/mob/animal.dmi' - icon_state = "parrot_fly" - icon_living = "parrot_fly" - icon_dead = "parrot_dead" - pass_flags = PASSTABLE - can_collar = 1 - - var/list/clean_speak = list( - "Hi", - "Hello!", - "Cracker?", - "BAWWWWK george mellons griffing me") - speak_emote = list("squawks","says","yells") - emote_hear = list("squawks","bawks") - emote_see = list("flutters its wings") - - speak_chance = 1//1% (1 in 100) chance every tick; So about once per 150 seconds, assuming an average tick is 1.5s - turns_per_move = 5 - butcher_results = list(/obj/item/reagent_containers/food/snacks/cracker = 3) - - response_help = "pets the" - response_disarm = "gently moves aside the" - response_harm = "swats the" - stop_automated_movement = 1 - universal_speak = 1 - mob_size = MOB_SIZE_SMALL - - var/parrot_state = PARROT_WANDER //Hunt for a perch when created - var/parrot_sleep_max = 25 //The time the parrot sits while perched before looking around. Mosly a way to avoid the parrot's AI in process_ai() being run every single tick. - var/parrot_sleep_dur = 25 //Same as above, this is the var that physically counts down - var/parrot_dam_zone = list("chest", "head", "l_arm", "l_leg", "r_arm", "r_leg") //For humans, select a bodypart to attack - - var/parrot_speed = 5 //"Delay in world ticks between movement." according to byond. Yeah, that's BS but it does directly affect movement. Higher number = slower. - var/parrot_been_shot = 0 //Parrots get a speed bonus after being shot. This will deincrement every process_ai() and at 0 the parrot will return to regular speed. - - var/list/speech_buffer = list() - var/list/available_channels = list() - - //Headset for Poly to yell at engineers :) - var/obj/item/radio/headset/ears = null - - //The thing the parrot is currently interested in. This gets used for items the parrot wants to pick up, mobs it wants to steal from, - //mobs it wants to attack or mobs that have attacked it - var/atom/movable/parrot_interest = null - - //Parrots will generally sit on their pertch unless something catches their eye. - //These vars store their preffered perch and if they dont have one, what they can use as a perch - var/obj/parrot_perch = null - var/obj/desired_perches = list(/obj/structure/computerframe, /obj/structure/displaycase, \ - /obj/structure/filingcabinet, /obj/machinery/teleport, \ - /obj/machinery/suit_storage_unit, /obj/machinery/clonepod, \ - /obj/machinery/dna_scannernew, /obj/machinery/telecomms, \ - /obj/machinery/nuclearbomb, /obj/machinery/particle_accelerator, \ - /obj/machinery/recharge_station, /obj/machinery/smartfridge, \ - /obj/machinery/computer) - - //Parrots are kleptomaniacs. This variable ... stores the item a parrot is holding. - var/obj/item/held_item = null - gold_core_spawnable = FRIENDLY_SPAWN - - -/mob/living/simple_animal/parrot/New() - ..() - GLOB.hear_radio_list += src - if(!ears) - var/headset = pick(/obj/item/radio/headset/headset_sec, \ - /obj/item/radio/headset/headset_eng, \ - /obj/item/radio/headset/headset_med, \ - /obj/item/radio/headset/headset_sci, \ - /obj/item/radio/headset/headset_cargo) - ears = new headset(src) - update_speak() - - parrot_sleep_dur = parrot_sleep_max //In case someone decides to change the max without changing the duration var - - verbs.Add(/mob/living/simple_animal/parrot/proc/steal_from_ground, \ - /mob/living/simple_animal/parrot/proc/steal_from_mob, \ - /mob/living/simple_animal/parrot/verb/drop_held_item_player, \ - /mob/living/simple_animal/parrot/proc/perch_player) - -/mob/living/simple_animal/parrot/Destroy() - GLOB.hear_radio_list -= src - return ..() - -/mob/living/simple_animal/parrot/death(gibbed) - if(can_die()) - if(held_item) - held_item.loc = src.loc - held_item = null - walk(src,0) - return ..() - -/mob/living/simple_animal/parrot/Stat() - ..() - stat("Held Item", held_item) - -/* - * Inventory - */ -/mob/living/simple_animal/parrot/show_inv(mob/user as mob) - user.set_machine(src) - - var/dat = {""} - - dat += "" - if(can_collar) - dat += "" - dat += "" - - dat += {"
        Headset:[(ears && !(ears.flags&ABSTRACT)) ? ears : "Empty"]
         
        Collar:[(pcollar && !(pcollar.flags&ABSTRACT)) ? pcollar : "Empty"]
        - Close - "} - - var/datum/browser/popup = new(user, "mob\ref[src]", "[src]", 440, 500) - popup.set_content(dat) - popup.open() - -/mob/living/simple_animal/parrot/Topic(href, href_list) - - //Can the usr physically do this? - if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) - return - - //Is the usr's mob type able to do this? - if(ishuman(usr) || isrobot(usr)) - if(href_list["remove_inv"]) - var/remove_from = href_list["remove_inv"] - switch(remove_from) - if("ears") - if(ears) - if(available_channels.len) - say("[pick(available_channels)]BAWWWWWK LEAVE THE HEADSET BAWKKKKK!") - else - say("BAWWWWWK LEAVE THE HEADSET BAWKKKKK!") - ears.forceMove(loc) - ears = null - update_speak() - else - to_chat(usr, "There is nothing to remove from its [remove_from].") - return - show_inv(usr) - else if(href_list["add_inv"]) - var/add_to = href_list["add_inv"] - if(!usr.get_active_hand()) - to_chat(usr, "You have nothing in your hand to put on its [add_to].") - return - switch(add_to) - if("ears") - if(ears) - to_chat(usr, "It's already wearing something.") - return - else - var/obj/item/item_to_add = usr.get_active_hand() - if(!item_to_add) - return - - if( !istype(item_to_add, /obj/item/radio/headset) ) - to_chat(usr, "This object won't fit.") - return - - var/obj/item/radio/headset/headset_to_add = item_to_add - - usr.drop_item() - headset_to_add.forceMove(src) - ears = headset_to_add - to_chat(usr, "You fit the headset onto [src].") - - available_channels.Cut() - for(var/ch in headset_to_add.channels) - switch(ch) - if("Engineering") - available_channels.Add(":e") - if("Command") - available_channels.Add(":c") - if("Security") - available_channels.Add(":s") - if("Science") - available_channels.Add(":n") - if("Medical") - available_channels.Add(":m") - if("Mining") - available_channels.Add(":d") - if("Cargo") - available_channels.Add(":q") - - if(headset_to_add.translate_binary) - available_channels.Add(":b") - update_speak() - show_inv(usr) - else - ..() - - -/* - * Attack responces - */ -//Humans, monkeys, aliens -/mob/living/simple_animal/parrot/attack_hand(mob/living/carbon/M as mob) - ..() - if(client) return - if(!stat && M.a_intent == "hurt") - - icon_state = "parrot_fly" //It is going to be flying regardless of whether it flees or attacks - - if(parrot_state == PARROT_PERCH) - parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched - - parrot_interest = M - parrot_state = PARROT_SWOOP //The parrot just got hit, it WILL move, now to pick a direction.. - - if(M.health < 50) //Weakened mob? Fight back! - parrot_state |= PARROT_ATTACK - else - parrot_state |= PARROT_FLEE //Otherwise, fly like a bat out of hell! - drop_held_item(0) - return - -//Mobs with objects -/mob/living/simple_animal/parrot/attackby(var/obj/item/O as obj, var/mob/user as mob, params) - ..() - if(!stat && !client && !istype(O, /obj/item/stack/medical)) - if(O.force) - if(parrot_state == PARROT_PERCH) - parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched - - parrot_interest = user - parrot_state = PARROT_SWOOP | PARROT_FLEE - icon_state = "parrot_fly" - drop_held_item(0) - return - -//Bullets -/mob/living/simple_animal/parrot/bullet_act(var/obj/item/projectile/Proj) - ..() - if(!stat && !client) - if(parrot_state == PARROT_PERCH) - parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched - - parrot_interest = null - parrot_state = PARROT_WANDER //OWFUCK, Been shot! RUN LIKE HELL! - parrot_been_shot += 5 - icon_state = "parrot_fly" - drop_held_item(0) - return - - -/* - * AI - Not really intelligent, but I'm calling it AI anyway. - */ -/mob/living/simple_animal/parrot/Life(seconds, times_fired) - ..() - - //Sprite and AI update for when a parrot gets pulled - if(pulledby && stat == CONSCIOUS) - icon_state = "parrot_fly" - -/mob/living/simple_animal/parrot/proc/update_speak() - speak.Cut() - - if(available_channels.len && ears) - for(var/possible_phrase in clean_speak) - //50/50 chance to not use the radio at all - speak += "[prob(50) ? pick(available_channels) : ""][possible_phrase]" - - else //If we have no headset or channels to use, dont try to use any! - for(var/possible_phrase in clean_speak) - speak += possible_phrase - -/mob/living/simple_animal/parrot/handle_automated_movement() - if(pulledby) - parrot_state = PARROT_WANDER - return - - if(!isturf(src.loc) || !canmove || buckled) - return //If it can't move, dont let it move. (The buckled check probably isn't necessary thanks to canmove) - - -//-----SPEECH - /* Parrot speech mimickry! - Phrases that the parrot hears in mob/living/say() get added to speach_buffer. - Every once in a while, the parrot picks one of the lines from the buffer and replaces an element of the 'speech' list. - Then it clears the buffer to make sure they dont magically remember something from hours ago. */ - if(speech_buffer.len && prob(10)) - if(clean_speak.len) - clean_speak -= pick(clean_speak) - - clean_speak += pick(speech_buffer) - speech_buffer.Cut() - - -//-----SLEEPING - if(parrot_state == PARROT_PERCH) - if(parrot_perch && parrot_perch.loc != src.loc) //Make sure someone hasnt moved our perch on us - if(parrot_perch in view(src)) - parrot_state = PARROT_SWOOP | PARROT_RETURN - icon_state = "parrot_fly" - return - else - parrot_state = PARROT_WANDER - icon_state = "parrot_fly" - return - - if(--parrot_sleep_dur) //Zzz - return - - else - //This way we only call the stuff below once every [sleep_max] ticks. - parrot_sleep_dur = parrot_sleep_max - - //Cycle through message modes for the headset - update_speak() - - //Search for item to steal - parrot_interest = search_for_item() - if(parrot_interest) - custom_emote(1,"looks in [parrot_interest]'s direction and takes flight.") - parrot_state = PARROT_SWOOP | PARROT_STEAL - icon_state = "parrot_fly" - return - -//-----WANDERING - This is basically a 'I dont know what to do yet' state - else if(parrot_state == PARROT_WANDER) - //Stop movement, we'll set it later - walk(src, 0) - parrot_interest = null - - //Wander around aimlessly. This will help keep the loops from searches down - //and possibly move the mob into a new are in view of something they can use - if(prob(90)) - step(src, pick(cardinal)) - return - - if(!held_item && !parrot_perch) //If we've got nothing to do.. look for something to do. - var/atom/movable/AM = search_for_perch_and_item() //This handles checking through lists so we know it's either a perch or stealable item - if(AM) - if(istype(AM, /obj/item) || isliving(AM)) //If stealable item - parrot_interest = AM - custom_emote(1,"turns and flies towards [parrot_interest]") - parrot_state = PARROT_SWOOP | PARROT_STEAL - return - else //Else it's a perch - parrot_perch = AM - parrot_state = PARROT_SWOOP | PARROT_RETURN - return - return - - if(parrot_interest && parrot_interest in view(src)) - parrot_state = PARROT_SWOOP | PARROT_STEAL - return - - if(parrot_perch && parrot_perch in view(src)) - parrot_state = PARROT_SWOOP | PARROT_RETURN - return - - else //Have an item but no perch? Find one! - parrot_perch = search_for_perch() - if(parrot_perch) - parrot_state = PARROT_SWOOP | PARROT_RETURN - return -//-----STEALING - else if(parrot_state == (PARROT_SWOOP | PARROT_STEAL)) - walk(src,0) - if(!parrot_interest || held_item) - parrot_state = PARROT_SWOOP | PARROT_RETURN - return - - if(!(parrot_interest in view(src))) - parrot_state = PARROT_SWOOP | PARROT_RETURN - return - - if(in_range(src, parrot_interest)) - - if(isliving(parrot_interest)) - steal_from_mob() - - else //This should ensure that we only grab the item we want, and make sure it's not already collected on our perch - if(!parrot_perch || parrot_interest.loc != parrot_perch.loc) - held_item = parrot_interest - parrot_interest.loc = src - visible_message("[src] grabs the [held_item]!", "You grab the [held_item]!", "You hear the sounds of wings flapping furiously.") - - parrot_interest = null - parrot_state = PARROT_SWOOP | PARROT_RETURN - return - - walk_to(src, parrot_interest, 1, parrot_speed) - return - -//-----RETURNING TO PERCH - else if(parrot_state == (PARROT_SWOOP | PARROT_RETURN)) - walk(src, 0) - if(!parrot_perch || !isturf(parrot_perch.loc)) //Make sure the perch exists and somehow isnt inside of something else. - parrot_perch = null - parrot_state = PARROT_WANDER - return - - if(in_range(src, parrot_perch)) - src.loc = parrot_perch.loc - drop_held_item() - parrot_state = PARROT_PERCH - icon_state = "parrot_sit" - return - - walk_to(src, parrot_perch, 1, parrot_speed) - return - -//-----FLEEING - else if(parrot_state == (PARROT_SWOOP | PARROT_FLEE)) - walk(src,0) - if(!parrot_interest || !isliving(parrot_interest)) //Sanity - parrot_state = PARROT_WANDER - - walk_away(src, parrot_interest, 1, parrot_speed-parrot_been_shot) - parrot_been_shot-- - return - -//-----ATTACKING - else if(parrot_state == (PARROT_SWOOP | PARROT_ATTACK)) - - //If we're attacking a nothing, an object, a turf or a ghost for some stupid reason, switch to wander - if(!parrot_interest || !isliving(parrot_interest)) - parrot_interest = null - parrot_state = PARROT_WANDER - return - - var/mob/living/L = parrot_interest - - //If the mob is close enough to interact with - if(in_range(src, parrot_interest)) - - //If the mob we've been chasing/attacking dies or falls into crit, check for loot! - if(L.stat) - parrot_interest = null - if(!held_item) - held_item = steal_from_ground() - if(!held_item) - held_item = steal_from_mob() //Apparently it's possible for dead mobs to hang onto items in certain circumstances. - if(parrot_perch in view(src)) //If we have a home nearby, go to it, otherwise find a new home - parrot_state = PARROT_SWOOP | PARROT_RETURN - else - parrot_state = PARROT_WANDER - return - - //Time for the hurt to begin! - var/damage = rand(5,10) - - if(ishuman(parrot_interest)) - var/mob/living/carbon/human/H = parrot_interest - var/obj/item/organ/external/affecting = H.get_organ(ran_zone(pick(parrot_dam_zone))) - - H.apply_damage(damage, BRUTE, affecting, H.run_armor_check(affecting, "melee"), sharp = 1) - custom_emote(1, pick("pecks [H]'s [affecting].", "cuts [H]'s [affecting] with its talons.")) - - else - L.adjustBruteLoss(damage) - custom_emote(1, pick("pecks at [L].", "claws [L].")) - return - - //Otherwise, fly towards the mob! - else - walk_to(src, parrot_interest, 1, parrot_speed) - return -//-----STATE MISHAP - else //This should not happen. If it does lets reset everything and try again - walk(src,0) - parrot_interest = null - parrot_perch = null - drop_held_item() - parrot_state = PARROT_WANDER - return - -/* - * Procs - */ - -/mob/living/simple_animal/parrot/movement_delay() - if(client && stat == CONSCIOUS && parrot_state != "parrot_fly") - icon_state = "parrot_fly" - //Because the most appropriate place to set icon_state is movement_delay(), clearly - return ..() - -/mob/living/simple_animal/parrot/proc/search_for_item() - for(var/atom/movable/AM in view(src)) - //Skip items we already stole or are wearing or are too big - if(parrot_perch && AM.loc == parrot_perch.loc || AM.loc == src) - continue - - if(istype(AM, /obj/item)) - var/obj/item/I = AM - if(I.w_class < WEIGHT_CLASS_SMALL) - return I - - if(iscarbon(AM)) - var/mob/living/carbon/C = AM - if((C.l_hand && C.l_hand.w_class <= WEIGHT_CLASS_SMALL) || (C.r_hand && C.r_hand.w_class <= WEIGHT_CLASS_SMALL)) - return C - return null - -/mob/living/simple_animal/parrot/proc/search_for_perch() - for(var/obj/O in view(src)) - for(var/path in desired_perches) - if(istype(O, path)) - return O - return null - -//This proc was made to save on doing two 'in view' loops seperatly -/mob/living/simple_animal/parrot/proc/search_for_perch_and_item() - for(var/atom/movable/AM in view(src)) - for(var/perch_path in desired_perches) - if(istype(AM, perch_path)) - return AM - - //Skip items we already stole or are wearing or are too big - if(parrot_perch && AM.loc == parrot_perch.loc || AM.loc == src) - continue - - if(istype(AM, /obj/item)) - var/obj/item/I = AM - if(I.w_class <= WEIGHT_CLASS_SMALL) - return I - - if(iscarbon(AM)) - var/mob/living/carbon/C = AM - if(C.l_hand && C.l_hand.w_class <= WEIGHT_CLASS_SMALL || C.r_hand && C.r_hand.w_class <= WEIGHT_CLASS_SMALL) - return C - return null - - -/* - * Verbs - These are actually procs, but can be used as verbs by player-controlled parrots. - */ -/mob/living/simple_animal/parrot/proc/steal_from_ground() - set name = "Steal from ground" - set category = "Parrot" - set desc = "Grabs a nearby item." - - if(stat) - return -1 - - if(held_item) - to_chat(src, "You are already holding the [held_item]") - return 1 - - for(var/obj/item/I in view(1,src)) - //Make sure we're not already holding it and it's small enough - if(I.loc != src && I.w_class <= WEIGHT_CLASS_SMALL) - - //If we have a perch and the item is sitting on it, continue - if(!client && parrot_perch && I.loc == parrot_perch.loc) - continue - - held_item = I - I.loc = src - visible_message("[src] grabs the [held_item]!", "You grab the [held_item]!", "You hear the sounds of wings flapping furiously.") - return held_item - - to_chat(src, "There is nothing of interest to take.") - return 0 - -/mob/living/simple_animal/parrot/proc/steal_from_mob() - set name = "Steal from mob" - set category = "Parrot" - set desc = "Steals an item right out of a person's hand!" - - if(stat) - return -1 - - if(held_item) - to_chat(src, "You are already holding the [held_item]") - return 1 - - var/obj/item/stolen_item = null - - for(var/mob/living/carbon/C in view(1,src)) - if(C.l_hand && C.l_hand.w_class <= WEIGHT_CLASS_SMALL) - stolen_item = C.l_hand - - if(C.r_hand && C.r_hand.w_class <= WEIGHT_CLASS_SMALL) - stolen_item = C.r_hand - - if(stolen_item) - C.unEquip(stolen_item) - held_item = stolen_item - stolen_item.loc = src - visible_message("[src] grabs the [held_item] out of [C]'s hand!", "You snag the [held_item] out of [C]'s hand!", "You hear the sounds of wings flapping furiously.") - return held_item - - to_chat(src, "There is nothing of interest to take.") - return 0 - -/mob/living/simple_animal/parrot/verb/drop_held_item_player() - set name = "Drop held item" - set category = "Parrot" - set desc = "Drop the item you're holding." - - if(stat) - return - - src.drop_held_item() - - return - -/mob/living/simple_animal/parrot/proc/drop_held_item(var/drop_gently = 1) - set name = "Drop held item" - set category = "Parrot" - set desc = "Drop the item you're holding." - - if(stat) - return -1 - - if(!held_item) - to_chat(src, "You have nothing to drop!") - return 0 - - if(!drop_gently) - if(istype(held_item, /obj/item/grenade)) - var/obj/item/grenade/G = held_item - G.loc = src.loc - G.prime() - to_chat(src, "You let go of the [held_item]!") - held_item = null - return 1 - - to_chat(src, "You drop the [held_item].") - - held_item.loc = src.loc - held_item = null - return 1 - -/mob/living/simple_animal/parrot/proc/perch_player() - set name = "Sit" - set category = "Parrot" - set desc = "Sit on a nice comfy perch." - - if(stat || !client) - return - - if(icon_state == "parrot_fly") - for(var/atom/movable/AM in view(src,1)) - for(var/perch_path in desired_perches) - if(istype(AM, perch_path)) - src.loc = AM.loc - icon_state = "parrot_sit" - return - to_chat(src, "There is no perch nearby to sit on.") - return - -/* - * Sub-types - */ -/mob/living/simple_animal/parrot/Poly - name = "Poly" - desc = "Poly the Parrot. An expert on quantum cracker theory." - clean_speak = list( - "Poly wanna cracker!", - "Check the singlo, you chucklefucks!", - "Check the tesla, you shits!", - "STOP HOT-WIRING THE ENGINE, FUCKING CHRIST!", - "Wire the solars, you lazy bums!", - "WHO TOOK THE DAMN HARDSUITS?", - "OH GOD ITS FREE CALL THE SHUTTLE", - "Why are there so many atmos alerts?", - "OH GOD WHY WOULD YOU TURN ON THE PA BEFORE CONTAINMENT IS UP?", - "Remember to lock the emitters!", - "Stop goofing off and repair the goddam station!", - "The singularity is not your friend!", - "What were the wires again?", - "Goddam emaggers!" - ) - unique_pet = TRUE - gold_core_spawnable = NO_SPAWN - -/mob/living/simple_animal/parrot/Poly/New() - ears = new /obj/item/radio/headset/headset_eng(src) - available_channels = list(":e") - ..() - -/mob/living/simple_animal/parrot/handle_message_mode(var/message_mode, list/message_pieces, var/verb, var/used_radios) - if(message_mode && istype(ears)) - ears.talk_into(src, message_pieces, message_mode, verb) - used_radios += ears - -/mob/living/simple_animal/parrot/hear_say(list/message_pieces, var/verb = "says", var/italics = 0, var/mob/speaker = null) - if(speaker != src && prob(50)) - parrot_hear(html_decode(multilingual_to_message(message_pieces))) - ..() - - - -/mob/living/simple_animal/parrot/hear_radio(list/message_pieces, var/verb="says", var/part_a, var/part_b, var/mob/speaker = null, var/hard_to_hear = 0, var/atom/follow_target) - if(speaker != src && prob(50)) - parrot_hear(html_decode(multilingual_to_message(message_pieces))) - ..() - - -/mob/living/simple_animal/parrot/proc/parrot_hear(var/message="") - if(!message || stat) - return - speech_buffer.Add(message) +/* Parrots! + * Contains + * Defines + * Inventory (headset stuff) + * Attack responces + * AI + * Procs / Verbs (usable by players) + * Sub-types + */ + +/* + * Defines + */ + +//Only a maximum of one action and one intent should be active at any given time. +//Actions +#define PARROT_PERCH 1 //Sitting/sleeping, not moving +#define PARROT_SWOOP 2 //Moving towards or away from a target +#define PARROT_WANDER 4 //Moving without a specific target in mind + +//Intents +#define PARROT_STEAL 8 //Flying towards a target to steal it/from it +#define PARROT_ATTACK 16 //Flying towards a target to attack it +#define PARROT_RETURN 32 //Flying towards its perch +#define PARROT_FLEE 64 //Flying away from its attacker + + +/mob/living/simple_animal/parrot + name = "\improper Parrot" + desc = "The parrot squaks, \"It's a Parrot! BAWWK!\"" + icon = 'icons/mob/animal.dmi' + icon_state = "parrot_fly" + icon_living = "parrot_fly" + icon_dead = "parrot_dead" + pass_flags = PASSTABLE + can_collar = 1 + + var/list/clean_speak = list( + "Hi", + "Hello!", + "Cracker?", + "BAWWWWK george mellons griffing me") + speak_emote = list("squawks","says","yells") + emote_hear = list("squawks","bawks") + emote_see = list("flutters its wings") + + speak_chance = 1//1% (1 in 100) chance every tick; So about once per 150 seconds, assuming an average tick is 1.5s + turns_per_move = 5 + butcher_results = list(/obj/item/reagent_containers/food/snacks/cracker = 3) + + response_help = "pets the" + response_disarm = "gently moves aside the" + response_harm = "swats the" + stop_automated_movement = 1 + universal_speak = 1 + mob_size = MOB_SIZE_SMALL + + var/parrot_state = PARROT_WANDER //Hunt for a perch when created + var/parrot_sleep_max = 25 //The time the parrot sits while perched before looking around. Mosly a way to avoid the parrot's AI in process_ai() being run every single tick. + var/parrot_sleep_dur = 25 //Same as above, this is the var that physically counts down + var/parrot_dam_zone = list("chest", "head", "l_arm", "l_leg", "r_arm", "r_leg") //For humans, select a bodypart to attack + + var/parrot_speed = 5 //"Delay in world ticks between movement." according to byond. Yeah, that's BS but it does directly affect movement. Higher number = slower. + var/parrot_been_shot = 0 //Parrots get a speed bonus after being shot. This will deincrement every process_ai() and at 0 the parrot will return to regular speed. + + var/list/speech_buffer = list() + var/list/available_channels = list() + + //Headset for Poly to yell at engineers :) + var/obj/item/radio/headset/ears = null + + //The thing the parrot is currently interested in. This gets used for items the parrot wants to pick up, mobs it wants to steal from, + //mobs it wants to attack or mobs that have attacked it + var/atom/movable/parrot_interest = null + + //Parrots will generally sit on their pertch unless something catches their eye. + //These vars store their preffered perch and if they dont have one, what they can use as a perch + var/obj/parrot_perch = null + var/obj/desired_perches = list(/obj/structure/computerframe, /obj/structure/displaycase, \ + /obj/structure/filingcabinet, /obj/machinery/teleport, \ + /obj/machinery/suit_storage_unit, /obj/machinery/clonepod, \ + /obj/machinery/dna_scannernew, /obj/machinery/telecomms, \ + /obj/machinery/nuclearbomb, /obj/machinery/particle_accelerator, \ + /obj/machinery/recharge_station, /obj/machinery/smartfridge, \ + /obj/machinery/computer) + + //Parrots are kleptomaniacs. This variable ... stores the item a parrot is holding. + var/obj/item/held_item = null + gold_core_spawnable = FRIENDLY_SPAWN + + +/mob/living/simple_animal/parrot/New() + ..() + GLOB.hear_radio_list += src + if(!ears) + var/headset = pick(/obj/item/radio/headset/headset_sec, \ + /obj/item/radio/headset/headset_eng, \ + /obj/item/radio/headset/headset_med, \ + /obj/item/radio/headset/headset_sci, \ + /obj/item/radio/headset/headset_cargo) + ears = new headset(src) + update_speak() + + parrot_sleep_dur = parrot_sleep_max //In case someone decides to change the max without changing the duration var + + verbs.Add(/mob/living/simple_animal/parrot/proc/steal_from_ground, \ + /mob/living/simple_animal/parrot/proc/steal_from_mob, \ + /mob/living/simple_animal/parrot/verb/drop_held_item_player, \ + /mob/living/simple_animal/parrot/proc/perch_player) + +/mob/living/simple_animal/parrot/Destroy() + GLOB.hear_radio_list -= src + return ..() + +/mob/living/simple_animal/parrot/death(gibbed) + if(can_die()) + if(held_item) + held_item.loc = src.loc + held_item = null + walk(src,0) + return ..() + +/mob/living/simple_animal/parrot/Stat() + ..() + stat("Held Item", held_item) + +/* + * Inventory + */ +/mob/living/simple_animal/parrot/show_inv(mob/user as mob) + user.set_machine(src) + + var/dat = {""} + + dat += "" + if(can_collar) + dat += "" + dat += "" + + dat += {"
        Headset:[(ears && !(ears.flags&ABSTRACT)) ? ears : "Empty"]
         
        Collar:[(pcollar && !(pcollar.flags&ABSTRACT)) ? pcollar : "Empty"]
        + Close + "} + + var/datum/browser/popup = new(user, "mob\ref[src]", "[src]", 440, 500) + popup.set_content(dat) + popup.open() + +/mob/living/simple_animal/parrot/Topic(href, href_list) + + //Can the usr physically do this? + if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) + return + + //Is the usr's mob type able to do this? + if(ishuman(usr) || isrobot(usr)) + if(href_list["remove_inv"]) + var/remove_from = href_list["remove_inv"] + switch(remove_from) + if("ears") + if(ears) + if(available_channels.len) + say("[pick(available_channels)]BAWWWWWK LEAVE THE HEADSET BAWKKKKK!") + else + say("BAWWWWWK LEAVE THE HEADSET BAWKKKKK!") + ears.forceMove(loc) + ears = null + update_speak() + else + to_chat(usr, "There is nothing to remove from its [remove_from].") + return + show_inv(usr) + else if(href_list["add_inv"]) + var/add_to = href_list["add_inv"] + if(!usr.get_active_hand()) + to_chat(usr, "You have nothing in your hand to put on its [add_to].") + return + switch(add_to) + if("ears") + if(ears) + to_chat(usr, "It's already wearing something.") + return + else + var/obj/item/item_to_add = usr.get_active_hand() + if(!item_to_add) + return + + if( !istype(item_to_add, /obj/item/radio/headset) ) + to_chat(usr, "This object won't fit.") + return + + var/obj/item/radio/headset/headset_to_add = item_to_add + + usr.drop_item() + headset_to_add.forceMove(src) + ears = headset_to_add + to_chat(usr, "You fit the headset onto [src].") + + available_channels.Cut() + for(var/ch in headset_to_add.channels) + switch(ch) + if("Engineering") + available_channels.Add(":e") + if("Command") + available_channels.Add(":c") + if("Security") + available_channels.Add(":s") + if("Science") + available_channels.Add(":n") + if("Medical") + available_channels.Add(":m") + if("Mining") + available_channels.Add(":d") + if("Cargo") + available_channels.Add(":q") + + if(headset_to_add.translate_binary) + available_channels.Add(":b") + update_speak() + show_inv(usr) + else + ..() + + +/* + * Attack responces + */ +//Humans, monkeys, aliens +/mob/living/simple_animal/parrot/attack_hand(mob/living/carbon/M as mob) + ..() + if(client) return + if(!stat && M.a_intent == "hurt") + + icon_state = "parrot_fly" //It is going to be flying regardless of whether it flees or attacks + + if(parrot_state == PARROT_PERCH) + parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched + + parrot_interest = M + parrot_state = PARROT_SWOOP //The parrot just got hit, it WILL move, now to pick a direction.. + + if(M.health < 50) //Weakened mob? Fight back! + parrot_state |= PARROT_ATTACK + else + parrot_state |= PARROT_FLEE //Otherwise, fly like a bat out of hell! + drop_held_item(0) + return + +//Mobs with objects +/mob/living/simple_animal/parrot/attackby(var/obj/item/O as obj, var/mob/user as mob, params) + ..() + if(!stat && !client && !istype(O, /obj/item/stack/medical)) + if(O.force) + if(parrot_state == PARROT_PERCH) + parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched + + parrot_interest = user + parrot_state = PARROT_SWOOP | PARROT_FLEE + icon_state = "parrot_fly" + drop_held_item(0) + return + +//Bullets +/mob/living/simple_animal/parrot/bullet_act(var/obj/item/projectile/Proj) + ..() + if(!stat && !client) + if(parrot_state == PARROT_PERCH) + parrot_sleep_dur = parrot_sleep_max //Reset it's sleep timer if it was perched + + parrot_interest = null + parrot_state = PARROT_WANDER //OWFUCK, Been shot! RUN LIKE HELL! + parrot_been_shot += 5 + icon_state = "parrot_fly" + drop_held_item(0) + return + + +/* + * AI - Not really intelligent, but I'm calling it AI anyway. + */ +/mob/living/simple_animal/parrot/Life(seconds, times_fired) + ..() + + //Sprite and AI update for when a parrot gets pulled + if(pulledby && stat == CONSCIOUS) + icon_state = "parrot_fly" + +/mob/living/simple_animal/parrot/proc/update_speak() + speak.Cut() + + if(available_channels.len && ears) + for(var/possible_phrase in clean_speak) + //50/50 chance to not use the radio at all + speak += "[prob(50) ? pick(available_channels) : ""][possible_phrase]" + + else //If we have no headset or channels to use, dont try to use any! + for(var/possible_phrase in clean_speak) + speak += possible_phrase + +/mob/living/simple_animal/parrot/handle_automated_movement() + if(pulledby) + parrot_state = PARROT_WANDER + return + + if(!isturf(src.loc) || !canmove || buckled) + return //If it can't move, dont let it move. (The buckled check probably isn't necessary thanks to canmove) + + +//-----SPEECH + /* Parrot speech mimickry! + Phrases that the parrot hears in mob/living/say() get added to speach_buffer. + Every once in a while, the parrot picks one of the lines from the buffer and replaces an element of the 'speech' list. + Then it clears the buffer to make sure they dont magically remember something from hours ago. */ + if(speech_buffer.len && prob(10)) + if(clean_speak.len) + clean_speak -= pick(clean_speak) + + clean_speak += pick(speech_buffer) + speech_buffer.Cut() + + +//-----SLEEPING + if(parrot_state == PARROT_PERCH) + if(parrot_perch && parrot_perch.loc != src.loc) //Make sure someone hasnt moved our perch on us + if(parrot_perch in view(src)) + parrot_state = PARROT_SWOOP | PARROT_RETURN + icon_state = "parrot_fly" + return + else + parrot_state = PARROT_WANDER + icon_state = "parrot_fly" + return + + if(--parrot_sleep_dur) //Zzz + return + + else + //This way we only call the stuff below once every [sleep_max] ticks. + parrot_sleep_dur = parrot_sleep_max + + //Cycle through message modes for the headset + update_speak() + + //Search for item to steal + parrot_interest = search_for_item() + if(parrot_interest) + custom_emote(1,"looks in [parrot_interest]'s direction and takes flight.") + parrot_state = PARROT_SWOOP | PARROT_STEAL + icon_state = "parrot_fly" + return + +//-----WANDERING - This is basically a 'I dont know what to do yet' state + else if(parrot_state == PARROT_WANDER) + //Stop movement, we'll set it later + walk(src, 0) + parrot_interest = null + + //Wander around aimlessly. This will help keep the loops from searches down + //and possibly move the mob into a new are in view of something they can use + if(prob(90)) + step(src, pick(cardinal)) + return + + if(!held_item && !parrot_perch) //If we've got nothing to do.. look for something to do. + var/atom/movable/AM = search_for_perch_and_item() //This handles checking through lists so we know it's either a perch or stealable item + if(AM) + if(istype(AM, /obj/item) || isliving(AM)) //If stealable item + parrot_interest = AM + custom_emote(1,"turns and flies towards [parrot_interest]") + parrot_state = PARROT_SWOOP | PARROT_STEAL + return + else //Else it's a perch + parrot_perch = AM + parrot_state = PARROT_SWOOP | PARROT_RETURN + return + return + + if(parrot_interest && parrot_interest in view(src)) + parrot_state = PARROT_SWOOP | PARROT_STEAL + return + + if(parrot_perch && parrot_perch in view(src)) + parrot_state = PARROT_SWOOP | PARROT_RETURN + return + + else //Have an item but no perch? Find one! + parrot_perch = search_for_perch() + if(parrot_perch) + parrot_state = PARROT_SWOOP | PARROT_RETURN + return +//-----STEALING + else if(parrot_state == (PARROT_SWOOP | PARROT_STEAL)) + walk(src,0) + if(!parrot_interest || held_item) + parrot_state = PARROT_SWOOP | PARROT_RETURN + return + + if(!(parrot_interest in view(src))) + parrot_state = PARROT_SWOOP | PARROT_RETURN + return + + if(in_range(src, parrot_interest)) + + if(isliving(parrot_interest)) + steal_from_mob() + + else //This should ensure that we only grab the item we want, and make sure it's not already collected on our perch + if(!parrot_perch || parrot_interest.loc != parrot_perch.loc) + held_item = parrot_interest + parrot_interest.loc = src + visible_message("[src] grabs the [held_item]!", "You grab the [held_item]!", "You hear the sounds of wings flapping furiously.") + + parrot_interest = null + parrot_state = PARROT_SWOOP | PARROT_RETURN + return + + walk_to(src, parrot_interest, 1, parrot_speed) + return + +//-----RETURNING TO PERCH + else if(parrot_state == (PARROT_SWOOP | PARROT_RETURN)) + walk(src, 0) + if(!parrot_perch || !isturf(parrot_perch.loc)) //Make sure the perch exists and somehow isnt inside of something else. + parrot_perch = null + parrot_state = PARROT_WANDER + return + + if(in_range(src, parrot_perch)) + src.loc = parrot_perch.loc + drop_held_item() + parrot_state = PARROT_PERCH + icon_state = "parrot_sit" + return + + walk_to(src, parrot_perch, 1, parrot_speed) + return + +//-----FLEEING + else if(parrot_state == (PARROT_SWOOP | PARROT_FLEE)) + walk(src,0) + if(!parrot_interest || !isliving(parrot_interest)) //Sanity + parrot_state = PARROT_WANDER + + walk_away(src, parrot_interest, 1, parrot_speed-parrot_been_shot) + parrot_been_shot-- + return + +//-----ATTACKING + else if(parrot_state == (PARROT_SWOOP | PARROT_ATTACK)) + + //If we're attacking a nothing, an object, a turf or a ghost for some stupid reason, switch to wander + if(!parrot_interest || !isliving(parrot_interest)) + parrot_interest = null + parrot_state = PARROT_WANDER + return + + var/mob/living/L = parrot_interest + + //If the mob is close enough to interact with + if(in_range(src, parrot_interest)) + + //If the mob we've been chasing/attacking dies or falls into crit, check for loot! + if(L.stat) + parrot_interest = null + if(!held_item) + held_item = steal_from_ground() + if(!held_item) + held_item = steal_from_mob() //Apparently it's possible for dead mobs to hang onto items in certain circumstances. + if(parrot_perch in view(src)) //If we have a home nearby, go to it, otherwise find a new home + parrot_state = PARROT_SWOOP | PARROT_RETURN + else + parrot_state = PARROT_WANDER + return + + //Time for the hurt to begin! + var/damage = rand(5,10) + + if(ishuman(parrot_interest)) + var/mob/living/carbon/human/H = parrot_interest + var/obj/item/organ/external/affecting = H.get_organ(ran_zone(pick(parrot_dam_zone))) + + H.apply_damage(damage, BRUTE, affecting, H.run_armor_check(affecting, "melee"), sharp = 1) + custom_emote(1, pick("pecks [H]'s [affecting].", "cuts [H]'s [affecting] with its talons.")) + + else + L.adjustBruteLoss(damage) + custom_emote(1, pick("pecks at [L].", "claws [L].")) + return + + //Otherwise, fly towards the mob! + else + walk_to(src, parrot_interest, 1, parrot_speed) + return +//-----STATE MISHAP + else //This should not happen. If it does lets reset everything and try again + walk(src,0) + parrot_interest = null + parrot_perch = null + drop_held_item() + parrot_state = PARROT_WANDER + return + +/* + * Procs + */ + +/mob/living/simple_animal/parrot/movement_delay() + if(client && stat == CONSCIOUS && parrot_state != "parrot_fly") + icon_state = "parrot_fly" + //Because the most appropriate place to set icon_state is movement_delay(), clearly + return ..() + +/mob/living/simple_animal/parrot/proc/search_for_item() + for(var/atom/movable/AM in view(src)) + //Skip items we already stole or are wearing or are too big + if(parrot_perch && AM.loc == parrot_perch.loc || AM.loc == src) + continue + + if(istype(AM, /obj/item)) + var/obj/item/I = AM + if(I.w_class < WEIGHT_CLASS_SMALL) + return I + + if(iscarbon(AM)) + var/mob/living/carbon/C = AM + if((C.l_hand && C.l_hand.w_class <= WEIGHT_CLASS_SMALL) || (C.r_hand && C.r_hand.w_class <= WEIGHT_CLASS_SMALL)) + return C + return null + +/mob/living/simple_animal/parrot/proc/search_for_perch() + for(var/obj/O in view(src)) + for(var/path in desired_perches) + if(istype(O, path)) + return O + return null + +//This proc was made to save on doing two 'in view' loops seperatly +/mob/living/simple_animal/parrot/proc/search_for_perch_and_item() + for(var/atom/movable/AM in view(src)) + for(var/perch_path in desired_perches) + if(istype(AM, perch_path)) + return AM + + //Skip items we already stole or are wearing or are too big + if(parrot_perch && AM.loc == parrot_perch.loc || AM.loc == src) + continue + + if(istype(AM, /obj/item)) + var/obj/item/I = AM + if(I.w_class <= WEIGHT_CLASS_SMALL) + return I + + if(iscarbon(AM)) + var/mob/living/carbon/C = AM + if(C.l_hand && C.l_hand.w_class <= WEIGHT_CLASS_SMALL || C.r_hand && C.r_hand.w_class <= WEIGHT_CLASS_SMALL) + return C + return null + + +/* + * Verbs - These are actually procs, but can be used as verbs by player-controlled parrots. + */ +/mob/living/simple_animal/parrot/proc/steal_from_ground() + set name = "Steal from ground" + set category = "Parrot" + set desc = "Grabs a nearby item." + + if(stat) + return -1 + + if(held_item) + to_chat(src, "You are already holding the [held_item]") + return 1 + + for(var/obj/item/I in view(1,src)) + //Make sure we're not already holding it and it's small enough + if(I.loc != src && I.w_class <= WEIGHT_CLASS_SMALL) + + //If we have a perch and the item is sitting on it, continue + if(!client && parrot_perch && I.loc == parrot_perch.loc) + continue + + held_item = I + I.loc = src + visible_message("[src] grabs the [held_item]!", "You grab the [held_item]!", "You hear the sounds of wings flapping furiously.") + return held_item + + to_chat(src, "There is nothing of interest to take.") + return 0 + +/mob/living/simple_animal/parrot/proc/steal_from_mob() + set name = "Steal from mob" + set category = "Parrot" + set desc = "Steals an item right out of a person's hand!" + + if(stat) + return -1 + + if(held_item) + to_chat(src, "You are already holding the [held_item]") + return 1 + + var/obj/item/stolen_item = null + + for(var/mob/living/carbon/C in view(1,src)) + if(C.l_hand && C.l_hand.w_class <= WEIGHT_CLASS_SMALL) + stolen_item = C.l_hand + + if(C.r_hand && C.r_hand.w_class <= WEIGHT_CLASS_SMALL) + stolen_item = C.r_hand + + if(stolen_item) + C.unEquip(stolen_item) + held_item = stolen_item + stolen_item.loc = src + visible_message("[src] grabs the [held_item] out of [C]'s hand!", "You snag the [held_item] out of [C]'s hand!", "You hear the sounds of wings flapping furiously.") + return held_item + + to_chat(src, "There is nothing of interest to take.") + return 0 + +/mob/living/simple_animal/parrot/verb/drop_held_item_player() + set name = "Drop held item" + set category = "Parrot" + set desc = "Drop the item you're holding." + + if(stat) + return + + src.drop_held_item() + + return + +/mob/living/simple_animal/parrot/proc/drop_held_item(var/drop_gently = 1) + set name = "Drop held item" + set category = "Parrot" + set desc = "Drop the item you're holding." + + if(stat) + return -1 + + if(!held_item) + to_chat(src, "You have nothing to drop!") + return 0 + + if(!drop_gently) + if(istype(held_item, /obj/item/grenade)) + var/obj/item/grenade/G = held_item + G.loc = src.loc + G.prime() + to_chat(src, "You let go of the [held_item]!") + held_item = null + return 1 + + to_chat(src, "You drop the [held_item].") + + held_item.loc = src.loc + held_item = null + return 1 + +/mob/living/simple_animal/parrot/proc/perch_player() + set name = "Sit" + set category = "Parrot" + set desc = "Sit on a nice comfy perch." + + if(stat || !client) + return + + if(icon_state == "parrot_fly") + for(var/atom/movable/AM in view(src,1)) + for(var/perch_path in desired_perches) + if(istype(AM, perch_path)) + src.loc = AM.loc + icon_state = "parrot_sit" + return + to_chat(src, "There is no perch nearby to sit on.") + return + +/* + * Sub-types + */ +/mob/living/simple_animal/parrot/Poly + name = "Poly" + desc = "Poly the Parrot. An expert on quantum cracker theory." + clean_speak = list( + "Poly wanna cracker!", + "Check the singlo, you chucklefucks!", + "Check the tesla, you shits!", + "STOP HOT-WIRING THE ENGINE, FUCKING CHRIST!", + "Wire the solars, you lazy bums!", + "WHO TOOK THE DAMN HARDSUITS?", + "OH GOD ITS FREE CALL THE SHUTTLE", + "Why are there so many atmos alerts?", + "OH GOD WHY WOULD YOU TURN ON THE PA BEFORE CONTAINMENT IS UP?", + "Remember to lock the emitters!", + "Stop goofing off and repair the goddam station!", + "The singularity is not your friend!", + "What were the wires again?", + "Goddam emaggers!" + ) + unique_pet = TRUE + gold_core_spawnable = NO_SPAWN + +/mob/living/simple_animal/parrot/Poly/New() + ears = new /obj/item/radio/headset/headset_eng(src) + available_channels = list(":e") + ..() + +/mob/living/simple_animal/parrot/handle_message_mode(var/message_mode, list/message_pieces, var/verb, var/used_radios) + if(message_mode && istype(ears)) + ears.talk_into(src, message_pieces, message_mode, verb) + used_radios += ears + +/mob/living/simple_animal/parrot/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency) + if(speaker != src && prob(50)) + parrot_hear(html_decode(multilingual_to_message(message_pieces))) + ..() + + + +/mob/living/simple_animal/parrot/hear_radio(list/message_pieces, var/verb="says", var/part_a, var/part_b, var/mob/speaker = null, var/hard_to_hear = 0, var/atom/follow_target) + if(speaker != src && prob(50)) + parrot_hear(html_decode(multilingual_to_message(message_pieces))) + ..() + + +/mob/living/simple_animal/parrot/proc/parrot_hear(var/message="") + if(!message || stat) + return + speech_buffer.Add(message) diff --git a/code/modules/mob/living/simple_animal/shade.dm b/code/modules/mob/living/simple_animal/shade.dm index 948c16314c9..663838d3a45 100644 --- a/code/modules/mob/living/simple_animal/shade.dm +++ b/code/modules/mob/living/simple_animal/shade.dm @@ -1,61 +1,61 @@ -/mob/living/simple_animal/shade - name = "Shade" - real_name = "Shade" - desc = "A bound spirit" - icon = 'icons/mob/mob.dmi' - icon_state = "shade" - icon_living = "shade" - icon_dead = "shade_dead" - maxHealth = 50 - health = 50 - speak_emote = list("hisses") - emote_hear = list("wails","screeches") - response_help = "puts their hand through" - response_disarm = "flails at" - response_harm = "punches the" - melee_damage_lower = 5 - melee_damage_upper = 15 - attacktext = "drains the life from" - minbodytemp = 0 - maxbodytemp = 4000 - atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) - speed = -1 - stop_automated_movement = 1 - status_flags = 0 - faction = list("cult") - status_flags = CANPUSH - loot = list(/obj/item/reagent_containers/food/snacks/ectoplasm) - del_on_death = 1 - deathmessage = "lets out a contented sigh as their form unwinds." - -/mob/living/simple_animal/shade/death(gibbed) - . = ..() - SSticker.mode.remove_cultist(src.mind, FALSE) - -/mob/living/simple_animal/shade/attackby(var/obj/item/O as obj, var/mob/user as mob) //Marker -Agouri - if(istype(O, /obj/item/soulstone)) - O.transfer_soul("SHADE", src, user) - else - if(O.force) - var/damage = O.force - if(O.damtype == STAMINA) - damage = 0 - health -= damage - user.visible_message("[src] has been attacked with the [O] by [user]. ") - else - user.visible_message("[user] gently taps [src] with the [O]. ", "This weapon is ineffective, it does no damage.") - return - -/mob/living/simple_animal/shade/cult/Initialize(mapload) - . = ..() - name = SSticker.cultdat?.shade_name - real_name = SSticker.cultdat?.shade_name - icon_state = SSticker.cultdat?.shade_icon_state - -/mob/living/simple_animal/shade/sword - universal_speak = 1 - faction = list("neutral") - -/mob/living/simple_animal/shade/sword/Initialize(mapload) - .=..() - status_flags |= GODMODE \ No newline at end of file +/mob/living/simple_animal/shade + name = "Shade" + real_name = "Shade" + desc = "A bound spirit" + icon = 'icons/mob/mob.dmi' + icon_state = "shade" + icon_living = "shade" + icon_dead = "shade_dead" + maxHealth = 50 + health = 50 + speak_emote = list("hisses") + emote_hear = list("wails","screeches") + response_help = "puts their hand through" + response_disarm = "flails at" + response_harm = "punches the" + melee_damage_lower = 5 + melee_damage_upper = 15 + attacktext = "drains the life from" + minbodytemp = 0 + maxbodytemp = 4000 + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + speed = -1 + stop_automated_movement = 1 + status_flags = 0 + faction = list("cult") + status_flags = CANPUSH + loot = list(/obj/item/reagent_containers/food/snacks/ectoplasm) + del_on_death = 1 + deathmessage = "lets out a contented sigh as their form unwinds." + +/mob/living/simple_animal/shade/death(gibbed) + . = ..() + SSticker.mode.remove_cultist(src.mind, FALSE) + +/mob/living/simple_animal/shade/attackby(var/obj/item/O as obj, var/mob/user as mob) //Marker -Agouri + if(istype(O, /obj/item/soulstone)) + O.transfer_soul("SHADE", src, user) + else + if(O.force) + var/damage = O.force + if(O.damtype == STAMINA) + damage = 0 + health -= damage + user.visible_message("[src] has been attacked with the [O] by [user]. ") + else + user.visible_message("[user] gently taps [src] with the [O]. ", "This weapon is ineffective, it does no damage.") + return + +/mob/living/simple_animal/shade/cult/Initialize(mapload) + . = ..() + name = SSticker.cultdat?.shade_name + real_name = SSticker.cultdat?.shade_name + icon_state = SSticker.cultdat?.shade_icon_state + +/mob/living/simple_animal/shade/sword + universal_speak = 1 + faction = list("neutral") + +/mob/living/simple_animal/shade/sword/Initialize(mapload) + .=..() + status_flags |= GODMODE diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 01c1f7cfe43..7665d20f932 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -1,615 +1,615 @@ -/mob/living/simple_animal - name = "animal" - icon = 'icons/mob/animal.dmi' - health = 20 - maxHealth = 20 - gender = PLURAL //placeholder - - universal_understand = 1 - universal_speak = 0 - status_flags = CANPUSH - - var/icon_living = "" - var/icon_dead = "" - var/icon_resting = "" - var/icon_gib = null //We only try to show a gibbing animation if this exists. - var/flip_on_death = FALSE //Flip the sprite upside down on death. Mostly here for things lacking custom dead sprites. - - var/list/speak = list() - var/speak_chance = 0 - var/list/emote_hear = list() //Hearable emotes - var/list/emote_see = list() //Unlike speak_emote, the list of things in this variable only show by themselves with no spoken text. IE: Ian barks, Ian yaps - - var/turns_per_move = 1 - var/turns_since_move = 0 - var/stop_automated_movement = 0 //Use this to temporarely stop random movement or to if you write special movement code for animals. - var/wander = 1 // Does the mob wander around when idle? - var/stop_automated_movement_when_pulled = 1 //When set to 1 this stops the animal from moving when someone is pulling it. - - //Interaction - var/response_help = "pokes" - var/response_disarm = "shoves" - var/response_harm = "hits" - var/harm_intent_damage = 3 - var/force_threshold = 0 //Minimum force required to deal any damage - - //Temperature effect - var/minbodytemp = 250 - var/maxbodytemp = 350 - var/heat_damage_per_tick = 2 //amount of damage applied if animal's body temperature is higher than maxbodytemp - var/cold_damage_per_tick = 2 //same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp - - //Healable by medical stacks? Defaults to yes. - var/healable = 1 - - //Atmos effect - Yes, you can make creatures that require plasma or co2 to survive. N2O is a trace gas and handled separately, hence why it isn't here. It'd be hard to add it. Hard and me don't mix (Yes, yes make all the dick jokes you want with that.) - Errorage - var/list/atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) //Leaving something at 0 means it's off - has no maximum - var/unsuitable_atmos_damage = 2 //This damage is taken when atmos doesn't fit all the requirements above - - //LETTING SIMPLE ANIMALS ATTACK? WHAT COULD GO WRONG. Defaults to zero so Ian can still be cuddly - var/melee_damage_lower = 0 - var/melee_damage_upper = 0 - var/obj_damage = 0 //how much damage this simple animal does to objects, if any - var/armour_penetration = 0 //How much armour they ignore, as a flat reduction from the targets armour value - var/melee_damage_type = BRUTE //Damage type of a simple mob's melee attack, should it do damage. - var/list/damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1) // 1 for full damage , 0 for none , -1 for 1:1 heal from that source - var/attacktext = "attacks" - var/attack_sound = null - var/friendly = "nuzzles" //If the mob does no damage with it's attack - var/environment_smash = ENVIRONMENT_SMASH_NONE //Set to 1 to allow breaking of crates,lockers,racks,tables; 2 for walls; 3 for Rwalls - - var/speed = 1 //LETS SEE IF I CAN SET SPEEDS FOR SIMPLE MOBS WITHOUT DESTROYING EVERYTHING. Higher speed is slower, negative speed is faster - var/can_hide = 0 - - var/obj/item/clothing/accessory/petcollar/pcollar = null - var/collar_type //if the mob has collar sprites, define them. - var/unique_pet = FALSE // if the mob can be renamed - var/can_collar = FALSE // can add collar to mob or not - - //Hot simple_animal baby making vars - var/list/childtype = null - var/next_scan_time = 0 - var/animal_species //Sorry, no spider+corgi buttbabies. - - var/buffed = 0 //In the event that you want to have a buffing effect on the mob, but don't want it to stack with other effects, any outside force that applies a buff to a simple mob should at least set this to 1, so we have something to check against - var/gold_core_spawnable = NO_SPAWN //If the mob can be spawned with a gold slime core. HOSTILE_SPAWN are spawned with plasma, FRIENDLY_SPAWN are spawned with blood - - var/mob/living/carbon/human/master_commander = null //holding var for determining who own/controls a sentient simple animal (for sentience potions). - - var/datum/component/spawner/nest - - var/sentience_type = SENTIENCE_ORGANIC // Sentience type, for slime potions - - var/list/loot = list() //list of things spawned at mob's loc when it dies - var/del_on_death = 0 //causes mob to be deleted on death, useful for mobs that spawn lootable corpses - var/deathmessage = "" - var/death_sound = null //The sound played on death - - var/allow_movement_on_non_turfs = FALSE - - var/attacked_sound = "punch" - - var/AIStatus = AI_ON //The Status of our AI, can be set to AI_ON (On, usual processing), AI_IDLE (Will not process, but will return to AI_ON if an enemy comes near), AI_OFF (Off, Not processing ever) - var/can_have_ai = TRUE //once we have become sentient, we can never go back - - var/shouldwakeup = FALSE //convenience var for forcibly waking up an idling AI on next check. - - //domestication - var/tame = 0 - - var/my_z // I don't want to confuse this with client registered_z - -/mob/living/simple_animal/Initialize(mapload) - . = ..() - GLOB.simple_animals[AIStatus] += src - if(gender == PLURAL) - gender = pick(MALE, FEMALE) - if(!real_name) - real_name = name - if(!loc) - stack_trace("Simple animal being instantiated in nullspace") - verbs -= /mob/verb/observe - if(!can_hide) - verbs -= /mob/living/simple_animal/verb/hide - if(pcollar) - pcollar = new(src) - regenerate_icons() - -/mob/living/simple_animal/Destroy() - QDEL_NULL(pcollar) - master_commander = null - GLOB.simple_animals[AIStatus] -= src - if(SSnpcpool.state == SS_PAUSED && LAZYLEN(SSnpcpool.currentrun)) - SSnpcpool.currentrun -= src - - if(nest) - nest.spawned_mobs -= src - nest = null - - var/turf/T = get_turf(src) - if (T && AIStatus == AI_Z_OFF) - SSidlenpcpool.idle_mobs_by_zlevel[T.z] -= src - - return ..() - -/mob/living/simple_animal/handle_atom_del(atom/A) - if(A == pcollar) - pcollar = null - return ..() - -/mob/living/simple_animal/examine(mob/user) - . = ..() - if(stat == DEAD) - . += "Upon closer examination, [p_they()] appear[p_s()] to be dead." - -/mob/living/simple_animal/updatehealth(reason = "none given") - ..(reason) - health = Clamp(health, 0, maxHealth) - med_hud_set_status() - -/mob/living/simple_animal/StartResting(updating = 1) - ..() - if(icon_resting && stat != DEAD) - icon_state = icon_resting - if(collar_type) - collar_type = "[initial(collar_type)]_rest" - regenerate_icons() - -/mob/living/simple_animal/StopResting(updating = 1) - ..() - if(icon_resting && stat != DEAD) - icon_state = icon_living - if(collar_type) - collar_type = "[initial(collar_type)]" - regenerate_icons() - -/mob/living/simple_animal/update_stat(reason = "none given") - if(status_flags & GODMODE) - return - - ..(reason) - if(stat != DEAD) - if(health < 1) - death() - create_debug_log("died of damage, trigger reason: [reason]") - -/mob/living/simple_animal/proc/handle_automated_action() - set waitfor = FALSE - return - -/mob/living/simple_animal/proc/handle_automated_movement() - set waitfor = FALSE - if(!stop_automated_movement && wander) - if((isturf(loc) || allow_movement_on_non_turfs) && !resting && !buckled && canmove) //This is so it only moves if it's not inside a closet, gentics machine, etc. - turns_since_move++ - if(turns_since_move >= turns_per_move) - if(!(stop_automated_movement_when_pulled && pulledby)) //Soma animals don't move when pulled - var/anydir = pick(cardinal) - if(Process_Spacemove(anydir)) - Move(get_step(src,anydir), anydir) - turns_since_move = 0 - return 1 - -/mob/living/simple_animal/proc/handle_automated_speech(override) - set waitfor = FALSE - if(speak_chance) - if(prob(speak_chance) || override) - if(speak && speak.len) - if((emote_hear && emote_hear.len) || (emote_see && emote_see.len)) - var/length = speak.len - if(emote_hear && emote_hear.len) - length += emote_hear.len - if(emote_see && emote_see.len) - length += emote_see.len - var/randomValue = rand(1,length) - if(randomValue <= speak.len) - say(pick(speak)) - else - randomValue -= speak.len - if(emote_see && randomValue <= emote_see.len) - custom_emote(1, pick(emote_see)) - else - custom_emote(2, pick(emote_hear)) - else - say(pick(speak)) - else - if(!(emote_hear && emote_hear.len) && (emote_see && emote_see.len)) - custom_emote(1, pick(emote_see)) - if((emote_hear && emote_hear.len) && !(emote_see && emote_see.len)) - custom_emote(2, pick(emote_hear)) - if((emote_hear && emote_hear.len) && (emote_see && emote_see.len)) - var/length = emote_hear.len + emote_see.len - var/pick = rand(1,length) - if(pick <= emote_see.len) - custom_emote(1, pick(emote_see)) - else - custom_emote(2,pick(emote_hear)) - - -/mob/living/simple_animal/handle_environment(datum/gas_mixture/environment) - var/atmos_suitable = 1 - - var/areatemp = get_temperature(environment) - - if(abs(areatemp - bodytemperature) > 5 && !(BREATHLESS in mutations)) - var/diff = areatemp - bodytemperature - diff = diff / 5 - bodytemperature += diff - - var/tox = environment.toxins - var/oxy = environment.oxygen - var/n2 = environment.nitrogen - var/co2 = environment.carbon_dioxide - - if(atmos_requirements["min_oxy"] && oxy < atmos_requirements["min_oxy"]) - atmos_suitable = 0 - throw_alert("not_enough_oxy", /obj/screen/alert/not_enough_oxy) - else if(atmos_requirements["max_oxy"] && oxy > atmos_requirements["max_oxy"]) - atmos_suitable = 0 - throw_alert("too_much_oxy", /obj/screen/alert/too_much_oxy) - else - clear_alert("not_enough_oxy") - clear_alert("too_much_oxy") - - if(atmos_requirements["min_tox"] && tox < atmos_requirements["min_tox"]) - atmos_suitable = 0 - throw_alert("not_enough_tox", /obj/screen/alert/not_enough_tox) - else if(atmos_requirements["max_tox"] && tox > atmos_requirements["max_tox"]) - atmos_suitable = 0 - throw_alert("too_much_tox", /obj/screen/alert/too_much_tox) - else - clear_alert("too_much_tox") - clear_alert("not_enough_tox") - - if(atmos_requirements["min_n2"] && n2 < atmos_requirements["min_n2"]) - atmos_suitable = 0 - else if(atmos_requirements["max_n2"] && n2 > atmos_requirements["max_n2"]) - atmos_suitable = 0 - - if(atmos_requirements["min_co2"] && co2 < atmos_requirements["min_co2"]) - atmos_suitable = 0 - else if(atmos_requirements["max_co2"] && co2 > atmos_requirements["max_co2"]) - atmos_suitable = 0 - - if(!atmos_suitable) - adjustHealth(unsuitable_atmos_damage) - - handle_temperature_damage() - -/mob/living/simple_animal/proc/handle_temperature_damage() - if(bodytemperature < minbodytemp) - adjustHealth(cold_damage_per_tick) - else if(bodytemperature > maxbodytemp) - adjustHealth(heat_damage_per_tick) - -/mob/living/simple_animal/gib() - if(icon_gib) - flick(icon_gib, src) - if(butcher_results) - var/atom/Tsec = drop_location() - for(var/path in butcher_results) - for(var/i in 1 to butcher_results[path]) - new path(Tsec) - if(pcollar) - pcollar.forceMove(drop_location()) - pcollar = null - ..() - -/mob/living/simple_animal/emote(act, m_type = 1, message = null, force) - if(stat) - return - act = lowertext(act) - switch(act) //IMPORTANT: Emotes MUST NOT CONFLICT anywhere along the chain. - if("scream") - message = "\The [src] whimpers." - m_type = 2 - if("help") - to_chat(src, "scream") - - ..() - -/mob/living/simple_animal/say_quote(message) - var/verb = "says" - - if(speak_emote.len) - verb = pick(speak_emote) - - return verb - -/mob/living/simple_animal/movement_delay() - . = ..() - - . = speed - - . += config.animal_delay - -/mob/living/simple_animal/Stat() - ..() - if(statpanel("Status")) - stat(null, "Health: [round((health / maxHealth) * 100)]%") - return TRUE - -/mob/living/simple_animal/proc/drop_loot() - if(loot.len) - for(var/i in loot) - new i(loc) - -/mob/living/simple_animal/revive() - ..() - density = initial(density) - -/mob/living/simple_animal/death(gibbed) - // Only execute the below if we successfully died - . = ..() - if(!.) - return FALSE - flying = FALSE - if(nest) - nest.spawned_mobs -= src - nest = null - drop_loot() - if(!gibbed) - if(death_sound) - playsound(get_turf(src),death_sound, 200, 1) - if(deathmessage) - visible_message("\The [src] [deathmessage]") - else if(!del_on_death) - visible_message("\The [src] stops moving...") - if(del_on_death) - //Prevent infinite loops if the mob Destroy() is overridden in such - //a manner as to cause a call to death() again - del_on_death = FALSE - ghostize() - qdel(src) - else - health = 0 - icon_state = icon_dead - if(flip_on_death) - transform = transform.Turn(180) - density = 0 - if(collar_type) - collar_type = "[initial(collar_type)]_dead" - regenerate_icons() - -/mob/living/simple_animal/proc/CanAttack(atom/the_target) - if(see_invisible < the_target.invisibility) - return FALSE - if(ismob(the_target)) - var/mob/M = the_target - if(M.status_flags & GODMODE) - return FALSE - if(isliving(the_target)) - var/mob/living/L = the_target - if(L.stat != CONSCIOUS) - return FALSE - if(ismecha(the_target)) - var/obj/mecha/M = the_target - if(M.occupant) - return FALSE - if(isspacepod(the_target)) - var/obj/spacepod/S = the_target - if(S.pilot) - return FALSE - return TRUE - -/mob/living/simple_animal/handle_fire() - return TRUE - -/mob/living/simple_animal/IgniteMob() - return FALSE - -/mob/living/simple_animal/ExtinguishMob() - return - -/mob/living/simple_animal/revive() - ..() - health = maxHealth - icon = initial(icon) - icon_state = icon_living - density = initial(density) - update_canmove() - flying = initial(flying) - if(collar_type) - collar_type = "[initial(collar_type)]" - regenerate_icons() - -/mob/living/simple_animal/proc/make_babies() // <3 <3 <3 - if(gender != FEMALE || stat || next_scan_time > world.time || !childtype || !animal_species || !SSticker.IsRoundInProgress()) - return FALSE - next_scan_time = world.time + 400 - - var/alone = TRUE - var/mob/living/simple_animal/partner - var/children = 0 - - for(var/mob/M in oview(7, src)) - if(M.stat != CONSCIOUS) //Check if it's conscious FIRST. - continue - else if(istype(M, childtype)) //Check for children SECOND. - children++ - else if(istype(M, animal_species)) - if(M.ckey) - continue - else if(!istype(M, childtype) && M.gender == MALE) //Better safe than sorry ;_; - partner = M - else if(isliving(M) && !faction_check_mob(M)) //shyness check. we're not shy in front of things that share a faction with us. - return //we never mate when not alone, so just abort early - - if(alone && partner && children < 3) - var/childspawn = pickweight(childtype) - var/turf/target = get_turf(loc) - if(target) - return new childspawn(target) - -/mob/living/simple_animal/show_inv(mob/user as mob) - if(!can_collar) - return - - user.set_machine(src) - var/dat = "
        Collar:[(pcollar && !(pcollar.flags & ABSTRACT)) ? pcollar : "Empty"]
        " - dat += "Close" - - var/datum/browser/popup = new(user, "mob\ref[src]", "[src]", 440, 250) - popup.set_content(dat) - popup.open() - -/mob/living/simple_animal/get_item_by_slot(slot_id) - switch(slot_id) - if(slot_collar) - return pcollar - . = ..() - -/mob/living/simple_animal/can_equip(obj/item/I, slot, disable_warning = 0) - // . = ..() // Do not call parent. We do not want animals using their hand slots. - switch(slot) - if(slot_collar) - if(pcollar) - return FALSE - if(!can_collar) - return FALSE - if(!istype(I, /obj/item/clothing/accessory/petcollar)) - return FALSE - return TRUE - -/mob/living/simple_animal/equip_to_slot(obj/item/W, slot) - if(!istype(W)) - return FALSE - - if(!slot) - return FALSE - - W.layer = ABOVE_HUD_LAYER - W.plane = ABOVE_HUD_PLANE - - switch(slot) - if(slot_collar) - add_collar(W) - -/mob/living/simple_animal/unEquip(obj/item/I, force) - . = ..() - if(!. || !I) - return - - if(I == pcollar) - pcollar = null - regenerate_icons() - -/mob/living/simple_animal/get_access() - . = ..() - if(pcollar) - . |= pcollar.GetAccess() - -/mob/living/simple_animal/update_canmove(delay_action_updates = 0) - if(paralysis || stunned || IsWeakened() || stat || resting) - drop_r_hand() - drop_l_hand() - canmove = 0 - else if(buckled) - canmove = 0 - else - canmove = 1 - if(!canmove) - walk(src, 0) //stop mid walk - - update_transform() - if(!delay_action_updates) - update_action_buttons_icon() - return canmove - -/mob/living/simple_animal/update_transform() - var/matrix/ntransform = matrix(transform) //aka transform.Copy() - var/changed = FALSE - - if(resize != RESIZE_DEFAULT_SIZE) - changed = TRUE - ntransform.Scale(resize) - resize = RESIZE_DEFAULT_SIZE - - if(changed) - animate(src, transform = ntransform, time = 2, easing = EASE_IN|EASE_OUT) - -/mob/living/simple_animal/proc/sentience_act() //Called when a simple animal gains sentience via gold slime potion - toggle_ai(AI_OFF) - can_have_ai = FALSE - -/mob/living/simple_animal/grant_death_vision() - sight |= SEE_TURFS - sight |= SEE_MOBS - sight |= SEE_OBJS - see_in_dark = 8 - see_invisible = SEE_INVISIBLE_OBSERVER - sync_lighting_plane_alpha() - -/mob/living/simple_animal/update_sight() - if(!client) - return - - if(stat == DEAD) - grant_death_vision() - return - - see_invisible = initial(see_invisible) - see_in_dark = initial(see_in_dark) - sight = initial(sight) - - if(client.eye != src) - var/atom/A = client.eye - if(A.update_remote_sight(src)) //returns 1 if we override all other sight updates. - return - - SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT) - sync_lighting_plane_alpha() - -/mob/living/simple_animal/proc/toggle_ai(togglestatus) - if(!can_have_ai && (togglestatus != AI_OFF)) - return - if(AIStatus != togglestatus) - if(togglestatus > 0 && togglestatus < 5) - if(togglestatus == AI_Z_OFF || AIStatus == AI_Z_OFF) - var/turf/T = get_turf(src) - if(AIStatus == AI_Z_OFF) - SSidlenpcpool.idle_mobs_by_zlevel[T.z] -= src - else - SSidlenpcpool.idle_mobs_by_zlevel[T.z] += src - GLOB.simple_animals[AIStatus] -= src - GLOB.simple_animals[togglestatus] += src - AIStatus = togglestatus - else - stack_trace("Something attempted to set simple animals AI to an invalid state: [togglestatus]") - -/mob/living/simple_animal/proc/consider_wakeup() - if(pulledby || shouldwakeup) - toggle_ai(AI_ON) - -/mob/living/simple_animal/adjustHealth(amount, updating_health = TRUE) - . = ..() - if(!ckey && !stat)//Not unconscious - if(AIStatus == AI_IDLE) - toggle_ai(AI_ON) - -/mob/living/simple_animal/onTransitZ(old_z, new_z) - ..() - if(AIStatus == AI_Z_OFF) - SSidlenpcpool.idle_mobs_by_zlevel[old_z] -= src - toggle_ai(initial(AIStatus)) - -/mob/living/simple_animal/proc/add_collar(obj/item/clothing/accessory/petcollar/P, mob/user) - if(QDELETED(P) || pcollar) - return - if(user && !user.unEquip(P)) - return - P.forceMove(src) - P.equipped(src) - pcollar = P - regenerate_icons() - if(user) - to_chat(user, "You put [P] around [src]'s neck.") - if(P.tagname && !unique_pet) - name = P.tagname - real_name = P.tagname - -/mob/living/simple_animal/regenerate_icons() - if(pcollar && collar_type) - cut_overlays() - add_overlay("[collar_type]collar") - add_overlay("[collar_type]tag") \ No newline at end of file +/mob/living/simple_animal + name = "animal" + icon = 'icons/mob/animal.dmi' + health = 20 + maxHealth = 20 + gender = PLURAL //placeholder + + universal_understand = 1 + universal_speak = 0 + status_flags = CANPUSH + + var/icon_living = "" + var/icon_dead = "" + var/icon_resting = "" + var/icon_gib = null //We only try to show a gibbing animation if this exists. + var/flip_on_death = FALSE //Flip the sprite upside down on death. Mostly here for things lacking custom dead sprites. + + var/list/speak = list() + var/speak_chance = 0 + var/list/emote_hear = list() //Hearable emotes + var/list/emote_see = list() //Unlike speak_emote, the list of things in this variable only show by themselves with no spoken text. IE: Ian barks, Ian yaps + + var/turns_per_move = 1 + var/turns_since_move = 0 + var/stop_automated_movement = 0 //Use this to temporarely stop random movement or to if you write special movement code for animals. + var/wander = 1 // Does the mob wander around when idle? + var/stop_automated_movement_when_pulled = 1 //When set to 1 this stops the animal from moving when someone is pulling it. + + //Interaction + var/response_help = "pokes" + var/response_disarm = "shoves" + var/response_harm = "hits" + var/harm_intent_damage = 3 + var/force_threshold = 0 //Minimum force required to deal any damage + + //Temperature effect + var/minbodytemp = 250 + var/maxbodytemp = 350 + var/heat_damage_per_tick = 2 //amount of damage applied if animal's body temperature is higher than maxbodytemp + var/cold_damage_per_tick = 2 //same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp + + //Healable by medical stacks? Defaults to yes. + var/healable = 1 + + //Atmos effect - Yes, you can make creatures that require plasma or co2 to survive. N2O is a trace gas and handled separately, hence why it isn't here. It'd be hard to add it. Hard and me don't mix (Yes, yes make all the dick jokes you want with that.) - Errorage + var/list/atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) //Leaving something at 0 means it's off - has no maximum + var/unsuitable_atmos_damage = 2 //This damage is taken when atmos doesn't fit all the requirements above + + //LETTING SIMPLE ANIMALS ATTACK? WHAT COULD GO WRONG. Defaults to zero so Ian can still be cuddly + var/melee_damage_lower = 0 + var/melee_damage_upper = 0 + var/obj_damage = 0 //how much damage this simple animal does to objects, if any + var/armour_penetration = 0 //How much armour they ignore, as a flat reduction from the targets armour value + var/melee_damage_type = BRUTE //Damage type of a simple mob's melee attack, should it do damage. + var/list/damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1) // 1 for full damage , 0 for none , -1 for 1:1 heal from that source + var/attacktext = "attacks" + var/attack_sound = null + var/friendly = "nuzzles" //If the mob does no damage with it's attack + var/environment_smash = ENVIRONMENT_SMASH_NONE //Set to 1 to allow breaking of crates,lockers,racks,tables; 2 for walls; 3 for Rwalls + + var/speed = 1 //LETS SEE IF I CAN SET SPEEDS FOR SIMPLE MOBS WITHOUT DESTROYING EVERYTHING. Higher speed is slower, negative speed is faster + var/can_hide = 0 + + var/obj/item/clothing/accessory/petcollar/pcollar = null + var/collar_type //if the mob has collar sprites, define them. + var/unique_pet = FALSE // if the mob can be renamed + var/can_collar = FALSE // can add collar to mob or not + + //Hot simple_animal baby making vars + var/list/childtype = null + var/next_scan_time = 0 + var/animal_species //Sorry, no spider+corgi buttbabies. + + var/buffed = 0 //In the event that you want to have a buffing effect on the mob, but don't want it to stack with other effects, any outside force that applies a buff to a simple mob should at least set this to 1, so we have something to check against + var/gold_core_spawnable = NO_SPAWN //If the mob can be spawned with a gold slime core. HOSTILE_SPAWN are spawned with plasma, FRIENDLY_SPAWN are spawned with blood + + var/mob/living/carbon/human/master_commander = null //holding var for determining who own/controls a sentient simple animal (for sentience potions). + + var/datum/component/spawner/nest + + var/sentience_type = SENTIENCE_ORGANIC // Sentience type, for slime potions + + var/list/loot = list() //list of things spawned at mob's loc when it dies + var/del_on_death = 0 //causes mob to be deleted on death, useful for mobs that spawn lootable corpses + var/deathmessage = "" + var/death_sound = null //The sound played on death + + var/allow_movement_on_non_turfs = FALSE + + var/attacked_sound = "punch" + + var/AIStatus = AI_ON //The Status of our AI, can be set to AI_ON (On, usual processing), AI_IDLE (Will not process, but will return to AI_ON if an enemy comes near), AI_OFF (Off, Not processing ever) + var/can_have_ai = TRUE //once we have become sentient, we can never go back + + var/shouldwakeup = FALSE //convenience var for forcibly waking up an idling AI on next check. + + //domestication + var/tame = 0 + + var/my_z // I don't want to confuse this with client registered_z + +/mob/living/simple_animal/Initialize(mapload) + . = ..() + GLOB.simple_animals[AIStatus] += src + if(gender == PLURAL) + gender = pick(MALE, FEMALE) + if(!real_name) + real_name = name + if(!loc) + stack_trace("Simple animal being instantiated in nullspace") + verbs -= /mob/verb/observe + if(!can_hide) + verbs -= /mob/living/simple_animal/verb/hide + if(pcollar) + pcollar = new(src) + regenerate_icons() + +/mob/living/simple_animal/Destroy() + QDEL_NULL(pcollar) + master_commander = null + GLOB.simple_animals[AIStatus] -= src + if(SSnpcpool.state == SS_PAUSED && LAZYLEN(SSnpcpool.currentrun)) + SSnpcpool.currentrun -= src + + if(nest) + nest.spawned_mobs -= src + nest = null + + var/turf/T = get_turf(src) + if (T && AIStatus == AI_Z_OFF) + SSidlenpcpool.idle_mobs_by_zlevel[T.z] -= src + + return ..() + +/mob/living/simple_animal/handle_atom_del(atom/A) + if(A == pcollar) + pcollar = null + return ..() + +/mob/living/simple_animal/examine(mob/user) + . = ..() + if(stat == DEAD) + . += "Upon closer examination, [p_they()] appear[p_s()] to be dead." + +/mob/living/simple_animal/updatehealth(reason = "none given") + ..(reason) + health = Clamp(health, 0, maxHealth) + med_hud_set_status() + +/mob/living/simple_animal/StartResting(updating = 1) + ..() + if(icon_resting && stat != DEAD) + icon_state = icon_resting + if(collar_type) + collar_type = "[initial(collar_type)]_rest" + regenerate_icons() + +/mob/living/simple_animal/StopResting(updating = 1) + ..() + if(icon_resting && stat != DEAD) + icon_state = icon_living + if(collar_type) + collar_type = "[initial(collar_type)]" + regenerate_icons() + +/mob/living/simple_animal/update_stat(reason = "none given") + if(status_flags & GODMODE) + return + + ..(reason) + if(stat != DEAD) + if(health < 1) + death() + create_debug_log("died of damage, trigger reason: [reason]") + +/mob/living/simple_animal/proc/handle_automated_action() + set waitfor = FALSE + return + +/mob/living/simple_animal/proc/handle_automated_movement() + set waitfor = FALSE + if(!stop_automated_movement && wander) + if((isturf(loc) || allow_movement_on_non_turfs) && !resting && !buckled && canmove) //This is so it only moves if it's not inside a closet, gentics machine, etc. + turns_since_move++ + if(turns_since_move >= turns_per_move) + if(!(stop_automated_movement_when_pulled && pulledby)) //Soma animals don't move when pulled + var/anydir = pick(cardinal) + if(Process_Spacemove(anydir)) + Move(get_step(src,anydir), anydir) + turns_since_move = 0 + return 1 + +/mob/living/simple_animal/proc/handle_automated_speech(override) + set waitfor = FALSE + if(speak_chance) + if(prob(speak_chance) || override) + if(speak && speak.len) + if((emote_hear && emote_hear.len) || (emote_see && emote_see.len)) + var/length = speak.len + if(emote_hear && emote_hear.len) + length += emote_hear.len + if(emote_see && emote_see.len) + length += emote_see.len + var/randomValue = rand(1,length) + if(randomValue <= speak.len) + say(pick(speak)) + else + randomValue -= speak.len + if(emote_see && randomValue <= emote_see.len) + custom_emote(1, pick(emote_see)) + else + custom_emote(2, pick(emote_hear)) + else + say(pick(speak)) + else + if(!(emote_hear && emote_hear.len) && (emote_see && emote_see.len)) + custom_emote(1, pick(emote_see)) + if((emote_hear && emote_hear.len) && !(emote_see && emote_see.len)) + custom_emote(2, pick(emote_hear)) + if((emote_hear && emote_hear.len) && (emote_see && emote_see.len)) + var/length = emote_hear.len + emote_see.len + var/pick = rand(1,length) + if(pick <= emote_see.len) + custom_emote(1, pick(emote_see)) + else + custom_emote(2,pick(emote_hear)) + + +/mob/living/simple_animal/handle_environment(datum/gas_mixture/environment) + var/atmos_suitable = 1 + + var/areatemp = get_temperature(environment) + + if(abs(areatemp - bodytemperature) > 5 && !(BREATHLESS in mutations)) + var/diff = areatemp - bodytemperature + diff = diff / 5 + bodytemperature += diff + + var/tox = environment.toxins + var/oxy = environment.oxygen + var/n2 = environment.nitrogen + var/co2 = environment.carbon_dioxide + + if(atmos_requirements["min_oxy"] && oxy < atmos_requirements["min_oxy"]) + atmos_suitable = 0 + throw_alert("not_enough_oxy", /obj/screen/alert/not_enough_oxy) + else if(atmos_requirements["max_oxy"] && oxy > atmos_requirements["max_oxy"]) + atmos_suitable = 0 + throw_alert("too_much_oxy", /obj/screen/alert/too_much_oxy) + else + clear_alert("not_enough_oxy") + clear_alert("too_much_oxy") + + if(atmos_requirements["min_tox"] && tox < atmos_requirements["min_tox"]) + atmos_suitable = 0 + throw_alert("not_enough_tox", /obj/screen/alert/not_enough_tox) + else if(atmos_requirements["max_tox"] && tox > atmos_requirements["max_tox"]) + atmos_suitable = 0 + throw_alert("too_much_tox", /obj/screen/alert/too_much_tox) + else + clear_alert("too_much_tox") + clear_alert("not_enough_tox") + + if(atmos_requirements["min_n2"] && n2 < atmos_requirements["min_n2"]) + atmos_suitable = 0 + else if(atmos_requirements["max_n2"] && n2 > atmos_requirements["max_n2"]) + atmos_suitable = 0 + + if(atmos_requirements["min_co2"] && co2 < atmos_requirements["min_co2"]) + atmos_suitable = 0 + else if(atmos_requirements["max_co2"] && co2 > atmos_requirements["max_co2"]) + atmos_suitable = 0 + + if(!atmos_suitable) + adjustHealth(unsuitable_atmos_damage) + + handle_temperature_damage() + +/mob/living/simple_animal/proc/handle_temperature_damage() + if(bodytemperature < minbodytemp) + adjustHealth(cold_damage_per_tick) + else if(bodytemperature > maxbodytemp) + adjustHealth(heat_damage_per_tick) + +/mob/living/simple_animal/gib() + if(icon_gib) + flick(icon_gib, src) + if(butcher_results) + var/atom/Tsec = drop_location() + for(var/path in butcher_results) + for(var/i in 1 to butcher_results[path]) + new path(Tsec) + if(pcollar) + pcollar.forceMove(drop_location()) + pcollar = null + ..() + +/mob/living/simple_animal/emote(act, m_type = 1, message = null, force) + if(stat) + return + act = lowertext(act) + switch(act) //IMPORTANT: Emotes MUST NOT CONFLICT anywhere along the chain. + if("scream") + message = "\The [src] whimpers." + m_type = 2 + if("help") + to_chat(src, "scream") + + ..() + +/mob/living/simple_animal/say_quote(message) + var/verb = "says" + + if(speak_emote.len) + verb = pick(speak_emote) + + return verb + +/mob/living/simple_animal/movement_delay() + . = ..() + + . = speed + + . += config.animal_delay + +/mob/living/simple_animal/Stat() + ..() + if(statpanel("Status")) + stat(null, "Health: [round((health / maxHealth) * 100)]%") + return TRUE + +/mob/living/simple_animal/proc/drop_loot() + if(loot.len) + for(var/i in loot) + new i(loc) + +/mob/living/simple_animal/revive() + ..() + density = initial(density) + +/mob/living/simple_animal/death(gibbed) + // Only execute the below if we successfully died + . = ..() + if(!.) + return FALSE + flying = FALSE + if(nest) + nest.spawned_mobs -= src + nest = null + drop_loot() + if(!gibbed) + if(death_sound) + playsound(get_turf(src),death_sound, 200, 1) + if(deathmessage) + visible_message("\The [src] [deathmessage]") + else if(!del_on_death) + visible_message("\The [src] stops moving...") + if(del_on_death) + //Prevent infinite loops if the mob Destroy() is overridden in such + //a manner as to cause a call to death() again + del_on_death = FALSE + ghostize() + qdel(src) + else + health = 0 + icon_state = icon_dead + if(flip_on_death) + transform = transform.Turn(180) + density = 0 + if(collar_type) + collar_type = "[initial(collar_type)]_dead" + regenerate_icons() + +/mob/living/simple_animal/proc/CanAttack(atom/the_target) + if(see_invisible < the_target.invisibility) + return FALSE + if(ismob(the_target)) + var/mob/M = the_target + if(M.status_flags & GODMODE) + return FALSE + if(isliving(the_target)) + var/mob/living/L = the_target + if(L.stat != CONSCIOUS) + return FALSE + if(ismecha(the_target)) + var/obj/mecha/M = the_target + if(M.occupant) + return FALSE + if(isspacepod(the_target)) + var/obj/spacepod/S = the_target + if(S.pilot) + return FALSE + return TRUE + +/mob/living/simple_animal/handle_fire() + return TRUE + +/mob/living/simple_animal/IgniteMob() + return FALSE + +/mob/living/simple_animal/ExtinguishMob() + return + +/mob/living/simple_animal/revive() + ..() + health = maxHealth + icon = initial(icon) + icon_state = icon_living + density = initial(density) + update_canmove() + flying = initial(flying) + if(collar_type) + collar_type = "[initial(collar_type)]" + regenerate_icons() + +/mob/living/simple_animal/proc/make_babies() // <3 <3 <3 + if(gender != FEMALE || stat || next_scan_time > world.time || !childtype || !animal_species || !SSticker.IsRoundInProgress()) + return FALSE + next_scan_time = world.time + 400 + + var/alone = TRUE + var/mob/living/simple_animal/partner + var/children = 0 + + for(var/mob/M in oview(7, src)) + if(M.stat != CONSCIOUS) //Check if it's conscious FIRST. + continue + else if(istype(M, childtype)) //Check for children SECOND. + children++ + else if(istype(M, animal_species)) + if(M.ckey) + continue + else if(!istype(M, childtype) && M.gender == MALE) //Better safe than sorry ;_; + partner = M + else if(isliving(M) && !faction_check_mob(M)) //shyness check. we're not shy in front of things that share a faction with us. + return //we never mate when not alone, so just abort early + + if(alone && partner && children < 3) + var/childspawn = pickweight(childtype) + var/turf/target = get_turf(loc) + if(target) + return new childspawn(target) + +/mob/living/simple_animal/show_inv(mob/user as mob) + if(!can_collar) + return + + user.set_machine(src) + var/dat = "
        Collar:[(pcollar && !(pcollar.flags & ABSTRACT)) ? pcollar : "Empty"]
        " + dat += "Close" + + var/datum/browser/popup = new(user, "mob\ref[src]", "[src]", 440, 250) + popup.set_content(dat) + popup.open() + +/mob/living/simple_animal/get_item_by_slot(slot_id) + switch(slot_id) + if(slot_collar) + return pcollar + . = ..() + +/mob/living/simple_animal/can_equip(obj/item/I, slot, disable_warning = 0) + // . = ..() // Do not call parent. We do not want animals using their hand slots. + switch(slot) + if(slot_collar) + if(pcollar) + return FALSE + if(!can_collar) + return FALSE + if(!istype(I, /obj/item/clothing/accessory/petcollar)) + return FALSE + return TRUE + +/mob/living/simple_animal/equip_to_slot(obj/item/W, slot) + if(!istype(W)) + return FALSE + + if(!slot) + return FALSE + + W.layer = ABOVE_HUD_LAYER + W.plane = ABOVE_HUD_PLANE + + switch(slot) + if(slot_collar) + add_collar(W) + +/mob/living/simple_animal/unEquip(obj/item/I, force) + . = ..() + if(!. || !I) + return + + if(I == pcollar) + pcollar = null + regenerate_icons() + +/mob/living/simple_animal/get_access() + . = ..() + if(pcollar) + . |= pcollar.GetAccess() + +/mob/living/simple_animal/update_canmove(delay_action_updates = 0) + if(paralysis || stunned || IsWeakened() || stat || resting) + drop_r_hand() + drop_l_hand() + canmove = 0 + else if(buckled) + canmove = 0 + else + canmove = 1 + if(!canmove) + walk(src, 0) //stop mid walk + + update_transform() + if(!delay_action_updates) + update_action_buttons_icon() + return canmove + +/mob/living/simple_animal/update_transform() + var/matrix/ntransform = matrix(transform) //aka transform.Copy() + var/changed = FALSE + + if(resize != RESIZE_DEFAULT_SIZE) + changed = TRUE + ntransform.Scale(resize) + resize = RESIZE_DEFAULT_SIZE + + if(changed) + animate(src, transform = ntransform, time = 2, easing = EASE_IN|EASE_OUT) + +/mob/living/simple_animal/proc/sentience_act() //Called when a simple animal gains sentience via gold slime potion + toggle_ai(AI_OFF) + can_have_ai = FALSE + +/mob/living/simple_animal/grant_death_vision() + sight |= SEE_TURFS + sight |= SEE_MOBS + sight |= SEE_OBJS + see_in_dark = 8 + see_invisible = SEE_INVISIBLE_OBSERVER + sync_lighting_plane_alpha() + +/mob/living/simple_animal/update_sight() + if(!client) + return + + if(stat == DEAD) + grant_death_vision() + return + + see_invisible = initial(see_invisible) + see_in_dark = initial(see_in_dark) + sight = initial(sight) + + if(client.eye != src) + var/atom/A = client.eye + if(A.update_remote_sight(src)) //returns 1 if we override all other sight updates. + return + + SEND_SIGNAL(src, COMSIG_MOB_UPDATE_SIGHT) + sync_lighting_plane_alpha() + +/mob/living/simple_animal/proc/toggle_ai(togglestatus) + if(!can_have_ai && (togglestatus != AI_OFF)) + return + if(AIStatus != togglestatus) + if(togglestatus > 0 && togglestatus < 5) + if(togglestatus == AI_Z_OFF || AIStatus == AI_Z_OFF) + var/turf/T = get_turf(src) + if(AIStatus == AI_Z_OFF) + SSidlenpcpool.idle_mobs_by_zlevel[T.z] -= src + else + SSidlenpcpool.idle_mobs_by_zlevel[T.z] += src + GLOB.simple_animals[AIStatus] -= src + GLOB.simple_animals[togglestatus] += src + AIStatus = togglestatus + else + stack_trace("Something attempted to set simple animals AI to an invalid state: [togglestatus]") + +/mob/living/simple_animal/proc/consider_wakeup() + if(pulledby || shouldwakeup) + toggle_ai(AI_ON) + +/mob/living/simple_animal/adjustHealth(amount, updating_health = TRUE) + . = ..() + if(!ckey && !stat)//Not unconscious + if(AIStatus == AI_IDLE) + toggle_ai(AI_ON) + +/mob/living/simple_animal/onTransitZ(old_z, new_z) + ..() + if(AIStatus == AI_Z_OFF) + SSidlenpcpool.idle_mobs_by_zlevel[old_z] -= src + toggle_ai(initial(AIStatus)) + +/mob/living/simple_animal/proc/add_collar(obj/item/clothing/accessory/petcollar/P, mob/user) + if(QDELETED(P) || pcollar) + return + if(user && !user.unEquip(P)) + return + P.forceMove(src) + P.equipped(src) + pcollar = P + regenerate_icons() + if(user) + to_chat(user, "You put [P] around [src]'s neck.") + if(P.tagname && !unique_pet) + name = P.tagname + real_name = P.tagname + +/mob/living/simple_animal/regenerate_icons() + if(pcollar && collar_type) + cut_overlays() + add_overlay("[collar_type]collar") + add_overlay("[collar_type]tag") diff --git a/code/modules/mob/living/simple_animal/slime/death.dm b/code/modules/mob/living/simple_animal/slime/death.dm index 29a1adbd347..cf72363ac9a 100644 --- a/code/modules/mob/living/simple_animal/slime/death.dm +++ b/code/modules/mob/living/simple_animal/slime/death.dm @@ -29,4 +29,4 @@ /mob/living/simple_animal/slime/gib() death(TRUE) - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/modules/mob/living/simple_animal/slime/emote.dm b/code/modules/mob/living/simple_animal/slime/emote.dm index 7fc7f4c5f87..94806ede9d1 100644 --- a/code/modules/mob/living/simple_animal/slime/emote.dm +++ b/code/modules/mob/living/simple_animal/slime/emote.dm @@ -102,4 +102,4 @@ loc.audible_message(message) if(regenerate_icons) - regenerate_icons() \ No newline at end of file + regenerate_icons() diff --git a/code/modules/mob/living/simple_animal/slime/life.dm b/code/modules/mob/living/simple_animal/slime/life.dm index efd4c19d55c..deb9981645f 100644 --- a/code/modules/mob/living/simple_animal/slime/life.dm +++ b/code/modules/mob/living/simple_animal/slime/life.dm @@ -614,4 +614,4 @@ return 0 if(holding_still) return 0 - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/mob/living/simple_animal/slime/say.dm b/code/modules/mob/living/simple_animal/slime/say.dm index 01269aab8aa..74241dc676c 100644 --- a/code/modules/mob/living/simple_animal/slime/say.dm +++ b/code/modules/mob/living/simple_animal/slime/say.dm @@ -9,7 +9,7 @@ return verb -/mob/living/simple_animal/slime/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol) +/mob/living/simple_animal/slime/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol, sound_frequency) if(speaker != src && !stat) if(speaker in Friends) speech_buffer = list() diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm index f42bc2b6b74..339240af529 100644 --- a/code/modules/mob/living/simple_animal/slime/slime.dm +++ b/code/modules/mob/living/simple_animal/slime/slime.dm @@ -422,7 +422,7 @@ visible_message("The mutated core shudders, and collapses into a puddle, unable to maintain its form.") qdel(src) */ -/mob/living/simple_animal/slime/water_act(volume, temperature, source, method = TOUCH) +/mob/living/simple_animal/slime/water_act(volume, temperature, source, method = REAGENT_TOUCH) . = ..() var/water_damage = rand(10, 15) * volume adjustBruteLoss(water_damage) @@ -507,4 +507,4 @@ if(buckled) to_chat(src, "I can't vent crawl while feeding...") return - ..() \ No newline at end of file + ..() diff --git a/code/modules/mob/living/stat_states.dm b/code/modules/mob/living/stat_states.dm index 70cced62aab..57fd6e17cbc 100644 --- a/code/modules/mob/living/stat_states.dm +++ b/code/modules/mob/living/stat_states.dm @@ -73,4 +73,4 @@ return 1 /mob/living/proc/check_death_method() - return TRUE \ No newline at end of file + return TRUE diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm index 72f136304d7..2e5c6e66137 100644 --- a/code/modules/mob/living/status_procs.dm +++ b/code/modules/mob/living/status_procs.dm @@ -598,4 +598,4 @@ else if(priority_absorb_key["self_message"]) to_chat(src, "[priority_absorb_key["self_message"]]") priority_absorb_key["stuns_absorbed"] += amount - return TRUE \ No newline at end of file + return TRUE diff --git a/code/modules/mob/living/taste.dm b/code/modules/mob/living/taste.dm index a403aa39244..03b0ff5752b 100644 --- a/code/modules/mob/living/taste.dm +++ b/code/modules/mob/living/taste.dm @@ -27,4 +27,4 @@ // "something indescribable" -> too many tastes, not enough flavor. last_taste_time = world.time - last_taste_text = text_output \ No newline at end of file + last_taste_text = text_output diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm index 66b4f2f9a16..ada7669a520 100644 --- a/code/modules/mob/login.dm +++ b/code/modules/mob/login.dm @@ -1,70 +1,70 @@ -//handles setting lastKnownIP and computer_id for use by the ban systems as well as checking for multikeying -/mob/proc/update_Login_details() - //Multikey checks and logging - lastKnownIP = client.address - computer_id = client.computer_id - log_access_in(client) - create_attack_log("Logged in at [atom_loc_line(get_turf(src))]") - if(config.log_access) - for(var/mob/M in GLOB.player_list) - if(M == src) continue - if( M.key && (M.key != key) ) - var/matches - if( (M.lastKnownIP == client.address) ) - matches += "IP ([client.address])" - if( (M.computer_id == client.computer_id) ) - if(matches) matches += " and " - matches += "ID ([client.computer_id])" - if(!config.disable_cid_warn_popup) - spawn() alert("You have logged in already with another key this round, please log out of this one NOW or risk being banned!") - if(matches) - if(M.client) - message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(M)].", 1) - log_adminwarn("Notice: [key_name(src)] has the same [matches] as [key_name(M)].") - else - message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(M)] (no longer logged in). ", 1) - log_adminwarn("Notice: [key_name(src)] has the same [matches] as [key_name(M)] (no longer logged in).") - -/mob/Login() - GLOB.player_list |= src - update_Login_details() - world.update_status() - - client.images = null //remove the images such as AIs being unable to see runes - client.screen = list() //remove hud items just in case - if(client.click_intercept) - client.click_intercept.quit() // Let's not keep any old click_intercepts - - if(!hud_used) - create_mob_hud() - if(hud_used) - hud_used.show_hud(hud_used.hud_version) - - next_move = 1 - sight |= SEE_SELF - ..() - - reset_perspective(loc) - - - if(ckey in GLOB.deadmins) - verbs += /client/proc/readmin - - //Clear ability list and update from mob. - client.verbs -= ability_verbs - - if(abilities) - client.verbs |= abilities - - //HUD updates (antag hud, etc) - //readd this mob's HUDs (antag, med, etc) - reload_huds() - - add_click_catcher() - - if(viewing_alternate_appearances && viewing_alternate_appearances.len) - for(var/datum/alternate_appearance/AA in viewing_alternate_appearances) - AA.display_to(list(src)) - - update_client_colour(0) - update_morgue() +//handles setting lastKnownIP and computer_id for use by the ban systems as well as checking for multikeying +/mob/proc/update_Login_details() + //Multikey checks and logging + lastKnownIP = client.address + computer_id = client.computer_id + log_access_in(client) + create_attack_log("Logged in at [atom_loc_line(get_turf(src))]") + if(config.log_access) + for(var/mob/M in GLOB.player_list) + if(M == src) continue + if( M.key && (M.key != key) ) + var/matches + if( (M.lastKnownIP == client.address) ) + matches += "IP ([client.address])" + if( (M.computer_id == client.computer_id) ) + if(matches) matches += " and " + matches += "ID ([client.computer_id])" + if(!config.disable_cid_warn_popup) + spawn() alert("You have logged in already with another key this round, please log out of this one NOW or risk being banned!") + if(matches) + if(M.client) + message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(M)].", 1) + log_adminwarn("Notice: [key_name(src)] has the same [matches] as [key_name(M)].") + else + message_admins("Notice: [key_name_admin(src)] has the same [matches] as [key_name_admin(M)] (no longer logged in). ", 1) + log_adminwarn("Notice: [key_name(src)] has the same [matches] as [key_name(M)] (no longer logged in).") + +/mob/Login() + GLOB.player_list |= src + update_Login_details() + world.update_status() + + client.images = null //remove the images such as AIs being unable to see runes + client.screen = list() //remove hud items just in case + if(client.click_intercept) + client.click_intercept.quit() // Let's not keep any old click_intercepts + + if(!hud_used) + create_mob_hud() + if(hud_used) + hud_used.show_hud(hud_used.hud_version) + + next_move = 1 + sight |= SEE_SELF + ..() + + reset_perspective(loc) + + + if(ckey in GLOB.deadmins) + verbs += /client/proc/readmin + + //Clear ability list and update from mob. + client.verbs -= ability_verbs + + if(abilities) + client.verbs |= abilities + + //HUD updates (antag hud, etc) + //readd this mob's HUDs (antag, med, etc) + reload_huds() + + add_click_catcher() + + if(viewing_alternate_appearances && viewing_alternate_appearances.len) + for(var/datum/alternate_appearance/AA in viewing_alternate_appearances) + AA.display_to(list(src)) + + update_client_colour(0) + update_morgue() diff --git a/code/modules/mob/logout.dm b/code/modules/mob/logout.dm index 376b8d46ea5..1e019d16529 100644 --- a/code/modules/mob/logout.dm +++ b/code/modules/mob/logout.dm @@ -1,20 +1,20 @@ -/mob/Logout() - SSnanoui.user_logout(src) // this is used to clean up (remove) this user's Nano UIs - unset_machine() - GLOB.player_list -= src - log_access_out(src) - create_attack_log("Logged out at [atom_loc_line(get_turf(src))]") - // `holder` is nil'd out by now, so we check the `admin_datums` array directly - //Only report this stuff if we are currently playing. - if(admin_datums[ckey] && SSticker && SSticker.current_state == GAME_STATE_PLAYING) - var/datum/admins/temp_admin = admin_datums[ckey] - // Triggers on people with banhammer power only - no mentors tripping the alarm - if(temp_admin.rights & R_BAN) - message_admins("Admin logout: [key_name_admin(src)]") - var/list/admincounter = staff_countup(R_BAN) - if(admincounter[1] == 0) // No active admins - send2irc(config.admin_notify_irc, "[key_name(src)] logged out - No active admins, [admincounter[2]] non-admin staff, [admincounter[3]] inactive staff.") - - ..() - update_morgue() - return 1 +/mob/Logout() + SSnanoui.user_logout(src) // this is used to clean up (remove) this user's Nano UIs + unset_machine() + GLOB.player_list -= src + log_access_out(src) + create_attack_log("Logged out at [atom_loc_line(get_turf(src))]") + // `holder` is nil'd out by now, so we check the `admin_datums` array directly + //Only report this stuff if we are currently playing. + if(admin_datums[ckey] && SSticker && SSticker.current_state == GAME_STATE_PLAYING) + var/datum/admins/temp_admin = admin_datums[ckey] + // Triggers on people with banhammer power only - no mentors tripping the alarm + if(temp_admin.rights & R_BAN) + message_admins("Admin logout: [key_name_admin(src)]") + var/list/admincounter = staff_countup(R_BAN) + if(admincounter[1] == 0) // No active admins + send2irc(config.admin_notify_irc, "[key_name(src)] logged out - No active admins, [admincounter[2]] non-admin staff, [admincounter[3]] inactive staff.") + + ..() + update_morgue() + return 1 diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 609940bc2a6..65d2cb4788e 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1406,4 +1406,4 @@ var/list/slot_equipment_priority = list( \ ///Force set the mob nutrition /mob/proc/set_nutrition(change) - nutrition = max(0, change) \ No newline at end of file + nutrition = max(0, change) diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 4091b8df9d7..5d63b7b4ad9 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -208,4 +208,4 @@ var/list/tkgrabbed_objects = list() // Assoc list of items to TK grabs var/forced_look = null // This can either be a numerical direction or a soft object reference (UID). It makes the mob always face towards the selected thing. - var/registered_z \ No newline at end of file + var/registered_z diff --git a/code/modules/mob/mob_grab.dm b/code/modules/mob/mob_grab.dm index 329a01907e2..f595f6b4839 100644 --- a/code/modules/mob/mob_grab.dm +++ b/code/modules/mob/mob_grab.dm @@ -1,452 +1,452 @@ -#define UPGRADE_COOLDOWN 40 -#define UPGRADE_KILL_TIMER 100 - -//times it takes for a mob to eat -#define EAT_TIME_XENO 30 -#define EAT_TIME_FAT 100 - -//time it takes for a mob to be eaten (in deciseconds) (overrides mob eat time) -#define EAT_TIME_ANIMAL 30 - -/obj/item/grab - name = "grab" - flags = NOBLUDGEON | ABSTRACT | DROPDEL - var/obj/screen/grab/hud = null - var/mob/living/affecting = null - var/mob/living/assailant = null - var/state = GRAB_PASSIVE - - var/allow_upgrade = 1 - var/last_upgrade = 0 - var/last_hit_zone = 0 -// var/force_down //determines if the affecting mob will be pinned to the ground //disabled due to balance, kept for an example for any new things. - var/dancing //determines if assailant and affecting keep looking at each other. Basically a wrestling position - - layer = 21 - plane = HUD_PLANE - item_state = "nothing" - icon = 'icons/mob/screen_gen.dmi' - w_class = WEIGHT_CLASS_BULKY - - -/obj/item/grab/New(var/mob/user, var/mob/victim) - ..() - - //Okay, first off, some fucking sanity checking. No user, or no victim, or they are not mobs, no grab. - if(!istype(user) || !istype(victim)) - return - - loc = user - assailant = user - affecting = victim - - if(affecting.anchored) - qdel(src) - return - - affecting.grabbed_by += src - - hud = new /obj/screen/grab(src) - hud.icon_state = "reinforce" - icon_state = "grabbed" - hud.name = "reinforce grab" - hud.master = src - - //check if assailant is grabbed by victim as well - if(assailant.grabbed_by) - for(var/obj/item/grab/G in assailant.grabbed_by) - if(G.assailant == affecting && G.affecting == assailant) - G.dancing = 1 - G.adjust_position() - dancing = 1 - - clean_grabbed_by(assailant, affecting) - adjust_position() - -/obj/item/grab/proc/clean_grabbed_by(var/mob/user, var/mob/victim) //Cleans up any nulls in the grabbed_by list. - if(istype(user)) - - for(var/entry in user.grabbed_by) - if(isnull(entry) || !istype(entry, /obj/item/grab)) //the isnull() here is probably redundant- but it might outperform istype, who knows. Better safe than sorry. - user.grabbed_by -= entry - - if(istype(victim)) - - for(var/entry in victim.grabbed_by) - if(isnull(entry) || !istype(entry, /obj/item/grab)) - victim.grabbed_by -= entry - - return 1 - -//Used by throw code to hand over the mob, instead of throwing the grab. The grab is then deleted by the throw code. -/obj/item/grab/proc/get_mob_if_throwable() - if(affecting && assailant.Adjacent(affecting)) - if(affecting.buckled) - return null - if(state >= GRAB_AGGRESSIVE) - return affecting - return null - -//This makes sure that the grab screen object is displayed in the correct hand. -/obj/item/grab/proc/synch() - if(affecting) - if(assailant.r_hand == src) - hud.screen_loc = ui_rhand - else - hud.screen_loc = ui_lhand - assailant.client.screen += hud - -/obj/item/grab/process() - if(!confirm()) - return //If the confirm fails, the grab is about to be deleted. That means it shouldn't continue processing. - - if(assailant.client) - if(!hud) - return //this somehow can runtime under the right circumstances - assailant.client.screen -= hud - assailant.client.screen += hud - - var/hit_zone = assailant.zone_selected - last_hit_zone = hit_zone - - if(assailant.pulling == affecting) - assailant.stop_pulling() - - if(state <= GRAB_AGGRESSIVE) - allow_upgrade = 1 - //disallow upgrading if we're grabbing more than one person - if((assailant.l_hand && assailant.l_hand != src && istype(assailant.l_hand, /obj/item/grab))) - var/obj/item/grab/G = assailant.l_hand - if(G.affecting != affecting) - allow_upgrade = 0 - if((assailant.r_hand && assailant.r_hand != src && istype(assailant.r_hand, /obj/item/grab))) - var/obj/item/grab/G = assailant.r_hand - if(G.affecting != affecting) - allow_upgrade = 0 - - //disallow upgrading past aggressive if we're being grabbed aggressively - for(var/obj/item/grab/G in affecting.grabbed_by) - if(G == src) continue - if(G.state >= GRAB_AGGRESSIVE) - allow_upgrade = 0 - - if(allow_upgrade) - if(state < GRAB_AGGRESSIVE) - hud.icon_state = "reinforce" - else - hud.icon_state = "reinforce1" - else - hud.icon_state = "!reinforce" - - if(state >= GRAB_AGGRESSIVE) - if(!HAS_TRAIT(assailant, TRAIT_PACIFISM)) - affecting.drop_r_hand() - affecting.drop_l_hand() - - - //var/announce = 0 - //(hit_zone != last_hit_zone) - //announce = 1 - /* if(ishuman(affecting)) - switch(hit_zone) - /*if("mouth") - if(announce) - assailant.visible_message("[assailant] covers [affecting]'s mouth!") - if(affecting.silent < 3) - affecting.silent = 3 - if("eyes") - if(announce) - assailant.visible_message("[assailant] covers [affecting]'s eyes!") - if(affecting.eye_blind < 3) - affecting.eye_blind = 3*///These are being left in the code as an example for adding new hit-zone based things. - - if(force_down) - if(affecting.loc != assailant.loc) - force_down = 0 - else - affecting.Weaken(3) //This is being left in the code as an example of adding a new variable to do something in grab code. - -*/ - - var/breathing_tube = affecting.get_organ_slot("breathing_tube") - - if(state >= GRAB_NECK) - affecting.Stun(5) //It will hamper your voice, being choked and all. - if(isliving(affecting) && !breathing_tube) - var/mob/living/L = affecting - L.adjustOxyLoss(1) - - if(state >= GRAB_KILL) - //affecting.apply_effect(STUTTER, 5) //would do this, but affecting isn't declared as mob/living for some stupid reason. - affecting.Stuttering(5) //It will hamper your voice, being choked and all. - affecting.Weaken(5) //Should keep you down unless you get help. - if(!breathing_tube) - affecting.AdjustLoseBreath(2, bound_lower = 0, bound_upper = 3) - - adjust_position() - - -/obj/item/grab/attack_self(mob/user) - s_click(hud) - -//Updating pixelshift, position and direction -//Gets called on process, when the grab gets upgraded or the assailant moves -/obj/item/grab/proc/adjust_position() - if(affecting.buckled) - return - if(affecting.lying && state != GRAB_KILL) - animate(affecting, pixel_x = 0, pixel_y = 0, 5, 1, LINEAR_EASING) - return //KJK - /* if(force_down) //THIS GOES ABOVE THE RETURN LABELED KJK - affecting.setDir(SOUTH)*///This shows how you can apply special directions based on a variable. //face up - - var/shift = 0 - var/adir = get_dir(assailant, affecting) - affecting.layer = 4 - switch(state) - if(GRAB_PASSIVE) - shift = 8 - if(dancing) //look at partner - shift = 10 - assailant.setDir(get_dir(assailant, affecting)) - if(GRAB_AGGRESSIVE) - shift = 12 - if(GRAB_NECK, GRAB_UPGRADING) - shift = -10 - adir = assailant.dir - affecting.setDir(assailant.dir) - affecting.forceMove(assailant.loc) - if(GRAB_KILL) - shift = 0 - adir = 1 - affecting.setDir(SOUTH)//face up - affecting.forceMove(assailant.loc) - - switch(adir) - if(NORTH) - animate(affecting, pixel_x = 0, pixel_y =-shift, 5, 1, LINEAR_EASING) - affecting.layer = 3.9 - if(SOUTH) - animate(affecting, pixel_x = 0, pixel_y = shift, 5, 1, LINEAR_EASING) - if(WEST) - animate(affecting, pixel_x = shift, pixel_y = 0, 5, 1, LINEAR_EASING) - if(EAST) - animate(affecting, pixel_x =-shift, pixel_y = 0, 5, 1, LINEAR_EASING) - -/obj/item/grab/proc/s_click(obj/screen/S) - if(!affecting) - return - if(state >= GRAB_AGGRESSIVE && HAS_TRAIT(assailant, TRAIT_PACIFISM)) - to_chat(assailant, "You don't want to risk hurting [affecting]!") - return - if(state == GRAB_UPGRADING) - return - if(assailant.next_move > world.time) - return - if(world.time < (last_upgrade + UPGRADE_COOLDOWN)) - return - if(!assailant.canmove || assailant.lying) - qdel(src) - return - - last_upgrade = world.time - - if(state < GRAB_AGGRESSIVE) - if(!allow_upgrade) - return - //if(!affecting.lying) - assailant.visible_message("[assailant] has grabbed [affecting] aggressively (now hands)!") - /* else - assailant.visible_message("[assailant] pins [affecting] down to the ground (now hands)!") - force_down = 1 - affecting.Weaken(3) - step_to(assailant, affecting) - assailant.setDir(EAST) //face the victim - affecting.setDir(SOUTH) //face up //This is an example of a new feature based on the context of the location of the victim. - */ //It means that upgrading while someone is lying on the ground would cause you to go into pin mode. - state = GRAB_AGGRESSIVE - icon_state = "grabbed1" - hud.icon_state = "reinforce1" - add_attack_logs(assailant, affecting, "Aggressively grabbed", ATKLOG_ALL) - else if(state < GRAB_NECK) - if(isslime(affecting)) - to_chat(assailant, "You squeeze [affecting], but nothing interesting happens.") - return - - assailant.visible_message("[assailant] has reinforced [assailant.p_their()] grip on [affecting] (now neck)!") - state = GRAB_NECK - icon_state = "grabbed+1" - assailant.setDir(get_dir(assailant, affecting)) - add_attack_logs(assailant, affecting, "Neck grabbed", ATKLOG_ALL) - if(!iscarbon(assailant)) - affecting.LAssailant = null - else - affecting.LAssailant = assailant - hud.icon_state = "kill" - hud.name = "kill" - affecting.Stun(10) //10 ticks of ensured grab - else if(state < GRAB_UPGRADING) - assailant.visible_message("[assailant] starts to tighten [assailant.p_their()] grip on [affecting]'s neck!") - hud.icon_state = "kill1" - - state = GRAB_KILL - assailant.visible_message("[assailant] has tightened [assailant.p_their()] grip on [affecting]'s neck!") - add_attack_logs(assailant, affecting, "Strangled") - - assailant.next_move = world.time + 10 - if(!affecting.get_organ_slot("breathing_tube")) - affecting.AdjustLoseBreath(1) - affecting.setDir(WEST) - adjust_position() - -//This is used to make sure the victim hasn't managed to yackety sax away before using the grab. -/obj/item/grab/proc/confirm() - if(!assailant || !affecting) - qdel(src) - return 0 - - if(affecting) - if(!isturf(assailant.loc) || ( !isturf(affecting.loc) || assailant.loc != affecting.loc && get_dist(assailant, affecting) > 1) ) - qdel(src) - return 0 - return 1 - - -/obj/item/grab/attack(mob/living/M, mob/living/carbon/user) - if(!affecting) - return - - if(M == affecting) - if(ishuman(M) && ishuman(assailant)) - var/mob/living/carbon/human/affected = affecting - var/mob/living/carbon/human/attacker = assailant - switch(assailant.a_intent) - if(INTENT_HELP) - /*if(force_down) - to_chat(assailant, "You no longer pin [affecting] to the ground.") - force_down = 0 - return*///This is a very basic demonstration of a new feature based on attacking someone with the grab, based on intent. - //This specific example would allow you to stop pinning people to the floor without moving away from them. - return - - if(INTENT_GRAB) - return - - if(INTENT_HARM) //This checks that the user is on harm intent. - if(last_hit_zone == "head") //This checks the hitzone the user has selected. In this specific case, they have the head selected. - if(affecting.lying) - return - assailant.visible_message("[assailant] thrusts [assailant.p_their()] head into [affecting]'s skull!") //A visible message for what is going on. - var/damage = 5 - var/obj/item/clothing/hat = attacker.head - if(istype(hat)) - damage += hat.force * 3 - affecting.apply_damage(damage*rand(90, 110)/100, BRUTE, "head", affected.run_armor_check(affecting, "melee")) - playsound(assailant.loc, "swing_hit", 25, 1, -1) - add_attack_logs(assailant, affecting, "Headbutted") - return - - /*if(last_hit_zone == "eyes") - if(state < GRAB_NECK) - to_chat(assailant, "You require a better grab to do this.") - return - if((affected.head && affected.head.flags_cover & HEADCOVERSEYES) || \ - (affected.wear_mask && affected.wear_mask.flags_cover & MASKCOVERSEYES) || \ - (affected.glasses && affected.glasses.flags_cover & GLASSESCOVERSEYES)) - to_chat(assailant, "You're going to need to remove the eye covering first.") - return - if(!affected.internal_bodyparts_by_name["eyes"]) - to_chat(assailant, "You cannot locate any eyes on [affecting]!") - return - assailant.visible_message("[assailant] presses [assailant.p_their()] fingers into [affecting]'s eyes!") - to_chat(affecting, "You feel immense pain as digits are being pressed into your eyes!") - add_attack_logs(assailant, affecting, "Eye-fucked with their fingers") - var/obj/item/organ/internal/eyes/eyes = affected.get_int_organ(/obj/item/organ/internal/eyes) - eyes.damage += rand(3,4) - if(eyes.damage >= eyes.min_broken_damage) - if(M.stat != 2) - to_chat(M, "You go blind!")*///This is a demonstration of adding a new damaging type based on intent as well as hitzone. - - //This specific example would allow you to squish people's eyes with a GRAB_NECK. - - if(INTENT_DISARM) //This checks that the user is on disarm intent. - /* if(state < GRAB_AGGRESSIVE) - to_chat(assailant, "You require a better grab to do this.") - return - if(!force_down) - assailant.visible_message("[user] is forcing [affecting] to the ground!") - force_down = 1 - affecting.Weaken(3) - affecting.lying = 1 - step_to(assailant, affecting) - assailant.setDir(EAST) //face the victim - affecting.setDir(SOUTH) //face up - return - else - to_chat(assailant, "You are already pinning [affecting] to the ground.") - return*///This is an example of something being done with an agressive grab + disarm intent. - return - - - if(M == assailant && state >= GRAB_AGGRESSIVE) //no eatin unless you have an agressive grab - if(checkvalid(user, affecting)) //wut - var/mob/living/carbon/attacker = user - - if(affecting.buckled) - to_chat(user, "[affecting] is buckled!") - return - - user.visible_message("[user] is attempting to devour \the [affecting]!") - - if(!do_after(user, checktime(user, affecting), target = affecting)) return - - if(affecting.buckled) - to_chat(user, "[affecting] is buckled!") - return - - user.visible_message("[user] devours \the [affecting]!") - if(affecting.mind) - add_attack_logs(attacker, affecting, "Devoured") - - affecting.forceMove(user) - attacker.stomach_contents.Add(affecting) - qdel(src) - -/obj/item/grab/proc/checkvalid(var/mob/attacker, var/mob/prey) //does all the checking for the attack proc to see if a mob can eat another with the grab - if(isalien(attacker) && iscarbon(prey)) //Xenomorphs eating carbon mobs - return 1 - - var/mob/living/carbon/human/H = attacker - if(ishuman(H) && is_type_in_list(prey, H.dna.species.allowed_consumed_mobs)) //species eating of other mobs - return 1 - - return 0 - -/obj/item/grab/proc/checktime(var/mob/attacker, var/mob/prey) //Returns the time the attacker has to wait before they eat the prey - if(isalien(attacker)) - return EAT_TIME_XENO //xenos get a speed boost - - if(istype(prey,/mob/living/simple_animal)) //simple animals get eaten at xeno-eating-speed regardless - return EAT_TIME_ANIMAL - - return EAT_TIME_FAT //if it doesn't fit into the above, it's probably a fat guy, take EAT_TIME_FAT to do it - -/obj/item/grab/Destroy() - if(affecting) - affecting.pixel_x = 0 - affecting.pixel_y = 0 //used to be an animate, not quick enough for del'ing - affecting.layer = initial(affecting.layer) - affecting.grabbed_by -= src - affecting = null - if(assailant) - if(assailant.client) - assailant.client.screen -= hud - assailant = null - QDEL_NULL(hud) - return ..() - - -#undef EAT_TIME_XENO -#undef EAT_TIME_FAT - -#undef EAT_TIME_ANIMAL +#define UPGRADE_COOLDOWN 40 +#define UPGRADE_KILL_TIMER 100 + +//times it takes for a mob to eat +#define EAT_TIME_XENO 30 +#define EAT_TIME_FAT 100 + +//time it takes for a mob to be eaten (in deciseconds) (overrides mob eat time) +#define EAT_TIME_ANIMAL 30 + +/obj/item/grab + name = "grab" + flags = NOBLUDGEON | ABSTRACT | DROPDEL + var/obj/screen/grab/hud = null + var/mob/living/affecting = null + var/mob/living/assailant = null + var/state = GRAB_PASSIVE + + var/allow_upgrade = 1 + var/last_upgrade = 0 + var/last_hit_zone = 0 +// var/force_down //determines if the affecting mob will be pinned to the ground //disabled due to balance, kept for an example for any new things. + var/dancing //determines if assailant and affecting keep looking at each other. Basically a wrestling position + + layer = 21 + plane = HUD_PLANE + item_state = "nothing" + icon = 'icons/mob/screen_gen.dmi' + w_class = WEIGHT_CLASS_BULKY + + +/obj/item/grab/New(var/mob/user, var/mob/victim) + ..() + + //Okay, first off, some fucking sanity checking. No user, or no victim, or they are not mobs, no grab. + if(!istype(user) || !istype(victim)) + return + + loc = user + assailant = user + affecting = victim + + if(affecting.anchored) + qdel(src) + return + + affecting.grabbed_by += src + + hud = new /obj/screen/grab(src) + hud.icon_state = "reinforce" + icon_state = "grabbed" + hud.name = "reinforce grab" + hud.master = src + + //check if assailant is grabbed by victim as well + if(assailant.grabbed_by) + for(var/obj/item/grab/G in assailant.grabbed_by) + if(G.assailant == affecting && G.affecting == assailant) + G.dancing = 1 + G.adjust_position() + dancing = 1 + + clean_grabbed_by(assailant, affecting) + adjust_position() + +/obj/item/grab/proc/clean_grabbed_by(var/mob/user, var/mob/victim) //Cleans up any nulls in the grabbed_by list. + if(istype(user)) + + for(var/entry in user.grabbed_by) + if(isnull(entry) || !istype(entry, /obj/item/grab)) //the isnull() here is probably redundant- but it might outperform istype, who knows. Better safe than sorry. + user.grabbed_by -= entry + + if(istype(victim)) + + for(var/entry in victim.grabbed_by) + if(isnull(entry) || !istype(entry, /obj/item/grab)) + victim.grabbed_by -= entry + + return 1 + +//Used by throw code to hand over the mob, instead of throwing the grab. The grab is then deleted by the throw code. +/obj/item/grab/proc/get_mob_if_throwable() + if(affecting && assailant.Adjacent(affecting)) + if(affecting.buckled) + return null + if(state >= GRAB_AGGRESSIVE) + return affecting + return null + +//This makes sure that the grab screen object is displayed in the correct hand. +/obj/item/grab/proc/synch() + if(affecting) + if(assailant.r_hand == src) + hud.screen_loc = ui_rhand + else + hud.screen_loc = ui_lhand + assailant.client.screen += hud + +/obj/item/grab/process() + if(!confirm()) + return //If the confirm fails, the grab is about to be deleted. That means it shouldn't continue processing. + + if(assailant.client) + if(!hud) + return //this somehow can runtime under the right circumstances + assailant.client.screen -= hud + assailant.client.screen += hud + + var/hit_zone = assailant.zone_selected + last_hit_zone = hit_zone + + if(assailant.pulling == affecting) + assailant.stop_pulling() + + if(state <= GRAB_AGGRESSIVE) + allow_upgrade = 1 + //disallow upgrading if we're grabbing more than one person + if((assailant.l_hand && assailant.l_hand != src && istype(assailant.l_hand, /obj/item/grab))) + var/obj/item/grab/G = assailant.l_hand + if(G.affecting != affecting) + allow_upgrade = 0 + if((assailant.r_hand && assailant.r_hand != src && istype(assailant.r_hand, /obj/item/grab))) + var/obj/item/grab/G = assailant.r_hand + if(G.affecting != affecting) + allow_upgrade = 0 + + //disallow upgrading past aggressive if we're being grabbed aggressively + for(var/obj/item/grab/G in affecting.grabbed_by) + if(G == src) continue + if(G.state >= GRAB_AGGRESSIVE) + allow_upgrade = 0 + + if(allow_upgrade) + if(state < GRAB_AGGRESSIVE) + hud.icon_state = "reinforce" + else + hud.icon_state = "reinforce1" + else + hud.icon_state = "!reinforce" + + if(state >= GRAB_AGGRESSIVE) + if(!HAS_TRAIT(assailant, TRAIT_PACIFISM)) + affecting.drop_r_hand() + affecting.drop_l_hand() + + + //var/announce = 0 + //(hit_zone != last_hit_zone) + //announce = 1 + /* if(ishuman(affecting)) + switch(hit_zone) + /*if("mouth") + if(announce) + assailant.visible_message("[assailant] covers [affecting]'s mouth!") + if(affecting.silent < 3) + affecting.silent = 3 + if("eyes") + if(announce) + assailant.visible_message("[assailant] covers [affecting]'s eyes!") + if(affecting.eye_blind < 3) + affecting.eye_blind = 3*///These are being left in the code as an example for adding new hit-zone based things. + + if(force_down) + if(affecting.loc != assailant.loc) + force_down = 0 + else + affecting.Weaken(3) //This is being left in the code as an example of adding a new variable to do something in grab code. + +*/ + + var/breathing_tube = affecting.get_organ_slot("breathing_tube") + + if(state >= GRAB_NECK) + affecting.Stun(5) //It will hamper your voice, being choked and all. + if(isliving(affecting) && !breathing_tube) + var/mob/living/L = affecting + L.adjustOxyLoss(1) + + if(state >= GRAB_KILL) + //affecting.apply_effect(STUTTER, 5) //would do this, but affecting isn't declared as mob/living for some stupid reason. + affecting.Stuttering(5) //It will hamper your voice, being choked and all. + affecting.Weaken(5) //Should keep you down unless you get help. + if(!breathing_tube) + affecting.AdjustLoseBreath(2, bound_lower = 0, bound_upper = 3) + + adjust_position() + + +/obj/item/grab/attack_self(mob/user) + s_click(hud) + +//Updating pixelshift, position and direction +//Gets called on process, when the grab gets upgraded or the assailant moves +/obj/item/grab/proc/adjust_position() + if(affecting.buckled) + return + if(affecting.lying && state != GRAB_KILL) + animate(affecting, pixel_x = 0, pixel_y = 0, 5, 1, LINEAR_EASING) + return //KJK + /* if(force_down) //THIS GOES ABOVE THE RETURN LABELED KJK + affecting.setDir(SOUTH)*///This shows how you can apply special directions based on a variable. //face up + + var/shift = 0 + var/adir = get_dir(assailant, affecting) + affecting.layer = 4 + switch(state) + if(GRAB_PASSIVE) + shift = 8 + if(dancing) //look at partner + shift = 10 + assailant.setDir(get_dir(assailant, affecting)) + if(GRAB_AGGRESSIVE) + shift = 12 + if(GRAB_NECK, GRAB_UPGRADING) + shift = -10 + adir = assailant.dir + affecting.setDir(assailant.dir) + affecting.forceMove(assailant.loc) + if(GRAB_KILL) + shift = 0 + adir = 1 + affecting.setDir(SOUTH)//face up + affecting.forceMove(assailant.loc) + + switch(adir) + if(NORTH) + animate(affecting, pixel_x = 0, pixel_y =-shift, 5, 1, LINEAR_EASING) + affecting.layer = 3.9 + if(SOUTH) + animate(affecting, pixel_x = 0, pixel_y = shift, 5, 1, LINEAR_EASING) + if(WEST) + animate(affecting, pixel_x = shift, pixel_y = 0, 5, 1, LINEAR_EASING) + if(EAST) + animate(affecting, pixel_x =-shift, pixel_y = 0, 5, 1, LINEAR_EASING) + +/obj/item/grab/proc/s_click(obj/screen/S) + if(!affecting) + return + if(state >= GRAB_AGGRESSIVE && HAS_TRAIT(assailant, TRAIT_PACIFISM)) + to_chat(assailant, "You don't want to risk hurting [affecting]!") + return + if(state == GRAB_UPGRADING) + return + if(assailant.next_move > world.time) + return + if(world.time < (last_upgrade + UPGRADE_COOLDOWN)) + return + if(!assailant.canmove || assailant.lying) + qdel(src) + return + + last_upgrade = world.time + + if(state < GRAB_AGGRESSIVE) + if(!allow_upgrade) + return + //if(!affecting.lying) + assailant.visible_message("[assailant] has grabbed [affecting] aggressively (now hands)!") + /* else + assailant.visible_message("[assailant] pins [affecting] down to the ground (now hands)!") + force_down = 1 + affecting.Weaken(3) + step_to(assailant, affecting) + assailant.setDir(EAST) //face the victim + affecting.setDir(SOUTH) //face up //This is an example of a new feature based on the context of the location of the victim. + */ //It means that upgrading while someone is lying on the ground would cause you to go into pin mode. + state = GRAB_AGGRESSIVE + icon_state = "grabbed1" + hud.icon_state = "reinforce1" + add_attack_logs(assailant, affecting, "Aggressively grabbed", ATKLOG_ALL) + else if(state < GRAB_NECK) + if(isslime(affecting)) + to_chat(assailant, "You squeeze [affecting], but nothing interesting happens.") + return + + assailant.visible_message("[assailant] has reinforced [assailant.p_their()] grip on [affecting] (now neck)!") + state = GRAB_NECK + icon_state = "grabbed+1" + assailant.setDir(get_dir(assailant, affecting)) + add_attack_logs(assailant, affecting, "Neck grabbed", ATKLOG_ALL) + if(!iscarbon(assailant)) + affecting.LAssailant = null + else + affecting.LAssailant = assailant + hud.icon_state = "kill" + hud.name = "kill" + affecting.Stun(10) //10 ticks of ensured grab + else if(state < GRAB_UPGRADING) + assailant.visible_message("[assailant] starts to tighten [assailant.p_their()] grip on [affecting]'s neck!") + hud.icon_state = "kill1" + + state = GRAB_KILL + assailant.visible_message("[assailant] has tightened [assailant.p_their()] grip on [affecting]'s neck!") + add_attack_logs(assailant, affecting, "Strangled") + + assailant.next_move = world.time + 10 + if(!affecting.get_organ_slot("breathing_tube")) + affecting.AdjustLoseBreath(1) + affecting.setDir(WEST) + adjust_position() + +//This is used to make sure the victim hasn't managed to yackety sax away before using the grab. +/obj/item/grab/proc/confirm() + if(!assailant || !affecting) + qdel(src) + return 0 + + if(affecting) + if(!isturf(assailant.loc) || ( !isturf(affecting.loc) || assailant.loc != affecting.loc && get_dist(assailant, affecting) > 1) ) + qdel(src) + return 0 + return 1 + + +/obj/item/grab/attack(mob/living/M, mob/living/carbon/user) + if(!affecting) + return + + if(M == affecting) + if(ishuman(M) && ishuman(assailant)) + var/mob/living/carbon/human/affected = affecting + var/mob/living/carbon/human/attacker = assailant + switch(assailant.a_intent) + if(INTENT_HELP) + /*if(force_down) + to_chat(assailant, "You no longer pin [affecting] to the ground.") + force_down = 0 + return*///This is a very basic demonstration of a new feature based on attacking someone with the grab, based on intent. + //This specific example would allow you to stop pinning people to the floor without moving away from them. + return + + if(INTENT_GRAB) + return + + if(INTENT_HARM) //This checks that the user is on harm intent. + if(last_hit_zone == "head") //This checks the hitzone the user has selected. In this specific case, they have the head selected. + if(affecting.lying) + return + assailant.visible_message("[assailant] thrusts [assailant.p_their()] head into [affecting]'s skull!") //A visible message for what is going on. + var/damage = 5 + var/obj/item/clothing/hat = attacker.head + if(istype(hat)) + damage += hat.force * 3 + affecting.apply_damage(damage*rand(90, 110)/100, BRUTE, "head", affected.run_armor_check(affecting, "melee")) + playsound(assailant.loc, "swing_hit", 25, 1, -1) + add_attack_logs(assailant, affecting, "Headbutted") + return + + /*if(last_hit_zone == "eyes") + if(state < GRAB_NECK) + to_chat(assailant, "You require a better grab to do this.") + return + if((affected.head && affected.head.flags_cover & HEADCOVERSEYES) || \ + (affected.wear_mask && affected.wear_mask.flags_cover & MASKCOVERSEYES) || \ + (affected.glasses && affected.glasses.flags_cover & GLASSESCOVERSEYES)) + to_chat(assailant, "You're going to need to remove the eye covering first.") + return + if(!affected.internal_bodyparts_by_name["eyes"]) + to_chat(assailant, "You cannot locate any eyes on [affecting]!") + return + assailant.visible_message("[assailant] presses [assailant.p_their()] fingers into [affecting]'s eyes!") + to_chat(affecting, "You feel immense pain as digits are being pressed into your eyes!") + add_attack_logs(assailant, affecting, "Eye-fucked with their fingers") + var/obj/item/organ/internal/eyes/eyes = affected.get_int_organ(/obj/item/organ/internal/eyes) + eyes.damage += rand(3,4) + if(eyes.damage >= eyes.min_broken_damage) + if(M.stat != 2) + to_chat(M, "You go blind!")*///This is a demonstration of adding a new damaging type based on intent as well as hitzone. + + //This specific example would allow you to squish people's eyes with a GRAB_NECK. + + if(INTENT_DISARM) //This checks that the user is on disarm intent. + /* if(state < GRAB_AGGRESSIVE) + to_chat(assailant, "You require a better grab to do this.") + return + if(!force_down) + assailant.visible_message("[user] is forcing [affecting] to the ground!") + force_down = 1 + affecting.Weaken(3) + affecting.lying = 1 + step_to(assailant, affecting) + assailant.setDir(EAST) //face the victim + affecting.setDir(SOUTH) //face up + return + else + to_chat(assailant, "You are already pinning [affecting] to the ground.") + return*///This is an example of something being done with an agressive grab + disarm intent. + return + + + if(M == assailant && state >= GRAB_AGGRESSIVE) //no eatin unless you have an agressive grab + if(checkvalid(user, affecting)) //wut + var/mob/living/carbon/attacker = user + + if(affecting.buckled) + to_chat(user, "[affecting] is buckled!") + return + + user.visible_message("[user] is attempting to devour \the [affecting]!") + + if(!do_after(user, checktime(user, affecting), target = affecting)) return + + if(affecting.buckled) + to_chat(user, "[affecting] is buckled!") + return + + user.visible_message("[user] devours \the [affecting]!") + if(affecting.mind) + add_attack_logs(attacker, affecting, "Devoured") + + affecting.forceMove(user) + attacker.stomach_contents.Add(affecting) + qdel(src) + +/obj/item/grab/proc/checkvalid(var/mob/attacker, var/mob/prey) //does all the checking for the attack proc to see if a mob can eat another with the grab + if(isalien(attacker) && iscarbon(prey)) //Xenomorphs eating carbon mobs + return 1 + + var/mob/living/carbon/human/H = attacker + if(ishuman(H) && is_type_in_list(prey, H.dna.species.allowed_consumed_mobs)) //species eating of other mobs + return 1 + + return 0 + +/obj/item/grab/proc/checktime(var/mob/attacker, var/mob/prey) //Returns the time the attacker has to wait before they eat the prey + if(isalien(attacker)) + return EAT_TIME_XENO //xenos get a speed boost + + if(istype(prey,/mob/living/simple_animal)) //simple animals get eaten at xeno-eating-speed regardless + return EAT_TIME_ANIMAL + + return EAT_TIME_FAT //if it doesn't fit into the above, it's probably a fat guy, take EAT_TIME_FAT to do it + +/obj/item/grab/Destroy() + if(affecting) + affecting.pixel_x = 0 + affecting.pixel_y = 0 //used to be an animate, not quick enough for del'ing + affecting.layer = initial(affecting.layer) + affecting.grabbed_by -= src + affecting = null + if(assailant) + if(assailant.client) + assailant.client.screen -= hud + assailant = null + QDEL_NULL(hud) + return ..() + + +#undef EAT_TIME_XENO +#undef EAT_TIME_FAT + +#undef EAT_TIME_ANIMAL diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index a96a832fef4..a894ad7cd07 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -1,683 +1,686 @@ -/proc/issmall(A) - if(A && istype(A, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = A - if(H.dna.species && H.dna.species.is_small) - return 1 - return 0 - -/proc/ispet(A) - if(istype(A, /mob/living/simple_animal)) - var/mob/living/simple_animal/SA = A - if(SA.can_collar) - return 1 - return 0 - -/mob/proc/isSynthetic() - return 0 - -/mob/living/carbon/human/isSynthetic() - if(ismachine(src)) - return TRUE - return FALSE - -/mob/proc/get_screen_colour() - -/mob/proc/update_client_colour(var/time = 10) //Update the mob's client.color with an animation the specified time in length. - if(!client) //No client_colour without client. If the player logs back in they'll be back through here anyway. - return - client.colour_transition(get_screen_colour(), time = time) //Get the colour matrix we're going to transition to depending on relevance (magic glasses first, eyes second). - -/mob/living/carbon/human/get_screen_colour() //Fetch the colour matrix from wherever (e.g. eyes) so it can be compared to client.color. - . = ..() - if(.) - return . - - var/obj/item/clothing/glasses/worn_glasses = glasses - var/obj/item/organ/internal/eyes/eyes = get_int_organ(/obj/item/organ/internal/eyes) - if(istype(worn_glasses) && worn_glasses.color_view) //Check to see if they got those magic glasses and they're augmenting the colour of what the wearer sees. If they're not, color_view should be null. - return worn_glasses.color_view - else if(eyes) //If they're not, check to see if their eyes got one of them there colour matrices. Will be null if eyes are robotic/the mob isn't colourblind and they have no default colour matrix. - return eyes.get_colourmatrix() - -/proc/ismindshielded(A) //Checks to see if the person contains a mindshield implant, then checks that the implant is actually inside of them - for(var/obj/item/implant/mindshield/L in A) - if(L && L.implanted) - return 1 - return 0 - -/proc/ismindslave(A) //Checks to see if the person contains a mindslave implant, then checks that the implant is actually inside of them - for(var/obj/item/implant/traitor/T in A) - if(T && T.implanted) - return 1 - return 0 - -/proc/isLivingSSD(mob/M) - return istype(M) && M.player_logged && M.stat != DEAD - -/proc/isAntag(A) - if(istype(A, /mob/living/carbon)) - var/mob/living/carbon/C = A - if(C.mind && C.mind.special_role) - return 1 - return 0 - -/proc/isNonCrewAntag(A) - if(!isAntag(A)) - return 0 - - var/mob/living/carbon/C = A - var/special_role = C.mind.special_role - var/list/crew_roles = list( - SPECIAL_ROLE_BLOB, - SPECIAL_ROLE_CULTIST, - SPECIAL_ROLE_CHANGELING, - SPECIAL_ROLE_ERT, - SPECIAL_ROLE_HEAD_REV, - SPECIAL_ROLE_REV, - SPECIAL_ROLE_SHADOWLING, - SPECIAL_ROLE_SHADOWLING_THRALL, - SPECIAL_ROLE_TRAITOR, - SPECIAL_ROLE_VAMPIRE, - SPECIAL_ROLE_VAMPIRE_THRALL - ) - if(special_role in crew_roles) - return 0 - - return 1 - -/proc/cannotPossess(A) - var/mob/dead/observer/G = A - if(G.has_enabled_antagHUD == 1 && config.antag_hud_restricted) - return 1 - return 0 - - -/proc/iscuffed(A) - if(istype(A, /mob/living/carbon)) - var/mob/living/carbon/C = A - if(C.handcuffed) - return 1 - return 0 - -/proc/hassensorlevel(A, var/level) - var/mob/living/carbon/human/H = A - if(istype(H) && istype(H.w_uniform, /obj/item/clothing/under)) - var/obj/item/clothing/under/U = H.w_uniform - return U.sensor_mode >= level - return 0 - -/proc/getsensorlevel(A) - var/mob/living/carbon/human/H = A - if(istype(H) && istype(H.w_uniform, /obj/item/clothing/under)) - var/obj/item/clothing/under/U = H.w_uniform - return U.sensor_mode - return SUIT_SENSOR_OFF - -/proc/offer_control(mob/M) - to_chat(M, "Control of your mob has been offered to dead players.") - log_admin("[key_name(usr)] has offered control of ([key_name(M)]) to ghosts.") - var/minhours = input(usr, "Minimum hours required to play [M]?", "Set Min Hrs", 10) as num - message_admins("[key_name_admin(usr)] has offered control of ([key_name_admin(M)]) to ghosts with [minhours] hrs playtime") - var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as [M.real_name ? M.real_name : M.name]?", poll_time = 100, min_hours = minhours) - var/mob/dead/observer/theghost = null - - if(LAZYLEN(candidates)) - theghost = pick(candidates) - to_chat(M, "Your mob has been taken over by a ghost!") - message_admins("[key_name_admin(theghost)] has taken control of ([key_name_admin(M)])") - M.ghostize() - M.key = theghost.key - else - to_chat(M, "There were no ghosts willing to take control.") - message_admins("No ghosts were willing to take control of [key_name_admin(M)])") - -/proc/check_zone(zone) - if(!zone) return "chest" - switch(zone) - if("eyes") - zone = "head" - if("mouth") - zone = "head" - return zone - -// Returns zone with a certain probability. -// If the probability misses, returns "chest" instead. -// If "chest" was passed in as zone, then on a "miss" will return "head", "l_arm", or "r_arm" -// Do not use this if someone is intentionally trying to hit a specific body part. -// Use get_zone_with_miss_chance() for that. -/proc/ran_zone(zone, probability = 80) - - zone = check_zone(zone) - - if(prob(probability)) - return zone - - var/t = rand(1, 18) // randomly pick a different zone, or maybe the same one - switch(t) - if(1) return "head" - if(2) return "chest" - if(3 to 4) return "l_arm" - if(5 to 6) return "l_hand" - if(7 to 8) return "r_arm" - if(9 to 10) return "r_hand" - if(11 to 12) return "l_leg" - if(13 to 14) return "l_foot" - if(15 to 16) return "r_leg" - if(17 to 18) return "r_foot" - - return zone - -/proc/above_neck(zone) - var/list/zones = list("head", "mouth", "eyes") - if(zones.Find(zone)) - return 1 - else - return 0 - -/proc/stars(n, pr) - if(pr == null) - pr = 25 - if(pr <= 0) - return null - else - if(pr >= 100) - return n - var/te = n - var/t = "" - n = length(n) - var/p = null - p = 1 - while(p <= n) - if((copytext(te, p, p + 1) == " " || prob(pr))) - t = text("[][]", t, copytext(te, p, p + 1)) - else - t = text("[]*", t) - p++ - return t - -/proc/stars_all(list/message_pieces, pr) - for(var/datum/multilingual_say_piece/S in message_pieces) - S.message = stars(S.message, pr) - -/proc/slur(phrase, var/list/slurletters = ("'"))//use a different list as an input if you want to make robots slur with $#@%! characters - phrase = html_decode(phrase) - var/leng=length(phrase) - var/counter=length(phrase) - var/newphrase="" - var/newletter="" - while(counter>=1) - newletter=copytext(phrase,(leng-counter)+1,(leng-counter)+2) - if(rand(1,3)==3) - if(lowertext(newletter)=="o") newletter="u" - if(lowertext(newletter)=="s") newletter="ch" - if(lowertext(newletter)=="a") newletter="ah" - if(lowertext(newletter)=="c") newletter="k" - switch(rand(1,15)) - if(1,3,5,8) newletter="[lowertext(newletter)]" - if(2,4,6,15) newletter="[uppertext(newletter)]" - if(7) newletter+=pick(slurletters) - //if(9,10) newletter="[newletter]" - //if(11,12) newletter="[newletter]" - //if(13) newletter="[newletter]" - newphrase+="[newletter]" - counter-=1 - return newphrase - -/proc/stutter(n) - var/te = html_decode(n) - var/t = ""//placed before the message. Not really sure what it's for. - n = length(n)//length of the entire word - var/p = null - p = 1//1 is the start of any word - while(p <= n)//while P, which starts at 1 is less or equal to N which is the length. - var/n_letter = copytext(te, p, p + 1)//copies text from a certain distance. In this case, only one letter at a time. - if(prob(80) && (ckey(n_letter) in list("b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","y","z"))) - if(prob(10)) - n_letter = text("[n_letter]-[n_letter]-[n_letter]-[n_letter]")//replaces the current letter with this instead. - else - if(prob(20)) - n_letter = text("[n_letter]-[n_letter]-[n_letter]") - else - if(prob(5)) - n_letter = null - else - n_letter = text("[n_letter]-[n_letter]") - t = text("[t][n_letter]")//since the above is ran through for each letter, the text just adds up back to the original word. - p++//for each letter p is increased to find where the next letter will be. - return sanitize(copytext(t,1,MAX_MESSAGE_LEN)) - -/proc/robostutter(n) //for robutts - var/te = html_decode(n) - var/t = ""//placed before the message. Not really sure what it's for. - n = length(n)//length of the entire word - var/p = null - p = 1//1 is the start of any word - while(p <= n)//while P, which starts at 1 is less or equal to N which is the length. - var/robotletter = pick("@", "!", "#", "$", "%", "&", "?") //for beep boop - var/n_letter = copytext(te, p, p + 1)//copies text from a certain distance. In this case, only one letter at a time. - if(prob(80) && (ckey(n_letter) in list("b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","y","z"))) - if(prob(10)) - n_letter = text("[n_letter]-[robotletter]-[n_letter]-[n_letter]")//replaces the current letter with this instead. - else - if(prob(20)) - n_letter = text("[n_letter]-[robotletter]-[n_letter]") - else - if(prob(5)) - n_letter = robotletter - else - n_letter = text("[n_letter]-[n_letter]") - t = text("[t][n_letter]")//since the above is ran through for each letter, the text just adds up back to the original word. - p++//for each letter p is increased to find where the next letter will be. - return sanitize(copytext(t,1,MAX_MESSAGE_LEN)) - - -/proc/Gibberish(t, p)//t is the inputted message, and any value higher than 70 for p will cause letters to be replaced instead of added - /* Turn text into complete gibberish! */ - var/returntext = "" - for(var/i = 1, i <= length(t), i++) - - var/letter = copytext(t, i, i+1) - if(prob(50)) - if(p >= 70) - letter = "" - - for(var/j = 1, j <= rand(0, 2), j++) - letter += pick("#","@","*","&","%","$","/", "<", ">", ";","*","*","*","*","*","*","*") - - returntext += letter - - return returntext - -/proc/Gibberish_all(list/message_pieces, p) - for(var/datum/multilingual_say_piece/S in message_pieces) - S.message = Gibberish(S.message, p) - - -proc/muffledspeech(phrase) - phrase = html_decode(phrase) - var/leng=length(phrase) - var/counter=length(phrase) - var/newphrase="" - var/newletter="" - while(counter>=1) - newletter=copytext(phrase,(leng-counter)+1,(leng-counter)+2) - if(newletter in list(" ", "!", "?", ".", ",")) - //do nothing - else if(lowertext(newletter) in list("a", "e", "i", "o", "u", "y")) - newletter = "ph" - else - newletter = "m" - newphrase+="[newletter]" - counter-=1 - return newphrase - -/proc/muffledspeech_all(list/message_pieces) - for(var/datum/multilingual_say_piece/S in message_pieces) - S.message = muffledspeech(S.message) - - -/proc/shake_camera(mob/M, duration, strength=1) - if(!M || !M.client || M.shakecamera) - return - M.shakecamera = 1 - spawn(1) - - var/atom/oldeye=M.client.eye - var/aiEyeFlag = 0 - if(istype(oldeye, /mob/camera/aiEye)) - aiEyeFlag = 1 - - var/x - for(x=0; x
        Player polls" - if(check_rights(R_SERVER)) - output += "(Create new poll)" - output +="
        " - - output += "" - var/color1 = "#ececec" - var/color2 = "#e2e2e2" - var/i = 0 - - output += "" - while(select_query.NextRow()) - var/pollid = select_query.item[1] - var/pollquestion = select_query.item[2] - var/voted = text2num(select_query.item[3]) - output += "" - if(can_vote() && !voted) - output += "" - i++ - - // Show expired polls. Non admins can view admin polls at this stage - // (just like tgstation's web interface so don't complain) - // (Also why was there no ingame interface tg not having an ingame - // interface is retarded because it cucks downstreams) - select_query = dbcon.NewQuery("SELECT id, question FROM [format_table_name("poll_question")] WHERE Now() > endtime ORDER BY id DESC") - select_query.Execute() - - output += "" - while(select_query.NextRow()) - var/pollid = select_query.item[1] - var/pollquestion = select_query.item[2] - output += "" - - output += "
        Active Polls
        [pollquestion]
        [poll_player(pollid, 1)]
        Expired Polls
        [pollquestion]
        " - - src << browse(output,"window=playerpolllist;size=500x300") - -/client/proc/poll_results(var/pollid = -1) - if(pollid == -1) - return - establish_db_connection() - if(!dbcon.IsConnected()) - return - var/DBQuery/select_query = dbcon.NewQuery("SELECT polltype, question, adminonly, multiplechoiceoptions, starttime, endtime FROM [format_table_name("poll_question")] WHERE id = [pollid] AND endtime < Now()") - select_query.Execute() - var/question = "" - var/polltype = "" - var/adminonly = 0 - var/multiplechoiceoptions = 0 - var/starttime = "" - var/endtime = "" - var/found = 0 - while(select_query.NextRow()) - polltype = select_query.item[1] - question = select_query.item[2] - adminonly = text2num(select_query.item[3]) - multiplechoiceoptions = text2num(select_query.item[4]) - starttime = select_query.item[5] - endtime = select_query.item[6] - found = 1 - break - if(!found) - to_chat(src, "Poll question details not found. (Maybe the poll isn't expired yet?)") - return - - if(polltype == POLLTYPE_MULTI) - question += " (Choose up to [multiplechoiceoptions] options)" - if(adminonly) - question = "(Admin only poll) " + question - - var output = "" - if(polltype == POLLTYPE_MULTI || polltype == POLLTYPE_OPTION) - select_query = dbcon.NewQuery("SELECT text, percentagecalc, (SELECT COUNT(optionid) FROM [format_table_name("poll_vote")] WHERE optionid = poll_option.id GROUP BY optionid) AS votecount FROM [format_table_name("poll_option")] WHERE pollid = [pollid]"); - select_query.Execute() - var/list/options = list() - var/total_votes = 1 - var/total_percent_votes = 1 - var/max_votes = 1 - while(select_query.NextRow()) - var/text = select_query.item[1] - var/percentagecalc = select_query.item[2] - var/votecount = text2num(select_query.item[3]) - if(percentagecalc) - total_percent_votes += votecount - total_votes += votecount - if(votecount > max_votes) - max_votes = votecount - options[++options.len] = list(text, percentagecalc, votecount) - // fuck ie. - output += {" - - - - - - " - for(var/list/option in options) - var/bar_width = option[3] * 390 / max_votes - var/percentage = option[2] ? "[round(option[3] * 100 / total_percent_votes)]%" : "N/A" - output += "" - output += "" - output += "" - output += "" - output += "
        [question]
        [starttime] - [endtime]
        "} - var/list/colors = list("#66c2a5", "#fc8d62", "#8da0cb", "#e78ac3", "#a6d854", "#ffd92f", "#e5c494", "#b3b3b3") - var/color_index = 0 - for(var/list/option in options) - var/bar_width = option[3] * 700 / total_votes - var/percentage = option[2] ? "[round(option[3] * 100 / total_percent_votes)]%" : "N/A" - color_index++ - if(color_index > colors.len) - color_index = 1 - output += "
        " - output += "

        (Hover over the colored bar to read description)
        [option[1]][option[3]][percentage]
        " - if(polltype == POLLTYPE_RATING) - output += {" - - - - "} - select_query = dbcon.NewQuery("SELECT id, text, (SELECT AVG(rating) FROM [format_table_name("poll_vote")] WHERE optionid = poll_option.id AND rating != 'abstain') AS avgrating, (SELECT COUNT(rating) FROM [format_table_name("poll_vote")] WHERE optionid = poll_option.id AND rating != 'abstain') AS countvotes, minval, maxval FROM [format_table_name("poll_option")] WHERE pollid = [pollid]") - select_query.Execute() - while(select_query.NextRow()) - output += {" - - " - output += "
        [question]
        [starttime] - [endtime]
        [select_query.item[2]] - N = [select_query.item[4]] - AVG = [select_query.item[3]] - "} - var/optionid = select_query.item[1] - var/totalvotes = text2num(select_query.item[4]) - var/minval = text2num(select_query.item[5]) - var/maxval = text2num(select_query.item[6]) - var/maxvote = 1 - var/list/votecounts = list() - for(var/I in minval to maxval) - var/DBQuery/rating_query = dbcon.NewQuery("SELECT COUNT(rating) AS countrating FROM [format_table_name("poll_vote")] WHERE optionid = [optionid] AND rating = [I] GROUP BY rating") - rating_query.Execute() - var/votecount = 0 - while(rating_query.NextRow()) - votecount = text2num(rating_query.item[1]) - votecounts["[I]"] = votecount - if(votecount > maxvote) - maxvote = votecount - for(var/I in minval to maxval) - var/votecount = votecounts["[I]"] - var/bar_width = votecount * 200 / maxvote - output += {" - - - - - - "} - output += "
        [I][votecount]([votecount / totalvotes]%)
        " - if(polltype == POLLTYPE_TEXT) - select_query = dbcon.NewQuery("SELECT replytext, COUNT(replytext) AS countresponse, GROUP_CONCAT(DISTINCT ckey SEPARATOR ', ') as ckeys FROM [format_table_name("poll_textreply")] WHERE pollid = [pollid] GROUP BY replytext ORDER BY countresponse DESC"); - select_query.Execute() - output += {" - - - - "} - while(select_query.NextRow()) - var/replytext = select_query.item[1] - var/countresponse = select_query.item[2] - var/ckeys = select_query.item[3] - output += {" - - - - "} - output += "
        [question]
        [starttime] - [endtime]
        [ckeys] ([countresponse] player\s) responded with:[replytext]
        " - output += "" - - src << browse(output,"window=pollresults;size=950x500") - -/client/proc/poll_player(var/pollid = -1, var/inline = 0) - if(pollid == -1) return - establish_db_connection() - if(dbcon.IsConnected()) - - var/DBQuery/select_query = dbcon.NewQuery("SELECT starttime, endtime, question, polltype, multiplechoiceoptions FROM [format_table_name("poll_question")] WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime [holder ? "AND adminonly = 0" : ""]") - select_query.Execute() - - var/pollstarttime = "" - var/pollendtime = "" - var/pollquestion = "" - var/polltype = "" - var/found = 0 - var/multiplechoiceoptions = 0 - var/canvote = can_vote() - - while(select_query.NextRow()) - pollstarttime = select_query.item[1] - pollendtime = select_query.item[2] - pollquestion = select_query.item[3] - polltype = select_query.item[4] - found = 1 - break - - if(!found) - to_chat(usr, "Poll question details not found. (Maybe you do not have access?)") - return - - switch(polltype) - //Polls that have enumerated options - if(POLLTYPE_OPTION) - var/DBQuery/voted_query = dbcon.NewQuery("SELECT optionid FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'") - voted_query.Execute() - - var/voted = 0 // If the can't vote then consider them voted - var/votedoptionid = 0 - while(voted_query.NextRow()) - votedoptionid = text2num(voted_query.item[1]) - voted = 1 - break - - var/list/datum/polloption/options = list() - - var/DBQuery/options_query = dbcon.NewQuery("SELECT id, text FROM [format_table_name("poll_option")] WHERE pollid = [pollid]") - options_query.Execute() - while(options_query.NextRow()) - var/datum/polloption/PO = new() - PO.optionid = text2num(options_query.item[1]) - PO.optiontext = options_query.item[2] - options += PO - - var/output - if(!inline) - output += "
        Player poll" - output +="
        " - output += "Question: [pollquestion]
        " - output += "Poll runs from [pollstarttime] until [pollendtime]

        " - - if(canvote && !voted) //Only make this a form if we have not voted yet - output += "

        " - output += "" - output += "" - - output += "
        " - for(var/datum/polloption/O in options) - if(O.optionid && O.optiontext) - if(voted || !canvote) - if(votedoptionid == O.optionid) - output += "[O.optiontext]
        " - else - output += "[O.optiontext]
        " - else - output += " [O.optiontext]
        " - output += "
        " - - if(canvote && !voted) //Only make this a form if we have not voted yet - output += "

        " - output += "

        " - - output += "
        " - - if(inline) - return output - else - src << browse(output,"window=playerpoll;size=500x250") - - //Polls with a text input - if(POLLTYPE_TEXT) - var/DBQuery/voted_query = dbcon.NewQuery("SELECT replytext FROM [format_table_name("poll_textreply")] WHERE pollid = [pollid] AND ckey = '[ckey]'") - voted_query.Execute() - - var/voted = 0 - var/vote_text = "" - while(voted_query.NextRow()) - vote_text = voted_query.item[1] - voted = 1 - break - - - var/output - if(!inline) - output += "
        Player poll" - output +="
        " - output += "Question: [pollquestion]
        " - output += "Feedback gathering runs from [pollstarttime] until [pollendtime]

        " - - if(canvote && !voted) //Only make this a form if we have not voted yet - 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 += "" - output += "
        " - else - output += "[vote_text]" - - if(inline) - return output - else - src << browse(output,"window=playerpoll;size=500x500") - - //Polls with a text input - if(POLLTYPE_RATING) - var/DBQuery/voted_query = dbcon.NewQuery("SELECT o.text, v.rating FROM [format_table_name("poll_option")] o, erro_poll_vote v WHERE o.pollid = [pollid] AND v.ckey = '[ckey]' AND o.id = v.optionid") - voted_query.Execute() - - var/output - if(!inline) - output += "
        Player poll" - output +="
        " - output += "Question: [pollquestion]
        " - output += "Poll runs from [pollstarttime] until [pollendtime]

        " - - var/voted = 0 - while(voted_query.NextRow()) - voted = 1 - - var/optiontext = voted_query.item[1] - var/rating = voted_query.item[2] - - output += "
        [optiontext] - [rating]" - - if(canvote && !voted) //Only make this a form if we have not voted yet - output += "

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

        " - output += "

        " - - if(inline) - return output - else - src << browse(output,"window=playerpoll;size=500x500") - if(POLLTYPE_MULTI) - var/DBQuery/voted_query = dbcon.NewQuery("SELECT optionid FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'") - voted_query.Execute() - - var/list/votedfor = list() - var/voted = 0 - while(voted_query.NextRow()) - votedfor.Add(text2num(voted_query.item[1])) - voted = 1 - - var/list/datum/polloption/options = list() - var/maxoptionid = 0 - var/minoptionid = 0 - - var/DBQuery/options_query = dbcon.NewQuery("SELECT id, text FROM [format_table_name("poll_option")] WHERE pollid = [pollid]") - options_query.Execute() - while(options_query.NextRow()) - var/datum/polloption/PO = new() - PO.optionid = text2num(options_query.item[1]) - PO.optiontext = options_query.item[2] - if(PO.optionid > maxoptionid) - maxoptionid = PO.optionid - if(PO.optionid < minoptionid || !minoptionid) - minoptionid = PO.optionid - options += PO - - - if(select_query.item[5]) - multiplechoiceoptions = text2num(select_query.item[5]) - - var/output - if(!inline) - output += "
        Player poll" - output +="
        " - 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(canvote && !voted) //Only make this a form if we have not voted yet - output += "

        " - output += "" - output += "" - output += "" - output += "" - - output += "
        " - for(var/datum/polloption/O in options) - if(O.optionid && O.optiontext) - if(canvote && voted) - if(O.optionid in votedfor) - output += "[O.optiontext]
        " - else - output += "[O.optiontext]
        " - else - output += " [O.optiontext]
        " - output += "
        " - - if(canvote && !voted) //Only make this a form if we have not voted yet - output += "

        " - output += "

        " - - output += "
        " - - if(inline) - return output - else - src << browse(output,"window=playerpoll;size=600x250") - return - -/client/proc/vote_on_poll(var/pollid = -1, var/optionid = -1, var/multichoice = 0) - if(pollid == -1 || optionid == -1) - return - - if(!isnum(pollid) || !isnum(optionid)) - return - establish_db_connection() - if(dbcon.IsConnected()) - - var/DBQuery/select_query = dbcon.NewQuery("SELECT starttime, endtime, question, polltype, multiplechoiceoptions FROM [format_table_name("poll_question")] WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime [holder ? "AND adminonly = 0" : ""]") - select_query.Execute() - - var/validpoll = 0 - var/multiplechoiceoptions = 0 - - while(select_query.NextRow()) - if(select_query.item[4] != POLLTYPE_OPTION && select_query.item[4] != POLLTYPE_MULTI) - return - validpoll = 1 - if(select_query.item[5]) - multiplechoiceoptions = text2num(select_query.item[5]) - break - - if(!validpoll) - to_chat(usr, "Poll is not valid.") - return - - var/DBQuery/select_query2 = dbcon.NewQuery("SELECT id FROM [format_table_name("poll_option")] WHERE id = [optionid] AND pollid = [pollid]") - select_query2.Execute() - - var/validoption = 0 - - while(select_query2.NextRow()) - validoption = 1 - break - - if(!validoption) - to_chat(usr, "Poll option is not valid.") - return - - var/alreadyvoted = 0 - - var/DBQuery/voted_query = dbcon.NewQuery("SELECT id FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'") - voted_query.Execute() - - while(voted_query.NextRow()) - alreadyvoted += 1 - if(!multichoice) - break - - if(!multichoice && alreadyvoted) - to_chat(usr, "You already voted in this poll.") - return - - if(multichoice && (alreadyvoted >= multiplechoiceoptions)) - to_chat(usr, "You already have more than [multiplechoiceoptions] logged votes on this poll. Enough is enough. Contact the database admin if this is an error.") - return - - var/adminrank = "Player" - if(usr && usr.client && usr.client.holder) - adminrank = usr.client.holder.rank - - - var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO [format_table_name("poll_vote")] (id ,datetime ,pollid ,optionid ,ckey ,ip ,adminrank) VALUES (null, Now(), [pollid], [optionid], '[ckey]', '[usr.client.address]', '[adminrank]')") - insert_query.Execute() - - to_chat(usr, "Vote successful.") - usr << browse(null,"window=playerpoll") - - -/client/proc/log_text_poll_reply(var/pollid = -1, var/replytext = "") - if(pollid == -1 || replytext == "") - return - - if(!isnum(pollid) || !istext(replytext)) - return - establish_db_connection() - if(dbcon.IsConnected()) - - var/DBQuery/select_query = dbcon.NewQuery("SELECT starttime, endtime, question, polltype FROM [format_table_name("poll_question")] WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime [holder ? "AND adminonly = 0" : ""]") - select_query.Execute() - - var/validpoll = 0 - - while(select_query.NextRow()) - if(select_query.item[4] != POLLTYPE_TEXT) - return - validpoll = 1 - break - - if(!validpoll) - to_chat(usr, "Poll is not valid.") - return - - var/alreadyvoted = 0 - - var/DBQuery/voted_query = dbcon.NewQuery("SELECT id FROM [format_table_name("poll_textreply")] WHERE pollid = [pollid] AND ckey = '[ckey]'") - voted_query.Execute() - - while(voted_query.NextRow()) - alreadyvoted = 1 - break - - if(alreadyvoted) - to_chat(usr, "You already sent your feedback for this poll.") - return - - var/adminrank = "Player" - if(usr && usr.client && usr.client.holder) - adminrank = usr.client.holder.rank - - - replytext = replacetext(replytext, "%BR%", "") - replytext = replacetext(replytext, "\n", "%BR%") - var/text_pass = reject_bad_text(replytext,8000) - replytext = replacetext(replytext, "%BR%", "
        ") - - if(!text_pass) - to_chat(usr, "The text you entered was blank, contained illegal characters or was too long. Please correct the text and submit again.") - return - - var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO [format_table_name("poll_textreply")] (id ,datetime ,pollid ,ckey ,ip ,replytext ,adminrank) VALUES (null, Now(), [pollid], '[ckey]', '[usr.client.address]', '[replytext]', '[adminrank]')") - insert_query.Execute() - - to_chat(usr, "Feedback logging successful.") - usr << browse(null,"window=playerpoll") - - -/client/proc/vote_on_numval_poll(var/pollid = -1, var/optionid = -1, var/rating = null) - if(pollid == -1 || optionid == -1) - return - - if(!isnum(pollid) || !isnum(optionid)) - return - establish_db_connection() - if(dbcon.IsConnected()) - - var/DBQuery/select_query = dbcon.NewQuery("SELECT starttime, endtime, question, polltype FROM [format_table_name("poll_question")] WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime [holder ? "AND adminonly = 0" : ""]") - select_query.Execute() - - var/validpoll = 0 - - while(select_query.NextRow()) - if(select_query.item[4] != POLLTYPE_RATING) - return - validpoll = 1 - break - - if(!validpoll) - to_chat(usr, "Poll is not valid.") - return - - var/DBQuery/select_query2 = dbcon.NewQuery("SELECT id FROM [format_table_name("poll_option")] WHERE id = [optionid] AND pollid = [pollid]") - select_query2.Execute() - - var/validoption = 0 - - while(select_query2.NextRow()) - validoption = 1 - break - - if(!validoption) - to_chat(usr, "Poll option is not valid.") - return - - var/alreadyvoted = 0 - - var/DBQuery/voted_query = dbcon.NewQuery("SELECT id FROM [format_table_name("poll_vote")] WHERE optionid = [optionid] AND ckey = '[ckey]'") - voted_query.Execute() - - while(voted_query.NextRow()) - alreadyvoted = 1 - break - - if(alreadyvoted) - to_chat(usr, "You already voted in this poll.") - return - - var/adminrank = "Player" - if(usr && usr.client && usr.client.holder) - adminrank = usr.client.holder.rank - - - var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO [format_table_name("poll_vote")] (id ,datetime ,pollid ,optionid ,ckey ,ip ,adminrank, rating) VALUES (null, Now(), [pollid], [optionid], '[ckey]', '[usr.client.address]', '[adminrank]', [(isnull(rating)) ? "null" : rating])") - insert_query.Execute() - - to_chat(usr, "Vote successful.") - usr << browse(null,"window=playerpoll") \ No newline at end of file +/datum/polloption + var/optionid + var/optiontext + +/client/verb/polls_verb() + set name = "Show Player Polls" + set category = "OOC" + handle_player_polling() + +/client/proc/can_vote() + return istext(player_age) || player_age >= 30 + +/client/proc/handle_player_polling() + establish_db_connection() + if(dbcon.IsConnected()) + var/isadmin = 0 + if(holder) + isadmin = 1 + + var/DBQuery/select_query = dbcon.NewQuery("SELECT id, question, (id IN (SELECT pollid FROM [format_table_name("poll_vote")] WHERE ckey = '[ckey]') OR id IN (SELECT pollid FROM [format_table_name("poll_textreply")] WHERE ckey = '[ckey]')) AS voted FROM [format_table_name("poll_question")] WHERE [(isadmin ? "" : "adminonly = false AND")] Now() BETWEEN starttime AND endtime") + select_query.Execute() + + var/output = "
        Player polls" + if(check_rights(R_SERVER)) + output += "(Create new poll)" + output +="
        " + + output += "" + var/color1 = "#ececec" + var/color2 = "#e2e2e2" + var/i = 0 + + output += "" + while(select_query.NextRow()) + var/pollid = select_query.item[1] + var/pollquestion = select_query.item[2] + var/voted = text2num(select_query.item[3]) + output += "" + if(can_vote() && !voted) + output += "" + i++ + + // Show expired polls. Non admins can view admin polls at this stage + // (just like tgstation's web interface so don't complain) + // (Also why was there no ingame interface tg not having an ingame + // interface is retarded because it cucks downstreams) + select_query = dbcon.NewQuery("SELECT id, question FROM [format_table_name("poll_question")] WHERE Now() > endtime ORDER BY id DESC") + select_query.Execute() + + output += "" + while(select_query.NextRow()) + var/pollid = select_query.item[1] + var/pollquestion = select_query.item[2] + output += "" + + output += "
        Active Polls
        [pollquestion]
        [poll_player(pollid, 1)]
        Expired Polls
        [pollquestion]
        " + + src << browse(output,"window=playerpolllist;size=500x300") + +/client/proc/poll_results(var/pollid = -1) + if(pollid == -1) + return + establish_db_connection() + if(!dbcon.IsConnected()) + return + var/DBQuery/select_query = dbcon.NewQuery("SELECT polltype, question, adminonly, multiplechoiceoptions, starttime, endtime FROM [format_table_name("poll_question")] WHERE id = [pollid] AND endtime < Now()") + select_query.Execute() + var/question = "" + var/polltype = "" + var/adminonly = 0 + var/multiplechoiceoptions = 0 + var/starttime = "" + var/endtime = "" + var/found = 0 + while(select_query.NextRow()) + polltype = select_query.item[1] + question = select_query.item[2] + adminonly = text2num(select_query.item[3]) + multiplechoiceoptions = text2num(select_query.item[4]) + starttime = select_query.item[5] + endtime = select_query.item[6] + found = 1 + break + if(!found) + to_chat(src, "Poll question details not found. (Maybe the poll isn't expired yet?)") + return + + if(polltype == POLLTYPE_MULTI) + question += " (Choose up to [multiplechoiceoptions] options)" + if(adminonly) + question = "(Admin only poll) " + question + + var output = "" + if(polltype == POLLTYPE_MULTI || polltype == POLLTYPE_OPTION) + select_query = dbcon.NewQuery("SELECT text, percentagecalc, (SELECT COUNT(optionid) FROM [format_table_name("poll_vote")] WHERE optionid = poll_option.id GROUP BY optionid) AS votecount FROM [format_table_name("poll_option")] WHERE pollid = [pollid]"); + select_query.Execute() + var/list/options = list() + var/total_votes = 1 + var/total_percent_votes = 1 + var/max_votes = 1 + while(select_query.NextRow()) + var/text = select_query.item[1] + var/percentagecalc = select_query.item[2] + var/votecount = text2num(select_query.item[3]) + if(percentagecalc) + total_percent_votes += votecount + total_votes += votecount + if(votecount > max_votes) + max_votes = votecount + options[++options.len] = list(text, percentagecalc, votecount) + // fuck ie. + output += {" + + + + + + " + for(var/list/option in options) + var/bar_width = option[3] * 390 / max_votes + var/percentage = option[2] ? "[round(option[3] * 100 / total_percent_votes)]%" : "N/A" + output += "" + output += "" + output += "" + output += "" + output += "
        [question]
        [starttime] - [endtime]
        "} + var/list/colors = list("#66c2a5", "#fc8d62", "#8da0cb", "#e78ac3", "#a6d854", "#ffd92f", "#e5c494", "#b3b3b3") + var/color_index = 0 + for(var/list/option in options) + var/bar_width = option[3] * 700 / total_votes + var/percentage = option[2] ? "[round(option[3] * 100 / total_percent_votes)]%" : "N/A" + color_index++ + if(color_index > colors.len) + color_index = 1 + output += "
        " + output += "

        (Hover over the colored bar to read description)
        [option[1]][option[3]][percentage]
        " + if(polltype == POLLTYPE_RATING) + output += {" + + + + "} + select_query = dbcon.NewQuery("SELECT id, text, (SELECT AVG(rating) FROM [format_table_name("poll_vote")] WHERE optionid = poll_option.id AND rating != 'abstain') AS avgrating, (SELECT COUNT(rating) FROM [format_table_name("poll_vote")] WHERE optionid = poll_option.id AND rating != 'abstain') AS countvotes, minval, maxval FROM [format_table_name("poll_option")] WHERE pollid = [pollid]") + select_query.Execute() + while(select_query.NextRow()) + output += {" + + " + output += "
        [question]
        [starttime] - [endtime]
        [select_query.item[2]] + N = [select_query.item[4]] + AVG = [select_query.item[3]] + "} + var/optionid = select_query.item[1] + var/totalvotes = text2num(select_query.item[4]) + var/minval = text2num(select_query.item[5]) + var/maxval = text2num(select_query.item[6]) + var/maxvote = 1 + var/list/votecounts = list() + for(var/I in minval to maxval) + var/DBQuery/rating_query = dbcon.NewQuery("SELECT COUNT(rating) AS countrating FROM [format_table_name("poll_vote")] WHERE optionid = [optionid] AND rating = [I] GROUP BY rating") + rating_query.Execute() + var/votecount = 0 + while(rating_query.NextRow()) + votecount = text2num(rating_query.item[1]) + votecounts["[I]"] = votecount + if(votecount > maxvote) + maxvote = votecount + for(var/I in minval to maxval) + var/votecount = votecounts["[I]"] + var/bar_width = votecount * 200 / maxvote + output += {" + + + + + + "} + output += "
        [I][votecount]([votecount / totalvotes]%)
        " + if(polltype == POLLTYPE_TEXT) + select_query = dbcon.NewQuery("SELECT replytext, COUNT(replytext) AS countresponse, GROUP_CONCAT(DISTINCT ckey SEPARATOR ', ') as ckeys FROM [format_table_name("poll_textreply")] WHERE pollid = [pollid] GROUP BY replytext ORDER BY countresponse DESC"); + select_query.Execute() + output += {" + + + + "} + while(select_query.NextRow()) + var/replytext = select_query.item[1] + var/countresponse = select_query.item[2] + var/ckeys = select_query.item[3] + output += {" + + + + "} + output += "
        [question]
        [starttime] - [endtime]
        [ckeys] ([countresponse] player\s) responded with:[replytext]
        " + output += "" + + src << browse(output,"window=pollresults;size=950x500") + +/client/proc/poll_player(var/pollid = -1, var/inline = 0) + if(pollid == -1) return + establish_db_connection() + if(dbcon.IsConnected()) + + var/DBQuery/select_query = dbcon.NewQuery("SELECT starttime, endtime, question, polltype, multiplechoiceoptions FROM [format_table_name("poll_question")] WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime [holder ? "AND adminonly = 0" : ""]") + select_query.Execute() + + var/pollstarttime = "" + var/pollendtime = "" + var/pollquestion = "" + var/polltype = "" + var/found = 0 + var/multiplechoiceoptions = 0 + var/canvote = can_vote() + + while(select_query.NextRow()) + pollstarttime = select_query.item[1] + pollendtime = select_query.item[2] + pollquestion = select_query.item[3] + polltype = select_query.item[4] + found = 1 + break + + if(!found) + to_chat(usr, "Poll question details not found. (Maybe you do not have access?)") + return + + switch(polltype) + //Polls that have enumerated options + if(POLLTYPE_OPTION) + var/DBQuery/voted_query = dbcon.NewQuery("SELECT optionid FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'") + voted_query.Execute() + + var/voted = 0 // If the can't vote then consider them voted + var/votedoptionid = 0 + while(voted_query.NextRow()) + votedoptionid = text2num(voted_query.item[1]) + voted = 1 + break + + var/list/datum/polloption/options = list() + + var/DBQuery/options_query = dbcon.NewQuery("SELECT id, text FROM [format_table_name("poll_option")] WHERE pollid = [pollid]") + options_query.Execute() + while(options_query.NextRow()) + var/datum/polloption/PO = new() + PO.optionid = text2num(options_query.item[1]) + PO.optiontext = options_query.item[2] + options += PO + + var/output + if(!inline) + output += "
        Player poll" + output +="
        " + output += "Question: [pollquestion]
        " + output += "Poll runs from [pollstarttime] until [pollendtime]

        " + + if(canvote && !voted) //Only make this a form if we have not voted yet + output += "

        " + output += "" + output += "" + + output += "
        " + for(var/datum/polloption/O in options) + if(O.optionid && O.optiontext) + if(voted || !canvote) + if(votedoptionid == O.optionid) + output += "[O.optiontext]
        " + else + output += "[O.optiontext]
        " + else + output += " [O.optiontext]
        " + output += "
        " + + if(canvote && !voted) //Only make this a form if we have not voted yet + output += "

        " + output += "

        " + + output += "
        " + + if(inline) + return output + else + src << browse(output,"window=playerpoll;size=500x250") + + //Polls with a text input + if(POLLTYPE_TEXT) + var/DBQuery/voted_query = dbcon.NewQuery("SELECT replytext FROM [format_table_name("poll_textreply")] WHERE pollid = [pollid] AND ckey = '[ckey]'") + voted_query.Execute() + + var/voted = 0 + var/vote_text = "" + while(voted_query.NextRow()) + vote_text = voted_query.item[1] + voted = 1 + break + + + var/output + if(!inline) + output += "
        Player poll" + output +="
        " + output += "Question: [pollquestion]
        " + output += "Feedback gathering runs from [pollstarttime] until [pollendtime]

        " + + if(canvote && !voted) //Only make this a form if we have not voted yet + 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 += "" + output += "
        " + else + output += "[vote_text]" + + if(inline) + return output + else + src << browse(output,"window=playerpoll;size=500x500") + + //Polls with a text input + if(POLLTYPE_RATING) + var/DBQuery/voted_query = dbcon.NewQuery("SELECT o.text, v.rating FROM [format_table_name("poll_option")] o, erro_poll_vote v WHERE o.pollid = [pollid] AND v.ckey = '[ckey]' AND o.id = v.optionid") + voted_query.Execute() + + var/output + if(!inline) + output += "
        Player poll" + output +="
        " + output += "Question: [pollquestion]
        " + output += "Poll runs from [pollstarttime] until [pollendtime]

        " + + var/voted = 0 + while(voted_query.NextRow()) + voted = 1 + + var/optiontext = voted_query.item[1] + var/rating = voted_query.item[2] + + output += "
        [optiontext] - [rating]" + + if(canvote && !voted) //Only make this a form if we have not voted yet + output += "

        " + output += "" + output += "" + + var/minid = 999999 + var/maxid = 0 + + var/DBQuery/option_query = dbcon.NewQuery("SELECT id, text, minval, maxval, descmin, descmid, descmax FROM [format_table_name("poll_option")] WHERE pollid = [pollid]") + option_query.Execute() + while(option_query.NextRow()) + var/optionid = text2num(option_query.item[1]) + var/optiontext = option_query.item[2] + var/minvalue = text2num(option_query.item[3]) + var/maxvalue = text2num(option_query.item[4]) + var/descmin = option_query.item[5] + var/descmid = option_query.item[6] + var/descmax = option_query.item[7] + + if(optionid < minid) + minid = optionid + if(optionid > maxid) + maxid = optionid + + var/midvalue = round( (maxvalue + minvalue) / 2) + + if(isnull(minvalue) || isnull(maxvalue) || (minvalue == maxvalue)) + continue + + output += "
        [optiontext]: " + + output += "" + output += "" + + output += "

        " + output += "

        " + + if(inline) + return output + else + src << browse(output,"window=playerpoll;size=500x500") + if(POLLTYPE_MULTI) + var/DBQuery/voted_query = dbcon.NewQuery("SELECT optionid FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'") + voted_query.Execute() + + var/list/votedfor = list() + var/voted = 0 + while(voted_query.NextRow()) + votedfor.Add(text2num(voted_query.item[1])) + voted = 1 + + var/list/datum/polloption/options = list() + var/maxoptionid = 0 + var/minoptionid = 0 + + var/DBQuery/options_query = dbcon.NewQuery("SELECT id, text FROM [format_table_name("poll_option")] WHERE pollid = [pollid]") + options_query.Execute() + while(options_query.NextRow()) + var/datum/polloption/PO = new() + PO.optionid = text2num(options_query.item[1]) + PO.optiontext = options_query.item[2] + if(PO.optionid > maxoptionid) + maxoptionid = PO.optionid + if(PO.optionid < minoptionid || !minoptionid) + minoptionid = PO.optionid + options += PO + + + if(select_query.item[5]) + multiplechoiceoptions = text2num(select_query.item[5]) + + var/output + if(!inline) + output += "
        Player poll" + output +="
        " + 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(canvote && !voted) //Only make this a form if we have not voted yet + output += "

        " + output += "" + output += "" + output += "" + output += "" + + output += "
        " + for(var/datum/polloption/O in options) + if(O.optionid && O.optiontext) + if(canvote && voted) + if(O.optionid in votedfor) + output += "[O.optiontext]
        " + else + output += "[O.optiontext]
        " + else + output += " [O.optiontext]
        " + output += "
        " + + if(canvote && !voted) //Only make this a form if we have not voted yet + output += "

        " + output += "

        " + + output += "
        " + + if(inline) + return output + else + src << browse(output,"window=playerpoll;size=600x250") + return + +/client/proc/vote_on_poll(var/pollid = -1, var/optionid = -1, var/multichoice = 0) + if(pollid == -1 || optionid == -1) + return + + if(!isnum(pollid) || !isnum(optionid)) + return + establish_db_connection() + if(dbcon.IsConnected()) + + var/DBQuery/select_query = dbcon.NewQuery("SELECT starttime, endtime, question, polltype, multiplechoiceoptions FROM [format_table_name("poll_question")] WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime [holder ? "AND adminonly = 0" : ""]") + select_query.Execute() + + var/validpoll = 0 + var/multiplechoiceoptions = 0 + + while(select_query.NextRow()) + if(select_query.item[4] != POLLTYPE_OPTION && select_query.item[4] != POLLTYPE_MULTI) + return + validpoll = 1 + if(select_query.item[5]) + multiplechoiceoptions = text2num(select_query.item[5]) + break + + if(!validpoll) + to_chat(usr, "Poll is not valid.") + return + + var/DBQuery/select_query2 = dbcon.NewQuery("SELECT id FROM [format_table_name("poll_option")] WHERE id = [optionid] AND pollid = [pollid]") + select_query2.Execute() + + var/validoption = 0 + + while(select_query2.NextRow()) + validoption = 1 + break + + if(!validoption) + to_chat(usr, "Poll option is not valid.") + return + + var/alreadyvoted = 0 + + var/DBQuery/voted_query = dbcon.NewQuery("SELECT id FROM [format_table_name("poll_vote")] WHERE pollid = [pollid] AND ckey = '[ckey]'") + voted_query.Execute() + + while(voted_query.NextRow()) + alreadyvoted += 1 + if(!multichoice) + break + + if(!multichoice && alreadyvoted) + to_chat(usr, "You already voted in this poll.") + return + + if(multichoice && (alreadyvoted >= multiplechoiceoptions)) + to_chat(usr, "You already have more than [multiplechoiceoptions] logged votes on this poll. Enough is enough. Contact the database admin if this is an error.") + return + + var/adminrank = "Player" + if(usr && usr.client && usr.client.holder) + adminrank = usr.client.holder.rank + + + var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO [format_table_name("poll_vote")] (id ,datetime ,pollid ,optionid ,ckey ,ip ,adminrank) VALUES (null, Now(), [pollid], [optionid], '[ckey]', '[usr.client.address]', '[adminrank]')") + insert_query.Execute() + + to_chat(usr, "Vote successful.") + usr << browse(null,"window=playerpoll") + + +/client/proc/log_text_poll_reply(var/pollid = -1, var/replytext = "") + if(pollid == -1 || replytext == "") + return + + if(!isnum(pollid) || !istext(replytext)) + return + establish_db_connection() + if(dbcon.IsConnected()) + + var/DBQuery/select_query = dbcon.NewQuery("SELECT starttime, endtime, question, polltype FROM [format_table_name("poll_question")] WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime [holder ? "AND adminonly = 0" : ""]") + select_query.Execute() + + var/validpoll = 0 + + while(select_query.NextRow()) + if(select_query.item[4] != POLLTYPE_TEXT) + return + validpoll = 1 + break + + if(!validpoll) + to_chat(usr, "Poll is not valid.") + return + + var/alreadyvoted = 0 + + var/DBQuery/voted_query = dbcon.NewQuery("SELECT id FROM [format_table_name("poll_textreply")] WHERE pollid = [pollid] AND ckey = '[ckey]'") + voted_query.Execute() + + while(voted_query.NextRow()) + alreadyvoted = 1 + break + + if(alreadyvoted) + to_chat(usr, "You already sent your feedback for this poll.") + return + + var/adminrank = "Player" + if(usr && usr.client && usr.client.holder) + adminrank = usr.client.holder.rank + + + replytext = replacetext(replytext, "%BR%", "") + replytext = replacetext(replytext, "\n", "%BR%") + var/text_pass = reject_bad_text(replytext,8000) + replytext = replacetext(replytext, "%BR%", "
        ") + + if(!text_pass) + to_chat(usr, "The text you entered was blank, contained illegal characters or was too long. Please correct the text and submit again.") + return + + var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO [format_table_name("poll_textreply")] (id ,datetime ,pollid ,ckey ,ip ,replytext ,adminrank) VALUES (null, Now(), [pollid], '[ckey]', '[usr.client.address]', '[replytext]', '[adminrank]')") + insert_query.Execute() + + to_chat(usr, "Feedback logging successful.") + usr << browse(null,"window=playerpoll") + + +/client/proc/vote_on_numval_poll(var/pollid = -1, var/optionid = -1, var/rating = null) + if(pollid == -1 || optionid == -1) + return + + if(!isnum(pollid) || !isnum(optionid)) + return + establish_db_connection() + if(dbcon.IsConnected()) + + var/DBQuery/select_query = dbcon.NewQuery("SELECT starttime, endtime, question, polltype FROM [format_table_name("poll_question")] WHERE id = [pollid] AND Now() BETWEEN starttime AND endtime [holder ? "AND adminonly = 0" : ""]") + select_query.Execute() + + var/validpoll = 0 + + while(select_query.NextRow()) + if(select_query.item[4] != POLLTYPE_RATING) + return + validpoll = 1 + break + + if(!validpoll) + to_chat(usr, "Poll is not valid.") + return + + var/DBQuery/select_query2 = dbcon.NewQuery("SELECT id FROM [format_table_name("poll_option")] WHERE id = [optionid] AND pollid = [pollid]") + select_query2.Execute() + + var/validoption = 0 + + while(select_query2.NextRow()) + validoption = 1 + break + + if(!validoption) + to_chat(usr, "Poll option is not valid.") + return + + var/alreadyvoted = 0 + + var/DBQuery/voted_query = dbcon.NewQuery("SELECT id FROM [format_table_name("poll_vote")] WHERE optionid = [optionid] AND ckey = '[ckey]'") + voted_query.Execute() + + while(voted_query.NextRow()) + alreadyvoted = 1 + break + + if(alreadyvoted) + to_chat(usr, "You already voted in this poll.") + return + + var/adminrank = "Player" + if(usr && usr.client && usr.client.holder) + adminrank = usr.client.holder.rank + + + var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO [format_table_name("poll_vote")] (id ,datetime ,pollid ,optionid ,ckey ,ip ,adminrank, rating) VALUES (null, Now(), [pollid], [optionid], '[ckey]', '[usr.client.address]', '[adminrank]', [(isnull(rating)) ? "null" : rating])") + insert_query.Execute() + + to_chat(usr, "Vote successful.") + usr << browse(null,"window=playerpoll") diff --git a/code/modules/mob/new_player/preferences_setup.dm b/code/modules/mob/new_player/preferences_setup.dm index 64f9e139542..3489e56be54 100644 --- a/code/modules/mob/new_player/preferences_setup.dm +++ b/code/modules/mob/new_player/preferences_setup.dm @@ -1,957 +1,957 @@ -/datum/preferences - //The mob should have a gender you want before running this proc. Will run fine without H -/datum/preferences/proc/random_character(gender_override) - var/datum/species/S = GLOB.all_species[species] - if(!istype(S)) //The species was invalid. Set the species to the default, fetch the datum for that species and generate a random character. - species = initial(species) - S = GLOB.all_species[species] - var/datum/robolimb/robohead - - if(S.bodyflags & ALL_RPARTS) - var/head_model = "[!rlimb_data["head"] ? "Morpheus Cyberkinetics" : rlimb_data["head"]]" - robohead = all_robolimbs[head_model] - if(gender_override) - gender = gender_override - else - gender = pick(MALE, FEMALE) - underwear = random_underwear(gender, species) - undershirt = random_undershirt(gender, species) - socks = random_socks(gender, species) - if(body_accessory_by_species[species]) - body_accessory = random_body_accessory(species) - if(body_accessory == "None") //Required to prevent a bug where the information/icons in the character preferences screen wouldn't update despite the data being changed. - body_accessory = null - if(S.bodyflags & (HAS_SKIN_TONE|HAS_ICON_SKIN_TONE)) - s_tone = random_skin_tone(species) - h_style = random_hair_style(gender, species, robohead) - f_style = random_facial_hair_style(gender, species, robohead) - if(species in list("Human", "Unathi", "Tajaran", "Skrell", "Machine", "Wryn", "Vulpkanin", "Vox")) - randomize_hair_color("hair") - randomize_hair_color("facial") - if(S.bodyflags & HAS_HEAD_ACCESSORY) - ha_style = random_head_accessory(species) - hacc_colour = randomize_skin_color(1) - if(S.bodyflags & HAS_HEAD_MARKINGS) - m_styles["head"] = random_marking_style("head", species, robohead, null, alt_head) - m_colours["head"] = randomize_skin_color(1) - if(S.bodyflags & HAS_BODY_MARKINGS) - m_styles["body"] = random_marking_style("body", species) - m_colours["body"] = randomize_skin_color(1) - if(S.bodyflags & HAS_TAIL_MARKINGS) //Species with tail markings. - m_styles["tail"] = random_marking_style("tail", species, null, body_accessory) - m_colours["tail"] = randomize_skin_color(1) - if(!(S.bodyflags & ALL_RPARTS)) - randomize_eyes_color() - if(S.bodyflags & HAS_SKIN_COLOR) - randomize_skin_color() - backbag = 2 - age = rand(AGE_MIN, AGE_MAX) - - -/datum/preferences/proc/randomize_hair_color(var/target = "hair") - if(prob (75) && target == "facial") // Chance to inherit hair color - f_colour = h_colour - return - - var/red - var/green - var/blue - - var/col = pick ("blonde", "black", "chestnut", "copper", "brown", "wheat", "old", "punk") - switch(col) - if("blonde") - red = 255 - green = 255 - blue = 0 - if("black") - red = 0 - green = 0 - blue = 0 - if("chestnut") - red = 153 - green = 102 - blue = 51 - if("copper") - red = 255 - green = 153 - blue = 0 - if("brown") - red = 102 - green = 51 - blue = 0 - if("wheat") - red = 255 - green = 255 - blue = 153 - if("old") - red = rand (100, 255) - green = red - blue = red - if("punk") - red = rand (0, 255) - green = rand (0, 255) - blue = rand (0, 255) - - red = max(min(red + rand (-25, 25), 255), 0) - green = max(min(green + rand (-25, 25), 255), 0) - blue = max(min(blue + rand (-25, 25), 255), 0) - - switch(target) - if("hair") - h_colour = rgb(red, green, blue) - if("facial") - f_colour = rgb(red, green, blue) - -/datum/preferences/proc/randomize_eyes_color() - var/red - var/green - var/blue - - var/col = pick ("black", "grey", "brown", "chestnut", "blue", "lightblue", "green", "albino") - switch(col) - if("black") - red = 0 - green = 0 - blue = 0 - if("grey") - red = rand (100, 200) - green = red - blue = red - if("brown") - red = 102 - green = 51 - blue = 0 - if("chestnut") - red = 153 - green = 102 - blue = 0 - if("blue") - red = 51 - green = 102 - blue = 204 - if("lightblue") - red = 102 - green = 204 - blue = 255 - if("green") - red = 0 - green = 102 - blue = 0 - if("albino") - red = rand (200, 255) - green = rand (0, 150) - blue = rand (0, 150) - - red = max(min(red + rand (-25, 25), 255), 0) - green = max(min(green + rand (-25, 25), 255), 0) - blue = max(min(blue + rand (-25, 25), 255), 0) - - e_colour = rgb(red, green, blue) - -/datum/preferences/proc/randomize_skin_color(var/pass_on) - var/red - var/green - var/blue - - var/col = pick ("black", "grey", "brown", "chestnut", "blue", "lightblue", "green", "albino") - switch(col) - if("black") - red = 0 - green = 0 - blue = 0 - if("grey") - red = rand (100, 200) - green = red - blue = red - if("brown") - red = 102 - green = 51 - blue = 0 - if("chestnut") - red = 153 - green = 102 - blue = 0 - if("blue") - red = 51 - green = 102 - blue = 204 - if("lightblue") - red = 102 - green = 204 - blue = 255 - if("green") - red = 0 - green = 102 - blue = 0 - if("albino") - red = rand (200, 255) - green = rand (0, 150) - blue = rand (0, 150) - - red = max(min(red + rand (-25, 25), 255), 0) - green = max(min(green + rand (-25, 25), 255), 0) - blue = max(min(blue + rand (-25, 25), 255), 0) - - if(pass_on) - return rgb(red, green, blue) - else - s_colour = rgb(red, green, blue) - -/datum/preferences/proc/blend_backpack(var/icon/clothes_s,var/backbag,var/satchel,var/backpack="backpack") - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', backpack), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', satchel), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - return clothes_s - -/datum/preferences/proc/update_preview_icon(var/for_observer=0) //seriously. This is horrendous. - qdel(preview_icon_front) - qdel(preview_icon_side) - qdel(preview_icon) - - var/g = "m" - if(gender == FEMALE) g = "f" - - var/icon/icobase - var/datum/species/current_species = GLOB.all_species[species] - - //Icon-based species colour. - var/coloured_tail - if(current_species) - if(current_species.bodyflags & HAS_ICON_SKIN_TONE) //Handling species-specific icon-based skin tones by flagged race. - var/mob/living/carbon/human/H = new - H.dna.species = current_species - H.s_tone = s_tone - H.dna.species.updatespeciescolor(H, 0) //The mob's species wasn't set, so it's almost certainly different than the character's species at the moment. Thus, we need to be owner-insensitive. - var/obj/item/organ/external/chest/C = H.get_organ("chest") - icobase = C.icobase ? C.icobase : C.dna.species.icobase - if(H.dna.species.bodyflags & HAS_TAIL) - coloured_tail = H.tail ? H.tail : H.dna.species.tail - - qdel(H) - else - icobase = current_species.icobase - else - icobase = 'icons/mob/human_races/r_human.dmi' - - var/fat="" - if(disabilities & DISABILITY_FLAG_FAT && (CAN_BE_FAT in current_species.species_traits)) - fat="_fat" - preview_icon = new /icon(icobase, "torso_[g][fat]") - preview_icon.Blend(new /icon(icobase, "groin_[g]"), ICON_OVERLAY) - var/head = "head" - if(alt_head && current_species.bodyflags & HAS_ALT_HEADS) - var/datum/sprite_accessory/alt_heads/H = GLOB.alt_heads_list[alt_head] - if(H.icon_state) - head = H.icon_state - preview_icon.Blend(new /icon(icobase, "[head]_[g]"), ICON_OVERLAY) - - for(var/name in list("chest", "groin", "head", "r_arm", "r_hand", "r_leg", "r_foot", "l_leg", "l_foot", "l_arm", "l_hand")) - if(organ_data[name] == "amputated") continue - if(organ_data[name] == "cyborg") - var/datum/robolimb/R - if(rlimb_data[name]) R = all_robolimbs[rlimb_data[name]] - if(!R) R = basic_robolimb - if(name == "chest") - name = "torso" - preview_icon.Blend(icon(R.icon, "[name]"), ICON_OVERLAY) // This doesn't check gendered_icon. Not an issue while only limbs can be robotic. - continue - preview_icon.Blend(new /icon(icobase, "[name]"), ICON_OVERLAY) - - // Skin color - if(current_species && (current_species.bodyflags & HAS_SKIN_COLOR)) - preview_icon.Blend(s_colour, ICON_ADD) - - // Skin tone - if(current_species && (current_species.bodyflags & HAS_SKIN_TONE)) - if(s_tone >= 0) - preview_icon.Blend(rgb(s_tone, s_tone, s_tone), ICON_ADD) - else - preview_icon.Blend(rgb(-s_tone, -s_tone, -s_tone), ICON_SUBTRACT) - - //Tail - if(current_species && (current_species.bodyflags & HAS_TAIL)) - var/tail_icon - var/tail_icon_state - var/tail_shift_x - var/tail_shift_y - var/blend_mode = ICON_ADD - - if(body_accessory) - var/datum/body_accessory/accessory = body_accessory_by_name[body_accessory] - tail_icon = accessory.icon - tail_icon_state = accessory.icon_state - if(accessory.blend_mode) - blend_mode = accessory.blend_mode - if(accessory.pixel_x_offset) - tail_shift_x = accessory.pixel_x_offset - if(accessory.pixel_y_offset) - tail_shift_y = accessory.pixel_y_offset - else - tail_icon = "icons/effects/species.dmi" - if(coloured_tail) - tail_icon_state = "[coloured_tail]_s" - else - tail_icon_state = "[current_species.tail]_s" - - var/icon/temp = new/icon("icon" = tail_icon, "icon_state" = tail_icon_state) - if(tail_shift_x) - temp.Shift(EAST, tail_shift_x) - if(tail_shift_y) - temp.Shift(NORTH, tail_shift_y) - - if(current_species && (current_species.bodyflags & HAS_SKIN_COLOR)) - temp.Blend(s_colour, blend_mode) - - if(current_species && (current_species.bodyflags & HAS_TAIL_MARKINGS)) - var/tail_marking = m_styles["tail"] - var/datum/sprite_accessory/tail_marking_style = GLOB.marking_styles_list[tail_marking] - if(tail_marking_style && tail_marking_style.species_allowed) - var/icon/t_marking_s = new/icon("icon" = tail_marking_style.icon, "icon_state" = "[tail_marking_style.icon_state]_s") - t_marking_s.Blend(m_colours["tail"], ICON_ADD) - temp.Blend(t_marking_s, ICON_OVERLAY) - - preview_icon.Blend(temp, ICON_OVERLAY) - - //Markings - if(current_species && ((current_species.bodyflags & HAS_HEAD_MARKINGS) || (current_species.bodyflags & HAS_BODY_MARKINGS))) - if(current_species.bodyflags & HAS_BODY_MARKINGS) //Body markings. - var/body_marking = m_styles["body"] - var/datum/sprite_accessory/body_marking_style = GLOB.marking_styles_list[body_marking] - if(body_marking_style && body_marking_style.species_allowed) - var/icon/b_marking_s = new/icon("icon" = body_marking_style.icon, "icon_state" = "[body_marking_style.icon_state]_s") - b_marking_s.Blend(m_colours["body"], ICON_ADD) - preview_icon.Blend(b_marking_s, ICON_OVERLAY) - if(current_species.bodyflags & HAS_HEAD_MARKINGS) //Head markings. - var/head_marking = m_styles["head"] - var/datum/sprite_accessory/head_marking_style = GLOB.marking_styles_list[head_marking] - if(head_marking_style && head_marking_style.species_allowed) - var/icon/h_marking_s = new/icon("icon" = head_marking_style.icon, "icon_state" = "[head_marking_style.icon_state]_s") - h_marking_s.Blend(m_colours["head"], ICON_ADD) - preview_icon.Blend(h_marking_s, ICON_OVERLAY) - - - var/icon/face_s = new/icon("icon" = 'icons/mob/human_face.dmi', "icon_state" = "bald_s") - if(!(current_species.bodyflags & NO_EYES)) - var/icon/eyes_s = new/icon("icon" = 'icons/mob/human_face.dmi', "icon_state" = current_species ? current_species.eyes : "eyes_s") - eyes_s.Blend(e_colour, ICON_ADD) - face_s.Blend(eyes_s, ICON_OVERLAY) - - - var/datum/sprite_accessory/hair_style = GLOB.hair_styles_full_list[h_style] - if(hair_style) - var/icon/hair_s = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_s") - if(current_species.name == "Slime People") // whee I am part of the problem - hair_s.Blend("[s_colour]A0", ICON_ADD) - else if(hair_style.do_colouration) - hair_s.Blend(h_colour, ICON_ADD) - - if(hair_style.secondary_theme) - var/icon/hair_secondary_s = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_[hair_style.secondary_theme]_s") - if(!hair_style.no_sec_colour && hair_style.do_colouration ) - hair_secondary_s.Blend(h_sec_colour, ICON_ADD) - hair_s.Blend(hair_secondary_s, ICON_OVERLAY) - - face_s.Blend(hair_s, ICON_OVERLAY) - - //Head Accessory - if(current_species && (current_species.bodyflags & HAS_HEAD_ACCESSORY)) - var/datum/sprite_accessory/head_accessory_style = GLOB.head_accessory_styles_list[ha_style] - if(head_accessory_style && head_accessory_style.species_allowed) - var/icon/head_accessory_s = new/icon("icon" = head_accessory_style.icon, "icon_state" = "[head_accessory_style.icon_state]_s") - head_accessory_s.Blend(hacc_colour, ICON_ADD) - face_s.Blend(head_accessory_s, ICON_OVERLAY) - - var/datum/sprite_accessory/facial_hair_style = GLOB.facial_hair_styles_list[f_style] - if(facial_hair_style && facial_hair_style.species_allowed) - var/icon/facial_s = new/icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_s") - if(current_species.name == "Slime People") // whee I am part of the problem - facial_s.Blend("[s_colour]A0", ICON_ADD) - else if(facial_hair_style.do_colouration) - facial_s.Blend(f_colour, ICON_ADD) - - if(facial_hair_style.secondary_theme) - var/icon/facial_secondary_s = new/icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_[facial_hair_style.secondary_theme]_s") - if(!facial_hair_style.no_sec_colour && facial_hair_style.do_colouration) - facial_secondary_s.Blend(f_sec_colour, ICON_ADD) - facial_s.Blend(facial_secondary_s, ICON_OVERLAY) - - face_s.Blend(facial_s, ICON_OVERLAY) - - var/icon/underwear_s = null - if(underwear && (current_species.clothing_flags & HAS_UNDERWEAR)) - var/datum/sprite_accessory/underwear/U = GLOB.underwear_list[underwear] - if(U) - var/u_icon = U.sprite_sheets && (current_species.name in U.sprite_sheets) ? U.sprite_sheets[current_species.name] : U.icon //Species-fit the undergarment. - underwear_s = new/icon(u_icon, "uw_[U.icon_state]_s", ICON_OVERLAY) - - var/icon/undershirt_s = null - if(undershirt && (current_species.clothing_flags & HAS_UNDERSHIRT)) - var/datum/sprite_accessory/undershirt/U2 = GLOB.undershirt_list[undershirt] - if(U2) - var/u2_icon = U2.sprite_sheets && (current_species.name in U2.sprite_sheets) ? U2.sprite_sheets[current_species.name] : U2.icon - undershirt_s = new/icon(u2_icon, "us_[U2.icon_state]_s", ICON_OVERLAY) - - var/icon/socks_s = null - if(socks && (current_species.clothing_flags & HAS_SOCKS)) - var/datum/sprite_accessory/socks/U3 = GLOB.socks_list[socks] - if(U3) - var/u3_icon = U3.sprite_sheets && (current_species.name in U3.sprite_sheets) ? U3.sprite_sheets[current_species.name] : U3.icon - socks_s = new/icon(u3_icon, "sk_[U3.icon_state]_s", ICON_OVERLAY) - - var/icon/clothes_s = null - var/uniform_dmi='icons/mob/uniform.dmi' - if(disabilities&DISABILITY_FLAG_FAT) - uniform_dmi='icons/mob/uniform_fat.dmi' - if(job_support_low & CIVILIAN)//This gives the preview icon clothes depending on which job(if any) is set to 'high' - clothes_s = new /icon(uniform_dmi, "grey_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if(backbag == 2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - else if(backbag == 3 || backbag == 4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - - else if(job_support_high)//I hate how this looks, but there's no reason to go through this switch if it's empty - switch(job_support_high) - if(HOP) - clothes_s = new /icon(uniform_dmi, "hop_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "ianshirt"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(BARTENDER) - clothes_s = new /icon(uniform_dmi, "ba_suit_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "tophat"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(BOTANIST) - clothes_s = new /icon(uniform_dmi, "hydroponics_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "ggloves"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "apron"), ICON_OVERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "nymph"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-hyd"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(CHEF) - clothes_s = new /icon(uniform_dmi, "chef_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "chef"), ICON_OVERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "apronchef"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JANITOR) - clothes_s = new /icon(uniform_dmi, "janitor_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "bio_janitor"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(LIBRARIAN) - clothes_s = new /icon(uniform_dmi, "red_suit_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "hairflower"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(QUARTERMASTER) - clothes_s = new /icon(uniform_dmi, "qm_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "poncho"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(CARGOTECH) - clothes_s = new /icon(uniform_dmi, "cargotech_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "flat_cap"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(MINER) - clothes_s = new /icon(uniform_dmi, "miner_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "bearpelt"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-eng"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(LAWYER) - clothes_s = new /icon(uniform_dmi, "internalaffairs_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/inhands/items_righthand.dmi', "briefcase"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "suitjacket_blue"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(CHAPLAIN) - clothes_s = new /icon(uniform_dmi, "chapblack_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "imperium_monk"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(CLOWN) - clothes_s = new /icon(uniform_dmi, "clown_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "clown"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/mask.dmi', "clown"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "clownpack"), ICON_OVERLAY) - if(MIME) - clothes_s = new /icon(uniform_dmi, "mime_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "lgloves"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/mask.dmi', "mime"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "beret"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "suspenders"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - - else if(job_medsci_high) - switch(job_medsci_high) - if(RD) - clothes_s = new /icon(uniform_dmi, "director_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "petehat"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-tox"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(SCIENTIST) - clothes_s = new /icon(uniform_dmi, "toxinswhite_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_tox_open"), ICON_OVERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "metroid"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-tox"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(CHEMIST) - clothes_s = new /icon(uniform_dmi, "chemistrywhite_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labgreen"), ICON_OVERLAY) - else - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_chem_open"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-chem"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(CMO) - clothes_s = new /icon(uniform_dmi, "cmo_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "bio_cmo"), ICON_OVERLAY) - else - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_cmo_open"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "medicalpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-med"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(DOCTOR) - clothes_s = new /icon(uniform_dmi, "medical_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "surgeon"), ICON_OVERLAY) - else - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "medicalpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-med"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(CORONER) - clothes_s = new /icon(uniform_dmi, "medical_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "mortician"), ICON_OVERLAY) - else - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "medicalpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-med"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(GENETICIST) - clothes_s = new /icon(uniform_dmi, "geneticswhite_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "monkeysuit"), ICON_OVERLAY) - else - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_gen_open"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-gen"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(VIROLOGIST) - clothes_s = new /icon(uniform_dmi, "virologywhite_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/mask.dmi', "sterile"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_vir_open"), ICON_OVERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "plaguedoctor"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "medicalpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-vir"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(PSYCHIATRIST) - clothes_s = new /icon(uniform_dmi, "psych_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(PARAMEDIC) - clothes_s = new /icon(uniform_dmi, "paramedic_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/mask.dmi', "cigoff"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "bluesoft"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "medicalpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-med"), ICON_OVERLAY) - if(ROBOTICIST) - clothes_s = new /icon(uniform_dmi, "robotics_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/inhands/items_righthand.dmi', "toolbox_blue"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - - else if(job_engsec_high) - switch(job_engsec_high) - if(CAPTAIN) - clothes_s = new /icon(uniform_dmi, "captain_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "centcomcaptain"), ICON_OVERLAY) - else - clothes_s.Blend(new /icon('icons/mob/head.dmi', "captain"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-cap"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(HOS) - clothes_s = new /icon(uniform_dmi, "hosred_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "beret_hos"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "securitypack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-sec"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(WARDEN) - clothes_s = new /icon('icons/mob/uniform.dmi', "warden_s") - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "slippers_worn"), ICON_OVERLAY) - else - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "securitypack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-sec"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(DETECTIVE) - clothes_s = new /icon(uniform_dmi, "detective_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/mask.dmi', "cigaron"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "detective"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "detective"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(OFFICER) - clothes_s = new /icon(uniform_dmi, "secred_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "beret_officer"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "securitypack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-sec"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(CHIEF) - clothes_s = new /icon(uniform_dmi, "chief_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "hardhat0_white"), ICON_OVERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/inhands/items_righthand.dmi', "blueprints"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "engiepack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-eng"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(ENGINEER) - clothes_s = new /icon(uniform_dmi, "engine_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "orange"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "hardhat0_yellow"), ICON_OVERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "hazard"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "engiepack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-eng"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(ATMOSTECH) - clothes_s = new /icon(uniform_dmi, "atmos_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) - if(prob(1)) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "firesuit"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - - if(AI)//Gives AI and borgs assistant-wear, so they can still customize their character - clothes_s = new /icon(uniform_dmi, "grey_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "straight_jacket"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "cardborg_h"), ICON_OVERLAY) - if(backbag == 2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - else if(backbag == 3 || backbag == 4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(CYBORG) - clothes_s = new /icon(uniform_dmi, "grey_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "cardborg"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "cardborg_h"), ICON_OVERLAY) - if(backbag == 2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - else if(backbag == 3 || backbag == 4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - else if(job_karma_high) - switch(job_karma_high) - if(MECHANIC) - clothes_s = new /icon(uniform_dmi, "mechanic_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "orange"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "hardhat0_yellow"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "engiepack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-eng"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(PILOT) - clothes_s = new /icon(uniform_dmi, "secred_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "bomber"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "securitypack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-sec"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(BRIGDOC) - clothes_s = new /icon(uniform_dmi, "medical_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "fr_jacket_open"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "medicalpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-med"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(NANO) - clothes_s = new /icon(uniform_dmi, "officer_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(BLUESHIELD) - clothes_s = new /icon(uniform_dmi, "officer_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/hands.dmi', "swat_gl"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "blueshield"), ICON_OVERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "securitypack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - if(JUDGE) - clothes_s = new /icon(uniform_dmi, "really_black_suit_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/head.dmi', "mercy_hood"), ICON_UNDERLAY) - clothes_s.Blend(new /icon('icons/mob/suit.dmi', "judge"), ICON_UNDERLAY) - switch(backbag) - if(2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - if(3) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) - if(4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - - if(disabilities & NEARSIGHTED) - preview_icon.Blend(new /icon('icons/mob/eyes.dmi', "glasses"), ICON_OVERLAY) - - // Observers get tourist outfit. - if(for_observer) - clothes_s = new /icon(uniform_dmi, "tourist_s") - clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) - if(backbag == 2) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) - else if(backbag == 3 || backbag == 4) - clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) - - if(underwear_s) - preview_icon.Blend(underwear_s, ICON_OVERLAY) - if(undershirt_s) - preview_icon.Blend(undershirt_s, ICON_OVERLAY) - if(socks_s) - preview_icon.Blend(socks_s, ICON_OVERLAY) - if(clothes_s) - preview_icon.Blend(clothes_s, ICON_OVERLAY) - preview_icon.Blend(face_s, ICON_OVERLAY) - preview_icon_front = new(preview_icon, dir = SOUTH) - preview_icon_side = new(preview_icon, dir = WEST) - - qdel(face_s) - qdel(underwear_s) - qdel(undershirt_s) - qdel(socks_s) - qdel(clothes_s) +/datum/preferences + //The mob should have a gender you want before running this proc. Will run fine without H +/datum/preferences/proc/random_character(gender_override) + var/datum/species/S = GLOB.all_species[species] + if(!istype(S)) //The species was invalid. Set the species to the default, fetch the datum for that species and generate a random character. + species = initial(species) + S = GLOB.all_species[species] + var/datum/robolimb/robohead + + if(S.bodyflags & ALL_RPARTS) + var/head_model = "[!rlimb_data["head"] ? "Morpheus Cyberkinetics" : rlimb_data["head"]]" + robohead = all_robolimbs[head_model] + if(gender_override) + gender = gender_override + else + gender = pick(MALE, FEMALE) + underwear = random_underwear(gender, species) + undershirt = random_undershirt(gender, species) + socks = random_socks(gender, species) + if(body_accessory_by_species[species]) + body_accessory = random_body_accessory(species) + if(body_accessory == "None") //Required to prevent a bug where the information/icons in the character preferences screen wouldn't update despite the data being changed. + body_accessory = null + if(S.bodyflags & (HAS_SKIN_TONE|HAS_ICON_SKIN_TONE)) + s_tone = random_skin_tone(species) + h_style = random_hair_style(gender, species, robohead) + f_style = random_facial_hair_style(gender, species, robohead) + if(species in list("Human", "Unathi", "Tajaran", "Skrell", "Machine", "Wryn", "Vulpkanin", "Vox")) + randomize_hair_color("hair") + randomize_hair_color("facial") + if(S.bodyflags & HAS_HEAD_ACCESSORY) + ha_style = random_head_accessory(species) + hacc_colour = randomize_skin_color(1) + if(S.bodyflags & HAS_HEAD_MARKINGS) + m_styles["head"] = random_marking_style("head", species, robohead, null, alt_head) + m_colours["head"] = randomize_skin_color(1) + if(S.bodyflags & HAS_BODY_MARKINGS) + m_styles["body"] = random_marking_style("body", species) + m_colours["body"] = randomize_skin_color(1) + if(S.bodyflags & HAS_TAIL_MARKINGS) //Species with tail markings. + m_styles["tail"] = random_marking_style("tail", species, null, body_accessory) + m_colours["tail"] = randomize_skin_color(1) + if(!(S.bodyflags & ALL_RPARTS)) + randomize_eyes_color() + if(S.bodyflags & HAS_SKIN_COLOR) + randomize_skin_color() + backbag = 2 + age = rand(AGE_MIN, AGE_MAX) + + +/datum/preferences/proc/randomize_hair_color(var/target = "hair") + if(prob (75) && target == "facial") // Chance to inherit hair color + f_colour = h_colour + return + + var/red + var/green + var/blue + + var/col = pick ("blonde", "black", "chestnut", "copper", "brown", "wheat", "old", "punk") + switch(col) + if("blonde") + red = 255 + green = 255 + blue = 0 + if("black") + red = 0 + green = 0 + blue = 0 + if("chestnut") + red = 153 + green = 102 + blue = 51 + if("copper") + red = 255 + green = 153 + blue = 0 + if("brown") + red = 102 + green = 51 + blue = 0 + if("wheat") + red = 255 + green = 255 + blue = 153 + if("old") + red = rand (100, 255) + green = red + blue = red + if("punk") + red = rand (0, 255) + green = rand (0, 255) + blue = rand (0, 255) + + red = max(min(red + rand (-25, 25), 255), 0) + green = max(min(green + rand (-25, 25), 255), 0) + blue = max(min(blue + rand (-25, 25), 255), 0) + + switch(target) + if("hair") + h_colour = rgb(red, green, blue) + if("facial") + f_colour = rgb(red, green, blue) + +/datum/preferences/proc/randomize_eyes_color() + var/red + var/green + var/blue + + var/col = pick ("black", "grey", "brown", "chestnut", "blue", "lightblue", "green", "albino") + switch(col) + if("black") + red = 0 + green = 0 + blue = 0 + if("grey") + red = rand (100, 200) + green = red + blue = red + if("brown") + red = 102 + green = 51 + blue = 0 + if("chestnut") + red = 153 + green = 102 + blue = 0 + if("blue") + red = 51 + green = 102 + blue = 204 + if("lightblue") + red = 102 + green = 204 + blue = 255 + if("green") + red = 0 + green = 102 + blue = 0 + if("albino") + red = rand (200, 255) + green = rand (0, 150) + blue = rand (0, 150) + + red = max(min(red + rand (-25, 25), 255), 0) + green = max(min(green + rand (-25, 25), 255), 0) + blue = max(min(blue + rand (-25, 25), 255), 0) + + e_colour = rgb(red, green, blue) + +/datum/preferences/proc/randomize_skin_color(var/pass_on) + var/red + var/green + var/blue + + var/col = pick ("black", "grey", "brown", "chestnut", "blue", "lightblue", "green", "albino") + switch(col) + if("black") + red = 0 + green = 0 + blue = 0 + if("grey") + red = rand (100, 200) + green = red + blue = red + if("brown") + red = 102 + green = 51 + blue = 0 + if("chestnut") + red = 153 + green = 102 + blue = 0 + if("blue") + red = 51 + green = 102 + blue = 204 + if("lightblue") + red = 102 + green = 204 + blue = 255 + if("green") + red = 0 + green = 102 + blue = 0 + if("albino") + red = rand (200, 255) + green = rand (0, 150) + blue = rand (0, 150) + + red = max(min(red + rand (-25, 25), 255), 0) + green = max(min(green + rand (-25, 25), 255), 0) + blue = max(min(blue + rand (-25, 25), 255), 0) + + if(pass_on) + return rgb(red, green, blue) + else + s_colour = rgb(red, green, blue) + +/datum/preferences/proc/blend_backpack(var/icon/clothes_s,var/backbag,var/satchel,var/backpack="backpack") + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', backpack), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', satchel), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + return clothes_s + +/datum/preferences/proc/update_preview_icon(var/for_observer=0) //seriously. This is horrendous. + qdel(preview_icon_front) + qdel(preview_icon_side) + qdel(preview_icon) + + var/g = "m" + if(gender == FEMALE) g = "f" + + var/icon/icobase + var/datum/species/current_species = GLOB.all_species[species] + + //Icon-based species colour. + var/coloured_tail + if(current_species) + if(current_species.bodyflags & HAS_ICON_SKIN_TONE) //Handling species-specific icon-based skin tones by flagged race. + var/mob/living/carbon/human/H = new + H.dna.species = current_species + H.s_tone = s_tone + H.dna.species.updatespeciescolor(H, 0) //The mob's species wasn't set, so it's almost certainly different than the character's species at the moment. Thus, we need to be owner-insensitive. + var/obj/item/organ/external/chest/C = H.get_organ("chest") + icobase = C.icobase ? C.icobase : C.dna.species.icobase + if(H.dna.species.bodyflags & HAS_TAIL) + coloured_tail = H.tail ? H.tail : H.dna.species.tail + + qdel(H) + else + icobase = current_species.icobase + else + icobase = 'icons/mob/human_races/r_human.dmi' + + var/fat="" + if(disabilities & DISABILITY_FLAG_FAT && (CAN_BE_FAT in current_species.species_traits)) + fat="_fat" + preview_icon = new /icon(icobase, "torso_[g][fat]") + preview_icon.Blend(new /icon(icobase, "groin_[g]"), ICON_OVERLAY) + var/head = "head" + if(alt_head && current_species.bodyflags & HAS_ALT_HEADS) + var/datum/sprite_accessory/alt_heads/H = GLOB.alt_heads_list[alt_head] + if(H.icon_state) + head = H.icon_state + preview_icon.Blend(new /icon(icobase, "[head]_[g]"), ICON_OVERLAY) + + for(var/name in list("chest", "groin", "head", "r_arm", "r_hand", "r_leg", "r_foot", "l_leg", "l_foot", "l_arm", "l_hand")) + if(organ_data[name] == "amputated") continue + if(organ_data[name] == "cyborg") + var/datum/robolimb/R + if(rlimb_data[name]) R = all_robolimbs[rlimb_data[name]] + if(!R) R = basic_robolimb + if(name == "chest") + name = "torso" + preview_icon.Blend(icon(R.icon, "[name]"), ICON_OVERLAY) // This doesn't check gendered_icon. Not an issue while only limbs can be robotic. + continue + preview_icon.Blend(new /icon(icobase, "[name]"), ICON_OVERLAY) + + // Skin color + if(current_species && (current_species.bodyflags & HAS_SKIN_COLOR)) + preview_icon.Blend(s_colour, ICON_ADD) + + // Skin tone + if(current_species && (current_species.bodyflags & HAS_SKIN_TONE)) + if(s_tone >= 0) + preview_icon.Blend(rgb(s_tone, s_tone, s_tone), ICON_ADD) + else + preview_icon.Blend(rgb(-s_tone, -s_tone, -s_tone), ICON_SUBTRACT) + + //Tail + if(current_species && (current_species.bodyflags & HAS_TAIL)) + var/tail_icon + var/tail_icon_state + var/tail_shift_x + var/tail_shift_y + var/blend_mode = ICON_ADD + + if(body_accessory) + var/datum/body_accessory/accessory = body_accessory_by_name[body_accessory] + tail_icon = accessory.icon + tail_icon_state = accessory.icon_state + if(accessory.blend_mode) + blend_mode = accessory.blend_mode + if(accessory.pixel_x_offset) + tail_shift_x = accessory.pixel_x_offset + if(accessory.pixel_y_offset) + tail_shift_y = accessory.pixel_y_offset + else + tail_icon = "icons/effects/species.dmi" + if(coloured_tail) + tail_icon_state = "[coloured_tail]_s" + else + tail_icon_state = "[current_species.tail]_s" + + var/icon/temp = new/icon("icon" = tail_icon, "icon_state" = tail_icon_state) + if(tail_shift_x) + temp.Shift(EAST, tail_shift_x) + if(tail_shift_y) + temp.Shift(NORTH, tail_shift_y) + + if(current_species && (current_species.bodyflags & HAS_SKIN_COLOR)) + temp.Blend(s_colour, blend_mode) + + if(current_species && (current_species.bodyflags & HAS_TAIL_MARKINGS)) + var/tail_marking = m_styles["tail"] + var/datum/sprite_accessory/tail_marking_style = GLOB.marking_styles_list[tail_marking] + if(tail_marking_style && tail_marking_style.species_allowed) + var/icon/t_marking_s = new/icon("icon" = tail_marking_style.icon, "icon_state" = "[tail_marking_style.icon_state]_s") + t_marking_s.Blend(m_colours["tail"], ICON_ADD) + temp.Blend(t_marking_s, ICON_OVERLAY) + + preview_icon.Blend(temp, ICON_OVERLAY) + + //Markings + if(current_species && ((current_species.bodyflags & HAS_HEAD_MARKINGS) || (current_species.bodyflags & HAS_BODY_MARKINGS))) + if(current_species.bodyflags & HAS_BODY_MARKINGS) //Body markings. + var/body_marking = m_styles["body"] + var/datum/sprite_accessory/body_marking_style = GLOB.marking_styles_list[body_marking] + if(body_marking_style && body_marking_style.species_allowed) + var/icon/b_marking_s = new/icon("icon" = body_marking_style.icon, "icon_state" = "[body_marking_style.icon_state]_s") + b_marking_s.Blend(m_colours["body"], ICON_ADD) + preview_icon.Blend(b_marking_s, ICON_OVERLAY) + if(current_species.bodyflags & HAS_HEAD_MARKINGS) //Head markings. + var/head_marking = m_styles["head"] + var/datum/sprite_accessory/head_marking_style = GLOB.marking_styles_list[head_marking] + if(head_marking_style && head_marking_style.species_allowed) + var/icon/h_marking_s = new/icon("icon" = head_marking_style.icon, "icon_state" = "[head_marking_style.icon_state]_s") + h_marking_s.Blend(m_colours["head"], ICON_ADD) + preview_icon.Blend(h_marking_s, ICON_OVERLAY) + + + var/icon/face_s = new/icon("icon" = 'icons/mob/human_face.dmi', "icon_state" = "bald_s") + if(!(current_species.bodyflags & NO_EYES)) + var/icon/eyes_s = new/icon("icon" = 'icons/mob/human_face.dmi', "icon_state" = current_species ? current_species.eyes : "eyes_s") + eyes_s.Blend(e_colour, ICON_ADD) + face_s.Blend(eyes_s, ICON_OVERLAY) + + + var/datum/sprite_accessory/hair_style = GLOB.hair_styles_full_list[h_style] + if(hair_style) + var/icon/hair_s = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_s") + if(current_species.name == "Slime People") // whee I am part of the problem + hair_s.Blend("[s_colour]A0", ICON_ADD) + else if(hair_style.do_colouration) + hair_s.Blend(h_colour, ICON_ADD) + + if(hair_style.secondary_theme) + var/icon/hair_secondary_s = new/icon("icon" = hair_style.icon, "icon_state" = "[hair_style.icon_state]_[hair_style.secondary_theme]_s") + if(!hair_style.no_sec_colour && hair_style.do_colouration ) + hair_secondary_s.Blend(h_sec_colour, ICON_ADD) + hair_s.Blend(hair_secondary_s, ICON_OVERLAY) + + face_s.Blend(hair_s, ICON_OVERLAY) + + //Head Accessory + if(current_species && (current_species.bodyflags & HAS_HEAD_ACCESSORY)) + var/datum/sprite_accessory/head_accessory_style = GLOB.head_accessory_styles_list[ha_style] + if(head_accessory_style && head_accessory_style.species_allowed) + var/icon/head_accessory_s = new/icon("icon" = head_accessory_style.icon, "icon_state" = "[head_accessory_style.icon_state]_s") + head_accessory_s.Blend(hacc_colour, ICON_ADD) + face_s.Blend(head_accessory_s, ICON_OVERLAY) + + var/datum/sprite_accessory/facial_hair_style = GLOB.facial_hair_styles_list[f_style] + if(facial_hair_style && facial_hair_style.species_allowed) + var/icon/facial_s = new/icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_s") + if(current_species.name == "Slime People") // whee I am part of the problem + facial_s.Blend("[s_colour]A0", ICON_ADD) + else if(facial_hair_style.do_colouration) + facial_s.Blend(f_colour, ICON_ADD) + + if(facial_hair_style.secondary_theme) + var/icon/facial_secondary_s = new/icon("icon" = facial_hair_style.icon, "icon_state" = "[facial_hair_style.icon_state]_[facial_hair_style.secondary_theme]_s") + if(!facial_hair_style.no_sec_colour && facial_hair_style.do_colouration) + facial_secondary_s.Blend(f_sec_colour, ICON_ADD) + facial_s.Blend(facial_secondary_s, ICON_OVERLAY) + + face_s.Blend(facial_s, ICON_OVERLAY) + + var/icon/underwear_s = null + if(underwear && (current_species.clothing_flags & HAS_UNDERWEAR)) + var/datum/sprite_accessory/underwear/U = GLOB.underwear_list[underwear] + if(U) + var/u_icon = U.sprite_sheets && (current_species.name in U.sprite_sheets) ? U.sprite_sheets[current_species.name] : U.icon //Species-fit the undergarment. + underwear_s = new/icon(u_icon, "uw_[U.icon_state]_s", ICON_OVERLAY) + + var/icon/undershirt_s = null + if(undershirt && (current_species.clothing_flags & HAS_UNDERSHIRT)) + var/datum/sprite_accessory/undershirt/U2 = GLOB.undershirt_list[undershirt] + if(U2) + var/u2_icon = U2.sprite_sheets && (current_species.name in U2.sprite_sheets) ? U2.sprite_sheets[current_species.name] : U2.icon + undershirt_s = new/icon(u2_icon, "us_[U2.icon_state]_s", ICON_OVERLAY) + + var/icon/socks_s = null + if(socks && (current_species.clothing_flags & HAS_SOCKS)) + var/datum/sprite_accessory/socks/U3 = GLOB.socks_list[socks] + if(U3) + var/u3_icon = U3.sprite_sheets && (current_species.name in U3.sprite_sheets) ? U3.sprite_sheets[current_species.name] : U3.icon + socks_s = new/icon(u3_icon, "sk_[U3.icon_state]_s", ICON_OVERLAY) + + var/icon/clothes_s = null + var/uniform_dmi='icons/mob/uniform.dmi' + if(disabilities&DISABILITY_FLAG_FAT) + uniform_dmi='icons/mob/uniform_fat.dmi' + if(job_support_low & JOB_CIVILIAN)//This gives the preview icon clothes depending on which job(if any) is set to 'high' + clothes_s = new /icon(uniform_dmi, "grey_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if(backbag == 2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + else if(backbag == 3 || backbag == 4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + + else if(job_support_high)//I hate how this looks, but there's no reason to go through this switch if it's empty + switch(job_support_high) + if(JOB_HOP) + clothes_s = new /icon(uniform_dmi, "hop_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "ianshirt"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_BARTENDER) + clothes_s = new /icon(uniform_dmi, "ba_suit_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "tophat"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_BOTANIST) + clothes_s = new /icon(uniform_dmi, "hydroponics_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "ggloves"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "apron"), ICON_OVERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "nymph"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-hyd"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_CHEF) + clothes_s = new /icon(uniform_dmi, "chef_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "chef"), ICON_OVERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "apronchef"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_JANITOR) + clothes_s = new /icon(uniform_dmi, "janitor_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "bio_janitor"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_LIBRARIAN) + clothes_s = new /icon(uniform_dmi, "red_suit_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "hairflower"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_QUARTERMASTER) + clothes_s = new /icon(uniform_dmi, "qm_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "poncho"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_CARGOTECH) + clothes_s = new /icon(uniform_dmi, "cargotech_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "flat_cap"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_MINER) + clothes_s = new /icon(uniform_dmi, "miner_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "bearpelt"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-eng"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_LAWYER) + clothes_s = new /icon(uniform_dmi, "internalaffairs_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/inhands/items_righthand.dmi', "briefcase"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "suitjacket_blue"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_CHAPLAIN) + clothes_s = new /icon(uniform_dmi, "chapblack_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "imperium_monk"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_CLOWN) + clothes_s = new /icon(uniform_dmi, "clown_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "clown"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/mask.dmi', "clown"), ICON_OVERLAY) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "clownpack"), ICON_OVERLAY) + if(JOB_MIME) + clothes_s = new /icon(uniform_dmi, "mime_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "lgloves"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/mask.dmi', "mime"), ICON_OVERLAY) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "beret"), ICON_OVERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "suspenders"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + + else if(job_medsci_high) + switch(job_medsci_high) + if(JOB_RD) + clothes_s = new /icon(uniform_dmi, "director_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "petehat"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-tox"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_SCIENTIST) + clothes_s = new /icon(uniform_dmi, "toxinswhite_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_tox_open"), ICON_OVERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "metroid"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-tox"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_CHEMIST) + clothes_s = new /icon(uniform_dmi, "chemistrywhite_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labgreen"), ICON_OVERLAY) + else + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_chem_open"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-chem"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_CMO) + clothes_s = new /icon(uniform_dmi, "cmo_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "bio_cmo"), ICON_OVERLAY) + else + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_cmo_open"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "medicalpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-med"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_DOCTOR) + clothes_s = new /icon(uniform_dmi, "medical_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "surgeon"), ICON_OVERLAY) + else + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "medicalpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-med"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_CORONER) + clothes_s = new /icon(uniform_dmi, "medical_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "mortician"), ICON_OVERLAY) + else + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "medicalpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-med"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_GENETICIST) + clothes_s = new /icon(uniform_dmi, "geneticswhite_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "monkeysuit"), ICON_OVERLAY) + else + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_gen_open"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-gen"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_VIROLOGIST) + clothes_s = new /icon(uniform_dmi, "virologywhite_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/mask.dmi', "sterile"), ICON_OVERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_vir_open"), ICON_OVERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "plaguedoctor"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "medicalpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-vir"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_PSYCHIATRIST) + clothes_s = new /icon(uniform_dmi, "psych_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_PARAMEDIC) + clothes_s = new /icon(uniform_dmi, "paramedic_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/mask.dmi', "cigoff"), ICON_OVERLAY) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "bluesoft"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "medicalpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-med"), ICON_OVERLAY) + if(JOB_ROBOTICIST) + clothes_s = new /icon(uniform_dmi, "robotics_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "labcoat_open"), ICON_OVERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/inhands/items_righthand.dmi', "toolbox_blue"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + + else if(job_engsec_high) + switch(job_engsec_high) + if(JOB_CAPTAIN) + clothes_s = new /icon(uniform_dmi, "captain_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "centcomcaptain"), ICON_OVERLAY) + else + clothes_s.Blend(new /icon('icons/mob/head.dmi', "captain"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-cap"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_HOS) + clothes_s = new /icon(uniform_dmi, "hosred_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "beret_hos"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "securitypack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-sec"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_WARDEN) + clothes_s = new /icon('icons/mob/uniform.dmi', "warden_s") + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "slippers_worn"), ICON_OVERLAY) + else + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "securitypack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-sec"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_DETECTIVE) + clothes_s = new /icon(uniform_dmi, "detective_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/mask.dmi', "cigaron"), ICON_OVERLAY) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "detective"), ICON_OVERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "detective"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_OFFICER) + clothes_s = new /icon(uniform_dmi, "secred_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "beret_officer"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "securitypack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-sec"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_CHIEF) + clothes_s = new /icon(uniform_dmi, "chief_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "brown"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "hardhat0_white"), ICON_OVERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/inhands/items_righthand.dmi', "blueprints"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "engiepack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-eng"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_ENGINEER) + clothes_s = new /icon(uniform_dmi, "engine_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "orange"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "hardhat0_yellow"), ICON_OVERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "hazard"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "engiepack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-eng"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_ATMOSTECH) + clothes_s = new /icon(uniform_dmi, "atmos_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "bgloves"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) + if(prob(1)) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "firesuit"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + + if(JOB_AI)//Gives AI and borgs assistant-wear, so they can still customize their character + clothes_s = new /icon(uniform_dmi, "grey_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "straight_jacket"), ICON_OVERLAY) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "cardborg_h"), ICON_OVERLAY) + if(backbag == 2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + else if(backbag == 3 || backbag == 4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_CYBORG) + clothes_s = new /icon(uniform_dmi, "grey_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "cardborg"), ICON_OVERLAY) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "cardborg_h"), ICON_OVERLAY) + if(backbag == 2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + else if(backbag == 3 || backbag == 4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + else if(job_karma_high) + switch(job_karma_high) + if(JOB_MECHANIC) + clothes_s = new /icon(uniform_dmi, "mechanic_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "orange"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/belt.dmi', "utility"), ICON_OVERLAY) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "hardhat0_yellow"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "engiepack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-eng"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_PILOT) + clothes_s = new /icon(uniform_dmi, "secred_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "bomber"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "securitypack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-sec"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_BRIGDOC) + clothes_s = new /icon(uniform_dmi, "medical_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "white"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "fr_jacket_open"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "medicalpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-med"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_NANO) + clothes_s = new /icon(uniform_dmi, "officer_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_BLUESHIELD) + clothes_s = new /icon(uniform_dmi, "officer_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "jackboots"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/hands.dmi', "swat_gl"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "blueshield"), ICON_OVERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "securitypack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + if(JOB_JUDGE) + clothes_s = new /icon(uniform_dmi, "really_black_suit_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "laceups"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/head.dmi', "mercy_hood"), ICON_UNDERLAY) + clothes_s.Blend(new /icon('icons/mob/suit.dmi', "judge"), ICON_UNDERLAY) + switch(backbag) + if(2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + if(3) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel-norm"), ICON_OVERLAY) + if(4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + + if(disabilities & NEARSIGHTED) + preview_icon.Blend(new /icon('icons/mob/eyes.dmi', "glasses"), ICON_OVERLAY) + + // Observers get tourist outfit. + if(for_observer) + clothes_s = new /icon(uniform_dmi, "tourist_s") + clothes_s.Blend(new /icon('icons/mob/feet.dmi', "black"), ICON_UNDERLAY) + if(backbag == 2) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "backpack"), ICON_OVERLAY) + else if(backbag == 3 || backbag == 4) + clothes_s.Blend(new /icon('icons/mob/back.dmi', "satchel"), ICON_OVERLAY) + + if(underwear_s) + preview_icon.Blend(underwear_s, ICON_OVERLAY) + if(undershirt_s) + preview_icon.Blend(undershirt_s, ICON_OVERLAY) + if(socks_s) + preview_icon.Blend(socks_s, ICON_OVERLAY) + if(clothes_s) + preview_icon.Blend(clothes_s, ICON_OVERLAY) + preview_icon.Blend(face_s, ICON_OVERLAY) + preview_icon_front = new(preview_icon, dir = SOUTH) + preview_icon_side = new(preview_icon, dir = WEST) + + qdel(face_s) + qdel(underwear_s) + qdel(undershirt_s) + qdel(socks_s) + qdel(clothes_s) diff --git a/code/modules/mob/new_player/sprite_accessories/diona/diona_hair.dm b/code/modules/mob/new_player/sprite_accessories/diona/diona_hair.dm index 3ab33449080..37bb1bdd1ee 100644 --- a/code/modules/mob/new_player/sprite_accessories/diona/diona_hair.dm +++ b/code/modules/mob/new_player/sprite_accessories/diona/diona_hair.dm @@ -56,4 +56,4 @@ /datum/sprite_accessory/hair/diona/diona_wildflow name = "Wildflowers" - icon_state = "wildflower" \ No newline at end of file + icon_state = "wildflower" diff --git a/code/modules/mob/new_player/sprite_accessories/drask/drask_body_markings.dm b/code/modules/mob/new_player/sprite_accessories/drask/drask_body_markings.dm index abc16905788..da8d8c9160f 100644 --- a/code/modules/mob/new_player/sprite_accessories/drask/drask_body_markings.dm +++ b/code/modules/mob/new_player/sprite_accessories/drask/drask_body_markings.dm @@ -4,4 +4,4 @@ /datum/sprite_accessory/body_markings/drask/arm_spines_drask icon = 'icons/mob/sprite_accessories/drask/drask_body_markings.dmi' name = "Drask Arm Spines" - icon_state = "armspines" \ No newline at end of file + icon_state = "armspines" diff --git a/code/modules/mob/new_player/sprite_accessories/human/human_body_markings.dm b/code/modules/mob/new_player/sprite_accessories/human/human_body_markings.dm index 9cef4230139..9dd5345d483 100644 --- a/code/modules/mob/new_player/sprite_accessories/human/human_body_markings.dm +++ b/code/modules/mob/new_player/sprite_accessories/human/human_body_markings.dm @@ -32,4 +32,4 @@ /datum/sprite_accessory/body_markings/tattoo/nightling name = "Nightling Tattoo" - icon_state = "nightling" \ No newline at end of file + icon_state = "nightling" diff --git a/code/modules/mob/new_player/sprite_accessories/human/human_facial_hair.dm b/code/modules/mob/new_player/sprite_accessories/human/human_facial_hair.dm index e3136011393..d0a3d369c4d 100644 --- a/code/modules/mob/new_player/sprite_accessories/human/human_facial_hair.dm +++ b/code/modules/mob/new_player/sprite_accessories/human/human_facial_hair.dm @@ -102,4 +102,4 @@ icon_state = "unshaven" ////////////////////////////// //////END VG HAIRSTYLES/////// -////////////////////////////// \ No newline at end of file +////////////////////////////// diff --git a/code/modules/mob/new_player/sprite_accessories/human/human_hair.dm b/code/modules/mob/new_player/sprite_accessories/human/human_hair.dm index bc9931a8782..f9e1ee1cc49 100644 --- a/code/modules/mob/new_player/sprite_accessories/human/human_hair.dm +++ b/code/modules/mob/new_player/sprite_accessories/human/human_hair.dm @@ -755,4 +755,4 @@ /datum/sprite_accessory/hair/fluff/pinapple_fluff_hair //Pineapple Salad hair fluff its for a slime..has to go under human name = "Sasook Hair" icon_state = "psalad_fluff_hair" - species_allowed = list("Slime People") \ No newline at end of file + species_allowed = list("Slime People") diff --git a/code/modules/mob/new_player/sprite_accessories/ipc/ipc_face.dm b/code/modules/mob/new_player/sprite_accessories/ipc/ipc_face.dm index 85e0f708aaf..8910a8f5229 100644 --- a/code/modules/mob/new_player/sprite_accessories/ipc/ipc_face.dm +++ b/code/modules/mob/new_player/sprite_accessories/ipc/ipc_face.dm @@ -132,4 +132,4 @@ /datum/sprite_accessory/hair/ipc/fluff/lumi_waiting //Lumi Fluff hair name = "Lumi Waiting" - icon_state = "lumi_waiting" \ No newline at end of file + icon_state = "lumi_waiting" diff --git a/code/modules/mob/new_player/sprite_accessories/ipc/ipc_optics.dm b/code/modules/mob/new_player/sprite_accessories/ipc/ipc_optics.dm index ff157a25d7d..1a0e2756065 100644 --- a/code/modules/mob/new_player/sprite_accessories/ipc/ipc_optics.dm +++ b/code/modules/mob/new_player/sprite_accessories/ipc/ipc_optics.dm @@ -23,4 +23,4 @@ /datum/sprite_accessory/body_markings/head/optics/xion_alt name = "Xion Alt. Optics" icon_state = "xion_alt_optics" - models_allowed = list("Xion Manufacturing Group alt.") \ No newline at end of file + models_allowed = list("Xion Manufacturing Group alt.") diff --git a/code/modules/mob/new_player/sprite_accessories/kidan/kidan_body_markings.dm b/code/modules/mob/new_player/sprite_accessories/kidan/kidan_body_markings.dm index 622ba3736f4..ba8d90fb720 100644 --- a/code/modules/mob/new_player/sprite_accessories/kidan/kidan_body_markings.dm +++ b/code/modules/mob/new_player/sprite_accessories/kidan/kidan_body_markings.dm @@ -4,4 +4,4 @@ /datum/sprite_accessory/body_markings/kidan/outline_kid name = "Kidan Outline" - icon_state = "outline" \ No newline at end of file + icon_state = "outline" diff --git a/code/modules/mob/new_player/sprite_accessories/kidan/kidan_hair.dm b/code/modules/mob/new_player/sprite_accessories/kidan/kidan_hair.dm index cf2bd865e26..795c58622be 100644 --- a/code/modules/mob/new_player/sprite_accessories/kidan/kidan_hair.dm +++ b/code/modules/mob/new_player/sprite_accessories/kidan/kidan_hair.dm @@ -31,4 +31,4 @@ /datum/sprite_accessory/hair/kidan/kidan_tall_horns name = "Tall Horns" - icon_state = "tall_horns" \ No newline at end of file + icon_state = "tall_horns" diff --git a/code/modules/mob/new_player/sprite_accessories/kidan/kidan_head_accessories.dm b/code/modules/mob/new_player/sprite_accessories/kidan/kidan_head_accessories.dm index 521daa2023f..aa938926eb9 100644 --- a/code/modules/mob/new_player/sprite_accessories/kidan/kidan_head_accessories.dm +++ b/code/modules/mob/new_player/sprite_accessories/kidan/kidan_head_accessories.dm @@ -53,4 +53,4 @@ /datum/sprite_accessory/head_accessory/kidan/kidan_very_short name = "Very Short" icon_state = "very_short" -// End \ No newline at end of file +// End diff --git a/code/modules/mob/new_player/sprite_accessories/nucleation/nucleation_face.dm b/code/modules/mob/new_player/sprite_accessories/nucleation/nucleation_face.dm index 87a6f31cc31..3218a15357a 100644 --- a/code/modules/mob/new_player/sprite_accessories/nucleation/nucleation_face.dm +++ b/code/modules/mob/new_player/sprite_accessories/nucleation/nucleation_face.dm @@ -29,4 +29,4 @@ /datum/sprite_accessory/hair/nucleation/nuc_neutron name = "Nucleation Neutron Bomb" - icon_state = "neutron" \ No newline at end of file + icon_state = "neutron" diff --git a/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_body_markings.dm b/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_body_markings.dm index decbcce8578..572bd5d556b 100644 --- a/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_body_markings.dm +++ b/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_body_markings.dm @@ -20,4 +20,4 @@ /datum/sprite_accessory/body_markings/tajara/patchy_taj name = "Tajaran Patches" - icon_state = "patch" \ No newline at end of file + icon_state = "patch" diff --git a/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_facial_hair.dm b/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_facial_hair.dm index 794b89c8d74..3e9ae78ef42 100644 --- a/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_facial_hair.dm +++ b/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_facial_hair.dm @@ -28,4 +28,4 @@ /datum/sprite_accessory/facial_hair/tajara/taj_smallstache name = "Tajara Smallstache" - icon_state = "facial_smallstache" \ No newline at end of file + icon_state = "facial_smallstache" diff --git a/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_hair.dm b/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_hair.dm index 7b55508877c..03cb618ee0b 100644 --- a/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_hair.dm +++ b/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_hair.dm @@ -78,4 +78,4 @@ /datum/sprite_accessory/hair/tajara/taj_hair_fingercurl name = "Tajara Finger Curls" icon_state = "fingerwave" - glasses_over = null \ No newline at end of file + glasses_over = null diff --git a/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_head_markings.dm b/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_head_markings.dm index 88d534bb3c2..a3afa6823b4 100644 --- a/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_head_markings.dm +++ b/code/modules/mob/new_player/sprite_accessories/tajaran/tajaran_head_markings.dm @@ -41,4 +41,4 @@ /datum/sprite_accessory/body_markings/head/tajara/patchy_taj //Companion marking for Tajaran Patches. name = "Tajaran Patches Head" - icon_state = "patch" \ No newline at end of file + icon_state = "patch" diff --git a/code/modules/mob/new_player/sprite_accessories/unathi/unathi_alt_heads.dm b/code/modules/mob/new_player/sprite_accessories/unathi/unathi_alt_heads.dm index 7b16b9fab10..aa8c992e014 100644 --- a/code/modules/mob/new_player/sprite_accessories/unathi/unathi_alt_heads.dm +++ b/code/modules/mob/new_player/sprite_accessories/unathi/unathi_alt_heads.dm @@ -2,4 +2,4 @@ name = "Unathi Sharp Snout" species_allowed = list("Unathi") icon_state = "head_sharp" - suffix = "sharp" \ No newline at end of file + suffix = "sharp" diff --git a/code/modules/mob/new_player/sprite_accessories/unathi/unathi_body_markings.dm b/code/modules/mob/new_player/sprite_accessories/unathi/unathi_body_markings.dm index e65fe303d36..a37b65c4561 100644 --- a/code/modules/mob/new_player/sprite_accessories/unathi/unathi_body_markings.dm +++ b/code/modules/mob/new_player/sprite_accessories/unathi/unathi_body_markings.dm @@ -16,4 +16,4 @@ /datum/sprite_accessory/body_markings/unathi/points_una name = "Unathi Points" - icon_state = "points" \ No newline at end of file + icon_state = "points" diff --git a/code/modules/mob/new_player/sprite_accessories/unathi/unathi_facial_hair.dm b/code/modules/mob/new_player/sprite_accessories/unathi/unathi_facial_hair.dm index 7cba458e2b0..16b9a13fae4 100644 --- a/code/modules/mob/new_player/sprite_accessories/unathi/unathi_facial_hair.dm +++ b/code/modules/mob/new_player/sprite_accessories/unathi/unathi_facial_hair.dm @@ -73,4 +73,4 @@ /datum/sprite_accessory/facial_hair/unathi/una_spikes name = "Spikes" icon = 'icons/mob/sprite_accessories/unathi/unathi_hair.dmi' - icon_state = "spikes_horns" \ No newline at end of file + icon_state = "spikes_horns" diff --git a/code/modules/mob/new_player/sprite_accessories/unathi/unathi_head_accessories.dm b/code/modules/mob/new_player/sprite_accessories/unathi/unathi_head_accessories.dm index 0cab3238016..52968c00219 100644 --- a/code/modules/mob/new_player/sprite_accessories/unathi/unathi_head_accessories.dm +++ b/code/modules/mob/new_player/sprite_accessories/unathi/unathi_head_accessories.dm @@ -51,4 +51,4 @@ /datum/sprite_accessory/head_accessory/unathi/spikes name = "Spikes" - icon_state = "spikes_horns" \ No newline at end of file + icon_state = "spikes_horns" diff --git a/code/modules/mob/new_player/sprite_accessories/vox/vox_body_markings.dm b/code/modules/mob/new_player/sprite_accessories/vox/vox_body_markings.dm index 563b39356d9..1bf69829bef 100644 --- a/code/modules/mob/new_player/sprite_accessories/vox/vox_body_markings.dm +++ b/code/modules/mob/new_player/sprite_accessories/vox/vox_body_markings.dm @@ -16,4 +16,4 @@ /datum/sprite_accessory/body_markings/tattoo/vox/tiger_body_vox name = "Vox Tiger-stripe Tattoo" - icon_state = "tiger" \ No newline at end of file + icon_state = "tiger" diff --git a/code/modules/mob/new_player/sprite_accessories/vox/vox_facial_hair.dm b/code/modules/mob/new_player/sprite_accessories/vox/vox_facial_hair.dm index 18d212de255..ae189382669 100644 --- a/code/modules/mob/new_player/sprite_accessories/vox/vox_facial_hair.dm +++ b/code/modules/mob/new_player/sprite_accessories/vox/vox_facial_hair.dm @@ -17,4 +17,4 @@ /datum/sprite_accessory/facial_hair/vox/vox_beard name = "Vox Quill Beard" - icon_state = "beard" \ No newline at end of file + icon_state = "beard" diff --git a/code/modules/mob/new_player/sprite_accessories/vox/vox_tail_markings.dm b/code/modules/mob/new_player/sprite_accessories/vox/vox_tail_markings.dm index e77c6f7e19d..6a3d50317ac 100644 --- a/code/modules/mob/new_player/sprite_accessories/vox/vox_tail_markings.dm +++ b/code/modules/mob/new_player/sprite_accessories/vox/vox_tail_markings.dm @@ -12,4 +12,4 @@ /datum/sprite_accessory/body_markings/tail/vox/vox_stripe name = "Vox Tail Stripe" - icon_state = "stripe" \ No newline at end of file + icon_state = "stripe" diff --git a/code/modules/mob/new_player/sprite_accessories/vulpkanin/vulpkanin_head_accessories.dm b/code/modules/mob/new_player/sprite_accessories/vulpkanin/vulpkanin_head_accessories.dm index 66edd5974fc..047fa6cd73d 100644 --- a/code/modules/mob/new_player/sprite_accessories/vulpkanin/vulpkanin_head_accessories.dm +++ b/code/modules/mob/new_player/sprite_accessories/vulpkanin/vulpkanin_head_accessories.dm @@ -54,4 +54,4 @@ /datum/sprite_accessory/head_accessory/vulpkanin/vulp_slash name = "Slash" - icon_state = "slash" \ No newline at end of file + icon_state = "slash" diff --git a/code/modules/mob/new_player/sprite_accessories/vulpkanin/vulpkanin_head_markings.dm b/code/modules/mob/new_player/sprite_accessories/vulpkanin/vulpkanin_head_markings.dm index 47253385ba1..c04f7061efc 100644 --- a/code/modules/mob/new_player/sprite_accessories/vulpkanin/vulpkanin_head_markings.dm +++ b/code/modules/mob/new_player/sprite_accessories/vulpkanin/vulpkanin_head_markings.dm @@ -32,4 +32,4 @@ /datum/sprite_accessory/body_markings/head/vulpkanin/points_sharp_vulp //Companion marking for Vulpkanin Points Sharp. name = "Vulpkanin Points Head 2" - icon_state = "points_sharp" \ No newline at end of file + icon_state = "points_sharp" diff --git a/code/modules/mob/new_player/sprite_accessories/vulpkanin/vulpkanin_tail_markings.dm b/code/modules/mob/new_player/sprite_accessories/vulpkanin/vulpkanin_tail_markings.dm index 05060d3c676..bceaa8ae729 100644 --- a/code/modules/mob/new_player/sprite_accessories/vulpkanin/vulpkanin_tail_markings.dm +++ b/code/modules/mob/new_player/sprite_accessories/vulpkanin/vulpkanin_tail_markings.dm @@ -28,4 +28,4 @@ /datum/sprite_accessory/body_markings/tail/vulpkanin/vulp_hybrid_silverf name = "Vulpkanin Bushy Straight Tail Black Fade White Tip" tails_allowed = list("Straight Bushy Tail") - icon_state = "sbsilverfade" \ No newline at end of file + icon_state = "sbsilverfade" diff --git a/code/modules/mob/new_player/sprite_accessories/wryn/wryn_face.dm b/code/modules/mob/new_player/sprite_accessories/wryn/wryn_face.dm index 38d17ea2950..668808de217 100644 --- a/code/modules/mob/new_player/sprite_accessories/wryn/wryn_face.dm +++ b/code/modules/mob/new_player/sprite_accessories/wryn/wryn_face.dm @@ -5,4 +5,4 @@ /datum/sprite_accessory/hair/wryn/wry_antennae_default name = "Antennae" - icon_state = "antennae" \ No newline at end of file + icon_state = "antennae" diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index 93eb035b187..e218ce3c25d 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -1,208 +1,213 @@ - -/mob/proc/say() - return - -/mob/verb/whisper(message as text) - set name = "Whisper" - set category = "IC" - return - -/mob/verb/say_verb(message as text) - set name = "Say" - set category = "IC" - - //Let's try to make users fix their errors - we try to detect single, out-of-place letters and 'unintended' words - /* - var/first_letter = copytext(message,1,2) - if((copytext(message,2,3) == " " && first_letter != "I" && first_letter != "A" && first_letter != ";") || cmptext(copytext(message,1,5), "say ") || cmptext(copytext(message,1,4), "me ") || cmptext(copytext(message,1,6), "looc ") || cmptext(copytext(message,1,5), "ooc ") || cmptext(copytext(message,2,6), "say ")) - var/response = alert(usr, "Do you really want to say this using the *say* verb?\n\n[message]\n", "Confirm your message", "Yes", "Edit message", "No") - if(response == "Edit message") - message = input(usr, "Please edit your message carefully:", "Edit message", message) - if(!message) - return - else if(response == "No") - return - */ - - set_typing_indicator(0) - usr.say(message) - - -/mob/verb/me_verb(message as text) - set name = "Me" - set category = "IC" - - message = strip_html_properly(message) - - set_typing_indicator(0) - if(use_me) - custom_emote(usr.emote_type, message) - else - usr.emote(message) - - -/mob/proc/say_dead(var/message) - if(!(client && client.holder)) - if(!config.dsay_allowed) - to_chat(src, "Deadchat is globally muted.") - return - - if(client && !(client.prefs.toggles & CHAT_DEAD)) - to_chat(usr, "You have deadchat muted.") - return - - say_dead_direct("[pick("complains", "moans", "whines", "laments", "blubbers", "salts")], \"[message]\"", src) - -/mob/proc/say_understands(var/mob/other, var/datum/language/speaking = null) - if(stat == DEAD) - return 1 - - //Universal speak makes everything understandable, for obvious reasons. - if(universal_speak || universal_understand) - return 1 - - //Languages are handled after. - if(!speaking) - if(!other) - return 1 - if(other.universal_speak) - return 1 - if(isAI(src) && ispAI(other)) - return 1 - if(istype(other, src.type) || istype(src, other.type)) - return 1 - return 0 - - if(speaking.flags & INNATE) - return 1 - - //Language check. - for(var/datum/language/L in languages) - if(speaking.name == L.name) - return 1 - - return 0 - - -/mob/proc/say_quote(var/message, var/datum/language/speaking = null) - var/verb = "says" - var/ending = copytext(message, length(message)) - - if(speaking) - verb = speaking.get_spoken_verb(ending) - else - if(ending == "!") - verb = pick("exclaims", "shouts", "yells") - else if(ending == "?") - verb = "asks" - return verb - - -/mob/proc/emote(act, type, message, force) - if(act == "me") - return custom_emote(type, message) - - -/mob/proc/get_ear() - // returns an atom representing a location on the map from which this - // mob can hear things - - // should be overloaded for all mobs whose "ear" is separate from their "mob" - - return get_turf(src) - -/mob/proc/say_test(var/text) - var/ending = copytext(text, length(text)) - if(ending == "?") - return "1" - else if(ending == "!") - return "2" - return "0" - -//parses the message mode code (e.g. :h, :w) from text, such as that supplied to say. -//returns the message mode string or null for no message mode. -//standard mode is the mode returned for the special ';' radio code. -/mob/proc/parse_message_mode(var/message, var/standard_mode = "headset") - if(length(message) >= 1 && copytext(message, 1, 2) == ";") - return standard_mode - - if(length(message) >= 2) - var/channel_prefix = copytext(message, 1 ,3) - return department_radio_keys[channel_prefix] - - return null - -/datum/multilingual_say_piece - var/datum/language/speaking = null - var/message = "" - -/datum/multilingual_say_piece/New(datum/language/new_speaking, new_message) - . = ..() - speaking = new_speaking - if(new_message) - message = new_message - -/mob/proc/find_valid_prefixes(message) - var/list/prefixes = list() // [["Common", start, end], ["Gutter", start, end]] - for(var/i in 1 to length(message)) - var/selection = trim_right(lowertext(copytext(message, i, i + 3))) - var/datum/language/L = GLOB.language_keys[selection] - if(L != null && can_speak_language(L)) // What the fuck... remove the L != null check if you ever find out what the fuck is adding `null` to the languages list on absolutely random mobs... seriously what the hell... - prefixes[++prefixes.len] = list(L, i, i + length(selection)) - else if(!L && i == 1) - prefixes[++prefixes.len] = list(get_default_language(), i, i) - else - return prefixes - -/proc/strip_prefixes(message) - . = "" - var/last_index = 1 - for(var/i in 1 to length(message)) - var/selection = trim_right(lowertext(copytext(message, i, i + 3))) - var/datum/language/L = GLOB.language_keys[selection] - if(L) - . += copytext(message, last_index, i) - last_index = i + 3 - if(i + 1 > length(message)) - . += copytext(message, last_index) - -// this returns a structured message with language sections -// list(/datum/multilingual_say_piece(common, "hi"), /datum/multilingual_say_piece(farwa, "squik"), /datum/multilingual_say_piece(common, "meow!")) -/mob/proc/parse_languages(message) - . = list() - - // Noise language is a snowflake - if(copytext(message, 1, 2) == "!" && length(message) > 1) - return list(new /datum/multilingual_say_piece(GLOB.all_languages["Noise"], trim(strip_prefixes(copytext(message, 2))))) - - // Scan the message for prefixes - var/list/prefix_locations = find_valid_prefixes(message) - if(!LAZYLEN(prefix_locations)) // There are no prefixes... or at least, no _valid_ prefixes. - . += new /datum/multilingual_say_piece(get_default_language(), trim(strip_prefixes(message))) // So we'll just strip those pesky things and still make the message. - - for(var/i in 1 to length(prefix_locations)) - var/current = prefix_locations[i] // ["Common", keypos] - - // There are a few things that will make us want to ignore all other languages in - namely, HIVEMIND languages. - var/datum/language/L = current[1] - if(L && L.flags & HIVEMIND) - . = new /datum/multilingual_say_piece(L, trim(strip_prefixes(message))) - break - - if(i + 1 > length(prefix_locations)) // We are out of lookaheads, that means the rest of the message is in cur lang - var/spoke_message = handle_autohiss(trim(copytext(message, current[3])), L) - . += new /datum/multilingual_say_piece(current[1], spoke_message) - else - var/next = prefix_locations[i + 1] // We look ahead at the next message to see where we need to stop. - var/spoke_message = handle_autohiss(trim(copytext(message, current[3], next[2])), L) - . += new /datum/multilingual_say_piece(current[1], spoke_message) - -/* These are here purely because it would be hell to try to convert everything over to using the multi-lingual system at once */ -/proc/message_to_multilingual(message, datum/language/speaking = null) - . = list(new /datum/multilingual_say_piece(speaking, message)) - -/proc/multilingual_to_message(list/message_pieces) - . = "" - for(var/datum/multilingual_say_piece/S in message_pieces) - . += S.message + " " - . = trim_right(.) \ No newline at end of file + +#define ILLEGAL_CHARACTERS_LIST list("<" = "", ">" = "", "^" = "", \ + "\[" = "", "]" = "", "{" = "", "}" = "") + +/mob/proc/say() + return + +/mob/verb/whisper(message as text) + set name = "Whisper" + set category = "IC" + return + +/mob/verb/say_verb(message as text) + set name = "Say" + set category = "IC" + + //Let's try to make users fix their errors - we try to detect single, out-of-place letters and 'unintended' words + /* + var/first_letter = copytext(message,1,2) + if((copytext(message,2,3) == " " && first_letter != "I" && first_letter != "A" && first_letter != ";") || cmptext(copytext(message,1,5), "say ") || cmptext(copytext(message,1,4), "me ") || cmptext(copytext(message,1,6), "looc ") || cmptext(copytext(message,1,5), "ooc ") || cmptext(copytext(message,2,6), "say ")) + var/response = alert(usr, "Do you really want to say this using the *say* verb?\n\n[message]\n", "Confirm your message", "Yes", "Edit message", "No") + if(response == "Edit message") + message = input(usr, "Please edit your message carefully:", "Edit message", message) + if(!message) + return + else if(response == "No") + return + */ + message = replace_characters(message, ILLEGAL_CHARACTERS_LIST) + set_typing_indicator(0) + usr.say(message) + + +/mob/verb/me_verb(message as text) + set name = "Me" + set category = "IC" + + message = strip_html_properly(message) + + set_typing_indicator(0) + if(use_me) + custom_emote(usr.emote_type, message) + else + usr.emote(message) + + +/mob/proc/say_dead(var/message) + if(!(client && client.holder)) + if(!config.dsay_allowed) + to_chat(src, "Deadchat is globally muted.") + return + + if(client && !(client.prefs.toggles & CHAT_DEAD)) + to_chat(usr, "You have deadchat muted.") + return + + say_dead_direct("[pick("complains", "moans", "whines", "laments", "blubbers", "salts")], \"[message]\"", src) + +/mob/proc/say_understands(var/mob/other, var/datum/language/speaking = null) + if(stat == DEAD) + return 1 + + //Universal speak makes everything understandable, for obvious reasons. + if(universal_speak || universal_understand) + return 1 + + //Languages are handled after. + if(!speaking) + if(!other) + return 1 + if(other.universal_speak) + return 1 + if(isAI(src) && ispAI(other)) + return 1 + if(istype(other, src.type) || istype(src, other.type)) + return 1 + return 0 + + if(speaking.flags & INNATE) + return 1 + + //Language check. + for(var/datum/language/L in languages) + if(speaking.name == L.name) + return 1 + + return 0 + + +/mob/proc/say_quote(var/message, var/datum/language/speaking = null) + var/verb = "says" + var/ending = copytext(message, length(message)) + + if(speaking) + verb = speaking.get_spoken_verb(ending) + else + if(ending == "!") + verb = pick("exclaims", "shouts", "yells") + else if(ending == "?") + verb = "asks" + return verb + + +/mob/proc/emote(act, type, message, force) + if(act == "me") + return custom_emote(type, message) + + +/mob/proc/get_ear() + // returns an atom representing a location on the map from which this + // mob can hear things + + // should be overloaded for all mobs whose "ear" is separate from their "mob" + + return get_turf(src) + +/mob/proc/say_test(var/text) + var/ending = copytext(text, length(text)) + if(ending == "?") + return "1" + else if(ending == "!") + return "2" + return "0" + +//parses the message mode code (e.g. :h, :w) from text, such as that supplied to say. +//returns the message mode string or null for no message mode. +//standard mode is the mode returned for the special ';' radio code. +/mob/proc/parse_message_mode(var/message, var/standard_mode = "headset") + if(length(message) >= 1 && copytext(message, 1, 2) == ";") + return standard_mode + + if(length(message) >= 2) + var/channel_prefix = copytext(message, 1 ,3) + return department_radio_keys[channel_prefix] + + return null + +/datum/multilingual_say_piece + var/datum/language/speaking = null + var/message = "" + +/datum/multilingual_say_piece/New(datum/language/new_speaking, new_message) + . = ..() + speaking = new_speaking + if(new_message) + message = new_message + +/mob/proc/find_valid_prefixes(message) + var/list/prefixes = list() // [["Common", start, end], ["Gutter", start, end]] + for(var/i in 1 to length(message)) + var/selection = trim_right(lowertext(copytext(message, i, i + 3))) + var/datum/language/L = GLOB.language_keys[selection] + if(L != null && can_speak_language(L)) // What the fuck... remove the L != null check if you ever find out what the fuck is adding `null` to the languages list on absolutely random mobs... seriously what the hell... + prefixes[++prefixes.len] = list(L, i, i + length(selection)) + else if(!L && i == 1) + prefixes[++prefixes.len] = list(get_default_language(), i, i) + else + return prefixes + +/proc/strip_prefixes(message) + . = "" + var/last_index = 1 + for(var/i in 1 to length(message)) + var/selection = trim_right(lowertext(copytext(message, i, i + 3))) + var/datum/language/L = GLOB.language_keys[selection] + if(L) + . += copytext(message, last_index, i) + last_index = i + 3 + if(i + 1 > length(message)) + . += copytext(message, last_index) + +// this returns a structured message with language sections +// list(/datum/multilingual_say_piece(common, "hi"), /datum/multilingual_say_piece(farwa, "squik"), /datum/multilingual_say_piece(common, "meow!")) +/mob/proc/parse_languages(message) + . = list() + + // Noise language is a snowflake + if(copytext(message, 1, 2) == "!" && length(message) > 1) + return list(new /datum/multilingual_say_piece(GLOB.all_languages["Noise"], trim(strip_prefixes(copytext(message, 2))))) + + // Scan the message for prefixes + var/list/prefix_locations = find_valid_prefixes(message) + if(!LAZYLEN(prefix_locations)) // There are no prefixes... or at least, no _valid_ prefixes. + . += new /datum/multilingual_say_piece(get_default_language(), trim(strip_prefixes(message))) // So we'll just strip those pesky things and still make the message. + + for(var/i in 1 to length(prefix_locations)) + var/current = prefix_locations[i] // ["Common", keypos] + + // There are a few things that will make us want to ignore all other languages in - namely, HIVEMIND languages. + var/datum/language/L = current[1] + if(L && L.flags & HIVEMIND) + . = new /datum/multilingual_say_piece(L, trim(strip_prefixes(message))) + break + + if(i + 1 > length(prefix_locations)) // We are out of lookaheads, that means the rest of the message is in cur lang + var/spoke_message = handle_autohiss(trim(copytext(message, current[3])), L) + . += new /datum/multilingual_say_piece(current[1], spoke_message) + else + var/next = prefix_locations[i + 1] // We look ahead at the next message to see where we need to stop. + var/spoke_message = handle_autohiss(trim(copytext(message, current[3], next[2])), L) + . += new /datum/multilingual_say_piece(current[1], spoke_message) + +/* These are here purely because it would be hell to try to convert everything over to using the multi-lingual system at once */ +/proc/message_to_multilingual(message, datum/language/speaking = null) + . = list(new /datum/multilingual_say_piece(speaking, message)) + +/proc/multilingual_to_message(list/message_pieces) + . = "" + for(var/datum/multilingual_say_piece/S in message_pieces) + . += S.message + " " + . = trim_right(.) + +#undef ILLEGAL_CHARACTERS_LIST diff --git a/code/modules/mob/status_procs.dm b/code/modules/mob/status_procs.dm index 6716ea0e4a9..0aeb9beef84 100644 --- a/code/modules/mob/status_procs.dm +++ b/code/modules/mob/status_procs.dm @@ -204,4 +204,4 @@ /mob/proc/adjust_bodytemperature(amount, min_temp = 0, max_temp = INFINITY) if(bodytemperature >= min_temp && bodytemperature <= max_temp) - bodytemperature = Clamp(bodytemperature + amount, min_temp, max_temp) \ No newline at end of file + bodytemperature = Clamp(bodytemperature + amount, min_temp, max_temp) diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index 52b3bb61b27..fe19d366550 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -1,301 +1,301 @@ -/mob/living/carbon/human/proc/monkeyize() - var/mob/H = src - H.dna.SetSEState(MONKEYBLOCK,1) - genemutcheck(H,MONKEYBLOCK,null,MUTCHK_FORCED) - -/mob/new_player/AIize() - spawning = 1 - return ..() - -/mob/living/carbon/AIize() - if(notransform) - return - for(var/obj/item/W in src) - unEquip(W) - notransform = 1 - canmove = 0 - icon = null - invisibility = 101 - return ..() - -/mob/proc/AIize() - if(client) - stop_sound_channel(CHANNEL_LOBBYMUSIC) - - var/mob/living/silicon/ai/O = new (loc,,,1)//No MMI but safety is in effect. - O.invisibility = 0 - O.aiRestorePowerRoutine = 0 - - if(mind) - mind.transfer_to(O) - O.mind.original = O - else - O.key = key - - O.on_mob_init() - - O.add_ai_verbs() - - O.rename_self("AI",1) - - spawn() - qdel(src) - return O - - - -//human -> robot -/mob/living/carbon/human/proc/Robotize() - if(notransform) - return - for(var/obj/item/W in src) - unEquip(W) - regenerate_icons() - notransform = 1 - canmove = 0 - icon = null - invisibility = 101 - for(var/t in bodyparts) - qdel(t) - for(var/i in internal_organs) - qdel(i) - - var/mob/living/silicon/robot/O = new /mob/living/silicon/robot( loc ) - - // cyborgs produced by Robotize get an automatic power cell - O.cell = new /obj/item/stock_parts/cell/high(O) - - O.gender = gender - O.invisibility = 0 - - if(mind) //TODO - mind.transfer_to(O) - if(O.mind.assigned_role == "Cyborg") - O.mind.original = O - else if(mind && mind.special_role) - O.mind.store_memory("In case you look at this after being borged, the objectives are only here until I find a way to make them not show up for you, as I can't simply delete them without screwing up round-end reporting. --NeoFite") - else - O.key = key - - O.loc = loc - O.job = "Cyborg" - O.notify_ai(1) - - if(O.mind && O.mind.assigned_role == "Cyborg") - if(O.mind.role_alt_title == "Android") - O.mmi = new /obj/item/mmi/robotic_brain(O) - else if(O.mind.role_alt_title == "Robot") - O.mmi = null //Robots do not have removable brains. - else - O.mmi = new /obj/item/mmi(O) - - if(O.mmi) O.mmi.transfer_identity(src) //Does not transfer key/client. - - O.update_pipe_vision() - - O.Namepick() - - spawn(0)//To prevent the proc from returning null. - qdel(src) - return O - -//human -> alien -/mob/living/carbon/human/proc/Alienize() - if(notransform) - return - for(var/obj/item/W in src) - unEquip(W) - regenerate_icons() - notransform = 1 - canmove = 0 - icon = null - invisibility = 101 - for(var/t in bodyparts) - qdel(t) - - var/alien_caste = pick("Hunter","Sentinel","Drone") - var/mob/living/carbon/alien/humanoid/new_xeno - switch(alien_caste) - if("Hunter") - new_xeno = new /mob/living/carbon/alien/humanoid/hunter(loc) - if("Sentinel") - new_xeno = new /mob/living/carbon/alien/humanoid/sentinel(loc) - if("Drone") - new_xeno = new /mob/living/carbon/alien/humanoid/drone(loc) - - new_xeno.a_intent = INTENT_HARM - new_xeno.key = key - - to_chat(new_xeno, "You are now an alien.") - new_xeno.update_pipe_vision() - spawn(0)//To prevent the proc from returning null. - qdel(src) - return - -/mob/living/carbon/human/proc/slimeize(reproduce as num) - if(notransform) - return - notransform = TRUE - canmove = FALSE - for(var/obj/item/I in src) - unEquip(I) - regenerate_icons() - icon = null - invisibility = INVISIBILITY_MAXIMUM - for(var/t in bodyparts) - qdel(t) - - var/mob/living/simple_animal/slime/new_slime - if(reproduce) - var/number = pick(14;2,3,4) //reproduce (has a small chance of producing 3 or 4 offspring) - var/list/babies = list() - for(var/i=1,i<=number,i++) - var/mob/living/simple_animal/slime/M = new/mob/living/simple_animal/slime(loc) - M.set_nutrition(round(nutrition / number)) - step_away(M,src) - babies += M - new_slime = pick(babies) - else - new_slime = new /mob/living/simple_animal/slime(loc) - new_slime.a_intent = INTENT_HARM - new_slime.key = key - - to_chat(new_slime, "You are now a slime. Skreee!") - new_slime.update_pipe_vision() - . = new_slime - qdel(src) - -/mob/living/carbon/human/proc/corgize() - if(notransform) - return - for(var/obj/item/W in src) - unEquip(W) - regenerate_icons() - notransform = 1 - canmove = 0 - icon = null - invisibility = 101 - for(var/t in bodyparts) //this really should not be necessary - qdel(t) - - var/mob/living/simple_animal/pet/dog/corgi/new_corgi = new /mob/living/simple_animal/pet/dog/corgi (loc) - new_corgi.key = key - - to_chat(new_corgi, "You are now a Corgi. Yap Yap!") - new_corgi.update_pipe_vision() - spawn(0)//To prevent the proc from returning null. - qdel(src) - return - -/mob/living/carbon/human/Animalize() - - var/list/mobtypes = typesof(/mob/living/simple_animal) - var/mobpath = input("Which type of mob should [src] turn into?", "Choose a type") in mobtypes - - if(notransform) - return - for(var/obj/item/W in src) - unEquip(W) - - regenerate_icons() - notransform = 1 - canmove = 0 - icon = null - invisibility = 101 - - for(var/t in bodyparts) - qdel(t) - - var/mob/new_mob = new mobpath(src.loc) - - new_mob.key = key - new_mob.a_intent = INTENT_HARM - - - to_chat(new_mob, "You suddenly feel more... animalistic.") - new_mob.update_pipe_vision() - spawn() - qdel(src) - return - -/mob/proc/Animalize() - - var/list/mobtypes = typesof(/mob/living/simple_animal) - var/mobpath = input("Which type of mob should [src] turn into?", "Choose a type") in mobtypes - - var/mob/new_mob = new mobpath(src.loc) - - new_mob.key = key - new_mob.a_intent = INTENT_HARM - to_chat(new_mob, "You feel more... animalistic") - new_mob.update_pipe_vision() - - qdel(src) - - -/mob/living/carbon/human/proc/paize(var/name) - if(notransform) - return - for(var/obj/item/W in src) - unEquip(W) - regenerate_icons() - notransform = 1 - canmove = 0 - icon = null - invisibility = 101 - for(var/t in bodyparts) //this really should not be necessary - qdel(t) - - var/obj/item/paicard/card = new(loc) - var/mob/living/silicon/pai/pai = new(card) - pai.key = key - card.setPersonality(pai) - - pai.name = name - pai.real_name = name - card.name = name - - to_chat(pai, "You have become a pAI! Your name is [pai.name].") - pai.update_pipe_vision() - spawn(0)//To prevent the proc from returning null. - qdel(src) - return - -/mob/proc/safe_respawn(var/MP) - if(!MP) - return 0 - - if(!GAMEMODE_IS_NUCLEAR) - if(ispath(MP, /mob/living/simple_animal/pet/cat/Syndi)) - return 0 - if(ispath(MP, /mob/living/simple_animal/pet/cat)) - return 1 - if(ispath(MP, /mob/living/simple_animal/pet/dog/corgi)) - return 1 - if(ispath(MP, /mob/living/simple_animal/crab)) - return 1 - if(ispath(MP, /mob/living/simple_animal/chicken)) - return 1 - if(ispath(MP, /mob/living/simple_animal/cow)) - return 1 - if(ispath(MP, /mob/living/simple_animal/parrot)) - return 1 - if(!GAMEMODE_IS_NUCLEAR) - if(ispath(MP, /mob/living/simple_animal/pet/dog/fox/Syndifox)) - return 0 - if(ispath(MP, /mob/living/simple_animal/pet/dog/fox)) - return 1 - if(ispath(MP, /mob/living/simple_animal/chick)) - return 1 - if(ispath(MP, /mob/living/simple_animal/pet/dog/pug)) - return 1 - if(ispath(MP, /mob/living/simple_animal/butterfly)) - return 1 - - if(ispath(MP, /mob/living/simple_animal/borer) && !jobban_isbanned(src, ROLE_BORER) && !jobban_isbanned(src, "Syndicate")) - return 1 - - if(ispath(MP, /mob/living/simple_animal/diona) && !jobban_isbanned(src, ROLE_NYMPH)) - return 1 - - return 0 +/mob/living/carbon/human/proc/monkeyize() + var/mob/H = src + H.dna.SetSEState(MONKEYBLOCK,1) + genemutcheck(H,MONKEYBLOCK,null,MUTCHK_FORCED) + +/mob/new_player/AIize() + spawning = 1 + return ..() + +/mob/living/carbon/AIize() + if(notransform) + return + for(var/obj/item/W in src) + unEquip(W) + notransform = 1 + canmove = 0 + icon = null + invisibility = 101 + return ..() + +/mob/proc/AIize() + if(client) + stop_sound_channel(CHANNEL_LOBBYMUSIC) + + var/mob/living/silicon/ai/O = new (loc,,,1)//No MMI but safety is in effect. + O.invisibility = 0 + O.aiRestorePowerRoutine = 0 + + if(mind) + mind.transfer_to(O) + O.mind.original = O + else + O.key = key + + O.on_mob_init() + + O.add_ai_verbs() + + O.rename_self("AI",1) + + spawn() + qdel(src) + return O + + + +//human -> robot +/mob/living/carbon/human/proc/Robotize() + if(notransform) + return + for(var/obj/item/W in src) + unEquip(W) + regenerate_icons() + notransform = 1 + canmove = 0 + icon = null + invisibility = 101 + for(var/t in bodyparts) + qdel(t) + for(var/i in internal_organs) + qdel(i) + + var/mob/living/silicon/robot/O = new /mob/living/silicon/robot( loc ) + + // cyborgs produced by Robotize get an automatic power cell + O.cell = new /obj/item/stock_parts/cell/high(O) + + O.gender = gender + O.invisibility = 0 + + if(mind) //TODO + mind.transfer_to(O) + if(O.mind.assigned_role == "Cyborg") + O.mind.original = O + else if(mind && mind.special_role) + O.mind.store_memory("In case you look at this after being borged, the objectives are only here until I find a way to make them not show up for you, as I can't simply delete them without screwing up round-end reporting. --NeoFite") + else + O.key = key + + O.loc = loc + O.job = "Cyborg" + O.notify_ai(1) + + if(O.mind && O.mind.assigned_role == "Cyborg") + if(O.mind.role_alt_title == "Android") + O.mmi = new /obj/item/mmi/robotic_brain(O) + else if(O.mind.role_alt_title == "Robot") + O.mmi = null //Robots do not have removable brains. + else + O.mmi = new /obj/item/mmi(O) + + if(O.mmi) O.mmi.transfer_identity(src) //Does not transfer key/client. + + O.update_pipe_vision() + + O.Namepick() + + spawn(0)//To prevent the proc from returning null. + qdel(src) + return O + +//human -> alien +/mob/living/carbon/human/proc/Alienize() + if(notransform) + return + for(var/obj/item/W in src) + unEquip(W) + regenerate_icons() + notransform = 1 + canmove = 0 + icon = null + invisibility = 101 + for(var/t in bodyparts) + qdel(t) + + var/alien_caste = pick("Hunter","Sentinel","Drone") + var/mob/living/carbon/alien/humanoid/new_xeno + switch(alien_caste) + if("Hunter") + new_xeno = new /mob/living/carbon/alien/humanoid/hunter(loc) + if("Sentinel") + new_xeno = new /mob/living/carbon/alien/humanoid/sentinel(loc) + if("Drone") + new_xeno = new /mob/living/carbon/alien/humanoid/drone(loc) + + new_xeno.a_intent = INTENT_HARM + new_xeno.key = key + + to_chat(new_xeno, "You are now an alien.") + new_xeno.update_pipe_vision() + spawn(0)//To prevent the proc from returning null. + qdel(src) + return + +/mob/living/carbon/human/proc/slimeize(reproduce as num) + if(notransform) + return + notransform = TRUE + canmove = FALSE + for(var/obj/item/I in src) + unEquip(I) + regenerate_icons() + icon = null + invisibility = INVISIBILITY_MAXIMUM + for(var/t in bodyparts) + qdel(t) + + var/mob/living/simple_animal/slime/new_slime + if(reproduce) + var/number = pick(14;2,3,4) //reproduce (has a small chance of producing 3 or 4 offspring) + var/list/babies = list() + for(var/i=1,i<=number,i++) + var/mob/living/simple_animal/slime/M = new/mob/living/simple_animal/slime(loc) + M.set_nutrition(round(nutrition / number)) + step_away(M,src) + babies += M + new_slime = pick(babies) + else + new_slime = new /mob/living/simple_animal/slime(loc) + new_slime.a_intent = INTENT_HARM + new_slime.key = key + + to_chat(new_slime, "You are now a slime. Skreee!") + new_slime.update_pipe_vision() + . = new_slime + qdel(src) + +/mob/living/carbon/human/proc/corgize() + if(notransform) + return + for(var/obj/item/W in src) + unEquip(W) + regenerate_icons() + notransform = 1 + canmove = 0 + icon = null + invisibility = 101 + for(var/t in bodyparts) //this really should not be necessary + qdel(t) + + var/mob/living/simple_animal/pet/dog/corgi/new_corgi = new /mob/living/simple_animal/pet/dog/corgi (loc) + new_corgi.key = key + + to_chat(new_corgi, "You are now a Corgi. Yap Yap!") + new_corgi.update_pipe_vision() + spawn(0)//To prevent the proc from returning null. + qdel(src) + return + +/mob/living/carbon/human/Animalize() + + var/list/mobtypes = typesof(/mob/living/simple_animal) + var/mobpath = input("Which type of mob should [src] turn into?", "Choose a type") in mobtypes + + if(notransform) + return + for(var/obj/item/W in src) + unEquip(W) + + regenerate_icons() + notransform = 1 + canmove = 0 + icon = null + invisibility = 101 + + for(var/t in bodyparts) + qdel(t) + + var/mob/new_mob = new mobpath(src.loc) + + new_mob.key = key + new_mob.a_intent = INTENT_HARM + + + to_chat(new_mob, "You suddenly feel more... animalistic.") + new_mob.update_pipe_vision() + spawn() + qdel(src) + return + +/mob/proc/Animalize() + + var/list/mobtypes = typesof(/mob/living/simple_animal) + var/mobpath = input("Which type of mob should [src] turn into?", "Choose a type") in mobtypes + + var/mob/new_mob = new mobpath(src.loc) + + new_mob.key = key + new_mob.a_intent = INTENT_HARM + to_chat(new_mob, "You feel more... animalistic") + new_mob.update_pipe_vision() + + qdel(src) + + +/mob/living/carbon/human/proc/paize(var/name) + if(notransform) + return + for(var/obj/item/W in src) + unEquip(W) + regenerate_icons() + notransform = 1 + canmove = 0 + icon = null + invisibility = 101 + for(var/t in bodyparts) //this really should not be necessary + qdel(t) + + var/obj/item/paicard/card = new(loc) + var/mob/living/silicon/pai/pai = new(card) + pai.key = key + card.setPersonality(pai) + + pai.name = name + pai.real_name = name + card.name = name + + to_chat(pai, "You have become a pAI! Your name is [pai.name].") + pai.update_pipe_vision() + spawn(0)//To prevent the proc from returning null. + qdel(src) + return + +/mob/proc/safe_respawn(var/MP) + if(!MP) + return 0 + + if(!GAMEMODE_IS_NUCLEAR) + if(ispath(MP, /mob/living/simple_animal/pet/cat/Syndi)) + return 0 + if(ispath(MP, /mob/living/simple_animal/pet/cat)) + return 1 + if(ispath(MP, /mob/living/simple_animal/pet/dog/corgi)) + return 1 + if(ispath(MP, /mob/living/simple_animal/crab)) + return 1 + if(ispath(MP, /mob/living/simple_animal/chicken)) + return 1 + if(ispath(MP, /mob/living/simple_animal/cow)) + return 1 + if(ispath(MP, /mob/living/simple_animal/parrot)) + return 1 + if(!GAMEMODE_IS_NUCLEAR) + if(ispath(MP, /mob/living/simple_animal/pet/dog/fox/Syndifox)) + return 0 + if(ispath(MP, /mob/living/simple_animal/pet/dog/fox)) + return 1 + if(ispath(MP, /mob/living/simple_animal/chick)) + return 1 + if(ispath(MP, /mob/living/simple_animal/pet/dog/pug)) + return 1 + if(ispath(MP, /mob/living/simple_animal/butterfly)) + return 1 + + if(ispath(MP, /mob/living/simple_animal/borer) && !jobban_isbanned(src, ROLE_BORER) && !jobban_isbanned(src, "Syndicate")) + return 1 + + if(ispath(MP, /mob/living/simple_animal/diona) && !jobban_isbanned(src, ROLE_NYMPH)) + return 1 + + return 0 diff --git a/code/modules/mob/typing_indicator.dm b/code/modules/mob/typing_indicator.dm index 9ad5449ee8b..1dd4c9f8ccb 100644 --- a/code/modules/mob/typing_indicator.dm +++ b/code/modules/mob/typing_indicator.dm @@ -90,4 +90,4 @@ var/global/image/typing_indicator if(prefs.toggles & SHOW_TYPING) if(istype(mob)) mob.set_typing_indicator(0) - feedback_add_details("admin_verb","TID") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! \ No newline at end of file + feedback_add_details("admin_verb","TID") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/mob/update_icons.dm b/code/modules/mob/update_icons.dm index 8a1f1b04cb1..0efe10b40cf 100644 --- a/code/modules/mob/update_icons.dm +++ b/code/modules/mob/update_icons.dm @@ -1,68 +1,68 @@ -//Most of these are defined at this level to reduce on checks elsewhere in the code. -//Having them here also makes for a nice reference list of the various overlay-updating procs available - -/mob/proc/regenerate_icons() //TODO: phase this out completely if possible - return - -/mob/proc/update_icons() - return - -/mob/proc/update_inv_handcuffed() - return - -/mob/proc/update_inv_legcuffed() - return - -/mob/proc/update_inv_back() - return - -/mob/proc/update_inv_l_hand() - return - -/mob/proc/update_inv_r_hand() - return - -/mob/proc/update_inv_wear_mask() - return - -/mob/proc/update_inv_wear_suit() - return - -/mob/proc/update_inv_w_uniform() - return - -/mob/proc/update_inv_belt() - return - -/mob/proc/update_inv_head() - return - -/mob/proc/update_inv_gloves() - return - -/mob/proc/update_mutations() - return - -/mob/proc/update_inv_wear_id() - return - -/mob/proc/update_inv_shoes() - return - -/mob/proc/update_inv_glasses() - return - -/mob/proc/update_inv_s_store() - return - -/mob/proc/update_inv_pockets() - return - -/mob/proc/update_inv_wear_pda() - return - -/mob/proc/update_inv_ears() - return - -/mob/proc/update_transform() - return \ No newline at end of file +//Most of these are defined at this level to reduce on checks elsewhere in the code. +//Having them here also makes for a nice reference list of the various overlay-updating procs available + +/mob/proc/regenerate_icons() //TODO: phase this out completely if possible + return + +/mob/proc/update_icons() + return + +/mob/proc/update_inv_handcuffed() + return + +/mob/proc/update_inv_legcuffed() + return + +/mob/proc/update_inv_back() + return + +/mob/proc/update_inv_l_hand() + return + +/mob/proc/update_inv_r_hand() + return + +/mob/proc/update_inv_wear_mask() + return + +/mob/proc/update_inv_wear_suit() + return + +/mob/proc/update_inv_w_uniform() + return + +/mob/proc/update_inv_belt() + return + +/mob/proc/update_inv_head() + return + +/mob/proc/update_inv_gloves() + return + +/mob/proc/update_mutations() + return + +/mob/proc/update_inv_wear_id() + return + +/mob/proc/update_inv_shoes() + return + +/mob/proc/update_inv_glasses() + return + +/mob/proc/update_inv_s_store() + return + +/mob/proc/update_inv_pockets() + return + +/mob/proc/update_inv_wear_pda() + return + +/mob/proc/update_inv_ears() + return + +/mob/proc/update_transform() + return diff --git a/code/modules/mob/update_status.dm b/code/modules/mob/update_status.dm index 29bcced144a..1708bcfb7a2 100644 --- a/code/modules/mob/update_status.dm +++ b/code/modules/mob/update_status.dm @@ -48,7 +48,10 @@ /mob/proc/restrained(ignore_grab) // All are created free - return 0 + return FALSE + +/mob/proc/get_restraining_item() + return null // Procs that update other things about the mob diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm index b58dfd4cc66..1e43884df9a 100644 --- a/code/modules/modular_computers/computers/item/computer.dm +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -325,23 +325,6 @@ qdel(src) return - if(istype(W, /obj/item/weldingtool)) - var/obj/item/weldingtool/WT = W - if(!WT.isOn()) - to_chat(user, "\The [W] is off.") - return - - if(obj_integrity == max_integrity) - to_chat(user, "\The [src] does not require repairs.") - return - - to_chat(user, "You begin repairing damage to \the [src]...") - var/dmg = round(max_integrity - obj_integrity) - if(WT.remove_fuel(round(dmg/75)) && do_after(usr, dmg/10)) - obj_integrity = max_integrity - to_chat(user, "You repair \the [src].") - return - if(istype(W, /obj/item/screwdriver)) if(!all_components.len) to_chat(user, "This device doesn't have any components installed.") @@ -369,6 +352,12 @@ ..() +/obj/item/modular_computer/welder_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + default_welder_repair(user, I) + // Used by processor to relay qdel() to machinery type. /obj/item/modular_computer/proc/relay_qdel() return diff --git a/code/modules/modular_computers/computers/item/laptop_presets.dm b/code/modules/modular_computers/computers/item/laptop_presets.dm index e492712d6e4..43720dfa2fc 100644 --- a/code/modules/modular_computers/computers/item/laptop_presets.dm +++ b/code/modules/modular_computers/computers/item/laptop_presets.dm @@ -18,4 +18,4 @@ /obj/item/modular_computer/laptop/preset/civillian/install_programs() var/obj/item/computer_hardware/hard_drive/hard_drive = all_components[MC_HDD] hard_drive.store_file(new/datum/computer_file/program/chatclient()) - hard_drive.store_file(new/datum/computer_file/program/nttransfer()) \ No newline at end of file + hard_drive.store_file(new/datum/computer_file/program/nttransfer()) diff --git a/code/modules/modular_computers/computers/item/processor.dm b/code/modules/modular_computers/computers/item/processor.dm index 417b98f15bf..7bd5be3cf81 100644 --- a/code/modules/modular_computers/computers/item/processor.dm +++ b/code/modules/modular_computers/computers/item/processor.dm @@ -71,4 +71,4 @@ if(MC_SDD) machinery_computer.verbs -= /obj/machinery/modular_computer/proc/eject_disk if(MC_AI) - machinery_computer.verbs -= /obj/machinery/modular_computer/proc/eject_card \ No newline at end of file + machinery_computer.verbs -= /obj/machinery/modular_computer/proc/eject_card diff --git a/code/modules/modular_computers/computers/item/tablet.dm b/code/modules/modular_computers/computers/item/tablet.dm index 7547c797e25..f0a044b1d36 100644 --- a/code/modules/modular_computers/computers/item/tablet.dm +++ b/code/modules/modular_computers/computers/item/tablet.dm @@ -9,4 +9,4 @@ max_hardware_size = 1 w_class = WEIGHT_CLASS_SMALL steel_sheet_cost = 1 - slot_flags = SLOT_ID | SLOT_BELT \ No newline at end of file + slot_flags = SLOT_ID | SLOT_BELT diff --git a/code/modules/modular_computers/computers/machinery/console_presets.dm b/code/modules/modular_computers/computers/machinery/console_presets.dm index 5dd324945fa..4f1bd269337 100644 --- a/code/modules/modular_computers/computers/machinery/console_presets.dm +++ b/code/modules/modular_computers/computers/machinery/console_presets.dm @@ -73,4 +73,4 @@ /obj/machinery/modular_computer/console/preset/civilian/install_programs() var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD] hard_drive.store_file(new/datum/computer_file/program/chatclient()) - hard_drive.store_file(new/datum/computer_file/program/nttransfer()) \ No newline at end of file + hard_drive.store_file(new/datum/computer_file/program/nttransfer()) diff --git a/code/modules/modular_computers/computers/machinery/modular_console.dm b/code/modules/modular_computers/computers/machinery/modular_console.dm index aeb4c18bc2d..e16ce82702c 100644 --- a/code/modules/modular_computers/computers/machinery/modular_console.dm +++ b/code/modules/modular_computers/computers/machinery/modular_console.dm @@ -50,4 +50,4 @@ if(cpu) cpu.screen_on = 1 - update_icon() \ No newline at end of file + update_icon() diff --git a/code/modules/modular_computers/documentation.md b/code/modules/modular_computers/documentation.md index 88d8602729d..9caae69aade 100644 --- a/code/modules/modular_computers/documentation.md +++ b/code/modules/modular_computers/documentation.md @@ -16,8 +16,8 @@ This is how the base program is setup. the rest is mostly tgui stuff. I'll use t extended_desc = "This program is a dummy. // This is a sort of a description, visible when looking on the ntnet size = 12 // size of the program. Big programs need more hard drive space. Don't make it too big though. requires_ntnet = 1 // If this is set, the program will not run without an ntnet connection, and will close if the connection is lost. Mainly for primarily online programs. - required_access = access_network //This is access required to run the program itself. ONLY SET THIS FOR SUPER SECURE SHIT. This also acts as transfer_access as well. - transfer_access = access_change_ids // This is the access needed to download from ntnet or host on the ptp program. This is what you want to use most of the time. + required_access = ACCESS_NETWORK //This is access required to run the program itself. ONLY SET THIS FOR SUPER SECURE SHIT. This also acts as transfer_access as well. + transfer_access = ACCESS_CHANGE_IDS // This is the access needed to download from ntnet or host on the ptp program. This is what you want to use most of the time. available_on_ntnet = 1 //If it's available to download on ntnet. pretty self explanatory. available_on_syndinet = 0 // ditto but on emagged syndie net. Use this for antag programs usage_flags = PROGRAM_ALL // Bitflags (PROGRAM_CONSOLE, PROGRAM_LAPTOP, PROGRAM_TABLET combination) or PROGRAM_ALL diff --git a/code/modules/modular_computers/file_system/computer_file.dm b/code/modules/modular_computers/file_system/computer_file.dm index f3b5a9659b2..a93aaf2412e 100644 --- a/code/modules/modular_computers/file_system/computer_file.dm +++ b/code/modules/modular_computers/file_system/computer_file.dm @@ -47,4 +47,4 @@ var/global/file_uid = 0 input_password = sanitize(input(user, "Please enter a password to access file '[filename]':")) if(input_password == password) return TRUE - return FALSE \ No newline at end of file + return FALSE diff --git a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm index 8aa80631641..22b0fa1a511 100644 --- a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm +++ b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm @@ -100,4 +100,4 @@ data["relays"] += list(list("id" = R.uid)) data["focus"] = target ? target.uid : null - return data \ No newline at end of file + return data diff --git a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm index 22df710c6f8..42f722af799 100644 --- a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm +++ b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm @@ -71,4 +71,4 @@ data["armed"] = armed - return data \ No newline at end of file + return data diff --git a/code/modules/modular_computers/file_system/programs/command/card.dm b/code/modules/modular_computers/file_system/programs/command/card.dm index 31bb8733463..f211ee6fd49 100644 --- a/code/modules/modular_computers/file_system/programs/command/card.dm +++ b/code/modules/modular_computers/file_system/programs/command/card.dm @@ -3,7 +3,7 @@ filedesc = "ID card modification program" program_icon_state = "id" extended_desc = "Program for programming employee ID cards to access parts of the station." - transfer_access = access_change_ids + transfer_access = ACCESS_CHANGE_IDS requires_ntnet = 0 size = 8 var/is_centcom = 0 @@ -57,7 +57,7 @@ return 0 /datum/computer_file/program/card_mod/proc/check_access(obj/item/I) - if(access_change_ids in I.GetAccess()) + if(ACCESS_CHANGE_IDS in I.GetAccess()) return 1 return 0 @@ -481,4 +481,4 @@ data["regions"] = regions - return data \ No newline at end of file + return data diff --git a/code/modules/modular_computers/file_system/programs/command/comms.dm b/code/modules/modular_computers/file_system/programs/command/comms.dm index dfce733cc7a..eb6f079d078 100644 --- a/code/modules/modular_computers/file_system/programs/command/comms.dm +++ b/code/modules/modular_computers/file_system/programs/command/comms.dm @@ -3,7 +3,7 @@ filedesc = "Command and communications" program_icon_state = "comm" extended_desc = "Used to command and control the station. Can relay long-range communications. This program can not be run on tablet computers." - required_access = access_heads + required_access = ACCESS_HEADS requires_ntnet = 1 size = 12 usage_flags = PROGRAM_CONSOLE | PROGRAM_LAPTOP @@ -175,10 +175,10 @@ return var/list/access = usr.get_access() - if(access_heads in access) + if(ACCESS_HEADS in access) authenticated = COMM_AUTHENTICATION_MIN - if(access_captain in access) + if(ACCESS_CAPTAIN in access) authenticated = COMM_AUTHENTICATION_MAX var/mob/living/carbon/human/H = usr var/obj/item/card/id = H.get_idcard(TRUE) @@ -221,7 +221,7 @@ var/obj/item/pda/pda = I I = pda.id if(I && istype(I)) - if(access_captain in I.access) + if(ACCESS_CAPTAIN in I.access) change_security_level(usr, text2num(href_list["level"])) else to_chat(usr, "You are not authorized to do this.") @@ -382,4 +382,4 @@ to_chat(usr, "Nano-Mob Hunter GO! game server is offline for extended maintenance. Contact your Central Command administrators for more info if desired.") SSnanoui.update_uis(src) - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/modular_computers/file_system/programs/engineering/alarm.dm b/code/modules/modular_computers/file_system/programs/engineering/alarm.dm index 226511e2c09..8cb47337a6d 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/alarm.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/alarm.dm @@ -68,4 +68,4 @@ "lost_sources" = lost_sources.len ? sanitize(english_list(lost_sources, nothing_text = "", and_text = ", ")) : "")) data["categories"] = categories - return data \ No newline at end of file + return data diff --git a/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm b/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm index 783ae9fa947..0695c496541 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm @@ -4,7 +4,7 @@ program_icon_state = "power_monitor" extended_desc = "This program connects to sensors around the station to provide information about electrical systems" ui_header = "power_norm.gif" - transfer_access = access_engine + transfer_access = ACCESS_ENGINE usage_flags = PROGRAM_CONSOLE requires_ntnet = 0 network_destination = "power monitoring system" @@ -45,4 +45,4 @@ data["powerdemand"] = powernet.load data["apcs"] = apc_repository.apc_data(powernet) - return data \ No newline at end of file + return data diff --git a/code/modules/modular_computers/file_system/programs/engineering/sm_monitor.dm b/code/modules/modular_computers/file_system/programs/engineering/sm_monitor.dm index db9081f6ad1..379e71ba484 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/sm_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/sm_monitor.dm @@ -5,7 +5,7 @@ program_icon_state = "smmon_0" extended_desc = "This program connects to specially calibrated supermatter sensors to provide information on the status of supermatter-based engines." requires_ntnet = TRUE - transfer_access = access_construction + transfer_access = ACCESS_CONSTRUCTION network_destination = "supermatter monitoring system" size = 5 var/last_status = SUPERMATTER_INACTIVE diff --git a/code/modules/modular_computers/file_system/programs/generic/file_browser.dm b/code/modules/modular_computers/file_system/programs/generic/file_browser.dm index 23b2cdab7ab..e7edc437639 100644 --- a/code/modules/modular_computers/file_system/programs/generic/file_browser.dm +++ b/code/modules/modular_computers/file_system/programs/generic/file_browser.dm @@ -225,4 +225,4 @@ if(!F || F.undeletable) return 1 if(F.can_access_file(usr)) - F.password = "" \ No newline at end of file + F.password = "" diff --git a/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm b/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm index d8278081ffa..c0447b591bf 100644 --- a/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm +++ b/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm @@ -175,4 +175,4 @@ /datum/computer_file/program/ntnetdownload/kill_program(forced) abort_file_download() - return ..(forced) \ No newline at end of file + return ..(forced) diff --git a/code/modules/modular_computers/file_system/programs/generic/ntnrc_client.dm b/code/modules/modular_computers/file_system/programs/generic/ntnrc_client.dm index 19b3fe80ffe..5a6affbb0a0 100644 --- a/code/modules/modular_computers/file_system/programs/generic/ntnrc_client.dm +++ b/code/modules/modular_computers/file_system/programs/generic/ntnrc_client.dm @@ -154,7 +154,7 @@ channel = null return 1 var/mob/living/user = usr - if(can_run(user, 1, access_network)) + if(can_run(user, 1, ACCESS_NETWORK)) if(channel) var/response = alert(user, "Really engage admin-mode? You will be disconnected from your current channel!", "NTNRC Admin mode", "Yes", "No") if(response == "Yes") diff --git a/code/modules/modular_computers/file_system/programs/generic/nttransfer.dm b/code/modules/modular_computers/file_system/programs/generic/nttransfer.dm index ef26a35730c..184b2cf300f 100644 --- a/code/modules/modular_computers/file_system/programs/generic/nttransfer.dm +++ b/code/modules/modular_computers/file_system/programs/generic/nttransfer.dm @@ -192,4 +192,4 @@ var/global/nttransfer_uid = 0 ))) data["servers"] = all_servers - return data \ No newline at end of file + return data diff --git a/code/modules/modular_computers/file_system/programs/research/airestorer.dm b/code/modules/modular_computers/file_system/programs/research/airestorer.dm index a39adb8a48e..d5ff31af66b 100644 --- a/code/modules/modular_computers/file_system/programs/research/airestorer.dm +++ b/code/modules/modular_computers/file_system/programs/research/airestorer.dm @@ -6,7 +6,7 @@ size = 12 requires_ntnet = 0 usage_flags = PROGRAM_CONSOLE - transfer_access = access_heads + transfer_access = ACCESS_HEADS available_on_ntnet = 1 var/restoring = FALSE @@ -134,4 +134,4 @@ /datum/computer_file/program/aidiag/kill_program(forced) restoring = FALSE - return ..(forced) \ No newline at end of file + return ..(forced) diff --git a/code/modules/modular_computers/file_system/programs/research/ntmonitor.dm b/code/modules/modular_computers/file_system/programs/research/ntmonitor.dm index bf1d71a95ec..6f3f41df272 100644 --- a/code/modules/modular_computers/file_system/programs/research/ntmonitor.dm +++ b/code/modules/modular_computers/file_system/programs/research/ntmonitor.dm @@ -5,7 +5,7 @@ extended_desc = "This program monitors stationwide NTNet network, provides access to logging systems, and allows for configuration changes" size = 12 requires_ntnet = 1 - required_access = access_network //Network control is a more secure program. + required_access = ACCESS_NETWORK //Network control is a more secure program. available_on_ntnet = 1 /datum/computer_file/program/ntnetmonitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) @@ -87,4 +87,4 @@ data["ntnetlogs"] += list(list("entry" = i)) data["ntnetmaxlogs"] = ntnet_global.setting_maxlogcount - return data \ No newline at end of file + return data diff --git a/code/modules/modular_computers/hardware/CPU.dm b/code/modules/modular_computers/hardware/CPU.dm index fa6443240ec..02e7d41d512 100644 --- a/code/modules/modular_computers/hardware/CPU.dm +++ b/code/modules/modular_computers/hardware/CPU.dm @@ -41,4 +41,4 @@ w_class = WEIGHT_CLASS_TINY power_usage = 75 max_idle_programs = 2 - origin_tech = "programming=4;engineering=3" \ No newline at end of file + origin_tech = "programming=4;engineering=3" diff --git a/code/modules/modular_computers/hardware/ai_slot.dm b/code/modules/modular_computers/hardware/ai_slot.dm index 1a112b248a5..1b97ae5ede8 100644 --- a/code/modules/modular_computers/hardware/ai_slot.dm +++ b/code/modules/modular_computers/hardware/ai_slot.dm @@ -77,4 +77,4 @@ if(istype(I, /obj/item/screwdriver)) to_chat(user, "You press down on the manual eject button with \the [I].") try_eject(0, user, 1) - return \ No newline at end of file + return diff --git a/code/modules/modular_computers/hardware/battery_module.dm b/code/modules/modular_computers/hardware/battery_module.dm index 98f1ab5667e..7cec597dc13 100644 --- a/code/modules/modular_computers/hardware/battery_module.dm +++ b/code/modules/modular_computers/hardware/battery_module.dm @@ -106,4 +106,4 @@ desc = "A tiny power cell, commonly seen in low-end portable microcomputers." icon_state = "cell_micro" w_class = WEIGHT_CLASS_TINY - maxcharge = 300 \ No newline at end of file + maxcharge = 300 diff --git a/code/modules/modular_computers/hardware/hard_drive.dm b/code/modules/modular_computers/hardware/hard_drive.dm index 82c4aa5f62c..ce7fc997192 100644 --- a/code/modules/modular_computers/hardware/hard_drive.dm +++ b/code/modules/modular_computers/hardware/hard_drive.dm @@ -170,4 +170,4 @@ origin_tech = "programming=1;engineering=1" max_capacity = 32 icon_state = "ssd_micro" - w_class = WEIGHT_CLASS_TINY \ No newline at end of file + w_class = WEIGHT_CLASS_TINY diff --git a/code/modules/modular_computers/hardware/printer.dm b/code/modules/modular_computers/hardware/printer.dm index 0db45f33e6e..49c5d72a6f1 100644 --- a/code/modules/modular_computers/hardware/printer.dm +++ b/code/modules/modular_computers/hardware/printer.dm @@ -67,4 +67,4 @@ icon_state = "printer_mini" w_class = WEIGHT_CLASS_TINY stored_paper = 5 - max_paper = 15 \ No newline at end of file + max_paper = 15 diff --git a/code/modules/nano/interaction/not_incapacitated.dm b/code/modules/nano/interaction/not_incapacitated.dm index a0116409c90..db12edf3119 100644 --- a/code/modules/nano/interaction/not_incapacitated.dm +++ b/code/modules/nano/interaction/not_incapacitated.dm @@ -22,4 +22,4 @@ return STATUS_CLOSE if(user.incapacitated() || user.lying || (turf_check && !isturf(user.loc))) return STATUS_DISABLED - return STATUS_INTERACTIVE \ No newline at end of file + return STATUS_INTERACTIVE diff --git a/code/modules/nano/modules/ert_manager.dm b/code/modules/nano/modules/ert_manager.dm index 6675cd985d4..e218ad2ae68 100644 --- a/code/modules/nano/modules/ert_manager.dm +++ b/code/modules/nano/modules/ert_manager.dm @@ -70,6 +70,7 @@ notify_ghosts("An ERT is being dispatched. Open positions: [slot_text]") message_admins("[key_name_admin(usr)] dispatched a [ert_type] ERT. Slots: [slot_text]", 1) log_admin("[key_name(usr)] dispatched a [ert_type] ERT. Slots: [slot_text]") + event_announcement.Announce("Attention, [station_name()]. We are attempting to assemble an ERT. Standby.", "ERT Protocol Activated") autoclose = 1 ui_interact(usr) trigger_armed_response_team(convert_ert_string(ert_type), commander_slots, security_slots, medical_slots, engineering_slots, janitor_slots, paranormal_slots, cyborg_slots) diff --git a/code/modules/nano/nanomapgen.dm b/code/modules/nano/nanomapgen.dm index 10bf8f288a6..be9c5bbe2b3 100644 --- a/code/modules/nano/nanomapgen.dm +++ b/code/modules/nano/nanomapgen.dm @@ -87,4 +87,4 @@ if(Tile.Width() != NANOMAP_MAX_ICON_DIMENSION || Tile.Height() != NANOMAP_MAX_ICON_DIMENSION) return NANOMAP_BADOUTPUT - return NANOMAP_SUCCESS \ No newline at end of file + return NANOMAP_SUCCESS diff --git a/code/modules/ninja/Ninja_Readme.dm b/code/modules/ninja/Ninja_Readme.dm index 0ffdd02fcbc..70b786d12ed 100644 --- a/code/modules/ninja/Ninja_Readme.dm +++ b/code/modules/ninja/Ninja_Readme.dm @@ -1 +1 @@ -// TODO \ No newline at end of file +// TODO diff --git a/code/modules/ninja/__ninjaDefines.dm b/code/modules/ninja/__ninjaDefines.dm index a8dc65f9f13..4c3562273cc 100644 --- a/code/modules/ninja/__ninjaDefines.dm +++ b/code/modules/ninja/__ninjaDefines.dm @@ -10,4 +10,4 @@ Contents: //ninjacost() specificCheck defines #define N_STEALTH_CANCEL 1 #define N_SMOKE_BOMB 2 -#define N_ADRENALINE 3 \ No newline at end of file +#define N_ADRENALINE 3 diff --git a/code/modules/ninja/admin_ninja_verbs.dm b/code/modules/ninja/admin_ninja_verbs.dm index 0ffdd02fcbc..70b786d12ed 100644 --- a/code/modules/ninja/admin_ninja_verbs.dm +++ b/code/modules/ninja/admin_ninja_verbs.dm @@ -1 +1 @@ -// TODO \ No newline at end of file +// TODO diff --git a/code/modules/ninja/energy_katana.dm b/code/modules/ninja/energy_katana.dm index ee851253449..cf694e59d83 100644 --- a/code/modules/ninja/energy_katana.dm +++ b/code/modules/ninja/energy_katana.dm @@ -75,4 +75,4 @@ /obj/item/katana/energy/Destroy() QDEL_NULL(spark_system) - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/ninja/ninja_event.dm b/code/modules/ninja/ninja_event.dm index 0ffdd02fcbc..70b786d12ed 100644 --- a/code/modules/ninja/ninja_event.dm +++ b/code/modules/ninja/ninja_event.dm @@ -1 +1 @@ -// TODO \ No newline at end of file +// TODO diff --git a/code/modules/ninja/suit/n_suit_verbs/energy_net_nets.dm b/code/modules/ninja/suit/n_suit_verbs/energy_net_nets.dm index 0ffdd02fcbc..70b786d12ed 100644 --- a/code/modules/ninja/suit/n_suit_verbs/energy_net_nets.dm +++ b/code/modules/ninja/suit/n_suit_verbs/energy_net_nets.dm @@ -1 +1 @@ -// TODO \ No newline at end of file +// TODO diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_adrenaline.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_adrenaline.dm index 0ffdd02fcbc..70b786d12ed 100644 --- a/code/modules/ninja/suit/n_suit_verbs/ninja_adrenaline.dm +++ b/code/modules/ninja/suit/n_suit_verbs/ninja_adrenaline.dm @@ -1 +1 @@ -// TODO \ No newline at end of file +// TODO diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_cost_check.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_cost_check.dm index 0ffdd02fcbc..70b786d12ed 100644 --- a/code/modules/ninja/suit/n_suit_verbs/ninja_cost_check.dm +++ b/code/modules/ninja/suit/n_suit_verbs/ninja_cost_check.dm @@ -1 +1 @@ -// TODO \ No newline at end of file +// TODO diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_empulse.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_empulse.dm index 0ffdd02fcbc..70b786d12ed 100644 --- a/code/modules/ninja/suit/n_suit_verbs/ninja_empulse.dm +++ b/code/modules/ninja/suit/n_suit_verbs/ninja_empulse.dm @@ -1 +1 @@ -// TODO \ No newline at end of file +// TODO diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_net.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_net.dm index 0ffdd02fcbc..70b786d12ed 100644 --- a/code/modules/ninja/suit/n_suit_verbs/ninja_net.dm +++ b/code/modules/ninja/suit/n_suit_verbs/ninja_net.dm @@ -1 +1 @@ -// TODO \ No newline at end of file +// TODO diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_smoke.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_smoke.dm index 0ffdd02fcbc..70b786d12ed 100644 --- a/code/modules/ninja/suit/n_suit_verbs/ninja_smoke.dm +++ b/code/modules/ninja/suit/n_suit_verbs/ninja_smoke.dm @@ -1 +1 @@ -// TODO \ No newline at end of file +// TODO diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_stars.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_stars.dm index c7ca781c04e..54a7f4e4206 100644 --- a/code/modules/ninja/suit/n_suit_verbs/ninja_stars.dm +++ b/code/modules/ninja/suit/n_suit_verbs/ninja_stars.dm @@ -20,4 +20,4 @@ /obj/item/gun/energy/shuriken/proc/throw() qdel(src) - return \ No newline at end of file + return diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_stealth.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_stealth.dm index 0ffdd02fcbc..70b786d12ed 100644 --- a/code/modules/ninja/suit/n_suit_verbs/ninja_stealth.dm +++ b/code/modules/ninja/suit/n_suit_verbs/ninja_stealth.dm @@ -1 +1 @@ -// TODO \ No newline at end of file +// TODO diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_teleporting.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_teleporting.dm index 0ffdd02fcbc..70b786d12ed 100644 --- a/code/modules/ninja/suit/n_suit_verbs/ninja_teleporting.dm +++ b/code/modules/ninja/suit/n_suit_verbs/ninja_teleporting.dm @@ -1 +1 @@ -// TODO \ No newline at end of file +// TODO diff --git a/code/modules/ninja/suit/suit_process.dm b/code/modules/ninja/suit/suit_process.dm index 0ffdd02fcbc..70b786d12ed 100644 --- a/code/modules/ninja/suit/suit_process.dm +++ b/code/modules/ninja/suit/suit_process.dm @@ -1 +1 @@ -// TODO \ No newline at end of file +// TODO diff --git a/code/modules/paperwork/contract.dm b/code/modules/paperwork/contract.dm index c32e1fc8a88..25e72610237 100644 --- a/code/modules/paperwork/contract.dm +++ b/code/modules/paperwork/contract.dm @@ -329,4 +329,4 @@ if(!istype(user) || !user.mind) return -1 user.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/summon_friend(null)) - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/paperwork/faxmachine.dm b/code/modules/paperwork/faxmachine.dm index 176d2abc85c..2f3088c5484 100644 --- a/code/modules/paperwork/faxmachine.dm +++ b/code/modules/paperwork/faxmachine.dm @@ -15,7 +15,7 @@ var/global/list/fax_blacklist = list() var/syndie_restricted = FALSE //is it a syndicate base fax restricted from contacting NT assets? var/long_range_enabled = 0 // Can we send messages off the station? - req_one_access = list(access_lawyer, access_heads, access_armory) + req_one_access = list(ACCESS_LAWYER, ACCESS_HEADS, ACCESS_ARMORY) use_power = IDLE_POWER_USE idle_power_usage = 30 @@ -50,7 +50,7 @@ var/global/list/fax_blacklist = list() name = "syndicate long range fax machine" emagged = TRUE syndie_restricted = TRUE - req_one_access = list(access_syndicate) + req_one_access = list(ACCESS_SYNDICATE) //No point setting fax network, being emagged overrides that anyway. /obj/machinery/photocopier/faxmachine/longrange/syndie/update_network() @@ -344,4 +344,4 @@ var/global/list/fax_blacklist = list() if(scan) scan.forceMove(get_turf(src)) var/mob/living/simple_animal/hostile/mimic/copy/M = new(loc, src, null, 1) // it will delete src on creation and override any machine checks - M.name = "angry fax machine" \ No newline at end of file + M.name = "angry fax machine" diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm index a88565d3818..46376c1828d 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/paper.dm @@ -80,7 +80,7 @@ else data = "
        [infolinks ? info_links : info]
        [stamps]" if(view) - var/datum/browser/popup = new(user, name, , paper_width, paper_height) + var/datum/browser/popup = new(user, "Paper[UID()]", , paper_width, paper_height) popup.stylesheets = list() popup.set_content(data) if(!stars) @@ -720,8 +720,9 @@ origin_tech = "combat=4;materials=4;engineering=4;biotech=4" /obj/item/paper/researchnotes/New() + ..() var/list/possible_techs = list("materials", "engineering", "plasmatech", "powerstorage", "bluespace", "biotech", "combat", "magnets", "programming", "syndicate") var/mytech = pick(possible_techs) var/mylevel = rand(7, 9) origin_tech = "[mytech]=[mylevel]" - name = "research notes - [mytech] [mylevel]" \ No newline at end of file + name = "research notes - [mytech] [mylevel]" diff --git a/code/modules/paperwork/paper_bundle.dm b/code/modules/paperwork/paper_bundle.dm index ecc26ba0cff..06bd0ec25c6 100644 --- a/code/modules/paperwork/paper_bundle.dm +++ b/code/modules/paperwork/paper_bundle.dm @@ -63,13 +63,13 @@ qdel(W) else if(istype(W, /obj/item/pen) || istype(W, /obj/item/toy/crayon)) - usr << browse("", "window=[name]") //Closes the dialog + usr << browse("", "window=PaperBundle[UID()]") //Closes the dialog P = src[page] P.attackby(W, user, params) update_icon() - if(winget(usr, "[name]", "is-visible") == "true") // NOT MY FAULT IT IS A BUILT IN PROC PLEASE DO NOT HIT ME + if(winget(usr, "PaperBundle[UID()]", "is-visible") == "true") // NOT MY FAULT IT IS A BUILT IN PROC PLEASE DO NOT HIT ME attack_self(usr) //Update the browsed page. add_fingerprint(usr) return @@ -125,7 +125,7 @@ if(istype(src[page], /obj/item/paper)) var/obj/item/paper/P = W dat += P.show_content(usr, view = 0) - usr << browse(dat, "window=[name]") + usr << browse(dat, "window=PaperBundle[UID()]") else if(istype(src[page], /obj/item/photo)) var/obj/item/photo/P = W usr << browse_rsc(P.img, "tmp_photo.png") @@ -133,7 +133,7 @@ + "" \ + "

        Written on the back:
        [P.scribble]" : ""]"\ - + "", "window=[name]") + + "", "window=PaperBundle[UID()]") /obj/item/paper_bundle/attack_self(mob/user as mob) src.show_content(user) diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm index e7be4698fcf..0954127b00a 100644 --- a/code/modules/paperwork/photocopier.dm +++ b/code/modules/paperwork/photocopier.dm @@ -167,10 +167,6 @@ updateUsrDialog() else to_chat(user, "This cartridge is not yet ready for replacement! Use up the rest of the toner.") - else if(istype(O, /obj/item/wrench)) - playsound(loc, O.usesound, 50, 1) - anchored = !anchored - to_chat(user, "You [anchored ? "wrench" : "unwrench"] \the [src].") else if(istype(O, /obj/item/grab)) //For ass-copying. var/obj/item/grab/G = O if(ismob(G.affecting) && G.affecting != ass) @@ -185,6 +181,10 @@ else return ..() +/obj/machinery/photocopier/wrench_act(mob/user, obj/item/I) + . = TRUE + default_unfasten_wrench(user, I) + /obj/machinery/photocopier/proc/copy(var/obj/item/paper/copy) var/obj/item/paper/c = new /obj/item/paper (loc) c.info = copy.info diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm index 11beb1a7cb6..159b224e384 100644 --- a/code/modules/paperwork/photography.dm +++ b/code/modules/paperwork/photography.dm @@ -1,571 +1,580 @@ -/* Photography! - * Contains: - * Camera - * Camera Film - * Photos - * Photo Albums - */ - -/******* -* film * -*******/ -/obj/item/camera_film - name = "film cartridge" - icon = 'icons/obj/items.dmi' - desc = "A camera film cartridge. Insert it into a camera to reload it." - icon_state = "film" - item_state = "electropack" - w_class = WEIGHT_CLASS_TINY - resistance_flags = FLAMMABLE - - -/******** -* photo * -********/ -/obj/item/photo - name = "photo" - icon = 'icons/obj/items.dmi' - icon_state = "photo" - item_state = "paper" - w_class = WEIGHT_CLASS_SMALL - resistance_flags = FLAMMABLE - max_integrity = 50 - var/blueprints = 0 // Does this have the blueprints? - var/icon/img //Big photo image - var/scribble //Scribble on the back. - var/icon/tiny - var/photo_size = 3 - -/obj/item/photo/attack_self(mob/user as mob) - user.examinate(src) - -/obj/item/photo/attackby(obj/item/P as obj, mob/user as mob, params) - if(istype(P, /obj/item/pen) || istype(P, /obj/item/toy/crayon)) - var/txt = sanitize(input(user, "What would you like to write on the back?", "Photo Writing", null) as text) - txt = copytext(txt, 1, 128) - if(loc == user && user.stat == 0) - scribble = txt - else if(istype(P, /obj/item/lighter)) - burnphoto(P, user) - ..() - -/obj/item/photo/proc/burnphoto(obj/item/lighter/P, mob/user) - var/class = "" - - if(P.lit && !user.restrained()) - if(istype(P, /obj/item/lighter/zippo)) - class = "" - - user.visible_message("[class][user] holds \the [P] up to \the [src], it looks like [user.p_theyre()] trying to burn it!", \ - "[class]You hold [P] up to [src], burning it slowly.") - - spawn(20) - if(get_dist(src, user) < 2 && user.get_active_hand() == P && P.lit) - user.visible_message("[class][user] burns right through \the [src], turning it to ash. It flutters through the air before settling on the floor in a heap.", \ - "[class]You burn right through \the [src], turning it to ash. It flutters through the air before settling on the floor in a heap.") - - if(user.is_in_inactive_hand(src)) - user.unEquip(src) - - new /obj/effect/decal/cleanable/ash(get_turf(src)) - qdel(src) - - else - to_chat(user, "You must hold \the [P] steady to burn \the [src].") - -/obj/item/photo/examine(mob/user) - . = ..() - if(in_range(user, src) || isobserver(user)) - show(user) - else - . += "It is too far away." - -/obj/item/photo/proc/show(mob/user as mob) - usr << browse_rsc(img, "tmp_photo.png") - usr << browse("[name]" \ - + "" \ - + "" \ - + "[scribble ? "
        Written on the back:
        [scribble]" : ""]"\ - + "", "window=book;size=[64*photo_size]x[scribble ? 400 : 64*photo_size]") - onclose(usr, "[name]") - return - -/obj/item/photo/verb/rename() - set name = "Rename photo" - set category = "Object" - set src in usr - - var/n_name = sanitize(copytext(input(usr, "What would you like to label the photo?", "Photo Labelling", name) as text, 1, MAX_MESSAGE_LEN)) - //loc.loc check is for making possible renaming photos in clipboards - if(( (loc == usr || (loc.loc && loc.loc == usr)) && usr.stat == 0)) - name = "[(n_name ? text("[n_name]") : "photo")]" - add_fingerprint(usr) - return - - -/************** -* photo album * -**************/ -/obj/item/storage/photo_album - name = "Photo album" - icon = 'icons/obj/items.dmi' - icon_state = "album" - item_state = "briefcase" - can_hold = list(/obj/item/photo) - resistance_flags = FLAMMABLE - -/obj/item/storage/photo_album/MouseDrop(obj/over_object as obj) - - if((istype(usr, /mob/living/carbon/human))) - var/mob/M = usr - if(!( istype(over_object, /obj/screen) )) - return ..() - playsound(loc, "rustle", 50, 1, -5) - if((!( M.restrained() ) && !( M.stat ) && M.back == src)) - switch(over_object.name) - if("r_hand") - M.unEquip(src) - M.put_in_r_hand(src) - if("l_hand") - M.unEquip(src) - M.put_in_l_hand(src) - add_fingerprint(usr) - return - if(over_object == usr && in_range(src, usr) || usr.contents.Find(src)) - if(usr.s_active) - usr.s_active.close(usr) - show_to(usr) - return - return - -/********* -* camera * -*********/ -/obj/item/camera - name = "camera" - icon = 'icons/obj/items.dmi' - desc = "A polaroid camera. 10 photos left." - icon_state = "camera" - item_state = "electropack" - w_class = WEIGHT_CLASS_SMALL - slot_flags = SLOT_BELT - var/list/matter = list("metal" = 2000) - var/pictures_max = 10 - var/pictures_left = 10 - var/on = 1 - var/blueprints = 0 - var/icon_on = "camera" - var/icon_off = "camera_off" - var/size = 3 - var/see_ghosts = 0 //for the spoop of it - - -/obj/item/camera/spooky/CheckParts(list/parts_list) - ..() - var/obj/item/camera/C = locate(/obj/item/camera) in contents - if(C) - pictures_max = C.pictures_max - pictures_left = C.pictures_left - visible_message("[C] has been imbued with godlike power!") - qdel(C) - - -var/list/SpookyGhosts = list("ghost","shade","shade2","ghost-narsie","horror","shadow","ghostian2") - -/obj/item/camera/spooky - name = "camera obscura" - desc = "A polaroid camera, some say it can see ghosts!" - see_ghosts = 1 - -/obj/item/camera/verb/change_size() - set name = "Set Photo Focus" - set category = "Object" - var/nsize = input("Photo Size","Pick a size of resulting photo.") as null|anything in list(1,3,5,7) - if(nsize) - size = nsize - to_chat(usr, "Camera will now take [size]x[size] photos.") - -/obj/item/camera/attack(mob/living/carbon/human/M as mob, mob/user as mob) - return - -/obj/item/camera/attack_self(mob/user as mob) - on = !on - if(on) - src.icon_state = icon_on - else - src.icon_state = icon_off - to_chat(user, "You switch the camera [on ? "on" : "off"].") - return - -/obj/item/camera/attackby(obj/item/I as obj, mob/user as mob, params) - if(istype(I, /obj/item/camera_film)) - if(pictures_left) - to_chat(user, "[src] still has some film in it!") - return - to_chat(user, "You insert [I] into [src].") - user.drop_item() - qdel(I) - pictures_left = pictures_max - return - ..() - - -/obj/item/camera/proc/get_icon(list/turfs, turf/center, mob/user) - - //Bigger icon base to capture those icons that were shifted to the next tile - //i.e. pretty much all wall-mounted machinery - var/icon/res = icon('icons/effects/96x96.dmi', "") - res.Scale(size*32, size*32) - // Initialize the photograph to black. - res.Blend("#000", ICON_OVERLAY) - - var/atoms[] = list() - for(var/turf/the_turf in turfs) - // Add ourselves to the list of stuff to draw - atoms.Add(the_turf); - // As well as anything that isn't invisible. - for(var/atom/A in the_turf) - if(A.invisibility) - if(see_ghosts && istype(A,/mob/dead/observer)) - var/mob/dead/observer/O = A - if(O.following) - continue - if(user.mind && !(user.mind.assigned_role == "Chaplain")) - atoms.Add(image('icons/mob/mob.dmi', O.loc, pick(SpookyGhosts), 4, SOUTH)) - else - atoms.Add(image('icons/mob/mob.dmi', O.loc, "ghost", 4, SOUTH)) - else//its not a ghost - continue - else//not invisable, not a spookyghost add it. - var/disguised = null - if(user.viewing_alternate_appearances && user.viewing_alternate_appearances.len && ishuman(A) && A.alternate_appearances && A.alternate_appearances.len) //This whole thing and the stuff below just checks if the atom is a Solid Snake cosplayer. - for(var/datum/alternate_appearance/alt_appearance in user.viewing_alternate_appearances) - if(alt_appearance.owner == A) //If it turns out they are, don't blow their cover. That'd be rude. - atoms.Add(image(alt_appearance.img, A.loc, layer = 4, dir = A.dir)) //Render their disguise. - atoms.Remove(A) //Don't blow their cover. - disguised = 1 - continue - if(!disguised) //If they aren't, treat them normally. - atoms.Add(A) - - - // Sort the atoms into their layers - var/list/sorted = sort_atoms_by_layer(atoms) - var/center_offset = (size-1)/2 * 32 + 1 - for(var/i; i <= sorted.len; i++) - var/atom/A = sorted[i] - if(A) - var/icon/img = getFlatIcon(A)//build_composite_icon(A) - if(istype(A, /obj/item/areaeditor/blueprints)) - blueprints = 1 - - // If what we got back is actually a picture, draw it. - if(istype(img, /icon)) - // Check if we're looking at a mob that's lying down - if(istype(A, /mob/living) && A:lying) - // If they are, apply that effect to their picture. - img.BecomeLying() - // Calculate where we are relative to the center of the photo - var/xoff = (A.x - center.x) * 32 + center_offset - var/yoff = (A.y - center.y) * 32 + center_offset - if(istype(A,/atom/movable)) - xoff+=A:step_x - yoff+=A:step_y - res.Blend(img, blendMode2iconMode(A.blend_mode), A.pixel_x + xoff, A.pixel_y + yoff) - - // Lastly, render any contained effects on top. - for(var/turf/the_turf in turfs) - // Calculate where we are relative to the center of the photo - var/xoff = (the_turf.x - center.x) * 32 + center_offset - var/yoff = (the_turf.y - center.y) * 32 + center_offset - res.Blend(getFlatIcon(the_turf.loc), blendMode2iconMode(the_turf.blend_mode),xoff,yoff) - return res - - -/obj/item/camera/proc/get_mobs(turf/the_turf as turf) - var/mob_detail - for(var/mob/M in the_turf) - if(M.invisibility) - if(see_ghosts && istype(M,/mob/dead/observer)) - var/mob/dead/observer/O = M - if(O.following) - continue - if(!mob_detail) - mob_detail = "You can see a g-g-g-g-ghooooost! " - else - mob_detail += "You can also see a g-g-g-g-ghooooost!" - else - continue - - var/holding = null - - if(istype(M, /mob/living/carbon)) - var/mob/living/carbon/A = M - if(A.l_hand || A.r_hand) - if(A.l_hand) holding = "They are holding \a [A.l_hand]" - if(A.r_hand) - if(holding) - holding += " and \a [A.r_hand]" - else - holding = "They are holding \a [A.r_hand]" - - if(!mob_detail) - mob_detail = "You can see [A] on the photo[A:health < 75 ? " - [A] looks hurt":""].[holding ? " [holding]":"."]. " - else - mob_detail += "You can also see [A] on the photo[A:health < 75 ? " - [A] looks hurt":""].[holding ? " [holding]":"."]." - return mob_detail - -/obj/item/camera/afterattack(atom/target as mob|obj|turf|area, mob/user as mob, flag) - if(!on || !pictures_left || ismob(target.loc)) return - captureimage(target, user, flag) - - playsound(loc, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 75, 1, -3) - - pictures_left-- - desc = "A polaroid camera. It has [pictures_left] photos left." - to_chat(user, "[pictures_left] photos left.") - icon_state = icon_off - on = 0 - if(istype(src,/obj/item/camera/spooky)) - if(user.mind && user.mind.assigned_role == "Chaplain" && see_ghosts) - if(prob(24)) - handle_haunt(user) - spawn(64) - icon_state = icon_on - on = 1 - -/obj/item/camera/proc/can_capture_turf(turf/T, mob/user) - var/viewer = user - if(user.client) //To make shooting through security cameras possible - viewer = user.client.eye - var/can_see = (T in view(viewer)) //No x-ray vision cameras. - return can_see - -/obj/item/camera/proc/captureimage(atom/target, mob/user, flag) - var/x_c = target.x - (size-1)/2 - var/y_c = target.y + (size-1)/2 - var/z_c = target.z - var/list/turfs = list() - var/mobs = "" - for(var/i = 1; i <= size; i++) - for(var/j = 1; j <= size; j++) - var/turf/T = locate(x_c, y_c, z_c) - if(can_capture_turf(T, user)) - turfs.Add(T) - mobs += get_mobs(T) - x_c++ - y_c-- - x_c = x_c - size - - var/datum/picture/P = createpicture(target, user, turfs, mobs, flag, blueprints) - printpicture(user, P) - -/obj/item/camera/proc/createpicture(atom/target, mob/user, list/turfs, mobs, flag) - var/icon/photoimage = get_icon(turfs, target, user) - - var/icon/small_img = icon(photoimage) - var/icon/tiny_img = icon(photoimage) - var/icon/ic = icon('icons/obj/items.dmi',"photo") - var/icon/pc = icon('icons/obj/bureaucracy.dmi', "photo") - small_img.Scale(8, 8) - tiny_img.Scale(4, 4) - ic.Blend(small_img,ICON_OVERLAY, 10, 13) - pc.Blend(tiny_img,ICON_OVERLAY, 12, 19) - - var/datum/picture/P = new() - if(istype(src,/obj/item/camera/digital)) - P.fields["name"] = input(user,"Name photo:","photo") - P.name = P.fields["name"]//So the name is displayed on the print/delete list. - else - P.fields["name"] = "photo" - P.fields["author"] = user - P.fields["icon"] = ic - P.fields["tiny"] = pc - P.fields["img"] = photoimage - P.fields["desc"] = mobs - P.fields["pixel_x"] = rand(-10, 10) - P.fields["pixel_y"] = rand(-10, 10) - P.fields["size"] = size - - return P - -/obj/item/camera/proc/printpicture(mob/user, var/datum/picture/P) - var/obj/item/photo/Photo = new/obj/item/photo() - Photo.loc = user.loc - if(!user.get_inactive_hand()) - user.put_in_inactive_hand(Photo) - - if(blueprints) - Photo.blueprints = 1 - blueprints = 0 - Photo.construct(P) - -/obj/item/photo/proc/construct(var/datum/picture/P) - name = P.fields["name"] - icon = P.fields["icon"] - tiny = P.fields["tiny"] - img = P.fields["img"] - desc = P.fields["desc"] - pixel_x = P.fields["pixel_x"] - pixel_y = P.fields["pixel_y"] - photo_size = P.fields["size"] - -/obj/item/photo/proc/copy() - var/obj/item/photo/p = new/obj/item/photo() - - p.icon = icon(icon, icon_state) - p.img = icon(img) - p.tiny = icon(tiny) - p.name = name - p.desc = desc - p.scribble = scribble - p.blueprints = blueprints - - return p - -/***************** -* digital camera * -******************/ -/obj/item/camera/digital - name = "digital camera" - desc = "A digital camera. A small screen shows there is space for 10 photos left." - var/list/datum/picture/saved_pictures = list() - pictures_left = 30 - var/max_storage = 10 - -/obj/item/camera/digital/afterattack(atom/target as mob|obj|turf|area, mob/user as mob, flag) - if(!on || !pictures_left || ismob(target.loc)) return - captureimage(target, user, flag) - - playsound(loc, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 75, 1, -3) - - desc = "A digital camera. A small screen shows that there are currently [saved_pictures.len] pictures stored." - icon_state = icon_off - on = 0 - spawn(64) - icon_state = icon_on - on = 1 - -/obj/item/camera/digital/captureimage(atom/target, mob/user, flag) - if(saved_pictures.len >= max_storage) - to_chat(user, "Maximum photo storage capacity reached.") - return - to_chat(user, "Picture saved.") - var/x_c = target.x - (size-1)/2 - var/y_c = target.y + (size-1)/2 - var/z_c = target.z - var/list/turfs = list() - var/mobs = "" - for(var/i = 1; i <= size; i++) - for(var/j = 1; j <= size; j++) - var/turf/T = locate(x_c, y_c, z_c) - if(can_capture_turf(T, user)) - turfs.Add(T) - mobs += get_mobs(T) - x_c++ - y_c-- - x_c = x_c - size - - var/datum/picture/P = createpicture(target, user, turfs, mobs, flag) - saved_pictures += P - -/obj/item/camera/digital/verb/print_picture() - set name = "Print picture" - set category = "Object" - set src in usr - - if(saved_pictures.len == 0) - to_chat(usr, "No images saved.") - return - if(pictures_left == 0) - to_chat(usr, "There is no film left to print.") - return - - var/datum/picture/P = null - P = input("Select image to print:",P) as null|anything in saved_pictures - if(P) - printpicture(usr,P) - pictures_left -- - -/obj/item/camera/digital/verb/delete_picture() - set name = "Delete picture" - set category = "Object" - set src in usr - - if(saved_pictures.len == 0) - to_chat(usr, "No images saved") - return - var/datum/picture/P = null - P = input("Select image to delete:",P) as null|anything in saved_pictures - if(P) - saved_pictures -= P - -/************** -*video camera * -***************/ - -/obj/item/videocam - name = "video camera" - icon = 'icons/obj/items.dmi' - desc = "video camera that can send live feed to the entertainment network." - icon_state = "videocam" - item_state = "videocam" - w_class = WEIGHT_CLASS_SMALL - slot_flags = SLOT_BELT - materials = list(MAT_METAL=2000) - var/on = 0 - var/obj/machinery/camera/camera - var/icon_on = "videocam_on" - var/icon_off = "videocam" - var/canhear_range = 7 - -/obj/item/videocam/attack_self(mob/user) - on = !on - if(camera) - if(on==0) - src.icon_state = icon_off - camera.c_tag = null - camera.network = list() - else - src.icon_state = icon_on - camera.network = list("news") - camera.c_tag = user.name - else - - src.icon_state = icon_on - camera = new /obj/machinery/camera(src) - camera.network = list("news") - cameranet.removeCamera(camera) - camera.c_tag = user.name - to_chat(user, "You switch the camera [on ? "on" : "off"].") - -/obj/item/videocam/examine(mob/user) - . = ..() - if(in_range(user, src)) - . += "This video camera can send live feeds to the entertainment network. It's [camera ? "" : "in"]active." - -/obj/item/videocam/hear_talk(mob/M as mob, list/message_pieces) - var/msg = multilingual_to_message(message_pieces) - if(camera && on) - if(get_dist(src, M) <= canhear_range) - talk_into(M, msg) - for(var/obj/machinery/computer/security/telescreen/T in GLOB.machines) - if(T.watchers[M] == camera) - T.audible_message("(Newscaster) [M] says, '[msg]'", hearing_distance = 2) - -/obj/item/videocam/hear_message(mob/M as mob, msg) - if(camera && on) - for(var/obj/machinery/computer/security/telescreen/T in GLOB.machines) - if(T.watchers[M] == camera) - T.audible_message("(Newscaster) [M] [msg]", hearing_distance = 2) - - -///hauntings, like hallucinations but more spooky - -/obj/item/camera/proc/handle_haunt(mob/user as mob) - var/list/creepyasssounds = list('sound/effects/ghost.ogg', 'sound/effects/ghost2.ogg', 'sound/effects/heartbeat.ogg', 'sound/effects/screech.ogg',\ - 'sound/hallucinations/behind_you1.ogg', 'sound/hallucinations/behind_you2.ogg', 'sound/hallucinations/far_noise.ogg', 'sound/hallucinations/growl1.ogg', 'sound/hallucinations/growl2.ogg',\ - 'sound/hallucinations/growl3.ogg', 'sound/hallucinations/im_here1.ogg', 'sound/hallucinations/im_here2.ogg', 'sound/hallucinations/i_see_you1.ogg', 'sound/hallucinations/i_see_you2.ogg',\ - 'sound/hallucinations/look_up1.ogg', 'sound/hallucinations/look_up2.ogg', 'sound/hallucinations/over_here1.ogg', 'sound/hallucinations/over_here2.ogg', 'sound/hallucinations/over_here3.ogg',\ - 'sound/hallucinations/turn_around1.ogg', 'sound/hallucinations/turn_around2.ogg', 'sound/hallucinations/veryfar_noise.ogg', 'sound/hallucinations/wail.ogg') - user << pick(creepyasssounds) +/* Photography! + * Contains: + * Camera + * Camera Film + * Photos + * Photo Albums + */ + +/******* +* film * +*******/ +/obj/item/camera_film + name = "film cartridge" + icon = 'icons/obj/items.dmi' + desc = "A camera film cartridge. Insert it into a camera to reload it." + icon_state = "film" + item_state = "electropack" + w_class = WEIGHT_CLASS_TINY + resistance_flags = FLAMMABLE + + +/******** +* photo * +********/ +/obj/item/photo + name = "photo" + icon = 'icons/obj/items.dmi' + icon_state = "photo" + item_state = "paper" + w_class = WEIGHT_CLASS_SMALL + resistance_flags = FLAMMABLE + max_integrity = 50 + var/blueprints = 0 // Does this have the blueprints? + var/icon/img //Big photo image + var/scribble //Scribble on the back. + var/icon/tiny + var/photo_size = 3 + +/obj/item/photo/attack_self(mob/user as mob) + user.examinate(src) + +/obj/item/photo/attackby(obj/item/P as obj, mob/user as mob, params) + if(istype(P, /obj/item/pen) || istype(P, /obj/item/toy/crayon)) + var/txt = sanitize(input(user, "What would you like to write on the back?", "Photo Writing", null) as text) + txt = copytext(txt, 1, 128) + if(loc == user && user.stat == 0) + scribble = txt + else if(istype(P, /obj/item/lighter)) + burnphoto(P, user) + ..() + +/obj/item/photo/proc/burnphoto(obj/item/lighter/P, mob/user) + var/class = "" + + if(P.lit && !user.restrained()) + if(istype(P, /obj/item/lighter/zippo)) + class = "" + + user.visible_message("[class][user] holds \the [P] up to \the [src], it looks like [user.p_theyre()] trying to burn it!", \ + "[class]You hold [P] up to [src], burning it slowly.") + + spawn(20) + if(get_dist(src, user) < 2 && user.get_active_hand() == P && P.lit) + user.visible_message("[class][user] burns right through \the [src], turning it to ash. It flutters through the air before settling on the floor in a heap.", \ + "[class]You burn right through \the [src], turning it to ash. It flutters through the air before settling on the floor in a heap.") + + if(user.is_in_inactive_hand(src)) + user.unEquip(src) + + new /obj/effect/decal/cleanable/ash(get_turf(src)) + qdel(src) + + else + to_chat(user, "You must hold \the [P] steady to burn \the [src].") + +/obj/item/photo/examine(mob/user) + . = ..() + if(in_range(user, src) || isobserver(user)) + show(user) + else + . += "It is too far away." + +/obj/item/photo/proc/show(mob/user as mob) + var/icon/img_shown = new/icon(img) + var/colormatrix = user.get_screen_colour() + // Apply colorblindness effects, if any. + if(islist(colormatrix)) + img_shown.MapColors( + colormatrix[1], colormatrix[2], colormatrix[3], + colormatrix[4], colormatrix[5], colormatrix[6], + colormatrix[7], colormatrix[8], colormatrix[9], + ) + usr << browse_rsc(img_shown, "tmp_photo.png") + usr << browse("[name]" \ + + "" \ + + "" \ + + "[scribble ? "
        Written on the back:
        [scribble]" : ""]"\ + + "", "window=Photo[UID()];size=[64*photo_size]x[scribble ? 400 : 64*photo_size]") + onclose(usr, "Photo[UID()]") + return + +/obj/item/photo/verb/rename() + set name = "Rename photo" + set category = "Object" + set src in usr + + var/n_name = sanitize(copytext(input(usr, "What would you like to label the photo?", "Photo Labelling", name) as text, 1, MAX_MESSAGE_LEN)) + //loc.loc check is for making possible renaming photos in clipboards + if(( (loc == usr || (loc.loc && loc.loc == usr)) && usr.stat == 0)) + name = "[(n_name ? text("[n_name]") : "photo")]" + add_fingerprint(usr) + return + + +/************** +* photo album * +**************/ +/obj/item/storage/photo_album + name = "Photo album" + icon = 'icons/obj/items.dmi' + icon_state = "album" + item_state = "briefcase" + can_hold = list(/obj/item/photo) + resistance_flags = FLAMMABLE + +/obj/item/storage/photo_album/MouseDrop(obj/over_object as obj) + + if((istype(usr, /mob/living/carbon/human))) + var/mob/M = usr + if(!( istype(over_object, /obj/screen) )) + return ..() + playsound(loc, "rustle", 50, 1, -5) + if((!( M.restrained() ) && !( M.stat ) && M.back == src)) + switch(over_object.name) + if("r_hand") + M.unEquip(src) + M.put_in_r_hand(src) + if("l_hand") + M.unEquip(src) + M.put_in_l_hand(src) + add_fingerprint(usr) + return + if(over_object == usr && in_range(src, usr) || usr.contents.Find(src)) + if(usr.s_active) + usr.s_active.close(usr) + show_to(usr) + return + return + +/********* +* camera * +*********/ +/obj/item/camera + name = "camera" + icon = 'icons/obj/items.dmi' + desc = "A polaroid camera. 10 photos left." + icon_state = "camera" + item_state = "electropack" + w_class = WEIGHT_CLASS_SMALL + slot_flags = SLOT_BELT + var/list/matter = list("metal" = 2000) + var/pictures_max = 10 + var/pictures_left = 10 + var/on = 1 + var/blueprints = 0 + var/icon_on = "camera" + var/icon_off = "camera_off" + var/size = 3 + var/see_ghosts = 0 //for the spoop of it + + +/obj/item/camera/spooky/CheckParts(list/parts_list) + ..() + var/obj/item/camera/C = locate(/obj/item/camera) in contents + if(C) + pictures_max = C.pictures_max + pictures_left = C.pictures_left + visible_message("[C] has been imbued with godlike power!") + qdel(C) + + +var/list/SpookyGhosts = list("ghost","shade","shade2","ghost-narsie","horror","shadow","ghostian2") + +/obj/item/camera/spooky + name = "camera obscura" + desc = "A polaroid camera, some say it can see ghosts!" + see_ghosts = 1 + +/obj/item/camera/verb/change_size() + set name = "Set Photo Focus" + set category = "Object" + var/nsize = input("Photo Size","Pick a size of resulting photo.") as null|anything in list(1,3,5,7) + if(nsize) + size = nsize + to_chat(usr, "Camera will now take [size]x[size] photos.") + +/obj/item/camera/attack(mob/living/carbon/human/M as mob, mob/user as mob) + return + +/obj/item/camera/attack_self(mob/user as mob) + on = !on + if(on) + src.icon_state = icon_on + else + src.icon_state = icon_off + to_chat(user, "You switch the camera [on ? "on" : "off"].") + return + +/obj/item/camera/attackby(obj/item/I as obj, mob/user as mob, params) + if(istype(I, /obj/item/camera_film)) + if(pictures_left) + to_chat(user, "[src] still has some film in it!") + return + to_chat(user, "You insert [I] into [src].") + user.drop_item() + qdel(I) + pictures_left = pictures_max + return + ..() + + +/obj/item/camera/proc/get_icon(list/turfs, turf/center, mob/user) + + //Bigger icon base to capture those icons that were shifted to the next tile + //i.e. pretty much all wall-mounted machinery + var/icon/res = icon('icons/effects/96x96.dmi', "") + res.Scale(size*32, size*32) + // Initialize the photograph to black. + res.Blend("#000", ICON_OVERLAY) + + var/atoms[] = list() + for(var/turf/the_turf in turfs) + // Add ourselves to the list of stuff to draw + atoms.Add(the_turf); + // As well as anything that isn't invisible. + for(var/atom/A in the_turf) + if(A.invisibility) + if(see_ghosts && istype(A,/mob/dead/observer)) + var/mob/dead/observer/O = A + if(O.following) + continue + if(user.mind && !(user.mind.assigned_role == "Chaplain")) + atoms.Add(image('icons/mob/mob.dmi', O.loc, pick(SpookyGhosts), 4, SOUTH)) + else + atoms.Add(image('icons/mob/mob.dmi', O.loc, "ghost", 4, SOUTH)) + else//its not a ghost + continue + else//not invisable, not a spookyghost add it. + var/disguised = null + if(user.viewing_alternate_appearances && user.viewing_alternate_appearances.len && ishuman(A) && A.alternate_appearances && A.alternate_appearances.len) //This whole thing and the stuff below just checks if the atom is a Solid Snake cosplayer. + for(var/datum/alternate_appearance/alt_appearance in user.viewing_alternate_appearances) + if(alt_appearance.owner == A) //If it turns out they are, don't blow their cover. That'd be rude. + atoms.Add(image(alt_appearance.img, A.loc, layer = 4, dir = A.dir)) //Render their disguise. + atoms.Remove(A) //Don't blow their cover. + disguised = 1 + continue + if(!disguised) //If they aren't, treat them normally. + atoms.Add(A) + + + // Sort the atoms into their layers + var/list/sorted = sort_atoms_by_layer(atoms) + var/center_offset = (size-1)/2 * 32 + 1 + for(var/i; i <= sorted.len; i++) + var/atom/A = sorted[i] + if(A) + var/icon/img = getFlatIcon(A)//build_composite_icon(A) + if(istype(A, /obj/item/areaeditor/blueprints)) + blueprints = 1 + + // If what we got back is actually a picture, draw it. + if(istype(img, /icon)) + // Check if we're looking at a mob that's lying down + if(istype(A, /mob/living) && A:lying) + // If they are, apply that effect to their picture. + img.BecomeLying() + // Calculate where we are relative to the center of the photo + var/xoff = (A.x - center.x) * 32 + center_offset + var/yoff = (A.y - center.y) * 32 + center_offset + if(istype(A,/atom/movable)) + xoff+=A:step_x + yoff+=A:step_y + res.Blend(img, blendMode2iconMode(A.blend_mode), A.pixel_x + xoff, A.pixel_y + yoff) + + // Lastly, render any contained effects on top. + for(var/turf/the_turf in turfs) + // Calculate where we are relative to the center of the photo + var/xoff = (the_turf.x - center.x) * 32 + center_offset + var/yoff = (the_turf.y - center.y) * 32 + center_offset + res.Blend(getFlatIcon(the_turf.loc), blendMode2iconMode(the_turf.blend_mode),xoff,yoff) + return res + + +/obj/item/camera/proc/get_mobs(turf/the_turf as turf) + var/mob_detail + for(var/mob/M in the_turf) + if(M.invisibility) + if(see_ghosts && istype(M,/mob/dead/observer)) + var/mob/dead/observer/O = M + if(O.following) + continue + if(!mob_detail) + mob_detail = "You can see a g-g-g-g-ghooooost! " + else + mob_detail += "You can also see a g-g-g-g-ghooooost!" + else + continue + + var/holding = null + + if(istype(M, /mob/living/carbon)) + var/mob/living/carbon/A = M + if(A.l_hand || A.r_hand) + if(A.l_hand) holding = "They are holding \a [A.l_hand]" + if(A.r_hand) + if(holding) + holding += " and \a [A.r_hand]" + else + holding = "They are holding \a [A.r_hand]" + + if(!mob_detail) + mob_detail = "You can see [A] on the photo[A:health < 75 ? " - [A] looks hurt":""].[holding ? " [holding]":"."]. " + else + mob_detail += "You can also see [A] on the photo[A:health < 75 ? " - [A] looks hurt":""].[holding ? " [holding]":"."]." + return mob_detail + +/obj/item/camera/afterattack(atom/target as mob|obj|turf|area, mob/user as mob, flag) + if(!on || !pictures_left || ismob(target.loc)) return + captureimage(target, user, flag) + + playsound(loc, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 75, 1, -3) + + pictures_left-- + desc = "A polaroid camera. It has [pictures_left] photos left." + to_chat(user, "[pictures_left] photos left.") + icon_state = icon_off + on = 0 + if(istype(src,/obj/item/camera/spooky)) + if(user.mind && user.mind.assigned_role == "Chaplain" && see_ghosts) + if(prob(24)) + handle_haunt(user) + spawn(64) + icon_state = icon_on + on = 1 + +/obj/item/camera/proc/can_capture_turf(turf/T, mob/user) + var/viewer = user + if(user.client) //To make shooting through security cameras possible + viewer = user.client.eye + var/can_see = (T in view(viewer)) //No x-ray vision cameras. + return can_see + +/obj/item/camera/proc/captureimage(atom/target, mob/user, flag) + var/x_c = target.x - (size-1)/2 + var/y_c = target.y + (size-1)/2 + var/z_c = target.z + var/list/turfs = list() + var/mobs = "" + for(var/i = 1; i <= size; i++) + for(var/j = 1; j <= size; j++) + var/turf/T = locate(x_c, y_c, z_c) + if(can_capture_turf(T, user)) + turfs.Add(T) + mobs += get_mobs(T) + x_c++ + y_c-- + x_c = x_c - size + + var/datum/picture/P = createpicture(target, user, turfs, mobs, flag, blueprints) + printpicture(user, P) + +/obj/item/camera/proc/createpicture(atom/target, mob/user, list/turfs, mobs, flag) + var/icon/photoimage = get_icon(turfs, target, user) + + var/icon/small_img = icon(photoimage) + var/icon/tiny_img = icon(photoimage) + var/icon/ic = icon('icons/obj/items.dmi',"photo") + var/icon/pc = icon('icons/obj/bureaucracy.dmi', "photo") + small_img.Scale(8, 8) + tiny_img.Scale(4, 4) + ic.Blend(small_img,ICON_OVERLAY, 10, 13) + pc.Blend(tiny_img,ICON_OVERLAY, 12, 19) + + var/datum/picture/P = new() + if(istype(src,/obj/item/camera/digital)) + P.fields["name"] = input(user,"Name photo:","photo") + P.name = P.fields["name"]//So the name is displayed on the print/delete list. + else + P.fields["name"] = "photo" + P.fields["author"] = user + P.fields["icon"] = ic + P.fields["tiny"] = pc + P.fields["img"] = photoimage + P.fields["desc"] = mobs + P.fields["pixel_x"] = rand(-10, 10) + P.fields["pixel_y"] = rand(-10, 10) + P.fields["size"] = size + + return P + +/obj/item/camera/proc/printpicture(mob/user, var/datum/picture/P) + var/obj/item/photo/Photo = new/obj/item/photo() + Photo.loc = user.loc + if(!user.get_inactive_hand()) + user.put_in_inactive_hand(Photo) + + if(blueprints) + Photo.blueprints = 1 + blueprints = 0 + Photo.construct(P) + +/obj/item/photo/proc/construct(var/datum/picture/P) + name = P.fields["name"] + icon = P.fields["icon"] + tiny = P.fields["tiny"] + img = P.fields["img"] + desc = P.fields["desc"] + pixel_x = P.fields["pixel_x"] + pixel_y = P.fields["pixel_y"] + photo_size = P.fields["size"] + +/obj/item/photo/proc/copy() + var/obj/item/photo/p = new/obj/item/photo() + + p.icon = icon(icon, icon_state) + p.img = icon(img) + p.tiny = icon(tiny) + p.name = name + p.desc = desc + p.scribble = scribble + p.blueprints = blueprints + + return p + +/***************** +* digital camera * +******************/ +/obj/item/camera/digital + name = "digital camera" + desc = "A digital camera. A small screen shows there is space for 10 photos left." + var/list/datum/picture/saved_pictures = list() + pictures_left = 30 + var/max_storage = 10 + +/obj/item/camera/digital/afterattack(atom/target as mob|obj|turf|area, mob/user as mob, flag) + if(!on || !pictures_left || ismob(target.loc)) return + captureimage(target, user, flag) + + playsound(loc, pick('sound/items/polaroid1.ogg', 'sound/items/polaroid2.ogg'), 75, 1, -3) + + desc = "A digital camera. A small screen shows that there are currently [saved_pictures.len] pictures stored." + icon_state = icon_off + on = 0 + spawn(64) + icon_state = icon_on + on = 1 + +/obj/item/camera/digital/captureimage(atom/target, mob/user, flag) + if(saved_pictures.len >= max_storage) + to_chat(user, "Maximum photo storage capacity reached.") + return + to_chat(user, "Picture saved.") + var/x_c = target.x - (size-1)/2 + var/y_c = target.y + (size-1)/2 + var/z_c = target.z + var/list/turfs = list() + var/mobs = "" + for(var/i = 1; i <= size; i++) + for(var/j = 1; j <= size; j++) + var/turf/T = locate(x_c, y_c, z_c) + if(can_capture_turf(T, user)) + turfs.Add(T) + mobs += get_mobs(T) + x_c++ + y_c-- + x_c = x_c - size + + var/datum/picture/P = createpicture(target, user, turfs, mobs, flag) + saved_pictures += P + +/obj/item/camera/digital/verb/print_picture() + set name = "Print picture" + set category = "Object" + set src in usr + + if(saved_pictures.len == 0) + to_chat(usr, "No images saved.") + return + if(pictures_left == 0) + to_chat(usr, "There is no film left to print.") + return + + var/datum/picture/P = null + P = input("Select image to print:",P) as null|anything in saved_pictures + if(P) + printpicture(usr,P) + pictures_left -- + +/obj/item/camera/digital/verb/delete_picture() + set name = "Delete picture" + set category = "Object" + set src in usr + + if(saved_pictures.len == 0) + to_chat(usr, "No images saved") + return + var/datum/picture/P = null + P = input("Select image to delete:",P) as null|anything in saved_pictures + if(P) + saved_pictures -= P + +/************** +*video camera * +***************/ + +/obj/item/videocam + name = "video camera" + icon = 'icons/obj/items.dmi' + desc = "video camera that can send live feed to the entertainment network." + icon_state = "videocam" + item_state = "videocam" + w_class = WEIGHT_CLASS_SMALL + slot_flags = SLOT_BELT + materials = list(MAT_METAL=2000) + var/on = 0 + var/obj/machinery/camera/camera + var/icon_on = "videocam_on" + var/icon_off = "videocam" + var/canhear_range = 7 + +/obj/item/videocam/attack_self(mob/user) + on = !on + if(camera) + if(on==0) + src.icon_state = icon_off + camera.c_tag = null + camera.network = list() + else + src.icon_state = icon_on + camera.network = list("news") + camera.c_tag = user.name + else + + src.icon_state = icon_on + camera = new /obj/machinery/camera(src) + camera.network = list("news") + cameranet.removeCamera(camera) + camera.c_tag = user.name + to_chat(user, "You switch the camera [on ? "on" : "off"].") + +/obj/item/videocam/examine(mob/user) + . = ..() + if(in_range(user, src)) + . += "This video camera can send live feeds to the entertainment network. It's [camera ? "" : "in"]active." + +/obj/item/videocam/hear_talk(mob/M as mob, list/message_pieces) + var/msg = multilingual_to_message(message_pieces) + if(camera && on) + if(get_dist(src, M) <= canhear_range) + talk_into(M, msg) + for(var/obj/machinery/computer/security/telescreen/T in GLOB.machines) + if(T.watchers[M] == camera) + T.audible_message("(Newscaster) [M] says, '[msg]'", hearing_distance = 2) + +/obj/item/videocam/hear_message(mob/M as mob, msg) + if(camera && on) + for(var/obj/machinery/computer/security/telescreen/T in GLOB.machines) + if(T.watchers[M] == camera) + T.audible_message("(Newscaster) [M] [msg]", hearing_distance = 2) + + +///hauntings, like hallucinations but more spooky + +/obj/item/camera/proc/handle_haunt(mob/user as mob) + var/list/creepyasssounds = list('sound/effects/ghost.ogg', 'sound/effects/ghost2.ogg', 'sound/effects/heartbeat.ogg', 'sound/effects/screech.ogg',\ + 'sound/hallucinations/behind_you1.ogg', 'sound/hallucinations/behind_you2.ogg', 'sound/hallucinations/far_noise.ogg', 'sound/hallucinations/growl1.ogg', 'sound/hallucinations/growl2.ogg',\ + 'sound/hallucinations/growl3.ogg', 'sound/hallucinations/im_here1.ogg', 'sound/hallucinations/im_here2.ogg', 'sound/hallucinations/i_see_you1.ogg', 'sound/hallucinations/i_see_you2.ogg',\ + 'sound/hallucinations/look_up1.ogg', 'sound/hallucinations/look_up2.ogg', 'sound/hallucinations/over_here1.ogg', 'sound/hallucinations/over_here2.ogg', 'sound/hallucinations/over_here3.ogg',\ + 'sound/hallucinations/turn_around1.ogg', 'sound/hallucinations/turn_around2.ogg', 'sound/hallucinations/veryfar_noise.ogg', 'sound/hallucinations/wail.ogg') + user << pick(creepyasssounds) diff --git a/code/modules/pda/PDA.dm b/code/modules/pda/PDA.dm index b2cf43e5b84..4030924a8ac 100755 --- a/code/modules/pda/PDA.dm +++ b/code/modules/pda/PDA.dm @@ -1,520 +1,520 @@ - -//The advanced pea-green monochrome lcd of tomorrow. - -var/global/list/obj/item/pda/PDAs = list() - - -/obj/item/pda - name = "PDA" - desc = "A portable microcomputer by Thinktronic Systems, LTD. Functionality determined by a preprogrammed ROM cartridge." - icon = 'icons/obj/pda.dmi' - icon_state = "pda" - item_state = "electronic" - w_class = WEIGHT_CLASS_TINY - slot_flags = SLOT_ID | SLOT_BELT | SLOT_PDA - armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) - resistance_flags = FIRE_PROOF | ACID_PROOF - origin_tech = "programming=2" - - //Main variables - var/owner = null - var/default_cartridge = 0 // Access level defined by cartridge - var/obj/item/cartridge/cartridge = null //current cartridge - var/datum/data/pda/app/current_app = null - var/datum/data/pda/app/lastapp = null - var/ui_tick = 0 - - //Secondary variables - var/model_name = "Thinktronic 5230 Personal Data Assistant" - var/datum/data/pda/utility/scanmode/scanmode = null - - var/lock_code = "" // Lockcode to unlock uplink - var/honkamt = 0 //How many honks left when infected with honk.exe - var/mimeamt = 0 //How many silence left when infected with mime.exe - var/detonate = 1 // Can the PDA be blown up? - var/ttone = "beep" //The ringtone! - var/list/ttone_sound = list("beep" = 'sound/machines/twobeep.ogg', - "boom" = 'sound/effects/explosionfar.ogg', - "slip" = 'sound/misc/slip.ogg', - "honk" = 'sound/items/bikehorn.ogg', - "SKREE" = 'sound/voice/shriek1.ogg', - "holy" = 'sound/items/PDA/ambicha4-short.ogg', - "xeno" = 'sound/voice/hiss1.ogg') - - var/list/programs = list( - new/datum/data/pda/app/main_menu, - new/datum/data/pda/app/notekeeper, - new/datum/data/pda/app/messenger, - new/datum/data/pda/app/manifest, - new/datum/data/pda/app/atmos_scanner, - new/datum/data/pda/utility/scanmode/notes, - new/datum/data/pda/utility/flashlight) - var/list/shortcut_cache = list() - var/list/shortcut_cat_order = list() - var/list/notifying_programs = list() - - var/obj/item/card/id/id = null //Making it possible to slot an ID card into the PDA so it can function as both. - var/ownjob = null //related to above - var/ownrank = null // this one is rank, never alt title - - var/obj/item/paicard/pai = null // A slot for a personal AI device - var/retro_mode = 0 - - -/* - * The Actual PDA - */ -/obj/item/pda/Initialize(mapload) - . = ..() - PDAs += src - PDAs = sortAtom(PDAs) - update_programs() - if(default_cartridge) - cartridge = new default_cartridge(src) - cartridge.update_programs(src) - new /obj/item/pen(src) - start_program(find_program(/datum/data/pda/app/main_menu)) - -/obj/item/pda/proc/can_use() - if(!ismob(loc)) - return 0 - - var/mob/M = loc - if(M.incapacitated()) - return 0 - if((src in M.contents) || ( istype(loc, /turf) && in_range(src, M) )) - return 1 - else - return 0 - -/obj/item/pda/GetAccess() - if(id) - return id.GetAccess() - else - return ..() - -/obj/item/pda/GetID() - return id - -/obj/item/pda/MouseDrop(obj/over_object as obj, src_location, over_location) - var/mob/M = usr - if((!istype(over_object, /obj/screen)) && can_use()) - return attack_self(M) - -/obj/item/pda/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = inventory_state) - ui_tick++ - var/datum/nanoui/old_ui = SSnanoui.get_open_ui(user, src, "main") - var/auto_update = 1 - if(!current_app) - return - - if(current_app.update == PDA_APP_NOUPDATE && current_app == lastapp) - auto_update = 0 - if(old_ui && (current_app == lastapp && ui_tick % 5 && current_app.update == PDA_APP_UPDATE_SLOW)) - return - - lastapp = current_app - - var/title = "Personal Data Assistant" - - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "pda.tmpl", title, 630, 600, state = state) - ui.set_state_key("pda") - - // open the new ui window - ui.open() - - // auto update every Master Controller tick - ui.set_auto_update(auto_update) - -/obj/item/pda/ui_data(mob/user, ui_key = "main", datum/topic_state/state = inventory_state) - var/data[0] - - data["owner"] = owner // Who is your daddy... - data["ownjob"] = ownjob // ...and what does he do? - - // update list of shortcuts, only if they changed - if(!shortcut_cache.len) - shortcut_cache = list() - shortcut_cat_order = list() - var/prog_list = programs.Copy() - if(cartridge) - prog_list |= cartridge.programs - - for(var/A in prog_list) - var/datum/data/pda/P = A - - if(P.hidden) - continue - var/list/cat - if(P.category in shortcut_cache) - cat = shortcut_cache[P.category] - else - cat = list() - shortcut_cache[P.category] = cat - shortcut_cat_order += P.category - cat |= list(list(name = P.name, icon = P.icon, notify_icon = P.notify_icon, ref = "\ref[P]")) - - // force the order of a few core categories - shortcut_cat_order = list("General") \ - + sortList(shortcut_cat_order - list("General", "Scanners", "Utilities")) \ - + list("Scanners", "Utilities") - - data["idInserted"] = (id ? 1 : 0) - data["idLink"] = (id ? text("[id.registered_name], [id.assignment]") : "--------") - - data["useRetro"] = retro_mode - - data["cartridge_name"] = cartridge ? cartridge.name : "" - data["stationTime"] = station_time_timestamp() - - data["app"] = list() - current_app.update_ui(user, data) - data["app"] |= list( - "name" = current_app.title, - "icon" = current_app.icon, - "template" = current_app.template, - "has_back" = current_app.has_back) - - return data - -/obj/item/pda/attack_self(mob/user as mob) - user.set_machine(src) - if(active_uplink_check(user)) - return - ui_interact(user) //NanoUI requires this proc - -/obj/item/pda/proc/start_program(datum/data/pda/P) - if(P && ((P in programs) || (cartridge && (P in cartridge.programs)))) - return P.start() - return 0 - -/obj/item/pda/proc/find_program(type) - var/datum/data/pda/A = locate(type) in programs - if(A) - return A - if(cartridge) - A = locate(type) in cartridge.programs - if(A) - return A - return null - -// force the cache to rebuild on update_ui -/obj/item/pda/proc/update_shortcuts() - shortcut_cache.Cut() - -/obj/item/pda/proc/update_programs() - for(var/A in programs) - var/datum/data/pda/P = A - P.pda = src - -/obj/item/pda/Topic(href, href_list) - . = ..() - if(.) - return - - var/mob/user = usr - var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main") - var/mob/living/U = usr - if(usr.stat == DEAD) - return 0 - if(!can_use()) //Why reinvent the wheel? There's a proc that does exactly that. - U.unset_machine() - if(ui) - ui.close() - return 0 - - add_fingerprint(U) - U.set_machine(src) - - if(href_list["radiomenu"] && !isnull(cartridge) && !isnull(cartridge.radio)) - cartridge.radio.Topic(href, href_list) - return 1 - - . = 1 - - switch(href_list["choice"]) - if("Home")//Go home, largely replaces the old Return - var/datum/data/pda/app/main_menu/A = find_program(/datum/data/pda/app/main_menu) - if(A) - start_program(A) - if("StartProgram") - if(href_list["program"]) - var/datum/data/pda/app/A = locate(href_list["program"]) - if(A) - start_program(A) - if("Eject")//Ejects the cart, only done from hub. - if(!isnull(cartridge)) - var/turf/T = loc - if(ismob(T)) - T = T.loc - var/obj/item/cartridge/C = cartridge - C.forceMove(T) - if(scanmode in C.programs) - scanmode = null - if(current_app in C.programs) - start_program(find_program(/datum/data/pda/app/main_menu)) - if(C.radio) - C.radio.hostpda = null - for(var/datum/data/pda/P in notifying_programs) - if(P in C.programs) - P.unnotify() - cartridge = null - update_shortcuts() - if("Authenticate")//Checks for ID - id_check(usr, 1) - if("Retro") - retro_mode = !retro_mode - if("Ringtone") - return set_ringtone() - else - if(current_app) - . = current_app.Topic(href, href_list) - -//EXTRA FUNCTIONS=================================== - if((honkamt > 0) && (prob(60)))//For clown virus. - honkamt-- - playsound(loc, 'sound/items/bikehorn.ogg', 30, 1) - - return // return 1 tells it to refresh the UI in NanoUI - -/obj/item/pda/proc/close(mob/user) - var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main") - ui.close() - -/obj/item/pda/verb/verb_reset_pda() - set category = "Object" - set name = "Reset PDA" - set src in usr - - if(issilicon(usr)) - return - - if(can_use(usr)) - start_program(find_program(/datum/data/pda/app/main_menu)) - notifying_programs.Cut() - overlays -= image('icons/obj/pda.dmi', "pda-r") - to_chat(usr, "You press the reset button on \the [src].") - else - to_chat(usr, "You cannot do this while restrained.") - -/obj/item/pda/AltClick(mob/user) - ..() - if(issilicon(user)) - return - - if(can_use(user)) - if(id) - remove_id(user) - else - to_chat(user, "This PDA does not have an ID in it!") - -/obj/item/pda/CtrlClick(mob/user) - ..() - if(issilicon(user)) - return - - if(can_use(user)) - remove_pen(user) - -/obj/item/pda/proc/remove_id(mob/user) - if(id) - if(ismob(loc)) - var/mob/M = loc - M.put_in_hands(id) - to_chat(user, "You remove the ID from the [name].") - else - id.forceMove(get_turf(src)) - overlays -= image('icons/goonstation/objects/pda_overlay.dmi', id.icon_state) - id = null - -/obj/item/pda/verb/verb_remove_id() - set category = "Object" - set name = "Remove id" - set src in usr - - if(issilicon(usr)) - return - - if( can_use(usr) ) - if(id) - remove_id(usr) - else - to_chat(usr, "This PDA does not have an ID in it.") - else - to_chat(usr, "You cannot do this while restrained.") - -/obj/item/pda/verb/verb_remove_pen() - set category = "Object" - set name = "Remove pen" - set src in usr - remove_pen(usr) - -/obj/item/pda/proc/remove_pen(mob/user) - - if(issilicon(user)) - return - - if( can_use(user) ) - var/obj/item/pen/O = locate() in src - if(O) - to_chat(user, "You remove the [O] from [src].") - if(istype(loc, /mob)) - var/mob/M = loc - if(M.get_active_hand() == null) - M.put_in_hands(O) - return - O.forceMove(get_turf(src)) - else - to_chat(user, "This PDA does not have a pen in it.") - else - to_chat(user, "You cannot do this while restrained.") - -/obj/item/pda/proc/id_check(mob/user as mob, choice as num)//To check for IDs; 1 for in-pda use, 2 for out of pda use. - if(choice == 1) - if(id) - remove_id(user) - else - var/obj/item/I = user.get_active_hand() - if(istype(I, /obj/item/card/id)) - user.drop_item() - I.forceMove(src) - id = I - else - var/obj/item/card/I = user.get_active_hand() - if(istype(I, /obj/item/card/id) && I:registered_name) - var/obj/old_id = id - user.drop_item() - I.forceMove(src) - id = I - user.put_in_hands(old_id) - return - -/obj/item/pda/attackby(obj/item/C as obj, mob/user as mob, params) - ..() - if(istype(C, /obj/item/cartridge) && !cartridge) - cartridge = C - user.drop_item() - cartridge.forceMove(src) - cartridge.update_programs(src) - update_shortcuts() - to_chat(user, "You insert [cartridge] into [src].") - if(cartridge.radio) - cartridge.radio.hostpda = src - - else if(istype(C, /obj/item/card/id)) - var/obj/item/card/id/idcard = C - if(!idcard.registered_name) - to_chat(user, "\The [src] rejects the ID.") - return - if(!owner) - owner = idcard.registered_name - ownjob = idcard.assignment - ownrank = idcard.rank - name = "PDA-[owner] ([ownjob])" - to_chat(user, "Card scanned.") - else - //Basic safety check. If either both objects are held by user or PDA is on ground and card is in hand. - if(((src in user.contents) && (C in user.contents)) || (istype(loc, /turf) && in_range(src, user) && (C in user.contents)) ) - if( can_use(user) )//If they can still act. - id_check(user, 2) - to_chat(user, "You put the ID into \the [src]'s slot.
        You can remove it with ALT click.
        ") - overlays += image('icons/goonstation/objects/pda_overlay.dmi', C.icon_state) - - else if(istype(C, /obj/item/paicard) && !src.pai) - user.drop_item() - C.forceMove(src) - pai = C - to_chat(user, "You slot \the [C] into [src].") - else if(istype(C, /obj/item/pen)) - var/obj/item/pen/O = locate() in src - if(O) - to_chat(user, "There is already a pen in \the [src].") - else - user.drop_item() - C.forceMove(src) - to_chat(user, "You slide \the [C] into \the [src].") - else if(istype(C, /obj/item/nanomob_card)) - if(cartridge && istype(cartridge, /obj/item/cartridge/mob_hunt_game)) - cartridge.attackby(C, user, params) - -/obj/item/pda/attack(mob/living/C as mob, mob/living/user as mob) - if(istype(C, /mob/living/carbon) && scanmode) - scanmode.scan_mob(C, user) - -/obj/item/pda/afterattack(atom/A as mob|obj|turf|area, mob/user as mob, proximity) - if(proximity && scanmode) - scanmode.scan_atom(A, user) - -/obj/item/pda/proc/explode() //This needs tuning. - if(!detonate) - return - var/turf/T = get_turf(src.loc) - - if(ismob(loc)) - var/mob/M = loc - M.show_message("Your [src] explodes!", 1) - - if(T) - T.hotspot_expose(700,125) - - explosion(T, -1, -1, 2, 3) - qdel(src) - return - -/obj/item/pda/Destroy() - PDAs -= src - var/T = get_turf(loc) - if(id) - id.forceMove(T) - if(pai) - pai.forceMove(T) - current_app = null - scanmode = null - QDEL_LIST(programs) - QDEL_NULL(cartridge) - return ..() - -// Pass along the pulse to atoms in contents, largely added so pAIs are vulnerable to EMP -/obj/item/pda/emp_act(severity) - for(var/atom/A in src) - A.emp_act(severity) - -/obj/item/pda/proc/play_ringtone() - var/S - - if(ttone in ttone_sound) - S = ttone_sound[ttone] - else - S = 'sound/machines/twobeep.ogg' - playsound(loc, S, 50, 1) - for(var/mob/O in hearers(3, loc)) - O.show_message(text("[bicon(src)] *[ttone]*")) - -/obj/item/pda/proc/set_ringtone() - var/t = input("Please enter new ringtone", name, ttone) as text - if(in_range(src, usr) && loc == usr) - if(t) - if(hidden_uplink && hidden_uplink.check_trigger(usr, lowertext(t), lowertext(lock_code))) - to_chat(usr, "The PDA softly beeps.") - close(usr) - else - t = sanitize(copytext(t, 1, 20)) - ttone = t - return 1 - else - close(usr) - return 0 - -/obj/item/pda/process() - if(current_app) - current_app.program_process() - -/obj/item/pda/extinguish_light() - var/datum/data/pda/utility/flashlight/FL = find_program(/datum/data/pda/utility/flashlight) - if(FL && FL.fon) - FL.start() + +//The advanced pea-green monochrome lcd of tomorrow. + +var/global/list/obj/item/pda/PDAs = list() + + +/obj/item/pda + name = "PDA" + desc = "A portable microcomputer by Thinktronic Systems, LTD. Functionality determined by a preprogrammed ROM cartridge." + icon = 'icons/obj/pda.dmi' + icon_state = "pda" + item_state = "electronic" + w_class = WEIGHT_CLASS_TINY + slot_flags = SLOT_ID | SLOT_BELT | SLOT_PDA + armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100) + resistance_flags = FIRE_PROOF | ACID_PROOF + origin_tech = "programming=2" + + //Main variables + var/owner = null + var/default_cartridge = 0 // Access level defined by cartridge + var/obj/item/cartridge/cartridge = null //current cartridge + var/datum/data/pda/app/current_app = null + var/datum/data/pda/app/lastapp = null + var/ui_tick = 0 + + //Secondary variables + var/model_name = "Thinktronic 5230 Personal Data Assistant" + var/datum/data/pda/utility/scanmode/scanmode = null + + var/lock_code = "" // Lockcode to unlock uplink + var/honkamt = 0 //How many honks left when infected with honk.exe + var/mimeamt = 0 //How many silence left when infected with mime.exe + var/detonate = 1 // Can the PDA be blown up? + var/ttone = "beep" //The ringtone! + var/list/ttone_sound = list("beep" = 'sound/machines/twobeep.ogg', + "boom" = 'sound/effects/explosionfar.ogg', + "slip" = 'sound/misc/slip.ogg', + "honk" = 'sound/items/bikehorn.ogg', + "SKREE" = 'sound/voice/shriek1.ogg', + "holy" = 'sound/items/PDA/ambicha4-short.ogg', + "xeno" = 'sound/voice/hiss1.ogg') + + var/list/programs = list( + new/datum/data/pda/app/main_menu, + new/datum/data/pda/app/notekeeper, + new/datum/data/pda/app/messenger, + new/datum/data/pda/app/manifest, + new/datum/data/pda/app/atmos_scanner, + new/datum/data/pda/utility/scanmode/notes, + new/datum/data/pda/utility/flashlight) + var/list/shortcut_cache = list() + var/list/shortcut_cat_order = list() + var/list/notifying_programs = list() + + var/obj/item/card/id/id = null //Making it possible to slot an ID card into the PDA so it can function as both. + var/ownjob = null //related to above + var/ownrank = null // this one is rank, never alt title + + var/obj/item/paicard/pai = null // A slot for a personal AI device + var/retro_mode = 0 + + +/* + * The Actual PDA + */ +/obj/item/pda/Initialize(mapload) + . = ..() + PDAs += src + PDAs = sortAtom(PDAs) + update_programs() + if(default_cartridge) + cartridge = new default_cartridge(src) + cartridge.update_programs(src) + new /obj/item/pen(src) + start_program(find_program(/datum/data/pda/app/main_menu)) + +/obj/item/pda/proc/can_use() + if(!ismob(loc)) + return 0 + + var/mob/M = loc + if(M.incapacitated()) + return 0 + if((src in M.contents) || ( istype(loc, /turf) && in_range(src, M) )) + return 1 + else + return 0 + +/obj/item/pda/GetAccess() + if(id) + return id.GetAccess() + else + return ..() + +/obj/item/pda/GetID() + return id + +/obj/item/pda/MouseDrop(obj/over_object as obj, src_location, over_location) + var/mob/M = usr + if((!istype(over_object, /obj/screen)) && can_use()) + return attack_self(M) + +/obj/item/pda/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = inventory_state) + ui_tick++ + var/datum/nanoui/old_ui = SSnanoui.get_open_ui(user, src, "main") + var/auto_update = 1 + if(!current_app) + return + + if(current_app.update == PDA_APP_NOUPDATE && current_app == lastapp) + auto_update = 0 + if(old_ui && (current_app == lastapp && ui_tick % 5 && current_app.update == PDA_APP_UPDATE_SLOW)) + return + + lastapp = current_app + + var/title = "Personal Data Assistant" + + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "pda.tmpl", title, 630, 600, state = state) + ui.set_state_key("pda") + + // open the new ui window + ui.open() + + // auto update every Master Controller tick + ui.set_auto_update(auto_update) + +/obj/item/pda/ui_data(mob/user, ui_key = "main", datum/topic_state/state = inventory_state) + var/data[0] + + data["owner"] = owner // Who is your daddy... + data["ownjob"] = ownjob // ...and what does he do? + + // update list of shortcuts, only if they changed + if(!shortcut_cache.len) + shortcut_cache = list() + shortcut_cat_order = list() + var/prog_list = programs.Copy() + if(cartridge) + prog_list |= cartridge.programs + + for(var/A in prog_list) + var/datum/data/pda/P = A + + if(P.hidden) + continue + var/list/cat + if(P.category in shortcut_cache) + cat = shortcut_cache[P.category] + else + cat = list() + shortcut_cache[P.category] = cat + shortcut_cat_order += P.category + cat |= list(list(name = P.name, icon = P.icon, notify_icon = P.notify_icon, ref = "\ref[P]")) + + // force the order of a few core categories + shortcut_cat_order = list("General") \ + + sortList(shortcut_cat_order - list("General", "Scanners", "Utilities")) \ + + list("Scanners", "Utilities") + + data["idInserted"] = (id ? 1 : 0) + data["idLink"] = (id ? text("[id.registered_name], [id.assignment]") : "--------") + + data["useRetro"] = retro_mode + + data["cartridge_name"] = cartridge ? cartridge.name : "" + data["stationTime"] = station_time_timestamp() + + data["app"] = list() + current_app.update_ui(user, data) + data["app"] |= list( + "name" = current_app.title, + "icon" = current_app.icon, + "template" = current_app.template, + "has_back" = current_app.has_back) + + return data + +/obj/item/pda/attack_self(mob/user as mob) + user.set_machine(src) + if(active_uplink_check(user)) + return + ui_interact(user) //NanoUI requires this proc + +/obj/item/pda/proc/start_program(datum/data/pda/P) + if(P && ((P in programs) || (cartridge && (P in cartridge.programs)))) + return P.start() + return 0 + +/obj/item/pda/proc/find_program(type) + var/datum/data/pda/A = locate(type) in programs + if(A) + return A + if(cartridge) + A = locate(type) in cartridge.programs + if(A) + return A + return null + +// force the cache to rebuild on update_ui +/obj/item/pda/proc/update_shortcuts() + shortcut_cache.Cut() + +/obj/item/pda/proc/update_programs() + for(var/A in programs) + var/datum/data/pda/P = A + P.pda = src + +/obj/item/pda/Topic(href, href_list) + . = ..() + if(.) + return + + var/mob/user = usr + var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main") + var/mob/living/U = usr + if(usr.stat == DEAD) + return 0 + if(!can_use()) //Why reinvent the wheel? There's a proc that does exactly that. + U.unset_machine() + if(ui) + ui.close() + return 0 + + add_fingerprint(U) + U.set_machine(src) + + if(href_list["radiomenu"] && !isnull(cartridge) && !isnull(cartridge.radio)) + cartridge.radio.Topic(href, href_list) + return 1 + + . = 1 + + switch(href_list["choice"]) + if("Home")//Go home, largely replaces the old Return + var/datum/data/pda/app/main_menu/A = find_program(/datum/data/pda/app/main_menu) + if(A) + start_program(A) + if("StartProgram") + if(href_list["program"]) + var/datum/data/pda/app/A = locate(href_list["program"]) + if(A) + start_program(A) + if("Eject")//Ejects the cart, only done from hub. + if(!isnull(cartridge)) + var/turf/T = loc + if(ismob(T)) + T = T.loc + var/obj/item/cartridge/C = cartridge + C.forceMove(T) + if(scanmode in C.programs) + scanmode = null + if(current_app in C.programs) + start_program(find_program(/datum/data/pda/app/main_menu)) + if(C.radio) + C.radio.hostpda = null + for(var/datum/data/pda/P in notifying_programs) + if(P in C.programs) + P.unnotify() + cartridge = null + update_shortcuts() + if("Authenticate")//Checks for ID + id_check(usr, 1) + if("Retro") + retro_mode = !retro_mode + if("Ringtone") + return set_ringtone() + else + if(current_app) + . = current_app.Topic(href, href_list) + +//EXTRA FUNCTIONS=================================== + if((honkamt > 0) && (prob(60)))//For clown virus. + honkamt-- + playsound(loc, 'sound/items/bikehorn.ogg', 30, 1) + + return // return 1 tells it to refresh the UI in NanoUI + +/obj/item/pda/proc/close(mob/user) + var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main") + ui.close() + +/obj/item/pda/verb/verb_reset_pda() + set category = "Object" + set name = "Reset PDA" + set src in usr + + if(issilicon(usr)) + return + + if(can_use(usr)) + start_program(find_program(/datum/data/pda/app/main_menu)) + notifying_programs.Cut() + overlays -= image('icons/obj/pda.dmi', "pda-r") + to_chat(usr, "You press the reset button on \the [src].") + else + to_chat(usr, "You cannot do this while restrained.") + +/obj/item/pda/AltClick(mob/user) + ..() + if(issilicon(user)) + return + + if(can_use(user)) + if(id) + remove_id(user) + else + to_chat(user, "This PDA does not have an ID in it!") + +/obj/item/pda/CtrlClick(mob/user) + ..() + if(issilicon(user)) + return + + if(can_use(user)) + remove_pen(user) + +/obj/item/pda/proc/remove_id(mob/user) + if(id) + if(ismob(loc)) + var/mob/M = loc + M.put_in_hands(id) + to_chat(user, "You remove the ID from the [name].") + else + id.forceMove(get_turf(src)) + overlays -= image('icons/goonstation/objects/pda_overlay.dmi', id.icon_state) + id = null + +/obj/item/pda/verb/verb_remove_id() + set category = "Object" + set name = "Remove id" + set src in usr + + if(issilicon(usr)) + return + + if( can_use(usr) ) + if(id) + remove_id(usr) + else + to_chat(usr, "This PDA does not have an ID in it.") + else + to_chat(usr, "You cannot do this while restrained.") + +/obj/item/pda/verb/verb_remove_pen() + set category = "Object" + set name = "Remove pen" + set src in usr + remove_pen(usr) + +/obj/item/pda/proc/remove_pen(mob/user) + + if(issilicon(user)) + return + + if( can_use(user) ) + var/obj/item/pen/O = locate() in src + if(O) + to_chat(user, "You remove the [O] from [src].") + if(istype(loc, /mob)) + var/mob/M = loc + if(M.get_active_hand() == null) + M.put_in_hands(O) + return + O.forceMove(get_turf(src)) + else + to_chat(user, "This PDA does not have a pen in it.") + else + to_chat(user, "You cannot do this while restrained.") + +/obj/item/pda/proc/id_check(mob/user as mob, choice as num)//To check for IDs; 1 for in-pda use, 2 for out of pda use. + if(choice == 1) + if(id) + remove_id(user) + else + var/obj/item/I = user.get_active_hand() + if(istype(I, /obj/item/card/id)) + user.drop_item() + I.forceMove(src) + id = I + else + var/obj/item/card/I = user.get_active_hand() + if(istype(I, /obj/item/card/id) && I:registered_name) + var/obj/old_id = id + user.drop_item() + I.forceMove(src) + id = I + user.put_in_hands(old_id) + return + +/obj/item/pda/attackby(obj/item/C as obj, mob/user as mob, params) + ..() + if(istype(C, /obj/item/cartridge) && !cartridge) + cartridge = C + user.drop_item() + cartridge.forceMove(src) + cartridge.update_programs(src) + update_shortcuts() + to_chat(user, "You insert [cartridge] into [src].") + if(cartridge.radio) + cartridge.radio.hostpda = src + + else if(istype(C, /obj/item/card/id)) + var/obj/item/card/id/idcard = C + if(!idcard.registered_name) + to_chat(user, "\The [src] rejects the ID.") + return + if(!owner) + owner = idcard.registered_name + ownjob = idcard.assignment + ownrank = idcard.rank + name = "PDA-[owner] ([ownjob])" + to_chat(user, "Card scanned.") + else + //Basic safety check. If either both objects are held by user or PDA is on ground and card is in hand. + if(((src in user.contents) && (C in user.contents)) || (istype(loc, /turf) && in_range(src, user) && (C in user.contents)) ) + if( can_use(user) )//If they can still act. + id_check(user, 2) + to_chat(user, "You put the ID into \the [src]'s slot.
        You can remove it with ALT click.
        ") + overlays += image('icons/goonstation/objects/pda_overlay.dmi', C.icon_state) + + else if(istype(C, /obj/item/paicard) && !src.pai) + user.drop_item() + C.forceMove(src) + pai = C + to_chat(user, "You slot \the [C] into [src].") + else if(istype(C, /obj/item/pen)) + var/obj/item/pen/O = locate() in src + if(O) + to_chat(user, "There is already a pen in \the [src].") + else + user.drop_item() + C.forceMove(src) + to_chat(user, "You slide \the [C] into \the [src].") + else if(istype(C, /obj/item/nanomob_card)) + if(cartridge && istype(cartridge, /obj/item/cartridge/mob_hunt_game)) + cartridge.attackby(C, user, params) + +/obj/item/pda/attack(mob/living/C as mob, mob/living/user as mob) + if(istype(C, /mob/living/carbon) && scanmode) + scanmode.scan_mob(C, user) + +/obj/item/pda/afterattack(atom/A as mob|obj|turf|area, mob/user as mob, proximity) + if(proximity && scanmode) + scanmode.scan_atom(A, user) + +/obj/item/pda/proc/explode() //This needs tuning. + if(!detonate) + return + var/turf/T = get_turf(src.loc) + + if(ismob(loc)) + var/mob/M = loc + M.show_message("Your [src] explodes!", 1) + + if(T) + T.hotspot_expose(700,125) + + explosion(T, -1, -1, 2, 3) + qdel(src) + return + +/obj/item/pda/Destroy() + PDAs -= src + var/T = get_turf(loc) + if(id) + id.forceMove(T) + if(pai) + pai.forceMove(T) + current_app = null + scanmode = null + QDEL_LIST(programs) + QDEL_NULL(cartridge) + return ..() + +// Pass along the pulse to atoms in contents, largely added so pAIs are vulnerable to EMP +/obj/item/pda/emp_act(severity) + for(var/atom/A in src) + A.emp_act(severity) + +/obj/item/pda/proc/play_ringtone() + var/S + + if(ttone in ttone_sound) + S = ttone_sound[ttone] + else + S = 'sound/machines/twobeep.ogg' + playsound(loc, S, 50, 1) + for(var/mob/O in hearers(3, loc)) + O.show_message(text("[bicon(src)] *[ttone]*")) + +/obj/item/pda/proc/set_ringtone() + var/t = input("Please enter new ringtone", name, ttone) as text + if(in_range(src, usr) && loc == usr) + if(t) + if(hidden_uplink && hidden_uplink.check_trigger(usr, lowertext(t), lowertext(lock_code))) + to_chat(usr, "The PDA softly beeps.") + close(usr) + else + t = sanitize(copytext(t, 1, 20)) + ttone = t + return 1 + else + close(usr) + return 0 + +/obj/item/pda/process() + if(current_app) + current_app.program_process() + +/obj/item/pda/extinguish_light() + var/datum/data/pda/utility/flashlight/FL = find_program(/datum/data/pda/utility/flashlight) + if(FL && FL.fon) + FL.start() diff --git a/code/modules/pda/ai.dm b/code/modules/pda/ai.dm index 5860ff660f3..ac4e6c9fd6b 100644 --- a/code/modules/pda/ai.dm +++ b/code/modules/pda/ai.dm @@ -101,4 +101,4 @@ if(!pAI.software["messenger"]) to_chat(usr, "You have not purchased the digital messenger!") return 0 - return ..() && !pAI.silence_time \ No newline at end of file + return ..() && !pAI.silence_time diff --git a/code/modules/pda/cart.dm b/code/modules/pda/cart.dm index 7b7ec95b0e3..6339f5375d7 100644 --- a/code/modules/pda/cart.dm +++ b/code/modules/pda/cart.dm @@ -1,325 +1,325 @@ -/obj/item/cartridge - name = "generic cartridge" - desc = "A data cartridge for portable microcomputers." - icon = 'icons/obj/pda.dmi' - icon_state = "cart" - item_state = "electronic" - w_class = WEIGHT_CLASS_TINY - - var/obj/item/integrated_radio/radio = null - - var/charges = 0 - - var/list/stored_data = list() - var/list/programs = list() - var/list/messenger_plugins = list() - -/obj/item/cartridge/Destroy() - QDEL_NULL(radio) - QDEL_LIST(programs) - QDEL_LIST(messenger_plugins) - return ..() - -/obj/item/cartridge/proc/update_programs(obj/item/pda/pda) - for(var/A in programs) - var/datum/data/pda/P = A - P.pda = pda - for(var/A in messenger_plugins) - var/datum/data/pda/messenger_plugin/P = A - P.pda = pda - -/obj/item/cartridge/engineering - name = "Power-ON Cartridge" - icon_state = "cart-e" - programs = list( - new/datum/data/pda/app/power, - new/datum/data/pda/utility/scanmode/halogen) - -/obj/item/cartridge/atmos - name = "BreatheDeep Cartridge" - icon_state = "cart-a" - programs = list(new/datum/data/pda/utility/scanmode/gas) - -/obj/item/cartridge/medical - name = "Med-U Cartridge" - icon_state = "cart-m" - programs = list( - new/datum/data/pda/app/crew_records/medical, - new/datum/data/pda/utility/scanmode/medical) - -/obj/item/cartridge/chemistry - name = "ChemWhiz Cartridge" - icon_state = "cart-chem" - programs = list(new/datum/data/pda/utility/scanmode/reagent) - -/obj/item/cartridge/security - name = "R.O.B.U.S.T. Cartridge" - icon_state = "cart-s" - programs = list( - new/datum/data/pda/app/crew_records/security, - new/datum/data/pda/app/secbot_control) - -/obj/item/cartridge/security/Initialize(mapload) - . = ..() - radio = new /obj/item/integrated_radio/beepsky(src) - -/obj/item/cartridge/detective - name = "D.E.T.E.C.T. Cartridge" - icon_state = "cart-s" - programs = list( - new/datum/data/pda/app/crew_records/medical, - new/datum/data/pda/utility/scanmode/medical, - - new/datum/data/pda/app/crew_records/security) - - -/obj/item/cartridge/janitor - name = "CustodiPRO Cartridge" - desc = "The ultimate in clean-room design." - icon_state = "cart-j" - programs = list(new/datum/data/pda/app/janitor) - -/obj/item/cartridge/lawyer - name = "P.R.O.V.E. Cartridge" - icon_state = "cart-s" - programs = list(new/datum/data/pda/app/crew_records/security) - -/obj/item/cartridge/clown - name = "Honkworks 5.0" - icon_state = "cart-clown" - charges = 5 - programs = list(new/datum/data/pda/utility/honk) - messenger_plugins = list(new/datum/data/pda/messenger_plugin/virus/clown) - -/obj/item/cartridge/mime - name = "Gestur-O 1000" - icon_state = "cart-mi" - charges = 5 - messenger_plugins = list(new/datum/data/pda/messenger_plugin/virus/mime) - -/* -/obj/item/cartridge/botanist - name = "Green Thumb v4.20" - icon_state = "cart-b" - access_flora = 1 -*/ - -/obj/item/cartridge/signal - name = "generic signaler cartridge" - desc = "A data cartridge with an integrated radio signaler module." - programs = list(new/datum/data/pda/app/signaller) - -/obj/item/cartridge/signal/Initialize(mapload) - . = ..() - radio = new /obj/item/integrated_radio/signal(src) - -/obj/item/cartridge/signal/toxins - name = "Signal Ace 2" - desc = "Complete with integrated radio signaler!" - icon_state = "cart-tox" - programs = list( - new/datum/data/pda/utility/scanmode/gas, - - new/datum/data/pda/utility/scanmode/reagent, - - new/datum/data/pda/app/signaller) - -/obj/item/cartridge/quartermaster - name = "Space Parts & Space Vendors Cartridge" - desc = "Perfect for the Quartermaster on the go!" - icon_state = "cart-q" - programs = list( - new/datum/data/pda/app/supply, - new/datum/data/pda/app/mule_control) - -/obj/item/cartridge/quartermaster/Initialize(mapload) - . = ..() - radio = new /obj/item/integrated_radio/mule(src) - -/obj/item/cartridge/head - name = "Easy-Record DELUXE" - icon_state = "cart-h" - programs = list(new/datum/data/pda/app/status_display) - -/obj/item/cartridge/hop - name = "HumanResources9001" - icon_state = "cart-h" - programs = list( - new/datum/data/pda/app/crew_records/security, - - new/datum/data/pda/app/janitor, - - new/datum/data/pda/app/supply, - new/datum/data/pda/app/mule_control, - - new/datum/data/pda/app/status_display) - -/obj/item/cartridge/hop/Initialize(mapload) - . = ..() - radio = new /obj/item/integrated_radio/mule(src) - -/obj/item/cartridge/hos - name = "R.O.B.U.S.T. DELUXE" - icon_state = "cart-hos" - programs = list( - new/datum/data/pda/app/crew_records/security, - new/datum/data/pda/app/secbot_control, - - new/datum/data/pda/app/status_display) - -/obj/item/cartridge/hos/Initialize(mapload) - . = ..() - radio = new /obj/item/integrated_radio/beepsky(src) - -/obj/item/cartridge/ce - name = "Power-On DELUXE" - icon_state = "cart-ce" - programs = list( - new/datum/data/pda/app/power, - new/datum/data/pda/utility/scanmode/halogen, - - new/datum/data/pda/utility/scanmode/gas, - - new/datum/data/pda/app/status_display) - -/obj/item/cartridge/cmo - name = "Med-U DELUXE" - icon_state = "cart-cmo" - programs = list( - new/datum/data/pda/app/crew_records/medical, - new/datum/data/pda/utility/scanmode/medical, - - new/datum/data/pda/utility/scanmode/reagent, - - new/datum/data/pda/app/status_display) - -/obj/item/cartridge/rd - name = "Signal Ace DELUXE" - icon_state = "cart-rd" - programs = list( - new/datum/data/pda/utility/scanmode/gas, - - new/datum/data/pda/utility/scanmode/reagent, - - new/datum/data/pda/app/signaller, - - new/datum/data/pda/app/status_display) - -/obj/item/cartridge/rd/Initialize(mapload) - . = ..() - radio = new /obj/item/integrated_radio/signal(src) - -/obj/item/cartridge/captain - name = "Value-PAK Cartridge" - desc = "Now with 200% more value!" - icon_state = "cart-c" - programs = list( - new/datum/data/pda/app/power, - new/datum/data/pda/utility/scanmode/halogen, - - new/datum/data/pda/utility/scanmode/gas, - - new/datum/data/pda/app/crew_records/medical, - new/datum/data/pda/utility/scanmode/medical, - - new/datum/data/pda/utility/scanmode/reagent, - - new/datum/data/pda/app/crew_records/security, - new/datum/data/pda/app/secbot_control, - - new/datum/data/pda/app/janitor, - - new/datum/data/pda/app/supply, - new/datum/data/pda/app/mule_control, - - new/datum/data/pda/app/status_display) - -/obj/item/cartridge/captain/Initialize(mapload) - . = ..() - radio = new /obj/item/integrated_radio/beepsky(src) - -/obj/item/cartridge/supervisor - name = "Easy-Record DELUXE" - icon_state = "cart-h" - programs = list( - new/datum/data/pda/app/crew_records/security, - - new/datum/data/pda/app/status_display) - -/obj/item/cartridge/centcom - name = "Value-PAK Cartridge" - desc = "Now with 200% more value!" - icon_state = "cart-c" - programs = list( - new/datum/data/pda/app/power, - new/datum/data/pda/utility/scanmode/halogen, - - new/datum/data/pda/utility/scanmode/gas, - - new/datum/data/pda/app/crew_records/medical, - new/datum/data/pda/utility/scanmode/medical, - - new/datum/data/pda/utility/scanmode/reagent, - - new/datum/data/pda/app/crew_records/security, - new/datum/data/pda/app/secbot_control, - - new/datum/data/pda/app/janitor, - - new/datum/data/pda/app/supply, - new/datum/data/pda/app/mule_control, - - new/datum/data/pda/app/status_display) - -/obj/item/cartridge/centcom/Initialize(mapload) - . = ..() - radio = new /obj/item/integrated_radio/beepsky(src) - -/obj/item/cartridge/syndicate - name = "Detomatix Cartridge" - icon_state = "cart" - var/initial_remote_door_id = "smindicate" //Make sure this matches the syndicate shuttle's shield/door id!! //don't ask about the name, testing. - charges = 4 - programs = list(new/datum/data/pda/utility/toggle_door) - messenger_plugins = list(new/datum/data/pda/messenger_plugin/virus/detonate) - -/obj/item/cartridge/syndicate/Initialize(mapload) - . = ..() - var/datum/data/pda/utility/toggle_door/D = programs[1] - if(istype(D)) - D.remote_door_id = initial_remote_door_id - -/obj/item/cartridge/frame - name = "F.R.A.M.E. cartridge" - icon_state = "cart" - charges = 5 - var/telecrystals = 0 - messenger_plugins = list(new/datum/data/pda/messenger_plugin/virus/frame) - -/obj/item/cartridge/mob_hunt_game - name = "Nano-Mob Hunter GO! Cartridge" - desc = "The hit new PDA game that lets you track down and capture your favorite Nano-Mobs living in your world!" - icon_state = "cart-eye" - programs = list(new/datum/data/pda/app/mob_hunter_game) - var/emagged = 0 - -/obj/item/cartridge/mob_hunt_game/attackby(obj/item/O, mob/user, params) - if(istype(O, /obj/item/nanomob_card)) - var/obj/item/nanomob_card/card = O - var/datum/data/pda/app/mob_hunter_game/my_game = programs[1] - if(my_game.register_capture(card.mob_data)) - to_chat(user, "Transfer successful!") - qdel(card) - else - to_chat(user, "Transfer failed. Could not read mob data from card.") - else - ..() - -/obj/item/cartridge/mob_hunt_game/emag_act(mob/user) - if(!emagged) - emagged = 1 - var/datum/data/pda/app/mob_hunter_game/my_game = programs[1] - my_game.hacked = 1 - to_chat(user, "TR4P_M45T3R.mod successfully initialized. ToS violated. User Agreement nullified. Gotta pwn them all.") - to_chat(user, "You can now create trapped versions of any mob in your collection that will damage hunters who attempt to capture it.") - description_antag = "This copy of Nano-Mob Hunter GO! has been hacked to allow the creation of trap mobs which will cause any PDA that attempts to capture it to shock anyone holding it. Hacked copies of the game will not trigger the trap." \ No newline at end of file +/obj/item/cartridge + name = "generic cartridge" + desc = "A data cartridge for portable microcomputers." + icon = 'icons/obj/pda.dmi' + icon_state = "cart" + item_state = "electronic" + w_class = WEIGHT_CLASS_TINY + + var/obj/item/integrated_radio/radio = null + + var/charges = 0 + + var/list/stored_data = list() + var/list/programs = list() + var/list/messenger_plugins = list() + +/obj/item/cartridge/Destroy() + QDEL_NULL(radio) + QDEL_LIST(programs) + QDEL_LIST(messenger_plugins) + return ..() + +/obj/item/cartridge/proc/update_programs(obj/item/pda/pda) + for(var/A in programs) + var/datum/data/pda/P = A + P.pda = pda + for(var/A in messenger_plugins) + var/datum/data/pda/messenger_plugin/P = A + P.pda = pda + +/obj/item/cartridge/engineering + name = "Power-ON Cartridge" + icon_state = "cart-e" + programs = list( + new/datum/data/pda/app/power, + new/datum/data/pda/utility/scanmode/halogen) + +/obj/item/cartridge/atmos + name = "BreatheDeep Cartridge" + icon_state = "cart-a" + programs = list(new/datum/data/pda/utility/scanmode/gas) + +/obj/item/cartridge/medical + name = "Med-U Cartridge" + icon_state = "cart-m" + programs = list( + new/datum/data/pda/app/crew_records/medical, + new/datum/data/pda/utility/scanmode/medical) + +/obj/item/cartridge/chemistry + name = "ChemWhiz Cartridge" + icon_state = "cart-chem" + programs = list(new/datum/data/pda/utility/scanmode/reagent) + +/obj/item/cartridge/security + name = "R.O.B.U.S.T. Cartridge" + icon_state = "cart-s" + programs = list( + new/datum/data/pda/app/crew_records/security, + new/datum/data/pda/app/secbot_control) + +/obj/item/cartridge/security/Initialize(mapload) + . = ..() + radio = new /obj/item/integrated_radio/beepsky(src) + +/obj/item/cartridge/detective + name = "D.E.T.E.C.T. Cartridge" + icon_state = "cart-s" + programs = list( + new/datum/data/pda/app/crew_records/medical, + new/datum/data/pda/utility/scanmode/medical, + + new/datum/data/pda/app/crew_records/security) + + +/obj/item/cartridge/janitor + name = "CustodiPRO Cartridge" + desc = "The ultimate in clean-room design." + icon_state = "cart-j" + programs = list(new/datum/data/pda/app/janitor) + +/obj/item/cartridge/lawyer + name = "P.R.O.V.E. Cartridge" + icon_state = "cart-s" + programs = list(new/datum/data/pda/app/crew_records/security) + +/obj/item/cartridge/clown + name = "Honkworks 5.0" + icon_state = "cart-clown" + charges = 5 + programs = list(new/datum/data/pda/utility/honk) + messenger_plugins = list(new/datum/data/pda/messenger_plugin/virus/clown) + +/obj/item/cartridge/mime + name = "Gestur-O 1000" + icon_state = "cart-mi" + charges = 5 + messenger_plugins = list(new/datum/data/pda/messenger_plugin/virus/mime) + +/* +/obj/item/cartridge/botanist + name = "Green Thumb v4.20" + icon_state = "cart-b" + access_flora = 1 +*/ + +/obj/item/cartridge/signal + name = "generic signaler cartridge" + desc = "A data cartridge with an integrated radio signaler module." + programs = list(new/datum/data/pda/app/signaller) + +/obj/item/cartridge/signal/Initialize(mapload) + . = ..() + radio = new /obj/item/integrated_radio/signal(src) + +/obj/item/cartridge/signal/toxins + name = "Signal Ace 2" + desc = "Complete with integrated radio signaler!" + icon_state = "cart-tox" + programs = list( + new/datum/data/pda/utility/scanmode/gas, + + new/datum/data/pda/utility/scanmode/reagent, + + new/datum/data/pda/app/signaller) + +/obj/item/cartridge/quartermaster + name = "Space Parts & Space Vendors Cartridge" + desc = "Perfect for the Quartermaster on the go!" + icon_state = "cart-q" + programs = list( + new/datum/data/pda/app/supply, + new/datum/data/pda/app/mule_control) + +/obj/item/cartridge/quartermaster/Initialize(mapload) + . = ..() + radio = new /obj/item/integrated_radio/mule(src) + +/obj/item/cartridge/head + name = "Easy-Record DELUXE" + icon_state = "cart-h" + programs = list(new/datum/data/pda/app/status_display) + +/obj/item/cartridge/hop + name = "HumanResources9001" + icon_state = "cart-h" + programs = list( + new/datum/data/pda/app/crew_records/security, + + new/datum/data/pda/app/janitor, + + new/datum/data/pda/app/supply, + new/datum/data/pda/app/mule_control, + + new/datum/data/pda/app/status_display) + +/obj/item/cartridge/hop/Initialize(mapload) + . = ..() + radio = new /obj/item/integrated_radio/mule(src) + +/obj/item/cartridge/hos + name = "R.O.B.U.S.T. DELUXE" + icon_state = "cart-hos" + programs = list( + new/datum/data/pda/app/crew_records/security, + new/datum/data/pda/app/secbot_control, + + new/datum/data/pda/app/status_display) + +/obj/item/cartridge/hos/Initialize(mapload) + . = ..() + radio = new /obj/item/integrated_radio/beepsky(src) + +/obj/item/cartridge/ce + name = "Power-On DELUXE" + icon_state = "cart-ce" + programs = list( + new/datum/data/pda/app/power, + new/datum/data/pda/utility/scanmode/halogen, + + new/datum/data/pda/utility/scanmode/gas, + + new/datum/data/pda/app/status_display) + +/obj/item/cartridge/cmo + name = "Med-U DELUXE" + icon_state = "cart-cmo" + programs = list( + new/datum/data/pda/app/crew_records/medical, + new/datum/data/pda/utility/scanmode/medical, + + new/datum/data/pda/utility/scanmode/reagent, + + new/datum/data/pda/app/status_display) + +/obj/item/cartridge/rd + name = "Signal Ace DELUXE" + icon_state = "cart-rd" + programs = list( + new/datum/data/pda/utility/scanmode/gas, + + new/datum/data/pda/utility/scanmode/reagent, + + new/datum/data/pda/app/signaller, + + new/datum/data/pda/app/status_display) + +/obj/item/cartridge/rd/Initialize(mapload) + . = ..() + radio = new /obj/item/integrated_radio/signal(src) + +/obj/item/cartridge/captain + name = "Value-PAK Cartridge" + desc = "Now with 200% more value!" + icon_state = "cart-c" + programs = list( + new/datum/data/pda/app/power, + new/datum/data/pda/utility/scanmode/halogen, + + new/datum/data/pda/utility/scanmode/gas, + + new/datum/data/pda/app/crew_records/medical, + new/datum/data/pda/utility/scanmode/medical, + + new/datum/data/pda/utility/scanmode/reagent, + + new/datum/data/pda/app/crew_records/security, + new/datum/data/pda/app/secbot_control, + + new/datum/data/pda/app/janitor, + + new/datum/data/pda/app/supply, + new/datum/data/pda/app/mule_control, + + new/datum/data/pda/app/status_display) + +/obj/item/cartridge/captain/Initialize(mapload) + . = ..() + radio = new /obj/item/integrated_radio/beepsky(src) + +/obj/item/cartridge/supervisor + name = "Easy-Record DELUXE" + icon_state = "cart-h" + programs = list( + new/datum/data/pda/app/crew_records/security, + + new/datum/data/pda/app/status_display) + +/obj/item/cartridge/centcom + name = "Value-PAK Cartridge" + desc = "Now with 200% more value!" + icon_state = "cart-c" + programs = list( + new/datum/data/pda/app/power, + new/datum/data/pda/utility/scanmode/halogen, + + new/datum/data/pda/utility/scanmode/gas, + + new/datum/data/pda/app/crew_records/medical, + new/datum/data/pda/utility/scanmode/medical, + + new/datum/data/pda/utility/scanmode/reagent, + + new/datum/data/pda/app/crew_records/security, + new/datum/data/pda/app/secbot_control, + + new/datum/data/pda/app/janitor, + + new/datum/data/pda/app/supply, + new/datum/data/pda/app/mule_control, + + new/datum/data/pda/app/status_display) + +/obj/item/cartridge/centcom/Initialize(mapload) + . = ..() + radio = new /obj/item/integrated_radio/beepsky(src) + +/obj/item/cartridge/syndicate + name = "Detomatix Cartridge" + icon_state = "cart" + var/initial_remote_door_id = "smindicate" //Make sure this matches the syndicate shuttle's shield/door id!! //don't ask about the name, testing. + charges = 4 + programs = list(new/datum/data/pda/utility/toggle_door) + messenger_plugins = list(new/datum/data/pda/messenger_plugin/virus/detonate) + +/obj/item/cartridge/syndicate/Initialize(mapload) + . = ..() + var/datum/data/pda/utility/toggle_door/D = programs[1] + if(istype(D)) + D.remote_door_id = initial_remote_door_id + +/obj/item/cartridge/frame + name = "F.R.A.M.E. cartridge" + icon_state = "cart" + charges = 5 + var/telecrystals = 0 + messenger_plugins = list(new/datum/data/pda/messenger_plugin/virus/frame) + +/obj/item/cartridge/mob_hunt_game + name = "Nano-Mob Hunter GO! Cartridge" + desc = "The hit new PDA game that lets you track down and capture your favorite Nano-Mobs living in your world!" + icon_state = "cart-eye" + programs = list(new/datum/data/pda/app/mob_hunter_game) + var/emagged = 0 + +/obj/item/cartridge/mob_hunt_game/attackby(obj/item/O, mob/user, params) + if(istype(O, /obj/item/nanomob_card)) + var/obj/item/nanomob_card/card = O + var/datum/data/pda/app/mob_hunter_game/my_game = programs[1] + if(my_game.register_capture(card.mob_data)) + to_chat(user, "Transfer successful!") + qdel(card) + else + to_chat(user, "Transfer failed. Could not read mob data from card.") + else + ..() + +/obj/item/cartridge/mob_hunt_game/emag_act(mob/user) + if(!emagged) + emagged = 1 + var/datum/data/pda/app/mob_hunter_game/my_game = programs[1] + my_game.hacked = 1 + to_chat(user, "TR4P_M45T3R.mod successfully initialized. ToS violated. User Agreement nullified. Gotta pwn them all.") + to_chat(user, "You can now create trapped versions of any mob in your collection that will damage hunters who attempt to capture it.") + description_antag = "This copy of Nano-Mob Hunter GO! has been hacked to allow the creation of trap mobs which will cause any PDA that attempts to capture it to shock anyone holding it. Hacked copies of the game will not trigger the trap." diff --git a/code/modules/pda/core_apps.dm b/code/modules/pda/core_apps.dm index 2b5ce32355d..cd86e620970 100644 --- a/code/modules/pda/core_apps.dm +++ b/code/modules/pda/core_apps.dm @@ -108,4 +108,4 @@ "reading" = 1 ) if(isnull(data["aircontents"])) - data["aircontents"] = list("reading" = 0) \ No newline at end of file + data["aircontents"] = list("reading" = 0) diff --git a/code/modules/pda/messenger_plugins.dm b/code/modules/pda/messenger_plugins.dm index 2666b82c5df..c69d8890164 100644 --- a/code/modules/pda/messenger_plugins.dm +++ b/code/modules/pda/messenger_plugins.dm @@ -88,4 +88,4 @@ var/obj/item/cartridge/frame/parent_cart = pda.cartridge P.hidden_uplink.uses = parent_cart.telecrystals parent_cart.telecrystals = 0 - P.hidden_uplink.active = TRUE \ No newline at end of file + P.hidden_uplink.active = TRUE diff --git a/code/modules/pda/mob_hunt_game_app.dm b/code/modules/pda/mob_hunt_game_app.dm index 2bb28ba0264..c48ebdd4e9e 100644 --- a/code/modules/pda/mob_hunt_game_app.dm +++ b/code/modules/pda/mob_hunt_game_app.dm @@ -173,4 +173,4 @@ if("Transfer") print_card() if("Set_Trap") - set_trap() \ No newline at end of file + set_trap() diff --git a/code/modules/pda/pdas.dm b/code/modules/pda/pdas.dm index cb7b7731274..0e5c58f968a 100644 --- a/code/modules/pda/pdas.dm +++ b/code/modules/pda/pdas.dm @@ -200,16 +200,16 @@ icon_state = "pdabox" /obj/item/storage/box/PDAs/New() - ..() - new /obj/item/pda(src) - new /obj/item/pda(src) - new /obj/item/pda(src) - new /obj/item/pda(src) - new /obj/item/cartridge/head(src) + ..() + new /obj/item/pda(src) + new /obj/item/pda(src) + new /obj/item/pda(src) + new /obj/item/pda(src) + new /obj/item/cartridge/head(src) - var/newcart = pick( /obj/item/cartridge/engineering, - /obj/item/cartridge/security, - /obj/item/cartridge/medical, - /obj/item/cartridge/signal/toxins, - /obj/item/cartridge/quartermaster) - new newcart(src) + var/newcart = pick( /obj/item/cartridge/engineering, + /obj/item/cartridge/security, + /obj/item/cartridge/medical, + /obj/item/cartridge/signal/toxins, + /obj/item/cartridge/quartermaster) + new newcart(src) diff --git a/code/modules/pda/radio.dm b/code/modules/pda/radio.dm index a49305598e6..3a2f4901caa 100644 --- a/code/modules/pda/radio.dm +++ b/code/modules/pda/radio.dm @@ -1,208 +1,208 @@ -//TODO convert this crap over to proper radios or find a way to utilize regualr radios for this object, this thing needs to go. - -/obj/item/integrated_radio - name = "\improper PDA radio module" - desc = "An electronic radio system of Nanotrasen origin." - icon = 'icons/obj/module.dmi' - icon_state = "power_mod" - var/obj/item/pda/hostpda = null - var/list/botlist = null // list of bots - var/mob/living/simple_animal/bot/active // the active bot; if null, show bot list - var/list/botstatus // the status signal sent by the bot - var/bot_type //The type of bot it is. - var/bot_filter //Determines which radio filter to use. - - var/control_freq = 1447 - - var/on = 0 //Are we currently active?? - var/menu_message = "" - -/obj/item/integrated_radio/Initialize(mapload) - . = ..() - if(istype(loc.loc, /obj/item/pda)) - hostpda = loc.loc - if(bot_filter) - add_to_radio(bot_filter) - -/obj/item/integrated_radio/Destroy() - if(SSradio) - SSradio.remove_object(src, control_freq) - hostpda = null - return ..() - -/obj/item/integrated_radio/proc/post_signal(var/freq, var/key, var/value, var/key2, var/value2, var/key3, var/value3,var/key4, var/value4, s_filter) - -// to_chat(world, "Post: [freq]: [key]=[value], [key2]=[value2]") - var/datum/radio_frequency/frequency = SSradio.return_frequency(freq) - - if(!frequency) - return - - var/datum/signal/signal = new() - signal.source = src - signal.transmission_method = 1 - signal.data[key] = value - if(key2) - signal.data[key2] = value2 - if(key3) - signal.data[key3] = value3 - if(key4) - signal.data[key4] = value4 - - frequency.post_signal(src, signal, filter = s_filter) - -/obj/item/integrated_radio/receive_signal(datum/signal/signal) - if(bot_type && istype(signal.source, /obj/machinery/bot_core) && signal.data["type"] == bot_type) - if(!botlist) - botlist = new() - - var/obj/machinery/bot_core/core = signal.source - - if(istype(core) && !(core.owner in botlist)) - botlist += core.owner - - if(active == core.owner) - var/list/b = signal.data - botstatus = b.Copy() - -/obj/item/integrated_radio/Topic(href, href_list) - ..() - switch(href_list["op"]) - if("control") - active = locate(href_list["bot"]) - spawn(0) - post_signal(control_freq, "command", "bot_status", "active", active, s_filter = bot_filter) - - if("scanbots") // find all bots - botlist = null - spawn(0) - post_signal(control_freq, "command", "bot_status", s_filter = bot_filter) - - if("botlist") - active = null - - if("stop", "go", "home") - spawn(0) - post_signal(control_freq, "command", href_list["op"], "active", active, s_filter = bot_filter) - post_signal(control_freq, "command", "bot_status", "active", active, s_filter = bot_filter) - - if("summon") - spawn(0) - post_signal(control_freq, "command", "summon", "active", active, "target", get_turf(hostpda), "useraccess", hostpda.GetAccess(), "user", usr, s_filter = bot_filter) - post_signal(control_freq, "command", "bot_status", "active", active, s_filter = bot_filter) - -/obj/item/integrated_radio/proc/add_to_radio(bot_filter) //Master filter control for bots. Must be placed in the bot's local New() to support map spawned bots. - if(SSradio) - SSradio.add_object(src, control_freq, filter = bot_filter) - -/obj/item/integrated_radio/honkbot - bot_filter = RADIO_HONKBOT - bot_type = HONK_BOT - -/obj/item/integrated_radio/beepsky - bot_filter = RADIO_SECBOT - bot_type = SEC_BOT - -/obj/item/integrated_radio/medbot - bot_filter = RADIO_MEDBOT - bot_type = MED_BOT - -/obj/item/integrated_radio/floorbot - bot_filter = RADIO_FLOORBOT - bot_type = FLOOR_BOT - -/obj/item/integrated_radio/cleanbot - bot_filter = RADIO_CLEANBOT - bot_type = CLEAN_BOT - -/obj/item/integrated_radio/mule - bot_filter = RADIO_MULEBOT - bot_type = MULE_BOT - -/obj/item/integrated_radio/mule/Topic(href, href_list) - ..() - switch(href_list["op"]) - if("start") - spawn(0) - post_signal(control_freq, "command", "start", "active", active, s_filter = RADIO_MULEBOT) - - if("unload") - spawn(0) - post_signal(control_freq, "command", "unload", "active", active, s_filter = RADIO_MULEBOT) - - if("setdest") - if(GLOB.deliverybeacons) - var/dest = input("Select Bot Destination", "Mulebot [active.suffix] Interlink", active.destination) as null|anything in GLOB.deliverybeacontags - if(dest) - spawn(0) - post_signal(control_freq, "command", "target", "active", active, "destination", dest, s_filter = RADIO_MULEBOT) - - if("retoff") - spawn(0) - post_signal(control_freq, "command", "autoret", "active", active, "value", 0, s_filter = RADIO_MULEBOT) - - if("reton") - spawn(0) - post_signal(control_freq, "command", "autoret", "active", active, "value", 1, s_filter = RADIO_MULEBOT) - - if("pickoff") - spawn(0) - post_signal(control_freq, "command", "autopick", "active", active, "value", 0, s_filter = RADIO_MULEBOT) - - if("pickon") - spawn(0) - post_signal(control_freq, "command", "autopick", "active", active, "value", 1, s_filter = RADIO_MULEBOT) - - spawn(10) - post_signal(control_freq, "command", "bot_status", "active", active, s_filter = RADIO_MULEBOT) - - - -/* - * Radio Cartridge, essentially a signaler. - */ - - -/obj/item/integrated_radio/signal - var/frequency = RSD_FREQ - var/code = 30.0 - var/last_transmission - var/datum/radio_frequency/radio_connection - -/obj/item/integrated_radio/signal/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - return ..() - -/obj/item/integrated_radio/signal/Initialize(mapload) - . = ..() - if(!SSradio) - return - if(src.frequency < PUBLIC_LOW_FREQ || src.frequency > PUBLIC_HIGH_FREQ) - src.frequency = sanitize_frequency(src.frequency) - - set_frequency(frequency) - -/obj/item/integrated_radio/signal/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - radio_connection = SSradio.add_object(src, frequency) - -/obj/item/integrated_radio/signal/proc/send_signal(message="ACTIVATE") - - if(last_transmission && world.time < (last_transmission + 5)) - return - last_transmission = world.time - - var/time = time2text(world.realtime,"hh:mm:ss") - var/turf/T = get_turf(src) - lastsignalers.Add("[time] : [usr.key] used [src] @ location ([T.x],[T.y],[T.z]) : [format_frequency(frequency)]/[code]") - - var/datum/signal/signal = new - signal.source = src - signal.encryption = code - signal.data["message"] = message - - spawn(0) - radio_connection.post_signal(src, signal) \ No newline at end of file +//TODO convert this crap over to proper radios or find a way to utilize regualr radios for this object, this thing needs to go. + +/obj/item/integrated_radio + name = "\improper PDA radio module" + desc = "An electronic radio system of Nanotrasen origin." + icon = 'icons/obj/module.dmi' + icon_state = "power_mod" + var/obj/item/pda/hostpda = null + var/list/botlist = null // list of bots + var/mob/living/simple_animal/bot/active // the active bot; if null, show bot list + var/list/botstatus // the status signal sent by the bot + var/bot_type //The type of bot it is. + var/bot_filter //Determines which radio filter to use. + + var/control_freq = 1447 + + var/on = 0 //Are we currently active?? + var/menu_message = "" + +/obj/item/integrated_radio/Initialize(mapload) + . = ..() + if(istype(loc.loc, /obj/item/pda)) + hostpda = loc.loc + if(bot_filter) + add_to_radio(bot_filter) + +/obj/item/integrated_radio/Destroy() + if(SSradio) + SSradio.remove_object(src, control_freq) + hostpda = null + return ..() + +/obj/item/integrated_radio/proc/post_signal(var/freq, var/key, var/value, var/key2, var/value2, var/key3, var/value3,var/key4, var/value4, s_filter) + +// to_chat(world, "Post: [freq]: [key]=[value], [key2]=[value2]") + var/datum/radio_frequency/frequency = SSradio.return_frequency(freq) + + if(!frequency) + return + + var/datum/signal/signal = new() + signal.source = src + signal.transmission_method = 1 + signal.data[key] = value + if(key2) + signal.data[key2] = value2 + if(key3) + signal.data[key3] = value3 + if(key4) + signal.data[key4] = value4 + + frequency.post_signal(src, signal, filter = s_filter) + +/obj/item/integrated_radio/receive_signal(datum/signal/signal) + if(bot_type && istype(signal.source, /obj/machinery/bot_core) && signal.data["type"] == bot_type) + if(!botlist) + botlist = new() + + var/obj/machinery/bot_core/core = signal.source + + if(istype(core) && !(core.owner in botlist)) + botlist += core.owner + + if(active == core.owner) + var/list/b = signal.data + botstatus = b.Copy() + +/obj/item/integrated_radio/Topic(href, href_list) + ..() + switch(href_list["op"]) + if("control") + active = locate(href_list["bot"]) + spawn(0) + post_signal(control_freq, "command", "bot_status", "active", active, s_filter = bot_filter) + + if("scanbots") // find all bots + botlist = null + spawn(0) + post_signal(control_freq, "command", "bot_status", s_filter = bot_filter) + + if("botlist") + active = null + + if("stop", "go", "home") + spawn(0) + post_signal(control_freq, "command", href_list["op"], "active", active, s_filter = bot_filter) + post_signal(control_freq, "command", "bot_status", "active", active, s_filter = bot_filter) + + if("summon") + spawn(0) + post_signal(control_freq, "command", "summon", "active", active, "target", get_turf(hostpda), "useraccess", hostpda.GetAccess(), "user", usr, s_filter = bot_filter) + post_signal(control_freq, "command", "bot_status", "active", active, s_filter = bot_filter) + +/obj/item/integrated_radio/proc/add_to_radio(bot_filter) //Master filter control for bots. Must be placed in the bot's local New() to support map spawned bots. + if(SSradio) + SSradio.add_object(src, control_freq, filter = bot_filter) + +/obj/item/integrated_radio/honkbot + bot_filter = RADIO_HONKBOT + bot_type = HONK_BOT + +/obj/item/integrated_radio/beepsky + bot_filter = RADIO_SECBOT + bot_type = SEC_BOT + +/obj/item/integrated_radio/medbot + bot_filter = RADIO_MEDBOT + bot_type = MED_BOT + +/obj/item/integrated_radio/floorbot + bot_filter = RADIO_FLOORBOT + bot_type = FLOOR_BOT + +/obj/item/integrated_radio/cleanbot + bot_filter = RADIO_CLEANBOT + bot_type = CLEAN_BOT + +/obj/item/integrated_radio/mule + bot_filter = RADIO_MULEBOT + bot_type = MULE_BOT + +/obj/item/integrated_radio/mule/Topic(href, href_list) + ..() + switch(href_list["op"]) + if("start") + spawn(0) + post_signal(control_freq, "command", "start", "active", active, s_filter = RADIO_MULEBOT) + + if("unload") + spawn(0) + post_signal(control_freq, "command", "unload", "active", active, s_filter = RADIO_MULEBOT) + + if("setdest") + if(GLOB.deliverybeacons) + var/dest = input("Select Bot Destination", "Mulebot [active.suffix] Interlink", active.destination) as null|anything in GLOB.deliverybeacontags + if(dest) + spawn(0) + post_signal(control_freq, "command", "target", "active", active, "destination", dest, s_filter = RADIO_MULEBOT) + + if("retoff") + spawn(0) + post_signal(control_freq, "command", "autoret", "active", active, "value", 0, s_filter = RADIO_MULEBOT) + + if("reton") + spawn(0) + post_signal(control_freq, "command", "autoret", "active", active, "value", 1, s_filter = RADIO_MULEBOT) + + if("pickoff") + spawn(0) + post_signal(control_freq, "command", "autopick", "active", active, "value", 0, s_filter = RADIO_MULEBOT) + + if("pickon") + spawn(0) + post_signal(control_freq, "command", "autopick", "active", active, "value", 1, s_filter = RADIO_MULEBOT) + + spawn(10) + post_signal(control_freq, "command", "bot_status", "active", active, s_filter = RADIO_MULEBOT) + + + +/* + * Radio Cartridge, essentially a signaler. + */ + + +/obj/item/integrated_radio/signal + var/frequency = RSD_FREQ + var/code = 30.0 + var/last_transmission + var/datum/radio_frequency/radio_connection + +/obj/item/integrated_radio/signal/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + return ..() + +/obj/item/integrated_radio/signal/Initialize(mapload) + . = ..() + if(!SSradio) + return + if(src.frequency < PUBLIC_LOW_FREQ || src.frequency > PUBLIC_HIGH_FREQ) + src.frequency = sanitize_frequency(src.frequency) + + set_frequency(frequency) + +/obj/item/integrated_radio/signal/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + radio_connection = SSradio.add_object(src, frequency) + +/obj/item/integrated_radio/signal/proc/send_signal(message="ACTIVATE") + + if(last_transmission && world.time < (last_transmission + 5)) + return + last_transmission = world.time + + var/time = time2text(world.realtime,"hh:mm:ss") + var/turf/T = get_turf(src) + lastsignalers.Add("[time] : [usr.key] used [src] @ location ([T.x],[T.y],[T.z]) : [format_frequency(frequency)]/[code]") + + var/datum/signal/signal = new + signal.source = src + signal.encryption = code + signal.data["message"] = message + + spawn(0) + radio_connection.post_signal(src, signal) diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index 4d7dafa69bf..3757d7e8cd1 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -1,1375 +1,1384 @@ -#define APC_WIRE_IDSCAN 1 -#define APC_WIRE_MAIN_POWER1 2 -#define APC_WIRE_MAIN_POWER2 3 -#define APC_WIRE_AI_CONTROL 4 - -//update_state -#define UPSTATE_CELL_IN 1 -#define UPSTATE_OPENED1 2 -#define UPSTATE_OPENED2 4 -#define UPSTATE_MAINT 8 -#define UPSTATE_BROKE 16 -#define UPSTATE_BLUESCREEN 32 -#define UPSTATE_WIREEXP 64 -#define UPSTATE_ALLGOOD 128 - -//update_overlay -#define APC_UPOVERLAY_CHARGEING0 1 -#define APC_UPOVERLAY_CHARGEING1 2 -#define APC_UPOVERLAY_CHARGEING2 4 -#define APC_UPOVERLAY_EQUIPMENT0 8 -#define APC_UPOVERLAY_EQUIPMENT1 16 -#define APC_UPOVERLAY_EQUIPMENT2 32 -#define APC_UPOVERLAY_LIGHTING0 64 -#define APC_UPOVERLAY_LIGHTING1 128 -#define APC_UPOVERLAY_LIGHTING2 256 -#define APC_UPOVERLAY_ENVIRON0 512 -#define APC_UPOVERLAY_ENVIRON1 1024 -#define APC_UPOVERLAY_ENVIRON2 2048 -#define APC_UPOVERLAY_LOCKED 4096 - -#define APC_UPDATE_ICON_COOLDOWN 200 // 20 seconds - -// APC malf status -#define APC_MALF_NOT_HACKED 1 -#define APC_MALF_HACKED 2 // APC hacked by user, and user is in its core. -#define APC_MALF_SHUNTED_HERE 3 // User is shunted in this APC -#define APC_MALF_SHUNTED_OTHER 4 // User is shunted in another APC - -// the Area Power Controller (APC), formerly Power Distribution Unit (PDU) -// one per area, needs wire conection to power network through a terminal - -// controls power to devices in that area -// may be opened to change power cell -// three different channels (lighting/equipment/environ) - may each be set to on, off, or auto - - -//NOTE: STUFF STOLEN FROM AIRLOCK.DM thx - - -/obj/machinery/power/apc - name = "area power controller" - desc = "A control terminal for the area electrical systems." - icon_state = "apc0" - use_power = NO_POWER_USE - max_integrity = 200 - integrity_failure = 50 - resistance_flags = FIRE_PROOF - req_access = list(access_engine_equip) - siemens_strength = 1 - damage_deflection = 10 - var/area/area - var/areastring = null - var/obj/item/stock_parts/cell/cell - var/start_charge = 90 // initial cell charge % - var/cell_type = 2500 //Base cell has 2500 capacity. Enter the path of a different cell you want to use. cell determines charge rates, max capacity, ect. These can also be changed with other APC vars, but isn't recommended to minimize the risk of accidental usage of dirty editted APCs - var/opened = 0 //0=closed, 1=opened, 2=cover removed - var/shorted = 0 - var/lighting = 3 - var/equipment = 3 - var/environ = 3 - var/operating = 1 - var/charging = 0 - var/chargemode = 1 - var/chargecount = 0 - var/locked = 1 - var/coverlocked = 1 - var/aidisabled = 0 - var/tdir = null - var/obj/machinery/power/terminal/terminal = null - var/lastused_light = 0 - var/lastused_equip = 0 - var/lastused_environ = 0 - var/lastused_total = 0 - var/main_status = 0 - powernet = 0 // set so that APCs aren't found as powernet nodes //Hackish, Horrible, was like this before I changed it :( - var/malfhack = 0 //New var for my changes to AI malf. --NeoFite - var/mob/living/silicon/ai/malfai = null //See above --NeoFite - var/debug= 0 - var/autoflag= 0 // 0 = off, 1= eqp and lights off, 2 = eqp off, 3 = all on. -// luminosity = 1 - var/has_electronics = 0 // 0 - none, 1 - plugged in, 2 - secured by screwdriver - var/overload = 1 //used for the Blackout malf module - var/beenhit = 0 // used for counting how many times it has been hit, used for Aliens at the moment - var/mob/living/silicon/ai/occupier = null - var/longtermpower = 10 - var/update_state = -1 - var/update_overlay = -1 - var/global/status_overlays = 0 - var/updating_icon = 0 - var/datum/wires/apc/wires = null - //var/debug = 0 - var/global/list/status_overlays_lock - var/global/list/status_overlays_charging - var/global/list/status_overlays_equipment - var/global/list/status_overlays_lighting - var/global/list/status_overlays_environ - var/indestructible = 0 // If set, prevents aliens from destroying it - var/keep_preset_name = 0 - - var/report_power_alarm = TRUE - - var/shock_proof = 0 //if set to 1, this APC will not arc bolts of electricity if it's overloaded. - - // Nightshift - var/nightshift_lights = FALSE - var/last_nightshift_switch = 0 - -/obj/machinery/power/apc/worn_out - name = "\improper Worn out APC" - keep_preset_name = 1 - locked = 0 - environ = 0 - equipment = 0 - lighting = 0 - operating = 0 - -/obj/machinery/power/apc/noalarm - report_power_alarm = FALSE - -/obj/machinery/power/apc/syndicate //general syndicate access - req_access = list(access_syndicate) - report_power_alarm = FALSE - -/obj/item/apc_electronics - name = "power control module" - desc = "Heavy-duty switching circuits for power control." - icon = 'icons/obj/module.dmi' - icon_state = "power_mod" - w_class = WEIGHT_CLASS_SMALL - origin_tech = "engineering=2;programming=1" - item_state = "electronic" - flags = CONDUCT - usesound = 'sound/items/deconstruct.ogg' - toolspeed = 1 - -/obj/machinery/power/apc/get_cell() - return cell - -/obj/machinery/power/apc/connect_to_network() - //Override because the APC does not directly connect to the network; it goes through a terminal. - //The terminal is what the power computer looks for anyway. - if(terminal) - terminal.connect_to_network() - -/obj/machinery/power/apc/New(turf/loc, ndir, building = 0) - if(!armor) - armor = list("melee" = 20, "bullet" = 20, "laser" = 10, "energy" = 100, "bomb" = 30, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 50) - ..() - GLOB.apcs += src - GLOB.apcs = sortAtom(GLOB.apcs) - - wires = new(src) - // offset 24 pixels in direction of dir - // this allows the APC to be embedded in a wall, yet still inside an area - if(building) - setDir(ndir) - tdir = dir // to fix Vars bug - setDir(SOUTH) - - pixel_x = (src.tdir & 3)? 0 : (src.tdir == 4 ? 24 : -24) - pixel_y = (src.tdir & 3)? (src.tdir ==1 ? 24 : -24) : 0 - if(building) - area = get_area(src) - area.apc |= src - opened = 1 - operating = 0 - name = "[area.name] APC" - stat |= MAINT - update_icon() - addtimer(CALLBACK(src, .proc/update), 5) - -/obj/machinery/power/apc/Destroy() - GLOB.apcs -= src - if(malfai && operating) - malfai.malf_picker.processing_time = Clamp(malfai.malf_picker.processing_time - 10,0,1000) - area.power_light = 0 - area.power_equip = 0 - area.power_environ = 0 - area.power_change() - if(occupier) - malfvacate(1) - QDEL_NULL(wires) - QDEL_NULL(cell) - if(terminal) - disconnect_terminal() - area.apc -= src - return ..() - -/obj/machinery/power/apc/proc/make_terminal() - // create a terminal object at the same position as original turf loc - // wires will attach to this - terminal = new/obj/machinery/power/terminal(src.loc) - terminal.setDir(tdir) - terminal.master = src - -/obj/machinery/power/apc/Initialize(mapload) - . = ..() - if(!mapload) - return - has_electronics = 2 //installed and secured - // is starting with a power cell installed, create it and set its charge level - if(cell_type) - cell = new/obj/item/stock_parts/cell/upgraded(src) - cell.maxcharge = cell_type // cell_type is maximum charge (old default was 1000 or 2500 (values one and two respectively) - cell.charge = start_charge * cell.maxcharge / 100 // (convert percentage to actual value) - - var/area/A = get_area(src) - - - //if area isn't specified use current - if(keep_preset_name) - if(isarea(A)) - area = A - // no-op, keep the name - else if(isarea(A) && src.areastring == null) - area = A - name = "\improper [area.name] APC" - else - area = get_area_name(areastring) - name = "\improper [area.name] APC" - area.apc |= src - - update_icon() - - make_terminal() - - addtimer(CALLBACK(src, .proc/update), 5) - -/obj/machinery/power/apc/examine(mob/user) - . = ..() - if(in_range(user, src)) - if(stat & BROKEN) - . += "Looks broken." - else if(opened) - if(has_electronics && terminal) - . += "The cover is [opened==2?"removed":"open"] and the power cell is [ cell ? "installed" : "missing"]." - else if(!has_electronics && terminal) - . += "There are some wires but no electronics." - else if(has_electronics && !terminal) - . += "Electronics installed but not wired." - else /* if(!has_electronics && !terminal) */ - . += "There is no electronics nor connected wires." - else - if(stat & MAINT) - . += "The cover is closed. Something wrong with it: it doesn't work." - else if(malfhack) - . += "The cover is broken. It may be hard to force it open." - else - . += "The cover is closed." - -// update the APC icon to show the three base states -// also add overlays for indicator lights -/obj/machinery/power/apc/update_icon(force_update = FALSE) - - if(!status_overlays || force_update) - status_overlays = 1 - status_overlays_lock = new - status_overlays_charging = new - status_overlays_equipment = new - status_overlays_lighting = new - status_overlays_environ = new - - status_overlays_lock.len = 2 - status_overlays_charging.len = 3 - status_overlays_equipment.len = 4 - status_overlays_lighting.len = 4 - status_overlays_environ.len = 4 - - status_overlays_lock[1] = image(icon, "apcox-0") // 0=blue 1=red - status_overlays_lock[2] = image(icon, "apcox-1") - - status_overlays_charging[1] = image(icon, "apco3-0") - status_overlays_charging[2] = image(icon, "apco3-1") - status_overlays_charging[3] = image(icon, "apco3-2") - - status_overlays_equipment[1] = image(icon, "apco0-0") // 0=red, 1=green, 2=blue - status_overlays_equipment[2] = image(icon, "apco0-1") - status_overlays_equipment[3] = image(icon, "apco0-2") - status_overlays_equipment[4] = image(icon, "apco0-3") - - status_overlays_lighting[1] = image(icon, "apco1-0") - status_overlays_lighting[2] = image(icon, "apco1-1") - status_overlays_lighting[3] = image(icon, "apco1-2") - status_overlays_lighting[4] = image(icon, "apco1-3") - - status_overlays_environ[1] = image(icon, "apco2-0") - status_overlays_environ[2] = image(icon, "apco2-1") - status_overlays_environ[3] = image(icon, "apco2-2") - status_overlays_environ[4] = image(icon, "apco2-3") - - - - var/update = check_updates() //returns 0 if no need to update icons. - // 1 if we need to update the icon_state - // 2 if we need to update the overlays - if(!update && !force_update) - return - - if(force_update || update & 1) // Updating the icon state - if(update_state & UPSTATE_ALLGOOD) - icon_state = "apc0" - else if(update_state & (UPSTATE_OPENED1|UPSTATE_OPENED2)) - var/basestate = "apc[ cell ? "2" : "1" ]" - if(update_state & UPSTATE_OPENED1) - if(update_state & (UPSTATE_MAINT|UPSTATE_BROKE)) - icon_state = "apcmaint" //disabled APC cannot hold cell - else - icon_state = basestate - else if(update_state & UPSTATE_OPENED2) - icon_state = "[basestate]-nocover" - else if(update_state & UPSTATE_BROKE) - icon_state = "apc-b" - else if(update_state & UPSTATE_BLUESCREEN) - icon_state = "apcemag" - else if(update_state & UPSTATE_WIREEXP) - icon_state = "apcewires" - - - - if(!(update_state & UPSTATE_ALLGOOD)) - if(overlays.len) - overlays = 0 - return - - - - if(force_update || update & 2) - - if(overlays.len) - overlays.len = 0 - - if(!(stat & (BROKEN|MAINT)) && update_state & UPSTATE_ALLGOOD) - overlays += status_overlays_lock[locked+1] - overlays += status_overlays_charging[charging+1] - if(operating) - overlays += status_overlays_equipment[equipment+1] - overlays += status_overlays_lighting[lighting+1] - overlays += status_overlays_environ[environ+1] - - -/obj/machinery/power/apc/proc/check_updates() - - var/last_update_state = update_state - var/last_update_overlay = update_overlay - update_state = 0 - update_overlay = 0 - - if(cell) - update_state |= UPSTATE_CELL_IN - if(stat & BROKEN) - update_state |= UPSTATE_BROKE - if(stat & MAINT) - update_state |= UPSTATE_MAINT - if(opened) - if(opened==1) - update_state |= UPSTATE_OPENED1 - if(opened==2) - update_state |= UPSTATE_OPENED2 - else if(emagged || malfai) - update_state |= UPSTATE_BLUESCREEN - else if(panel_open) - update_state |= UPSTATE_WIREEXP - if(update_state <= 1) - update_state |= UPSTATE_ALLGOOD - - if(update_state & UPSTATE_ALLGOOD) - if(locked) - update_overlay |= APC_UPOVERLAY_LOCKED - - if(!charging) - update_overlay |= APC_UPOVERLAY_CHARGEING0 - else if(charging == 1) - update_overlay |= APC_UPOVERLAY_CHARGEING1 - else if(charging == 2) - update_overlay |= APC_UPOVERLAY_CHARGEING2 - - if(!equipment) - update_overlay |= APC_UPOVERLAY_EQUIPMENT0 - else if(equipment == 1) - update_overlay |= APC_UPOVERLAY_EQUIPMENT1 - else if(equipment == 2) - update_overlay |= APC_UPOVERLAY_EQUIPMENT2 - - if(!lighting) - update_overlay |= APC_UPOVERLAY_LIGHTING0 - else if(lighting == 1) - update_overlay |= APC_UPOVERLAY_LIGHTING1 - else if(lighting == 2) - update_overlay |= APC_UPOVERLAY_LIGHTING2 - - if(!environ) - update_overlay |= APC_UPOVERLAY_ENVIRON0 - else if(environ==1) - update_overlay |= APC_UPOVERLAY_ENVIRON1 - else if(environ==2) - update_overlay |= APC_UPOVERLAY_ENVIRON2 - - var/results = 0 - if(last_update_state == update_state && last_update_overlay == update_overlay) - return 0 - if(last_update_state != update_state) - results += 1 - if(last_update_overlay != update_overlay) - results += 2 - return results - -// Used in process so it doesn't update the icon too much -/obj/machinery/power/apc/proc/queue_icon_update() - - if(!updating_icon) - updating_icon = 1 - // Start the update - spawn(APC_UPDATE_ICON_COOLDOWN) - update_icon() - updating_icon = 0 - -/obj/machinery/power/apc/get_spooked(second_pass = FALSE) - if(opened || panel_open) - return - if(stat & (NOPOWER | BROKEN)) - return - if(!second_pass) //The first time, we just cut overlays - addtimer(CALLBACK(src, .get_spooked, TRUE), 1) - cut_overlays() - else - flick("apcemag", src) //Second time we cause the APC to update its icon, then add a timer to update icon later - addtimer(CALLBACK(src, .proc/update_icon, TRUE), 10) - -//attack with an item - open/close cover, insert cell, or (un)lock interface -/obj/machinery/power/apc/attackby(obj/item/W, mob/living/user, params) - - if(issilicon(user) && get_dist(src,user)>1) - return src.attack_hand(user) - if(istype(W, /obj/item/crowbar)) //Using crowbar - if(opened) // a) on open apc - if(has_electronics==1) - if(terminal) - to_chat(user, "Disconnect the wires first!") - return - playsound(src.loc, W.usesound, 50, 1) - to_chat(user, "You are trying to remove the power control board..." ) - if(do_after(user, 50*W.toolspeed, target = src)) - if(has_electronics==1) - has_electronics = 0 - if(stat & BROKEN) - user.visible_message(\ - "[user.name] has broken the power control board inside [src.name]!", - "You break the charred power control board and remove the remains.", - "You hear a crack.") - return - //SSticker.mode:apcs-- //XSI said no and I agreed. -rastaf0 - else if(emagged) // We emag board, not APC's frame - emagged = FALSE - user.visible_message( - "[user.name] has discarded emaged power control board from [src.name]!", - "You discarded shorten board.") - return - else if(malfhack) // AI hacks board, not APC's frame - user.visible_message(\ - "[user.name] has discarded strangely programmed power control board from [src.name]!", - "You discarded strangely programmed board.") - malfai = null - malfhack = 0 - return - else - user.visible_message(\ - "[user.name] has removed the power control board from [src.name]!", - "You remove the power control board.") - new /obj/item/apc_electronics(loc) - return - else if(opened!=2) //cover isn't removed - opened = 0 - coverlocked = TRUE //closing cover relocks it - update_icon() - return - else if(!(stat & BROKEN)) // b) on closed and not broken APC - if(coverlocked && !(stat & MAINT)) // locked... - to_chat(user, "The cover is locked and cannot be opened!") - return - else if(panel_open) // wires are exposed - to_chat(user, "Exposed wires prevents you from opening it!") - return - else - opened = 1 - update_icon() - return - - else if (istype(W, /obj/item/stock_parts/cell) && opened) // trying to put a cell inside - if(cell) - to_chat(user, "There is a power cell already installed!") - return - else - if(stat & MAINT) - to_chat(user, "There is no connector for your power cell!") - return - if(!user.drop_item()) - return - W.forceMove(src) - cell = W - user.visible_message(\ - "[user.name] has inserted the power cell to [src.name]!",\ - "You insert the power cell.") - chargecount = 0 - update_icon() - - else if(istype(W, /obj/item/screwdriver)) // haxing - if(opened) - if(cell) - to_chat(user, "Close the APC first!") //Less hints more mystery! - return - else - if(has_electronics==1) - has_electronics = 2 - stat &= ~MAINT - playsound(src.loc, W.usesound, 50, 1) - to_chat(user, "You screw the circuit electronics into place.") - else if(has_electronics==2) - has_electronics = 1 - stat |= MAINT - playsound(src.loc, W.usesound, 50, 1) - to_chat(user, "You unfasten the electronics.") - else /* has_electronics==0 */ - to_chat(user, "There is nothing to secure!") - return - update_icon() - else if(emagged) - to_chat(user, "The interface is broken!") - else if((stat & MAINT) && !opened) - ..() //its an empty closed frame... theres no wires to expose! - else - panel_open = !panel_open - to_chat(user, "The wires have been [panel_open ? "exposed" : "unexposed"]") - update_icon() - - else if(W.GetID()) // trying to unlock the interface with an ID card - togglelock(user) - - else if(istype(W, /obj/item/stack/cable_coil) && opened) - var/turf/host_turf = get_turf(src) - if(!host_turf) - throw EXCEPTION("attackby on APC when it's not on a turf") - return - if(host_turf.intact) - to_chat(user, "You must remove the floor plating in front of the APC first!") - return - else if(terminal) // it already have terminal - to_chat(user, "This APC is already wired!") - return - else if(has_electronics == 0) - to_chat(user, "There is nothing to wire!") - return - - var/obj/item/stack/cable_coil/C = W - if(C.get_amount() < 10) - to_chat(user, "You need ten lengths of cable for APC!") - return - user.visible_message("[user.name] adds cables to the APC frame.", \ - "You start adding cables to the APC frame...") - playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1) - if(do_after(user, 20, target = src)) - if(C.get_amount() < 10 || !C) - return - if(C.get_amount() >= 10 && !terminal && opened && has_electronics > 0) - var/turf/T = get_turf(src) - var/obj/structure/cable/N = T.get_cable_node() - if(prob(50) && electrocute_mob(usr, N, N, 1, TRUE)) - do_sparks(5, TRUE, src) - return - C.use(10) - to_chat(user, "You add cables to the APC frame.") - make_terminal() - terminal.connect_to_network() - - else if(istype(W, /obj/item/wirecutters) && terminal && opened) - terminal.dismantle(user, W) - - else if(istype(W, /obj/item/apc_electronics) && opened) - if(has_electronics!=0) // there are already electronicks inside - to_chat(user, "You cannot put the board inside, there already is one!") - return - else if(stat & BROKEN) - to_chat(user, "You cannot put the board inside, the frame is damaged!") - return - - user.visible_message("[user.name] inserts the power control board into [src].", \ - "You start to insert the power control board into the frame...") - playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1) - if(do_after(user, 10, target = src)) - if(has_electronics==0) - has_electronics = 1 - locked = FALSE - to_chat(user, "You place the power control board inside the frame.") - qdel(W) - - else if(istype(W, /obj/item/weldingtool) && opened && has_electronics==0 && !terminal) - var/obj/item/weldingtool/WT = W - if(WT.get_fuel() < 3) - to_chat(user, "You need more welding fuel to complete this task!") - return - user.visible_message("[user.name] welds [src].", \ - "You start welding the APC frame...", \ - "You hear welding.") - playsound(src.loc, WT.usesound, 50, 1) - if(do_after(user, 50*W.toolspeed, target = src)) - if(!src || !WT.remove_fuel(3, user)) - return - if((stat & BROKEN) || opened==2) - new /obj/item/stack/sheet/metal(loc) - user.visible_message(\ - "[user.name] has cut [src] apart with [W].",\ - "You disassembled the broken APC frame.") - else - new /obj/item/mounted/frame/apc_frame(loc) - user.visible_message(\ - "[user.name] has cut [src] from the wall with [W].",\ - "You cut the APC frame from the wall.") - qdel(src) - return - - else if(istype(W, /obj/item/mounted/frame/apc_frame) && opened) - if(!(stat & BROKEN || opened==2 || obj_integrity < max_integrity)) // There is nothing to repair - to_chat(user, "You found no reason for repairing this APC") - return - if(!(stat & BROKEN) && opened==2) // Cover is the only thing broken, we do not need to remove elctronicks to replace cover - user.visible_message("[user.name] replaces missing APC's cover.",\ - "You begin to replace APC's cover...") - if(do_after(user, 20, target = src)) // replacing cover is quicker than replacing whole frame - to_chat(user, "You replace missing APC's cover.") - qdel(W) - opened = 1 - update_icon() - return - if(has_electronics) - to_chat(user, "You cannot repair this APC until you remove the electronics still inside!") - return - user.visible_message("[user.name] replaces the damaged APC frame with a new one.",\ - "You begin to replace the damaged APC frame...") - if(do_after(user, 50, target = src)) - to_chat(user, "You replace the damaged APC frame with a new one.") - qdel(W) - stat &= ~BROKEN - obj_integrity = max_integrity - if(opened==2) - opened = 1 - update_icon() - return - else if(panel_open && !opened && is_wire_tool(W)) - wires.Interact(user) - else - return ..() - -/obj/machinery/power/apc/proc/togglelock(mob/living/user) - if(emagged) - to_chat(user, "The interface is broken!") - else if(opened) - to_chat(user, "You must close the cover to swipe an ID card!") - else if(panel_open) - to_chat(user, "You must close the panel!") - else if(stat & (BROKEN|MAINT)) - to_chat(user, "Nothing happens!") - else - if(allowed(usr) && !isWireCut(APC_WIRE_IDSCAN) && !malfhack) - locked = !locked - to_chat(user, "You [ locked ? "lock" : "unlock"] the APC interface.") - update_icon() - else - to_chat(user, "Access denied.") - -/obj/machinery/power/apc/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) - if(stat & BROKEN) - return damage_amount - . = ..() - -/obj/machinery/power/apc/obj_break(damage_flag) - if(!(flags & NODECONSTRUCT)) - set_broken() - -/obj/machinery/power/apc/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - if(!(stat & BROKEN)) - set_broken() - if(opened != 2) - opened = 2 - coverlocked = FALSE - visible_message("The APC cover is knocked down!") - update_icon() - -/obj/machinery/power/apc/emag_act(user as mob) - if(!(emagged || malfhack)) // trying to unlock with an emag card - if(opened) - to_chat(user, "You must close the cover to swipe an ID card.") - else if(panel_open) - to_chat(user, "You must close the panel first.") - else if(stat & (BROKEN|MAINT)) - to_chat(user, "Nothing happens.") - else - flick("apc-spark", src) - emagged = 1 - locked = 0 - to_chat(user, "You emag the APC interface.") - update_icon() - -// attack with hand - remove cell (if cover open) or interact with the APC -/obj/machinery/power/apc/attack_hand(mob/user) -// if(!can_use(user)) This already gets called in interact() and in topic() -// return - if(!user) - return - src.add_fingerprint(user) - - if(usr == user && opened && (!issilicon(user))) - if(cell) - if(issilicon(user)) - cell.loc=src.loc // Drop it, whoops. - else - user.put_in_hands(cell) - cell.add_fingerprint(user) - cell.update_icon() - - src.cell = null - user.visible_message("[user.name] removes the power cell from [src.name]!", "You remove the power cell.") -// to_chat(user, "You remove the power cell.") - charging = 0 - src.update_icon() - return - if(stat & (BROKEN|MAINT)) - return - - src.interact(user) - -/obj/machinery/power/apc/attack_ghost(mob/user) - if(panel_open) - wires.Interact(user) - return ui_interact(user) - -/obj/machinery/power/apc/interact(mob/user) - if(!user) - return - - if(panel_open) - wires.Interact(user) - - return ui_interact(user) - - -/obj/machinery/power/apc/proc/get_malf_status(mob/living/silicon/ai/malf) - if(!istype(malf)) - return FALSE - - // Only if they're a traitor OR they have the malf picker from the combat module - if(!malf.mind.has_antag_datum(/datum/antagonist/traitor) && !malf.malf_picker) - return FALSE - - if(malfai == (malf.parent || malf)) - if(occupier == malf) - return APC_MALF_SHUNTED_HERE - else if(istype(malf.loc, /obj/machinery/power/apc)) - return APC_MALF_SHUNTED_OTHER - else - return APC_MALF_HACKED - else - return APC_MALF_NOT_HACKED - -/obj/machinery/power/apc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - if(!user) - return - - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - // the ui does not exist, so we'll create a new one - ui = new(user, src, ui_key, "apc.tmpl", "[area.name] - APC", 510, issilicon(user) ? 535 : 460) - ui.open() - // Auto update every Master Controller tick - ui.set_auto_update(1) - -/obj/machinery/power/apc/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - data["locked"] = is_locked(user) - data["isOperating"] = operating - data["externalPower"] = main_status - data["powerCellStatus"] = cell ? cell.percent() : null - data["chargeMode"] = chargemode - data["chargingStatus"] = charging - data["totalLoad"] = round(lastused_equip + lastused_light + lastused_environ) - data["coverLocked"] = coverlocked - data["siliconUser"] = istype(user, /mob/living/silicon) - data["siliconLock"] = locked - data["malfStatus"] = get_malf_status(user) - data["nightshiftLights"] = nightshift_lights - - var/powerChannels[0] - powerChannels[++powerChannels.len] = list( - "title" = "Equipment", - "powerLoad" = round(lastused_equip), - "status" = equipment, - "topicParams" = list( - "auto" = list("eqp" = 3), - "on" = list("eqp" = 2), - "off" = list("eqp" = 1) - ) - ) - powerChannels[++powerChannels.len] = list( - "title" = "Lighting", - "powerLoad" = round(lastused_light), - "status" = lighting, - "topicParams" = list( - "auto" = list("lgt" = 3), - "on" = list("lgt" = 2), - "off" = list("lgt" = 1) - ) - ) - powerChannels[++powerChannels.len] = list( - "title" = "Environment", - "powerLoad" = round(lastused_environ), - "status" = environ, - "topicParams" = list( - "auto" = list("env" = 3), - "on" = list("env" = 2), - "off" = list("env" = 1) - ) - ) - - data["powerChannels"] = powerChannels - - return data - -/obj/machinery/power/apc/proc/report() - return "[area.name] : [equipment]/[lighting]/[environ] ([lastused_equip+lastused_light+lastused_environ]) : [cell? cell.percent() : "N/C"] ([charging])" - -/obj/machinery/power/apc/proc/update() - if(operating && !shorted) - area.power_light = (lighting > 1) - area.power_equip = (equipment > 1) - area.power_environ = (environ > 1) -// if(area.name == "AI Chamber") -// spawn(10) -// to_chat(world, " [area.name] [area.power_equip]") - else - area.power_light = 0 - area.power_equip = 0 - area.power_environ = 0 -// if(area.name == "AI Chamber") -// to_chat(world, "[area.power_equip]") - area.power_change() - -/obj/machinery/power/apc/proc/isWireCut(var/wireIndex) - return wires.IsIndexCut(wireIndex) - - -/obj/machinery/power/apc/proc/can_use(var/mob/user, var/loud = 0) //used by attack_hand() and Topic() - if(user.can_admin_interact()) - return 1 - - autoflag = 5 - if(istype(user, /mob/living/silicon)) - var/mob/living/silicon/ai/AI = user - var/mob/living/silicon/robot/robot = user - if( \ - src.aidisabled || \ - malfhack && istype(malfai) && \ - ( \ - (istype(AI) && (malfai!=AI && malfai != AI.parent)) || \ - (istype(robot) && (robot in malfai.connected_robots)) \ - ) \ - ) - if(!loud) - to_chat(user, "\The [src] has AI control disabled!") - user << browse(null, "window=apc") - user.unset_machine() - return 0 - else - if((!in_range(src, user) || !istype(src.loc, /turf))) - return 0 - - var/mob/living/carbon/human/H = user - if(istype(H)) - if(H.getBrainLoss() >= 60) - for(var/mob/M in viewers(src, null)) - to_chat(M, "[H] stares cluelessly at [src] and drools.") - return 0 - else if(prob(H.getBrainLoss())) - to_chat(user, "You momentarily forget how to use [src].") - return 0 - return 1 - -/obj/machinery/power/apc/proc/is_authenticated(mob/user as mob) - if(user.can_admin_interact()) - return 1 - if(isAI(user) || isrobot(user)) - return 1 - else - return !locked - -/obj/machinery/power/apc/proc/is_locked(mob/user as mob) - if(user.can_admin_interact()) - return 0 - if(isAI(user) || isrobot(user)) - return 0 - else - return locked - -/obj/machinery/power/apc/Topic(href, href_list, var/usingUI = 1) - if(..()) - return 1 - - if(!can_use(usr, 1)) - return 1 - - if(href_list["lock"]) - if(!is_authenticated(usr)) - return - - coverlocked = !coverlocked - - else if(href_list["breaker"]) - if(!is_authenticated(usr)) - return - - toggle_breaker() - - else if(href_list["toggle_nightshift"]) - if(!is_authenticated(usr)) - return - - if(last_nightshift_switch > world.time + 100) // don't spam... - to_chat(usr, "[src]'s night lighting circuit breaker is still cycling!") - return - last_nightshift_switch = world.time - set_nightshift(!nightshift_lights) - - else if(href_list["cmode"]) - if(!is_authenticated(usr)) - return - - chargemode = !chargemode - if(!chargemode) - charging = 0 - update_icon() - - else if(href_list["eqp"]) - if(!is_authenticated(usr)) - return - - var/val = text2num(href_list["eqp"]) - equipment = setsubsystem(val) - update_icon() - update() - - else if(href_list["lgt"]) - if(!is_authenticated(usr)) - return - - var/val = text2num(href_list["lgt"]) - lighting = setsubsystem(val) - update_icon() - update() - - else if(href_list["env"]) - if(!is_authenticated(usr)) - return - - var/val = text2num(href_list["env"]) - environ = setsubsystem(val) - update_icon() - update() - else if( href_list["close"] ) - SSnanoui.close_user_uis(usr, src) - - return 0 - else if(href_list["close2"]) - usr << browse(null, "window=apcwires") - - return 0 - - else if(href_list["overload"]) - if(issilicon(usr) && !aidisabled) - overload_lighting() - - else if(href_list["malfhack"]) - if(get_malf_status(usr)) - malfhack(usr) - - else if(href_list["occupyapc"]) - if(get_malf_status(usr)) - malfoccupy(usr) - - else if(href_list["deoccupyapc"]) - if(get_malf_status(usr)) - malfvacate() - - else if(href_list["toggleaccess"]) - if(istype(usr, /mob/living/silicon)) - if(emagged || aidisabled || (stat & (BROKEN|MAINT))) - to_chat(usr, "The APC does not respond to the command.") - else - locked = !locked - update_icon() - - return 0 - -/obj/machinery/power/apc/proc/toggle_breaker() - operating = !operating - update() - update_icon() - -/obj/machinery/power/apc/proc/malfhack(mob/living/silicon/ai/malf) - if(!istype(malf)) - return - if(get_malf_status(malf) != APC_MALF_NOT_HACKED) - return - if(malf.malfhacking) - to_chat(malf, "You are already hacking an APC.") - return - to_chat(malf, "Beginning override of APC systems. This takes some time, and you cannot perform other actions during the process.") - malf.malfhack = src - malf.malfhacking = addtimer(CALLBACK(malf, /mob/living/silicon/ai/.proc/malfhacked, src), 600, TIMER_STOPPABLE) - var/obj/screen/alert/hackingapc/A - A = malf.throw_alert("hackingapc", /obj/screen/alert/hackingapc) - A.target = src - -/obj/machinery/power/apc/proc/malfoccupy(mob/living/silicon/ai/malf) - if(!istype(malf)) - return - if(istype(malf.loc, /obj/machinery/power/apc)) // Already in an APC - to_chat(malf, "You must evacuate your current APC first!") - return - if(!malf.can_shunt) - to_chat(malf, "You cannot shunt!") - return - if(!is_station_level(src.z)) - return - occupier = new /mob/living/silicon/ai(src,malf.laws,null,1) - occupier.adjustOxyLoss(malf.getOxyLoss()) - if(!findtext(occupier.name, "APC Copy")) - occupier.name = "[malf.name] APC Copy" - if(malf.parent) - occupier.parent = malf.parent - else - occupier.parent = malf - malf.shunted = 1 - malf.mind.transfer_to(occupier) - occupier.eyeobj.name = "[occupier.name] (AI Eye)" - if(malf.parent) - qdel(malf) - occupier.verbs += /mob/living/silicon/ai/proc/corereturn - occupier.cancel_camera() - if((seclevel2num(get_security_level()) == SEC_LEVEL_DELTA) && malf.nuking) - for(var/obj/item/pinpointer/point in GLOB.pinpointer_list) - point.the_disk = src //the pinpointer will detect the shunted AI - -/obj/machinery/power/apc/proc/malfvacate(forced) - if(!occupier) - return - if(occupier.parent && occupier.parent.stat != DEAD) - occupier.mind.transfer_to(occupier.parent) - occupier.parent.shunted = 0 - occupier.parent.adjustOxyLoss(occupier.getOxyLoss()) - occupier.parent.cancel_camera() - qdel(occupier) - if(seclevel2num(get_security_level()) == SEC_LEVEL_DELTA) - for(var/obj/item/pinpointer/point in GLOB.pinpointer_list) - for(var/mob/living/silicon/ai/A in ai_list) - if((A.stat != DEAD) && A.nuking) - point.the_disk = A //The pinpointer tracks the AI back into its core. - else - to_chat(occupier, "Primary core damaged, unable to return core processes.") - if(forced) - occupier.loc = loc - occupier.death() - occupier.gib() - for(var/obj/item/pinpointer/point in GLOB.pinpointer_list) - point.the_disk = null //Pinpointers go back to tracking the nuke disk - -/obj/machinery/power/apc/proc/ion_act() - //intended to be exactly the same as an AI malf attack - if(!src.malfhack && is_station_level(src.z)) - if(prob(3)) - src.locked = 1 - if(src.cell.charge > 0) - src.cell.charge = 0 - cell.corrupt() - src.malfhack = 1 - update_icon() - var/datum/effect_system/smoke_spread/smoke = new - smoke.set_up(3, 0, src.loc) - smoke.attach(src) - smoke.start() - do_sparks(3, 1, src) - for(var/mob/M in viewers(src)) - M.show_message("The [src.name] suddenly lets out a blast of smoke and some sparks!", 3, "You hear sizzling electronics.", 2) - - -/obj/machinery/power/apc/surplus() - if(terminal) - return terminal.surplus() - else - return 0 - -/obj/machinery/power/apc/add_load(amount) - if(terminal && terminal.powernet) - terminal.add_load(amount) - -/obj/machinery/power/apc/avail() - if(terminal) - return terminal.avail() - else - return 0 - -/obj/machinery/power/apc/process() - if(stat & (BROKEN|MAINT)) - return - if(!area.requires_power) - return - - lastused_light = area.usage(STATIC_LIGHT) - lastused_light += area.usage(LIGHT) - lastused_equip = area.usage(EQUIP) - lastused_equip += area.usage(STATIC_EQUIP) - lastused_environ = area.usage(ENVIRON) - lastused_environ += area.usage(STATIC_ENVIRON) - area.clear_usage() - - lastused_total = lastused_light + lastused_equip + lastused_environ - - //store states to update icon if any change - var/last_lt = lighting - var/last_eq = equipment - var/last_en = environ - var/last_ch = charging - - var/excess = surplus() - - if(!src.avail()) - main_status = 0 - else if(excess < 0) - main_status = 1 - else - main_status = 2 - - if(debug) - log_debug("Status: [main_status] - Excess: [excess] - Last Equip: [lastused_equip] - Last Light: [lastused_light] - Longterm: [longtermpower]") - - if(cell && !shorted) - // draw power from cell as before to power the area - var/cellused = min(cell.charge, GLOB.CELLRATE * lastused_total) // clamp deduction to a max, amount left in cell - cell.use(cellused) - - if(excess > lastused_total) // if power excess recharge the cell - // by the same amount just used - cell.give(cellused) - add_load(cellused/GLOB.CELLRATE) // add the load used to recharge the cell - - - else // no excess, and not enough per-apc - if((cell.charge/GLOB.CELLRATE + excess) >= lastused_total) // can we draw enough from cell+grid to cover last usage? - cell.charge = min(cell.maxcharge, cell.charge + GLOB.CELLRATE * excess) //recharge with what we can - add_load(excess) // so draw what we can from the grid - charging = 0 - - else // not enough power available to run the last tick! - charging = 0 - chargecount = 0 - // This turns everything off in the case that there is still a charge left on the battery, just not enough to run the room. - equipment = autoset(equipment, 0) - lighting = autoset(lighting, 0) - environ = autoset(environ, 0) - autoflag = 0 - - - // Set channels depending on how much charge we have left - - // Allow the APC to operate as normal if the cell can charge - if(charging && longtermpower < 10) - longtermpower += 1 - else if(longtermpower > -10) - longtermpower -= 2 - - if(cell.charge >= 1250 || longtermpower > 0) // Put most likely at the top so we don't check it last, effeciency 101 - if(autoflag != 3) - equipment = autoset(equipment, 1) - lighting = autoset(lighting, 1) - environ = autoset(environ, 1) - autoflag = 3 - if(report_power_alarm && is_station_contact(z)) - SSalarms.power_alarm.clearAlarm(loc, src) - else if(cell.charge < 1250 && cell.charge > 750 && longtermpower < 0) // <30%, turn off equipment - if(autoflag != 2) - equipment = autoset(equipment, 2) - lighting = autoset(lighting, 1) - environ = autoset(environ, 1) - if(report_power_alarm && is_station_contact(z)) - SSalarms.power_alarm.triggerAlarm(loc, src) - autoflag = 2 - else if(cell.charge < 750 && cell.charge > 10) // <15%, turn off lighting & equipment - if((autoflag > 1 && longtermpower < 0) || (autoflag > 1 && longtermpower >= 0)) - equipment = autoset(equipment, 2) - lighting = autoset(lighting, 2) - environ = autoset(environ, 1) - if(report_power_alarm && is_station_contact(z)) - SSalarms.power_alarm.triggerAlarm(loc, src) - autoflag = 1 - else if(cell.charge <= 0) // zero charge, turn all off - if(autoflag != 0) - equipment = autoset(equipment, 0) - lighting = autoset(lighting, 0) - environ = autoset(environ, 0) - if(report_power_alarm && is_station_contact(z)) - SSalarms.power_alarm.triggerAlarm(loc, src) - autoflag = 0 - - // now trickle-charge the cell - if(chargemode && charging == 1 && operating) - if(excess > 0) // check to make sure we have enough to charge - // Max charge is capped to % per second constant - var/ch = min(excess*GLOB.CELLRATE, cell.maxcharge*GLOB.CHARGELEVEL) - add_load(ch/GLOB.CELLRATE) // Removes the power we're taking from the grid - cell.give(ch) // actually recharge the cell - - else - charging = 0 // stop charging - chargecount = 0 - - // show cell as fully charged if so - if(cell.charge >= cell.maxcharge) - cell.charge = cell.maxcharge - charging = 2 - - if(chargemode) - if(!charging) - if(excess > cell.maxcharge*GLOB.CHARGELEVEL) - chargecount++ - else - chargecount = 0 - - if(chargecount == 10) - - chargecount = 0 - charging = 1 - - else // chargemode off - charging = 0 - chargecount = 0 - - if(excess >= 5000000 && !shock_proof) //If there's more than 5,000,000 watts in the grid, start arcing and shocking people. - if(prob(5)) - var/list/shock_mobs = list() - for(var/C in view(get_turf(src), 5)) //We only want to shock a single random mob in range, not every one. - if(isliving(C)) - shock_mobs += C - if(shock_mobs.len) - var/mob/living/L = pick(shock_mobs) - L.electrocute_act(rand(5, 25), "electrical arc") - playsound(get_turf(L), 'sound/effects/eleczap.ogg', 75, 1) - Beam(L, icon_state = "lightning[rand(1, 12)]", icon = 'icons/effects/effects.dmi', time = 5) - - else // no cell, switch everything off - - charging = 0 - chargecount = 0 - equipment = autoset(equipment, 0) - lighting = autoset(lighting, 0) - environ = autoset(environ, 0) - if(report_power_alarm && is_station_contact(z)) - SSalarms.power_alarm.triggerAlarm(loc, src) - autoflag = 0 - - // update icon & area power if anything changed - - if(last_lt != lighting || last_eq != equipment || last_en != environ) - queue_icon_update() - update() - else if(last_ch != charging) - queue_icon_update() - -/obj/machinery/power/apc/proc/autoset(var/val, var/on) - if(on==0) - if(val==2) // if on, return off - return 0 - else if(val==3) // if auto-on, return auto-off - return 1 - - else if(on==1) - if(val==1) // if auto-off, return auto-on - return 3 - - else if(on==2) - if(val==3) // if auto-on, return auto-off - return 1 - - return val - -// damage and destruction acts - -/obj/machinery/power/apc/emp_act(severity) - if(cell) - cell.emp_act(severity) - if(occupier) - occupier.emp_act(severity) - lighting = 0 - equipment = 0 - environ = 0 - update_icon() - update() - spawn(600) - equipment = 3 - environ = 3 - update_icon() - update() - ..() - -/obj/machinery/power/apc/blob_act(obj/structure/blob/B) - set_broken() - -/obj/machinery/power/apc/disconnect_terminal() - if(terminal) - terminal.master = null - terminal = null - -/obj/machinery/power/apc/proc/set_broken() - if(malfai && operating) - malfai.malf_picker.processing_time = Clamp(malfai.malf_picker.processing_time - 10,0,1000) - stat |= BROKEN - operating = 0 - if(occupier) - malfvacate(1) - update_icon() - update() - -// overload all the lights in this APC area - -/obj/machinery/power/apc/proc/overload_lighting(chance = 100) - if(!operating || shorted) - return - if(cell && cell.charge >= 20) - cell.use(20) - spawn(0) - for(var/obj/machinery/light/L in area) - if(prob(chance)) - L.break_light_tube(0, 1) - stoplag() - -/obj/machinery/power/apc/proc/null_charge() - for(var/obj/machinery/light/L in area) - L.break_light_tube(0, 1) - stoplag() - -/obj/machinery/power/apc/proc/setsubsystem(val) - if(cell && cell.charge > 0) - return (val==1) ? 0 : val - else if(val == 3) - return 1 - else - return 0 - -/obj/machinery/power/apc/proc/set_nightshift(on) - set waitfor = FALSE - nightshift_lights = on - for(var/obj/machinery/light/L in area) - if(L.nightshift_allowed) - L.nightshift_enabled = nightshift_lights - L.update(FALSE) - CHECK_TICK - -#undef APC_UPDATE_ICON_COOLDOWN +#define APC_WIRE_IDSCAN 1 +#define APC_WIRE_MAIN_POWER1 2 +#define APC_WIRE_MAIN_POWER2 3 +#define APC_WIRE_AI_CONTROL 4 + +//update_state +#define UPSTATE_CELL_IN 1 +#define UPSTATE_OPENED1 2 +#define UPSTATE_OPENED2 4 +#define UPSTATE_MAINT 8 +#define UPSTATE_BROKE 16 +#define UPSTATE_BLUESCREEN 32 +#define UPSTATE_WIREEXP 64 +#define UPSTATE_ALLGOOD 128 + +//update_overlay +#define APC_UPOVERLAY_CHARGEING0 1 +#define APC_UPOVERLAY_CHARGEING1 2 +#define APC_UPOVERLAY_CHARGEING2 4 +#define APC_UPOVERLAY_EQUIPMENT0 8 +#define APC_UPOVERLAY_EQUIPMENT1 16 +#define APC_UPOVERLAY_EQUIPMENT2 32 +#define APC_UPOVERLAY_LIGHTING0 64 +#define APC_UPOVERLAY_LIGHTING1 128 +#define APC_UPOVERLAY_LIGHTING2 256 +#define APC_UPOVERLAY_ENVIRON0 512 +#define APC_UPOVERLAY_ENVIRON1 1024 +#define APC_UPOVERLAY_ENVIRON2 2048 +#define APC_UPOVERLAY_LOCKED 4096 + +#define APC_UPDATE_ICON_COOLDOWN 200 // 20 seconds + +// APC malf status +#define APC_MALF_NOT_HACKED 1 +#define APC_MALF_HACKED 2 // APC hacked by user, and user is in its core. +#define APC_MALF_SHUNTED_HERE 3 // User is shunted in this APC +#define APC_MALF_SHUNTED_OTHER 4 // User is shunted in another APC + +// the Area Power Controller (APC), formerly Power Distribution Unit (PDU) +// one per area, needs wire conection to power network through a terminal + +// controls power to devices in that area +// may be opened to change power cell +// three different channels (lighting/equipment/environ) - may each be set to on, off, or auto + + +//NOTE: STUFF STOLEN FROM AIRLOCK.DM thx + + +/obj/machinery/power/apc + name = "area power controller" + desc = "A control terminal for the area electrical systems." + icon_state = "apc0" + use_power = NO_POWER_USE + max_integrity = 200 + integrity_failure = 50 + resistance_flags = FIRE_PROOF + req_access = list(ACCESS_ENGINE_EQUIP) + siemens_strength = 1 + damage_deflection = 10 + var/area/area + var/areastring = null + var/obj/item/stock_parts/cell/cell + var/start_charge = 90 // initial cell charge % + var/cell_type = 2500 //Base cell has 2500 capacity. Enter the path of a different cell you want to use. cell determines charge rates, max capacity, ect. These can also be changed with other APC vars, but isn't recommended to minimize the risk of accidental usage of dirty editted APCs + var/opened = 0 //0=closed, 1=opened, 2=cover removed + var/shorted = 0 + var/lighting = 3 + var/equipment = 3 + var/environ = 3 + var/operating = 1 + var/charging = 0 + var/chargemode = 1 + var/chargecount = 0 + var/locked = 1 + var/coverlocked = 1 + var/aidisabled = 0 + var/tdir = null + var/obj/machinery/power/terminal/terminal = null + var/lastused_light = 0 + var/lastused_equip = 0 + var/lastused_environ = 0 + var/lastused_total = 0 + var/main_status = 0 + powernet = 0 // set so that APCs aren't found as powernet nodes //Hackish, Horrible, was like this before I changed it :( + var/malfhack = 0 //New var for my changes to AI malf. --NeoFite + var/mob/living/silicon/ai/malfai = null //See above --NeoFite + var/debug= 0 + var/autoflag= 0 // 0 = off, 1= eqp and lights off, 2 = eqp off, 3 = all on. +// luminosity = 1 + var/has_electronics = 0 // 0 - none, 1 - plugged in, 2 - secured by screwdriver + var/overload = 1 //used for the Blackout malf module + var/beenhit = 0 // used for counting how many times it has been hit, used for Aliens at the moment + var/mob/living/silicon/ai/occupier = null + var/longtermpower = 10 + var/update_state = -1 + var/update_overlay = -1 + var/global/status_overlays = 0 + var/updating_icon = 0 + var/datum/wires/apc/wires = null + //var/debug = 0 + var/global/list/status_overlays_lock + var/global/list/status_overlays_charging + var/global/list/status_overlays_equipment + var/global/list/status_overlays_lighting + var/global/list/status_overlays_environ + var/indestructible = 0 // If set, prevents aliens from destroying it + var/keep_preset_name = 0 + + var/report_power_alarm = TRUE + + var/shock_proof = 0 //if set to 1, this APC will not arc bolts of electricity if it's overloaded. + + // Nightshift + var/nightshift_lights = FALSE + var/last_nightshift_switch = 0 + +/obj/machinery/power/apc/worn_out + name = "\improper Worn out APC" + keep_preset_name = 1 + locked = 0 + environ = 0 + equipment = 0 + lighting = 0 + operating = 0 + +/obj/machinery/power/apc/noalarm + report_power_alarm = FALSE + +/obj/machinery/power/apc/syndicate //general syndicate access + req_access = list(ACCESS_SYNDICATE) + report_power_alarm = FALSE + +/obj/item/apc_electronics + name = "power control module" + desc = "Heavy-duty switching circuits for power control." + icon = 'icons/obj/module.dmi' + icon_state = "power_mod" + w_class = WEIGHT_CLASS_SMALL + origin_tech = "engineering=2;programming=1" + item_state = "electronic" + flags = CONDUCT + usesound = 'sound/items/deconstruct.ogg' + toolspeed = 1 + +/obj/machinery/power/apc/get_cell() + return cell + +/obj/machinery/power/apc/connect_to_network() + //Override because the APC does not directly connect to the network; it goes through a terminal. + //The terminal is what the power computer looks for anyway. + if(terminal) + terminal.connect_to_network() + +/obj/machinery/power/apc/New(turf/loc, ndir, building = 0) + if(!armor) + armor = list("melee" = 20, "bullet" = 20, "laser" = 10, "energy" = 100, "bomb" = 30, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 50) + ..() + GLOB.apcs += src + GLOB.apcs = sortAtom(GLOB.apcs) + + wires = new(src) + // offset 24 pixels in direction of dir + // this allows the APC to be embedded in a wall, yet still inside an area + if(building) + setDir(ndir) + tdir = dir // to fix Vars bug + setDir(SOUTH) + + pixel_x = (src.tdir & 3)? 0 : (src.tdir == 4 ? 24 : -24) + pixel_y = (src.tdir & 3)? (src.tdir ==1 ? 24 : -24) : 0 + if(building) + area = get_area(src) + area.apc |= src + opened = 1 + operating = 0 + name = "[area.name] APC" + stat |= MAINT + update_icon() + addtimer(CALLBACK(src, .proc/update), 5) + +/obj/machinery/power/apc/Destroy() + GLOB.apcs -= src + if(malfai && operating) + malfai.malf_picker.processing_time = Clamp(malfai.malf_picker.processing_time - 10,0,1000) + area.power_light = 0 + area.power_equip = 0 + area.power_environ = 0 + area.power_change() + if(occupier) + malfvacate(1) + QDEL_NULL(wires) + QDEL_NULL(cell) + if(terminal) + disconnect_terminal() + area.apc -= src + return ..() + +/obj/machinery/power/apc/proc/make_terminal() + // create a terminal object at the same position as original turf loc + // wires will attach to this + terminal = new/obj/machinery/power/terminal(src.loc) + terminal.setDir(tdir) + terminal.master = src + +/obj/machinery/power/apc/Initialize(mapload) + . = ..() + if(!mapload) + return + has_electronics = 2 //installed and secured + // is starting with a power cell installed, create it and set its charge level + if(cell_type) + cell = new/obj/item/stock_parts/cell/upgraded(src) + cell.maxcharge = cell_type // cell_type is maximum charge (old default was 1000 or 2500 (values one and two respectively) + cell.charge = start_charge * cell.maxcharge / 100 // (convert percentage to actual value) + + var/area/A = get_area(src) + + + //if area isn't specified use current + if(keep_preset_name) + if(isarea(A)) + area = A + // no-op, keep the name + else if(isarea(A) && src.areastring == null) + area = A + name = "\improper [area.name] APC" + else + area = get_area_name(areastring) + name = "\improper [area.name] APC" + area.apc |= src + + update_icon() + + make_terminal() + + addtimer(CALLBACK(src, .proc/update), 5) + +/obj/machinery/power/apc/examine(mob/user) + . = ..() + if(in_range(user, src)) + if(stat & BROKEN) + . += "Looks broken." + else if(opened) + if(has_electronics && terminal) + . += "The cover is [opened==2?"removed":"open"] and the power cell is [ cell ? "installed" : "missing"]." + else if(!has_electronics && terminal) + . += "There are some wires but no electronics." + else if(has_electronics && !terminal) + . += "Electronics installed but not wired." + else /* if(!has_electronics && !terminal) */ + . += "There is no electronics nor connected wires." + else + if(stat & MAINT) + . += "The cover is closed. Something wrong with it: it doesn't work." + else if(malfhack) + . += "The cover is broken. It may be hard to force it open." + else + . += "The cover is closed." + +// update the APC icon to show the three base states +// also add overlays for indicator lights +/obj/machinery/power/apc/update_icon(force_update = FALSE) + + if(!status_overlays || force_update) + status_overlays = 1 + status_overlays_lock = new + status_overlays_charging = new + status_overlays_equipment = new + status_overlays_lighting = new + status_overlays_environ = new + + status_overlays_lock.len = 2 + status_overlays_charging.len = 3 + status_overlays_equipment.len = 4 + status_overlays_lighting.len = 4 + status_overlays_environ.len = 4 + + status_overlays_lock[1] = image(icon, "apcox-0") // 0=blue 1=red + status_overlays_lock[2] = image(icon, "apcox-1") + + status_overlays_charging[1] = image(icon, "apco3-0") + status_overlays_charging[2] = image(icon, "apco3-1") + status_overlays_charging[3] = image(icon, "apco3-2") + + status_overlays_equipment[1] = image(icon, "apco0-0") // 0=red, 1=green, 2=blue + status_overlays_equipment[2] = image(icon, "apco0-1") + status_overlays_equipment[3] = image(icon, "apco0-2") + status_overlays_equipment[4] = image(icon, "apco0-3") + + status_overlays_lighting[1] = image(icon, "apco1-0") + status_overlays_lighting[2] = image(icon, "apco1-1") + status_overlays_lighting[3] = image(icon, "apco1-2") + status_overlays_lighting[4] = image(icon, "apco1-3") + + status_overlays_environ[1] = image(icon, "apco2-0") + status_overlays_environ[2] = image(icon, "apco2-1") + status_overlays_environ[3] = image(icon, "apco2-2") + status_overlays_environ[4] = image(icon, "apco2-3") + + + + var/update = check_updates() //returns 0 if no need to update icons. + // 1 if we need to update the icon_state + // 2 if we need to update the overlays + if(!update && !force_update) + return + + if(force_update || update & 1) // Updating the icon state + if(update_state & UPSTATE_ALLGOOD) + icon_state = "apc0" + else if(update_state & (UPSTATE_OPENED1|UPSTATE_OPENED2)) + var/basestate = "apc[ cell ? "2" : "1" ]" + if(update_state & UPSTATE_OPENED1) + if(update_state & (UPSTATE_MAINT|UPSTATE_BROKE)) + icon_state = "apcmaint" //disabled APC cannot hold cell + else + icon_state = basestate + else if(update_state & UPSTATE_OPENED2) + icon_state = "[basestate]-nocover" + else if(update_state & UPSTATE_BROKE) + icon_state = "apc-b" + else if(update_state & UPSTATE_BLUESCREEN) + icon_state = "apcemag" + else if(update_state & UPSTATE_WIREEXP) + icon_state = "apcewires" + + + + if(!(update_state & UPSTATE_ALLGOOD)) + if(overlays.len) + overlays = 0 + return + + + + if(force_update || update & 2) + + if(overlays.len) + overlays.len = 0 + + if(!(stat & (BROKEN|MAINT)) && update_state & UPSTATE_ALLGOOD) + overlays += status_overlays_lock[locked+1] + overlays += status_overlays_charging[charging+1] + if(operating) + overlays += status_overlays_equipment[equipment+1] + overlays += status_overlays_lighting[lighting+1] + overlays += status_overlays_environ[environ+1] + + +/obj/machinery/power/apc/proc/check_updates() + + var/last_update_state = update_state + var/last_update_overlay = update_overlay + update_state = 0 + update_overlay = 0 + + if(cell) + update_state |= UPSTATE_CELL_IN + if(stat & BROKEN) + update_state |= UPSTATE_BROKE + if(stat & MAINT) + update_state |= UPSTATE_MAINT + if(opened) + if(opened==1) + update_state |= UPSTATE_OPENED1 + if(opened==2) + update_state |= UPSTATE_OPENED2 + else if(emagged || malfai) + update_state |= UPSTATE_BLUESCREEN + else if(panel_open) + update_state |= UPSTATE_WIREEXP + if(update_state <= 1) + update_state |= UPSTATE_ALLGOOD + + if(update_state & UPSTATE_ALLGOOD) + if(locked) + update_overlay |= APC_UPOVERLAY_LOCKED + + if(!charging) + update_overlay |= APC_UPOVERLAY_CHARGEING0 + else if(charging == 1) + update_overlay |= APC_UPOVERLAY_CHARGEING1 + else if(charging == 2) + update_overlay |= APC_UPOVERLAY_CHARGEING2 + + if(!equipment) + update_overlay |= APC_UPOVERLAY_EQUIPMENT0 + else if(equipment == 1) + update_overlay |= APC_UPOVERLAY_EQUIPMENT1 + else if(equipment == 2) + update_overlay |= APC_UPOVERLAY_EQUIPMENT2 + + if(!lighting) + update_overlay |= APC_UPOVERLAY_LIGHTING0 + else if(lighting == 1) + update_overlay |= APC_UPOVERLAY_LIGHTING1 + else if(lighting == 2) + update_overlay |= APC_UPOVERLAY_LIGHTING2 + + if(!environ) + update_overlay |= APC_UPOVERLAY_ENVIRON0 + else if(environ==1) + update_overlay |= APC_UPOVERLAY_ENVIRON1 + else if(environ==2) + update_overlay |= APC_UPOVERLAY_ENVIRON2 + + var/results = 0 + if(last_update_state == update_state && last_update_overlay == update_overlay) + return 0 + if(last_update_state != update_state) + results += 1 + if(last_update_overlay != update_overlay) + results += 2 + return results + +// Used in process so it doesn't update the icon too much +/obj/machinery/power/apc/proc/queue_icon_update() + + if(!updating_icon) + updating_icon = 1 + // Start the update + spawn(APC_UPDATE_ICON_COOLDOWN) + update_icon() + updating_icon = 0 + +/obj/machinery/power/apc/get_spooked(second_pass = FALSE) + if(opened || panel_open) + return + if(stat & (NOPOWER | BROKEN)) + return + if(!second_pass) //The first time, we just cut overlays + addtimer(CALLBACK(src, .get_spooked, TRUE), 1) + cut_overlays() + else + flick("apcemag", src) //Second time we cause the APC to update its icon, then add a timer to update icon later + addtimer(CALLBACK(src, .proc/update_icon, TRUE), 10) + +//attack with an item - open/close cover, insert cell, or (un)lock interface +/obj/machinery/power/apc/attackby(obj/item/W, mob/living/user, params) + + if(issilicon(user) && get_dist(src,user)>1) + return src.attack_hand(user) + + else if (istype(W, /obj/item/stock_parts/cell) && opened) // trying to put a cell inside + if(cell) + to_chat(user, "There is a power cell already installed!") + return + else + if(stat & MAINT) + to_chat(user, "There is no connector for your power cell!") + return + if(!user.drop_item()) + return + W.forceMove(src) + cell = W + user.visible_message(\ + "[user.name] has inserted the power cell to [src.name]!",\ + "You insert the power cell.") + chargecount = 0 + update_icon() + + else if(W.GetID()) // trying to unlock the interface with an ID card + togglelock(user) + + else if(istype(W, /obj/item/stack/cable_coil) && opened) + var/turf/host_turf = get_turf(src) + if(!host_turf) + throw EXCEPTION("attackby on APC when it's not on a turf") + return + if(host_turf.intact) + to_chat(user, "You must remove the floor plating in front of the APC first!") + return + else if(terminal) // it already have terminal + to_chat(user, "This APC is already wired!") + return + else if(has_electronics == 0) + to_chat(user, "There is nothing to wire!") + return + + var/obj/item/stack/cable_coil/C = W + if(C.get_amount() < 10) + to_chat(user, "You need ten lengths of cable for APC!") + return + user.visible_message("[user.name] adds cables to the APC frame.", \ + "You start adding cables to the APC frame...") + playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1) + if(do_after(user, 20, target = src)) + if(C.get_amount() < 10 || !C) + return + if(C.get_amount() >= 10 && !terminal && opened && has_electronics > 0) + var/turf/T = get_turf(src) + var/obj/structure/cable/N = T.get_cable_node() + if(prob(50) && electrocute_mob(usr, N, N, 1, TRUE)) + do_sparks(5, TRUE, src) + return + C.use(10) + to_chat(user, "You add cables to the APC frame.") + make_terminal() + terminal.connect_to_network() + + else if(istype(W, /obj/item/apc_electronics) && opened) + if(has_electronics!=0) // there are already electronicks inside + to_chat(user, "You cannot put the board inside, there already is one!") + return + else if(stat & BROKEN) + to_chat(user, "You cannot put the board inside, the frame is damaged!") + return + + user.visible_message("[user.name] inserts the power control board into [src].", \ + "You start to insert the power control board into the frame...") + playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1) + if(do_after(user, 10, target = src)) + if(has_electronics==0) + has_electronics = 1 + locked = FALSE + to_chat(user, "You place the power control board inside the frame.") + qdel(W) + + else if(istype(W, /obj/item/mounted/frame/apc_frame) && opened) + if(!(stat & BROKEN || opened==2 || obj_integrity < max_integrity)) // There is nothing to repair + to_chat(user, "You found no reason for repairing this APC") + return + if(!(stat & BROKEN) && opened==2) // Cover is the only thing broken, we do not need to remove elctronicks to replace cover + user.visible_message("[user.name] replaces missing APC's cover.",\ + "You begin to replace APC's cover...") + if(do_after(user, 20, target = src)) // replacing cover is quicker than replacing whole frame + to_chat(user, "You replace missing APC's cover.") + qdel(W) + opened = 1 + update_icon() + return + if(has_electronics) + to_chat(user, "You cannot repair this APC until you remove the electronics still inside!") + return + user.visible_message("[user.name] replaces the damaged APC frame with a new one.",\ + "You begin to replace the damaged APC frame...") + if(do_after(user, 50, target = src)) + to_chat(user, "You replace the damaged APC frame with a new one.") + qdel(W) + stat &= ~BROKEN + obj_integrity = max_integrity + if(opened==2) + opened = 1 + update_icon() + return + else + return ..() + + +/obj/machinery/power/apc/crowbar_act(mob/living/user, obj/item/I) + . = TRUE + if(!I.tool_start_check(user, 0)) + return + if(opened) // a) on open apc + if(has_electronics==1) + if(terminal) + to_chat(user, "Disconnect the wires first!") + return + to_chat(user, "You are trying to remove the power control board..." ) + if(I.use_tool(src, user, 50, volume = I.tool_volume)) + if(has_electronics==1) + has_electronics = 0 + if(stat & BROKEN) + user.visible_message(\ + "[user.name] has broken the power control board inside [src.name]!", + "You break the charred power control board and remove the remains.", + "You hear a crack.") + return + //SSticker.mode:apcs-- //XSI said no and I agreed. -rastaf0 + else if(emagged) // We emag board, not APC's frame + emagged = FALSE + user.visible_message( + "[user.name] has discarded emaged power control board from [src.name]!", + "You discarded shorten board.") + return + else if(malfhack) // AI hacks board, not APC's frame + user.visible_message(\ + "[user.name] has discarded strangely programmed power control board from [src.name]!", + "You discarded strangely programmed board.") + malfai = null + malfhack = 0 + return + else + user.visible_message(\ + "[user.name] has removed the power control board from [src.name]!", + "You remove the power control board.") + new /obj/item/apc_electronics(loc) + return + else if(opened!=2) //cover isn't removed + opened = 0 + coverlocked = TRUE //closing cover relocks it + update_icon() + return + else if(!(stat & BROKEN)) // b) on closed and not broken APC + if(coverlocked && !(stat & MAINT)) // locked... + to_chat(user, "The cover is locked and cannot be opened!") + return + else if(panel_open) // wires are exposed + to_chat(user, "Exposed wires prevents you from opening it!") + return + else + opened = 1 + update_icon() + +/obj/machinery/power/apc/screwdriver_act(mob/living/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + else if(opened) + if(cell && !(stat & MAINT)) + to_chat(user, "Close the APC first!") //Less hints more mystery! + return + else + if(has_electronics==1) + has_electronics = 2 + stat &= ~MAINT + to_chat(user, "You screw the circuit electronics into place.") + else if(has_electronics==2) + has_electronics = 1 + stat |= MAINT + to_chat(user, "You unfasten the electronics.") + else + to_chat(user, "There is nothing to secure!") + return + update_icon() + else if(emagged) + to_chat(user, "The interface is broken!") + else + panel_open = !panel_open + to_chat(user, "The wires have been [panel_open ? "exposed" : "unexposed"]") + update_icon() + + +/obj/machinery/power/apc/wirecutter_act(mob/living/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(panel_open && !opened) + wires.Interact(user) + else if(terminal && opened) + terminal.dismantle(user, I) + +/obj/machinery/power/apc/multitool_act(mob/living/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(panel_open && !opened) + wires.Interact(user) + +/obj/machinery/power/apc/proc/togglelock(mob/living/user) + if(emagged) + to_chat(user, "The interface is broken!") + else if(opened) + to_chat(user, "You must close the cover to swipe an ID card!") + else if(panel_open) + to_chat(user, "You must close the panel!") + else if(stat & (BROKEN|MAINT)) + to_chat(user, "Nothing happens!") + else + if(allowed(usr) && !isWireCut(APC_WIRE_IDSCAN) && !malfhack) + locked = !locked + to_chat(user, "You [ locked ? "lock" : "unlock"] the APC interface.") + update_icon() + else + to_chat(user, "Access denied.") + +/obj/machinery/power/apc/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir) + if(stat & BROKEN) + return damage_amount + . = ..() + +/obj/machinery/power/apc/obj_break(damage_flag) + if(!(flags & NODECONSTRUCT)) + set_broken() + +/obj/machinery/power/apc/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + if(!(stat & BROKEN)) + set_broken() + if(opened != 2) + opened = 2 + coverlocked = FALSE + visible_message("The APC cover is knocked down!") + update_icon() + +/obj/machinery/power/apc/welder_act(mob/user, obj/item/I) + if(!opened || has_electronics || terminal) + return + . = TRUE + if(!I.tool_use_check(user, 3)) + return + WELDER_ATTEMPT_SLICING_MESSAGE + if(I.use_tool(src, user, 50, amount = 3, volume = I.tool_volume)) + if((stat & BROKEN) || opened==2) + new /obj/item/stack/sheet/metal(loc) + user.visible_message(\ + "[user.name] has cut [src] apart with [I].",\ + "You disassembled the broken APC frame.") + else + new /obj/item/mounted/frame/apc_frame(loc) + user.visible_message(\ + "[user.name] has cut [src] from the wall with [I].",\ + "You cut the APC frame from the wall.") + qdel(src) + +/obj/machinery/power/apc/emag_act(user as mob) + if(!(emagged || malfhack)) // trying to unlock with an emag card + if(opened) + to_chat(user, "You must close the cover to swipe an ID card.") + else if(panel_open) + to_chat(user, "You must close the panel first.") + else if(stat & (BROKEN|MAINT)) + to_chat(user, "Nothing happens.") + else + flick("apc-spark", src) + emagged = 1 + locked = 0 + to_chat(user, "You emag the APC interface.") + update_icon() + +// attack with hand - remove cell (if cover open) or interact with the APC +/obj/machinery/power/apc/attack_hand(mob/user) +// if(!can_use(user)) This already gets called in interact() and in topic() +// return + if(!user) + return + src.add_fingerprint(user) + + if(usr == user && opened && (!issilicon(user))) + if(cell) + if(issilicon(user)) + cell.loc=src.loc // Drop it, whoops. + else + user.put_in_hands(cell) + cell.add_fingerprint(user) + cell.update_icon() + + src.cell = null + user.visible_message("[user.name] removes the power cell from [src.name]!", "You remove the power cell.") +// to_chat(user, "You remove the power cell.") + charging = 0 + src.update_icon() + return + if(stat & (BROKEN|MAINT)) + return + + src.interact(user) + +/obj/machinery/power/apc/attack_ghost(mob/user) + if(panel_open) + wires.Interact(user) + return ui_interact(user) + +/obj/machinery/power/apc/interact(mob/user) + if(!user) + return + + if(panel_open) + wires.Interact(user) + + return ui_interact(user) + + +/obj/machinery/power/apc/proc/get_malf_status(mob/living/silicon/ai/malf) + if(!istype(malf)) + return FALSE + + // Only if they're a traitor OR they have the malf picker from the combat module + if(!malf.mind.has_antag_datum(/datum/antagonist/traitor) && !malf.malf_picker) + return FALSE + + if(malfai == (malf.parent || malf)) + if(occupier == malf) + return APC_MALF_SHUNTED_HERE + else if(istype(malf.loc, /obj/machinery/power/apc)) + return APC_MALF_SHUNTED_OTHER + else + return APC_MALF_HACKED + else + return APC_MALF_NOT_HACKED + +/obj/machinery/power/apc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + if(!user) + return + + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + // the ui does not exist, so we'll create a new one + ui = new(user, src, ui_key, "apc.tmpl", "[area.name] - APC", 510, issilicon(user) ? 535 : 460) + ui.open() + // Auto update every Master Controller tick + ui.set_auto_update(1) + +/obj/machinery/power/apc/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) + var/data[0] + data["locked"] = is_locked(user) + data["isOperating"] = operating + data["externalPower"] = main_status + data["powerCellStatus"] = cell ? cell.percent() : null + data["chargeMode"] = chargemode + data["chargingStatus"] = charging + data["totalLoad"] = round(lastused_equip + lastused_light + lastused_environ) + data["coverLocked"] = coverlocked + data["siliconUser"] = istype(user, /mob/living/silicon) + data["siliconLock"] = locked + data["malfStatus"] = get_malf_status(user) + data["nightshiftLights"] = nightshift_lights + + var/powerChannels[0] + powerChannels[++powerChannels.len] = list( + "title" = "Equipment", + "powerLoad" = round(lastused_equip), + "status" = equipment, + "topicParams" = list( + "auto" = list("eqp" = 3), + "on" = list("eqp" = 2), + "off" = list("eqp" = 1) + ) + ) + powerChannels[++powerChannels.len] = list( + "title" = "Lighting", + "powerLoad" = round(lastused_light), + "status" = lighting, + "topicParams" = list( + "auto" = list("lgt" = 3), + "on" = list("lgt" = 2), + "off" = list("lgt" = 1) + ) + ) + powerChannels[++powerChannels.len] = list( + "title" = "Environment", + "powerLoad" = round(lastused_environ), + "status" = environ, + "topicParams" = list( + "auto" = list("env" = 3), + "on" = list("env" = 2), + "off" = list("env" = 1) + ) + ) + + data["powerChannels"] = powerChannels + + return data + +/obj/machinery/power/apc/proc/report() + return "[area.name] : [equipment]/[lighting]/[environ] ([lastused_equip+lastused_light+lastused_environ]) : [cell? cell.percent() : "N/C"] ([charging])" + +/obj/machinery/power/apc/proc/update() + if(operating && !shorted) + area.power_light = (lighting > 1) + area.power_equip = (equipment > 1) + area.power_environ = (environ > 1) +// if(area.name == "AI Chamber") +// spawn(10) +// to_chat(world, " [area.name] [area.power_equip]") + else + area.power_light = 0 + area.power_equip = 0 + area.power_environ = 0 +// if(area.name == "AI Chamber") +// to_chat(world, "[area.power_equip]") + area.power_change() + +/obj/machinery/power/apc/proc/isWireCut(var/wireIndex) + return wires.IsIndexCut(wireIndex) + + +/obj/machinery/power/apc/proc/can_use(var/mob/user, var/loud = 0) //used by attack_hand() and Topic() + if(user.can_admin_interact()) + return 1 + + autoflag = 5 + if(istype(user, /mob/living/silicon)) + var/mob/living/silicon/ai/AI = user + var/mob/living/silicon/robot/robot = user + if( \ + src.aidisabled || \ + malfhack && istype(malfai) && \ + ( \ + (istype(AI) && (malfai!=AI && malfai != AI.parent)) || \ + (istype(robot) && (robot in malfai.connected_robots)) \ + ) \ + ) + if(!loud) + to_chat(user, "\The [src] has AI control disabled!") + user << browse(null, "window=apc") + user.unset_machine() + return 0 + else + if((!in_range(src, user) || !istype(src.loc, /turf))) + return 0 + + var/mob/living/carbon/human/H = user + if(istype(H)) + if(H.getBrainLoss() >= 60) + for(var/mob/M in viewers(src, null)) + to_chat(M, "[H] stares cluelessly at [src] and drools.") + return 0 + else if(prob(H.getBrainLoss())) + to_chat(user, "You momentarily forget how to use [src].") + return 0 + return 1 + +/obj/machinery/power/apc/proc/is_authenticated(mob/user as mob) + if(user.can_admin_interact()) + return 1 + if(isAI(user) || isrobot(user)) + return 1 + else + return !locked + +/obj/machinery/power/apc/proc/is_locked(mob/user as mob) + if(user.can_admin_interact()) + return 0 + if(isAI(user) || isrobot(user)) + return 0 + else + return locked + +/obj/machinery/power/apc/Topic(href, href_list, var/usingUI = 1) + if(..()) + return 1 + + if(!can_use(usr, 1)) + return 1 + + if(href_list["lock"]) + if(!is_authenticated(usr)) + return + + coverlocked = !coverlocked + + else if(href_list["breaker"]) + if(!is_authenticated(usr)) + return + + toggle_breaker() + + else if(href_list["toggle_nightshift"]) + if(!is_authenticated(usr)) + return + + if(last_nightshift_switch > world.time + 100) // don't spam... + to_chat(usr, "[src]'s night lighting circuit breaker is still cycling!") + return + last_nightshift_switch = world.time + set_nightshift(!nightshift_lights) + + else if(href_list["cmode"]) + if(!is_authenticated(usr)) + return + + chargemode = !chargemode + if(!chargemode) + charging = 0 + update_icon() + + else if(href_list["eqp"]) + if(!is_authenticated(usr)) + return + + var/val = text2num(href_list["eqp"]) + equipment = setsubsystem(val) + update_icon() + update() + + else if(href_list["lgt"]) + if(!is_authenticated(usr)) + return + + var/val = text2num(href_list["lgt"]) + lighting = setsubsystem(val) + update_icon() + update() + + else if(href_list["env"]) + if(!is_authenticated(usr)) + return + + var/val = text2num(href_list["env"]) + environ = setsubsystem(val) + update_icon() + update() + else if( href_list["close"] ) + SSnanoui.close_user_uis(usr, src) + + return 0 + else if(href_list["close2"]) + usr << browse(null, "window=apcwires") + + return 0 + + else if(href_list["overload"]) + if(issilicon(usr) && !aidisabled) + overload_lighting() + + else if(href_list["malfhack"]) + if(get_malf_status(usr)) + malfhack(usr) + + else if(href_list["occupyapc"]) + if(get_malf_status(usr)) + malfoccupy(usr) + + else if(href_list["deoccupyapc"]) + if(get_malf_status(usr)) + malfvacate() + + else if(href_list["toggleaccess"]) + if(istype(usr, /mob/living/silicon)) + if(emagged || aidisabled || (stat & (BROKEN|MAINT))) + to_chat(usr, "The APC does not respond to the command.") + else + locked = !locked + update_icon() + + return 0 + +/obj/machinery/power/apc/proc/toggle_breaker() + operating = !operating + update() + update_icon() + +/obj/machinery/power/apc/proc/malfhack(mob/living/silicon/ai/malf) + if(!istype(malf)) + return + if(get_malf_status(malf) != APC_MALF_NOT_HACKED) + return + if(malf.malfhacking) + to_chat(malf, "You are already hacking an APC.") + return + to_chat(malf, "Beginning override of APC systems. This takes some time, and you cannot perform other actions during the process.") + malf.malfhack = src + malf.malfhacking = addtimer(CALLBACK(malf, /mob/living/silicon/ai/.proc/malfhacked, src), 600, TIMER_STOPPABLE) + var/obj/screen/alert/hackingapc/A + A = malf.throw_alert("hackingapc", /obj/screen/alert/hackingapc) + A.target = src + +/obj/machinery/power/apc/proc/malfoccupy(mob/living/silicon/ai/malf) + if(!istype(malf)) + return + if(istype(malf.loc, /obj/machinery/power/apc)) // Already in an APC + to_chat(malf, "You must evacuate your current APC first!") + return + if(!malf.can_shunt) + to_chat(malf, "You cannot shunt!") + return + if(!is_station_level(src.z)) + return + occupier = new /mob/living/silicon/ai(src,malf.laws,null,1) + occupier.adjustOxyLoss(malf.getOxyLoss()) + if(!findtext(occupier.name, "APC Copy")) + occupier.name = "[malf.name] APC Copy" + if(malf.parent) + occupier.parent = malf.parent + else + occupier.parent = malf + malf.shunted = 1 + malf.mind.transfer_to(occupier) + occupier.eyeobj.name = "[occupier.name] (AI Eye)" + if(malf.parent) + qdel(malf) + occupier.verbs += /mob/living/silicon/ai/proc/corereturn + occupier.cancel_camera() + if((seclevel2num(get_security_level()) == SEC_LEVEL_DELTA) && malf.nuking) + for(var/obj/item/pinpointer/point in GLOB.pinpointer_list) + point.the_disk = src //the pinpointer will detect the shunted AI + +/obj/machinery/power/apc/proc/malfvacate(forced) + if(!occupier) + return + if(occupier.parent && occupier.parent.stat != DEAD) + occupier.mind.transfer_to(occupier.parent) + occupier.parent.shunted = 0 + occupier.parent.adjustOxyLoss(occupier.getOxyLoss()) + occupier.parent.cancel_camera() + qdel(occupier) + if(seclevel2num(get_security_level()) == SEC_LEVEL_DELTA) + for(var/obj/item/pinpointer/point in GLOB.pinpointer_list) + for(var/mob/living/silicon/ai/A in ai_list) + if((A.stat != DEAD) && A.nuking) + point.the_disk = A //The pinpointer tracks the AI back into its core. + else + to_chat(occupier, "Primary core damaged, unable to return core processes.") + if(forced) + occupier.loc = loc + occupier.death() + occupier.gib() + for(var/obj/item/pinpointer/point in GLOB.pinpointer_list) + point.the_disk = null //Pinpointers go back to tracking the nuke disk + +/obj/machinery/power/apc/proc/ion_act() + //intended to be exactly the same as an AI malf attack + if(!src.malfhack && is_station_level(src.z)) + if(prob(3)) + src.locked = 1 + if(src.cell.charge > 0) + src.cell.charge = 0 + cell.corrupt() + src.malfhack = 1 + update_icon() + var/datum/effect_system/smoke_spread/smoke = new + smoke.set_up(3, 0, src.loc) + smoke.attach(src) + smoke.start() + do_sparks(3, 1, src) + for(var/mob/M in viewers(src)) + M.show_message("The [src.name] suddenly lets out a blast of smoke and some sparks!", 3, "You hear sizzling electronics.", 2) + + +/obj/machinery/power/apc/surplus() + if(terminal) + return terminal.surplus() + else + return 0 + +/obj/machinery/power/apc/add_load(amount) + if(terminal && terminal.powernet) + terminal.add_load(amount) + +/obj/machinery/power/apc/avail() + if(terminal) + return terminal.avail() + else + return 0 + +/obj/machinery/power/apc/process() + if(stat & (BROKEN|MAINT)) + return + if(!area.requires_power) + return + + lastused_light = area.usage(STATIC_LIGHT) + lastused_light += area.usage(LIGHT) + lastused_equip = area.usage(EQUIP) + lastused_equip += area.usage(STATIC_EQUIP) + lastused_environ = area.usage(ENVIRON) + lastused_environ += area.usage(STATIC_ENVIRON) + area.clear_usage() + + lastused_total = lastused_light + lastused_equip + lastused_environ + + //store states to update icon if any change + var/last_lt = lighting + var/last_eq = equipment + var/last_en = environ + var/last_ch = charging + + var/excess = surplus() + + if(!src.avail()) + main_status = 0 + else if(excess < 0) + main_status = 1 + else + main_status = 2 + + if(debug) + log_debug("Status: [main_status] - Excess: [excess] - Last Equip: [lastused_equip] - Last Light: [lastused_light] - Longterm: [longtermpower]") + + if(cell && !shorted) + // draw power from cell as before to power the area + var/cellused = min(cell.charge, GLOB.CELLRATE * lastused_total) // clamp deduction to a max, amount left in cell + cell.use(cellused) + + if(excess > lastused_total) // if power excess recharge the cell + // by the same amount just used + cell.give(cellused) + add_load(cellused/GLOB.CELLRATE) // add the load used to recharge the cell + + + else // no excess, and not enough per-apc + if((cell.charge/GLOB.CELLRATE + excess) >= lastused_total) // can we draw enough from cell+grid to cover last usage? + cell.charge = min(cell.maxcharge, cell.charge + GLOB.CELLRATE * excess) //recharge with what we can + add_load(excess) // so draw what we can from the grid + charging = 0 + + else // not enough power available to run the last tick! + charging = 0 + chargecount = 0 + // This turns everything off in the case that there is still a charge left on the battery, just not enough to run the room. + equipment = autoset(equipment, 0) + lighting = autoset(lighting, 0) + environ = autoset(environ, 0) + autoflag = 0 + + + // Set channels depending on how much charge we have left + + // Allow the APC to operate as normal if the cell can charge + if(charging && longtermpower < 10) + longtermpower += 1 + else if(longtermpower > -10) + longtermpower -= 2 + + if(cell.charge >= 1250 || longtermpower > 0) // Put most likely at the top so we don't check it last, effeciency 101 + if(autoflag != 3) + equipment = autoset(equipment, 1) + lighting = autoset(lighting, 1) + environ = autoset(environ, 1) + autoflag = 3 + if(report_power_alarm && is_station_contact(z)) + SSalarms.power_alarm.clearAlarm(loc, src) + else if(cell.charge < 1250 && cell.charge > 750 && longtermpower < 0) // <30%, turn off equipment + if(autoflag != 2) + equipment = autoset(equipment, 2) + lighting = autoset(lighting, 1) + environ = autoset(environ, 1) + if(report_power_alarm && is_station_contact(z)) + SSalarms.power_alarm.triggerAlarm(loc, src) + autoflag = 2 + else if(cell.charge < 750 && cell.charge > 10) // <15%, turn off lighting & equipment + if((autoflag > 1 && longtermpower < 0) || (autoflag > 1 && longtermpower >= 0)) + equipment = autoset(equipment, 2) + lighting = autoset(lighting, 2) + environ = autoset(environ, 1) + if(report_power_alarm && is_station_contact(z)) + SSalarms.power_alarm.triggerAlarm(loc, src) + autoflag = 1 + else if(cell.charge <= 0) // zero charge, turn all off + if(autoflag != 0) + equipment = autoset(equipment, 0) + lighting = autoset(lighting, 0) + environ = autoset(environ, 0) + if(report_power_alarm && is_station_contact(z)) + SSalarms.power_alarm.triggerAlarm(loc, src) + autoflag = 0 + + // now trickle-charge the cell + if(chargemode && charging == 1 && operating) + if(excess > 0) // check to make sure we have enough to charge + // Max charge is capped to % per second constant + var/ch = min(excess*GLOB.CELLRATE, cell.maxcharge*GLOB.CHARGELEVEL) + add_load(ch/GLOB.CELLRATE) // Removes the power we're taking from the grid + cell.give(ch) // actually recharge the cell + + else + charging = 0 // stop charging + chargecount = 0 + + // show cell as fully charged if so + if(cell.charge >= cell.maxcharge) + cell.charge = cell.maxcharge + charging = 2 + + if(chargemode) + if(!charging) + if(excess > cell.maxcharge*GLOB.CHARGELEVEL) + chargecount++ + else + chargecount = 0 + + if(chargecount == 10) + + chargecount = 0 + charging = 1 + + else // chargemode off + charging = 0 + chargecount = 0 + + if(excess >= 5000000 && !shock_proof) //If there's more than 5,000,000 watts in the grid, start arcing and shocking people. + if(prob(5)) + var/list/shock_mobs = list() + for(var/C in view(get_turf(src), 5)) //We only want to shock a single random mob in range, not every one. + if(isliving(C)) + shock_mobs += C + if(shock_mobs.len) + var/mob/living/L = pick(shock_mobs) + L.electrocute_act(rand(5, 25), "electrical arc") + playsound(get_turf(L), 'sound/effects/eleczap.ogg', 75, 1) + Beam(L, icon_state = "lightning[rand(1, 12)]", icon = 'icons/effects/effects.dmi', time = 5) + + else // no cell, switch everything off + + charging = 0 + chargecount = 0 + equipment = autoset(equipment, 0) + lighting = autoset(lighting, 0) + environ = autoset(environ, 0) + if(report_power_alarm && is_station_contact(z)) + SSalarms.power_alarm.triggerAlarm(loc, src) + autoflag = 0 + + // update icon & area power if anything changed + + if(last_lt != lighting || last_eq != equipment || last_en != environ) + queue_icon_update() + update() + else if(last_ch != charging) + queue_icon_update() + +/obj/machinery/power/apc/proc/autoset(var/val, var/on) + if(on==0) + if(val==2) // if on, return off + return 0 + else if(val==3) // if auto-on, return auto-off + return 1 + + else if(on==1) + if(val==1) // if auto-off, return auto-on + return 3 + + else if(on==2) + if(val==3) // if auto-on, return auto-off + return 1 + + return val + +// damage and destruction acts + +/obj/machinery/power/apc/emp_act(severity) + if(cell) + cell.emp_act(severity) + if(occupier) + occupier.emp_act(severity) + lighting = 0 + equipment = 0 + environ = 0 + update_icon() + update() + spawn(600) + equipment = 3 + environ = 3 + update_icon() + update() + ..() + +/obj/machinery/power/apc/blob_act(obj/structure/blob/B) + set_broken() + +/obj/machinery/power/apc/disconnect_terminal() + if(terminal) + terminal.master = null + terminal = null + +/obj/machinery/power/apc/proc/set_broken() + if(malfai && operating) + malfai.malf_picker.processing_time = Clamp(malfai.malf_picker.processing_time - 10,0,1000) + stat |= BROKEN + operating = 0 + if(occupier) + malfvacate(1) + update_icon() + update() + +// overload all the lights in this APC area + +/obj/machinery/power/apc/proc/overload_lighting(chance = 100) + if(!operating || shorted) + return + if(cell && cell.charge >= 20) + cell.use(20) + spawn(0) + for(var/obj/machinery/light/L in area) + if(prob(chance)) + L.break_light_tube(0, 1) + stoplag() + +/obj/machinery/power/apc/proc/null_charge() + for(var/obj/machinery/light/L in area) + L.break_light_tube(0, 1) + stoplag() + +/obj/machinery/power/apc/proc/setsubsystem(val) + if(cell && cell.charge > 0) + return (val==1) ? 0 : val + else if(val == 3) + return 1 + else + return 0 + +/obj/machinery/power/apc/proc/set_nightshift(on) + set waitfor = FALSE + nightshift_lights = on + for(var/obj/machinery/light/L in area) + if(L.nightshift_allowed) + L.nightshift_enabled = nightshift_lights + L.update(FALSE) + CHECK_TICK + +#undef APC_UPDATE_ICON_COOLDOWN diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index 6d22afe282f..7260c836d78 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -1,859 +1,867 @@ -#define HEALPERCABLE 3 -#define MAXCABLEPERHEAL 8 -/////////////////////////////// -//CABLE STRUCTURE -/////////////////////////////// - - -//////////////////////////////// -// Definitions -//////////////////////////////// - -/* Cable directions (d1 and d2) - - - 9 1 5 - \ | / - 8 - 0 - 4 - / | \ - 10 2 6 - -If d1 = 0 and d2 = 0, there's no cable -If d1 = 0 and d2 = dir, it's a O-X cable, getting from the center of the tile to dir (knot cable) -If d1 = dir1 and d2 = dir2, it's a full X-X cable, getting from dir1 to dir2 -By design, d1 is the smallest direction and d2 is the highest -*/ - -/obj/structure/cable - level = 1 - anchored = 1 - on_blueprints = TRUE - var/datum/powernet/powernet - name = "power cable" - desc = "A flexible superconducting cable for heavy-duty power transfer" - icon = 'icons/obj/power_cond/power_cond_white.dmi' - icon_state = "0-1" - var/d1 = 0 - var/d2 = 1 - layer = WIRE_LAYER //Just below unary stuff, which is at 2.45 and above pipes, which are at 2.4 - color = COLOR_RED - -/obj/structure/cable/yellow - color = COLOR_YELLOW - -/obj/structure/cable/green - color = COLOR_GREEN - -/obj/structure/cable/blue - color = COLOR_BLUE - -/obj/structure/cable/pink - color = COLOR_PINK - -/obj/structure/cable/orange - color = COLOR_ORANGE - -/obj/structure/cable/cyan - color = COLOR_CYAN - -/obj/structure/cable/white - color = COLOR_WHITE - -/obj/structure/cable/Initialize(mapload) - . = ..() - - // ensure d1 & d2 reflect the icon_state for entering and exiting cable - var/dash = findtext(icon_state, "-") - d1 = text2num(copytext( icon_state, 1, dash )) - d2 = text2num(copytext( icon_state, dash+1 )) - - var/turf/T = get_turf(src) // hide if turf is not intact - if(level == 1) - hide(T.intact) - LAZYADD(GLOB.cable_list, src) //add it to the global cable list - -/obj/structure/cable/Destroy() // called when a cable is deleted - if(powernet) - cut_cable_from_powernet() // update the powernets - LAZYREMOVE(GLOB.cable_list, src) //remove it from global cable list - return ..() // then go ahead and delete the cable - -/obj/structure/cable/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - var/turf/T = get_turf(src) - if(d1) // 0-X cables are 1 unit, X-X cables are 2 units long - new/obj/item/stack/cable_coil(T, 2, paramcolor = color) - else - new/obj/item/stack/cable_coil(T, 1, paramcolor = color) - qdel(src) - -/////////////////////////////////// -// General procedures -/////////////////////////////////// - -//If underfloor, hide the cable -/obj/structure/cable/hide(i) - - if(level == 1 && isturf(loc)) - invisibility = i ? 101 : 0 - updateicon() - -/obj/structure/cable/proc/updateicon() - if(invisibility) - icon_state = "[d1]-[d2]-f" - else - icon_state = "[d1]-[d2]" - - -//////////////////////////////////////////// -// Power related -/////////////////////////////////////////// - -// All power generation handled in add_avail() -// Machines should use add_load(), surplus(), avail() -// Non-machines should use add_delayedload(), delayed_surplus(), newavail() - -/obj/structure/cable/proc/add_avail(amount) - if(powernet) - powernet.newavail += amount - -/obj/structure/cable/proc/add_load(amount) - if(powernet) - powernet.load += amount - -/obj/structure/cable/proc/surplus() - if(powernet) - return Clamp(powernet.avail-powernet.load, 0, powernet.avail) - else - return 0 - -/obj/structure/cable/proc/avail() - if(powernet) - return powernet.avail - else - return 0 - -/obj/structure/cable/proc/add_delayedload(amount) - if(powernet) - powernet.delayedload += amount - -/obj/structure/cable/proc/delayed_surplus() - if(powernet) - return Clamp(powernet.newavail - powernet.delayedload, 0, powernet.newavail) - else - return 0 - -/obj/structure/cable/proc/newavail() - if(powernet) - return powernet.newavail - else - return 0 - -//Telekinesis has no effect on a cable -/obj/structure/cable/attack_tk(mob/user) - return - -// Items usable on a cable : -// - Wirecutters : cut it duh ! -// - Cable coil : merge cables -// - Multitool : get the power currently passing through the cable -// -/obj/structure/cable/attackby(obj/item/W, mob/user) - - var/turf/T = get_turf(src) - if(T.intact) - return - - if(iswirecutter(W)) - if(shock(user, 50)) - return - user.visible_message("[user] cuts the cable.", "You cut the cable.") - investigate_log("was cut by [key_name(usr, 1)] in [get_area(user)]([T.x], [T.y], [T.z] - [ADMIN_JMP(T)])","wires") - deconstruct() - return - - - else if(istype(W, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/coil = W - if(coil.get_amount() < 1) - to_chat(user, "Not enough cable!") - return - coil.cable_join(src, user) - - else if(istype(W, /obj/item/twohanded/rcl)) - var/obj/item/twohanded/rcl/R = W - if(R.loaded) - R.loaded.cable_join(src, user) - R.is_empty(user) - - else if(istype(W, /obj/item/multitool)) - - if(powernet && (powernet.avail > 0)) // is it powered? - to_chat(user, "Total power: [DisplayPower(powernet.avail)]\nLoad: [DisplayPower(powernet.load)]\nExcess power: [DisplayPower(surplus())]") - else - to_chat(user, "The cable is not powered.") - shock(user, 5, 0.2) - - else if(istype(W, /obj/item/toy/crayon)) - var/obj/item/toy/crayon/C = W - cable_color(C.colourName) - - else - if(W.flags & CONDUCT) - shock(user, 50, 0.7) - - add_fingerprint(user) - -// shock the user with probability prb -/obj/structure/cable/proc/shock(mob/user, prb, siemens_coeff = 1) - if(!prob(prb)) - return FALSE - if(electrocute_mob(user, powernet, src, siemens_coeff)) - do_sparks(5, 1, src) - return TRUE - else - return FALSE - -/obj/structure/cable/singularity_pull(S, current_size) - ..() - if(current_size >= STAGE_FIVE) - deconstruct() - -obj/structure/cable/proc/cable_color(var/colorC) - if(colorC) - if(colorC == "rainbow") - color = color_rainbow() - else - color = colorC - else - color = "#DD0000" - -/obj/structure/cable/proc/color_rainbow() - color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) - return color - -///////////////////////////////////////////////// -// Cable laying helpers -//////////////////////////////////////////////// - -//handles merging diagonally matching cables -//for info : direction^3 is flipping horizontally, direction^12 is flipping vertically -/obj/structure/cable/proc/mergeDiagonalsNetworks(direction) - - //search for and merge diagonally matching cables from the first direction component (north/south) - var/turf/T = get_step(src, direction&3)//go north/south - - for(var/obj/structure/cable/C in T) - - if(!C) - continue - - if(src == C) - continue - - if(C.d1 == (direction^3) || C.d2 == (direction^3)) //we've got a diagonally matching cable - if(!C.powernet) //if the matching cable somehow got no powernet, make him one (should not happen for cables) - var/datum/powernet/newPN = new() - newPN.add_cable(C) - - if(powernet) //if we already have a powernet, then merge the two powernets - merge_powernets(powernet,C.powernet) - else - C.powernet.add_cable(src) //else, we simply connect to the matching cable powernet - - //the same from the second direction component (east/west) - T = get_step(src, direction&12)//go east/west - - for(var/obj/structure/cable/C in T) - - if(!C) - continue - - if(src == C) - continue - if(C.d1 == (direction^12) || C.d2 == (direction^12)) //we've got a diagonally matching cable - if(!C.powernet) //if the matching cable somehow got no powernet, make him one (should not happen for cables) - var/datum/powernet/newPN = new() - newPN.add_cable(C) - - if(powernet) //if we already have a powernet, then merge the two powernets - merge_powernets(powernet,C.powernet) - else - C.powernet.add_cable(src) //else, we simply connect to the matching cable powernet - -// merge with the powernets of power objects in the given direction -/obj/structure/cable/proc/mergeConnectedNetworks(direction) - - var/fdir = (!direction)? 0 : turn(direction, 180) //flip the direction, to match with the source position on its turf - - if(!(d1 == direction || d2 == direction)) //if the cable is not pointed in this direction, do nothing - return - - var/turf/TB = get_step(src, direction) - - for(var/obj/structure/cable/C in TB) - - if(!C) - continue - - if(src == C) - continue - - if(C.d1 == fdir || C.d2 == fdir) //we've got a matching cable in the neighbor turf - if(!C.powernet) //if the matching cable somehow got no powernet, make him one (should not happen for cables) - var/datum/powernet/newPN = new() - newPN.add_cable(C) - - if(powernet) //if we already have a powernet, then merge the two powernets - merge_powernets(powernet,C.powernet) - else - C.powernet.add_cable(src) //else, we simply connect to the matching cable powernet - -// merge with the powernets of power objects in the source turf -/obj/structure/cable/proc/mergeConnectedNetworksOnTurf() - var/list/to_connect = list() - - if(!powernet) //if we somehow have no powernet, make one (should not happen for cables) - var/datum/powernet/newPN = new() - newPN.add_cable(src) - - //first let's add turf cables to our powernet - //then we'll connect machines on turf with a node cable is present - for(var/AM in loc) - if(istype(AM, /obj/structure/cable)) - var/obj/structure/cable/C = AM - if(C.d1 == d1 || C.d2 == d1 || C.d1 == d2 || C.d2 == d2) //only connected if they have a common direction - if(C.powernet == powernet) - continue - if(C.powernet) - merge_powernets(powernet, C.powernet) - else - powernet.add_cable(C) //the cable was powernetless, let's just add it to our powernet - - else if(istype(AM, /obj/machinery/power/apc)) - var/obj/machinery/power/apc/N = AM - if(!N.terminal) - continue // APC are connected through their terminal - - if(N.terminal.powernet == powernet) - continue - - to_connect += N.terminal //we'll connect the machines after all cables are merged - - else if(istype(AM, /obj/machinery/power)) //other power machines - var/obj/machinery/power/M = AM - - if(M.powernet == powernet) - continue - - to_connect += M //we'll connect the machines after all cables are merged - - //now that cables are done, let's connect found machines - for(var/obj/machinery/power/PM in to_connect) - if(!PM.connect_to_network()) - PM.disconnect_from_network() //if we somehow can't connect the machine to the new powernet, remove it from the old nonetheless - -////////////////////////////////////////////// -// Powernets handling helpers -////////////////////////////////////////////// - -//if powernetless_only = 1, will only get connections without powernet -/obj/structure/cable/proc/get_connections(powernetless_only = 0) - . = list() // this will be a list of all connected power objects - var/turf/T - - //get matching cables from the first direction - if(d1) //if not a node cable - T = get_step(src, d1) - if(T) - . += power_list(T, src, turn(d1, 180), powernetless_only) //get adjacents matching cables - - if(d1&(d1-1)) //diagonal direction, must check the 4 possibles adjacents tiles - T = get_step(src,d1&3) // go north/south - if(T) - . += power_list(T, src, d1 ^ 3, powernetless_only) //get diagonally matching cables - T = get_step(src,d1&12) // go east/west - if(T) - . += power_list(T, src, d1 ^ 12, powernetless_only) //get diagonally matching cables - - . += power_list(loc, src, d1, powernetless_only) //get on turf matching cables - - //do the same on the second direction (which can't be 0) - T = get_step(src, d2) - if(T) - . += power_list(T, src, turn(d2, 180), powernetless_only) //get adjacents matching cables - - if(d2&(d2-1)) //diagonal direction, must check the 4 possibles adjacents tiles - T = get_step(src,d2&3) // go north/south - if(T) - . += power_list(T, src, d2 ^ 3, powernetless_only) //get diagonally matching cables - T = get_step(src,d2&12) // go east/west - if(T) - . += power_list(T, src, d2 ^ 12, powernetless_only) //get diagonally matching cables - . += power_list(loc, src, d2, powernetless_only) //get on turf matching cables - - return . - -//should be called after placing a cable which extends another cable, creating a "smooth" cable that no longer terminates in the centre of a turf. -//needed as this can, unlike other placements, disconnect cables -/obj/structure/cable/proc/denode() - var/turf/T1 = loc - if(!T1) - return - - var/list/powerlist = power_list(T1,src,0,0) //find the other cables that ended in the centre of the turf, with or without a powernet - if(powerlist.len>0) - var/datum/powernet/PN = new() - propagate_network(powerlist[1],PN) //propagates the new powernet beginning at the source cable - - if(PN.is_empty()) //can happen with machines made nodeless when smoothing cables - qdel(PN) - -/obj/structure/cable/proc/auto_propogate_cut_cable(obj/O) - if(O && !QDELETED(O)) - var/datum/powernet/newPN = new()// creates a new powernet... - propagate_network(O, newPN)//... and propagates it to the other side of the cable - -// cut the cable's powernet at this cable and updates the powergrid -/obj/structure/cable/proc/cut_cable_from_powernet(remove=TRUE) - var/turf/T1 = loc - var/list/P_list - if(!T1) - return - if(d1) - T1 = get_step(T1, d1) - P_list = power_list(T1, src, turn(d1,180),0,cable_only = 1) // what adjacently joins on to cut cable... - - P_list += power_list(loc, src, d1, 0, cable_only = 1)//... and on turf - - - if(P_list.len == 0)//if nothing in both list, then the cable was a lone cable, just delete it and its powernet - powernet.remove_cable(src) - - for(var/obj/machinery/power/P in T1)//check if it was powering a machine - if(!P.connect_to_network()) //can't find a node cable on a the turf to connect to - P.disconnect_from_network() //remove from current network (and delete powernet) - return - - var/obj/O = P_list[1] - // remove the cut cable from its turf and powernet, so that it doesn't get count in propagate_network worklist - if(remove) - loc = null - powernet.remove_cable(src) //remove the cut cable from its powernet - - // queue it to rebuild - SSmachines.deferred_powernet_rebuilds += O -// addtimer(CALLBACK(O, .proc/auto_propogate_cut_cable, O), 0) //so we don't rebuild the network X times when singulo/explosion destroys a line of X cables - - // Disconnect machines connected to nodes - if(d1 == 0) // if we cut a node (O-X) cable - for(var/obj/machinery/power/P in T1) - if(!P.connect_to_network()) //can't find a node cable on a the turf to connect to - P.disconnect_from_network() //remove from current network - - -/////////////////////////////////////////////// -// The cable coil object, used for laying cable -/////////////////////////////////////////////// - -//////////////////////////////// -// Definitions -//////////////////////////////// - -GLOBAL_LIST_INIT(cable_coil_recipes, list (new/datum/stack_recipe("cable restraints", /obj/item/restraints/handcuffs/cable, 15))) - -/obj/item/stack/cable_coil - name = "cable coil" - singular_name = "cable" - icon = 'icons/obj/power.dmi' - icon_state = "coil" - item_state = "coil_red" - amount = MAXCOIL - max_amount = MAXCOIL - merge_type = /obj/item/stack/cable_coil // This is here to let its children merge between themselves - color = COLOR_RED - desc = "A coil of power cable." - throwforce = 10 - w_class = WEIGHT_CLASS_SMALL - throw_speed = 2 - throw_range = 5 - materials = list(MAT_METAL=10, MAT_GLASS=5) - flags = CONDUCT - slot_flags = SLOT_BELT - item_state = "coil" - attack_verb = list("whipped", "lashed", "disciplined", "flogged") - usesound = 'sound/items/deconstruct.ogg' - toolspeed = 1 - -/obj/item/stack/cable_coil/suicide_act(mob/user) - if(locate(/obj/structure/chair/stool) in user.loc) - user.visible_message("[user] is making a noose with the [name]! It looks like [user.p_theyre()] trying to commit suicide.") - else - user.visible_message("[user] is strangling [user.p_them()]self with the [name]! It looks like [user.p_theyre()] trying to commit suicide.") - return OXYLOSS - -/obj/item/stack/cable_coil/New(loc, length = MAXCOIL, paramcolor = null) - ..() - amount = length - if(paramcolor) - color = paramcolor - pixel_x = rand(-2,2) - pixel_y = rand(-2,2) - update_icon() - recipes = GLOB.cable_coil_recipes - update_wclass() - -/////////////////////////////////// -// General procedures -/////////////////////////////////// -//you can use wires to heal robotics -/obj/item/stack/cable_coil/attack(mob/M, mob/user) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - var/obj/item/organ/external/S = H.bodyparts_by_name[user.zone_selected] - - if(!S) - return - if(!S.is_robotic() || user.a_intent != INTENT_HELP || S.open == 2) - return ..() - - if(S.burn_dam > ROBOLIMB_SELF_REPAIR_CAP) - to_chat(user, "The damage is far too severe to patch over externally.") - return - - if(!S.burn_dam) - to_chat(user, "Nothing to fix!") - return - - if(H == user) - if(!do_mob(user, H, 10)) - return 0 - var/cable_used = 0 - var/childlist - if(!isnull(S.children)) - childlist = S.children.Copy() - var/parenthealed = FALSE - while(cable_used <= MAXCABLEPERHEAL && amount >= 1) - var/obj/item/organ/external/E - if(S.burn_dam) - E = S - else if(LAZYLEN(childlist)) - E = pick_n_take(childlist) - if(!E.burn_dam || !E.is_robotic()) - continue - else if(S.parent && !parenthealed) - E = S.parent - parenthealed = TRUE - if(!E.burn_dam || !E.is_robotic()) - break - else - break - while(cable_used <= MAXCABLEPERHEAL && E.burn_dam && amount >= 1) - use(1) - cable_used += 1 - E.heal_damage(0, HEALPERCABLE, 0, 1) - user.visible_message("\The [user] repairs some burn damage on \the [M]'s [E.name] with \the [src].") - return 1 - - else - return ..() - -/obj/item/stack/cable_coil/split() - var/obj/item/stack/cable_coil/C = ..() - C.color = color - return C - -/obj/item/stack/cable_coil/update_icon() - if(!color) - color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_ORANGE, COLOR_WHITE, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) - if(amount == 1) - icon_state = "coil1" - name = "cable piece" - else if(amount == 2) - icon_state = "coil2" - name = "cable piece" - else - icon_state = "coil" - name = "cable coil" - ..() - -/obj/item/stack/cable_coil/proc/update_wclass() - if(amount == 1) - w_class = WEIGHT_CLASS_TINY - else - w_class = WEIGHT_CLASS_SMALL - -/obj/item/stack/cable_coil/examine(mob/user) - . = ..() - if(in_range(user, src)) - if(get_amount() == 1) - . += "A short piece of power cable." - else if(get_amount() == 2) - . += "A piece of power cable." - else - . += "A coil of power cable. There are [get_amount()] lengths of cable in the coil." - -// Items usable on a cable coil : -// - Wirecutters : cut them duh ! -// - Cable coil : merge cables -/obj/item/stack/cable_coil/attackby(obj/item/W, mob/user) - ..() - if(istype(W, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = W - // Cable merging is handled by parent proc - if(C.amount >= MAXCOIL) - to_chat(user, "The coil is as long as it will get.") - return - if( (C.amount + src.amount <= MAXCOIL) ) - to_chat(user, "You join the cable coils together.") - return - else - to_chat(user, "You transfer [get_amount_transferred()] length\s of cable from one coil to the other.") - return - - if(istype(W, /obj/item/toy/crayon)) - var/obj/item/toy/crayon/C = W - cable_color(C.colourName) - -/////////////////////////////////////////////// -// Cable laying procedures -////////////////////////////////////////////// - -/obj/item/stack/cable_coil/proc/get_new_cable(location) - var/obj/structure/cable/C = new(location) - C.cable_color(color) - - return C - -// called when cable_coil is clicked on a turf/simulated/floor -/obj/item/stack/cable_coil/proc/place_turf(turf/T, mob/user, dirnew) - if(!isturf(user.loc)) - return - - if(!isturf(T) || T.intact || !T.can_have_cabling()) - to_chat(user, "You can only lay cables on catwalks and plating!") - return - - if(get_amount() < 1) // Out of cable - to_chat(user, "There is no cable left!") - return - - if(get_dist(T,user) > 1) // Too far - to_chat(user, "You can't lay cable at a place that far away!") - return - - var/dirn - if(!dirnew) //If we weren't given a direction, come up with one! (Called as null from catwalk.dm and floor.dm) - if(user.loc == T) - dirn = user.dir //If laying on the tile we're on, lay in the direction we're facing - else - dirn = get_dir(T, user) - else - dirn = dirnew - - for(var/obj/structure/cable/LC in T) - if(LC.d2 == dirn && LC.d1 == 0) - to_chat(user, "There's already a cable at that position!") - return - - var/obj/structure/cable/C = get_new_cable(T) - - //set up the new cable - C.d1 = 0 //it's a O-X node cable - C.d2 = dirn - C.add_fingerprint(user) - C.updateicon() - - //create a new powernet with the cable, if needed it will be merged later - var/datum/powernet/PN = new() - PN.add_cable(C) - - C.mergeConnectedNetworks(C.d2) //merge the powernet with adjacents powernets - C.mergeConnectedNetworksOnTurf() //merge the powernet with on turf powernets - - if(C.d2 & (C.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions - C.mergeDiagonalsNetworks(C.d2) - - use(1) - - if(C.shock(user, 50)) - if(prob(50)) //fail - new /obj/item/stack/cable_coil(get_turf(C), 1, paramcolor = C.color) - C.deconstruct() - - return C - -// called when cable_coil is click on an installed obj/cable -// or click on a turf that already contains a "node" cable -/obj/item/stack/cable_coil/proc/cable_join(obj/structure/cable/C, mob/user) - var/turf/U = user.loc - if(!isturf(U)) - return - - var/turf/T = get_turf(C) - - if(!isturf(T) || T.intact) // sanity checks, also stop use interacting with T-scanner revealed cable - return - - if(get_dist(C, user) > 1) // make sure it's close enough - to_chat(user, "You can't lay cable at a place that far away!") - return - - - if(U == T) //if clicked on the turf we're standing on, try to put a cable in the direction we're facing - place_turf(T,user) - return - - var/dirn = get_dir(C, user) - - // one end of the clicked cable is pointing towards us - if(C.d1 == dirn || C.d2 == dirn) - if(U.intact) // can't place a cable if the floor is complete - to_chat(user, "You can't lay cable there unless the floor tiles are removed!") - return - else - // cable is pointing at us, we're standing on an open tile - // so create a stub pointing at the clicked cable on our tile - - var/fdirn = turn(dirn, 180) // the opposite direction - - for(var/obj/structure/cable/LC in U) // check to make sure there's not a cable there already - if(LC.d1 == fdirn || LC.d2 == fdirn) - to_chat(user, "There's already a cable at that position!") - return - - var/obj/structure/cable/NC = get_new_cable (U) - - NC.d1 = 0 - NC.d2 = fdirn - NC.add_fingerprint(user) - NC.update_icon() - - //create a new powernet with the cable, if needed it will be merged later - var/datum/powernet/newPN = new() - newPN.add_cable(NC) - - NC.mergeConnectedNetworks(NC.d2) //merge the powernet with adjacents powernets - NC.mergeConnectedNetworksOnTurf() //merge the powernet with on turf powernets - - if(NC.d2 & (NC.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions - NC.mergeDiagonalsNetworks(NC.d2) - - use(1) - - if(NC.shock(user, 50)) - if(prob(50)) //fail - NC.deconstruct() - return - - // exisiting cable doesn't point at our position, so see if it's a stub - else if(C.d1 == 0) - // if so, make it a full cable pointing from it's old direction to our dirn - var/nd1 = C.d2 // these will be the new directions - var/nd2 = dirn - - - if(nd1 > nd2) // swap directions to match icons/states - nd1 = dirn - nd2 = C.d2 - - - for(var/obj/structure/cable/LC in T) // check to make sure there's no matching cable - if(LC == C) // skip the cable we're interacting with - continue - if((LC.d1 == nd1 && LC.d2 == nd2) || (LC.d1 == nd2 && LC.d2 == nd1) ) // make sure no cable matches either direction - to_chat(user, "There's already a cable at that position!") - return - - - C.cable_color(color) - - C.d1 = nd1 - C.d2 = nd2 - - C.add_fingerprint() - C.updateicon() - - - C.mergeConnectedNetworks(C.d1) //merge the powernets... - C.mergeConnectedNetworks(C.d2) //...in the two new cable directions - C.mergeConnectedNetworksOnTurf() - - if(C.d1 & (C.d1 - 1))// if the cable is layed diagonally, check the others 2 possible directions - C.mergeDiagonalsNetworks(C.d1) - - if(C.d2 & (C.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions - C.mergeDiagonalsNetworks(C.d2) - - use(1) - - if(C.shock(user, 50)) - if(prob(50)) //fail - C.deconstruct() - return - - C.denode()// this call may have disconnected some cables that terminated on the centre of the turf, if so split the powernets. - return - -////////////////////////////// -// Misc. -///////////////////////////// - -/obj/item/stack/cable_coil/cut - item_state = "coil2" - -/obj/item/stack/cable_coil/cut/Initialize(mapload) - . = ..() - src.amount = rand(1,2) - pixel_x = rand(-2,2) - pixel_y = rand(-2,2) - update_icon() - update_wclass() - -/obj/item/stack/cable_coil/yellow - color = COLOR_YELLOW - -/obj/item/stack/cable_coil/blue - color = COLOR_BLUE - -/obj/item/stack/cable_coil/green - color = COLOR_GREEN - -/obj/item/stack/cable_coil/pink - color = COLOR_PINK - -/obj/item/stack/cable_coil/orange - color = COLOR_ORANGE - -/obj/item/stack/cable_coil/cyan - color = COLOR_CYAN - -/obj/item/stack/cable_coil/white - color = COLOR_WHITE - -/obj/item/stack/cable_coil/random/New() - color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_WHITE, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) - ..() - -/obj/item/stack/cable_coil/proc/cable_color(var/colorC) - if(colorC) - if(colorC == "rainbow") - colorC = color_rainbow() - color = colorC - else - color = COLOR_RED - -/obj/item/stack/cable_coil/proc/color_rainbow() - color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) - return color - -/obj/item/stack/cable_coil/cyborg - name = "cyborg cable coil" - -/obj/item/stack/cable_coil/cyborg/attack_self(mob/user) - var/cablecolor = input(user,"Pick a cable color.","Cable Color") in list("red","yellow","green","blue","pink","orange","cyan","white") - color = cablecolor - update_icon() - -#undef MAXCABLEPERHEAL -#undef HEALPERCABLE +#define HEALPERCABLE 3 +#define MAXCABLEPERHEAL 8 +/////////////////////////////// +//CABLE STRUCTURE +/////////////////////////////// + + +//////////////////////////////// +// Definitions +//////////////////////////////// + +/* Cable directions (d1 and d2) + + + 9 1 5 + \ | / + 8 - 0 - 4 + / | \ + 10 2 6 + +If d1 = 0 and d2 = 0, there's no cable +If d1 = 0 and d2 = dir, it's a O-X cable, getting from the center of the tile to dir (knot cable) +If d1 = dir1 and d2 = dir2, it's a full X-X cable, getting from dir1 to dir2 +By design, d1 is the smallest direction and d2 is the highest +*/ + +/obj/structure/cable + level = 1 + anchored = 1 + on_blueprints = TRUE + var/datum/powernet/powernet + name = "power cable" + desc = "A flexible superconducting cable for heavy-duty power transfer" + icon = 'icons/obj/power_cond/power_cond_white.dmi' + icon_state = "0-1" + var/d1 = 0 + var/d2 = 1 + layer = WIRE_LAYER //Just below unary stuff, which is at 2.45 and above pipes, which are at 2.4 + color = COLOR_RED + +/obj/structure/cable/yellow + color = COLOR_YELLOW + +/obj/structure/cable/green + color = COLOR_GREEN + +/obj/structure/cable/blue + color = COLOR_BLUE + +/obj/structure/cable/pink + color = COLOR_PINK + +/obj/structure/cable/orange + color = COLOR_ORANGE + +/obj/structure/cable/cyan + color = COLOR_CYAN + +/obj/structure/cable/white + color = COLOR_WHITE + +/obj/structure/cable/Initialize(mapload) + . = ..() + + // ensure d1 & d2 reflect the icon_state for entering and exiting cable + var/dash = findtext(icon_state, "-") + d1 = text2num(copytext( icon_state, 1, dash )) + d2 = text2num(copytext( icon_state, dash+1 )) + + var/turf/T = get_turf(src) // hide if turf is not intact + if(level == 1) + hide(T.intact) + LAZYADD(GLOB.cable_list, src) //add it to the global cable list + +/obj/structure/cable/Destroy() // called when a cable is deleted + if(powernet) + cut_cable_from_powernet() // update the powernets + LAZYREMOVE(GLOB.cable_list, src) //remove it from global cable list + return ..() // then go ahead and delete the cable + +/obj/structure/cable/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + var/turf/T = get_turf(src) + if(d1) // 0-X cables are 1 unit, X-X cables are 2 units long + new/obj/item/stack/cable_coil(T, 2, paramcolor = color) + else + new/obj/item/stack/cable_coil(T, 1, paramcolor = color) + qdel(src) + +/////////////////////////////////// +// General procedures +/////////////////////////////////// + +//If underfloor, hide the cable +/obj/structure/cable/hide(i) + + if(level == 1 && isturf(loc)) + invisibility = i ? 101 : 0 + updateicon() + +/obj/structure/cable/proc/updateicon() + if(invisibility) + icon_state = "[d1]-[d2]-f" + else + icon_state = "[d1]-[d2]" + + +//////////////////////////////////////////// +// Power related +/////////////////////////////////////////// + +// All power generation handled in add_avail() +// Machines should use add_load(), surplus(), avail() +// Non-machines should use add_delayedload(), delayed_surplus(), newavail() + +/obj/structure/cable/proc/add_avail(amount) + if(powernet) + powernet.newavail += amount + +/obj/structure/cable/proc/add_load(amount) + if(powernet) + powernet.load += amount + +/obj/structure/cable/proc/surplus() + if(powernet) + return Clamp(powernet.avail-powernet.load, 0, powernet.avail) + else + return 0 + +/obj/structure/cable/proc/avail() + if(powernet) + return powernet.avail + else + return 0 + +/obj/structure/cable/proc/add_delayedload(amount) + if(powernet) + powernet.delayedload += amount + +/obj/structure/cable/proc/delayed_surplus() + if(powernet) + return Clamp(powernet.newavail - powernet.delayedload, 0, powernet.newavail) + else + return 0 + +/obj/structure/cable/proc/newavail() + if(powernet) + return powernet.newavail + else + return 0 + +//Telekinesis has no effect on a cable +/obj/structure/cable/attack_tk(mob/user) + return + +// Items usable on a cable : +// - Wirecutters : cut it duh ! +// - Cable coil : merge cables +// - Multitool : get the power currently passing through the cable +// +/obj/structure/cable/attackby(obj/item/W, mob/user) + var/turf/T = get_turf(src) + if(T.intact) + return + + else if(istype(W, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/coil = W + if(coil.get_amount() < 1) + to_chat(user, "Not enough cable!") + return + coil.cable_join(src, user) + + else if(istype(W, /obj/item/twohanded/rcl)) + var/obj/item/twohanded/rcl/R = W + if(R.loaded) + R.loaded.cable_join(src, user) + R.is_empty(user) + + else if(istype(W, /obj/item/toy/crayon)) + var/obj/item/toy/crayon/C = W + cable_color(C.colourName) + + else + if(W.flags & CONDUCT) + shock(user, 50, 0.7) + + add_fingerprint(user) + +/obj/structure/cable/multitool_act(mob/user, obj/item/I) + . = TRUE + var/turf/T = get_turf(src) + if(T.intact) + return + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(powernet && (powernet.avail > 0)) // is it powered? + to_chat(user, "Total power: [DisplayPower(powernet.avail)]\nLoad: [DisplayPower(powernet.load)]\nExcess power: [DisplayPower(surplus())]") + else + to_chat(user, "The cable is not powered.") + shock(user, 5, 0.2) + +/obj/structure/cable/wirecutter_act(mob/user, obj/item/I) + . = TRUE + var/turf/T = get_turf(src) + if(T.intact) + return + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(shock(user, 50)) + return + user.visible_message("[user] cuts the cable.", "You cut the cable.") + investigate_log("was cut by [key_name(usr, 1)] in [get_area(user)]([T.x], [T.y], [T.z] - [ADMIN_JMP(T)])","wires") + deconstruct() + +// shock the user with probability prb +/obj/structure/cable/proc/shock(mob/user, prb, siemens_coeff = 1) + if(!prob(prb)) + return FALSE + if(electrocute_mob(user, powernet, src, siemens_coeff)) + do_sparks(5, 1, src) + return TRUE + else + return FALSE + +/obj/structure/cable/singularity_pull(S, current_size) + ..() + if(current_size >= STAGE_FIVE) + deconstruct() + +obj/structure/cable/proc/cable_color(var/colorC) + if(colorC) + if(colorC == "rainbow") + color = color_rainbow() + else + color = colorC + else + color = "#DD0000" + +/obj/structure/cable/proc/color_rainbow() + color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) + return color + +///////////////////////////////////////////////// +// Cable laying helpers +//////////////////////////////////////////////// + +//handles merging diagonally matching cables +//for info : direction^3 is flipping horizontally, direction^12 is flipping vertically +/obj/structure/cable/proc/mergeDiagonalsNetworks(direction) + + //search for and merge diagonally matching cables from the first direction component (north/south) + var/turf/T = get_step(src, direction&3)//go north/south + + for(var/obj/structure/cable/C in T) + + if(!C) + continue + + if(src == C) + continue + + if(C.d1 == (direction^3) || C.d2 == (direction^3)) //we've got a diagonally matching cable + if(!C.powernet) //if the matching cable somehow got no powernet, make him one (should not happen for cables) + var/datum/powernet/newPN = new() + newPN.add_cable(C) + + if(powernet) //if we already have a powernet, then merge the two powernets + merge_powernets(powernet,C.powernet) + else + C.powernet.add_cable(src) //else, we simply connect to the matching cable powernet + + //the same from the second direction component (east/west) + T = get_step(src, direction&12)//go east/west + + for(var/obj/structure/cable/C in T) + + if(!C) + continue + + if(src == C) + continue + if(C.d1 == (direction^12) || C.d2 == (direction^12)) //we've got a diagonally matching cable + if(!C.powernet) //if the matching cable somehow got no powernet, make him one (should not happen for cables) + var/datum/powernet/newPN = new() + newPN.add_cable(C) + + if(powernet) //if we already have a powernet, then merge the two powernets + merge_powernets(powernet,C.powernet) + else + C.powernet.add_cable(src) //else, we simply connect to the matching cable powernet + +// merge with the powernets of power objects in the given direction +/obj/structure/cable/proc/mergeConnectedNetworks(direction) + + var/fdir = (!direction)? 0 : turn(direction, 180) //flip the direction, to match with the source position on its turf + + if(!(d1 == direction || d2 == direction)) //if the cable is not pointed in this direction, do nothing + return + + var/turf/TB = get_step(src, direction) + + for(var/obj/structure/cable/C in TB) + + if(!C) + continue + + if(src == C) + continue + + if(C.d1 == fdir || C.d2 == fdir) //we've got a matching cable in the neighbor turf + if(!C.powernet) //if the matching cable somehow got no powernet, make him one (should not happen for cables) + var/datum/powernet/newPN = new() + newPN.add_cable(C) + + if(powernet) //if we already have a powernet, then merge the two powernets + merge_powernets(powernet,C.powernet) + else + C.powernet.add_cable(src) //else, we simply connect to the matching cable powernet + +// merge with the powernets of power objects in the source turf +/obj/structure/cable/proc/mergeConnectedNetworksOnTurf() + var/list/to_connect = list() + + if(!powernet) //if we somehow have no powernet, make one (should not happen for cables) + var/datum/powernet/newPN = new() + newPN.add_cable(src) + + //first let's add turf cables to our powernet + //then we'll connect machines on turf with a node cable is present + for(var/AM in loc) + if(istype(AM, /obj/structure/cable)) + var/obj/structure/cable/C = AM + if(C.d1 == d1 || C.d2 == d1 || C.d1 == d2 || C.d2 == d2) //only connected if they have a common direction + if(C.powernet == powernet) + continue + if(C.powernet) + merge_powernets(powernet, C.powernet) + else + powernet.add_cable(C) //the cable was powernetless, let's just add it to our powernet + + else if(istype(AM, /obj/machinery/power/apc)) + var/obj/machinery/power/apc/N = AM + if(!N.terminal) + continue // APC are connected through their terminal + + if(N.terminal.powernet == powernet) + continue + + to_connect += N.terminal //we'll connect the machines after all cables are merged + + else if(istype(AM, /obj/machinery/power)) //other power machines + var/obj/machinery/power/M = AM + + if(M.powernet == powernet) + continue + + to_connect += M //we'll connect the machines after all cables are merged + + //now that cables are done, let's connect found machines + for(var/obj/machinery/power/PM in to_connect) + if(!PM.connect_to_network()) + PM.disconnect_from_network() //if we somehow can't connect the machine to the new powernet, remove it from the old nonetheless + +////////////////////////////////////////////// +// Powernets handling helpers +////////////////////////////////////////////// + +//if powernetless_only = 1, will only get connections without powernet +/obj/structure/cable/proc/get_connections(powernetless_only = 0) + . = list() // this will be a list of all connected power objects + var/turf/T + + //get matching cables from the first direction + if(d1) //if not a node cable + T = get_step(src, d1) + if(T) + . += power_list(T, src, turn(d1, 180), powernetless_only) //get adjacents matching cables + + if(d1&(d1-1)) //diagonal direction, must check the 4 possibles adjacents tiles + T = get_step(src,d1&3) // go north/south + if(T) + . += power_list(T, src, d1 ^ 3, powernetless_only) //get diagonally matching cables + T = get_step(src,d1&12) // go east/west + if(T) + . += power_list(T, src, d1 ^ 12, powernetless_only) //get diagonally matching cables + + . += power_list(loc, src, d1, powernetless_only) //get on turf matching cables + + //do the same on the second direction (which can't be 0) + T = get_step(src, d2) + if(T) + . += power_list(T, src, turn(d2, 180), powernetless_only) //get adjacents matching cables + + if(d2&(d2-1)) //diagonal direction, must check the 4 possibles adjacents tiles + T = get_step(src,d2&3) // go north/south + if(T) + . += power_list(T, src, d2 ^ 3, powernetless_only) //get diagonally matching cables + T = get_step(src,d2&12) // go east/west + if(T) + . += power_list(T, src, d2 ^ 12, powernetless_only) //get diagonally matching cables + . += power_list(loc, src, d2, powernetless_only) //get on turf matching cables + + return . + +//should be called after placing a cable which extends another cable, creating a "smooth" cable that no longer terminates in the centre of a turf. +//needed as this can, unlike other placements, disconnect cables +/obj/structure/cable/proc/denode() + var/turf/T1 = loc + if(!T1) + return + + var/list/powerlist = power_list(T1,src,0,0) //find the other cables that ended in the centre of the turf, with or without a powernet + if(powerlist.len>0) + var/datum/powernet/PN = new() + propagate_network(powerlist[1],PN) //propagates the new powernet beginning at the source cable + + if(PN.is_empty()) //can happen with machines made nodeless when smoothing cables + qdel(PN) + +/obj/structure/cable/proc/auto_propogate_cut_cable(obj/O) + if(O && !QDELETED(O)) + var/datum/powernet/newPN = new()// creates a new powernet... + propagate_network(O, newPN)//... and propagates it to the other side of the cable + +// cut the cable's powernet at this cable and updates the powergrid +/obj/structure/cable/proc/cut_cable_from_powernet(remove=TRUE) + var/turf/T1 = loc + var/list/P_list + if(!T1) + return + if(d1) + T1 = get_step(T1, d1) + P_list = power_list(T1, src, turn(d1,180),0,cable_only = 1) // what adjacently joins on to cut cable... + + P_list += power_list(loc, src, d1, 0, cable_only = 1)//... and on turf + + + if(P_list.len == 0)//if nothing in both list, then the cable was a lone cable, just delete it and its powernet + powernet.remove_cable(src) + + for(var/obj/machinery/power/P in T1)//check if it was powering a machine + if(!P.connect_to_network()) //can't find a node cable on a the turf to connect to + P.disconnect_from_network() //remove from current network (and delete powernet) + return + + var/obj/O = P_list[1] + // remove the cut cable from its turf and powernet, so that it doesn't get count in propagate_network worklist + if(remove) + loc = null + powernet.remove_cable(src) //remove the cut cable from its powernet + + // queue it to rebuild + SSmachines.deferred_powernet_rebuilds += O +// addtimer(CALLBACK(O, .proc/auto_propogate_cut_cable, O), 0) //so we don't rebuild the network X times when singulo/explosion destroys a line of X cables + + // Disconnect machines connected to nodes + if(d1 == 0) // if we cut a node (O-X) cable + for(var/obj/machinery/power/P in T1) + if(!P.connect_to_network()) //can't find a node cable on a the turf to connect to + P.disconnect_from_network() //remove from current network + + +/////////////////////////////////////////////// +// The cable coil object, used for laying cable +/////////////////////////////////////////////// + +//////////////////////////////// +// Definitions +//////////////////////////////// + +GLOBAL_LIST_INIT(cable_coil_recipes, list (new/datum/stack_recipe("cable restraints", /obj/item/restraints/handcuffs/cable, 15))) + +/obj/item/stack/cable_coil + name = "cable coil" + singular_name = "cable" + icon = 'icons/obj/power.dmi' + icon_state = "coil" + item_state = "coil_red" + amount = MAXCOIL + max_amount = MAXCOIL + merge_type = /obj/item/stack/cable_coil // This is here to let its children merge between themselves + color = COLOR_RED + desc = "A coil of power cable." + throwforce = 10 + w_class = WEIGHT_CLASS_SMALL + throw_speed = 2 + throw_range = 5 + materials = list(MAT_METAL=10, MAT_GLASS=5) + flags = CONDUCT + slot_flags = SLOT_BELT + item_state = "coil" + attack_verb = list("whipped", "lashed", "disciplined", "flogged") + usesound = 'sound/items/deconstruct.ogg' + toolspeed = 1 + +/obj/item/stack/cable_coil/suicide_act(mob/user) + if(locate(/obj/structure/chair/stool) in user.loc) + user.visible_message("[user] is making a noose with the [name]! It looks like [user.p_theyre()] trying to commit suicide.") + else + user.visible_message("[user] is strangling [user.p_them()]self with the [name]! It looks like [user.p_theyre()] trying to commit suicide.") + return OXYLOSS + +/obj/item/stack/cable_coil/New(loc, length = MAXCOIL, paramcolor = null) + ..() + amount = length + if(paramcolor) + color = paramcolor + pixel_x = rand(-2,2) + pixel_y = rand(-2,2) + update_icon() + recipes = GLOB.cable_coil_recipes + update_wclass() + +/////////////////////////////////// +// General procedures +/////////////////////////////////// +//you can use wires to heal robotics +/obj/item/stack/cable_coil/attack(mob/M, mob/user) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + var/obj/item/organ/external/S = H.bodyparts_by_name[user.zone_selected] + + if(!S) + return + if(!S.is_robotic() || user.a_intent != INTENT_HELP || S.open == 2) + return ..() + + if(S.burn_dam > ROBOLIMB_SELF_REPAIR_CAP) + to_chat(user, "The damage is far too severe to patch over externally.") + return + + if(!S.burn_dam) + to_chat(user, "Nothing to fix!") + return + + if(H == user) + if(!do_mob(user, H, 10)) + return 0 + var/cable_used = 0 + var/childlist + if(!isnull(S.children)) + childlist = S.children.Copy() + var/parenthealed = FALSE + while(cable_used <= MAXCABLEPERHEAL && amount >= 1) + var/obj/item/organ/external/E + if(S.burn_dam) + E = S + else if(LAZYLEN(childlist)) + E = pick_n_take(childlist) + if(!E.burn_dam || !E.is_robotic()) + continue + else if(S.parent && !parenthealed) + E = S.parent + parenthealed = TRUE + if(!E.burn_dam || !E.is_robotic()) + break + else + break + while(cable_used <= MAXCABLEPERHEAL && E.burn_dam && amount >= 1) + use(1) + cable_used += 1 + E.heal_damage(0, HEALPERCABLE, 0, 1) + user.visible_message("\The [user] repairs some burn damage on \the [M]'s [E.name] with \the [src].") + return 1 + + else + return ..() + +/obj/item/stack/cable_coil/split() + var/obj/item/stack/cable_coil/C = ..() + C.color = color + return C + +/obj/item/stack/cable_coil/update_icon() + if(!color) + color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_ORANGE, COLOR_WHITE, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) + if(amount == 1) + icon_state = "coil1" + name = "cable piece" + else if(amount == 2) + icon_state = "coil2" + name = "cable piece" + else + icon_state = "coil" + name = "cable coil" + ..() + +/obj/item/stack/cable_coil/proc/update_wclass() + if(amount == 1) + w_class = WEIGHT_CLASS_TINY + else + w_class = WEIGHT_CLASS_SMALL + +/obj/item/stack/cable_coil/examine(mob/user) + . = ..() + if(in_range(user, src)) + if(get_amount() == 1) + . += "A short piece of power cable." + else if(get_amount() == 2) + . += "A piece of power cable." + else + . += "A coil of power cable. There are [get_amount()] lengths of cable in the coil." + +// Items usable on a cable coil : +// - Wirecutters : cut them duh ! +// - Cable coil : merge cables +/obj/item/stack/cable_coil/attackby(obj/item/W, mob/user) + ..() + if(istype(W, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/C = W + // Cable merging is handled by parent proc + if(C.amount >= MAXCOIL) + to_chat(user, "The coil is as long as it will get.") + return + if( (C.amount + src.amount <= MAXCOIL) ) + to_chat(user, "You join the cable coils together.") + return + else + to_chat(user, "You transfer [get_amount_transferred()] length\s of cable from one coil to the other.") + return + + if(istype(W, /obj/item/toy/crayon)) + var/obj/item/toy/crayon/C = W + cable_color(C.colourName) + +/////////////////////////////////////////////// +// Cable laying procedures +////////////////////////////////////////////// + +/obj/item/stack/cable_coil/proc/get_new_cable(location) + var/obj/structure/cable/C = new(location) + C.cable_color(color) + + return C + +// called when cable_coil is clicked on a turf/simulated/floor +/obj/item/stack/cable_coil/proc/place_turf(turf/T, mob/user, dirnew) + if(!isturf(user.loc)) + return + + if(!isturf(T) || T.intact || !T.can_have_cabling()) + to_chat(user, "You can only lay cables on catwalks and plating!") + return + + if(get_amount() < 1) // Out of cable + to_chat(user, "There is no cable left!") + return + + if(get_dist(T,user) > 1) // Too far + to_chat(user, "You can't lay cable at a place that far away!") + return + + var/dirn + if(!dirnew) //If we weren't given a direction, come up with one! (Called as null from catwalk.dm and floor.dm) + if(user.loc == T) + dirn = user.dir //If laying on the tile we're on, lay in the direction we're facing + else + dirn = get_dir(T, user) + else + dirn = dirnew + + for(var/obj/structure/cable/LC in T) + if(LC.d2 == dirn && LC.d1 == 0) + to_chat(user, "There's already a cable at that position!") + return + + var/obj/structure/cable/C = get_new_cable(T) + + //set up the new cable + C.d1 = 0 //it's a O-X node cable + C.d2 = dirn + C.add_fingerprint(user) + C.updateicon() + + //create a new powernet with the cable, if needed it will be merged later + var/datum/powernet/PN = new() + PN.add_cable(C) + + C.mergeConnectedNetworks(C.d2) //merge the powernet with adjacents powernets + C.mergeConnectedNetworksOnTurf() //merge the powernet with on turf powernets + + if(C.d2 & (C.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions + C.mergeDiagonalsNetworks(C.d2) + + use(1) + + if(C.shock(user, 50)) + if(prob(50)) //fail + new /obj/item/stack/cable_coil(get_turf(C), 1, paramcolor = C.color) + C.deconstruct() + + return C + +// called when cable_coil is click on an installed obj/cable +// or click on a turf that already contains a "node" cable +/obj/item/stack/cable_coil/proc/cable_join(obj/structure/cable/C, mob/user) + var/turf/U = user.loc + if(!isturf(U)) + return + + var/turf/T = get_turf(C) + + if(!isturf(T) || T.intact) // sanity checks, also stop use interacting with T-scanner revealed cable + return + + if(get_dist(C, user) > 1) // make sure it's close enough + to_chat(user, "You can't lay cable at a place that far away!") + return + + + if(U == T) //if clicked on the turf we're standing on, try to put a cable in the direction we're facing + place_turf(T,user) + return + + var/dirn = get_dir(C, user) + + // one end of the clicked cable is pointing towards us + if(C.d1 == dirn || C.d2 == dirn) + if(U.intact) // can't place a cable if the floor is complete + to_chat(user, "You can't lay cable there unless the floor tiles are removed!") + return + else + // cable is pointing at us, we're standing on an open tile + // so create a stub pointing at the clicked cable on our tile + + var/fdirn = turn(dirn, 180) // the opposite direction + + for(var/obj/structure/cable/LC in U) // check to make sure there's not a cable there already + if(LC.d1 == fdirn || LC.d2 == fdirn) + to_chat(user, "There's already a cable at that position!") + return + + var/obj/structure/cable/NC = get_new_cable (U) + + NC.d1 = 0 + NC.d2 = fdirn + NC.add_fingerprint(user) + NC.update_icon() + + //create a new powernet with the cable, if needed it will be merged later + var/datum/powernet/newPN = new() + newPN.add_cable(NC) + + NC.mergeConnectedNetworks(NC.d2) //merge the powernet with adjacents powernets + NC.mergeConnectedNetworksOnTurf() //merge the powernet with on turf powernets + + if(NC.d2 & (NC.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions + NC.mergeDiagonalsNetworks(NC.d2) + + use(1) + + if(NC.shock(user, 50)) + if(prob(50)) //fail + NC.deconstruct() + return + + // exisiting cable doesn't point at our position, so see if it's a stub + else if(C.d1 == 0) + // if so, make it a full cable pointing from it's old direction to our dirn + var/nd1 = C.d2 // these will be the new directions + var/nd2 = dirn + + + if(nd1 > nd2) // swap directions to match icons/states + nd1 = dirn + nd2 = C.d2 + + + for(var/obj/structure/cable/LC in T) // check to make sure there's no matching cable + if(LC == C) // skip the cable we're interacting with + continue + if((LC.d1 == nd1 && LC.d2 == nd2) || (LC.d1 == nd2 && LC.d2 == nd1) ) // make sure no cable matches either direction + to_chat(user, "There's already a cable at that position!") + return + + + C.cable_color(color) + + C.d1 = nd1 + C.d2 = nd2 + + C.add_fingerprint() + C.updateicon() + + + C.mergeConnectedNetworks(C.d1) //merge the powernets... + C.mergeConnectedNetworks(C.d2) //...in the two new cable directions + C.mergeConnectedNetworksOnTurf() + + if(C.d1 & (C.d1 - 1))// if the cable is layed diagonally, check the others 2 possible directions + C.mergeDiagonalsNetworks(C.d1) + + if(C.d2 & (C.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions + C.mergeDiagonalsNetworks(C.d2) + + use(1) + + if(C.shock(user, 50)) + if(prob(50)) //fail + C.deconstruct() + return + + C.denode()// this call may have disconnected some cables that terminated on the centre of the turf, if so split the powernets. + return + +////////////////////////////// +// Misc. +///////////////////////////// + +/obj/item/stack/cable_coil/cut + item_state = "coil2" + +/obj/item/stack/cable_coil/cut/Initialize(mapload) + . = ..() + src.amount = rand(1,2) + pixel_x = rand(-2,2) + pixel_y = rand(-2,2) + update_icon() + update_wclass() + +/obj/item/stack/cable_coil/yellow + color = COLOR_YELLOW + +/obj/item/stack/cable_coil/blue + color = COLOR_BLUE + +/obj/item/stack/cable_coil/green + color = COLOR_GREEN + +/obj/item/stack/cable_coil/pink + color = COLOR_PINK + +/obj/item/stack/cable_coil/orange + color = COLOR_ORANGE + +/obj/item/stack/cable_coil/cyan + color = COLOR_CYAN + +/obj/item/stack/cable_coil/white + color = COLOR_WHITE + +/obj/item/stack/cable_coil/random/New() + color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_WHITE, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) + ..() + +/obj/item/stack/cable_coil/proc/cable_color(var/colorC) + if(colorC) + if(colorC == "rainbow") + colorC = color_rainbow() + color = colorC + else + color = COLOR_RED + +/obj/item/stack/cable_coil/proc/color_rainbow() + color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN) + return color + +/obj/item/stack/cable_coil/cyborg + name = "cyborg cable coil" + +/obj/item/stack/cable_coil/cyborg/attack_self(mob/user) + var/cablecolor = input(user,"Pick a cable color.","Cable Color") in list("red","yellow","green","blue","pink","orange","cyan","white") + color = cablecolor + update_icon() + +#undef MAXCABLEPERHEAL +#undef HEALPERCABLE diff --git a/code/modules/power/cable_heavyduty.dm b/code/modules/power/cable_heavyduty.dm index 03b6aafed28..2519dbcd198 100644 --- a/code/modules/power/cable_heavyduty.dm +++ b/code/modules/power/cable_heavyduty.dm @@ -25,4 +25,4 @@ ..() /obj/structure/cable/heavyduty/cable_color(var/colorC) - return \ No newline at end of file + return diff --git a/code/modules/power/cable_logic.dm b/code/modules/power/cable_logic.dm index b262b6fc3b8..09d10320fd5 100644 --- a/code/modules/power/cable_logic.dm +++ b/code/modules/power/cable_logic.dm @@ -1,292 +1,292 @@ -#define LOGIC_HIGH 5 - -//Indicators only have one input and no outputs -/obj/machinery/logic/indicator - //Input is searched from the 'dir' direction - var/obj/structure/cable/input - -/obj/machinery/logic/indicator/process() - if(input) - return 1 - - - if(!input) - var/turf/T = get_step(src, dir) - if(T) - var/inv_dir = turn(dir, 180) - for(var/obj/structure/cable/C in T) - if(C.d1 == inv_dir || C.d2 == inv_dir) - input = C - return 1 - - return 0 //If it gets to here, it means no suitable wire to link to was found. - -/obj/machinery/logic/indicator/bulb - icon = 'icons/obj/lighting.dmi' - icon_state = "bulb0" - -/obj/machinery/logic/indicator/bulb/process() - if(!..()) //Parent proc checks if input1 exists. - return - - var/datum/powernet/pn_input = input.powernet - if(!pn_input) - return - - if(pn_input.avail >= LOGIC_HIGH) - icon_state = "bulb1" - else - icon_state = "bulb0" - - - - -//Sensors only have one output and no inputs -/obj/machinery/logic/sensor - //Output is searched from the 'dir' direction - var/obj/structure/cable/output - -/obj/machinery/logic/sensor/process() - if(output) - return 1 - - if(!output) - var/turf/T = get_step(src, dir) - if(T) - var/inv_dir = turn(dir, 180) - for(var/obj/structure/cable/C in T) - if(C.d1 == inv_dir || C.d2 == inv_dir) - output = C - return 1 - - return 0 //If it gets to here, it means no suitable wire to link to was found. - -//Constant high generator. This will continue to send a signal of LOGIC_HIGH as long as it exists. -/obj/machinery/logic/sensor/constant_high - icon = 'icons/obj/atmospherics/outlet_injector.dmi' - icon_state = "off" - -/obj/machinery/logic/sensor/constant_high/process() - if(!..()) //Parent proc checks if input1 exists. - return - - var/datum/powernet/pn_output = output.powernet - if(!pn_output) - return - - pn_output.newavail = max(pn_output.avail, LOGIC_HIGH) - - - - -//ONE INPUT logic elements have one input and one output -/obj/machinery/logic/oneinput - var/dir_input = 2 - var/dir_output = 1 - var/obj/structure/cable/input - var/obj/structure/cable/output - icon = 'icons/atmos/heat.dmi' - icon_state = "intact" - -/obj/machinery/logic/oneinput/process() - if(input && output) - return 1 - - if(!dir_input || !dir_output) - return 0 - - if(!input) - var/turf/T = get_step(src, dir_input) - if(T) - var/inv_dir = turn(dir_input, 180) - for(var/obj/structure/cable/C in T) - if(C.d1 == inv_dir || C.d2 == inv_dir) - input = C - - if(!output) - var/turf/T = get_step(src, dir_output) - if(T) - var/inv_dir = turn(dir_output, 180) - for(var/obj/structure/cable/C in T) - if(C.d1 == inv_dir || C.d2 == inv_dir) - output = C - - return 0 //On the process() call, where everything is still being searched for, it returns 0. It will return 1 on the next process() call. - -//NOT GATE -/obj/machinery/logic/oneinput/not/process() - if(!..()) //Parent proc checks if input1, input2 and output exist. - return - - var/datum/powernet/pn_input = input.powernet - - if(!pn_input) - return - - var/datum/powernet/pn_output = output.powernet - if(!pn_output) - return - - if( !(pn_input.avail >= LOGIC_HIGH)) - pn_output.newavail = max(pn_output.avail, LOGIC_HIGH) //Set the output avilable power to 5 or whatever it was before. - else - pn_output.load += LOGIC_HIGH //Otherwise increase the load to 5 - - - - - - - - - -//TWO INPUT logic elements have two inputs and one output -/obj/machinery/logic/twoinput - var/dir_input1 = 2 - var/dir_input2 = 8 - var/dir_output = 1 - var/obj/structure/cable/input1 - var/obj/structure/cable/input2 - var/obj/structure/cable/output - icon = 'icons/obj/atmospherics/mixer.dmi' - icon_state = "intact_off" - -/obj/machinery/logic/twoinput/process() - if(input1 && input2 && output) - return 1 - - if(!dir_input1 || !dir_input2 || !dir_output) - return 0 - - if(!input1) - var/turf/T = get_step(src, dir_input1) - if(T) - var/inv_dir = turn(dir_input1, 180) - for(var/obj/structure/cable/C in T) - if(C.d1 == inv_dir || C.d2 == inv_dir) - input1 = C - - if(!input2) - var/turf/T = get_step(src, dir_input2) - if(T) - var/inv_dir = turn(dir_input2, 180) - for(var/obj/structure/cable/C in T) - if(C.d1 == inv_dir || C.d2 == inv_dir) - input2 = C - - if(!output) - var/turf/T = get_step(src, dir_output) - if(T) - var/inv_dir = turn(dir_output, 180) - for(var/obj/structure/cable/C in T) - if(C.d1 == inv_dir || C.d2 == inv_dir) - output = C - - return 0 //On the process() call, where everything is still being searched for, it returns 0. It will return 1 on the next process() call. - -//AND GATE -/obj/machinery/logic/twoinput/and/process() - if(!..()) //Parent proc checks if input1, input2 and output exist. - return - - var/datum/powernet/pn_input1 = input1.powernet - var/datum/powernet/pn_input2 = input2.powernet - - if(!pn_input1 || !pn_input2) - return - - var/datum/powernet/pn_output = output.powernet - if(!pn_output) - return - - if( (pn_input1.avail >= LOGIC_HIGH) && (pn_input2.avail >= LOGIC_HIGH) ) - pn_output.newavail = max(pn_output.avail, LOGIC_HIGH) //Set the output avilable power to 5 or whatever it was before. - else - pn_output.load += LOGIC_HIGH //Otherwise increase the load to 5 - -//OR GATE -/obj/machinery/logic/twoinput/or/process() - if(!..()) //Parent proc checks if input1, input2 and output exist. - return - - var/datum/powernet/pn_input1 = input1.powernet - var/datum/powernet/pn_input2 = input2.powernet - - if(!pn_input1 || !pn_input2) - return - - var/datum/powernet/pn_output = output.powernet - if(!pn_output) - return - - if( (pn_input1.avail >= LOGIC_HIGH) || (pn_input2.avail >= LOGIC_HIGH) ) - pn_output.newavail = max(pn_output.avail, LOGIC_HIGH) //Set the output avilable power to 5 or whatever it was before. - else - pn_output.load += LOGIC_HIGH //Otherwise increase the load to 5 - -//XOR GATE -/obj/machinery/logic/twoinput/xor/process() - if(!..()) //Parent proc checks if input1, input2 and output exist. - return - - var/datum/powernet/pn_input1 = input1.powernet - var/datum/powernet/pn_input2 = input2.powernet - - if(!pn_input1 || !pn_input2) - return - - var/datum/powernet/pn_output = output.powernet - if(!pn_output) - return - - if( (pn_input1.avail >= LOGIC_HIGH) != (pn_input2.avail >= LOGIC_HIGH) ) - pn_output.newavail = max(pn_output.avail, LOGIC_HIGH) //Set the output avilable power to 5 or whatever it was before. - else - pn_output.load += LOGIC_HIGH //Otherwise increase the load to 5 - -//XNOR GATE (EQUIVALENCE) -/obj/machinery/logic/twoinput/xnor/process() - if(!..()) //Parent proc checks if input1, input2 and output exist. - return - - var/datum/powernet/pn_input1 = input1.powernet - var/datum/powernet/pn_input2 = input2.powernet - - if(!pn_input1 || !pn_input2) - return - - var/datum/powernet/pn_output = output.powernet - if(!pn_output) - return - - if( (pn_input1.avail >= LOGIC_HIGH) == (pn_input2.avail >= LOGIC_HIGH) ) - pn_output.newavail = max(pn_output.avail, LOGIC_HIGH) //Set the output avilable power to 5 or whatever it was before. - else - pn_output.load += LOGIC_HIGH //Otherwise increase the load to 5 - -#define RELAY_POWER_TRANSFER 2000 //How much power a relay transfers through. - -//RELAY - input1 governs the flow from input2 to output -/obj/machinery/logic/twoinput/relay/process() - if(!..()) //Parent proc checks if input1, input2 and output exist. - return - - var/datum/powernet/pn_input1 = input1.powernet - - if(!pn_input1) - return - - if( pn_input1.avail >= LOGIC_HIGH ) - var/datum/powernet/pn_input2 = input2.powernet - var/datum/powernet/pn_output = output.powernet - - if(!pn_output) - return - - if(pn_input2.avail >= RELAY_POWER_TRANSFER) - pn_input2.load += RELAY_POWER_TRANSFER - pn_output.newavail += RELAY_POWER_TRANSFER - - -#undef RELAY_POWER_TRANSFER -#undef LOGIC_HIGH \ No newline at end of file +#define LOGIC_HIGH 5 + +//Indicators only have one input and no outputs +/obj/machinery/logic/indicator + //Input is searched from the 'dir' direction + var/obj/structure/cable/input + +/obj/machinery/logic/indicator/process() + if(input) + return 1 + + + if(!input) + var/turf/T = get_step(src, dir) + if(T) + var/inv_dir = turn(dir, 180) + for(var/obj/structure/cable/C in T) + if(C.d1 == inv_dir || C.d2 == inv_dir) + input = C + return 1 + + return 0 //If it gets to here, it means no suitable wire to link to was found. + +/obj/machinery/logic/indicator/bulb + icon = 'icons/obj/lighting.dmi' + icon_state = "bulb0" + +/obj/machinery/logic/indicator/bulb/process() + if(!..()) //Parent proc checks if input1 exists. + return + + var/datum/powernet/pn_input = input.powernet + if(!pn_input) + return + + if(pn_input.avail >= LOGIC_HIGH) + icon_state = "bulb1" + else + icon_state = "bulb0" + + + + +//Sensors only have one output and no inputs +/obj/machinery/logic/sensor + //Output is searched from the 'dir' direction + var/obj/structure/cable/output + +/obj/machinery/logic/sensor/process() + if(output) + return 1 + + if(!output) + var/turf/T = get_step(src, dir) + if(T) + var/inv_dir = turn(dir, 180) + for(var/obj/structure/cable/C in T) + if(C.d1 == inv_dir || C.d2 == inv_dir) + output = C + return 1 + + return 0 //If it gets to here, it means no suitable wire to link to was found. + +//Constant high generator. This will continue to send a signal of LOGIC_HIGH as long as it exists. +/obj/machinery/logic/sensor/constant_high + icon = 'icons/obj/atmospherics/outlet_injector.dmi' + icon_state = "off" + +/obj/machinery/logic/sensor/constant_high/process() + if(!..()) //Parent proc checks if input1 exists. + return + + var/datum/powernet/pn_output = output.powernet + if(!pn_output) + return + + pn_output.newavail = max(pn_output.avail, LOGIC_HIGH) + + + + +//ONE INPUT logic elements have one input and one output +/obj/machinery/logic/oneinput + var/dir_input = 2 + var/dir_output = 1 + var/obj/structure/cable/input + var/obj/structure/cable/output + icon = 'icons/atmos/heat.dmi' + icon_state = "intact" + +/obj/machinery/logic/oneinput/process() + if(input && output) + return 1 + + if(!dir_input || !dir_output) + return 0 + + if(!input) + var/turf/T = get_step(src, dir_input) + if(T) + var/inv_dir = turn(dir_input, 180) + for(var/obj/structure/cable/C in T) + if(C.d1 == inv_dir || C.d2 == inv_dir) + input = C + + if(!output) + var/turf/T = get_step(src, dir_output) + if(T) + var/inv_dir = turn(dir_output, 180) + for(var/obj/structure/cable/C in T) + if(C.d1 == inv_dir || C.d2 == inv_dir) + output = C + + return 0 //On the process() call, where everything is still being searched for, it returns 0. It will return 1 on the next process() call. + +//NOT GATE +/obj/machinery/logic/oneinput/not/process() + if(!..()) //Parent proc checks if input1, input2 and output exist. + return + + var/datum/powernet/pn_input = input.powernet + + if(!pn_input) + return + + var/datum/powernet/pn_output = output.powernet + if(!pn_output) + return + + if( !(pn_input.avail >= LOGIC_HIGH)) + pn_output.newavail = max(pn_output.avail, LOGIC_HIGH) //Set the output avilable power to 5 or whatever it was before. + else + pn_output.load += LOGIC_HIGH //Otherwise increase the load to 5 + + + + + + + + + +//TWO INPUT logic elements have two inputs and one output +/obj/machinery/logic/twoinput + var/dir_input1 = 2 + var/dir_input2 = 8 + var/dir_output = 1 + var/obj/structure/cable/input1 + var/obj/structure/cable/input2 + var/obj/structure/cable/output + icon = 'icons/obj/atmospherics/mixer.dmi' + icon_state = "intact_off" + +/obj/machinery/logic/twoinput/process() + if(input1 && input2 && output) + return 1 + + if(!dir_input1 || !dir_input2 || !dir_output) + return 0 + + if(!input1) + var/turf/T = get_step(src, dir_input1) + if(T) + var/inv_dir = turn(dir_input1, 180) + for(var/obj/structure/cable/C in T) + if(C.d1 == inv_dir || C.d2 == inv_dir) + input1 = C + + if(!input2) + var/turf/T = get_step(src, dir_input2) + if(T) + var/inv_dir = turn(dir_input2, 180) + for(var/obj/structure/cable/C in T) + if(C.d1 == inv_dir || C.d2 == inv_dir) + input2 = C + + if(!output) + var/turf/T = get_step(src, dir_output) + if(T) + var/inv_dir = turn(dir_output, 180) + for(var/obj/structure/cable/C in T) + if(C.d1 == inv_dir || C.d2 == inv_dir) + output = C + + return 0 //On the process() call, where everything is still being searched for, it returns 0. It will return 1 on the next process() call. + +//AND GATE +/obj/machinery/logic/twoinput/and/process() + if(!..()) //Parent proc checks if input1, input2 and output exist. + return + + var/datum/powernet/pn_input1 = input1.powernet + var/datum/powernet/pn_input2 = input2.powernet + + if(!pn_input1 || !pn_input2) + return + + var/datum/powernet/pn_output = output.powernet + if(!pn_output) + return + + if( (pn_input1.avail >= LOGIC_HIGH) && (pn_input2.avail >= LOGIC_HIGH) ) + pn_output.newavail = max(pn_output.avail, LOGIC_HIGH) //Set the output avilable power to 5 or whatever it was before. + else + pn_output.load += LOGIC_HIGH //Otherwise increase the load to 5 + +//OR GATE +/obj/machinery/logic/twoinput/or/process() + if(!..()) //Parent proc checks if input1, input2 and output exist. + return + + var/datum/powernet/pn_input1 = input1.powernet + var/datum/powernet/pn_input2 = input2.powernet + + if(!pn_input1 || !pn_input2) + return + + var/datum/powernet/pn_output = output.powernet + if(!pn_output) + return + + if( (pn_input1.avail >= LOGIC_HIGH) || (pn_input2.avail >= LOGIC_HIGH) ) + pn_output.newavail = max(pn_output.avail, LOGIC_HIGH) //Set the output avilable power to 5 or whatever it was before. + else + pn_output.load += LOGIC_HIGH //Otherwise increase the load to 5 + +//XOR GATE +/obj/machinery/logic/twoinput/xor/process() + if(!..()) //Parent proc checks if input1, input2 and output exist. + return + + var/datum/powernet/pn_input1 = input1.powernet + var/datum/powernet/pn_input2 = input2.powernet + + if(!pn_input1 || !pn_input2) + return + + var/datum/powernet/pn_output = output.powernet + if(!pn_output) + return + + if( (pn_input1.avail >= LOGIC_HIGH) != (pn_input2.avail >= LOGIC_HIGH) ) + pn_output.newavail = max(pn_output.avail, LOGIC_HIGH) //Set the output avilable power to 5 or whatever it was before. + else + pn_output.load += LOGIC_HIGH //Otherwise increase the load to 5 + +//XNOR GATE (EQUIVALENCE) +/obj/machinery/logic/twoinput/xnor/process() + if(!..()) //Parent proc checks if input1, input2 and output exist. + return + + var/datum/powernet/pn_input1 = input1.powernet + var/datum/powernet/pn_input2 = input2.powernet + + if(!pn_input1 || !pn_input2) + return + + var/datum/powernet/pn_output = output.powernet + if(!pn_output) + return + + if( (pn_input1.avail >= LOGIC_HIGH) == (pn_input2.avail >= LOGIC_HIGH) ) + pn_output.newavail = max(pn_output.avail, LOGIC_HIGH) //Set the output avilable power to 5 or whatever it was before. + else + pn_output.load += LOGIC_HIGH //Otherwise increase the load to 5 + +#define RELAY_POWER_TRANSFER 2000 //How much power a relay transfers through. + +//RELAY - input1 governs the flow from input2 to output +/obj/machinery/logic/twoinput/relay/process() + if(!..()) //Parent proc checks if input1, input2 and output exist. + return + + var/datum/powernet/pn_input1 = input1.powernet + + if(!pn_input1) + return + + if( pn_input1.avail >= LOGIC_HIGH ) + var/datum/powernet/pn_input2 = input2.powernet + var/datum/powernet/pn_output = output.powernet + + if(!pn_output) + return + + if(pn_input2.avail >= RELAY_POWER_TRANSFER) + pn_input2.load += RELAY_POWER_TRANSFER + pn_output.newavail += RELAY_POWER_TRANSFER + + +#undef RELAY_POWER_TRANSFER +#undef LOGIC_HIGH diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm index 9a7818ed321..24f55d34454 100644 --- a/code/modules/power/cell.dm +++ b/code/modules/power/cell.dm @@ -1,356 +1,356 @@ -/obj/item/stock_parts/cell - name = "power cell" - desc = "A rechargeable electrochemical power cell." - icon = 'icons/obj/power.dmi' - icon_state = "cell" - item_state = "cell" - origin_tech = "powerstorage=1" - force = 5 - throwforce = 5 - throw_speed = 2 - throw_range = 5 - w_class = WEIGHT_CLASS_SMALL - var/charge = 0 // note %age conveted to actual charge in New - var/maxcharge = 1000 - materials = list(MAT_METAL = 700, MAT_GLASS = 50) - var/rigged = FALSE // true if rigged to explode - var/chargerate = 100 //how much power is given every tick in a recharger - var/self_recharge = 0 //does it self recharge, over time, or not? - var/ratingdesc = TRUE - var/grown_battery = FALSE // If it's a grown that acts as a battery, add a wire overlay to it. - -/obj/item/stock_parts/cell/get_cell() - return src - -/obj/item/stock_parts/cell/New() - ..() - START_PROCESSING(SSobj, src) - charge = maxcharge - if(ratingdesc) - desc += " This one has a power rating of [DisplayPower(maxcharge)], and you should not swallow it." - update_icon() - -/obj/item/stock_parts/cell/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/stock_parts/cell/vv_edit_var(var_name, var_value) - switch(var_name) - if("self_recharge") - if(var_value) - START_PROCESSING(SSobj, src) - else - STOP_PROCESSING(SSobj, src) - . = ..() - -/obj/item/stock_parts/cell/process() - if(self_recharge) - give(chargerate * 0.25) - else - return PROCESS_KILL - -/obj/item/stock_parts/cell/update_icon() - overlays.Cut() - if(grown_battery) - overlays += image('icons/obj/power.dmi', "grown_wires") - if(charge < 0.01) - return - else if(charge/maxcharge >=0.995) - overlays += "cell-o2" - else - overlays += "cell-o1" - -/obj/item/stock_parts/cell/proc/percent() // return % charge of cell - return 100 * charge / maxcharge - -// use power from a cell -/obj/item/stock_parts/cell/proc/use(amount) - if(rigged && amount > 0) - explode() - return 0 - if(charge < amount) - return 0 - charge = (charge - amount) - return 1 - -// recharge the cell -/obj/item/stock_parts/cell/proc/give(amount) - if(rigged && amount > 0) - explode() - return 0 - if(maxcharge < amount) - amount = maxcharge - var/power_used = min(maxcharge - charge, amount) - charge += power_used - return power_used - -/obj/item/stock_parts/cell/examine(mob/user) - . = ..() - if(rigged) - . += "This power cell seems to be faulty!" - else - . += "The charge meter reads [round(percent() )]%." - -/obj/item/stock_parts/cell/suicide_act(mob/user) - to_chat(viewers(user), "[user] is licking the electrodes of the [src]! It looks like [user.p_theyre()] trying to commit suicide.") - return FIRELOSS - -/obj/item/stock_parts/cell/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/reagent_containers/syringe)) - var/obj/item/reagent_containers/syringe/S = W - - to_chat(user, "You inject the solution into the power cell.") - - if(S.reagents.has_reagent("plasma", 5) || S.reagents.has_reagent("plasma_dust", 5)) - - rigged = TRUE - - log_admin("LOG: [key_name(user)] injected a power cell with plasma, rigging it to explode.") - message_admins("LOG: [key_name_admin(user)] injected a power cell with plasma, rigging it to explode.") - - S.reagents.clear_reagents() - else - return ..() - - -/obj/item/stock_parts/cell/proc/explode() - var/turf/T = get_turf(loc) - if(charge == 0) - return - var/devastation_range = -1 //round(charge/11000) - var/heavy_impact_range = round(sqrt(charge) / 60) - var/light_impact_range = round(sqrt(charge) / 30) - var/flash_range = light_impact_range - if(light_impact_range == 0) - rigged = FALSE - corrupt() - return - //explosion(T, 0, 1, 2, 2) - log_admin("LOG: Rigged power cell explosion, last touched by [fingerprintslast]") - message_admins("LOG: Rigged power cell explosion, last touched by [fingerprintslast]") - - explosion(T, devastation_range, heavy_impact_range, light_impact_range, flash_range) - qdel(src) - -/obj/item/stock_parts/cell/proc/corrupt() - charge /= 2 - maxcharge = max(maxcharge / 2, chargerate) - if(prob(10)) - rigged = TRUE //broken batterys are dangerous - -/obj/item/stock_parts/cell/emp_act(severity) - charge -= 1000 / severity - if(charge < 0) - charge = 0 - ..() - -/obj/item/stock_parts/cell/ex_act(severity) - ..() - if(!QDELETED(src)) - switch(severity) - if(2) - if(prob(50)) - corrupt() - if(3) - if(prob(25)) - corrupt() - -/obj/item/stock_parts/cell/blob_act(obj/structure/blob/B) - ex_act(EXPLODE_DEVASTATE) - -/obj/item/stock_parts/cell/proc/get_electrocute_damage() - if(charge >= 1000) - return Clamp(20 + round(charge / 25000), 20, 195) + rand(-5, 5) - else - return 0 - -// Cell variants -/obj/item/stock_parts/cell/empty/New() - ..() - charge = 0 - -/obj/item/stock_parts/cell/crap - name = "\improper Nanotrasen brand rechargeable AA battery" - desc = "You can't top the plasma top." //TOTALLY TRADEMARK INFRINGEMENT - maxcharge = 500 - materials = list(MAT_GLASS = 40) - rating = 2 - -/obj/item/stock_parts/cell/crap/empty/New() - ..() - charge = 0 - update_icon() - -/obj/item/stock_parts/cell/upgraded - name = "upgraded power cell" - desc = "A power cell with a slightly higher capacity than normal!" - maxcharge = 2500 - materials = list(MAT_GLASS = 50) - rating = 2 - chargerate = 1000 - -/obj/item/stock_parts/cell/upgraded/plus - name = "upgraded power cell+" - desc = "A power cell with an even higher capacity than the base model!" - maxcharge = 5000 - -/obj/item/stock_parts/cell/secborg - name = "security borg rechargeable D battery" - origin_tech = null - maxcharge = 600 //600 max charge / 100 charge per shot = six shots - materials = list(MAT_GLASS = 40) - rating = 2.5 - -/obj/item/stock_parts/cell/secborg/empty/New() - ..() - charge = 0 - update_icon() - -/obj/item/stock_parts/cell/pulse //200 pulse shots - name = "pulse rifle power cell" - maxcharge = 40000 - rating = 3 - chargerate = 1500 - -/obj/item/stock_parts/cell/pulse/carbine //25 pulse shots - name = "pulse carbine power cell" - maxcharge = 5000 - -/obj/item/stock_parts/cell/pulse/pistol //10 pulse shots - name = "pulse pistol power cell" - maxcharge = 2000 - -/obj/item/stock_parts/cell/high - name = "high-capacity power cell" - origin_tech = "powerstorage=2" - icon_state = "hcell" - maxcharge = 10000 - materials = list(MAT_GLASS = 60) - rating = 3 - chargerate = 1500 - -/obj/item/stock_parts/cell/high/plus - name = "high-capacity power cell+" - desc = "Where did these come from?" - icon_state = "h+cell" - maxcharge = 15000 - chargerate = 2250 - -/obj/item/stock_parts/cell/high/empty/New() - ..() - charge = 0 - update_icon() - -/obj/item/stock_parts/cell/super - name = "super-capacity power cell" - origin_tech = "powerstorage=3;materials=3" - icon_state = "scell" - maxcharge = 20000 - materials = list(MAT_GLASS = 300) - rating = 4 - chargerate = 2000 - -/obj/item/stock_parts/cell/super/empty/New() - ..() - charge = 0 - update_icon() - -/obj/item/stock_parts/cell/hyper - name = "hyper-capacity power cell" - origin_tech = "powerstorage=4;engineering=4;materials=4" - icon_state = "hpcell" - maxcharge = 30000 - materials = list(MAT_GLASS = 400) - rating = 5 - chargerate = 3000 - -/obj/item/stock_parts/cell/hyper/empty/New() - ..() - charge = 0 - update_icon() - -/obj/item/stock_parts/cell/bluespace - name = "bluespace power cell" - desc = "A rechargeable transdimensional power cell." - origin_tech = "powerstorage=5;bluespace=4;materials=4;engineering=4" - icon_state = "bscell" - maxcharge = 40000 - materials = list(MAT_GLASS = 600) - rating = 6 - chargerate = 4000 - -/obj/item/stock_parts/cell/bluespace/empty/New() - ..() - charge = 0 - update_icon() - -/obj/item/stock_parts/cell/infinite - name = "infinite-capacity power cell!" - icon_state = "icell" - origin_tech = "powerstorage=7" - maxcharge = 30000 - materials = list(MAT_GLASS=1000) - rating = 6 - chargerate = 30000 - -/obj/item/stock_parts/cell/infinite/use() - return 1 - -/obj/item/stock_parts/cell/infinite/abductor - name = "void core" - desc = "An alien power cell that produces energy seemingly out of nowhere." - icon = 'icons/obj/abductor.dmi' - icon_state = "cell" - maxcharge = 50000 - rating = 12 - ratingdesc = FALSE - -/obj/item/stock_parts/cell/infinite/abductor/update_icon() - return - - -/obj/item/stock_parts/cell/potato - name = "potato battery" - desc = "A rechargeable starch based power cell." - icon = 'icons/obj/hydroponics/harvest.dmi' - icon_state = "potato" - origin_tech = "powerstorage=1;biotech=1" - charge = 100 - maxcharge = 300 - materials = list() - rating = 1 - grown_battery = TRUE //it has the overlays for wires - -/obj/item/stock_parts/cell/high/slime - name = "charged slime core" - desc = "A yellow slime core infused with plasma, it crackles with power." - origin_tech = "powerstorage=5;biotech=4" - icon = 'icons/mob/slimes.dmi' - icon_state = "yellow slime extract" - materials = list() - rating = 5 //self-recharge makes these desirable - self_recharge = 1 // Infused slime cores self-recharge, over time - chargerate = 500 - -/obj/item/stock_parts/cell/emproof - name = "\improper EMP-proof cell" - desc = "An EMP-proof cell." - maxcharge = 500 - rating = 3 - -/obj/item/stock_parts/cell/emproof/empty/New() - ..() - charge = 0 - update_icon() - -/obj/item/stock_parts/cell/emproof/emp_act(severity) - return - -/obj/item/stock_parts/cell/emproof/corrupt() - return - -/obj/item/stock_parts/cell/ninja - name = "spider-clan power cell" - desc = "A standard ninja-suit power cell." - maxcharge = 10000 - materials = list(MAT_GLASS = 60) \ No newline at end of file +/obj/item/stock_parts/cell + name = "power cell" + desc = "A rechargeable electrochemical power cell." + icon = 'icons/obj/power.dmi' + icon_state = "cell" + item_state = "cell" + origin_tech = "powerstorage=1" + force = 5 + throwforce = 5 + throw_speed = 2 + throw_range = 5 + w_class = WEIGHT_CLASS_SMALL + var/charge = 0 // note %age conveted to actual charge in New + var/maxcharge = 1000 + materials = list(MAT_METAL = 700, MAT_GLASS = 50) + var/rigged = FALSE // true if rigged to explode + var/chargerate = 100 //how much power is given every tick in a recharger + var/self_recharge = 0 //does it self recharge, over time, or not? + var/ratingdesc = TRUE + var/grown_battery = FALSE // If it's a grown that acts as a battery, add a wire overlay to it. + +/obj/item/stock_parts/cell/get_cell() + return src + +/obj/item/stock_parts/cell/New() + ..() + START_PROCESSING(SSobj, src) + charge = maxcharge + if(ratingdesc) + desc += " This one has a power rating of [DisplayPower(maxcharge)], and you should not swallow it." + update_icon() + +/obj/item/stock_parts/cell/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/stock_parts/cell/vv_edit_var(var_name, var_value) + switch(var_name) + if("self_recharge") + if(var_value) + START_PROCESSING(SSobj, src) + else + STOP_PROCESSING(SSobj, src) + . = ..() + +/obj/item/stock_parts/cell/process() + if(self_recharge) + give(chargerate * 0.25) + else + return PROCESS_KILL + +/obj/item/stock_parts/cell/update_icon() + overlays.Cut() + if(grown_battery) + overlays += image('icons/obj/power.dmi', "grown_wires") + if(charge < 0.01) + return + else if(charge/maxcharge >=0.995) + overlays += "cell-o2" + else + overlays += "cell-o1" + +/obj/item/stock_parts/cell/proc/percent() // return % charge of cell + return 100 * charge / maxcharge + +// use power from a cell +/obj/item/stock_parts/cell/use(amount) + if(rigged && amount > 0) + explode() + return 0 + if(charge < amount) + return 0 + charge = (charge - amount) + return 1 + +// recharge the cell +/obj/item/stock_parts/cell/proc/give(amount) + if(rigged && amount > 0) + explode() + return 0 + if(maxcharge < amount) + amount = maxcharge + var/power_used = min(maxcharge - charge, amount) + charge += power_used + return power_used + +/obj/item/stock_parts/cell/examine(mob/user) + . = ..() + if(rigged) + . += "This power cell seems to be faulty!" + else + . += "The charge meter reads [round(percent() )]%." + +/obj/item/stock_parts/cell/suicide_act(mob/user) + to_chat(viewers(user), "[user] is licking the electrodes of the [src]! It looks like [user.p_theyre()] trying to commit suicide.") + return FIRELOSS + +/obj/item/stock_parts/cell/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/reagent_containers/syringe)) + var/obj/item/reagent_containers/syringe/S = W + + to_chat(user, "You inject the solution into the power cell.") + + if(S.reagents.has_reagent("plasma", 5) || S.reagents.has_reagent("plasma_dust", 5)) + + rigged = TRUE + + log_admin("LOG: [key_name(user)] injected a power cell with plasma, rigging it to explode.") + message_admins("LOG: [key_name_admin(user)] injected a power cell with plasma, rigging it to explode.") + + S.reagents.clear_reagents() + else + return ..() + + +/obj/item/stock_parts/cell/proc/explode() + var/turf/T = get_turf(loc) + if(charge == 0) + return + var/devastation_range = -1 //round(charge/11000) + var/heavy_impact_range = round(sqrt(charge) / 60) + var/light_impact_range = round(sqrt(charge) / 30) + var/flash_range = light_impact_range + if(light_impact_range == 0) + rigged = FALSE + corrupt() + return + //explosion(T, 0, 1, 2, 2) + log_admin("LOG: Rigged power cell explosion, last touched by [fingerprintslast]") + message_admins("LOG: Rigged power cell explosion, last touched by [fingerprintslast]") + + explosion(T, devastation_range, heavy_impact_range, light_impact_range, flash_range) + qdel(src) + +/obj/item/stock_parts/cell/proc/corrupt() + charge /= 2 + maxcharge = max(maxcharge / 2, chargerate) + if(prob(10)) + rigged = TRUE //broken batterys are dangerous + +/obj/item/stock_parts/cell/emp_act(severity) + charge -= 1000 / severity + if(charge < 0) + charge = 0 + ..() + +/obj/item/stock_parts/cell/ex_act(severity) + ..() + if(!QDELETED(src)) + switch(severity) + if(2) + if(prob(50)) + corrupt() + if(3) + if(prob(25)) + corrupt() + +/obj/item/stock_parts/cell/blob_act(obj/structure/blob/B) + ex_act(EXPLODE_DEVASTATE) + +/obj/item/stock_parts/cell/proc/get_electrocute_damage() + if(charge >= 1000) + return Clamp(20 + round(charge / 25000), 20, 195) + rand(-5, 5) + else + return 0 + +// Cell variants +/obj/item/stock_parts/cell/empty/New() + ..() + charge = 0 + +/obj/item/stock_parts/cell/crap + name = "\improper Nanotrasen brand rechargeable AA battery" + desc = "You can't top the plasma top." //TOTALLY TRADEMARK INFRINGEMENT + maxcharge = 500 + materials = list(MAT_GLASS = 40) + rating = 2 + +/obj/item/stock_parts/cell/crap/empty/New() + ..() + charge = 0 + update_icon() + +/obj/item/stock_parts/cell/upgraded + name = "upgraded power cell" + desc = "A power cell with a slightly higher capacity than normal!" + maxcharge = 2500 + materials = list(MAT_GLASS = 50) + rating = 2 + chargerate = 1000 + +/obj/item/stock_parts/cell/upgraded/plus + name = "upgraded power cell+" + desc = "A power cell with an even higher capacity than the base model!" + maxcharge = 5000 + +/obj/item/stock_parts/cell/secborg + name = "security borg rechargeable D battery" + origin_tech = null + maxcharge = 600 //600 max charge / 100 charge per shot = six shots + materials = list(MAT_GLASS = 40) + rating = 2.5 + +/obj/item/stock_parts/cell/secborg/empty/New() + ..() + charge = 0 + update_icon() + +/obj/item/stock_parts/cell/pulse //200 pulse shots + name = "pulse rifle power cell" + maxcharge = 40000 + rating = 3 + chargerate = 1500 + +/obj/item/stock_parts/cell/pulse/carbine //25 pulse shots + name = "pulse carbine power cell" + maxcharge = 5000 + +/obj/item/stock_parts/cell/pulse/pistol //10 pulse shots + name = "pulse pistol power cell" + maxcharge = 2000 + +/obj/item/stock_parts/cell/high + name = "high-capacity power cell" + origin_tech = "powerstorage=2" + icon_state = "hcell" + maxcharge = 10000 + materials = list(MAT_GLASS = 60) + rating = 3 + chargerate = 1500 + +/obj/item/stock_parts/cell/high/plus + name = "high-capacity power cell+" + desc = "Where did these come from?" + icon_state = "h+cell" + maxcharge = 15000 + chargerate = 2250 + +/obj/item/stock_parts/cell/high/empty/New() + ..() + charge = 0 + update_icon() + +/obj/item/stock_parts/cell/super + name = "super-capacity power cell" + origin_tech = "powerstorage=3;materials=3" + icon_state = "scell" + maxcharge = 20000 + materials = list(MAT_GLASS = 300) + rating = 4 + chargerate = 2000 + +/obj/item/stock_parts/cell/super/empty/New() + ..() + charge = 0 + update_icon() + +/obj/item/stock_parts/cell/hyper + name = "hyper-capacity power cell" + origin_tech = "powerstorage=4;engineering=4;materials=4" + icon_state = "hpcell" + maxcharge = 30000 + materials = list(MAT_GLASS = 400) + rating = 5 + chargerate = 3000 + +/obj/item/stock_parts/cell/hyper/empty/New() + ..() + charge = 0 + update_icon() + +/obj/item/stock_parts/cell/bluespace + name = "bluespace power cell" + desc = "A rechargeable transdimensional power cell." + origin_tech = "powerstorage=5;bluespace=4;materials=4;engineering=4" + icon_state = "bscell" + maxcharge = 40000 + materials = list(MAT_GLASS = 600) + rating = 6 + chargerate = 4000 + +/obj/item/stock_parts/cell/bluespace/empty/New() + ..() + charge = 0 + update_icon() + +/obj/item/stock_parts/cell/infinite + name = "infinite-capacity power cell!" + icon_state = "icell" + origin_tech = "powerstorage=7" + maxcharge = 30000 + materials = list(MAT_GLASS=1000) + rating = 6 + chargerate = 30000 + +/obj/item/stock_parts/cell/infinite/use() + return 1 + +/obj/item/stock_parts/cell/infinite/abductor + name = "void core" + desc = "An alien power cell that produces energy seemingly out of nowhere." + icon = 'icons/obj/abductor.dmi' + icon_state = "cell" + maxcharge = 50000 + rating = 12 + ratingdesc = FALSE + +/obj/item/stock_parts/cell/infinite/abductor/update_icon() + return + + +/obj/item/stock_parts/cell/potato + name = "potato battery" + desc = "A rechargeable starch based power cell." + icon = 'icons/obj/hydroponics/harvest.dmi' + icon_state = "potato" + origin_tech = "powerstorage=1;biotech=1" + charge = 100 + maxcharge = 300 + materials = list() + rating = 1 + grown_battery = TRUE //it has the overlays for wires + +/obj/item/stock_parts/cell/high/slime + name = "charged slime core" + desc = "A yellow slime core infused with plasma, it crackles with power." + origin_tech = "powerstorage=5;biotech=4" + icon = 'icons/mob/slimes.dmi' + icon_state = "yellow slime extract" + materials = list() + rating = 5 //self-recharge makes these desirable + self_recharge = 1 // Infused slime cores self-recharge, over time + chargerate = 500 + +/obj/item/stock_parts/cell/emproof + name = "\improper EMP-proof cell" + desc = "An EMP-proof cell." + maxcharge = 500 + rating = 3 + +/obj/item/stock_parts/cell/emproof/empty/New() + ..() + charge = 0 + update_icon() + +/obj/item/stock_parts/cell/emproof/emp_act(severity) + return + +/obj/item/stock_parts/cell/emproof/corrupt() + return + +/obj/item/stock_parts/cell/ninja + name = "spider-clan power cell" + desc = "A standard ninja-suit power cell." + maxcharge = 10000 + materials = list(MAT_GLASS = 60) diff --git a/code/modules/power/generator.dm b/code/modules/power/generator.dm index 3d137a3c298..4a2d8b66518 100644 --- a/code/modules/power/generator.dm +++ b/code/modules/power/generator.dm @@ -1,247 +1,250 @@ -/obj/machinery/power/generator - name = "thermoelectric generator" - desc = "It's a high efficiency thermoelectric generator." - icon_state = "teg" - anchored = 0 - density = 1 - use_power = NO_POWER_USE - - var/obj/machinery/atmospherics/binary/circulator/cold_circ - var/obj/machinery/atmospherics/binary/circulator/hot_circ - - var/cold_dir = WEST - var/hot_dir = EAST - - var/lastgen = 0 - var/lastgenlev = -1 - var/lastcirc = "00" - -/obj/machinery/power/generator/New() - ..() - update_desc() - -/obj/machinery/power/generator/proc/update_desc() - desc = initial(desc) + " Its cold circulator is located on the [dir2text(cold_dir)] side, and its heat circulator is located on the [dir2text(hot_dir)] side." - -/obj/machinery/power/generator/Destroy() - disconnect() - return ..() - -/obj/machinery/power/generator/proc/disconnect() - if(cold_circ) - cold_circ.generator = null - if(hot_circ) - hot_circ.generator = null - if(powernet) - disconnect_from_network() - -/obj/machinery/power/generator/Initialize() - ..() - connect() - -/obj/machinery/power/generator/proc/connect() - connect_to_network() - - var/obj/machinery/atmospherics/binary/circulator/circpath = /obj/machinery/atmospherics/binary/circulator - cold_circ = locate(circpath) in get_step(src, cold_dir) - hot_circ = locate(circpath) in get_step(src, hot_dir) - - if(cold_circ && cold_circ.side == cold_dir) - cold_circ.generator = src - cold_circ.update_icon() - else - cold_circ = null - - if(hot_circ && hot_circ.side == hot_dir) - hot_circ.generator = src - hot_circ.update_icon() - else - hot_circ = null - - power_change() - update_icon() - updateDialog() - -/obj/machinery/power/generator/power_change() - if(!anchored) - stat |= NOPOWER - else - ..() - -/obj/machinery/power/generator/update_icon() - if(stat & (NOPOWER|BROKEN)) - overlays.Cut() - else - overlays.Cut() - - if(lastgenlev != 0) - overlays += image('icons/obj/power.dmi', "teg-op[lastgenlev]") - - overlays += image('icons/obj/power.dmi', "teg-oc[lastcirc]") - -/obj/machinery/power/generator/process() - if(stat & (NOPOWER|BROKEN)) - return - - if(!cold_circ || !hot_circ) - return - - lastgen = 0 - - if(powernet) - - //log_debug("cold_circ and hot_circ pass") - - var/datum/gas_mixture/cold_air = cold_circ.return_transfer_air() - var/datum/gas_mixture/hot_air = hot_circ.return_transfer_air() - - //log_debug("hot_air = [hot_air]; cold_air = [cold_air];") - - if(cold_air && hot_air) - - //log_debug("hot_air = [hot_air] temperature = [hot_air.temperature]; cold_air = [cold_air] temperature = [hot_air.temperature];") - - //log_debug("coldair and hotair pass") - var/cold_air_heat_capacity = cold_air.heat_capacity() - var/hot_air_heat_capacity = hot_air.heat_capacity() - - var/delta_temperature = hot_air.temperature - cold_air.temperature - - //log_debug("delta_temperature = [delta_temperature]; cold_air_heat_capacity = [cold_air_heat_capacity]; hot_air_heat_capacity = [hot_air_heat_capacity]") - - if(delta_temperature > 0 && cold_air_heat_capacity > 0 && hot_air_heat_capacity > 0) - var/efficiency = 0.65 - - var/energy_transfer = delta_temperature * hot_air_heat_capacity * cold_air_heat_capacity / (hot_air_heat_capacity + cold_air_heat_capacity) - - var/heat = energy_transfer * (1 - efficiency) - lastgen = energy_transfer * efficiency - - //log_debug("lastgen = [lastgen]; heat = [heat]; delta_temperature = [delta_temperature]; hot_air_heat_capacity = [hot_air_heat_capacity]; cold_air_heat_capacity = [cold_air_heat_capacity];") - - hot_air.temperature = hot_air.temperature - energy_transfer / hot_air_heat_capacity - cold_air.temperature = cold_air.temperature + heat / cold_air_heat_capacity - - //log_debug("POWER: [lastgen] W generated at [efficiency * 100]% efficiency and sinks sizes [cold_air_heat_capacity], [hot_air_heat_capacity]") - - add_avail(lastgen) - // update icon overlays only if displayed level has changed - - if(hot_air) - var/datum/gas_mixture/hot_circ_air1 = hot_circ.get_outlet_air() - hot_circ_air1.merge(hot_air) - - if(cold_air) - var/datum/gas_mixture/cold_circ_air1 = cold_circ.get_outlet_air() - cold_circ_air1.merge(cold_air) - - var/genlev = max(0, min( round(11 * lastgen / 100000), 11)) - var/circ = "[cold_circ && cold_circ.last_pressure_delta > 0 ? "1" : "0"][hot_circ && hot_circ.last_pressure_delta > 0 ? "1" : "0"]" - if((genlev != lastgenlev) || (circ != lastcirc)) - lastgenlev = genlev - lastcirc = circ - update_icon() - - updateDialog() - -/obj/machinery/power/generator/attack_ai(mob/user) - return attack_hand(user) - -/obj/machinery/power/generator/attack_ghost(mob/user) - if(stat & (NOPOWER|BROKEN)) - return - interact(user) - -/obj/machinery/power/generator/attack_hand(mob/user) - if(..()) - user << browse(null, "window=teg") - return - interact(user) - -/obj/machinery/power/generator/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/wrench)) - anchored = !anchored - if(!anchored) - disconnect() - power_change() - else - connect() - playsound(loc, W.usesound, 50, 1) - to_chat(user, "You [anchored ? "secure" : "unsecure"] the bolts holding [src] to the floor.") - else if(ismultitool(W)) - if(cold_dir == WEST) - cold_dir = EAST - hot_dir = WEST - else if(cold_dir == NORTH) - cold_dir = SOUTH - hot_dir = NORTH - else if(cold_dir == EAST) - cold_dir = WEST - hot_dir = EAST - else - cold_dir = NORTH - hot_dir = SOUTH - connect() - to_chat(user, "You reverse the generator's circulator settings. The cold circulator is now on the [dir2text(cold_dir)] side, and the heat circulator is now on the [dir2text(hot_dir)] side.") - update_desc() - else - return ..() - -/obj/machinery/power/generator/proc/get_menu(include_link = 1) - var/t = "" - if(!powernet) - t += "Unable to connect to the power network!" - t += "
        Retry" - else if(cold_circ && hot_circ) - var/datum/gas_mixture/cold_circ_air1 = cold_circ.get_outlet_air() - var/datum/gas_mixture/cold_circ_air2 = cold_circ.get_inlet_air() - var/datum/gas_mixture/hot_circ_air1 = hot_circ.get_outlet_air() - var/datum/gas_mixture/hot_circ_air2 = hot_circ.get_inlet_air() - - t += "
        " - - t += "Output: [round(lastgen)] W" - - t += "
        " - - t += "Cold loop
        " - t += "Temperature Inlet: [round(cold_circ_air2.temperature, 0.1)] K / Outlet: [round(cold_circ_air1.temperature, 0.1)] K
        " - t += "Pressure Inlet: [round(cold_circ_air2.return_pressure(), 0.1)] kPa / Outlet: [round(cold_circ_air1.return_pressure(), 0.1)] kPa
        " - - t += "Hot loop
        " - t += "Temperature Inlet: [round(hot_circ_air2.temperature, 0.1)] K / Outlet: [round(hot_circ_air1.temperature, 0.1)] K
        " - t += "Pressure Inlet: [round(hot_circ_air2.return_pressure(), 0.1)] kPa / Outlet: [round(hot_circ_air1.return_pressure(), 0.1)] kPa
        " - - t += "
        " - else - t += "Unable to locate all parts!" - t += "
        Retry" - if(include_link) - t += "
        Close" - - return t - -/obj/machinery/power/generator/interact(mob/user) - user.set_machine(src) - - var/datum/browser/popup = new(user, "teg", "Thermo-Electric Generator", 460, 300) - popup.set_content(get_menu()) - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) - popup.open() - return 1 - -/obj/machinery/power/generator/Topic(href, href_list) - if(..()) - return 0 - if( href_list["close"] ) - usr << browse(null, "window=teg") - usr.unset_machine() - return 0 - if( href_list["check"] ) - if(!powernet || !cold_circ || !hot_circ) - connect() - return 1 - -/obj/machinery/power/generator/power_change() - ..() - update_icon() +/obj/machinery/power/generator + name = "thermoelectric generator" + desc = "It's a high efficiency thermoelectric generator." + icon_state = "teg" + anchored = 0 + density = 1 + use_power = NO_POWER_USE + + var/obj/machinery/atmospherics/binary/circulator/cold_circ + var/obj/machinery/atmospherics/binary/circulator/hot_circ + + var/cold_dir = WEST + var/hot_dir = EAST + + var/lastgen = 0 + var/lastgenlev = -1 + var/lastcirc = "00" + +/obj/machinery/power/generator/New() + ..() + update_desc() + +/obj/machinery/power/generator/proc/update_desc() + desc = initial(desc) + " Its cold circulator is located on the [dir2text(cold_dir)] side, and its heat circulator is located on the [dir2text(hot_dir)] side." + +/obj/machinery/power/generator/Destroy() + disconnect() + return ..() + +/obj/machinery/power/generator/proc/disconnect() + if(cold_circ) + cold_circ.generator = null + if(hot_circ) + hot_circ.generator = null + if(powernet) + disconnect_from_network() + +/obj/machinery/power/generator/Initialize() + ..() + connect() + +/obj/machinery/power/generator/proc/connect() + connect_to_network() + + var/obj/machinery/atmospherics/binary/circulator/circpath = /obj/machinery/atmospherics/binary/circulator + cold_circ = locate(circpath) in get_step(src, cold_dir) + hot_circ = locate(circpath) in get_step(src, hot_dir) + + if(cold_circ && cold_circ.side == cold_dir) + cold_circ.generator = src + cold_circ.update_icon() + else + cold_circ = null + + if(hot_circ && hot_circ.side == hot_dir) + hot_circ.generator = src + hot_circ.update_icon() + else + hot_circ = null + + power_change() + update_icon() + updateDialog() + +/obj/machinery/power/generator/power_change() + if(!anchored) + stat |= NOPOWER + else + ..() + +/obj/machinery/power/generator/update_icon() + if(stat & (NOPOWER|BROKEN)) + overlays.Cut() + else + overlays.Cut() + + if(lastgenlev != 0) + overlays += image('icons/obj/power.dmi', "teg-op[lastgenlev]") + + overlays += image('icons/obj/power.dmi', "teg-oc[lastcirc]") + +/obj/machinery/power/generator/process() + if(stat & (NOPOWER|BROKEN)) + return + + if(!cold_circ || !hot_circ) + return + + lastgen = 0 + + if(powernet) + + //log_debug("cold_circ and hot_circ pass") + + var/datum/gas_mixture/cold_air = cold_circ.return_transfer_air() + var/datum/gas_mixture/hot_air = hot_circ.return_transfer_air() + + //log_debug("hot_air = [hot_air]; cold_air = [cold_air];") + + if(cold_air && hot_air) + + //log_debug("hot_air = [hot_air] temperature = [hot_air.temperature]; cold_air = [cold_air] temperature = [hot_air.temperature];") + + //log_debug("coldair and hotair pass") + var/cold_air_heat_capacity = cold_air.heat_capacity() + var/hot_air_heat_capacity = hot_air.heat_capacity() + + var/delta_temperature = hot_air.temperature - cold_air.temperature + + //log_debug("delta_temperature = [delta_temperature]; cold_air_heat_capacity = [cold_air_heat_capacity]; hot_air_heat_capacity = [hot_air_heat_capacity]") + + if(delta_temperature > 0 && cold_air_heat_capacity > 0 && hot_air_heat_capacity > 0) + var/efficiency = 0.65 + + var/energy_transfer = delta_temperature * hot_air_heat_capacity * cold_air_heat_capacity / (hot_air_heat_capacity + cold_air_heat_capacity) + + var/heat = energy_transfer * (1 - efficiency) + lastgen = energy_transfer * efficiency + + //log_debug("lastgen = [lastgen]; heat = [heat]; delta_temperature = [delta_temperature]; hot_air_heat_capacity = [hot_air_heat_capacity]; cold_air_heat_capacity = [cold_air_heat_capacity];") + + hot_air.temperature = hot_air.temperature - energy_transfer / hot_air_heat_capacity + cold_air.temperature = cold_air.temperature + heat / cold_air_heat_capacity + + //log_debug("POWER: [lastgen] W generated at [efficiency * 100]% efficiency and sinks sizes [cold_air_heat_capacity], [hot_air_heat_capacity]") + + add_avail(lastgen) + // update icon overlays only if displayed level has changed + + if(hot_air) + var/datum/gas_mixture/hot_circ_air1 = hot_circ.get_outlet_air() + hot_circ_air1.merge(hot_air) + + if(cold_air) + var/datum/gas_mixture/cold_circ_air1 = cold_circ.get_outlet_air() + cold_circ_air1.merge(cold_air) + + var/genlev = max(0, min( round(11 * lastgen / 100000), 11)) + var/circ = "[cold_circ && cold_circ.last_pressure_delta > 0 ? "1" : "0"][hot_circ && hot_circ.last_pressure_delta > 0 ? "1" : "0"]" + if((genlev != lastgenlev) || (circ != lastcirc)) + lastgenlev = genlev + lastcirc = circ + update_icon() + + updateDialog() + +/obj/machinery/power/generator/attack_ai(mob/user) + return attack_hand(user) + +/obj/machinery/power/generator/attack_ghost(mob/user) + if(stat & (NOPOWER|BROKEN)) + return + interact(user) + +/obj/machinery/power/generator/attack_hand(mob/user) + if(..()) + user << browse(null, "window=teg") + return + interact(user) + +/obj/machinery/power/generator/multitool_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(cold_dir == WEST) + cold_dir = EAST + hot_dir = WEST + else if(cold_dir == NORTH) + cold_dir = SOUTH + hot_dir = NORTH + else if(cold_dir == EAST) + cold_dir = WEST + hot_dir = EAST + else + cold_dir = NORTH + hot_dir = SOUTH + connect() + to_chat(user, "You reverse the generator's circulator settings. The cold circulator is now on the [dir2text(cold_dir)] side, and the heat circulator is now on the [dir2text(hot_dir)] side.") + update_desc() + +/obj/machinery/power/generator/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + anchored = !anchored + if(!anchored) + disconnect() + power_change() + else + connect() + to_chat(user, "You [anchored ? "secure" : "unsecure"] the bolts holding [src] to the floor.") + +/obj/machinery/power/generator/proc/get_menu(include_link = 1) + var/t = "" + if(!powernet) + t += "Unable to connect to the power network!" + t += "
        Retry" + else if(cold_circ && hot_circ) + var/datum/gas_mixture/cold_circ_air1 = cold_circ.get_outlet_air() + var/datum/gas_mixture/cold_circ_air2 = cold_circ.get_inlet_air() + var/datum/gas_mixture/hot_circ_air1 = hot_circ.get_outlet_air() + var/datum/gas_mixture/hot_circ_air2 = hot_circ.get_inlet_air() + + t += "
        " + + t += "Output: [round(lastgen)] W" + + t += "
        " + + t += "Cold loop
        " + t += "Temperature Inlet: [round(cold_circ_air2.temperature, 0.1)] K / Outlet: [round(cold_circ_air1.temperature, 0.1)] K
        " + t += "Pressure Inlet: [round(cold_circ_air2.return_pressure(), 0.1)] kPa / Outlet: [round(cold_circ_air1.return_pressure(), 0.1)] kPa
        " + + t += "Hot loop
        " + t += "Temperature Inlet: [round(hot_circ_air2.temperature, 0.1)] K / Outlet: [round(hot_circ_air1.temperature, 0.1)] K
        " + t += "Pressure Inlet: [round(hot_circ_air2.return_pressure(), 0.1)] kPa / Outlet: [round(hot_circ_air1.return_pressure(), 0.1)] kPa
        " + + t += "
        " + else + t += "Unable to locate all parts!" + t += "
        Retry" + if(include_link) + t += "
        Close" + + return t + +/obj/machinery/power/generator/interact(mob/user) + user.set_machine(src) + + var/datum/browser/popup = new(user, "teg", "Thermo-Electric Generator", 460, 300) + popup.set_content(get_menu()) + popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) + popup.open() + return 1 + +/obj/machinery/power/generator/Topic(href, href_list) + if(..()) + return 0 + if( href_list["close"] ) + usr << browse(null, "window=teg") + usr.unset_machine() + return 0 + if( href_list["check"] ) + if(!powernet || !cold_circ || !hot_circ) + connect() + return 1 + +/obj/machinery/power/generator/power_change() + ..() + update_icon() diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm index 08464600487..a5b83d8472c 100644 --- a/code/modules/power/gravitygenerator.dm +++ b/code/modules/power/gravitygenerator.dm @@ -5,14 +5,14 @@ var/list/gravity_generators = list() // We will keep track of this by adding new gravity generators to the list, and keying it with the z level. -var/const/POWER_IDLE = 0 -var/const/POWER_UP = 1 -var/const/POWER_DOWN = 2 +#define GRAV_POWER_IDLE 0 +#define GRAV_POWER_UP 1 +#define GRAV_POWER_DOWN 2 -var/const/GRAV_NEEDS_SCREWDRIVER = 0 -var/const/GRAV_NEEDS_WELDING = 1 -var/const/GRAV_NEEDS_PLASTEEL = 2 -var/const/GRAV_NEEDS_WRENCH = 3 +#define GRAV_NEEDS_SCREWDRIVER 0 +#define GRAV_NEEDS_WELDING 1 +#define GRAV_NEEDS_PLASTEEL 2 +#define GRAV_NEEDS_WRENCH 3 // // Abstract Generator @@ -111,7 +111,7 @@ var/const/GRAV_NEEDS_WRENCH = 3 var/breaker = 1 var/list/parts = list() var/obj/middle = null - var/charging_state = POWER_IDLE + var/charging_state = GRAV_POWER_IDLE var/charge_count = 100 var/current_overlay = null var/broken_state = 0 @@ -187,15 +187,6 @@ var/const/GRAV_NEEDS_WRENCH = 3 broken_state++ update_icon() return - if(GRAV_NEEDS_WELDING) - if(istype(I, /obj/item/weldingtool)) - var/obj/item/weldingtool/WT = I - if(WT.remove_fuel(1, user)) - to_chat(user, "You mend the damaged framework.") - playsound(src.loc, WT.usesound, 50, 1) - broken_state++ - update_icon() - return if(GRAV_NEEDS_PLASTEEL) if(istype(I, /obj/item/stack/sheet/plasteel)) var/obj/item/stack/sheet/plasteel/PS = I @@ -216,6 +207,16 @@ var/const/GRAV_NEEDS_WRENCH = 3 return return ..() +/obj/machinery/gravity_generator/main/welder_act(mob/user, obj/item/I) + if(broken_state != GRAV_NEEDS_WELDING) + return + . = TRUE + if(!I.use_tool(src, user, amount = 1, volume = I.tool_volume)) + return + to_chat(user, "You mend the damaged framework.") + broken_state++ + update_icon() + /obj/machinery/gravity_generator/main/attack_hand(mob/user as mob) if(!..()) return interact(user) @@ -237,8 +238,8 @@ var/const/GRAV_NEEDS_WRENCH = 3 dat += "ON OFF " dat += "
        Generator Status:
        " - if(charging_state != POWER_IDLE) - dat += "WARNING Radiation Detected.
        [charging_state == POWER_UP ? "Charging..." : "Discharging..."]" + if(charging_state != GRAV_POWER_IDLE) + dat += "WARNING Radiation Detected.
        [charging_state == GRAV_POWER_UP ? "Charging..." : "Discharging..."]" else if(on) dat += "Powered." else @@ -271,7 +272,7 @@ var/const/GRAV_NEEDS_WRENCH = 3 /obj/machinery/gravity_generator/main/get_status() if(stat & BROKEN) return "fix[min(broken_state, 3)]" - return on || charging_state != POWER_IDLE ? "on" : "off" + return on || charging_state != GRAV_POWER_IDLE ? "on" : "off" /obj/machinery/gravity_generator/main/update_icon() ..() @@ -286,13 +287,13 @@ var/const/GRAV_NEEDS_WRENCH = 3 else if(breaker) new_state = 1 - charging_state = new_state ? POWER_UP : POWER_DOWN // Startup sequence animation. - investigate_log("is now [charging_state == POWER_UP ? "charging" : "discharging"].", "gravity") + charging_state = new_state ? GRAV_POWER_UP : GRAV_POWER_DOWN // Startup sequence animation. + investigate_log("is now [charging_state == GRAV_POWER_UP ? "charging" : "discharging"].", "gravity") update_icon() // Set the state of the gravity. /obj/machinery/gravity_generator/main/proc/set_state(var/new_state) - charging_state = POWER_IDLE + charging_state = GRAV_POWER_IDLE on = new_state use_power = on ? ACTIVE_POWER_USE : IDLE_POWER_USE // Sound the alert if gravity was just enabled or disabled. @@ -326,15 +327,15 @@ var/const/GRAV_NEEDS_WRENCH = 3 /obj/machinery/gravity_generator/main/process() if(stat & BROKEN) return - if(charging_state != POWER_IDLE) - if(charging_state == POWER_UP && charge_count >= 100) + if(charging_state != GRAV_POWER_IDLE) + if(charging_state == GRAV_POWER_UP && charge_count >= 100) set_state(1) - else if(charging_state == POWER_DOWN && charge_count <= 0) + else if(charging_state == GRAV_POWER_DOWN && charge_count <= 0) set_state(0) else - if(charging_state == POWER_UP) + if(charging_state == GRAV_POWER_UP) charge_count += 2 - else if(charging_state == POWER_DOWN) + else if(charging_state == GRAV_POWER_DOWN) charge_count -= 2 if(charge_count % 4 == 0 && prob(75)) // Let them know it is charging/discharging. diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index d02263d8ed8..78c2561ae7d 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -1,740 +1,740 @@ -// The lighting system -// -// consists of light fixtures (/obj/machinery/light) and light tube/bulb items (/obj/item/light) - - -// status values shared between lighting fixtures and items -#define LIGHT_OK 0 -#define LIGHT_EMPTY 1 -#define LIGHT_BROKEN 2 -#define LIGHT_BURNED 3 - -/obj/machinery/light_construct - name = "light fixture frame" - desc = "A light fixture under construction." - icon = 'icons/obj/lighting.dmi' - icon_state = "tube-construct-stage1" - anchored = 1 - layer = 5 - max_integrity = 200 - armor = list("melee" = 50, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 50) - var/stage = 1 - var/fixture_type = "tube" - var/sheets_refunded = 2 - var/obj/machinery/light/newlight = null - -/obj/machinery/light_construct/New() - ..() - if(fixture_type == "bulb") - icon_state = "bulb-construct-stage1" - -/obj/machinery/light_construct/examine(mob/user) - . = ..() - if(get_dist(user, src) <= 2) - switch(stage) - if(1) - . += "It's an empty frame." - if(2) - . += "It's wired." - if(3) - . += "The casing is closed." - -/obj/machinery/light_construct/attackby(obj/item/W as obj, mob/living/user as mob, params) - src.add_fingerprint(user) - if(istype(W, /obj/item/wrench)) - if(src.stage == 1) - playsound(src.loc, W.usesound, 75, 1) - to_chat(usr, "You begin deconstructing [src].") - if(!do_after(usr, 30 * W.toolspeed, target = src)) - return - new /obj/item/stack/sheet/metal( get_turf(src.loc), sheets_refunded ) - user.visible_message("[user.name] deconstructs [src].", \ - "You deconstruct [src].", "You hear a noise.") - playsound(src.loc, W.usesound, 75, 1) - qdel(src) - if(src.stage == 2) - to_chat(usr, "You have to remove the wires first.") - return - - if(src.stage == 3) - to_chat(usr, "You have to unscrew the case first.") - return - - if(istype(W, /obj/item/wirecutters)) - if(src.stage != 2) return - src.stage = 1 - switch(fixture_type) - if("tube") - src.icon_state = "tube-construct-stage1" - if("bulb") - src.icon_state = "bulb-construct-stage1" - new /obj/item/stack/cable_coil(get_turf(src.loc), 1, paramcolor = COLOR_RED) - user.visible_message("[user.name] removes the wiring from [src].", \ - "You remove the wiring from [src].", "You hear a noise.") - playsound(loc, W.usesound, 100, 1) - return - - if(istype(W, /obj/item/stack/cable_coil)) - if(src.stage != 1) return - var/obj/item/stack/cable_coil/coil = W - coil.use(1) - switch(fixture_type) - if("tube") - src.icon_state = "tube-construct-stage2" - if("bulb") - src.icon_state = "bulb-construct-stage2" - src.stage = 2 - playsound(loc, coil.usesound, 50, 1) - user.visible_message("[user.name] adds wires to [src].", \ - "You add wires to [src].") - return - - if(istype(W, /obj/item/screwdriver)) - if(src.stage == 2) - switch(fixture_type) - if("tube") - src.icon_state = "tube-empty" - if("bulb") - src.icon_state = "bulb-empty" - src.stage = 3 - user.visible_message("[user.name] closes [src]'s casing.", \ - "You close [src]'s casing.", "You hear a noise.") - playsound(src.loc, W.usesound, 75, 1) - - switch(fixture_type) - - if("tube") - newlight = new /obj/machinery/light/built(src.loc) - if("bulb") - newlight = new /obj/machinery/light/small/built(src.loc) - - newlight.dir = src.dir - src.transfer_fingerprints_to(newlight) - qdel(src) - return - else - return ..() - -/obj/machinery/light_construct/blob_act(obj/structure/blob/B) - if(B && B.loc == loc) - qdel(src) - -/obj/machinery/light_construct/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - new /obj/item/stack/sheet/metal(loc, sheets_refunded) - qdel(src) - -/obj/machinery/light_construct/small - name = "small light fixture frame" - desc = "A small light fixture under construction." - icon = 'icons/obj/lighting.dmi' - icon_state = "bulb-construct-stage1" - anchored = 1 - layer = 5 - stage = 1 - fixture_type = "bulb" - sheets_refunded = 1 - - -// the standard tube light fixture -/obj/machinery/light - name = "light fixture" - icon = 'icons/obj/lighting.dmi' - var/base_state = "tube" // base description and icon_state - icon_state = "tube1" - desc = "A lighting fixture." - anchored = 1 - layer = 5 // They were appearing under mobs which is a little weird - Ostaf - max_integrity = 100 - use_power = ACTIVE_POWER_USE - idle_power_usage = 2 - active_power_usage = 20 - power_channel = LIGHT //Lights are calc'd via area so they dont need to be in the machine list - var/on = FALSE // 1 if on, 0 if off - var/on_gs = 0 - var/static_power_used = 0 - var/brightness_range = 8 // luminosity when on, also used in power calculation - var/brightness_power = 1 - var/brightness_color = "#FFFFFF" - var/status = LIGHT_OK // LIGHT_OK, _EMPTY, _BURNED or _BROKEN - var/flickering = 0 - var/light_type = /obj/item/light/tube // the type of light item - var/fitting = "tube" - var/switchcount = 0 // count of number of times switched on/off - // this is used to calc the probability the light burns out - - var/rigged = 0 // true if rigged to explode - var/lightmaterials = list(MAT_GLASS=100) //stores the materials the light is made of to stop infinite glass exploit - - var/nightshift_enabled = FALSE //Currently in night shift mode? - var/nightshift_allowed = TRUE //Set to FALSE to never let this light get switched to night mode. - var/nightshift_light_range = 8 - var/nightshift_light_power = 0.45 - var/nightshift_light_color = "#FFDDCC" - -// the smaller bulb light fixture - -/obj/machinery/light/small - icon_state = "bulb1" - base_state = "bulb" - fitting = "bulb" - brightness_range = 4 - brightness_color = "#a0a080" - nightshift_light_range = 4 - desc = "A small lighting fixture." - light_type = /obj/item/light/bulb - -/obj/machinery/light/spot - name = "spotlight" - fitting = "large tube" - light_type = /obj/item/light/tube/large - brightness_range = 12 - brightness_power = 4 - -/obj/machinery/light/built/New() - status = LIGHT_EMPTY - update(0) - ..() - -/obj/machinery/light/small/built/New() - status = LIGHT_EMPTY - update(0) - ..() - -// create a new lighting fixture -/obj/machinery/light/New() - ..() - spawn(2) - var/area/A = get_area(src) - if(A && !A.requires_power) - on = 1 - - switch(fitting) - if("tube") - brightness_range = 8 - if(prob(2)) - break_light_tube(1) - if("bulb") - brightness_range = 4 - brightness_color = "#a0a080" - if(prob(5)) - break_light_tube() - spawn(1) - update(0) - -/obj/machinery/light/Destroy() - var/area/A = get_area(src) - if(A) - on = FALSE -// A.update_lights() - return ..() - -/obj/machinery/light/update_icon() - - switch(status) // set icon_states - if(LIGHT_OK) - icon_state = "[base_state][on]" - if(LIGHT_EMPTY) - icon_state = "[base_state]-empty" - on = FALSE - if(LIGHT_BURNED) - icon_state = "[base_state]-burned" - on = FALSE - if(LIGHT_BROKEN) - icon_state = "[base_state]-broken" - on = FALSE - return - -/obj/machinery/light/get_spooked() - flicker() - -// update the icon_state and luminosity of the light depending on its state -/obj/machinery/light/proc/update(var/trigger = TRUE) - switch(status) - if(LIGHT_BROKEN, LIGHT_BURNED, LIGHT_EMPTY) - on = FALSE - update_icon() - if(on) - var/BR = nightshift_enabled ? nightshift_light_range : brightness_range - var/PO = nightshift_enabled ? nightshift_light_power : brightness_power - var/CO = nightshift_enabled ? nightshift_light_color : brightness_color - var/matching = light_range == BR && light_power == PO && light_color == CO - if(!matching) - switchcount++ - if(rigged) - if(status == LIGHT_OK && trigger) - log_admin("LOG: Rigged light explosion, last touched by [fingerprintslast]") - message_admins("LOG: Rigged light explosion, last touched by [fingerprintslast]") - explode() - else if(prob(min(60, switchcount * switchcount * 0.01))) - if(status == LIGHT_OK && trigger) - status = LIGHT_BURNED - icon_state = "[base_state]-burned" - on = FALSE - set_light(0) - else - use_power = ACTIVE_POWER_USE - set_light(BR, PO, CO) - else - use_power = IDLE_POWER_USE - set_light(0) - - active_power_usage = (brightness_range * 10) - if(on != on_gs) - on_gs = on - if(on) - static_power_used = brightness_range * 20 //20W per unit luminosity - addStaticPower(static_power_used, STATIC_LIGHT) - else - removeStaticPower(static_power_used, STATIC_LIGHT) - -// attempt to set the light's on/off status -// will not switch on if broken/burned/empty -/obj/machinery/light/proc/seton(var/s) - on = (s && status == LIGHT_OK) - update() - -// examine verb -/obj/machinery/light/examine(mob/user) - . = ..() - if(in_range(user, src)) - switch(status) - if(LIGHT_OK) - . += "[desc] It is turned [on? "on" : "off"]." - if(LIGHT_EMPTY) - . += "[desc] The [fitting] has been removed." - if(LIGHT_BURNED) - . += "[desc] The [fitting] is burnt out." - if(LIGHT_BROKEN) - . += "[desc] The [fitting] has been smashed." - - - -// attack with item - insert light (if right type), otherwise try to break the light - -/obj/machinery/light/attackby(obj/item/W, mob/living/user, params) - user.changeNext_move(CLICK_CD_MELEE) // This is an ugly hack and I hate it forever - //Light replacer code - if(istype(W, /obj/item/lightreplacer)) - var/obj/item/lightreplacer/LR = W - LR.ReplaceLight(src, user) - - // attempt to insert light - else if(istype(W, /obj/item/light)) - if(status != LIGHT_EMPTY) - to_chat(user, "There is a [fitting] already inserted.") - else - src.add_fingerprint(user) - var/obj/item/light/L = W - if(istype(L, light_type)) - status = L.status - to_chat(user, "You insert the [L.name].") - switchcount = L.switchcount - rigged = L.rigged - brightness_range = L.brightness_range - brightness_power = L.brightness_power - brightness_color = L.brightness_color - lightmaterials = L.materials - on = has_power() - update() - - user.drop_item() //drop the item to update overlays and such - qdel(L) - - if(on && rigged) - - log_admin("LOG: Rigged light explosion, last touched by [fingerprintslast]") - message_admins("LOG: Rigged light explosion, last touched by [fingerprintslast]") - - explode() - else - to_chat(user, "This type of light requires a [fitting].") - return - - // attempt to break the light - //If xenos decide they want to smash a light bulb with a toolbox, who am I to stop them? /N - - else if(status != LIGHT_BROKEN && status != LIGHT_EMPTY) - - user.do_attack_animation(src) - if(prob(1+W.force * 5)) - - to_chat(user, "You hit the light, and it smashes!") - for(var/mob/M in viewers(src)) - if(M == user) - continue - M.show_message("[user.name] smashed the light!", 3, "You hear a tinkle of breaking glass", 2) - if(on && (W.flags & CONDUCT)) - if(prob(12)) - electrocute_mob(user, get_area(src), src, 0.3, TRUE) - break_light_tube() - - else - user.visible_message("[user.name] hits the light.") - playsound(src.loc, 'sound/effects/glasshit.ogg', 75, 1) - - // attempt to stick weapon into light socket - else if(status == LIGHT_EMPTY) - if(istype(W, /obj/item/screwdriver)) //If it's a screwdriver open it. - playsound(src.loc, W.usesound, 75, 1) - user.visible_message("[user.name] opens [src]'s casing.", \ - "You open [src]'s casing.", "You hear a noise.") - deconstruct() - return - - to_chat(user, "You stick \the [W] into the light socket!") - if(has_power() && (W.flags & CONDUCT)) - do_sparks(3, 1, src) - if(prob(75)) - electrocute_mob(user, get_area(src), src, rand(0.7, 1), TRUE) - else - return ..() - -/obj/machinery/light/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - var/obj/machinery/light_construct/newlight = null - var/cur_stage = 2 - if(!disassembled) - cur_stage = 1 - switch(fitting) - if("tube") - newlight = new /obj/machinery/light_construct(src.loc) - newlight.icon_state = "tube-construct-stage2" - - if("bulb") - newlight = new /obj/machinery/light_construct/small(src.loc) - newlight.icon_state = "bulb-construct-stage2" - newlight.setDir(src.dir) - newlight.stage = cur_stage - if(!disassembled) - newlight.obj_integrity = newlight.max_integrity * 0.5 - if(status != LIGHT_BROKEN) - break_light_tube() - if(status != LIGHT_EMPTY) - drop_light_tube() - new /obj/item/stack/cable_coil(loc, 1, "red") - transfer_fingerprints_to(newlight) - qdel(src) - -/obj/machinery/light/attacked_by(obj/item/I, mob/living/user) - ..() - if(status == LIGHT_BROKEN || status == LIGHT_EMPTY) - if(on && (I.flags & CONDUCT)) - if(prob(12)) - electrocute_mob(user, get_area(src), src, 0.3, TRUE) - -/obj/machinery/light/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1) - . = ..() - if(. && !QDELETED(src)) - if(prob(damage_amount * 5)) - break_light_tube() - -/obj/machinery/light/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - switch(damage_type) - if(BRUTE) - switch(status) - if(LIGHT_EMPTY) - playsound(loc, 'sound/weapons/smash.ogg', 50, TRUE) - if(LIGHT_BROKEN) - playsound(loc, 'sound/effects/hit_on_shattered_glass.ogg', 90, TRUE) - else - playsound(loc, 'sound/effects/glasshit.ogg', 90, TRUE) - if(BURN) - playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE) - -// returns whether this light has power -// true if area has power and lightswitch is on -/obj/machinery/light/proc/has_power() - var/area/A = get_area(src) - return A.lightswitch && A.power_light - -/obj/machinery/light/proc/flicker(var/amount = rand(10, 20)) - if(flickering) return - flickering = 1 - spawn(0) - if(on && status == LIGHT_OK) - for(var/i = 0; i < amount; i++) - if(status != LIGHT_OK) break - on = !on - update(0) - sleep(rand(5, 15)) - on = (status == LIGHT_OK) - update(0) - flickering = 0 - -// ai attack - make lights flicker, because why not -/obj/machinery/light/attack_ai(mob/user) - src.flicker(1) - -// attack with hand - remove tube/bulb -// if hands aren't protected and the light is on, burn the player - -/obj/machinery/light/attack_hand(mob/user) - user.changeNext_move(CLICK_CD_MELEE) - add_fingerprint(user) - - if(status == LIGHT_EMPTY) - to_chat(user, "There is no [fitting] in this light.") - return - - // make it burn hands if not wearing fire-insulated gloves - if(on) - var/prot = 0 - var/mob/living/carbon/human/H = user - - if(istype(H)) - if(H.gloves) - var/obj/item/clothing/gloves/G = H.gloves - if(G.max_heat_protection_temperature) - prot = (G.max_heat_protection_temperature > 360) - else - prot = 1 - - if(prot > 0 || (HEATRES in user.mutations)) - to_chat(user, "You remove the light [fitting]") - else if(TK in user.mutations) - to_chat(user, "You telekinetically remove the light [fitting].") - else - if(user.a_intent == INTENT_DISARM || user.a_intent == INTENT_GRAB) - to_chat(user, "You try to remove the light [fitting], but you burn your hand on it!") - - var/obj/item/organ/external/affecting = H.get_organ("[user.hand ? "l" : "r" ]_hand") - if(affecting.receive_damage( 0, 5 )) // 5 burn damage - H.UpdateDamageIcon() - H.updatehealth() - return - else - to_chat(user, "You try to remove the light [fitting], but it's too hot to touch!") - return - else - to_chat(user, "You remove the light [fitting].") - // create a light tube/bulb item and put it in the user's hand - drop_light_tube(user) - -// break the light and make sparks if was on - -/obj/machinery/light/proc/drop_light_tube(mob/user) - var/obj/item/light/L = new light_type() - L.status = status - L.rigged = rigged - L.brightness_range = brightness_range - L.brightness_power = brightness_power - L.brightness_color = brightness_color - L.materials = lightmaterials - - // light item inherits the switchcount, then zero it - L.switchcount = switchcount - switchcount = 0 - - L.update() - L.forceMove(loc) - - if(user) //puts it in our active hand - L.add_fingerprint(user) - user.put_in_active_hand(L) - - status = LIGHT_EMPTY - update() - return L - -/obj/machinery/light/attack_tk(mob/user) - if(status == LIGHT_EMPTY) - to_chat(user, "There is no [fitting] in this light.") - return - - to_chat(user, "You telekinetically remove the light [fitting].") - // create a light tube/bulb item and put it in the user's hand - var/obj/item/light/L = drop_light_tube() - L.attack_tk(user) - -/obj/machinery/light/proc/break_light_tube(skip_sound_and_sparks = 0, overloaded = 0) - if(status == LIGHT_EMPTY || status == LIGHT_BROKEN) - return - - if(!skip_sound_and_sparks) - if(status == LIGHT_OK || status == LIGHT_BURNED) - playsound(src.loc, 'sound/effects/glasshit.ogg', 75, 1) - if(on || overloaded) - do_sparks(3, 1, src) - status = LIGHT_BROKEN - update() - -/obj/machinery/light/proc/fix() - if(status == LIGHT_OK) - return - status = LIGHT_OK - on = 1 - update() - -/obj/machinery/light/tesla_act(power, explosive = FALSE) - if(explosive) - explosion(loc,0,0,0,flame_range = 5, adminlog = 0) - qdel(src) - -// timed process -// use power - -// called when area power state changes -/obj/machinery/light/power_change() - var/area/A = get_area(src) - if(A) - seton(A.lightswitch && A.power_light) - -// called when on fire - -/obj/machinery/light/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - ..() - if(prob(max(0, exposed_temperature - 673))) //0% at <400C, 100% at >500C - break_light_tube() - -// explode the light - -/obj/machinery/light/proc/explode() - var/turf/T = get_turf(src.loc) - spawn(0) - break_light_tube() // break it first to give a warning - sleep(2) - explosion(T, 0, 0, 2, 2) - sleep(1) - qdel(src) - -// the light item -// can be tube or bulb subtypes -// will fit into empty /obj/machinery/light of the corresponding type - -/obj/item/light - icon = 'icons/obj/lighting.dmi' - force = 2 - throwforce = 5 - w_class = WEIGHT_CLASS_TINY - var/status = 0 // LIGHT_OK, LIGHT_BURNED or LIGHT_BROKEN - var/base_state - var/switchcount = 0 // number of times switched - materials = list(MAT_GLASS=100) - var/rigged = 0 // true if rigged to explode - var/brightness_range = 2 //how much light it gives off - var/brightness_power = 1 - var/brightness_color = null - -/obj/item/light/ComponentInitialize() - . = ..() - AddComponent(/datum/component/caltrop, force) - -/obj/item/light/Crossed(mob/living/L) - if(istype(L) && has_gravity(loc)) - if(L.incorporeal_move || L.flying) - return - playsound(loc, 'sound/effects/glass_step.ogg', 50, TRUE) - if(status == LIGHT_BURNED || status == LIGHT_OK) - shatter() - return ..() - -/obj/item/light/tube - name = "light tube" - desc = "A replacement light tube." - icon_state = "ltube" - base_state = "ltube" - item_state = "c_tube" - brightness_range = 8 - -/obj/item/light/tube/large - w_class = WEIGHT_CLASS_SMALL - name = "large light tube" - brightness_range = 15 - brightness_power = 2 - -/obj/item/light/bulb - name = "light bulb" - desc = "A replacement light bulb." - icon_state = "lbulb" - base_state = "lbulb" - item_state = "contvapour" - brightness_range = 5 - brightness_color = "#a0a080" - -/obj/item/light/throw_impact(atom/hit_atom) - ..() - shatter() - -/obj/item/light/bulb/fire - name = "fire bulb" - desc = "A replacement fire bulb." - icon_state = "fbulb" - base_state = "fbulb" - item_state = "egg4" - brightness_range = 5 - -// update the icon state and description of the light - -/obj/item/light/proc/update() - switch(status) - if(LIGHT_OK) - icon_state = base_state - desc = "A replacement [name]." - if(LIGHT_BURNED) - icon_state = "[base_state]-burned" - desc = "A burnt-out [name]." - if(LIGHT_BROKEN) - icon_state = "[base_state]-broken" - desc = "A broken [name]." - - -/obj/item/light/New() - ..() - switch(name) - if("light tube") - brightness_range = rand(6,9) - if("light bulb") - brightness_range = rand(4,6) - update() - - -// attack bulb/tube with object -// if a syringe, can inject plasma to make it explode -/obj/item/light/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/reagent_containers/syringe)) - var/obj/item/reagent_containers/syringe/S = I - - to_chat(user, "You inject the solution into the [src].") - - if(S.reagents.has_reagent("plasma", 5) || S.reagents.has_reagent("plasma_dust", 5)) - - log_admin("LOG: [key_name(user)] injected a light with plasma, rigging it to explode.") - message_admins("LOG: [key_name_admin(user)] injected a light with plasma, rigging it to explode.") - - rigged = 1 - - S.reagents.clear_reagents() - else - return ..() - -/obj/item/light/attack(mob/living/M, mob/living/user, def_zone) - ..() - shatter() - -/obj/item/light/attack_obj(obj/O, mob/living/user) - ..() - shatter() - -/obj/item/light/proc/shatter() - if(status == LIGHT_OK || status == LIGHT_BURNED) - src.visible_message("[name] shatters.","You hear a small glass object shatter.") - status = LIGHT_BROKEN - force = 5 - sharp = 1 - playsound(src.loc, 'sound/effects/glasshit.ogg', 75, 1) - update() - -/obj/item/light/suicide_act(mob/living/carbon/human/user) - user.visible_message("[user] touches [src], burning [user.p_their()] hands off!", "You touch [src], burning your hands off!") - - for(var/oname in list("l_hand", "r_hand")) - var/obj/item/organ/external/limb = user.get_organ(oname) - if(limb) - limb.droplimb(0, DROPLIMB_BURN) - return FIRELOSS - -/obj/machinery/light/extinguish_light() - on = FALSE - visible_message("[src] flickers and falls dark.") - update(0) +// The lighting system +// +// consists of light fixtures (/obj/machinery/light) and light tube/bulb items (/obj/item/light) + + +// status values shared between lighting fixtures and items +#define LIGHT_OK 0 +#define LIGHT_EMPTY 1 +#define LIGHT_BROKEN 2 +#define LIGHT_BURNED 3 + +/obj/machinery/light_construct + name = "light fixture frame" + desc = "A light fixture under construction." + icon = 'icons/obj/lighting.dmi' + icon_state = "tube-construct-stage1" + anchored = 1 + layer = 5 + max_integrity = 200 + armor = list("melee" = 50, "bullet" = 10, "laser" = 10, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 50) + var/stage = 1 + var/fixture_type = "tube" + var/sheets_refunded = 2 + var/obj/machinery/light/newlight = null + +/obj/machinery/light_construct/New() + ..() + if(fixture_type == "bulb") + icon_state = "bulb-construct-stage1" + +/obj/machinery/light_construct/examine(mob/user) + . = ..() + if(get_dist(user, src) <= 2) + switch(stage) + if(1) + . += "It's an empty frame." + if(2) + . += "It's wired." + if(3) + . += "The casing is closed." + +/obj/machinery/light_construct/attackby(obj/item/W as obj, mob/living/user as mob, params) + src.add_fingerprint(user) + if(istype(W, /obj/item/wrench)) + if(src.stage == 1) + playsound(src.loc, W.usesound, 75, 1) + to_chat(usr, "You begin deconstructing [src].") + if(!do_after(usr, 30 * W.toolspeed, target = src)) + return + new /obj/item/stack/sheet/metal( get_turf(src.loc), sheets_refunded ) + user.visible_message("[user.name] deconstructs [src].", \ + "You deconstruct [src].", "You hear a noise.") + playsound(src.loc, W.usesound, 75, 1) + qdel(src) + if(src.stage == 2) + to_chat(usr, "You have to remove the wires first.") + return + + if(src.stage == 3) + to_chat(usr, "You have to unscrew the case first.") + return + + if(istype(W, /obj/item/wirecutters)) + if(src.stage != 2) return + src.stage = 1 + switch(fixture_type) + if("tube") + src.icon_state = "tube-construct-stage1" + if("bulb") + src.icon_state = "bulb-construct-stage1" + new /obj/item/stack/cable_coil(get_turf(src.loc), 1, paramcolor = COLOR_RED) + user.visible_message("[user.name] removes the wiring from [src].", \ + "You remove the wiring from [src].", "You hear a noise.") + playsound(loc, W.usesound, 100, 1) + return + + if(istype(W, /obj/item/stack/cable_coil)) + if(src.stage != 1) return + var/obj/item/stack/cable_coil/coil = W + coil.use(1) + switch(fixture_type) + if("tube") + src.icon_state = "tube-construct-stage2" + if("bulb") + src.icon_state = "bulb-construct-stage2" + src.stage = 2 + playsound(loc, coil.usesound, 50, 1) + user.visible_message("[user.name] adds wires to [src].", \ + "You add wires to [src].") + return + + if(istype(W, /obj/item/screwdriver)) + if(src.stage == 2) + switch(fixture_type) + if("tube") + src.icon_state = "tube-empty" + if("bulb") + src.icon_state = "bulb-empty" + src.stage = 3 + user.visible_message("[user.name] closes [src]'s casing.", \ + "You close [src]'s casing.", "You hear a noise.") + playsound(src.loc, W.usesound, 75, 1) + + switch(fixture_type) + + if("tube") + newlight = new /obj/machinery/light/built(src.loc) + if("bulb") + newlight = new /obj/machinery/light/small/built(src.loc) + + newlight.dir = src.dir + src.transfer_fingerprints_to(newlight) + qdel(src) + return + else + return ..() + +/obj/machinery/light_construct/blob_act(obj/structure/blob/B) + if(B && B.loc == loc) + qdel(src) + +/obj/machinery/light_construct/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + new /obj/item/stack/sheet/metal(loc, sheets_refunded) + qdel(src) + +/obj/machinery/light_construct/small + name = "small light fixture frame" + desc = "A small light fixture under construction." + icon = 'icons/obj/lighting.dmi' + icon_state = "bulb-construct-stage1" + anchored = 1 + layer = 5 + stage = 1 + fixture_type = "bulb" + sheets_refunded = 1 + + +// the standard tube light fixture +/obj/machinery/light + name = "light fixture" + icon = 'icons/obj/lighting.dmi' + var/base_state = "tube" // base description and icon_state + icon_state = "tube1" + desc = "A lighting fixture." + anchored = 1 + layer = 5 // They were appearing under mobs which is a little weird - Ostaf + max_integrity = 100 + use_power = ACTIVE_POWER_USE + idle_power_usage = 2 + active_power_usage = 20 + power_channel = LIGHT //Lights are calc'd via area so they dont need to be in the machine list + var/on = FALSE // 1 if on, 0 if off + var/on_gs = 0 + var/static_power_used = 0 + var/brightness_range = 8 // luminosity when on, also used in power calculation + var/brightness_power = 1 + var/brightness_color = "#FFFFFF" + var/status = LIGHT_OK // LIGHT_OK, _EMPTY, _BURNED or _BROKEN + var/flickering = 0 + var/light_type = /obj/item/light/tube // the type of light item + var/fitting = "tube" + var/switchcount = 0 // count of number of times switched on/off + // this is used to calc the probability the light burns out + + var/rigged = 0 // true if rigged to explode + var/lightmaterials = list(MAT_GLASS=100) //stores the materials the light is made of to stop infinite glass exploit + + var/nightshift_enabled = FALSE //Currently in night shift mode? + var/nightshift_allowed = TRUE //Set to FALSE to never let this light get switched to night mode. + var/nightshift_light_range = 8 + var/nightshift_light_power = 0.45 + var/nightshift_light_color = "#FFDDCC" + +// the smaller bulb light fixture + +/obj/machinery/light/small + icon_state = "bulb1" + base_state = "bulb" + fitting = "bulb" + brightness_range = 4 + brightness_color = "#a0a080" + nightshift_light_range = 4 + desc = "A small lighting fixture." + light_type = /obj/item/light/bulb + +/obj/machinery/light/spot + name = "spotlight" + fitting = "large tube" + light_type = /obj/item/light/tube/large + brightness_range = 12 + brightness_power = 4 + +/obj/machinery/light/built/New() + status = LIGHT_EMPTY + update(0) + ..() + +/obj/machinery/light/small/built/New() + status = LIGHT_EMPTY + update(0) + ..() + +// create a new lighting fixture +/obj/machinery/light/New() + ..() + spawn(2) + var/area/A = get_area(src) + if(A && !A.requires_power) + on = 1 + + switch(fitting) + if("tube") + brightness_range = 8 + if(prob(2)) + break_light_tube(1) + if("bulb") + brightness_range = 4 + brightness_color = "#a0a080" + if(prob(5)) + break_light_tube() + spawn(1) + update(0) + +/obj/machinery/light/Destroy() + var/area/A = get_area(src) + if(A) + on = FALSE +// A.update_lights() + return ..() + +/obj/machinery/light/update_icon() + + switch(status) // set icon_states + if(LIGHT_OK) + icon_state = "[base_state][on]" + if(LIGHT_EMPTY) + icon_state = "[base_state]-empty" + on = FALSE + if(LIGHT_BURNED) + icon_state = "[base_state]-burned" + on = FALSE + if(LIGHT_BROKEN) + icon_state = "[base_state]-broken" + on = FALSE + return + +/obj/machinery/light/get_spooked() + flicker() + +// update the icon_state and luminosity of the light depending on its state +/obj/machinery/light/proc/update(var/trigger = TRUE) + switch(status) + if(LIGHT_BROKEN, LIGHT_BURNED, LIGHT_EMPTY) + on = FALSE + update_icon() + if(on) + var/BR = nightshift_enabled ? nightshift_light_range : brightness_range + var/PO = nightshift_enabled ? nightshift_light_power : brightness_power + var/CO = nightshift_enabled ? nightshift_light_color : brightness_color + var/matching = light_range == BR && light_power == PO && light_color == CO + if(!matching) + switchcount++ + if(rigged) + if(status == LIGHT_OK && trigger) + log_admin("LOG: Rigged light explosion, last touched by [fingerprintslast]") + message_admins("LOG: Rigged light explosion, last touched by [fingerprintslast]") + explode() + else if(prob(min(60, switchcount * switchcount * 0.01))) + if(status == LIGHT_OK && trigger) + status = LIGHT_BURNED + icon_state = "[base_state]-burned" + on = FALSE + set_light(0) + else + use_power = ACTIVE_POWER_USE + set_light(BR, PO, CO) + else + use_power = IDLE_POWER_USE + set_light(0) + + active_power_usage = (brightness_range * 10) + if(on != on_gs) + on_gs = on + if(on) + static_power_used = brightness_range * 20 //20W per unit luminosity + addStaticPower(static_power_used, STATIC_LIGHT) + else + removeStaticPower(static_power_used, STATIC_LIGHT) + +// attempt to set the light's on/off status +// will not switch on if broken/burned/empty +/obj/machinery/light/proc/seton(var/s) + on = (s && status == LIGHT_OK) + update() + +// examine verb +/obj/machinery/light/examine(mob/user) + . = ..() + if(in_range(user, src)) + switch(status) + if(LIGHT_OK) + . += "[desc] It is turned [on? "on" : "off"]." + if(LIGHT_EMPTY) + . += "[desc] The [fitting] has been removed." + if(LIGHT_BURNED) + . += "[desc] The [fitting] is burnt out." + if(LIGHT_BROKEN) + . += "[desc] The [fitting] has been smashed." + + + +// attack with item - insert light (if right type), otherwise try to break the light + +/obj/machinery/light/attackby(obj/item/W, mob/living/user, params) + user.changeNext_move(CLICK_CD_MELEE) // This is an ugly hack and I hate it forever + //Light replacer code + if(istype(W, /obj/item/lightreplacer)) + var/obj/item/lightreplacer/LR = W + LR.ReplaceLight(src, user) + + // attempt to insert light + else if(istype(W, /obj/item/light)) + if(status != LIGHT_EMPTY) + to_chat(user, "There is a [fitting] already inserted.") + else + src.add_fingerprint(user) + var/obj/item/light/L = W + if(istype(L, light_type)) + status = L.status + to_chat(user, "You insert the [L.name].") + switchcount = L.switchcount + rigged = L.rigged + brightness_range = L.brightness_range + brightness_power = L.brightness_power + brightness_color = L.brightness_color + lightmaterials = L.materials + on = has_power() + update() + + user.drop_item() //drop the item to update overlays and such + qdel(L) + + if(on && rigged) + + log_admin("LOG: Rigged light explosion, last touched by [fingerprintslast]") + message_admins("LOG: Rigged light explosion, last touched by [fingerprintslast]") + + explode() + else + to_chat(user, "This type of light requires a [fitting].") + return + + // attempt to break the light + //If xenos decide they want to smash a light bulb with a toolbox, who am I to stop them? /N + + else if(status != LIGHT_BROKEN && status != LIGHT_EMPTY) + + user.do_attack_animation(src) + if(prob(1+W.force * 5)) + + to_chat(user, "You hit the light, and it smashes!") + for(var/mob/M in viewers(src)) + if(M == user) + continue + M.show_message("[user.name] smashed the light!", 3, "You hear a tinkle of breaking glass", 2) + if(on && (W.flags & CONDUCT)) + if(prob(12)) + electrocute_mob(user, get_area(src), src, 0.3, TRUE) + break_light_tube() + + else + user.visible_message("[user.name] hits the light.") + playsound(src.loc, 'sound/effects/glasshit.ogg', 75, 1) + + // attempt to stick weapon into light socket + else if(status == LIGHT_EMPTY) + if(istype(W, /obj/item/screwdriver)) //If it's a screwdriver open it. + playsound(src.loc, W.usesound, 75, 1) + user.visible_message("[user.name] opens [src]'s casing.", \ + "You open [src]'s casing.", "You hear a noise.") + deconstruct() + return + + to_chat(user, "You stick \the [W] into the light socket!") + if(has_power() && (W.flags & CONDUCT)) + do_sparks(3, 1, src) + if(prob(75)) + electrocute_mob(user, get_area(src), src, rand(0.7, 1), TRUE) + else + return ..() + +/obj/machinery/light/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + var/obj/machinery/light_construct/newlight = null + var/cur_stage = 2 + if(!disassembled) + cur_stage = 1 + switch(fitting) + if("tube") + newlight = new /obj/machinery/light_construct(src.loc) + newlight.icon_state = "tube-construct-stage2" + + if("bulb") + newlight = new /obj/machinery/light_construct/small(src.loc) + newlight.icon_state = "bulb-construct-stage2" + newlight.setDir(src.dir) + newlight.stage = cur_stage + if(!disassembled) + newlight.obj_integrity = newlight.max_integrity * 0.5 + if(status != LIGHT_BROKEN) + break_light_tube() + if(status != LIGHT_EMPTY) + drop_light_tube() + new /obj/item/stack/cable_coil(loc, 1, "red") + transfer_fingerprints_to(newlight) + qdel(src) + +/obj/machinery/light/attacked_by(obj/item/I, mob/living/user) + ..() + if(status == LIGHT_BROKEN || status == LIGHT_EMPTY) + if(on && (I.flags & CONDUCT)) + if(prob(12)) + electrocute_mob(user, get_area(src), src, 0.3, TRUE) + +/obj/machinery/light/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1) + . = ..() + if(. && !QDELETED(src)) + if(prob(damage_amount * 5)) + break_light_tube() + +/obj/machinery/light/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + switch(damage_type) + if(BRUTE) + switch(status) + if(LIGHT_EMPTY) + playsound(loc, 'sound/weapons/smash.ogg', 50, TRUE) + if(LIGHT_BROKEN) + playsound(loc, 'sound/effects/hit_on_shattered_glass.ogg', 90, TRUE) + else + playsound(loc, 'sound/effects/glasshit.ogg', 90, TRUE) + if(BURN) + playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE) + +// returns whether this light has power +// true if area has power and lightswitch is on +/obj/machinery/light/proc/has_power() + var/area/A = get_area(src) + return A.lightswitch && A.power_light + +/obj/machinery/light/proc/flicker(var/amount = rand(10, 20)) + if(flickering) return + flickering = 1 + spawn(0) + if(on && status == LIGHT_OK) + for(var/i = 0; i < amount; i++) + if(status != LIGHT_OK) break + on = !on + update(0) + sleep(rand(5, 15)) + on = (status == LIGHT_OK) + update(0) + flickering = 0 + +// ai attack - make lights flicker, because why not +/obj/machinery/light/attack_ai(mob/user) + src.flicker(1) + +// attack with hand - remove tube/bulb +// if hands aren't protected and the light is on, burn the player + +/obj/machinery/light/attack_hand(mob/user) + user.changeNext_move(CLICK_CD_MELEE) + add_fingerprint(user) + + if(status == LIGHT_EMPTY) + to_chat(user, "There is no [fitting] in this light.") + return + + // make it burn hands if not wearing fire-insulated gloves + if(on) + var/prot = 0 + var/mob/living/carbon/human/H = user + + if(istype(H)) + if(H.gloves) + var/obj/item/clothing/gloves/G = H.gloves + if(G.max_heat_protection_temperature) + prot = (G.max_heat_protection_temperature > 360) + else + prot = 1 + + if(prot > 0 || (HEATRES in user.mutations)) + to_chat(user, "You remove the light [fitting]") + else if(TK in user.mutations) + to_chat(user, "You telekinetically remove the light [fitting].") + else + if(user.a_intent == INTENT_DISARM || user.a_intent == INTENT_GRAB) + to_chat(user, "You try to remove the light [fitting], but you burn your hand on it!") + + var/obj/item/organ/external/affecting = H.get_organ("[user.hand ? "l" : "r" ]_hand") + if(affecting.receive_damage( 0, 5 )) // 5 burn damage + H.UpdateDamageIcon() + H.updatehealth() + return + else + to_chat(user, "You try to remove the light [fitting], but it's too hot to touch!") + return + else + to_chat(user, "You remove the light [fitting].") + // create a light tube/bulb item and put it in the user's hand + drop_light_tube(user) + +// break the light and make sparks if was on + +/obj/machinery/light/proc/drop_light_tube(mob/user) + var/obj/item/light/L = new light_type() + L.status = status + L.rigged = rigged + L.brightness_range = brightness_range + L.brightness_power = brightness_power + L.brightness_color = brightness_color + L.materials = lightmaterials + + // light item inherits the switchcount, then zero it + L.switchcount = switchcount + switchcount = 0 + + L.update() + L.forceMove(loc) + + if(user) //puts it in our active hand + L.add_fingerprint(user) + user.put_in_active_hand(L) + + status = LIGHT_EMPTY + update() + return L + +/obj/machinery/light/attack_tk(mob/user) + if(status == LIGHT_EMPTY) + to_chat(user, "There is no [fitting] in this light.") + return + + to_chat(user, "You telekinetically remove the light [fitting].") + // create a light tube/bulb item and put it in the user's hand + var/obj/item/light/L = drop_light_tube() + L.attack_tk(user) + +/obj/machinery/light/proc/break_light_tube(skip_sound_and_sparks = 0, overloaded = 0) + if(status == LIGHT_EMPTY || status == LIGHT_BROKEN) + return + + if(!skip_sound_and_sparks) + if(status == LIGHT_OK || status == LIGHT_BURNED) + playsound(src.loc, 'sound/effects/glasshit.ogg', 75, 1) + if(on || overloaded) + do_sparks(3, 1, src) + status = LIGHT_BROKEN + update() + +/obj/machinery/light/proc/fix() + if(status == LIGHT_OK) + return + status = LIGHT_OK + on = 1 + update() + +/obj/machinery/light/tesla_act(power, explosive = FALSE) + if(explosive) + explosion(loc,0,0,0,flame_range = 5, adminlog = 0) + qdel(src) + +// timed process +// use power + +// called when area power state changes +/obj/machinery/light/power_change() + var/area/A = get_area(src) + if(A) + seton(A.lightswitch && A.power_light) + +// called when on fire + +/obj/machinery/light/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + ..() + if(prob(max(0, exposed_temperature - 673))) //0% at <400C, 100% at >500C + break_light_tube() + +// explode the light + +/obj/machinery/light/proc/explode() + var/turf/T = get_turf(src.loc) + spawn(0) + break_light_tube() // break it first to give a warning + sleep(2) + explosion(T, 0, 0, 2, 2) + sleep(1) + qdel(src) + +// the light item +// can be tube or bulb subtypes +// will fit into empty /obj/machinery/light of the corresponding type + +/obj/item/light + icon = 'icons/obj/lighting.dmi' + force = 2 + throwforce = 5 + w_class = WEIGHT_CLASS_TINY + var/status = 0 // LIGHT_OK, LIGHT_BURNED or LIGHT_BROKEN + var/base_state + var/switchcount = 0 // number of times switched + materials = list(MAT_GLASS=100) + var/rigged = 0 // true if rigged to explode + var/brightness_range = 2 //how much light it gives off + var/brightness_power = 1 + var/brightness_color = null + +/obj/item/light/ComponentInitialize() + . = ..() + AddComponent(/datum/component/caltrop, force) + +/obj/item/light/Crossed(mob/living/L) + if(istype(L) && has_gravity(loc)) + if(L.incorporeal_move || L.flying) + return + playsound(loc, 'sound/effects/glass_step.ogg', 50, TRUE) + if(status == LIGHT_BURNED || status == LIGHT_OK) + shatter() + return ..() + +/obj/item/light/tube + name = "light tube" + desc = "A replacement light tube." + icon_state = "ltube" + base_state = "ltube" + item_state = "c_tube" + brightness_range = 8 + +/obj/item/light/tube/large + w_class = WEIGHT_CLASS_SMALL + name = "large light tube" + brightness_range = 15 + brightness_power = 2 + +/obj/item/light/bulb + name = "light bulb" + desc = "A replacement light bulb." + icon_state = "lbulb" + base_state = "lbulb" + item_state = "contvapour" + brightness_range = 5 + brightness_color = "#a0a080" + +/obj/item/light/throw_impact(atom/hit_atom) + ..() + shatter() + +/obj/item/light/bulb/fire + name = "fire bulb" + desc = "A replacement fire bulb." + icon_state = "fbulb" + base_state = "fbulb" + item_state = "egg4" + brightness_range = 5 + +// update the icon state and description of the light + +/obj/item/light/proc/update() + switch(status) + if(LIGHT_OK) + icon_state = base_state + desc = "A replacement [name]." + if(LIGHT_BURNED) + icon_state = "[base_state]-burned" + desc = "A burnt-out [name]." + if(LIGHT_BROKEN) + icon_state = "[base_state]-broken" + desc = "A broken [name]." + + +/obj/item/light/New() + ..() + switch(name) + if("light tube") + brightness_range = rand(6,9) + if("light bulb") + brightness_range = rand(4,6) + update() + + +// attack bulb/tube with object +// if a syringe, can inject plasma to make it explode +/obj/item/light/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/reagent_containers/syringe)) + var/obj/item/reagent_containers/syringe/S = I + + to_chat(user, "You inject the solution into the [src].") + + if(S.reagents.has_reagent("plasma", 5) || S.reagents.has_reagent("plasma_dust", 5)) + + log_admin("LOG: [key_name(user)] injected a light with plasma, rigging it to explode.") + message_admins("LOG: [key_name_admin(user)] injected a light with plasma, rigging it to explode.") + + rigged = 1 + + S.reagents.clear_reagents() + else + return ..() + +/obj/item/light/attack(mob/living/M, mob/living/user, def_zone) + ..() + shatter() + +/obj/item/light/attack_obj(obj/O, mob/living/user) + ..() + shatter() + +/obj/item/light/proc/shatter() + if(status == LIGHT_OK || status == LIGHT_BURNED) + src.visible_message("[name] shatters.","You hear a small glass object shatter.") + status = LIGHT_BROKEN + force = 5 + sharp = 1 + playsound(src.loc, 'sound/effects/glasshit.ogg', 75, 1) + update() + +/obj/item/light/suicide_act(mob/living/carbon/human/user) + user.visible_message("[user] touches [src], burning [user.p_their()] hands off!", "You touch [src], burning your hands off!") + + for(var/oname in list("l_hand", "r_hand")) + var/obj/item/organ/external/limb = user.get_organ(oname) + if(limb) + limb.droplimb(0, DROPLIMB_BURN) + return FIRELOSS + +/obj/machinery/light/extinguish_light() + on = FALSE + visible_message("[src] flickers and falls dark.") + update(0) diff --git a/code/modules/power/port_gen.dm b/code/modules/power/port_gen.dm index ee0924578ff..54b81cb9272 100644 --- a/code/modules/power/port_gen.dm +++ b/code/modules/power/port_gen.dm @@ -1,459 +1,459 @@ -//Baseline portable generator. Has all the default handling. Not intended to be used on it's own (since it generates unlimited power). -/obj/machinery/power/port_gen - name = "Placeholder Generator" //seriously, don't use this. It can't be anchored without VV magic. - desc = "A portable generator for emergency backup power" - icon = 'icons/obj/power.dmi' - icon_state = "portgen0_0" - density = 1 - anchored = 0 - use_power = NO_POWER_USE - - var/active = 0 - var/power_gen = 5000 - var/open = 0 - var/recent_fault = 0 - var/power_output = 1 - var/base_icon = "portgen0" - -/obj/machinery/power/port_gen/proc/IsBroken() - return (stat & (BROKEN|EMPED)) - -/obj/machinery/power/port_gen/proc/HasFuel() //Placeholder for fuel check. - return 1 - -/obj/machinery/power/port_gen/proc/UseFuel() //Placeholder for fuel use. - return - -/obj/machinery/power/port_gen/proc/DropFuel() - return - -/obj/machinery/power/port_gen/proc/handleInactive() - return - -/obj/machinery/power/port_gen/update_icon() - icon_state = "[base_icon]_[active]" - -/obj/machinery/power/port_gen/process() - if(active && HasFuel() && !IsBroken() && anchored && powernet) - add_avail(power_gen * power_output) - UseFuel() - else - active = 0 - handleInactive() - update_icon() - -/obj/machinery/power/powered() - return 1 //doesn't require an external power source - -/obj/machinery/power/port_gen/attack_hand(mob/user as mob) - if(..()) - return - if(!anchored) - return - -/obj/machinery/power/port_gen/examine(mob/user) - . = ..() - if(!in_range(user, src)) - if(active) - . += "The generator is on." - else - . += "The generator is off." - -/obj/machinery/power/port_gen/emp_act(severity) - var/duration = 6000 //ten minutes - switch(severity) - if(1) - stat &= BROKEN - if(prob(75)) explode() - if(2) - if(prob(25)) stat &= BROKEN - if(prob(10)) explode() - if(3) - if(prob(10)) stat &= BROKEN - duration = 300 - - stat |= EMPED - if(duration) - spawn(duration) - stat &= ~EMPED - -/obj/machinery/power/port_gen/proc/explode() - explosion(src.loc, -1, 3, 5, -1) - qdel(src) - -#define TEMPERATURE_DIVISOR 40 -#define TEMPERATURE_CHANGE_MAX 20 - -//A power generator that runs on solid plasma sheets. -/obj/machinery/power/port_gen/pacman - name = "\improper P.A.C.M.A.N.-type Portable Generator" - desc = "A power generator that runs on solid plasma sheets. Rated for 80 kW max safe output." - - var/sheet_name = "Plasma Sheets" - var/sheet_path = /obj/item/stack/sheet/mineral/plasma - var/board_path = /obj/item/circuitboard/pacman - - /* - These values were chosen so that the generator can run safely up to 80 kW - A full 50 plasma sheet stack should last 20 minutes at power_output = 4 - temperature_gain and max_temperature are set so that the max safe power level is 4. - Setting to 5 or higher can only be done temporarily before the generator overheats. - */ - power_gen = 20000 //Watts output per power_output level - var/max_power_output = 5 //The maximum power setting without emagging. - var/max_safe_output = 4 // For UI use, maximal output that won't cause overheat. - var/time_per_sheet = 96 //fuel efficiency - how long 1 sheet lasts at power level 1 - var/max_sheets = 100 //max capacity of the hopper - var/max_temperature = 300 //max temperature before overheating increases - var/temperature_gain = 50 //how much the temperature increases per power output level, in degrees per level - - var/sheets = 0 //How many sheets of material are loaded in the generator - var/sheet_left = 0 //How much is left of the current sheet - var/temperature = 0 //The current temperature - var/overheating = 0 //if this gets high enough the generator explodes - -/obj/machinery/power/port_gen/pacman/Initialize() - ..() - if(anchored) - connect_to_network() - -/obj/machinery/power/port_gen/pacman/New() - ..() - component_parts = list() - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stock_parts/micro_laser(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - component_parts += new /obj/item/stack/cable_coil(null, 1) - component_parts += new /obj/item/stock_parts/capacitor(null) - component_parts += new board_path(null) - RefreshParts() - -/obj/machinery/power/port_gen/pacman/upgraded/New() - ..() - component_parts = list() - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - component_parts += new /obj/item/stack/cable_coil(null, 1) - component_parts += new /obj/item/stock_parts/capacitor/super(null) - component_parts += new board_path(null) - RefreshParts() - -/obj/machinery/power/port_gen/pacman/Destroy() - DropFuel() - return ..() - -/obj/machinery/power/port_gen/pacman/RefreshParts() - var/temp_rating = 0 - for(var/obj/item/stock_parts/SP in component_parts) - if(istype(SP, /obj/item/stock_parts/matter_bin)) - max_sheets = SP.rating * SP.rating * 50 - else if(istype(SP, /obj/item/stock_parts/micro_laser) || istype(SP, /obj/item/stock_parts/capacitor)) - temp_rating += SP.rating - - power_gen = round(initial(power_gen) * (max(2, temp_rating) / 2)) - -/obj/machinery/power/port_gen/pacman/examine(mob/user) - . = ..() - . += "\The [src] appears to be producing [power_gen*power_output] W." - . += "There [sheets == 1 ? "is" : "are"] [sheets] sheet\s left in the hopper." - if(IsBroken()) - . += "\The [src] seems to have broken down." - if(overheating) - . += "\The [src] is overheating!" - -/obj/machinery/power/port_gen/pacman/HasFuel() - var/needed_sheets = power_output / time_per_sheet - if(sheets >= needed_sheets - sheet_left) - return 1 - return 0 - -//Removes one stack's worth of material from the generator. -/obj/machinery/power/port_gen/pacman/DropFuel() - if(sheets) - var/obj/item/stack/sheet/mineral/S = new sheet_path(loc) - var/amount = min(sheets, S.max_amount) - S.amount = amount - sheets -= amount - -/obj/machinery/power/port_gen/pacman/UseFuel() - - //how much material are we using this iteration? - var/needed_sheets = power_output / time_per_sheet - - //HasFuel() should guarantee us that there is enough fuel left, so no need to check that - //the only thing we need to worry about is if we are going to rollover to the next sheet - if(needed_sheets > sheet_left) - sheets-- - sheet_left = (1 + sheet_left) - needed_sheets - else - sheet_left -= needed_sheets - - //calculate the "target" temperature range - //This should probably depend on the external temperature somehow, but whatever. - var/lower_limit = 56 + power_output * temperature_gain - var/upper_limit = 76 + power_output * temperature_gain - - /* - Hot or cold environments can affect the equilibrium temperature - The lower the pressure the less effect it has. I guess it cools using a radiator or something when in vacuum. - Gives traitors more opportunities to sabotage the generator or allows enterprising engineers to build additional - cooling in order to get more power out. - */ - var/datum/gas_mixture/environment = loc.return_air() - if(environment) - var/ratio = min(environment.return_pressure()/ONE_ATMOSPHERE, 1) - var/ambient = environment.temperature - T20C - lower_limit += ambient*ratio - upper_limit += ambient*ratio - - var/average = (upper_limit + lower_limit)/2 - - //calculate the temperature increase - var/bias = 0 - if(temperature < lower_limit) - bias = min(round((average - temperature)/TEMPERATURE_DIVISOR, 1), TEMPERATURE_CHANGE_MAX) - else if(temperature > upper_limit) - bias = max(round((temperature - average)/TEMPERATURE_DIVISOR, 1), -TEMPERATURE_CHANGE_MAX) - - //limit temperature increase so that it cannot raise temperature above upper_limit, - //or if it is already above upper_limit, limit the increase to 0. - var/inc_limit = max(upper_limit - temperature, 0) - var/dec_limit = min(temperature - lower_limit, 0) - temperature += between(dec_limit, rand(-7 + bias, 7 + bias), inc_limit) - - if(temperature > max_temperature) - overheat() - else if(overheating > 0) - overheating-- - -/obj/machinery/power/port_gen/pacman/handleInactive() - var/cooling_temperature = 20 - var/datum/gas_mixture/environment = loc.return_air() - if(environment) - var/ratio = min(environment.return_pressure()/ONE_ATMOSPHERE, 1) - var/ambient = environment.temperature - T20C - cooling_temperature += ambient*ratio - - if(temperature > cooling_temperature) - var/temp_loss = (temperature - cooling_temperature)/TEMPERATURE_DIVISOR - temp_loss = between(2, round(temp_loss, 1), TEMPERATURE_CHANGE_MAX) - temperature = max(temperature - temp_loss, cooling_temperature) - SSnanoui.update_uis(src) - - if(overheating) - overheating-- - -/obj/machinery/power/port_gen/pacman/proc/overheat() - overheating++ - if(overheating > 60) - explode() - -/obj/machinery/power/port_gen/pacman/explode() - //Vapourize all the plasma - //When ground up in a grinder, 1 sheet produces 20 u of plasma -- Chemistry-Machinery.dm - //1 mol = 10 u? I dunno. 1 mol of carbon is definitely bigger than a pill - /*var/plasma = (sheets+sheet_left)*20 - var/datum/gas_mixture/environment = loc.return_air() - if(environment) - environment.adjust_gas("plasma", plasma/10, temperature + T0C)*/ - - sheets = 0 - sheet_left = 0 - ..() - -/obj/machinery/power/port_gen/pacman/emag_act(var/remaining_charges, var/mob/user) - if(active && prob(25)) - explode() //if they're foolish enough to emag while it's running - - if(!emagged) - emagged = 1 - return 1 - -/obj/machinery/power/port_gen/pacman/attackby(var/obj/item/O as obj, var/mob/user as mob) - if(istype(O, sheet_path)) - var/obj/item/stack/addstack = O - var/amount = min((max_sheets - sheets), addstack.amount) - if(amount < 1) - to_chat(user, "The [src.name] is full!") - return - to_chat(user, "You add [amount] sheet\s to the [src.name].") - sheets += amount - addstack.use(amount) - SSnanoui.update_uis(src) - return - else if(!active) - if(istype(O, /obj/item/wrench)) - - if(!anchored) - connect_to_network() - to_chat(user, "You secure the generator to the floor.") - else - disconnect_from_network() - to_chat(user, "You unsecure the generator from the floor.") - - playsound(src.loc, O.usesound, 50, 1) - anchored = !anchored - - else if(istype(O, /obj/item/screwdriver)) - panel_open = !panel_open - playsound(src.loc, O.usesound, 50, 1) - if(panel_open) - to_chat(user, "You open the access panel.") - else - to_chat(user, "You close the access panel.") - else if(istype(O, /obj/item/storage/part_replacer) && panel_open) - exchange_parts(user, O) - return - else if(istype(O, /obj/item/crowbar) && panel_open) - default_deconstruction_crowbar(O) - else - return ..() - -/obj/machinery/power/port_gen/pacman/attack_hand(mob/user as mob) - ..() - if(!anchored) - return - ui_interact(user) - -/obj/machinery/power/port_gen/pacman/attack_ai(var/mob/user as mob) - src.add_hiddenprint(user) - return src.attack_hand(user) - -/obj/machinery/power/port_gen/pacman/attack_ghost(var/mob/user) - return src.attack_hand(user) - -/obj/machinery/power/port_gen/pacman/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - if(IsBroken()) - return - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "pacman.tmpl", src.name, 500, 560) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/power/port_gen/pacman/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - - data["active"] = active - if(istype(user, /mob/living/silicon/ai)) - data["is_ai"] = 1 - else if(istype(user, /mob/living/silicon/robot) && !Adjacent(user)) - data["is_ai"] = 1 - else - data["is_ai"] = 0 - - data["output_set"] = power_output - data["output_max"] = max_power_output - data["output_safe"] = max_safe_output - data["output_watts"] = power_output * power_gen - data["temperature_current"] = src.temperature - data["temperature_max"] = src.max_temperature - data["temperature_overheat"] = overheating - // 1 sheet = 1000cm3? - data["fuel_stored"] = round((sheets * 1000) + (sheet_left * 1000)) - data["fuel_capacity"] = round(max_sheets * 1000, 0.1) - data["fuel_usage"] = active ? round((power_output / time_per_sheet) * 1000) : 0 - data["fuel_type"] = sheet_name - - return data - -/obj/machinery/power/port_gen/pacman/Topic(href, href_list) - if(..()) - return - - src.add_fingerprint(usr) - if(href_list["action"]) - if(href_list["action"] == "enable") - if(!active && HasFuel() && !IsBroken()) - active = 1 - update_icon() - if(href_list["action"] == "disable") - if(active) - active = 0 - update_icon() - if(href_list["action"] == "eject") - if(!active) - DropFuel() - if(href_list["action"] == "lower_power") - if(power_output > 1) - power_output-- - if(href_list["action"] == "higher_power") - if(power_output < max_power_output || (emagged && power_output < round(max_power_output*2.5))) - power_output++ - - SSnanoui.update_uis(src) - -/obj/machinery/power/port_gen/pacman/super - name = "S.U.P.E.R.P.A.C.M.A.N.-type Portable Generator" - desc = "A power generator that utilizes uranium sheets as fuel. Can run for much longer than the standard PACMAN type generators. Rated for 80 kW max safe output." - icon_state = "portgen1_0" - base_icon = "portgen1" - sheet_path = /obj/item/stack/sheet/mineral/uranium - sheet_name = "Uranium Sheets" - time_per_sheet = 576 //same power output, but a 50 sheet stack will last 2 hours at max safe power - board_path = /obj/item/circuitboard/pacman/super - -/obj/machinery/power/port_gen/pacman/super/upgraded/New() - ..() - component_parts = list() - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - component_parts += new /obj/item/stack/cable_coil(null, 1) - component_parts += new /obj/item/stock_parts/capacitor/super(null) - component_parts += new board_path(null) - RefreshParts() - -/obj/machinery/power/port_gen/pacman/super/UseFuel() - //produces a tiny amount of radiation when in use - if(prob(2*power_output)) - for(var/mob/living/L in range(src, 5)) - L.apply_effect(1, IRRADIATE) //should amount to ~5 rads per minute at max safe power - ..() - -/obj/machinery/power/port_gen/pacman/super/explode() - //a nice burst of radiation - var/rads = 50 + (sheets + sheet_left)*1.5 - for(var/mob/living/L in range(src, 10)) - //should really fall with the square of the distance, but that makes the rads value drop too fast - //I dunno, maybe physics works different when you live in 2D -- SM radiation also works like this, apparently - L.apply_effect(max(20, round(rads/get_dist(L,src))), IRRADIATE) - - explosion(src.loc, 3, 3, 5, 3) - qdel(src) - -/obj/machinery/power/port_gen/pacman/mrs - name = "M.R.S.P.A.C.M.A.N.-type Portable Generator" - desc = "An advanced power generator that runs on diamonds. Rated for 200 kW maximum safe output!" - icon_state = "portgen2_0" - base_icon = "portgen2" - sheet_path = /obj/item/stack/sheet/mineral/diamond - sheet_name = "Diamond Sheets" - - //I don't think tritium has any other use, so we might as well make this rewarding for players - //max safe power output (power level = 8) is 200 kW and lasts for 1 hour - 3 or 4 of these could power the station - power_gen = 25000 //watts - max_power_output = 10 - max_safe_output = 8 - time_per_sheet = 576 - max_temperature = 800 - temperature_gain = 90 - board_path = /obj/item/circuitboard/pacman/mrs - -/obj/machinery/power/port_gen/pacman/mrs/upgraded/New() - ..() - component_parts = list() - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) - component_parts += new /obj/item/stack/cable_coil(null, 1) - component_parts += new /obj/item/stack/cable_coil(null, 1) - component_parts += new /obj/item/stock_parts/capacitor/super(null) - component_parts += new board_path(null) - RefreshParts() - -/obj/machinery/power/port_gen/pacman/mrs/explode() - //no special effects, but the explosion is pretty big (same as a supermatter shard). - explosion(src.loc, 3, 6, 12, 16, 1) - qdel(src) +//Baseline portable generator. Has all the default handling. Not intended to be used on it's own (since it generates unlimited power). +/obj/machinery/power/port_gen + name = "Placeholder Generator" //seriously, don't use this. It can't be anchored without VV magic. + desc = "A portable generator for emergency backup power" + icon = 'icons/obj/power.dmi' + icon_state = "portgen0_0" + density = 1 + anchored = 0 + use_power = NO_POWER_USE + + var/active = 0 + var/power_gen = 5000 + var/open = 0 + var/recent_fault = 0 + var/power_output = 1 + var/base_icon = "portgen0" + +/obj/machinery/power/port_gen/proc/IsBroken() + return (stat & (BROKEN|EMPED)) + +/obj/machinery/power/port_gen/proc/HasFuel() //Placeholder for fuel check. + return 1 + +/obj/machinery/power/port_gen/proc/UseFuel() //Placeholder for fuel use. + return + +/obj/machinery/power/port_gen/proc/DropFuel() + return + +/obj/machinery/power/port_gen/proc/handleInactive() + return + +/obj/machinery/power/port_gen/update_icon() + icon_state = "[base_icon]_[active]" + +/obj/machinery/power/port_gen/process() + if(active && HasFuel() && !IsBroken() && anchored && powernet) + add_avail(power_gen * power_output) + UseFuel() + else + active = 0 + handleInactive() + update_icon() + +/obj/machinery/power/powered() + return 1 //doesn't require an external power source + +/obj/machinery/power/port_gen/attack_hand(mob/user as mob) + if(..()) + return + if(!anchored) + return + +/obj/machinery/power/port_gen/examine(mob/user) + . = ..() + if(!in_range(user, src)) + if(active) + . += "The generator is on." + else + . += "The generator is off." + +/obj/machinery/power/port_gen/emp_act(severity) + var/duration = 6000 //ten minutes + switch(severity) + if(1) + stat &= BROKEN + if(prob(75)) explode() + if(2) + if(prob(25)) stat &= BROKEN + if(prob(10)) explode() + if(3) + if(prob(10)) stat &= BROKEN + duration = 300 + + stat |= EMPED + if(duration) + spawn(duration) + stat &= ~EMPED + +/obj/machinery/power/port_gen/proc/explode() + explosion(src.loc, -1, 3, 5, -1) + qdel(src) + +#define TEMPERATURE_DIVISOR 40 +#define TEMPERATURE_CHANGE_MAX 20 + +//A power generator that runs on solid plasma sheets. +/obj/machinery/power/port_gen/pacman + name = "\improper P.A.C.M.A.N.-type Portable Generator" + desc = "A power generator that runs on solid plasma sheets. Rated for 80 kW max safe output." + + var/sheet_name = "Plasma Sheets" + var/sheet_path = /obj/item/stack/sheet/mineral/plasma + var/board_path = /obj/item/circuitboard/pacman + + /* + These values were chosen so that the generator can run safely up to 80 kW + A full 50 plasma sheet stack should last 20 minutes at power_output = 4 + temperature_gain and max_temperature are set so that the max safe power level is 4. + Setting to 5 or higher can only be done temporarily before the generator overheats. + */ + power_gen = 20000 //Watts output per power_output level + var/max_power_output = 5 //The maximum power setting without emagging. + var/max_safe_output = 4 // For UI use, maximal output that won't cause overheat. + var/time_per_sheet = 96 //fuel efficiency - how long 1 sheet lasts at power level 1 + var/max_sheets = 100 //max capacity of the hopper + var/max_temperature = 300 //max temperature before overheating increases + var/temperature_gain = 50 //how much the temperature increases per power output level, in degrees per level + + var/sheets = 0 //How many sheets of material are loaded in the generator + var/sheet_left = 0 //How much is left of the current sheet + var/temperature = 0 //The current temperature + var/overheating = 0 //if this gets high enough the generator explodes + +/obj/machinery/power/port_gen/pacman/Initialize() + ..() + if(anchored) + connect_to_network() + +/obj/machinery/power/port_gen/pacman/New() + ..() + component_parts = list() + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/micro_laser(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + component_parts += new /obj/item/stack/cable_coil(null, 1) + component_parts += new /obj/item/stock_parts/capacitor(null) + component_parts += new board_path(null) + RefreshParts() + +/obj/machinery/power/port_gen/pacman/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + component_parts += new /obj/item/stack/cable_coil(null, 1) + component_parts += new /obj/item/stock_parts/capacitor/super(null) + component_parts += new board_path(null) + RefreshParts() + +/obj/machinery/power/port_gen/pacman/Destroy() + DropFuel() + return ..() + +/obj/machinery/power/port_gen/pacman/RefreshParts() + var/temp_rating = 0 + for(var/obj/item/stock_parts/SP in component_parts) + if(istype(SP, /obj/item/stock_parts/matter_bin)) + max_sheets = SP.rating * SP.rating * 50 + else if(istype(SP, /obj/item/stock_parts/micro_laser) || istype(SP, /obj/item/stock_parts/capacitor)) + temp_rating += SP.rating + + power_gen = round(initial(power_gen) * (max(2, temp_rating) / 2)) + +/obj/machinery/power/port_gen/pacman/examine(mob/user) + . = ..() + . += "\The [src] appears to be producing [power_gen*power_output] W." + . += "There [sheets == 1 ? "is" : "are"] [sheets] sheet\s left in the hopper." + if(IsBroken()) + . += "\The [src] seems to have broken down." + if(overheating) + . += "\The [src] is overheating!" + +/obj/machinery/power/port_gen/pacman/HasFuel() + var/needed_sheets = power_output / time_per_sheet + if(sheets >= needed_sheets - sheet_left) + return 1 + return 0 + +//Removes one stack's worth of material from the generator. +/obj/machinery/power/port_gen/pacman/DropFuel() + if(sheets) + var/obj/item/stack/sheet/mineral/S = new sheet_path(loc) + var/amount = min(sheets, S.max_amount) + S.amount = amount + sheets -= amount + +/obj/machinery/power/port_gen/pacman/UseFuel() + + //how much material are we using this iteration? + var/needed_sheets = power_output / time_per_sheet + + //HasFuel() should guarantee us that there is enough fuel left, so no need to check that + //the only thing we need to worry about is if we are going to rollover to the next sheet + if(needed_sheets > sheet_left) + sheets-- + sheet_left = (1 + sheet_left) - needed_sheets + else + sheet_left -= needed_sheets + + //calculate the "target" temperature range + //This should probably depend on the external temperature somehow, but whatever. + var/lower_limit = 56 + power_output * temperature_gain + var/upper_limit = 76 + power_output * temperature_gain + + /* + Hot or cold environments can affect the equilibrium temperature + The lower the pressure the less effect it has. I guess it cools using a radiator or something when in vacuum. + Gives traitors more opportunities to sabotage the generator or allows enterprising engineers to build additional + cooling in order to get more power out. + */ + var/datum/gas_mixture/environment = loc.return_air() + if(environment) + var/ratio = min(environment.return_pressure()/ONE_ATMOSPHERE, 1) + var/ambient = environment.temperature - T20C + lower_limit += ambient*ratio + upper_limit += ambient*ratio + + var/average = (upper_limit + lower_limit)/2 + + //calculate the temperature increase + var/bias = 0 + if(temperature < lower_limit) + bias = min(round((average - temperature)/TEMPERATURE_DIVISOR, 1), TEMPERATURE_CHANGE_MAX) + else if(temperature > upper_limit) + bias = max(round((temperature - average)/TEMPERATURE_DIVISOR, 1), -TEMPERATURE_CHANGE_MAX) + + //limit temperature increase so that it cannot raise temperature above upper_limit, + //or if it is already above upper_limit, limit the increase to 0. + var/inc_limit = max(upper_limit - temperature, 0) + var/dec_limit = min(temperature - lower_limit, 0) + temperature += between(dec_limit, rand(-7 + bias, 7 + bias), inc_limit) + + if(temperature > max_temperature) + overheat() + else if(overheating > 0) + overheating-- + +/obj/machinery/power/port_gen/pacman/handleInactive() + var/cooling_temperature = 20 + var/datum/gas_mixture/environment = loc.return_air() + if(environment) + var/ratio = min(environment.return_pressure()/ONE_ATMOSPHERE, 1) + var/ambient = environment.temperature - T20C + cooling_temperature += ambient*ratio + + if(temperature > cooling_temperature) + var/temp_loss = (temperature - cooling_temperature)/TEMPERATURE_DIVISOR + temp_loss = between(2, round(temp_loss, 1), TEMPERATURE_CHANGE_MAX) + temperature = max(temperature - temp_loss, cooling_temperature) + SSnanoui.update_uis(src) + + if(overheating) + overheating-- + +/obj/machinery/power/port_gen/pacman/proc/overheat() + overheating++ + if(overheating > 60) + explode() + +/obj/machinery/power/port_gen/pacman/explode() + //Vapourize all the plasma + //When ground up in a grinder, 1 sheet produces 20 u of plasma -- Chemistry-Machinery.dm + //1 mol = 10 u? I dunno. 1 mol of carbon is definitely bigger than a pill + /*var/plasma = (sheets+sheet_left)*20 + var/datum/gas_mixture/environment = loc.return_air() + if(environment) + environment.adjust_gas("plasma", plasma/10, temperature + T0C)*/ + + sheets = 0 + sheet_left = 0 + ..() + +/obj/machinery/power/port_gen/pacman/emag_act(var/remaining_charges, var/mob/user) + if(active && prob(25)) + explode() //if they're foolish enough to emag while it's running + + if(!emagged) + emagged = 1 + return 1 + +/obj/machinery/power/port_gen/pacman/attackby(var/obj/item/O as obj, var/mob/user as mob) + if(istype(O, sheet_path)) + var/obj/item/stack/addstack = O + var/amount = min((max_sheets - sheets), addstack.amount) + if(amount < 1) + to_chat(user, "The [src.name] is full!") + return + to_chat(user, "You add [amount] sheet\s to the [src.name].") + sheets += amount + addstack.use(amount) + SSnanoui.update_uis(src) + return + else if(!active) + if(istype(O, /obj/item/wrench)) + + if(!anchored) + connect_to_network() + to_chat(user, "You secure the generator to the floor.") + else + disconnect_from_network() + to_chat(user, "You unsecure the generator from the floor.") + + playsound(src.loc, O.usesound, 50, 1) + anchored = !anchored + + else if(istype(O, /obj/item/screwdriver)) + panel_open = !panel_open + playsound(src.loc, O.usesound, 50, 1) + if(panel_open) + to_chat(user, "You open the access panel.") + else + to_chat(user, "You close the access panel.") + else if(istype(O, /obj/item/storage/part_replacer) && panel_open) + exchange_parts(user, O) + return + else if(istype(O, /obj/item/crowbar) && panel_open) + default_deconstruction_crowbar(user, O) + else + return ..() + +/obj/machinery/power/port_gen/pacman/attack_hand(mob/user as mob) + ..() + if(!anchored) + return + ui_interact(user) + +/obj/machinery/power/port_gen/pacman/attack_ai(var/mob/user as mob) + src.add_hiddenprint(user) + return src.attack_hand(user) + +/obj/machinery/power/port_gen/pacman/attack_ghost(var/mob/user) + return src.attack_hand(user) + +/obj/machinery/power/port_gen/pacman/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + if(IsBroken()) + return + + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "pacman.tmpl", src.name, 500, 560) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/power/port_gen/pacman/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) + var/data[0] + + data["active"] = active + if(istype(user, /mob/living/silicon/ai)) + data["is_ai"] = 1 + else if(istype(user, /mob/living/silicon/robot) && !Adjacent(user)) + data["is_ai"] = 1 + else + data["is_ai"] = 0 + + data["output_set"] = power_output + data["output_max"] = max_power_output + data["output_safe"] = max_safe_output + data["output_watts"] = power_output * power_gen + data["temperature_current"] = src.temperature + data["temperature_max"] = src.max_temperature + data["temperature_overheat"] = overheating + // 1 sheet = 1000cm3? + data["fuel_stored"] = round((sheets * 1000) + (sheet_left * 1000)) + data["fuel_capacity"] = round(max_sheets * 1000, 0.1) + data["fuel_usage"] = active ? round((power_output / time_per_sheet) * 1000) : 0 + data["fuel_type"] = sheet_name + + return data + +/obj/machinery/power/port_gen/pacman/Topic(href, href_list) + if(..()) + return + + src.add_fingerprint(usr) + if(href_list["action"]) + if(href_list["action"] == "enable") + if(!active && HasFuel() && !IsBroken()) + active = 1 + update_icon() + if(href_list["action"] == "disable") + if(active) + active = 0 + update_icon() + if(href_list["action"] == "eject") + if(!active) + DropFuel() + if(href_list["action"] == "lower_power") + if(power_output > 1) + power_output-- + if(href_list["action"] == "higher_power") + if(power_output < max_power_output || (emagged && power_output < round(max_power_output*2.5))) + power_output++ + + SSnanoui.update_uis(src) + +/obj/machinery/power/port_gen/pacman/super + name = "S.U.P.E.R.P.A.C.M.A.N.-type Portable Generator" + desc = "A power generator that utilizes uranium sheets as fuel. Can run for much longer than the standard PACMAN type generators. Rated for 80 kW max safe output." + icon_state = "portgen1_0" + base_icon = "portgen1" + sheet_path = /obj/item/stack/sheet/mineral/uranium + sheet_name = "Uranium Sheets" + time_per_sheet = 576 //same power output, but a 50 sheet stack will last 2 hours at max safe power + board_path = /obj/item/circuitboard/pacman/super + +/obj/machinery/power/port_gen/pacman/super/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + component_parts += new /obj/item/stack/cable_coil(null, 1) + component_parts += new /obj/item/stock_parts/capacitor/super(null) + component_parts += new board_path(null) + RefreshParts() + +/obj/machinery/power/port_gen/pacman/super/UseFuel() + //produces a tiny amount of radiation when in use + if(prob(2*power_output)) + for(var/mob/living/L in range(src, 5)) + L.apply_effect(1, IRRADIATE) //should amount to ~5 rads per minute at max safe power + ..() + +/obj/machinery/power/port_gen/pacman/super/explode() + //a nice burst of radiation + var/rads = 50 + (sheets + sheet_left)*1.5 + for(var/mob/living/L in range(src, 10)) + //should really fall with the square of the distance, but that makes the rads value drop too fast + //I dunno, maybe physics works different when you live in 2D -- SM radiation also works like this, apparently + L.apply_effect(max(20, round(rads/get_dist(L,src))), IRRADIATE) + + explosion(src.loc, 3, 3, 5, 3) + qdel(src) + +/obj/machinery/power/port_gen/pacman/mrs + name = "M.R.S.P.A.C.M.A.N.-type Portable Generator" + desc = "An advanced power generator that runs on diamonds. Rated for 200 kW maximum safe output!" + icon_state = "portgen2_0" + base_icon = "portgen2" + sheet_path = /obj/item/stack/sheet/mineral/diamond + sheet_name = "Diamond Sheets" + + //I don't think tritium has any other use, so we might as well make this rewarding for players + //max safe power output (power level = 8) is 200 kW and lasts for 1 hour - 3 or 4 of these could power the station + power_gen = 25000 //watts + max_power_output = 10 + max_safe_output = 8 + time_per_sheet = 576 + max_temperature = 800 + temperature_gain = 90 + board_path = /obj/item/circuitboard/pacman/mrs + +/obj/machinery/power/port_gen/pacman/mrs/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) + component_parts += new /obj/item/stack/cable_coil(null, 1) + component_parts += new /obj/item/stack/cable_coil(null, 1) + component_parts += new /obj/item/stock_parts/capacitor/super(null) + component_parts += new board_path(null) + RefreshParts() + +/obj/machinery/power/port_gen/pacman/mrs/explode() + //no special effects, but the explosion is pretty big (same as a supermatter shard). + explosion(src.loc, 3, 6, 12, 16, 1) + qdel(src) diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index 33741d1475b..7b43291b5a2 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -1,380 +1,380 @@ -////////////////////////////// -// POWER MACHINERY BASE CLASS -////////////////////////////// - -///////////////////////////// -// Definitions -///////////////////////////// - -/obj/machinery/power - name = null - icon = 'icons/obj/power.dmi' - anchored = TRUE - on_blueprints = TRUE - var/datum/powernet/powernet = null - use_power = NO_POWER_USE - idle_power_usage = 0 - active_power_usage = 0 - -/obj/machinery/power/Destroy() - disconnect_from_network() - return ..() - -/////////////////////////////// -// General procedures -////////////////////////////// - -// common helper procs for all power machines -// All power generation handled in add_avail() -// Machines should use add_load(), surplus(), avail() -// Non-machines should use add_delayedload(), delayed_surplus(), newavail() - -/obj/machinery/power/proc/add_avail(amount) - if(powernet) - powernet.newavail += amount - return TRUE - else - return FALSE - -/obj/machinery/power/proc/add_load(amount) - if(powernet) - powernet.load += amount - -/obj/machinery/power/proc/surplus() - if(powernet) - return Clamp(powernet.avail-powernet.load, 0, powernet.avail) - else - return 0 - -/obj/machinery/power/proc/avail() - if(powernet) - return powernet.avail - else - return 0 - -/obj/machinery/power/proc/add_delayedload(amount) - if(powernet) - powernet.delayedload += amount - -/obj/machinery/power/proc/delayed_surplus() - if(powernet) - return Clamp(powernet.newavail - powernet.delayedload, 0, powernet.newavail) - else - return 0 - -/obj/machinery/power/proc/newavail() - if(powernet) - return powernet.newavail - else - return 0 - -/obj/machinery/power/proc/disconnect_terminal() // machines without a terminal will just return, no harm no fowl. - return - -// returns true if the area has power on given channel (or doesn't require power). -// defaults to power_channel -/obj/machinery/proc/powered(var/chan = -1) // defaults to power_channel - if(!loc) - return FALSE - if(!use_power) - return TRUE - - var/area/A = get_area(src) // make sure it's in an area - if(!A) - return FALSE // if not, then not powered - if(chan == -1) - chan = power_channel - return A.powered(chan) // return power status of the area - -// increment the power usage stats for an area -/obj/machinery/proc/use_power(amount, chan = -1) // defaults to power_channel - var/area/A = get_area(src) // make sure it's in an area - if(!A) - return - if(chan == -1) - chan = power_channel - A.use_power(amount, chan) - -/obj/machinery/proc/addStaticPower(value, powerchannel) - var/area/A = get_area(src) - if(!A) - return - A.addStaticPower(value, powerchannel) - -/obj/machinery/proc/removeStaticPower(value, powerchannel) - addStaticPower(-value, powerchannel) - -/obj/machinery/proc/power_change() // called whenever the power settings of the containing area change - // by default, check equipment channel & set flag - // can override if needed - if(powered(power_channel)) - stat &= ~NOPOWER - else - - stat |= NOPOWER - return - -// connect the machine to a powernet if a node cable is present on the turf -/obj/machinery/power/proc/connect_to_network() - var/turf/T = src.loc - if(!T || !istype(T)) - return FALSE - - var/obj/structure/cable/C = T.get_cable_node() //check if we have a node cable on the machine turf, the first found is picked - if(!C || !C.powernet) - return FALSE - - C.powernet.add_machine(src) - return TRUE - -// remove and disconnect the machine from its current powernet -/obj/machinery/power/proc/disconnect_from_network() - if(!powernet) - return FALSE - powernet.remove_machine(src) - return TRUE - -// attach a wire to a power machine - leads from the turf you are standing on -//almost never called, overwritten by all power machines but terminal and generator -/obj/machinery/power/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/coil = I - var/turf/T = user.loc - if(T.intact || !isfloorturf(T)) - return - if(get_dist(src, user) > 1) - return - coil.place_turf(T, user) - else - return ..() - - -/////////////////////////////////////////// -// Powernet handling helpers -////////////////////////////////////////// - -//returns all the cables WITHOUT a powernet in neighbors turfs, -//pointing towards the turf the machine is located at -/obj/machinery/power/proc/get_connections() - - . = list() - - var/cdir - var/turf/T - - for(var/card in cardinal) - T = get_step(loc,card) - cdir = get_dir(T,loc) - - for(var/obj/structure/cable/C in T) - if(C.powernet) - continue - if(C.d1 == cdir || C.d2 == cdir) - . += C - return . - -//returns all the cables in neighbors turfs, -//pointing towards the turf the machine is located at -/obj/machinery/power/proc/get_marked_connections() - - . = list() - - var/cdir - var/turf/T - - for(var/card in cardinal) - T = get_step(loc,card) - cdir = get_dir(T,loc) - - for(var/obj/structure/cable/C in T) - if(C.d1 == cdir || C.d2 == cdir) - . += C - return . - -//returns all the NODES (O-X) cables WITHOUT a powernet in the turf the machine is located at -/obj/machinery/power/proc/get_indirect_connections() - . = list() - for(var/obj/structure/cable/C in loc) - if(C.powernet) - continue - if(C.d1 == 0) // the cable is a node cable - . += C - return . - -/////////////////////////////////////////// -// GLOBAL PROCS for powernets handling -////////////////////////////////////////// - - -// returns a list of all power-related objects (nodes, cable, junctions) in turf, -// excluding source, that match the direction d -// if unmarked==1, only return those with no powernet -/proc/power_list(turf/T, source, d, unmarked=0, cable_only = 0) - . = list() - - for(var/AM in T) - if(AM == source) - continue //we don't want to return source - - if(!cable_only && istype(AM, /obj/machinery/power)) - var/obj/machinery/power/P = AM - if(P.powernet == 0) - continue // exclude APCs which have powernet=0 - - if(!unmarked || !P.powernet) //if unmarked=1 we only return things with no powernet - if(d == 0) - . += P - - else if(istype(AM, /obj/structure/cable)) - var/obj/structure/cable/C = AM - - if(!unmarked || !C.powernet) - if(C.d1 == d || C.d2 == d) - . += C - return . - -//remove the old powernet and replace it with a new one throughout the network. -/proc/propagate_network(obj/O, datum/powernet/PN) - var/list/worklist = list() - var/list/found_machines = list() - var/index = 1 - var/obj/P = null - - worklist+=O //start propagating from the passed object - - while(index<=worklist.len) //until we've exhausted all power objects - P = worklist[index] //get the next power object found - index++ - - if(istype(P, /obj/structure/cable)) - var/obj/structure/cable/C = P - if(C.powernet != PN) //add it to the powernet, if it isn't already there - PN.add_cable(C) - worklist |= C.get_connections() //get adjacents power objects, with or without a powernet - - else if(P.anchored && istype(P, /obj/machinery/power)) - var/obj/machinery/power/M = P - found_machines |= M //we wait until the powernet is fully propagates to connect the machines - - else - continue - - //now that the powernet is set, connect found machines to it - for(var/obj/machinery/power/PM in found_machines) - if(!PM.connect_to_network()) //couldn't find a node on its turf... - PM.disconnect_from_network() //... so disconnect if already on a powernet - - -//Merge two powernets, the bigger (in cable length term) absorbing the other -/proc/merge_powernets(datum/powernet/net1, datum/powernet/net2) - if(!net1 || !net2) //if one of the powernet doesn't exist, return - return - - if(net1 == net2) //don't merge same powernets - return - - //We assume net1 is larger. If net2 is in fact larger we are just going to make them switch places to reduce on code. - if(net1.cables.len < net2.cables.len) //net2 is larger than net1. Let's switch them around - var/temp = net1 - net1 = net2 - net2 = temp - - //merge net2 into net1 - for(var/obj/structure/cable/Cable in net2.cables) //merge cables - net1.add_cable(Cable) - - for(var/obj/machinery/power/Node in net2.nodes) //merge power machines - if(!Node.connect_to_network()) - Node.disconnect_from_network() //if somehow we can't connect the machine to the new powernet, disconnect it from the old nonetheless - - return net1 - -//Determines how strong could be shock, deals damage to mob, uses power. -//M is a mob who touched wire/whatever -//power_source is a source of electricity, can be powercell, area, apc, cable, powernet or null -//source is an object caused electrocuting (airlock, grille, etc) -//No animations will be performed by this proc. -/proc/electrocute_mob(mob/living/M, power_source, obj/source, siemens_coeff = 1, dist_check = FALSE) - if(!M || ismecha(M.loc)) - return FALSE //feckin mechs are dumb - if(dist_check) - if(!in_range(source, M)) - return FALSE - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.gloves) - var/obj/item/clothing/gloves/G = H.gloves - if(G.siemens_coefficient == 0) - return FALSE //to avoid spamming with insulated glvoes on - - var/area/source_area - if(istype(power_source, /area)) - source_area = power_source - power_source = source_area.get_apc() - if(istype(power_source, /obj/structure/cable)) - var/obj/structure/cable/Cable = power_source - power_source = Cable.powernet - - var/datum/powernet/PN - var/obj/item/stock_parts/cell/cell - - if(istype(power_source, /datum/powernet)) - PN = power_source - else if(istype(power_source, /obj/item/stock_parts/cell)) - cell = power_source - else if(istype(power_source, /obj/machinery/power/apc)) - var/obj/machinery/power/apc/apc = power_source - cell = apc.cell - if(apc.terminal) - PN = apc.terminal.powernet - else if(!power_source) - return 0 - else - log_admin("ERROR: /proc/electrocute_mob([M], [power_source], [source]): wrong power_source") - return 0 - if(!cell && !PN) - return 0 - var/PN_damage = 0 - var/cell_damage = 0 - if(PN) - PN_damage = PN.get_electrocute_damage() - if(cell) - cell_damage = cell.get_electrocute_damage() - var/shock_damage = 0 - if(PN_damage >= cell_damage) - power_source = PN - shock_damage = PN_damage - else - power_source = cell - shock_damage = cell_damage - var/drained_hp = M.electrocute_act(shock_damage, source, siemens_coeff) //zzzzzzap! - var/drained_energy = drained_hp*20 - - if(source_area) - source_area.use_power(drained_energy/GLOB.CELLRATE) - else if(istype(power_source, /datum/powernet)) - var/drained_power = drained_energy/GLOB.CELLRATE //convert from "joules" to "watts" - PN.delayedload += (min(drained_power, max(PN.newavail - PN.delayedload, 0))) - else if (istype(power_source, /obj/item/stock_parts/cell)) - cell.use(drained_energy) - return drained_energy - -//////////////////////////////////////////////// -// Misc. -/////////////////////////////////////////////// - - -// return a knot cable (O-X) if one is present in the turf -// null if there's none -/turf/proc/get_cable_node() - if(!can_have_cabling()) - return null - for(var/obj/structure/cable/C in src) - if(C.d1 == 0) - return C - return null - -/area/proc/get_apc() - for(var/obj/machinery/power/apc/APC in GLOB.apcs) - if(APC.area == src) - return APC \ No newline at end of file +////////////////////////////// +// POWER MACHINERY BASE CLASS +////////////////////////////// + +///////////////////////////// +// Definitions +///////////////////////////// + +/obj/machinery/power + name = null + icon = 'icons/obj/power.dmi' + anchored = TRUE + on_blueprints = TRUE + var/datum/powernet/powernet = null + use_power = NO_POWER_USE + idle_power_usage = 0 + active_power_usage = 0 + +/obj/machinery/power/Destroy() + disconnect_from_network() + return ..() + +/////////////////////////////// +// General procedures +////////////////////////////// + +// common helper procs for all power machines +// All power generation handled in add_avail() +// Machines should use add_load(), surplus(), avail() +// Non-machines should use add_delayedload(), delayed_surplus(), newavail() + +/obj/machinery/power/proc/add_avail(amount) + if(powernet) + powernet.newavail += amount + return TRUE + else + return FALSE + +/obj/machinery/power/proc/add_load(amount) + if(powernet) + powernet.load += amount + +/obj/machinery/power/proc/surplus() + if(powernet) + return Clamp(powernet.avail-powernet.load, 0, powernet.avail) + else + return 0 + +/obj/machinery/power/proc/avail() + if(powernet) + return powernet.avail + else + return 0 + +/obj/machinery/power/proc/add_delayedload(amount) + if(powernet) + powernet.delayedload += amount + +/obj/machinery/power/proc/delayed_surplus() + if(powernet) + return Clamp(powernet.newavail - powernet.delayedload, 0, powernet.newavail) + else + return 0 + +/obj/machinery/power/proc/newavail() + if(powernet) + return powernet.newavail + else + return 0 + +/obj/machinery/power/proc/disconnect_terminal() // machines without a terminal will just return, no harm no fowl. + return + +// returns true if the area has power on given channel (or doesn't require power). +// defaults to power_channel +/obj/machinery/proc/powered(var/chan = -1) // defaults to power_channel + if(!loc) + return FALSE + if(!use_power) + return TRUE + + var/area/A = get_area(src) // make sure it's in an area + if(!A) + return FALSE // if not, then not powered + if(chan == -1) + chan = power_channel + return A.powered(chan) // return power status of the area + +// increment the power usage stats for an area +/obj/machinery/proc/use_power(amount, chan = -1) // defaults to power_channel + var/area/A = get_area(src) // make sure it's in an area + if(!A) + return + if(chan == -1) + chan = power_channel + A.use_power(amount, chan) + +/obj/machinery/proc/addStaticPower(value, powerchannel) + var/area/A = get_area(src) + if(!A) + return + A.addStaticPower(value, powerchannel) + +/obj/machinery/proc/removeStaticPower(value, powerchannel) + addStaticPower(-value, powerchannel) + +/obj/machinery/proc/power_change() // called whenever the power settings of the containing area change + // by default, check equipment channel & set flag + // can override if needed + if(powered(power_channel)) + stat &= ~NOPOWER + else + + stat |= NOPOWER + return + +// connect the machine to a powernet if a node cable is present on the turf +/obj/machinery/power/proc/connect_to_network() + var/turf/T = src.loc + if(!T || !istype(T)) + return FALSE + + var/obj/structure/cable/C = T.get_cable_node() //check if we have a node cable on the machine turf, the first found is picked + if(!C || !C.powernet) + return FALSE + + C.powernet.add_machine(src) + return TRUE + +// remove and disconnect the machine from its current powernet +/obj/machinery/power/proc/disconnect_from_network() + if(!powernet) + return FALSE + powernet.remove_machine(src) + return TRUE + +// attach a wire to a power machine - leads from the turf you are standing on +//almost never called, overwritten by all power machines but terminal and generator +/obj/machinery/power/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/stack/cable_coil)) + var/obj/item/stack/cable_coil/coil = I + var/turf/T = user.loc + if(T.intact || !isfloorturf(T)) + return + if(get_dist(src, user) > 1) + return + coil.place_turf(T, user) + else + return ..() + + +/////////////////////////////////////////// +// Powernet handling helpers +////////////////////////////////////////// + +//returns all the cables WITHOUT a powernet in neighbors turfs, +//pointing towards the turf the machine is located at +/obj/machinery/power/proc/get_connections() + + . = list() + + var/cdir + var/turf/T + + for(var/card in cardinal) + T = get_step(loc,card) + cdir = get_dir(T,loc) + + for(var/obj/structure/cable/C in T) + if(C.powernet) + continue + if(C.d1 == cdir || C.d2 == cdir) + . += C + return . + +//returns all the cables in neighbors turfs, +//pointing towards the turf the machine is located at +/obj/machinery/power/proc/get_marked_connections() + + . = list() + + var/cdir + var/turf/T + + for(var/card in cardinal) + T = get_step(loc,card) + cdir = get_dir(T,loc) + + for(var/obj/structure/cable/C in T) + if(C.d1 == cdir || C.d2 == cdir) + . += C + return . + +//returns all the NODES (O-X) cables WITHOUT a powernet in the turf the machine is located at +/obj/machinery/power/proc/get_indirect_connections() + . = list() + for(var/obj/structure/cable/C in loc) + if(C.powernet) + continue + if(C.d1 == 0) // the cable is a node cable + . += C + return . + +/////////////////////////////////////////// +// GLOBAL PROCS for powernets handling +////////////////////////////////////////// + + +// returns a list of all power-related objects (nodes, cable, junctions) in turf, +// excluding source, that match the direction d +// if unmarked==1, only return those with no powernet +/proc/power_list(turf/T, source, d, unmarked=0, cable_only = 0) + . = list() + + for(var/AM in T) + if(AM == source) + continue //we don't want to return source + + if(!cable_only && istype(AM, /obj/machinery/power)) + var/obj/machinery/power/P = AM + if(P.powernet == 0) + continue // exclude APCs which have powernet=0 + + if(!unmarked || !P.powernet) //if unmarked=1 we only return things with no powernet + if(d == 0) + . += P + + else if(istype(AM, /obj/structure/cable)) + var/obj/structure/cable/C = AM + + if(!unmarked || !C.powernet) + if(C.d1 == d || C.d2 == d) + . += C + return . + +//remove the old powernet and replace it with a new one throughout the network. +/proc/propagate_network(obj/O, datum/powernet/PN) + var/list/worklist = list() + var/list/found_machines = list() + var/index = 1 + var/obj/P = null + + worklist+=O //start propagating from the passed object + + while(index<=worklist.len) //until we've exhausted all power objects + P = worklist[index] //get the next power object found + index++ + + if(istype(P, /obj/structure/cable)) + var/obj/structure/cable/C = P + if(C.powernet != PN) //add it to the powernet, if it isn't already there + PN.add_cable(C) + worklist |= C.get_connections() //get adjacents power objects, with or without a powernet + + else if(P.anchored && istype(P, /obj/machinery/power)) + var/obj/machinery/power/M = P + found_machines |= M //we wait until the powernet is fully propagates to connect the machines + + else + continue + + //now that the powernet is set, connect found machines to it + for(var/obj/machinery/power/PM in found_machines) + if(!PM.connect_to_network()) //couldn't find a node on its turf... + PM.disconnect_from_network() //... so disconnect if already on a powernet + + +//Merge two powernets, the bigger (in cable length term) absorbing the other +/proc/merge_powernets(datum/powernet/net1, datum/powernet/net2) + if(!net1 || !net2) //if one of the powernet doesn't exist, return + return + + if(net1 == net2) //don't merge same powernets + return + + //We assume net1 is larger. If net2 is in fact larger we are just going to make them switch places to reduce on code. + if(net1.cables.len < net2.cables.len) //net2 is larger than net1. Let's switch them around + var/temp = net1 + net1 = net2 + net2 = temp + + //merge net2 into net1 + for(var/obj/structure/cable/Cable in net2.cables) //merge cables + net1.add_cable(Cable) + + for(var/obj/machinery/power/Node in net2.nodes) //merge power machines + if(!Node.connect_to_network()) + Node.disconnect_from_network() //if somehow we can't connect the machine to the new powernet, disconnect it from the old nonetheless + + return net1 + +//Determines how strong could be shock, deals damage to mob, uses power. +//M is a mob who touched wire/whatever +//power_source is a source of electricity, can be powercell, area, apc, cable, powernet or null +//source is an object caused electrocuting (airlock, grille, etc) +//No animations will be performed by this proc. +/proc/electrocute_mob(mob/living/M, power_source, obj/source, siemens_coeff = 1, dist_check = FALSE) + if(!M || ismecha(M.loc)) + return FALSE //feckin mechs are dumb + if(dist_check) + if(!in_range(source, M)) + return FALSE + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(H.gloves) + var/obj/item/clothing/gloves/G = H.gloves + if(G.siemens_coefficient == 0) + return FALSE //to avoid spamming with insulated glvoes on + + var/area/source_area + if(istype(power_source, /area)) + source_area = power_source + power_source = source_area.get_apc() + if(istype(power_source, /obj/structure/cable)) + var/obj/structure/cable/Cable = power_source + power_source = Cable.powernet + + var/datum/powernet/PN + var/obj/item/stock_parts/cell/cell + + if(istype(power_source, /datum/powernet)) + PN = power_source + else if(istype(power_source, /obj/item/stock_parts/cell)) + cell = power_source + else if(istype(power_source, /obj/machinery/power/apc)) + var/obj/machinery/power/apc/apc = power_source + cell = apc.cell + if(apc.terminal) + PN = apc.terminal.powernet + else if(!power_source) + return 0 + else + log_admin("ERROR: /proc/electrocute_mob([M], [power_source], [source]): wrong power_source") + return 0 + if(!cell && !PN) + return 0 + var/PN_damage = 0 + var/cell_damage = 0 + if(PN) + PN_damage = PN.get_electrocute_damage() + if(cell) + cell_damage = cell.get_electrocute_damage() + var/shock_damage = 0 + if(PN_damage >= cell_damage) + power_source = PN + shock_damage = PN_damage + else + power_source = cell + shock_damage = cell_damage + var/drained_hp = M.electrocute_act(shock_damage, source, siemens_coeff) //zzzzzzap! + var/drained_energy = drained_hp*20 + + if(source_area) + source_area.use_power(drained_energy/GLOB.CELLRATE) + else if(istype(power_source, /datum/powernet)) + var/drained_power = drained_energy/GLOB.CELLRATE //convert from "joules" to "watts" + PN.delayedload += (min(drained_power, max(PN.newavail - PN.delayedload, 0))) + else if (istype(power_source, /obj/item/stock_parts/cell)) + cell.use(drained_energy) + return drained_energy + +//////////////////////////////////////////////// +// Misc. +/////////////////////////////////////////////// + + +// return a knot cable (O-X) if one is present in the turf +// null if there's none +/turf/proc/get_cable_node() + if(!can_have_cabling()) + return null + for(var/obj/structure/cable/C in src) + if(C.d1 == 0) + return C + return null + +/area/proc/get_apc() + for(var/obj/machinery/power/apc/APC in GLOB.apcs) + if(APC.area == src) + return APC diff --git a/code/modules/power/powernet.dm b/code/modules/power/powernet.dm index b64481b1104..a4ab64a2099 100644 --- a/code/modules/power/powernet.dm +++ b/code/modules/power/powernet.dm @@ -99,4 +99,4 @@ if(avail >= 1000) return Clamp(20 + round(avail / 25000), 20, 195) + rand(-5, 5) else - return 0 \ No newline at end of file + return 0 diff --git a/code/modules/power/singularity/collector.dm b/code/modules/power/singularity/collector.dm index efdd6ab2681..870a42a1f7c 100644 --- a/code/modules/power/singularity/collector.dm +++ b/code/modules/power/singularity/collector.dm @@ -1,148 +1,148 @@ -var/global/list/rad_collectors = list() - -/obj/machinery/power/rad_collector - name = "Radiation Collector Array" - desc = "A device which uses Hawking Radiation and plasma to produce power." - icon = 'icons/obj/singularity.dmi' - icon_state = "ca" - anchored = 0 - density = 1 - req_access = list(access_engine_equip) -// use_power = NO_POWER_USE - max_integrity = 350 - integrity_failure = 80 - var/obj/item/tank/plasma/P = null - var/last_power = 0 - var/active = 0 - var/locked = 0 - var/drainratio = 1 - -/obj/machinery/power/rad_collector/Initialize(mapload) - . = ..() - rad_collectors += src - -/obj/machinery/power/rad_collector/Destroy() - rad_collectors -= src - return ..() - -/obj/machinery/power/rad_collector/process() - if(P) - if(P.air_contents.toxins <= 0) - investigate_log("out of fuel.","singulo") - P.air_contents.toxins = 0 - eject() - else - P.air_contents.toxins -= 0.001*drainratio - return - - -/obj/machinery/power/rad_collector/attack_hand(mob/user as mob) - if(anchored) - if(!src.locked) - toggle_power() - user.visible_message("[user.name] turns the [src.name] [active? "on":"off"].", \ - "You turn the [src.name] [active? "on":"off"].") - investigate_log("turned [active?"on":"off"] by [user.key]. [P?"Fuel: [round(P.air_contents.toxins/0.29)]%":"It is empty"].","singulo") - return - else - to_chat(user, "The controls are locked!") - return - - -/obj/machinery/power/rad_collector/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/multitool)) - to_chat(user, "The [W.name] detects that [last_power]W were recently produced.") - return 1 - else if(istype(W, /obj/item/analyzer) && P) - atmosanalyzer_scan(P.air_contents, user) - else if(istype(W, /obj/item/tank/plasma)) - if(!src.anchored) - to_chat(user, "The [src] needs to be secured to the floor first.") - return 1 - if(src.P) - to_chat(user, "There's already a plasma tank loaded.") - return 1 - user.drop_item() - src.P = W - W.loc = src - update_icons() - else if(istype(W, /obj/item/crowbar)) - if(P && !src.locked) - eject() - return 1 - else if(istype(W, /obj/item/wrench)) - if(P) - to_chat(user, "Remove the plasma tank first.") - return 1 - playsound(src.loc, W.usesound, 75, 1) - src.anchored = !src.anchored - user.visible_message("[user.name] [anchored? "secures":"unsecures"] the [src.name].", \ - "You [anchored? "secure":"undo"] the external bolts.", \ - "You hear a ratchet") - if(anchored) - connect_to_network() - else - disconnect_from_network() - else if(istype(W, /obj/item/card/id)||istype(W, /obj/item/pda)) - if(src.allowed(user)) - if(active) - src.locked = !src.locked - to_chat(user, "The controls are now [src.locked ? "locked." : "unlocked."]") - else - src.locked = 0 //just in case it somehow gets locked - to_chat(user, "The controls can only be locked when the [src] is active") - else - to_chat(user, "Access denied!") - return 1 - else - return ..() - -/obj/machinery/power/rad_collector/obj_break(damage_flag) - if(!(stat & BROKEN) && !(flags & NODECONSTRUCT)) - eject() - stat |= BROKEN - -/obj/machinery/power/rad_collector/proc/eject() - locked = 0 - var/obj/item/tank/plasma/Z = src.P - if(!Z) - return - Z.loc = get_turf(src) - Z.layer = initial(Z.layer) - Z.plane = initial(Z.plane) - src.P = null - if(active) - toggle_power() - else - update_icons() - -/obj/machinery/power/rad_collector/proc/receive_pulse(var/pulse_strength) - if(P && active) - var/power_produced = 0 - power_produced = P.air_contents.toxins*pulse_strength*20 - add_avail(power_produced) - last_power = power_produced - return - return - - -/obj/machinery/power/rad_collector/proc/update_icons() - overlays.Cut() - if(P) - overlays += image('icons/obj/singularity.dmi', "ptank") - if(stat & (NOPOWER|BROKEN)) - return - if(active) - overlays += image('icons/obj/singularity.dmi', "on") - - -/obj/machinery/power/rad_collector/proc/toggle_power() - active = !active - if(active) - icon_state = "ca_on" - flick("ca_active", src) - else - icon_state = "ca" - flick("ca_deactive", src) - update_icons() - return +var/global/list/rad_collectors = list() + +/obj/machinery/power/rad_collector + name = "Radiation Collector Array" + desc = "A device which uses Hawking Radiation and plasma to produce power." + icon = 'icons/obj/singularity.dmi' + icon_state = "ca" + anchored = 0 + density = 1 + req_access = list(ACCESS_ENGINE_EQUIP) +// use_power = NO_POWER_USE + max_integrity = 350 + integrity_failure = 80 + var/obj/item/tank/plasma/P = null + var/last_power = 0 + var/active = 0 + var/locked = 0 + var/drainratio = 1 + +/obj/machinery/power/rad_collector/Initialize(mapload) + . = ..() + rad_collectors += src + +/obj/machinery/power/rad_collector/Destroy() + rad_collectors -= src + return ..() + +/obj/machinery/power/rad_collector/process() + if(P) + if(P.air_contents.toxins <= 0) + investigate_log("out of fuel.","singulo") + P.air_contents.toxins = 0 + eject() + else + P.air_contents.toxins -= 0.001*drainratio + return + + +/obj/machinery/power/rad_collector/attack_hand(mob/user as mob) + if(anchored) + if(!src.locked) + toggle_power() + user.visible_message("[user.name] turns the [src.name] [active? "on":"off"].", \ + "You turn the [src.name] [active? "on":"off"].") + investigate_log("turned [active?"on":"off"] by [user.key]. [P?"Fuel: [round(P.air_contents.toxins/0.29)]%":"It is empty"].","singulo") + return + else + to_chat(user, "The controls are locked!") + return + + +/obj/machinery/power/rad_collector/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/multitool)) + to_chat(user, "The [W.name] detects that [last_power]W were recently produced.") + return 1 + else if(istype(W, /obj/item/analyzer) && P) + atmosanalyzer_scan(P.air_contents, user) + else if(istype(W, /obj/item/tank/plasma)) + if(!src.anchored) + to_chat(user, "The [src] needs to be secured to the floor first.") + return 1 + if(src.P) + to_chat(user, "There's already a plasma tank loaded.") + return 1 + user.drop_item() + src.P = W + W.loc = src + update_icons() + else if(istype(W, /obj/item/crowbar)) + if(P && !src.locked) + eject() + return 1 + else if(istype(W, /obj/item/wrench)) + if(P) + to_chat(user, "Remove the plasma tank first.") + return 1 + playsound(src.loc, W.usesound, 75, 1) + src.anchored = !src.anchored + user.visible_message("[user.name] [anchored? "secures":"unsecures"] the [src.name].", \ + "You [anchored? "secure":"undo"] the external bolts.", \ + "You hear a ratchet") + if(anchored) + connect_to_network() + else + disconnect_from_network() + else if(istype(W, /obj/item/card/id)||istype(W, /obj/item/pda)) + if(src.allowed(user)) + if(active) + src.locked = !src.locked + to_chat(user, "The controls are now [src.locked ? "locked." : "unlocked."]") + else + src.locked = 0 //just in case it somehow gets locked + to_chat(user, "The controls can only be locked when the [src] is active") + else + to_chat(user, "Access denied!") + return 1 + else + return ..() + +/obj/machinery/power/rad_collector/obj_break(damage_flag) + if(!(stat & BROKEN) && !(flags & NODECONSTRUCT)) + eject() + stat |= BROKEN + +/obj/machinery/power/rad_collector/proc/eject() + locked = 0 + var/obj/item/tank/plasma/Z = src.P + if(!Z) + return + Z.loc = get_turf(src) + Z.layer = initial(Z.layer) + Z.plane = initial(Z.plane) + src.P = null + if(active) + toggle_power() + else + update_icons() + +/obj/machinery/power/rad_collector/proc/receive_pulse(var/pulse_strength) + if(P && active) + var/power_produced = 0 + power_produced = P.air_contents.toxins*pulse_strength*20 + add_avail(power_produced) + last_power = power_produced + return + return + + +/obj/machinery/power/rad_collector/proc/update_icons() + overlays.Cut() + if(P) + overlays += image('icons/obj/singularity.dmi', "ptank") + if(stat & (NOPOWER|BROKEN)) + return + if(active) + overlays += image('icons/obj/singularity.dmi', "on") + + +/obj/machinery/power/rad_collector/proc/toggle_power() + active = !active + if(active) + icon_state = "ca_on" + flick("ca_active", src) + else + icon_state = "ca" + flick("ca_deactive", src) + update_icons() + return diff --git a/code/modules/power/singularity/containment_field.dm b/code/modules/power/singularity/containment_field.dm index d28518ae32b..70ef60b11d6 100644 --- a/code/modules/power/singularity/containment_field.dm +++ b/code/modules/power/singularity/containment_field.dm @@ -1,126 +1,126 @@ -/obj/machinery/field/containment - name = "Containment Field" - desc = "An energy field." - icon = 'icons/obj/singularity.dmi' - icon_state = "Contain_F" - anchored = 1 - density = 0 - move_resist = INFINITY - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - use_power = NO_POWER_USE - light_range = 4 - layer = OBJ_LAYER + 0.1 - var/obj/machinery/field/generator/FG1 = null - var/obj/machinery/field/generator/FG2 = null - -/obj/machinery/field/containment/Destroy() - FG1.fields -= src - FG2.fields -= src - return ..() - -/obj/machinery/field/containment/attack_hand(mob/user) - if(get_dist(src, user) > 1) - return 0 - else - shock_field(user) - return 1 - -/obj/machinery/field/containment/attackby(obj/item/W, mob/user, params) - shock(user) - return TRUE - -/obj/machinery/field/containment/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - switch(damage_type) - if(BURN) - playsound(loc, 'sound/effects/empulse.ogg', 75, TRUE) - if(BRUTE) - playsound(loc, 'sound/effects/empulse.ogg', 75, TRUE) - -/obj/machinery/field/containment/blob_act(obj/structure/blob/B) - return FALSE - - -/obj/machinery/field/containment/ex_act(severity) - return 0 - -/obj/machinery/field/containment/attack_animal(mob/living/simple_animal/M) - if(!FG1 || !FG2) - qdel(src) - return - if(ismegafauna(M)) - M.visible_message("[M] glows fiercely as the containment field flickers out!") - FG1.calc_power(INFINITY) //rip that 'containment' field - M.adjustHealth(-M.obj_damage) - else - ..() - -/obj/machinery/field/containment/Crossed(mob/mover, oldloc) - if(isliving(mover)) - shock_field(mover) - - if(istype(mover, /obj/machinery) || istype(mover, /obj/structure) || istype(mover, /obj/mecha)) - bump_field(mover) - -/obj/machinery/field/containment/proc/set_master(master1,master2) - if(!master1 || !master2) - return 0 - FG1 = master1 - FG2 = master2 - return 1 - -/obj/machinery/field/containment/shock_field(mob/living/user) - if(!FG1 || !FG2) - qdel(src) - return 0 - ..() - -/obj/machinery/field/containment/Move() - qdel(src) - -// Abstract Field Class -// Used for overriding certain procs - -/obj/machinery/field - var/hasShocked = 0 //Used to add a delay between shocks. In some cases this used to crash servers by spawning hundreds of sparks every second. - -/obj/machinery/field/CanPass(atom/movable/mover, turf/target, height=0) - if(hasShocked) - return 0 - if(isliving(mover)) // Don't let mobs through - shock_field(mover) - return 0 - if(istype(mover, /obj/machinery) || istype(mover, /obj/structure) || istype(mover, /obj/mecha)) - bump_field(mover) - return 0 - return ..() - -/obj/machinery/field/proc/shock_field(mob/living/user) - if(isliving(user)) - var/shock_damage = min(rand(30,40),rand(30,40)) - - if(isliving(user) && !issilicon(user)) - var/stun = min(shock_damage, 15) - user.Stun(stun) - user.Weaken(10) - user.electrocute_act(shock_damage, src, 1) - - else if(issilicon(user)) - if(prob(20)) - user.Stun(2) - user.take_overall_damage(0, shock_damage) - user.visible_message("[user.name] was shocked by the [src.name]!", \ - "Energy pulse detected, system damaged!", \ - "You hear an electrical crack.") - - user.updatehealth() - bump_field(user) - -/obj/machinery/field/proc/bump_field(atom/movable/AM as mob|obj) - if(hasShocked) - return 0 - hasShocked = 1 - do_sparks(5, 1, AM.loc) - var/atom/target = get_edge_target_turf(AM, get_dir(src, get_step_away(AM, src))) - AM.throw_at(target, 200, 4) - spawn(5) - hasShocked = 0 +/obj/machinery/field/containment + name = "Containment Field" + desc = "An energy field." + icon = 'icons/obj/singularity.dmi' + icon_state = "Contain_F" + anchored = 1 + density = 0 + move_resist = INFINITY + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + use_power = NO_POWER_USE + light_range = 4 + layer = OBJ_LAYER + 0.1 + var/obj/machinery/field/generator/FG1 = null + var/obj/machinery/field/generator/FG2 = null + +/obj/machinery/field/containment/Destroy() + FG1.fields -= src + FG2.fields -= src + return ..() + +/obj/machinery/field/containment/attack_hand(mob/user) + if(get_dist(src, user) > 1) + return 0 + else + shock_field(user) + return 1 + +/obj/machinery/field/containment/attackby(obj/item/W, mob/user, params) + shock(user) + return TRUE + +/obj/machinery/field/containment/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + switch(damage_type) + if(BURN) + playsound(loc, 'sound/effects/empulse.ogg', 75, TRUE) + if(BRUTE) + playsound(loc, 'sound/effects/empulse.ogg', 75, TRUE) + +/obj/machinery/field/containment/blob_act(obj/structure/blob/B) + return FALSE + + +/obj/machinery/field/containment/ex_act(severity) + return 0 + +/obj/machinery/field/containment/attack_animal(mob/living/simple_animal/M) + if(!FG1 || !FG2) + qdel(src) + return + if(ismegafauna(M)) + M.visible_message("[M] glows fiercely as the containment field flickers out!") + FG1.calc_power(INFINITY) //rip that 'containment' field + M.adjustHealth(-M.obj_damage) + else + ..() + +/obj/machinery/field/containment/Crossed(mob/mover, oldloc) + if(isliving(mover)) + shock_field(mover) + + if(istype(mover, /obj/machinery) || istype(mover, /obj/structure) || istype(mover, /obj/mecha)) + bump_field(mover) + +/obj/machinery/field/containment/proc/set_master(master1,master2) + if(!master1 || !master2) + return 0 + FG1 = master1 + FG2 = master2 + return 1 + +/obj/machinery/field/containment/shock_field(mob/living/user) + if(!FG1 || !FG2) + qdel(src) + return 0 + ..() + +/obj/machinery/field/containment/Move() + qdel(src) + +// Abstract Field Class +// Used for overriding certain procs + +/obj/machinery/field + var/hasShocked = 0 //Used to add a delay between shocks. In some cases this used to crash servers by spawning hundreds of sparks every second. + +/obj/machinery/field/CanPass(atom/movable/mover, turf/target, height=0) + if(hasShocked) + return 0 + if(isliving(mover)) // Don't let mobs through + shock_field(mover) + return 0 + if(istype(mover, /obj/machinery) || istype(mover, /obj/structure) || istype(mover, /obj/mecha)) + bump_field(mover) + return 0 + return ..() + +/obj/machinery/field/proc/shock_field(mob/living/user) + if(isliving(user)) + var/shock_damage = min(rand(30,40),rand(30,40)) + + if(isliving(user) && !issilicon(user)) + var/stun = min(shock_damage, 15) + user.Stun(stun) + user.Weaken(10) + user.electrocute_act(shock_damage, src, 1) + + else if(issilicon(user)) + if(prob(20)) + user.Stun(2) + user.take_overall_damage(0, shock_damage) + user.visible_message("[user.name] was shocked by the [src.name]!", \ + "Energy pulse detected, system damaged!", \ + "You hear an electrical crack.") + + user.updatehealth() + bump_field(user) + +/obj/machinery/field/proc/bump_field(atom/movable/AM as mob|obj) + if(hasShocked) + return 0 + hasShocked = 1 + do_sparks(5, 1, AM.loc) + var/atom/target = get_edge_target_turf(AM, get_dir(src, get_step_away(AM, src))) + AM.throw_at(target, 200, 4) + spawn(5) + hasShocked = 0 diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index 82d78e99e53..9579fcc6bf4 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -1,356 +1,346 @@ -/obj/machinery/power/emitter - name = "Emitter" - desc = "A heavy duty industrial laser" - icon = 'icons/obj/singularity.dmi' - icon_state = "emitter" - anchored = 0 - density = 1 - req_access = list(access_engine_equip) - - use_power = NO_POWER_USE - idle_power_usage = 10 - active_power_usage = 300 - - var/active = 0 - var/powered = 0 - var/fire_delay = 100 - var/maximum_fire_delay = 100 - var/minimum_fire_delay = 20 - var/last_shot = 0 - var/shot_number = 0 - var/state = 0 - var/locked = 0 - - var/frequency = 0 - var/id_tag = null - var/projectile_type = /obj/item/projectile/beam/emitter - var/projectile_sound = 'sound/weapons/emitter.ogg' - var/datum/radio_frequency/radio_connection - var/datum/effect_system/spark_spread/sparks - -/obj/machinery/power/emitter/Initialize(mapload) - . = ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/emitter(null) - component_parts += new /obj/item/stock_parts/micro_laser(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - RefreshParts() - if(state == 2 && anchored) - connect_to_network() - sparks = new - sparks.attach(src) - sparks.set_up(5, 1, src) - if(frequency) - set_frequency(frequency) - -/obj/machinery/power/emitter/RefreshParts() - var/max_firedelay = 120 - var/firedelay = 120 - var/min_firedelay = 24 - var/power_usage = 350 - for(var/obj/item/stock_parts/micro_laser/L in component_parts) - max_firedelay -= 20 * L.rating - min_firedelay -= 4 * L.rating - firedelay -= 20 * L.rating - maximum_fire_delay = max_firedelay - minimum_fire_delay = min_firedelay - fire_delay = firedelay - for(var/obj/item/stock_parts/manipulator/M in component_parts) - power_usage -= 50 * M.rating - active_power_usage = power_usage - - //Radio remote control -/obj/machinery/power/emitter/proc/set_frequency(new_frequency) - SSradio.remove_object(src, frequency) - frequency = new_frequency - if(frequency) - radio_connection = SSradio.add_object(src, frequency, RADIO_ATMOSIA) - - -/obj/machinery/power/emitter/verb/rotate() - set name = "Rotate" - set category = "Object" - set src in oview(1) - - if(src.anchored || usr:stat) - to_chat(usr, "It is fastened to the floor!") - return 0 - src.dir = turn(src.dir, 90) - return 1 - -/obj/machinery/power/emitter/AltClick(mob/user) - if(user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!Adjacent(user)) - return - rotate() - -/obj/machinery/power/emitter/multitool_menu(var/mob/user,var/obj/item/multitool/P) - return {" - - "} - -/obj/machinery/power/emitter/receive_signal(datum/signal/signal) - if(!signal.data["tag"] || (signal.data["tag"] != id_tag)) - return 0 - - var/on=0 - switch(signal.data["command"]) - if("on") - on=1 - - if("off") - on=0 - - if("set") - on = signal.data["state"] > 0 - - if("toggle") - on = !active - - if(anchored && state == 2 && on != active) - active=on - var/statestr=on?"on":"off" - // Spammy message_admins("Emitter turned [statestr] by radio signal ([signal.data["command"]] @ [frequency]) in [formatJumpTo(src)]",0,1) - log_game("Emitter turned [statestr] by radio signal ([signal.data["command"]] @ [frequency]) in ([x], [y], [z]) AAC prints: [jointext(signal.data["hiddenprints"], "")]") - investigate_log("turned [statestr] by radio signal ([signal.data["command"]] @ [frequency]) AAC prints: [jointext(signal.data["hiddenprints"], "")]","singulo") - update_icon() - -/obj/machinery/power/emitter/Destroy() - if(SSradio) - SSradio.remove_object(src, frequency) - radio_connection = null - msg_admin_attack("Emitter deleted at ([x],[y],[z] - [ADMIN_JMP(src)])", ATKLOG_FEW) - log_game("Emitter deleted at ([x],[y],[z])") - investigate_log("deleted at ([x],[y],[z])","singulo") - QDEL_NULL(sparks) - return ..() - -/obj/machinery/power/emitter/update_icon() - if(active && powernet && avail(active_power_usage)) - icon_state = "emitter_+a" - else - icon_state = "emitter" - - -/obj/machinery/power/emitter/attack_hand(mob/user as mob) - src.add_fingerprint(user) - if(state == 2) - if(!powernet) - to_chat(user, "The emitter isn't connected to a wire.") - return 1 - if(!src.locked) - if(src.active==1) - src.active = 0 - to_chat(user, "You turn off the [src].") - message_admins("Emitter turned off by [key_name_admin(user)] in ([x], [y], [z] - JMP)",0,1) - log_game("Emitter turned off by [key_name(user)] in [x], [y], [z]") - investigate_log("turned off by [key_name(usr)]","singulo") - else - src.active = 1 - to_chat(user, "You turn on the [src].") - src.shot_number = 0 - src.fire_delay = maximum_fire_delay - message_admins("Emitter turned on by [key_name_admin(user)] in ([x], [y], [z] - JMP)",0,1) - log_game("Emitter turned on by [key_name(user)] in [x], [y], [z]") - investigate_log("turned on by [key_name(usr)]","singulo") - update_icon() - else - to_chat(user, "The controls are locked!") - else - to_chat(user, "The [src] needs to be firmly secured to the floor first.") - return 1 - - -/obj/machinery/power/emitter/emp_act(var/severity)//Emitters are hardened but still might have issues -// add_load(1000) -/* if((severity == 1)&&prob(1)&&prob(1)) - if(src.active) - src.active = 0 - src.use_power = IDLE_POWER_USE */ - return 1 - -/obj/machinery/power/emitter/attack_animal(mob/living/simple_animal/M) - if(ismegafauna(M) && anchored) - state = 0 - anchored = FALSE - M.visible_message("[M] rips [src] free from its moorings!") - else - ..() - if(!anchored) - step(src, get_dir(M, src)) - -/obj/machinery/power/emitter/process() - if(stat & (BROKEN)) - return - if(src.state != 2 || (!powernet && active_power_usage)) - src.active = 0 - update_icon() - return - if(active == TRUE) - if(!active_power_usage || surplus() >= active_power_usage) - add_load(active_power_usage) - if(!powered) - powered = 1 - update_icon() - investigate_log("regained power and turned on","singulo") - else - if(powered) - powered = 0 - update_icon() - investigate_log("lost power and turned off","singulo") - return - - if(!check_delay()) - return FALSE - fire_beam() - -/obj/machinery/power/emitter/proc/check_delay() - if((last_shot + fire_delay) <= world.time) - return TRUE - return FALSE - -/obj/machinery/power/emitter/proc/fire_beam() - var/obj/item/projectile/P = new projectile_type(get_turf(src)) - playsound(get_turf(src), projectile_sound, 50, TRUE) - if(prob(35)) - sparks.start() - switch(dir) - if(NORTH) - P.yo = 20 - P.xo = 0 - if(NORTHEAST) - P.yo = 20 - P.xo = 20 - if(EAST) - P.yo = 0 - P.xo = 20 - if(SOUTHEAST) - P.yo = -20 - P.xo = 20 - if(WEST) - P.yo = 0 - P.xo = -20 - if(SOUTHWEST) - P.yo = -20 - P.xo = -20 - if(NORTHWEST) - P.yo = 20 - P.xo = -20 - else // Any other - P.yo = -20 - P.xo = 0 - - last_shot = world.time - if(shot_number < 3) - fire_delay = 20 - shot_number ++ - else - fire_delay = rand(minimum_fire_delay, maximum_fire_delay) - shot_number = 0 - P.setDir(dir) - P.starting = loc - P.Angle = null - P.fire() - return P - -/obj/machinery/power/emitter/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/multitool)) - update_multitool_menu(user) - return 1 - - if(istype(W, /obj/item/wrench)) - if(active) - to_chat(user, "Turn off the [src] first.") - return - switch(state) - if(0) - state = 1 - playsound(src.loc, W.usesound, 75, 1) - user.visible_message("[user.name] secures [src.name] to the floor.", \ - "You secure the external reinforcing bolts to the floor.", \ - "You hear a ratchet") - src.anchored = 1 - if(1) - state = 0 - playsound(src.loc,W.usesound, 75, 1) - user.visible_message("[user.name] unsecures [src.name] reinforcing bolts from the floor.", \ - "You undo the external reinforcing bolts.", \ - "You hear a ratchet") - src.anchored = 0 - if(2) - to_chat(user, "The [src.name] needs to be unwelded from the floor.") - return - - if(istype(W, /obj/item/weldingtool)) - var/obj/item/weldingtool/WT = W - if(active) - to_chat(user, "Turn off the [src] first.") - return - switch(state) - if(0) - to_chat(user, "The [src.name] needs to be wrenched to the floor.") - if(1) - if(WT.remove_fuel(0,user)) - playsound(src.loc, WT.usesound, 50, 1) - user.visible_message("[user.name] starts to weld the [src.name] to the floor.", \ - "You start to weld the [src] to the floor.", \ - "You hear welding") - if(do_after(user, 20 * WT.toolspeed, target = src)) - if(!src || !WT.isOn()) return - state = 2 - to_chat(user, "You weld the [src] to the floor.") - connect_to_network() - else - to_chat(user, "You need more welding fuel to complete this task.") - if(2) - if(WT.remove_fuel(0,user)) - playsound(src.loc, WT.usesound, 50, 1) - user.visible_message("[user.name] starts to cut the [src.name] free from the floor.", \ - "You start to cut the [src] free from the floor.", \ - "You hear welding") - if(do_after(user, 20 * WT.toolspeed, target = src)) - if(!src || !WT.isOn()) return - state = 1 - to_chat(user, "You cut the [src] free from the floor.") - disconnect_from_network() - else - to_chat(user, "You need more welding fuel to complete this task.") - return - - if(istype(W, /obj/item/card/id) || istype(W, /obj/item/pda)) - if(emagged) - to_chat(user, "The lock seems to be broken") - return - if(src.allowed(user)) - if(active) - src.locked = !src.locked - to_chat(user, "The controls are now [src.locked ? "locked." : "unlocked."]") - else - src.locked = 0 //just in case it somehow gets locked - to_chat(user, "The controls can only be locked when the [src] is online") - else - to_chat(user, "Access denied.") - return - - if(default_deconstruction_screwdriver(user, "emitter_open", "emitter", W)) - return - - if(exchange_parts(user, W)) - return - - if(default_deconstruction_crowbar(W)) - return - - return ..() - -/obj/machinery/power/emitter/emag_act(var/mob/living/user as mob) - if(!emagged) - locked = 0 - emagged = 1 - if(user) - user.visible_message("[user.name] emags the [src.name].","You short out the lock.") +/obj/machinery/power/emitter + name = "Emitter" + desc = "A heavy duty industrial laser" + icon = 'icons/obj/singularity.dmi' + icon_state = "emitter" + anchored = 0 + density = 1 + req_access = list(ACCESS_ENGINE_EQUIP) + + use_power = NO_POWER_USE + idle_power_usage = 10 + active_power_usage = 300 + + var/active = 0 + var/powered = 0 + var/fire_delay = 100 + var/maximum_fire_delay = 100 + var/minimum_fire_delay = 20 + var/last_shot = 0 + var/shot_number = 0 + var/state = 0 + var/locked = 0 + + var/frequency = 0 + var/id_tag = null + var/projectile_type = /obj/item/projectile/beam/emitter + var/projectile_sound = 'sound/weapons/emitter.ogg' + var/datum/radio_frequency/radio_connection + var/datum/effect_system/spark_spread/sparks + +/obj/machinery/power/emitter/Initialize(mapload) + . = ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/emitter(null) + component_parts += new /obj/item/stock_parts/micro_laser(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + RefreshParts() + if(state == 2 && anchored) + connect_to_network() + sparks = new + sparks.attach(src) + sparks.set_up(5, 1, src) + if(frequency) + set_frequency(frequency) + +/obj/machinery/power/emitter/RefreshParts() + var/max_firedelay = 120 + var/firedelay = 120 + var/min_firedelay = 24 + var/power_usage = 350 + for(var/obj/item/stock_parts/micro_laser/L in component_parts) + max_firedelay -= 20 * L.rating + min_firedelay -= 4 * L.rating + firedelay -= 20 * L.rating + maximum_fire_delay = max_firedelay + minimum_fire_delay = min_firedelay + fire_delay = firedelay + for(var/obj/item/stock_parts/manipulator/M in component_parts) + power_usage -= 50 * M.rating + active_power_usage = power_usage + + //Radio remote control +/obj/machinery/power/emitter/proc/set_frequency(new_frequency) + SSradio.remove_object(src, frequency) + frequency = new_frequency + if(frequency) + radio_connection = SSradio.add_object(src, frequency, RADIO_ATMOSIA) + + +/obj/machinery/power/emitter/verb/rotate() + set name = "Rotate" + set category = "Object" + set src in oview(1) + + if(src.anchored || usr:stat) + to_chat(usr, "It is fastened to the floor!") + return 0 + src.dir = turn(src.dir, 90) + return 1 + +/obj/machinery/power/emitter/AltClick(mob/user) + if(user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!Adjacent(user)) + return + rotate() + +/obj/machinery/power/emitter/multitool_menu(var/mob/user,var/obj/item/multitool/P) + return {" + + "} + +/obj/machinery/power/emitter/receive_signal(datum/signal/signal) + if(!signal.data["tag"] || (signal.data["tag"] != id_tag)) + return 0 + + var/on=0 + switch(signal.data["command"]) + if("on") + on=1 + + if("off") + on=0 + + if("set") + on = signal.data["state"] > 0 + + if("toggle") + on = !active + + if(anchored && state == 2 && on != active) + active=on + var/statestr=on?"on":"off" + // Spammy message_admins("Emitter turned [statestr] by radio signal ([signal.data["command"]] @ [frequency]) in [formatJumpTo(src)]",0,1) + log_game("Emitter turned [statestr] by radio signal ([signal.data["command"]] @ [frequency]) in ([x], [y], [z]) AAC prints: [jointext(signal.data["hiddenprints"], "")]") + investigate_log("turned [statestr] by radio signal ([signal.data["command"]] @ [frequency]) AAC prints: [jointext(signal.data["hiddenprints"], "")]","singulo") + update_icon() + +/obj/machinery/power/emitter/Destroy() + if(SSradio) + SSradio.remove_object(src, frequency) + radio_connection = null + msg_admin_attack("Emitter deleted at ([x],[y],[z] - [ADMIN_JMP(src)])", ATKLOG_FEW) + log_game("Emitter deleted at ([x],[y],[z])") + investigate_log("deleted at ([x],[y],[z])","singulo") + QDEL_NULL(sparks) + return ..() + +/obj/machinery/power/emitter/update_icon() + if(active && powernet && avail(active_power_usage)) + icon_state = "emitter_+a" + else + icon_state = "emitter" + + +/obj/machinery/power/emitter/attack_hand(mob/user as mob) + src.add_fingerprint(user) + if(state == 2) + if(!powernet) + to_chat(user, "The emitter isn't connected to a wire.") + return 1 + if(!src.locked) + if(src.active==1) + src.active = 0 + to_chat(user, "You turn off the [src].") + message_admins("Emitter turned off by [key_name_admin(user)] in ([x], [y], [z] - JMP)",0,1) + log_game("Emitter turned off by [key_name(user)] in [x], [y], [z]") + investigate_log("turned off by [key_name(usr)]","singulo") + else + src.active = 1 + to_chat(user, "You turn on the [src].") + src.shot_number = 0 + src.fire_delay = maximum_fire_delay + message_admins("Emitter turned on by [key_name_admin(user)] in ([x], [y], [z] - JMP)",0,1) + log_game("Emitter turned on by [key_name(user)] in [x], [y], [z]") + investigate_log("turned on by [key_name(usr)]","singulo") + update_icon() + else + to_chat(user, "The controls are locked!") + else + to_chat(user, "The [src] needs to be firmly secured to the floor first.") + return 1 + + +/obj/machinery/power/emitter/emp_act(var/severity)//Emitters are hardened but still might have issues +// add_load(1000) +/* if((severity == 1)&&prob(1)&&prob(1)) + if(src.active) + src.active = 0 + src.use_power = IDLE_POWER_USE */ + return 1 + +/obj/machinery/power/emitter/attack_animal(mob/living/simple_animal/M) + if(ismegafauna(M) && anchored) + state = 0 + anchored = FALSE + M.visible_message("[M] rips [src] free from its moorings!") + else + ..() + if(!anchored) + step(src, get_dir(M, src)) + +/obj/machinery/power/emitter/process() + if(stat & (BROKEN)) + return + if(src.state != 2 || (!powernet && active_power_usage)) + src.active = 0 + update_icon() + return + if(active == TRUE) + if(!active_power_usage || surplus() >= active_power_usage) + add_load(active_power_usage) + if(!powered) + powered = 1 + update_icon() + investigate_log("regained power and turned on","singulo") + else + if(powered) + powered = 0 + update_icon() + investigate_log("lost power and turned off","singulo") + return + + if(!check_delay()) + return FALSE + fire_beam() + +/obj/machinery/power/emitter/proc/check_delay() + if((last_shot + fire_delay) <= world.time) + return TRUE + return FALSE + +/obj/machinery/power/emitter/proc/fire_beam() + var/obj/item/projectile/P = new projectile_type(get_turf(src)) + playsound(get_turf(src), projectile_sound, 50, TRUE) + if(prob(35)) + sparks.start() + switch(dir) + if(NORTH) + P.yo = 20 + P.xo = 0 + if(NORTHEAST) + P.yo = 20 + P.xo = 20 + if(EAST) + P.yo = 0 + P.xo = 20 + if(SOUTHEAST) + P.yo = -20 + P.xo = 20 + if(WEST) + P.yo = 0 + P.xo = -20 + if(SOUTHWEST) + P.yo = -20 + P.xo = -20 + if(NORTHWEST) + P.yo = 20 + P.xo = -20 + else // Any other + P.yo = -20 + P.xo = 0 + + last_shot = world.time + if(shot_number < 3) + fire_delay = 20 + shot_number ++ + else + fire_delay = rand(minimum_fire_delay, maximum_fire_delay) + shot_number = 0 + P.setDir(dir) + P.starting = loc + P.Angle = null + P.fire() + return P + +/obj/machinery/power/emitter/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/multitool)) + update_multitool_menu(user) + return 1 + + if(istype(W, /obj/item/wrench)) + if(active) + to_chat(user, "Turn off the [src] first.") + return + switch(state) + if(0) + state = 1 + playsound(src.loc, W.usesound, 75, 1) + user.visible_message("[user.name] secures [src.name] to the floor.", \ + "You secure the external reinforcing bolts to the floor.", \ + "You hear a ratchet") + src.anchored = 1 + if(1) + state = 0 + playsound(src.loc,W.usesound, 75, 1) + user.visible_message("[user.name] unsecures [src.name] reinforcing bolts from the floor.", \ + "You undo the external reinforcing bolts.", \ + "You hear a ratchet") + src.anchored = 0 + if(2) + to_chat(user, "The [src.name] needs to be unwelded from the floor.") + return + + if(istype(W, /obj/item/card/id) || istype(W, /obj/item/pda)) + if(emagged) + to_chat(user, "The lock seems to be broken") + return + if(src.allowed(user)) + if(active) + src.locked = !src.locked + to_chat(user, "The controls are now [src.locked ? "locked." : "unlocked."]") + else + src.locked = 0 //just in case it somehow gets locked + to_chat(user, "The controls can only be locked when the [src] is online") + else + to_chat(user, "Access denied.") + return + + if(default_deconstruction_screwdriver(user, "emitter_open", "emitter", W)) + return + + if(exchange_parts(user, W)) + return + + if(default_deconstruction_crowbar(user, W)) + return + + return ..() + +/obj/machinery/power/emitter/emag_act(var/mob/living/user as mob) + if(!emagged) + locked = 0 + emagged = 1 + if(user) + user.visible_message("[user.name] emags the [src.name].","You short out the lock.") + + +/obj/machinery/power/emitter/welder_act(mob/user, obj/item/I) + if(active) + to_chat(user, "Turn off [src] first.") + return + if(state == 0) + to_chat(user, "[src] needs to be wrenched to the floor.") + return + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if(state == 1) + WELDER_ATTEMPT_FLOOR_WELD_MESSAGE + else if(state == 2) + WELDER_ATTEMPT_FLOOR_SLICE_MESSAGE + if(!I.use_tool(src, user, 20, volume = I.tool_volume)) + return + if(state == 1) + WELDER_FLOOR_WELD_SUCCESS_MESSAGE + connect_to_network() + state = 2 + else if(state == 2) + WELDER_FLOOR_SLICE_SUCCESS_MESSAGE + disconnect_from_network() + state = 1 diff --git a/code/modules/power/singularity/field_generator.dm b/code/modules/power/singularity/field_generator.dm index d6711be53e1..62f2ed36065 100644 --- a/code/modules/power/singularity/field_generator.dm +++ b/code/modules/power/singularity/field_generator.dm @@ -1,349 +1,337 @@ -/* -field_generator power level display - The icon used for the field_generator need to have 'num_power_levels' number of icon states - named 'Field_Gen +p[num]' where 'num' ranges from 1 to 'num_power_levels' - - The power level is displayed using overlays. The current displayed power level is stored in 'powerlevel'. - The overlay in use and the powerlevel variable must be kept in sync. A powerlevel equal to 0 means that - no power level overlay is currently in the overlays list. - -Aygar -*/ - -#define field_generator_max_power 250 - -#define FG_UNSECURED 0 -#define FG_SECURED 1 -#define FG_WELDED 2 - -#define FG_OFFLINE 0 -#define FG_CHARGING 1 -#define FG_ONLINE 2 - -/obj/machinery/field/generator - name = "Field Generator" - desc = "A large thermal battery that projects a high amount of energy when powered." - icon = 'icons/obj/machines/field_generator.dmi' - icon_state = "Field_Gen" - anchored = 0 - density = 1 - use_power = NO_POWER_USE - max_integrity = 500 - //100% immune to lasers and energy projectiles since it absorbs their energy. - armor = list("melee" = 25, "bullet" = 10, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 70) - var/const/num_power_levels = 6 // Total number of power level icon has - var/power_level = 0 - var/active = FG_OFFLINE - var/power = 20 // Current amount of power - var/state = FG_UNSECURED - var/warming_up = 0 - var/list/obj/machinery/field/containment/fields - var/list/obj/machinery/field/generator/connected_gens - var/clean_up = 0 - -/obj/machinery/field/generator/update_icon() - overlays.Cut() - if(warming_up) - overlays += "+a[warming_up]" - if(fields.len) - overlays += "+on" - if(power_level) - overlays += "+p[power_level]" - - -/obj/machinery/field/generator/Initialize(mapload) - . = ..() - fields = list() - connected_gens = list() - - -/obj/machinery/field/generator/process() - if(active == FG_ONLINE) - calc_power() - -/obj/machinery/field/generator/attack_hand(mob/user) - if(state == FG_WELDED) - if(get_dist(src, user) <= 1)//Need to actually touch the thing to turn it on - if(active >= FG_CHARGING) - to_chat(user, "You are unable to turn off the [name] once it is online!") - return 1 - else - user.visible_message("[user.name] turns on the [name].", \ - "You turn on the [name].", \ - "You hear heavy droning.") - turn_on() - investigate_log("activated by [user.key].","singulo") - - add_fingerprint(user) - else - to_chat(user, "[src] needs to be firmly secured to the floor first!") - - -/obj/machinery/field/generator/attackby(obj/item/W, mob/user, params) - if(active) - to_chat(user, "[src] needs to be off!") - return - else if(istype(W, /obj/item/wrench)) - switch(state) - if(FG_UNSECURED) - if(isinspace()) return - state = FG_SECURED - playsound(loc, W.usesound, 75, 1) - user.visible_message("[user.name] secures [name] to the floor.", \ - "You secure the external reinforcing bolts to the floor.", \ - "You hear ratchet.") - anchored = 1 - if(FG_SECURED) - state = FG_UNSECURED - playsound(loc, W.usesound, 75, 1) - user.visible_message("[user.name] unsecures [name] reinforcing bolts from the floor.", \ - "You undo the external reinforcing bolts.", \ - "You hear ratchet.") - anchored = 0 - if(FG_WELDED) - to_chat(user, "The [name] needs to be unwelded from the floor!") - - else if(istype(W, /obj/item/weldingtool)) - var/obj/item/weldingtool/WT = W - switch(state) - if(FG_UNSECURED) - to_chat(user, "The [name] needs to be wrenched to the floor!") - - if(FG_SECURED) - if(WT.remove_fuel(0,user)) - playsound(loc, WT.usesound, 50, 1) - user.visible_message("[user.name] starts to weld the [name] to the floor.", \ - "You start to weld \the [src] to the floor...", \ - "You hear welding.") - if(do_after(user, 20 * WT.toolspeed, target = src)) - if(!src || !WT.isOn()) - return - state = FG_WELDED - to_chat(user, "You weld the field generator to the floor.") - - if(FG_WELDED) - if(WT.remove_fuel(0,user)) - playsound(loc, WT.usesound, 50, 1) - user.visible_message("[user.name] starts to cut the [name] free from the floor.", \ - "You start to cut \the [src] free from the floor...", \ - "You hear welding.") - if(do_after(user, 20 * WT.toolspeed, target = src)) - if(!src || !WT.isOn()) - return - state = FG_SECURED - to_chat(user, "You cut \the [src] free from the floor.") - - else - return ..() - - -/obj/machinery/field/generator/emp_act() - return 0 - -/obj/machinery/field/generator/attack_animal(mob/living/simple_animal/M) - if(M.environment_smash & ENVIRONMENT_SMASH_RWALLS && active == FG_OFFLINE && state != FG_UNSECURED) - state = FG_UNSECURED - anchored = FALSE - M.visible_message("[M] rips [src] free from its moorings!") - else - ..() - if(!anchored) - step(src, get_dir(M, src)) - -/obj/machinery/field/generator/blob_act(obj/structure/blob/B) - if(active) - return 0 - else - ..() - -/obj/machinery/field/generator/bullet_act(obj/item/projectile/Proj) - if(Proj.flag != "bullet") - power = min(power + Proj.damage, field_generator_max_power) - check_power_level() - return 0 - - -/obj/machinery/field/generator/Destroy() - cleanup() - return ..() - - -/obj/machinery/field/generator/proc/check_power_level() - var/new_level = round(num_power_levels * power / field_generator_max_power) - if(new_level != power_level) - power_level = new_level - update_icon() - -/obj/machinery/field/generator/proc/turn_off() - active = FG_OFFLINE - spawn(1) - cleanup() - while(warming_up > 0 && !active) - sleep(50) - warming_up-- - update_icon() - -/obj/machinery/field/generator/proc/turn_on() - active = FG_CHARGING - spawn(1) - while(warming_up < 3 && active) - sleep(50) - warming_up++ - update_icon() - if(warming_up >= 3) - start_fields() - - -/obj/machinery/field/generator/proc/calc_power() - var/power_draw = 2 + fields.len - - if(draw_power(round(power_draw/2, 1))) - check_power_level() - return 1 - else - visible_message("The [name] shuts down!", "You hear something shutting down.") - turn_off() - investigate_log("ran out of power and deactivated","singulo") - power = 0 - check_power_level() - return 0 - -//This could likely be better, it tends to start loopin if you have a complex generator loop setup. Still works well enough to run the engine fields will likely recode the field gens and fields sometime -Mport -/obj/machinery/field/generator/proc/draw_power(draw = 0, failsafe = 0, obj/machinery/field/generator/G = null, obj/machinery/field/generator/last = null) - if((G && (G == src)) || (failsafe >= 8))//Loopin, set fail - return 0 - else - failsafe++ - - if(power >= draw)//We have enough power - power -= draw - return 1 - - else//Need more power - draw -= power - power = 0 - for(var/CG in connected_gens) - var/obj/machinery/field/generator/FG = CG - if(FG == last)//We just asked you - continue - if(G)//Another gen is askin for power and we dont have it - if(FG.draw_power(draw,failsafe,G,src))//Can you take the load - return 1 - else - return 0 - else//We are askin another for power - if(FG.draw_power(draw,failsafe,src,src)) - return 1 - else - return 0 - - -/obj/machinery/field/generator/proc/start_fields() - if(state != FG_WELDED || !anchored) - turn_off() - return - spawn(1) - setup_field(1) - spawn(2) - setup_field(2) - spawn(3) - setup_field(4) - spawn(4) - setup_field(8) - spawn(5) - active = FG_ONLINE - - -/obj/machinery/field/generator/proc/setup_field(NSEW) - var/turf/T = loc - if(!istype(T)) - return 0 - - var/obj/machinery/field/generator/G = null - var/steps = 0 - if(!NSEW)//Make sure its ran right - return 0 - for(var/dist in 0 to 7) // checks out to 8 tiles away for another generator - T = get_step(T, NSEW) - if(T.density)//We cant shoot a field though this - return 0 - - G = locate(/obj/machinery/field/generator) in T - if(G) - steps -= 1 - if(!G.active) - return 0 - break - - for(var/TC in T.contents) - var/atom/A = TC - if(ismob(A)) - continue - if(A.density) - return 0 - - steps++ - - if(!G) - return 0 - - T = loc - for(var/dist in 0 to steps) // creates each field tile - var/field_dir = get_dir(T,get_step(G.loc, NSEW)) - T = get_step(T, NSEW) - if(!locate(/obj/machinery/field/containment) in T) - var/obj/machinery/field/containment/CF = new/obj/machinery/field/containment() - CF.set_master(src,G) - CF.loc = T - CF.dir = field_dir - fields += CF - G.fields += CF - for(var/mob/living/L in T) - CF.Crossed(L, null) - - connected_gens |= G - G.connected_gens |= src - update_icon() - - -/obj/machinery/field/generator/proc/cleanup() - clean_up = 1 - for(var/F in fields) - qdel(F) - - for(var/CG in connected_gens) - var/obj/machinery/field/generator/FG = CG - FG.connected_gens -= src - if(!FG.clean_up)//Makes the other gens clean up as well - FG.cleanup() - connected_gens -= FG - clean_up = 0 - update_icon() - - //This is here to help fight the "hurr durr, release singulo cos nobody will notice before the - //singulo eats the evidence". It's not fool-proof but better than nothing. - //I want to avoid using global variables. - spawn(1) - var/temp = 1 //stops spam - for(var/obj/singularity/O in GLOB.singularities) - if(O.last_warning && temp) - if((world.time - O.last_warning) > 50) //to stop message-spam - temp = 0 - message_admins("A singulo exists and a containment field has failed. Location: [get_area(src)] (JMP)",1) - investigate_log("has failed whilst a singulo exists.","singulo") - O.last_warning = world.time - -/obj/machinery/field/generator/shock_field(mob/living/user) - if(fields.len) - ..() - -/obj/machinery/field/generator/bump_field(atom/movable/AM as mob|obj) - if(fields.len) - ..() - -#undef FG_UNSECURED -#undef FG_SECURED -#undef FG_WELDED - -#undef FG_OFFLINE -#undef FG_CHARGING -#undef FG_ONLINE +/* +field_generator power level display + The icon used for the field_generator need to have 'num_power_levels' number of icon states + named 'Field_Gen +p[num]' where 'num' ranges from 1 to 'num_power_levels' + + The power level is displayed using overlays. The current displayed power level is stored in 'powerlevel'. + The overlay in use and the powerlevel variable must be kept in sync. A powerlevel equal to 0 means that + no power level overlay is currently in the overlays list. + -Aygar +*/ + +#define field_generator_max_power 250 + +#define FG_UNSECURED 0 +#define FG_SECURED 1 +#define FG_WELDED 2 + +#define FG_OFFLINE 0 +#define FG_CHARGING 1 +#define FG_ONLINE 2 + +/obj/machinery/field/generator + name = "Field Generator" + desc = "A large thermal battery that projects a high amount of energy when powered." + icon = 'icons/obj/machines/field_generator.dmi' + icon_state = "Field_Gen" + anchored = 0 + density = 1 + use_power = NO_POWER_USE + max_integrity = 500 + //100% immune to lasers and energy projectiles since it absorbs their energy. + armor = list("melee" = 25, "bullet" = 10, "laser" = 100, "energy" = 100, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 70) + var/const/num_power_levels = 6 // Total number of power level icon has + var/power_level = 0 + var/active = FG_OFFLINE + var/power = 20 // Current amount of power + var/state = FG_UNSECURED + var/warming_up = 0 + var/list/obj/machinery/field/containment/fields + var/list/obj/machinery/field/generator/connected_gens + var/clean_up = 0 + +/obj/machinery/field/generator/update_icon() + overlays.Cut() + if(warming_up) + overlays += "+a[warming_up]" + if(fields.len) + overlays += "+on" + if(power_level) + overlays += "+p[power_level]" + + +/obj/machinery/field/generator/Initialize(mapload) + . = ..() + fields = list() + connected_gens = list() + + +/obj/machinery/field/generator/process() + if(active == FG_ONLINE) + calc_power() + +/obj/machinery/field/generator/attack_hand(mob/user) + if(state == FG_WELDED) + if(get_dist(src, user) <= 1)//Need to actually touch the thing to turn it on + if(active >= FG_CHARGING) + to_chat(user, "You are unable to turn off the [name] once it is online!") + return 1 + else + user.visible_message("[user.name] turns on the [name].", \ + "You turn on the [name].", \ + "You hear heavy droning.") + turn_on() + investigate_log("activated by [user.key].","singulo") + + add_fingerprint(user) + else + to_chat(user, "[src] needs to be firmly secured to the floor first!") + + +/obj/machinery/field/generator/attackby(obj/item/W, mob/user, params) + if(active) + to_chat(user, "[src] needs to be off!") + return + else if(istype(W, /obj/item/wrench)) + switch(state) + if(FG_UNSECURED) + if(isinspace()) return + state = FG_SECURED + playsound(loc, W.usesound, 75, 1) + user.visible_message("[user.name] secures [name] to the floor.", \ + "You secure the external reinforcing bolts to the floor.", \ + "You hear ratchet.") + anchored = 1 + if(FG_SECURED) + state = FG_UNSECURED + playsound(loc, W.usesound, 75, 1) + user.visible_message("[user.name] unsecures [name] reinforcing bolts from the floor.", \ + "You undo the external reinforcing bolts.", \ + "You hear ratchet.") + anchored = 0 + if(FG_WELDED) + to_chat(user, "The [name] needs to be unwelded from the floor!") + else + return ..() + + +/obj/machinery/field/generator/welder_act(mob/user, obj/item/I) + . = TRUE + if(state == FG_UNSECURED) + to_chat(user, "[src] needs to be wrenched to the floor!") + return + if(!I.tool_use_check(user, 0)) + return + if(state == FG_SECURED) + WELDER_ATTEMPT_FLOOR_SLICE_MESSAGE + else if(state == FG_WELDED) + WELDER_ATTEMPT_FLOOR_WELD_MESSAGE + if(I.use_tool(src, user, 20, volume = I.tool_volume)) + if(state == FG_SECURED) + WELDER_FLOOR_SLICE_SUCCESS_MESSAGE + state = FG_WELDED + else if(state == FG_WELDED) + WELDER_FLOOR_WELD_SUCCESS_MESSAGE + state = FG_SECURED + +/obj/machinery/field/generator/emp_act() + return 0 + +/obj/machinery/field/generator/attack_animal(mob/living/simple_animal/M) + if(M.environment_smash & ENVIRONMENT_SMASH_RWALLS && active == FG_OFFLINE && state != FG_UNSECURED) + state = FG_UNSECURED + anchored = FALSE + M.visible_message("[M] rips [src] free from its moorings!") + else + ..() + if(!anchored) + step(src, get_dir(M, src)) + +/obj/machinery/field/generator/blob_act(obj/structure/blob/B) + if(active) + return 0 + else + ..() + +/obj/machinery/field/generator/bullet_act(obj/item/projectile/Proj) + if(Proj.flag != "bullet") + power = min(power + Proj.damage, field_generator_max_power) + check_power_level() + return 0 + + +/obj/machinery/field/generator/Destroy() + cleanup() + return ..() + + +/obj/machinery/field/generator/proc/check_power_level() + var/new_level = round(num_power_levels * power / field_generator_max_power) + if(new_level != power_level) + power_level = new_level + update_icon() + +/obj/machinery/field/generator/proc/turn_off() + active = FG_OFFLINE + spawn(1) + cleanup() + while(warming_up > 0 && !active) + sleep(50) + warming_up-- + update_icon() + +/obj/machinery/field/generator/proc/turn_on() + active = FG_CHARGING + spawn(1) + while(warming_up < 3 && active) + sleep(50) + warming_up++ + update_icon() + if(warming_up >= 3) + start_fields() + + +/obj/machinery/field/generator/proc/calc_power() + var/power_draw = 2 + fields.len + + if(draw_power(round(power_draw/2, 1))) + check_power_level() + return 1 + else + visible_message("The [name] shuts down!", "You hear something shutting down.") + turn_off() + investigate_log("ran out of power and deactivated","singulo") + power = 0 + check_power_level() + return 0 + +//This could likely be better, it tends to start loopin if you have a complex generator loop setup. Still works well enough to run the engine fields will likely recode the field gens and fields sometime -Mport +/obj/machinery/field/generator/proc/draw_power(draw = 0, failsafe = 0, obj/machinery/field/generator/G = null, obj/machinery/field/generator/last = null) + if((G && (G == src)) || (failsafe >= 8))//Loopin, set fail + return 0 + else + failsafe++ + + if(power >= draw)//We have enough power + power -= draw + return 1 + + else//Need more power + draw -= power + power = 0 + for(var/CG in connected_gens) + var/obj/machinery/field/generator/FG = CG + if(FG == last)//We just asked you + continue + if(G)//Another gen is askin for power and we dont have it + if(FG.draw_power(draw,failsafe,G,src))//Can you take the load + return 1 + else + return 0 + else//We are askin another for power + if(FG.draw_power(draw,failsafe,src,src)) + return 1 + else + return 0 + + +/obj/machinery/field/generator/proc/start_fields() + if(state != FG_WELDED || !anchored) + turn_off() + return + spawn(1) + setup_field(1) + spawn(2) + setup_field(2) + spawn(3) + setup_field(4) + spawn(4) + setup_field(8) + spawn(5) + active = FG_ONLINE + + +/obj/machinery/field/generator/proc/setup_field(NSEW) + var/turf/T = loc + if(!istype(T)) + return 0 + + var/obj/machinery/field/generator/G = null + var/steps = 0 + if(!NSEW)//Make sure its ran right + return 0 + for(var/dist in 0 to 7) // checks out to 8 tiles away for another generator + T = get_step(T, NSEW) + if(T.density)//We cant shoot a field though this + return 0 + + G = locate(/obj/machinery/field/generator) in T + if(G) + steps -= 1 + if(!G.active) + return 0 + break + + for(var/TC in T.contents) + var/atom/A = TC + if(ismob(A)) + continue + if(A.density) + return 0 + + steps++ + + if(!G) + return 0 + + T = loc + for(var/dist in 0 to steps) // creates each field tile + var/field_dir = get_dir(T,get_step(G.loc, NSEW)) + T = get_step(T, NSEW) + if(!locate(/obj/machinery/field/containment) in T) + var/obj/machinery/field/containment/CF = new/obj/machinery/field/containment() + CF.set_master(src,G) + CF.loc = T + CF.dir = field_dir + fields += CF + G.fields += CF + for(var/mob/living/L in T) + CF.Crossed(L, null) + + connected_gens |= G + G.connected_gens |= src + update_icon() + + +/obj/machinery/field/generator/proc/cleanup() + clean_up = 1 + for(var/F in fields) + qdel(F) + + for(var/CG in connected_gens) + var/obj/machinery/field/generator/FG = CG + FG.connected_gens -= src + if(!FG.clean_up)//Makes the other gens clean up as well + FG.cleanup() + connected_gens -= FG + clean_up = 0 + update_icon() + + //This is here to help fight the "hurr durr, release singulo cos nobody will notice before the + //singulo eats the evidence". It's not fool-proof but better than nothing. + //I want to avoid using global variables. + spawn(1) + var/temp = 1 //stops spam + for(var/obj/singularity/O in GLOB.singularities) + if(O.last_warning && temp) + if((world.time - O.last_warning) > 50) //to stop message-spam + temp = 0 + message_admins("A singulo exists and a containment field has failed. Location: [get_area(src)] (JMP)",1) + investigate_log("has failed whilst a singulo exists.","singulo") + O.last_warning = world.time + +/obj/machinery/field/generator/shock_field(mob/living/user) + if(fields.len) + ..() + +/obj/machinery/field/generator/bump_field(atom/movable/AM as mob|obj) + if(fields.len) + ..() + +#undef FG_UNSECURED +#undef FG_SECURED +#undef FG_WELDED + +#undef FG_OFFLINE +#undef FG_CHARGING +#undef FG_ONLINE diff --git a/code/modules/power/singularity/generator.dm b/code/modules/power/singularity/generator.dm index c5494f372e7..9bed8105a3e 100644 --- a/code/modules/power/singularity/generator.dm +++ b/code/modules/power/singularity/generator.dm @@ -1,38 +1,38 @@ -/////SINGULARITY SPAWNER -/obj/machinery/the_singularitygen - name = "Gravitational Singularity Generator" - desc = "An odd device which produces a Gravitational Singularity when set up." - icon = 'icons/obj/singularity.dmi' - icon_state = "TheSingGen" - anchored = 0 - density = 1 - use_power = NO_POWER_USE - resistance_flags = FIRE_PROOF - var/energy = 0 - var/creation_type = /obj/singularity - -/obj/machinery/the_singularitygen/process() - var/turf/T = get_turf(src) - if(src.energy >= 200) - message_admins("A [creation_type] has been created at [x], [y], [z] (JMP)") - investigate_log("A [creation_type] has been created at [x], [y], [z]","singulo") - - var/obj/singularity/S = new creation_type(T, 50) - transfer_fingerprints_to(S) - if(src) qdel(src) - -/obj/machinery/the_singularitygen/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/wrench)) - anchored = !anchored - playsound(src.loc, W.usesound, 75, 1) - if(anchored) - user.visible_message("[user.name] secures [src.name] to the floor.", \ - "You secure the [src.name] to the floor.", \ - "You hear a ratchet") - src.add_hiddenprint(user) - else - user.visible_message("[user.name] unsecures [src.name] from the floor.", \ - "You unsecure the [src.name] from the floor.", \ - "You hear a ratchet") - return - return ..() +/////SINGULARITY SPAWNER +/obj/machinery/the_singularitygen + name = "Gravitational Singularity Generator" + desc = "An odd device which produces a Gravitational Singularity when set up." + icon = 'icons/obj/singularity.dmi' + icon_state = "TheSingGen" + anchored = 0 + density = 1 + use_power = NO_POWER_USE + resistance_flags = FIRE_PROOF + var/energy = 0 + var/creation_type = /obj/singularity + +/obj/machinery/the_singularitygen/process() + var/turf/T = get_turf(src) + if(src.energy >= 200) + message_admins("A [creation_type] has been created at [x], [y], [z] (JMP)") + investigate_log("A [creation_type] has been created at [x], [y], [z]","singulo") + + var/obj/singularity/S = new creation_type(T, 50) + transfer_fingerprints_to(S) + if(src) qdel(src) + +/obj/machinery/the_singularitygen/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/wrench)) + anchored = !anchored + playsound(src.loc, W.usesound, 75, 1) + if(anchored) + user.visible_message("[user.name] secures [src.name] to the floor.", \ + "You secure the [src.name] to the floor.", \ + "You hear a ratchet") + src.add_hiddenprint(user) + else + user.visible_message("[user.name] unsecures [src.name] from the floor.", \ + "You unsecure the [src.name] from the floor.", \ + "You hear a ratchet") + return + return ..() diff --git a/code/modules/power/singularity/investigate.dm b/code/modules/power/singularity/investigate.dm index 6e22b1ad118..43e8c9f8a8b 100644 --- a/code/modules/power/singularity/investigate.dm +++ b/code/modules/power/singularity/investigate.dm @@ -1,4 +1,4 @@ /area/engine/engineering/power_alert(var/alarming) if(alarming) investigate_log("has a power alarm!","singulo") - ..() \ No newline at end of file + ..() diff --git a/code/modules/power/singularity/particle_accelerator/particle.dm b/code/modules/power/singularity/particle_accelerator/particle.dm index 327c29cebda..e409ccc7602 100644 --- a/code/modules/power/singularity/particle_accelerator/particle.dm +++ b/code/modules/power/singularity/particle_accelerator/particle.dm @@ -1,70 +1,59 @@ -/obj/effect/accelerated_particle - name = "Accelerated Particles" - desc = "Small things moving very fast." - icon = 'icons/obj/machines/particle_accelerator.dmi' - icon_state = "particle" - anchored = TRUE - density = FALSE - var/movement_range = 10 - var/energy = 10 - var/speed = 1 - -/obj/effect/accelerated_particle/weak - movement_range = 8 - energy = 5 - -/obj/effect/accelerated_particle/strong - movement_range = 15 - energy = 15 - -/obj/effect/accelerated_particle/powerful - movement_range = 20 - energy = 50 - - -/obj/effect/accelerated_particle/New(loc) - ..() - - addtimer(CALLBACK(src, .proc/move), 1) - - -/obj/effect/accelerated_particle/Bump(atom/A) - if(A) - if(isliving(A)) - toxmob(A) - else if(istype(A, /obj/machinery/the_singularitygen)) - var/obj/machinery/the_singularitygen/S = A - S.energy += energy - else if(istype(A, /obj/singularity)) - var/obj/singularity/S = A - S.energy += energy - else if(istype(A, /obj/structure/blob)) - var/obj/structure/blob/B = A - B.take_damage(energy * 0.6) - movement_range = 0 - -/obj/effect/accelerated_particle/Crossed(atom/A, oldloc) - if(isliving(A)) - toxmob(A) - - -/obj/effect/accelerated_particle/ex_act(severity) - qdel(src) - -/obj/effect/accelerated_particle/singularity_pull() - return - - - -/obj/effect/accelerated_particle/proc/toxmob(mob/living/M) - M.apply_effect((energy * 6), IRRADIATE, 0) - -/obj/effect/accelerated_particle/proc/move() - if(!step(src,dir)) - forceMove(get_step(src, dir)) - movement_range-- - if(movement_range == 0) - qdel(src) - else - sleep(speed) - move() \ No newline at end of file +/obj/effect/accelerated_particle + name = "Accelerated Particles" + desc = "Small things moving very fast." + icon = 'icons/obj/machines/particle_accelerator.dmi' + icon_state = "particle" + anchored = TRUE + density = FALSE + var/movement_range = 10 + var/energy = 10 + var/speed = 1 + +/obj/effect/accelerated_particle/weak + movement_range = 8 + energy = 5 + +/obj/effect/accelerated_particle/strong + movement_range = 15 + energy = 15 + +/obj/effect/accelerated_particle/powerful + movement_range = 20 + energy = 50 + + +/obj/effect/accelerated_particle/Initialize(loc) + . = ..() + addtimer(CALLBACK(src, .proc/propagate), 1) + RegisterSignal(src, COMSIG_CROSSED_MOVABLE, .proc/try_irradiate) + RegisterSignal(src, COMSIG_MOVABLE_CROSSED, .proc/try_irradiate) + QDEL_IN(src, movement_range) + +/obj/effect/accelerated_particle/proc/try_irradiate(src, atom/A) + if(isliving(A)) + var/mob/living/L = A + L.apply_effect((energy * 6), IRRADIATE, 0) + else if(istype(A, /obj/machinery/the_singularitygen)) + var/obj/machinery/the_singularitygen/S = A + S.energy += energy + else if(istype(A, /obj/structure/blob)) + var/obj/structure/blob/B = A + B.take_damage(energy * 0.6) + movement_range = 0 + +/obj/effect/accelerated_particle/Bump(obj/singularity/S) + if(!istype(S)) + return ..() + S.energy += energy + + +/obj/effect/accelerated_particle/ex_act(severity) + qdel(src) + +/obj/effect/accelerated_particle/singularity_pull() + return + +/obj/effect/accelerated_particle/proc/propagate() + addtimer(CALLBACK(src, .proc/propagate), 1) + if(!step(src,dir)) + forceMove(get_step(src, dir)) diff --git a/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm b/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm index 2942acfdb35..20113d61a35 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm @@ -1,377 +1,360 @@ -/*Composed of 7 parts -3 Particle emitters -proc -emit_particle() - -1 power box -the only part of this thing that uses power, can hack to mess with the pa/make it better - -1 fuel chamber -contains procs for mixing gas and whatever other fuel it uses -mix_gas() - -1 gas holder WIP -acts like a tank valve on the ground that you wrench gas tanks onto -proc -extract_gas() -return_gas() -attach_tank() -remove_tank() -get_available_mix() - -1 End Cap - -1 Control computer -interface for the pa, acts like a computer with an html menu for diff parts and a status report -all other parts contain only a ref to this -a /machine/, tells the others to do work -contains ref for all parts -proc -process() -check_build() - -Setup map - |EC| -CC|FC| - |PB| -PE|PE|PE - - -Icon Addemdum -Icon system is much more robust, and the icons are all variable based. -Each part has a reference string, powered, strength, and contruction values. -Using this the update_icon() proc is simplified a bit (using for absolutely was problematic with naming), -so the icon_state comes out be: -"[reference][strength]", with a switch controlling construction_states and ensuring that it doesn't -power on while being contructed, and all these variables are set by the computer through it's scan list -Essential order of the icons: -Standard - [reference] -Wrenched - [reference] -Wired - [reference]w -Closed - [reference]c -Powered - [reference]p[strength] -Strength being set by the computer and a null strength (Computer is powered off or inactive) returns a 'null', counting as empty -So, hopefully this is helpful if any more icons are to be added/changed/wondering what the hell is going on here - -*/ - -/obj/structure/particle_accelerator - name = "Particle Accelerator" - desc = "Part of a Particle Accelerator." - icon = 'icons/obj/machines/particle_accelerator.dmi' - icon_state = "none" - anchored = 0 - density = 1 - max_integrity = 500 - armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 80) - var/obj/machinery/particle_accelerator/control_box/master = null - var/construction_state = 0 - var/reference = null - var/powered = 0 - var/strength = null - var/desc_holder = null - -/obj/structure/particle_accelerator/Destroy() - construction_state = 0 - if(master) - master.part_scan() - return ..() - -/obj/structure/particle_accelerator/end_cap - name = "Alpha Particle Generation Array" - desc_holder = "This is where Alpha particles are generated from \[REDACTED\]" - icon_state = "end_cap" - reference = "end_cap" - -/obj/structure/particle_accelerator/update_icon() - ..() - return - - -/obj/structure/particle_accelerator/verb/rotate() - set name = "Rotate Clockwise" - set category = "Object" - set src in oview(1) - - if(usr.stat || !usr.canmove || usr.restrained()) - return - if(anchored) - to_chat(usr, "It is fastened to the floor!") - return 0 - dir = turn(dir, 270) - return 1 - -/obj/structure/particle_accelerator/AltClick(mob/user) - if(user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!Adjacent(user)) - return - rotate() - -/obj/structure/particle_accelerator/verb/rotateccw() - set name = "Rotate Counter Clockwise" - set category = "Object" - set src in oview(1) - - if(usr.stat || !usr.canmove || usr.restrained()) - return - if(anchored) - to_chat(usr, "It is fastened to the floor!") - return 0 - dir = turn(dir, 90) - return 1 - -/obj/structure/particle_accelerator/examine(mob/user) - switch(construction_state) - if(0) - desc = text("A [name], looks like it's not attached to the flooring") - if(1) - desc = text("A [name], it is missing some cables") - if(2) - desc = text("A [name], the panel is open") - if(3) - desc = text("The [name] is assembled") - if(powered) - desc = desc_holder - . = ..() - -/obj/structure/particle_accelerator/attackby(obj/item/W, mob/user, params) - if(istool(W)) - if(process_tool_hit(W,user)) - return - return..() - -/obj/structure/particle_accelerator/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - new /obj/item/stack/sheet/metal (loc, 5) - qdel(src) - -/obj/structure/particle_accelerator/Move() - . = ..() - if(master && master.active) - master.toggle_power() - investigate_log("was moved whilst active; it powered down.","singulo") - -/obj/machinery/particle_accelerator/control_box/blob_act(obj/structure/blob/B) - if(prob(50)) - qdel(src) - -/obj/structure/particle_accelerator/update_icon() - switch(construction_state) - if(0,1) - icon_state="[reference]" - if(2) - icon_state="[reference]w" - if(3) - if(powered) - icon_state="[reference]p[strength]" - else - icon_state="[reference]c" - return - -/obj/structure/particle_accelerator/proc/update_state() - if(master) - master.update_state() - return 0 - - -/obj/structure/particle_accelerator/proc/report_ready(var/obj/O) - if(O && (O == master)) - if(construction_state >= 3) - return 1 - return 0 - - -/obj/structure/particle_accelerator/proc/report_master() - if(master) - return master - return 0 - - -/obj/structure/particle_accelerator/proc/connect_master(var/obj/O) - if(O && istype(O,/obj/machinery/particle_accelerator/control_box)) - if(O.dir == dir) - master = O - return 1 - return 0 - - -/obj/structure/particle_accelerator/proc/process_tool_hit(var/obj/item/O, var/mob/user) - if(!(O) || !(user)) - return 0 - if(!ismob(user) || !isobj(O)) - return 0 - var/temp_state = construction_state - - switch(construction_state)//TODO:Might be more interesting to have it need several parts rather than a single list of steps - if(0) - if(iswrench(O) && !isinspace()) - playsound(loc, O.usesound, 50, 1) - anchored = 1 - user.visible_message("[user.name] secures the [name] to the floor.", \ - "You secure the external bolts.") - temp_state++ - if(1) - if(iswrench(O)) - playsound(loc, O.usesound, 50, 1) - anchored = 0 - user.visible_message("[user.name] detaches the [name] from the floor.", \ - "You remove the external bolts.") - temp_state-- - else if(iscoil(O)) - var/obj/item/stack/cable_coil/C = O - if(C.use(1)) - playsound(loc, O.usesound, 50, 1) - user.visible_message("[user.name] adds wires to the [name].", \ - "You add some wires.") - temp_state++ - else - to_chat(user, "You need one length of cable to wire the [name]!") - return - if(2) - if(iswirecutter(O))//TODO:Shock user if its on? - playsound(loc, O.usesound, 50, 1) - user.visible_message("[user.name] removes some wires from the [name].", \ - "You remove some wires.") - temp_state-- - else if(isscrewdriver(O)) - playsound(loc, O.usesound, 50, 1) - user.visible_message("[user.name] closes the [name]'s access panel.", \ - "You close the access panel.") - temp_state++ - if(3) - if(isscrewdriver(O)) - playsound(loc, O.usesound, 50, 1) - user.visible_message("[user.name] opens the [name]'s access panel.", \ - "You open the access panel.") - temp_state-- - if(temp_state == construction_state)//Nothing changed - return 0 - else - construction_state = temp_state - if(construction_state < 3)//Was taken apart, update state - update_state() - update_icon() - return 1 - return 0 - - - -/obj/machinery/particle_accelerator - name = "Particle Accelerator" - desc = "Part of a Particle Accelerator." - icon = 'icons/obj/machines/particle_accelerator.dmi' - icon_state = "none" - anchored = 0 - density = 1 - use_power = NO_POWER_USE - idle_power_usage = 0 - active_power_usage = 0 - var/construction_state = 0 - var/active = 0 - var/reference = null - var/powered = null - var/strength = 0 - var/desc_holder = null - - -/obj/machinery/particle_accelerator/verb/rotate() - set name = "Rotate Clockwise" - set category = "Object" - set src in oview(1) - - if(usr.stat || !usr.canmove || usr.restrained()) - return - if(anchored) - to_chat(usr, "It is fastened to the floor!") - return 0 - dir = turn(dir, 270) - return 1 - -/obj/machinery/particle_accelerator/verb/rotateccw() - set name = "Rotate Counter-Clockwise" - set category = "Object" - set src in oview(1) - - if(usr.stat || !usr.canmove || usr.restrained()) - return - if(anchored) - to_chat(usr, "It is fastened to the floor!") - return 0 - dir = turn(dir, 90) - return 1 - -/obj/machinery/particle_accelerator/update_icon() - return - -/obj/machinery/particle_accelerator/attackby(obj/item/W, mob/user, params) - if(istool(W)) - if(process_tool_hit(W,user)) - return - return ..() - -/obj/machinery/particle_accelerator/proc/update_state() - return 0 - - -/obj/machinery/particle_accelerator/proc/process_tool_hit(var/obj/item/O, var/mob/user) - if(!(O) || !(user)) - return 0 - if(!ismob(user) || !isobj(O)) - return 0 - var/temp_state = construction_state - switch(construction_state)//TODO:Might be more interesting to have it need several parts rather than a single list of steps - if(0) - if(iswrench(O)) - playsound(loc, O.usesound, 50, 1) - anchored = 1 - user.visible_message("[user.name] secures the [name] to the floor.", \ - "You secure the external bolts.") - temp_state++ - power_change() - if(1) - if(iswrench(O)) - playsound(loc, O.usesound, 50, 1) - anchored = 0 - user.visible_message("[user.name] detaches the [name] from the floor.", \ - "You remove the external bolts.") - temp_state-- - power_change() - else if(iscoil(O)) - if(O:use(1)) - playsound(loc, O.usesound, 50, 1) - user.visible_message("[user.name] adds wires to the [name].", \ - "You add some wires.") - temp_state++ - if(2) - if(iswirecutter(O))//TODO:Shock user if its on? - playsound(loc, O.usesound, 50, 1) - user.visible_message("[user.name] removes some wires from the [name].", \ - "You remove some wires.") - temp_state-- - else if(isscrewdriver(O)) - playsound(loc, O.usesound, 50, 1) - user.visible_message("[user.name] closes the [name]'s access panel.", \ - "You close the access panel.") - temp_state++ - if(3) - if(isscrewdriver(O)) - playsound(loc, O.usesound, 50, 1) - user.visible_message("[user.name] opens the [name]'s access panel.", \ - "You open the access panel.") - temp_state-- - active = 0 - if(temp_state == construction_state)//Nothing changed - return 0 - else - if(construction_state < 3)//Was taken apart, update state - update_state() - if(use_power) - use_power = NO_POWER_USE - construction_state = temp_state - if(construction_state >= 3) - use_power = IDLE_POWER_USE - update_icon() - return 1 - return 0 +/*Composed of 7 parts +3 Particle emitters +proc +emit_particle() + +1 power box +the only part of this thing that uses power, can hack to mess with the pa/make it better + +1 fuel chamber +contains procs for mixing gas and whatever other fuel it uses +mix_gas() + +1 gas holder WIP +acts like a tank valve on the ground that you wrench gas tanks onto +proc +extract_gas() +return_gas() +attach_tank() +remove_tank() +get_available_mix() + +1 End Cap + +1 Control computer +interface for the pa, acts like a computer with an html menu for diff parts and a status report +all other parts contain only a ref to this +a /machine/, tells the others to do work +contains ref for all parts +proc +process() +check_build() + +Setup map + |EC| +CC|FC| + |PB| +PE|PE|PE + + +Icon Addemdum +Icon system is much more robust, and the icons are all variable based. +Each part has a reference string, powered, strength, and contruction values. +Using this the update_icon() proc is simplified a bit (using for absolutely was problematic with naming), +so the icon_state comes out be: +"[reference][strength]", with a switch controlling construction_states and ensuring that it doesn't +power on while being contructed, and all these variables are set by the computer through it's scan list +Essential order of the icons: +Standard - [reference] +Wrenched - [reference] +Wired - [reference]w +Closed - [reference]c +Powered - [reference]p[strength] +Strength being set by the computer and a null strength (Computer is powered off or inactive) returns a 'null', counting as empty +So, hopefully this is helpful if any more icons are to be added/changed/wondering what the hell is going on here + +*/ +#define ACCELERATOR_UNWRENCHED 0 +#define ACCELERATOR_WRENCHED 1 +#define ACCELERATOR_WIRED 2 +#define ACCELERATOR_READY 3 + +/obj/structure/particle_accelerator + name = "Particle Accelerator" + desc = "Part of a Particle Accelerator." + icon = 'icons/obj/machines/particle_accelerator.dmi' + icon_state = "none" + anchored = 0 + density = 1 + max_integrity = 500 + armor = list("melee" = 30, "bullet" = 20, "laser" = 20, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 80) + var/obj/machinery/particle_accelerator/control_box/master = null + var/construction_state = 0 + var/reference = null + var/powered = 0 + var/strength = null + var/desc_holder = null + +/obj/structure/particle_accelerator/Destroy() + construction_state = 0 + if(master) + master.part_scan() + return ..() + +/obj/structure/particle_accelerator/end_cap + name = "Alpha Particle Generation Array" + desc_holder = "This is where Alpha particles are generated from \[REDACTED\]" + icon_state = "end_cap" + reference = "end_cap" + +/obj/structure/particle_accelerator/update_icon() + ..() + return + + +/obj/structure/particle_accelerator/verb/rotate() + set name = "Rotate Clockwise" + set category = "Object" + set src in oview(1) + + if(usr.stat || !usr.canmove || usr.restrained()) + return + if(anchored) + to_chat(usr, "It is fastened to the floor!") + return 0 + dir = turn(dir, 270) + return 1 + +/obj/structure/particle_accelerator/AltClick(mob/user) + if(user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!Adjacent(user)) + return + rotate() + +/obj/structure/particle_accelerator/verb/rotateccw() + set name = "Rotate Counter Clockwise" + set category = "Object" + set src in oview(1) + + if(usr.stat || !usr.canmove || usr.restrained()) + return + if(anchored) + to_chat(usr, "It is fastened to the floor!") + return 0 + dir = turn(dir, 90) + return 1 + +/obj/structure/particle_accelerator/examine(mob/user) + switch(construction_state) + if(0) + desc = text("A [name], looks like it's not attached to the flooring") + if(1) + desc = text("A [name], it is missing some cables") + if(2) + desc = text("A [name], the panel is open") + if(3) + desc = text("The [name] is assembled") + if(powered) + desc = desc_holder + . = ..() + +/obj/structure/particle_accelerator/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + new /obj/item/stack/sheet/metal (loc, 5) + qdel(src) + +/obj/structure/particle_accelerator/Move() + . = ..() + if(master && master.active) + master.toggle_power() + investigate_log("was moved whilst active; it powered down.","singulo") + +/obj/machinery/particle_accelerator/control_box/blob_act(obj/structure/blob/B) + if(prob(50)) + qdel(src) + +/obj/structure/particle_accelerator/update_icon() + switch(construction_state) + if(0,1) + icon_state="[reference]" + if(2) + icon_state="[reference]w" + if(3) + if(powered) + icon_state="[reference]p[strength]" + else + icon_state="[reference]c" + return + +/obj/structure/particle_accelerator/proc/update_state() + if(master) + master.update_state() + return 0 + + +/obj/structure/particle_accelerator/proc/report_ready(var/obj/O) + if(O && (O == master)) + if(construction_state >= 3) + return 1 + return 0 + + +/obj/structure/particle_accelerator/proc/report_master() + if(master) + return master + return 0 + + +/obj/structure/particle_accelerator/proc/connect_master(var/obj/O) + if(O && istype(O,/obj/machinery/particle_accelerator/control_box)) + if(O.dir == dir) + master = O + return 1 + return 0 + +/obj/structure/particle_accelerator/attackby(obj/item/W, mob/user, params) + if(!iscoil(W)) + return ..() + if(construction_state == ACCELERATOR_WRENCHED) + var/obj/item/stack/cable_coil/C = W + if(C.use(1)) + playsound(loc, C.usesound, 50, 1) + user.visible_message("[user.name] adds wires to the [name].", \ + "You add some wires.") + construction_state = ACCELERATOR_WIRED + update_icon() + +/obj/structure/particle_accelerator/screwdriver_act(mob/user, obj/item/I) + if(construction_state != ACCELERATOR_WIRED && construction_state != ACCELERATOR_READY) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(construction_state == ACCELERATOR_WIRED) + SCREWDRIVER_CLOSE_PANEL_MESSAGE + construction_state = ACCELERATOR_READY + + else + construction_state = ACCELERATOR_WIRED + SCREWDRIVER_OPEN_PANEL_MESSAGE + update_state() + update_icon() + +/obj/structure/particle_accelerator/wirecutter_act(mob/user, obj/item/I) + if(construction_state != ACCELERATOR_WIRED) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + WIRECUTTER_SNIP_MESSAGE + construction_state = ACCELERATOR_WRENCHED + +/obj/structure/particle_accelerator/wrench_act(mob/user, obj/item/I) + if(construction_state != ACCELERATOR_UNWRENCHED && construction_state != ACCELERATOR_WRENCHED) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(construction_state == ACCELERATOR_UNWRENCHED) + anchored = TRUE + WRENCH_ANCHOR_MESSAGE + construction_state = ACCELERATOR_WRENCHED + else + anchored = FALSE + WRENCH_UNANCHOR_MESSAGE + construction_state = ACCELERATOR_UNWRENCHED + update_icon() + + +/obj/machinery/particle_accelerator + name = "Particle Accelerator" + desc = "Part of a Particle Accelerator." + icon = 'icons/obj/machines/particle_accelerator.dmi' + icon_state = "none" + anchored = 0 + density = 1 + use_power = NO_POWER_USE + idle_power_usage = 0 + active_power_usage = 0 + var/construction_state = 0 + var/active = 0 + var/reference = null + var/powered = null + var/strength = 0 + var/desc_holder = null + + +/obj/machinery/particle_accelerator/verb/rotate() + set name = "Rotate Clockwise" + set category = "Object" + set src in oview(1) + + if(usr.stat || !usr.canmove || usr.restrained()) + return + if(anchored) + to_chat(usr, "It is fastened to the floor!") + return 0 + dir = turn(dir, 270) + return 1 + +/obj/machinery/particle_accelerator/verb/rotateccw() + set name = "Rotate Counter-Clockwise" + set category = "Object" + set src in oview(1) + + if(usr.stat || !usr.canmove || usr.restrained()) + return + if(anchored) + to_chat(usr, "It is fastened to the floor!") + return 0 + dir = turn(dir, 90) + return 1 + +/obj/machinery/particle_accelerator/update_icon() + return + +/obj/machinery/particle_accelerator/attackby(obj/item/W, mob/user, params) + if(!iscoil(W)) + return ..() + if(construction_state == ACCELERATOR_WRENCHED) + var/obj/item/stack/cable_coil/C = W + if(C.use(1)) + playsound(loc, C.usesound, 50, 1) + user.visible_message("[user.name] adds wires to the [name].", \ + "You add some wires.") + construction_state = ACCELERATOR_WIRED + update_icon() + +/obj/machinery/particle_accelerator/screwdriver_act(mob/user, obj/item/I) + if(construction_state != ACCELERATOR_WIRED && construction_state != ACCELERATOR_READY) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(construction_state == ACCELERATOR_WIRED) + SCREWDRIVER_CLOSE_PANEL_MESSAGE + construction_state = ACCELERATOR_READY + use_power = IDLE_POWER_USE + else + construction_state = ACCELERATOR_WIRED + SCREWDRIVER_OPEN_PANEL_MESSAGE + use_power = NO_POWER_USE + update_state() + update_icon() + +/obj/machinery/particle_accelerator/wirecutter_act(mob/user, obj/item/I) + if(construction_state != ACCELERATOR_WIRED) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + WIRECUTTER_SNIP_MESSAGE + construction_state = ACCELERATOR_WRENCHED + +/obj/machinery/particle_accelerator/wrench_act(mob/user, obj/item/I) + if(construction_state != ACCELERATOR_UNWRENCHED && construction_state != ACCELERATOR_WRENCHED) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(construction_state == ACCELERATOR_UNWRENCHED) + anchored = TRUE + WRENCH_ANCHOR_MESSAGE + construction_state = ACCELERATOR_WRENCHED + else + anchored = FALSE + WRENCH_UNANCHOR_MESSAGE + construction_state = ACCELERATOR_UNWRENCHED + update_icon() + + +/obj/machinery/particle_accelerator/proc/update_state() + return 0 + + +#undef ACCELERATOR_UNWRENCHED +#undef ACCELERATOR_WRENCHED +#undef ACCELERATOR_WIRED +#undef ACCELERATOR_READY diff --git a/code/modules/power/singularity/particle_accelerator/particle_chamber.dm b/code/modules/power/singularity/particle_accelerator/particle_chamber.dm index 411c039d480..c3c058a5289 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_chamber.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_chamber.dm @@ -1,10 +1,10 @@ -/obj/structure/particle_accelerator/fuel_chamber - name = "EM Acceleration Chamber" - desc_holder = "This part is where the Alpha particles are accelerated to radical speeds." - icon = 'icons/obj/machines/particle_accelerator.dmi' - icon_state = "fuel_chamber" - reference = "fuel_chamber" - -/obj/structure/particle_accelerator/fuel_chamber/update_icon() - ..() - return \ No newline at end of file +/obj/structure/particle_accelerator/fuel_chamber + name = "EM Acceleration Chamber" + desc_holder = "This part is where the Alpha particles are accelerated to radical speeds." + icon = 'icons/obj/machines/particle_accelerator.dmi' + icon_state = "fuel_chamber" + reference = "fuel_chamber" + +/obj/structure/particle_accelerator/fuel_chamber/update_icon() + ..() + return diff --git a/code/modules/power/singularity/particle_accelerator/particle_control.dm b/code/modules/power/singularity/particle_accelerator/particle_control.dm index 80de53fe22d..f1a7f130592 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_control.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_control.dm @@ -1,279 +1,279 @@ -/obj/machinery/particle_accelerator/control_box - name = "Particle Accelerator Control Console" - desc = "This part controls the density of the particles." - icon = 'icons/obj/machines/particle_accelerator.dmi' - icon_state = "control_box" - reference = "control_box" - anchored = 0 - density = 1 - use_power = NO_POWER_USE - idle_power_usage = 500 - active_power_usage = 10000 - construction_state = 0 - active = 0 - dir = 1 - var/strength_upper_limit = 2 - var/interface_control = 1 - var/list/obj/structure/particle_accelerator/connected_parts - var/assembled = 0 - var/parts = null - var/datum/wires/particle_acc/control_box/wires = null - -/obj/machinery/particle_accelerator/control_box/Initialize(mapload) - . = ..() - wires = new(src) - connected_parts = list() - update_icon() - -/obj/machinery/particle_accelerator/control_box/Destroy() - if(active) - toggle_power() - QDEL_NULL(wires) - return ..() - -/obj/machinery/particle_accelerator/control_box/attack_ghost(user as mob) - return attack_hand(user) - -/obj/machinery/particle_accelerator/control_box/attack_hand(mob/user as mob) - if(construction_state >= 3) - interact(user) - else if(construction_state == 2) // Wires exposed - wires.Interact(user) - -/obj/machinery/particle_accelerator/control_box/update_state() - if(construction_state < 3) - use_power = NO_POWER_USE - assembled = 0 - active = 0 - for(var/obj/structure/particle_accelerator/part in connected_parts) - part.strength = null - part.powered = 0 - part.update_icon() - connected_parts = list() - return - if(!part_scan()) - use_power = IDLE_POWER_USE - active = 0 - connected_parts = list() - - return - -/obj/machinery/particle_accelerator/control_box/update_icon() - if(active) - icon_state = "[reference]p[strength]" - else - if(stat & NOPOWER) - icon_state = "[reference]w" - return - else if(use_power && assembled) - icon_state = "[reference]p" - else - switch(construction_state) - if(0) - icon_state = "[reference]" - if(1) - icon_state = "[reference]" - if(2) - icon_state = "[reference]w" - else - icon_state = "[reference]c" - return - -/obj/machinery/particle_accelerator/control_box/Topic(href, href_list) - if(..(href, href_list)) - return 1 - - if(!interface_control) - to_chat(usr, "ERROR: Request timed out. Check wire contacts.") - return - - if(href_list["close"]) - usr << browse(null, "window=pacontrol") - usr.unset_machine() - return - if(href_list["togglep"]) - if(!wires.IsIndexCut(PARTICLE_TOGGLE_WIRE)) - toggle_power() - - else if(href_list["scan"]) - part_scan() - - else if(href_list["strengthup"]) - if(!wires.IsIndexCut(PARTICLE_STRENGTH_WIRE)) - add_strength() - - else if(href_list["strengthdown"]) - if(!wires.IsIndexCut(PARTICLE_STRENGTH_WIRE)) - remove_strength() - - updateDialog() - update_icon() - return - - -/obj/machinery/particle_accelerator/control_box/proc/strength_change() - for(var/obj/structure/particle_accelerator/part in connected_parts) - part.strength = strength - part.update_icon() - -/obj/machinery/particle_accelerator/control_box/proc/add_strength(var/s) - if(assembled) - strength++ - if(strength > strength_upper_limit) - strength = strength_upper_limit - else - message_admins("PA Control Computer increased to [strength] by [key_name_admin(usr)] in ([x],[y],[z] - JMP)",0,1) - log_game("PA Control Computer increased to [strength] by [key_name(usr)] in ([x],[y],[z])") - investigate_log("increased to [strength] by [key_name(usr)]","singulo") - use_log += text("\[[time_stamp()]\] [usr.name] ([key_name(usr)]) has increased the PA Control Computer to [strength].") - - investigate_log("increased to [strength] by [usr.key]","singulo") - strength_change() - -/obj/machinery/particle_accelerator/control_box/proc/remove_strength(var/s) - if(assembled) - strength-- - if(strength < 0) - strength = 0 - else - message_admins("PA Control Computer decreased to [strength] by [key_name_admin(usr)] in ([x],[y],[z] - JMP)",0,1) - log_game("PA Control Computer decreased to [strength] by [key_name(usr)] in ([x],[y],[z])") - investigate_log("decreased to [strength] by [key_name(usr)]","singulo") - use_log += text("\[[time_stamp()]\] [usr.name] ([key_name(usr)]) has decreased the PA Control Computer to [strength].") - - strength_change() - -/obj/machinery/particle_accelerator/control_box/power_change() - ..() - if(stat & NOPOWER) - active = 0 - use_power = NO_POWER_USE - else if(!stat && construction_state <= 3) - use_power = IDLE_POWER_USE - update_icon() - - if((stat & NOPOWER) || (!stat && construction_state <= 3)) //Only update the part icons if something's changed (i.e. any of the above condition sets are met). - for(var/obj/structure/particle_accelerator/part in connected_parts) - part.strength = null - part.powered = 0 - part.update_icon() - return - - -/obj/machinery/particle_accelerator/control_box/process() - if(active) - //a part is missing! - if(length(connected_parts) < 6) - investigate_log("lost a connected part; It powered down.","singulo") - toggle_power() - return - //emit some particles - for(var/obj/structure/particle_accelerator/particle_emitter/PE in connected_parts) - if(PE) - PE.emit_particle(strength) - return - - -/obj/machinery/particle_accelerator/control_box/proc/part_scan() - for(var/obj/structure/particle_accelerator/fuel_chamber/F in orange(1,src)) - dir = F.dir - connected_parts = list() - var/tally = 0 - var/ldir = turn(dir,-90) - var/rdir = turn(dir,90) - var/odir = turn(dir,180) - var/turf/T = loc - T = get_step(T,rdir) - if(check_part(T,/obj/structure/particle_accelerator/fuel_chamber)) - tally++ - T = get_step(T,odir) - if(check_part(T,/obj/structure/particle_accelerator/end_cap)) - tally++ - T = get_step(T,dir) - T = get_step(T,dir) - if(check_part(T,/obj/structure/particle_accelerator/power_box)) - tally++ - T = get_step(T,dir) - if(check_part(T,/obj/structure/particle_accelerator/particle_emitter/center)) - tally++ - T = get_step(T,ldir) - if(check_part(T,/obj/structure/particle_accelerator/particle_emitter/left)) - tally++ - T = get_step(T,rdir) - T = get_step(T,rdir) - if(check_part(T,/obj/structure/particle_accelerator/particle_emitter/right)) - tally++ - if(tally >= 6) - assembled = 1 - return 1 - else - assembled = 0 - return 0 - - -/obj/machinery/particle_accelerator/control_box/proc/check_part(var/turf/T, var/type) - if(!(T)||!(type)) - return 0 - var/obj/structure/particle_accelerator/PA = locate(/obj/structure/particle_accelerator) in T - if(istype(PA, type)) - if(PA.connect_master(src)) - if(PA.report_ready(src)) - connected_parts.Add(PA) - return 1 - return 0 - - -/obj/machinery/particle_accelerator/control_box/proc/toggle_power() - active = !active - investigate_log("turned [active?"ON":"OFF"] by [usr ? usr.key : "outside forces"]","singulo") - if(active) - msg_admin_attack("PA Control Computer turned ON by [key_name_admin(usr)]", ATKLOG_FEW) - log_game("PA Control Computer turned ON by [key_name(usr)] in ([x],[y],[z])") - use_log += text("\[[time_stamp()]\] [key_name(usr)] has turned on the PA Control Computer.") - if(active) - use_power = ACTIVE_POWER_USE - for(var/obj/structure/particle_accelerator/part in connected_parts) - part.strength = strength - part.powered = 1 - part.update_icon() - else - use_power = IDLE_POWER_USE - for(var/obj/structure/particle_accelerator/part in connected_parts) - part.strength = null - part.powered = 0 - part.update_icon() - return 1 - - -/obj/machinery/particle_accelerator/control_box/interact(mob/user) - if(((get_dist(src, user) > 1) && !isobserver(user)) || (stat & (BROKEN|NOPOWER))) - if(!istype(user, /mob/living/silicon)) - user.unset_machine() - user << browse(null, "window=pacontrol") - return - user.set_machine(src) - - var/dat = "" - dat += "Close

        " - dat += "

        Status

        " - if(!assembled) - dat += "Unable to detect all parts!
        " - dat += "Run Scan

        " - else - dat += "All parts in place.

        " - dat += "Power:" - if(active) - dat += "On
        " - else - dat += "Off
        " - dat += "Toggle Power

        " - dat += "Particle Strength: [strength] " - dat += "--|++

        " - - //user << browse(dat, "window=pacontrol;size=420x500") - //onclose(user, "pacontrol") - var/datum/browser/popup = new(user, "pacontrol", name, 420, 500) - popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) - popup.open() - return +/obj/machinery/particle_accelerator/control_box + name = "Particle Accelerator Control Console" + desc = "This part controls the density of the particles." + icon = 'icons/obj/machines/particle_accelerator.dmi' + icon_state = "control_box" + reference = "control_box" + anchored = 0 + density = 1 + use_power = NO_POWER_USE + idle_power_usage = 500 + active_power_usage = 10000 + construction_state = 0 + active = 0 + dir = 1 + var/strength_upper_limit = 2 + var/interface_control = 1 + var/list/obj/structure/particle_accelerator/connected_parts + var/assembled = 0 + var/parts = null + var/datum/wires/particle_acc/control_box/wires = null + +/obj/machinery/particle_accelerator/control_box/Initialize(mapload) + . = ..() + wires = new(src) + connected_parts = list() + update_icon() + +/obj/machinery/particle_accelerator/control_box/Destroy() + if(active) + toggle_power() + QDEL_NULL(wires) + return ..() + +/obj/machinery/particle_accelerator/control_box/attack_ghost(user as mob) + return attack_hand(user) + +/obj/machinery/particle_accelerator/control_box/attack_hand(mob/user as mob) + if(construction_state >= 3) + interact(user) + else if(construction_state == 2) // Wires exposed + wires.Interact(user) + +/obj/machinery/particle_accelerator/control_box/update_state() + if(construction_state < 3) + use_power = NO_POWER_USE + assembled = 0 + active = 0 + for(var/obj/structure/particle_accelerator/part in connected_parts) + part.strength = null + part.powered = 0 + part.update_icon() + connected_parts = list() + return + if(!part_scan()) + use_power = IDLE_POWER_USE + active = 0 + connected_parts = list() + + return + +/obj/machinery/particle_accelerator/control_box/update_icon() + if(active) + icon_state = "[reference]p[strength]" + else + if(stat & NOPOWER) + icon_state = "[reference]w" + return + else if(use_power && assembled) + icon_state = "[reference]p" + else + switch(construction_state) + if(0) + icon_state = "[reference]" + if(1) + icon_state = "[reference]" + if(2) + icon_state = "[reference]w" + else + icon_state = "[reference]c" + return + +/obj/machinery/particle_accelerator/control_box/Topic(href, href_list) + if(..(href, href_list)) + return 1 + + if(!interface_control) + to_chat(usr, "ERROR: Request timed out. Check wire contacts.") + return + + if(href_list["close"]) + usr << browse(null, "window=pacontrol") + usr.unset_machine() + return + if(href_list["togglep"]) + if(!wires.IsIndexCut(PARTICLE_TOGGLE_WIRE)) + toggle_power() + + else if(href_list["scan"]) + part_scan() + + else if(href_list["strengthup"]) + if(!wires.IsIndexCut(PARTICLE_STRENGTH_WIRE)) + add_strength() + + else if(href_list["strengthdown"]) + if(!wires.IsIndexCut(PARTICLE_STRENGTH_WIRE)) + remove_strength() + + updateDialog() + update_icon() + return + + +/obj/machinery/particle_accelerator/control_box/proc/strength_change() + for(var/obj/structure/particle_accelerator/part in connected_parts) + part.strength = strength + part.update_icon() + +/obj/machinery/particle_accelerator/control_box/proc/add_strength(var/s) + if(assembled) + strength++ + if(strength > strength_upper_limit) + strength = strength_upper_limit + else + message_admins("PA Control Computer increased to [strength] by [key_name_admin(usr)] in ([x],[y],[z] - JMP)",0,1) + log_game("PA Control Computer increased to [strength] by [key_name(usr)] in ([x],[y],[z])") + investigate_log("increased to [strength] by [key_name(usr)]","singulo") + use_log += text("\[[time_stamp()]\] [usr.name] ([key_name(usr)]) has increased the PA Control Computer to [strength].") + + investigate_log("increased to [strength] by [usr.key]","singulo") + strength_change() + +/obj/machinery/particle_accelerator/control_box/proc/remove_strength(var/s) + if(assembled) + strength-- + if(strength < 0) + strength = 0 + else + message_admins("PA Control Computer decreased to [strength] by [key_name_admin(usr)] in ([x],[y],[z] - JMP)",0,1) + log_game("PA Control Computer decreased to [strength] by [key_name(usr)] in ([x],[y],[z])") + investigate_log("decreased to [strength] by [key_name(usr)]","singulo") + use_log += text("\[[time_stamp()]\] [usr.name] ([key_name(usr)]) has decreased the PA Control Computer to [strength].") + + strength_change() + +/obj/machinery/particle_accelerator/control_box/power_change() + ..() + if(stat & NOPOWER) + active = 0 + use_power = NO_POWER_USE + else if(!stat && construction_state <= 3) + use_power = IDLE_POWER_USE + update_icon() + + if((stat & NOPOWER) || (!stat && construction_state <= 3)) //Only update the part icons if something's changed (i.e. any of the above condition sets are met). + for(var/obj/structure/particle_accelerator/part in connected_parts) + part.strength = null + part.powered = 0 + part.update_icon() + return + + +/obj/machinery/particle_accelerator/control_box/process() + if(active) + //a part is missing! + if(length(connected_parts) < 6) + investigate_log("lost a connected part; It powered down.","singulo") + toggle_power() + return + //emit some particles + for(var/obj/structure/particle_accelerator/particle_emitter/PE in connected_parts) + if(PE) + PE.emit_particle(strength) + return + + +/obj/machinery/particle_accelerator/control_box/proc/part_scan() + for(var/obj/structure/particle_accelerator/fuel_chamber/F in orange(1,src)) + dir = F.dir + connected_parts = list() + var/tally = 0 + var/ldir = turn(dir,-90) + var/rdir = turn(dir,90) + var/odir = turn(dir,180) + var/turf/T = loc + T = get_step(T,rdir) + if(check_part(T,/obj/structure/particle_accelerator/fuel_chamber)) + tally++ + T = get_step(T,odir) + if(check_part(T,/obj/structure/particle_accelerator/end_cap)) + tally++ + T = get_step(T,dir) + T = get_step(T,dir) + if(check_part(T,/obj/structure/particle_accelerator/power_box)) + tally++ + T = get_step(T,dir) + if(check_part(T,/obj/structure/particle_accelerator/particle_emitter/center)) + tally++ + T = get_step(T,ldir) + if(check_part(T,/obj/structure/particle_accelerator/particle_emitter/left)) + tally++ + T = get_step(T,rdir) + T = get_step(T,rdir) + if(check_part(T,/obj/structure/particle_accelerator/particle_emitter/right)) + tally++ + if(tally >= 6) + assembled = 1 + return 1 + else + assembled = 0 + return 0 + + +/obj/machinery/particle_accelerator/control_box/proc/check_part(var/turf/T, var/type) + if(!(T)||!(type)) + return 0 + var/obj/structure/particle_accelerator/PA = locate(/obj/structure/particle_accelerator) in T + if(istype(PA, type)) + if(PA.connect_master(src)) + if(PA.report_ready(src)) + connected_parts.Add(PA) + return 1 + return 0 + + +/obj/machinery/particle_accelerator/control_box/proc/toggle_power() + active = !active + investigate_log("turned [active?"ON":"OFF"] by [usr ? usr.key : "outside forces"]","singulo") + if(active) + msg_admin_attack("PA Control Computer turned ON by [key_name_admin(usr)]", ATKLOG_FEW) + log_game("PA Control Computer turned ON by [key_name(usr)] in ([x],[y],[z])") + use_log += text("\[[time_stamp()]\] [key_name(usr)] has turned on the PA Control Computer.") + if(active) + use_power = ACTIVE_POWER_USE + for(var/obj/structure/particle_accelerator/part in connected_parts) + part.strength = strength + part.powered = 1 + part.update_icon() + else + use_power = IDLE_POWER_USE + for(var/obj/structure/particle_accelerator/part in connected_parts) + part.strength = null + part.powered = 0 + part.update_icon() + return 1 + + +/obj/machinery/particle_accelerator/control_box/interact(mob/user) + if(((get_dist(src, user) > 1) && !isobserver(user)) || (stat & (BROKEN|NOPOWER))) + if(!istype(user, /mob/living/silicon)) + user.unset_machine() + user << browse(null, "window=pacontrol") + return + user.set_machine(src) + + var/dat = "" + dat += "Close

        " + dat += "

        Status

        " + if(!assembled) + dat += "Unable to detect all parts!
        " + dat += "Run Scan

        " + else + dat += "All parts in place.

        " + dat += "Power:" + if(active) + dat += "On
        " + else + dat += "Off
        " + dat += "Toggle Power

        " + dat += "Particle Strength: [strength] " + dat += "--|++

        " + + //user << browse(dat, "window=pacontrol;size=420x500") + //onclose(user, "pacontrol") + var/datum/browser/popup = new(user, "pacontrol", name, 420, 500) + popup.set_content(dat) + popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) + popup.open() + return diff --git a/code/modules/power/singularity/particle_accelerator/particle_emitter.dm b/code/modules/power/singularity/particle_accelerator/particle_emitter.dm index c2a1ccfa8da..a3a7d4b2cfc 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_emitter.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_emitter.dm @@ -1,48 +1,48 @@ -/obj/structure/particle_accelerator/particle_emitter - name = "EM Containment Grid" - desc_holder = "This part launches the Alpha particles. You might not want to stand near this end." - icon = 'icons/obj/machines/particle_accelerator.dmi' - icon_state = "none" - var/fire_delay = 50 - var/last_shot = 0 - -/obj/structure/particle_accelerator/particle_emitter/center - icon_state = "emitter_center" - reference = "emitter_center" - -/obj/structure/particle_accelerator/particle_emitter/left - icon_state = "emitter_left" - reference = "emitter_left" - -/obj/structure/particle_accelerator/particle_emitter/right - icon_state = "emitter_right" - reference = "emitter_right" - -/obj/structure/particle_accelerator/particle_emitter/update_icon() - ..() - return - -/obj/structure/particle_accelerator/particle_emitter/proc/set_delay(var/delay) - if(delay && delay >= 0) - fire_delay = delay - return 1 - return 0 - - -/obj/structure/particle_accelerator/particle_emitter/proc/emit_particle(strength = 0) - if((last_shot + fire_delay) <= world.time) - last_shot = world.time - var/turf/T = get_turf(src) - var/obj/effect/accelerated_particle/P - switch(strength) - if(0) - P = new/obj/effect/accelerated_particle/weak(T) - if(1) - P = new/obj/effect/accelerated_particle(T) - if(2) - P = new/obj/effect/accelerated_particle/strong(T) - if(3) - P = new/obj/effect/accelerated_particle/powerful(T) - P.setDir(dir) - return TRUE - return FALSE \ No newline at end of file +/obj/structure/particle_accelerator/particle_emitter + name = "EM Containment Grid" + desc_holder = "This part launches the Alpha particles. You might not want to stand near this end." + icon = 'icons/obj/machines/particle_accelerator.dmi' + icon_state = "none" + var/fire_delay = 50 + var/last_shot = 0 + +/obj/structure/particle_accelerator/particle_emitter/center + icon_state = "emitter_center" + reference = "emitter_center" + +/obj/structure/particle_accelerator/particle_emitter/left + icon_state = "emitter_left" + reference = "emitter_left" + +/obj/structure/particle_accelerator/particle_emitter/right + icon_state = "emitter_right" + reference = "emitter_right" + +/obj/structure/particle_accelerator/particle_emitter/update_icon() + ..() + return + +/obj/structure/particle_accelerator/particle_emitter/proc/set_delay(var/delay) + if(delay && delay >= 0) + fire_delay = delay + return 1 + return 0 + + +/obj/structure/particle_accelerator/particle_emitter/proc/emit_particle(strength = 0) + if((last_shot + fire_delay) <= world.time) + last_shot = world.time + var/turf/T = get_turf(src) + var/obj/effect/accelerated_particle/P + switch(strength) + if(0) + P = new/obj/effect/accelerated_particle/weak(T) + if(1) + P = new/obj/effect/accelerated_particle(T) + if(2) + P = new/obj/effect/accelerated_particle/strong(T) + if(3) + P = new/obj/effect/accelerated_particle/powerful(T) + P.setDir(dir) + return TRUE + return FALSE diff --git a/code/modules/power/singularity/particle_accelerator/particle_power.dm b/code/modules/power/singularity/particle_accelerator/particle_power.dm index 75ae7eb3263..527c745436e 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_power.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_power.dm @@ -1,6 +1,6 @@ -/obj/structure/particle_accelerator/power_box - name = "Particle Focusing EM Lens" - desc_holder = "This part uses electromagnetic waves to focus the Alpha particles." - icon = 'icons/obj/machines/particle_accelerator.dmi' - icon_state = "power_box" - reference = "power_box" \ No newline at end of file +/obj/structure/particle_accelerator/power_box + name = "Particle Focusing EM Lens" + desc_holder = "This part uses electromagnetic waves to focus the Alpha particles." + icon = 'icons/obj/machines/particle_accelerator.dmi' + icon_state = "power_box" + reference = "power_box" diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm index c67e676ee13..fcc37bf943b 100644 --- a/code/modules/power/singularity/singularity.dm +++ b/code/modules/power/singularity/singularity.dm @@ -1,443 +1,443 @@ -/obj/singularity - name = "gravitational singularity" - desc = "A gravitational singularity." - icon = 'icons/obj/singularity.dmi' - icon_state = "singularity_s1" - anchored = 1 - density = 1 - layer = MASSIVE_OBJ_LAYER - light_range = 6 - appearance_flags = 0 - var/current_size = 1 - var/allowed_size = 1 - var/contained = 1 //Are we going to move around? - var/energy = 100 //How strong are we? - var/dissipate = 1 //Do we lose energy over time? - var/dissipate_delay = 10 - var/dissipate_track = 0 - var/dissipate_strength = 1 //How much energy do we lose? - var/move_self = 1 //Do we move on our own? - var/grav_pull = 4 //How many tiles out do we pull? - move_resist = INFINITY //no, you don't get to push the singulo. Not even you OP wizard gateway statues - var/consume_range = 0 //How many tiles out do we eat - var/event_chance = 15 //Prob for event each tick - var/target = null //its target. moves towards the target if it has one - var/last_failed_movement = 0//Will not move in the same dir if it couldnt before, will help with the getting stuck on fields thing - var/last_warning - var/consumedSupermatter = 0 //If the singularity has eaten a supermatter shard and can go to stage six - allow_spin = 0 - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF - -/obj/singularity/New(loc, var/starting_energy = 50, var/temp = 0) - //CARN: admin-alert for chuckle-fuckery. - admin_investigate_setup() - - src.energy = starting_energy - ..() - START_PROCESSING(SSobj, src) - GLOB.poi_list |= src - GLOB.singularities += src - for(var/obj/machinery/power/singularity_beacon/singubeacon in GLOB.machines) - if(singubeacon.active) - target = singubeacon - break - -/obj/singularity/Destroy() - STOP_PROCESSING(SSobj, src) - GLOB.poi_list.Remove(src) - GLOB.singularities -= src - target = null - return ..() - -/obj/singularity/Move(atom/newloc, direct) - if(current_size >= STAGE_FIVE || check_turfs_in(direct)) - last_failed_movement = 0//Reset this because we moved - return ..() - else - last_failed_movement = direct - return 0 - - -/obj/singularity/attack_hand(mob/user) - consume(user) - return 1 - -/obj/singularity/attack_alien(mob/user) - consume(user) - -/obj/singularity/attack_animal(mob/user) - consume(user) - -/obj/singularity/attackby(obj/item/W, mob/user, params) - consume(user) - return 1 - -/obj/singularity/Process_Spacemove() //The singularity stops drifting for no man! - return 0 - -/obj/singularity/blob_act(obj/structure/blob/B) - return - -/obj/singularity/ex_act(severity) - switch(severity) - if(1) - if(current_size <= STAGE_TWO) - investigate_log("has been destroyed by a heavy explosion.","singulo") - qdel(src) - return - else - energy -= round(((energy+1)/2),1) - if(2) - energy -= round(((energy+1)/3),1) - if(3) - energy -= round(((energy+1)/4),1) - return - - -/obj/singularity/bullet_act(obj/item/projectile/P) - qdel(P) - return 0 //Will there be an impact? Who knows. Will we see it? No. - - -/obj/singularity/Bump(atom/A) - consume(A) - return - - -/obj/singularity/Bumped(atom/A) - consume(A) - return - - -/obj/singularity/process() - if(allowed_size >= STAGE_TWO) - // Start moving even before we reach "true" stage two. - // If we are stage one and are sufficiently energetic to be allowed to 2, - // it might mean we are stuck in a corner somewere. So move around to try to expand. - move() - if(current_size >= STAGE_TWO) - pulse() - if(prob(event_chance))//Chance for it to run a special event TODO:Come up with one or two more that fit - event() - eat() - dissipate() - check_energy() - - return - - -/obj/singularity/attack_ai() //to prevent ais from gibbing themselves when they click on one. - return - - -/obj/singularity/proc/admin_investigate_setup() - last_warning = world.time - var/count = locate(/obj/machinery/field/containment) in urange(30, src, 1) - if(!count) - message_admins("A singularity has been created without containment fields active at [x], [y], [z] (JMP)") - investigate_log("was created. [count?"":"No containment fields were active"]","singulo") - -/obj/singularity/proc/dissipate() - if(!dissipate) - return - if(dissipate_track >= dissipate_delay) - src.energy -= dissipate_strength - dissipate_track = 0 - else - dissipate_track++ - - -/obj/singularity/proc/expand(force_size = 0) - var/temp_allowed_size = src.allowed_size - if(force_size) - temp_allowed_size = force_size - if(temp_allowed_size >= STAGE_SIX && !consumedSupermatter) - temp_allowed_size = STAGE_FIVE - switch(temp_allowed_size) - if(STAGE_ONE) - current_size = STAGE_ONE - icon = 'icons/obj/singularity.dmi' - icon_state = "singularity_s1" - pixel_x = 0 - pixel_y = 0 - grav_pull = 4 - consume_range = 0 - dissipate_delay = 10 - dissipate_track = 0 - dissipate_strength = 1 - if(STAGE_TWO) - if((check_turfs_in(1,1))&&(check_turfs_in(2,1))&&(check_turfs_in(4,1))&&(check_turfs_in(8,1))) - current_size = STAGE_TWO - icon = 'icons/effects/96x96.dmi' - icon_state = "singularity_s3" - pixel_x = -32 - pixel_y = -32 - grav_pull = 6 - consume_range = 1 - dissipate_delay = 5 - dissipate_track = 0 - dissipate_strength = 5 - if(STAGE_THREE) - if((check_turfs_in(1,2))&&(check_turfs_in(2,2))&&(check_turfs_in(4,2))&&(check_turfs_in(8,2))) - current_size = STAGE_THREE - icon = 'icons/effects/160x160.dmi' - icon_state = "singularity_s5" - pixel_x = -64 - pixel_y = -64 - grav_pull = 8 - consume_range = 2 - dissipate_delay = 4 - dissipate_track = 0 - dissipate_strength = 20 - if(STAGE_FOUR) - if((check_turfs_in(1,3))&&(check_turfs_in(2,3))&&(check_turfs_in(4,3))&&(check_turfs_in(8,3))) - current_size = STAGE_FOUR - icon = 'icons/effects/224x224.dmi' - icon_state = "singularity_s7" - pixel_x = -96 - pixel_y = -96 - grav_pull = 10 - consume_range = 3 - dissipate_delay = 10 - dissipate_track = 0 - dissipate_strength = 10 - if(STAGE_FIVE)//this one also lacks a check for gens because it eats everything - current_size = STAGE_FIVE - icon = 'icons/effects/288x288.dmi' - icon_state = "singularity_s9" - pixel_x = -128 - pixel_y = -128 - grav_pull = 10 - consume_range = 4 - dissipate = 0 //It cant go smaller due to e loss - if(STAGE_SIX) //This only happens if a stage 5 singulo consumes a supermatter shard. - current_size = STAGE_SIX - icon = 'icons/effects/352x352.dmi' - icon_state = "singularity_s11" - pixel_x = -160 - pixel_y = -160 - grav_pull = 15 - consume_range = 5 - dissipate = 0 - if(current_size == allowed_size) - investigate_log("grew to size [current_size]","singulo") - return 1 - else if(current_size < (--temp_allowed_size)) - expand(temp_allowed_size) - else - return 0 - - -/obj/singularity/proc/check_energy() - if(energy <= 0) - investigate_log("collapsed.","singulo") - qdel(src) - return 0 - switch(energy)//Some of these numbers might need to be changed up later -Mport - if(1 to 199) - allowed_size = STAGE_ONE - if(200 to 499) - allowed_size = STAGE_TWO - if(500 to 999) - allowed_size = STAGE_THREE - if(1000 to 1999) - allowed_size = STAGE_FOUR - if(2000 to INFINITY) - if(energy >= 3000 && consumedSupermatter) - allowed_size = STAGE_SIX - else - allowed_size = STAGE_FIVE - if(current_size != allowed_size) - expand() - return 1 - - -/obj/singularity/proc/eat() - set background = BACKGROUND_ENABLED - for(var/tile in spiral_range_turfs(grav_pull, src)) - var/turf/T = tile - if(!T || !isturf(loc)) - continue - if(get_dist(T, src) > consume_range) - T.singularity_pull(src, current_size) - else - consume(T) - for(var/thing in T) - if(isturf(loc) && thing != src) - var/atom/movable/X = thing - if(get_dist(X, src) > consume_range) - X.singularity_pull(src, current_size) - else - consume(X) - CHECK_TICK - - -/obj/singularity/proc/consume(atom/A) - var/gain = A.singularity_act(current_size) - src.energy += gain - if(istype(A, /obj/machinery/power/supermatter_shard) && !consumedSupermatter) - desc = "[initial(desc)] It glows fiercely with inner fire." - name = "supermatter-charged [initial(name)]" - consumedSupermatter = 1 - set_light(10) - return - - -/obj/singularity/proc/move(force_move = 0) - if(!move_self) - return 0 - - var/movement_dir = pick(alldirs - last_failed_movement) - - if(force_move) - movement_dir = force_move - - if(target && prob(60)) - movement_dir = get_dir(src,target) //moves to a singulo beacon, if there is one - - step(src, movement_dir) - - -/obj/singularity/proc/check_turfs_in(direction = 0, step = 0) - if(!direction) - return 0 - var/steps = 0 - if(!step) - switch(current_size) - if(STAGE_ONE) - steps = 1 - if(STAGE_TWO) - steps = 3//Yes this is right - if(STAGE_THREE) - steps = 3 - if(STAGE_FOUR) - steps = 4 - if(STAGE_FIVE) - steps = 5 - else - steps = step - var/list/turfs = list() - var/turf/T = src.loc - for(var/i = 1 to steps) - T = get_step(T,direction) - if(!isturf(T)) - return 0 - turfs.Add(T) - var/dir2 = 0 - var/dir3 = 0 - switch(direction) - if(NORTH||SOUTH) - dir2 = 4 - dir3 = 8 - if(EAST||WEST) - dir2 = 1 - dir3 = 2 - var/turf/T2 = T - for(var/j = 1 to steps-1) - T2 = get_step(T2,dir2) - if(!isturf(T2)) - return 0 - turfs.Add(T2) - for(var/k = 1 to steps-1) - T = get_step(T,dir3) - if(!isturf(T)) - return 0 - turfs.Add(T) - for(var/turf/T3 in turfs) - if(isnull(T3)) - continue - if(!can_move(T3)) - return 0 - return 1 - - -/obj/singularity/proc/can_move(turf/T) - if(!T) - return 0 - if((locate(/obj/machinery/field/containment) in T)||(locate(/obj/machinery/shieldwall) in T)) - return 0 - else if(locate(/obj/machinery/field/generator) in T) - var/obj/machinery/field/generator/G = locate(/obj/machinery/field/generator) in T - if(G && G.active) - return 0 - else if(locate(/obj/machinery/shieldwallgen) in T) - var/obj/machinery/shieldwallgen/S = locate(/obj/machinery/shieldwallgen) in T - if(S && S.active) - return 0 - return 1 - - -/obj/singularity/proc/event() - var/numb = pick(1,2,3,4,5,6) - switch(numb) - if(1)//EMP - emp_area() - if(2,3)//tox damage all carbon mobs in area - toxmob() - if(4)//Stun mobs who lack optic scanners - mezzer() - if(5,6) //Sets all nearby mobs on fire - if(current_size < STAGE_SIX) - return 0 - combust_mobs() - else - return 0 - return 1 - - -/obj/singularity/proc/toxmob() - var/toxrange = 10 - var/radiation = 15 - var/radiationmin = 3 - if(energy>200) - radiation += round((energy-150)/10,1) - radiationmin = round((radiation/5),1) - for(var/mob/living/M in view(toxrange, src.loc)) - M.apply_effect(rand(radiationmin,radiation), IRRADIATE) - - -/obj/singularity/proc/combust_mobs() - for(var/mob/living/carbon/C in urange(20, src, 1)) - C.visible_message("[C]'s skin bursts into flame!", \ - "You feel an inner fire as your skin bursts into flames!") - C.adjust_fire_stacks(5) - C.IgniteMob() - return - - -/obj/singularity/proc/mezzer() - for(var/mob/living/carbon/M in oviewers(8, src)) - if(istype(M, /mob/living/carbon/brain)) //Ignore brains - continue - - if(M.stat == CONSCIOUS) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(istype(H.glasses, /obj/item/clothing/glasses/meson)) - var/obj/item/clothing/glasses/meson/MS = H.glasses - if(MS.vision_flags == SEE_TURFS) - to_chat(H, "You look directly into the [src.name], good thing you had your protective eyewear on!") - return - - M.apply_effect(3, STUN) - M.visible_message("[M] stares blankly at the [src.name]!", \ - "You look directly into the [src.name] and feel weak.") - return - - -/obj/singularity/proc/emp_area() - empulse(src, 8, 10) - return - - -/obj/singularity/proc/pulse() - for(var/obj/machinery/power/rad_collector/R in rad_collectors) - if(R.z == z && get_dist(R, src) <= 15) // Better than using orange() every process - R.receive_pulse(energy) - -/obj/singularity/singularity_act() - var/gain = (energy/2) - var/dist = max((current_size - 2),1) - explosion(src.loc,(dist),(dist*2),(dist*4)) - qdel(src) - return(gain) +/obj/singularity + name = "gravitational singularity" + desc = "A gravitational singularity." + icon = 'icons/obj/singularity.dmi' + icon_state = "singularity_s1" + anchored = 1 + density = 1 + layer = MASSIVE_OBJ_LAYER + light_range = 6 + appearance_flags = 0 + var/current_size = 1 + var/allowed_size = 1 + var/contained = 1 //Are we going to move around? + var/energy = 100 //How strong are we? + var/dissipate = 1 //Do we lose energy over time? + var/dissipate_delay = 10 + var/dissipate_track = 0 + var/dissipate_strength = 1 //How much energy do we lose? + var/move_self = 1 //Do we move on our own? + var/grav_pull = 4 //How many tiles out do we pull? + move_resist = INFINITY //no, you don't get to push the singulo. Not even you OP wizard gateway statues + var/consume_range = 0 //How many tiles out do we eat + var/event_chance = 15 //Prob for event each tick + var/target = null //its target. moves towards the target if it has one + var/last_failed_movement = 0//Will not move in the same dir if it couldnt before, will help with the getting stuck on fields thing + var/last_warning + var/consumedSupermatter = 0 //If the singularity has eaten a supermatter shard and can go to stage six + allow_spin = 0 + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF | FREEZE_PROOF + +/obj/singularity/New(loc, var/starting_energy = 50, var/temp = 0) + //CARN: admin-alert for chuckle-fuckery. + admin_investigate_setup() + + src.energy = starting_energy + ..() + START_PROCESSING(SSobj, src) + GLOB.poi_list |= src + GLOB.singularities += src + for(var/obj/machinery/power/singularity_beacon/singubeacon in GLOB.machines) + if(singubeacon.active) + target = singubeacon + break + +/obj/singularity/Destroy() + STOP_PROCESSING(SSobj, src) + GLOB.poi_list.Remove(src) + GLOB.singularities -= src + target = null + return ..() + +/obj/singularity/Move(atom/newloc, direct) + if(current_size >= STAGE_FIVE || check_turfs_in(direct)) + last_failed_movement = 0//Reset this because we moved + return ..() + else + last_failed_movement = direct + return 0 + + +/obj/singularity/attack_hand(mob/user) + consume(user) + return 1 + +/obj/singularity/attack_alien(mob/user) + consume(user) + +/obj/singularity/attack_animal(mob/user) + consume(user) + +/obj/singularity/attackby(obj/item/W, mob/user, params) + consume(user) + return 1 + +/obj/singularity/Process_Spacemove() //The singularity stops drifting for no man! + return 0 + +/obj/singularity/blob_act(obj/structure/blob/B) + return + +/obj/singularity/ex_act(severity) + switch(severity) + if(1) + if(current_size <= STAGE_TWO) + investigate_log("has been destroyed by a heavy explosion.","singulo") + qdel(src) + return + else + energy -= round(((energy+1)/2),1) + if(2) + energy -= round(((energy+1)/3),1) + if(3) + energy -= round(((energy+1)/4),1) + return + + +/obj/singularity/bullet_act(obj/item/projectile/P) + qdel(P) + return 0 //Will there be an impact? Who knows. Will we see it? No. + + +/obj/singularity/Bump(atom/A) + consume(A) + return + + +/obj/singularity/Bumped(atom/A) + consume(A) + return + + +/obj/singularity/process() + if(allowed_size >= STAGE_TWO) + // Start moving even before we reach "true" stage two. + // If we are stage one and are sufficiently energetic to be allowed to 2, + // it might mean we are stuck in a corner somewere. So move around to try to expand. + move() + if(current_size >= STAGE_TWO) + pulse() + if(prob(event_chance))//Chance for it to run a special event TODO:Come up with one or two more that fit + event() + eat() + dissipate() + check_energy() + + return + + +/obj/singularity/attack_ai() //to prevent ais from gibbing themselves when they click on one. + return + + +/obj/singularity/proc/admin_investigate_setup() + last_warning = world.time + var/count = locate(/obj/machinery/field/containment) in urange(30, src, 1) + if(!count) + message_admins("A singularity has been created without containment fields active at [x], [y], [z] (JMP)") + investigate_log("was created. [count?"":"No containment fields were active"]","singulo") + +/obj/singularity/proc/dissipate() + if(!dissipate) + return + if(dissipate_track >= dissipate_delay) + src.energy -= dissipate_strength + dissipate_track = 0 + else + dissipate_track++ + + +/obj/singularity/proc/expand(force_size = 0) + var/temp_allowed_size = src.allowed_size + if(force_size) + temp_allowed_size = force_size + if(temp_allowed_size >= STAGE_SIX && !consumedSupermatter) + temp_allowed_size = STAGE_FIVE + switch(temp_allowed_size) + if(STAGE_ONE) + current_size = STAGE_ONE + icon = 'icons/obj/singularity.dmi' + icon_state = "singularity_s1" + pixel_x = 0 + pixel_y = 0 + grav_pull = 4 + consume_range = 0 + dissipate_delay = 10 + dissipate_track = 0 + dissipate_strength = 1 + if(STAGE_TWO) + if((check_turfs_in(1,1))&&(check_turfs_in(2,1))&&(check_turfs_in(4,1))&&(check_turfs_in(8,1))) + current_size = STAGE_TWO + icon = 'icons/effects/96x96.dmi' + icon_state = "singularity_s3" + pixel_x = -32 + pixel_y = -32 + grav_pull = 6 + consume_range = 1 + dissipate_delay = 5 + dissipate_track = 0 + dissipate_strength = 5 + if(STAGE_THREE) + if((check_turfs_in(1,2))&&(check_turfs_in(2,2))&&(check_turfs_in(4,2))&&(check_turfs_in(8,2))) + current_size = STAGE_THREE + icon = 'icons/effects/160x160.dmi' + icon_state = "singularity_s5" + pixel_x = -64 + pixel_y = -64 + grav_pull = 8 + consume_range = 2 + dissipate_delay = 4 + dissipate_track = 0 + dissipate_strength = 20 + if(STAGE_FOUR) + if((check_turfs_in(1,3))&&(check_turfs_in(2,3))&&(check_turfs_in(4,3))&&(check_turfs_in(8,3))) + current_size = STAGE_FOUR + icon = 'icons/effects/224x224.dmi' + icon_state = "singularity_s7" + pixel_x = -96 + pixel_y = -96 + grav_pull = 10 + consume_range = 3 + dissipate_delay = 10 + dissipate_track = 0 + dissipate_strength = 10 + if(STAGE_FIVE)//this one also lacks a check for gens because it eats everything + current_size = STAGE_FIVE + icon = 'icons/effects/288x288.dmi' + icon_state = "singularity_s9" + pixel_x = -128 + pixel_y = -128 + grav_pull = 10 + consume_range = 4 + dissipate = 0 //It cant go smaller due to e loss + if(STAGE_SIX) //This only happens if a stage 5 singulo consumes a supermatter shard. + current_size = STAGE_SIX + icon = 'icons/effects/352x352.dmi' + icon_state = "singularity_s11" + pixel_x = -160 + pixel_y = -160 + grav_pull = 15 + consume_range = 5 + dissipate = 0 + if(current_size == allowed_size) + investigate_log("grew to size [current_size]","singulo") + return 1 + else if(current_size < (--temp_allowed_size)) + expand(temp_allowed_size) + else + return 0 + + +/obj/singularity/proc/check_energy() + if(energy <= 0) + investigate_log("collapsed.","singulo") + qdel(src) + return 0 + switch(energy)//Some of these numbers might need to be changed up later -Mport + if(1 to 199) + allowed_size = STAGE_ONE + if(200 to 499) + allowed_size = STAGE_TWO + if(500 to 999) + allowed_size = STAGE_THREE + if(1000 to 1999) + allowed_size = STAGE_FOUR + if(2000 to INFINITY) + if(energy >= 3000 && consumedSupermatter) + allowed_size = STAGE_SIX + else + allowed_size = STAGE_FIVE + if(current_size != allowed_size) + expand() + return 1 + + +/obj/singularity/proc/eat() + set background = BACKGROUND_ENABLED + for(var/tile in spiral_range_turfs(grav_pull, src)) + var/turf/T = tile + if(!T || !isturf(loc)) + continue + if(get_dist(T, src) > consume_range) + T.singularity_pull(src, current_size) + else + consume(T) + for(var/thing in T) + if(isturf(loc) && thing != src) + var/atom/movable/X = thing + if(get_dist(X, src) > consume_range) + X.singularity_pull(src, current_size) + else + consume(X) + CHECK_TICK + + +/obj/singularity/proc/consume(atom/A) + var/gain = A.singularity_act(current_size) + src.energy += gain + if(istype(A, /obj/machinery/power/supermatter_shard) && !consumedSupermatter) + desc = "[initial(desc)] It glows fiercely with inner fire." + name = "supermatter-charged [initial(name)]" + consumedSupermatter = 1 + set_light(10) + return + + +/obj/singularity/proc/move(force_move = 0) + if(!move_self) + return 0 + + var/movement_dir = pick(alldirs - last_failed_movement) + + if(force_move) + movement_dir = force_move + + if(target && prob(60)) + movement_dir = get_dir(src,target) //moves to a singulo beacon, if there is one + + step(src, movement_dir) + + +/obj/singularity/proc/check_turfs_in(direction = 0, step = 0) + if(!direction) + return 0 + var/steps = 0 + if(!step) + switch(current_size) + if(STAGE_ONE) + steps = 1 + if(STAGE_TWO) + steps = 3//Yes this is right + if(STAGE_THREE) + steps = 3 + if(STAGE_FOUR) + steps = 4 + if(STAGE_FIVE) + steps = 5 + else + steps = step + var/list/turfs = list() + var/turf/T = src.loc + for(var/i = 1 to steps) + T = get_step(T,direction) + if(!isturf(T)) + return 0 + turfs.Add(T) + var/dir2 = 0 + var/dir3 = 0 + switch(direction) + if(NORTH||SOUTH) + dir2 = 4 + dir3 = 8 + if(EAST||WEST) + dir2 = 1 + dir3 = 2 + var/turf/T2 = T + for(var/j = 1 to steps-1) + T2 = get_step(T2,dir2) + if(!isturf(T2)) + return 0 + turfs.Add(T2) + for(var/k = 1 to steps-1) + T = get_step(T,dir3) + if(!isturf(T)) + return 0 + turfs.Add(T) + for(var/turf/T3 in turfs) + if(isnull(T3)) + continue + if(!can_move(T3)) + return 0 + return 1 + + +/obj/singularity/proc/can_move(turf/T) + if(!T) + return 0 + if((locate(/obj/machinery/field/containment) in T)||(locate(/obj/machinery/shieldwall) in T)) + return 0 + else if(locate(/obj/machinery/field/generator) in T) + var/obj/machinery/field/generator/G = locate(/obj/machinery/field/generator) in T + if(G && G.active) + return 0 + else if(locate(/obj/machinery/shieldwallgen) in T) + var/obj/machinery/shieldwallgen/S = locate(/obj/machinery/shieldwallgen) in T + if(S && S.active) + return 0 + return 1 + + +/obj/singularity/proc/event() + var/numb = pick(1,2,3,4,5,6) + switch(numb) + if(1)//EMP + emp_area() + if(2,3)//tox damage all carbon mobs in area + toxmob() + if(4)//Stun mobs who lack optic scanners + mezzer() + if(5,6) //Sets all nearby mobs on fire + if(current_size < STAGE_SIX) + return 0 + combust_mobs() + else + return 0 + return 1 + + +/obj/singularity/proc/toxmob() + var/toxrange = 10 + var/radiation = 15 + var/radiationmin = 3 + if(energy>200) + radiation += round((energy-150)/10,1) + radiationmin = round((radiation/5),1) + for(var/mob/living/M in view(toxrange, src.loc)) + M.apply_effect(rand(radiationmin,radiation), IRRADIATE) + + +/obj/singularity/proc/combust_mobs() + for(var/mob/living/carbon/C in urange(20, src, 1)) + C.visible_message("[C]'s skin bursts into flame!", \ + "You feel an inner fire as your skin bursts into flames!") + C.adjust_fire_stacks(5) + C.IgniteMob() + return + + +/obj/singularity/proc/mezzer() + for(var/mob/living/carbon/M in oviewers(8, src)) + if(istype(M, /mob/living/carbon/brain)) //Ignore brains + continue + + if(M.stat == CONSCIOUS) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(istype(H.glasses, /obj/item/clothing/glasses/meson)) + var/obj/item/clothing/glasses/meson/MS = H.glasses + if(MS.vision_flags == SEE_TURFS) + to_chat(H, "You look directly into the [src.name], good thing you had your protective eyewear on!") + return + + M.apply_effect(3, STUN) + M.visible_message("[M] stares blankly at the [src.name]!", \ + "You look directly into the [src.name] and feel weak.") + return + + +/obj/singularity/proc/emp_area() + empulse(src, 8, 10) + return + + +/obj/singularity/proc/pulse() + for(var/obj/machinery/power/rad_collector/R in rad_collectors) + if(R.z == z && get_dist(R, src) <= 15) // Better than using orange() every process + R.receive_pulse(energy) + +/obj/singularity/singularity_act() + var/gain = (energy/2) + var/dist = max((current_size - 2),1) + explosion(src.loc,(dist),(dist*2),(dist*4)) + qdel(src) + return(gain) diff --git a/code/modules/power/smes.dm b/code/modules/power/smes.dm index 4773df11395..05451033703 100644 --- a/code/modules/power/smes.dm +++ b/code/modules/power/smes.dm @@ -1,493 +1,493 @@ -// the SMES -// stores power - -#define SMESMAXCHARGELEVEL 200000 -#define SMESMAXOUTPUT 200000 -#define SMESRATE 0.05 // rate of internal charge to external power - - - -/obj/machinery/power/smes - name = "power storage unit" - desc = "A high-capacity superconducting magnetic energy storage (SMES) unit." - icon_state = "smes" - density = TRUE - use_power = NO_POWER_USE - - var/capacity = 5e6 // maximum charge - var/charge = 0 // actual charge - - var/input_attempt = TRUE // 1 = attempting to charge, 0 = not attempting to charge - var/inputting = TRUE // 1 = actually inputting, 0 = not inputting - var/input_level = 50000 // amount of power the SMES attempts to charge by - var/input_level_max = 200000 // cap on input_level - var/input_available = 0 // amount of charge available from input last tick - - var/output_attempt = TRUE // 1 = attempting to output, 0 = not attempting to output - var/outputting = TRUE // 1 = actually outputting, 0 = not outputting - var/output_level = 50000 // amount of power the SMES attempts to output - var/output_level_max = 200000 // cap on output_level - var/output_used = 0 // amount of power actually outputted. may be less than output_level if the powernet returns excess power - - //Holders for powerout event. - var/last_output_attempt = 0 - var/last_input_attempt = 0 - var/last_charge = 0 - - var/name_tag = null - var/obj/machinery/power/terminal/terminal = null - -/obj/machinery/power/smes/Initialize(mapload) - . = ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/smes(null) - component_parts += new /obj/item/stock_parts/cell/high(null) - component_parts += new /obj/item/stock_parts/cell/high(null) - component_parts += new /obj/item/stock_parts/cell/high(null) - component_parts += new /obj/item/stock_parts/cell/high(null) - component_parts += new /obj/item/stock_parts/cell/high(null) - component_parts += new /obj/item/stock_parts/capacitor(null) - component_parts += new /obj/item/stack/cable_coil(null, 5) - RefreshParts() - - dir_loop: - for(var/d in cardinal) - var/turf/T = get_step(src, d) - for(var/obj/machinery/power/terminal/term in T) - if(term && term.dir == turn(d, 180)) - terminal = term - break dir_loop - - if(!terminal) - stat |= BROKEN - return - terminal.master = src - update_icon() - -/obj/machinery/power/smes/upgraded/Initialize(mapload) - . = ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/smes(null) - component_parts += new /obj/item/stock_parts/cell/hyper(null) - component_parts += new /obj/item/stock_parts/cell/hyper(null) - component_parts += new /obj/item/stock_parts/cell/hyper(null) - component_parts += new /obj/item/stock_parts/cell/hyper(null) - component_parts += new /obj/item/stock_parts/cell/hyper(null) - component_parts += new /obj/item/stock_parts/capacitor/super(null) - component_parts += new /obj/item/stack/cable_coil(null, 5) - RefreshParts() - -/obj/machinery/power/smes/RefreshParts() - var/IO = 0 - var/C = 0 - for(var/obj/item/stock_parts/capacitor/CP in component_parts) - IO += CP.rating - input_level_max = 200000 * IO - output_level_max = 200000 * IO - for(var/obj/item/stock_parts/cell/PC in component_parts) - C += PC.maxcharge - capacity = C / (15000) * 1e6 - -/obj/machinery/power/smes/update_icon() - overlays.Cut() - if(stat & BROKEN) return - - overlays += image('icons/obj/power.dmi', "smes-op[outputting]") - - if(inputting == 2) - overlays += image('icons/obj/power.dmi', "smes-oc2") - else if(inputting == 1) - overlays += image('icons/obj/power.dmi', "smes-oc1") - else - if(input_attempt) - overlays += image('icons/obj/power.dmi', "smes-oc0") - - var/clevel = chargedisplay() - if(clevel>0) - overlays += image('icons/obj/power.dmi', "smes-og[clevel]") - return - -/obj/machinery/power/smes/attackby(obj/item/I, mob/user, params) - //opening using screwdriver - if(default_deconstruction_screwdriver(user, "[initial(icon_state)]-o", initial(icon_state), I)) - update_icon() - return - - //changing direction using wrench - if(default_change_direction_wrench(user, I)) - terminal = null - var/turf/T = get_step(src, dir) - for(var/obj/machinery/power/terminal/term in T) - if(term && term.dir == turn(dir, 180)) - terminal = term - terminal.master = src - to_chat(user, "Terminal found.") - break - if(!terminal) - to_chat(user, "No power source found.") - return - stat &= ~BROKEN - update_icon() - return - - //exchanging parts using the RPE - if(exchange_parts(user, I)) - return - - //building and linking a terminal - if(istype(I, /obj/item/stack/cable_coil)) - var/dir = get_dir(user,src) - if(dir & (dir-1))//we don't want diagonal click - return - - if(terminal) //is there already a terminal ? - to_chat(user, "This SMES already has a power terminal!") - return - - if(!panel_open) //is the panel open ? - to_chat(user, "You must open the maintenance panel first!") - return - - var/turf/T = get_turf(user) - if(T.intact) //is the floor plating removed ? - to_chat(user, "You must first remove the floor plating!") - return - - var/obj/item/stack/cable_coil/C = I - if(C.amount < 10) - to_chat(user, "You need more wires.") - return - - if(user.loc == loc) - to_chat(user, "You must not be on the same tile as the [src].") - return - - //Direction the terminal will face to - var/tempDir = get_dir(user, src) - switch(tempDir) - if(NORTHEAST, SOUTHEAST) - tempDir = EAST - if(NORTHWEST, SOUTHWEST) - tempDir = WEST - var/turf/tempLoc = get_step(src, reverse_direction(tempDir)) - if(istype(tempLoc, /turf/space)) - to_chat(user, "You can't build a terminal on space.") - return - else if(istype(tempLoc)) - if(tempLoc.intact) - to_chat(user, "You must remove the floor plating first.") - return - - to_chat(user, "You start adding cable to the [src].") - playsound(loc, C.usesound, 50, 1) - - if(do_after(user, 50, target = src)) - if(!terminal && panel_open) - T = get_turf(user) - var/obj/structure/cable/N = T.get_cable_node() //get the connecting node cable, if there's one - if(prob(50) && electrocute_mob(usr, N, N, 1, TRUE)) //animate the electrocution if uncautious and unlucky - do_sparks(5, 1, src) - return - - C.use(10) // make sure the cable gets used up - user.visible_message(\ - "[user.name] adds the cables and connects the power terminal.",\ - "You add the cables and connect the power terminal.") - - make_terminal(user, tempDir, tempLoc) - terminal.connect_to_network() - return - - //disassembling the terminal - if(istype(I, /obj/item/wirecutters) && terminal && panel_open) - var/turf/T = get_turf(terminal) - if(T.intact) //is the floor plating removed ? - to_chat(user, "You must first expose the power terminal!") - return - - to_chat(user, "You begin to dismantle the power terminal...") - playsound(src.loc, I.usesound, 50, 1) - - if(do_after(user, 50 * I.toolspeed, target = src)) - if(terminal && panel_open) - if(prob(50) && electrocute_mob(usr, terminal.powernet, terminal, 1, TRUE)) //animate the electrocution if uncautious and unlucky - do_sparks(5, 1, src) - return - - //give the wires back and delete the terminal - new /obj/item/stack/cable_coil(T,10) - user.visible_message(\ - "[user.name] cuts the cables and dismantles the power terminal.",\ - "You cut the cables and dismantle the power terminal.") - inputting = 0 //stop inputting, since we have don't have a terminal anymore - qdel(terminal) - return - - //crowbarring it ! - if(default_deconstruction_crowbar(I)) - return - return ..() - -/obj/machinery/power/smes/disconnect_terminal() - if(terminal) - terminal.master = null - terminal = null - return 1 - return 0 - -/obj/machinery/power/smes/proc/make_terminal(user, tempDir, tempLoc) - // create a terminal object at the same position as original turf loc - // wires will attach to this - terminal = new /obj/machinery/power/terminal(tempLoc) - terminal.dir = tempDir - terminal.master = src - -/obj/machinery/power/smes/Destroy() - if(SSticker && SSticker.current_state == GAME_STATE_PLAYING) - var/area/area = get_area(src) - if(area) - message_admins("SMES deleted at ([area.name])") - log_game("SMES deleted at ([area.name])") - investigate_log("deleted at ([area.name])","singulo") - if(terminal) - disconnect_terminal() - return ..() - - return round(5.5*charge/(capacity ? capacity : 5e6)) - -/obj/machinery/power/smes/proc/chargedisplay() - return round(5.5*charge/(capacity ? capacity : 5e6)) - -/obj/machinery/power/smes/process() - if(stat & BROKEN) - return - - //store machine state to see if we need to update the icon overlays - var/last_disp = chargedisplay() - var/last_chrg = inputting - var/last_onln = outputting - - //inputting - if(terminal && input_attempt) - input_available = terminal.surplus() - - if(inputting) - if(input_available > 0) // if there's power available, try to charge - - var/load = min(min((capacity-charge)/SMESRATE, input_level), input_available) // charge at set rate, limited to spare capacity - - charge += load * SMESRATE // increase the charge - - terminal.add_load(load) // add the load to the terminal side network - - else // if not enough capcity - inputting = FALSE // stop inputting - - else - if(input_attempt && input_available > 0) - inputting = TRUE - else - inputting = FALSE - - //outputting - if(output_attempt) - if(outputting) - output_used = min( charge/SMESRATE, output_level) //limit output to that stored - - if (add_avail(output_used)) // add output to powernet if it exists (smes side) - charge -= output_used*SMESRATE // reduce the storage (may be recovered in /restore() if excessive) - else - outputting = FALSE - - if(output_used < 0.0001) // either from no charge or set to 0 - outputting = FALSE - investigate_log("lost power and turned off", "singulo") - else if(output_attempt && charge > output_level && output_level > 0) - outputting = TRUE - else - output_used = 0 - else - outputting = FALSE - - // only update icon if state changed - if(last_disp != chargedisplay() || last_chrg != inputting || last_onln != outputting) - update_icon() - - - -// called after all power processes are finished -// restores charge level to smes if there was excess this ptick -/obj/machinery/power/smes/proc/restore() - if(stat & BROKEN) - return - - if(!outputting) - output_used = 0 - return - - var/excess = powernet.netexcess // this was how much wasn't used on the network last ptick, minus any removed by other SMESes - - excess = min(output_used, excess) // clamp it to how much was actually output by this SMES last ptick - - excess = min((capacity-charge)/SMESRATE, excess) // for safety, also limit recharge by space capacity of SMES (shouldn't happen) - - // now recharge this amount - - var/clev = chargedisplay() - - charge += excess * SMESRATE // restore unused power - powernet.netexcess -= excess // remove the excess from the powernet, so later SMESes don't try to use it - - output_used -= excess - - if(clev != chargedisplay() ) //if needed updates the icons overlay - update_icon() - return - -/obj/machinery/power/smes/attack_ai(mob/user) - add_hiddenprint(user) - ui_interact(user) - -/obj/machinery/power/smes/attack_ghost(mob/user) - ui_interact(user) - -/obj/machinery/power/smes/attack_hand(mob/user) - add_fingerprint(user) - ui_interact(user) - -/obj/machinery/power/smes/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - if(stat & BROKEN) - return - - - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "smes.tmpl", "SMES Power Storage Unit", 540, 380) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - -/obj/machinery/power/smes/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - - data["nameTag"] = name_tag - data["storedCapacity"] = round(100.0*charge/capacity, 0.1) - data["charging"] = inputting - data["chargeMode"] = input_attempt - data["chargeLevel"] = input_level - data["chargeMax"] = input_level_max - data["outputOnline"] = output_attempt - data["outputLevel"] = output_level - data["outputMax"] = output_level_max - data["outputLoad"] = round(output_used) - - if(outputting) - data["outputting"] = 2 // smes is outputting - else if(!outputting && output_attempt) - data["outputting"] = 1 // smes is online but not outputting because it's charge level is too low - else - data["outputting"] = 0 // smes is not outputting - - return data - -/obj/machinery/power/smes/Topic(href, href_list) - if(..()) - return 1 - - if( href_list["cmode"] ) - inputting(!input_attempt) - update_icon() - - else if( href_list["online"] ) - outputting(!output_attempt) - update_icon() - - else if( href_list["input"] ) - switch( href_list["input"] ) - if("min") - input_level = 0 - if("max") - input_level = input_level_max - if("set") - input_level = input(usr, "Enter new input level (0-[input_level_max])", "SMES Input Power Control", input_level) as num - input_level = max(0, min(input_level_max, input_level)) // clamp to range - - else if( href_list["output"] ) - switch( href_list["output"] ) - if("min") - output_level = 0 - if("max") - output_level = output_level_max - if("set") - output_level = input(usr, "Enter new output level (0-[output_level_max])", "SMES Output Power Control", output_level) as num - output_level = max(0, min(output_level_max, output_level)) // clamp to range - - investigate_log("input/output; [input_level>output_level?"":""][input_level]/[output_level] | Output-mode: [output_attempt?"on":"off"] | Input-mode: [input_attempt?"auto":"off"] by [usr.key]","singulo") - - return 1 - -/obj/machinery/power/smes/proc/ion_act() - if(is_station_level(src.z)) - if(prob(1)) //explosion - for(var/mob/M in viewers(src)) - M.show_message("The [src.name] is making strange noises!", 3, "You hear sizzling electronics.", 2) - sleep(10*pick(4,5,6,7,10,14)) - var/datum/effect_system/smoke_spread/smoke = new - smoke.set_up(3, 0, src.loc) - smoke.attach(src) - smoke.start() - explosion(src.loc, -1, 0, 1, 3, 1, 0) - qdel(src) - return - if(prob(15)) //Power drain - do_sparks(3, 1, src) - if(prob(50)) - emp_act(1) - else - emp_act(2) - if(prob(5)) //smoke only - var/datum/effect_system/smoke_spread/smoke = new - smoke.set_up(3, 0, src.loc) - smoke.attach(src) - smoke.start() - -/obj/machinery/power/smes/proc/inputting(var/do_input) - input_attempt = do_input - if(!input_attempt) - inputting = 0 - -/obj/machinery/power/smes/proc/outputting(var/do_output) - output_attempt = do_output - if(!output_attempt) - outputting = 0 - -/obj/machinery/power/smes/emp_act(severity) - inputting(rand(0,1)) - outputting(rand(0,1)) - output_level = rand(0, output_level_max) - input_level = rand(0, input_level_max) - charge -= 1e6/severity - if(charge < 0) - charge = 0 - update_icon() - ..() - -/obj/machinery/power/smes/engineering - charge = 2e6 // Engineering starts with some charge for singulo - -/obj/machinery/power/smes/magical - name = "magical power storage unit" - desc = "A high-capacity superconducting magnetic energy storage (SMES) unit. Magically produces power." - capacity = 9000000 - output_level = 250000 - -/obj/machinery/power/smes/magical/process() - capacity = INFINITY - charge = INFINITY - ..() - -#undef SMESRATE +// the SMES +// stores power + +#define SMESMAXCHARGELEVEL 200000 +#define SMESMAXOUTPUT 200000 +#define SMESRATE 0.05 // rate of internal charge to external power + + + +/obj/machinery/power/smes + name = "power storage unit" + desc = "A high-capacity superconducting magnetic energy storage (SMES) unit." + icon_state = "smes" + density = TRUE + use_power = NO_POWER_USE + + var/capacity = 5e6 // maximum charge + var/charge = 0 // actual charge + + var/input_attempt = TRUE // 1 = attempting to charge, 0 = not attempting to charge + var/inputting = TRUE // 1 = actually inputting, 0 = not inputting + var/input_level = 50000 // amount of power the SMES attempts to charge by + var/input_level_max = 200000 // cap on input_level + var/input_available = 0 // amount of charge available from input last tick + + var/output_attempt = TRUE // 1 = attempting to output, 0 = not attempting to output + var/outputting = TRUE // 1 = actually outputting, 0 = not outputting + var/output_level = 50000 // amount of power the SMES attempts to output + var/output_level_max = 200000 // cap on output_level + var/output_used = 0 // amount of power actually outputted. may be less than output_level if the powernet returns excess power + + //Holders for powerout event. + var/last_output_attempt = 0 + var/last_input_attempt = 0 + var/last_charge = 0 + + var/name_tag = null + var/obj/machinery/power/terminal/terminal = null + +/obj/machinery/power/smes/Initialize(mapload) + . = ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/smes(null) + component_parts += new /obj/item/stock_parts/cell/high(null) + component_parts += new /obj/item/stock_parts/cell/high(null) + component_parts += new /obj/item/stock_parts/cell/high(null) + component_parts += new /obj/item/stock_parts/cell/high(null) + component_parts += new /obj/item/stock_parts/cell/high(null) + component_parts += new /obj/item/stock_parts/capacitor(null) + component_parts += new /obj/item/stack/cable_coil(null, 5) + RefreshParts() + + dir_loop: + for(var/d in cardinal) + var/turf/T = get_step(src, d) + for(var/obj/machinery/power/terminal/term in T) + if(term && term.dir == turn(d, 180)) + terminal = term + break dir_loop + + if(!terminal) + stat |= BROKEN + return + terminal.master = src + update_icon() + +/obj/machinery/power/smes/upgraded/Initialize(mapload) + . = ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/smes(null) + component_parts += new /obj/item/stock_parts/cell/hyper(null) + component_parts += new /obj/item/stock_parts/cell/hyper(null) + component_parts += new /obj/item/stock_parts/cell/hyper(null) + component_parts += new /obj/item/stock_parts/cell/hyper(null) + component_parts += new /obj/item/stock_parts/cell/hyper(null) + component_parts += new /obj/item/stock_parts/capacitor/super(null) + component_parts += new /obj/item/stack/cable_coil(null, 5) + RefreshParts() + +/obj/machinery/power/smes/RefreshParts() + var/IO = 0 + var/C = 0 + for(var/obj/item/stock_parts/capacitor/CP in component_parts) + IO += CP.rating + input_level_max = 200000 * IO + output_level_max = 200000 * IO + for(var/obj/item/stock_parts/cell/PC in component_parts) + C += PC.maxcharge + capacity = C / (15000) * 1e6 + +/obj/machinery/power/smes/update_icon() + overlays.Cut() + if(stat & BROKEN) return + + overlays += image('icons/obj/power.dmi', "smes-op[outputting]") + + if(inputting == 2) + overlays += image('icons/obj/power.dmi', "smes-oc2") + else if(inputting == 1) + overlays += image('icons/obj/power.dmi', "smes-oc1") + else + if(input_attempt) + overlays += image('icons/obj/power.dmi', "smes-oc0") + + var/clevel = chargedisplay() + if(clevel>0) + overlays += image('icons/obj/power.dmi', "smes-og[clevel]") + return + +/obj/machinery/power/smes/attackby(obj/item/I, mob/user, params) + //opening using screwdriver + if(default_deconstruction_screwdriver(user, "[initial(icon_state)]-o", initial(icon_state), I)) + update_icon() + return + + //changing direction using wrench + if(default_change_direction_wrench(user, I)) + terminal = null + var/turf/T = get_step(src, dir) + for(var/obj/machinery/power/terminal/term in T) + if(term && term.dir == turn(dir, 180)) + terminal = term + terminal.master = src + to_chat(user, "Terminal found.") + break + if(!terminal) + to_chat(user, "No power source found.") + return + stat &= ~BROKEN + update_icon() + return + + //exchanging parts using the RPE + if(exchange_parts(user, I)) + return + + //building and linking a terminal + if(istype(I, /obj/item/stack/cable_coil)) + var/dir = get_dir(user,src) + if(dir & (dir-1))//we don't want diagonal click + return + + if(terminal) //is there already a terminal ? + to_chat(user, "This SMES already has a power terminal!") + return + + if(!panel_open) //is the panel open ? + to_chat(user, "You must open the maintenance panel first!") + return + + var/turf/T = get_turf(user) + if(T.intact) //is the floor plating removed ? + to_chat(user, "You must first remove the floor plating!") + return + + var/obj/item/stack/cable_coil/C = I + if(C.amount < 10) + to_chat(user, "You need more wires.") + return + + if(user.loc == loc) + to_chat(user, "You must not be on the same tile as the [src].") + return + + //Direction the terminal will face to + var/tempDir = get_dir(user, src) + switch(tempDir) + if(NORTHEAST, SOUTHEAST) + tempDir = EAST + if(NORTHWEST, SOUTHWEST) + tempDir = WEST + var/turf/tempLoc = get_step(src, reverse_direction(tempDir)) + if(istype(tempLoc, /turf/space)) + to_chat(user, "You can't build a terminal on space.") + return + else if(istype(tempLoc)) + if(tempLoc.intact) + to_chat(user, "You must remove the floor plating first.") + return + + to_chat(user, "You start adding cable to the [src].") + playsound(loc, C.usesound, 50, 1) + + if(do_after(user, 50, target = src)) + if(!terminal && panel_open) + T = get_turf(user) + var/obj/structure/cable/N = T.get_cable_node() //get the connecting node cable, if there's one + if(prob(50) && electrocute_mob(usr, N, N, 1, TRUE)) //animate the electrocution if uncautious and unlucky + do_sparks(5, 1, src) + return + + C.use(10) // make sure the cable gets used up + user.visible_message(\ + "[user.name] adds the cables and connects the power terminal.",\ + "You add the cables and connect the power terminal.") + + make_terminal(user, tempDir, tempLoc) + terminal.connect_to_network() + return + + //disassembling the terminal + if(istype(I, /obj/item/wirecutters) && terminal && panel_open) + var/turf/T = get_turf(terminal) + if(T.intact) //is the floor plating removed ? + to_chat(user, "You must first expose the power terminal!") + return + + to_chat(user, "You begin to dismantle the power terminal...") + playsound(src.loc, I.usesound, 50, 1) + + if(do_after(user, 50 * I.toolspeed, target = src)) + if(terminal && panel_open) + if(prob(50) && electrocute_mob(usr, terminal.powernet, terminal, 1, TRUE)) //animate the electrocution if uncautious and unlucky + do_sparks(5, 1, src) + return + + //give the wires back and delete the terminal + new /obj/item/stack/cable_coil(T,10) + user.visible_message(\ + "[user.name] cuts the cables and dismantles the power terminal.",\ + "You cut the cables and dismantle the power terminal.") + inputting = 0 //stop inputting, since we have don't have a terminal anymore + qdel(terminal) + return + + //crowbarring it ! + if(default_deconstruction_crowbar(user, I)) + return + return ..() + +/obj/machinery/power/smes/disconnect_terminal() + if(terminal) + terminal.master = null + terminal = null + return 1 + return 0 + +/obj/machinery/power/smes/proc/make_terminal(user, tempDir, tempLoc) + // create a terminal object at the same position as original turf loc + // wires will attach to this + terminal = new /obj/machinery/power/terminal(tempLoc) + terminal.dir = tempDir + terminal.master = src + +/obj/machinery/power/smes/Destroy() + if(SSticker && SSticker.current_state == GAME_STATE_PLAYING) + var/area/area = get_area(src) + if(area) + message_admins("SMES deleted at ([area.name])") + log_game("SMES deleted at ([area.name])") + investigate_log("deleted at ([area.name])","singulo") + if(terminal) + disconnect_terminal() + return ..() + + return round(5.5*charge/(capacity ? capacity : 5e6)) + +/obj/machinery/power/smes/proc/chargedisplay() + return round(5.5*charge/(capacity ? capacity : 5e6)) + +/obj/machinery/power/smes/process() + if(stat & BROKEN) + return + + //store machine state to see if we need to update the icon overlays + var/last_disp = chargedisplay() + var/last_chrg = inputting + var/last_onln = outputting + + //inputting + if(terminal && input_attempt) + input_available = terminal.surplus() + + if(inputting) + if(input_available > 0) // if there's power available, try to charge + + var/load = min(min((capacity-charge)/SMESRATE, input_level), input_available) // charge at set rate, limited to spare capacity + + charge += load * SMESRATE // increase the charge + + terminal.add_load(load) // add the load to the terminal side network + + else // if not enough capcity + inputting = FALSE // stop inputting + + else + if(input_attempt && input_available > 0) + inputting = TRUE + else + inputting = FALSE + + //outputting + if(output_attempt) + if(outputting) + output_used = min( charge/SMESRATE, output_level) //limit output to that stored + + if (add_avail(output_used)) // add output to powernet if it exists (smes side) + charge -= output_used*SMESRATE // reduce the storage (may be recovered in /restore() if excessive) + else + outputting = FALSE + + if(output_used < 0.0001) // either from no charge or set to 0 + outputting = FALSE + investigate_log("lost power and turned off", "singulo") + else if(output_attempt && charge > output_level && output_level > 0) + outputting = TRUE + else + output_used = 0 + else + outputting = FALSE + + // only update icon if state changed + if(last_disp != chargedisplay() || last_chrg != inputting || last_onln != outputting) + update_icon() + + + +// called after all power processes are finished +// restores charge level to smes if there was excess this ptick +/obj/machinery/power/smes/proc/restore() + if(stat & BROKEN) + return + + if(!outputting) + output_used = 0 + return + + var/excess = powernet.netexcess // this was how much wasn't used on the network last ptick, minus any removed by other SMESes + + excess = min(output_used, excess) // clamp it to how much was actually output by this SMES last ptick + + excess = min((capacity-charge)/SMESRATE, excess) // for safety, also limit recharge by space capacity of SMES (shouldn't happen) + + // now recharge this amount + + var/clev = chargedisplay() + + charge += excess * SMESRATE // restore unused power + powernet.netexcess -= excess // remove the excess from the powernet, so later SMESes don't try to use it + + output_used -= excess + + if(clev != chargedisplay() ) //if needed updates the icons overlay + update_icon() + return + +/obj/machinery/power/smes/attack_ai(mob/user) + add_hiddenprint(user) + ui_interact(user) + +/obj/machinery/power/smes/attack_ghost(mob/user) + ui_interact(user) + +/obj/machinery/power/smes/attack_hand(mob/user) + add_fingerprint(user) + ui_interact(user) + +/obj/machinery/power/smes/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + if(stat & BROKEN) + return + + + // update the ui if it exists, returns null if no ui is passed/found + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + // the ui does not exist, so we'll create a new() one + // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm + ui = new(user, src, ui_key, "smes.tmpl", "SMES Power Storage Unit", 540, 380) + // open the new ui window + ui.open() + // auto update every Master Controller tick + ui.set_auto_update(1) + +/obj/machinery/power/smes/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) + var/data[0] + + data["nameTag"] = name_tag + data["storedCapacity"] = round(100.0*charge/capacity, 0.1) + data["charging"] = inputting + data["chargeMode"] = input_attempt + data["chargeLevel"] = input_level + data["chargeMax"] = input_level_max + data["outputOnline"] = output_attempt + data["outputLevel"] = output_level + data["outputMax"] = output_level_max + data["outputLoad"] = round(output_used) + + if(outputting) + data["outputting"] = 2 // smes is outputting + else if(!outputting && output_attempt) + data["outputting"] = 1 // smes is online but not outputting because it's charge level is too low + else + data["outputting"] = 0 // smes is not outputting + + return data + +/obj/machinery/power/smes/Topic(href, href_list) + if(..()) + return 1 + + if( href_list["cmode"] ) + inputting(!input_attempt) + update_icon() + + else if( href_list["online"] ) + outputting(!output_attempt) + update_icon() + + else if( href_list["input"] ) + switch( href_list["input"] ) + if("min") + input_level = 0 + if("max") + input_level = input_level_max + if("set") + input_level = input(usr, "Enter new input level (0-[input_level_max])", "SMES Input Power Control", input_level) as num + input_level = max(0, min(input_level_max, input_level)) // clamp to range + + else if( href_list["output"] ) + switch( href_list["output"] ) + if("min") + output_level = 0 + if("max") + output_level = output_level_max + if("set") + output_level = input(usr, "Enter new output level (0-[output_level_max])", "SMES Output Power Control", output_level) as num + output_level = max(0, min(output_level_max, output_level)) // clamp to range + + investigate_log("input/output; [input_level>output_level?"":""][input_level]/[output_level] | Output-mode: [output_attempt?"on":"off"] | Input-mode: [input_attempt?"auto":"off"] by [usr.key]","singulo") + + return 1 + +/obj/machinery/power/smes/proc/ion_act() + if(is_station_level(src.z)) + if(prob(1)) //explosion + for(var/mob/M in viewers(src)) + M.show_message("The [src.name] is making strange noises!", 3, "You hear sizzling electronics.", 2) + sleep(10*pick(4,5,6,7,10,14)) + var/datum/effect_system/smoke_spread/smoke = new + smoke.set_up(3, 0, src.loc) + smoke.attach(src) + smoke.start() + explosion(src.loc, -1, 0, 1, 3, 1, 0) + qdel(src) + return + if(prob(15)) //Power drain + do_sparks(3, 1, src) + if(prob(50)) + emp_act(1) + else + emp_act(2) + if(prob(5)) //smoke only + var/datum/effect_system/smoke_spread/smoke = new + smoke.set_up(3, 0, src.loc) + smoke.attach(src) + smoke.start() + +/obj/machinery/power/smes/proc/inputting(var/do_input) + input_attempt = do_input + if(!input_attempt) + inputting = 0 + +/obj/machinery/power/smes/proc/outputting(var/do_output) + output_attempt = do_output + if(!output_attempt) + outputting = 0 + +/obj/machinery/power/smes/emp_act(severity) + inputting(rand(0,1)) + outputting(rand(0,1)) + output_level = rand(0, output_level_max) + input_level = rand(0, input_level_max) + charge -= 1e6/severity + if(charge < 0) + charge = 0 + update_icon() + ..() + +/obj/machinery/power/smes/engineering + charge = 2e6 // Engineering starts with some charge for singulo + +/obj/machinery/power/smes/magical + name = "magical power storage unit" + desc = "A high-capacity superconducting magnetic energy storage (SMES) unit. Magically produces power." + capacity = 9000000 + output_level = 250000 + +/obj/machinery/power/smes/magical/process() + capacity = INFINITY + charge = INFINITY + ..() + +#undef SMESRATE diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm index f3ca60effda..3a1f13bd10e 100644 --- a/code/modules/power/solar.dm +++ b/code/modules/power/solar.dm @@ -1,523 +1,521 @@ -#define SOLAR_MAX_DIST 40 -#define SOLARGENRATE 1500 - -/obj/machinery/power/solar - name = "solar panel" - desc = "A solar panel. Generates electricity when in contact with sunlight." - icon = 'icons/obj/power.dmi' - icon_state = "sp_base" - density = TRUE - use_power = NO_POWER_USE - idle_power_usage = 0 - active_power_usage = 0 - max_integrity = 150 - integrity_failure = 50 - var/id = 0 - var/obscured = 0 - var/sunfrac = 0 - var/adir = SOUTH // actual dir - var/ndir = SOUTH // target dir - var/turn_angle = 0 - var/obj/machinery/power/solar_control/control = null - -/obj/machinery/power/solar/Initialize(mapload, obj/item/solar_assembly/S) - . = ..() - Make(S) - connect_to_network() - -/obj/machinery/power/solar/Destroy() - unset_control() //remove from control computer - return ..() - -//set the control of the panel to a given computer if closer than SOLAR_MAX_DIST -/obj/machinery/power/solar/proc/set_control(var/obj/machinery/power/solar_control/SC) - if(!SC || (get_dist(src, SC) > SOLAR_MAX_DIST)) - return 0 - control = SC - SC.connected_panels |= src - return 1 - -//set the control of the panel to null and removes it from the control list of the previous control computer if needed -/obj/machinery/power/solar/proc/unset_control() - if(control) - control.connected_panels.Remove(src) - control = null - -/obj/machinery/power/solar/proc/Make(var/obj/item/solar_assembly/S) - if(!S) - S = new /obj/item/solar_assembly(src) - S.glass_type = /obj/item/stack/sheet/glass - S.anchored = 1 - S.loc = src - if(S.glass_type == /obj/item/stack/sheet/rglass) //if the panel is in reinforced glass - max_integrity *= 2 //this need to be placed here, because panels already on the map don't have an assembly linked to - obj_integrity = max_integrity - update_icon() - - - -/obj/machinery/power/solar/attackby(obj/item/W, mob/user, params) - if(iscrowbar(W)) - playsound(src.loc, 'sound/machines/click.ogg', 50, 1) - user.visible_message("[user] begins to take the glass off the solar panel.", "You begin to take the glass off the solar panel...") - if(do_after(user, 50 * W.toolspeed, target = src)) - playsound(src.loc, W.usesound, 50, 1) - user.visible_message("[user] takes the glass off the solar panel.", "You take the glass off the solar panel.") - deconstruct(TRUE) - return - return ..() - -/obj/machinery/power/solar/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - switch(damage_type) - if(BRUTE) - if(stat & BROKEN) - playsound(loc, 'sound/effects/hit_on_shattered_glass.ogg', 60, TRUE) - else - playsound(loc, 'sound/effects/glasshit.ogg', 90, TRUE) - if(BURN) - playsound(loc, 'sound/items/welder.ogg', 100, TRUE) - -/obj/machinery/power/solar/obj_break(damage_flag) - if(!(stat & BROKEN) && !(flags & NODECONSTRUCT)) - playsound(loc, 'sound/effects/glassbr3.ogg', 100, TRUE) - stat |= BROKEN - unset_control() - update_icon() - -/obj/machinery/power/solar/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - if(disassembled) - var/obj/item/solar_assembly/S = locate() in src - if(S) - S.forceMove(loc) - S.give_glass(stat & BROKEN) - else - playsound(src, "shatter", 70, TRUE) - new /obj/item/shard(src.loc) - new /obj/item/shard(src.loc) - qdel(src) - -/obj/machinery/power/solar/update_icon() - ..() - overlays.Cut() - if(stat & BROKEN) - overlays += image('icons/obj/power.dmi', icon_state = "solar_panel-b", layer = FLY_LAYER) - else - overlays += image('icons/obj/power.dmi', icon_state = "solar_panel", layer = FLY_LAYER) - src.dir = angle2dir(adir) - return - -//calculates the fraction of the sunlight that the panel recieves -/obj/machinery/power/solar/proc/update_solar_exposure() - if(obscured) - sunfrac = 0 - return - - //find the smaller angle between the direction the panel is facing and the direction of the sun (the sign is not important here) - var/p_angle = min(abs(adir - SSsun.angle), 360 - abs(adir - SSsun.angle)) - - if(p_angle > 90) // if facing more than 90deg from sun, zero output - sunfrac = 0 - return - - sunfrac = cos(p_angle) ** 2 - //isn't the power received from the incoming light proportionnal to cos(p_angle) (Lambert's cosine law) rather than cos(p_angle)^2 ? - -/obj/machinery/power/solar/process()//TODO: remove/add this from machines to save on processing as needed ~Carn PRIORITY - if(stat & BROKEN) - return - if(!control) //if there's no sun or the panel is not linked to a solar control computer, no need to proceed - return - - if(powernet) - if(powernet == control.powernet)//check if the panel is still connected to the computer - if(obscured) //get no light from the sun, so don't generate power - return - var/sgen = SOLARGENRATE * sunfrac - add_avail(sgen) - control.gen += sgen - else //if we're no longer on the same powernet, remove from control computer - unset_control() - -/obj/machinery/power/solar/proc/broken() - . = (!(stat & BROKEN)) - stat |= BROKEN - unset_control() - update_icon() - -/obj/machinery/power/solar/fake/New(var/turf/loc, var/obj/item/solar_assembly/S) - ..(loc, S, 0) - -/obj/machinery/power/solar/fake/process() - . = PROCESS_KILL - return - -//trace towards sun to see if we're in shadow -/obj/machinery/power/solar/proc/occlusion() - - var/ax = x // start at the solar panel - var/ay = y - var/turf/T = null - var/dx = SSsun.dx - var/dy = SSsun.dy - - for(var/i = 1 to 20) // 20 steps is enough - ax += dx // do step - ay += dy - - T = locate( round(ax,0.5),round(ay,0.5),z) - - if(T.x == 1 || T.x==world.maxx || T.y==1 || T.y==world.maxy) // not obscured if we reach the edge - break - - if(T.density) // if we hit a solid turf, panel is obscured - obscured = 1 - return - - obscured = 0 // if hit the edge or stepped 20 times, not obscured - update_solar_exposure() - - -// -// Solar Assembly - For construction of solar arrays. -// - -/obj/item/solar_assembly - name = "solar panel assembly" - desc = "A solar panel assembly kit, allows constructions of a solar panel, or with a tracking circuit board, a solar tracker" - icon = 'icons/obj/power.dmi' - icon_state = "sp_base" - item_state = "electropack" - w_class = WEIGHT_CLASS_BULKY // Pretty big! - anchored = 0 - var/tracker = 0 - var/glass_type = null - -/obj/item/solar_assembly/attack_hand(var/mob/user) - if(!anchored && isturf(loc)) // You can't pick it up - ..() - -// Give back the glass type we were supplied with -/obj/item/solar_assembly/proc/give_glass() - if(glass_type) - var/obj/item/stack/sheet/S = new glass_type(src.loc) - S.amount = 2 - glass_type = null - - -/obj/item/solar_assembly/attackby(var/obj/item/W, var/mob/user, params) - - if(!anchored && isturf(loc)) - if(istype(W, /obj/item/wrench)) - anchored = 1 - user.visible_message("[user] wrenches the solar assembly into place.", "You wrench the solar assembly into place.") - playsound(src.loc, W.usesound, 50, 1) - return 1 - else - if(istype(W, /obj/item/wrench)) - anchored = 0 - user.visible_message("[user] unwrenches the solar assembly from its place.", "You unwrench the solar assembly from its place.") - playsound(src.loc, W.usesound, 50, 1) - return 1 - - if(istype(W, /obj/item/stack/sheet/glass) || istype(W, /obj/item/stack/sheet/rglass)) - var/obj/item/stack/sheet/S = W - if(S.use(2)) - glass_type = W.type - playsound(loc, S.usesound, 50, 1) - user.visible_message("[user] places the glass on the solar assembly.", "You place the glass on the solar assembly.") - if(tracker) - new /obj/machinery/power/tracker(get_turf(src), src) - else - new /obj/machinery/power/solar(get_turf(src), src) - else - to_chat(user, "You need two sheets of glass to put them into a solar panel.") - return - return 1 - - if(!tracker) - if(istype(W, /obj/item/tracker_electronics)) - if(!user.drop_item()) - return - tracker = 1 - qdel(W) - user.visible_message("[user] inserts the electronics into the solar assembly.", "You insert the electronics into the solar assembly.") - return 1 - else if(istype(W, /obj/item/crowbar)) - new /obj/item/tracker_electronics(src.loc) - tracker = 0 - playsound(loc, W.usesound, 50, 1) - user.visible_message("[user] takes out the electronics from the solar assembly.", "You take out the electronics from the solar assembly.") - return 1 - else - return ..() - -// -// Solar Control Computer -// - -/obj/machinery/power/solar_control - name = "solar panel control" - desc = "A controller for solar panel arrays." - icon = 'icons/obj/computer.dmi' - icon_state = "computer" - anchored = 1 - density = 1 - use_power = IDLE_POWER_USE - idle_power_usage = 250 - max_integrity = 200 - integrity_failure = 100 - var/icon_screen = "solar" - var/icon_keyboard = "power_key" - var/id = 0 - var/cdir = 0 - var/targetdir = 0 // target angle in manual tracking (since it updates every game minute) - var/gen = 0 - var/lastgen = 0 - var/track = 0 // 0= off 1=timed 2=auto (tracker) - var/trackrate = 600 // 300-900 seconds - var/nexttime = 0 // time for a panel to rotate of 1? in manual tracking - var/autostart = 0 // Automatically search for connected devices - var/obj/machinery/power/tracker/connected_tracker = null - var/list/connected_panels = list() - -// Used for mapping in solar array which automatically starts itself (telecomms, for example) -/obj/machinery/power/solar_control/autostart - track = 2 // Auto tracking mode - autostart = 1 // Automatically start - -/obj/machinery/power/solar_control/Initialize() - ..() - if(!powernet) - return - connect_to_network() - set_panels(cdir) - if(autostart) - src.search_for_connected() - if(connected_tracker && track == 2) - connected_tracker.set_angle(SSsun.angle) - set_panels(cdir) - -/obj/machinery/power/solar_control/Destroy() - for(var/obj/machinery/power/solar/M in connected_panels) - M.unset_control() - if(connected_tracker) - connected_tracker.unset_control() - return ..() - -/obj/machinery/power/solar_control/disconnect_from_network() - ..() - SSsun.solars.Remove(src) - -/obj/machinery/power/solar_control/connect_to_network() - var/to_return = ..() - if(powernet) //if connected and not already in solar list... - SSsun.solars |= src //... add it - return to_return - -//search for unconnected panels and trackers in the computer powernet and connect them -/obj/machinery/power/solar_control/proc/search_for_connected() - if(powernet) - for(var/obj/machinery/power/M in powernet.nodes) - if(istype(M, /obj/machinery/power/solar)) - var/obj/machinery/power/solar/S = M - if(!S.control) //i.e unconnected - S.set_control(src) - else if(istype(M, /obj/machinery/power/tracker)) - if(!connected_tracker) //if there's already a tracker connected to the computer don't add another - var/obj/machinery/power/tracker/T = M - if(!T.control) //i.e unconnected - T.set_control(src) - -//called by the sun controller, update the facing angle (either manually or via tracking) and rotates the panels accordingly -/obj/machinery/power/solar_control/proc/update() - if(stat & (NOPOWER | BROKEN)) - return - - switch(track) - if(1) - if(trackrate) //we're manual tracking. If we set a rotation speed... - cdir = targetdir //...the current direction is the targetted one (and rotates panels to it) - if(2) // auto-tracking - if(connected_tracker) - connected_tracker.set_angle(SSsun.angle) - - set_panels(cdir) - updateDialog() - -/obj/machinery/power/solar_control/update_icon() - overlays.Cut() - if(stat & NOPOWER) - overlays += "[icon_keyboard]_off" - return - overlays += icon_keyboard - if(stat & BROKEN) - overlays += "[icon_state]_broken" - else - overlays += icon_screen - if(cdir > -1) - overlays += image('icons/obj/computer.dmi', "solcon-o", FLY_LAYER, angle2dir(cdir)) - -/obj/machinery/power/solar_control/attack_ai(mob/user as mob) - src.add_hiddenprint(user) - ui_interact(user) - -/obj/machinery/power/solar_control/attack_ghost(mob/user as mob) - ui_interact(user) - -/obj/machinery/power/solar_control/attack_hand(mob/user) - if(..(user)) - return 1 - - if(stat & BROKEN) - return - - ui_interact(user) - -/obj/machinery/power/solar_control/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "solar_control.tmpl", name, 490, 420) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/power/solar_control/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - - data["generated"] = round(lastgen) - data["angle"] = cdir - data["direction"] = angle2text(cdir) - - data["tracking_state"] = track - data["tracking_rate"] = trackrate - data["rotating_way"] = (trackrate<0 ? "CCW" : "CW") - - data["connected_panels"] = connected_panels.len - data["connected_tracker"] = (connected_tracker ? 1 : 0) - - return data - -/obj/machinery/power/solar_control/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/screwdriver)) - playsound(src.loc, I.usesound, 50, 1) - if(do_after(user, 20 * I.toolspeed, target = src)) - if(src.stat & BROKEN) - to_chat(user, "The broken glass falls out.") - var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc ) - new /obj/item/shard( src.loc ) - var/obj/item/circuitboard/solar_control/M = new /obj/item/circuitboard/solar_control( A ) - for(var/obj/C in src) - C.loc = src.loc - A.circuit = M - A.state = 3 - A.icon_state = "3" - A.anchored = 1 - qdel(src) - else - to_chat(user, "You disconnect the monitor.") - var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc ) - var/obj/item/circuitboard/solar_control/M = new /obj/item/circuitboard/solar_control( A ) - for(var/obj/C in src) - C.loc = src.loc - A.circuit = M - A.state = 4 - A.icon_state = "4" - A.anchored = 1 - qdel(src) - else - return ..() - -/obj/machinery/power/solar_control/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - switch(damage_type) - if(BRUTE) - if(stat & BROKEN) - playsound(src.loc, 'sound/effects/hit_on_shattered_glass.ogg', 70, TRUE) - else - playsound(src.loc, 'sound/effects/glasshit.ogg', 75, TRUE) - if(BURN) - playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE) - -/obj/machinery/power/solar_control/obj_break(damage_flag) - if(!(stat & BROKEN) && !(flags & NODECONSTRUCT)) - playsound(loc, 'sound/effects/glassbr3.ogg', 100, TRUE) - stat |= BROKEN - update_icon() - -/obj/machinery/power/solar_control/process() - lastgen = gen - gen = 0 - - if(stat & (NOPOWER | BROKEN)) - return - - if(connected_tracker) //NOTE : handled here so that we don't add trackers to the processing list - if(connected_tracker.powernet != powernet) - connected_tracker.unset_control() - - if(track==1 && trackrate) //manual tracking and set a rotation speed - if(nexttime <= world.time) //every time we need to increase/decrease the angle by 1?... - targetdir = (targetdir + trackrate/abs(trackrate) + 360) % 360 //... do it - nexttime += 36000/abs(trackrate) //reset the counter for the next 1? - -/obj/machinery/power/solar_control/Topic(href, href_list) - if(..()) - return - - if(href_list["rate_control"]) - if(href_list["cdir"]) - src.cdir = dd_range(0,359,(360+src.cdir+text2num(href_list["cdir"]))%360) - src.targetdir = src.cdir - if(track == 2) //manual update, so losing auto-tracking - track = 0 - spawn(1) - set_panels(cdir) - if(href_list["tdir"]) - src.trackrate = dd_range(-7200,7200,src.trackrate+text2num(href_list["tdir"])) - if(src.trackrate) nexttime = world.time + 36000/abs(trackrate) - - if(href_list["track"]) - track = text2num(href_list["track"]) - if(track == 2) - if(connected_tracker) - connected_tracker.set_angle(SSsun.angle) - set_panels(cdir) - else if(track == 1) //begin manual tracking - src.targetdir = src.cdir - if(src.trackrate) nexttime = world.time + 36000/abs(trackrate) - set_panels(targetdir) - - if(href_list["search_connected"]) - search_for_connected() - if(connected_tracker && track == 2) - connected_tracker.set_angle(SSsun.angle) - set_panels(cdir) - - return - -//rotates the panel to the passed angle -/obj/machinery/power/solar_control/proc/set_panels(var/cdir) - - for(var/obj/machinery/power/solar/S in connected_panels) - S.adir = cdir //instantly rotates the panel - S.occlusion()//and - S.update_icon() //update it - - update_icon() - - -/obj/machinery/power/solar_control/power_change() - ..() - update_icon() - - -/obj/machinery/power/solar_control/proc/broken() - stat |= BROKEN - update_icon() - -// -// MISC -// - -/obj/item/paper/solar - name = "paper- 'Going green! Setup your own solar array instructions.'" - info = "

        Welcome

        At greencorps we love the environment, and space. With this package you are able to help mother nature and produce energy without any usage of fossil fuel or plasma! Singularity energy is dangerous while solar energy is safe, which is why it's better. Now here is how you setup your own solar array.

        You can make a solar panel by wrenching the solar assembly onto a cable node. Adding a glass panel, reinforced or regular glass will do, will finish the construction of your solar panel. It is that easy!

        Now after setting up 19 more of these solar panels you will want to create a solar tracker to keep track of our mother nature's gift, the sun. These are the same steps as before except you insert the tracker equipment circuit into the assembly before performing the final step of adding the glass. You now have a tracker! Now the last step is to add a computer to calculate the sun's movements and to send commands to the solar panels to change direction with the sun. Setting up the solar computer is the same as setting up any computer, so you should have no trouble in doing that. You do need to put a wire node under the computer, and the wire needs to be connected to the tracker.

        Congratulations, you should have a working solar array. If you are having trouble, here are some tips. Make sure all solar equipment are on a cable node, even the computer. You can always deconstruct your creations if you make a mistake.

        That's all to it, be safe, be green!

        " +#define SOLAR_MAX_DIST 40 +#define SOLARGENRATE 1500 + +/obj/machinery/power/solar + name = "solar panel" + desc = "A solar panel. Generates electricity when in contact with sunlight." + icon = 'icons/obj/power.dmi' + icon_state = "sp_base" + density = TRUE + use_power = NO_POWER_USE + idle_power_usage = 0 + active_power_usage = 0 + max_integrity = 150 + integrity_failure = 50 + var/id = 0 + var/obscured = 0 + var/sunfrac = 0 + var/adir = SOUTH // actual dir + var/ndir = SOUTH // target dir + var/turn_angle = 0 + var/obj/machinery/power/solar_control/control = null + +/obj/machinery/power/solar/Initialize(mapload, obj/item/solar_assembly/S) + . = ..() + Make(S) + connect_to_network() + +/obj/machinery/power/solar/Destroy() + unset_control() //remove from control computer + return ..() + +//set the control of the panel to a given computer if closer than SOLAR_MAX_DIST +/obj/machinery/power/solar/proc/set_control(var/obj/machinery/power/solar_control/SC) + if(!SC || (get_dist(src, SC) > SOLAR_MAX_DIST)) + return 0 + control = SC + SC.connected_panels |= src + return 1 + +//set the control of the panel to null and removes it from the control list of the previous control computer if needed +/obj/machinery/power/solar/proc/unset_control() + if(control) + control.connected_panels.Remove(src) + control = null + +/obj/machinery/power/solar/proc/Make(var/obj/item/solar_assembly/S) + if(!S) + S = new /obj/item/solar_assembly(src) + S.glass_type = /obj/item/stack/sheet/glass + S.anchored = 1 + S.loc = src + if(S.glass_type == /obj/item/stack/sheet/rglass) //if the panel is in reinforced glass + max_integrity *= 2 //this need to be placed here, because panels already on the map don't have an assembly linked to + obj_integrity = max_integrity + update_icon() + + +/obj/machinery/power/solar/crowbar_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + playsound(loc, 'sound/machines/click.ogg', 50, 1) + user.visible_message("[user] begins to take the glass off the solar panel.", "You begin to take the glass off the solar panel...") + if(I.use_tool(src, user, 50, volume = I.tool_volume)) + user.visible_message("[user] takes the glass off the solar panel.", "You take the glass off the solar panel.") + deconstruct(TRUE) + +/obj/machinery/power/solar/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + switch(damage_type) + if(BRUTE) + if(stat & BROKEN) + playsound(loc, 'sound/effects/hit_on_shattered_glass.ogg', 60, TRUE) + else + playsound(loc, 'sound/effects/glasshit.ogg', 90, TRUE) + if(BURN) + playsound(loc, 'sound/items/welder.ogg', 100, TRUE) + +/obj/machinery/power/solar/obj_break(damage_flag) + if(!(stat & BROKEN) && !(flags & NODECONSTRUCT)) + playsound(loc, 'sound/effects/glassbr3.ogg', 100, TRUE) + stat |= BROKEN + unset_control() + update_icon() + +/obj/machinery/power/solar/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + if(disassembled) + var/obj/item/solar_assembly/S = locate() in src + if(S) + S.forceMove(loc) + S.give_glass(stat & BROKEN) + else + playsound(src, "shatter", 70, TRUE) + new /obj/item/shard(src.loc) + new /obj/item/shard(src.loc) + qdel(src) + +/obj/machinery/power/solar/update_icon() + ..() + overlays.Cut() + if(stat & BROKEN) + overlays += image('icons/obj/power.dmi', icon_state = "solar_panel-b", layer = FLY_LAYER) + else + overlays += image('icons/obj/power.dmi', icon_state = "solar_panel", layer = FLY_LAYER) + src.dir = angle2dir(adir) + return + +//calculates the fraction of the sunlight that the panel recieves +/obj/machinery/power/solar/proc/update_solar_exposure() + if(obscured) + sunfrac = 0 + return + + //find the smaller angle between the direction the panel is facing and the direction of the sun (the sign is not important here) + var/p_angle = min(abs(adir - SSsun.angle), 360 - abs(adir - SSsun.angle)) + + if(p_angle > 90) // if facing more than 90deg from sun, zero output + sunfrac = 0 + return + + sunfrac = cos(p_angle) ** 2 + //isn't the power received from the incoming light proportionnal to cos(p_angle) (Lambert's cosine law) rather than cos(p_angle)^2 ? + +/obj/machinery/power/solar/process()//TODO: remove/add this from machines to save on processing as needed ~Carn PRIORITY + if(stat & BROKEN) + return + if(!control) //if there's no sun or the panel is not linked to a solar control computer, no need to proceed + return + + if(powernet) + if(powernet == control.powernet)//check if the panel is still connected to the computer + if(obscured) //get no light from the sun, so don't generate power + return + var/sgen = SOLARGENRATE * sunfrac + add_avail(sgen) + control.gen += sgen + else //if we're no longer on the same powernet, remove from control computer + unset_control() + +/obj/machinery/power/solar/proc/broken() + . = (!(stat & BROKEN)) + stat |= BROKEN + unset_control() + update_icon() + +/obj/machinery/power/solar/fake/New(var/turf/loc, var/obj/item/solar_assembly/S) + ..(loc, S, 0) + +/obj/machinery/power/solar/fake/process() + . = PROCESS_KILL + return + +//trace towards sun to see if we're in shadow +/obj/machinery/power/solar/proc/occlusion() + + var/ax = x // start at the solar panel + var/ay = y + var/turf/T = null + var/dx = SSsun.dx + var/dy = SSsun.dy + + for(var/i = 1 to 20) // 20 steps is enough + ax += dx // do step + ay += dy + + T = locate( round(ax,0.5),round(ay,0.5),z) + + if(T.x == 1 || T.x==world.maxx || T.y==1 || T.y==world.maxy) // not obscured if we reach the edge + break + + if(T.density) // if we hit a solid turf, panel is obscured + obscured = 1 + return + + obscured = 0 // if hit the edge or stepped 20 times, not obscured + update_solar_exposure() + + +// +// Solar Assembly - For construction of solar arrays. +// + +/obj/item/solar_assembly + name = "solar panel assembly" + desc = "A solar panel assembly kit, allows constructions of a solar panel, or with a tracking circuit board, a solar tracker" + icon = 'icons/obj/power.dmi' + icon_state = "sp_base" + item_state = "electropack" + w_class = WEIGHT_CLASS_BULKY // Pretty big! + anchored = 0 + var/tracker = 0 + var/glass_type = null + +/obj/item/solar_assembly/attack_hand(var/mob/user) + if(!anchored && isturf(loc)) // You can't pick it up + ..() + +// Give back the glass type we were supplied with +/obj/item/solar_assembly/proc/give_glass() + if(glass_type) + var/obj/item/stack/sheet/S = new glass_type(src.loc) + S.amount = 2 + glass_type = null + + +/obj/item/solar_assembly/attackby(var/obj/item/W, var/mob/user, params) + + if(!anchored && isturf(loc)) + if(istype(W, /obj/item/wrench)) + anchored = 1 + user.visible_message("[user] wrenches the solar assembly into place.", "You wrench the solar assembly into place.") + playsound(src.loc, W.usesound, 50, 1) + return 1 + else + if(istype(W, /obj/item/wrench)) + anchored = 0 + user.visible_message("[user] unwrenches the solar assembly from its place.", "You unwrench the solar assembly from its place.") + playsound(src.loc, W.usesound, 50, 1) + return 1 + + if(istype(W, /obj/item/stack/sheet/glass) || istype(W, /obj/item/stack/sheet/rglass)) + var/obj/item/stack/sheet/S = W + if(S.use(2)) + glass_type = W.type + playsound(loc, S.usesound, 50, 1) + user.visible_message("[user] places the glass on the solar assembly.", "You place the glass on the solar assembly.") + if(tracker) + new /obj/machinery/power/tracker(get_turf(src), src) + else + new /obj/machinery/power/solar(get_turf(src), src) + else + to_chat(user, "You need two sheets of glass to put them into a solar panel.") + return + return 1 + + if(!tracker) + if(istype(W, /obj/item/tracker_electronics)) + if(!user.drop_item()) + return + tracker = 1 + qdel(W) + user.visible_message("[user] inserts the electronics into the solar assembly.", "You insert the electronics into the solar assembly.") + return 1 + else if(istype(W, /obj/item/crowbar)) + new /obj/item/tracker_electronics(src.loc) + tracker = 0 + playsound(loc, W.usesound, 50, 1) + user.visible_message("[user] takes out the electronics from the solar assembly.", "You take out the electronics from the solar assembly.") + return 1 + else + return ..() + +// +// Solar Control Computer +// + +/obj/machinery/power/solar_control + name = "solar panel control" + desc = "A controller for solar panel arrays." + icon = 'icons/obj/computer.dmi' + icon_state = "computer" + anchored = 1 + density = 1 + use_power = IDLE_POWER_USE + idle_power_usage = 250 + max_integrity = 200 + integrity_failure = 100 + var/icon_screen = "solar" + var/icon_keyboard = "power_key" + var/id = 0 + var/cdir = 0 + var/targetdir = 0 // target angle in manual tracking (since it updates every game minute) + var/gen = 0 + var/lastgen = 0 + var/track = 0 // 0= off 1=timed 2=auto (tracker) + var/trackrate = 600 // 300-900 seconds + var/nexttime = 0 // time for a panel to rotate of 1? in manual tracking + var/autostart = 0 // Automatically search for connected devices + var/obj/machinery/power/tracker/connected_tracker = null + var/list/connected_panels = list() + +// Used for mapping in solar array which automatically starts itself (telecomms, for example) +/obj/machinery/power/solar_control/autostart + track = 2 // Auto tracking mode + autostart = 1 // Automatically start + +/obj/machinery/power/solar_control/Initialize() + ..() + if(!powernet) + return + connect_to_network() + set_panels(cdir) + if(autostart) + src.search_for_connected() + if(connected_tracker && track == 2) + connected_tracker.set_angle(SSsun.angle) + set_panels(cdir) + +/obj/machinery/power/solar_control/Destroy() + for(var/obj/machinery/power/solar/M in connected_panels) + M.unset_control() + if(connected_tracker) + connected_tracker.unset_control() + return ..() + +/obj/machinery/power/solar_control/disconnect_from_network() + ..() + SSsun.solars.Remove(src) + +/obj/machinery/power/solar_control/connect_to_network() + var/to_return = ..() + if(powernet) //if connected and not already in solar list... + SSsun.solars |= src //... add it + return to_return + +//search for unconnected panels and trackers in the computer powernet and connect them +/obj/machinery/power/solar_control/proc/search_for_connected() + if(powernet) + for(var/obj/machinery/power/M in powernet.nodes) + if(istype(M, /obj/machinery/power/solar)) + var/obj/machinery/power/solar/S = M + if(!S.control) //i.e unconnected + S.set_control(src) + else if(istype(M, /obj/machinery/power/tracker)) + if(!connected_tracker) //if there's already a tracker connected to the computer don't add another + var/obj/machinery/power/tracker/T = M + if(!T.control) //i.e unconnected + T.set_control(src) + +//called by the sun controller, update the facing angle (either manually or via tracking) and rotates the panels accordingly +/obj/machinery/power/solar_control/proc/update() + if(stat & (NOPOWER | BROKEN)) + return + + switch(track) + if(1) + if(trackrate) //we're manual tracking. If we set a rotation speed... + cdir = targetdir //...the current direction is the targetted one (and rotates panels to it) + if(2) // auto-tracking + if(connected_tracker) + connected_tracker.set_angle(SSsun.angle) + + set_panels(cdir) + updateDialog() + +/obj/machinery/power/solar_control/update_icon() + overlays.Cut() + if(stat & NOPOWER) + overlays += "[icon_keyboard]_off" + return + overlays += icon_keyboard + if(stat & BROKEN) + overlays += "[icon_state]_broken" + else + overlays += icon_screen + if(cdir > -1) + overlays += image('icons/obj/computer.dmi', "solcon-o", FLY_LAYER, angle2dir(cdir)) + +/obj/machinery/power/solar_control/attack_ai(mob/user as mob) + src.add_hiddenprint(user) + ui_interact(user) + +/obj/machinery/power/solar_control/attack_ghost(mob/user as mob) + ui_interact(user) + +/obj/machinery/power/solar_control/attack_hand(mob/user) + if(..(user)) + return 1 + + if(stat & BROKEN) + return + + ui_interact(user) + +/obj/machinery/power/solar_control/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "solar_control.tmpl", name, 490, 420) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/power/solar_control/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) + var/data[0] + + data["generated"] = round(lastgen) + data["angle"] = cdir + data["direction"] = angle2text(cdir) + + data["tracking_state"] = track + data["tracking_rate"] = trackrate + data["rotating_way"] = (trackrate<0 ? "CCW" : "CW") + + data["connected_panels"] = connected_panels.len + data["connected_tracker"] = (connected_tracker ? 1 : 0) + + return data + +/obj/machinery/power/solar_control/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/screwdriver)) + playsound(src.loc, I.usesound, 50, 1) + if(do_after(user, 20 * I.toolspeed, target = src)) + if(src.stat & BROKEN) + to_chat(user, "The broken glass falls out.") + var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc ) + new /obj/item/shard( src.loc ) + var/obj/item/circuitboard/solar_control/M = new /obj/item/circuitboard/solar_control( A ) + for(var/obj/C in src) + C.loc = src.loc + A.circuit = M + A.state = 3 + A.icon_state = "3" + A.anchored = 1 + qdel(src) + else + to_chat(user, "You disconnect the monitor.") + var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc ) + var/obj/item/circuitboard/solar_control/M = new /obj/item/circuitboard/solar_control( A ) + for(var/obj/C in src) + C.loc = src.loc + A.circuit = M + A.state = 4 + A.icon_state = "4" + A.anchored = 1 + qdel(src) + else + return ..() + +/obj/machinery/power/solar_control/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) + switch(damage_type) + if(BRUTE) + if(stat & BROKEN) + playsound(src.loc, 'sound/effects/hit_on_shattered_glass.ogg', 70, TRUE) + else + playsound(src.loc, 'sound/effects/glasshit.ogg', 75, TRUE) + if(BURN) + playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE) + +/obj/machinery/power/solar_control/obj_break(damage_flag) + if(!(stat & BROKEN) && !(flags & NODECONSTRUCT)) + playsound(loc, 'sound/effects/glassbr3.ogg', 100, TRUE) + stat |= BROKEN + update_icon() + +/obj/machinery/power/solar_control/process() + lastgen = gen + gen = 0 + + if(stat & (NOPOWER | BROKEN)) + return + + if(connected_tracker) //NOTE : handled here so that we don't add trackers to the processing list + if(connected_tracker.powernet != powernet) + connected_tracker.unset_control() + + if(track==1 && trackrate) //manual tracking and set a rotation speed + if(nexttime <= world.time) //every time we need to increase/decrease the angle by 1?... + targetdir = (targetdir + trackrate/abs(trackrate) + 360) % 360 //... do it + nexttime += 36000/abs(trackrate) //reset the counter for the next 1? + +/obj/machinery/power/solar_control/Topic(href, href_list) + if(..()) + return + + if(href_list["rate_control"]) + if(href_list["cdir"]) + src.cdir = dd_range(0,359,(360+src.cdir+text2num(href_list["cdir"]))%360) + src.targetdir = src.cdir + if(track == 2) //manual update, so losing auto-tracking + track = 0 + spawn(1) + set_panels(cdir) + if(href_list["tdir"]) + src.trackrate = dd_range(-7200,7200,src.trackrate+text2num(href_list["tdir"])) + if(src.trackrate) nexttime = world.time + 36000/abs(trackrate) + + if(href_list["track"]) + track = text2num(href_list["track"]) + if(track == 2) + if(connected_tracker) + connected_tracker.set_angle(SSsun.angle) + set_panels(cdir) + else if(track == 1) //begin manual tracking + src.targetdir = src.cdir + if(src.trackrate) nexttime = world.time + 36000/abs(trackrate) + set_panels(targetdir) + + if(href_list["search_connected"]) + search_for_connected() + if(connected_tracker && track == 2) + connected_tracker.set_angle(SSsun.angle) + set_panels(cdir) + + return + +//rotates the panel to the passed angle +/obj/machinery/power/solar_control/proc/set_panels(var/cdir) + + for(var/obj/machinery/power/solar/S in connected_panels) + S.adir = cdir //instantly rotates the panel + S.occlusion()//and + S.update_icon() //update it + + update_icon() + + +/obj/machinery/power/solar_control/power_change() + ..() + update_icon() + + +/obj/machinery/power/solar_control/proc/broken() + stat |= BROKEN + update_icon() + +// +// MISC +// + +/obj/item/paper/solar + name = "paper- 'Going green! Setup your own solar array instructions.'" + info = "

        Welcome

        At greencorps we love the environment, and space. With this package you are able to help mother nature and produce energy without any usage of fossil fuel or plasma! Singularity energy is dangerous while solar energy is safe, which is why it's better. Now here is how you setup your own solar array.

        You can make a solar panel by wrenching the solar assembly onto a cable node. Adding a glass panel, reinforced or regular glass will do, will finish the construction of your solar panel. It is that easy!

        Now after setting up 19 more of these solar panels you will want to create a solar tracker to keep track of our mother nature's gift, the sun. These are the same steps as before except you insert the tracker equipment circuit into the assembly before performing the final step of adding the glass. You now have a tracker! Now the last step is to add a computer to calculate the sun's movements and to send commands to the solar panels to change direction with the sun. Setting up the solar computer is the same as setting up any computer, so you should have no trouble in doing that. You do need to put a wire node under the computer, and the wire needs to be connected to the tracker.

        Congratulations, you should have a working solar array. If you are having trouble, here are some tips. Make sure all solar equipment are on a cable node, even the computer. You can always deconstruct your creations if you make a mistake.

        That's all to it, be safe, be green!

        " diff --git a/code/modules/power/supermatter/sm_shard.dm b/code/modules/power/supermatter/sm_shard.dm index c5d7549e0a7..b9e4a2435e6 100644 --- a/code/modules/power/supermatter/sm_shard.dm +++ b/code/modules/power/supermatter/sm_shard.dm @@ -13,6 +13,7 @@ var/brightness = 2 /obj/item/shard/supermatter/New() + ..() src.icon_state = "supermatter" + pick("large", "medium", "small") switch(src.icon_state) if("supermattersmall") diff --git a/code/modules/power/terminal.dm b/code/modules/power/terminal.dm index 058872cde6d..e66128ba724 100644 --- a/code/modules/power/terminal.dm +++ b/code/modules/power/terminal.dm @@ -1,76 +1,76 @@ -// the underfloor wiring terminal for the APC -// autogenerated when an APC is placed -// all conduit connects go to this object instead of the APC -// using this solves the problem of having the APC in a wall yet also inside an area - -/obj/machinery/power/terminal - name = "terminal" - icon_state = "term" - desc = "It's an underfloor wiring terminal for power equipment." - level = 1 - layer = WIRE_TERMINAL_LAYER //a bit above wires - var/obj/machinery/power/master = null - - -/obj/machinery/power/terminal/Initialize(mapload) - . = ..() - var/turf/T = get_turf(src) - if(level == 1) - hide(T.intact) - -/obj/machinery/power/terminal/Destroy() - if(master) - master.disconnect_terminal() - master = null - return ..() - - -/obj/machinery/power/terminal/hide(i) - if(i) - invisibility = 101 - icon_state = "term-f" - else - invisibility = 0 - icon_state = "term" - -/obj/machinery/power/proc/can_terminal_dismantle() - . = 0 - -/obj/machinery/power/apc/can_terminal_dismantle() - . = 0 - if(opened) - . = 1 - -/obj/machinery/power/smes/can_terminal_dismantle() - . = 0 - if(panel_open) - . = 1 - - -/obj/machinery/power/terminal/proc/dismantle(mob/living/user, obj/item/W) - if(isturf(loc)) - var/turf/T = loc - if(T.intact) - to_chat(user, "You must first expose the power terminal!") - return - - if(!master || master.can_terminal_dismantle()) - user.visible_message("[user.name] dismantles the power terminal from [master].", \ - "You begin to cut the cables...") - - playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1) - if(do_after(user, 50*W.toolspeed, target = src)) - if(!master || master.can_terminal_dismantle()) - if(prob(50) && electrocute_mob(user, powernet, src, 1, TRUE)) - do_sparks(5, TRUE, master) - return - new /obj/item/stack/cable_coil(loc, 10) - to_chat(user, "You cut the cables and dismantle the power terminal.") - qdel(src) - - -/obj/machinery/power/terminal/attackby(obj/item/W, mob/living/user, params) - if(istype(W, /obj/item/wirecutters)) - dismantle(user, W) - else - return ..() \ No newline at end of file +// the underfloor wiring terminal for the APC +// autogenerated when an APC is placed +// all conduit connects go to this object instead of the APC +// using this solves the problem of having the APC in a wall yet also inside an area + +/obj/machinery/power/terminal + name = "terminal" + icon_state = "term" + desc = "It's an underfloor wiring terminal for power equipment." + level = 1 + layer = WIRE_TERMINAL_LAYER //a bit above wires + var/obj/machinery/power/master = null + + +/obj/machinery/power/terminal/Initialize(mapload) + . = ..() + var/turf/T = get_turf(src) + if(level == 1) + hide(T.intact) + +/obj/machinery/power/terminal/Destroy() + if(master) + master.disconnect_terminal() + master = null + return ..() + + +/obj/machinery/power/terminal/hide(i) + if(i) + invisibility = 101 + icon_state = "term-f" + else + invisibility = 0 + icon_state = "term" + +/obj/machinery/power/proc/can_terminal_dismantle() + . = 0 + +/obj/machinery/power/apc/can_terminal_dismantle() + . = 0 + if(opened) + . = 1 + +/obj/machinery/power/smes/can_terminal_dismantle() + . = 0 + if(panel_open) + . = 1 + + +/obj/machinery/power/terminal/proc/dismantle(mob/living/user, obj/item/W) + if(isturf(loc)) + var/turf/T = loc + if(T.intact) + to_chat(user, "You must first expose the power terminal!") + return + + if(!master || master.can_terminal_dismantle()) + user.visible_message("[user.name] dismantles the power terminal from [master].", \ + "You begin to cut the cables...") + + playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1) + if(do_after(user, 50*W.toolspeed, target = src)) + if(!master || master.can_terminal_dismantle()) + if(prob(50) && electrocute_mob(user, powernet, src, 1, TRUE)) + do_sparks(5, TRUE, master) + return + new /obj/item/stack/cable_coil(loc, 10) + to_chat(user, "You cut the cables and dismantle the power terminal.") + qdel(src) + + +/obj/machinery/power/terminal/attackby(obj/item/W, mob/living/user, params) + if(istype(W, /obj/item/wirecutters)) + dismantle(user, W) + else + return ..() diff --git a/code/modules/power/tesla/coil.dm b/code/modules/power/tesla/coil.dm index 6f1e84da526..5f58bcb5dd4 100644 --- a/code/modules/power/tesla/coil.dm +++ b/code/modules/power/tesla/coil.dm @@ -33,28 +33,48 @@ input_power_multiplier = power_multiplier /obj/machinery/power/tesla_coil/attackby(obj/item/W, mob/user, params) - if(default_deconstruction_screwdriver(user, "coil_open[anchored]", "coil[anchored]", W)) - return - if(exchange_parts(user, W)) return - if(default_unfasten_wrench(user, W)) + else if(istype(W, /obj/item/assembly/signaler) && panel_open) + wires.Interact(user) + + else + return ..() + +/obj/machinery/power/tesla_coil/crowbar_act(mob/user, obj/item/I) + . = TRUE + default_deconstruction_crowbar(user, I) + +/obj/machinery/power/tesla_coil/multitool_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(panel_open) + wires.Interact(user) + +/obj/machinery/power/tesla_coil/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + default_deconstruction_screwdriver(user, "coil_open[anchored]", "coil[anchored]", I) + +/obj/machinery/power/tesla_coil/wirecutter_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(panel_open) + wires.Interact(user) + +/obj/machinery/power/tesla_coil/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(default_unfasten_wrench(user, I)) if(!anchored) disconnect_from_network() else connect_to_network() - return - - if(default_deconstruction_crowbar(W)) - return - - else if(iswirecutter(W) || ismultitool(W) || istype(W, /obj/item/assembly/signaler)) - if(panel_open) - wires.Interact(user) - - else - return ..() /obj/machinery/power/tesla_coil/tesla_act(var/power) if(anchored && !panel_open) @@ -97,22 +117,23 @@ RefreshParts() /obj/machinery/power/grounding_rod/attackby(obj/item/W, mob/user, params) - if(default_deconstruction_screwdriver(user, "grounding_rod_open[anchored]", "grounding_rod[anchored]", W)) - return - if(exchange_parts(user, W)) return - if(default_unfasten_wrench(user, W)) - return +/obj/machinery/power/grounding_rod/screwdriver_act(mob/user, obj/item/I) + . = TRUE + default_deconstruction_screwdriver(user, "grounding_rod_open[anchored]", "grounding_rod[anchored]", I) - if(default_deconstruction_crowbar(W)) - return +/obj/machinery/power/grounding_rod/wrench_act(mob/user, obj/item/I) + . = TRUE + default_unfasten_wrench(user, I) - return ..() +/obj/machinery/power/grounding_rod/crowbar_act(mob/user, obj/item/I) + . = TRUE + default_deconstruction_crowbar(user, I) /obj/machinery/power/grounding_rod/tesla_act(var/power) if(anchored && !panel_open) flick("grounding_rodhit", src) else - ..() \ No newline at end of file + ..() diff --git a/code/modules/power/tesla/energy_ball.dm b/code/modules/power/tesla/energy_ball.dm index 7d7910b45d4..a3a8bb42ec6 100644 --- a/code/modules/power/tesla/energy_ball.dm +++ b/code/modules/power/tesla/energy_ball.dm @@ -288,4 +288,4 @@ closest_blob.tesla_act(power, explosive) else if(closest_structure) - closest_structure.tesla_act(power, explosive) \ No newline at end of file + closest_structure.tesla_act(power, explosive) diff --git a/code/modules/power/tesla/generator.dm b/code/modules/power/tesla/generator.dm index 53eea847ad9..63bce099fab 100644 --- a/code/modules/power/tesla/generator.dm +++ b/code/modules/power/tesla/generator.dm @@ -7,4 +7,4 @@ /obj/machinery/the_singularitygen/tesla/tesla_act(power, explosive = FALSE) if(explosive) - energy += power \ No newline at end of file + energy += power diff --git a/code/modules/power/tracker.dm b/code/modules/power/tracker.dm index 5706eb6d3da..c8964c7ec34 100644 --- a/code/modules/power/tracker.dm +++ b/code/modules/power/tracker.dm @@ -1,100 +1,100 @@ -//Solar tracker - -//Machine that tracks the sun and reports it's direction to the solar controllers -//As long as this is working, solar panels on same powernet will track automatically - -/obj/machinery/power/tracker - name = "solar tracker" - desc = "A solar directional tracker." - icon = 'icons/obj/power.dmi' - icon_state = "tracker" - density = TRUE - use_power = NO_POWER_USE - max_integrity = 250 - integrity_failure = 50 - - var/id = 0 - var/sun_angle = 0 // sun angle as set by sun datum - var/obj/machinery/power/solar_control/control = null - -/obj/machinery/power/tracker/Initialize(mapload, obj/item/solar_assembly/S) - . = ..() - Make(S) - connect_to_network() - -/obj/machinery/power/tracker/Destroy() - unset_control() //remove from control computer - return ..() - -//set the control of the tracker to a given computer if closer than SOLAR_MAX_DIST -/obj/machinery/power/tracker/proc/set_control(obj/machinery/power/solar_control/SC) - if(SC && (get_dist(src, SC) > SOLAR_MAX_DIST)) - return 0 - control = SC - SC.connected_tracker = src - return 1 - -//set the control of the tracker to null and removes it from the previous control computer if needed -/obj/machinery/power/tracker/proc/unset_control() - if(control) - control.connected_tracker = null - control = null - -/obj/machinery/power/tracker/proc/Make(obj/item/solar_assembly/S) - if(!S) - S = new /obj/item/solar_assembly(src) - S.glass_type = /obj/item/stack/sheet/glass - S.tracker = 1 - S.anchored = TRUE - S.forceMove(src) - update_icon() - -//updates the tracker icon and the facing angle for the control computer -/obj/machinery/power/tracker/proc/set_angle(angle) - sun_angle = angle - - //set icon dir to show sun illumination - setDir(turn(NORTH, -angle - 22.5)) // 22.5 deg bias ensures, e.g. 67.5-112.5 is EAST - - if(powernet && (powernet == control.powernet)) //update if we're still in the same powernet - control.cdir = angle - -/obj/machinery/power/tracker/attackby(var/obj/item/W, var/mob/user) - if(iscrowbar(W)) - playsound(src.loc, 'sound/machines/click.ogg', 50, 1) - user.visible_message("[user] begins to take the glass off the solar tracker.") - if(do_after(user, 50 * W.toolspeed, target = src)) - playsound(src.loc, W.usesound, 50, 1) - user.visible_message("[user] takes the glass off the tracker.") - deconstruct(TRUE) - return - return ..() - -/obj/machinery/power/tracker/obj_break(damage_flag) - if(!(stat & BROKEN) && !(flags & NODECONSTRUCT)) - playsound(loc, 'sound/effects/glassbr3.ogg', 100, TRUE) - stat |= BROKEN - unset_control() - -/obj/machinery/power/solar/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - if(disassembled) - var/obj/item/solar_assembly/S = locate() in src - if(S) - S.forceMove(loc) - S.give_glass(stat & BROKEN) - else - playsound(src, "shatter", 70, TRUE) - new /obj/item/shard(src.loc) - new /obj/item/shard(src.loc) - qdel(src) - -// Tracker Electronic - -/obj/item/tracker_electronics - - name = "tracker electronics" - icon = 'icons/obj/doors/door_assembly.dmi' - icon_state = "door_electronics" - w_class = WEIGHT_CLASS_SMALL - origin_tech = "engineering=2;programming=1" +//Solar tracker + +//Machine that tracks the sun and reports it's direction to the solar controllers +//As long as this is working, solar panels on same powernet will track automatically + +/obj/machinery/power/tracker + name = "solar tracker" + desc = "A solar directional tracker." + icon = 'icons/obj/power.dmi' + icon_state = "tracker" + density = TRUE + use_power = NO_POWER_USE + max_integrity = 250 + integrity_failure = 50 + + var/id = 0 + var/sun_angle = 0 // sun angle as set by sun datum + var/obj/machinery/power/solar_control/control = null + +/obj/machinery/power/tracker/Initialize(mapload, obj/item/solar_assembly/S) + . = ..() + Make(S) + connect_to_network() + +/obj/machinery/power/tracker/Destroy() + unset_control() //remove from control computer + return ..() + +//set the control of the tracker to a given computer if closer than SOLAR_MAX_DIST +/obj/machinery/power/tracker/proc/set_control(obj/machinery/power/solar_control/SC) + if(SC && (get_dist(src, SC) > SOLAR_MAX_DIST)) + return 0 + control = SC + SC.connected_tracker = src + return 1 + +//set the control of the tracker to null and removes it from the previous control computer if needed +/obj/machinery/power/tracker/proc/unset_control() + if(control) + control.connected_tracker = null + control = null + +/obj/machinery/power/tracker/proc/Make(obj/item/solar_assembly/S) + if(!S) + S = new /obj/item/solar_assembly(src) + S.glass_type = /obj/item/stack/sheet/glass + S.tracker = 1 + S.anchored = TRUE + S.forceMove(src) + update_icon() + +//updates the tracker icon and the facing angle for the control computer +/obj/machinery/power/tracker/proc/set_angle(angle) + sun_angle = angle + + //set icon dir to show sun illumination + setDir(turn(NORTH, -angle - 22.5)) // 22.5 deg bias ensures, e.g. 67.5-112.5 is EAST + + if(powernet && (powernet == control.powernet)) //update if we're still in the same powernet + control.cdir = angle + +/obj/machinery/power/tracker/crowbar_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + playsound(src.loc, 'sound/machines/click.ogg', 50, 1) + user.visible_message("[user] begins to take the glass off the solar tracker.") + if(I.use_tool(src, user, 50, volume = I.tool_volume)) + user.visible_message("[user] takes the glass off the tracker.") + deconstruct(TRUE) + + +/obj/machinery/power/tracker/obj_break(damage_flag) + if(!(stat & BROKEN) && !(flags & NODECONSTRUCT)) + playsound(loc, 'sound/effects/glassbr3.ogg', 100, TRUE) + stat |= BROKEN + unset_control() + +/obj/machinery/power/solar/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + if(disassembled) + var/obj/item/solar_assembly/S = locate() in src + if(S) + S.forceMove(loc) + S.give_glass(stat & BROKEN) + else + playsound(src, "shatter", 70, TRUE) + new /obj/item/shard(src.loc) + new /obj/item/shard(src.loc) + qdel(src) + +// Tracker Electronic + +/obj/item/tracker_electronics + + name = "tracker electronics" + icon = 'icons/obj/doors/door_assembly.dmi' + icon_state = "door_electronics" + w_class = WEIGHT_CLASS_SMALL + origin_tech = "engineering=2;programming=1" diff --git a/code/modules/power/turbine.dm b/code/modules/power/turbine.dm index 6f3c0856406..97499ba6b3b 100644 --- a/code/modules/power/turbine.dm +++ b/code/modules/power/turbine.dm @@ -1,401 +1,405 @@ -// TURBINE v2 AKA rev4407 Engine reborn! - -// How to use it? - Mappers -// -// This is a very good power generating mechanism. All you need is a blast furnace with soaring flames and output. -// Not everything is included yet so the turbine can run out of fuel quiet quickly. The best thing about the turbine is that even -// though something is on fire that passes through it, it won't be on fire as it passes out of it. So the exhaust fumes can still -// containt unreacted fuel - plasma and oxygen that needs to be filtered out and re-routed back. This of course requires smart piping -// For a computer to work with the turbine the compressor requires a comp_id matching with the turbine computer's id. This will be -// subjected to a change in the near future mind you. Right now this method of generating power is a good backup but don't expect it -// become a main power source unless some work is done. Have fun. At 50k RPM it generates 60k power. So more than one turbine is needed! -// -// - Numbers -// -// Example setup S - sparker -// B - Blast doors into space for venting -// *BBB****BBB* C - Compressor -// S CT * T - Turbine -// * ^ * * V * D - Doors with firedoor -// **|***D**|** ^ - Fuel feed (Not vent, but a gas outlet) -// | | V - Suction vent (Like the ones in atmos -// - - -/obj/machinery/power/compressor - name = "compressor" - desc = "The compressor stage of a gas turbine generator." - icon = 'icons/obj/pipes.dmi' - icon_state = "compressor" - anchored = 1 - density = 1 - resistance_flags = FIRE_PROOF - var/obj/machinery/power/turbine/turbine - var/datum/gas_mixture/gas_contained - var/turf/simulated/inturf - var/starter = 0 - var/rpm = 0 - var/rpmtarget = 0 - var/capacity = 1e6 - var/comp_id = 0 - var/efficiency - - -/obj/machinery/power/turbine - name = "gas turbine generator" - desc = "A gas turbine used for backup power generation." - icon = 'icons/obj/pipes.dmi' - icon_state = "turbine" - anchored = 1 - density = 1 - resistance_flags = FIRE_PROOF - var/opened = 0 - var/obj/machinery/power/compressor/compressor - var/turf/simulated/outturf - var/lastgen - var/productivity = 1 - -/obj/machinery/computer/turbine_computer - name = "gas turbine control computer" - desc = "A computer to remotely control a gas turbine" - icon_screen = "turbinecomp" - icon_keyboard = "tech_key" - circuit = /obj/item/circuitboard/turbine_computer - var/obj/machinery/power/compressor/compressor - var/id = 0 - -// the inlet stage of the gas turbine electricity generator - -/obj/machinery/power/compressor/Initialize(mapload) - . = ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/power_compressor(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stack/cable_coil(null, 5) - RefreshParts() -// The inlet of the compressor is the direction it faces - - gas_contained = new - inturf = get_step(src, dir) - locate_machinery() - if(!turbine) - stat |= BROKEN - - -#define COMPFRICTION 5e5 -#define COMPSTARTERLOAD 2800 - - -// Crucial to make things work!!!! -// OLD FIX - explanation given down below. -// /obj/machinery/power/compressor/CanPass(atom/movable/mover, turf/target, height=0) -// return !density - -/obj/machinery/power/compressor/locate_machinery() - if(turbine) - return - turbine = locate() in get_step(src, get_dir(inturf, src)) - if(turbine) - turbine.locate_machinery() - -/obj/machinery/power/compressor/RefreshParts() - var/E = 0 - for(var/obj/item/stock_parts/manipulator/M in component_parts) - E += M.rating - efficiency = E / 6 - -/obj/machinery/power/compressor/attackby(obj/item/I, mob/user, params) - if(default_deconstruction_screwdriver(user, initial(icon_state), initial(icon_state), I)) - return - - if(default_change_direction_wrench(user, I)) - turbine = null - inturf = get_step(src, dir) - locate_machinery() - if(turbine) - to_chat(user, "Turbine connected.") - stat &= ~BROKEN - else - to_chat(user, "Turbine not connected.") - stat |= BROKEN - return - - if(exchange_parts(user, I)) - return - - if(default_deconstruction_crowbar(I)) - return - return ..() - -/obj/machinery/power/compressor/CanAtmosPass(turf/T) - return !density - -/obj/machinery/power/compressor/process() - if(!turbine) - stat = BROKEN - if(stat & BROKEN || panel_open) - return - if(!starter) - return - overlays.Cut() - - rpm = 0.9* rpm + 0.1 * rpmtarget - var/datum/gas_mixture/environment = inturf.return_air() - - // It's a simplified version taking only 1/10 of the moles from the turf nearby. It should be later changed into a better version - - var/transfer_moles = environment.total_moles()/10 - //var/transfer_moles = rpm/10000*capacity - var/datum/gas_mixture/removed = inturf.remove_air(transfer_moles) - gas_contained.merge(removed) - -// RPM function to include compression friction - be advised that too low/high of a compfriction value can make things screwy - - rpm = max(0, rpm - (rpm*rpm)/(COMPFRICTION*efficiency)) - - - if(starter && !(stat & NOPOWER)) - use_power(2800) - if(rpm<1000) - rpmtarget = 1000 - else - if(rpm<1000) - rpmtarget = 0 - - - if(rpm>50000) - overlays += image('icons/obj/pipes.dmi', "comp-o4", FLY_LAYER) - else if(rpm>10000) - overlays += image('icons/obj/pipes.dmi', "comp-o3", FLY_LAYER) - else if(rpm>2000) - overlays += image('icons/obj/pipes.dmi', "comp-o2", FLY_LAYER) - else if(rpm>500) - overlays += image('icons/obj/pipes.dmi', "comp-o1", FLY_LAYER) - //TODO: DEFERRED - -// These are crucial to working of a turbine - the stats modify the power output. TurbGenQ modifies how much raw energy can you get from -// rpms, TurbGenG modifies the shape of the curve - the lower the value the less straight the curve is. - -#define TURBPRES 9000000 -#define TURBGENQ 100000 -#define TURBGENG 0.5 - -/obj/machinery/power/turbine/Initialize(mapload) - . = ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/power_turbine(src) - component_parts += new /obj/item/stock_parts/capacitor(src) - component_parts += new /obj/item/stock_parts/capacitor(src) - component_parts += new /obj/item/stock_parts/capacitor(src) - component_parts += new /obj/item/stock_parts/capacitor(src) - component_parts += new /obj/item/stock_parts/capacitor(src) - component_parts += new /obj/item/stock_parts/capacitor(src) - component_parts += new /obj/item/stack/cable_coil(src, 5) - RefreshParts() -// The outlet is pointed at the direction of the turbine component - - outturf = get_step(src, dir) - locate_machinery() - if(!compressor) - stat |= BROKEN - -/obj/machinery/power/turbine/RefreshParts() - var/P = 0 - for(var/obj/item/stock_parts/capacitor/C in component_parts) - P += C.rating - productivity = P / 6 - -/obj/machinery/power/turbine/locate_machinery() - if(compressor) - return - compressor = locate() in get_step(src, get_dir(outturf, src)) - if(compressor) - compressor.locate_machinery() - -/obj/machinery/power/turbine/CanAtmosPass(turf/T) - return !density - -/obj/machinery/power/turbine/process() - - if(!compressor) - stat = BROKEN - - if((stat & BROKEN) || panel_open) - return - if(!compressor.starter) - return - overlays.Cut() - - // This is the power generation function. If anything is needed it's good to plot it in EXCEL before modifying - // the TURBGENQ and TURBGENG values - - lastgen = ((compressor.rpm / TURBGENQ)**TURBGENG) * TURBGENQ * productivity - - add_avail(lastgen) - - // Weird function but it works. Should be something else... - - var/newrpm = ((compressor.gas_contained.temperature) * compressor.gas_contained.total_moles())/4 - - newrpm = max(0, newrpm) - - if(!compressor.starter || newrpm > 1000) - compressor.rpmtarget = newrpm - - if(compressor.gas_contained.total_moles()>0) - var/oamount = min(compressor.gas_contained.total_moles(), (compressor.rpm+100)/35000*compressor.capacity) - var/datum/gas_mixture/removed = compressor.gas_contained.remove(oamount) - outturf.assume_air(removed) - -// If it works, put an overlay that it works! - - if(lastgen > 100) - overlays += image('icons/obj/pipes.dmi', "turb-o", FLY_LAYER) - - updateDialog() - -/obj/machinery/power/turbine/attack_hand(mob/user) - - if(..()) - return - - interact(user) - -/obj/machinery/power/turbine/attackby(obj/item/I, mob/user, params) - if(default_deconstruction_screwdriver(user, initial(icon_state), initial(icon_state), I)) - return - - if(default_change_direction_wrench(user, I)) - compressor = null - outturf = get_step(src, dir) - locate_machinery() - if(compressor) - to_chat(user, "Compressor connected.") - stat &= ~BROKEN - else - to_chat(user, "Compressor not connected.") - stat |= BROKEN - return - - if(exchange_parts(user, I)) - return - - if(default_deconstruction_crowbar(I)) - return - return ..() - -/obj/machinery/power/turbine/interact(mob/user) - - if( !Adjacent(user) || (stat & (NOPOWER|BROKEN)) && (!istype(user, /mob/living/silicon)) ) - user.unset_machine(src) - user << browse(null, "window=turbine") - return - - var/t = "Gas Turbine Generator
        "
        -
        -	t += "Generated power : [round(lastgen)] W

        " - - t += "Turbine: [round(compressor.rpm)] RPM
        " - - t += "Starter: [ compressor.starter ? "Off On" : "Off On"]" - - t += "

        Close" - - t += "
        " - var/datum/browser/popup = new(user, "turbine", name) - popup.set_content(t) - popup.open() - - return - -/obj/machinery/power/turbine/Topic(href, href_list) - if(..()) - return - - if( href_list["close"] ) - usr << browse(null, "window=turbine") - usr.unset_machine(src) - return - - else if( href_list["str"] ) - if(compressor) - compressor.starter = !compressor.starter - - updateDialog() - - - - - -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -// COMPUTER NEEDS A SERIOUS REWRITE. - - - -/obj/machinery/computer/turbine_computer/Initialize() - ..() - spawn(10) - locate_machinery() - -/obj/machinery/computer/turbine_computer/locate_machinery() - compressor = locate(/obj/machinery/power/compressor) in range(5, src) - -/obj/machinery/computer/turbine_computer/attack_hand(var/mob/user as mob) - if(..()) - return - - interact(user) - -/obj/machinery/computer/turbine_computer/interact(mob/user) - - var/dat - if(compressor && compressor.turbine) - dat += "
        Gas turbine remote control system
        " - if(compressor.stat || compressor.turbine.stat) - dat += "[compressor.stat ? "Compressor is inoperable
        " : "Turbine is inoperable"]" - else - dat += {"Turbine status: [ src.compressor.starter ? "Off On" : "Off On"] - \n
        - \nTurbine speed: [src.compressor.rpm]rpm
        - \nPower currently being generated: [src.compressor.turbine.lastgen]W
        - \nInternal gas temperature: [src.compressor.gas_contained.temperature]K
        - \n
        Close - \n
        - \n"} - else - dat += "There is [!compressor ? "no compressor" : " compressor[!compressor.turbine ? " but no turbine" : ""]"].
        " - if(!compressor) - dat += "Search for compressor" - - var/datum/browser/popup = new(user, "turbinecomputer", name) - popup.set_content(dat) - popup.open() - return - -/obj/machinery/computer/turbine_computer/Topic(href, href_list) - if(..()) - return - - else if( href_list["str"] ) - if(compressor && compressor.turbine) - compressor.starter = !compressor.starter - else if( href_list["close"] ) - usr << browse(null, "window=turbinecomputer") - usr.unset_machine(src) - return - else if(href_list["search"]) - locate_machinery() - - src.updateUsrDialog() - return - -/obj/machinery/computer/turbine_computer/process() - src.updateDialog() - return +// TURBINE v2 AKA rev4407 Engine reborn! + +// How to use it? - Mappers +// +// This is a very good power generating mechanism. All you need is a blast furnace with soaring flames and output. +// Not everything is included yet so the turbine can run out of fuel quiet quickly. The best thing about the turbine is that even +// though something is on fire that passes through it, it won't be on fire as it passes out of it. So the exhaust fumes can still +// containt unreacted fuel - plasma and oxygen that needs to be filtered out and re-routed back. This of course requires smart piping +// For a computer to work with the turbine the compressor requires a comp_id matching with the turbine computer's id. This will be +// subjected to a change in the near future mind you. Right now this method of generating power is a good backup but don't expect it +// become a main power source unless some work is done. Have fun. At 50k RPM it generates 60k power. So more than one turbine is needed! +// +// - Numbers +// +// Example setup S - sparker +// B - Blast doors into space for venting +// *BBB****BBB* C - Compressor +// S CT * T - Turbine +// * ^ * * V * D - Doors with firedoor +// **|***D**|** ^ - Fuel feed (Not vent, but a gas outlet) +// | | V - Suction vent (Like the ones in atmos +// + + +/obj/machinery/power/compressor + name = "compressor" + desc = "The compressor stage of a gas turbine generator." + icon = 'icons/obj/pipes.dmi' + icon_state = "compressor" + anchored = 1 + density = 1 + resistance_flags = FIRE_PROOF + var/obj/machinery/power/turbine/turbine + var/datum/gas_mixture/gas_contained + var/turf/simulated/inturf + var/starter = 0 + var/rpm = 0 + var/rpmtarget = 0 + var/capacity = 1e6 + var/comp_id = 0 + var/efficiency + + +/obj/machinery/power/turbine + name = "gas turbine generator" + desc = "A gas turbine used for backup power generation." + icon = 'icons/obj/pipes.dmi' + icon_state = "turbine" + anchored = 1 + density = 1 + resistance_flags = FIRE_PROOF + var/opened = 0 + var/obj/machinery/power/compressor/compressor + var/turf/simulated/outturf + var/lastgen + var/productivity = 1 + +/obj/machinery/computer/turbine_computer + name = "gas turbine control computer" + desc = "A computer to remotely control a gas turbine" + icon_screen = "turbinecomp" + icon_keyboard = "tech_key" + circuit = /obj/item/circuitboard/turbine_computer + var/obj/machinery/power/compressor/compressor + var/id = 0 + +// the inlet stage of the gas turbine electricity generator + +/obj/machinery/power/compressor/Initialize(mapload) + . = ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/power_compressor(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stack/cable_coil(null, 5) + RefreshParts() +// The inlet of the compressor is the direction it faces + + gas_contained = new + inturf = get_step(src, dir) + locate_machinery() + if(!turbine) + stat |= BROKEN + + +#define COMPFRICTION 5e5 +#define COMPSTARTERLOAD 2800 + + +// Crucial to make things work!!!! +// OLD FIX - explanation given down below. +// /obj/machinery/power/compressor/CanPass(atom/movable/mover, turf/target, height=0) +// return !density + +/obj/machinery/power/compressor/locate_machinery() + if(turbine) + return + turbine = locate() in get_step(src, get_dir(inturf, src)) + if(turbine) + turbine.locate_machinery() + +/obj/machinery/power/compressor/RefreshParts() + var/E = 0 + for(var/obj/item/stock_parts/manipulator/M in component_parts) + E += M.rating + efficiency = E / 6 + +/obj/machinery/power/compressor/attackby(obj/item/I, mob/user, params) + if(default_change_direction_wrench(user, I)) + turbine = null + inturf = get_step(src, dir) + locate_machinery() + if(turbine) + to_chat(user, "Turbine connected.") + stat &= ~BROKEN + else + to_chat(user, "Turbine not connected.") + stat |= BROKEN + return + + if(exchange_parts(user, I)) + return + + + return ..() + +/obj/machinery/power/compressor/crowbar_act(mob/user, obj/item/I) + if(default_deconstruction_crowbar(user, I)) + return TRUE + +/obj/machinery/power/compressor/screwdriver_act(mob/user, obj/item/I) + if(default_deconstruction_screwdriver(user, initial(icon_state), initial(icon_state), I)) + return TRUE + +/obj/machinery/power/compressor/CanAtmosPass(turf/T) + return !density + +/obj/machinery/power/compressor/process() + if(!turbine) + stat = BROKEN + if(stat & BROKEN || panel_open) + return + if(!starter) + return + overlays.Cut() + + rpm = 0.9* rpm + 0.1 * rpmtarget + var/datum/gas_mixture/environment = inturf.return_air() + + // It's a simplified version taking only 1/10 of the moles from the turf nearby. It should be later changed into a better version + + var/transfer_moles = environment.total_moles()/10 + //var/transfer_moles = rpm/10000*capacity + var/datum/gas_mixture/removed = inturf.remove_air(transfer_moles) + gas_contained.merge(removed) + +// RPM function to include compression friction - be advised that too low/high of a compfriction value can make things screwy + + rpm = max(0, rpm - (rpm*rpm)/(COMPFRICTION*efficiency)) + + + if(starter && !(stat & NOPOWER)) + use_power(2800) + if(rpm<1000) + rpmtarget = 1000 + else + if(rpm<1000) + rpmtarget = 0 + + + if(rpm>50000) + overlays += image('icons/obj/pipes.dmi', "comp-o4", FLY_LAYER) + else if(rpm>10000) + overlays += image('icons/obj/pipes.dmi', "comp-o3", FLY_LAYER) + else if(rpm>2000) + overlays += image('icons/obj/pipes.dmi', "comp-o2", FLY_LAYER) + else if(rpm>500) + overlays += image('icons/obj/pipes.dmi', "comp-o1", FLY_LAYER) + //TODO: DEFERRED + +// These are crucial to working of a turbine - the stats modify the power output. TurbGenQ modifies how much raw energy can you get from +// rpms, TurbGenG modifies the shape of the curve - the lower the value the less straight the curve is. + +#define TURBPRES 9000000 +#define TURBGENQ 100000 +#define TURBGENG 0.5 + +/obj/machinery/power/turbine/Initialize(mapload) + . = ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/power_turbine(src) + component_parts += new /obj/item/stock_parts/capacitor(src) + component_parts += new /obj/item/stock_parts/capacitor(src) + component_parts += new /obj/item/stock_parts/capacitor(src) + component_parts += new /obj/item/stock_parts/capacitor(src) + component_parts += new /obj/item/stock_parts/capacitor(src) + component_parts += new /obj/item/stock_parts/capacitor(src) + component_parts += new /obj/item/stack/cable_coil(src, 5) + RefreshParts() +// The outlet is pointed at the direction of the turbine component + + outturf = get_step(src, dir) + locate_machinery() + if(!compressor) + stat |= BROKEN + +/obj/machinery/power/turbine/RefreshParts() + var/P = 0 + for(var/obj/item/stock_parts/capacitor/C in component_parts) + P += C.rating + productivity = P / 6 + +/obj/machinery/power/turbine/locate_machinery() + if(compressor) + return + compressor = locate() in get_step(src, get_dir(outturf, src)) + if(compressor) + compressor.locate_machinery() + +/obj/machinery/power/turbine/CanAtmosPass(turf/T) + return !density + +/obj/machinery/power/turbine/process() + + if(!compressor) + stat = BROKEN + + if((stat & BROKEN) || panel_open) + return + if(!compressor.starter) + return + overlays.Cut() + + // This is the power generation function. If anything is needed it's good to plot it in EXCEL before modifying + // the TURBGENQ and TURBGENG values + + lastgen = ((compressor.rpm / TURBGENQ)**TURBGENG) * TURBGENQ * productivity + + add_avail(lastgen) + + // Weird function but it works. Should be something else... + + var/newrpm = ((compressor.gas_contained.temperature) * compressor.gas_contained.total_moles())/4 + + newrpm = max(0, newrpm) + + if(!compressor.starter || newrpm > 1000) + compressor.rpmtarget = newrpm + + if(compressor.gas_contained.total_moles()>0) + var/oamount = min(compressor.gas_contained.total_moles(), (compressor.rpm+100)/35000*compressor.capacity) + var/datum/gas_mixture/removed = compressor.gas_contained.remove(oamount) + outturf.assume_air(removed) + +// If it works, put an overlay that it works! + + if(lastgen > 100) + overlays += image('icons/obj/pipes.dmi', "turb-o", FLY_LAYER) + + updateDialog() + +/obj/machinery/power/turbine/attack_hand(mob/user) + + if(..()) + return + + interact(user) + +/obj/machinery/power/turbine/attackby(obj/item/I, mob/user, params) + if(default_deconstruction_screwdriver(user, initial(icon_state), initial(icon_state), I)) + return + + if(default_change_direction_wrench(user, I)) + compressor = null + outturf = get_step(src, dir) + locate_machinery() + if(compressor) + to_chat(user, "Compressor connected.") + stat &= ~BROKEN + else + to_chat(user, "Compressor not connected.") + stat |= BROKEN + return + + if(exchange_parts(user, I)) + return + + if(default_deconstruction_crowbar(user, I)) + return + return ..() + +/obj/machinery/power/turbine/interact(mob/user) + + if( !Adjacent(user) || (stat & (NOPOWER|BROKEN)) && (!istype(user, /mob/living/silicon)) ) + user.unset_machine(src) + user << browse(null, "window=turbine") + return + + var/t = "Gas Turbine Generator
        "
        +
        +	t += "Generated power : [round(lastgen)] W

        " + + t += "Turbine: [round(compressor.rpm)] RPM
        " + + t += "Starter: [ compressor.starter ? "Off On" : "Off On"]" + + t += "

        Close" + + t += "
        " + var/datum/browser/popup = new(user, "turbine", name) + popup.set_content(t) + popup.open() + + return + +/obj/machinery/power/turbine/Topic(href, href_list) + if(..()) + return + + if( href_list["close"] ) + usr << browse(null, "window=turbine") + usr.unset_machine(src) + return + + else if( href_list["str"] ) + if(compressor) + compressor.starter = !compressor.starter + + updateDialog() + + + + + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// COMPUTER NEEDS A SERIOUS REWRITE. + + + +/obj/machinery/computer/turbine_computer/Initialize() + ..() + spawn(10) + locate_machinery() + +/obj/machinery/computer/turbine_computer/locate_machinery() + compressor = locate(/obj/machinery/power/compressor) in range(5, src) + +/obj/machinery/computer/turbine_computer/attack_hand(var/mob/user as mob) + if(..()) + return + + interact(user) + +/obj/machinery/computer/turbine_computer/interact(mob/user) + + var/dat + if(compressor && compressor.turbine) + dat += "
        Gas turbine remote control system
        " + if(compressor.stat || compressor.turbine.stat) + dat += "[compressor.stat ? "Compressor is inoperable
        " : "Turbine is inoperable"]" + else + dat += {"Turbine status: [ src.compressor.starter ? "Off On" : "Off On"] + \n
        + \nTurbine speed: [src.compressor.rpm]rpm
        + \nPower currently being generated: [src.compressor.turbine.lastgen]W
        + \nInternal gas temperature: [src.compressor.gas_contained.temperature]K
        + \n
        Close + \n
        + \n"} + else + dat += "There is [!compressor ? "no compressor" : " compressor[!compressor.turbine ? " but no turbine" : ""]"].
        " + if(!compressor) + dat += "Search for compressor" + + var/datum/browser/popup = new(user, "turbinecomputer", name) + popup.set_content(dat) + popup.open() + return + +/obj/machinery/computer/turbine_computer/Topic(href, href_list) + if(..()) + return + + else if( href_list["str"] ) + if(compressor && compressor.turbine) + compressor.starter = !compressor.starter + else if( href_list["close"] ) + usr << browse(null, "window=turbinecomputer") + usr.unset_machine(src) + return + else if(href_list["search"]) + locate_machinery() + + src.updateUsrDialog() + return + +/obj/machinery/computer/turbine_computer/process() + src.updateDialog() + return diff --git a/code/modules/procedural_mapping/mapGeneratorModule.dm b/code/modules/procedural_mapping/mapGeneratorModule.dm index 030c61e972a..e8db8c8390b 100644 --- a/code/modules/procedural_mapping/mapGeneratorModule.dm +++ b/code/modules/procedural_mapping/mapGeneratorModule.dm @@ -142,4 +142,4 @@ /datum/mapGeneratorModule/denseLayer clusterCheckFlags = CLUSTER_CHECK_NONE spawnableAtoms = list(/atom = 75) - spawnableTurfs = list(/turf = 75) \ No newline at end of file + spawnableTurfs = list(/turf = 75) diff --git a/code/modules/procedural_mapping/mapGeneratorReadme.dm b/code/modules/procedural_mapping/mapGeneratorReadme.dm index ae656b5b30b..ce34e3729cb 100644 --- a/code/modules/procedural_mapping/mapGeneratorReadme.dm +++ b/code/modules/procedural_mapping/mapGeneratorReadme.dm @@ -143,4 +143,4 @@ Variable Breakdown (For Mappers): -*/ \ No newline at end of file +*/ diff --git a/code/modules/procedural_mapping/mapGenerators/asteroid.dm b/code/modules/procedural_mapping/mapGenerators/asteroid.dm index 10fe0fc9332..90ca219588e 100644 --- a/code/modules/procedural_mapping/mapGenerators/asteroid.dm +++ b/code/modules/procedural_mapping/mapGenerators/asteroid.dm @@ -42,4 +42,4 @@ /datum/mapGeneratorModule/splatterLayer/asteroidMonsters) /datum/mapGenerator/asteroid/filled - modules = list(/datum/mapGeneratorModule/bottomLayer/asteroidWalls) \ No newline at end of file + modules = list(/datum/mapGeneratorModule/bottomLayer/asteroidWalls) diff --git a/code/modules/procedural_mapping/mapGenerators/lava_river.dm b/code/modules/procedural_mapping/mapGenerators/lava_river.dm index 86f9bd68baf..19a61aaabe6 100644 --- a/code/modules/procedural_mapping/mapGenerators/lava_river.dm +++ b/code/modules/procedural_mapping/mapGenerators/lava_river.dm @@ -22,4 +22,4 @@ var/datum/mapGenerator/lavaland/L = mother if(!istype(L)) return - spawn_rivers(L.start_z, river_nodes, river_type, min_x = L.min_x, min_y = L.min_y, max_x = L.max_x, max_y = L.max_y) \ No newline at end of file + spawn_rivers(L.start_z, river_nodes, river_type, min_x = L.min_x, min_y = L.min_y, max_x = L.max_x, max_y = L.max_y) diff --git a/code/modules/procedural_mapping/mapGenerators/lavaland.dm b/code/modules/procedural_mapping/mapGenerators/lavaland.dm index c65a0d84d71..a0ba2ae816e 100644 --- a/code/modules/procedural_mapping/mapGenerators/lavaland.dm +++ b/code/modules/procedural_mapping/mapGenerators/lavaland.dm @@ -26,4 +26,4 @@ modules = list(/datum/mapGeneratorModule/bottomLayer/lavaland_mineral/dense) /datum/mapGenerator/lavaland/normal_ores - modules = list(/datum/mapGeneratorModule/bottomLayer/lavaland_mineral) \ No newline at end of file + modules = list(/datum/mapGeneratorModule/bottomLayer/lavaland_mineral) diff --git a/code/modules/projectiles/ammunition.dm b/code/modules/projectiles/ammunition.dm index 726580573e3..f8b13b47874 100644 --- a/code/modules/projectiles/ammunition.dm +++ b/code/modules/projectiles/ammunition.dm @@ -1,221 +1,222 @@ -/obj/item/ammo_casing - name = "bullet casing" - desc = "A bullet casing." - icon = 'icons/obj/ammo.dmi' - icon_state = "s-casing" - flags = CONDUCT - slot_flags = SLOT_BELT - throwforce = 1 - w_class = WEIGHT_CLASS_TINY - var/fire_sound = null //What sound should play when this ammo is fired - var/drop_sound = "casingdrop" //What sound should play when this ammo hits the ground - var/caliber = null //Which kind of guns it can be loaded into - var/projectile_type = null //The bullet type to create when New() is called - var/obj/item/projectile/BB = null //The loaded bullet - var/pellets = 1 //Pellets for spreadshot - var/variance = 0 //Variance for inaccuracy fundamental to the casing - var/delay = 0 //Delay for energy weapons - var/randomspread = 0 //Randomspread for automatics - var/click_cooldown_override = 0 //Override this to make your gun have a faster fire rate, in tenths of a second. 4 is the default gun cooldown. - var/harmful = TRUE //pacifism check for boolet, set to FALSE if bullet is non-lethal - -/obj/item/ammo_casing/New() - ..() - if(projectile_type) - BB = new projectile_type(src) - pixel_x = rand(-10.0, 10) - pixel_y = rand(-10.0, 10) - dir = pick(alldirs) - update_icon() - -/obj/item/ammo_casing/update_icon() - ..() - icon_state = "[initial(icon_state)][BB ? "-live" : ""]" - desc = "[initial(desc)][BB ? "" : " This one is spent."]" - -/obj/item/ammo_casing/proc/newshot(params) //For energy weapons, shotgun shells and wands (!). - if(!BB) - BB = new projectile_type(src, params) - return - -/obj/item/ammo_casing/attackby(obj/item/I as obj, mob/user as mob, params) - if(istype(I, /obj/item/ammo_box)) - var/obj/item/ammo_box/box = I - if(isturf(loc)) - var/boolets = 0 - for(var/obj/item/ammo_casing/bullet in loc) - if(box.stored_ammo.len >= box.max_ammo) - break - if(bullet.BB) - if(box.give_round(bullet, 0)) - boolets++ - else - continue - if(boolets > 0) - box.update_icon() - to_chat(user, "You collect [boolets] shell\s. [box] now contains [box.stored_ammo.len] shell\s.") - playsound(src, 'sound/weapons/gun_interactions/bulletinsert.ogg', 50, 1) - else - to_chat(user, "You fail to collect anything!") - else - if(istype(I, /obj/item/screwdriver)) - if(BB) - if(initial(BB.name) == "bullet") - var/tmp_label = "" - var/label_text = sanitize(input(user, "Inscribe some text into \the [initial(BB.name)]","Inscription",tmp_label)) - if(length(label_text) > 20) - to_chat(user, "The inscription can be at most 20 characters long.") - else - if(label_text == "") - to_chat(user, "You scratch the inscription off of [initial(BB)].") - BB.name = initial(BB.name) - else - to_chat(user, "You inscribe \"[label_text]\" into \the [initial(BB.name)].") - BB.name = "[initial(BB.name)] \"[label_text]\"" - else - to_chat(user, "You can only inscribe a metal bullet.")//because inscribing beanbags is silly - - else - to_chat(user, "There is no bullet in the casing to inscribe anything into.") - ..() - -//Boxes of ammo -/obj/item/ammo_box - name = "ammo box (generic)" - desc = "A box of ammo?" - icon_state = "357" - icon = 'icons/obj/ammo.dmi' - flags = CONDUCT - slot_flags = SLOT_BELT - item_state = "syringe_kit" - materials = list(MAT_METAL = 30000) - throwforce = 2 - w_class = WEIGHT_CLASS_TINY - throw_speed = 4 - throw_range = 10 - var/list/stored_ammo = list() - var/ammo_type = /obj/item/ammo_casing - var/max_ammo = 7 - var/multiple_sprites = 0 - var/caliber - var/multiload = 1 - var/list/initial_mats //For calculating refund values. - -/obj/item/ammo_box/New() - for(var/i in 1 to max_ammo) - stored_ammo += new ammo_type(src) - update_icon() - initial_mats = materials.Copy() - update_mat_value() - -/obj/item/ammo_box/Destroy() - QDEL_LIST(stored_ammo) - stored_ammo = null - return ..() - -/obj/item/ammo_box/proc/get_round(keep = 0) - if(!stored_ammo.len) - return null - else - var/b = stored_ammo[stored_ammo.len] - stored_ammo -= b - if(keep) - stored_ammo.Insert(1,b) - update_mat_value() - return b - -/obj/item/ammo_box/proc/give_round(obj/item/ammo_casing/R, replace_spent = 0) - // Boxes don't have a caliber type, magazines do. Not sure if it's intended or not, but if we fail to find a caliber, then we fall back to ammo_type. - if(!R || (caliber && R.caliber != caliber) || (!caliber && R.type != ammo_type)) - return 0 - - if(stored_ammo.len < max_ammo) - stored_ammo += R - R.loc = src - playsound(src, 'sound/weapons/gun_interactions/bulletinsert.ogg', 50, 1) - update_mat_value() - return 1 - //for accessibles magazines (e.g internal ones) when full, start replacing spent ammo - else if(replace_spent) - for(var/obj/item/ammo_casing/AC in stored_ammo) - if(!AC.BB)//found a spent ammo - stored_ammo -= AC - AC.loc = get_turf(loc) - - stored_ammo += R - R.loc = src - playsound(src, 'sound/weapons/gun_interactions/shotguninsert.ogg', 50, 1) - update_mat_value() - return 1 - - return 0 - -/obj/item/ammo_box/proc/can_load(mob/user) - return 1 - -/obj/item/ammo_box/attackby(obj/item/A, mob/user, params, silent = 0, replace_spent = 0) - var/num_loaded = 0 - if(!can_load(user)) - return - if(istype(A, /obj/item/ammo_box)) - var/obj/item/ammo_box/AM = A - for(var/obj/item/ammo_casing/AC in AM.stored_ammo) - var/did_load = give_round(AC, replace_spent) - if(did_load) - AM.stored_ammo -= AC - num_loaded++ - if(!multiload || !did_load) - break - if(istype(A, /obj/item/ammo_casing)) - var/obj/item/ammo_casing/AC = A - if(give_round(AC, replace_spent)) - user.drop_item() - AC.loc = src - num_loaded++ - if(num_loaded) - if(!silent) - to_chat(user, "You load [num_loaded] shell\s into \the [src]!") - playsound(src, 'sound/weapons/gun_interactions/shotguninsert.ogg', 50, 1) - A.update_icon() - update_icon() - - return num_loaded - -/obj/item/ammo_box/attack_self(mob/user as mob) - var/obj/item/ammo_casing/A = get_round() - if(A) - user.put_in_hands(A) - playsound(src, 'sound/weapons/gun_interactions/remove_bullet.ogg', 50, 1) - to_chat(user, "You remove a round from \the [src]!") - update_icon() - -/obj/item/ammo_box/update_icon() - switch(multiple_sprites) - if(1) - icon_state = "[initial(icon_state)]-[stored_ammo.len]" - if(2) - icon_state = "[initial(icon_state)]-[stored_ammo.len ? "[max_ammo]" : "0"]" - desc = "[initial(desc)] There are [stored_ammo.len] shell\s left!" - -/obj/item/ammo_box/proc/update_mat_value() - var/num_ammo = 0 - for(var/B in stored_ammo) - var/obj/item/ammo_casing/AC = B - if(!AC.BB) //Skip any casing which are empty - continue - num_ammo++ - for(var/M in initial_mats) //In case we have multiple types of materials - var/materials_per = initial_mats[M] / max_ammo - - var/value = max(materials_per * num_ammo, 500) //Enforce a minimum of 500 units even if empty. - materials[M] = value - -//Behavior for magazines -/obj/item/ammo_box/magazine/proc/ammo_count() - return stored_ammo.len - -/obj/item/ammo_box/magazine/proc/empty_magazine() - var/turf_mag = get_turf(src) - for(var/obj/item/ammo in stored_ammo) - ammo.forceMove(turf_mag) - stored_ammo -= ammo +/obj/item/ammo_casing + name = "bullet casing" + desc = "A bullet casing." + icon = 'icons/obj/ammo.dmi' + icon_state = "s-casing" + flags = CONDUCT + slot_flags = SLOT_BELT + throwforce = 1 + w_class = WEIGHT_CLASS_TINY + var/fire_sound = null //What sound should play when this ammo is fired + var/drop_sound = "casingdrop" //What sound should play when this ammo hits the ground + var/caliber = null //Which kind of guns it can be loaded into + var/projectile_type = null //The bullet type to create when New() is called + var/obj/item/projectile/BB = null //The loaded bullet + var/pellets = 1 //Pellets for spreadshot + var/variance = 0 //Variance for inaccuracy fundamental to the casing + var/delay = 0 //Delay for energy weapons + var/randomspread = 0 //Randomspread for automatics + var/click_cooldown_override = 0 //Override this to make your gun have a faster fire rate, in tenths of a second. 4 is the default gun cooldown. + var/harmful = TRUE //pacifism check for boolet, set to FALSE if bullet is non-lethal + +/obj/item/ammo_casing/New() + ..() + if(projectile_type) + BB = new projectile_type(src) + pixel_x = rand(-10.0, 10) + pixel_y = rand(-10.0, 10) + dir = pick(alldirs) + update_icon() + +/obj/item/ammo_casing/update_icon() + ..() + icon_state = "[initial(icon_state)][BB ? "-live" : ""]" + desc = "[initial(desc)][BB ? "" : " This one is spent."]" + +/obj/item/ammo_casing/proc/newshot(params) //For energy weapons, shotgun shells and wands (!). + if(!BB) + BB = new projectile_type(src, params) + return + +/obj/item/ammo_casing/attackby(obj/item/I as obj, mob/user as mob, params) + if(istype(I, /obj/item/ammo_box)) + var/obj/item/ammo_box/box = I + if(isturf(loc)) + var/boolets = 0 + for(var/obj/item/ammo_casing/bullet in loc) + if(box.stored_ammo.len >= box.max_ammo) + break + if(bullet.BB) + if(box.give_round(bullet, 0)) + boolets++ + else + continue + if(boolets > 0) + box.update_icon() + to_chat(user, "You collect [boolets] shell\s. [box] now contains [box.stored_ammo.len] shell\s.") + playsound(src, 'sound/weapons/gun_interactions/bulletinsert.ogg', 50, 1) + else + to_chat(user, "You fail to collect anything!") + else + if(istype(I, /obj/item/screwdriver)) + if(BB) + if(initial(BB.name) == "bullet") + var/tmp_label = "" + var/label_text = sanitize(input(user, "Inscribe some text into \the [initial(BB.name)]","Inscription",tmp_label)) + if(length(label_text) > 20) + to_chat(user, "The inscription can be at most 20 characters long.") + else + if(label_text == "") + to_chat(user, "You scratch the inscription off of [initial(BB)].") + BB.name = initial(BB.name) + else + to_chat(user, "You inscribe \"[label_text]\" into \the [initial(BB.name)].") + BB.name = "[initial(BB.name)] \"[label_text]\"" + else + to_chat(user, "You can only inscribe a metal bullet.")//because inscribing beanbags is silly + + else + to_chat(user, "There is no bullet in the casing to inscribe anything into.") + ..() + +//Boxes of ammo +/obj/item/ammo_box + name = "ammo box (generic)" + desc = "A box of ammo?" + icon_state = "357" + icon = 'icons/obj/ammo.dmi' + flags = CONDUCT + slot_flags = SLOT_BELT + item_state = "syringe_kit" + materials = list(MAT_METAL = 30000) + throwforce = 2 + w_class = WEIGHT_CLASS_TINY + throw_speed = 4 + throw_range = 10 + var/list/stored_ammo = list() + var/ammo_type = /obj/item/ammo_casing + var/max_ammo = 7 + var/multiple_sprites = 0 + var/caliber + var/multiload = 1 + var/list/initial_mats //For calculating refund values. + +/obj/item/ammo_box/New() + ..() + for(var/i in 1 to max_ammo) + stored_ammo += new ammo_type(src) + update_icon() + initial_mats = materials.Copy() + update_mat_value() + +/obj/item/ammo_box/Destroy() + QDEL_LIST(stored_ammo) + stored_ammo = null + return ..() + +/obj/item/ammo_box/proc/get_round(keep = 0) + if(!stored_ammo.len) + return null + else + var/b = stored_ammo[stored_ammo.len] + stored_ammo -= b + if(keep) + stored_ammo.Insert(1,b) + update_mat_value() + return b + +/obj/item/ammo_box/proc/give_round(obj/item/ammo_casing/R, replace_spent = 0) + // Boxes don't have a caliber type, magazines do. Not sure if it's intended or not, but if we fail to find a caliber, then we fall back to ammo_type. + if(!R || (caliber && R.caliber != caliber) || (!caliber && R.type != ammo_type)) + return 0 + + if(stored_ammo.len < max_ammo) + stored_ammo += R + R.loc = src + playsound(src, 'sound/weapons/gun_interactions/bulletinsert.ogg', 50, 1) + update_mat_value() + return 1 + //for accessibles magazines (e.g internal ones) when full, start replacing spent ammo + else if(replace_spent) + for(var/obj/item/ammo_casing/AC in stored_ammo) + if(!AC.BB)//found a spent ammo + stored_ammo -= AC + AC.loc = get_turf(loc) + + stored_ammo += R + R.loc = src + playsound(src, 'sound/weapons/gun_interactions/shotguninsert.ogg', 50, 1) + update_mat_value() + return 1 + + return 0 + +/obj/item/ammo_box/proc/can_load(mob/user) + return 1 + +/obj/item/ammo_box/attackby(obj/item/A, mob/user, params, silent = 0, replace_spent = 0) + var/num_loaded = 0 + if(!can_load(user)) + return + if(istype(A, /obj/item/ammo_box)) + var/obj/item/ammo_box/AM = A + for(var/obj/item/ammo_casing/AC in AM.stored_ammo) + var/did_load = give_round(AC, replace_spent) + if(did_load) + AM.stored_ammo -= AC + num_loaded++ + if(!multiload || !did_load) + break + if(istype(A, /obj/item/ammo_casing)) + var/obj/item/ammo_casing/AC = A + if(give_round(AC, replace_spent)) + user.drop_item() + AC.loc = src + num_loaded++ + if(num_loaded) + if(!silent) + to_chat(user, "You load [num_loaded] shell\s into \the [src]!") + playsound(src, 'sound/weapons/gun_interactions/shotguninsert.ogg', 50, 1) + A.update_icon() + update_icon() + + return num_loaded + +/obj/item/ammo_box/attack_self(mob/user as mob) + var/obj/item/ammo_casing/A = get_round() + if(A) + user.put_in_hands(A) + playsound(src, 'sound/weapons/gun_interactions/remove_bullet.ogg', 50, 1) + to_chat(user, "You remove a round from \the [src]!") + update_icon() + +/obj/item/ammo_box/update_icon() + switch(multiple_sprites) + if(1) + icon_state = "[initial(icon_state)]-[stored_ammo.len]" + if(2) + icon_state = "[initial(icon_state)]-[stored_ammo.len ? "[max_ammo]" : "0"]" + desc = "[initial(desc)] There are [stored_ammo.len] shell\s left!" + +/obj/item/ammo_box/proc/update_mat_value() + var/num_ammo = 0 + for(var/B in stored_ammo) + var/obj/item/ammo_casing/AC = B + if(!AC.BB) //Skip any casing which are empty + continue + num_ammo++ + for(var/M in initial_mats) //In case we have multiple types of materials + var/materials_per = initial_mats[M] / max_ammo + + var/value = max(materials_per * num_ammo, 500) //Enforce a minimum of 500 units even if empty. + materials[M] = value + +//Behavior for magazines +/obj/item/ammo_box/magazine/proc/ammo_count() + return stored_ammo.len + +/obj/item/ammo_box/magazine/proc/empty_magazine() + var/turf_mag = get_turf(src) + for(var/obj/item/ammo in stored_ammo) + ammo.forceMove(turf_mag) + stored_ammo -= ammo diff --git a/code/modules/projectiles/ammunition/boxes.dm b/code/modules/projectiles/ammunition/boxes.dm index ae88bc9db61..4484be63c83 100644 --- a/code/modules/projectiles/ammunition/boxes.dm +++ b/code/modules/projectiles/ammunition/boxes.dm @@ -1,148 +1,148 @@ -/obj/item/ammo_box/a357 - name = "speed loader (.357)" - desc = "Designed to quickly reload revolvers." - icon_state = "357" - ammo_type = /obj/item/ammo_casing/a357 - max_ammo = 7 - multiple_sprites = 1 - -/obj/item/ammo_box/c38 - name = "speed loader (.38)" - desc = "Designed to quickly reload revolvers." - icon_state = "38" - ammo_type = /obj/item/ammo_casing/c38 - max_ammo = 6 - multiple_sprites = 1 - -/obj/item/ammo_box/c9mm - name = "ammo box (9mm)" - icon_state = "9mmbox" - origin_tech = "combat=2" - ammo_type = /obj/item/ammo_casing/c9mm - max_ammo = 30 - -/obj/item/ammo_box/c10mm - name = "ammo box (10mm)" - icon_state = "10mmbox" - origin_tech = "combat=2" - ammo_type = /obj/item/ammo_casing/c10mm - max_ammo = 20 - -/obj/item/ammo_box/c45 - name = "ammo box (.45)" - icon_state = "45box" - origin_tech = "combat=2" - ammo_type = /obj/item/ammo_casing/c45 - max_ammo = 20 - -/obj/item/ammo_box/rubber45 - name = "ammo box (.45 rubber)" - icon_state = "45box-r" - ammo_type = /obj/item/ammo_casing/rubber45 - max_ammo = 16 - -/obj/item/ammo_box/a40mm - name = "ammo box (40mm grenades)" - icon_state = "40mm" - ammo_type = /obj/item/ammo_casing/a40mm - max_ammo = 4 - multiple_sprites = 1 - -/obj/item/ammo_box/a762 - name = "stripper clip (7.62mm)" - desc = "A stripper clip." - icon_state = "762" - ammo_type = /obj/item/ammo_casing/a762 - max_ammo = 5 - multiple_sprites = 1 - -/obj/item/ammo_box/n762 - name = "ammo box (7.62x38mmR)" - icon_state = "riflebox" - origin_tech = "combat=2" - ammo_type = /obj/item/ammo_casing/n762 - max_ammo = 14 - -/obj/item/ammo_box/shotgun - name = "Shotgun Speedloader (slug)" - icon_state = "slugloader" - origin_tech = "combat=2" - ammo_type = /obj/item/ammo_casing/shotgun - max_ammo = 7 - materials = list(MAT_METAL=28000) - multiple_sprites = 1 - -/obj/item/ammo_box/shotgun/buck - name = "Shotgun Speedloader (buckshot)" - icon_state = "buckloader" - ammo_type = /obj/item/ammo_casing/shotgun/buckshot - - -/obj/item/ammo_box/shotgun/dragonsbreath - name = "Shotgun Speedloader (dragonsbreath)" - icon_state = "dragonsbreathloader" - ammo_type = /obj/item/ammo_casing/shotgun/incendiary/dragonsbreath - - -/obj/item/ammo_box/shotgun/stun - name = "Shotgun Speedloader (stun shells)" - icon_state = "stunloader" - ammo_type = /obj/item/ammo_casing/shotgun/stunslug - - -/obj/item/ammo_box/shotgun/beanbag - name = "Shotgun Speedloader (beanbag shells)" - icon_state = "beanbagloader" - ammo_type = /obj/item/ammo_casing/shotgun/beanbag - materials = list(MAT_METAL=1750) - - -/obj/item/ammo_box/shotgun/rubbershot - name = "Shotgun Speedloader (rubbershot shells)" - icon_state = "rubbershotloader" - ammo_type = /obj/item/ammo_casing/shotgun/rubbershot - materials = list(MAT_METAL=1750) - - -/obj/item/ammo_box/shotgun/tranquilizer - name = "Shotgun Speedloader (tranquilizer darts)" - icon_state = "tranqloader" - ammo_type = /obj/item/ammo_casing/shotgun/tranquilizer - materials = list(MAT_METAL=1750) - - -//FOAM DARTS -/obj/item/ammo_box/foambox - name = "ammo box (Foam Darts)" - icon = 'icons/obj/guns/toy.dmi' - icon_state = "foambox" - ammo_type = /obj/item/ammo_casing/caseless/foam_dart - max_ammo = 40 - materials = list(MAT_METAL = 500) - -/obj/item/ammo_box/foambox/riot - icon_state = "foambox_riot" - ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot - materials = list(MAT_METAL = 50000) - -/obj/item/ammo_box/foambox/sniper - name = "ammo box (Foam Sniper Darts)" - icon = 'icons/obj/guns/toy.dmi' - icon_state = "foambox_sniper" - ammo_type = /obj/item/ammo_casing/caseless/foam_dart/sniper - max_ammo = 40 - materials = list(MAT_METAL = 900) - -/obj/item/ammo_box/foambox/sniper/riot - icon_state = "foambox_sniper_riot" - ammo_type = /obj/item/ammo_casing/caseless/foam_dart/sniper/riot - materials = list(MAT_METAL = 90000) - - - -/obj/item/ammo_box/caps - name = "speed loader (caps)" - icon_state = "357" - ammo_type = /obj/item/ammo_casing/cap - max_ammo = 7 - multiple_sprites = 1 +/obj/item/ammo_box/a357 + name = "speed loader (.357)" + desc = "Designed to quickly reload revolvers." + icon_state = "357" + ammo_type = /obj/item/ammo_casing/a357 + max_ammo = 7 + multiple_sprites = 1 + +/obj/item/ammo_box/c38 + name = "speed loader (.38)" + desc = "Designed to quickly reload revolvers." + icon_state = "38" + ammo_type = /obj/item/ammo_casing/c38 + max_ammo = 6 + multiple_sprites = 1 + +/obj/item/ammo_box/c9mm + name = "ammo box (9mm)" + icon_state = "9mmbox" + origin_tech = "combat=2" + ammo_type = /obj/item/ammo_casing/c9mm + max_ammo = 30 + +/obj/item/ammo_box/c10mm + name = "ammo box (10mm)" + icon_state = "10mmbox" + origin_tech = "combat=2" + ammo_type = /obj/item/ammo_casing/c10mm + max_ammo = 20 + +/obj/item/ammo_box/c45 + name = "ammo box (.45)" + icon_state = "45box" + origin_tech = "combat=2" + ammo_type = /obj/item/ammo_casing/c45 + max_ammo = 20 + +/obj/item/ammo_box/rubber45 + name = "ammo box (.45 rubber)" + icon_state = "45box-r" + ammo_type = /obj/item/ammo_casing/rubber45 + max_ammo = 16 + +/obj/item/ammo_box/a40mm + name = "ammo box (40mm grenades)" + icon_state = "40mm" + ammo_type = /obj/item/ammo_casing/a40mm + max_ammo = 4 + multiple_sprites = 1 + +/obj/item/ammo_box/a762 + name = "stripper clip (7.62mm)" + desc = "A stripper clip." + icon_state = "762" + ammo_type = /obj/item/ammo_casing/a762 + max_ammo = 5 + multiple_sprites = 1 + +/obj/item/ammo_box/n762 + name = "ammo box (7.62x38mmR)" + icon_state = "riflebox" + origin_tech = "combat=2" + ammo_type = /obj/item/ammo_casing/n762 + max_ammo = 14 + +/obj/item/ammo_box/shotgun + name = "Shotgun Speedloader (slug)" + icon_state = "slugloader" + origin_tech = "combat=2" + ammo_type = /obj/item/ammo_casing/shotgun + max_ammo = 7 + materials = list(MAT_METAL=28000) + multiple_sprites = 1 + +/obj/item/ammo_box/shotgun/buck + name = "Shotgun Speedloader (buckshot)" + icon_state = "buckloader" + ammo_type = /obj/item/ammo_casing/shotgun/buckshot + + +/obj/item/ammo_box/shotgun/dragonsbreath + name = "Shotgun Speedloader (dragonsbreath)" + icon_state = "dragonsbreathloader" + ammo_type = /obj/item/ammo_casing/shotgun/incendiary/dragonsbreath + + +/obj/item/ammo_box/shotgun/stun + name = "Shotgun Speedloader (stun shells)" + icon_state = "stunloader" + ammo_type = /obj/item/ammo_casing/shotgun/stunslug + + +/obj/item/ammo_box/shotgun/beanbag + name = "Shotgun Speedloader (beanbag shells)" + icon_state = "beanbagloader" + ammo_type = /obj/item/ammo_casing/shotgun/beanbag + materials = list(MAT_METAL=1750) + + +/obj/item/ammo_box/shotgun/rubbershot + name = "Shotgun Speedloader (rubbershot shells)" + icon_state = "rubbershotloader" + ammo_type = /obj/item/ammo_casing/shotgun/rubbershot + materials = list(MAT_METAL=1750) + + +/obj/item/ammo_box/shotgun/tranquilizer + name = "Shotgun Speedloader (tranquilizer darts)" + icon_state = "tranqloader" + ammo_type = /obj/item/ammo_casing/shotgun/tranquilizer + materials = list(MAT_METAL=1750) + + +//FOAM DARTS +/obj/item/ammo_box/foambox + name = "ammo box (Foam Darts)" + icon = 'icons/obj/guns/toy.dmi' + icon_state = "foambox" + ammo_type = /obj/item/ammo_casing/caseless/foam_dart + max_ammo = 40 + materials = list(MAT_METAL = 500) + +/obj/item/ammo_box/foambox/riot + icon_state = "foambox_riot" + ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot + materials = list(MAT_METAL = 50000) + +/obj/item/ammo_box/foambox/sniper + name = "ammo box (Foam Sniper Darts)" + icon = 'icons/obj/guns/toy.dmi' + icon_state = "foambox_sniper" + ammo_type = /obj/item/ammo_casing/caseless/foam_dart/sniper + max_ammo = 40 + materials = list(MAT_METAL = 900) + +/obj/item/ammo_box/foambox/sniper/riot + icon_state = "foambox_sniper_riot" + ammo_type = /obj/item/ammo_casing/caseless/foam_dart/sniper/riot + materials = list(MAT_METAL = 90000) + + + +/obj/item/ammo_box/caps + name = "speed loader (caps)" + icon_state = "357" + ammo_type = /obj/item/ammo_casing/cap + max_ammo = 7 + multiple_sprites = 1 diff --git a/code/modules/projectiles/ammunition/magazines.dm b/code/modules/projectiles/ammunition/magazines.dm index 2f422e227b3..5890110b635 100644 --- a/code/modules/projectiles/ammunition/magazines.dm +++ b/code/modules/projectiles/ammunition/magazines.dm @@ -157,6 +157,8 @@ multiload = 0 /obj/item/ammo_box/magazine/internal/rus357/New() + ..() + stored_ammo.Cut() // We only want 1 bullet in there stored_ammo += new ammo_type(src) /obj/item/ammo_box/magazine/internal/boltaction @@ -504,4 +506,4 @@ icon_state = "c20r45-[round(ammo_count(),2)]" /obj/item/ammo_box/magazine/toy/smgm45/riot - ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot \ No newline at end of file + ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 4df4e46da20..411a460ac62 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -1,521 +1,524 @@ -/obj/item/gun - name = "gun" - desc = "It's a gun. It's pretty terrible, though." - icon = 'icons/obj/guns/projectile.dmi' - icon_state = "detective" - item_state = "gun" - flags = CONDUCT - slot_flags = SLOT_BELT - materials = list(MAT_METAL=2000) - w_class = WEIGHT_CLASS_NORMAL - throwforce = 5 - throw_speed = 3 - throw_range = 5 - force = 5 - origin_tech = "combat=1" - needs_permit = 1 - attack_verb = list("struck", "hit", "bashed") - - var/fire_sound = "gunshot" - var/magin_sound = 'sound/weapons/gun_interactions/smg_magin.ogg' - var/magout_sound = 'sound/weapons/gun_interactions/smg_magout.ogg' - var/fire_sound_text = "gunshot" //the fire sound that shows in chat messages: laser blast, gunshot, etc. - var/suppressed = 0 //whether or not a message is displayed when fired - var/can_suppress = 0 - var/can_unsuppress = 1 - var/recoil = 0 //boom boom shake the room - var/clumsy_check = 1 - var/obj/item/ammo_casing/chambered = null - var/trigger_guard = TRIGGER_GUARD_NORMAL //trigger guard on the weapon, hulks can't fire them with their big meaty fingers - var/sawn_desc = null //description change if weapon is sawn-off - var/sawn_state = SAWN_INTACT - var/burst_size = 1 //how large a burst is - var/fire_delay = 0 //rate of fire for burst firing and semi auto - var/firing_burst = 0 //Prevent the weapon from firing again while already firing - var/semicd = 0 //cooldown handler - var/weapon_weight = WEAPON_LIGHT - var/list/restricted_species - - var/spread = 0 - var/randomspread = 1 - - var/unique_rename = 0 //allows renaming with a pen - var/unique_reskin = 0 //allows one-time reskinning - var/current_skin = null //the skin choice if we had a reskin - var/list/options = list() - - lefthand_file = 'icons/mob/inhands/guns_lefthand.dmi' - righthand_file = 'icons/mob/inhands/guns_righthand.dmi' - - var/obj/item/flashlight/gun_light = null - var/can_flashlight = 0 - - var/can_bayonet = FALSE //if a bayonet can be added or removed if it already has one. - var/obj/item/kitchen/knife/bayonet - var/mutable_appearance/knife_overlay - var/knife_x_offset = 0 - var/knife_y_offset = 0 - - var/list/upgrades = list() - - var/ammo_x_offset = 0 //used for positioning ammo count overlay on sprite - var/ammo_y_offset = 0 - var/flight_x_offset = 0 - var/flight_y_offset = 0 - - //Zooming - var/zoomable = FALSE //whether the gun generates a Zoom action on creation - var/zoomed = FALSE //Zoom toggle - var/zoom_amt = 3 //Distance in TURFs to move the user's screen forward (the "zoom" effect) - var/datum/action/toggle_scope_zoom/azoom - -/obj/item/gun/New() - ..() - if(gun_light) - verbs += /obj/item/gun/proc/toggle_gunlight - build_zooming() - -/obj/item/gun/Destroy() - QDEL_NULL(bayonet) - return ..() - -/obj/item/gun/handle_atom_del(atom/A) - if(A == bayonet) - clear_bayonet() - return ..() - -/obj/item/gun/examine(mob/user) - . = ..() - if(unique_reskin && !current_skin) - . += "Alt-click it to reskin it." - if(unique_rename) - . += "Use a pen on it to rename it." - if(bayonet) - . += "It has \a [bayonet] [can_bayonet ? "" : "permanently "]affixed to it." - if(can_bayonet) //if it has a bayonet and this is false, the bayonet is permanent. - . += "[bayonet] looks like it can be unscrewed from [src]." - else if(can_bayonet) - . += "It has a bayonet lug on it." - -/obj/item/gun/proc/process_chamber() - return 0 - -//check if there's enough ammo/energy/whatever to shoot one time -//i.e if clicking would make it shoot -/obj/item/gun/proc/can_shoot() - return 1 - -/obj/item/gun/proc/shoot_with_empty_chamber(mob/living/user as mob|obj) - to_chat(user, "*click*") - playsound(user, 'sound/weapons/empty.ogg', 100, 1) - -/obj/item/gun/proc/shoot_live_shot(mob/living/user as mob|obj, pointblank = 0, mob/pbtarget = null, message = 1) - if(recoil) - shake_camera(user, recoil + 1, recoil) - - if(suppressed) - playsound(user, fire_sound, 10, 1) - else - playsound(user, fire_sound, 50, 1) - if(!message) - return - if(pointblank) - user.visible_message("[user] fires [src] point blank at [pbtarget]!", "You fire [src] point blank at [pbtarget]!", "You hear \a [fire_sound_text]!") - else - user.visible_message("[user] fires [src]!", "You fire [src]!", "You hear \a [fire_sound_text]!") - -/obj/item/gun/emp_act(severity) - for(var/obj/O in contents) - O.emp_act(severity) - -/obj/item/gun/afterattack(atom/target, mob/living/user, flag, params) - if(firing_burst) - return - if(flag) //It's adjacent, is the user, or is on the user's person - if(target in user.contents) //can't shoot stuff inside us. - return - if(!ismob(target) || user.a_intent == INTENT_HARM) //melee attack - return - if(target == user && user.zone_selected != "mouth") //so we can't shoot ourselves (unless mouth selected) - return - - if(istype(user))//Check if the user can use the gun, if the user isn't alive(turrets) assume it can. - var/mob/living/L = user - if(!can_trigger_gun(L)) - return - - if(!can_shoot()) //Just because you can pull the trigger doesn't mean it can't shoot. - shoot_with_empty_chamber(user) - return - - if(flag) - if(user.zone_selected == "mouth") - handle_suicide(user, target, params) - return - - - //Exclude lasertag guns from the CLUMSY check. - if(clumsy_check) - if(istype(user)) - if((CLUMSY in user.mutations) && prob(40)) - to_chat(user, "You shoot yourself in the foot with \the [src]!") - var/shot_leg = pick("l_foot", "r_foot") - process_fire(user, user, 0, params, zone_override = shot_leg) - user.drop_item() - return - - if(weapon_weight == WEAPON_HEAVY && user.get_inactive_hand()) - to_chat(user, "You need both hands free to fire \the [src]!") - return - - //DUAL WIELDING - var/bonus_spread = 0 - var/loop_counter = 0 - if(ishuman(user) && user.a_intent == INTENT_HARM) - var/mob/living/carbon/human/H = user - for(var/obj/item/gun/G in get_both_hands(H)) - if(G == src || G.weapon_weight >= WEAPON_MEDIUM) - continue - else if(G.can_trigger_gun(user)) - bonus_spread += 24 * G.weapon_weight - loop_counter++ - addtimer(CALLBACK(G, .proc/process_fire, target, user, 1, params, null, bonus_spread), loop_counter) - - process_fire(target,user,1,params, null, bonus_spread) - -/obj/item/gun/proc/can_trigger_gun(mob/living/user) - if(!user.can_use_guns(src)) - return 0 - if(restricted_species && restricted_species.len && !is_type_in_list(user.dna.species, restricted_species)) - to_chat(user, "[src] is incompatible with your biology!") - return 0 - return 1 - -/obj/item/gun/proc/newshot() - return - -/obj/item/gun/proc/process_fire(atom/target as mob|obj|turf, mob/living/user as mob|obj, message = 1, params, zone_override, bonus_spread = 0) - add_fingerprint(user) - - if(semicd) - return - - var/sprd = 0 - var/randomized_gun_spread = 0 - if(spread) - randomized_gun_spread = rand(0,spread) - var/randomized_bonus_spread = rand(0, bonus_spread) - - if(burst_size > 1) - if(chambered && chambered.harmful) - if(HAS_TRAIT(user, TRAIT_PACIFISM)) // If the user has the pacifist trait, then they won't be able to fire [src] if the round chambered inside of [src] is lethal. - to_chat(user, "[src] is lethally chambered! You don't want to risk harming anyone...") - return - firing_burst = 1 - for(var/i = 1 to burst_size) - if(!user) - break - if(!issilicon(user)) - if( i>1 && !(src in get_both_hands(user))) //for burst firing - break - if(chambered) - if(randomspread) - sprd = round((rand() - 0.5) * (randomized_gun_spread + randomized_bonus_spread)) - else - sprd = round((i / burst_size - 0.5) * (randomized_gun_spread + randomized_bonus_spread)) - if(!chambered.fire(target, user, params, ,suppressed, zone_override, sprd)) - shoot_with_empty_chamber(user) - break - else - if(get_dist(user, target) <= 1) //Making sure whether the target is in vicinity for the pointblank shot - shoot_live_shot(user, 1, target, message) - else - shoot_live_shot(user, 0, target, message) - else - shoot_with_empty_chamber(user) - break - process_chamber() - update_icon() - sleep(fire_delay) - firing_burst = 0 - else - if(chambered) - if(HAS_TRAIT(user, TRAIT_PACIFISM)) // If the user has the pacifist trait, then they won't be able to fire [src] if the round chambered inside of [src] is lethal. - if(chambered.harmful) // Is the bullet chambered harmful? - to_chat(user, "[src] is lethally chambered! You don't want to risk harming anyone...") - return - sprd = round((pick(1,-1)) * (randomized_gun_spread + randomized_bonus_spread)) - if(!chambered.fire(target, user, params, , suppressed, zone_override, sprd)) - shoot_with_empty_chamber(user) - return - else - if(get_dist(user, target) <= 1) //Making sure whether the target is in vicinity for the pointblank shot - shoot_live_shot(user, 1, target, message) - else - shoot_live_shot(user, 0, target, message) - else - shoot_with_empty_chamber(user) - return - process_chamber() - update_icon() - semicd = 1 - spawn(fire_delay) - semicd = 0 - - if(user) - if(user.hand) - user.update_inv_l_hand() - else - user.update_inv_r_hand() - feedback_add_details("gun_fired","[type]") - -/obj/item/gun/attack(mob/M, mob/user) - if(user.a_intent == INTENT_HARM) //Flogging - if(bayonet) - M.attackby(bayonet, user) - else - return ..() - -/obj/item/gun/attack_obj(obj/O, mob/user) - if(user.a_intent == INTENT_HARM) - if(bayonet) - O.attackby(bayonet, user) - return - return ..() - -/obj/item/gun/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/flashlight/seclite)) - var/obj/item/flashlight/seclite/S = I - if(can_flashlight) - if(!gun_light) - if(!user.unEquip(I)) - return - to_chat(user, "You click [S] into place on [src].") - if(S.on) - set_light(0) - gun_light = S - I.loc = src - update_icon() - update_gun_light(user) - var/datum/action/A = new /datum/action/item_action/toggle_gunlight(src) - if(loc == user) - A.Grant(user) - - if(isscrewdriver(I)) - if(gun_light && can_flashlight) - for(var/obj/item/flashlight/seclite/S in src) - to_chat(user, "You unscrew the seclite from [src].") - gun_light = null - S.loc = get_turf(user) - update_gun_light(user) - S.update_brightness(user) - update_icon() - for(var/datum/action/item_action/toggle_gunlight/TGL in actions) - qdel(TGL) - else if(bayonet && can_bayonet) //if it has a bayonet, and the bayonet can be removed - bayonet.forceMove(get_turf(user)) - clear_bayonet() - - if(unique_rename) - if(istype(I, /obj/item/pen)) - rename_gun(user) - if(istype(I, /obj/item/kitchen/knife)) - var/obj/item/kitchen/knife/K = I - if(!can_bayonet || !K.bayonet || bayonet) //ensure the gun has an attachment point available, and that the knife is compatible with it. - return ..() - if(!user.drop_item()) - return - K.forceMove(src) - to_chat(user, "You attach [K] to [src]'s bayonet lug.") - bayonet = K - var/state = "bayonet" //Generic state. - if(bayonet.icon_state in icon_states('icons/obj/guns/bayonets.dmi')) //Snowflake state? - state = bayonet.icon_state - var/icon/bayonet_icons = 'icons/obj/guns/bayonets.dmi' - knife_overlay = mutable_appearance(bayonet_icons, state) - knife_overlay.pixel_x = knife_x_offset - knife_overlay.pixel_y = knife_y_offset - overlays += knife_overlay - else - return ..() - -/obj/item/gun/proc/toggle_gunlight() - set name = "Toggle Gun Light" - set category = "Object" - set desc = "Click to toggle your weapon's attached flashlight." - - if(!gun_light) - return - - var/mob/living/carbon/human/user = usr - if(!isturf(user.loc)) - to_chat(user, "You cannot turn the light on while in this [user.loc]!") - gun_light.on = !gun_light.on - to_chat(user, "You toggle the gun light [gun_light.on ? "on":"off"].") - - playsound(user, 'sound/weapons/empty.ogg', 100, 1) - update_gun_light(user) - -/obj/item/gun/proc/update_gun_light(mob/user = null) - if(gun_light) - if(gun_light.on) - set_light(gun_light.brightness_on) - else - set_light(0) - update_icon() - else - set_light(0) - - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - -/obj/item/gun/proc/clear_bayonet() - if(!bayonet) - return - bayonet = null - if(knife_overlay) - overlays -= knife_overlay - knife_overlay = null - return TRUE - -/obj/item/gun/extinguish_light() - if(gun_light.on) - toggle_gunlight() - visible_message("[src]'s light fades and turns off.") - -/obj/item/gun/pickup(mob/user) - . = ..() - if(azoom) - azoom.Grant(user) - -/obj/item/gun/dropped(mob/user) - ..() - zoom(user,FALSE) - if(azoom) - azoom.Remove(user) - -/obj/item/gun/AltClick(mob/user) - ..() - if(user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(unique_reskin && !current_skin && loc == user) - reskin_gun(user) - -/obj/item/gun/proc/reskin_gun(mob/M) - var/choice = input(M,"Warning, you can only reskin your weapon once!","Reskin Gun") in options - - if(src && choice && !current_skin && !M.incapacitated() && in_range(M,src)) - if(options[choice] == null) - return - current_skin = options[choice] - to_chat(M, "Your gun is now skinned as [choice]. Say hello to your new friend.") - update_icon() - -/obj/item/gun/proc/rename_gun(mob/M) - var/input = stripped_input(M,"What do you want to name the gun?", ,"", MAX_NAME_LEN) - if(src && input && !M.stat && in_range(M,src) && !M.restrained() && M.canmove) - name = input - to_chat(M, "You name the gun [input]. Say hello to your new friend.") - return - -/obj/item/gun/proc/handle_suicide(mob/living/carbon/human/user, mob/living/carbon/human/target, params) - if(!ishuman(user) || !ishuman(target)) - return - - if(semicd) - return - - if(user == target) - target.visible_message("[user] sticks [src] in [user.p_their()] mouth, ready to pull the trigger...", \ - "You stick [src] in your mouth, ready to pull the trigger...") - else - target.visible_message("[user] points [src] at [target]'s head, ready to pull the trigger...", \ - "[user] points [src] at your head, ready to pull the trigger...") - - semicd = 1 - - if(!do_mob(user, target, 120) || user.zone_selected != "mouth") - if(user) - if(user == target) - user.visible_message("[user] decided life was worth living.") - else if(target && target.Adjacent(user)) - target.visible_message("[user] has decided to spare [target]'s life.", "[user] has decided to spare your life!") - semicd = 0 - return - - semicd = 0 - - target.visible_message("[user] pulls the trigger!", "[user] pulls the trigger!") - - if(chambered && chambered.BB) - chambered.BB.damage *= 5 - - process_fire(target, user, 1, params) - -/obj/item/gun/proc/isHandgun() - return 1 - -///////////// -// ZOOMING // -///////////// - -/datum/action/toggle_scope_zoom - name = "Toggle Scope" - check_flags = AB_CHECK_CONSCIOUS|AB_CHECK_RESTRAINED|AB_CHECK_STUNNED|AB_CHECK_LYING - button_icon_state = "sniper_zoom" - var/obj/item/gun/gun = null - -/datum/action/toggle_scope_zoom/Trigger() - gun.zoom(owner) - -/datum/action/toggle_scope_zoom/IsAvailable() - . = ..() - if(!. && gun) - gun.zoom(owner, FALSE) - -/datum/action/toggle_scope_zoom/Remove(mob/living/L) - gun.zoom(L, FALSE) - ..() - -/obj/item/gun/proc/zoom(mob/living/user, forced_zoom) - if(!user || !user.client) - return - - switch(forced_zoom) - if(FALSE) - zoomed = FALSE - if(TRUE) - zoomed = TRUE - else - zoomed = !zoomed - - if(zoomed) - var/_x = 0 - var/_y = 0 - switch(user.dir) - if(NORTH) - _y = zoom_amt - if(EAST) - _x = zoom_amt - if(SOUTH) - _y = -zoom_amt - if(WEST) - _x = -zoom_amt - - user.client.pixel_x = world.icon_size*_x - user.client.pixel_y = world.icon_size*_y - else - user.client.pixel_x = 0 - user.client.pixel_y = 0 - - -//Proc, so that gun accessories/scopes/etc. can easily add zooming. -/obj/item/gun/proc/build_zooming() - if(azoom) - return - - if(zoomable) - azoom = new() - azoom.gun = src +/obj/item/gun + name = "gun" + desc = "It's a gun. It's pretty terrible, though." + icon = 'icons/obj/guns/projectile.dmi' + icon_state = "detective" + item_state = "gun" + flags = CONDUCT + slot_flags = SLOT_BELT + materials = list(MAT_METAL=2000) + w_class = WEIGHT_CLASS_NORMAL + throwforce = 5 + throw_speed = 3 + throw_range = 5 + force = 5 + origin_tech = "combat=1" + needs_permit = 1 + attack_verb = list("struck", "hit", "bashed") + + var/fire_sound = "gunshot" + var/magin_sound = 'sound/weapons/gun_interactions/smg_magin.ogg' + var/magout_sound = 'sound/weapons/gun_interactions/smg_magout.ogg' + var/fire_sound_text = "gunshot" //the fire sound that shows in chat messages: laser blast, gunshot, etc. + var/suppressed = 0 //whether or not a message is displayed when fired + var/can_suppress = 0 + var/can_unsuppress = 1 + var/recoil = 0 //boom boom shake the room + var/clumsy_check = 1 + var/obj/item/ammo_casing/chambered = null + var/trigger_guard = TRIGGER_GUARD_NORMAL //trigger guard on the weapon, hulks can't fire them with their big meaty fingers + var/sawn_desc = null //description change if weapon is sawn-off + var/sawn_state = SAWN_INTACT + var/burst_size = 1 //how large a burst is + var/fire_delay = 0 //rate of fire for burst firing and semi auto + var/firing_burst = 0 //Prevent the weapon from firing again while already firing + var/semicd = 0 //cooldown handler + var/weapon_weight = WEAPON_LIGHT + var/list/restricted_species + + var/spread = 0 + var/randomspread = 1 + + var/unique_rename = 0 //allows renaming with a pen + var/unique_reskin = 0 //allows one-time reskinning + var/current_skin = null //the skin choice if we had a reskin + var/list/options = list() + + lefthand_file = 'icons/mob/inhands/guns_lefthand.dmi' + righthand_file = 'icons/mob/inhands/guns_righthand.dmi' + + var/obj/item/flashlight/gun_light = null + var/can_flashlight = 0 + + var/can_bayonet = FALSE //if a bayonet can be added or removed if it already has one. + var/obj/item/kitchen/knife/bayonet + var/mutable_appearance/knife_overlay + var/knife_x_offset = 0 + var/knife_y_offset = 0 + + var/list/upgrades = list() + + var/ammo_x_offset = 0 //used for positioning ammo count overlay on sprite + var/ammo_y_offset = 0 + var/flight_x_offset = 0 + var/flight_y_offset = 0 + + //Zooming + var/zoomable = FALSE //whether the gun generates a Zoom action on creation + var/zoomed = FALSE //Zoom toggle + var/zoom_amt = 3 //Distance in TURFs to move the user's screen forward (the "zoom" effect) + var/datum/action/toggle_scope_zoom/azoom + +/obj/item/gun/New() + ..() + if(gun_light) + verbs += /obj/item/gun/proc/toggle_gunlight + build_zooming() + +/obj/item/gun/Destroy() + QDEL_NULL(bayonet) + return ..() + +/obj/item/gun/handle_atom_del(atom/A) + if(A == bayonet) + clear_bayonet() + return ..() + +/obj/item/gun/examine(mob/user) + . = ..() + if(unique_reskin && !current_skin) + . += "Alt-click it to reskin it." + if(unique_rename) + . += "Use a pen on it to rename it." + if(bayonet) + . += "It has \a [bayonet] [can_bayonet ? "" : "permanently "]affixed to it." + if(can_bayonet) //if it has a bayonet and this is false, the bayonet is permanent. + . += "[bayonet] looks like it can be unscrewed from [src]." + else if(can_bayonet) + . += "It has a bayonet lug on it." + +/obj/item/gun/proc/process_chamber() + return 0 + +//check if there's enough ammo/energy/whatever to shoot one time +//i.e if clicking would make it shoot +/obj/item/gun/proc/can_shoot() + return 1 + +/obj/item/gun/proc/shoot_with_empty_chamber(mob/living/user as mob|obj) + to_chat(user, "*click*") + playsound(user, 'sound/weapons/empty.ogg', 100, 1) + +/obj/item/gun/proc/shoot_live_shot(mob/living/user as mob|obj, pointblank = 0, mob/pbtarget = null, message = 1) + if(recoil) + shake_camera(user, recoil + 1, recoil) + + if(suppressed) + playsound(user, fire_sound, 10, 1) + else + playsound(user, fire_sound, 50, 1) + if(!message) + return + if(pointblank) + user.visible_message("[user] fires [src] point blank at [pbtarget]!", "You fire [src] point blank at [pbtarget]!", "You hear \a [fire_sound_text]!") + else + user.visible_message("[user] fires [src]!", "You fire [src]!", "You hear \a [fire_sound_text]!") + +/obj/item/gun/emp_act(severity) + for(var/obj/O in contents) + O.emp_act(severity) + +/obj/item/gun/afterattack(atom/target, mob/living/user, flag, params) + if(firing_burst) + return + if(flag) //It's adjacent, is the user, or is on the user's person + if(target in user.contents) //can't shoot stuff inside us. + return + if(!ismob(target) || user.a_intent == INTENT_HARM) //melee attack + return + if(target == user && user.zone_selected != "mouth") //so we can't shoot ourselves (unless mouth selected) + return + + if(istype(user))//Check if the user can use the gun, if the user isn't alive(turrets) assume it can. + var/mob/living/L = user + if(!can_trigger_gun(L)) + return + + if(!can_shoot()) //Just because you can pull the trigger doesn't mean it can't shoot. + shoot_with_empty_chamber(user) + return + + if(flag) + if(user.zone_selected == "mouth") + handle_suicide(user, target, params) + return + + + //Exclude lasertag guns from the CLUMSY check. + if(clumsy_check) + if(istype(user)) + if((CLUMSY in user.mutations) && prob(40)) + to_chat(user, "You shoot yourself in the foot with \the [src]!") + var/shot_leg = pick("l_foot", "r_foot") + process_fire(user, user, 0, params, zone_override = shot_leg) + user.drop_item() + return + + if(weapon_weight == WEAPON_HEAVY && user.get_inactive_hand()) + to_chat(user, "You need both hands free to fire \the [src]!") + return + + //DUAL WIELDING + var/bonus_spread = 0 + var/loop_counter = 0 + if(ishuman(user) && user.a_intent == INTENT_HARM) + var/mob/living/carbon/human/H = user + for(var/obj/item/gun/G in get_both_hands(H)) + if(G == src || G.weapon_weight >= WEAPON_MEDIUM) + continue + else if(G.can_trigger_gun(user)) + bonus_spread += 24 * G.weapon_weight + loop_counter++ + addtimer(CALLBACK(G, .proc/process_fire, target, user, 1, params, null, bonus_spread), loop_counter) + + process_fire(target,user,1,params, null, bonus_spread) + +/obj/item/gun/proc/can_trigger_gun(mob/living/user) + if(!user.can_use_guns(src)) + return 0 + if(restricted_species && restricted_species.len && !is_type_in_list(user.dna.species, restricted_species)) + to_chat(user, "[src] is incompatible with your biology!") + return 0 + return 1 + +/obj/item/gun/proc/newshot() + return + +/obj/item/gun/proc/process_fire(atom/target as mob|obj|turf, mob/living/user as mob|obj, message = 1, params, zone_override, bonus_spread = 0) + add_fingerprint(user) + + if(semicd) + return + + var/sprd = 0 + var/randomized_gun_spread = 0 + if(spread) + randomized_gun_spread = rand(0,spread) + var/randomized_bonus_spread = rand(0, bonus_spread) + + if(burst_size > 1) + if(chambered && chambered.harmful) + if(HAS_TRAIT(user, TRAIT_PACIFISM)) // If the user has the pacifist trait, then they won't be able to fire [src] if the round chambered inside of [src] is lethal. + to_chat(user, "[src] is lethally chambered! You don't want to risk harming anyone...") + return + firing_burst = 1 + for(var/i = 1 to burst_size) + if(!user) + break + if(!issilicon(user)) + if( i>1 && !(src in get_both_hands(user))) //for burst firing + break + if(chambered) + if(randomspread) + sprd = round((rand() - 0.5) * (randomized_gun_spread + randomized_bonus_spread)) + else + sprd = round((i / burst_size - 0.5) * (randomized_gun_spread + randomized_bonus_spread)) + if(!chambered.fire(target, user, params, ,suppressed, zone_override, sprd)) + shoot_with_empty_chamber(user) + break + else + if(get_dist(user, target) <= 1) //Making sure whether the target is in vicinity for the pointblank shot + shoot_live_shot(user, 1, target, message) + else + shoot_live_shot(user, 0, target, message) + else + shoot_with_empty_chamber(user) + break + process_chamber() + update_icon() + sleep(fire_delay) + firing_burst = 0 + else + if(chambered) + if(HAS_TRAIT(user, TRAIT_PACIFISM)) // If the user has the pacifist trait, then they won't be able to fire [src] if the round chambered inside of [src] is lethal. + if(chambered.harmful) // Is the bullet chambered harmful? + to_chat(user, "[src] is lethally chambered! You don't want to risk harming anyone...") + return + sprd = round((pick(1,-1)) * (randomized_gun_spread + randomized_bonus_spread)) + if(!chambered.fire(target, user, params, , suppressed, zone_override, sprd)) + shoot_with_empty_chamber(user) + return + else + if(get_dist(user, target) <= 1) //Making sure whether the target is in vicinity for the pointblank shot + shoot_live_shot(user, 1, target, message) + else + shoot_live_shot(user, 0, target, message) + else + shoot_with_empty_chamber(user) + return + process_chamber() + update_icon() + semicd = 1 + spawn(fire_delay) + semicd = 0 + + if(user) + if(user.hand) + user.update_inv_l_hand() + else + user.update_inv_r_hand() + feedback_add_details("gun_fired","[type]") + +/obj/item/gun/attack(mob/M, mob/user) + if(user.a_intent == INTENT_HARM) //Flogging + if(bayonet) + M.attackby(bayonet, user) + else + return ..() + +/obj/item/gun/attack_obj(obj/O, mob/user) + if(user.a_intent == INTENT_HARM) + if(bayonet) + O.attackby(bayonet, user) + return + return ..() + +/obj/item/gun/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/flashlight/seclite)) + var/obj/item/flashlight/seclite/S = I + if(can_flashlight) + if(!gun_light) + if(!user.unEquip(I)) + return + to_chat(user, "You click [S] into place on [src].") + if(S.on) + set_light(0) + gun_light = S + I.loc = src + update_icon() + update_gun_light(user) + var/datum/action/A = new /datum/action/item_action/toggle_gunlight(src) + if(loc == user) + A.Grant(user) + + if(unique_rename) + if(istype(I, /obj/item/pen)) + rename_gun(user) + if(istype(I, /obj/item/kitchen/knife)) + var/obj/item/kitchen/knife/K = I + if(!can_bayonet || !K.bayonet || bayonet) //ensure the gun has an attachment point available, and that the knife is compatible with it. + return ..() + if(!user.drop_item()) + return + K.forceMove(src) + to_chat(user, "You attach [K] to [src]'s bayonet lug.") + bayonet = K + var/state = "bayonet" //Generic state. + if(bayonet.icon_state in icon_states('icons/obj/guns/bayonets.dmi')) //Snowflake state? + state = bayonet.icon_state + var/icon/bayonet_icons = 'icons/obj/guns/bayonets.dmi' + knife_overlay = mutable_appearance(bayonet_icons, state) + knife_overlay.pixel_x = knife_x_offset + knife_overlay.pixel_y = knife_y_offset + overlays += knife_overlay + else + return ..() + +/obj/item/gun/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(gun_light && can_flashlight) + for(var/obj/item/flashlight/seclite/S in src) + to_chat(user, "You unscrew the seclite from [src].") + gun_light = null + S.loc = get_turf(user) + update_gun_light(user) + S.update_brightness(user) + update_icon() + for(var/datum/action/item_action/toggle_gunlight/TGL in actions) + qdel(TGL) + else if(bayonet && can_bayonet) //if it has a bayonet, and the bayonet can be removed + bayonet.forceMove(get_turf(user)) + clear_bayonet() + +/obj/item/gun/proc/toggle_gunlight() + set name = "Toggle Gun Light" + set category = "Object" + set desc = "Click to toggle your weapon's attached flashlight." + + if(!gun_light) + return + + var/mob/living/carbon/human/user = usr + if(!isturf(user.loc)) + to_chat(user, "You cannot turn the light on while in this [user.loc]!") + gun_light.on = !gun_light.on + to_chat(user, "You toggle the gun light [gun_light.on ? "on":"off"].") + + playsound(user, 'sound/weapons/empty.ogg', 100, 1) + update_gun_light(user) + +/obj/item/gun/proc/update_gun_light(mob/user = null) + if(gun_light) + if(gun_light.on) + set_light(gun_light.brightness_on) + else + set_light(0) + update_icon() + else + set_light(0) + + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + +/obj/item/gun/proc/clear_bayonet() + if(!bayonet) + return + bayonet = null + if(knife_overlay) + overlays -= knife_overlay + knife_overlay = null + return TRUE + +/obj/item/gun/extinguish_light() + if(gun_light.on) + toggle_gunlight() + visible_message("[src]'s light fades and turns off.") + +/obj/item/gun/pickup(mob/user) + . = ..() + if(azoom) + azoom.Grant(user) + +/obj/item/gun/dropped(mob/user) + ..() + zoom(user,FALSE) + if(azoom) + azoom.Remove(user) + +/obj/item/gun/AltClick(mob/user) + ..() + if(user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(unique_reskin && !current_skin && loc == user) + reskin_gun(user) + +/obj/item/gun/proc/reskin_gun(mob/M) + var/choice = input(M,"Warning, you can only reskin your weapon once!","Reskin Gun") in options + + if(src && choice && !current_skin && !M.incapacitated() && in_range(M,src)) + if(options[choice] == null) + return + current_skin = options[choice] + to_chat(M, "Your gun is now skinned as [choice]. Say hello to your new friend.") + update_icon() + +/obj/item/gun/proc/rename_gun(mob/M) + var/input = stripped_input(M,"What do you want to name the gun?", ,"", MAX_NAME_LEN) + if(src && input && !M.stat && in_range(M,src) && !M.restrained() && M.canmove) + name = input + to_chat(M, "You name the gun [input]. Say hello to your new friend.") + return + +/obj/item/gun/proc/handle_suicide(mob/living/carbon/human/user, mob/living/carbon/human/target, params) + if(!ishuman(user) || !ishuman(target)) + return + + if(semicd) + return + + if(user == target) + target.visible_message("[user] sticks [src] in [user.p_their()] mouth, ready to pull the trigger...", \ + "You stick [src] in your mouth, ready to pull the trigger...") + else + target.visible_message("[user] points [src] at [target]'s head, ready to pull the trigger...", \ + "[user] points [src] at your head, ready to pull the trigger...") + + semicd = 1 + + if(!do_mob(user, target, 120) || user.zone_selected != "mouth") + if(user) + if(user == target) + user.visible_message("[user] decided life was worth living.") + else if(target && target.Adjacent(user)) + target.visible_message("[user] has decided to spare [target]'s life.", "[user] has decided to spare your life!") + semicd = 0 + return + + semicd = 0 + + target.visible_message("[user] pulls the trigger!", "[user] pulls the trigger!") + + if(chambered && chambered.BB) + chambered.BB.damage *= 5 + + process_fire(target, user, 1, params) + +/obj/item/gun/proc/isHandgun() + return 1 + +///////////// +// ZOOMING // +///////////// + +/datum/action/toggle_scope_zoom + name = "Toggle Scope" + check_flags = AB_CHECK_CONSCIOUS|AB_CHECK_RESTRAINED|AB_CHECK_STUNNED|AB_CHECK_LYING + button_icon_state = "sniper_zoom" + var/obj/item/gun/gun = null + +/datum/action/toggle_scope_zoom/Trigger() + gun.zoom(owner) + +/datum/action/toggle_scope_zoom/IsAvailable() + . = ..() + if(!. && gun) + gun.zoom(owner, FALSE) + +/datum/action/toggle_scope_zoom/Remove(mob/living/L) + gun.zoom(L, FALSE) + ..() + +/obj/item/gun/proc/zoom(mob/living/user, forced_zoom) + if(!user || !user.client) + return + + switch(forced_zoom) + if(FALSE) + zoomed = FALSE + if(TRUE) + zoomed = TRUE + else + zoomed = !zoomed + + if(zoomed) + var/_x = 0 + var/_y = 0 + switch(user.dir) + if(NORTH) + _y = zoom_amt + if(EAST) + _x = zoom_amt + if(SOUTH) + _y = -zoom_amt + if(WEST) + _x = -zoom_amt + + user.client.pixel_x = world.icon_size*_x + user.client.pixel_y = world.icon_size*_y + else + user.client.pixel_x = 0 + user.client.pixel_y = 0 + + +//Proc, so that gun accessories/scopes/etc. can easily add zooming. +/obj/item/gun/proc/build_zooming() + if(azoom) + return + + if(zoomable) + azoom = new() + azoom.gun = src diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm index c745d6be846..d17da9a6d99 100644 --- a/code/modules/projectiles/guns/energy.dm +++ b/code/modules/projectiles/guns/energy.dm @@ -1,212 +1,220 @@ -/obj/item/gun/energy - icon_state = "energy" - name = "energy gun" - desc = "A basic energy-based gun." - icon = 'icons/obj/guns/energy.dmi' - fire_sound_text = "laser blast" - - var/obj/item/stock_parts/cell/cell //What type of power cell this uses - var/cell_type = /obj/item/stock_parts/cell - var/modifystate = 0 - var/list/ammo_type = list(/obj/item/ammo_casing/energy) - var/select = 1 //The state of the select fire switch. Determines from the ammo_type list what kind of shot is fired next. - var/can_charge = 1 - var/charge_sections = 4 - ammo_x_offset = 2 - var/shaded_charge = 0 //if this gun uses a stateful charge bar for more detail - var/selfcharge = 0 - var/use_external_power = 0 //if set, the weapon will look for an external power source to draw from, otherwise it recharges magically - var/charge_tick = 0 - var/charge_delay = 4 - -/obj/item/gun/energy/emp_act(severity) - cell.use(round(cell.charge / severity)) - if(chambered)//phil235 - if(chambered.BB) - qdel(chambered.BB) - chambered.BB = null - chambered = null - newshot() //phil235 - update_icon() - -/obj/item/gun/energy/get_cell() - return cell - -/obj/item/gun/energy/New() - ..() - if(cell_type) - cell = new cell_type(src) - else - cell = new(src) - cell.give(cell.maxcharge) - update_ammo_types() - on_recharge() - if(selfcharge) - START_PROCESSING(SSobj, src) - update_icon() - -/obj/item/gun/energy/proc/update_ammo_types() - var/obj/item/ammo_casing/energy/shot - for(var/i = 1, i <= ammo_type.len, i++) - var/shottype = ammo_type[i] - shot = new shottype(src) - ammo_type[i] = shot - shot = ammo_type[select] - fire_sound = shot.fire_sound - fire_delay = shot.delay - -/obj/item/gun/energy/Destroy() - if(selfcharge) - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/gun/energy/process() - if(selfcharge) //Every [recharge_time] ticks, recharge a shot for the cyborg - charge_tick++ - if(charge_tick < charge_delay) - return - charge_tick = 0 - if(!cell) - return // check if we actually need to recharge - var/obj/item/ammo_casing/energy/E = ammo_type[select] - if(use_external_power) - var/obj/item/stock_parts/cell/external = get_external_cell() - if(!external || !external.use(E.e_cost)) //Take power from the borg... - return //Note, uses /10 because of shitty mods to the cell system - cell.give(100) //... to recharge the shot - on_recharge() - update_icon() - -/obj/item/gun/energy/proc/on_recharge() - newshot() - -/obj/item/gun/energy/attack_self(mob/living/user as mob) - if(ammo_type.len > 1) - select_fire(user) - update_icon() - if(istype(user,/mob/living/carbon/human)) //This has to be here or else if you toggle modes by clicking the gun in hand - var/mob/living/carbon/human/H = user //Otherwise the mob icon doesn't update, blame shitty human update_icons() code - H.update_inv_l_hand() - H.update_inv_r_hand() - -/obj/item/gun/energy/can_shoot() - var/obj/item/ammo_casing/energy/shot = ammo_type[select] - return cell.charge >= shot.e_cost - -/obj/item/gun/energy/newshot() - if(!ammo_type || !cell) - return - if(!chambered) - var/obj/item/ammo_casing/energy/shot = ammo_type[select] - if(cell.charge >= shot.e_cost) //if there's enough power in the cell cell... - chambered = shot //...prepare a new shot based on the current ammo type selected - if(!chambered.BB) - chambered.newshot() - -/obj/item/gun/energy/process_chamber() - if(chambered && !chambered.BB) //if BB is null, i.e the shot has been fired... - var/obj/item/ammo_casing/energy/shot = chambered - cell.use(shot.e_cost)//... drain the cell cell - robocharge() - chambered = null //either way, released the prepared shot - newshot() - -/obj/item/gun/energy/proc/select_fire(mob/living/user) - select++ - if(select > ammo_type.len) - select = 1 - var/obj/item/ammo_casing/energy/shot = ammo_type[select] - fire_sound = shot.fire_sound - fire_delay = shot.delay - if(shot.select_name) - to_chat(user, "[src] is now set to [shot.select_name].") - if(chambered)//phil235 - if(chambered.BB) - qdel(chambered.BB) - chambered.BB = null - chambered = null - newshot() - update_icon() - return - -/obj/item/gun/energy/update_icon() - overlays.Cut() - var/ratio = Ceiling((cell.charge / cell.maxcharge) * charge_sections) - var/obj/item/ammo_casing/energy/shot = ammo_type[select] - var/iconState = "[icon_state]_charge" - var/itemState = null - if(!initial(item_state)) - itemState = icon_state - if(modifystate) - overlays += "[icon_state]_[shot.select_name]" - iconState += "_[shot.select_name]" - if(itemState) - itemState += "[shot.select_name]" - if(cell.charge < shot.e_cost) - overlays += "[icon_state]_empty" - else - if(!shaded_charge) - for(var/i = ratio, i >= 1, i--) - overlays += image(icon = icon, icon_state = iconState, pixel_x = ammo_x_offset * (i -1)) - else - overlays += image(icon = icon, icon_state = "[icon_state]_[modifystate ? "[shot.select_name]_" : ""]charge[ratio]") - if(gun_light && can_flashlight) - var/iconF = "flight" - if(gun_light.on) - iconF = "flight_on" - overlays += image(icon = icon, icon_state = iconF, pixel_x = flight_x_offset, pixel_y = flight_y_offset) - if(bayonet && can_bayonet) - overlays += knife_overlay - if(itemState) - itemState += "[ratio]" - item_state = itemState - -/obj/item/gun/energy/ui_action_click() - toggle_gunlight() - -/obj/item/gun/energy/suicide_act(mob/user) - if(can_shoot()) - user.visible_message("[user] is putting the barrel of the [name] in [user.p_their()] mouth. It looks like [user.p_theyre()] trying to commit suicide.") - sleep(25) - if(user.l_hand == src || user.r_hand == src) - user.visible_message("[user] melts [user.p_their()] face off with the [name]!") - playsound(loc, fire_sound, 50, 1, -1) - var/obj/item/ammo_casing/energy/shot = ammo_type[select] - cell.use(shot.e_cost) - update_icon() - return FIRELOSS - else - user.visible_message("[user] panics and starts choking to death!") - return OXYLOSS - else - user.visible_message("[user] is pretending to blow [user.p_their()] brains out with the [name]! It looks like [user.p_theyre()] trying to commit suicide!") - playsound(loc, 'sound/weapons/empty.ogg', 50, 1, -1) - return OXYLOSS - -/obj/item/gun/energy/vv_edit_var(var_name, var_value) - switch(var_name) - if("selfcharge") - if(var_value) - START_PROCESSING(SSobj, src) - else - STOP_PROCESSING(SSobj, src) - . = ..() - -/obj/item/gun/energy/proc/robocharge() - if(isrobot(loc)) - var/mob/living/silicon/robot/R = loc - if(R && R.cell) - var/obj/item/ammo_casing/energy/shot = ammo_type[select] //Necessary to find cost of shot - if(R.cell.use(shot.e_cost)) //Take power from the borg... - cell.give(shot.e_cost) //... to recharge the shot - -/obj/item/gun/energy/proc/get_external_cell() - if(istype(loc, /obj/item/rig_module)) - var/obj/item/rig_module/module = loc - if(module.holder && module.holder.wearer) - var/mob/living/carbon/human/H = module.holder.wearer - if(istype(H) && H.back) - var/obj/item/rig/suit = H.back - if(istype(suit)) - return suit.cell - return null +/obj/item/gun/energy + icon_state = "energy" + name = "energy gun" + desc = "A basic energy-based gun." + icon = 'icons/obj/guns/energy.dmi' + fire_sound_text = "laser blast" + + var/obj/item/stock_parts/cell/cell //What type of power cell this uses + var/cell_type = /obj/item/stock_parts/cell + var/modifystate = 0 + var/list/ammo_type = list(/obj/item/ammo_casing/energy) + var/select = 1 //The state of the select fire switch. Determines from the ammo_type list what kind of shot is fired next. + var/can_charge = 1 + var/charge_sections = 4 + ammo_x_offset = 2 + var/shaded_charge = 0 //if this gun uses a stateful charge bar for more detail + var/selfcharge = 0 + var/use_external_power = 0 //if set, the weapon will look for an external power source to draw from, otherwise it recharges magically + var/charge_tick = 0 + var/charge_delay = 4 + +/obj/item/gun/energy/emp_act(severity) + cell.use(round(cell.charge / severity)) + if(chambered)//phil235 + if(chambered.BB) + qdel(chambered.BB) + chambered.BB = null + chambered = null + newshot() //phil235 + update_icon() + +/obj/item/gun/energy/get_cell() + return cell + +/obj/item/gun/energy/New() + ..() + if(cell_type) + cell = new cell_type(src) + else + cell = new(src) + cell.give(cell.maxcharge) + update_ammo_types() + on_recharge() + if(selfcharge) + START_PROCESSING(SSobj, src) + update_icon() + +/obj/item/gun/energy/proc/update_ammo_types() + var/obj/item/ammo_casing/energy/shot + for(var/i = 1, i <= ammo_type.len, i++) + var/shottype = ammo_type[i] + shot = new shottype(src) + ammo_type[i] = shot + shot = ammo_type[select] + fire_sound = shot.fire_sound + fire_delay = shot.delay + +/obj/item/gun/energy/Destroy() + if(selfcharge) + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/gun/energy/process() + if(selfcharge) //Every [recharge_time] ticks, recharge a shot for the cyborg + charge_tick++ + if(charge_tick < charge_delay) + return + charge_tick = 0 + if(!cell) + return // check if we actually need to recharge + var/obj/item/ammo_casing/energy/E = ammo_type[select] + if(use_external_power) + var/obj/item/stock_parts/cell/external = get_external_cell() + if(!external || !external.use(E.e_cost)) //Take power from the borg... + return //Note, uses /10 because of shitty mods to the cell system + cell.give(100) //... to recharge the shot + on_recharge() + update_icon() + +/obj/item/gun/energy/proc/on_recharge() + newshot() + +/obj/item/gun/energy/attack_self(mob/living/user as mob) + if(ammo_type.len > 1) + select_fire(user) + update_icon() + if(istype(user,/mob/living/carbon/human)) //This has to be here or else if you toggle modes by clicking the gun in hand + var/mob/living/carbon/human/H = user //Otherwise the mob icon doesn't update, blame shitty human update_icons() code + H.update_inv_l_hand() + H.update_inv_r_hand() + +/obj/item/gun/energy/can_shoot() + var/obj/item/ammo_casing/energy/shot = ammo_type[select] + return cell.charge >= shot.e_cost + +/obj/item/gun/energy/newshot() + if(!ammo_type || !cell) + return + if(!chambered) + var/obj/item/ammo_casing/energy/shot = ammo_type[select] + if(cell.charge >= shot.e_cost) //if there's enough power in the WEAPON'S cell... + chambered = shot //...prepare a new shot based on the current ammo type selected + if(!chambered.BB) + chambered.newshot() + +/obj/item/gun/energy/process_chamber() + if(chambered && !chambered.BB) //if BB is null, i.e the shot has been fired... + var/obj/item/ammo_casing/energy/shot = chambered + cell.use(shot.e_cost)//... drain the cell cell + robocharge() + chambered = null //either way, released the prepared shot + newshot() + +/obj/item/gun/energy/process_fire(atom/target, mob/living/user, message = 1, params, zone_override, bonus_spread = 0) + if(!chambered && can_shoot()) + process_chamber() + return ..() + +/obj/item/gun/energy/proc/select_fire(mob/living/user) + select++ + if(select > ammo_type.len) + select = 1 + var/obj/item/ammo_casing/energy/shot = ammo_type[select] + fire_sound = shot.fire_sound + fire_delay = shot.delay + if(shot.select_name) + to_chat(user, "[src] is now set to [shot.select_name].") + if(chambered)//phil235 + if(chambered.BB) + qdel(chambered.BB) + chambered.BB = null + chambered = null + newshot() + update_icon() + return + +/obj/item/gun/energy/update_icon() + overlays.Cut() + var/ratio = Ceiling((cell.charge / cell.maxcharge) * charge_sections) + var/obj/item/ammo_casing/energy/shot = ammo_type[select] + var/iconState = "[icon_state]_charge" + var/itemState = null + if(!initial(item_state)) + itemState = icon_state + if(modifystate) + overlays += "[icon_state]_[shot.select_name]" + iconState += "_[shot.select_name]" + if(itemState) + itemState += "[shot.select_name]" + if(cell.charge < shot.e_cost) + overlays += "[icon_state]_empty" + else + if(!shaded_charge) + for(var/i = ratio, i >= 1, i--) + overlays += image(icon = icon, icon_state = iconState, pixel_x = ammo_x_offset * (i -1)) + else + overlays += image(icon = icon, icon_state = "[icon_state]_[modifystate ? "[shot.select_name]_" : ""]charge[ratio]") + if(gun_light && can_flashlight) + var/iconF = "flight" + if(gun_light.on) + iconF = "flight_on" + overlays += image(icon = icon, icon_state = iconF, pixel_x = flight_x_offset, pixel_y = flight_y_offset) + if(bayonet && can_bayonet) + overlays += knife_overlay + if(itemState) + itemState += "[ratio]" + item_state = itemState + +/obj/item/gun/energy/ui_action_click() + toggle_gunlight() + +/obj/item/gun/energy/suicide_act(mob/user) + if(can_shoot()) + user.visible_message("[user] is putting the barrel of the [name] in [user.p_their()] mouth. It looks like [user.p_theyre()] trying to commit suicide.") + sleep(25) + if(user.l_hand == src || user.r_hand == src) + user.visible_message("[user] melts [user.p_their()] face off with the [name]!") + playsound(loc, fire_sound, 50, 1, -1) + var/obj/item/ammo_casing/energy/shot = ammo_type[select] + cell.use(shot.e_cost) + update_icon() + return FIRELOSS + else + user.visible_message("[user] panics and starts choking to death!") + return OXYLOSS + else + user.visible_message("[user] is pretending to blow [user.p_their()] brains out with the [name]! It looks like [user.p_theyre()] trying to commit suicide!
        ") + playsound(loc, 'sound/weapons/empty.ogg', 50, 1, -1) + return OXYLOSS + +/obj/item/gun/energy/vv_edit_var(var_name, var_value) + switch(var_name) + if("selfcharge") + if(var_value) + START_PROCESSING(SSobj, src) + else + STOP_PROCESSING(SSobj, src) + . = ..() + +/obj/item/gun/energy/proc/robocharge() + if(cell.charge == cell.maxcharge) + // No point in recharging a weapon's cell that is already at 100%. That would just waste borg cell power for no reason. + return + if(isrobot(loc)) + var/mob/living/silicon/robot/R = loc + if(R && R.cell) + var/obj/item/ammo_casing/energy/shot = ammo_type[select] //Necessary to find cost of shot + if(R.cell.use(shot.e_cost)) //Take power from the borg... + cell.give(shot.e_cost) //... to recharge the shot + +/obj/item/gun/energy/proc/get_external_cell() + if(istype(loc, /obj/item/rig_module)) + var/obj/item/rig_module/module = loc + if(module.holder && module.holder.wearer) + var/mob/living/carbon/human/H = module.holder.wearer + if(istype(H) && H.back) + var/obj/item/rig/suit = H.back + if(istype(suit)) + return suit.cell + return null diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm index 60ab08f979d..ecf5eeed77e 100644 --- a/code/modules/projectiles/guns/energy/kinetic_accelerator.dm +++ b/code/modules/projectiles/guns/energy/kinetic_accelerator.dm @@ -37,20 +37,23 @@ . += "There is a [M.name] mod installed, using [M.cost]% capacity." /obj/item/gun/energy/kinetic_accelerator/attackby(obj/item/I, mob/user) - if(iscrowbar(I)) - if(modkits.len) - to_chat(user, "You pry the modifications out.") - playsound(loc, I.usesound, 100, 1) - for(var/obj/item/borg/upgrade/modkit/M in modkits) - M.uninstall(src) - else - to_chat(user, "There are no modifications currently installed.") - else if(istype(I, /obj/item/borg/upgrade/modkit)) + if(istype(I, /obj/item/borg/upgrade/modkit)) var/obj/item/borg/upgrade/modkit/MK = I MK.install(src, user) else return ..() +/obj/item/gun/energy/kinetic_accelerator/crowbar_act(mob/user, obj/item/I) + . = TRUE + if(!modkits.len) + to_chat(user, "There are no modifications currently installed.") + return + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + to_chat(user, "You pry the modifications out.") + for(var/obj/item/borg/upgrade/modkit/M in modkits) + M.uninstall(src) + /obj/item/gun/energy/kinetic_accelerator/proc/get_remaining_mod_capacity() var/current_capacity_used = 0 for(var/A in get_modkits()) @@ -596,4 +599,4 @@ desc = "Causes kinetic accelerator bolts to have an adjustable-colored tracer trail and explosion. Use in-hand to change color." /obj/item/borg/upgrade/modkit/tracer/adjustable/attack_self(mob/user) - bolt_color = input(user,"","Choose Color",bolt_color) as color|null \ No newline at end of file + bolt_color = input(user,"","Choose Color",bolt_color) as color|null diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm index 8635e08dfba..9d4cfaee3d4 100644 --- a/code/modules/projectiles/guns/energy/laser.dm +++ b/code/modules/projectiles/guns/energy/laser.dm @@ -1,159 +1,159 @@ -/obj/item/gun/energy/laser - name = "laser gun" - desc = "A basic energy-based laser gun that fires concentrated beams of light which pass through glass and thin metal." - icon_state = "laser" - item_state = "laser" - w_class = WEIGHT_CLASS_NORMAL - materials = list(MAT_METAL=2000) - origin_tech = "combat=4;magnets=2" - ammo_type = list(/obj/item/ammo_casing/energy/lasergun) - ammo_x_offset = 1 - shaded_charge = 1 - -/obj/item/gun/energy/laser/practice - name = "practice laser gun" - desc = "A modified version of the basic laser gun, this one fires less concentrated energy bolts designed for target practice." - origin_tech = "combat=2;magnets=2" - ammo_type = list(/obj/item/ammo_casing/energy/laser/practice) - clumsy_check = 0 - needs_permit = 0 - -/obj/item/gun/energy/laser/retro - name ="retro laser gun" - icon_state = "retro" - desc = "An older model of the basic lasergun, no longer used by Nanotrasen's private security or military forces. Nevertheless, it is still quite deadly and easy to maintain, making it a favorite amongst pirates and other outlaws." - ammo_x_offset = 3 - -/obj/item/gun/energy/laser/captain - name = "antique laser gun" - icon_state = "caplaser" - item_state = "caplaser" - desc = "This is an antique laser gun. All craftsmanship is of the highest quality. It is decorated with assistant leather and chrome. The object menaces with spikes of energy. On the item is an image of Space Station 13. The station is exploding." - force = 10 - origin_tech = null - ammo_x_offset = 3 - selfcharge = 1 - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF - -/obj/item/gun/energy/laser/captain/scattershot - name = "scatter shot laser rifle" - icon_state = "lasercannon" - item_state = "laser" - desc = "An industrial-grade heavy-duty laser rifle with a modified laser lense to scatter its shot into multiple smaller lasers. The inner-core can self-charge for theorically infinite use." - origin_tech = "combat=5;materials=4;powerstorage=4" - ammo_type = list(/obj/item/ammo_casing/energy/laser/scatter, /obj/item/ammo_casing/energy/laser) - shaded_charge = 0 - -/obj/item/gun/energy/laser/cyborg - can_charge = 0 - desc = "An energy-based laser gun that draws power from the cyborg's internal energy cell directly. So this is what freedom looks like?" - ammo_type = list(/obj/item/ammo_casing/energy/laser/cyborg) - origin_tech = null - -/obj/item/gun/energy/laser/cyborg/newshot() - ..() - robocharge() - -/obj/item/gun/energy/laser/cyborg/emp_act() - return - -/obj/item/gun/energy/laser/scatter - name = "scatter laser gun" - desc = "A laser gun equipped with a refraction kit that spreads bolts." - ammo_type = list(/obj/item/ammo_casing/energy/laser/scatter, /obj/item/ammo_casing/energy/laser) - -///Laser Cannon - -/obj/item/gun/energy/lasercannon - name = "accelerator laser cannon" - desc = "An advanced laser cannon that does more damage the farther away the target is." - icon_state = "lasercannon" - item_state = "laser" - w_class = WEIGHT_CLASS_BULKY - force = 10 - flags = CONDUCT - slot_flags = SLOT_BACK - origin_tech = "combat=4;magnets=4;powerstorage=3" - ammo_type = list(/obj/item/ammo_casing/energy/laser/accelerator) - ammo_x_offset = 3 - -/obj/item/gun/energy/lasercannon/isHandgun() - return 0 - -/obj/item/ammo_casing/energy/laser/accelerator - projectile_type = /obj/item/projectile/beam/laser/accelerator - select_name = "accelerator" - fire_sound = 'sound/weapons/lasercannonfire.ogg' - -/obj/item/projectile/beam/laser/accelerator - name = "accelerator laser" - icon_state = "heavylaser" - range = 255 - damage = 6 - -/obj/item/projectile/beam/laser/accelerator/Range() - ..() - damage = min(damage+7, 100) - -/obj/item/gun/energy/lasercannon/mounted - name = "mounted laser cannon" - selfcharge = 1 - use_external_power = 1 - charge_delay = 10 - -/obj/item/gun/energy/lasercannon/cyborg - -/obj/item/gun/energy/lasercannon/cyborg/newshot() - ..() - robocharge() - -/obj/item/gun/energy/lasercannon/cyborg/emp_act() - return - -/obj/item/gun/energy/xray - name = "xray laser gun" - desc = "A high-power laser gun capable of expelling concentrated xray blasts. These blasts will penetrate solid objects, but will decrease in power the longer they have to travel." - icon_state = "xray" - origin_tech = "combat=6;materials=4;magnets=4;syndicate=1" - ammo_type = list(/obj/item/ammo_casing/energy/xray) - -/obj/item/gun/energy/immolator - name = "Immolator laser gun" - desc = "A modified laser gun, shooting highly concetrated beams with higher intensity that ignites the target, for the cost of draining more power per shot" - icon_state = "immolator" - item_state = "laser" - ammo_type = list(/obj/item/ammo_casing/energy/immolator) - origin_tech = "combat=4;magnets=4;powerstorage=3" - shaded_charge = 1 - -/obj/item/gun/energy/immolator/multi - name = "multi lens immolator cannon" - desc = "A large laser cannon, similar to the Immolator Laser, with toggleable firemodes. It is frequently used by military-like forces through Nanotrasen." - icon_state = "multilensimmolator" - ammo_type = list(/obj/item/ammo_casing/energy/immolator/strong, /obj/item/ammo_casing/energy/immolator/scatter) - origin_tech = "combat=5;magnets=5;powerstorage=4" - -/obj/item/gun/energy/immolator/multi/update_icon() - ..() - var/obj/item/ammo_casing/energy/shot = ammo_type[select] - var/append = shot.select_name - overlays += image(icon = icon, icon_state = "multilensimmolator-[append]") - -////////Laser Tag//////////////////// - -/obj/item/gun/energy/laser/tag - name = "laser tag gun" - desc = "Standard issue weapon of the Imperial Guard" - origin_tech = "combat=2;magnets=2" - clumsy_check = 0 - needs_permit = 0 - ammo_x_offset = 2 - selfcharge = 1 - -/obj/item/gun/energy/laser/tag/blue - icon_state = "bluetag" - ammo_type = list(/obj/item/ammo_casing/energy/laser/bluetag) - -/obj/item/gun/energy/laser/tag/red - icon_state = "redtag" - ammo_type = list(/obj/item/ammo_casing/energy/laser/redtag) +/obj/item/gun/energy/laser + name = "laser gun" + desc = "A basic energy-based laser gun that fires concentrated beams of light which pass through glass and thin metal." + icon_state = "laser" + item_state = "laser" + w_class = WEIGHT_CLASS_NORMAL + materials = list(MAT_METAL=2000) + origin_tech = "combat=4;magnets=2" + ammo_type = list(/obj/item/ammo_casing/energy/lasergun) + ammo_x_offset = 1 + shaded_charge = 1 + +/obj/item/gun/energy/laser/practice + name = "practice laser gun" + desc = "A modified version of the basic laser gun, this one fires less concentrated energy bolts designed for target practice." + origin_tech = "combat=2;magnets=2" + ammo_type = list(/obj/item/ammo_casing/energy/laser/practice) + clumsy_check = 0 + needs_permit = 0 + +/obj/item/gun/energy/laser/retro + name ="retro laser gun" + icon_state = "retro" + desc = "An older model of the basic lasergun, no longer used by Nanotrasen's private security or military forces. Nevertheless, it is still quite deadly and easy to maintain, making it a favorite amongst pirates and other outlaws." + ammo_x_offset = 3 + +/obj/item/gun/energy/laser/captain + name = "antique laser gun" + icon_state = "caplaser" + item_state = "caplaser" + desc = "This is an antique laser gun. All craftsmanship is of the highest quality. It is decorated with assistant leather and chrome. The object menaces with spikes of energy. On the item is an image of Space Station 13. The station is exploding." + force = 10 + origin_tech = null + ammo_x_offset = 3 + selfcharge = 1 + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + +/obj/item/gun/energy/laser/captain/scattershot + name = "scatter shot laser rifle" + icon_state = "lasercannon" + item_state = "laser" + desc = "An industrial-grade heavy-duty laser rifle with a modified laser lense to scatter its shot into multiple smaller lasers. The inner-core can self-charge for theorically infinite use." + origin_tech = "combat=5;materials=4;powerstorage=4" + ammo_type = list(/obj/item/ammo_casing/energy/laser/scatter, /obj/item/ammo_casing/energy/laser) + shaded_charge = 0 + +/obj/item/gun/energy/laser/cyborg + can_charge = 0 + desc = "An energy-based laser gun that draws power from the cyborg's internal energy cell directly. So this is what freedom looks like?" + ammo_type = list(/obj/item/ammo_casing/energy/laser/cyborg) + origin_tech = null + +/obj/item/gun/energy/laser/cyborg/newshot() + ..() + robocharge() + +/obj/item/gun/energy/laser/cyborg/emp_act() + return + +/obj/item/gun/energy/laser/scatter + name = "scatter laser gun" + desc = "A laser gun equipped with a refraction kit that spreads bolts." + ammo_type = list(/obj/item/ammo_casing/energy/laser/scatter, /obj/item/ammo_casing/energy/laser) + +///Laser Cannon + +/obj/item/gun/energy/lasercannon + name = "accelerator laser cannon" + desc = "An advanced laser cannon that does more damage the farther away the target is." + icon_state = "lasercannon" + item_state = "laser" + w_class = WEIGHT_CLASS_BULKY + force = 10 + flags = CONDUCT + slot_flags = SLOT_BACK + origin_tech = "combat=4;magnets=4;powerstorage=3" + ammo_type = list(/obj/item/ammo_casing/energy/laser/accelerator) + ammo_x_offset = 3 + +/obj/item/gun/energy/lasercannon/isHandgun() + return 0 + +/obj/item/ammo_casing/energy/laser/accelerator + projectile_type = /obj/item/projectile/beam/laser/accelerator + select_name = "accelerator" + fire_sound = 'sound/weapons/lasercannonfire.ogg' + +/obj/item/projectile/beam/laser/accelerator + name = "accelerator laser" + icon_state = "heavylaser" + range = 255 + damage = 6 + +/obj/item/projectile/beam/laser/accelerator/Range() + ..() + damage = min(damage+7, 100) + +/obj/item/gun/energy/lasercannon/mounted + name = "mounted laser cannon" + selfcharge = 1 + use_external_power = 1 + charge_delay = 10 + +/obj/item/gun/energy/lasercannon/cyborg + +/obj/item/gun/energy/lasercannon/cyborg/newshot() + ..() + robocharge() + +/obj/item/gun/energy/lasercannon/cyborg/emp_act() + return + +/obj/item/gun/energy/xray + name = "xray laser gun" + desc = "A high-power laser gun capable of expelling concentrated xray blasts. These blasts will penetrate solid objects, but will decrease in power the longer they have to travel." + icon_state = "xray" + origin_tech = "combat=6;materials=4;magnets=4;syndicate=1" + ammo_type = list(/obj/item/ammo_casing/energy/xray) + +/obj/item/gun/energy/immolator + name = "Immolator laser gun" + desc = "A modified laser gun, shooting highly concetrated beams with higher intensity that ignites the target, for the cost of draining more power per shot" + icon_state = "immolator" + item_state = "laser" + ammo_type = list(/obj/item/ammo_casing/energy/immolator) + origin_tech = "combat=4;magnets=4;powerstorage=3" + shaded_charge = 1 + +/obj/item/gun/energy/immolator/multi + name = "multi lens immolator cannon" + desc = "A large laser cannon, similar to the Immolator Laser, with toggleable firemodes. It is frequently used by military-like forces through Nanotrasen." + icon_state = "multilensimmolator" + ammo_type = list(/obj/item/ammo_casing/energy/immolator/strong, /obj/item/ammo_casing/energy/immolator/scatter) + origin_tech = "combat=5;magnets=5;powerstorage=4" + +/obj/item/gun/energy/immolator/multi/update_icon() + ..() + var/obj/item/ammo_casing/energy/shot = ammo_type[select] + var/append = shot.select_name + overlays += image(icon = icon, icon_state = "multilensimmolator-[append]") + +////////Laser Tag//////////////////// + +/obj/item/gun/energy/laser/tag + name = "laser tag gun" + desc = "Standard issue weapon of the Imperial Guard" + origin_tech = "combat=2;magnets=2" + clumsy_check = 0 + needs_permit = 0 + ammo_x_offset = 2 + selfcharge = 1 + +/obj/item/gun/energy/laser/tag/blue + icon_state = "bluetag" + ammo_type = list(/obj/item/ammo_casing/energy/laser/bluetag) + +/obj/item/gun/energy/laser/tag/red + icon_state = "redtag" + ammo_type = list(/obj/item/ammo_casing/energy/laser/redtag) diff --git a/code/modules/projectiles/guns/energy/nuclear.dm b/code/modules/projectiles/guns/energy/nuclear.dm index 4663b2d5c04..09b2400c165 100644 --- a/code/modules/projectiles/guns/energy/nuclear.dm +++ b/code/modules/projectiles/guns/energy/nuclear.dm @@ -1,99 +1,99 @@ -/obj/item/gun/energy/gun - name = "energy gun" - desc = "A basic energy-based gun with two settings: kill and disable." - icon_state = "energy" - item_state = null //so the human update icon uses the icon_state instead. - ammo_type = list(/obj/item/ammo_casing/energy/disabler, /obj/item/ammo_casing/energy/laser) - origin_tech = "combat=4;magnets=3" - modifystate = 2 - can_flashlight = 1 - ammo_x_offset = 3 - flight_x_offset = 15 - flight_y_offset = 10 - -/obj/item/gun/energy/gun/cyborg - desc = "An energy-based laser gun that draws power from the cyborg's internal energy cell directly. So this is what freedom looks like?" - -/obj/item/gun/energy/gun/cyborg/newshot() - ..() - robocharge() - -/obj/item/gun/energy/gun/cyborg/emp_act() - return - -/obj/item/gun/energy/gun/mounted - name = "mounted energy gun" - selfcharge = 1 - use_external_power = 1 - -/obj/item/gun/energy/gun/mini - name = "miniature energy gun" - desc = "A small, pistol-sized energy gun with a built-in flashlight. It has two settings: disable and kill." - icon_state = "mini" - w_class = WEIGHT_CLASS_SMALL - ammo_x_offset = 2 - charge_sections = 3 - can_flashlight = 0 // Can't attach or detach the flashlight, and override it's icon update - actions_types = list(/datum/action/item_action/toggle_gunlight) - -/obj/item/gun/energy/gun/mini/New() - gun_light = new /obj/item/flashlight/seclite(src) - ..() - cell.maxcharge = 600 - cell.charge = 600 - -/obj/item/gun/energy/gun/mini/update_icon() - ..() - if(gun_light && gun_light.on) - overlays += "mini-light" - -/obj/item/gun/energy/gun/hos - name = "\improper X-01 MultiPhase Energy Gun" - desc = "This is an expensive, modern recreation of an antique laser gun. This gun has several unique firemodes, but lacks the ability to recharge over time." - icon_state = "hoslaser" - origin_tech = null - force = 10 - ammo_type = list(/obj/item/ammo_casing/energy/electrode/hos, /obj/item/ammo_casing/energy/laser/hos, /obj/item/ammo_casing/energy/disabler) - ammo_x_offset = 4 - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF - -/obj/item/gun/energy/gun/blueshield - name = "advanced stun revolver" - desc = "An advanced stun revolver with the capacity to shoot both electrodes and lasers." - icon_state = "bsgun" - item_state = "gun" - force = 7 - ammo_type = list(/obj/item/ammo_casing/energy/electrode/hos, /obj/item/ammo_casing/energy/laser/hos) - ammo_x_offset = 1 - shaded_charge = 1 - -/obj/item/gun/energy/gun/blueshield/pdw9 - name = "PDW-9 taser pistol" - desc = "A military grade sidearm, used by many militia forces throughout the local sector." - icon_state = "pdw9pistol" - -/obj/item/gun/energy/gun/turret - name = "hybrid turret gun" - desc = "A heavy hybrid energy cannon with two settings: Stun and kill." - icon_state = "turretlaser" - item_state = "turretlaser" - slot_flags = null - w_class = WEIGHT_CLASS_HUGE - ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser) - weapon_weight = WEAPON_HEAVY - can_flashlight = 0 - trigger_guard = TRIGGER_GUARD_NONE - ammo_x_offset = 2 - -/obj/item/gun/energy/gun/nuclear - name = "advanced energy gun" - desc = "An energy gun with an experimental miniaturized nuclear reactor that automatically charges the internal power cell." - icon_state = "nucgun" - item_state = "nucgun" - origin_tech = "combat=4;magnets=4;powerstorage=4" - var/fail_tick = 0 - charge_delay = 5 - can_charge = 0 - ammo_x_offset = 1 - ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser, /obj/item/ammo_casing/energy/disabler) - selfcharge = 1 +/obj/item/gun/energy/gun + name = "energy gun" + desc = "A basic energy-based gun with two settings: kill and disable." + icon_state = "energy" + item_state = null //so the human update icon uses the icon_state instead. + ammo_type = list(/obj/item/ammo_casing/energy/disabler, /obj/item/ammo_casing/energy/laser) + origin_tech = "combat=4;magnets=3" + modifystate = 2 + can_flashlight = 1 + ammo_x_offset = 3 + flight_x_offset = 15 + flight_y_offset = 10 + +/obj/item/gun/energy/gun/cyborg + desc = "An energy-based laser gun that draws power from the cyborg's internal energy cell directly. So this is what freedom looks like?" + +/obj/item/gun/energy/gun/cyborg/newshot() + ..() + robocharge() + +/obj/item/gun/energy/gun/cyborg/emp_act() + return + +/obj/item/gun/energy/gun/mounted + name = "mounted energy gun" + selfcharge = 1 + use_external_power = 1 + +/obj/item/gun/energy/gun/mini + name = "miniature energy gun" + desc = "A small, pistol-sized energy gun with a built-in flashlight. It has two settings: disable and kill." + icon_state = "mini" + w_class = WEIGHT_CLASS_SMALL + ammo_x_offset = 2 + charge_sections = 3 + can_flashlight = 0 // Can't attach or detach the flashlight, and override it's icon update + actions_types = list(/datum/action/item_action/toggle_gunlight) + +/obj/item/gun/energy/gun/mini/New() + gun_light = new /obj/item/flashlight/seclite(src) + ..() + cell.maxcharge = 600 + cell.charge = 600 + +/obj/item/gun/energy/gun/mini/update_icon() + ..() + if(gun_light && gun_light.on) + overlays += "mini-light" + +/obj/item/gun/energy/gun/hos + name = "\improper X-01 MultiPhase Energy Gun" + desc = "This is an expensive, modern recreation of an antique laser gun. This gun has several unique firemodes, but lacks the ability to recharge over time." + icon_state = "hoslaser" + origin_tech = null + force = 10 + ammo_type = list(/obj/item/ammo_casing/energy/electrode/hos, /obj/item/ammo_casing/energy/laser/hos, /obj/item/ammo_casing/energy/disabler) + ammo_x_offset = 4 + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + +/obj/item/gun/energy/gun/blueshield + name = "advanced stun revolver" + desc = "An advanced stun revolver with the capacity to shoot both electrodes and lasers." + icon_state = "bsgun" + item_state = "gun" + force = 7 + ammo_type = list(/obj/item/ammo_casing/energy/electrode/hos, /obj/item/ammo_casing/energy/laser/hos) + ammo_x_offset = 1 + shaded_charge = 1 + +/obj/item/gun/energy/gun/blueshield/pdw9 + name = "PDW-9 taser pistol" + desc = "A military grade sidearm, used by many militia forces throughout the local sector." + icon_state = "pdw9pistol" + +/obj/item/gun/energy/gun/turret + name = "hybrid turret gun" + desc = "A heavy hybrid energy cannon with two settings: Stun and kill." + icon_state = "turretlaser" + item_state = "turretlaser" + slot_flags = null + w_class = WEIGHT_CLASS_HUGE + ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser) + weapon_weight = WEAPON_HEAVY + can_flashlight = 0 + trigger_guard = TRIGGER_GUARD_NONE + ammo_x_offset = 2 + +/obj/item/gun/energy/gun/nuclear + name = "advanced energy gun" + desc = "An energy gun with an experimental miniaturized nuclear reactor that automatically charges the internal power cell." + icon_state = "nucgun" + item_state = "nucgun" + origin_tech = "combat=4;magnets=4;powerstorage=4" + var/fail_tick = 0 + charge_delay = 5 + can_charge = 0 + ammo_x_offset = 1 + ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser, /obj/item/ammo_casing/energy/disabler) + selfcharge = 1 diff --git a/code/modules/projectiles/guns/energy/pulse.dm b/code/modules/projectiles/guns/energy/pulse.dm index 1a5766dc900..c773cb3563c 100644 --- a/code/modules/projectiles/guns/energy/pulse.dm +++ b/code/modules/projectiles/guns/energy/pulse.dm @@ -1,85 +1,85 @@ -/obj/item/gun/energy/pulse - name = "pulse rifle" - desc = "A heavy-duty, multifaceted energy rifle with three modes. Preferred by front-line combat personnel." - icon_state = "pulse" - item_state = null - w_class = WEIGHT_CLASS_BULKY - force = 10 - flags = CONDUCT - slot_flags = SLOT_BACK - ammo_type = list(/obj/item/ammo_casing/energy/laser/pulse, /obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser) - cell_type = /obj/item/stock_parts/cell/pulse - -/obj/item/gun/energy/pulse/emp_act(severity) - return - -/obj/item/gun/energy/pulse/isHandgun() - return 0 - -/obj/item/gun/energy/pulse/cyborg - -/obj/item/gun/energy/pulse/cyborg/newshot() - ..() - robocharge() - -/obj/item/gun/energy/pulse/carbine - name = "pulse carbine" - desc = "A compact variant of the pulse rifle with less firepower but easier storage." - w_class = WEIGHT_CLASS_NORMAL - slot_flags = SLOT_BELT - icon_state = "pulse_carbine" - item_state = "pulse" - cell_type = /obj/item/stock_parts/cell/pulse/carbine - can_flashlight = 1 - flight_x_offset = 18 - flight_y_offset = 12 - -/obj/item/gun/energy/pulse/pistol - name = "pulse pistol" - desc = "A pulse rifle in an easily concealed handgun package with low capacity." - w_class = WEIGHT_CLASS_SMALL - slot_flags = SLOT_BELT - icon_state = "pulse_pistol" - item_state = "gun" - cell_type = /obj/item/stock_parts/cell/pulse/pistol - can_charge = 0 - -/obj/item/gun/energy/pulse/pistol/isHandgun() - return 1 - -/obj/item/gun/energy/pulse/destroyer - name = "pulse destroyer" - desc = "A heavy-duty, pulse-based energy weapon." - cell_type = /obj/item/stock_parts/cell/infinite - ammo_type = list(/obj/item/ammo_casing/energy/laser/pulse) - -/obj/item/gun/energy/pulse/destroyer/attack_self(mob/living/user) - to_chat(user, "[name] has three settings, and they are all DESTROY.") - -/obj/item/gun/energy/pulse/destroyer/annihilator - name = "pulse ANNIHILATOR" - desc = "For when the situation calls for a little more than a pulse destroyer." - ammo_type = list(/obj/item/ammo_casing/energy/laser/scatter/pulse) - -/obj/item/gun/energy/pulse/pistol/m1911 - name = "\improper M1911-P" - desc = "A compact pulse core in a classic handgun frame for Nanotrasen officers. It's not the size of the gun, it's the size of the hole it puts through people." - icon_state = "m1911" - item_state = "gun" - cell_type = /obj/item/stock_parts/cell/infinite - -/obj/item/gun/energy/pulse/pistol/m1911/isHandgun() - return 1 - -/obj/item/gun/energy/pulse/turret - name = "pulse turret gun" - desc = "A heavy, turret-mounted pulse energy cannon." - icon_state = "turretlaser" - item_state = "turretlaser" - slot_flags = null - w_class = WEIGHT_CLASS_HUGE - ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser/pulse) - weapon_weight = WEAPON_MEDIUM - can_flashlight = 0 - trigger_guard = TRIGGER_GUARD_NONE - ammo_x_offset = 2 +/obj/item/gun/energy/pulse + name = "pulse rifle" + desc = "A heavy-duty, multifaceted energy rifle with three modes. Preferred by front-line combat personnel." + icon_state = "pulse" + item_state = null + w_class = WEIGHT_CLASS_BULKY + force = 10 + flags = CONDUCT + slot_flags = SLOT_BACK + ammo_type = list(/obj/item/ammo_casing/energy/laser/pulse, /obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser) + cell_type = /obj/item/stock_parts/cell/pulse + +/obj/item/gun/energy/pulse/emp_act(severity) + return + +/obj/item/gun/energy/pulse/isHandgun() + return 0 + +/obj/item/gun/energy/pulse/cyborg + +/obj/item/gun/energy/pulse/cyborg/newshot() + ..() + robocharge() + +/obj/item/gun/energy/pulse/carbine + name = "pulse carbine" + desc = "A compact variant of the pulse rifle with less firepower but easier storage." + w_class = WEIGHT_CLASS_NORMAL + slot_flags = SLOT_BELT + icon_state = "pulse_carbine" + item_state = "pulse" + cell_type = /obj/item/stock_parts/cell/pulse/carbine + can_flashlight = 1 + flight_x_offset = 18 + flight_y_offset = 12 + +/obj/item/gun/energy/pulse/pistol + name = "pulse pistol" + desc = "A pulse rifle in an easily concealed handgun package with low capacity." + w_class = WEIGHT_CLASS_SMALL + slot_flags = SLOT_BELT + icon_state = "pulse_pistol" + item_state = "gun" + cell_type = /obj/item/stock_parts/cell/pulse/pistol + can_charge = 0 + +/obj/item/gun/energy/pulse/pistol/isHandgun() + return 1 + +/obj/item/gun/energy/pulse/destroyer + name = "pulse destroyer" + desc = "A heavy-duty, pulse-based energy weapon." + cell_type = /obj/item/stock_parts/cell/infinite + ammo_type = list(/obj/item/ammo_casing/energy/laser/pulse) + +/obj/item/gun/energy/pulse/destroyer/attack_self(mob/living/user) + to_chat(user, "[name] has three settings, and they are all DESTROY.") + +/obj/item/gun/energy/pulse/destroyer/annihilator + name = "pulse ANNIHILATOR" + desc = "For when the situation calls for a little more than a pulse destroyer." + ammo_type = list(/obj/item/ammo_casing/energy/laser/scatter/pulse) + +/obj/item/gun/energy/pulse/pistol/m1911 + name = "\improper M1911-P" + desc = "A compact pulse core in a classic handgun frame for Nanotrasen officers. It's not the size of the gun, it's the size of the hole it puts through people." + icon_state = "m1911" + item_state = "gun" + cell_type = /obj/item/stock_parts/cell/infinite + +/obj/item/gun/energy/pulse/pistol/m1911/isHandgun() + return 1 + +/obj/item/gun/energy/pulse/turret + name = "pulse turret gun" + desc = "A heavy, turret-mounted pulse energy cannon." + icon_state = "turretlaser" + item_state = "turretlaser" + slot_flags = null + w_class = WEIGHT_CLASS_HUGE + ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser/pulse) + weapon_weight = WEAPON_MEDIUM + can_flashlight = 0 + trigger_guard = TRIGGER_GUARD_NONE + ammo_x_offset = 2 diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm index 0572ce36f76..7705f3eb66c 100644 --- a/code/modules/projectiles/guns/energy/special.dm +++ b/code/modules/projectiles/guns/energy/special.dm @@ -1,519 +1,519 @@ -// Ion Rifles // -/obj/item/gun/energy/ionrifle - name = "ion rifle" - desc = "A man portable anti-armor weapon designed to disable mechanical threats" - icon_state = "ionrifle" - item_state = null //so the human update icon uses the icon_state instead. - fire_sound = 'sound/weapons/ionrifle.ogg' - origin_tech = "combat=4;magnets=4" - w_class = WEIGHT_CLASS_HUGE - flags = CONDUCT - slot_flags = SLOT_BACK - ammo_type = list(/obj/item/ammo_casing/energy/ion) - ammo_x_offset = 3 - flight_x_offset = 17 - flight_y_offset = 9 - -/obj/item/gun/energy/ionrifle/emp_act(severity) - return - -/obj/item/gun/energy/ionrifle/isHandgun() - return 0 - -/obj/item/gun/energy/ionrifle/carbine - name = "ion carbine" - desc = "The MK.II Prototype Ion Projector is a lightweight carbine version of the larger ion rifle, built to be ergonomic and efficient." - icon_state = "ioncarbine" - w_class = WEIGHT_CLASS_NORMAL - slot_flags = SLOT_BELT - ammo_x_offset = 2 - flight_x_offset = 18 - flight_y_offset = 11 - -// Decloner // -/obj/item/gun/energy/decloner - name = "biological demolecularisor" - desc = "A gun that discharges high amounts of controlled radiation to slowly break a target into component elements." - icon_state = "decloner" - fire_sound = 'sound/weapons/pulse3.ogg' - origin_tech = "combat=4;materials=4;biotech=5;plasmatech=6" - ammo_type = list(/obj/item/ammo_casing/energy/declone) - ammo_x_offset = 1 - -/obj/item/gun/energy/decloner/update_icon() - ..() - var/obj/item/ammo_casing/energy/shot = ammo_type[select] - if(cell.charge > shot.e_cost) - overlays += "decloner_spin" - -// Flora Gun // -/obj/item/gun/energy/floragun - name = "floral somatoray" - desc = "A tool that discharges controlled radiation which induces mutation in plant cells." - icon_state = "flora" - item_state = "gun" - fire_sound = 'sound/effects/stealthoff.ogg' - ammo_type = list(/obj/item/ammo_casing/energy/flora/yield, /obj/item/ammo_casing/energy/flora/mut) - origin_tech = "materials=2;biotech=4" - modifystate = 1 - ammo_x_offset = 1 - selfcharge = 1 - -// Meteor Gun // -/obj/item/gun/energy/meteorgun - name = "meteor gun" - desc = "For the love of god, make sure you're aiming this the right way!" - icon = 'icons/obj/guns/projectile.dmi' - icon_state = "riotgun" - item_state = "c20r" - fire_sound = 'sound/weapons/gunshots/gunshot_shotgun.ogg' - w_class = WEIGHT_CLASS_BULKY - ammo_type = list(/obj/item/ammo_casing/energy/meteor) - cell_type = /obj/item/stock_parts/cell/potato - clumsy_check = 0 //Admin spawn only, might as well let clowns use it. - selfcharge = 1 - -/obj/item/gun/energy/meteorgun/pen - name = "meteor pen" - desc = "The pen is mightier than the sword." - icon = 'icons/obj/bureaucracy.dmi' - icon_state = "pen" - item_state = "pen" - lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' - righthand_file = 'icons/mob/inhands/items_righthand.dmi' - w_class = WEIGHT_CLASS_TINY - -// Mind Flayer // -/obj/item/gun/energy/mindflayer - name = "\improper Mind Flayer" - desc = "A prototype weapon recovered from the ruins of Research-Station Epsilon." - icon_state = "xray" - item_state = null - ammo_type = list(/obj/item/ammo_casing/energy/mindflayer) - ammo_x_offset = 2 - -// Energy Crossbows // -/obj/item/gun/energy/kinetic_accelerator/crossbow - name = "mini energy crossbow" - desc = "A weapon favored by syndicate stealth specialists." - icon_state = "crossbow" - item_state = "crossbow" - w_class = WEIGHT_CLASS_SMALL - materials = list(MAT_METAL=2000) - origin_tech = "combat=4;magnets=4;syndicate=5" - suppressed = 1 - ammo_type = list(/obj/item/ammo_casing/energy/bolt) - weapon_weight = WEAPON_LIGHT - unique_rename = 0 - overheat_time = 20 - holds_charge = TRUE - unique_frequency = TRUE - can_flashlight = 0 - max_mod_capacity = 0 - empty_state = null - -/obj/item/gun/energy/kinetic_accelerator/crossbow/ninja - name = "energy dart thrower" - ammo_type = list(/obj/item/ammo_casing/energy/dart) - -/obj/item/gun/energy/kinetic_accelerator/crossbow/large - name = "energy crossbow" - desc = "A reverse engineered weapon using syndicate technology." - icon_state = "crossbowlarge" - w_class = WEIGHT_CLASS_NORMAL - materials = list(MAT_METAL=4000) - origin_tech = "combat=4;magnets=4;syndicate=2" - suppressed = 0 - ammo_type = list(/obj/item/ammo_casing/energy/bolt/large) - -/obj/item/gun/energy/kinetic_accelerator/crossbow/large/cyborg - desc = "One and done!" - icon_state = "crossbowlarge" - origin_tech = null - materials = list() - -/obj/item/gun/energy/kinetic_accelerator/suicide_act(mob/user) - if(!suppressed) - playsound(loc, 'sound/weapons/kenetic_reload.ogg', 60, 1) - user.visible_message("[user] cocks the [name] and pretends to blow [user.p_their()] brains out! It looks like [user.p_theyre()] trying to commit suicide!") - shoot_live_shot() - return OXYLOSS - -// Plasma Cutters // -/obj/item/gun/energy/plasmacutter - name = "plasma cutter" - desc = "A mining tool capable of expelling concentrated plasma bursts. You could use it to cut limbs off of xenos! Or, you know, mine stuff." - icon_state = "plasmacutter" - item_state = "plasmacutter" - modifystate = -1 - origin_tech = "combat=1;materials=3;magnets=2;plasmatech=3;engineering=1" - ammo_type = list(/obj/item/ammo_casing/energy/plasma) - fire_sound = 'sound/weapons/laser.ogg' - usesound = 'sound/items/welder.ogg' - toolspeed = 1 - container_type = OPENCONTAINER - flags = CONDUCT - attack_verb = list("attacked", "slashed", "cut", "sliced") - force = 12 - sharp = 1 - can_charge = 0 - -/obj/item/gun/energy/plasmacutter/examine(mob/user) - . = ..() - if(cell) - . += "[src] is [round(cell.percent())]% charged." - -/obj/item/gun/energy/plasmacutter/attackby(obj/item/A, mob/user) - if(istype(A, /obj/item/stack/sheet/mineral/plasma)) - if(cell.charge >= cell.maxcharge) - to_chat(user,"[src] is already fully charged.") - return - var/obj/item/stack/sheet/S = A - S.use(1) - cell.give(1000) - on_recharge() - to_chat(user, "You insert [A] in [src], recharging it.") - else if(istype(A, /obj/item/stack/ore/plasma)) - if(cell.charge >= cell.maxcharge) - to_chat(user,"[src] is already fully charged.") - return - var/obj/item/stack/ore/S = A - S.use(1) - cell.give(500) - on_recharge() - to_chat(user, "You insert [A] in [src], recharging it.") - else - return ..() - -/obj/item/gun/energy/plasmacutter/update_icon() - return - -/obj/item/gun/energy/plasmacutter/adv - name = "advanced plasma cutter" - icon_state = "adv_plasmacutter" - modifystate = "adv_plasmacutter" - origin_tech = "combat=3;materials=4;magnets=3;plasmatech=4;engineering=2" - ammo_type = list(/obj/item/ammo_casing/energy/plasma/adv) - force = 15 - -// Wormhole Projectors // -/obj/item/gun/energy/wormhole_projector - name = "bluespace wormhole projector" - desc = "A projector that emits high density quantum-coupled bluespace beams." - ammo_type = list(/obj/item/ammo_casing/energy/wormhole, /obj/item/ammo_casing/energy/wormhole/orange) - item_state = null - icon_state = "wormhole_projector1" - origin_tech = "combat=4;bluespace=6;plasmatech=4;engineering=4" - var/obj/effect/portal/blue - var/obj/effect/portal/orange - - -/obj/item/gun/energy/wormhole_projector/update_icon() - icon_state = "wormhole_projector[select]" - item_state = icon_state - return - -/obj/item/gun/energy/wormhole_projector/process_chamber() - ..() - select_fire(usr) - -/obj/item/gun/energy/wormhole_projector/portal_destroyed(obj/effect/portal/P) - if(P.icon_state == "portal") - blue = null - if(orange) - orange.target = null - else - orange = null - if(blue) - blue.target = null - -/obj/item/gun/energy/wormhole_projector/proc/create_portal(obj/item/projectile/beam/wormhole/W) - var/obj/effect/portal/P = new /obj/effect/portal(get_turf(W), null, src) - P.precision = 0 - P.failchance = 0 - P.can_multitool_to_remove = 1 - if(W.name == "bluespace beam") - qdel(blue) - blue = P - else - qdel(orange) - P.icon_state = "portal1" - orange = P - if(orange && blue) - blue.target = get_turf(orange) - orange.target = get_turf(blue) - -/* 3d printer 'pseudo guns' for borgs */ -/obj/item/gun/energy/printer - name = "cyborg lmg" - desc = "A machinegun that fires 3d-printed flachettes slowly regenerated using a cyborg's internal power source." - icon_state = "l6closed0" - icon = 'icons/obj/guns/projectile.dmi' - cell_type = /obj/item/stock_parts/cell/secborg - ammo_type = list(/obj/item/ammo_casing/energy/c3dbullet) - can_charge = 0 - -/obj/item/gun/energy/printer/update_icon() - return - -/obj/item/gun/energy/printer/emp_act() - return - -// Instakill Lasers // -/obj/item/gun/energy/laser/instakill - name = "instakill rifle" - icon_state = "instagib" - item_state = "instagib" - desc = "A specialized ASMD laser-rifle, capable of flat-out disintegrating most targets in a single hit." - ammo_type = list(/obj/item/ammo_casing/energy/instakill) - force = 60 - origin_tech = "combat=7;magnets=6" - -/obj/item/gun/energy/laser/instakill/emp_act() //implying you could stop the instagib - return - -/obj/item/gun/energy/laser/instakill/red - desc = "A specialized ASMD laser-rifle, capable of flat-out disintegrating most targets in a single hit. This one has a red design." - icon_state = "instagibred" - item_state = "instagibred" - ammo_type = list(/obj/item/ammo_casing/energy/instakill/red) - -/obj/item/gun/energy/laser/instakill/blue - desc = "A specialized ASMD laser-rifle, capable of flat-out disintegrating most targets in a single hit. This one has a blue design." - icon_state = "instagibblue" - item_state = "instagibblue" - ammo_type = list(/obj/item/ammo_casing/energy/instakill/blue) - -// HONK Rifle // -/obj/item/gun/energy/clown - name = "HONK Rifle" - desc = "Clown Planet's finest." - icon_state = "disabler" - ammo_type = list(/obj/item/ammo_casing/energy/clown) - clumsy_check = 0 - selfcharge = 1 - ammo_x_offset = 3 - -/obj/item/gun/energy/toxgun - name = "plasma pistol" - desc = "A specialized firearm designed to fire lethal bolts of toxins." - icon_state = "toxgun" - fire_sound = 'sound/effects/stealthoff.ogg' - - w_class = WEIGHT_CLASS_NORMAL - origin_tech = "combat=4;magnets=4;powerstorage=3" - ammo_type = list(/obj/item/ammo_casing/energy/toxplasma) - shaded_charge = 1 - -// Energy Sniper // -/obj/item/gun/energy/sniperrifle - name = "L.W.A.P. Sniper Rifle" - desc = "A rifle constructed of lightweight materials, fitted with a SMART aiming-system scope." - icon_state = "esniper" - origin_tech = "combat=6;materials=5;powerstorage=4" - ammo_type = list(/obj/item/ammo_casing/energy/sniper) - slot_flags = SLOT_BACK - w_class = WEIGHT_CLASS_BULKY - zoomable = TRUE - zoom_amt = 7 //Long range, enough to see in front of you, but no tiles behind you. - shaded_charge = 1 - -// Temperature Gun // -/obj/item/gun/energy/temperature - name = "temperature gun" - icon = 'icons/obj/guns/gun_temperature.dmi' - icon_state = "tempgun_4" - item_state = "tempgun_4" - slot_flags = SLOT_BACK - w_class = WEIGHT_CLASS_BULKY - fire_sound = 'sound/weapons/pulse3.ogg' - desc = "A gun that changes the body temperature of its targets." - var/temperature = 300 - var/target_temperature = 300 - origin_tech = "combat=4;materials=4;powerstorage=3;magnets=2" - - ammo_type = list(/obj/item/ammo_casing/energy/temp) - selfcharge = 1 - - var/powercost = "" - var/powercostcolor = "" - - var/emagged = 0 //ups the temperature cap from 500 to 1000, targets hit by beams over 500 Kelvin will burst into flames - var/dat = "" - -/obj/item/gun/energy/temperature/New() - ..() - update_icon() - START_PROCESSING(SSobj, src) - - -/obj/item/gun/energy/temperature/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/gun/energy/temperature/newshot() - ..() - -/obj/item/gun/energy/temperature/attack_self(mob/living/user as mob) - user.set_machine(src) - update_dat() - user << browse("Temperature Gun Configuration
        [dat]", "window=tempgun;size=510x120") - onclose(user, "tempgun") - -/obj/item/gun/energy/temperature/emag_act(mob/user) - if(!emagged) - emagged = TRUE - to_chat(user, "You double the gun's temperature cap! Targets hit by searing beams will burst into flames!") - desc = "A gun that changes the body temperature of its targets. Its temperature cap has been hacked." - -/obj/item/gun/energy/temperature/Topic(href, href_list) - if(..()) - return - usr.set_machine(src) - add_fingerprint(usr) - - if(href_list["temp"]) - var/amount = text2num(href_list["temp"]) - if(amount > 0) - target_temperature = min((500 + 500*emagged), target_temperature+amount) - else - target_temperature = max(0, target_temperature+amount) - if(istype(loc, /mob)) - attack_self(loc) - add_fingerprint(usr) - return - -/obj/item/gun/energy/temperature/process() - ..() - var/obj/item/ammo_casing/energy/temp/T = ammo_type[select] - T.temp = temperature - switch(temperature) - if(0 to 100) - T.e_cost = 300 - powercost = "High" - if(100 to 250) - T.e_cost = 200 - powercost = "Medium" - if(251 to 300) - T.e_cost = 100 - powercost = "Low" - if(301 to 400) - T.e_cost = 200 - powercost = "Medium" - if(401 to 1000) - T.e_cost = 300 - powercost = "High" - switch(powercost) - if("High") - powercostcolor = "orange" - if("Medium") - powercostcolor = "green" - else - powercostcolor = "blue" - if(target_temperature != temperature) - var/difference = abs(target_temperature - temperature) - if(difference >= (10 + 40*emagged)) //so emagged temp guns adjust their temperature much more quickly - if(target_temperature < temperature) - temperature -= (10 + 40*emagged) - else - temperature += (10 + 40*emagged) - else - temperature = target_temperature - update_icon() - - if(istype(loc, /mob/living/carbon)) - var /mob/living/carbon/M = loc - if(src == M.machine) - update_dat() - M << browse("Temperature Gun Configuration
        [dat]", "window=tempgun;size=510x102") - return - -/obj/item/gun/energy/temperature/proc/update_dat() - dat = "" - dat += "Current output temperature: " - if(temperature > 500) - dat += "[temperature] ([round(temperature-T0C)]°C)" - dat += " SEARING!" - else if(temperature > (T0C + 50)) - dat += "[temperature] ([round(temperature-T0C)]°C)" - else if(temperature > (T0C - 50)) - dat += "[temperature] ([round(temperature-T0C)]°C)" - else - dat += "[temperature] ([round(temperature-T0C)]°C)" - dat += "
        " - dat += "Target output temperature: " //might be string idiocy, but at least it's easy to read - dat += "- " - dat += "- " - dat += "- " - dat += "[target_temperature] " - dat += "+ " - dat += "+ " - dat += "+" - dat += "
        " - dat += "Power cost: " - dat += "[powercost]" - -/obj/item/gun/energy/temperature/proc/update_temperature() - switch(temperature) - if(501 to INFINITY) - item_state = "tempgun_8" - if(400 to 500) - item_state = "tempgun_7" - if(360 to 400) - item_state = "tempgun_6" - if(335 to 360) - item_state = "tempgun_5" - if(295 to 335) - item_state = "tempgun_4" - if(260 to 295) - item_state = "tempgun_3" - if(200 to 260) - item_state = "tempgun_2" - if(120 to 260) - item_state = "tempgun_1" - if(-INFINITY to 120) - item_state = "tempgun_0" - icon_state = item_state - -/obj/item/gun/energy/temperature/update_icon() - overlays = 0 - update_temperature() - update_user() - update_charge() - -/obj/item/gun/energy/temperature/proc/update_user() - if(istype(loc,/mob/living/carbon)) - var/mob/living/carbon/M = loc - M.update_inv_back() - M.update_inv_l_hand() - M.update_inv_r_hand() - -/obj/item/gun/energy/temperature/proc/update_charge() - var/charge = cell.charge - switch(charge) - if(900 to INFINITY) overlays += "900" - if(800 to 900) overlays += "800" - if(700 to 800) overlays += "700" - if(600 to 700) overlays += "600" - if(500 to 600) overlays += "500" - if(400 to 500) overlays += "400" - if(300 to 400) overlays += "300" - if(200 to 300) overlays += "200" - if(100 to 202) overlays += "100" - if(-INFINITY to 100) overlays += "0" - -// Mimic Gun // -/obj/item/gun/energy/mimicgun - name = "mimic gun" - desc = "A self-defense weapon that exhausts organic targets, weakening them until they collapse. Why does this one have teeth?" - icon_state = "disabler" - ammo_type = list(/obj/item/ammo_casing/energy/mimic) - clumsy_check = 0 //Admin spawn only, might as well let clowns use it. - selfcharge = 1 - ammo_x_offset = 3 - var/mimic_type = /obj/item/gun/projectile/automatic/pistol //Setting this to the mimicgun type does exactly what you think it will. - -/obj/item/gun/energy/mimicgun/newshot() - var/obj/item/ammo_casing/energy/mimic/M = ammo_type[select] - M.mimic_type = mimic_type - ..() +// Ion Rifles // +/obj/item/gun/energy/ionrifle + name = "ion rifle" + desc = "A man portable anti-armor weapon designed to disable mechanical threats" + icon_state = "ionrifle" + item_state = null //so the human update icon uses the icon_state instead. + fire_sound = 'sound/weapons/ionrifle.ogg' + origin_tech = "combat=4;magnets=4" + w_class = WEIGHT_CLASS_HUGE + flags = CONDUCT + slot_flags = SLOT_BACK + ammo_type = list(/obj/item/ammo_casing/energy/ion) + ammo_x_offset = 3 + flight_x_offset = 17 + flight_y_offset = 9 + +/obj/item/gun/energy/ionrifle/emp_act(severity) + return + +/obj/item/gun/energy/ionrifle/isHandgun() + return 0 + +/obj/item/gun/energy/ionrifle/carbine + name = "ion carbine" + desc = "The MK.II Prototype Ion Projector is a lightweight carbine version of the larger ion rifle, built to be ergonomic and efficient." + icon_state = "ioncarbine" + w_class = WEIGHT_CLASS_NORMAL + slot_flags = SLOT_BELT + ammo_x_offset = 2 + flight_x_offset = 18 + flight_y_offset = 11 + +// Decloner // +/obj/item/gun/energy/decloner + name = "biological demolecularisor" + desc = "A gun that discharges high amounts of controlled radiation to slowly break a target into component elements." + icon_state = "decloner" + fire_sound = 'sound/weapons/pulse3.ogg' + origin_tech = "combat=4;materials=4;biotech=5;plasmatech=6" + ammo_type = list(/obj/item/ammo_casing/energy/declone) + ammo_x_offset = 1 + +/obj/item/gun/energy/decloner/update_icon() + ..() + var/obj/item/ammo_casing/energy/shot = ammo_type[select] + if(cell.charge > shot.e_cost) + overlays += "decloner_spin" + +// Flora Gun // +/obj/item/gun/energy/floragun + name = "floral somatoray" + desc = "A tool that discharges controlled radiation which induces mutation in plant cells." + icon_state = "flora" + item_state = "gun" + fire_sound = 'sound/effects/stealthoff.ogg' + ammo_type = list(/obj/item/ammo_casing/energy/flora/yield, /obj/item/ammo_casing/energy/flora/mut) + origin_tech = "materials=2;biotech=4" + modifystate = 1 + ammo_x_offset = 1 + selfcharge = 1 + +// Meteor Gun // +/obj/item/gun/energy/meteorgun + name = "meteor gun" + desc = "For the love of god, make sure you're aiming this the right way!" + icon = 'icons/obj/guns/projectile.dmi' + icon_state = "riotgun" + item_state = "c20r" + fire_sound = 'sound/weapons/gunshots/gunshot_shotgun.ogg' + w_class = WEIGHT_CLASS_BULKY + ammo_type = list(/obj/item/ammo_casing/energy/meteor) + cell_type = /obj/item/stock_parts/cell/potato + clumsy_check = 0 //Admin spawn only, might as well let clowns use it. + selfcharge = 1 + +/obj/item/gun/energy/meteorgun/pen + name = "meteor pen" + desc = "The pen is mightier than the sword." + icon = 'icons/obj/bureaucracy.dmi' + icon_state = "pen" + item_state = "pen" + lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' + righthand_file = 'icons/mob/inhands/items_righthand.dmi' + w_class = WEIGHT_CLASS_TINY + +// Mind Flayer // +/obj/item/gun/energy/mindflayer + name = "\improper Mind Flayer" + desc = "A prototype weapon recovered from the ruins of Research-Station Epsilon." + icon_state = "xray" + item_state = null + ammo_type = list(/obj/item/ammo_casing/energy/mindflayer) + ammo_x_offset = 2 + +// Energy Crossbows // +/obj/item/gun/energy/kinetic_accelerator/crossbow + name = "mini energy crossbow" + desc = "A weapon favored by syndicate stealth specialists." + icon_state = "crossbow" + item_state = "crossbow" + w_class = WEIGHT_CLASS_SMALL + materials = list(MAT_METAL=2000) + origin_tech = "combat=4;magnets=4;syndicate=5" + suppressed = 1 + ammo_type = list(/obj/item/ammo_casing/energy/bolt) + weapon_weight = WEAPON_LIGHT + unique_rename = 0 + overheat_time = 20 + holds_charge = TRUE + unique_frequency = TRUE + can_flashlight = 0 + max_mod_capacity = 0 + empty_state = null + +/obj/item/gun/energy/kinetic_accelerator/crossbow/ninja + name = "energy dart thrower" + ammo_type = list(/obj/item/ammo_casing/energy/dart) + +/obj/item/gun/energy/kinetic_accelerator/crossbow/large + name = "energy crossbow" + desc = "A reverse engineered weapon using syndicate technology." + icon_state = "crossbowlarge" + w_class = WEIGHT_CLASS_NORMAL + materials = list(MAT_METAL=4000) + origin_tech = "combat=4;magnets=4;syndicate=2" + suppressed = 0 + ammo_type = list(/obj/item/ammo_casing/energy/bolt/large) + +/obj/item/gun/energy/kinetic_accelerator/crossbow/large/cyborg + desc = "One and done!" + icon_state = "crossbowlarge" + origin_tech = null + materials = list() + +/obj/item/gun/energy/kinetic_accelerator/suicide_act(mob/user) + if(!suppressed) + playsound(loc, 'sound/weapons/kenetic_reload.ogg', 60, 1) + user.visible_message("[user] cocks the [name] and pretends to blow [user.p_their()] brains out! It looks like [user.p_theyre()] trying to commit suicide!
        ") + shoot_live_shot() + return OXYLOSS + +// Plasma Cutters // +/obj/item/gun/energy/plasmacutter + name = "plasma cutter" + desc = "A mining tool capable of expelling concentrated plasma bursts. You could use it to cut limbs off of xenos! Or, you know, mine stuff." + icon_state = "plasmacutter" + item_state = "plasmacutter" + modifystate = -1 + origin_tech = "combat=1;materials=3;magnets=2;plasmatech=3;engineering=1" + ammo_type = list(/obj/item/ammo_casing/energy/plasma) + fire_sound = 'sound/weapons/laser.ogg' + usesound = 'sound/items/welder.ogg' + toolspeed = 1 + container_type = OPENCONTAINER + flags = CONDUCT + attack_verb = list("attacked", "slashed", "cut", "sliced") + force = 12 + sharp = 1 + can_charge = 0 + +/obj/item/gun/energy/plasmacutter/examine(mob/user) + . = ..() + if(cell) + . += "[src] is [round(cell.percent())]% charged." + +/obj/item/gun/energy/plasmacutter/attackby(obj/item/A, mob/user) + if(istype(A, /obj/item/stack/sheet/mineral/plasma)) + if(cell.charge >= cell.maxcharge) + to_chat(user,"[src] is already fully charged.") + return + var/obj/item/stack/sheet/S = A + S.use(1) + cell.give(1000) + on_recharge() + to_chat(user, "You insert [A] in [src], recharging it.") + else if(istype(A, /obj/item/stack/ore/plasma)) + if(cell.charge >= cell.maxcharge) + to_chat(user,"[src] is already fully charged.") + return + var/obj/item/stack/ore/S = A + S.use(1) + cell.give(500) + on_recharge() + to_chat(user, "You insert [A] in [src], recharging it.") + else + return ..() + +/obj/item/gun/energy/plasmacutter/update_icon() + return + +/obj/item/gun/energy/plasmacutter/adv + name = "advanced plasma cutter" + icon_state = "adv_plasmacutter" + modifystate = "adv_plasmacutter" + origin_tech = "combat=3;materials=4;magnets=3;plasmatech=4;engineering=2" + ammo_type = list(/obj/item/ammo_casing/energy/plasma/adv) + force = 15 + +// Wormhole Projectors // +/obj/item/gun/energy/wormhole_projector + name = "bluespace wormhole projector" + desc = "A projector that emits high density quantum-coupled bluespace beams." + ammo_type = list(/obj/item/ammo_casing/energy/wormhole, /obj/item/ammo_casing/energy/wormhole/orange) + item_state = null + icon_state = "wormhole_projector1" + origin_tech = "combat=4;bluespace=6;plasmatech=4;engineering=4" + var/obj/effect/portal/blue + var/obj/effect/portal/orange + + +/obj/item/gun/energy/wormhole_projector/update_icon() + icon_state = "wormhole_projector[select]" + item_state = icon_state + return + +/obj/item/gun/energy/wormhole_projector/process_chamber() + ..() + select_fire(usr) + +/obj/item/gun/energy/wormhole_projector/portal_destroyed(obj/effect/portal/P) + if(P.icon_state == "portal") + blue = null + if(orange) + orange.target = null + else + orange = null + if(blue) + blue.target = null + +/obj/item/gun/energy/wormhole_projector/proc/create_portal(obj/item/projectile/beam/wormhole/W) + var/obj/effect/portal/P = new /obj/effect/portal(get_turf(W), null, src) + P.precision = 0 + P.failchance = 0 + P.can_multitool_to_remove = 1 + if(W.name == "bluespace beam") + qdel(blue) + blue = P + else + qdel(orange) + P.icon_state = "portal1" + orange = P + if(orange && blue) + blue.target = get_turf(orange) + orange.target = get_turf(blue) + +/* 3d printer 'pseudo guns' for borgs */ +/obj/item/gun/energy/printer + name = "cyborg lmg" + desc = "A machinegun that fires 3d-printed flachettes slowly regenerated using a cyborg's internal power source." + icon_state = "l6closed0" + icon = 'icons/obj/guns/projectile.dmi' + cell_type = /obj/item/stock_parts/cell/secborg + ammo_type = list(/obj/item/ammo_casing/energy/c3dbullet) + can_charge = 0 + +/obj/item/gun/energy/printer/update_icon() + return + +/obj/item/gun/energy/printer/emp_act() + return + +// Instakill Lasers // +/obj/item/gun/energy/laser/instakill + name = "instakill rifle" + icon_state = "instagib" + item_state = "instagib" + desc = "A specialized ASMD laser-rifle, capable of flat-out disintegrating most targets in a single hit." + ammo_type = list(/obj/item/ammo_casing/energy/instakill) + force = 60 + origin_tech = "combat=7;magnets=6" + +/obj/item/gun/energy/laser/instakill/emp_act() //implying you could stop the instagib + return + +/obj/item/gun/energy/laser/instakill/red + desc = "A specialized ASMD laser-rifle, capable of flat-out disintegrating most targets in a single hit. This one has a red design." + icon_state = "instagibred" + item_state = "instagibred" + ammo_type = list(/obj/item/ammo_casing/energy/instakill/red) + +/obj/item/gun/energy/laser/instakill/blue + desc = "A specialized ASMD laser-rifle, capable of flat-out disintegrating most targets in a single hit. This one has a blue design." + icon_state = "instagibblue" + item_state = "instagibblue" + ammo_type = list(/obj/item/ammo_casing/energy/instakill/blue) + +// HONK Rifle // +/obj/item/gun/energy/clown + name = "HONK Rifle" + desc = "Clown Planet's finest." + icon_state = "disabler" + ammo_type = list(/obj/item/ammo_casing/energy/clown) + clumsy_check = 0 + selfcharge = 1 + ammo_x_offset = 3 + +/obj/item/gun/energy/toxgun + name = "plasma pistol" + desc = "A specialized firearm designed to fire lethal bolts of toxins." + icon_state = "toxgun" + fire_sound = 'sound/effects/stealthoff.ogg' + + w_class = WEIGHT_CLASS_NORMAL + origin_tech = "combat=4;magnets=4;powerstorage=3" + ammo_type = list(/obj/item/ammo_casing/energy/toxplasma) + shaded_charge = 1 + +// Energy Sniper // +/obj/item/gun/energy/sniperrifle + name = "L.W.A.P. Sniper Rifle" + desc = "A rifle constructed of lightweight materials, fitted with a SMART aiming-system scope." + icon_state = "esniper" + origin_tech = "combat=6;materials=5;powerstorage=4" + ammo_type = list(/obj/item/ammo_casing/energy/sniper) + slot_flags = SLOT_BACK + w_class = WEIGHT_CLASS_BULKY + zoomable = TRUE + zoom_amt = 7 //Long range, enough to see in front of you, but no tiles behind you. + shaded_charge = 1 + +// Temperature Gun // +/obj/item/gun/energy/temperature + name = "temperature gun" + icon = 'icons/obj/guns/gun_temperature.dmi' + icon_state = "tempgun_4" + item_state = "tempgun_4" + slot_flags = SLOT_BACK + w_class = WEIGHT_CLASS_BULKY + fire_sound = 'sound/weapons/pulse3.ogg' + desc = "A gun that changes the body temperature of its targets." + var/temperature = 300 + var/target_temperature = 300 + origin_tech = "combat=4;materials=4;powerstorage=3;magnets=2" + + ammo_type = list(/obj/item/ammo_casing/energy/temp) + selfcharge = 1 + + var/powercost = "" + var/powercostcolor = "" + + var/emagged = 0 //ups the temperature cap from 500 to 1000, targets hit by beams over 500 Kelvin will burst into flames + var/dat = "" + +/obj/item/gun/energy/temperature/New() + ..() + update_icon() + START_PROCESSING(SSobj, src) + + +/obj/item/gun/energy/temperature/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/gun/energy/temperature/newshot() + ..() + +/obj/item/gun/energy/temperature/attack_self(mob/living/user as mob) + user.set_machine(src) + update_dat() + user << browse("Temperature Gun Configuration
        [dat]", "window=tempgun;size=510x120") + onclose(user, "tempgun") + +/obj/item/gun/energy/temperature/emag_act(mob/user) + if(!emagged) + emagged = TRUE + to_chat(user, "You double the gun's temperature cap! Targets hit by searing beams will burst into flames!") + desc = "A gun that changes the body temperature of its targets. Its temperature cap has been hacked." + +/obj/item/gun/energy/temperature/Topic(href, href_list) + if(..()) + return + usr.set_machine(src) + add_fingerprint(usr) + + if(href_list["temp"]) + var/amount = text2num(href_list["temp"]) + if(amount > 0) + target_temperature = min((500 + 500*emagged), target_temperature+amount) + else + target_temperature = max(0, target_temperature+amount) + if(istype(loc, /mob)) + attack_self(loc) + add_fingerprint(usr) + return + +/obj/item/gun/energy/temperature/process() + ..() + var/obj/item/ammo_casing/energy/temp/T = ammo_type[select] + T.temp = temperature + switch(temperature) + if(0 to 100) + T.e_cost = 300 + powercost = "High" + if(100 to 250) + T.e_cost = 200 + powercost = "Medium" + if(251 to 300) + T.e_cost = 100 + powercost = "Low" + if(301 to 400) + T.e_cost = 200 + powercost = "Medium" + if(401 to 1000) + T.e_cost = 300 + powercost = "High" + switch(powercost) + if("High") + powercostcolor = "orange" + if("Medium") + powercostcolor = "green" + else + powercostcolor = "blue" + if(target_temperature != temperature) + var/difference = abs(target_temperature - temperature) + if(difference >= (10 + 40*emagged)) //so emagged temp guns adjust their temperature much more quickly + if(target_temperature < temperature) + temperature -= (10 + 40*emagged) + else + temperature += (10 + 40*emagged) + else + temperature = target_temperature + update_icon() + + if(istype(loc, /mob/living/carbon)) + var /mob/living/carbon/M = loc + if(src == M.machine) + update_dat() + M << browse("Temperature Gun Configuration
        [dat]", "window=tempgun;size=510x102") + return + +/obj/item/gun/energy/temperature/proc/update_dat() + dat = "" + dat += "Current output temperature: " + if(temperature > 500) + dat += "[temperature] ([round(temperature-T0C)]°C)" + dat += " SEARING!" + else if(temperature > (T0C + 50)) + dat += "[temperature] ([round(temperature-T0C)]°C)" + else if(temperature > (T0C - 50)) + dat += "[temperature] ([round(temperature-T0C)]°C)" + else + dat += "[temperature] ([round(temperature-T0C)]°C)" + dat += "
        " + dat += "Target output temperature: " //might be string idiocy, but at least it's easy to read + dat += "- " + dat += "- " + dat += "- " + dat += "[target_temperature] " + dat += "+ " + dat += "+ " + dat += "+" + dat += "
        " + dat += "Power cost: " + dat += "[powercost]" + +/obj/item/gun/energy/temperature/proc/update_temperature() + switch(temperature) + if(501 to INFINITY) + item_state = "tempgun_8" + if(400 to 500) + item_state = "tempgun_7" + if(360 to 400) + item_state = "tempgun_6" + if(335 to 360) + item_state = "tempgun_5" + if(295 to 335) + item_state = "tempgun_4" + if(260 to 295) + item_state = "tempgun_3" + if(200 to 260) + item_state = "tempgun_2" + if(120 to 260) + item_state = "tempgun_1" + if(-INFINITY to 120) + item_state = "tempgun_0" + icon_state = item_state + +/obj/item/gun/energy/temperature/update_icon() + overlays = 0 + update_temperature() + update_user() + update_charge() + +/obj/item/gun/energy/temperature/proc/update_user() + if(istype(loc,/mob/living/carbon)) + var/mob/living/carbon/M = loc + M.update_inv_back() + M.update_inv_l_hand() + M.update_inv_r_hand() + +/obj/item/gun/energy/temperature/proc/update_charge() + var/charge = cell.charge + switch(charge) + if(900 to INFINITY) overlays += "900" + if(800 to 900) overlays += "800" + if(700 to 800) overlays += "700" + if(600 to 700) overlays += "600" + if(500 to 600) overlays += "500" + if(400 to 500) overlays += "400" + if(300 to 400) overlays += "300" + if(200 to 300) overlays += "200" + if(100 to 202) overlays += "100" + if(-INFINITY to 100) overlays += "0" + +// Mimic Gun // +/obj/item/gun/energy/mimicgun + name = "mimic gun" + desc = "A self-defense weapon that exhausts organic targets, weakening them until they collapse. Why does this one have teeth?" + icon_state = "disabler" + ammo_type = list(/obj/item/ammo_casing/energy/mimic) + clumsy_check = 0 //Admin spawn only, might as well let clowns use it. + selfcharge = 1 + ammo_x_offset = 3 + var/mimic_type = /obj/item/gun/projectile/automatic/pistol //Setting this to the mimicgun type does exactly what you think it will. + +/obj/item/gun/energy/mimicgun/newshot() + var/obj/item/ammo_casing/energy/mimic/M = ammo_type[select] + M.mimic_type = mimic_type + ..() diff --git a/code/modules/projectiles/guns/energy/stun.dm b/code/modules/projectiles/guns/energy/stun.dm index 5f06a0fa11e..70bb8be7e46 100644 --- a/code/modules/projectiles/guns/energy/stun.dm +++ b/code/modules/projectiles/guns/energy/stun.dm @@ -1,60 +1,60 @@ -/obj/item/gun/energy/taser - name = "taser gun" - desc = "A small, low capacity gun used for non-lethal takedowns." - icon_state = "taser" - item_state = null //so the human update icon uses the icon_state instead. - origin_tech = "combat=3" - ammo_type = list(/obj/item/ammo_casing/energy/electrode) - ammo_x_offset = 3 - -/obj/item/gun/energy/taser/mounted - name = "mounted taser gun" - selfcharge = 1 - use_external_power = 1 - -/obj/item/gun/energy/shock_revolver - name = "tesla revolver" - desc = "A high-tech revolver that fires internal, reusable shock cartridges in a revolving cylinder. The cartridges can be recharged using conventional rechargers." - icon_state = "stunrevolver" - item_state = "gun" - origin_tech = "combat=4;materials=4;powerstorage=4" - ammo_type = list(/obj/item/ammo_casing/energy/shock_revolver) - can_flashlight = 0 - shaded_charge = 1 - -/obj/item/gun/energy/gun/advtaser - name = "hybrid taser" - desc = "A dual-mode taser designed to fire both short-range high-power electrodes and long-range disabler beams." - icon_state = "advtaser" - ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/disabler) - origin_tech = "combat=4" - ammo_x_offset = 2 - -/obj/item/gun/energy/gun/advtaser/cyborg - name = "cyborg taser" - desc = "An integrated hybrid taser that draws directly from a cyborg's power cell. The weapon contains a limiter to prevent the cyborg's power cell from overheating." - can_flashlight = 0 - can_charge = 0 - -/obj/item/gun/energy/gun/advtaser/cyborg/newshot() - ..() - robocharge() - -/obj/item/gun/energy/disabler - name = "disabler" - desc = "A self-defense weapon that exhausts organic targets, weakening them until they collapse." - icon_state = "disabler" - item_state = null - origin_tech = "combat=3" - ammo_type = list(/obj/item/ammo_casing/energy/disabler) - ammo_x_offset = 3 - -/obj/item/gun/energy/disabler/cyborg - name = "cyborg disabler" - desc = "An integrated disabler that draws from a cyborg's power cell. This weapon contains a limiter to prevent the cyborg's power cell from overheating." - ammo_type = list(/obj/item/ammo_casing/energy/disabler/cyborg) - can_charge = 0 - -/obj/item/gun/energy/disabler/cyborg/newshot() - ..() - robocharge() +/obj/item/gun/energy/taser + name = "taser gun" + desc = "A small, low capacity gun used for non-lethal takedowns." + icon_state = "taser" + item_state = null //so the human update icon uses the icon_state instead. + origin_tech = "combat=3" + ammo_type = list(/obj/item/ammo_casing/energy/electrode) + ammo_x_offset = 3 + +/obj/item/gun/energy/taser/mounted + name = "mounted taser gun" + selfcharge = 1 + use_external_power = 1 + +/obj/item/gun/energy/shock_revolver + name = "tesla revolver" + desc = "A high-tech revolver that fires internal, reusable shock cartridges in a revolving cylinder. The cartridges can be recharged using conventional rechargers." + icon_state = "stunrevolver" + item_state = "gun" + origin_tech = "combat=4;materials=4;powerstorage=4" + ammo_type = list(/obj/item/ammo_casing/energy/shock_revolver) + can_flashlight = 0 + shaded_charge = 1 + +/obj/item/gun/energy/gun/advtaser + name = "hybrid taser" + desc = "A dual-mode taser designed to fire both short-range high-power electrodes and long-range disabler beams." + icon_state = "advtaser" + ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/disabler) + origin_tech = "combat=4" + ammo_x_offset = 2 + +/obj/item/gun/energy/gun/advtaser/cyborg + name = "cyborg taser" + desc = "An integrated hybrid taser that draws directly from a cyborg's power cell. The weapon contains a limiter to prevent the cyborg's power cell from overheating." + can_flashlight = 0 + can_charge = 0 + +/obj/item/gun/energy/gun/advtaser/cyborg/newshot() + ..() + robocharge() + +/obj/item/gun/energy/disabler + name = "disabler" + desc = "A self-defense weapon that exhausts organic targets, weakening them until they collapse." + icon_state = "disabler" + item_state = null + origin_tech = "combat=3" + ammo_type = list(/obj/item/ammo_casing/energy/disabler) + ammo_x_offset = 3 + +/obj/item/gun/energy/disabler/cyborg + name = "cyborg disabler" + desc = "An integrated disabler that draws from a cyborg's power cell. This weapon contains a limiter to prevent the cyborg's power cell from overheating." + ammo_type = list(/obj/item/ammo_casing/energy/disabler/cyborg) + can_charge = 0 + +/obj/item/gun/energy/disabler/cyborg/newshot() + ..() + robocharge() diff --git a/code/modules/projectiles/guns/misc/blastcannon.dm b/code/modules/projectiles/guns/misc/blastcannon.dm index 89cceab6cd3..6b29a203c02 100644 --- a/code/modules/projectiles/guns/misc/blastcannon.dm +++ b/code/modules/projectiles/guns/misc/blastcannon.dm @@ -127,4 +127,4 @@ lightr = max(lightr - 1, 0) /obj/item/projectile/blastwave/ex_act() - return \ No newline at end of file + return diff --git a/code/modules/projectiles/guns/mounted.dm b/code/modules/projectiles/guns/mounted.dm index d024c596fe1..162cc541fd2 100644 --- a/code/modules/projectiles/guns/mounted.dm +++ b/code/modules/projectiles/guns/mounted.dm @@ -17,4 +17,4 @@ item_state = "armcannonlase" force = 5 selfcharge = 1 - trigger_guard = TRIGGER_GUARD_ALLOW_ALL \ No newline at end of file + trigger_guard = TRIGGER_GUARD_ALLOW_ALL diff --git a/code/modules/projectiles/guns/projectile.dm b/code/modules/projectiles/guns/projectile.dm index 133331f8e44..25db1f10dcb 100644 --- a/code/modules/projectiles/guns/projectile.dm +++ b/code/modules/projectiles/guns/projectile.dm @@ -1,214 +1,214 @@ -/obj/item/gun/projectile - desc = "Now comes in flavors like GUN. Uses 10mm ammo, for some reason" - name = "projectile gun" - icon_state = "pistol" - origin_tech = "combat=2;materials=2" - w_class = WEIGHT_CLASS_NORMAL - materials = list(MAT_METAL=1000) - - var/mag_type = /obj/item/ammo_box/magazine/m10mm //Removes the need for max_ammo and caliber info - var/obj/item/ammo_box/magazine/magazine - var/can_tactical = FALSE //check to see if the gun can tactically reload - -/obj/item/gun/projectile/New() - ..() - if(!magazine) - magazine = new mag_type(src) - chamber_round() - update_icon() - return - -/obj/item/gun/projectile/update_icon() - ..() - if(current_skin) - icon_state = "[current_skin][suppressed ? "-suppressed" : ""][sawn_state ? "-sawn" : ""]" - else - icon_state = "[initial(icon_state)][suppressed ? "-suppressed" : ""][sawn_state ? "-sawn" : ""]" - if(bayonet && can_bayonet) - overlays += knife_overlay - -/obj/item/gun/projectile/process_chamber(eject_casing = 1, empty_chamber = 1) - var/obj/item/ammo_casing/AC = chambered //Find chambered round - if(isnull(AC) || !istype(AC)) - chamber_round() - return - if(eject_casing) - AC.loc = get_turf(src) //Eject casing onto ground. - AC.SpinAnimation(10, 1) //next gen special effects - playsound(src, chambered.drop_sound, 100, 1) - if(empty_chamber) - chambered = null - chamber_round() - return - -/obj/item/gun/projectile/proc/chamber_round() - if(chambered || !magazine) - return - else if(magazine.ammo_count()) - chambered = magazine.get_round() - chambered.loc = src - return - -/obj/item/gun/projectile/can_shoot() - if(!magazine || !magazine.ammo_count(0)) - return 0 - return 1 - -/obj/item/gun/projectile/proc/can_reload() - return !magazine - -/obj/item/gun/projectile/proc/reload(obj/item/ammo_box/magazine/AM, mob/user as mob) - user.remove_from_mob(AM) - magazine = AM - magazine.loc = src - playsound(src, magin_sound, 50, 1) - chamber_round() - AM.update_icon() - update_icon() - return - -/obj/item/gun/projectile/attackby(var/obj/item/A as obj, mob/user as mob, params) - if(istype(A, /obj/item/ammo_box/magazine)) - var/obj/item/ammo_box/magazine/AM = A - if(istype(AM, mag_type)) - if(can_reload()) - reload(AM, user) - to_chat(user, "You load a new magazine into \the [src].") - return TRUE - else if(!can_tactical) - to_chat(user, "There's already a magazine in \the [src].") - return TRUE - else - to_chat(user, "You perform a tactical reload on \the [src], replacing the magazine.") - magazine.loc = get_turf(loc) - magazine.update_icon() - magazine = null - reload(AM, user) - return TRUE - else - to_chat(user, "You can't put this type of ammo in \the [src].") - return TRUE - if(istype(A, /obj/item/suppressor)) - var/obj/item/suppressor/S = A - if(can_suppress) - if(!suppressed) - if(!user.unEquip(A)) - return - to_chat(user, "You screw [S] onto [src].") - suppressed = A - S.oldsound = fire_sound - S.initial_w_class = w_class - fire_sound = 'sound/weapons/gunshots/gunshot_silenced.ogg' - w_class = WEIGHT_CLASS_NORMAL //so pistols do not fit in pockets when suppressed - A.loc = src - update_icon() - return - else - to_chat(user, "[src] already has a suppressor.") - return - else - to_chat(user, "You can't seem to figure out how to fit [S] on [src].") - return - else - return ..() - -/obj/item/gun/projectile/attack_hand(mob/user) - if(loc == user) - if(suppressed && can_unsuppress) - var/obj/item/suppressor/S = suppressed - if(user.l_hand != src && user.r_hand != src) - ..() - return - to_chat(user, "You unscrew [suppressed] from [src].") - user.put_in_hands(suppressed) - fire_sound = S.oldsound - w_class = S.initial_w_class - suppressed = 0 - update_icon() - return - ..() - -/obj/item/gun/projectile/attack_self(mob/living/user as mob) - var/obj/item/ammo_casing/AC = chambered //Find chambered round - if(magazine) - magazine.loc = get_turf(loc) - user.put_in_hands(magazine) - magazine.update_icon() - magazine = null - to_chat(user, "You pull the magazine out of \the [src]!") - playsound(src, magout_sound, 50, 1) - else if(chambered) - AC.loc = get_turf(src) - AC.SpinAnimation(10, 1) - chambered = null - to_chat(user, "You unload the round from \the [src]'s chamber.") - playsound(src, 'sound/weapons/gun_interactions/remove_bullet.ogg', 50, 1) - else - to_chat(user, "There's no magazine in \the [src].") - update_icon() - return - -/obj/item/gun/projectile/examine(mob/user) - . = ..() - . += "Has [get_ammo()] round\s remaining." - -/obj/item/gun/projectile/proc/get_ammo(countchambered = 1) - var/boolets = 0 //mature var names for mature people - if(chambered && countchambered) - boolets++ - if(magazine) - boolets += magazine.ammo_count() - return boolets - -/obj/item/gun/projectile/suicide_act(mob/user) - if(chambered && chambered.BB && !chambered.BB.nodamage) - user.visible_message("[user] is putting the barrel of the [name] in [user.p_their()] mouth. It looks like [user.p_theyre()] trying to commit suicide.") - sleep(25) - if(user.l_hand == src || user.r_hand == src) - process_fire(user, user, 0, zone_override = "head") - user.visible_message("[user] blows [user.p_their()] brains out with the [name]!") - return BRUTELOSS - else - user.visible_message("[user] panics and starts choking to death!") - return OXYLOSS - else - user.visible_message("[user] is pretending to blow [user.p_their()] brains out with the [name]! It looks like [user.p_theyre()] trying to commit suicide!") - playsound(loc, 'sound/weapons/empty.ogg', 50, 1, -1) - return OXYLOSS - -/obj/item/gun/projectile/proc/sawoff(mob/user) - if(sawn_state == SAWN_OFF) - to_chat(user, "\The [src] is already shortened!") - return - if(bayonet) - to_chat(user, "You cannot saw-off [src] with [bayonet] attached!") - return - user.changeNext_move(CLICK_CD_MELEE) - user.visible_message("[user] begins to shorten \the [src].", "You begin to shorten \the [src]...") - - //if there's any live ammo inside the gun, makes it go off - if(blow_up(user)) - user.visible_message("\The [src] goes off!", "\The [src] goes off in your face!") - return - - if(do_after(user, 30, target = src)) - if(sawn_state == SAWN_OFF) - return - user.visible_message("[user] shortens \the [src]!", "You shorten \the [src].") - name = "sawn-off [name]" - desc = sawn_desc - w_class = WEIGHT_CLASS_NORMAL - item_state = "gun"//phil235 is it different with different skin? - slot_flags &= ~SLOT_BACK //you can't sling it on your back - slot_flags |= SLOT_BELT //but you can wear it on your belt (poorly concealed under a trenchcoat, ideally) - sawn_state = SAWN_OFF - update_icon() - return 1 - -// Sawing guns related proc -/obj/item/gun/projectile/proc/blow_up(mob/user) - . = 0 - for(var/obj/item/ammo_casing/AC in magazine.stored_ammo) - if(AC.BB) - process_fire(user, user,0) - . = 1 \ No newline at end of file +/obj/item/gun/projectile + desc = "Now comes in flavors like GUN. Uses 10mm ammo, for some reason" + name = "projectile gun" + icon_state = "pistol" + origin_tech = "combat=2;materials=2" + w_class = WEIGHT_CLASS_NORMAL + materials = list(MAT_METAL=1000) + + var/mag_type = /obj/item/ammo_box/magazine/m10mm //Removes the need for max_ammo and caliber info + var/obj/item/ammo_box/magazine/magazine + var/can_tactical = FALSE //check to see if the gun can tactically reload + +/obj/item/gun/projectile/New() + ..() + if(!magazine) + magazine = new mag_type(src) + chamber_round() + update_icon() + return + +/obj/item/gun/projectile/update_icon() + ..() + if(current_skin) + icon_state = "[current_skin][suppressed ? "-suppressed" : ""][sawn_state ? "-sawn" : ""]" + else + icon_state = "[initial(icon_state)][suppressed ? "-suppressed" : ""][sawn_state ? "-sawn" : ""]" + if(bayonet && can_bayonet) + overlays += knife_overlay + +/obj/item/gun/projectile/process_chamber(eject_casing = 1, empty_chamber = 1) + var/obj/item/ammo_casing/AC = chambered //Find chambered round + if(isnull(AC) || !istype(AC)) + chamber_round() + return + if(eject_casing) + AC.loc = get_turf(src) //Eject casing onto ground. + AC.SpinAnimation(10, 1) //next gen special effects + playsound(src, chambered.drop_sound, 100, 1) + if(empty_chamber) + chambered = null + chamber_round() + return + +/obj/item/gun/projectile/proc/chamber_round() + if(chambered || !magazine) + return + else if(magazine.ammo_count()) + chambered = magazine.get_round() + chambered.loc = src + return + +/obj/item/gun/projectile/can_shoot() + if(!magazine || !magazine.ammo_count(0)) + return 0 + return 1 + +/obj/item/gun/projectile/proc/can_reload() + return !magazine + +/obj/item/gun/projectile/proc/reload(obj/item/ammo_box/magazine/AM, mob/user as mob) + user.remove_from_mob(AM) + magazine = AM + magazine.loc = src + playsound(src, magin_sound, 50, 1) + chamber_round() + AM.update_icon() + update_icon() + return + +/obj/item/gun/projectile/attackby(var/obj/item/A as obj, mob/user as mob, params) + if(istype(A, /obj/item/ammo_box/magazine)) + var/obj/item/ammo_box/magazine/AM = A + if(istype(AM, mag_type)) + if(can_reload()) + reload(AM, user) + to_chat(user, "You load a new magazine into \the [src].") + return TRUE + else if(!can_tactical) + to_chat(user, "There's already a magazine in \the [src].") + return TRUE + else + to_chat(user, "You perform a tactical reload on \the [src], replacing the magazine.") + magazine.loc = get_turf(loc) + magazine.update_icon() + magazine = null + reload(AM, user) + return TRUE + else + to_chat(user, "You can't put this type of ammo in \the [src].") + return TRUE + if(istype(A, /obj/item/suppressor)) + var/obj/item/suppressor/S = A + if(can_suppress) + if(!suppressed) + if(!user.unEquip(A)) + return + to_chat(user, "You screw [S] onto [src].") + suppressed = A + S.oldsound = fire_sound + S.initial_w_class = w_class + fire_sound = 'sound/weapons/gunshots/gunshot_silenced.ogg' + w_class = WEIGHT_CLASS_NORMAL //so pistols do not fit in pockets when suppressed + A.loc = src + update_icon() + return + else + to_chat(user, "[src] already has a suppressor.") + return + else + to_chat(user, "You can't seem to figure out how to fit [S] on [src].") + return + else + return ..() + +/obj/item/gun/projectile/attack_hand(mob/user) + if(loc == user) + if(suppressed && can_unsuppress) + var/obj/item/suppressor/S = suppressed + if(user.l_hand != src && user.r_hand != src) + ..() + return + to_chat(user, "You unscrew [suppressed] from [src].") + user.put_in_hands(suppressed) + fire_sound = S.oldsound + w_class = S.initial_w_class + suppressed = 0 + update_icon() + return + ..() + +/obj/item/gun/projectile/attack_self(mob/living/user as mob) + var/obj/item/ammo_casing/AC = chambered //Find chambered round + if(magazine) + magazine.loc = get_turf(loc) + user.put_in_hands(magazine) + magazine.update_icon() + magazine = null + to_chat(user, "You pull the magazine out of \the [src]!") + playsound(src, magout_sound, 50, 1) + else if(chambered) + AC.loc = get_turf(src) + AC.SpinAnimation(10, 1) + chambered = null + to_chat(user, "You unload the round from \the [src]'s chamber.") + playsound(src, 'sound/weapons/gun_interactions/remove_bullet.ogg', 50, 1) + else + to_chat(user, "There's no magazine in \the [src].") + update_icon() + return + +/obj/item/gun/projectile/examine(mob/user) + . = ..() + . += "Has [get_ammo()] round\s remaining." + +/obj/item/gun/projectile/proc/get_ammo(countchambered = 1) + var/boolets = 0 //mature var names for mature people + if(chambered && countchambered) + boolets++ + if(magazine) + boolets += magazine.ammo_count() + return boolets + +/obj/item/gun/projectile/suicide_act(mob/user) + if(chambered && chambered.BB && !chambered.BB.nodamage) + user.visible_message("[user] is putting the barrel of the [name] in [user.p_their()] mouth. It looks like [user.p_theyre()] trying to commit suicide.") + sleep(25) + if(user.l_hand == src || user.r_hand == src) + process_fire(user, user, 0, zone_override = "head") + user.visible_message("[user] blows [user.p_their()] brains out with the [name]!") + return BRUTELOSS + else + user.visible_message("[user] panics and starts choking to death!") + return OXYLOSS + else + user.visible_message("[user] is pretending to blow [user.p_their()] brains out with the [name]! It looks like [user.p_theyre()] trying to commit suicide!") + playsound(loc, 'sound/weapons/empty.ogg', 50, 1, -1) + return OXYLOSS + +/obj/item/gun/projectile/proc/sawoff(mob/user) + if(sawn_state == SAWN_OFF) + to_chat(user, "\The [src] is already shortened!") + return + if(bayonet) + to_chat(user, "You cannot saw-off [src] with [bayonet] attached!") + return + user.changeNext_move(CLICK_CD_MELEE) + user.visible_message("[user] begins to shorten \the [src].", "You begin to shorten \the [src]...") + + //if there's any live ammo inside the gun, makes it go off + if(blow_up(user)) + user.visible_message("\The [src] goes off!", "\The [src] goes off in your face!") + return + + if(do_after(user, 30, target = src)) + if(sawn_state == SAWN_OFF) + return + user.visible_message("[user] shortens \the [src]!", "You shorten \the [src].") + name = "sawn-off [name]" + desc = sawn_desc + w_class = WEIGHT_CLASS_NORMAL + item_state = "gun"//phil235 is it different with different skin? + slot_flags &= ~SLOT_BACK //you can't sling it on your back + slot_flags |= SLOT_BELT //but you can wear it on your belt (poorly concealed under a trenchcoat, ideally) + sawn_state = SAWN_OFF + update_icon() + return 1 + +// Sawing guns related proc +/obj/item/gun/projectile/proc/blow_up(mob/user) + . = 0 + for(var/obj/item/ammo_casing/AC in magazine.stored_ammo) + if(AC.BB) + process_fire(user, user,0) + . = 1 diff --git a/code/modules/projectiles/guns/projectile/automatic.dm b/code/modules/projectiles/guns/projectile/automatic.dm index 6601f75d52b..f2ce54bbd52 100644 --- a/code/modules/projectiles/guns/projectile/automatic.dm +++ b/code/modules/projectiles/guns/projectile/automatic.dm @@ -1,306 +1,306 @@ -/obj/item/gun/projectile/automatic - w_class = WEIGHT_CLASS_NORMAL - var/alarmed = 0 - var/select = 1 - can_tactical = TRUE - can_suppress = 1 - burst_size = 3 - fire_delay = 2 - actions_types = list(/datum/action/item_action/toggle_firemode) - -/obj/item/gun/projectile/automatic/isHandgun() - return 0 - -/obj/item/gun/projectile/automatic/update_icon() - ..() - overlays.Cut() - if(!select) - overlays += "[initial(icon_state)]semi" - if(select == 1) - overlays += "[initial(icon_state)]burst" - icon_state = "[initial(icon_state)][magazine ? "-[magazine.max_ammo]" : ""][chambered ? "" : "-e"][suppressed ? "-suppressed" : ""]" - if(bayonet && can_bayonet) - overlays += knife_overlay - -/obj/item/gun/projectile/automatic/attackby(var/obj/item/A as obj, mob/user as mob, params) - . = ..() - if(.) - if(alarmed) // Did the empty clip alarm go off already? - alarmed = 0 // Reset the alarm once a magazine is loaded - return - if(istype(A, /obj/item/ammo_box/magazine)) - var/obj/item/ammo_box/magazine/AM = A - if(istype(AM, mag_type)) - if(magazine) - to_chat(user, "You perform a tactical reload on \the [src], replacing the magazine.") - magazine.loc = get_turf(loc) - magazine.update_icon() - magazine = null - else - to_chat(user, "You insert the magazine into \the [src].") - if(alarmed) - alarmed = 0 - user.remove_from_mob(AM) - magazine = AM - magazine.loc = src - chamber_round() - A.update_icon() - update_icon() - return 1 - -/obj/item/gun/projectile/automatic/ui_action_click() - burst_select() - -/obj/item/gun/projectile/automatic/proc/burst_select() - var/mob/living/carbon/human/user = usr - select = !select - if(!select) - burst_size = 1 - fire_delay = 0 - to_chat(user, "You switch to semi-automatic.") - else - burst_size = initial(burst_size) - fire_delay = initial(fire_delay) - to_chat(user, "You switch to [burst_size] round burst.") - - playsound(user, 'sound/weapons/gun_interactions/selector.ogg', 100, 1) - update_icon() - for(var/X in actions) - var/datum/action/A = X - A.UpdateButtonIcon() - -/obj/item/gun/projectile/automatic/can_shoot() - return get_ammo() - -/obj/item/gun/projectile/automatic/proc/empty_alarm() - if(!chambered && !get_ammo() && !alarmed) - playsound(loc, 'sound/weapons/smg_empty_alarm.ogg', 40, 1) - update_icon() - alarmed = 1 - -//Saber SMG// -/obj/item/gun/projectile/automatic/proto - name = "\improper Nanotrasen Saber SMG" - desc = "A prototype three-round burst 9mm submachine gun, designated 'SABR'. Has a threaded barrel for suppressors." - icon_state = "saber" - mag_type = /obj/item/ammo_box/magazine/smgm9mm - origin_tech = "combat=4;materials=2" - fire_sound = 'sound/weapons/gunshots/gunshot_pistol.ogg' - -//C-20r SMG// -/obj/item/gun/projectile/automatic/c20r - name = "\improper C-20r SMG" - desc = "A two-round burst .45 SMG, designated 'C-20r'. Has a 'Scarborough Arms - Per falcis, per pravitas' buttstamp." - icon_state = "c20r" - item_state = "c20r" - origin_tech = "combat=5;materials=2;syndicate=6" - mag_type = /obj/item/ammo_box/magazine/smgm45 - fire_sound = 'sound/weapons/gunshots/gunshot_smg.ogg' - fire_delay = 2 - burst_size = 2 - can_bayonet = TRUE - knife_x_offset = 26 - knife_y_offset = 12 - -/obj/item/gun/projectile/automatic/c20r/New() - ..() - update_icon() - -/obj/item/gun/projectile/automatic/c20r/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, flag) - ..() - empty_alarm() - -/obj/item/gun/projectile/automatic/c20r/update_icon() - ..() - icon_state = "c20r[magazine ? "-[Ceiling(get_ammo(0)/4)*4]" : ""][chambered ? "" : "-e"][suppressed ? "-suppressed" : ""]" - -//WT550// -/obj/item/gun/projectile/automatic/wt550 - name = "security auto rifle" - desc = "An outdated personal defense weapon utilized by law enforcement. The WT-550 Automatic Rifle fires 4.6x30mm rounds." - icon_state = "wt550" - item_state = "arg" - mag_type = /obj/item/ammo_box/magazine/wt550m9 - fire_sound = 'sound/weapons/gunshots/gunshot_rifle.ogg' - magin_sound = 'sound/weapons/gun_interactions/batrifle_magin.ogg' - magout_sound = 'sound/weapons/gun_interactions/batrifle_magout.ogg' - fire_delay = 2 - can_suppress = 0 - burst_size = 1 - actions_types = list() - can_bayonet = TRUE - knife_x_offset = 25 - knife_y_offset = 12 - -/obj/item/gun/projectile/automatic/wt550/update_icon() - ..() - icon_state = "wt550[magazine ? "-[Ceiling(get_ammo(0)/4)*4]" : ""]" - -//Type-U3 Uzi// -/obj/item/gun/projectile/automatic/mini_uzi - name = "\improper 'Type U3' Uzi" - desc = "A lightweight, burst-fire submachine gun, for when you really want someone dead. Uses 9mm rounds." - icon_state = "mini-uzi" - origin_tech = "combat=4;materials=2;syndicate=4" - mag_type = /obj/item/ammo_box/magazine/uzim9mm - fire_sound = 'sound/weapons/gunshots/gunshot_pistol.ogg' - burst_size = 2 - -//M-90gl Carbine// -/obj/item/gun/projectile/automatic/m90 - name = "\improper M-90gl Carbine" - desc = "A three-round burst 5.56 toploading carbine, designated 'M-90gl'. Has an attached underbarrel grenade launcher which can be toggled on and off." - icon_state = "m90" - item_state = "m90-4" - origin_tech = "combat=5;materials=2;syndicate=6" - mag_type = /obj/item/ammo_box/magazine/m556 - fire_sound = 'sound/weapons/gunshots/gunshot_rifle.ogg' - magin_sound = 'sound/weapons/gun_interactions/batrifle_magin.ogg' - magout_sound = 'sound/weapons/gun_interactions/batrifle_magout.ogg' - can_suppress = 0 - var/obj/item/gun/projectile/revolver/grenadelauncher/underbarrel - burst_size = 3 - fire_delay = 2 - -/obj/item/gun/projectile/automatic/m90/New() - ..() - underbarrel = new /obj/item/gun/projectile/revolver/grenadelauncher(src) - update_icon() - -/obj/item/gun/projectile/automatic/m90/afterattack(var/atom/target, var/mob/living/user, flag, params) - if(select == 2) - underbarrel.afterattack(target, user, flag, params) - else - ..() - return - -/obj/item/gun/projectile/automatic/m90/attackby(var/obj/item/A, mob/user, params) - if(istype(A, /obj/item/ammo_casing)) - if(istype(A, underbarrel.magazine.ammo_type)) - underbarrel.attack_self() - underbarrel.attackby(A, user, params) - else - return ..() - -/obj/item/gun/projectile/automatic/m90/update_icon() - ..() - overlays.Cut() - switch(select) - if(0) - overlays += "[initial(icon_state)]semi" - if(1) - overlays += "[initial(icon_state)]burst" - if(2) - overlays += "[initial(icon_state)]gren" - icon_state = "[initial(icon_state)][magazine ? "" : "-e"]" - if(magazine) - overlays += image(icon = icon, icon_state = "m90-[Ceiling(get_ammo(0)/6)*6]") - item_state = "m90-[Ceiling(get_ammo(0)/7.5)]" - else - item_state = "m90-0" - return - -/obj/item/gun/projectile/automatic/m90/burst_select() - var/mob/living/carbon/human/user = usr - switch(select) - if(0) - select = 1 - burst_size = initial(burst_size) - fire_delay = initial(fire_delay) - to_chat(user, "You switch to [burst_size] round burst.") - if(1) - select = 2 - to_chat(user, "You switch to grenades.") - if(2) - select = 0 - burst_size = 1 - fire_delay = 0 - to_chat(user, "You switch to semi-auto.") - playsound(user, 'sound/weapons/gun_interactions/selector.ogg', 100, 1) - update_icon() - -//Tommy Gun// -/obj/item/gun/projectile/automatic/tommygun - name = "\improper Thompson SMG" - desc = "A genuine 'Chicago Typewriter'." - icon_state = "tommygun" - item_state = "shotgun" - w_class = WEIGHT_CLASS_HUGE - slot_flags = 0 - origin_tech = "combat=5;materials=1;syndicate=3" - mag_type = /obj/item/ammo_box/magazine/tommygunm45 - fire_sound = 'sound/weapons/gunshots/gunshot_smg.ogg' - can_suppress = 0 - burst_size = 4 - fire_delay = 1 - -//ARG Assault Rifle// -/obj/item/gun/projectile/automatic/ar - name = "ARG" - desc = "A robust assault rile used by Nanotrasen fighting forces." - icon_state = "arg" - item_state = "arg" - slot_flags = 0 - origin_tech = "combat=6;engineering=4" - mag_type = /obj/item/ammo_box/magazine/m556 - fire_sound = 'sound/weapons/gunshots/gunshot_mg.ogg' - magin_sound = 'sound/weapons/gun_interactions/batrifle_magin.ogg' - magout_sound = 'sound/weapons/gun_interactions/batrifle_magout.ogg' - can_suppress = 0 - burst_size = 3 - fire_delay = 1 - -// Bulldog shotgun // -/obj/item/gun/projectile/automatic/shotgun/bulldog - name = "\improper 'Bulldog' Shotgun" - desc = "A compact, mag-fed semi-automatic shotgun for combat in narrow corridors, nicknamed 'Bulldog' by boarding parties. Compatible only with specialized 8-round drum magazines." - icon_state = "bulldog" - item_state = "bulldog" - w_class = WEIGHT_CLASS_NORMAL - origin_tech = "combat=6;materials=4;syndicate=6" - mag_type = /obj/item/ammo_box/magazine/m12g - fire_sound = 'sound/weapons/gunshots/gunshot_shotgun.ogg' - magin_sound = 'sound/weapons/gun_interactions/batrifle_magin.ogg' - magout_sound = 'sound/weapons/gun_interactions/batrifle_magout.ogg' - can_suppress = 0 - burst_size = 1 - fire_delay = 0 - actions_types = list() - -/obj/item/gun/projectile/automatic/shotgun/bulldog/New() - ..() - update_icon() - -/obj/item/gun/projectile/automatic/shotgun/bulldog/proc/update_magazine() - if(magazine) - overlays.Cut() - overlays += "[magazine.icon_state]" - return - -/obj/item/gun/projectile/automatic/shotgun/bulldog/update_icon() - overlays.Cut() - update_magazine() - icon_state = "bulldog[chambered ? "" : "-e"]" - -/obj/item/gun/projectile/automatic/shotgun/bulldog/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, flag) - ..() - empty_alarm() - -//Laser carbine// -/obj/item/gun/projectile/automatic/lasercarbine - name = "\improper IK-60 Laser Carbine" - desc = "A short, compact carbine like rifle, relying more on battery cartridges rather than a built in power cell. Utilized by the Nanotrasen Navy for combat operations." - icon_state = "lasercarbine" - item_state = "laser" - w_class = WEIGHT_CLASS_NORMAL - origin_tech = "combat=4;materials=2" - mag_type = /obj/item/ammo_box/magazine/laser - fire_sound = 'sound/weapons/gunshots/gunshot_lascarbine.ogg' - magin_sound = 'sound/weapons/gun_interactions/batrifle_magin.ogg' - magout_sound = 'sound/weapons/gun_interactions/batrifle_magout.ogg' - can_suppress = 0 - burst_size = 2 - -/obj/item/gun/projectile/automatic/lasercarbine/update_icon() - ..() - icon_state = "lasercarbine[magazine ? "-[Ceiling(get_ammo(0)/5)*5]" : ""]" +/obj/item/gun/projectile/automatic + w_class = WEIGHT_CLASS_NORMAL + var/alarmed = 0 + var/select = 1 + can_tactical = TRUE + can_suppress = 1 + burst_size = 3 + fire_delay = 2 + actions_types = list(/datum/action/item_action/toggle_firemode) + +/obj/item/gun/projectile/automatic/isHandgun() + return 0 + +/obj/item/gun/projectile/automatic/update_icon() + ..() + overlays.Cut() + if(!select) + overlays += "[initial(icon_state)]semi" + if(select == 1) + overlays += "[initial(icon_state)]burst" + icon_state = "[initial(icon_state)][magazine ? "-[magazine.max_ammo]" : ""][chambered ? "" : "-e"][suppressed ? "-suppressed" : ""]" + if(bayonet && can_bayonet) + overlays += knife_overlay + +/obj/item/gun/projectile/automatic/attackby(var/obj/item/A as obj, mob/user as mob, params) + . = ..() + if(.) + if(alarmed) // Did the empty clip alarm go off already? + alarmed = 0 // Reset the alarm once a magazine is loaded + return + if(istype(A, /obj/item/ammo_box/magazine)) + var/obj/item/ammo_box/magazine/AM = A + if(istype(AM, mag_type)) + if(magazine) + to_chat(user, "You perform a tactical reload on \the [src], replacing the magazine.") + magazine.loc = get_turf(loc) + magazine.update_icon() + magazine = null + else + to_chat(user, "You insert the magazine into \the [src].") + if(alarmed) + alarmed = 0 + user.remove_from_mob(AM) + magazine = AM + magazine.loc = src + chamber_round() + A.update_icon() + update_icon() + return 1 + +/obj/item/gun/projectile/automatic/ui_action_click() + burst_select() + +/obj/item/gun/projectile/automatic/proc/burst_select() + var/mob/living/carbon/human/user = usr + select = !select + if(!select) + burst_size = 1 + fire_delay = 0 + to_chat(user, "You switch to semi-automatic.") + else + burst_size = initial(burst_size) + fire_delay = initial(fire_delay) + to_chat(user, "You switch to [burst_size] round burst.") + + playsound(user, 'sound/weapons/gun_interactions/selector.ogg', 100, 1) + update_icon() + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + +/obj/item/gun/projectile/automatic/can_shoot() + return get_ammo() + +/obj/item/gun/projectile/automatic/proc/empty_alarm() + if(!chambered && !get_ammo() && !alarmed) + playsound(loc, 'sound/weapons/smg_empty_alarm.ogg', 40, 1) + update_icon() + alarmed = 1 + +//Saber SMG// +/obj/item/gun/projectile/automatic/proto + name = "\improper Nanotrasen Saber SMG" + desc = "A prototype three-round burst 9mm submachine gun, designated 'SABR'. Has a threaded barrel for suppressors." + icon_state = "saber" + mag_type = /obj/item/ammo_box/magazine/smgm9mm + origin_tech = "combat=4;materials=2" + fire_sound = 'sound/weapons/gunshots/gunshot_pistol.ogg' + +//C-20r SMG// +/obj/item/gun/projectile/automatic/c20r + name = "\improper C-20r SMG" + desc = "A two-round burst .45 SMG, designated 'C-20r'. Has a 'Scarborough Arms - Per falcis, per pravitas' buttstamp." + icon_state = "c20r" + item_state = "c20r" + origin_tech = "combat=5;materials=2;syndicate=6" + mag_type = /obj/item/ammo_box/magazine/smgm45 + fire_sound = 'sound/weapons/gunshots/gunshot_smg.ogg' + fire_delay = 2 + burst_size = 2 + can_bayonet = TRUE + knife_x_offset = 26 + knife_y_offset = 12 + +/obj/item/gun/projectile/automatic/c20r/New() + ..() + update_icon() + +/obj/item/gun/projectile/automatic/c20r/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, flag) + ..() + empty_alarm() + +/obj/item/gun/projectile/automatic/c20r/update_icon() + ..() + icon_state = "c20r[magazine ? "-[Ceiling(get_ammo(0)/4)*4]" : ""][chambered ? "" : "-e"][suppressed ? "-suppressed" : ""]" + +//WT550// +/obj/item/gun/projectile/automatic/wt550 + name = "security auto rifle" + desc = "An outdated personal defense weapon utilized by law enforcement. The WT-550 Automatic Rifle fires 4.6x30mm rounds." + icon_state = "wt550" + item_state = "arg" + mag_type = /obj/item/ammo_box/magazine/wt550m9 + fire_sound = 'sound/weapons/gunshots/gunshot_rifle.ogg' + magin_sound = 'sound/weapons/gun_interactions/batrifle_magin.ogg' + magout_sound = 'sound/weapons/gun_interactions/batrifle_magout.ogg' + fire_delay = 2 + can_suppress = 0 + burst_size = 1 + actions_types = list() + can_bayonet = TRUE + knife_x_offset = 25 + knife_y_offset = 12 + +/obj/item/gun/projectile/automatic/wt550/update_icon() + ..() + icon_state = "wt550[magazine ? "-[Ceiling(get_ammo(0)/4)*4]" : ""]" + +//Type-U3 Uzi// +/obj/item/gun/projectile/automatic/mini_uzi + name = "\improper 'Type U3' Uzi" + desc = "A lightweight, burst-fire submachine gun, for when you really want someone dead. Uses 9mm rounds." + icon_state = "mini-uzi" + origin_tech = "combat=4;materials=2;syndicate=4" + mag_type = /obj/item/ammo_box/magazine/uzim9mm + fire_sound = 'sound/weapons/gunshots/gunshot_pistol.ogg' + burst_size = 2 + +//M-90gl Carbine// +/obj/item/gun/projectile/automatic/m90 + name = "\improper M-90gl Carbine" + desc = "A three-round burst 5.56 toploading carbine, designated 'M-90gl'. Has an attached underbarrel grenade launcher which can be toggled on and off." + icon_state = "m90" + item_state = "m90-4" + origin_tech = "combat=5;materials=2;syndicate=6" + mag_type = /obj/item/ammo_box/magazine/m556 + fire_sound = 'sound/weapons/gunshots/gunshot_rifle.ogg' + magin_sound = 'sound/weapons/gun_interactions/batrifle_magin.ogg' + magout_sound = 'sound/weapons/gun_interactions/batrifle_magout.ogg' + can_suppress = 0 + var/obj/item/gun/projectile/revolver/grenadelauncher/underbarrel + burst_size = 3 + fire_delay = 2 + +/obj/item/gun/projectile/automatic/m90/New() + ..() + underbarrel = new /obj/item/gun/projectile/revolver/grenadelauncher(src) + update_icon() + +/obj/item/gun/projectile/automatic/m90/afterattack(var/atom/target, var/mob/living/user, flag, params) + if(select == 2) + underbarrel.afterattack(target, user, flag, params) + else + ..() + return + +/obj/item/gun/projectile/automatic/m90/attackby(var/obj/item/A, mob/user, params) + if(istype(A, /obj/item/ammo_casing)) + if(istype(A, underbarrel.magazine.ammo_type)) + underbarrel.attack_self() + underbarrel.attackby(A, user, params) + else + return ..() + +/obj/item/gun/projectile/automatic/m90/update_icon() + ..() + overlays.Cut() + switch(select) + if(0) + overlays += "[initial(icon_state)]semi" + if(1) + overlays += "[initial(icon_state)]burst" + if(2) + overlays += "[initial(icon_state)]gren" + icon_state = "[initial(icon_state)][magazine ? "" : "-e"]" + if(magazine) + overlays += image(icon = icon, icon_state = "m90-[Ceiling(get_ammo(0)/6)*6]") + item_state = "m90-[Ceiling(get_ammo(0)/7.5)]" + else + item_state = "m90-0" + return + +/obj/item/gun/projectile/automatic/m90/burst_select() + var/mob/living/carbon/human/user = usr + switch(select) + if(0) + select = 1 + burst_size = initial(burst_size) + fire_delay = initial(fire_delay) + to_chat(user, "You switch to [burst_size] round burst.") + if(1) + select = 2 + to_chat(user, "You switch to grenades.") + if(2) + select = 0 + burst_size = 1 + fire_delay = 0 + to_chat(user, "You switch to semi-auto.") + playsound(user, 'sound/weapons/gun_interactions/selector.ogg', 100, 1) + update_icon() + +//Tommy Gun// +/obj/item/gun/projectile/automatic/tommygun + name = "\improper Thompson SMG" + desc = "A genuine 'Chicago Typewriter'." + icon_state = "tommygun" + item_state = "shotgun" + w_class = WEIGHT_CLASS_HUGE + slot_flags = 0 + origin_tech = "combat=5;materials=1;syndicate=3" + mag_type = /obj/item/ammo_box/magazine/tommygunm45 + fire_sound = 'sound/weapons/gunshots/gunshot_smg.ogg' + can_suppress = 0 + burst_size = 4 + fire_delay = 1 + +//ARG Assault Rifle// +/obj/item/gun/projectile/automatic/ar + name = "ARG" + desc = "A robust assault rile used by Nanotrasen fighting forces." + icon_state = "arg" + item_state = "arg" + slot_flags = 0 + origin_tech = "combat=6;engineering=4" + mag_type = /obj/item/ammo_box/magazine/m556 + fire_sound = 'sound/weapons/gunshots/gunshot_mg.ogg' + magin_sound = 'sound/weapons/gun_interactions/batrifle_magin.ogg' + magout_sound = 'sound/weapons/gun_interactions/batrifle_magout.ogg' + can_suppress = 0 + burst_size = 3 + fire_delay = 1 + +// Bulldog shotgun // +/obj/item/gun/projectile/automatic/shotgun/bulldog + name = "\improper 'Bulldog' Shotgun" + desc = "A compact, mag-fed semi-automatic shotgun for combat in narrow corridors, nicknamed 'Bulldog' by boarding parties. Compatible only with specialized 8-round drum magazines." + icon_state = "bulldog" + item_state = "bulldog" + w_class = WEIGHT_CLASS_NORMAL + origin_tech = "combat=6;materials=4;syndicate=6" + mag_type = /obj/item/ammo_box/magazine/m12g + fire_sound = 'sound/weapons/gunshots/gunshot_shotgun.ogg' + magin_sound = 'sound/weapons/gun_interactions/batrifle_magin.ogg' + magout_sound = 'sound/weapons/gun_interactions/batrifle_magout.ogg' + can_suppress = 0 + burst_size = 1 + fire_delay = 0 + actions_types = list() + +/obj/item/gun/projectile/automatic/shotgun/bulldog/New() + ..() + update_icon() + +/obj/item/gun/projectile/automatic/shotgun/bulldog/proc/update_magazine() + if(magazine) + overlays.Cut() + overlays += "[magazine.icon_state]" + return + +/obj/item/gun/projectile/automatic/shotgun/bulldog/update_icon() + overlays.Cut() + update_magazine() + icon_state = "bulldog[chambered ? "" : "-e"]" + +/obj/item/gun/projectile/automatic/shotgun/bulldog/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, flag) + ..() + empty_alarm() + +//Laser carbine// +/obj/item/gun/projectile/automatic/lasercarbine + name = "\improper IK-60 Laser Carbine" + desc = "A short, compact carbine like rifle, relying more on battery cartridges rather than a built in power cell. Utilized by the Nanotrasen Navy for combat operations." + icon_state = "lasercarbine" + item_state = "laser" + w_class = WEIGHT_CLASS_NORMAL + origin_tech = "combat=4;materials=2" + mag_type = /obj/item/ammo_box/magazine/laser + fire_sound = 'sound/weapons/gunshots/gunshot_lascarbine.ogg' + magin_sound = 'sound/weapons/gun_interactions/batrifle_magin.ogg' + magout_sound = 'sound/weapons/gun_interactions/batrifle_magout.ogg' + can_suppress = 0 + burst_size = 2 + +/obj/item/gun/projectile/automatic/lasercarbine/update_icon() + ..() + icon_state = "lasercarbine[magazine ? "-[Ceiling(get_ammo(0)/5)*5]" : ""]" diff --git a/code/modules/projectiles/guns/projectile/pistol.dm b/code/modules/projectiles/guns/projectile/pistol.dm index 64d0c2d7a67..1314410cd36 100644 --- a/code/modules/projectiles/guns/projectile/pistol.dm +++ b/code/modules/projectiles/guns/projectile/pistol.dm @@ -1,118 +1,118 @@ -//Stetchkin// -/obj/item/gun/projectile/automatic/pistol - name = "stechkin pistol" - desc = "A small, easily concealable 10mm handgun. Has a threaded barrel for suppressors." - icon_state = "pistol" - w_class = WEIGHT_CLASS_SMALL - origin_tech = "combat=3;materials=2;syndicate=4" - mag_type = /obj/item/ammo_box/magazine/m10mm - fire_sound = 'sound/weapons/gunshots/gunshot_pistol.ogg' - magin_sound = 'sound/weapons/gun_interactions/pistol_magin.ogg' - magout_sound = 'sound/weapons/gun_interactions/pistol_magout.ogg' - can_suppress = 1 - burst_size = 1 - fire_delay = 0 - actions_types = list() - -/obj/item/gun/projectile/automatic/pistol/isHandgun() - return 1 - -/obj/item/gun/projectile/automatic/pistol/update_icon() - ..() - icon_state = "[initial(icon_state)][chambered ? "" : "-e"][suppressed ? "-suppressed" : ""]" - return - -//M1911// -/obj/item/gun/projectile/automatic/pistol/m1911 - name = "\improper M1911" - desc = "A classic .45 handgun with a small magazine capacity." - icon_state = "m1911" - w_class = WEIGHT_CLASS_NORMAL - mag_type = /obj/item/ammo_box/magazine/m45 - can_suppress = 0 - -//Enforcer// -/obj/item/gun/projectile/automatic/pistol/enforcer - name = "Enforcer" - desc = "A pistol of modern design." - icon_state = "enforcer_grey" - force = 10 - mag_type = /obj/item/ammo_box/magazine/enforcer - can_suppress = TRUE - unique_reskin = TRUE - can_flashlight = TRUE - -/obj/item/gun/projectile/automatic/pistol/enforcer/New() - ..() - options["Grey slide"] = "enforcer_grey" - options["Red slide"] = "enforcer_red" - options["Green slide"] = "enforcer_green" - options["Tan slide"] = "enforcer_tan" - options["Black slide"] = "enforcer_black" - options["Green Handle"] = "enforcer_greengrip" - options["Tan Handle"] = "enforcer_tangrip" - options["Red Handle"] = "enforcer_redgrip" - options["Cancel"] = null - -/obj/item/gun/projectile/automatic/pistol/enforcer/update_icon() - ..() - if(current_skin) - icon_state = "[current_skin][chambered ? "" : "-e"]" - else - icon_state = "[initial(icon_state)][chambered ? "" : "-e"]" - overlays.Cut() - if(suppressed) - overlays += image(icon = icon, icon_state = "enforcer_supp", pixel_x = 4) - if(gun_light) - var/iconF = "Enforcer_light" - if(gun_light.on) - iconF = "Enforcer_light-on" - overlays += image(icon = icon, icon_state = iconF, pixel_x = 0) - -/obj/item/gun/projectile/automatic/pistol/enforcer/ui_action_click() - toggle_gunlight() - -/obj/item/gun/projectile/automatic/pistol/enforcer/lethal - -/obj/item/gun/projectile/automatic/pistol/enforcer/lethal/New() - magazine = new/obj/item/ammo_box/magazine/enforcer/lethal - ..() - -//Desert Eagle// -/obj/item/gun/projectile/automatic/pistol/deagle - name = "desert eagle" - desc = "A robust .50 AE handgun." - icon_state = "deagle" - force = 14.0 - mag_type = /obj/item/ammo_box/magazine/m50 - fire_sound = 'sound/weapons/gunshots/gunshot_pistolH.ogg' - magin_sound = 'sound/weapons/gun_interactions/hpistol_magin.ogg' - magout_sound = 'sound/weapons/gun_interactions/hpistol_magout.ogg' - can_suppress = 0 - -/obj/item/gun/projectile/automatic/pistol/deagle/update_icon() - ..() - icon_state = "[initial(icon_state)][magazine ? "" : "-e"]" - -/obj/item/gun/projectile/automatic/pistol/deagle/gold - desc = "A gold plated desert eagle folded over a million times by superior martian gunsmiths. Uses .50 AE ammo." - icon_state = "deagleg" - item_state = "deagleg" - -/obj/item/gun/projectile/automatic/pistol/deagle/camo - desc = "A Deagle brand Deagle for operators operating operationally. Uses .50 AE ammo." - icon_state = "deaglecamo" - item_state = "deagleg" - -//APS Pistol// -/obj/item/gun/projectile/automatic/pistol/APS - name = "stechkin APS pistol" - desc = "The original russian version of a widely used Syndicate sidearm. Uses 9mm ammo." - icon_state = "aps" - w_class = WEIGHT_CLASS_NORMAL - origin_tech = "combat=3;materials=2;syndicate=3" - mag_type = /obj/item/ammo_box/magazine/pistolm9mm - can_suppress = 0 - burst_size = 3 - fire_delay = 2 - actions_types = list(/datum/action/item_action/toggle_firemode) +//Stetchkin// +/obj/item/gun/projectile/automatic/pistol + name = "stechkin pistol" + desc = "A small, easily concealable 10mm handgun. Has a threaded barrel for suppressors." + icon_state = "pistol" + w_class = WEIGHT_CLASS_SMALL + origin_tech = "combat=3;materials=2;syndicate=4" + mag_type = /obj/item/ammo_box/magazine/m10mm + fire_sound = 'sound/weapons/gunshots/gunshot_pistol.ogg' + magin_sound = 'sound/weapons/gun_interactions/pistol_magin.ogg' + magout_sound = 'sound/weapons/gun_interactions/pistol_magout.ogg' + can_suppress = 1 + burst_size = 1 + fire_delay = 0 + actions_types = list() + +/obj/item/gun/projectile/automatic/pistol/isHandgun() + return 1 + +/obj/item/gun/projectile/automatic/pistol/update_icon() + ..() + icon_state = "[initial(icon_state)][chambered ? "" : "-e"][suppressed ? "-suppressed" : ""]" + return + +//M1911// +/obj/item/gun/projectile/automatic/pistol/m1911 + name = "\improper M1911" + desc = "A classic .45 handgun with a small magazine capacity." + icon_state = "m1911" + w_class = WEIGHT_CLASS_NORMAL + mag_type = /obj/item/ammo_box/magazine/m45 + can_suppress = 0 + +//Enforcer// +/obj/item/gun/projectile/automatic/pistol/enforcer + name = "Enforcer" + desc = "A pistol of modern design." + icon_state = "enforcer_grey" + force = 10 + mag_type = /obj/item/ammo_box/magazine/enforcer + can_suppress = TRUE + unique_reskin = TRUE + can_flashlight = TRUE + +/obj/item/gun/projectile/automatic/pistol/enforcer/New() + ..() + options["Grey slide"] = "enforcer_grey" + options["Red slide"] = "enforcer_red" + options["Green slide"] = "enforcer_green" + options["Tan slide"] = "enforcer_tan" + options["Black slide"] = "enforcer_black" + options["Green Handle"] = "enforcer_greengrip" + options["Tan Handle"] = "enforcer_tangrip" + options["Red Handle"] = "enforcer_redgrip" + options["Cancel"] = null + +/obj/item/gun/projectile/automatic/pistol/enforcer/update_icon() + ..() + if(current_skin) + icon_state = "[current_skin][chambered ? "" : "-e"]" + else + icon_state = "[initial(icon_state)][chambered ? "" : "-e"]" + overlays.Cut() + if(suppressed) + overlays += image(icon = icon, icon_state = "enforcer_supp", pixel_x = 4) + if(gun_light) + var/iconF = "Enforcer_light" + if(gun_light.on) + iconF = "Enforcer_light-on" + overlays += image(icon = icon, icon_state = iconF, pixel_x = 0) + +/obj/item/gun/projectile/automatic/pistol/enforcer/ui_action_click() + toggle_gunlight() + +/obj/item/gun/projectile/automatic/pistol/enforcer/lethal + +/obj/item/gun/projectile/automatic/pistol/enforcer/lethal/New() + magazine = new/obj/item/ammo_box/magazine/enforcer/lethal + ..() + +//Desert Eagle// +/obj/item/gun/projectile/automatic/pistol/deagle + name = "desert eagle" + desc = "A robust .50 AE handgun." + icon_state = "deagle" + force = 14.0 + mag_type = /obj/item/ammo_box/magazine/m50 + fire_sound = 'sound/weapons/gunshots/gunshot_pistolH.ogg' + magin_sound = 'sound/weapons/gun_interactions/hpistol_magin.ogg' + magout_sound = 'sound/weapons/gun_interactions/hpistol_magout.ogg' + can_suppress = 0 + +/obj/item/gun/projectile/automatic/pistol/deagle/update_icon() + ..() + icon_state = "[initial(icon_state)][magazine ? "" : "-e"]" + +/obj/item/gun/projectile/automatic/pistol/deagle/gold + desc = "A gold plated desert eagle folded over a million times by superior martian gunsmiths. Uses .50 AE ammo." + icon_state = "deagleg" + item_state = "deagleg" + +/obj/item/gun/projectile/automatic/pistol/deagle/camo + desc = "A Deagle brand Deagle for operators operating operationally. Uses .50 AE ammo." + icon_state = "deaglecamo" + item_state = "deagleg" + +//APS Pistol// +/obj/item/gun/projectile/automatic/pistol/APS + name = "stechkin APS pistol" + desc = "The original russian version of a widely used Syndicate sidearm. Uses 9mm ammo." + icon_state = "aps" + w_class = WEIGHT_CLASS_NORMAL + origin_tech = "combat=3;materials=2;syndicate=3" + mag_type = /obj/item/ammo_box/magazine/pistolm9mm + can_suppress = 0 + burst_size = 3 + fire_delay = 2 + actions_types = list(/datum/action/item_action/toggle_firemode) diff --git a/code/modules/projectiles/guns/projectile/revolver.dm b/code/modules/projectiles/guns/projectile/revolver.dm index 21992f43704..642e0fabc3d 100644 --- a/code/modules/projectiles/guns/projectile/revolver.dm +++ b/code/modules/projectiles/guns/projectile/revolver.dm @@ -1,473 +1,475 @@ -/obj/item/gun/projectile/revolver - name = "\improper .357 revolver" - desc = "A suspicious revolver. Uses .357 ammo." - icon_state = "revolver" - mag_type = /obj/item/ammo_box/magazine/internal/cylinder - origin_tech = "combat=3;materials=2" - fire_sound = 'sound/weapons/gunshots/gunshot_strong.ogg' - -/obj/item/gun/projectile/revolver/New() - ..() - if(!istype(magazine, /obj/item/ammo_box/magazine/internal/cylinder)) - verbs -= /obj/item/gun/projectile/revolver/verb/spin - -/obj/item/gun/projectile/revolver/chamber_round(var/spin = 1) - if(spin) - chambered = magazine.get_round(1) - else - chambered = magazine.stored_ammo[1] - return - -/obj/item/gun/projectile/revolver/shoot_with_empty_chamber(mob/living/user as mob|obj) - ..() - chamber_round(1) - -/obj/item/gun/projectile/revolver/process_chamber() - return ..(0, 1) - -/obj/item/gun/projectile/revolver/attackby(obj/item/A, mob/user, params) - . = ..() - if(.) - return - var/num_loaded = magazine.attackby(A, user, params, 1) - if(num_loaded) - to_chat(user, "You load [num_loaded] shell\s into \the [src].") - A.update_icon() - update_icon() - chamber_round(0) - -/obj/item/gun/projectile/revolver/attack_self(mob/living/user) - var/num_unloaded = 0 - chambered = null - while(get_ammo() > 0) - var/obj/item/ammo_casing/CB - CB = magazine.get_round(0) - if(CB) - CB.loc = get_turf(loc) - CB.SpinAnimation(10, 1) - CB.update_icon() - playsound(get_turf(CB), "casingdrop", 60, 1) - num_unloaded++ - if(num_unloaded) - to_chat(user, "You unload [num_unloaded] shell\s from [src].") - else - to_chat(user, "[src] is empty!") - -/obj/item/gun/projectile/revolver/verb/spin() - set name = "Spin Chamber" - set category = "Object" - set desc = "Click to spin your revolver's chamber." - - var/mob/M = usr - - if(M.stat || !in_range(M,src)) - return - - if(istype(magazine, /obj/item/ammo_box/magazine/internal/cylinder)) - var/obj/item/ammo_box/magazine/internal/cylinder/C = magazine - C.spin() - chamber_round(0) - playsound(loc, 'sound/weapons/revolver_spin.ogg', 50, 1) - usr.visible_message("[usr] spins [src]'s chamber.", "You spin [src]'s chamber.") - else - verbs -= /obj/item/gun/projectile/revolver/verb/spin - -/obj/item/gun/projectile/revolver/can_shoot() - return get_ammo(0,0) - -/obj/item/gun/projectile/revolver/get_ammo(countchambered = 0, countempties = 1) - var/boolets = 0 //mature var names for mature people - if(chambered && countchambered) - boolets++ - if(magazine) - boolets += magazine.ammo_count(countempties) - return boolets - -/obj/item/gun/projectile/revolver/examine(mob/user) - . = ..() - . += "[get_ammo(0,0)] of those are live rounds." - -/obj/item/gun/projectile/revolver/detective - desc = "A cheap Martian knock-off of a classic law enforcement firearm. Uses .38-special rounds." - name = "\improper .38 Mars Special" - icon_state = "detective" - mag_type = /obj/item/ammo_box/magazine/internal/cylinder/rev38 - unique_rename = 1 - unique_reskin = 1 - -/obj/item/gun/projectile/revolver/detective/New() - ..() - options["The Original"] = "detective" - options["Leopard Spots"] = "detective_leopard" - options["Black Panther"] = "detective_panther" - options["Gold Trim"] = "detective_gold" - options["The Peacemaker"] = "detective_peacemaker" - options["Cancel"] = null - -/obj/item/gun/projectile/revolver/detective/process_fire(atom/target as mob|obj|turf, mob/living/user as mob|obj, message = 1, params, zone_override = "") - if(magazine.caliber != initial(magazine.caliber)) - if(prob(70 - (magazine.ammo_count() * 10))) //minimum probability of 10, maximum of 60 - playsound(user, fire_sound, 50, 1) - to_chat(user, "[src] blows up in your face!") - user.take_organ_damage(0,20) - user.unEquip(src) - return 0 - ..() - -/obj/item/gun/projectile/revolver/detective/attackby(obj/item/A, mob/user, params) - if(istype(A, /obj/item/screwdriver)) - if(magazine.caliber == "38") - to_chat(user, "You begin to reinforce the barrel of [src]...") - if(magazine.ammo_count()) - afterattack(user, user) //you know the drill - user.visible_message("[src] goes off!", "[src] goes off in your face!") - return - if(do_after(user, 30 * A.toolspeed, target = src)) - if(magazine.ammo_count()) - to_chat(user, "You can't modify it!") - return - magazine.caliber = "357" - desc = "The barrel and chamber assembly seems to have been modified." - to_chat(user, "You reinforce the barrel of [src]. Now it will fire .357 rounds.") - else - to_chat(user, "You begin to revert the modifications to [src]...") - if(magazine.ammo_count()) - afterattack(user, user) //and again - user.visible_message("[src] goes off!", "[src] goes off in your face!") - return - if(do_after(user, 30 * A.toolspeed, target = src)) - if(magazine.ammo_count()) - to_chat(user, "You can't modify it!") - return - magazine.caliber = "38" - desc = initial(desc) - to_chat(user, "You remove the modifications on [src]. Now it will fire .38 rounds.") - else - return ..() - -/obj/item/gun/projectile/revolver/fingergun //Summoned by the Finger Gun spell, from advanced mimery traitor item - name = "\improper finger gun" - desc = "Bang bang bang!" - icon_state = "fingergun" - mag_type = /obj/item/ammo_box/magazine/internal/cylinder/rev38/invisible - origin_tech = "" - flags = ABSTRACT | NODROP | DROPDEL - slot_flags = null - fire_sound = null - fire_sound_text = null - lefthand_file = null - righthand_file = null - clumsy_check = 0 //Stole your uplink! Honk! - needs_permit = 0 //go away beepsky - -/obj/item/gun/projectile/revolver/fingergun/fake - desc = "Pew pew pew!" - mag_type = /obj/item/ammo_box/magazine/internal/cylinder/rev38/invisible/fake - -/obj/item/gun/projectile/revolver/fingergun/New() - ..() - verbs -= /obj/item/gun/projectile/revolver/verb/spin - -/obj/item/gun/projectile/revolver/fingergun/shoot_with_empty_chamber(/*mob/living/user as mob|obj*/) - to_chat(usr, "You are out of ammo! You holster your fingers.") - qdel(src) - return - -/obj/item/gun/projectile/revolver/fingergun/afterattack(atom/target, mob/living/user, flag, params) - if(!user.mind.miming) - to_chat(usr, "You must dedicate yourself to silence first. Use your fingers if you wish to holster them.") - return - ..() - -/obj/item/gun/projectile/revolver/fingergun/attackby(obj/item/A, mob/user, params) - return - -/obj/item/gun/projectile/revolver/fingergun/attack_self(mob/living/user) - to_chat(usr, "You holster your fingers. Another time.") - qdel(src) - return - -/obj/item/gun/projectile/revolver/mateba - name = "\improper Unica 6 auto-revolver" - desc = "A retro high-powered autorevolver typically used by officers of the New Russia military. Uses .357 ammo." //>10mm hole >.357 - icon_state = "mateba" - -/obj/item/gun/projectile/revolver/golden - name = "\improper Golden revolver" - desc = "This ain't no game, ain't never been no show, And I'll gladly gun down the oldest lady you know. Uses .357 ammo." - icon_state = "goldrevolver" - fire_sound = 'sound/weapons/resonator_blast.ogg' - recoil = 8 - -/obj/item/gun/projectile/revolver/nagant - name = "nagant revolver" - desc = "An old model of revolver that originated in Russia. Able to be suppressed. Uses 7.62x38mmR ammo." - icon_state = "nagant" - origin_tech = "combat=3" - can_suppress = 1 - mag_type = /obj/item/ammo_box/magazine/internal/cylinder/rev762 - -// A gun to play Russian Roulette! -// You can spin the chamber to randomize the position of the bullet. - -/obj/item/gun/projectile/revolver/russian - name = "\improper Russian Revolver" - desc = "A Russian-made revolver for drinking games. Uses .357 ammo, and has a mechanism that spins the chamber before each trigger pull." - origin_tech = "combat=2;materials=2" - mag_type = /obj/item/ammo_box/magazine/internal/rus357 - var/spun = 0 - - -/obj/item/gun/projectile/revolver/russian/New() - ..() - Spin() - update_icon() - -/obj/item/gun/projectile/revolver/russian/proc/Spin() - chambered = null - var/random = rand(1, magazine.max_ammo) - if(random <= get_ammo(0,0)) - chamber_round() - spun = 1 - -/obj/item/gun/projectile/revolver/russian/attackby(obj/item/A, mob/user, params) - var/num_loaded = ..() - if(num_loaded) - user.visible_message("[user] loads a single bullet into the revolver and spins the chamber.", "You load a single bullet into the chamber and spin it.") - else - user.visible_message("[user] spins the chamber of the revolver.", "You spin the revolver's chamber.") - if(get_ammo() > 0) - Spin() - update_icon() - A.update_icon() - return - -/obj/item/gun/projectile/revolver/russian/attack_self(mob/user) - if(!spun && can_shoot()) - user.visible_message("[user] spins the chamber of the revolver.", "You spin the revolver's chamber.") - Spin() - else - var/num_unloaded = 0 - while(get_ammo() > 0) - var/obj/item/ammo_casing/CB - CB = magazine.get_round() - chambered = null - CB.loc = get_turf(loc) - CB.update_icon() - playsound(get_turf(CB), "casingdrop", 60, 1) - num_unloaded++ - if(num_unloaded) - to_chat(user, "You unload [num_unloaded] shell\s from [src].") - else - to_chat(user, "[src] is empty.") - -/obj/item/gun/projectile/revolver/russian/afterattack(atom/target as mob|obj|turf, mob/living/user as mob|obj, flag, params) - if(flag) - if(!(target in user.contents) && ismob(target)) - if(user.a_intent == INTENT_HARM) // Flogging action - return - - if(isliving(user)) - if(!can_trigger_gun(user)) - return - if(target != user) - if(ismob(target)) - to_chat(user, "A mechanism prevents you from shooting anyone but yourself!") - return - - if(ishuman(user)) - if(!spun) - to_chat(user, "You need to spin the revolver's chamber first!") - return - - spun = 0 - - if(chambered) - var/obj/item/ammo_casing/AC = chambered - if(AC.fire(user, user)) - playsound(user, fire_sound, 50, 1) - var/zone = check_zone(user.zone_selected) - if(zone == "head" || zone == "eyes" || zone == "mouth") - shoot_self(user, zone) - else - user.visible_message("[user.name] cowardly fires [src] at [user.p_their()] [zone]!", "You cowardly fire [src] at your [zone]!", "You hear a gunshot!") - return - - user.visible_message("*click*") - playsound(user, 'sound/weapons/empty.ogg', 100, 1) - -/obj/item/gun/projectile/revolver/russian/proc/shoot_self(mob/living/carbon/human/user, affecting = "head") - user.apply_damage(300, BRUTE, affecting) - user.visible_message("[user.name] fires [src] at [user.p_their()] head!", "You fire [src] at your head!", "You hear a gunshot!") - -/obj/item/gun/projectile/revolver/russian/soul - name = "cursed Russian revolver" - desc = "To play with this revolver requires wagering your very soul." - -/obj/item/gun/projectile/revolver/russian/soul/shoot_self(mob/living/user) - ..() - var/obj/item/soulstone/anybody/SS = new /obj/item/soulstone/anybody(get_turf(src)) - if(!SS.transfer_soul("FORCE", user)) //Something went wrong - qdel(SS) - return - user.visible_message("[user.name]'s soul is captured by \the [src]!", "You've lost the gamble! Your soul is forfeit!") - -/obj/item/gun/projectile/revolver/capgun - name = "cap gun" - desc = "Looks almost like the real thing! Ages 8 and up." - origin_tech = null - mag_type = /obj/item/ammo_box/magazine/internal/cylinder/cap - -///////////////////////////// -// DOUBLE BARRELED SHOTGUN // -///////////////////////////// - -/obj/item/gun/projectile/revolver/doublebarrel - name = "double-barreled shotgun" - desc = "A true classic." - icon_state = "dshotgun" - item_state = "shotgun" - w_class = WEIGHT_CLASS_BULKY - force = 10 - flags = CONDUCT - slot_flags = SLOT_BACK - mag_type = /obj/item/ammo_box/magazine/internal/shot/dual - fire_sound = 'sound/weapons/gunshots/gunshot_shotgun.ogg' - sawn_desc = "Omar's coming!" - unique_rename = 1 - unique_reskin = 1 - -/obj/item/gun/projectile/revolver/doublebarrel/New() - ..() - options["Default"] = "dshotgun" - options["Dark Red Finish"] = "dshotgun-d" - options["Ash"] = "dshotgun-f" - options["Faded Grey"] = "dshotgun-g" - options["Maple"] = "dshotgun-l" - options["Rosewood"] = "dshotgun-p" - options["Cancel"] = null - -/obj/item/gun/projectile/revolver/doublebarrel/attackby(obj/item/A, mob/user, params) - if(istype(A, /obj/item/ammo_box) || istype(A, /obj/item/ammo_casing)) - chamber_round() - if(istype(A, /obj/item/melee/energy)) - var/obj/item/melee/energy/W = A - if(W.active) - sawoff(user) - if(istype(A, /obj/item/circular_saw) || istype(A, /obj/item/gun/energy/plasmacutter)) - sawoff(user) - else - return ..() - -/obj/item/gun/projectile/revolver/doublebarrel/attack_self(mob/living/user) - var/num_unloaded = 0 - while(get_ammo() > 0) - var/obj/item/ammo_casing/CB - CB = magazine.get_round(0) - chambered = null - CB.loc = get_turf(loc) - CB.SpinAnimation(10, 1) - CB.update_icon() - playsound(get_turf(CB), 'sound/weapons/gun_interactions/shotgun_fall.ogg', 70, 1) - num_unloaded++ - if(num_unloaded) - to_chat(user, "You break open \the [src] and unload [num_unloaded] shell\s.") - else - to_chat(user, "[src] is empty.") - -/obj/item/gun/projectile/revolver/doublebarrel/isHandgun() //contrary to popular opinion, double barrels are not, shockingly, handguns - return 0 - -// IMPROVISED SHOTGUN // - -/obj/item/gun/projectile/revolver/doublebarrel/improvised - name = "improvised shotgun" - desc = "Essentially a tube that aims shotgun shells." - icon_state = "ishotgun" - item_state = "shotgun" - w_class = WEIGHT_CLASS_BULKY - force = 10 - slot_flags = null - mag_type = /obj/item/ammo_box/magazine/internal/shot/improvised - fire_sound = 'sound/weapons/gunshots/gunshot_shotgun.ogg' - sawn_desc = "I'm just here for the gasoline." - unique_rename = 0 - unique_reskin = 0 - var/slung = 0 - -/obj/item/gun/projectile/revolver/doublebarrel/improvised/attackby(obj/item/A, mob/user, params) - if(istype(A, /obj/item/stack/cable_coil) && !sawn_state) - var/obj/item/stack/cable_coil/C = A - if(C.use(10)) - slot_flags = SLOT_BACK - icon_state = "ishotgunsling" - to_chat(user, "You tie the lengths of cable to the shotgun, making a sling.") - slung = 1 - update_icon() - else - to_chat(user, "You need at least ten lengths of cable if you want to make a sling.") - return - else - return ..() - -/obj/item/gun/projectile/revolver/doublebarrel/improvised/update_icon() - ..() - if(slung && (slot_flags & SLOT_BELT) ) - slung = 0 - icon_state = "ishotgun-sawn" - -/obj/item/gun/projectile/revolver/doublebarrel/improvised/sawoff(mob/user) - . = ..() - if(. && slung) //sawing off the gun removes the sling - new /obj/item/stack/cable_coil(get_turf(src), 10) - slung = 0 - update_icon() - -//caneshotgun - -/obj/item/gun/projectile/revolver/doublebarrel/improvised/cane - name = "cane" - desc = "A cane used by a true gentleman. Or a clown." - icon = 'icons/obj/items.dmi' - lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' - righthand_file = 'icons/mob/inhands/items_righthand.dmi' - icon_state = "cane" - item_state = "stick" - sawn_state = SAWN_OFF - w_class = WEIGHT_CLASS_SMALL - force = 10 - can_unsuppress = 0 - slot_flags = null - origin_tech = "" // NO GIVAWAYS - mag_type = /obj/item/ammo_box/magazine/internal/shot/improvised/cane - sawn_desc = "I'm sorry, but why did you saw your cane in the first place?" - attack_verb = list("bludgeoned", "whacked", "disciplined", "thrashed") - fire_sound = 'sound/weapons/gunshots/gunshot_silenced.ogg' - suppressed = 1 - needs_permit = 0 //its just a cane beepsky..... - -/obj/item/gun/projectile/revolver/doublebarrel/improvised/cane/is_crutch() - return 1 - -/obj/item/gun/projectile/revolver/doublebarrel/improvised/cane/update_icon() - return - -/obj/item/gun/projectile/revolver/doublebarrel/improvised/cane/attackby(obj/item/A, mob/user, params) - if(istype(A, /obj/item/stack/cable_coil)) - return - else - return ..() - -/obj/item/gun/projectile/revolver/doublebarrel/improvised/cane/examine(mob/user) // HAD TO REPEAT EXAMINE CODE BECAUSE GUN CODE DOESNT STEALTH - var/f_name = "\a [src]." - if(blood_DNA && !istype(src, /obj/effect/decal)) - if(gender == PLURAL) - f_name = "some " - else - f_name = "a " - f_name += "blood-stained [name]!" - - . = list("[bicon(src)] That's [f_name]") - - if(desc) - . += desc +/obj/item/gun/projectile/revolver + name = "\improper .357 revolver" + desc = "A suspicious revolver. Uses .357 ammo." + icon_state = "revolver" + mag_type = /obj/item/ammo_box/magazine/internal/cylinder + origin_tech = "combat=3;materials=2" + fire_sound = 'sound/weapons/gunshots/gunshot_strong.ogg' + +/obj/item/gun/projectile/revolver/New() + ..() + if(!istype(magazine, /obj/item/ammo_box/magazine/internal/cylinder)) + verbs -= /obj/item/gun/projectile/revolver/verb/spin + +/obj/item/gun/projectile/revolver/chamber_round(var/spin = 1) + if(spin) + chambered = magazine.get_round(1) + else + chambered = magazine.stored_ammo[1] + return + +/obj/item/gun/projectile/revolver/shoot_with_empty_chamber(mob/living/user as mob|obj) + ..() + chamber_round(1) + +/obj/item/gun/projectile/revolver/process_chamber() + return ..(0, 1) + +/obj/item/gun/projectile/revolver/attackby(obj/item/A, mob/user, params) + . = ..() + if(.) + return + var/num_loaded = magazine.attackby(A, user, params, 1) + if(num_loaded) + to_chat(user, "You load [num_loaded] shell\s into \the [src].") + A.update_icon() + update_icon() + chamber_round(0) + +/obj/item/gun/projectile/revolver/attack_self(mob/living/user) + var/num_unloaded = 0 + chambered = null + while(get_ammo() > 0) + var/obj/item/ammo_casing/CB + CB = magazine.get_round(0) + if(CB) + CB.loc = get_turf(loc) + CB.SpinAnimation(10, 1) + CB.update_icon() + playsound(get_turf(CB), "casingdrop", 60, 1) + num_unloaded++ + if(num_unloaded) + to_chat(user, "You unload [num_unloaded] shell\s from [src].") + else + to_chat(user, "[src] is empty!") + +/obj/item/gun/projectile/revolver/verb/spin() + set name = "Spin Chamber" + set category = "Object" + set desc = "Click to spin your revolver's chamber." + + var/mob/M = usr + + if(M.stat || !in_range(M,src)) + return + + if(istype(magazine, /obj/item/ammo_box/magazine/internal/cylinder)) + var/obj/item/ammo_box/magazine/internal/cylinder/C = magazine + C.spin() + chamber_round(0) + playsound(loc, 'sound/weapons/revolver_spin.ogg', 50, 1) + usr.visible_message("[usr] spins [src]'s chamber.", "You spin [src]'s chamber.") + else + verbs -= /obj/item/gun/projectile/revolver/verb/spin + +/obj/item/gun/projectile/revolver/can_shoot() + return get_ammo(0,0) + +/obj/item/gun/projectile/revolver/get_ammo(countchambered = 0, countempties = 1) + var/boolets = 0 //mature var names for mature people + if(chambered && countchambered) + boolets++ + if(magazine) + boolets += magazine.ammo_count(countempties) + return boolets + +/obj/item/gun/projectile/revolver/examine(mob/user) + . = ..() + . += "[get_ammo(0,0)] of those are live rounds." + +/obj/item/gun/projectile/revolver/detective + desc = "A cheap Martian knock-off of a classic law enforcement firearm. Uses .38-special rounds." + name = "\improper .38 Mars Special" + icon_state = "detective" + mag_type = /obj/item/ammo_box/magazine/internal/cylinder/rev38 + unique_rename = 1 + unique_reskin = 1 + +/obj/item/gun/projectile/revolver/detective/New() + ..() + options["The Original"] = "detective" + options["Leopard Spots"] = "detective_leopard" + options["Black Panther"] = "detective_panther" + options["Gold Trim"] = "detective_gold" + options["The Peacemaker"] = "detective_peacemaker" + options["Cancel"] = null + +/obj/item/gun/projectile/revolver/detective/process_fire(atom/target as mob|obj|turf, mob/living/user as mob|obj, message = 1, params, zone_override = "") + if(magazine.caliber != initial(magazine.caliber)) + if(prob(70 - (magazine.ammo_count() * 10))) //minimum probability of 10, maximum of 60 + playsound(user, fire_sound, 50, 1) + to_chat(user, "[src] blows up in your face!") + user.take_organ_damage(0,20) + user.unEquip(src) + return 0 + ..() + +/obj/item/gun/projectile/revolver/detective/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + if(magazine.caliber == "38") + to_chat(user, "You begin to reinforce the barrel of [src]...") + if(magazine.ammo_count()) + afterattack(user, user) //you know the drill + user.visible_message("[src] goes off!", "[src] goes off in your face!") + return + if(!I.use_tool(src, user, 30, volume = I.tool_volume)) + return + if(magazine.ammo_count()) + to_chat(user, "You can't modify it!") + return + magazine.caliber = "357" + desc = "The barrel and chamber assembly seems to have been modified." + to_chat(user, "You reinforce the barrel of [src]. Now it will fire .357 rounds.") + else + to_chat(user, "You begin to revert the modifications to [src]...") + if(magazine.ammo_count()) + afterattack(user, user) //and again + user.visible_message("[src] goes off!", "[src] goes off in your face!") + return + if(!I.use_tool(src, user, 30, volume = I.tool_volume)) + return + if(magazine.ammo_count()) + to_chat(user, "You can't modify it!") + return + magazine.caliber = "38" + desc = initial(desc) + to_chat(user, "You remove the modifications on [src]. Now it will fire .38 rounds.") + +/obj/item/gun/projectile/revolver/fingergun //Summoned by the Finger Gun spell, from advanced mimery traitor item + name = "\improper finger gun" + desc = "Bang bang bang!" + icon_state = "fingergun" + mag_type = /obj/item/ammo_box/magazine/internal/cylinder/rev38/invisible + origin_tech = "" + flags = ABSTRACT | NODROP | DROPDEL + slot_flags = null + fire_sound = null + fire_sound_text = null + lefthand_file = null + righthand_file = null + clumsy_check = 0 //Stole your uplink! Honk! + needs_permit = 0 //go away beepsky + +/obj/item/gun/projectile/revolver/fingergun/fake + desc = "Pew pew pew!" + mag_type = /obj/item/ammo_box/magazine/internal/cylinder/rev38/invisible/fake + +/obj/item/gun/projectile/revolver/fingergun/New() + ..() + verbs -= /obj/item/gun/projectile/revolver/verb/spin + +/obj/item/gun/projectile/revolver/fingergun/shoot_with_empty_chamber(/*mob/living/user as mob|obj*/) + to_chat(usr, "You are out of ammo! You holster your fingers.") + qdel(src) + return + +/obj/item/gun/projectile/revolver/fingergun/afterattack(atom/target, mob/living/user, flag, params) + if(!user.mind.miming) + to_chat(usr, "You must dedicate yourself to silence first. Use your fingers if you wish to holster them.") + return + ..() + +/obj/item/gun/projectile/revolver/fingergun/attackby(obj/item/A, mob/user, params) + return + +/obj/item/gun/projectile/revolver/fingergun/attack_self(mob/living/user) + to_chat(usr, "You holster your fingers. Another time.") + qdel(src) + return + +/obj/item/gun/projectile/revolver/mateba + name = "\improper Unica 6 auto-revolver" + desc = "A retro high-powered autorevolver typically used by officers of the New Russia military. Uses .357 ammo." //>10mm hole >.357 + icon_state = "mateba" + +/obj/item/gun/projectile/revolver/golden + name = "\improper Golden revolver" + desc = "This ain't no game, ain't never been no show, And I'll gladly gun down the oldest lady you know. Uses .357 ammo." + icon_state = "goldrevolver" + fire_sound = 'sound/weapons/resonator_blast.ogg' + recoil = 8 + +/obj/item/gun/projectile/revolver/nagant + name = "nagant revolver" + desc = "An old model of revolver that originated in Russia. Able to be suppressed. Uses 7.62x38mmR ammo." + icon_state = "nagant" + origin_tech = "combat=3" + can_suppress = 1 + mag_type = /obj/item/ammo_box/magazine/internal/cylinder/rev762 + +// A gun to play Russian Roulette! +// You can spin the chamber to randomize the position of the bullet. + +/obj/item/gun/projectile/revolver/russian + name = "\improper Russian Revolver" + desc = "A Russian-made revolver for drinking games. Uses .357 ammo, and has a mechanism that spins the chamber before each trigger pull." + origin_tech = "combat=2;materials=2" + mag_type = /obj/item/ammo_box/magazine/internal/rus357 + var/spun = 0 + + +/obj/item/gun/projectile/revolver/russian/New() + ..() + Spin() + update_icon() + +/obj/item/gun/projectile/revolver/russian/proc/Spin() + chambered = null + var/random = rand(1, magazine.max_ammo) + if(random <= get_ammo(0,0)) + chamber_round() + spun = 1 + +/obj/item/gun/projectile/revolver/russian/attackby(obj/item/A, mob/user, params) + var/num_loaded = ..() + if(num_loaded) + user.visible_message("[user] loads a single bullet into the revolver and spins the chamber.", "You load a single bullet into the chamber and spin it.") + else + user.visible_message("[user] spins the chamber of the revolver.", "You spin the revolver's chamber.") + if(get_ammo() > 0) + Spin() + update_icon() + A.update_icon() + return + +/obj/item/gun/projectile/revolver/russian/attack_self(mob/user) + if(!spun && can_shoot()) + user.visible_message("[user] spins the chamber of the revolver.", "You spin the revolver's chamber.") + Spin() + else + var/num_unloaded = 0 + while(get_ammo() > 0) + var/obj/item/ammo_casing/CB + CB = magazine.get_round() + chambered = null + CB.loc = get_turf(loc) + CB.update_icon() + playsound(get_turf(CB), "casingdrop", 60, 1) + num_unloaded++ + if(num_unloaded) + to_chat(user, "You unload [num_unloaded] shell\s from [src].") + else + to_chat(user, "[src] is empty.") + +/obj/item/gun/projectile/revolver/russian/afterattack(atom/target as mob|obj|turf, mob/living/user as mob|obj, flag, params) + if(flag) + if(!(target in user.contents) && ismob(target)) + if(user.a_intent == INTENT_HARM) // Flogging action + return + + if(isliving(user)) + if(!can_trigger_gun(user)) + return + if(target != user) + if(ismob(target)) + to_chat(user, "A mechanism prevents you from shooting anyone but yourself!") + return + + if(ishuman(user)) + if(!spun) + to_chat(user, "You need to spin the revolver's chamber first!") + return + + spun = 0 + + if(chambered) + var/obj/item/ammo_casing/AC = chambered + if(AC.fire(user, user)) + playsound(user, fire_sound, 50, 1) + var/zone = check_zone(user.zone_selected) + if(zone == "head" || zone == "eyes" || zone == "mouth") + shoot_self(user, zone) + else + user.visible_message("[user.name] cowardly fires [src] at [user.p_their()] [zone]!", "You cowardly fire [src] at your [zone]!", "You hear a gunshot!") + return + + user.visible_message("*click*") + playsound(user, 'sound/weapons/empty.ogg', 100, 1) + +/obj/item/gun/projectile/revolver/russian/proc/shoot_self(mob/living/carbon/human/user, affecting = "head") + user.apply_damage(300, BRUTE, affecting) + user.visible_message("[user.name] fires [src] at [user.p_their()] head!", "You fire [src] at your head!", "You hear a gunshot!") + +/obj/item/gun/projectile/revolver/russian/soul + name = "cursed Russian revolver" + desc = "To play with this revolver requires wagering your very soul." + +/obj/item/gun/projectile/revolver/russian/soul/shoot_self(mob/living/user) + ..() + var/obj/item/soulstone/anybody/SS = new /obj/item/soulstone/anybody(get_turf(src)) + if(!SS.transfer_soul("FORCE", user)) //Something went wrong + qdel(SS) + return + user.visible_message("[user.name]'s soul is captured by \the [src]!", "You've lost the gamble! Your soul is forfeit!") + +/obj/item/gun/projectile/revolver/capgun + name = "cap gun" + desc = "Looks almost like the real thing! Ages 8 and up." + origin_tech = null + mag_type = /obj/item/ammo_box/magazine/internal/cylinder/cap + +///////////////////////////// +// DOUBLE BARRELED SHOTGUN // +///////////////////////////// + +/obj/item/gun/projectile/revolver/doublebarrel + name = "double-barreled shotgun" + desc = "A true classic." + icon_state = "dshotgun" + item_state = "shotgun" + w_class = WEIGHT_CLASS_BULKY + force = 10 + flags = CONDUCT + slot_flags = SLOT_BACK + mag_type = /obj/item/ammo_box/magazine/internal/shot/dual + fire_sound = 'sound/weapons/gunshots/gunshot_shotgun.ogg' + sawn_desc = "Omar's coming!" + unique_rename = 1 + unique_reskin = 1 + +/obj/item/gun/projectile/revolver/doublebarrel/New() + ..() + options["Default"] = "dshotgun" + options["Dark Red Finish"] = "dshotgun-d" + options["Ash"] = "dshotgun-f" + options["Faded Grey"] = "dshotgun-g" + options["Maple"] = "dshotgun-l" + options["Rosewood"] = "dshotgun-p" + options["Cancel"] = null + +/obj/item/gun/projectile/revolver/doublebarrel/attackby(obj/item/A, mob/user, params) + if(istype(A, /obj/item/ammo_box) || istype(A, /obj/item/ammo_casing)) + chamber_round() + if(istype(A, /obj/item/melee/energy)) + var/obj/item/melee/energy/W = A + if(W.active) + sawoff(user) + if(istype(A, /obj/item/circular_saw) || istype(A, /obj/item/gun/energy/plasmacutter)) + sawoff(user) + else + return ..() + +/obj/item/gun/projectile/revolver/doublebarrel/attack_self(mob/living/user) + var/num_unloaded = 0 + while(get_ammo() > 0) + var/obj/item/ammo_casing/CB + CB = magazine.get_round(0) + chambered = null + CB.loc = get_turf(loc) + CB.SpinAnimation(10, 1) + CB.update_icon() + playsound(get_turf(CB), 'sound/weapons/gun_interactions/shotgun_fall.ogg', 70, 1) + num_unloaded++ + if(num_unloaded) + to_chat(user, "You break open \the [src] and unload [num_unloaded] shell\s.") + else + to_chat(user, "[src] is empty.") + +/obj/item/gun/projectile/revolver/doublebarrel/isHandgun() //contrary to popular opinion, double barrels are not, shockingly, handguns + return 0 + +// IMPROVISED SHOTGUN // + +/obj/item/gun/projectile/revolver/doublebarrel/improvised + name = "improvised shotgun" + desc = "Essentially a tube that aims shotgun shells." + icon_state = "ishotgun" + item_state = "shotgun" + w_class = WEIGHT_CLASS_BULKY + force = 10 + slot_flags = null + mag_type = /obj/item/ammo_box/magazine/internal/shot/improvised + fire_sound = 'sound/weapons/gunshots/gunshot_shotgun.ogg' + sawn_desc = "I'm just here for the gasoline." + unique_rename = 0 + unique_reskin = 0 + var/slung = 0 + +/obj/item/gun/projectile/revolver/doublebarrel/improvised/attackby(obj/item/A, mob/user, params) + if(istype(A, /obj/item/stack/cable_coil) && !sawn_state) + var/obj/item/stack/cable_coil/C = A + if(C.use(10)) + slot_flags = SLOT_BACK + icon_state = "ishotgunsling" + to_chat(user, "You tie the lengths of cable to the shotgun, making a sling.") + slung = 1 + update_icon() + else + to_chat(user, "You need at least ten lengths of cable if you want to make a sling.") + return + else + return ..() + +/obj/item/gun/projectile/revolver/doublebarrel/improvised/update_icon() + ..() + if(slung && (slot_flags & SLOT_BELT) ) + slung = 0 + icon_state = "ishotgun-sawn" + +/obj/item/gun/projectile/revolver/doublebarrel/improvised/sawoff(mob/user) + . = ..() + if(. && slung) //sawing off the gun removes the sling + new /obj/item/stack/cable_coil(get_turf(src), 10) + slung = 0 + update_icon() + +//caneshotgun + +/obj/item/gun/projectile/revolver/doublebarrel/improvised/cane + name = "cane" + desc = "A cane used by a true gentleman. Or a clown." + icon = 'icons/obj/items.dmi' + lefthand_file = 'icons/mob/inhands/items_lefthand.dmi' + righthand_file = 'icons/mob/inhands/items_righthand.dmi' + icon_state = "cane" + item_state = "stick" + sawn_state = SAWN_OFF + w_class = WEIGHT_CLASS_SMALL + force = 10 + can_unsuppress = 0 + slot_flags = null + origin_tech = "" // NO GIVAWAYS + mag_type = /obj/item/ammo_box/magazine/internal/shot/improvised/cane + sawn_desc = "I'm sorry, but why did you saw your cane in the first place?" + attack_verb = list("bludgeoned", "whacked", "disciplined", "thrashed") + fire_sound = 'sound/weapons/gunshots/gunshot_silenced.ogg' + suppressed = 1 + needs_permit = 0 //its just a cane beepsky..... + +/obj/item/gun/projectile/revolver/doublebarrel/improvised/cane/is_crutch() + return 1 + +/obj/item/gun/projectile/revolver/doublebarrel/improvised/cane/update_icon() + return + +/obj/item/gun/projectile/revolver/doublebarrel/improvised/cane/attackby(obj/item/A, mob/user, params) + if(istype(A, /obj/item/stack/cable_coil)) + return + else + return ..() + +/obj/item/gun/projectile/revolver/doublebarrel/improvised/cane/examine(mob/user) // HAD TO REPEAT EXAMINE CODE BECAUSE GUN CODE DOESNT STEALTH + var/f_name = "\a [src]." + if(blood_DNA && !istype(src, /obj/effect/decal)) + if(gender == PLURAL) + f_name = "some " + else + f_name = "a " + f_name += "blood-stained [name]!" + + . = list("[bicon(src)] That's [f_name]") + + if(desc) + . += desc diff --git a/code/modules/projectiles/guns/projectile/shotgun.dm b/code/modules/projectiles/guns/projectile/shotgun.dm index ee6b013af0d..17a55722d2c 100644 --- a/code/modules/projectiles/guns/projectile/shotgun.dm +++ b/code/modules/projectiles/guns/projectile/shotgun.dm @@ -1,331 +1,331 @@ -/obj/item/gun/projectile/shotgun - name = "shotgun" - desc = "A traditional shotgun with wood furniture and a four-shell capacity underneath." - icon_state = "shotgun" - item_state = "shotgun" - w_class = WEIGHT_CLASS_BULKY - force = 10 - flags = CONDUCT - slot_flags = SLOT_BACK - origin_tech = "combat=4;materials=2" - mag_type = /obj/item/ammo_box/magazine/internal/shot - fire_sound = 'sound/weapons/gunshots/gunshot_shotgun.ogg' - var/recentpump = 0 // to prevent spammage - weapon_weight = WEAPON_MEDIUM - -/obj/item/gun/projectile/shotgun/attackby(obj/item/A, mob/user, params) - . = ..() - if(.) - return - var/num_loaded = magazine.attackby(A, user, params, 1) - if(num_loaded) - to_chat(user, "You load [num_loaded] shell\s into \the [src]!") - A.update_icon() - update_icon() - - -/obj/item/gun/projectile/shotgun/process_chamber() - return ..(0, 0) - -/obj/item/gun/projectile/shotgun/chamber_round() - return - -/obj/item/gun/projectile/shotgun/can_shoot() - if(!chambered) - return 0 - return (chambered.BB ? 1 : 0) - -/obj/item/gun/projectile/shotgun/attack_self(mob/living/user) - if(recentpump) - return - pump(user) - recentpump = 1 - spawn(10) - recentpump = 0 - return - - -/obj/item/gun/projectile/shotgun/proc/pump(mob/M) - playsound(M, 'sound/weapons/gun_interactions/shotgunpump.ogg', 60, 1) - pump_unload(M) - pump_reload(M) - update_icon() //I.E. fix the desc - return 1 - -/obj/item/gun/projectile/shotgun/proc/pump_unload(mob/M) - if(chambered)//We have a shell in the chamber - chambered.loc = get_turf(src)//Eject casing - chambered.SpinAnimation(5, 1) - playsound(src, chambered.drop_sound, 60, 1) - chambered = null - -/obj/item/gun/projectile/shotgun/proc/pump_reload(mob/M) - if(!magazine.ammo_count()) - return 0 - var/obj/item/ammo_casing/AC = magazine.get_round() //load next casing. - chambered = AC - -/obj/item/gun/projectile/shotgun/examine(mob/user) - . = ..() - if(chambered) - . += "A [chambered.BB ? "live" : "spent"] one is in the chamber." - -/obj/item/gun/projectile/shotgun/isHandgun() //You cannot, in fact, holster a shotgun. - return 0 - -/obj/item/gun/projectile/shotgun/lethal - mag_type = /obj/item/ammo_box/magazine/internal/shot/lethal - -// RIOT SHOTGUN // - -/obj/item/gun/projectile/shotgun/riot //for spawn in the armory - name = "riot shotgun" - desc = "A sturdy shotgun with a longer magazine and a fixed tactical stock designed for non-lethal riot control." - icon_state = "riotshotgun" - mag_type = /obj/item/ammo_box/magazine/internal/shot/riot - sawn_desc = "Come with me if you want to live." - sawn_state = SAWN_INTACT - -/obj/item/gun/projectile/shotgun/riot/attackby(obj/item/A, mob/user, params) - if(istype(A, /obj/item/circular_saw) || istype(A, /obj/item/gun/energy/plasmacutter)) - sawoff(user) - if(istype(A, /obj/item/melee/energy)) - var/obj/item/melee/energy/W = A - if(W.active) - sawoff(user) - if(istype(A, /obj/item/pipe)) - unsaw(A, user) - else - return ..() - -/obj/item/gun/projectile/shotgun/riot/sawoff(mob/user) - if(sawn_state == SAWN_OFF) - to_chat(user, "[src] has already been shortened!") - return - if(istype(loc, /obj/item/storage)) //To prevent inventory exploits - to_chat(user, "How do you plan to modify [src] while it's in a bag.") - return - if(chambered) //if the gun is chambering live ammo, shoot self, if chambering empty ammo, 'click' - if(chambered.BB) - afterattack(user, user) - user.visible_message("\The [src] goes off!", "\The [src] goes off in your face!") - return - else - afterattack(user, user) - user.visible_message("The [src] goes click!", "The [src] you are holding goes click.") - if(magazine.ammo_count()) //Spill the mag onto the floor - user.visible_message("[user.name] opens [src] up and the shells go goes flying around!", "You open [src] up and the shells go goes flying everywhere!!") - while(get_ammo(0) > 0) - var/obj/item/ammo_casing/CB - CB = magazine.get_round(0) - if(CB) - CB.loc = get_turf(loc) - CB.update_icon() - - if(do_after(user, 30, target = src)) - user.visible_message("[user] shortens \the [src]!", "You shorten \the [src].") - post_sawoff() - return 1 - - -/obj/item/gun/projectile/shotgun/riot/proc/post_sawoff() - name = "assault shotgun" - desc = sawn_desc - w_class = WEIGHT_CLASS_NORMAL - current_skin = "riotshotgun-short" - item_state = "gun" //phil235 is it different with different skin? - slot_flags &= ~SLOT_BACK //you can't sling it on your back - slot_flags |= SLOT_BELT //but you can wear it on your belt (poorly concealed under a trenchcoat, ideally) - sawn_state = SAWN_OFF - magazine.max_ammo = 3 - update_icon() - - -/obj/item/gun/projectile/shotgun/riot/proc/unsaw(obj/item/A, mob/user) - if(sawn_state == SAWN_INTACT) - to_chat(user, "[src] has not been shortened!") - return - if(istype(loc, /obj/item/storage)) //To prevent inventory exploits - to_chat(user, "How do you plan to modify [src] while it's in a bag.") - return - if(chambered) //if the gun is chambering live ammo, shoot self, if chambering empty ammo, 'click' - if(chambered.BB) - afterattack(user, user) - user.visible_message("\The [src] goes off!", "\The [src] goes off in your face!") - return - else - afterattack(user, user) - user.visible_message("The [src] goes click!", "The [src] you are holding goes click.") - if(magazine.ammo_count()) //Spill the mag onto the floor - user.visible_message("[user.name] opens [src] up and the shells go goes flying around!", "You open [src] up and the shells go goes flying everywhere!!") - while(get_ammo() > 0) - var/obj/item/ammo_casing/CB - CB = magazine.get_round(0) - if(CB) - CB.loc = get_turf(loc) - CB.update_icon() - - if(do_after(user, 30, target = src)) - qdel(A) - user.visible_message("[user] lengthens [src]!", "You lengthen [src].") - post_unsaw(user) - return 1 - -/obj/item/gun/projectile/shotgun/riot/proc/post_unsaw() - name = initial(name) - desc = initial(desc) - w_class = initial(w_class) - current_skin = "riotshotgun" - item_state = initial(item_state) - slot_flags &= ~SLOT_BELT - slot_flags |= SLOT_BACK - sawn_state = SAWN_INTACT - magazine.max_ammo = 6 - update_icon() - -/obj/item/gun/projectile/shotgun/riot/update_icon() //Can't use the old proc as it makes it go to riotshotgun-short_sawn - ..() - if(current_skin) - icon_state = "[current_skin]" - else - icon_state = "[initial(icon_state)]" - -/obj/item/gun/projectile/shotgun/riot/short - mag_type = /obj/item/ammo_box/magazine/internal/shot/riot/short - -/obj/item/gun/projectile/shotgun/riot/short/New() - ..() - post_sawoff() - - - -/////////////////////// -// BOLT ACTION RIFLE // -/////////////////////// - -/obj/item/gun/projectile/shotgun/boltaction - name = "\improper Mosin Nagant" - desc = "This piece of junk looks like something that could have been used 700 years ago." - icon_state = "moistnugget" - item_state = "moistnugget" - slot_flags = 0 //no SLOT_BACK sprite, alas - mag_type = /obj/item/ammo_box/magazine/internal/boltaction - fire_sound = 'sound/weapons/gunshots/gunshot_rifle.ogg' - var/bolt_open = 0 - can_bayonet = TRUE - knife_x_offset = 27 - knife_y_offset = 13 - -/obj/item/gun/projectile/shotgun/boltaction/pump(mob/M) - playsound(M, 'sound/weapons/gun_interactions/rifle_load.ogg', 60, 1) - if(bolt_open) - pump_reload(M) - else - pump_unload(M) - bolt_open = !bolt_open - update_icon() //I.E. fix the desc - return 1 - -/obj/item/gun/projectile/shotgun/blow_up(mob/user) - . = 0 - if(chambered && chambered.BB) - process_fire(user, user,0) - . = 1 - -/obj/item/gun/projectile/shotgun/boltaction/attackby(obj/item/A, mob/user, params) - if(!bolt_open) - to_chat(user, "The bolt is closed!") - return - . = ..() - -/obj/item/gun/projectile/shotgun/boltaction/examine(mob/user) - . = ..() - . += "The bolt is [bolt_open ? "open" : "closed"]." - -/obj/item/gun/projectile/shotgun/boltaction/enchanted - name = "enchanted bolt action rifle" - desc = "Careful not to lose your head." - var/guns_left = 30 - mag_type = /obj/item/ammo_box/magazine/internal/boltaction/enchanted - can_bayonet = FALSE - -/obj/item/gun/projectile/shotgun/boltaction/enchanted/New() - ..() - bolt_open = 1 - pump() - -/obj/item/gun/projectile/shotgun/boltaction/enchanted/dropped() - ..() - guns_left = 0 - -/obj/item/gun/projectile/shotgun/boltaction/enchanted/shoot_live_shot(mob/living/user as mob|obj, pointblank = 0, mob/pbtarget = null, message = 1) - ..() - if(guns_left) - var/obj/item/gun/projectile/shotgun/boltaction/enchanted/GUN = new - GUN.guns_left = guns_left - 1 - user.drop_item() - user.swap_hand() - user.put_in_hands(GUN) - else - user.drop_item() - spawn(0) - throw_at(pick(oview(7,get_turf(user))),1,1) - user.visible_message("[user] tosses aside the spent rifle!") - -// Automatic Shotguns// - -/obj/item/gun/projectile/shotgun/automatic - -/obj/item/gun/projectile/shotgun/automatic/shoot_live_shot(mob/living/user as mob|obj) - ..() - pump(user) - -/obj/item/gun/projectile/shotgun/automatic/combat - name = "combat shotgun" - desc = "A semi automatic shotgun with tactical furniture and a six-shell capacity underneath." - icon_state = "cshotgun" - origin_tech = "combat=6" - mag_type = /obj/item/ammo_box/magazine/internal/shot/com - w_class = WEIGHT_CLASS_HUGE - -//Dual Feed Shotgun - -/obj/item/gun/projectile/shotgun/automatic/dual_tube - name = "cycler shotgun" - desc = "An advanced shotgun with two separate magazine tubes, allowing you to quickly toggle between ammo types." - icon_state = "cycler" - origin_tech = "combat=4;materials=2" - mag_type = /obj/item/ammo_box/magazine/internal/shot/tube - w_class = WEIGHT_CLASS_HUGE - var/toggled = 0 - var/obj/item/ammo_box/magazine/internal/shot/alternate_magazine - -/obj/item/gun/projectile/shotgun/automatic/dual_tube/New() - ..() - if(!alternate_magazine) - alternate_magazine = new mag_type(src) - -/obj/item/gun/projectile/shotgun/automatic/dual_tube/attack_self(mob/living/user) - if(!chambered && magazine.contents.len) - pump() - else - toggle_tube(user) - -/obj/item/gun/projectile/shotgun/automatic/dual_tube/proc/toggle_tube(mob/living/user) - var/current_mag = magazine - var/alt_mag = alternate_magazine - magazine = alt_mag - alternate_magazine = current_mag - toggled = !toggled - if(toggled) - to_chat(user, "You switch to tube B.") - else - to_chat(user, "You switch to tube A.") - playsound(user, 'sound/weapons/gun_interactions/selector.ogg', 100, 1) - -/obj/item/gun/projectile/shotgun/automatic/dual_tube/AltClick(mob/living/user) - if(user.incapacitated() || !Adjacent(user) || !istype(user)) - return - pump() - -// DOUBLE BARRELED SHOTGUN, IMPROVISED SHOTGUN, and CANE SHOTGUN are in revolver.dm +/obj/item/gun/projectile/shotgun + name = "shotgun" + desc = "A traditional shotgun with wood furniture and a four-shell capacity underneath." + icon_state = "shotgun" + item_state = "shotgun" + w_class = WEIGHT_CLASS_BULKY + force = 10 + flags = CONDUCT + slot_flags = SLOT_BACK + origin_tech = "combat=4;materials=2" + mag_type = /obj/item/ammo_box/magazine/internal/shot + fire_sound = 'sound/weapons/gunshots/gunshot_shotgun.ogg' + var/recentpump = 0 // to prevent spammage + weapon_weight = WEAPON_MEDIUM + +/obj/item/gun/projectile/shotgun/attackby(obj/item/A, mob/user, params) + . = ..() + if(.) + return + var/num_loaded = magazine.attackby(A, user, params, 1) + if(num_loaded) + to_chat(user, "You load [num_loaded] shell\s into \the [src]!") + A.update_icon() + update_icon() + + +/obj/item/gun/projectile/shotgun/process_chamber() + return ..(0, 0) + +/obj/item/gun/projectile/shotgun/chamber_round() + return + +/obj/item/gun/projectile/shotgun/can_shoot() + if(!chambered) + return 0 + return (chambered.BB ? 1 : 0) + +/obj/item/gun/projectile/shotgun/attack_self(mob/living/user) + if(recentpump) + return + pump(user) + recentpump = 1 + spawn(10) + recentpump = 0 + return + + +/obj/item/gun/projectile/shotgun/proc/pump(mob/M) + playsound(M, 'sound/weapons/gun_interactions/shotgunpump.ogg', 60, 1) + pump_unload(M) + pump_reload(M) + update_icon() //I.E. fix the desc + return 1 + +/obj/item/gun/projectile/shotgun/proc/pump_unload(mob/M) + if(chambered)//We have a shell in the chamber + chambered.loc = get_turf(src)//Eject casing + chambered.SpinAnimation(5, 1) + playsound(src, chambered.drop_sound, 60, 1) + chambered = null + +/obj/item/gun/projectile/shotgun/proc/pump_reload(mob/M) + if(!magazine.ammo_count()) + return 0 + var/obj/item/ammo_casing/AC = magazine.get_round() //load next casing. + chambered = AC + +/obj/item/gun/projectile/shotgun/examine(mob/user) + . = ..() + if(chambered) + . += "A [chambered.BB ? "live" : "spent"] one is in the chamber." + +/obj/item/gun/projectile/shotgun/isHandgun() //You cannot, in fact, holster a shotgun. + return 0 + +/obj/item/gun/projectile/shotgun/lethal + mag_type = /obj/item/ammo_box/magazine/internal/shot/lethal + +// RIOT SHOTGUN // + +/obj/item/gun/projectile/shotgun/riot //for spawn in the armory + name = "riot shotgun" + desc = "A sturdy shotgun with a longer magazine and a fixed tactical stock designed for non-lethal riot control." + icon_state = "riotshotgun" + mag_type = /obj/item/ammo_box/magazine/internal/shot/riot + sawn_desc = "Come with me if you want to live." + sawn_state = SAWN_INTACT + +/obj/item/gun/projectile/shotgun/riot/attackby(obj/item/A, mob/user, params) + if(istype(A, /obj/item/circular_saw) || istype(A, /obj/item/gun/energy/plasmacutter)) + sawoff(user) + if(istype(A, /obj/item/melee/energy)) + var/obj/item/melee/energy/W = A + if(W.active) + sawoff(user) + if(istype(A, /obj/item/pipe)) + unsaw(A, user) + else + return ..() + +/obj/item/gun/projectile/shotgun/riot/sawoff(mob/user) + if(sawn_state == SAWN_OFF) + to_chat(user, "[src] has already been shortened!") + return + if(istype(loc, /obj/item/storage)) //To prevent inventory exploits + to_chat(user, "How do you plan to modify [src] while it's in a bag.") + return + if(chambered) //if the gun is chambering live ammo, shoot self, if chambering empty ammo, 'click' + if(chambered.BB) + afterattack(user, user) + user.visible_message("\The [src] goes off!", "\The [src] goes off in your face!") + return + else + afterattack(user, user) + user.visible_message("The [src] goes click!", "The [src] you are holding goes click.") + if(magazine.ammo_count()) //Spill the mag onto the floor + user.visible_message("[user.name] opens [src] up and the shells go goes flying around!", "You open [src] up and the shells go goes flying everywhere!!") + while(get_ammo(0) > 0) + var/obj/item/ammo_casing/CB + CB = magazine.get_round(0) + if(CB) + CB.loc = get_turf(loc) + CB.update_icon() + + if(do_after(user, 30, target = src)) + user.visible_message("[user] shortens \the [src]!", "You shorten \the [src].") + post_sawoff() + return 1 + + +/obj/item/gun/projectile/shotgun/riot/proc/post_sawoff() + name = "assault shotgun" + desc = sawn_desc + w_class = WEIGHT_CLASS_NORMAL + current_skin = "riotshotgun-short" + item_state = "gun" //phil235 is it different with different skin? + slot_flags &= ~SLOT_BACK //you can't sling it on your back + slot_flags |= SLOT_BELT //but you can wear it on your belt (poorly concealed under a trenchcoat, ideally) + sawn_state = SAWN_OFF + magazine.max_ammo = 3 + update_icon() + + +/obj/item/gun/projectile/shotgun/riot/proc/unsaw(obj/item/A, mob/user) + if(sawn_state == SAWN_INTACT) + to_chat(user, "[src] has not been shortened!") + return + if(istype(loc, /obj/item/storage)) //To prevent inventory exploits + to_chat(user, "How do you plan to modify [src] while it's in a bag.") + return + if(chambered) //if the gun is chambering live ammo, shoot self, if chambering empty ammo, 'click' + if(chambered.BB) + afterattack(user, user) + user.visible_message("\The [src] goes off!", "\The [src] goes off in your face!") + return + else + afterattack(user, user) + user.visible_message("The [src] goes click!", "The [src] you are holding goes click.") + if(magazine.ammo_count()) //Spill the mag onto the floor + user.visible_message("[user.name] opens [src] up and the shells go goes flying around!", "You open [src] up and the shells go goes flying everywhere!!") + while(get_ammo() > 0) + var/obj/item/ammo_casing/CB + CB = magazine.get_round(0) + if(CB) + CB.loc = get_turf(loc) + CB.update_icon() + + if(do_after(user, 30, target = src)) + qdel(A) + user.visible_message("[user] lengthens [src]!", "You lengthen [src].") + post_unsaw(user) + return 1 + +/obj/item/gun/projectile/shotgun/riot/proc/post_unsaw() + name = initial(name) + desc = initial(desc) + w_class = initial(w_class) + current_skin = "riotshotgun" + item_state = initial(item_state) + slot_flags &= ~SLOT_BELT + slot_flags |= SLOT_BACK + sawn_state = SAWN_INTACT + magazine.max_ammo = 6 + update_icon() + +/obj/item/gun/projectile/shotgun/riot/update_icon() //Can't use the old proc as it makes it go to riotshotgun-short_sawn + ..() + if(current_skin) + icon_state = "[current_skin]" + else + icon_state = "[initial(icon_state)]" + +/obj/item/gun/projectile/shotgun/riot/short + mag_type = /obj/item/ammo_box/magazine/internal/shot/riot/short + +/obj/item/gun/projectile/shotgun/riot/short/New() + ..() + post_sawoff() + + + +/////////////////////// +// BOLT ACTION RIFLE // +/////////////////////// + +/obj/item/gun/projectile/shotgun/boltaction + name = "\improper Mosin Nagant" + desc = "This piece of junk looks like something that could have been used 700 years ago." + icon_state = "moistnugget" + item_state = "moistnugget" + slot_flags = 0 //no SLOT_BACK sprite, alas + mag_type = /obj/item/ammo_box/magazine/internal/boltaction + fire_sound = 'sound/weapons/gunshots/gunshot_rifle.ogg' + var/bolt_open = 0 + can_bayonet = TRUE + knife_x_offset = 27 + knife_y_offset = 13 + +/obj/item/gun/projectile/shotgun/boltaction/pump(mob/M) + playsound(M, 'sound/weapons/gun_interactions/rifle_load.ogg', 60, 1) + if(bolt_open) + pump_reload(M) + else + pump_unload(M) + bolt_open = !bolt_open + update_icon() //I.E. fix the desc + return 1 + +/obj/item/gun/projectile/shotgun/blow_up(mob/user) + . = 0 + if(chambered && chambered.BB) + process_fire(user, user,0) + . = 1 + +/obj/item/gun/projectile/shotgun/boltaction/attackby(obj/item/A, mob/user, params) + if(!bolt_open) + to_chat(user, "The bolt is closed!") + return + . = ..() + +/obj/item/gun/projectile/shotgun/boltaction/examine(mob/user) + . = ..() + . += "The bolt is [bolt_open ? "open" : "closed"]." + +/obj/item/gun/projectile/shotgun/boltaction/enchanted + name = "enchanted bolt action rifle" + desc = "Careful not to lose your head." + var/guns_left = 30 + mag_type = /obj/item/ammo_box/magazine/internal/boltaction/enchanted + can_bayonet = FALSE + +/obj/item/gun/projectile/shotgun/boltaction/enchanted/New() + ..() + bolt_open = 1 + pump() + +/obj/item/gun/projectile/shotgun/boltaction/enchanted/dropped() + ..() + guns_left = 0 + +/obj/item/gun/projectile/shotgun/boltaction/enchanted/shoot_live_shot(mob/living/user as mob|obj, pointblank = 0, mob/pbtarget = null, message = 1) + ..() + if(guns_left) + var/obj/item/gun/projectile/shotgun/boltaction/enchanted/GUN = new + GUN.guns_left = guns_left - 1 + user.drop_item() + user.swap_hand() + user.put_in_hands(GUN) + else + user.drop_item() + spawn(0) + throw_at(pick(oview(7,get_turf(user))),1,1) + user.visible_message("[user] tosses aside the spent rifle!") + +// Automatic Shotguns// + +/obj/item/gun/projectile/shotgun/automatic + +/obj/item/gun/projectile/shotgun/automatic/shoot_live_shot(mob/living/user as mob|obj) + ..() + pump(user) + +/obj/item/gun/projectile/shotgun/automatic/combat + name = "combat shotgun" + desc = "A semi automatic shotgun with tactical furniture and a six-shell capacity underneath." + icon_state = "cshotgun" + origin_tech = "combat=6" + mag_type = /obj/item/ammo_box/magazine/internal/shot/com + w_class = WEIGHT_CLASS_HUGE + +//Dual Feed Shotgun + +/obj/item/gun/projectile/shotgun/automatic/dual_tube + name = "cycler shotgun" + desc = "An advanced shotgun with two separate magazine tubes, allowing you to quickly toggle between ammo types." + icon_state = "cycler" + origin_tech = "combat=4;materials=2" + mag_type = /obj/item/ammo_box/magazine/internal/shot/tube + w_class = WEIGHT_CLASS_HUGE + var/toggled = 0 + var/obj/item/ammo_box/magazine/internal/shot/alternate_magazine + +/obj/item/gun/projectile/shotgun/automatic/dual_tube/New() + ..() + if(!alternate_magazine) + alternate_magazine = new mag_type(src) + +/obj/item/gun/projectile/shotgun/automatic/dual_tube/attack_self(mob/living/user) + if(!chambered && magazine.contents.len) + pump() + else + toggle_tube(user) + +/obj/item/gun/projectile/shotgun/automatic/dual_tube/proc/toggle_tube(mob/living/user) + var/current_mag = magazine + var/alt_mag = alternate_magazine + magazine = alt_mag + alternate_magazine = current_mag + toggled = !toggled + if(toggled) + to_chat(user, "You switch to tube B.") + else + to_chat(user, "You switch to tube A.") + playsound(user, 'sound/weapons/gun_interactions/selector.ogg', 100, 1) + +/obj/item/gun/projectile/shotgun/automatic/dual_tube/AltClick(mob/living/user) + if(user.incapacitated() || !Adjacent(user) || !istype(user)) + return + pump() + +// DOUBLE BARRELED SHOTGUN, IMPROVISED SHOTGUN, and CANE SHOTGUN are in revolver.dm diff --git a/code/modules/projectiles/guns/projectile/sniper.dm b/code/modules/projectiles/guns/projectile/sniper.dm index de3f4c02a87..09d9ec15aed 100644 --- a/code/modules/projectiles/guns/projectile/sniper.dm +++ b/code/modules/projectiles/guns/projectile/sniper.dm @@ -208,4 +208,4 @@ else if(ammo) overlays += image('icons/obj/ammo.dmi', icon_state = ".50mag-f") else - icon_state = "[initial(icon_state)]" \ No newline at end of file + icon_state = "[initial(icon_state)]" diff --git a/code/modules/projectiles/guns/projectile/toy.dm b/code/modules/projectiles/guns/projectile/toy.dm index e9625e69795..55a9aa79c6b 100644 --- a/code/modules/projectiles/guns/projectile/toy.dm +++ b/code/modules/projectiles/guns/projectile/toy.dm @@ -133,4 +133,4 @@ mag_type = /obj/item/ammo_box/magazine/toy/sniper_rounds /obj/item/gun/projectile/automatic/sniper_rifle/toy/process_chamber(eject_casing = 0, empty_chamber = 1) - ..() \ No newline at end of file + ..() diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index d31231823f8..626bcaf05a8 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -1,355 +1,355 @@ -/obj/item/projectile - name = "projectile" - icon = 'icons/obj/projectiles.dmi' - icon_state = "bullet" - density = 0 - resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - anchored = 1 //There's a reason this is here, Mport. God fucking damn it -Agouri. Find&Fix by Pete. The reason this is here is to stop the curving of emitter shots. - flags = ABSTRACT - pass_flags = PASSTABLE - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - hitsound = 'sound/weapons/pierce.ogg' - var/hitsound_wall = "" - var/def_zone = "" //Aiming at - var/mob/firer = null//Who shot it - var/obj/item/ammo_casing/ammo_casing = null - var/suppressed = 0 //Attack message - var/yo = null - var/xo = null - var/current = null - var/atom/original = null // the original target clicked - var/turf/starting = null // the projectile's starting turf - var/list/permutated = list() // we've passed through these atoms, don't try to hit them again - var/paused = FALSE //for suspending the projectile midair - var/p_x = 16 - var/p_y = 16 // the pixel location of the tile that the player clicked. Default is the center - var/speed = 1 //Amount of deciseconds it takes for projectile to travel - var/Angle = null - var/spread = 0 //amount (in degrees) of projectile spread - var/legacy = FALSE //legacy projectile system - animate_movement = 0 - - var/ignore_source_check = FALSE - - var/damage = 10 - var/tile_dropoff = 0 //how much damage should be decremented as the bullet moves - var/tile_dropoff_s = 0 //same as above but for stamina - var/damage_type = BRUTE //BRUTE, BURN, TOX, OXY, CLONE are the only things that should be in here - var/nodamage = FALSE //Determines if the projectile will skip any damage inflictions - var/flag = "bullet" //Defines what armor to use when it hits things. Must be set to bullet, laser, energy,or bomb //Cael - bio and rad are also valid - var/projectile_type = "/obj/item/projectile" - var/range = 50 //This will de-increment every step. When 0, it will delete the projectile. - var/is_reflectable = FALSE // Can it be reflected or not? - var/alwayslog = FALSE // ALWAYS log this projectile on hit even if it doesn't hit a living target. Useful for AOE explosion / EMP. - //Effects - var/stun = 0 - var/weaken = 0 - var/paralyze = 0 - var/irradiate = 0 - var/stutter = 0 - var/slur = 0 - var/eyeblur = 0 - var/drowsy = 0 - var/stamina = 0 - var/jitter = 0 - var/forcedodge = 0 //to pass through everything - var/dismemberment = 0 //The higher the number, the greater the bonus to dismembering. 0 will not dismember at all. - var/impact_effect_type //what type of impact effect to show when hitting something - var/ricochets = 0 - var/ricochets_max = 2 - var/ricochet_chance = 30 - - var/log_override = FALSE //whether print to admin attack logs or just keep it in the diary - -/obj/item/projectile/New() - permutated = list() - return ..() - -/obj/item/projectile/proc/Range() - range-- - if(damage && tile_dropoff) - damage = max(0, damage - tile_dropoff) // decrement projectile damage based on dropoff value for each tile it moves - if(stamina && tile_dropoff_s) - stamina = max(0, stamina - tile_dropoff_s) // as above, but with stamina - if(range <= 0 && loc) - on_range() - if(!damage && !stamina && (tile_dropoff || tile_dropoff_s)) - on_range() - -/obj/item/projectile/proc/on_range() //if we want there to be effects when they reach the end of their range - qdel(src) - -/obj/item/projectile/proc/prehit(atom/target) - return TRUE - -/obj/item/projectile/proc/on_hit(atom/target, blocked = 0, hit_zone) - var/turf/target_loca = get_turf(target) - var/hitx - var/hity - if(target == original) - hitx = target.pixel_x + p_x - 16 - hity = target.pixel_y + p_y - 16 - else - hitx = target.pixel_x + rand(-8, 8) - hity = target.pixel_y + rand(-8, 8) - if(!nodamage && (damage_type == BRUTE || damage_type == BURN) && iswallturf(target_loca) && prob(75)) - var/turf/simulated/wall/W = target_loca - if(impact_effect_type) - new impact_effect_type(target_loca, hitx, hity) - - W.add_dent(WALL_DENT_SHOT, hitx, hity) - return 0 - if(alwayslog) - add_attack_logs(firer, target, "Shot with a [type]") - if(!isliving(target)) - if(impact_effect_type) - new impact_effect_type(target_loca, hitx, hity) - return 0 - var/mob/living/L = target - var/mob/living/carbon/human/H - if(blocked < 100) // not completely blocked - if(damage && L.blood_volume && damage_type == BRUTE) - var/splatter_dir = dir - if(starting) - splatter_dir = get_dir(starting, target_loca) - if(isalien(L)) - new /obj/effect/temp_visual/dir_setting/bloodsplatter/xenosplatter(target_loca, splatter_dir) - else - var/blood_color = "#C80000" - if(ishuman(target)) - H = target - blood_color = H.dna.species.blood_color - new /obj/effect/temp_visual/dir_setting/bloodsplatter(target_loca, splatter_dir, blood_color) - if(prob(33)) - var/list/shift = list("x" = 0, "y" = 0) - var/turf/step_over = get_step(target_loca, splatter_dir) - - if(get_splatter_blockage(step_over, target, splatter_dir, target_loca)) //If you can't cross the tile or any of its relevant obstacles... - shift = pixel_shift_dir(splatter_dir) //Pixel shift the blood there instead (so you can't see wallsplatter through walls). - else - target_loca = step_over - L.add_splatter_floor(target_loca, shift_x = shift["x"], shift_y = shift["y"]) - if(istype(H)) - for(var/mob/living/carbon/human/M in step_over) //Bloody the mobs who're infront of the spray. - M.bloody_hands(H) - /* Uncomment when bloody_body stops randomly not transferring blood colour. - M.bloody_body(H) */ - else if(impact_effect_type) - new impact_effect_type(target_loca, hitx, hity) - var/organ_hit_text = "" - if(L.has_limbs) - organ_hit_text = " in \the [parse_zone(def_zone)]" - if(suppressed) - playsound(loc, hitsound, 5, 1, -1) - to_chat(L, "You're shot by \a [src][organ_hit_text]!") - else - if(hitsound) - var/volume = vol_by_damage() - playsound(loc, hitsound, volume, 1, -1) - L.visible_message("[L] is hit by \a [src][organ_hit_text]!", \ - "[L] is hit by \a [src][organ_hit_text]!") //X has fired Y is now given by the guns so you cant tell who shot you if you could not see the shooter - - var/reagent_note - var/has_reagents = FALSE - if(reagents && reagents.reagent_list) - reagent_note = " REAGENTS:" - for(var/datum/reagent/R in reagents.reagent_list) - reagent_note += R.id + " (" - reagent_note += num2text(R.volume) + ") " - has_reagents = TRUE - if(!log_override && firer && !alwayslog) - if(has_reagents) - add_attack_logs(firer, L, "Shot with a [type] (containing [reagent_note])") - else - add_attack_logs(firer, L, "Shot with a [type]") - return L.apply_effects(stun, weaken, paralyze, irradiate, slur, stutter, eyeblur, drowsy, blocked, stamina, jitter) - -/obj/item/projectile/proc/get_splatter_blockage(var/turf/step_over, var/atom/target, var/splatter_dir, var/target_loca) //Check whether the place we want to splatter blood is blocked (i.e. by windows). - var/turf/step_cardinal = !(splatter_dir in list(NORTH, SOUTH, EAST, WEST)) ? get_step(target_loca, get_cardinal_dir(target_loca, step_over)) : null - - if(step_over.density && !step_over.CanPass(target, step_over, 1)) //Preliminary simple check. - return TRUE - for(var/atom/movable/border_obstacle in step_over) //Check to see if we're blocked by a (non-full) window or some such. Do deeper investigation if we're splattering blood diagonally. - if(border_obstacle.flags&ON_BORDER && get_dir(step_cardinal ? step_cardinal : target_loca, step_over) == turn(border_obstacle.dir, 180)) - return TRUE - -/obj/item/projectile/proc/vol_by_damage() - if(damage) - return Clamp((damage) * 0.67, 30, 100)// Multiply projectile damage by 0.67, then clamp the value between 30 and 100 - else - return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume - -/obj/item/projectile/Bump(atom/A, yes) - if(!yes) //prevents double bumps. - return - - if(check_ricochet(A) && check_ricochet_flag(A) && ricochets < ricochets_max) - ricochets++ - if(A.handle_ricochet(src)) - on_ricochet(A) - ignore_source_check = TRUE - range = initial(range) - return TRUE - if(firer && !ignore_source_check) - if(A == firer || (A == firer.loc && ismecha(A))) //cannot shoot yourself or your mech - loc = A.loc - return 0 - - var/distance = get_dist(get_turf(A), starting) // Get the distance between the turf shot from and the mob we hit and use that for the calculations. - def_zone = ran_zone(def_zone, max(100-(7*distance), 5)) //Lower accurancy/longer range tradeoff. 7 is a balanced number to use. - - if(isturf(A) && hitsound_wall) - var/volume = Clamp(vol_by_damage() + 20, 0, 100) - if(suppressed) - volume = 5 - playsound(loc, hitsound_wall, volume, 1, -1) - else if(ishuman(A)) - var/mob/living/carbon/human/H = A - var/obj/item/organ/external/organ = H.get_organ(check_zone(def_zone)) - if(isnull(organ)) - return - - var/turf/target_turf = get_turf(A) - prehit(A) - var/permutation = A.bullet_act(src, def_zone) // searches for return value, could be deleted after run so check A isn't null - if(permutation == -1 || forcedodge)// the bullet passes through a dense object! - loc = target_turf - if(A) - permutated.Add(A) - return 0 - else - if(A && A.density && !ismob(A) && !(A.flags & ON_BORDER)) //if we hit a dense non-border obj or dense turf then we also hit one of the mobs on that tile. - var/list/mobs_list = list() - for(var/mob/living/L in target_turf) - mobs_list += L - if(mobs_list.len) - var/mob/living/picked_mob = pick(mobs_list) - prehit(picked_mob) - picked_mob.bullet_act(src, def_zone) - qdel(src) - -/obj/item/projectile/Process_Spacemove(var/movement_dir = 0) - return 1 //Bullets don't drift in space - -/obj/item/projectile/proc/fire(var/setAngle) - if(setAngle) - Angle = setAngle - if(!legacy) //new projectiles - set waitfor = 0 - while(loc) - if(!paused) - if((!( current ) || loc == current)) - current = locate(Clamp(x+xo,1,world.maxx),Clamp(y+yo,1,world.maxy),z) - if(isnull(Angle)) - Angle=round(Get_Angle(src,current)) - if(spread) - Angle += (rand() - 0.5) * spread - var/matrix/M = new - M.Turn(Angle) - transform = M - - var/Pixel_x=round(sin(Angle)+16*sin(Angle)*2) - var/Pixel_y=round(cos(Angle)+16*cos(Angle)*2) - var/pixel_x_offset = pixel_x + Pixel_x - var/pixel_y_offset = pixel_y + Pixel_y - var/new_x = x - var/new_y = y - - while(pixel_x_offset > 16) - pixel_x_offset -= 32 - pixel_x -= 32 - new_x++// x++ - while(pixel_x_offset < -16) - pixel_x_offset += 32 - pixel_x += 32 - new_x-- - - while(pixel_y_offset > 16) - pixel_y_offset -= 32 - pixel_y -= 32 - new_y++ - while(pixel_y_offset < -16) - pixel_y_offset += 32 - pixel_y += 32 - new_y-- - - speed = round(speed) - step_towards(src, locate(new_x, new_y, z)) - if(speed <= 1) - pixel_x = pixel_x_offset - pixel_y = pixel_y_offset - else - animate(src, pixel_x = pixel_x_offset, pixel_y = pixel_y_offset, time = max(1, (speed <= 3 ? speed - 1 : speed))) - - if(original && (original.layer>=2.75) || ismob(original)) - if(loc == get_turf(original)) - if(!(original in permutated)) - Bump(original, 1) - Range() - sleep(max(1, speed)) - else //old projectile system - set waitfor = 0 - while(loc) - if(!paused) - if((!( current ) || loc == current)) - current = locate(Clamp(x+xo,1,world.maxx),Clamp(y+yo,1,world.maxy),z) - step_towards(src, current) - if(original && (original.layer>=2.75) || ismob(original)) - if(loc == get_turf(original)) - if(!(original in permutated)) - Bump(original, 1) - Range() - sleep(1) - -obj/item/projectile/proc/reflect_back(atom/source, list/position_modifiers = list(0, 0, 0, 0, 0, -1, 1, -2, 2)) - if(starting) - var/new_x = starting.x + pick(position_modifiers) - var/new_y = starting.y + pick(position_modifiers) - var/turf/curloc = get_turf(source) - - if(ismob(source)) - firer = source // The reflecting mob will be the new firer - else - firer = null // Reflected by something other than a mob so firer will be null - - // redirect the projectile - original = locate(new_x, new_y, z) - starting = curloc - current = curloc - yo = new_y - curloc.y - xo = new_x - curloc.x - Angle = null // Will be calculated in fire() - -obj/item/projectile/Crossed(atom/movable/AM, oldloc) //A mob moving on a tile with a projectile is hit by it. - ..() - if(isliving(AM) && AM.density && !checkpass(PASSMOB)) - Bump(AM, 1) - -/obj/item/projectile/Destroy() - ammo_casing = null - return ..() - -/obj/item/projectile/proc/dumbfire(var/dir) - current = get_ranged_target_turf(src, dir, world.maxx) //world.maxx is the range. Not sure how to handle this better. - fire() - - -/obj/item/projectile/proc/on_ricochet(atom/A) - return - -/obj/item/projectile/proc/check_ricochet() - if(prob(ricochet_chance)) - return TRUE - return FALSE - -/obj/item/projectile/proc/check_ricochet_flag(atom/A) - if(A.flags_2 & CHECK_RICOCHET_2) - return TRUE - return FALSE - -/obj/item/projectile/proc/setAngle(new_angle) //wrapper for overrides. - Angle = new_angle - return TRUE - -/obj/item/projectile/experience_pressure_difference() - return \ No newline at end of file +/obj/item/projectile + name = "projectile" + icon = 'icons/obj/projectiles.dmi' + icon_state = "bullet" + density = 0 + resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + anchored = 1 //There's a reason this is here, Mport. God fucking damn it -Agouri. Find&Fix by Pete. The reason this is here is to stop the curving of emitter shots. + flags = ABSTRACT + pass_flags = PASSTABLE + mouse_opacity = MOUSE_OPACITY_TRANSPARENT + hitsound = 'sound/weapons/pierce.ogg' + var/hitsound_wall = "" + var/def_zone = "" //Aiming at + var/mob/firer = null//Who shot it + var/obj/item/ammo_casing/ammo_casing = null + var/suppressed = 0 //Attack message + var/yo = null + var/xo = null + var/current = null + var/atom/original = null // the original target clicked + var/turf/starting = null // the projectile's starting turf + var/list/permutated = list() // we've passed through these atoms, don't try to hit them again + var/paused = FALSE //for suspending the projectile midair + var/p_x = 16 + var/p_y = 16 // the pixel location of the tile that the player clicked. Default is the center + var/speed = 1 //Amount of deciseconds it takes for projectile to travel + var/Angle = null + var/spread = 0 //amount (in degrees) of projectile spread + var/legacy = FALSE //legacy projectile system + animate_movement = 0 + + var/ignore_source_check = FALSE + + var/damage = 10 + var/tile_dropoff = 0 //how much damage should be decremented as the bullet moves + var/tile_dropoff_s = 0 //same as above but for stamina + var/damage_type = BRUTE //BRUTE, BURN, TOX, OXY, CLONE are the only things that should be in here + var/nodamage = FALSE //Determines if the projectile will skip any damage inflictions + var/flag = "bullet" //Defines what armor to use when it hits things. Must be set to bullet, laser, energy,or bomb //Cael - bio and rad are also valid + var/projectile_type = "/obj/item/projectile" + var/range = 50 //This will de-increment every step. When 0, it will delete the projectile. + var/is_reflectable = FALSE // Can it be reflected or not? + var/alwayslog = FALSE // ALWAYS log this projectile on hit even if it doesn't hit a living target. Useful for AOE explosion / EMP. + //Effects + var/stun = 0 + var/weaken = 0 + var/paralyze = 0 + var/irradiate = 0 + var/stutter = 0 + var/slur = 0 + var/eyeblur = 0 + var/drowsy = 0 + var/stamina = 0 + var/jitter = 0 + var/forcedodge = 0 //to pass through everything + var/dismemberment = 0 //The higher the number, the greater the bonus to dismembering. 0 will not dismember at all. + var/impact_effect_type //what type of impact effect to show when hitting something + var/ricochets = 0 + var/ricochets_max = 2 + var/ricochet_chance = 30 + + var/log_override = FALSE //whether print to admin attack logs or just keep it in the diary + +/obj/item/projectile/New() + permutated = list() + return ..() + +/obj/item/projectile/proc/Range() + range-- + if(damage && tile_dropoff) + damage = max(0, damage - tile_dropoff) // decrement projectile damage based on dropoff value for each tile it moves + if(stamina && tile_dropoff_s) + stamina = max(0, stamina - tile_dropoff_s) // as above, but with stamina + if(range <= 0 && loc) + on_range() + if(!damage && !stamina && (tile_dropoff || tile_dropoff_s)) + on_range() + +/obj/item/projectile/proc/on_range() //if we want there to be effects when they reach the end of their range + qdel(src) + +/obj/item/projectile/proc/prehit(atom/target) + return TRUE + +/obj/item/projectile/proc/on_hit(atom/target, blocked = 0, hit_zone) + var/turf/target_loca = get_turf(target) + var/hitx + var/hity + if(target == original) + hitx = target.pixel_x + p_x - 16 + hity = target.pixel_y + p_y - 16 + else + hitx = target.pixel_x + rand(-8, 8) + hity = target.pixel_y + rand(-8, 8) + if(!nodamage && (damage_type == BRUTE || damage_type == BURN) && iswallturf(target_loca) && prob(75)) + var/turf/simulated/wall/W = target_loca + if(impact_effect_type) + new impact_effect_type(target_loca, hitx, hity) + + W.add_dent(WALL_DENT_SHOT, hitx, hity) + return 0 + if(alwayslog) + add_attack_logs(firer, target, "Shot with a [type]") + if(!isliving(target)) + if(impact_effect_type) + new impact_effect_type(target_loca, hitx, hity) + return 0 + var/mob/living/L = target + var/mob/living/carbon/human/H + if(blocked < 100) // not completely blocked + if(damage && L.blood_volume && damage_type == BRUTE) + var/splatter_dir = dir + if(starting) + splatter_dir = get_dir(starting, target_loca) + if(isalien(L)) + new /obj/effect/temp_visual/dir_setting/bloodsplatter/xenosplatter(target_loca, splatter_dir) + else + var/blood_color = "#C80000" + if(ishuman(target)) + H = target + blood_color = H.dna.species.blood_color + new /obj/effect/temp_visual/dir_setting/bloodsplatter(target_loca, splatter_dir, blood_color) + if(prob(33)) + var/list/shift = list("x" = 0, "y" = 0) + var/turf/step_over = get_step(target_loca, splatter_dir) + + if(get_splatter_blockage(step_over, target, splatter_dir, target_loca)) //If you can't cross the tile or any of its relevant obstacles... + shift = pixel_shift_dir(splatter_dir) //Pixel shift the blood there instead (so you can't see wallsplatter through walls). + else + target_loca = step_over + L.add_splatter_floor(target_loca, shift_x = shift["x"], shift_y = shift["y"]) + if(istype(H)) + for(var/mob/living/carbon/human/M in step_over) //Bloody the mobs who're infront of the spray. + M.bloody_hands(H) + /* Uncomment when bloody_body stops randomly not transferring blood colour. + M.bloody_body(H) */ + else if(impact_effect_type) + new impact_effect_type(target_loca, hitx, hity) + var/organ_hit_text = "" + if(L.has_limbs) + organ_hit_text = " in \the [parse_zone(def_zone)]" + if(suppressed) + playsound(loc, hitsound, 5, 1, -1) + to_chat(L, "You're shot by \a [src][organ_hit_text]!") + else + if(hitsound) + var/volume = vol_by_damage() + playsound(loc, hitsound, volume, 1, -1) + L.visible_message("[L] is hit by \a [src][organ_hit_text]!", \ + "[L] is hit by \a [src][organ_hit_text]!") //X has fired Y is now given by the guns so you cant tell who shot you if you could not see the shooter + + var/reagent_note + var/has_reagents = FALSE + if(reagents && reagents.reagent_list) + reagent_note = " REAGENTS:" + for(var/datum/reagent/R in reagents.reagent_list) + reagent_note += R.id + " (" + reagent_note += num2text(R.volume) + ") " + has_reagents = TRUE + if(!log_override && firer && !alwayslog) + if(has_reagents) + add_attack_logs(firer, L, "Shot with a [type] (containing [reagent_note])") + else + add_attack_logs(firer, L, "Shot with a [type]") + return L.apply_effects(stun, weaken, paralyze, irradiate, slur, stutter, eyeblur, drowsy, blocked, stamina, jitter) + +/obj/item/projectile/proc/get_splatter_blockage(var/turf/step_over, var/atom/target, var/splatter_dir, var/target_loca) //Check whether the place we want to splatter blood is blocked (i.e. by windows). + var/turf/step_cardinal = !(splatter_dir in list(NORTH, SOUTH, EAST, WEST)) ? get_step(target_loca, get_cardinal_dir(target_loca, step_over)) : null + + if(step_over.density && !step_over.CanPass(target, step_over, 1)) //Preliminary simple check. + return TRUE + for(var/atom/movable/border_obstacle in step_over) //Check to see if we're blocked by a (non-full) window or some such. Do deeper investigation if we're splattering blood diagonally. + if(border_obstacle.flags&ON_BORDER && get_dir(step_cardinal ? step_cardinal : target_loca, step_over) == turn(border_obstacle.dir, 180)) + return TRUE + +/obj/item/projectile/proc/vol_by_damage() + if(damage) + return Clamp((damage) * 0.67, 30, 100)// Multiply projectile damage by 0.67, then clamp the value between 30 and 100 + else + return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume + +/obj/item/projectile/Bump(atom/A, yes) + if(!yes) //prevents double bumps. + return + + if(check_ricochet(A) && check_ricochet_flag(A) && ricochets < ricochets_max) + ricochets++ + if(A.handle_ricochet(src)) + on_ricochet(A) + ignore_source_check = TRUE + range = initial(range) + return TRUE + if(firer && !ignore_source_check) + if(A == firer || (A == firer.loc && ismecha(A))) //cannot shoot yourself or your mech + loc = A.loc + return 0 + + var/distance = get_dist(get_turf(A), starting) // Get the distance between the turf shot from and the mob we hit and use that for the calculations. + def_zone = ran_zone(def_zone, max(100-(7*distance), 5)) //Lower accurancy/longer range tradeoff. 7 is a balanced number to use. + + if(isturf(A) && hitsound_wall) + var/volume = Clamp(vol_by_damage() + 20, 0, 100) + if(suppressed) + volume = 5 + playsound(loc, hitsound_wall, volume, 1, -1) + else if(ishuman(A)) + var/mob/living/carbon/human/H = A + var/obj/item/organ/external/organ = H.get_organ(check_zone(def_zone)) + if(isnull(organ)) + return + + var/turf/target_turf = get_turf(A) + prehit(A) + var/permutation = A.bullet_act(src, def_zone) // searches for return value, could be deleted after run so check A isn't null + if(permutation == -1 || forcedodge)// the bullet passes through a dense object! + loc = target_turf + if(A) + permutated.Add(A) + return 0 + else + if(A && A.density && !ismob(A) && !(A.flags & ON_BORDER)) //if we hit a dense non-border obj or dense turf then we also hit one of the mobs on that tile. + var/list/mobs_list = list() + for(var/mob/living/L in target_turf) + mobs_list += L + if(mobs_list.len) + var/mob/living/picked_mob = pick(mobs_list) + prehit(picked_mob) + picked_mob.bullet_act(src, def_zone) + qdel(src) + +/obj/item/projectile/Process_Spacemove(var/movement_dir = 0) + return 1 //Bullets don't drift in space + +/obj/item/projectile/proc/fire(var/setAngle) + if(setAngle) + Angle = setAngle + if(!legacy) //new projectiles + set waitfor = 0 + while(loc) + if(!paused) + if((!( current ) || loc == current)) + current = locate(Clamp(x+xo,1,world.maxx),Clamp(y+yo,1,world.maxy),z) + if(isnull(Angle)) + Angle=round(Get_Angle(src,current)) + if(spread) + Angle += (rand() - 0.5) * spread + var/matrix/M = new + M.Turn(Angle) + transform = M + + var/Pixel_x=round(sin(Angle)+16*sin(Angle)*2) + var/Pixel_y=round(cos(Angle)+16*cos(Angle)*2) + var/pixel_x_offset = pixel_x + Pixel_x + var/pixel_y_offset = pixel_y + Pixel_y + var/new_x = x + var/new_y = y + + while(pixel_x_offset > 16) + pixel_x_offset -= 32 + pixel_x -= 32 + new_x++// x++ + while(pixel_x_offset < -16) + pixel_x_offset += 32 + pixel_x += 32 + new_x-- + + while(pixel_y_offset > 16) + pixel_y_offset -= 32 + pixel_y -= 32 + new_y++ + while(pixel_y_offset < -16) + pixel_y_offset += 32 + pixel_y += 32 + new_y-- + + speed = round(speed) + step_towards(src, locate(new_x, new_y, z)) + if(speed <= 1) + pixel_x = pixel_x_offset + pixel_y = pixel_y_offset + else + animate(src, pixel_x = pixel_x_offset, pixel_y = pixel_y_offset, time = max(1, (speed <= 3 ? speed - 1 : speed))) + + if(original && (original.layer>=2.75) || ismob(original)) + if(loc == get_turf(original)) + if(!(original in permutated)) + Bump(original, 1) + Range() + sleep(max(1, speed)) + else //old projectile system + set waitfor = 0 + while(loc) + if(!paused) + if((!( current ) || loc == current)) + current = locate(Clamp(x+xo,1,world.maxx),Clamp(y+yo,1,world.maxy),z) + step_towards(src, current) + if(original && (original.layer>=2.75) || ismob(original)) + if(loc == get_turf(original)) + if(!(original in permutated)) + Bump(original, 1) + Range() + sleep(1) + +obj/item/projectile/proc/reflect_back(atom/source, list/position_modifiers = list(0, 0, 0, 0, 0, -1, 1, -2, 2)) + if(starting) + var/new_x = starting.x + pick(position_modifiers) + var/new_y = starting.y + pick(position_modifiers) + var/turf/curloc = get_turf(source) + + if(ismob(source)) + firer = source // The reflecting mob will be the new firer + else + firer = null // Reflected by something other than a mob so firer will be null + + // redirect the projectile + original = locate(new_x, new_y, z) + starting = curloc + current = curloc + yo = new_y - curloc.y + xo = new_x - curloc.x + Angle = null // Will be calculated in fire() + +obj/item/projectile/Crossed(atom/movable/AM, oldloc) //A mob moving on a tile with a projectile is hit by it. + ..() + if(isliving(AM) && AM.density && !checkpass(PASSMOB)) + Bump(AM, 1) + +/obj/item/projectile/Destroy() + ammo_casing = null + return ..() + +/obj/item/projectile/proc/dumbfire(var/dir) + current = get_ranged_target_turf(src, dir, world.maxx) //world.maxx is the range. Not sure how to handle this better. + fire() + + +/obj/item/projectile/proc/on_ricochet(atom/A) + return + +/obj/item/projectile/proc/check_ricochet() + if(prob(ricochet_chance)) + return TRUE + return FALSE + +/obj/item/projectile/proc/check_ricochet_flag(atom/A) + if(A.flags_2 & CHECK_RICOCHET_2) + return TRUE + return FALSE + +/obj/item/projectile/proc/setAngle(new_angle) //wrapper for overrides. + Angle = new_angle + return TRUE + +/obj/item/projectile/experience_pressure_difference() + return diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index 814ac3d7e46..13f959be590 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -1,171 +1,171 @@ -/obj/item/projectile/beam - name = "laser" - icon_state = "laser" - pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE - damage = 20 - damage_type = BURN - hitsound = 'sound/weapons/sear.ogg' - hitsound_wall = 'sound/weapons/effects/searwall.ogg' - flag = "laser" - eyeblur = 2 - impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser - is_reflectable = TRUE - light_range = 2 - light_color = LIGHT_COLOR_RED - ricochets_max = 50 //Honk! - ricochet_chance = 80 - -/obj/item/projectile/beam/laser - -/obj/item/projectile/beam/laser/heavylaser - name = "heavy laser" - icon_state = "heavylaser" - damage = 40 - -/obj/item/projectile/beam/practice - name = "practice laser" - damage = 0 - nodamage = 1 - log_override = TRUE - -/obj/item/projectile/beam/scatter - name = "laser pellet" - icon_state = "scatterlaser" - damage = 5 - -/obj/item/projectile/beam/xray - name = "xray beam" - icon_state = "xray" - damage = 15 - tile_dropoff = 0.75 - irradiate = 30 - forcedodge = 1 - range = 15 - impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser - light_color = LIGHT_COLOR_GREEN - -/obj/item/projectile/beam/disabler - name = "disabler beam" - icon_state = "omnilaser" - damage = 30 - damage_type = STAMINA - flag = "energy" - hitsound = 'sound/weapons/tap.ogg' - eyeblur = 0 - impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser - light_color = LIGHT_COLOR_CYAN - -/obj/item/projectile/beam/pulse - name = "pulse" - icon_state = "u_laser" - damage = 50 - impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser - light_color = LIGHT_COLOR_DARKBLUE - -/obj/item/projectile/beam/pulse/on_hit(var/atom/target, var/blocked = 0) - if(istype(target,/turf/)||istype(target,/obj/structure/)) - target.ex_act(2) - ..() - -/obj/item/projectile/beam/pulse/shot - damage = 40 - -/obj/item/projectile/beam/emitter - name = "emitter beam" - icon_state = "emitter" - damage = 30 - impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser - light_color = LIGHT_COLOR_GREEN - -/obj/item/projectile/beam/emitter/singularity_pull() - return //don't want the emitters to miss - -/obj/item/projectile/beam/lasertag - name = "laser tag beam" - icon_state = "omnilaser" - hitsound = 'sound/weapons/tap.ogg' - nodamage = 1 - damage_type = STAMINA - flag = "laser" - var/suit_types = list(/obj/item/clothing/suit/redtag, /obj/item/clothing/suit/bluetag) - log_override = TRUE - impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser - light_color = LIGHT_COLOR_DARKBLUE - -/obj/item/projectile/beam/lasertag/on_hit(atom/target, blocked = 0) - . = ..() - if(ishuman(target)) - var/mob/living/carbon/human/M = target - if(istype(M.wear_suit)) - if(M.wear_suit.type in suit_types) - M.adjustStaminaLoss(34) - return 1 - -/obj/item/projectile/beam/lasertag/omni - name = "laser tag beam" - icon_state = "omnilaser" - -/obj/item/projectile/beam/lasertag/redtag - icon_state = "laser" - suit_types = list(/obj/item/clothing/suit/bluetag) - impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser - light_color = LIGHT_COLOR_RED - -/obj/item/projectile/beam/lasertag/bluetag - icon_state = "bluelaser" - suit_types = list(/obj/item/clothing/suit/redtag) - -/obj/item/projectile/beam/sniper - name = "sniper beam" - icon_state = "sniperlaser" - damage = 60 - stun = 5 - weaken = 5 - stutter = 5 - impact_effect_type = /obj/effect/temp_visual/impact_effect/purple_laser - light_color = LIGHT_COLOR_PINK - -/obj/item/projectile/beam/immolator - name = "immolation beam" - -/obj/item/projectile/beam/immolator/strong - name = "heavy immolation beam" - damage = 45 - icon_state = "heavylaser" - -/obj/item/projectile/beam/immolator/weak - name = "light immolation beam" - damage = 8 - icon_state = "scatterlaser" - -/obj/item/projectile/beam/immolator/on_hit(var/atom/target, var/blocked = 0) - . = ..() - if(istype(target, /mob/living/carbon)) - var/mob/living/carbon/M = target - M.adjust_fire_stacks(1) - M.IgniteMob() - -/obj/item/projectile/beam/instakill - name = "instagib laser" - icon_state = "purple_laser" - damage = 200 - damage_type = BURN - impact_effect_type = /obj/effect/temp_visual/impact_effect/purple_laser - light_color = LIGHT_COLOR_PURPLE - -/obj/item/projectile/beam/instakill/blue - icon_state = "blue_laser" - impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser - light_color = LIGHT_COLOR_DARKBLUE - -/obj/item/projectile/beam/instakill/red - icon_state = "red_laser" - impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser - light_color = LIGHT_COLOR_RED - -/obj/item/projectile/beam/instakill/on_hit(atom/target) - . = ..() - if(isliving(target)) - var/mob/living/L = target - L.visible_message("[L] explodes!") - L.gib() \ No newline at end of file +/obj/item/projectile/beam + name = "laser" + icon_state = "laser" + pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE + damage = 20 + damage_type = BURN + hitsound = 'sound/weapons/sear.ogg' + hitsound_wall = 'sound/weapons/effects/searwall.ogg' + flag = "laser" + eyeblur = 2 + impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser + is_reflectable = TRUE + light_range = 2 + light_color = LIGHT_COLOR_RED + ricochets_max = 50 //Honk! + ricochet_chance = 80 + +/obj/item/projectile/beam/laser + +/obj/item/projectile/beam/laser/heavylaser + name = "heavy laser" + icon_state = "heavylaser" + damage = 40 + +/obj/item/projectile/beam/practice + name = "practice laser" + damage = 0 + nodamage = 1 + log_override = TRUE + +/obj/item/projectile/beam/scatter + name = "laser pellet" + icon_state = "scatterlaser" + damage = 5 + +/obj/item/projectile/beam/xray + name = "xray beam" + icon_state = "xray" + damage = 15 + tile_dropoff = 0.75 + irradiate = 30 + forcedodge = 1 + range = 15 + impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser + light_color = LIGHT_COLOR_GREEN + +/obj/item/projectile/beam/disabler + name = "disabler beam" + icon_state = "omnilaser" + damage = 30 + damage_type = STAMINA + flag = "energy" + hitsound = 'sound/weapons/tap.ogg' + eyeblur = 0 + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + light_color = LIGHT_COLOR_CYAN + +/obj/item/projectile/beam/pulse + name = "pulse" + icon_state = "u_laser" + damage = 50 + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + light_color = LIGHT_COLOR_DARKBLUE + +/obj/item/projectile/beam/pulse/on_hit(var/atom/target, var/blocked = 0) + if(istype(target,/turf/)||istype(target,/obj/structure/)) + target.ex_act(2) + ..() + +/obj/item/projectile/beam/pulse/shot + damage = 40 + +/obj/item/projectile/beam/emitter + name = "emitter beam" + icon_state = "emitter" + damage = 30 + impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser + light_color = LIGHT_COLOR_GREEN + +/obj/item/projectile/beam/emitter/singularity_pull() + return //don't want the emitters to miss + +/obj/item/projectile/beam/lasertag + name = "laser tag beam" + icon_state = "omnilaser" + hitsound = 'sound/weapons/tap.ogg' + nodamage = 1 + damage_type = STAMINA + flag = "laser" + var/suit_types = list(/obj/item/clothing/suit/redtag, /obj/item/clothing/suit/bluetag) + log_override = TRUE + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + light_color = LIGHT_COLOR_DARKBLUE + +/obj/item/projectile/beam/lasertag/on_hit(atom/target, blocked = 0) + . = ..() + if(ishuman(target)) + var/mob/living/carbon/human/M = target + if(istype(M.wear_suit)) + if(M.wear_suit.type in suit_types) + M.adjustStaminaLoss(34) + return 1 + +/obj/item/projectile/beam/lasertag/omni + name = "laser tag beam" + icon_state = "omnilaser" + +/obj/item/projectile/beam/lasertag/redtag + icon_state = "laser" + suit_types = list(/obj/item/clothing/suit/bluetag) + impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser + light_color = LIGHT_COLOR_RED + +/obj/item/projectile/beam/lasertag/bluetag + icon_state = "bluelaser" + suit_types = list(/obj/item/clothing/suit/redtag) + +/obj/item/projectile/beam/sniper + name = "sniper beam" + icon_state = "sniperlaser" + damage = 60 + stun = 5 + weaken = 5 + stutter = 5 + impact_effect_type = /obj/effect/temp_visual/impact_effect/purple_laser + light_color = LIGHT_COLOR_PINK + +/obj/item/projectile/beam/immolator + name = "immolation beam" + +/obj/item/projectile/beam/immolator/strong + name = "heavy immolation beam" + damage = 45 + icon_state = "heavylaser" + +/obj/item/projectile/beam/immolator/weak + name = "light immolation beam" + damage = 8 + icon_state = "scatterlaser" + +/obj/item/projectile/beam/immolator/on_hit(var/atom/target, var/blocked = 0) + . = ..() + if(istype(target, /mob/living/carbon)) + var/mob/living/carbon/M = target + M.adjust_fire_stacks(1) + M.IgniteMob() + +/obj/item/projectile/beam/instakill + name = "instagib laser" + icon_state = "purple_laser" + damage = 200 + damage_type = BURN + impact_effect_type = /obj/effect/temp_visual/impact_effect/purple_laser + light_color = LIGHT_COLOR_PURPLE + +/obj/item/projectile/beam/instakill/blue + icon_state = "blue_laser" + impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser + light_color = LIGHT_COLOR_DARKBLUE + +/obj/item/projectile/beam/instakill/red + icon_state = "red_laser" + impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser + light_color = LIGHT_COLOR_RED + +/obj/item/projectile/beam/instakill/on_hit(atom/target) + . = ..() + if(isliving(target)) + var/mob/living/L = target + L.visible_message("[L] explodes!") + L.gib() diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm index 6303178217c..2323aa06cf4 100644 --- a/code/modules/projectiles/projectile/bullets.dm +++ b/code/modules/projectiles/projectile/bullets.dm @@ -1,300 +1,300 @@ -/obj/item/projectile/bullet - name = "bullet" - icon_state = "bullet" - damage = 60 - damage_type = BRUTE - flag = "bullet" - hitsound_wall = "ricochet" - impact_effect_type = /obj/effect/temp_visual/impact_effect - -/obj/item/projectile/bullet/weakbullet //beanbag, heavy stamina damage - name = "beanbag slug" - damage = 5 - stamina = 80 - -/obj/item/projectile/bullet/weakbullet/booze - -/obj/item/projectile/bullet/weakbullet/booze/on_hit(atom/target, blocked = 0) - if(..(target, blocked)) - var/mob/living/M = target - M.AdjustDizzy(20) - M.AdjustSlur(20) - M.AdjustConfused(20) - M.AdjustEyeBlurry(20) - M.AdjustDrowsy(20) - for(var/datum/reagent/consumable/ethanol/A in M.reagents.reagent_list) - M.AdjustParalysis(2) - M.AdjustDizzy(10) - M.AdjustSlur(10) - M.AdjustConfused(10) - M.AdjustEyeBlurry(10) - M.AdjustDrowsy(10) - A.volume += 5 //Because we can - -/obj/item/projectile/bullet/weakbullet2 //detective revolver instastuns, but multiple shots are better for keeping punks down - name = "rubber bullet" - damage = 5 - weaken = 3 - stamina = 60 - icon_state = "bullet-r" - -/obj/item/projectile/bullet/weakbullet2/invisible //finger gun bullets - name = "invisible bullet" - damage = 0 - icon_state = null - hitsound_wall = null - -/obj/item/projectile/bullet/weakbullet2/invisible/fake - weaken = 0 - stamina = 0 - nodamage = 1 - log_override = TRUE - -/obj/item/projectile/bullet/weakbullet3 - damage = 20 - -/obj/item/projectile/bullet/weakbullet4 - name = "rubber bullet" - damage = 5 - stamina = 30 - icon_state = "bullet-r" - -/obj/item/projectile/bullet/toxinbullet - damage = 15 - damage_type = TOX - -/obj/item/projectile/bullet/incendiary - -/obj/item/projectile/bullet/incendiary/on_hit(var/atom/target, var/blocked = 0) - . = ..() - if(iscarbon(target)) - var/mob/living/carbon/M = target - M.adjust_fire_stacks(4) - M.IgniteMob() - -/obj/item/projectile/bullet/incendiary/firebullet - damage = 10 - -/obj/item/projectile/bullet/armourpiercing - damage = 17 - armour_penetration = 10 - -/obj/item/projectile/bullet/pellet - name = "pellet" - damage = 12.5 - tile_dropoff = 0.75 - tile_dropoff_s = 1.25 - -/obj/item/projectile/bullet/pellet/rubber - name = "rubber pellet" - damage = 3 - stamina = 25 - icon_state = "bullet-r" - -/obj/item/projectile/bullet/pellet/weak - tile_dropoff = 0.55 //Come on it does 6 damage don't be like that. - damage = 6 - -/obj/item/projectile/bullet/pellet/weak/New() - range = rand(1, 8) - ..() - -/obj/item/projectile/bullet/pellet/weak/on_range() - do_sparks(1, 1, src) - ..() - -/obj/item/projectile/bullet/pellet/overload - damage = 3 - -/obj/item/projectile/bullet/pellet/overload/New() - range = rand(1, 10) - ..() - -/obj/item/projectile/bullet/pellet/assassination - damage = 12 - tile_dropoff = 1 // slightly less damage and greater damage falloff compared to normal buckshot - -/obj/item/projectile/bullet/pellet/assassination/on_hit(atom/target, blocked = 0) - if(..(target, blocked)) - var/mob/living/M = target - M.AdjustSilence(2) // HELP MIME KILLING ME IN MAINT - -/obj/item/projectile/bullet/pellet/overload/on_hit(atom/target, blocked = 0) - ..() - explosion(target, 0, 0, 2) - -/obj/item/projectile/bullet/pellet/overload/on_range() - explosion(src, 0, 0, 2) - do_sparks(3, 3, src) - ..() - -/obj/item/projectile/bullet/midbullet - damage = 20 - stamina = 65 //two rounds from the c20r knocks people down - -/obj/item/projectile/bullet/midbullet_r - damage = 5 - stamina = 75 //Still two rounds to knock people down - -/obj/item/projectile/bullet/midbullet2 - damage = 25 - -/obj/item/projectile/bullet/midbullet3 - damage = 30 - -/obj/item/projectile/bullet/midbullet3/hp - damage = 40 - armour_penetration = -50 - -/obj/item/projectile/bullet/midbullet3/ap - damage = 27 - armour_penetration = 40 - -/obj/item/projectile/bullet/midbullet3/fire/on_hit(atom/target, blocked = 0) - if(..(target, blocked)) - var/mob/living/M = target - M.adjust_fire_stacks(1) - M.IgniteMob() - -/obj/item/projectile/bullet/heavybullet - damage = 35 - -/obj/item/projectile/bullet/stunshot//taser slugs for shotguns, nothing special - name = "stunshot" - damage = 5 - stun = 5 - weaken = 5 - stutter = 5 - jitter = 20 - range = 7 - icon_state = "spark" - color = "#FFFF00" - -/obj/item/projectile/bullet/incendiary/shell - name = "incendiary slug" - damage = 20 - -/obj/item/projectile/bullet/incendiary/shell/Move() - ..() - var/turf/location = get_turf(src) - if(location) - new /obj/effect/hotspot(location) - location.hotspot_expose(700, 50, 1) - -/obj/item/projectile/bullet/incendiary/shell/dragonsbreath - name = "dragonsbreath round" - damage = 5 - -/obj/item/projectile/bullet/meteorshot - name = "meteor" - icon = 'icons/obj/meteor.dmi' - icon_state = "dust" - damage = 30 - weaken = 8 - stun = 8 - hitsound = 'sound/effects/meteorimpact.ogg' - -/obj/item/projectile/bullet/meteorshot/on_hit(var/atom/target, var/blocked = 0) - ..() - if(istype(target, /atom/movable)) - var/atom/movable/M = target - var/atom/throw_target = get_edge_target_turf(M, get_dir(src, get_step_away(M, src))) - M.throw_at(throw_target, 3, 2) - -/obj/item/projectile/bullet/meteorshot/New() - ..() - SpinAnimation() - -/obj/item/projectile/bullet/meteorshot/weak - damage = 10 - weaken = 4 - stun = 4 - -/obj/item/projectile/bullet/mime - damage = 0 - stun = 5 - weaken = 5 - slur = 20 - stutter = 20 - -/obj/item/projectile/bullet/mime/on_hit(var/atom/target, var/blocked = 0) - ..(target, blocked) - if(iscarbon(target)) - var/mob/living/carbon/M = target - M.Silence(10) - else if(istype(target, /obj/mecha/combat/honker)) - var/obj/mecha/chassis = target - chassis.occupant_message("A mimetech anti-honk bullet has hit \the [chassis]!") - chassis.use_power(chassis.get_charge() / 2) - for(var/obj/item/mecha_parts/mecha_equipment/weapon/honker in chassis.equipment) - honker.set_ready_state(0) - -/obj/item/projectile/bullet/dart - name = "dart" - icon_state = "cbbolt" - damage = 6 - var/piercing = FALSE - -/obj/item/projectile/bullet/dart/New() - ..() - create_reagents(50) - reagents.set_reacting(FALSE) - -/obj/item/projectile/bullet/dart/on_hit(var/atom/target, var/blocked = 0, var/hit_zone) - if(iscarbon(target)) - var/mob/living/carbon/M = target - if(blocked != 100) - if(M.can_inject(null, FALSE, hit_zone, piercing)) // Pass the hit zone to see if it can inject by whether it hit the head or the body. - ..() - reagents.trans_to(M, reagents.total_volume) - return 1 - else - blocked = 100 - target.visible_message("The [name] was deflected!", \ - "You were protected against the [name]!") - ..(target, blocked, hit_zone) - reagents.set_reacting(TRUE) - reagents.handle_reactions() - return 1 - -/obj/item/projectile/bullet/dart/metalfoam - -/obj/item/projectile/bullet/dart/metalfoam/New() - ..() - reagents.add_reagent("aluminum", 15) - reagents.add_reagent("fluorosurfactant", 5) - reagents.add_reagent("sacid", 5) - -//This one is for future syringe guns update -/obj/item/projectile/bullet/dart/syringe - name = "syringe" - icon = 'icons/obj/chemical.dmi' - icon_state = "syringeproj" - -/obj/item/projectile/bullet/dart/syringe/tranquilizer - -/obj/item/projectile/bullet/dart/syringe/tranquilizer/New() - ..() - reagents.add_reagent("haloperidol", 15) - -/obj/item/projectile/bullet/neurotoxin - name = "neurotoxin spit" - icon_state = "neurotoxin" - damage = 5 - damage_type = TOX - weaken = 5 - -/obj/item/projectile/bullet/neurotoxin/on_hit(var/atom/target, var/blocked = 0) - if(isalien(target)) - weaken = 0 - nodamage = 1 - . = ..() // Execute the rest of the code. - -/obj/item/projectile/bullet/cap - name = "cap" - damage = 0 - nodamage = 1 - -/obj/item/projectile/bullet/cap/fire() - loc = null - qdel(src) +/obj/item/projectile/bullet + name = "bullet" + icon_state = "bullet" + damage = 60 + damage_type = BRUTE + flag = "bullet" + hitsound_wall = "ricochet" + impact_effect_type = /obj/effect/temp_visual/impact_effect + +/obj/item/projectile/bullet/weakbullet //beanbag, heavy stamina damage + name = "beanbag slug" + damage = 5 + stamina = 80 + +/obj/item/projectile/bullet/weakbullet/booze + +/obj/item/projectile/bullet/weakbullet/booze/on_hit(atom/target, blocked = 0) + if(..(target, blocked)) + var/mob/living/M = target + M.AdjustDizzy(20) + M.AdjustSlur(20) + M.AdjustConfused(20) + M.AdjustEyeBlurry(20) + M.AdjustDrowsy(20) + for(var/datum/reagent/consumable/ethanol/A in M.reagents.reagent_list) + M.AdjustParalysis(2) + M.AdjustDizzy(10) + M.AdjustSlur(10) + M.AdjustConfused(10) + M.AdjustEyeBlurry(10) + M.AdjustDrowsy(10) + A.volume += 5 //Because we can + +/obj/item/projectile/bullet/weakbullet2 //detective revolver instastuns, but multiple shots are better for keeping punks down + name = "rubber bullet" + damage = 5 + weaken = 3 + stamina = 60 + icon_state = "bullet-r" + +/obj/item/projectile/bullet/weakbullet2/invisible //finger gun bullets + name = "invisible bullet" + damage = 0 + icon_state = null + hitsound_wall = null + +/obj/item/projectile/bullet/weakbullet2/invisible/fake + weaken = 0 + stamina = 0 + nodamage = 1 + log_override = TRUE + +/obj/item/projectile/bullet/weakbullet3 + damage = 20 + +/obj/item/projectile/bullet/weakbullet4 + name = "rubber bullet" + damage = 5 + stamina = 30 + icon_state = "bullet-r" + +/obj/item/projectile/bullet/toxinbullet + damage = 15 + damage_type = TOX + +/obj/item/projectile/bullet/incendiary + +/obj/item/projectile/bullet/incendiary/on_hit(var/atom/target, var/blocked = 0) + . = ..() + if(iscarbon(target)) + var/mob/living/carbon/M = target + M.adjust_fire_stacks(4) + M.IgniteMob() + +/obj/item/projectile/bullet/incendiary/firebullet + damage = 10 + +/obj/item/projectile/bullet/armourpiercing + damage = 17 + armour_penetration = 10 + +/obj/item/projectile/bullet/pellet + name = "pellet" + damage = 12.5 + tile_dropoff = 0.75 + tile_dropoff_s = 1.25 + +/obj/item/projectile/bullet/pellet/rubber + name = "rubber pellet" + damage = 3 + stamina = 25 + icon_state = "bullet-r" + +/obj/item/projectile/bullet/pellet/weak + tile_dropoff = 0.55 //Come on it does 6 damage don't be like that. + damage = 6 + +/obj/item/projectile/bullet/pellet/weak/New() + range = rand(1, 8) + ..() + +/obj/item/projectile/bullet/pellet/weak/on_range() + do_sparks(1, 1, src) + ..() + +/obj/item/projectile/bullet/pellet/overload + damage = 3 + +/obj/item/projectile/bullet/pellet/overload/New() + range = rand(1, 10) + ..() + +/obj/item/projectile/bullet/pellet/assassination + damage = 12 + tile_dropoff = 1 // slightly less damage and greater damage falloff compared to normal buckshot + +/obj/item/projectile/bullet/pellet/assassination/on_hit(atom/target, blocked = 0) + if(..(target, blocked)) + var/mob/living/M = target + M.AdjustSilence(2) // HELP MIME KILLING ME IN MAINT + +/obj/item/projectile/bullet/pellet/overload/on_hit(atom/target, blocked = 0) + ..() + explosion(target, 0, 0, 2) + +/obj/item/projectile/bullet/pellet/overload/on_range() + explosion(src, 0, 0, 2) + do_sparks(3, 3, src) + ..() + +/obj/item/projectile/bullet/midbullet + damage = 20 + stamina = 65 //two rounds from the c20r knocks people down + +/obj/item/projectile/bullet/midbullet_r + damage = 5 + stamina = 75 //Still two rounds to knock people down + +/obj/item/projectile/bullet/midbullet2 + damage = 25 + +/obj/item/projectile/bullet/midbullet3 + damage = 30 + +/obj/item/projectile/bullet/midbullet3/hp + damage = 40 + armour_penetration = -50 + +/obj/item/projectile/bullet/midbullet3/ap + damage = 27 + armour_penetration = 40 + +/obj/item/projectile/bullet/midbullet3/fire/on_hit(atom/target, blocked = 0) + if(..(target, blocked)) + var/mob/living/M = target + M.adjust_fire_stacks(1) + M.IgniteMob() + +/obj/item/projectile/bullet/heavybullet + damage = 35 + +/obj/item/projectile/bullet/stunshot//taser slugs for shotguns, nothing special + name = "stunshot" + damage = 5 + stun = 5 + weaken = 5 + stutter = 5 + jitter = 20 + range = 7 + icon_state = "spark" + color = "#FFFF00" + +/obj/item/projectile/bullet/incendiary/shell + name = "incendiary slug" + damage = 20 + +/obj/item/projectile/bullet/incendiary/shell/Move() + ..() + var/turf/location = get_turf(src) + if(location) + new /obj/effect/hotspot(location) + location.hotspot_expose(700, 50, 1) + +/obj/item/projectile/bullet/incendiary/shell/dragonsbreath + name = "dragonsbreath round" + damage = 5 + +/obj/item/projectile/bullet/meteorshot + name = "meteor" + icon = 'icons/obj/meteor.dmi' + icon_state = "dust" + damage = 30 + weaken = 8 + stun = 8 + hitsound = 'sound/effects/meteorimpact.ogg' + +/obj/item/projectile/bullet/meteorshot/on_hit(var/atom/target, var/blocked = 0) + ..() + if(istype(target, /atom/movable)) + var/atom/movable/M = target + var/atom/throw_target = get_edge_target_turf(M, get_dir(src, get_step_away(M, src))) + M.throw_at(throw_target, 3, 2) + +/obj/item/projectile/bullet/meteorshot/New() + ..() + SpinAnimation() + +/obj/item/projectile/bullet/meteorshot/weak + damage = 10 + weaken = 4 + stun = 4 + +/obj/item/projectile/bullet/mime + damage = 0 + stun = 5 + weaken = 5 + slur = 20 + stutter = 20 + +/obj/item/projectile/bullet/mime/on_hit(var/atom/target, var/blocked = 0) + ..(target, blocked) + if(iscarbon(target)) + var/mob/living/carbon/M = target + M.Silence(10) + else if(istype(target, /obj/mecha/combat/honker)) + var/obj/mecha/chassis = target + chassis.occupant_message("A mimetech anti-honk bullet has hit \the [chassis]!") + chassis.use_power(chassis.get_charge() / 2) + for(var/obj/item/mecha_parts/mecha_equipment/weapon/honker in chassis.equipment) + honker.set_ready_state(0) + +/obj/item/projectile/bullet/dart + name = "dart" + icon_state = "cbbolt" + damage = 6 + var/piercing = FALSE + +/obj/item/projectile/bullet/dart/New() + ..() + create_reagents(50) + reagents.set_reacting(FALSE) + +/obj/item/projectile/bullet/dart/on_hit(var/atom/target, var/blocked = 0, var/hit_zone) + if(iscarbon(target)) + var/mob/living/carbon/M = target + if(blocked != 100) + if(M.can_inject(null, FALSE, hit_zone, piercing)) // Pass the hit zone to see if it can inject by whether it hit the head or the body. + ..() + reagents.trans_to(M, reagents.total_volume) + return 1 + else + blocked = 100 + target.visible_message("The [name] was deflected!", \ + "You were protected against the [name]!") + ..(target, blocked, hit_zone) + reagents.set_reacting(TRUE) + reagents.handle_reactions() + return 1 + +/obj/item/projectile/bullet/dart/metalfoam + +/obj/item/projectile/bullet/dart/metalfoam/New() + ..() + reagents.add_reagent("aluminum", 15) + reagents.add_reagent("fluorosurfactant", 5) + reagents.add_reagent("sacid", 5) + +//This one is for future syringe guns update +/obj/item/projectile/bullet/dart/syringe + name = "syringe" + icon = 'icons/obj/chemical.dmi' + icon_state = "syringeproj" + +/obj/item/projectile/bullet/dart/syringe/tranquilizer + +/obj/item/projectile/bullet/dart/syringe/tranquilizer/New() + ..() + reagents.add_reagent("haloperidol", 15) + +/obj/item/projectile/bullet/neurotoxin + name = "neurotoxin spit" + icon_state = "neurotoxin" + damage = 5 + damage_type = TOX + weaken = 5 + +/obj/item/projectile/bullet/neurotoxin/on_hit(var/atom/target, var/blocked = 0) + if(isalien(target)) + weaken = 0 + nodamage = 1 + . = ..() // Execute the rest of the code. + +/obj/item/projectile/bullet/cap + name = "cap" + damage = 0 + nodamage = 1 + +/obj/item/projectile/bullet/cap/fire() + loc = null + qdel(src) diff --git a/code/modules/projectiles/projectile/energy.dm b/code/modules/projectiles/projectile/energy.dm index e1ca470c95a..1781e1970eb 100644 --- a/code/modules/projectiles/projectile/energy.dm +++ b/code/modules/projectiles/projectile/energy.dm @@ -1,97 +1,97 @@ -/obj/item/projectile/energy - name = "energy" - icon_state = "spark" - damage = 0 - damage_type = BURN - flag = "energy" - is_reflectable = TRUE - -/obj/item/projectile/energy/electrode - name = "electrode" - icon_state = "spark" - color = "#FFFF00" - nodamage = 1 - stun = 5 - weaken = 5 - stutter = 5 - jitter = 20 - hitsound = 'sound/weapons/tase.ogg' - range = 7 - //Damage will be handled on the MOB side, to prevent window shattering. - -/obj/item/projectile/energy/electrode/on_hit(var/atom/target, var/blocked = 0) - . = ..() - if(!ismob(target) || blocked >= 100) //Fully blocked by mob or collided with dense object - burst into sparks! - do_sparks(1, 1, src) - else if(iscarbon(target)) - var/mob/living/carbon/C = target - if(HULK in C.mutations) - C.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" )) - else if(C.status_flags & CANWEAKEN) - spawn(5) - C.do_jitter_animation(jitter) - -/obj/item/projectile/energy/electrode/on_range() //to ensure the bolt sparks when it reaches the end of its range if it didn't hit a target yet - do_sparks(1, 1, src) - ..() - -/obj/item/projectile/energy/declone - name = "declone" - icon_state = "declone" - damage = 20 - damage_type = CLONE - irradiate = 10 - impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser - -/obj/item/projectile/energy/dart - name = "dart" - icon_state = "toxin" - damage = 5 - damage_type = TOX - weaken = 5 - range = 7 - -/obj/item/projectile/energy/shuriken - name = "shuriken" - icon_state = "toxin" - damage = 10 - damage_type = TOX - weaken = 5 - stutter = 5 - -/obj/item/projectile/energy/bolt - name = "bolt" - icon_state = "cbbolt" - damage = 15 - damage_type = TOX - nodamage = 0 - weaken = 5 - stutter = 5 - -/obj/item/projectile/energy/bolt/large - damage = 20 - -/obj/item/projectile/energy/shock_revolver - name = "shock bolt" - icon_state = "purple_laser" - impact_effect_type = /obj/effect/temp_visual/impact_effect/purple_laser - var/chain - -/obj/item/ammo_casing/energy/shock_revolver/ready_proj(atom/target, mob/living/user, quiet, zone_override = "") - ..() - var/obj/item/projectile/energy/shock_revolver/P = BB - spawn(1) - P.chain = P.Beam(user,icon_state="purple_lightning",icon = 'icons/effects/effects.dmi',time=1000, maxdistance = 30) - -/obj/item/projectile/energy/shock_revolver/on_hit(atom/target) - . = ..() - if(isliving(target)) - tesla_zap(src, 3, 10000) - qdel(chain) - -/obj/item/projectile/energy/toxplasma - name = "plasma bolt" - icon_state = "energy" - damage = 20 - damage_type = TOX - irradiate = 20 +/obj/item/projectile/energy + name = "energy" + icon_state = "spark" + damage = 0 + damage_type = BURN + flag = "energy" + is_reflectable = TRUE + +/obj/item/projectile/energy/electrode + name = "electrode" + icon_state = "spark" + color = "#FFFF00" + nodamage = 1 + stun = 5 + weaken = 5 + stutter = 5 + jitter = 20 + hitsound = 'sound/weapons/tase.ogg' + range = 7 + //Damage will be handled on the MOB side, to prevent window shattering. + +/obj/item/projectile/energy/electrode/on_hit(var/atom/target, var/blocked = 0) + . = ..() + if(!ismob(target) || blocked >= 100) //Fully blocked by mob or collided with dense object - burst into sparks! + do_sparks(1, 1, src) + else if(iscarbon(target)) + var/mob/living/carbon/C = target + if(HULK in C.mutations) + C.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" )) + else if(C.status_flags & CANWEAKEN) + spawn(5) + C.do_jitter_animation(jitter) + +/obj/item/projectile/energy/electrode/on_range() //to ensure the bolt sparks when it reaches the end of its range if it didn't hit a target yet + do_sparks(1, 1, src) + ..() + +/obj/item/projectile/energy/declone + name = "declone" + icon_state = "declone" + damage = 20 + damage_type = CLONE + irradiate = 10 + impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser + +/obj/item/projectile/energy/dart + name = "dart" + icon_state = "toxin" + damage = 5 + damage_type = TOX + weaken = 5 + range = 7 + +/obj/item/projectile/energy/shuriken + name = "shuriken" + icon_state = "toxin" + damage = 10 + damage_type = TOX + weaken = 5 + stutter = 5 + +/obj/item/projectile/energy/bolt + name = "bolt" + icon_state = "cbbolt" + damage = 15 + damage_type = TOX + nodamage = 0 + weaken = 5 + stutter = 5 + +/obj/item/projectile/energy/bolt/large + damage = 20 + +/obj/item/projectile/energy/shock_revolver + name = "shock bolt" + icon_state = "purple_laser" + impact_effect_type = /obj/effect/temp_visual/impact_effect/purple_laser + var/chain + +/obj/item/ammo_casing/energy/shock_revolver/ready_proj(atom/target, mob/living/user, quiet, zone_override = "") + ..() + var/obj/item/projectile/energy/shock_revolver/P = BB + spawn(1) + P.chain = P.Beam(user,icon_state="purple_lightning",icon = 'icons/effects/effects.dmi',time=1000, maxdistance = 30) + +/obj/item/projectile/energy/shock_revolver/on_hit(atom/target) + . = ..() + if(isliving(target)) + tesla_zap(src, 3, 10000) + qdel(chain) + +/obj/item/projectile/energy/toxplasma + name = "plasma bolt" + icon_state = "energy" + damage = 20 + damage_type = TOX + irradiate = 20 diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index 76d7c513bcc..01503fb16b7 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -344,4 +344,4 @@ to_chat(target, "You get splatted by [src].") M.Weaken(slip_weaken) M.Stun(slip_stun) - . = ..() \ No newline at end of file + . = ..() diff --git a/code/modules/projectiles/projectile/reusable.dm b/code/modules/projectiles/projectile/reusable.dm index bd249037946..244100c3120 100644 --- a/code/modules/projectiles/projectile/reusable.dm +++ b/code/modules/projectiles/projectile/reusable.dm @@ -77,4 +77,4 @@ icon_state = "foamdartsniper_riot" ammo_type = /obj/item/ammo_casing/caseless/foam_dart/sniper/riot stamina = 100 - log_override = FALSE \ No newline at end of file + log_override = FALSE diff --git a/code/modules/projectiles/projectile/special.dm b/code/modules/projectiles/projectile/special.dm index 06ab5c9a61b..1f82155b7ee 100644 --- a/code/modules/projectiles/projectile/special.dm +++ b/code/modules/projectiles/projectile/special.dm @@ -1,348 +1,348 @@ -/obj/item/projectile/ion - name = "ion bolt" - icon_state = "ion" - damage = 0 - alwayslog = TRUE - damage_type = BURN - nodamage = 1 - impact_effect_type = /obj/effect/temp_visual/impact_effect/ion - flag = "energy" - -/obj/item/projectile/ion/on_hit(var/atom/target, var/blocked = 0) - ..() - empulse(target, 1, 1, 1, cause = "[type] fired by [key_name(firer)]") - return 1 - -/obj/item/projectile/ion/weak - -/obj/item/projectile/ion/weak/on_hit(atom/target, blocked = 0) - ..() - empulse(target, 0, 0, 1, cause = "[type] fired by [key_name(firer)]") - return 1 - -/obj/item/projectile/bullet/gyro - name ="explosive bolt" - icon_state= "bolter" - damage = 50 - alwayslog = TRUE - flag = "bullet" - -/obj/item/projectile/bullet/gyro/on_hit(var/atom/target, var/blocked = 0) - ..() - explosion(target, -1, 0, 2, cause = "[type] fired by [key_name(firer)]") - return 1 - -/obj/item/projectile/bullet/a40mm - name ="40mm grenade" - desc = "USE A WEEL GUN" - icon_state= "bolter" - alwayslog = TRUE - damage = 60 - flag = "bullet" - -/obj/item/projectile/bullet/a40mm/on_hit(atom/target, blocked = 0) - ..() - explosion(target, -1, 0, 2, 1, 0, flame_range = 3, cause = "[type] fired by [key_name(firer)]") - return 1 - -/obj/item/projectile/temp - name = "temperature beam" - icon_state = "temp_4" - damage = 0 - damage_type = BURN - nodamage = 1 - flag = "energy" - var/temperature = 300 - pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE - -/obj/item/projectile/temp/New(loc, shot_temp) - ..() - if(!isnull(shot_temp)) - temperature = shot_temp - switch(temperature) - if(501 to INFINITY) - name = "searing beam" //if emagged - icon_state = "temp_8" - if(400 to 500) - name = "burning beam" //temp at which mobs start taking HEAT_DAMAGE_LEVEL_2 - icon_state = "temp_7" - if(360 to 400) - name = "hot beam" //temp at which mobs start taking HEAT_DAMAGE_LEVEL_1 - icon_state = "temp_6" - if(335 to 360) - name = "warm beam" //temp at which players get notified of their high body temp - icon_state = "temp_5" - if(295 to 335) - name = "ambient beam" - icon_state = "temp_4" - if(260 to 295) - name = "cool beam" //temp at which players get notified of their low body temp - icon_state = "temp_3" - if(200 to 260) - name = "cold beam" //temp at which mobs start taking COLD_DAMAGE_LEVEL_1 - icon_state = "temp_2" - if(120 to 260) - name = "ice beam" //temp at which mobs start taking COLD_DAMAGE_LEVEL_2 - icon_state = "temp_1" - if(-INFINITY to 120) - name = "freeze beam" //temp at which mobs start taking COLD_DAMAGE_LEVEL_3 - icon_state = "temp_0" - else - name = "temperature beam"//failsafe - icon_state = "temp_4" - - -/obj/item/projectile/temp/on_hit(var/atom/target, var/blocked = 0)//These two could likely check temp protection on the mob - ..() - if(isliving(target)) - var/mob/living/M = target - M.bodytemperature = temperature - if(temperature > 500)//emagged - M.adjust_fire_stacks(0.5) - M.IgniteMob() - playsound(M.loc, 'sound/effects/bamf.ogg', 50, 0) - return 1 - -/obj/item/projectile/meteor - name = "meteor" - icon = 'icons/obj/meteor.dmi' - icon_state = "small" - damage = 0 - damage_type = BRUTE - nodamage = 1 - flag = "bullet" - -/obj/item/projectile/meteor/Bump(atom/A, yes) - if(yes) - return - if(A == firer) - loc = A.loc - return - playsound(loc, 'sound/effects/meteorimpact.ogg', 40, 1) - for(var/mob/M in urange(10, src)) - if(!M.stat) - shake_camera(M, 3, 1) - qdel(src) - -/obj/item/projectile/energy/floramut - name = "alpha somatoray" - icon_state = "energy" - damage = 0 - damage_type = TOX - nodamage = 1 - impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser - flag = "energy" - -/obj/item/projectile/energy/floramut/on_hit(var/atom/target, var/blocked = 0) - ..() - var/mob/living/M = target - if(ishuman(target)) - var/mob/living/carbon/human/H = M - if(IS_PLANT in H.dna.species.species_traits) - if(prob(15)) - M.apply_effect((rand(30,80)),IRRADIATE) - M.Weaken(5) - M.visible_message("[M] writhes in pain as [M.p_their()] vacuoles boil.", "You writhe in pain as your vacuoles boil!", "You hear the crunching of leaves.") - if(prob(80)) - randmutb(M) - domutcheck(M,null) - else - randmutg(M) - domutcheck(M,null) - else - M.adjustFireLoss(rand(5,15)) - M.show_message("The radiation beam singes you!") - else if(iscarbon(target)) - M.show_message("The radiation beam dissipates harmlessly through your body.") - else - return 1 - -/obj/item/projectile/energy/florayield - name = "beta somatoray" - icon_state = "energy2" - damage = 0 - damage_type = TOX - nodamage = 1 - flag = "energy" - -/obj/item/projectile/energy/florayield/on_hit(var/atom/target, var/blocked = 0) - ..() - var/mob/M = target - if(ishuman(target)) //These rays make plantmen fat. - var/mob/living/carbon/human/H = M - if(IS_PLANT in H.dna.species.species_traits) - H.set_nutrition(min(H.nutrition+30, NUTRITION_LEVEL_FULL)) - else if(iscarbon(target)) - M.show_message("The radiation beam dissipates harmlessly through your body.") - else - return 1 - - -/obj/item/projectile/beam/mindflayer - name = "flayer ray" - -/obj/item/projectile/beam/mindflayer/on_hit(var/atom/target, var/blocked = 0) - . = ..() - if(ishuman(target)) - var/mob/living/carbon/human/M = target - M.adjustBrainLoss(20) - M.AdjustHallucinate(20) - -/obj/item/projectile/clown - name = "snap-pop" - icon = 'icons/obj/toy.dmi' - icon_state = "snappop" - -/obj/item/projectile/clown/Bump(atom/A as mob|obj|turf|area) - do_sparks(3, 1, src) - new /obj/effect/decal/cleanable/ash(loc) - visible_message("The [name] explodes!","You hear a snap!") - playsound(src, 'sound/effects/snap.ogg', 50, 1) - qdel(src) - -/obj/item/projectile/beam/wormhole - name = "bluespace beam" - icon_state = "spark" - hitsound = "sparks" - damage = 0 - var/obj/item/gun/energy/wormhole_projector/gun - color = "#33CCFF" - nodamage = TRUE - -/obj/item/projectile/beam/wormhole/orange - name = "orange bluespace beam" - color = "#FF6600" - -/obj/item/projectile/beam/wormhole/New(var/obj/item/ammo_casing/energy/wormhole/casing) - if(casing) - gun = casing.gun - -/obj/item/projectile/beam/wormhole/on_hit(atom/target) - if(ismob(target)) - if(is_teleport_allowed(target.z)) - var/turf/portal_destination = pick(orange(6, src)) - do_teleport(target, portal_destination) - return ..() - if(!gun) - qdel(src) - gun.create_portal(src) - -/obj/item/projectile/bullet/frag12 - name ="explosive slug" - damage = 25 - weaken = 5 - alwayslog = TRUE - -/obj/item/projectile/bullet/frag12/on_hit(atom/target, blocked = 0) - ..() - explosion(target, -1, 0, 1) - return 1 - -/obj/item/projectile/plasma - name = "plasma blast" - icon_state = "plasmacutter" - damage_type = BRUTE - damage = 5 - range = 3 - dismemberment = 20 - impact_effect_type = /obj/effect/temp_visual/impact_effect/purple_laser - -/obj/item/projectile/plasma/on_hit(atom/target) - . = ..() - if(ismineralturf(target)) - forcedodge = 1 - var/turf/simulated/mineral/M = target - M.gets_drilled(firer) - else - forcedodge = 0 - -/obj/item/projectile/plasma/adv - damage = 7 - range = 5 - -/obj/item/projectile/plasma/adv/mech - damage = 10 - range = 9 - -/obj/item/projectile/energy/teleport - name = "teleportation burst" - icon_state = "bluespace" - damage = 0 - nodamage = 1 - alwayslog = TRUE - var/teleport_target = null - -/obj/item/projectile/energy/teleport/New(loc, tele_target) - ..(loc) - if(tele_target) - teleport_target = tele_target - -/obj/item/projectile/energy/teleport/on_hit(var/atom/target, var/blocked = 0) - if(isliving(target)) - if(teleport_target) - do_teleport(target, teleport_target, 0)//teleport what's in the tile to the beacon - else - do_teleport(target, target, 15) //Otherwise it just warps you off somewhere. - add_attack_logs(firer, target, "Shot with a [type] [teleport_target ? "(Destination: [teleport_target])" : ""]") - -/obj/item/projectile/snowball - name = "snowball" - icon_state = "snowball" - hitsound = 'sound/items/dodgeball.ogg' - damage = 4 - damage_type = BURN - -/obj/item/projectile/snowball/on_hit(atom/target) //chilling - . = ..() - if(istype(target, /mob/living)) - var/mob/living/M = target - M.bodytemperature = max(0, M.bodytemperature - 50) //each hit will drop your body temp, so don't get surrounded! - M.ExtinguishMob() //bright side, they counter being on fire! - -/obj/item/projectile/ornament - name = "ornament" - icon_state = "ornament-1" - hitsound = 'sound/effects/glasshit.ogg' - damage = 7 - damage_type = BRUTE - -/obj/item/projectile/ornament/New() - icon_state = pick("ornament-1", "ornament-2") - ..() - -/obj/item/projectile/ornament/on_hit(atom/target) //knockback - ..() - if(istype(target, /turf)) - return 0 - var/obj/T = target - var/throwdir = get_dir(firer,target) - T.throw_at(get_edge_target_turf(target, throwdir),10,10) - return 1 - -/obj/item/projectile/mimic - name = "googly-eyed gun" - hitsound = 'sound/weapons/genhit1.ogg' - damage = 0 - nodamage = 1 - damage_type = BURN - flag = "melee" - var/obj/item/gun/stored_gun - -/obj/item/projectile/mimic/New(loc, mimic_type) - ..(loc) - if(mimic_type) - stored_gun = new mimic_type(src) - icon = stored_gun.icon - icon_state = stored_gun.icon_state - overlays = stored_gun.overlays - SpinAnimation(20, -1) - -/obj/item/projectile/mimic/on_hit(atom/target) - ..() - var/turf/T = get_turf(src) - var/obj/item/gun/G = stored_gun - stored_gun = null - G.forceMove(T) - var/mob/living/simple_animal/hostile/mimic/copy/ranged/R = new /mob/living/simple_animal/hostile/mimic/copy/ranged(T, G, firer) - if(ismob(target)) - R.target = target +/obj/item/projectile/ion + name = "ion bolt" + icon_state = "ion" + damage = 0 + alwayslog = TRUE + damage_type = BURN + nodamage = 1 + impact_effect_type = /obj/effect/temp_visual/impact_effect/ion + flag = "energy" + +/obj/item/projectile/ion/on_hit(var/atom/target, var/blocked = 0) + ..() + empulse(target, 1, 1, 1, cause = "[type] fired by [key_name(firer)]") + return 1 + +/obj/item/projectile/ion/weak + +/obj/item/projectile/ion/weak/on_hit(atom/target, blocked = 0) + ..() + empulse(target, 0, 0, 1, cause = "[type] fired by [key_name(firer)]") + return 1 + +/obj/item/projectile/bullet/gyro + name ="explosive bolt" + icon_state= "bolter" + damage = 50 + alwayslog = TRUE + flag = "bullet" + +/obj/item/projectile/bullet/gyro/on_hit(var/atom/target, var/blocked = 0) + ..() + explosion(target, -1, 0, 2, cause = "[type] fired by [key_name(firer)]") + return 1 + +/obj/item/projectile/bullet/a40mm + name ="40mm grenade" + desc = "USE A WEEL GUN" + icon_state= "bolter" + alwayslog = TRUE + damage = 60 + flag = "bullet" + +/obj/item/projectile/bullet/a40mm/on_hit(atom/target, blocked = 0) + ..() + explosion(target, -1, 0, 2, 1, 0, flame_range = 3, cause = "[type] fired by [key_name(firer)]") + return 1 + +/obj/item/projectile/temp + name = "temperature beam" + icon_state = "temp_4" + damage = 0 + damage_type = BURN + nodamage = 1 + flag = "energy" + var/temperature = 300 + pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE + +/obj/item/projectile/temp/New(loc, shot_temp) + ..() + if(!isnull(shot_temp)) + temperature = shot_temp + switch(temperature) + if(501 to INFINITY) + name = "searing beam" //if emagged + icon_state = "temp_8" + if(400 to 500) + name = "burning beam" //temp at which mobs start taking HEAT_DAMAGE_LEVEL_2 + icon_state = "temp_7" + if(360 to 400) + name = "hot beam" //temp at which mobs start taking HEAT_DAMAGE_LEVEL_1 + icon_state = "temp_6" + if(335 to 360) + name = "warm beam" //temp at which players get notified of their high body temp + icon_state = "temp_5" + if(295 to 335) + name = "ambient beam" + icon_state = "temp_4" + if(260 to 295) + name = "cool beam" //temp at which players get notified of their low body temp + icon_state = "temp_3" + if(200 to 260) + name = "cold beam" //temp at which mobs start taking COLD_DAMAGE_LEVEL_1 + icon_state = "temp_2" + if(120 to 260) + name = "ice beam" //temp at which mobs start taking COLD_DAMAGE_LEVEL_2 + icon_state = "temp_1" + if(-INFINITY to 120) + name = "freeze beam" //temp at which mobs start taking COLD_DAMAGE_LEVEL_3 + icon_state = "temp_0" + else + name = "temperature beam"//failsafe + icon_state = "temp_4" + + +/obj/item/projectile/temp/on_hit(var/atom/target, var/blocked = 0)//These two could likely check temp protection on the mob + ..() + if(isliving(target)) + var/mob/living/M = target + M.bodytemperature = temperature + if(temperature > 500)//emagged + M.adjust_fire_stacks(0.5) + M.IgniteMob() + playsound(M.loc, 'sound/effects/bamf.ogg', 50, 0) + return 1 + +/obj/item/projectile/meteor + name = "meteor" + icon = 'icons/obj/meteor.dmi' + icon_state = "small" + damage = 0 + damage_type = BRUTE + nodamage = 1 + flag = "bullet" + +/obj/item/projectile/meteor/Bump(atom/A, yes) + if(yes) + return + if(A == firer) + loc = A.loc + return + playsound(loc, 'sound/effects/meteorimpact.ogg', 40, 1) + for(var/mob/M in urange(10, src)) + if(!M.stat) + shake_camera(M, 3, 1) + qdel(src) + +/obj/item/projectile/energy/floramut + name = "alpha somatoray" + icon_state = "energy" + damage = 0 + damage_type = TOX + nodamage = 1 + impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser + flag = "energy" + +/obj/item/projectile/energy/floramut/on_hit(var/atom/target, var/blocked = 0) + ..() + var/mob/living/M = target + if(ishuman(target)) + var/mob/living/carbon/human/H = M + if(IS_PLANT in H.dna.species.species_traits) + if(prob(15)) + M.apply_effect((rand(30,80)),IRRADIATE) + M.Weaken(5) + M.visible_message("[M] writhes in pain as [M.p_their()] vacuoles boil.", "You writhe in pain as your vacuoles boil!", "You hear the crunching of leaves.") + if(prob(80)) + randmutb(M) + domutcheck(M,null) + else + randmutg(M) + domutcheck(M,null) + else + M.adjustFireLoss(rand(5,15)) + M.show_message("The radiation beam singes you!") + else if(iscarbon(target)) + M.show_message("The radiation beam dissipates harmlessly through your body.") + else + return 1 + +/obj/item/projectile/energy/florayield + name = "beta somatoray" + icon_state = "energy2" + damage = 0 + damage_type = TOX + nodamage = 1 + flag = "energy" + +/obj/item/projectile/energy/florayield/on_hit(var/atom/target, var/blocked = 0) + ..() + var/mob/M = target + if(ishuman(target)) //These rays make plantmen fat. + var/mob/living/carbon/human/H = M + if(IS_PLANT in H.dna.species.species_traits) + H.set_nutrition(min(H.nutrition+30, NUTRITION_LEVEL_FULL)) + else if(iscarbon(target)) + M.show_message("The radiation beam dissipates harmlessly through your body.") + else + return 1 + + +/obj/item/projectile/beam/mindflayer + name = "flayer ray" + +/obj/item/projectile/beam/mindflayer/on_hit(var/atom/target, var/blocked = 0) + . = ..() + if(ishuman(target)) + var/mob/living/carbon/human/M = target + M.adjustBrainLoss(20) + M.AdjustHallucinate(20) + +/obj/item/projectile/clown + name = "snap-pop" + icon = 'icons/obj/toy.dmi' + icon_state = "snappop" + +/obj/item/projectile/clown/Bump(atom/A as mob|obj|turf|area) + do_sparks(3, 1, src) + new /obj/effect/decal/cleanable/ash(loc) + visible_message("The [name] explodes!","You hear a snap!") + playsound(src, 'sound/effects/snap.ogg', 50, 1) + qdel(src) + +/obj/item/projectile/beam/wormhole + name = "bluespace beam" + icon_state = "spark" + hitsound = "sparks" + damage = 0 + var/obj/item/gun/energy/wormhole_projector/gun + color = "#33CCFF" + nodamage = TRUE + +/obj/item/projectile/beam/wormhole/orange + name = "orange bluespace beam" + color = "#FF6600" + +/obj/item/projectile/beam/wormhole/New(var/obj/item/ammo_casing/energy/wormhole/casing) + if(casing) + gun = casing.gun + +/obj/item/projectile/beam/wormhole/on_hit(atom/target) + if(ismob(target)) + if(is_teleport_allowed(target.z)) + var/turf/portal_destination = pick(orange(6, src)) + do_teleport(target, portal_destination) + return ..() + if(!gun) + qdel(src) + gun.create_portal(src) + +/obj/item/projectile/bullet/frag12 + name ="explosive slug" + damage = 25 + weaken = 5 + alwayslog = TRUE + +/obj/item/projectile/bullet/frag12/on_hit(atom/target, blocked = 0) + ..() + explosion(target, -1, 0, 1) + return 1 + +/obj/item/projectile/plasma + name = "plasma blast" + icon_state = "plasmacutter" + damage_type = BRUTE + damage = 5 + range = 3 + dismemberment = 20 + impact_effect_type = /obj/effect/temp_visual/impact_effect/purple_laser + +/obj/item/projectile/plasma/on_hit(atom/target) + . = ..() + if(ismineralturf(target)) + forcedodge = 1 + var/turf/simulated/mineral/M = target + M.gets_drilled(firer) + else + forcedodge = 0 + +/obj/item/projectile/plasma/adv + damage = 7 + range = 5 + +/obj/item/projectile/plasma/adv/mech + damage = 10 + range = 9 + +/obj/item/projectile/energy/teleport + name = "teleportation burst" + icon_state = "bluespace" + damage = 0 + nodamage = 1 + alwayslog = TRUE + var/teleport_target = null + +/obj/item/projectile/energy/teleport/New(loc, tele_target) + ..(loc) + if(tele_target) + teleport_target = tele_target + +/obj/item/projectile/energy/teleport/on_hit(var/atom/target, var/blocked = 0) + if(isliving(target)) + if(teleport_target) + do_teleport(target, teleport_target, 0)//teleport what's in the tile to the beacon + else + do_teleport(target, target, 15) //Otherwise it just warps you off somewhere. + add_attack_logs(firer, target, "Shot with a [type] [teleport_target ? "(Destination: [teleport_target])" : ""]") + +/obj/item/projectile/snowball + name = "snowball" + icon_state = "snowball" + hitsound = 'sound/items/dodgeball.ogg' + damage = 4 + damage_type = BURN + +/obj/item/projectile/snowball/on_hit(atom/target) //chilling + . = ..() + if(istype(target, /mob/living)) + var/mob/living/M = target + M.bodytemperature = max(0, M.bodytemperature - 50) //each hit will drop your body temp, so don't get surrounded! + M.ExtinguishMob() //bright side, they counter being on fire! + +/obj/item/projectile/ornament + name = "ornament" + icon_state = "ornament-1" + hitsound = 'sound/effects/glasshit.ogg' + damage = 7 + damage_type = BRUTE + +/obj/item/projectile/ornament/New() + icon_state = pick("ornament-1", "ornament-2") + ..() + +/obj/item/projectile/ornament/on_hit(atom/target) //knockback + ..() + if(istype(target, /turf)) + return 0 + var/obj/T = target + var/throwdir = get_dir(firer,target) + T.throw_at(get_edge_target_turf(target, throwdir),10,10) + return 1 + +/obj/item/projectile/mimic + name = "googly-eyed gun" + hitsound = 'sound/weapons/genhit1.ogg' + damage = 0 + nodamage = 1 + damage_type = BURN + flag = "melee" + var/obj/item/gun/stored_gun + +/obj/item/projectile/mimic/New(loc, mimic_type) + ..(loc) + if(mimic_type) + stored_gun = new mimic_type(src) + icon = stored_gun.icon + icon_state = stored_gun.icon_state + overlays = stored_gun.overlays + SpinAnimation(20, -1) + +/obj/item/projectile/mimic/on_hit(atom/target) + ..() + var/turf/T = get_turf(src) + var/obj/item/gun/G = stored_gun + stored_gun = null + G.forceMove(T) + var/mob/living/simple_animal/hostile/mimic/copy/ranged/R = new /mob/living/simple_animal/hostile/mimic/copy/ranged(T, G, firer) + if(ismob(target)) + R.target = target diff --git a/code/modules/reagents/chem_splash.dm b/code/modules/reagents/chem_splash.dm index c73ffc72ced..6ad51779965 100644 --- a/code/modules/reagents/chem_splash.dm +++ b/code/modules/reagents/chem_splash.dm @@ -69,7 +69,7 @@ var/atom/A = thing var/distance = max(1,get_dist(A, epicenter)) var/fraction = 0.5/(2 ** distance) //50/25/12/6... for a 200u splash, 25/12/6/3... for a 100u, 12/6/3/1 for a 50u - splash_holder.reaction(A, TOUCH, fraction) + splash_holder.reaction(A, REAGENT_TOUCH, fraction) qdel(splash_holder) return 1 diff --git a/code/modules/reagents/chemistry/colors.dm b/code/modules/reagents/chemistry/colors.dm index 0eea945349c..e9ced2ecfbe 100644 --- a/code/modules/reagents/chemistry/colors.dm +++ b/code/modules/reagents/chemistry/colors.dm @@ -28,4 +28,4 @@ var/list/random_color_list = list("#00aedb","#a200ff","#f47835","#d41243","#d111 color = BlendRGB(color, reagent_color, vol_temp/vol_counter) else color = BlendRGB(reagent_color, color, vol_temp/vol_counter) - return color \ No newline at end of file + return color diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm index 1945c6dffe5..9774021210c 100644 --- a/code/modules/reagents/chemistry/holder.dm +++ b/code/modules/reagents/chemistry/holder.dm @@ -1,5 +1,3 @@ -var/const/TOUCH = 1 -var/const/INGEST = 2 #define ADDICTION_TIME 4800 //8 minutes /////////////////////////////////////////////////////////////////////////////////// @@ -11,7 +9,9 @@ var/const/INGEST = 2 var/atom/my_atom = null var/chem_temp = T20C var/list/datum/reagent/addiction_list = new/list() + var/list/addiction_threshold_accumulated = new/list() var/flags + var/list/reagents_generated_per_cycle = new/list() /datum/reagents/New(maximum = 100) maximum_volume = maximum @@ -209,7 +209,7 @@ var/const/INGEST = 2 return var/datum/reagents/R = target.reagents - if(get_reagent_amount(reagent) (R.last_addiction_dose + ADDICTION_TIME))) //Each addiction lasts 8 minutes before it can end to_chat(M, "You no longer feel reliant on [R.name]!") addiction_list.Remove(R) @@ -348,7 +361,8 @@ var/const/INGEST = 2 if(flags & REAGENT_NOREACT) STOP_PROCESSING(SSobj, src) return - + for(var/thing in reagents_generated_per_cycle) + add_reagent(thing, reagents_generated_per_cycle[thing]) for(var/datum/reagent/R in reagent_list) R.on_tick() @@ -558,7 +572,7 @@ var/const/INGEST = 2 can_process = 1 return can_process -/datum/reagents/proc/reaction(atom/A, method = TOUCH, volume_modifier = 1) +/datum/reagents/proc/reaction(atom/A, method = REAGENT_TOUCH, volume_modifier = 1) var/react_type if(isliving(A)) react_type = "LIVING" @@ -571,7 +585,7 @@ var/const/INGEST = 2 if(react_type == "LIVING" && ishuman(A)) var/mob/living/carbon/human/H = A - if(method == TOUCH) + if(method == REAGENT_TOUCH) var/obj/item/organ/external/head/affecting = H.get_organ("head") if(affecting) if(chem_temp > H.dna.species.heat_level_1) @@ -587,7 +601,7 @@ var/const/INGEST = 2 H.emote("scream") H.adjust_bodytemperature(- min(max(T0C - chem_temp - 20, 5), 500)) - if(method == INGEST) + if(method == REAGENT_INGEST) if(chem_temp > H.dna.species.heat_level_1) to_chat(H, "You scald yourself trying to consume the boiling hot substance!") H.adjustFireLoss(7) diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm index e822dc1ea88..cca429276dd 100644 --- a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm +++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm @@ -19,7 +19,7 @@ var/image/icon_beaker = null //cached overlay var/list/dispensable_reagents = list("hydrogen", "lithium", "carbon", "nitrogen", "oxygen", "fluorine", "sodium", "aluminum", "silicon", "phosphorus", "sulfur", "chlorine", "potassium", "iron", - "copper", "mercury", "plasma", "radium", "water", "ethanol", "sugar", "iodine", "bromine", "silver") + "copper", "mercury", "plasma", "radium", "water", "ethanol", "sugar", "iodine", "bromine", "silver", "chromium") var/list/upgrade_reagents = list("oil", "ash", "acetone", "saltpetre", "ammonia", "diethylamine", "fuel") var/list/hacked_reagents = list("toxin") var/hack_message = "You disable the safety safeguards, enabling the \"Mad Scientist\" mode." @@ -242,46 +242,10 @@ return TRUE // update UIs attached to this object /obj/machinery/chem_dispenser/attackby(obj/item/I, mob/user, params) - if(ismultitool(I)) - if(!hackedcheck) - to_chat(user, hack_message) - dispensable_reagents += hacked_reagents - hackedcheck = TRUE - else - to_chat(user, unhack_message) - dispensable_reagents -= hacked_reagents - hackedcheck = FALSE - SSnanoui.update_uis(src) - return - - if(default_deconstruction_screwdriver(user, "[initial(icon_state)]-o", "[initial(icon_state)]", I)) - return - if(exchange_parts(user, I)) SSnanoui.update_uis(src) return - if(iswrench(I)) - playsound(src, I.usesound, 50, 1) - if(anchored) - anchored = FALSE - to_chat(user, "[src] can now be moved.") - else if(!anchored) - anchored = TRUE - to_chat(user, "[src] is now secured.") - return - - if(panel_open) - if(iscrowbar(I)) - if(beaker) - beaker.forceMove(loc) - beaker = null - if(cell) - cell.forceMove(loc) - cell = null - default_deconstruction_crowbar(I) - return TRUE - if(isrobot(user)) return @@ -307,6 +271,48 @@ return return ..() +/obj/machinery/chem_dispenser/crowbar_act(mob/user, obj/item/I) + if(!panel_open) + return + if(default_deconstruction_crowbar(user, I)) + if(beaker) + beaker.forceMove(loc) + beaker = null + if(cell) + cell.forceMove(loc) + cell = null + return TRUE + + +/obj/machinery/chem_dispenser/multitool_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(!hackedcheck) + to_chat(user, hack_message) + dispensable_reagents += hacked_reagents + hackedcheck = TRUE + else + to_chat(user, unhack_message) + dispensable_reagents -= hacked_reagents + hackedcheck = FALSE + SSnanoui.update_uis(src) + + +/obj/machinery/chem_dispenser/screwdriver_act(mob/user, obj/item/I) + if(default_deconstruction_screwdriver(user, "[initial(icon_state)]-o", "[initial(icon_state)]", I)) + return TRUE + +/obj/machinery/chem_dispenser/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(anchored) + anchored = FALSE + WRENCH_UNANCHOR_MESSAGE + else if(!anchored) + anchored = TRUE + WRENCH_ANCHOR_MESSAGE /obj/machinery/chem_dispenser/attack_ai(mob/user) return attack_hand(user) @@ -391,4 +397,4 @@ component_parts += new /obj/item/stock_parts/manipulator/pico(null) component_parts += new /obj/item/stack/sheet/glass(null) component_parts += new cell_type(null) - RefreshParts() \ No newline at end of file + RefreshParts() diff --git a/code/modules/reagents/chemistry/machinery/chem_heater.dm b/code/modules/reagents/chemistry/machinery/chem_heater.dm index 247c802f239..169b668250c 100644 --- a/code/modules/reagents/chemistry/machinery/chem_heater.dm +++ b/code/modules/reagents/chemistry/machinery/chem_heater.dm @@ -58,9 +58,6 @@ SSnanoui.update_uis(src) /obj/machinery/chem_heater/attackby(obj/item/I, mob/user) - if(default_unfasten_wrench(user, I)) - power_change() - return if(isrobot(user)) return @@ -77,19 +74,26 @@ SSnanoui.update_uis(src) return - if(default_deconstruction_screwdriver(user, "mixer0b", "mixer0b", I)) - return - if(exchange_parts(user, I)) return - if(panel_open) - if(istype(I, /obj/item/crowbar)) - eject_beaker() - default_deconstruction_crowbar(I) - return 1 return ..() +/obj/machinery/chem_heater/wrench_act(mob/user, obj/item/I) + . = TRUE + default_unfasten_wrench(user, I) + +/obj/machinery/chem_heater/screwdriver_act(mob/user, obj/item/I) + . = TRUE + default_deconstruction_screwdriver(user, "mixer0b", "mixer0b", I) + +/obj/machinery/chem_heater/crowbar_act(mob/user, obj/item/I) + if(!panel_open) + return + . = TRUE + eject_beaker() + default_deconstruction_crowbar(user, I) + /obj/machinery/chem_heater/attack_hand(mob/user) ui_interact(user) diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm index ca069ddf514..39d491bf192 100644 --- a/code/modules/reagents/chemistry/machinery/chem_master.dm +++ b/code/modules/reagents/chemistry/machinery/chem_master.dm @@ -77,30 +77,12 @@ update_icon() /obj/machinery/chem_master/attackby(obj/item/I, mob/user, params) - if(default_deconstruction_screwdriver(user, "mixer0_nopower", "mixer0", I)) - if(beaker) - beaker.forceMove(get_turf(src)) - beaker = null - reagents.clear_reagents() - if(loaded_pill_bottle) - loaded_pill_bottle.forceMove(get_turf(src)) - loaded_pill_bottle = null - return - if(exchange_parts(user, I)) return if(panel_open) - if(iscrowbar(I)) - default_deconstruction_crowbar(I) - return TRUE - else - to_chat(user, "You can't use the [name] while it's panel is opened!") - return TRUE - - if(default_unfasten_wrench(user, I)) - power_change() - return + to_chat(user, "You can't use the [name] while it's panel is opened!") + return TRUE if(istype(I, /obj/item/reagent_containers/glass) || istype(I, /obj/item/reagent_containers/food/drinks/drinkingglass)) if(beaker) @@ -131,6 +113,35 @@ else return ..() + + + + +/obj/machinery/chem_master/crowbar_act(mob/user, obj/item/I) + if(!panel_open) + return + if(default_deconstruction_crowbar(user, I)) + return TRUE + +/obj/machinery/chem_master/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(default_deconstruction_screwdriver(user, "mixer0_nopower", "mixer0", I)) + if(beaker) + beaker.forceMove(get_turf(src)) + beaker = null + reagents.clear_reagents() + if(loaded_pill_bottle) + loaded_pill_bottle.forceMove(get_turf(src)) + loaded_pill_bottle = null + return TRUE + +/obj/machinery/chem_master/wrench_act(mob/user, obj/item/I) + if(panel_open) + return + if(default_unfasten_wrench(user, I)) + power_change() + return TRUE + /obj/machinery/chem_master/Topic(href, href_list) if(..()) return TRUE @@ -491,4 +502,4 @@ component_parts += new /obj/item/stack/sheet/glass(null) component_parts += new /obj/item/reagent_containers/glass/beaker(null) component_parts += new /obj/item/reagent_containers/glass/beaker(null) - RefreshParts() \ No newline at end of file + RefreshParts() diff --git a/code/modules/reagents/chemistry/machinery/reagentgrinder.dm b/code/modules/reagents/chemistry/machinery/reagentgrinder.dm index 9601746443a..2422c7a143d 100644 --- a/code/modules/reagents/chemistry/machinery/reagentgrinder.dm +++ b/code/modules/reagents/chemistry/machinery/reagentgrinder.dm @@ -133,21 +133,35 @@ else icon_state = "juicer0" -/obj/machinery/reagentgrinder/attackby(obj/item/I, mob/user, params) - if(default_unfasten_wrench(user, I)) +/obj/machinery/reagentgrinder/crowbar_act(mob/user, obj/item/I) + . = TRUE + if(!anchored || beaker) return + if(!panel_open) + return + if(!I.tool_use_check(user, 0)) + return + default_deconstruction_crowbar(I) + +/obj/machinery/reagentgrinder/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!anchored || beaker) + return + if(!I.tool_use_check(user, 0)) + return + default_deconstruction_screwdriver(user, "juicer_open", "juicer0", I) + +/obj/machinery/reagentgrinder/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + default_unfasten_wrench(user, I) + +/obj/machinery/reagentgrinder/attackby(obj/item/I, mob/user, params) if(exchange_parts(user, I)) return - if(anchored && !beaker) - if(default_deconstruction_screwdriver(user, "juicer_open", "juicer0", I)) - return - - if(panel_open && istype(I, /obj/item/crowbar)) - default_deconstruction_crowbar(I) - return - if(istype(I, /obj/item/reagent_containers) && (I.container_type & OPENCONTAINER) ) if(beaker) to_chat(user, "There's already a container inside.") @@ -214,6 +228,8 @@ src.updateUsrDialog() return FALSE + + /obj/machinery/reagentgrinder/attack_ai(mob/user) return FALSE diff --git a/code/modules/reagents/chemistry/readme.dm b/code/modules/reagents/chemistry/readme.dm index 6c76d0a3a0f..ddcdb8a123e 100644 --- a/code/modules/reagents/chemistry/readme.dm +++ b/code/modules/reagents/chemistry/readme.dm @@ -238,4 +238,4 @@ About the Tools: It simply tells us how much to transfer when 'pouring' our reagents into something else. -*/ \ No newline at end of file +*/ diff --git a/code/modules/reagents/chemistry/reagents.dm b/code/modules/reagents/chemistry/reagents.dm index 05967be04ca..26dfa2d815f 100644 --- a/code/modules/reagents/chemistry/reagents.dm +++ b/code/modules/reagents/chemistry/reagents.dm @@ -20,6 +20,9 @@ var/can_synth = TRUE //whether or not a mech syringe gun and synthesize this reagent var/overdose_threshold = 0 var/addiction_chance = 0 + var/addiction_chance_additional = 100 // If we want to lower the chance of addiction even more, set this + var/addiction_threshold = 0 // How much of a chem do we have to absorb before we can start rolling for its ill effects? + var/minor_addiction = FALSE var/addiction_stage = 1 var/last_addiction_dose = 0 var/overdosed = FALSE // You fucked up and this is now triggering it's overdose effects, purge that shit quick. @@ -40,25 +43,20 @@ /datum/reagent/proc/reaction_temperature(exposed_temperature, exposed_volume) //By default we do nothing. return -/datum/reagent/proc/reaction_mob(mob/living/M, method = TOUCH, volume) //Some reagents transfer on touch, others don't; dependent on if they penetrate the skin or not. +/datum/reagent/proc/reaction_mob(mob/living/M, method = REAGENT_TOUCH, volume) //Some reagents transfer on touch, others don't; dependent on if they penetrate the skin or not. if(holder) //for catching rare runtimes - if(method == TOUCH && penetrates_skin) + if(method == REAGENT_TOUCH && penetrates_skin) var/block = M.get_permeability_protection() var/amount = round(volume * (1 - block), 0.1) if(M.reagents) if(amount >= 1) M.reagents.add_reagent(id, amount) - if(method == INGEST) //Yes, even Xenos can get addicted to drugs. + if(method == REAGENT_INGEST) //Yes, even Xenos can get addicted to drugs. var/can_become_addicted = M.reagents.reaction_check(M, src) if(can_become_addicted) - if(prob(addiction_chance) && !is_type_in_list(src, M.reagents.addiction_list)) - to_chat(M, "You suddenly feel invigorated and guilty...") - var/datum/reagent/new_reagent = new type() - new_reagent.last_addiction_dose = world.timeofday - M.reagents.addiction_list.Add(new_reagent) - else if(is_type_in_list(src, M.reagents.addiction_list)) + if(is_type_in_list(src, M.reagents.addiction_list)) to_chat(M, "You feel slightly better, but for how long?") for(var/A in M.reagents.addiction_list) var/datum/reagent/AD = A @@ -75,9 +73,24 @@ /datum/reagent/proc/on_mob_life(mob/living/M) current_cycle++ - holder.remove_reagent(id, metabolization_rate * M.metabolism_efficiency * M.digestion_ratio) //By default it slowly disappears. + var/total_depletion_rate = metabolization_rate * M.metabolism_efficiency * M.digestion_ratio // Cache it + + handle_addiction(M, total_depletion_rate) + + holder.remove_reagent(id, total_depletion_rate) //By default it slowly disappears. return STATUS_UPDATE_NONE +/datum/reagent/proc/handle_addiction(mob/living/M, consumption_rate) + if(addiction_chance && !is_type_in_list(src, M.reagents.addiction_list)) + M.reagents.addiction_threshold_accumulated[id] += consumption_rate + var/current_threshold_accumulated = M.reagents.addiction_threshold_accumulated[id] + + if(addiction_threshold < current_threshold_accumulated && prob(addiction_chance) && prob(addiction_chance_additional)) + to_chat(M, "You suddenly feel invigorated and guilty...") + var/datum/reagent/new_reagent = new type() + new_reagent.last_addiction_dose = world.timeofday + M.reagents.addiction_list.Add(new_reagent) + /datum/reagent/proc/on_mob_death(mob/living/M) //use this to have chems have a "death-triggered" effect return @@ -139,6 +152,8 @@ M.emote("twitch_s") if(prob(8)) M.emote("shiver") + if(prob(4)) + to_chat(M, "Your head hurts.") if(prob(4)) to_chat(M, "You begin craving [name]!") return STATUS_UPDATE_NONE @@ -147,23 +162,32 @@ if(prob(8)) M.emote("twitch") if(prob(4)) - to_chat(M, "You have the strong urge for some [name]!") + to_chat(M, "You have a pounding headache.") if(prob(4)) + to_chat(M, "You have the strong urge for some [name]!") + else if(prob(4)) to_chat(M, "You REALLY crave some [name]!") return STATUS_UPDATE_NONE /datum/reagent/proc/addiction_act_stage5(mob/living/M) var/update_flags = STATUS_UPDATE_NONE + if(minor_addiction) + if(prob(6)) + M.AdjustSlowed(3) + to_chat(M, "You feel [pick("tired", "exhausted", "sluggish")].") + else + if(prob(6)) + to_chat(M, "Your stomach lurches painfully!") + M.visible_message("[M] gags and retches!") + update_flags |= M.Stun(rand(2,4), FALSE) + update_flags |= M.Weaken(rand(2,4), FALSE) if(prob(8)) - M.emote("twitch") - if(prob(6)) - to_chat(M, "Your stomach lurches painfully!") - M.visible_message("[M] gags and retches!") - update_flags |= M.Stun(rand(2,4), FALSE) - update_flags |= M.Weaken(rand(2,4), FALSE) + M.emote(pick("twitch", "twitch_s", "shiver")) + if(prob(4)) + to_chat(M, "Your head is killing you!") if(prob(5)) to_chat(M, "You feel like you can't live without [name]!") - if(prob(5)) + else if(prob(5)) to_chat(M, "You would DIE for some [name] right now!") return update_flags @@ -194,4 +218,4 @@ if(M.healthdoll) M.healthdoll.cached_healthdoll_overlays.Cut() if(M.dna.species) - M.dna.species.handle_hud_icons(M) \ No newline at end of file + M.dna.species.handle_hud_icons(M) diff --git a/code/modules/reagents/chemistry/reagents/admin.dm b/code/modules/reagents/chemistry/reagents/admin.dm index 6f22078b278..90e6cff3bf6 100644 --- a/code/modules/reagents/chemistry/reagents/admin.dm +++ b/code/modules/reagents/chemistry/reagents/admin.dm @@ -23,6 +23,7 @@ I.receive_damage(-5, FALSE) for(var/obj/item/organ/external/E in H.bodyparts) E.mend_fracture() + E.internal_bleeding = FALSE M.SetEyeBlind(0, FALSE) M.CureNearsighted(FALSE) M.CureBlind(FALSE) diff --git a/code/modules/reagents/chemistry/reagents/alcohol.dm b/code/modules/reagents/chemistry/reagents/alcohol.dm index d4d2bbe2d88..2831d0e3f89 100644 --- a/code/modules/reagents/chemistry/reagents/alcohol.dm +++ b/code/modules/reagents/chemistry/reagents/alcohol.dm @@ -6,6 +6,8 @@ reagent_state = LIQUID nutriment_factor = 0 //So alcohol can fill you up! If they want to. color = "#404030" // rgb: 64, 64, 48 + addiction_chance = 1 + addiction_threshold = 10 var/dizzy_adj = 3 var/alcohol_perc = 1 //percentage of ethanol in a beverage 0.0 - 1.0 taste_description = "liquid fire" @@ -31,8 +33,8 @@ else to_chat(usr, "It wasn't enough...") -/datum/reagent/consumable/ethanol/reaction_mob(mob/living/M, method=TOUCH, volume)//Splashing people with ethanol isn't quite as good as fuel. - if(method == TOUCH) +/datum/reagent/consumable/ethanol/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume)//Splashing people with ethanol isn't quite as good as fuel. + if(method == REAGENT_TOUCH) M.adjust_fire_stacks(volume / 15) @@ -1140,8 +1142,8 @@ can_synth = FALSE taste_description = "LIQUID FUCKING DEATH OH GOD WHAT THE FUCK" -/datum/reagent/consumable/ethanol/dragons_breath/reaction_mob(mob/living/M, method=TOUCH, volume) - if(method == INGEST && prob(20)) +/datum/reagent/consumable/ethanol/dragons_breath/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) + if(method == REAGENT_INGEST && prob(20)) if(M.on_fire) M.adjust_fire_stacks(6) @@ -1190,17 +1192,18 @@ taste_description = "motor oil" /datum/reagent/consumable/ethanol/synthanol/on_mob_life(mob/living/M) - if(!M.isSynthetic()) + if(!(M.dna.species.reagent_tag & PROCESS_SYN)) holder.remove_reagent(id, 3.6) //gets removed from organics very fast if(prob(25)) holder.remove_reagent(id, 15) M.fakevomit() + return ..() -/datum/reagent/consumable/ethanol/synthanol/reaction_mob(mob/living/M, method=TOUCH, volume) - if(M.isSynthetic()) +/datum/reagent/consumable/ethanol/synthanol/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) + if(M.dna.species.reagent_tag & PROCESS_SYN) return - if(method == INGEST) + if(method == REAGENT_INGEST) to_chat(M, pick("That was awful!", "Yuck!")) /datum/reagent/consumable/ethanol/synthanol/robottears diff --git a/code/modules/reagents/chemistry/reagents/blob.dm b/code/modules/reagents/chemistry/reagents/blob.dm index 571db376f80..acf244a892d 100644 --- a/code/modules/reagents/chemistry/reagents/blob.dm +++ b/code/modules/reagents/chemistry/reagents/blob.dm @@ -7,7 +7,7 @@ var/message_living = null //extension to first mob sent to only living mobs i.e. silicons have no skin to be burnt can_synth = FALSE -/datum/reagent/blob/reaction_mob(mob/living/M, method=TOUCH, volume, show_message, touch_protection) +/datum/reagent/blob/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume, show_message, touch_protection) return round(volume * min(1.5 - touch_protection, 1), 0.1) //full touch protection means 50% volume, any prot below 0.5 means 100% volume. /datum/reagent/blob/proc/damage_reaction(obj/structure/blob/B, damage, damage_type, damage_flag) //when the blob takes damage, do this @@ -21,8 +21,8 @@ complementary_color = "#a15656" message_living = ", and you feel your skin ripping and tearing off" -/datum/reagent/blob/ripping_tendrils/reaction_mob(mob/living/M, method=TOUCH, volume) - if(method == TOUCH) +/datum/reagent/blob/ripping_tendrils/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) + if(method == REAGENT_TOUCH) volume = ..() M.apply_damage(0.6*volume, BRUTE) M.adjustStaminaLoss(volume) @@ -38,8 +38,8 @@ message = "The blob splashes you with burning oil" message_living = ", and you feel your skin char and melt" -/datum/reagent/blob/boiling_oil/reaction_mob(mob/living/M, method=TOUCH, volume) - if(method == TOUCH) +/datum/reagent/blob/boiling_oil/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) + if(method == REAGENT_TOUCH) M.adjust_fire_stacks(round(volume/10)) volume = ..() M.apply_damage(0.6*volume, BURN) @@ -54,8 +54,8 @@ complementary_color = "#b0cd73" message_living = ", and you feel sick and nauseated" -/datum/reagent/blob/envenomed_filaments/reaction_mob(mob/living/M, method=TOUCH, volume) - if(method == TOUCH) +/datum/reagent/blob/envenomed_filaments/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) + if(method == REAGENT_TOUCH) volume = ..() M.apply_damage(0.6*volume, TOX) M.hallucination += 0.6*volume @@ -70,8 +70,8 @@ complementary_color = "#56ebc9" message_living = ", and your lungs feel heavy and weak" -/datum/reagent/blob/lexorin_jelly/reaction_mob(mob/living/M, method=TOUCH, volume) - if(method == TOUCH) +/datum/reagent/blob/lexorin_jelly/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) + if(method == REAGENT_TOUCH) volume = ..() M.apply_damage(0.4*volume, BRUTE) M.apply_damage(1*volume, OXY) @@ -86,8 +86,8 @@ complementary_color = "#ebb756" message = "The blob pummels you" -/datum/reagent/blob/kinetic/reaction_mob(mob/living/M, method=TOUCH, volume) - if(method == TOUCH) +/datum/reagent/blob/kinetic/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) + if(method == REAGENT_TOUCH) volume = ..() var/damage = rand(5, 35)/25 M.apply_damage(damage*volume, BRUTE) @@ -101,8 +101,8 @@ message = "The blob splashes you with an icy liquid" message_living = ", and you feel cold and tired" -/datum/reagent/blob/cryogenic_liquid/reaction_mob(mob/living/M, method=TOUCH, volume) - if(method == TOUCH) +/datum/reagent/blob/cryogenic_liquid/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) + if(method == REAGENT_TOUCH) volume = ..() M.apply_damage(0.4*volume, BURN) M.adjustStaminaLoss(volume) @@ -117,8 +117,8 @@ complementary_color = "#a2a256" message = "The blob slams into you, and sends you flying" -/datum/reagent/blob/b_sorium/reaction_mob(mob/living/M, method=TOUCH, volume) - if(method == TOUCH) +/datum/reagent/blob/b_sorium/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) + if(method == REAGENT_TOUCH) reagent_vortex(M, 1, volume) volume = ..() M.apply_damage(0.6*volume, BRUTE) @@ -157,4 +157,4 @@ if(message_living && !issilicon(M)) totalmessage += message_living totalmessage += "!" - to_chat(M, "[totalmessage]") \ No newline at end of file + to_chat(M, "[totalmessage]") diff --git a/code/modules/reagents/chemistry/reagents/disease.dm b/code/modules/reagents/chemistry/reagents/disease.dm index 331157d4acc..1cd1111b49e 100644 --- a/code/modules/reagents/chemistry/reagents/disease.dm +++ b/code/modules/reagents/chemistry/reagents/disease.dm @@ -211,4 +211,4 @@ /datum/reagent/plasma_dust/plasmavirusfood/weak name = "weakened virus plasma" id = "weakplasmavirusfood" - color = "#CEC3C6" // rgb: 206,195,198 \ No newline at end of file + color = "#CEC3C6" // rgb: 206,195,198 diff --git a/code/modules/reagents/chemistry/reagents/drinks.dm b/code/modules/reagents/chemistry/reagents/drinks.dm index 6298e79d71d..ae58c806126 100644 --- a/code/modules/reagents/chemistry/reagents/drinks.dm +++ b/code/modules/reagents/chemistry/reagents/drinks.dm @@ -106,8 +106,8 @@ drink_desc = "As colorful and healthy as it is delicious." taste_description = "citrus juice" -/datum/reagent/consumable/drink/triple_citrus/reaction_mob(mob/living/M, method=TOUCH, volume) - if(method == INGEST) +/datum/reagent/consumable/drink/triple_citrus/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) + if(method == REAGENT_INGEST) M.adjustToxLoss(-rand(1,2)) /datum/reagent/consumable/drink/berryjuice @@ -276,7 +276,10 @@ adj_sleepy = -2 adj_temp_hot = 25 overdose_threshold = 45 - addiction_chance = 1 // It's true. + addiction_chance = 2 // It's true. + addiction_chance_additional = 20 + addiction_threshold = 10 + minor_addiction = TRUE heart_rate_increase = 1 drink_icon = "glass_brown" drink_name = "Glass of coffee" @@ -367,6 +370,10 @@ adj_drowsy = -1 adj_sleepy = -3 adj_temp_hot = 20 + addiction_chance = 1 + addiction_chance_additional = 1 + addiction_threshold = 10 + minor_addiction = TRUE drink_icon = "glass_brown" drink_name = "Glass of Tea" drink_desc = "A glass of hot tea. Perhaps a cup with a handle would have been smarter?" diff --git a/code/modules/reagents/chemistry/reagents/drugs.dm b/code/modules/reagents/chemistry/reagents/drugs.dm index 1e2099a901b..a9f5d8a9cb5 100644 --- a/code/modules/reagents/chemistry/reagents/drugs.dm +++ b/code/modules/reagents/chemistry/reagents/drugs.dm @@ -35,7 +35,8 @@ reagent_state = LIQUID color = "#9087A2" metabolization_rate = 0.2 - addiction_chance = 65 + addiction_chance = 15 + addiction_threshold = 10 heart_rate_decrease = 1 taste_description = "a synthetic high" @@ -88,7 +89,9 @@ reagent_state = LIQUID color = "#60A584" // rgb: 96, 165, 132 overdose_threshold = 35 - addiction_chance = 70 + addiction_chance = 15 + addiction_threshold = 10 + minor_addiction = TRUE heart_rate_increase = 1 taste_description = "calm" @@ -154,7 +157,8 @@ reagent_state = LIQUID color = "#60A584" // rgb: 96, 165, 132 overdose_threshold = 20 - addiction_chance = 50 + addiction_chance = 10 + addiction_threshold = 5 taste_description = "bitterness" /datum/reagent/crank/on_mob_life(mob/living/M) @@ -227,7 +231,8 @@ reagent_state = LIQUID color = "#0264B4" overdose_threshold = 20 - addiction_chance = 50 + addiction_chance = 10 + addiction_threshold = 10 taste_description = "very poor life choices" @@ -299,7 +304,8 @@ reagent_state = LIQUID color = "#60A584" // rgb: 96, 165, 132 overdose_threshold = 20 - addiction_chance = 60 + addiction_chance = 10 + addiction_threshold = 5 metabolization_rate = 0.6 heart_rate_increase = 1 taste_description = "speed" @@ -360,7 +366,8 @@ reagent_state = SOLID color = "#FAFAFA" overdose_threshold = 20 - addiction_chance = 80 + addiction_chance = 15 + addiction_threshold = 5 metabolization_rate = 0.6 taste_description = "WAAAAGH" @@ -397,8 +404,8 @@ to_chat(M, "THEY'RE GONNA GET YOU!") return ..() | update_flags -/datum/reagent/bath_salts/reaction_mob(mob/living/M, method=TOUCH, volume) - if(method == INGEST) +/datum/reagent/bath_salts/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) + if(method == REAGENT_INGEST) to_chat(M, "You feel FUCKED UP!!!!!!") M << 'sound/effects/singlebeat.ogg' M.emote("faint") @@ -460,7 +467,8 @@ description = "Jenkem is a prison drug made from fermenting feces in a solution of urine. Extremely disgusting." reagent_state = LIQUID color = "#644600" - addiction_chance = 30 + addiction_chance = 5 + addiction_threshold = 5 taste_description = "the inside of a toilet... or worse" /datum/reagent/jenkem/on_mob_life(mob/living/M) @@ -517,6 +525,32 @@ M.Drowsy(10) return ..() +/datum/reagent/cbd + name = "Cannabidiol" + id = "cbd" + description = "A non-psychoactive phytocannabinoid extracted from the cannabis plant." + reagent_state = LIQUID + color = "#00e100" + taste_description = "relaxation" + +/datum/reagent/cbd/on_mob_life(mob/living/M) + var/update_flags = STATUS_UPDATE_NONE + if(prob(5)) + M.emote(pick("hsigh", "yawn")) + if(prob(5)) + to_chat(M, "[pick("You feel peaceful.", "You breathe softly.", "You feel chill.", "You vibe.")]") + if(prob(10)) + M.AdjustConfused(-5) + update_flags |= M.SetWeakened(0, FALSE) + if(volume >= 70 && prob(25)) + if(M.reagents.has_reagent("thc") <= 20) + M.Drowsy(10) + if(prob(25)) + update_flags |= M.adjustBruteLoss(-2, FALSE) + update_flags |= M.adjustFireLoss(-2, FALSE) + return ..() | update_flags + + /datum/reagent/fliptonium name = "Fliptonium" id = "fliptonium" @@ -526,7 +560,9 @@ metabolization_rate = 0.2 overdose_threshold = 15 process_flags = ORGANIC | SYNTHETIC //Flipping for everyone! - addiction_chance = 10 + addiction_chance = 1 + addiction_chance_additional = 20 + addiction_threshold = 10 taste_description = "flips" /datum/reagent/fliptonium/on_mob_life(mob/living/M) @@ -556,8 +592,8 @@ update_flags |= M.SetSleeping(0, FALSE) return ..() | update_flags -/datum/reagent/fliptonium/reaction_mob(mob/living/M, method=TOUCH, volume) - if(method == INGEST || method == TOUCH) +/datum/reagent/fliptonium/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) + if(method == REAGENT_INGEST || method == REAGENT_TOUCH) M.SpinAnimation(speed = 12, loops = -1) ..() @@ -631,7 +667,8 @@ color = "#1BB1FF" process_flags = SYNTHETIC overdose_threshold = 20 - addiction_chance = 60 + addiction_chance = 10 + addiction_threshold = 5 metabolization_rate = 0.6 taste_description = "wiper fluid" @@ -684,7 +721,8 @@ process_flags = SYNTHETIC overdose_threshold = 20 - addiction_chance = 50 + addiction_chance = 10 + addiction_threshold = 5 taste_description = "silicon" diff --git a/code/modules/reagents/chemistry/reagents/food.dm b/code/modules/reagents/chemistry/reagents/food.dm index 5353dc54b88..6d1c4d74e72 100644 --- a/code/modules/reagents/chemistry/reagents/food.dm +++ b/code/modules/reagents/chemistry/reagents/food.dm @@ -149,6 +149,10 @@ description = "This is what makes chilis hot." reagent_state = LIQUID color = "#B31008" // rgb: 179, 16, 8 + addiction_chance = 1 + addiction_chance_additional = 10 + addiction_threshold = 2 + minor_addiction = TRUE taste_description = "HOTNESS" taste_mult = 1.5 @@ -187,8 +191,8 @@ M.visible_message("[M] [pick("dry heaves!","coughs!","splutters!")]") return ..() -/datum/reagent/consumable/condensedcapsaicin/reaction_mob(mob/living/M, method=TOUCH, volume) - if(method == TOUCH) +/datum/reagent/consumable/condensedcapsaicin/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) + if(method == REAGENT_TOUCH) if(ishuman(M)) var/mob/living/carbon/human/victim = M var/mouth_covered = 0 @@ -548,8 +552,8 @@ color = "#c0c9a0" taste_description = "pungency" -/datum/reagent/consumable/onion/reaction_mob(mob/living/M, method = TOUCH, volume) - if(method == TOUCH) +/datum/reagent/consumable/onion/reaction_mob(mob/living/M, method = REAGENT_TOUCH, volume) + if(method == REAGENT_TOUCH) if(!M.is_mouth_covered() && !M.is_eyes_covered()) if(!M.get_organ_slot("eyes")) //can't blind somebody with no eyes to_chat(M, "Your eye sockets feel wet.") @@ -651,6 +655,10 @@ reagent_state = LIQUID color = "#B2B139" overdose_threshold = 50 + addiction_chance = 2 + addiction_chance_additional = 10 + addiction_threshold = 5 + minor_addiction = TRUE harmless = FALSE taste_description = "cheese?" @@ -667,7 +675,10 @@ description = "Hell, I don't even know if this IS cheese. Whatever it is, it ain't normal. If you want to, pour it out to make it solid." reagent_state = SOLID color = "#50FF00" - addiction_chance = 5 + addiction_chance = 1 + addiction_chance_additional = 10 + addiction_threshold = 5 + minor_addiction = TRUE taste_description = "cheeeeeese...?" /datum/reagent/consumable/weird_cheese/on_mob_life(mob/living/M) @@ -806,8 +817,8 @@ color = "#AC7E67" taste_description = "pepperoni" -/datum/reagent/consumable/pepperoni/reaction_mob(mob/living/M, method=TOUCH, volume) - if(method == TOUCH) +/datum/reagent/consumable/pepperoni/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) + if(method == REAGENT_TOUCH) if(ishuman(M)) var/mob/living/carbon/human/H = M @@ -838,8 +849,8 @@ color = "#63DE63" taste_description = "burned food" -/datum/reagent/questionmark/reaction_mob(mob/living/carbon/human/H, method = TOUCH, volume) - if(istype(H) && method == INGEST) +/datum/reagent/questionmark/reaction_mob(mob/living/carbon/human/H, method = REAGENT_TOUCH, volume) + if(istype(H) && method == REAGENT_INGEST) if(H.dna.species.taste_sensitivity < TASTE_SENSITIVITY_NO_TASTE) // If you can taste it, then you know how awful it is. H.Stun(2, FALSE) H.Weaken(2, FALSE) @@ -901,8 +912,8 @@ color = "#C87D28" taste_description = "mold" -/datum/reagent/fungus/reaction_mob(mob/living/M, method=TOUCH, volume) - if(method == INGEST) +/datum/reagent/fungus/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) + if(method == REAGENT_INGEST) var/ranchance = rand(1,10) if(ranchance == 1) to_chat(M, "You feel very sick.") @@ -928,8 +939,8 @@ to_chat(M, "[spooky_message]") return ..() -/datum/reagent/ectoplasm/reaction_mob(mob/living/M, method=TOUCH, volume) - if(method == INGEST) +/datum/reagent/ectoplasm/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) + if(method == REAGENT_INGEST) var/spooky_eat = pick("Ugh, why did you eat that? Your mouth feels haunted. Haunted with bad flavors.", "Ugh, why did you eat that? It has the texture of ham aspic. From the 1950s. Left out in the sun.", "Ugh, why did you eat that? It tastes like a ghost fart.", "Ugh, why did you eat that? It tastes like flavor died.") to_chat(M, "[spooky_eat]") @@ -1018,4 +1029,4 @@ if(prob(80)) update_flags |= M.adjustBruteLoss(-1 * REAGENTS_EFFECT_MULTIPLIER, FALSE) update_flags |= M.adjustFireLoss(-1 * REAGENTS_EFFECT_MULTIPLIER, FALSE) - return ..() | update_flags \ No newline at end of file + return ..() | update_flags diff --git a/code/modules/reagents/chemistry/reagents/medicine.dm b/code/modules/reagents/chemistry/reagents/medicine.dm index c297716c1a7..c9e7ac0c1dc 100644 --- a/code/modules/reagents/chemistry/reagents/medicine.dm +++ b/code/modules/reagents/chemistry/reagents/medicine.dm @@ -6,7 +6,11 @@ /datum/reagent/medicine/on_mob_life(mob/living/M) current_cycle++ - holder.remove_reagent(id, (metabolization_rate / M.metabolism_efficiency) * M.digestion_ratio) //medicine reagents stay longer if you have a better metabolism + var/total_depletion_rate = (metabolization_rate / M.metabolism_efficiency) * M.digestion_ratio // Cache it + + handle_addiction(M, total_depletion_rate) + + holder.remove_reagent(id, total_depletion_rate) //medicine reagents stay longer if you have a better metabolism return STATUS_UPDATE_NONE /datum/reagent/medicine/hydrocodone @@ -28,8 +32,8 @@ taste_description = "antiseptic" //makes you squeaky clean -/datum/reagent/medicine/sterilizine/reaction_mob(mob/living/M, method=TOUCH, volume) - if(method == TOUCH) +/datum/reagent/medicine/sterilizine/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) + if(method == REAGENT_TOUCH) M.germ_level -= min(volume*20, M.germ_level) /datum/reagent/medicine/sterilizine/reaction_obj(obj/O, volume) @@ -184,13 +188,13 @@ update_flags |= M.adjustFireLoss(-2*REAGENTS_EFFECT_MULTIPLIER, FALSE) return ..() | update_flags -/datum/reagent/medicine/silver_sulfadiazine/reaction_mob(mob/living/M, method=TOUCH, volume, show_message = 1) +/datum/reagent/medicine/silver_sulfadiazine/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume, show_message = 1) if(iscarbon(M)) - if(method == TOUCH) + if(method == REAGENT_TOUCH) M.adjustFireLoss(-volume) if(show_message) to_chat(M, "The silver sulfadiazine soothes your burns.") - if(method == INGEST) + if(method == REAGENT_INGEST) M.adjustToxLoss(0.5*volume) if(show_message) to_chat(M, "You feel sick...") @@ -211,14 +215,14 @@ update_flags |= M.adjustBruteLoss(-2*REAGENTS_EFFECT_MULTIPLIER, FALSE) return ..() | update_flags -/datum/reagent/medicine/styptic_powder/reaction_mob(mob/living/M, method=TOUCH, volume, show_message = 1) +/datum/reagent/medicine/styptic_powder/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume, show_message = 1) if(iscarbon(M)) - if(method == TOUCH) + if(method == REAGENT_TOUCH) M.adjustBruteLoss(-volume) if(show_message) to_chat(M, "The styptic powder stings like hell as it closes some of your wounds!") M.emote("scream") - if(method == INGEST) + if(method == REAGENT_INGEST) M.adjustToxLoss(0.5*volume) if(show_message) to_chat(M, "You feel gross!") @@ -254,9 +258,9 @@ color = "#FFEBEB" taste_description = "blood" -/datum/reagent/medicine/synthflesh/reaction_mob(mob/living/M, method=TOUCH, volume, show_message = 1) +/datum/reagent/medicine/synthflesh/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume, show_message = 1) if(iscarbon(M)) - if(method == TOUCH) + if(method == REAGENT_TOUCH) M.adjustBruteLoss(-1.5*volume) M.adjustFireLoss(-1.5*volume) if(show_message) @@ -293,7 +297,9 @@ color = "#C8A5DC" metabolization_rate = 0.2 overdose_threshold = 30 - addiction_chance = 5 + addiction_chance = 1 + addiction_chance_additional = 20 + addiction_threshold = 5 harmless = FALSE taste_description = "health" @@ -405,7 +411,7 @@ id = "sal_acid" description = "This is a is a standard salicylate pain reliever and fever reducer." reagent_state = LIQUID - color = "#B3B3B3" + color = "#B54848" metabolization_rate = 0.1 shock_reduction = 25 overdose_threshold = 25 @@ -415,7 +421,26 @@ /datum/reagent/medicine/sal_acid/on_mob_life(mob/living/M) var/update_flags = STATUS_UPDATE_NONE if(prob(55)) - update_flags |= M.adjustBruteLoss(-2*REAGENTS_EFFECT_MULTIPLIER, FALSE) + update_flags |= M.adjustBruteLoss(-2 * REAGENTS_EFFECT_MULTIPLIER, FALSE) + if(M.bodytemperature > 310.15) + M.bodytemperature = max(310.15, M.bodytemperature - 10) + return ..() | update_flags + +/datum/reagent/medicine/menthol + name = "Menthol" + id = "menthol" + description = "Menthol relieves burns and aches while providing a cooling sensation." + reagent_state = LIQUID + color = "#F0F9CA" + metabolization_rate = 0.1 + taste_description = "soothing" + +/datum/reagent/medicine/menthol/on_mob_life(mob/living/M) + var/update_flags = STATUS_UPDATE_NONE + if(prob(55)) + update_flags |= M.adjustFireLoss(-2 * REAGENTS_EFFECT_MULTIPLIER, FALSE) + if(M.bodytemperature > 280) + M.bodytemperature = max(280, M.bodytemperature - 10) return ..() | update_flags /datum/reagent/medicine/salbutamol @@ -440,7 +465,9 @@ reagent_state = LIQUID color = "#C8A5DC" metabolization_rate = 0.2 - addiction_chance = 20 + addiction_chance = 1 + addiction_chance_additional = 20 + addiction_threshold = 10 harmless = FALSE taste_description = "oxygenation" @@ -462,7 +489,9 @@ color = "#C8A5DC" metabolization_rate = 0.3 overdose_threshold = 35 - addiction_chance = 25 + addiction_chance = 1 + addiction_chance = 10 + addiction_threshold = 10 harmless = FALSE taste_description = "stimulation" @@ -512,7 +541,8 @@ description = "Anti-allergy medication. May cause drowsiness, do not operate heavy machinery while using this." reagent_state = LIQUID color = "#5BCBE1" - addiction_chance = 10 + addiction_chance = 1 + addiction_threshold = 10 harmless = FALSE taste_description = "antihistamine" @@ -535,7 +565,8 @@ reagent_state = LIQUID color = "#C8A5DC" overdose_threshold = 20 - addiction_chance = 50 + addiction_chance = 10 + addiction_threshold = 15 shock_reduction = 50 harmless = FALSE taste_description = "a delightful numbing" @@ -689,11 +720,11 @@ update_flags |= M.adjustToxLoss(2*REAGENTS_EFFECT_MULTIPLIER, FALSE) return ..() | update_flags -/datum/reagent/medicine/strange_reagent/reaction_mob(mob/living/M, method = TOUCH, volume) +/datum/reagent/medicine/strange_reagent/reaction_mob(mob/living/M, method = REAGENT_TOUCH, volume) if(volume < 1) // gotta pay to play return ..() - if(isanimal(M) && method == TOUCH) + if(isanimal(M) && method == REAGENT_TOUCH) var/mob/living/simple_animal/SM = M if(SM.sentience_type != revive_type) // No reviving Ash Drakes for you return @@ -703,7 +734,7 @@ SM.visible_message("[SM] seems to rise from the dead!") if(iscarbon(M)) - if(method == INGEST || (method == TOUCH && prob(25))) + if(method == REAGENT_INGEST || (method == REAGENT_TOUCH && prob(25))) if(M.stat == DEAD) if(M.getBruteLoss() + M.getFireLoss() + M.getCloneLoss() >= 150) M.delayed_gib() @@ -877,13 +908,52 @@ M.reagents.remove_reagent("sugar", 5) return ..() +/datum/reagent/heparin + name = "Heparin" + id = "heparin" + description = "An anticoagulant used in heart surgeries, and in the treatment of heart attacks and blood clots." + reagent_state = LIQUID + color = "#eee6da" + overdose_threshold = 20 + taste_description = "bitterness" + +/datum/reagent/heparin/on_mob_life(mob/living/M) + M.reagents.remove_reagent("cholesterol", 2) + return ..() + +/datum/reagent/heparin/overdose_process(mob/living/carbon/M, severity) + var/list/overdose_info = ..() + var/effect = overdose_info[REAGENT_OVERDOSE_EFFECT] + var/update_flags = overdose_info[REAGENT_OVERDOSE_FLAGS] + if(severity == 1) + if(effect <= 2) + M.vomit(0, TRUE, FALSE) + M.blood_volume = max(M.blood_volume - rand(5, 10), 0) + else if(effect <= 4) + M.vomit(0, TRUE, FALSE) + M.blood_volume = max(M.blood_volume - rand(1, 2), 0) + else if(severity == 2) + if(effect <= 2) + M.visible_message("[M] is bleeding from [M.p_their()] very pores!") + M.bleed(rand(10, 20)) + else if(effect <= 4) + M.vomit(0, TRUE, FALSE) + M.blood_volume = max(M.blood_volume - rand(5, 10), 0) + else if(effect <= 8) + M.vomit(0, TRUE, FALSE) + M.blood_volume = max(M.blood_volume - rand(1, 2), 0) + return list(effect, update_flags) + + /datum/reagent/medicine/teporone name = "Teporone" id = "teporone" description = "This experimental plasma-based compound seems to regulate body temperature." reagent_state = LIQUID color = "#D782E6" - addiction_chance = 20 + addiction_chance = 1 + addiction_chance_additional = 10 + addiction_threshold = 10 overdose_threshold = 50 taste_description = "warmth and stability" diff --git a/code/modules/reagents/chemistry/reagents/misc.dm b/code/modules/reagents/chemistry/reagents/misc.dm index cc39fb6c18d..eb6be801434 100644 --- a/code/modules/reagents/chemistry/reagents/misc.dm +++ b/code/modules/reagents/chemistry/reagents/misc.dm @@ -117,7 +117,7 @@ color = "#D0D0D0" // rgb: 208, 208, 208 taste_description = "sub-par bling" -/datum/reagent/silver/reaction_mob(mob/living/M, method=TOUCH, volume) +/datum/reagent/silver/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) if(M.has_bane(BANE_SILVER)) M.reagents.add_reagent("toxin", volume) . = ..() @@ -130,7 +130,6 @@ color = "#A8A8A8" // rgb: 168, 168, 168 taste_description = "metal" - /datum/reagent/silicon name = "Silicon" id = "silicon" @@ -147,6 +146,12 @@ color = "#6E3B08" // rgb: 110, 59, 8 taste_description = "copper" +/datum/reagent/chromium + name = "Chromium" + id = "chromium" + description = "A catalytic chemical element." + color = "#DCDCDC" + taste_description = "bitterness" /datum/reagent/iron name = "Iron" @@ -164,7 +169,7 @@ H.blood_volume += 0.8 return ..() -/datum/reagent/iron/reaction_mob(mob/living/M, method=TOUCH, volume) +/datum/reagent/iron/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) if(M.has_bane(BANE_IRON) && holder && holder.chem_temp < 150) //If the target is weak to cold iron, then poison them. M.reagents.add_reagent("toxin", volume) ..() @@ -303,7 +308,7 @@ H.dna.species.blood_color = "#[num2hex(rand(0, 255))][num2hex(rand(0, 255))][num2hex(rand(0, 255))]" return ..() -/datum/reagent/colorful_reagent/reaction_mob(mob/living/simple_animal/M, method=TOUCH, volume) +/datum/reagent/colorful_reagent/reaction_mob(mob/living/simple_animal/M, method=REAGENT_TOUCH, volume) if(isanimal(M)) M.color = pick(random_color_list) ..() @@ -430,7 +435,7 @@ M.can_change_intents = TRUE ..() -/datum/reagent/love/reaction_mob(mob/living/M, method=TOUCH, volume) +/datum/reagent/love/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) to_chat(M, "You feel loved!") /datum/reagent/jestosterone //Formerly known as Nitrogen tungstide hypochlorite before NT fired the chemists for trying to be funny @@ -667,13 +672,13 @@ overdose_threshold = 11 //Slightly more than one un-nozzled spraybottle. taste_description = "sour oranges" -/datum/reagent/spraytan/reaction_mob(mob/living/M, method=TOUCH, reac_volume, show_message = 1) +/datum/reagent/spraytan/reaction_mob(mob/living/M, method=REAGENT_TOUCH, reac_volume, show_message = 1) if(ishuman(M)) - if(method == TOUCH) + if(method == REAGENT_TOUCH) var/mob/living/carbon/human/N = M set_skin_color(N) - if(method == INGEST) + if(method == REAGENT_INGEST) if(show_message) to_chat(M, "That tasted horrible.") ..() diff --git a/code/modules/reagents/chemistry/reagents/pyrotechnic.dm b/code/modules/reagents/chemistry/reagents/pyrotechnic.dm index 17eee91080e..dc79b5f4b3c 100644 --- a/code/modules/reagents/chemistry/reagents/pyrotechnic.dm +++ b/code/modules/reagents/chemistry/reagents/pyrotechnic.dm @@ -17,12 +17,12 @@ var/radius = min(max(0, volume / size_divisor), 8) fireflash_sm(T, radius, rand(temp_fire - temp_deviance, temp_fire + temp_deviance), 500) -/datum/reagent/phlogiston/reaction_mob(mob/living/M, method = TOUCH, volume) +/datum/reagent/phlogiston/reaction_mob(mob/living/M, method = REAGENT_TOUCH, volume) if(holder.chem_temp <= T0C - 50) return M.adjust_fire_stacks(mob_burning) M.IgniteMob() - if(method == INGEST) + if(method == REAGENT_INGEST) M.adjustFireLoss(min(max(10, volume * 2), 45)) to_chat(M, "It burns!") M.emote("scream") @@ -66,8 +66,8 @@ T.create_reagents(volume) T.reagents.add_reagent("napalm", volume) -/datum/reagent/napalm/reaction_mob(mob/living/M, method = TOUCH, volume) - if(method == TOUCH) +/datum/reagent/napalm/reaction_mob(mob/living/M, method = REAGENT_TOUCH, volume) + if(method == REAGENT_TOUCH) if(M.on_fire) M.adjust_fire_stacks(14) M.emote("scream") @@ -131,8 +131,8 @@ T.create_reagents(50) T.reagents.add_reagent("fuel", volume) -/datum/reagent/fuel/reaction_mob(mob/living/M, method=TOUCH, volume)//Splashing people with welding fuel to make them easy to ignite! - if(method == TOUCH) +/datum/reagent/fuel/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume)//Splashing people with welding fuel to make them easy to ignite! + if(method == REAGENT_TOUCH) if(M.on_fire) M.adjust_fire_stacks(6) @@ -161,8 +161,8 @@ C.adjustPlasma(10) return ..() | update_flags -/datum/reagent/plasma/reaction_mob(mob/living/M, method = TOUCH, volume)//Splashing people with plasma is stronger than fuel! - if(method == TOUCH) +/datum/reagent/plasma/reaction_mob(mob/living/M, method = REAGENT_TOUCH, volume)//Splashing people with plasma is stronger than fuel! + if(method == REAGENT_TOUCH) if(M.on_fire) M.adjust_fire_stacks(6) @@ -176,8 +176,8 @@ process_flags = ORGANIC | SYNTHETIC taste_description = "rust" -/datum/reagent/thermite/reaction_mob(mob/living/M, method= TOUCH, volume) - if(method == TOUCH) +/datum/reagent/thermite/reaction_mob(mob/living/M, method= REAGENT_TOUCH, volume) + if(method == REAGENT_TOUCH) if(M.on_fire) M.adjust_fire_stacks(20) @@ -198,6 +198,7 @@ if(!S.reagents) S.create_reagents(volume) S.reagents.add_reagent("thermite", volume) + S.thermite = TRUE S.overlays.Cut() S.overlays = image('icons/effects/effects.dmi', icon_state = "thermite") if(S.active_hotspot) @@ -240,11 +241,11 @@ var/radius = min((volume - 3) * 0.15, 3) fireflash_sm(T, radius, 4500 + volume * 500, 350) -/datum/reagent/clf3/reaction_mob(mob/living/M, method = TOUCH, volume) - if(method == TOUCH || method == INGEST) +/datum/reagent/clf3/reaction_mob(mob/living/M, method = REAGENT_TOUCH, volume) + if(method == REAGENT_TOUCH || method == REAGENT_INGEST) M.adjust_fire_stacks(10) M.IgniteMob() - if(method == INGEST) + if(method == REAGENT_INGEST) M.adjustFireLoss(min(max(15, volume * 2.5), 90)) to_chat(M, "It burns!") M.emote("scream") @@ -346,8 +347,8 @@ holder.temperature_reagents(holder.chem_temp - 200) ..() -/datum/reagent/cryostylane/reaction_mob(mob/living/M, method = TOUCH, volume) - if(method == TOUCH) +/datum/reagent/cryostylane/reaction_mob(mob/living/M, method = REAGENT_TOUCH, volume) + if(method == REAGENT_TOUCH) M.ExtinguishMob() /datum/reagent/cryostylane/reaction_turf(turf/simulated/T, volume) @@ -390,9 +391,9 @@ var/cooling_temperature = 3 // more effective than water taste_description = "the inside of a fire extinguisher" -/datum/reagent/firefighting_foam/reaction_mob(mob/living/M, method=TOUCH, volume) +/datum/reagent/firefighting_foam/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) // Put out fire - if(method == TOUCH) + if(method == REAGENT_TOUCH) M.adjust_fire_stacks(-10) // more effective than water /datum/reagent/firefighting_foam/reaction_obj(obj/O, volume) @@ -433,8 +434,8 @@ C.adjustPlasma(20) return ..() | update_flags -/datum/reagent/plasma_dust/reaction_mob(mob/living/M, method=TOUCH, volume)//Splashing people with plasma dust is stronger than fuel! - if(method == TOUCH) +/datum/reagent/plasma_dust/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume)//Splashing people with plasma dust is stronger than fuel! + if(method == REAGENT_TOUCH) M.adjust_fire_stacks(volume / 5) return ..() diff --git a/code/modules/reagents/chemistry/reagents/toxins.dm b/code/modules/reagents/chemistry/reagents/toxins.dm index 59a7d07e241..28ed544cb18 100644 --- a/code/modules/reagents/chemistry/reagents/toxins.dm +++ b/code/modules/reagents/chemistry/reagents/toxins.dm @@ -85,8 +85,8 @@ can_synth = FALSE taste_description = "slime" -/datum/reagent/aslimetoxin/reaction_mob(mob/living/M, method=TOUCH, volume) - if(method != TOUCH) +/datum/reagent/aslimetoxin/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) + if(method != REAGENT_TOUCH) M.ForceContractDisease(new /datum/disease/transformation/slime(0)) @@ -165,12 +165,12 @@ taste_mult = 0.9 taste_description = "slime" -/datum/reagent/mutagen/reaction_mob(mob/living/M, method=TOUCH, volume) +/datum/reagent/mutagen/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) if(!..()) return if(!M.dna) return //No robots, AIs, aliens, Ians or other mobs should be affected by this. - if((method==TOUCH && prob(33)) || method==INGEST) + if((method==REAGENT_TOUCH && prob(33)) || method==REAGENT_INGEST) randmutb(M) domutcheck(M, null) M.UpdateAppearance() @@ -232,7 +232,7 @@ can_synth = FALSE taste_description = "CAAAARL" -/datum/reagent/romerol/reaction_mob(mob/living/carbon/human/H, method = TOUCH, volume) +/datum/reagent/romerol/reaction_mob(mob/living/carbon/human/H, method = REAGENT_TOUCH, volume) if(!istype(H)) return // Silently add the zombie infection organ to be activated upon death @@ -297,10 +297,10 @@ update_flags |= M.adjustFireLoss(1, FALSE) return ..() | update_flags -/datum/reagent/acid/reaction_mob(mob/living/M, method = TOUCH, volume) +/datum/reagent/acid/reaction_mob(mob/living/M, method = REAGENT_TOUCH, volume) if(ishuman(M) && !isgrey(M)) var/mob/living/carbon/human/H = M - if(method == TOUCH) + if(method == REAGENT_TOUCH) if(volume > 25) if(H.wear_mask) to_chat(H, "Your [H.wear_mask] protects you from the acid!") @@ -350,10 +350,10 @@ update_flags |= M.adjustToxLoss(1*REAGENTS_EFFECT_MULTIPLIER, FALSE) return ..() | update_flags -/datum/reagent/acid/facid/reaction_mob(mob/living/M, method = TOUCH, volume) +/datum/reagent/acid/facid/reaction_mob(mob/living/M, method = REAGENT_TOUCH, volume) if(ishuman(M)) var/mob/living/carbon/human/H = M - if(method == TOUCH) + if(method == REAGENT_TOUCH) if(volume > 9) if(!H.wear_mask && !H.head) var/obj/item/organ/external/affecting = H.get_organ("head") @@ -383,6 +383,36 @@ H.adjustFireLoss(min(max(8, (volume - 5) * 3), 75)) to_chat(H, "The blueish acidic substance stings[volume < 5 ? " you, but isn't concentrated enough to harm you" : null]!") +/datum/reagent/acetic_acid + name = "acetic acid" + id = "acetic_acid" + description = "A weak acid that is the main component of vinegar and bad hangovers." + color = "#0080ff" + reagent_state = LIQUID + taste_description = "vinegar" + +/datum/reagent/acetic_acid/reaction_mob(mob/M, method = REAGENT_TOUCH, volume) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(method == REAGENT_TOUCH) + if(H.wear_mask || H.head) + return + if(volume >= 50 && prob(75)) + var/obj/item/organ/external/affecting = H.get_organ("head") + if(affecting) + affecting.disfigure() + H.adjustBruteLoss(5) + H.adjustFireLoss(15) + H.emote("scream") + else + H.adjustBruteLoss(min(5, volume * 0.25)) + else + to_chat(H, "The transparent acidic substance stings[volume < 25 ? " you, but isn't concentrated enough to harm you" : null]!") + if(volume >= 25) + H.adjustBruteLoss(2) + H.emote("scream") + + /datum/reagent/carpotoxin name = "Carpotoxin" id = "carpotoxin" @@ -473,9 +503,9 @@ overdose_threshold = 40 taste_mult = 0 -/datum/reagent/histamine/reaction_mob(mob/living/M, method=TOUCH, volume) //dumping histamine on someone is VERY mean. +/datum/reagent/histamine/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) //dumping histamine on someone is VERY mean. if(iscarbon(M)) - if(method == TOUCH) + if(method == REAGENT_TOUCH) M.reagents.add_reagent("histamine",10) else to_chat(M, "You feel a burning sensation in your throat...") @@ -546,7 +576,7 @@ id = "formaldehyde" description = "Formaldehyde is a common industrial chemical and is used to preserve corpses and medical samples. It is highly toxic and irritating." reagent_state = LIQUID - color = "#DED6D0" + color = "#B44B00" penetrates_skin = TRUE taste_description = "bitterness" @@ -557,6 +587,20 @@ M.reagents.add_reagent("histamine",rand(5,15)) return ..() | update_flags +/datum/reagent/acetaldehyde + name = "Acetaldehyde" + id = "acetaldehyde" + description = "Acetaldehyde is a common industrial chemical. It is a severe irritant." + reagent_state = LIQUID + color = "#B44B00" + penetrates_skin = TRUE + taste_description = "apples" + +/datum/reagent/acetaldehyde/on_mob_life(mob/living/M) + var/update_flags = STATUS_UPDATE_NONE + update_flags |= M.adjustFireLoss(1 * REAGENTS_EFFECT_MULTIPLIER, FALSE) + return ..() | update_flags + /datum/reagent/venom name = "Venom" id = "venom" @@ -924,23 +968,6 @@ M.AdjustLoseBreath(1) return ..() | update_flags -/datum/reagent/heparin //Based on a real-life anticoagulant. - name = "Heparin" - id = "heparin" - description = "A powerful anticoagulant. Victims will bleed uncontrollably and suffer scaling bruising." - reagent_state = LIQUID - color = "#C8C8C8" //RGB: 200, 200, 200 - metabolization_rate = 0.2 * REAGENTS_METABOLISM - taste_mult = 0 - -/datum/reagent/heparin/on_mob_life(mob/living/M) - var/update_flags = STATUS_UPDATE_NONE - if(ishuman(M)) - var/mob/living/carbon/human/H = M - H.bleed_rate = min(H.bleed_rate + 2, 8) - update_flags |= H.adjustBruteLoss(1, FALSE) - return ..() | update_flags - /datum/reagent/sarin name = "Sarin" id = "sarin" @@ -1030,7 +1057,7 @@ var/obj/structure/spacevine/SV = O SV.on_chem_effect(src) -/datum/reagent/glyphosate/reaction_mob(mob/living/M, method=TOUCH, volume) +/datum/reagent/glyphosate/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) if(iscarbon(M)) var/mob/living/carbon/C = M if(!C.wear_mask) // If not wearing a mask @@ -1071,7 +1098,7 @@ O.visible_message("The ants die.") qdel(O) -/datum/reagent/pestkiller/reaction_mob(mob/living/M, method=TOUCH, volume) +/datum/reagent/pestkiller/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) if(iscarbon(M)) var/mob/living/carbon/C = M if(!C.wear_mask) // If not wearing a mask @@ -1161,12 +1188,12 @@ color = "#00FD00" taste_description = "slime" -/datum/reagent/glowing_slurry/reaction_mob(mob/living/M, method=TOUCH, volume) //same as mutagen +/datum/reagent/glowing_slurry/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) //same as mutagen if(!..()) return if(!M.dna) return //No robots, AIs, aliens, Ians or other mobs should be affected by this. - if((method==TOUCH && prob(50)) || method==INGEST) + if((method==REAGENT_TOUCH && prob(50)) || method==REAGENT_INGEST) randmutb(M) domutcheck(M, null) M.UpdateAppearance() @@ -1201,9 +1228,9 @@ update_flags |= M.adjustBruteLoss(2, FALSE) return ..() | update_flags -/datum/reagent/ants/reaction_mob(mob/living/M, method=TOUCH, volume) //NOT THE ANTS +/datum/reagent/ants/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) //NOT THE ANTS if(iscarbon(M)) - if(method == TOUCH || method==INGEST) + if(method == REAGENT_TOUCH || method==REAGENT_INGEST) to_chat(M, "OH SHIT ANTS!!!!") M.emote("scream") M.adjustBruteLoss(4) @@ -1235,5 +1262,5 @@ color = "#5EFF3B" //RGB: 94, 255, 59 taste_description = "decay" -/datum/reagent/gluttonytoxin/reaction_mob(mob/living/L, method=TOUCH, reac_volume) +/datum/reagent/gluttonytoxin/reaction_mob(mob/living/L, method=REAGENT_TOUCH, reac_volume) L.ForceContractDisease(new /datum/disease/transformation/morph()) diff --git a/code/modules/reagents/chemistry/reagents/water.dm b/code/modules/reagents/chemistry/reagents/water.dm index b56fecf342a..c6960aac916 100644 --- a/code/modules/reagents/chemistry/reagents/water.dm +++ b/code/modules/reagents/chemistry/reagents/water.dm @@ -21,7 +21,7 @@ drink_desc = "The father of all refreshments." var/water_temperature = 283.15 // As reagents don't have a temperature value, we'll just use 10 celsius. -/datum/reagent/water/reaction_mob(mob/living/M, method = TOUCH, volume) +/datum/reagent/water/reaction_mob(mob/living/M, method = REAGENT_TOUCH, volume) M.water_act(volume, water_temperature, src, method) /datum/reagent/water/reaction_turf(turf/T, volume) @@ -82,7 +82,7 @@ for(var/mob/living/simple_animal/slime/M in T) M.adjustToxLoss(rand(5, 10)) -/datum/reagent/space_cleaner/reaction_mob(mob/living/M, method=TOUCH, volume) +/datum/reagent/space_cleaner/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) if(iscarbon(M)) var/mob/living/carbon/C = M if(ishuman(M)) @@ -128,7 +128,7 @@ taste_description = "blood" taste_mult = 1.3 -/datum/reagent/blood/reaction_mob(mob/living/M, method=TOUCH, volume) +/datum/reagent/blood/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) if(data && data["viruses"]) for(var/thing in data["viruses"]) var/datum/disease/D = thing @@ -136,12 +136,12 @@ if(D.spread_flags & SPECIAL || D.spread_flags & NON_CONTAGIOUS) continue - if(method == TOUCH) + if(method == REAGENT_TOUCH) M.ContractDisease(D) else //ingest, patch or inject M.ForceContractDisease(D) - if(method == INGEST && iscarbon(M)) + if(method == REAGENT_INGEST && iscarbon(M)) var/mob/living/carbon/C = M if(C.get_blood_id() == "blood") if((!data || !(data["blood_type"] in get_safe_blood(C.dna.blood_type)))) @@ -210,8 +210,8 @@ color = "#C81040" // rgb: 200, 16, 64 taste_description = "antibodies" -/datum/reagent/vaccine/reaction_mob(mob/living/M, method=TOUCH, volume) - if(islist(data) && (method == INGEST)) +/datum/reagent/vaccine/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) + if(islist(data) && (method == REAGENT_INGEST)) for(var/thing in M.viruses) var/datum/disease/D = thing if(D.GetDiseaseID() in data) @@ -230,8 +230,8 @@ color = "#757547" taste_description = "puke" -/datum/reagent/fishwater/reaction_mob(mob/living/M, method=TOUCH, volume) - if(method == INGEST) +/datum/reagent/fishwater/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) + if(method == REAGENT_INGEST) to_chat(M, "Oh god, why did you drink that?") /datum/reagent/fishwater/on_mob_life(mob/living/M) @@ -250,7 +250,7 @@ color = "#757547" taste_description = "the inside of a toilet... or worse" -/datum/reagent/fishwater/toiletwater/reaction_mob(mob/living/M, method=TOUCH, volume) //For shennanigans +/datum/reagent/fishwater/toiletwater/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) //For shennanigans return /datum/reagent/holywater @@ -334,11 +334,11 @@ return ..() | update_flags -/datum/reagent/holywater/reaction_mob(mob/living/M, method=TOUCH, volume) +/datum/reagent/holywater/reaction_mob(mob/living/M, method=REAGENT_TOUCH, volume) // Vampires have their powers weakened by holy water applied to the skin. if(ishuman(M) && M.mind && M.mind.vampire && !M.mind.vampire.get_ability(/datum/vampire_passive/full)) var/mob/living/carbon/human/H=M - if(method == TOUCH) + if(method == REAGENT_TOUCH) if(H.wear_mask) to_chat(H, "Your mask protects you from the holy water!") return diff --git a/code/modules/reagents/chemistry/recipes.dm b/code/modules/reagents/chemistry/recipes.dm index 144f7b9a8ac..2d99b4025bd 100644 --- a/code/modules/reagents/chemistry/recipes.dm +++ b/code/modules/reagents/chemistry/recipes.dm @@ -66,4 +66,4 @@ if(setting_type) X.throw_at(T, 20 + round(volume * 2), 1 + round(volume / 10)) else - X.throw_at(get_edge_target_turf(T, get_dir(T, X)), 20 + round(volume * 2), 1 + round(volume / 10)) \ No newline at end of file + X.throw_at(get_edge_target_turf(T, get_dir(T, X)), 20 + round(volume * 2), 1 + round(volume / 10)) diff --git a/code/modules/reagents/chemistry/recipes/drinks.dm b/code/modules/reagents/chemistry/recipes/drinks.dm index 5d088bd9ccb..1f15c95bbf2 100644 --- a/code/modules/reagents/chemistry/recipes/drinks.dm +++ b/code/modules/reagents/chemistry/recipes/drinks.dm @@ -867,4 +867,4 @@ result = "bacchus_blessing" required_reagents = list("hooch" = 1, "absinthe" = 1, "manlydorf" = 1, "syndicatebomb" = 1) result_amount = 4 - mix_message = "The mixture turns to a sickening froth." \ No newline at end of file + mix_message = "The mixture turns to a sickening froth." diff --git a/code/modules/reagents/chemistry/recipes/drugs.dm b/code/modules/reagents/chemistry/recipes/drugs.dm index d2499a06c54..f8c16598946 100644 --- a/code/modules/reagents/chemistry/recipes/drugs.dm +++ b/code/modules/reagents/chemistry/recipes/drugs.dm @@ -113,4 +113,4 @@ result = "surge" required_reagents = list("thermite" = 3, "uranium" = 1, "fluorosurfactant" = 1, "sacid" = 1) result_amount = 6 - mix_message = "The mixture congeals into a metallic green gel that crackles with electrical activity." \ No newline at end of file + mix_message = "The mixture congeals into a metallic green gel that crackles with electrical activity." diff --git a/code/modules/reagents/chemistry/recipes/medicine.dm b/code/modules/reagents/chemistry/recipes/medicine.dm index 8bce89128d6..f7fb78dcdec 100644 --- a/code/modules/reagents/chemistry/recipes/medicine.dm +++ b/code/modules/reagents/chemistry/recipes/medicine.dm @@ -69,6 +69,13 @@ required_reagents = list("sodiumchloride" = 1, "water" = 1, "sugar" = 1) result_amount = 3 +/datum/chemical_reaction/heparin + name = "Heparin" + id = "Heparin" + result = "heparin" + required_reagents = list("sugar" = 1, "meatslurry" = 1, "phenol" = 1, "sacid" = 1) + result_amount = 2 + /datum/chemical_reaction/synthflesh name = "Synthflesh" id = "synthflesh" @@ -272,4 +279,4 @@ required_reagents = list("ethanol" = 1, "copper" = 1, "silver" = 1) result_amount = 3 min_temp = T0C + 100 - mix_message = "The solution gently swirls with a metallic sheen." \ No newline at end of file + mix_message = "The solution gently swirls with a metallic sheen." diff --git a/code/modules/reagents/chemistry/recipes/others.dm b/code/modules/reagents/chemistry/recipes/others.dm index c118af7d763..e0f9b41e01c 100644 --- a/code/modules/reagents/chemistry/recipes/others.dm +++ b/code/modules/reagents/chemistry/recipes/others.dm @@ -322,6 +322,23 @@ result_amount = 3 mix_message = "The solution crystallizes with a brief flare of light." +/datum/chemical_reaction/acetaldehyde + name = "Acetaldehyde" + id = "acetaldehyde" + result = "acetaldehyde" + required_reagents = list("chromium" = 1, "oxygen" = 1, "copper" = 1, "ethanol" = 1) + result_amount = 3 + min_temp = T0C + 275 + mix_message = "It smells like a bad hangover in here." + +/datum/chemical_reaction/acetic_acid + name = "Acetic Acid" + id = "acetic_acid" + result = "acetic_acid" + required_reagents = list("acetaldehyde" = 1, "oxygen" = 1, "nitrogen" = 4) + result_amount = 3 + mix_message = "It smells like vinegar and a bad hangover in here." + /datum/chemical_reaction/ice name = "Ice" id = "ice" diff --git a/code/modules/reagents/chemistry/recipes/slime_extracts.dm b/code/modules/reagents/chemistry/recipes/slime_extracts.dm index 1767d3964a1..b1fb33edf85 100644 --- a/code/modules/reagents/chemistry/recipes/slime_extracts.dm +++ b/code/modules/reagents/chemistry/recipes/slime_extracts.dm @@ -306,7 +306,7 @@ if(holder && holder.my_atom) var/turf/simulated/T = get_turf(holder.my_atom) if(istype(T)) - T.atmos_spawn_air(SPAWN_HEAT | SPAWN_TOXINS, 50) + T.atmos_spawn_air(LINDA_SPAWN_HEAT | LINDA_SPAWN_TOXINS, 50) //Yellow diff --git a/code/modules/reagents/chemistry/recipes/toxins.dm b/code/modules/reagents/chemistry/recipes/toxins.dm index 6d93a575240..977e3e0b276 100644 --- a/code/modules/reagents/chemistry/recipes/toxins.dm +++ b/code/modules/reagents/chemistry/recipes/toxins.dm @@ -167,19 +167,10 @@ required_reagents = list("mutadone" = 3, "lithium" = 1) result_amount = 4 -/datum/chemical_reaction/heparin - name = "Heparin" - id = "Heparin" - result = "heparin" - required_reagents = list("formaldehyde" = 1, "sodium" = 1, "chlorine" = 1, "lithium" = 1) - result_amount = 4 - mix_message = "The mixture thins and loses all color." - mix_sound = 'sound/goonstation/misc/drinkfizz.ogg' - /datum/chemical_reaction/rotatium name = "Rotatium" id = "Rotatium" result = "rotatium" required_reagents = list("lsd" = 1, "teslium" = 1, "methamphetamine" = 1) result_amount = 3 - mix_message = "After sparks, fire, and the smell of LSD, the mix is constantly spinning with no stop in sight." \ No newline at end of file + mix_message = "After sparks, fire, and the smell of LSD, the mix is constantly spinning with no stop in sight." diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm index 41f9524874b..db65f761e2e 100644 --- a/code/modules/reagents/reagent_containers.dm +++ b/code/modules/reagents/reagent_containers.dm @@ -1,74 +1,74 @@ -/obj/item/reagent_containers - name = "Container" - desc = "..." - icon = 'icons/obj/chemical.dmi' - icon_state = null - w_class = WEIGHT_CLASS_TINY - var/amount_per_transfer_from_this = 5 - var/possible_transfer_amounts = list(5,10,15,25,30) - var/volume = 30 - var/list/list_reagents = null - var/spawned_disease = null - var/disease_amount = 20 - var/has_lid = FALSE // Used for containers where we want to put lids on and off - -/obj/item/reagent_containers/verb/set_APTFT() //set amount_per_transfer_from_this - set name = "Set transfer amount" - set category = "Object" - set src in range(0) - - if(usr.stat || !usr.canmove || usr.restrained()) - return - var/default = null - if(amount_per_transfer_from_this in possible_transfer_amounts) - default = amount_per_transfer_from_this - var/N = input("Amount per transfer from this:", "[src]", default) as null|anything in possible_transfer_amounts - if(N) - amount_per_transfer_from_this = N - -/obj/item/reagent_containers/New() - ..() - if(!possible_transfer_amounts) - verbs -= /obj/item/reagent_containers/verb/set_APTFT - create_reagents(volume) - if(spawned_disease) - var/datum/disease/F = new spawned_disease(0) - var/list/data = list("viruses" = list(F), "blood_color" = "#A10808") - reagents.add_reagent("blood", disease_amount, data) - add_initial_reagents() - -obj/item/reagent_containers/proc/add_initial_reagents() - if(list_reagents) - reagents.add_reagent_list(list_reagents) - -/obj/item/reagent_containers/ex_act() - if(reagents) - for(var/datum/reagent/R in reagents.reagent_list) - R.on_ex_act() - if(!QDELETED(src)) - ..() - -/obj/item/reagent_containers/attack_self(mob/user) - if(has_lid) - if(is_open_container()) - to_chat(usr, "You put the lid on [src].") - container_type ^= REFILLABLE | DRAINABLE - else - to_chat(usr, "You take the lid off [src].") - container_type |= REFILLABLE | DRAINABLE - update_icon() - return - -/obj/item/reagent_containers/afterattack(obj/target, mob/user , flag) - return - -/obj/item/reagent_containers/wash(mob/user, atom/source) - if(is_open_container()) - if(reagents.total_volume >= volume) - to_chat(user, "[src] is full.") - return - else - reagents.add_reagent("water", min(volume - reagents.total_volume, amount_per_transfer_from_this)) - to_chat(user, "You fill [src] from [source].") - return - ..() +/obj/item/reagent_containers + name = "Container" + desc = "..." + icon = 'icons/obj/chemical.dmi' + icon_state = null + w_class = WEIGHT_CLASS_TINY + var/amount_per_transfer_from_this = 5 + var/possible_transfer_amounts = list(5,10,15,25,30) + var/volume = 30 + var/list/list_reagents = null + var/spawned_disease = null + var/disease_amount = 20 + var/has_lid = FALSE // Used for containers where we want to put lids on and off + +/obj/item/reagent_containers/verb/set_APTFT() //set amount_per_transfer_from_this + set name = "Set transfer amount" + set category = "Object" + set src in range(0) + + if(usr.stat || !usr.canmove || usr.restrained()) + return + var/default = null + if(amount_per_transfer_from_this in possible_transfer_amounts) + default = amount_per_transfer_from_this + var/N = input("Amount per transfer from this:", "[src]", default) as null|anything in possible_transfer_amounts + if(N) + amount_per_transfer_from_this = N + +/obj/item/reagent_containers/New() + ..() + if(!possible_transfer_amounts) + verbs -= /obj/item/reagent_containers/verb/set_APTFT + create_reagents(volume) + if(spawned_disease) + var/datum/disease/F = new spawned_disease(0) + var/list/data = list("viruses" = list(F), "blood_color" = "#A10808") + reagents.add_reagent("blood", disease_amount, data) + add_initial_reagents() + +obj/item/reagent_containers/proc/add_initial_reagents() + if(list_reagents) + reagents.add_reagent_list(list_reagents) + +/obj/item/reagent_containers/ex_act() + if(reagents) + for(var/datum/reagent/R in reagents.reagent_list) + R.on_ex_act() + if(!QDELETED(src)) + ..() + +/obj/item/reagent_containers/attack_self(mob/user) + if(has_lid) + if(is_open_container()) + to_chat(usr, "You put the lid on [src].") + container_type ^= REFILLABLE | DRAINABLE + else + to_chat(usr, "You take the lid off [src].") + container_type |= REFILLABLE | DRAINABLE + update_icon() + return + +/obj/item/reagent_containers/afterattack(obj/target, mob/user , flag) + return + +/obj/item/reagent_containers/wash(mob/user, atom/source) + if(is_open_container()) + if(reagents.total_volume >= volume) + to_chat(user, "[src] is full.") + return + else + reagents.add_reagent("water", min(volume - reagents.total_volume, amount_per_transfer_from_this)) + to_chat(user, "You fill [src] from [source].") + return + ..() diff --git a/code/modules/reagents/reagent_containers/borghydro.dm b/code/modules/reagents/reagent_containers/borghydro.dm index d82c128be47..f80fe2cb62a 100644 --- a/code/modules/reagents/reagent_containers/borghydro.dm +++ b/code/modules/reagents/reagent_containers/borghydro.dm @@ -1,116 +1,116 @@ - -/obj/item/reagent_containers/borghypo - name = "Cyborg Hypospray" - desc = "An advanced chemical synthesizer and injection system, designed for heavy-duty medical equipment." - icon = 'icons/obj/hypo.dmi' - item_state = "hypo" - icon_state = "borghypo" - amount_per_transfer_from_this = 5 - volume = 30 - possible_transfer_amounts = null - var/mode = 1 - var/charge_cost = 50 - var/charge_tick = 0 - var/recharge_time = 5 //Time it takes for shots to recharge (in seconds) - var/bypass_protection = 0 //If the hypospray can go through armor or thick material - - var/list/datum/reagents/reagent_list = list() - var/list/reagent_ids = list("salglu_solution", "epinephrine", "spaceacillin", "charcoal", "hydrocodone") - //var/list/reagent_ids = list("salbutamol", "silver_sulfadiazine", "styptic_powder", "charcoal", "epinephrine", "spaceacillin", "hydrocodone") - -/obj/item/reagent_containers/borghypo/surgeon - reagent_ids = list("styptic_powder", "epinephrine", "salbutamol") - -/obj/item/reagent_containers/borghypo/crisis - reagent_ids = list("salglu_solution", "epinephrine", "sal_acid") - -/obj/item/reagent_containers/borghypo/syndicate - name = "syndicate cyborg hypospray" - desc = "An experimental piece of Syndicate technology used to produce powerful restorative nanites used to very quickly restore injuries of all types. Also metabolizes potassium iodide, for radiation poisoning, and hydrocodone, for field surgery and pain relief." - icon_state = "borghypo_s" - charge_cost = 20 - recharge_time = 2 - reagent_ids = list("syndicate_nanites", "potass_iodide", "hydrocodone") - bypass_protection = 1 - -/obj/item/reagent_containers/borghypo/New() - ..() - for(var/R in reagent_ids) - add_reagent(R) - - START_PROCESSING(SSobj, src) - -/obj/item/reagent_containers/borghypo/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/reagent_containers/borghypo/process() //Every [recharge_time] seconds, recharge some reagents for the cyborg - charge_tick++ - if(charge_tick < recharge_time) return 0 - charge_tick = 0 - - if(isrobot(loc)) - var/mob/living/silicon/robot/R = loc - if(R && R.cell) - var/datum/reagents/RG = reagent_list[mode] - if(RG.total_volume < RG.maximum_volume) //Don't recharge reagents and drain power if the storage is full. - R.cell.use(charge_cost) //Take power from borg... - RG.add_reagent(reagent_ids[mode], 5) //And fill hypo with reagent. - //update_icon() - return 1 - -// Use this to add more chemicals for the borghypo to produce. -/obj/item/reagent_containers/borghypo/proc/add_reagent(reagent) - reagent_ids |= reagent - var/datum/reagents/RG = new(30) - RG.my_atom = src - reagent_list += RG - - var/datum/reagents/R = reagent_list[reagent_list.len] - R.add_reagent(reagent, 30) - -/obj/item/reagent_containers/borghypo/attack(mob/living/M, mob/user) - var/datum/reagents/R = reagent_list[mode] - if(!R.total_volume) - to_chat(user, "The injector is empty.") - return - if(!istype(M)) - return - if(R.total_volume && M.can_inject(user, TRUE, user.zone_selected, penetrate_thick = bypass_protection)) - to_chat(user, "You inject [M] with the injector.") - to_chat(M, "You feel a tiny prick!") - - R.add_reagent(M) - if(M.reagents) - var/datum/reagent/injected = GLOB.chemical_reagents_list[reagent_ids[mode]] - var/contained = injected.name - var/trans = R.trans_to(M, amount_per_transfer_from_this) - add_attack_logs(user, M, "Injected with [name] containing [contained], transfered [trans] units", injected.harmless ? ATKLOG_ALMOSTALL : null) - M.LAssailant = user - to_chat(user, "[trans] units injected. [R.total_volume] units remaining.") - return - -/obj/item/reagent_containers/borghypo/attack_self(mob/user) - playsound(loc, 'sound/effects/pop.ogg', 50, 0) //Change the mode - mode++ - if(mode > reagent_list.len) - mode = 1 - - charge_tick = 0 //Prevents wasted chems/cell charge if you're cycling through modes. - var/datum/reagent/R = GLOB.chemical_reagents_list[reagent_ids[mode]] - to_chat(user, "Synthesizer is now producing '[R.name]'.") - return - -/obj/item/reagent_containers/borghypo/examine(mob/user) - . = ..() - if(get_dist(user, src) <= 2) - var/empty = TRUE - - for(var/datum/reagents/RS in reagent_list) - var/datum/reagent/R = locate() in RS.reagent_list - if(R) - . += "It currently has [R.volume] units of [R.name] stored." - empty = FALSE - - if(empty) - . += "It is currently empty. Allow some time for the internal syntheszier to produce more." + +/obj/item/reagent_containers/borghypo + name = "Cyborg Hypospray" + desc = "An advanced chemical synthesizer and injection system, designed for heavy-duty medical equipment." + icon = 'icons/obj/hypo.dmi' + item_state = "hypo" + icon_state = "borghypo" + amount_per_transfer_from_this = 5 + volume = 30 + possible_transfer_amounts = null + var/mode = 1 + var/charge_cost = 50 + var/charge_tick = 0 + var/recharge_time = 5 //Time it takes for shots to recharge (in seconds) + var/bypass_protection = 0 //If the hypospray can go through armor or thick material + + var/list/datum/reagents/reagent_list = list() + var/list/reagent_ids = list("salglu_solution", "epinephrine", "spaceacillin", "charcoal", "hydrocodone") + //var/list/reagent_ids = list("salbutamol", "silver_sulfadiazine", "styptic_powder", "charcoal", "epinephrine", "spaceacillin", "hydrocodone") + +/obj/item/reagent_containers/borghypo/surgeon + reagent_ids = list("styptic_powder", "epinephrine", "salbutamol") + +/obj/item/reagent_containers/borghypo/crisis + reagent_ids = list("salglu_solution", "epinephrine", "sal_acid") + +/obj/item/reagent_containers/borghypo/syndicate + name = "syndicate cyborg hypospray" + desc = "An experimental piece of Syndicate technology used to produce powerful restorative nanites used to very quickly restore injuries of all types. Also metabolizes potassium iodide, for radiation poisoning, and hydrocodone, for field surgery and pain relief." + icon_state = "borghypo_s" + charge_cost = 20 + recharge_time = 2 + reagent_ids = list("syndicate_nanites", "potass_iodide", "hydrocodone") + bypass_protection = 1 + +/obj/item/reagent_containers/borghypo/New() + ..() + for(var/R in reagent_ids) + add_reagent(R) + + START_PROCESSING(SSobj, src) + +/obj/item/reagent_containers/borghypo/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/reagent_containers/borghypo/process() //Every [recharge_time] seconds, recharge some reagents for the cyborg + charge_tick++ + if(charge_tick < recharge_time) return 0 + charge_tick = 0 + + if(isrobot(loc)) + var/mob/living/silicon/robot/R = loc + if(R && R.cell) + var/datum/reagents/RG = reagent_list[mode] + if(RG.total_volume < RG.maximum_volume) //Don't recharge reagents and drain power if the storage is full. + R.cell.use(charge_cost) //Take power from borg... + RG.add_reagent(reagent_ids[mode], 5) //And fill hypo with reagent. + //update_icon() + return 1 + +// Use this to add more chemicals for the borghypo to produce. +/obj/item/reagent_containers/borghypo/proc/add_reagent(reagent) + reagent_ids |= reagent + var/datum/reagents/RG = new(30) + RG.my_atom = src + reagent_list += RG + + var/datum/reagents/R = reagent_list[reagent_list.len] + R.add_reagent(reagent, 30) + +/obj/item/reagent_containers/borghypo/attack(mob/living/M, mob/user) + var/datum/reagents/R = reagent_list[mode] + if(!R.total_volume) + to_chat(user, "The injector is empty.") + return + if(!istype(M)) + return + if(R.total_volume && M.can_inject(user, TRUE, user.zone_selected, penetrate_thick = bypass_protection)) + to_chat(user, "You inject [M] with the injector.") + to_chat(M, "You feel a tiny prick!") + + R.add_reagent(M) + if(M.reagents) + var/datum/reagent/injected = GLOB.chemical_reagents_list[reagent_ids[mode]] + var/contained = injected.name + var/trans = R.trans_to(M, amount_per_transfer_from_this) + add_attack_logs(user, M, "Injected with [name] containing [contained], transfered [trans] units", injected.harmless ? ATKLOG_ALMOSTALL : null) + M.LAssailant = user + to_chat(user, "[trans] units injected. [R.total_volume] units remaining.") + return + +/obj/item/reagent_containers/borghypo/attack_self(mob/user) + playsound(loc, 'sound/effects/pop.ogg', 50, 0) //Change the mode + mode++ + if(mode > reagent_list.len) + mode = 1 + + charge_tick = 0 //Prevents wasted chems/cell charge if you're cycling through modes. + var/datum/reagent/R = GLOB.chemical_reagents_list[reagent_ids[mode]] + to_chat(user, "Synthesizer is now producing '[R.name]'.") + return + +/obj/item/reagent_containers/borghypo/examine(mob/user) + . = ..() + if(get_dist(user, src) <= 2) + var/empty = TRUE + + for(var/datum/reagents/RS in reagent_list) + var/datum/reagent/R = locate() in RS.reagent_list + if(R) + . += "It currently has [R.volume] units of [R.name] stored." + empty = FALSE + + if(empty) + . += "It is currently empty. Allow some time for the internal syntheszier to produce more." diff --git a/code/modules/reagents/reagent_containers/bottle.dm b/code/modules/reagents/reagent_containers/bottle.dm index b9080bf3d12..61552eae789 100644 --- a/code/modules/reagents/reagent_containers/bottle.dm +++ b/code/modules/reagents/reagent_containers/bottle.dm @@ -392,4 +392,4 @@ name = "BVAK bottle" desc = "A small bottle containing Bio Virus Antidote Kit." icon_state = "wide_bottle" - list_reagents = list("atropine" = 5, "epinephrine" = 5, "salbutamol" = 10, "spaceacillin" = 10) \ No newline at end of file + list_reagents = list("atropine" = 5, "epinephrine" = 5, "salbutamol" = 10, "spaceacillin" = 10) diff --git a/code/modules/reagents/reagent_containers/dropper.dm b/code/modules/reagents/reagent_containers/dropper.dm index 88c1e1609ce..ddd15413820 100644 --- a/code/modules/reagents/reagent_containers/dropper.dm +++ b/code/modules/reagents/reagent_containers/dropper.dm @@ -1,144 +1,144 @@ -//////////////////////////////////////////////////////////////////////////////// -/// Droppers. /// -//////////////////////////////////////////////////////////////////////////////// - -/obj/item/reagent_containers/dropper - name = "dropper" - desc = "A dropper. Transfers 5 units." - icon_state = "dropper" - item_state = "dropper" - amount_per_transfer_from_this = 5 - possible_transfer_amounts = list(1, 2, 3, 4, 5) - volume = 5 - -/obj/item/reagent_containers/dropper/on_reagent_change() - if(!reagents.total_volume) - icon_state = "[initial(icon_state)]" - else - icon_state = "[initial(icon_state)]1" - -/obj/item/reagent_containers/dropper/attack(mob/living/M, mob/living/user, def_zone) - return - -/obj/item/reagent_containers/dropper/afterattack(atom/target, mob/user, proximity) - if(!proximity) - return - var/to_transfer = 0 - if(iscarbon(target)) - var/mob/living/carbon/C = target - if(!reagents.total_volume) - return - if(user != C) - visible_message("[user] begins to drip something into [C]'s eyes!") - if(!do_mob(user, C, 30)) - return - if(ishuman(target)) - var/mob/living/carbon/human/H = target - var/obj/item/safe_thing = null - - if(H.glasses) - safe_thing = H.glasses - if(H.wear_mask ) - if(H.wear_mask.flags_cover & MASKCOVERSEYES) - safe_thing = H.wear_mask - if(H.head) - if(H.head.flags_cover & MASKCOVERSEYES) - safe_thing = H.head - - if(safe_thing) - visible_message("[user] tries to drip something into [H]'s eyes, but fails!") - - reagents.reaction(safe_thing, TOUCH) - to_transfer = reagents.remove_any(amount_per_transfer_from_this) - - to_chat(user, "You transfer [to_transfer] units of the solution.") - return - - visible_message("[user] drips something into [C]'s eyes!") - reagents.reaction(C, TOUCH) - - var/list/injected = list() - for(var/datum/reagent/R in reagents.reagent_list) - injected += R.name - var/contained = english_list(injected) - add_attack_logs(user, C, "Dripped with [src] containing ([contained]), transfering [to_transfer]") - - to_transfer = reagents.trans_to(C, amount_per_transfer_from_this) - to_chat(user, "You transfer [to_transfer] units of the solution.") - - if(isobj(target)) - if(!target.reagents) - return - - if(reagents.total_volume) - if(!target.is_open_container() && !(istype(target, /obj/item/reagent_containers/food) && !istype(target, /obj/item/reagent_containers/food/pill)) && !istype(target, /obj/item/clothing/mask/cigarette)) - to_chat(user, "You cannot directly fill this object.") - return - - if(target.reagents.total_volume >= target.reagents.maximum_volume) - to_chat(user, "[target] is full.") - return - - to_transfer = reagents.trans_to(target, amount_per_transfer_from_this) - to_chat(user, "You transfer [to_transfer] units of the solution.") - - else - if(!target.is_open_container() && !istype(target, /obj/structure/reagent_dispensers)) - to_chat(user, "You cannot directly remove reagents from [target].") - return - - if(!target.reagents.total_volume) - to_chat(user, "[target] is empty.") - return - - to_transfer = target.reagents.trans_to(src, amount_per_transfer_from_this) - - to_chat(user, "You fill [src] with [to_transfer] units of the solution.") - -/obj/item/reagent_containers/dropper/cyborg - name = "Industrial Dropper" - desc = "A larger dropper. Transfers 10 units." - amount_per_transfer_from_this = 10 - possible_transfer_amounts = list(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) - volume = 10 - -/obj/item/reagent_containers/dropper/precision - name = "pipette" - desc = "A high precision pippette. Holds 1 unit." - icon_state = "pipette" - amount_per_transfer_from_this = 1 - possible_transfer_amounts = list(0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1) - volume = 1 - -//Syndicate item. Virus transmitting mini hypospray -/obj/item/reagent_containers/dropper/precision/viral_injector - -/obj/item/reagent_containers/dropper/precision/viral_injector/attack(mob/living/M, mob/living/user, def_zone) - if(M.can_inject(user, TRUE)) - to_chat(user, "You stab [M] with the [src].") - if(reagents.total_volume && M.reagents) - var/list/injected = list() - for(var/datum/reagent/R in reagents.reagent_list) - injected += R.name - var/datum/reagent/blood/B = R - - if(istype(B) && B.data["viruses"]) - var/virList = list() - for(var/dis in B.data["viruses"]) - var/datum/disease/D = dis - var/virusData = D.name - var/english_symptoms = list() - var/datum/disease/advance/A = D - if(A) - for(var/datum/symptom/S in A.symptoms) - english_symptoms += S.name - virusData += " ([english_list(english_symptoms)])" - virList += virusData - var/str = english_list(virList) - add_attack_logs(user, M, "Infected with [str].") - - reagents.reaction(M, INGEST, reagents.total_volume) - reagents.trans_to(M, 1) - - var/contained = english_list(injected) - add_attack_logs(user, M, "Injected with [src] containing ([contained])") +//////////////////////////////////////////////////////////////////////////////// +/// Droppers. /// +//////////////////////////////////////////////////////////////////////////////// + +/obj/item/reagent_containers/dropper + name = "dropper" + desc = "A dropper. Transfers 5 units." + icon_state = "dropper" + item_state = "dropper" + amount_per_transfer_from_this = 5 + possible_transfer_amounts = list(1, 2, 3, 4, 5) + volume = 5 + +/obj/item/reagent_containers/dropper/on_reagent_change() + if(!reagents.total_volume) + icon_state = "[initial(icon_state)]" + else + icon_state = "[initial(icon_state)]1" + +/obj/item/reagent_containers/dropper/attack(mob/living/M, mob/living/user, def_zone) + return + +/obj/item/reagent_containers/dropper/afterattack(atom/target, mob/user, proximity) + if(!proximity) + return + var/to_transfer = 0 + if(iscarbon(target)) + var/mob/living/carbon/C = target + if(!reagents.total_volume) + return + if(user != C) + visible_message("[user] begins to drip something into [C]'s eyes!") + if(!do_mob(user, C, 30)) + return + if(ishuman(target)) + var/mob/living/carbon/human/H = target + var/obj/item/safe_thing = null + + if(H.glasses) + safe_thing = H.glasses + if(H.wear_mask ) + if(H.wear_mask.flags_cover & MASKCOVERSEYES) + safe_thing = H.wear_mask + if(H.head) + if(H.head.flags_cover & MASKCOVERSEYES) + safe_thing = H.head + + if(safe_thing) + visible_message("[user] tries to drip something into [H]'s eyes, but fails!") + + reagents.reaction(safe_thing, REAGENT_TOUCH) + to_transfer = reagents.remove_any(amount_per_transfer_from_this) + + to_chat(user, "You transfer [to_transfer] units of the solution.") + return + + visible_message("[user] drips something into [C]'s eyes!") + reagents.reaction(C, REAGENT_TOUCH) + + var/list/injected = list() + for(var/datum/reagent/R in reagents.reagent_list) + injected += R.name + var/contained = english_list(injected) + add_attack_logs(user, C, "Dripped with [src] containing ([contained]), transfering [to_transfer]") + + to_transfer = reagents.trans_to(C, amount_per_transfer_from_this) + to_chat(user, "You transfer [to_transfer] units of the solution.") + + if(isobj(target)) + if(!target.reagents) + return + + if(reagents.total_volume) + if(!target.is_open_container() && !(istype(target, /obj/item/reagent_containers/food) && !istype(target, /obj/item/reagent_containers/food/pill)) && !istype(target, /obj/item/clothing/mask/cigarette)) + to_chat(user, "You cannot directly fill this object.") + return + + if(target.reagents.total_volume >= target.reagents.maximum_volume) + to_chat(user, "[target] is full.") + return + + to_transfer = reagents.trans_to(target, amount_per_transfer_from_this) + to_chat(user, "You transfer [to_transfer] units of the solution.") + + else + if(!target.is_open_container() && !istype(target, /obj/structure/reagent_dispensers)) + to_chat(user, "You cannot directly remove reagents from [target].") + return + + if(!target.reagents.total_volume) + to_chat(user, "[target] is empty.") + return + + to_transfer = target.reagents.trans_to(src, amount_per_transfer_from_this) + + to_chat(user, "You fill [src] with [to_transfer] units of the solution.") + +/obj/item/reagent_containers/dropper/cyborg + name = "Industrial Dropper" + desc = "A larger dropper. Transfers 10 units." + amount_per_transfer_from_this = 10 + possible_transfer_amounts = list(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + volume = 10 + +/obj/item/reagent_containers/dropper/precision + name = "pipette" + desc = "A high precision pippette. Holds 1 unit." + icon_state = "pipette" + amount_per_transfer_from_this = 1 + possible_transfer_amounts = list(0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1) + volume = 1 + +//Syndicate item. Virus transmitting mini hypospray +/obj/item/reagent_containers/dropper/precision/viral_injector + +/obj/item/reagent_containers/dropper/precision/viral_injector/attack(mob/living/M, mob/living/user, def_zone) + if(M.can_inject(user, TRUE)) + to_chat(user, "You stab [M] with the [src].") + if(reagents.total_volume && M.reagents) + var/list/injected = list() + for(var/datum/reagent/R in reagents.reagent_list) + injected += R.name + var/datum/reagent/blood/B = R + + if(istype(B) && B.data["viruses"]) + var/virList = list() + for(var/dis in B.data["viruses"]) + var/datum/disease/D = dis + var/virusData = D.name + var/english_symptoms = list() + var/datum/disease/advance/A = D + if(A) + for(var/datum/symptom/S in A.symptoms) + english_symptoms += S.name + virusData += " ([english_list(english_symptoms)])" + virList += virusData + var/str = english_list(virList) + add_attack_logs(user, M, "Infected with [str].") + + reagents.reaction(M, REAGENT_INGEST, reagents.total_volume) + reagents.trans_to(M, 1) + + var/contained = english_list(injected) + add_attack_logs(user, M, "Injected with [src] containing ([contained])") diff --git a/code/modules/reagents/reagent_containers/glass_containers.dm b/code/modules/reagents/reagent_containers/glass_containers.dm index 0114a7bdc76..3e9480f166d 100644 --- a/code/modules/reagents/reagent_containers/glass_containers.dm +++ b/code/modules/reagents/reagent_containers/glass_containers.dm @@ -79,7 +79,7 @@ else M.LAssailant = user - reagents.reaction(M, TOUCH) + reagents.reaction(M, REAGENT_TOUCH) reagents.clear_reagents() else if(M != user) @@ -95,7 +95,7 @@ to_chat(user, "You swallow a gulp of [src].") var/fraction = min(5 / reagents.total_volume, 1) - reagents.reaction(M, INGEST, fraction) + reagents.reaction(M, REAGENT_INGEST, fraction) addtimer(CALLBACK(reagents, /datum/reagents.proc/trans_to, M, 5), 5) playsound(M.loc,'sound/items/drink.ogg', rand(10,50), 1) else @@ -146,7 +146,7 @@ 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.reaction(target, REAGENT_TOUCH) reagents.clear_reagents() /obj/item/reagent_containers/glass/attackby(obj/item/I, mob/user, params) @@ -361,7 +361,7 @@ ..() if(slot == slot_head && reagents.total_volume) to_chat(user, "[src]'s contents spill all over you!") - reagents.reaction(user, TOUCH) + reagents.reaction(user, REAGENT_TOUCH) reagents.clear_reagents() /obj/item/reagent_containers/glass/bucket/attackby(obj/D, mob/user, params) @@ -397,4 +397,4 @@ amount_per_transfer_from_this = 20 /obj/item/reagent_containers/glass/beaker/waterbottle/large/empty - list_reagents = list() \ No newline at end of file + list_reagents = list() diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm index 3b71a5bd741..490a5d808fa 100644 --- a/code/modules/reagents/reagent_containers/hypospray.dm +++ b/code/modules/reagents/reagent_containers/hypospray.dm @@ -1,175 +1,175 @@ -//////////////////////////////////////////////////////////////////////////////// -/// HYPOSPRAY -//////////////////////////////////////////////////////////////////////////////// - -/obj/item/reagent_containers/hypospray - name = "hypospray" - desc = "The DeForest Medical Corporation hypospray is a sterile, air-needle autoinjector for rapid administration of drugs to patients." - icon = 'icons/obj/hypo.dmi' - item_state = "hypo" - icon_state = "hypo" - amount_per_transfer_from_this = 5 - volume = 30 - possible_transfer_amounts = list(1,2,3,4,5,10,15,20,25,30) - resistance_flags = ACID_PROOF - container_type = OPENCONTAINER - slot_flags = SLOT_BELT - var/ignore_flags = FALSE - var/emagged = FALSE - var/safety_hypo = FALSE - -/obj/item/reagent_containers/hypospray/attack(mob/living/M, mob/user) - if(!reagents.total_volume) - to_chat(user, "[src] is empty!") - return - if(!iscarbon(M)) - return - - if(reagents.total_volume && (ignore_flags || M.can_inject(user, TRUE))) // Ignore flag should be checked first or there will be an error message. - to_chat(M, "You feel a tiny prick!") - to_chat(user, "You inject [M] with [src].") - - if(M.reagents) - var/list/injected = list() - for(var/datum/reagent/R in reagents.reagent_list) - injected += R.name - - var/primary_reagent_name = reagents.get_master_reagent_name() - var/trans = reagents.trans_to(M, amount_per_transfer_from_this) - - if(safety_hypo) - visible_message("[user] injects [M] with [trans] units of [primary_reagent_name].") - playsound(loc, 'sound/goonstation/items/hypo.ogg', 80, 0) - - to_chat(user, "[trans] unit\s injected. [reagents.total_volume] unit\s remaining in [src].") - - var/contained = english_list(injected) - - add_attack_logs(user, M, "Injected with [src] containing ([contained])", reagents.harmless_helper() ? ATKLOG_ALMOSTALL : null) - - return TRUE - -/obj/item/reagent_containers/hypospray/on_reagent_change() - if(safety_hypo && !emagged) - var/found_forbidden_reagent = FALSE - for(var/datum/reagent/R in reagents.reagent_list) - if(!GLOB.safe_chem_list.Find(R.id)) - reagents.del_reagent(R.id) - found_forbidden_reagent = TRUE - if(found_forbidden_reagent) - if(ismob(loc)) - to_chat(loc, "[src] identifies and removes a harmful substance.") - else - visible_message("[src] identifies and removes a harmful substance.") - -/obj/item/reagent_containers/hypospray/emag_act(mob/user) - if(safety_hypo && !emagged) - emagged = TRUE - ignore_flags = TRUE - to_chat(user, "You short out the safeties on [src].") - -/obj/item/reagent_containers/hypospray/safety - name = "medical hypospray" - desc = "A general use medical hypospray for quick injection of chemicals. There is a safety button by the trigger." - icon_state = "medivend_hypo" - safety_hypo = TRUE - -/obj/item/reagent_containers/hypospray/safety/ert - list_reagents = list("omnizine" = 30) - -/obj/item/reagent_containers/hypospray/CMO - list_reagents = list("omnizine" = 30) - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF - -/obj/item/reagent_containers/hypospray/combat - name = "combat stimulant injector" - desc = "A modified air-needle autoinjector, used by support operatives to quickly heal injuries in combat." - amount_per_transfer_from_this = 15 - possible_transfer_amounts = list(15) - icon_state = "combat_hypo" - volume = 90 - ignore_flags = 1 // So they can heal their comrades. - list_reagents = list("epinephrine" = 30, "weak_omnizine" = 30, "salglu_solution" = 30) - -/obj/item/reagent_containers/hypospray/combat/nanites - desc = "A modified air-needle autoinjector for use in combat situations. Prefilled with expensive medical nanites for rapid healing." - volume = 100 - list_reagents = list("nanites" = 100) - -/obj/item/reagent_containers/hypospray/autoinjector - name = "emergency autoinjector" - desc = "A rapid and safe way to stabilize patients in critical condition for personnel without advanced medical knowledge." - icon_state = "autoinjector" - item_state = "autoinjector" - amount_per_transfer_from_this = 10 - possible_transfer_amounts = list(10) - volume = 10 - ignore_flags = TRUE //so you can medipen through hardsuits - container_type = DRAWABLE - flags = null - list_reagents = list("epinephrine" = 10) - -/obj/item/reagent_containers/hypospray/autoinjector/attack(mob/M, mob/user) - if(!reagents.total_volume) - to_chat(user, "[src] is empty!") - return - ..() - update_icon() - return TRUE - -/obj/item/reagent_containers/hypospray/autoinjector/update_icon() - if(reagents.total_volume > 0) - icon_state = initial(icon_state) - else - icon_state = "[initial(icon_state)]0" - -/obj/item/reagent_containers/hypospray/autoinjector/examine() - . = ..() - if(reagents && reagents.reagent_list.len) - . += "It is currently loaded." - else - . += "It is spent." - -/obj/item/reagent_containers/hypospray/autoinjector/teporone //basilisks - name = "teporone autoinjector" - desc = "A rapid way to regulate your body's temperature in the event of a hardsuit malfunction." - icon_state = "lepopen" - list_reagents = list("teporone" = 10) - -/obj/item/reagent_containers/hypospray/autoinjector/stimpack //goliath kiting - name = "stimpack autoinjector" - desc = "A rapid way to stimulate your body's adrenaline, allowing for freer movement in restrictive armor." - icon_state = "stimpen" - volume = 20 - amount_per_transfer_from_this = 20 - list_reagents = list("methamphetamine" = 10, "coffee" = 10) - -/obj/item/reagent_containers/hypospray/autoinjector/stimulants - name = "Stimulants autoinjector" - desc = "Rapidly stimulates and regenerates the body's organ system." - icon_state = "stimpen" - amount_per_transfer_from_this = 50 - possible_transfer_amounts = list(50) - volume = 50 - list_reagents = list("stimulants" = 50) - -/obj/item/reagent_containers/hypospray/autoinjector/survival - name = "survival medipen" - desc = "A medipen for surviving in the harshest of environments, heals and protects from environmental hazards.
        WARNING: Do not inject more than one pen in quick succession." - icon_state = "stimpen" - volume = 42 - amount_per_transfer_from_this = 42 - list_reagents = list("salbutamol" = 10, "teporone" = 15, "epinephrine" = 10, "lavaland_extract" = 2, "weak_omnizine" = 5) //Short burst of healing, followed by minor healing from the saline - -/obj/item/reagent_containers/hypospray/autoinjector/nanocalcium - name = "nanocalcium autoinjector" - desc = "After a short period of time the nanites will slow the body's systems and assist with bone repair. Nanomachines son." - icon_state = "bonepen" - amount_per_transfer_from_this = 30 - possible_transfer_amounts = list(30) - volume = 30 - list_reagents = list("nanocalcium" = 30) - -/obj/item/reagent_containers/hypospray/autoinjector/nanocalcium/attack(mob/living/M, mob/user) - if(..()) - playsound(loc, 'sound/weapons/smg_empty_alarm.ogg', 20, 1) +//////////////////////////////////////////////////////////////////////////////// +/// HYPOSPRAY +//////////////////////////////////////////////////////////////////////////////// + +/obj/item/reagent_containers/hypospray + name = "hypospray" + desc = "The DeForest Medical Corporation hypospray is a sterile, air-needle autoinjector for rapid administration of drugs to patients." + icon = 'icons/obj/hypo.dmi' + item_state = "hypo" + icon_state = "hypo" + amount_per_transfer_from_this = 5 + volume = 30 + possible_transfer_amounts = list(1,2,3,4,5,10,15,20,25,30) + resistance_flags = ACID_PROOF + container_type = OPENCONTAINER + slot_flags = SLOT_BELT + var/ignore_flags = FALSE + var/emagged = FALSE + var/safety_hypo = FALSE + +/obj/item/reagent_containers/hypospray/attack(mob/living/M, mob/user) + if(!reagents.total_volume) + to_chat(user, "[src] is empty!") + return + if(!iscarbon(M)) + return + + if(reagents.total_volume && (ignore_flags || M.can_inject(user, TRUE))) // Ignore flag should be checked first or there will be an error message. + to_chat(M, "You feel a tiny prick!") + to_chat(user, "You inject [M] with [src].") + + if(M.reagents) + var/list/injected = list() + for(var/datum/reagent/R in reagents.reagent_list) + injected += R.name + + var/primary_reagent_name = reagents.get_master_reagent_name() + var/trans = reagents.trans_to(M, amount_per_transfer_from_this) + + if(safety_hypo) + visible_message("[user] injects [M] with [trans] units of [primary_reagent_name].") + playsound(loc, 'sound/goonstation/items/hypo.ogg', 80, 0) + + to_chat(user, "[trans] unit\s injected. [reagents.total_volume] unit\s remaining in [src].") + + var/contained = english_list(injected) + + add_attack_logs(user, M, "Injected with [src] containing ([contained])", reagents.harmless_helper() ? ATKLOG_ALMOSTALL : null) + + return TRUE + +/obj/item/reagent_containers/hypospray/on_reagent_change() + if(safety_hypo && !emagged) + var/found_forbidden_reagent = FALSE + for(var/datum/reagent/R in reagents.reagent_list) + if(!GLOB.safe_chem_list.Find(R.id)) + reagents.del_reagent(R.id) + found_forbidden_reagent = TRUE + if(found_forbidden_reagent) + if(ismob(loc)) + to_chat(loc, "[src] identifies and removes a harmful substance.") + else + visible_message("[src] identifies and removes a harmful substance.") + +/obj/item/reagent_containers/hypospray/emag_act(mob/user) + if(safety_hypo && !emagged) + emagged = TRUE + ignore_flags = TRUE + to_chat(user, "You short out the safeties on [src].") + +/obj/item/reagent_containers/hypospray/safety + name = "medical hypospray" + desc = "A general use medical hypospray for quick injection of chemicals. There is a safety button by the trigger." + icon_state = "medivend_hypo" + safety_hypo = TRUE + +/obj/item/reagent_containers/hypospray/safety/ert + list_reagents = list("omnizine" = 30) + +/obj/item/reagent_containers/hypospray/CMO + list_reagents = list("omnizine" = 30) + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + +/obj/item/reagent_containers/hypospray/combat + name = "combat stimulant injector" + desc = "A modified air-needle autoinjector, used by support operatives to quickly heal injuries in combat." + amount_per_transfer_from_this = 15 + possible_transfer_amounts = list(15) + icon_state = "combat_hypo" + volume = 90 + ignore_flags = 1 // So they can heal their comrades. + list_reagents = list("epinephrine" = 30, "weak_omnizine" = 30, "salglu_solution" = 30) + +/obj/item/reagent_containers/hypospray/combat/nanites + desc = "A modified air-needle autoinjector for use in combat situations. Prefilled with expensive medical nanites for rapid healing." + volume = 100 + list_reagents = list("nanites" = 100) + +/obj/item/reagent_containers/hypospray/autoinjector + name = "emergency autoinjector" + desc = "A rapid and safe way to stabilize patients in critical condition for personnel without advanced medical knowledge." + icon_state = "autoinjector" + item_state = "autoinjector" + amount_per_transfer_from_this = 10 + possible_transfer_amounts = list(10) + volume = 10 + ignore_flags = TRUE //so you can medipen through hardsuits + container_type = DRAWABLE + flags = null + list_reagents = list("epinephrine" = 10) + +/obj/item/reagent_containers/hypospray/autoinjector/attack(mob/M, mob/user) + if(!reagents.total_volume) + to_chat(user, "[src] is empty!") + return + ..() + update_icon() + return TRUE + +/obj/item/reagent_containers/hypospray/autoinjector/update_icon() + if(reagents.total_volume > 0) + icon_state = initial(icon_state) + else + icon_state = "[initial(icon_state)]0" + +/obj/item/reagent_containers/hypospray/autoinjector/examine() + . = ..() + if(reagents && reagents.reagent_list.len) + . += "It is currently loaded." + else + . += "It is spent." + +/obj/item/reagent_containers/hypospray/autoinjector/teporone //basilisks + name = "teporone autoinjector" + desc = "A rapid way to regulate your body's temperature in the event of a hardsuit malfunction." + icon_state = "lepopen" + list_reagents = list("teporone" = 10) + +/obj/item/reagent_containers/hypospray/autoinjector/stimpack //goliath kiting + name = "stimpack autoinjector" + desc = "A rapid way to stimulate your body's adrenaline, allowing for freer movement in restrictive armor." + icon_state = "stimpen" + volume = 20 + amount_per_transfer_from_this = 20 + list_reagents = list("methamphetamine" = 10, "coffee" = 10) + +/obj/item/reagent_containers/hypospray/autoinjector/stimulants + name = "Stimulants autoinjector" + desc = "Rapidly stimulates and regenerates the body's organ system." + icon_state = "stimpen" + amount_per_transfer_from_this = 50 + possible_transfer_amounts = list(50) + volume = 50 + list_reagents = list("stimulants" = 50) + +/obj/item/reagent_containers/hypospray/autoinjector/survival + name = "survival medipen" + desc = "A medipen for surviving in the harshest of environments, heals and protects from environmental hazards.
        WARNING: Do not inject more than one pen in quick succession." + icon_state = "stimpen" + volume = 42 + amount_per_transfer_from_this = 42 + list_reagents = list("salbutamol" = 10, "teporone" = 15, "epinephrine" = 10, "lavaland_extract" = 2, "weak_omnizine" = 5) //Short burst of healing, followed by minor healing from the saline + +/obj/item/reagent_containers/hypospray/autoinjector/nanocalcium + name = "nanocalcium autoinjector" + desc = "After a short period of time the nanites will slow the body's systems and assist with bone repair. Nanomachines son." + icon_state = "bonepen" + amount_per_transfer_from_this = 30 + possible_transfer_amounts = list(30) + volume = 30 + list_reagents = list("nanocalcium" = 30) + +/obj/item/reagent_containers/hypospray/autoinjector/nanocalcium/attack(mob/living/M, mob/user) + if(..()) + playsound(loc, 'sound/weapons/smg_empty_alarm.ogg', 20, 1) diff --git a/code/modules/reagents/reagent_containers/iv_bag.dm b/code/modules/reagents/reagent_containers/iv_bag.dm index 44fa7a722d1..0f0168a8db0 100644 --- a/code/modules/reagents/reagent_containers/iv_bag.dm +++ b/code/modules/reagents/reagent_containers/iv_bag.dm @@ -68,7 +68,7 @@ if(mode) // Injecting if(reagents.total_volume) var/fraction = min(amount_per_transfer_from_this/reagents.total_volume, 1) //The amount of reagents we'll transfer to the person - reagents.reaction(injection_target, INGEST, fraction) //React the amount we're transfering. + reagents.reaction(injection_target, REAGENT_INGEST, fraction) //React the amount we're transfering. reagents.trans_to(injection_target, amount_per_transfer_from_this) update_icon() else // Drawing diff --git a/code/modules/reagents/reagent_containers/patch.dm b/code/modules/reagents/reagent_containers/patch.dm index 1784cf4741e..b020015e929 100644 --- a/code/modules/reagents/reagent_containers/patch.dm +++ b/code/modules/reagents/reagent_containers/patch.dm @@ -6,7 +6,7 @@ item_state = "bandaid" possible_transfer_amounts = null volume = 40 - apply_type = TOUCH + apply_type = REAGENT_TOUCH apply_method = "apply" transfer_efficiency = 0.5 //patches aren't as effective at getting chemicals into the bloodstream. diff --git a/code/modules/reagents/reagent_containers/pill.dm b/code/modules/reagents/reagent_containers/pill.dm index e22e9c86793..f6980c03620 100644 --- a/code/modules/reagents/reagent_containers/pill.dm +++ b/code/modules/reagents/reagent_containers/pill.dm @@ -1,155 +1,155 @@ -//////////////////////////////////////////////////////////////////////////////// -/// Pills. -//////////////////////////////////////////////////////////////////////////////// -/obj/item/reagent_containers/food/pill - name = "pill" - desc = "a pill." - icon = 'icons/obj/chemical.dmi' - icon_state = null - item_state = "pill" - possible_transfer_amounts = null - volume = 100 - consume_sound = null - can_taste = FALSE - antable = FALSE - -/obj/item/reagent_containers/food/pill/New() - ..() - if(!icon_state) - icon_state = "pill[rand(1,20)]" - -/obj/item/reagent_containers/food/pill/attack_self(mob/user) - return - -/obj/item/reagent_containers/food/pill/attack(mob/living/carbon/M, mob/user, def_zone) - if(!istype(M)) - return 0 - bitesize = reagents.total_volume - if(M.eat(src, user)) - spawn(0) - qdel(src) - return 1 - return 0 - -/obj/item/reagent_containers/food/pill/afterattack(obj/target, mob/user, proximity) - if(!proximity) - return - - if(target.is_open_container() != 0 && target.reagents) - if(!target.reagents.total_volume) - to_chat(user, "[target] is empty. Cant dissolve [src].") - return - - to_chat(user, "You dissolve [src] in [target].") - reagents.trans_to(target, reagents.total_volume) - for(var/mob/O in viewers(2, user)) - O.show_message("[user] puts something in [target].", 1) - spawn(5) - qdel(src) - -//////////////////////////////////////////////////////////////////////////////// -/// Pills. END -//////////////////////////////////////////////////////////////////////////////// - -//Pills -/obj/item/reagent_containers/food/pill/tox - name = "Toxins pill" - desc = "Highly toxic." - icon_state = "pill21" - list_reagents = list("toxin" = 50) - -/obj/item/reagent_containers/food/pill/initropidril - name = "initropidril pill" - desc = "Don't swallow this." - icon_state = "pill21" - list_reagents = list("initropidril" = 50) - -/obj/item/reagent_containers/food/pill/fakedeath - name = "fake death pill" - desc = "Swallow then rest to appear dead, stand up to wake up. Also mutes the user's voice." - icon_state = "pill4" - list_reagents = list("capulettium_plus" = 50) - -/obj/item/reagent_containers/food/pill/adminordrazine - name = "Adminordrazine pill" - desc = "It's magic. We don't have to explain it." - icon_state = "pill16" - list_reagents = list("adminordrazine" = 50) - -/obj/item/reagent_containers/food/pill/morphine - name = "Morphine pill" - desc = "Commonly used to treat insomnia." - icon_state = "pill8" - list_reagents = list("morphine" = 30) - -/obj/item/reagent_containers/food/pill/methamphetamine - name = "Methamphetamine pill" - desc = "Helps improve the ability to concentrate." - icon_state = "pill8" - list_reagents = list("methamphetamine" = 5) - -/obj/item/reagent_containers/food/pill/haloperidol - name = "Haloperidol pill" - desc = "Haloperidol is an anti-psychotic use to treat psychiatric problems." - icon_state = "pill8" - list_reagents = list("haloperidol" = 15) - -/obj/item/reagent_containers/food/pill/happy - name = "Happy pill" - desc = "Happy happy joy joy!" - icon_state = "pill18" - list_reagents = list("space_drugs" = 15, "sugar" = 15) - -/obj/item/reagent_containers/food/pill/zoom - name = "Zoom pill" - desc = "Zoooom!" - icon_state = "pill18" - list_reagents = list("synaptizine" = 5, "methamphetamine" = 5) - -/obj/item/reagent_containers/food/pill/charcoal - name = "Charcoal pill" - desc = "Neutralizes many common toxins." - icon_state = "pill17" - list_reagents = list("charcoal" = 50) - -/obj/item/reagent_containers/food/pill/epinephrine - name = "Epinephrine pill" - desc = "Used to provide shots of adrenaline." - icon_state = "pill6" - list_reagents = list("epinephrine" = 50) - -/obj/item/reagent_containers/food/pill/salicylic - name = "Salicylic Acid pill" - desc = "Commonly used to treat moderate pain and fevers." - icon_state = "pill4" - list_reagents = list("sal_acid" = 20) - -/obj/item/reagent_containers/food/pill/salbutamol - name = "Salbutamol pill" - desc = "Used to treat respiratory distress." - icon_state = "pill8" - list_reagents = list("salbutamol" = 20) - -/obj/item/reagent_containers/food/pill/hydrocodone - name = "Hydrocodone pill" - desc = "Used to treat extreme pain." - icon_state = "pill6" - list_reagents = list("hydrocodone" = 15) - -/obj/item/reagent_containers/food/pill/calomel - name = "calomel pill" - desc = "Can be used to purge impurities, but is highly toxic itself." - icon_state = "pill3" - list_reagents = list("calomel" = 15) - -/obj/item/reagent_containers/food/pill/mutadone - name = "mutadone pill" - desc = "Used to cure genetic abnormalities." - icon_state = "pill18" - list_reagents = list("mutadone" = 20) - -/obj/item/reagent_containers/food/pill/mannitol - name = "mannitol pill" - desc = "Used to treat cranial swelling." - icon_state = "pill19" - list_reagents = list("mannitol" = 20) \ No newline at end of file +//////////////////////////////////////////////////////////////////////////////// +/// Pills. +//////////////////////////////////////////////////////////////////////////////// +/obj/item/reagent_containers/food/pill + name = "pill" + desc = "a pill." + icon = 'icons/obj/chemical.dmi' + icon_state = null + item_state = "pill" + possible_transfer_amounts = null + volume = 100 + consume_sound = null + can_taste = FALSE + antable = FALSE + +/obj/item/reagent_containers/food/pill/New() + ..() + if(!icon_state) + icon_state = "pill[rand(1,20)]" + +/obj/item/reagent_containers/food/pill/attack_self(mob/user) + return + +/obj/item/reagent_containers/food/pill/attack(mob/living/carbon/M, mob/user, def_zone) + if(!istype(M)) + return 0 + bitesize = reagents.total_volume + if(M.eat(src, user)) + spawn(0) + qdel(src) + return 1 + return 0 + +/obj/item/reagent_containers/food/pill/afterattack(obj/target, mob/user, proximity) + if(!proximity) + return + + if(target.is_open_container() != 0 && target.reagents) + if(!target.reagents.total_volume) + to_chat(user, "[target] is empty. Cant dissolve [src].") + return + + to_chat(user, "You dissolve [src] in [target].") + reagents.trans_to(target, reagents.total_volume) + for(var/mob/O in viewers(2, user)) + O.show_message("[user] puts something in [target].", 1) + spawn(5) + qdel(src) + +//////////////////////////////////////////////////////////////////////////////// +/// Pills. END +//////////////////////////////////////////////////////////////////////////////// + +//Pills +/obj/item/reagent_containers/food/pill/tox + name = "Toxins pill" + desc = "Highly toxic." + icon_state = "pill21" + list_reagents = list("toxin" = 50) + +/obj/item/reagent_containers/food/pill/initropidril + name = "initropidril pill" + desc = "Don't swallow this." + icon_state = "pill21" + list_reagents = list("initropidril" = 50) + +/obj/item/reagent_containers/food/pill/fakedeath + name = "fake death pill" + desc = "Swallow then rest to appear dead, stand up to wake up. Also mutes the user's voice." + icon_state = "pill4" + list_reagents = list("capulettium_plus" = 50) + +/obj/item/reagent_containers/food/pill/adminordrazine + name = "Adminordrazine pill" + desc = "It's magic. We don't have to explain it." + icon_state = "pill16" + list_reagents = list("adminordrazine" = 50) + +/obj/item/reagent_containers/food/pill/morphine + name = "Morphine pill" + desc = "Commonly used to treat insomnia." + icon_state = "pill8" + list_reagents = list("morphine" = 30) + +/obj/item/reagent_containers/food/pill/methamphetamine + name = "Methamphetamine pill" + desc = "Helps improve the ability to concentrate." + icon_state = "pill8" + list_reagents = list("methamphetamine" = 5) + +/obj/item/reagent_containers/food/pill/haloperidol + name = "Haloperidol pill" + desc = "Haloperidol is an anti-psychotic use to treat psychiatric problems." + icon_state = "pill8" + list_reagents = list("haloperidol" = 15) + +/obj/item/reagent_containers/food/pill/happy + name = "Happy pill" + desc = "Happy happy joy joy!" + icon_state = "pill18" + list_reagents = list("space_drugs" = 15, "sugar" = 15) + +/obj/item/reagent_containers/food/pill/zoom + name = "Zoom pill" + desc = "Zoooom!" + icon_state = "pill18" + list_reagents = list("synaptizine" = 5, "methamphetamine" = 5) + +/obj/item/reagent_containers/food/pill/charcoal + name = "Charcoal pill" + desc = "Neutralizes many common toxins." + icon_state = "pill17" + list_reagents = list("charcoal" = 50) + +/obj/item/reagent_containers/food/pill/epinephrine + name = "Epinephrine pill" + desc = "Used to provide shots of adrenaline." + icon_state = "pill6" + list_reagents = list("epinephrine" = 50) + +/obj/item/reagent_containers/food/pill/salicylic + name = "Salicylic Acid pill" + desc = "Commonly used to treat moderate pain and fevers." + icon_state = "pill4" + list_reagents = list("sal_acid" = 20) + +/obj/item/reagent_containers/food/pill/salbutamol + name = "Salbutamol pill" + desc = "Used to treat respiratory distress." + icon_state = "pill8" + list_reagents = list("salbutamol" = 20) + +/obj/item/reagent_containers/food/pill/hydrocodone + name = "Hydrocodone pill" + desc = "Used to treat extreme pain." + icon_state = "pill6" + list_reagents = list("hydrocodone" = 15) + +/obj/item/reagent_containers/food/pill/calomel + name = "calomel pill" + desc = "Can be used to purge impurities, but is highly toxic itself." + icon_state = "pill3" + list_reagents = list("calomel" = 15) + +/obj/item/reagent_containers/food/pill/mutadone + name = "mutadone pill" + desc = "Used to cure genetic abnormalities." + icon_state = "pill18" + list_reagents = list("mutadone" = 20) + +/obj/item/reagent_containers/food/pill/mannitol + name = "mannitol pill" + desc = "Used to treat cranial swelling." + icon_state = "pill19" + list_reagents = list("mannitol" = 20) diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm index a4320639f39..8826efa5eb8 100644 --- a/code/modules/reagents/reagent_containers/spray.dm +++ b/code/modules/reagents/reagent_containers/spray.dm @@ -1,217 +1,217 @@ -/obj/item/reagent_containers/spray - name = "spray bottle" - desc = "A spray bottle, with an unscrewable top." - icon = 'icons/obj/janitor.dmi' - icon_state = "cleaner" - item_state = "cleaner" - flags = NOBLUDGEON - container_type = OPENCONTAINER - slot_flags = SLOT_BELT - throwforce = 0 - w_class = WEIGHT_CLASS_SMALL - throw_speed = 3 - throw_range = 7 - var/spray_maxrange = 3 //what the sprayer will set spray_currentrange to in the attack_self. - var/spray_currentrange = 3 //the range of tiles the sprayer will reach when in fixed mode. - amount_per_transfer_from_this = 5 - volume = 250 - possible_transfer_amounts = null - - -/obj/item/reagent_containers/spray/afterattack(atom/A, mob/user) - if(istype(A, /obj/item/storage) || istype(A, /obj/structure/table) || istype(A, /obj/structure/rack) || istype(A, /obj/structure/closet) \ - || istype(A, /obj/item/reagent_containers) || istype(A, /obj/structure/sink) || istype(A, /obj/structure/janitorialcart) || istype(A, /obj/machinery/hydroponics)) - return - - if(istype(A, /obj/effect/proc_holder/spell)) - return - - if(istype(A, /obj/structure/reagent_dispensers) && get_dist(src,A) <= 1) //this block copypasted from reagent_containers/glass, for lack of a better solution - if(!A.reagents.total_volume && A.reagents) - to_chat(user, "[A] is empty.") - return - - if(reagents.total_volume >= reagents.maximum_volume) - to_chat(user, "[src] is full.") - return - - var/trans = A.reagents.trans_to(src, 50) //This is a static amount, otherwise, it'll take forever to fill. - to_chat(user, "You fill [src] with [trans] units of the contents of [A].") - return - - if(reagents.total_volume < amount_per_transfer_from_this) - to_chat(user, "[src] is empty!") - return - - var/contents_log = reagents.reagent_list.Join(", ") - spray(A) - - playsound(loc, 'sound/effects/spray2.ogg', 50, 1, -6) - user.changeNext_move(CLICK_CD_RANGE*2) - user.newtonian_move(get_dir(A, user)) - - if(reagents.reagent_list.len == 1 && reagents.has_reagent("cleaner")) // Only show space cleaner logs if it's burning people from being too hot or cold - if(reagents.chem_temp < 300 && reagents.chem_temp > 280) // 280 is the cold threshold for slimes, 300 the hot threshold for drask - return - - var/attack_log_type = ATKLOG_MOST - if(reagents.has_reagent("sacid") || reagents.has_reagent("facid") || reagents.has_reagent("lube")) - attack_log_type = ATKLOG_FEW - msg_admin_attack("[key_name_admin(user)] used a spray bottle at [COORD(user)] - Contents: [contents_log] - Temperature: [reagents.chem_temp]K", attack_log_type) - log_game("[key_name(user)] used a spray bottle at [COORD(user)] - Contents: [contents_log] - Temperature: [reagents.chem_temp]K") - return - - -/obj/item/reagent_containers/spray/proc/spray(var/atom/A) - var/obj/effect/decal/chempuff/D = new /obj/effect/decal/chempuff(get_turf(src)) - D.create_reagents(amount_per_transfer_from_this) - reagents.trans_to(D, amount_per_transfer_from_this, 1/spray_currentrange) - D.icon += mix_color_from_reagents(D.reagents.reagent_list) - spawn(0) - for(var/i=0, iYou [amount_per_transfer_from_this == 10 ? "remove" : "fix"] the nozzle. You'll now use [amount_per_transfer_from_this] units per spray.
        ") - -/obj/item/reagent_containers/spray/examine(mob/user) - . = ..() - if(get_dist(user, src) && user == loc) - . += "[round(reagents.total_volume)] units left." - -/obj/item/reagent_containers/spray/verb/empty() - - set name = "Empty Spray Bottle" - set category = "Object" - set src in usr - if(usr.stat || !usr.canmove || usr.restrained()) - return - if(alert(usr, "Are you sure you want to empty that?", "Empty Bottle:", "Yes", "No") != "Yes") - return - if(isturf(usr.loc) && loc == usr) - to_chat(usr, "You empty [src] onto the floor.") - reagents.reaction(usr.loc) - reagents.clear_reagents() - -//space cleaner -/obj/item/reagent_containers/spray/cleaner - name = "space cleaner" - desc = "BLAM!-brand non-foaming space cleaner!" - list_reagents = list("cleaner" = 250) - -/obj/item/reagent_containers/spray/cleaner/drone - name = "space cleaner" - desc = "BLAM!-brand non-foaming space cleaner!" - volume = 50 - list_reagents = list("cleaner" = 50) - -//spray tan -/obj/item/reagent_containers/spray/spraytan - name = "spray tan" - volume = 50 - desc = "Gyaro brand spray tan. Do not spray near eyes or other orifices." - list_reagents = list("spraytan" = 50) - -//pepperspray -/obj/item/reagent_containers/spray/pepper - name = "pepperspray" - desc = "Manufactured by UhangInc, used to blind and down an opponent quickly." - icon = 'icons/obj/items.dmi' - icon_state = "pepperspray" - item_state = "pepperspray" - volume = 40 - spray_maxrange = 4 - amount_per_transfer_from_this = 5 - list_reagents = list("condensedcapsaicin" = 40) - -//water flower -/obj/item/reagent_containers/spray/waterflower - name = "water flower" - desc = "A seemingly innocent sunflower...with a twist." - icon = 'icons/obj/hydroponics/harvest.dmi' - icon_state = "sunflower" - item_state = "sunflower" - amount_per_transfer_from_this = 1 - volume = 10 - list_reagents = list("water" = 10) - -/obj/item/reagent_containers/spray/waterflower/attack_self(mob/user) //Don't allow changing how much the flower sprays - return - -//chemsprayer -/obj/item/reagent_containers/spray/chemsprayer - name = "chem sprayer" - desc = "A utility used to spray large amounts of reagents in a given area." - icon = 'icons/obj/guns/projectile.dmi' - icon_state = "chemsprayer" - item_state = "chemsprayer" - throwforce = 0 - w_class = WEIGHT_CLASS_NORMAL - spray_maxrange = 7 - spray_currentrange = 7 - amount_per_transfer_from_this = 10 - volume = 600 - origin_tech = "combat=3;materials=3;engineering=3" - - -/obj/item/reagent_containers/spray/chemsprayer/spray(var/atom/A) - var/Sprays[3] - for(var/i=1, i<=3, i++) // intialize sprays - if(reagents.total_volume < 1) break - var/obj/effect/decal/chempuff/D = new/obj/effect/decal/chempuff(get_turf(src)) - D.create_reagents(amount_per_transfer_from_this) - reagents.trans_to(D, amount_per_transfer_from_this) - - D.icon += mix_color_from_reagents(D.reagents.reagent_list) - - Sprays[i] = D - - var/direction = get_dir(src, A) - var/turf/T = get_turf(A) - var/turf/T1 = get_step(T,turn(direction, 90)) - var/turf/T2 = get_step(T,turn(direction, -90)) - var/list/the_targets = list(T,T1,T2) - - for(var/i=1, i<=Sprays.len, i++) - spawn() - var/obj/effect/decal/chempuff/D = Sprays[i] - if(!D) continue - - // Spreads the sprays a little bit - var/turf/my_target = pick(the_targets) - the_targets -= my_target - - for(var/j=0, j<=spray_currentrange, j++) - step_towards(D, my_target) - D.reagents.reaction(get_turf(D)) - for(var/atom/t in get_turf(D)) - D.reagents.reaction(t) - sleep(2) - qdel(D) - - - -/obj/item/reagent_containers/spray/chemsprayer/attack_self(var/mob/user) - - amount_per_transfer_from_this = (amount_per_transfer_from_this == 10 ? 5 : 10) - to_chat(user, "You adjust the output switch. You'll now use [amount_per_transfer_from_this] units per spray.") - - -// Plant-B-Gone -/obj/item/reagent_containers/spray/plantbgone // -- Skie - name = "Plant-B-Gone" - desc = "Kills those pesky weeds!" - icon = 'icons/obj/hydroponics/equipment.dmi' - icon_state = "plantbgone" - item_state = "plantbgone" - volume = 100 - list_reagents = list("glyphosate" = 100) +/obj/item/reagent_containers/spray + name = "spray bottle" + desc = "A spray bottle, with an unscrewable top." + icon = 'icons/obj/janitor.dmi' + icon_state = "cleaner" + item_state = "cleaner" + flags = NOBLUDGEON + container_type = OPENCONTAINER + slot_flags = SLOT_BELT + throwforce = 0 + w_class = WEIGHT_CLASS_SMALL + throw_speed = 3 + throw_range = 7 + var/spray_maxrange = 3 //what the sprayer will set spray_currentrange to in the attack_self. + var/spray_currentrange = 3 //the range of tiles the sprayer will reach when in fixed mode. + amount_per_transfer_from_this = 5 + volume = 250 + possible_transfer_amounts = null + + +/obj/item/reagent_containers/spray/afterattack(atom/A, mob/user) + if(istype(A, /obj/item/storage) || istype(A, /obj/structure/table) || istype(A, /obj/structure/rack) || istype(A, /obj/structure/closet) \ + || istype(A, /obj/item/reagent_containers) || istype(A, /obj/structure/sink) || istype(A, /obj/structure/janitorialcart) || istype(A, /obj/machinery/hydroponics)) + return + + if(istype(A, /obj/effect/proc_holder/spell)) + return + + if(istype(A, /obj/structure/reagent_dispensers) && get_dist(src,A) <= 1) //this block copypasted from reagent_containers/glass, for lack of a better solution + if(!A.reagents.total_volume && A.reagents) + to_chat(user, "[A] is empty.") + return + + if(reagents.total_volume >= reagents.maximum_volume) + to_chat(user, "[src] is full.") + return + + var/trans = A.reagents.trans_to(src, 50) //This is a static amount, otherwise, it'll take forever to fill. + to_chat(user, "You fill [src] with [trans] units of the contents of [A].") + return + + if(reagents.total_volume < amount_per_transfer_from_this) + to_chat(user, "[src] is empty!") + return + + var/contents_log = reagents.reagent_list.Join(", ") + spray(A) + + playsound(loc, 'sound/effects/spray2.ogg', 50, 1, -6) + user.changeNext_move(CLICK_CD_RANGE*2) + user.newtonian_move(get_dir(A, user)) + + if(reagents.reagent_list.len == 1 && reagents.has_reagent("cleaner")) // Only show space cleaner logs if it's burning people from being too hot or cold + if(reagents.chem_temp < 300 && reagents.chem_temp > 280) // 280 is the cold threshold for slimes, 300 the hot threshold for drask + return + + var/attack_log_type = ATKLOG_MOST + if(reagents.has_reagent("sacid") || reagents.has_reagent("facid") || reagents.has_reagent("lube")) + attack_log_type = ATKLOG_FEW + msg_admin_attack("[key_name_admin(user)] used a spray bottle at [COORD(user)] - Contents: [contents_log] - Temperature: [reagents.chem_temp]K", attack_log_type) + log_game("[key_name(user)] used a spray bottle at [COORD(user)] - Contents: [contents_log] - Temperature: [reagents.chem_temp]K") + return + + +/obj/item/reagent_containers/spray/proc/spray(var/atom/A) + var/obj/effect/decal/chempuff/D = new /obj/effect/decal/chempuff(get_turf(src)) + D.create_reagents(amount_per_transfer_from_this) + reagents.trans_to(D, amount_per_transfer_from_this, 1/spray_currentrange) + D.icon += mix_color_from_reagents(D.reagents.reagent_list) + spawn(0) + for(var/i=0, iYou [amount_per_transfer_from_this == 10 ? "remove" : "fix"] the nozzle. You'll now use [amount_per_transfer_from_this] units per spray.
        ") + +/obj/item/reagent_containers/spray/examine(mob/user) + . = ..() + if(get_dist(user, src) && user == loc) + . += "[round(reagents.total_volume)] units left." + +/obj/item/reagent_containers/spray/verb/empty() + + set name = "Empty Spray Bottle" + set category = "Object" + set src in usr + if(usr.stat || !usr.canmove || usr.restrained()) + return + if(alert(usr, "Are you sure you want to empty that?", "Empty Bottle:", "Yes", "No") != "Yes") + return + if(isturf(usr.loc) && loc == usr) + to_chat(usr, "You empty [src] onto the floor.") + reagents.reaction(usr.loc) + reagents.clear_reagents() + +//space cleaner +/obj/item/reagent_containers/spray/cleaner + name = "space cleaner" + desc = "BLAM!-brand non-foaming space cleaner!" + list_reagents = list("cleaner" = 250) + +/obj/item/reagent_containers/spray/cleaner/drone + name = "space cleaner" + desc = "BLAM!-brand non-foaming space cleaner!" + volume = 50 + list_reagents = list("cleaner" = 50) + +//spray tan +/obj/item/reagent_containers/spray/spraytan + name = "spray tan" + volume = 50 + desc = "Gyaro brand spray tan. Do not spray near eyes or other orifices." + list_reagents = list("spraytan" = 50) + +//pepperspray +/obj/item/reagent_containers/spray/pepper + name = "pepperspray" + desc = "Manufactured by UhangInc, used to blind and down an opponent quickly." + icon = 'icons/obj/items.dmi' + icon_state = "pepperspray" + item_state = "pepperspray" + volume = 40 + spray_maxrange = 4 + amount_per_transfer_from_this = 5 + list_reagents = list("condensedcapsaicin" = 40) + +//water flower +/obj/item/reagent_containers/spray/waterflower + name = "water flower" + desc = "A seemingly innocent sunflower...with a twist." + icon = 'icons/obj/hydroponics/harvest.dmi' + icon_state = "sunflower" + item_state = "sunflower" + amount_per_transfer_from_this = 1 + volume = 10 + list_reagents = list("water" = 10) + +/obj/item/reagent_containers/spray/waterflower/attack_self(mob/user) //Don't allow changing how much the flower sprays + return + +//chemsprayer +/obj/item/reagent_containers/spray/chemsprayer + name = "chem sprayer" + desc = "A utility used to spray large amounts of reagents in a given area." + icon = 'icons/obj/guns/projectile.dmi' + icon_state = "chemsprayer" + item_state = "chemsprayer" + throwforce = 0 + w_class = WEIGHT_CLASS_NORMAL + spray_maxrange = 7 + spray_currentrange = 7 + amount_per_transfer_from_this = 10 + volume = 600 + origin_tech = "combat=3;materials=3;engineering=3" + + +/obj/item/reagent_containers/spray/chemsprayer/spray(var/atom/A) + var/Sprays[3] + for(var/i=1, i<=3, i++) // intialize sprays + if(reagents.total_volume < 1) break + var/obj/effect/decal/chempuff/D = new/obj/effect/decal/chempuff(get_turf(src)) + D.create_reagents(amount_per_transfer_from_this) + reagents.trans_to(D, amount_per_transfer_from_this) + + D.icon += mix_color_from_reagents(D.reagents.reagent_list) + + Sprays[i] = D + + var/direction = get_dir(src, A) + var/turf/T = get_turf(A) + var/turf/T1 = get_step(T,turn(direction, 90)) + var/turf/T2 = get_step(T,turn(direction, -90)) + var/list/the_targets = list(T,T1,T2) + + for(var/i=1, i<=Sprays.len, i++) + spawn() + var/obj/effect/decal/chempuff/D = Sprays[i] + if(!D) continue + + // Spreads the sprays a little bit + var/turf/my_target = pick(the_targets) + the_targets -= my_target + + for(var/j=0, j<=spray_currentrange, j++) + step_towards(D, my_target) + D.reagents.reaction(get_turf(D)) + for(var/atom/t in get_turf(D)) + D.reagents.reaction(t) + sleep(2) + qdel(D) + + + +/obj/item/reagent_containers/spray/chemsprayer/attack_self(var/mob/user) + + amount_per_transfer_from_this = (amount_per_transfer_from_this == 10 ? 5 : 10) + to_chat(user, "You adjust the output switch. You'll now use [amount_per_transfer_from_this] units per spray.") + + +// Plant-B-Gone +/obj/item/reagent_containers/spray/plantbgone // -- Skie + name = "Plant-B-Gone" + desc = "Kills those pesky weeds!" + icon = 'icons/obj/hydroponics/equipment.dmi' + icon_state = "plantbgone" + item_state = "plantbgone" + volume = 100 + list_reagents = list("glyphosate" = 100) diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index 13c755214a4..e87e72d8750 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -1,229 +1,234 @@ -#define SYRINGE_DRAW 0 -#define SYRINGE_INJECT 1 -#define SYRINGE_BROKEN 2 - -/obj/item/reagent_containers/syringe - name = "Syringe" - desc = "A syringe." - icon = 'icons/goonstation/objects/syringe.dmi' - item_state = "syringe_0" - icon_state = "0" - amount_per_transfer_from_this = 5 - possible_transfer_amounts = list() - volume = 15 - sharp = TRUE - var/busy = FALSE - var/mode = SYRINGE_DRAW - var/projectile_type = /obj/item/projectile/bullet/dart/syringe - materials = list(MAT_METAL=10, MAT_GLASS=20) - container_type = TRANSPARENT - -/obj/item/reagent_containers/syringe/New() - ..() - if(list_reagents) //syringe starts in inject mode if its already got something inside - mode = SYRINGE_INJECT - update_icon() - -/obj/item/reagent_containers/syringe/on_reagent_change() - update_icon() - -/obj/item/reagent_containers/syringe/pickup(mob/user) - . = ..() - update_icon() - -/obj/item/reagent_containers/syringe/dropped(mob/user) - ..() - update_icon() - -/obj/item/reagent_containers/syringe/attack_self(mob/user) - mode = !mode - update_icon() - -/obj/item/reagent_containers/syringe/attack_hand() - ..() - update_icon() - -/obj/item/reagent_containers/syringe/attack(mob/living/M, mob/living/user, def_zone) - return - -/obj/item/reagent_containers/syringe/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/storage/bag)) - ..() - -/obj/item/reagent_containers/syringe/afterattack(atom/target, mob/user , proximity) - if(!proximity) - return - if(!target.reagents) - return - - var/mob/living/L - if(isliving(target)) - L = target - if(!L.can_inject(user, TRUE)) - return - - switch(mode) - if(SYRINGE_DRAW) - - if(reagents.holder_full()) - to_chat(user, "The syringe is full.") - return - - if(L) //living mob - var/drawn_amount = reagents.maximum_volume - reagents.total_volume - if(target != user) - target.visible_message("[user] is trying to take a blood sample from [target]!", \ - "[user] is trying to take a blood sample from [target]!") - busy = TRUE - if(!do_mob(user, target)) - busy = FALSE - return - if(reagents.holder_full()) - return - busy = FALSE - if(L.transfer_blood_to(src, drawn_amount)) - user.visible_message("[user] takes a blood sample from [L].") - else - to_chat(user, "You are unable to draw any blood from [L]!") - - else //if not mob - if(!target.reagents.total_volume) - to_chat(user, "[target] is empty!") - return - - if(!target.is_drawable(user)) - to_chat(user, "You cannot directly remove reagents from [target]!") - return - - var/trans = target.reagents.trans_to(src, amount_per_transfer_from_this) // transfer from, transfer to - who cares? - - to_chat(user, "You fill [src] with [trans] units of the solution. It now contains [reagents.total_volume] units.") - if(reagents.holder_full()) - mode = !mode - update_icon() - - if(SYRINGE_INJECT) - if(!reagents.total_volume) - to_chat(user, "[src] is empty.") - return - - if(!L && !target.is_injectable(user)) //only checks on non-living mobs, due to how can_inject() handles - to_chat(user, "You cannot directly fill [target]!") - return - - if(target.reagents.total_volume >= target.reagents.maximum_volume) - to_chat(user, "[target] is full.") - return - - if(L) //living mob - if(!L.can_inject(user, TRUE)) - return - if(L != user) - L.visible_message("[user] is trying to inject [L]!", \ - "[user] is trying to inject you!") - if(!do_mob(user, L)) - return - if(!reagents.total_volume) - return - if(L.reagents.total_volume >= L.reagents.maximum_volume) - return - L.visible_message("[user] injects [L] with the syringe!", \ - "[user] injects [L] with the syringe!") - - var/list/rinject = list() - for(var/datum/reagent/R in reagents.reagent_list) - rinject += R.name - var/contained = english_list(rinject) - - add_attack_logs(user, L, "Injected with [name] containing [contained], transfered [amount_per_transfer_from_this] units", reagents.harmless_helper() ? ATKLOG_ALMOSTALL : null) - - var/fraction = min(amount_per_transfer_from_this / reagents.total_volume, 1) - reagents.reaction(L, INGEST, fraction) - reagents.trans_to(target, amount_per_transfer_from_this) - to_chat(user, "You inject [amount_per_transfer_from_this] units of the solution. The syringe now contains [reagents.total_volume] units.") - if(reagents.total_volume <= 0 && mode == SYRINGE_INJECT) - mode = SYRINGE_DRAW - update_icon() - -/obj/item/reagent_containers/syringe/update_icon() - cut_overlays() - var/rounded_vol - if(reagents && reagents.total_volume) - rounded_vol = Clamp(round((reagents.total_volume / volume * 15), 5), 1, 15) - var/image/filling_overlay = mutable_appearance('icons/obj/reagentfillings.dmi', "syringe[rounded_vol]") - filling_overlay.icon += mix_color_from_reagents(reagents.reagent_list) - add_overlay(filling_overlay) - else - rounded_vol = 0 - icon_state = "[rounded_vol]" - item_state = "syringe_[rounded_vol]" - if(ismob(loc)) - var/mob/M = loc - var/injoverlay - switch(mode) - if(SYRINGE_DRAW) - injoverlay = "draw" - if(SYRINGE_INJECT) - injoverlay = "inject" - add_overlay(injoverlay) - M.update_inv_l_hand() - M.update_inv_r_hand() - -/obj/item/reagent_containers/syringe/antiviral - name = "Syringe (spaceacillin)" - desc = "Contains antiviral agents." - list_reagents = list("spaceacillin" = 15) - -/obj/item/reagent_containers/syringe/charcoal - name = "Syringe (charcoal)" - desc = "Contains charcoal - used to treat toxins and damage from toxins." - list_reagents = list("charcoal" = 15) - -/obj/item/reagent_containers/syringe/epinephrine - name = "Syringe (Epinephrine)" - desc = "Contains epinephrine - used to stabilize patients." - list_reagents = list("epinephrine" = 15) - -/obj/item/reagent_containers/syringe/insulin - name = "Syringe (insulin)" - desc = "Contains insulin - used to treat diabetes." - list_reagents = list("insulin" = 15) - -/obj/item/reagent_containers/syringe/calomel - name = "Syringe (calomel)" - desc = "Contains calomel, which be used to purge impurities, but is highly toxic itself." - list_reagents = list("calomel" = 15) - -/obj/item/reagent_containers/syringe/bioterror - name = "bioterror syringe" - desc = "Contains several paralyzing reagents." - list_reagents = list("neurotoxin" = 5, "capulettium_plus" = 5, "sodium_thiopental" = 5) - -/obj/item/reagent_containers/syringe/gluttony - name = "Gluttony's Blessing" - desc = "A syringe recovered from a dread place. It probably isn't wise to use." - amount_per_transfer_from_this = 1 - volume = 1 - list_reagents = list("gluttonytoxin" = 1) - -/obj/item/reagent_containers/syringe/capulettium_plus - name = "capulettium plus syringe" - desc = "For silencing targets. Allows for fake deaths." - list_reagents = list("capulettium_plus" = 15) - -/obj/item/reagent_containers/syringe/sarin - name = "sarin syringe" - desc = "A deadly neurotoxin, for killing." - list_reagents = list("sarin" = 15) - -/obj/item/reagent_containers/syringe/pancuronium - name = "pancuronium syringe" - desc = "A powerful paralyzing poison." - list_reagents = list("pancuronium" = 15) - -/obj/item/reagent_containers/syringe/lethal - name = "lethal injection syringe" - desc = "A syringe used for lethal injections. It can hold up to 50 units." - amount_per_transfer_from_this = 50 - volume = 50 - list_reagents = list("toxin" = 15, "pancuronium" = 10, "cyanide" = 5, "facid" = 10, "fluorine" = 10) \ No newline at end of file +#define SYRINGE_DRAW 0 +#define SYRINGE_INJECT 1 +#define SYRINGE_BROKEN 2 + +/obj/item/reagent_containers/syringe + name = "Syringe" + desc = "A syringe." + icon = 'icons/goonstation/objects/syringe.dmi' + item_state = "syringe_0" + icon_state = "0" + amount_per_transfer_from_this = 5 + possible_transfer_amounts = list() + volume = 15 + sharp = TRUE + var/busy = FALSE + var/mode = SYRINGE_DRAW + var/projectile_type = /obj/item/projectile/bullet/dart/syringe + materials = list(MAT_METAL=10, MAT_GLASS=20) + container_type = TRANSPARENT + +/obj/item/reagent_containers/syringe/New() + ..() + if(list_reagents) //syringe starts in inject mode if its already got something inside + mode = SYRINGE_INJECT + update_icon() + +/obj/item/reagent_containers/syringe/on_reagent_change() + update_icon() + +/obj/item/reagent_containers/syringe/pickup(mob/user) + . = ..() + update_icon() + +/obj/item/reagent_containers/syringe/dropped(mob/user) + ..() + update_icon() + +/obj/item/reagent_containers/syringe/attack_self(mob/user) + mode = !mode + update_icon() + +/obj/item/reagent_containers/syringe/attack_hand() + ..() + update_icon() + +/obj/item/reagent_containers/syringe/attack(mob/living/M, mob/living/user, def_zone) + return + +/obj/item/reagent_containers/syringe/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/storage/bag)) + ..() + +/obj/item/reagent_containers/syringe/afterattack(atom/target, mob/user , proximity) + if(!proximity) + return + if(!target.reagents) + return + + var/mob/living/L + if(isliving(target)) + L = target + if(!L.can_inject(user, TRUE)) + return + + switch(mode) + if(SYRINGE_DRAW) + + if(reagents.holder_full()) + to_chat(user, "The syringe is full.") + return + + if(L) //living mob + var/drawn_amount = reagents.maximum_volume - reagents.total_volume + if(target != user) + target.visible_message("[user] is trying to take a blood sample from [target]!", \ + "[user] is trying to take a blood sample from [target]!") + busy = TRUE + if(!do_mob(user, target)) + busy = FALSE + return + if(reagents.holder_full()) + return + busy = FALSE + if(L.transfer_blood_to(src, drawn_amount)) + user.visible_message("[user] takes a blood sample from [L].") + else + to_chat(user, "You are unable to draw any blood from [L]!") + + else //if not mob + if(!target.reagents.total_volume) + to_chat(user, "[target] is empty!") + return + + if(!target.is_drawable(user)) + to_chat(user, "You cannot directly remove reagents from [target]!") + return + + var/trans = target.reagents.trans_to(src, amount_per_transfer_from_this) // transfer from, transfer to - who cares? + + to_chat(user, "You fill [src] with [trans] units of the solution. It now contains [reagents.total_volume] units.") + if(reagents.holder_full()) + mode = !mode + update_icon() + + if(SYRINGE_INJECT) + if(!reagents.total_volume) + to_chat(user, "[src] is empty.") + return + + if(!L && !target.is_injectable(user)) //only checks on non-living mobs, due to how can_inject() handles + to_chat(user, "You cannot directly fill [target]!") + return + + if(target.reagents.total_volume >= target.reagents.maximum_volume) + to_chat(user, "[target] is full.") + return + + if(L) //living mob + if(!L.can_inject(user, TRUE)) + return + if(L != user) + L.visible_message("[user] is trying to inject [L]!", \ + "[user] is trying to inject you!") + if(!do_mob(user, L)) + return + if(!reagents.total_volume) + return + if(L.reagents.total_volume >= L.reagents.maximum_volume) + return + L.visible_message("[user] injects [L] with the syringe!", \ + "[user] injects [L] with the syringe!") + + var/list/rinject = list() + for(var/datum/reagent/R in reagents.reagent_list) + rinject += R.name + var/contained = english_list(rinject) + + add_attack_logs(user, L, "Injected with [name] containing [contained], transfered [amount_per_transfer_from_this] units", reagents.harmless_helper() ? ATKLOG_ALMOSTALL : null) + + var/fraction = min(amount_per_transfer_from_this / reagents.total_volume, 1) + reagents.reaction(L, REAGENT_INGEST, fraction) + reagents.trans_to(target, amount_per_transfer_from_this) + to_chat(user, "You inject [amount_per_transfer_from_this] units of the solution. The syringe now contains [reagents.total_volume] units.") + if(reagents.total_volume <= 0 && mode == SYRINGE_INJECT) + mode = SYRINGE_DRAW + update_icon() + +/obj/item/reagent_containers/syringe/update_icon() + cut_overlays() + var/rounded_vol + if(reagents && reagents.total_volume) + rounded_vol = Clamp(round((reagents.total_volume / volume * 15), 5), 1, 15) + var/image/filling_overlay = mutable_appearance('icons/obj/reagentfillings.dmi', "syringe[rounded_vol]") + filling_overlay.icon += mix_color_from_reagents(reagents.reagent_list) + add_overlay(filling_overlay) + else + rounded_vol = 0 + icon_state = "[rounded_vol]" + item_state = "syringe_[rounded_vol]" + if(ismob(loc)) + var/mob/M = loc + var/injoverlay + switch(mode) + if(SYRINGE_DRAW) + injoverlay = "draw" + if(SYRINGE_INJECT) + injoverlay = "inject" + add_overlay(injoverlay) + M.update_inv_l_hand() + M.update_inv_r_hand() + +/obj/item/reagent_containers/syringe/antiviral + name = "Syringe (spaceacillin)" + desc = "Contains antiviral agents." + list_reagents = list("spaceacillin" = 15) + +/obj/item/reagent_containers/syringe/charcoal + name = "Syringe (charcoal)" + desc = "Contains charcoal - used to treat toxins and damage from toxins." + list_reagents = list("charcoal" = 15) + +/obj/item/reagent_containers/syringe/epinephrine + name = "Syringe (Epinephrine)" + desc = "Contains epinephrine - used to stabilize patients." + list_reagents = list("epinephrine" = 15) + +/obj/item/reagent_containers/syringe/insulin + name = "Syringe (insulin)" + desc = "Contains insulin - used to treat diabetes." + list_reagents = list("insulin" = 15) + +/obj/item/reagent_containers/syringe/calomel + name = "Syringe (calomel)" + desc = "Contains calomel, which be used to purge impurities, but is highly toxic itself." + list_reagents = list("calomel" = 15) + +/obj/item/reagent_containers/syringe/heparin + name = "Syringe (heparin)" + desc = "Contains heparin, a blood anticoagulant." + list_reagents = list("heparin" = 15) + +/obj/item/reagent_containers/syringe/bioterror + name = "bioterror syringe" + desc = "Contains several paralyzing reagents." + list_reagents = list("neurotoxin" = 5, "capulettium_plus" = 5, "sodium_thiopental" = 5) + +/obj/item/reagent_containers/syringe/gluttony + name = "Gluttony's Blessing" + desc = "A syringe recovered from a dread place. It probably isn't wise to use." + amount_per_transfer_from_this = 1 + volume = 1 + list_reagents = list("gluttonytoxin" = 1) + +/obj/item/reagent_containers/syringe/capulettium_plus + name = "capulettium plus syringe" + desc = "For silencing targets. Allows for fake deaths." + list_reagents = list("capulettium_plus" = 15) + +/obj/item/reagent_containers/syringe/sarin + name = "sarin syringe" + desc = "A deadly neurotoxin, for killing." + list_reagents = list("sarin" = 15) + +/obj/item/reagent_containers/syringe/pancuronium + name = "pancuronium syringe" + desc = "A powerful paralyzing poison." + list_reagents = list("pancuronium" = 15) + +/obj/item/reagent_containers/syringe/lethal + name = "lethal injection syringe" + desc = "A syringe used for lethal injections. It can hold up to 50 units." + amount_per_transfer_from_this = 50 + volume = 50 + list_reagents = list("toxin" = 15, "pancuronium" = 10, "cyanide" = 5, "facid" = 10, "fluorine" = 10) diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm index c1e4b89e774..194675a21d8 100644 --- a/code/modules/reagents/reagent_dispenser.dm +++ b/code/modules/reagents/reagent_dispenser.dm @@ -1,307 +1,278 @@ -/obj/structure/reagent_dispensers - name = "Dispenser" - desc = "..." - icon = 'icons/obj/objects.dmi' - icon_state = "watertank" - density = 1 - anchored = 0 - pressure_resistance = 2*ONE_ATMOSPHERE - container_type = DRAINABLE | AMOUNT_VISIBLE - max_integrity = 300 - var/tank_volume = 1000 //In units, how much the dispenser can hold - var/reagent_id = "water" //The ID of the reagent that the dispenser uses - var/lastrigger = "" // The last person to rig this fuel tank - Stored with the object. Only the last person matter for investigation - -/obj/structure/reagent_dispensers/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) - . = ..() - if(. && obj_integrity > 0) - if(tank_volume && (damage_flag == "bullet" || damage_flag == "laser")) - boom() - -/obj/structure/reagent_dispensers/attackby(obj/item/I, mob/user, params) - if(I.is_refillable()) - return FALSE //so we can refill them via their afterattack. - return ..() - -/obj/structure/reagent_dispensers/New() - create_reagents(tank_volume) - reagents.add_reagent(reagent_id, tank_volume) - ..() - -/obj/structure/reagent_dispensers/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) - ..() - if(reagents) - for(var/i in 1 to 8) - if(reagents) - reagents.temperature_reagents(exposed_temperature) - -/obj/structure/reagent_dispensers/proc/boom() - visible_message("[src] ruptures!") - chem_splash(loc, 5, list(reagents)) - qdel(src) - -/obj/structure/reagent_dispensers/deconstruct(disassembled = TRUE) - if(!(flags & NODECONSTRUCT)) - if(!disassembled) - boom() - else - qdel(src) - -//Dispensers -/obj/structure/reagent_dispensers/watertank - name = "water tank" - desc = "A water tank." - icon_state = "water" - -/obj/structure/reagent_dispensers/watertank/high - name = "high-capacity water tank" - desc = "A highly-pressurized water tank made to hold gargantuan amounts of water.." - icon_state = "water_high" //I was gonna clean my room... - tank_volume = 100000 - - -/obj/structure/reagent_dispensers/oil - name = "oil tank" - desc = "A tank of oil, commonly used to by robotics to fix leaking IPCs or just to loosen up those rusted underused parts." - icon_state = "oil" - reagent_id = "oil" - tank_volume = 3000 - -/obj/structure/reagent_dispensers/fueltank - name = "fuel tank" - desc = "A tank full of industrial welding fuel. Do not consume." - icon_state = "fuel" - reagent_id = "fuel" - tank_volume = 4000 - var/obj/item/assembly_holder/rig = null - var/accepts_rig = 1 - -/obj/structure/reagent_dispensers/fueltank/Destroy() - QDEL_NULL(rig) - return ..() - -/obj/structure/reagent_dispensers/fueltank/bullet_act(obj/item/projectile/P) - ..() - if(!QDELETED(src)) //wasn't deleted by the projectile's effects. - if(!P.nodamage && ((P.damage_type == BURN) || (P.damage_type == BRUTE))) - message_admins("[key_name_admin(P.firer)] triggered a fueltank explosion with [P.name] at [COORD(loc)] ") - log_game("[key_name(P.firer)] triggered a fueltank explosion with [P.name] at [COORD(loc)]") - investigate_log("[key_name(P.firer)] triggered a fueltank explosion with [P.name] at [COORD(loc)]", INVESTIGATE_BOMB) - boom() - -/obj/structure/reagent_dispensers/fueltank/boom(rigtrigger = FALSE) // Prevent case where someone who rigged the tank is blamed for the explosion when the rig isn't what triggered the explosion - if(rigtrigger) // If the explosion is triggered by an assembly holder - message_admins("A fueltank, last rigged by [lastrigger], was triggered at [COORD(loc)]") // Then admin is informed of the last person who rigged the fuel tank - log_game("A fueltank, last rigged by [lastrigger], triggered at [COORD(loc)]") - investigate_log("A fueltank, last rigged by [lastrigger], triggered at [COORD(loc)]", INVESTIGATE_BOMB) - if(reagents) - reagents.set_reagent_temp(1000) //uh-oh - qdel(src) - -/obj/structure/reagent_dispensers/fueltank/blob_act(obj/structure/blob/B) - boom() - -/obj/structure/reagent_dispensers/fueltank/ex_act() - boom() - -/obj/structure/reagent_dispensers/fueltank/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) - ..() - boom() - -/obj/structure/reagent_dispensers/fueltank/tesla_act() - ..() //extend the zap - boom() - -/obj/structure/reagent_dispensers/fueltank/examine(mob/user) - . = ..() - if(get_dist(user, src) <= 2 && rig) - . += "There is some kind of device rigged to the tank." - -/obj/structure/reagent_dispensers/fueltank/attack_hand() - if(rig) - usr.visible_message("[usr] begins to detach [rig] from [src].", "You begin to detach [rig] from [src].") - if(do_after(usr, 20, target = src)) - usr.visible_message("[usr] detaches [rig] from [src].", "You detach [rig] from [src].") - rig.forceMove(get_turf(usr)) - rig = null - lastrigger = null - overlays.Cut() - -/obj/structure/reagent_dispensers/fueltank/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/assembly_holder) && accepts_rig) - if(rig) - to_chat(user, "There is another device in the way.") - return ..() - user.visible_message("[user] begins rigging [I] to [src].", "You begin rigging [I] to [src]") - if(do_after(user, 20, target = src)) - user.visible_message("[user] rigs [I] to [src].", "You rig [I] to [src].") - - var/obj/item/assembly_holder/H = I - if(istype(H.a_left, /obj/item/assembly/igniter) || istype(H.a_right, /obj/item/assembly/igniter)) - msg_admin_attack("[key_name_admin(user)] rigged [src.name] with [I.name] for explosion (JMP)", ATKLOG_FEW) - log_game("[key_name(user)] rigged [src.name] with [I.name] for explosion at [COORD(loc)]") - investigate_log("[key_name(user)] rigged [src.name] with [I.name] for explosion at [COORD(loc)]", INVESTIGATE_BOMB) - - lastrigger = "[key_name(user)]" - rig = H - user.drop_item() - H.forceMove(src) - - var/icon/test = getFlatIcon(H) - test.Shift(NORTH, 1) - test.Shift(EAST, 6) - overlays += test - - if(istype(I, /obj/item/weldingtool)) - if(!reagents.has_reagent("fuel")) - to_chat(user, "[src] is out of fuel!") - return - var/obj/item/weldingtool/W = I - if(!W.welding) - if(W.reagents.has_reagent("fuel", W.max_fuel)) - to_chat(user, "Your [W] is already full!") - return - reagents.trans_to(W, W.max_fuel) - user.visible_message("[user] refills [user.p_their()] [W].", "You refill [W].") - playsound(src, 'sound/effects/refill.ogg', 50, 1) - W.update_icon() - else - user.visible_message("[user] catastrophically fails at refilling [user.p_their()] [W]!", "That was stupid of you.") - message_admins("[key_name_admin(user)] triggered a fueltank explosion at [COORD(loc)]") - log_game("[key_name(user)] triggered a fueltank explosion at [COORD(loc)]") - investigate_log("[key_name(user)] triggered a fueltank explosion at [COORD(loc)]", INVESTIGATE_BOMB) - boom() - else - return ..() - -/obj/structure/reagent_dispensers/fueltank/Move() - ..() - if(rig) - rig.process_movement() - -/obj/structure/reagent_dispensers/fueltank/HasProximity(atom/movable/AM) - if(rig) - rig.HasProximity(AM) - -/obj/structure/reagent_dispensers/fueltank/Crossed(atom/movable/AM, oldloc) - if(rig) - rig.Crossed(AM, oldloc) - -/obj/structure/reagent_dispensers/fueltank/hear_talk(mob/living/M, list/message_pieces) - if(rig) - rig.hear_talk(M, message_pieces) - -/obj/structure/reagent_dispensers/fueltank/hear_message(mob/living/M, msg) - if(rig) - rig.hear_message(M, msg) - -/obj/structure/reagent_dispensers/fueltank/Bump() - ..() - if(rig) - rig.process_movement() - - -/obj/structure/reagent_dispensers/peppertank - name = "pepper spray refiller" - desc = "Contains condensed capsaicin for use in law \"enforcement.\"" - icon_state = "pepper" - anchored = 1 - density = 0 - reagent_id = "condensedcapsaicin" - -/obj/structure/reagent_dispensers/water_cooler - name = "liquid cooler" - desc = "A machine that dispenses liquid to drink." - icon = 'icons/obj/vending.dmi' - icon_state = "water_cooler" - anchored = 1 - tank_volume = 500 - reagent_id = "water" - var/paper_cups = 25 //Paper cups left from the cooler - -/obj/structure/reagent_dispensers/water_cooler/examine(mob/user) - . = ..() - if(get_dist(user, src) <= 2) - . += "There are [paper_cups ? paper_cups : "no"] paper cups left." - -/obj/structure/reagent_dispensers/water_cooler/attack_hand(mob/living/user) - if(!paper_cups) - to_chat(user, "There aren't any cups left!") - return - user.visible_message("[user] takes a cup from [src].", "You take a paper cup from [src].") - var/obj/item/reagent_containers/food/drinks/sillycup/S = new(get_turf(src)) - user.put_in_hands(S) - paper_cups-- - -/obj/structure/reagent_dispensers/water_cooler/attackby(obj/item/W, mob/living/user, params) - add_fingerprint(user) - user.changeNext_move(CLICK_CD_MELEE) - if(iswrench(W)) - if(anchored) - playsound(loc, W.usesound, 100, 1) - user.visible_message("[user] starts loosening [src]'s floor casters.", \ - "You start loosening [src]'s floor casters...") - if(do_after(user, 40 * W.toolspeed, target = src)) - if(!loc || !anchored) - return - user.visible_message("[user] loosened [src]'s floor casters.", \ - "You loosen [src]'s floor casters.") - anchored = 0 - else - if(!isfloorturf(loc)) - user.visible_message("A floor must be present to secure [src]!") - return - playsound(loc, W.usesound, 100, 1) - user.visible_message("[user] start securing [src]'s floor casters...", \ - "You start securing [src]'s floor casters...") - if(do_after(user, 40 * W.toolspeed, target = src)) - if(!loc || anchored) - return - user.visible_message("[user] has secured [src]'s floor casters.", \ - "You have secured [src]'s floor casters.") - anchored = 1 - return - return ..() - -/obj/structure/reagent_dispensers/beerkeg - name = "beer keg" - desc = "Beer is liquid bread, it's good for you..." - icon_state = "beer" - reagent_id = "beer" - -/obj/structure/reagent_dispensers/beerkeg/blob_act(obj/structure/blob/B) - explosion(loc, 0, 3, 5, 7, 10) - if(!QDELETED(src)) - qdel(src) - -/obj/structure/reagent_dispensers/beerkeg/nuke - name = "Nanotrasen-brand nuclear fission explosive" - desc = "One of the more successful achievements of the Nanotrasen Corporate Warfare Division, their nuclear fission explosives are renowned for being cheap\ - to produce and devestatingly effective. Signs explain that though this is just a model, every Nanotrasen station is equipped with one, just in case. \ - All Captains carefully guard the disk needed to detonate them - at least, the sign says they do. There seems to be a tap on the back." - icon = 'icons/obj/stationobjs.dmi' - icon_state = "nuclearbomb0" - -/obj/structure/reagent_dispensers/virusfood - name = "virus food dispenser" - desc = "A dispenser of low-potency virus mutagenic." - icon_state = "virus_food" - anchored = 1 - density = 0 - reagent_id = "virusfood" - -/obj/structure/reagent_dispensers/spacecleanertank - name = "space cleaner refiller" - desc = "Refills space cleaner bottles." - icon_state = "cleaner" - anchored = 1 - density = 0 - tank_volume = 5000 - reagent_id = "cleaner" - -/obj/structure/reagent_dispensers/fueltank/chem - icon_state = "fuel_chem" - anchored = 1 - density = 0 - accepts_rig = 0 - tank_volume = 1000 +/obj/structure/reagent_dispensers + name = "Dispenser" + desc = "..." + icon = 'icons/obj/objects.dmi' + icon_state = "watertank" + density = 1 + anchored = 0 + pressure_resistance = 2*ONE_ATMOSPHERE + container_type = DRAINABLE | AMOUNT_VISIBLE + max_integrity = 300 + var/tank_volume = 1000 //In units, how much the dispenser can hold + var/reagent_id = "water" //The ID of the reagent that the dispenser uses + var/lastrigger = "" // The last person to rig this fuel tank - Stored with the object. Only the last person matter for investigation + +/obj/structure/reagent_dispensers/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir) + . = ..() + if(. && obj_integrity > 0) + if(tank_volume && (damage_flag == "bullet" || damage_flag == "laser")) + boom() + +/obj/structure/reagent_dispensers/attackby(obj/item/I, mob/user, params) + if(I.is_refillable()) + return FALSE //so we can refill them via their afterattack. + return ..() + +/obj/structure/reagent_dispensers/New() + create_reagents(tank_volume) + reagents.add_reagent(reagent_id, tank_volume) + ..() + +/obj/structure/reagent_dispensers/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume) + ..() + if(reagents) + for(var/i in 1 to 8) + if(reagents) + reagents.temperature_reagents(exposed_temperature) + +/obj/structure/reagent_dispensers/proc/boom() + visible_message("[src] ruptures!") + chem_splash(loc, 5, list(reagents)) + qdel(src) + +/obj/structure/reagent_dispensers/deconstruct(disassembled = TRUE) + if(!(flags & NODECONSTRUCT)) + if(!disassembled) + boom() + else + qdel(src) + +//Dispensers +/obj/structure/reagent_dispensers/watertank + name = "water tank" + desc = "A water tank." + icon_state = "water" + +/obj/structure/reagent_dispensers/watertank/high + name = "high-capacity water tank" + desc = "A highly-pressurized water tank made to hold gargantuan amounts of water.." + icon_state = "water_high" //I was gonna clean my room... + tank_volume = 100000 + + +/obj/structure/reagent_dispensers/oil + name = "oil tank" + desc = "A tank of oil, commonly used to by robotics to fix leaking IPCs or just to loosen up those rusted underused parts." + icon_state = "oil" + reagent_id = "oil" + tank_volume = 3000 + +/obj/structure/reagent_dispensers/fueltank + name = "fuel tank" + desc = "A tank full of industrial welding fuel. Do not consume." + icon_state = "fuel" + reagent_id = "fuel" + tank_volume = 4000 + var/obj/item/assembly_holder/rig = null + var/accepts_rig = 1 + +/obj/structure/reagent_dispensers/fueltank/Destroy() + QDEL_NULL(rig) + return ..() + +/obj/structure/reagent_dispensers/fueltank/bullet_act(obj/item/projectile/P) + ..() + if(!QDELETED(src)) //wasn't deleted by the projectile's effects. + if(!P.nodamage && ((P.damage_type == BURN) || (P.damage_type == BRUTE))) + message_admins("[key_name_admin(P.firer)] triggered a fueltank explosion with [P.name] at [COORD(loc)] ") + log_game("[key_name(P.firer)] triggered a fueltank explosion with [P.name] at [COORD(loc)]") + investigate_log("[key_name(P.firer)] triggered a fueltank explosion with [P.name] at [COORD(loc)]", INVESTIGATE_BOMB) + boom() + +/obj/structure/reagent_dispensers/fueltank/boom(rigtrigger = FALSE) // Prevent case where someone who rigged the tank is blamed for the explosion when the rig isn't what triggered the explosion + if(rigtrigger) // If the explosion is triggered by an assembly holder + message_admins("A fueltank, last rigged by [lastrigger], was triggered at [COORD(loc)]") // Then admin is informed of the last person who rigged the fuel tank + log_game("A fueltank, last rigged by [lastrigger], triggered at [COORD(loc)]") + investigate_log("A fueltank, last rigged by [lastrigger], triggered at [COORD(loc)]", INVESTIGATE_BOMB) + if(reagents) + reagents.set_reagent_temp(1000) //uh-oh + qdel(src) + +/obj/structure/reagent_dispensers/fueltank/blob_act(obj/structure/blob/B) + boom() + +/obj/structure/reagent_dispensers/fueltank/ex_act() + boom() + +/obj/structure/reagent_dispensers/fueltank/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume, global_overlay = TRUE) + ..() + boom() + +/obj/structure/reagent_dispensers/fueltank/tesla_act() + ..() //extend the zap + boom() + +/obj/structure/reagent_dispensers/fueltank/examine(mob/user) + . = ..() + if(get_dist(user, src) <= 2 && rig) + . += "There is some kind of device rigged to the tank." + +/obj/structure/reagent_dispensers/fueltank/attack_hand() + if(rig) + usr.visible_message("[usr] begins to detach [rig] from [src].", "You begin to detach [rig] from [src].") + if(do_after(usr, 20, target = src)) + usr.visible_message("[usr] detaches [rig] from [src].", "You detach [rig] from [src].") + rig.forceMove(get_turf(usr)) + rig = null + lastrigger = null + overlays.Cut() + +/obj/structure/reagent_dispensers/fueltank/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/assembly_holder) && accepts_rig) + if(rig) + to_chat(user, "There is another device in the way.") + return ..() + user.visible_message("[user] begins rigging [I] to [src].", "You begin rigging [I] to [src]") + if(do_after(user, 20, target = src)) + user.visible_message("[user] rigs [I] to [src].", "You rig [I] to [src].") + + var/obj/item/assembly_holder/H = I + if(istype(H.a_left, /obj/item/assembly/igniter) || istype(H.a_right, /obj/item/assembly/igniter)) + msg_admin_attack("[key_name_admin(user)] rigged [src.name] with [I.name] for explosion (JMP)", ATKLOG_FEW) + log_game("[key_name(user)] rigged [src.name] with [I.name] for explosion at [COORD(loc)]") + investigate_log("[key_name(user)] rigged [src.name] with [I.name] for explosion at [COORD(loc)]", INVESTIGATE_BOMB) + + lastrigger = "[key_name(user)]" + rig = H + user.drop_item() + H.forceMove(src) + + var/icon/test = getFlatIcon(H) + test.Shift(NORTH, 1) + test.Shift(EAST, 6) + overlays += test + else + return ..() + +obj/structure/reagent_dispensers/fueltank/welder_act(mob/user, obj/item/I) + . = TRUE + if(!reagents.has_reagent("fuel")) + to_chat(user, "[src] is out of fuel!") + return + if(I.tool_enabled && I.use_tool(src, user, volume = I.tool_volume)) //check it's enabled first to prevent duplicate messages when refuelling + user.visible_message("[user] catastrophically fails at refilling [user.p_their()] [I]!", "That was stupid of you.") + message_admins("[key_name_admin(user)] triggered a fueltank explosion at [COORD(loc)]") + log_game("[key_name(user)] triggered a fueltank explosion at [COORD(loc)]") + investigate_log("[key_name(user)] triggered a fueltank explosion at [COORD(loc)]", INVESTIGATE_BOMB) + boom() + else + I.refill(user, src, reagents.get_reagent_amount("fuel")) //Try dump all fuel into the welder + + +/obj/structure/reagent_dispensers/fueltank/Move() + ..() + if(rig) + rig.process_movement() + +/obj/structure/reagent_dispensers/fueltank/HasProximity(atom/movable/AM) + if(rig) + rig.HasProximity(AM) + +/obj/structure/reagent_dispensers/fueltank/Crossed(atom/movable/AM, oldloc) + if(rig) + rig.Crossed(AM, oldloc) + +/obj/structure/reagent_dispensers/fueltank/hear_talk(mob/living/M, list/message_pieces) + if(rig) + rig.hear_talk(M, message_pieces) + +/obj/structure/reagent_dispensers/fueltank/hear_message(mob/living/M, msg) + if(rig) + rig.hear_message(M, msg) + +/obj/structure/reagent_dispensers/fueltank/Bump() + ..() + if(rig) + rig.process_movement() + + +/obj/structure/reagent_dispensers/peppertank + name = "pepper spray refiller" + desc = "Contains condensed capsaicin for use in law \"enforcement.\"" + icon_state = "pepper" + anchored = 1 + density = 0 + reagent_id = "condensedcapsaicin" + +/obj/structure/reagent_dispensers/water_cooler + name = "liquid cooler" + desc = "A machine that dispenses liquid to drink." + icon = 'icons/obj/vending.dmi' + icon_state = "water_cooler" + anchored = 1 + tank_volume = 500 + reagent_id = "water" + var/paper_cups = 25 //Paper cups left from the cooler + +/obj/structure/reagent_dispensers/water_cooler/examine(mob/user) + . = ..() + if(get_dist(user, src) <= 2) + . += "There are [paper_cups ? paper_cups : "no"] paper cups left." + +/obj/structure/reagent_dispensers/water_cooler/attack_hand(mob/living/user) + if(!paper_cups) + to_chat(user, "There aren't any cups left!") + return + user.visible_message("[user] takes a cup from [src].", "You take a paper cup from [src].") + var/obj/item/reagent_containers/food/drinks/sillycup/S = new(get_turf(src)) + user.put_in_hands(S) + paper_cups-- + +/obj/structure/reagent_dispensers/water_cooler/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + default_unfasten_wrench(user, I, 40) + +/obj/structure/reagent_dispensers/beerkeg + name = "beer keg" + desc = "Beer is liquid bread, it's good for you..." + icon_state = "beer" + reagent_id = "beer" + +/obj/structure/reagent_dispensers/beerkeg/blob_act(obj/structure/blob/B) + explosion(loc, 0, 3, 5, 7, 10) + if(!QDELETED(src)) + qdel(src) + +/obj/structure/reagent_dispensers/beerkeg/nuke + name = "Nanotrasen-brand nuclear fission explosive" + desc = "One of the more successful achievements of the Nanotrasen Corporate Warfare Division, their nuclear fission explosives are renowned for being cheap\ + to produce and devestatingly effective. Signs explain that though this is just a model, every Nanotrasen station is equipped with one, just in case. \ + All Captains carefully guard the disk needed to detonate them - at least, the sign says they do. There seems to be a tap on the back." + icon = 'icons/obj/stationobjs.dmi' + icon_state = "nuclearbomb0" + +/obj/structure/reagent_dispensers/virusfood + name = "virus food dispenser" + desc = "A dispenser of low-potency virus mutagenic." + icon_state = "virus_food" + anchored = 1 + density = 0 + reagent_id = "virusfood" + +/obj/structure/reagent_dispensers/spacecleanertank + name = "space cleaner refiller" + desc = "Refills space cleaner bottles." + icon_state = "cleaner" + anchored = 1 + density = 0 + tank_volume = 5000 + reagent_id = "cleaner" + +/obj/structure/reagent_dispensers/fueltank/chem + icon_state = "fuel_chem" + anchored = 1 + density = 0 + accepts_rig = 0 + tank_volume = 1000 diff --git a/code/modules/recycling/conveyor2.dm b/code/modules/recycling/conveyor2.dm index 933f223d120..e82db0ddc60 100644 --- a/code/modules/recycling/conveyor2.dm +++ b/code/modules/recycling/conveyor2.dm @@ -1,481 +1,492 @@ -#define DIRECTION_FORWARDS 1 -#define DIRECTION_OFF 0 -#define DIRECTION_REVERSED -1 -#define IS_OPERATING (operating && can_conveyor_run()) - -GLOBAL_LIST_INIT(conveyor_belts, list()) //Saves us having to look through the entire machines list for our things -GLOBAL_LIST_INIT(conveyor_switches, list()) - -//conveyor2 is pretty much like the original, except it supports corners, but not diverters. -//Except this is pretty heavily modified so it's more like conveyor2.5 - -/obj/machinery/conveyor - icon = 'icons/obj/recycling.dmi' - icon_state = "conveyor_stopped_cw" - name = "conveyor belt" - desc = "It's a conveyor belt, commonly used to transport large numbers of items elsewhere quite quickly." - layer = CONVEYOR_LAYER // so they appear under stuff but not below stuff like vents - anchored = TRUE - move_force = MOVE_FORCE_DEFAULT - var/operating = FALSE //NB: this can be TRUE while the belt doesn't go - var/forwards // The direction the conveyor sends you in - var/backwards // hopefully self-explanatory - var/clockwise = TRUE // For corner pieces - do we go clockwise or counterclockwise? - var/operable = TRUE // Can this belt actually go? - var/list/affecting // the list of all items that will be moved this ptick - var/reversed = FALSE // set to TRUE to have the conveyor belt be reversed - var/id //ID of the connected lever - - // create a conveyor -/obj/machinery/conveyor/New(loc, new_dir, new_id) - ..(loc) - GLOB.conveyor_belts += src - if(new_id) - id = new_id - if(new_dir) - dir = new_dir - update_move_direction() - for(var/I in GLOB.conveyor_switches) - var/obj/machinery/conveyor_switch/S = I - if(id == S.id) - S.conveyors += src - -/obj/machinery/conveyor/Destroy() - GLOB.conveyor_belts -= src - return ..() - -/obj/machinery/conveyor/setDir(newdir) - . = ..() - update_move_direction() - -// attack with item, place item on conveyor -/obj/machinery/conveyor/attackby(obj/item/I, mob/user) - if(iscrowbar(I)) - if(!(stat & BROKEN)) - var/obj/item/conveyor_construct/C = new(loc) - C.id = id - transfer_fingerprints_to(C) - playsound(loc, I.usesound, 50, 1) - to_chat(user,"You remove the conveyor belt.") - qdel(src) - else if(stat & BROKEN) - return ..() - else if(iswrench(I)) - set_rotation(user) - update_move_direction() - playsound(loc, I.usesound, 50, 1) - else if(istype(I, /obj/item/conveyor_switch_construct)) - var/obj/item/conveyor_switch_construct/S = I - if(S.id == id) - return ..() - for(var/obj/machinery/conveyor_switch/CS in GLOB.conveyor_switches) - if(CS.id == id) - CS.conveyors -= src - id = S.id - to_chat(user, "You link [I] with [src].") - else if(user.a_intent != INTENT_HARM) - if(user.drop_item()) - I.forceMove(loc) - else - return ..() - -// attack with hand, move pulled object onto conveyor -/obj/machinery/conveyor/attack_hand(mob/user as mob) - user.Move_Pulled(src) - -/obj/machinery/conveyor/update_icon() - ..() - if(IS_OPERATING) - icon_state = "conveyor_started_[clockwise ? "cw" : "ccw"]" - if(reversed) - icon_state += "_r" - else - icon_state = "conveyor_stopped_[clockwise ? "cw" : "ccw"]" - -/obj/machinery/conveyor/proc/update_move_direction() - update_icon() - switch(dir) - if(NORTH) - forwards = NORTH - backwards = SOUTH - if(EAST) - forwards = EAST - backwards = WEST - if(SOUTH) - forwards = SOUTH - backwards = NORTH - if(WEST) - forwards = WEST - backwards = EAST - if(NORTHEAST) - forwards = clockwise ? EAST : NORTH - backwards = clockwise ? SOUTH : WEST - if(SOUTHEAST) - forwards = clockwise ? SOUTH : EAST - backwards = clockwise ? WEST : NORTH - if(SOUTHWEST) - forwards = clockwise ? WEST : SOUTH - backwards = clockwise ? NORTH : EAST - if(NORTHWEST) - forwards = clockwise ? NORTH : WEST - backwards = clockwise ? EAST : SOUTH - if(!reversed) - return - var/temporary_direction = forwards - forwards = backwards - backwards = temporary_direction - -/obj/machinery/conveyor/proc/set_rotation(mob/user) - dir = turn(reversed ? backwards : forwards, -90) //Fuck it, let's do it this way instead of doing something clever with dir - var/turf/left = get_step(src, turn(dir, 90)) //We need to get conveyors to the right, left, and behind this one to be able to determine if we need to make a corner piece - var/turf/right = get_step(src, turn(dir, -90)) - var/turf/back = get_step(src, turn(dir, 180)) - to_chat(user, "You rotate [src].") - var/obj/machinery/conveyor/CL = locate() in left - var/obj/machinery/conveyor/CR = locate() in right - var/obj/machinery/conveyor/CB = locate() in back - var/link_to_left = FALSE - var/link_to_right = FALSE - var/link_to_back = FALSE - if(CL) - if(CL.id == id && get_step(CL, CL.reversed ? CL.backwards : CL.forwards) == loc) - link_to_left = TRUE - if(CR) - if(CR.id == id && get_step(CR, CR.reversed ? CR.backwards : CR.forwards) == loc) - link_to_right = TRUE - if(CB) - if(CB.id == id && get_step(CB, CB.reversed ? CB.backwards : CB.forwards) == loc) - link_to_back = TRUE - if(link_to_back) //Don't need to do anything because we can assume the conveyor carries on in a line - return - else if(!(link_to_left ^ link_to_right)) //Either no valid conveyors point here, or two point here (making a "junction" with this belt as the middle piece). Either way we don't need a corner - return - if(link_to_right) - dir = turn(dir, 45) - clockwise = TRUE - else if(link_to_left) - dir = turn(dir, -45) - clockwise = FALSE - -/obj/machinery/conveyor/power_change() - ..() - update_icon() - -/obj/machinery/conveyor/process() - if(!IS_OPERATING) - return - use_power(100) - affecting = loc.contents - src // moved items will be all in loc - var/still_stuff_to_move = FALSE - for(var/atom/movable/AM in affecting) - if(AM.anchored) - continue - still_stuff_to_move = TRUE - addtimer(CALLBACK(src, .proc/move_thing, AM), 1) - CHECK_TICK - if(!still_stuff_to_move && speed_process) - makeNormalProcess() - else if(still_stuff_to_move && !speed_process) - makeSpeedProcess() - -/obj/machinery/conveyor/Crossed(atom/movable/AM, oldloc) - if(!speed_process && !AM.anchored) - makeSpeedProcess() - ..() - -/obj/machinery/conveyor/proc/move_thing(atom/movable/AM) - if(move_force < (AM.move_resist)) - return FALSE - if(!AM.anchored && AM.loc == loc) - step(AM, forwards) - - -/obj/machinery/conveyor/proc/can_conveyor_run() - if(stat & BROKEN) - return FALSE - else if(stat & NOPOWER) - return FALSE - else if(!operable) - return FALSE - return TRUE - -// make the conveyor broken and propagate inoperability to any connected conveyor with the same conveyor datum -/obj/machinery/conveyor/proc/make_broken() - stat |= BROKEN - operable = FALSE - update_icon() - var/obj/machinery/conveyor/C = locate() in get_step(src, forwards) - if(C) - C.set_operable(TRUE, id, FALSE) - C = locate() in get_step(src, backwards) - if(C) - C.set_operable(FALSE, id, FALSE) - -/obj/machinery/conveyor/proc/set_operable(propagate_forwards, match_id, op) //Sets a conveyor inoperable if ID matches it, and propagates forwards / backwards - if(id != match_id) - return - operable = op - update_icon() - var/obj/machinery/conveyor/C = locate() in get_step(src, propagate_forwards ? forwards : backwards) - if(C) - C.set_operable(propagate_forwards ? TRUE : FALSE, id, op) - -// the conveyor control switch - -/obj/machinery/conveyor_switch - name = "conveyor switch" - desc = "This switch controls any and all conveyor belts it is linked to." - icon = 'icons/obj/recycling.dmi' - icon_state = "switch-off" - var/position = DIRECTION_OFF - var/reversed = TRUE - var/one_way = FALSE // Do we go in one direction? - anchored = TRUE - var/id - var/list/conveyors = list() - -/obj/machinery/conveyor_switch/New(newloc, new_id) - ..(newloc) - GLOB.conveyor_switches += src - if(!id) - id = new_id - for(var/I in GLOB.conveyor_belts) - var/obj/machinery/conveyor/C = I - if(C.id == id) - conveyors += C - -/obj/machinery/conveyor_switch/Destroy() - GLOB.conveyor_switches -= src - return ..() - -// update the icon depending on the position - -/obj/machinery/conveyor_switch/update_icon() - overlays.Cut() - if(!position) - icon_state = "switch-off" - else if(position == DIRECTION_REVERSED) - icon_state = "switch-rev" - if(!(stat & NOPOWER)) - overlays += "redlight" - else if(position == DIRECTION_FORWARDS) - icon_state = "switch-fwd" - if(!(stat & NOPOWER)) - overlays += "greenlight" - -/obj/machinery/conveyor_switch/oneway - one_way = TRUE - -// attack with hand, switch position -/obj/machinery/conveyor_switch/attack_hand(mob/user) - if(..()) - return TRUE - toggle(user) - -/obj/machinery/conveyor_switch/attack_ghost(mob/user) - if(user.can_advanced_admin_interact()) - toggle(user) - -/obj/machinery/conveyor_switch/proc/toggle(mob/user) - add_fingerprint(user) - if(!allowed(user) && !user.can_advanced_admin_interact()) //this is in Para but not TG. I don't think there's any which are set anyway. - to_chat(user, "Access denied.") - return - if(position) - position = DIRECTION_OFF - else - reversed = one_way ? FALSE : !reversed - position = reversed ? DIRECTION_REVERSED : DIRECTION_FORWARDS - update_icon() - for(var/obj/machinery/conveyor/C in conveyors) - C.operating = abs(position) - if(C.reversed != reversed) - C.reversed = reversed - C.update_move_direction() - else - C.update_icon() - CHECK_TICK - for(var/I in GLOB.conveyor_switches) // find any switches with same id as this one, and set their positions to match us - var/obj/machinery/conveyor_switch/S = I - if(S == src || S.id != id) - continue - S.position = position - S.one_way = one_way //Break everything!!1! - S.reversed = reversed - S.update_icon() - CHECK_TICK - -/obj/machinery/conveyor_switch/attackby(obj/item/I, mob/user) - if(iscrowbar(I)) - var/obj/item/conveyor_switch_construct/C = new(loc, id) - transfer_fingerprints_to(C) - to_chat(user,"You detach the conveyor switch.") - qdel(src) - else if(ismultitool(I)) - one_way = !one_way - to_chat(user, "[src] will now go [one_way ? "forwards only" : "both forwards and backwards"].") - else - return ..() - -/obj/machinery/conveyor_switch/power_change() - ..() - update_icon() - -// CONVEYOR CONSTRUCTION STARTS HERE - -/obj/item/conveyor_construct - icon = 'icons/obj/recycling.dmi' - icon_state = "conveyor_loose" - name = "conveyor belt assembly" - desc = "A conveyor belt assembly, used for the assembly of conveyor belt systems." - w_class = WEIGHT_CLASS_BULKY - var/id - -/obj/item/conveyor_construct/attackby(obj/item/I, mob/user, params) - ..() - if(!istype(I, /obj/item/conveyor_switch_construct)) - return - var/obj/item/conveyor_switch_construct/C = I - to_chat(user, "You link [src] to [C].") - id = C.id - -/obj/item/conveyor_construct/afterattack(turf/T, mob/user, proximity) - if(!proximity) - return - if(user.incapacitated()) - return - if(!istype(T, /turf/simulated/floor)) - return - if(T == get_turf(user)) - to_chat(user, "You cannot place [src] under yourself.") - return - if(locate(/obj/machinery/conveyor) in T) //Can't put conveyors beneath conveyors - to_chat(user, "There's already a conveyor there!") - return - var/obj/machinery/conveyor/C = new(T, user.dir, id) - transfer_fingerprints_to(C) - qdel(src) - -/obj/item/conveyor_switch_construct - name = "conveyor switch assembly" - desc = "A conveyor control switch assembly. When set up, it'll control any and all conveyor belts it is linked to." - icon = 'icons/obj/recycling.dmi' - icon_state = "switch" - w_class = WEIGHT_CLASS_BULKY - var/id - -/obj/item/conveyor_switch_construct/New(loc, new_id) - ..(loc) - if(new_id) - id = new_id - else - id = world.time + rand() //this couldn't possibly go wrong - - -/obj/item/conveyor_switch_construct/afterattack(turf/T, mob/user, proximity) - if(!proximity) - return - if(user.incapacitated()) - return - if(!istype(T, /turf/simulated/floor)) - return - var/found = FALSE - for(var/obj/machinery/conveyor/C in view()) - if(C.id == id) - found = TRUE - break - if(!found) - to_chat(user, "[src] did not detect any linked conveyor belts in range.") - return - var/obj/machinery/conveyor_switch/NC = new(T, id) - transfer_fingerprints_to(NC) - qdel(src) - -/obj/item/conveyor_switch_construct/attackby(obj/item/I, mob/user) - if(!istype(I, /obj/item/conveyor_switch_construct)) - return ..() - var/obj/item/conveyor_switch_construct/S = I - id = S.id - to_chat(user, "You link the two switch constructs.") - -/obj/item/paper/conveyor - name = "paper- 'Nano-it-up U-build series, #9: Build your very own conveyor belt, in SPACE'" - info = "

        Congratulations!

        You are now the proud owner of the best conveyor set available for space mail order! \ - We at Nano-it-up know you love to prepare your own structures without wasting time, so we have devised a special streamlined \ - assembly procedure that puts all other mail-order products to shame!

        \ -

        Firstly, you need to link the conveyor switch assembly to each of the conveyor belt assemblies. After doing so, you simply need to install the belt \ - assemblies onto the floor, et voila, belt built. Our special Nano-it-up smart switch will detected any linked assemblies as far as the eye can see!

        \ -

        Set single directional switches by using your multitool on the switch after you've installed the switch assembly.

        \ -

        This convenience, you can only have it when you Nano-it-up. Stay nano!

        " - -/obj/machinery/conveyor/counterclockwise - clockwise = FALSE - icon_state = "conveyor_stopped_ccw" - -/obj/machinery/conveyor/auto/New(loc, newdir) - ..(loc, newdir) - operating = TRUE - update_icon() - -//Other types of conveyor, mostly for saving yourself a headache during mapping - -/obj/machinery/conveyor/north - dir = NORTH - -/obj/machinery/conveyor/northeast - dir = NORTHEAST - -/obj/machinery/conveyor/east - dir = EAST - -/obj/machinery/conveyor/southeast - dir = SOUTHEAST - -/obj/machinery/conveyor/south - dir = SOUTH - -/obj/machinery/conveyor/southwest - dir = SOUTHWEST - -/obj/machinery/conveyor/west - dir = WEST - -/obj/machinery/conveyor/northwest - dir = NORTHWEST - -/obj/machinery/conveyor/north/ccw - icon_state = "conveyor_stopped_ccw" - clockwise = FALSE - -/obj/machinery/conveyor/northeast/ccw - icon_state = "conveyor_stopped_ccw" - clockwise = FALSE - -/obj/machinery/conveyor/east/ccw - icon_state = "conveyor_stopped_ccw" - clockwise = FALSE - -/obj/machinery/conveyor/southeast/ccw - icon_state = "conveyor_stopped_ccw" - clockwise = FALSE - -/obj/machinery/conveyor/south/ccw - icon_state = "conveyor_stopped_ccw" - clockwise = FALSE - -/obj/machinery/conveyor/southwest/ccw - icon_state = "conveyor_stopped_ccw" - clockwise = FALSE - -/obj/machinery/conveyor/west/ccw - icon_state = "conveyor_stopped_ccw" - clockwise = FALSE - -/obj/machinery/conveyor/northwest/ccw - icon_state = "conveyor_stopped_ccw" - clockwise = FALSE - -#undef DIRECTION_FORWARDS -#undef DIRECTION_OFF -#undef DIRECTION_REVERSED -#undef IS_OPERATING +#define DIRECTION_FORWARDS 1 +#define DIRECTION_OFF 0 +#define DIRECTION_REVERSED -1 +#define IS_OPERATING (operating && can_conveyor_run()) + +GLOBAL_LIST_INIT(conveyor_belts, list()) //Saves us having to look through the entire machines list for our things +GLOBAL_LIST_INIT(conveyor_switches, list()) + +//conveyor2 is pretty much like the original, except it supports corners, but not diverters. +//Except this is pretty heavily modified so it's more like conveyor2.5 + +/obj/machinery/conveyor + icon = 'icons/obj/recycling.dmi' + icon_state = "conveyor_stopped_cw" + name = "conveyor belt" + desc = "It's a conveyor belt, commonly used to transport large numbers of items elsewhere quite quickly." + layer = CONVEYOR_LAYER // so they appear under stuff but not below stuff like vents + anchored = TRUE + move_force = MOVE_FORCE_DEFAULT + var/operating = FALSE //NB: this can be TRUE while the belt doesn't go + var/forwards // The direction the conveyor sends you in + var/backwards // hopefully self-explanatory + var/clockwise = TRUE // For corner pieces - do we go clockwise or counterclockwise? + var/operable = TRUE // Can this belt actually go? + var/list/affecting // the list of all items that will be moved this ptick + var/reversed = FALSE // set to TRUE to have the conveyor belt be reversed + var/id //ID of the connected lever + + // create a conveyor +/obj/machinery/conveyor/New(loc, new_dir, new_id) + ..(loc) + GLOB.conveyor_belts += src + if(new_id) + id = new_id + if(new_dir) + dir = new_dir + update_move_direction() + for(var/I in GLOB.conveyor_switches) + var/obj/machinery/conveyor_switch/S = I + if(id == S.id) + S.conveyors += src + +/obj/machinery/conveyor/Destroy() + GLOB.conveyor_belts -= src + return ..() + +/obj/machinery/conveyor/setDir(newdir) + . = ..() + update_move_direction() + +// attack with item, place item on conveyor +/obj/machinery/conveyor/attackby(obj/item/I, mob/user) + if(stat & BROKEN) + return ..() + else if(istype(I, /obj/item/conveyor_switch_construct)) + var/obj/item/conveyor_switch_construct/S = I + if(S.id == id) + return ..() + for(var/obj/machinery/conveyor_switch/CS in GLOB.conveyor_switches) + if(CS.id == id) + CS.conveyors -= src + id = S.id + to_chat(user, "You link [I] with [src].") + else if(user.a_intent != INTENT_HARM) + if(user.drop_item()) + I.forceMove(loc) + else + return ..() + + +/obj/machinery/conveyor/crowbar_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(!(stat & BROKEN)) + var/obj/item/conveyor_construct/C = new(loc) + C.id = id + transfer_fingerprints_to(C) + to_chat(user,"You remove [src].") + qdel(src) + +/obj/machinery/conveyor/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + set_rotation(user) + update_move_direction() + +// attack with hand, move pulled object onto conveyor +/obj/machinery/conveyor/attack_hand(mob/user as mob) + user.Move_Pulled(src) + +/obj/machinery/conveyor/update_icon() + ..() + if(IS_OPERATING) + icon_state = "conveyor_started_[clockwise ? "cw" : "ccw"]" + if(reversed) + icon_state += "_r" + else + icon_state = "conveyor_stopped_[clockwise ? "cw" : "ccw"]" + +/obj/machinery/conveyor/proc/update_move_direction() + update_icon() + switch(dir) + if(NORTH) + forwards = NORTH + backwards = SOUTH + if(EAST) + forwards = EAST + backwards = WEST + if(SOUTH) + forwards = SOUTH + backwards = NORTH + if(WEST) + forwards = WEST + backwards = EAST + if(NORTHEAST) + forwards = clockwise ? EAST : NORTH + backwards = clockwise ? SOUTH : WEST + if(SOUTHEAST) + forwards = clockwise ? SOUTH : EAST + backwards = clockwise ? WEST : NORTH + if(SOUTHWEST) + forwards = clockwise ? WEST : SOUTH + backwards = clockwise ? NORTH : EAST + if(NORTHWEST) + forwards = clockwise ? NORTH : WEST + backwards = clockwise ? EAST : SOUTH + if(!reversed) + return + var/temporary_direction = forwards + forwards = backwards + backwards = temporary_direction + +/obj/machinery/conveyor/proc/set_rotation(mob/user) + dir = turn(reversed ? backwards : forwards, -90) //Fuck it, let's do it this way instead of doing something clever with dir + var/turf/left = get_step(src, turn(dir, 90)) //We need to get conveyors to the right, left, and behind this one to be able to determine if we need to make a corner piece + var/turf/right = get_step(src, turn(dir, -90)) + var/turf/back = get_step(src, turn(dir, 180)) + to_chat(user, "You rotate [src].") + var/obj/machinery/conveyor/CL = locate() in left + var/obj/machinery/conveyor/CR = locate() in right + var/obj/machinery/conveyor/CB = locate() in back + var/link_to_left = FALSE + var/link_to_right = FALSE + var/link_to_back = FALSE + if(CL) + if(CL.id == id && get_step(CL, CL.reversed ? CL.backwards : CL.forwards) == loc) + link_to_left = TRUE + if(CR) + if(CR.id == id && get_step(CR, CR.reversed ? CR.backwards : CR.forwards) == loc) + link_to_right = TRUE + if(CB) + if(CB.id == id && get_step(CB, CB.reversed ? CB.backwards : CB.forwards) == loc) + link_to_back = TRUE + if(link_to_back) //Don't need to do anything because we can assume the conveyor carries on in a line + return + else if(!(link_to_left ^ link_to_right)) //Either no valid conveyors point here, or two point here (making a "junction" with this belt as the middle piece). Either way we don't need a corner + return + if(link_to_right) + dir = turn(dir, 45) + clockwise = TRUE + else if(link_to_left) + dir = turn(dir, -45) + clockwise = FALSE + +/obj/machinery/conveyor/power_change() + ..() + update_icon() + +/obj/machinery/conveyor/process() + if(!IS_OPERATING) + return + use_power(100) + affecting = loc.contents - src // moved items will be all in loc + var/still_stuff_to_move = FALSE + for(var/atom/movable/AM in affecting) + if(AM.anchored) + continue + still_stuff_to_move = TRUE + addtimer(CALLBACK(src, .proc/move_thing, AM), 1) + CHECK_TICK + if(!still_stuff_to_move && speed_process) + makeNormalProcess() + else if(still_stuff_to_move && !speed_process) + makeSpeedProcess() + +/obj/machinery/conveyor/Crossed(atom/movable/AM, oldloc) + if(!speed_process && !AM.anchored) + makeSpeedProcess() + ..() + +/obj/machinery/conveyor/proc/move_thing(atom/movable/AM) + if(move_force < (AM.move_resist)) + return FALSE + if(!AM.anchored && AM.loc == loc) + step(AM, forwards) + + +/obj/machinery/conveyor/proc/can_conveyor_run() + if(stat & BROKEN) + return FALSE + else if(stat & NOPOWER) + return FALSE + else if(!operable) + return FALSE + return TRUE + +// make the conveyor broken and propagate inoperability to any connected conveyor with the same conveyor datum +/obj/machinery/conveyor/proc/make_broken() + stat |= BROKEN + operable = FALSE + update_icon() + var/obj/machinery/conveyor/C = locate() in get_step(src, forwards) + if(C) + C.set_operable(TRUE, id, FALSE) + C = locate() in get_step(src, backwards) + if(C) + C.set_operable(FALSE, id, FALSE) + +/obj/machinery/conveyor/proc/set_operable(propagate_forwards, match_id, op) //Sets a conveyor inoperable if ID matches it, and propagates forwards / backwards + if(id != match_id) + return + operable = op + update_icon() + var/obj/machinery/conveyor/C = locate() in get_step(src, propagate_forwards ? forwards : backwards) + if(C) + C.set_operable(propagate_forwards ? TRUE : FALSE, id, op) + +// the conveyor control switch + +/obj/machinery/conveyor_switch + name = "conveyor switch" + desc = "This switch controls any and all conveyor belts it is linked to." + icon = 'icons/obj/recycling.dmi' + icon_state = "switch-off" + var/position = DIRECTION_OFF + var/reversed = TRUE + var/one_way = FALSE // Do we go in one direction? + anchored = TRUE + var/id + var/list/conveyors = list() + +/obj/machinery/conveyor_switch/New(newloc, new_id) + ..(newloc) + GLOB.conveyor_switches += src + if(!id) + id = new_id + for(var/I in GLOB.conveyor_belts) + var/obj/machinery/conveyor/C = I + if(C.id == id) + conveyors += C + +/obj/machinery/conveyor_switch/Destroy() + GLOB.conveyor_switches -= src + return ..() + +// update the icon depending on the position + +/obj/machinery/conveyor_switch/update_icon() + overlays.Cut() + if(!position) + icon_state = "switch-off" + else if(position == DIRECTION_REVERSED) + icon_state = "switch-rev" + if(!(stat & NOPOWER)) + overlays += "redlight" + else if(position == DIRECTION_FORWARDS) + icon_state = "switch-fwd" + if(!(stat & NOPOWER)) + overlays += "greenlight" + +/obj/machinery/conveyor_switch/oneway + one_way = TRUE + +// attack with hand, switch position +/obj/machinery/conveyor_switch/attack_hand(mob/user) + if(..()) + return TRUE + toggle(user) + +/obj/machinery/conveyor_switch/attack_ghost(mob/user) + if(user.can_advanced_admin_interact()) + toggle(user) + +/obj/machinery/conveyor_switch/proc/toggle(mob/user) + add_fingerprint(user) + if(!allowed(user) && !user.can_advanced_admin_interact()) //this is in Para but not TG. I don't think there's any which are set anyway. + to_chat(user, "Access denied.") + return + if(position) + position = DIRECTION_OFF + else + reversed = one_way ? FALSE : !reversed + position = reversed ? DIRECTION_REVERSED : DIRECTION_FORWARDS + update_icon() + for(var/obj/machinery/conveyor/C in conveyors) + C.operating = abs(position) + if(C.reversed != reversed) + C.reversed = reversed + C.update_move_direction() + else + C.update_icon() + CHECK_TICK + for(var/I in GLOB.conveyor_switches) // find any switches with same id as this one, and set their positions to match us + var/obj/machinery/conveyor_switch/S = I + if(S == src || S.id != id) + continue + S.position = position + S.one_way = one_way //Break everything!!1! + S.reversed = reversed + S.update_icon() + CHECK_TICK + +/obj/machinery/conveyor_switch/crowbar_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + var/obj/item/conveyor_switch_construct/C = new(loc, id) + transfer_fingerprints_to(C) + to_chat(user,"You detach [src].") + qdel(src) + +/obj/machinery/conveyor_switch/multitool_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + one_way = !one_way + to_chat(user, "[src] will now go [one_way ? "forwards only" : "both forwards and backwards"].") + +/obj/machinery/conveyor_switch/power_change() + ..() + update_icon() + +// CONVEYOR CONSTRUCTION STARTS HERE + +/obj/item/conveyor_construct + icon = 'icons/obj/recycling.dmi' + icon_state = "conveyor_loose" + name = "conveyor belt assembly" + desc = "A conveyor belt assembly, used for the assembly of conveyor belt systems." + w_class = WEIGHT_CLASS_BULKY + var/id + +/obj/item/conveyor_construct/attackby(obj/item/I, mob/user, params) + ..() + if(!istype(I, /obj/item/conveyor_switch_construct)) + return + var/obj/item/conveyor_switch_construct/C = I + to_chat(user, "You link [src] to [C].") + id = C.id + +/obj/item/conveyor_construct/afterattack(turf/T, mob/user, proximity) + if(!proximity) + return + if(user.incapacitated()) + return + if(!istype(T, /turf/simulated/floor)) + return + if(T == get_turf(user)) + to_chat(user, "You cannot place [src] under yourself.") + return + if(locate(/obj/machinery/conveyor) in T) //Can't put conveyors beneath conveyors + to_chat(user, "There's already a conveyor there!") + return + var/obj/machinery/conveyor/C = new(T, user.dir, id) + transfer_fingerprints_to(C) + qdel(src) + +/obj/item/conveyor_switch_construct + name = "conveyor switch assembly" + desc = "A conveyor control switch assembly. When set up, it'll control any and all conveyor belts it is linked to." + icon = 'icons/obj/recycling.dmi' + icon_state = "switch" + w_class = WEIGHT_CLASS_BULKY + var/id + +/obj/item/conveyor_switch_construct/New(loc, new_id) + ..(loc) + if(new_id) + id = new_id + else + id = world.time + rand() //this couldn't possibly go wrong + + +/obj/item/conveyor_switch_construct/afterattack(turf/T, mob/user, proximity) + if(!proximity) + return + if(user.incapacitated()) + return + if(!istype(T, /turf/simulated/floor)) + return + var/found = FALSE + for(var/obj/machinery/conveyor/C in view()) + if(C.id == id) + found = TRUE + break + if(!found) + to_chat(user, "[src] did not detect any linked conveyor belts in range.") + return + var/obj/machinery/conveyor_switch/NC = new(T, id) + transfer_fingerprints_to(NC) + qdel(src) + +/obj/item/conveyor_switch_construct/attackby(obj/item/I, mob/user) + if(!istype(I, /obj/item/conveyor_switch_construct)) + return ..() + var/obj/item/conveyor_switch_construct/S = I + id = S.id + to_chat(user, "You link the two switch constructs.") + +/obj/item/paper/conveyor + name = "paper- 'Nano-it-up U-build series, #9: Build your very own conveyor belt, in SPACE'" + info = "

        Congratulations!

        You are now the proud owner of the best conveyor set available for space mail order! \ + We at Nano-it-up know you love to prepare your own structures without wasting time, so we have devised a special streamlined \ + assembly procedure that puts all other mail-order products to shame!

        \ +

        Firstly, you need to link the conveyor switch assembly to each of the conveyor belt assemblies. After doing so, you simply need to install the belt \ + assemblies onto the floor, et voila, belt built. Our special Nano-it-up smart switch will detected any linked assemblies as far as the eye can see!

        \ +

        Set single directional switches by using your multitool on the switch after you've installed the switch assembly.

        \ +

        This convenience, you can only have it when you Nano-it-up. Stay nano!

        " + +/obj/machinery/conveyor/counterclockwise + clockwise = FALSE + icon_state = "conveyor_stopped_ccw" + +/obj/machinery/conveyor/auto/New(loc, newdir) + ..(loc, newdir) + operating = TRUE + update_icon() + +//Other types of conveyor, mostly for saving yourself a headache during mapping + +/obj/machinery/conveyor/north + dir = NORTH + +/obj/machinery/conveyor/northeast + dir = NORTHEAST + +/obj/machinery/conveyor/east + dir = EAST + +/obj/machinery/conveyor/southeast + dir = SOUTHEAST + +/obj/machinery/conveyor/south + dir = SOUTH + +/obj/machinery/conveyor/southwest + dir = SOUTHWEST + +/obj/machinery/conveyor/west + dir = WEST + +/obj/machinery/conveyor/northwest + dir = NORTHWEST + +/obj/machinery/conveyor/north/ccw + icon_state = "conveyor_stopped_ccw" + clockwise = FALSE + +/obj/machinery/conveyor/northeast/ccw + icon_state = "conveyor_stopped_ccw" + clockwise = FALSE + +/obj/machinery/conveyor/east/ccw + icon_state = "conveyor_stopped_ccw" + clockwise = FALSE + +/obj/machinery/conveyor/southeast/ccw + icon_state = "conveyor_stopped_ccw" + clockwise = FALSE + +/obj/machinery/conveyor/south/ccw + icon_state = "conveyor_stopped_ccw" + clockwise = FALSE + +/obj/machinery/conveyor/southwest/ccw + icon_state = "conveyor_stopped_ccw" + clockwise = FALSE + +/obj/machinery/conveyor/west/ccw + icon_state = "conveyor_stopped_ccw" + clockwise = FALSE + +/obj/machinery/conveyor/northwest/ccw + icon_state = "conveyor_stopped_ccw" + clockwise = FALSE + +#undef DIRECTION_FORWARDS +#undef DIRECTION_OFF +#undef DIRECTION_REVERSED +#undef IS_OPERATING diff --git a/code/modules/recycling/disposal-construction.dm b/code/modules/recycling/disposal-construction.dm index 0c1c775c1f9..df29663588f 100644 --- a/code/modules/recycling/disposal-construction.dm +++ b/code/modules/recycling/disposal-construction.dm @@ -1,268 +1,265 @@ -// Disposal pipe construction -// This is the pipe that you drag around, not the attached ones. - -/obj/structure/disposalconstruct - - name = "disposal pipe segment" - desc = "A huge pipe segment used for constructing disposal systems." - icon = 'icons/obj/pipes/disposal.dmi' - icon_state = "conpipe-s" - anchored = 0 - density = 0 - pressure_resistance = 5*ONE_ATMOSPHERE - level = 2 - max_integrity = 200 - var/ptype = PIPE_DISPOSALS_STRAIGHT //Use the defines - var/base_state - var/dpdir = 0 // directions as disposalpipe - -/obj/structure/disposalconstruct/New(loc, pipe_type, direction) - ..() - if(pipe_type) - ptype = pipe_type - if(dir) - dir = direction - update() - - // update iconstate and dpdir due to dir and type -/obj/structure/disposalconstruct/proc/update() - base_state = get_pipe_icon(ptype) - icon_state = "con[base_state]" - var/flip = turn(dir, 180) - var/left = turn(dir, 90) - var/right = turn(dir, -90) - name = get_pipe_name(ptype, PIPETYPE_DISPOSAL) - switch(ptype) - if(PIPE_DISPOSALS_STRAIGHT) - dpdir = dir | flip - if(PIPE_DISPOSALS_BENT) - dpdir = dir | right - if(PIPE_DISPOSALS_JUNCTION_RIGHT) - dpdir = dir | right | flip - if(PIPE_DISPOSALS_JUNCTION_LEFT) - dpdir = dir | left | flip - if(PIPE_DISPOSALS_Y_JUNCTION) - dpdir = dir | left | right - if(PIPE_DISPOSALS_TRUNK) - dpdir = dir - if(PIPE_DISPOSALS_SORT_RIGHT) - dpdir = dir | right | flip - if(PIPE_DISPOSALS_SORT_LEFT) - dpdir = dir | left | flip - // disposal bin has only one dir, thus we don't need to care about setting it - if(PIPE_DISPOSALS_BIN) - if(!anchored) - icon_state = "[base_state]-unanchored" - else - icon_state = base_state - if(PIPE_DISPOSALS_OUTLET) - dpdir = dir - icon_state = base_state - if(PIPE_DISPOSALS_CHUTE) - dpdir = dir - icon_state = base_state - if(!(ptype in list(PIPE_DISPOSALS_BIN, PIPE_DISPOSALS_OUTLET, PIPE_DISPOSALS_CHUTE))) - icon_state = "con[base_state]" - if(invisibility) // if invisible, fade icon - icon -= rgb(0,0,0,128) - -// hide called by levelupdate if turf intact status changes -// change visibility status and force update of icon -/obj/structure/disposalconstruct/hide(var/intact) - invisibility = (intact && level==1) ? 101: 0 // hide if floor is intact - update() - - -// flip and rotate verbs -/obj/structure/disposalconstruct/verb/rotate() - set name = "Rotate Pipe" - set src in view(1) - - if(usr.stat) - return - - if(anchored) - to_chat(usr, "You must unfasten the pipe before rotating it.") - return - - dir = turn(dir, -90) - update() - -/obj/structure/disposalconstruct/AltClick(mob/user) - if(user.incapacitated()) - to_chat(user, "You can't do that right now!") - return - if(!Adjacent(user)) - return - rotate() - -/obj/structure/disposalconstruct/verb/flip() - set name = "Flip Pipe" - set src in view(1) - if(usr.stat) - return - - if(anchored) - to_chat(usr, "You must unfasten the pipe before flipping it.") - return - - dir = turn(dir, 180) - switch(ptype) - if(PIPE_DISPOSALS_JUNCTION_RIGHT) - ptype = PIPE_DISPOSALS_JUNCTION_LEFT - if(PIPE_DISPOSALS_JUNCTION_LEFT) - ptype = PIPE_DISPOSALS_JUNCTION_RIGHT - if(PIPE_DISPOSALS_SORT_RIGHT) - ptype = PIPE_DISPOSALS_SORT_LEFT - if(PIPE_DISPOSALS_SORT_LEFT) - ptype = PIPE_DISPOSALS_SORT_RIGHT - - update() - -// returns the type path of disposalpipe corresponding to this item dtype -/obj/structure/disposalconstruct/proc/dpipetype() - switch(ptype) - if(PIPE_DISPOSALS_STRAIGHT, PIPE_DISPOSALS_BENT) - return /obj/structure/disposalpipe/segment - if(PIPE_DISPOSALS_JUNCTION_RIGHT, PIPE_DISPOSALS_JUNCTION_LEFT, PIPE_DISPOSALS_Y_JUNCTION) - return /obj/structure/disposalpipe/junction - if(PIPE_DISPOSALS_TRUNK) - return /obj/structure/disposalpipe/trunk - if(PIPE_DISPOSALS_BIN) - return /obj/machinery/disposal - if(PIPE_DISPOSALS_OUTLET) - return /obj/structure/disposaloutlet - if(PIPE_DISPOSALS_CHUTE) - return /obj/machinery/disposal/deliveryChute - if(PIPE_DISPOSALS_SORT_RIGHT, PIPE_DISPOSALS_SORT_LEFT) - return /obj/structure/disposalpipe/sortjunction - return - - - -// attackby item -// wrench: (un)anchor -// weldingtool: convert to real pipe - -/obj/structure/disposalconstruct/attackby(var/obj/item/I, var/mob/user, params) - var/nicetype = "pipe" - var/ispipe = 0 // Indicates if we should change the level of this pipe - src.add_fingerprint(user) - switch(ptype) - if(PIPE_DISPOSALS_BIN) - nicetype = "disposal bin" - if(PIPE_DISPOSALS_OUTLET) - nicetype = "disposal outlet" - if(PIPE_DISPOSALS_CHUTE) - nicetype = "delivery chute" - if(PIPE_DISPOSALS_SORT_RIGHT, PIPE_DISPOSALS_SORT_LEFT) - nicetype = "sorting pipe" - ispipe = 1 - else - nicetype = "pipe" - ispipe = 1 - - var/turf/T = src.loc - if(T.intact) - to_chat(user, "You can only attach the [nicetype] if the floor plating is removed.") - return - - if(istype(I, /obj/item/wrench)) - if(anchored) - anchored = 0 - if(ispipe) - level = 2 - density = 0 - else - density = 1 - to_chat(user, "You detach the [nicetype] from the underfloor.") - else - anchored = 1 - if(ispipe) - level = 1 // We don't want disposal bins to disappear under the floors - density = 0 - else - density = 1 // We don't want disposal bins or outlets to go density 0 - to_chat(user, "You attach the [nicetype] to the underfloor.") - playsound(src.loc, I.usesound, 100, 1) - update() - return - - - if(ptype in list(PIPE_DISPOSALS_BIN, PIPE_DISPOSALS_OUTLET, PIPE_DISPOSALS_CHUTE)) // Disposal or outlet - var/obj/structure/disposalpipe/trunk/CP = locate() in T - if(!CP) // There's no trunk - to_chat(user, "The [nicetype] requires a trunk underneath it in order to work.") - return - else - for(var/obj/structure/disposalpipe/CP in T) - if(CP) - update() - var/pdir = CP.dpdir - if(istype(CP, /obj/structure/disposalpipe/broken)) - pdir = CP.dir - if(pdir & dpdir) - to_chat(user, "There is already a [nicetype] at that location.") - return - - if(istype(I, /obj/item/weldingtool)) - if(anchored) - var/obj/item/weldingtool/W = I - if(W.remove_fuel(0,user)) - playsound(src.loc, W.usesound, 100, 1) - to_chat(user, "Welding the [nicetype] in place.") - if(do_after(user, 20 * W.toolspeed, target = src)) - if(!src || !W.isOn()) return - to_chat(user, "The [nicetype] has been welded in place!") - update() // TODO: Make this neat - if(ispipe) // Pipe - - var/pipetype = dpipetype() - var/obj/structure/disposalpipe/P = new pipetype(src.loc) - src.transfer_fingerprints_to(P) - P.base_icon_state = base_state - P.dir = dir - P.dpdir = dpdir - P.update_icon() - - //Needs some special treatment ;) - if(ptype == PIPE_DISPOSALS_SORT_RIGHT || ptype == PIPE_DISPOSALS_SORT_LEFT) - var/obj/structure/disposalpipe/sortjunction/SortP = P - SortP.updatedir() - - else if(ptype == PIPE_DISPOSALS_BIN) // Disposal bin - var/obj/machinery/disposal/P = new /obj/machinery/disposal(src.loc) - src.transfer_fingerprints_to(P) - P.mode = 0 // start with pump off - - else if(ptype == PIPE_DISPOSALS_OUTLET) // Disposal outlet - - var/obj/structure/disposaloutlet/P = new /obj/structure/disposaloutlet(src.loc) - src.transfer_fingerprints_to(P) - P.dir = dir - - else if(ptype==PIPE_DISPOSALS_CHUTE) // Disposal outlet - - var/obj/machinery/disposal/deliveryChute/P = new /obj/machinery/disposal/deliveryChute(src.loc) - src.transfer_fingerprints_to(P) - P.dir = dir - - qdel(src) - return - else - to_chat(user, "You need more welding fuel to complete this task.") - return - else - to_chat(user, "You need to attach it to the plating first!") - return - -/obj/structure/disposalconstruct/rpd_act(mob/user, obj/item/rpd/our_rpd) - . = TRUE - if(our_rpd.mode == RPD_ROTATE_MODE) - rotate() - else if(our_rpd.mode == RPD_FLIP_MODE) - flip() - else if(our_rpd.mode == RPD_DELETE_MODE) - our_rpd.delete_single_pipe(user, src) - else - return ..() +// Disposal pipe construction +// This is the pipe that you drag around, not the attached ones. + +/obj/structure/disposalconstruct + + name = "disposal pipe segment" + desc = "A huge pipe segment used for constructing disposal systems." + icon = 'icons/obj/pipes/disposal.dmi' + icon_state = "conpipe-s" + anchored = 0 + density = 0 + pressure_resistance = 5*ONE_ATMOSPHERE + level = 2 + max_integrity = 200 + var/ptype = PIPE_DISPOSALS_STRAIGHT //Use the defines + var/base_state + var/dpdir = 0 // directions as disposalpipe + +/obj/structure/disposalconstruct/New(loc, pipe_type, direction) + ..() + if(pipe_type) + ptype = pipe_type + if(dir) + dir = direction + update() + + // update iconstate and dpdir due to dir and type +/obj/structure/disposalconstruct/proc/update() + base_state = get_pipe_icon(ptype) + icon_state = "con[base_state]" + var/flip = turn(dir, 180) + var/left = turn(dir, 90) + var/right = turn(dir, -90) + name = get_pipe_name(ptype, PIPETYPE_DISPOSAL) + switch(ptype) + if(PIPE_DISPOSALS_STRAIGHT) + dpdir = dir | flip + if(PIPE_DISPOSALS_BENT) + dpdir = dir | right + if(PIPE_DISPOSALS_JUNCTION_RIGHT) + dpdir = dir | right | flip + if(PIPE_DISPOSALS_JUNCTION_LEFT) + dpdir = dir | left | flip + if(PIPE_DISPOSALS_Y_JUNCTION) + dpdir = dir | left | right + if(PIPE_DISPOSALS_TRUNK) + dpdir = dir + if(PIPE_DISPOSALS_SORT_RIGHT) + dpdir = dir | right | flip + if(PIPE_DISPOSALS_SORT_LEFT) + dpdir = dir | left | flip + // disposal bin has only one dir, thus we don't need to care about setting it + if(PIPE_DISPOSALS_BIN) + if(!anchored) + icon_state = "[base_state]-unanchored" + else + icon_state = base_state + if(PIPE_DISPOSALS_OUTLET) + dpdir = dir + icon_state = base_state + if(PIPE_DISPOSALS_CHUTE) + dpdir = dir + icon_state = base_state + if(!(ptype in list(PIPE_DISPOSALS_BIN, PIPE_DISPOSALS_OUTLET, PIPE_DISPOSALS_CHUTE))) + icon_state = "con[base_state]" + if(invisibility) // if invisible, fade icon + icon -= rgb(0,0,0,128) + +// hide called by levelupdate if turf intact status changes +// change visibility status and force update of icon +/obj/structure/disposalconstruct/hide(var/intact) + invisibility = (intact && level==1) ? 101: 0 // hide if floor is intact + update() + + +// flip and rotate verbs +/obj/structure/disposalconstruct/verb/rotate() + set name = "Rotate Pipe" + set src in view(1) + + if(usr.stat) + return + + if(anchored) + to_chat(usr, "You must unfasten the pipe before rotating it.") + return + + dir = turn(dir, -90) + update() + +/obj/structure/disposalconstruct/AltClick(mob/user) + if(user.incapacitated()) + to_chat(user, "You can't do that right now!") + return + if(!Adjacent(user)) + return + rotate() + +/obj/structure/disposalconstruct/verb/flip() + set name = "Flip Pipe" + set src in view(1) + if(usr.stat) + return + + if(anchored) + to_chat(usr, "You must unfasten the pipe before flipping it.") + return + + dir = turn(dir, 180) + switch(ptype) + if(PIPE_DISPOSALS_JUNCTION_RIGHT) + ptype = PIPE_DISPOSALS_JUNCTION_LEFT + if(PIPE_DISPOSALS_JUNCTION_LEFT) + ptype = PIPE_DISPOSALS_JUNCTION_RIGHT + if(PIPE_DISPOSALS_SORT_RIGHT) + ptype = PIPE_DISPOSALS_SORT_LEFT + if(PIPE_DISPOSALS_SORT_LEFT) + ptype = PIPE_DISPOSALS_SORT_RIGHT + + update() + +// returns the type path of disposalpipe corresponding to this item dtype +/obj/structure/disposalconstruct/proc/dpipetype() + switch(ptype) + if(PIPE_DISPOSALS_STRAIGHT, PIPE_DISPOSALS_BENT) + return /obj/structure/disposalpipe/segment + if(PIPE_DISPOSALS_JUNCTION_RIGHT, PIPE_DISPOSALS_JUNCTION_LEFT, PIPE_DISPOSALS_Y_JUNCTION) + return /obj/structure/disposalpipe/junction + if(PIPE_DISPOSALS_TRUNK) + return /obj/structure/disposalpipe/trunk + if(PIPE_DISPOSALS_BIN) + return /obj/machinery/disposal + if(PIPE_DISPOSALS_OUTLET) + return /obj/structure/disposaloutlet + if(PIPE_DISPOSALS_CHUTE) + return /obj/machinery/disposal/deliveryChute + if(PIPE_DISPOSALS_SORT_RIGHT, PIPE_DISPOSALS_SORT_LEFT) + return /obj/structure/disposalpipe/sortjunction + return + + + +// attackby item +// wrench: (un)anchor +// weldingtool: convert to real pipe + +/obj/structure/disposalconstruct/attackby(var/obj/item/I, var/mob/user, params) + var/nicetype = "pipe" + var/ispipe = 0 // Indicates if we should change the level of this pipe + src.add_fingerprint(user) + switch(ptype) + if(PIPE_DISPOSALS_BIN) + nicetype = "disposal bin" + if(PIPE_DISPOSALS_OUTLET) + nicetype = "disposal outlet" + if(PIPE_DISPOSALS_CHUTE) + nicetype = "delivery chute" + if(PIPE_DISPOSALS_SORT_RIGHT, PIPE_DISPOSALS_SORT_LEFT) + nicetype = "sorting pipe" + ispipe = 1 + else + nicetype = "pipe" + ispipe = 1 + + var/turf/T = src.loc + if(T.intact) + to_chat(user, "You can only attach the [nicetype] if the floor plating is removed.") + return + + if(istype(I, /obj/item/wrench)) + if(anchored) + anchored = 0 + if(ispipe) + level = 2 + density = 0 + else + density = 1 + to_chat(user, "You detach the [nicetype] from the underfloor.") + else + anchored = 1 + if(ispipe) + level = 1 // We don't want disposal bins to disappear under the floors + density = 0 + else + density = 1 // We don't want disposal bins or outlets to go density 0 + to_chat(user, "You attach the [nicetype] to the underfloor.") + playsound(src.loc, I.usesound, 100, 1) + update() + return + + + if(ptype in list(PIPE_DISPOSALS_BIN, PIPE_DISPOSALS_OUTLET, PIPE_DISPOSALS_CHUTE)) // Disposal or outlet + var/obj/structure/disposalpipe/trunk/CP = locate() in T + if(!CP) // There's no trunk + to_chat(user, "The [nicetype] requires a trunk underneath it in order to work.") + return + else + for(var/obj/structure/disposalpipe/CP in T) + if(CP) + update() + var/pdir = CP.dpdir + if(istype(CP, /obj/structure/disposalpipe/broken)) + pdir = CP.dir + if(pdir & dpdir) + to_chat(user, "There is already a [nicetype] at that location.") + return + + if(istype(I, /obj/item/weldingtool)) + if(anchored) + if(I.tool_use_check(user, 0)) + to_chat(user, "Welding the [nicetype] in place.") + if(I.use_tool(src, user, 20, volume = I.tool_volume)) + to_chat(user, "The [nicetype] has been welded in place!") + update() // TODO: Make this neat + if(ispipe) // Pipe + + var/pipetype = dpipetype() + var/obj/structure/disposalpipe/P = new pipetype(src.loc) + src.transfer_fingerprints_to(P) + P.base_icon_state = base_state + P.dir = dir + P.dpdir = dpdir + P.update_icon() + + //Needs some special treatment ;) + if(ptype == PIPE_DISPOSALS_SORT_RIGHT || ptype == PIPE_DISPOSALS_SORT_LEFT) + var/obj/structure/disposalpipe/sortjunction/SortP = P + SortP.updatedir() + + else if(ptype == PIPE_DISPOSALS_BIN) // Disposal bin + var/obj/machinery/disposal/P = new /obj/machinery/disposal(src.loc) + src.transfer_fingerprints_to(P) + P.mode = 0 // start with pump off + + else if(ptype == PIPE_DISPOSALS_OUTLET) // Disposal outlet + + var/obj/structure/disposaloutlet/P = new /obj/structure/disposaloutlet(src.loc) + src.transfer_fingerprints_to(P) + P.dir = dir + + else if(ptype==PIPE_DISPOSALS_CHUTE) // Disposal outlet + + var/obj/machinery/disposal/deliveryChute/P = new /obj/machinery/disposal/deliveryChute(src.loc) + src.transfer_fingerprints_to(P) + P.dir = dir + + qdel(src) + return + else + to_chat(user, "You need more welding fuel to complete this task.") + return + else + to_chat(user, "You need to attach it to the plating first!") + return + +/obj/structure/disposalconstruct/rpd_act(mob/user, obj/item/rpd/our_rpd) + . = TRUE + if(our_rpd.mode == RPD_ROTATE_MODE) + rotate() + else if(our_rpd.mode == RPD_FLIP_MODE) + flip() + else if(our_rpd.mode == RPD_DELETE_MODE) + our_rpd.delete_single_pipe(user, src) + else + return ..() diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm index 02ca42e94d9..906d1136ebc 100644 --- a/code/modules/recycling/disposal.dm +++ b/code/modules/recycling/disposal.dm @@ -1,1401 +1,1384 @@ -// Disposal bin -// Holds items for disposal into pipe system -// Draws air from turf, gradually charges internal reservoir -// Once full (~1 atm), uses air resv to flush items into the pipes -// Automatically recharges air (unless off), will flush when ready if pre-set -// Can hold items and human size things, no other draggables -// Toilets are a type of disposal bin for small objects only and work on magic. By magic, I mean torque rotation -#define SEND_PRESSURE 0.05*ONE_ATMOSPHERE - -/obj/machinery/disposal - name = "disposal unit" - desc = "A pneumatic waste disposal unit." - icon = 'icons/obj/pipes/disposal.dmi' - icon_state = "disposal" - anchored = 1 - density = 1 - on_blueprints = TRUE - armor = list("melee" = 25, "bullet" = 10, "laser" = 10, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 30) - max_integrity = 200 - resistance_flags = FIRE_PROOF - var/datum/gas_mixture/air_contents // internal reservoir - var/mode = 1 // item mode 0=off 1=charging 2=charged - var/flush = 0 // true if flush handle is pulled - var/obj/structure/disposalpipe/trunk/trunk = null // the attached pipe trunk - var/flushing = 0 // true if flushing in progress - var/flush_every_ticks = 30 //Every 30 ticks it will look whether it is ready to flush - var/flush_count = 0 //this var adds 1 once per tick. When it reaches flush_every_ticks it resets and tries to flush. - var/last_sound = 0 - active_power_usage = 600 - idle_power_usage = 100 - -// create a new disposal -// find the attached trunk (if present) -/obj/machinery/disposal/New() - ..() - trunk_check() - //gas.volume = 1.05 * CELLSTANDARD - update() - -/obj/machinery/disposal/proc/trunk_check() - var/obj/structure/disposalpipe/trunk/T = locate() in loc - if(!T) - mode = 0 - flush = 0 - else - mode = initial(mode) - flush = initial(flush) - T.nicely_link_to_other_stuff(src) - -//When the disposalsoutlet is forcefully moved. Due to meteorshot (not the recall spell) -/obj/machinery/disposal/Moved(atom/OldLoc, Dir) - . = ..() - eject() - var/ptype = istype(src, /obj/machinery/disposal/deliveryChute) ? PIPE_DISPOSALS_CHUTE : PIPE_DISPOSALS_BIN //Check what disposaltype it is - var/turf/T = OldLoc - if(T.intact) - var/turf/simulated/floor/F = T - F.remove_tile(null,TRUE,TRUE) - T.visible_message("The floortile is ripped from the floor!", "You hear a loud bang!") - if(trunk) - trunk.remove_trunk_links() - var/obj/structure/disposalconstruct/C = new (loc) - transfer_fingerprints_to(C) - C.ptype = ptype - C.update() - C.anchored = 0 - C.density = 1 - qdel(src) - -/obj/machinery/disposal/Destroy() - eject() - if(trunk) - trunk.remove_trunk_links() - return ..() - -/obj/machinery/disposal/singularity_pull(S, current_size) - ..() - if(current_size >= STAGE_FIVE) - deconstruct() - -/obj/machinery/disposal/Initialize() - // this will get a copy of the air turf and take a SEND PRESSURE amount of air from it - ..() - var/atom/L = loc - var/datum/gas_mixture/env = new - env.copy_from(L.return_air()) - var/datum/gas_mixture/removed = env.remove(SEND_PRESSURE + 1) - air_contents = new - air_contents.merge(removed) - trunk_check() - -// attack by item places it in to disposal -/obj/machinery/disposal/attackby(var/obj/item/I, var/mob/user, params) - if(stat & BROKEN || !I || !user) - return - - src.add_fingerprint(user) - if(mode<=0) // It's off - if(istype(I, /obj/item/screwdriver)) - if(contents.len > 0) - to_chat(user, "Eject the items first!") - return - if(mode==0) // It's off but still not unscrewed - mode=-1 // Set it to doubleoff l0l - playsound(src.loc, I.usesound, 50, 1) - to_chat(user, "You remove the screws around the power connection.") - return - else if(mode==-1) - mode=0 - playsound(src.loc, I.usesound, 50, 1) - to_chat(user, "You attach the screws around the power connection.") - return - else if(istype(I,/obj/item/weldingtool) && mode==-1) - if(contents.len > 0) - to_chat(user, "Eject the items first!") - return - var/obj/item/weldingtool/W = I - if(W.remove_fuel(0,user)) - playsound(src.loc, W.usesound, 100, 1) - to_chat(user, "You start slicing the floorweld off the disposal unit.") - - if(do_after(user, 20 * W.toolspeed, target = src)) - if(!src || !W.isOn()) return - to_chat(user, "You sliced the floorweld off the disposal unit.") - var/obj/structure/disposalconstruct/C = new (src.loc) - src.transfer_fingerprints_to(C) - C.ptype = PIPE_DISPOSALS_BIN - C.anchored = 1 - C.density = 1 - C.update() - qdel(src) - return - else - to_chat(user, "You need more welding fuel to complete this task.") - return - - if(istype(I, /obj/item/melee/energy/blade)) - to_chat(user, "You can't place that item inside the disposal unit.") - return - - if(istype(I, /obj/item/storage)) - var/obj/item/storage/S = I - if((S.allow_quick_empty || S.allow_quick_gather) && S.contents.len) - S.hide_from(user) - user.visible_message("[user] empties \the [S] into \the [src].", "You empty \the [S] into \the [src].") - for(var/obj/item/O in S.contents) - S.remove_from_storage(O, src) - S.update_icon() // For content-sensitive icons - update() - return - - var/obj/item/grab/G = I - if(istype(G)) // handle grabbed mob - if(ismob(G.affecting)) - var/mob/GM = G.affecting - for(var/mob/V in viewers(usr)) - V.show_message("[usr] starts putting [GM.name] into the disposal.", 3) - if(do_after(usr, 20, target = GM)) - GM.forceMove(src) - for(var/mob/C in viewers(src)) - C.show_message("[GM.name] has been placed in the [src] by [user].", 3) - qdel(G) - add_attack_logs(usr, GM, "Disposal'ed", !!GM.ckey ? null : ATKLOG_ALL) - return - - if(!I) - return - - if(!user.drop_item()) - return - if(I) - I.forceMove(src) - - to_chat(user, "You place \the [I] into the [src].") - for(var/mob/M in viewers(src)) - if(M == user) - continue - M.show_message("[user.name] places \the [I] into the [src].", 3) - - update() - -// mouse drop another mob or self -// -/obj/machinery/disposal/MouseDrop_T(mob/target, mob/user) - if(!istype(target) || target.buckled || target.has_buckled_mobs() || get_dist(user, src) > 1 || get_dist(user, target) > 1 || user.stat || istype(user, /mob/living/silicon/ai)) - return - if(isanimal(user) && target != user) return //animals cannot put mobs other than themselves into disposal - src.add_fingerprint(user) - var/target_loc = target.loc - var/msg - for(var/mob/V in viewers(usr)) - if(target == user && !user.stat && !user.IsWeakened() && !user.stunned && !user.paralysis) - V.show_message("[usr] starts climbing into the disposal.", 3) - if(target != user && !user.restrained() && !user.stat && !user.IsWeakened() && !user.stunned && !user.paralysis) - if(target.anchored) return - V.show_message("[usr] starts stuffing [target.name] into the disposal.", 3) - if(!do_after(usr, 20, target = target)) - return - if(target_loc != target.loc) - return - if(target == user && !user.stat && !user.IsWeakened() && !user.stunned && !user.paralysis) // if drop self, then climbed in - // must be awake, not stunned or whatever - msg = "[user.name] climbs into the [src]." - to_chat(user, "You climb into the [src].") - else if(target != user && !user.restrained() && !user.stat && !user.IsWeakened() && !user.stunned && !user.paralysis) - msg = "[user.name] stuffs [target.name] into the [src]!" - to_chat(user, "You stuff [target.name] into the [src]!") - - add_attack_logs(user, target, "Disposal'ed", !!target.ckey ? null : ATKLOG_ALL) - else - return - target.forceMove(src) - - for(var/mob/C in viewers(src)) - if(C == user) - continue - C.show_message(msg, 3) - - update() - return - -// attempt to move while inside -/obj/machinery/disposal/relaymove(mob/user as mob) - if(user.stat || src.flushing) - return - src.go_out(user) - return - -// leave the disposal -/obj/machinery/disposal/proc/go_out(mob/user) - if(user) - user.forceMove(loc) - update() - -// ai as human but can't flush -/obj/machinery/disposal/attack_ai(mob/user as mob) - src.add_hiddenprint(user) - ui_interact(user) - -/obj/machinery/disposal/attack_ghost(mob/user as mob) - ui_interact(user) - -// human interact with machine -/obj/machinery/disposal/attack_hand(mob/user as mob) - if(..(user)) - return 1 - - if(stat & BROKEN) - return - - if(user && user.loc == src) - to_chat(usr, "You cannot reach the controls from inside.") - return - - // Clumsy folks can only flush it. - if(user.IsAdvancedToolUser()) - ui_interact(user) - else - flush = !flush - update() - return - -/obj/machinery/disposal/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "disposal_bin.tmpl", "Waste Disposal Unit", 395, 250) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/disposal/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - - var/pressure = Clamp(100* air_contents.return_pressure() / (SEND_PRESSURE), 0, 100) - var/pressure_round = round(pressure,1) - - data["isAI"] = isAI(user) - data["flushing"] = flush - data["mode"] = mode - if(mode <= 0) - data["pumpstatus"] = "N/A" - else if(mode == 1) - data["pumpstatus"] = "Pressurizing" - else if(mode == 2) - data["pumpstatus"] = "Ready" - else - data["pumpstatus"] = "Idle" - data["pressure"] = pressure_round - - return data - -/obj/machinery/disposal/Topic(href, href_list) - if(usr.loc == src) - to_chat(usr, "You cannot reach the controls from inside.") - return - - if(mode==-1 && !href_list["eject"]) // only allow ejecting if mode is -1 - to_chat(usr, "The disposal units power is disabled.") - return - - if(..()) - return - - if(stat & BROKEN) - return - - src.add_fingerprint(usr) - - if(usr.stat || usr.restrained() || src.flushing) - return - - if(istype(src.loc, /turf)) - if(href_list["pump"]) - if(text2num(href_list["pump"])) - mode = 1 - else - mode = 0 - update() - - if(!isAI(usr)) - if(href_list["handle"]) - flush = text2num(href_list["handle"]) - update() - - if(href_list["eject"]) - eject() - return - -// eject the contents of the disposal unit -/obj/machinery/disposal/proc/eject() - for(var/atom/movable/AM in src) - AM.loc = src.loc - AM.pipe_eject(0) - update() - -// update the icon & overlays to reflect mode & status -/obj/machinery/disposal/proc/update() - overlays.Cut() - if(stat & BROKEN) - icon_state = "disposal-broken" - mode = 0 - flush = 0 - return - - // flush handle - if(flush) - overlays += image('icons/obj/pipes/disposal.dmi', "dispover-handle") - - // only handle is shown if no power - if(stat & NOPOWER || mode == -1) - return - - // check for items in disposal - occupied light - if(contents.len > 0) - overlays += image('icons/obj/pipes/disposal.dmi', "dispover-full") - - // charging and ready light - if(mode == 1) - overlays += image('icons/obj/pipes/disposal.dmi', "dispover-charge") - else if(mode == 2) - overlays += image('icons/obj/pipes/disposal.dmi', "dispover-ready") - -// timed process -// charge the gas reservoir and perform flush if ready -/obj/machinery/disposal/process() - use_power = NO_POWER_USE - if(stat & BROKEN) // nothing can happen if broken - return - - flush_count++ - if( flush_count >= flush_every_ticks ) - if( contents.len ) - if(mode == 2) - spawn(0) - feedback_inc("disposal_auto_flush",1) - flush() - flush_count = 0 - - src.updateDialog() - - if(flush && air_contents.return_pressure() >= SEND_PRESSURE ) // flush can happen even without power - flush() - - if(stat & NOPOWER) // won't charge if no power - return - - use_power = IDLE_POWER_USE - - if(mode != 1) // if off or ready, no need to charge - return - - // otherwise charge - use_power = ACTIVE_POWER_USE - - var/atom/L = loc // recharging from loc turf - - var/datum/gas_mixture/env = L.return_air() - var/pressure_delta = (SEND_PRESSURE*1.01) - air_contents.return_pressure() - - if(env.temperature > 0) - var/transfer_moles = 0.1 * pressure_delta*air_contents.volume/(env.temperature * R_IDEAL_GAS_EQUATION) - - //Actually transfer the gas - var/datum/gas_mixture/removed = env.remove(transfer_moles) - air_contents.merge(removed) - air_update_turf() - - - // if full enough, switch to ready mode - if(air_contents.return_pressure() >= SEND_PRESSURE) - mode = 2 - update() - return - -// perform a flush -/obj/machinery/disposal/proc/flush() - - flushing = 1 - flick("[icon_state]-flush", src) - - var/wrapcheck = 0 - var/obj/structure/disposalholder/H = new() // virtual holder object which actually - // travels through the pipes. - //Hacky test to get drones to mail themselves through disposals. - for(var/mob/living/silicon/robot/drone/D in src) - wrapcheck = 1 - - for(var/mob/living/silicon/robot/syndicate/saboteur/R in src) - wrapcheck = 1 - - for(var/obj/item/smallDelivery/O in src) - wrapcheck = 1 - - if(wrapcheck == 1) - H.tomail = 1 - - sleep(10) - if(last_sound < world.time + 1) - playsound(src, 'sound/machines/disposalflush.ogg', 50, 0, 0) - last_sound = world.time - sleep(5) // wait for animation to finish - - - H.init(src) // copy the contents of disposer to holder - air_contents = new() // The holder just took our gas; replace it - H.start(src) // start the holder processing movement - flushing = 0 - // now reset disposal state - flush = 0 - if(mode == 2) // if was ready, - mode = 1 // switch to charging - update() - return - - -// called when area power changes -/obj/machinery/disposal/power_change() - ..() // do default setting/reset of stat NOPOWER bit - update() // update icon - return - - -// called when holder is expelled from a disposal -// should usually only occur if the pipe network is modified -/obj/machinery/disposal/proc/expel(var/obj/structure/disposalholder/H) - - var/turf/target - playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) - if(H) // Somehow, someone managed to flush a window which broke mid-transit and caused the disposal to go in an infinite loop trying to expel null, hopefully this fixes it - for(var/atom/movable/AM in H) - target = get_offset_target_turf(src.loc, rand(5)-rand(5), rand(5)-rand(5)) - - AM.loc = src.loc - AM.pipe_eject(0) - if(!istype(AM, /mob/living/silicon/robot/drone) && !istype(AM, /mob/living/silicon/robot/syndicate/saboteur)) //Poor drones kept smashing windows and taking system damage being fired out of disposals. ~Z - spawn(1) - if(AM) - AM.throw_at(target, 5, 1) - - H.vent_gas(loc) - qdel(H) - -/obj/machinery/disposal/CanPass(atom/movable/mover, turf/target, height=0) - if(istype(mover,/obj/item) && mover.throwing) - var/obj/item/I = mover - if(istype(I, /obj/item/projectile)) - return - if(prob(75)) - I.loc = src - for(var/mob/M in viewers(src)) - M.show_message("\the [I] lands in \the [src].", 3) - update() - else - for(var/mob/M in viewers(src)) - M.show_message("\the [I] bounces off of \the [src]'s rim!.", 3) - return 0 - else - return ..(mover, target, height) - - -/obj/machinery/disposal/singularity_pull(S, current_size) - if(current_size >= STAGE_FIVE) - qdel(src) - -/obj/machinery/disposal/get_remote_view_fullscreens(mob/user) - if(user.stat == DEAD || !(user.sight & (SEEOBJS|SEEMOBS))) - user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 2) - -// virtual disposal object -// travels through pipes in lieu of actual items -// contents will be items flushed by the disposal -// this allows the gas flushed to be tracked - -/obj/structure/disposalholder - invisibility = 101 - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF - var/datum/gas_mixture/gas = null // gas used to flush, will appear at exit point - var/active = 0 // true if the holder is moving, otherwise inactive - dir = 0 - var/count = 1000 //*** can travel 1000 steps before going inactive (in case of loops) - var/has_fat_guy = 0 // true if contains a fat person - var/destinationTag = 0 // changes if contains a delivery container - var/tomail = 0 //changes if contains wrapped package - var/hasmob = 0 //If it contains a mob - -/obj/structure/disposalholder/Destroy() - QDEL_NULL(gas) - active = 0 - return ..() - - // initialize a holder from the contents of a disposal unit -/obj/structure/disposalholder/proc/init(var/obj/machinery/disposal/D) - gas = D.air_contents// transfer gas resv. into holder object - - //Check for any living mobs trigger hasmob. - //hasmob effects whether the package goes to cargo or its tagged destination. - for(var/mob/living/M in D) - if(M && M.stat != 2 && !istype(M, /mob/living/silicon/robot/drone) && !istype(M, /mob/living/silicon/robot/syndicate/saboteur)) - hasmob = 1 - - //Checks 1 contents level deep. This means that players can be sent through disposals... - //...but it should require a second person to open the package. (i.e. person inside a wrapped locker) - for(var/obj/O in D) - if(O.contents) - for(var/mob/living/M in O.contents) - if(M && M.stat != 2 && !istype(M,/mob/living/silicon/robot/drone) && !istype(M, /mob/living/silicon/robot/syndicate/saboteur)) - hasmob = 1 - - // now everything inside the disposal gets put into the holder - // note AM since can contain mobs or objs - for(var/atom/movable/AM in D) - AM.loc = src - SEND_SIGNAL(AM, COMSIG_MOVABLE_DISPOSING, src, D) - if(istype(AM, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = AM - if(FAT in H.mutations) // is a human and fat? - has_fat_guy = 1 // set flag on holder - if(istype(AM, /obj/structure/bigDelivery) && !hasmob) - var/obj/structure/bigDelivery/T = AM - destinationTag = T.sortTag - if(istype(AM, /obj/item/smallDelivery) && !hasmob) - var/obj/item/smallDelivery/T = AM - destinationTag = T.sortTag - //Drones can mail themselves through maint. - if(istype(AM, /mob/living/silicon/robot/drone)) - var/mob/living/silicon/robot/drone/drone = AM - destinationTag = drone.mail_destination - if(istype(AM, /mob/living/silicon/robot/syndicate/saboteur)) - var/mob/living/silicon/robot/syndicate/saboteur/S = AM - destinationTag = S.mail_destination - if(istype(AM, /obj/item/shippingPackage) && !hasmob) - var/obj/item/shippingPackage/sp = AM - if(sp.sealed) //only sealed packages get delivered to their intended destination - destinationTag = sp.sortTag - - - // start the movement process - // argument is the disposal unit the holder started in -/obj/structure/disposalholder/proc/start(var/obj/machinery/disposal/D) - if(!D.trunk) - D.expel(src) // no trunk connected, so expel immediately - return - - loc = D.trunk - active = 1 - dir = DOWN - spawn(1) - move() // spawn off the movement process - - return - - // movement process, persists while holder is moving through pipes -/obj/structure/disposalholder/proc/move() - var/obj/structure/disposalpipe/last - while(active) - /* if(hasmob && prob(3)) - for(var/mob/living/H in src) - if(!istype(H,/mob/living/silicon/robot/drone)) //Drones use the mailing code to move through the disposal system, - H.take_overall_damage(20, 0, "Blunt Trauma") */ //horribly maim any living creature jumping down disposals. c'est la vie - - if(has_fat_guy && prob(2)) // chance of becoming stuck per segment if contains a fat guy - active = 0 - // find the fat guys - for(var/mob/living/carbon/human/H in src) - - break - sleep(1) // was 1 - var/obj/structure/disposalpipe/curr = loc - last = curr - curr = curr.transfer(src) - if(!curr) - last.expel(src, loc, dir) - - // - if(!(count--)) - active = 0 - return - - - - // find the turf which should contain the next pipe -/obj/structure/disposalholder/proc/nextloc() - return get_step(loc,dir) - - // find a matching pipe on a turf -/obj/structure/disposalholder/proc/findpipe(var/turf/T) - if(!T) - return null - - var/fdir = turn(dir, 180) // flip the movement direction - for(var/obj/structure/disposalpipe/P in T) - if(fdir & P.dpdir) // find pipe direction mask that matches flipped dir - return P - // if no matching pipe, return null - return null - - // merge two holder objects - // used when a a holder meets a stuck holder -/obj/structure/disposalholder/proc/merge(var/obj/structure/disposalholder/other) - for(var/atom/movable/AM in other) - AM.loc = src // move everything in other holder to this one - if(ismob(AM)) - var/mob/M = AM - if(M.client) // if a client mob, update eye to follow this holder - M.client.eye = src - - if(other.has_fat_guy) - has_fat_guy = 1 - qdel(other) - - - // called when player tries to move while in a pipe -/obj/structure/disposalholder/relaymove(mob/user as mob) - if(!istype(user,/mob/living)) - return - - var/mob/living/U = user - - if(U.stat || U.last_special <= world.time) - return - - U.last_special = world.time+100 - - if(src.loc) - for(var/mob/M in hearers(src.loc.loc)) - to_chat(M, "CLONG, clong!") - - playsound(src.loc, 'sound/effects/clang.ogg', 50, 0, 0) - - // called to vent all gas in holder to a location -/obj/structure/disposalholder/proc/vent_gas(var/atom/location) - if(location) - location.assume_air(gas) // vent all gas to turf - air_update_turf() - return - -// Disposal pipes - -/obj/structure/disposalpipe - icon = 'icons/obj/pipes/disposal.dmi' - name = "disposal pipe" - desc = "An underfloor disposal pipe." - anchored = 1 - density = 0 - - on_blueprints = TRUE - level = 1 // underfloor only - var/dpdir = 0 // bitmask of pipe directions - dir = 0 // dir will contain dominant direction for junction pipes - var/health = 10 // health points 0-10 - max_integrity = 200 - armor = list("melee" = 25, "bullet" = 10, "laser" = 10, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 30) - damage_deflection = 10 - layer = DISPOSAL_PIPE_LAYER // slightly lower than wires and other pipes - var/base_icon_state // initial icon state on map - - // new pipe, set the icon_state as on map -/obj/structure/disposalpipe/New() - ..() - base_icon_state = icon_state - - -// pipe is deleted -// ensure if holder is present, it is expelled -/obj/structure/disposalpipe/Destroy() - for(var/obj/structure/disposalholder/H in contents) - H.active = 0 - var/turf/T = loc - if(T.density) - // deleting pipe is inside a dense turf (wall) - // this is unlikely, but just dump out everything into the turf in case - - for(var/atom/movable/AM in H) - AM.loc = T - AM.pipe_eject(0) - qdel(H) - ..() - return - - // otherwise, do normal expel from turf - expel(H, T, 0) - return ..() - -/obj/structure/disposalpipe/singularity_pull(S, current_size) - ..() - if(current_size >= STAGE_FIVE) - deconstruct() - -// returns the direction of the next pipe object, given the entrance dir -// by default, returns the bitmask of remaining directions -/obj/structure/disposalpipe/proc/nextdir(var/fromdir) - return dpdir & (~turn(fromdir, 180)) - -// transfer the holder through this pipe segment -// overriden for special behaviour -// -/obj/structure/disposalpipe/proc/transfer(var/obj/structure/disposalholder/H) - var/nextdir = nextdir(H.dir) - H.dir = nextdir - var/turf/T = H.nextloc() - var/obj/structure/disposalpipe/P = H.findpipe(T) - - if(P) - // find other holder in next loc, if inactive merge it with current - var/obj/structure/disposalholder/H2 = locate() in P - if(H2 && !H2.active) - H.merge(H2) - - H.loc = P - else // if wasn't a pipe, then set loc to turf - H.loc = T - return null - - return P - - -// update the icon_state to reflect hidden status -/obj/structure/disposalpipe/proc/update() - var/turf/T = src.loc - hide(T.intact && !istype(T,/turf/space)) // space never hides pipes - update_icon() - -// hide called by levelupdate if turf intact status changes -// change visibility status and force update of icon -/obj/structure/disposalpipe/hide(var/intact) - invisibility = intact ? 101: 0 // hide if floor is intact - update_icon() - -// update actual icon_state depending on visibility -// if invisible, append "f" to icon_state to show faded version -// this will be revealed if a T-scanner is used -// if visible, use regular icon_state -/obj/structure/disposalpipe/update_icon() - if(invisibility) - icon_state = "[base_icon_state]f" - else - icon_state = base_icon_state - - -// expel the held objects into a turf -// called when there is a break in the pipe -// - -/obj/structure/disposalpipe/proc/expel(var/obj/structure/disposalholder/H, var/turf/T, var/direction) - - if(!T) - return - - var/turf/target - - if(T.density) // dense ouput turf, so stop holder - H.active = 0 - H.loc = src - return - if(T.intact && istype(T,/turf/simulated/floor)) //intact floor, pop the tile - var/turf/simulated/floor/F = T - new F.builtin_tile.type(H) - F.remove_tile(null,TRUE,FALSE) - - if(direction) // direction is specified - if(istype(T, /turf/space)) // if ended in space, then range is unlimited - target = get_edge_target_turf(T, direction) - else // otherwise limit to 10 tiles - target = get_ranged_target_turf(T, direction, 10) - - playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) - if(H) - for(var/atom/movable/AM in H) - AM.loc = T - AM.pipe_eject(direction) - spawn(1) - if(AM) - AM.throw_at(target, 100, 1) - H.vent_gas(T) - qdel(H) - - else // no specified direction, so throw in random direction - - playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) - if(H) - for(var/atom/movable/AM in H) - target = get_offset_target_turf(T, rand(5)-rand(5), rand(5)-rand(5)) - - AM.loc = T - AM.pipe_eject(0) - spawn(1) - if(AM) - AM.throw_at(target, 5, 1) - - H.vent_gas(T) // all gas vent to turf - qdel(H) - -// call to break the pipe -// will expel any holder inside at the time -// then delete the pipe -// remains : set to leave broken pipe pieces in place -/obj/structure/disposalpipe/proc/broken(remains = 0) - if(remains) - for(var/D in cardinal) - if(D & dpdir) - var/obj/structure/disposalpipe/broken/P = new(src.loc) - P.setDir(D) - - invisibility = 101 // make invisible (since we won't delete the pipe immediately) - var/obj/structure/disposalholder/H = locate() in src - if(H) - // holder was present - H.active = 0 - var/turf/T = src.loc - if(T.density) - // broken pipe is inside a dense turf (wall) - // this is unlikely, but just dump out everything into the turf in case - - for(var/atom/movable/AM in H) - AM.loc = T - AM.pipe_eject(0) - qdel(H) - return - - // otherwise, do normal expel from turf - if(H) - expel(H, T, 0) - - spawn(2) // delete pipe after 2 ticks to ensure expel proc finished - qdel(src) - -// pipe affected by explosion -/obj/structure/disposalpipe/ex_act(severity) - switch(severity) - if(1) - broken(0) - if(2) - health -= rand(5, 15) - healthcheck() - if(3) - health -= rand(0, 15) - healthcheck() - -// test health for brokenness -/obj/structure/disposalpipe/proc/healthcheck() - if(health < -2) - broken(0) - else if(health<1) - broken(1) - return - -//attack by item -//weldingtool: unfasten and convert to obj/disposalconstruct - -/obj/structure/disposalpipe/attackby(var/obj/item/I, var/mob/user, params) - var/turf/T = get_turf(src) - if(T.intact) - return // prevent interaction with T-scanner revealed pipes - - add_fingerprint(user) - - if(istype(I, /obj/item/weldingtool)) - var/obj/item/weldingtool/W = I - if(W.remove_fuel(0, user)) - to_chat(user, "You begin slicing \the [src].") - playsound(loc, W.usesound, 100, 1) - if(do_after(user, 30 * W.toolspeed, target = src)) - to_chat(user, "You finish slicing \the [src].") - welded() - else - to_chat(user, "You need more welding fuel to cut the pipe.") - -// called when pipe is cut with welder -/obj/structure/disposalpipe/proc/welded() - - var/obj/structure/disposalconstruct/C = new (src.loc) - switch(base_icon_state) - if("pipe-s") - C.ptype = PIPE_DISPOSALS_STRAIGHT - if("pipe-c") - C.ptype = PIPE_DISPOSALS_BENT - if("pipe-j1") - C.ptype = PIPE_DISPOSALS_JUNCTION_RIGHT - if("pipe-j2") - C.ptype = PIPE_DISPOSALS_JUNCTION_LEFT - if("pipe-y") - C.ptype = PIPE_DISPOSALS_Y_JUNCTION - if("pipe-t") - C.ptype = PIPE_DISPOSALS_TRUNK - if("pipe-j1s") - C.ptype = PIPE_DISPOSALS_SORT_RIGHT - if("pipe-j2s") - C.ptype = PIPE_DISPOSALS_SORT_LEFT - src.transfer_fingerprints_to(C) - C.dir = dir - C.density = 0 - C.anchored = 1 - C.update() - - qdel(src) - -// *** TEST verb -//client/verb/dispstop() -// for(var/obj/structure/disposalholder/H in world) -// H.active = 0 - -// a straight or bent segment -/obj/structure/disposalpipe/segment - icon_state = "pipe-s" - -/obj/structure/disposalpipe/segment/New() - ..() - if(icon_state == "pipe-s") - dpdir = dir | turn(dir, 180) - else - dpdir = dir | turn(dir, -90) - - update() - - - -//a three-way junction with dir being the dominant direction -/obj/structure/disposalpipe/junction - icon_state = "pipe-j1" - -/obj/structure/disposalpipe/junction/New() - ..() - if(icon_state == "pipe-j1") - dpdir = dir | turn(dir, -90) | turn(dir,180) - else if(icon_state == "pipe-j2") - dpdir = dir | turn(dir, 90) | turn(dir,180) - else // pipe-y - dpdir = dir | turn(dir,90) | turn(dir, -90) - update() - - - // next direction to move - // if coming in from secondary dirs, then next is primary dir - // if coming in from primary dir, then next is equal chance of other dirs - -/obj/structure/disposalpipe/junction/nextdir(var/fromdir) - var/flipdir = turn(fromdir, 180) - if(flipdir != dir) // came from secondary dir - return dir // so exit through primary - else // came from primary - // so need to choose either secondary exit - var/mask = ..(fromdir) - - // find a bit which is set - var/setbit = 0 - if(mask & NORTH) - setbit = NORTH - else if(mask & SOUTH) - setbit = SOUTH - else if(mask & EAST) - setbit = EAST - else - setbit = WEST - - if(prob(50)) // 50% chance to choose the found bit or the other one - return setbit - else - return mask & (~setbit) - -//a three-way junction that sorts objects -/obj/structure/disposalpipe/sortjunction - - icon_state = "pipe-j1s" - var/sortType = 0 //Look at the list called TAGGERLOCATIONS in /code/_globalvars/lists/flavor_misc.dm - var/posdir = 0 - var/negdir = 0 - var/sortdir = 0 - -/obj/structure/disposalpipe/sortjunction/proc/updatedesc() - desc = "An underfloor disposal pipe with a package sorting mechanism." - if(sortType>0) - var/tag = uppertext(GLOB.TAGGERLOCATIONS[sortType]) - desc += "\nIt's tagged with [tag]" - -/obj/structure/disposalpipe/sortjunction/proc/updatedir() - posdir = dir - negdir = turn(posdir, 180) - - if(icon_state == "pipe-j1s") - sortdir = turn(posdir, -90) - else - icon_state = "pipe-j2s" - sortdir = turn(posdir, 90) - - dpdir = sortdir | posdir | negdir - -/obj/structure/disposalpipe/sortjunction/New() - ..() - updatedir() - updatedesc() - update() - return - -/obj/structure/disposalpipe/sortjunction/attackby(var/obj/item/I, var/mob/user, params) - if(..()) - return - - if(istype(I, /obj/item/destTagger)) - var/obj/item/destTagger/O = I - - if(O.currTag > 0)// Tag set - sortType = O.currTag - name = GLOB.TAGGERLOCATIONS[O.currTag] - playsound(src.loc, 'sound/machines/twobeep.ogg', 100, 1) - var/tag = uppertext(GLOB.TAGGERLOCATIONS[O.currTag]) - to_chat(user, "Changed filter to [tag]") - updatedesc() - - - // next direction to move - // if coming in from negdir, then next is primary dir or sortdir - // if coming in from posdir, then flip around and go back to posdir - // if coming in from sortdir, go to posdir - -/obj/structure/disposalpipe/sortjunction/nextdir(var/fromdir, var/sortTag) - //var/flipdir = turn(fromdir, 180) - if(fromdir != sortdir) // probably came from the negdir - - if(src.sortType == sortTag) //if destination matches filtered type... - return sortdir // exit through sortdirection - else - return posdir - else // came from sortdir - // so go with the flow to positive direction - return posdir - -/obj/structure/disposalpipe/sortjunction/transfer(var/obj/structure/disposalholder/H) - var/nextdir = nextdir(H.dir, H.destinationTag) - H.dir = nextdir - var/turf/T = H.nextloc() - var/obj/structure/disposalpipe/P = H.findpipe(T) - - if(P) - // find other holder in next loc, if inactive merge it with current - var/obj/structure/disposalholder/H2 = locate() in P - if(H2 && !H2.active) - H.merge(H2) - H.loc = P - else // if wasn't a pipe, then set loc to turf - H.loc = T - return null - - return P - - -//a three-way junction that sorts objects destined for the mail office mail table (tomail = 1) -/obj/structure/disposalpipe/wrapsortjunction - desc = "An underfloor disposal pipe which sorts wrapped and unwrapped objects." - icon_state = "pipe-j1s" - var/posdir = 0 - var/negdir = 0 - var/sortdir = 0 - -/obj/structure/disposalpipe/wrapsortjunction/New() - ..() - posdir = dir - if(icon_state == "pipe-j1s") - sortdir = turn(posdir, -90) - negdir = turn(posdir, 180) - else - icon_state = "pipe-j2s" - sortdir = turn(posdir, 90) - negdir = turn(posdir, 180) - dpdir = sortdir | posdir | negdir - - update() - return - - - // next direction to move - // if coming in from negdir, then next is primary dir or sortdir - // if coming in from posdir, then flip around and go back to posdir - // if coming in from sortdir, go to posdir - -/obj/structure/disposalpipe/wrapsortjunction/nextdir(var/fromdir, var/istomail) - //var/flipdir = turn(fromdir, 180) - if(fromdir != sortdir) // probably came from the negdir - if(istomail) //if destination matches filtered type... - return sortdir // exit through sortdirection - else - return posdir - else // came from sortdir - return posdir // so go with the flow to positive direction - -/obj/structure/disposalpipe/wrapsortjunction/transfer(var/obj/structure/disposalholder/H) - var/nextdir = nextdir(H.dir, H.tomail) - H.dir = nextdir - var/turf/T = H.nextloc() - var/obj/structure/disposalpipe/P = H.findpipe(T) - - if(P) - // find other holder in next loc, if inactive merge it with current - var/obj/structure/disposalholder/H2 = locate() in P - if(H2 && !H2.active) - H.merge(H2) - - H.loc = P - else // if wasn't a pipe, then set loc to turf - H.loc = T - return null - - return P - -//a trunk joining to a disposal bin or outlet on the same turf -/obj/structure/disposalpipe/trunk - icon_state = "pipe-t" - var/obj/linked // the linked obj/machinery/disposal or obj/disposaloutlet - -/obj/structure/disposalpipe/trunk/New() - ..() - dpdir = dir - spawn(1) - getlinked() - - update() - return - -/obj/structure/disposalpipe/trunk/Destroy() - if(istype(linked, /obj/structure/disposaloutlet)) - var/obj/structure/disposaloutlet/O = linked - O.expel(animation = 0) - else if(istype(linked, /obj/machinery/disposal)) - var/obj/machinery/disposal/D = linked - if(D.trunk == src) - D.go_out() - D.trunk = null - remove_trunk_links() - return ..() - -/obj/structure/disposalpipe/trunk/proc/getlinked() - var/obj/machinery/disposal/D = locate() in src.loc - if(D) - nicely_link_to_other_stuff(D) - return - var/obj/structure/disposaloutlet/O = locate() in src.loc - if(O) - nicely_link_to_other_stuff(O) - -/obj/structure/disposalpipe/trunk/proc/remove_trunk_links() //disposals is well-coded - if(!linked) - return - else if(istype(linked, /obj/machinery/disposal)) //jk lol - var/obj/machinery/disposal/D = linked - D.trunk = null - else if(istype(linked, /obj/structure/disposaloutlet)) //God fucking damn it - var/obj/structure/disposaloutlet/D = linked - D.linkedtrunk = null - linked = null - -/obj/structure/disposalpipe/trunk/proc/nicely_link_to_other_stuff(obj/O) - remove_trunk_links() //Breaks the connections between this trunk and the linked machinery so we don't get sent to nullspace or some shit like that - if(istype(O, /obj/machinery/disposal)) - var/obj/machinery/disposal/D = O - linked = D - D.trunk = src - else if(istype(O, /obj/structure/disposaloutlet)) - var/obj/structure/disposaloutlet/D = O - linked = D - D.linkedtrunk = src - - // Override attackby so we disallow trunkremoval when somethings ontop -/obj/structure/disposalpipe/trunk/attackby(var/obj/item/I, var/mob/user, params) - - //Disposal bins or chutes - //Disposal constructors - var/obj/structure/disposalconstruct/C = locate() in src.loc - if(C && C.anchored) - return - - var/turf/T = src.loc - if(T.intact) - return // prevent interaction with T-scanner revealed pipes - src.add_fingerprint(user) - if(istype(I, /obj/item/weldingtool)) - var/obj/item/weldingtool/W = I - - if(W.remove_fuel(0,user)) - playsound(loc, W.usesound, 100, 1) - to_chat(user, "Slicing the disposal pipe.") - if(do_after(user, 30 * W.toolspeed, target = src)) - if(!W.isOn()) - return - welded() - else - to_chat(user, "You need more welding fuel to cut the pipe.") - return - - // would transfer to next pipe segment, but we are in a trunk - // if not entering from disposal bin, - // transfer to linked object (outlet or bin) - -/obj/structure/disposalpipe/trunk/transfer(obj/structure/disposalholder/H) - if(!H) - return - if(H.dir == DOWN) // we just entered from a disposer - return ..() // so do base transfer proc - // otherwise, go to the linked object - if(!linked) - expel(H, loc, FALSE) // expel at turf - else if(istype(linked, /obj/structure/disposaloutlet)) - var/obj/structure/disposaloutlet/DO = linked - for(var/atom/movable/AM in H) - AM.forceMove(DO) - qdel(H) - H.vent_gas(loc) - DO.expel() - else if(istype(linked, /obj/machinery/disposal)) - var/obj/machinery/disposal/D = linked - H.forceMove(D) - D.expel(H) // expel at disposal - else //just in case - expel(H, loc, FALSE) - // nextdir - -/obj/structure/disposalpipe/trunk/nextdir(var/fromdir) - if(fromdir == DOWN) - return dir - else - return 0 - -// a broken pipe -/obj/structure/disposalpipe/broken - icon_state = "pipe-b" - dpdir = 0 // broken pipes have dpdir=0 so they're not found as 'real' pipes - // i.e. will be treated as an empty turf - desc = "A broken piece of disposal pipe." - -/obj/structure/disposalpipe/broken/New() - ..() - update() - return - -/obj/structure/disposalpipe/broken/welded() - qdel(src) - -// the disposal outlet machine - -/obj/structure/disposaloutlet - name = "disposal outlet" - desc = "An outlet for the pneumatic disposal system." - icon = 'icons/obj/pipes/disposal.dmi' - icon_state = "outlet" - density = 1 - anchored = 1 - var/active = 0 - var/turf/target // this will be where the output objects are 'thrown' to. - var/obj/structure/disposalpipe/trunk/linkedtrunk - var/mode = 0 - -/obj/structure/disposaloutlet/New() - ..() - spawn(1) - target = get_ranged_target_turf(src, dir, 10) - var/obj/structure/disposalpipe/trunk/T = locate() in loc - if(T) - T.nicely_link_to_other_stuff(src) - -/obj/structure/disposaloutlet/Destroy() - if(linkedtrunk) - linkedtrunk.remove_trunk_links() - expel(FALSE) - return ..() - - -// expel the contents of the outlet -/obj/structure/disposaloutlet/proc/expel(animation = TRUE) - if(animation) - flick("outlet-open", src) - playsound(src, 'sound/machines/warning-buzzer.ogg', 50, 0, 0) - sleep(20) //wait until correct animation frame - playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) - for(var/atom/movable/AM in contents) - AM.forceMove(loc) - AM.pipe_eject(dir) - if(istype(AM,/mob/living/silicon/robot/drone) || istype(AM, /mob/living/silicon/robot/syndicate/saboteur)) //Drones keep smashing windows from being fired out of chutes. Bad for the station. ~Z - return - spawn(5) - if(QDELETED(AM)) - return - AM.throw_at(target, 3, 1) - - -/obj/structure/disposaloutlet/attackby(var/obj/item/I, var/mob/user, params) - if(!I || !user) - return - src.add_fingerprint(user) - if(istype(I, /obj/item/screwdriver)) - if(mode==0) - mode=1 - playsound(src.loc, I.usesound, 50, 1) - to_chat(user, "You remove the screws around the power connection.") - return - else if(mode==1) - mode=0 - playsound(src.loc, I.usesound, 50, 1) - to_chat(user, "You attach the screws around the power connection.") - return - else if(istype(I,/obj/item/weldingtool) && mode==1) - var/obj/item/weldingtool/W = I - if(W.remove_fuel(0,user)) - playsound(src.loc, W.usesound, 100, 1) - to_chat(user, "You start slicing the floorweld off the disposal outlet.") - if(do_after(user, 20 * W.toolspeed, target = src)) - if(!src || !W.isOn()) return - to_chat(user, "You sliced the floorweld off the disposal outlet.") - var/obj/structure/disposalconstruct/C = new (src.loc) - src.transfer_fingerprints_to(C) - C.ptype = PIPE_DISPOSALS_OUTLET - C.update() - C.anchored = 1 - C.density = 1 - qdel(src) - return - else - to_chat(user, "You need more welding fuel to complete this task.") - return - -//When the disposalsoutlet is forcefully moved. Due to meteorshot or the recall item spell for instance -/obj/structure/disposaloutlet/Moved(atom/OldLoc, Dir) - . = ..() - var/turf/T = OldLoc - if(T.intact) - var/turf/simulated/floor/F = T - F.remove_tile(null,TRUE,TRUE) - T.visible_message("The floortile is ripped from the floor!", "You hear a loud bang!") - if(linkedtrunk) - linkedtrunk.remove_trunk_links() - var/obj/structure/disposalconstruct/C = new (loc) - transfer_fingerprints_to(C) - C.ptype = PIPE_DISPOSALS_OUTLET - C.update() - C.anchored = 0 - C.density = 1 - qdel(src) - -// called when movable is expelled from a disposal pipe or outlet -// by default does nothing, override for special behaviour - -/atom/movable/proc/pipe_eject(var/direction) - return - -// check if mob has client, if so restore client view on eject -/mob/pipe_eject(var/direction) - reset_perspective(null) - -/obj/effect/decal/cleanable/blood/gibs/pipe_eject(var/direction) - var/list/dirs - if(direction) - dirs = list( direction, turn(direction, -45), turn(direction, 45)) - else - dirs = alldirs.Copy() - - src.streak(dirs) - -/obj/effect/decal/cleanable/blood/gibs/robot/gib/pipe_eject(var/direction) - var/list/dirs - if(direction) - dirs = list( direction, turn(direction, -45), turn(direction, 45)) - else - dirs = alldirs.Copy() - - src.streak(dirs) +// Disposal bin +// Holds items for disposal into pipe system +// Draws air from turf, gradually charges internal reservoir +// Once full (~1 atm), uses air resv to flush items into the pipes +// Automatically recharges air (unless off), will flush when ready if pre-set +// Can hold items and human size things, no other draggables +// Toilets are a type of disposal bin for small objects only and work on magic. By magic, I mean torque rotation +#define SEND_PRESSURE 0.05*ONE_ATMOSPHERE + +/obj/machinery/disposal + name = "disposal unit" + desc = "A pneumatic waste disposal unit." + icon = 'icons/obj/pipes/disposal.dmi' + icon_state = "disposal" + anchored = 1 + density = 1 + on_blueprints = TRUE + armor = list("melee" = 25, "bullet" = 10, "laser" = 10, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 30) + max_integrity = 200 + resistance_flags = FIRE_PROOF + var/datum/gas_mixture/air_contents // internal reservoir + var/mode = 1 // item mode 0=off 1=charging 2=charged + var/flush = 0 // true if flush handle is pulled + var/obj/structure/disposalpipe/trunk/trunk = null // the attached pipe trunk + var/flushing = 0 // true if flushing in progress + var/flush_every_ticks = 30 //Every 30 ticks it will look whether it is ready to flush + var/flush_count = 0 //this var adds 1 once per tick. When it reaches flush_every_ticks it resets and tries to flush. + var/last_sound = 0 + var/required_mode_to_deconstruct = -1 + var/deconstructs_to = PIPE_DISPOSALS_BIN + active_power_usage = 600 + idle_power_usage = 100 + + +// create a new disposal +// find the attached trunk (if present) +/obj/machinery/disposal/New() + ..() + trunk_check() + //gas.volume = 1.05 * CELLSTANDARD + update() + +/obj/machinery/disposal/proc/trunk_check() + var/obj/structure/disposalpipe/trunk/T = locate() in loc + if(!T) + mode = 0 + flush = 0 + else + mode = initial(mode) + flush = initial(flush) + T.nicely_link_to_other_stuff(src) + +//When the disposalsoutlet is forcefully moved. Due to meteorshot (not the recall spell) +/obj/machinery/disposal/Moved(atom/OldLoc, Dir) + . = ..() + eject() + var/ptype = istype(src, /obj/machinery/disposal/deliveryChute) ? PIPE_DISPOSALS_CHUTE : PIPE_DISPOSALS_BIN //Check what disposaltype it is + var/turf/T = OldLoc + if(T.intact) + var/turf/simulated/floor/F = T + F.remove_tile(null,TRUE,TRUE) + T.visible_message("The floortile is ripped from the floor!", "You hear a loud bang!") + if(trunk) + trunk.remove_trunk_links() + var/obj/structure/disposalconstruct/C = new (loc) + transfer_fingerprints_to(C) + C.ptype = ptype + C.update() + C.anchored = 0 + C.density = 1 + qdel(src) + +/obj/machinery/disposal/Destroy() + eject() + if(trunk) + trunk.remove_trunk_links() + return ..() + +/obj/machinery/disposal/singularity_pull(S, current_size) + ..() + if(current_size >= STAGE_FIVE) + deconstruct() + +/obj/machinery/disposal/Initialize() + // this will get a copy of the air turf and take a SEND PRESSURE amount of air from it + ..() + var/atom/L = loc + var/datum/gas_mixture/env = new + env.copy_from(L.return_air()) + var/datum/gas_mixture/removed = env.remove(SEND_PRESSURE + 1) + air_contents = new + air_contents.merge(removed) + trunk_check() + +// attack by item places it in to disposal +/obj/machinery/disposal/attackby(var/obj/item/I, var/mob/user, params) + if(stat & BROKEN || !I || !user) + return + + src.add_fingerprint(user) + + if(istype(I, /obj/item/melee/energy/blade)) + to_chat(user, "You can't place that item inside the disposal unit.") + return + + if(istype(I, /obj/item/storage)) + var/obj/item/storage/S = I + if((S.allow_quick_empty || S.allow_quick_gather) && S.contents.len) + S.hide_from(user) + user.visible_message("[user] empties \the [S] into \the [src].", "You empty \the [S] into \the [src].") + for(var/obj/item/O in S.contents) + S.remove_from_storage(O, src) + S.update_icon() // For content-sensitive icons + update() + return + + var/obj/item/grab/G = I + if(istype(G)) // handle grabbed mob + if(ismob(G.affecting)) + var/mob/GM = G.affecting + for(var/mob/V in viewers(usr)) + V.show_message("[usr] starts putting [GM.name] into the disposal.", 3) + if(do_after(usr, 20, target = GM)) + GM.forceMove(src) + for(var/mob/C in viewers(src)) + C.show_message("[GM.name] has been placed in the [src] by [user].", 3) + qdel(G) + add_attack_logs(usr, GM, "Disposal'ed", !!GM.ckey ? null : ATKLOG_ALL) + return + + if(!I) + return + + if(!user.drop_item()) + return + if(I) + I.forceMove(src) + + to_chat(user, "You place \the [I] into the [src].") + for(var/mob/M in viewers(src)) + if(M == user) + continue + M.show_message("[user.name] places \the [I] into the [src].", 3) + + update() + + + + +/obj/machinery/disposal/screwdriver_act(mob/user, obj/item/I) + if(mode>0) // It's on + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(contents.len > 0) + to_chat(user, "Eject the items first!") + return + if(mode==0) // It's off but still not unscrewed + mode=-1 // Set it to doubleoff l0l + else if(mode==-1) + mode=0 + to_chat(user, "You [mode ? "unfasten": "fasten"] the screws around the power connection.") + +/obj/machinery/disposal/welder_act(mob/user, obj/item/I) + . = TRUE + if(mode != required_mode_to_deconstruct) + return + if(contents.len > 0) + to_chat(user, "Eject the items first!") + return + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_FLOOR_SLICE_MESSAGE + if(I.use_tool(src, user, 20, volume = I.tool_volume)) + WELDER_FLOOR_SLICE_SUCCESS_MESSAGE + var/obj/structure/disposalconstruct/C = new (src.loc) + C.ptype = deconstructs_to + C.update() + C.anchored = 1 + C.density = 1 + qdel(src) + +// mouse drop another mob or self +// +/obj/machinery/disposal/MouseDrop_T(mob/target, mob/user) + if(!istype(target) || target.buckled || target.has_buckled_mobs() || get_dist(user, src) > 1 || get_dist(user, target) > 1 || user.stat || istype(user, /mob/living/silicon/ai)) + return + if(isanimal(user) && target != user) return //animals cannot put mobs other than themselves into disposal + src.add_fingerprint(user) + var/target_loc = target.loc + var/msg + for(var/mob/V in viewers(usr)) + if(target == user && !user.stat && !user.IsWeakened() && !user.stunned && !user.paralysis) + V.show_message("[usr] starts climbing into the disposal.", 3) + if(target != user && !user.restrained() && !user.stat && !user.IsWeakened() && !user.stunned && !user.paralysis) + if(target.anchored) return + V.show_message("[usr] starts stuffing [target.name] into the disposal.", 3) + if(!do_after(usr, 20, target = target)) + return + if(target_loc != target.loc) + return + if(target == user && !user.stat && !user.IsWeakened() && !user.stunned && !user.paralysis) // if drop self, then climbed in + // must be awake, not stunned or whatever + msg = "[user.name] climbs into the [src]." + to_chat(user, "You climb into the [src].") + else if(target != user && !user.restrained() && !user.stat && !user.IsWeakened() && !user.stunned && !user.paralysis) + msg = "[user.name] stuffs [target.name] into the [src]!" + to_chat(user, "You stuff [target.name] into the [src]!") + + add_attack_logs(user, target, "Disposal'ed", !!target.ckey ? null : ATKLOG_ALL) + else + return + target.forceMove(src) + + for(var/mob/C in viewers(src)) + if(C == user) + continue + C.show_message(msg, 3) + + update() + return + +// attempt to move while inside +/obj/machinery/disposal/relaymove(mob/user as mob) + if(user.stat || src.flushing) + return + src.go_out(user) + return + +// leave the disposal +/obj/machinery/disposal/proc/go_out(mob/user) + if(user) + user.forceMove(loc) + update() + +// ai as human but can't flush +/obj/machinery/disposal/attack_ai(mob/user as mob) + src.add_hiddenprint(user) + ui_interact(user) + +/obj/machinery/disposal/attack_ghost(mob/user as mob) + ui_interact(user) + +// human interact with machine +/obj/machinery/disposal/attack_hand(mob/user as mob) + if(..(user)) + return 1 + + if(stat & BROKEN) + return + + if(user && user.loc == src) + to_chat(usr, "You cannot reach the controls from inside.") + return + + // Clumsy folks can only flush it. + if(user.IsAdvancedToolUser()) + ui_interact(user) + else + flush = !flush + update() + return + +/obj/machinery/disposal/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "disposal_bin.tmpl", "Waste Disposal Unit", 395, 250) + ui.open() + ui.set_auto_update(1) + +/obj/machinery/disposal/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) + var/data[0] + + var/pressure = Clamp(100* air_contents.return_pressure() / (SEND_PRESSURE), 0, 100) + var/pressure_round = round(pressure,1) + + data["isAI"] = isAI(user) + data["flushing"] = flush + data["mode"] = mode + if(mode <= 0) + data["pumpstatus"] = "N/A" + else if(mode == 1) + data["pumpstatus"] = "Pressurizing" + else if(mode == 2) + data["pumpstatus"] = "Ready" + else + data["pumpstatus"] = "Idle" + data["pressure"] = pressure_round + + return data + +/obj/machinery/disposal/Topic(href, href_list) + if(usr.loc == src) + to_chat(usr, "You cannot reach the controls from inside.") + return + + if(mode==-1 && !href_list["eject"]) // only allow ejecting if mode is -1 + to_chat(usr, "The disposal units power is disabled.") + return + + if(..()) + return + + if(stat & BROKEN) + return + + src.add_fingerprint(usr) + + if(usr.stat || usr.restrained() || src.flushing) + return + + if(istype(src.loc, /turf)) + if(href_list["pump"]) + if(text2num(href_list["pump"])) + mode = 1 + else + mode = 0 + update() + + if(!isAI(usr)) + if(href_list["handle"]) + flush = text2num(href_list["handle"]) + update() + + if(href_list["eject"]) + eject() + return + +// eject the contents of the disposal unit +/obj/machinery/disposal/proc/eject() + for(var/atom/movable/AM in src) + AM.loc = src.loc + AM.pipe_eject(0) + update() + +// update the icon & overlays to reflect mode & status +/obj/machinery/disposal/proc/update() + overlays.Cut() + if(stat & BROKEN) + icon_state = "disposal-broken" + mode = 0 + flush = 0 + return + + // flush handle + if(flush) + overlays += image('icons/obj/pipes/disposal.dmi', "dispover-handle") + + // only handle is shown if no power + if(stat & NOPOWER || mode == -1) + return + + // check for items in disposal - occupied light + if(contents.len > 0) + overlays += image('icons/obj/pipes/disposal.dmi', "dispover-full") + + // charging and ready light + if(mode == 1) + overlays += image('icons/obj/pipes/disposal.dmi', "dispover-charge") + else if(mode == 2) + overlays += image('icons/obj/pipes/disposal.dmi', "dispover-ready") + +// timed process +// charge the gas reservoir and perform flush if ready +/obj/machinery/disposal/process() + use_power = NO_POWER_USE + if(stat & BROKEN) // nothing can happen if broken + return + + flush_count++ + if( flush_count >= flush_every_ticks ) + if( contents.len ) + if(mode == 2) + spawn(0) + feedback_inc("disposal_auto_flush",1) + flush() + flush_count = 0 + + src.updateDialog() + + if(flush && air_contents.return_pressure() >= SEND_PRESSURE ) // flush can happen even without power + flush() + + if(stat & NOPOWER) // won't charge if no power + return + + use_power = IDLE_POWER_USE + + if(mode != 1) // if off or ready, no need to charge + return + + // otherwise charge + use_power = ACTIVE_POWER_USE + + var/atom/L = loc // recharging from loc turf + + var/datum/gas_mixture/env = L.return_air() + var/pressure_delta = (SEND_PRESSURE*1.01) - air_contents.return_pressure() + + if(env.temperature > 0) + var/transfer_moles = 0.1 * pressure_delta*air_contents.volume/(env.temperature * R_IDEAL_GAS_EQUATION) + + //Actually transfer the gas + var/datum/gas_mixture/removed = env.remove(transfer_moles) + air_contents.merge(removed) + air_update_turf() + + + // if full enough, switch to ready mode + if(air_contents.return_pressure() >= SEND_PRESSURE) + mode = 2 + update() + return + +// perform a flush +/obj/machinery/disposal/proc/flush() + + flushing = 1 + flick("[icon_state]-flush", src) + + var/wrapcheck = 0 + var/obj/structure/disposalholder/H = new() // virtual holder object which actually + // travels through the pipes. + //Hacky test to get drones to mail themselves through disposals. + for(var/mob/living/silicon/robot/drone/D in src) + wrapcheck = 1 + + for(var/mob/living/silicon/robot/syndicate/saboteur/R in src) + wrapcheck = 1 + + for(var/obj/item/smallDelivery/O in src) + wrapcheck = 1 + + if(wrapcheck == 1) + H.tomail = 1 + + sleep(10) + if(last_sound < world.time + 1) + playsound(src, 'sound/machines/disposalflush.ogg', 50, 0, 0) + last_sound = world.time + sleep(5) // wait for animation to finish + + + H.init(src) // copy the contents of disposer to holder + air_contents = new() // The holder just took our gas; replace it + H.start(src) // start the holder processing movement + flushing = 0 + // now reset disposal state + flush = 0 + if(mode == 2) // if was ready, + mode = 1 // switch to charging + update() + return + + +// called when area power changes +/obj/machinery/disposal/power_change() + ..() // do default setting/reset of stat NOPOWER bit + update() // update icon + return + + +// called when holder is expelled from a disposal +// should usually only occur if the pipe network is modified +/obj/machinery/disposal/proc/expel(var/obj/structure/disposalholder/H) + + var/turf/target + playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) + if(H) // Somehow, someone managed to flush a window which broke mid-transit and caused the disposal to go in an infinite loop trying to expel null, hopefully this fixes it + for(var/atom/movable/AM in H) + target = get_offset_target_turf(src.loc, rand(5)-rand(5), rand(5)-rand(5)) + + AM.loc = src.loc + AM.pipe_eject(0) + if(!istype(AM, /mob/living/silicon/robot/drone) && !istype(AM, /mob/living/silicon/robot/syndicate/saboteur)) //Poor drones kept smashing windows and taking system damage being fired out of disposals. ~Z + spawn(1) + if(AM) + AM.throw_at(target, 5, 1) + + H.vent_gas(loc) + qdel(H) + +/obj/machinery/disposal/CanPass(atom/movable/mover, turf/target, height=0) + if(istype(mover,/obj/item) && mover.throwing) + var/obj/item/I = mover + if(istype(I, /obj/item/projectile)) + return + if(prob(75)) + I.loc = src + for(var/mob/M in viewers(src)) + M.show_message("\the [I] lands in \the [src].", 3) + update() + else + for(var/mob/M in viewers(src)) + M.show_message("\the [I] bounces off of \the [src]'s rim!.", 3) + return 0 + else + return ..(mover, target, height) + + +/obj/machinery/disposal/singularity_pull(S, current_size) + if(current_size >= STAGE_FIVE) + qdel(src) + +/obj/machinery/disposal/get_remote_view_fullscreens(mob/user) + if(user.stat == DEAD || !(user.sight & (SEEOBJS|SEEMOBS))) + user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 2) + +// virtual disposal object +// travels through pipes in lieu of actual items +// contents will be items flushed by the disposal +// this allows the gas flushed to be tracked + +/obj/structure/disposalholder + invisibility = 101 + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF + var/datum/gas_mixture/gas = null // gas used to flush, will appear at exit point + var/active = 0 // true if the holder is moving, otherwise inactive + dir = 0 + var/count = 1000 //*** can travel 1000 steps before going inactive (in case of loops) + var/has_fat_guy = 0 // true if contains a fat person + var/destinationTag = 0 // changes if contains a delivery container + var/tomail = 0 //changes if contains wrapped package + var/hasmob = 0 //If it contains a mob + +/obj/structure/disposalholder/Destroy() + QDEL_NULL(gas) + active = 0 + return ..() + + // initialize a holder from the contents of a disposal unit +/obj/structure/disposalholder/proc/init(var/obj/machinery/disposal/D) + gas = D.air_contents// transfer gas resv. into holder object + + //Check for any living mobs trigger hasmob. + //hasmob effects whether the package goes to cargo or its tagged destination. + for(var/mob/living/M in D) + if(M && M.stat != 2 && !istype(M, /mob/living/silicon/robot/drone) && !istype(M, /mob/living/silicon/robot/syndicate/saboteur)) + hasmob = 1 + + //Checks 1 contents level deep. This means that players can be sent through disposals... + //...but it should require a second person to open the package. (i.e. person inside a wrapped locker) + for(var/obj/O in D) + if(O.contents) + for(var/mob/living/M in O.contents) + if(M && M.stat != 2 && !istype(M,/mob/living/silicon/robot/drone) && !istype(M, /mob/living/silicon/robot/syndicate/saboteur)) + hasmob = 1 + + // now everything inside the disposal gets put into the holder + // note AM since can contain mobs or objs + for(var/atom/movable/AM in D) + AM.loc = src + SEND_SIGNAL(AM, COMSIG_MOVABLE_DISPOSING, src, D) + if(istype(AM, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = AM + if(FAT in H.mutations) // is a human and fat? + has_fat_guy = 1 // set flag on holder + if(istype(AM, /obj/structure/bigDelivery) && !hasmob) + var/obj/structure/bigDelivery/T = AM + destinationTag = T.sortTag + if(istype(AM, /obj/item/smallDelivery) && !hasmob) + var/obj/item/smallDelivery/T = AM + destinationTag = T.sortTag + //Drones can mail themselves through maint. + if(istype(AM, /mob/living/silicon/robot/drone)) + var/mob/living/silicon/robot/drone/drone = AM + destinationTag = drone.mail_destination + if(istype(AM, /mob/living/silicon/robot/syndicate/saboteur)) + var/mob/living/silicon/robot/syndicate/saboteur/S = AM + destinationTag = S.mail_destination + if(istype(AM, /obj/item/shippingPackage) && !hasmob) + var/obj/item/shippingPackage/sp = AM + if(sp.sealed) //only sealed packages get delivered to their intended destination + destinationTag = sp.sortTag + + + // start the movement process + // argument is the disposal unit the holder started in +/obj/structure/disposalholder/proc/start(var/obj/machinery/disposal/D) + if(!D.trunk) + D.expel(src) // no trunk connected, so expel immediately + return + + loc = D.trunk + active = 1 + dir = DOWN + spawn(1) + move() // spawn off the movement process + + return + + // movement process, persists while holder is moving through pipes +/obj/structure/disposalholder/proc/move() + var/obj/structure/disposalpipe/last + while(active) + /* if(hasmob && prob(3)) + for(var/mob/living/H in src) + if(!istype(H,/mob/living/silicon/robot/drone)) //Drones use the mailing code to move through the disposal system, + H.take_overall_damage(20, 0, "Blunt Trauma") */ //horribly maim any living creature jumping down disposals. c'est la vie + + if(has_fat_guy && prob(2)) // chance of becoming stuck per segment if contains a fat guy + active = 0 + // find the fat guys + for(var/mob/living/carbon/human/H in src) + + break + sleep(1) // was 1 + var/obj/structure/disposalpipe/curr = loc + last = curr + curr = curr.transfer(src) + if(!curr) + last.expel(src, loc, dir) + + // + if(!(count--)) + active = 0 + return + + + + // find the turf which should contain the next pipe +/obj/structure/disposalholder/proc/nextloc() + return get_step(loc,dir) + + // find a matching pipe on a turf +/obj/structure/disposalholder/proc/findpipe(var/turf/T) + if(!T) + return null + + var/fdir = turn(dir, 180) // flip the movement direction + for(var/obj/structure/disposalpipe/P in T) + if(fdir & P.dpdir) // find pipe direction mask that matches flipped dir + return P + // if no matching pipe, return null + return null + + // merge two holder objects + // used when a a holder meets a stuck holder +/obj/structure/disposalholder/proc/merge(var/obj/structure/disposalholder/other) + for(var/atom/movable/AM in other) + AM.loc = src // move everything in other holder to this one + if(ismob(AM)) + var/mob/M = AM + if(M.client) // if a client mob, update eye to follow this holder + M.client.eye = src + + if(other.has_fat_guy) + has_fat_guy = 1 + qdel(other) + + + // called when player tries to move while in a pipe +/obj/structure/disposalholder/relaymove(mob/user as mob) + if(!istype(user,/mob/living)) + return + + var/mob/living/U = user + + if(U.stat || U.last_special <= world.time) + return + + U.last_special = world.time+100 + + if(src.loc) + for(var/mob/M in hearers(src.loc.loc)) + to_chat(M, "CLONG, clong!") + + playsound(src.loc, 'sound/effects/clang.ogg', 50, 0, 0) + + // called to vent all gas in holder to a location +/obj/structure/disposalholder/proc/vent_gas(var/atom/location) + if(location) + location.assume_air(gas) // vent all gas to turf + air_update_turf() + return + +// Disposal pipes + +/obj/structure/disposalpipe + icon = 'icons/obj/pipes/disposal.dmi' + name = "disposal pipe" + desc = "An underfloor disposal pipe." + anchored = 1 + density = 0 + + on_blueprints = TRUE + level = 1 // underfloor only + var/dpdir = 0 // bitmask of pipe directions + dir = 0 // dir will contain dominant direction for junction pipes + var/health = 10 // health points 0-10 + max_integrity = 200 + armor = list("melee" = 25, "bullet" = 10, "laser" = 10, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 90, "acid" = 30) + damage_deflection = 10 + layer = DISPOSAL_PIPE_LAYER // slightly lower than wires and other pipes + var/base_icon_state // initial icon state on map + + // new pipe, set the icon_state as on map +/obj/structure/disposalpipe/New() + ..() + base_icon_state = icon_state + + +// pipe is deleted +// ensure if holder is present, it is expelled +/obj/structure/disposalpipe/Destroy() + for(var/obj/structure/disposalholder/H in contents) + H.active = 0 + var/turf/T = loc + if(T.density) + // deleting pipe is inside a dense turf (wall) + // this is unlikely, but just dump out everything into the turf in case + + for(var/atom/movable/AM in H) + AM.loc = T + AM.pipe_eject(0) + qdel(H) + ..() + return + + // otherwise, do normal expel from turf + expel(H, T, 0) + return ..() + +/obj/structure/disposalpipe/singularity_pull(S, current_size) + ..() + if(current_size >= STAGE_FIVE) + deconstruct() + +// returns the direction of the next pipe object, given the entrance dir +// by default, returns the bitmask of remaining directions +/obj/structure/disposalpipe/proc/nextdir(var/fromdir) + return dpdir & (~turn(fromdir, 180)) + +// transfer the holder through this pipe segment +// overriden for special behaviour +// +/obj/structure/disposalpipe/proc/transfer(var/obj/structure/disposalholder/H) + var/nextdir = nextdir(H.dir) + H.dir = nextdir + var/turf/T = H.nextloc() + var/obj/structure/disposalpipe/P = H.findpipe(T) + + if(P) + // find other holder in next loc, if inactive merge it with current + var/obj/structure/disposalholder/H2 = locate() in P + if(H2 && !H2.active) + H.merge(H2) + + H.loc = P + else // if wasn't a pipe, then set loc to turf + H.loc = T + return null + + return P + + +// update the icon_state to reflect hidden status +/obj/structure/disposalpipe/proc/update() + var/turf/T = src.loc + hide(T.intact && !istype(T,/turf/space)) // space never hides pipes + update_icon() + +// hide called by levelupdate if turf intact status changes +// change visibility status and force update of icon +/obj/structure/disposalpipe/hide(var/intact) + invisibility = intact ? 101: 0 // hide if floor is intact + update_icon() + +// update actual icon_state depending on visibility +// if invisible, append "f" to icon_state to show faded version +// this will be revealed if a T-scanner is used +// if visible, use regular icon_state +/obj/structure/disposalpipe/update_icon() + if(invisibility) + icon_state = "[base_icon_state]f" + else + icon_state = base_icon_state + + +// expel the held objects into a turf +// called when there is a break in the pipe +// + +/obj/structure/disposalpipe/proc/expel(var/obj/structure/disposalholder/H, var/turf/T, var/direction) + + if(!T) + return + + var/turf/target + + if(T.density) // dense ouput turf, so stop holder + H.active = 0 + H.loc = src + return + if(T.intact && istype(T,/turf/simulated/floor)) //intact floor, pop the tile + var/turf/simulated/floor/F = T + new F.builtin_tile.type(H) + F.remove_tile(null,TRUE,FALSE) + + if(direction) // direction is specified + if(istype(T, /turf/space)) // if ended in space, then range is unlimited + target = get_edge_target_turf(T, direction) + else // otherwise limit to 10 tiles + target = get_ranged_target_turf(T, direction, 10) + + playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) + if(H) + for(var/atom/movable/AM in H) + AM.loc = T + AM.pipe_eject(direction) + spawn(1) + if(AM) + AM.throw_at(target, 100, 1) + H.vent_gas(T) + qdel(H) + + else // no specified direction, so throw in random direction + + playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) + if(H) + for(var/atom/movable/AM in H) + target = get_offset_target_turf(T, rand(5)-rand(5), rand(5)-rand(5)) + + AM.loc = T + AM.pipe_eject(0) + spawn(1) + if(AM) + AM.throw_at(target, 5, 1) + + H.vent_gas(T) // all gas vent to turf + qdel(H) + +// call to break the pipe +// will expel any holder inside at the time +// then delete the pipe +// remains : set to leave broken pipe pieces in place +/obj/structure/disposalpipe/proc/broken(remains = 0) + if(remains) + for(var/D in cardinal) + if(D & dpdir) + var/obj/structure/disposalpipe/broken/P = new(src.loc) + P.setDir(D) + + invisibility = 101 // make invisible (since we won't delete the pipe immediately) + var/obj/structure/disposalholder/H = locate() in src + if(H) + // holder was present + H.active = 0 + var/turf/T = src.loc + if(T.density) + // broken pipe is inside a dense turf (wall) + // this is unlikely, but just dump out everything into the turf in case + + for(var/atom/movable/AM in H) + AM.loc = T + AM.pipe_eject(0) + qdel(H) + return + + // otherwise, do normal expel from turf + if(H) + expel(H, T, 0) + + spawn(2) // delete pipe after 2 ticks to ensure expel proc finished + qdel(src) + +// pipe affected by explosion +/obj/structure/disposalpipe/ex_act(severity) + switch(severity) + if(1) + broken(0) + if(2) + health -= rand(5, 15) + healthcheck() + if(3) + health -= rand(0, 15) + healthcheck() + +// test health for brokenness +/obj/structure/disposalpipe/proc/healthcheck() + if(health < -2) + broken(0) + else if(health<1) + broken(1) + return + +//attack by item +//weldingtool: unfasten and convert to obj/disposalconstruct + +/obj/structure/disposalpipe/attackby(var/obj/item/I, var/mob/user, params) + var/turf/T = get_turf(src) + if(T.intact) + return // prevent interaction with T-scanner revealed pipes + + add_fingerprint(user) + +/obj/structure/disposalpipe/welder_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_SLICING_MESSAGE + if(!I.use_tool(src, user, 30, volume = I.tool_volume)) + return + WELDER_SLICING_SUCCESS_MESSAGE + var/obj/structure/disposalconstruct/C = new (get_turf(src)) + switch(base_icon_state) + if("pipe-s") + C.ptype = PIPE_DISPOSALS_STRAIGHT + if("pipe-c") + C.ptype = PIPE_DISPOSALS_BENT + if("pipe-j1") + C.ptype = PIPE_DISPOSALS_JUNCTION_RIGHT + if("pipe-j2") + C.ptype = PIPE_DISPOSALS_JUNCTION_LEFT + if("pipe-y") + C.ptype = PIPE_DISPOSALS_Y_JUNCTION + if("pipe-t") + C.ptype = PIPE_DISPOSALS_TRUNK + if("pipe-j1s") + C.ptype = PIPE_DISPOSALS_SORT_RIGHT + if("pipe-j2s") + C.ptype = PIPE_DISPOSALS_SORT_LEFT + src.transfer_fingerprints_to(C) + C.dir = dir + C.density = FALSE + C.anchored = TRUE + C.update() + + qdel(src) + +// *** TEST verb +//client/verb/dispstop() +// for(var/obj/structure/disposalholder/H in world) +// H.active = 0 + +// a straight or bent segment +/obj/structure/disposalpipe/segment + icon_state = "pipe-s" + +/obj/structure/disposalpipe/segment/New() + ..() + if(icon_state == "pipe-s") + dpdir = dir | turn(dir, 180) + else + dpdir = dir | turn(dir, -90) + + update() + + + +//a three-way junction with dir being the dominant direction +/obj/structure/disposalpipe/junction + icon_state = "pipe-j1" + +/obj/structure/disposalpipe/junction/New() + ..() + if(icon_state == "pipe-j1") + dpdir = dir | turn(dir, -90) | turn(dir,180) + else if(icon_state == "pipe-j2") + dpdir = dir | turn(dir, 90) | turn(dir,180) + else // pipe-y + dpdir = dir | turn(dir,90) | turn(dir, -90) + update() + + + // next direction to move + // if coming in from secondary dirs, then next is primary dir + // if coming in from primary dir, then next is equal chance of other dirs + +/obj/structure/disposalpipe/junction/nextdir(var/fromdir) + var/flipdir = turn(fromdir, 180) + if(flipdir != dir) // came from secondary dir + return dir // so exit through primary + else // came from primary + // so need to choose either secondary exit + var/mask = ..(fromdir) + + // find a bit which is set + var/setbit = 0 + if(mask & NORTH) + setbit = NORTH + else if(mask & SOUTH) + setbit = SOUTH + else if(mask & EAST) + setbit = EAST + else + setbit = WEST + + if(prob(50)) // 50% chance to choose the found bit or the other one + return setbit + else + return mask & (~setbit) + +//a three-way junction that sorts objects +/obj/structure/disposalpipe/sortjunction + + icon_state = "pipe-j1s" + var/sortType = 0 //Look at the list called TAGGERLOCATIONS in /code/_globalvars/lists/flavor_misc.dm + var/posdir = 0 + var/negdir = 0 + var/sortdir = 0 + +/obj/structure/disposalpipe/sortjunction/proc/updatedesc() + desc = "An underfloor disposal pipe with a package sorting mechanism." + if(sortType>0) + var/tag = uppertext(GLOB.TAGGERLOCATIONS[sortType]) + desc += "\nIt's tagged with [tag]" + +/obj/structure/disposalpipe/sortjunction/proc/updatedir() + posdir = dir + negdir = turn(posdir, 180) + + if(icon_state == "pipe-j1s") + sortdir = turn(posdir, -90) + else + icon_state = "pipe-j2s" + sortdir = turn(posdir, 90) + + dpdir = sortdir | posdir | negdir + +/obj/structure/disposalpipe/sortjunction/New() + ..() + updatedir() + updatedesc() + update() + return + +/obj/structure/disposalpipe/sortjunction/attackby(var/obj/item/I, var/mob/user, params) + if(..()) + return + + if(istype(I, /obj/item/destTagger)) + var/obj/item/destTagger/O = I + + if(O.currTag > 0)// Tag set + sortType = O.currTag + name = GLOB.TAGGERLOCATIONS[O.currTag] + playsound(src.loc, 'sound/machines/twobeep.ogg', 100, 1) + var/tag = uppertext(GLOB.TAGGERLOCATIONS[O.currTag]) + to_chat(user, "Changed filter to [tag]") + updatedesc() + + + // next direction to move + // if coming in from negdir, then next is primary dir or sortdir + // if coming in from posdir, then flip around and go back to posdir + // if coming in from sortdir, go to posdir + +/obj/structure/disposalpipe/sortjunction/nextdir(var/fromdir, var/sortTag) + //var/flipdir = turn(fromdir, 180) + if(fromdir != sortdir) // probably came from the negdir + + if(src.sortType == sortTag) //if destination matches filtered type... + return sortdir // exit through sortdirection + else + return posdir + else // came from sortdir + // so go with the flow to positive direction + return posdir + +/obj/structure/disposalpipe/sortjunction/transfer(var/obj/structure/disposalholder/H) + var/nextdir = nextdir(H.dir, H.destinationTag) + H.dir = nextdir + var/turf/T = H.nextloc() + var/obj/structure/disposalpipe/P = H.findpipe(T) + + if(P) + // find other holder in next loc, if inactive merge it with current + var/obj/structure/disposalholder/H2 = locate() in P + if(H2 && !H2.active) + H.merge(H2) + H.loc = P + else // if wasn't a pipe, then set loc to turf + H.loc = T + return null + + return P + + +//a three-way junction that sorts objects destined for the mail office mail table (tomail = 1) +/obj/structure/disposalpipe/wrapsortjunction + desc = "An underfloor disposal pipe which sorts wrapped and unwrapped objects." + icon_state = "pipe-j1s" + var/posdir = 0 + var/negdir = 0 + var/sortdir = 0 + +/obj/structure/disposalpipe/wrapsortjunction/New() + ..() + posdir = dir + if(icon_state == "pipe-j1s") + sortdir = turn(posdir, -90) + negdir = turn(posdir, 180) + else + icon_state = "pipe-j2s" + sortdir = turn(posdir, 90) + negdir = turn(posdir, 180) + dpdir = sortdir | posdir | negdir + + update() + return + + + // next direction to move + // if coming in from negdir, then next is primary dir or sortdir + // if coming in from posdir, then flip around and go back to posdir + // if coming in from sortdir, go to posdir + +/obj/structure/disposalpipe/wrapsortjunction/nextdir(var/fromdir, var/istomail) + //var/flipdir = turn(fromdir, 180) + if(fromdir != sortdir) // probably came from the negdir + if(istomail) //if destination matches filtered type... + return sortdir // exit through sortdirection + else + return posdir + else // came from sortdir + return posdir // so go with the flow to positive direction + +/obj/structure/disposalpipe/wrapsortjunction/transfer(var/obj/structure/disposalholder/H) + var/nextdir = nextdir(H.dir, H.tomail) + H.dir = nextdir + var/turf/T = H.nextloc() + var/obj/structure/disposalpipe/P = H.findpipe(T) + + if(P) + // find other holder in next loc, if inactive merge it with current + var/obj/structure/disposalholder/H2 = locate() in P + if(H2 && !H2.active) + H.merge(H2) + + H.loc = P + else // if wasn't a pipe, then set loc to turf + H.loc = T + return null + + return P + +//a trunk joining to a disposal bin or outlet on the same turf +/obj/structure/disposalpipe/trunk + icon_state = "pipe-t" + var/obj/linked // the linked obj/machinery/disposal or obj/disposaloutlet + +/obj/structure/disposalpipe/trunk/New() + ..() + dpdir = dir + spawn(1) + getlinked() + + update() + return + +/obj/structure/disposalpipe/trunk/Destroy() + if(istype(linked, /obj/structure/disposaloutlet)) + var/obj/structure/disposaloutlet/O = linked + O.expel(animation = 0) + else if(istype(linked, /obj/machinery/disposal)) + var/obj/machinery/disposal/D = linked + if(D.trunk == src) + D.go_out() + D.trunk = null + remove_trunk_links() + return ..() + +/obj/structure/disposalpipe/trunk/proc/getlinked() + var/obj/machinery/disposal/D = locate() in src.loc + if(D) + nicely_link_to_other_stuff(D) + return + var/obj/structure/disposaloutlet/O = locate() in src.loc + if(O) + nicely_link_to_other_stuff(O) + +/obj/structure/disposalpipe/trunk/proc/remove_trunk_links() //disposals is well-coded + if(!linked) + return + else if(istype(linked, /obj/machinery/disposal)) //jk lol + var/obj/machinery/disposal/D = linked + D.trunk = null + else if(istype(linked, /obj/structure/disposaloutlet)) //God fucking damn it + var/obj/structure/disposaloutlet/D = linked + D.linkedtrunk = null + linked = null + +/obj/structure/disposalpipe/trunk/proc/nicely_link_to_other_stuff(obj/O) + remove_trunk_links() //Breaks the connections between this trunk and the linked machinery so we don't get sent to nullspace or some shit like that + if(istype(O, /obj/machinery/disposal)) + var/obj/machinery/disposal/D = O + linked = D + D.trunk = src + else if(istype(O, /obj/structure/disposaloutlet)) + var/obj/structure/disposaloutlet/D = O + linked = D + D.linkedtrunk = src + + // Override attackby so we disallow trunkremoval when somethings ontop +/obj/structure/disposalpipe/trunk/attackby(var/obj/item/I, var/mob/user, params) + + //Disposal bins or chutes + //Disposal constructors + var/obj/structure/disposalconstruct/C = locate() in src.loc + if(C && C.anchored) + return + + var/turf/T = src.loc + if(T.intact) + return // prevent interaction with T-scanner revealed pipes + src.add_fingerprint(user) + + // would transfer to next pipe segment, but we are in a trunk + // if not entering from disposal bin, + // transfer to linked object (outlet or bin) + +/obj/structure/disposalpipe/trunk/transfer(obj/structure/disposalholder/H) + if(!H) + return + if(H.dir == DOWN) // we just entered from a disposer + return ..() // so do base transfer proc + // otherwise, go to the linked object + if(!linked) + expel(H, loc, FALSE) // expel at turf + else if(istype(linked, /obj/structure/disposaloutlet)) + var/obj/structure/disposaloutlet/DO = linked + for(var/atom/movable/AM in H) + AM.forceMove(DO) + qdel(H) + H.vent_gas(loc) + DO.expel() + else if(istype(linked, /obj/machinery/disposal)) + var/obj/machinery/disposal/D = linked + H.forceMove(D) + D.expel(H) // expel at disposal + else //just in case + expel(H, loc, FALSE) + // nextdir + +/obj/structure/disposalpipe/trunk/nextdir(var/fromdir) + if(fromdir == DOWN) + return dir + else + return 0 + +// a broken pipe +/obj/structure/disposalpipe/broken + icon_state = "pipe-b" + dpdir = 0 // broken pipes have dpdir=0 so they're not found as 'real' pipes + // i.e. will be treated as an empty turf + desc = "A broken piece of disposal pipe." + +/obj/structure/disposalpipe/broken/New() + ..() + update() + return + +/obj/structure/disposalpipe/broken/welder_act(mob/user, obj/item/I) + if(I.use_tool(src, user, 0, volume = I.tool_volume)) + to_chat(user, "You remove [src]!") + I.play_tool_sound(src, I.tool_volume) + qdel(src) + return TRUE + +// the disposal outlet machine + +/obj/structure/disposaloutlet + name = "disposal outlet" + desc = "An outlet for the pneumatic disposal system." + icon = 'icons/obj/pipes/disposal.dmi' + icon_state = "outlet" + density = 1 + anchored = 1 + var/active = 0 + var/turf/target // this will be where the output objects are 'thrown' to. + var/obj/structure/disposalpipe/trunk/linkedtrunk + var/mode = 0 + +/obj/structure/disposaloutlet/New() + ..() + spawn(1) + target = get_ranged_target_turf(src, dir, 10) + var/obj/structure/disposalpipe/trunk/T = locate() in loc + if(T) + T.nicely_link_to_other_stuff(src) + +/obj/structure/disposaloutlet/Destroy() + if(linkedtrunk) + linkedtrunk.remove_trunk_links() + expel(FALSE) + return ..() + + +// expel the contents of the outlet +/obj/structure/disposaloutlet/proc/expel(animation = TRUE) + if(animation) + flick("outlet-open", src) + playsound(src, 'sound/machines/warning-buzzer.ogg', 50, 0, 0) + sleep(20) //wait until correct animation frame + playsound(src, 'sound/machines/hiss.ogg', 50, 0, 0) + for(var/atom/movable/AM in contents) + AM.forceMove(loc) + AM.pipe_eject(dir) + if(istype(AM,/mob/living/silicon/robot/drone) || istype(AM, /mob/living/silicon/robot/syndicate/saboteur)) //Drones keep smashing windows from being fired out of chutes. Bad for the station. ~Z + return + spawn(5) + if(QDELETED(AM)) + return + AM.throw_at(target, 3, 1) + + +/obj/structure/disposaloutlet/attackby(var/obj/item/I, var/mob/user, params) + if(!I || !user) + return + src.add_fingerprint(user) + if(istype(I, /obj/item/screwdriver)) + if(mode==0) + mode=1 + playsound(src.loc, I.usesound, 50, 1) + to_chat(user, "You remove the screws around the power connection.") + return + else if(mode==1) + mode=0 + playsound(src.loc, I.usesound, 50, 1) + to_chat(user, "You attach the screws around the power connection.") + return + +/obj/structure/disposaloutlet/welder_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_FLOOR_SLICE_MESSAGE + if(I.use_tool(src, user, 20, volume = I.tool_volume)) + WELDER_FLOOR_SLICE_SUCCESS_MESSAGE + var/obj/structure/disposalconstruct/C = new (src.loc) + C.ptype = PIPE_DISPOSALS_OUTLET + C.update() + C.anchored = TRUE + C.density = TRUE + transfer_fingerprints_to(C) + qdel(src) + +//When the disposalsoutlet is forcefully moved. Due to meteorshot or the recall item spell for instance +/obj/structure/disposaloutlet/Moved(atom/OldLoc, Dir) + . = ..() + var/turf/T = OldLoc + if(T.intact) + var/turf/simulated/floor/F = T + F.remove_tile(null,TRUE,TRUE) + T.visible_message("The floortile is ripped from the floor!", "You hear a loud bang!") + if(linkedtrunk) + linkedtrunk.remove_trunk_links() + var/obj/structure/disposalconstruct/C = new (loc) + transfer_fingerprints_to(C) + C.ptype = PIPE_DISPOSALS_OUTLET + C.update() + C.anchored = 0 + C.density = 1 + qdel(src) + +// called when movable is expelled from a disposal pipe or outlet +// by default does nothing, override for special behaviour + +/atom/movable/proc/pipe_eject(var/direction) + return + +// check if mob has client, if so restore client view on eject +/mob/pipe_eject(var/direction) + reset_perspective(null) + +/obj/effect/decal/cleanable/blood/gibs/pipe_eject(var/direction) + var/list/dirs + if(direction) + dirs = list( direction, turn(direction, -45), turn(direction, 45)) + else + dirs = alldirs.Copy() + + src.streak(dirs) + +/obj/effect/decal/cleanable/blood/gibs/robot/gib/pipe_eject(var/direction) + var/list/dirs + if(direction) + dirs = list( direction, turn(direction, -45), turn(direction, 45)) + else + dirs = alldirs.Copy() + + src.streak(dirs) diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm index fd94341d93c..7d2be6953c6 100755 --- a/code/modules/recycling/sortingmachinery.dm +++ b/code/modules/recycling/sortingmachinery.dm @@ -1,451 +1,444 @@ -/obj/structure/bigDelivery - name = "large parcel" - desc = "A big wrapped package." - icon = 'icons/obj/storage.dmi' - icon_state = "deliverycloset" - density = 1 - mouse_drag_pointer = MOUSE_ACTIVE_POINTER - var/obj/wrapped = null - var/init_welded = 0 - var/giftwrapped = 0 - var/sortTag = 0 - -/obj/structure/bigDelivery/Destroy() - var/turf/T = get_turf(src) - for(var/atom/movable/AM in contents) - AM.forceMove(T) - return ..() - -/obj/structure/bigDelivery/ex_act(severity) - for(var/atom/movable/AM in contents) - AM.ex_act() - CHECK_TICK - ..() - -/obj/structure/bigDelivery/attack_hand(mob/user as mob) - playsound(src.loc, 'sound/items/poster_ripped.ogg', 50, 1) - if(wrapped) - wrapped.loc = get_turf(src) - if(istype(wrapped, /obj/structure/closet)) - var/obj/structure/closet/O = wrapped - O.welded = init_welded - var/turf/T = get_turf(src) - for(var/atom/movable/AM in src) - AM.loc = T - - qdel(src) - -/obj/structure/bigDelivery/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/destTagger)) - var/obj/item/destTagger/O = W - - if(sortTag != O.currTag) - var/tag = uppertext(GLOB.TAGGERLOCATIONS[O.currTag]) - to_chat(user, "*[tag]*") - sortTag = O.currTag - playsound(loc, 'sound/machines/twobeep.ogg', 100, 1) - - else if(istype(W, /obj/item/shippingPackage)) - var/obj/item/shippingPackage/sp = W - if(sp.sealed) - return - else - sortTag = sp.sortTag - to_chat(user, "You rip the label off the shipping package and affix it to [src].") - qdel(sp) - playsound(loc, 'sound/items/poster_ripped.ogg', 50, 1) - - else if(istype(W, /obj/item/pen)) - var/str = copytext(sanitize(input(user,"Label text?","Set label","")),1,MAX_NAME_LEN) - if(!str || !length(str)) - to_chat(user, "Invalid text.") - return - user.visible_message("[user] labels [src] as [str].") - name = "[name] ([str])" - - else if(istype(W, /obj/item/stack/wrapping_paper) && !giftwrapped) - var/obj/item/stack/wrapping_paper/WP = W - if(WP.use(3)) - user.visible_message("[user] wraps the package in festive paper!") - giftwrapped = 1 - if(istype(wrapped, /obj/structure/closet/crate)) - icon_state = "giftcrate" - else - icon_state = "giftcloset" - if(WP.amount <= 0 && !WP.loc) //if we used our last wrapping paper, drop a cardboard tube - new /obj/item/c_tube( get_turf(user) ) - else - to_chat(user, "You need more paper.") - else - return ..() - -/obj/item/smallDelivery - name = "small parcel" - desc = "A small wrapped package." - icon = 'icons/obj/storage.dmi' - icon_state = "deliverycrateSmall" - item_state = "deliverypackage" - var/obj/item/wrapped = null - var/giftwrapped = 0 - var/sortTag = 0 - -/obj/item/smallDelivery/ex_act(severity) - for(var/atom/movable/AM in contents) - AM.ex_act() - CHECK_TICK - ..() - -/obj/item/smallDelivery/attack_self(mob/user as mob) - if(wrapped && wrapped.loc) //sometimes items can disappear. For example, bombs. --rastaf0 - wrapped.loc = user.loc - if(ishuman(user)) - user.put_in_hands(wrapped) - else - wrapped.loc = get_turf(src) - playsound(src.loc, 'sound/items/poster_ripped.ogg', 50, 1) - qdel(src) - -/obj/item/smallDelivery/attackby(obj/item/W as obj, mob/user as mob, params) - if(istype(W, /obj/item/destTagger)) - var/obj/item/destTagger/O = W - - if(sortTag != O.currTag) - var/tag = uppertext(GLOB.TAGGERLOCATIONS[O.currTag]) - to_chat(user, "*[tag]*") - sortTag = O.currTag - playsound(loc, 'sound/machines/twobeep.ogg', 100, 1) - - else if(istype(W, /obj/item/shippingPackage)) - var/obj/item/shippingPackage/sp = W - if(sp.sealed) - return - else - sortTag = sp.sortTag - to_chat(user, "You rip the label off the shipping package and affix it to [src].") - qdel(sp) - playsound(loc, 'sound/items/poster_ripped.ogg', 50, 1) - - else if(istype(W, /obj/item/pen)) - var/str = copytext(sanitize(input(user,"Label text?","Set label","")),1,MAX_NAME_LEN) - if(!str || !length(str)) - to_chat(user, "Invalid text.") - return - user.visible_message("[user] labels [src] as [str].") - name = "[name] ([str])" - - else if(istype(W, /obj/item/stack/wrapping_paper) && !giftwrapped) - var/obj/item/stack/wrapping_paper/WP = W - if(WP.use(1)) - icon_state = "giftcrate[wrapped.w_class]" - giftwrapped = 1 - user.visible_message("[user] wraps the package in festive paper!") - if(WP.amount <= 0 && !WP.loc) //if we used our last wrapping paper, drop a cardboard tube - new /obj/item/c_tube( get_turf(user) ) - else - to_chat(user, "You need more paper.") - else - return ..() - -/obj/item/stack/packageWrap - name = "package wrapper" - icon = 'icons/obj/items.dmi' - icon_state = "deliveryPaper" - singular_name = "package wrapper" - flags = NOBLUDGEON - amount = 25 - max_amount = 25 - resistance_flags = FLAMMABLE - -/obj/item/stack/packageWrap/afterattack(var/obj/target as obj, mob/user as mob, proximity) - if(!proximity) return - if(!istype(target)) //this really shouldn't be necessary (but it is). -Pete - return - if(istype(target, /obj/item/smallDelivery) || istype(target,/obj/structure/bigDelivery) \ - || istype(target, /obj/item/evidencebag) || istype(target, /obj/structure/closet/body_bag)) - return - if(target.anchored) - return - if(target in user) - return - - if(istype(target, /obj/item) && !(istype(target, /obj/item/storage) && !istype(target,/obj/item/storage/box) && !istype(target, /obj/item/shippingPackage))) - var/obj/item/O = target - if(use(1)) - var/obj/item/smallDelivery/P = new /obj/item/smallDelivery(get_turf(O.loc)) //Aaannd wrap it up! - if(!istype(O.loc, /turf)) - if(user.client) - user.client.screen -= O - P.wrapped = O - O.loc = P - var/i = round(O.w_class) - if(i in list(1,2,3,4,5)) - P.icon_state = "deliverycrate[i]" - P.w_class = i - P.add_fingerprint(usr) - O.add_fingerprint(usr) - add_fingerprint(usr) - else - return - else if(istype(target, /obj/structure/closet/crate)) - var/obj/structure/closet/crate/O = target - if(O.opened) - return - if(amount >= 3 && do_after_once(user, 15, target = target)) - if(O.opened || !use(3)) - return - var/obj/structure/bigDelivery/P = new /obj/structure/bigDelivery(get_turf(O.loc)) - P.icon_state = "deliverycrate" - P.wrapped = O - O.loc = P - else - to_chat(user, "You need more paper.") - return - else if(istype (target, /obj/structure/closet)) - var/obj/structure/closet/O = target - if(O.opened) - return - if(amount >= 3 && do_after_once(user, 15, target = target)) - if(O.opened || !use(3)) - return - var/obj/structure/bigDelivery/P = new /obj/structure/bigDelivery(get_turf(O.loc)) - P.wrapped = O - P.init_welded = O.welded - O.welded = 1 - O.loc = P - else - to_chat(user, "You need more paper.") - return - else - to_chat(user, "The object you are trying to wrap is unsuitable for the sorting machinery.") - return - - user.visible_message("[user] wraps [target].") - user.create_attack_log("Has used [name] on [target]") - - if(amount <= 0 && !src.loc) //if we used our last wrapping paper, drop a cardboard tube - new /obj/item/c_tube( get_turf(user) ) - return - -/obj/item/destTagger - name = "destination tagger" - desc = "Used to set the destination of properly wrapped packages." - icon = 'icons/obj/device.dmi' - icon_state = "dest_tagger" - var/currTag = 0 - //The whole system for the sorttype var is determined based on the order of this list, - //disposals must always be 1, since anything that's untagged will automatically go to disposals, or sorttype = 1 --Superxpdude - - w_class = WEIGHT_CLASS_TINY - item_state = "electronic" - flags = CONDUCT - slot_flags = SLOT_BELT - -/obj/item/destTagger/proc/openwindow(mob/user as mob) - var/dat = "

        TagMaster 2.2

        " - - dat += "" - for(var/i = 1, i <= GLOB.TAGGERLOCATIONS.len, i++) - dat += "" - - if(i%4==0) - dat += "" - - dat += "
        [GLOB.TAGGERLOCATIONS[i]]

        Current Selection: [currTag ? GLOB.TAGGERLOCATIONS[currTag] : "None"]
        " - - user << browse(dat, "window=destTagScreen;size=450x350") - onclose(user, "destTagScreen") - -/obj/item/destTagger/attack_self(mob/user as mob) - openwindow(user) - return - -/obj/item/destTagger/Topic(href, href_list) - src.add_fingerprint(usr) - if(href_list["nextTag"]) - var/n = text2num(href_list["nextTag"]) - src.currTag = n - openwindow(usr) - -/obj/machinery/disposal/deliveryChute - name = "Delivery chute" - desc = "A chute for big and small packages alike!" - density = 1 - icon_state = "intake" - - var/c_mode = 0 - -/obj/machinery/disposal/deliveryChute/New() - ..() - spawn(5) - trunk = locate() in src.loc - if(trunk) - trunk.linked = src // link the pipe trunk to self - -/obj/machinery/disposal/deliveryChute/interact() - return - -/obj/machinery/disposal/deliveryChute/update() - return - -/obj/machinery/disposal/deliveryChute/Bumped(atom/movable/AM) //Go straight into the chute - if(istype(AM, /obj/item/projectile)) return - switch(dir) - if(NORTH) - if(AM.loc.y != src.loc.y+1) return - if(EAST) - if(AM.loc.x != src.loc.x+1) return - if(SOUTH) - if(AM.loc.y != src.loc.y-1) return - if(WEST) - if(AM.loc.x != src.loc.x-1) return - - if(istype(AM, /obj)) - var/obj/O = AM - O.loc = src - else if(istype(AM, /mob)) - var/mob/M = AM - M.loc = src - src.flush() - -/obj/machinery/disposal/deliveryChute/flush() - flushing = 1 - flick("intake-closing", src) - var/deliveryCheck = 0 - var/obj/structure/disposalholder/H = new() // virtual holder object which actually - // travels through the pipes. - for(var/obj/structure/bigDelivery/O in src) - deliveryCheck = 1 - if(O.sortTag == 0) - O.sortTag = 1 - for(var/obj/item/smallDelivery/O in src) - deliveryCheck = 1 - if(O.sortTag == 0) - O.sortTag = 1 - for(var/obj/item/shippingPackage/O in src) - deliveryCheck = 1 - if(!O.sealed || O.sortTag == 0) //unsealed or untagged shipping packages will default to disposals - O.sortTag = 1 - if(deliveryCheck == 0) - H.destinationTag = 1 - - sleep(10) - playsound(src, 'sound/machines/disposalflush.ogg', 50, 0, 0) - sleep(5) // wait for animation to finish - - H.init(src) // copy the contents of disposer to holder - air_contents = new() // The holder just took our gas; replace it - H.start(src) // start the holder processing movement - flushing = 0 - // now reset disposal state - flush = 0 - if(mode == 2) // if was ready, - mode = 1 // switch to charging - update() - return - -/obj/machinery/disposal/deliveryChute/attackby(obj/item/I, mob/user, params) - if(!I || !user) - return - - if(istype(I, /obj/item/screwdriver)) - if(c_mode==0) - c_mode=1 - playsound(src.loc, I.usesound, 50, 1) - to_chat(user, "You remove the screws around the power connection.") - return - else if(c_mode==1) - c_mode=0 - playsound(src.loc, I.usesound, 50, 1) - to_chat(user, "You attach the screws around the power connection.") - return - else if(istype(I,/obj/item/weldingtool) && c_mode==1) - var/obj/item/weldingtool/W = I - if(W.remove_fuel(0,user)) - playsound(src.loc, W.usesound, 100, 1) - to_chat(user, "You start slicing the floorweld off the delivery chute.") - if(do_after(user, 20 * W.toolspeed, target = src)) - if(!src || !W.isOn()) return - to_chat(user, "You sliced the floorweld off the delivery chute.") - var/obj/structure/disposalconstruct/C = new (src.loc) - C.ptype = PIPE_DISPOSALS_CHUTE - C.update() - C.anchored = 1 - C.density = 1 - qdel(src) - return - else - to_chat(user, "You need more welding fuel to complete this task.") - return - -/obj/item/shippingPackage - name = "Shipping package" - desc = "A pre-labeled package for shipping an item to coworkers." - icon = 'icons/obj/storage.dmi' - icon_state = "shippack" - var/obj/item/wrapped = null - var/sortTag = 0 - var/sealed = 0 - -/obj/item/shippingPackage/attackby(obj/item/O, mob/user, params) - if(sealed) - if(istype(O, /obj/item/pen)) - var/str = copytext(sanitize(input(user,"Intended recipient?","Address","")),1,MAX_NAME_LEN) - if(!str || !length(str)) - to_chat(user, "Invalid text.") - return - user.visible_message("[user] addresses [src] to [str].") - name = "Shipping package (RE: [str])" - return - if(wrapped) - to_chat(user, "[src] already contains \a [wrapped].") - return - if(istype(O, /obj/item) && !istype(O, /obj/item/storage) && !istype(O, /obj/item/shippingPackage)) - if(!user.canUnEquip(O)) - to_chat(user, "[O] is stuck to your hand, you cannot put it in [src]!") - return - if(O.w_class > 3) - to_chat(user, "[O] is too large to fit in [src].") - else - wrapped = O - user.unEquip(O) - O.forceMove(src) - O.add_fingerprint(usr) - add_fingerprint(usr) - to_chat(user, "You put [O] in [src].") - -/obj/item/shippingPackage/attack_self(mob/user) - if(sealed) - to_chat(user, "You tear open [src], dropping the contents onto the floor.") - playsound(loc, 'sound/items/poster_ripped.ogg', 50, 1) - user.unEquip(src) - wrapped.forceMove(get_turf(user)) - wrapped = null - qdel(src) - else if(wrapped) - switch(alert("Select an action:",, "Remove Object", "Seal Package", "Cancel")) - if("Remove Object") - to_chat(user, "You shake out [src]'s contents onto the floor.") - wrapped.forceMove(get_turf(user)) - wrapped = null - if("Seal Package") - to_chat(user, "You seal [src], preparing it for delivery.") - icon_state = "shippack_sealed" - sealed = 1 - update_desc() - else - if(alert("Do you want to tear up the package?",, "Yes", "No") == "Yes") - to_chat(user, "You shred [src].") - playsound(loc, 'sound/items/poster_ripped.ogg', 50, 1) - user.unEquip(src) - qdel(src) - -/obj/item/shippingPackage/proc/update_desc() - desc = "A pre-labeled package for shipping an item to coworkers." - if(sortTag) - desc += " The label says \"Deliver to [GLOB.TAGGERLOCATIONS[sortTag]]\"." - if(!sealed) - desc += " The package is not sealed." - -/obj/item/shippingPackage/Destroy() - QDEL_NULL(wrapped) - return ..() +/obj/structure/bigDelivery + name = "large parcel" + desc = "A big wrapped package." + icon = 'icons/obj/storage.dmi' + icon_state = "deliverycloset" + density = 1 + mouse_drag_pointer = MOUSE_ACTIVE_POINTER + var/obj/wrapped = null + var/init_welded = 0 + var/giftwrapped = 0 + var/sortTag = 0 + +/obj/structure/bigDelivery/Destroy() + var/turf/T = get_turf(src) + for(var/atom/movable/AM in contents) + AM.forceMove(T) + return ..() + +/obj/structure/bigDelivery/ex_act(severity) + for(var/atom/movable/AM in contents) + AM.ex_act() + CHECK_TICK + ..() + +/obj/structure/bigDelivery/attack_hand(mob/user as mob) + playsound(src.loc, 'sound/items/poster_ripped.ogg', 50, 1) + if(wrapped) + wrapped.loc = get_turf(src) + if(istype(wrapped, /obj/structure/closet)) + var/obj/structure/closet/O = wrapped + O.welded = init_welded + var/turf/T = get_turf(src) + for(var/atom/movable/AM in src) + AM.loc = T + + qdel(src) + +/obj/structure/bigDelivery/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/destTagger)) + var/obj/item/destTagger/O = W + + if(sortTag != O.currTag) + var/tag = uppertext(GLOB.TAGGERLOCATIONS[O.currTag]) + to_chat(user, "*[tag]*") + sortTag = O.currTag + playsound(loc, 'sound/machines/twobeep.ogg', 100, 1) + + else if(istype(W, /obj/item/shippingPackage)) + var/obj/item/shippingPackage/sp = W + if(sp.sealed) + return + else + sortTag = sp.sortTag + to_chat(user, "You rip the label off the shipping package and affix it to [src].") + qdel(sp) + playsound(loc, 'sound/items/poster_ripped.ogg', 50, 1) + + else if(istype(W, /obj/item/pen)) + var/str = copytext(sanitize(input(user,"Label text?","Set label","")),1,MAX_NAME_LEN) + if(!str || !length(str)) + to_chat(user, "Invalid text.") + return + user.visible_message("[user] labels [src] as [str].") + name = "[name] ([str])" + + else if(istype(W, /obj/item/stack/wrapping_paper) && !giftwrapped) + var/obj/item/stack/wrapping_paper/WP = W + if(WP.use(3)) + user.visible_message("[user] wraps the package in festive paper!") + giftwrapped = 1 + if(istype(wrapped, /obj/structure/closet/crate)) + icon_state = "giftcrate" + else + icon_state = "giftcloset" + if(WP.amount <= 0 && !WP.loc) //if we used our last wrapping paper, drop a cardboard tube + new /obj/item/c_tube( get_turf(user) ) + else + to_chat(user, "You need more paper.") + else + return ..() + +/obj/item/smallDelivery + name = "small parcel" + desc = "A small wrapped package." + icon = 'icons/obj/storage.dmi' + icon_state = "deliverycrateSmall" + item_state = "deliverypackage" + var/obj/item/wrapped = null + var/giftwrapped = 0 + var/sortTag = 0 + +/obj/item/smallDelivery/ex_act(severity) + for(var/atom/movable/AM in contents) + AM.ex_act() + CHECK_TICK + ..() + +/obj/item/smallDelivery/attack_self(mob/user as mob) + if(wrapped && wrapped.loc) //sometimes items can disappear. For example, bombs. --rastaf0 + wrapped.loc = user.loc + if(ishuman(user)) + user.put_in_hands(wrapped) + else + wrapped.loc = get_turf(src) + playsound(src.loc, 'sound/items/poster_ripped.ogg', 50, 1) + qdel(src) + +/obj/item/smallDelivery/attackby(obj/item/W as obj, mob/user as mob, params) + if(istype(W, /obj/item/destTagger)) + var/obj/item/destTagger/O = W + + if(sortTag != O.currTag) + var/tag = uppertext(GLOB.TAGGERLOCATIONS[O.currTag]) + to_chat(user, "*[tag]*") + sortTag = O.currTag + playsound(loc, 'sound/machines/twobeep.ogg', 100, 1) + + else if(istype(W, /obj/item/shippingPackage)) + var/obj/item/shippingPackage/sp = W + if(sp.sealed) + return + else + sortTag = sp.sortTag + to_chat(user, "You rip the label off the shipping package and affix it to [src].") + qdel(sp) + playsound(loc, 'sound/items/poster_ripped.ogg', 50, 1) + + else if(istype(W, /obj/item/pen)) + var/str = copytext(sanitize(input(user,"Label text?","Set label","")),1,MAX_NAME_LEN) + if(!str || !length(str)) + to_chat(user, "Invalid text.") + return + user.visible_message("[user] labels [src] as [str].") + name = "[name] ([str])" + + else if(istype(W, /obj/item/stack/wrapping_paper) && !giftwrapped) + var/obj/item/stack/wrapping_paper/WP = W + if(WP.use(1)) + icon_state = "giftcrate[wrapped.w_class]" + giftwrapped = 1 + user.visible_message("[user] wraps the package in festive paper!") + if(WP.amount <= 0 && !WP.loc) //if we used our last wrapping paper, drop a cardboard tube + new /obj/item/c_tube( get_turf(user) ) + else + to_chat(user, "You need more paper.") + else + return ..() + +/obj/item/stack/packageWrap + name = "package wrapper" + icon = 'icons/obj/items.dmi' + icon_state = "deliveryPaper" + singular_name = "package wrapper" + flags = NOBLUDGEON + amount = 25 + max_amount = 25 + resistance_flags = FLAMMABLE + +/obj/item/stack/packageWrap/afterattack(var/obj/target as obj, mob/user as mob, proximity) + if(!proximity) return + if(!istype(target)) //this really shouldn't be necessary (but it is). -Pete + return + if(istype(target, /obj/item/smallDelivery) || istype(target,/obj/structure/bigDelivery) \ + || istype(target, /obj/item/evidencebag) || istype(target, /obj/structure/closet/body_bag)) + return + if(target.anchored) + return + if(target in user) + return + + if(istype(target, /obj/item) && !(istype(target, /obj/item/storage) && !istype(target,/obj/item/storage/box) && !istype(target, /obj/item/shippingPackage))) + var/obj/item/O = target + if(use(1)) + var/obj/item/smallDelivery/P = new /obj/item/smallDelivery(get_turf(O.loc)) //Aaannd wrap it up! + if(!istype(O.loc, /turf)) + if(user.client) + user.client.screen -= O + P.wrapped = O + O.loc = P + var/i = round(O.w_class) + if(i in list(1,2,3,4,5)) + P.icon_state = "deliverycrate[i]" + P.w_class = i + P.add_fingerprint(usr) + O.add_fingerprint(usr) + add_fingerprint(usr) + else + return + else if(istype(target, /obj/structure/closet/crate)) + var/obj/structure/closet/crate/O = target + if(O.opened) + return + if(amount >= 3 && do_after_once(user, 15, target = target)) + if(O.opened || !use(3)) + return + var/obj/structure/bigDelivery/P = new /obj/structure/bigDelivery(get_turf(O.loc)) + P.icon_state = "deliverycrate" + P.wrapped = O + O.loc = P + else + to_chat(user, "You need more paper.") + return + else if(istype (target, /obj/structure/closet)) + var/obj/structure/closet/O = target + if(O.opened) + return + if(amount >= 3 && do_after_once(user, 15, target = target)) + if(O.opened || !use(3)) + return + var/obj/structure/bigDelivery/P = new /obj/structure/bigDelivery(get_turf(O.loc)) + P.wrapped = O + P.init_welded = O.welded + O.welded = 1 + O.loc = P + else + to_chat(user, "You need more paper.") + return + else + to_chat(user, "The object you are trying to wrap is unsuitable for the sorting machinery.") + return + + user.visible_message("[user] wraps [target].") + user.create_attack_log("Has used [name] on [target]") + + if(amount <= 0 && !src.loc) //if we used our last wrapping paper, drop a cardboard tube + new /obj/item/c_tube( get_turf(user) ) + return + +/obj/item/destTagger + name = "destination tagger" + desc = "Used to set the destination of properly wrapped packages." + icon = 'icons/obj/device.dmi' + icon_state = "dest_tagger" + var/currTag = 0 + //The whole system for the sorttype var is determined based on the order of this list, + //disposals must always be 1, since anything that's untagged will automatically go to disposals, or sorttype = 1 --Superxpdude + + w_class = WEIGHT_CLASS_TINY + item_state = "electronic" + flags = CONDUCT + slot_flags = SLOT_BELT + +/obj/item/destTagger/proc/openwindow(mob/user as mob) + var/dat = "

        TagMaster 2.2

        " + + dat += "" + for(var/i = 1, i <= GLOB.TAGGERLOCATIONS.len, i++) + dat += "" + + if(i%4==0) + dat += "" + + dat += "
        [GLOB.TAGGERLOCATIONS[i]]

        Current Selection: [currTag ? GLOB.TAGGERLOCATIONS[currTag] : "None"]
        " + + user << browse(dat, "window=destTagScreen;size=450x350") + onclose(user, "destTagScreen") + +/obj/item/destTagger/attack_self(mob/user as mob) + openwindow(user) + return + +/obj/item/destTagger/Topic(href, href_list) + src.add_fingerprint(usr) + if(href_list["nextTag"]) + var/n = text2num(href_list["nextTag"]) + src.currTag = n + openwindow(usr) + +/obj/machinery/disposal/deliveryChute + name = "Delivery chute" + desc = "A chute for big and small packages alike!" + density = 1 + icon_state = "intake" + required_mode_to_deconstruct = 1 + deconstructs_to = PIPE_DISPOSALS_CHUTE + var/can_deconstruct = FALSE + +/obj/machinery/disposal/deliveryChute/New() + ..() + spawn(5) + trunk = locate() in src.loc + if(trunk) + trunk.linked = src // link the pipe trunk to self + +/obj/machinery/disposal/deliveryChute/interact() + return + +/obj/machinery/disposal/deliveryChute/update() + return + +/obj/machinery/disposal/deliveryChute/Bumped(atom/movable/AM) //Go straight into the chute + if(istype(AM, /obj/item/projectile)) return + switch(dir) + if(NORTH) + if(AM.loc.y != src.loc.y+1) return + if(EAST) + if(AM.loc.x != src.loc.x+1) return + if(SOUTH) + if(AM.loc.y != src.loc.y-1) return + if(WEST) + if(AM.loc.x != src.loc.x-1) return + + if(istype(AM, /obj)) + var/obj/O = AM + O.loc = src + else if(istype(AM, /mob)) + var/mob/M = AM + M.loc = src + src.flush() + +/obj/machinery/disposal/deliveryChute/flush() + flushing = 1 + flick("intake-closing", src) + var/deliveryCheck = 0 + var/obj/structure/disposalholder/H = new() // virtual holder object which actually + // travels through the pipes. + for(var/obj/structure/bigDelivery/O in src) + deliveryCheck = 1 + if(O.sortTag == 0) + O.sortTag = 1 + for(var/obj/item/smallDelivery/O in src) + deliveryCheck = 1 + if(O.sortTag == 0) + O.sortTag = 1 + for(var/obj/item/shippingPackage/O in src) + deliveryCheck = 1 + if(!O.sealed || O.sortTag == 0) //unsealed or untagged shipping packages will default to disposals + O.sortTag = 1 + if(deliveryCheck == 0) + H.destinationTag = 1 + + sleep(10) + playsound(src, 'sound/machines/disposalflush.ogg', 50, 0, 0) + sleep(5) // wait for animation to finish + + H.init(src) // copy the contents of disposer to holder + air_contents = new() // The holder just took our gas; replace it + H.start(src) // start the holder processing movement + flushing = 0 + // now reset disposal state + flush = 0 + if(mode == 2) // if was ready, + mode = 1 // switch to charging + update() + return + +/obj/machinery/disposal/deliveryChute/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + can_deconstruct = !can_deconstruct + to_chat(user, "You [can_deconstruct ? "unfasten": "fasten"] the screws around the power connection.") + +/obj/machinery/disposal/deliveryChute/welder_act(mob/user, obj/item/I) + . = TRUE + if(!can_deconstruct) + return + if(contents.len > 0) + to_chat(user, "Eject the items first!") + return + if(!I.tool_use_check(user, 0)) + return + WELDER_ATTEMPT_FLOOR_SLICE_MESSAGE + if(I.use_tool(src, user, 20, volume = I.tool_volume)) + WELDER_FLOOR_SLICE_SUCCESS_MESSAGE + var/obj/structure/disposalconstruct/C = new (loc) + C.ptype = deconstructs_to + C.update() + C.anchored = TRUE + C.density = TRUE + qdel(src) + +/obj/item/shippingPackage + name = "Shipping package" + desc = "A pre-labeled package for shipping an item to coworkers." + icon = 'icons/obj/storage.dmi' + icon_state = "shippack" + var/obj/item/wrapped = null + var/sortTag = 0 + var/sealed = 0 + +/obj/item/shippingPackage/attackby(obj/item/O, mob/user, params) + if(sealed) + if(istype(O, /obj/item/pen)) + var/str = copytext(sanitize(input(user,"Intended recipient?","Address","")),1,MAX_NAME_LEN) + if(!str || !length(str)) + to_chat(user, "Invalid text.") + return + user.visible_message("[user] addresses [src] to [str].") + name = "Shipping package (RE: [str])" + return + if(wrapped) + to_chat(user, "[src] already contains \a [wrapped].") + return + if(istype(O, /obj/item) && !istype(O, /obj/item/storage) && !istype(O, /obj/item/shippingPackage)) + if(!user.canUnEquip(O)) + to_chat(user, "[O] is stuck to your hand, you cannot put it in [src]!") + return + if(O.w_class > 3) + to_chat(user, "[O] is too large to fit in [src].") + else + wrapped = O + user.unEquip(O) + O.forceMove(src) + O.add_fingerprint(usr) + add_fingerprint(usr) + to_chat(user, "You put [O] in [src].") + +/obj/item/shippingPackage/attack_self(mob/user) + if(sealed) + to_chat(user, "You tear open [src], dropping the contents onto the floor.") + playsound(loc, 'sound/items/poster_ripped.ogg', 50, 1) + user.unEquip(src) + wrapped.forceMove(get_turf(user)) + wrapped = null + qdel(src) + else if(wrapped) + switch(alert("Select an action:",, "Remove Object", "Seal Package", "Cancel")) + if("Remove Object") + to_chat(user, "You shake out [src]'s contents onto the floor.") + wrapped.forceMove(get_turf(user)) + wrapped = null + if("Seal Package") + to_chat(user, "You seal [src], preparing it for delivery.") + icon_state = "shippack_sealed" + sealed = 1 + update_desc() + else + if(alert("Do you want to tear up the package?",, "Yes", "No") == "Yes") + to_chat(user, "You shred [src].") + playsound(loc, 'sound/items/poster_ripped.ogg', 50, 1) + user.unEquip(src) + qdel(src) + +/obj/item/shippingPackage/proc/update_desc() + desc = "A pre-labeled package for shipping an item to coworkers." + if(sortTag) + desc += " The label says \"Deliver to [GLOB.TAGGERLOCATIONS[sortTag]]\"." + if(!sealed) + desc += " The package is not sealed." + +/obj/item/shippingPackage/Destroy() + QDEL_NULL(wrapped) + return ..() diff --git a/code/modules/research/circuitprinter.dm b/code/modules/research/circuitprinter.dm index 2ddab9b2f12..eda48794d86 100644 --- a/code/modules/research/circuitprinter.dm +++ b/code/modules/research/circuitprinter.dm @@ -1,105 +1,105 @@ -/*///////////////Circuit Imprinter (By Darem)//////////////////////// - Used to print new circuit boards (for computers and similar systems) and AI modules. Each circuit board pattern are stored in -a /datum/desgin on the linked R&D console. You can then print them out in a fasion similar to a regular lathe. However, instead of -using metal and glass, it uses glass and reagents (usually sulfuric acis). - -*/ -/obj/machinery/r_n_d/circuit_imprinter - name = "Circuit Imprinter" - desc = "Manufactures circuit boards for the construction of machines." - icon_state = "circuit_imprinter" - container_type = OPENCONTAINER - - var/efficiency_coeff - - var/list/categories = list( - "AI Modules", - "Computer Boards", - "Computer Parts", - "Engineering Machinery", - "Exosuit Modules", - "Hydroponics Machinery", - "Medical Machinery", - "Misc. Machinery", - "Research Machinery", - "Subspace Telecomms", - "Teleportation Machinery" - ) - - reagents = new() - -/obj/machinery/r_n_d/circuit_imprinter/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/circuit_imprinter(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/reagent_containers/glass/beaker(null) - component_parts += new /obj/item/reagent_containers/glass/beaker(null) - RefreshParts() - reagents.my_atom = src - -/obj/machinery/r_n_d/circuit_imprinter/upgraded/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/circuit_imprinter(null) - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/manipulator/pico(null) - component_parts += new /obj/item/reagent_containers/glass/beaker/large(null) - component_parts += new /obj/item/reagent_containers/glass/beaker/large(null) - RefreshParts() - reagents.my_atom = src - -/obj/machinery/r_n_d/circuit_imprinter/RefreshParts() - reagents.maximum_volume = 0 - for(var/obj/item/reagent_containers/glass/G in component_parts) - reagents.maximum_volume += G.volume - G.reagents.trans_to(src, G.reagents.total_volume) - - materials.max_amount = 0 - for(var/obj/item/stock_parts/matter_bin/M in component_parts) - materials.max_amount += M.rating * 75000 - - var/T = 0 - for(var/obj/item/stock_parts/manipulator/M in component_parts) - T += M.rating - efficiency_coeff = 2 ** (T - 1) //Only 1 manipulator here, you're making runtimes Razharas - -/obj/machinery/r_n_d/circuit_imprinter/proc/check_mat(datum/design/being_built, var/M) - var/list/all_materials = being_built.reagents_list + being_built.materials - - var/A = materials.amount(M) - if(!A) - A = reagents.get_reagent_amount(M) - - return round(A / max(1, (all_materials[M]/efficiency_coeff))) - -/obj/machinery/r_n_d/circuit_imprinter/attackby(var/obj/item/O as obj, var/mob/user as mob, params) - if(shocked) - if(shock(user,50)) - return TRUE - if(default_deconstruction_screwdriver(user, "circuit_imprinter_t", "circuit_imprinter", O)) - if(linked_console) - linked_console.linked_imprinter = null - linked_console = null - return - - if(exchange_parts(user, O)) - return - - if(panel_open) - if(istype(O, /obj/item/crowbar)) - for(var/obj/I in component_parts) - if(istype(I, /obj/item/reagent_containers/glass/beaker)) - reagents.trans_to(I, reagents.total_volume) - I.loc = src.loc - materials.retrieve_all() - default_deconstruction_crowbar(O) - return - else - to_chat(user, "You can't load the [src.name] while it's opened.") - return - if(O.is_open_container()) - return FALSE - else - return ..() \ No newline at end of file +/*///////////////Circuit Imprinter (By Darem)//////////////////////// + Used to print new circuit boards (for computers and similar systems) and AI modules. Each circuit board pattern are stored in +a /datum/desgin on the linked R&D console. You can then print them out in a fasion similar to a regular lathe. However, instead of +using metal and glass, it uses glass and reagents (usually sulfuric acis). + +*/ +/obj/machinery/r_n_d/circuit_imprinter + name = "Circuit Imprinter" + desc = "Manufactures circuit boards for the construction of machines." + icon_state = "circuit_imprinter" + container_type = OPENCONTAINER + + var/efficiency_coeff + + var/list/categories = list( + "AI Modules", + "Computer Boards", + "Computer Parts", + "Engineering Machinery", + "Exosuit Modules", + "Hydroponics Machinery", + "Medical Machinery", + "Misc. Machinery", + "Research Machinery", + "Subspace Telecomms", + "Teleportation Machinery" + ) + + reagents = new() + +/obj/machinery/r_n_d/circuit_imprinter/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/circuit_imprinter(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/reagent_containers/glass/beaker(null) + component_parts += new /obj/item/reagent_containers/glass/beaker(null) + RefreshParts() + reagents.my_atom = src + +/obj/machinery/r_n_d/circuit_imprinter/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/circuit_imprinter(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/manipulator/pico(null) + component_parts += new /obj/item/reagent_containers/glass/beaker/large(null) + component_parts += new /obj/item/reagent_containers/glass/beaker/large(null) + RefreshParts() + reagents.my_atom = src + +/obj/machinery/r_n_d/circuit_imprinter/RefreshParts() + reagents.maximum_volume = 0 + for(var/obj/item/reagent_containers/glass/G in component_parts) + reagents.maximum_volume += G.volume + G.reagents.trans_to(src, G.reagents.total_volume) + + materials.max_amount = 0 + for(var/obj/item/stock_parts/matter_bin/M in component_parts) + materials.max_amount += M.rating * 75000 + + var/T = 0 + for(var/obj/item/stock_parts/manipulator/M in component_parts) + T += M.rating + efficiency_coeff = 2 ** (T - 1) //Only 1 manipulator here, you're making runtimes Razharas + +/obj/machinery/r_n_d/circuit_imprinter/proc/check_mat(datum/design/being_built, var/M) + var/list/all_materials = being_built.reagents_list + being_built.materials + + var/A = materials.amount(M) + if(!A) + A = reagents.get_reagent_amount(M) + + return round(A / max(1, (all_materials[M]/efficiency_coeff))) + +/obj/machinery/r_n_d/circuit_imprinter/attackby(var/obj/item/O as obj, var/mob/user as mob, params) + if(shocked) + if(shock(user,50)) + return TRUE + if(default_deconstruction_screwdriver(user, "circuit_imprinter_t", "circuit_imprinter", O)) + if(linked_console) + linked_console.linked_imprinter = null + linked_console = null + return + + if(exchange_parts(user, O)) + return + + if(panel_open) + if(istype(O, /obj/item/crowbar)) + for(var/obj/I in component_parts) + if(istype(I, /obj/item/reagent_containers/glass/beaker)) + reagents.trans_to(I, reagents.total_volume) + I.loc = src.loc + materials.retrieve_all() + default_deconstruction_crowbar(user, O) + return + else + to_chat(user, "You can't load the [src.name] while it's opened.") + return + if(O.is_open_container()) + return FALSE + else + return ..() diff --git a/code/modules/research/designs.dm b/code/modules/research/designs.dm index e7e3f1c3acd..bfe959cb6c3 100644 --- a/code/modules/research/designs.dm +++ b/code/modules/research/designs.dm @@ -1,47 +1,47 @@ -/*************************************************************** -** Design Datums ** -** All the data for building stuff. ** -***************************************************************/ -/* -For the materials datum, it assumes you need reagents unless specified otherwise. To designate a material that isn't a reagent, -you use one of the material IDs below. These are NOT ids in the usual sense (they aren't defined in the object or part of a datum), -they are simply references used as part of a "has materials?" type proc. They all start with a $ to denote that they aren't reagents. -The currently supporting non-reagent materials: -- MAT_METAL (/obj/item/stack/metal). -- MAT_GLASS (/obj/item/stack/glass). -- MAT_PLASMA (/obj/item/stack/plasma). -- MAT_SILVER (/obj/item/stack/silver). -- MAT_GOLD (/obj/item/stack/gold). -- MAT_URANIUM (/obj/item/stack/uranium). -- MAT_DIAMOND (/obj/item/stack/diamond). -- MAT_BANANIUM (/obj/item/stack/bananium). -- MAT_TRANQUILLITE (/obj/item/stack/tranquillite). -(Insert new ones here) - -Don't add new keyword/IDs if they are made from an existing one (such as rods which are made from metal). Only add raw materials. - -Design Guidlines -- When adding new designs, check rdreadme.dm to see what kind of things have already been made and where new stuff is needed. -- A single sheet of anything is 2000 units of material. Materials besides metal/glass require help from other jobs (mining for -other types of metals and chemistry for reagents). -- Add the AUTOLATHE tag to - - -*/ - -/datum/design //Datum for object designs, used in construction - var/name = "Name" //Name of the created object. - var/desc = "Desc" //Description of the created object. - var/id = "id" //ID of the created object for easy refernece. Alphanumeric, lower-case, no symbols - var/list/req_tech = list() //IDs of that techs the object originated from and the minimum level requirements. - var/build_type = null //Flag as to what kind machine the design is built in. See defines. - var/list/materials = list() //List of materials. Format: "id" = amount. - var/construction_time //Amount of time required for building the object - var/build_path = null //The file path of the object that gets created - var/list/make_reagents = list() //Reagents produced. Format: "id" = amount. Currently only supported by the biogenerator. - var/locked = 0 //If true it will spawn inside a lockbox with currently sec access - var/access_requirement = list(access_armory) //What special access requirements will the lockbox have? Defaults to armory. - var/category = null //Primarily used for Mech Fabricators, but can be used for anything - var/list/reagents_list = list() //List of reagents. Format: "id" = amount. - var/maxstack = 1 - var/lathe_time_factor = 1 //How many times faster than normal is this to build on the protolathe +/*************************************************************** +** Design Datums ** +** All the data for building stuff. ** +***************************************************************/ +/* +For the materials datum, it assumes you need reagents unless specified otherwise. To designate a material that isn't a reagent, +you use one of the material IDs below. These are NOT ids in the usual sense (they aren't defined in the object or part of a datum), +they are simply references used as part of a "has materials?" type proc. They all start with a $ to denote that they aren't reagents. +The currently supporting non-reagent materials: +- MAT_METAL (/obj/item/stack/metal). +- MAT_GLASS (/obj/item/stack/glass). +- MAT_PLASMA (/obj/item/stack/plasma). +- MAT_SILVER (/obj/item/stack/silver). +- MAT_GOLD (/obj/item/stack/gold). +- MAT_URANIUM (/obj/item/stack/uranium). +- MAT_DIAMOND (/obj/item/stack/diamond). +- MAT_BANANIUM (/obj/item/stack/bananium). +- MAT_TRANQUILLITE (/obj/item/stack/tranquillite). +(Insert new ones here) + +Don't add new keyword/IDs if they are made from an existing one (such as rods which are made from metal). Only add raw materials. + +Design Guidlines +- When adding new designs, check rdreadme.dm to see what kind of things have already been made and where new stuff is needed. +- A single sheet of anything is 2000 units of material. Materials besides metal/glass require help from other jobs (mining for +other types of metals and chemistry for reagents). +- Add the AUTOLATHE tag to + + +*/ + +/datum/design //Datum for object designs, used in construction + var/name = "Name" //Name of the created object. + var/desc = "Desc" //Description of the created object. + var/id = "id" //ID of the created object for easy refernece. Alphanumeric, lower-case, no symbols + var/list/req_tech = list() //IDs of that techs the object originated from and the minimum level requirements. + var/build_type = null //Flag as to what kind machine the design is built in. See defines. + var/list/materials = list() //List of materials. Format: "id" = amount. + var/construction_time //Amount of time required for building the object + var/build_path = null //The file path of the object that gets created + var/list/make_reagents = list() //Reagents produced. Format: "id" = amount. Currently only supported by the biogenerator. + var/locked = 0 //If true it will spawn inside a lockbox with currently sec access + var/access_requirement = list(ACCESS_ARMORY) //What special access requirements will the lockbox have? Defaults to armory. + var/category = null //Primarily used for Mech Fabricators, but can be used for anything + var/list/reagents_list = list() //List of reagents. Format: "id" = amount. + var/maxstack = 1 + var/lathe_time_factor = 1 //How many times faster than normal is this to build on the protolathe diff --git a/code/modules/research/designs/autolathe_designs.dm b/code/modules/research/designs/autolathe_designs.dm index 0bcdd3ba40d..2e7c668a83f 100644 --- a/code/modules/research/designs/autolathe_designs.dm +++ b/code/modules/research/designs/autolathe_designs.dm @@ -872,14 +872,6 @@ build_path = /obj/item/assembly/mousetrap category = list("initial", "Miscellaneous") -/datum/design/videocam - name = "Video Camera" - id = "videocam" - build_type = AUTOLATHE - materials = list(MAT_METAL = 120, MAT_GLASS = 60) - build_path = /obj/item/videocam - category = list("initial", "Miscellaneous") - /datum/design/logic_board name = "Logic Circuit" id = "logic_board" diff --git a/code/modules/research/designs/biogenerator_designs.dm b/code/modules/research/designs/biogenerator_designs.dm index a0bfdf01c61..b70771cb8d3 100644 --- a/code/modules/research/designs/biogenerator_designs.dm +++ b/code/modules/research/designs/biogenerator_designs.dm @@ -177,4 +177,4 @@ build_type = BIOGENERATOR materials = list(MAT_BIOMASS = 300) build_path = /obj/item/clothing/head/rice_hat - category = list("initial","Leather and Cloth") \ No newline at end of file + category = list("initial","Leather and Cloth") diff --git a/code/modules/research/designs/computer_part_designs.dm b/code/modules/research/designs/computer_part_designs.dm index 3c653107ce6..438a679f97a 100644 --- a/code/modules/research/designs/computer_part_designs.dm +++ b/code/modules/research/designs/computer_part_designs.dm @@ -248,4 +248,4 @@ build_type = IMPRINTER materials = list(MAT_GLASS = 3200, MAT_GOLD = 1000) build_path = /obj/item/computer_hardware/processor_unit/photonic/small - category = list("Computer Parts") \ No newline at end of file + category = list("Computer Parts") diff --git a/code/modules/research/designs/mining_designs.dm b/code/modules/research/designs/mining_designs.dm index 76439c64987..1fa33e6d27b 100644 --- a/code/modules/research/designs/mining_designs.dm +++ b/code/modules/research/designs/mining_designs.dm @@ -1,112 +1,112 @@ -///////////////////////////////////////// -/////////////////Mining////////////////// -///////////////////////////////////////// -/datum/design/drill - name = "Mining Drill" - desc = "Yours is the drill that will pierce through the rock walls." - id = "drill" - req_tech = list("materials" = 2, "powerstorage" = 2, "engineering" = 3) - build_type = PROTOLATHE - materials = list(MAT_METAL = 6000, MAT_GLASS = 1000) - build_path = /obj/item/pickaxe/drill - category = list("Mining") - -/datum/design/drill_diamond - name = "Diamond Mining Drill" - desc = "Yours is the drill that will pierce the heavens!" - id = "drill_diamond" - req_tech = list("materials" = 6, "powerstorage" = 5, "engineering" = 5) - build_type = PROTOLATHE - materials = list(MAT_METAL = 3000, MAT_GLASS = 1000, MAT_DIAMOND = 2000) //Yes, a whole diamond is needed. - build_path = /obj/item/pickaxe/drill/diamonddrill - category = list("Mining") - -/datum/design/plasmacutter - name = "Plasma Cutter" - desc = "You could use it to cut limbs off of xenos! Or, you know, mine stuff." - id = "plasmacutter" - req_tech = list("materials" = 3, "plasmatech" = 3, "magnets" = 2) - build_type = PROTOLATHE - materials = list(MAT_METAL = 1500, MAT_GLASS = 500, MAT_PLASMA = 400) - build_path = /obj/item/gun/energy/plasmacutter - category = list("Mining") - -/datum/design/plasmacutter_adv - name = "Advanced Plasma Cutter" - desc = "It's an advanced plasma cutter, oh my god." - id = "plasmacutter_adv" - req_tech = list("materials" = 4, "plasmatech" = 4, "engineering" = 2, "combat" = 3, "magnets" = 3) - build_type = PROTOLATHE - materials = list(MAT_METAL = 3000, MAT_GLASS = 1000, MAT_PLASMA = 2000, MAT_GOLD = 500) - build_path = /obj/item/gun/energy/plasmacutter/adv - category = list("Mining") - -/datum/design/jackhammer - name = "Sonic Jackhammer" - desc = "Essentially a handheld planet-cracker. Can drill through walls with ease as well." - id = "jackhammer" - req_tech = list("materials" = 7, "powerstorage" = 5, "engineering" = 6, "magnets" = 5) - build_type = PROTOLATHE - materials = list(MAT_METAL = 6000, MAT_GLASS = 2000, MAT_SILVER = 2000, MAT_DIAMOND = 6000) - build_path = /obj/item/pickaxe/drill/jackhammer - category = list("Mining") - -/datum/design/superresonator - name = "Upgraded Resonator" - desc = "An upgraded version of the resonator that allows more fields to be active at once." - id = "superresonator" - req_tech = list("materials" = 4, "powerstorage" = 3, "engineering" = 3, "magnets" = 3) - build_type = PROTOLATHE - materials = list(MAT_METAL = 4000, MAT_GLASS = 1500, MAT_SILVER = 1000, MAT_URANIUM = 1000) - build_path = /obj/item/resonator/upgraded - category = list("Mining") - -/datum/design/trigger_guard_mod - name = "Kinetic Accelerator Trigger Guard Mod" - desc = "A device which allows kinetic accelerators to be wielded by any organism." - id = "triggermod" - req_tech = list("materials" = 5, "powerstorage" = 4, "engineering" = 4, "magnets" = 4, "combat" = 3) - build_type = PROTOLATHE - materials = list(MAT_METAL = 2000, MAT_GLASS = 1500, MAT_GOLD = 1500, MAT_URANIUM = 1000) - build_path = /obj/item/borg/upgrade/modkit/trigger_guard - category = list("Mining") - -/datum/design/damage_mod - name = "Kinetic Accelerator Damage Mod" - desc = "A device which allows kinetic accelerators to deal more damage." - id = "damagemod" - req_tech = list("materials" = 5, "powerstorage" = 4, "engineering" = 4, "magnets" = 4, "combat" = 3) - build_type = PROTOLATHE | MECHFAB - materials = list(MAT_METAL = 2000, MAT_GLASS = 1500, MAT_GOLD = 1500, MAT_URANIUM = 1000) - build_path = /obj/item/borg/upgrade/modkit/damage - category = list("Mining", "Cyborg Upgrade Modules") - -/datum/design/cooldown_mod - name = "Kinetic Accelerator Cooldown Mod" - desc = "A device which decreases the cooldown of a Kinetic Accelerator." - id = "cooldownmod" - req_tech = list("materials" = 5, "powerstorage" = 4, "engineering" = 4, "magnets" = 4, "combat" = 3) - build_type = PROTOLATHE | MECHFAB - materials = list(MAT_METAL = 2000, MAT_GLASS = 1500, MAT_GOLD = 1500, MAT_URANIUM = 1000) - build_path = /obj/item/borg/upgrade/modkit/cooldown - category = list("Mining", "Cyborg Upgrade Modules") - -/datum/design/range_mod - name = "Kinetic Accelerator Range Mod" - desc = "A device which allows kinetic accelerators to fire at a further range." - id = "rangemod" - req_tech = list("materials" = 5, "powerstorage" = 4, "engineering" = 4, "magnets" = 4, "combat" = 3) - build_type = PROTOLATHE | MECHFAB - materials = list(MAT_METAL = 2000, MAT_GLASS = 1500, MAT_GOLD = 1500, MAT_URANIUM = 1000) - build_path = /obj/item/borg/upgrade/modkit/range - category = list("Mining", "Cyborg Upgrade Modules") - -/datum/design/hyperaccelerator - name = "Kinetic Accelerator Mining AoE Mod" - desc = "A modification kit for Kinetic Accelerators which causes it to fire AoE blasts that destroy rock." - id = "hypermod" - req_tech = list("materials" = 7, "powerstorage" = 5, "engineering" = 5, "magnets" = 5, "combat" = 4) - build_type = PROTOLATHE | MECHFAB - materials = list(MAT_METAL = 8000, MAT_GLASS = 1500, MAT_SILVER = 2000, MAT_GOLD = 2000, MAT_DIAMOND = 2000) - build_path = /obj/item/borg/upgrade/modkit/aoe/turfs - category = list("Mining", "Cyborg Upgrade Modules") \ No newline at end of file +///////////////////////////////////////// +/////////////////Mining////////////////// +///////////////////////////////////////// +/datum/design/drill + name = "Mining Drill" + desc = "Yours is the drill that will pierce through the rock walls." + id = "drill" + req_tech = list("materials" = 2, "powerstorage" = 2, "engineering" = 3) + build_type = PROTOLATHE + materials = list(MAT_METAL = 6000, MAT_GLASS = 1000) + build_path = /obj/item/pickaxe/drill + category = list("Mining") + +/datum/design/drill_diamond + name = "Diamond Mining Drill" + desc = "Yours is the drill that will pierce the heavens!" + id = "drill_diamond" + req_tech = list("materials" = 6, "powerstorage" = 5, "engineering" = 5) + build_type = PROTOLATHE + materials = list(MAT_METAL = 3000, MAT_GLASS = 1000, MAT_DIAMOND = 2000) //Yes, a whole diamond is needed. + build_path = /obj/item/pickaxe/drill/diamonddrill + category = list("Mining") + +/datum/design/plasmacutter + name = "Plasma Cutter" + desc = "You could use it to cut limbs off of xenos! Or, you know, mine stuff." + id = "plasmacutter" + req_tech = list("materials" = 3, "plasmatech" = 3, "magnets" = 2) + build_type = PROTOLATHE + materials = list(MAT_METAL = 1500, MAT_GLASS = 500, MAT_PLASMA = 400) + build_path = /obj/item/gun/energy/plasmacutter + category = list("Mining") + +/datum/design/plasmacutter_adv + name = "Advanced Plasma Cutter" + desc = "It's an advanced plasma cutter, oh my god." + id = "plasmacutter_adv" + req_tech = list("materials" = 4, "plasmatech" = 4, "engineering" = 2, "combat" = 3, "magnets" = 3) + build_type = PROTOLATHE + materials = list(MAT_METAL = 3000, MAT_GLASS = 1000, MAT_PLASMA = 2000, MAT_GOLD = 500) + build_path = /obj/item/gun/energy/plasmacutter/adv + category = list("Mining") + +/datum/design/jackhammer + name = "Sonic Jackhammer" + desc = "Essentially a handheld planet-cracker. Can drill through walls with ease as well." + id = "jackhammer" + req_tech = list("materials" = 7, "powerstorage" = 5, "engineering" = 6, "magnets" = 5) + build_type = PROTOLATHE + materials = list(MAT_METAL = 6000, MAT_GLASS = 2000, MAT_SILVER = 2000, MAT_DIAMOND = 6000) + build_path = /obj/item/pickaxe/drill/jackhammer + category = list("Mining") + +/datum/design/superresonator + name = "Upgraded Resonator" + desc = "An upgraded version of the resonator that allows more fields to be active at once." + id = "superresonator" + req_tech = list("materials" = 4, "powerstorage" = 3, "engineering" = 3, "magnets" = 3) + build_type = PROTOLATHE + materials = list(MAT_METAL = 4000, MAT_GLASS = 1500, MAT_SILVER = 1000, MAT_URANIUM = 1000) + build_path = /obj/item/resonator/upgraded + category = list("Mining") + +/datum/design/trigger_guard_mod + name = "Kinetic Accelerator Trigger Guard Mod" + desc = "A device which allows kinetic accelerators to be wielded by any organism." + id = "triggermod" + req_tech = list("materials" = 5, "powerstorage" = 4, "engineering" = 4, "magnets" = 4, "combat" = 3) + build_type = PROTOLATHE + materials = list(MAT_METAL = 2000, MAT_GLASS = 1500, MAT_GOLD = 1500, MAT_URANIUM = 1000) + build_path = /obj/item/borg/upgrade/modkit/trigger_guard + category = list("Mining") + +/datum/design/damage_mod + name = "Kinetic Accelerator Damage Mod" + desc = "A device which allows kinetic accelerators to deal more damage." + id = "damagemod" + req_tech = list("materials" = 5, "powerstorage" = 4, "engineering" = 4, "magnets" = 4, "combat" = 3) + build_type = PROTOLATHE | MECHFAB + materials = list(MAT_METAL = 2000, MAT_GLASS = 1500, MAT_GOLD = 1500, MAT_URANIUM = 1000) + build_path = /obj/item/borg/upgrade/modkit/damage + category = list("Mining", "Cyborg Upgrade Modules") + +/datum/design/cooldown_mod + name = "Kinetic Accelerator Cooldown Mod" + desc = "A device which decreases the cooldown of a Kinetic Accelerator." + id = "cooldownmod" + req_tech = list("materials" = 5, "powerstorage" = 4, "engineering" = 4, "magnets" = 4, "combat" = 3) + build_type = PROTOLATHE | MECHFAB + materials = list(MAT_METAL = 2000, MAT_GLASS = 1500, MAT_GOLD = 1500, MAT_URANIUM = 1000) + build_path = /obj/item/borg/upgrade/modkit/cooldown + category = list("Mining", "Cyborg Upgrade Modules") + +/datum/design/range_mod + name = "Kinetic Accelerator Range Mod" + desc = "A device which allows kinetic accelerators to fire at a further range." + id = "rangemod" + req_tech = list("materials" = 5, "powerstorage" = 4, "engineering" = 4, "magnets" = 4, "combat" = 3) + build_type = PROTOLATHE | MECHFAB + materials = list(MAT_METAL = 2000, MAT_GLASS = 1500, MAT_GOLD = 1500, MAT_URANIUM = 1000) + build_path = /obj/item/borg/upgrade/modkit/range + category = list("Mining", "Cyborg Upgrade Modules") + +/datum/design/hyperaccelerator + name = "Kinetic Accelerator Mining AoE Mod" + desc = "A modification kit for Kinetic Accelerators which causes it to fire AoE blasts that destroy rock." + id = "hypermod" + req_tech = list("materials" = 7, "powerstorage" = 5, "engineering" = 5, "magnets" = 5, "combat" = 4) + build_type = PROTOLATHE | MECHFAB + materials = list(MAT_METAL = 8000, MAT_GLASS = 1500, MAT_SILVER = 2000, MAT_GOLD = 2000, MAT_DIAMOND = 2000) + build_path = /obj/item/borg/upgrade/modkit/aoe/turfs + category = list("Mining", "Cyborg Upgrade Modules") diff --git a/code/modules/research/designs/smelting_designs.dm b/code/modules/research/designs/smelting_designs.dm index a043d90fb19..0fc368164b5 100644 --- a/code/modules/research/designs/smelting_designs.dm +++ b/code/modules/research/designs/smelting_designs.dm @@ -50,4 +50,4 @@ materials = list(MAT_METAL = 4000, MAT_PLASMA = 4000) build_path = /obj/item/stack/sheet/mineral/abductor category = list("Stock Parts") - lathe_time_factor = 5 \ No newline at end of file + lathe_time_factor = 5 diff --git a/code/modules/research/designs/stock_parts_designs.dm b/code/modules/research/designs/stock_parts_designs.dm index e32b79cc91b..e27aaf0ee70 100644 --- a/code/modules/research/designs/stock_parts_designs.dm +++ b/code/modules/research/designs/stock_parts_designs.dm @@ -248,4 +248,4 @@ build_type = PROTOLATHE materials = list(MAT_METAL = 15000, MAT_GLASS = 5000, MAT_SILVER = 2500) //hardcore build_path = /obj/item/storage/part_replacer/bluespace - category = list("Stock Parts") \ No newline at end of file + category = list("Stock Parts") diff --git a/code/modules/research/designs/weapon_designs.dm b/code/modules/research/designs/weapon_designs.dm index 6810aff0b75..417adf813d7 100644 --- a/code/modules/research/designs/weapon_designs.dm +++ b/code/modules/research/designs/weapon_designs.dm @@ -67,7 +67,7 @@ materials = list(MAT_SILVER = 2000, MAT_METAL = 5000, MAT_DIAMOND = 2000, MAT_BLUESPACE = 3000) build_path = /obj/item/gun/energy/wormhole_projector locked = 1 - access_requirement = list(access_rd) //screw you, HoS, this aint yours; this is only for a man of science---and trouble. + access_requirement = list(ACCESS_RD) //screw you, HoS, this aint yours; this is only for a man of science---and trouble. category = list("Weapons") /datum/design/large_grenade diff --git a/code/modules/research/destructive_analyzer.dm b/code/modules/research/destructive_analyzer.dm index 027a8549df4..58a8ab21284 100644 --- a/code/modules/research/destructive_analyzer.dm +++ b/code/modules/research/destructive_analyzer.dm @@ -1,88 +1,88 @@ -/* -Destructive Analyzer - -It is used to destroy hand-held objects and advance technological research. Controls are in the linked R&D console. - -Note: Must be placed within 3 tiles of the R&D Console -*/ -/obj/machinery/r_n_d/destructive_analyzer - name = "Destructive Analyzer" - desc = "Learn science by destroying things!" - icon_state = "d_analyzer" - var/decon_mod = 0 - -/obj/machinery/r_n_d/destructive_analyzer/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/destructive_analyzer(null) - component_parts += new /obj/item/stock_parts/scanning_module(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stock_parts/micro_laser(null) - RefreshParts() - -/obj/machinery/r_n_d/destructive_analyzer/upgraded/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/destructive_analyzer(null) - component_parts += new /obj/item/stock_parts/scanning_module/phasic(null) - component_parts += new /obj/item/stock_parts/manipulator/pico(null) - component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) - RefreshParts() - -/obj/machinery/r_n_d/destructive_analyzer/RefreshParts() - var/T = 0 - for(var/obj/item/stock_parts/S in component_parts) - T += S.rating - decon_mod = T - - -/obj/machinery/r_n_d/destructive_analyzer/proc/ConvertReqString2List(var/list/source_list) - var/list/temp_list = params2list(source_list) - for(var/O in temp_list) - temp_list[O] = text2num(temp_list[O]) - return temp_list - - -/obj/machinery/r_n_d/destructive_analyzer/attackby(var/obj/item/O as obj, var/mob/user as mob, params) - if(shocked) - if(shock(user,50)) - return TRUE - if(default_deconstruction_screwdriver(user, "d_analyzer_t", "d_analyzer", O)) - if(linked_console) - linked_console.linked_destroy = null - linked_console = null - return - - if(exchange_parts(user, O)) - return - - if(default_deconstruction_crowbar(O)) - return - - if(disabled) - return - if(!linked_console) - to_chat(user, "The [src.name] must be linked to an R&D console first!") - return - if(busy) - to_chat(user, "The [src.name] is busy right now.") - return - if(istype(O, /obj/item) && !loaded_item) - if(!O.origin_tech) - to_chat(user, "This doesn't seem to have a tech origin!") - return - var/list/temp_tech = ConvertReqString2List(O.origin_tech) - if(temp_tech.len == 0) - to_chat(user, "You cannot deconstruct this item!") - return - if(!user.drop_item()) - to_chat(user, "\The [O] is stuck to your hand, you cannot put it in the [src.name]!") - return - busy = 1 - loaded_item = O - O.loc = src - to_chat(user, "You add the [O.name] to the [src.name]!") - flick("d_analyzer_la", src) - spawn(10) - icon_state = "d_analyzer_l" - busy = 0 \ No newline at end of file +/* +Destructive Analyzer + +It is used to destroy hand-held objects and advance technological research. Controls are in the linked R&D console. + +Note: Must be placed within 3 tiles of the R&D Console +*/ +/obj/machinery/r_n_d/destructive_analyzer + name = "Destructive Analyzer" + desc = "Learn science by destroying things!" + icon_state = "d_analyzer" + var/decon_mod = 0 + +/obj/machinery/r_n_d/destructive_analyzer/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/destructive_analyzer(null) + component_parts += new /obj/item/stock_parts/scanning_module(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stock_parts/micro_laser(null) + RefreshParts() + +/obj/machinery/r_n_d/destructive_analyzer/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/destructive_analyzer(null) + component_parts += new /obj/item/stock_parts/scanning_module/phasic(null) + component_parts += new /obj/item/stock_parts/manipulator/pico(null) + component_parts += new /obj/item/stock_parts/micro_laser/ultra(null) + RefreshParts() + +/obj/machinery/r_n_d/destructive_analyzer/RefreshParts() + var/T = 0 + for(var/obj/item/stock_parts/S in component_parts) + T += S.rating + decon_mod = T + + +/obj/machinery/r_n_d/destructive_analyzer/proc/ConvertReqString2List(var/list/source_list) + var/list/temp_list = params2list(source_list) + for(var/O in temp_list) + temp_list[O] = text2num(temp_list[O]) + return temp_list + + +/obj/machinery/r_n_d/destructive_analyzer/attackby(var/obj/item/O as obj, var/mob/user as mob, params) + if(shocked) + if(shock(user,50)) + return TRUE + if(default_deconstruction_screwdriver(user, "d_analyzer_t", "d_analyzer", O)) + if(linked_console) + linked_console.linked_destroy = null + linked_console = null + return + + if(exchange_parts(user, O)) + return + + if(default_deconstruction_crowbar(user, O)) + return + + if(disabled) + return + if(!linked_console) + to_chat(user, "The [src.name] must be linked to an R&D console first!") + return + if(busy) + to_chat(user, "The [src.name] is busy right now.") + return + if(istype(O, /obj/item) && !loaded_item) + if(!O.origin_tech) + to_chat(user, "This doesn't seem to have a tech origin!") + return + var/list/temp_tech = ConvertReqString2List(O.origin_tech) + if(temp_tech.len == 0) + to_chat(user, "You cannot deconstruct this item!") + return + if(!user.drop_item()) + to_chat(user, "\The [O] is stuck to your hand, you cannot put it in the [src.name]!") + return + busy = 1 + loaded_item = O + O.loc = src + to_chat(user, "You add the [O.name] to the [src.name]!") + flick("d_analyzer_la", src) + spawn(10) + icon_state = "d_analyzer_l" + busy = 0 diff --git a/code/modules/research/experimentor.dm b/code/modules/research/experimentor.dm index 1548125b785..3951cf23312 100644 --- a/code/modules/research/experimentor.dm +++ b/code/modules/research/experimentor.dm @@ -125,7 +125,7 @@ return if(panel_open && istype(O, /obj/item/crowbar)) - default_deconstruction_crowbar(O) + default_deconstruction_crowbar(user, O) return if(!checkCircumstances(O)) @@ -157,7 +157,7 @@ return -/obj/machinery/r_n_d/experimentor/default_deconstruction_crowbar(obj/item/O) +/obj/machinery/r_n_d/experimentor/default_deconstruction_crowbar(user, obj/item/O) ejectItem() ..(O) @@ -618,6 +618,7 @@ var/floof /obj/item/relic/New() + ..() icon_state = pick("shock_kit","armor-igniter-analyzer","infra-igniter0","infra-igniter1","radio-multitool","prox-radio1","radio-radio","timer-multitool0","radio-igniter-tank") realName = "[pick("broken","twisted","spun","improved","silly","regular","badly made")] [pick("device","object","toy","suspicious tech","gear")]" floof = pick(/mob/living/simple_animal/pet/dog/corgi, /mob/living/simple_animal/pet/cat, /mob/living/simple_animal/pet/dog/fox, /mob/living/simple_animal/mouse, /mob/living/simple_animal/pet/dog/pug, /mob/living/simple_animal/lizard, /mob/living/simple_animal/diona, /mob/living/simple_animal/butterfly, /mob/living/carbon/human/monkey) diff --git a/code/modules/research/message_server.dm b/code/modules/research/message_server.dm index fa5015987e5..2a0e201ac54 100644 --- a/code/modules/research/message_server.dm +++ b/code/modules/research/message_server.dm @@ -1,380 +1,380 @@ -var/global/list/obj/machinery/message_server/message_servers = list() - -/datum/data_pda_msg - var/recipient = "Unspecified" //name of the person - var/sender = "Unspecified" //name of the sender - var/message = "Blank" //transferred message - -/datum/data_pda_msg/New(var/param_rec = "",var/param_sender = "",var/param_message = "") - - if(param_rec) - recipient = param_rec - if(param_sender) - sender = param_sender - if(param_message) - message = param_message - -/datum/data_rc_msg - var/rec_dpt = "Unspecified" //name of the person - var/send_dpt = "Unspecified" //name of the sender - var/message = "Blank" //transferred message - var/stamp = "Unstamped" - var/id_auth = "Unauthenticated" - var/priority = "Normal" - -/datum/data_rc_msg/New(var/param_rec = "",var/param_sender = "",var/param_message = "",var/param_stamp = "",var/param_id_auth = "",var/param_priority) - if(param_rec) - rec_dpt = param_rec - if(param_sender) - send_dpt = param_sender - if(param_message) - message = param_message - if(param_stamp) - stamp = param_stamp - if(param_id_auth) - id_auth = param_id_auth - if(param_priority) - switch(param_priority) - if(1) - priority = "Normal" - if(2) - priority = "High" - if(3) - priority = "Extreme" - else - priority = "Undetermined" - -/obj/machinery/message_server - icon = 'icons/obj/machines/research.dmi' - icon_state = "server" - name = "Messaging Server" - density = 1 - anchored = 1.0 - use_power = IDLE_POWER_USE - idle_power_usage = 10 - active_power_usage = 100 - - var/list/datum/data_pda_msg/pda_msgs = list() - var/list/datum/data_rc_msg/rc_msgs = list() - var/active = 1 - var/decryptkey = "password" - -/obj/machinery/message_server/New() - message_servers += src - decryptkey = GenerateKey() - send_pda_message("System Administrator", "system", "This is an automated message. The messaging system is functioning correctly.") - ..() - return - -/obj/machinery/message_server/Destroy() - message_servers -= src - return ..() - -/obj/machinery/message_server/process() - //if(decryptkey == "password") - // decryptkey = generateKey() - if(active && (stat & (BROKEN|NOPOWER))) - active = 0 - return - if(prob(3)) - playsound(loc, "computer_ambience", 50, 1) - update_icon() - return - -/obj/machinery/message_server/proc/send_pda_message(var/recipient = "",var/sender = "",var/message = "") - pda_msgs += new/datum/data_pda_msg(recipient,sender,message) - -/obj/machinery/message_server/proc/send_rc_message(var/recipient = "",var/sender = "",var/message = "",var/stamp = "", var/id_auth = "", var/priority = 1) - rc_msgs += new/datum/data_rc_msg(recipient,sender,message,stamp,id_auth) - var/authmsg = "[message]
        " - if(id_auth) - authmsg += "[id_auth]
        " - if(stamp) - authmsg += "[stamp]
        " - for(var/obj/machinery/requests_console/Console in allConsoles) - if(ckey(Console.department) == ckey(recipient)) - if(Console.inoperable()) - Console.message_log += "Message lost due to console failure.
        Please contact [station_name()] system adminsitrator or AI for technical assistance.
        " - continue - if(Console.newmessagepriority < priority) - Console.newmessagepriority = priority - Console.icon_state = "req_comp[priority]" - switch(priority) - if(2) - if(!Console.silent) - playsound(Console.loc, 'sound/machines/twobeep.ogg', 50, 1) - Console.audible_message(text("[bicon(Console)] *The Requests Console beeps: 'PRIORITY Alert in [sender]'"),,5) - Console.message_log += "High Priority message from [sender]
        [authmsg]" - else - if(!Console.silent) - playsound(Console.loc, 'sound/machines/twobeep.ogg', 50, 1) - Console.audible_message(text("[bicon(Console)] *The Requests Console beeps: 'Message from [sender]'"),,4) - Console.message_log += "Message from [sender]
        [authmsg]" - Console.set_light(2) - -/obj/machinery/message_server/attack_hand(user as mob) -// to_chat(user, "There seem to be some parts missing from this server. They should arrive on the station in a few days, give or take a few CentComm delays.") - to_chat(user, "You toggle PDA message passing from [active ? "On" : "Off"] to [active ? "Off" : "On"]") - active = !active - update_icon() - - return - -/obj/machinery/message_server/update_icon() - if((stat & (BROKEN|NOPOWER))) - icon_state = "server-nopower" - else if(!active) - icon_state = "server-off" - else - icon_state = "server-on" - - return - - -/datum/feedback_variable - var/variable - var/value - var/details - -/datum/feedback_variable/New(var/param_variable,var/param_value = 0) - variable = param_variable - value = param_value - -/datum/feedback_variable/vv_edit_var(var_name, var_value) - return FALSE // come on guys don't break the stats - -/datum/feedback_variable/proc/inc(var/num = 1) - if(isnum(value)) - value += num - else - value = text2num(value) - if(isnum(value)) - value += num - else - value = num - -/datum/feedback_variable/proc/dec(var/num = 1) - if(isnum(value)) - value -= num - else - value = text2num(value) - if(isnum(value)) - value -= num - else - value = -num - -/datum/feedback_variable/proc/set_value(var/num) - if(isnum(num)) - value = num - -/datum/feedback_variable/proc/get_value() - return value - -/datum/feedback_variable/proc/get_variable() - return variable - -/datum/feedback_variable/proc/set_details(var/text) - if(istext(text)) - details = text - -/datum/feedback_variable/proc/add_details(var/text) - if(istext(text)) - if(!details) - details = text - else - details += " [text]" - -/datum/feedback_variable/proc/get_details() - return details - -/datum/feedback_variable/proc/get_parsed() - return list(variable,value,details) - -var/obj/machinery/blackbox_recorder/blackbox - -//TODO: kill whoever designed this cancer -/obj/machinery/blackbox_recorder - icon = 'icons/obj/stationobjs.dmi' - icon_state = "blackbox" - name = "Blackbox Recorder" - density = 1 - anchored = 1.0 - use_power = IDLE_POWER_USE - idle_power_usage = 10 - active_power_usage = 100 - var/list/messages = list() //Stores messages of non-standard frequencies - var/list/messages_admin = list() - - var/list/msg_common = list() - var/list/msg_science = list() - var/list/msg_command = list() - var/list/msg_medical = list() - var/list/msg_engineering = list() - var/list/msg_security = list() - var/list/msg_deathsquad = list() - var/list/msg_syndicate = list() - var/list/msg_syndteam = list() - var/list/msg_mining = list() - var/list/msg_cargo = list() - var/list/msg_service = list() - - var/list/datum/feedback_variable/feedback = new() - - //Only one can exsist in the world! -/obj/machinery/blackbox_recorder/New() - if(blackbox) - if(istype(blackbox,/obj/machinery/blackbox_recorder)) - qdel(src) - blackbox = src - -/obj/machinery/blackbox_recorder/Destroy() - var/turf/T = locate(1,1,2) - if(T) - blackbox = null - var/obj/machinery/blackbox_recorder/BR = new/obj/machinery/blackbox_recorder(T) - BR.msg_common = msg_common - BR.msg_science = msg_science - BR.msg_command = msg_command - BR.msg_medical = msg_medical - BR.msg_engineering = msg_engineering - BR.msg_security = msg_security - BR.msg_deathsquad = msg_deathsquad - BR.msg_syndicate = msg_syndicate - BR.msg_syndteam = msg_syndteam - BR.msg_mining = msg_mining - BR.msg_cargo = msg_cargo - BR.msg_service = msg_service - BR.feedback = feedback - BR.messages = messages - BR.messages_admin = messages_admin - if(blackbox != BR) - blackbox = BR - return ..() - -/obj/machinery/blackbox_recorder/proc/find_feedback_datum(var/variable) - for(var/datum/feedback_variable/FV in feedback) - if(FV.get_variable() == variable) - return FV - var/datum/feedback_variable/FV = new(variable) - feedback += FV - return FV - -/obj/machinery/blackbox_recorder/proc/get_round_feedback() - return feedback - -/obj/machinery/blackbox_recorder/proc/round_end_data_gathering() - - var/pda_msg_amt = 0 - var/rc_msg_amt = 0 - - for(var/obj/machinery/message_server/MS in GLOB.machines) - if(MS.pda_msgs.len > pda_msg_amt) - pda_msg_amt = MS.pda_msgs.len - if(MS.rc_msgs.len > rc_msg_amt) - rc_msg_amt = MS.rc_msgs.len - - feedback_set_details("radio_usage","") - - feedback_add_details("radio_usage","COM-[msg_common.len]") - feedback_add_details("radio_usage","SCI-[msg_science.len]") - feedback_add_details("radio_usage","HEA-[msg_command.len]") - feedback_add_details("radio_usage","MED-[msg_medical.len]") - feedback_add_details("radio_usage","ENG-[msg_engineering.len]") - feedback_add_details("radio_usage","SEC-[msg_security.len]") - feedback_add_details("radio_usage","DTH-[msg_deathsquad.len]") - feedback_add_details("radio_usage","SYN-[msg_syndicate.len]") - feedback_add_details("radio_usage","SYT-[msg_syndteam.len]") - feedback_add_details("radio_usage","MIN-[msg_mining.len]") - feedback_add_details("radio_usage","CAR-[msg_cargo.len]") - feedback_add_details("radio_usage","SRV-[msg_service.len]") - feedback_add_details("radio_usage","OTH-[messages.len]") - feedback_add_details("radio_usage","PDA-[pda_msg_amt]") - feedback_add_details("radio_usage","RC-[rc_msg_amt]") - - - feedback_set_details("round_end","[time2text(world.realtime)]") //This one MUST be the last one that gets set. - - -//This proc is only to be called at round end. -/obj/machinery/blackbox_recorder/proc/save_all_data_to_sql() - if(!feedback) return - - round_end_data_gathering() //round_end time logging and some other data processing - establish_db_connection() - if(!dbcon.IsConnected()) return - var/round_id - - var/DBQuery/query = dbcon.NewQuery("SELECT MAX(round_id) AS round_id FROM [format_table_name("feedback")]") - query.Execute() - while(query.NextRow()) - round_id = query.item[1] - - if(!isnum(round_id)) - round_id = text2num(round_id) - round_id++ - - for(var/datum/feedback_variable/FV in feedback) - var/sql = "INSERT INTO [format_table_name("feedback")] VALUES (null, Now(), [round_id], \"[FV.get_variable()]\", [FV.get_value()], \"[FV.get_details()]\")" - var/DBQuery/query_insert = dbcon.NewQuery(sql) - query_insert.Execute() - -/obj/machinery/blackbox_recorder/vv_edit_var(var_name, var_value) - return FALSE // don't fuck with the stupid blackbox shit - - -proc/feedback_set(var/variable,var/value) - if(!blackbox) return - - variable = sanitizeSQL(variable) - - var/datum/feedback_variable/FV = blackbox.find_feedback_datum(variable) - - if(!FV) return - - FV.set_value(value) - -proc/feedback_inc(var/variable,var/value) - if(!blackbox) return - - variable = sanitizeSQL(variable) - - var/datum/feedback_variable/FV = blackbox.find_feedback_datum(variable) - - if(!FV) return - - FV.inc(value) - -proc/feedback_dec(var/variable,var/value) - if(!blackbox) return - - variable = sanitizeSQL(variable) - - var/datum/feedback_variable/FV = blackbox.find_feedback_datum(variable) - - if(!FV) return - - FV.dec(value) - -proc/feedback_set_details(var/variable,var/details) - if(!blackbox) return - - variable = sanitizeSQL(variable) - details = sanitizeSQL(details) - - var/datum/feedback_variable/FV = blackbox.find_feedback_datum(variable) - - if(!FV) return - - FV.set_details(details) - -proc/feedback_add_details(var/variable,var/details) - if(!blackbox) return - - variable = sanitizeSQL(variable) - details = sanitizeSQL(details) - - var/datum/feedback_variable/FV = blackbox.find_feedback_datum(variable) - - if(!FV) return - - FV.add_details(details) +var/global/list/obj/machinery/message_server/message_servers = list() + +/datum/data_pda_msg + var/recipient = "Unspecified" //name of the person + var/sender = "Unspecified" //name of the sender + var/message = "Blank" //transferred message + +/datum/data_pda_msg/New(var/param_rec = "",var/param_sender = "",var/param_message = "") + + if(param_rec) + recipient = param_rec + if(param_sender) + sender = param_sender + if(param_message) + message = param_message + +/datum/data_rc_msg + var/rec_dpt = "Unspecified" //name of the person + var/send_dpt = "Unspecified" //name of the sender + var/message = "Blank" //transferred message + var/stamp = "Unstamped" + var/id_auth = "Unauthenticated" + var/priority = "Normal" + +/datum/data_rc_msg/New(var/param_rec = "",var/param_sender = "",var/param_message = "",var/param_stamp = "",var/param_id_auth = "",var/param_priority) + if(param_rec) + rec_dpt = param_rec + if(param_sender) + send_dpt = param_sender + if(param_message) + message = param_message + if(param_stamp) + stamp = param_stamp + if(param_id_auth) + id_auth = param_id_auth + if(param_priority) + switch(param_priority) + if(1) + priority = "Normal" + if(2) + priority = "High" + if(3) + priority = "Extreme" + else + priority = "Undetermined" + +/obj/machinery/message_server + icon = 'icons/obj/machines/research.dmi' + icon_state = "server" + name = "Messaging Server" + density = 1 + anchored = 1.0 + use_power = IDLE_POWER_USE + idle_power_usage = 10 + active_power_usage = 100 + + var/list/datum/data_pda_msg/pda_msgs = list() + var/list/datum/data_rc_msg/rc_msgs = list() + var/active = 1 + var/decryptkey = "password" + +/obj/machinery/message_server/New() + message_servers += src + decryptkey = GenerateKey() + send_pda_message("System Administrator", "system", "This is an automated message. The messaging system is functioning correctly.") + ..() + return + +/obj/machinery/message_server/Destroy() + message_servers -= src + return ..() + +/obj/machinery/message_server/process() + //if(decryptkey == "password") + // decryptkey = generateKey() + if(active && (stat & (BROKEN|NOPOWER))) + active = 0 + return + if(prob(3)) + playsound(loc, "computer_ambience", 50, 1) + update_icon() + return + +/obj/machinery/message_server/proc/send_pda_message(var/recipient = "",var/sender = "",var/message = "") + pda_msgs += new/datum/data_pda_msg(recipient,sender,message) + +/obj/machinery/message_server/proc/send_rc_message(var/recipient = "",var/sender = "",var/message = "",var/stamp = "", var/id_auth = "", var/priority = 1) + rc_msgs += new/datum/data_rc_msg(recipient,sender,message,stamp,id_auth) + var/authmsg = "[message]
        " + if(id_auth) + authmsg += "[id_auth]
        " + if(stamp) + authmsg += "[stamp]
        " + for(var/obj/machinery/requests_console/Console in allConsoles) + if(ckey(Console.department) == ckey(recipient)) + if(Console.inoperable()) + Console.message_log += "Message lost due to console failure.
        Please contact [station_name()] system adminsitrator or AI for technical assistance.
        " + continue + if(Console.newmessagepriority < priority) + Console.newmessagepriority = priority + Console.icon_state = "req_comp[priority]" + switch(priority) + if(2) + if(!Console.silent) + playsound(Console.loc, 'sound/machines/twobeep.ogg', 50, 1) + Console.audible_message(text("[bicon(Console)] *The Requests Console beeps: 'PRIORITY Alert in [sender]'"),,5) + Console.message_log += "High Priority message from [sender]
        [authmsg]" + else + if(!Console.silent) + playsound(Console.loc, 'sound/machines/twobeep.ogg', 50, 1) + Console.audible_message(text("[bicon(Console)] *The Requests Console beeps: 'Message from [sender]'"),,4) + Console.message_log += "Message from [sender]
        [authmsg]" + Console.set_light(2) + +/obj/machinery/message_server/attack_hand(user as mob) +// to_chat(user, "There seem to be some parts missing from this server. They should arrive on the station in a few days, give or take a few CentComm delays.") + to_chat(user, "You toggle PDA message passing from [active ? "On" : "Off"] to [active ? "Off" : "On"]") + active = !active + update_icon() + + return + +/obj/machinery/message_server/update_icon() + if((stat & (BROKEN|NOPOWER))) + icon_state = "server-nopower" + else if(!active) + icon_state = "server-off" + else + icon_state = "server-on" + + return + + +/datum/feedback_variable + var/variable + var/value + var/details + +/datum/feedback_variable/New(var/param_variable,var/param_value = 0) + variable = param_variable + value = param_value + +/datum/feedback_variable/vv_edit_var(var_name, var_value) + return FALSE // come on guys don't break the stats + +/datum/feedback_variable/proc/inc(var/num = 1) + if(isnum(value)) + value += num + else + value = text2num(value) + if(isnum(value)) + value += num + else + value = num + +/datum/feedback_variable/proc/dec(var/num = 1) + if(isnum(value)) + value -= num + else + value = text2num(value) + if(isnum(value)) + value -= num + else + value = -num + +/datum/feedback_variable/proc/set_value(var/num) + if(isnum(num)) + value = num + +/datum/feedback_variable/proc/get_value() + return value + +/datum/feedback_variable/proc/get_variable() + return variable + +/datum/feedback_variable/proc/set_details(var/text) + if(istext(text)) + details = text + +/datum/feedback_variable/proc/add_details(var/text) + if(istext(text)) + if(!details) + details = text + else + details += " [text]" + +/datum/feedback_variable/proc/get_details() + return details + +/datum/feedback_variable/proc/get_parsed() + return list(variable,value,details) + +var/obj/machinery/blackbox_recorder/blackbox + +//TODO: kill whoever designed this cancer +/obj/machinery/blackbox_recorder + icon = 'icons/obj/stationobjs.dmi' + icon_state = "blackbox" + name = "Blackbox Recorder" + density = 1 + anchored = 1.0 + use_power = IDLE_POWER_USE + idle_power_usage = 10 + active_power_usage = 100 + var/list/messages = list() //Stores messages of non-standard frequencies + var/list/messages_admin = list() + + var/list/msg_common = list() + var/list/msg_science = list() + var/list/msg_command = list() + var/list/msg_medical = list() + var/list/msg_engineering = list() + var/list/msg_security = list() + var/list/msg_deathsquad = list() + var/list/msg_syndicate = list() + var/list/msg_syndteam = list() + var/list/msg_mining = list() + var/list/msg_cargo = list() + var/list/msg_service = list() + + var/list/datum/feedback_variable/feedback = new() + + //Only one can exsist in the world! +/obj/machinery/blackbox_recorder/New() + if(blackbox) + if(istype(blackbox,/obj/machinery/blackbox_recorder)) + qdel(src) + blackbox = src + +/obj/machinery/blackbox_recorder/Destroy() + var/turf/T = locate(1,1,2) + if(T) + blackbox = null + var/obj/machinery/blackbox_recorder/BR = new/obj/machinery/blackbox_recorder(T) + BR.msg_common = msg_common + BR.msg_science = msg_science + BR.msg_command = msg_command + BR.msg_medical = msg_medical + BR.msg_engineering = msg_engineering + BR.msg_security = msg_security + BR.msg_deathsquad = msg_deathsquad + BR.msg_syndicate = msg_syndicate + BR.msg_syndteam = msg_syndteam + BR.msg_mining = msg_mining + BR.msg_cargo = msg_cargo + BR.msg_service = msg_service + BR.feedback = feedback + BR.messages = messages + BR.messages_admin = messages_admin + if(blackbox != BR) + blackbox = BR + return ..() + +/obj/machinery/blackbox_recorder/proc/find_feedback_datum(var/variable) + for(var/datum/feedback_variable/FV in feedback) + if(FV.get_variable() == variable) + return FV + var/datum/feedback_variable/FV = new(variable) + feedback += FV + return FV + +/obj/machinery/blackbox_recorder/proc/get_round_feedback() + return feedback + +/obj/machinery/blackbox_recorder/proc/round_end_data_gathering() + + var/pda_msg_amt = 0 + var/rc_msg_amt = 0 + + for(var/obj/machinery/message_server/MS in GLOB.machines) + if(MS.pda_msgs.len > pda_msg_amt) + pda_msg_amt = MS.pda_msgs.len + if(MS.rc_msgs.len > rc_msg_amt) + rc_msg_amt = MS.rc_msgs.len + + feedback_set_details("radio_usage","") + + feedback_add_details("radio_usage","COM-[msg_common.len]") + feedback_add_details("radio_usage","SCI-[msg_science.len]") + feedback_add_details("radio_usage","HEA-[msg_command.len]") + feedback_add_details("radio_usage","MED-[msg_medical.len]") + feedback_add_details("radio_usage","ENG-[msg_engineering.len]") + feedback_add_details("radio_usage","SEC-[msg_security.len]") + feedback_add_details("radio_usage","DTH-[msg_deathsquad.len]") + feedback_add_details("radio_usage","SYN-[msg_syndicate.len]") + feedback_add_details("radio_usage","SYT-[msg_syndteam.len]") + feedback_add_details("radio_usage","MIN-[msg_mining.len]") + feedback_add_details("radio_usage","CAR-[msg_cargo.len]") + feedback_add_details("radio_usage","SRV-[msg_service.len]") + feedback_add_details("radio_usage","OTH-[messages.len]") + feedback_add_details("radio_usage","PDA-[pda_msg_amt]") + feedback_add_details("radio_usage","RC-[rc_msg_amt]") + + + feedback_set_details("round_end","[time2text(world.realtime)]") //This one MUST be the last one that gets set. + + +//This proc is only to be called at round end. +/obj/machinery/blackbox_recorder/proc/save_all_data_to_sql() + if(!feedback) return + + round_end_data_gathering() //round_end time logging and some other data processing + establish_db_connection() + if(!dbcon.IsConnected()) return + var/round_id + + var/DBQuery/query = dbcon.NewQuery("SELECT MAX(round_id) AS round_id FROM [format_table_name("feedback")]") + query.Execute() + while(query.NextRow()) + round_id = query.item[1] + + if(!isnum(round_id)) + round_id = text2num(round_id) + round_id++ + + for(var/datum/feedback_variable/FV in feedback) + var/sql = "INSERT INTO [format_table_name("feedback")] VALUES (null, Now(), [round_id], \"[FV.get_variable()]\", [FV.get_value()], \"[FV.get_details()]\")" + var/DBQuery/query_insert = dbcon.NewQuery(sql) + query_insert.Execute() + +/obj/machinery/blackbox_recorder/vv_edit_var(var_name, var_value) + return FALSE // don't fuck with the stupid blackbox shit + + +proc/feedback_set(var/variable,var/value) + if(!blackbox) return + + variable = sanitizeSQL(variable) + + var/datum/feedback_variable/FV = blackbox.find_feedback_datum(variable) + + if(!FV) return + + FV.set_value(value) + +proc/feedback_inc(var/variable,var/value) + if(!blackbox) return + + variable = sanitizeSQL(variable) + + var/datum/feedback_variable/FV = blackbox.find_feedback_datum(variable) + + if(!FV) return + + FV.inc(value) + +proc/feedback_dec(var/variable,var/value) + if(!blackbox) return + + variable = sanitizeSQL(variable) + + var/datum/feedback_variable/FV = blackbox.find_feedback_datum(variable) + + if(!FV) return + + FV.dec(value) + +proc/feedback_set_details(var/variable,var/details) + if(!blackbox) return + + variable = sanitizeSQL(variable) + details = sanitizeSQL(details) + + var/datum/feedback_variable/FV = blackbox.find_feedback_datum(variable) + + if(!FV) return + + FV.set_details(details) + +proc/feedback_add_details(var/variable,var/details) + if(!blackbox) return + + variable = sanitizeSQL(variable) + details = sanitizeSQL(details) + + var/datum/feedback_variable/FV = blackbox.find_feedback_datum(variable) + + if(!FV) return + + FV.add_details(details) diff --git a/code/modules/research/protolathe.dm b/code/modules/research/protolathe.dm index a441dff00b3..6f2e608ef4b 100644 --- a/code/modules/research/protolathe.dm +++ b/code/modules/research/protolathe.dm @@ -1,113 +1,113 @@ -/* -Protolathe - -Similar to an autolathe, you load glass and metal sheets (but not other objects) into it to be used as raw materials for the stuff -it creates. All the menus and other manipulation commands are in the R&D console. - -Note: Must be placed west/left of and R&D console to function. - -*/ -/obj/machinery/r_n_d/protolathe - name = "Protolathe" - desc = "Converts raw materials into useful objects." - icon_state = "protolathe" - container_type = OPENCONTAINER - - var/efficiency_coeff - - var/list/categories = list( - "Bluespace", - "Computer Parts", - "Equipment", - "Janitorial", - "Medical", - "Mining", - "Miscellaneous", - "Power", - "Stock Parts", - "Weapons" - ) - - reagents = new() - - -/obj/machinery/r_n_d/protolathe/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/protolathe(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stock_parts/matter_bin(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/stock_parts/manipulator(null) - component_parts += new /obj/item/reagent_containers/glass/beaker/large(null) - component_parts += new /obj/item/reagent_containers/glass/beaker/large(null) - RefreshParts() - - reagents.my_atom = src - -/obj/machinery/r_n_d/protolathe/upgraded/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/protolathe(null) - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/matter_bin/super(null) - component_parts += new /obj/item/stock_parts/manipulator/pico(null) - component_parts += new /obj/item/stock_parts/manipulator/pico(null) - component_parts += new /obj/item/reagent_containers/glass/beaker/large(null) - component_parts += new /obj/item/reagent_containers/glass/beaker/large(null) - RefreshParts() - - reagents.my_atom = src - -/obj/machinery/r_n_d/protolathe/RefreshParts() - var/T = 0 - for(var/obj/item/reagent_containers/glass/G in component_parts) - G.reagents.trans_to(src, G.reagents.total_volume) - for(var/obj/item/stock_parts/matter_bin/M in component_parts) - T += M.rating - materials.max_amount = T * 75000 - T = 1.2 - for(var/obj/item/stock_parts/manipulator/M in component_parts) - T -= M.rating/10 - efficiency_coeff = min(max(0, T), 1) - -/obj/machinery/r_n_d/protolathe/proc/check_mat(datum/design/being_built, var/M) // now returns how many times the item can be built with the material - var/A = materials.amount(M) - if(!A) - A = reagents.get_reagent_amount(M) - A = A / max(1, (being_built.reagents_list[M])) - else - A = A / max(1, (being_built.materials[M])) - return A - -/obj/machinery/r_n_d/protolathe/attackby(var/obj/item/O as obj, var/mob/user as mob, params) - if(shocked) - if(shock(user,50)) - return TRUE - if(default_deconstruction_screwdriver(user, "protolathe_t", "protolathe", O)) - if(linked_console) - linked_console.linked_lathe = null - linked_console = null - return - - if(exchange_parts(user, O)) - return - - if(panel_open) - if(istype(O, /obj/item/crowbar)) - for(var/obj/I in component_parts) - if(istype(I, /obj/item/reagent_containers/glass/beaker)) - reagents.trans_to(I, reagents.total_volume) - I.loc = src.loc - for(var/obj/item/reagent_containers/glass/G in component_parts) - reagents.trans_to(G, G.reagents.maximum_volume) - materials.retrieve_all() - default_deconstruction_crowbar(O) - return 1 - else - to_chat(user, "You can't load the [src.name] while it's opened.") - return 1 - if(O.is_open_container()) - return FALSE - else - return ..() \ No newline at end of file +/* +Protolathe + +Similar to an autolathe, you load glass and metal sheets (but not other objects) into it to be used as raw materials for the stuff +it creates. All the menus and other manipulation commands are in the R&D console. + +Note: Must be placed west/left of and R&D console to function. + +*/ +/obj/machinery/r_n_d/protolathe + name = "Protolathe" + desc = "Converts raw materials into useful objects." + icon_state = "protolathe" + container_type = OPENCONTAINER + + var/efficiency_coeff + + var/list/categories = list( + "Bluespace", + "Computer Parts", + "Equipment", + "Janitorial", + "Medical", + "Mining", + "Miscellaneous", + "Power", + "Stock Parts", + "Weapons" + ) + + reagents = new() + + +/obj/machinery/r_n_d/protolathe/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/protolathe(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/matter_bin(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/stock_parts/manipulator(null) + component_parts += new /obj/item/reagent_containers/glass/beaker/large(null) + component_parts += new /obj/item/reagent_containers/glass/beaker/large(null) + RefreshParts() + + reagents.my_atom = src + +/obj/machinery/r_n_d/protolathe/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/protolathe(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/matter_bin/super(null) + component_parts += new /obj/item/stock_parts/manipulator/pico(null) + component_parts += new /obj/item/stock_parts/manipulator/pico(null) + component_parts += new /obj/item/reagent_containers/glass/beaker/large(null) + component_parts += new /obj/item/reagent_containers/glass/beaker/large(null) + RefreshParts() + + reagents.my_atom = src + +/obj/machinery/r_n_d/protolathe/RefreshParts() + var/T = 0 + for(var/obj/item/reagent_containers/glass/G in component_parts) + G.reagents.trans_to(src, G.reagents.total_volume) + for(var/obj/item/stock_parts/matter_bin/M in component_parts) + T += M.rating + materials.max_amount = T * 75000 + T = 1.2 + for(var/obj/item/stock_parts/manipulator/M in component_parts) + T -= M.rating/10 + efficiency_coeff = min(max(0, T), 1) + +/obj/machinery/r_n_d/protolathe/proc/check_mat(datum/design/being_built, var/M) // now returns how many times the item can be built with the material + var/A = materials.amount(M) + if(!A) + A = reagents.get_reagent_amount(M) + A = A / max(1, (being_built.reagents_list[M])) + else + A = A / max(1, (being_built.materials[M])) + return A + +/obj/machinery/r_n_d/protolathe/attackby(var/obj/item/O as obj, var/mob/user as mob, params) + if(shocked) + if(shock(user,50)) + return TRUE + if(default_deconstruction_screwdriver(user, "protolathe_t", "protolathe", O)) + if(linked_console) + linked_console.linked_lathe = null + linked_console = null + return + + if(exchange_parts(user, O)) + return + + if(panel_open) + if(istype(O, /obj/item/crowbar)) + for(var/obj/I in component_parts) + if(istype(I, /obj/item/reagent_containers/glass/beaker)) + reagents.trans_to(I, reagents.total_volume) + I.loc = src.loc + for(var/obj/item/reagent_containers/glass/G in component_parts) + reagents.trans_to(G, G.reagents.maximum_volume) + materials.retrieve_all() + default_deconstruction_crowbar(user, O) + return 1 + else + to_chat(user, "You can't load the [src.name] while it's opened.") + return 1 + if(O.is_open_container()) + return FALSE + else + return ..() diff --git a/code/modules/research/rd-readme.dm b/code/modules/research/rd-readme.dm index 1ecc9dbb0da..d1b8f77056c 100644 --- a/code/modules/research/rd-readme.dm +++ b/code/modules/research/rd-readme.dm @@ -1,239 +1,239 @@ -/* -Research and Development System. (Designed specifically for the /tg/station 13 (Space Station 13) open source project) - -///////////////Overview/////////////////// -This system is a "tech tree" research and development system designed for SS13. It allows a "researcher" job (this document assumes -the "scientist" job is given this role) the tools necessiary to research new and better technologies. In general, the system works -by breaking existing technology and using what you learn from to advance your knowledge of SCIENCE! As your knowledge progresses, -you can build newer (and better?) devices (which you can also, eventually, deconstruct to advance your knowledge). - -A brief overview is below. For more details, see the related files. - -////////////Game Use///////////// -The major research and development is performed using a combination of four machines: -- R&D Console: A computer console that allows you to manipulate the other devices that are linked to it and view/manipulate the -technologies you have researched so far. -- Protolathe: Used to make new hand-held devices and parts for larger devices. All metals and reagents as raw materials. -- Destructive Analyzer: You can put hand-held objects into it and it'll analyze them for technological advancements but it destroys -them in the process. Destroyed items will send their raw materials to a linked Protolathe (if any) -- Circuit Imprinter: Similar to the Protolathe, it allows for the construction of circuit boards. Uses glass and acid as the raw -materials. - -While researching you are dealing with two different types of information: Technology Paths and Device Designs. Technology Paths -are the "Tech Trees" of the game. You start out with a number of them at the game start and they are improved by using the -Destructive Analyzer. By themselves, they don't do a whole lot. However, they unlock Device Designs. This is the information used -by the circuit imprinter and the protolathe to produce objects. - -//EXISTING TECH -Each tech path should have at LEAST one item at every level (levels 1 - 20). This is to allow for a more fluid progression of the -researching. Existing tech (ie, anything you can find on the station or get from the quartermaster) shouldn't go higher then -level 5 or 7. Everything past that should be stuff you research. - -Below is a checklist to make sure every tree is filled. As new items get added to R&D, add them here if there is an empty slot. -When thinking about new stuff, check here to see if there are any slots unfilled. - -//MATERIALS -1 | Metal -2 | Solid Plasma -3 | Silver -4 | Gold, Super Capacitor -5 | Uranium, Nuclear Gun, SUPERPACMAN -6 | Diamond, MRSPACMAN -7 | -8 | -9 | -10 | -11 | -12 | -13 | -14 | -15 | -16 | -17 | -18 | -19 | -20 | - -//PLASMA TECH -1 | -2 | Solid Plasma -3 | Pacman Generator -4 | -5 | -6 | -7 | -8 | -9 | -10 | -11 | -12 | -13 | -14 | -15 | -16 | -17 | -18 | -19 | -20 | - -//POWER TECH -1 | Basic Capacitor, Basic Cell -2 | High-Capacity Cell (10,000) -3 | Super-Capacity Cell (20,000), Powersink, PACMAN -4 | SUPERPACMAN -5 | MRSPACMAN, Super Capacitor -6 | Hyper-Capacity Cell (30,000) -7 | -8 | -9 | -10 | -11 | -12 | -13 | -14 | -15 | -16 | -17 | -18 | -19 | -20 | - -//BLUE SPACE -1 | -2 | Teleporter Console Board -3 | Teleport Gun, Hand Tele -4 | Teleportation Scroll -5 | -6 | -7 | -8 | -9 | -10 | -11 | -12 | -13 | -14 | -15 | -16 | -17 | -18 | -19 | -20 | - -//BIOTECH -1 | Bruise Pack, Scalple -2 | PANDEMIC Board, Mass Spectrometer -3 | AI Core, Brains (MMI) -4 | MMI+Radio -5 | -6 | -7 | -8 | -9 | -10 | -11 | -12 | -13 | -14 | -15 | -16 | -17 | -18 | -19 | -20 | - -//MAGNETS -1 | Basic Sensor -2 | Comm Console Board -3 | Adv Sensor -4 | Adv Mass Spectrometer, Chameleon Projector -5 | Phasic Sensor -6 | -7 | -8 | -9 | -10 | -11 | -12 | -13 | -14 | -15 | -16 | -17 | -18 | -19 | -20 | - -//PROGRAMMING -1 | Arcade Board -2 | Sec Camera -3 | Cloning Machine Console Board -4 | AI Core, Intellicard -5 | Pico-Manipulator, Ultra-Micro-Laser -6 | -7 | -8 | -9 | -10 | -11 | -12 | -13 | -14 | -15 | -16 | -17 | -18 | -19 | -20 | - -//SYNDICATE -1 | Sleepypen -2 | TYRANT Module, Emag -3 | Cloaking Device, Power Sink -4 | -5 | -6 | -7 | -8 | -9 | -10 | -11 | -12 | -13 | -14 | -15 | -16 | -17 | -18 | -19 | -20 | - -//COMBAT -1 | Flashbang, Mousetrap, Nettle -2 | Stun Baton -3 | Power Axe, Death Nettle, Nuclear Gun -4 | -5 | -6 | -7 | -8 | -9 | -10 | -11 | -12 | -13 | -14 | -15 | -16 | -17 | -18 | -19 | -20 | - - - - - - - -*/ \ No newline at end of file +/* +Research and Development System. (Designed specifically for the /tg/station 13 (Space Station 13) open source project) + +///////////////Overview/////////////////// +This system is a "tech tree" research and development system designed for SS13. It allows a "researcher" job (this document assumes +the "scientist" job is given this role) the tools necessiary to research new and better technologies. In general, the system works +by breaking existing technology and using what you learn from to advance your knowledge of SCIENCE! As your knowledge progresses, +you can build newer (and better?) devices (which you can also, eventually, deconstruct to advance your knowledge). + +A brief overview is below. For more details, see the related files. + +////////////Game Use///////////// +The major research and development is performed using a combination of four machines: +- R&D Console: A computer console that allows you to manipulate the other devices that are linked to it and view/manipulate the +technologies you have researched so far. +- Protolathe: Used to make new hand-held devices and parts for larger devices. All metals and reagents as raw materials. +- Destructive Analyzer: You can put hand-held objects into it and it'll analyze them for technological advancements but it destroys +them in the process. Destroyed items will send their raw materials to a linked Protolathe (if any) +- Circuit Imprinter: Similar to the Protolathe, it allows for the construction of circuit boards. Uses glass and acid as the raw +materials. + +While researching you are dealing with two different types of information: Technology Paths and Device Designs. Technology Paths +are the "Tech Trees" of the game. You start out with a number of them at the game start and they are improved by using the +Destructive Analyzer. By themselves, they don't do a whole lot. However, they unlock Device Designs. This is the information used +by the circuit imprinter and the protolathe to produce objects. + +//EXISTING TECH +Each tech path should have at LEAST one item at every level (levels 1 - 20). This is to allow for a more fluid progression of the +researching. Existing tech (ie, anything you can find on the station or get from the quartermaster) shouldn't go higher then +level 5 or 7. Everything past that should be stuff you research. + +Below is a checklist to make sure every tree is filled. As new items get added to R&D, add them here if there is an empty slot. +When thinking about new stuff, check here to see if there are any slots unfilled. + +//MATERIALS +1 | Metal +2 | Solid Plasma +3 | Silver +4 | Gold, Super Capacitor +5 | Uranium, Nuclear Gun, SUPERPACMAN +6 | Diamond, MRSPACMAN +7 | +8 | +9 | +10 | +11 | +12 | +13 | +14 | +15 | +16 | +17 | +18 | +19 | +20 | + +//PLASMA TECH +1 | +2 | Solid Plasma +3 | Pacman Generator +4 | +5 | +6 | +7 | +8 | +9 | +10 | +11 | +12 | +13 | +14 | +15 | +16 | +17 | +18 | +19 | +20 | + +//POWER TECH +1 | Basic Capacitor, Basic Cell +2 | High-Capacity Cell (10,000) +3 | Super-Capacity Cell (20,000), Powersink, PACMAN +4 | SUPERPACMAN +5 | MRSPACMAN, Super Capacitor +6 | Hyper-Capacity Cell (30,000) +7 | +8 | +9 | +10 | +11 | +12 | +13 | +14 | +15 | +16 | +17 | +18 | +19 | +20 | + +//BLUE SPACE +1 | +2 | Teleporter Console Board +3 | Teleport Gun, Hand Tele +4 | Teleportation Scroll +5 | +6 | +7 | +8 | +9 | +10 | +11 | +12 | +13 | +14 | +15 | +16 | +17 | +18 | +19 | +20 | + +//BIOTECH +1 | Bruise Pack, Scalple +2 | PANDEMIC Board, Mass Spectrometer +3 | AI Core, Brains (MMI) +4 | MMI+Radio +5 | +6 | +7 | +8 | +9 | +10 | +11 | +12 | +13 | +14 | +15 | +16 | +17 | +18 | +19 | +20 | + +//MAGNETS +1 | Basic Sensor +2 | Comm Console Board +3 | Adv Sensor +4 | Adv Mass Spectrometer, Chameleon Projector +5 | Phasic Sensor +6 | +7 | +8 | +9 | +10 | +11 | +12 | +13 | +14 | +15 | +16 | +17 | +18 | +19 | +20 | + +//PROGRAMMING +1 | Arcade Board +2 | Sec Camera +3 | Cloning Machine Console Board +4 | AI Core, Intellicard +5 | Pico-Manipulator, Ultra-Micro-Laser +6 | +7 | +8 | +9 | +10 | +11 | +12 | +13 | +14 | +15 | +16 | +17 | +18 | +19 | +20 | + +//SYNDICATE +1 | Sleepypen +2 | TYRANT Module, Emag +3 | Cloaking Device, Power Sink +4 | +5 | +6 | +7 | +8 | +9 | +10 | +11 | +12 | +13 | +14 | +15 | +16 | +17 | +18 | +19 | +20 | + +//COMBAT +1 | Flashbang, Mousetrap, Nettle +2 | Stun Baton +3 | Power Axe, Death Nettle, Nuclear Gun +4 | +5 | +6 | +7 | +8 | +9 | +10 | +11 | +12 | +13 | +14 | +15 | +16 | +17 | +18 | +19 | +20 | + + + + + + + +*/ diff --git a/code/modules/research/rdconsole.dm b/code/modules/research/rdconsole.dm index 202b931b7a9..d4132bf3f4b 100644 --- a/code/modules/research/rdconsole.dm +++ b/code/modules/research/rdconsole.dm @@ -1,948 +1,948 @@ -/* -Research and Development (R&D) Console - -This is the main work horse of the R&D system. It contains the menus/controls for the Destructive Analyzer, Protolathe, and Circuit -imprinter. It also contains the /datum/research holder with all the known/possible technology paths and device designs. - -Basic use: When it first is created, it will attempt to link up to related devices within 3 squares. It'll only link up if they -aren't already linked to another console. Any consoles it cannot link up with (either because all of a certain type are already -linked or there aren't any in range), you'll just not have access to that menu. In the settings menu, there are menu options that -allow a player to attempt to re-sync with nearby consoles. You can also force it to disconnect from a specific console. - -The imprinting and construction menus do NOT require toxins access to access but all the other menus do. However, if you leave it -on a menu, nothing is to stop the person from using the options on that menu (although they won't be able to change to a different -one). You can also lock the console on the settings menu if you're feeling paranoid and you don't want anyone messing with it who -doesn't have toxins access. - -When a R&D console is destroyed or even partially disassembled, you lose all research data on it. However, there are two ways around -this dire fate: -- The easiest way is to go to the settings menu and select "Sync Database with Network." That causes it to upload (but not download) -it's data to every other device in the game. Each console has a "disconnect from network" option that'll will cause data base sync -operations to skip that console. This is useful if you want to make a "public" R&D console or, for example, give the engineers -a circuit imprinter with certain designs on it and don't want it accidentally updating. The downside of this method is that you have -to have physical access to the other console to send data back. Note: An R&D console is on Centcom so if a random griffan happens to -cause a ton of data to be lost, an admin can go send it back. -- The second method is with Technology Disks and Design Disks. Each of these disks can hold a single technology or design datum in -it's entirety. You can then take the disk to any R&D console and upload it's data to it. This method is a lot more secure (since it -won't update every console in existence) but it's more of a hassle to do. Also, the disks can be stolen. - - -*/ - -// Who likes #defines? -// I don't! -// but I gotta add 'em anyways because we have a bias against /const statements for some reason -#define TECH_UPDATE_DELAY 50 -#define DESIGN_UPDATE_DELAY 50 -#define PROTOLATHE_CONSTRUCT_DELAY 32 -#define SYNC_RESEARCH_DELAY 30 -#define DECONSTRUCT_DELAY 24 -#define SYNC_DEVICE_DELAY 20 -#define RESET_RESEARCH_DELAY 20 -#define IMPRINTER_DELAY 16 - -/obj/machinery/computer/rdconsole - name = "\improper R&D console" - icon_screen = "rdcomp" - icon_keyboard = "rd_key" - light_color = LIGHT_COLOR_FADEDPURPLE - circuit = /obj/item/circuitboard/rdconsole - var/datum/research/files //Stores all the collected research data. - var/obj/item/disk/tech_disk/t_disk = null //Stores the technology disk. - var/obj/item/disk/design_disk/d_disk = null //Stores the design disk. - - var/obj/machinery/r_n_d/destructive_analyzer/linked_destroy = null //Linked Destructive Analyzer - var/obj/machinery/r_n_d/protolathe/linked_lathe = null //Linked Protolathe - var/obj/machinery/r_n_d/circuit_imprinter/linked_imprinter = null //Linked Circuit Imprinter - - var/screen = 1.0 //Which screen is currently showing. - - var/menu = 0 // Current menu. - var/submenu = 0 - var/wait_message = 0 - var/wait_message_timer = 0 - - var/id = 0 //ID of the computer (for server restrictions). - var/sync = 1 //If sync = 0, it doesn't show up on Server Control Console - - req_access = list(access_tox) //Data and setting manipulation requires scientist access. - - var/selected_category - var/list/datum/design/matching_designs = list() //for the search function - -/proc/CallTechName(ID) //A simple helper proc to find the name of a tech with a given ID. - for(var/T in subtypesof(/datum/tech)) - var/datum/tech/tt = T - if(initial(tt.id) == ID) - return initial(tt.name) - -/proc/CallMaterialName(ID) - if(copytext(ID, 1, 2) == "$") - var/return_name = copytext(ID, 2) - switch(return_name) - if("metal") - return_name = "Metal" - if("glass") - return_name = "Glass" - if("gold") - return_name = "Gold" - if("silver") - return_name = "Silver" - if("plasma") - return_name = "Solid Plasma" - if("uranium") - return_name = "Uranium" - if("diamond") - return_name = "Diamond" - if("clown") - return_name = "Bananium" - if("mime") - return_name = "Tranquillite" - if("titanium") - return_name = "Titanium" - if("bluespace") - return_name = "Bluespace Mesh" - if("plastic") - return_name = "Plastic" - return return_name - else - for(var/R in subtypesof(/datum/reagent)) - var/datum/reagent/rt = R - if(initial(rt.id) == ID) - return initial(rt.name) - -/obj/machinery/computer/rdconsole/proc/SyncRDevices() //Makes sure it is properly sync'ed up with the devices attached to it (if any). - for(var/obj/machinery/r_n_d/D in range(3,src)) - if(!isnull(D.linked_console) || D.disabled || D.panel_open) - continue - if(istype(D, /obj/machinery/r_n_d/destructive_analyzer)) - if(linked_destroy == null) - linked_destroy = D - D.linked_console = src - else if(istype(D, /obj/machinery/r_n_d/protolathe)) - if(linked_lathe == null) - linked_lathe = D - D.linked_console = src - else if(istype(D, /obj/machinery/r_n_d/circuit_imprinter)) - if(linked_imprinter == null) - linked_imprinter = D - D.linked_console = src - return - -//Have it automatically push research to the centcom server so wild griffins can't fuck up R&D's work --NEO -/obj/machinery/computer/rdconsole/proc/griefProtection() - for(var/obj/machinery/r_n_d/server/centcom/C in world) - files.push_data(C.files) - -/obj/machinery/computer/rdconsole/proc/Maximize() - for(var/datum/tech/T in files.possible_tech) - files.known_tech[T.id] = T - for(var/v in files.known_tech) - var/datum/tech/KT = files.known_tech[v] - if(KT.level < KT.max_level) - KT.level=KT.max_level - files.RefreshResearch() - -/obj/machinery/computer/rdconsole/New() - ..() - files = new /datum/research(src) //Setup the research data holder. - matching_designs = list() - if(!id) - for(var/obj/machinery/r_n_d/server/centcom/S in world) - S.initialize_serv() - break - -/obj/machinery/computer/rdconsole/Initialize() - ..() - SyncRDevices() - -/obj/machinery/computer/rdconsole/Destroy() - if(wait_message_timer) - deltimer(wait_message_timer) - wait_message_timer = 0 - return ..() - -/* Instead of calling this every tick, it is only being called when needed -/obj/machinery/computer/rdconsole/process() - griefProtection() -*/ - -/obj/machinery/computer/rdconsole/attackby(var/obj/item/D as obj, var/mob/user as mob, params) - - //Loading a disk into it. - if(istype(D, /obj/item/disk)) - if(t_disk || d_disk) - to_chat(user, "A disk is already loaded into the machine.") - return - - if(istype(D, /obj/item/disk/tech_disk)) t_disk = D - else if(istype(D, /obj/item/disk/design_disk)) d_disk = D - else - to_chat(user, "Machine cannot accept disks in that format.") - return - if(!user.drop_item()) - return - D.loc = src - to_chat(user, "You add the disk to the machine!") - else if(!(linked_destroy && linked_destroy.busy) && !(linked_lathe && linked_lathe.busy) && !(linked_imprinter && linked_imprinter.busy)) - ..() - SSnanoui.update_uis(src) - return - -/obj/machinery/computer/rdconsole/emag_act(user as mob) - if(!emagged) - playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1) - req_access = list() - emagged = 1 - to_chat(user, "You disable the security protocols") - -/obj/machinery/computer/rdconsole/Topic(href, href_list) - if(..()) - return 1 - - if(!allowed(usr) && !isobserver(usr)) - return 1 - - add_fingerprint(usr) - - usr.set_machine(src) - if(href_list["menu"]) //Switches menu screens. Converts a sent text string into a number. Saves a LOT of code. - var/temp_screen = text2num(href_list["menu"]) - menu = temp_screen - if(href_list["submenu"]) //Switches menu screens. Converts a sent text string into a number. Saves a LOT of code. - var/temp_screen = text2num(href_list["submenu"]) - submenu = temp_screen - - if(href_list["category"]) - var/compare - - matching_designs.Cut() - - if(menu == 4) - compare = PROTOLATHE - else - compare = IMPRINTER - - for(var/v in files.known_designs) - var/datum/design/D = files.known_designs[v] - if(!(D.build_type & compare)) - continue - if(href_list["category"] in D.category) - matching_designs.Add(D) - submenu = 1 - - selected_category = "Viewing Category [href_list["category"]]" - - else if(href_list["updt_tech"]) //Update the research holder with information from the technology disk. - add_wait_message("Updating Database...", TECH_UPDATE_DELAY) - spawn(TECH_UPDATE_DELAY) - clear_wait_message() - files.AddTech2Known(t_disk.stored) - SSnanoui.update_uis(src) - griefProtection() //Update centcom too - - else if(href_list["clear_tech"]) //Erase data on the technology disk. - if(t_disk) - t_disk.wipe_tech() - - else if(href_list["eject_tech"]) //Eject the technology disk. - if(t_disk) - t_disk.loc = src.loc - t_disk = null - menu = 0 - submenu = 0 - - else if(href_list["copy_tech"]) //Copy some technology data from the research holder to the disk. - // Somehow this href makes me very nervous - t_disk.stored = files.known_tech[href_list["copy_tech_ID"]] - menu = 2 - submenu = 0 - - else if(href_list["updt_design"]) //Updates the research holder with design data from the design disk. - add_wait_message("Updating Database...", DESIGN_UPDATE_DELAY) - spawn(DESIGN_UPDATE_DELAY) - clear_wait_message() - files.AddDesign2Known(d_disk.blueprint) - SSnanoui.update_uis(src) - griefProtection() //Update centcom too - - else if(href_list["clear_design"]) //Erases data on the design disk. - if(d_disk) - d_disk.wipe_blueprint() - - else if(href_list["eject_design"]) //Eject the design disk. - if(d_disk) - d_disk.loc = src.loc - d_disk = null - menu = 0 - submenu = 0 - - else if(href_list["copy_design"]) //Copy design data from the research holder to the design disk. - // This href ALSO makes me very nervous - var/datum/design/D = files.known_designs[href_list["copy_design_ID"]] - if(D) - // eeeeeep design datums are global be careful! - var/autolathe_friendly = 1 - for(var/x in D.materials) - if( !(x in list(MAT_METAL, MAT_GLASS))) - autolathe_friendly = 0 - D.category -= "Imported" - if(D.locked) - autolathe_friendly = 0 - D.category -= "Imported" - if(D.build_type & (AUTOLATHE|PROTOLATHE|CRAFTLATHE)) // Specifically excludes circuit imprinter and mechfab - D.build_type = autolathe_friendly ? (D.build_type | AUTOLATHE) : D.build_type - D.category |= "Imported" - d_disk.blueprint = D - menu = 2 - submenu = 0 - - else if(href_list["eject_item"]) //Eject the item inside the destructive analyzer. - if(linked_destroy) - if(linked_destroy.busy) - to_chat(usr, " The destructive analyzer is busy at the moment.") - - else if(linked_destroy.loaded_item) - linked_destroy.loaded_item.loc = linked_destroy.loc - linked_destroy.loaded_item = null - linked_destroy.icon_state = "d_analyzer" - menu = 3 - - else if(href_list["maxresearch"]) //Eject the item inside the destructive analyzer. - if(!check_rights(R_ADMIN)) - return - if(alert("Are you sure you want to maximize research levels?","Confirmation","Yes","No")=="No") - return - log_admin("[key_name(usr)] has maximized the research levels.") - message_admins("[key_name_admin(usr)] has maximized the research levels.") - Maximize() - SSnanoui.update_uis(src) - griefProtection() //Update centcomm too - - else if(href_list["deconstruct"]) //Deconstruct the item in the destructive analyzer and update the research holder. - if(linked_destroy) - if(linked_destroy.busy) - to_chat(usr, "The destructive analyzer is busy at the moment.") - return - var/list/temp_tech = linked_destroy.ConvertReqString2List(linked_destroy.loaded_item.origin_tech) - var/cancontinue = FALSE - for(var/T in temp_tech) - if(files.IsTechHigher(T, temp_tech[T])) - cancontinue = TRUE - break - if(!cancontinue) - var/choice = input("This item does not raise tech levels. Proceed destroying loaded item anyway?") in list("Proceed", "Cancel") - if(choice == "Cancel" || !linked_destroy) - return - linked_destroy.busy = 1 - add_wait_message("Processing and Updating Database...", DECONSTRUCT_DELAY) - SSnanoui.update_uis(src) - flick("d_analyzer_process", linked_destroy) - spawn(DECONSTRUCT_DELAY) - clear_wait_message() - if(linked_destroy) - linked_destroy.busy = 0 - if(!linked_destroy.hacked) - if(!linked_destroy.loaded_item) - to_chat(usr, "The destructive analyzer appears to be empty.") - menu = 0 - submenu = 0 - return - for(var/T in temp_tech) - var/datum/tech/KT = files.known_tech[T] //For stat logging of high levels - if(files.IsTechHigher(T, temp_tech[T]) && KT.level >= 5) //For stat logging of high levels - feedback_add_details("high_research_level","[KT][KT.level + 1]") //+1 to show the level which we're about to get - files.UpdateTech(T, temp_tech[T]) - menu = 0 - submenu = 0 - if(linked_lathe) //Also sends salvaged materials to a linked protolathe, if any. - for(var/material in linked_destroy.loaded_item.materials) - var/can_insert = min(linked_lathe.materials.max_amount - linked_lathe.materials.total_amount, linked_destroy.loaded_item.materials[material] * (linked_destroy.decon_mod / 10), linked_destroy.loaded_item.materials[material]) - linked_lathe.materials.insert_amount(can_insert, material) - linked_destroy.loaded_item = null - else - menu = 0 - submenu = 0 - for(var/obj/I in linked_destroy.contents) - for(var/mob/M in I.contents) - M.death() - if(istype(I,/obj/item/stack/sheet))//Only deconsturcts one sheet at a time instead of the entire stack - var/obj/item/stack/sheet/S = I - if(S.amount > 1) - S.amount-- - linked_destroy.loaded_item = S - else - qdel(S) - linked_destroy.icon_state = "d_analyzer" - else - if(!(I in linked_destroy.component_parts)) - qdel(I) - linked_destroy.icon_state = "d_analyzer" - use_power(250) - SSnanoui.update_uis(src) - - else if(href_list["sync"]) //Sync the research holder with all the R&D consoles in the game that aren't sync protected. - if(!sync) - to_chat(usr, "You must connect to the network first!") - else - add_wait_message("Updating Database...", SYNC_RESEARCH_DELAY) - griefProtection() //Putting this here because I dont trust the sync process - spawn(SYNC_RESEARCH_DELAY) - clear_wait_message() - if(src) - for(var/obj/machinery/r_n_d/server/S in world) - var/server_processed = 0 - if(S.disabled) - continue - if((id in S.id_with_upload) || istype(S, /obj/machinery/r_n_d/server/centcom)) - files.push_data(S.files) - server_processed = 1 - if(((id in S.id_with_download) && !istype(S, /obj/machinery/r_n_d/server/centcom)) || S.hacked) - S.files.push_data(files) - server_processed = 1 - if(!istype(S, /obj/machinery/r_n_d/server/centcom) && server_processed) - S.produce_heat(100) - SSnanoui.update_uis(src) - - else if(href_list["togglesync"]) //Prevents the console from being synced by other consoles. Can still send data. - sync = !sync - - else if(href_list["build"]) //Causes the Protolathe to build something. - if(linked_lathe) - if(linked_lathe.busy) - to_chat(usr, "Protolathe is busy at the moment.") - return - var/coeff = linked_lathe.efficiency_coeff - var/g2g = 1 - var/datum/design/being_built = files.known_designs[href_list["build"]] - if(being_built) - var/power = 2000 - var/amount=text2num(href_list["amount"]) - if(being_built.make_reagents.len) - return 0 - amount = max(1, min(10, amount)) - for(var/M in being_built.materials) - power += round(being_built.materials[M] * amount / 5) - power = max(2000, power) - var/key = usr.key //so we don't lose the info during the spawn delay - if(!(being_built.build_type & PROTOLATHE)) - g2g = 0 - message_admins("Protolathe exploit attempted by [key_name(usr, TRUE)]!") - - if(g2g) //If input is incorrect, nothing happens - var/new_coeff = coeff * being_built.lathe_time_factor - var/time_to_construct = PROTOLATHE_CONSTRUCT_DELAY * new_coeff * amount ** 0.8 - var/enough_materials = 1 - - add_wait_message("Constructing Prototype. Please Wait...", time_to_construct) - linked_lathe.busy = 1 - flick("protolathe_n",linked_lathe) - use_power(power) - - var/list/efficient_mats = list() - for(var/MAT in being_built.materials) - efficient_mats[MAT] = being_built.materials[MAT]*coeff - - if(!linked_lathe.materials.has_materials(efficient_mats, amount)) - src.visible_message("The [src.name] beeps, \"Not enough materials to complete prototype.\"") - enough_materials = 0 - g2g = 0 - else - for(var/R in being_built.reagents_list) - if(!linked_lathe.reagents.has_reagent(R, being_built.reagents_list[R])*coeff) - src.visible_message("The [src.name] beeps, \"Not enough reagents to complete prototype.\"") - enough_materials = 0 - g2g = 0 - - if(enough_materials) - linked_lathe.materials.use_amount(efficient_mats, amount) - for(var/R in being_built.reagents_list) - linked_lathe.reagents.remove_reagent(R, being_built.reagents_list[R]*coeff) - - var/P = being_built.build_path //lets save these values before the spawn() just in case. Nobody likes runtimes. - var/O = being_built.locked - - spawn(time_to_construct) - if(g2g) //And if we only fail the material requirements, we still spend time and power - for(var/i = 0, iCircuit Imprinter is busy at the moment.
        ") - return - var/datum/design/being_built = null - being_built = files.known_designs[href_list["imprint"]] - if(being_built) - var/power = 2000 - for(var/M in being_built.materials) - power += round(being_built.materials[M] / 5) - power = max(2000, power) - if(!(being_built.build_type & IMPRINTER)) - g2g = 0 - message_admins("Circuit imprinter exploit attempted by [key_name(usr, TRUE)]!") - - if(g2g) //Again, if input is wrong, do nothing - add_wait_message("Imprinting Circuit. Please Wait...", IMPRINTER_DELAY) - linked_imprinter.busy = 1 - flick("circuit_imprinter_ani",linked_imprinter) - use_power(power) - - var/list/efficient_mats = list() - for(var/MAT in being_built.materials) - efficient_mats[MAT] = being_built.materials[MAT]/coeff - - if(!linked_imprinter.materials.has_materials(efficient_mats)) - visible_message("The [src.name] beeps, \"Not enough materials to complete prototype.\"") - enough_materials = 0 - g2g = 0 - else - for(var/R in being_built.reagents_list) - if(!linked_imprinter.reagents.has_reagent(R, being_built.reagents_list[R]/coeff)) - visible_message("The [name] beeps, \"Not enough reagents to complete prototype.\"") - enough_materials = 0 - g2g = 0 - - if(enough_materials) - linked_imprinter.materials.use_amount(efficient_mats) - for(var/R in being_built.reagents_list) - linked_imprinter.reagents.remove_reagent(R, being_built.reagents_list[R]/coeff) - - var/P = being_built.build_path //lets save these values before the spawn() just in case. Nobody likes runtimes. - spawn(IMPRINTER_DELAY) - if(g2g) - var/obj/item/new_item = new P(src) - new_item.loc = linked_imprinter.loc - linked_imprinter.busy = 0 - clear_wait_message() - SSnanoui.update_uis(src) - - else if(href_list["disposeI"] && linked_imprinter) //Causes the circuit imprinter to dispose of a single reagent (all of it) - linked_imprinter.reagents.del_reagent(href_list["disposeI"]) - - else if(href_list["disposeallI"] && linked_imprinter) //Causes the circuit imprinter to dispose of all it's reagents. - linked_imprinter.reagents.clear_reagents() - - else if(href_list["disposeP"] && linked_lathe) //Causes the protolathe to dispose of a single reagent (all of it) - linked_lathe.reagents.del_reagent(href_list["disposeP"]) - - else if(href_list["disposeallP"] && linked_lathe) //Causes the protolathe to dispose of all it's reagents. - linked_lathe.reagents.clear_reagents() - - else if(href_list["lathe_ejectsheet"] && linked_lathe) //Causes the protolathe to eject a sheet of material - var/desired_num_sheets - if(href_list["lathe_ejectsheet_amt"] == "custom") - desired_num_sheets = input("How many sheets would you like to eject from the machine?", "How much?", 1) as null|num - desired_num_sheets = max(0,desired_num_sheets) // If you input too high of a number, the mineral datum will take care of it either way - if(!desired_num_sheets) - return - desired_num_sheets = round(desired_num_sheets) // No partial-sheet goofery - else - desired_num_sheets = text2num(href_list["lathe_ejectsheet_amt"]) - linked_lathe.materials.retrieve_sheets(desired_num_sheets, href_list["lathe_ejectsheet"]) - - else if(href_list["imprinter_ejectsheet"] && linked_imprinter) //Causes the protolathe to eject a sheet of material - var/desired_num_sheets = text2num(href_list["imprinter_ejectsheet_amt"]) - if(href_list["imprinter_ejectsheet_amt"] == "custom") - desired_num_sheets = input("How many sheets would you like to eject from the machine?", "How much?", 1) as null|num - desired_num_sheets = max(0,desired_num_sheets) // for the imprinter they have something hacky, that still will guard against shenanigans. eh - if(!desired_num_sheets) - return - desired_num_sheets = round(desired_num_sheets) // No partial-sheet goofery - else - desired_num_sheets = text2num(href_list["imprinter_ejectsheet_amt"]) - linked_imprinter.materials.retrieve_sheets(desired_num_sheets, href_list["imprinter_ejectsheet"]) - - else if(href_list["find_device"]) //The R&D console looks for devices nearby to link up with. - add_wait_message("Updating Database...", SYNC_DEVICE_DELAY) - spawn(SYNC_DEVICE_DELAY) - SyncRDevices() - clear_wait_message() - SSnanoui.update_uis(src) - - else if(href_list["disconnect"]) //The R&D console disconnects with a specific device. - switch(href_list["disconnect"]) - if("destroy") - linked_destroy.linked_console = null - linked_destroy = null - if("lathe") - linked_lathe.linked_console = null - linked_lathe = null - if("imprinter") - linked_imprinter.linked_console = null - linked_imprinter = null - - else if(href_list["reset"]) //Reset the R&D console's database. - griefProtection() - var/choice = alert("Are you sure you want to reset the R&D console's database? Data lost cannot be recovered.", "R&D Console Database Reset", "Continue", "Cancel") - if(choice == "Continue") - add_wait_message("Updating Database...", RESET_RESEARCH_DELAY) - qdel(files) - files = new /datum/research(src) - spawn(RESET_RESEARCH_DELAY) - clear_wait_message() - SSnanoui.update_uis(src) - - else if(href_list["search"]) //Search for designs with name matching pattern - var/compare - - matching_designs.Cut() - - if(menu == 4) - compare = PROTOLATHE - else - compare = IMPRINTER - - for(var/v in files.known_designs) - var/datum/design/D = files.known_designs[v] - if(!(D.build_type & compare)) - continue - if(findtext(D.name,href_list["to_search"])) - matching_designs.Add(D) - submenu = 1 - - selected_category = "Search Results for '[href_list["to_search"]]'" - - SSnanoui.update_uis(src) - return - - -/obj/machinery/computer/rdconsole/attack_hand(mob/user as mob) - if(..()) - return 1 - if(!allowed(user) && !isobserver(user)) - to_chat(user, "Access denied.") - return 1 - ui_interact(user) - -/obj/machinery/computer/rdconsole/ui_interact(mob/user, ui_key="main", var/datum/nanoui/ui = null, var/force_open = 1) - user.set_machine(src) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) - if(!ui) - ui = new(user, src, ui_key, "r_n_d.tmpl", src.name, 800, 550) - ui.open() - -/obj/machinery/computer/rdconsole/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) - var/data[0] - - files.RefreshResearch() - - data["menu"] = menu - data["submenu"] = submenu - data["wait_message"] = wait_message - data["src_ref"] = UID() - - data["linked_destroy"] = linked_destroy ? 1 : 0 - data["linked_lathe"] = linked_lathe ? 1 : 0 - data["linked_imprinter"] = linked_imprinter ? 1 : 0 - data["sync"] = sync - data["admin"] = check_rights(R_ADMIN,0) - data["disk_type"] = d_disk ? 2 : (t_disk ? 1 : 0) - data["category"] = selected_category - - if(menu == 0 || menu == 1) - var/list/tech_levels = list() - data["tech_levels"] = tech_levels - for(var/v in files.known_tech) - var/datum/tech/T = files.known_tech[v] - if(T.level <= 0) - continue - var/list/this_tech_list = list() - this_tech_list["name"] = T.name - this_tech_list["level"] = T.level - this_tech_list["desc"] = T.desc - tech_levels[++tech_levels.len] = this_tech_list - - if(menu == 2) - - if(t_disk != null && t_disk.stored != null && submenu == 0) //Technology Disk Menu - var/list/disk_data = list() - data["disk_data"] = disk_data - disk_data["name"] = t_disk.stored.name - disk_data["level"] = t_disk.stored.level - disk_data["desc"] = t_disk.stored.desc - - if(t_disk != null && submenu == 1) - var/list/to_copy = list() - data["to_copy"] = to_copy - for(var/v in files.known_tech) - var/datum/tech/T = files.known_tech[v] - var/list/item = list() - to_copy[++to_copy.len] = item - if(T.level <= 0) - continue - item["name"] = T.name - item["id"] = T.id - - if(d_disk != null && d_disk.blueprint != null && submenu == 0) - var/list/disk_data = list() - data["disk_data"] = disk_data - disk_data["name"] = d_disk.blueprint.name - var/b_type = d_disk.blueprint.build_type - var/list/lathe_types = list() - disk_data["lathe_types"] = lathe_types - if(b_type) - if(b_type & IMPRINTER) lathe_types += "Circuit Imprinter" - if(b_type & PROTOLATHE) lathe_types += "Protolathe" - if(b_type & AUTOLATHE) lathe_types += "Autolathe" - if(b_type & MECHFAB) lathe_types += "Mech Fabricator" - if(b_type & PODFAB) lathe_types += "Spacepod Fabricator" - if(b_type & BIOGENERATOR) lathe_types += "Biogenerator" - if(b_type & SMELTER) lathe_types += "Smelter" - var/list/materials = list() - disk_data["materials"] = materials - for(var/M in d_disk.blueprint.materials) - var/list/material = list() - materials[++materials.len] = material - material["name"] = CallMaterialName(M) - material["amount"] = d_disk.blueprint.materials[M] - - if(d_disk != null && submenu == 1) - var/list/to_copy = list() - data["to_copy"] = to_copy - for(var/v in files.known_designs) - var/datum/design/D = files.known_designs[v] - var/list/item = list() - to_copy[++to_copy.len] = item - item["name"] = D.name - item["id"] = D.id - - if(menu == 3 && linked_destroy && linked_destroy.loaded_item) - var/list/loaded_item_list = list() - data["loaded_item"] = loaded_item_list - loaded_item_list["name"] = linked_destroy.loaded_item.name - var/list/temp_tech = linked_destroy.ConvertReqString2List(linked_destroy.loaded_item.origin_tech) - var/list/tech_list = list() - loaded_item_list["origin_tech"] = tech_list - for(var/T in temp_tech) - var/list/tech_item = list() - tech_list[++tech_list.len] = tech_item - tech_item["name"] = CallTechName(T) - tech_item["object_level"] = temp_tech[T] - for(var/v in files.known_tech) - var/datum/tech/F = files.known_tech[v] - if(F.name == CallTechName(T)) - tech_item["current_level"] = F.level - break - - if(menu == 4 && linked_lathe) - data["total_materials"] = linked_lathe.materials.total_amount - data["max_materials"] = linked_lathe.materials.max_amount - data["total_chemicals"] = linked_lathe.reagents.total_volume - data["max_chemicals"] = linked_lathe.reagents.maximum_volume - data["categories"] = linked_lathe.categories - if(submenu == 1) - var/list/designs_list = list() - data["matching_designs"] = designs_list - var/coeff = linked_lathe.efficiency_coeff - for(var/datum/design/D in matching_designs) - var/list/design_list = list() - designs_list[++designs_list.len] = design_list - var/list/materials_list = list() - design_list["materials"] = materials_list - design_list["id"] = D.id - design_list["name"] = sanitize(D.name) - var/c = 50 - for(var/M in D.materials) - var/list/material_list = list() - materials_list[++materials_list.len] = material_list - material_list["name"] = CallMaterialName(M) - material_list["amount"] = D.materials[M]*coeff - var/t = linked_lathe.check_mat(D, M) - - if(t < 1) - material_list["is_red"] = 1 - else - material_list["is_red"] = 0 - c = min(c, t) - - for(var/R in D.reagents_list) - var/list/material_list = list() - materials_list[++materials_list.len] = material_list - material_list["name"] = CallMaterialName(R) - material_list["amount"] = D.reagents_list[R]*coeff - var/t = linked_lathe.check_mat(D, R) - - if(t < 1) - material_list["is_red"] = 1 - else - material_list["is_red"] = 0 - c = min(c, t) - design_list["can_build"] = c - if(submenu == 2) - var/list/materials_list = list() - data["loaded_materials"] = materials_list - materials_list[++materials_list.len] = list("name" = "Metal", "id" = MAT_METAL, "amount" = linked_lathe.materials.amount(MAT_METAL)) - materials_list[++materials_list.len] = list("name" = "Glass", "id" = MAT_GLASS, "amount" = linked_lathe.materials.amount(MAT_GLASS)) - materials_list[++materials_list.len] = list("name" = "Gold", "id" = MAT_GOLD, "amount" = linked_lathe.materials.amount(MAT_GOLD)) - materials_list[++materials_list.len] = list("name" = "Silver", "id" = MAT_SILVER, "amount" = linked_lathe.materials.amount(MAT_SILVER)) - materials_list[++materials_list.len] = list("name" = "Solid Plasma", "id" = MAT_PLASMA, "amount" = linked_lathe.materials.amount(MAT_PLASMA)) - materials_list[++materials_list.len] = list("name" = "Uranium", "id" = MAT_URANIUM, "amount" = linked_lathe.materials.amount(MAT_URANIUM)) - materials_list[++materials_list.len] = list("name" = "Diamond", "id" = MAT_DIAMOND, "amount" = linked_lathe.materials.amount(MAT_DIAMOND)) - materials_list[++materials_list.len] = list("name" = "Bananium", "id" = MAT_BANANIUM, "amount" = linked_lathe.materials.amount(MAT_BANANIUM)) - materials_list[++materials_list.len] = list("name" = "Tranquillite", "id" = MAT_TRANQUILLITE, "amount" = linked_lathe.materials.amount(MAT_TRANQUILLITE)) - materials_list[++materials_list.len] = list("name" = "Titanium", "id" = MAT_TITANIUM, "amount" = linked_lathe.materials.amount(MAT_TITANIUM)) - materials_list[++materials_list.len] = list("name" = "Plastic", "id" = MAT_PLASTIC, "amount" = linked_lathe.materials.amount(MAT_PLASTIC)) - materials_list[++materials_list.len] = list("name" = "Bluespace Mesh", "id" = MAT_BLUESPACE, "amount" = linked_lathe.materials.amount(MAT_BLUESPACE)) - if(submenu == 3) - var/list/loaded_chemicals = list() - data["loaded_chemicals"] = loaded_chemicals - for(var/datum/reagent/R in linked_lathe.reagents.reagent_list) - var/list/loaded_chemical = list() - loaded_chemicals[++loaded_chemicals.len] = loaded_chemical - loaded_chemical["name"] = R.name - loaded_chemical["volume"] = R.volume - loaded_chemical["id"] = R.id - - if(menu == 5 && linked_imprinter) - data["total_materials"] = linked_imprinter.materials.total_amount - data["max_materials"] = linked_imprinter.materials.max_amount - data["total_chemicals"] = linked_imprinter.reagents.total_volume - data["max_chemicals"] = linked_imprinter.reagents.maximum_volume - data["categories"] = linked_imprinter.categories - if(submenu == 1) - var/list/designs_list = list() - data["matching_designs"] = designs_list - var/coeff = linked_imprinter.efficiency_coeff - for(var/datum/design/D in matching_designs) - var/list/design_list = list() - designs_list[++designs_list.len] = design_list - var/list/materials_list = list() - design_list["materials"] = materials_list - design_list["id"] = D.id - design_list["name"] = sanitize(D.name) - var/check_materials = 1 - for(var/M in D.materials) - var/list/material_list = list() - materials_list[++materials_list.len] = material_list - material_list["name"] = CallMaterialName(M) - material_list["amount"] = D.materials[M] / coeff - if(!linked_imprinter.check_mat(D, M)) - check_materials = 0 - material_list["is_red"] = 1 - else - material_list["is_red"] = 0 - - for(var/R in D.reagents_list) - var/list/material_list = list() - materials_list[++materials_list.len] = material_list - material_list["name"] = CallMaterialName(R) - material_list["amount"] = D.reagents_list[R]*coeff - if(!linked_imprinter.check_mat(D, R)) - check_materials = 0 - material_list["is_red"] = 1 - else - material_list["is_red"] = 0 - - design_list["can_build"] = check_materials - if(submenu == 2) - var/list/materials_list = list() - data["loaded_materials"] = materials_list - materials_list[++materials_list.len] = list("name" = "Metal", "id" = MAT_METAL, "amount" = linked_imprinter.materials.amount(MAT_METAL)) - materials_list[++materials_list.len] = list("name" = "Glass", "id" = MAT_GLASS, "amount" = linked_imprinter.materials.amount(MAT_GLASS)) - materials_list[++materials_list.len] = list("name" = "Gold", "id" = MAT_GOLD, "amount" = linked_imprinter.materials.amount(MAT_GOLD)) - materials_list[++materials_list.len] = list("name" = "Silver", "id" = MAT_SILVER, "amount" = linked_imprinter.materials.amount(MAT_SILVER)) - materials_list[++materials_list.len] = list("name" = "Solid Plasma", "id" = MAT_PLASMA, "amount" = linked_imprinter.materials.amount(MAT_PLASMA)) - materials_list[++materials_list.len] = list("name" = "Uranium", "id" = MAT_URANIUM, "amount" = linked_imprinter.materials.amount(MAT_URANIUM)) - materials_list[++materials_list.len] = list("name" = "Diamond", "id" = MAT_DIAMOND, "amount" = linked_imprinter.materials.amount(MAT_DIAMOND)) - materials_list[++materials_list.len] = list("name" = "Bananium", "id" = MAT_BANANIUM, "amount" = linked_imprinter.materials.amount(MAT_BANANIUM)) - materials_list[++materials_list.len] = list("name" = "Tranquillite", "id" = MAT_TRANQUILLITE, "amount" = linked_imprinter.materials.amount(MAT_TRANQUILLITE)) - materials_list[++materials_list.len] = list("name" = "Titanium", "id" = MAT_TITANIUM, "amount" = linked_imprinter.materials.amount(MAT_TITANIUM)) - materials_list[++materials_list.len] = list("name" = "Plastic", "id" = MAT_PLASTIC, "amount" = linked_imprinter.materials.amount(MAT_PLASTIC)) - materials_list[++materials_list.len] = list("name" = "Bluespace Mesh", "id" = MAT_BLUESPACE, "amount" = linked_imprinter.materials.amount(MAT_BLUESPACE)) - if(submenu == 3) - var/list/loaded_chemicals = list() - data["loaded_chemicals"] = loaded_chemicals - for(var/datum/reagent/R in linked_imprinter.reagents.reagent_list) - var/list/loaded_chemical = list() - loaded_chemicals[++loaded_chemicals.len] = loaded_chemical - loaded_chemical["name"] = R.name - loaded_chemical["volume"] = R.volume - loaded_chemical["id"] = R.id - - return data - -//helper proc that guarantees the wait message will not freeze the UI -/obj/machinery/computer/rdconsole/proc/add_wait_message(message, delay) - wait_message = message - wait_message_timer = addtimer(CALLBACK(src, .proc/clear_wait_message), delay, TIMER_UNIQUE | TIMER_STOPPABLE) - -// This is here to guarantee that we never lock the console, so long as the timer -// process is running -// So long as the spawns never runtime, though, the timer should be stopped -// before it gets the chance to fire -// Since the timer process can have significant delays, you should call this -// in the operations that take time, once they complete -/obj/machinery/computer/rdconsole/proc/clear_wait_message() - wait_message = "" - if(wait_message_timer) - // This could be expensive, and will still be called - // if the timer calls this function - deltimer(wait_message_timer) - wait_message_timer = 0 - SSnanoui.update_uis(src) - - -/obj/machinery/computer/rdconsole/core - name = "core R&D console" - desc = "A console used to interface with R&D tools." - id = 1 - -/obj/machinery/computer/rdconsole/robotics - name = "robotics R&D console" - desc = "A console used to interface with R&D tools." - id = 2 - req_access = list(access_robotics) - circuit = /obj/item/circuitboard/rdconsole/robotics - -/obj/machinery/computer/rdconsole/experiment - name = "\improper E.X.P.E.R.I-MENTOR R&D console" - desc = "A console used to interface with R&D tools." - id = 3 - circuit = /obj/item/circuitboard/rdconsole/experiment - -/obj/machinery/computer/rdconsole/mechanics - name = "mechanics R&D console" - desc = "A console used to interface with R&D tools." - id = 4 - req_access = list(access_mechanic) - circuit = /obj/item/circuitboard/rdconsole/mechanics - -/obj/machinery/computer/rdconsole/public - name = "public R&D console" - desc = "A console used to interface with R&D tools." - id = 5 - req_access = list() - circuit = /obj/item/circuitboard/rdconsole/public - -#undef TECH_UPDATE_DELAY -#undef DESIGN_UPDATE_DELAY -#undef PROTOLATHE_CONSTRUCT_DELAY -#undef SYNC_RESEARCH_DELAY -#undef DECONSTRUCT_DELAY -#undef SYNC_DEVICE_DELAY -#undef RESET_RESEARCH_DELAY -#undef IMPRINTER_DELAY +/* +Research and Development (R&D) Console + +This is the main work horse of the R&D system. It contains the menus/controls for the Destructive Analyzer, Protolathe, and Circuit +imprinter. It also contains the /datum/research holder with all the known/possible technology paths and device designs. + +Basic use: When it first is created, it will attempt to link up to related devices within 3 squares. It'll only link up if they +aren't already linked to another console. Any consoles it cannot link up with (either because all of a certain type are already +linked or there aren't any in range), you'll just not have access to that menu. In the settings menu, there are menu options that +allow a player to attempt to re-sync with nearby consoles. You can also force it to disconnect from a specific console. + +The imprinting and construction menus do NOT require toxins access to access but all the other menus do. However, if you leave it +on a menu, nothing is to stop the person from using the options on that menu (although they won't be able to change to a different +one). You can also lock the console on the settings menu if you're feeling paranoid and you don't want anyone messing with it who +doesn't have toxins access. + +When a R&D console is destroyed or even partially disassembled, you lose all research data on it. However, there are two ways around +this dire fate: +- The easiest way is to go to the settings menu and select "Sync Database with Network." That causes it to upload (but not download) +it's data to every other device in the game. Each console has a "disconnect from network" option that'll will cause data base sync +operations to skip that console. This is useful if you want to make a "public" R&D console or, for example, give the engineers +a circuit imprinter with certain designs on it and don't want it accidentally updating. The downside of this method is that you have +to have physical access to the other console to send data back. Note: An R&D console is on Centcom so if a random griffan happens to +cause a ton of data to be lost, an admin can go send it back. +- The second method is with Technology Disks and Design Disks. Each of these disks can hold a single technology or design datum in +it's entirety. You can then take the disk to any R&D console and upload it's data to it. This method is a lot more secure (since it +won't update every console in existence) but it's more of a hassle to do. Also, the disks can be stolen. + + +*/ + +// Who likes #defines? +// I don't! +// but I gotta add 'em anyways because we have a bias against /const statements for some reason +#define TECH_UPDATE_DELAY 50 +#define DESIGN_UPDATE_DELAY 50 +#define PROTOLATHE_CONSTRUCT_DELAY 32 +#define SYNC_RESEARCH_DELAY 30 +#define DECONSTRUCT_DELAY 24 +#define SYNC_DEVICE_DELAY 20 +#define RESET_RESEARCH_DELAY 20 +#define IMPRINTER_DELAY 16 + +/obj/machinery/computer/rdconsole + name = "\improper R&D console" + icon_screen = "rdcomp" + icon_keyboard = "rd_key" + light_color = LIGHT_COLOR_FADEDPURPLE + circuit = /obj/item/circuitboard/rdconsole + var/datum/research/files //Stores all the collected research data. + var/obj/item/disk/tech_disk/t_disk = null //Stores the technology disk. + var/obj/item/disk/design_disk/d_disk = null //Stores the design disk. + + var/obj/machinery/r_n_d/destructive_analyzer/linked_destroy = null //Linked Destructive Analyzer + var/obj/machinery/r_n_d/protolathe/linked_lathe = null //Linked Protolathe + var/obj/machinery/r_n_d/circuit_imprinter/linked_imprinter = null //Linked Circuit Imprinter + + var/screen = 1.0 //Which screen is currently showing. + + var/menu = 0 // Current menu. + var/submenu = 0 + var/wait_message = 0 + var/wait_message_timer = 0 + + var/id = 0 //ID of the computer (for server restrictions). + var/sync = 1 //If sync = 0, it doesn't show up on Server Control Console + + req_access = list(ACCESS_TOX) //Data and setting manipulation requires scientist access. + + var/selected_category + var/list/datum/design/matching_designs = list() //for the search function + +/proc/CallTechName(ID) //A simple helper proc to find the name of a tech with a given ID. + for(var/T in subtypesof(/datum/tech)) + var/datum/tech/tt = T + if(initial(tt.id) == ID) + return initial(tt.name) + +/proc/CallMaterialName(ID) + if(copytext(ID, 1, 2) == "$") + var/return_name = copytext(ID, 2) + switch(return_name) + if("metal") + return_name = "Metal" + if("glass") + return_name = "Glass" + if("gold") + return_name = "Gold" + if("silver") + return_name = "Silver" + if("plasma") + return_name = "Solid Plasma" + if("uranium") + return_name = "Uranium" + if("diamond") + return_name = "Diamond" + if("clown") + return_name = "Bananium" + if("mime") + return_name = "Tranquillite" + if("titanium") + return_name = "Titanium" + if("bluespace") + return_name = "Bluespace Mesh" + if("plastic") + return_name = "Plastic" + return return_name + else + for(var/R in subtypesof(/datum/reagent)) + var/datum/reagent/rt = R + if(initial(rt.id) == ID) + return initial(rt.name) + +/obj/machinery/computer/rdconsole/proc/SyncRDevices() //Makes sure it is properly sync'ed up with the devices attached to it (if any). + for(var/obj/machinery/r_n_d/D in range(3,src)) + if(!isnull(D.linked_console) || D.disabled || D.panel_open) + continue + if(istype(D, /obj/machinery/r_n_d/destructive_analyzer)) + if(linked_destroy == null) + linked_destroy = D + D.linked_console = src + else if(istype(D, /obj/machinery/r_n_d/protolathe)) + if(linked_lathe == null) + linked_lathe = D + D.linked_console = src + else if(istype(D, /obj/machinery/r_n_d/circuit_imprinter)) + if(linked_imprinter == null) + linked_imprinter = D + D.linked_console = src + return + +//Have it automatically push research to the centcom server so wild griffins can't fuck up R&D's work --NEO +/obj/machinery/computer/rdconsole/proc/griefProtection() + for(var/obj/machinery/r_n_d/server/centcom/C in world) + files.push_data(C.files) + +/obj/machinery/computer/rdconsole/proc/Maximize() + for(var/datum/tech/T in files.possible_tech) + files.known_tech[T.id] = T + for(var/v in files.known_tech) + var/datum/tech/KT = files.known_tech[v] + if(KT.level < KT.max_level) + KT.level=KT.max_level + files.RefreshResearch() + +/obj/machinery/computer/rdconsole/New() + ..() + files = new /datum/research(src) //Setup the research data holder. + matching_designs = list() + if(!id) + for(var/obj/machinery/r_n_d/server/centcom/S in world) + S.initialize_serv() + break + +/obj/machinery/computer/rdconsole/Initialize() + ..() + SyncRDevices() + +/obj/machinery/computer/rdconsole/Destroy() + if(wait_message_timer) + deltimer(wait_message_timer) + wait_message_timer = 0 + return ..() + +/* Instead of calling this every tick, it is only being called when needed +/obj/machinery/computer/rdconsole/process() + griefProtection() +*/ + +/obj/machinery/computer/rdconsole/attackby(var/obj/item/D as obj, var/mob/user as mob, params) + + //Loading a disk into it. + if(istype(D, /obj/item/disk)) + if(t_disk || d_disk) + to_chat(user, "A disk is already loaded into the machine.") + return + + if(istype(D, /obj/item/disk/tech_disk)) t_disk = D + else if(istype(D, /obj/item/disk/design_disk)) d_disk = D + else + to_chat(user, "Machine cannot accept disks in that format.") + return + if(!user.drop_item()) + return + D.loc = src + to_chat(user, "You add the disk to the machine!") + else if(!(linked_destroy && linked_destroy.busy) && !(linked_lathe && linked_lathe.busy) && !(linked_imprinter && linked_imprinter.busy)) + ..() + SSnanoui.update_uis(src) + return + +/obj/machinery/computer/rdconsole/emag_act(user as mob) + if(!emagged) + playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1) + req_access = list() + emagged = 1 + to_chat(user, "You disable the security protocols") + +/obj/machinery/computer/rdconsole/Topic(href, href_list) + if(..()) + return 1 + + if(!allowed(usr) && !isobserver(usr)) + return 1 + + add_fingerprint(usr) + + usr.set_machine(src) + if(href_list["menu"]) //Switches menu screens. Converts a sent text string into a number. Saves a LOT of code. + var/temp_screen = text2num(href_list["menu"]) + menu = temp_screen + if(href_list["submenu"]) //Switches menu screens. Converts a sent text string into a number. Saves a LOT of code. + var/temp_screen = text2num(href_list["submenu"]) + submenu = temp_screen + + if(href_list["category"]) + var/compare + + matching_designs.Cut() + + if(menu == 4) + compare = PROTOLATHE + else + compare = IMPRINTER + + for(var/v in files.known_designs) + var/datum/design/D = files.known_designs[v] + if(!(D.build_type & compare)) + continue + if(href_list["category"] in D.category) + matching_designs.Add(D) + submenu = 1 + + selected_category = "Viewing Category [href_list["category"]]" + + else if(href_list["updt_tech"]) //Update the research holder with information from the technology disk. + add_wait_message("Updating Database...", TECH_UPDATE_DELAY) + spawn(TECH_UPDATE_DELAY) + clear_wait_message() + files.AddTech2Known(t_disk.stored) + SSnanoui.update_uis(src) + griefProtection() //Update centcom too + + else if(href_list["clear_tech"]) //Erase data on the technology disk. + if(t_disk) + t_disk.wipe_tech() + + else if(href_list["eject_tech"]) //Eject the technology disk. + if(t_disk) + t_disk.loc = src.loc + t_disk = null + menu = 0 + submenu = 0 + + else if(href_list["copy_tech"]) //Copy some technology data from the research holder to the disk. + // Somehow this href makes me very nervous + t_disk.stored = files.known_tech[href_list["copy_tech_ID"]] + menu = 2 + submenu = 0 + + else if(href_list["updt_design"]) //Updates the research holder with design data from the design disk. + add_wait_message("Updating Database...", DESIGN_UPDATE_DELAY) + spawn(DESIGN_UPDATE_DELAY) + clear_wait_message() + files.AddDesign2Known(d_disk.blueprint) + SSnanoui.update_uis(src) + griefProtection() //Update centcom too + + else if(href_list["clear_design"]) //Erases data on the design disk. + if(d_disk) + d_disk.wipe_blueprint() + + else if(href_list["eject_design"]) //Eject the design disk. + if(d_disk) + d_disk.loc = src.loc + d_disk = null + menu = 0 + submenu = 0 + + else if(href_list["copy_design"]) //Copy design data from the research holder to the design disk. + // This href ALSO makes me very nervous + var/datum/design/D = files.known_designs[href_list["copy_design_ID"]] + if(D) + // eeeeeep design datums are global be careful! + var/autolathe_friendly = 1 + for(var/x in D.materials) + if( !(x in list(MAT_METAL, MAT_GLASS))) + autolathe_friendly = 0 + D.category -= "Imported" + if(D.locked) + autolathe_friendly = 0 + D.category -= "Imported" + if(D.build_type & (AUTOLATHE|PROTOLATHE|CRAFTLATHE)) // Specifically excludes circuit imprinter and mechfab + D.build_type = autolathe_friendly ? (D.build_type | AUTOLATHE) : D.build_type + D.category |= "Imported" + d_disk.blueprint = D + menu = 2 + submenu = 0 + + else if(href_list["eject_item"]) //Eject the item inside the destructive analyzer. + if(linked_destroy) + if(linked_destroy.busy) + to_chat(usr, " The destructive analyzer is busy at the moment.") + + else if(linked_destroy.loaded_item) + linked_destroy.loaded_item.loc = linked_destroy.loc + linked_destroy.loaded_item = null + linked_destroy.icon_state = "d_analyzer" + menu = 3 + + else if(href_list["maxresearch"]) //Eject the item inside the destructive analyzer. + if(!check_rights(R_ADMIN)) + return + if(alert("Are you sure you want to maximize research levels?","Confirmation","Yes","No")=="No") + return + log_admin("[key_name(usr)] has maximized the research levels.") + message_admins("[key_name_admin(usr)] has maximized the research levels.") + Maximize() + SSnanoui.update_uis(src) + griefProtection() //Update centcomm too + + else if(href_list["deconstruct"]) //Deconstruct the item in the destructive analyzer and update the research holder. + if(linked_destroy) + if(linked_destroy.busy) + to_chat(usr, "The destructive analyzer is busy at the moment.") + return + var/list/temp_tech = linked_destroy.ConvertReqString2List(linked_destroy.loaded_item.origin_tech) + var/cancontinue = FALSE + for(var/T in temp_tech) + if(files.IsTechHigher(T, temp_tech[T])) + cancontinue = TRUE + break + if(!cancontinue) + var/choice = input("This item does not raise tech levels. Proceed destroying loaded item anyway?") in list("Proceed", "Cancel") + if(choice == "Cancel" || !linked_destroy) + return + linked_destroy.busy = 1 + add_wait_message("Processing and Updating Database...", DECONSTRUCT_DELAY) + SSnanoui.update_uis(src) + flick("d_analyzer_process", linked_destroy) + spawn(DECONSTRUCT_DELAY) + clear_wait_message() + if(linked_destroy) + linked_destroy.busy = 0 + if(!linked_destroy.hacked) + if(!linked_destroy.loaded_item) + to_chat(usr, "The destructive analyzer appears to be empty.") + menu = 0 + submenu = 0 + return + for(var/T in temp_tech) + var/datum/tech/KT = files.known_tech[T] //For stat logging of high levels + if(files.IsTechHigher(T, temp_tech[T]) && KT.level >= 5) //For stat logging of high levels + feedback_add_details("high_research_level","[KT][KT.level + 1]") //+1 to show the level which we're about to get + files.UpdateTech(T, temp_tech[T]) + menu = 0 + submenu = 0 + if(linked_lathe) //Also sends salvaged materials to a linked protolathe, if any. + for(var/material in linked_destroy.loaded_item.materials) + var/can_insert = min(linked_lathe.materials.max_amount - linked_lathe.materials.total_amount, linked_destroy.loaded_item.materials[material] * (linked_destroy.decon_mod / 10), linked_destroy.loaded_item.materials[material]) + linked_lathe.materials.insert_amount(can_insert, material) + linked_destroy.loaded_item = null + else + menu = 0 + submenu = 0 + for(var/obj/I in linked_destroy.contents) + for(var/mob/M in I.contents) + M.death() + if(istype(I,/obj/item/stack/sheet))//Only deconsturcts one sheet at a time instead of the entire stack + var/obj/item/stack/sheet/S = I + if(S.amount > 1) + S.amount-- + linked_destroy.loaded_item = S + else + qdel(S) + linked_destroy.icon_state = "d_analyzer" + else + if(!(I in linked_destroy.component_parts)) + qdel(I) + linked_destroy.icon_state = "d_analyzer" + use_power(250) + SSnanoui.update_uis(src) + + else if(href_list["sync"]) //Sync the research holder with all the R&D consoles in the game that aren't sync protected. + if(!sync) + to_chat(usr, "You must connect to the network first!") + else + add_wait_message("Updating Database...", SYNC_RESEARCH_DELAY) + griefProtection() //Putting this here because I dont trust the sync process + spawn(SYNC_RESEARCH_DELAY) + clear_wait_message() + if(src) + for(var/obj/machinery/r_n_d/server/S in world) + var/server_processed = 0 + if(S.disabled) + continue + if((id in S.id_with_upload) || istype(S, /obj/machinery/r_n_d/server/centcom)) + files.push_data(S.files) + server_processed = 1 + if(((id in S.id_with_download) && !istype(S, /obj/machinery/r_n_d/server/centcom)) || S.hacked) + S.files.push_data(files) + server_processed = 1 + if(!istype(S, /obj/machinery/r_n_d/server/centcom) && server_processed) + S.produce_heat(100) + SSnanoui.update_uis(src) + + else if(href_list["togglesync"]) //Prevents the console from being synced by other consoles. Can still send data. + sync = !sync + + else if(href_list["build"]) //Causes the Protolathe to build something. + if(linked_lathe) + if(linked_lathe.busy) + to_chat(usr, "Protolathe is busy at the moment.") + return + var/coeff = linked_lathe.efficiency_coeff + var/g2g = 1 + var/datum/design/being_built = files.known_designs[href_list["build"]] + if(being_built) + var/power = 2000 + var/amount=text2num(href_list["amount"]) + if(being_built.make_reagents.len) + return 0 + amount = max(1, min(10, amount)) + for(var/M in being_built.materials) + power += round(being_built.materials[M] * amount / 5) + power = max(2000, power) + var/key = usr.key //so we don't lose the info during the spawn delay + if(!(being_built.build_type & PROTOLATHE)) + g2g = 0 + message_admins("Protolathe exploit attempted by [key_name(usr, TRUE)]!") + + if(g2g) //If input is incorrect, nothing happens + var/new_coeff = coeff * being_built.lathe_time_factor + var/time_to_construct = PROTOLATHE_CONSTRUCT_DELAY * new_coeff * amount ** 0.8 + var/enough_materials = 1 + + add_wait_message("Constructing Prototype. Please Wait...", time_to_construct) + linked_lathe.busy = 1 + flick("protolathe_n",linked_lathe) + use_power(power) + + var/list/efficient_mats = list() + for(var/MAT in being_built.materials) + efficient_mats[MAT] = being_built.materials[MAT]*coeff + + if(!linked_lathe.materials.has_materials(efficient_mats, amount)) + src.visible_message("The [src.name] beeps, \"Not enough materials to complete prototype.\"") + enough_materials = 0 + g2g = 0 + else + for(var/R in being_built.reagents_list) + if(!linked_lathe.reagents.has_reagent(R, being_built.reagents_list[R])*coeff) + src.visible_message("The [src.name] beeps, \"Not enough reagents to complete prototype.\"") + enough_materials = 0 + g2g = 0 + + if(enough_materials) + linked_lathe.materials.use_amount(efficient_mats, amount) + for(var/R in being_built.reagents_list) + linked_lathe.reagents.remove_reagent(R, being_built.reagents_list[R]*coeff) + + var/P = being_built.build_path //lets save these values before the spawn() just in case. Nobody likes runtimes. + var/O = being_built.locked + + spawn(time_to_construct) + if(g2g) //And if we only fail the material requirements, we still spend time and power + for(var/i = 0, iCircuit Imprinter is busy at the moment.
        ") + return + var/datum/design/being_built = null + being_built = files.known_designs[href_list["imprint"]] + if(being_built) + var/power = 2000 + for(var/M in being_built.materials) + power += round(being_built.materials[M] / 5) + power = max(2000, power) + if(!(being_built.build_type & IMPRINTER)) + g2g = 0 + message_admins("Circuit imprinter exploit attempted by [key_name(usr, TRUE)]!") + + if(g2g) //Again, if input is wrong, do nothing + add_wait_message("Imprinting Circuit. Please Wait...", IMPRINTER_DELAY) + linked_imprinter.busy = 1 + flick("circuit_imprinter_ani",linked_imprinter) + use_power(power) + + var/list/efficient_mats = list() + for(var/MAT in being_built.materials) + efficient_mats[MAT] = being_built.materials[MAT]/coeff + + if(!linked_imprinter.materials.has_materials(efficient_mats)) + visible_message("The [src.name] beeps, \"Not enough materials to complete prototype.\"") + enough_materials = 0 + g2g = 0 + else + for(var/R in being_built.reagents_list) + if(!linked_imprinter.reagents.has_reagent(R, being_built.reagents_list[R]/coeff)) + visible_message("The [name] beeps, \"Not enough reagents to complete prototype.\"") + enough_materials = 0 + g2g = 0 + + if(enough_materials) + linked_imprinter.materials.use_amount(efficient_mats) + for(var/R in being_built.reagents_list) + linked_imprinter.reagents.remove_reagent(R, being_built.reagents_list[R]/coeff) + + var/P = being_built.build_path //lets save these values before the spawn() just in case. Nobody likes runtimes. + spawn(IMPRINTER_DELAY) + if(g2g) + var/obj/item/new_item = new P(src) + new_item.loc = linked_imprinter.loc + linked_imprinter.busy = 0 + clear_wait_message() + SSnanoui.update_uis(src) + + else if(href_list["disposeI"] && linked_imprinter) //Causes the circuit imprinter to dispose of a single reagent (all of it) + linked_imprinter.reagents.del_reagent(href_list["disposeI"]) + + else if(href_list["disposeallI"] && linked_imprinter) //Causes the circuit imprinter to dispose of all it's reagents. + linked_imprinter.reagents.clear_reagents() + + else if(href_list["disposeP"] && linked_lathe) //Causes the protolathe to dispose of a single reagent (all of it) + linked_lathe.reagents.del_reagent(href_list["disposeP"]) + + else if(href_list["disposeallP"] && linked_lathe) //Causes the protolathe to dispose of all it's reagents. + linked_lathe.reagents.clear_reagents() + + else if(href_list["lathe_ejectsheet"] && linked_lathe) //Causes the protolathe to eject a sheet of material + var/desired_num_sheets + if(href_list["lathe_ejectsheet_amt"] == "custom") + desired_num_sheets = input("How many sheets would you like to eject from the machine?", "How much?", 1) as null|num + desired_num_sheets = max(0,desired_num_sheets) // If you input too high of a number, the mineral datum will take care of it either way + if(!desired_num_sheets) + return + desired_num_sheets = round(desired_num_sheets) // No partial-sheet goofery + else + desired_num_sheets = text2num(href_list["lathe_ejectsheet_amt"]) + linked_lathe.materials.retrieve_sheets(desired_num_sheets, href_list["lathe_ejectsheet"]) + + else if(href_list["imprinter_ejectsheet"] && linked_imprinter) //Causes the protolathe to eject a sheet of material + var/desired_num_sheets = text2num(href_list["imprinter_ejectsheet_amt"]) + if(href_list["imprinter_ejectsheet_amt"] == "custom") + desired_num_sheets = input("How many sheets would you like to eject from the machine?", "How much?", 1) as null|num + desired_num_sheets = max(0,desired_num_sheets) // for the imprinter they have something hacky, that still will guard against shenanigans. eh + if(!desired_num_sheets) + return + desired_num_sheets = round(desired_num_sheets) // No partial-sheet goofery + else + desired_num_sheets = text2num(href_list["imprinter_ejectsheet_amt"]) + linked_imprinter.materials.retrieve_sheets(desired_num_sheets, href_list["imprinter_ejectsheet"]) + + else if(href_list["find_device"]) //The R&D console looks for devices nearby to link up with. + add_wait_message("Updating Database...", SYNC_DEVICE_DELAY) + spawn(SYNC_DEVICE_DELAY) + SyncRDevices() + clear_wait_message() + SSnanoui.update_uis(src) + + else if(href_list["disconnect"]) //The R&D console disconnects with a specific device. + switch(href_list["disconnect"]) + if("destroy") + linked_destroy.linked_console = null + linked_destroy = null + if("lathe") + linked_lathe.linked_console = null + linked_lathe = null + if("imprinter") + linked_imprinter.linked_console = null + linked_imprinter = null + + else if(href_list["reset"]) //Reset the R&D console's database. + griefProtection() + var/choice = alert("Are you sure you want to reset the R&D console's database? Data lost cannot be recovered.", "R&D Console Database Reset", "Continue", "Cancel") + if(choice == "Continue") + add_wait_message("Updating Database...", RESET_RESEARCH_DELAY) + qdel(files) + files = new /datum/research(src) + spawn(RESET_RESEARCH_DELAY) + clear_wait_message() + SSnanoui.update_uis(src) + + else if(href_list["search"]) //Search for designs with name matching pattern + var/compare + + matching_designs.Cut() + + if(menu == 4) + compare = PROTOLATHE + else + compare = IMPRINTER + + for(var/v in files.known_designs) + var/datum/design/D = files.known_designs[v] + if(!(D.build_type & compare)) + continue + if(findtext(D.name,href_list["to_search"])) + matching_designs.Add(D) + submenu = 1 + + selected_category = "Search Results for '[href_list["to_search"]]'" + + SSnanoui.update_uis(src) + return + + +/obj/machinery/computer/rdconsole/attack_hand(mob/user as mob) + if(..()) + return 1 + if(!allowed(user) && !isobserver(user)) + to_chat(user, "Access denied.") + return 1 + ui_interact(user) + +/obj/machinery/computer/rdconsole/ui_interact(mob/user, ui_key="main", var/datum/nanoui/ui = null, var/force_open = 1) + user.set_machine(src) + ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open) + if(!ui) + ui = new(user, src, ui_key, "r_n_d.tmpl", src.name, 800, 550) + ui.open() + +/obj/machinery/computer/rdconsole/ui_data(mob/user, ui_key = "main", datum/topic_state/state = default_state) + var/data[0] + + files.RefreshResearch() + + data["menu"] = menu + data["submenu"] = submenu + data["wait_message"] = wait_message + data["src_ref"] = UID() + + data["linked_destroy"] = linked_destroy ? 1 : 0 + data["linked_lathe"] = linked_lathe ? 1 : 0 + data["linked_imprinter"] = linked_imprinter ? 1 : 0 + data["sync"] = sync + data["admin"] = check_rights(R_ADMIN,0) + data["disk_type"] = d_disk ? 2 : (t_disk ? 1 : 0) + data["category"] = selected_category + + if(menu == 0 || menu == 1) + var/list/tech_levels = list() + data["tech_levels"] = tech_levels + for(var/v in files.known_tech) + var/datum/tech/T = files.known_tech[v] + if(T.level <= 0) + continue + var/list/this_tech_list = list() + this_tech_list["name"] = T.name + this_tech_list["level"] = T.level + this_tech_list["desc"] = T.desc + tech_levels[++tech_levels.len] = this_tech_list + + if(menu == 2) + + if(t_disk != null && t_disk.stored != null && submenu == 0) //Technology Disk Menu + var/list/disk_data = list() + data["disk_data"] = disk_data + disk_data["name"] = t_disk.stored.name + disk_data["level"] = t_disk.stored.level + disk_data["desc"] = t_disk.stored.desc + + if(t_disk != null && submenu == 1) + var/list/to_copy = list() + data["to_copy"] = to_copy + for(var/v in files.known_tech) + var/datum/tech/T = files.known_tech[v] + var/list/item = list() + to_copy[++to_copy.len] = item + if(T.level <= 0) + continue + item["name"] = T.name + item["id"] = T.id + + if(d_disk != null && d_disk.blueprint != null && submenu == 0) + var/list/disk_data = list() + data["disk_data"] = disk_data + disk_data["name"] = d_disk.blueprint.name + var/b_type = d_disk.blueprint.build_type + var/list/lathe_types = list() + disk_data["lathe_types"] = lathe_types + if(b_type) + if(b_type & IMPRINTER) lathe_types += "Circuit Imprinter" + if(b_type & PROTOLATHE) lathe_types += "Protolathe" + if(b_type & AUTOLATHE) lathe_types += "Autolathe" + if(b_type & MECHFAB) lathe_types += "Mech Fabricator" + if(b_type & PODFAB) lathe_types += "Spacepod Fabricator" + if(b_type & BIOGENERATOR) lathe_types += "Biogenerator" + if(b_type & SMELTER) lathe_types += "Smelter" + var/list/materials = list() + disk_data["materials"] = materials + for(var/M in d_disk.blueprint.materials) + var/list/material = list() + materials[++materials.len] = material + material["name"] = CallMaterialName(M) + material["amount"] = d_disk.blueprint.materials[M] + + if(d_disk != null && submenu == 1) + var/list/to_copy = list() + data["to_copy"] = to_copy + for(var/v in files.known_designs) + var/datum/design/D = files.known_designs[v] + var/list/item = list() + to_copy[++to_copy.len] = item + item["name"] = D.name + item["id"] = D.id + + if(menu == 3 && linked_destroy && linked_destroy.loaded_item) + var/list/loaded_item_list = list() + data["loaded_item"] = loaded_item_list + loaded_item_list["name"] = linked_destroy.loaded_item.name + var/list/temp_tech = linked_destroy.ConvertReqString2List(linked_destroy.loaded_item.origin_tech) + var/list/tech_list = list() + loaded_item_list["origin_tech"] = tech_list + for(var/T in temp_tech) + var/list/tech_item = list() + tech_list[++tech_list.len] = tech_item + tech_item["name"] = CallTechName(T) + tech_item["object_level"] = temp_tech[T] + for(var/v in files.known_tech) + var/datum/tech/F = files.known_tech[v] + if(F.name == CallTechName(T)) + tech_item["current_level"] = F.level + break + + if(menu == 4 && linked_lathe) + data["total_materials"] = linked_lathe.materials.total_amount + data["max_materials"] = linked_lathe.materials.max_amount + data["total_chemicals"] = linked_lathe.reagents.total_volume + data["max_chemicals"] = linked_lathe.reagents.maximum_volume + data["categories"] = linked_lathe.categories + if(submenu == 1) + var/list/designs_list = list() + data["matching_designs"] = designs_list + var/coeff = linked_lathe.efficiency_coeff + for(var/datum/design/D in matching_designs) + var/list/design_list = list() + designs_list[++designs_list.len] = design_list + var/list/materials_list = list() + design_list["materials"] = materials_list + design_list["id"] = D.id + design_list["name"] = sanitize(D.name) + var/c = 50 + for(var/M in D.materials) + var/list/material_list = list() + materials_list[++materials_list.len] = material_list + material_list["name"] = CallMaterialName(M) + material_list["amount"] = D.materials[M]*coeff + var/t = linked_lathe.check_mat(D, M) + + if(t < 1) + material_list["is_red"] = 1 + else + material_list["is_red"] = 0 + c = min(c, t) + + for(var/R in D.reagents_list) + var/list/material_list = list() + materials_list[++materials_list.len] = material_list + material_list["name"] = CallMaterialName(R) + material_list["amount"] = D.reagents_list[R]*coeff + var/t = linked_lathe.check_mat(D, R) + + if(t < 1) + material_list["is_red"] = 1 + else + material_list["is_red"] = 0 + c = min(c, t) + design_list["can_build"] = c + if(submenu == 2) + var/list/materials_list = list() + data["loaded_materials"] = materials_list + materials_list[++materials_list.len] = list("name" = "Metal", "id" = MAT_METAL, "amount" = linked_lathe.materials.amount(MAT_METAL)) + materials_list[++materials_list.len] = list("name" = "Glass", "id" = MAT_GLASS, "amount" = linked_lathe.materials.amount(MAT_GLASS)) + materials_list[++materials_list.len] = list("name" = "Gold", "id" = MAT_GOLD, "amount" = linked_lathe.materials.amount(MAT_GOLD)) + materials_list[++materials_list.len] = list("name" = "Silver", "id" = MAT_SILVER, "amount" = linked_lathe.materials.amount(MAT_SILVER)) + materials_list[++materials_list.len] = list("name" = "Solid Plasma", "id" = MAT_PLASMA, "amount" = linked_lathe.materials.amount(MAT_PLASMA)) + materials_list[++materials_list.len] = list("name" = "Uranium", "id" = MAT_URANIUM, "amount" = linked_lathe.materials.amount(MAT_URANIUM)) + materials_list[++materials_list.len] = list("name" = "Diamond", "id" = MAT_DIAMOND, "amount" = linked_lathe.materials.amount(MAT_DIAMOND)) + materials_list[++materials_list.len] = list("name" = "Bananium", "id" = MAT_BANANIUM, "amount" = linked_lathe.materials.amount(MAT_BANANIUM)) + materials_list[++materials_list.len] = list("name" = "Tranquillite", "id" = MAT_TRANQUILLITE, "amount" = linked_lathe.materials.amount(MAT_TRANQUILLITE)) + materials_list[++materials_list.len] = list("name" = "Titanium", "id" = MAT_TITANIUM, "amount" = linked_lathe.materials.amount(MAT_TITANIUM)) + materials_list[++materials_list.len] = list("name" = "Plastic", "id" = MAT_PLASTIC, "amount" = linked_lathe.materials.amount(MAT_PLASTIC)) + materials_list[++materials_list.len] = list("name" = "Bluespace Mesh", "id" = MAT_BLUESPACE, "amount" = linked_lathe.materials.amount(MAT_BLUESPACE)) + if(submenu == 3) + var/list/loaded_chemicals = list() + data["loaded_chemicals"] = loaded_chemicals + for(var/datum/reagent/R in linked_lathe.reagents.reagent_list) + var/list/loaded_chemical = list() + loaded_chemicals[++loaded_chemicals.len] = loaded_chemical + loaded_chemical["name"] = R.name + loaded_chemical["volume"] = R.volume + loaded_chemical["id"] = R.id + + if(menu == 5 && linked_imprinter) + data["total_materials"] = linked_imprinter.materials.total_amount + data["max_materials"] = linked_imprinter.materials.max_amount + data["total_chemicals"] = linked_imprinter.reagents.total_volume + data["max_chemicals"] = linked_imprinter.reagents.maximum_volume + data["categories"] = linked_imprinter.categories + if(submenu == 1) + var/list/designs_list = list() + data["matching_designs"] = designs_list + var/coeff = linked_imprinter.efficiency_coeff + for(var/datum/design/D in matching_designs) + var/list/design_list = list() + designs_list[++designs_list.len] = design_list + var/list/materials_list = list() + design_list["materials"] = materials_list + design_list["id"] = D.id + design_list["name"] = sanitize(D.name) + var/check_materials = 1 + for(var/M in D.materials) + var/list/material_list = list() + materials_list[++materials_list.len] = material_list + material_list["name"] = CallMaterialName(M) + material_list["amount"] = D.materials[M] / coeff + if(!linked_imprinter.check_mat(D, M)) + check_materials = 0 + material_list["is_red"] = 1 + else + material_list["is_red"] = 0 + + for(var/R in D.reagents_list) + var/list/material_list = list() + materials_list[++materials_list.len] = material_list + material_list["name"] = CallMaterialName(R) + material_list["amount"] = D.reagents_list[R]*coeff + if(!linked_imprinter.check_mat(D, R)) + check_materials = 0 + material_list["is_red"] = 1 + else + material_list["is_red"] = 0 + + design_list["can_build"] = check_materials + if(submenu == 2) + var/list/materials_list = list() + data["loaded_materials"] = materials_list + materials_list[++materials_list.len] = list("name" = "Metal", "id" = MAT_METAL, "amount" = linked_imprinter.materials.amount(MAT_METAL)) + materials_list[++materials_list.len] = list("name" = "Glass", "id" = MAT_GLASS, "amount" = linked_imprinter.materials.amount(MAT_GLASS)) + materials_list[++materials_list.len] = list("name" = "Gold", "id" = MAT_GOLD, "amount" = linked_imprinter.materials.amount(MAT_GOLD)) + materials_list[++materials_list.len] = list("name" = "Silver", "id" = MAT_SILVER, "amount" = linked_imprinter.materials.amount(MAT_SILVER)) + materials_list[++materials_list.len] = list("name" = "Solid Plasma", "id" = MAT_PLASMA, "amount" = linked_imprinter.materials.amount(MAT_PLASMA)) + materials_list[++materials_list.len] = list("name" = "Uranium", "id" = MAT_URANIUM, "amount" = linked_imprinter.materials.amount(MAT_URANIUM)) + materials_list[++materials_list.len] = list("name" = "Diamond", "id" = MAT_DIAMOND, "amount" = linked_imprinter.materials.amount(MAT_DIAMOND)) + materials_list[++materials_list.len] = list("name" = "Bananium", "id" = MAT_BANANIUM, "amount" = linked_imprinter.materials.amount(MAT_BANANIUM)) + materials_list[++materials_list.len] = list("name" = "Tranquillite", "id" = MAT_TRANQUILLITE, "amount" = linked_imprinter.materials.amount(MAT_TRANQUILLITE)) + materials_list[++materials_list.len] = list("name" = "Titanium", "id" = MAT_TITANIUM, "amount" = linked_imprinter.materials.amount(MAT_TITANIUM)) + materials_list[++materials_list.len] = list("name" = "Plastic", "id" = MAT_PLASTIC, "amount" = linked_imprinter.materials.amount(MAT_PLASTIC)) + materials_list[++materials_list.len] = list("name" = "Bluespace Mesh", "id" = MAT_BLUESPACE, "amount" = linked_imprinter.materials.amount(MAT_BLUESPACE)) + if(submenu == 3) + var/list/loaded_chemicals = list() + data["loaded_chemicals"] = loaded_chemicals + for(var/datum/reagent/R in linked_imprinter.reagents.reagent_list) + var/list/loaded_chemical = list() + loaded_chemicals[++loaded_chemicals.len] = loaded_chemical + loaded_chemical["name"] = R.name + loaded_chemical["volume"] = R.volume + loaded_chemical["id"] = R.id + + return data + +//helper proc that guarantees the wait message will not freeze the UI +/obj/machinery/computer/rdconsole/proc/add_wait_message(message, delay) + wait_message = message + wait_message_timer = addtimer(CALLBACK(src, .proc/clear_wait_message), delay, TIMER_UNIQUE | TIMER_STOPPABLE) + +// This is here to guarantee that we never lock the console, so long as the timer +// process is running +// So long as the spawns never runtime, though, the timer should be stopped +// before it gets the chance to fire +// Since the timer process can have significant delays, you should call this +// in the operations that take time, once they complete +/obj/machinery/computer/rdconsole/proc/clear_wait_message() + wait_message = "" + if(wait_message_timer) + // This could be expensive, and will still be called + // if the timer calls this function + deltimer(wait_message_timer) + wait_message_timer = 0 + SSnanoui.update_uis(src) + + +/obj/machinery/computer/rdconsole/core + name = "core R&D console" + desc = "A console used to interface with R&D tools." + id = 1 + +/obj/machinery/computer/rdconsole/robotics + name = "robotics R&D console" + desc = "A console used to interface with R&D tools." + id = 2 + req_access = list(ACCESS_ROBOTICS) + circuit = /obj/item/circuitboard/rdconsole/robotics + +/obj/machinery/computer/rdconsole/experiment + name = "\improper E.X.P.E.R.I-MENTOR R&D console" + desc = "A console used to interface with R&D tools." + id = 3 + circuit = /obj/item/circuitboard/rdconsole/experiment + +/obj/machinery/computer/rdconsole/mechanics + name = "mechanics R&D console" + desc = "A console used to interface with R&D tools." + id = 4 + req_access = list(ACCESS_MECHANIC) + circuit = /obj/item/circuitboard/rdconsole/mechanics + +/obj/machinery/computer/rdconsole/public + name = "public R&D console" + desc = "A console used to interface with R&D tools." + id = 5 + req_access = list() + circuit = /obj/item/circuitboard/rdconsole/public + +#undef TECH_UPDATE_DELAY +#undef DESIGN_UPDATE_DELAY +#undef PROTOLATHE_CONSTRUCT_DELAY +#undef SYNC_RESEARCH_DELAY +#undef DECONSTRUCT_DELAY +#undef SYNC_DEVICE_DELAY +#undef RESET_RESEARCH_DELAY +#undef IMPRINTER_DELAY diff --git a/code/modules/research/rdmachines.dm b/code/modules/research/rdmachines.dm index f6fc35932bc..7acdc53ce3d 100644 --- a/code/modules/research/rdmachines.dm +++ b/code/modules/research/rdmachines.dm @@ -1,133 +1,133 @@ -//All devices that link into the R&D console fall into thise type for easy identification and some shared procs. - - -/obj/machinery/r_n_d - name = "R&D Device" - icon = 'icons/obj/machines/research.dmi' - density = 1 - anchored = 1 - use_power = IDLE_POWER_USE - var/busy = 0 - var/hacked = 0 - var/disabled = 0 - var/shocked = 0 - var/list/wires = list() - var/hack_wire - var/disable_wire - var/shock_wire - var/obj/machinery/computer/rdconsole/linked_console - var/obj/item/loaded_item = null - var/datum/component/material_container/materials //Store for hyper speed! - -/obj/machinery/r_n_d/New() - materials = AddComponent(/datum/component/material_container, - list(MAT_METAL, MAT_GLASS, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_PLASMA, MAT_URANIUM, MAT_BANANIUM, MAT_TRANQUILLITE, MAT_TITANIUM, MAT_BLUESPACE, MAT_PLASTIC), 0, - TRUE, /obj/item/stack, CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert)) - materials.precise_insertion = TRUE - ..() - wires["Red"] = 0 - wires["Blue"] = 0 - wires["Green"] = 0 - wires["Yellow"] = 0 - wires["Black"] = 0 - wires["White"] = 0 - var/list/w = list("Red","Blue","Green","Yellow","Black","White") - src.hack_wire = pick(w) - w -= src.hack_wire - src.shock_wire = pick(w) - w -= src.shock_wire - src.disable_wire = pick(w) - w -= src.disable_wire - -/obj/machinery/r_n_d/attack_hand(mob/user as mob) - if(shocked) - shock(user,50) - if(panel_open) - var/list/dat = list() - dat += "[src.name] Wires:
        " - for(var/wire in wires) - dat += "[wire] Wire: [src.wires[wire] ? "Mend" : "Cut"] Pulse
        " - - dat += "The red light is [src.disabled ? "off" : "on"].
        " - dat += "The green light is [src.shocked ? "off" : "on"].
        " - dat += "The blue light is [src.hacked ? "off" : "on"].
        " - user << browse("[src.name] Hacking[dat.Join("")]","window=hack_win") - return - - -/obj/machinery/r_n_d/Topic(href, href_list) - if(..()) - return - usr.set_machine(src) - src.add_fingerprint(usr) - if(href_list["pulse"]) - var/temp_wire = href_list["wire"] - if(!istype(usr.get_active_hand(), /obj/item/multitool)) - to_chat(usr, "You need a multitool!") - else - if(src.wires[temp_wire]) - to_chat(usr, "You can't pulse a cut wire.") - else - if(src.hack_wire == href_list["wire"]) - src.hacked = !src.hacked - spawn(100) src.hacked = !src.hacked - if(src.disable_wire == href_list["wire"]) - src.disabled = !src.disabled - src.shock(usr,50) - spawn(100) src.disabled = !src.disabled - if(src.shock_wire == href_list["wire"]) - src.shocked = !src.shocked - src.shock(usr,50) - spawn(100) src.shocked = !src.shocked - if(href_list["cut"]) - if(!istype(usr.get_active_hand(), /obj/item/wirecutters)) - to_chat(usr, "You need wirecutters!") - else - var/temp_wire = href_list["wire"] - wires[temp_wire] = !wires[temp_wire] - if(src.hack_wire == temp_wire) - src.hacked = !src.hacked - if(src.disable_wire == temp_wire) - src.disabled = !src.disabled - src.shock(usr,50) - if(src.shock_wire == temp_wire) - src.shocked = !src.shocked - src.shock(usr,50) - src.updateUsrDialog() - -//whether the machine can have an item inserted in its current state. -/obj/machinery/r_n_d/proc/is_insertion_ready(mob/user) - if(panel_open) - to_chat(user, "You can't load [src] while it's opened!") - return FALSE - if(disabled) - return FALSE - if(!linked_console) - to_chat(user, "[src] must be linked to an R&D console first!") - return FALSE - if(busy) - to_chat(user, "[src] is busy right now.") - return FALSE - if(stat & BROKEN) - to_chat(user, "[src] is broken.") - return FALSE - if(stat & NOPOWER) - to_chat(user, "[src] has no power.") - return FALSE - if(loaded_item) - to_chat(user, "[src] is already loaded.") - return FALSE - return TRUE - -/obj/machinery/r_n_d/proc/AfterMaterialInsert(type_inserted, id_inserted, amount_inserted) - var/stack_name - if(ispath(type_inserted, /obj/item/stack/ore/bluespace_crystal)) - stack_name = "bluespace polycrystal" - use_power(MINERAL_MATERIAL_AMOUNT / 10) - else - var/obj/item/stack/S = type_inserted - stack_name = initial(S.name) - use_power(min(1000, (amount_inserted / 100))) - overlays += "[initial(name)]_[stack_name]" - sleep(10) - overlays -= "[initial(name)]_[stack_name]" +//All devices that link into the R&D console fall into thise type for easy identification and some shared procs. + + +/obj/machinery/r_n_d + name = "R&D Device" + icon = 'icons/obj/machines/research.dmi' + density = 1 + anchored = 1 + use_power = IDLE_POWER_USE + var/busy = 0 + var/hacked = 0 + var/disabled = 0 + var/shocked = 0 + var/list/wires = list() + var/hack_wire + var/disable_wire + var/shock_wire + var/obj/machinery/computer/rdconsole/linked_console + var/obj/item/loaded_item = null + var/datum/component/material_container/materials //Store for hyper speed! + +/obj/machinery/r_n_d/New() + materials = AddComponent(/datum/component/material_container, + list(MAT_METAL, MAT_GLASS, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_PLASMA, MAT_URANIUM, MAT_BANANIUM, MAT_TRANQUILLITE, MAT_TITANIUM, MAT_BLUESPACE, MAT_PLASTIC), 0, + TRUE, /obj/item/stack, CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert)) + materials.precise_insertion = TRUE + ..() + wires["Red"] = 0 + wires["Blue"] = 0 + wires["Green"] = 0 + wires["Yellow"] = 0 + wires["Black"] = 0 + wires["White"] = 0 + var/list/w = list("Red","Blue","Green","Yellow","Black","White") + src.hack_wire = pick(w) + w -= src.hack_wire + src.shock_wire = pick(w) + w -= src.shock_wire + src.disable_wire = pick(w) + w -= src.disable_wire + +/obj/machinery/r_n_d/attack_hand(mob/user as mob) + if(shocked) + shock(user,50) + if(panel_open) + var/list/dat = list() + dat += "[src.name] Wires:
        " + for(var/wire in wires) + dat += "[wire] Wire: [src.wires[wire] ? "Mend" : "Cut"] Pulse
        " + + dat += "The red light is [src.disabled ? "off" : "on"].
        " + dat += "The green light is [src.shocked ? "off" : "on"].
        " + dat += "The blue light is [src.hacked ? "off" : "on"].
        " + user << browse("[src.name] Hacking[dat.Join("")]","window=hack_win") + return + + +/obj/machinery/r_n_d/Topic(href, href_list) + if(..()) + return + usr.set_machine(src) + src.add_fingerprint(usr) + if(href_list["pulse"]) + var/temp_wire = href_list["wire"] + if(!istype(usr.get_active_hand(), /obj/item/multitool)) + to_chat(usr, "You need a multitool!") + else + if(src.wires[temp_wire]) + to_chat(usr, "You can't pulse a cut wire.") + else + if(src.hack_wire == href_list["wire"]) + src.hacked = !src.hacked + spawn(100) src.hacked = !src.hacked + if(src.disable_wire == href_list["wire"]) + src.disabled = !src.disabled + src.shock(usr,50) + spawn(100) src.disabled = !src.disabled + if(src.shock_wire == href_list["wire"]) + src.shocked = !src.shocked + src.shock(usr,50) + spawn(100) src.shocked = !src.shocked + if(href_list["cut"]) + if(!istype(usr.get_active_hand(), /obj/item/wirecutters)) + to_chat(usr, "You need wirecutters!") + else + var/temp_wire = href_list["wire"] + wires[temp_wire] = !wires[temp_wire] + if(src.hack_wire == temp_wire) + src.hacked = !src.hacked + if(src.disable_wire == temp_wire) + src.disabled = !src.disabled + src.shock(usr,50) + if(src.shock_wire == temp_wire) + src.shocked = !src.shocked + src.shock(usr,50) + src.updateUsrDialog() + +//whether the machine can have an item inserted in its current state. +/obj/machinery/r_n_d/proc/is_insertion_ready(mob/user) + if(panel_open) + to_chat(user, "You can't load [src] while it's opened!") + return FALSE + if(disabled) + return FALSE + if(!linked_console) + to_chat(user, "[src] must be linked to an R&D console first!") + return FALSE + if(busy) + to_chat(user, "[src] is busy right now.") + return FALSE + if(stat & BROKEN) + to_chat(user, "[src] is broken.") + return FALSE + if(stat & NOPOWER) + to_chat(user, "[src] has no power.") + return FALSE + if(loaded_item) + to_chat(user, "[src] is already loaded.") + return FALSE + return TRUE + +/obj/machinery/r_n_d/proc/AfterMaterialInsert(type_inserted, id_inserted, amount_inserted) + var/stack_name + if(ispath(type_inserted, /obj/item/stack/ore/bluespace_crystal)) + stack_name = "bluespace polycrystal" + use_power(MINERAL_MATERIAL_AMOUNT / 10) + else + var/obj/item/stack/S = type_inserted + stack_name = initial(S.name) + use_power(min(1000, (amount_inserted / 100))) + overlays += "[initial(name)]_[stack_name]" + sleep(10) + overlays -= "[initial(name)]_[stack_name]" diff --git a/code/modules/research/research.dm b/code/modules/research/research.dm index 05786daed94..b5fc9e9c879 100644 --- a/code/modules/research/research.dm +++ b/code/modules/research/research.dm @@ -1,399 +1,400 @@ -/* -General Explination: -The research datum is the "folder" where all the research information is stored in a R&D console. It's also a holder for all the -various procs used to manipulate it. It has four variables and seven procs: - -Variables: -- possible_tech is a list of all the /datum/tech that can potentially be researched by the player. The RefreshResearch() proc -(explained later) only goes through those when refreshing what you know. Generally, possible_tech contains ALL of the existing tech -but it is possible to add tech to the game that DON'T start in it (example: Xeno tech). Generally speaking, you don't want to mess -with these since they should be the default version of the datums. They're actually stored in a list rather then using typesof to -refer to them since it makes it a bit easier to search through them for specific information. -- know_tech is the companion list to possible_tech. It's the tech you can actually research and improve. Until it's added to this -list, it can't be improved. All the tech in this list are visible to the player. -- possible_designs is functionally identical to possbile_tech except it's for /datum/design. -- known_designs is functionally identical to known_tech except it's for /datum/design - -Procs: -- TechHasReqs: Used by other procs (specifically RefreshResearch) to see whether all of a tech's requirements are currently in -known_tech and at a high enough level. -- DesignHasReqs: Same as TechHasReqs but for /datum/design and known_design. -- AddTech2Known: Adds a /datum/tech to known_tech. It checks to see whether it already has that tech (if so, it just replaces it). If -it doesn't have it, it adds it. Note: It does NOT check possible_tech at all. So if you want to add something strange to it (like -a player made tech?) you can. -- AddDesign2Known: Same as AddTech2Known except for /datum/design and known_designs. -- RefreshResearch: This is the workhorse of the R&D system. It updates the /datum/research holder and adds any unlocked tech paths -and designs you have reached the requirements for. It only checks through possible_tech and possible_designs, however, so it won't -accidentally add "secret" tech to it. -- UpdateTech is used as part of the actual researching process. It takes an ID and finds techs with that same ID in known_tech. When -it finds it, it checks to see whether it can improve it at all. If the known_tech's level is less then or equal to -the inputted level, it increases the known tech's level to the inputted level -1 or know tech's level +1 (whichever is higher). - -The tech datums are the actual "tech trees" that you improve through researching. Each one has five variables: -- Name: Pretty obvious. This is often viewable to the players. -- Desc: Pretty obvious. Also player viewable. -- ID: This is the unique ID of the tech that is used by the various procs to find and/or maniuplate it. -- Level: This is the current level of the tech. All techs start at 1 and have a max of 20. Devices and some techs require a certain -level in specific techs before you can produce them. -- Req_tech: This is a list of the techs required to unlock this tech path. If left blank, it'll automatically be loaded into the -research holder datum. - -*/ -/*************************************************************** -** Master Types ** -** Includes all the helper procs and basic tech processing. ** -***************************************************************/ - -/datum/research //Holder for all the existing, archived, and known tech. Individual to console. - - //Datum/tech go here. - // Possible is a list of direct datum references - // known is a list of id -> datum mappings - var/list/possible_tech = list() //List of all tech in the game that players have access to (barring special events). - var/list/known_tech = list() //List of locally known tech. - var/list/possible_designs = list() //List of all designs - var/list/known_designs = list() //List of available designs - -/datum/research/New() //Insert techs into possible_tech here. Known_tech automatically updated. - // MON DIEU!!! - // These are semi-global, but not TOTALLY global? - // Using research disks, you can get techs/designs from one research datum - // onto another. What consequences this could have, I am presently unsure, but - // I imagine nothing good. - for(var/T in subtypesof(/datum/tech)) - possible_tech += new T(src) - for(var/D in subtypesof(/datum/design)) - possible_designs += new D(src) - RefreshResearch() - - - -//Checks to see if tech has all the required pre-reqs. -//Input: datum/tech; Output: 0/1 (false/true) -/datum/research/proc/TechHasReqs(var/datum/tech/T) - if(T.req_tech.len == 0) - return TRUE - for(var/req in T.req_tech) - var/datum/tech/known = known_tech[req] - if(!known || known.level < T.req_tech[req]) - return FALSE - return TRUE - -//Checks to see if design has all the required pre-reqs. -//Input: datum/design; Output: 0/1 (false/true) -/datum/research/proc/DesignHasReqs(var/datum/design/D) - if(D.req_tech.len == 0) - return TRUE - for(var/req in D.req_tech) - var/datum/tech/known = known_tech[req] - if(!known || known.level < D.req_tech[req]) - return FALSE - return TRUE - -//Adds a tech to known_tech list. Checks to make sure there aren't duplicates and updates existing tech's levels if needed. -//Input: datum/tech; Output: Null -/datum/research/proc/AddTech2Known(var/datum/tech/T) - if(T.id in known_tech) - var/datum/tech/known = known_tech[T.id] - if(T.level > known.level) - known.level = T.level - return - known_tech[T.id] = T - -/datum/research/proc/AddDesign2Known(var/datum/design/D) - if(D.id in known_designs) - return - // Global datums make me nervous - known_designs[D.id] = D - -//Refreshes known_tech and known_designs list. -//Input/Output: n/a -/datum/research/proc/RefreshResearch() - for(var/datum/tech/PT in possible_tech) - if(TechHasReqs(PT)) - AddTech2Known(PT) - for(var/datum/design/PD in possible_designs) - if(DesignHasReqs(PD)) - AddDesign2Known(PD) - for(var/v in known_tech) - var/datum/tech/T = known_tech[v] - T.level = Clamp(T.level, 0, 20) - -//Refreshes the levels of a given tech. -//Input: Tech's ID and Level; Output: null -/datum/research/proc/UpdateTech(var/ID, var/level) - var/datum/tech/KT = known_tech[ID] - if(KT) - if(KT.level <= level) - // Will bump the tech to (value_of_target) automatically - - // after that it'll bump it up by 1 until it's greater - // than the source tech - KT.level = max((KT.level + 1), level) - -//Checks if the origin level can raise current tech levels -//Input: Tech's ID and Level; Output: TRUE for yes, FALSE for no -/datum/research/proc/IsTechHigher(ID, level) - var/datum/tech/KT = known_tech[ID] - if(KT) - if(KT.level <= level) - return TRUE - else - return FALSE - -/datum/research/proc/FindDesignByID(var/id) - return known_designs[id] - -// A common task is for one research datum to copy over its techs and designs -// and update them on another research datum. -// Arguments: -// `other` - The research datum to send designs and techs to -/datum/research/proc/push_data(datum/research/other) - for(var/v in known_tech) - var/datum/tech/T = known_tech[v] - other.AddTech2Known(T) - for(var/v in known_designs) - var/datum/design/D = known_designs[v] - other.AddDesign2Known(D) - other.RefreshResearch() - - -//Autolathe files -/datum/research/autolathe - -/datum/research/autolathe/DesignHasReqs(var/datum/design/D) - return D && (D.build_type & AUTOLATHE) && ("initial" in D.category) - -/datum/research/autolathe/AddDesign2Known(var/datum/design/D) - if(!(D.build_type & AUTOLATHE)) - return - ..() - -//Biogenerator files -/datum/research/biogenerator/New() - for(var/T in (subtypesof(/datum/tech))) - possible_tech += new T(src) - for(var/path in subtypesof(/datum/design)) - var/datum/design/D = new path(src) - possible_designs += D - if((D.build_type & BIOGENERATOR) && ("initial" in D.category)) - AddDesign2Known(D) - -/datum/research/biogenerator/AddDesign2Known(datum/design/D) - if(!(D.build_type & BIOGENERATOR)) - return - ..() - -//Smelter files -/datum/research/smelter/New() - for(var/T in (subtypesof(/datum/tech))) - possible_tech += new T(src) - for(var/path in subtypesof(/datum/design)) - var/datum/design/D = new path(src) - possible_designs += D - if((D.build_type & SMELTER) && ("initial" in D.category)) - AddDesign2Known(D) - -/datum/research/smelter/AddDesign2Known(datum/design/D) - if(!(D.build_type & SMELTER)) - return - ..() - -/*************************************************************** -** Technology Datums ** -** Includes all the various technoliges and what they make. ** -***************************************************************/ - -/datum/tech //Datum of individual technologies. - var/name = "name" //Name of the technology. - var/desc = "description" //General description of what it does and what it makes. - var/id = "id" //An easily referenced ID. Must be alphanumeric, lower-case, and no symbols. - var/level = 1 //A simple number scale of the research level. Level 0 = Secret tech. - var/max_level = 1 // Maximum level this can be at (for job objectives) - var/rare = 1 //How much CentCom wants to get that tech. Used in supply shuttle tech cost calculation. - var/list/req_tech = list() //List of ids associated values of techs required to research this tech. "id" = # - - -//Trunk Technologies (don't require any other techs and you start knowning them). - -/datum/tech/materials - name = "Materials Research" - desc = "Development of new and improved materials." - id = "materials" - max_level = 7 - -/datum/tech/engineering - name = "Engineering Research" - desc = "Development of new and improved engineering parts and methods." - id = "engineering" - max_level = 7 - -/datum/tech/plasmatech - name = "Plasma Research" - desc = "Research into the mysterious substance colloqually known as 'plasma'." - id = "plasmatech" - max_level = 7 - rare = 3 - -/datum/tech/powerstorage - name = "Power Manipulation Technology" - desc = "The various technologies behind the storage and generation of electicity." - id = "powerstorage" - max_level = 7 - -/datum/tech/bluespace - name = "'Blue-space' Research" - desc = "Research into the sub-reality known as 'blue-space'." - id = "bluespace" - max_level = 7 - rare = 2 - -/datum/tech/biotech - name = "Biological Technology" - desc = "Research into the deeper mysteries of life and organic substances." - id = "biotech" - max_level = 7 - -/datum/tech/combat - name = "Combat Systems Research" - desc = "The development of offensive and defensive systems." - id = "combat" - max_level = 7 - -/datum/tech/magnets - name = "Electromagnetic Spectrum Research" - desc = "Research into the electromagnetic spectrum. No clue how they actually work, though." - id = "magnets" - max_level = 7 - -/datum/tech/programming - name = "Data Theory Research" - desc = "The development of new computer and artificial intelligence and data storage systems." - id = "programming" - max_level = 7 - -/datum/tech/toxins //not meant to be raised by deconstruction, do not give objects toxins as an origin_tech - name = "Toxins Research" - desc = "Research into plasma based explosive devices. Upgrade through testing explosives in the toxins lab." - id = "toxins" - max_level = 7 - rare = 2 - -/datum/tech/syndicate - name = "Illegal Technologies Research" - desc = "The study of technologies that violate standard Nanotrasen regulations." - id = "syndicate" - max_level = 0 // Don't count towards maxed research, since it's illegal. - rare = 4 - -/datum/tech/abductor - name = "Alien Technologies Research" - desc = "The study of technologies used by the advanced alien race known as Abductors." - id = "abductor" - rare = 5 - level = 0 - -/* -datum/tech/arcane - name = "Arcane Research" - desc = "Research into the occult and arcane field for use in practical science" - id = "arcane" - level = 0 //It didn't become "secret" as advertised. - -//Branch Techs -datum/tech/explosives - name = "Explosives Research" - desc = "The creation and application of explosive materials." - id = "explosives" - req_tech = list("materials" = 3) - -datum/tech/generators - name = "Power Generation Technology" - desc = "Research into more powerful and more reliable sources." - id = "generators" - req_tech = list("powerstorage" = 2) - -datum/tech/robotics - name = "Robotics Technology" - desc = "The development of advanced automated, autonomous machines." - id = "robotics" - req_tech = list("materials" = 3, "programming" = 3) -*/ - -/datum/tech/proc/getCost(var/current_level = null) - // Calculates tech disk's supply points sell cost - if(!current_level) - current_level = initial(level) - - if(current_level >= level) - return 0 - - var/cost = 0 - for(var/i=current_level+1, i<=level, i++) - if(i == initial(level)) - continue - cost += i*5*rare - - return cost - -/obj/item/disk/tech_disk - name = "\improper Technology Disk" - desc = "A disk for storing technology data for further research." - icon_state = "datadisk2" - materials = list(MAT_METAL=30, MAT_GLASS=10) - var/datum/tech/stored - var/default_name = "\improper Technology Disk" - var/default_desc = "A disk for storing technology data for further research." - -/obj/item/disk/tech_disk/New() - src.pixel_x = rand(-5.0, 5) - src.pixel_y = rand(-5.0, 5) - -/obj/item/disk/tech_disk/proc/load_tech(datum/tech/T) - name = "[default_name] \[[T]\]" - desc = T.desc + " Level: '[T.level]'" - // NOTE: This is just a reference to the tech on the system it grabbed it from - // This seems highly fragile - stored = T - -/obj/item/disk/tech_disk/proc/wipe_tech() - name = default_name - desc = default_desc - stored = null - -/obj/item/disk/design_disk - name = "\improper Component Design Disk" - desc = "A disk for storing device design data for construction in lathes." - icon_state = "datadisk2" - materials = list(MAT_METAL=100, MAT_GLASS=100) - var/datum/design/blueprint - // I'm doing this so that disk paths with pre-loaded designs don't get weird names - // Otherwise, I'd use "initial()" - var/default_name = "\improper Component Design Disk" - var/default_desc = "A disk for storing device design data for construction in lathes." - -/obj/item/disk/design_disk/New() - ..() - pixel_x = rand(-5, 5) - pixel_y = rand(-5, 5) - -/obj/item/disk/design_disk/proc/load_blueprint(datum/design/D) - name = "[default_name] \[[D]\]" - desc = D.desc - // NOTE: This is just a reference to the design on the system it grabbed it from - // This seems highly fragile - blueprint = D - -/obj/item/disk/design_disk/proc/wipe_blueprint() - name = default_name - desc = default_desc - blueprint = null - -/obj/item/disk/design_disk/golem_shell - name = "golem creation disk" - desc = "A gift from the Liberator." - icon_state = "datadisk1" - -/obj/item/disk/design_disk/golem_shell/Initialize() - . = ..() - var/datum/design/golem_shell/G = new - blueprint = G \ No newline at end of file +/* +General Explination: +The research datum is the "folder" where all the research information is stored in a R&D console. It's also a holder for all the +various procs used to manipulate it. It has four variables and seven procs: + +Variables: +- possible_tech is a list of all the /datum/tech that can potentially be researched by the player. The RefreshResearch() proc +(explained later) only goes through those when refreshing what you know. Generally, possible_tech contains ALL of the existing tech +but it is possible to add tech to the game that DON'T start in it (example: Xeno tech). Generally speaking, you don't want to mess +with these since they should be the default version of the datums. They're actually stored in a list rather then using typesof to +refer to them since it makes it a bit easier to search through them for specific information. +- know_tech is the companion list to possible_tech. It's the tech you can actually research and improve. Until it's added to this +list, it can't be improved. All the tech in this list are visible to the player. +- possible_designs is functionally identical to possbile_tech except it's for /datum/design. +- known_designs is functionally identical to known_tech except it's for /datum/design + +Procs: +- TechHasReqs: Used by other procs (specifically RefreshResearch) to see whether all of a tech's requirements are currently in +known_tech and at a high enough level. +- DesignHasReqs: Same as TechHasReqs but for /datum/design and known_design. +- AddTech2Known: Adds a /datum/tech to known_tech. It checks to see whether it already has that tech (if so, it just replaces it). If +it doesn't have it, it adds it. Note: It does NOT check possible_tech at all. So if you want to add something strange to it (like +a player made tech?) you can. +- AddDesign2Known: Same as AddTech2Known except for /datum/design and known_designs. +- RefreshResearch: This is the workhorse of the R&D system. It updates the /datum/research holder and adds any unlocked tech paths +and designs you have reached the requirements for. It only checks through possible_tech and possible_designs, however, so it won't +accidentally add "secret" tech to it. +- UpdateTech is used as part of the actual researching process. It takes an ID and finds techs with that same ID in known_tech. When +it finds it, it checks to see whether it can improve it at all. If the known_tech's level is less then or equal to +the inputted level, it increases the known tech's level to the inputted level -1 or know tech's level +1 (whichever is higher). + +The tech datums are the actual "tech trees" that you improve through researching. Each one has five variables: +- Name: Pretty obvious. This is often viewable to the players. +- Desc: Pretty obvious. Also player viewable. +- ID: This is the unique ID of the tech that is used by the various procs to find and/or maniuplate it. +- Level: This is the current level of the tech. All techs start at 1 and have a max of 20. Devices and some techs require a certain +level in specific techs before you can produce them. +- Req_tech: This is a list of the techs required to unlock this tech path. If left blank, it'll automatically be loaded into the +research holder datum. + +*/ +/*************************************************************** +** Master Types ** +** Includes all the helper procs and basic tech processing. ** +***************************************************************/ + +/datum/research //Holder for all the existing, archived, and known tech. Individual to console. + + //Datum/tech go here. + // Possible is a list of direct datum references + // known is a list of id -> datum mappings + var/list/possible_tech = list() //List of all tech in the game that players have access to (barring special events). + var/list/known_tech = list() //List of locally known tech. + var/list/possible_designs = list() //List of all designs + var/list/known_designs = list() //List of available designs + +/datum/research/New() //Insert techs into possible_tech here. Known_tech automatically updated. + // MON DIEU!!! + // These are semi-global, but not TOTALLY global? + // Using research disks, you can get techs/designs from one research datum + // onto another. What consequences this could have, I am presently unsure, but + // I imagine nothing good. + for(var/T in subtypesof(/datum/tech)) + possible_tech += new T(src) + for(var/D in subtypesof(/datum/design)) + possible_designs += new D(src) + RefreshResearch() + + + +//Checks to see if tech has all the required pre-reqs. +//Input: datum/tech; Output: 0/1 (false/true) +/datum/research/proc/TechHasReqs(var/datum/tech/T) + if(T.req_tech.len == 0) + return TRUE + for(var/req in T.req_tech) + var/datum/tech/known = known_tech[req] + if(!known || known.level < T.req_tech[req]) + return FALSE + return TRUE + +//Checks to see if design has all the required pre-reqs. +//Input: datum/design; Output: 0/1 (false/true) +/datum/research/proc/DesignHasReqs(var/datum/design/D) + if(D.req_tech.len == 0) + return TRUE + for(var/req in D.req_tech) + var/datum/tech/known = known_tech[req] + if(!known || known.level < D.req_tech[req]) + return FALSE + return TRUE + +//Adds a tech to known_tech list. Checks to make sure there aren't duplicates and updates existing tech's levels if needed. +//Input: datum/tech; Output: Null +/datum/research/proc/AddTech2Known(var/datum/tech/T) + if(T.id in known_tech) + var/datum/tech/known = known_tech[T.id] + if(T.level > known.level) + known.level = T.level + return + known_tech[T.id] = T + +/datum/research/proc/AddDesign2Known(var/datum/design/D) + if(D.id in known_designs) + return + // Global datums make me nervous + known_designs[D.id] = D + +//Refreshes known_tech and known_designs list. +//Input/Output: n/a +/datum/research/proc/RefreshResearch() + for(var/datum/tech/PT in possible_tech) + if(TechHasReqs(PT)) + AddTech2Known(PT) + for(var/datum/design/PD in possible_designs) + if(DesignHasReqs(PD)) + AddDesign2Known(PD) + for(var/v in known_tech) + var/datum/tech/T = known_tech[v] + T.level = Clamp(T.level, 0, 20) + +//Refreshes the levels of a given tech. +//Input: Tech's ID and Level; Output: null +/datum/research/proc/UpdateTech(var/ID, var/level) + var/datum/tech/KT = known_tech[ID] + if(KT) + if(KT.level <= level) + // Will bump the tech to (value_of_target) automatically - + // after that it'll bump it up by 1 until it's greater + // than the source tech + KT.level = max((KT.level + 1), level) + +//Checks if the origin level can raise current tech levels +//Input: Tech's ID and Level; Output: TRUE for yes, FALSE for no +/datum/research/proc/IsTechHigher(ID, level) + var/datum/tech/KT = known_tech[ID] + if(KT) + if(KT.level <= level) + return TRUE + else + return FALSE + +/datum/research/proc/FindDesignByID(var/id) + return known_designs[id] + +// A common task is for one research datum to copy over its techs and designs +// and update them on another research datum. +// Arguments: +// `other` - The research datum to send designs and techs to +/datum/research/proc/push_data(datum/research/other) + for(var/v in known_tech) + var/datum/tech/T = known_tech[v] + other.AddTech2Known(T) + for(var/v in known_designs) + var/datum/design/D = known_designs[v] + other.AddDesign2Known(D) + other.RefreshResearch() + + +//Autolathe files +/datum/research/autolathe + +/datum/research/autolathe/DesignHasReqs(var/datum/design/D) + return D && (D.build_type & AUTOLATHE) && ("initial" in D.category) + +/datum/research/autolathe/AddDesign2Known(var/datum/design/D) + if(!(D.build_type & AUTOLATHE)) + return + ..() + +//Biogenerator files +/datum/research/biogenerator/New() + for(var/T in (subtypesof(/datum/tech))) + possible_tech += new T(src) + for(var/path in subtypesof(/datum/design)) + var/datum/design/D = new path(src) + possible_designs += D + if((D.build_type & BIOGENERATOR) && ("initial" in D.category)) + AddDesign2Known(D) + +/datum/research/biogenerator/AddDesign2Known(datum/design/D) + if(!(D.build_type & BIOGENERATOR)) + return + ..() + +//Smelter files +/datum/research/smelter/New() + for(var/T in (subtypesof(/datum/tech))) + possible_tech += new T(src) + for(var/path in subtypesof(/datum/design)) + var/datum/design/D = new path(src) + possible_designs += D + if((D.build_type & SMELTER) && ("initial" in D.category)) + AddDesign2Known(D) + +/datum/research/smelter/AddDesign2Known(datum/design/D) + if(!(D.build_type & SMELTER)) + return + ..() + +/*************************************************************** +** Technology Datums ** +** Includes all the various technoliges and what they make. ** +***************************************************************/ + +/datum/tech //Datum of individual technologies. + var/name = "name" //Name of the technology. + var/desc = "description" //General description of what it does and what it makes. + var/id = "id" //An easily referenced ID. Must be alphanumeric, lower-case, and no symbols. + var/level = 1 //A simple number scale of the research level. Level 0 = Secret tech. + var/max_level = 1 // Maximum level this can be at (for job objectives) + var/rare = 1 //How much CentCom wants to get that tech. Used in supply shuttle tech cost calculation. + var/list/req_tech = list() //List of ids associated values of techs required to research this tech. "id" = # + + +//Trunk Technologies (don't require any other techs and you start knowning them). + +/datum/tech/materials + name = "Materials Research" + desc = "Development of new and improved materials." + id = "materials" + max_level = 7 + +/datum/tech/engineering + name = "Engineering Research" + desc = "Development of new and improved engineering parts and methods." + id = "engineering" + max_level = 7 + +/datum/tech/plasmatech + name = "Plasma Research" + desc = "Research into the mysterious substance colloqually known as 'plasma'." + id = "plasmatech" + max_level = 7 + rare = 3 + +/datum/tech/powerstorage + name = "Power Manipulation Technology" + desc = "The various technologies behind the storage and generation of electicity." + id = "powerstorage" + max_level = 7 + +/datum/tech/bluespace + name = "'Blue-space' Research" + desc = "Research into the sub-reality known as 'blue-space'." + id = "bluespace" + max_level = 7 + rare = 2 + +/datum/tech/biotech + name = "Biological Technology" + desc = "Research into the deeper mysteries of life and organic substances." + id = "biotech" + max_level = 7 + +/datum/tech/combat + name = "Combat Systems Research" + desc = "The development of offensive and defensive systems." + id = "combat" + max_level = 7 + +/datum/tech/magnets + name = "Electromagnetic Spectrum Research" + desc = "Research into the electromagnetic spectrum. No clue how they actually work, though." + id = "magnets" + max_level = 7 + +/datum/tech/programming + name = "Data Theory Research" + desc = "The development of new computer and artificial intelligence and data storage systems." + id = "programming" + max_level = 7 + +/datum/tech/toxins //not meant to be raised by deconstruction, do not give objects toxins as an origin_tech + name = "Toxins Research" + desc = "Research into plasma based explosive devices. Upgrade through testing explosives in the toxins lab." + id = "toxins" + max_level = 7 + rare = 2 + +/datum/tech/syndicate + name = "Illegal Technologies Research" + desc = "The study of technologies that violate standard Nanotrasen regulations." + id = "syndicate" + max_level = 0 // Don't count towards maxed research, since it's illegal. + rare = 4 + +/datum/tech/abductor + name = "Alien Technologies Research" + desc = "The study of technologies used by the advanced alien race known as Abductors." + id = "abductor" + rare = 5 + level = 0 + +/* +datum/tech/arcane + name = "Arcane Research" + desc = "Research into the occult and arcane field for use in practical science" + id = "arcane" + level = 0 //It didn't become "secret" as advertised. + +//Branch Techs +datum/tech/explosives + name = "Explosives Research" + desc = "The creation and application of explosive materials." + id = "explosives" + req_tech = list("materials" = 3) + +datum/tech/generators + name = "Power Generation Technology" + desc = "Research into more powerful and more reliable sources." + id = "generators" + req_tech = list("powerstorage" = 2) + +datum/tech/robotics + name = "Robotics Technology" + desc = "The development of advanced automated, autonomous machines." + id = "robotics" + req_tech = list("materials" = 3, "programming" = 3) +*/ + +/datum/tech/proc/getCost(var/current_level = null) + // Calculates tech disk's supply points sell cost + if(!current_level) + current_level = initial(level) + + if(current_level >= level) + return 0 + + var/cost = 0 + for(var/i=current_level+1, i<=level, i++) + if(i == initial(level)) + continue + cost += i*5*rare + + return cost + +/obj/item/disk/tech_disk + name = "\improper Technology Disk" + desc = "A disk for storing technology data for further research." + icon_state = "datadisk2" + materials = list(MAT_METAL=30, MAT_GLASS=10) + var/datum/tech/stored + var/default_name = "\improper Technology Disk" + var/default_desc = "A disk for storing technology data for further research." + +/obj/item/disk/tech_disk/New() + ..() + src.pixel_x = rand(-5.0, 5) + src.pixel_y = rand(-5.0, 5) + +/obj/item/disk/tech_disk/proc/load_tech(datum/tech/T) + name = "[default_name] \[[T]\]" + desc = T.desc + " Level: '[T.level]'" + // NOTE: This is just a reference to the tech on the system it grabbed it from + // This seems highly fragile + stored = T + +/obj/item/disk/tech_disk/proc/wipe_tech() + name = default_name + desc = default_desc + stored = null + +/obj/item/disk/design_disk + name = "\improper Component Design Disk" + desc = "A disk for storing device design data for construction in lathes." + icon_state = "datadisk2" + materials = list(MAT_METAL=100, MAT_GLASS=100) + var/datum/design/blueprint + // I'm doing this so that disk paths with pre-loaded designs don't get weird names + // Otherwise, I'd use "initial()" + var/default_name = "\improper Component Design Disk" + var/default_desc = "A disk for storing device design data for construction in lathes." + +/obj/item/disk/design_disk/New() + ..() + pixel_x = rand(-5, 5) + pixel_y = rand(-5, 5) + +/obj/item/disk/design_disk/proc/load_blueprint(datum/design/D) + name = "[default_name] \[[D]\]" + desc = D.desc + // NOTE: This is just a reference to the design on the system it grabbed it from + // This seems highly fragile + blueprint = D + +/obj/item/disk/design_disk/proc/wipe_blueprint() + name = default_name + desc = default_desc + blueprint = null + +/obj/item/disk/design_disk/golem_shell + name = "golem creation disk" + desc = "A gift from the Liberator." + icon_state = "datadisk1" + +/obj/item/disk/design_disk/golem_shell/Initialize() + . = ..() + var/datum/design/golem_shell/G = new + blueprint = G diff --git a/code/modules/research/server.dm b/code/modules/research/server.dm index abb20ac091a..de9194953da 100644 --- a/code/modules/research/server.dm +++ b/code/modules/research/server.dm @@ -1,360 +1,360 @@ -/obj/machinery/r_n_d/server - name = "R&D Server" - icon = 'icons/obj/machines/research.dmi' - icon_state = "server" - var/datum/research/files - var/health = 100 - var/list/id_with_upload = list() //List of R&D consoles with upload to server access. - var/list/id_with_download = list() //List of R&D consoles with download from server access. - var/id_with_upload_string = "" //String versions for easy editing in map editor. - var/id_with_download_string = "" - var/server_id = 0 - var/heat_gen = 100 - var/heating_power = 40000 - var/delay = 10 - req_access = list(access_rd) //Only the R&D can change server settings. - var/plays_sound = 0 - -/obj/machinery/r_n_d/server/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/rdserver(null) - component_parts += new /obj/item/stock_parts/scanning_module(null) - component_parts += new /obj/item/stack/cable_coil(null,1) - component_parts += new /obj/item/stack/cable_coil(null,1) - RefreshParts() - initialize_serv(); //Agouri // fuck you agouri - -/obj/machinery/r_n_d/server/upgraded/New() - ..() - component_parts = list() - component_parts += new /obj/item/circuitboard/rdserver(null) - component_parts += new /obj/item/stock_parts/scanning_module/phasic(null) - component_parts += new /obj/item/stack/cable_coil(null,1) - component_parts += new /obj/item/stack/cable_coil(null,1) - RefreshParts() - -/obj/machinery/r_n_d/server/Destroy() - griefProtection() - return ..() - -/obj/machinery/r_n_d/server/RefreshParts() - var/tot_rating = 0 - for(var/obj/item/stock_parts/SP in src) - tot_rating += SP.rating - heat_gen /= max(1, tot_rating) - -/obj/machinery/r_n_d/server/proc/initialize_serv() - if(!files) - files = new /datum/research(src) - var/list/temp_list - if(!id_with_upload.len) - temp_list = list() - temp_list = splittext(id_with_upload_string, ";") - for(var/N in temp_list) - id_with_upload += text2num(N) - if(!id_with_download.len) - temp_list = list() - temp_list = splittext(id_with_download_string, ";") - for(var/N in temp_list) - id_with_download += text2num(N) - -/obj/machinery/r_n_d/server/process() - if(prob(3) && plays_sound) - playsound(loc, "computer_ambience", 50, 1) - - var/datum/gas_mixture/environment = loc.return_air() - switch(environment.temperature) - if(0 to T0C) - health = min(100, health + 1) - if(T0C to (T20C + 20)) - health = Clamp(health, 0, 100) - if((T20C + 20) to (T0C + 70)) - health = max(0, health - 1) - if(health <= 0) - /*griefProtection() This seems to get called twice before running any code that deletes/damages the server or it's files anwyay. - refreshParts and the hasReq procs that get called by this are laggy and do not need to be called by every server on the map every tick */ - var/updateRD = 0 - files.known_designs = list() - for(var/v in files.known_tech) - var/datum/tech/T = files.known_tech[v] - // Slowly decrease research if health drops below 0 - if(prob(1)) - updateRD++ - T.level-- - if(updateRD) - files.RefreshResearch() - if(delay) - delay-- - else - produce_heat(heat_gen) - delay = initial(delay) - -/obj/machinery/r_n_d/server/emp_act(severity) - griefProtection() - ..() - - -/obj/machinery/r_n_d/server/ex_act(severity) - griefProtection() - return ..() - -/obj/machinery/r_n_d/server/blob_act(obj/structure/blob/B) - griefProtection() - return ..() - -// Backup files to CentComm to help admins recover data after griefer attacks -/obj/machinery/r_n_d/server/proc/griefProtection() - for(var/obj/machinery/r_n_d/server/centcom/C in GLOB.machines) - files.push_data(C.files) - -/obj/machinery/r_n_d/server/proc/produce_heat(heat_amt) - if(!(stat & (NOPOWER|BROKEN))) // Blatantly stolen from space heater. - var/turf/simulated/L = loc - if(istype(L)) - var/datum/gas_mixture/env = L.return_air() - if(env.temperature < (heat_amt+T0C)) - - var/transfer_moles = 0.25 * env.total_moles() - - var/datum/gas_mixture/removed = env.remove(transfer_moles) - - if(removed) - - var/heat_capacity = removed.heat_capacity() - if(heat_capacity == 0 || heat_capacity == null) - heat_capacity = 1 - removed.temperature = min((removed.temperature*heat_capacity + heating_power)/heat_capacity, 1000) - - env.merge(removed) - air_update_turf() - -/obj/machinery/r_n_d/server/attackby(var/obj/item/O as obj, var/mob/user as mob, params) - if(disabled) - return - - if(shocked) - shock(user,50) - - if(istype(O, /obj/item/screwdriver)) - default_deconstruction_screwdriver(user, "server_o", "server", O) - return 1 - - if(exchange_parts(user, O)) - return 1 - - if(panel_open) - if(istype(O, /obj/item/crowbar)) - griefProtection() - default_deconstruction_crowbar(O) - return 1 - else - return ..() - -/obj/machinery/r_n_d/server/attack_hand(mob/user as mob) - if(disabled) - return - - if(shocked) - shock(user,50) - return - -/obj/machinery/r_n_d/server/centcom - name = "CentComm. Central R&D Database" - server_id = -1 - -/obj/machinery/r_n_d/server/centcom/Initialize() - ..() - var/list/no_id_servers = list() - var/list/server_ids = list() - for(var/obj/machinery/r_n_d/server/S in GLOB.machines) - switch(S.server_id) - if(-1) - continue - if(0) - no_id_servers += S - else - server_ids += S.server_id - - for(var/obj/machinery/r_n_d/server/S in no_id_servers) - var/num = 1 - while(!S.server_id) - if(num in server_ids) - num++ - else - S.server_id = num - server_ids += num - no_id_servers -= S - -/obj/machinery/r_n_d/server/centcom/process() - return PROCESS_KILL //don't need process() - - -/obj/machinery/computer/rdservercontrol - name = "\improper R&D server controller" - icon_screen = "rdcomp" - icon_keyboard = "rd_key" - light_color = LIGHT_COLOR_FADEDPURPLE - circuit = /obj/item/circuitboard/rdservercontrol - var/screen = 0 - var/obj/machinery/r_n_d/server/temp_server - var/list/servers = list() - var/list/consoles = list() - var/badmin = 0 - -/obj/machinery/computer/rdservercontrol/Topic(href, href_list) - if(..()) - return - - add_fingerprint(usr) - usr.set_machine(src) - if(!src.allowed(usr) && !emagged) - to_chat(usr, "You do not have the required access level") - return - - if(href_list["main"]) - screen = 0 - - else if(href_list["access"] || href_list["data"] || href_list["transfer"]) - temp_server = null - consoles = list() - servers = list() - for(var/obj/machinery/r_n_d/server/S in GLOB.machines) - if(S.server_id == text2num(href_list["access"]) || S.server_id == text2num(href_list["data"]) || S.server_id == text2num(href_list["transfer"])) - temp_server = S - break - if(href_list["access"]) - screen = 1 - for(var/obj/machinery/computer/rdconsole/C in GLOB.machines) - if(C.sync) - consoles += C - else if(href_list["data"]) - screen = 2 - else if(href_list["transfer"]) - screen = 3 - for(var/obj/machinery/r_n_d/server/S in GLOB.machines) - if(S == src) - continue - servers += S - - else if(href_list["upload_toggle"]) - var/num = text2num(href_list["upload_toggle"]) - if(num in temp_server.id_with_upload) - temp_server.id_with_upload -= num - else - temp_server.id_with_upload += num - - else if(href_list["download_toggle"]) - var/num = text2num(href_list["download_toggle"]) - if(num in temp_server.id_with_download) - temp_server.id_with_download -= num - else - temp_server.id_with_download += num - - else if(href_list["reset_tech"]) - var/choice = alert("Technology Data Reset", "Are you sure you want to reset this technology to its default data? Data lost cannot be recovered.", "Continue", "Cancel") - if(choice == "Continue") - for(var/I in temp_server.files.known_tech) - var/datum/tech/T = temp_server.files.known_tech[I] - if(T.id == href_list["reset_tech"]) - T.level = 1 - break - temp_server.files.RefreshResearch() - - else if(href_list["reset_design"]) - var/choice = alert("Design Data Deletion", "Are you sure you want to delete this design? Data lost cannot be recovered.", "Continue", "Cancel") - if(choice == "Continue") - for(var/I in temp_server.files.known_designs) - var/datum/design/D = temp_server.files.known_designs[I] - if(D.id == href_list["reset_design"]) - temp_server.files.known_designs -= D.id - break - temp_server.files.RefreshResearch() - - updateUsrDialog() - return - -/obj/machinery/computer/rdservercontrol/attack_hand(mob/user as mob) - if(stat & (BROKEN|NOPOWER)) - return - user.set_machine(src) - var/dat = "" - - switch(screen) - if(0) //Main Menu - dat += "Connected Servers:

        " - - for(var/obj/machinery/r_n_d/server/S in GLOB.machines) - if(istype(S, /obj/machinery/r_n_d/server/centcom) && !badmin) - continue - dat += "[S.name] || " - dat += "Access Rights | " - dat += "Data Management" - if(badmin) dat += " | Server-to-Server Transfer" - dat += "
        " - - if(1) //Access rights menu - dat += "[temp_server.name] Access Rights

        " - dat += "Consoles with Upload Access
        " - for(var/obj/machinery/computer/rdconsole/C in consoles) - var/turf/console_turf = get_turf(C) - dat += "* [console_turf.loc]" //FYI, these are all numeric ids, eventually. - if(C.id in temp_server.id_with_upload) - dat += " (Remove)
        " - else - dat += " (Add)
        " - dat += "Consoles with Download Access
        " - for(var/obj/machinery/computer/rdconsole/C in consoles) - var/turf/console_turf = get_turf(C) - dat += "* [console_turf.loc]" - if(C.id in temp_server.id_with_download) - dat += " (Remove)
        " - else - dat += " (Add)
        " - dat += "
        Main Menu" - - if(2) //Data Management menu - dat += "[temp_server.name] Data Management

        " - dat += "Known Technologies
        " - for(var/I in temp_server.files.known_tech) - var/datum/tech/T = temp_server.files.known_tech[I] - if(T.level <= 0) - continue - dat += "* [T.name] " - dat += "(Reset)
        " //FYI, these are all strings. - dat += "Known Designs
        " - for(var/I in temp_server.files.known_designs) - var/datum/design/D = temp_server.files.known_designs[I] - dat += "* [D.name] " - dat += "(Delete)
        " - dat += "
        Main Menu" - - if(3) //Server Data Transfer - dat += "[temp_server.name] Server to Server Transfer

        " - dat += "Send Data to what server?
        " - for(var/obj/machinery/r_n_d/server/S in servers) - dat += "[S.name] (Transfer)
        " - dat += "
        Main Menu" - user << browse("R&D Server Control
        [dat]", "window=server_control;size=575x400") - onclose(user, "server_control") - return - -/obj/machinery/computer/rdservercontrol/emag_act(user as mob) - if(!emagged) - playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1) - emagged = 1 - to_chat(user, "You you disable the security protocols") - src.updateUsrDialog() - -/obj/machinery/r_n_d/server/core - name = "Core R&D Server" - id_with_upload_string = "1;3" - id_with_download_string = "1;3" - server_id = 1 - plays_sound = 1 - -/obj/machinery/r_n_d/server/robotics - name = "Robotics and Mechanic R&D Server" - id_with_upload_string = "1;2;4" - id_with_download_string = "1;2;4" - server_id = 2 +/obj/machinery/r_n_d/server + name = "R&D Server" + icon = 'icons/obj/machines/research.dmi' + icon_state = "server" + var/datum/research/files + var/health = 100 + var/list/id_with_upload = list() //List of R&D consoles with upload to server access. + var/list/id_with_download = list() //List of R&D consoles with download from server access. + var/id_with_upload_string = "" //String versions for easy editing in map editor. + var/id_with_download_string = "" + var/server_id = 0 + var/heat_gen = 100 + var/heating_power = 40000 + var/delay = 10 + req_access = list(ACCESS_RD) //Only the R&D can change server settings. + var/plays_sound = 0 + +/obj/machinery/r_n_d/server/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/rdserver(null) + component_parts += new /obj/item/stock_parts/scanning_module(null) + component_parts += new /obj/item/stack/cable_coil(null,1) + component_parts += new /obj/item/stack/cable_coil(null,1) + RefreshParts() + initialize_serv(); //Agouri // fuck you agouri + +/obj/machinery/r_n_d/server/upgraded/New() + ..() + component_parts = list() + component_parts += new /obj/item/circuitboard/rdserver(null) + component_parts += new /obj/item/stock_parts/scanning_module/phasic(null) + component_parts += new /obj/item/stack/cable_coil(null,1) + component_parts += new /obj/item/stack/cable_coil(null,1) + RefreshParts() + +/obj/machinery/r_n_d/server/Destroy() + griefProtection() + return ..() + +/obj/machinery/r_n_d/server/RefreshParts() + var/tot_rating = 0 + for(var/obj/item/stock_parts/SP in src) + tot_rating += SP.rating + heat_gen /= max(1, tot_rating) + +/obj/machinery/r_n_d/server/proc/initialize_serv() + if(!files) + files = new /datum/research(src) + var/list/temp_list + if(!id_with_upload.len) + temp_list = list() + temp_list = splittext(id_with_upload_string, ";") + for(var/N in temp_list) + id_with_upload += text2num(N) + if(!id_with_download.len) + temp_list = list() + temp_list = splittext(id_with_download_string, ";") + for(var/N in temp_list) + id_with_download += text2num(N) + +/obj/machinery/r_n_d/server/process() + if(prob(3) && plays_sound) + playsound(loc, "computer_ambience", 50, 1) + + var/datum/gas_mixture/environment = loc.return_air() + switch(environment.temperature) + if(0 to T0C) + health = min(100, health + 1) + if(T0C to (T20C + 20)) + health = Clamp(health, 0, 100) + if((T20C + 20) to (T0C + 70)) + health = max(0, health - 1) + if(health <= 0) + /*griefProtection() This seems to get called twice before running any code that deletes/damages the server or it's files anwyay. + refreshParts and the hasReq procs that get called by this are laggy and do not need to be called by every server on the map every tick */ + var/updateRD = 0 + files.known_designs = list() + for(var/v in files.known_tech) + var/datum/tech/T = files.known_tech[v] + // Slowly decrease research if health drops below 0 + if(prob(1)) + updateRD++ + T.level-- + if(updateRD) + files.RefreshResearch() + if(delay) + delay-- + else + produce_heat(heat_gen) + delay = initial(delay) + +/obj/machinery/r_n_d/server/emp_act(severity) + griefProtection() + ..() + + +/obj/machinery/r_n_d/server/ex_act(severity) + griefProtection() + return ..() + +/obj/machinery/r_n_d/server/blob_act(obj/structure/blob/B) + griefProtection() + return ..() + +// Backup files to CentComm to help admins recover data after griefer attacks +/obj/machinery/r_n_d/server/proc/griefProtection() + for(var/obj/machinery/r_n_d/server/centcom/C in GLOB.machines) + files.push_data(C.files) + +/obj/machinery/r_n_d/server/proc/produce_heat(heat_amt) + if(!(stat & (NOPOWER|BROKEN))) // Blatantly stolen from space heater. + var/turf/simulated/L = loc + if(istype(L)) + var/datum/gas_mixture/env = L.return_air() + if(env.temperature < (heat_amt+T0C)) + + var/transfer_moles = 0.25 * env.total_moles() + + var/datum/gas_mixture/removed = env.remove(transfer_moles) + + if(removed) + + var/heat_capacity = removed.heat_capacity() + if(heat_capacity == 0 || heat_capacity == null) + heat_capacity = 1 + removed.temperature = min((removed.temperature*heat_capacity + heating_power)/heat_capacity, 1000) + + env.merge(removed) + air_update_turf() + +/obj/machinery/r_n_d/server/attackby(var/obj/item/O as obj, var/mob/user as mob, params) + if(disabled) + return + + if(shocked) + shock(user,50) + + if(istype(O, /obj/item/screwdriver)) + default_deconstruction_screwdriver(user, "server_o", "server", O) + return 1 + + if(exchange_parts(user, O)) + return 1 + + if(panel_open) + if(istype(O, /obj/item/crowbar)) + griefProtection() + default_deconstruction_crowbar(user, O) + return 1 + else + return ..() + +/obj/machinery/r_n_d/server/attack_hand(mob/user as mob) + if(disabled) + return + + if(shocked) + shock(user,50) + return + +/obj/machinery/r_n_d/server/centcom + name = "CentComm. Central R&D Database" + server_id = -1 + +/obj/machinery/r_n_d/server/centcom/Initialize() + ..() + var/list/no_id_servers = list() + var/list/server_ids = list() + for(var/obj/machinery/r_n_d/server/S in GLOB.machines) + switch(S.server_id) + if(-1) + continue + if(0) + no_id_servers += S + else + server_ids += S.server_id + + for(var/obj/machinery/r_n_d/server/S in no_id_servers) + var/num = 1 + while(!S.server_id) + if(num in server_ids) + num++ + else + S.server_id = num + server_ids += num + no_id_servers -= S + +/obj/machinery/r_n_d/server/centcom/process() + return PROCESS_KILL //don't need process() + + +/obj/machinery/computer/rdservercontrol + name = "\improper R&D server controller" + icon_screen = "rdcomp" + icon_keyboard = "rd_key" + light_color = LIGHT_COLOR_FADEDPURPLE + circuit = /obj/item/circuitboard/rdservercontrol + var/screen = 0 + var/obj/machinery/r_n_d/server/temp_server + var/list/servers = list() + var/list/consoles = list() + var/badmin = 0 + +/obj/machinery/computer/rdservercontrol/Topic(href, href_list) + if(..()) + return + + add_fingerprint(usr) + usr.set_machine(src) + if(!src.allowed(usr) && !emagged) + to_chat(usr, "You do not have the required access level") + return + + if(href_list["main"]) + screen = 0 + + else if(href_list["access"] || href_list["data"] || href_list["transfer"]) + temp_server = null + consoles = list() + servers = list() + for(var/obj/machinery/r_n_d/server/S in GLOB.machines) + if(S.server_id == text2num(href_list["access"]) || S.server_id == text2num(href_list["data"]) || S.server_id == text2num(href_list["transfer"])) + temp_server = S + break + if(href_list["access"]) + screen = 1 + for(var/obj/machinery/computer/rdconsole/C in GLOB.machines) + if(C.sync) + consoles += C + else if(href_list["data"]) + screen = 2 + else if(href_list["transfer"]) + screen = 3 + for(var/obj/machinery/r_n_d/server/S in GLOB.machines) + if(S == src) + continue + servers += S + + else if(href_list["upload_toggle"]) + var/num = text2num(href_list["upload_toggle"]) + if(num in temp_server.id_with_upload) + temp_server.id_with_upload -= num + else + temp_server.id_with_upload += num + + else if(href_list["download_toggle"]) + var/num = text2num(href_list["download_toggle"]) + if(num in temp_server.id_with_download) + temp_server.id_with_download -= num + else + temp_server.id_with_download += num + + else if(href_list["reset_tech"]) + var/choice = alert("Technology Data Reset", "Are you sure you want to reset this technology to its default data? Data lost cannot be recovered.", "Continue", "Cancel") + if(choice == "Continue") + for(var/I in temp_server.files.known_tech) + var/datum/tech/T = temp_server.files.known_tech[I] + if(T.id == href_list["reset_tech"]) + T.level = 1 + break + temp_server.files.RefreshResearch() + + else if(href_list["reset_design"]) + var/choice = alert("Design Data Deletion", "Are you sure you want to delete this design? Data lost cannot be recovered.", "Continue", "Cancel") + if(choice == "Continue") + for(var/I in temp_server.files.known_designs) + var/datum/design/D = temp_server.files.known_designs[I] + if(D.id == href_list["reset_design"]) + temp_server.files.known_designs -= D.id + break + temp_server.files.RefreshResearch() + + updateUsrDialog() + return + +/obj/machinery/computer/rdservercontrol/attack_hand(mob/user as mob) + if(stat & (BROKEN|NOPOWER)) + return + user.set_machine(src) + var/dat = "" + + switch(screen) + if(0) //Main Menu + dat += "Connected Servers:

        " + + for(var/obj/machinery/r_n_d/server/S in GLOB.machines) + if(istype(S, /obj/machinery/r_n_d/server/centcom) && !badmin) + continue + dat += "[S.name] || " + dat += "Access Rights | " + dat += "Data Management" + if(badmin) dat += " | Server-to-Server Transfer" + dat += "
        " + + if(1) //Access rights menu + dat += "[temp_server.name] Access Rights

        " + dat += "Consoles with Upload Access
        " + for(var/obj/machinery/computer/rdconsole/C in consoles) + var/turf/console_turf = get_turf(C) + dat += "* [console_turf.loc]" //FYI, these are all numeric ids, eventually. + if(C.id in temp_server.id_with_upload) + dat += " (Remove)
        " + else + dat += " (Add)
        " + dat += "Consoles with Download Access
        " + for(var/obj/machinery/computer/rdconsole/C in consoles) + var/turf/console_turf = get_turf(C) + dat += "* [console_turf.loc]" + if(C.id in temp_server.id_with_download) + dat += " (Remove)
        " + else + dat += " (Add)
        " + dat += "
        Main Menu" + + if(2) //Data Management menu + dat += "[temp_server.name] Data Management

        " + dat += "Known Technologies
        " + for(var/I in temp_server.files.known_tech) + var/datum/tech/T = temp_server.files.known_tech[I] + if(T.level <= 0) + continue + dat += "* [T.name] " + dat += "(Reset)
        " //FYI, these are all strings. + dat += "Known Designs
        " + for(var/I in temp_server.files.known_designs) + var/datum/design/D = temp_server.files.known_designs[I] + dat += "* [D.name] " + dat += "(Delete)
        " + dat += "
        Main Menu" + + if(3) //Server Data Transfer + dat += "[temp_server.name] Server to Server Transfer

        " + dat += "Send Data to what server?
        " + for(var/obj/machinery/r_n_d/server/S in servers) + dat += "[S.name] (Transfer)
        " + dat += "
        Main Menu" + user << browse("R&D Server Control
        [dat]", "window=server_control;size=575x400") + onclose(user, "server_control") + return + +/obj/machinery/computer/rdservercontrol/emag_act(user as mob) + if(!emagged) + playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1) + emagged = 1 + to_chat(user, "You you disable the security protocols") + src.updateUsrDialog() + +/obj/machinery/r_n_d/server/core + name = "Core R&D Server" + id_with_upload_string = "1;3" + id_with_download_string = "1;3" + server_id = 1 + plays_sound = 1 + +/obj/machinery/r_n_d/server/robotics + name = "Robotics and Mechanic R&D Server" + id_with_upload_string = "1;2;4" + id_with_download_string = "1;2;4" + server_id = 2 diff --git a/code/modules/research/xenobiology/xenobio_camera.dm b/code/modules/research/xenobiology/xenobio_camera.dm index dbb8f597905..746e0ece0e8 100644 --- a/code/modules/research/xenobiology/xenobio_camera.dm +++ b/code/modules/research/xenobiology/xenobio_camera.dm @@ -163,15 +163,20 @@ if(loaded) to_chat(user, "You fill [src] with the monkey cubes stored in [O]. [src] now has [monkeys] monkey cubes stored.") return - else if(ismultitool(O)) - var/obj/item/multitool/I = O - if(istype(I.buffer, /obj/machinery/monkey_recycler)) - to_chat(user, "You link [src] with [I.buffer] in [I] buffer.") - connected_recycler = I.buffer - connected_recycler.connected += src - return return ..() +/obj/machinery/computer/camera_advanced/xenobio/multitool_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(!I.multitool_check_buffer(user)) + return + var/obj/item/multitool/M = I + if(istype(M.buffer, /obj/machinery/monkey_recycler)) + M.set_multitool_buffer(user, src) + connected_recycler = M.buffer + connected_recycler.connected += src + /datum/action/innate/slime_place name = "Place Slimes" button_icon_state = "slime_down" diff --git a/code/modules/ruins/lavalandruin_code/animal_hospital.dm b/code/modules/ruins/lavalandruin_code/animal_hospital.dm index 62793fa4226..1cf21d5a890 100644 --- a/code/modules/ruins/lavalandruin_code/animal_hospital.dm +++ b/code/modules/ruins/lavalandruin_code/animal_hospital.dm @@ -15,4 +15,4 @@ /obj/effect/mob_spawn/human/doctor/alive/lavaland/Destroy() var/obj/structure/fluff/empty_sleeper/S = new(drop_location()) S.setDir(dir) - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/ruins/lavalandruin_code/ash_walker_den.dm b/code/modules/ruins/lavalandruin_code/ash_walker_den.dm index e95a5381b99..c0b8e0f5555 100644 --- a/code/modules/ruins/lavalandruin_code/ash_walker_den.dm +++ b/code/modules/ruins/lavalandruin_code/ash_walker_den.dm @@ -86,4 +86,4 @@ /datum/outfit/ashwalker name ="Ashwalker" head = /obj/item/clothing/head/helmet/gladiator - uniform = /obj/item/clothing/under/gladiator/ash_walker \ No newline at end of file + uniform = /obj/item/clothing/under/gladiator/ash_walker diff --git a/code/modules/ruins/lavalandruin_code/clown_planet.dm b/code/modules/ruins/lavalandruin_code/clown_planet.dm index 6121c40d2af..3105739126a 100644 --- a/code/modules/ruins/lavalandruin_code/clown_planet.dm +++ b/code/modules/ruins/lavalandruin_code/clown_planet.dm @@ -4,4 +4,4 @@ info = "If you dare not continue down this path of madness, escape can be found through the chute in this room." /obj/item/paper/crumpled/bloody/ruins/lavaland/clown_planet/hope - info = "Abandon hope, all ye who enter here." \ No newline at end of file + info = "Abandon hope, all ye who enter here." diff --git a/code/modules/ruins/lavalandruin_code/dead_ratvar.dm b/code/modules/ruins/lavalandruin_code/dead_ratvar.dm index 4c37572c989..0d56293f1bc 100644 --- a/code/modules/ruins/lavalandruin_code/dead_ratvar.dm +++ b/code/modules/ruins/lavalandruin_code/dead_ratvar.dm @@ -92,22 +92,26 @@ /obj/structure/clockwork/wall_gear/emp_act(severity) return +/obj/structure/clockwork/wall_gear/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(anchored) + to_chat(user, "[src] needs to be unsecured to disassemble it!") + return + if(!I.tool_use_check(user, 0)) + return + TOOL_ATTEMPT_DISMANTLE_MESSAGE + if(I.use_tool(src, user, 30, volume = I.tool_volume)) + TOOL_DISMANTLE_SUCCESS_MESSAGE + deconstruct(TRUE) + +/obj/structure/clockwork/wall_gear/wrench_act(mob/user, obj/item/I) + . = TRUE + if(!I.tool_use_check(user, 0)) + return + default_unfasten_wrench(user, I, 10) + /obj/structure/clockwork/wall_gear/attackby(obj/item/I, mob/user, params) - if(iswrench(I)) - default_unfasten_wrench(user, I, 10) - return 1 - else if(isscrewdriver(I)) - if(anchored) - to_chat(user, "[src] needs to be unsecured to disassemble it!") - else - var/obj/item/screwdriver/S = I - user.visible_message("[user] starts to disassemble [src].", "You start to disassemble [src]...") - if(do_after(user, 30 * S.toolspeed, target = src) && !anchored) - playsound(loc, S.usesound, 50, 1) - to_chat(user, "You disassemble [src].") - deconstruct(TRUE) - return 1 - else if(istype(I, /obj/item/stack/tile/brass)) + if(istype(I, /obj/item/stack/tile/brass)) var/obj/item/stack/tile/brass/W = I if(W.get_amount() < 1) to_chat(user, "You need one brass sheet to do this!") diff --git a/code/modules/ruins/lavalandruin_code/fountain_hall.dm b/code/modules/ruins/lavalandruin_code/fountain_hall.dm index 75e2b8d9aa3..26c8ebca7c6 100644 --- a/code/modules/ruins/lavalandruin_code/fountain_hall.dm +++ b/code/modules/ruins/lavalandruin_code/fountain_hall.dm @@ -46,4 +46,4 @@ if(last_process + time_between_uses > world.time) icon_state = "fountain" else - icon_state = "fountain-red" \ No newline at end of file + icon_state = "fountain-red" diff --git a/code/modules/ruins/lavalandruin_code/pizzaparty.dm b/code/modules/ruins/lavalandruin_code/pizzaparty.dm index 3201652c371..192a80e3e7f 100644 --- a/code/modules/ruins/lavalandruin_code/pizzaparty.dm +++ b/code/modules/ruins/lavalandruin_code/pizzaparty.dm @@ -8,4 +8,4 @@ /obj/structure/reagent_dispensers/water_cooler/pizzaparty name = "punch cooler" - reagent_id = "bacchus_blessing" \ No newline at end of file + reagent_id = "bacchus_blessing" diff --git a/code/modules/ruins/lavalandruin_code/puzzle.dm b/code/modules/ruins/lavalandruin_code/puzzle.dm index dcd72b44c64..0220498df5e 100644 --- a/code/modules/ruins/lavalandruin_code/puzzle.dm +++ b/code/modules/ruins/lavalandruin_code/puzzle.dm @@ -348,4 +348,4 @@ //Move them into random block var/obj/structure/puzzle_element/E = pick(cube.elements) prisoner.forceMove(E) - return TRUE \ No newline at end of file + return TRUE diff --git a/code/modules/ruins/lavalandruin_code/seed_vault.dm b/code/modules/ruins/lavalandruin_code/seed_vault.dm index db16c7b3a71..2c999a7ce29 100644 --- a/code/modules/ruins/lavalandruin_code/seed_vault.dm +++ b/code/modules/ruins/lavalandruin_code/seed_vault.dm @@ -30,4 +30,4 @@ /obj/effect/mob_spawn/human/seed_vault/Destroy() new/obj/structure/fluff/empty_terrarium(get_turf(src)) - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/ruins/lavalandruin_code/syndicate_base.dm b/code/modules/ruins/lavalandruin_code/syndicate_base.dm index 57a5ab5c7f1..33e4fb527ae 100644 --- a/code/modules/ruins/lavalandruin_code/syndicate_base.dm +++ b/code/modules/ruins/lavalandruin_code/syndicate_base.dm @@ -2,7 +2,7 @@ /obj/machinery/vending/syndichem name = "\improper SyndiChem" desc = "A vending machine full of grenades and grenade accessories. Sponsored by DonkCo(tm)." - req_access = list(access_syndicate) + req_access = list(ACCESS_SYNDICATE) products = list(/obj/item/stack/cable_coil/random = 5, /obj/item/assembly/igniter = 20, /obj/item/assembly/prox_sensor = 5, @@ -83,4 +83,4 @@ new /obj/item/gps/internal/lavaland_syndicate_base(src) /obj/item/gps/internal/lavaland_syndicate_base - gpstag = "Encrypted Signal" \ No newline at end of file + gpstag = "Encrypted Signal" diff --git a/code/modules/ruins/objects_and_mobs/gym.dm b/code/modules/ruins/objects_and_mobs/gym.dm index 69abf1a7e88..e2d593a82d6 100644 --- a/code/modules/ruins/objects_and_mobs/gym.dm +++ b/code/modules/ruins/objects_and_mobs/gym.dm @@ -90,4 +90,4 @@ sleep(3) animate(user, pixel_y = 2, time = 3) sleep(3) - cut_overlay(swole_overlay) \ No newline at end of file + cut_overlay(swole_overlay) diff --git a/code/modules/ruins/objects_and_mobs/necropolis_gate.dm b/code/modules/ruins/objects_and_mobs/necropolis_gate.dm index 5a65a536b46..d5f7a1e1aa2 100644 --- a/code/modules/ruins/objects_and_mobs/necropolis_gate.dm +++ b/code/modules/ruins/objects_and_mobs/necropolis_gate.dm @@ -407,4 +407,4 @@ GLOBAL_DATUM(necropolis_gate, /obj/structure/necropolis_gate/legion_gate) #undef STABLE #undef COLLAPSE_ON_CROSS #undef DESTROY_ON_CROSS -#undef UNIQUE_EFFECT \ No newline at end of file +#undef UNIQUE_EFFECT diff --git a/code/modules/security_levels/keycard authentication.dm b/code/modules/security_levels/keycard authentication.dm index e1e3104e643..99a04205117 100644 --- a/code/modules/security_levels/keycard authentication.dm +++ b/code/modules/security_levels/keycard authentication.dm @@ -22,7 +22,7 @@ active_power_usage = 6 power_channel = ENVIRON - req_access = list(access_keycard_auth) + req_access = list(ACCESS_KEYCARD_AUTH) resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF /obj/machinery/keycard_auth/attack_ai(mob/user as mob) @@ -99,7 +99,7 @@ if(href_list["reset"]) reset() if(href_list["ert"]) - ert_reason = input(usr, "Reason for ERT Call:", "", "") + ert_reason = stripped_input(usr, "Reason for ERT Call:", "", "") SSnanoui.update_uis(src) add_fingerprint(usr) @@ -165,9 +165,10 @@ feedback_inc("alert_keycard_auth_stationRevoke",1) if("Emergency Response Team") if(is_ert_blocked()) - to_chat(usr, "All Emergency Response Teams are dispatched and can not be called at this time.") + atom_say("All Emergency Response Teams are dispatched and can not be called at this time.") return - to_chat(usr, "ERT request transmitted.") + atom_say("ERT request transmitted!") + command_announcer.autosay("ERT request transmitted. Reason: [ert_reason]", name) print_centcom_report(ert_reason, station_time_timestamp() + " ERT Request") var/fullmin_count = 0 @@ -221,4 +222,4 @@ var/global/station_all_access = 0 D.emergency = 0 D.update_icon(0) minor_announcement.Announce("Access restrictions on all station airlocks have been re-added. Seek station AI or a colleague's assistance if you are stuck.") - station_all_access = 0 \ No newline at end of file + station_all_access = 0 diff --git a/code/modules/shuttle/README.md b/code/modules/shuttle/README.md index 9568131ba94..6a7168c1269 100644 --- a/code/modules/shuttle/README.md +++ b/code/modules/shuttle/README.md @@ -143,4 +143,4 @@ not yet created when `New()` is called. To fix this issue, all docking ports will not initialize automatically on `New()`. Instead, they are manually initialized by the shuttle controller when it is created, via -a proc called `initialize()`. \ No newline at end of file +a proc called `initialize()`. diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm index 5af2d472416..7f9eddc0df4 100644 --- a/code/modules/shuttle/emergency.dm +++ b/code/modules/shuttle/emergency.dm @@ -30,7 +30,7 @@ to_chat(user, "The access level of [W:registered_name]\'s card is not high enough. ") return - if(!(access_heads in W:access)) //doesn't have this access + if(!(ACCESS_HEADS in W:access)) //doesn't have this access to_chat(user, "The access level of [W:registered_name]\'s card is not high enough. ") return 0 diff --git a/code/modules/shuttle/ert.dm b/code/modules/shuttle/ert.dm index cbd007b32f9..1f664a69c8f 100644 --- a/code/modules/shuttle/ert.dm +++ b/code/modules/shuttle/ert.dm @@ -1,9 +1,10 @@ /obj/machinery/computer/shuttle/ert name = "specops shuttle console" - req_access = list(access_cent_general) + req_access = list(ACCESS_CENT_GENERAL) shuttleId = "specops" possible_destinations = "specops_home;specops_away;specops_custom" resistance_flags = INDESTRUCTIBLE + flags = NODECONSTRUCT /obj/machinery/computer/camera_advanced/shuttle_docker/ert name = "specops navigation computer" @@ -16,6 +17,7 @@ x_offset = 0 y_offset = 0 resistance_flags = INDESTRUCTIBLE + flags = NODECONSTRUCT access_tcomms = FALSE access_construction = FALSE - access_mining = FALSE \ No newline at end of file + access_mining = FALSE diff --git a/code/modules/shuttle/navigation_computer.dm b/code/modules/shuttle/navigation_computer.dm index 4ef9cfc55b0..c57cea9216e 100644 --- a/code/modules/shuttle/navigation_computer.dm +++ b/code/modules/shuttle/navigation_computer.dm @@ -32,9 +32,9 @@ jumpto_ports += list("nav_z1" = 1) if(access_tcomms) jumpto_ports += list("nav_z3" = 1) - if(access_construction) + if(ACCESS_CONSTRUCTION) jumpto_ports += list("nav_z4" = 1) - if(access_mining) + if(ACCESS_MINING) jumpto_ports += list("nav_z5" = 1) if(access_derelict) jumpto_ports += list("nav_z6" = 1) @@ -354,4 +354,4 @@ remote_eye.setLoc(T) to_chat(target, "Jumped to [selected]") else - playsound(console, 'sound/machines/terminal_prompt_deny.ogg', 25, 0) \ No newline at end of file + playsound(console, 'sound/machines/terminal_prompt_deny.ogg', 25, 0) diff --git a/code/modules/shuttle/on_move.dm b/code/modules/shuttle/on_move.dm index 0a11a4397ad..dbec1c611f7 100644 --- a/code/modules/shuttle/on_move.dm +++ b/code/modules/shuttle/on_move.dm @@ -72,4 +72,4 @@ return FALSE disconnect() LateInitialize() - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm index 820f752aa5d..36ad93eb28e 100644 --- a/code/modules/shuttle/shuttle.dm +++ b/code/modules/shuttle/shuttle.dm @@ -875,7 +875,7 @@ /obj/machinery/computer/shuttle/admin name = "admin shuttle console" - req_access = list(access_cent_general) + req_access = list(ACCESS_CENT_GENERAL) shuttleId = "admin" possible_destinations = "admin_home;admin_away;admin_custom" resistance_flags = INDESTRUCTIBLE @@ -900,7 +900,7 @@ resistance_flags = INDESTRUCTIBLE /obj/machinery/computer/shuttle/trade/sol - req_access = list(access_trade_sol) + req_access = list(ACCESS_TRADE_SOL) possible_destinations = "trade_sol_base;trade_dock" shuttleId = "trade_sol" @@ -942,4 +942,4 @@ T.color = color if(T.dir != dir) T.dir = dir - return T \ No newline at end of file + return T diff --git a/code/modules/shuttle/shuttle_rotate.dm b/code/modules/shuttle/shuttle_rotate.dm index bea581d4174..2adaad8e166 100644 --- a/code/modules/shuttle/shuttle_rotate.dm +++ b/code/modules/shuttle/shuttle_rotate.dm @@ -88,4 +88,4 @@ If ever any of these procs are useful for non-shuttles, rename it to proc/rotate //prevents shuttles attempting to rotate this since it messes up sprites /obj/machinery/gravity_generator/shuttleRotate(rotation, params) params = NONE - return ..() \ No newline at end of file + return ..() diff --git a/code/modules/shuttle/supply.dm b/code/modules/shuttle/supply.dm index 446b6466dc5..80094c876df 100644 --- a/code/modules/shuttle/supply.dm +++ b/code/modules/shuttle/supply.dm @@ -382,7 +382,7 @@ name = "Supply Shuttle Console" desc = "Used to order supplies." icon_screen = "supply" - req_access = list(access_cargo) + req_access = list(ACCESS_CARGO) circuit = /obj/item/circuitboard/supplycomp var/temp = null var/reqtime = 0 @@ -421,7 +421,6 @@ for(var/category in all_supply_groups) category_list.Add(list(list("name" = get_supply_group_name(category), "category" = category))) data["categories"] = category_list - var/cat = text2num(last_viewed_group) var/packs_list[0] for(var/set_name in SSshuttle.supply_packs) @@ -448,7 +447,6 @@ owned = 1 requests_list.Add(list(list("ordernum" = SO.ordernum, "supply_type" = SO.object.name, "orderedby" = SO.orderedby, "owned" = owned, "command1" = list("rreq" = SO.ordernum)))) data["requests"] = requests_list - var/orders_list[0] for(var/set_name in SSshuttle.shoppinglist) var/datum/supply_order/SO = set_name @@ -462,7 +460,6 @@ data["moving"] = SSshuttle.supply.mode != SHUTTLE_IDLE data["at_station"] = SSshuttle.supply.getDockedId() == "supply_home" data["timeleft"] = SSshuttle.supply.timeLeft(600) - return data /obj/machinery/computer/ordercomp/Topic(href, href_list) diff --git a/code/modules/shuttle/syndicate.dm b/code/modules/shuttle/syndicate.dm index 5c0be6a78a5..f9ec738b167 100644 --- a/code/modules/shuttle/syndicate.dm +++ b/code/modules/shuttle/syndicate.dm @@ -4,11 +4,12 @@ name = "syndicate shuttle terminal" icon_screen = "syndishuttle" icon_keyboard = "syndie_key" - req_access = list(access_syndicate) + req_access = list(ACCESS_SYNDICATE) circuit = /obj/item/circuitboard/shuttle/syndicate shuttleId = "syndicate" possible_destinations = "syndicate_away;syndicate_z5;syndicate_z3;syndicate_ne;syndicate_nw;syndicate_n;syndicate_se;syndicate_sw;syndicate_s;syndicate_custom" resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF + flags = NODECONSTRUCT var/challenge = FALSE var/moved = FALSE @@ -29,7 +30,7 @@ name = "syndicate assault pod control" icon = 'icons/obj/terminals.dmi' icon_state = "dorm_available" - req_access = list(access_syndicate) + req_access = list(ACCESS_SYNDICATE) circuit = /obj/item/circuitboard/shuttle/syndicate/drop_pod shuttleId = "steel_rain" possible_destinations = null @@ -46,7 +47,7 @@ desc = "Used to call and send the SST shuttle." icon_keyboard = "syndie_key" icon_screen = "syndishuttle" - req_access = list(access_syndicate) + req_access = list(ACCESS_SYNDICATE) shuttleId = "sst" possible_destinations = "sst_home;sst_away;sst_custom" resistance_flags = INDESTRUCTIBLE @@ -56,7 +57,7 @@ desc = "Used to call and send the SIT shuttle." icon_keyboard = "syndie_key" icon_screen = "syndishuttle" - req_access = list(access_syndicate) + req_access = list(ACCESS_SYNDICATE) shuttleId = "sit" possible_destinations = "sit_arrivals;sit_engshuttle;sit_away;sit_custom" resistance_flags = INDESTRUCTIBLE @@ -92,4 +93,4 @@ x_offset = 0 y_offset = 0 -#undef SYNDICATE_CHALLENGE_TIMER \ No newline at end of file +#undef SYNDICATE_CHALLENGE_TIMER diff --git a/code/modules/shuttle/vox.dm b/code/modules/shuttle/vox.dm index 3899a23dee1..6d67e87e415 100644 --- a/code/modules/shuttle/vox.dm +++ b/code/modules/shuttle/vox.dm @@ -1,9 +1,10 @@ /obj/machinery/computer/shuttle/vox name = "skipjack control console" - req_access = list(access_vox) + req_access = list(ACCESS_VOX) shuttleId = "skipjack" possible_destinations = "skipjack_away;skipjack_ne;skipjack_nw;skipjack_se;skipjack_sw;skipjack_z5;skipjack_custom" resistance_flags = INDESTRUCTIBLE + flags = NODECONSTRUCT /obj/machinery/computer/camera_advanced/shuttle_docker/vox name = "skipjack navigation computer" @@ -16,4 +17,5 @@ x_offset = -10 y_offset = -10 resistance_flags = INDESTRUCTIBLE + flags = NODECONSTRUCT access_derelict = TRUE diff --git a/code/modules/space_management/level_traits.dm b/code/modules/space_management/level_traits.dm index 39b584df64d..6fcd267105f 100644 --- a/code/modules/space_management/level_traits.dm +++ b/code/modules/space_management/level_traits.dm @@ -60,4 +60,4 @@ var/list/default_map_traits = MAP_TRANSITION_CONFIG /proc/level_name_to_num(name) var/datum/space_level/S = space_manager.get_zlev_by_name(name) - return S.zpos \ No newline at end of file + return S.zpos diff --git a/code/modules/space_management/space_chunk.dm b/code/modules/space_management/space_chunk.dm index b311dad0028..fada2527d1c 100644 --- a/code/modules/space_management/space_chunk.dm +++ b/code/modules/space_management/space_chunk.dm @@ -97,4 +97,4 @@ #undef BOTTOM_LEFT_CHUNK #undef BOTTOM_RIGHT_CHUNK #undef TOP_LEFT_CHUNK -#undef TOP_RIGHT_CHUNK \ No newline at end of file +#undef TOP_RIGHT_CHUNK diff --git a/code/modules/space_management/zlevel_manager.dm b/code/modules/space_management/zlevel_manager.dm index acb1cf5c0a0..e45aff76078 100644 --- a/code/modules/space_management/zlevel_manager.dm +++ b/code/modules/space_management/zlevel_manager.dm @@ -168,4 +168,4 @@ var/global/datum/zlev_manager/space_manager = new var/datum/space_level/heap/heap = z_list["[C.zpos]"] if(!istype(heap)) throw EXCEPTION("Attempted to free chunk at invalid z-level ([C.x],[C.y],[C.zpos]) [C.width]x[C.height]") - heap.free(C) \ No newline at end of file + heap.free(C) diff --git a/code/modules/spacepods/construction.dm b/code/modules/spacepods/construction.dm index a6a7ddb0927..be2998fc0a3 100644 --- a/code/modules/spacepods/construction.dm +++ b/code/modules/spacepods/construction.dm @@ -53,12 +53,12 @@ list( "desc" = "A crudely-wired pod frame.", state_prev = list( - "key" = /obj/item/wirecutters, + "key" = TOOL_WIRECUTTER, "vis_msg" = "{USER} cuts out the {HOLDER}'s wiring.", "self_msg" = "You remove the {HOLDER}'s wiring." ), state_next = list( - "key" = /obj/item/screwdriver, + "key" = TOOL_SCREWDRIVER, "vis_msg" = "{USER} adjusts the wiring.", "self_msg" = "You adjust the {HOLDER}'s wiring." ) @@ -67,7 +67,7 @@ list( "desc" = "A wired pod frame.", state_prev = list( - "key" = /obj/item/screwdriver, + "key" = TOOL_SCREWDRIVER, "vis_msg" = "{USER} unclips {HOLDER}'s wiring harnesses.", "self_msg" = "You unclip {HOLDER}'s wiring harnesses." ), @@ -82,7 +82,7 @@ list( "desc" = "A wired pod frame with a loose mainboard.", state_prev = list( - "key" = /obj/item/crowbar, + "key" = TOOL_CROWBAR, "vis_msg" = "{USER} pries out the mainboard.", "self_msg" = "You pry out the mainboard.", @@ -90,7 +90,7 @@ "amount" = 1 ), state_next = list( - "key" = /obj/item/screwdriver, + "key" = TOOL_SCREWDRIVER, "vis_msg" = "{USER} secures the mainboard.", "self_msg" = "You secure the mainboard." ) @@ -99,7 +99,7 @@ list( "desc" = "A wired pod frame with a secured mainboard.", state_prev = list( - "key" = /obj/item/screwdriver, + "key" = TOOL_SCREWDRIVER, "vis_msg" = "{USER} unsecures the mainboard.", "self_msg" = "You unscrew the mainboard from the {HOLDER}." ), @@ -115,7 +115,7 @@ list( "desc" = "A naked space pod with a loose core.", state_prev = list( - "key" = /obj/item/crowbar, + "key" = TOOL_CROWBAR, "vis_msg" = "{USER} delicately removes the core from the {HOLDER} with a crowbar.", "self_msg" = "You delicately remove the core from the {HOLDER} with a crowbar.", @@ -123,7 +123,7 @@ "amount" = 1 ), state_next = list( - "key" = /obj/item/wrench, + "key" = TOOL_WRENCH, "vis_msg" = "{USER} secures the core's bolts.", "self_msg" = "You secure the core's bolts." ) @@ -132,7 +132,7 @@ list( "desc" = "A naked space pod with an exposed core. How lewd.", state_prev = list( - "key" = /obj/item/wrench, + "key" = TOOL_WRENCH, "vis_msg" = "{USER} unsecures the {HOLDER}'s core.", "self_msg" = "You unsecure the {HOLDER}'s core." ), @@ -147,7 +147,7 @@ list( "desc" = "A space pod with loose bulkhead panelling exposed.", state_prev = list( - "key" = /obj/item/crowbar, + "key" = TOOL_CROWBAR, "vis_msg" = "{USER} pops the {HOLDER}'s bulkhead panelling loose.", "self_msg" = "You pop the {HOLDER}'s bulkhead panelling loose.", @@ -155,7 +155,7 @@ "amount" = 5, ), state_next = list( - "key" = /obj/item/wrench, + "key" = TOOL_WRENCH, "vis_msg" = "{USER} secures the {HOLDER}'s bulkhead panelling.", "self_msg" = "You secure the {HOLDER}'s bulkhead panelling." ) @@ -164,12 +164,12 @@ list( "desc" = "A space pod with unwelded bulkhead panelling exposed.", state_prev = list( - "key" = /obj/item/wrench, + "key" = TOOL_WRENCH, "vis_msg" = "{USER} unbolts the {HOLDER}'s bulkhead panelling.", "self_msg" = "You unbolt the {HOLDER}'s bulkhead panelling." ), state_next = list( - "key" = /obj/item/weldingtool, + "key" = TOOL_WELDER, "vis_msg" = "{USER} seals the {HOLDER}'s bulkhead panelling with a weld.", "self_msg" = "You seal the {HOLDER}'s bulkhead panelling with a weld." ) @@ -178,7 +178,7 @@ list( "desc" = "A space pod with sealed bulkhead panelling exposed.", state_prev = list( - "key" = /obj/item/weldingtool, + "key" = TOOL_WELDER, "vis_msg" = "{USER} cuts the {HOLDER}'s bulkhead panelling loose.", "self_msg" = "You cut the {HOLDER}'s bulkhead panelling loose." ), @@ -193,14 +193,14 @@ list( "desc" = "A space pod with unsecured armor.", state_prev = list( - "key" = /obj/item/crowbar, + "key" = TOOL_CROWBAR, "vis_msg" = "{USER} pries off {HOLDER}'s armor.", "self_msg" = "You pry off {HOLDER}'s armor.", "spawn" = /obj/item/pod_parts/armor, "amount" = 1 ), state_next = list( - "key" = /obj/item/wrench, + "key" = TOOL_WRENCH, "vis_msg" = "{USER} bolts down the {HOLDER}'s armor.", "self_msg" = "You bolt down the {HOLDER}'s armor." ) @@ -209,12 +209,12 @@ list( "desc" = "A space pod with unsecured armor.", state_prev = list( - "key" = /obj/item/wrench, + "key" = TOOL_WRENCH, "vis_msg" = "{USER} unsecures the {HOLDER}'s armor.", "self_msg" = "You unsecure the {HOLDER}'s armor." ), state_next = list( - "key" = /obj/item/weldingtool, + "key" = TOOL_WELDER, "vis_msg" = "{USER} welds the {HOLDER}'s armor.", "self_msg" = "You weld the {HOLDER}'s armor." ) @@ -225,4 +225,4 @@ spawn_result(mob/user as mob) ..() feedback_inc("spacepod_created",1) - return \ No newline at end of file + return diff --git a/code/modules/spacepods/equipment.dm b/code/modules/spacepods/equipment.dm index 8ca4d1be65c..5996b5c7603 100644 --- a/code/modules/spacepods/equipment.dm +++ b/code/modules/spacepods/equipment.dm @@ -156,16 +156,16 @@ icon_state = "pod_locator" enabled = 0 -/obj/item/spacepod_equipment/misc/tracker/attackby(obj/item/I as obj, mob/user as mob, params) - if(isscrewdriver(I)) - if(enabled) - enabled = 0 - user.show_message("You disable \the [src]'s power.") - return - enabled = 1 - user.show_message("You enable \the [src]'s power.") - else - return ..() +/obj/item/spacepod_equipment/misc/tracker/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(enabled) + enabled = 0 + user.show_message("You disable \the [src]'s power.") + return + enabled = 1 + user.show_message("You enable \the [src]'s power.") /* /////////////////////////////////////// diff --git a/code/modules/spacepods/lock_buster.dm b/code/modules/spacepods/lock_buster.dm index 75f5e4ee9db..8a83315ce3d 100644 --- a/code/modules/spacepods/lock_buster.dm +++ b/code/modules/spacepods/lock_buster.dm @@ -11,4 +11,4 @@ icon_state = "lock_buster_on" else icon_state = "lock_buster_off" - to_chat(usr, "You turn the [src] [on ? "on" : "off"].") \ No newline at end of file + to_chat(usr, "You turn the [src] [on ? "on" : "off"].") diff --git a/code/modules/spacepods/spacepod.dm b/code/modules/spacepods/spacepod.dm index 32239466b82..5b0be539578 100644 --- a/code/modules/spacepods/spacepod.dm +++ b/code/modules/spacepods/spacepod.dm @@ -312,106 +312,109 @@ to_chat(M, mymessage) /obj/spacepod/attackby(obj/item/W as obj, mob/user as mob, params) - if(user.a_intent == INTENT_HARM) - ..() - deal_damage(W.force) - else - if(iscrowbar(W)) - if(!equipment_system.lock_system || unlocked || hatch_open) - hatch_open = !hatch_open - playsound(loc, W.usesound, 50, 1) - to_chat(user, "You [hatch_open ? "open" : "close"] the maintenance hatch.") - else - to_chat(user, "The hatch is locked shut!") + if(istype(W, /obj/item/stock_parts/cell)) + if(!hatch_open) + to_chat(user, "The maintenance hatch is closed!") return - if(istype(W, /obj/item/stock_parts/cell)) - if(!hatch_open) - to_chat(user, "The maintenance hatch is closed!") - return - if(battery) - to_chat(user, "The pod already has a battery.") - return - to_chat(user, "You insert [W] into the pod.") - user.drop_item(W) - battery = W - W.forceMove(src) + if(battery) + to_chat(user, "The pod already has a battery.") return - if(istype(W, /obj/item/spacepod_equipment)) - if(!hatch_open) - to_chat(user, "The maintenance hatch is closed!") - return - if(!equipment_system) - to_chat(user, "The pod has no equipment datum, yell at the coders") - return - if(istype(W, /obj/item/spacepod_equipment/weaponry)) - add_equipment(user, W, "weapon_system") - return - if(istype(W, /obj/item/spacepod_equipment/misc)) - add_equipment(user, W, "misc_system") - return - if(istype(W, /obj/item/spacepod_equipment/cargo)) - add_equipment(user, W, "cargo_system") - return - if(istype(W, /obj/item/spacepod_equipment/sec_cargo)) - add_equipment(user, W, "sec_cargo_system") - return - if(istype(W, /obj/item/spacepod_equipment/lock)) - add_equipment(user, W, "lock_system") - return - - if(istype(W, /obj/item/spacepod_key) && istype(equipment_system.lock_system, /obj/item/spacepod_equipment/lock/keyed)) - var/obj/item/spacepod_key/key = W - if(key.id == equipment_system.lock_system.id) - lock_pod() - return - else - to_chat(user, "This is the wrong key!") - return - - if(istype(W, /obj/item/weldingtool)) - if(!hatch_open) - to_chat(user, "You must open the maintenance hatch before attempting repairs.") - return - var/obj/item/weldingtool/WT = W - if(!WT.isOn()) - to_chat(user, "The welder must be on for this task.") - return - if(health < initial(health)) - to_chat(user, "You start welding the spacepod...") - playsound(loc, W.usesound, 50, 1) - if(do_after(user, 20 * W.toolspeed, target = src)) - if(!src || !WT.remove_fuel(3, user)) return - repair_damage(10) - to_chat(user, "You mend some [pick("dents","bumps","damage")] with [WT]") - return - to_chat(user, "[src] is fully repaired!") + to_chat(user, "You insert [W] into the pod.") + user.drop_item(W) + battery = W + W.forceMove(src) + return + else if(istype(W, /obj/item/spacepod_equipment)) + if(!hatch_open) + to_chat(user, "The maintenance hatch is closed!") + return + if(!equipment_system) + to_chat(user, "The pod has no equipment datum, yell at the coders") + return + if(istype(W, /obj/item/spacepod_equipment/weaponry)) + add_equipment(user, W, "weapon_system") + return + if(istype(W, /obj/item/spacepod_equipment/misc)) + add_equipment(user, W, "misc_system") + return + if(istype(W, /obj/item/spacepod_equipment/cargo)) + add_equipment(user, W, "cargo_system") + return + if(istype(W, /obj/item/spacepod_equipment/sec_cargo)) + add_equipment(user, W, "sec_cargo_system") + return + if(istype(W, /obj/item/spacepod_equipment/lock)) + add_equipment(user, W, "lock_system") return - if(istype(W, /obj/item/lock_buster)) - var/obj/item/lock_buster/L = W - if(L.on && equipment_system.lock_system) - user.visible_message(user, "[user] is drilling through the [src]'s lock!", - "You start drilling through the [src]'s lock!") - if(do_after(user, 100 * W.toolspeed, target = src)) - QDEL_NULL(equipment_system.lock_system) - unlocked = TRUE - user.visible_message(user, "[user] has destroyed the [src]'s lock!", - "You destroy the [src]'s lock!") - else - user.visible_message(user, "[user] fails to break through the [src]'s lock!", - "You were unable to break through the [src]'s lock!") - return - if(L.on && unlocked == FALSE) //The buster is on, we don't have a lock system, and the pod is still somehow locked, unlocking. + else if(istype(W, /obj/item/spacepod_key) && istype(equipment_system.lock_system, /obj/item/spacepod_equipment/lock/keyed)) + var/obj/item/spacepod_key/key = W + if(key.id == equipment_system.lock_system.id) + lock_pod() + return + else + to_chat(user, "This is the wrong key!") + return + + else if(istype(W, /obj/item/lock_buster)) + var/obj/item/lock_buster/L = W + if(L.on && equipment_system.lock_system) + user.visible_message(user, "[user] is drilling through the [src]'s lock!", + "You start drilling through the [src]'s lock!") + if(do_after(user, 100 * W.toolspeed, target = src)) + QDEL_NULL(equipment_system.lock_system) unlocked = TRUE - user.visible_message(user, "[user] repairs [src]'s doors with [L].", - "You repair [src]'s doors with [L].") - to_chat(user, "Turn the [L] on first.") + user.visible_message(user, "[user] has destroyed the [src]'s lock!", + "You destroy the [src]'s lock!") + else + user.visible_message(user, "[user] fails to break through the [src]'s lock!", + "You were unable to break through the [src]'s lock!") return + if(L.on && unlocked == FALSE) //The buster is on, we don't have a lock system, and the pod is still somehow locked, unlocking. + unlocked = TRUE + user.visible_message(user, "[user] repairs [src]'s doors with [L].", + "You repair [src]'s doors with [L].") + to_chat(user, "Turn the [L] on first.") + return - if(cargo_hold.storage_slots > 0 && !hatch_open && unlocked) // must be the last option as all items not listed prior will be stored - cargo_hold.attackby(W, user, params) + else if(cargo_hold.storage_slots > 0 && !hatch_open && unlocked) // must be the last option as all items not listed prior will be stored + cargo_hold.attackby(W, user, params) + else + if(user.a_intent == INTENT_HARM) + deal_damage(W.force) + return ..() -obj/spacepod/proc/add_equipment(mob/user, var/obj/item/spacepod_equipment/SPE, var/slot) +/obj/spacepod/crowbar_act(mob/user, obj/item/I) + if(user.a_intent == INTENT_HARM) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(!equipment_system.lock_system || unlocked || hatch_open) + hatch_open = !hatch_open + to_chat(user, "You [hatch_open ? "open" : "close"] the maintenance hatch.") + else + to_chat(user, "The hatch is locked shut!") + +/obj/spacepod/welder_act(mob/user, obj/item/I) + if(user.a_intent == INTENT_HARM) + return + . = TRUE + if(!hatch_open) + to_chat(user, "You must open the maintenance hatch before attempting repairs.") + return + if(health >= initial(health)) + to_chat(user, "[src] is fully repaired!") + return + if(!I.tool_use_check(user, 0)) + return + to_chat(user, "You start welding the spacepod...") + if(I.use_tool(src, user, 20, 3, volume = I.tool_volume)) + repair_damage(10) + to_chat(user, "You mend some [pick("dents","bumps","damage")] with [I]") + + +/obj/spacepod/proc/add_equipment(mob/user, var/obj/item/spacepod_equipment/SPE, var/slot) if(equipment_system.vars[slot]) to_chat(user, "The pod already has a [slot], remove it first.") return diff --git a/code/modules/station_goals/dna_vault.dm b/code/modules/station_goals/dna_vault.dm index a55657fc724..5b07d27361e 100644 --- a/code/modules/station_goals/dna_vault.dm +++ b/code/modules/station_goals/dna_vault.dm @@ -323,4 +323,4 @@ var/list/non_simple_animals = typecacheof(list(/mob/living/carbon/human/monkey,/ #undef VAULT_STUNTIME #undef VAULT_ARMOUR #undef VAULT_SPEED -#undef VAULT_QUICK \ No newline at end of file +#undef VAULT_QUICK diff --git a/code/modules/station_goals/station_goal.dm b/code/modules/station_goals/station_goal.dm index 90d4defa26c..d6be4540a01 100644 --- a/code/modules/station_goals/station_goal.dm +++ b/code/modules/station_goals/station_goal.dm @@ -46,4 +46,4 @@ on_report() send_report() else if(href_list["remove"]) - qdel(src) \ No newline at end of file + qdel(src) diff --git a/code/modules/surgery/cavity_implant.dm b/code/modules/surgery/cavity_implant.dm index e86ab317fce..d7b7621e9c0 100644 --- a/code/modules/surgery/cavity_implant.dm +++ b/code/modules/surgery/cavity_implant.dm @@ -203,4 +203,4 @@ return 1 else to_chat(user, "You don't find anything in [target]'s [target_zone].") - return 0 \ No newline at end of file + return 0 diff --git a/code/modules/surgery/core_removal.dm b/code/modules/surgery/core_removal.dm index 21458720978..a54aa25d228 100644 --- a/code/modules/surgery/core_removal.dm +++ b/code/modules/surgery/core_removal.dm @@ -58,4 +58,4 @@ /datum/surgery_step/slime/extract_core/fail_step(mob/living/user, mob/living/simple_animal/slime/target, target_zone, obj/item/tool) user.visible_message(" [user]'s hand slips, tearing [target]'s flesh with \the [tool]!", \ " Your hand slips, tearing [target]'s flesh with \the [tool]!") - return FALSE \ No newline at end of file + return FALSE diff --git a/code/modules/surgery/dental_implant.dm b/code/modules/surgery/dental_implant.dm index 5f2b071591f..dece5826719 100644 --- a/code/modules/surgery/dental_implant.dm +++ b/code/modules/surgery/dental_implant.dm @@ -51,8 +51,8 @@ to_chat(owner, "You grit your teeth and burst the implanted [target]!") add_attack_logs(owner, owner, "Swallowed implanted [target]") if(target.reagents.total_volume) - target.reagents.reaction(owner, INGEST) + target.reagents.reaction(owner, REAGENT_INGEST) target.reagents.trans_to(owner, target.reagents.total_volume) Remove(owner) qdel(target) - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/surgery/implant_removal.dm b/code/modules/surgery/implant_removal.dm index 48895a3378a..078d0ec1384 100644 --- a/code/modules/surgery/implant_removal.dm +++ b/code/modules/surgery/implant_removal.dm @@ -76,4 +76,4 @@ else user.visible_message(" [user] could not find anything inside [target]'s [affected.name], and pulls \the [tool] out.", \ "You could not find anything inside [target]'s [affected.name].") - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/surgery/limb_augmentation.dm b/code/modules/surgery/limb_augmentation.dm index bec73e6e69b..561257c2595 100644 --- a/code/modules/surgery/limb_augmentation.dm +++ b/code/modules/surgery/limb_augmentation.dm @@ -54,4 +54,4 @@ affected.open = 0 affected.germ_level = 0 - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/surgery/organs/augments_arms.dm b/code/modules/surgery/organs/augments_arms.dm index 840b2ed5749..6215bbe88bc 100644 --- a/code/modules/surgery/organs/augments_arms.dm +++ b/code/modules/surgery/organs/augments_arms.dm @@ -33,17 +33,18 @@ . = ..() . += "[src] is assembled in the [parent_organ == "r_arm" ? "right" : "left"] arm configuration. You can use a screwdriver to reassemble it." -/obj/item/organ/internal/cyberimp/arm/attackby(obj/item/I, mob/user, params) - if(isscrewdriver(I)) - if(parent_organ == "r_arm") - parent_organ = "l_arm" - else - parent_organ = "r_arm" - slot = parent_organ + "_device" - to_chat(user, "You modify [src] to be installed on the [parent_organ == "r_arm" ? "right" : "left"] arm.") - update_icon() +/obj/item/organ/internal/cyberimp/arm/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(parent_organ == "r_arm") + parent_organ = "l_arm" else - return ..() + parent_organ = "r_arm" + slot = parent_organ + "_device" + to_chat(user, "You modify [src] to be installed on the [parent_organ == "r_arm" ? "right" : "left"] arm.") + update_icon() + /obj/item/organ/internal/cyberimp/arm/remove(mob/living/carbon/M, special = 0) Retract() diff --git a/code/modules/surgery/organs/augments_eyes.dm b/code/modules/surgery/organs/augments_eyes.dm index 463911861c2..80dceea5048 100644 --- a/code/modules/surgery/organs/augments_eyes.dm +++ b/code/modules/surgery/organs/augments_eyes.dm @@ -12,12 +12,11 @@ var/see_invisible = 0 var/lighting_alpha - var/eye_colour = "#000000" + var/eye_colour = "#000000" // Should never be null var/old_eye_colour = "#000000" var/flash_protect = 0 var/aug_message = "Your vision is augmented!" - /obj/item/organ/internal/cyberimp/eyes/insert(var/mob/living/carbon/M, var/special = 0) ..() var/mob/living/carbon/human/H = M diff --git a/code/modules/surgery/organs/augments_internal.dm b/code/modules/surgery/organs/augments_internal.dm index e9626a6b76d..fab2d0fd8a4 100644 --- a/code/modules/surgery/organs/augments_internal.dm +++ b/code/modules/surgery/organs/augments_internal.dm @@ -39,9 +39,9 @@ /obj/item/organ/internal/cyberimp/brain/anti_drop name = "Anti-drop implant" desc = "This cybernetic brain implant will allow you to force your hand muscles to contract, preventing item dropping. Twitch ear to toggle." - var/active = 0 - var/l_hand_ignore = 0 - var/r_hand_ignore = 0 + var/active = FALSE + var/l_hand_ignore = FALSE + var/r_hand_ignore = FALSE var/obj/item/l_hand_obj = null var/obj/item/r_hand_obj = null implant_color = "#DE7E00" @@ -56,17 +56,17 @@ r_hand_obj = owner.r_hand if(l_hand_obj) if(owner.l_hand.flags & NODROP) - l_hand_ignore = 1 + l_hand_ignore = TRUE else owner.l_hand.flags |= NODROP - l_hand_ignore = 0 + l_hand_ignore = FALSE if(r_hand_obj) if(owner.r_hand.flags & NODROP) - r_hand_ignore = 1 + r_hand_ignore = TRUE else owner.r_hand.flags |= NODROP - r_hand_ignore = 0 + r_hand_ignore = FALSE if(!l_hand_obj && !r_hand_obj) to_chat(owner, "You are not holding any items, your hands relax...") @@ -102,12 +102,15 @@ A = pick(oview(range)) L_item.throw_at(A, range, 2) to_chat(owner, "Your left arm spasms and throws the [L_item.name]!") + l_hand_obj = null if(R_item) A = pick(oview(range)) R_item.throw_at(A, range, 2) to_chat(owner, "Your right arm spasms and throws the [R_item.name]!") + r_hand_obj = null /obj/item/organ/internal/cyberimp/brain/anti_drop/proc/release_items() + active = FALSE if(!l_hand_ignore && l_hand_obj in owner.contents) l_hand_obj.flags ^= NODROP if(!r_hand_ignore && r_hand_obj in owner.contents) diff --git a/code/modules/surgery/organs/blood.dm b/code/modules/surgery/organs/blood.dm index 1d4bb3a9876..63a7f8a3590 100644 --- a/code/modules/surgery/organs/blood.dm +++ b/code/modules/surgery/organs/blood.dm @@ -70,13 +70,15 @@ if(BP.internal_bleeding) internal_bleeding_rate += 0.5 - bleed_rate = max(bleed_rate - 0.5, temp_bleed)//if no wounds, other bleed effects (heparin) naturally decreases + bleed_rate = max(bleed_rate - 0.5, temp_bleed)//if no wounds, other bleed effects naturally decreases + + var/additional_bleed = round(Clamp((reagents.get_reagent_amount("heparin") / 10), 0, 2), 1) //Heparin worsens existing bleeding if(internal_bleeding_rate && !(status_flags & FAKEDEATH)) - bleed_internal(internal_bleeding_rate) + bleed_internal(internal_bleeding_rate + additional_bleed) if(bleed_rate && !bleedsuppress && !(status_flags & FAKEDEATH)) - bleed(bleed_rate) + bleed(bleed_rate + additional_bleed) //Makes a blood drop, leaking amt units of blood from the mob /mob/living/carbon/proc/bleed(amt) diff --git a/code/modules/surgery/organs/eyes.dm b/code/modules/surgery/organs/eyes.dm index fba2df57e8a..60f4c22743d 100644 --- a/code/modules/surgery/organs/eyes.dm +++ b/code/modules/surgery/organs/eyes.dm @@ -5,7 +5,7 @@ organ_tag = "eyes" parent_organ = "head" slot = "eyes" - var/eye_colour = "#000000" + var/eye_colour = "#000000" // Should never be null var/list/colourmatrix = null var/list/colourblind_matrix = MATRIX_GREYSCALE //Special colourblindness parameters. By default, it's black-and-white. var/list/replace_colours = LIST_GREYSCALE_REPLACE @@ -78,4 +78,4 @@ icon_state = "eyes-prosthetic" desc = "An electronic device designed to mimic the functions of a pair of human eyes. It has no benefits over organic eyes, but is easy to produce." origin_tech = "biotech=4" - status = ORGAN_ROBOT \ No newline at end of file + status = ORGAN_ROBOT diff --git a/code/modules/surgery/organs/heart.dm b/code/modules/surgery/organs/heart.dm index 987315ac13b..9269b2340af 100644 --- a/code/modules/surgery/organs/heart.dm +++ b/code/modules/surgery/organs/heart.dm @@ -267,4 +267,4 @@ owner.adjustFireLoss(numMid) if(prob(numLow)) to_chat(owner, "Your [name] lurches awkwardly!") - owner.ForceContractDisease(new /datum/disease/critical/heart_failure(0)) \ No newline at end of file + owner.ForceContractDisease(new /datum/disease/critical/heart_failure(0)) diff --git a/code/modules/surgery/organs/helpers.dm b/code/modules/surgery/organs/helpers.dm index 91bba8d4a1b..ca2698f2ad4 100644 --- a/code/modules/surgery/organs/helpers.dm +++ b/code/modules/surgery/organs/helpers.dm @@ -122,4 +122,4 @@ for(var/zone in full) if(has_organ(zone)) full -= zone - return full \ No newline at end of file + return full diff --git a/code/modules/surgery/organs/kidneys.dm b/code/modules/surgery/organs/kidneys.dm index 26b59191a7c..9d3048beaba 100644 --- a/code/modules/surgery/organs/kidneys.dm +++ b/code/modules/surgery/organs/kidneys.dm @@ -22,4 +22,4 @@ icon_state = "kidneys-c" desc = "An electronic device designed to mimic the functions of human kidneys. It has no benefits over a pair of organic kidneys, but is easy to produce." origin_tech = "biotech=4" - status = ORGAN_ROBOT \ No newline at end of file + status = ORGAN_ROBOT diff --git a/code/modules/surgery/organs/liver.dm b/code/modules/surgery/organs/liver.dm index ba3112b6392..799b8e8a6ab 100644 --- a/code/modules/surgery/organs/liver.dm +++ b/code/modules/surgery/organs/liver.dm @@ -55,4 +55,4 @@ icon_state = "liver-c" desc = "An electronic device designed to mimic the functions of a human liver. It has no benefits over an organic liver, but is easy to produce." origin_tech = "biotech=4" - status = ORGAN_ROBOT \ No newline at end of file + status = ORGAN_ROBOT diff --git a/code/modules/surgery/organs/lungs.dm b/code/modules/surgery/organs/lungs.dm index 47fa74969d0..782bcbe6ca6 100644 --- a/code/modules/surgery/organs/lungs.dm +++ b/code/modules/surgery/organs/lungs.dm @@ -355,4 +355,4 @@ cold_level_1_threshold = 200 cold_level_2_threshold = 140 - cold_level_3_threshold = 100 \ No newline at end of file + cold_level_3_threshold = 100 diff --git a/code/modules/surgery/organs/mmi_holder.dm b/code/modules/surgery/organs/mmi_holder.dm index d4228fb9625..bd885471ff1 100644 --- a/code/modules/surgery/organs/mmi_holder.dm +++ b/code/modules/surgery/organs/mmi_holder.dm @@ -33,4 +33,4 @@ desc = stored_mmi.desc icon = stored_mmi.icon icon_state = stored_mmi.icon_state - set_dna(stored_mmi.brainmob.dna) \ No newline at end of file + set_dna(stored_mmi.brainmob.dna) diff --git a/code/modules/surgery/organs/organ_external.dm b/code/modules/surgery/organs/organ_external.dm index 1fde13e246e..305c08ff17e 100644 --- a/code/modules/surgery/organs/organ_external.dm +++ b/code/modules/surgery/organs/organ_external.dm @@ -404,7 +404,7 @@ Note that amputating the affected organ does in fact remove the infection from t fracture() /obj/item/organ/external/proc/check_for_internal_bleeding(damage) - if(NO_BLOOD in owner.dna.species.species_traits) + if(owner && NO_BLOOD in owner.dna.species.species_traits) return var/local_damage = brute_dam + damage if(damage > 15 && local_damage > 30 && prob(damage) && !is_robotic()) diff --git a/code/modules/surgery/organs/pain.dm b/code/modules/surgery/organs/pain.dm index 4863bd63901..91e4a2f6781 100644 --- a/code/modules/surgery/organs/pain.dm +++ b/code/modules/surgery/organs/pain.dm @@ -81,4 +81,4 @@ mob/living/carbon/human/proc/handle_pain() continue if(I.damage > 2 && prob(2)) var/obj/item/organ/external/parent = get_organ(I.parent_organ) - custom_pain("You feel a sharp pain in your [parent.limb_name]") \ No newline at end of file + custom_pain("You feel a sharp pain in your [parent.limb_name]") diff --git a/code/modules/surgery/organs/robolimbs.dm b/code/modules/surgery/organs/robolimbs.dm index d3c297bc0c7..2e25a1c1998 100644 --- a/code/modules/surgery/organs/robolimbs.dm +++ b/code/modules/surgery/organs/robolimbs.dm @@ -153,4 +153,4 @@ var/global/datum/robolimb/basic_robolimb parts = list("head") is_monitor = 1 selectable = 0 - has_subtypes = null \ No newline at end of file + has_subtypes = null diff --git a/code/modules/surgery/organs/subtypes/abductor.dm b/code/modules/surgery/organs/subtypes/abductor.dm index b499135417e..7fabbd96ae1 100644 --- a/code/modules/surgery/organs/subtypes/abductor.dm +++ b/code/modules/surgery/organs/subtypes/abductor.dm @@ -4,4 +4,4 @@ /obj/item/organ/internal/eyes/abductor name = "abductor eyeballs" - see_in_dark = 3 \ No newline at end of file + see_in_dark = 3 diff --git a/code/modules/surgery/organs/subtypes/diona.dm b/code/modules/surgery/organs/subtypes/diona.dm index 0e76c8fe20b..3317aca4648 100644 --- a/code/modules/surgery/organs/subtypes/diona.dm +++ b/code/modules/surgery/organs/subtypes/diona.dm @@ -102,4 +102,4 @@ name = "nutrient vessel" icon = 'icons/mob/alien.dmi' icon_state = "claw" - alcohol_intensity = 0.5 \ No newline at end of file + alcohol_intensity = 0.5 diff --git a/code/modules/surgery/organs/subtypes/drask.dm b/code/modules/surgery/organs/subtypes/drask.dm index 49b11d096b6..201c05b83e3 100644 --- a/code/modules/surgery/organs/subtypes/drask.dm +++ b/code/modules/surgery/organs/subtypes/drask.dm @@ -26,4 +26,4 @@ name = "drask eyeballs" icon = 'icons/obj/species_organs/drask.dmi' desc = "Drask eyes. They look even stranger disembodied" - see_in_dark = 5 \ No newline at end of file + see_in_dark = 5 diff --git a/code/modules/surgery/organs/subtypes/grey.dm b/code/modules/surgery/organs/subtypes/grey.dm index 6e4a0e3eafd..15b7fea2264 100644 --- a/code/modules/surgery/organs/subtypes/grey.dm +++ b/code/modules/surgery/organs/subtypes/grey.dm @@ -35,4 +35,4 @@ /obj/item/organ/internal/kidneys/grey name = "grey kidneys" - icon = 'icons/obj/species_organs/grey.dmi' \ No newline at end of file + icon = 'icons/obj/species_organs/grey.dmi' diff --git a/code/modules/surgery/organs/subtypes/kidan.dm b/code/modules/surgery/organs/subtypes/kidan.dm index b87531ac337..6407917bcae 100644 --- a/code/modules/surgery/organs/subtypes/kidan.dm +++ b/code/modules/surgery/organs/subtypes/kidan.dm @@ -135,4 +135,4 @@ #undef KIDAN_LANTERN_HUNGERCOST #undef KIDAN_LANTERN_MINHUNGER -#undef KIDAN_LANTERN_LIGHT \ No newline at end of file +#undef KIDAN_LANTERN_LIGHT diff --git a/code/modules/surgery/organs/subtypes/machine.dm b/code/modules/surgery/organs/subtypes/machine.dm index 837e9b5ffb7..52cad684e11 100644 --- a/code/modules/surgery/organs/subtypes/machine.dm +++ b/code/modules/surgery/organs/subtypes/machine.dm @@ -145,4 +145,4 @@ /obj/item/organ/internal/ears/microphone/remove(mob/living/user, special = FALSE) if(!special) to_chat(owner, "BZZZZZZZZZZZZZZT! Microphone error!") - . = ..() \ No newline at end of file + . = ..() diff --git a/code/modules/surgery/organs/subtypes/nucleation.dm b/code/modules/surgery/organs/subtypes/nucleation.dm index 3be564fc800..bac821f7f6c 100644 --- a/code/modules/surgery/organs/subtypes/nucleation.dm +++ b/code/modules/surgery/organs/subtypes/nucleation.dm @@ -31,4 +31,4 @@ /obj/item/organ/internal/brain/crystal name = "crystallized brain" icon_state = "crystal-brain" - organ_tag = "crystallized brain" \ No newline at end of file + organ_tag = "crystallized brain" diff --git a/code/modules/surgery/organs/subtypes/plasmaman.dm b/code/modules/surgery/organs/subtypes/plasmaman.dm index 3fd59189fb0..4de19d0950e 100644 --- a/code/modules/surgery/organs/subtypes/plasmaman.dm +++ b/code/modules/surgery/organs/subtypes/plasmaman.dm @@ -18,4 +18,4 @@ /obj/item/organ/internal/kidneys/plasmaman name = "plasmaman kidneys" - icon = 'icons/obj/species_organs/plasmaman.dmi' \ No newline at end of file + icon = 'icons/obj/species_organs/plasmaman.dmi' diff --git a/code/modules/surgery/organs/subtypes/shadow.dm b/code/modules/surgery/organs/subtypes/shadow.dm index 56bec1a4a7e..d147feb0317 100644 --- a/code/modules/surgery/organs/subtypes/shadow.dm +++ b/code/modules/surgery/organs/subtypes/shadow.dm @@ -1,3 +1,3 @@ /obj/item/organ/internal/eyes/shadow name = "dark orbs" - see_in_dark = 8 \ No newline at end of file + see_in_dark = 8 diff --git a/code/modules/surgery/organs/subtypes/skrell.dm b/code/modules/surgery/organs/subtypes/skrell.dm index 3c1e3cd40ce..8a56956777f 100644 --- a/code/modules/surgery/organs/subtypes/skrell.dm +++ b/code/modules/surgery/organs/subtypes/skrell.dm @@ -87,4 +87,4 @@ /obj/item/organ/internal/eyes/skrell name = "skrell eyeballs" - icon = 'icons/obj/species_organs/skrell.dmi' \ No newline at end of file + icon = 'icons/obj/species_organs/skrell.dmi' diff --git a/code/modules/surgery/organs/subtypes/slime.dm b/code/modules/surgery/organs/subtypes/slime.dm index 14c89aded63..752375047fe 100644 --- a/code/modules/surgery/organs/subtypes/slime.dm +++ b/code/modules/surgery/organs/subtypes/slime.dm @@ -12,4 +12,4 @@ icon = 'icons/obj/species_organs/slime.dmi' name = "slime lungs" icon_state = "lungs" - desc = "This is a slime's gas exchange membrane, this membrane used for oxygen intake and gas exchange. These seem to work similar to lungs." \ No newline at end of file + desc = "This is a slime's gas exchange membrane, this membrane used for oxygen intake and gas exchange. These seem to work similar to lungs." diff --git a/code/modules/surgery/organs/subtypes/tajaran.dm b/code/modules/surgery/organs/subtypes/tajaran.dm index 99598a80fb1..e8d87b55b89 100644 --- a/code/modules/surgery/organs/subtypes/tajaran.dm +++ b/code/modules/surgery/organs/subtypes/tajaran.dm @@ -32,4 +32,4 @@ /obj/item/organ/internal/kidneys/tajaran name = "tajaran kidneys" - icon = 'icons/obj/species_organs/tajaran.dmi' \ No newline at end of file + icon = 'icons/obj/species_organs/tajaran.dmi' diff --git a/code/modules/surgery/organs/subtypes/unathi.dm b/code/modules/surgery/organs/subtypes/unathi.dm index a612bc7f253..15409b85d7e 100644 --- a/code/modules/surgery/organs/subtypes/unathi.dm +++ b/code/modules/surgery/organs/subtypes/unathi.dm @@ -27,4 +27,4 @@ /obj/item/organ/internal/kidneys/unathi name = "unathi kidneys" - icon = 'icons/obj/species_organs/unathi.dmi' \ No newline at end of file + icon = 'icons/obj/species_organs/unathi.dmi' diff --git a/code/modules/surgery/organs/subtypes/unbreakable.dm b/code/modules/surgery/organs/subtypes/unbreakable.dm index 245629ba26e..b4fca2150fc 100644 --- a/code/modules/surgery/organs/subtypes/unbreakable.dm +++ b/code/modules/surgery/organs/subtypes/unbreakable.dm @@ -65,4 +65,4 @@ cannot_amputate = TRUE /obj/item/organ/external/head/unbreakable/sturdy - cannot_amputate = TRUE \ No newline at end of file + cannot_amputate = TRUE diff --git a/code/modules/surgery/organs/subtypes/vox.dm b/code/modules/surgery/organs/subtypes/vox.dm index e7366c748c2..1ab1eef7238 100644 --- a/code/modules/surgery/organs/subtypes/vox.dm +++ b/code/modules/surgery/organs/subtypes/vox.dm @@ -26,4 +26,4 @@ /obj/item/organ/internal/kidneys/vox name = "vox kidneys" icon = 'icons/obj/species_organs/vox.dmi' - sterile = TRUE \ No newline at end of file + sterile = TRUE diff --git a/code/modules/surgery/organs/subtypes/vulpkanin.dm b/code/modules/surgery/organs/subtypes/vulpkanin.dm index 820b152c43f..a2815d32dc6 100644 --- a/code/modules/surgery/organs/subtypes/vulpkanin.dm +++ b/code/modules/surgery/organs/subtypes/vulpkanin.dm @@ -32,4 +32,4 @@ /obj/item/organ/internal/kidneys/vulpkanin name = "vulpkanin kidneys" - icon = 'icons/obj/species_organs/vulpkanin.dmi' \ No newline at end of file + icon = 'icons/obj/species_organs/vulpkanin.dmi' diff --git a/code/modules/surgery/organs/subtypes/wryn.dm b/code/modules/surgery/organs/subtypes/wryn.dm index 8900bb8b37d..cbc60599744 100644 --- a/code/modules/surgery/organs/subtypes/wryn.dm +++ b/code/modules/surgery/organs/subtypes/wryn.dm @@ -8,4 +8,4 @@ slot = "hivenode" /obj/item/organ/internal/eyes/wryn - see_in_dark = 3 \ No newline at end of file + see_in_dark = 3 diff --git a/code/modules/surgery/organs_internal.dm b/code/modules/surgery/organs_internal.dm index a67774389c0..554f8bd63e4 100644 --- a/code/modules/surgery/organs_internal.dm +++ b/code/modules/surgery/organs_internal.dm @@ -340,7 +340,7 @@ user.visible_message(" [user] has poured some of [tool] over [target]'s [I.name].", " You have poured some of [tool] over [target]'s [I.name].") R.trans_to(target, GHETTO_DISINFECT_AMOUNT) - R.reaction(target, INGEST) + R.reaction(target, REAGENT_INGEST) else if(current_type == "finish") if(affected && affected.encased) @@ -410,7 +410,7 @@ I.receive_damage(rand(4,8),0) R.trans_to(target, GHETTO_DISINFECT_AMOUNT * 10) - R.reaction(target, INGEST) + R.reaction(target, REAGENT_INGEST) user.visible_message(" [user]'s hand slips, splashing the contents of [tool] all over [target]'s [affected.name] incision!", \ " Your hand slips, splashing the contents of [tool] all over [target]'s [affected.name] incision!") diff --git a/code/modules/surgery/other.dm b/code/modules/surgery/other.dm index 939fa360d03..b2bd8e6acd8 100644 --- a/code/modules/surgery/other.dm +++ b/code/modules/surgery/other.dm @@ -208,7 +208,7 @@ var/trans = container.reagents.trans_to(target, container.amount_per_transfer_from_this) if(trans > 0) - container.reagents.reaction(target, INGEST) //technically it's contact, but the reagents are being applied to internal tissue + container.reagents.reaction(target, REAGENT_INGEST) //technically it's contact, but the reagents are being applied to internal tissue if(mitocholide) affected.status &= ~ORGAN_DEAD @@ -228,7 +228,7 @@ var/obj/item/reagent_containers/container = tool var/trans = container.reagents.trans_to(target, container.amount_per_transfer_from_this) - container.reagents.reaction(target, INGEST) //technically it's contact, but the reagents are being applied to internal tissue + container.reagents.reaction(target, REAGENT_INGEST) //technically it's contact, but the reagents are being applied to internal tissue user.visible_message(" [user]'s hand slips, applying [trans] units of the solution to the wrong place in [target]'s [affected.name] with the [tool]!" , \ " Your hand slips, applying [trans] units of the solution to the wrong place in [target]'s [affected.name] with the [tool]!") diff --git a/code/modules/surgery/plastic_surgery.dm b/code/modules/surgery/plastic_surgery.dm index 0aac0bf705f..ed93f95c3c9 100644 --- a/code/modules/surgery/plastic_surgery.dm +++ b/code/modules/surgery/plastic_surgery.dm @@ -53,4 +53,4 @@ user.visible_message(" [user]'s hand slips, tearing skin on [target]'s face with [tool]!", \ " Your hand slips, tearing skin on [target]'s face with [tool]!") target.apply_damage(10, BRUTE, head, sharp = TRUE) - return FALSE \ No newline at end of file + return FALSE diff --git a/code/modules/surgery/remove_embedded_object.dm b/code/modules/surgery/remove_embedded_object.dm index 340492545be..2040138640f 100644 --- a/code/modules/surgery/remove_embedded_object.dm +++ b/code/modules/surgery/remove_embedded_object.dm @@ -63,4 +63,4 @@ else to_chat(user, "You can't find [target]'s [parse_zone(user.zone_selected)], let alone any objects embedded in it!") - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/surgery/rig_removal.dm b/code/modules/surgery/rig_removal.dm index 1fbd3e528a3..de15eacf8f3 100644 --- a/code/modules/surgery/rig_removal.dm +++ b/code/modules/surgery/rig_removal.dm @@ -31,10 +31,11 @@ /datum/surgery_step/hardsuit/can_use(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) if(!istype(target)) return 0 - if(istype(tool,/obj/item/weldingtool)) - var/obj/item/weldingtool/welder = tool - if(!welder.isOn() || !welder.remove_fuel(1,user)) - return 0 + if(tool.tool_behaviour == TOOL_WELDER) + if(!tool.tool_use_check(user, 0)) + return + if(!tool.use(1)) + return return (target_zone == "chest") && istype(target.back, /obj/item/rig) && (target.back.flags&NODROP) /datum/surgery_step/rigsuit/begin_step(mob/user, mob/living/carbon/human/target, target_zone, obj/item/tool) diff --git a/code/modules/surgery/robotics.dm b/code/modules/surgery/robotics.dm index c3198b961be..31164aaf54e 100644 --- a/code/modules/surgery/robotics.dm +++ b/code/modules/surgery/robotics.dm @@ -225,9 +225,8 @@ if(!(affected.brute_dam > 0 || affected.disfigured)) to_chat(user, "The [affected] does not require welding repair!") return -1 - if(istype(tool,/obj/item/weldingtool)) - var/obj/item/weldingtool/welder = tool - if(!welder.isOn() || !welder.remove_fuel(1,user)) + if(tool.tool_behaviour == TOOL_WELDER) + if(!tool.use(1)) return -1 user.visible_message("[user] begins to patch damage to [target]'s [affected.name]'s support structure with \the [tool]." , \ "You begin to patch damage to [target]'s [affected.name]'s support structure with \the [tool].") diff --git a/code/modules/telesci/telepad.dm b/code/modules/telesci/telepad.dm index 581df91e267..1ec94634b47 100644 --- a/code/modules/telesci/telepad.dm +++ b/code/modules/telesci/telepad.dm @@ -37,23 +37,28 @@ efficiency = E /obj/machinery/telepad/attackby(obj/item/I, mob/user, params) - if(default_deconstruction_screwdriver(user, "pad-idle-o", "pad-idle", I)) - return - - if(panel_open) - if(istype(I, /obj/item/multitool)) - var/obj/item/multitool/M = I - M.buffer = src - to_chat(user, "You save the data in the [I.name]'s buffer.") - return - if(exchange_parts(user, I)) return - - if(default_deconstruction_crowbar(I)) - return return ..() +/obj/machinery/telepad/screwdriver_act(mob/user, obj/item/I) + . = TRUE + default_deconstruction_screwdriver(user, "pad-idle-o", "pad-idle", I) + +/obj/machinery/telepad/multitool_act(mob/user, obj/item/I) + if(!panel_open) + return + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + if(!I.multitool_check_buffer(user)) + return + var/obj/item/multitool/M = I + M.set_multitool_buffer(user, src) + +/obj/machinery/telepad/crowbar_act(mob/user, obj/item/I) + . = TRUE + default_deconstruction_crowbar(user, I) //CARGO TELEPAD// /obj/machinery/telepad_cargo @@ -67,32 +72,23 @@ active_power_usage = 500 var/stage = 0 -/obj/machinery/telepad_cargo/attackby(obj/item/I, mob/user, params) - if(iswrench(I)) - playsound(src, I.usesound, 50, 1) - if(anchored) - anchored = FALSE - to_chat(user, " The [src] can now be moved.") - else if(!anchored) - anchored = TRUE - to_chat(user, " The [src] is now secured.") +/obj/machinery/telepad_cargo/crowbar_act(mob/living/user, obj/item/I) + if(stage != 1) return - if(isscrewdriver(I)) - if(stage == 0) - playsound(src, I.usesound, 50, 1) - to_chat(user, " You unscrew the telepad's tracking beacon.") - stage = 1 - else if(stage == 1) - playsound(src, I.usesound, 50, 1) - to_chat(user, " You screw in the telepad's tracking beacon.") - stage = 0 + . = TRUE + default_deconstruction_crowbar(user, I) + +/obj/machinery/telepad_cargo/screwdriver_act(mob/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) return - if(iswelder(I) && stage == 1) - playsound(src, I.usesound, 50, 1) - to_chat(user, " You disassemble the telepad.") - deconstruct(TRUE) - return - return ..() + to_chat(user, " You [stage ? "screw in" : "unscrew"] the telepad's tracking beacon.") + stage = !stage + +/obj/machinery/telepad_cargo/wrench_act(mob/user, obj/item/I) + . = TRUE + default_unfasten_wrench(user, I) + /obj/machinery/telepad_cargo/deconstruct(disassembled = TRUE) if(!(flags & NODECONSTRUCT)) diff --git a/code/modules/telesci/telesci_computer.dm b/code/modules/telesci/telesci_computer.dm index 2835a445f1c..9d66758207f 100644 --- a/code/modules/telesci/telesci_computer.dm +++ b/code/modules/telesci/telesci_computer.dm @@ -4,7 +4,7 @@ icon_keyboard = "telesci_key" icon_screen = "telesci" circuit = /obj/item/circuitboard/telesci_console - req_access = list(access_research) + req_access = list(ACCESS_RESEARCH) var/sending = 1 var/obj/machinery/telepad/telepad = null var/temp_msg = "Telescience control console initialized.
        Welcome." diff --git a/code/modules/tram/tram_control_pad.dm b/code/modules/tram/tram_control_pad.dm index 16d2252ef82..3bbb3811de2 100644 --- a/code/modules/tram/tram_control_pad.dm +++ b/code/modules/tram/tram_control_pad.dm @@ -30,4 +30,4 @@ usr << browse(null, "window=trampad") src.add_fingerprint(usr) - src.updateUsrDialog() \ No newline at end of file + src.updateUsrDialog() diff --git a/code/modules/tram/tram_floor.dm b/code/modules/tram/tram_floor.dm index fc3b1d162d5..a5c3ec2a3a0 100644 --- a/code/modules/tram/tram_floor.dm +++ b/code/modules/tram/tram_floor.dm @@ -17,4 +17,4 @@ if(istype(TF)) if(TF in controller.tram_floors) continue controller.add_floor(TF) - TF.spread_floors() \ No newline at end of file + TF.spread_floors() diff --git a/code/modules/tram/tram_rail.dm b/code/modules/tram/tram_rail.dm index 2a22f8d4238..254f2e8636b 100644 --- a/code/modules/tram/tram_rail.dm +++ b/code/modules/tram/tram_rail.dm @@ -5,4 +5,4 @@ icon_state = "rail" var/godir = null var/stop_duration = null - layer = TURF_LAYER + 0.1 \ No newline at end of file + layer = TURF_LAYER + 0.1 diff --git a/code/modules/tram/tram_wall.dm b/code/modules/tram/tram_wall.dm index bfde77f6a4c..73c278f0ca0 100644 --- a/code/modules/tram/tram_wall.dm +++ b/code/modules/tram/tram_wall.dm @@ -18,4 +18,4 @@ if(istype(TW)) if(TW in controller.tram_walls) continue controller.add_wall(TW) - TW.spread_walls() \ No newline at end of file + TW.spread_walls() diff --git a/code/modules/vehicle/ambulance.dm b/code/modules/vehicle/ambulance.dm index 7b4797c6fe3..450a6c88372 100644 --- a/code/modules/vehicle/ambulance.dm +++ b/code/modules/vehicle/ambulance.dm @@ -118,4 +118,4 @@ to_chat(usr, "You unhook the bed to the ambulance.") else amb.bed = src - to_chat(usr, "You hook the bed to the ambulance.") \ No newline at end of file + to_chat(usr, "You hook the bed to the ambulance.") diff --git a/code/modules/vehicle/atv.dm b/code/modules/vehicle/atv.dm index 39d3e7e4612..a077c848f05 100644 --- a/code/modules/vehicle/atv.dm +++ b/code/modules/vehicle/atv.dm @@ -74,4 +74,4 @@ turret.pixel_y = 4 if(WEST) turret.pixel_x = 12 - turret.pixel_y = 4 \ No newline at end of file + turret.pixel_y = 4 diff --git a/code/modules/vehicle/janicart.dm b/code/modules/vehicle/janicart.dm index 7df2f8da59a..12ed9f727eb 100644 --- a/code/modules/vehicle/janicart.dm +++ b/code/modules/vehicle/janicart.dm @@ -93,4 +93,4 @@ mybag.forceMove(get_turf(user)) user.put_in_hands(mybag) mybag = null - update_icon() \ No newline at end of file + update_icon() diff --git a/code/modules/vehicle/motorcycle.dm b/code/modules/vehicle/motorcycle.dm index ea165c1c8df..d208471fa74 100644 --- a/code/modules/vehicle/motorcycle.dm +++ b/code/modules/vehicle/motorcycle.dm @@ -26,4 +26,4 @@ if(dir == SOUTH) layer = ABOVE_MOB_LAYER else - layer = OBJ_LAYER \ No newline at end of file + layer = OBJ_LAYER diff --git a/code/modules/vehicle/secway.dm b/code/modules/vehicle/secway.dm index 48821a6e630..b3f022c5fe8 100644 --- a/code/modules/vehicle/secway.dm +++ b/code/modules/vehicle/secway.dm @@ -13,4 +13,4 @@ /obj/item/key/security desc = "A keyring with a small steel key, and a rubber stun baton accessory." - icon_state = "keysec" \ No newline at end of file + icon_state = "keysec" diff --git a/code/modules/vehicle/speedbike.dm b/code/modules/vehicle/speedbike.dm index 75e25c2c5e6..6de2b58d99a 100644 --- a/code/modules/vehicle/speedbike.dm +++ b/code/modules/vehicle/speedbike.dm @@ -47,4 +47,4 @@ /obj/vehicle/space/speedbike/red icon_state = "speedbike_red" - overlay_state = "cover_red" \ No newline at end of file + overlay_state = "cover_red" diff --git a/code/modules/vehicle/sportscar.dm b/code/modules/vehicle/sportscar.dm index 0f9543d4811..07352f3f65d 100644 --- a/code/modules/vehicle/sportscar.dm +++ b/code/modules/vehicle/sportscar.dm @@ -45,4 +45,4 @@ if(dir == SOUTH) layer = ABOVE_MOB_LAYER else - layer = OBJ_LAYER \ No newline at end of file + layer = OBJ_LAYER diff --git a/code/modules/vehicle/vehicle.dm b/code/modules/vehicle/vehicle.dm index cbaa3dc56e5..79021affa25 100644 --- a/code/modules/vehicle/vehicle.dm +++ b/code/modules/vehicle/vehicle.dm @@ -221,4 +221,4 @@ spaceworthy = TRUE /obj/vehicle/space/Process_Spacemove(direction) - return TRUE \ No newline at end of file + return TRUE diff --git a/code/modules/zombie/items.dm b/code/modules/zombie/items.dm index c8214ed8283..0f0f85b088b 100644 --- a/code/modules/zombie/items.dm +++ b/code/modules/zombie/items.dm @@ -66,4 +66,4 @@ var/obj/item/organ/external/O = L.get_organ("head") if(O) O.droplimb() - return (BRUTELOSS) \ No newline at end of file + return (BRUTELOSS) diff --git a/code/modules/zombie/organs.dm b/code/modules/zombie/organs.dm index f88ed23c107..6ea40c90e96 100644 --- a/code/modules/zombie/organs.dm +++ b/code/modules/zombie/organs.dm @@ -96,4 +96,4 @@ to_chat(owner, "You are now a zombie! Do not seek to be cured, do not help any non-zombies in any way, do not harm your zombie brethren and spread the disease by killing others. You are a creature of hunger and violence.") /obj/item/organ/internal/zombie_infection/nodamage - causes_damage = FALSE \ No newline at end of file + causes_damage = FALSE diff --git a/code/world.dm b/code/world.dm index 8e8d2176172..c79588e7c6c 100644 --- a/code/world.dm +++ b/code/world.dm @@ -1,9 +1,9 @@ -// This file is just for the necessary /world definition -// Try looking in game/world.dm - -/world - mob = /mob/new_player - turf = /turf/space - area = /area/space - view = "15x15" - cache_lifespan = 0 //stops player uploaded stuff from being kept in the rsc past the current session +// This file is just for the necessary /world definition +// Try looking in game/world.dm + +/world + mob = /mob/new_player + turf = /turf/space + area = /area/space + view = "15x15" + cache_lifespan = 0 //stops player uploaded stuff from being kept in the rsc past the current session diff --git a/config/example/config.txt b/config/example/config.txt index f4de07f581f..4fbc5508db3 100644 --- a/config/example/config.txt +++ b/config/example/config.txt @@ -114,7 +114,6 @@ PROBABILITY RAGINMAGES 0 PROBABILITY METEOR 0 PROBABILITY HEIST 0 PROBABILITY VAMPIRE 3 -PROBABILITY NATIONS 0 PROBABILITY EXTENDED 2 PROBABILITY NUCLEAR 3 PROBABILITY ABDUCTION 0 @@ -191,6 +190,9 @@ GUEST_JOBBAN ## Uncomment this to stop people connecting to your server without a registered ckey. (i.e. guest-* are all blocked from connecting) GUEST_BAN +## above this player count threshold, never-before-seen players are blocked from connecting +PANIC_BUNKER_THRESHOLD 150 + ### IPINTEL: ### This allows you to detect likely proxies by checking ips against getipintel.net ## Rating to warn at: (0.90 is good, 1 is 100% likely to be a spammer/proxy, 0.8 is 80%, etc) anything equal to or higher then this number triggers an admin warning diff --git a/config/example/dbconfig.txt b/config/example/dbconfig.txt index 654a7e348d5..b1113fb625d 100644 --- a/config/example/dbconfig.txt +++ b/config/example/dbconfig.txt @@ -9,7 +9,7 @@ ## This value must be set to the version of the paradise schema in use. ## If this value does not match, the SQL database will not be loaded and an error will be generated. ## Roundstart will be delayed. -DB_VERSION 10 +DB_VERSION 11 ## Server the MySQL database can be found at. # Examples: localhost, 200.135.5.43, www.mysqldb.com, etc. diff --git a/goon/browserassets/css/browserOutput-dark.css b/goon/browserassets/css/browserOutput-dark.css index 7a2c6123383..e7b79557619 100644 --- a/goon/browserassets/css/browserOutput-dark.css +++ b/goon/browserassets/css/browserOutput-dark.css @@ -381,6 +381,8 @@ h1.alert, h2.alert {color: #FFF;} .revenwarning {color: #760fbb; font-style: italic;} .revendanger {color: #760fbb; font-weight: bold; font-size: 120%;} +.specialnotice {color: #36525e; font-weight: bold; font-size: 120%;} + /* /vg/ */ .good {color: green;} .average {color: #FF8000;} diff --git a/goon/browserassets/css/browserOutput.css b/goon/browserassets/css/browserOutput.css index 87de7558ab9..962ce7bb8e7 100644 --- a/goon/browserassets/css/browserOutput.css +++ b/goon/browserassets/css/browserOutput.css @@ -378,6 +378,10 @@ h1.alert, h2.alert {color: #000000;} .revenwarning {color: #760fbb; font-style: italic;} .revendanger {color: #760fbb; font-weight: bold; font-size: 120%;} +.specialnoticebold {color: #36525e; font-weight: bold; font-size: 120%;} + +.specialnotice {color: #36525e; font-size: 120%;} + /* /vg/ */ .good {color: green;} .average {color: #FF8000;} diff --git a/goon/code/datums/browserOutput.dm b/goon/code/datums/browserOutput.dm index f782e663d0a..f32b1d53610 100644 --- a/goon/code/datums/browserOutput.dm +++ b/goon/code/datums/browserOutput.dm @@ -310,4 +310,4 @@ var/to_chat_src return SSchat.queue(target, message, flag) -#undef MAX_COOKIE_LENGTH \ No newline at end of file +#undef MAX_COOKIE_LENGTH diff --git a/icons/mob/alienqueen.dmi b/icons/mob/alienqueen.dmi index 5d09aad2480..b01af7e71b4 100644 Binary files a/icons/mob/alienqueen.dmi and b/icons/mob/alienqueen.dmi differ diff --git a/icons/mob/animal.dmi b/icons/mob/animal.dmi index c0c259af7ac..549304a43f3 100644 Binary files a/icons/mob/animal.dmi and b/icons/mob/animal.dmi differ diff --git a/icons/mob/back.dmi b/icons/mob/back.dmi index 2e3c9f7cae0..8ac73bf20d6 100644 Binary files a/icons/mob/back.dmi and b/icons/mob/back.dmi differ diff --git a/icons/mob/inhands/clothing_lefthand.dmi b/icons/mob/inhands/clothing_lefthand.dmi index 745dd19aac7..589135b8701 100644 Binary files a/icons/mob/inhands/clothing_lefthand.dmi and b/icons/mob/inhands/clothing_lefthand.dmi differ diff --git a/icons/mob/inhands/clothing_righthand.dmi b/icons/mob/inhands/clothing_righthand.dmi index e4e9fc0f859..8fd5f7d71b8 100644 Binary files a/icons/mob/inhands/clothing_righthand.dmi and b/icons/mob/inhands/clothing_righthand.dmi differ diff --git a/icons/mob/inhands/items_lefthand.dmi b/icons/mob/inhands/items_lefthand.dmi index 87f4688dadb..3d02cfd7a58 100644 Binary files a/icons/mob/inhands/items_lefthand.dmi and b/icons/mob/inhands/items_lefthand.dmi differ diff --git a/icons/mob/inhands/items_righthand.dmi b/icons/mob/inhands/items_righthand.dmi index dde1156d295..265230ad369 100644 Binary files a/icons/mob/inhands/items_righthand.dmi and b/icons/mob/inhands/items_righthand.dmi differ diff --git a/icons/mob/lavaland/blood_drunk.dmi b/icons/mob/lavaland/blood_drunk.dmi new file mode 100644 index 00000000000..7cf9e2c0197 Binary files /dev/null and b/icons/mob/lavaland/blood_drunk.dmi differ diff --git a/icons/mob/species/drask/eyes.dmi b/icons/mob/species/drask/eyes.dmi index 818c719a541..92b8d7c2190 100644 Binary files a/icons/mob/species/drask/eyes.dmi and b/icons/mob/species/drask/eyes.dmi differ diff --git a/icons/mob/species/drask/uniform.dmi b/icons/mob/species/drask/uniform.dmi index 0828c290e05..25cfb51013c 100644 Binary files a/icons/mob/species/drask/uniform.dmi and b/icons/mob/species/drask/uniform.dmi differ diff --git a/icons/mob/species/grey/eyes.dmi b/icons/mob/species/grey/eyes.dmi index 937474d97dc..c3914dfd0be 100644 Binary files a/icons/mob/species/grey/eyes.dmi and b/icons/mob/species/grey/eyes.dmi differ diff --git a/icons/mob/species/grey/uniform.dmi b/icons/mob/species/grey/uniform.dmi index 1636097f72e..c956187ffa1 100644 Binary files a/icons/mob/species/grey/uniform.dmi and b/icons/mob/species/grey/uniform.dmi differ diff --git a/icons/mob/species/vox/back.dmi b/icons/mob/species/vox/back.dmi index a2f488122df..a0a8445964a 100644 Binary files a/icons/mob/species/vox/back.dmi and b/icons/mob/species/vox/back.dmi differ diff --git a/icons/mob/species/vox/eyes.dmi b/icons/mob/species/vox/eyes.dmi index 3e3705d2a90..dfa565dd2a7 100644 Binary files a/icons/mob/species/vox/eyes.dmi and b/icons/mob/species/vox/eyes.dmi differ diff --git a/icons/mob/species/vox/uniform.dmi b/icons/mob/species/vox/uniform.dmi index 6f050370f7c..3eb4fec6f6f 100644 Binary files a/icons/mob/species/vox/uniform.dmi and b/icons/mob/species/vox/uniform.dmi differ diff --git a/icons/mob/suit.dmi b/icons/mob/suit.dmi index 9bcabb11e78..6c622704fc7 100644 Binary files a/icons/mob/suit.dmi and b/icons/mob/suit.dmi differ diff --git a/icons/mob/uniform.dmi b/icons/mob/uniform.dmi index b67023a9935..1e185efd9c5 100644 Binary files a/icons/mob/uniform.dmi and b/icons/mob/uniform.dmi differ diff --git a/icons/obj/clothing/uniforms.dmi b/icons/obj/clothing/uniforms.dmi index 15a4adb9f41..bb7a1ce9188 100644 Binary files a/icons/obj/clothing/uniforms.dmi and b/icons/obj/clothing/uniforms.dmi differ diff --git a/icons/obj/food/food.dmi b/icons/obj/food/food.dmi index aea237f5bed..01295525b15 100644 Binary files a/icons/obj/food/food.dmi and b/icons/obj/food/food.dmi differ diff --git a/icons/obj/library.dmi b/icons/obj/library.dmi index edb8d7cb2c5..645cb812422 100644 Binary files a/icons/obj/library.dmi and b/icons/obj/library.dmi differ diff --git a/icons/obj/storage.dmi b/icons/obj/storage.dmi index c9293cbb7a7..33274c3fdb2 100644 Binary files a/icons/obj/storage.dmi and b/icons/obj/storage.dmi differ diff --git a/icons/obj/trash.dmi b/icons/obj/trash.dmi index 6edd905c554..5fa2372fa80 100644 Binary files a/icons/obj/trash.dmi and b/icons/obj/trash.dmi differ diff --git a/icons/turf/floors.dmi b/icons/turf/floors.dmi index 2265194d678..a612d889222 100644 Binary files a/icons/turf/floors.dmi and b/icons/turf/floors.dmi differ diff --git a/nano/package-lock.json b/nano/package-lock.json new file mode 100644 index 00000000000..652f460953c --- /dev/null +++ b/nano/package-lock.json @@ -0,0 +1,6237 @@ +{ + "name": "nanoui", + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "@gulp-sourcemaps/identity-map": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/identity-map/-/identity-map-1.0.2.tgz", + "integrity": "sha512-ciiioYMLdo16ShmfHBXJBOFm3xPC4AuwO4xeRpFeHz7WK9PYsWCmigagG2XyzZpubK4a3qNKoUBDhbzHfa50LQ==", + "dev": true, + "requires": { + "acorn": "^5.0.3", + "css": "^2.2.1", + "normalize-path": "^2.1.1", + "source-map": "^0.6.0", + "through2": "^2.0.3" + }, + "dependencies": { + "acorn": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", + "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "dev": true + } + } + }, + "@gulp-sourcemaps/map-sources": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz", + "integrity": "sha1-iQrnxdjId/bThIYCFazp1+yUW9o=", + "dev": true, + "requires": { + "normalize-path": "^2.0.1", + "through2": "^2.0.3" + } + }, + "@webassemblyjs/ast": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", + "integrity": "sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ==", + "requires": { + "@webassemblyjs/helper-module-context": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/wast-parser": "1.8.5" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz", + "integrity": "sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ==" + }, + "@webassemblyjs/helper-api-error": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz", + "integrity": "sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA==" + }, + "@webassemblyjs/helper-buffer": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz", + "integrity": "sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q==" + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz", + "integrity": "sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ==", + "requires": { + "@webassemblyjs/wast-printer": "1.8.5" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz", + "integrity": "sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow==" + }, + "@webassemblyjs/helper-module-context": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz", + "integrity": "sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "mamacro": "^0.0.3" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz", + "integrity": "sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ==" + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz", + "integrity": "sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz", + "integrity": "sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g==", + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.8.5.tgz", + "integrity": "sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A==", + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.8.5.tgz", + "integrity": "sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw==" + }, + "@webassemblyjs/wasm-edit": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz", + "integrity": "sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/helper-wasm-section": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5", + "@webassemblyjs/wasm-opt": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5", + "@webassemblyjs/wast-printer": "1.8.5" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz", + "integrity": "sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/ieee754": "1.8.5", + "@webassemblyjs/leb128": "1.8.5", + "@webassemblyjs/utf8": "1.8.5" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz", + "integrity": "sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-buffer": "1.8.5", + "@webassemblyjs/wasm-gen": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz", + "integrity": "sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-api-error": "1.8.5", + "@webassemblyjs/helper-wasm-bytecode": "1.8.5", + "@webassemblyjs/ieee754": "1.8.5", + "@webassemblyjs/leb128": "1.8.5", + "@webassemblyjs/utf8": "1.8.5" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz", + "integrity": "sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/floating-point-hex-parser": "1.8.5", + "@webassemblyjs/helper-api-error": "1.8.5", + "@webassemblyjs/helper-code-frame": "1.8.5", + "@webassemblyjs/helper-fsm": "1.8.5", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz", + "integrity": "sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/wast-parser": "1.8.5", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + }, + "accord": { + "version": "0.29.0", + "resolved": "https://registry.npmjs.org/accord/-/accord-0.29.0.tgz", + "integrity": "sha512-3OOR92FTc2p5/EcOzPcXp+Cbo+3C15nV9RXHlOUBCBpHhcB+0frbSNR9ehED/o7sTcyGVtqGJpguToEdlXhD0w==", + "dev": true, + "requires": { + "convert-source-map": "^1.5.0", + "glob": "^7.0.5", + "indx": "^0.2.3", + "lodash.clone": "^4.3.2", + "lodash.defaults": "^4.0.1", + "lodash.flatten": "^4.2.0", + "lodash.merge": "^4.4.0", + "lodash.partialright": "^4.1.4", + "lodash.pick": "^4.2.1", + "lodash.uniq": "^4.3.0", + "resolve": "^1.5.0", + "semver": "^5.3.0", + "uglify-js": "^2.8.22", + "when": "^3.7.8" + }, + "dependencies": { + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dev": true, + "requires": { + "center-align": "^0.1.1", + "right-align": "^0.1.1", + "wordwrap": "0.0.2" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "uglify-js": { + "version": "2.8.29", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", + "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", + "dev": true, + "requires": { + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" + } + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "requires": { + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", + "window-size": "0.1.0" + } + } + } + }, + "acorn": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==" + }, + "ajv": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", + "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==" + }, + "ajv-keywords": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", + "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==" + }, + "align-text": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", + "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", + "dev": true, + "requires": { + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "requires": { + "ansi-wrap": "^0.1.0" + } + }, + "ansi-cyan": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-cyan/-/ansi-cyan-0.1.1.tgz", + "integrity": "sha1-U4rlKK+JgvKK4w2G8vF0VtJgmHM=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-gray": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", + "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-red": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ansi-red/-/ansi-red-0.1.1.tgz", + "integrity": "sha1-jGOPnRCAgAo1PJwoyKgcpHBdlGw=", + "dev": true, + "requires": { + "ansi-wrap": "0.1.0" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "ansi-wrap": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz", + "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=" + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "append-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz", + "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=", + "dev": true, + "requires": { + "buffer-equal": "^1.0.0" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "archy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", + "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", + "dev": true + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + }, + "arr-filter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz", + "integrity": "sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4=", + "dev": true, + "requires": { + "make-iterator": "^1.0.0" + } + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" + }, + "arr-map": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz", + "integrity": "sha1-Onc0X/wc814qkYJWAfnljy4kysQ=", + "dev": true, + "requires": { + "make-iterator": "^1.0.0" + } + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" + }, + "array-each": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", + "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=", + "dev": true + }, + "array-initial": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", + "integrity": "sha1-L6dLJnOTccOUe9enrcc74zSz15U=", + "dev": true, + "requires": { + "array-slice": "^1.0.0", + "is-number": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + } + } + }, + "array-last": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz", + "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==", + "dev": true, + "requires": { + "is-number": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", + "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "dev": true + } + } + }, + "array-slice": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", + "dev": true + }, + "array-sort": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz", + "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==", + "dev": true, + "requires": { + "default-compare": "^1.0.0", + "get-value": "^2.0.6", + "kind-of": "^5.0.2" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", + "optional": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "optional": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "optional": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" + }, + "async-done": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", + "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.2", + "process-nextick-args": "^2.0.0", + "stream-exhaust": "^1.0.1" + } + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==" + }, + "async-settle": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", + "integrity": "sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs=", + "dev": true, + "requires": { + "async-done": "^1.2.2" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "optional": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" + }, + "autoprefixer": { + "version": "8.6.5", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-8.6.5.tgz", + "integrity": "sha512-PLWJN3Xo/rycNkx+mp8iBDMTm3FeWe4VmYaZDSqL5QQB9sLsQkG5k8n+LNDFnhh9kdq2K+egL/icpctOmDHwig==", + "dev": true, + "requires": { + "browserslist": "^3.2.8", + "caniuse-lite": "^1.0.30000864", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^6.0.23", + "postcss-value-parser": "^3.2.3" + }, + "dependencies": { + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "optional": true + }, + "aws4": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", + "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==", + "optional": true + }, + "bach": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", + "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=", + "dev": true, + "requires": { + "arr-filter": "^1.1.1", + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "array-each": "^1.0.0", + "array-initial": "^1.0.0", + "array-last": "^1.1.1", + "async-done": "^1.2.2", + "async-settle": "^1.0.0", + "now-and-later": "^2.0.0" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "optional": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==" + }, + "binaryextensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-2.2.0.tgz", + "integrity": "sha512-bHhs98rj/7i/RZpCSJ3uk55pLXOItjIrh2sRQZSM6OoktScX+LxJzvlU+FELp9j3TdcddTmmYArLSGptCTwjuw==", + "dev": true + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + } + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", + "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", + "requires": { + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz", + "integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30000844", + "electron-to-chromium": "^1.3.47" + } + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "buffer-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", + "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" + }, + "cacache": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz", + "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==", + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + }, + "dependencies": { + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" + } + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "dev": true, + "requires": { + "callsites": "^2.0.0" + } + }, + "caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "dev": true, + "requires": { + "caller-callsite": "^2.0.0" + } + }, + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", + "dev": true + }, + "camelcase": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", + "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30001025", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001025.tgz", + "integrity": "sha512-SKyFdHYfXUZf5V85+PJgLYyit27q4wgvZuf8QTOk1osbypcROihMBlx9GRar2/pIcKH2r4OehdlBr9x6PXetAQ==", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "optional": true + }, + "center-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", + "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", + "dev": true, + "requires": { + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + } + } + }, + "chownr": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz", + "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==" + }, + "chrome-trace-event": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", + "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", + "requires": { + "tslib": "^1.9.0" + } + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "clean-css": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz", + "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", + "requires": { + "source-map": "~0.6.0" + } + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" + } + }, + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=" + }, + "clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=", + "dev": true + }, + "clone-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", + "dev": true + }, + "cloneable-readable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", + "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "collection-map": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz", + "integrity": "sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw=", + "dev": true, + "requires": { + "arr-map": "^2.0.2", + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + } + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/color/-/color-0.10.1.tgz", + "integrity": "sha1-wEGI34KiCd3rzOzazT7DIPGTc58=", + "dev": true, + "requires": { + "color-convert": "^0.5.3", + "color-string": "^0.3.0" + }, + "dependencies": { + "color-convert": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", + "integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0=", + "dev": true + } + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "color-string": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", + "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", + "dev": true, + "requires": { + "color-name": "^1.0.0" + } + }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "optional": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "concat-with-sourcemaps": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz", + "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==", + "dev": true, + "requires": { + "source-map": "^0.6.1" + } + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==" + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } + } + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" + }, + "copy-props": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.4.tgz", + "integrity": "sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A==", + "dev": true, + "requires": { + "each-props": "^1.3.0", + "is-plain-object": "^2.0.1" + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dev": true, + "requires": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "dependencies": { + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + } + } + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "css": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz", + "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "source-map": "^0.6.1", + "source-map-resolve": "^0.5.2", + "urix": "^0.1.0" + } + }, + "cyclist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=" + }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dev": true, + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "optional": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "debug-fabulous": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/debug-fabulous/-/debug-fabulous-1.1.0.tgz", + "integrity": "sha512-GZqvGIgKNlUnHUPQhepnUZFIMoi3dgZKQBzKDeL2g7oJF9SNAji/AAu36dusFUas0O+pae74lNeoIPHqXWDkLg==", + "dev": true, + "requires": { + "debug": "3.X", + "memoizee": "0.4.X", + "object-assign": "4.X" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + }, + "default-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", + "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", + "dev": true, + "requires": { + "kind-of": "^5.0.2" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "default-resolution": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz", + "integrity": "sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ=", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "optional": true + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "dev": true + }, + "detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==" + }, + "domurl": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domurl/-/domurl-2.3.0.tgz", + "integrity": "sha512-KoPGBykwI0AnDjWuEz3SQECab4FbwvE/1CJk1qTBGiIdyquCFRLs0eldfl6ESrpSKd1GGjMnzS2g7+Xl12obqg==" + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "each-props": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz", + "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.1", + "object.defaults": "^1.1.0" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "optional": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "editions": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/editions/-/editions-1.3.4.tgz", + "integrity": "sha512-gzao+mxnYDzIysXKMQi/+M1mjy/rjestjg6OPoYTtI+3Izp23oiGZitsl9lPDPiTGXbcSIk1iJWhliSaglxnUg==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.345", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.345.tgz", + "integrity": "sha512-f8nx53+Z9Y+SPWGg3YdHrbYYfIJAtbUjpFfW4X1RwTZ94iUG7geg9tV8HqzAXX7XTNgyWgAFvce4yce8ZKxKmg==", + "dev": true + }, + "elliptic": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", + "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz", + "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==", + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + }, + "dependencies": { + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + } + } + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "requires": { + "prr": "~1.0.1" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es5-ext": { + "version": "0.10.53", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", + "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "dev": true, + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.3", + "next-tick": "~1.0.0" + } + }, + "es5-shim": { + "version": "4.5.13", + "resolved": "https://registry.npmjs.org/es5-shim/-/es5-shim-4.5.13.tgz", + "integrity": "sha512-xi6hh6gsvDE0MaW4Vp1lgNEBpVcCXRWfPXj5egDvtgLz4L9MEvNwYEMdJH+JJinWkwa8c3c3o5HduV7dB/e1Hw==" + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dev": true, + "requires": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "es6-weak-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", + "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.46", + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.1" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + }, + "event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "events": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz", + "integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==" + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + } + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "ext": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", + "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", + "dev": true, + "requires": { + "type": "^2.0.0" + }, + "dependencies": { + "type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.0.0.tgz", + "integrity": "sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==", + "dev": true + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "optional": true + }, + "fancy-log": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "dev": true, + "requires": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" + } + }, + "fast-deep-equal": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", + "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "figgy-pudding": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.1.tgz", + "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==" + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "optional": true + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + } + } + }, + "filter-gradient": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/filter-gradient/-/filter-gradient-1.0.1.tgz", + "integrity": "sha1-ikFRiyIoQ1H6ymNqb9sba5BMogc=", + "dev": true, + "requires": { + "color": "^0.10.1" + } + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "findup-sync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", + "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "dev": true, + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + } + }, + "fined": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", + "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", + "dev": true, + "requires": { + "expand-tilde": "^2.0.2", + "is-plain-object": "^2.0.3", + "object.defaults": "^1.1.0", + "object.pick": "^1.2.0", + "parse-filepath": "^1.0.1" + } + }, + "flagged-respawn": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", + "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", + "dev": true + }, + "flush-write-stream": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", + "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "font-awesome": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/font-awesome/-/font-awesome-4.7.0.tgz", + "integrity": "sha1-j6jPBBGhoxr9B7BtKQK7n8gVoTM=" + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" + }, + "for-own": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", + "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", + "dev": true, + "requires": { + "for-in": "^1.0.1" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "optional": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "optional": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "requires": { + "map-cache": "^0.2.2" + } + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-mkdirp-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", + "integrity": "sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "through2": "^2.0.3" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fsevents": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.11.tgz", + "integrity": "sha512-+ux3lx6peh0BpvY0JebGyZoiR4D+oYzdPZMKJwkZ+sFkNJzpL7tXc/wehS49gUAxg3tmMHPHZkA8JU2rhhgDHw==", + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1", + "node-pre-gyp": "*" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "optional": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "optional": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.3", + "bundled": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "optional": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "debug": { + "version": "3.2.6", + "bundled": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.7", + "bundled": true, + "optional": true, + "requires": { + "minipass": "^2.6.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.6", + "bundled": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.3", + "bundled": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "bundled": true, + "optional": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "optional": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "optional": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "optional": true + }, + "minipass": { + "version": "2.9.0", + "bundled": true, + "optional": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.3.3", + "bundled": true, + "optional": true, + "requires": { + "minipass": "^2.9.0" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "optional": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.1.2", + "bundled": true, + "optional": true + }, + "needle": { + "version": "2.4.0", + "bundled": true, + "optional": true, + "requires": { + "debug": "^3.2.6", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.14.0", + "bundled": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4.4.2" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.1.1", + "bundled": true, + "optional": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "bundled": true, + "optional": true + }, + "npm-packlist": { + "version": "1.4.7", + "bundled": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "optional": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.7.1", + "bundled": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "optional": true + }, + "semver": { + "version": "5.7.1", + "bundled": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "optional": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "optional": true + }, + "tar": { + "version": "4.4.13", + "bundled": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "optional": true + }, + "yallist": { + "version": "3.1.1", + "bundled": true, + "optional": true + } + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", + "dev": true + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "optional": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "glob-stream": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz", + "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=", + "dev": true, + "requires": { + "extend": "^3.0.0", + "glob": "^7.1.1", + "glob-parent": "^3.1.0", + "is-negated-glob": "^1.0.0", + "ordered-read-streams": "^1.0.0", + "pumpify": "^1.3.5", + "readable-stream": "^2.1.5", + "remove-trailing-separator": "^1.0.1", + "to-absolute-glob": "^2.0.0", + "unique-stream": "^2.0.2" + } + }, + "glob-watcher": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.3.tgz", + "integrity": "sha512-8tWsULNEPHKQ2MR4zXuzSmqbdyV5PtwwCaWSGQ1WwHsJ07ilNeN1JB8ntxhckbnpSHaf9dXFUHzIWvm1I13dsg==", + "dev": true, + "requires": { + "anymatch": "^2.0.0", + "async-done": "^1.2.0", + "chokidar": "^2.0.0", + "is-negated-glob": "^1.0.0", + "just-debounce": "^1.0.0", + "object.defaults": "^1.1.0" + } + }, + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + } + }, + "glogg": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", + "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", + "dev": true, + "requires": { + "sparkles": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==" + }, + "gulp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", + "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==", + "dev": true, + "requires": { + "glob-watcher": "^5.0.3", + "gulp-cli": "^2.2.0", + "undertaker": "^1.2.1", + "vinyl-fs": "^3.0.0" + }, + "dependencies": { + "gulp-cli": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.2.0.tgz", + "integrity": "sha512-rGs3bVYHdyJpLqR0TUBnlcZ1O5O++Zs4bA0ajm+zr3WFCfiSLjGwoCBqFs18wzN+ZxahT9DkOK5nDf26iDsWjA==", + "dev": true, + "requires": { + "ansi-colors": "^1.0.1", + "archy": "^1.0.0", + "array-sort": "^1.0.0", + "color-support": "^1.1.3", + "concat-stream": "^1.6.0", + "copy-props": "^2.0.1", + "fancy-log": "^1.3.2", + "gulplog": "^1.0.0", + "interpret": "^1.1.0", + "isobject": "^3.0.1", + "liftoff": "^3.1.0", + "matchdep": "^2.0.0", + "mute-stdout": "^1.0.0", + "pretty-hrtime": "^1.0.0", + "replace-homedir": "^1.0.0", + "semver-greatest-satisfied-range": "^1.1.0", + "v8flags": "^3.0.1", + "yargs": "^7.1.0" + } + } + } + }, + "gulp-clean-css": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/gulp-clean-css/-/gulp-clean-css-3.10.0.tgz", + "integrity": "sha512-7Isf9Y690o/Q5MVjEylH1H7L8WeZ89woW7DnhD5unTintOdZb67KdOayRgp9trUFo+f9UyJtuatV42e/+kghPg==", + "requires": { + "clean-css": "4.2.1", + "plugin-error": "1.0.1", + "through2": "2.0.3", + "vinyl-sourcemaps-apply": "0.2.1" + } + }, + "gulp-concat": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/gulp-concat/-/gulp-concat-2.6.1.tgz", + "integrity": "sha1-Yz0WyV2IUEYorQJmVmPO5aR5M1M=", + "dev": true, + "requires": { + "concat-with-sourcemaps": "^1.0.0", + "through2": "^2.0.0", + "vinyl": "^2.0.0" + } + }, + "gulp-less": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/gulp-less/-/gulp-less-4.0.1.tgz", + "integrity": "sha512-hmM2k0FfQp7Ptm3ZaqO2CkMX3hqpiIOn4OHtuSsCeFym63F7oWlEua5v6u1cIjVUKYsVIs9zPg9vbqTEb/udpA==", + "dev": true, + "requires": { + "accord": "^0.29.0", + "less": "2.6.x || ^3.7.1", + "object-assign": "^4.0.1", + "plugin-error": "^0.1.2", + "replace-ext": "^1.0.0", + "through2": "^2.0.0", + "vinyl-sourcemaps-apply": "^0.2.0" + }, + "dependencies": { + "arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + } + }, + "arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", + "dev": true + }, + "array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true + }, + "extend-shallow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", + "dev": true, + "requires": { + "kind-of": "^1.1.0" + } + }, + "kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", + "dev": true + }, + "plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", + "dev": true, + "requires": { + "ansi-cyan": "^0.1.1", + "ansi-red": "^0.1.1", + "arr-diff": "^1.0.1", + "arr-union": "^2.0.1", + "extend-shallow": "^1.1.2" + } + } + } + }, + "gulp-order": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gulp-order/-/gulp-order-1.2.0.tgz", + "integrity": "sha512-RduLhxbEye1NT6fnvev3792VUsZA8F+5zRFczAgpOSELz587Fv1Nn7l4ZiaiQzFPV7aECh1WRc2dPWjjcxCa0g==", + "dev": true, + "requires": { + "minimatch": "~3.0.4", + "stable": "~0.1.8", + "through": "~2.3.8" + } + }, + "gulp-postcss": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/gulp-postcss/-/gulp-postcss-8.0.0.tgz", + "integrity": "sha512-Wtl6vH7a+8IS/fU5W9IbOpcaLqKxd5L1DUOzaPmlnCbX1CrG0aWdwVnC3Spn8th0m8D59YbysV5zPUe1n/GJYg==", + "dev": true, + "requires": { + "fancy-log": "^1.3.2", + "plugin-error": "^1.0.1", + "postcss": "^7.0.2", + "postcss-load-config": "^2.0.0", + "vinyl-sourcemaps-apply": "^0.2.1" + } + }, + "gulp-replace": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulp-replace/-/gulp-replace-1.0.0.tgz", + "integrity": "sha512-lgdmrFSI1SdhNMXZQbrC75MOl1UjYWlOWNbNRnz+F/KHmgxt3l6XstBoAYIdadwETFyG/6i+vWUSCawdC3pqOw==", + "dev": true, + "requires": { + "istextorbinary": "2.2.1", + "readable-stream": "^2.0.1", + "replacestream": "^4.0.0" + } + }, + "gulp-sourcemaps": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/gulp-sourcemaps/-/gulp-sourcemaps-2.6.5.tgz", + "integrity": "sha512-SYLBRzPTew8T5Suh2U8jCSDKY+4NARua4aqjj8HOysBh2tSgT9u4jc1FYirAdPx1akUxxDeK++fqw6Jg0LkQRg==", + "dev": true, + "requires": { + "@gulp-sourcemaps/identity-map": "1.X", + "@gulp-sourcemaps/map-sources": "1.X", + "acorn": "5.X", + "convert-source-map": "1.X", + "css": "2.X", + "debug-fabulous": "1.X", + "detect-newline": "2.X", + "graceful-fs": "4.X", + "source-map": "~0.6.0", + "strip-bom-string": "1.X", + "through2": "2.X" + }, + "dependencies": { + "acorn": { + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", + "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "dev": true + } + } + }, + "gulp-uglify": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gulp-uglify/-/gulp-uglify-3.0.2.tgz", + "integrity": "sha512-gk1dhB74AkV2kzqPMQBLA3jPoIAPd/nlNzP2XMDSG8XZrqnlCiDGAqC+rZOumzFvB5zOphlFh6yr3lgcAb/OOg==", + "dev": true, + "requires": { + "array-each": "^1.0.1", + "extend-shallow": "^3.0.2", + "gulplog": "^1.0.0", + "has-gulplog": "^0.1.0", + "isobject": "^3.0.1", + "make-error-cause": "^1.1.1", + "safe-buffer": "^5.1.2", + "through2": "^2.0.0", + "uglify-js": "^3.0.5", + "vinyl-sourcemaps-apply": "^0.2.0" + } + }, + "gulplog": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", + "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", + "dev": true, + "requires": { + "glogg": "^1.0.0" + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "optional": true + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "optional": true, + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-gulplog": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", + "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", + "dev": true, + "requires": { + "sparkles": "^1.0.0" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.5.tgz", + "integrity": "sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==", + "dev": true + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "optional": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=" + }, + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=", + "optional": true + }, + "import-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", + "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=", + "dev": true, + "requires": { + "import-from": "^2.1.0" + } + }, + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "dev": true, + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + } + }, + "import-from": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", + "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + }, + "indx": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/indx/-/indx-0.2.3.tgz", + "integrity": "sha1-Fdz1bunPZcAjTFE8J/vVgOcPvFA=", + "dev": true + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true + }, + "interpret": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", + "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==", + "dev": true + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, + "is-absolute": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "dev": true, + "requires": { + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" + } + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", + "dev": true + }, + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "requires": { + "is-plain-object": "^2.0.4" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-negated-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", + "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=", + "dev": true + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "requires": { + "isobject": "^3.0.1" + } + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-relative": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "dev": true, + "requires": { + "is-unc-path": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "optional": true + }, + "is-unc-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "dev": true, + "requires": { + "unc-path-regex": "^0.1.2" + } + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "is-valid-glob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz", + "integrity": "sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "optional": true + }, + "istextorbinary": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-2.2.1.tgz", + "integrity": "sha512-TS+hoFl8Z5FAFMK38nhBkdLt44CclNRgDHWeMgsV8ko3nDlr/9UI2Sf839sW7enijf8oKsZYXRvM8g0it9Zmcw==", + "dev": true, + "requires": { + "binaryextensions": "2", + "editions": "^1.3.3", + "textextensions": "2" + } + }, + "js-url": { + "version": "git+https://github.com/websanova/js-url.git#beead70d84e3b065697e68d443343a27b20c0d39", + "from": "git+https://github.com/websanova/js-url.git" + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "optional": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "optional": true + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "requires": { + "minimist": "^1.2.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "optional": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "just-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.0.0.tgz", + "integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo=", + "dev": true + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + }, + "last-run": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz", + "integrity": "sha1-RblpQsF7HHnHchmCWbqUO+v4yls=", + "dev": true, + "requires": { + "default-resolution": "^2.0.0", + "es6-weak-map": "^2.0.1" + } + }, + "lazy-cache": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", + "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", + "dev": true + }, + "lazystream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", + "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", + "dev": true, + "requires": { + "readable-stream": "^2.0.5" + } + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "^1.0.0" + } + }, + "lead": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", + "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=", + "dev": true, + "requires": { + "flush-write-stream": "^1.0.2" + } + }, + "less": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/less/-/less-3.11.0.tgz", + "integrity": "sha512-dAui5qzfxuWY7BIEt9/gy5EbDhwDb44rqaIUFYeu8wEE8huMZ/PzB+gNFONEG5DUPrOrOGcAjGeYVg6AFiA9KQ==", + "requires": { + "clone": "^2.1.2", + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "mime": "^1.4.1", + "mkdirp": "^0.5.0", + "promise": "^7.1.1", + "request": "^2.83.0", + "source-map": "~0.6.0" + } + }, + "less-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-5.0.0.tgz", + "integrity": "sha512-bquCU89mO/yWLaUq0Clk7qCsKhsF/TZpJUzETRvJa9KSVEL9SO3ovCvdEHISBhrC81OwC8QSVX7E0bzElZj9cg==", + "requires": { + "clone": "^2.1.1", + "loader-utils": "^1.1.0", + "pify": "^4.0.1" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + } + } + }, + "less-plugin-autoprefix": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/less-plugin-autoprefix/-/less-plugin-autoprefix-2.0.0.tgz", + "integrity": "sha512-UktaMtHAhCVTIQxjlceo1MiiQiUZws7gi3hfcpxkCLn1CJdVdlRGCb4n1/M64hniw2DLd7YzOl7ojshs1tWYcQ==", + "dev": true, + "requires": { + "autoprefixer": "^8.6.3", + "postcss": "^6.0.22" + }, + "dependencies": { + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "liftoff": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz", + "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==", + "dev": true, + "requires": { + "extend": "^3.0.0", + "findup-sync": "^3.0.0", + "fined": "^1.0.1", + "flagged-respawn": "^1.0.0", + "is-plain-object": "^2.0.4", + "object.map": "^1.0.0", + "rechoir": "^0.6.2", + "resolve": "^1.1.7" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==" + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "dependencies": { + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + } + } + }, + "lodash.clone": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", + "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=", + "dev": true + }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=", + "dev": true + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lodash.partialright": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.partialright/-/lodash.partialright-4.2.1.tgz", + "integrity": "sha1-ATDYDoM2MmTUAHTzKbij56ihzEs=", + "dev": true + }, + "lodash.pick": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=", + "dev": true + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, + "longest": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", + "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", + "dev": true + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "lru-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", + "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=", + "dev": true, + "requires": { + "es5-ext": "~0.10.2" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + } + } + }, + "make-error": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", + "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", + "dev": true + }, + "make-error-cause": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/make-error-cause/-/make-error-cause-1.2.2.tgz", + "integrity": "sha1-3wOI/NCzeBbf8KX7gQiTl3fcvJ0=", + "dev": true, + "requires": { + "make-error": "^1.2.0" + } + }, + "make-iterator": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", + "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "mamacro": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/mamacro/-/mamacro-0.0.3.tgz", + "integrity": "sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA==" + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "requires": { + "object-visit": "^1.0.0" + } + }, + "matchdep": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", + "integrity": "sha1-xvNINKDY28OzfCfui7yyfHd1WC4=", + "dev": true, + "requires": { + "findup-sync": "^2.0.0", + "micromatch": "^3.0.4", + "resolve": "^1.4.0", + "stack-trace": "0.0.10" + }, + "dependencies": { + "findup-sync": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", + "dev": true, + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^3.1.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + } + }, + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "memoizee": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.14.tgz", + "integrity": "sha512-/SWFvWegAIYAO4NQMpcX+gcra0yEZu4OntmUdrBaWrJncxOqAziGFlHxc7yjKVK2uu3lpPW27P27wkR82wA8mg==", + "dev": true, + "requires": { + "d": "1", + "es5-ext": "^0.10.45", + "es6-weak-map": "^2.0.2", + "event-emitter": "^0.3.5", + "is-promise": "^2.1", + "lru-queue": "0.1", + "next-tick": "1", + "timers-ext": "^0.1.5" + } + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "merge-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-1.0.1.tgz", + "integrity": "sha1-QEEgLVCKNCugAXQAjfDCUbjBNeE=", + "dev": true, + "requires": { + "readable-stream": "^2.0.1" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "optional": true + }, + "mime-db": { + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", + "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==", + "optional": true + }, + "mime-types": { + "version": "2.1.26", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz", + "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==", + "optional": true, + "requires": { + "mime-db": "1.43.0" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "mute-stdout": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", + "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==", + "dev": true + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "neo-async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", + "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==" + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", + "dev": true + }, + "node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + } + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true + }, + "now-and-later": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz", + "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==", + "dev": true, + "requires": { + "once": "^1.3.2" + } + }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", + "dev": true + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.defaults": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", + "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", + "dev": true, + "requires": { + "array-each": "^1.0.1", + "array-slice": "^1.0.0", + "for-own": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "object.map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", + "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", + "dev": true, + "requires": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "requires": { + "isobject": "^3.0.1" + } + }, + "object.reduce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz", + "integrity": "sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60=", + "dev": true, + "requires": { + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onecolor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/onecolor/-/onecolor-3.1.0.tgz", + "integrity": "sha512-YZSypViXzu3ul5LMu/m6XjJ9ol8qAy9S2VjHl5E6UlhUH1KGKWabyEJifn0Jjpw23bYDzC2ucKMPGiH5kfwSGQ==", + "dev": true + }, + "ordered-read-streams": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz", + "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=", + "dev": true, + "requires": { + "readable-stream": "^2.0.1" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, + "requires": { + "lcid": "^1.0.0" + } + }, + "p-limit": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", + "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, + "parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "requires": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "parse-asn1": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", + "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-filepath": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", + "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", + "dev": true, + "requires": { + "is-absolute": "^1.0.0", + "map-cache": "^0.2.0", + "path-root": "^0.1.1" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "parse-node-version": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", + "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", + "dev": true + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" + }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==" + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-root": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", + "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", + "dev": true, + "requires": { + "path-root-regex": "^0.1.0" + } + }, + "path-root-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", + "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=", + "dev": true + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pbkdf2": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", + "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "optional": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "requires": { + "find-up": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + } + } + }, + "pleeease-filters": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pleeease-filters/-/pleeease-filters-4.0.0.tgz", + "integrity": "sha1-ZjKy+wVkjSdY2GU4T7zteeHMrsc=", + "dev": true, + "requires": { + "onecolor": "^3.0.4", + "postcss": "^6.0.1" + }, + "dependencies": { + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "plugin-error": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", + "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", + "requires": { + "ansi-colors": "^1.0.1", + "arr-diff": "^4.0.0", + "arr-union": "^3.1.0", + "extend-shallow": "^3.0.2" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" + }, + "postcss": { + "version": "7.0.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.26.tgz", + "integrity": "sha512-IY4oRjpXWYshuTDFxMVkJDtWIk2LhsTlu8bZnbEJA4+bYT16Lvpo8Qv6EvDumhYRgzjZl489pmsY3qVgJQ08nA==", + "dev": true, + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + } + }, + "postcss-color-rgba-fallback": { + "version": "git+https://github.com/postcss/postcss-color-rgba-fallback.git#96cda7e1587175322dcbfee601e3f6eb110899f3", + "from": "git+https://github.com/postcss/postcss-color-rgba-fallback.git", + "dev": true, + "requires": { + "postcss": "^7.0.17", + "postcss-value-parser": "^4.0.2", + "rgb-hex": "^3.0.0" + } + }, + "postcss-filter-gradient": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postcss-filter-gradient/-/postcss-filter-gradient-1.0.0.tgz", + "integrity": "sha1-68KOUX8yzjZVHxUwLqTXihZMyIY=", + "dev": true, + "requires": { + "filter-gradient": "^1.0.1", + "postcss": "^6.0.0" + }, + "dependencies": { + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-load-config": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.0.tgz", + "integrity": "sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q==", + "dev": true, + "requires": { + "cosmiconfig": "^5.0.0", + "import-cwd": "^2.0.0" + } + }, + "postcss-opacity": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-opacity/-/postcss-opacity-5.0.0.tgz", + "integrity": "sha512-n6LgHk5HWIsyEHgPqM2jwXrkh4SuH+cZOIWh4tUp4ug3P7FkzxiJuqrpEaBvNwH/dKs5PHjHL2vPeR+nLbs+Mw==", + "dev": true, + "requires": { + "postcss": "^6.0.7" + }, + "dependencies": { + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-value-parser": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.3.tgz", + "integrity": "sha512-N7h4pG+Nnu5BEIzyeaaIYWs0LI5XC40OrRh5L60z0QjFsqGWcHcbkBvpe1WYpcIS9yQ8sOi/vIPt1ejQCrMVrg==", + "dev": true + }, + "pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", + "dev": true + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "optional": true, + "requires": { + "asap": "~2.0.3" + } + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=" + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" + }, + "psl": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz", + "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==", + "optional": true + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "pumpify": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", + "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "optional": true + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "^1.1.6" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "remove-bom-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz", + "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", + "dev": true, + "requires": { + "is-buffer": "^1.1.5", + "is-utf8": "^0.2.1" + } + }, + "remove-bom-stream": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz", + "integrity": "sha1-BfGlk/FuQuH7kOv1nejlaVJflSM=", + "dev": true, + "requires": { + "remove-bom-buffer": "^3.0.0", + "safe-buffer": "^5.1.0", + "through2": "^2.0.3" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", + "dev": true + }, + "replace-homedir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz", + "integrity": "sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw=", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1", + "is-absolute": "^1.0.0", + "remove-trailing-separator": "^1.1.0" + } + }, + "replacestream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/replacestream/-/replacestream-4.0.3.tgz", + "integrity": "sha512-AC0FiLS352pBBiZhd4VXB1Ab/lh0lEgpP+GGvZqbQh8a5cmXVoTe5EX/YeTFArnp4SRGTHh1qCHu9lGs1qG8sA==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.3", + "object-assign": "^4.0.1", + "readable-stream": "^2.0.2" + } + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "optional": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "resolve": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz", + "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, + "resolve-options": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz", + "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=", + "dev": true, + "requires": { + "value-or-function": "^3.0.0" + } + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" + }, + "rgb-hex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/rgb-hex/-/rgb-hex-3.0.0.tgz", + "integrity": "sha512-8h7ZcwxCBDKvchSWbWngJuSCqJGQ6nDuLLg+QcRyQDbX9jMWt+PpPeXAhSla0GOooEomk3lCprUpGkMdsLjKyg==", + "dev": true + }, + "right-align": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", + "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", + "dev": true, + "requires": { + "align-text": "^0.1.1" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "requires": { + "aproba": "^1.1.1" + } + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "optional": true + }, + "schema-utils": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.4.tgz", + "integrity": "sha512-VNjcaUxVnEeun6B2fiiUDjXXBtD4ZSH7pdbfIu1pOFwgptDPLMo/z9jr4sUfsjFVPqDCEin/F7IYlq7/E6yDbQ==", + "requires": { + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "semver-greatest-satisfied-range": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", + "integrity": "sha1-E+jCZYq5aRywzXEJMkAoDTb3els=", + "dev": true, + "requires": { + "sver-compat": "^1.5.0" + } + }, + "serialize-javascript": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", + "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==" + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "source-map-resolve": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", + "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "requires": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz", + "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" + }, + "sparkles": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", + "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==", + "dev": true + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "optional": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "stable": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", + "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==", + "dev": true + }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "dev": true + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-exhaust": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", + "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==", + "dev": true + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + }, + "strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI=", + "dev": true + }, + "style-loader": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.1.3.tgz", + "integrity": "sha512-rlkH7X/22yuwFYK357fMN/BxYOorfnfq0eD7+vqlemSK4wEcejFF1dg4zxP0euBW8NrYx2WZzZ8PPFevr7D+Kw==", + "requires": { + "loader-utils": "^1.2.3", + "schema-utils": "^2.6.4" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "sver-compat": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz", + "integrity": "sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg=", + "dev": true, + "requires": { + "es6-iterator": "^2.0.1", + "es6-symbol": "^3.1.1" + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" + }, + "terser": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.3.tgz", + "integrity": "sha512-Lw+ieAXmY69d09IIc/yqeBqXpEQIpDGZqT34ui1QWXIUpR2RjbqEkT8X7Lgex19hslSqcWM5iMN2kM11eMsESQ==", + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + } + }, + "terser-webpack-plugin": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz", + "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==", + "requires": { + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^2.1.2", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "textextensions": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-2.6.0.tgz", + "integrity": "sha512-49WtAWS+tcsy93dRt6P0P3AMD2m5PvXRhuEA0kaXos5ZLlujtYmpmFsB+QvWUSxE1ZsstmYXfQ7L40+EcQgpAQ==", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "requires": { + "readable-stream": "^2.1.5", + "xtend": "~4.0.1" + } + }, + "through2-filter": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz", + "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==", + "dev": true, + "requires": { + "through2": "~2.0.0", + "xtend": "~4.0.0" + } + }, + "time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", + "dev": true + }, + "timers-browserify": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", + "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==", + "requires": { + "setimmediate": "^1.0.4" + } + }, + "timers-ext": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", + "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", + "dev": true, + "requires": { + "es5-ext": "~0.10.46", + "next-tick": "1" + } + }, + "to-absolute-glob": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", + "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=", + "dev": true, + "requires": { + "is-absolute": "^1.0.0", + "is-negated-glob": "^1.0.0" + } + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=" + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "to-through": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz", + "integrity": "sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY=", + "dev": true, + "requires": { + "through2": "^2.0.3" + } + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "optional": true, + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "optional": true + } + } + }, + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "optional": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true + }, + "type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "dev": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "uglify-js": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.7.7.tgz", + "integrity": "sha512-FeSU+hi7ULYy6mn8PKio/tXsdSXN35lm4KgV2asx00kzrLU9Pi3oAslcJT70Jdj7PHX29gGUPOT6+lXGBbemhA==", + "dev": true, + "requires": { + "commander": "~2.20.3", + "source-map": "~0.6.1" + } + }, + "uglify-to-browserify": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", + "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", + "dev": true, + "optional": true + }, + "unc-path-regex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", + "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", + "dev": true + }, + "undertaker": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.2.1.tgz", + "integrity": "sha512-71WxIzDkgYk9ZS+spIB8iZXchFhAdEo2YU8xYqBYJ39DIUIqziK78ftm26eecoIY49X0J2MLhG4hr18Yp6/CMA==", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "bach": "^1.0.0", + "collection-map": "^1.0.0", + "es6-weak-map": "^2.0.1", + "last-run": "^1.1.0", + "object.defaults": "^1.0.0", + "object.reduce": "^1.0.0", + "undertaker-registry": "^1.0.0" + } + }, + "undertaker-registry": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz", + "integrity": "sha1-XkvaMI5KiirlhPm5pDWaSZglzFA=", + "dev": true + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" + } + } + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unique-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz", + "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==", + "dev": true, + "requires": { + "json-stable-stringify-without-jsonify": "^1.0.1", + "through2-filter": "^3.0.0" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" + } + } + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + } + } + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "optional": true + }, + "v8flags": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.3.tgz", + "integrity": "sha512-amh9CCg3ZxkzQ48Mhcb8iX7xpAfYJgePHxWMQCBWECpOSqJUXgY26ncA61UTV0BkPqfhcy6mzwCIoP4ygxpW8w==", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "value-or-function": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz", + "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=", + "dev": true + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "optional": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vinyl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", + "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", + "dev": true, + "requires": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + } + }, + "vinyl-fs": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz", + "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==", + "dev": true, + "requires": { + "fs-mkdirp-stream": "^1.0.0", + "glob-stream": "^6.1.0", + "graceful-fs": "^4.0.0", + "is-valid-glob": "^1.0.0", + "lazystream": "^1.0.0", + "lead": "^1.0.0", + "object.assign": "^4.0.4", + "pumpify": "^1.3.5", + "readable-stream": "^2.3.3", + "remove-bom-buffer": "^3.0.0", + "remove-bom-stream": "^1.2.0", + "resolve-options": "^1.1.0", + "through2": "^2.0.0", + "to-through": "^2.0.0", + "value-or-function": "^3.0.0", + "vinyl": "^2.0.0", + "vinyl-sourcemap": "^1.1.0" + } + }, + "vinyl-sourcemap": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz", + "integrity": "sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY=", + "dev": true, + "requires": { + "append-buffer": "^1.0.2", + "convert-source-map": "^1.5.0", + "graceful-fs": "^4.1.6", + "normalize-path": "^2.1.1", + "now-and-later": "^2.0.0", + "remove-bom-buffer": "^3.0.0", + "vinyl": "^2.0.0" + } + }, + "vinyl-sourcemaps-apply": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", + "integrity": "sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU=", + "requires": { + "source-map": "^0.5.1" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==" + }, + "watchpack": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", + "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", + "requires": { + "chokidar": "^2.0.2", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" + } + }, + "webpack": { + "version": "4.41.5", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.5.tgz", + "integrity": "sha512-wp0Co4vpyumnp3KlkmpM5LWuzvZYayDwM2n17EHFr4qxBBbRokC7DJawPJC7TfSFZ9HZ6GsdH40EBj4UV0nmpw==", + "requires": { + "@webassemblyjs/ast": "1.8.5", + "@webassemblyjs/helper-module-context": "1.8.5", + "@webassemblyjs/wasm-edit": "1.8.5", + "@webassemblyjs/wasm-parser": "1.8.5", + "acorn": "^6.2.1", + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^4.1.0", + "eslint-scope": "^4.0.3", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.4.0", + "loader-utils": "^1.2.3", + "memory-fs": "^0.4.1", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.1", + "neo-async": "^2.6.1", + "node-libs-browser": "^2.2.1", + "schema-utils": "^1.0.0", + "tapable": "^1.1.3", + "terser-webpack-plugin": "^1.4.3", + "watchpack": "^1.6.0", + "webpack-sources": "^1.4.1" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + } + }, + "when": { + "version": "3.7.8", + "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz", + "integrity": "sha1-xxMLan6gRpPoQs3J56Hyqjmjn4I=", + "dev": true + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", + "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=", + "dev": true + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "dev": true + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true + }, + "worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "yargs": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz", + "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", + "dev": true, + "requires": { + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.0" + } + }, + "yargs-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz", + "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", + "dev": true, + "requires": { + "camelcase": "^3.0.0" + } + } + } +} diff --git a/nano/package.json b/nano/package.json index 9bfa118075b..6c81eede229 100644 --- a/nano/package.json +++ b/nano/package.json @@ -6,18 +6,23 @@ "es5-shim": "^4.5.12", "font-awesome": "^4.7.0", "gulp-clean-css": "^3.10.0", - "js-url": "git+https://github.com/websanova/js-url.git" + "js-url": "git+https://github.com/websanova/js-url.git", + "less": "^3.11.0", + "less-loader": "^5.0.0", + "style-loader": "^1.1.3", + "tslib": "^1.10.0", + "webpack": "^4.41.5" }, "devDependencies": { "fs-extra": "^7.0.1", - "gulp": "^4.0.0", + "gulp": "^4.0.2", "gulp-concat": "^2.6.1", "gulp-less": "^4.0.1", "gulp-order": "^1.2.0", "gulp-postcss": "^8.0.0", "gulp-replace": "^1.0.0", - "gulp-sourcemaps": "^2.6.4", - "gulp-uglify": "^3.0.1", + "gulp-sourcemaps": "^2.6.5", + "gulp-uglify": "^3.0.2", "less-plugin-autoprefix": "^2.0.0", "merge-stream": "^1.0.1", "pleeease-filters": "^4.0.0", diff --git a/paradise.dme b/paradise.dme index 62d9624cf5c..01420a30169 100644 --- a/paradise.dme +++ b/paradise.dme @@ -18,6 +18,7 @@ #include "code\__DEFINES\_globals.dm" #include "code\__DEFINES\_readme.dm" #include "code\__DEFINES\_tick.dm" +#include "code\__DEFINES\access.dm" #include "code\__DEFINES\admin.dm" #include "code\__DEFINES\antagonists.dm" #include "code\__DEFINES\atmospherics.dm" @@ -39,6 +40,8 @@ #include "code\__DEFINES\hydroponics.dm" #include "code\__DEFINES\inventory.dm" #include "code\__DEFINES\is_helpers.dm" +#include "code\__DEFINES\job.dm" +#include "code\__DEFINES\js.dm" #include "code\__DEFINES\language.dm" #include "code\__DEFINES\layers.dm" #include "code\__DEFINES\lighting.dm" @@ -65,6 +68,7 @@ #include "code\__DEFINES\station_goals.dm" #include "code\__DEFINES\status_effects.dm" #include "code\__DEFINES\subsystems.dm" +#include "code\__DEFINES\tools.dm" #include "code\__DEFINES\typeids.dm" #include "code\__DEFINES\vv.dm" #include "code\__DEFINES\zlevel.dm" @@ -89,6 +93,7 @@ #include "code\__HELPERS\sanitize_values.dm" #include "code\__HELPERS\text.dm" #include "code\__HELPERS\time.dm" +#include "code\__HELPERS\tool_helpers.dm" #include "code\__HELPERS\traits.dm" #include "code\__HELPERS\type2type.dm" #include "code\__HELPERS\typelists.dm" @@ -869,7 +874,6 @@ #include "code\game\objects\items\devices\lightreplacer.dm" #include "code\game\objects\items\devices\machineprototype.dm" #include "code\game\objects\items\devices\megaphone.dm" -#include "code\game\objects\items\devices\multitool.dm" #include "code\game\objects\items\devices\paicard.dm" #include "code\game\objects\items\devices\pipe_painter.dm" #include "code\game\objects\items\devices\pizza_bomb.dm" @@ -917,6 +921,13 @@ #include "code\game\objects\items\stacks\sheets\sheets.dm" #include "code\game\objects\items\stacks\tiles\tile_mineral.dm" #include "code\game\objects\items\stacks\tiles\tile_types.dm" +#include "code\game\objects\items\tools\crowbar.dm" +#include "code\game\objects\items\tools\multitool.dm" +#include "code\game\objects\items\tools\screwdriver.dm" +#include "code\game\objects\items\tools\tool_behaviour.dm" +#include "code\game\objects\items\tools\welder.dm" +#include "code\game\objects\items\tools\wirecutters.dm" +#include "code\game\objects\items\tools\wrench.dm" #include "code\game\objects\items\weapons\AI_modules.dm" #include "code\game\objects\items\weapons\alien_specific.dm" #include "code\game\objects\items\weapons\bee_briefcase.dm" @@ -926,6 +937,7 @@ #include "code\game\objects\items\weapons\chrono_eraser.dm" #include "code\game\objects\items\weapons\cigs.dm" #include "code\game\objects\items\weapons\clown_items.dm" +#include "code\game\objects\items\weapons\conversion_kit.dm" #include "code\game\objects\items\weapons\cosmetics.dm" #include "code\game\objects\items\weapons\courtroom.dm" #include "code\game\objects\items\weapons\defib.dm" @@ -970,7 +982,6 @@ #include "code\game\objects\items\weapons\tape.dm" #include "code\game\objects\items\weapons\teleportation.dm" #include "code\game\objects\items\weapons\teleprod.dm" -#include "code\game\objects\items\weapons\tools.dm" #include "code\game\objects\items\weapons\twohanded.dm" #include "code\game\objects\items\weapons\vending_items.dm" #include "code\game\objects\items\weapons\weaponry.dm" @@ -1152,8 +1163,6 @@ #include "code\game\verbs\randomtip.dm" #include "code\game\verbs\suicide.dm" #include "code\game\verbs\who.dm" -#include "code\js\byjax.dm" -#include "code\js\menus.dm" #include "code\LINDA\LINDA_fire.dm" #include "code\LINDA\LINDA_system.dm" #include "code\LINDA\LINDA_turf_tile.dm" diff --git a/rust_g.dll b/rust_g.dll index 80d09ac19ca..9707322d7d1 100644 Binary files a/rust_g.dll and b/rust_g.dll differ diff --git a/sound/goonstation/voice/deathgasp_1.ogg b/sound/goonstation/voice/deathgasp_1.ogg new file mode 100644 index 00000000000..71788a14f45 Binary files /dev/null and b/sound/goonstation/voice/deathgasp_1.ogg differ diff --git a/sound/goonstation/voice/deathgasp_2.ogg b/sound/goonstation/voice/deathgasp_2.ogg new file mode 100644 index 00000000000..38daa763fc4 Binary files /dev/null and b/sound/goonstation/voice/deathgasp_2.ogg differ diff --git a/sound/goonstation/voice/female_dying_gasp_1.ogg b/sound/goonstation/voice/female_dying_gasp_1.ogg new file mode 100644 index 00000000000..0e329374617 Binary files /dev/null and b/sound/goonstation/voice/female_dying_gasp_1.ogg differ diff --git a/sound/goonstation/voice/female_dying_gasp_2.ogg b/sound/goonstation/voice/female_dying_gasp_2.ogg new file mode 100644 index 00000000000..76857f4ee92 Binary files /dev/null and b/sound/goonstation/voice/female_dying_gasp_2.ogg differ diff --git a/sound/goonstation/voice/female_dying_gasp_3.ogg b/sound/goonstation/voice/female_dying_gasp_3.ogg new file mode 100644 index 00000000000..e6e0898062c Binary files /dev/null and b/sound/goonstation/voice/female_dying_gasp_3.ogg differ diff --git a/sound/goonstation/voice/female_dying_gasp_4.ogg b/sound/goonstation/voice/female_dying_gasp_4.ogg new file mode 100644 index 00000000000..516f763f864 Binary files /dev/null and b/sound/goonstation/voice/female_dying_gasp_4.ogg differ diff --git a/sound/goonstation/voice/female_dying_gasp_5.ogg b/sound/goonstation/voice/female_dying_gasp_5.ogg new file mode 100644 index 00000000000..cb076af860a Binary files /dev/null and b/sound/goonstation/voice/female_dying_gasp_5.ogg differ diff --git a/sound/goonstation/voice/gasp.ogg b/sound/goonstation/voice/gasp.ogg new file mode 100644 index 00000000000..b1fe8bc177f Binary files /dev/null and b/sound/goonstation/voice/gasp.ogg differ diff --git a/sound/goonstation/voice/male_dying_gasp_1.ogg b/sound/goonstation/voice/male_dying_gasp_1.ogg new file mode 100644 index 00000000000..767d6ecb995 Binary files /dev/null and b/sound/goonstation/voice/male_dying_gasp_1.ogg differ diff --git a/sound/goonstation/voice/male_dying_gasp_2.ogg b/sound/goonstation/voice/male_dying_gasp_2.ogg new file mode 100644 index 00000000000..8a545f53265 Binary files /dev/null and b/sound/goonstation/voice/male_dying_gasp_2.ogg differ diff --git a/sound/goonstation/voice/male_dying_gasp_3.ogg b/sound/goonstation/voice/male_dying_gasp_3.ogg new file mode 100644 index 00000000000..acfbfe913ec Binary files /dev/null and b/sound/goonstation/voice/male_dying_gasp_3.ogg differ diff --git a/sound/goonstation/voice/male_dying_gasp_4.ogg b/sound/goonstation/voice/male_dying_gasp_4.ogg new file mode 100644 index 00000000000..516f763f864 Binary files /dev/null and b/sound/goonstation/voice/male_dying_gasp_4.ogg differ diff --git a/sound/goonstation/voice/male_dying_gasp_5.ogg b/sound/goonstation/voice/male_dying_gasp_5.ogg new file mode 100644 index 00000000000..cb076af860a Binary files /dev/null and b/sound/goonstation/voice/male_dying_gasp_5.ogg differ diff --git a/sound/items/PDA/__credits.dm b/sound/items/PDA/__credits.dm index 4814eb325b5..5012fea2b34 100644 --- a/sound/items/PDA/__credits.dm +++ b/sound/items/PDA/__credits.dm @@ -7,4 +7,4 @@ //ambicha4-short.ogg /*4-second excerpt from sound/ambience/ambicha4.ogg -*/ \ No newline at end of file +*/ diff --git a/tools/travis/README.MD b/tools/travis/README.MD new file mode 100644 index 00000000000..3079e9bfa43 --- /dev/null +++ b/tools/travis/README.MD @@ -0,0 +1,3 @@ +# Info + +All scripts utilised by Travis CI should be placed into this folder for the sake of maintainability, and knowing where stuff is \ No newline at end of file diff --git a/tools/travis/check_grep.sh b/tools/travis/check_grep.sh new file mode 100644 index 00000000000..22d02647bcf --- /dev/null +++ b/tools/travis/check_grep.sh @@ -0,0 +1,34 @@ +#!/bin/bash +set -euo pipefail + +#nb: must be bash to support shopt globstar +shopt -s globstar + +st=0 + +if grep -El '^\".+\" = \(.+\)' _maps/**/*.dmm; then + echo "ERROR: Non-TGM formatted map detected. Please convert it using Map Merger!" + st=1 +fi; +if grep -P 'step_[xy]' _maps/**/*.dmm; then + echo "ERROR: step_x/step_y variables detected in maps, please remove them." + st=1 +fi; +#### TODO: Uncomment after managing all globs #### +#if grep -P '^/*var/' code/**/*.dm; then + #echo "ERROR: Unmanaged global var use detected in code, please use the helpers." + #st=1 +#fi; +#### +nl=' +' +nl=$'\n' +while read f; do + t=$(tail -c2 "$f"; printf x); r1="${nl}$"; r2="${nl}${r1}" + if [[ ! ${t%x} =~ $r1 ]]; then + echo "file $f is missing a trailing newline" + st=1 + fi; +done < <(find . -type f -name '*.dm') + +exit $st \ No newline at end of file diff --git a/tools/travis/check_line_endings.py b/tools/travis/check_line_endings.py new file mode 100644 index 00000000000..86c2a8b77c7 --- /dev/null +++ b/tools/travis/check_line_endings.py @@ -0,0 +1,49 @@ + +#!/usr/bin/env python + +import os +import sys +import glob + +WINDOWS_NEWLINE = b'\r\n' + +FILES_TO_READ = [] +FILES_TO_READ.extend(glob.glob(r"**/*.dm", recursive=True)) +FILES_TO_READ.extend(glob.glob(r"**/*.dmm", recursive=True)) +FILES_TO_READ.extend(glob.glob(r"*.dme")) +#for i in FILES_TO_READ: +# if os.path.isdir(i): +# FILES_TO_READ.remove(i) + + +def _reader(filepath): + data = open(filepath, "rb") + return data + + +def main(): + filelist = [] + foundfiles = False + + for file in FILES_TO_READ: + data = _reader(file) + lines = data.readlines() + for line in lines: + if line.endswith(WINDOWS_NEWLINE): + filelist.append(file) + foundfiles = True + break + data.close() + + if not foundfiles: + print("No CRLF files found.") + sys.exit(0) + else: + print("Found files with suspected CRLF type.") + for i in filelist: + print(i) + sys.exit(1) + + +if __name__ == "__main__": + main()