diff --git a/.tgs.yml b/.tgs.yml new file mode 100644 index 0000000000..22a65f0edc --- /dev/null +++ b/.tgs.yml @@ -0,0 +1,23 @@ +# This file is used by TGS (https://github.com/tgstation/tgstation-server) clients to quickly initialize a server instance for the codebase +# The format isn't documented anywhere but hopefully we never have to change it. If there are questions, contact the TGS maintainer Cyberboss/@Dominion#0444 +version: 1 +# The BYOND version to use (kept in sync with dependencies.sh by the "TGS Test Suite" CI job) +# Must be interpreted as a string, keep quoted +byond: "514.1556" +# Folders to create in "/Configuration/GameStaticFiles/" +static_files: + # Config directory should be static + - name: config + # This implies the folder should be pre-populated with contents from the repo + populate: true + # Data directory must be static + - name: data +# String dictionary. The value is the location of the file in the repo to upload to TGS. The key is the name of the file to upload to "/Configuration/EventScripts/" +# This one is for Linux hosted servers +linux_scripts: + PreCompile.sh: tools/tgs4_scripts/PreCompile.sh +# Same as above for Windows hosted servers +windows_scripts: + PreCompile.bat: tools/tgs_scripts/PreCompile.bat +# The security level the game should be run at +security: Trusted diff --git a/.tgs4.yml b/.tgs4.yml deleted file mode 100644 index 932a3a6672..0000000000 --- a/.tgs4.yml +++ /dev/null @@ -1,8 +0,0 @@ -static_files: - - name: config - populate: true - - name: data -linux_scripts: - PreCompile.sh: tools/tgs4_scripts/PreCompile.sh -windows_scripts: - PreCompile.bat: tools/tgs4_scripts/PreCompile.bat diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 37c401bfe4..0fc5b8b75d 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -17,7 +17,8 @@ "pranaygp.vscode-css-peek", "cschlosser.doxdocgen", "oderwat.indent-rainbow", - "ms-vsliveshare.vsliveshare-pack", - "Goonstation.goonstation-extpack" + "goonstation.goonstation-extpack", + "ms-vsliveshare.vsliveshare", + "hbenl.vscode-test-explorer-liveshare" ] } diff --git a/_maps/RandomZLevels/away_mission/Academy.dmm b/_maps/RandomZLevels/away_mission/Academy.dmm index 98c69e34b3..a780e8072d 100644 --- a/_maps/RandomZLevels/away_mission/Academy.dmm +++ b/_maps/RandomZLevels/away_mission/Academy.dmm @@ -203,7 +203,7 @@ /area/awaymission/academy/headmaster) "aJ" = ( /obj/structure/destructible/cult/tome, -/obj/item/dice/d20/fate/one_use, +/obj/item/dice/d20, /turf/open/floor/wood, /area/awaymission/academy/headmaster) "aK" = ( diff --git a/_maps/map_files/TauStation/TauStation.dmm b/_maps/map_files/TauStation/TauStation.dmm index 7d378ae1b3..1d64701453 100644 --- a/_maps/map_files/TauStation/TauStation.dmm +++ b/_maps/map_files/TauStation/TauStation.dmm @@ -8244,7 +8244,7 @@ /area/science/xenobiology) "cST" = ( /obj/structure/closet/secure_closet/personal/cabinet, -/obj/item/clothing/under/tuxedo, +/obj/item/clothing/under/suit/tuxedo, /obj/machinery/atmospherics/components/unary/vent_scrubber/on{ dir = 8 }, @@ -8421,8 +8421,8 @@ /area/hallway/primary/starboard) "cVT" = ( /obj/structure/closet/secure_closet/personal/cabinet, -/obj/item/clothing/under/wedding_dress, -/obj/item/clothing/under/tuxedo, +/obj/item/clothing/under/dress/wedding, +/obj/item/clothing/under/suit/tuxedo, /turf/open/floor/carpet/royalblack, /area/commons/dorms) "cVU" = ( diff --git a/code/__DEFINES/tgs.dm b/code/__DEFINES/tgs.dm index 83e06658c5..3744a95a0f 100644 --- a/code/__DEFINES/tgs.dm +++ b/code/__DEFINES/tgs.dm @@ -1,6 +1,6 @@ // tgstation-server DMAPI -#define TGS_DMAPI_VERSION "6.0.6" +#define TGS_DMAPI_VERSION "6.2.0" // All functions and datums outside this document are subject to change with any version and should not be relied on. @@ -227,6 +227,8 @@ var/is_private_channel /// Tag string associated with the channel in TGS var/custom_tag + /// [TRUE]/[FALSE] if the channel supports embeds + var/embeds_supported // Represents a chat user /datum/tgs_chat_user @@ -256,9 +258,11 @@ var/help_text = "" /// If this command should be available to game administrators only var/admin_only = FALSE + /// A subtype of [/datum/tgs_chat_command] that is ignored when enumerating available commands. Use this to create shared base /datums for commands. + var/ignore_type /** - * Process command activation. Should return a string to respond to the issuer with. + * Process command activation. Should return a [/datum/tgs_message_content] to respond to the issuer with. * * sender - The [/datum/tgs_chat_user] who issued the command. * params - The trimmed string following the command `/datum/tgs_chat_command/var/name]. @@ -266,6 +270,107 @@ /datum/tgs_chat_command/proc/Run(datum/tgs_chat_user/sender, params) CRASH("[type] has no implementation for Run()") +/// User definable chat message +/datum/tgs_message_content + /// The tring content of the message. Must be provided in New(). + var/text + + /// The [/datum/tgs_chat_embed] to embed in the message. Not supported on all chat providers. + var/datum/tgs_chat_embed/structure/embed + +/datum/tgs_message_content/New(text) + if(!istext(text)) + TGS_ERROR_LOG("[/datum/tgs_message_content] created with no text!") + text = null + + src.text = text + +/// User definable chat embed. Currently mirrors Discord chat embeds. See https://discord.com/developers/docs/resources/channel#embed-object-embed-structure for details. +/datum/tgs_chat_embed/structure + var/title + var/description + var/url + + /// Timestamp must be encoded as: time2text(world.timeofday, "YYYY-MM-DD hh:mm:ss"). Use the active timezone. + var/timestamp + + /// Colour must be #AARRGGBB or #RRGGBB hex string + var/colour + + /// See https://discord.com/developers/docs/resources/channel#embed-object-embed-image-structure for details. + var/datum/tgs_chat_embed/media/image + + /// See https://discord.com/developers/docs/resources/channel#embed-object-embed-thumbnail-structure for details. + var/datum/tgs_chat_embed/media/thumbnail + + /// See https://discord.com/developers/docs/resources/channel#embed-object-embed-image-structure for details. + var/datum/tgs_chat_embed/media/video + + var/datum/tgs_chat_embed/footer/footer + var/datum/tgs_chat_embed/provider/provider + var/datum/tgs_chat_embed/provider/author/author + + var/list/datum/tgs_chat_embed/field/fields + +/// Common datum for similar discord embed medias +/datum/tgs_chat_embed/media + /// Must be set in New(). + var/url + var/width + var/height + var/proxy_url + +/datum/tgs_chat_embed/media/New(url) + if(!istext(url)) + CRASH("[/datum/tgs_chat_embed/media] created with no url!") + + src.url = url + +/// See https://discord.com/developers/docs/resources/channel#embed-object-embed-footer-structure for details. +/datum/tgs_chat_embed/footer + /// Must be set in New(). + var/text + var/icon_url + var/proxy_icon_url + +/datum/tgs_chat_embed/footer/New(text) + if(!istext(text)) + CRASH("[/datum/tgs_chat_embed/footer] created with no text!") + + src.text = text + +/// See https://discord.com/developers/docs/resources/channel#embed-object-embed-provider-structure for details. +/datum/tgs_chat_embed/provider + var/name + var/url + +/// See https://discord.com/developers/docs/resources/channel#embed-object-embed-author-structure for details. Must have name set in New(). +/datum/tgs_chat_embed/provider/author + var/icon_url + var/proxy_icon_url + +/datum/tgs_chat_embed/provider/author/New(name) + if(!istext(name)) + CRASH("[/datum/tgs_chat_embed/provider/author] created with no name!") + + src.name = name + +/// See https://discord.com/developers/docs/resources/channel#embed-object-embed-field-structure for details. Must have name and value set in New(). +/datum/tgs_chat_embed/field + var/name + var/value + var/is_inline + +/datum/tgs_chat_embed/field/New(name, value) + if(!istext(name)) + CRASH("[/datum/tgs_chat_embed/field] created with no name!") + + if(!istext(value)) + CRASH("[/datum/tgs_chat_embed/field] created with no value!") + + src.name = name + src.value = value + // API FUNCTIONS /// Returns the maximum supported [/datum/tgs_version] of the DMAPI. @@ -296,19 +401,19 @@ /** * Send a message to connected chats. * - * message - The string to send. + * message - The [/datum/tgs_message_content] to send. * admin_only: If [TRUE], message will be sent to admin connected chats. Vice-versa applies. */ -/world/proc/TgsTargetedChatBroadcast(message, admin_only = FALSE) +/world/proc/TgsTargetedChatBroadcast(datum/tgs_message_content/message, admin_only = FALSE) return /** * Send a private message to a specific user. * - * message - The string to send. + * message - The [/datum/tgs_message_content] to send. * user: The [/datum/tgs_chat_user] to PM. */ -/world/proc/TgsChatPrivateMessage(message, datum/tgs_chat_user/user) +/world/proc/TgsChatPrivateMessage(datum/tgs_message_content/message, datum/tgs_chat_user/user) return // The following functions will sleep if a call to TgsNew() is sleeping @@ -316,10 +421,10 @@ /** * Send a message to connected chats that are flagged as game-related in TGS. * - * message - The string to send. + * message - The [/datum/tgs_message_content] to send. * channels - Optional list of [/datum/tgs_chat_channel]s to restrict the message to. */ -/world/proc/TgsChatBroadcast(message, list/channels = null) +/world/proc/TgsChatBroadcast(datum/tgs_message_content/message, list/channels = null) return /// Returns the current [/datum/tgs_version] of TGS if it is running the server, null otherwise. diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm index 5e860f3638..c501133677 100644 --- a/code/controllers/subsystem/mapping.dm +++ b/code/controllers/subsystem/mapping.dm @@ -506,8 +506,9 @@ GLOBAL_LIST_EMPTY(the_station_areas) var/away_name var/datum/space_level/away_level + possible_options += "Custom" - var/answer = input("What kind ? ","Away/VR") as null|anything in list(possible_options + "Custom") + var/answer = input("What kind ? ","Away/VR") as null|anything in possible_options switch(answer) if(null) return diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index 6dcb73dbf8..cc8bab617d 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -285,19 +285,25 @@ GLOBAL_LIST_EMPTY(cryopod_computers) if(target.mind.assigned_role in GLOB.command_positions) LAZYADD(caught_string, "Head of Staff") addendum = " Be sure to put your locker items back into your locker!" + //SPLURT CHANGES (Addendums changed, only tells antags to ahelp and not heads) if(iscultist(target) || is_servant_of_ratvar(target)) LAZYADD(caught_string, "Cultist") + addendum = AHELP_FIRST_MESSAGE if(is_devil(target)) LAZYADD(caught_string, "Devil") + addendum = AHELP_FIRST_MESSAGE if(target.mind.has_antag_datum(/datum/antagonist/gang)) LAZYADD(caught_string, "Gangster") + addendum = AHELP_FIRST_MESSAGE if(target.mind.has_antag_datum(/datum/antagonist/rev/head)) LAZYADD(caught_string, "Head Revolutionary") + addendum = AHELP_FIRST_MESSAGE if(target.mind.has_antag_datum(/datum/antagonist/rev)) LAZYADD(caught_string, "Revolutionary") + addendum = AHELP_FIRST_MESSAGE if(caught_string) - tgui_alert(target, "You're a [english_list(caught_string)]! [AHELP_FIRST_MESSAGE][addendum]") + tgui_alert(target, "You're a [english_list(caught_string)]. [addendum]") target.client.cryo_warned = world.time if(!istype(target) || !can_interact(user) || !target.Adjacent(user) || !ismob(target) || isanimal(target) || !istype(user.loc, /turf) || target.buckled) diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 2dcb8ef98f..42316b6832 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -918,6 +918,14 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp else to_chat(usr, "Can't become a pAI candidate while not dead!") +/* So long, Mafia mode, disabled for now +/mob/dead/observer/verb/mafia_game_signup() + set category = "Ghost" + set name = "Signup for Mafia" + set desc = "Sign up for a game of Mafia to pass the time while dead." + mafia_signup() +*/ + /mob/dead/observer/verb/mafia_game_signup() set category = "Ghost" set name = "Signup for Mafia" diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 2e9030765a..5a9891a787 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -1037,7 +1037,8 @@ if(incapacitated()) return - switch(alert("Would you like to enter cryo? This will ghost you. Remember to AHELP before cryoing out of important roles, even with no admins online.",,"Yes.","No.")) + //SPLURT CHANGES (No longer tells AI it has to ahelp first) + switch(alert("Would you like to enter cryo? This will ghost you.",,"Yes.","No.")) if("Yes.") src.ghostize(FALSE, penalize = TRUE) var/announce_rank = "Artificial Intelligence," diff --git a/code/modules/ruins/objects_and_mobs/sin_ruins.dm b/code/modules/ruins/objects_and_mobs/sin_ruins.dm index ddb5c9df3c..1de929fd22 100644 --- a/code/modules/ruins/objects_and_mobs/sin_ruins.dm +++ b/code/modules/ruins/objects_and_mobs/sin_ruins.dm @@ -67,7 +67,8 @@ And see a bag full of dice. Confused, \ you take one... and the bag vanishes.") var/turf/T = get_turf(user) - var/obj/item/dice/d20/fate/one_use/critical_fail = new(T) + //SPLURT CHANGE (So long, free wiznerd) + var/obj/item/dice/d20/critical_fail = new(T) user.put_in_hands(critical_fail) qdel(src) diff --git a/code/modules/tgs/core/README.md b/code/modules/tgs/core/README.md index aa3c7a9c9d..b82d8f49e2 100644 --- a/code/modules/tgs/core/README.md +++ b/code/modules/tgs/core/README.md @@ -5,4 +5,5 @@ This folder contains all DMAPI code not directly involved in an API. - [_definitions.dm](./definitions.dm) contains defines needed across DMAPI internals. - [core.dm](./core.dm) contains the implementations of the `/world/proc/TgsXXX()` procs. Many map directly to the `/datum/tgs_api` functions. It also contains the /datum selection and setup code. - [datum.dm](./datum.dm) contains the `/datum/tgs_api` declarations that all APIs must implement. -- [tgs_version.dm](./tgs_version.dm) contains the `/datum/tgs_version` definition \ No newline at end of file +- [tgs_version.dm](./tgs_version.dm) contains the `/datum/tgs_version` definition +- diff --git a/code/modules/tgs/core/datum.dm b/code/modules/tgs/core/datum.dm index 4d37ed662d..68b0330fe8 100644 --- a/code/modules/tgs/core/datum.dm +++ b/code/modules/tgs/core/datum.dm @@ -4,6 +4,8 @@ TGS_DEFINE_AND_SET_GLOBAL(tgs, null) var/datum/tgs_version/version var/datum/tgs_event_handler/event_handler + var/list/warned_deprecated_command_runs + /datum/tgs_api/New(datum/tgs_event_handler/event_handler, datum/tgs_version/version) . = ..() src.event_handler = event_handler diff --git a/code/modules/tgs/includes.dm b/code/modules/tgs/includes.dm index 4018074f4e..25e1b8421a 100644 --- a/code/modules/tgs/includes.dm +++ b/code/modules/tgs/includes.dm @@ -14,4 +14,5 @@ #include "v5\_defines.dm" #include "v5\api.dm" #include "v5\commands.dm" +#include "v5\serializers.dm" #include "v5\undefs.dm" diff --git a/code/modules/tgs/v3210/api.dm b/code/modules/tgs/v3210/api.dm index 3c218d5b10..b881662d71 100644 --- a/code/modules/tgs/v3210/api.dm +++ b/code/modules/tgs/v3210/api.dm @@ -193,16 +193,19 @@ /datum/tgs_api/v3210/ChatChannelInfo() return list() // :omegalul: -/datum/tgs_api/v3210/ChatBroadcast(message, list/channels) +/datum/tgs_api/v3210/ChatBroadcast(datum/tgs_message_content/message, list/channels) if(channels) return TGS_UNIMPLEMENTED + message = UpgradeDeprecatedChatMessage(message) ChatTargetedBroadcast(message, TRUE) ChatTargetedBroadcast(message, FALSE) -/datum/tgs_api/v3210/ChatTargetedBroadcast(message, admin_only) - ExportService("[admin_only ? SERVICE_REQUEST_IRC_ADMIN_CHANNEL_MESSAGE : SERVICE_REQUEST_IRC_BROADCAST] [message]") +/datum/tgs_api/v3210/ChatTargetedBroadcast(datum/tgs_message_content/message, admin_only) + message = UpgradeDeprecatedChatMessage(message) + ExportService("[admin_only ? SERVICE_REQUEST_IRC_ADMIN_CHANNEL_MESSAGE : SERVICE_REQUEST_IRC_BROADCAST] [message.text]") /datum/tgs_api/v3210/ChatPrivateMessage(message, datum/tgs_chat_user/user) + UpgradeDeprecatedChatMessage(message) return TGS_UNIMPLEMENTED /datum/tgs_api/v3210/SecurityLevel() diff --git a/code/modules/tgs/v3210/commands.dm b/code/modules/tgs/v3210/commands.dm index 4ccfc1a8a6..d9bd287465 100644 --- a/code/modules/tgs/v3210/commands.dm +++ b/code/modules/tgs/v3210/commands.dm @@ -10,9 +10,12 @@ var/warned_about_the_dangers_of_robutussin = !warnings_only for(var/I in typesof(/datum/tgs_chat_command) - /datum/tgs_chat_command) if(!warned_about_the_dangers_of_robutussin) - TGS_ERROR_LOG("Custom chat commands in [ApiVersion()] lacks the /datum/tgs_chat_user/sender.channel field!") + TGS_WARNING_LOG("Custom chat commands in [ApiVersion()] lacks the /datum/tgs_chat_user/sender.channel field!") warned_about_the_dangers_of_robutussin = TRUE var/datum/tgs_chat_command/stc = I + if(stc.ignore_type == I) + continue + var/command_name = initial(stc.name) if(!command_name || findtext(command_name, " ") || findtext(command_name, "'") || findtext(command_name, "\"")) if(warnings_only && !warned_command_names[command_name]) @@ -49,4 +52,7 @@ sender = "<@[sender]>" user.mention = sender - return stc.Run(user, params) || TRUE + var/datum/tgs_message_content/result = stc.Run(user, params) + result = UpgradeDeprecatedCommandResponse(result, command) + + return result?.text || TRUE diff --git a/code/modules/tgs/v4/api.dm b/code/modules/tgs/v4/api.dm index 0e37a7aa24..2f05c38633 100644 --- a/code/modules/tgs/v4/api.dm +++ b/code/modules/tgs/v4/api.dm @@ -256,33 +256,36 @@ /datum/tgs_api/v4/Revision() return cached_revision -/datum/tgs_api/v4/ChatBroadcast(message, list/channels) +/datum/tgs_api/v4/ChatBroadcast(datum/tgs_message_content/message, list/channels) var/list/ids if(length(channels)) ids = list() for(var/I in channels) var/datum/tgs_chat_channel/channel = I ids += channel.id - message = list("message" = message, "channelIds" = ids) + message = UpgradeDeprecatedChatMessage(message) + message = list("message" = message.text, "channelIds" = ids) if(intercepted_message_queue) intercepted_message_queue += list(message) else Export(TGS4_COMM_CHAT, message) -/datum/tgs_api/v4/ChatTargetedBroadcast(message, admin_only) +/datum/tgs_api/v4/ChatTargetedBroadcast(datum/tgs_message_content/message, admin_only) var/list/channels = list() for(var/I in ChatChannelInfo()) var/datum/tgs_chat_channel/channel = I if (!channel.is_private_channel && ((channel.is_admin_channel && admin_only) || (!channel.is_admin_channel && !admin_only))) channels += channel.id - message = list("message" = message, "channelIds" = channels) + message = UpgradeDeprecatedChatMessage(message) + message = list("message" = message.text, "channelIds" = channels) if(intercepted_message_queue) intercepted_message_queue += list(message) else Export(TGS4_COMM_CHAT, message) -/datum/tgs_api/v4/ChatPrivateMessage(message, datum/tgs_chat_user/user) - message = list("message" = message, "channelIds" = list(user.channel.id)) +/datum/tgs_api/v4/ChatPrivateMessage(datum/tgs_message_content/message, datum/tgs_chat_user/user) + message = UpgradeDeprecatedChatMessage(message) + message = list("message" = message.text, "channelIds" = list(user.channel.id)) if(intercepted_message_queue) intercepted_message_queue += list(message) else diff --git a/code/modules/tgs/v4/commands.dm b/code/modules/tgs/v4/commands.dm index 4ca1500167..d6d3d718d4 100644 --- a/code/modules/tgs/v4/commands.dm +++ b/code/modules/tgs/v4/commands.dm @@ -3,6 +3,9 @@ custom_commands = list() for(var/I in typesof(/datum/tgs_chat_command) - /datum/tgs_chat_command) var/datum/tgs_chat_command/stc = new I + if(stc.ignore_type == I) + continue + var/command_name = stc.name if(!command_name || findtext(command_name, " ") || findtext(command_name, "'") || findtext(command_name, "\"")) TGS_ERROR_LOG("Custom command [command_name] ([I]) can't be used as it is empty or contains illegal characters!") @@ -34,8 +37,8 @@ var/datum/tgs_chat_command/sc = custom_commands[command] if(sc) - var/result = sc.Run(u, params) - if(result == null) - result = "" - return result + var/datum/tgs_message_content/result = sc.Run(u, params) + result = UpgradeDeprecatedCommandResponse(result, command) + + return result?.text return "Unknown command: [command]!" diff --git a/code/modules/tgs/v5/README.md b/code/modules/tgs/v5/README.md index 5b48d57a1f..619b58cd72 100644 --- a/code/modules/tgs/v5/README.md +++ b/code/modules/tgs/v5/README.md @@ -2,7 +2,9 @@ This DMAPI implements bridge requests using HTTP GET requests to TGS. It has no security restrictions. +- [__interop_version.dm](./__interop_version.dm) contains the version of the API used between the DMAPI and TGS. - [_defines.dm](./_defines.dm) contains constant definitions. - [api.dm](./api.dm) contains the bulk of the API code. - [commands.dm](./commands.dm) contains functions relating to `/datum/tgs_chat_command`s. +- [serializers.dm](./serializers.dm) contains function to help convert interop `/datum`s into a JSON encodable `list()` format. - [undefs.dm](./undefs.dm) Undoes the work of `_defines.dm`. diff --git a/code/modules/tgs/v5/__interop_version.dm b/code/modules/tgs/v5/__interop_version.dm new file mode 100644 index 0000000000..4add7374ad --- /dev/null +++ b/code/modules/tgs/v5/__interop_version.dm @@ -0,0 +1 @@ +"5.4.0" diff --git a/code/modules/tgs/v5/_defines.dm b/code/modules/tgs/v5/_defines.dm index 10bc4cbe40..7f31c23ef4 100644 --- a/code/modules/tgs/v5/_defines.dm +++ b/code/modules/tgs/v5/_defines.dm @@ -25,7 +25,6 @@ #define DMAPI5_BRIDGE_RESPONSE_NEW_PORT "newPort" #define DMAPI5_BRIDGE_RESPONSE_RUNTIME_INFORMATION "runtimeInformation" -#define DMAPI5_CHAT_MESSAGE_TEXT "text" #define DMAPI5_CHAT_MESSAGE_CHANNEL_IDS "channelIds" #define DMAPI5_RUNTIME_INFORMATION_ACCESS_IDENTIFIER "accessIdentifier" @@ -75,6 +74,7 @@ #define DMAPI5_TOPIC_PARAMETER_CHAT_UPDATE "chatUpdate" #define DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION "newServerVersion" +#define DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE "commandResponse" #define DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE_MESSAGE "commandResponseMessage" #define DMAPI5_TOPIC_RESPONSE_CHAT_RESPONSES "chatResponses" @@ -93,6 +93,7 @@ #define DMAPI5_CHAT_CHANNEL_IS_ADMIN_CHANNEL "isAdminChannel" #define DMAPI5_CHAT_CHANNEL_IS_PRIVATE_CHANNEL "isPrivateChannel" #define DMAPI5_CHAT_CHANNEL_TAG "tag" +#define DMAPI5_CHAT_CHANNEL_EMBEDS_SUPPORTED "embedsSupported" #define DMAPI5_CUSTOM_CHAT_COMMAND_NAME "name" #define DMAPI5_CUSTOM_CHAT_COMMAND_HELP_TEXT "helpText" diff --git a/code/modules/tgs/v5/api.dm b/code/modules/tgs/v5/api.dm index 572944ed40..36610b1242 100644 --- a/code/modules/tgs/v5/api.dm +++ b/code/modules/tgs/v5/api.dm @@ -19,7 +19,7 @@ /datum/tgs_api/v5/ApiVersion() return new /datum/tgs_version( - #include "interop_version.dm" + #include "__interop_version.dm" ) /datum/tgs_api/v5/OnWorldNew(minimum_required_security_level) @@ -104,6 +104,7 @@ return json_encode(response) /datum/tgs_api/v5/OnTopic(T) + RequireInitialBridgeResponse() var/list/params = params2list(T) var/json = params[DMAPI5_TOPIC_DATA] if(!json) @@ -297,7 +298,15 @@ RequireInitialBridgeResponse() return revision -/datum/tgs_api/v5/ChatBroadcast(message, list/channels) +// Common proc b/c it's used by the V3/V4 APIs +/datum/tgs_api/proc/UpgradeDeprecatedChatMessage(datum/tgs_message_content/message) + if(!istext(message)) + return message + + TGS_WARNING_LOG("Received legacy string when a [/datum/tgs_message_content] was expected. Please audit all calls to TgsChatBroadcast, TgsChatTargetedBroadcast, and TgsChatPrivateMessage to ensure they use the new /datum.") + return new /datum/tgs_message_content(message) + +/datum/tgs_api/v5/ChatBroadcast(datum/tgs_message_content/message, list/channels) if(!length(channels)) channels = ChatChannelInfo() @@ -306,26 +315,33 @@ var/datum/tgs_chat_channel/channel = I ids += channel.id - message = list(DMAPI5_CHAT_MESSAGE_TEXT = message, DMAPI5_CHAT_MESSAGE_CHANNEL_IDS = ids) + message = UpgradeDeprecatedChatMessage(message) + message = message._interop_serialize() + message[DMAPI5_CHAT_MESSAGE_CHANNEL_IDS] = ids if(intercepted_message_queue) intercepted_message_queue += list(message) else Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = message)) -/datum/tgs_api/v5/ChatTargetedBroadcast(message, admin_only) +/datum/tgs_api/v5/ChatTargetedBroadcast(datum/tgs_message_content/message, admin_only) var/list/channels = list() for(var/I in ChatChannelInfo()) var/datum/tgs_chat_channel/channel = I if (!channel.is_private_channel && ((channel.is_admin_channel && admin_only) || (!channel.is_admin_channel && !admin_only))) channels += channel.id - message = list(DMAPI5_CHAT_MESSAGE_TEXT = message, DMAPI5_CHAT_MESSAGE_CHANNEL_IDS = channels) + + message = UpgradeDeprecatedChatMessage(message) + message = message._interop_serialize() + message[DMAPI5_CHAT_MESSAGE_CHANNEL_IDS] = channels if(intercepted_message_queue) intercepted_message_queue += list(message) else Bridge(DMAPI5_BRIDGE_COMMAND_CHAT_SEND, list(DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE = message)) -/datum/tgs_api/v5/ChatPrivateMessage(message, datum/tgs_chat_user/user) - message = list(DMAPI5_CHAT_MESSAGE_TEXT = message, DMAPI5_CHAT_MESSAGE_CHANNEL_IDS = list(user.channel.id)) +/datum/tgs_api/v5/ChatPrivateMessage(datum/tgs_message_content/message, datum/tgs_chat_user/user) + message = UpgradeDeprecatedChatMessage(message) + message = message._interop_serialize() + message[DMAPI5_CHAT_MESSAGE_CHANNEL_IDS] = list(user.channel.id) if(intercepted_message_queue) intercepted_message_queue += list(message) else @@ -354,6 +370,7 @@ channel.is_admin_channel = channel_json[DMAPI5_CHAT_CHANNEL_IS_ADMIN_CHANNEL] channel.is_private_channel = channel_json[DMAPI5_CHAT_CHANNEL_IS_PRIVATE_CHANNEL] channel.custom_tag = channel_json[DMAPI5_CHAT_CHANNEL_TAG] + channel.embeds_supported = channel_json[DMAPI5_CHAT_CHANNEL_EMBEDS_SUPPORTED] return channel /datum/tgs_api/v5/SecurityLevel() diff --git a/code/modules/tgs/v5/commands.dm b/code/modules/tgs/v5/commands.dm index 6d31dd3422..71ede42c3b 100644 --- a/code/modules/tgs/v5/commands.dm +++ b/code/modules/tgs/v5/commands.dm @@ -3,14 +3,17 @@ custom_commands = list() for(var/I in typesof(/datum/tgs_chat_command) - /datum/tgs_chat_command) var/datum/tgs_chat_command/stc = new I + if(stc.ignore_type == I) + continue + var/command_name = stc.name if(!command_name || findtext(command_name, " ") || findtext(command_name, "'") || findtext(command_name, "\"")) - TGS_WARNING_LOG("Custom command [command_name] ([I]) can't be used as it is empty or contains illegal characters!") + TGS_ERROR_LOG("Custom command [command_name] ([I]) can't be used as it is empty or contains illegal characters!") continue if(results[command_name]) var/datum/other = custom_commands[command_name] - TGS_WARNING_LOG("Custom commands [other.type] and [I] have the same name (\"[command_name]\"), only [other.type] will be available!") + TGS_ERROR_LOG("Custom commands [other.type] and [I] have the same name (\"[command_name]\"), only [other.type] will be available!") continue results += list(list(DMAPI5_CUSTOM_CHAT_COMMAND_NAME = command_name, DMAPI5_CUSTOM_CHAT_COMMAND_HELP_TEXT = stc.help_text, DMAPI5_CUSTOM_CHAT_COMMAND_ADMIN_ONLY = stc.admin_only)) custom_commands[command_name] = stc @@ -30,11 +33,28 @@ var/datum/tgs_chat_command/sc = custom_commands[command] if(sc) - var/text_response = sc.Run(u, params) + var/datum/tgs_message_content/response = sc.Run(u, params) + response = UpgradeDeprecatedCommandResponse(response, command) + var/list/topic_response = list() - if(!istext(text_response)) - TGS_ERROR_LOG("Custom command [command] should return a string! Got: \"[text_response]\"") - text_response = null - topic_response[DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE_MESSAGE] = text_response + topic_response[DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE_MESSAGE] = response?.text + topic_response[DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE] = response?._interop_serialize() return json_encode(topic_response) return TopicResponse("Unknown custom chat command: [command]!") + +// Common proc b/c it's used by the V3/V4 APIs +/datum/tgs_api/proc/UpgradeDeprecatedCommandResponse(datum/tgs_message_content/response, command) + // Backwards compatibility, used to return a string + if(istext(response)) + warned_deprecated_command_runs = warned_deprecated_command_runs || list() + if(!warned_deprecated_command_runs[command]) + TGS_WARNING_LOG("Custom chat command \"[command]\" is still returning a string. This behaviour is deprecated, please upgrade it to return a [/datum/tgs_message_content].") + warned_deprecated_command_runs[command] = TRUE + + return new /datum/tgs_message_content(response) + + if(!istype(response)) + TGS_ERROR_LOG("Custom chat command \"[command]\" should return a [/datum/tgs_message_content]! Got: \"[response]\"") + return null + + return response diff --git a/code/modules/tgs/v5/interop_version.dm b/code/modules/tgs/v5/interop_version.dm deleted file mode 100644 index c7bf62ecae..0000000000 --- a/code/modules/tgs/v5/interop_version.dm +++ /dev/null @@ -1 +0,0 @@ -"5.3.0" diff --git a/code/modules/tgs/v5/serializers.dm b/code/modules/tgs/v5/serializers.dm new file mode 100644 index 0000000000..38814e2d9f --- /dev/null +++ b/code/modules/tgs/v5/serializers.dm @@ -0,0 +1,52 @@ +/datum/tgs_message_content/proc/_interop_serialize() + return list("text" = text, "embed" = embed?._interop_serialize()) + +/datum/tgs_chat_embed/proc/_interop_serialize() + CRASH("Base /proc/interop_serialize called on [type]!") + +/datum/tgs_chat_embed/structure/_interop_serialize() + var/list/serialized_fields + if(islist(fields)) + serialized_fields = list() + for(var/datum/tgs_chat_embed/field/field as anything in fields) + serialized_fields += list(field._interop_serialize()) + return list( + "title" = title, + "description" = description, + "url" = url, + "timestamp" = timestamp, + "colour" = colour, + "image" = image?._interop_serialize(), + "thumbnail" = thumbnail?._interop_serialize(), + "video" = video?._interop_serialize(), + "footer" = footer?._interop_serialize(), + "provider" = provider?._interop_serialize(), + "author" = author?._interop_serialize(), + "fields" = serialized_fields + ) + +/datum/tgs_chat_embed/media/_interop_serialize() + return list( + "url" = url, + "width" = width, + "height" = height, + "proxyUrl" = proxy_url + ) + +/datum/tgs_chat_embed/provider/_interop_serialize() + return list( + "url" = url, + "name" = name + ) + +/datum/tgs_chat_embed/provider/author/_interop_serialize() + . = ..() + .["iconUrl"] = icon_url + .["proxyIconUrl"] = proxy_icon_url + +/datum/tgs_chat_embed/field/_interop_serialize() + return list( + "name" = name, + "value" = value, + "isInline" = is_inline + ) diff --git a/code/modules/tgs/v5/undefs.dm b/code/modules/tgs/v5/undefs.dm index 5885a60e75..6209945372 100644 --- a/code/modules/tgs/v5/undefs.dm +++ b/code/modules/tgs/v5/undefs.dm @@ -25,7 +25,6 @@ #undef DMAPI5_BRIDGE_RESPONSE_NEW_PORT #undef DMAPI5_BRIDGE_RESPONSE_RUNTIME_INFORMATION -#undef DMAPI5_CHAT_MESSAGE_TEXT #undef DMAPI5_CHAT_MESSAGE_CHANNEL_IDS #undef DMAPI5_RUNTIME_INFORMATION_ACCESS_IDENTIFIER @@ -75,6 +74,7 @@ #undef DMAPI5_TOPIC_PARAMETER_CHAT_UPDATE #undef DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION +#undef DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE #undef DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE_MESSAGE #undef DMAPI5_TOPIC_RESPONSE_CHAT_RESPONSES @@ -93,6 +93,7 @@ #undef DMAPI5_CHAT_CHANNEL_IS_ADMIN_CHANNEL #undef DMAPI5_CHAT_CHANNEL_IS_PRIVATE_CHANNEL #undef DMAPI5_CHAT_CHANNEL_TAG +#undef DMAPI5_CHAT_CHANNEL_EMBEDS_SUPPORTED #undef DMAPI5_CUSTOM_CHAT_COMMAND_NAME #undef DMAPI5_CUSTOM_CHAT_COMMAND_HELP_TEXT diff --git a/code/modules/vending/autodrobe.dm b/code/modules/vending/autodrobe.dm index e5b47f3a80..5a6c7c973f 100644 --- a/code/modules/vending/autodrobe.dm +++ b/code/modules/vending/autodrobe.dm @@ -164,7 +164,6 @@ /obj/item/clothing/under/costume/christmas/croptop/green = 3, /obj/item/clothing/head/christmashat = 3, /obj/item/clothing/head/christmashatg = 3, - /obj/item/clothing/under/wedding_dress = 1, /obj/item/clothing/under/costume/drfreeze = 1) refill_canister = /obj/item/vending_refill/autodrobe default_price = PRICE_ALMOST_CHEAP diff --git a/config/splurt/discord.txt b/config/splurt/discord.txt index b58f4479c0..e182f12f43 100644 --- a/config/splurt/discord.txt +++ b/config/splurt/discord.txt @@ -34,3 +34,8 @@ ## Uncomment this if your players need to remain in the discord to keep verification ## ## Note, this only produces aesthetic changes, the real magic happens with the redbot modules #FORCED_DISCORD_STAY + +# Enable the sauce commands +## Uncomment if you've set up title_screens_sources.json to source your splashscreens +## This enables people to use splashsauce and allsplashscreens to get info about the splashscreens +#SAUCE_COMMAND_ENABLED diff --git a/config/splurt/general.txt b/config/splurt/general.txt index 71f874a79c..b42b8e7234 100644 --- a/config/splurt/general.txt +++ b/config/splurt/general.txt @@ -18,3 +18,7 @@ WEIGHTED_STATION_TRAITS # SM delamination # Comment to make the SM not explode SM_DELAMINATION + +# Protolathe access +# Comment to make protolathes and mechfabs use their access locks +PROTOLOCK_ALL_ACCESS diff --git a/config/splurt/title_screens_sources.json b/config/splurt/title_screens_sources.json new file mode 100644 index 0000000000..6b64107deb --- /dev/null +++ b/config/splurt/title_screens_sources.json @@ -0,0 +1,8 @@ +{ + "example_screen.png": { + "name": "name of the pic", + "author": "name of the author", + "link": "link to the pic", + "dw_link": "direct download link of the pic" + } +} diff --git a/html/changelogs/archive/2023-03.yml b/html/changelogs/archive/2023-03.yml index 4208664ef4..4dde24147a 100644 --- a/html/changelogs/archive/2023-03.yml +++ b/html/changelogs/archive/2023-03.yml @@ -93,3 +93,33 @@ - bugfix: Exosuit Drill Mech can no longer drill multiple times at once - bugfix: Exosuit Drill Mech can no longer drill people past melee range - bugfix: Exosuit Drill Mech now does the correct amount of damage to objects + LeDrascol: + - rscadd: 'Added new cybernetic organ: Mesmer Eyes' + - rscadd: Added examine text to the Hypnotic Gaze quirk + - tweak: Hypnotic Gaze consent prompt now includes additional information + - tweak: Brainwashed victims are no longer shown in the round-end panel + - admin: Hypnotic Gaze quirk can now be added and removed mid-round + - balance: Mech fabricators now use their access restrictions + - balance: Removed access requirements for maintenance and off-station mech fabricators +2023-03-29: + Anonymous (feat. Bagzerk): + - rscadd: 'Emote: \*agony.' +2023-03-30: + BongaTheProto: + - balance: Removes the locks on protolathes and other machines + - balance: Well actually makes it a config but it's set to disable the locks so + potato tomato + CloverIsLucky: + - rscdel: Removed duplicated mining points card. + KeplerWasTaken: + - rscdel: Removes Die of Fate from Academy + - rscdel: Removes Die of Fate from Greed Ruin + - tweak: BO and BS arrival on station messages are now more fitting + - bugfix: Head of Staff and AI cryo messages no longer say you need to ahelp + PhazeJump: + - bugfix: Fixed the eastern dragon tail + - bugfix: Added a file for succubus tail wagging sprite + - bugfix: Added some code for the succubus tail not to disappear while wagging +2023-03-31: + legumesan: + - rscadd: new hairstyle diff --git a/html/changelogs/archive/2023-04.yml b/html/changelogs/archive/2023-04.yml new file mode 100644 index 0000000000..71ff37a767 --- /dev/null +++ b/html/changelogs/archive/2023-04.yml @@ -0,0 +1,5 @@ +2023-04-02: + BongaTheProto: + - rscadd: Adds a lot of new suits + - bugfix: fixes the tuxedo sprites + - bugfix: fixes the double wedding crate diff --git a/modular_sand/code/modules/jobs/job_types/_job_alt_titles.dm b/modular_sand/code/modules/jobs/job_types/_job_alt_titles.dm index 03f5850a29..f1e7b2eb1c 100644 --- a/modular_sand/code/modules/jobs/job_types/_job_alt_titles.dm +++ b/modular_sand/code/modules/jobs/job_types/_job_alt_titles.dm @@ -1,97 +1,201 @@ //Command /datum/job/captain - alt_titles = list("Colony Overseer") + alt_titles = list( + "Colony Overseer", + "Senator", + "Consul" + ) /datum/job/chief_engineer - alt_titles = list("Senior Engineer") + alt_titles = list( + "Senior Engineer" + ) /datum/job/hop - alt_titles = list("Crew Resource Officer", "Executive Officer") + alt_titles = list( + "Crew Resource Officer", + "Executive Officer" + ) /datum/job/hos - alt_titles = list("Chief of Security", "Sheriff") + alt_titles = list( + "Chief of Security", + "Sheriff", + "Praetor" + ) /datum/job/qm - alt_titles = list("Resource Manager", "Logistics Supervisor") + alt_titles = list( + "Resource Manager", + "Logistics Supervisor" + ) /datum/job/rd - alt_titles = list("Chief Science Officer", "Research Overseer") + alt_titles = list( + "Chief Science Officer", + "Research Overseer" + ) //Engineering /datum/job/atmos - alt_titles = list("Firefighter", "Life Support Specialist") + alt_titles = list( + "Firefighter", + "Life Support Specialist" + ) /datum/job/engineer - alt_titles = list("Maintenance Technician", "Engine Technician", "Electrician") + alt_titles = list( + "Maintenance Technician", + "Engine Technician", + "Electrician" + ) //Service /datum/job/assistant - alt_titles = list("Civilian", "Visitor", "Businessman", "Trader", "Entertainer", "Intern", "Off-Duty Civilian") + alt_titles = list( + "Civilian", + "Visitor", + "Businessman", + "Trader", + "Entertainer", + "Intern", + "Off-Duty Civilian" + ) /datum/job/bartender - alt_titles = list("Barista") + alt_titles = list( + "Barista" + ) /datum/job/chaplain - alt_titles = list("Priest", "Cult Leader", "Pope", "Bishop") + alt_titles = list( + "Priest", + "Cult Leader", + "Pope", + "Bishop", // How about you actually say something about it instead of crying on a downstream's comments i won't read. + "Pontiff" + ) /datum/job/clown //The most useless role in the game, delet this - alt_titles = list("Entertainer") + alt_titles = list( + "Entertainer" + ) /datum/job/cook - alt_titles = list("Culinary Artist", "Butcher", "Chef", "Nutritionist") + alt_titles = list( + "Culinary Artist", + "Butcher", + "Chef", + "Nutritionist" + ) /datum/job/curator - alt_titles = list("Journalist", "Librarian") + alt_titles = list( + "Journalist", + "Librarian" + ) /datum/job/hydro - alt_titles = list("Gardener", "Herbalist", "Botanical Researcher", "Florist") + alt_titles = list( + "Gardener", + "Herbalist", + "Botanical Researcher", + "Florist" + ) /datum/job/janitor - alt_titles = list("Custodian", "Sanitation Technician", "Maid") + alt_titles = list( + "Custodian", + "Sanitation Technician", + "Maid" + ) /datum/job/lawyer - alt_titles = list("Human Resources Agent", "Internal Affairs Agent") + alt_titles = list( + "Human Resources Agent", + "Internal Affairs Agent" + ) /datum/job/mime - alt_titles = list("Performer") + alt_titles = list( + "Performer" + ) //Science /datum/job/roboticist - alt_titles = list("Biomechanical Engineer", "Mechatronic Engineer", "Mechanic", "Robotics Operator") + alt_titles = list( + "Biomechanical Engineer", + "Mechatronic Engineer", + "Mechanic", + "Robotics Operator" + ) /datum/job/scientist - alt_titles = list("Circuitry Designer", "Xenobiologist", "Xenobotanist", "Chemical Researcher") + alt_titles = list( + "Circuitry Designer", + "Xenobiologist", + "Xenobotanist", + "Chemical Researcher" + ) //Medical /datum/job/chemist - alt_titles = list("Pharmacist", "Pharmacologist") + alt_titles = list( + "Pharmacist", + "Pharmacologist" + ) /datum/job/doctor - alt_titles = list("Nurse", "Surgeon", "Medical Secretary") + alt_titles = list( + "Nurse", + "Surgeon", + "Medical Secretary" + ) /datum/job/geneticist - alt_titles = list("Genetic Therapist") + alt_titles = list( + "Genetic Therapist" + ) /datum/job/paramedic - alt_titles = list("Emergency Medical Technician") + alt_titles = list( + "Emergency Medical Technician" + ) /datum/job/virologist - alt_titles = list("Pathologist") + alt_titles = list( + "Pathologist" + ) //Security /datum/job/detective - alt_titles = list("Forensics Technician", "Private Investigator") + alt_titles = list( + "Forensics Technician", + "Private Investigator" + ) /datum/job/officer - alt_titles = list("Security Agent", "Security Guard", "Guardsman", "Enforcer", "Police Officer") + alt_titles = list( + "Security Cadet", + "Security Guard", + "Peacekeeper", + "Enforcer" + ) /datum/job/warden - alt_titles = list("Brig Chief") + alt_titles = list( + "Brig Chief" + ) //Cargo /datum/job/cargo_tech - alt_titles = list("Shipping Specialist", "Delivery Manager") + alt_titles = list( + "Shipping Specialist", + "Delivery Manager" + ) /datum/job/mining - alt_titles = list("Explorer") + alt_titles = list( + "Explorer" + ) diff --git a/modular_sand/code/modules/mob/dead/new_player/sprite_accessories/ears.dm b/modular_sand/code/modules/mob/dead/new_player/sprite_accessories/ears.dm index ae41189936..887c2be58d 100644 --- a/modular_sand/code/modules/mob/dead/new_player/sprite_accessories/ears.dm +++ b/modular_sand/code/modules/mob/dead/new_player/sprite_accessories/ears.dm @@ -9,3 +9,10 @@ icon = 'modular_sand/icons/mob/mam_ears.dmi' icon_state = "cobraears" matrixed_sections = MATRIX_RED_GREEN + +/datum/sprite_accessory/ears/mam_ears/zorgoia //Ported from ARFS-Coyote + name = "Zorgoia" + icon_state = "zorgoia" + icon = 'modular_sand/icons/mob/mam_ears.dmi' + color_src = MATRIXED + matrixed_sections = MATRIX_RED diff --git a/modular_sand/code/modules/mob/dead/new_player/sprite_accessories/tails.dm b/modular_sand/code/modules/mob/dead/new_player/sprite_accessories/tails.dm index 9465fc210f..9b04a8d631 100644 --- a/modular_sand/code/modules/mob/dead/new_player/sprite_accessories/tails.dm +++ b/modular_sand/code/modules/mob/dead/new_player/sprite_accessories/tails.dm @@ -9,3 +9,24 @@ icon = 'modular_sand/icons/mob/mam_tails.dmi' icon_state = "demonspade" matrixed_sections = MATRIX_RED + +/datum/sprite_accessory/tails/mam_tails/zorgoia //Ported from ARFS-Coyote + name = "Zorgoia" + icon_state = "zorgoia" + icon = 'modular_sand/icons/mob/mam_tails.dmi' + color_src = MATRIXED + matrixed_sections = MATRIX_ALL + +/datum/sprite_accessory/tails/mam_tails/fennecdown //Ported from ARFS-Coyote + name = "Downward Fennec Tail" + icon_state = "downwardfennec" + icon = 'modular_sand/icons/mob/mam_tails.dmi' + color_src = MATRIXED + matrixed_sections = MATRIX_RED_GREEN + +/datum/sprite_accessory/tails/human/fennecdown //Ported from ARFS-Coyote + name = "Downward Fennec Tail" + icon_state = "downwardfennec" + icon = 'modular_sand/icons/mob/mam_tails.dmi' + color_src = MATRIXED + matrixed_sections = MATRIX_RED_GREEN diff --git a/modular_sand/code/modules/mob/living/silicon/robot/robot_modules.dm b/modular_sand/code/modules/mob/living/silicon/robot/robot_modules.dm index dd9ea00929..2bbb9f4c7d 100644 --- a/modular_sand/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/modular_sand/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -88,10 +88,11 @@ basic_modules += /obj/item/reagent_containers/borghypo/borgshaker/sodashaker basic_modules += /obj/item/reagent_containers/borghypo/borgshaker/miscshaker . = ..() - +/* /obj/item/robot_module/miner/Initialize(mapload) basic_modules += /obj/item/card/id/miningborg - . = ..() + . = ..() +*/ //Sandstorm, the main citacode already gives a mining points card to the minerborg, borgs don't need another one. /datum/robot_energy_storage/plasma name = "Plasma Buffer Container" diff --git a/modular_sand/code/modules/mob/mob.dm b/modular_sand/code/modules/mob/mob.dm index ba6de1bc02..2aeb71c20a 100644 --- a/modular_sand/code/modules/mob/mob.dm +++ b/modular_sand/code/modules/mob/mob.dm @@ -28,7 +28,11 @@ /mob/proc/set_thirst(change) thirst = max(0, change) -/mob/proc/can_use_production(obj/machinery/rnd/production/machine_target) +/mob/proc/can_use_production(obj/machinery/machine_target) + // Check if access is required + if(!machine_target.req_access) + return TRUE + // Check if server is NOT using minimal access // This is intended for low populations if((!PROTOLOCK_DURING_LOWPOP) && (!JOB_MINIMAL_ACCESS)) @@ -97,5 +101,42 @@ // Default to false return FALSE +/mob/proc/can_use_mechfab_topic(obj/machinery/mecha_part_fabricator/machine_target, action, var/list/params) + // Basic actions that are always permitted + if(action == "sync_rnd") + return TRUE + + // Define user's access type + var/user_access = usr.can_use_production(machine_target) + + // Switch result based on access type + // This currently doesn't do anything special + switch(user_access) + // Type: Low population + if(PROTOLOCK_ACCESS_LOWPOP) + return TRUE + + // Type: Standard + if(PROTOLOCK_ACCESS_NORMAL) + return TRUE + + // Type: Captain + if(PROTOLOCK_ACCESS_CAPTAIN) + return TRUE + + // Type: Mineral / ORM + if(PROTOLOCK_ACCESS_MINERAL) + // Check if permitted topic + if(action == "remove_mat") + return TRUE + + // Topic prohibited + // Deny usage + else + return FALSE + + // Default to false + return FALSE + #undef JOB_MINIMAL_ACCESS #undef PROTOLOCK_DURING_LOWPOP diff --git a/modular_sand/code/modules/research/machinery/_production.dm b/modular_sand/code/modules/research/machinery/_production.dm index 103d83c98f..a61486f138 100644 --- a/modular_sand/code/modules/research/machinery/_production.dm +++ b/modular_sand/code/modules/research/machinery/_production.dm @@ -5,10 +5,6 @@ gen_access() /obj/machinery/rnd/production/ui_interact(mob/user) - // Check if access is required - if(!req_access) - return ..() - // Check if user can use machine if(!user.can_use_production(src)) // Warn in local chat and return @@ -19,10 +15,6 @@ . = ..() /obj/machinery/rnd/production/Topic(raw, ls) - // Check if access is required - if(!req_access) - return ..() - // Check if user can use machine if(!usr.can_use_production_topic(src, raw, ls)) // Alert in local chat diff --git a/modular_sand/code/modules/vehicles/mecha/mech_fabricator.dm b/modular_sand/code/modules/vehicles/mecha/mech_fabricator.dm new file mode 100644 index 0000000000..d252de855c --- /dev/null +++ b/modular_sand/code/modules/vehicles/mecha/mech_fabricator.dm @@ -0,0 +1,42 @@ +/obj/machinery/mecha_part_fabricator/maint + req_access = null + +/obj/machinery/mecha_part_fabricator/offstation + req_access = null + +/obj/machinery/mecha_part_fabricator/ui_interact(mob/user, datum/tgui/ui) + // Check if user can use machine + if(!user.can_use_production(src)) + // Check if UI doesn't exist + if(!ui) + // Send normal warning + say("Access denied: No valid credentials detected.") + + // UI does exist + else + // Close UI + ui.close() + + // Send alternate warning + say("Access revoked: Valid credentials no longer detected.") + + // Return + return + + // Return normally + . = ..() + +/obj/machinery/mecha_part_fabricator/ui_act(action, var/list/params) + // Check if user can use machine + if(!usr.can_use_mechfab_topic(src, action, params)) + // Alert in local chat + usr.visible_message(span_warning("[usr] pushes a button on [src], causing it to chime with the familiar sound of rejection."), span_warning("The machine buzzes with a soft chime. It seems you don't have access to that button.")) + + // Play sound + playsound(loc, 'sound/machines/uplinkerror.ogg', 70, 0) + + // Return + return + + // Return normally + . = ..() diff --git a/modular_sand/icons/mob/mam_ears.dmi b/modular_sand/icons/mob/mam_ears.dmi index d15acea8dd..2360da03c9 100644 Binary files a/modular_sand/icons/mob/mam_ears.dmi and b/modular_sand/icons/mob/mam_ears.dmi differ diff --git a/modular_sand/icons/mob/mam_tails.dmi b/modular_sand/icons/mob/mam_tails.dmi index c0fa5d349e..faa2b13ffa 100644 Binary files a/modular_sand/icons/mob/mam_tails.dmi and b/modular_sand/icons/mob/mam_tails.dmi differ diff --git a/modular_splurt/code/controllers/configuration/entries/splurt_discord.dm b/modular_splurt/code/controllers/configuration/entries/splurt_discord.dm index c2c100fe6d..5c1750a560 100644 --- a/modular_splurt/code/controllers/configuration/entries/splurt_discord.dm +++ b/modular_splurt/code/controllers/configuration/entries/splurt_discord.dm @@ -10,3 +10,5 @@ config_entry_value = "verify" /datum/config_entry/flag/forced_discord_stay + +/datum/config_entry/flag/sauce_command_enabled diff --git a/modular_splurt/code/controllers/configuration/entries/splurt_general.dm b/modular_splurt/code/controllers/configuration/entries/splurt_general.dm index a6e389db1b..f73ee4a324 100644 --- a/modular_splurt/code/controllers/configuration/entries/splurt_general.dm +++ b/modular_splurt/code/controllers/configuration/entries/splurt_general.dm @@ -9,3 +9,5 @@ config_entry_value = DEFAULT_SAVE_SLOTS /datum/config_entry/flag/sm_delamination + +/datum/config_entry/flag/protolock_all_access diff --git a/modular_splurt/code/datums/mood_events/generic_positive_events.dm b/modular_splurt/code/datums/mood_events/generic_positive_events.dm index 2128822dda..ecab088eac 100644 --- a/modular_splurt/code/datums/mood_events/generic_positive_events.dm +++ b/modular_splurt/code/datums/mood_events/generic_positive_events.dm @@ -38,3 +38,8 @@ description = span_nicegreen("I tasted familiarity from the blood I drank!\n") mood_change = 2 timeout = 2 MINUTES + +/datum/mood_event/brainwashed + description = span_mind_control("I\'ve been shown the path, and I must follow it!\n") + mood_change = 1 + hidden = TRUE diff --git a/modular_splurt/code/datums/traits/neutral.dm b/modular_splurt/code/datums/traits/neutral.dm index ff9cd14b61..44a529dd06 100644 --- a/modular_splurt/code/datums/traits/neutral.dm +++ b/modular_splurt/code/datums/traits/neutral.dm @@ -70,11 +70,31 @@ lose_text = span_notice("Your eyes return to normal.") medical_record_text = "Prolonged exposure to Patient's eyes exhibits soporific effects." -/datum/quirk/Hypnotic_gaze/on_spawn() - var/mob/living/carbon/human/Hypno_eyes = quirk_holder - var/datum/action/innate/Hypnotize/spell = new - spell.Grant(Hypno_eyes) - spell.owner = Hypno_eyes +/datum/quirk/Hypnotic_gaze/add() + // Define quirk mob + var/mob/living/carbon/human/quirk_mob = quirk_holder + + // Add quirk ability action datum + var/datum/action/cooldown/hypnotize/act_hypno = new + act_hypno.Grant(quirk_mob) + + // Add examine text + RegisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE, .proc/quirk_examine_Hypnotic_gaze) + +/datum/quirk/Hypnotic_gaze/remove() + // Define quirk mob + var/mob/living/carbon/human/quirk_mob = quirk_holder + + // Remove quirk ability action datum + var/datum/action/cooldown/hypnotize/act_hypno = locate() in quirk_mob.actions + act_hypno.Remove(quirk_mob) + + // Remove examine text + UnregisterSignal(quirk_holder, COMSIG_PARENT_EXAMINE) + +// Quirk examine text +/datum/quirk/Hypnotic_gaze/proc/quirk_examine_Hypnotic_gaze(atom/examine_target, mob/living/carbon/human/examiner, list/examine_list) + examine_list += "[quirk_holder.p_their(TRUE)] eyes glimmer with an entrancing power..." /datum/quirk/overweight name = "Overweight" diff --git a/modular_splurt/code/datums/traits/trait_actions.dm b/modular_splurt/code/datums/traits/trait_actions.dm index 5e553ae64d..8ec86ae7c4 100644 --- a/modular_splurt/code/datums/traits/trait_actions.dm +++ b/modular_splurt/code/datums/traits/trait_actions.dm @@ -2,19 +2,142 @@ #define BLOODFLEDGE_COOLDOWN_BITE 60 // Six seconds #define BLOODFLEDGE_COOLDOWN_REVIVE 3000 // Five minutes #define BLOODFLEDGE_BANK_CAPACITY (BLOODFLEDGE_DRAIN_NUM * 2) +#define HYPNOEYES_COOLDOWN_NORMAL 3 SECONDS +#define HYPNOEYES_COOLDOWN_BRAINWASH 30 SECONDS // // Quirk: Hypnotic Gaze // -/datum/action/innate/Hypnotize +/datum/action/cooldown/hypnotize name = "Hypnotize" desc = "Stare deeply into someone's eyes, drawing them into a hypnotic slumber." button_icon_state = "Hypno_eye" icon_icon = 'modular_splurt/icons/mob/actions/lewd_actions/lewd_icons.dmi' background_icon_state = "bg_alien" + transparent_when_unavailable = TRUE + cooldown_time = HYPNOEYES_COOLDOWN_NORMAL + + // Should this create a brainwashed victim? + // Enabled by using Mesmer Eyes with the quirk + var/mode_brainwash = FALSE + + // Terminology used + var/term_hypno = "hypnotize" + var/term_suggest = "suggestion" + +/datum/action/cooldown/hypnotize/IsAvailable(silent) + . = ..() + + // Check parent return + if(!.) + return FALSE + + // Check for carbon owner + if(!iscarbon(owner)) + // Warn user and return + to_chat(owner, span_warning("You shouldn't have this ability!")) + return FALSE + + // Define action owner + var/mob/living/carbon/human/action_owner = owner + + // Check if owner has eye protection + if(action_owner.get_eye_protection()) + // Warn the user, then return + to_chat(action_owner, span_warning("Your eyes need to be visible for this ability to work.")) + return FALSE + + // Define owner's eyes + var/obj/item/organ/eyes/owner_eyes = owner.getorganslot(ORGAN_SLOT_EYES) + + // Check if eyes exist + if(!istype(owner_eyes)) + // Warn the user, then return + to_chat(action_owner, span_warning("You need eyes to use this ability!")) + return FALSE + + // Check if owner is blind + if(HAS_TRAIT(action_owner, TRAIT_BLIND)) + // Warn the user, then return + to_chat(action_owner, span_warning("Your blind [owner_eyes] are of no use.")) + return FALSE + + // All checks passed + return TRUE + +/datum/action/cooldown/hypnotize/proc/set_brainwash(set_to = FALSE) + // Check if state will change + if(mode_brainwash == set_to) + // Do nothing + return + + // Set new variable + mode_brainwash = set_to + + // Define toggle message + var/toggle_message = "experiences an error?" + + // Define log message type + var/log_message_type = "somehow screwed up" + + // Check if brainwashing + if(mode_brainwash) + // Set ability text + name = "Brainwash" + desc = "Stare deeply into someone's eyes, and force them to become your loyal slave." + + // Set cooldown time + cooldown_time = HYPNOEYES_COOLDOWN_BRAINWASH + + // Set terminology + term_hypno = "brainwash" + term_suggest = "command" + + // Set message suffix + toggle_message = "suddenly feels more intense!" + + // Set log message type + log_message_type = "GAINED" + + // Not brainwashing + else + // Set ability text + name = "Hypnotize" + desc = "Stare deeply into someone's eyes, drawing them into a hypnotic slumber." + + // Set cooldown time + cooldown_time = HYPNOEYES_COOLDOWN_NORMAL + + // Set terminology + term_hypno = "hypnotize" + term_suggest = "suggestion" + + // Set message suffix + toggle_message = "fades back to normal levels..." + + // Set log message type + log_message_type = "LOST" + + // Update buttons + owner.update_action_buttons() + + // Reset cooldown time + StartCooldown() + + // Alert user + to_chat(owner, span_mind_control("Your hypnotic power [toggle_message] You'll need time to adjust before using it again.")) + + // Log interaction + log_admin("[key_name(owner)] [log_message_type] hypnotic brainwashing powers.") + +/datum/action/cooldown/hypnotize/Trigger() + . = ..() + + // Check parent return + if(!.) + return -/datum/action/innate/Hypnotize/Activate() // Define action owner var/mob/living/carbon/human/action_owner = owner @@ -30,7 +153,7 @@ // Check for cyborg if(iscyborg(grab_target)) // Warn the user, then return - to_chat(action_owner, span_warning("You can't hypnotize a cyborg!")) + to_chat(action_owner, span_warning("You can't [term_hypno] a cyborg!")) return // Check for alien @@ -51,13 +174,13 @@ // Check if target is alive if(!isliving(grab_target)) // Warn the user, then return - to_chat(action_owner, span_warning("You can't hypnotize the dead!")) + to_chat(action_owner, span_warning("You can't [term_hypno] the dead!")) return // Check for aggressive grab if(action_owner.grab_state < GRAB_AGGRESSIVE) // Warn the user, then return - to_chat(action_owner, span_warning("You need a stronger grip before trying this!")) + to_chat(action_owner, span_warning("You need a stronger grip before trying to [term_hypno] [grab_target]!")) return // Define target @@ -93,7 +216,7 @@ // Check if target has eye protection if(action_target.get_eye_protection()) // Warn the user, then return - to_chat(action_owner, span_warning("You have difficulty focusing on [action_target]'s eyes due to some form of protection, and are left unable to hypnotize them.")) + to_chat(action_owner, span_warning("You have difficulty focusing on [action_target]'s eyes due to some form of protection, and are left unable to [term_hypno] them.")) to_chat(action_target, span_notice("[action_owner] stares intensely at you, but stops after a moment.")) return @@ -136,7 +259,7 @@ // Check for sleep if(action_target.IsSleeping()) // Warn the user, then return - to_chat(action_owner, span_warning("You can't hypnotize [action_target] whilst [action_target.p_theyre()] asleep!")) + to_chat(action_owner, span_warning("You can't [term_hypno] [action_target] whilst [action_target.p_theyre()] asleep!")) return // Check for combat mode @@ -154,7 +277,7 @@ if(!do_mob(action_owner, action_target, 5 SECONDS)) // Action timer was interrupted // Warn the user, then return - to_chat(action_owner, span_warning("You lose concentration on [action_target], and fail to hypnotize [action_target.p_them()]!")) + to_chat(action_owner, span_warning("You lose concentration on [action_target], and fail to [term_hypno] [action_target.p_them()]!")) to_chat(action_target, span_notice("[action_owner]'s gaze is broken prematurely, freeing you from any potential effects.")) return @@ -164,32 +287,38 @@ // Check for non-consensual setting if(action_target.client?.prefs.nonconpref != "Yes") // Non-consensual is NOT enabled + // Define warning suffix + var/warning_target = (mode_brainwash ? "You will become a brainwashed victim, and be required to follow all orders given. [action_owner] accepts all responsibility for antagonistic orders." : "These are only suggestions, and you may disobey cases that strongly violate your character.") + // Prompt target for consent response - input_consent = alert(action_target, "Will you fall into a hypnotic stupor? This will allow [action_owner] to issue hypnotic suggestions.", "Hypnosis", "Yes", "No") + input_consent = alert(action_target, "Will you fall into a hypnotic stupor? This will allow [action_owner] to issue hypnotic [term_suggest]s. [warning_target]", "Hypnosis", "Yes", "No") // When consent is denied if(input_consent == "No") // Warn the users, then return - to_chat(action_owner, span_warning("[action_target]'s attention breaks, despite the attempt to hypnotize [action_target.p_them()]! [action_target.p_they()] clearly don't want this!")) + to_chat(action_owner, span_warning("[action_target]'s attention breaks, despite the attempt to [term_hypno] [action_target.p_them()]! [action_target.p_they()] clearly don't want this!")) to_chat(action_target, span_notice("Your concentration breaks as you realize you have no interest in following [action_owner]'s words!")) return // Display local message - action_target.visible_message(span_warning("[action_target] falls into a deep slumber!"), span_danger("Your eyelids gently shut as you fall into a deep slumber. All you can hear is [action_owner]'s voice as you commit to following all of their suggestions.")) + action_target.visible_message(span_warning("[action_target] falls into a deep slumber!"), span_danger("Your eyelids gently shut as you fall into a deep slumber. All you can hear is [action_owner]'s voice as you commit to following all of their [term_suggest]s.")) // Set sleeping - action_target.SetSleeping(1200) + action_target.SetSleeping(2 MINUTES) // Set drowsiness action_target.drowsyness = max(action_target.drowsyness, 40) + // Define warning suffix + var/warning_owner = (mode_brainwash ? "You are responsible for any antagonistic actions they take as a result of the brainwashing." : "This is only a suggestion, and [action_target.p_they()] may disobey if it violates [action_target.p_their()] character.") + // Prompt action owner for response - var/input_suggestion = input("What would you like to suggest [action_target] do? Leave blank to release [action_target.p_them()] instead.", "Hypnotic suggestion", null, null) + var/input_suggestion = input("What would you like to suggest [action_target] do? Leave blank to release [action_target.p_them()] instead. [warning_owner]", "Hypnotic [term_suggest]", null, null) // Check if input text exists if(!input_suggestion) // Alert user of no input - to_chat(action_owner, "You decide not to give [action_target] a suggestion.") + to_chat(action_owner, "You decide not to give [action_target] a [term_suggest].") // Remove sleep, then return action_target.SetSleeping(0) @@ -198,9 +327,21 @@ // Sanitize input text input_suggestion = sanitize(input_suggestion) - // Display message to users - to_chat(action_owner, "You whisper your suggestion in a smooth calming voice to [action_target]") - to_chat(action_target, span_hypnophrase("...[input_suggestion]...")) + // Check if brainwash mode + if(mode_brainwash) + // Create brainwash objective + brainwash(action_target, input_suggestion) + + // Not in brainwash mode + else + // Display message to target + to_chat(action_target, span_mind_control("...[input_suggestion]...")) + + // Start cooldown + StartCooldown() + + // Display message to action owner + to_chat(action_owner, "You whisper your [term_suggest] in a smooth calming voice to [action_target]") // Play a sound effect playsound(action_target, 'sound/magic/domain.ogg', 20, 1) @@ -1402,3 +1543,6 @@ // Update outline effect action_mob.remove_filter("rad_fiend_glow") action_mob.add_filter("rad_fiend_glow", 1, list("type" = "outline", "color" = glow_color+"30", "size" = glow_range)) + +#undef HYPNOEYES_COOLDOWN_NORMAL +#undef HYPNOEYES_COOLDOWN_BRAINWASH diff --git a/modular_splurt/code/modules/antagonists/brainwashing/brainwashing.dm b/modular_splurt/code/modules/antagonists/brainwashing/brainwashing.dm new file mode 100644 index 0000000000..bdcbed912c --- /dev/null +++ b/modular_splurt/code/modules/antagonists/brainwashing/brainwashing.dm @@ -0,0 +1,12 @@ +/datum/antagonist/brainwashed + // Do not add to living antag list + soft_antag = TRUE + + // Do not add to round end report + show_in_roundend = FALSE + + // Do not show to ghosts + show_to_ghosts = FALSE + + // Give minor mood event + antag_moodlet = /datum/mood_event/brainwashed diff --git a/modular_splurt/code/modules/cargo/packs/costumes_toys.dm b/modular_splurt/code/modules/cargo/packs/costumes_toys.dm index 904d19048e..2290fe603f 100644 --- a/modular_splurt/code/modules/cargo/packs/costumes_toys.dm +++ b/modular_splurt/code/modules/cargo/packs/costumes_toys.dm @@ -5,3 +5,11 @@ LAZYADD(contains, extra_contains) . = ..() +/datum/supply_pack/costumes_toys/wedding/New() + . = ..() + var/list/extra_contains = list( + /obj/item/bouquet, + /obj/item/bouquet/sunflower, + /obj/item/bouquet/poppy + ) + LAZYADD(contains, extra_contains) diff --git a/modular_splurt/code/modules/cargo/packs/misc.dm b/modular_splurt/code/modules/cargo/packs/misc.dm index bf5eec556d..98bdc574ac 100644 --- a/modular_splurt/code/modules/cargo/packs/misc.dm +++ b/modular_splurt/code/modules/cargo/packs/misc.dm @@ -250,18 +250,19 @@ /obj/item/wrench) crate_name = "stripper pole crate" + /datum/supply_pack/misc/wedding - name = "Wedding Crate" + name = "Small Wedding Crate" desc = "Almost everything you need to host a wedding! Don't forget a ring!" cost = 1500 - contains = list(/obj/item/clothing/under/wedding_dress, - /obj/item/clothing/under/tuxedo, + contains = list(/obj/item/clothing/under/dress/wedding, + /obj/item/clothing/under/suit/tuxedo, /obj/item/storage/belt/cummerbund, /obj/item/bouquet, /obj/item/bouquet/sunflower, /obj/item/bouquet/poppy, /obj/item/reagent_containers/food/drinks/bottle/champagne) - crate_name = "wedding crate" + crate_name = "small wedding crate" /datum/supply_pack/misc/random1k name = "1k Surplus Crate" diff --git a/modular_splurt/code/modules/client/loadout/uniform.dm b/modular_splurt/code/modules/client/loadout/uniform.dm index 12c14d564d..fedfbf61bc 100644 --- a/modular_splurt/code/modules/client/loadout/uniform.dm +++ b/modular_splurt/code/modules/client/loadout/uniform.dm @@ -170,3 +170,71 @@ path = /obj/item/clothing/under/rank/cargo/miner/lavaland/stripper subcategory = LOADOUT_SUBCATEGORY_UNIFORM_JOBS restricted_roles = list("Shaft Miner") + +/datum/gear/uniform/suit/tuxedo + name = "Tuxedo suit" + path = /obj/item/clothing/under/suit/tuxedo + +/datum/gear/uniform/suit/tuxedo/carp + name = "Carpskin suit" + path = /obj/item/clothing/under/suit/carpskin + +/datum/gear/uniform/suit/pencil + name = "Black Pencilskirt" + path = /obj/item/clothing/under/suit/pencil + +/datum/gear/uniform/suit/pencil/black_really + name = "Executive Pencilskirt" + path = /obj/item/clothing/under/suit/pencil/black_really + +/datum/gear/uniform/suit/pencil/charcoal + name = "Charcoal Pencilskirt" + path = /obj/item/clothing/under/suit/pencil/charcoal + +/datum/gear/uniform/suit/pencil/navy + name = "Navy Pencilskirt" + path = /obj/item/clothing/under/suit/pencil/navy + +/datum/gear/uniform/suit/pencil/burgandy + name = "Burgandy Pencilskirt" + path = /obj/item/clothing/under/suit/pencil/burgandy + +/datum/gear/uniform/suit/pencil/checkered + name = "Checkered Pencilskirt" + path = /obj/item/clothing/under/suit/pencil/checkered + +/datum/gear/uniform/suit/pencil/tan + name = "Tan Pencilskirt" + path = /obj/item/clothing/under/suit/pencil/tan + +/datum/gear/uniform/suit/pencil/green + name = "Green Pencilskirt" + path = /obj/item/clothing/under/suit/pencil/green + +/datum/gear/uniform/suit/executive_suit_alt + name = "Wide-collared Executive Suit" + path = /obj/item/clothing/under/suit/black_really_collared + +/datum/gear/uniform/suit/executive_skirt_alt + name = "Wide-collared Executive Suitskirt" + path = /obj/item/clothing/under/suit/black_really_collared/skirt + +/datum/gear/uniform/suit/inferno + name = "Inferno Suit" + path = /obj/item/clothing/under/suit/inferno + +/datum/gear/uniform/suit/inferno_skirt + name = "Inferno Skirt" + path = /obj/item/clothing/under/suit/inferno/skirt + +/datum/gear/uniform/suit/designer_inferno + name = "Designer Inferno Suit" + path = /obj/item/clothing/under/suit/inferno/beeze + +/datum/gear/uniform/suit/helltaker + name = "Red Shirt with White Trousers" + path = /obj/item/clothing/under/suit/helltaker + +/datum/gear/uniform/suit/helltaker/skirt + name = "Red Shirt with White Skirt" + path = /obj/item/clothing/under/suit/helltaker/skirt diff --git a/modular_splurt/code/modules/clothing/suits/miscellaneous.dm b/modular_splurt/code/modules/clothing/suits/miscellaneous.dm index 7a14c9ff14..8307e30221 100644 --- a/modular_splurt/code/modules/clothing/suits/miscellaneous.dm +++ b/modular_splurt/code/modules/clothing/suits/miscellaneous.dm @@ -6,6 +6,8 @@ mutantrace_variation = STYLE_DIGITIGRADE|STYLE_NO_ANTHRO_ICON //Own stuff + +/* Already exists /obj/item/clothing/under/wedding_dress name = "wedding dress" desc = "A luxurious gown for once-in-a-lifetime occasions." @@ -15,14 +17,7 @@ flags_cover = HIDESHOES mutantrace_variation = NONE can_adjust = FALSE - -/obj/item/clothing/under/tuxedo - name = "tuxedo" - desc = "A formal black tuxedo. It exudes classiness." - icon = 'modular_splurt/icons/obj/clothing/uniforms.dmi' - icon_state = "tuxedo" - mutantrace_variation = NONE //temporary - can_adjust = FALSE +*/ /obj/item/clothing/suit/hooded/wintercoat/security/pink name = "pink security winter coat" diff --git a/modular_splurt/code/modules/clothing/under/jobs/civilian/suits.dm b/modular_splurt/code/modules/clothing/under/jobs/civilian/suits.dm new file mode 100644 index 0000000000..8c2668d802 --- /dev/null +++ b/modular_splurt/code/modules/clothing/under/jobs/civilian/suits.dm @@ -0,0 +1,138 @@ +/obj/item/clothing/under/suit/tuxedo + name = "tuxedo" + desc = "A formal black tuxedo. It exudes classiness." + icon = 'modular_splurt/icons/obj/clothing/under/suits.dmi' + mob_overlay_icon = 'modular_splurt/icons/mob/clothing/under/suits.dmi' + anthro_mob_worn_overlay = 'modular_splurt/icons/mob/clothing/under/suits_digi.dmi' + icon_state = "tuxedo" + can_adjust = FALSE + +/obj/item/clothing/under/suit/carpskin + name = "carpskin suit" + desc = "An luxurious suit made with only the finest scales, perfect for conducting dodgy business deals." + icon = 'modular_splurt/icons/obj/clothing/under/suits.dmi' + mob_overlay_icon = 'modular_splurt/icons/mob/clothing/under/suits.dmi' + anthro_mob_worn_overlay = 'modular_splurt/icons/mob/clothing/under/suits_digi.dmi' + icon_state = "carpskin_suit" + +/obj/item/clothing/under/suit/pencil + name = "black pencilskirt" + desc = "A clean white shirt with a tight-fitting black pencilskirt." + icon_state = "black_pencil" + icon = 'modular_splurt/icons/obj/clothing/under/suits.dmi' + mob_overlay_icon = 'modular_splurt/icons/mob/clothing/under/suits.dmi' + anthro_mob_worn_overlay = 'modular_splurt/icons/mob/clothing/under/suits_digi.dmi' + +/obj/item/clothing/under/suit/pencil/black_really + name = "executive pencilskirt" + desc = "A sleek suit with a tight-fitting black pencilskirt." + icon_state = "really_black_pencil" + +/obj/item/clothing/under/suit/pencil/charcoal + name = "charcoal pencilskirt" + desc = "A clean white shirt with a tight-fitting charcoal pencilskirt." + icon_state = "charcoal_pencil" + +/obj/item/clothing/under/suit/pencil/navy + name = "navy pencilskirt" + desc = "A clean white shirt with a tight-fitting navy-blue pencilskirt." + icon_state = "navy_pencil" + +/obj/item/clothing/under/suit/pencil/burgandy + name = "burgandy pencilskirt" + desc = "A clean white shirt with a tight-fitting burgandy-red pencilskirt." + icon_state = "burgandy_pencil" + +/obj/item/clothing/under/suit/pencil/checkered + name = "checkered pencilskirt" + desc = "A clean white shirt with a tight-fitting grey checkered pencilskirt." + icon_state = "checkered_pencil" + +/obj/item/clothing/under/suit/pencil/tan + name = "tan pencilskirt" + desc = "A clean white shirt with a tight-fitting tan pencilskirt." + icon_state = "tan_pencil" + +/obj/item/clothing/under/suit/pencil/green + name = "green pencilskirt" + desc = "A clean white shirt with a tight-fitting green pencilskirt." + icon_state = "green_pencil" + +/obj/item/clothing/under/suit/scarface + name = "cuban suit" + desc = "A yayo coloured silk suit with a crimson shirt. You just know how to hide, how to lie. Me, I don't have that problem. Me, I always tell the truth. Even when I lie." + icon_state = "scarface" + icon = 'modular_splurt/icons/obj/clothing/under/suits.dmi' + mob_overlay_icon = 'modular_splurt/icons/mob/clothing/under/suits.dmi' + anthro_mob_worn_overlay = 'modular_splurt/icons/mob/clothing/under/suits_digi.dmi' + +/obj/item/clothing/under/suit/black_really_collared + name = "wide-collared executive suit" + desc = "A formal black suit with the collar worn wide, intended for the station's finest." + icon_state = "really_black_suit_collar" + icon = 'modular_splurt/icons/obj/clothing/under/suits.dmi' + mob_overlay_icon = 'modular_splurt/icons/mob/clothing/under/suits.dmi' + anthro_mob_worn_overlay = 'modular_splurt/icons/mob/clothing/under/suits_digi.dmi' + +/obj/item/clothing/under/suit/black_really_collared/skirt + name = "wide-collared executive suitskirt" + desc = "A formal black suit with the collar worn wide, intended for the station's finest." + icon_state = "really_black_suit_skirt_collar" + icon = 'modular_splurt/icons/obj/clothing/under/suits.dmi' + mob_overlay_icon = 'modular_splurt/icons/mob/clothing/under/suits.dmi' + anthro_mob_worn_overlay = 'modular_splurt/icons/mob/clothing/under/suits_digi.dmi' + body_parts_covered = CHEST|GROIN|ARMS + mutantrace_variation = STYLE_DIGITIGRADE|STYLE_NO_ANTHRO_ICON + +/obj/item/clothing/under/suit/inferno + name = "inferno suit" + desc = "Stylish enough to impress the devil." + obj_flags = UNIQUE_RENAME + icon_state = "lucifer" + icon = 'modular_splurt/icons/obj/clothing/under/suits.dmi' + mob_overlay_icon = 'modular_splurt/icons/mob/clothing/under/suits.dmi' + anthro_mob_worn_overlay = 'modular_splurt/icons/mob/clothing/under/suits_digi.dmi' + unique_reskin = list( + "Pride" = list("icon_state" = "lucifer"), + "Wrath" = list("icon_state" = "justice"), + "Gluttony" = list("icon_state" = "malina"), + "Envy" = list("icon_state" = "zdara"), + "Vanity" = list("icon_state" = "cereberus"), + ) + +/obj/item/clothing/under/suit/inferno/skirt + name = "inferno suitskirt" + icon_state = "modeus" + unique_reskin = list( + "Lust" = list("icon_state" = "modeus"), + "Sloth" = list("icon_state" = "pande"), + ) + +/obj/item/clothing/under/suit/inferno/beeze + name = "designer inferno suit" + desc = "A fancy tail-coated suit with a fluffy bow emblazoned on the chest, complete with an NT pin." + icon_state = "beeze" + obj_flags = NONE + unique_reskin = null + +/obj/item/clothing/under/suit/helltaker + name = "red shirt with white pants" + desc = "No time. Busy gathering girls." + icon_state = "helltaker" + icon = 'modular_splurt/icons/obj/clothing/under/suits.dmi' + mob_overlay_icon = 'modular_splurt/icons/mob/clothing/under/suits.dmi' + anthro_mob_worn_overlay = 'modular_splurt/icons/mob/clothing/under/suits_digi.dmi' + +/obj/item/clothing/under/suit/helltaker/skirt + name = "red shirt with white skirt" + desc = "No time. Busy gathering boys." + icon_state = "helltakerskirt" + +/obj/item/clothing/under/suit/error + name = "error suit" + desc = "Oops! Seems whoever made this suit forgot to download the base cloth for it!" + icon = 'modular_splurt/icons/obj/clothing/under/suits.dmi' + mob_overlay_icon = 'modular_splurt/icons/mob/clothing/under/suits.dmi' + anthro_mob_worn_overlay = 'modular_splurt/icons/mob/clothing/under/suits_digi.dmi' + icon_state = "error_suit" + diff --git a/modular_splurt/code/modules/jobs/job_types/_job_alt_titles.dm b/modular_splurt/code/modules/jobs/job_types/_job_alt_titles.dm index 5e292fe993..b988f3ba16 100644 --- a/modular_splurt/code/modules/jobs/job_types/_job_alt_titles.dm +++ b/modular_splurt/code/modules/jobs/job_types/_job_alt_titles.dm @@ -128,7 +128,11 @@ . = ..() /datum/job/chaplain/New() // Yell at upstream maintainer(s) to fix "Bichop" title. + var/list/rem_titles = list( + "Bichop" + ) var/list/extra_titles = list( + "Bishop", "Priestess", "Prior", "Monk", @@ -136,6 +140,7 @@ "Counselor" ) LAZYADD(alt_titles, extra_titles) + LAZYREMOVE(alt_titles, rem_titles) . = ..() /datum/job/clown // Sorry, but no TWO entertainer titles. @@ -301,9 +306,13 @@ "Police Officer", "Slutcurity Officer" ) + var/list/rem_titles = list( + "Peacekeeper" + ) if(SSevents.holidays && SSevents.holidays[HALLOWEEN]) LAZYADD(extra_titles, "Spookcurity Officer") LAZYADD(alt_titles, extra_titles) + LAZYREMOVE(alt_titles, rem_titles) . = ..() /datum/job/warden/New() diff --git a/modular_splurt/code/modules/jobs/job_types/blueshield.dm b/modular_splurt/code/modules/jobs/job_types/blueshield.dm index 9f0467fcee..a6de268cb3 100644 --- a/modular_splurt/code/modules/jobs/job_types/blueshield.dm +++ b/modular_splurt/code/modules/jobs/job_types/blueshield.dm @@ -15,8 +15,8 @@ considered_combat_role = TRUE //Brigger then shit yes it is exp_type_department = EXP_TYPE_COMMAND alt_titles = list("Command Security", "Command Guard", "Command Bodyguard", "Blueguard", "Blueshit") - custom_spawn_text = " YOU ARE NOT PART OF COMMAND. YOU WILL NEVER BE PART OF COMMAND. YOU ARE NOT PART OF CENTRAL COMMAND. IF YOU ACT LIKE YOU ARE, YOU WILL NEVER PLAY BLUESHIELD EVER AGAIN." - + //SPLURT CHANGES (Tells the blueshield they are command, and just a bodyguard, not security) + custom_spawn_text = "You are a member of command, meant to act as a bodyguard for all Heads of Staff. However, you are not a member of security nor a Head of Staff yourself - avoid acting as either." outfit = /datum/outfit/job/blueshield plasma_outfit = /datum/outfit/plasmaman/blueshield diff --git a/modular_splurt/code/modules/jobs/job_types/bridgeofficer.dm b/modular_splurt/code/modules/jobs/job_types/bridgeofficer.dm index a148a6a9d2..5132b7fa60 100644 --- a/modular_splurt/code/modules/jobs/job_types/bridgeofficer.dm +++ b/modular_splurt/code/modules/jobs/job_types/bridgeofficer.dm @@ -16,8 +16,9 @@ considered_combat_role = FALSE outfit = /datum/outfit/job/bridgeofficer plasma_outfit = /datum/outfit/plasmaman/bridgeofficer - custom_spawn_text = "Bridge Officer: You are here to assist the bridge staff with whatever they need. You have very limited access. You are not Secuirty and you are not in the line of secceustion. You have no power, listen to the Captain and the HOP." - access = list( ACCESS_HEADS, ACCESS_MAINT_TUNNELS, ACCESS_BRIDGE_OFFICER, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH) + //SPLURT CHANGES (Changes the custom spawn text for the Bridge Officer) + custom_spawn_text = "You are an assistant who's primary focus is serving the Heads of Staff. Nothing more. Nothing less." + access = list(ACCESS_HEADS, ACCESS_MAINT_TUNNELS, ACCESS_BRIDGE_OFFICER, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH) minimal_access = list(ACCESS_HEADS, ACCESS_MAINT_TUNNELS, ACCESS_BRIDGE_OFFICER, ACCESS_RC_ANNOUNCE, ACCESS_KEYCARD_AUTH) paycheck = PAYCHECK_MEDIUM paycheck_department = ACCOUNT_CIV diff --git a/modular_splurt/code/modules/mob/dead/new_player/sprite_accesories/hair_head.dm b/modular_splurt/code/modules/mob/dead/new_player/sprite_accesories/hair_head.dm index a25ebdbf27..64a26a6134 100644 --- a/modular_splurt/code/modules/mob/dead/new_player/sprite_accesories/hair_head.dm +++ b/modular_splurt/code/modules/mob/dead/new_player/sprite_accesories/hair_head.dm @@ -160,3 +160,8 @@ name = "Deathhawk" icon = 'modular_splurt/icons/mob/hair.dmi' icon_state = "hair_deathhawk" + +/datum/sprite_accessory/hair/fluffball + name = "Fluffball" + icon = 'modular_splurt/icons/mob/hair.dmi' + icon_state = "hair_fluffball" diff --git a/modular_splurt/code/modules/mob/dead/new_player/sprite_accesories/tails.dm b/modular_splurt/code/modules/mob/dead/new_player/sprite_accesories/tails.dm index 7f9eff453e..fe76b9267c 100644 --- a/modular_splurt/code/modules/mob/dead/new_player/sprite_accesories/tails.dm +++ b/modular_splurt/code/modules/mob/dead/new_player/sprite_accesories/tails.dm @@ -194,7 +194,7 @@ matrixed_sections = MATRIX_RED_GREEN /datum/sprite_accessory/tails_animated/mam_tails_animated/easterndragon - name = "Eastern Dragon" + name = "Dragon Eastern" icon_state = "easternd" icon = 'modular_splurt/icons/mob/mam_tails.dmi' matrixed_sections = MATRIX_RED_GREEN @@ -407,6 +407,12 @@ icon_state = "succubus" color_src = MUTCOLORS +/datum/sprite_accessory/tails_animated/human/succubus + name = "Succubus tail" + icon = 'modular_splurt/icons/mob/mam_tails.dmi' + icon_state = "succubus" + color_src = MUTCOLORS + /datum/sprite_accessory/tails/mam_tails/kangaroo/alt name = "kangaroo (alt)" icon = 'modular_splurt/icons/mob/64_mam_tails.dmi' diff --git a/modular_splurt/code/modules/mob/living/emotes.dm b/modular_splurt/code/modules/mob/living/emotes.dm index 300a7da222..ec5f1a1d88 100644 --- a/modular_splurt/code/modules/mob/living/emotes.dm +++ b/modular_splurt/code/modules/mob/living/emotes.dm @@ -631,6 +631,14 @@ emote_sound = 'modular_splurt/sound/voice/choir.ogg' emote_cooldown = 6 SECONDS +/datum/emote/living/audio/agony + key = "agony" + key_third_person = "agonys" + message = "let out a choir of agony!" + message_mime = "is visibly in agony." + emote_sound = 'modular_splurt/sound/voice/agony.ogg' + emote_cooldown = 7 SECONDS + /datum/emote/living/audio/sicko key = "sicko" key_third_person = "sickos" diff --git a/modular_splurt/code/modules/mob/mob.dm b/modular_splurt/code/modules/mob/mob.dm index 3d5b2fea19..cba1b8c2d6 100644 --- a/modular_splurt/code/modules/mob/mob.dm +++ b/modular_splurt/code/modules/mob/mob.dm @@ -1,3 +1,5 @@ +#define PROTOLOCK_ALL_ACCESS CONFIG_GET(flag/protolock_all_access) + /mob/Initialize() . = ..() create_player_panel() @@ -79,3 +81,9 @@ // All checks passed return TRUE + +//Makes the protolocks able to be disabled +/mob/can_use_production(obj/machinery/machine_target) + if(PROTOLOCK_ALL_ACCESS) + return TRUE + . = ..() diff --git a/modular_splurt/code/modules/research/designs/medical_designs.dm b/modular_splurt/code/modules/research/designs/medical_designs.dm index e5228d97ec..99343587b5 100644 --- a/modular_splurt/code/modules/research/designs/medical_designs.dm +++ b/modular_splurt/code/modules/research/designs/medical_designs.dm @@ -74,6 +74,13 @@ /datum/design/cyberimp_thermals category = list("Cybernetics", "Medical Designs") +// Derivative of glow eyes +/datum/design/cyberimp_gloweyes/cyberimp_hypnoeyes + name = "Mesmer Eyes" + desc = "Cybernetic eyes with integrated memetic sub-systems." + id = "ci-hypnoeyes" + build_path = /obj/item/organ/eyes/robotic/hypno + //Cybernetic implants /datum/design/cyberimp_breather diff --git a/modular_splurt/code/modules/research/techweb/nodes/medical_nodes.dm b/modular_splurt/code/modules/research/techweb/nodes/medical_nodes.dm index eeb846271f..9b049b908f 100644 --- a/modular_splurt/code/modules/research/techweb/nodes/medical_nodes.dm +++ b/modular_splurt/code/modules/research/techweb/nodes/medical_nodes.dm @@ -36,3 +36,11 @@ LAZYREMOVE(design_ids, removed_designs) LAZYADD(design_ids, added_designs) . = ..() + +/datum/techweb_node/alien_cyber_organs + id = "alien_cyber_organs" + display_name = "Alien Cybernetic Organs" + description = "Morally dubious experimental parts." + prereq_ids = list("cyber_organs", "alien_surgery") + design_ids = list("ci-hypnoeyes") + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 1000) diff --git a/modular_splurt/code/modules/surgery/organs/eyes.dm b/modular_splurt/code/modules/surgery/organs/eyes.dm index 6959e01a60..cab6b30fba 100644 --- a/modular_splurt/code/modules/surgery/organs/eyes.dm +++ b/modular_splurt/code/modules/surgery/organs/eyes.dm @@ -6,3 +6,72 @@ /obj/item/organ/eyes/ipc name = "IPC eyes" + +/obj/item/organ/eyes/robotic/hypno + name = "mesmer eyes" + desc = "Cybernetic eyes with integrated memetic sub-systems. They're full of swirls and bright colors!" + +// On add organ +/obj/item/organ/eyes/robotic/hypno/Insert(mob/living/carbon/eye_user, special, drop_if_replaced) + . = ..() + + // Check parent return + if(!.) + return + + // Define ability + var/datum/action/cooldown/hypnotize/eye_ability + + // Check for pre-existing hypnotic quirk + if(eye_user.has_quirk(/datum/quirk/Hypnotic_gaze)) + // Locate existing action + eye_ability = locate() in eye_user.actions + + // Enable brainwash mode, then return + eye_ability.set_brainwash(TRUE) + return + + // Create new hypnotic ability + eye_ability = new + + // Grant action to user + eye_ability.Grant(eye_user) + + // Add trait + ADD_TRAIT(eye_user, TRAIT_HYPNOTIC_GAZE, ORGAN_TRAIT) + + // Alert user + to_chat(eye_user, span_nicegreen("Your [src] begin to glimmer with an entrancing power!")) + + // Add examine text + RegisterSignal(eye_user, COMSIG_PARENT_EXAMINE, .proc/examine_user) + +// On remove organ +/obj/item/organ/eyes/robotic/hypno/Remove(mob/living/carbon/eye_user, special, drop_if_replaced) + . = ..() + + // Define ability + var/datum/action/cooldown/hypnotize/eye_ability = locate() in eye_user.actions + + // Check for pre-existing hypnotic quirk + if(eye_user.has_quirk(/datum/quirk/Hypnotic_gaze)) + // Disable brainwash mode, then return + eye_ability.set_brainwash(FALSE) + return + + // Remove ability + eye_ability.Remove(eye_user) + + // Remove trait + REMOVE_TRAIT(eye_user, TRAIT_HYPNOTIC_GAZE, ORGAN_TRAIT) + + // Alert user + to_chat(eye_user, span_warning("You power of suggestion fades.")) + + // Remove examine text + UnregisterSignal(eye_user, COMSIG_PARENT_EXAMINE) + +// Organ examine text +/obj/item/organ/eyes/robotic/hypno/proc/examine_user(atom/examine_target, mob/living/carbon/human/examiner, list/examine_list) + // Add examine text + examine_list += "[usr.p_their(TRUE)] eyes glimmer with an entrancing power." diff --git a/modular_splurt/code/modules/tgs/chat_commands.dm b/modular_splurt/code/modules/tgs/chat_commands.dm index f6c994c09b..60e864b19a 100644 --- a/modular_splurt/code/modules/tgs/chat_commands.dm +++ b/modular_splurt/code/modules/tgs/chat_commands.dm @@ -6,3 +6,64 @@ /datum/tgs_chat_command/tidi/Run(datum/tgs_chat_user/sender, params) return "Time Dilation: [round(SStime_track.time_dilation_current,1)]% AVG:([round(SStime_track.time_dilation_avg_fast,1)]%, [round(SStime_track.time_dilation_avg,1)]%, [round(SStime_track.time_dilation_avg_slow,1)]%)" +/datum/tgs_chat_command/splashsauce + name = "splashsauce" + help_text = "Sauce of the current splashscreen" + +/datum/tgs_chat_command/splashsauce/Run(datum/tgs_chat_user/sender, params) + //Check if the sauce system is turned on + if(!CONFIG_GET(flag/sauce_command_enabled)) + return new /datum/tgs_message_content("This command is not enabled!") + + //Get the current splashscreen + var/list/raw_path = splittext(SStitle.file_path, "/") + var/current_splashscreen = raw_path[raw_path.len] + + //Get the info of all splashscreens + var/list/splashinfo = safe_json_decode(rustg_file_read("[global.config.directory]/splurt/title_screens_sources.json")) + + //Find the info of the current splashscreen + if(!splashinfo.Find(current_splashscreen)) + return new /datum/tgs_message_content("The current splashscreen has no info set up yet!") + + //create info and embed of the sauce + var/datum/tgs_message_content/message = new("Source found!") + message.embed = new + + message.embed.author = new(splashinfo[current_splashscreen]["author"]) + message.embed.title = splashinfo[current_splashscreen]["name"] + message.embed.description = splashinfo[current_splashscreen]["link"] + message.embed.timestamp = time2text(world.timeofday, "YYYY-MM-DD hh:mm:ss") + message.embed.image = new(splashinfo[current_splashscreen]["dw_link"]) + message.embed.colour = "#00ff00" + + return message + +/datum/tgs_chat_command/allsplashscreens + name = "allsplashscreens" + help_text = "All info for all splashscreens currently in the server" + +/datum/tgs_chat_command/allsplashscreens/Run(datum/tgs_chat_user/sender, params) + //Check if the sauce system is turned on + if(!CONFIG_GET(flag/sauce_command_enabled)) + return new /datum/tgs_message_content("This command is not enabled!") + + //Get the info of all splashscreens + var/list/splashinfo = safe_json_decode(rustg_file_read("[global.config.directory]/splurt/title_screens_sources.json")) + if(!splashinfo.len) + return new /datum/tgs_message_content("There are no splashscreens set up yet!") + + //Organize the splashscreen info + var/datum/tgs_message_content/message = new("Splashscreens:") + message.embed = new + + message.embed.title = "Currently available splashscreens" + message.embed.description = "These are the splashscreens with info currently available" + message.embed.timestamp = time2text(world.timeofday, "YYYY-MM-DD hh:mm:ss") + message.embed.colour = "#350bce" + + for(var/pic in splashinfo) + var/list/info = splashinfo[pic] + var/datum/tgs_chat_embed/field/field = new("by [info["author"]]", "\[[info["name"]]\]([info["link"]])") + LAZYADD(message.embed.fields, field) + return message diff --git a/modular_splurt/code/modules/vending/autodrobe.dm b/modular_splurt/code/modules/vending/autodrobe.dm index adc0a19b57..065deb7ff3 100644 --- a/modular_splurt/code/modules/vending/autodrobe.dm +++ b/modular_splurt/code/modules/vending/autodrobe.dm @@ -8,8 +8,23 @@ /obj/item/clothing/suit/hooded/pyramidhead = 1, /obj/item/clothing/suit/baroness = 3, /obj/item/clothing/suit/baroness/ladyballat = 3, - /obj/item/clothing/suit/flatwoods = 3 + /obj/item/clothing/suit/flatwoods = 3, + /obj/item/clothing/under/dress/wedding = 1, + /obj/item/clothing/under/suit/pencil = 3, + /obj/item/clothing/under/suit/pencil/black_really = 3, + /obj/item/clothing/under/suit/pencil/charcoal = 3, + /obj/item/clothing/under/suit/pencil/navy = 3, + /obj/item/clothing/under/suit/pencil/burgandy = 3, + /obj/item/clothing/under/suit/pencil/checkered = 3, + /obj/item/clothing/under/suit/pencil/tan = 3, + /obj/item/clothing/under/suit/pencil/green = 3, + /obj/item/clothing/under/suit/error = 1 + ) + var/list/extra_premium = list( + /obj/item/clothing/under/suit/tuxedo = 1, + /obj/item/clothing/under/suit/carpskin = 1 ) LAZYADD(products, extra_products) + LAZYADD(premium, extra_premium) . = ..() diff --git a/modular_splurt/code/modules/vending/clothesmate.dm b/modular_splurt/code/modules/vending/clothesmate.dm index d6dee6e947..7051fb2edf 100644 --- a/modular_splurt/code/modules/vending/clothesmate.dm +++ b/modular_splurt/code/modules/vending/clothesmate.dm @@ -21,7 +21,13 @@ /obj/item/clothing/accessory/longcrop = 3, /obj/item/clothing/accessory/formalcrop = 3, /obj/item/clothing/under/misc/leia_outfit = 2, - /obj/item/clothing/under/performer/polychromic = 2 + /obj/item/clothing/under/performer/polychromic = 2, + /obj/item/clothing/under/suit/black_really_collared = 3, + /obj/item/clothing/under/suit/black_really_collared/skirt = 3, + /obj/item/clothing/under/suit/inferno = 3, + /obj/item/clothing/under/suit/inferno/skirt = 3, + /obj/item/clothing/under/suit/helltaker = 3, + /obj/item/clothing/under/suit/helltaker/skirt = 3 ) var/list/extra_contraband = list( /obj/item/clothing/under/rank/civilian/lawyer/galaxy_red = 3, diff --git a/modular_splurt/code/modules/vending/wardrobes.dm b/modular_splurt/code/modules/vending/wardrobes.dm index 6115c7d19f..378abf4967 100644 --- a/modular_splurt/code/modules/vending/wardrobes.dm +++ b/modular_splurt/code/modules/vending/wardrobes.dm @@ -31,11 +31,30 @@ /obj/machinery/vending/wardrobe/law_wardrobe/Initialize(mapload) var/list/extra_products = list( /obj/item/clothing/under/rank/civilian/lawyer/galaxy_blue = 3, - /obj/item/clothing/under/rank/civilian/lawyer/galaxy_red = 3 + /obj/item/clothing/under/rank/civilian/lawyer/galaxy_red = 3, + /obj/item/clothing/under/suit/black_really_collared = 3, + /obj/item/clothing/under/suit/black_really_collared/skirt = 3, + /obj/item/clothing/under/suit/inferno = 3, + /obj/item/clothing/under/suit/inferno/skirt = 3, + /obj/item/clothing/under/suit/inferno/beeze ) LAZYADD(products, extra_products) . = ..() +/obj/machinery/vending/wardrobe/hydro_wardrobe/Initialize(mapload) + var/list/extra_contraband = list( + /obj/item/clothing/under/suit/scarface = 2, + ) + LAZYADD(contraband, extra_contraband) + . = ..() + +/obj/machinery/vending/wardrobe/cargo_wardrobe/Initialize(mapload) + var/list/extra_contraband = list( + /obj/item/clothing/under/suit/scarface = 2, + ) + LAZYADD(contraband, extra_contraband) + . = ..() + /obj/machinery/vending/wardrobe/blueshield_wardrobe name = "\improper BlueDrobe" desc = "A vending machine for blueshield and blueshield-related clothing!" diff --git a/modular_splurt/icons/mob/clothing/under/suits.dmi b/modular_splurt/icons/mob/clothing/under/suits.dmi new file mode 100644 index 0000000000..2e002c0f6a Binary files /dev/null and b/modular_splurt/icons/mob/clothing/under/suits.dmi differ diff --git a/modular_splurt/icons/mob/clothing/under/suits_digi.dmi b/modular_splurt/icons/mob/clothing/under/suits_digi.dmi new file mode 100644 index 0000000000..b83cc5c454 Binary files /dev/null and b/modular_splurt/icons/mob/clothing/under/suits_digi.dmi differ diff --git a/modular_splurt/icons/mob/hair.dmi b/modular_splurt/icons/mob/hair.dmi index fad073e9c2..58352c60cc 100644 Binary files a/modular_splurt/icons/mob/hair.dmi and b/modular_splurt/icons/mob/hair.dmi differ diff --git a/modular_splurt/icons/mob/mam_tails.dmi b/modular_splurt/icons/mob/mam_tails.dmi index e2ec928443..ae30f9745b 100644 Binary files a/modular_splurt/icons/mob/mam_tails.dmi and b/modular_splurt/icons/mob/mam_tails.dmi differ diff --git a/modular_splurt/icons/obj/clothing/under/suits.dmi b/modular_splurt/icons/obj/clothing/under/suits.dmi new file mode 100644 index 0000000000..74736b9827 Binary files /dev/null and b/modular_splurt/icons/obj/clothing/under/suits.dmi differ diff --git a/modular_splurt/icons/obj/clothing/uniforms.dmi b/modular_splurt/icons/obj/clothing/uniforms.dmi index 35db3cd79d..ccff738589 100644 Binary files a/modular_splurt/icons/obj/clothing/uniforms.dmi and b/modular_splurt/icons/obj/clothing/uniforms.dmi differ diff --git a/modular_splurt/sound/voice/agony.ogg b/modular_splurt/sound/voice/agony.ogg new file mode 100644 index 0000000000..6898ed8fc8 Binary files /dev/null and b/modular_splurt/sound/voice/agony.ogg differ diff --git a/tgstation.dme b/tgstation.dme index 7659f3f8ec..6b33caa41c 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4285,6 +4285,7 @@ #include "modular_sand\code\modules\tgs\custom_procs.dm" #include "modular_sand\code\modules\uplink\uplink_items.dm" #include "modular_sand\code\modules\uplink\uplink_roles.dm" +#include "modular_sand\code\modules\vehicles\mecha\mech_fabricator.dm" #include "modular_sand\code\modules\vending\autodrobe.dm" #include "modular_sand\code\modules\vending\clothesmate.dm" #include "modular_sand\code\modules\vending\kinkmate.dm" @@ -4508,6 +4509,7 @@ #include "modular_splurt\code\modules\antagonists\_common\antag_spawner.dm" #include "modular_splurt\code\modules\antagonists\bloodsucker\datum_bloodsucker.dm" #include "modular_splurt\code\modules\antagonists\bloodsucker\levelup.dm" +#include "modular_splurt\code\modules\antagonists\brainwashing\brainwashing.dm" #include "modular_splurt\code\modules\antagonists\ert_cleanup\ert_cleanup.dm" #include "modular_splurt\code\modules\antagonists\qareen\qareen.dm" #include "modular_splurt\code\modules\antagonists\qareen\qareen_abilities.dm" @@ -4639,6 +4641,7 @@ #include "modular_splurt\code\modules\clothing\under\jobs\security.dm" #include "modular_splurt\code\modules\clothing\under\jobs\civilian\civilian.dm" #include "modular_splurt\code\modules\clothing\under\jobs\civilian\clown_mime.dm" +#include "modular_splurt\code\modules\clothing\under\jobs\civilian\suits.dm" #include "modular_splurt\code\modules\clothing\underwear\_underwear.dm" #include "modular_splurt\code\modules\clothing\underwear\boxers.dm" #include "modular_splurt\code\modules\clothing\underwear\shirts.dm"