diff --git a/SQL/Aurora_SQL_Schema.sql b/SQL/Aurora_SQL_Schema.sql
index 715bb11cae6..65664dd8ad4 100644
--- a/SQL/Aurora_SQL_Schema.sql
+++ b/SQL/Aurora_SQL_Schema.sql
@@ -108,6 +108,7 @@ CREATE TABLE `ss13_characters` (
`organs_data` text,
`organs_robotic` text,
`gear` text,
+ `deleted_at` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `ss13_characters_ckey` (`ckey`),
KEY `ss13_characteres_name` (`name`),
@@ -120,6 +121,7 @@ CREATE TABLE `ss13_characters_flavour` (
`records_medical` text,
`records_security` text,
`records_exploit` text,
+ `records_ccia` text,
`flavour_general` text,
`flavour_head` text,
`flavour_face` text,
@@ -276,6 +278,8 @@ CREATE TABLE `ss13_player_preferences` (
`UI_style_alpha` int(11) NOT NULL,
`be_special` int(11) NOT NULL,
`asfx_togs` int(11) NOT NULL,
+ `lastmotd` text NOT NULL,
+ `lastmemo` text NOT NULL,
PRIMARY KEY (`ckey`),
CONSTRAINT `player_preferences_fk_ckey` FOREIGN KEY (`ckey`) REFERENCES `ss13_player` (`ckey`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
diff --git a/baystation12.dme b/baystation12.dme
index 99e846cd3ba..be280731912 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -134,6 +134,7 @@
#include "code\datums\modules.dm"
#include "code\datums\organs.dm"
#include "code\datums\recipe.dm"
+#include "code\datums\server_greeting.dm"
#include "code\datums\sun.dm"
#include "code\datums\supplypacks.dm"
#include "code\datums\diseases\appendicitis.dm"
@@ -587,6 +588,7 @@
#include "code\game\objects\items\devices\paicard.dm"
#include "code\game\objects\items\devices\pipe_painter.dm"
#include "code\game\objects\items\devices\powersink.dm"
+#include "code\game\objects\items\devices\radio_jammer.dm"
#include "code\game\objects\items\devices\scanners.dm"
#include "code\game\objects\items\devices\spy_bug.dm"
#include "code\game\objects\items\devices\suit_cooling.dm"
@@ -810,8 +812,8 @@
#include "code\modules\admin\admin.dm"
#include "code\modules\admin\admin_attack_log.dm"
#include "code\modules\admin\admin_investigate.dm"
-#include "code\modules\admin\admin_memo.dm"
#include "code\modules\admin\admin_ranks.dm"
+#include "code\modules\admin\admin_server_greeting.dm"
#include "code\modules\admin\admin_verbs.dm"
#include "code\modules\admin\banjob.dm"
#include "code\modules\admin\create_mob.dm"
@@ -895,6 +897,7 @@
#include "code\modules\client\preferences_ambience.dm"
#include "code\modules\client\preferences_factions.dm"
#include "code\modules\client\preferences_gear.dm"
+#include "code\modules\client\preferences_notification.dm"
#include "code\modules\client\preferences_save_migration.dm"
#include "code\modules\client\preferences_savefile.dm"
#include "code\modules\client\preferences_spawnpoints.dm"
@@ -976,6 +979,7 @@
#include "code\modules\clothing\under\jobs\engineering.dm"
#include "code\modules\clothing\under\jobs\medsci.dm"
#include "code\modules\clothing\under\jobs\security.dm"
+#include "code\modules\customitems\item_defines.dm"
#include "code\modules\customitems\item_spawning.dm"
#include "code\modules\detectivework\footprints.dm"
#include "code\modules\detectivework\forensics.dm"
@@ -1223,6 +1227,7 @@
#include "code\modules\mob\living\carbon\human\human_organs.dm"
#include "code\modules\mob\living\carbon\human\human_powers.dm"
#include "code\modules\mob\living\carbon\human\human_species.dm"
+#include "code\modules\mob\living\carbon\human\intoxication.dm"
#include "code\modules\mob\living\carbon\human\inventory.dm"
#include "code\modules\mob\living\carbon\human\life.dm"
#include "code\modules\mob\living\carbon\human\login.dm"
@@ -1330,6 +1335,7 @@
#include "code\modules\mob\living\simple_animal\friendly\corgi.dm"
#include "code\modules\mob\living\simple_animal\friendly\crab.dm"
#include "code\modules\mob\living\simple_animal\friendly\farm_animals.dm"
+#include "code\modules\mob\living\simple_animal\friendly\fox.dm"
#include "code\modules\mob\living\simple_animal\friendly\lizard.dm"
#include "code\modules\mob\living\simple_animal\friendly\mouse.dm"
#include "code\modules\mob\living\simple_animal\friendly\mushroom.dm"
diff --git a/code/__HELPERS/logging.dm b/code/__HELPERS/logging.dm
index 2d02e569b3a..77d3288deeb 100644
--- a/code/__HELPERS/logging.dm
+++ b/code/__HELPERS/logging.dm
@@ -20,14 +20,17 @@
/proc/testing(msg)
world.log << "## TESTING: [msg][log_end]"
+/proc/game_log(category, text)
+ diary << "\[[time_stamp()]] [game_id] [category]: [text][log_end]"
+
/proc/log_admin(text)
admin_log.Add(text)
if (config.log_admin)
- diary << "\[[time_stamp()]]ADMIN: [text][log_end]"
+ game_log("ADMIN", text)
/proc/log_debug(text)
if (config.log_debug)
- diary << "\[[time_stamp()]]DEBUG: [text][log_end]"
+ game_log("DEBUG", text)
for(var/client/C in admins)
if(!C.prefs) //This is to avoid null.toggles runtime error while still initialyzing players preferences
@@ -37,55 +40,55 @@
/proc/log_game(text)
if (config.log_game)
- diary << "\[[time_stamp()]]GAME: [text][log_end]"
+ game_log("GAME", text)
/proc/log_vote(text)
if (config.log_vote)
- diary << "\[[time_stamp()]]VOTE: [text][log_end]"
+ game_log("VOTE", text)
/proc/log_access(text)
if (config.log_access)
- diary << "\[[time_stamp()]]ACCESS: [text][log_end]"
+ game_log("ACCESS", text)
/proc/log_say(text)
if (config.log_say)
- diary << "\[[time_stamp()]]SAY: [text][log_end]"
+ game_log("SAY", text)
/proc/log_ooc(text)
if (config.log_ooc)
- diary << "\[[time_stamp()]]OOC: [text][log_end]"
+ game_log("OOC", text)
/proc/log_whisper(text)
if (config.log_whisper)
- diary << "\[[time_stamp()]]WHISPER: [text][log_end]"
+ game_log("WHISPER", text)
/proc/log_emote(text)
if (config.log_emote)
- diary << "\[[time_stamp()]]EMOTE: [text][log_end]"
+ game_log("EMOTE", text)
/proc/log_attack(text)
if (config.log_attack)
- diary << "\[[time_stamp()]]ATTACK: [text][log_end]" //Seperate attack logs? Why? FOR THE GLORY OF SATAN!
+ game_log("ATTACK", text)
/proc/log_adminsay(text)
if (config.log_adminchat)
- diary << "\[[time_stamp()]]ADMINSAY: [text][log_end]"
+ game_log("ADMINSAY", text)
/proc/log_adminwarn(text)
if (config.log_adminwarn)
- diary << "\[[time_stamp()]]ADMINWARN: [text][log_end]"
+ game_log("ADMINWARN", text)
/proc/log_pda(text)
if (config.log_pda)
- diary << "\[[time_stamp()]]PDA: [text][log_end]"
+ game_log("PDA", text)
/proc/log_to_dd(text)
world.log << text //this comes before the config check because it can't possibly runtime
if(config.log_world_output)
- diary << "\[[time_stamp()]]DD_OUTPUT: [text][log_end]"
+ game_log("DD_OUTPUT", text)
/proc/log_misc(text)
- diary << "\[[time_stamp()]]MISC: [text][log_end]"
+ game_log("MISC", text)
//pretty print a direction bitflag, can be useful for debugging.
/proc/print_dir(var/dir)
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index cc77f20c337..9e501827e49 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -134,3 +134,10 @@ Proc for attack log creation, because really why not
return 0
var/mob/living/silicon/robot/R = thing.loc
return (thing in R.module.modules)
+
+/proc/get_exposed_defense_zone(var/atom/movable/target)
+ var/obj/item/weapon/grab/G = locate() in target
+ if (G && G.state >= GRAB_NECK)
+ return pick("head", "l_hand", "r_hand", "l_foot", "r_foot", "l_arm", "r_arm", "l_leg", "r_leg")
+ else
+ return pick("chest", "groin")
diff --git a/code/__HELPERS/names.dm b/code/__HELPERS/names.dm
index 0b26e51ac45..f06071b0045 100644
--- a/code/__HELPERS/names.dm
+++ b/code/__HELPERS/names.dm
@@ -46,6 +46,10 @@ var/religion_name = null
/proc/system_name()
return "Nyx"
+/proc/commstation_name()
+ if (commstation_name)
+ return commstation_name
+
/proc/station_name()
if (station_name)
return station_name
diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm
index 9204ace71b6..907a2bfdc49 100644
--- a/code/__HELPERS/text.dm
+++ b/code/__HELPERS/text.dm
@@ -324,10 +324,26 @@ proc/TextPreview(var/string,var/len=40)
if (!message)
return ""
+ // ---Begin URL caching.
+ var/list/urls = list()
+ var/regex/url_find = new("(https?:\\/\\/\[^\\s\]*)", "g")
+ while (url_find.Find(message))
+ urls += url_find.match
+
+
+ if (urls.len)
+ var/i = 1
+ for (var/url in urls)
+ var/ref = "\ref[urls]-[i]"
+ urls[url] = ref
+ message = replacetextEx(message, url, ref)
+ i++
+ // ---End URL caching
+
var/list/tags = list("*" = list("", ""),
- "_" = list("", ""),
- "~" = list("", ""),
- "-" = list("", ""))
+ "/" = list("", ""),
+ "~" = list("", ""),
+ "_" = list("", ""))
if (ignore_tags && ignore_tags.len)
tags -= ignore_tags
@@ -339,4 +355,13 @@ proc/TextPreview(var/string,var/len=40)
var/regex/markup = new("(\\[tag])(\[^\\[tag]\]*)(\\[tag])", "g")
message = markup.Replace(message, "[marker_begin]$2[marker_end]")
+ // ---Unload URL cache
+ if (urls.len)
+ for (var/url in urls)
+ message = replacetextEx(message, urls[url], url)
+
return message
+
+//Converts New Lines to html
+/proc/nl2br(var/text)
+ return replacetextEx(text,"\n","
")
diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm
index 3d7622a7133..c7108ebe079 100644
--- a/code/_onclick/click.dm
+++ b/code/_onclick/click.dm
@@ -318,23 +318,14 @@
return
/mob/living/LaserEyes(atom/A)
- next_move = world.time + 6
var/turf/T = get_turf(src)
- var/turf/U = get_turf(A)
- var/obj/item/projectile/beam/LE = new /obj/item/projectile/beam( loc )
- LE.icon = 'icons/effects/genetics.dmi'
- LE.icon_state = "eyelasers"
- playsound(usr.loc, 'sound/weapons/taser2.ogg', 75, 1)
-
- LE.firer = src
- LE.def_zone = get_organ_target()
- LE.original = A
- LE.current = T
- LE.yo = U.y - T.y
- LE.xo = U.x - T.x
- spawn( 1 )
- LE.process()
+ var/obj/item/projectile/beam/LE = new (T)
+ LE.muzzle_type = /obj/effect/projectile/eyelaser/muzzle
+ LE.tracer_type = /obj/effect/projectile/eyelaser/tracer
+ LE.impact_type = /obj/effect/projectile/eyelaser/impact
+ playsound(usr.loc, 'sound/weapons/wave.ogg', 75, 1)
+ LE.launch(A)
/mob/living/carbon/human/LaserEyes()
if(nutrition>0)
@@ -342,7 +333,7 @@
nutrition = max(nutrition - rand(1,5),0)
handle_regular_hud_updates()
else
- src << "\red You're out of energy! You need food!"
+ src << "You're out of energy! You need food!"
// Simple helper to face what you clicked on, in case it should be needed in more than one place
/mob/proc/face_atom(var/atom/A)
diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index 1910a7e8cfa..d652ef40233 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -156,6 +156,7 @@ var/list/gamemode_cache = list()
var/admin_legacy_system = 0 //Defines whether the server uses the legacy admin system with admins.txt or the SQL system. Config option in config.txt
var/ban_legacy_system = 0 //Defines whether the server uses the legacy banning system with the files in /data or the SQL system. Config option in config.txt
var/use_age_restriction_for_jobs = 0 //Do jobs use account age restrictions? --requires database
+ var/sql_stats = 0 //Do we record round statistics on the database (deaths, round reports, population, etcetera) or not?
var/sql_whitelists = 0 //Defined whether the server uses an SQL based whitelist system, or the legacy one with two .txts. Config option in config.txt
var/sql_saves = 0 //Defines whether the server uses an SQL based character and preference saving system. Config option in config.txt
@@ -718,6 +719,9 @@ var/list/gamemode_cache = list()
if("allow_chat_markup")
config.allow_chat_markup = 1
+ if("sql_stats")
+ config.sql_stats = 1
+
else
log_misc("Unknown setting in configuration: '[name]'")
diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm
index d55662620ba..b6dcbdf782b 100644
--- a/code/datums/datacore.dm
+++ b/code/datums/datacore.dm
@@ -69,6 +69,8 @@
G.fields["religion"] = H.religion
G.fields["photo_front"] = front
G.fields["photo_side"] = side
+ G.fields["ccia_record"] = H.ccia_record
+ G.fields["ccia_actions"] = H.ccia_actions
if(H.gen_record && !jobban_isbanned(H, "Records"))
G.fields["notes"] = H.gen_record
else
diff --git a/code/datums/server_greeting.dm b/code/datums/server_greeting.dm
new file mode 100644
index 00000000000..b395e402d6e
--- /dev/null
+++ b/code/datums/server_greeting.dm
@@ -0,0 +1,211 @@
+/*
+ * Server greeting datum.
+ * Contains the following information:
+ * - hashes for the message of the day and staff memos
+ * - the current message of the day and staff memos
+ * - the fully parsed welcome screen HTML data
+ *
+ * #TODO_LATER: Update this to be reliant on the game, and not an HTML template, whenever we update to TGui. Less hacks involved, then.
+ */
+
+#define MEMOFILE "data/greeting.sav"
+
+#define OUTDATED_MOTD 1
+#define OUTDATED_MEMO 2
+#define OUTDATED_NOTE 4
+
+/datum/server_greeting
+ // Hashes to figure out if we need to display the greeting message.
+ // These correspond to motd_hash and memo_hash on /datum/preferences for each client.
+ var/motd_hash = ""
+ var/memo_hash = ""
+
+ // The stored strings of general subcomponents.
+ var/motd = ""
+ var/memo_list[] = list()
+ var/memo = ""
+
+ var/raw_data_user = ""
+ var/raw_data_staff = ""
+ // The near-final string to be displayed.
+ // Only one placeholder remains: .
+ var/user_data = ""
+ var/staff_data = ""
+
+/datum/server_greeting/New()
+ ..()
+
+ load_from_file()
+
+ prepare_data()
+
+/*
+ * Populates variables from save file, and loads the raw HTML data.
+ * Needs to be called at least once for successful initialization.
+ */
+/datum/server_greeting/proc/load_from_file()
+ var/savefile/F = new(MEMOFILE)
+ if (F)
+ if (F["motd"])
+ F["motd"] >> motd
+
+ if (F["memo"])
+ F["memo"] >> memo_list
+
+ raw_data_staff = file2text('html/templates/welcome_screen.html')
+
+ // This is a lazy way, but it disables the user from being able to see the memo button.
+ var/staff_button = "
Staff Memos"
+ raw_data_user = replacetextEx(raw_data_staff, staff_button, "")
+
+/*
+ * Generates hashes, placeholders, and reparses var/memo.
+ * Then updates staff_data and user_data with the new contents.
+ * To be called after load_from_file or update_value.
+ */
+/datum/server_greeting/proc/prepare_data()
+ if (!motd)
+ motd = "No new announcements to showcase."
+ motd_hash = ""
+ else
+ motd_hash = md5(motd)
+
+ memo = ""
+
+ if (memo_list.len)
+ for (var/ckey in memo_list)
+ var/data = {"
+ [ckey] wrote on [memo_list[ckey]["date"]]:
+ [memo_list[ckey]["content"]]
+ "}
+
+ memo += data
+
+ memo_hash = md5(memo)
+ else
+ memo = "No memos have been posted."
+ memo_hash = ""
+
+ var/html_one = raw_data_staff
+ html_one = replacetextEx(html_one, "", motd)
+ html_one = replacetextEx(html_one, "", memo)
+ staff_data = html_one
+
+ var/html_two = raw_data_user
+ html_two = replacetextEx(html_two, "", motd)
+ user_data = html_two
+
+ return
+
+/*
+ * Helper to update the MoTD or memo contents.
+ * Args:
+ * - var/change string
+ * - var/new_value mixed
+ * Returns:
+ * - 1 upon success
+ * - 0 upon failure
+ */
+/datum/server_greeting/proc/update_value(var/change, var/new_value)
+ if (!change || !new_value)
+ return 0
+
+ switch (change)
+ if ("motd")
+ motd = new_value
+ motd_hash = md5(new_value)
+
+ if ("memo_write")
+ memo_list[new_value[1]] = list("date" = time2text(world.realtime, "DD-MMM-YYYY"), "content" = new_value[2])
+
+ if ("memo_delete")
+ if (memo_list[new_value])
+ memo_list -= new_value
+ else
+ return 0
+
+ else
+ return 0
+
+ var/savefile/F = new(MEMOFILE)
+ F["motd"] << motd
+ F["memo"] << memo_list
+
+ prepare_data()
+
+ return 1
+
+/*
+ * Helper proc to determine whether or not we need to show the greeting window to a user.
+ * Args:
+ * - var/user client
+ * Returns:
+ * - int
+ */
+/datum/server_greeting/proc/find_outdated_info(var/client/user)
+ if (!user || !user.prefs)
+ return 0
+
+ var/outdated_info = 0
+
+ if (motd_hash && user.prefs.motd_hash != motd_hash)
+ outdated_info |= OUTDATED_MOTD
+
+ if (user.holder && memo_hash && user.prefs.memo_hash != memo_hash)
+ outdated_info |= OUTDATED_MEMO
+
+ if (user.prefs.notifications.len)
+ outdated_info |= OUTDATED_NOTE
+
+ return outdated_info
+
+/*
+ * Composes the final message and displays it to the user.
+ * Also clears the user's notifications, should he have any.
+ */
+/datum/server_greeting/proc/display_to_client(var/client/user, var/outdated_info = 0)
+ if (!user)
+ return
+
+ var/notifications = "You do not have any notifications to show.
"
+ var/list/outdated_tabs = list()
+ var/save_prefs = 0
+
+ if (outdated_info & OUTDATED_NOTE)
+ outdated_tabs += "#note-tab"
+
+ notifications = ""
+ for (var/datum/client_notification/a in user.prefs.notifications)
+ notifications += a.get_html()
+
+ if (outdated_info & OUTDATED_MEMO)
+ outdated_tabs += "#memo-tab"
+ user.prefs.memo_hash = memo_hash
+ save_prefs = 1
+
+ if (outdated_info & OUTDATED_MOTD)
+ outdated_tabs += "#motd-tab"
+ user.prefs.motd_hash = motd_hash
+ save_prefs = 1
+
+ var/data = user_data
+
+ if (user.holder)
+ data = staff_data
+
+ data = replacetextEx(data, "", notifications)
+
+ if (outdated_tabs.len)
+ var/tab_string = json_encode(outdated_tabs)
+ data = replacetextEx(data, "var updated_tabs = \[\]", "var updated_tabs = [tab_string]")
+
+ user << browse(data, "window=welcome_screen;size=640x500")
+
+ if (save_prefs)
+ user.prefs.handle_preferences_save(user)
+
+#undef OUTDATED_NOTE
+#undef OUTDATED_MEMO
+#undef OUTDATED_MOTD
+
+#undef MEMOFILE
diff --git a/code/datums/supplypacks.dm b/code/datums/supplypacks.dm
index 018b3d0538f..a85f48971e8 100644
--- a/code/datums/supplypacks.dm
+++ b/code/datums/supplypacks.dm
@@ -1552,3 +1552,11 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
/obj/item/device/kit/paint/gygax/recitence
)
name = "Random Gygax exosuit modkit"
+
+/datum/supply_packs/jukebox
+ name = "Jukebox"
+ contains = list(/obj/machinery/media/jukebox/)
+ cost = 200
+ containertype = /obj/structure/largecrate
+ containername = "jukebox Crate"
+ group = "Hospitality"
diff --git a/code/defines/obj/weapon.dm b/code/defines/obj/weapon.dm
index 501ecbe44fb..e92a003a190 100644
--- a/code/defines/obj/weapon.dm
+++ b/code/defines/obj/weapon.dm
@@ -186,6 +186,20 @@
throw_range = 5
w_class = 2.0
attack_verb = list("warned", "cautioned", "smashed")
+/obj/item/weapon/caution/attack_self(mob/user as mob)
+ if(src.icon_state == "caution")
+ src.icon_state = "caution_blinking"
+ user << "You turn the sign on."
+ else
+ src.icon_state = "caution"
+ user << "You turn the sign off."
+/obj/item/weapon/caution/AltClick()
+ if(src.icon_state == "caution")
+ src.icon_state = "caution_blinking"
+ usr << "You turn the sign on."
+ else
+ src.icon_state = "caution"
+ usr << "You turn the sign off."
/obj/item/weapon/caution/cone
desc = "This cone is trying to warn you of something!"
diff --git a/code/defines/procs/radio.dm b/code/defines/procs/radio.dm
index 0ee38f3b120..a18d2074152 100644
--- a/code/defines/procs/radio.dm
+++ b/code/defines/procs/radio.dm
@@ -1,7 +1,7 @@
-#define TELECOMMS_RECEPTION_NONE 0
-#define TELECOMMS_RECEPTION_SENDER 1
-#define TELECOMMS_RECEPTION_RECEIVER 2
-#define TELECOMMS_RECEPTION_BOTH 3
+#define TELECOMMS_RECEPTION_NONE 1
+#define TELECOMMS_RECEPTION_SENDER 2
+#define TELECOMMS_RECEPTION_RECEIVER 4
+#define TELECOMMS_RECEPTION_BOTH 8
/proc/register_radio(source, old_frequency, new_frequency, radio_filter)
if(old_frequency)
@@ -44,7 +44,7 @@
/proc/get_message_server()
if(message_servers)
for (var/obj/machinery/message_server/MS in message_servers)
- if(MS.active)
+ if(MS.active && !within_jamming_range(MS))
return MS
return null
@@ -52,10 +52,12 @@
return signal && signal.data["done"]
/proc/get_sender_reception(var/atom/sender, var/datum/signal/signal)
- return check_signal(signal) ? TELECOMMS_RECEPTION_SENDER : TELECOMMS_RECEPTION_NONE
+ if (check_signal(signal) && !within_jamming_range(sender))
+ return TELECOMMS_RECEPTION_SENDER
+ return TELECOMMS_RECEPTION_NONE
/proc/get_receiver_reception(var/receiver, var/datum/signal/signal)
- if(receiver && check_signal(signal))
+ if(receiver && check_signal(signal) && !within_jamming_range(receiver))
var/turf/pos = get_turf(receiver)
if(pos && (pos.z in signal.data["level"]))
return TELECOMMS_RECEPTION_RECEIVER
diff --git a/code/defines/procs/statistics.dm b/code/defines/procs/statistics.dm
index e807e4cbbce..3f9b6954a09 100644
--- a/code/defines/procs/statistics.dm
+++ b/code/defines/procs/statistics.dm
@@ -1,5 +1,5 @@
proc/sql_poll_population()
- if(!config.sql_enabled)
+ if(!config.sql_enabled || !config.sql_stats)
return
var/admincount = admins.len
var/playercount = 0
@@ -26,7 +26,7 @@ proc/sql_report_round_end()
return
proc/sql_report_death(var/mob/living/carbon/human/H)
- if(!config.sql_enabled)
+ if(!config.sql_enabled || !config.sql_stats)
return
if(!H)
return
@@ -60,7 +60,7 @@ proc/sql_report_death(var/mob/living/carbon/human/H)
proc/sql_report_cyborg_death(var/mob/living/silicon/robot/H)
- if(!config.sql_enabled)
+ if(!config.sql_enabled || !config.sql_stats)
return
if(!H)
return
@@ -94,7 +94,7 @@ proc/sql_report_cyborg_death(var/mob/living/silicon/robot/H)
proc/statistic_cycle()
- if(!config.sql_enabled)
+ if(!config.sql_enabled || !config.sql_stats)
return
while(1)
sql_poll_population()
@@ -102,6 +102,9 @@ proc/statistic_cycle()
//This proc is used for feedback. It is executed at round end.
proc/sql_commit_feedback()
+ if(!config.sql_enabled || !config.sql_stats)
+ return
+
if(!blackbox)
log_game("Round ended without a blackbox recorder. No feedback was sent to the database.")
return
diff --git a/code/game/antagonist/antagonist_add.dm b/code/game/antagonist/antagonist_add.dm
index 5ca66731c47..39e7a834aba 100644
--- a/code/game/antagonist/antagonist_add.dm
+++ b/code/game/antagonist/antagonist_add.dm
@@ -30,6 +30,8 @@
if(faction_verb && player.current)
player.current.verbs |= faction_verb
+ player.current.client.verbs += /client/proc/aooc
+
// Handle only adding a mind and not bothering with gear etc.
if(nonstandard_role_type)
faction_members |= player
@@ -41,8 +43,12 @@
return 1
/datum/antagonist/proc/remove_antagonist(var/datum/mind/player, var/show_message, var/implanted)
+ if(!istype(player))
+ return 0
+
if(player.current && faction_verb)
player.current.verbs -= faction_verb
+
if(player in current_antagonists)
player.current << "You are no longer a [role_text]!"
current_antagonists -= player
@@ -50,5 +56,10 @@
player.special_role = null
update_icons_removed(player)
BITSET(player.current.hud_updateflag, SPECIALROLE_HUD)
+
+ if (!is_special_character(player))
+ player.current.client.verbs -= /client/proc/aooc
+
return 1
- return 0
\ No newline at end of file
+
+ return 0
diff --git a/code/game/gamemodes/cult/cult_items.dm b/code/game/gamemodes/cult/cult_items.dm
index a17c030b111..66f7866011f 100644
--- a/code/game/gamemodes/cult/cult_items.dm
+++ b/code/game/gamemodes/cult/cult_items.dm
@@ -6,6 +6,8 @@
w_class = 4
force = 30
throwforce = 10
+ edge = 1
+ sharp = 1
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
/obj/item/weapon/melee/cultblade/cultify()
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index ee20601afec..a6128889bb3 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -78,6 +78,7 @@ var/global/list/additional_antag_types = list()
new/datum/uplink_item(/obj/item/weapon/storage/toolbox/syndicate, 1, "Fully Loaded Toolbox", "ST"),
new/datum/uplink_item(/obj/item/weapon/plastique, 2, "C-4 (Destroys walls)", "C4"),
new/datum/uplink_item(/obj/item/device/encryptionkey/syndicate, 2, "Encrypted Radio Channel Key", "ER"),
+ new/datum/uplink_item(/obj/item/device/radiojammer, 2, "Small Radio Jammer", "RJ"),
new/datum/uplink_item(/obj/item/device/encryptionkey/binary, 3, "Binary Translator Key", "BT"),
new/datum/uplink_item(/obj/item/weapon/card/emag, 3, "Cryptographic Sequencer", "EC"),
new/datum/uplink_item(/obj/item/weapon/storage/box/syndie_kit/clerical, 3, "Morphic Clerical Kit", "CK"),
diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm
index 9d18216aa53..9b207f8ae61 100644
--- a/code/game/gamemodes/meteor/meteors.dm
+++ b/code/game/gamemodes/meteor/meteors.dm
@@ -5,6 +5,9 @@
/var/const/meteors_in_small_wave = 10
/proc/meteor_wave(var/number = meteors_in_wave)
+ if (number == 0)
+ return
+
if(!ticker || wavesecret)
return
@@ -34,23 +37,23 @@
do
switch(pick(1,2,3,4))
if(1) //NORTH
- starty = world.maxy-(TRANSITIONEDGE+1)
- startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1))
+ starty = world.maxy-(TRANSITIONEDGE+2)
+ startx = rand((TRANSITIONEDGE+2), world.maxx-(TRANSITIONEDGE+2))
endy = TRANSITIONEDGE
endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE)
if(2) //EAST
- starty = rand((TRANSITIONEDGE+1),world.maxy-(TRANSITIONEDGE+1))
- startx = world.maxx-(TRANSITIONEDGE+1)
+ starty = rand((TRANSITIONEDGE+2),world.maxy-(TRANSITIONEDGE+1))
+ startx = world.maxx-(TRANSITIONEDGE+2)
endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE)
endx = TRANSITIONEDGE
if(3) //SOUTH
- starty = (TRANSITIONEDGE+1)
- startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1))
+ starty = (TRANSITIONEDGE+2)
+ startx = rand((TRANSITIONEDGE+2), world.maxx-(TRANSITIONEDGE+2))
endy = world.maxy-TRANSITIONEDGE
endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE)
if(4) //WEST
- starty = rand((TRANSITIONEDGE+1), world.maxy-(TRANSITIONEDGE+1))
- startx = (TRANSITIONEDGE+1)
+ starty = rand((TRANSITIONEDGE+2), world.maxy-(TRANSITIONEDGE+2))
+ startx = (TRANSITIONEDGE+2)
endy = rand(TRANSITIONEDGE,world.maxy-TRANSITIONEDGE)
endx = world.maxx-TRANSITIONEDGE
@@ -73,7 +76,7 @@
M = new /obj/effect/meteor/small( pickedstart )
M.dest = pickedgoal
- spawn(0)
+ spawn(1)
walk_towards(M, M.dest, 1)
return
@@ -84,39 +87,69 @@
icon_state = "flaming"
density = 1
anchored = 1.0
- var/hits = 1
- var/detonation_chance = 15
- var/power = 4
- var/power_step = 1
+ var/hits = 3
+ var/detonation_chance = 50
+ var/power = 2
+ var/power_step = 0.75
var/dest
+ var/shieldsoundrange = 220 // The maximum number of tiles away the sound can be heard, falls off over distance, so it will be quiet near the limit
pass_flags = PASSTABLE
+ var/done = 0//This is set to 1 when the meteor is done colliding, and is used to ignore additional bumps while waiting for deletion
+
/obj/effect/meteor/small
name = "small meteor"
icon_state = "smallf"
pass_flags = PASSTABLE | PASSGRILLE
- power = 2
+ power = 1
+ power_step = 0.5
+ hits = 2
+ detonation_chance = 30
+ shieldsoundrange = 120
+
/obj/effect/meteor/Destroy()
walk(src,0) //this cancels the walk_towards() proc
..()
/obj/effect/meteor/Bump(atom/A)
- spawn(0)
+ if (!done)
+ spawn(0)
+
+ if (A)
+ A.meteorhit(src)
+ playsound(src.loc, 'sound/effects/meteorimpact.ogg', 40, 1)
+
+ if (istype(A, /obj/effect/energy_field))//If a normal/small meteor impacts an energy field, then it makes a widely audible impact sound and qdels
+ done = 1
+ hits = 0
+ power *= 0.5
+ power_step *= 0.5
+ var/turf/T = src.loc
+ if (!T)
+ T = A.loc
+
+ if (T)//We have a double safety check on T to prevent runtime errors
+ meteor_shield_impact_sound(T, shieldsoundrange)
+ msg_admin_attack("Meteor impacted energy field at coords (JMP)")
+ spawn()//Delaying the Qdel a frame provides a little more safety
+ qdel(src)
+
+ if (--src.hits == 0 && !done)
+ //Prevent meteors from blowing up the singularity's containment.
+ //Changing emitter and generator ex_act would result in them being bomb and C4 proof.
+ done = 1
+ if(!istype(A,/obj/machinery/power/emitter) && \
+ !istype(A,/obj/machinery/field_generator) && \
+ prob(detonation_chance))
+ explosion(loc, power, power + power_step, power + power_step * 2, power + power_step * 3, 0)
+ msg_admin_attack("Meteor exploded at coords (JMP)")
+ else
+ msg_admin_attack("Meteor dissipated without exploding at coords (JMP)")
+ spawn()
+ qdel(src)
- if (A)
- A.meteorhit(src)
- playsound(src.loc, 'sound/effects/meteorimpact.ogg', 40, 1)
- if (--src.hits <= 0)
- //Prevent meteors from blowing up the singularity's containment.
- //Changing emitter and generator ex_act would result in them being bomb and C4 proof.
- if(!istype(A,/obj/machinery/power/emitter) && \
- !istype(A,/obj/machinery/field_generator) && \
- prob(detonation_chance))
- explosion(loc, power, power + power_step, power + power_step * 2, power + power_step * 3, 0)
- qdel(src)
- return
/obj/effect/meteor/ex_act(severity)
@@ -128,37 +161,83 @@
/obj/effect/meteor/big
name = "big meteor"
hits = 5
- power = 1
+ power = 4
+ power_step = 1
+ detonation_chance = 60
+ shieldsoundrange = 310//This can be set larger than the dimensions of the map, to allow it to remain louder at extreme distance
ex_act(severity)
return
Bump(atom/A)
- spawn(0)
- //Prevent meteors from blowing up the singularity's containment.
- //Changing emitter and generator ex_act would result in them being bomb and C4 proof
- if(!istype(A,/obj/machinery/power/emitter) && \
- !istype(A,/obj/machinery/field_generator))
- if(--src.hits <= 0)
- qdel(src) //Dont blow up singularity containment if we get stuck there.
+ if (!done)
+ spawn(0)
+ //Prevent meteors from blowing up the singularity's containment.
+ //Changing emitter and generator ex_act would result in them being bomb and C4 proof
+ if(!istype(A,/obj/machinery/power/emitter) && \
+ !istype(A,/obj/machinery/field_generator))
+ if(--src.hits <= 0)
+ qdel(src) //Dont blow up singularity containment if we get stuck there.
- if (A)
- for(var/mob/M in player_list)
- var/turf/T = get_turf(M)
- if(!T || T.z != src.z)
- continue
- shake_camera(M, 3, get_dist(M.loc, src.loc) > 20 ? 1 : 3)
- playsound(src.loc, 'sound/effects/meteorimpact.ogg', 40, 1)
- explosion(src.loc, 0, 1, 2, 3, 0)
+ if (istype(A, /obj/effect/energy_field))//If a big meteor impacts an energy field, then it detonates immediately with reduced power
+ done = 1
+ hits = 0
+ power *= 0.5
+ power_step *= 0.5
+ for(var/mob/M in player_list)
+ var/turf/T = get_turf(M)
+ if(!T || T.z != src.z)
+ continue
+ shake_camera(M, 3, get_dist(M.loc, src.loc) > 20 ? 1 : 3)
+ var/turf/T = src.loc
+ if (!T)
+ T = A.loc
- if (--src.hits <= 0)
- if(prob(detonation_chance) && !istype(A, /obj/structure/grille))
+ if (T)
+ meteor_shield_impact_sound(T, shieldsoundrange)
explosion(loc, power, power + power_step, power + power_step * 2, power + power_step * 3, 0)
- qdel(src)
- return
+ msg_admin_attack("Large Meteor impacted energy field and then exploded at coords (JMP)")
+ spawn()//Have to delay the qdel a little, or the playsound will throw a runtime
+ qdel(src)
+
+ else if (A)
+ for(var/mob/M in player_list)
+ var/turf/T = get_turf(M)
+ if(!T || T.z != src.z)
+ continue
+ shake_camera(M, 3, get_dist(M.loc, src.loc) > 20 ? 1 : 3)
+ playsound(src.loc, 'sound/effects/meteorimpact.ogg', 40, 1)
+ explosion(src.loc, 0, 1, 2, 3, 0)
+
+ if (--src.hits == 0 && !done)
+ done = 1
+ if(prob(detonation_chance) && !istype(A, /obj/structure/grille))
+ explosion(loc, power, power + power_step, power + power_step * 2, power + power_step * 3, 0)
+ msg_admin_attack("Large Meteor exploded at coords (JMP)")
+ else
+ msg_admin_attack("Large Meteor dissipated without a final explosion at coords (JMP)")
+ spawn()
+ qdel(src)
+
/obj/effect/meteor/attackby(obj/item/weapon/W as obj, mob/user as mob)
if(istype(W, /obj/item/weapon/pickaxe))
qdel(src)
return
..()
+
+//This function takes a turf to prevent race conditions, as the object calling it will probably be deleted in the same frame
+/proc/meteor_shield_impact_sound(var/turf/T, var/range)
+ //The supplied volume is reduced by an amount = distance - viewrange * 2, viewrange is 7 i think
+
+ //Calculate the supplied volume so it will be heard with slightly > 0 volume at the maximum range.
+ //The +1 gives it that tiny amount
+ range = ((range - world.view) * 2)+1
+
+
+ for(var/mob/M in world)
+ if(M.client && M.z == T.z)
+ if(M.ear_deaf <= 0 || !M.ear_deaf)
+ M.playsound_local(T, 'sound/effects/meteorimpact.ogg', range, 1, usepressure = 0)
+
+
diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm
index 2a96edcea94..1eaefc57ec6 100644
--- a/code/game/gamemodes/nuclear/nuclear.dm
+++ b/code/game/gamemodes/nuclear/nuclear.dm
@@ -5,9 +5,7 @@
/datum/game_mode/nuclear
name = "Mercenary"
round_description = "A mercenary strike force is approaching the station!"
- extended_round_description = "NanoTrasen's wealth and success created several enemies over the years,\
- and many seek to undermine them using illegal ways. Their crown jewel research stations are not safe from those\
- malicious activities."
+ extended_round_description = "NanoTrasen's wealth and success created several enemies over the years and many seek to undermine them using illegal ways. Their crown jewel research stations are not safe from those malicious activities."
config_tag = "mercenary"
required_players = 15
required_enemies = 1
diff --git a/code/game/gamemodes/revolution/revolution.dm b/code/game/gamemodes/revolution/revolution.dm
index e2393b117bc..81fbcfa080b 100644
--- a/code/game/gamemodes/revolution/revolution.dm
+++ b/code/game/gamemodes/revolution/revolution.dm
@@ -9,6 +9,6 @@
uplink_welcome = "AntagCorp Uplink Console:"
uplink_uses = 10
end_on_antag_death = 0
- shuttle_delay = 3
+// shuttle_delay = 3
antag_tags = list(MODE_REVOLUTIONARY, MODE_LOYALIST)
require_all_templates = 1
diff --git a/code/game/jobs/job/civilian.dm b/code/game/jobs/job/civilian.dm
index b59c603bc84..dc90cb0b929 100644
--- a/code/game/jobs/job/civilian.dm
+++ b/code/game/jobs/job/civilian.dm
@@ -90,6 +90,7 @@
/datum/job/qm
title = "Quartermaster"
flag = QUARTERMASTER
+ head_position = 1
department = "Cargo"
department_flag = CIVILIAN
faction = "Station"
diff --git a/code/game/jobs/job/medical.dm b/code/game/jobs/job/medical.dm
index 8de924339dc..1a045628be3 100644
--- a/code/game/jobs/job/medical.dm
+++ b/code/game/jobs/job/medical.dm
@@ -44,8 +44,8 @@
spawn_positions = 3
supervisors = "the chief medical officer"
selection_color = "#ffeef0"
- access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics)
- minimal_access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_virology)
+ access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_chemistry, access_virology, access_genetics, access_eva)
+ minimal_access = list(access_medical, access_medical_equip, access_morgue, access_surgery, access_virology, access_eva)
alt_titles = list("Surgeon","Emergency Physician","Nurse","Virologist")
equip(var/mob/living/carbon/human/H)
diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm
index b4e75b046ff..4347085e3a3 100644
--- a/code/game/machinery/cloning.dm
+++ b/code/game/machinery/cloning.dm
@@ -137,6 +137,7 @@
H.dna.UpdateUI()
H.set_cloned_appearance()
+ H.regenerate_icons()
update_icon()
for(var/datum/language/L in R.languages)
diff --git a/code/game/machinery/computer/skills.dm b/code/game/machinery/computer/skills.dm
index 90879901373..89cf42acf89 100644
--- a/code/game/machinery/computer/skills.dm
+++ b/code/game/machinery/computer/skills.dm
@@ -90,21 +90,31 @@
var/icon/side = active1.fields["photo_side"]
user << browse_rsc(front, "front.png")
user << browse_rsc(side, "side.png")
- dat += text("")
+ dat += text({"
+Employment/skills summary:
[decode(active1.fields["notes"])]
+CCIA Notes:
[nl2br(decode(active1.fields["ccia_record"]))]
"})
+
+ //Add the CCIA Actions
+ dat+= text({"CCIA Actions:
| Title | Type | CCIA Thread |
"})
+ for (var/list/action in active1.fields["ccia_actions"])
+ dat+= text("| [action[1]] | [action[2]] | Open |
")
+ dat+= text("| [nl2br(action[4])] |
")
+ dat+= text("
")
+
+
else
dat += "General Record Lost!
"
dat += text("\nDelete Record (ALL)
\nPrint Record
\nBack
", src, src, src)
@@ -159,6 +169,9 @@ What a mess.*/
if ((usr.contents.Find(src) || (in_range(src, usr) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon)))
usr.set_machine(src)
switch(href_list["choice"])
+// Open Action URL
+ if("openActionUrl")
+ usr << link(href_list["url"])
// SORTING!
if("Sorting")
// Reverse the order if clicked twice
@@ -277,7 +290,7 @@ What a mess.*/
var/obj/item/weapon/paper/P = new /obj/item/weapon/paper( loc )
P.info = "Employment Record
"
if ((istype(active1, /datum/data/record) && data_core.general.Find(active1)))
- P.info += text("Name: [] ID: []
\nSex: []
\nAge: []
\nFingerprint: []
\nPhysical Status: []
\nMental Status: []
\nEmployment/Skills Summary:
\n[]
", active1.fields["name"], active1.fields["id"], active1.fields["sex"], active1.fields["age"], active1.fields["fingerprint"], active1.fields["p_stat"], active1.fields["m_stat"], decode(active1.fields["notes"]))
+ P.info += text("Name: [] ID: []
\nSex: []
\nAge: []
\nFingerprint: []
\nPhysical Status: []
\nMental Status: []
\nEmployment/Skills Summary:
\n[]
CCIA Actions / Records:
This terminal is not authorized to print CCIA records and/or notes", active1.fields["name"], active1.fields["id"], active1.fields["sex"], active1.fields["age"], active1.fields["fingerprint"], active1.fields["p_stat"], active1.fields["m_stat"], decode(active1.fields["notes"]))
else
P.info += "General Record Lost!
"
P.info += ""
diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm
index efe0f2f6e80..6124abb4ebe 100644
--- a/code/game/machinery/doors/airlock.dm
+++ b/code/game/machinery/doors/airlock.dm
@@ -3,6 +3,7 @@
icon = 'icons/obj/doors/Doorint.dmi'
icon_state = "door_closed"
power_channel = ENVIRON
+ hatch_colour = "#7d7d7d"
explosion_resistance = 10
var/aiControlDisabled = 0 //If 1, AI control is disabled until the AI hacks back in and disables the lock. If 2, the AI has bypassed the lock. If -1, the control is enabled but the AI had bypassed it earlier, so if it is disabled again the AI would have no trouble getting back in.
@@ -33,6 +34,7 @@
var/obj/item/device/magnetic_lock/bracer = null
var/open_sound_powered = 'sound/machines/airlock.ogg'
var/open_sound_unpowered = 'sound/machines/airlock_creaking.ogg'
+ hashatch = 1
/obj/machinery/door/airlock/attack_generic(var/mob/user, var/damage)
if(stat & (BROKEN|NOPOWER))
@@ -57,6 +59,7 @@
name = "Airlock"
icon = 'icons/obj/doors/Doorcom.dmi'
assembly_type = /obj/structure/door_assembly/door_assembly_com
+ hatch_colour = "#446892"
/obj/machinery/door/airlock/sac
name = "Airlock"
@@ -71,26 +74,45 @@
name = "Airlock"
icon = 'icons/obj/doors/Doorsec.dmi'
assembly_type = /obj/structure/door_assembly/door_assembly_sec
+ hatch_colour = "#c82b2b"
/obj/machinery/door/airlock/engineering
name = "Airlock"
icon = 'icons/obj/doors/Dooreng.dmi'
assembly_type = /obj/structure/door_assembly/door_assembly_eng
+ hatch_colour = "#caa638"
/obj/machinery/door/airlock/medical
name = "Airlock"
icon = 'icons/obj/doors/Doormed.dmi'
assembly_type = /obj/structure/door_assembly/door_assembly_med
+ hatch_colour = "#d2d2d2"
/obj/machinery/door/airlock/maintenance
name = "Maintenance Access"
icon = 'icons/obj/doors/Doormaint.dmi'
assembly_type = /obj/structure/door_assembly/door_assembly_mai
+ hatch_colour = "#7d7d7d"
/obj/machinery/door/airlock/external
name = "External Airlock"
icon = 'icons/obj/doors/Doorext.dmi'
assembly_type = /obj/structure/door_assembly/door_assembly_ext
+ hashatch = 0
+
+/obj/machinery/door/airlock/science
+ name = "Airlock"
+ icon = 'icons/obj/doors/Doorsci.dmi'
+ assembly_type = /obj/structure/door_assembly/door_assembly_science
+ hatch_colour = "#d2d2d2"
+
+/obj/machinery/door/airlock/glass_science
+ name = "Glass Airlocks"
+ icon = 'icons/obj/doors/Doorsciglass.dmi'
+ opacity = 0
+ assembly_type = /obj/structure/door_assembly/door_assembly_science
+ glass = 1
+ hatch_colour = "#d2d2d2"
/obj/machinery/door/airlock/glass
name = "Glass Airlock"
@@ -101,11 +123,13 @@
explosion_resistance = 5
opacity = 0
glass = 1
+ hatch_colour = "#eaeaea"
/obj/machinery/door/airlock/centcom
name = "Airlock"
icon = 'icons/obj/doors/Doorele.dmi'
opacity = 0
+ hatch_colour = "#606061"
/obj/machinery/door/airlock/vault
name = "Vault"
@@ -114,6 +138,7 @@
opacity = 1
secured_wires = 1
assembly_type = /obj/structure/door_assembly/door_assembly_highsecurity //Until somebody makes better sprites.
+ hashatch = 0
/obj/machinery/door/airlock/vault/bolted
icon_state = "door_locked"
@@ -124,6 +149,7 @@
icon = 'icons/obj/doors/Doorfreezer.dmi'
opacity = 1
assembly_type = /obj/structure/door_assembly/door_assembly_fre
+ hatch_colour = "#ffffff"
/obj/machinery/door/airlock/hatch
name = "Airtight Hatch"
@@ -131,6 +157,15 @@
explosion_resistance = 20
opacity = 1
assembly_type = /obj/structure/door_assembly/door_assembly_hatch
+ hatch_colour = "#5b5b5b"
+ var/hatch_colour_bolted = "#695a5a"
+
+ update_icon()//Special hatch colour setting for this one snowflakey door that changes color when bolted
+ if(density && locked && lights && src.arePowerSystemsOn())
+ hatch_image.color = hatch_colour_bolted
+ else
+ hatch_image.color = hatch_colour
+ ..()
/obj/machinery/door/airlock/maintenance_hatch
name = "Maintenance Hatch"
@@ -138,6 +173,7 @@
explosion_resistance = 20
opacity = 1
assembly_type = /obj/structure/door_assembly/door_assembly_mhatch
+ hatch_colour = "#7d7d7d"
/obj/machinery/door/airlock/glass_command
name = "Maintenance Hatch"
@@ -148,6 +184,7 @@
opacity = 0
assembly_type = /obj/structure/door_assembly/door_assembly_com
glass = 1
+ hatch_colour = "#345882"
/obj/machinery/door/airlock/glass_engineering
name = "Maintenance Hatch"
@@ -158,6 +195,7 @@
opacity = 0
assembly_type = /obj/structure/door_assembly/door_assembly_eng
glass = 1
+ hatch_colour = "#caa638"
/obj/machinery/door/airlock/glass_security
name = "Maintenance Hatch"
@@ -168,6 +206,7 @@
opacity = 0
assembly_type = /obj/structure/door_assembly/door_assembly_sec
glass = 1
+ hatch_colour = "#b81b1b"
/obj/machinery/door/airlock/glass_medical
name = "Maintenance Hatch"
@@ -178,21 +217,25 @@
opacity = 0
assembly_type = /obj/structure/door_assembly/door_assembly_med
glass = 1
+ hatch_colour = "#d2d2d2"
/obj/machinery/door/airlock/mining
name = "Mining Airlock"
icon = 'icons/obj/doors/Doormining.dmi'
assembly_type = /obj/structure/door_assembly/door_assembly_min
+ hatch_colour = "#c29142"
/obj/machinery/door/airlock/atmos
name = "Atmospherics Airlock"
icon = 'icons/obj/doors/Dooratmo.dmi'
assembly_type = /obj/structure/door_assembly/door_assembly_atmo
+ hatch_colour = "#caa638"
/obj/machinery/door/airlock/research
name = "Airlock"
icon = 'icons/obj/doors/Doorresearch.dmi'
assembly_type = /obj/structure/door_assembly/door_assembly_research
+ hatch_colour = "#d2d2d2"
/obj/machinery/door/airlock/glass_research
name = "Maintenance Hatch"
@@ -204,6 +247,7 @@
assembly_type = /obj/structure/door_assembly/door_assembly_research
glass = 1
heat_proof = 1
+ hatch_colour = "#d2d2d2"
/obj/machinery/door/airlock/glass_mining
name = "Maintenance Hatch"
@@ -214,6 +258,7 @@
opacity = 0
assembly_type = /obj/structure/door_assembly/door_assembly_min
glass = 1
+ hatch_colour = "#c29142"
/obj/machinery/door/airlock/glass_atmos
name = "Maintenance Hatch"
@@ -224,41 +269,45 @@
opacity = 0
assembly_type = /obj/structure/door_assembly/door_assembly_atmo
glass = 1
+ hatch_colour = "#caa638"
+
+
/obj/machinery/door/airlock/gold
name = "Gold Airlock"
icon = 'icons/obj/doors/Doorgold.dmi'
mineral = "gold"
+ hatch_colour = "#dbbb2b"
/obj/machinery/door/airlock/silver
name = "Silver Airlock"
icon = 'icons/obj/doors/Doorsilver.dmi'
mineral = "silver"
+ hatch_colour = "#ffffff"
/obj/machinery/door/airlock/diamond
name = "Diamond Airlock"
icon = 'icons/obj/doors/Doordiamond.dmi'
mineral = "diamond"
+ hatch_colour = "#66eeee"
-/obj/machinery/door/airlock/uranium
- name = "Uranium Airlock"
- desc = "And they said I was crazy."
- icon = 'icons/obj/doors/Dooruranium.dmi'
- mineral = "uranium"
- var/last_event = 0
-/obj/machinery/door/airlock/process()
- // Deliberate no call to parent.
- if(main_power_lost_until > 0 && world.time >= main_power_lost_until)
- regainMainPower()
- if(backup_power_lost_until > 0 && world.time >= backup_power_lost_until)
- regainBackupPower()
+/obj/machinery/door/airlock/sandstone
+ name = "Sandstone Airlock"
+ icon = 'icons/obj/doors/Doorsand.dmi'
+ mineral = "sandstone"
+ hatch_colour = "#efc8a8"
+
+/obj/machinery/door/airlock/highsecurity
+ name = "Secure Airlock"
+ icon = 'icons/obj/doors/hightechsecurity.dmi'
+ explosion_resistance = 20
+ secured_wires = 1
+ assembly_type = /obj/structure/door_assembly/door_assembly_highsecurity
+ hatch_colour = "#5a5a66"
- else if(electrified_until > 0 && world.time >= electrified_until)
- electrify(0)
- ..()
/obj/machinery/door/airlock/uranium/process()
if(world.time > last_event+20)
@@ -267,16 +316,28 @@
last_event = world.time
..()
+//---Uranium doors
+/obj/machinery/door/airlock/uranium
+ name = "Uranium Airlock"
+ desc = "And they said I was crazy."
+ icon = 'icons/obj/doors/Dooruranium.dmi'
+ mineral = "uranium"
+ var/last_event = 0
+ hatch_colour = "#004400"
+
/obj/machinery/door/airlock/uranium/proc/radiate()
for(var/mob/living/L in range (3,src))
L.apply_effect(15,IRRADIATE,0)
return
+
+//---Phoron door
/obj/machinery/door/airlock/phoron
name = "Phoron Airlock"
desc = "No way this can end badly."
icon = 'icons/obj/doors/Doorphoron.dmi'
mineral = "phoron"
+ hatch_colour = "#891199"
/obj/machinery/door/airlock/phoron/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
if(exposed_temperature > 300)
@@ -297,29 +358,22 @@
new/obj/structure/door_assembly( src.loc )
qdel(src)
-/obj/machinery/door/airlock/sandstone
- name = "Sandstone Airlock"
- icon = 'icons/obj/doors/Doorsand.dmi'
- mineral = "sandstone"
+//-------------------------
-/obj/machinery/door/airlock/science
- name = "Airlock"
- icon = 'icons/obj/doors/Doorsci.dmi'
- assembly_type = /obj/structure/door_assembly/door_assembly_science
+/obj/machinery/door/airlock/process()
+ // Deliberate no call to parent.
+ if(main_power_lost_until > 0 && world.time >= main_power_lost_until)
+ regainMainPower()
+
+ if(backup_power_lost_until > 0 && world.time >= backup_power_lost_until)
+ regainBackupPower()
+
+ else if(electrified_until > 0 && world.time >= electrified_until)
+ electrify(0)
+
+ ..()
-/obj/machinery/door/airlock/glass_science
- name = "Glass Airlocks"
- icon = 'icons/obj/doors/Doorsciglass.dmi'
- opacity = 0
- assembly_type = /obj/structure/door_assembly/door_assembly_science
- glass = 1
-/obj/machinery/door/airlock/highsecurity
- name = "Secure Airlock"
- icon = 'icons/obj/doors/hightechsecurity.dmi'
- explosion_resistance = 20
- secured_wires = 1
- assembly_type = /obj/structure/door_assembly/door_assembly_highsecurity
/*
About the new airlock wires panel:
@@ -510,6 +564,13 @@ About the new airlock wires panel:
overlays += image(icon, "welded")
else if (health < maxhealth * 3/4 && !(stat & NOPOWER))
overlays += image(icon, "sparks_damaged")
+
+ if (hashatch)
+ if (hatchstate)
+ hatch_image.icon_state = "[hatchstyle]_open"
+ else
+ hatch_image.icon_state = hatchstyle
+ overlays += hatch_image
else
icon_state = "door_open"
if((stat & BROKEN) && !(stat & NOPOWER))
diff --git a/code/game/machinery/doors/blast_door.dm b/code/game/machinery/doors/blast_door.dm
index 00101893e94..b24cedc257f 100644
--- a/code/game/machinery/doors/blast_door.dm
+++ b/code/game/machinery/doors/blast_door.dm
@@ -19,7 +19,7 @@
var/icon_state_closed = null
var/icon_state_closing = null
- closed_layer = 3.3 // Above airlocks when closed
+ closed_layer = 3.4 // Above airlocks when closed
var/id = 1.0
dir = 1
explosion_resistance = 25
@@ -144,7 +144,7 @@
if(stat & BROKEN)
stat &= ~BROKEN
-
+
/obj/machinery/door/blast/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if(air_group) return 1
return ..()
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index 21d64016037..668bc53a553 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -30,6 +30,19 @@
var/block_air_zones = 1 //If set, air zones cannot merge across the door even when it is opened.
var/close_door_at = 0 //When to automatically close the door, if possible
+ var/hashatch = 0//If 1, this door has hatches, and certain small creatures can move through them without opening the door
+ var/hatchstate = 0//0: closed, 1: open
+ var/hatchstyle = "1x1"
+ var/hatch_offset_x = 0
+ var/hatch_offset_y = 0
+ var/hatch_colour = "#FFFFFF"
+ var/hatch_open_sound = 'sound/machines/hatch_open.ogg'
+ var/hatch_close_sound = 'sound/machines/hatch_close.ogg'
+
+ var/hatchclosetime //A world.time value to tell us when the hatch should close
+
+ var/image/hatch_image
+
//Multi-tile doors
dir = EAST
var/width = 1
@@ -67,8 +80,41 @@
health = maxhealth
update_nearby_tiles(need_rebuild=1)
+ if (hashatch)
+ setup_hatch()
return
+/obj/machinery/door/proc/setup_hatch()
+
+ if (overlays != null)
+ hatch_image = image('icons/obj/doors/hatches.dmi', src, hatchstyle, closed_layer+0.1)
+ hatch_image.color = hatch_colour
+ hatch_image.pixel_x = hatch_offset_x
+ hatch_image.pixel_y = hatch_offset_y
+
+ overlays += hatch_image
+ update_icon()
+ else
+ spawn(10)
+ setup_hatch()
+
+/obj/machinery/door/proc/open_hatch(var/atom/mover = null)
+ if (!hatchstate)
+ hatchstate = 1
+ update_icon()
+ playsound(src.loc, hatch_open_sound, 40, 1, -1)
+ hatchclosetime = world.time + 29
+
+ if (istype(mover, /mob/living/silicon))
+ var/mob/living/silicon/S = mover
+ S.under_door()
+
+
+/obj/machinery/door/proc/close_hatch()
+ hatchstate = 0//hatch stays open for 3 seconds
+ update_icon()
+ playsound(src.loc, hatch_close_sound, 30, 1, -1)
+
/obj/machinery/door/Destroy()
density = 0
update_nearby_tiles()
@@ -82,6 +128,8 @@
close()
else
close_door_at = 0
+ if (hatchstate && world.time > hatchclosetime)
+ close_hatch()
/obj/machinery/door/proc/can_open()
if(!density || operating || !ticker)
@@ -138,8 +186,12 @@
/obj/machinery/door/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if(air_group) return !block_air_zones
- if(istype(mover) && mover.checkpass(PASSGLASS))
- return !opacity
+ if (istype(mover))
+ if(mover.checkpass(PASSGLASS))
+ return !opacity
+ if(density && hashatch && mover.checkpass(PASSDOORHATCH))
+ open_hatch(mover)
+ return 1//If this door is closed, but it has hatches, and this creature can go through hatches. Then we let it through without opening
return !density
diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm
index 10020ec0dee..d93ba6e3bad 100644
--- a/code/game/machinery/doors/firedoor.dm
+++ b/code/game/machinery/doors/firedoor.dm
@@ -20,11 +20,13 @@
density = 0
layer = DOOR_OPEN_LAYER - 0.01
open_layer = DOOR_OPEN_LAYER - 0.01 // Just below doors when open
- closed_layer = DOOR_CLOSED_LAYER + 0.01 // Just above doors when closed
+ closed_layer = DOOR_CLOSED_LAYER + 0.2 // Just above doors when closed
//These are frequenly used with windows, so make sure zones can pass.
//Generally if a firedoor is at a place where there should be a zone boundery then there will be a regular door underneath it.
block_air_zones = 0
+ hashatch = 1
+ hatch_colour = "#f7d003"
var/blocked = 0
var/lockdown = 0 // When the door has detected a problem, it locks.
@@ -207,14 +209,14 @@
if(density && istype(C, /obj/item/weapon/screwdriver))
hatch_open = !hatch_open
- user.visible_message("[user] has [hatch_open ? "opened" : "closed"] \the [src] maintenance hatch.",
- "You have [hatch_open ? "opened" : "closed"] the [src] maintenance hatch.")
+ user.visible_message("[user] has [hatch_open ? "opened" : "closed"] \the [src] maintenance panel.",
+ "You have [hatch_open ? "opened" : "closed"] the [src] maintenance panel.")
update_icon()
return
if(blocked && istype(C, /obj/item/weapon/crowbar) && !repairing)
if(!hatch_open)
- user << "You must open the maintenance hatch first!"
+ user << "You must open the maintenance panel first!"
else
user.visible_message("[user] is removing the electronics from \the [src].",
"You start to remove the electronics from [src].")
@@ -338,13 +340,15 @@
return
/obj/machinery/door/firedoor/close()
+ overlays.Cut()
latetoggle()
return ..()
/obj/machinery/door/firedoor/open(var/forced = 0)
+ overlays.Cut()
if(hatch_open)
hatch_open = 0
- visible_message("The maintenance hatch of \the [src] closes.")
+ visible_message("The maintenance panel of \the [src] closes.")
update_icon()
if(!forced)
@@ -383,6 +387,13 @@
for(var/i=1;i<=ALERT_STATES.len;i++)
if(dir_alerts[d] & (1<<(i-1)))
overlays += new/icon(icon,"alert_[ALERT_STATES[i]]", dir=cdir)
+ if (hashatch)
+ hatch_image.color = hatch_colour//This line is unnecessary, but is here for configuring colours ingame. Should be removed when finished
+ if (hatchstate)
+ hatch_image.icon_state = "[hatchstyle]_open"
+ else
+ hatch_image.icon_state = hatchstyle
+ overlays += hatch_image
else
icon_state = "door_open"
if(blocked)
diff --git a/code/game/machinery/doors/multi_tile.dm b/code/game/machinery/doors/multi_tile.dm
index a2d599fa007..a9896e8b310 100644
--- a/code/game/machinery/doors/multi_tile.dm
+++ b/code/game/machinery/doors/multi_tile.dm
@@ -1,6 +1,8 @@
//Terribly sorry for the code doubling, but things go derpy otherwise.
/obj/machinery/door/airlock/multi_tile
width = 2
+ hatch_offset_x = 16
+ hatch_colour = "#d2d2d2"
/obj/machinery/door/airlock/multi_tile/New()
..()
@@ -17,4 +19,23 @@
icon = 'icons/obj/doors/Door2x1glass.dmi'
opacity = 0
glass = 1
- assembly_type = /obj/structure/door_assembly/multi_tile
\ No newline at end of file
+ assembly_type = /obj/structure/door_assembly/multi_tile
+
+/obj/machinery/door/airlock/multi_tile/setup_hatch()
+
+ if (overlays != null)
+ hatch_image = image('icons/obj/doors/hatches.dmi', src, hatchstyle, closed_layer+0.1)
+ hatch_image.color = hatch_colour
+ switch(dir)
+ if(EAST, WEST)
+ hatch_image.pixel_x = hatch_offset_x
+ hatch_image.pixel_y = hatch_offset_y
+ else
+ hatch_image.pixel_x = hatch_offset_y
+ hatch_image.pixel_y = hatch_offset_x
+ hatch_image.transform = turn(hatch_image.transform, 90)
+
+ overlays += hatch_image
+ else
+ spawn(10)//If overlays aren't initialised, wait a second and try again
+ setup_hatch()
\ No newline at end of file
diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm
index 692662e6d2f..99bdb423679 100644
--- a/code/game/machinery/doors/windowdoor.dm
+++ b/code/game/machinery/doors/windowdoor.dm
@@ -72,12 +72,11 @@
sleep(50)
close()
return
- var/mob/M = AM // we've returned by here if M is not a mob
if (!( ticker ))
return
if (src.operating)
return
- if (src.density && !M.small && src.allowed(AM))
+ if (src.density && src.allowed(AM))
open()
if(src.check_access(null))
sleep(50)
diff --git a/code/game/machinery/jukebox.dm b/code/game/machinery/jukebox.dm
index 90305a7a786..363edf64f53 100644
--- a/code/game/machinery/jukebox.dm
+++ b/code/game/machinery/jukebox.dm
@@ -13,7 +13,7 @@ datum/track/New(var/title_name, var/audio)
icon = 'icons/obj/jukebox.dmi'
icon_state = "jukebox2-nopower"
var/state_base = "jukebox2"
- anchored = 1
+ anchored = 0
density = 1
power_channel = EQUIP
use_power = 1
@@ -33,6 +33,9 @@ datum/track/New(var/title_name, var/audio)
new/datum/track("Part A", 'sound/misc/TestLoop1.ogg'),
new/datum/track("Scratch", 'sound/music/title1.ogg'),
new/datum/track("Trai`Tor", 'sound/music/traitor.ogg'),
+ new/datum/track("Thunderdome", 'sound/music/THUNDERDOME.ogg'),
+ new/datum/track("Space Oddity", 'sound/music/space_oddity.ogg'),
+ new/datum/track("Space Asshole", 'sound/music/space_asshole.ogg'),
)
diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm
index 307b504b074..d517091a1f0 100644
--- a/code/game/machinery/portable_turret.dm
+++ b/code/game/machinery/portable_turret.dm
@@ -618,7 +618,6 @@ var/list/turret_icons
else
A = new projectile(loc)
playsound(loc, shot_sound, 75, 1)
- A.original = target
// Lethal/emagged turrets use twice the power due to higher energy beams
// Emagged turrets again use twice as much power due to higher firing rates
@@ -626,19 +625,9 @@ var/list/turret_icons
//Turrets aim for the center of mass by default.
//If the target is grabbing someone then the turret smartly aims for extremities
- var/obj/item/weapon/grab/G = locate() in target
- if(G && G.state >= GRAB_NECK) //works because mobs are currently not allowed to upgrade to NECK if they are grabbing two people.
- A.def_zone = pick("head", "l_hand", "r_hand", "l_foot", "r_foot", "l_arm", "r_arm", "l_leg", "r_leg")
- else
- A.def_zone = pick("chest", "groin")
+ var/def_zone = get_exposed_defense_zone(target)
- //Shooting Code:
- A.current = T
- A.starting = T
- A.yo = U.y - T.y
- A.xo = U.x - T.x
- spawn(1)
- A.process()
+ A.launch(target, def_zone)
/datum/turret_checks
var/enabled
diff --git a/code/game/machinery/turrets.dm b/code/game/machinery/turrets.dm
index 87edc01204c..d4a327f054c 100644
--- a/code/game/machinery/turrets.dm
+++ b/code/game/machinery/turrets.dm
@@ -261,26 +261,17 @@
A = new /obj/item/projectile/beam/lastertag/blue( loc )
if(6)
A = new /obj/item/projectile/beam/lastertag/red( loc )
- A.original = target
use_power(500)
else
A = new /obj/item/projectile/energy/electrode( loc )
use_power(200)
-
+
//Turrets aim for the center of mass by default.
//If the target is grabbing someone then the turret smartly aims for extremities
- var/obj/item/weapon/grab/G = locate() in target
- if(G && G.state >= GRAB_NECK) //works because mobs are currently not allowed to upgrade to NECK if they are grabbing two people.
- A.def_zone = pick("head", "l_hand", "r_hand", "l_foot", "r_foot", "l_arm", "r_arm", "l_leg", "r_leg")
- else
- A.def_zone = pick("chest", "groin")
-
- A.current = T
- A.starting = T
- A.yo = U.y - T.y
- A.xo = U.x - T.x
- spawn( 0 )
- A.process()
+ var/def_zone = get_exposed_defense_zone(target)
+
+ A.launch(target, def_zone)
+
return
@@ -507,25 +498,16 @@
cur_target = null
return
src.set_dir(get_dir(src,target))
- var/turf/targloc = get_turf(target)
- var/target_x = targloc.x
- var/target_y = targloc.y
- var/target_z = targloc.z
- targloc = null
spawn for(var/i=1 to min(projectiles, projectiles_per_shot))
if(!src) break
var/turf/curloc = get_turf(src)
- targloc = locate(target_x+GaussRandRound(deviation,1),target_y+GaussRandRound(deviation,1),target_z)
- if (!targloc || !curloc)
- continue
- if (targloc == curloc)
- continue
+
playsound(src, 'sound/weapons/Gunshot.ogg', 50, 1)
var/obj/item/projectile/A = new /obj/item/projectile(curloc)
src.projectiles--
- A.current = curloc
- A.yo = targloc.y - curloc.y
- A.xo = targloc.x - curloc.x
- A.process()
+
+ var/def_zone = get_exposed_defense_zone(target)
+
+ A.launch(target, def_zone)
sleep(2)
return
diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm
index 01873e28c1e..67f76f83d50 100644
--- a/code/game/machinery/vending.dm
+++ b/code/game/machinery/vending.dm
@@ -202,7 +202,7 @@
return
else if(handled)
nanomanager.update_uis(src)
- return // don't smack that machine with your 2 thalers
+ return // don't smack that machine with your 2 credits
if (I || istype(W, /obj/item/weapon/spacecash))
attack_hand(user)
@@ -530,12 +530,20 @@
if(coin.string_attached)
if(prob(50))
user << "\blue You successfully pull the coin out before \the [src] could swallow it."
+ src.visible_message("\blue The [src] putters to life, coughing out its 'premium' item after a moment.")
+ playsound(src.loc, 'sound/items/poster_being_created.ogg', 50, 1)
else
- user << "\blue You weren't able to pull the coin out fast enough, the machine ate it, string and all."
+ user << "\red You weren't able to pull the coin out fast enough, the machine ate it, string and all."
+ src.visible_message("\blue The [src] putters to life, coughing out its 'premium' item after a moment.")
+ playsound(src.loc, 'sound/items/poster_being_created.ogg', 50, 1)
qdel(coin)
+ coin = null
categories &= ~CAT_COIN
else
+ src.visible_message("\blue The [src] putters to life, coughing out its 'premium' item after a moment.")
+ playsound(src.loc, 'sound/items/poster_being_created.ogg', 50, 1)
qdel(coin)
+ coin = null
categories &= ~CAT_COIN
R.amount--
@@ -694,7 +702,10 @@
/obj/item/weapon/reagent_containers/food/drinks/flask/barflask = 2, /obj/item/weapon/reagent_containers/food/drinks/flask/vacuumflask = 2,
/obj/item/weapon/reagent_containers/food/drinks/drinkingglass = 30,/obj/item/weapon/reagent_containers/food/drinks/ice = 9,
/obj/item/weapon/reagent_containers/food/drinks/bottle/melonliquor = 2,/obj/item/weapon/reagent_containers/food/drinks/bottle/bluecuracao = 2,
- /obj/item/weapon/reagent_containers/food/drinks/bottle/absinthe = 2,/obj/item/weapon/reagent_containers/food/drinks/bottle/grenadine = 5)
+ /obj/item/weapon/reagent_containers/food/drinks/bottle/absinthe = 2,/obj/item/weapon/reagent_containers/food/drinks/bottle/grenadine = 5,
+ /obj/item/weapon/reagent_containers/food/drinks/bottle/chartreusegreen = 5,/obj/item/weapon/reagent_containers/food/drinks/bottle/chartreuseyellow =5,
+ /obj/item/weapon/reagent_containers/food/drinks/bottle/cremewhite = 4, /obj/item/weapon/reagent_containers/food/drinks/bottle/brandy = 4,
+ /obj/item/weapon/reagent_containers/food/drinks/bottle/guinnes = 4, /obj/item/weapon/reagent_containers/food/drinks/bottle/drambuie = 4)
contraband = list(/obj/item/weapon/reagent_containers/food/drinks/tea = 10)
vend_delay = 15
idle_power_usage = 211 //refrigerator - believe it or not, this is actually the average power consumption of a refrigerated vending machine according to NRCan.
diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm
index 61eaa214da9..de4c60d1a3b 100644
--- a/code/game/mecha/equipment/weapons/weapons.dm
+++ b/code/game/mecha/equipment/weapons/weapons.dm
@@ -48,14 +48,11 @@
/obj/item/mecha_parts/mecha_equipment/weapon/proc/Fire(atom/A, atom/target, turf/aimloc)
var/obj/item/projectile/P = A
- P.shot_from = src
- P.original = target
- P.starting = P.loc
- P.current = P.loc
- P.firer = chassis.occupant
- P.yo = aimloc.y - P.loc.y
- P.xo = aimloc.x - P.loc.x
- P.process()
+ var/def_zone
+ if (chassis && istype(chassis.occupant, /mob/living/carbon/human))
+ var/mob/living/carbon/human/H = chassis.occupant
+ def_zone = H.zone_sel.selecting
+ P.launch(target, def_zone)
/obj/item/mecha_parts/mecha_equipment/weapon/energy
name = "general energy weapon"
@@ -280,4 +277,4 @@
return "* [chassis.selected==src?"":""][src.name][chassis.selected==src?"":""]\[[src.projectiles]\]"
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/flashbang/clusterbang/limited/rearm()
- return//Extra bit of security
\ No newline at end of file
+ return//Extra bit of security
diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm
index 1fc5b6f9f61..cec76e16dc3 100644
--- a/code/game/mecha/mech_fabricator.dm
+++ b/code/game/mecha/mech_fabricator.dm
@@ -132,6 +132,7 @@
),
+ "Hardsuit Modules" = list(),
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 4ae15667f74..73377d7a517 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -41,7 +41,8 @@
var/obj/item/device/uplink/hidden/hidden_uplink = null // All items can have an uplink hidden inside, just remember to add the triggers.
var/zoomdevicename = null //name used for message when binoculars/scope is used
var/zoom = 0 //1 if item is actively being used to zoom. For scoped guns and binoculars.
-
+ var/contained_sprite = 0 //1 if item_state, lefthand, righthand, and worn sprite are all in one dmi
+
var/item_state = null // Used to specify the item state for the on-mob overlays.
var/item_state_slots = null //overrides the default item_state for particular slots.
diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm
index e01237c2270..46450071afb 100644
--- a/code/game/objects/items/devices/PDA/PDA.dm
+++ b/code/game/objects/items/devices/PDA/PDA.dm
@@ -973,7 +973,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
t = reception.message
if(reception.message_server && (reception.telecomms_reception & TELECOMMS_RECEPTION_SENDER)) // only send the message if it's stable
- if(reception.telecomms_reception & TELECOMMS_RECEPTION_RECEIVER == 0) // Does our recipient have a broadcaster on their level?
+ if(!(reception.telecomms_reception & TELECOMMS_RECEPTION_RECEIVER)) // Does our recipient have a broadcaster on their level?
U << "ERROR: Cannot reach recipient."
return
var/send_result = reception.message_server.send_pda_message("[P.owner]","[owner]","[t]")
@@ -1198,6 +1198,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
user.drop_item()
C.loc = src
pai = C
+ pai.update_location()//This notifies the pAI that they've been slotted into a PDA
user << "You slot \the [C] into [src]."
nanomanager.update_uis(src) // update all UIs attached to src
else if(istype(C, /obj/item/weapon/pen))
diff --git a/code/game/objects/items/devices/paicard.dm b/code/game/objects/items/devices/paicard.dm
index 7edf72e9a50..3e3910ef0b0 100644
--- a/code/game/objects/items/devices/paicard.dm
+++ b/code/game/objects/items/devices/paicard.dm
@@ -323,4 +323,24 @@
if(pai && pai.client && !pai.canmove)
var/rendered = "[text]"
pai.show_message(rendered, 2)
- ..()
\ No newline at end of file
+ ..()
+
+/obj/item/device/paicard/dropped(mob/user)
+
+ ///When an object is put into a container, drop fires twice.
+ //once with it on the floor, and then once in the container
+ //We only care about the second one
+ if (istype(loc, /obj/item/weapon/storage)) //The second drop reads the container its placed into as the location
+ update_location()
+
+
+/obj/item/device/paicard/equipped(var/mob/user, var/slot)
+ ..()
+ update_location(slot)
+
+/obj/item/device/paicard/proc/update_location(var/slotnumber = null)
+ if (!slotnumber)
+ if (istype(loc, /mob))
+ slotnumber = get_equip_slot()
+
+ report_onmob_location(1, slotnumber, pai)
\ No newline at end of file
diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm
index 6e1677d3ab0..62253e31101 100644
--- a/code/game/objects/items/devices/radio/radio.dm
+++ b/code/game/objects/items/devices/radio/radio.dm
@@ -292,6 +292,9 @@
/* ###### Radio headsets can only broadcast through subspace ###### */
if(subspace_transmission)
+ // Check for jamming.
+ if (within_jamming_range(src))
+ return
// First, we want to generate a new radio signal
var/datum/signal/signal = new
signal.transmission_method = 2 // 2 would be a subspace transmission.
@@ -434,6 +437,8 @@
return -1
if(!listening)
return -1
+ if (within_jamming_range(src))
+ return -1
if(!(0 in level))
var/turf/position = get_turf(src)
if(!position || !(position.z in level))
@@ -652,4 +657,4 @@
return
/obj/item/device/radio/off
- listening = 0
\ No newline at end of file
+ listening = 0
diff --git a/code/game/objects/items/devices/radio_jammer.dm b/code/game/objects/items/devices/radio_jammer.dm
new file mode 100644
index 00000000000..ba6828d24a6
--- /dev/null
+++ b/code/game/objects/items/devices/radio_jammer.dm
@@ -0,0 +1,130 @@
+//Global list for housing active radiojammers:
+var/list/active_radio_jammers = list()
+
+proc/within_jamming_range(var/atom/test) // tests if an object is near a radio jammer
+ if (active_radio_jammers && active_radio_jammers.len)
+ for (var/obj/item/device/radiojammer/Jammer in active_radio_jammers)
+ if (get_dist(test, Jammer) <= Jammer.radius)
+ return 1
+
+ return 0
+
+/obj/item/device/radiojammer
+ name = "radio jammer"
+ desc = "A small, inconspicious looking item with an 'ON/OFF' toggle."
+ icon = 'icons/obj/device.dmi'
+ icon_state = "shield0"
+ w_class = 2
+
+ var/active = 0
+ var/radius = 7
+ var/icon_state_active = "shield1"
+ var/icon_state_inactive = "shield0"
+
+/obj/item/device/radiojammer/New()
+ ..()
+ update()
+
+/obj/item/device/radiojammer/Destroy()
+ if (active)
+ active_radio_jammers -= src
+ ..()
+
+
+/obj/item/device/radiojammer/attack_self()
+ toggle()
+
+
+/obj/item/device/radiojammer/emp_act()
+ toggle()
+
+
+/obj/item/device/radiojammer/proc/toggle()
+ if (active)
+ usr << "You deactivate \the [src]."
+ else
+ usr << "You activate \the [src]."
+ set_active(!active)
+
+
+/obj/item/device/radiojammer/proc/set_active(var/new_value)
+ active = new_value
+ update()
+
+
+/obj/item/device/radiojammer/proc/update()
+ if (active)
+ active_radio_jammers += src
+ icon_state = icon_state_active
+ else
+ active_radio_jammers -= src
+ icon_state = icon_state_inactive
+
+
+/obj/item/device/radiojammer/improvised
+ name = "improvised radio jammer"
+ desc = "An awkward bundle of wires, batteries, and radio transmitters."
+ var/obj/item/weapon/cell/cell
+ var/obj/item/device/assembly_holder/assembly_holder
+ // 10 seconds of operation on a standard cell. 200 (roughly 3 minutes) on a super cap.
+ var/power_drain_per_second = 100
+ var/last_updated = null
+ radius = 5
+ icon = 'icons/obj/assemblies/new_assemblies.dmi'
+ icon_state_active = "improvised_jammer_active"
+ icon_state_inactive = "improvised_jammer_inactive"
+
+
+/obj/item/device/radiojammer/improvised/New(var/obj/item/device/assembly_holder/incoming_holder, var/obj/item/weapon/cell/incoming_cell, var/mob/user)
+ ..()
+ cell = incoming_cell
+ assembly_holder = incoming_holder
+
+ // Spawn() required to properly move the assembly. Why? No clue!
+ // This does not make any sense, but sure.
+ spawn(0)
+ incoming_holder.forceMove(src, 1)
+ incoming_cell.forceMove(src, 1)
+
+ user.put_in_active_hand(src)
+
+/obj/item/device/radiojammer/improvised/Destroy()
+ if (active)
+ processing_objects.Remove(src)
+ ..()
+
+
+/obj/item/device/radiojammer/improvised/process()
+ var/current = world.time // current tick
+ var/delta = (current - last_updated) / 10.0 // delta in seconds
+ last_updated = current
+ if (!cell.use(delta * power_drain_per_second))
+ set_active(0)
+ cell.charge = 0 // drain the last of the battery
+
+
+/obj/item/device/radiojammer/improvised/attackby(obj/item/weapon/W as obj, mob/user as mob)
+ if (istype(W, /obj/item/weapon/screwdriver))
+ user << "You disassemble the improvised signal jammer."
+ user.put_in_hands(assembly_holder)
+ user.put_in_hands(cell)
+ qdel(src)
+
+/obj/item/device/radiojammer/improvised/set_active(var/new_value)
+ if (new_value == 1)
+ if (!cell || !cell.charge)
+ return
+
+ ..()
+
+/obj/item/device/radiojammer/improvised/update()
+ if (active)
+ active_radio_jammers += src
+ icon_state = icon_state_active
+ processing_objects.Add(src)
+
+ last_updated = world.time
+ else
+ active_radio_jammers -= src
+ icon_state = icon_state_inactive
+ processing_objects.Remove(src)
diff --git a/code/game/objects/items/glassjar.dm b/code/game/objects/items/glassjar.dm
index 54062ff8530..96f4e03407b 100644
--- a/code/game/objects/items/glassjar.dm
+++ b/code/game/objects/items/glassjar.dm
@@ -71,7 +71,7 @@
if(contains != 1)
return
var/obj/item/weapon/spacecash/S = W
- user.visible_message("[user] puts [S.worth] [S.worth > 1 ? "thalers" : "thaler"] into \the [src].")
+ user.visible_message("[user] puts [S.worth] [S.worth > 1 ? "credits" : "credit"] into \the [src].")
user.drop_from_inventory(S)
S.loc = src
update_icon()
diff --git a/code/game/objects/items/stacks/nanopaste.dm b/code/game/objects/items/stacks/nanopaste.dm
index f85bc336be9..a71c2bc5455 100644
--- a/code/game/objects/items/stacks/nanopaste.dm
+++ b/code/game/objects/items/stacks/nanopaste.dm
@@ -6,6 +6,9 @@
icon_state = "tube"
origin_tech = "materials=4;engineering=3"
amount = 10
+
+ var/list/construction_cost = list(DEFAULT_WALL_MATERIAL = 7000, "glass" = 7000)
+ var/construction_time = 5
/obj/item/stack/nanopaste/attack(mob/living/M as mob, mob/user as mob)
diff --git a/code/game/objects/items/weapons/material/swords.dm b/code/game/objects/items/weapons/material/swords.dm
index 69c805dcef4..8d2339af756 100644
--- a/code/game/objects/items/weapons/material/swords.dm
+++ b/code/game/objects/items/weapons/material/swords.dm
@@ -4,6 +4,7 @@
icon_state = "claymore"
item_state = "claymore"
slot_flags = SLOT_BELT|SLOT_BACK
+ w_class = 4
force_divisor = 0.7 // 42 when wielded with hardnes 60 (steel)
thrown_force_divisor = 0.5 // 10 when thrown with weight 20 (steel)
sharp = 1
@@ -28,3 +29,35 @@
/obj/item/weapon/material/sword/katana/suicide_act(mob/user)
viewers(user) << "[user] is slitting \his stomach open with the [src.name]! It looks like \he's trying to commit seppuku."
return(BRUTELOSS)
+
+/obj/item/weapon/material/sword/rapier
+ name = "rapier"
+ desc = "A slender, fancy and sharply pointed sword."
+ icon_state = "rapier"
+ item_state = "claymore"
+ slot_flags = SLOT_BELT
+ attack_verb = list("attacked", "stabbed", "prodded", "poked", "lunged")
+
+/obj/item/weapon/material/sword/longsword
+ name = "longsword"
+ desc = "A double-edged large blade."
+ icon_state = "longsword"
+ item_state = "claymore"
+ slot_flags = SLOT_BELT | SLOT_BACK
+
+/obj/item/weapon/material/sword/trench
+ name = "trench knife"
+ desc = "A military knife used to slash and stab enemies in close quarters."
+ force_divisor = 0.4
+ icon_state = "trench"
+ item_state = "knife"
+ w_class = 3
+ flags = NOSHIELD
+ slot_flags = SLOT_BELT
+
+/obj/item/weapon/material/sword/sabre
+ name = "sabre"
+ desc = "A sharp curved backsword."
+ icon_state = "sabre"
+ item_state = "katana"
+ slot_flags = SLOT_BELT
diff --git a/code/game/objects/items/weapons/melee/energy.dm b/code/game/objects/items/weapons/melee/energy.dm
index 2bba23c6c13..4ac80d56471 100644
--- a/code/game/objects/items/weapons/melee/energy.dm
+++ b/code/game/objects/items/weapons/melee/energy.dm
@@ -60,7 +60,7 @@
name = "energy glaive"
desc = "An energized glaive."
icon_state = "eglaive0"
- active_force = 60
+ active_force = 40
active_throwforce = 60
active_w_class = 5
force = 20
diff --git a/code/game/objects/items/weapons/melee/misc.dm b/code/game/objects/items/weapons/melee/misc.dm
index c5efdae1fb1..250133a98d6 100644
--- a/code/game/objects/items/weapons/melee/misc.dm
+++ b/code/game/objects/items/weapons/melee/misc.dm
@@ -14,3 +14,40 @@
suicide_act(mob/user)
viewers(user) << "\red [user] is strangling \himself with the [src.name]! It looks like \he's trying to commit suicide."
return (OXYLOSS)
+
+/obj/item/weapon/melee/chainsword
+ name = "chainsword"
+ desc = "A deadly chainsaw in the shape of a sword."
+ icon = 'icons/obj/weapons.dmi'
+ icon_state = "chainswordoff"
+ flags = CONDUCT
+ slot_flags = SLOT_BELT
+ force = 15
+ throwforce = 7
+ w_class = 4
+ sharp = 1
+ edge = 1
+ origin_tech = "combat=5"
+ attack_verb = list("chopped", "sliced", "shredded", "slashed", "cut", "ripped")
+ hitsound = 'sound/weapons/bladeslice.ogg'
+ var/active = 0
+
+/obj/item/weapon/melee/chainsword/attack_self(mob/user)
+ active= !active
+ if(active)
+ playsound(user, 'sound/weapons/circsawhit.ogg', 50, 1)
+ user << "\blue \The [src] rumbles to life."
+ force = 35
+ hitsound = 'sound/weapons/circsawhit.ogg'
+ icon_state = "chainswordon"
+ slot_flags = null
+ else
+ user << "\blue \The [src] slowly powers down."
+ force = initial(force)
+ hitsound = initial(hitsound)
+ icon_state = initial(icon_state)
+ slot_flags = initial(slot_flags)
+
+/obj/item/weapon/melee/chainsword/suicide_act(mob/user)
+ viewers(user) << "\red [user] is slicing \himself apart with the [src.name]! It looks like \he's trying to commit suicide."
+ return (BRUTELOSS|OXYLOSS)
diff --git a/code/game/objects/items/weapons/paint.dm b/code/game/objects/items/weapons/paint.dm
index fe1247cc899..ab91ad3eca3 100644
--- a/code/game/objects/items/weapons/paint.dm
+++ b/code/game/objects/items/weapons/paint.dm
@@ -18,13 +18,19 @@ var/global/list/cached_icons = list()
flags = OPENCONTAINER
var/paint_type = "red"
+ attack(mob/M as mob, mob/user as mob, def_zone)
+ if(istype(M, /mob/living/))
+ user.visible_message("\The [M] has been splashed with something by [user] to no effect!")
+ reagents.trans_to_turf(M.loc, 5)
+ return
+
afterattack(turf/simulated/target, mob/user, proximity)
- if(!proximity) return
+ if(!proximity)
+ return
if(istype(target) && reagents.total_volume > 5)
user.visible_message("\The [target] has been splashed with something by [user]!")
reagents.trans_to_turf(target, 5)
- else
- return ..()
+ return
New()
if(paint_type && lentext(paint_type) > 0)
diff --git a/code/game/objects/items/weapons/tools.dm b/code/game/objects/items/weapons/tools.dm
index 6b42af95405..78b7d9ad024 100644
--- a/code/game/objects/items/weapons/tools.dm
+++ b/code/game/objects/items/weapons/tools.dm
@@ -215,7 +215,7 @@
if(prob(5))
remove_fuel(1)
- if(get_fuel() == 0)
+ if(get_fuel() < 1)
setWelding(0)
//I'm not sure what this does. I assume it has to do with starting fires...
@@ -324,14 +324,13 @@
src.w_class = 4
welding = 1
update_icon()
- processing_objects |= src
+ set_processing(1)
else
if(M)
M << "You need more welding fuel to complete this task."
return
//Otherwise
else if(!set_welding && welding)
- processing_objects -= src
if(M)
M << "You switch \the [src] off."
else if(T)
@@ -340,8 +339,18 @@
src.damtype = "brute"
src.w_class = initial(src.w_class)
src.welding = 0
+ set_processing(0)
update_icon()
+
+//A wrapper function for the experimental tool to override
+/obj/item/weapon/weldingtool/proc/set_processing(var/state = 0)
+ if (state == 1)
+ processing_objects.Add(src)
+ else
+ processing_objects.Remove(src)
+
+
//Decides whether or not to damage a player's eyes based on what they're wearing as protection
//Note: This should probably be moved to mob
/obj/item/weapon/weldingtool/proc/eyecheck(mob/user as mob)
@@ -396,25 +405,62 @@
/obj/item/weapon/weldingtool/hugetank
name = "upgraded welding tool"
max_fuel = 80
- w_class = 3.0
+ w_class = 2.0
matter = list(DEFAULT_WALL_MATERIAL = 70, "glass" = 120)
origin_tech = "engineering=3"
+
+
+
+//The Experimental Welding Tool!
/obj/item/weapon/weldingtool/experimental
name = "experimental welding tool"
+ desc = "A scientifically-enhanced welding tool that uses fuel-producing microbes to gradually replenish its fuel supply"
max_fuel = 40
- w_class = 3.0
+ w_class = 2.0
matter = list(DEFAULT_WALL_MATERIAL = 70, "glass" = 120)
- origin_tech = "engineering=4;phorontech=3"
+ origin_tech = "engineering=4;biotech=4"
var/last_gen = 0
+ var/fuelgen_delay = 800//The time, in deciseconds, required to regenerate one unit of fuel
+ //800 = 1 unit per 1 minute and 20 seconds,
+ //This is roughly half the rate that fuel is lost if the welder is left idle, so it you carelessly leave it on it will still run out
+/obj/item/weapon/weldingtool/Destroy()
+ processing_objects.Remove(src)//Stop processing when destroyed regardless of conditions
+ ..()
+
+
+//Make sure the experimental tool only stops processing when its turned off AND full
+/obj/item/weapon/weldingtool/experimental/set_processing(var/state = 0)
+ if (state == 1)
+ processing_objects.Add(src)
+ last_gen = world.time
+ else if (welding == 0 && get_fuel() >= max_fuel)
+ processing_objects.Remove(src)
+
+
+/obj/item/weapon/weldingtool/experimental/process()
+ ..()
+ fuel_gen()
/obj/item/weapon/weldingtool/experimental/proc/fuel_gen()//Proc to make the experimental welder generate fuel, optimized as fuck -Sieve
- var/gen_amount = ((world.time-last_gen)/25)
- reagents += (gen_amount)
- if(reagents > max_fuel)
- reagents = max_fuel
+
+ if (get_fuel() < max_fuel)
+ var/gen_amount = ((world.time-last_gen)/fuelgen_delay)
+ var/remainder = max_fuel - get_fuel()
+ gen_amount = min(gen_amount, remainder)
+ reagents.add_reagent("fuel", gen_amount)
+ //reagents += (gen_amount)
+
+ if(get_fuel() >= max_fuel)
+ //reagents = max_fuel
+ set_processing(0)
+ else
+ set_processing(0)
+ last_gen = world.time
+
+
/*
* Crowbar
diff --git a/code/game/objects/items/weapons/weaponry.dm b/code/game/objects/items/weapons/weaponry.dm
index 38a988012c3..d9579719291 100644
--- a/code/game/objects/items/weapons/weaponry.dm
+++ b/code/game/objects/items/weapons/weaponry.dm
@@ -32,11 +32,6 @@
/obj/item/weapon/nullrod/attack(mob/M as mob, mob/living/user as mob) //Paste from old-code to decult with a null rod.
- M.attack_log += text("\[[time_stamp()]\] Has been attacked with [src.name] by [user.name] ([user.ckey])")
- user.attack_log += text("\[[time_stamp()]\] Used the [src.name] to attack [M.name] ([M.ckey])")
-
- msg_admin_attack("[user.name] ([user.ckey]) attacked [M.name] ([M.ckey]) with [src.name] (INTENT: [uppertext(user.a_intent)]) (JMP)")
-
if (!(istype(user, /mob/living/carbon/human) || ticker) && ticker.mode.name != "monkey")
user << "You don't have the dexterity to do this!"
return
@@ -47,7 +42,7 @@
user.Paralyse(20)
return
- if (M.stat !=2 && ishuman(M))
+ if (M.stat !=2 && ishuman(M) && user.a_intent != I_HURT)
var/mob/living/K = M
if(cult && (K.mind in cult.current_antagonists) && prob(33))
if(do_after(user, 15))
@@ -55,7 +50,7 @@
var/choice = alert(K,"Do you want to give up your goal?","Become cleansed","Resist","Give in")
switch(choice)
if("Resist")
- K.visible_message("\red The gaze in [K]'s eyes remains determined.", "\blue You turn away from the light, remaining true to your dark lord. The light burns you due to rejection!")
+ K.visible_message("\red The gaze in [K]'s eyes remains determined.", "\blue You turn away from the light, remaining true to your dark lord. Anathema!")
K.say("*scream")
K.take_overall_damage(5, 15)
if("Give in")
@@ -72,6 +67,13 @@
user << "The rod appears to do nothing."
M.visible_message("\The [user] waves \the [src] over \the [M]'s head.")
return
+ M.attack_log += text("\[[time_stamp()]\] Is being deconverted with the [src.name] by [user.name] ([user.ckey])")
+ user.attack_log += text("\[[time_stamp()]\] Used the [src.name] to attempt to deconvert [M.name] ([M.ckey])")
+
+ msg_admin_attack("[key_name(user)] attempted to deconvert [key_name(M)] with [src.name] (INTENT: [uppertext(user.a_intent)]) (JMP)")
+
+ else
+ return ..()
/obj/item/weapon/nullrod/afterattack(atom/A, mob/user as mob, proximity)
if(!proximity)
diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm
index 0c4703fe9ae..af720a7736a 100644
--- a/code/game/objects/structures/crates_lockers/crates.dm
+++ b/code/game/objects/structures/crates_lockers/crates.dm
@@ -73,6 +73,13 @@
return
if(W.loc != user) // This should stop mounted modules ending up outside the module.
return
+ if(W.abstract) //Prevents 'abstract' items (such as grabs) from creeping into the material realm.
+ if(istype(W, /obj/item/weapon/grab))
+ var/obj/item/weapon/grab/G = W
+ user << "[G.affecting] just doesn't fit!"
+ else
+ user << "[W] does not belong there!"
+ return
user.drop_item()
if(W)
W.forceMove(src.loc)
diff --git a/code/game/sound.dm b/code/game/sound.dm
index 310ad7e8030..58d8a7ce4e9 100644
--- a/code/game/sound.dm
+++ b/code/game/sound.dm
@@ -47,7 +47,7 @@ var/list/footstepfx = list("defaultstep","concretestep","grassstep","dirtstep","
var/const/FALLOFF_SOUNDS = 0.5
-/mob/proc/playsound_local(var/turf/turf_source, soundin, vol as num, vary, frequency, falloff, is_global)
+/mob/proc/playsound_local(var/turf/turf_source, soundin, vol as num, vary, frequency, falloff, is_global, var/usepressure = 1)
if(!src.client || ear_deaf > 0) return
if(soundin in footstepfx)
@@ -59,7 +59,7 @@ var/const/FALLOFF_SOUNDS = 0.5
var/sound/S = sound(soundin)
S.wait = 0 //No queue
S.channel = 0 //Any channel
- S.volume = vol
+
S.environment = -1
if (vary)
if(frequency)
@@ -74,30 +74,36 @@ var/const/FALLOFF_SOUNDS = 0.5
//sound volume falloff with distance
var/distance = get_dist(T, turf_source)
- S.volume -= max(distance - world.view, 0) * 2 //multiplicative falloff to add on top of natural audio falloff.
+ vol -= max(distance - world.view, 0) * 2 //multiplicative falloff to add on top of natural audio falloff.
+ //Not sure if author understood what they were doing, but this is not multiplicative, its linear, and its implementation breaks longdistance sounds.
+ //This extra falloff should probably be rewritten or removed, but for now ive implemented a quick fix by only setting S.volume to vol after calculations are done
+ //This fix allows feeding in high volume values (>100) to make longrange sounds audible
+ // -Nanako
- //sound volume falloff with pressure
- var/pressure_factor = 1.0
+ if (usepressure)
+ //sound volume falloff with pressure. Pass usepressure = 0 to disable these calculations
+ var/pressure_factor = 1.0
- var/datum/gas_mixture/hearer_env = T.return_air()
- var/datum/gas_mixture/source_env = turf_source.return_air()
+ var/datum/gas_mixture/hearer_env = T.return_air()
+ var/datum/gas_mixture/source_env = turf_source.return_air()
- if (hearer_env && source_env)
- var/pressure = min(hearer_env.return_pressure(), source_env.return_pressure())
+ if (hearer_env && source_env)
+ var/pressure = min(hearer_env.return_pressure(), source_env.return_pressure())
- if (pressure < ONE_ATMOSPHERE)
- pressure_factor = max((pressure - SOUND_MINIMUM_PRESSURE)/(ONE_ATMOSPHERE - SOUND_MINIMUM_PRESSURE), 0)
- else //in space
- pressure_factor = 0
+ if (pressure < ONE_ATMOSPHERE)
+ pressure_factor = max((pressure - SOUND_MINIMUM_PRESSURE)/(ONE_ATMOSPHERE - SOUND_MINIMUM_PRESSURE), 0)
+ else //in space
+ pressure_factor = 0
- if (distance <= 1)
- pressure_factor = max(pressure_factor, 0.15) //hearing through contact
+ if (distance <= 1)
+ pressure_factor = max(pressure_factor, 0.15) //hearing through contact
- S.volume *= pressure_factor
+ vol *= pressure_factor
- if (S.volume <= 0)
+ if (vol <= 0)
return //no volume means no sound
+ S.volume = vol
var/dx = turf_source.x - T.x // Hearing from the right/left
S.x = dx
var/dz = turf_source.y - T.y // Hearing from infront/behind
@@ -112,6 +118,8 @@ var/const/FALLOFF_SOUNDS = 0.5
src << S
+
+
/client/proc/playtitlemusic()
if(!ticker || !ticker.login_music) return
if(prefs.toggles & SOUND_LOBBY)
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index b39063e3fc9..2fa4d08ce5e 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -420,9 +420,12 @@
if(istype(src, /turf/simulated))
var/turf/simulated/T = src
T.dirt = 0
+ T.color = null
for(var/obj/effect/O in src)
- if(istype(O,/obj/effect/rune) || istype(O,/obj/effect/decal/cleanable) || istype(O,/obj/effect/overlay))
+ if(istype(O,/obj/effect/decal/cleanable) || istype(O,/obj/effect/overlay))
qdel(O)
+ if(istype(O,/obj/effect/rune))
+ user << "\red No matter how well you wash, the bloody symbols remain!"
else
user << "\The [source] is too dry to wash that."
source.reagents.trans_to_turf(src, 1, 10) //10 is the multiplier for the reaction effect. probably needed to wet the floor properly.
diff --git a/code/global.dm b/code/global.dm
index a2948510017..2dbd6d65b9e 100644
--- a/code/global.dm
+++ b/code/global.dm
@@ -94,9 +94,10 @@ var/blobevent = 0
var/diary = null
var/href_logfile = null
var/station_name = "NSS Exodus"
+var/commstation_name = "NMSS Odin"
var/game_version = "Baystation12"
var/changelog_hash = ""
-var/game_year = (text2num(time2text(world.realtime, "YYYY")) + 544)
+var/game_year = (text2num(time2text(world.realtime, "YYYY")) + 442)
var/going = 1.0
var/master_mode = "extended" // "extended"
@@ -178,7 +179,7 @@ var/datum/moduletypes/mods = new()
var/wavesecret = 0
var/gravity_is_on = 1
-var/join_motd = null
+var/datum/server_greeting/server_greeting = null
var/forceblob = 0
var/datum/nanomanager/nanomanager = new() // NanoManager, the manager for Nano UIs.
diff --git a/code/modules/admin/DB ban/ban_mirroring.dm b/code/modules/admin/DB ban/ban_mirroring.dm
index 0ecc78a62dc..56e9a26efc2 100644
--- a/code/modules/admin/DB ban/ban_mirroring.dm
+++ b/code/modules/admin/DB ban/ban_mirroring.dm
@@ -113,8 +113,6 @@
var/DBQuery/query = dbcon.NewQuery("SELECT ban_mirror_id, player_ckey, ban_mirror_ip, ban_mirror_computerid, date(ban_mirror_datetime) as datetime FROM ss13_ban_mirrors WHERE ban_id = :ban_id")
query.Execute(list(":ban_id" = ban_id))
- testing("Ban ID: [ban_id]")
-
var/mirrors[] = list()
while (query.NextRow())
var/items[] = list()
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index ee9d318c3a2..681c773b6e9 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -77,7 +77,8 @@ proc/admin_notice(var/message, var/rights)
OOC |
PRAY |
ADMINHELP |
- DEADCHAT\]
+ DEADCHAT |
+ AOOC\]
(toggle all)
"}
diff --git a/code/modules/admin/admin_memo.dm b/code/modules/admin/admin_memo.dm
deleted file mode 100644
index 4bcaf10d9c7..00000000000
--- a/code/modules/admin/admin_memo.dm
+++ /dev/null
@@ -1,54 +0,0 @@
-#define MEMOFILE "data/memo.sav" //where the memos are saved
-#define ENABLE_MEMOS 1 //using a define because screw making a config variable for it. This is more efficient and purty.
-
-//switch verb so we don't spam up the verb lists with like, 3 verbs for this feature.
-/client/proc/admin_memo(task in list("write","show","delete"))
- set name = "Memo"
- set category = "Server"
- if(!ENABLE_MEMOS) return
- if(!check_rights(0)) return
- switch(task)
- if("write") admin_memo_write()
- if("show") admin_memo_show()
- if("delete") admin_memo_delete()
-
-//write a message
-/client/proc/admin_memo_write()
- var/savefile/F = new(MEMOFILE)
- if(F)
- var/memo = sanitize(input(src,"Type your memo\n(Leaving it blank will delete your current memo):","Write Memo",null) as null|message, extra = 0)
- switch(memo)
- if(null)
- return
- if("")
- F.dir.Remove(ckey)
- src << "Memo removed"
- return
- if( findtext(memo,"
+
+
+
+
+
+