This commit is contained in:
LetterJay
2017-10-01 12:22:41 -05:00
54 changed files with 67595 additions and 4694 deletions
+153 -146
View File
@@ -1,147 +1,154 @@
##Citadel Station 13 <BR>
Based and maintained from /tg/station.<BR>
[![Build Status](https://api.travis-ci.org/Citadel-Station-13/Citadel-Station-13.png)](https://travis-ci.org/Citadel-Station-13/Citadel-Station-13) [![Krihelimeter](http://www.krihelinator.xyz/badge/Citadel-Station-13/Citadel-Station-13)](http://www.krihelinator.xyz)
[![Percentage of issues still open](http://isitmaintained.com/badge/open/Citadel-Station-13/Citadel-Station-13.svg)](http://isitmaintained.com/project/Citadel-Station-13/Citadel-Station-13 "Percentage of issues still open") [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/Citadel-Station-13/Citadel-Station-13.svg)](http://isitmaintained.com/project/Citadel-Station-13/Citadel-Station-13 "Average time to resolve an issue")
**Upstream Information** <BR>
**Website:** http://www.tgstation13.org <BR>
**Code:** https://github.com/tgstation/tgstation <BR>
**Wiki** http://tgstation13.org/wiki/Main_Page <BR>
**IRC:** irc://irc.rizon.net/coderbus or if you dont have an IRC client, you can click [here](https://kiwiirc.com/client/irc.rizon.net:6667/?&theme=cli#coderbus).<BR>
**Citadel Station Information** <BR>
**Forums:** http://citadel-station.net/forum/ <BR>
**Ban Appeals:** http://citadel-station.net/forum/forumdisplay.php?fid=8 <BR>
**Code:** https://github.com/Citadel-Station-13/Citadel-Station-13 <BR>
**Discord:** [Here](https://discord.gg/3gJ9pnM). <BR>
## DOWNLOADING
There are a number of ways to download the source code. Some are described here, an alternative all-inclusive guide is also located at http://www.tgstation13.org/wiki/Downloading_the_source_code
Option 1:
Follow this: http://www.tgstation13.org/wiki/Setting_up_git
##Citadel Station 13 <BR>
Based and maintained from /tg/station.<BR>
[![Build Status](https://api.travis-ci.org/Citadel-Station-13/Citadel-Station-13.png)](https://travis-ci.org/Citadel-Station-13/Citadel-Station-13) [![Krihelimeter](http://www.krihelinator.xyz/badge/Citadel-Station-13/Citadel-Station-13)](http://www.krihelinator.xyz)
[![Percentage of issues still open](http://isitmaintained.com/badge/open/Citadel-Station-13/Citadel-Station-13.svg)](http://isitmaintained.com/project/Citadel-Station-13/Citadel-Station-13 "Percentage of issues still open") [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/Citadel-Station-13/Citadel-Station-13.svg)](http://isitmaintained.com/project/Citadel-Station-13/Citadel-Station-13 "Average time to resolve an issue")
**Upstream Information** <BR>
**Website:** http://www.tgstation13.org <BR>
**Code:** https://github.com/tgstation/tgstation <BR>
**Wiki** http://tgstation13.org/wiki/Main_Page <BR>
**IRC:** irc://irc.rizon.net/coderbus or if you dont have an IRC client, you can click [here](https://kiwiirc.com/client/irc.rizon.net:6667/?&theme=cli#coderbus).<BR>
**Citadel Station Information** <BR>
**Forums:** http://citadel-station.net/forum/ <BR>
**Ban Appeals:** http://citadel-station.net/forum/forumdisplay.php?fid=8 <BR>
**Code:** https://github.com/Citadel-Station-13/Citadel-Station-13 <BR>
**Discord:** [Here](https://discord.gg/3gJ9pnM). <BR>
## DOWNLOADING
There are a number of ways to download the source code. Some are described here, an alternative all-inclusive guide is also located at http://www.tgstation13.org/wiki/Downloading_the_source_code
Option 1:
Follow this: http://www.tgstation13.org/wiki/Setting_up_git
Option 2: Download the source code as a zip by clicking the ZIP button in the
code tab of https://github.com/tgstation/tgstation
(note: this will use a lot of bandwidth if you wish to update and is a lot of
hassle if you want to make any changes at all, so it's not recommended.)
## INSTALLATION
First-time installation should be fairly straightforward. First, you'll need
BYOND installed. You can get it from http://www.byond.com/. Once you've done
that, extract the game files to wherever you want to keep them. This is a
sourcecode-only release, so the next step is to compile the server files.
Open tgstation.dme by double-clicking it, open the Build menu, and click
compile. This'll take a little while, and if everything's done right you'll get
a message like this:
```
saving tgstation.dmb (DEBUG mode)
tgstation.dmb - 0 errors, 0 warnings
```
If you see any errors or warnings, something has gone wrong - possibly a corrupt
download or the files extracted wrong. If problems persist, ask for assistance
in irc://irc.rizon.net/coderbus
Once that's done, open up the config folder. You'll want to edit config.txt to
set the probabilities for different gamemodes in Secret and to set your server
location so that all your players don't get disconnected at the end of each
round. It's recommended you don't turn on the gamemodes with probability 0,
except Extended, as they have various issues and aren't currently being tested,
so they may have unknown and bizarre bugs. Extended is essentially no mode, and
isn't in the Secret rotation by default as it's just not very fun.
You'll also want to edit config/admins.txt to remove the default admins and add
your own. "Game Master" is the highest level of access, and probably the one
you'll want to use for now. You can set up your own ranks and find out more in
config/admin_ranks.txt
The format is
```
byondkey = Rank
((i.e. spessmans = Host ))
```
where the admin rank must be properly capitalised.
Finally, to start the server, run Dream Daemon and enter the path to your
compiled tgstation.dmb file. Make sure to set the port to the one you
specified in the config.txt, and set the Security box to 'Safe'. Then press GO
and the server should start up and be ready to join. It is also recommended that
you set up the SQL backend (see below).
## UPDATING
To update an existing installation, first back up your /config and /data folders
as these store your server configuration, player preferences and banlist.
Then, extract the new files (preferably into a clean directory, but updating in
place should work fine), copy your /config and /data folders back into the new
install, overwriting when prompted except if we've specified otherwise, and
recompile the game. Once you start the server up again, you should be running
the new version.
## MAPS
Citadel Station maintains their own map, but frequently uses /tg/station's currently maintained maps as well.
* [tgstation2 (default)](http://tgstation13.org/wiki/Boxstation)
* [MetaStation](https://tgstation13.org/wiki/MetaStation)
* [DeltaStation](https://tgstation13.org/wiki/DeltaStation)
* [OmegaStation](https://tgstation13.org/wiki/OmegaStation)
* [PubbyStation](https://tgstation13.org/wiki/PubbyStation)
All maps have their own code file that is in the base of the _maps directory. Maps are loaded dynamically when the game starts. Follow this guideline when adding your own map, to your fork, for easy compatibility.
The map that will be loaded for the upcoming round is determined by reading data/next_map.json, which is a copy of the json files found in the _maps tree. If this file does not exist, the default map from config/maps.txt will be loaded. Failing that, tgstation2 will be loaded. If you want to set a specific map to load next round you can use the Change Map verb in game before restarting the server or copy a json from _maps to data/next_map.json before starting the server. Also, for debugging purposes, ticking a corresponding map's code file in Dream Maker will force that map to load every round.
If you are hosting a server, and want randomly picked maps to be played each round, you can enable map rotation in [config.txt](config/config.txt) and then set the maps to be picked in the [maps.txt](config/maps.txt) file.
Anytime you want to make changes to a map it's imperative you use the [Map Merging tools](http://tgstation13.org/wiki/Map_Merger)
## AWAY MISSIONS
/tg/station supports loading away missions however they are disabled by default.
Map files for away missions are located in the _maps/RandomZLevels directory. Each away mission includes it's own code definitions located in /code/modules/awaymissions/mission_code. These files must be included and compiled with the server beforehand otherwise the server will crash upon trying to load away missions that lack their code.
To enable an away mission open `config/awaymissionconfig.txt` and uncomment one of the .dmm lines by removing the #. If more than one away mission is uncommented then the away mission loader will randomly select one the enabled ones to load.
## SQL SETUP
The SQL backend requires a MySQL server. SQL is required for the library, stats tracking, admin notes, and job-only bans, among other features, mostly related to server administration. Your server details go in /config/dbconfig.txt, and the SQL schema is in /SQL/tgstation_schema.sql and /SQL/tgstation_schema_prefix.sql depending on if you want table prefixes. More detailed setup instructions are located here: http://www.tgstation13.org/wiki/Downloading_the_source_code#Setting_up_the_database
## IRC BOT SETUP
Included in the repository is a python3 compatible IRC bot capable of relaying adminhelps to a specified
IRC channel/server, see the /bot folder for more
## CONTRIBUTING
Please see [CONTRIBUTING.md](.github/CONTRIBUTING.md)
## LICENSE
All code after [commit 333c566b88108de218d882840e61928a9b759d8f on 2014/31/12 at 4:38 PM PST](https://github.com/tgstation/tgstation/commit/333c566b88108de218d882840e61928a9b759d8f) is licensed under [GNU AGPL v3](http://www.gnu.org/licenses/agpl-3.0.html).
All code before [commit 333c566b88108de218d882840e61928a9b759d8f on 2014/31/12 at 4:38 PM PST](https://github.com/tgstation/tgstation/commit/333c566b88108de218d882840e61928a9b759d8f) is licensed under [GNU GPL v3](https://www.gnu.org/licenses/gpl-3.0.html).
(Including tools unless their readme specifies otherwise.)
See LICENSE-AGPLv3.txt and LICENSE-GPLv3.txt for more details.
tgui clientside is licensed as a subproject under the MIT license.
Font Awesome font files, used by tgui, are licensed under the SIL Open Font License v1.1
tgui assets are licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/).
See tgui/LICENSE.md for the MIT license.
See tgui/assets/fonts/SIL-OFL-1.1-LICENSE.md for the SIL Open Font License.
All assets including icons and sound are under a [Creative Commons 3.0 BY-SA license](http://creativecommons.org/licenses/by-sa/3.0/) unless otherwise indicated.
code tab of https://github.com/tgstation/tgstation
(note: this will use a lot of bandwidth if you wish to update and is a lot of
hassle if you want to make any changes at all, so it's not recommended.)
## INSTALLATION
First-time installation should be fairly straightforward. First, you'll need
BYOND installed. You can get it from http://www.byond.com/. Once you've done
that, extract the game files to wherever you want to keep them. This is a
sourcecode-only release, so the next step is to compile the server files.
Open tgstation.dme by double-clicking it, open the Build menu, and click
compile. This'll take a little while, and if everything's done right you'll get
a message like this:
```
saving tgstation.dmb (DEBUG mode)
tgstation.dmb - 0 errors, 0 warnings
```
If you see any errors or warnings, something has gone wrong - possibly a corrupt
download or the files extracted wrong. If problems persist, ask for assistance
in irc://irc.rizon.net/coderbus
Once that's done, open up the config folder. You'll want to edit config.txt to
set the probabilities for different gamemodes in Secret and to set your server
location so that all your players don't get disconnected at the end of each
round. It's recommended you don't turn on the gamemodes with probability 0,
except Extended, as they have various issues and aren't currently being tested,
so they may have unknown and bizarre bugs. Extended is essentially no mode, and
isn't in the Secret rotation by default as it's just not very fun.
You'll also want to edit config/admins.txt to remove the default admins and add
your own. "Game Master" is the highest level of access, and probably the one
you'll want to use for now. You can set up your own ranks and find out more in
config/admin_ranks.txt
The format is
```
byondkey = Rank
```
where the admin rank must be properly capitalised.
Finally, to start the server, run Dream Daemon and enter the path to your
compiled tgstation.dmb file. Make sure to set the port to the one you
specified in the config.txt, and set the Security box to 'Safe'. Then press GO
and the server should start up and be ready to join. It is also recommended that
you set up the SQL backend (see below).
## UPDATING
To update an existing installation, first back up your /config and /data folders
as these store your server configuration, player preferences and banlist.
Then, extract the new files (preferably into a clean directory, but updating in
place should work fine), copy your /config and /data folders back into the new
install, overwriting when prompted except if we've specified otherwise, and
recompile the game. Once you start the server up again, you should be running
the new version.
## HOSTING
If you'd like a more robust server hosting option for tgstation and its
derivatives. Check out our server tools suite at
https://github.com/tgstation/tgstation-server
## MAPS
/tg/station currently comes equipped with five maps.
* [BoxStation (default)](http://tgstation13.org/wiki/Boxstation)
* [MetaStation](https://tgstation13.org/wiki/MetaStation)
* [DeltaStation](https://tgstation13.org/wiki/DeltaStation)
* [OmegaStation](https://tgstation13.org/wiki/OmegaStation)
* [PubbyStation](https://tgstation13.org/wiki/PubbyStation)
All maps have their own code file that is in the base of the _maps directory. Maps are loaded dynamically when the game starts. Follow this guideline when adding your own map, to your fork, for easy compatibility.
The map that will be loaded for the upcoming round is determined by reading data/next_map.json, which is a copy of the json files found in the _maps tree. If this file does not exist, the default map from config/maps.txt will be loaded. Failing that, tgstation2 will be loaded. If you want to set a specific map to load next round you can use the Change Map verb in game before restarting the server or copy a json from _maps to data/next_map.json before starting the server. Also, for debugging purposes, ticking a corresponding map's code file in Dream Maker will force that map to load every round.
If you are hosting a server, and want randomly picked maps to be played each round, you can enable map rotation in [config.txt](config/config.txt) and then set the maps to be picked in the [maps.txt](config/maps.txt) file.
Anytime you want to make changes to a map it's imperative you use the [Map Merging tools](http://tgstation13.org/wiki/Map_Merger)
## AWAY MISSIONS
/tg/station supports loading away missions however they are disabled by default.
Map files for away missions are located in the _maps/RandomZLevels directory. Each away mission includes it's own code definitions located in /code/modules/awaymissions/mission_code. These files must be included and compiled with the server beforehand otherwise the server will crash upon trying to load away missions that lack their code.
To enable an away mission open `config/awaymissionconfig.txt` and uncomment one of the .dmm lines by removing the #. If more than one away mission is uncommented then the away mission loader will randomly select one the enabled ones to load.
## SQL SETUP
The SQL backend requires a MySQL server. SQL is required for the library, stats tracking, admin notes, and job-only bans, among other features, mostly related to server administration. Your server details go in /config/dbconfig.txt, and the SQL schema is in /SQL/tgstation_schema.sql and /SQL/tgstation_schema_prefix.sql depending on if you want table prefixes. More detailed setup instructions are located here: http://www.tgstation13.org/wiki/Downloading_the_source_code#Setting_up_the_database
## IRC BOT SETUP
Included in the repository is a python3 compatible IRC bot capable of relaying adminhelps to a specified
IRC channel/server, see the /tools/minibot folder for more
## CONTRIBUTING
Please see [CONTRIBUTING.md](.github/CONTRIBUTING.md)
## LICENSE
All code after [commit 333c566b88108de218d882840e61928a9b759d8f on 2014/31/12 at 4:38 PM PST](https://github.com/tgstation/tgstation/commit/333c566b88108de218d882840e61928a9b759d8f) is licensed under [GNU AGPL v3](http://www.gnu.org/licenses/agpl-3.0.html).
All code before [commit 333c566b88108de218d882840e61928a9b759d8f on 2014/31/12 at 4:38 PM PST](https://github.com/tgstation/tgstation/commit/333c566b88108de218d882840e61928a9b759d8f) is licensed under [GNU GPL v3](https://www.gnu.org/licenses/gpl-3.0.html).
(Including tools unless their readme specifies otherwise.)
See LICENSE-AGPLv3.txt and LICENSE-GPLv3.txt for more details.
tgui clientside is licensed as a subproject under the MIT license.
Font Awesome font files, used by tgui, are licensed under the SIL Open Font License v1.1
tgui assets are licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/).
The TGS3 API is licensed as a subproject under the MIT license.
See tgui/LICENSE.md for the MIT license.
See tgui/assets/fonts/SIL-OFL-1.1-LICENSE.md for the SIL Open Font License.
See the footers of code/\_\_DEFINES/server\_tools.dm, code/modules/server\_tools/st\_commands.dm, and code/modules/server\_tools/st\_inteface.dm for the MIT license.
All assets including icons and sound are under a [Creative Commons 3.0 BY-SA license](http://creativecommons.org/licenses/by-sa/3.0/) unless otherwise indicated.
+22
View File
@@ -0,0 +1,22 @@
{
"documentation": "/tg/station server 3 configuration file",
"changelog": {
"script": "tools/ss13_genchangelog.py",
"arguments": "html/changelog.html html/changelogs",
"pip_dependancies": [
"PyYaml",
"beautifulsoup4"
]
},
"synchronize_paths": [
"html/changelog.html",
"html/changelogs/*"
],
"static_directories": [
"config",
"data"
],
"dlls": [
"libmysql.dll"
]
}
File diff suppressed because it is too large Load Diff
+3
View File
@@ -53,6 +53,9 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
// TESLA_IGNORE grants immunity from being targeted by tesla-style electricity
#define TESLA_IGNORE_2 512
// Stops you from putting things like an RCD or other items into an ORM or protolathe for materials.
#define NO_MAT_REDEMPTION_2 1024
//turf-only flags
#define NOJAUNT_1 1
#define UNUSED_TRANSIT_TURF_1 2
+2
View File
@@ -7,6 +7,8 @@
#define PLANE_SPACE_PARALLAX -90
#define GAME_PLANE 0
#define SPACE_LAYER 1.8
#define ABOVE_SPACE_LAYER 1.9
//#define TURF_LAYER 2 //For easy recordkeeping; this is a byond define
#define MID_TURF_LAYER 2.02
#define HIGH_TURF_LAYER 2.03
+103 -14
View File
@@ -1,34 +1,123 @@
// /tg/station 13 server tools API v3.1
//CONFIGURATION
//use this define if you want to do configuration outside of this file
#ifndef SERVER_TOOLS_EXTERNAL_CONFIGURATION
//Comment this out once you've filled in the below
//#error /tg/station server tools interface unconfigured
//Required interfaces (fill in with your codebase equivalent):
//create a global variable named `Name` and set it to `Value`
//These globals must not be modifiable from anywhere outside of the server tools
#define SERVER_TOOLS_DEFINE_AND_SET_GLOBAL(Name, Value) GLOBAL_VAR_INIT(##Name, ##Value); GLOBAL_PROTECT(##Name)
//Read the value in the global variable `Name`
#define SERVER_TOOLS_READ_GLOBAL(Name) GLOB.##Name
//Set the value in the global variable `Name` to `Value`
#define SERVER_TOOLS_WRITE_GLOBAL(Name, Value) GLOB.##Name = ##Value
//display an announcement `message` from the server to all players
#define SERVER_TOOLS_WORLD_ANNOUNCE(message) to_chat(world, "<span class='boldannounce'>[html_encode(##message)]</span>")
//Write a string `message` to a server log
#define SERVER_TOOLS_LOG(message) log_world("SERVICE: [##message]")
//Notify current in-game administrators of a string `event`
#define SERVER_TOOLS_NOTIFY_ADMINS(event) message_admins(##event)
#endif
//Required hooks:
//Put this somewhere in /world/New() that is always run
#define SERVER_TOOLS_ON_NEW ServiceInit()
//Put this somewhere in /world/Topic(T, Addr, Master, Keys) that is always run before T is modified
#define SERVER_TOOLS_ON_TOPIC var/service_topic_return = ServiceCommand(params2list(T)); if(service_topic_return) return service_topic_return
//Put at the beginning of world/Reboot(reason)
#define SERVER_TOOLS_ON_REBOOT ServiceReboot()
//Optional callable functions:
//Returns the string version of the API
#define SERVER_TOOLS_API_VERSION ServiceAPIVersion()
//Returns TRUE if the world was launched under the server tools and the API matches, FALSE otherwise
//No function below this succeed if this is FALSE
#define SERVER_TOOLS_PRESENT RunningService()
//Gets the current version of the service running the server
#define SERVER_TOOLS_VERSION ServiceVersion()
//Forces a hard reboot of BYOND by ending the process
//unlike del(world) clients will try to reconnect
//If the service has not requested a shutdown, the world will reboot shortly after
#define SERVER_TOOLS_REBOOT_BYOND world.ServiceEndProcess()
/*
Gets the list of any testmerged github pull requests
"[PR Number]" => list(
"title" -> PR title
"commit" -> Full hash of commit merged
"author" -> Github username of the author of the PR
)
*/
#define SERVER_TOOLS_PR_LIST GetTestMerges()
//Sends a message to connected game chats
#define SERVER_TOOLS_CHAT_BROADCAST(message) world.ChatBroadcast(message)
//Sends a message to connected admin chats
#define SERVER_TOOLS_RELAY_BROADCAST(message) world.AdminBroadcast(message)
//IMPLEMENTATION
#define SERVICE_API_VERSION_STRING "3.1.0.0"
#define REBOOT_MODE_NORMAL 0
#define REBOOT_MODE_HARD 1
#define REBOOT_MODE_SHUTDOWN 2
#define IRC_STATUS_THROTTLE 5
#define PR_ANNOUNCEMENTS_PER_ROUND 5 //The number of unique PR announcements allowed per round
//This makes sure that a single person can only spam 3 reopens and 3 closes before being ignored
//keep these in sync with TGS3
#define SERVICE_WORLD_PARAM "server_service"
#define SERVICE_VERSION_PARAM "server_service_version"
#define SERVICE_PR_TEST_JSON "prtestjob.json"
#define SERVICE_PR_TEST_JSON_OLD "..\\..\\[SERVICE_PR_TEST_JSON]"
#define SERVICE_INTERFACE_DLL "TGServiceInterface.dll"
#define SERVICE_INTERFACE_FUNCTION "DDEntryPoint"
#define SERVICE_CMD_HARD_REBOOT "hard_reboot"
#define SERVICE_CMD_GRACEFUL_SHUTDOWN "graceful_shutdown"
#define SERVICE_CMD_WORLD_ANNOUNCE "world_announce"
#define SERVICE_CMD_IRC_CHECK "irc_check"
#define SERVICE_CMD_IRC_STATUS "irc_status"
#define SERVICE_CMD_ADMIN_MSG "adminmsg"
#define SERVICE_CMD_NAME_CHECK "namecheck"
#define SERVICE_CMD_ADMIN_WHO "adminwho"
#define SERVICE_CMD_LIST_CUSTOM "list_custom_commands"
#define SERVICE_CMD_PARAM_KEY "serviceCommsKey"
#define SERVICE_CMD_PARAM_COMMAND "command"
#define SERVICE_CMD_PARAM_MESSAGE "message"
#define SERVICE_CMD_PARAM_TARGET "target"
#define SERVICE_CMD_PARAM_SENDER "sender"
#define SERVICE_CMD_PARAM_CUSTOM "custom"
#define SERVICE_CMD_API_COMPATIBLE "api_compat"
#define SERVICE_JSON_PARAM_HELPTEXT "help_text"
#define SERVICE_JSON_PARAM_ADMINONLY "admin_only"
#define SERVICE_JSON_PARAM_REQUIREDPARAMETERS "required_parameters"
#define SERVICE_REQUEST_KILL_PROCESS "killme"
#define SERVICE_REQUEST_IRC_BROADCAST "irc"
#define SERVICE_REQUEST_IRC_ADMIN_CHANNEL_MESSAGE "send2irc"
#define SERVICE_REQUEST_WORLD_REBOOT "worldreboot"
#define SERVICE_REQUEST_API_VERSION "api_ver"
/*
The MIT License
Copyright (c) 2011 Dominic Tarr
Permission is hereby granted, free of charge,
to any person obtaining a copy of this software and
associated documentation files (the "Software"), to
deal in the Software without restriction, including
without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom
the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
-57
View File
@@ -1364,63 +1364,6 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
return FALSE
return TRUE
//WHATEVER YOU USE THIS FOR MUST BE SANITIZED TO SHIT, IT USES SHELL
//It also sleeps
//Set this to TRUE before calling
//This prevents RCEs from badmins
//kevinz000 if you touch this I will hunt you down
GLOBAL_VAR_INIT(valid_HTTPSGet, FALSE)
GLOBAL_PROTECT(valid_HTTPSGet)
/proc/HTTPSGet(url) //tgs2 support
if(findtext(url, "\""))
GLOB.valid_HTTPSGet = FALSE
if(!GLOB.valid_HTTPSGet)
if(usr)
CRASH("[usr.ckey]([usr]) just attempted an invalid HTTPSGet on: [url]!")
else
CRASH("Invalid HTTPSGet call on: [url]")
GLOB.valid_HTTPSGet = FALSE
//"This has got to be the ugliest hack I have ever done"
//warning, here be dragons
/*
| @___oo
/\ /\ / (__,,,,|
) /^\) ^\/ _)
) /^\/ _)
) _ / / _)
/\ )/\/ || | )_)
< > |(,,) )__)
|| / \)___)\
| \____( )___) )___
\______(_______;;; __;;;
*/
var/temp_file = "data/HTTPSGetOutput.txt"
var/command
if(world.system_type == MS_WINDOWS)
command = "powershell -Command \"wget [url] -OutFile [temp_file]\""
else if(world.system_type == UNIX)
command = "wget -O [temp_file] [url]"
else
CRASH("Invalid world.system_type ([world.system_type])? Yell at Lummox.")
log_world("HTTPSGet: [url]")
var/result = shell(command)
if(result != 0)
log_world("Download failed: shell exited with code: [result]")
return
var/f = file(temp_file)
if(!f)
log_world("Download failed: Temp file not found")
return
. = file2text(f)
f = null
fdel(temp_file)
#define UNTIL(X) while(!(X)) stoplag()
/proc/pass()
+91 -104
View File
@@ -1,104 +1,91 @@
/mob/dead/observer/DblClickOn(var/atom/A, var/params)
if(client.click_intercept)
if(call(client.click_intercept,"InterceptClickOn")(src,params,A))
return
if(can_reenter_corpse && mind && mind.current)
if(A == mind.current || (mind.current in A)) // double click your corpse or whatever holds it
reenter_corpse() // (cloning scanner, body bag, closet, mech, etc)
return // seems legit.
// Things you might plausibly want to follow
if(ismovableatom(A))
ManualFollow(A)
// Otherwise jump
else if(A.loc)
loc = get_turf(A)
update_parallax_contents()
/mob/dead/observer/ClickOn(var/atom/A, var/params)
if(client.click_intercept)
if(call(client.click_intercept,"InterceptClickOn")(src,params,A))
return
var/list/modifiers = params2list(params)
if(modifiers["shift"] && modifiers["middle"])
ShiftMiddleClickOn(A)
return
if(modifiers["shift"] && modifiers["ctrl"])
CtrlShiftClickOn(A)
return
if(modifiers["middle"])
MiddleClickOn(A)
return
if(modifiers["shift"])
ShiftClickOn(A)
return
if(modifiers["alt"])
AltClickOn(A)
return
if(modifiers["ctrl"])
CtrlClickOn(A)
return
if(world.time <= next_move)
return
// You are responsible for checking config.ghost_interaction when you override this function
// Not all of them require checking, see below
A.attack_ghost(src)
// Oh by the way this didn't work with old click code which is why clicking shit didn't spam you
/atom/proc/attack_ghost(mob/dead/observer/user)
if(user.client)
if(IsAdminGhost(user))
attack_ai(user)
if(user.client.prefs.inquisitive_ghost)
user.examinate(src)
// ---------------------------------------
// And here are some good things for free:
// Now you can click through portals, wormholes, gateways, and teleporters while observing. -Sayu
/obj/machinery/teleport/hub/attack_ghost(mob/user)
var/atom/l = loc
var/obj/machinery/computer/teleporter/com = locate(/obj/machinery/computer/teleporter, locate(l.x - 2, l.y, l.z))
if(com && com.locked)
user.forceMove(get_turf(com.locked))
/obj/effect/portal/attack_ghost(mob/user)
if(linked)
user.forceMove(get_turf(linked))
/obj/machinery/gateway/centerstation/attack_ghost(mob/user)
if(awaygate)
user.forceMove(awaygate.loc)
else
to_chat(user, "[src] has no destination.")
/obj/machinery/gateway/centeraway/attack_ghost(mob/user)
if(stationgate)
user.forceMove(stationgate.loc)
else
to_chat(user, "[src] has no destination.")
/obj/item/storage/attack_ghost(mob/user)
orient2hud(user)
show_to(user)
/obj/machinery/teleport/hub/attack_ghost(mob/user)
if(power_station && power_station.engaged && power_station.teleporter_console && power_station.teleporter_console.target)
user.forceMove(get_turf(power_station.teleporter_console.target))
// -------------------------------------------
// This was supposed to be used by adminghosts
// I think it is a *terrible* idea
// but I'm leaving it here anyway
// commented out, of course.
/*
/atom/proc/attack_admin(mob/user as mob)
if(!user || !user.client || !user.client.holder)
return
attack_hand(user)
*/
/mob/dead/observer/DblClickOn(var/atom/A, var/params)
if(client.click_intercept)
if(call(client.click_intercept,"InterceptClickOn")(src,params,A))
return
if(can_reenter_corpse && mind && mind.current)
if(A == mind.current || (mind.current in A)) // double click your corpse or whatever holds it
reenter_corpse() // (cloning scanner, body bag, closet, mech, etc)
return // seems legit.
// Things you might plausibly want to follow
if(ismovableatom(A))
ManualFollow(A)
// Otherwise jump
else if(A.loc)
loc = get_turf(A)
update_parallax_contents()
/mob/dead/observer/ClickOn(var/atom/A, var/params)
if(client.click_intercept)
if(call(client.click_intercept,"InterceptClickOn")(src,params,A))
return
var/list/modifiers = params2list(params)
if(modifiers["shift"] && modifiers["middle"])
ShiftMiddleClickOn(A)
return
if(modifiers["shift"] && modifiers["ctrl"])
CtrlShiftClickOn(A)
return
if(modifiers["middle"])
MiddleClickOn(A)
return
if(modifiers["shift"])
ShiftClickOn(A)
return
if(modifiers["alt"])
AltClickOn(A)
return
if(modifiers["ctrl"])
CtrlClickOn(A)
return
if(world.time <= next_move)
return
// You are responsible for checking config.ghost_interaction when you override this function
// Not all of them require checking, see below
A.attack_ghost(src)
// Oh by the way this didn't work with old click code which is why clicking shit didn't spam you
/atom/proc/attack_ghost(mob/dead/observer/user)
if(user.client)
if(IsAdminGhost(user))
attack_ai(user)
if(user.client.prefs.inquisitive_ghost)
user.examinate(src)
// ---------------------------------------
// And here are some good things for free:
// Now you can click through portals, wormholes, gateways, and teleporters while observing. -Sayu
/obj/machinery/teleport/hub/attack_ghost(mob/user)
var/atom/l = loc
var/obj/machinery/computer/teleporter/com = locate(/obj/machinery/computer/teleporter, locate(l.x - 2, l.y, l.z))
if(com && com.locked)
user.forceMove(get_turf(com.locked))
/obj/effect/portal/attack_ghost(mob/user)
if(linked)
user.forceMove(get_turf(linked))
/obj/machinery/gateway/centerstation/attack_ghost(mob/user)
if(awaygate)
user.forceMove(awaygate.loc)
else
to_chat(user, "[src] has no destination.")
/obj/machinery/gateway/centeraway/attack_ghost(mob/user)
if(stationgate)
user.forceMove(stationgate.loc)
else
to_chat(user, "[src] has no destination.")
/obj/item/storage/attack_ghost(mob/user)
orient2hud(user)
show_to(user)
/obj/machinery/teleport/hub/attack_ghost(mob/user)
if(power_station && power_station.engaged && power_station.teleporter_console && power_station.teleporter_console.target)
user.forceMove(get_turf(power_station.teleporter_console.target))
File diff suppressed because it is too large Load Diff
@@ -229,7 +229,7 @@ CONFIG_DEF(flag/forbid_singulo_possession)
CONFIG_DEF(flag/useircbot) //tgs2 support
protection = CONFIG_ENTRY_LOCKED
CONFIG_DEF(flag/automute_on) //enables automuting/spam prevention
CONFIG_DEF(string/panic_server_name)
+2
View File
@@ -89,6 +89,8 @@ SUBSYSTEM_DEF(ticker)
for(var/client/C in GLOB.clients)
window_flash(C, ignorepref = TRUE) //let them know lobby has opened up.
to_chat(world, "<span class='boldnotice'>Welcome to [station_name()]!</span>")
if(CONFIG_GET(flag/irc_announce_new_game))
SERVER_TOOLS_CHAT_BROADCAST("New round starting on [SSmapping.config.map_name]!")
current_state = GAME_STATE_PREGAME
//Everyone who wants to be an observer is now spawned
create_observers()
+1 -1
View File
@@ -238,7 +238,7 @@
/datum/ai_laws/proc/pick_weighted_lawset()
var/datum/ai_laws/lawtype
var/list/law_weights = CONFIG_GET(keyed_number_list/law_weight)
while(!lawtype && law_weights)
while(!lawtype && law_weights.len)
var/possible_id = pickweight(law_weights)
lawtype = lawid_to_type(possible_id)
if(!lawtype)
+11 -5
View File
@@ -85,7 +85,7 @@
if(owner.assigned_role == "Clown")
var/mob/living/carbon/human/traitor_mob = owner.current
if(traitor_mob&&istype(traitor_mob))
if(!silent)
if(!silent)
to_chat(traitor_mob, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.")
traitor_mob.dna.remove_mutation(CLOWNMUT)
@@ -210,10 +210,16 @@
kill_objective.find_target()
add_objective(kill_objective)
else
var/datum/objective/steal/steal_objective = new
steal_objective.owner = owner
steal_objective.find_target()
add_objective(steal_objective)
if(prob(15) && !(locate(/datum/objective/download in owner.objectives)))
var/datum/objective/download/download_objective = new
download_objective.owner = owner
download_objective.gen_amount_goal()
add_objective(download_objective)
else
var/datum/objective/steal/steal_objective = new
steal_objective.owner = owner
steal_objective.find_target()
add_objective(steal_objective)
/datum/antagonist/traitor/AI/forge_single_objective()
.=1
+6 -53
View File
@@ -2,25 +2,10 @@
var/originmastercommit
var/commit
var/list/testmerge = list()
var/has_pr_details = FALSE //tgs2 support
var/date
/datum/getrev/New()
if(world.RunningService())
var/file_name
if(ServiceVersion()) //will return null for versions < 3.0.91.0
file_name = SERVICE_PR_TEST_JSON_OLD
else
file_name = SERVICE_PR_TEST_JSON
if(fexists(file_name))
testmerge = json_decode(file2text(file_name))
#ifdef SERVERTOOLS
else if(!world.RunningService() && fexists("../prtestjob.lk")) //tgs2 support
var/list/tmp = world.file2list("..\\prtestjob.lk")
for(var/I in tmp)
if(I)
testmerge |= I
#endif
testmerge = SERVER_TOOLS_PR_LIST
log_world("Running /tg/ revision:")
var/list/logs = world.file2list(".git/logs/HEAD")
if(logs)
@@ -36,53 +21,21 @@
log_world(commit)
for(var/line in testmerge)
if(line)
if(world.RunningService())
var/tmcommit = testmerge[line]["commit"]
log_world("Test merge active of PR #[line] commit [tmcommit]")
SSblackbox.add_details("testmerged_prs","[line]|[tmcommit]")
else //tgs2 support
log_world("Test merge active of PR #[line]")
SSblackbox.add_details("testmerged_prs","[line]")
var/tmcommit = testmerge[line]["commit"]
log_world("Test merge active of PR #[line] commit [tmcommit]")
SSblackbox.add_details("testmerged_prs","[line]|[tmcommit]")
log_world("Based off origin/master commit [originmastercommit]")
else
log_world(originmastercommit)
/datum/getrev/proc/DownloadPRDetails()
var/repo_id = CONFIG_GET(number/githubrepoid)
if(!repo_id)
if(testmerge.len)
log_world("PR details download failed: No github repo config set")
return
for(var/line in testmerge)
if(!isnum(text2num(line)))
log_world("PR details download failed: Invalid PR number: [line]")
return
var/url = "https://api.github.com/repositories/[repo_id]/pulls/[line].json"
GLOB.valid_HTTPSGet = TRUE
var/json = HTTPSGet(url)
if(!json)
return
testmerge[line] = json_decode(json)
if(!testmerge[line])
log_world("PR details download failed: null details returned")
return
CHECK_TICK
log_world("PR details successfully downloaded")
has_pr_details = TRUE
/datum/getrev/proc/GetTestMergeInfo(header = TRUE)
if(!testmerge.len)
return ""
. = header ? "The following pull requests are currently test merged:<br>" : ""
for(var/line in testmerge)
var/details
if(world.RunningService())
var/cm = testmerge[line]["commit"]
details = ": '" + html_encode(testmerge[line]["title"]) + "' by " + html_encode(testmerge[line]["author"]) + " at commit " + html_encode(copytext(cm, 1, min(length(cm), 7)))
else if(has_pr_details) //tgs2 support
details = ": '" + html_encode(testmerge[line]["title"]) + "' by " + html_encode(testmerge[line]["user"]["login"])
var/cm = testmerge[line]["commit"]
var/details = ": '" + html_encode(testmerge[line]["title"]) + "' by " + html_encode(testmerge[line]["author"]) + " at commit " + html_encode(copytext(cm, 1, min(length(cm), 7)))
if(details && findtext(details, "\[s\]") && (!usr || !usr.client.holder))
continue
. += "<a href=\"[CONFIG_GET(string/githuburl)]/pull/[line]\">#[line][details]</a><br>"
+10 -4
View File
@@ -133,10 +133,16 @@ GLOBAL_LIST_INIT(slot2type, list("head" = /obj/item/clothing/head/changeling, "w
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
if(prob(85))
var/datum/objective/steal/steal_objective = new
steal_objective.owner = changeling
steal_objective.find_target()
changeling.objectives += steal_objective
else
var/datum/objective/download/download_objective = new
download_objective.owner = changeling
download_objective.gen_amount_goal()
changeling.objectives += download_objective
var/list/active_ais = active_ais()
if(active_ais.len && prob(100/GLOB.joined_player_list.len))
+25 -18
View File
@@ -506,24 +506,31 @@ GLOBAL_LIST_EMPTY(possible_items_special)
explanation_text = "Download [target_amount] research level\s."
return target_amount
/datum/objective/download/check_completion()//NINJACODE.
var/current_amount = 0
/datum/objective/download/check_completion()
var/list/current_tech = list()
var/list/datum/mind/owners = get_owners()
for(var/datum/mind/M in owners)
if(!ishuman(owner.current))
continue
var/mob/living/carbon/human/H = owner.current
if(!H || H.stat == DEAD || !istype(H.wear_suit, /obj/item/clothing/suit/space/space_ninja))
continue
var/obj/item/clothing/suit/space/space_ninja/SN = H.wear_suit
if(!SN.s_initialized)
continue
for(var/datum/tech/current_data in SN.stored_research)
if(current_data.level)
current_amount += (current_data.level-1)
return current_amount >= target_amount
for(var/datum/mind/owner in owners)
if(ismob(owner.current))
var/mob/M = owner.current //Yeah if you get morphed and you eat a quantum tech disk with the RD's latest backup good on you soldier.
if(ishuman(M))
var/mob/living/carbon/human/H = M
if(H && (H.stat != DEAD) && istype(H.wear_suit, /obj/item/clothing/suit/space/space_ninja))
var/obj/item/clothing/suit/space/space_ninja/S = H.wear_suit
for(var/datum/tech/T in S.stored_research)
current_tech[T.id] = T.level? T.level : 0
var/list/otherwise = M.GetAllContents()
for(var/obj/item/disk/tech_disk/TD in otherwise)
for(var/datum/tech/T in TD.tech_stored)
if(!T.id || !T.level)
continue
else if(!current_tech[T.id])
current_tech[T.id] = T.level
else if(T.level > current_tech[T.id])
current_tech[T.id] = T.level
var/total = 0
for(var/i in current_tech)
total += current_tech[i]
return total >= target_amount
/datum/objective/capture
@@ -586,7 +593,7 @@ GLOBAL_LIST_EMPTY(possible_items_special)
for(var/datum/mind/M in owners)
if(!owner || !owner.changeling || !owner.changeling.stored_profiles)
continue
absorbedcount += M.changeling.absorbedcount
absorbedcount += M.changeling.absorbedcount
return absorbedcount >= target_amount
@@ -196,6 +196,7 @@
equip_cooldown = 10
energy_drain = 250
range = MELEE|RANGED
flags_2 = NO_MAT_REDEMPTION_2
var/mode = 0 //0 - deconstruct, 1 - wall or floor, 2 - airlock.
/obj/item/mecha_parts/mecha_equipment/rcd/New()
+1
View File
@@ -123,6 +123,7 @@ obj/item/construction
lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
max_matter = 160
flags_2 = NO_MAT_REDEMPTION_2
var/mode = 1
var/canRturf = 0
var/ranged = FALSE
+71 -70
View File
@@ -1,70 +1,71 @@
/obj/item/bodybag
name = "body bag"
desc = "A folded bag designed for the storage and transportation of cadavers."
icon = 'icons/obj/bodybag.dmi'
icon_state = "bodybag_folded"
var/unfoldedbag_path = /obj/structure/closet/body_bag
w_class = WEIGHT_CLASS_SMALL
/obj/item/bodybag/attack_self(mob/user)
deploy_bodybag(user, user.loc)
/obj/item/bodybag/afterattack(atom/target, mob/user, proximity)
if(proximity)
if(isopenturf(target))
deploy_bodybag(user, target)
/obj/item/bodybag/proc/deploy_bodybag(mob/user, atom/location)
var/obj/structure/closet/body_bag/R = new unfoldedbag_path(location)
R.open(user)
R.add_fingerprint(user)
qdel(src)
// Bluespace bodybag
/obj/item/bodybag/bluespace
name = "bluespace body bag"
desc = "A folded bluespace body bag designed for the storage and transportation of cadavers."
icon = 'icons/obj/bodybag.dmi'
icon_state = "bluebodybag_folded"
unfoldedbag_path = /obj/structure/closet/body_bag/bluespace
w_class = WEIGHT_CLASS_SMALL
origin_tech = "bluespace=4;materials=4;plasmatech=4"
/obj/item/bodybag/bluespace/examine(mob/user)
..()
if(contents.len)
to_chat(user, "<span class='notice'>You can make out the shapes of [contents.len] objects through the fabric.</span>")
/obj/item/bodybag/bluespace/Destroy()
for(var/atom/movable/A in contents)
A.forceMove(get_turf(src))
if(isliving(A))
to_chat(A, "<span class='notice'>You suddenly feel the space around you torn apart! You're free!</span>")
return ..()
/obj/item/bodybag/bluespace/deploy_bodybag(mob/user, atom/location)
var/obj/structure/closet/body_bag/R = new unfoldedbag_path(location)
for(var/atom/movable/A in contents)
A.forceMove(R)
if(isliving(A))
to_chat(A, "<span class='notice'>You suddenly feel air around you! You're free!</span>")
R.open(user)
R.add_fingerprint(user)
qdel(src)
/obj/item/bodybag/bluespace/container_resist(mob/living/user)
if(user.incapacitated())
to_chat(user, "<span class='warning'>You can't get out while you're restrained like this!</span>")
return
user.changeNext_move(CLICK_CD_BREAKOUT)
user.last_special = world.time + CLICK_CD_BREAKOUT
to_chat(user, "<span class='notice'>You claw at the fabric of [src], trying to tear it open...</span>")
to_chat(loc, "<span class='warning'>Someone starts trying to break free of [src]!</span>")
if(!do_after(user, 200, target = src))
to_chat(loc, "<span class='warning'>The pressure subsides. It seems that they've stopped resisting...</span>")
return
loc.visible_message("<span class='warning'>[user] suddenly appears in front of [loc]!</span>", "<span class='userdanger'>[user] breaks free of [src]!</span>")
qdel(src)
/obj/item/bodybag
name = "body bag"
desc = "A folded bag designed for the storage and transportation of cadavers."
icon = 'icons/obj/bodybag.dmi'
icon_state = "bodybag_folded"
var/unfoldedbag_path = /obj/structure/closet/body_bag
w_class = WEIGHT_CLASS_SMALL
/obj/item/bodybag/attack_self(mob/user)
deploy_bodybag(user, user.loc)
/obj/item/bodybag/afterattack(atom/target, mob/user, proximity)
if(proximity)
if(isopenturf(target))
deploy_bodybag(user, target)
/obj/item/bodybag/proc/deploy_bodybag(mob/user, atom/location)
var/obj/structure/closet/body_bag/R = new unfoldedbag_path(location)
R.open(user)
R.add_fingerprint(user)
qdel(src)
// Bluespace bodybag
/obj/item/bodybag/bluespace
name = "bluespace body bag"
desc = "A folded bluespace body bag designed for the storage and transportation of cadavers."
icon = 'icons/obj/bodybag.dmi'
icon_state = "bluebodybag_folded"
unfoldedbag_path = /obj/structure/closet/body_bag/bluespace
w_class = WEIGHT_CLASS_SMALL
flags_2 = NO_MAT_REDEMPTION_2
origin_tech = "bluespace=4;materials=4;plasmatech=4"
/obj/item/bodybag/bluespace/examine(mob/user)
..()
if(contents.len)
to_chat(user, "<span class='notice'>You can make out the shapes of [contents.len] objects through the fabric.</span>")
/obj/item/bodybag/bluespace/Destroy()
for(var/atom/movable/A in contents)
A.forceMove(get_turf(src))
if(isliving(A))
to_chat(A, "<span class='notice'>You suddenly feel the space around you torn apart! You're free!</span>")
return ..()
/obj/item/bodybag/bluespace/deploy_bodybag(mob/user, atom/location)
var/obj/structure/closet/body_bag/R = new unfoldedbag_path(location)
for(var/atom/movable/A in contents)
A.forceMove(R)
if(isliving(A))
to_chat(A, "<span class='notice'>You suddenly feel air around you! You're free!</span>")
R.open(user)
R.add_fingerprint(user)
qdel(src)
/obj/item/bodybag/bluespace/container_resist(mob/living/user)
if(user.incapacitated())
to_chat(user, "<span class='warning'>You can't get out while you're restrained like this!</span>")
return
user.changeNext_move(CLICK_CD_BREAKOUT)
user.last_special = world.time + CLICK_CD_BREAKOUT
to_chat(user, "<span class='notice'>You claw at the fabric of [src], trying to tear it open...</span>")
to_chat(loc, "<span class='warning'>Someone starts trying to break free of [src]!</span>")
if(!do_after(user, 200, target = src))
to_chat(loc, "<span class='warning'>The pressure subsides. It seems that they've stopped resisting...</span>")
return
loc.visible_message("<span class='warning'>[user] suddenly appears in front of [loc]!</span>", "<span class='userdanger'>[user] breaks free of [src]!</span>")
qdel(src)
+3 -964
View File
@@ -1,4 +1,3 @@
<<<<<<< HEAD
/*********************MANUALS (BOOKS)***********************/
//Oh god what the fuck I am not good at computer
@@ -883,7 +882,8 @@
..()
/obj/item/book/manual/wiki/proc/initialize_wikibook()
if(config.wikiurl)
var/wikiurl = CONFIG_GET(string/wikiurl)
if(wikiurl)
dat = {"
<html><head>
@@ -901,7 +901,7 @@
}
</script>
<p id='loading'>You start skimming through the manual...</p>
<iframe width='100%' height='97%' onload="pageloaded(this)" src="[config.wikiurl]/[page_link]?printable=yes&remove_links=1" frameborder="0" id="main_frame"></iframe>
<iframe width='100%' height='97%' onload="pageloaded(this)" src="[wikiurl]/[page_link]?printable=yes&remove_links=1" frameborder="0" id="main_frame"></iframe>
</body>
</html>
@@ -957,964 +957,3 @@
author = "Engineering Encyclopedia"
title = "Hacking"
page_link = "Hacking"
=======
/*********************MANUALS (BOOKS)***********************/
//Oh god what the fuck I am not good at computer
/obj/item/book/manual
icon = 'icons/obj/library.dmi'
due_date = 0 // Game time in 1/10th seconds
unique = 1 // 0 - Normal book, 1 - Should not be treated as normal book, unable to be copied, unable to be modified
/obj/item/book/manual/engineering_particle_accelerator
name = "Particle Accelerator User's Guide"
icon_state ="bookParticleAccelerator"
author = "Engineering Encyclopedia" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned
title = "Particle Accelerator User's Guide"
dat = {"<html>
<head>
<style>
h1 {font-size: 18px; margin: 15px 0px 5px;}
h2 {font-size: 15px; margin: 15px 0px 5px;}
li {margin: 2px 0px 2px 15px;}
ul {list-style: none; margin: 5px; padding: 0px;}
ol {margin: 5px; padding: 0px 15px;}
</style>
</head>
<body>
<h3>Experienced user's guide</h3>
<h4>Setting up</h4>
<ol>
<li><b>Wrench</b> all pieces to the floor</li>
<li>Add <b>wires</b> to all the pieces</li>
<li>Close all the panels with your <b>screwdriver</b></li>
</ol>
<h4>Use</h4>
<ol>
<li>Open the control panel</li>
<li>Set the speed to 2</li>
<li>Start firing at the singularity generator</li>
<li><font color='red'><b>When the singularity reaches a large enough size so it starts moving on its own set the speed down to 0, but don't shut it off</b></font></li>
<li>Remember to wear a radiation suit when working with this machine... we did tell you that at the start, right?</li>
</ol>
</body>
</html>"}
/obj/item/book/manual/engineering_singularity_safety
name = "Singularity Safety in Special Circumstances"
icon_state ="bookEngineeringSingularitySafety"
author = "Engineering Encyclopedia" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned
title = "Singularity Safety in Special Circumstances"
//big pile of shit below.
dat = {"<html>
<head>
<style>
h1 {font-size: 18px; margin: 15px 0px 5px;}
h2 {font-size: 15px; margin: 15px 0px 5px;}
li {margin: 2px 0px 2px 15px;}
ul {list-style: none; margin: 5px; padding: 0px;}
ol {margin: 5px; padding: 0px 15px;}
</style>
</head>
<body>
<h3>Singularity Safety in Special Circumstances</h3>
<h4>Power outage</h4>
A power problem has made the entire station lose power? Could be station-wide wiring problems or syndicate power sinks. In any case follow these steps:
<p>
<b>Step one:</b> <b><font color='red'>PANIC!</font></b><br>
<b>Step two:</b> Get your ass over to engineering! <b>QUICKLY!!!</b><br>
<b>Step three:</b> Make sure the SMES is still powering the emitters, if not, setup the generator in secure storage and disconnect the emitters from the SMES.<br>
<b>Step four:</b> Next, head over to the APC and swipe it with your <b>ID card</b> - if it doesn't unlock, continue with step 15.<br>
<b>Step five:</b> Open the console and disengage the cover lock.<br>
<b>Step six:</b> Pry open the APC with a <b>Crowbar.</b><br>
<b>Step seven:</b> Take out the empty <b>power cell.</b><br>
<b>Step eight:</b> Put in the new, <b>full power cell</b> - if you don't have one, continue with step 15.<br>
<b>Step nine:</b> Quickly put on a <b>Radiation suit.</b><br>
<b>Step ten:</b> Check if the <b>singularity field generators</b> withstood the down-time - if they didn't, continue with step 15.<br>
<b>Step eleven:</b> Since disaster was averted you now have to ensure it doesn't repeat. If it was a powersink which caused it and if the engineering apc is wired to the same powernet, which the powersink is on, you have to remove the piece of wire which links the apc to the powernet. If it wasn't a powersink which caused it, then skip to step 14.<br>
<b>Step twelve:</b> Grab your crowbar and pry away the tile closest to the APC.<br>
<b>Step thirteen:</b> Use the wirecutters to cut the wire which is conecting the grid to the terminal. <br>
<b>Step fourteen:</b> Go to the bar and tell the guys how you saved them all. Stop reading this guide here.<br>
<b>Step fifteen:</b> <b>GET THE FUCK OUT OF THERE!!!</b><br>
</p>
<h4>Shields get damaged</h4>
Step one: <b>GET THE FUCK OUT OF THERE!!! FORGET THE WOMEN AND CHILDREN, SAVE YOURSELF!!!</b><br>
</body>
</html>
"}
/obj/item/book/manual/hydroponics_pod_people
name = "The Human Harvest - From seed to market"
icon_state ="bookHydroponicsPodPeople"
author = "Farmer John"
title = "The Human Harvest - From seed to market"
dat = {"<html>
<head>
<style>
h1 {font-size: 18px; margin: 15px 0px 5px;}
h2 {font-size: 15px; margin: 15px 0px 5px;}
li {margin: 2px 0px 2px 15px;}
ul {list-style: none; margin: 5px; padding: 0px;}
ol {margin: 5px; padding: 0px 15px;}
</style>
</head>
<body>
<h3>Growing Humans</h3>
Why would you want to grow humans? Well I'm expecting most readers to be in the slave trade, but a few might actually
want to revive fallen comrades. Growing pod people is easy, but prone to disaster.
<p>
<ol>
<li>Find a dead person who is in need of cloning. </li>
<li>Take a blood sample with a syringe. </li>
<li>Inject a seed pack with the blood sample. </li>
<li>Plant the seeds. </li>
<li>Tend to the plants water and nutrition levels until it is time to harvest the cloned human.</li>
</ol>
<p>
It really is that easy! Good luck!
</body>
</html>
"}
/obj/item/book/manual/medical_cloning
name = "Cloning techniques of the 26th century"
icon_state ="bookCloning"
author = "Medical Journal, volume 3" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned
title = "Cloning techniques of the 26th century"
//big pile of shit below.
dat = {"<html>
<head>
<style>
h1 {font-size: 18px; margin: 15px 0px 5px;}
h2 {font-size: 15px; margin: 15px 0px 5px;}
li {margin: 2px 0px 2px 15px;}
ul {list-style: none; margin: 5px; padding: 0px;}
ol {margin: 5px; padding: 0px 15px;}
</style>
</head>
<body>
<H3>How to Clone People</H3>
So theres 50 dead people lying on the floor, chairs are spinning like no tomorrow and you havent the foggiest idea of what to do? Not to worry! This guide is intended to teach you how to clone people and how to do it right, in a simple step-by-step process! If at any point of the guide you have a mental meltdown, genetics probably isnt for you and you should get a job-change as soon as possible before youre sued for malpractice.
<ol>
<li><a href='#1'>Acquire body</a></li>
<li><a href='#2'>Strip body</a></li>
<li><a href='#3'>Put body in cloning machine</a></li>
<li><a href='#4'>Scan body</a></li>
<li><a href='#5'>Clone body</a></li>
<li><a href='#6'>Get clean Structurel Enzymes for the body</a></li>
<li><a href='#7'>Put body in morgue</a></li>
<li><a href='#8'>Await cloned body</a></li>
<li><a href='#9'>Use the clean SW injector</a></li>
<li><a href='#10'>Give person clothes back</a></li>
<li><a href='#11'>Send person on their way</a></li>
</ol>
<a name='1'><H4>Step 1: Acquire body</H4>
This is pretty much vital for the process because without a body, you cannot clone it. Usually, bodies will be brought to you, so you do not need to worry so much about this step. If you already have a body, great! Move on to the next step.
<a name='2'><H4>Step 2: Strip body</H4>
The cloning machine does not like abiotic items. What this means is you cant clone anyone if theyre wearing clothes, so take all of it off. If its just one person, its courteous to put their possessions in the closet. If you have about seven people awaiting cloning, just leave the piles where they are, but dont mix them around and for Gods sake dont let the Clown in to steal them.
<a name='3'><H4>Step 3: Put body in cloning machine</H4>
Grab the body and then put it inside the DNA modifier. If you cannot do this, then you messed up at Step 2. Go back and check you took EVERYTHING off - a commonly missed item is their headset.
<a name='4'><H4>Step 4: Scan body</H4>
Go onto the computer and scan the body by pressing Scan - <Subject Name Here>. If youre successful, they will be added to the records (note that this can be done at any time, even with living people, so that they can be cloned without a body in the event that they are lying dead on port solars and didnt turn on their suit sensors)! If not, and it says “Error: Mental interface failure.”, then they have left their bodily confines and are one with the spirits. If this happens, just shout at them to get back in their body, click Refresh and try scanning them again. If theres no success, threaten them with gibbing. Still no success? Skip over to Step 7 and dont continue after it, as you have an unresponsive body and it cannot be cloned. If you got “Error: Unable to locate valid genetic data.“, you are trying to clone a monkey - start over.
<a name='5'><H4>Step 5: Clone body</H4>
Now that the body has a record, click View Records, click the subjects name, and then click Clone to start the cloning process. Congratulations! Youre halfway there. Remember not to Eject the cloning pod as this will kill the developing clone and youll have to start the process again.
<a name='6'><H4>Step 6: Get clean SEs for body</H4>
Cloning is a finicky and unreliable process. Whilst it will most certainly bring someone back from the dead, they can have any number of nasty disabilities given to them during the cloning process! For this reason, you need to prepare a clean, defect-free Structural Enzyme (SE) injection for when theyre done. If youre a competent Geneticist, you will already have one ready on your working computer. If, for any reason, you do not, then eject the body from the DNA modifier (NOT THE CLONING POD) and take it next door to the Genetics research room. Put the body in one of those DNA modifiers and then go onto the console. Go into View/Edit/Transfer Buffer, find an open slot and click “SE“ to save it. Then click Injector to get the SEs in syringe form. Put this in your pocket or something for when the body is done.
<a name='7'><H4>Step 7: Put body in morgue</H4>
Now that the cloning process has been initiated and you have some clean Structural Enzymes, you no longer need the body! Drag it to the morgue and tell the Chef over the radio that they have some fresh meat waiting for them in there. To put a body in a morgue bed, simply open the tray, grab the body, put it on the open tray, then close the tray again. Use one of the nearby pens to label the bed “CHEF MEAT” in order to avoid confusion.
<a name='8'><H4>Step 8: Await cloned body</H4>
Now go back to the lab and wait for your patient to be cloned. It wont be long now, I promise.
<a name='9'><H4>Step 9: Use the clean SE injector on person</H4>
Has your body been cloned yet? Great! As soon as the guy pops out, grab your injector and jab it in them. Once youve injected them, they now have clean Structural Enzymes and their defects, if any, will disappear in a short while.
<a name='10'><H4>Step 10: Give person clothes back</H4>
Obviously the person will be naked after they have been cloned. Provided you werent an irresponsible little shit, you should have protected their possessions from thieves and should be able to give them back to the patient. No matter how cruel you are, its simply against protocol to force your patients to walk outside naked.
<a name='11'><H4>Step 11: Send person on their way</H4>
Give the patient one last check-over - make sure they dont still have any defects and that they have all their possessions. Ask them how they died, if they know, so that you can report any foul play over the radio. Once youre done, your patient is ready to go back to work! Chances are they do not have Medbay access, so you should let them out of Genetics and the Medbay main entrance.
<p>If youve gotten this far, congratulations! You have mastered the art of cloning. Now, the real problem is how to resurrect yourself after that traitor had his way with you for cloning his target.
</body>
</html>
"}
/obj/item/book/manual/ripley_build_and_repair
name = "APLU \"Ripley\" Construction and Operation Manual"
icon_state ="book"
author = "Weyland-Yutani Corp" // Who wrote the thing, can be changed by pen or PC. It is not automatically assigned
title = "APLU \"Ripley\" Construction and Operation Manual"
//big pile of shit below.
dat = {"<html>
<head>
<style>
h1 {font-size: 18px; margin: 15px 0px 5px;}
h2 {font-size: 15px; margin: 15px 0px 5px;}
li {margin: 2px 0px 2px 15px;}
ul {list-style: none; margin: 5px; padding: 0px;}
ol {margin: 5px; padding: 0px 15px;}
</style>
</head>
<body>
<center>
<b style='font-size: 12px;'>Weyland-Yutani - Building Better Worlds</b>
<h1>Autonomous Power Loader Unit \"Ripley\"</h1>
</center>
<h2>Specifications:</h2>
<ul>
<li><b>Class:</b> Autonomous Power Loader</li>
<li><b>Scope:</b> Logistics and Construction</li>
<li><b>Weight:</b> 820kg (without operator and with empty cargo compartment)</li>
<li><b>Height:</b> 2.5m</li>
<li><b>Width:</b> 1.8m</li>
<li><b>Top speed:</b> 5km/hour</li>
<li><b>Operation in vacuum/hostile environment:</b> Possible</b>
<li><b>Airtank Volume:</b> 500liters</li>
<li><b>Devices:</b>
<ul>
<li>Hydraulic Clamp</li>
<li>High-speed Drill</li>
</ul>
</li>
<li><b>Propulsion Device:</b> Powercell-powered electro-hydraulic system.</li>
<li><b>Powercell capacity:</b> Varies.</li>
</ul>
<h2>Construction:</h2>
<ol>
<li>Connect all exosuit parts to the chassis frame</li>
<li>Connect all hydraulic fittings and tighten them up with a wrench</li>
<li>Adjust the servohydraulics with a screwdriver</li>
<li>Wire the chassis. (Cable is not included.)</li>
<li>Use the wirecutters to remove the excess cable if needed.</li>
<li>Install the central control module (Not included. Use supplied datadisk to create one).</li>
<li>Secure the mainboard with a screwdriver.</li>
<li>Install the peripherals control module (Not included. Use supplied datadisk to create one).</li>
<li>Secure the peripherals control module with a screwdriver</li>
<li>Install the internal armor plating (Not included due to Nanotrasen regulations. Can be made using 5 metal sheets.)</li>
<li>Secure the internal armor plating with a wrench</li>
<li>Weld the internal armor plating to the chassis</li>
<li>Install the external reinforced armor plating (Not included due to Nanotrasen regulations. Can be made using 5 reinforced metal sheets.)</li>
<li>Secure the external reinforced armor plating with a wrench</li>
<li>Weld the external reinforced armor plating to the chassis</li>
<li></li>
<li>Additional Information:</li>
<li>The firefighting variation is made in a similar fashion.</li>
<li>A firesuit must be connected to the Firefighter chassis for heat shielding.</li>
<li>Internal armor is plasteel for additional strength.</li>
<li>External armor must be installed in 2 parts, totaling 10 sheets.</li>
<li>Completed mech is more resiliant against fire, and is a bit more durable overall</li>
<li>Nanotrasen is determined to the safety of its <s>investments</s> employees.</li>
</ol>
</body>
</html>
<h2>Operation</h2>
Coming soon...
"}
/obj/item/book/manual/experimentor
name = "Mentoring your Experiments"
icon_state = "rdbook"
author = "Dr. H.P. Kritz"
title = "Mentoring your Experiments"
dat = {"<html>
<head>
<style>
h1 {font-size: 18px; margin: 15px 0px 5px;}
h2 {font-size: 15px; margin: 15px 0px 5px;}
li {margin: 2px 0px 2px 15px;}
ul {list-style: none; margin: 5px; padding: 0px;}
ol {margin: 5px; padding: 0px 15px;}
</style>
</head>
<body>
<h1>THE E.X.P.E.R.I-MENTOR</h1>
The Enhanced Xenobiological Period Extraction (and) Restoration Instructor is a machine designed to discover the secrets behind every item in existence.
With advanced technology, it can process 99.95% of items, and discover their uses and secrets.
The E.X.P.E.R.I-MENTOR is a Research apparatus that takes items, and through a process of elimination, it allows you to deduce new technological designs from them.
Due to the volatile nature of the E.X.P.E.R.I-MENTOR, there is a slight chance for malfunction, potentially causing irreparable damage to you or your environment.
However, upgrading the apparatus has proven to decrease the chances of undesirable, potentially life-threatening outcomes.
Please note that the E.X.P.E.R.I-MENTOR uses a state-of-the-art random generator, which has a larger entropy than the observable universe,
therefore it can generate wildly different results each day, therefore it is highly suggested to re-scan objects of interests frequently (e.g. each shift).
<h2>BASIC PROCESS</h2>
The usage of the E.X.P.E.R.I-MENTOR is quite simple:
<ol>
<li>Find an item with a technological background</li>
<li>Insert the item into the E.X.P.E.R.I-MENTOR</li>
<li>Cycle through each processing method of the device.</li>
<li>Stand back, even in case of a successful experiment, as the machine might produce undesired behaviour.</li>
</ol>
<h2>ADVANCED USAGE</h2>
The E.X.P.E.R.I-MENTOR has a variety of uses, beyond menial research work. The different results can be used to combat localised events, or even to get special items.
The E.X.P.E.R.I-MENTOR's OBLITERATE function has the added use of transferring the destroyed item's material into a linked lathe.
The IRRADIATE function can be used to transform items into other items, resulting in potential upgrades (or downgrades).
Users should remember to always wear appropriate protection when using the machine, because malfunction can occur at any moment!
<h1>EVENTS</h1>
<h2>GLOBAL (happens at any time):</h2>
<ol>
<li>DETECTION MALFUNCTION - The machine's onboard sensors have malfunctioned, causing it to redefine the item's experiment type.
Produces the message: The E.X.P.E.R.I-MENTOR's onboard detection system has malfunctioned!</li>
<li>IANIZATION - The machine's onboard corgi-filter has malfunctioned, causing it to produce a corgi from.. somewhere.
Produces the message: The E.X.P.E.R.I-MENTOR melts the banana, ian-izing the air around it!</li>
<li>RUNTIME ERROR - The machine's onboard C4T-P processor has encountered a critical error, causing it to produce a cat from.. somewhere.
Produces the message: The E.X.P.E.R.I-MENTOR encounters a run-time error!</li>
<li>B100DG0D.EXE - The machine has encountered an unknown subroutine, which has been injected into its runtime. It upgrades the held item!
Produces the message: The E.X.P.E.R.I-MENTOR improves the banana, drawing the life essence of those nearby!</li>
<li>POWERSINK - The machine's PSU has tripped the charging mechanism! It consumes massive amounts of power!
Produces the message: The E.X.P.E.R.I-MENTOR begins to smoke and hiss, shaking violently!</li>
</ol>
<h2>FAIL:</h2>
This event is produced when the item mismatches the selected experiment.
Produces a random message similar to: "the Banana rumbles, and shakes, the experiment was a failure!"
<h2>POKE:</h2>
<ol>
<li>WILD ARMS - The machine's gryoscopic processors malfunction, causing it to lash out at nearby people with its arms.
Produces the message: The E.X.P.E.R.I-MENTOR malfunctions and destroys the banana, lashing its arms out at nearby people!</li>
<li>MISTYPE - The machine's interface has been garbled, and it switches to OBLITERATE.
Produces the message: The E.X.P.E.R.I-MENTOR malfunctions!</li>
<li>THROW - The machine's spatial recognition device has shifted several meters across the room, causing it to try and repostion the item there.
Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, throwing the banana!</li>
</ol>
<h2>IRRADIATE:</h2>
<ol>
<li>RADIATION LEAK - The machine's shield has failed, resulting in a toxic radiation leak.
Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, melting the banana and leaking radiation!</li>
<li>RADIATION DUMP - The machine's recycling and containment functions have failed, resulting in a dump of toxic waste around it
Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, spewing toxic waste!</li>
<li>MUTATION - The machine's radio-isotope level meter has malfunctioned, causing it over-irradiate the item, making it transform.
Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, transforming the banana!</li>
</ol>
<h2>GAS:</h2>
<ol>
<li>TOXIN LEAK - The machine's filtering and vent systems have failed, resulting in a cloud of toxic gas being expelled.
Produces the message: The E.X.P.E.R.I-MENTOR destroys the banana, leaking dangerous gas!</li>
<li>GAS LEAK - The machine's vent systems have failed, resulting in a cloud of harmless, but obscuring gas.
Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, spewing harmless gas!</li>
<li>ELECTROMAGNETIC IONS - The machine's electrolytic scanners have failed, causing a dangerous Electromagnetic reaction.
Produces the message: The E.X.P.E.R.I-MENTOR melts the banana, ionizing the air around it!</li>
</ol>
<h2>HEAT:</h2>
<ol>
<li>TOASTER - The machine's heating coils have come into contact with the machine's gas storage, causing a large, sudden blast of flame.
Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, melting the banana and releasing a burst of flame!</li>
<li>SAUNA - The machine's vent loop has sprung a leak, resulting in a large amount of superheated air being dumped around it.
Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, melting the banana and leaking hot air!</li>
<li>EMERGENCY VENT - The machine's temperature gauge has malfunctioned, resulting in it attempting to cool the area around it, but instead, dumping a cloud of steam.
Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, activating its emergency coolant systems!</li>
</ol>
<h2>COLD:</h2>
<ol>
<li>FREEZER - The machine's cooling loop has sprung a leak, resulting in a cloud of super-cooled liquid being blasted into the air.
Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, shattering the banana and releasing a dangerous cloud of coolant!</li>
<li>FRIDGE - The machine's cooling loop has been exposed to the outside air, resulting in a large decrease in temperature.
Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, shattering the banana and leaking cold air!</li>
<li>SNOWSTORM - The machine's cooling loop has come into contact with the heating coils, resulting in a sudden blast of cool air.
Produces the message: The E.X.P.E.R.I-MENTOR malfunctions, releasing a flurry of chilly air as the banana pops out!</li>
</ol>
<h2>OBLITERATE:</h2>
<ol>
<li>IMPLOSION - The machine's pressure leveller has malfunctioned, causing it to pierce the space-time momentarily, making everything in the area fly towards it.
Produces the message: The E.X.P.E.R.I-MENTOR's crusher goes way too many levels too high, crushing right through space-time!</li>
<li>DISTORTION - The machine's pressure leveller has completely disabled, resulting in a momentary space-time distortion, causing everything to fly around.
Produces the message: The E.X.P.E.R.I-MENTOR's crusher goes one level too high, crushing right into space-time!</li>
</ol>
</body>
</html>
"}
/obj/item/book/manual/research_and_development
name = "Research and Development 101"
icon_state = "rdbook"
author = "Dr. L. Ight"
title = "Research and Development 101"
dat = {"<html>
<head>
<style>
h1 {font-size: 18px; margin: 15px 0px 5px;}
h2 {font-size: 15px; margin: 15px 0px 5px;}
li {margin: 2px 0px 2px 15px;}
ul {list-style: none; margin: 5px; padding: 0px;}
ol {margin: 5px; padding: 0px 15px;}
</style>
</head>
<body>
<h1>Science For Dummies</h1>
So you want to further SCIENCE? Good man/woman/thing! However, SCIENCE is a complicated process even though it's quite easy. For the most part, it's a three step process:
<ol>
<li> 1) Deconstruct items in the Destructive Analyzer to advance technology or improve the design.</li>
<li> 2) Build unlocked designs in the Protolathe and Circuit Imprinter</li>
<li> 3) Repeat!</li>
</ol>
Those are the basic steps to furthing science. What do you do science with, however? Well, you have four major tools: R&D Console, the Destructive Analyzer, the Protolathe, and the Circuit Imprinter.
<h2>The R&D Console</h2>
The R&D console is the cornerstone of any research lab. It is the central system from which the Destructive Analyzer, Protolathe, and Circuit Imprinter (your R&D systems) are controled. More on those systems in their own sections. On its own, the R&D console acts as a database for all your technological gains and new devices you discover. So long as the R&D console remains intact, you'll retain all that SCIENCE you've discovered. Protect it though, because if it gets damaged, you'll lose your data! In addition to this important purpose, the R&D console has a disk menu that lets you transfer data from the database onto disk or from the disk into the database. It also has a settings menu that lets you re-sync with nearby R&D devices (if they've become disconnected), lock the console from the unworthy, upload the data to all other R&D consoles in the network (all R&D consoles are networked by default), connect/disconnect from the network, and purge all data from the database.
<b>NOTE:</b> The technology list screen, circuit imprinter, and protolathe menus are accessible by non-scientists. This is intended to allow 'public' systems for the plebians to utilize some new devices.
<h2>Destructive Analyzer</h2>
This is the source of all technology. Whenever you put a handheld object in it, it analyzes it and determines what sort of technological advancements you can discover from it. If the technology of the object is equal or higher then your current knowledge, you can destroy the object to further those sciences. Some devices (notably, some devices made from the protolathe and circuit imprinter) aren't 100% reliable when you first discover them. If these devices break down, you can put them into the Destructive Analyzer and improve their reliability rather then futher science. If their reliability is high enough ,it'll also advance their related technologies.
<h2>Circuit Imprinter</h2>
This machine, along with the Protolathe, is used to actually produce new devices. The Circuit Imprinter takes glass and various chemicals (depends on the design) to produce new circuit boards to build new machines or computers. It can even be used to print AI modules.
<h2>Protolathe</h2>
This machine is an advanced form of the Autolathe that produce non-circuit designs. Unlike the Autolathe, it can use processed metal, glass, solid plasma, silver, gold, and diamonds along with a variety of chemicals to produce devices. The downside is that, again, not all devices you make are 100% reliable when you first discover them.
<h1>Reliability and You</h1>
As it has been stated, many devices when they're first discovered do not have a 100% reliablity when you first discover them. Instead, the reliablity of the device is dependent upon a base reliability value, whatever improvements to the design you've discovered through the Destructive Analyzer, and any advancements you've made with the device's source technologies. To be able to improve the reliability of a device, you have to use the device until it breaks beyond repair. Once that happens, you can analyze it in a Destructive Analyzer. Once the device reachs a certain minimum reliability, you'll gain tech advancements from it.
<h1>Building a Better Machine</h1>
Many machines produces from circuit boards and inserted into a machine frame require a variety of parts to construct. These are parts like capacitors, batteries, matter bins, and so forth. As your knowledge of science improves, more advanced versions are unlocked. If you use these parts when constructing something, its attributes may be improved. For example, if you use an advanced matter bin when constructing an autolathe (rather then a regular one), it'll hold more materials. Experiment around with stock parts of various qualities to see how they affect the end results! Be warned, however: Tier 3 and higher stock parts don't have 100% reliability and their low reliability may affect the reliability of the end machine.
</body>
</html>
"}
/obj/item/book/manual/robotics_cyborgs
name = "Cyborgs for Dummies"
icon_state = "borgbook"
author = "XISC"
title = "Cyborgs for Dummies"
dat = {"<html>
<head>
<style>
h1 {font-size: 21px; margin: 15px 0px 5px;}
h2 {font-size: 18px; margin: 15px 0px 5px;}
h3 {font-size: 15px; margin: 15px 0px 5px;}
li {margin: 2px 0px 2px 15px;}
ul {list-style: none; margin: 5px; padding: 0px;}
ol {margin: 5px; padding: 0px 15px;}
</style>
</head>
<body>
<h1>Cyborgs for Dummies</h1>
<h2>Chapters</h2>
<ol>
<li><a href="#Equipment">Cyborg Related Equipment</a></li>
<li><a href="#Modules">Cyborg Modules</a></li>
<li><a href="#Construction">Cyborg Construction</a></li>
<li><a href="#Deconstruction">Cyborg Deconstruction</a></li>
<li><a href="#Maintenance">Cyborg Maintenance</a></li>
<li><a href="#Repairs">Cyborg Repairs</a></li>
<li><a href="#Emergency">In Case of Emergency</a></li>
</ol>
<h2><a name="Equipment">Cyborg Related Equipment</h2>
<h3>Exosuit Fabricator</h3>
The Exosuit Fabricator is the most important piece of equipment related to cyborgs. It allows the construction of the core cyborg parts. Without these machines, cyborgs can not be built. It seems that they may also benefit from advanced research techniques.
<h3>Cyborg Recharging Station</h3>
This useful piece of equipment will suck power out of the power systems to charge a cyborg's power cell back up to full charge.
<h3>Robotics Control Console</h3>
This useful piece of equipment can be used to immobolize or destroy a cyborg. A word of warning: Cyborgs are expensive pieces of equipment, do not destroy them without good reason, or Nanotrasen may see to it that it never happens again.
<h2><a name="Modules">Cyborg Modules</h2>
When a cyborg is created it picks out of an array of modules to designate its purpose. There are 6 different cyborg modules.
<h3>Standard Cyborg</h3>
The standard cyborg module is a multi-purpose cyborg. It is equipped with various modules, allowing it to do basic tasks.<br>
<h3>Engineering Cyborg</h3>
The Engineering cyborg module comes equipped with various engineering-related tools to help with engineering-related tasks.<br>
<h3>Mining Cyborg</h3>
The Mining Cyborg module comes equipped with the latest in mining equipment. They are efficient at mining due to no need for oxygen, but their power cells limit their time in the mines.
<h3>Security Cyborg</h3>
The Security Cyborg module is equipped with effective security measures used to apprehend and arrest criminals without harming them a bit.
<h3>Janitor Cyborg</h3>
The Janitor Cyborg module is equipped with various cleaning-facilitating devices.
<h3>Service Cyborg</h3>
The service cyborg module comes ready to serve your human needs. It includes various entertainment and refreshment devices. Occasionally some service cyborgs may have been referred to as "Bros"
<h2><a name="Construction">Cyborg Construction</h2>
Cyborg construction is a rather easy process, requiring a decent amount of metal and a few other supplies.<br>The required materials to make a cyborg are:
<ul>
<li>Metal</li>
<li>Two Flashes</li>
<li>One Power Cell (Preferrably rated to 15000w)</li>
<li>Some electrical wires</li>
<li>One Human Brain</li>
<li>One Man-Machine Interface</li>
</ul>
Once you have acquired the materials, you can start on construction of your cyborg.<br>To construct a cyborg, follow the steps below:
<ol>
<li>Start the Exosuit Fabricators constructing all of the cyborg parts</li>
<li>While the parts are being constructed, take your human brain, and place it inside the Man-Machine Interface</li>
<li>Once you have a Robot Head, place your two flashes inside the eye sockets</li>
<li>Once you have your Robot Chest, wire the Robot chest, then insert the power cell</li>
<li>Attach all of the Robot parts to the Robot frame</li>
<li>Insert the Man-Machine Interface (With the Brain inside) Into the Robot Body</li>
<li>Congratulations! You have a new cyborg!</li>
</ol>
<h2><a name="Deconstruction">Cyborg Deconstruction</h2>
If you want to deconstruct a cyborg, say to remove its MMI without <a href="#Emergency">blowing the Cyborg to pieces</a>, they come apart very quickly, <b>and</b> very safely, in a few simple steps.
<ul>
<li>Crowbar</li>
<li>Wrench</li>
Optional:
<li>Screwdriver</li>
<li>Wirecutters</li>
</ul>
<ol>
<li>Begin by unlocking the Cyborg's access panel using your ID</li>
<li>Use your crowbar to open the Cyborg's access panel</li>
<li>Using your bare hands, remove the power cell from the Cyborg</li>
<li>Lockdown the Cyborg to disengage safety protocols</li>
<ol>
Option 1: Robotics console
<li>Use the Robotics console in the RD's office</li>
<li>Find the entry for your Cyborg</li>
<li>Press the Lockdown button on the Robotics console</li>
</ol>
<ol>
Option 2: Lockdown wire
<li>Use your screwdriver to expose the Cyborg's wiring</li>
<li>Use your wirecutters to start cutting all of the wires until the lockdown light turns off, cutting all of the wires irregardless of the lockdown light works as well</li>
</ol>
<li>Use your wrench to unfasten the Cyborg's bolts, the Cyborg will then fall apart onto the floor, the MMI will be there as well</li>
</ol>
<h2><a name="Maintenance">Cyborg Maintenance</h2>
Occasionally Cyborgs may require maintenance of a couple types, this could include replacing a power cell with a charged one, or possibly maintaining the cyborg's internal wiring.
<h3>Replacing a Power Cell</h3>
Replacing a Power cell is a common type of maintenance for cyborgs. It usually involves replacing the cell with a fully charged one, or upgrading the cell with a larger capacity cell.<br>The steps to replace a cell are follows:
<ol>
<li>Unlock the Cyborg's Interface by swiping your ID on it</li>
<li>Open the Cyborg's outer panel using a crowbar</li>
<li>Remove the old power cell</li>
<li>Insert the new power cell</li>
<li>Close the Cyborg's outer panel using a crowbar</li>
<li>Lock the Cyborg's Interface by swiping your ID on it, this will prevent non-qualified personnel from attempting to remove the power cell</li>
</ol>
<h3>Exposing the Internal Wiring</h3>
Exposing the internal wiring of a cyborg is fairly easy to do, and is mainly used for cyborg repairs.<br>You can easily expose the internal wiring by following the steps below:
<ol>
<li>Follow Steps 1 - 3 of "Replacing a Cyborg's Power Cell"</li>
<li>Open the cyborg's internal wiring panel by using a screwdriver to unsecure the panel</li>
</ol>
To re-seal the cyborg's internal wiring:
<ol>
<li>Use a screwdriver to secure the cyborg's internal panel</li>
<li>Follow steps 4 - 6 of "Replacing a Cyborg's Power Cell" to close up the cyborg</li>
</ol>
<h2><a name="Repairs">Cyborg Repairs</h2>
Occasionally a Cyborg may become damaged. This could be in the form of impact damage from a heavy or fast-travelling object, or it could be heat damage from high temperatures, or even lasers or Electromagnetic Pulses (EMPs).
<h3>Dents</h3>
If a cyborg becomes damaged due to impact from heavy or fast-moving objects, it will become dented. Sure, a dent may not seem like much, but it can compromise the structural integrity of the cyborg, possibly causing a critical failure.
Dents in a cyborg's frame are rather easy to repair, all you need is to apply a welding tool to the dented area, and the high-tech cyborg frame will repair the dent under the heat of the welder.
<h3>Excessive Heat Damage</h3>
If a cyborg becomes damaged due to excessive heat, it is likely that the internal wires will have been damaged. You must replace those wires to ensure that the cyborg remains functioning properly.<br>To replace the internal wiring follow the steps below:
<ol>
<li>Unlock the Cyborg's Interface by swiping your ID</li>
<li>Open the Cyborg's External Panel using a crowbar</li>
<li>Remove the Cyborg's Power Cell</li>
<li>Using a screwdriver, expose the internal wiring or the Cyborg</li>
<li>Replace the damaged wires inside the cyborg</li>
<li>Secure the internal wiring cover using a screwdriver</li>
<li>Insert the Cyborg's Power Cell</li>
<li>Close the Cyborg's External Panel using a crowbar</li>
<li>Lock the Cyborg's Interface by swiping your ID</li>
</ol>
These repair tasks may seem difficult, but are essential to keep your cyborgs running at peak efficiency.
<h2><a name="Emergency">In Case of Emergency</h2>
In case of emergency, there are a few steps you can take.
<h3>"Rogue" Cyborgs</h3>
If the cyborgs seem to become "rogue", they may have non-standard laws. In this case, use extreme caution.
To repair the situation, follow these steps:
<ol>
<li>Locate the nearest robotics console</li>
<li>Determine which cyborgs are "Rogue"</li>
<li>Press the lockdown button to immobolize the cyborg</li>
<li>Locate the cyborg</li>
<li>Expose the cyborg's internal wiring</li>
<li>Check to make sure the LawSync and AI Sync lights are lit</li>
<li>If they are not lit, pulse the LawSync wire using a multitool to enable the cyborg's Law Sync</li>
<li>Proceed to a cyborg upload console. Nanotrasen usually places these in the same location as AI uplaod consoles.</li>
<li>Use a "Reset" upload moduleto reset the cyborg's laws</li>
<li>Proceed to a Robotics Control console</li>
<li>Remove the lockdown on the cyborg</li>
</ol>
<h3>As a last resort</h3>
If all else fails in a case of cyborg-related emergency. There may be only one option. Using a Robotics Control console, you may have to remotely detonate the cyborg.
<h3>WARNING:</h3> Do not detonate a borg without an explicit reason for doing so. Cyborgs are expensive pieces of Nanotrasen equipment, and you may be punished for detonating them without reason.
</body>
</html>
"}
/obj/item/book/manual/chef_recipes
name = "Chef Recipes"
icon_state = "cooked_book"
author = "Lord Frenrir Cageth"
title = "Chef Recipes"
dat = {"<html>
<head>
<style>
h1 {font-size: 18px; margin: 15px 0px 5px;}
h2 {font-size: 15px; margin: 15px 0px 5px;}
li {margin: 2px 0px 2px 15px;}
ul {list-style: none; margin: 5px; padding: 0px;}
ol {margin: 5px; padding: 0px 15px;}
</style>
</head>
<body>
<h1>Food for Dummies</h1>
Here is a guide on basic food recipes and also how to not poison your customers accidentally.
<h2>Basic ingredients preparation:</h2>
<b>Dough:</b> 10u water + 15u flour for simple dough.<br>
15u egg yolk + 15u flour + 5u sugar for cake batter.<br>
Doughs can be transformed by using a knife and rolling pin.<br>
All doughs can be microwaved.<br>
<b>Bowl:</b> Add water to it for soup preparation.<br>
<b>Meat:</b> Microwave it, process it, slice it into microwavable cutlets with your knife, or use it raw.<br>
<b>Cheese:</b> Add 5u universal enzyme (catalyst) to milk and soy milk to prepare cheese (sliceable) and tofu.<br>
<b>Rice:</b> Mix 10u rice with 10u water in a bowl then microwave it.
<h2>Custom food:</h2>
Add ingredients to a base item to prepare a custom meal.<br>
The bases are:<br>
- bun (burger)<br>
- breadslices(sandwich)<br>
- plain bread<br>
- plain pie<br>
- vanilla cake<br>
- empty bowl (salad)<br>
- bowl with 10u water (soup)<br>
- boiled spaghetti<br>
- pizza bread<br>
- metal rod (kebab)
<h2>Table Craft:</h2>
Put ingredients on table, then click and drag the table onto yourself to see what recipes you can prepare.
<h2>Microwave:</h2>
Use it to cook or boil food ingredients (meats, doughs, egg, spaghetti, donkpocket, etc...).
It can cook multiple items at once.
<h2>Processor:</h2>
Use it to process certain ingredients (meat into faggot, doughslice into spaghetti, potato into fries,etc...)
<h2>Gibber:</h2>
Stuff an animal in it to grind it into meat.
<h2>Meat spike:</h2>
Stick an animal on it then begin collecting its meat.
<h2>Example recipes:</h2>
<b>Vanilla Cake</b>: Microwave cake batter.<br>
<b>Burger:</b> 1 bun + 1 meat steak<br>
<b>Bread:</b> Microwave dough.<br>
<b>Waffles:</b> 2 pastry base<br>
<b>Popcorn:</b> Microwave corn.<br>
<b>Meat Steak:</b> Microwave meat.<br>
<b>Meat Pie:</b> 1 plain pie + 1u black pepper + 1u salt + 2 meat cutlets<br>
<b>Boiled Spagetti:</b> Microwave spaghetti.<br>
<b>Donuts:</b> 1u sugar + 1 pastry base<br>
<b>Fries:</b> Process potato.
<h2>Sharing your food:</h2>
You can put your meals on your kitchen counter or load them in the snack vending machines.
</body>
</html>
"}
/obj/item/book/manual/barman_recipes
name = "Barman Recipes"
icon_state = "barbook"
author = "Sir John Rose"
title = "Barman Recipes"
dat = {"<html>
<head>
<style>
h1 {font-size: 18px; margin: 15px 0px 5px;}
h2 {font-size: 15px; margin: 15px 0px 5px;}
li {margin: 2px 0px 2px 15px;}
ul {list-style: none; margin: 5px; padding: 0px;}
ol {margin: 5px; padding: 0px 15px;}
</style>
</head>
<body>
<h1>Drinks for dummies</h1>
Heres a guide for some basic drinks.
<h2>Manly Dorf:</h2>
Mix ale and beer into a glass.
<h2>Grog:</h2>
Mix rum and water into a glass.
<h2>Black Russian:</h2>
Mix vodka and kahlua into a glass.
<h2>Irish Cream:</h2>
Mix cream and whiskey into a glass.
<h2>Screwdriver:</h2>
Mix vodka and orange juice into a glass.
<h2>Cafe Latte:</h2>
Mix milk and coffee into a glass.
<h2>Mead:</h2>
Mix Enzyme, water and sugar into a glass.
<h2>Gin Tonic:</h2>
Mix gin and tonic into a glass.
<h2>Classic Martini:</h2>
Mix vermouth and gin into a glass.
</body>
</html>
"}
/obj/item/book/manual/detective
name = "The Film Noir: Proper Procedures for Investigations"
icon_state ="bookDetective"
author = "Nanotrasen"
title = "The Film Noir: Proper Procedures for Investigations"
dat = {"<html>
<head>
<style>
h1 {font-size: 18px; margin: 15px 0px 5px;}
h2 {font-size: 15px; margin: 15px 0px 5px;}
li {margin: 2px 0px 2px 15px;}
ul {list-style: none; margin: 5px; padding: 0px;}
ol {margin: 5px; padding: 0px 15px;}
</style>
</head>
<body>
<h3>Detective Work</h3>
Between your bouts of self-narration, and drinking whiskey on the rocks, you might get a case or two to solve.<br>
To have the best chance to solve your case, follow these directions:
<p>
<ol>
<li>Go to the crime scene. </li>
<li>Take your scanner and scan EVERYTHING (Yes, the doors, the tables, even the dog.) </li>
<li>Once you are reasonably certain you have every scrap of evidence you can use, find all possible entry points and scan them, too. </li>
<li>Return to your office. </li>
<li>Using your forensic scanning computer, scan your Scanner to upload all of your evidence into the database.</li>
<li>Browse through the resulting dossiers, looking for the one that either has the most complete set of prints, or the most suspicious items handled. </li>
<li>If you have 80% or more of the print (The print is displayed) go to step 10, otherwise continue to step 8.</li>
<li>Look for clues from the suit fibres you found on your perp, and go about looking for more evidence with this new information, scanning as you go. </li>
<li>Try to get a fingerprint card of your perp, as if used in the computer, the prints will be completed on their dossier.</li>
<li>Assuming you have enough of a print to see it, grab the biggest complete piece of the print and search the security records for it. </li>
<li>Since you now have both your dossier and the name of the person, print both out as evidence, and get security to nab your baddie.</li>
<li>Give yourself a pat on the back and a bottle of the ships finest vodka, you did it!</li>
</ol>
<p>
It really is that easy! Good luck!
</body>
</html>"}
/obj/item/book/manual/nuclear
name = "Fission Mailed: Nuclear Sabotage 101"
icon_state ="bookNuclear"
author = "Syndicate"
title = "Fission Mailed: Nuclear Sabotage 101"
dat = {"<html>
Nuclear Explosives 101:<br>
Hello and thank you for choosing the Syndicate for your nuclear information needs.<br>
Today's crash course will deal with the operation of a Fusion Class Nanotrasen made Nuclear Device.<br>
First and foremost, DO NOT TOUCH ANYTHING UNTIL THE BOMB IS IN PLACE.<br>
Pressing any button on the compacted bomb will cause it to extend and bolt itself into place.<br>
If this is done to unbolt it one must completely log in which at this time may not be possible.<br>
To make the nuclear device functional:<br>
<li>Place the nuclear device in the designated detonation zone.</li>
<li>Extend and anchor the nuclear device from its interface.</li>
<li>Insert the nuclear authorisation disk into slot.</li>
<li>Type numeric authorisation code into the keypad. This should have been provided. Note: If you make a mistake press R to reset the device.
<li>Press the E button to log onto the device.</li>
You now have activated the device. To deactivate the buttons at anytime for example when you've already prepped the bomb for detonation remove the auth disk OR press the R on the keypad.<br>
Now the bomb CAN ONLY be detonated using the timer. Manual detonation is not an option.<br>
Note: Nanotrasen is a pain in the neck.<br>
Toggle off the SAFETY.<br>
Note: You wouldn't believe how many Syndicate Operatives with doctorates have forgotten this step.<br>
So use the - - and + + to set a det time between 5 seconds and 10 minutes.<br>
Then press the timer toggle button to start the countdown.<br>
Now remove the auth. disk so that the buttons deactivate.<br>
Note: THE BOMB IS STILL SET AND WILL DETONATE<br>
Now before you remove the disk if you need to move the bomb you can:<br>
Toggle off the anchor, move it, and re-anchor.<br><br>
Good luck. Remember the order:<br>
<b>Disk, Code, Safety, Timer, Disk, RUN!</b><br>
Intelligence Analysts believe that normal Nanotrasen procedure is for the Captain to secure the nuclear authorisation disk.<br>
Good luck!
</html>"}
// Wiki books that are linked to the configured wiki link.
// A book that links to the wiki
/obj/item/book/manual/wiki
var/page_link = ""
window_size = "970x710"
/obj/item/book/manual/wiki/attack_self()
if(!dat)
initialize_wikibook()
..()
/obj/item/book/manual/wiki/proc/initialize_wikibook()
var/wikiurl = CONFIG_GET(string/wikiurl)
if(wikiurl)
dat = {"
<html><head>
<style>
iframe {
display: none;
}
</style>
</head>
<body>
<script type="text/javascript">
function pageloaded(myframe) {
document.getElementById("loading").style.display = "none";
myframe.style.display = "inline";
}
</script>
<p id='loading'>You start skimming through the manual...</p>
<iframe width='100%' height='97%' onload="pageloaded(this)" src="[wikiurl]/[page_link]?printable=yes&remove_links=1" frameborder="0" id="main_frame"></iframe>
</body>
</html>
"}
/obj/item/book/manual/wiki/chemistry
name = "Chemistry Textbook"
icon_state ="chemistrybook"
author = "Nanotrasen"
title = "Chemistry Textbook"
page_link = "Guide_to_chemistry"
/obj/item/book/manual/wiki/engineering_construction
name = "Station Repairs and Construction"
icon_state ="bookEngineering"
author = "Engineering Encyclopedia"
title = "Station Repairs and Construction"
page_link = "Guide_to_construction"
/obj/item/book/manual/wiki/engineering_guide
name = "Engineering Textbook"
icon_state ="bookEngineering2"
author = "Engineering Encyclopedia"
title = "Engineering Textbook"
page_link = "Guide_to_engineering"
/obj/item/book/manual/wiki/security_space_law
name = "Space Law"
desc = "A set of Nanotrasen guidelines for keeping law and order on their space stations."
icon_state = "bookSpaceLaw"
author = "Nanotrasen"
title = "Space Law"
page_link = "Space_Law"
/obj/item/book/manual/wiki/infections
name = "Infections - Making your own pandemic!"
icon_state = "bookInfections"
author = "Infections Encyclopedia"
title = "Infections - Making your own pandemic!"
page_link = "Infections"
/obj/item/book/manual/wiki/telescience
name = "Teleportation Science - Bluespace for dummies!"
icon_state = "book7"
author = "University of Bluespace"
title = "Teleportation Science - Bluespace for dummies!"
page_link = "Guide_to_telescience"
/obj/item/book/manual/wiki/engineering_hacking
name = "Hacking"
icon_state ="bookHacking"
author = "Engineering Encyclopedia"
title = "Hacking"
page_link = "Hacking"
>>>>>>> 4178c20... Configuration datum refactor (#30763)
@@ -40,6 +40,7 @@
max_w_class = WEIGHT_CLASS_GIGANTIC
max_combined_w_class = 35
resistance_flags = FIRE_PROOF
flags_2 = NO_MAT_REDEMPTION_2
var/pshoom = 'sound/items/pshoom.ogg'
var/alt_sound = 'sound/items/pshoom_2.ogg'
armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0, fire = 60, acid = 50)
+382 -381
View File
@@ -1,381 +1,382 @@
/*
* These absorb the functionality of the plant bag, ore satchel, etc.
* They use the use_to_pickup, quick_gather, and quick_empty functions
* that were already defined in weapon/storage, but which had been
* re-implemented in other classes.
*
* Contains:
* Trash Bag
* Mining Satchel
* Plant Bag
* Sheet Snatcher
* Book Bag
* Biowaste Bag
*
* -Sayu
*/
// Generic non-item
/obj/item/storage/bag
allow_quick_gather = 1
allow_quick_empty = 1
display_contents_with_number = 1 // should work fine now
use_to_pickup = 1
slot_flags = SLOT_BELT
// -----------------------------
// Trash bag
// -----------------------------
/obj/item/storage/bag/trash
name = "trash bag"
desc = "It's the heavy-duty black polymer kind. Time to take out the trash!"
icon = 'icons/obj/janitor.dmi'
icon_state = "trashbag"
item_state = "trashbag"
lefthand_file = 'icons/mob/inhands/equipment/custodial_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/custodial_righthand.dmi'
w_class = WEIGHT_CLASS_BULKY
max_w_class = WEIGHT_CLASS_SMALL
max_combined_w_class = 30
storage_slots = 30
can_hold = list() // any
cant_hold = list(/obj/item/disk/nuclear)
/obj/item/storage/bag/trash/suicide_act(mob/user)
user.visible_message("<span class='suicide'>[user] puts [src] over [user.p_their()] head and starts chomping at the insides! Disgusting!</span>")
playsound(loc, 'sound/items/eatfood.ogg', 50, 1, -1)
return (TOXLOSS)
/obj/item/storage/bag/trash/update_icon()
if(contents.len == 0)
icon_state = "[initial(icon_state)]"
else if(contents.len < 12)
icon_state = "[initial(icon_state)]1"
else if(contents.len < 21)
icon_state = "[initial(icon_state)]2"
else icon_state = "[initial(icon_state)]3"
/obj/item/storage/bag/trash/cyborg
/obj/item/storage/bag/trash/proc/janicart_insert(mob/user, obj/structure/janitorialcart/J)
J.put_in_cart(src, user)
J.mybag=src
J.update_icon()
/obj/item/storage/bag/trash/cyborg/janicart_insert(mob/user, obj/structure/janitorialcart/J)
return
/obj/item/storage/bag/trash/bluespace
name = "trash bag of holding"
desc = "The latest and greatest in custodial convenience, a trashbag that is capable of holding vast quantities of garbage."
icon_state = "bluetrashbag"
origin_tech = "materials=4;bluespace=4;engineering=4;plasmatech=3"
max_combined_w_class = 60
storage_slots = 60
// -----------------------------
// Mining Satchel
// -----------------------------
/obj/item/storage/bag/ore
name = "mining satchel"
desc = "This little bugger can be used to store and transport ores."
icon = 'icons/obj/mining.dmi'
icon_state = "satchel"
origin_tech = "engineering=2"
slot_flags = SLOT_BELT | SLOT_POCKET
w_class = WEIGHT_CLASS_NORMAL
storage_slots = 50
max_combined_w_class = 200 //Doesn't matter what this is, so long as it's more or equal to storage_slots * ore.w_class
max_w_class = WEIGHT_CLASS_NORMAL
can_hold = list(/obj/item/ore)
var/spam_protection = FALSE //If this is TRUE, the holder won't receive any messages when they fail to pick up ore through crossing it
/obj/item/storage/bag/ore/cyborg
name = "cyborg mining satchel"
/obj/item/storage/bag/ore/holding //miners, your messiah has arrived
name = "mining satchel of holding"
desc = "A revolution in convenience, this satchel allows for huge amounts of ore storage. It's been outfitted with anti-malfunction safety measures."
storage_slots = INFINITY
max_combined_w_class = INFINITY
origin_tech = "bluespace=4;materials=3;engineering=3"
icon_state = "satchel_bspace"
// -----------------------------
// Plant bag
// -----------------------------
/obj/item/storage/bag/plants
name = "plant bag"
icon = 'icons/obj/hydroponics/equipment.dmi'
icon_state = "plantbag"
storage_slots = 100; //the number of plant pieces it can carry.
max_combined_w_class = 100 //Doesn't matter what this is, so long as it's more or equal to storage_slots * plants.w_class
max_w_class = WEIGHT_CLASS_NORMAL
w_class = WEIGHT_CLASS_TINY
can_hold = list(/obj/item/reagent_containers/food/snacks/grown, /obj/item/seeds, /obj/item/grown, /obj/item/reagent_containers/honeycomb)
resistance_flags = FLAMMABLE
////////
/obj/item/storage/bag/plants/portaseeder
name = "portable seed extractor"
desc = "For the enterprising botanist on the go. Less efficient than the stationary model, it creates one seed per plant."
icon_state = "portaseeder"
origin_tech = "biotech=3;engineering=2"
/obj/item/storage/bag/plants/portaseeder/verb/dissolve_contents()
set name = "Activate Seed Extraction"
set category = "Object"
set desc = "Activate to convert your plants into plantable seeds."
if(usr.stat || !usr.canmove || usr.restrained())
return
for(var/obj/item/O in contents)
seedify(O, 1)
close_all()
// -----------------------------
// Sheet Snatcher
// -----------------------------
// Because it stacks stacks, this doesn't operate normally.
// However, making it a storage/bag allows us to reuse existing code in some places. -Sayu
/obj/item/storage/bag/sheetsnatcher
name = "sheet snatcher"
desc = "A patented Nanotrasen storage system designed for any kind of mineral sheet."
icon = 'icons/obj/mining.dmi'
icon_state = "sheetsnatcher"
var/capacity = 300; //the number of sheets it can carry.
w_class = WEIGHT_CLASS_NORMAL
allow_quick_empty = 1 // this function is superceded
/obj/item/storage/bag/sheetsnatcher/can_be_inserted(obj/item/W, stop_messages = 0)
if(!istype(W, /obj/item/stack/sheet) || istype(W, /obj/item/stack/sheet/mineral/sandstone) || istype(W, /obj/item/stack/sheet/mineral/wood))
if(!stop_messages)
to_chat(usr, "The snatcher does not accept [W].")
return 0 //I don't care, but the existing code rejects them for not being "sheets" *shrug* -Sayu
var/current = 0
for(var/obj/item/stack/sheet/S in contents)
current += S.amount
if(capacity == current)//If it's full, you're done
if(!stop_messages)
to_chat(usr, "<span class='danger'>The snatcher is full.</span>")
return 0
return 1
// Modified handle_item_insertion. Would prefer not to, but...
/obj/item/storage/bag/sheetsnatcher/handle_item_insertion(obj/item/W, prevent_warning = 0)
var/obj/item/stack/sheet/S = W
if(!istype(S)) return 0
var/amount
var/inserted = 0
var/current = 0
for(var/obj/item/stack/sheet/S2 in contents)
current += S2.amount
if(capacity < current + S.amount)//If the stack will fill it up
amount = capacity - current
else
amount = S.amount
for(var/obj/item/stack/sheet/sheet in contents)
if(S.type == sheet.type) // we are violating the amount limitation because these are not sane objects
sheet.amount += amount // they should only be removed through procs in this file, which split them up.
S.amount -= amount
inserted = 1
break
if(!inserted || !S.amount)
usr.dropItemToGround(S)
if (usr.client && usr.s_active != src)
usr.client.screen -= S
S.dropped(usr)
if(!S.amount)
qdel(S)
else
if(S.pulledby)
S.pulledby.stop_pulling()
S.loc = src
orient2hud(usr)
if(usr.s_active)
usr.s_active.show_to(usr)
update_icon()
return 1
// Sets up numbered display to show the stack size of each stored mineral
// NOTE: numbered display is turned off currently because it's broken
/obj/item/storage/bag/sheetsnatcher/orient2hud(mob/user)
var/adjusted_contents = contents.len
//Numbered contents display
var/list/datum/numbered_display/numbered_contents
if(display_contents_with_number)
numbered_contents = list()
adjusted_contents = 0
for(var/obj/item/stack/sheet/I in contents)
adjusted_contents++
var/datum/numbered_display/D = new/datum/numbered_display(I)
D.number = I.amount
numbered_contents.Add( D )
var/row_num = 0
var/col_count = min(7,storage_slots) -1
if (adjusted_contents > 7)
row_num = round((adjusted_contents-1) / 7) // 7 is the maximum allowed width.
src.standard_orient_objs(row_num, col_count, numbered_contents)
return
// Modified quick_empty verb drops appropriate sized stacks
/obj/item/storage/bag/sheetsnatcher/quick_empty()
var/location = get_turf(src)
for(var/obj/item/stack/sheet/S in contents)
while(S.amount)
var/obj/item/stack/sheet/N = new S.type(location)
var/stacksize = min(S.amount,N.max_amount)
N.amount = stacksize
S.amount -= stacksize
if(!S.amount)
qdel(S)// todo: there's probably something missing here
orient2hud(usr)
if(usr.s_active)
usr.s_active.show_to(usr)
update_icon()
// Instead of removing
/obj/item/storage/bag/sheetsnatcher/remove_from_storage(obj/item/W, atom/new_location)
var/obj/item/stack/sheet/S = W
if(!istype(S)) return 0
//I would prefer to drop a new stack, but the item/attack_hand code
// that calls this can't recieve a different object than you clicked on.
//Therefore, make a new stack internally that has the remainder.
// -Sayu
if(S.amount > S.max_amount)
var/obj/item/stack/sheet/temp = new S.type(src)
temp.amount = S.amount - S.max_amount
S.amount = S.max_amount
return ..(S,new_location)
// -----------------------------
// Sheet Snatcher (Cyborg)
// -----------------------------
/obj/item/storage/bag/sheetsnatcher/borg
name = "sheet snatcher 9000"
desc = ""
capacity = 500//Borgs get more because >specialization
// -----------------------------
// Book bag
// -----------------------------
/obj/item/storage/bag/books
name = "book bag"
desc = "A bag for books."
icon = 'icons/obj/library.dmi'
icon_state = "bookbag"
display_contents_with_number = 0 //This would look really stupid otherwise
storage_slots = 7
max_combined_w_class = 21
max_w_class = WEIGHT_CLASS_NORMAL
w_class = WEIGHT_CLASS_BULKY //Bigger than a book because physics
can_hold = list(/obj/item/book, /obj/item/storage/book, /obj/item/spellbook)
resistance_flags = FLAMMABLE
/*
* Trays - Agouri
*/
/obj/item/storage/bag/tray
name = "tray"
icon = 'icons/obj/food/containers.dmi'
icon_state = "tray"
desc = "A metal tray to lay food on."
force = 5
throwforce = 10
throw_speed = 3
throw_range = 5
w_class = WEIGHT_CLASS_BULKY
flags_1 = CONDUCT_1
materials = list(MAT_METAL=3000)
preposition = "on"
/obj/item/storage/bag/tray/attack(mob/living/M, mob/living/user)
..()
// Drop all the things. All of them.
var/list/obj/item/oldContents = contents.Copy()
quick_empty()
// Make each item scatter a bit
for(var/obj/item/I in oldContents)
spawn()
for(var/i = 1, i <= rand(1,2), i++)
if(I)
step(I, pick(NORTH,SOUTH,EAST,WEST))
sleep(rand(2,4))
if(prob(50))
playsound(M, 'sound/items/trayhit1.ogg', 50, 1)
else
playsound(M, 'sound/items/trayhit2.ogg', 50, 1)
if(ishuman(M) || ismonkey(M))
if(prob(10))
M.Knockdown(40)
/obj/item/storage/bag/tray/proc/rebuild_overlays()
cut_overlays()
for(var/obj/item/I in contents)
add_overlay(mutable_appearance(I.icon, I.icon_state))
/obj/item/storage/bag/tray/remove_from_storage(obj/item/W as obj, atom/new_location)
..()
rebuild_overlays()
/obj/item/storage/bag/tray/handle_item_insertion(obj/item/I, prevent_warning = 0)
add_overlay(mutable_appearance(I.icon, I.icon_state))
. = ..()
/*
* Chemistry bag
*/
/obj/item/storage/bag/chemistry
name = "chemistry bag"
icon = 'icons/obj/chemical.dmi'
icon_state = "bag"
desc = "A bag for storing pills, patches, and bottles."
storage_slots = 50
max_combined_w_class = 200
w_class = WEIGHT_CLASS_TINY
preposition = "in"
can_hold = list(/obj/item/reagent_containers/pill, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/glass/bottle)
resistance_flags = FLAMMABLE
/*
* Biowaste bag (mostly for xenobiologists)
*/
/obj/item/storage/bag/bio
name = "bio bag"
icon = 'icons/obj/chemical.dmi'
icon_state = "biobag"
desc = "A bag for the safe transportation and disposal of biowaste and other biological materials."
storage_slots = 25
max_combined_w_class = 200
w_class = WEIGHT_CLASS_TINY
preposition = "in"
can_hold = list(/obj/item/slime_extract, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/blood, /obj/item/reagent_containers/hypospray/medipen, /obj/item/reagent_containers/food/snacks/deadmouse, /obj/item/reagent_containers/food/snacks/monkeycube)
resistance_flags = FLAMMABLE
/*
* These absorb the functionality of the plant bag, ore satchel, etc.
* They use the use_to_pickup, quick_gather, and quick_empty functions
* that were already defined in weapon/storage, but which had been
* re-implemented in other classes.
*
* Contains:
* Trash Bag
* Mining Satchel
* Plant Bag
* Sheet Snatcher
* Book Bag
* Biowaste Bag
*
* -Sayu
*/
// Generic non-item
/obj/item/storage/bag
allow_quick_gather = 1
allow_quick_empty = 1
display_contents_with_number = 1 // should work fine now
use_to_pickup = 1
slot_flags = SLOT_BELT
// -----------------------------
// Trash bag
// -----------------------------
/obj/item/storage/bag/trash
name = "trash bag"
desc = "It's the heavy-duty black polymer kind. Time to take out the trash!"
icon = 'icons/obj/janitor.dmi'
icon_state = "trashbag"
item_state = "trashbag"
lefthand_file = 'icons/mob/inhands/equipment/custodial_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/custodial_righthand.dmi'
w_class = WEIGHT_CLASS_BULKY
max_w_class = WEIGHT_CLASS_SMALL
max_combined_w_class = 30
storage_slots = 30
can_hold = list() // any
cant_hold = list(/obj/item/disk/nuclear)
/obj/item/storage/bag/trash/suicide_act(mob/user)
user.visible_message("<span class='suicide'>[user] puts [src] over [user.p_their()] head and starts chomping at the insides! Disgusting!</span>")
playsound(loc, 'sound/items/eatfood.ogg', 50, 1, -1)
return (TOXLOSS)
/obj/item/storage/bag/trash/update_icon()
if(contents.len == 0)
icon_state = "[initial(icon_state)]"
else if(contents.len < 12)
icon_state = "[initial(icon_state)]1"
else if(contents.len < 21)
icon_state = "[initial(icon_state)]2"
else icon_state = "[initial(icon_state)]3"
/obj/item/storage/bag/trash/cyborg
/obj/item/storage/bag/trash/proc/janicart_insert(mob/user, obj/structure/janitorialcart/J)
J.put_in_cart(src, user)
J.mybag=src
J.update_icon()
/obj/item/storage/bag/trash/cyborg/janicart_insert(mob/user, obj/structure/janitorialcart/J)
return
/obj/item/storage/bag/trash/bluespace
name = "trash bag of holding"
desc = "The latest and greatest in custodial convenience, a trashbag that is capable of holding vast quantities of garbage."
icon_state = "bluetrashbag"
origin_tech = "materials=4;bluespace=4;engineering=4;plasmatech=3"
max_combined_w_class = 60
storage_slots = 60
flags_2 = NO_MAT_REDEMPTION_2
// -----------------------------
// Mining Satchel
// -----------------------------
/obj/item/storage/bag/ore
name = "mining satchel"
desc = "This little bugger can be used to store and transport ores."
icon = 'icons/obj/mining.dmi'
icon_state = "satchel"
origin_tech = "engineering=2"
slot_flags = SLOT_BELT | SLOT_POCKET
w_class = WEIGHT_CLASS_NORMAL
storage_slots = 50
max_combined_w_class = 200 //Doesn't matter what this is, so long as it's more or equal to storage_slots * ore.w_class
max_w_class = WEIGHT_CLASS_NORMAL
can_hold = list(/obj/item/ore)
var/spam_protection = FALSE //If this is TRUE, the holder won't receive any messages when they fail to pick up ore through crossing it
/obj/item/storage/bag/ore/cyborg
name = "cyborg mining satchel"
/obj/item/storage/bag/ore/holding //miners, your messiah has arrived
name = "mining satchel of holding"
desc = "A revolution in convenience, this satchel allows for huge amounts of ore storage. It's been outfitted with anti-malfunction safety measures."
storage_slots = INFINITY
max_combined_w_class = INFINITY
origin_tech = "bluespace=4;materials=3;engineering=3"
icon_state = "satchel_bspace"
// -----------------------------
// Plant bag
// -----------------------------
/obj/item/storage/bag/plants
name = "plant bag"
icon = 'icons/obj/hydroponics/equipment.dmi'
icon_state = "plantbag"
storage_slots = 100; //the number of plant pieces it can carry.
max_combined_w_class = 100 //Doesn't matter what this is, so long as it's more or equal to storage_slots * plants.w_class
max_w_class = WEIGHT_CLASS_NORMAL
w_class = WEIGHT_CLASS_TINY
can_hold = list(/obj/item/reagent_containers/food/snacks/grown, /obj/item/seeds, /obj/item/grown, /obj/item/reagent_containers/honeycomb)
resistance_flags = FLAMMABLE
////////
/obj/item/storage/bag/plants/portaseeder
name = "portable seed extractor"
desc = "For the enterprising botanist on the go. Less efficient than the stationary model, it creates one seed per plant."
icon_state = "portaseeder"
origin_tech = "biotech=3;engineering=2"
/obj/item/storage/bag/plants/portaseeder/verb/dissolve_contents()
set name = "Activate Seed Extraction"
set category = "Object"
set desc = "Activate to convert your plants into plantable seeds."
if(usr.stat || !usr.canmove || usr.restrained())
return
for(var/obj/item/O in contents)
seedify(O, 1)
close_all()
// -----------------------------
// Sheet Snatcher
// -----------------------------
// Because it stacks stacks, this doesn't operate normally.
// However, making it a storage/bag allows us to reuse existing code in some places. -Sayu
/obj/item/storage/bag/sheetsnatcher
name = "sheet snatcher"
desc = "A patented Nanotrasen storage system designed for any kind of mineral sheet."
icon = 'icons/obj/mining.dmi'
icon_state = "sheetsnatcher"
var/capacity = 300; //the number of sheets it can carry.
w_class = WEIGHT_CLASS_NORMAL
allow_quick_empty = 1 // this function is superceded
/obj/item/storage/bag/sheetsnatcher/can_be_inserted(obj/item/W, stop_messages = 0)
if(!istype(W, /obj/item/stack/sheet) || istype(W, /obj/item/stack/sheet/mineral/sandstone) || istype(W, /obj/item/stack/sheet/mineral/wood))
if(!stop_messages)
to_chat(usr, "The snatcher does not accept [W].")
return 0 //I don't care, but the existing code rejects them for not being "sheets" *shrug* -Sayu
var/current = 0
for(var/obj/item/stack/sheet/S in contents)
current += S.amount
if(capacity == current)//If it's full, you're done
if(!stop_messages)
to_chat(usr, "<span class='danger'>The snatcher is full.</span>")
return 0
return 1
// Modified handle_item_insertion. Would prefer not to, but...
/obj/item/storage/bag/sheetsnatcher/handle_item_insertion(obj/item/W, prevent_warning = 0)
var/obj/item/stack/sheet/S = W
if(!istype(S)) return 0
var/amount
var/inserted = 0
var/current = 0
for(var/obj/item/stack/sheet/S2 in contents)
current += S2.amount
if(capacity < current + S.amount)//If the stack will fill it up
amount = capacity - current
else
amount = S.amount
for(var/obj/item/stack/sheet/sheet in contents)
if(S.type == sheet.type) // we are violating the amount limitation because these are not sane objects
sheet.amount += amount // they should only be removed through procs in this file, which split them up.
S.amount -= amount
inserted = 1
break
if(!inserted || !S.amount)
usr.dropItemToGround(S)
if (usr.client && usr.s_active != src)
usr.client.screen -= S
S.dropped(usr)
if(!S.amount)
qdel(S)
else
if(S.pulledby)
S.pulledby.stop_pulling()
S.loc = src
orient2hud(usr)
if(usr.s_active)
usr.s_active.show_to(usr)
update_icon()
return 1
// Sets up numbered display to show the stack size of each stored mineral
// NOTE: numbered display is turned off currently because it's broken
/obj/item/storage/bag/sheetsnatcher/orient2hud(mob/user)
var/adjusted_contents = contents.len
//Numbered contents display
var/list/datum/numbered_display/numbered_contents
if(display_contents_with_number)
numbered_contents = list()
adjusted_contents = 0
for(var/obj/item/stack/sheet/I in contents)
adjusted_contents++
var/datum/numbered_display/D = new/datum/numbered_display(I)
D.number = I.amount
numbered_contents.Add( D )
var/row_num = 0
var/col_count = min(7,storage_slots) -1
if (adjusted_contents > 7)
row_num = round((adjusted_contents-1) / 7) // 7 is the maximum allowed width.
src.standard_orient_objs(row_num, col_count, numbered_contents)
return
// Modified quick_empty verb drops appropriate sized stacks
/obj/item/storage/bag/sheetsnatcher/quick_empty()
var/location = get_turf(src)
for(var/obj/item/stack/sheet/S in contents)
while(S.amount)
var/obj/item/stack/sheet/N = new S.type(location)
var/stacksize = min(S.amount,N.max_amount)
N.amount = stacksize
S.amount -= stacksize
if(!S.amount)
qdel(S)// todo: there's probably something missing here
orient2hud(usr)
if(usr.s_active)
usr.s_active.show_to(usr)
update_icon()
// Instead of removing
/obj/item/storage/bag/sheetsnatcher/remove_from_storage(obj/item/W, atom/new_location)
var/obj/item/stack/sheet/S = W
if(!istype(S)) return 0
//I would prefer to drop a new stack, but the item/attack_hand code
// that calls this can't recieve a different object than you clicked on.
//Therefore, make a new stack internally that has the remainder.
// -Sayu
if(S.amount > S.max_amount)
var/obj/item/stack/sheet/temp = new S.type(src)
temp.amount = S.amount - S.max_amount
S.amount = S.max_amount
return ..(S,new_location)
// -----------------------------
// Sheet Snatcher (Cyborg)
// -----------------------------
/obj/item/storage/bag/sheetsnatcher/borg
name = "sheet snatcher 9000"
desc = ""
capacity = 500//Borgs get more because >specialization
// -----------------------------
// Book bag
// -----------------------------
/obj/item/storage/bag/books
name = "book bag"
desc = "A bag for books."
icon = 'icons/obj/library.dmi'
icon_state = "bookbag"
display_contents_with_number = 0 //This would look really stupid otherwise
storage_slots = 7
max_combined_w_class = 21
max_w_class = WEIGHT_CLASS_NORMAL
w_class = WEIGHT_CLASS_BULKY //Bigger than a book because physics
can_hold = list(/obj/item/book, /obj/item/storage/book, /obj/item/spellbook)
resistance_flags = FLAMMABLE
/*
* Trays - Agouri
*/
/obj/item/storage/bag/tray
name = "tray"
icon = 'icons/obj/food/containers.dmi'
icon_state = "tray"
desc = "A metal tray to lay food on."
force = 5
throwforce = 10
throw_speed = 3
throw_range = 5
w_class = WEIGHT_CLASS_BULKY
flags_1 = CONDUCT_1
materials = list(MAT_METAL=3000)
preposition = "on"
/obj/item/storage/bag/tray/attack(mob/living/M, mob/living/user)
..()
// Drop all the things. All of them.
var/list/obj/item/oldContents = contents.Copy()
quick_empty()
// Make each item scatter a bit
for(var/obj/item/I in oldContents)
spawn()
for(var/i = 1, i <= rand(1,2), i++)
if(I)
step(I, pick(NORTH,SOUTH,EAST,WEST))
sleep(rand(2,4))
if(prob(50))
playsound(M, 'sound/items/trayhit1.ogg', 50, 1)
else
playsound(M, 'sound/items/trayhit2.ogg', 50, 1)
if(ishuman(M) || ismonkey(M))
if(prob(10))
M.Knockdown(40)
/obj/item/storage/bag/tray/proc/rebuild_overlays()
cut_overlays()
for(var/obj/item/I in contents)
add_overlay(mutable_appearance(I.icon, I.icon_state))
/obj/item/storage/bag/tray/remove_from_storage(obj/item/W as obj, atom/new_location)
..()
rebuild_overlays()
/obj/item/storage/bag/tray/handle_item_insertion(obj/item/I, prevent_warning = 0)
add_overlay(mutable_appearance(I.icon, I.icon_state))
. = ..()
/*
* Chemistry bag
*/
/obj/item/storage/bag/chemistry
name = "chemistry bag"
icon = 'icons/obj/chemical.dmi'
icon_state = "bag"
desc = "A bag for storing pills, patches, and bottles."
storage_slots = 50
max_combined_w_class = 200
w_class = WEIGHT_CLASS_TINY
preposition = "in"
can_hold = list(/obj/item/reagent_containers/pill, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/glass/bottle)
resistance_flags = FLAMMABLE
/*
* Biowaste bag (mostly for xenobiologists)
*/
/obj/item/storage/bag/bio
name = "bio bag"
icon = 'icons/obj/chemical.dmi'
icon_state = "biobag"
desc = "A bag for the safe transportation and disposal of biowaste and other biological materials."
storage_slots = 25
max_combined_w_class = 200
w_class = WEIGHT_CLASS_TINY
preposition = "in"
can_hold = list(/obj/item/slime_extract, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/blood, /obj/item/reagent_containers/hypospray/medipen, /obj/item/reagent_containers/food/snacks/deadmouse, /obj/item/reagent_containers/food/snacks/monkeycube)
resistance_flags = FLAMMABLE
+35 -19
View File
@@ -26,12 +26,11 @@
return
/obj/structure/lattice/ratvar_act()
if(IsEven(x + y))
new /obj/structure/lattice/clockwork(loc)
else
new /obj/structure/lattice/clockwork/large(loc)
new /obj/structure/lattice/clockwork(loc)
/obj/structure/lattice/attackby(obj/item/C, mob/user, params)
if(resistance_flags & INDESTRUCTIBLE)
return
if(istype(C, /obj/item/wirecutters))
to_chat(user, "<span class='notice'>Slicing [name] joints ...</span>")
deconstruct()
@@ -45,7 +44,6 @@
qdel(src)
/obj/structure/lattice/singularity_pull(S, current_size)
..()
if(current_size >= STAGE_FOUR)
deconstruct()
@@ -55,22 +53,22 @@
icon = 'icons/obj/smooth_structures/lattice_clockwork.dmi'
/obj/structure/lattice/clockwork/Initialize(mapload)
canSmoothWith += /turf/open/indestructible/clock_spawn_room //list overrides are a terrible thing
. = ..()
ratvar_act()
if(z == ZLEVEL_CITYOFCOGS)
resistance_flags |= INDESTRUCTIBLE
/obj/structure/lattice/clockwork/ratvar_act()
if(IsOdd(x+y))
new /obj/structure/lattice/clockwork/large(loc) // deletes old one
/obj/structure/lattice/clockwork/large/Initialize(mapload)
. = ..()
icon = 'icons/obj/smooth_structures/lattice_clockwork_large.dmi'
pixel_x = -9
pixel_y = -9
/obj/structure/lattice/clockwork/large/ratvar_act()
if(IsEven(x + y))
new /obj/structure/lattice/clockwork(loc)
icon = 'icons/obj/smooth_structures/lattice_clockwork_large.dmi'
pixel_x = -9
pixel_y = -9
else
icon = 'icons/obj/smooth_structures/lattice_clockwork.dmi'
pixel_x = 0
pixel_y = 0
return TRUE
/obj/structure/lattice/catwalk
name = "catwalk"
@@ -99,11 +97,29 @@
/obj/structure/lattice/catwalk/clockwork
name = "clockwork catwalk"
icon = 'icons/obj/smooth_structures/catwalk_clockwork.dmi'
canSmoothWith = list(/obj/structure/lattice,
/turf/open/floor,
/turf/open/indestructible/clock_spawn_room,
/turf/closed/wall,
/obj/structure/falsewall)
smooth = SMOOTH_MORE
/obj/structure/lattice/catwalk/clockwork/Initialize(mapload)
. = ..()
new /obj/effect/temp_visual/ratvar/floor/catwalk(loc)
new /obj/effect/temp_visual/ratvar/beam/catwalk(loc)
ratvar_act()
if(!mapload)
new /obj/effect/temp_visual/ratvar/floor/catwalk(loc)
new /obj/effect/temp_visual/ratvar/beam/catwalk(loc)
if(z == ZLEVEL_CITYOFCOGS)
resistance_flags |= INDESTRUCTIBLE
/obj/structure/lattice/catwalk/clockwork/ratvar_act()
return
if(IsOdd(x+y))
icon = 'icons/obj/smooth_structures/catwalk_clockwork_large.dmi'
pixel_x = -9
pixel_y = -9
else
icon = 'icons/obj/smooth_structures/catwalk_clockwork.dmi'
pixel_x = 0
pixel_y = 0
return TRUE
@@ -142,6 +142,7 @@
name = "clockwork floor"
desc = "Tightly-pressed brass tiles. They emit minute vibration."
icon_state = "plating"
baseturf = /turf/open/floor/clockwork
var/uses_overlay = TRUE
var/obj/effect/clockwork/overlay/floor/realappearence
@@ -152,18 +153,16 @@
new /obj/effect/temp_visual/ratvar/beam(src)
realappearence = new /obj/effect/clockwork/overlay/floor(src)
realappearence.linked = src
change_construction_value(1)
/turf/open/floor/clockwork/Destroy()
STOP_PROCESSING(SSobj, src)
if(uses_overlay)
change_construction_value(-1)
if(realappearence)
qdel(realappearence)
realappearence = null
if(uses_overlay && realappearence)
QDEL_NULL(realappearence)
return ..()
/turf/open/floor/clockwork/ReplaceWithLattice()
if(baseturf == type)
return
..()
for(var/obj/structure/lattice/L in src)
L.ratvar_act()
@@ -197,6 +196,8 @@
L.adjustToxLoss(-3, TRUE, TRUE)
/turf/open/floor/clockwork/attackby(obj/item/I, mob/living/user, params)
if(baseturf == type)
return
if(istype(I, /obj/item/crowbar))
user.visible_message("<span class='notice'>[user] begins slowly prying up [src]...</span>", "<span class='notice'>You begin painstakingly prying up [src]...</span>")
playsound(src, I.usesound, 20, 1)
+47
View File
@@ -0,0 +1,47 @@
/turf/open/indestructible/reebe_void
name = "void"
icon_state = "reebemap"
layer = SPACE_LAYER
baseturf = /turf/open/indestructible/reebe_void
planetary_atmos = TRUE
/turf/open/indestructible/reebe_void/Initialize(mapload)
. = ..()
icon_state = "reebegame"
/turf/open/indestructible/reebe_void/spawning
icon_state = "reebespawn"
/turf/open/indestructible/reebe_void/spawning/Initialize(mapload)
. = ..()
if(mapload)
for(var/i in 1 to 3)
if(prob(1))
new /obj/item/clockwork/alloy_shards/large(src)
if(prob(2))
new /obj/item/clockwork/alloy_shards/medium(src)
if(prob(3))
new /obj/item/clockwork/alloy_shards/small(src)
/turf/open/indestructible/reebe_void/spawning/lattices
icon_state = "reebelattice"
/turf/open/indestructible/reebe_void/spawning/lattices/Initialize(mapload)
. = ..()
if(mapload)
if(prob(2.5))
new /obj/structure/lattice/catwalk/clockwork(src)
else if(prob(5))
new /obj/structure/lattice/clockwork(src)
/turf/open/indestructible/reebe_void/Enter(atom/movable/AM, atom/old_loc)
if(!..())
return FALSE
else
if(istype(AM, /obj/structure/window))
return FALSE
if(istype(AM, /obj/item/projectile))
return TRUE
if((locate(/obj/structure/lattice) in src))
return TRUE
return FALSE
+1 -7
View File
@@ -55,6 +55,7 @@
sheet_type = /obj/item/stack/tile/brass
sheet_amount = 1
girder_type = /obj/structure/destructible/clockwork/wall_gear
baseturf = /turf/open/floor/clockwork/reebe
var/obj/effect/clockwork/overlay/wall/realappearence
/turf/closed/wall/clockwork/Initialize()
@@ -63,15 +64,8 @@
new /obj/effect/temp_visual/ratvar/beam(src)
realappearence = new /obj/effect/clockwork/overlay/wall(src)
realappearence.linked = src
change_construction_value(5)
/turf/closed/wall/clockwork/examine(mob/user)
..()
if((is_servant_of_ratvar(user) || isobserver(user)))
to_chat(user, "<span class='brass'>It is linked to a Tinkerer's Cache, generating components!</span>")
/turf/closed/wall/clockwork/Destroy()
change_construction_value(-5)
if(realappearence)
qdel(realappearence)
realappearence = null
+1
View File
@@ -14,6 +14,7 @@
var/global/datum/gas_mixture/immutable/space/space_gas = new
plane = PLANE_SPACE
layer = SPACE_LAYER
light_power = 0.25
dynamic_lighting = DYNAMIC_LIGHTING_DISABLED
+7 -36
View File
@@ -1,3 +1,6 @@
#define PR_ANNOUNCEMENTS_PER_ROUND 5 //The number of unique PR announcements allowed per round
//This makes sure that a single person can only spam 3 reopens and 3 closes before being ignored
GLOBAL_VAR(security_mode)
GLOBAL_PROTECT(security_mode)
@@ -19,8 +22,7 @@ GLOBAL_PROTECT(security_mode)
SetupLogs()
if(!RunningService()) //tgs2 support
GLOB.revdata.DownloadPRDetails()
SERVER_TOOLS_ON_NEW
load_motd()
load_admins()
@@ -33,8 +35,6 @@ GLOBAL_PROTECT(security_mode)
Master.Initialize(10, FALSE)
if(CONFIG_GET(flag/irc_announce_new_game))
IRCBroadcast("New round starting on [SSmapping.config.map_name]!")
/world/proc/SetupExternalRSC()
#if (PRELOAD_RSC == 0)
@@ -125,8 +125,8 @@ GLOBAL_PROTECT(security_mode)
if(!pinging && !playing && config && CONFIG_GET(flag/log_world_topic))
WRITE_FILE(GLOB.world_game_log, "TOPIC: \"[T]\", from:[addr], master:[master], key:[key]")
if(input[SERVICE_CMD_PARAM_KEY])
return ServiceCommand(input)
SERVER_TOOLS_ON_TOPIC //redirect to server tools if necessary
var/comms_key = CONFIG_GET(string/comms_key)
var/key_valid = (comms_key && input["key"] == comms_key)
@@ -143,17 +143,6 @@ GLOBAL_PROTECT(security_mode)
n++
return n
else if("ircstatus" in input) //tgs2 support
var/static/last_irc_status = 0
if(world.time - last_irc_status < 50)
return
var/list/adm = get_admin_counts()
var/list/allmins = adm["total"]
var/status = "Admins: [allmins.len] (Active: [english_list(adm["present"])] AFK: [english_list(adm["afk"])] Stealth: [english_list(adm["stealth"])] Skipped: [english_list(adm["noflags"])]). "
status += "Players: [GLOB.clients.len] (Active: [get_active_player_count(0,1,0)]). Mode: [SSticker.mode.name]."
send2irc("Status", status)
last_irc_status = world.time
else if("status" in input)
var/list/s = list()
s["version"] = GLOB.game_version
@@ -211,24 +200,6 @@ GLOBAL_PROTECT(security_mode)
if(input["crossmessage"] == "News_Report")
minor_announce(input["message"], "Breaking Update From [input["message_sender"]]")
else if("adminmsg" in input) //tgs2 support
if(!key_valid)
return "Bad Key"
else
return IrcPm(input["adminmsg"],input["msg"],input["sender"])
else if("namecheck" in input) //tgs2 support
if(!key_valid)
return "Bad Key"
else
log_admin("IRC Name Check: [input["sender"]] on [input["namecheck"]]")
message_admins("IRC name checking on [input["namecheck"]] from [input["sender"]]")
return keywords_lookup(input["namecheck"],1)
else if("adminwho" in input) //tgs2 support
if(!key_valid)
return "Bad Key"
else
return ircadminwho()
else if("server_hop" in input)
show_server_hop_transfer_screen(input["server_hop"])
@@ -247,7 +218,7 @@ GLOBAL_PROTECT(security_mode)
C.AnnouncePR(final_composed)
/world/Reboot(reason = 0, fast_track = FALSE)
ServiceReboot() //handles alternative actions if necessary
SERVER_TOOLS_ON_REBOOT
if (reason || fast_track) //special reboot, do none of the normal stuff
if (usr)
log_admin("[key_name(usr)] Has requested an immediate world restart via client side debugging tools")
+9 -6
View File
@@ -421,8 +421,8 @@
return
var/list/options = list("Regular Restart", "Hard Restart (No Delay/Feeback Reason)", "Hardest Restart (No actions, just reboot)")
if(world.RunningService())
options += "Service Restart (Force restart DD)";
if(SERVER_TOOLS_PRESENT)
options += "Server Restart (Kill and restart DD)";
var/rebootconfirm
if(SSticker.admin_delay_notice)
@@ -434,16 +434,19 @@
var/result = input(usr, "Select reboot method", "World Reboot", options[1]) as null|anything in options
if(result)
SSblackbox.add_details("admin_verb","Reboot World") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
var/init_by = "Initiated by [usr.client.holder.fakekey ? "Admin" : usr.key]."
switch(result)
if("Regular Restart")
SSticker.Reboot("Initiated by [usr.client.holder.fakekey ? "Admin" : usr.key].", "admin reboot - by [usr.key] [usr.client.holder.fakekey ? "(stealth)" : ""]", 10)
SSticker.Reboot(init_by, "admin reboot - by [usr.key] [usr.client.holder.fakekey ? "(stealth)" : ""]", 10)
if("Hard Restart (No Delay, No Feeback Reason)")
to_chat(world, "World reboot - [init_by]")
world.Reboot()
if("Hardest Restart (No actions, just reboot)")
to_chat(world, "Hard world reboot - [init_by]")
world.Reboot(fast_track = TRUE)
if("Service Restart (Force restart DD)")
GLOB.reboot_mode = REBOOT_MODE_HARD
world.ServiceReboot()
if("Server Restart (Kill and restart DD)")
to_chat(world, "Server restart - [init_by]")
SERVER_TOOLS_REBOOT_BYOND
/datum/admins/proc/end_round()
set category = "Server"
+63
View File
@@ -0,0 +1,63 @@
#define IRC_STATUS_THROTTLE 5
/datum/server_tools_command/ircstatus
name = "status"
help_text = "Gets the admincount, playercount, gamemode, and true game mode of the server"
admin_only = TRUE
var/static/last_irc_status = 0
/datum/server_tools_command/ircstatus/Run(sender, params)
var/rtod = REALTIMEOFDAY
if(rtod - last_irc_status < IRC_STATUS_THROTTLE)
return
last_irc_status = rtod
var/list/adm = get_admin_counts()
var/list/allmins = adm["total"]
var/status = "Admins: [allmins.len] (Active: [english_list(adm["present"])] AFK: [english_list(adm["afk"])] Stealth: [english_list(adm["stealth"])] Skipped: [english_list(adm["noflags"])]). "
status += "Players: [GLOB.clients.len] (Active: [get_active_player_count(0,1,0)]). Mode: [SSticker.mode ? SSticker.mode.name : "Not started"]."
return status
/datum/server_tools_command/irccheck
name = "check"
help_text = "Gets the playercount, gamemode, and address of the server"
admin_only = TRUE
var/static/last_irc_check = 0
/datum/server_tools_command/irccheck/Run(sender, params)
var/rtod = REALTIMEOFDAY
if(rtod - last_irc_check < IRC_STATUS_THROTTLE)
return
last_irc_check = rtod
var/server = CONFIG_GET(string/server)
return "[GLOB.round_id ? "Round #[GLOB.round_id]: " : ""][GLOB.clients.len] players on [SSmapping.config.map_name], Mode: [GLOB.master_mode]; Round [SSticker.HasRoundStarted() ? (SSticker.IsRoundInProgress() ? "Active" : "Finishing") : "Starting"] -- [server ? server : "[world.internet_address]:[world.port]"]"
/datum/server_tools_command/ahelp
name = "ahelp"
help_text = "<ckey> <message|ticket <close|resolve|icissue|reject|reopen <ticket #>|list>>"
required_parameters = 2
admin_only = TRUE
/datum/server_tools_command/ahelp/Run(sender, params)
var/list/all_params = splittext(params, " ")
var/target = all_params[1]
all_params.Cut(1, 2)
return IrcPm(target, all_params.Join(" "), sender)
/datum/server_tools_command/namecheck
name = "namecheck"
help_text = "Returns info on the specified target"
required_parameters = 1
admin_only = TRUE
/datum/server_tools_command/namecheck/Run(sender, params)
log_admin("IRC Name Check: [sender] on [params]")
message_admins("IRC name checking on [params] from [sender]")
return keywords_lookup(params, 1)
/datum/server_tools_command/adminwho
name = "adminwho"
help_text = "Lists administrators currently on the server"
admin_only = TRUE
/datum/server_tools_command/adminwho/Run(sender, params)
return ircadminwho()
+2 -4
View File
@@ -587,10 +587,8 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
/proc/send2irc(msg,msg2)
if(world.RunningService())
world.ExportService("[SERVICE_REQUEST_IRC_ADMIN_CHANNEL_MESSAGE] [msg] | [msg2]")
else if(CONFIG_GET(flag/useircbot))
shell("python nudge.py [msg] [msg2]")
if(SERVER_TOOLS_PRESENT)
SERVER_TOOLS_RELAY_BROADCAST("[msg] | [msg2]")
/proc/send2otherserver(source,msg,type = "Ahelp")
var/comms_key = CONFIG_GET(string/comms_key)
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -46,7 +46,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/preferred_map = null
var/uses_glasses_colour = 0
var/screenshake = 100
var/damagescreenshake = 2
@@ -571,7 +571,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "[features["flavor_text"]]"
else
dat += "[TextPreview(features["flavor_text"])]...<BR>"
if(config.mutant_races)//really don't need this check, but fuck un-tabbing all those lines
if(CONFIG_GET(flag/join_with_mutant_race))//really don't need this check, but fuck un-tabbing all those lines
dat += "<h2>Body</h2>"
dat += "<b>Gender:</b> <a href='?_src_=prefs;preference=gender'>[gender == MALE ? "Male" : "Female"]</a><BR>"
dat += "<b>Species:</b><a href='?_src_=prefs;preference=species;task=input'>[pref_species.id]</a><BR>"
+1 -1
View File
@@ -460,7 +460,7 @@
makeTrail(newloc, T, old_direction)
/mob/living/movement_delay(ignorewalk = 0)
. = ..()
. = 0
if(isopenturf(loc) && !is_flying())
var/turf/open/T = loc
. += T.slowdown
@@ -207,16 +207,11 @@
"Medical" = /obj/item/robot_module/medical, \
"Miner" = /obj/item/robot_module/miner, \
"Janitor" = /obj/item/robot_module/janitor, \
<<<<<<< HEAD
"Service" = /obj/item/robot_module/butler, \
"MediHound" = /obj/item/robot_module/medihound, \
"Security K9" = /obj/item/robot_module/k9, \
"Scrub Puppy" = /obj/item/robot_module/scrubpup)
if(!config.forbid_peaceborg)
=======
"Service" = /obj/item/robot_module/butler)
if(!CONFIG_GET(flag/disable_peaceborg))
>>>>>>> 4178c20... Configuration datum refactor (#30763)
modulelist["Peacekeeper"] = /obj/item/robot_module/peacekeeper
if(!CONFIG_GET(flag/disable_secborg))
modulelist["Security"] = /obj/item/robot_module/security
@@ -1,4 +1,3 @@
<<<<<<< HEAD
/mob/living/silicon/robot/Process_Spacemove(movement_dir = 0)
if(ionpulse())
return 1
@@ -6,8 +5,10 @@
/mob/living/silicon/robot/movement_delay()
. = ..()
. += speed
. += config.robot_delay
var/static/config_robot_delay
if(isnull(config_robot_delay))
config_robot_delay = CONFIG_GET(number/robot_delay)
. += speed + config_robot_delay
/mob/living/silicon/robot/mob_negates_gravity()
return magpulse
@@ -23,31 +24,3 @@
. = ..()
if(riding_datum)
riding_datum.on_vehicle_move()
=======
/mob/living/silicon/robot/Process_Spacemove(movement_dir = 0)
if(ionpulse())
return 1
return ..()
/mob/living/silicon/robot/movement_delay()
. = ..()
var/static/config_robot_delay
if(isnull(config_robot_delay))
config_robot_delay = CONFIG_GET(number/robot_delay)
. += speed + config_robot_delay
/mob/living/silicon/robot/mob_negates_gravity()
return magpulse
/mob/living/silicon/robot/mob_has_gravity()
return ..() || mob_negates_gravity()
/mob/living/silicon/robot/experience_pressure_difference(pressure_difference, direction)
if(!magpulse)
return ..()
/mob/living/silicon/robot/Moved()
. = ..()
if(riding_datum)
riding_datum.on_vehicle_move()
>>>>>>> 4178c20... Configuration datum refactor (#30763)
+1 -6
View File
@@ -1,7 +1,2 @@
<<<<<<< HEAD
/mob/living/silicon/robot/IsVocal()
return !config.silent_borg
=======
/mob/living/silicon/robot/IsVocal()
return !CONFIG_GET(flag/silent_borg)
>>>>>>> 4178c20... Configuration datum refactor (#30763)
return !CONFIG_GET(flag/silent_borg)
@@ -424,6 +424,41 @@
setDir(i)
sleep(1)
/mob/living/simple_animal/pet/dog/corgi/Ian/narsie_act()
playsound(src, 'sound/magic/demon_dies.ogg', 75, TRUE)
var/mob/living/simple_animal/pet/dog/corgi/narsie/N = new(loc)
N.setDir(dir)
gib()
/mob/living/simple_animal/pet/dog/corgi/narsie
name = "Nars-Ian"
desc = "Ia! Ia!"
icon_state = "narsian"
icon_living = "narsian"
icon_dead = "narsian_dead"
faction = list("dog", "cult")
gold_core_spawnable = FALSE
nofur = TRUE
/mob/living/simple_animal/pet/dog/corgi/narsie/Life()
..()
for(var/mob/living/simple_animal/pet/P in range(1, src))
if(P != src && prob(5))
visible_message("<span class='warning'>[src] devours [P]!</span>", \
"<span class='cult big bold'>DELICIOUS SOULS</span>")
playsound(src, 'sound/magic/demon_attack1.ogg', 75, TRUE)
narsie_act()
P.gib()
/mob/living/simple_animal/pet/dog/corgi/narsie/update_corgi_fluff()
..()
speak = list("Tari'karat-pasnar!", "IA! IA!", "BRRUUURGHGHRHR")
speak_emote = list("growls", "barks ominously")
emote_hear = list("barks echoingly!", "woofs hauntingly!", "yaps in an eldritch manner.", "mutters something unspeakable.")
emote_see = list("communes with the unnameable.", "ponders devouring some souls.", "shakes.")
/mob/living/simple_animal/pet/dog/corgi/narsie/narsie_act()
adjustBruteLoss(-maxHealth)
/mob/living/simple_animal/pet/dog/corgi/regenerate_icons()
+43 -17
View File
@@ -33,6 +33,7 @@
icon_state = "parrot_fly"
icon_living = "parrot_fly"
icon_dead = "parrot_dead"
var/icon_sit = "parrot_sit"
density = FALSE
health = 80
maxHealth = 80
@@ -284,7 +285,7 @@
return
if(!stat && M.a_intent == INTENT_HARM)
icon_state = "parrot_fly" //It is going to be flying regardless of whether it flees or attacks
icon_state = icon_living //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
@@ -320,7 +321,7 @@
if(M.melee_damage_upper > 0 && !stat)
parrot_interest = M
parrot_state = PARROT_SWOOP | PARROT_ATTACK //Attack other animals regardless
icon_state = "parrot_fly"
icon_state = icon_living
//Mobs with objects
/mob/living/simple_animal/parrot/attackby(obj/item/O, mob/living/user, params)
@@ -335,7 +336,7 @@
parrot_state |= PARROT_ATTACK
else
parrot_state |= PARROT_FLEE
icon_state = "parrot_fly"
icon_state = icon_living
drop_held_item(0)
else if(istype(O, /obj/item/reagent_containers/food/snacks/cracker)) //Poly wants a cracker.
qdel(O)
@@ -358,7 +359,7 @@
parrot_interest = null
parrot_state = PARROT_WANDER | PARROT_FLEE //Been shot and survived! RUN LIKE HELL!
//parrot_been_shot += 5
icon_state = "parrot_fly"
icon_state = icon_living
drop_held_item(0)
return
@@ -371,7 +372,7 @@
//Sprite update for when a parrot gets pulled
if(pulledby && stat == CONSCIOUS)
icon_state = "parrot_fly"
icon_state = icon_living
if(!client)
parrot_state = PARROT_WANDER
return
@@ -400,11 +401,11 @@
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"
icon_state = icon_living
return
else
parrot_state = PARROT_WANDER
icon_state = "parrot_fly"
icon_state = icon_living
return
if(--parrot_sleep_dur) //Zzz
@@ -445,7 +446,7 @@
if(parrot_interest)
emote("me", 1, "looks in [parrot_interest]'s direction and takes flight.")
parrot_state = PARROT_SWOOP | PARROT_STEAL
icon_state = "parrot_fly"
icon_state = icon_living
return
//-----WANDERING - This is basically a 'I dont know what to do yet' state
@@ -530,7 +531,7 @@
src.loc = parrot_perch.loc
drop_held_item()
parrot_state = PARROT_PERCH
icon_state = "parrot_sit"
icon_state = icon_sit
return
walk_to(src, parrot_perch, 1, parrot_speed)
@@ -603,8 +604,8 @@
*/
/mob/living/simple_animal/parrot/movement_delay()
if(client && stat == CONSCIOUS && parrot_state != "parrot_fly")
icon_state = "parrot_fly"
if(client && stat == CONSCIOUS && parrot_state != icon_living)
icon_state = icon_living
//Because the most appropriate place to set icon_state is movement_delay(), clearly
return ..()
@@ -797,12 +798,12 @@
if(stat || !client)
return
if(icon_state == "parrot_fly")
if(icon_state == icon_living)
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"
icon_state = icon_sit
return
to_chat(src, "<span class='warning'>There is no perch nearby to sit on!</span>")
return
@@ -816,7 +817,7 @@
if(stat || !client)
return
if(icon_state == "parrot_fly")
if(icon_state == icon_living)
for(var/mob/living/carbon/human/H in view(src,1))
if(H.has_buckled_mobs() && H.buckled_mobs.len >= H.max_buckled_mobs) //Already has a parrot, or is being eaten by a slime
continue
@@ -824,7 +825,7 @@
return
to_chat(src, "<span class='warning'>There is nobody nearby that you can sit on!</span>")
else
icon_state = "parrot_fly"
icon_state = icon_living
parrot_state = PARROT_WANDER
if(buckled)
to_chat(src, "<span class='notice'>You are no longer sitting on [buckled]'s shoulder.</span>")
@@ -842,7 +843,7 @@
H.buckle_mob(src, force=1)
pixel_y = 9
pixel_x = pick(-8,8) //pick left or right shoulder
icon_state = "parrot_sit"
icon_state = icon_sit
parrot_state = PARROT_PERCH
to_chat(src, "<span class='notice'>You sit on [H]'s shoulder.</span>")
@@ -961,6 +962,12 @@
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
/mob/living/simple_animal/parrot/Poly/ratvar_act()
playsound(src, 'sound/magic/clockwork/fellowship_armory.ogg', 75, TRUE)
var/mob/living/simple_animal/parrot/clock_hawk/H = new(loc)
H.setDir(dir)
dust()
/mob/living/simple_animal/parrot/Poly/ghost
name = "The Ghost of Poly"
desc = "Doomed to squawk the earth."
@@ -996,4 +1003,23 @@
loc = H
H.ContractDisease(P)
parrot_interest = null
H.visible_message("<span class='danger'>[src] dive bombs into [H]'s chest and vanishes!</span>", "<span class='userdanger'>[src] dive bombs into your chest, vanishing! This can't be good!</span>")
H.visible_message("<span class='danger'>[src] dive bombs into [H]'s chest and vanishes!</span>", "<span class='userdanger'>[src] dive bombs into your chest, vanishing! This can't be good!</span>")
/mob/living/simple_animal/parrot/clock_hawk
name = "clock hawk"
desc = "Cbyl jnaan penpxre! Fdhnnnjx!"
icon_state = "clock_hawk_fly"
icon_living = "clock_hawk_fly"
icon_sit = "clock_hawk_sit"
speak = list("Penpxre!", "Ratvar vf n qhzo anzr naljnl!")
speak_emote = list("squawks rustily", "says crassly", "yells brassly")
emote_hear = list("squawks rustily.", "bawks metallically!")
emote_see = list("flutters its metal wings.")
faction = list("ratvar")
gold_core_spawnable = FALSE
del_on_death = TRUE
death_sound = 'sound/magic/clockwork/anima_fragment_death.ogg'
/mob/living/simple_animal/parrot/clock_hawk/ratvar_act()
return
+66 -66
View File
@@ -1,66 +1,66 @@
/mob/living/simple_animal/shade
name = "Shade"
real_name = "Shade"
desc = "A bound spirit"
gender = PLURAL
icon = 'icons/mob/mob.dmi'
icon_state = "shade"
icon_living = "shade"
maxHealth = 50
health = 50
healable = 0
speak_emote = list("hisses")
emote_hear = list("wails.","screeches.")
response_help = "puts their hand through"
response_disarm = "flails at"
response_harm = "punches"
speak_chance = 1
melee_damage_lower = 5
melee_damage_upper = 15
attacktext = "metaphysically strikes"
minbodytemp = 0
maxbodytemp = INFINITY
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
movement_type = FLYING
loot = list(/obj/item/ectoplasm)
del_on_death = TRUE
initial_language_holder = /datum/language_holder/construct
/mob/living/simple_animal/shade/death()
deathmessage = "lets out a contented sigh as [p_their()] form unwinds."
..()
/mob/living/simple_animal/shade/canSuicide()
if(istype(loc, /obj/item/device/soulstone)) //do not suicide inside the soulstone
return 0
return ..()
/mob/living/simple_animal/shade/Process_Spacemove(movement_dir = 0)
return TRUE //this doesn't make much sense; you'd thing TRUE would mean it'd process spacemove but it means it doesn't
/mob/living/simple_animal/shade/attack_animal(mob/living/simple_animal/M)
if(isconstruct(M))
var/mob/living/simple_animal/hostile/construct/C = M
if(!C.can_repair_constructs)
return
if(health < maxHealth)
adjustHealth(-25)
Beam(M,icon_state="sendbeam",time=4)
M.visible_message("<span class='danger'>[M] heals \the <b>[src]</b>.</span>", \
"<span class='cult'>You heal <b>[src]</b>, leaving <b>[src]</b> at <b>[health]/[maxHealth]</b> health.</span>")
else
to_chat(M, "<span class='cult'>You cannot heal <b>[src]</b>, as [p_they()] [p_are()] unharmed!</span>")
else if(src != M)
return ..()
/mob/living/simple_animal/shade/attackby(obj/item/O, mob/user, params) //Marker -Agouri
if(istype(O, /obj/item/device/soulstone))
var/obj/item/device/soulstone/SS = O
SS.transfer_soul("SHADE", src, user)
else
..()
/mob/living/simple_animal/shade
name = "Shade"
real_name = "Shade"
desc = "A bound spirit"
gender = PLURAL
icon = 'icons/mob/mob.dmi'
icon_state = "shade"
icon_living = "shade"
maxHealth = 50
health = 50
healable = 0
speak_emote = list("hisses")
emote_hear = list("wails.","screeches.")
response_help = "puts their hand through"
response_disarm = "flails at"
response_harm = "punches"
speak_chance = 1
melee_damage_lower = 5
melee_damage_upper = 15
attacktext = "metaphysically strikes"
minbodytemp = 0
maxbodytemp = INFINITY
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
movement_type = FLYING
loot = list(/obj/item/ectoplasm)
del_on_death = TRUE
initial_language_holder = /datum/language_holder/construct
/mob/living/simple_animal/shade/death()
deathmessage = "lets out a contented sigh as [p_their()] form unwinds."
..()
/mob/living/simple_animal/shade/canSuicide()
if(istype(loc, /obj/item/device/soulstone)) //do not suicide inside the soulstone
return 0
return ..()
/mob/living/simple_animal/shade/Process_Spacemove(movement_dir = 0)
return TRUE //this doesn't make much sense; you'd thing TRUE would mean it'd process spacemove but it means it doesn't
/mob/living/simple_animal/shade/attack_animal(mob/living/simple_animal/M)
if(isconstruct(M))
var/mob/living/simple_animal/hostile/construct/C = M
if(!C.can_repair_constructs)
return
if(health < maxHealth)
adjustHealth(-25)
Beam(M,icon_state="sendbeam",time=4)
M.visible_message("<span class='danger'>[M] heals \the <b>[src]</b>.</span>", \
"<span class='cult'>You heal <b>[src]</b>, leaving <b>[src]</b> at <b>[health]/[maxHealth]</b> health.</span>")
else
to_chat(M, "<span class='cult'>You cannot heal <b>[src]</b>, as [p_they()] [p_are()] unharmed!</span>")
else if(src != M)
return ..()
/mob/living/simple_animal/shade/attackby(obj/item/O, mob/user, params) //Marker -Agouri
if(istype(O, /obj/item/device/soulstone))
var/obj/item/device/soulstone/SS = O
SS.transfer_soul("SHADE", src, user)
else
. = ..()
@@ -271,14 +271,11 @@
/mob/living/simple_animal/movement_delay()
. = ..()
. = speed
var/static/config_animal_delay
if(isnull(config_animal_delay))
config_animal_delay = CONFIG_GET(number/animal_delay)
. += config_animal_delay
return ..() + speed + config_animal_delay
/mob/living/simple_animal/Stat()
..()
+1 -1
View File
@@ -162,7 +162,7 @@
for(var/mob/M in get_hearers_in_view(range, src))
M.show_message( message, 2, deaf_message, 1)
/mob/proc/movement_delay()
/mob/proc/movement_delay() //update /living/movement_delay() if you change this
return 0
/mob/proc/Life()
@@ -2,6 +2,7 @@
slot_flags = SLOT_BACK
lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
flags_2 = NO_MAT_REDEMPTION_2
/obj/item/gun/magic/staff/change
name = "staff of change"
@@ -1,161 +1,164 @@
/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/syringe.dmi'
item_state = "hypo"
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
icon_state = "hypo"
amount_per_transfer_from_this = 5
volume = 30
possible_transfer_amounts = list()
resistance_flags = ACID_PROOF
container_type = OPENCONTAINER_1
slot_flags = SLOT_BELT
var/ignore_flags = 0
var/infinite = FALSE
/obj/item/reagent_containers/hypospray/attack_paw(mob/user)
return attack_hand(user)
/obj/item/reagent_containers/hypospray/attack(mob/living/M, mob/user)
if(!reagents.total_volume)
to_chat(user, "<span class='warning'>[src] is empty!</span>")
return
if(!iscarbon(M))
return
if(reagents.total_volume && (ignore_flags || M.can_inject(user, 1))) // Ignore flag should be checked first or there will be an error message.
to_chat(M, "<span class='warning'>You feel a tiny prick!</span>")
to_chat(user, "<span class='notice'>You inject [M] with [src].</span>")
var/fraction = min(amount_per_transfer_from_this/reagents.total_volume, 1)
reagents.reaction(M, INJECT, fraction)
if(M.reagents)
var/list/injected = list()
for(var/datum/reagent/R in reagents.reagent_list)
injected += R.name
var/trans = 0
if(!infinite)
trans = reagents.trans_to(M, amount_per_transfer_from_this)
else
trans = reagents.copy_to(M, amount_per_transfer_from_this)
to_chat(user, "<span class='notice'>[trans] unit\s injected. [reagents.total_volume] unit\s remaining in [src].</span>")
var/contained = english_list(injected)
add_logs(user, M, "injected", src, "([contained])")
/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 = 10
icon_state = "combat_hypo"
volume = 90
ignore_flags = 1 // So they can heal their comrades.
list_reagents = list("epinephrine" = 30, "omnizine" = 30, "leporazine" = 15, "atropine" = 15)
/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" = 80, "synaptizine" = 20)
/obj/item/reagent_containers/hypospray/magillitis
name = "experimental autoinjector"
desc = "A modified air-needle autoinjector with a small single-use reservoir. It contains an experimental serum."
icon_state = "combat_hypo"
volume = 5
container_type = NONE
list_reagents = list("magillitis" = 5)
//MediPens
/obj/item/reagent_containers/hypospray/medipen
name = "epinephrine medipen"
desc = "A rapid and safe way to stabilize patients in critical condition for personnel without advanced medical knowledge."
icon_state = "medipen"
item_state = "medipen"
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
amount_per_transfer_from_this = 10
volume = 10
ignore_flags = 1 //so you can medipen through hardsuits
container_type = DRAWABLE_1
flags_1 = null
list_reagents = list("epinephrine" = 10)
/obj/item/reagent_containers/hypospray/medipen/attack(mob/M, mob/user)
if(!reagents.total_volume)
to_chat(user, "<span class='warning'>[src] is empty!</span>")
return
..()
if(!iscyborg(user))
reagents.maximum_volume = 0 //Makes them useless afterwards
container_type = NONE
update_icon()
spawn(80)
if(iscyborg(user) && !reagents.total_volume)
var/mob/living/silicon/robot/R = user
if(R.cell.use(100))
reagents.add_reagent_list(list_reagents)
update_icon()
return
/obj/item/reagent_containers/hypospray/medipen/update_icon()
if(reagents.total_volume > 0)
icon_state = initial(icon_state)
else
icon_state = "[initial(icon_state)]0"
/obj/item/reagent_containers/hypospray/medipen/examine()
..()
if(reagents && reagents.reagent_list.len)
to_chat(usr, "<span class='notice'>It is currently loaded.</span>")
else
to_chat(usr, "<span class='notice'>It is spent.</span>")
/obj/item/reagent_containers/hypospray/medipen/stimpack //goliath kiting
name = "stimpack medipen"
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("ephedrine" = 10, "coffee" = 10)
/obj/item/reagent_containers/hypospray/medipen/stimpack/traitor
desc = "A modified stimulants autoinjector for use in combat situations. Has a mild healing effect."
list_reagents = list("stimulants" = 10, "omnizine" = 10)
/obj/item/reagent_containers/hypospray/medipen/morphine
name = "morphine medipen"
desc = "A rapid way to get you out of a tight situation and fast! You'll feel rather drowsy, though."
list_reagents = list("morphine" = 10)
/obj/item/reagent_containers/hypospray/medipen/tuberculosiscure
name = "BVAK autoinjector"
desc = "Bio Virus Antidote Kit autoinjector. Has a two use system for yourself, and someone else. Inject when infected."
icon_state = "stimpen"
volume = 60
amount_per_transfer_from_this = 30
list_reagents = list("atropine" = 10, "epinephrine" = 10, "salbutamol" = 20, "spaceacillin" = 20)
/obj/item/reagent_containers/hypospray/medipen/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 = 57
amount_per_transfer_from_this = 57
list_reagents = list("salbutamol" = 10, "leporazine" = 15, "tricordrazine" = 15, "epinephrine" = 10, "miningnanites" = 2, "omnizine" = 5)
/obj/item/reagent_containers/hypospray/medipen/species_mutator
name = "species mutator medipen"
desc = "Embark on a whirlwind tour of racial insensitivity by \
literally appropriating other races."
volume = 1
amount_per_transfer_from_this = 1
list_reagents = list("unstablemutationtoxin" = 1)
/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/syringe.dmi'
item_state = "hypo"
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
icon_state = "hypo"
amount_per_transfer_from_this = 5
volume = 30
possible_transfer_amounts = list()
resistance_flags = ACID_PROOF
container_type = OPENCONTAINER_1
slot_flags = SLOT_BELT
var/ignore_flags = 0
var/infinite = FALSE
/obj/item/reagent_containers/hypospray/attack_paw(mob/user)
return attack_hand(user)
/obj/item/reagent_containers/hypospray/attack(mob/living/M, mob/user)
if(!reagents.total_volume)
to_chat(user, "<span class='warning'>[src] is empty!</span>")
return
if(!iscarbon(M))
return
//Always log attemped injects for admins
var/list/injected = list()
for(var/datum/reagent/R in reagents.reagent_list)
injected += R.name
var/contained = english_list(injected)
add_logs(user, M, "attempted to inject", src, "([contained])")
if(reagents.total_volume && (ignore_flags || M.can_inject(user, 1))) // Ignore flag should be checked first or there will be an error message.
to_chat(M, "<span class='warning'>You feel a tiny prick!</span>")
to_chat(user, "<span class='notice'>You inject [M] with [src].</span>")
var/fraction = min(amount_per_transfer_from_this/reagents.total_volume, 1)
reagents.reaction(M, INJECT, fraction)
if(M.reagents)
var/trans = 0
if(!infinite)
trans = reagents.trans_to(M, amount_per_transfer_from_this)
else
trans = reagents.copy_to(M, amount_per_transfer_from_this)
to_chat(user, "<span class='notice'>[trans] unit\s injected. [reagents.total_volume] unit\s remaining in [src].</span>")
add_logs(user, M, "injected", src, "([contained])")
/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 = 10
icon_state = "combat_hypo"
volume = 90
ignore_flags = 1 // So they can heal their comrades.
list_reagents = list("epinephrine" = 30, "omnizine" = 30, "leporazine" = 15, "atropine" = 15)
/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" = 80, "synaptizine" = 20)
/obj/item/reagent_containers/hypospray/magillitis
name = "experimental autoinjector"
desc = "A modified air-needle autoinjector with a small single-use reservoir. It contains an experimental serum."
icon_state = "combat_hypo"
volume = 5
container_type = NONE
list_reagents = list("magillitis" = 5)
//MediPens
/obj/item/reagent_containers/hypospray/medipen
name = "epinephrine medipen"
desc = "A rapid and safe way to stabilize patients in critical condition for personnel without advanced medical knowledge."
icon_state = "medipen"
item_state = "medipen"
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
amount_per_transfer_from_this = 10
volume = 10
ignore_flags = 1 //so you can medipen through hardsuits
container_type = DRAWABLE_1
flags_1 = null
list_reagents = list("epinephrine" = 10)
/obj/item/reagent_containers/hypospray/medipen/attack(mob/M, mob/user)
if(!reagents.total_volume)
to_chat(user, "<span class='warning'>[src] is empty!</span>")
return
..()
if(!iscyborg(user))
reagents.maximum_volume = 0 //Makes them useless afterwards
container_type = NONE
update_icon()
spawn(80)
if(iscyborg(user) && !reagents.total_volume)
var/mob/living/silicon/robot/R = user
if(R.cell.use(100))
reagents.add_reagent_list(list_reagents)
update_icon()
return
/obj/item/reagent_containers/hypospray/medipen/update_icon()
if(reagents.total_volume > 0)
icon_state = initial(icon_state)
else
icon_state = "[initial(icon_state)]0"
/obj/item/reagent_containers/hypospray/medipen/examine()
..()
if(reagents && reagents.reagent_list.len)
to_chat(usr, "<span class='notice'>It is currently loaded.</span>")
else
to_chat(usr, "<span class='notice'>It is spent.</span>")
/obj/item/reagent_containers/hypospray/medipen/stimpack //goliath kiting
name = "stimpack medipen"
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("ephedrine" = 10, "coffee" = 10)
/obj/item/reagent_containers/hypospray/medipen/stimpack/traitor
desc = "A modified stimulants autoinjector for use in combat situations. Has a mild healing effect."
list_reagents = list("stimulants" = 10, "omnizine" = 10)
/obj/item/reagent_containers/hypospray/medipen/morphine
name = "morphine medipen"
desc = "A rapid way to get you out of a tight situation and fast! You'll feel rather drowsy, though."
list_reagents = list("morphine" = 10)
/obj/item/reagent_containers/hypospray/medipen/tuberculosiscure
name = "BVAK autoinjector"
desc = "Bio Virus Antidote Kit autoinjector. Has a two use system for yourself, and someone else. Inject when infected."
icon_state = "stimpen"
volume = 60
amount_per_transfer_from_this = 30
list_reagents = list("atropine" = 10, "epinephrine" = 10, "salbutamol" = 20, "spaceacillin" = 20)
/obj/item/reagent_containers/hypospray/medipen/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 = 57
amount_per_transfer_from_this = 57
list_reagents = list("salbutamol" = 10, "leporazine" = 15, "tricordrazine" = 15, "epinephrine" = 10, "miningnanites" = 2, "omnizine" = 5)
/obj/item/reagent_containers/hypospray/medipen/species_mutator
name = "species mutator medipen"
desc = "Embark on a whirlwind tour of racial insensitivity by \
literally appropriating other races."
volume = 1
amount_per_transfer_from_this = 1
list_reagents = list("unstablemutationtoxin" = 1)
@@ -110,6 +110,13 @@
update_icon()
if(SYRINGE_INJECT)
//Always log attemped injections for admins
var/list/rinject = list()
for(var/datum/reagent/R in reagents.reagent_list)
rinject += R.name
var/contained = english_list(rinject)
add_logs(user, L, "attemped to inject", src, addition="which had [contained]")
if(!reagents.total_volume)
to_chat(user, "<span class='notice'>[src] is empty.</span>")
return
@@ -137,11 +144,6 @@
L.visible_message("<span class='danger'>[user] injects [L] with the syringe!", \
"<span class='userdanger'>[user] injects [L] with the syringe!</span>")
var/list/rinject = list()
for(var/datum/reagent/R in reagents.reagent_list)
rinject += R.name
var/contained = english_list(rinject)
if(L != user)
add_logs(user, L, "injected", src, addition="which had [contained]")
else
+76
View File
@@ -0,0 +1,76 @@
/datum/server_tools_command
var/name = "" //the string to trigger this command on a chat bot. e.g. TGS3_BOT: do_this_command
var/help_text = "" //help text for this command
var/required_parameters = 0 //number of parameters required for this command
var/admin_only = FALSE //set to TRUE if this command should only be usable by registered chat admins
//override to implement command
//sender is the display name of who sent the command
//params is the trimmed string following the command name
/datum/server_tools_command/proc/Run(sender, params)
CRASH("[type] has no implementation for Run()")
/world/proc/ListServiceCustomCommands(warnings_only)
if(!warnings_only)
. = list()
var/list/command_name_types = list()
var/list/warned_command_names = warnings_only ? list() : null
for(var/I in typesof(/datum/server_tools_command) - /datum/server_tools_command)
var/datum/server_tools_command/stc = I
var/command_name = initial(stc.name)
var/static/list/warned_server_tools_names = list()
if(!command_name || findtext(command_name, " ") || findtext(command_name, "'") || findtext(command_name, "\""))
if(warnings_only && !warned_command_names[command_name])
SERVER_TOOLS_LOG("WARNING: Custom command [command_name] can't be used as it is empty or contains illegal characters!")
warned_command_names[command_name] = TRUE
continue
if(command_name_types[command_name])
if(warnings_only)
SERVER_TOOLS_LOG("WARNING: Custom commands [command_name_types[command_name]] and [stc] have the same name, only [command_name_types[command_name]] will be available!")
continue
command_name_types[stc] = command_name
if(!warnings_only)
.[command_name] = list(SERVICE_JSON_PARAM_HELPTEXT = initial(stc.help_text), SERVICE_JSON_PARAM_ADMINONLY = initial(stc.admin_only), SERVICE_JSON_PARAM_REQUIREDPARAMETERS = initial(stc.required_parameters))
/world/proc/HandleServiceCustomCommand(command, sender, params)
var/static/list/cached_custom_server_tools_commands
if(!cached_custom_server_tools_commands)
cached_custom_server_tools_commands = list()
for(var/I in typesof(/datum/server_tools_command) - /datum/server_tools_command)
var/datum/server_tools_command/stc = I
cached_custom_server_tools_commands[lowertext(initial(stc.name))] = stc
var/command_type = cached_custom_server_tools_commands[command]
if(!command_type)
return FALSE
var/datum/server_tools_command/stc = new command_type
return stc.Run(sender, params) || TRUE
/*
The MIT License
Copyright (c) 2011 Dominic Tarr
Permission is hereby granted, free of charge,
to any person obtaining a copy of this software and
associated documentation files (the "Software"), to
deal in the Software without restriction, including
without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom
the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
+125
View File
@@ -0,0 +1,125 @@
SERVER_TOOLS_DEFINE_AND_SET_GLOBAL(reboot_mode, REBOOT_MODE_NORMAL)
SERVER_TOOLS_DEFINE_AND_SET_GLOBAL(server_tools_api_compatible, FALSE)
/proc/GetTestMerges()
if(RunningService() && fexists(SERVICE_PR_TEST_JSON))
. = json_decode(file2text(SERVICE_PR_TEST_JSON))
if(.)
return
return list()
/world/proc/ServiceInit()
if(!RunningService(TRUE))
return
ListServiceCustomCommands(TRUE)
ExportService("[SERVICE_REQUEST_API_VERSION] [SERVER_TOOLS_API_VERSION]", TRUE)
/proc/RunningService(skip_compat_check = FALSE)
return (skip_compat_check || SERVER_TOOLS_READ_GLOBAL(server_tools_api_compatible)) && world.params[SERVICE_WORLD_PARAM] != null
/proc/ServiceVersion()
if(RunningService(TRUE))
return world.params[SERVICE_VERSION_PARAM]
/proc/ServiceAPIVersion()
return SERVICE_API_VERSION_STRING
/world/proc/ExportService(command, skip_compat_check = FALSE)
. = FALSE
if(!RunningService(skip_compat_check))
return
if(skip_compat_check && !fexists(SERVICE_INTERFACE_DLL))
CRASH("Service parameter present but no interface DLL detected. This is symptomatic of running a service less than version 3.1! Please upgrade.")
call(SERVICE_INTERFACE_DLL, SERVICE_INTERFACE_FUNCTION)(command) //trust no retval
return TRUE
/world/proc/ChatBroadcast(message)
ExportService("[SERVICE_REQUEST_IRC_BROADCAST] [message]")
/world/proc/AdminBroadcast(message)
ExportService("[SERVICE_REQUEST_IRC_ADMIN_CHANNEL_MESSAGE] [message]")
/world/proc/ServiceEndProcess()
SERVER_TOOLS_LOG("Sending shutdown request!");
sleep(world.tick_lag) //flush the buffers
ExportService(SERVICE_REQUEST_KILL_PROCESS)
//called at the exact moment the world is supposed to reboot
/world/proc/ServiceReboot()
switch(SERVER_TOOLS_READ_GLOBAL(reboot_mode))
if(REBOOT_MODE_HARD)
SERVER_TOOLS_WORLD_ANNOUNCE("Hard reboot triggered, you will automatically reconnect...")
ServiceEndProcess()
if(REBOOT_MODE_SHUTDOWN)
SERVER_TOOLS_WORLD_ANNOUNCE("The server is shutting down...")
ServiceEndProcess()
else
ExportService(SERVICE_REQUEST_WORLD_REBOOT) //just let em know
/world/proc/ServiceCommand(list/params)
var/their_sCK = params[SERVICE_CMD_PARAM_KEY]
if(!their_sCK || !RunningService(TRUE))
return FALSE //continue world/Topic
var/sCK = world.params[SERVICE_WORLD_PARAM]
if(their_sCK != sCK)
return "Invalid comms key!";
var/command = params[SERVICE_CMD_PARAM_COMMAND]
if(!command)
return "No command!"
switch(command)
if(SERVICE_CMD_API_COMPATIBLE)
SERVER_TOOLS_WRITE_GLOBAL(server_tools_api_compatible, TRUE)
return "SUCCESS"
if(SERVICE_CMD_HARD_REBOOT)
if(SERVER_TOOLS_READ_GLOBAL(reboot_mode) != REBOOT_MODE_HARD)
SERVER_TOOLS_WRITE_GLOBAL(reboot_mode, REBOOT_MODE_HARD)
SERVER_TOOLS_LOG("Hard reboot requested by service")
SERVER_TOOLS_NOTIFY_ADMINS("The world will hard reboot at the end of the game. Requested by service.")
if(SERVICE_CMD_GRACEFUL_SHUTDOWN)
if(SERVER_TOOLS_READ_GLOBAL(reboot_mode) != REBOOT_MODE_SHUTDOWN)
SERVER_TOOLS_WRITE_GLOBAL(reboot_mode, REBOOT_MODE_SHUTDOWN)
SERVER_TOOLS_LOG("Shutdown requested by service")
message_admins("The world will shutdown at the end of the game. Requested by service.")
if(SERVICE_CMD_WORLD_ANNOUNCE)
var/msg = params["message"]
if(!istext(msg) || !msg)
return "No message set!"
SERVER_TOOLS_WORLD_ANNOUNCE(msg)
return "SUCCESS"
if(SERVICE_CMD_LIST_CUSTOM)
return json_encode(ListServiceCustomCommands(FALSE))
else
var/custom_command_result = HandleServiceCustomCommand(lowertext(command), params[SERVICE_CMD_PARAM_SENDER], params[SERVICE_CMD_PARAM_CUSTOM])
if(custom_command_result)
return istext(custom_command_result) ? custom_command_result : "SUCCESS"
return "Unknown command: [command]"
/*
The MIT License
Copyright (c) 2011 Dominic Tarr
Permission is hereby granted, free of charge,
to any person obtaining a copy of this software and
associated documentation files (the "Software"), to
deal in the Software without restriction, including
without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom
the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
@@ -0,0 +1,5 @@
author: "Xhuis"
delete-after: True
changes:
- rscadd: "Ian has recently communed with unspeakable horrors and may now be warped by their power if Nar-Sie passes near them."
- rscadd: "In order to fight back against their ancient foe Ian, Poly has struck a bargain with Ratvar and will be transformed into a machine by their presence."
@@ -0,0 +1,4 @@
author: "CitadelStationBot"
delete-after: True
changes:
- rscadd: "The syndicate have recently begun sending agents to extract vital research information from Nanotrasen."
Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

+12 -11
View File
@@ -28,8 +28,8 @@
#include "code\__DEFINES\atmospherics.dm"
#include "code\__DEFINES\atom_hud.dm"
#include "code\__DEFINES\callbacks.dm"
#include "code\__DEFINES\citadel_defines.dm"
#include "code\__DEFINES\cinematics.dm"
#include "code\__DEFINES\citadel_defines.dm"
#include "code\__DEFINES\clockcult.dm"
#include "code\__DEFINES\combat.dm"
#include "code\__DEFINES\components.dm"
@@ -189,21 +189,19 @@
#include "code\citadel\organs\womb.dm"
#include "code\citadel\toys\dildos.dm"
#include "code\controllers\admin.dm"
#include "code\controllers\configuration.dm"
#include "code\controllers\configuration_citadel.dm"
#include "code\controllers\configuration\entries\comms.dm"
#include "code\controllers\configuration\entries\config.dm"
#include "code\controllers\configuration\entries\dbconfig.dm"
#include "code\controllers\configuration\entries\game_options.dm"
#include "code\controllers\configuration\config_entry.dm"
#include "code\controllers\configuration\configuration.dm"
#include "code\controllers\admin.dm"
#include "code\controllers\controller.dm"
#include "code\controllers\failsafe.dm"
#include "code\controllers\globals.dm"
#include "code\controllers\hooks.dm"
#include "code\controllers\master.dm"
#include "code\controllers\subsystem.dm"
#include "code\controllers\configuration\config_entry.dm"
#include "code\controllers\configuration\configuration.dm"
#include "code\controllers\configuration\entries\comms.dm"
#include "code\controllers\configuration\entries\config.dm"
#include "code\controllers\configuration\entries\dbconfig.dm"
#include "code\controllers\configuration\entries\game_options.dm"
#include "code\controllers\subsystem\acid.dm"
#include "code\controllers\subsystem\air.dm"
#include "code\controllers\subsystem\assets.dm"
@@ -1040,6 +1038,7 @@
#include "code\game\turfs\simulated\floor.dm"
#include "code\game\turfs\simulated\lava.dm"
#include "code\game\turfs\simulated\minerals.dm"
#include "code\game\turfs\simulated\reebe_void.dm"
#include "code\game\turfs\simulated\river.dm"
#include "code\game\turfs\simulated\walls.dm"
#include "code\game\turfs\simulated\water.dm"
@@ -1064,6 +1063,7 @@
#include "code\modules\admin\admin_verbs.dm"
#include "code\modules\admin\adminmenu.dm"
#include "code\modules\admin\banjob.dm"
#include "code\modules\admin\chat_commands.dm"
#include "code\modules\admin\create_mob.dm"
#include "code\modules\admin\create_object.dm"
#include "code\modules\admin\create_poll.dm"
@@ -2140,7 +2140,8 @@
#include "code\modules\ruins\spaceruin_code\TheDerelict.dm"
#include "code\modules\security_levels\keycard_authentication.dm"
#include "code\modules\security_levels\security_levels.dm"
#include "code\modules\server_tools\server_tools.dm"
#include "code\modules\server_tools\st_interface.dm"
#include "code\modules\server_tools\st_commands.dm"
#include "code\modules\shuttle\arrivals.dm"
#include "code\modules\shuttle\assault_pod.dm"
#include "code\modules\shuttle\computer.dm"
@@ -2290,9 +2291,9 @@
#include "code\modules\VR\vr_sleeper.dm"
#include "code\modules\zombie\items.dm"
#include "code\modules\zombie\organs.dm"
#include "modular_citadel\cit_screenshake.dm"
#include "interface\interface.dm"
#include "interface\menu.dm"
#include "interface\stylesheet.dm"
#include "interface\skin.dmf"
#include "modular_citadel\cit_screenshake.dm"
// END_INCLUDE