Merge branch 'master' into donoritemmodularization

This commit is contained in:
deathride58
2017-12-04 20:17:02 -05:00
569 changed files with 8426 additions and 5523 deletions
+1 -1
View File
@@ -671,4 +671,4 @@ into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+20 -14
View File
@@ -1,6 +1,12 @@
<<<<<<< HEAD
##Citadel Station 13 <BR>
Based and maintained from /tg/station.<BR>
=======
[![Build Status](https://travis-ci.org/tgstation/tgstation.png)](https://travis-ci.org/tgstation/tgstation) [![Krihelimeter](https://www.krihelinator.xyz/badge/tgstation/tgstation)](https://www.krihelinator.xyz)
[![Percentage of issues still open](https://isitmaintained.com/badge/open/tgstation/tgstation.svg)](https://isitmaintained.com/project/tgstation/tgstation "Percentage of issues still open") [![Average time to resolve an issue](https://isitmaintained.com/badge/resolution/tgstation/tgstation.svg)](https://isitmaintained.com/project/tgstation/tgstation "Average time to resolve an issue") ![Coverage](https://img.shields.io/badge/coverage---2%25-red.svg)
[![forthebadge](https://forthebadge.com/images/badges/built-with-resentment.svg)](https://forthebadge.com) [![forthebadge](https://forthebadge.com/images/badges/contains-technical-debt.svg)](https://forthebadge.com) [![forinfinityandbyond](https://user-images.githubusercontent.com/5211576/29499758-4efff304-85e6-11e7-8267-62919c3688a9.gif)](https://www.reddit.com/r/SS13/comments/5oplxp/what_is_the_main_problem_with_byond_as_an_engine/dclbu1a)
>>>>>>> c47dde9... Merge pull request #33172 from praisenarsie/patch-12
[![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)
@@ -32,12 +38,12 @@ 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
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
compile. This'll take a little while, and if everything's done right you'll get
a message like this:
```
@@ -49,17 +55,17 @@ 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
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,
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
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
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
@@ -71,8 +77,8 @@ 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
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).
@@ -106,7 +112,7 @@ https://github.com/tgstation/tgstation-server
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.
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, BoxStation 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.
@@ -122,7 +128,7 @@ To enable an away mission open `config/awaymissionconfig.txt` and uncomment one
## 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
The SQL backend requires a Mariadb server running 10.2 or later. Mysql is not supported but Mariadb is a drop in replacement for mysql. 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: https://www.tgstation13.org/wiki/Downloading_the_source_code#Setting_up_the_database
## IRC BOT SETUP
@@ -140,7 +146,7 @@ All code after [commit 333c566b88108de218d882840e61928a9b759d8f on 2014/31/12 at
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 and LICENSE-GPLv3.txt for more details.
See LICENSE and GPLv3.txt for more details.
tgui clientside is licensed as a subproject under the MIT license.
Font Awesome font files, used by tgui, are licensed under the SIL Open Font License v1.1
+22 -3
View File
@@ -1,15 +1,34 @@
Any time you make a change to the schema files, remember to increment the database schema version. Generally increment the minor number, major should be reserved for significant changes to the schema. Both values go up to 255.
The latest database version is 3.4; The query to update the schema revision table is:
The latest database version is 4.0; The query to update the schema revision table is:
INSERT INTO `schema_revision` (`major`, `minor`) VALUES (3, 4);
INSERT INTO `schema_revision` (`major`, `minor`) VALUES (4, 0);
or
INSERT INTO `SS13_schema_revision` (`major`, `minor`) VALUES (3, 4);
INSERT INTO `SS13_schema_revision` (`major`, `minor`) VALUES (4, 0);
In any query remember to add a prefix to the table names if you use one.
----------------------------------------------------
12 November 2017, by Jordie0608
Modified feedback table to use json, a python script is used to migrate data to this new format.
See the file 'feedback_conversion_2017-11-12.py' for instructions on how to use the script.
A new json feedback table can be created with:
CREATE TABLE `feedback` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`datetime` datetime NOT NULL,
`round_id` int(11) unsigned NOT NULL,
`key_name` varchar(32) NOT NULL,
`key_type` enum('text', 'amount', 'tally', 'nested tally', 'associative') NOT NULL,
`version` tinyint(3) unsigned NOT NULL,
`json` json NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM
----------------------------------------------------
28 August 2017, by MrStonedOne
Modified table 'messages', adding a deleted column and editing all indexes to include it
+532
View File
@@ -0,0 +1,532 @@
#Python 3+ Script for jsonifying feedback table data as of 2017-11-12 made by Jordie0608
#Apologies for the boilerplated and squirrely code in parts, this has been my first foray into python
#
#Before starting ensure you have installed the mysqlclient package https://github.com/PyMySQL/mysqlclient-python
#It can be downloaded from command line with pip:
#pip install mysqlclient
#
#You will also have to create a new feedback table for inserting converted data to per the schema:
#CREATE TABLE `feedback_new` (
# `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
# `datetime` datetime NOT NULL,
# `round_id` int(11) unsigned NOT NULL,
# `key_name` varchar(32) NOT NULL,
# `key_type` enum('text', 'amount', 'tally', 'nested tally', 'associative') NOT NULL,
# `version` tinyint(3) unsigned NOT NULL,
# `json` json NOT NULL,
# PRIMARY KEY (`id`)
#) ENGINE=MyISAM
#This is to prevent the destruction of legacy data and allow rollbacks to be performed in the event of an error during conversion
#Once conversion is complete remember to rename the old and new feedback tables; it's up to you if you want to keep the old table
#
#To view the parameters for this script, execute it with the argument --help
#All the positional arguments are required, remember to include prefixes in your table names if you use them
#An example of the command used to execute this script from powershell:
#python feedback_conversion_2017-11-12.py "localhost" "root" "password" "feedback" "SS13_feedback" "SS13_feedback_new"
#I found that this script would complete conversion of 10000 rows approximately every 2-3 seconds
#Depending on the size of your feedback table and the computer used it may take several minutes for the script to finish
#
#The script has been tested to complete with tgstation's feedback table as of 2017-10-23 01:34:06
#Due to the complexity of data that has potentially changed formats multiple times and suffered errors when recording I cannot guarantee it'll always execute successfully
#In the event of an error the new feedback table is automatically truncated
#The source table is never modified so you don't have to worry about losing any data due to errors
#Note that some feedback keys are renamed or coalesced into one, additionnaly some have been entirely removed
#
#While this script can be run with your game server(s) active, it may interfere with other database operations and any feedback created after the script has started won't be converted
import MySQLdb
import argparse
import json
import re
import sys
from datetime import datetime
def parse_text(details):
if not details:
return
if details.startswith('"') and details.endswith('"'):
details = details[1:-1] #the first and last " aren't removed by splitting the dictionary
details = details.split('" | "')
else:
if "_" in details:
details = details.split(' ')
return details
def parse_tally(details):
if not details:
return
overflowed = None
if len(details) >= 65535: #a string this long means the data hit the 64KB character limit of TEXT columns
overflowed = True
if details.startswith('"') and details.endswith('"'):
details = details[details.find('"')+1:details.rfind('"')] #unlike others some of the tally data has extra characters to remove
split_details = details.split('" | "')
else:
split_details = details.split(' ')
if overflowed:
split_details = split_details[:-1] #since the string overflowed the last element will be incomplete and needs to be ignored
details = {}
for i in split_details:
increment = 1
if '|' in i and i[i.find('|')+1:]:
increment = float(i[i.find('|')+1:])
i = i[:i.find('|')]
if i in details:
details[i] += increment
else:
details[i] = increment
for i in details:
details[i] = '{0:g}'.format(details[i]) #remove .0 from floats that have it to conform with DM
return details
def parse_nested(var_name, details):
if not details:
return
#group by data before pipe
if var_name in ("admin_toggle", "preferences_verb", "changeling_objective", "cult_objective", "traitor_objective", "wizard_objective", "mining_equipment_bought", "vending_machine_usage", "changeling_powers", "wizard_spell_improved", "testmerged_prs"):
if details.startswith('"') and details.endswith('"'):
details = details[1:-1]
split_details = details.split('" | "')
else:
split_details = details.split(' ')
details = {}
for i in split_details:
if "|" in i and i[:i.find('|')] not in details:
details[i[:i.find('|')]] = {}
elif "|" not in i and i[i.find('|')+1:] not in details:
details[i[i.find('|')+1:]] = 0
for i in split_details:
if "|" in i:
if details[i[:i.find('|')]] is not dict:
continue
if i[i.find('|')+1:] in details[i[:i.find('|')]]:
details[i[:i.find('|')]][i[i.find('|')+1:]] += 1
else:
details[i[:i.find('|')]][i[i.find('|')+1:]] = 1
else:
if i in details and type(details[i]) is not dict: #sometimes keys that should have a value after a pipe just don't and would otherwise error here
details[i] += 1
return details
#group by data after pipe
elif var_name in ("cargo_imports", "traitor_uplink_items_bought", "export_sold_cost", "item_used_for_combat", "played_url"):
if details.startswith('"') and details.endswith('"'):
details = details[1:-1]
split_details = details.split('" | "')
else:
split_details = details.split(' ')
details = {}
for i in split_details:
if i == i[i.rfind('|')+1:]: #there's no pipe and data to group by, so we fill it in
i = "{0}|missing data".format(i)
details[i[i.rfind('|')+1:]] = {}
for i in split_details:
if i == i[i.rfind('|')+1:]:
i = "{0}|missing data".format(i)
if i[:i.find('|')] in details[i[i.rfind('|')+1:]]:
details[i[i.rfind('|')+1:]][i[:i.find('|')]] += 1
else:
details[i[i.rfind('|')+1:]][i[:i.find('|')]] = 1
return details
elif var_name == "hivelord_core":
if details.startswith('"') and details.endswith('"'):
details = details[1:-1]
split_details = details.split('" | "')
else:
split_details = details.split(' ')
details = {}
for i in split_details:
if i[:i.find('|')] not in details:
details[i[:i.find('|')]] = {}
if "used" in i:
if "used" not in details:
details[i[:i.find('|')]]["used"] = {}
for i in split_details:
if "used" in i:
if "used" not in details[i[:i.find('|')]]:
details[i[:i.find('|')]]["used"] = {}
details[i[:i.find('|')]]["used"][i[i.rfind('|')+1:]] = 1
else:
if i[i.rfind('|')+1:] in details[i[:i.find('|')]]["used"]:
details[i[:i.find('|')]]["used"][i[i.rfind('|')+1:]] += 1
else:
details[i[:i.find('|')]]["used"][i[i.rfind('|')+1:]] = 1
elif "|" in i:
if i[i.find('|')+1:] in details[i[:i.find('|')]]:
details[i[:i.find('|')]][i[i.find('|')+1:]] += 1
else:
details[i[:i.find('|')]][i[i.find('|')+1:]] = 1
return details
elif var_name == "job_preferences":
if details.startswith('"') and details.endswith('"'):
details = details[1:-1]
split_details = details.split('|-" | "|')
else:
split_details = details.split('|- |')
details = {}
for i in split_details:
if i.startswith('|'):
i = i[1:]
if i[:i.find('|')] not in details:
details[i[:i.find('|')]] = {}
for i in split_details:
if i.startswith('|'):
i = i[1:]
if i.endswith('-'):
i = i[:-2]
sub_split = i.split('|')
job = sub_split[0]
sub_split = sub_split[1:]
for o in sub_split:
details[job][o[:o.find('=')].lower()] = o[o.find('=')+1:]
return details
def parse_associative(var_name, details):
if not details:
return
if var_name == "colonies_dropped":
if details.startswith('"') and details.endswith('"'):
details = details[1:-1]
split_details = details.split('|')
details = {}
details["1"] = {"x" : split_details[0], "y" : split_details[1], "z" : split_details[2]}
return details
elif var_name == "commendation":
if details.startswith('"') and details.endswith('"'):
details = details[1:-1]
if '}" | "{' in details:
split_details = details.split('}" | "{')
else:
split_details = details.split('} {')
details = {}
for i in split_details:
params = []
sub_split = i.split(',')
for o in sub_split:
o = re.sub('[^A-Za-z0-9 ]', '', o[o.find(':')+1:]) #remove all the formatting and escaped characters from being pre-encoded as json
params.append(o)
details[len(details)+1] = {"commender" : params[0], "commendee" : params[1], "medal" : params[2], "reason" : params[3]}
return details
elif var_name == "high_research_level":
if details.startswith('"') and details.endswith('"'):
details = details[1:-1]
split_details = details.split('" | "')
else:
split_details = details.split(' ')
details = {}
levels = {}
for i in split_details:
x = {i[:-1] : i[-1:]}
levels.update(x)
details["1"] = levels
return details
def parse_special(var_name, var_value, details):
#old data is essentially a tally in text form
if var_name == "immortality_talisman":
if details.startswith('"') and details.endswith('"'):
split_details = details.split('" | "')
else:
split_details = details.split(' ')
return len(split_details)
#now records channel names, so we have to fill in whats missing
elif var_name == "newscaster_channels":
details = ["missing data"]
details *= var_value
return details
#all the channels got renamed, plus we ignore any with an amount of zero
elif var_name == "radio_usage":
if details.startswith('"') and details.endswith('"'):
details = details[details.find('C'):-1]
split_details = details.split('" | "')
else:
split_details = details.split(' ')
details = {}
new_keys = {"COM":"common", "SCI":"science", "HEA":"command", "MED":"medical", "ENG":"engineering", "SEC":"security", "DTH":"centcom", "SYN":"syndicate", "SRV":"service", "CAR":"supply", "OTH":"other", "PDA":"PDA", "RC":"request console"}
for i in split_details:
if i.endswith('0'):
continue
if i[:i.find('-')] not in new_keys:
continue
details[new_keys[i[:i.find('-')]]] = i[i.find('-')+1:]
return details
#all of the data tracked by this is invalid due to recording the incorrect type
elif var_name == "shuttle_gib":
return {"missing data":1}
#all records have a prefix of 'slimebirth_' that needs to be removed
elif var_name == "slime_babies_born":
if details.startswith('"') and details.endswith('"'):
details = details[1:-1]
split_details = details.split('" | "')
else:
split_details = details.split(' ')
details = {}
for i in split_details:
if i[i.find('_')+1:].replace('_', ' ') in details:
details[i[i.find('_')+1:].replace('_', ' ')] += 1
else:
details[i[i.find('_')+1:].replace('_', ' ')] = 1
return details
#spaces were replaced by underscores, we need to undo this
elif var_name == "slime_core_harvested":
if details.startswith('"') and details.endswith('"'):
details = details[1:-1]
split_details = details.split('" | "')
else:
split_details = details.split(' ')
details = {}
for i in split_details:
if i.replace('_', ' ') in details:
details[i.replace('_', ' ')] += 1
else:
details[i.replace('_', ' ')] = 1
return details
def parse_multirow(var_name, var_value, details, multirows_completed):
if var_name in ("ahelp_close", "ahelp_icissue", "ahelp_reject", "ahelp_reopen", "ahelp_resolve", "ahelp_unresolved"):
ahelp_vars = {"ahelp_close":"closed", "ahelp_icissue":"IC", "ahelp_reject":"rejected", "ahelp_reopen":"reopened", "ahelp_resolve":"resolved", "ahelp_unresolved":"unresolved"}
details = {ahelp_vars[var_name]:var_value}
del ahelp_vars[var_name]
query_where = "round_id = {0} AND (".format(query_row[2])
for c, i in enumerate(ahelp_vars):
if c:
query_where += " OR "
query_where += "var_name = \"{0}\"".format(i)
query_where += ")"
cursor.execute("SELECT var_name, var_value FROM {0} WHERE {1}".format(current_table, query_where))
rows = cursor.fetchall()
if rows:
for r in rows:
details[ahelp_vars[r[0]]] = r[1]
keys = list(ahelp_vars.keys())
keys.append(var_name)
multirows_completed += keys
return details
elif var_name in ("alert_comms_blue", "alert_comms_green"):
level_vars = {"alert_comms_blue":"1", "alert_comms_green":"0"}
details = {level_vars[var_name]:var_value}
del level_vars[var_name]
i = list(level_vars)[0]
cursor.execute("SELECT var_value FROM {0} WHERE round_id = {1} AND var_name = \"{2}\"".format(current_table, query_row[2], i))
row = cursor.fetchone()
if row:
details[level_vars[i]] = row[0]
keys = list(level_vars.keys())
keys.append(var_name)
multirows_completed += keys
return details
elif var_name in ("alert_keycard_auth_bsa", "alert_keycard_auth_maint"):
auth_vars = {"alert_keycard_auth_maint":("emergency maintenance access", "enabled"), "alert_keycard_auth_bsa":("bluespace artillery", "unlocked")}
i = list(auth_vars[var_name])
details = {i[0]:{i[1]:var_value}}
del auth_vars[var_name]
i = list(auth_vars)[0]
cursor.execute("SELECT var_value FROM {0} WHERE round_id = {1} AND var_name = \"{2}\"".format(current_table, query_row[2], i))
row = cursor.fetchone()
if row:
o = list(auth_vars[i])
details[o[0]] = {o[1]:row[0]}
keys = list(auth_vars.keys())
keys.append(var_name)
multirows_completed += keys
return details
elif var_name in ("arcade_loss_hp_emagged", "arcade_loss_hp_normal", "arcade_loss_mana_emagged", "arcade_loss_mana_normal", "arcade_win_emagged", "arcade_win_normal"):
result_vars = {"arcade_loss_hp_emagged":("loss", "hp", "emagged"), "arcade_loss_hp_normal":("loss", "hp", "normal"), "arcade_loss_mana_emagged":("loss", "mana", "emagged"), "arcade_loss_mana_normal":("loss", "mana", "normal"), "arcade_win_emagged":("win", "emagged"), "arcade_win_normal":("win", "normal")}
i = list(result_vars[var_name])
del result_vars[var_name]
if i[0] == "loss":
details = {i[0]:{i[1]:{i[2]:var_value}}}
else:
details = {i[0]:{i[1]:var_value}}
query_where = "round_id = {0} AND (".format(query_row[2])
for c, i in enumerate(result_vars):
if c:
query_where += " OR "
query_where += "var_name = \"{0}\"".format(i)
query_where += ")"
cursor.execute("SELECT var_name, var_value FROM {0} WHERE {1}".format(current_table, query_where))
rows = cursor.fetchall()
if rows:
for r in rows:
i = list(result_vars[r[0]])
if i[0] not in details:
details[i[0]] = {}
if i[0] == "loss":
if i[1] not in details[i[0]]:
details[i[0]][i[1]] = {}
details[i[0]][i[1]][i[2]] = r[1]
else:
details[i[0]][i[1]] = r[1]
keys = list(result_vars.keys())
keys.append(var_name)
multirows_completed += keys
return details
elif var_name in ("cyborg_engineering", "cyborg_janitor", "cyborg_medical", "cyborg_miner", "cyborg_peacekeeper", "cyborg_security", "cyborg_service", "cyborg_standard"):
module_vars = {"cyborg_engineering":"Engineering", "cyborg_janitor":"Janitor", "cyborg_medical":"Medical", "cyborg_miner":"Miner", "cyborg_peacekeeper":"Peacekeeper", "cyborg_security":"Security", "cyborg_service":"Service", "cyborg_standard":"Standard"}
details = {module_vars[var_name]:var_value}
del module_vars[var_name]
query_where = "round_id = {0} AND (".format(query_row[2])
for c, i in enumerate(module_vars):
if c:
query_where += " OR "
query_where += "var_name = \"{0}\"".format(i)
query_where += ")"
cursor.execute("SELECT var_name, var_value FROM {0} WHERE {1}".format(current_table, query_where))
rows = cursor.fetchall()
if rows:
for r in rows:
details[module_vars[r[0]]] = r[1]
keys = list(module_vars.keys())
keys.append(var_name)
multirows_completed += keys
return details
elif var_name in ("escaped_human", "escaped_total", "round_end_clients", "round_end_ghosts", "survived_human", "survived_total"):
round_vars = {"escaped_human":("escapees", "human"), "escaped_total":("escapees", "total"), "round_end_clients":("clients"), "round_end_ghosts":("ghosts"), "survived_human":("survivors", "human"), "survived_total":("survivors", "total")}
if var_name in ("round_end_clients", "round_end_ghosts"):
i = round_vars[var_name]
details = {i:var_value}
else:
i = list(round_vars[var_name])
details = {i[0]:{i[1]:var_value}}
del round_vars[var_name]
query_where = "round_id = {0} AND (".format(query_row[2])
for c, i in enumerate(round_vars):
if c:
query_where += " OR "
query_where += "var_name = \"{0}\"".format(i)
query_where += ")"
cursor.execute("SELECT var_name, var_value FROM {0} WHERE {1}".format(current_table, query_where))
rows = cursor.fetchall()
if rows:
for r in rows:
if r[0] in ("round_end_clients", "round_end_ghosts"):
i = round_vars[r[0]]
details[i] = r[1]
else:
i = list(round_vars[r[0]])
if i[0] not in details:
details[i[0]] = {}
details[i[0]][i[1]] = r[1]
keys = list(round_vars.keys())
keys.append(var_name)
multirows_completed += keys
return details
elif var_name in ("mecha_durand_created", "mecha_firefighter_created", "mecha_gygax_created", "mecha_honker_created", "mecha_odysseus_created", "mecha_phazon_created", "mecha_ripley_created"):
mecha_vars ={"mecha_durand_created":"Durand", "mecha_firefighter_created":"APLU \"Firefighter\"", "mecha_gygax_created":"Gygax", "mecha_honker_created":"H.O.N.K", "mecha_odysseus_created":"Odysseus", "mecha_phazon_created":"Phazon", "mecha_ripley_created":"APLU \"Ripley\""}
details = {mecha_vars[var_name]:var_value}
del mecha_vars[var_name]
query_where = "round_id = {0} AND (".format(query_row[2])
for c, i in enumerate(mecha_vars):
if c:
query_where += " OR "
query_where += "var_name = \"{0}\"".format(i)
query_where += ")"
cursor.execute("SELECT var_name, var_value FROM {0} WHERE {1}".format(current_table, query_where))
rows = cursor.fetchall()
if rows:
for r in rows:
details[mecha_vars[r[0]]] = r[1]
keys = list(mecha_vars.keys())
keys.append(var_name)
multirows_completed += keys
return details
def pick_parsing(var_name, var_value, details, multirows_completed):
if var_name in text_keys:
return parse_text(details)
elif var_name in amount_keys:
return var_value
elif var_name in simple_tallies:
return parse_tally(details)
elif var_name in nested_tallies:
return parse_nested(var_name, details)
elif var_name in associatives:
return parse_associative(var_name, details)
elif var_name in special_cases:
return parse_special(var_name, var_value, details)
elif var_name in multirow:
return parse_multirow(var_name, var_value, details, multirows_completed)
else:
return False
if sys.version_info[0] < 3:
raise Exception("Python must be at least version 3 for this script.")
text_keys = ["religion_book", "religion_deity", "religion_name", "shuttle_fasttravel", "shuttle_manipulator", "shuttle_purchase", "shuttle_reason", "station_renames"]
amount_keys = ["admin_cookies_spawned", "cyborg_ais_created", "cyborg_frames_built", "cyborg_mmis_filled", "newscaster_newspapers_printed", "newscaster_stories", "nuclear_challenge_mode"]
simple_tallies = ["admin_secrets_fun_used", "admin_verb", "assembly_made", "brother_success", "cell_used", "changeling_power_purchase", "changeling_success", "chaplain_weapon", "chemical_reaction", "circuit_printed", "clockcult_scripture_recited", "contamination", "cult_runes_scribed", "engine_started", "event_admin_cancelled", "event_ran", "food_harvested", "food_made", "gun_fired", "handcuffs", "item_deconstructed", "item_printed", "jaunter", "lazarus_injector", "megafauna_kills", "mining_voucher_redeemed", "mobs_killed_mining", "object_crafted", "ore_mined", "pick_used_mining", "slime_cores_used", "surgeries_completed", "time_dilation_current", "traitor_random_uplink_items_gotten", "traitor_success", "voice_of_god", "warp_cube", "wisp_lantern", "wizard_spell_learned", "wizard_success", "zone_targeted"]
nested_tallies = ["admin_toggle", "cargo_imports", "changeling_objective", "changeling_powers", "cult_objective", "export_sold_cost", "hivelord_core", "item_used_for_combat", "job_preferences", "mining_equipment_bought", "played_url", "preferences_verb", "testmerged_prs", "traitor_objective", "traitor_uplink_items_bought", "vending_machine_usage", "wizard_objective", "wizard_spell_improved"]
associatives = ["colonies_dropped", "commendation", "high_research_level"]
special_cases = ["immortality_talisman", "newscaster_channels", "radio_usage", "shuttle_gib", "slime_babies_born", "slime_core_harvested"]
multirow = ["ahelp_close", "ahelp_icissue", "ahelp_reject", "ahelp_reopen", "ahelp_resolve", "ahelp_unresolved", "alert_comms_blue", "alert_comms_green", "alert_keycard_auth_bsa", "alert_keycard_auth_maint", "arcade_loss_hp_emagged", "arcade_loss_hp_normal", "arcade_loss_mana_emagged", "arcade_loss_mana_normal", "arcade_win_emagged", "arcade_win_normal", "cyborg_engineering", "cyborg_janitor", "cyborg_medical", "cyborg_miner", "cyborg_peacekeeper", "cyborg_security", "cyborg_service", "cyborg_standard", "escaped_human", "escaped_total", "mecha_durand_created", "mecha_firefighter_created", "mecha_gygax_created", "mecha_honker_created", "mecha_odysseus_created", "mecha_phazon_created", "mecha_ripley_created", "round_end_clients", "round_end_ghosts", "survived_human", "survived_total"]
renames = {"ahelp_stats":["ahelp_close", "ahelp_icissue", "ahelp_reject", "ahelp_reopen", "ahelp_resolve", "ahelp_unresolved"], "ais_created":["cyborg_ais_created"], "arcade_results":["arcade_loss_hp_emagged", "arcade_loss_hp_normal", "arcade_loss_mana_emagged", "arcade_loss_mana_normal", "arcade_win_emagged", "arcade_win_normal"], "cyborg_modules":["cyborg_engineering", "cyborg_janitor", "cyborg_medical", "cyborg_miner", "cyborg_peacekeeper", "cyborg_security", "cyborg_service", "cyborg_standard"], "immortality_talisman_uses":["immortality_talisman"], "keycard_auths":["alert_keycard_auth_bsa", "alert_keycard_auth_maint"], "mechas_created":["mecha_durand_created", "mecha_firefighter_created", "mecha_gygax_created", "mecha_honker_created", "mecha_odysseus_created", "mecha_phazon_created", "mecha_ripley_created"], "mmis_filled":["cyborg_mmis_filled"], "newspapers_printed":["newscaster_newspapers_printed"], "round_end_stats":["escaped_human", "escaped_total", "round_end_clients", "round_end_ghosts", "survived_human", "survived_total"], "security_level_changes":["alert_comms_blue", "alert_comms_green"]}
key_types = {"amount":["ais_created", "immortality_talisman_uses", "mmis_filled", "newspapers_printed", "admin_cookies_spawned", "cyborg_frames_built", "newscaster_stories", "nuclear_challenge_mode"],
"associative":["colonies_dropped", "commendation", "high_research_level"],
"nested tally":["admin_toggle", "arcade_results", "cargo_imports", "changeling_objective", "changeling_powers", "cult_objective", "export_sold_cost", "hivelord_core", "item_used_for_combat", "job_preferences", "keycard_auths", "mining_equipment_bought", "played_url", "preferences_verb", "round_end_stats", "testmerged_prs", "traitor_objective", "traitor_uplink_items_bought", "vending_machine_usage", "wizard_objective", "wizard_spell_improved"],
"tally":[ "admin_secrets_fun_used", "admin_verb", "ahelp_stats", "assembly_made", "brother_success", "cell_used", "changeling_power_purchase", "changeling_success", "chaplain_weapon", "chemical_reaction", "circuit_printed", "clockcult_scripture_recited", "contamination", "cult_runes_scribed", "cyborg_modules", "engine_started", "event_admin_cancelled", "event_ran", "food_harvested", "food_made", "gun_fired", "handcuffs", "item_deconstructed", "item_printed", "jaunter", "lazarus_injector", "mechas_created", "megafauna_kills", "mining_voucher_redeemed", "mobs_killed_mining", "object_crafted", "ore_mined", "pick_used_mining", "radio_usage", "security_level_changes", "shuttle_gib", "slime_babies_born", "slime_cores_used", "slime_core_harvested", "surgeries_completed", "time_dilation_current", "traitor_random_uplink_items_gotten", "traitor_success", "voice_of_god", "warp_cube", "wisp_lantern", "wizard_spell_learned", "wizard_success", "zone_targeted"],
"text":["shuttle_fasttravel", "shuttle_manipulator", "shuttle_purchase", "shuttle_reason", "newscaster_channels", "religion_book", "religion_deity", "religion_name", "station_renames"]}
multirows_completed = []
query_values = ""
current_round = 0
parser = argparse.ArgumentParser()
parser.add_argument("address", help="MySQL server address (use localhost for the current computer)")
parser.add_argument("username", help="MySQL login username")
parser.add_argument("password", help="MySQL login username")
parser.add_argument("database", help="Database name")
parser.add_argument("curtable", help="Name of the current feedback table (remember prefixes if you use them)")
parser.add_argument("newtable", help="Name of the new table to insert to, can't be same as the source table (remember prefixes)")
args = parser.parse_args()
db=MySQLdb.connect(host=args.address, user=args.username, passwd=args.password, db=args.database)
cursor=db.cursor()
current_table = args.curtable
new_table = args.newtable
cursor.execute("SELECT max(id) FROM {0}".format(current_table))
query_id = cursor.fetchone()
max_id = query_id[0]
start_time = datetime.now()
print("Beginning conversion at {0}".format(start_time.strftime("%Y-%m-%d %H:%M:%S")))
try:
for current_id in range(max_id):
if current_id % 10000 == 0:
cur_time = datetime.now()
print("Reached row ID {0} Duration: {1}".format(current_id, cur_time - start_time))
cursor.execute("SELECT * FROM {0} WHERE id = {1}".format(current_table, current_id))
query_row = cursor.fetchone()
if not query_row:
continue
else:
if current_round != query_row[2]:
multirows_completed.clear()
if query_values:
query_values = query_values[:-1]
query_values += ';'
cursor.execute("INSERT INTO {0} (datetime, round_id, key_name, key_type, version, json) VALUES {1}".format(new_table, query_values))
query_values = ""
current_round = query_row[2]
if query_row[3] in multirows_completed:
continue
parsed_data = pick_parsing(query_row[3], query_row[4], query_row[5], multirows_completed)
if not parsed_data:
continue
json_data = {}
json_data["data"] = parsed_data
new_key = query_row[3]
for r in renames:
if new_key in renames[r]:
new_key = r
break
new_key_type = None
for t in key_types:
if new_key in key_types[t]:
new_key_type = t
break
dequoted_json = re.sub("\'", "\\'", json.dumps(json_data))
query_values += "('{0}',{1},'{2}','{3}',{4},'{5}'),".format(query_row[1], query_row[2], new_key, new_key_type, 1, dequoted_json)
end_time = datetime.now()
print("Conversion completed at {0}".format(datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
print("Script duration: {0}".format(end_time - start_time))
except Exception as e:
end_time = datetime.now()
print("Error encountered on row ID {0} at {1}".format(current_id, datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
print("Script duration: {0}".format(end_time - start_time))
cursor.execute("TRUNCATE {0} ".format(new_table))
raise e
cursor.close()
+7 -6
View File
@@ -163,12 +163,13 @@ DROP TABLE IF EXISTS `feedback`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `feedback` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`time` datetime NOT NULL,
`round_id` int(8) NOT NULL,
`var_name` varchar(32) NOT NULL,
`var_value` int(16) DEFAULT NULL,
`details` text,
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`datetime` datetime NOT NULL,
`round_id` int(11) unsigned NOT NULL,
`key_name` varchar(32) NOT NULL,
`key_type` enum('text', 'amount', 'tally', 'nested tally', 'associative') NOT NULL,
`version` tinyint(3) unsigned NOT NULL,
`json` json NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
+7 -6
View File
@@ -163,12 +163,13 @@ DROP TABLE IF EXISTS `SS13_feedback`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `SS13_feedback` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`time` datetime NOT NULL,
`round_id` int(8) NOT NULL,
`var_name` varchar(32) NOT NULL,
`var_value` int(16) DEFAULT NULL,
`details` text,
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`datetime` datetime NOT NULL,
`round_id` int(11) unsigned NOT NULL,
`key_name` varchar(32) NOT NULL,
`version` tinyint(3) unsigned NOT NULL,
`key_type` enum('text', 'amount', 'tally', 'nested tally', 'associative') NOT NULL,
`json` json NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
@@ -270,7 +270,7 @@
/turf/open/floor/wood,
/area/ruin/powered/beach)
"be" = (
/obj/vehicle/scooter/skateboard{
/obj/vehicle/ridden/scooter/skateboard{
dir = 4
},
/turf/open/floor/plating/beach/sand,
@@ -528,7 +528,7 @@
/turf/open/floor/plasteel/white,
/area/ruin/powered/animal_hospital)
"bL" = (
/obj/vehicle/scooter/skateboard{
/obj/vehicle/ridden/scooter/skateboard{
dir = 4
},
/turf/open/floor/grass{
@@ -170,7 +170,7 @@
},
/area/ruin/powered/snow_biodome)
"aJ" = (
/obj/vehicle/atv,
/obj/vehicle/ridden/atv,
/turf/open/floor/plating/asteroid/snow{
initial_gas_mix = "o2=22;n2=82;TEMP=180"
},
@@ -19,7 +19,10 @@
req_access = null;
req_access_txt = "150"
},
/turf/open/space/basic,
/obj/effect/turf_decal/stripes/line{
dir = 10
},
/turf/open/floor/mineral/plastitanium,
/area/ruin/powered/syndicate_lava_base)
"ag" = (
/obj/structure/table/wood,
@@ -34,12 +37,16 @@
/obj/machinery/light/small{
dir = 1
},
/turf/open/space/basic,
/obj/effect/turf_decal/stripes/line,
/turf/open/floor/mineral/plastitanium,
/area/ruin/powered/syndicate_lava_base)
"ah" = (
/obj/structure/table/wood,
/obj/item/reagent_containers/food/snacks/salad/validsalad,
/turf/open/space/basic,
/obj/effect/turf_decal/stripes/line{
dir = 6
},
/turf/open/floor/mineral/plastitanium,
/area/ruin/powered/syndicate_lava_base)
"ai" = (
/obj/structure/rack{
@@ -108,7 +115,10 @@
},
/obj/item/stack/rods/fifty,
/obj/structure/table/reinforced,
/turf/open/space/basic,
/obj/effect/turf_decal/stripes/line{
dir = 9
},
/turf/open/floor/mineral/plastitanium,
/area/ruin/powered/syndicate_lava_base)
"as" = (
/turf/open/floor/wood,
@@ -146,7 +156,10 @@
/obj/machinery/light{
dir = 4
},
/turf/open/space/basic,
/obj/effect/turf_decal/stripes/line{
dir = 8
},
/turf/open/floor/mineral/plastitanium,
/area/ruin/powered/syndicate_lava_base)
"ay" = (
/obj/structure/table/wood,
@@ -192,7 +205,10 @@
/obj/item/stack/sheet/glass/fifty,
/obj/structure/table/reinforced,
/obj/item/wrench,
/turf/open/space/basic,
/obj/effect/turf_decal/stripes/line{
dir = 10
},
/turf/open/floor/mineral/plastitanium,
/area/ruin/powered/syndicate_lava_base)
"aC" = (
/obj/structure/table/wood,
@@ -532,7 +548,8 @@
/obj/structure/noticeboard{
pixel_y = 32
},
/turf/open/space/basic,
/obj/effect/turf_decal/stripes/corner,
/turf/open/floor/mineral/plastitanium,
/area/ruin/powered/syndicate_lava_base)
"bq" = (
/obj/structure/reagent_dispensers/virusfood{
@@ -543,7 +560,8 @@
/obj/item/stack/sheet/mineral/plasma{
amount = 5
},
/turf/open/space/basic,
/obj/effect/turf_decal/stripes/line,
/turf/open/floor/mineral/plastitanium,
/area/ruin/powered/syndicate_lava_base)
"br" = (
/obj/structure/extinguisher_cabinet{
@@ -674,7 +692,10 @@
/obj/item/stack/sheet/mineral/silver{
amount = 10
},
/turf/open/space/basic,
/obj/effect/turf_decal/stripes/line{
dir = 6
},
/turf/open/floor/mineral/plastitanium,
/area/ruin/powered/syndicate_lava_base)
"bH" = (
/obj/structure/chair/office/dark{
@@ -723,7 +744,10 @@
/area/ruin/powered/syndicate_lava_base)
"bM" = (
/obj/machinery/smartfridge/chemistry/virology/preloaded,
/turf/open/space/basic,
/obj/effect/turf_decal/stripes/line{
dir = 4
},
/turf/open/floor/mineral/plastitanium,
/area/ruin/powered/syndicate_lava_base)
"bN" = (
/obj/machinery/vending/toyliberationstation{
@@ -771,7 +795,10 @@
/obj/item/reagent_containers/dropper,
/obj/item/reagent_containers/syringe,
/obj/item/reagent_containers/glass/beaker/large,
/turf/open/space/basic,
/obj/effect/turf_decal/stripes/line{
dir = 5
},
/turf/open/floor/mineral/plastitanium,
/area/ruin/powered/syndicate_lava_base)
"bT" = (
/obj/structure/chair/office/dark,
@@ -842,7 +869,10 @@
/obj/item/clothing/head/bio_hood/general,
/obj/item/clothing/glasses/hud/health,
/obj/item/clothing/glasses/science,
/turf/open/space/basic,
/obj/effect/turf_decal/stripes/line{
dir = 6
},
/turf/open/floor/mineral/plastitanium,
/area/ruin/powered/syndicate_lava_base)
"cb" = (
/obj/machinery/shower{
@@ -2230,7 +2260,7 @@ fS
bO
cf
cm
cv
gm
ad
cG
cG
@@ -2505,19 +2535,19 @@ aq
bg
bn
go
gq
gs
go
go
fT
gu
go
co
cy
gw
go
fT
gy
gA
go
go
fT
gC
cv
go
gm
dn
dq
aO
@@ -2545,19 +2575,19 @@ ap
bg
bo
gp
gr
gt
gp
gp
fU
gv
gp
cp
cz
gx
gp
fU
gz
gB
gp
gp
fU
gD
cA
gp
gn
dn
dr
aO
@@ -2830,7 +2860,7 @@ bx
bx
ae
cq
cA
gn
ad
cI
cP
+2 -2
View File
@@ -559,7 +559,7 @@
/turf/template_noop,
/area/ruin/space/derelict/bridge/ai_upload)
"bM" = (
/obj/item/stock_parts/console_screen,
/obj/item/stack/sheet/glass,
/turf/template_noop,
/area/ruin/space/derelict/bridge/ai_upload)
"bN" = (
@@ -991,7 +991,7 @@
icon_state = "0-8"
},
/obj/structure/frame/machine,
/obj/item/stock_parts/console_screen,
/obj/item/stack/sheet/glass,
/turf/open/floor/plating/airless,
/area/ruin/space/derelict/gravity_generator)
"dj" = (
@@ -1488,8 +1488,6 @@
/area/ruin/space/has_grav/deepstorage/armory)
"db" = (
/obj/structure/table,
/obj/item/device/mass_spectrometer/adv,
/obj/item/device/mass_spectrometer/adv,
/obj/item/device/healthanalyzer,
/obj/item/device/healthanalyzer,
/obj/item/stack/medical/gauze,
+33 -33
View File
@@ -1979,7 +1979,7 @@
"fI" = (
/obj/effect/decal/cleanable/dirt,
/obj/machinery/door/airlock/maintenance_hatch,
/obj/machinery/atmospherics/pipe/simple/supply/visible,
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/turf/open/floor/plating,
/area/ruin/space/has_grav/ancientstation/hydroponics)
"fJ" = (
@@ -2111,12 +2111,12 @@
/turf/open/floor/plasteel/airless/floorgrime,
/area/ruin/space/has_grav/ancientstation/betanorth)
"ga" = (
/obj/machinery/atmospherics/pipe/simple/supply/visible{
dir = 4
},
/obj/structure/cable{
icon_state = "4-8"
},
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
/turf/open/floor/plating/airless,
/area/ruin/space/has_grav/ancientstation/betanorth)
"gb" = (
@@ -2124,7 +2124,7 @@
/obj/item/stack/cable_coil{
amount = 2
},
/obj/machinery/atmospherics/pipe/simple/supply/visible{
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
/turf/template_noop,
@@ -2134,12 +2134,12 @@
/obj/structure/cable{
icon_state = "2-4"
},
/obj/machinery/atmospherics/pipe/simple/supply/visible{
dir = 4
},
/obj/structure/cable{
icon_state = "4-8"
},
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
/turf/template_noop,
/area/template_noop)
"gd" = (
@@ -2147,7 +2147,7 @@
/obj/structure/cable{
icon_state = "4-8"
},
/obj/machinery/atmospherics/pipe/simple/supply/visible{
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
/turf/template_noop,
@@ -2163,7 +2163,7 @@
/obj/structure/cable{
icon_state = "4-8"
},
/obj/machinery/atmospherics/pipe/simple/supply/visible{
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
/turf/template_noop,
@@ -2176,7 +2176,7 @@
/obj/structure/cable{
icon_state = "2-4"
},
/obj/machinery/atmospherics/pipe/simple/supply/visible{
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
/turf/template_noop,
@@ -2192,7 +2192,7 @@
/obj/structure/cable{
icon_state = "4-8"
},
/obj/machinery/atmospherics/pipe/simple/supply/visible{
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
/turf/open/floor/plating,
@@ -2202,7 +2202,7 @@
/obj/structure/cable{
icon_state = "4-8"
},
/obj/machinery/atmospherics/pipe/simple/supply/visible{
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
/turf/open/floor/plating,
@@ -2216,7 +2216,7 @@
/obj/structure/cable{
icon_state = "2-8"
},
/obj/machinery/atmospherics/pipe/simple/supply/visible{
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
/turf/open/floor/plating,
@@ -2230,7 +2230,7 @@
icon_state = "1-8"
},
/obj/effect/decal/cleanable/dirt,
/obj/machinery/atmospherics/pipe/simple/supply/visible{
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
/turf/open/floor/plating,
@@ -2253,7 +2253,7 @@
/obj/item/stack/cable_coil{
amount = 2
},
/obj/machinery/atmospherics/pipe/simple/supply/visible{
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
/turf/open/floor/plating,
@@ -2318,7 +2318,7 @@
},
/obj/machinery/door/airlock/maintenance_hatch,
/obj/effect/decal/cleanable/dirt,
/obj/machinery/atmospherics/pipe/simple/supply/visible{
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
/turf/open/floor/plating,
@@ -2328,7 +2328,7 @@
icon_state = "4-8"
},
/obj/effect/decal/cleanable/dirt,
/obj/machinery/atmospherics/pipe/simple/supply/visible{
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
/turf/open/floor/plating,
@@ -2347,7 +2347,7 @@
/obj/structure/cable{
icon_state = "0-8"
},
/obj/machinery/atmospherics/pipe/simple/supply/visible{
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
/turf/open/floor/plating,
@@ -2358,7 +2358,7 @@
},
/obj/effect/decal/cleanable/dirt,
/obj/effect/decal/cleanable/dirt,
/obj/machinery/atmospherics/pipe/manifold/supply/visible{
/obj/machinery/atmospherics/pipe/manifold/supply/hidden{
dir = 4
},
/turf/open/floor/plating,
@@ -2485,7 +2485,7 @@
/obj/structure/cable{
icon_state = "0-4"
},
/obj/machinery/atmospherics/pipe/simple/supply/visible{
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
/turf/open/floor/plating,
@@ -2494,7 +2494,7 @@
/obj/structure/cable{
icon_state = "4-8"
},
/obj/machinery/atmospherics/pipe/simple/supply/visible{
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
/turf/open/floor/plating/airless,
@@ -2504,7 +2504,7 @@
/obj/structure/cable{
icon_state = "4-8"
},
/obj/machinery/atmospherics/pipe/simple/supply/visible{
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
/turf/open/floor/plating,
@@ -2514,7 +2514,7 @@
/obj/structure/cable{
icon_state = "4-8"
},
/obj/machinery/atmospherics/pipe/simple/supply/visible{
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
/turf/open/floor/plating,
@@ -2527,7 +2527,7 @@
/obj/structure/cable{
icon_state = "1-8"
},
/obj/machinery/atmospherics/pipe/simple/supply/visible{
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
/turf/open/floor/plating,
@@ -2538,7 +2538,7 @@
icon_state = "4-8"
},
/obj/machinery/door/airlock/maintenance_hatch,
/obj/machinery/atmospherics/pipe/simple/supply/visible{
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
/turf/open/floor/plating,
@@ -2610,8 +2610,8 @@
/area/ruin/space/has_grav/ancientstation/rnd)
"gQ" = (
/obj/structure/table,
/obj/item/stock_parts/console_screen,
/obj/item/stock_parts/console_screen,
/obj/item/stack/sheet/glass,
/obj/item/stack/sheet/glass,
/obj/item/stock_parts/matter_bin,
/obj/item/stock_parts/scanning_module{
pixel_x = 2;
@@ -2670,7 +2670,7 @@
"gZ" = (
/obj/effect/decal/cleanable/dirt,
/obj/machinery/door/airlock/maintenance_hatch,
/obj/machinery/atmospherics/pipe/simple/supply/visible,
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/turf/open/floor/plating,
/area/ruin/space/has_grav/ancientstation/kitchen)
"ha" = (
@@ -2687,7 +2687,7 @@
/area/ruin/space/has_grav/ancientstation/sec)
"hc" = (
/obj/effect/decal/cleanable/dirt,
/obj/machinery/atmospherics/pipe/simple/supply/visible,
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/turf/open/floor/plasteel/red/side{
dir = 4
},
@@ -2854,7 +2854,7 @@
/obj/machinery/light/small{
dir = 4
},
/obj/machinery/atmospherics/pipe/simple/supply/visible,
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
/turf/open/floor/plasteel/red/side{
dir = 4
},
@@ -4288,7 +4288,7 @@
/turf/open/floor/plasteel/white,
/area/ruin/space/has_grav/ancientstation/proto)
"kV" = (
/obj/machinery/atmospherics/pipe/manifold/supply/visible{
/obj/machinery/atmospherics/pipe/manifold/supply/hidden{
dir = 8
},
/turf/open/floor/plating/airless,
@@ -4457,7 +4457,7 @@
/area/template_noop)
"lv" = (
/obj/effect/decal/cleanable/dirt,
/obj/item/stock_parts/console_screen,
/obj/item/stack/sheet/glass,
/turf/open/floor/plasteel/airless/white/side{
dir = 4
},
+1 -13
View File
@@ -3301,18 +3301,6 @@
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
/turf/open/floor/plating,
/area/ruin/space/has_grav/hotel)
"iX" = (
/obj/structure/cable{
icon_state = "4-8"
},
/obj/structure/cable{
icon_state = "2-8"
},
/obj/machinery/atmospherics/pipe/manifold/supply/hidden{
dir = 1
},
/turf/open/floor/plating,
/area/ruin/space/has_grav/hotel)
"iY" = (
/obj/structure/cable{
icon_state = "4-8"
@@ -3944,7 +3932,7 @@
/area/ruin/space/has_grav/hotel/security)
"kP" = (
/obj/effect/turf_decal/stripes/corner{
dir = 2
dir = 1
},
/turf/open/floor/plasteel/darkyellow,
/area/ruin/space/has_grav/hotel/security)
+1 -1
View File
@@ -825,7 +825,7 @@
/turf/open/floor/plating/snowed/temperatre,
/area/awaymission/cabin)
"cT" = (
/obj/vehicle/atv,
/obj/vehicle/ridden/atv,
/turf/open/floor/plating/snowed/temperatre,
/area/awaymission/cabin)
"cU" = (
+3 -3
View File
@@ -15,7 +15,7 @@
/area/awaymission/snowdin/post)
"ae" = (
/obj/effect/decal/cleanable/oil,
/obj/vehicle/atv,
/obj/vehicle/ridden/atv,
/turf/open/floor/plating{
baseturf = /turf/open/floor/plating/asteroid/snow
},
@@ -41,7 +41,7 @@
},
/area/awaymission/snowdin/post)
"ai" = (
/obj/vehicle/atv,
/obj/vehicle/ridden/atv,
/turf/open/floor/plating{
baseturf = /turf/open/floor/plating/asteroid/snow
},
@@ -1842,7 +1842,7 @@
/turf/open/floor/plating/asteroid/snow,
/area/awaymission/snowdin)
"eZ" = (
/obj/vehicle/atv,
/obj/vehicle/ridden/atv,
/turf/open/floor/plating/asteroid/snow{
temperature = 140
},
+3 -3
View File
@@ -3735,9 +3735,9 @@
/area/awaymission/undergroundoutpost45/research)
"ik" = (
/obj/structure/table/glass,
/obj/item/stock_parts/console_screen,
/obj/item/stock_parts/console_screen,
/obj/item/stock_parts/console_screen,
/obj/item/stack/sheet/glass,
/obj/item/stack/sheet/glass,
/obj/item/stack/sheet/glass,
/obj/item/stock_parts/matter_bin,
/obj/item/stock_parts/matter_bin,
/turf/open/floor/plasteel/white/side{
File diff suppressed because it is too large Load Diff
+21 -21
View File
@@ -3164,7 +3164,7 @@
name = "dust"
},
/obj/structure/frame/computer,
/obj/item/stock_parts/console_screen,
/obj/item/stack/sheet/glass,
/turf/open/floor/plasteel/whitepurple/side{
dir = 8
},
@@ -7652,8 +7652,8 @@
},
/obj/item/stack/cable_coil/white,
/obj/item/stack/cable_coil/white,
/obj/item/stock_parts/console_screen,
/obj/item/stock_parts/console_screen,
/obj/item/stack/sheet/glass,
/obj/item/stack/sheet/glass,
/obj/item/circuitboard/machine/microwave,
/turf/open/floor/plasteel/dark,
/area/crew_quarters/electronic_marketing_den)
@@ -10106,7 +10106,7 @@
/turf/open/floor/plasteel/neutral,
/area/janitor)
"avA" = (
/obj/vehicle/janicart,
/obj/vehicle/ridden/janicart,
/obj/machinery/status_display{
pixel_y = 32
},
@@ -43446,9 +43446,9 @@
/obj/structure/table/reinforced,
/obj/item/folder/white,
/obj/item/stock_parts/cell/high,
/obj/item/stock_parts/console_screen,
/obj/item/stock_parts/console_screen,
/obj/item/stock_parts/console_screen,
/obj/item/stack/sheet/glass,
/obj/item/stack/sheet/glass,
/obj/item/stack/sheet/glass,
/turf/open/floor/plasteel/vault{
dir = 8
},
@@ -48974,7 +48974,7 @@
/turf/open/floor/plating,
/area/ai_monitored/security/armory)
"bVp" = (
/obj/vehicle/secway,
/obj/vehicle/ridden/secway,
/turf/open/floor/plasteel/vault{
dir = 8
},
@@ -57608,11 +57608,11 @@
},
/obj/effect/decal/cleanable/dirt,
/obj/effect/turf_decal/loading_area,
/turf/open/floor/plasteel/neutral,
/turf/open/floor/plasteel,
/area/teleporter)
"cmH" = (
/obj/effect/turf_decal/loading_area,
/turf/open/floor/plasteel/neutral,
/turf/open/floor/plasteel,
/area/teleporter)
"cmI" = (
/obj/structure/table,
@@ -61739,7 +61739,7 @@
/obj/item/clipboard,
/obj/item/folder/blue,
/obj/item/electronics/firelock,
/obj/item/stock_parts/console_screen,
/obj/item/stack/sheet/glass,
/obj/effect/turf_decal/bot,
/turf/open/floor/plasteel,
/area/maintenance/port)
@@ -75440,7 +75440,7 @@
/obj/structure/table,
/obj/item/clipboard,
/obj/item/electronics/airlock,
/obj/item/stock_parts/console_screen,
/obj/item/stack/sheet/glass,
/obj/item/device/assembly/signaler,
/obj/machinery/airalarm{
dir = 1;
@@ -79144,9 +79144,9 @@
/obj/structure/table/reinforced,
/obj/item/folder/white,
/obj/item/stock_parts/cell/high,
/obj/item/stock_parts/console_screen,
/obj/item/stock_parts/console_screen,
/obj/item/stock_parts/console_screen,
/obj/item/stack/sheet/glass,
/obj/item/stack/sheet/glass,
/obj/item/stack/sheet/glass,
/obj/structure/cable/white{
icon_state = "0-4"
},
@@ -84059,9 +84059,9 @@
/obj/structure/table/reinforced,
/obj/item/folder/white,
/obj/item/stock_parts/cell/high,
/obj/item/stock_parts/console_screen,
/obj/item/stock_parts/console_screen,
/obj/item/stock_parts/console_screen,
/obj/item/stack/sheet/glass,
/obj/item/stack/sheet/glass,
/obj/item/stack/sheet/glass,
/obj/effect/decal/cleanable/dirt,
/turf/open/floor/plating,
/area/science/research/abandoned)
@@ -86074,7 +86074,7 @@
/area/science/research/abandoned)
"dus" = (
/obj/structure/table/reinforced,
/obj/item/stock_parts/console_screen,
/obj/item/stack/sheet/glass,
/obj/item/stock_parts/micro_laser,
/obj/effect/decal/cleanable/dirt,
/obj/effect/turf_decal/delivery,
@@ -87643,7 +87643,7 @@
/obj/item/clipboard,
/obj/item/folder/yellow,
/obj/item/electronics/firealarm,
/obj/item/stock_parts/console_screen,
/obj/item/stack/sheet/glass,
/obj/effect/turf_decal/bot,
/turf/open/floor/plasteel,
/area/hallway/secondary/construction)
@@ -90494,7 +90494,7 @@
"dDk" = (
/obj/structure/frame/machine,
/obj/machinery/light/small,
/obj/item/stock_parts/console_screen,
/obj/item/stack/sheet/glass,
/obj/effect/decal/cleanable/dirt,
/turf/open/floor/plating,
/area/science/research/abandoned)
+8 -8
View File
@@ -8769,7 +8769,7 @@
/obj/machinery/atmospherics/components/unary/vent_pump/on{
dir = 4
},
/obj/vehicle/secway,
/obj/vehicle/ridden/secway,
/obj/item/key/security,
/turf/open/floor/plasteel/red/side{
dir = 10
@@ -25863,6 +25863,7 @@
forwards = 2;
id = "packageSort2"
},
/obj/effect/turf_decal/loading_area,
/turf/open/floor/plating,
/area/quartermaster/sorting)
"bbP" = (
@@ -29269,7 +29270,7 @@
name = "Station Intercom (General)";
pixel_y = -28
},
/obj/vehicle/janicart,
/obj/vehicle/ridden/janicart,
/obj/item/key/janitor,
/turf/open/floor/plating,
/area/janitor)
@@ -50276,7 +50277,7 @@
/area/science/research)
"caA" = (
/obj/structure/table,
/obj/item/stock_parts/console_screen,
/obj/item/stack/sheet/glass,
/obj/item/electronics/airlock,
/obj/item/device/assembly/timer{
pixel_x = -4;
@@ -52867,7 +52868,7 @@
"cfC" = (
/obj/item/trash/pistachios,
/obj/structure/closet,
/obj/item/stock_parts/console_screen,
/obj/item/stack/sheet/glass,
/obj/item/extinguisher,
/obj/item/storage/belt/utility,
/turf/open/floor/plating,
@@ -56851,9 +56852,9 @@
/area/science/lab)
"cnW" = (
/obj/structure/table,
/obj/item/stock_parts/console_screen,
/obj/item/stock_parts/console_screen,
/obj/item/stock_parts/console_screen,
/obj/item/stack/sheet/glass,
/obj/item/stack/sheet/glass,
/obj/item/stack/sheet/glass,
/obj/item/stock_parts/cell/high{
charge = 100;
maxcharge = 15000
@@ -74120,7 +74121,6 @@
desc = "A thin layer of dust coating the floor.";
name = "dust"
},
/obj/item/device/mass_spectrometer,
/turf/open/floor/mineral/titanium,
/area/shuttle/abandoned)
"cXL" = (
@@ -6043,7 +6043,7 @@
/turf/open/floor/plating,
/area/security/brig)
"akW" = (
/obj/vehicle/secway,
/obj/vehicle/ridden/secway,
/obj/machinery/light{
dir = 8
},
@@ -19057,7 +19057,7 @@
/turf/open/floor/plasteel,
/area/janitor)
"aJJ" = (
/obj/vehicle/janicart,
/obj/vehicle/ridden/janicart,
/obj/effect/decal/cleanable/dirt,
/obj/item/storage/bag/trash,
/obj/item/key/janitor,
@@ -24721,9 +24721,9 @@
/obj/structure/table/reinforced,
/obj/item/folder/white,
/obj/item/stock_parts/cell/high,
/obj/item/stock_parts/console_screen,
/obj/item/stock_parts/console_screen,
/obj/item/stock_parts/console_screen,
/obj/item/stack/sheet/glass,
/obj/item/stack/sheet/glass,
/obj/item/stack/sheet/glass,
/obj/effect/turf_decal/stripes/line{
dir = 8
},
@@ -25197,8 +25197,8 @@
/obj/structure/table/wood,
/obj/item/stack/cable_coil/white,
/obj/item/stack/cable_coil/white,
/obj/item/stock_parts/console_screen,
/obj/item/stock_parts/console_screen,
/obj/item/stack/sheet/glass,
/obj/item/stack/sheet/glass,
/obj/item/circuitboard/machine/microwave,
/obj/effect/decal/cleanable/dirt,
/turf/open/floor/wood,
+7 -10
View File
@@ -2614,7 +2614,7 @@
/turf/open/floor/plasteel/dark,
/area/security/armory)
"ail" = (
/obj/vehicle/secway,
/obj/vehicle/ridden/secway,
/turf/open/floor/plasteel/dark,
/area/security/armory)
"aim" = (
@@ -2647,7 +2647,7 @@
/turf/open/floor/plasteel/showroomfloor,
/area/security/main)
"air" = (
/obj/vehicle/secway,
/obj/vehicle/ridden/secway,
/obj/item/key/security,
/obj/effect/turf_decal/bot,
/turf/open/floor/plasteel/showroomfloor,
@@ -5716,7 +5716,6 @@
/turf/open/floor/plating,
/area/maintenance/department/security/brig)
"apA" = (
/mob/living/simple_animal/mouse/gray,
/turf/open/floor/plating{
icon_state = "platingdmg3"
},
@@ -14612,7 +14611,6 @@
/turf/open/floor/plating,
/area/maintenance/department/cargo)
"aKo" = (
/mob/living/simple_animal/mouse/gray,
/turf/open/floor/plating{
burnt = 1;
icon_state = "panelscorched"
@@ -21626,7 +21624,7 @@
/turf/open/floor/plasteel/neutral/corner,
/area/hallway/primary/central)
"baX" = (
/obj/vehicle/janicart,
/obj/vehicle/ridden/janicart,
/obj/structure/disposalpipe/segment,
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
@@ -24725,7 +24723,6 @@
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
/mob/living/simple_animal/mouse/gray,
/turf/open/floor/plating,
/area/maintenance/department/cargo)
"biB" = (
@@ -32699,8 +32696,8 @@
/area/science/lab)
"bAq" = (
/obj/structure/table,
/obj/item/stock_parts/console_screen,
/obj/item/stock_parts/console_screen,
/obj/item/stack/sheet/glass,
/obj/item/stack/sheet/glass,
/obj/item/stock_parts/capacitor,
/obj/item/stock_parts/capacitor,
/obj/item/stock_parts/manipulator,
@@ -47954,8 +47951,8 @@
/area/tcommsat/server)
"cnw" = (
/obj/structure/table,
/obj/item/stock_parts/console_screen,
/obj/item/stock_parts/console_screen,
/obj/item/stack/sheet/glass,
/obj/item/stack/sheet/glass,
/obj/item/stock_parts/micro_laser,
/obj/item/stock_parts/micro_laser,
/turf/open/floor/plasteel/dark/telecomms,
+21 -15
View File
@@ -8111,7 +8111,10 @@
width = 18
},
/obj/structure/fans/tiny,
/turf/open/space/basic,
/obj/effect/turf_decal/stripes/line{
dir = 1
},
/turf/open/floor/mineral/plastitanium,
/area/shuttle/syndicate/airlock)
"vs" = (
/obj/structure/sign/securearea{
@@ -8554,7 +8557,10 @@
id = "XCCFerry";
name = "XCC Ferry Hangar"
},
/turf/open/space/basic,
/obj/effect/turf_decal/loading_area{
dir = 4
},
/turf/open/floor/plasteel,
/area/centcom/ferry)
"ww" = (
/obj/machinery/button/door{
@@ -11598,7 +11604,7 @@
/turf/open/floor/plasteel,
/area/wizard_station)
"Ej" = (
/obj/vehicle/scooter/skateboard{
/obj/vehicle/ridden/scooter/skateboard{
icon_state = "skateboard";
dir = 4
},
@@ -40556,7 +40562,7 @@ aa
aa
KH
KH
KT
KS
La
Lb
Ld
@@ -42612,7 +42618,7 @@ aa
aa
KH
KH
KT
KS
Lc
Lb
Li
@@ -60072,11 +60078,11 @@ Ic
Ep
Ep
Mh
Mj
Ml
Mn
Mp
Mr
Mh
Mh
Mh
Mh
Mh
Iv
Iv
Iv
@@ -61614,11 +61620,11 @@ If
Gq
Ep
Mi
Mk
Mm
Mo
Mq
Ms
Mi
Mi
Mi
Mi
Mi
Iv
Iv
Iv
+1 -1
View File
@@ -1413,7 +1413,7 @@
name = "dust"
},
/obj/structure/frame/computer,
/obj/item/stock_parts/console_screen,
/obj/item/stack/sheet/glass,
/turf/open/floor/plasteel/whitepurple/side{
dir = 8
},
-1
View File
@@ -1134,7 +1134,6 @@
desc = "A thin layer of dust coating the floor.";
name = "dust"
},
/obj/item/device/mass_spectrometer,
/turf/open/floor/mineral/titanium,
/area/shuttle/abandoned)
"bW" = (
+12 -8
View File
@@ -127,15 +127,19 @@
#define TOXINLOVER 24
#define DIGITIGRADE 25 //Uses weird leg sprites. Optional for Lizards, required for ashwalkers. Don't give it to other races unless you make sprites for this (see human_parts_greyscale.dmi)
#define NO_UNDERWEAR 26
#define MUTCOLORS2 27
#define MUTCOLORS3 28
#define NOLIVER 29
#define NOSTOMACH 30
#define NOLIVER 27
#define NOSTOMACH 28
#define NO_DNA_COPY 29
#define DRINKSBLOOD 30
#define SPECIES_ORGANIC 31
#define SPECIES_INORGANIC 32
#define SPECIES_UNDEAD 33
#define SPECIES_ROBOTIC 34
//citadel code
#define NOAROUSAL 29 //Stops all arousal effects
#define NOGENITALS 30 //Cannot create, use, or otherwise have genitals
#define NO_DNA_COPY 31
#define DRINKSBLOOD 32
#define MUTCOLORS2 35
#define MUTCOLORS3 36
#define NOAROUSAL 37 //Stops all arousal effects
#define NOGENITALS 38 //Cannot create, use, or otherwise have genitals
#define ORGAN_SLOT_BRAIN "brain"
#define ORGAN_SLOT_APPENDIX "appendix"
+26 -20
View File
@@ -16,31 +16,37 @@
#define DIAG_BOT_HUD "12"// Bot HUDs
#define DIAG_TRACK_HUD "13"// Mech tracking beacon
#define DIAG_AIRLOCK_HUD "14"//Airlock shock overlay
#define DIAG_PATH_HUD "15"//Bot path indicators
//for antag huds. these are used at the /mob level
#define ANTAG_HUD "15"
#define ANTAG_HUD "16"
//by default everything in the hud_list of an atom is an image
//a value in hud_list with one of these will change that behavior
#define HUD_LIST_LIST 1
//data HUD (medhud, sechud) defines
//Don't forget to update human/New() if you change these!
#define DATA_HUD_SECURITY_BASIC 1
#define DATA_HUD_SECURITY_ADVANCED 2
#define DATA_HUD_MEDICAL_BASIC 3
#define DATA_HUD_MEDICAL_ADVANCED 4
#define DATA_HUD_DIAGNOSTIC 5
#define DATA_HUD_SECURITY_BASIC 1
#define DATA_HUD_SECURITY_ADVANCED 2
#define DATA_HUD_MEDICAL_BASIC 3
#define DATA_HUD_MEDICAL_ADVANCED 4
#define DATA_HUD_DIAGNOSTIC_BASIC 5
#define DATA_HUD_DIAGNOSTIC_ADVANCED 6
//antag HUD defines
#define ANTAG_HUD_CULT 6
#define ANTAG_HUD_REV 7
#define ANTAG_HUD_OPS 8
#define ANTAG_HUD_WIZ 9
#define ANTAG_HUD_SHADOW 10
#define ANTAG_HUD_TRAITOR 11
#define ANTAG_HUD_NINJA 12
#define ANTAG_HUD_CHANGELING 13
#define ANTAG_HUD_ABDUCTOR 14
#define ANTAG_HUD_DEVIL 15
#define ANTAG_HUD_SINTOUCHED 16
#define ANTAG_HUD_SOULLESS 17
#define ANTAG_HUD_CLOCKWORK 18
#define ANTAG_HUD_BROTHER 19
#define ANTAG_HUD_CULT 7
#define ANTAG_HUD_REV 8
#define ANTAG_HUD_OPS 9
#define ANTAG_HUD_WIZ 10
#define ANTAG_HUD_SHADOW 11
#define ANTAG_HUD_TRAITOR 12
#define ANTAG_HUD_NINJA 13
#define ANTAG_HUD_CHANGELING 14
#define ANTAG_HUD_ABDUCTOR 15
#define ANTAG_HUD_DEVIL 16
#define ANTAG_HUD_SINTOUCHED 17
#define ANTAG_HUD_SOULLESS 18
#define ANTAG_HUD_CLOCKWORK 19
#define ANTAG_HUD_BROTHER 20
// Notification action types
#define NOTIFY_JUMP "jump"
+14 -1
View File
@@ -10,6 +10,11 @@
#define COMPONENT_DUPE_ALLOWED 1 //duplicates allowed
#define COMPONENT_DUPE_UNIQUE 2 //new component is deleted
// Signal return value flags
// The other defines are under the signal they're used in
#define COMPONENT_ACTIVATED 1 // call parent.ComponentActivated(comp) and component.AfterComponentActivated()
// All signals. Format:
// When the signal is called: (signal arguments)
@@ -19,9 +24,11 @@
#define COMSIG_PARENT_QDELETED "parent_qdeleted" //before a datum's Destroy() is called: ()
#define COMSIG_COMPONENT_CLEAN_ACT "clean_act" //called on an object to clean it of cleanables. Usualy with soap: (num/strength)
#define COMSIG_COMPONENT_NTNET_RECIEVE "ntnet_recieve" //called on an object by its NTNET connection component on recieve. (sending_id(number), sending_netname(text), data(datum/netdata))
// /atom signals
#define COMSIG_PARENT_ATTACKBY "atom_attackby" //from base of atom/attackby(): (/obj/item, /mob/living, params)
#define COMPONENT_NO_AFTERATTACK 2 //Return this in response if you don't want afterattack to be called
#define COMSIG_ATOM_HULK_ATTACK "hulk_attack" //from base of atom/attack_hulk(): (/mob/living/carbon/human)
#define COMSIG_PARENT_EXAMINE "atom_examine" //from base of atom/examine(): (/mob)
#define COMSIG_ATOM_ENTERED "atom_entered" //from base of atom/Entered(): (/atom/movable, /atom)
@@ -52,6 +59,8 @@
#define COMSIG_MOVABLE_CROSSED "movable_crossed" //from base of atom/movable/Crossed(): (/atom/movable)
#define COMSIG_MOVABLE_COLLIDE "movable_collide" //from base of atom/movable/Collide(): (/atom)
#define COMSIG_MOVABLE_IMPACT "movable_impact" //from base of atom/movable/throw_impact(): (/atom, throwingdatum)
#define COMSIG_MOVABLE_BUCKLE "buckle" //from base of atom/movable/buckle_mob(): (mob, force)
#define COMSIG_MOVABLE_UNBUCKLE "unbuckle" //from base of atom/movable/unbuckle_mob(): (mob, force)
// /obj/item signals
#define COMSIG_ITEM_ATTACK "item_attack" //from base of obj/item/attack(): (/mob/living/target, /mob/living/user)
@@ -71,4 +80,8 @@
// /obj/machinery signals
#define COMSIG_MACHINE_PROCESS "machine_process" //from machinery subsystem fire(): ()
#define COMSIG_MACHINE_PROCESS_ATMOS "machine_process_atmos" //from air subsystem process_atmos_machinery(): ()
#define COMSIG_MACHINE_PROCESS_ATMOS "machine_process_atmos" //from air subsystem process_atmos_machinery(): ()
// /mob/living/carbon/human signals
#define COMSIG_HUMAN_MELEE_UNARMED_ATTACK "human_melee_unarmed_attack" //from mob/living/carbon/human/UnarmedAttack(): (atom/target)
#define COMSIG_HUMAN_MELEE_UNARMED_ATTACKBY "human_melee_unarmed_attackby" //from mob/living/carbon/human/UnarmedAttack(): (mob/living/carbon/human/attacker)
+2
View File
@@ -20,6 +20,7 @@
#define IC_FORMAT_BOOLEAN "\<BOOL\>"
#define IC_FORMAT_REF "\<REF\>"
#define IC_FORMAT_LIST "\<LIST\>"
#define IC_FORMAT_INDEX "\<INDEX\>"
#define IC_FORMAT_PULSE "\<PULSE\>"
@@ -33,6 +34,7 @@
#define IC_PINTYPE_BOOLEAN /datum/integrated_io/boolean
#define IC_PINTYPE_REF /datum/integrated_io/ref
#define IC_PINTYPE_LIST /datum/integrated_io/lists
#define IC_PINTYPE_INDEX /datum/integrated_io/index
#define IC_PINTYPE_PULSE_IN /datum/integrated_io/activate
#define IC_PINTYPE_PULSE_OUT /datum/integrated_io/activate/out
+5
View File
@@ -59,6 +59,9 @@
#define iszombie(A) (is_species(A, /datum/species/zombie))
#define ishumanbasic(A) (is_species(A, /datum/species/human))
//why arent catpeople a subspecies
#define iscatperson(A) (ishumanbasic(A) && ( A.dna.features["ears"] == "Cat" || A.dna.features["human_tail"] == "Cat") )
//more carbon mobs
#define ismonkey(A) (istype(A, /mob/living/carbon/monkey))
@@ -186,3 +189,5 @@ GLOBAL_LIST_INIT(glass_sheet_types, typecacheof(list(
/obj/item/stack/sheet/plasmarglass)))
#define is_glass_sheet(O) (is_type_in_typecache(O, GLOB.glass_sheet_types))
#define isblobmonster(O) (istype(O, /mob/living/simple_animal/hostile/blob))
+1 -1
View File
@@ -48,4 +48,4 @@ Last space-z level = empty
#define ZLEVEL_SPACEMIN 3
#define ZLEVEL_SPACEMAX 13
#define SPACERUIN_MAP_EDGE_PAD 15
#define SPACERUIN_MAP_EDGE_PAD 15
+12
View File
@@ -4,6 +4,11 @@
// #define EAST 4
// #define WEST 8
#define TEXT_NORTH "[NORTH]"
#define TEXT_SOUTH "[SOUTH]"
#define TEXT_EAST "[EAST]"
#define TEXT_WEST "[WEST]"
//These get to go at the top, because they're special
//You can use these defines to get the typepath of the currently running proc/verb (yes procs + verbs are objects)
/* eg:
@@ -478,3 +483,10 @@ GLOBAL_LIST_INIT(ghost_others_options, list(GHOST_OTHERS_SIMPLE, GHOST_OTHERS_DE
#define SYRINGE_DRAW 0
#define SYRINGE_INJECT 1
//gold slime core spawning
#define NO_SPAWN 0
#define HOSTILE_SPAWN 1
#define FRIENDLY_SPAWN 2
#define RIDING_OFFSET_ALL "ALL"
+1
View File
@@ -0,0 +1 @@
#define HID_RESTRICTED_END 101 //the first nonrestricted ID, automatically assigned on connection creation.
+6 -1
View File
@@ -11,4 +11,9 @@
#define INGEST 2 //ingestion
#define VAPOR 3 //foam, spray, blob attack
#define PATCH 4 //patches
#define INJECT 5 //injection
#define INJECT 5 //injection
//defines passed through to the on_reagent_change proc
#define DEL_REAGENT 1 //reagent deleted (fully cleared)
#define ADD_REAGENT 2 // reagent added
#define REM_REAGENT 3 // reagent removed (may still exist)
+7 -1
View File
@@ -49,4 +49,10 @@
#define LOGCHAT "chat"
#define LOGASAY "adminsay"
#define LOGCOMMENT "comment"
#define LOGOOC "ooc"
#define LOGOOC "ooc"
#define LINGHIVE_NONE 0
#define LINGHIVE_OUTSIDER 1
#define LINGHIVE_LING 2
#define LINGHIVE_LINK 3
+5 -4
View File
@@ -51,9 +51,10 @@
#define INIT_ORDER_TICKER 13
#define INIT_ORDER_MAPPING 12
#define INIT_ORDER_ATOMS 11
#define INIT_ORDER_LANGUAGE 10
#define INIT_ORDER_MACHINES 9
#define INIT_ORDER_CIRCUIT 8
#define INIT_ORDER_NETWORKS 10
#define INIT_ORDER_LANGUAGE 9
#define INIT_ORDER_MACHINES 8
#define INIT_ORDER_CIRCUIT 7
#define INIT_ORDER_TIMER 1
#define INIT_ORDER_DEFAULT 0
#define INIT_ORDER_AIR -1
@@ -100,4 +101,4 @@
A.overlays.Cut();\
}\
A.flags_1 &= ~OVERLAY_QUEUED_1;\
}
}
+3 -1
View File
@@ -225,7 +225,8 @@
else // A variation of get_hear inlined here to take advantage of the compiler's fastpath for obj/mob in view
var/lum = T.luminosity
T.luminosity = 6 // This is the maximum luminosity
processing_list = viewers(R, T)
for(var/mob/M in view(R, T))
processing_list += M
for(var/obj/O in view(R, T))
processing_list += O
T.luminosity = lum
@@ -311,6 +312,7 @@
else
return get_step(start, EAST)
/proc/try_move_adjacent(atom/movable/AM)
var/turf/T = get_turf(AM)
for(var/direction in GLOB.cardinals)
+1 -1
View File
@@ -949,7 +949,7 @@ GLOBAL_LIST_EMPTY(friendly_animal_types)
if(!GLOB.friendly_animal_types.len)
for(var/T in typesof(/mob/living/simple_animal))
var/mob/living/simple_animal/SA = T
if(initial(SA.gold_core_spawnable) == 2)
if(initial(SA.gold_core_spawnable) == FRIENDLY_SPAWN)
GLOB.friendly_animal_types += SA
+21
View File
@@ -232,3 +232,24 @@ GLOBAL_LIST_INIT(sqrtTable, list(1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4,
if(round(num) != num)
return round(num--)
else return num
//proc/get_overlap()
// Returns a list where [1] is all x values and [2] is all y values that overlap between the given pair of rectangles
/proc/get_overlap(x1, y1, x2, y2, x3, y3, x4, y4)
var/list/region_x1 = list()
var/list/region_y1 = list()
var/list/region_x2 = list()
var/list/region_y2 = list()
// These loops create loops filled with x/y values that the boundaries inhabit
// ex: list(5, 6, 7, 8, 9)
for(var/i in min(x1, x2) to max(x1, x2))
region_x1["[i]"] = TRUE
for(var/i in min(y1, y2) to max(y1, y2))
region_y1["[i]"] = TRUE
for(var/i in min(x3, x4) to max(x3, x4))
region_x2["[i]"] = TRUE
for(var/i in min(y3, y4) to max(y3, y4))
region_y2["[i]"] = TRUE
return list(region_x1 & region_x2, region_y1 & region_y2)
+3 -3
View File
@@ -35,7 +35,7 @@
#define MAX_CHARTER_LEN 80
//MINOR TWEAKS/MISC
#define AGE_MIN 17 //youngest a character can be
#define AGE_MIN 18 //youngest a character can be
#define AGE_MAX 85 //oldest a character can be
#define WIZARD_AGE_MIN 30 //youngest a wizard can be
#define APPRENTICE_AGE_MIN 29 //youngest an apprentice can be
@@ -79,5 +79,5 @@
//Update this whenever the db schema changes
//make sure you add an update to the schema_version stable in the db changelog
#define DB_MAJOR_VERSION 3
#define DB_MINOR_VERSION 4
#define DB_MAJOR_VERSION 4
#define DB_MINOR_VERSION 0
+1
View File
@@ -24,6 +24,7 @@ GLOBAL_LIST_EMPTY(landmarks_list) //list of all landmarks created
GLOBAL_LIST_EMPTY(start_landmarks_list) //list of all spawn points created
GLOBAL_LIST_EMPTY(department_security_spawns) //list of all department security spawns
GLOBAL_LIST_EMPTY(generic_event_spawns) //list of all spawns for events
GLOBAL_LIST_EMPTY(jobspawn_overrides) //These will take precedence over normal spawnpoints if created.
GLOBAL_LIST_EMPTY(wizardstart)
GLOBAL_LIST_EMPTY(nukeop_start)
+1
View File
@@ -24,6 +24,7 @@ GLOBAL_LIST_EMPTY(pai_list)
GLOBAL_LIST_EMPTY(available_ai_shells)
GLOBAL_LIST_INIT(simple_animals, list(list(),list(),list())) // One for each AI_* status define
GLOBAL_LIST_EMPTY(spidermobs) //all sentient spider mobs
GLOBAL_LIST_EMPTY(bots_list)
GLOBAL_LIST_EMPTY(language_datum_instances)
GLOBAL_LIST_EMPTY(all_languages)
+3 -1
View File
@@ -34,7 +34,9 @@
// No comment
/atom/proc/attackby(obj/item/W, mob/user, params)
return SendSignal(COMSIG_PARENT_ATTACKBY, W, user, params)
if(SendSignal(COMSIG_PARENT_ATTACKBY, W, user, params) & COMPONENT_NO_AFTERATTACK)
return TRUE
return FALSE
/obj/attackby(obj/item/I, mob/living/user, params)
return ..() || (can_be_hit && I.attack_obj(src, user))
+2
View File
@@ -25,7 +25,9 @@
if(override)
return
SendSignal(COMSIG_HUMAN_MELEE_UNARMED_ATTACK, A)
A.attack_hand(src)
SendSignal(COMSIG_HUMAN_MELEE_UNARMED_ATTACKBY, src)
/atom/proc/attack_hand(mob/user)
return
+3 -3
View File
@@ -121,7 +121,7 @@ GLOBAL_VAR_INIT(miscreants_allowed, FALSE)
prefs.chat_toggles ^= CHAT_LOOC
prefs.save_preferences()
src << "You will [(prefs.chat_toggles & CHAT_LOOC) ? "now" : "no longer"] see messages on the LOOC channel."
SSblackbox.add_details("admin_verb","TLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
SSblackbox.record_feedback("tally", "admin_verb", 1, "TLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/togglelooc()
set category = "Server"
@@ -130,7 +130,7 @@ GLOBAL_VAR_INIT(miscreants_allowed, FALSE)
toggle_looc()
log_admin("[key_name(usr)] toggled LOOC.")
message_admins("[key_name_admin(usr)] toggled LOOC.")
SSblackbox.add_details("admin_verb","TLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
SSblackbox.record_feedback("tally", "admin_verb", 1, "TLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/proc/toggle_looc(toggle = null)
if(toggle != null) //if we're specifically en/disabling ooc
@@ -150,7 +150,7 @@ GLOBAL_VAR_INIT(miscreants_allowed, FALSE)
log_admin("[key_name(usr)] toggled Dead LOOC.")
message_admins("[key_name_admin(usr)] toggled Dead LOOC.")
SSblackbox.add_details("admin_verb","TDLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
SSblackbox.record_feedback("tally", "admin_verb", 1, "TDLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/mob/living/carbon/proc/has_penis()
+11 -10
View File
@@ -215,7 +215,7 @@
damage = 15
armour_penetration = 10
light_range = 2
speed = 1.2
speed = 0.6
range = 25
light_color = LIGHT_COLOR_RED
@@ -224,10 +224,10 @@
icon_state = "magjectile-nl"
damage = 1
knockdown = 0
stamina = 30
stamina = 25
armour_penetration = -10
light_range = 2
speed = 1.2
speed = 0.7
range = 25
light_color = LIGHT_COLOR_BLUE
@@ -257,7 +257,7 @@
origin_tech = "magnets=5"
ammo_type = /obj/item/ammo_casing/caseless/anlmags
caliber = "mags"
max_ammo = 7
max_ammo = 15
multiple_sprites = 2
/obj/item/ammo_box/magazine/mmag/small/lethal
@@ -279,7 +279,7 @@
mag_type = /obj/item/ammo_box/magazine/mmag/small
can_suppress = 0
casing_ejector = 0
fire_delay = 5
fire_delay = 2
origin_tech = "combat=4;magnets=4"
/obj/item/gun/ballistic/automatic/pistol/mag/update_icon()
@@ -389,7 +389,7 @@
damage = 20
armour_penetration = 25
light_range = 3
speed = 1.2
speed = 0.7
range = 35
light_color = LIGHT_COLOR_RED
@@ -398,10 +398,10 @@
icon_state = "magjectile-large-nl"
damage = 0
knockdown = 0
stamina = 35
stamina = 20
armour_penetration = -10
light_range = 3
speed = 1.0
speed = 0.65
range = 35
light_color = LIGHT_COLOR_BLUE
@@ -430,7 +430,7 @@
origin_tech = "magnets=6"
ammo_type = /obj/item/ammo_casing/caseless/anlmagm
caliber = "magm"
max_ammo = 15
max_ammo = 27
multiple_sprites = 2
/obj/item/ammo_box/magazine/mmag/lethal
@@ -439,6 +439,7 @@
icon_state = "mediummagmag"
origin_tech = "combat=6"
ammo_type = /obj/item/ammo_casing/caseless/amagm
max_ammo = 21
///the gun itself///
@@ -455,7 +456,7 @@
can_suppress = 0
burst_size = 3
fire_delay = 2
spread = 30
spread = 20
recoil = 1
casing_ejector = 0
@@ -111,6 +111,16 @@
resistance_flags = FLAMMABLE
squeak_override = list('sound/misc/server-ready.ogg'= 1)
/obj/item/clothing/neck/cloak/festive
name = "Celebratory Cloak of Morozko"
desc = " It probably will protect from snow, charcoal or elves."
icon = 'icons/obj/custom.dmi'
icon_state = "festive-w"
item_state = "festive-i"
icon = 'icons/obj/custom.dmi'
w_class = WEIGHT_CLASS_SMALL
body_parts_covered = CHEST|GROIN|LEGS|ARMS
/*Zigfie*/
@@ -213,4 +223,15 @@
item_state = "pink-w"
/*Fractious*/
/obj/item/clothing/suit/vermillion
name = "vermillion clothing"
desc = "Some clothing."
icon_state = "vermillion-w"
item_state = "vermillion-i"
body_parts_covered = CHEST|GROIN|LEGS|ARMS|HANDS
icon = 'icons/obj/custom.dmi'
Binary file not shown.

Before

Width:  |  Height:  |  Size: 768 B

After

Width:  |  Height:  |  Size: 668 B

+2 -2
View File
@@ -45,9 +45,9 @@ INITIALIZE_IMMEDIATE(/obj/effect/statclick)
switch(controller)
if("Master")
Recreate_MC()
SSblackbox.add_details("admin_verb","Restart Master Controller")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Restart Master Controller")
if("Failsafe")
new /datum/controller/failsafe()
SSblackbox.add_details("admin_verb","Restart Failsafe Controller")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Restart Failsafe Controller")
message_admins("Admin [key_name_admin(usr)] has restarted the [controller] controller.")
@@ -381,3 +381,7 @@ CONFIG_TWEAK(flag/ValidateAndSet(str_val))
. = ..()
if(. && Master.current_runlevel)
world.sleep_offline = !value
CONFIG_DEF(number/rounds_until_hard_restart)
value = -1
min_val = 0
+164 -166
View File
@@ -5,21 +5,12 @@ SUBSYSTEM_DEF(blackbox)
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
init_order = INIT_ORDER_BLACKBOX
var/list/msg_common = list()
var/list/msg_science = list()
var/list/msg_command = list()
var/list/msg_medical = list()
var/list/msg_engineering = list()
var/list/msg_security = list()
var/list/msg_deathsquad = list()
var/list/msg_syndicate = list()
var/list/msg_service = list()
var/list/msg_cargo = list()
var/list/msg_other = list()
var/list/feedback = list() //list of datum/feedback_variable
var/triggertime = 0
var/sealed = FALSE //time to stop tracking stats?
var/list/research_levels = list() //list of highest tech levels attained that isn't lost lost by destruction of RD computers
var/list/versions = list("time_dilation_current" = 2,
"science_techweb_unlock" = 2) //associative list of any feedback variables that have had their format changed since creation and their current version, remember to update this
/datum/controller/subsystem/blackbox/Initialize()
@@ -44,20 +35,7 @@ SUBSYSTEM_DEF(blackbox)
/datum/controller/subsystem/blackbox/Recover()
msg_common = SSblackbox.msg_common
msg_science = SSblackbox.msg_science
msg_command = SSblackbox.msg_command
msg_medical = SSblackbox.msg_medical
msg_engineering = SSblackbox.msg_engineering
msg_security = SSblackbox.msg_security
msg_deathsquad = SSblackbox.msg_deathsquad
msg_syndicate = SSblackbox.msg_syndicate
msg_service = SSblackbox.msg_service
msg_cargo = SSblackbox.msg_cargo
msg_other = SSblackbox.msg_other
feedback = SSblackbox.feedback
sealed = SSblackbox.sealed
//no touchie
@@ -71,32 +49,14 @@ SUBSYSTEM_DEF(blackbox)
/datum/controller/subsystem/blackbox/Shutdown()
sealed = FALSE
set_val("ahelp_unresolved", GLOB.ahelp_tickets.active_tickets.len)
var/pda_msg_amt = 0
var/rc_msg_amt = 0
record_feedback("tally", "ahelp_stats", GLOB.ahelp_tickets.active_tickets.len, "unresolved")
for (var/obj/machinery/message_server/MS in GLOB.message_servers)
if (MS.pda_msgs.len > pda_msg_amt)
pda_msg_amt = MS.pda_msgs.len
if (MS.rc_msgs.len > rc_msg_amt)
rc_msg_amt = MS.rc_msgs.len
set_details("radio_usage","")
add_details("radio_usage","COM-[msg_common.len]")
add_details("radio_usage","SCI-[msg_science.len]")
add_details("radio_usage","HEA-[msg_command.len]")
add_details("radio_usage","MED-[msg_medical.len]")
add_details("radio_usage","ENG-[msg_engineering.len]")
add_details("radio_usage","SEC-[msg_security.len]")
add_details("radio_usage","DTH-[msg_deathsquad.len]")
add_details("radio_usage","SYN-[msg_syndicate.len]")
add_details("radio_usage","SRV-[msg_service.len]")
add_details("radio_usage","CAR-[msg_cargo.len]")
add_details("radio_usage","OTH-[msg_other.len]")
add_details("radio_usage","PDA-[pda_msg_amt]")
add_details("radio_usage","RC-[rc_msg_amt]")
if (MS.pda_msgs.len)
record_feedback("tally", "radio_usage", MS.pda_msgs.len, "PDA")
if (MS.rc_msgs.len)
record_feedback("tally", "radio_usage", MS.rc_msgs.len, "request console")
if(research_levels.len)
SSblackbox.record_feedback("associative", "high_research_level", 1, research_levels)
if (!SSdbcore.Connect())
return
@@ -104,79 +64,183 @@ SUBSYSTEM_DEF(blackbox)
var/list/sqlrowlist = list()
for (var/datum/feedback_variable/FV in feedback)
sqlrowlist += list(list("time" = "Now()", "round_id" = GLOB.round_id, "var_name" = "'[sanitizeSQL(FV.get_variable())]'", "var_value" = FV.get_value(), "details" = "'[sanitizeSQL(FV.get_details())]'"))
var/sqlversion = 1
if(FV.key in versions)
sqlversion = versions[FV.key]
sqlrowlist += list(list("datetime" = "Now()", "round_id" = GLOB.round_id, "key_name" = "'[sanitizeSQL(FV.key)]'", "key_type" = "'[FV.key_type]'", "version" = "[sqlversion]", "json" = "'[sanitizeSQL(json_encode(FV.json))]'"))
if (!length(sqlrowlist))
return
SSdbcore.MassInsert(format_table_name("feedback"), sqlrowlist, ignore_errors = TRUE, delayed = TRUE)
/datum/controller/subsystem/blackbox/proc/Seal()
if(sealed)
return
if(IsAdminAdvancedProcCall())
message_admins("[key_name_admin(usr)] sealed the blackbox!")
log_game("Blackbox sealed[IsAdminAdvancedProcCall() ? " by [key_name(usr)]" : ""].")
sealed = TRUE
/datum/controller/subsystem/blackbox/proc/LogBroadcast(blackbox_msg, freq)
/datum/controller/subsystem/blackbox/proc/log_research(tech, level)
if(!(tech in research_levels) || research_levels[tech] < level)
research_levels[tech] = level
/datum/controller/subsystem/blackbox/proc/LogBroadcast(freq)
if(sealed)
return
switch(freq)
if(1459)
msg_common += blackbox_msg
if(1351)
msg_science += blackbox_msg
if(1353)
msg_command += blackbox_msg
if(1355)
msg_medical += blackbox_msg
if(1357)
msg_engineering += blackbox_msg
if(1359)
msg_security += blackbox_msg
if(1441)
msg_deathsquad += blackbox_msg
if(1213)
msg_syndicate += blackbox_msg
if(1349)
msg_service += blackbox_msg
if(1347)
msg_cargo += blackbox_msg
record_feedback("tally", "radio_usage", 1, "common")
if(GLOB.SCI_FREQ)
record_feedback("tally", "radio_usage", 1, "science")
if(GLOB.COMM_FREQ)
record_feedback("tally", "radio_usage", 1, "command")
if(GLOB.MED_FREQ)
record_feedback("tally", "radio_usage", 1, "medical")
if(GLOB.ENG_FREQ)
record_feedback("tally", "radio_usage", 1, "engineering")
if(GLOB.SEC_FREQ)
record_feedback("tally", "radio_usage", 1, "security")
if(GLOB.SYND_FREQ)
record_feedback("tally", "radio_usage", 1, "syndicate")
if(GLOB.SERV_FREQ)
record_feedback("tally", "radio_usage", 1, "service")
if(GLOB.SUPP_FREQ)
record_feedback("tally", "radio_usage", 1, "supply")
if(GLOB.CENTCOM_FREQ)
record_feedback("tally", "radio_usage", 1, "centcom")
if(GLOB.AIPRIV_FREQ)
record_feedback("tally", "radio_usage", 1, "ai private")
if(GLOB.REDTEAM_FREQ)
record_feedback("tally", "radio_usage", 1, "CTF red team")
if(GLOB.BLUETEAM_FREQ)
record_feedback("tally", "radio_usage", 1, "CTF blue team")
else
msg_other += blackbox_msg
record_feedback("tally", "radio_usage", 1, "other")
/datum/controller/subsystem/blackbox/proc/find_feedback_datum(variable)
/datum/controller/subsystem/blackbox/proc/find_feedback_datum(key, key_type)
for(var/datum/feedback_variable/FV in feedback)
if(FV.get_variable() == variable)
if(FV.key == key)
return FV
var/datum/feedback_variable/FV = new(variable)
var/datum/feedback_variable/FV = new(key, key_type)
feedback += FV
return FV
/*
feedback data can be recorded in 5 formats:
"text"
used for simple single-string records i.e. the current map
further calls to the same key will append saved data unless the overwrite argument is true or it already exists
when encoded calls made with overwrite will lack square brackets
calls: SSblackbox.record_feedback("text", "example", 1, "sample text")
SSblackbox.record_feedback("text", "example", 1, "other text")
json: {"data":["sample text","other text"]}
"amount"
used to record simple counts of data i.e. the number of ahelps recieved
further calls to the same key will add or subtract (if increment argument is a negative) from the saved amount
calls: SSblackbox.record_feedback("amount", "example", 8)
SSblackbox.record_feedback("amount", "example", 2)
json: {"data":10}
"tally"
used to track the number of occurances of multiple related values i.e. how many times each type of gun is fired
further calls to the same key will:
add or subtract from the saved value of the data key if it already exists
append the key and it's value if it doesn't exist
calls: SSblackbox.record_feedback("tally", "example", 1, "sample data")
SSblackbox.record_feedback("tally", "example", 4, "sample data")
SSblackbox.record_feedback("tally", "example", 2, "other data")
json: {"data":{"sample data":5,"other data":2}}
"nested tally"
used to track the number of occurances of structured semi-relational values i.e. the results of arcade machines
similar to running total, but related values are nested in a multi-dimensional array built
the final element in the data list is used as the tracking key, all prior elements are used for nesting
all data list elements must be strings
further calls to the same key will:
add or subtract from the saved value of the data key if it already exists in the same multi-dimensional position
append the key and it's value if it doesn't exist
calls: SSblackbox.record_feedback("nested tally", "example", 1, list("fruit", "orange", "apricot"))
SSblackbox.record_feedback("nested tally", "example", 2, list("fruit", "orange", "orange"))
SSblackbox.record_feedback("nested tally", "example", 3, list("fruit", "orange", "apricot"))
SSblackbox.record_feedback("nested tally", "example", 10, list("fruit", "red", "apple"))
SSblackbox.record_feedback("nested tally", "example", 1, list("vegetable", "orange", "carrot"))
json: {"data":{"fruit":{"orange":{"apricot":4,"orange":2},"red":{"apple":10}},"vegetable":{"orange":{"carrot":1}}}}
tracking values associated with a number can't merge with a nesting value, trying to do so will append the list
call: SSblackbox.record_feedback("nested tally", "example", 3, list("fruit", "orange"))
json: {"data":{"fruit":{"orange":{"apricot":4,"orange":2},"red":{"apple":10},"orange":3},"vegetable":{"orange":{"carrot":1}}}}
"associative"
used to record text that's associated with a value i.e. coordinates
further calls to the same key will append a new list to existing data
calls: SSblackbox.record_feedback("associative", "example", 1, list("text" = "example", "path" = /obj/item, "number" = 4))
SSblackbox.record_feedback("associative", "example", 1, list("number" = 7, "text" = "example", "other text" = "sample"))
json: {"data":{"1":{"text":"example","path":"/obj/item","number":"4"},"2":{"number":"7","text":"example","other text":"sample"}}}
/datum/controller/subsystem/blackbox/proc/set_val(variable, value)
if(sealed)
Versioning
If the format of a feedback variable is ever changed, i.e. how many levels of nesting are used or a new type of data is added to it, add it to the versions list
When feedback is being saved if a key is in the versions list the value specified there will be used, otherwise all keys are assumed to be version = 1
versions is an associative list, remember to use the same string in it as defined on a feedback variable, example:
list/versions = list("round_end_stats" = 4,
"admin_toggle" = 2,
"gun_fired" = 2)
*/
/datum/controller/subsystem/blackbox/proc/record_feedback(key_type, key, increment, data, overwrite)
if(sealed || !key_type || !istext(key) || !isnum(increment || !data))
return
var/datum/feedback_variable/FV = find_feedback_datum(variable)
FV.set_value(value)
var/datum/feedback_variable/FV = find_feedback_datum(key, key_type)
switch(key_type)
if("text")
if(!istext(data))
return
if(!islist(FV.json["data"]))
FV.json["data"] = list()
if(overwrite)
FV.json["data"] = data
else
FV.json["data"] |= data
if("amount")
FV.json["data"] += increment
if("tally")
if(!islist(FV.json["data"]))
FV.json["data"] = list()
FV.json["data"]["[data]"] += increment
if("nested tally")
if(!islist(data))
return
if(!islist(FV.json["data"]))
FV.json["data"] = list()
FV.json["data"] = record_feedback_recurse_list(FV.json["data"], data, increment)
if("associative")
if(!islist(data))
return
if(!islist(FV.json["data"]))
FV.json["data"] = list()
var/pos = length(FV.json["data"]) + 1
FV.json["data"]["[pos]"] = list() //in 512 "pos" can be replaced with "[FV.json["data"].len+1]"
for(var/i in data)
FV.json["data"]["[pos]"]["[i]"] = "[data[i]]" //and here with "[FV.json["data"].len]"
/datum/controller/subsystem/blackbox/proc/inc(variable, value)
if(sealed)
return
var/datum/feedback_variable/FV = find_feedback_datum(variable)
FV.inc(value)
/datum/controller/subsystem/blackbox/proc/record_feedback_recurse_list(list/L, list/key_list, increment, depth = 1)
if(depth == key_list.len)
if(L.Find(key_list[depth]))
L["[key_list[depth]]"] += increment
else
var/list/LFI = list(key_list[depth] = increment)
L += LFI
else
if(!L.Find(key_list[depth]))
var/list/LGD = list(key_list[depth] = list())
L += LGD
L["[key_list[depth-1]]"] = .(L["[key_list[depth]]"], key_list, increment, ++depth)
return L
/datum/controller/subsystem/blackbox/proc/dec(variable,value)
if(sealed)
return
var/datum/feedback_variable/FV = find_feedback_datum(variable)
FV.dec(value)
/datum/feedback_variable
var/key
var/key_type
var/list/json = list()
/datum/controller/subsystem/blackbox/proc/set_details(variable,details)
if(sealed)
return
var/datum/feedback_variable/FV = find_feedback_datum(variable)
FV.set_details(details)
/datum/controller/subsystem/blackbox/proc/add_details(variable,details)
if(sealed)
return
var/datum/feedback_variable/FV = find_feedback_datum(variable)
FV.add_details(details)
/datum/feedback_variable/New(new_key, new_key_type)
key = new_key
key_type = new_key_type
/datum/controller/subsystem/blackbox/proc/ReportDeath(mob/living/L)
if(sealed)
@@ -208,69 +272,3 @@ SUBSYSTEM_DEF(blackbox)
var/map = sanitizeSQL(SSmapping.config.map_name)
var/datum/DBQuery/query_report_death = SSdbcore.NewQuery("INSERT INTO [format_table_name("death")] (pod, x_coord, y_coord, z_coord, mapname, server_ip, server_port, round_id, tod, job, special, name, byondkey, laname, lakey, bruteloss, fireloss, brainloss, oxyloss, toxloss, cloneloss, staminaloss, last_words, suicide) VALUES ('[sqlpod]', '[x_coord]', '[y_coord]', '[z_coord]', '[map]', INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', [GLOB.round_id], '[SQLtime()]', '[sqljob]', '[sqlspecial]', '[sqlname]', '[sqlkey]', '[laname]', '[lakey]', [sqlbrute], [sqlfire], [sqlbrain], [sqloxy], [sqltox], [sqlclone], [sqlstamina], '[last_words]', [suicide])")
query_report_death.Execute()
/datum/controller/subsystem/blackbox/proc/Seal()
if(sealed)
return
if(IsAdminAdvancedProcCall())
var/msg = "[key_name_admin(usr)] sealed the blackbox!"
message_admins(msg)
log_game("Blackbox sealed[IsAdminAdvancedProcCall() ? " by [key_name(usr)]" : ""].")
sealed = TRUE
//feedback variable datum, for storing all kinds of data
/datum/feedback_variable
var/variable
var/value
var/list/details
/datum/feedback_variable/New(param_variable, param_value = 0)
variable = param_variable
value = param_value
/datum/feedback_variable/proc/inc(num = 1)
if (isnum(value))
value += num
else
value = text2num(value)
if (isnum(value))
value += num
else
value = num
/datum/feedback_variable/proc/dec(num = 1)
if (isnum(value))
value -= num
else
value = text2num(value)
if (isnum(value))
value -= num
else
value = -num
/datum/feedback_variable/proc/set_value(num)
if (isnum(num))
value = num
/datum/feedback_variable/proc/get_value()
if (!isnum(value))
return 0
return value
/datum/feedback_variable/proc/get_variable()
return variable
/datum/feedback_variable/proc/set_details(deets)
details = list("\"[deets]\"")
/datum/feedback_variable/proc/add_details(deets)
if (!details)
set_details(deets)
else
details += "\"[deets]\""
/datum/feedback_variable/proc/get_details()
return details ? details.Join(" | ") : null
/datum/feedback_variable/proc/get_parsed()
return list(variable,value,details.Join(" | "))
+8 -1
View File
@@ -1,6 +1,6 @@
SUBSYSTEM_DEF(disease)
name = "Disease"
flags = SS_NO_FIRE | SS_NO_INIT
flags = SS_NO_FIRE
var/list/active_diseases = list() //List of Active disease in all mobs; purely for quick referencing.
var/list/diseases
@@ -12,6 +12,13 @@ SUBSYSTEM_DEF(disease)
if(!diseases)
diseases = subtypesof(/datum/disease)
/datum/controller/subsystem/disease/Initialize(timeofday)
var/list/all_common_diseases = diseases - typesof(/datum/disease/advance)
for(var/common_disease_type in all_common_diseases)
var/datum/disease/prototype = new common_disease_type()
archive_diseases[prototype.GetDiseaseID()] = prototype
..()
/datum/controller/subsystem/disease/stat_entry(msg)
..("P:[active_diseases.len]")
+25 -18
View File
@@ -393,6 +393,8 @@ SUBSYSTEM_DEF(job)
continue
S = sloc
break
if(length(GLOB.jobspawn_overrides[rank]))
S = pick(GLOB.jobspawn_overrides[rank])
if(S)
SendToAtom(H, S, buckle = FALSE)
if(!S) //if there isn't a spawnpoint send them to latejoin, if there's no latejoin go yell at your mapper
@@ -462,36 +464,37 @@ SUBSYSTEM_DEF(job)
/datum/controller/subsystem/job/proc/HandleFeedbackGathering()
for(var/datum/job/job in occupations)
var/tmp_str = "|[job.title]|"
var/level1 = 0 //high
var/level2 = 0 //medium
var/level3 = 0 //low
var/level4 = 0 //never
var/level5 = 0 //banned
var/level6 = 0 //account too young
var/high = 0 //high
var/medium = 0 //medium
var/low = 0 //low
var/never = 0 //never
var/banned = 0 //banned
var/young = 0 //account too young
for(var/mob/dead/new_player/player in GLOB.player_list)
if(!(player.ready == PLAYER_READY_TO_PLAY && player.mind && !player.mind.assigned_role))
continue //This player is not ready
if(jobban_isbanned(player, job.title))
level5++
banned++
continue
if(!job.player_old_enough(player.client))
level6++
young++
continue
if(job.required_playtime_remaining(player.client))
level6++
young++
continue
if(player.client.prefs.GetJobDepartment(job, 1) & job.flag)
level1++
high++
else if(player.client.prefs.GetJobDepartment(job, 2) & job.flag)
level2++
medium++
else if(player.client.prefs.GetJobDepartment(job, 3) & job.flag)
level3++
else level4++ //not selected
tmp_str += "HIGH=[level1]|MEDIUM=[level2]|LOW=[level3]|NEVER=[level4]|BANNED=[level5]|YOUNG=[level6]|-"
SSblackbox.add_details("job_preferences",tmp_str)
low++
else never++ //not selected
SSblackbox.record_feedback("nested tally", "job_preferences", high, list("[job.title]", "high"))
SSblackbox.record_feedback("nested tally", "job_preferences", medium, list("[job.title]", "medium"))
SSblackbox.record_feedback("nested tally", "job_preferences", low, list("[job.title]", "low"))
SSblackbox.record_feedback("nested tally", "job_preferences", never, list("[job.title]", "never"))
SSblackbox.record_feedback("nested tally", "job_preferences", banned, list("[job.title]", "banned"))
SSblackbox.record_feedback("nested tally", "job_preferences", young, list("[job.title]", "young"))
/datum/controller/subsystem/job/proc/PopcapReached()
var/hpc = CONFIG_GET(number/hard_popcap)
@@ -535,6 +538,10 @@ SUBSYSTEM_DEF(job)
M.forceMove(get_turf(A))
/datum/controller/subsystem/job/proc/SendToLateJoin(mob/M, buckle = TRUE)
if(M.mind && M.mind.assigned_role && length(GLOB.jobspawn_overrides[M.mind.assigned_role])) //We're doing something special today.
SendToAtom(M,pick(GLOB.jobspawn_overrides[M.mind.assigned_role]),FALSE)
return
if(latejoin_trackers.len)
SendToAtom(M, pick(latejoin_trackers), buckle)
else
+14
View File
@@ -62,6 +62,7 @@ SUBSYSTEM_DEF(mapping)
repopulate_sorted_areas()
// Set up Z-level transistions.
setup_map_transitions()
generate_station_area_list()
..()
/* Nuke threats, for making the blue tiles on the station go RED
@@ -139,6 +140,19 @@ SUBSYSTEM_DEF(mapping)
INIT_ANNOUNCE(msg)
#undef INIT_ANNOUNCE
GLOBAL_LIST_EMPTY(the_station_areas)
/datum/controller/subsystem/mapping/proc/generate_station_area_list()
var/list/station_areas_blacklist = typecacheof(list(/area/space, /area/mine, /area/ruin))
for(var/area/A in world)
var/turf/picked = safepick(get_area_turfs(A.type))
if(picked && (picked.z in GLOB.station_z_levels))
if(!(A.type in GLOB.the_station_areas) && !is_type_in_typecache(A, station_areas_blacklist))
GLOB.the_station_areas.Add(A.type)
if(!GLOB.the_station_areas.len)
log_world("ERROR: Station areas list failed to generate!")
/datum/controller/subsystem/mapping/proc/maprotate()
var/players = GLOB.clients.len
var/list/mapvotes = list()
+1 -1
View File
@@ -29,7 +29,7 @@ SUBSYSTEM_DEF(overlays)
/datum/controller/subsystem/overlays/Shutdown()
text2file("[GLOB.log_directory]/overlay.log", render_stats(stats))
text2file(render_stats(stats), "[GLOB.log_directory]/overlay.log")
/datum/controller/subsystem/overlays/Recover()
@@ -6,13 +6,11 @@ PROCESSING_SUBSYSTEM_DEF(circuit)
var/cipherkey
var/list/all_exonet_connections = list() //Address = connection datum.
var/list/obj/machinery/exonet_node/all_exonet_nodes = list()
var/list/all_components = list() // Associative list of [component_name]:[component_path] pairs
var/list/cached_components = list() // Associative list of [component_path]:[component] pairs
var/list/all_assemblies = list() // Associative list of [assembly_name]:[assembly_path] pairs
var/list/cached_assemblies = list() // Associative list of [assembly_path]:[assembly] pairs
var/list/all_circuits = list() // Associative list of [circuit_name]:[circuit_path] pairs
var/list/circuit_fabricator_recipe_list = list() // Associative list of [category_name]:[list_of_circuit_paths] pairs
var/cost_multiplier = MINERAL_MATERIAL_AMOUNT / 10 // Each circuit cost unit is 200cm3
@@ -58,22 +56,3 @@ PROCESSING_SUBSYSTEM_DEF(circuit)
/obj/item/device/integrated_electronics/debugger,
/obj/item/device/integrated_electronics/analyzer
)
/datum/controller/subsystem/processing/circuit/proc/get_exonet_node()
for(var/i in 1 to all_exonet_nodes.len)
var/obj/machinery/exonet_node/E = all_exonet_nodes[i]
if(E.is_operating())
return E
/datum/controller/subsystem/processing/circuit/proc/get_exonet_address(addr)
return all_exonet_connections[addr]
// Proc: get_atom_from_address()
// Parameters: 1 (target_address - the desired address to find)
// Description: Searches an address for the atom it is attached for, otherwise returns null.
/datum/controller/subsystem/processing/circuit/proc/get_atom_from_address(var/target_address)
var/datum/exonet_protocol/exonet = SScircuit.get_exonet_address(target_address)
if(exonet)
return exonet.holder
@@ -0,0 +1,36 @@
PROCESSING_SUBSYSTEM_DEF(networks)
name = "Networks"
priority = 80
wait = 1
stat_tag = "NET"
flags = SS_KEEP_TIMING
init_order = INIT_ORDER_NETWORKS
var/datum/ntnet/station/station_network
var/assignment_hardware_id = HID_RESTRICTED_END
var/list/networks_by_id = list() //id = network
var/list/interfaces_by_id = list() //hardware id = component interface
/datum/controller/subsystem/processing/networks/Initialize()
station_network = new
station_network.register_map_supremecy()
. = ..()
/datum/controller/subsystem/processing/networks/proc/register_network(datum/ntnet/network)
if(!networks_by_id[network.network_id])
networks_by_id[network.network_id] = network
return TRUE
return FALSE
/datum/controller/subsystem/processing/networks/proc/unregister_network(datum/ntnet/network)
networks_by_id -= network.network_id
return TRUE
/datum/controller/subsystem/processing/networks/proc/register_interface(datum/component/ntnet_interface/D)
if(!interfaces_by_id[D.hardware_id])
interfaces_by_id[D.hardware_id] = D
return TRUE
return FALSE
/datum/controller/subsystem/processing/networks/proc/unregister_interface(datum/component/ntnet_interface/D)
interfaces_by_id -= D.hardware_id
return TRUE
+1 -1
View File
@@ -13,6 +13,6 @@ PROCESSING_SUBSYSTEM_DEF(radiation)
return
warned_atoms[ref] = TRUE
var/atom/master = contamination.parent
SSblackbox.add_details("contaminated", "[master.type]")
SSblackbox.record_feedback("tally", "contaminated", 1, master.type)
var/msg = "has become contamintaed with enough radiation to contaminate other objects. || Source: [contamination.source] || Strength: [contamination.strength]"
master.investigate_log(msg, INVESTIGATE_RADIATION)
+7
View File
@@ -3,6 +3,13 @@ SUBSYSTEM_DEF(radio)
flags = SS_NO_FIRE|SS_NO_INIT
var/list/datum/radio_frequency/frequencies = list()
var/list/saymodes = list()
/datum/controller/subsystem/radio/PreInit(timeofday)
for(var/_SM in subtypesof(/datum/saymode))
var/datum/saymode/SM = new _SM()
saymodes[SM.key] = SM
return ..()
/datum/controller/subsystem/radio/proc/add_object(obj/device, new_frequency as num, filter = null as text|null)
var/f_text = num2text(new_frequency)
+29 -5
View File
@@ -167,7 +167,7 @@ SUBSYSTEM_DEF(shuttle)
var/mob/M = I
if(M.stat != DEAD)
++alive
var/total = GLOB.joined_player_list.len
if(alive / total <= threshold)
@@ -247,7 +247,7 @@ SUBSYSTEM_DEF(shuttle)
log_game("[key_name(user)] has called the shuttle.")
if(call_reason)
SSblackbox.add_details("shuttle_reason", call_reason)
SSblackbox.record_feedback("text", "shuttle_reason", 1, "[call_reason]")
log_game("Shuttle call reason: [call_reason]")
message_admins("[key_name_admin(user)] has called the shuttle. (<A HREF='?_src_=holder;[HrefToken()];trigger_centcom_recall=1'>TRIGGER CENTCOM RECALL</A>)")
@@ -622,6 +622,30 @@ SUBSYSTEM_DEF(shuttle)
return TRUE
/datum/controller/subsystem/shuttle/proc/get_containing_shuttle(atom/A)
for(var/obj/docking_port/mobile/M in mobile)
if(M.is_in_shuttle_bounds(A))
return M
var/list/mobile_cache = mobile
for(var/i in 1 to mobile_cache.len)
var/obj/docking_port/port = mobile_cache[i]
if(port.is_in_shuttle_bounds(A))
return port
/datum/controller/subsystem/shuttle/proc/get_containing_dock(atom/A)
. = list()
var/list/stationary_cache = stationary
for(var/i in 1 to stationary_cache.len)
var/obj/docking_port/port = stationary_cache[i]
if(port.is_in_shuttle_bounds(A))
. += port
/datum/controller/subsystem/shuttle/proc/get_dock_overlap(x0, y0, x1, y1, z)
. = list()
var/list/stationary_cache = stationary
for(var/i in 1 to stationary_cache.len)
var/obj/docking_port/port = stationary_cache[i]
if(!port || port.z != z)
continue
var/list/bounds = port.return_coords()
var/list/overlap = get_overlap(x0, y0, x1, y1, bounds[1], bounds[2], bounds[3], bounds[4])
var/list/xs = overlap[1]
var/list/ys = overlap[2]
if(xs.len && ys.len)
.[port] = overlap
+1 -1
View File
@@ -319,7 +319,7 @@ SUBSYSTEM_DEF(ticker)
//Cleanup some stuff
for(var/obj/effect/landmark/start/S in GLOB.landmarks_list)
//Deleting Startpoints but we need the ai point to AI-ize people later
if(S.name != "AI")
if(S.delete_after_roundstart)
qdel(S)
//assign crew objectives and generate miscreants
+1 -1
View File
@@ -35,4 +35,4 @@ SUBSYSTEM_DEF(time_track)
last_tick_realtime = current_realtime
last_tick_byond_time = current_byondtime
last_tick_tickcount = current_tickcount
SSblackbox.add_details("time_dilation_current", time_dilation_current)
SSblackbox.record_feedback("associative", "time_dilation_current", 1, list("[time_dilation_current]" = "[SQLtime()]"))
+3
View File
@@ -357,6 +357,9 @@ SUBSYSTEM_DEF(timer)
wait = max(wait, 0)
if(wait >= INFINITY)
CRASH("Attempted to create timer with INFINITY delay")
var/hash
if (flags & TIMER_UNIQUE)
-113
View File
@@ -1,113 +0,0 @@
/*
Exonet Protocol Version 2
This is designed to be a fairly simple fake-networking system, allowing you to send and receive messages
between the exonet_protocol datums, and for atoms to react to those messages, based on the contents of the message.
Hopefully, this can evolve to be a more robust fake-networking system and allow for some devious network hacking in the future.
Version 1 never existed.
*Setting up*
To set up the exonet link, define a variable on your desired atom it is like this;
var/datum/exonet_protocol/exonet = null
Afterwards, before you want to do networking, call exonet = New(src), then exonet.make_address(string), and give it a string to hash into the new IP.
The reason it needs a string is so you can have the addresses be persistant, assuming no-one already took it first.
When you're no longer wanting to use the address and want to free it up, like when you want to Destroy() it, you need to call remove_address()
Destroy() also automatically calls remove_address().
*Sending messages*
To send a message to another datum, you need to know it's EPv2 (fake IP) address. Once you know that, call send_message(), place your
intended address in the first argument, then the message in the second. For example, send_message(exonet.address, "ping") will make you
ping yourself.
*Receiving messages*
You don't need to do anything special to receive the messages, other than give your target exonet datum an address as well. Once something hits
your datum with send_message(), receive_message() is called, and the default action is to call receive_exonet_message() on the datum's holder.
You'll want to override receive_exonet_message() on your atom, and define what will occur when the message is received.
The receiving atom will receive the origin atom (the atom that sent the message), the origin address, and finally the message itself.
It's suggested to start with an if or switch statement for the message, to determine what to do.
*/
/datum/exonet_protocol
var/address = "" //Resembles IPv6, but with only five 'groups', e.g. XXXX:XXXX:XXXX:XXXX:XXXX
var/atom/holder
/datum/exonet_protocol/New(var/atom/H)
holder = H
/datum/exonet_protocol/Destroy()
remove_address()
holder = null
return ..()
// Proc: make_address()
// Parameters: 1 (string - used to make into a hash that will be part of the new address)
// Description: Allocates a new address based on the string supplied. It results in consistant addresses for each round assuming it is not already taken..
/datum/exonet_protocol/proc/make_address(var/string)
if(!string)
return
var/hex = copytext(md5(string),1,25)
if(!hex)
return
var/addr_1 = copytext(hex,1,5)
var/addr_2 = copytext(hex,5,9)
var/addr_3 = copytext(hex,9,13)
var/addr_4 = copytext(hex,13,17)
address = "fc00:[addr_1]:[addr_2]:[addr_3]:[addr_4]"
if(SScircuit.all_exonet_connections[address])
stack_trace("WARNING: Exonet address collision in make_address. Holder type if applicable is [holder? holder.type : "NO HOLDER"]!")
SScircuit.all_exonet_connections[address] = src
// Proc: make_arbitrary_address()
// Parameters: 1 (new_address - the desired address)
// Description: Allocates that specific address, if it is available.
/datum/exonet_protocol/proc/make_arbitrary_address(var/new_address)
if(new_address)
if(new_address == SScircuit.get_exonet_address(new_address) ) //Collision test.
return FALSE
address = new_address
SScircuit.all_exonet_connections[address] = src
return TRUE
// Proc: remove_address()
// Parameters: None
// Description: Deallocates the address, freeing it for use.
/datum/exonet_protocol/proc/remove_address()
SScircuit.all_exonet_connections -= address
address = ""
// Proc: send_message()
// Parameters: 3 (target_address - the desired address to send the message to, data_type - text stating what the content is meant to be used for,
// content - the actual 'message' being sent to the address)
// Description: Sends the message to target_address, by calling receive_message() on the desired datum. Returns true if the message is recieved.
/datum/exonet_protocol/proc/send_message(var/target_address, var/data_type, var/content)
if(!address)
return FALSE
var/obj/machinery/exonet_node/node = SScircuit.get_exonet_node()
if(!node) // Telecomms went boom, ion storm, etc.
return FALSE
var/datum/exonet_protocol/exonet = SScircuit.get_exonet_address(target_address)
if(exonet)
node.write_log(address, target_address, data_type, content)
return exonet.receive_message(holder, address, data_type, content)
// Proc: receive_message()
// Parameters: 4 (origin_atom - the origin datum's holder, origin_address - the address the message originated from,
// data_type - text stating what the content is meant to be used for, content - the actual 'message' being sent from origin_atom)
// Description: Called when send_message() successfully reaches the intended datum. By default, calls receive_exonet_message() on the holder atom.
/datum/exonet_protocol/proc/receive_message(var/atom/origin_atom, var/origin_address, var/data_type, var/content)
holder.receive_exonet_message(origin_atom, origin_address, data_type, content)
return TRUE // for send_message()
// Proc: receive_exonet_message()
// Parameters: 3 (origin_atom - the origin datum's holder, origin_address - the address the message originated from, message - the message that was sent)
// Description: Override this to make your atom do something when a message is received.
/atom/proc/receive_exonet_message(var/atom/origin_atom, var/origin_address, var/message, var/text)
return
+2 -2
View File
@@ -177,7 +177,7 @@
to_chat(owner.current, "<span class='notice'>We have removed our evolutions from this form, and are now ready to readapt.</span>")
reset_powers()
canrespec = 0
SSblackbox.add_details("changeling_power_purchase","Readapt")
SSblackbox.record_feedback("tally", "changeling_power_purchase", 1, "Readapt")
return 1
else
to_chat(owner.current, "<span class='danger'>You lack the power to readapt your evolutions!</span>")
@@ -277,7 +277,7 @@
if(stored_profiles.len > dna_max)
if(!push_out_profile())
return
if(!first_prof)
first_prof = prof
+3 -1
View File
@@ -5,6 +5,7 @@
var/datum/action/innate/cult/comm/communion = new
var/datum/action/innate/cult/mastervote/vote = new
job_rank = ROLE_CULTIST
var/ignore_implant = FALSE
/datum/antagonist/cult/Destroy()
QDEL_NULL(communion)
@@ -65,7 +66,7 @@
/datum/antagonist/cult/can_be_owned(datum/mind/new_owner)
. = ..()
if(.)
if(. && !ignore_implant)
. = is_convertable_to_cult(new_owner.current)
/datum/antagonist/cult/on_gain()
@@ -121,6 +122,7 @@
var/datum/action/innate/cult/master/finalreck/reckoning = new
var/datum/action/innate/cult/master/cultmark/bloodmark = new
var/datum/action/innate/cult/master/pulse/throwing = new
ignore_implant = TRUE
/datum/antagonist/cult/master/Destroy()
QDEL_NULL(reckoning)
+4 -4
View File
@@ -134,11 +134,11 @@
/datum/antagonist/rev/farewell()
if(ishuman(owner.current))
owner.current.visible_message("[owner.current] looks like they just remembered their real allegiance!", \
"<span class='userdanger'><FONT size = 3>You are no longer a brainwashed revolutionary! Your memory is hazy from the time you were a rebel...the only thing you remember is the name of the one who brainwashed you...</FONT></span>")
owner.current.visible_message("[owner.current] looks like they just remembered their real allegiance!")
to_chat(owner, "<span class='userdanger'><FONT size = 3>You are no longer a brainwashed revolutionary! Your memory is hazy from the time you were a rebel...the only thing you remember is the name of the one who brainwashed you...</FONT></span>")
else if(issilicon(owner.current))
owner.current.visible_message("The frame beeps contentedly, purging the hostile memory engram from the MMI before initalizing it.", \
"<span class='userdanger'><FONT size = 3>The frame's firmware detects and deletes your neural reprogramming! You remember nothing but the name of the one who flashed you.</FONT></span>")
owner.current.visible_message("The frame beeps contentedly, purging the hostile memory engram from the MMI before initalizing it.")
to_chat(owner, "<span class='userdanger'><FONT size = 3>The frame's firmware detects and deletes your neural reprogramming! You remember nothing but the name of the one who flashed you.</FONT></span>")
/datum/antagonist/rev/proc/remove_revolutionary(borged, deconverter)
log_attack("[owner.current] (Key: [key_name(owner.current)]) has been deconverted from the revolution by [deconverter] (Key: [key_name(deconverter)])!")
+7 -1
View File
@@ -29,7 +29,8 @@
icon = beam_icon
icon_state = beam_icon_state
beam_type = btype
addtimer(CALLBACK(src,.proc/End), time)
if(time < INFINITY)
addtimer(CALLBACK(src,.proc/End), time)
/datum/beam/proc/Start()
Draw()
@@ -149,6 +150,11 @@
owner = null
return ..()
/obj/effect/ebeam/singularity_pull()
return
/obj/effect/ebeam/singularity_act()
return
/atom/proc/Beam(atom/BeamTarget,icon_state="b_beam",icon='icons/effects/beam.dmi',time=50, maxdistance=10,beam_type=/obj/effect/ebeam,beam_sleep_time = 3)
var/datum/beam/newbeam = new(src,BeamTarget,icon,icon_state,time,maxdistance,beam_type,beam_sleep_time)
INVOKE_ASYNC(newbeam, /datum/beam/.proc/Start)
+2 -1
View File
@@ -34,7 +34,7 @@ Stands have a lot of procs which mimic mob procs. Rather than inserting hooks fo
* Lazy associated list of type -> component/list of components.
1. `/datum/component/var/enabled` (protected, boolean)
* If the component is enabled. If not, it will not react to signals
* `TRUE` by default
* `FALSE` by default, set to `TRUE` when a signal is registered
1. `/datum/component/var/dupe_mode` (protected, enum)
* How duplicate component types are handled when added to the datum.
* `COMPONENT_DUPE_HIGHLANDER` (default): Old component will be deleted, new component will first have `/datum/component/proc/InheritComponent(datum/component/old, FALSE)` on it
@@ -78,6 +78,7 @@ Stands have a lot of procs which mimic mob procs. Rather than inserting hooks fo
1. `/datum/proc/SendSignal(signal, ...)` (public, final)
* Call to send a signal to the components of the target datum
* Extra arguments are to be specified in the signal definition
* Returns a bitflag with signal specific information assembled from all activated components
1. `/datum/component/New(datum/parent, ...)` (private, final)
* Runs internal setup for the component
* Extra arguments are passed to `Initialize()`
+29 -17
View File
@@ -1,5 +1,5 @@
/datum/component
var/enabled = TRUE
var/enabled = FALSE
var/dupe_mode = COMPONENT_DUPE_HIGHLANDER
var/dupe_type
var/list/signal_procs
@@ -10,13 +10,21 @@
qdel(src)
CRASH("[type] instantiated!")
//check for common mishaps
if(!isnum(dupe_mode))
qdel(src)
CRASH("[type]: Invalid dupe_mode!")
if(dupe_type && !ispath(dupe_type))
qdel(src)
CRASH("[type]: Invalid dupe_type!")
parent = P
var/list/arguments = args.Copy()
arguments.Cut(1, 2)
if(Initialize(arglist(arguments)) == COMPONENT_INCOMPATIBLE)
qdel(src, TRUE, TRUE)
return
_CheckDupesAndJoinParent(P)
/datum/component/proc/_CheckDupesAndJoinParent()
@@ -45,12 +53,12 @@
if(!old)
//let the others know
P.SendSignal(COMSIG_COMPONENT_ADDED, src)
//lazy init the parent's dc list
var/list/dc = P.datum_components
if(!dc)
P.datum_components = dc = list()
//set up the typecache
var/our_type = type
for(var/I in _GetInverseTypeList(our_type))
@@ -114,7 +122,7 @@
if(!procs)
procs = list()
signal_procs = procs
var/list/sig_types = islist(sig_type_or_types) ? sig_type_or_types : list(sig_type_or_types)
for(var/sig_type in sig_types)
if(!override)
@@ -125,6 +133,8 @@
if(!istype(proc_or_callback, /datum/callback)) //if it wasnt a callback before, it is now
proc_or_callback = CALLBACK(src, proc_or_callback)
procs[sig_type] = proc_or_callback
enabled = TRUE
/datum/component/proc/InheritComponent(datum/component/C, i_am_original)
return
@@ -156,36 +166,35 @@
/datum/proc/SendSignal(sigtype, ...)
var/list/comps = datum_components
if(!comps)
return FALSE
return NONE
var/list/arguments = args.Copy()
arguments.Cut(1, 2)
var/target = comps[/datum/component]
if(!length(target))
var/datum/component/C = target
if(!C.enabled)
return FALSE
var/list/sps = C.signal_procs
var/datum/callback/CB = LAZYACCESS(sps, sigtype)
return NONE
var/datum/callback/CB = C.signal_procs[sigtype]
if(!CB)
return FALSE
return NONE
. = CB.InvokeAsync(arglist(arguments))
if(.)
if(. & COMPONENT_ACTIVATED)
ComponentActivated(C)
C.AfterComponentActivated()
else
. = FALSE
. = NONE
for(var/I in target)
var/datum/component/C = I
if(!C.enabled)
continue
var/list/sps = C.signal_procs
var/datum/callback/CB = LAZYACCESS(sps, sigtype)
continue
var/datum/callback/CB = C.signal_procs[sigtype]
if(!CB)
continue
if(CB.InvokeAsync(arglist(arguments)))
var/retval = CB.InvokeAsync(arglist(arguments))
. |= retval
if(retval & COMPONENT_ACTIVATED)
ComponentActivated(C)
C.AfterComponentActivated()
. = TRUE
/datum/proc/ComponentActivated(datum/component/C)
set waitfor = FALSE
@@ -255,3 +264,6 @@
target.TakeComponent(I)
else
target.TakeComponent(comps)
/datum/component/ui_host()
return parent
+19 -20
View File
@@ -20,27 +20,26 @@
/datum/component/archaeology/proc/Dig(obj/item/W, mob/living/user)
if(dug)
to_chat(user, "<span class='notice'>Looks like someone has dug here already.</span>")
return FALSE
else
var/digging_speed
if (istype(W, /obj/item/shovel))
var/obj/item/shovel/S = W
digging_speed = S.digspeed
else if (istype(W, /obj/item/pickaxe))
var/obj/item/pickaxe/P = W
digging_speed = P.digspeed
return
var/digging_speed
if (istype(W, /obj/item/shovel))
var/obj/item/shovel/S = W
digging_speed = S.digspeed
else if (istype(W, /obj/item/pickaxe))
var/obj/item/pickaxe/P = W
digging_speed = P.digspeed
if (digging_speed && isturf(user.loc))
to_chat(user, "<span class='notice'>You start digging...</span>")
playsound(parent, 'sound/effects/shovel_dig.ogg', 50, 1)
if (digging_speed && isturf(user.loc))
to_chat(user, "<span class='notice'>You start digging...</span>")
playsound(parent, 'sound/effects/shovel_dig.ogg', 50, 1)
if(do_after(user, digging_speed, target = parent))
to_chat(user, "<span class='notice'>You dig a hole.</span>")
gets_dug()
dug = TRUE
SSblackbox.add_details("pick_used_mining",W.type)
return TRUE
return FALSE
if(do_after(user, digging_speed, target = parent))
to_chat(user, "<span class='notice'>You dig a hole.</span>")
gets_dug()
dug = TRUE
SSblackbox.record_feedback("tally", "pick_used_mining", 1, W.type)
return COMPONENT_NO_AFTERATTACK
/datum/component/archaeology/proc/gets_dug()
if(dug)
+1 -1
View File
@@ -10,4 +10,4 @@
if(istype(victim))
for(var/datum/disease/D in diseases)
victim.ContactContractDisease(D, "feet")
return TRUE
return COMPONENT_ACTIVATED
+3 -3
View File
@@ -53,11 +53,11 @@
/datum/component/material_container/proc/OnAttackBy(obj/item/I, mob/living/user)
var/list/tc = allowed_typecache
if(user.a_intent == INTENT_HARM)
return FALSE
return
if((I.flags_2 & (HOLOGRAM_2 | NO_MAT_REDEMPTION_2)) || (tc && !is_type_in_typecache(I, tc)))
to_chat(user, "<span class='warning'>[parent] won't accept [I]!</span>")
return FALSE
. = TRUE
return
. = COMPONENT_ACTIVATED | COMPONENT_NO_AFTERATTACK
last_insert_success = FALSE
var/datum/callback/pc = precondition
if(pc && !pc.Invoke())
+59
View File
@@ -0,0 +1,59 @@
//Thing meant for allowing datums and objects to access a NTnet network datum.
/datum/proc/ntnet_recieve(datum/netdata/data)
return
/datum/proc/ntnet_send(datum/netdata/data, netid)
GET_COMPONENT(NIC, /datum/component/ntnet_interface)
if(!NIC)
return FALSE
return NIC.__network_send(data, netid)
/datum/component/ntnet_interface
var/hardware_id //text
var/network_name = "" //text
var/list/networks_connected_by_id = list() //id = datum/ntnet
/datum/component/ntnet_interface/Initialize(force_ID, force_name = "NTNet Device", autoconnect_station_network = TRUE) //Don't force ID unless you know what you're doing!
if(!force_ID)
hardware_id = "[SSnetworks.assignment_hardware_id++]"
else
hardware_id = force_ID
network_name = force_name
SSnetworks.register_interface(src)
if(autoconnect_station_network)
register_connection(SSnetworks.station_network)
/datum/component/ntnet_interface/Destroy()
unregister_all_connections()
SSnetworks.unregister_interface(src)
return ..()
/datum/component/ntnet_interface/proc/__network_recieve(datum/netdata/data) //Do not directly proccall!
parent.SendSignal(COMSIG_COMPONENT_NTNET_RECIEVE, data)
parent.ntnet_recieve(data)
/datum/component/ntnet_interface/proc/__network_send(datum/netdata/data, netid) //Do not directly proccall!
if(netid)
if(networks_connected_by_id[netid])
var/datum/ntnet/net = networks_connected_by_id[netid]
return net.process_data_transmit(src, data)
return FALSE
for(var/i in networks_connected_by_id)
var/datum/ntnet/net = networks_connected_by_id[i]
net.process_data_transmit(src, data)
return TRUE
/datum/component/ntnet_interface/proc/register_connection(datum/ntnet/net)
if(net.interface_connect(src))
networks_connected_by_id[net.network_id] = net
return TRUE
/datum/component/ntnet_interface/proc/unregister_all_connections()
for(var/i in networks_connected_by_id)
unregister_connection(networks_connected_by_id[i])
return TRUE
/datum/component/ntnet_interface/proc/unregister_connection(datum/ntnet/net)
net.interface_disconnect(src)
networks_connected_by_id -= net.network_id
return TRUE
+2 -2
View File
@@ -14,8 +14,8 @@
/datum/component/spraycan_paintable/proc/Repaint(obj/item/toy/crayon/spraycan/spraycan, mob/living/user)
if(!istype(spraycan) || user.a_intent == INTENT_HARM)
return FALSE
. = TRUE
return
. = COMPONENT_NO_AFTERATTACK
if(spraycan.is_capped)
to_chat(user, "<span class='warning'>Take the cap off first!</span>")
return
+326
View File
@@ -0,0 +1,326 @@
/datum/component/riding
var/next_vehicle_move = 0 //used for move delays
var/vehicle_move_delay = 2 //tick delay between movements, lower = faster, higher = slower
var/keytype
var/slowed = FALSE
var/slowvalue = 1
var/list/riding_offsets = list() //position_of_user = list(dir = list(px, py)), or RIDING_OFFSET_ALL for a generic one.
var/list/directional_vehicle_layers = list() //["[DIRECTION]"] = layer. Don't set it for a direction for default, set a direction to null for no change.
var/list/directional_vehicle_offsets = list() //same as above but instead of layer you have a list(px, py)
var/list/allowed_turf_typecache
var/list/forbid_turf_typecache //allow typecache for only certain turfs, forbid to allow all but those. allow only certain turfs will take precedence.
var/allow_one_away_from_valid_turf = TRUE //allow moving one tile away from a valid turf but not more.
var/override_allow_spacemove = FALSE
var/drive_verb = "drive"
var/ride_check_rider_incapacitated = FALSE
var/ride_check_rider_restrained = FALSE
var/ride_check_ridden_incapacitated = FALSE
/datum/component/riding/Initialize()
if(!ismovableatom(parent))
. = COMPONENT_INCOMPATIBLE
CRASH("RIDING COMPONENT ASSIGNED TO NON ATOM MOVABLE!")
RegisterSignal(COMSIG_MOVABLE_BUCKLE, .proc/vehicle_mob_buckle)
RegisterSignal(COMSIG_MOVABLE_UNBUCKLE, .proc/vehicle_mob_unbuckle)
RegisterSignal(COMSIG_MOVABLE_MOVED, .proc/vehicle_moved)
/datum/component/riding/proc/vehicle_mob_unbuckle(mob/living/M, force = FALSE)
restore_position(M)
unequip_buckle_inhands(M)
/datum/component/riding/proc/vehicle_mob_buckle(mob/living/M, force = FALSE)
handle_vehicle_offsets()
/datum/component/riding/proc/handle_vehicle_layer()
var/atom/movable/AM = parent
var/static/list/defaults = list(TEXT_NORTH = OBJ_LAYER, TEXT_SOUTH = ABOVE_MOB_LAYER, TEXT_EAST = ABOVE_MOB_LAYER, TEXT_WEST = ABOVE_MOB_LAYER)
. = defaults["[AM.dir]"]
if(directional_vehicle_layers["[AM.dir]"])
. = directional_vehicle_layers["[AM.dir]"]
if(isnull(.)) //you can set it to null to not change it.
. = AM.layer
AM.layer = .
/datum/component/riding/proc/set_vehicle_dir_layer(dir, layer)
directional_vehicle_layers["[dir]"] = layer
/datum/component/riding/proc/vehicle_moved()
var/atom/movable/AM = parent
for(var/i in AM.buckled_mobs)
ride_check(i)
handle_vehicle_offsets()
handle_vehicle_layer()
/datum/component/riding/proc/ride_check(mob/living/M)
var/atom/movable/AM = parent
var/mob/AMM = AM
if((ride_check_rider_restrained && M.restrained(TRUE)) || (ride_check_rider_incapacitated && M.incapacitated(FALSE, TRUE)) || (ride_check_ridden_incapacitated && istype(AMM) && AMM.incapacitated(FALSE, TRUE)))
AM.visible_message("<span class='warning'>[M] falls off of [AM]!</span>")
AM.unbuckle_mob(M)
return TRUE
/datum/component/riding/proc/force_dismount(mob/living/M)
var/atom/movable/AM = parent
AM.unbuckle_mob(M)
/datum/component/riding/proc/handle_vehicle_offsets()
var/atom/movable/AM = parent
var/AM_dir = "[AM.dir]"
var/passindex = 0
if(AM.has_buckled_mobs())
for(var/m in AM.buckled_mobs)
passindex++
var/mob/living/buckled_mob = m
var/list/offsets = get_offsets(passindex)
var/rider_dir = get_rider_dir(passindex)
buckled_mob.setDir(rider_dir)
dir_loop:
for(var/offsetdir in offsets)
if(offsetdir == AM_dir)
var/list/diroffsets = offsets[offsetdir]
buckled_mob.pixel_x = diroffsets[1]
if(diroffsets.len >= 2)
buckled_mob.pixel_y = diroffsets[2]
if(diroffsets.len == 3)
buckled_mob.layer = diroffsets[3]
break dir_loop
var/list/static/default_vehicle_pixel_offsets = list(TEXT_NORTH = list(0, 0), TEXT_SOUTH = list(0, 0), TEXT_EAST = list(0, 0), TEXT_WEST = list(0, 0))
var/px = default_vehicle_pixel_offsets[AM_dir]
var/py = default_vehicle_pixel_offsets[AM_dir]
if(directional_vehicle_offsets[AM_dir])
if(isnull(directional_vehicle_offsets[AM_dir]))
px = AM.pixel_x
py = AM.pixel_y
else
px = directional_vehicle_offsets[AM_dir][1]
py = directional_vehicle_offsets[AM_dir][2]
AM.pixel_x = px
AM.pixel_y = py
/datum/component/riding/proc/set_vehicle_dir_offsets(dir, x, y)
directional_vehicle_offsets["[dir]"] = list(x, y)
//Override this to set your vehicle's various pixel offsets
/datum/component/riding/proc/get_offsets(pass_index) // list(dir = x, y, layer)
. = list(TEXT_NORTH = list(0, 0), TEXT_SOUTH = list(0, 0), TEXT_EAST = list(0, 0), TEXT_WEST = list(0, 0))
if(riding_offsets["[pass_index]"])
. = riding_offsets["[pass_index]"]
else if(riding_offsets["[RIDING_OFFSET_ALL]"])
. = riding_offsets["[RIDING_OFFSET_ALL]"]
/datum/component/riding/proc/set_riding_offsets(index, list/offsets)
if(!islist(offsets))
return FALSE
riding_offsets["[index]"] = offsets
//Override this to set the passengers/riders dir based on which passenger they are.
//ie: rider facing the vehicle's dir, but passenger 2 facing backwards, etc.
/datum/component/riding/proc/get_rider_dir(pass_index)
var/atom/movable/AM = parent
return AM.dir
//KEYS
/datum/component/riding/proc/keycheck(mob/user)
return !keytype || user.is_holding_item_of_type(keytype)
//BUCKLE HOOKS
/datum/component/riding/proc/restore_position(mob/living/buckled_mob)
if(buckled_mob)
buckled_mob.pixel_x = 0
buckled_mob.pixel_y = 0
if(buckled_mob.client)
buckled_mob.client.change_view(world.view)
//MOVEMENT
/datum/component/riding/proc/turf_check(turf/next, turf/current)
if(allowed_turf_typecache && !allowed_turf_typecache[next.type])
return (allow_one_away_from_valid_turf && allowed_turf_typecache[current.type])
else if(forbid_turf_typecache && forbid_turf_typecache[next.type])
return (allow_one_away_from_valid_turf && !forbid_turf_typecache[current.type])
return TRUE
/datum/component/riding/proc/handle_ride(mob/user, direction)
var/atom/movable/AM = parent
if(user.incapacitated())
Unbuckle(user)
return
if(world.time < next_vehicle_move)
return
next_vehicle_move = world.time + vehicle_move_delay
if(keycheck(user))
var/turf/next = get_step(AM, direction)
var/turf/current = get_turf(AM)
if(!istype(next) || !istype(current))
return //not happening.
if(!turf_check(next, current))
to_chat(user, "Your \the [AM] can not go onto [next]!")
return
if(!Process_Spacemove(direction) || !isturf(AM.loc))
return
step(AM, direction)
handle_vehicle_layer()
handle_vehicle_offsets()
else
to_chat(user, "<span class='notice'>You'll need the keys in one of your hands to [drive_verb] [AM].</span>")
/datum/component/riding/proc/Unbuckle(atom/movable/M)
addtimer(CALLBACK(parent, /atom/movable/.proc/unbuckle_mob, M), 0, TIMER_UNIQUE)
/datum/component/riding/proc/Process_Spacemove(direction)
var/atom/movable/AM = parent
return override_allow_spacemove || AM.has_gravity()
/datum/component/riding/proc/account_limbs(mob/living/M)
if(M.get_num_legs() < 2 && !slowed)
vehicle_move_delay = vehicle_move_delay + slowvalue
slowed = TRUE
else if(slowed)
vehicle_move_delay = vehicle_move_delay - slowvalue
slowed = FALSE
///////Yes, I said humans. No, this won't end well...//////////
/datum/component/riding/human
/datum/component/riding/human/Initialize()
. = ..()
RegisterSignal(COMSIG_HUMAN_MELEE_UNARMED_ATTACK, .proc/on_host_unarmed_melee)
/datum/component/riding/human/proc/on_host_unarmed_melee(atom/target)
var/mob/living/carbon/human/AM = parent
if(AM.a_intent == INTENT_DISARM && (target in AM.buckled_mobs))
force_dismount(target)
/datum/component/riding/human/handle_vehicle_layer()
var/atom/movable/AM = parent
if(AM.buckled_mobs && AM.buckled_mobs.len)
if(AM.dir == SOUTH)
AM.layer = ABOVE_MOB_LAYER
else
AM.layer = OBJ_LAYER
else
AM.layer = MOB_LAYER
/datum/component/riding/human/force_dismount(mob/living/user)
var/atom/movable/AM = parent
AM.unbuckle_mob(user)
user.Knockdown(60)
user.visible_message("<span class='warning'>[AM] pushes [user] off of them!</span>")
/datum/component/riding/cyborg
/datum/component/riding/cyborg/ride_check(mob/user)
var/atom/movable/AM = parent
if(user.incapacitated())
var/kick = TRUE
if(iscyborg(AM))
var/mob/living/silicon/robot/R = AM
if(R.module && R.module.ride_allow_incapacitated)
kick = FALSE
if(kick)
to_chat(user, "<span class='userdanger'>You fall off of [AM]!</span>")
Unbuckle(user)
return
if(iscarbon(user))
var/mob/living/carbon/carbonuser = user
if(!carbonuser.get_num_arms())
Unbuckle(user)
to_chat(user, "<span class='userdanger'>You can't grab onto [AM] with no hands!</span>")
return
/datum/component/riding/cyborg/handle_vehicle_layer()
var/atom/movable/AM = parent
if(AM.buckled_mobs && AM.buckled_mobs.len)
if(AM.dir == SOUTH)
AM.layer = ABOVE_MOB_LAYER
else
AM.layer = OBJ_LAYER
else
AM.layer = MOB_LAYER
/datum/component/riding/cyborg/get_offsets(pass_index) // list(dir = x, y, layer)
return list(TEXT_NORTH = list(0, 4), TEXT_SOUTH = list(0, 4), TEXT_EAST = list(-6, 3), TEXT_WEST = list( 6, 3))
/datum/component/riding/cyborg/handle_vehicle_offsets()
var/atom/movable/AM = parent
if(AM.has_buckled_mobs())
for(var/mob/living/M in AM.buckled_mobs)
M.setDir(AM.dir)
if(iscyborg(AM))
var/mob/living/silicon/robot/R = AM
if(istype(R.module))
M.pixel_x = R.module.ride_offset_x[dir2text(AM.dir)]
M.pixel_y = R.module.ride_offset_y[dir2text(AM.dir)]
else
..()
/datum/component/riding/cyborg/force_dismount(mob/living/M)
var/atom/movable/AM = parent
AM.unbuckle_mob(M)
var/turf/target = get_edge_target_turf(AM, AM.dir)
var/turf/targetm = get_step(get_turf(AM), AM.dir)
M.Move(targetm)
M.visible_message("<span class='warning'>[M] is thrown clear of [AM]!</span>")
M.throw_at(target, 14, 5, AM)
M.Knockdown(60)
/datum/component/riding/proc/equip_buckle_inhands(mob/living/carbon/human/user, amount_required = 1)
var/atom/movable/AM = parent
var/amount_equipped = 0
for(var/amount_needed = amount_required, amount_needed > 0, amount_needed--)
var/obj/item/riding_offhand/inhand = new /obj/item/riding_offhand(user)
inhand.rider = user
inhand.parent = AM
if(user.put_in_hands(inhand, TRUE))
amount_equipped++
else
break
if(amount_equipped >= amount_required)
return TRUE
else
unequip_buckle_inhands(user)
return FALSE
/datum/component/riding/proc/unequip_buckle_inhands(mob/living/carbon/user)
var/atom/movable/AM = parent
for(var/obj/item/riding_offhand/O in user.contents)
if(O.parent != AM)
CRASH("RIDING OFFHAND ON WRONG MOB")
continue
if(O.selfdeleting)
continue
else
qdel(O)
return TRUE
/obj/item/riding_offhand
name = "offhand"
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "offhand"
w_class = WEIGHT_CLASS_HUGE
flags_1 = ABSTRACT_1 | DROPDEL_1 | NOBLUDGEON_1
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
var/mob/living/carbon/rider
var/mob/living/parent
var/selfdeleting = FALSE
/obj/item/riding_offhand/dropped()
selfdeleting = TRUE
. = ..()
/obj/item/riding_offhand/equipped()
if(loc != rider)
selfdeleting = TRUE
qdel(src)
. = ..()
/obj/item/riding_offhand/Destroy()
var/atom/movable/AM = parent
if(selfdeleting)
if(rider in AM.buckled_mobs)
AM.unbuckle_mob(rider)
. = ..()
+1 -1
View File
@@ -12,7 +12,7 @@
var/mob/victim = AM
if(istype(victim) && !victim.is_flying() && victim.slip(intensity, parent, lube_flags))
slip_victim = victim
return TRUE
return COMPONENT_ACTIVATED
/datum/component/slippery/AfterComponentActivated()
slip_victim = null
+1 -1
View File
@@ -19,7 +19,7 @@
if(ishuman(C))
var/mob/living/carbon/human/H = C
if(istype(H.dna.species, /datum/species/skeleton))
return ..() //undeads are unaffected by the spook-pocalypse.
return //undeads are unaffected by the spook-pocalypse.
if(istype(H.dna.species, /datum/species/zombie))
H.adjustStaminaLoss(25)
H.Knockdown(15) //zombies can't resist the doot
+20 -3
View File
@@ -3,13 +3,30 @@
var/amount
var/overlay
var/static/list/blacklist = typecacheof(/turf/closed/wall/mineral/diamond)
var/static/list/resistlist = typecacheof(/turf/closed/wall/r_wall)
var/static/list/blacklist = typecacheof(
/turf/open/lava,
/turf/open/space,
/turf/open/water,
/turf/open/chasm,
)
var/static/list/immunelist = typecacheof(
/turf/closed/wall/mineral/diamond,
/turf/closed/indestructible,
/turf/open/indestructible,
)
var/static/list/resistlist = typecacheof(
/turf/closed/wall/r_wall,
)
/datum/component/thermite/Initialize(_amount)
if(!istype(parent, /turf))
return COMPONENT_INCOMPATIBLE
. = COMPONENT_INCOMPATIBLE
CRASH("A thermite component has been applied to an incorrect object. parent: [parent]")
if(blacklist[parent.type])
return COMPONENT_INCOMPATIBLE
if(immunelist[parent.type])
_amount*=0 //Yeah the overlay can still go on it and be cleaned but you arent burning down a diamond wall
if(resistlist[parent.type])
_amount*=0.25
@@ -1,162 +1,204 @@
GLOBAL_LIST_EMPTY(uplinks)
/**
* Uplinks
*
* All /obj/item(s) have a hidden_uplink var. By default it's null. Give the item one with 'new(src') (it must be in it's contents). Then add 'uses.'
* Use whatever conditionals you want to check that the user has an uplink, and then call interact() on their uplink.
* You might also want the uplink menu to open if active. Check if the uplink is 'active' and then interact() with it.
**/
/obj/item/device/uplink
name = "syndicate uplink"
desc = "There is something wrong if you're examining this."
var/active = FALSE
var/lockable = TRUE
var/telecrystals = 20
var/selected_cat = null
var/owner = null
var/datum/game_mode/gamemode = null
var/spent_telecrystals = 0
var/purchase_log = ""
var/list/uplink_items
var/hidden_crystals = 0
/obj/item/device/uplink/Initialize()
. = ..()
GLOB.uplinks += src
uplink_items = get_uplink_items(gamemode)
/obj/item/device/uplink/proc/set_gamemode(gamemode)
src.gamemode = gamemode
uplink_items = get_uplink_items(gamemode)
/obj/item/device/uplink/Destroy()
GLOB.uplinks -= src
return ..()
/obj/item/device/uplink/attackby(obj/item/I, mob/user, params)
for(var/item in subtypesof(/datum/uplink_item))
var/datum/uplink_item/UI = item
var/path = null
if(initial(UI.refund_path))
path = initial(UI.refund_path)
else
path = initial(UI.item)
var/cost = 0
if(initial(UI.refund_amount))
cost = initial(UI.refund_amount)
else
cost = initial(UI.cost)
var/refundable = initial(UI.refundable)
if(I.type == path && refundable && I.check_uplink_validity())
telecrystals += cost
spent_telecrystals -= cost
to_chat(user, "<span class='notice'>[I] refunded.</span>")
qdel(I)
return
..()
/obj/item/device/uplink/interact(mob/user)
active = TRUE
if(user)
ui_interact(user)
/obj/item/device/uplink/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.inventory_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "uplink", name, 450, 750, master_ui, state)
ui.set_autoupdate(FALSE) // This UI is only ever opened by one person, and never is updated outside of user input.
ui.set_style("syndicate")
ui.open()
/obj/item/device/uplink/ui_data(mob/user)
if(!user.mind)
return
var/list/data = list()
data["telecrystals"] = telecrystals
data["lockable"] = lockable
data["categories"] = list()
for(var/category in uplink_items)
var/list/cat = list(
"name" = category,
"items" = (category == selected_cat ? list() : null))
if(category == selected_cat)
for(var/item in uplink_items[category])
var/datum/uplink_item/I = uplink_items[category][item]
if(I.limited_stock == 0)
continue
if(I.restricted_roles.len)
var/is_inaccessible = 1
for(var/R in I.restricted_roles)
if(R == user.mind.assigned_role)
is_inaccessible = 0
if(is_inaccessible)
continue
cat["items"] += list(list(
"name" = I.name,
"cost" = I.cost,
"desc" = I.desc,
))
data["categories"] += list(cat)
return data
/obj/item/device/uplink/ui_act(action, params)
if(!active)
return
switch(action)
if("buy")
var/item = params["item"]
var/list/buyable_items = list()
for(var/category in uplink_items)
buyable_items += uplink_items[category]
if(item in buyable_items)
var/datum/uplink_item/I = buyable_items[item]
I.buy(usr, src)
. = TRUE
if("lock")
active = FALSE
telecrystals += hidden_crystals
hidden_crystals = 0
SStgui.close_uis(src)
if("select")
selected_cat = params["category"]
return 1
/obj/item/device/uplink/ui_host()
return loc
// Refund certain items by hitting the uplink with it.
/obj/item/device/radio/uplink/attackby(obj/item/I, mob/user, params)
return hidden_uplink.attackby(I, user, params)
// A collection of pre-set uplinks, for admin spawns.
/obj/item/device/radio/uplink/Initialize()
. = ..()
icon_state = "radio"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
hidden_uplink = new(src)
hidden_uplink.active = TRUE
hidden_uplink.lockable = FALSE
/obj/item/device/radio/uplink/nuclear/Initialize()
. = ..()
hidden_uplink.set_gamemode(/datum/game_mode/nuclear)
/obj/item/device/multitool/uplink/Initialize()
. = ..()
hidden_uplink = new(src)
hidden_uplink.active = TRUE
hidden_uplink.lockable = FALSE
/obj/item/pen/uplink/Initialize()
. = ..()
hidden_uplink = new(src)
traitor_unlock_degrees = 360
GLOBAL_LIST_EMPTY(uplinks)
/**
* Uplinks
*
* All /obj/item(s) have a hidden_uplink var. By default it's null. Give the item one with 'new(src') (it must be in it's contents). Then add 'uses.'
* Use whatever conditionals you want to check that the user has an uplink, and then call interact() on their uplink.
* You might also want the uplink menu to open if active. Check if the uplink is 'active' and then interact() with it.
**/
/datum/component/uplink
dupe_mode = COMPONENT_DUPE_UNIQUE
var/name = "syndicate uplink"
var/active = FALSE
var/lockable = TRUE
var/locked = TRUE
var/telecrystals
var/selected_cat
var/owner = null
var/datum/game_mode/gamemode
var/spent_telecrystals = 0
var/datum/uplink_purchase_log/purchase_log
var/list/uplink_items
var/hidden_crystals = 0
/datum/component/uplink/Initialize(_owner, _lockable = TRUE, _enabled = FALSE, datum/game_mode/_gamemode, starting_tc = 20)
if(!isitem(parent))
return COMPONENT_INCOMPATIBLE
GLOB.uplinks += src
uplink_items = get_uplink_items(gamemode)
RegisterSignal(COMSIG_PARENT_ATTACKBY, .proc/OnAttackBy)
RegisterSignal(COMSIG_ITEM_ATTACK_SELF, .proc/interact)
owner = _owner
if(owner)
LAZYINITLIST(GLOB.uplink_purchase_logs_by_key)
if(GLOB.uplink_purchase_logs_by_key[owner])
purchase_log = GLOB.uplink_purchase_logs_by_key[owner]
else
purchase_log = new(owner, src)
lockable = _lockable
active = _enabled
gamemode = _gamemode
telecrystals = starting_tc
if(!lockable)
active = TRUE
locked = FALSE
/datum/component/uplink/InheritComponent(datum/component/uplink/U)
lockable |= U.lockable
active |= U.active
if(!gamemode)
gamemode = U.gamemode
telecrystals += U.telecrystals
if(purchase_log && U.purchase_log)
purchase_log.MergeWithAndDel(U.purchase_log)
/datum/component/uplink/Destroy()
GLOB.uplinks -= src
gamemode = null
return ..()
/datum/component/uplink/proc/LoadTC(mob/user, obj/item/stack/telecrystal/TC, silent = FALSE)
if(!silent)
to_chat(user, "<span class='notice'>You slot [TC] into [parent] and charge its internal uplink.</span>")
var/amt = TC.amount
telecrystals += amt
TC.use(amt)
/datum/component/uplink/proc/set_gamemode(_gamemode)
gamemode = _gamemode
uplink_items = get_uplink_items(gamemode)
/datum/component/uplink/proc/OnAttackBy(obj/item/I, mob/user)
if(!active)
return //no hitting everyone/everything just to try to slot tcs in!
if(istype(I, /obj/item/stack/telecrystal))
LoadTC(user, I)
for(var/item in subtypesof(/datum/uplink_item))
var/datum/uplink_item/UI = item
var/path = null
if(initial(UI.refund_path))
path = initial(UI.refund_path)
else
path = initial(UI.item)
var/cost = 0
if(initial(UI.refund_amount))
cost = initial(UI.refund_amount)
else
cost = initial(UI.cost)
var/refundable = initial(UI.refundable)
if(I.type == path && refundable && I.check_uplink_validity())
telecrystals += cost
spent_telecrystals -= cost
to_chat(user, "<span class='notice'>[I] refunded.</span>")
qdel(I)
return
/datum/component/uplink/proc/interact(mob/user)
if(locked)
return
active = TRUE
if(user)
ui_interact(user)
/datum/component/uplink/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.inventory_state)
active = TRUE
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "uplink", name, 450, 750, master_ui, state)
ui.set_autoupdate(FALSE) // This UI is only ever opened by one person, and never is updated outside of user input.
ui.set_style("syndicate")
ui.open()
/datum/component/uplink/ui_data(mob/user)
if(!user.mind)
return
var/list/data = list()
data["telecrystals"] = telecrystals
data["lockable"] = lockable
data["categories"] = list()
for(var/category in uplink_items)
var/list/cat = list(
"name" = category,
"items" = (category == selected_cat ? list() : null))
if(category == selected_cat)
for(var/item in uplink_items[category])
var/datum/uplink_item/I = uplink_items[category][item]
if(I.limited_stock == 0)
continue
if(I.restricted_roles.len)
var/is_inaccessible = 1
for(var/R in I.restricted_roles)
if(R == user.mind.assigned_role)
is_inaccessible = 0
if(is_inaccessible)
continue
cat["items"] += list(list(
"name" = I.name,
"cost" = I.cost,
"desc" = I.desc,
))
data["categories"] += list(cat)
return data
/datum/component/uplink/ui_act(action, params)
if(!active)
return
switch(action)
if("buy")
var/item = params["item"]
var/list/buyable_items = list()
for(var/category in uplink_items)
buyable_items += uplink_items[category]
if(item in buyable_items)
var/datum/uplink_item/I = buyable_items[item]
MakePurchase(usr, I)
. = TRUE
if("lock")
active = FALSE
locked = TRUE
telecrystals += hidden_crystals
hidden_crystals = 0
SStgui.close_uis(src)
if("select")
selected_cat = params["category"]
return TRUE
/datum/component/uplink/proc/MakePurchase(mob/user, datum/uplink_item/U)
if(!istype(U))
return
if (!user || user.incapacitated())
return
if(telecrystals < U.cost || U.limited_stock == 0)
return
telecrystals -= U.cost
var/atom/A = U.spawn_item(get_turf(user), src, user)
if(U.purchase_log_vis && purchase_log)
var/obj/item/storage/box/B = A
var/list/atom/logging = list()
if(istype(B) && B.contents.len > 0)
logging |= list(B)
else
logging |= A
for(var/atom/_logging in logging)
purchase_log.LogPurchase(_logging, U.cost)
if(U.limited_stock > 0)
U.limited_stock -= 1
SSblackbox.record_feedback("nested tally", "traitor_uplink_items_bought", 1, list("[initial(U.name)]", "[U.cost]"))
if(ishuman(user) && istype(A, /obj/item))
var/mob/living/carbon/human/H = user
if(H.put_in_hands(A))
to_chat(H, "[A] materializes into your hands!")
else
to_chat(H, "\The [A] materializes onto the floor.")
return TRUE
+23 -1
View File
@@ -31,7 +31,7 @@
.["Show VV To Player"] = "?_src_=vars;[HrefToken(TRUE)];expose=[REF(src)]"
/datum/proc/on_reagent_change()
/datum/proc/on_reagent_change(changetype)
return
@@ -748,6 +748,28 @@
src.give_disease(M)
href_list["datumrefresh"] = href_list["give_spell"]
else if(href_list["ninja"])
if(!check_rights(R_FUN))
return
var/mob/living/carbon/human/M = locate(href_list["ninja"]) in GLOB.carbon_list
if(!istype(M))
to_chat(usr, "This can only be used on instances of type /mob")
return
if(tgalert(usr, "Are you sure you want to make [M] into a ninja?", "Confirmation", "Yes", "No") == "No")
return
if(!M.mind)
M.mind_initialize()
var/datum/antagonist/ninja/hiyah = M.mind.has_antag_datum(/datum/antagonist/ninja)
if(!hiyah)
hiyah = add_ninja(M)
if(hiyah)
hiyah.equip_space_ninja()
href_list["datumrefresh"] = href_list["ninja"]
else if(href_list["gib"])
if(!check_rights(R_FUN))
return
+15 -4
View File
@@ -143,14 +143,25 @@
//Proc to use when you 100% want to infect someone, as long as they aren't immune
/mob/proc/ForceContractDisease(datum/disease/D)
if(!CanContractDisease(D))
return 0
return FALSE
AddDisease(D)
/mob/living/carbon/human/CanContractDisease(datum/disease/D)
if(dna && (VIRUSIMMUNE in dna.species.species_traits) && !D.bypasses_immunity)
return 0
if(dna)
if((VIRUSIMMUNE in dna.species.species_traits) && !D.bypasses_immunity)
return FALSE
var/can_infect = FALSE
for(var/host_type in D.infectable_hosts)
if(host_type in dna.species.species_traits)
can_infect = TRUE
break
if(!can_infect)
return FALSE
for(var/thing in D.required_organs)
if(!((locate(thing) in bodyparts) || (locate(thing) in internal_organs)))
return 0
return FALSE
return ..()
+6 -3
View File
@@ -30,6 +30,8 @@
var/list/required_organs = list()
var/needs_all_cures = TRUE
var/list/strain_data = list() //dna_spread special bullshit
var/list/infectable_hosts = list(SPECIES_ORGANIC) //if the disease can spread on organics, synthetics, or undead
var/process_dead = FALSE //if this ticks while the host is dead
/datum/disease/Destroy()
affected_mob = null
@@ -101,8 +103,9 @@
/datum/disease/proc/cure(add_resistance = TRUE)
if(affected_mob)
if(disease_flags & CAN_RESIST)
if(add_resistance && !(type in affected_mob.resistances))
affected_mob.resistances += type
var/id = GetDiseaseID()
if(add_resistance && !(id in affected_mob.resistances))
affected_mob.resistances += id
remove_virus()
qdel(src)
@@ -119,7 +122,7 @@
/datum/disease/proc/GetDiseaseID()
return type
return "[type]"
//don't use this proc directly. this should only ever be called by cure()
/datum/disease/proc/remove_virus()
-9
View File
@@ -95,15 +95,6 @@
return 0
return 1
// To add special resistances.
/datum/disease/advance/cure(resistance=1)
if(affected_mob)
var/id = "[GetDiseaseID()]"
if(resistance && !(id in affected_mob.resistances))
affected_mob.resistances[id] = id
remove_virus()
qdel(src) //delete the datum to stop it processing
// Returns the advance disease with a different reference memory.
/datum/disease/advance/Copy(process = 0)
return new /datum/disease/advance(process, src, 1)
+332 -197
View File
@@ -9,20 +9,15 @@
base_message_chance = 20 //here used for the overlays
symptom_delay_min = 1
symptom_delay_max = 1
var/hide_healing = FALSE
var/passive_message = "" //random message to infected but not actively healing people
threshold_desc = "<b>Stage Speed 6:</b> Doubles healing speed.<br>\
<b>Stage Speed 11:</b> Triples healing speed.<br>\
<b>Stealth 4:</b> Healing will no longer be visible to onlookers."
/datum/symptom/heal/Start(datum/disease/advance/A)
if(!..())
return
if(A.properties["stealth"] >= 4) //invisible healing
hide_healing = TRUE
if(A.properties["stage_rate"] >= 6) //stronger healing
power = 2
if(A.properties["stage_rate"] >= 11) //even stronger healing
power = 3
/datum/symptom/heal/Activate(datum/disease/advance/A)
if(!..())
@@ -31,299 +26,439 @@
var/mob/living/M = A.affected_mob
switch(A.stage)
if(4, 5)
Heal(M, A)
var/effectiveness = CanHeal(A)
if(!effectiveness)
if(passive_message && prob(2) && passive_message_condition(M))
to_chat(M, passive_message)
return
else
Heal(M, A, effectiveness)
return
/datum/symptom/heal/proc/Heal(mob/living/M, datum/disease/advance/A)
return 1
/datum/symptom/heal/proc/CanHeal(datum/disease/advance/A)
return power
/*
//////////////////////////////////////
/datum/symptom/heal/proc/Heal(mob/living/M, datum/disease/advance/A, actual_power)
return TRUE
Toxin Filter
/datum/symptom/heal/proc/passive_message_condition(mob/living/M)
return TRUE
Little bit hidden.
Lowers resistance tremendously.
Decreases stage speed tremendously.
Decreases transmittablity temrendously.
Fatal Level.
Bonus
Heals toxins in the affected mob's blood stream.
//////////////////////////////////////
*/
/datum/symptom/heal/toxin
name = "Toxic Filter"
desc = "The virus synthesizes regenerative chemicals in the bloodstream, repairing damage caused by toxins."
name = "Starlight Condensation"
desc = "The virus reacts to direct starlight, producing regenerative chemicals that can cure toxin damage."
stealth = 1
resistance = -4
stage_speed = -4
transmittable = -4
resistance = -3
stage_speed = -3
transmittable = -3
level = 6
passive_message = "<span class='notice'>You miss the feeling of starlight on your skin.</span>"
var/nearspace_penalty = 0.3
threshold_desc = "<b>Stage Speed 6:</b> Increases healing speed.<br>\
<b>Transmission 6:</b> Removes penalty for only being close to space."
/datum/symptom/heal/toxin/Heal(mob/living/M, datum/disease/advance/A)
var/heal_amt = 1 * power
if(M.toxloss > 0 && prob(base_message_chance) && !hide_healing)
new /obj/effect/temp_visual/heal(get_turf(M), "#66FF99")
/datum/symptom/heal/toxin/Start(datum/disease/advance/A)
if(!..())
return
if(A.properties["transmission"] >= 6)
nearspace_penalty = 1
if(A.properties["stage_rate"] >= 6)
power = 2
/datum/symptom/heal/toxin/CanHeal(datum/disease/advance/A)
var/mob/living/M = A.affected_mob
if(istype(get_turf(M), /turf/open/space))
return power
else
for(var/turf/T in view(M, 2))
if(istype(T, /turf/open/space))
return power * nearspace_penalty
/datum/symptom/heal/toxin/Heal(mob/living/M, datum/disease/advance/A, actual_power)
var/heal_amt = actual_power
if(M.getToxLoss() && prob(5))
to_chat(M, "<span class='notice'>Your skin tingles as the starlight purges toxins from your bloodstream.</span>")
M.adjustToxLoss(-heal_amt)
return 1
/*
//////////////////////////////////////
/datum/symptom/heal/toxin/passive_message_condition(mob/living/M)
if(M.getToxLoss())
return TRUE
return FALSE
Apoptosis
Lowers resistance.
Decreases stage speed.
Decreases transmittablity.
Bonus
Heals toxins in the affected mob's blood stream faster.
//////////////////////////////////////
*/
/datum/symptom/heal/toxin/plus
name = "Apoptoxin filter"
/datum/symptom/heal/chem
name = "Toxolysis"
stealth = 0
resistance = -2
stage_speed = -2
transmittable = -2
level = 8
desc = "The virus stimulates production of special stem cells in the bloodstream, causing rapid reparation of any damage caused by toxins."
level = 7
var/food_conversion = FALSE
desc = "The virus rapidly breaks down any foreign chemicals in the bloodstream."
threshold_desc = "<b>Resistance 7:</b> Increases chem removal speed.<br>\
<b>Stage Speed 6:</b> Consumed chemicals nourish the host."
/datum/symptom/heal/toxin/plus/Heal(mob/living/M, datum/disease/advance/A)
var/heal_amt = 2 * power
if(M.toxloss > 0 && prob(base_message_chance) && !hide_healing)
new /obj/effect/temp_visual/heal(get_turf(M), "#00FF00")
M.adjustToxLoss(-heal_amt)
/datum/symptom/heal/chem/Start(datum/disease/advance/A)
if(!..())
return
if(A.properties["stage_rate"] >= 6)
food_conversion = TRUE
if(A.properties["resistance"] >= 7)
power = 2
/datum/symptom/heal/chem/Heal(mob/living/M, datum/disease/advance/A, actual_power)
for(var/datum/reagent/R in M.reagents.reagent_list) //Not just toxins!
M.reagents.remove_reagent(R.id, actual_power)
if(food_conversion)
M.nutrition += 0.3
if(prob(2))
to_chat(M, "<span class='notice'>You feel a mild warmth as your blood purifies itself.</span>")
return 1
/*
//////////////////////////////////////
Regeneration
Little bit hidden.
Lowers resistance tremendously.
Decreases stage speed tremendously.
Decreases transmittablity temrendously.
Fatal Level.
/datum/symptom/heal/metabolism
name = "Metabolic Boost"
stealth = -1
resistance = -2
stage_speed = 2
transmittable = 1
level = 7
var/triple_metabolism = FALSE
var/reduced_hunger = FALSE
desc = "The virus causes the host's metabolism to accelerate rapidly, making them process chemicals twice as fast,\
but also causing increased hunger."
threshold_desc = "<b>Stealth 3:</b> Reduces hunger rate.<br>\
<b>Stage Speed 10:</b> Chemical metabolization is tripled instead of doubled."
Bonus
Heals brute damage slowly over time.
/datum/symptom/heal/metabolism/Start(datum/disease/advance/A)
if(!..())
return
if(A.properties["stage_rate"] >= 10)
triple_metabolism = TRUE
if(A.properties["stealth"] >= 3)
reduced_hunger = TRUE
//////////////////////////////////////
*/
/datum/symptom/heal/metabolism/Heal(mob/living/carbon/C, datum/disease/advance/A, actual_power)
if(!istype(C))
return
C.reagents.metabolize(C, can_overdose=TRUE) //this works even without a liver; it's intentional since the virus is metabolizing by itself
if(triple_metabolism)
C.reagents.metabolize(C, can_overdose=TRUE)
C.overeatduration = max(C.overeatduration - 2, 0)
var/lost_nutrition = 9 - (reduced_hunger * 5)
C.nutrition = max(C.nutrition - (lost_nutrition * HUNGER_FACTOR), 0) //Hunger depletes at 10x the normal speed
if(prob(2))
to_chat(C, "<span class='notice'>You feel an odd gurgle in your stomach, as if it was working much faster than normal.</span>")
return 1
/datum/symptom/heal/brute
name = "Regeneration"
desc = "The virus stimulates the regenerative process in the host, causing faster wound healing."
name = "Cellular Molding"
desc = "The virus is able to shift cells around when in conditions of high heat, repairing existing physical damage."
stealth = 1
resistance = -4
stage_speed = -4
transmittable = -4
resistance = -3
stage_speed = -3
transmittable = -3
level = 6
passive_message = "<span class='notice'>You feel the flesh pulsing under your skin for a moment, but it's too cold to move.</span>"
threshold_desc = "<b>Stage Speed 8:</b> Doubles healing speed."
/datum/symptom/heal/brute/Heal(mob/living/carbon/M, datum/disease/advance/A)
var/heal_amt = 2 * power
/datum/symptom/heal/brute/Start(datum/disease/advance/A)
if(!..())
return
if(A.properties["stage_rate"] >= 8)
power = 2
/datum/symptom/heal/brute/CanHeal(datum/disease/advance/A)
var/mob/living/M = A.affected_mob
switch(M.bodytemperature)
if(0 to 340)
return FALSE
if(340 to BODYTEMP_HEAT_DAMAGE_LIMIT)
. = 0.3 * power
if(BODYTEMP_HEAT_DAMAGE_LIMIT to 400)
. = 0.75 * power
if(400 to 460)
. = power
else
. = 1.5 * power
if(M.on_fire)
. *= 2
/datum/symptom/heal/brute/Heal(mob/living/carbon/M, datum/disease/advance/A, actual_power)
var/heal_amt = 2 * actual_power
var/list/parts = M.get_damaged_bodyparts(1,0) //brute only
if(!parts.len)
return
if(prob(5))
to_chat(M, "<span class='notice'>You feel your flesh moving beneath your heated skin, mending your wounds.</span>")
for(var/obj/item/bodypart/L in parts)
if(L.heal_damage(heal_amt/parts.len, 0))
M.update_damage_overlays()
if(prob(base_message_chance) && !hide_healing)
new /obj/effect/temp_visual/heal(get_turf(M), "#FF3333")
return 1
/datum/symptom/heal/brute/passive_message_condition(mob/living/M)
if(M.getBruteLoss())
return TRUE
return FALSE
/*
//////////////////////////////////////
Flesh Mending
No resistance change.
Decreases stage speed.
Decreases transmittablity.
Fatal Level.
Bonus
Heals brute damage over time. Turns cloneloss into burn damage.
//////////////////////////////////////
*/
/datum/symptom/heal/brute/plus
name = "Flesh Mending"
desc = "The virus rapidly mutates into body cells, effectively allowing it to quickly fix the host's wounds."
/datum/symptom/heal/coma
name = "Regenerative Coma"
desc = "The virus causes the host to fall into a death-like coma when severely damaged, then rapidly fixes the damage."
stealth = 0
resistance = 0
stage_speed = -2
transmittable = -2
level = 8
passive_message = "<span class='notice'>The pain from your wounds makes you feel oddly sleepy...</span>"
var/deathgasp = FALSE
var/active_coma = FALSE //to prevent multiple coma procs
threshold_desc = "<b>Stealth 2:</b> Host appears to die when falling into a coma.<br>\
<b>Stage Speed 7:</b> Increases healing speed."
/datum/symptom/heal/brute/plus/Heal(mob/living/carbon/M, datum/disease/advance/A)
var/heal_amt = 4 * power
/datum/symptom/heal/coma/Start(datum/disease/advance/A)
if(!..())
return
if(A.properties["stage_rate"] >= 7)
power = 1.5
if(A.properties["stealth"] >= 2)
deathgasp = TRUE
var/list/parts = M.get_damaged_bodyparts(1,0) //brute only
/datum/symptom/heal/coma/CanHeal(datum/disease/advance/A)
var/mob/living/M = A.affected_mob
if(M.status_flags & FAKEDEATH)
return power
else if(M.IsUnconscious() || M.stat == UNCONSCIOUS)
return power * 0.9
else if(M.stat == SOFT_CRIT)
return power * 0.5
else if(M.IsSleeping())
return power * 0.25
else if(M.getBruteLoss() + M.getFireLoss() >= 70 && !active_coma)
to_chat(M, "<span class='warning'>You feel yourself slip into a regenerative coma...</span>")
active_coma = TRUE
addtimer(CALLBACK(src, .proc/coma, M), 60)
if(M.getCloneLoss() > 0)
M.adjustCloneLoss(-1)
M.take_bodypart_damage(0, 1) //Deals BURN damage, which is not cured by this symptom
if(!hide_healing)
new /obj/effect/temp_visual/heal(get_turf(M), "#33FFCC")
/datum/symptom/heal/coma/proc/coma(mob/living/M)
if(deathgasp)
M.emote("deathgasp")
M.status_flags |= FAKEDEATH
M.update_stat()
M.update_canmove()
addtimer(CALLBACK(src, .proc/uncoma, M), 300)
/datum/symptom/heal/coma/proc/uncoma(mob/living/M)
if(!active_coma)
return
active_coma = FALSE
M.status_flags &= ~FAKEDEATH
M.update_stat()
M.update_canmove()
/datum/symptom/heal/coma/Heal(mob/living/carbon/M, datum/disease/advance/A, actual_power)
var/heal_amt = 4 * actual_power
var/list/parts = M.get_damaged_bodyparts(1,1)
if(!parts.len)
return
for(var/obj/item/bodypart/L in parts)
if(L.heal_damage(heal_amt/parts.len, 0))
if(L.heal_damage(heal_amt/parts.len, heal_amt/parts.len))
M.update_damage_overlays()
if(prob(base_message_chance) && !hide_healing)
new /obj/effect/temp_visual/heal(get_turf(M), "#CC1100")
if(active_coma && M.getBruteLoss() + M.getFireLoss() == 0)
uncoma(M)
return 1
/*
//////////////////////////////////////
Tissue Regrowth
Little bit hidden.
Lowers resistance tremendously.
Decreases stage speed tremendously.
Decreases transmittablity temrendously.
Fatal Level.
Bonus
Heals burn damage slowly over time.
//////////////////////////////////////
*/
/datum/symptom/heal/coma/passive_message_condition(mob/living/M)
if((M.getBruteLoss() + M.getFireLoss()) > 30)
return TRUE
return FALSE
/datum/symptom/heal/burn
name = "Tissue Regrowth"
desc = "The virus recycles dead and burnt tissues, speeding up the healing of damage caused by burns."
name = "Tissue Hydration"
desc = "The virus uses excess water inside and outside the body to repair burned tisue cells."
stealth = 1
resistance = -4
stage_speed = -4
transmittable = -4
resistance = -3
stage_speed = -3
transmittable = -3
level = 6
passive_message = "<span class='notice'>Your burned skin feels oddly dry...</span>"
var/absorption_coeff = 1
threshold_desc = "<b>Resistance 5:</b> Water is consumed at a much slower rate.<br>\
<b>Stage Speed 7:</b> Increases healing speed."
/datum/symptom/heal/burn/Heal(mob/living/carbon/M, datum/disease/advance/A)
var/heal_amt = 2 * power
/datum/symptom/heal/burn/Start(datum/disease/advance/A)
if(!..())
return
if(A.properties["stage_rate"] >= 7)
power = 2
if(A.properties["stealth"] >= 2)
absorption_coeff = 0.25
/datum/symptom/heal/burn/CanHeal(datum/disease/advance/A)
. = 0
var/mob/living/M = A.affected_mob
if(M.fire_stacks < 0)
M.fire_stacks = min(M.fire_stacks + 1 * absorption_coeff, 0)
. += power
if(M.reagents.has_reagent("holywater"))
M.reagents.remove_reagent("holywater", 0.5 * absorption_coeff)
. += power * 0.75
else if(M.reagents.has_reagent("water"))
M.reagents.remove_reagent("water", 0.5 * absorption_coeff)
. += power * 0.5
/datum/symptom/heal/burn/Heal(mob/living/carbon/M, datum/disease/advance/A, actual_power)
var/heal_amt = 2 * actual_power
var/list/parts = M.get_damaged_bodyparts(0,1) //burn only
if(!parts.len)
return
if(prob(5))
to_chat(M, "<span class='notice'>You feel yourself absorbing the water around you to soothe your burned skin.</span>")
for(var/obj/item/bodypart/L in parts)
if(L.heal_damage(0, heal_amt/parts.len))
M.update_damage_overlays()
if(prob(base_message_chance) && !hide_healing)
new /obj/effect/temp_visual/heal(get_turf(M), "#FF9933")
return 1
/datum/symptom/heal/burn/passive_message_condition(mob/living/M)
if(M.getFireLoss())
return TRUE
return FALSE
/*
//////////////////////////////////////
Heat Resistance //Needs a better name
No resistance change.
Decreases stage speed.
Decreases transmittablity.
Fatal Level.
Bonus
Heals burn damage over time, and helps stabilize body temperature.
//////////////////////////////////////
*/
/datum/symptom/heal/burn/plus
name = "Temperature Adaptation"
desc = "The virus quickly balances body heat, while also replacing tissues damaged by external sources."
/datum/symptom/heal/plasma
name = "Plasma Fixation"
desc = "The virus draws plasma from the atmosphere and from inside the body to stabilize body temperature and heal burns."
stealth = 0
resistance = 0
stage_speed = -2
transmittable = -2
level = 8
passive_message = "<span class='notice'>You feel an odd attraction to plasma.</span>"
var/temp_rate = 1
threshold_desc = "<b>Transmission 6:</b> Increases temperature adjustment rate.<br>\
<b>Stage Speed 7:</b> Increases healing speed."
/datum/symptom/heal/burn/plus/Heal(mob/living/carbon/M, datum/disease/advance/A)
var/heal_amt = 4 * power
/datum/symptom/heal/plasma/Start(datum/disease/advance/A)
if(!..())
return
if(A.properties["stage_rate"] >= 7)
power = 2
if(A.properties["trasmission"] >= 6)
temp_rate = 4
/datum/symptom/heal/plasma/CanHeal(datum/disease/advance/A)
var/mob/living/M = A.affected_mob
var/datum/gas_mixture/environment
var/list/gases
. = 0
if(M.loc)
environment = M.loc.return_air()
if(environment)
gases = environment.gases
if(gases["plasma"] && gases["plasma"][MOLES] > gases["plasma"][GAS_META][META_GAS_MOLES_VISIBLE]) //if there's enough plasma in the air to see
. += power * 0.5
if(M.reagents.has_reagent("plasma"))
. += power * 0.75
/datum/symptom/heal/plasma/Heal(mob/living/carbon/M, datum/disease/advance/A, actual_power)
var/heal_amt = 4 * actual_power
var/list/parts = M.get_damaged_bodyparts(0,1) //burn only
if(prob(5))
to_chat(M, "<span class='notice'>You feel yourself absorbing plasma inside and around you...</span>")
if(M.bodytemperature > 310)
M.bodytemperature = max(310, M.bodytemperature - (10 * heal_amt * TEMPERATURE_DAMAGE_COEFFICIENT))
M.bodytemperature = max(310, M.bodytemperature - (20 * temp_rate * TEMPERATURE_DAMAGE_COEFFICIENT))
if(prob(5))
to_chat(M, "<span class='notice'>You feel less hot.</span>")
else if(M.bodytemperature < 311)
M.bodytemperature = min(310, M.bodytemperature + (10 * heal_amt * TEMPERATURE_DAMAGE_COEFFICIENT))
M.bodytemperature = min(310, M.bodytemperature + (20 * temp_rate * TEMPERATURE_DAMAGE_COEFFICIENT))
if(prob(5))
to_chat(M, "<span class='notice'>You feel warmer.</span>")
if(!parts.len)
return
if(prob(5))
to_chat(M, "<span class='notice'>The pain from your burns fades rapidly.</span>")
for(var/obj/item/bodypart/L in parts)
if(L.heal_damage(0, heal_amt/parts.len))
M.update_damage_overlays()
if(prob(base_message_chance) && !hide_healing)
new /obj/effect/temp_visual/heal(get_turf(M), "#CC6600")
return 1
/*
//////////////////////////////////////
DNA Restoration
Not well hidden.
Lowers resistance minorly.
Does not affect stage speed.
Decreases transmittablity greatly.
Very high level.
Bonus
Heals brain damage, treats radiation, cleans SE of non-power mutations.
//////////////////////////////////////
*/
/datum/symptom/heal/dna
name = "Deoxyribonucleic Acid Restoration"
desc = "The virus repairs the host's genome, purging negative mutations."
/datum/symptom/heal/radiation
name = "Radioactive Resonance"
desc = "The virus uses radiation to fix damage through dna mutations."
stealth = -1
resistance = -1
resistance = -2
stage_speed = 0
transmittable = -3
level = 5
symptom_delay_min = 3
symptom_delay_max = 8
threshold_desc = "<b>Stage Speed 6:</b> Additionally heals brain damage.<br>\
<b>Stage Speed 11:</b> Increases brain damage healing."
level = 6
symptom_delay_min = 1
symptom_delay_max = 1
passive_message = "<span class='notice'>Your skin glows faintly for a moment.</span>"
var/cellular_damage = FALSE
threshold_desc = "<b>Transmission 6:</b> Additionally heals cellular damage.<br>\
<b>Resistance 7:</b> Increases healing speed."
/datum/symptom/heal/dna/Heal(mob/living/carbon/M, datum/disease/advance/A)
var/amt_healed = 2 * (power - 1)
M.adjustBrainLoss(-amt_healed)
//Non-power mutations, excluding race, so the virus does not force monkey -> human transformations.
var/list/unclean_mutations = (GLOB.not_good_mutations|GLOB.bad_mutations) - GLOB.mutations_list[RACEMUT]
M.dna.remove_mutation_group(unclean_mutations)
M.radiation = max(M.radiation - (2 * amt_healed), 0)
/datum/symptom/heal/radiation/Start(datum/disease/advance/A)
if(!..())
return
if(A.properties["resistance"] >= 7)
power = 2
if(A.properties["trasmission"] >= 6)
cellular_damage = TRUE
/datum/symptom/heal/radiation/CanHeal(datum/disease/advance/A)
var/mob/living/M = A.affected_mob
switch(M.radiation)
if(0)
return FALSE
if(1 to RAD_MOB_SAFE)
return 0.25
if(RAD_MOB_SAFE to RAD_BURN_THRESHOLD)
return 0.5
if(RAD_BURN_THRESHOLD to RAD_MOB_MUTATE)
return 0.75
if(RAD_MOB_MUTATE to RAD_MOB_KNOCKDOWN)
return 1
else
return 1.5
/datum/symptom/heal/radiation/Heal(mob/living/carbon/M, datum/disease/advance/A, actual_power)
var/heal_amt = actual_power
if(cellular_damage)
M.adjustCloneLoss(-heal_amt * 0.5)
var/list/parts = M.get_damaged_bodyparts(1,1)
if(!parts.len)
return
if(prob(4))
to_chat(M, "<span class='notice'>Your skin glows faintly, and you feel your wounds mending themselves.</span>")
for(var/obj/item/bodypart/L in parts)
if(L.heal_damage(heal_amt/parts.len, heal_amt/parts.len))
M.update_damage_overlays()
return 1
@@ -0,0 +1,30 @@
/datum/symptom/undead_adaptation
name = "Necrotic Metabolism"
desc = "The virus is able to thrive and act even within dead hosts."
stealth = 2
resistance = -2
stage_speed = 1
transmittable = 0
level = 5
severity = 0
/datum/symptom/undead_adaptation/Start(datum/disease/advance/A)
if(!..())
return
A.process_dead = TRUE
A.infectable_hosts |= SPECIES_UNDEAD
/datum/symptom/inorganic_adaptation
name = "Inorganic Biology"
desc = "The virus can survive and replicate even in an inorganic environment, increasing its resistance and infection rate."
stealth = -1
resistance = 4
stage_speed = -2
transmittable = 3
level = 5
severity = 0
/datum/symptom/inorganic_adaptation/Start(datum/disease/advance/A)
if(!..())
return
A.infectable_hosts |= SPECIES_INORGANIC
@@ -1,56 +1,6 @@
/*
//////////////////////////////////////
Weight Gain
Very Very Noticable.
Decreases resistance.
Decreases stage speed.
Reduced transmittable.
Intense Level.
Bonus
Increases the weight gain of the mob,
forcing it to eventually turn fat.
//////////////////////////////////////
*/
/datum/symptom/weight_gain
name = "Weight Gain"
desc = "The virus mutates the host's metabolism, making it gain weight much faster than normal."
stealth = -3
resistance = -3
stage_speed = -2
transmittable = -2
level = 4
severity = 3
base_message_chance = 100
symptom_delay_min = 15
symptom_delay_max = 45
threshold_desc = "<b>Stealth 4:</b> The symptom is less noticeable."
/datum/symptom/weight_gain/Start(datum/disease/advance/A)
if(!..())
return
if(A.properties["stealth"] >= 4) //warn less often
base_message_chance = 25
/datum/symptom/weight_gain/Activate(datum/disease/advance/A)
if(!..())
return
var/mob/living/M = A.affected_mob
switch(A.stage)
if(1, 2, 3, 4)
if(prob(base_message_chance))
to_chat(M, "<span class='warning'>[pick("You feel blubbery.", "Your stomach hurts.")]</span>")
else
M.overeatduration = min(M.overeatduration + 100, 600)
M.nutrition = min(M.nutrition + 100, NUTRITION_LEVEL_FULL)
/*
//////////////////////////////////////
Weight Loss
Very Very Noticable.
@@ -70,8 +20,8 @@ Bonus
name = "Weight Loss"
desc = "The virus mutates the host's metabolism, making it almost unable to gain nutrition from food."
stealth = -3
resistance = -2
stealth = -2
resistance = 2
stage_speed = -2
transmittable = -2
level = 3
@@ -99,43 +49,3 @@ Bonus
to_chat(M, "<span class='warning'><i>[pick("So hungry...", "You'd kill someone for a bite of food...", "Hunger cramps seize you...")]</i></span>")
M.overeatduration = max(M.overeatduration - 100, 0)
M.nutrition = max(M.nutrition - 100, 0)
/*
//////////////////////////////////////
Weight Even
Very Noticable.
Decreases resistance.
Decreases stage speed.
Reduced transmittable.
High level.
Bonus
Causes the weight of the mob to
be even, meaning eating isn't
required anymore.
//////////////////////////////////////
*/
/datum/symptom/weight_even
name = "Weight Even"
desc = "The virus alters the host's metabolism, making it far more efficient then normal, and synthesizing nutrients from normally unedible sources."
stealth = -3
resistance = -2
stage_speed = -2
transmittable = -2
level = 4
symptom_delay_min = 5
symptom_delay_max = 5
/datum/symptom/weight_even/Activate(datum/disease/advance/A)
if(!..())
return
var/mob/living/M = A.affected_mob
switch(A.stage)
if(4, 5)
M.overeatduration = 0
M.nutrition = NUTRITION_LEVEL_WELL_FED + 50
+1
View File
@@ -10,6 +10,7 @@
viable_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/monkey)
desc = "If left untreated subject will regurgitate bees."
severity = VIRUS_SEVERITY_MEDIUM
infectable_hosts = list(SPECIES_ORGANIC, SPECIES_UNDEAD) //bees nesting in corpses
/datum/disease/beesease/stage_act()
..()
+2
View File
@@ -10,6 +10,8 @@
permeability_mod = 0.75
desc = "This disease disrupts the magnetic field of your body, making it act as if a powerful magnet. Injections of iron help stabilize the field."
severity = VIRUS_SEVERITY_MEDIUM
infectable_hosts = list(SPECIES_ORGANIC, SPECIES_ROBOTIC)
process_dead = TRUE
/datum/disease/magnitis/stage_act()
..()
+2
View File
@@ -11,6 +11,8 @@
viable_mobtypes = list(/mob/living/carbon/human)
desc = "Subject is possesed by the vengeful spirit of a parrot. Call the priest."
severity = VIRUS_SEVERITY_MEDIUM
infectable_hosts = list(SPECIES_ORGANIC, SPECIES_UNDEAD, SPECIES_INORGANIC, SPECIES_ROBOTIC)
bypasses_immunity = TRUE //2spook
var/mob/living/simple_animal/parrot/Poly/ghost/parrot
/datum/disease/parrot_possession/stage_act()
+1
View File
@@ -9,6 +9,7 @@
viable_mobtypes = list(/mob/living/carbon/human)
permeability_mod = 1
severity = VIRUS_SEVERITY_BIOHAZARD
process_dead = TRUE
/datum/disease/rhumba_beat/stage_act()
..()
+2 -1
View File
@@ -127,7 +127,7 @@
stage4 = list("<span class='danger'>Your skin feels very loose.</span>", "<span class='danger'>You can feel... something...inside you.</span>")
stage5 = list("<span class='danger'>Your skin feels as if it's about to burst off!</span>")
new_form = /mob/living/silicon/robot
infectable_hosts = list(SPECIES_ORGANIC, SPECIES_UNDEAD, SPECIES_ROBOTIC)
/datum/disease/transformation/robot/stage_act()
..()
@@ -240,3 +240,4 @@
stage4 = list("<span class='danger'>You're ravenous.</span>")
stage5 = list("<span class='danger'>You have become a morph.</span>")
new_form = /mob/living/simple_animal/hostile/morph
infectable_hosts = list(SPECIES_ORGANIC, SPECIES_INORGANIC, SPECIES_UNDEAD) //magic!
@@ -68,6 +68,7 @@
var/obj/item/oldcell = locate (/obj/item/stock_parts/cell) in m
QDEL_NULL(oldcell)
m.CheckParts(holder.contents)
SSblackbox.record_feedback("tally", "mechas_created", 1, m.name)
QDEL_NULL(holder)
/datum/construction/proc/set_desc(index as num)
+1 -1
View File
@@ -23,7 +23,7 @@
if(line)
var/tmcommit = testmerge[line]["commit"]
log_world("Test merge active of PR #[line] commit [tmcommit]")
SSblackbox.add_details("testmerged_prs","[line]|[tmcommit]")
SSblackbox.record_feedback("nested tally", "testmerged_prs", 1, list("[line]", "[tmcommit]"))
log_world("Based off origin/master commit [originmastercommit]")
else
log_world(originmastercommit)
+3
View File
@@ -119,6 +119,9 @@
playSpecials(destturf,effectout,soundout)
if(ismegafauna(teleatom))
message_admins("[teleatom] [ADMIN_FLW(teleatom)] has teleported from [ADMIN_COORDJMP(curturf)] to [ADMIN_COORDJMP(destturf)].")
if(ismob(teleatom))
var/mob/M = teleatom
M.cancel_camera()
return 1
/datum/teleport/proc/teleport()
+128
View File
@@ -1,5 +1,14 @@
#define HOLOPAD_MAX_DIAL_TIME 200
#define HOLORECORD_DELAY "delay"
#define HOLORECORD_SAY "say"
#define HOLORECORD_SOUND "sound"
#define HOLORECORD_LANGUAGE "lang"
#define HOLORECORD_PRESET "preset"
#define HOLORECORD_RENAME "rename"
#define HOLORECORD_MAX_LENGTH 200
/mob/camera/aiEye/remote/holo/setLoc()
. = ..()
var/obj/machinery/holopad/H = origin
@@ -184,3 +193,122 @@
/datum/action/innate/end_holocall/Activate()
hcall.Disconnect(hcall.calling_holopad)
//RECORDS
/datum/holorecord
var/caller_name = "Unknown" //Caller name
var/image/caller_image
var/list/entries = list()
var/language = /datum/language/common //Initial language, can be changed by HOLORECORD_LANGUAGE entries
/obj/item/disk/holodisk
name = "holorecord disk"
desc = "Stores recorder holocalls."
icon_state = "holodisk"
var/datum/holorecord/record
//Preset variables
var/preset_image_type
var/preset_record_text
/obj/item/disk/holodisk/Initialize(mapload)
. = ..()
if(preset_record_text)
build_record()
/obj/item/disk/holodisk/Destroy()
QDEL_NULL(record)
return ..()
/obj/item/disk/holodisk/proc/build_record()
record = new
var/list/lines = splittext(preset_record_text,"\n")
for(var/line in lines)
var/prepared_line = trim(line)
if(!length(prepared_line))
continue
var/splitpoint = findtext(prepared_line," ")
if(!splitpoint)
continue
var/command = copytext(prepared_line,1,splitpoint)
var/value = copytext(prepared_line,splitpoint+1)
switch(command)
if("DELAY")
var/delay_value = text2num(value)
if(!delay_value)
continue
record.entries += list(list(HOLORECORD_DELAY,delay_value))
if("NAME")
if(!record.caller_name)
record.caller_name = value
else
record.entries += list(list(HOLORECORD_RENAME,value))
if("SAY")
record.entries += list(list(HOLORECORD_SAY,value))
if("SOUND")
record.entries += list(list(HOLORECORD_SOUND,value))
if("LANGUAGE")
var/lang_type = text2path(value)
if(ispath(lang_type,/datum/language))
record.entries += list(list(HOLORECORD_LANGUAGE,lang_type))
if("PRESET")
var/preset_type = text2path(value)
if(ispath(preset_type,/datum/preset_holoimage))
record.entries += list(list(HOLORECORD_PRESET,preset_type))
if(!preset_image_type)
record.caller_image = image('icons/mob/animal.dmi',"old")
else
var/datum/preset_holoimage/H = new preset_image_type
record.caller_image = H.build_image()
//These build caller image from outfit and some additional data, for use by mappers for ruin holorecords
/datum/preset_holoimage
var/nonhuman_mobtype //Fill this if you just want something nonhuman
var/outfit_type
var/species_type = /datum/species/human
/datum/preset_holoimage/proc/build_image()
if(nonhuman_mobtype)
var/mob/living/L = nonhuman_mobtype
. = image(initial(L.icon),initial(L.icon_state))
else
var/mob/living/carbon/human/dummy/mannequin = generate_or_wait_for_human_dummy("HOLODISK_PRESET")
if(species_type)
mannequin.set_species(species_type)
if(outfit_type)
mannequin.equipOutfit(outfit_type,TRUE)
mannequin.setDir(SOUTH)
COMPILE_OVERLAYS(mannequin)
. = getFlatIcon(mannequin)
unset_busy_human_dummy("HOLODISK_PRESET")
/obj/item/disk/holodisk/example
preset_image_type = /datum/preset_holoimage/clown
preset_record_text = {"
NAME Clown
DELAY 10
SAY Why did the chaplain cross the maint ?
DELAY 20
SAY He wanted to get to the other side!
SOUND clownstep
DELAY 30
LANGUAGE /datum/language/narsie
SAY Helped him get there!
DELAY 10
SAY ALSO IM SECRETLY A GORILLA
DELAY 10
PRESET /datum/preset_holoimage/gorilla
NAME Gorilla
LANGUAGE /datum/language/common
SAY OOGA
DELAY 20"}
/datum/preset_holoimage/engineer
outfit_type = /datum/outfit/job/engineer/gloved/rig
/datum/preset_holoimage/gorilla
nonhuman_mobtype = /mob/living/simple_animal/hostile/gorilla
/datum/preset_holoimage/clown
outfit_type = /datum/outfit/job/clown
+16 -2
View File
@@ -1,12 +1,15 @@
/* HUD DATUMS */
GLOBAL_LIST_EMPTY(all_huds)
//GLOBAL HUD LIST
GLOBAL_LIST_INIT(huds, list(
DATA_HUD_SECURITY_BASIC = new/datum/atom_hud/data/human/security/basic(),
DATA_HUD_SECURITY_ADVANCED = new/datum/atom_hud/data/human/security/advanced(),
DATA_HUD_MEDICAL_BASIC = new/datum/atom_hud/data/human/medical/basic(),
DATA_HUD_MEDICAL_ADVANCED = new/datum/atom_hud/data/human/medical/advanced(),
DATA_HUD_DIAGNOSTIC = new/datum/atom_hud/data/diagnostic(),
DATA_HUD_DIAGNOSTIC_BASIC = new/datum/atom_hud/data/diagnostic/basic(),
DATA_HUD_DIAGNOSTIC_ADVANCED = new/datum/atom_hud/data/diagnostic/advanced(),
ANTAG_HUD_CULT = new/datum/atom_hud/antag(),
ANTAG_HUD_REV = new/datum/atom_hud/antag(),
ANTAG_HUD_OPS = new/datum/atom_hud/antag(),
@@ -28,6 +31,17 @@ GLOBAL_LIST_INIT(huds, list(
var/list/mob/hudusers = list() //list with all mobs who can see the hud
var/list/hud_icons = list() //these will be the indexes for the atom's hud_list
/datum/atom_hud/New()
GLOB.all_huds += src
/datum/atom_hud/Destroy()
for(var/v in hudusers)
remove_hud_from(v)
for(var/v in hudatoms)
remove_from_hud(v)
GLOB.all_huds -= src
return ..()
/datum/atom_hud/proc/remove_hud_from(mob/M)
if(!M || !hudusers[M])
return
@@ -77,7 +91,7 @@ GLOBAL_LIST_INIT(huds, list(
//MOB PROCS
/mob/proc/reload_huds()
for(var/datum/atom_hud/hud in (GLOB.huds|GLOB.active_alternate_appearances))
for(var/datum/atom_hud/hud in GLOB.all_huds)
if(hud && hud.hudusers[src])
for(var/atom/A in hud.hudatoms)
hud.add_to_single_hud(src, A)
@@ -10,7 +10,7 @@
/datum/looping_sound/supermatter
mid_sounds = list('sound/machines/sm/supermatter1.ogg'=1,'sound/machines/sm/supermatter2.ogg'=1,'sound/machines/sm/supermatter3.ogg'=1)
mid_length = 6
mid_length = 10
volume = 1
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+9 -12
View File
@@ -299,9 +299,7 @@
to_chat(traitor_mob, "Unfortunately, [employer] wasn't able to get you an Uplink.")
. = 0
else
var/obj/item/device/uplink/U = new(uplink_loc)
U.owner = "[traitor_mob.key]"
uplink_loc.hidden_uplink = U
uplink_loc.LoadComponent(/datum/component/uplink, traitor_mob.key)
if(uplink_loc == R)
R.traitor_frequency = sanitize_frequency(rand(MIN_FREQ, MAX_FREQ))
@@ -717,7 +715,7 @@
if(((src in SSticker.mode.traitors) || (src in SSticker.mode.syndicates)) && ishuman(current))
text = "Uplink: <a href='?src=[REF(src)];common=uplink'>give</a>"
var/obj/item/device/uplink/U = find_syndicate_uplink()
var/datum/component/uplink/U = find_syndicate_uplink()
if(U)
text += " | <a href='?src=[REF(src)];common=takeuplink'>take</a>"
if (check_rights(R_FUN, 0))
@@ -1306,7 +1304,7 @@
log_admin("[key_name(usr)] removed [current]'s uplink.")
if("crystals")
if(check_rights(R_FUN, 0))
var/obj/item/device/uplink/U = find_syndicate_uplink()
var/datum/component/uplink/U = find_syndicate_uplink()
if(U)
var/crystals = input("Amount of telecrystals for [key]","Syndicate uplink", U.telecrystals) as null | num
if(!isnull(crystals))
@@ -1335,15 +1333,14 @@
/datum/mind/proc/find_syndicate_uplink()
var/list/L = current.GetAllContents()
for (var/obj/item/I in L)
if (I.hidden_uplink)
return I.hidden_uplink
return null
for (var/i in L)
var/atom/movable/I = i
. = I.GetComponent(/datum/component/uplink)
if(.)
break
/datum/mind/proc/take_uplink()
var/obj/item/device/uplink/H = find_syndicate_uplink()
if(H)
qdel(H)
qdel(find_syndicate_uplink())
/datum/mind/proc/make_Traitor()
if(!(has_antag_datum(ANTAG_DATUM_TRAITOR)))
-537
View File
@@ -113,543 +113,6 @@ GLOBAL_LIST_EMPTY(mutations_list)
/datum/mutation/human/proc/get_spans()
return list()
/datum/mutation/human/hulk
name = "Hulk"
quality = POSITIVE
get_chance = 15
lowest_value = 256 * 12
text_gain_indication = "<span class='notice'>Your muscles hurt!</span>"
species_allowed = list("fly") //no skeleton/lizard hulk
health_req = 25
/datum/mutation/human/hulk/on_acquiring(mob/living/carbon/human/owner)
if(..())
return
var/status = CANSTUN | CANKNOCKDOWN | CANUNCONSCIOUS | CANPUSH
owner.status_flags &= ~status
owner.update_body_parts()
/datum/mutation/human/hulk/on_attack_hand(mob/living/carbon/human/owner, atom/target, proximity)
if(proximity) //no telekinetic hulk attack
return target.attack_hulk(owner)
/datum/mutation/human/hulk/on_life(mob/living/carbon/human/owner)
if(owner.health < 0)
on_losing(owner)
to_chat(owner, "<span class='danger'>You suddenly feel very weak.</span>")
/datum/mutation/human/hulk/on_losing(mob/living/carbon/human/owner)
if(..())
return
owner.status_flags |= CANSTUN | CANKNOCKDOWN | CANUNCONSCIOUS | CANPUSH
owner.update_body_parts()
/datum/mutation/human/hulk/say_mod(message)
if(message)
message = "[uppertext(replacetext(message, ".", "!"))]!!"
return message
/datum/mutation/human/telekinesis
name = "Telekinesis"
quality = POSITIVE
get_chance = 20
lowest_value = 256 * 12
text_gain_indication = "<span class='notice'>You feel smarter!</span>"
limb_req = "head"
/datum/mutation/human/telekinesis/New()
..()
visual_indicators |= mutable_appearance('icons/effects/genetics.dmi', "telekinesishead", -MUTATIONS_LAYER)
/datum/mutation/human/telekinesis/get_visual_indicator(mob/living/carbon/human/owner)
return visual_indicators[1]
/datum/mutation/human/telekinesis/on_ranged_attack(mob/living/carbon/human/owner, atom/target)
target.attack_tk(owner)
/datum/mutation/human/cold_resistance
name = "Cold Resistance"
quality = POSITIVE
get_chance = 25
lowest_value = 256 * 12
text_gain_indication = "<span class='notice'>Your body feels warm!</span>"
time_coeff = 5
/datum/mutation/human/cold_resistance/New()
..()
visual_indicators |= mutable_appearance('icons/effects/genetics.dmi', "fire", -MUTATIONS_LAYER)
/datum/mutation/human/cold_resistance/get_visual_indicator(mob/living/carbon/human/owner)
return visual_indicators[1]
/datum/mutation/human/cold_resistance/on_life(mob/living/carbon/human/owner)
if(owner.getFireLoss())
if(prob(1))
owner.heal_bodypart_damage(0,1) //Is this really needed?
/datum/mutation/human/x_ray
name = "X Ray Vision"
quality = POSITIVE
get_chance = 25
lowest_value = 256 * 12
text_gain_indication = "<span class='notice'>The walls suddenly disappear!</span>"
time_coeff = 2
/datum/mutation/human/x_ray/on_acquiring(mob/living/carbon/human/owner)
if(..())
return
owner.update_sight()
/datum/mutation/human/x_ray/on_losing(mob/living/carbon/human/owner)
if(..())
return
owner.update_sight()
/datum/mutation/human/nearsight
name = "Near Sightness"
quality = MINOR_NEGATIVE
text_gain_indication = "<span class='danger'>You can't see very well.</span>"
/datum/mutation/human/nearsight/on_acquiring(mob/living/carbon/human/owner)
if(..())
return
owner.become_nearsighted()
/datum/mutation/human/nearsight/on_losing(mob/living/carbon/human/owner)
if(..())
return
owner.cure_nearsighted()
/datum/mutation/human/epilepsy
name = "Epilepsy"
quality = NEGATIVE
text_gain_indication = "<span class='danger'>You get a headache.</span>"
/datum/mutation/human/epilepsy/on_life(mob/living/carbon/human/owner)
if(prob(1) && owner.stat == CONSCIOUS)
owner.visible_message("<span class='danger'>[owner] starts having a seizure!</span>", "<span class='userdanger'>You have a seizure!</span>")
owner.Unconscious(200)
owner.Jitter(1000)
addtimer(CALLBACK(src, .proc/jitter_less, owner), 90)
/datum/mutation/human/epilepsy/proc/jitter_less(mob/living/carbon/human/owner)
if(owner)
owner.jitteriness = 10
/datum/mutation/human/bad_dna
name = "Unstable DNA"
quality = NEGATIVE
text_gain_indication = "<span class='danger'>You feel strange.</span>"
/datum/mutation/human/bad_dna/on_acquiring(mob/living/carbon/human/owner)
to_chat(owner, text_gain_indication)
var/mob/new_mob
if(prob(95))
if(prob(50))
new_mob = owner.randmutb()
else
new_mob = owner.randmuti()
else
new_mob = owner.randmutg()
if(new_mob && ismob(new_mob))
owner = new_mob
. = owner
on_losing(owner)
/datum/mutation/human/cough
name = "Cough"
quality = MINOR_NEGATIVE
text_gain_indication = "<span class='danger'>You start coughing.</span>"
/datum/mutation/human/cough/on_life(mob/living/carbon/human/owner)
if(prob(5) && owner.stat == CONSCIOUS)
owner.drop_all_held_items()
owner.emote("cough")
/datum/mutation/human/dwarfism
name = "Dwarfism"
quality = POSITIVE
get_chance = 15
lowest_value = 256 * 12
/datum/mutation/human/dwarfism/on_acquiring(mob/living/carbon/human/owner)
if(..())
return
owner.resize = 0.8
owner.update_transform()
owner.pass_flags |= PASSTABLE
owner.visible_message("<span class='danger'>[owner] suddenly shrinks!</span>", "<span class='notice'>Everything around you seems to grow..</span>")
/datum/mutation/human/dwarfism/on_losing(mob/living/carbon/human/owner)
if(..())
return
owner.resize = 1.25
owner.update_transform()
owner.pass_flags &= ~PASSTABLE
owner.visible_message("<span class='danger'>[owner] suddenly grows!</span>", "<span class='notice'>Everything around you seems to shrink..</span>")
/datum/mutation/human/clumsy
name = "Clumsiness"
quality = MINOR_NEGATIVE
text_gain_indication = "<span class='danger'>You feel lightheaded.</span>"
/datum/mutation/human/clumsy/on_acquiring(mob/living/carbon/human/owner)
if(..())
return
owner.disabilities |= CLUMSY
/datum/mutation/human/clumsy/on_losing(mob/living/carbon/human/owner)
if(..())
return
owner.disabilities &= ~CLUMSY
/datum/mutation/human/tourettes
name = "Tourettes Syndrome"
quality = NEGATIVE
text_gain_indication = "<span class='danger'>You twitch.</span>"
/datum/mutation/human/tourettes/on_life(mob/living/carbon/human/owner)
if(prob(10) && owner.stat == CONSCIOUS)
owner.Stun(200)
switch(rand(1, 3))
if(1)
owner.emote("twitch")
if(2 to 3)
owner.say("[prob(50) ? ";" : ""][pick("SHIT", "PISS", "FUCK", "CUNT", "COCKSUCKER", "MOTHERFUCKER", "TITS")]")
var/x_offset_old = owner.pixel_x
var/y_offset_old = owner.pixel_y
var/x_offset = owner.pixel_x + rand(-2,2)
var/y_offset = owner.pixel_y + rand(-1,1)
animate(owner, pixel_x = x_offset, pixel_y = y_offset, time = 1)
animate(owner, pixel_x = x_offset_old, pixel_y = y_offset_old, time = 1)
/datum/mutation/human/nervousness
name = "Nervousness"
quality = MINOR_NEGATIVE
text_gain_indication = "<span class='danger'>You feel nervous.</span>"
/datum/mutation/human/nervousness/on_life(mob/living/carbon/human/owner)
if(prob(10))
owner.stuttering = max(10, owner.stuttering)
/datum/mutation/human/deaf
name = "Deafness"
quality = NEGATIVE
text_gain_indication = "<span class='danger'>You can't seem to hear anything.</span>"
/datum/mutation/human/deaf/on_acquiring(mob/living/carbon/human/owner)
if(..())
return
owner.disabilities |= DEAF
/datum/mutation/human/deaf/on_losing(mob/living/carbon/human/owner)
if(..())
return
owner.disabilities &= ~DEAF
/datum/mutation/human/blind
name = "Blindness"
quality = NEGATIVE
text_gain_indication = "<span class='danger'>You can't seem to see anything.</span>"
/datum/mutation/human/blind/on_acquiring(mob/living/carbon/human/owner)
if(..())
return
owner.become_blind()
/datum/mutation/human/blind/on_losing(mob/living/carbon/human/owner)
if(..())
return
owner.cure_blind()
/datum/mutation/human/race
name = "Monkified"
quality = NEGATIVE
time_coeff = 2
/datum/mutation/human/race/on_acquiring(mob/living/carbon/human/owner)
if(owner.has_brain_worms())
to_chat(owner, "<span class='warning'>You feel something strongly clinging to your humanity!</span>")
return
if(..())
return
. = owner.monkeyize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_KEEPSE)
/datum/mutation/human/race/on_losing(mob/living/carbon/monkey/owner)
if(owner && istype(owner) && owner.stat != DEAD && (owner.dna.mutations.Remove(src)))
. = owner.humanize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_KEEPSE)
/datum/mutation/human/chameleon
name = "Chameleon"
quality = POSITIVE
get_chance = 20
lowest_value = 256 * 12
text_gain_indication = "<span class='notice'>You feel one with your surroundings.</span>"
text_lose_indication = "<span class='notice'>You feel oddly exposed.</span>"
time_coeff = 5
/datum/mutation/human/chameleon/on_acquiring(mob/living/carbon/human/owner)
if(..())
return
owner.alpha = CHAMELEON_MUTATION_DEFAULT_TRANSPARENCY
/datum/mutation/human/chameleon/on_life(mob/living/carbon/human/owner)
owner.alpha = max(0, owner.alpha - 25)
/datum/mutation/human/chameleon/on_move(mob/living/carbon/human/owner)
owner.alpha = CHAMELEON_MUTATION_DEFAULT_TRANSPARENCY
/datum/mutation/human/chameleon/on_attack_hand(mob/living/carbon/human/owner, atom/target, proximity)
if(proximity) //stops tk from breaking chameleon
owner.alpha = CHAMELEON_MUTATION_DEFAULT_TRANSPARENCY
return
/datum/mutation/human/chameleon/on_losing(mob/living/carbon/human/owner)
if(..())
return
owner.alpha = 255
/datum/mutation/human/wacky
name = "Wacky"
quality = MINOR_NEGATIVE
text_gain_indication = "<span class='sans'>You feel an off sensation in your voicebox.</span>"
text_lose_indication = "<span class='notice'>The off sensation passes.</span>"
/datum/mutation/human/wacky/get_spans()
return list(SPAN_SANS)
/datum/mutation/human/mute
name = "Mute"
quality = NEGATIVE
text_gain_indication = "<span class='danger'>You feel unable to express yourself at all.</span>"
text_lose_indication = "<span class='danger'>You feel able to speak freely again.</span>"
/datum/mutation/human/mute/on_acquiring(mob/living/carbon/human/owner)
if(..())
return
owner.disabilities |= MUTE
/datum/mutation/human/mute/on_losing(mob/living/carbon/human/owner)
if(..())
return
owner.disabilities &= ~MUTE
/datum/mutation/human/smile
name = "Smile"
quality = MINOR_NEGATIVE
dna_block = NON_SCANNABLE
text_gain_indication = "<span class='notice'>You feel so happy. Nothing can be wrong with anything. :)</span>"
text_lose_indication = "<span class='notice'>Everything is terrible again. :(</span>"
/datum/mutation/human/smile/say_mod(message)
if(message)
message = " [message] "
//Time for a friendly game of SS13
message = replacetext(message," stupid "," smart ")
message = replacetext(message," retard "," genius ")
message = replacetext(message," unrobust "," robust ")
message = replacetext(message," dumb "," smart ")
message = replacetext(message," awful "," great ")
message = replacetext(message," gay ",pick(" nice "," ok "," alright "))
message = replacetext(message," horrible "," fun ")
message = replacetext(message," terrible "," terribly fun ")
message = replacetext(message," terrifying "," wonderful ")
message = replacetext(message," gross "," cool ")
message = replacetext(message," disgusting "," amazing ")
message = replacetext(message," loser "," winner ")
message = replacetext(message," useless "," useful ")
message = replacetext(message," oh god "," cheese and crackers ")
message = replacetext(message," jesus "," gee wiz ")
message = replacetext(message," weak "," strong ")
message = replacetext(message," kill "," hug ")
message = replacetext(message," murder "," tease ")
message = replacetext(message," ugly "," beautiful ")
message = replacetext(message," douchbag "," nice guy ")
message = replacetext(message," whore "," lady ")
message = replacetext(message," nerd "," smart guy ")
message = replacetext(message," moron "," fun person ")
message = replacetext(message," IT'S LOOSE "," EVERYTHING IS FINE ")
message = replacetext(message," sex "," hug fight ")
message = replacetext(message," idiot "," genius ")
message = replacetext(message," fat "," thin ")
message = replacetext(message," beer "," water with ice ")
message = replacetext(message," drink "," water ")
message = replacetext(message," feminist "," empowered woman ")
message = replacetext(message," i hate you "," you're mean ")
message = replacetext(message," nigger "," african american ")
message = replacetext(message," jew "," jewish ")
message = replacetext(message," shit "," shiz ")
message = replacetext(message," crap "," poo ")
message = replacetext(message," slut "," tease ")
message = replacetext(message," ass "," butt ")
message = replacetext(message," damn "," dang ")
message = replacetext(message," fuck "," ")
message = replacetext(message," penis "," privates ")
message = replacetext(message," cunt "," privates ")
message = replacetext(message," dick "," jerk ")
message = replacetext(message," vagina "," privates ")
return trim(message)
/datum/mutation/human/unintelligable
name = "Unintelligable"
quality = NEGATIVE
text_gain_indication = "<span class='danger'>You can't seem to form any coherent thoughts!</span>"
text_lose_indication = "<span class='danger'>Your mind feels more clear.</span>"
/datum/mutation/human/unintelligable/say_mod(message)
if(message)
var/prefix=copytext(message,1,2)
if(prefix == ";")
message = copytext(message,2)
else if(prefix in list(":","#"))
prefix += copytext(message,2,3)
message = copytext(message,3)
else
prefix=""
var/list/words = splittext(message," ")
var/list/rearranged = list()
for(var/i=1;i<=words.len;i++)
var/cword = pick(words)
words.Remove(cword)
var/suffix = copytext(cword,length(cword)-1,length(cword))
while(length(cword)>0 && suffix in list(".",",",";","!",":","?"))
cword = copytext(cword,1 ,length(cword)-1)
suffix = copytext(cword,length(cword)-1,length(cword) )
if(length(cword))
rearranged += cword
message = "[prefix][uppertext(jointext(rearranged," "))]!!"
return message
/datum/mutation/human/swedish
name = "Swedish"
quality = MINOR_NEGATIVE
dna_block = NON_SCANNABLE
text_gain_indication = "<span class='notice'>You feel Swedish, however that works.</span>"
text_lose_indication = "<span class='notice'>The feeling of Swedishness passes.</span>"
/datum/mutation/human/swedish/say_mod(message)
if(message)
message = replacetext(message,"w","v")
message = replacetext(message,"j","y")
message = replacetext(message,"a",pick("","","","a"))
message = replacetext(message,"bo","bjo")
message = replacetext(message,"o",pick("","","o"))
if(prob(30))
message += " Bork[pick("",", bork",", bork, bork")]!"
return message
/datum/mutation/human/chav
name = "Chav"
quality = MINOR_NEGATIVE
dna_block = NON_SCANNABLE
text_gain_indication = "<span class='notice'>Ye feel like a reet prat like, innit?</span>"
text_lose_indication = "<span class='notice'>You no longer feel like being rude and sassy.</span>"
/datum/mutation/human/chav/say_mod(message)
if(message)
message = " [message] "
message = replacetext(message," looking at "," gawpin' at ")
message = replacetext(message," great "," bangin' ")
message = replacetext(message," man "," mate ")
message = replacetext(message," friend ",pick(" mate "," bruv "," bledrin "))
message = replacetext(message," what "," wot ")
message = replacetext(message," drink "," wet ")
message = replacetext(message," get "," giz ")
message = replacetext(message," what "," wot ")
message = replacetext(message," no thanks "," wuddent fukken do one ")
message = replacetext(message," i don't know "," wot mate ")
message = replacetext(message," no "," naw ")
message = replacetext(message," robust "," chin ")
message = replacetext(message," hi "," how what how ")
message = replacetext(message," hello "," sup bruv ")
message = replacetext(message," kill "," bang ")
message = replacetext(message," murder "," bang ")
message = replacetext(message," windows "," windies ")
message = replacetext(message," window "," windy ")
message = replacetext(message," break "," do ")
message = replacetext(message," your "," yer ")
message = replacetext(message," security "," coppers ")
return trim(message)
/datum/mutation/human/elvis
name = "Elvis"
quality = MINOR_NEGATIVE
dna_block = NON_SCANNABLE
text_gain_indication = "<span class='notice'>You feel pretty good, honeydoll.</span>"
text_lose_indication = "<span class='notice'>You feel a little less conversation would be great.</span>"
/datum/mutation/human/elvis/on_life(mob/living/carbon/human/owner)
switch(pick(1,2))
if(1)
if(prob(15))
var/list/dancetypes = list("swinging", "fancy", "stylish", "20'th century", "jivin'", "rock and roller", "cool", "salacious", "bashing", "smashing")
var/dancemoves = pick(dancetypes)
owner.visible_message("<b>[owner]</b> busts out some [dancemoves] moves!")
if(2)
if(prob(15))
owner.visible_message("<b>[owner]</b> [pick("jiggles their hips", "rotates their hips", "gyrates their hips", "taps their foot", "dances to an imaginary song", "jiggles their legs", "snaps their fingers")]!")
/datum/mutation/human/elvis/say_mod(message)
if(message)
message = " [message] "
message = replacetext(message," i'm not "," I aint ")
message = replacetext(message," girl ",pick(" honey "," baby "," baby doll "))
message = replacetext(message," man ",pick(" son "," buddy "," brother"," pal "," friendo "))
message = replacetext(message," out of "," outta ")
message = replacetext(message," thank you "," thank you, thank you very much ")
message = replacetext(message," what are you "," whatcha ")
message = replacetext(message," yes ",pick(" sure", "yea "))
message = replacetext(message," faggot "," square ")
message = replacetext(message," muh valids "," getting my kicks ")
return trim(message)
/datum/mutation/human/stoner
name = "Stoner"
quality = NEGATIVE
dna_block = NON_SCANNABLE
text_gain_indication = "<span class='notice'>You feel...totally chill, man!</span>"
text_lose_indication = "<span class='notice'>You feel like you have a better sense of time.</span>"
/datum/mutation/human/stoner/on_acquiring(mob/living/carbon/human/owner)
..()
owner.grant_language(/datum/language/beachbum)
owner.remove_language(/datum/language/common)
/datum/mutation/human/stoner/on_losing(mob/living/carbon/human/owner)
..()
owner.grant_language(/datum/language/common)
owner.remove_language(/datum/language/beachbum)
/datum/mutation/human/laser_eyes
name = "Laser Eyes"
quality = POSITIVE
dna_block = NON_SCANNABLE
text_gain_indication = "<span class='notice'>You feel pressure building up behind your eyes.</span>"
layer_used = FRONT_MUTATIONS_LAYER
limb_req = "head"
/datum/mutation/human/laser_eyes/New()
..()
visual_indicators |= mutable_appearance('icons/effects/genetics.dmi', "lasereyes", -FRONT_MUTATIONS_LAYER)
/datum/mutation/human/laser_eyes/get_visual_indicator(mob/living/carbon/human/owner)
return visual_indicators[1]
/datum/mutation/human/laser_eyes/on_ranged_attack(mob/living/carbon/human/owner, atom/target, mouseparams)
if(owner.a_intent == INTENT_HARM)
owner.LaserEyes(target, mouseparams)
/mob/living/carbon/proc/update_mutations_overlay()
return

Some files were not shown because too many files have changed in this diff Show More