diff --git a/aurorastation.dme b/aurorastation.dme
index fe6e31bb63a..ce2ef797b85 100644
--- a/aurorastation.dme
+++ b/aurorastation.dme
@@ -28,6 +28,7 @@
#include "code\__defines\color.dm"
#include "code\__defines\damage_organs.dm"
#include "code\__defines\dna.dm"
+#include "code\__defines\drinks.dm"
#include "code\__defines\dview.dm"
#include "code\__defines\extools.dm"
#include "code\__defines\flags.dm"
@@ -84,6 +85,7 @@
#include "code\_helpers\global_lists.dm"
#include "code\_helpers\icon_smoothing.dm"
#include "code\_helpers\icons.dm"
+#include "code\_helpers\images.dm"
#include "code\_helpers\lists.dm"
#include "code\_helpers\logging.dm"
#include "code\_helpers\maths.dm"
@@ -467,6 +469,7 @@
#include "code\game\gamemodes\changeling\implements\powers\stings.dm"
#include "code\game\gamemodes\changeling\implements\powers\suck.dm"
#include "code\game\gamemodes\cult\cult.dm"
+#include "code\game\gamemodes\cult\cultist_datum.dm"
#include "code\game\gamemodes\cult\hell_universe.dm"
#include "code\game\gamemodes\cult\narsie.dm"
#include "code\game\gamemodes\cult\cultify\mob.dm"
@@ -1405,6 +1408,7 @@
#include "code\modules\clothing\rings\material.dm"
#include "code\modules\clothing\rings\rings.dm"
#include "code\modules\clothing\sets\acting_captain.dm"
+#include "code\modules\clothing\sets\captain.dm"
#include "code\modules\clothing\shoes\colour.dm"
#include "code\modules\clothing\shoes\jobs.dm"
#include "code\modules\clothing\shoes\leg_guard.dm"
@@ -1585,6 +1589,7 @@
#include "code\modules\events\grid_check.dm"
#include "code\modules\events\infestation.dm"
#include "code\modules\events\ion_storm.dm"
+#include "code\modules\events\maint_drones.dm"
#include "code\modules\events\meteors.dm"
#include "code\modules\events\money_hacker.dm"
#include "code\modules\events\money_lotto.dm"
@@ -1846,6 +1851,7 @@
#include "code\modules\mining\mint.dm"
#include "code\modules\mining\ore.dm"
#include "code\modules\mining\ore_datum.dm"
+#include "code\modules\mining\ore_detector.dm"
#include "code\modules\mining\ore_satchel.dm"
#include "code\modules\mining\satchel_ore_boxdm.dm"
#include "code\modules\mining\drilling\drill.dm"
@@ -2143,6 +2149,7 @@
#include "code\modules\mob\living\simple_animal\hostile\phoron_worm.dm"
#include "code\modules\mob\living\simple_animal\hostile\pirate.dm"
#include "code\modules\mob\living\simple_animal\hostile\pra.dm"
+#include "code\modules\mob\living\simple_animal\hostile\rogue_maint_drone.dm"
#include "code\modules\mob\living\simple_animal\hostile\russian.dm"
#include "code\modules\mob\living\simple_animal\hostile\sarlacc.dm"
#include "code\modules\mob\living\simple_animal\hostile\spider_queen.dm"
diff --git a/code/__defines/drinks.dm b/code/__defines/drinks.dm
new file mode 100644
index 00000000000..2f25b7c039b
--- /dev/null
+++ b/code/__defines/drinks.dm
@@ -0,0 +1,4 @@
+#define NO_EMPTY_ICON 1 //does NOT have an iconstate_empty icon. If adding empty icons for a drink, make sure it does not have this flag
+#define UNIQUE_EMPTY_ICON 2 //Uses the empty_icon_state listed. Should really only be used when one trash state applies to multiple drinks. Remove if one is added
+#define UNIQUE_EMPTY_ICON_FILE 4 //Uses the empty_icon_state listed. also doesn't change its icon file, giving it a pseudo contained sprite status
+#define IS_GLASS 8 //Container is glass. Affects shattering, unacidable, etc.
\ No newline at end of file
diff --git a/code/__defines/guns.dm b/code/__defines/guns.dm
index 9b73aeaa3dd..d1b51aa9c91 100644
--- a/code/__defines/guns.dm
+++ b/code/__defines/guns.dm
@@ -6,6 +6,7 @@
#define SOUNDS_BULLET_METAL list('sound/effects/projectile_impact/bullet_metal1.ogg', 'sound/effects/projectile_impact/bullet_metal2.ogg', 'sound/effects/projectile_impact/bullet_metal3.ogg')
#define SOUNDS_LASER_MEAT list('sound/effects/projectile_impact/energy_meat1.ogg','sound/effects/projectile_impact/energy_meat2.ogg')
#define SOUNDS_LASER_METAL list('sound/effects/projectile_impact/energy_metal1.ogg','sound/effects/projectile_impact/energy_metal2.ogg')
+#define SOUNDS_ION_ANY list('sound/effects/projectile_impact/ion_any.ogg')
//Used in determining the currently permissable firemodes of wireless-control firing pins.
#define WIRELESS_PIN_AUTOMATIC 1
diff --git a/code/__defines/items_clothing.dm b/code/__defines/items_clothing.dm
index d91dc867554..7e165d6c6a7 100644
--- a/code/__defines/items_clothing.dm
+++ b/code/__defines/items_clothing.dm
@@ -37,6 +37,7 @@
#define NOSLIP 0x8 // Prevents from slipping on wet floors, in space, etc.
#define BLOCK_GAS_SMOKE_EFFECT 0x10 // Blocks the effect that chemical clouds would have on a mob -- glasses, mask and helmets ONLY! (NOTE: flag shared with ONESIZEFITSALL)
#define FLEXIBLEMATERIAL 0x20 // At the moment, masks with this flag will not prevent eating even if they are covering your face.
+#define SOUNDPROTECTION 0x40 // whether wearing this item will protect you from loud noises such as flashbangs | this only works for ear slots or the head slot
// Flags for pass_flags.
#define PASSTABLE 0x1
@@ -95,6 +96,7 @@
#define slot_w_uniform_str "slot_w_uniform"
#define slot_head_str "slot_head"
#define slot_wear_suit_str "slot_suit"
+#define slot_shoes_str "slot_shoes"
//itemstate suffixes. Used for containedsprite worn items
#define WORN_LHAND "_lh"
diff --git a/code/__defines/machinery.dm b/code/__defines/machinery.dm
index f7f76975e7d..7ef4ea02350 100644
--- a/code/__defines/machinery.dm
+++ b/code/__defines/machinery.dm
@@ -133,6 +133,4 @@ var/list/restricted_camera_networks = list(NETWORK_ERT,NETWORK_MERCENARY,"Secret
#define M_NO_PROCESS 27
// This controls how much power the AME generates per unit of fuel.
-// Assuming 100% efficency, use this equation to figure out power output.
-// power_generated = (fuel**2) * AM_POWER_FACTOR
-#define AM_POWER_FACTOR 50000
+#define AM_POWER_FACTOR 1000000
\ No newline at end of file
diff --git a/code/__defines/materials.dm b/code/__defines/materials.dm
index 11cf6d4a390..23572899ae4 100644
--- a/code/__defines/materials.dm
+++ b/code/__defines/materials.dm
@@ -64,3 +64,8 @@
#define MATERIAL_CLOTH_BEIGE "beige"
#define MATERIAL_CLOTH_LIME "lime"
+#define MATERIAL_ALTERATION_NONE 0
+#define MATERIAL_ALTERATION_NAME 1
+#define MATERIAL_ALTERATION_DESC 2
+#define MATERIAL_ALTERATION_COLOR 4
+#define MATERIAL_ALTERATION_ALL (~MATERIAL_ALTERATION_NONE)
diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm
index bbdfbf705a4..92ba7e73108 100644
--- a/code/__defines/mobs.dm
+++ b/code/__defines/mobs.dm
@@ -85,6 +85,10 @@
#define I_GRAB "grab"
#define I_HURT "harm"
+//movement intents
+#define M_WALK "walk"
+#define M_RUN "run"
+
// Limbs and robotic stuff.
#define BP_L_FOOT "l_foot"
#define BP_R_FOOT "r_foot"
@@ -100,6 +104,10 @@
#define BP_ALL_LIMBS list(BP_CHEST, BP_GROIN, BP_HEAD, BP_L_ARM, BP_R_ARM, BP_L_HAND, BP_R_HAND, BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT)
#define BP_IS_ROBOTIC(org) (org.status & ORGAN_ROBOT)
+#define ROBOTIC_NONE 0
+#define ROBOTIC_ASSISTED 1
+#define ROBOTIC_MECHANICAL 2
+
//Generic organs
#define BP_MOUTH "mouth"
#define BP_EYES "eyes"
@@ -204,6 +212,7 @@
#define INV_W_UNIFORM_DEF_ICON 'icons/mob/uniform.dmi'
#define INV_ACCESSORIES_DEF_ICON 'icons/mob/ties.dmi'
#define INV_SUIT_DEF_ICON 'icons/mob/suit.dmi'
+#define INV_SHOES_DEF_ICON 'icons/mob/feet.dmi'
// IPC tags
#define IPC_OWNERSHIP_SELF "Self Owned"
diff --git a/code/__defines/tgs.dm b/code/__defines/tgs.dm
index 2ca005e4470..4015d24808d 100644
--- a/code/__defines/tgs.dm
+++ b/code/__defines/tgs.dm
@@ -1,179 +1,349 @@
-//tgstation-server DMAPI
+// tgstation-server DMAPI
-//All functions and datums outside this document are subject to change with any version and should not be relied on
+#define TGS_DMAPI_VERSION "5.2.2"
-//CONFIGURATION
+// All functions and datums outside this document are subject to change with any version and should not be relied on.
-//create this define if you want to do configuration outside of this file
+// CONFIGURATION
+
+/// Create this define if you want to do TGS configuration outside of this file.
#ifndef TGS_EXTERNAL_CONFIGURATION
-//Comment this out once you've filled in the below
+// Comment this out once you've filled in the below.
//#error TGS API unconfigured
-//Required interfaces (fill in with your codebase equivalent):
+// Uncomment this if you wish to allow the game to interact with TGS 3.
+// This will raise the minimum required security level of your game to TGS_SECURITY_TRUSTED due to it utilizing call()()
+//#define TGS_V3_API
-//create a global variable named `Name` and set it to `Value`
-//These globals must not be modifiable from anywhere outside of the server tools
+// Required interfaces (fill in with your codebase equivalent):
+
+/// Create a global variable named `Name` and set it to `Value`.
#define TGS_DEFINE_AND_SET_GLOBAL(Name, Value) var/global/_tgs_##Name = ##Value
-//Read the value in the global variable `Name`
+/// Read the value in the global variable `Name`.
#define TGS_READ_GLOBAL(Name) global._tgs_##Name
-//Set the value in the global variable `Name` to `Value`
+/// Set the value in the global variable `Name` to `Value`.
#define TGS_WRITE_GLOBAL(Name, Value) global._tgs_##Name = ##Value
-//Disallow ANYONE from reflecting a given `path`, security measure to prevent in-game priveledge escalation
+/// Disallow ANYONE from reflecting a given `path`, security measure to prevent in-game use of DD -> TGS capabilities.
#define TGS_PROTECT_DATUM(Path)
-//display an announcement `message` from the server to all players
-#define TGS_WORLD_ANNOUNCE(message) world << "[html_encode(##message)]"
+/// Display an announcement `message` from the server to all players.
+#define TGS_WORLD_ANNOUNCE(message) world << "TGS Notification: [html_encode(##message)]"
-//Notify current in-game administrators of a string `event`
+/// Notify current in-game administrators of a string `event`.
#define TGS_NOTIFY_ADMINS(event) message_admins(##event)
-//Write an info `message` to a server log
+/// Write an info `message` to a server log.
#define TGS_INFO_LOG(message) log_tgs("[##message]")
-//Write an error `message` to a server log
+/// Write an warning `message` to a server log.
+#define TGS_WARNING_LOG(message) log_tgs("[##message]", SEVERITY_WARNING)
+
+/// Write an error `message` to a server log.
#define TGS_ERROR_LOG(message) log_tgs("[##message]", SEVERITY_ERROR)
-//Get the number of connected /clients
+/// Get the number of connected /clients.
#define TGS_CLIENT_COUNT clients.len
#endif
-//EVENT CODES
+// EVENT CODES
-//TODO
+/// Before a reboot mode change, extras parameters are the current and new reboot mode enums
+#define TGS_EVENT_REBOOT_MODE_CHANGE -1
+/// Before a port change is about to happen, extra parameters is new port
+#define TGS_EVENT_PORT_SWAP -2
+/// Before the instance is renamed, extra parameter is the new name
+#define TGS_EVENT_INSTANCE_RENAMED -3
+/// After the watchdog reattaches to DD, extra parameter is the new [/datum/tgs_version] of the server
+#define TGS_EVENT_WATCHDOG_REATTACH -4
+
+/// When the repository is reset to its origin reference. Parameters: Reference name, Commit SHA
+#define TGS_EVENT_REPO_RESET_ORIGIN 0
+/// When the repository performs a checkout. Parameters: Checkout git object
+#define TGS_EVENT_REPO_CHECKOUT 1
+/// When the repository performs a fetch operation. No parameters
+#define TGS_EVENT_REPO_FETCH 2
+/// When the repository merges a pull request. Parameters: PR Number, PR Sha, (Nullable) Comment made by TGS user
+#define TGS_EVENT_REPO_MERGE_PULL_REQUEST 3
+/// Before the repository makes a sychronize operation. Parameters: Absolute repostiory path
+#define TGS_EVENT_REPO_PRE_SYNCHRONIZE 4
+/// Before a BYOND install operation begins. Parameters: [/datum/tgs_version] of the installing BYOND
+#define TGS_EVENT_BYOND_INSTALL_START 5
+/// When a BYOND install operation fails. Parameters: Error message
+#define TGS_EVENT_BYOND_INSTALL_FAIL 6
+/// When the active BYOND version changes. Parameters: (Nullable) [/datum/tgs_version] of the current BYOND, [/datum/tgs_version] of the new BYOND
+#define TGS_EVENT_BYOND_ACTIVE_VERSION_CHANGE 7
+/// When the compiler starts running. Parameters: Game directory path, origin commit SHA
+#define TGS_EVENT_COMPILE_START 8
+/// When a compile is cancelled. No parameters
+#define TGS_EVENT_COMPILE_CANCELLED 9
+/// When a compile fails. Parameters: Game directory path, [TRUE]/[FALSE] based on if the cause for failure was DMAPI validation
+#define TGS_EVENT_COMPILE_FAILURE 10
+/// When a compile operation completes. Note, this event fires before the new .dmb is loaded into the watchdog. Consider using the [TGS_EVENT_DEPLOYMENT_COMPLETE] instead. Parameters: Game directory path
+#define TGS_EVENT_COMPILE_COMPLETE 11
+/// When an automatic update for the current instance begins. No parameters
+#define TGS_EVENT_INSTANCE_AUTO_UPDATE_START 12
+/// When the repository encounters a merge conflict: Parameters: Base SHA, target SHA, base reference, target reference
+#define TGS_EVENT_REPO_MERGE_CONFLICT 13
+/// When a deployment completes. No Parameters
+#define TGS_EVENT_DEPLOYMENT_COMPLETE 14
+/// Before the watchdog shuts down. Not sent for graceful shutdowns. No parameters.
+#define TGS_EVENT_WATCHDOG_SHUTDOWN 15
+/// Before the watchdog detaches for a TGS update/restart. No parameters.
+#define TGS_EVENT_WATCHDOG_DETACH 16
+// We don't actually implement this value as the DMAPI can never receive it
+// #define TGS_EVENT_WATCHDOG_LAUNCH 17
+
+// OTHER ENUMS
+
+/// The server will reboot normally.
+#define TGS_REBOOT_MODE_NORMAL 0
+/// The server will stop running on reboot.
+#define TGS_REBOOT_MODE_SHUTDOWN 1
+/// The watchdog will restart on reboot.
+#define TGS_REBOOT_MODE_RESTART 2
+
+/// DreamDaemon Trusted security level.
+#define TGS_SECURITY_TRUSTED 0
+/// DreamDaemon Safe security level.
+#define TGS_SECURITY_SAFE 1
+/// DreamDaemon Ultrasafe security level.
+#define TGS_SECURITY_ULTRASAFE 2
//REQUIRED HOOKS
-//Call this somewhere in /world/New() that is always run
-//event_handler: optional user defined event handler. The default behaviour is to broadcast the event in english to all connected admin channels
-/world/proc/TgsNew(datum/tgs_event_handler/event_handler)
+/**
+ * Call this somewhere in [/world/proc/New] that is always run. This function may sleep!
+ *
+ * * event_handler - Optional user defined [/datum/tgs_event_handler].
+ * * minimum_required_security_level: The minimum required security level to run the game in which the DMAPI is integrated. Can be one of [TGS_SECURITY_ULTRASAFE], [TGS_SECURITY_SAFE], or [TGS_SECURITY_TRUSTED].
+ */
+/world/proc/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SECURITY_ULTRASAFE)
return
-//Call this when your initializations are complete and your game is ready to play before any player interactions happen
-//This may use world.sleep_offline to make this happen so ensure no changes are made to it while this call is running
+/**
+ * Call this when your initializations are complete and your game is ready to play before any player interactions happen.
+ *
+ * This may use [/world/var/sleep_offline] to make this happen so ensure no changes are made to it while this call is running.
+ * Afterwards, consider explicitly setting it to what you want to avoid this BYOND bug: http://www.byond.com/forum/post/2575184
+ * Before this point, note that any static files or directories may be in use by another server. Your code should account for this.
+ * This function should not be called before ..() in [/world/proc/New].
+ */
/world/proc/TgsInitializationComplete()
return
-//Put this somewhere in /world/Topic(T, Addr, Master, Keys) that is always run before T is modified
-#define TGS_TOPIC var/tgs_topic_return = TgsTopic(T); if(tgs_topic_return) return tgs_topic_return
+/// Put this at the start of [/world/proc/Topic].
+#define TGS_TOPIC var/tgs_topic_return = TgsTopic(args[1]); if(tgs_topic_return) return tgs_topic_return
-//Call this at the beginning of world/Reboot(reason)
+/**
+ * Call this at the beginning of [world/proc/Reboot].
+ */
/world/proc/TgsReboot()
return
-//DATUM DEFINITIONS
-//unless otherwise specified all datums defined here should be considered read-only, warranty void if written
+// DATUM DEFINITIONS
+// All datums defined here should be considered read-only
-//represents git revision information about the current world build
+/// Represents git revision information.
/datum/tgs_revision_information
- var/commit //full sha of compiled commit
- var/origin_commit //full sha of last known remote commit. This may be null if the TGS repository is not currently tracking a remote branch
+ /// Full SHA of the commit.
+ var/commit
+ /// Full sha of last known remote commit. This may be null if the TGS repository is not currently tracking a remote branch.
+ var/origin_commit
-//represents a merge of a GitHub pull request
-/datum/tgs_revision_information/test_merge
- var/number //pull request number
- var/title //pull request title
- var/body //pull request body
- var/author //pull request github author
- var/url //link to pull request html
- var/pull_request_commit //commit of the pull request when it was merged
- var/time_merged //timestamp of when the merge commit for the pull request was created
- var/comment //optional comment left by the one who initiated the test merge
+/// Represents a version.
+/datum/tgs_version
+ /// The suite/major version number
+ var/suite
-//represents a connected chat channel
-/datum/tgs_chat_channel
- var/id //internal channel representation
- var/friendly_name //user friendly channel name
- var/server_name //server name the channel resides on
- var/provider_name //chat provider for the channel
- var/is_admin_channel //if the server operator has marked this channel for game admins only
- var/is_private_channel //if this is a private chat channel
+ // This group of variables can be null to represent a wild card
+ /// The minor version number. null for wildcards
+ var/minor
+ /// The patch version number. null for wildcards
+ var/patch
-//represents a chat user
-/datum/tgs_chat_user
- var/id //Internal user representation
- var/friendly_name //The user's public name
- var/mention //The text to use to ping this user in a message
- var/datum/tgs_chat_channel/channel //The /datum/tgs_chat_channel this user was from
+ /// Legacy version number. Generally null
+ var/deprecated_patch
-//user definable callback for handling events
-/datum/tgs_event_handler/proc/HandleEvent(event_code)
+ /// Unparsed string value
+ var/raw_parameter
+ /// String value minus prefix
+ var/deprefixed_parameter
+
+/**
+ * Returns [TRUE]/[FALSE] based on if the [/datum/tgs_version] contains wildcards.
+ */
+/datum/tgs_version/proc/Wildcard()
return
-//user definable chat command
-/datum/tgs_chat_command
- var/name = "" //the string to trigger this command on a chat bot. e.g. TGS3_BOT: do_this_command
- var/help_text = "" //help text for this command
- var/admin_only = FALSE //set to TRUE if this command should only be usable by registered chat admins
+/**
+ * Returns [TRUE]/[FALSE] based on if the [/datum/tgs_version] equals some other version.
+ *
+ * other_version - The [/datum/tgs_version] to compare against.
+ */
+/datum/tgs_version/proc/Equals(datum/tgs_version/other_version)
+ return
-//override to implement command
-//sender: The tgs_chat_user who send to command
-//params: The trimmed string following the command name
-//The return value will be stringified and sent to the appropriate chat
+/// Represents a merge of a GitHub pull request.
+/datum/tgs_revision_information/test_merge
+ /// The pull request number.
+ var/number
+ /// The pull request title when it was merged.
+ var/title
+ /// The pull request body when it was merged.
+ var/body
+ /// The GitHub username of the pull request's author.
+ var/author
+ /// An http URL to the pull request.
+ var/url
+ /// The SHA of the pull request when that was merged.
+ var/pull_request_commit
+ /// ISO 8601 timestamp of when the pull request was merged.
+ var/time_merged
+ /// (Nullable) Comment left by the TGS user who initiated the merge..
+ var/comment
+
+/// Represents a connected chat channel.
+/datum/tgs_chat_channel
+ /// TGS internal channel ID.
+ var/id
+ /// User friendly name of the channel.
+ var/friendly_name
+ /// Name of the chat connection. This is the IRC server address or the Discord guild.
+ var/connection_name
+ /// [TRUE]/[FALSE] based on if the server operator has marked this channel for game admins only.
+ var/is_admin_channel
+ /// [TRUE]/[FALSE] if the channel is a private message channel for a [/datum/tgs_chat_user].
+ var/is_private_channel
+ /// Tag string associated with the channel in TGS
+ var/custom_tag
+
+// Represents a chat user
+/datum/tgs_chat_user
+ /// TGS internal user ID.
+ var/id
+ // The user's display name.
+ var/friendly_name
+ // The string to use to ping this user in a message.
+ var/mention
+ /// The [/datum/tgs_chat_channel] the user was from
+ var/datum/tgs_chat_channel/channel
+
+/**
+ * User definable callback for handling TGS events.
+ *
+ * event_code - One of the TGS_EVENT_ defines. Extra parameters will be documented in each
+ */
+/datum/tgs_event_handler/proc/HandleEvent(event_code, ...)
+ set waitfor = FALSE
+ return
+
+/// User definable chat command
+/datum/tgs_chat_command
+ /// The string to trigger this command on a chat bot. e.g `@bot name ...` or `!tgs name ...`
+ var/name = ""
+ /// The help text displayed for this command
+ var/help_text = ""
+ /// If this command should be available to game administrators only
+ var/admin_only = FALSE
+
+/**
+ * Process command activation. Should return a string 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].
+ */
/datum/tgs_chat_command/proc/Run(datum/tgs_chat_user/sender, params)
CRASH("[type] has no implementation for Run()")
-//FUNCTIONS
+// API FUNCTIONS
-//Returns the respective string version of the API
+/// Returns the maximum supported [/datum/tgs_version] of the DMAPI.
/world/proc/TgsMaximumAPIVersion()
return
+/// Returns the minimum supported [/datum/tgs_version] of the DMAPI.
/world/proc/TgsMinimumAPIVersion()
return
-//Gets the current version of the server tools running the server
-/world/proc/TgsVersion()
- return
-
-//Returns TRUE if the world was launched under the server tools and the API matches, FALSE otherwise
-//No function below this succeeds if it returns FALSE
+/**
+ * Returns [TRUE] if DreamDaemon was launched under TGS, the API matches, and was properly initialized. [FALSE] will be returned otherwise.
+ */
/world/proc/TgsAvailable()
return
-/world/proc/TgsInstanceName()
- return
+// No function below this succeeds if it TgsAvailable() returns FALSE or if TgsNew() has yet to be called.
-//Get the current `/datum/tgs_revision_information`
-/world/proc/TgsRevision()
- return
-
-//Gets a list of active `/datum/tgs_revision_information/test_merge`s
-/world/proc/TgsTestMerges()
- return
-
-//Forces a hard reboot of BYOND by ending the process
-//unlike del(world) clients will try to reconnect
-//If the service has not requested a shutdown, the next server will take over
+/**
+ * Forces a hard reboot of DreamDaemon by ending the process.
+ *
+ * Unlike del(world) clients will try to reconnect.
+ * If TGS has not requested a [TGS_REBOOT_MODE_SHUTDOWN] DreamDaemon will be launched again
+ */
/world/proc/TgsEndProcess()
return
-//Gets a list of connected tgs_chat_channel
-/world/proc/TgsChatChannelInfo()
+/**
+ * Send a message to connected chats.
+ *
+ * message - The string to send.
+ * admin_only: If [TRUE], message will be sent to admin connected chats. Vice-versa applies.
+ */
+/world/proc/TgsTargetedChatBroadcast(message, admin_only = FALSE)
return
-//Sends a message to connected game chats
-//message: The message to send
-//channels: optional channels to limit the broadcast to
-/world/proc/TgsChatBroadcast(message, list/channels)
- return
-
-//Send a message to non-admin connected chats
-//message: The message to send
-//admin_only: If TRUE, message will instead be sent to only admin connected chats
-/world/proc/TgsTargetedChatBroadcast(message, admin_only)
- return
-
-//Send a private message to a specific user
-//message: The message to send
-//user: The /datum/tgs_chat_user to send to
+/**
+ * Send a private message to a specific user.
+ *
+ * message - The string to send.
+ * user: The [/datum/tgs_chat_user] to PM.
+ */
/world/proc/TgsChatPrivateMessage(message, datum/tgs_chat_user/user)
return
+// The following functions will sleep if a call to TgsNew() is sleeping
+
+/**
+ * Send a message to connected chats that are flagged as game-related in TGS.
+ *
+ * message - The string to send.
+ * channels - Optional list of [/datum/tgs_chat_channel]s to restrict the message to.
+ */
+/world/proc/TgsChatBroadcast(message, list/channels = null)
+ return
+
+/// Returns the current [/datum/tgs_version] of TGS if it is running the server, null otherwise.
+/world/proc/TgsVersion()
+ return
+
+/// Returns the current [/datum/tgs_version] of the DMAPI being used if it was activated, null otherwise.
+/world/proc/TgsApiVersion()
+ return
+
+/// Returns the name of the TGS instance running the game if TGS is present, null otherwise.
+/world/proc/TgsInstanceName()
+ return
+
+/// Return the current [/datum/tgs_revision_information] of the running server if TGS is present, null otherwise.
+/world/proc/TgsRevision()
+ return
+
+/// Returns the current BYOND security level as a TGS_SECURITY_ define if TGS is present, null otherwise.
+/world/proc/TgsSecurityLevel()
+ return
+
+/// Returns a list of active [/datum/tgs_revision_information/test_merge]s if TGS is present, null otherwise.
+/world/proc/TgsTestMerges()
+ return
+
+/// Returns a list of connected [/datum/tgs_chat_channel]s if TGS is present, null otherwise.
+/world/proc/TgsChatChannelInfo()
+ return
+
/*
The MIT License
diff --git a/code/_helpers/images.dm b/code/_helpers/images.dm
new file mode 100644
index 00000000000..6a4896ecbea
--- /dev/null
+++ b/code/_helpers/images.dm
@@ -0,0 +1,5 @@
+/proc/pixel_shift_to_turf(var/image/I, var/turf/source_turf, var/turf/target_turf)
+ var/y_shift = (target_turf.y - source_turf.y) * world.icon_size
+ var/x_shift = (target_turf.x - source_turf.x) * world.icon_size
+ I.pixel_y += y_shift
+ I.pixel_x += x_shift
\ No newline at end of file
diff --git a/code/_helpers/text.dm b/code/_helpers/text.dm
index f9b82fbd4eb..9c0a34288e5 100644
--- a/code/_helpers/text.dm
+++ b/code/_helpers/text.dm
@@ -546,6 +546,11 @@
t = replacetext(t, "", "\[/i\]")
t = replacetext(t, "", "\[u\]")
t = replacetext(t, "", "\[/u\]")
+ t = replacetext(t, " ", "\[br\]")
+ t = replacetext(t, "
", "\[hr\]")
+ t = replacetext(t, "
", "\[list\]")
+ t = replacetext(t, "
", "\[/list\]")
+ t = replacetext(t, "
", "\[*\]")
t = replacetext(t, "", "\[large\]")
t = replacetext(t, "", "\[/large\]")
t = replacetext(t, "", "\[small\]")
@@ -632,4 +637,4 @@
var/ending = copytext(string, length(string), (length(string) + 1))
if(ending && !correct_punctuation[ending])
string += "."
- return string
\ No newline at end of file
+ return string
diff --git a/code/_onclick/hud/fullscreen.dm b/code/_onclick/hud/fullscreen.dm
index 7c34b18c117..9147b7851b6 100644
--- a/code/_onclick/hud/fullscreen.dm
+++ b/code/_onclick/hud/fullscreen.dm
@@ -115,6 +115,7 @@
/obj/screen/fullscreen/flash/noise
icon_state = "noise"
+ alpha = 127
/obj/screen/fullscreen/noise
icon = 'icons/effects/static.dmi'
diff --git a/code/_onclick/hud/human.dm b/code/_onclick/hud/human.dm
index c0cc156d171..e2363e3ba66 100644
--- a/code/_onclick/hud/human.dm
+++ b/code/_onclick/hud/human.dm
@@ -127,7 +127,7 @@
if(hud_data.has_m_intent)
using = new /obj/screen/movement_intent()
using.icon = ui_style
- using.icon_state = (mymob.m_intent == "run" ? "running" : "walking")
+ using.icon_state = (mymob.m_intent == M_RUN ? "running" : "walking")
using.color = ui_color
using.alpha = ui_alpha
src.adding += using
@@ -296,6 +296,14 @@
mymob.bodytemp.screen_loc = ui_temp
hud_elements |= mymob.bodytemp
+ if(hud_data.has_cell)
+ mymob.cells = new /obj/screen()
+ mymob.cells.icon = 'icons/mob/screen/robot.dmi'
+ mymob.cells.icon_state = "charge-empty"
+ mymob.cells.name = "cell"
+ mymob.cells.screen_loc = ui_nutrition
+ hud_elements |= target.cells
+
if(hud_data.has_nutrition)
mymob.nutrition_icon = new /obj/screen/food()
mymob.nutrition_icon.icon = 'icons/mob/status_hunger.dmi'
diff --git a/code/_onclick/hud/morph.dm b/code/_onclick/hud/morph.dm
index b83ccedc5ce..77b56463fb1 100644
--- a/code/_onclick/hud/morph.dm
+++ b/code/_onclick/hud/morph.dm
@@ -5,7 +5,7 @@
var/obj/screen/mov_intent = new /obj/screen/movement_intent()
mov_intent.set_dir(SOUTHWEST)
mov_intent.icon = 'icons/mob/screen/morph.dmi'
- mov_intent.icon_state = (mymob.m_intent == "run" ? "running" : "walking")
+ mov_intent.icon_state = (mymob.m_intent == M_RUN ? "running" : "walking")
move_intent = mov_intent
mymob.healths = new /obj/screen()
diff --git a/code/_onclick/hud/nymph_hud.dm b/code/_onclick/hud/nymph_hud.dm
index e1112618748..70800182756 100644
--- a/code/_onclick/hud/nymph_hud.dm
+++ b/code/_onclick/hud/nymph_hud.dm
@@ -11,7 +11,7 @@
using = new /obj/screen/movement_intent()
using.set_dir(SOUTHWEST)
using.icon = 'icons/mob/screen/alien.dmi'
- using.icon_state = (mymob.m_intent == "run" ? "running" : "walking")
+ using.icon_state = (mymob.m_intent == M_RUN ? "running" : "walking")
src.adding += using
move_intent = using
diff --git a/code/_onclick/hud/screen_object_types/vampire.dm b/code/_onclick/hud/screen_object_types/vampire.dm
index a9a0138b520..51f04b93f8d 100644
--- a/code/_onclick/hud/screen_object_types/vampire.dm
+++ b/code/_onclick/hud/screen_object_types/vampire.dm
@@ -10,9 +10,9 @@
/obj/screen/vampire/blood/Click(var/location, var/control, var/params)
if(ishuman(usr))
- var/mob/living/carbon/human/H = usr
- to_chat(H, SPAN_NOTICE("You have [H.mind.vampire.blood_usable]u of blood to use for vampiric powers."))
- to_chat(H, SPAN_WARNING("If it drops too low, you will go into a blood frenzy. You can only store a maximum of [VAMPIRE_MAX_USABLE_BLOOD]u."))
+ var/datum/vampire/vampire = usr.mind.antag_datums[MODE_VAMPIRE]
+ to_chat(usr, SPAN_NOTICE("You have [vampire.blood_usable]u of blood to use for vampiric powers."))
+ to_chat(usr, SPAN_WARNING("If it drops too low, you will go into a blood frenzy. You can only store a maximum of [VAMPIRE_MAX_USABLE_BLOOD]u."))
/obj/screen/vampire/frenzy
name = "frenzy count"
@@ -21,9 +21,9 @@
/obj/screen/vampire/frenzy/Click(var/location, var/control, var/params)
if(ishuman(usr))
- var/mob/living/carbon/human/H = usr
- to_chat(H, SPAN_WARNING("Your frenzy counter is at [H.mind.vampire.frenzy]."))
- to_chat(H, SPAN_WARNING("If it raises too high, you will gain incredible power, but they will be very unsubtle. You can lower your frenzy counter by getting out of holy areas and by obtaining usable blood."))
+ var/datum/vampire/vampire = usr.mind.antag_datums[MODE_VAMPIRE]
+ to_chat(usr, SPAN_WARNING("Your frenzy counter is at [vampire.frenzy]."))
+ to_chat(usr, SPAN_WARNING("If it raises too high, you will gain incredible power, but they will be very unsubtle. You can lower your frenzy counter by getting out of holy areas and by obtaining usable blood."))
/obj/screen/vampire/suck
name = "blood_suck"
diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm
index b901089a575..b6e7b6a25f4 100644
--- a/code/_onclick/hud/screen_objects.dm
+++ b/code/_onclick/hud/screen_objects.dm
@@ -417,7 +417,7 @@
user.stamina_bar.update(user.stamina)
- if (user.m_intent == "run")
+ if (user.m_intent == M_RUN)
icon_state = "running"
else
icon_state = "walking"
@@ -435,21 +435,21 @@
if(C.legcuffed)
to_chat(C, "You are legcuffed! You cannot run until you get [C.legcuffed] removed!")
- C.m_intent = "walk" //Just incase
+ C.m_intent = M_WALK //Just incase
C.hud_used.move_intent.icon_state = "walking"
return 1
switch(usr.m_intent)
- if("run")
- usr.m_intent = "walk"
- if("walk")
- usr.m_intent = "run"
+ if(M_RUN)
+ usr.m_intent = M_WALK
+ if(M_WALK)
+ usr.m_intent = M_RUN
else if(istype(usr, /mob/living/simple_animal/hostile/morph))
var/mob/living/simple_animal/hostile/morph/M = usr
switch(usr.m_intent)
- if("run")
- usr.m_intent = "walk"
- if("walk")
- usr.m_intent = "run"
+ if(M_RUN)
+ usr.m_intent = M_WALK
+ if(M_WALK)
+ usr.m_intent = M_RUN
M.update_speed()
update_move_icon(usr)
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index b16e29ab341..b956bd57de0 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -64,22 +64,41 @@ avoid code duplication. This includes items that may sometimes act as a standard
return TRUE
return ..()
+/mob/living/simple_animal/attackby(obj/item/I, mob/living/user)
+ if(I.damtype == PAIN)
+ playsound(loc, 'sound/weapons/tap.ogg', I.get_clamped_volume(), 1, -1)
+ else
+ return ..()
+
// Proximity_flag is 1 if this afterattack was called on something adjacent, in your square, or on your person.
// Click parameters is the params string from byond Click() code, see that documentation.
/obj/item/proc/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
return
+/obj/item/proc/get_clamped_volume()
+ if(w_class)
+ if(force)
+ return Clamp((force + w_class) * 4, 30, 100)// Add the item's force to its weight class and multiply by 4, then clamp the value between 30 and 100
+ else
+ return Clamp(w_class * 6, 10, 100) // Multiply the item's weight class by 6, then clamp the value between 10 and 100
+
//I would prefer to rename this attack_as_weapon(), but that would involve touching hundreds of files.
/obj/item/proc/attack(mob/living/M, mob/living/user, var/target_zone = BP_CHEST)
- if(!force || (flags & NOBLUDGEON))
+ if(flags & NOBLUDGEON)
return 0
if(M == user && user.a_intent != I_HURT)
return 0
- if(user.is_pacified())
+ if(force && user.is_pacified())
+ to_chat(user, "You don't want to harm other living beings!")
return 0
+ if(!force)
+ playsound(loc, 'sound/weapons/tap.ogg', get_clamped_volume(), 1, -1)
+ else if(hitsound)
+ playsound(loc, hitsound, get_clamped_volume(), 1, -1)
+
/////////////////////////
user.lastattacked = M
M.lastattacker = user
@@ -103,9 +122,6 @@ avoid code duplication. This includes items that may sometimes act as a standard
//Called when a weapon is used to make a successful melee attack on a mob. Returns the blocked result
/obj/item/proc/apply_hit_effect(mob/living/target, mob/living/user, var/hit_zone)
- if(hitsound)
- playsound(loc, hitsound, 50, 1, -1)
-
var/power = force
if(HULK in user.mutations)
power *= 2
diff --git a/code/controllers/subsystems/cult.dm b/code/controllers/subsystems/cult.dm
index af50519ccd7..55e78800501 100644
--- a/code/controllers/subsystems/cult.dm
+++ b/code/controllers/subsystems/cult.dm
@@ -25,7 +25,8 @@
runes_by_name[R.name] = rune
tome_data += "
"
tome_data += "[capitalize_first_letters(R.name)]: [R.desc] "
- tome_data += "This rune [R.can_be_talisman() ? "can" : "cannot"] be turned into a talisman. "
+ tome_data += "This rune [R.can_be_talisman() ? "can" : "cannot"] be turned into a talisman. "
+ tome_data += "This rune [R.can_memorize() ? "can" : "cannot"] be memorized to be scribed without a tome. "
tome_data += "
"
/datum/controller/subsystem/cult/proc/add_rune(var/datum/rune/R)
diff --git a/code/controllers/subsystems/falling.dm b/code/controllers/subsystems/falling.dm
index af9daf1328e..3343bbf13ef 100644
--- a/code/controllers/subsystems/falling.dm
+++ b/code/controllers/subsystems/falling.dm
@@ -80,7 +80,7 @@
// fall_collateral if the next turf is not open space.
if (isopenturf(victim.loc) && victim.loc:is_hole)
victim.forceMove(below)
- if(victim.pulledby)
+ if(victim.pulledby && victim.pulledby.z != victim.z)
var/mob/M = victim.pulledby
M.stop_pulling()
diff --git a/code/controllers/subsystems/initialization/atoms.dm b/code/controllers/subsystems/initialization/atoms.dm
index 18bf0f97065..233b8037ad9 100644
--- a/code/controllers/subsystems/initialization/atoms.dm
+++ b/code/controllers/subsystems/initialization/atoms.dm
@@ -13,6 +13,8 @@ var/datum/controller/subsystem/atoms/SSatoms
var/initialized = INITIALIZATION_INSSATOMS
var/old_initialized
+ var/list/late_misc_firers // this is a list of things that fire when late misc init is called
+
var/list/late_loaders
var/list/created_atoms
var/list/late_qdel
diff --git a/code/controllers/subsystems/initialization/misc_late.dm b/code/controllers/subsystems/initialization/misc_late.dm
index 7c223bb7b18..fe6cf768b8a 100644
--- a/code/controllers/subsystems/initialization/misc_late.dm
+++ b/code/controllers/subsystems/initialization/misc_late.dm
@@ -37,8 +37,14 @@
populate_code_phrases()
// this covers mapped in drone fabs
- for(var/obj/machinery/drone_fabricator/DF in SSmachinery.all_machines)
- DF.enable_drone_spawn()
+ for(var/thing in SSatoms.late_misc_firers)
+ if(istype(thing, /obj/machinery/drone_fabricator))
+ var/obj/machinery/drone_fabricator/DF = thing
+ DF.enable_drone_spawn()
+ else if(istype(thing, /mob/living/silicon/robot/drone/mining))
+ var/mob/living/silicon/robot/drone/mining/MD = thing
+ MD.request_player()
+ LAZYREMOVE(SSatoms.late_misc_firers, thing)
if (config.use_forumuser_api)
update_admins_from_api(TRUE)
diff --git a/code/datums/brain_damage/mild.dm b/code/datums/brain_damage/mild.dm
index 002ff6075a3..d301441f497 100644
--- a/code/datums/brain_damage/mild.dm
+++ b/code/datums/brain_damage/mild.dm
@@ -153,7 +153,7 @@
/datum/brain_trauma/mild/muscle_weakness/on_life()
var/fall_chance = 5
- if(owner.m_intent == "run")
+ if(owner.m_intent == M_RUN)
fall_chance += 15
if(prob(fall_chance) && !owner.lying && !owner.buckled)
to_chat(owner, "Your leg gives out!")
diff --git a/code/datums/brain_damage/severe.dm b/code/datums/brain_damage/severe.dm
index 715dcde7c75..1aa599898d8 100644
--- a/code/datums/brain_damage/severe.dm
+++ b/code/datums/brain_damage/severe.dm
@@ -79,7 +79,7 @@
if(owner.stat == UNCONSCIOUS || owner.sleeping > 0)
return
var/sleep_chance = 5
- if(owner.m_intent == "run")
+ if(owner.m_intent == M_RUN)
sleep_chance += 15
if(owner.drowsyness)
sleep_chance += owner.drowsyness + 5
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index bf1f9d5166a..b4177846b19 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -54,9 +54,7 @@
var/has_been_rev = 0//Tracks if this mind has been a rev or not
var/datum/faction/faction //associated faction
- var/datum/changeling/changeling //changeling holder
-
- var/datum/vampire/vampire //vampire holder
+ var/list/antag_datums = list()
var/rev_cooldown = 0
@@ -88,6 +86,8 @@
/datum/mind/proc/transfer_to(mob/living/new_character)
if(!istype(new_character))
world.log << "## DEBUG: transfer_to(): Some idiot has tried to transfer_to( a non mob/living mob. Please inform Carn"
+ var/datum/changeling/changeling = antag_datums[MODE_CHANGELING]
+ var/datum/vampire/vampire = antag_datums[MODE_VAMPIRE]
if(current) //remove ourself from our old body's mind variable
if(changeling)
current.remove_changeling_powers()
@@ -485,8 +485,8 @@
role_alt_title = null
assigned_job = null
//faction = null //Uncommenting this causes a compile error due to 'undefined type', fucked if I know.
- changeling = null
- vampire = null
+ for(var/thing in antag_datums)
+ QDEL_NULL(thing)
initial_account = null
objectives = list()
special_verbs = list()
diff --git a/code/datums/repositories/crew.dm b/code/datums/repositories/crew.dm
index 660128f2913..23519c64bc7 100644
--- a/code/datums/repositories/crew.dm
+++ b/code/datums/repositories/crew.dm
@@ -60,7 +60,7 @@ var/global/datum/repository/crew/crew_repository = new()
if(isipc(H) && H.internal_organs_by_name[BP_IPCTAG]) // Don't make untagged IPCs obvious
var/obj/item/organ/internal/cell/cell = H.internal_organs_by_name[BP_CELL]
if(cell)
- crewmemberData["cellCharge"] = Floor(100*H.nutrition / H.max_nutrition)
+ crewmemberData["cellCharge"] = cell.percent()
if(C.sensor_mode >= SUIT_SENSOR_VITAL)
crewmemberData["pressure"] = "N/A"
diff --git a/code/datums/uplink/devices and tools.dm b/code/datums/uplink/devices and tools.dm
index cf9bb0c3e4e..6761a2f2a49 100644
--- a/code/datums/uplink/devices and tools.dm
+++ b/code/datums/uplink/devices and tools.dm
@@ -206,4 +206,10 @@
name = "Optical Material Scanner"
desc = "These glasses make use of scanning technology to allow the wearer to see objects through solid walls and floors."
item_cost = 2
- path = /obj/item/clothing/glasses/material
\ No newline at end of file
+ path = /obj/item/clothing/glasses/material
+
+/datum/uplink_item/item/tools/earmuff_headset
+ name = "Earmuff Headset"
+ desc = "This set of earmuffs has a secret compartment housing radio gear, allowing it to function as a standard headset."
+ item_cost = 2
+ path = /obj/item/device/radio/headset/earmuff
\ No newline at end of file
diff --git a/code/defines/obj/weapon.dm b/code/defines/obj/weapon.dm
index ad654c1ff77..4bc3c6472b2 100644
--- a/code/defines/obj/weapon.dm
+++ b/code/defines/obj/weapon.dm
@@ -327,18 +327,6 @@
pixel_x = rand(-16,16)
pixel_y = rand(-16,16)
-/obj/item/legcuffs
- name = "legcuffs"
- desc = "Use this to keep prisoners in line."
- gender = PLURAL
- icon = 'icons/obj/items.dmi'
- icon_state = "handcuff"
- flags = CONDUCT
- throwforce = 0
- w_class = ITEMSIZE_NORMAL
- origin_tech = list(TECH_MATERIAL = 1)
- var/breakouttime = 300 //Deciseconds = 30s = 0.5 minute
-
/obj/item/SWF_uplink
name = "station-bounced radio"
desc = "used to comunicate it appears."
diff --git a/code/defines/procs/hud.dm b/code/defines/procs/hud.dm
index 3125fe1da73..b37c9193b81 100644
--- a/code/defines/procs/hud.dm
+++ b/code/defines/procs/hud.dm
@@ -4,6 +4,7 @@ the HUD updates properly! */
//HUD image type used to properly clear client.images precisely
/image/hud_overlay
+ appearance_flags = RESET_COLOR|RESET_ALPHA
//Medical HUD outputs. Called by the Life() proc of the mob using it, usually.
proc/process_med_hud(var/mob/M, var/local_scanner, var/mob/Alt)
diff --git a/code/game/antagonist/outsider/burglar.dm b/code/game/antagonist/outsider/burglar.dm
index f82957e7e80..9887f55e445 100644
--- a/code/game/antagonist/outsider/burglar.dm
+++ b/code/game/antagonist/outsider/burglar.dm
@@ -17,7 +17,7 @@ var/datum/antagonist/burglar/burglars
hard_cap = 2
hard_cap_round = 3
- initial_spawn_req = 1
+ initial_spawn_req = 2
initial_spawn_target = 2
faction = "syndicate"
diff --git a/code/game/antagonist/station/changeling.dm b/code/game/antagonist/station/changeling.dm
index e1321a93efd..cc20866a979 100644
--- a/code/game/antagonist/station/changeling.dm
+++ b/code/game/antagonist/station/changeling.dm
@@ -26,7 +26,8 @@
faction = "Changeling"
/datum/antagonist/changeling/get_special_objective_text(var/datum/mind/player)
- return " Changeling ID: [player.changeling.changelingID]. Genomes Absorbed: [player.changeling.absorbedcount]"
+ var/datum/changeling/changeling = player.antag_datums[MODE_CHANGELING]
+ return " Changeling ID: [changeling.changelingID]. Genomes Absorbed: [changeling.absorbedcount]"
/datum/antagonist/changeling/update_antag_mob(var/datum/mind/player)
..()
diff --git a/code/game/antagonist/station/cultist.dm b/code/game/antagonist/station/cultist.dm
index f582be3da72..f29b02d7942 100644
--- a/code/game/antagonist/station/cultist.dm
+++ b/code/game/antagonist/station/cultist.dm
@@ -87,11 +87,19 @@ var/datum/antagonist/cultist/cult
if(player.current && !istype(player.current, /mob/living/simple_animal/construct))
player.current.add_language(LANGUAGE_CULT)
player.current.verbs |= /datum/antagonist/cultist/proc/appraise_offering
+ player.current.verbs |= /datum/cultist/proc/memorize_rune
+ player.current.verbs |= /datum/cultist/proc/forget_rune
+ player.current.verbs |= /datum/cultist/proc/scribe_rune
+ player.antag_datums[MODE_CULTIST] = new /datum/cultist()
+
/datum/antagonist/cultist/remove_antagonist(var/datum/mind/player)
. = ..()
player.current.verbs -= /datum/antagonist/cultist/proc/appraise_offering
+ player.current.verbs -= /datum/cultist/proc/memorize_rune
+ player.current.verbs -= /datum/cultist/proc/forget_rune
+ player.current.verbs -= /datum/cultist/proc/scribe_rune
/datum/antagonist/cultist/can_become_antag(var/datum/mind/player, ignore_role = 1)
if(!..())
@@ -104,7 +112,7 @@ var/datum/antagonist/cultist/cult
/datum/antagonist/cultist/proc/appraise_offering()
set name = "Appraise Offering"
set desc = "Find out if someone close-by can be converted to join the cult, or not."
- set category = "IC"
+ set category = "Cultist"
var/list/targets = list()
for(var/mob/living/carbon/target in view(5, usr))
diff --git a/code/game/antagonist/station/thrall.dm b/code/game/antagonist/station/thrall.dm
index ffcca8da03e..eb8c1f31341 100644
--- a/code/game/antagonist/station/thrall.dm
+++ b/code/game/antagonist/station/thrall.dm
@@ -25,6 +25,19 @@ var/datum/antagonist/thrall/thralls = null
thralls = src
+/datum/antagonist/thrall/handle_latelogin(var/mob/user)
+ var/datum/mind/M = user.mind
+ if(!M)
+ return
+ var/datum/vampire/vampire = M.antag_datums[MODE_VAMPIRE]
+ if(vampire.master_image)
+ user.client.images += vampire.master_image
+
/datum/antagonist/thrall/update_antag_mob(var/datum/mind/player)
..()
player.current.vampire_make_thrall()
+
+/datum/antagonist/thrall/remove_antagonist(datum/mind/player, show_message, implanted)
+ var/datum/vampire/vampire = player.antag_datums[MODE_VAMPIRE]
+ vampire.lose_master(player.current)
+ return ..()
\ No newline at end of file
diff --git a/code/game/antagonist/station/vampire.dm b/code/game/antagonist/station/vampire.dm
index fec8a56b6ee..07c2c7d4643 100644
--- a/code/game/antagonist/station/vampire.dm
+++ b/code/game/antagonist/station/vampire.dm
@@ -67,26 +67,36 @@ var/datum/antagonist/vampire/vamp = null
/datum/antagonist/vampire/remove_antagonist(var/datum/mind/player, var/show_message = TRUE, var/implanted)
. = ..()
if(.)
+ var/datum/vampire/vampire = player.antag_datums[MODE_VAMPIRE]
if(player.current.client)
- player.current.client.screen -= player.vampire.blood_hud
- player.current.client.screen -= player.vampire.frenzy_hud
+ player.current.client.screen -= vampire.blood_hud
+ player.current.client.screen -= vampire.frenzy_hud
player.current.verbs -= /datum/antagonist/vampire/proc/vampire_help
for(var/datum/power/vampire/P in vampirepowers)
player.current.verbs -= P.verbpath
- QDEL_NULL(player.vampire)
+ QDEL_NULL(vampire)
/datum/antagonist/vampire/handle_latelogin(var/mob/user)
var/datum/mind/M = user.mind
if(!M)
return
- if(M.vampire.status & VAMP_ISTHRALL)
+ var/datum/vampire/vampire = M.antag_datums[MODE_VAMPIRE]
+ if(vampire.master_image)
+ user.client.images += vampire.master_image
+ if(vampire.status & VAMP_ISTHRALL)
return
- M.vampire.blood_hud = new /obj/screen/vampire/blood()
- M.vampire.frenzy_hud = new /obj/screen/vampire/frenzy()
- M.vampire.blood_suck_hud = new /obj/screen/vampire/suck()
- user.client.screen += M.vampire.blood_hud
- user.client.screen += M.vampire.frenzy_hud
- user.client.screen += M.vampire.blood_suck_hud
+ vampire.blood_hud = new /obj/screen/vampire/blood()
+ vampire.frenzy_hud = new /obj/screen/vampire/frenzy()
+ vampire.blood_suck_hud = new /obj/screen/vampire/suck()
+ user.client.screen += vampire.blood_hud
+ user.client.screen += vampire.frenzy_hud
+ user.client.screen += vampire.blood_suck_hud
+
+ for(var/thrall in vampire.thralls)
+ var/mob/T = thrall
+ var/datum/vampire/T_vampire = T.mind.antag_datums[MODE_VAMPIRE]
+ if(T_vampire.thrall_image)
+ user.client.images += T_vampire.thrall_image
/datum/antagonist/vampire/proc/vampire_help()
set category = "Vampire"
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index 70afc8ba8dd..31db9f84adb 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -320,7 +320,7 @@ var/list/mob/living/forced_ambiance_list = new
L.lastarea = get_area(L.loc)
var/area/newarea = get_area(L.loc)
var/area/oldarea = L.lastarea
- if((oldarea.has_gravity() == 0) && (newarea.has_gravity() == 1) && (L.m_intent == "run")) // Being ready when you change areas gives you a chance to avoid falling all together.
+ if((oldarea.has_gravity() == 0) && (newarea.has_gravity() == 1) && (L.m_intent == M_RUN)) // Being ready when you change areas gives you a chance to avoid falling all together.
thunk(L)
L.update_floating( L.Check_Dense_Object() )
@@ -375,7 +375,7 @@ var/list/mob/living/forced_ambiance_list = new
if(H.Check_Shoegrip(FALSE))
return
- if(H.m_intent == "run")
+ if(H.m_intent == M_RUN)
H.AdjustStunned(2)
H.AdjustWeakened(2)
else
diff --git a/code/game/gamemodes/burglars/burglars.dm b/code/game/gamemodes/burglars/burglars.dm
index e667edf3b8e..42066c91673 100644
--- a/code/game/gamemodes/burglars/burglars.dm
+++ b/code/game/gamemodes/burglars/burglars.dm
@@ -4,7 +4,7 @@
name = "burglars"
config_tag = "burglars"
max_players = 15
- required_enemies = 1
+ required_enemies = 2
required_players = 4
round_description = "Something tiny is on the horizon! Is it the distance, or...?"
extended_round_description = "Two of the Orion Spur's best and brightest (or so they were told) have been tasked with burglarizing a station that will change the way capitalism grips this galaxy forever (or so they were told). It is up to the crew to repel them, and up to them to survive and possibly thrive."
diff --git a/code/game/gamemodes/changeling/helpers/_framework.dm b/code/game/gamemodes/changeling/helpers/_framework.dm
index 232bfc284c5..8c0a05608a1 100644
--- a/code/game/gamemodes/changeling/helpers/_framework.dm
+++ b/code/game/gamemodes/changeling/helpers/_framework.dm
@@ -40,8 +40,8 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
/mob/proc/absorbDNA(var/datum/absorbed_dna/newDNA)
var/datum/changeling/changeling = null
- if(src.mind && src.mind.changeling)
- changeling = src.mind.changeling
+ if(mind)
+ changeling = mind.antag_datums[MODE_CHANGELING]
if(!changeling)
return
@@ -58,8 +58,8 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
if(!mind)
return
- if(!mind.changeling)
- mind.changeling = new /datum/changeling(gender)
+ if(!mind.antag_datums[MODE_CHANGELING])
+ mind.antag_datums[MODE_CHANGELING] = new /datum/changeling(gender)
verbs += /datum/changeling/proc/EvolutionMenu
add_language("Changeling")
@@ -70,34 +70,39 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
for(var/P in powers)
powerinstances += new P()
+ var/datum/changeling/changeling = mind.antag_datums[MODE_CHANGELING]
+
// Code to auto-purchase free powers.
for(var/datum/power/changeling/P in powerinstances)
if(!P.genomecost) // Is it free?
- if(!(P in mind.changeling.purchasedpowers)) // Do we not have it already?
- mind.changeling.purchasePower(mind, P.name, 0) // Purchase it. Don't remake our verbs, we're doing it after this.
+ if(!(P in changeling.purchasedpowers)) // Do we not have it already?
+ changeling.purchasePower(mind, P.name, 0) // Purchase it. Don't remake our verbs, we're doing it after this.
- for(var/datum/power/changeling/P in mind.changeling.purchasedpowers)
+ for(var/datum/power/changeling/P in changeling.purchasedpowers)
if(P.isVerb)
if(lesser_form && !P.allowduringlesserform) continue
if(!(P in src.verbs))
src.verbs += P.verbpath
for(var/language in languages)
- mind.changeling.absorbed_languages |= language
+ changeling.absorbed_languages |= language
var/mob/living/carbon/human/H = src
if(istype(H))
var/datum/absorbed_dna/newDNA = new(H.real_name, H.dna, H.species.get_cloning_variant(), H.languages)
absorbDNA(newDNA)
- mind.changeling.mimiced_accent = H.accent
+ changeling.mimiced_accent = H.accent
return TRUE
//removes our changeling verbs
/mob/proc/remove_changeling_powers()
- if(!mind || !mind.changeling)
+ var/datum/changeling/changeling
+ if(mind)
+ changeling = mind.antag_datums[MODE_CHANGELING]
+ if(!changeling)
return
- for(var/datum/power/changeling/P in mind.changeling.purchasedpowers)
+ for(var/datum/power/changeling/P in changeling.purchasedpowers)
if(P.isVerb)
verbs -= P.verbpath
@@ -110,7 +115,7 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
if(!iscarbon(src))
return
- var/datum/changeling/changeling = src.mind.changeling
+ var/datum/changeling/changeling = mind.antag_datums[MODE_CHANGELING]
if(!changeling)
log_and_message_admins("has the changeling_transform() verb but is not a changeling.", src, get_turf(src))
return
diff --git a/code/game/gamemodes/changeling/helpers/_store.dm b/code/game/gamemodes/changeling/helpers/_store.dm
index 7e692f81164..b07c6e3a03c 100644
--- a/code/game/gamemodes/changeling/helpers/_store.dm
+++ b/code/game/gamemodes/changeling/helpers/_store.dm
@@ -220,9 +220,10 @@ var/list/datum/power/changeling/powerinstances = list()
set category = "Changeling"
set desc = "Buy new abilities with the genomes we obtained."
- if(!usr || !usr.mind || !usr.mind.changeling)
+ var/datum/changeling/changeling = usr.mind.antag_datums[MODE_CHANGELING]
+ if(!usr || !usr.mind || !changeling)
return
- src = usr.mind.changeling
+ src = changeling
if(!powerinstances.len)
for(var/P in powers)
@@ -480,7 +481,8 @@ var/list/datum/power/changeling/powerinstances = list()
/datum/changeling/proc/purchasePower(var/datum/mind/M, var/power_name, var/remake_verbs = 1)
- if(!M || !M.changeling)
+ var/datum/changeling/changeling = M.antag_datums[MODE_CHANGELING]
+ if(!M || !changeling)
return
var/datum/power/changeling/power = power_name
diff --git a/code/game/gamemodes/changeling/helpers/sting_click.dm b/code/game/gamemodes/changeling/helpers/sting_click.dm
index d5c269d39ff..3701e7f3cd9 100644
--- a/code/game/gamemodes/changeling/helpers/sting_click.dm
+++ b/code/game/gamemodes/changeling/helpers/sting_click.dm
@@ -1,9 +1,12 @@
#define CHECK_STING(holder, target) \
- if(isliving(target) && holder.a_intent == I_HURT && holder.mind && holder.mind.changeling && holder.mind.changeling.prepared_sting) { \
- if(holder.mind.changeling.prepared_sting.can_sting(A)) { \
- holder.mind.changeling.prepared_sting.do_sting(A); \
- QDEL_NULL(holder.mind.changeling.prepared_sting); \
- return; \
+ if(isliving(target) && holder.a_intent == I_HURT && holder.mind) { \
+ var/datum/changeling/changeling = holder.mind.antag_datums[MODE_CHANGELING]; \
+ if(changeling && changeling.prepared_sting) { \
+ if(changeling.prepared_sting.can_sting(A)) { \
+ changeling.prepared_sting.do_sting(A); \
+ QDEL_NULL(changeling.prepared_sting); \
+ return; \
+ } \
} \
}
diff --git a/code/game/gamemodes/changeling/implements/hivemind.dm b/code/game/gamemodes/changeling/implements/hivemind.dm
index 22c65e0b0fa..550b4d36ab3 100644
--- a/code/game/gamemodes/changeling/implements/hivemind.dm
+++ b/code/game/gamemodes/changeling/implements/hivemind.dm
@@ -15,7 +15,8 @@
set desc = "Relinquish your life and enter the land of the dead."
announce_ghost_joinleave(ghostize(1, 0))
- changeling_mob.mind.changeling.hivemind_members -= src
+ var/datum/changeling/changeling = changeling_mob.mind.antag_datums[MODE_CHANGELING]
+ changeling.hivemind_members -= src
relay_hivemind(SPAN_NOTICE("[src] has left our hivemind to join the living dead."), changeling_mob)
/mob/abstract/hivemind/proc/add_to_hivemind(var/mob/original_body, var/mob/living/carbon/human/ling)
@@ -28,7 +29,8 @@
ckey = original_body.ckey
changeling_mob = ling
if(changeling_mob)
- changeling_mob.mind.changeling.hivemind_members[name] = src
+ var/datum/changeling/changeling = changeling_mob.mind.antag_datums[MODE_CHANGELING]
+ changeling.hivemind_members[name] = src
introduction(changeling_mob)
/mob/abstract/hivemind/proc/introduction(var/mob/living/carbon/human/ling)
@@ -62,7 +64,8 @@
var/mob/living/simple_animal/hostile/morph/M = new /mob/living/simple_animal/hostile/morph(get_turf(changeling_mob))
M.stop_thinking = TRUE // prevent the AI from taking over when the player ghosts
M.ckey = ckey
- changeling_mob.mind.changeling.hivemind_members -= src
+ var/datum/changeling/changeling = changeling_mob.mind.antag_datums[MODE_CHANGELING]
+ changeling.hivemind_members -= src
to_chat(M, SPAN_DANGER(FONT_LARGE("You are a morph, released by [changeling_mob]!")))
to_chat(M, SPAN_DANGER("As a morph, you can disguise as objects by alt-clicking on them."))
diff --git a/code/game/gamemodes/changeling/implements/powers/body.dm b/code/game/gamemodes/changeling/implements/powers/body.dm
index db673a2ff9e..b0077df2892 100644
--- a/code/game/gamemodes/changeling/implements/powers/body.dm
+++ b/code/game/gamemodes/changeling/implements/powers/body.dm
@@ -287,8 +287,8 @@
C.digitalcamo = !C.digitalcamo
spawn(0)
- while(C && C.digitalcamo && C.mind && C.mind.changeling)
- C.mind.changeling.chem_charges = max(C.mind.changeling.chem_charges - 1, 0)
+ while(C && C.digitalcamo && C.mind && changeling)
+ changeling.chem_charges = max(changeling.chem_charges - 1, 0)
sleep(40)
src.verbs -= /mob/proc/changeling_digitalcamo
@@ -305,7 +305,7 @@
var/datum/changeling/changeling = changeling_power(30, 0, 100, UNCONSCIOUS)
if(!changeling)
return FALSE
- src.mind.changeling.chem_charges -= 30
+ changeling.chem_charges -= 30
var/mob/living/carbon/human/C = src
spawn(0)
@@ -366,11 +366,11 @@
feedback_add_details("changeling_powers","MV")
spawn(0)
- while(src && src.mind && src.mind.changeling && src.mind.changeling.mimicing)
- src.mind.changeling.chem_charges = max(src.mind.changeling.chem_charges - 1, 0)
+ while(src?.mind && changeling?.mimicing)
+ changeling.chem_charges = max(changeling.chem_charges - 1, 0)
sleep(40)
- if(src && src.mind && src.mind.changeling)
- src.mind.changeling.mimicing = ""
+ if(changeling)
+ changeling.mimicing = ""
/mob/proc/armblades()
set category = "Changeling"
@@ -380,7 +380,7 @@
var/datum/changeling/changeling = changeling_power(20, 0, 0)
if(!changeling)
return FALSE
- src.mind.changeling.chem_charges -= 20
+ changeling.chem_charges -= 20
var/mob/living/carbon/M = src
@@ -418,7 +418,7 @@
var/datum/changeling/changeling = changeling_power(20,0,0)
if(!changeling)
return FALSE
- src.mind.changeling.chem_charges -= 20
+ changeling.chem_charges -= 20
var/mob/living/carbon/M = src
@@ -465,7 +465,7 @@
if(alert("Are we sure we wish to reveal ourselves? This will only revert after ten minutes.", , "Yes", "No") == "No") //Changelings have to confirm whether they want to go full horrorform
return
- src.mind.changeling.chem_charges -= 40
+ changeling.chem_charges -= 40
M.visible_message("[M] writhes and contorts, their body expanding to inhuman proportions!", \
"We begin our transformation to our true form!")
diff --git a/code/game/gamemodes/changeling/implements/powers/hivemind.dm b/code/game/gamemodes/changeling/implements/powers/hivemind.dm
index 13eb29333b0..9483c1e381b 100644
--- a/code/game/gamemodes/changeling/implements/powers/hivemind.dm
+++ b/code/game/gamemodes/changeling/implements/powers/hivemind.dm
@@ -4,14 +4,13 @@
set name = "Hivemind Eject"
set desc = "Ejects a member of our internal hivemind."
- if(!(mind?.changeling))
+ var/datum/changeling/changeling = changeling_power()
+ if(!changeling)
return
- if(!mind.changeling.hivemind_members.len)
- return
- var/chosen_player = input("Choose a hivemind member to eject.", "Eject") as null|anything in mind.changeling.hivemind_members
+ var/chosen_player = input("Choose a hivemind member to eject.", "Eject") as null|anything in changeling.hivemind_members
if(!chosen_player)
return
- var/mob/abstract/hivemind/M = mind.changeling.hivemind_members[chosen_player]
+ var/mob/abstract/hivemind/M = changeling.hivemind_members[chosen_player]
M.ghost() //Deuces
return TRUE
@@ -27,9 +26,10 @@
return TRUE
/mob/proc/relay_hivemind(var/message, var/mob/ling)
- if(ling.mind?.changeling)
- for(var/H in ling.mind.changeling.hivemind_members) // tell the others in the hivemind
- var/mob/M = ling.mind.changeling.hivemind_members[H]
+ var/datum/changeling/changeling = ling.mind.antag_datums[MODE_CHANGELING]
+ if(changeling)
+ for(var/H in changeling.hivemind_members) // tell the others in the hivemind
+ var/mob/M = changeling.hivemind_members[H]
to_chat(M, message)
to_chat(ling, message)
@@ -41,17 +41,18 @@
set name = "Hivemind Release Morph"
set desc = "Releases a member of our internal hivemind as a morph, at the cost of one of our limbs."
- if(!mind?.changeling)
+ var/datum/changeling/changeling = changeling_power()
+ if(!changeling)
return
- if(!length(mind.changeling.hivemind_members))
+ if(!length(changeling.hivemind_members))
to_chat(src, SPAN_WARNING("We have no internal hivemind members to release!"))
return
- var/chosen_player = input("Choose a hivemind member to release as a morph.", "Hivemind Morph") as null|anything in mind.changeling.hivemind_members
+ var/chosen_player = input("Choose a hivemind member to release as a morph.", "Hivemind Morph") as null|anything in changeling.hivemind_members
if(!chosen_player)
return
- var/mob/abstract/hivemind/M = mind.changeling.hivemind_members[chosen_player]
+ var/mob/abstract/hivemind/M = changeling.hivemind_members[chosen_player]
M.release_as_morph()
return TRUE
diff --git a/code/game/gamemodes/changeling/implements/powers/resources.dm b/code/game/gamemodes/changeling/implements/powers/resources.dm
index b869a90a902..fa7b613fad8 100644
--- a/code/game/gamemodes/changeling/implements/powers/resources.dm
+++ b/code/game/gamemodes/changeling/implements/powers/resources.dm
@@ -1,11 +1,13 @@
//Speeds up chemical regeneration
/mob/proc/changeling_fastchemical()
- src.mind.changeling.chem_recharge_rate *= 2.5
+ var/datum/changeling/changeling = mind.antag_datums[MODE_CHANGELING]
+ changeling.chem_recharge_rate *= 2.5
return TRUE
//Increases maximum chemical storage
/mob/proc/changeling_engorgedglands()
- src.mind.changeling.chem_storage += 50
+ var/datum/changeling/changeling = mind.antag_datums[MODE_CHANGELING]
+ changeling.chem_storage += 50
return TRUE
// HIVE MIND UPLOAD/DOWNLOAD DNA
diff --git a/code/game/gamemodes/changeling/implements/powers/stings.dm b/code/game/gamemodes/changeling/implements/powers/stings.dm
index 345e781afcf..46d2da2c460 100644
--- a/code/game/gamemodes/changeling/implements/powers/stings.dm
+++ b/code/game/gamemodes/changeling/implements/powers/stings.dm
@@ -14,11 +14,14 @@
stealthy = set_stealthy
/datum/changeling_sting/proc/can_sting(var/mob/living/target)
- if(!target)
+ if(!target || !owner.mind)
return FALSE
- if(!(target in view(owner.mind.changeling.sting_range)))
+ var/datum/changeling/changeling = owner.mind.antag_datums[MODE_CHANGELING]
+ if(!changeling)
return FALSE
- if(!owner.sting_can_reach(target, owner.mind.changeling.sting_range))
+ if(!(target in view(changeling.sting_range)))
+ return FALSE
+ if(!owner.sting_can_reach(target, changeling.sting_range))
to_chat(owner, SPAN_WARNING("Our sting can't reach here!"))
return FALSE
if(!owner.changeling_power(required_chems))
@@ -30,8 +33,9 @@
return TRUE
/datum/changeling_sting/proc/do_sting(var/mob/living/target)
- owner.mind.changeling.chem_charges -= required_chems
- owner.mind.changeling.sting_range = 1
+ var/datum/changeling/changeling = owner.mind.antag_datums[MODE_CHANGELING]
+ changeling.chem_charges -= required_chems
+ changeling.sting_range = 1
owner.verbs -= verb_path
ADD_VERB_IN(owner, 10, verb_path)
@@ -53,17 +57,18 @@
feedback_add_details("changeling_powers", feedback_tag)
/mob/proc/changeling_sting(var/required_chems = 0, var/verb_path, var/datum_path, var/stealthy = FALSE)
- if(istype(mind.changeling.prepared_sting, datum_path))
- to_chat(src, SPAN_NOTICE("You unprepare the [mind.changeling.prepared_sting.name]."))
- QDEL_NULL(mind.changeling.prepared_sting)
+ var/datum/changeling/changeling = mind.antag_datums[MODE_CHANGELING]
+ if(istype(changeling.prepared_sting, datum_path))
+ to_chat(src, SPAN_NOTICE("You unprepare the [changeling.prepared_sting.name]."))
+ QDEL_NULL(changeling.prepared_sting)
return
- var/datum/changeling/changeling = changeling_power(required_chems)
+ changeling = changeling_power(required_chems)
if(!changeling)
return FALSE
changeling.prepared_sting = new datum_path(src, verb_path, required_chems, stealthy)
- to_chat(src, SPAN_NOTICE("You prepare to fire the [mind.changeling.prepared_sting.name]."))
+ to_chat(src, SPAN_NOTICE("You prepare to fire the [changeling.prepared_sting.name]."))
return TRUE
/mob/proc/changeling_hallucinate_sting()
@@ -153,8 +158,8 @@
set name = "Transformation Sting (40)"
set desc = "Causes the target to permanently transform into a collected DNA subject."
-
- if(!length(mind.changeling.absorbed_dna))
+ var/datum/changeling/changeling = mind.antag_datums[MODE_CHANGELING]
+ if(!length(changeling.absorbed_dna))
to_chat(src, SPAN_WARNING("You have no DNA to load this sting with!"))
return
@@ -163,23 +168,23 @@
return
var/list/names = list()
- for(var/thing in mind.changeling.absorbed_dna)
+ for(var/thing in changeling.absorbed_dna)
var/datum/absorbed_dna/DNA = thing
names += "[DNA.name]"
var/S = input(src, "Select the target DNA:", "Target DNA") as null|anything in names
if(!S)
- QDEL_NULL(mind.changeling.prepared_sting)
+ QDEL_NULL(changeling.prepared_sting)
to_chat(src, SPAN_NOTICE("With no DNA chosen, you unprepare the sting."))
return
- var/datum/absorbed_dna/chosen_dna = mind.changeling.GetDNA(S)
+ var/datum/absorbed_dna/chosen_dna = changeling.GetDNA(S)
if(!chosen_dna)
- QDEL_NULL(mind.changeling.prepared_sting)
+ QDEL_NULL(changeling.prepared_sting)
to_chat(src, SPAN_WARNING("Something went wrong, you do not possess the DNA of this person."))
return
- var/datum/changeling_sting/transformation/T = mind.changeling.prepared_sting
+ var/datum/changeling_sting/transformation/T = changeling.prepared_sting
T.dna_payload = chosen_dna
/datum/changeling_sting/transformation
@@ -253,11 +258,12 @@
set name = "Ranged Sting (10)"
set desc = "Your next sting ability can be used against targets 2 squares away."
- if(mind.changeling.sting_range > 1)
+ var/datum/changeling/changeling = mind.antag_datums[MODE_CHANGELING]
+ if(changeling.sting_range > 1)
to_chat(src, SPAN_WARNING("The range of your sting has already been boosted!"))
return
- var/datum/changeling/changeling = changeling_power(10, 0, 100)
+ changeling = changeling_power(10, 0, 100)
if(!changeling)
return FALSE
changeling.chem_charges -= 10
@@ -266,4 +272,4 @@
src.verbs -= /mob/proc/changeling_boost_range
ADD_VERB_IN(src, 5, /mob/proc/changeling_boost_range)
feedback_add_details("changeling_powers", "RS")
- return TRUE
\ No newline at end of file
+ return TRUE
diff --git a/code/game/gamemodes/changeling/implements/powers/suck.dm b/code/game/gamemodes/changeling/implements/powers/suck.dm
index f291fcf44b8..13073500df7 100644
--- a/code/game/gamemodes/changeling/implements/powers/suck.dm
+++ b/code/game/gamemodes/changeling/implements/powers/suck.dm
@@ -80,17 +80,18 @@
var/datum/absorbed_dna/newDNA = new(T.real_name, T.dna, T.species.get_cloning_variant(), T.languages)
absorbDNA(newDNA)
- if(T.mind && T.mind.changeling)
- if(T.mind.changeling.absorbed_dna)
- for(var/datum/absorbed_dna/dna_data in T.mind.changeling.absorbed_dna) //steal all their loot
+ var/datum/changeling/changeling_check = T.mind.antag_datums[MODE_CHANGELING]
+ if(changeling_check)
+ if(changeling_check.absorbed_dna)
+ for(var/datum/absorbed_dna/dna_data in changeling_check.absorbed_dna) //steal all their loot
if(changeling.GetDNA(dna_data.name))
continue
absorbDNA(dna_data)
changeling.absorbedcount++
- T.mind.changeling.absorbed_dna.len = TRUE
+ changeling_check.absorbed_dna.len = TRUE
- if(T.mind.changeling.purchasedpowers)
- for(var/datum/power/changeling/Tp in T.mind.changeling.purchasedpowers) //who the fuck named these variables?
+ if(changeling_check.purchasedpowers)
+ for(var/datum/power/changeling/Tp in changeling_check.purchasedpowers) //who the fuck named these variables?
if(Tp in changeling.purchasedpowers)
continue
else
@@ -100,11 +101,11 @@
else
src.make_changeling()
- changeling.chem_charges += T.mind.changeling.chem_charges
- changeling.geneticpoints += T.mind.changeling.geneticpoints
- T.mind.changeling.chem_charges = 0
- T.mind.changeling.geneticpoints = 0
- T.mind.changeling.absorbedcount = 0
+ changeling.chem_charges += changeling_check.chem_charges
+ changeling.geneticpoints += changeling_check.geneticpoints
+ changeling_check.chem_charges = 0
+ changeling_check.geneticpoints = 0
+ changeling_check.absorbedcount = 0
changeling.absorbedcount++
changeling.isabsorbing = FALSE
diff --git a/code/game/gamemodes/cult/cultist_datum.dm b/code/game/gamemodes/cult/cultist_datum.dm
new file mode 100644
index 00000000000..5d5fbf4ea8f
--- /dev/null
+++ b/code/game/gamemodes/cult/cultist_datum.dm
@@ -0,0 +1,112 @@
+/datum/cultist
+ var/list/memorized_runes
+
+/datum/cultist/proc/memorize_rune()
+ set name = "Memorize Rune"
+ set desc = "Stand atop a rune and memorize it, allowing you to draw it without your tome."
+ set category = "Cultist"
+
+ if(!ishuman(usr))
+ to_chat(usr, SPAN_WARNING("Your form is too simple to memorize runes!"))
+ return
+
+ if(usr.stat || usr.incapacitated())
+ to_chat(usr, SPAN_WARNING("You are in no shape to do this."))
+ return
+
+ var/datum/cultist/C = usr.mind.antag_datums[MODE_CULTIST]
+
+ if(LAZYLEN(C.memorized_runes) >= 3)
+ to_chat(usr, SPAN_WARNING("You can only memorize up to three runes!"))
+ return
+
+ var/mob/living/carbon/human/H = usr
+ var/obj/effect/rune/R = locate() in get_turf(H)
+ if(R)
+ if(!R.rune.can_memorize())
+ to_chat(H, SPAN_WARNING("This rune is too complex to be memorized!"))
+ return
+ if(LAZYISIN(C.memorized_runes, R.rune.name))
+ to_chat(H, SPAN_WARNING("This rune is already memorized!"))
+ return
+ H.visible_message("[H] bends over and runs their hands across \the [src].", SPAN_NOTICE("You bend over and run your hands across the patterns of the rune, slowly memorizing it."))
+ if(!do_after(H, 10 SECONDS, TRUE))
+ return
+ LAZYSET(C.memorized_runes, R.rune.name, R.rune.type)
+ to_chat(H, SPAN_NOTICE("You memorize the [R.rune.name]! You will now be able to scribe it at will."))
+ else
+ to_chat(H, SPAN_WARNING("There was no rune beneath you to memorize."))
+
+/datum/cultist/proc/forget_rune()
+ set name = "Forget Rune"
+ set desc = "Cleanse the knowledge of a rune from your memory, freeing up space for another."
+ set category = "Cultist"
+
+ if(!ishuman(usr))
+ to_chat(usr, SPAN_WARNING("Your form is too simple to memorize runes!"))
+ return
+
+ if(usr.stat || usr.incapacitated())
+ to_chat(usr, SPAN_WARNING("You are in no shape to do this."))
+ return
+
+ var/datum/cultist/C = usr.mind.antag_datums[MODE_CULTIST]
+
+ if(!LAZYLEN(C.memorized_runes))
+ to_chat(usr, SPAN_WARNING("You have no runes memorized!"))
+ return
+
+ var/chosen_rune = input("Choose a rune to forget.") as null|anything in C.memorized_runes
+ if(!chosen_rune)
+ return
+ LAZYREMOVE(C.memorized_runes, chosen_rune)
+
+/datum/cultist/proc/scribe_rune()
+ set name = "Scribe Rune"
+ set desc = "Scribe a rune that you have memorized."
+ set category = "Cultist"
+
+ if(!ishuman(usr))
+ to_chat(usr, SPAN_WARNING("Your form is too simple to memorize runes!"))
+ return
+
+ var/datum/cultist/C = usr.mind.antag_datums[MODE_CULTIST]
+
+ if(!LAZYLEN(C.memorized_runes))
+ to_chat(usr, SPAN_WARNING("You have no runes memorized!"))
+ return
+
+ if(usr.stat || usr.incapacitated())
+ to_chat(usr, SPAN_WARNING("You are in no shape to do this."))
+ return
+
+ var/chosen_rune = input("Choose a rune to scribe.") as null|anything in C.memorized_runes
+ if(!chosen_rune)
+ return
+
+ var/mob/living/carbon/human/H = usr
+ H.visible_message(SPAN_CULT("Blood flows out from \the [H]'s hands, taking shape beneath them..."))
+ H.drip(4)
+
+ if(do_after(H, 15 SECONDS))
+ create_rune(H, chosen_rune)
+
+/proc/create_rune(var/mob/living/carbon/human/scribe, var/chosen_rune)
+ if(scribe.stat || scribe.incapacitated())
+ to_chat(scribe, SPAN_WARNING("You are in no shape to do this."))
+ return
+
+ var/area/A = get_area(scribe)
+ //prevents using multiple dialogs to layer runes.
+ if(locate(/obj/effect/rune) in get_turf(scribe)) //This is check is done twice. once when choosing to scribe a rune, once here
+ to_chat(scribe, SPAN_WARNING("There is already a rune in this location."))
+ return
+
+ log_and_message_admins("created \an [chosen_rune] at \the [A.name] - [scribe.loc.x]-[scribe.loc.y]-[scribe.loc.z].") //only message if it's actually made
+
+ var/obj/effect/rune/R = new(get_turf(scribe), SScult.runes_by_name[chosen_rune])
+ to_chat(scribe, SPAN_CULT("You finish drawing the Geometer's markings."))
+ R.blood_DNA = list()
+ R.blood_DNA[scribe.dna.unique_enzymes] = scribe.dna.b_type
+ R.color = scribe.species.blood_color
+ R.filters = filter(type="drop_shadow", x = 1, y = 1, size = 4, color = scribe.species.blood_color)
\ No newline at end of file
diff --git a/code/game/gamemodes/cult/items/tome.dm b/code/game/gamemodes/cult/items/tome.dm
index 400f0480c62..99ea9930102 100644
--- a/code/game/gamemodes/cult/items/tome.dm
+++ b/code/game/gamemodes/cult/items/tome.dm
@@ -95,23 +95,7 @@
scribe.drip(4)
if(do_after(scribe, 50))
- var/area/A = get_area(scribe)
- if(use_check_and_message(scribe))
- return
-
- //prevents using multiple dialogs to layer runes.
- if(locate(/obj/effect/rune) in get_turf(scribe)) //This is check is done twice. once when choosing to scribe a rune, once here
- to_chat(scribe, SPAN_WARNING("There is already a rune in this location."))
- return
-
- log_and_message_admins("created \an [chosen_rune] at \the [A.name] - [user.loc.x]-[user.loc.y]-[user.loc.z].") //only message if it's actually made
-
- var/obj/effect/rune/R = new(get_turf(scribe), SScult.runes_by_name[chosen_rune])
- to_chat(scribe, SPAN_CULT("You finish drawing the Geometer's markings."))
- R.blood_DNA = list()
- R.blood_DNA[scribe.dna.unique_enzymes] = scribe.dna.b_type
- R.color = scribe.species.blood_color
- R.filters = filter(type="drop_shadow", x = 1, y = 1, size = 4, color = scribe.species.blood_color)
+ create_rune(scribe, chosen_rune)
else
to_chat(user, SPAN_CULT("The book seems full of illegible scribbles."))
diff --git a/code/game/gamemodes/cult/runes/_runes.dm b/code/game/gamemodes/cult/runes/_runes.dm
index b29f0884c07..9db4f862b6e 100644
--- a/code/game/gamemodes/cult/runes/_runes.dm
+++ b/code/game/gamemodes/cult/runes/_runes.dm
@@ -1,5 +1,6 @@
#define NO_TALISMAN 1
-#define HAS_SPECIAL_TALISMAN_ACTION 2
+#define CAN_MEMORIZE 2
+#define HAS_SPECIAL_TALISMAN_ACTION 4
/datum/rune
var/name // The rune's name.
@@ -42,3 +43,6 @@
/datum/rune/proc/can_be_talisman()
return !(rune_flags & NO_TALISMAN)
+
+/datum/rune/proc/can_memorize()
+ return rune_flags & CAN_MEMORIZE
diff --git a/code/game/gamemodes/cult/runes/communicate.dm b/code/game/gamemodes/cult/runes/communicate.dm
index 1dfd1d47b7f..f8a92fe64c6 100644
--- a/code/game/gamemodes/cult/runes/communicate.dm
+++ b/code/game/gamemodes/cult/runes/communicate.dm
@@ -1,6 +1,7 @@
/datum/rune/communicate
name = "communication rune"
desc = "This rune is used to send a message to all acolytes."
+ rune_flags = CAN_MEMORIZE
/datum/rune/communicate/do_rune_action(mob/living/user, atom/movable/A)
var/input = input(user, "Please choose a message to tell to the other acolytes.", "Voice of Blood", "")//sanitize() below, say() and whisper() have their own
diff --git a/code/game/gamemodes/cult/runes/convert.dm b/code/game/gamemodes/cult/runes/convert.dm
index 803496d4d3e..9e2a188ad7c 100644
--- a/code/game/gamemodes/cult/runes/convert.dm
+++ b/code/game/gamemodes/cult/runes/convert.dm
@@ -1,7 +1,7 @@
/datum/rune/convert
name = "conversion rune"
desc = "A rune used to convert the Unenlightened."
- rune_flags = NO_TALISMAN
+ rune_flags = NO_TALISMAN | CAN_MEMORIZE
var/list/converting
diff --git a/code/game/gamemodes/cult/runes/create_talisman.dm b/code/game/gamemodes/cult/runes/create_talisman.dm
index 07deb24ae15..60831749c32 100644
--- a/code/game/gamemodes/cult/runes/create_talisman.dm
+++ b/code/game/gamemodes/cult/runes/create_talisman.dm
@@ -1,7 +1,7 @@
/datum/rune/talisman
name = "talisman creation rune"
desc = "This rune creates a talisman out of a rune around it."
- rune_flags = NO_TALISMAN
+ rune_flags = NO_TALISMAN | CAN_MEMORIZE
/datum/rune/talisman/do_rune_action(mob/living/user, atom/movable/A)
var/obj/item/paper/new_talisman
diff --git a/code/game/gamemodes/cult/runes/deafen_others.dm b/code/game/gamemodes/cult/runes/deafen_others.dm
index 48ab178b4a8..e1089113904 100644
--- a/code/game/gamemodes/cult/runes/deafen_others.dm
+++ b/code/game/gamemodes/cult/runes/deafen_others.dm
@@ -1,7 +1,7 @@
/datum/rune/deafen
name = "deafening rune"
desc = "This rune is used to deafen all unbelievers in a wide range around us."
- rune_flags = HAS_SPECIAL_TALISMAN_ACTION
+ rune_flags = HAS_SPECIAL_TALISMAN_ACTION | CAN_MEMORIZE
/datum/rune/deafen/do_rune_action(mob/living/user, atom/movable/A)
do_deafen(user, A, 1, TRUE)
diff --git a/code/game/gamemodes/cult/runes/ghost_mode.dm b/code/game/gamemodes/cult/runes/ghost_mode.dm
index 981976a573d..7ee28727055 100644
--- a/code/game/gamemodes/cult/runes/ghost_mode.dm
+++ b/code/game/gamemodes/cult/runes/ghost_mode.dm
@@ -1,7 +1,7 @@
/datum/rune/ethereal
name = "ethereal rune"
desc = "This rune is used to temporarily send an acolyte into the afterlife."
- rune_flags = NO_TALISMAN
+ rune_flags = NO_TALISMAN | CAN_MEMORIZE
/datum/rune/ethereal/do_rune_action(mob/living/user, atom/movable/A) //some bits copypastaed from admin tools - Urist
if(get_turf(user) == get_turf(A) && ishuman(user))
diff --git a/code/game/gamemodes/cult/runes/hide_runes.dm b/code/game/gamemodes/cult/runes/hide_runes.dm
index c673a0d8a15..34f603473cc 100644
--- a/code/game/gamemodes/cult/runes/hide_runes.dm
+++ b/code/game/gamemodes/cult/runes/hide_runes.dm
@@ -1,6 +1,7 @@
/datum/rune/hide_runes
name = "concealment rune"
desc = "This rune is used to conceal other runes in an area around us."
+ rune_flags = CAN_MEMORIZE
/datum/rune/hide_runes/do_rune_action(mob/living/user, atom/movable/A)
var/rune_found
diff --git a/code/game/gamemodes/cult/runes/raise_dead.dm b/code/game/gamemodes/cult/runes/raise_dead.dm
index 80c6f89610a..b71da57caa8 100644
--- a/code/game/gamemodes/cult/runes/raise_dead.dm
+++ b/code/game/gamemodes/cult/runes/raise_dead.dm
@@ -1,7 +1,7 @@
/datum/rune/raise_dead
name = "revival rune"
desc = "This rune is used to revive a body in exchange for a dead sacrifice."
- rune_flags = NO_TALISMAN
+ rune_flags = NO_TALISMAN | CAN_MEMORIZE
/datum/rune/raise_dead/do_rune_action(mob/living/user, atom/movable/A)
var/mob/living/carbon/human/corpse_to_raise
diff --git a/code/game/gamemodes/cult/runes/reveal_runes.dm b/code/game/gamemodes/cult/runes/reveal_runes.dm
index 557e5ca8424..aed9e5b8792 100644
--- a/code/game/gamemodes/cult/runes/reveal_runes.dm
+++ b/code/game/gamemodes/cult/runes/reveal_runes.dm
@@ -1,7 +1,7 @@
/datum/rune/reveal_runes
name = "revealing rune"
desc = "This rune is used to reveal hidden runes in a radius around us."
- rune_flags = HAS_SPECIAL_TALISMAN_ACTION
+ rune_flags = HAS_SPECIAL_TALISMAN_ACTION | CAN_MEMORIZE
/datum/rune/reveal_runes/do_rune_action(mob/living/user, atom/movable/A)
reveal(user, A, 6, SPAN_WARNING("\The [A] turns into red dust, reveaing the surrounding runes."))
diff --git a/code/game/gamemodes/cult/runes/rune.dm b/code/game/gamemodes/cult/runes/rune.dm
index b0a07554df6..94a6c1ab139 100644
--- a/code/game/gamemodes/cult/runes/rune.dm
+++ b/code/game/gamemodes/cult/runes/rune.dm
@@ -26,6 +26,7 @@
if(iscultist(user) || isobserver(user))
to_chat(user, rune.get_cultist_fluff_text())
to_chat(user, "This rune [rune.can_be_talisman() ? "can" : "cannot"] be turned into a talisman.")
+ to_chat(user, "This rune [rune.can_memorize() ? "can" : "cannot"] be memorized to be scribed without a tome.")
else
to_chat(user, rune.get_normal_fluff_text())
diff --git a/code/game/gamemodes/cult/runes/sacrifice.dm b/code/game/gamemodes/cult/runes/sacrifice.dm
index adeaf9d9694..065676fbcab 100644
--- a/code/game/gamemodes/cult/runes/sacrifice.dm
+++ b/code/game/gamemodes/cult/runes/sacrifice.dm
@@ -1,7 +1,7 @@
/datum/rune/sacrifice
name = "sacrificial rune"
desc = "This rune is used to sacrifice an unbeliever."
- rune_flags = NO_TALISMAN
+ rune_flags = NO_TALISMAN | CAN_MEMORIZE
/datum/rune/sacrifice/do_rune_action(mob/living/user, atom/movable/A)
var/list/mob/living/carbon/human/cultists_in_range = list()
diff --git a/code/game/gamemodes/cult/runes/see_invisible.dm b/code/game/gamemodes/cult/runes/see_invisible.dm
index a0c8713717e..49b2beeb5fe 100644
--- a/code/game/gamemodes/cult/runes/see_invisible.dm
+++ b/code/game/gamemodes/cult/runes/see_invisible.dm
@@ -1,7 +1,7 @@
/datum/rune/see_invisible
name = "visibility rune"
desc = "This rune is used to see the invisible."
- rune_flags = NO_TALISMAN
+ rune_flags = NO_TALISMAN | CAN_MEMORIZE
/datum/rune/see_invisible/do_rune_action(mob/living/user, atom/movable/A)
if(user.seer == TRUE)
diff --git a/code/game/gamemodes/events/holidays/Christmas.dm b/code/game/gamemodes/events/holidays/Christmas.dm
index 2960a82eecb..acbcad18b0e 100644
--- a/code/game/gamemodes/events/holidays/Christmas.dm
+++ b/code/game/gamemodes/events/holidays/Christmas.dm
@@ -62,5 +62,9 @@
/obj/item/clothing/head/festive/santa
name = "santa hat"
icon_state = "santahat"
+ desc = "A cheap fabric santa hat."
+
+/obj/item/clothing/head/festive/santa/beard
+ desc = "A cheap fabric santa hat, this one with a beard."
body_parts_covered = HEAD
- desc = "A cheap fabric santa hat, with a fake beard and a little bell at the end."
+ icon_state = "santahat_alt"
\ No newline at end of file
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 8788632870c..d7c4f784fc1 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -636,20 +636,6 @@ var/global/list/additional_antag_types = list()
candidates += player.mind
players -= player
- // If we don't have enough antags, draft people who voted for the round.
- if(candidates.len < required_enemies)
- var/initial_candidates = candidates.len
-
- for(var/mob/abstract/new_player/player in players)
- if(player.ckey in SSvote.round_voters)
- log_debug("[player.key] voted for this round, so we are drafting them.")
- candidates += player.mind
- players -= player
-
- if (candidates.len >= required_enemies)
- log_debug("Drafted [candidates.len - initial_candidates] new antags from voters.")
- break
-
return candidates // Returns: The number of people who had the antagonist role set to yes, regardless of recomended_enemies, if that number is greater than required_enemies
// required_enemies if the number of people with that role set to yes is less than recomended_enemies,
// Less if there are not enough valid players in the game entirely to make required_enemies.
diff --git a/code/game/gamemodes/mixed/conflict.dm b/code/game/gamemodes/mixed/conflict.dm
index c715ded42e4..55f154da6d4 100644
--- a/code/game/gamemodes/mixed/conflict.dm
+++ b/code/game/gamemodes/mixed/conflict.dm
@@ -4,6 +4,6 @@
extended_round_description = "Burglars and heisters spawn during this round."
config_tag = "conflict"
required_players = 15
- required_enemies = 5
+ required_enemies = 6
antag_tags = list(MODE_RAIDER, MODE_BURGLAR)
require_all_templates = TRUE
\ No newline at end of file
diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm
index f74625d16bd..8571cc3b32a 100644
--- a/code/game/gamemodes/objective.dm
+++ b/code/game/gamemodes/objective.dm
@@ -600,7 +600,8 @@ datum/objective/capture
n_p ++
else if (SSticker.current_state == GAME_STATE_PLAYING)
for(var/mob/living/carbon/human/P in player_list)
- if(P.client && !(P.mind.changeling) && P.mind!=owner)
+ var/datum/changeling/changeling = P.mind.antag_datums[MODE_CHANGELING]
+ if(P.client && !changeling && P.mind != owner)
n_p ++
target_amount = min(target_amount, n_p)
@@ -608,7 +609,8 @@ datum/objective/capture
return target_amount
check_completion()
- if(owner && owner.changeling && owner.changeling.absorbed_dna && (owner.changeling.absorbedcount >= target_amount))
+ var/datum/changeling/changeling = owner.antag_datums[MODE_CHANGELING]
+ if(owner && changeling?.absorbed_dna && (changeling.absorbedcount >= target_amount))
return 1
else
return 0
diff --git a/code/game/gamemodes/vampire/vampire_datum.dm b/code/game/gamemodes/vampire/vampire_datum.dm
index 2c4f3a38af2..df3ae9f0210 100644
--- a/code/game/gamemodes/vampire/vampire_datum.dm
+++ b/code/game/gamemodes/vampire/vampire_datum.dm
@@ -18,6 +18,9 @@
var/obj/effect/dummy/veil_walk/holder = null // The veil_walk dummy.
var/mob/living/carbon/human/master = null // The vampire/thrall's master.
+ var/image/master_image
+ var/image/thrall_image
+
/datum/vampire/thrall
status = VAMP_ISTHRALL
@@ -45,3 +48,19 @@
return
blood_usable = max(0, blood_usable - blood_to_use)
+
+/datum/vampire/proc/assign_master(var/mob/M, var/mob/set_master, var/datum/vampire/V)
+ master = set_master
+ V.thralls += M
+ thrall_image = image('icons/mob/hud.dmi', M, "hudthrall")
+ set_master.client.images += thrall_image
+ master_image = image('icons/mob/hud.dmi', set_master, "hudvampire")
+ M.client.images += master_image
+
+/datum/vampire/proc/lose_master(var/mob/M)
+ QDEL_NULL(thrall_image)
+ QDEL_NULL(master_image)
+ if(master)
+ var/datum/vampire/master_vampire = master.mind.antag_datums[MODE_VAMPIRE]
+ master_vampire.thralls -= M
+ master = null
\ No newline at end of file
diff --git a/code/game/gamemodes/vampire/vampire_helpers.dm b/code/game/gamemodes/vampire/vampire_helpers.dm
index 0600c4cb47e..edb506a962d 100644
--- a/code/game/gamemodes/vampire/vampire_helpers.dm
+++ b/code/game/gamemodes/vampire/vampire_helpers.dm
@@ -2,26 +2,28 @@
/mob/proc/make_vampire()
if (!mind)
return
- if (!mind.vampire)
- mind.vampire = new /datum/vampire()
+ var/datum/vampire/vampire = mind.antag_datums[MODE_VAMPIRE]
+ if(!vampire)
+ mind.antag_datums[MODE_VAMPIRE] = new /datum/vampire()
+ vampire = mind.antag_datums[MODE_VAMPIRE]
// No powers to thralls. Ew.
- if (mind.vampire.status & VAMP_ISTHRALL)
+ if(vampire.status & VAMP_ISTHRALL)
return
- mind.vampire.blood_usable += 30
+ vampire.blood_usable += 30
if(client)
- mind.vampire.blood_hud = new /obj/screen/vampire/blood()
- mind.vampire.frenzy_hud = new /obj/screen/vampire/frenzy()
- client.screen += mind.vampire.blood_hud
- client.screen += mind.vampire.frenzy_hud
+ vampire.blood_hud = new /obj/screen/vampire/blood()
+ vampire.frenzy_hud = new /obj/screen/vampire/frenzy()
+ client.screen += vampire.blood_hud
+ client.screen += vampire.frenzy_hud
verbs += new /datum/antagonist/vampire/proc/vampire_help
for(var/datum/power/vampire/P in vampirepowers)
- if(!(P in mind.vampire.purchased_powers))
+ if(!(P in vampire.purchased_powers))
if(!P.blood_cost)
- mind.vampire.add_power(mind, P, 0)
+ vampire.add_power(mind, P, 0)
else if(P.isVerb && P.verbpath)
verbs += P.verbpath
@@ -29,11 +31,10 @@
// Checks the vampire's bloodlevel and unlocks new powers based on that.
/mob/proc/check_vampire_upgrade()
- if (!mind.vampire)
+ var/datum/vampire/vampire = mind.antag_datums[MODE_VAMPIRE]
+ if(!vampire)
return
- var/datum/vampire/vampire = mind.vampire
-
for (var/datum/power/vampire/P in vampirepowers)
if (P.blood_cost <= vampire.blood_total)
if (!(P in vampire.purchased_powers))
@@ -50,7 +51,7 @@
if (!ishuman(src))
return
- var/datum/vampire/vampire = mind.vampire
+ var/datum/vampire/vampire = mind.antag_datums[MODE_VAMPIRE]
if (!vampire)
log_debug("[src] has a vampire power but is not a vampire.")
return
@@ -73,26 +74,30 @@
/mob/proc/vampire_can_affect_target(var/mob/living/carbon/human/T, var/notify = 1, var/account_loyalty_implant = 0, var/ignore_thrall = FALSE, var/affect_ipc = TRUE)
if (!T || !istype(T))
return FALSE
+ var/datum/vampire/vampire = mind.antag_datums[MODE_VAMPIRE]
// How did you even get here?
- if (!mind.vampire)
+ if (!vampire)
return FALSE
- if ((mind.vampire.status & VAMP_FULLPOWER) && !(T.mind && T.mind.vampire && (T.mind.vampire.status & VAMP_FULLPOWER)))
- return TRUE
- if (T.mind)
- if (T.mind.assigned_role == "Chaplain")
- if (notify)
+ var/datum/vampire/vampire_check
+ if(T.mind)
+ if(T.mind.assigned_role == "Chaplain")
+ if(notify)
to_chat(src, "Your connection with the Veil is not strong enough to affect a man as devout as them.")
return FALSE
- else if (T.mind.vampire && (!(T.mind.vampire.status & VAMP_ISTHRALL) || ((T.mind.vampire.status & VAMP_ISTHRALL) && !ignore_thrall)))
- if (notify)
- to_chat(src, "You lack the power required to affect another creature of the Veil.")
- return FALSE
-
+ vampire_check = T.mind.antag_datums[MODE_VAMPIRE]
+ if(vampire_check)
+ if(!(vampire_check.status & VAMP_ISTHRALL && ignore_thrall))
+ if(notify)
+ to_chat(src, "You lack the power required to affect another creature of the Veil.")
+ return FALSE
+ if(vampire_check)
+ if((vampire.status & VAMP_FULLPOWER) && !(vampire_check.status & VAMP_FULLPOWER))
+ return TRUE
if(!affect_ipc && isipc(T))
if (notify)
to_chat(src, SPAN_WARNING("You lack the power to interact with mechanical constructs."))
return FALSE
- if(is_special_character(T) && (!(T.mind.vampire.status & VAMP_ISTHRALL)))
+ if(is_special_character(T) && (!(vampire_check.status & VAMP_ISTHRALL)))
if (notify)
to_chat(src, "\The [T]'s mind is too strong to be affected by our powers!")
return FALSE
@@ -123,13 +128,14 @@
return
var/datum/vampire/thrall/thrall = new()
- mind.vampire = thrall
+ mind.antag_datums[MODE_VAMPIRE] = thrall
/mob/proc/vampire_check_frenzy(var/force_frenzy = 0)
- if (!mind || !mind.vampire)
+ if(!mind)
+ return
+ var/datum/vampire/vampire = mind.antag_datums[MODE_VAMPIRE]
+ if(!vampire)
return
-
- var/datum/vampire/vampire = mind.vampire
// Thralls don't frenzy.
if (vampire.status & VAMP_ISTHRALL)
return
@@ -176,10 +182,9 @@
vampire.last_frenzy_message = world.time
/mob/proc/vampire_start_frenzy(var/force_frenzy = 0)
- var/datum/vampire/vampire = mind.vampire
-
+ var/datum/vampire/vampire = mind.antag_datums[MODE_VAMPIRE]
if (vampire.status & VAMP_FRENZIED)
- return
+ return TRUE
var/probablity = force_frenzy ? 100 : vampire.frenzy * 0.5
@@ -194,12 +199,19 @@
sight |= SEE_MOBS
verbs += /mob/living/carbon/human/proc/grapple
+
+ return TRUE
+
+/mob/living/carbon/human/vampire_start_frenzy()
+ . = ..()
+ if(.)
+ update_body()
/mob/proc/vampire_stop_frenzy(var/force_stop = 0)
- var/datum/vampire/vampire = mind.vampire
+ var/datum/vampire/vampire = mind.antag_datums[MODE_VAMPIRE]
if (!(vampire.status & VAMP_FRENZIED))
- return
+ return TRUE
if (prob(force_stop ? 100 : vampire.blood_usable))
vampire.status &= ~VAMP_FRENZIED
@@ -214,60 +226,73 @@
verbs -= /mob/living/carbon/human/proc/grapple
regenerate_icons()
+
+ return TRUE
+
+/mob/living/carbon/human/vampire_stop_frenzy()
+ . = ..()
+ if(.)
+ update_body()
// Removes all vampire powers.
/mob/proc/remove_vampire_powers()
- if (!mind || !mind.vampire)
+ if(!mind)
return
-
- for (var/datum/power/vampire/P in mind.vampire.purchased_powers)
+ var/datum/vampire/vampire = mind.antag_datums[MODE_VAMPIRE]
+ if(!vampire)
+ return
+ for (var/datum/power/vampire/P in vampire.purchased_powers)
if (P.isVerb)
verbs -= P.verbpath
- if (mind.vampire.status & VAMP_FRENZIED)
+ if (vampire.status & VAMP_FRENZIED)
vampire_stop_frenzy(1)
/mob/proc/handle_vampire()
- if(mind.vampire.status & VAMP_ISTHRALL)
+ var/datum/vampire/vampire = mind.antag_datums[MODE_VAMPIRE]
+ if(vampire.status & VAMP_ISTHRALL)
return
// Apply frenzy while in the chapel.
if (istype(get_area(loc), /area/chapel))
- mind.vampire.frenzy += 3
+ vampire.frenzy += 3
- if (mind.vampire.blood_usable < 10)
- mind.vampire.frenzy += 2
- else if (mind.vampire.frenzy > 0)
- mind.vampire.frenzy = max(0, mind.vampire.frenzy -= Clamp(mind.vampire.blood_usable * 0.1, 1, 10))
+ if (vampire.blood_usable < 10)
+ vampire.frenzy += 2
+ else if (vampire.frenzy > 0)
+ vampire.frenzy = max(0, vampire.frenzy -= Clamp(vampire.blood_usable * 0.1, 1, 10))
- mind.vampire.frenzy = round(min(mind.vampire.frenzy, 450))
+ vampire.frenzy = round(min(vampire.frenzy, 450))
vampire_check_frenzy()
if(client)
- if(!mind.vampire.blood_hud)
- mind.vampire.blood_hud = new /obj/screen/vampire/blood()
- client.screen += mind.vampire.blood_hud
- if(!mind.vampire.frenzy_hud)
- mind.vampire.frenzy_hud = new /obj/screen/vampire/frenzy()
- client.screen += mind.vampire.frenzy_hud
- if(!mind.vampire.blood_suck_hud)
- mind.vampire.blood_suck_hud = new /obj/screen/vampire/suck()
- client.screen += mind.vampire.blood_suck_hud
+ if(!vampire.blood_hud)
+ vampire.blood_hud = new /obj/screen/vampire/blood()
+ client.screen += vampire.blood_hud
+ if(!vampire.frenzy_hud)
+ vampire.frenzy_hud = new /obj/screen/vampire/frenzy()
+ client.screen += vampire.frenzy_hud
+ if(!vampire.blood_suck_hud)
+ vampire.blood_suck_hud = new /obj/screen/vampire/suck()
+ client.screen += vampire.blood_suck_hud
- mind.vampire.blood_hud.maptext = SMALL_FONTS(7, mind.vampire.blood_usable)
- if(mind.vampire.frenzy)
- if(!mind.vampire.frenzy_hud.alpha)
- animate(mind.vampire.frenzy_hud, 1 SECOND, alpha = 255, LINEAR_EASING)
- mind.vampire.frenzy_hud.maptext = SMALL_FONTS(7, mind.vampire.frenzy)
+ vampire.blood_hud.maptext = SMALL_FONTS(7, vampire.blood_usable)
+ if(vampire.frenzy)
+ if(!vampire.frenzy_hud.alpha)
+ animate(vampire.frenzy_hud, 1 SECOND, alpha = 255, LINEAR_EASING)
+ vampire.frenzy_hud.maptext = SMALL_FONTS(7, vampire.frenzy)
else
- if(mind.vampire.frenzy_hud.alpha)
- animate(mind.vampire.frenzy_hud, 1 SECOND, alpha = 0, LINEAR_EASING)
- mind.vampire.frenzy_hud.maptext = null
+ if(vampire.frenzy_hud.alpha)
+ animate(vampire.frenzy_hud, 1 SECOND, alpha = 0, LINEAR_EASING)
+ vampire.frenzy_hud.maptext = null
/mob/living/carbon/human/proc/finish_vamp_timeout(vamp_flags = 0)
- if (!mind || !mind.vampire)
+ if(!mind)
return FALSE
- if (vamp_flags && !(mind.vampire.status & vamp_flags))
+ var/datum/vampire/vampire = mind.antag_datums[MODE_VAMPIRE]
+ if(!vampire)
+ return FALSE
+ if (vamp_flags && !(vampire.status & vamp_flags))
return FALSE
return TRUE
diff --git a/code/game/gamemodes/vampire/vampire_powers.dm b/code/game/gamemodes/vampire/vampire_powers.dm
index 58bc2c26f63..f23e47551a8 100644
--- a/code/game/gamemodes/vampire/vampire_powers.dm
+++ b/code/game/gamemodes/vampire/vampire_powers.dm
@@ -56,8 +56,8 @@
return
var/datum/vampire/draining_vamp = null
- if(T.mind?.vampire)
- draining_vamp = T.mind.vampire
+ if(T.mind)
+ draining_vamp = T.mind.antag_datums[MODE_VAMPIRE]
var/target_aware = !!T.client
@@ -85,7 +85,7 @@
playsound(src.loc, 'sound/effects/drain_blood_new.ogg', 50, 1)
while(do_mob(src, T, 50) && vampire.status & VAMP_DRAINING && vampire.blood_usable < VAMPIRE_MAX_USABLE_BLOOD)
- if(!mind.vampire)
+ if(!vampire)
to_chat(src, SPAN_DANGER("Your fangs have disappeared!"))
return
@@ -342,7 +342,7 @@
var/list/victims = list()
for(var/mob/living/carbon/human/T in hearers(4, src) - src)
- if(istype(T) && (T.l_ear || T.r_ear) && istype((T.l_ear || T.r_ear), /obj/item/clothing/ears/earmuffs))
+ if(T.protected_from_sound())
continue
if(!vampire_can_affect_target(T, 0))
continue
@@ -498,7 +498,8 @@
last_valid_turf = get_turf(owner.loc)
owner.forceMove(src)
- if(owner.mind.vampire.status & VAMP_FULLPOWER)
+ var/datum/vampire/vampire = owner_mob.mind.antag_datums[MODE_VAMPIRE]
+ if(vampire.status & VAMP_FULLPOWER)
for(var/obj/item/grab/G in list(owner.l_hand, owner.r_hand))
G.affecting.vampire_phase_out(get_turf(G))
G.affecting.forceMove(src)
@@ -748,9 +749,9 @@
to_chat(T, SPAN_DANGER("Your mind blanks as you finish feeding from [src]'s wrist."))
thralls.add_antagonist(T.mind, 1, 1, 0, 1, 1)
- T.mind.vampire.master = src
- vampire.thralls += T
- to_chat(T, SPAN_NOTICE("You have been forced into a blood bond by [T.mind.vampire.master], and are thus their thrall. While a thrall may feel a myriad of emotions towards their master, ranging from fear, to hate, to love; the supernatural bond between them still forces the thrall to obey their master, and to listen to the master's commands.
You must obey your master's orders, you must protect them, you cannot harm them."))
+ var/datum/vampire/T_vampire = T.mind.antag_datums[MODE_VAMPIRE]
+ T_vampire.assign_master(T, src, vampire)
+ to_chat(T, SPAN_NOTICE("You have been forced into a blood bond by [T_vampire.master], and are thus their thrall. While a thrall may feel a myriad of emotions towards their master, ranging from fear, to hate, to love; the supernatural bond between them still forces the thrall to obey their master, and to listen to the master's commands.
You must obey your master's orders, you must protect them, you cannot harm them."))
to_chat(src, SPAN_NOTICE("You have completed the thralling process. They are now your slave and will obey your commands."))
admin_attack_log(src, T, "enthralled [key_name(T)]", "was enthralled by [key_name(src)]", "successfully enthralled")
@@ -883,30 +884,30 @@
to_chat(src, SPAN_WARNING("Your fangs are already sunk into a victim's neck!"))
return
- if(T.mind.vampire)
- var/datum/vampire/draining_vamp = T.mind.vampire
+ if(T.mind)
+ var/datum/vampire/draining_vamp = T.mind.antag_datums[MODE_VAMPIRE]
+ if(draining_vamp)
+ if(draining_vamp.status & VAMP_ISTHRALL)
+ var/choice_text = ""
+ var/denial_response = ""
+ if(draining_vamp.master == src)
+ choice_text = "[T] is your thrall. Do you wish to release them from the blood bond and give them the chance to become your equal?"
+ denial_response = "You opt against giving [T] a chance to ascend, and choose to keep them as a servant."
+ else
+ choice_text = "You can feel the taint of another master running in the veins of [T]. Do you wish to release them of their blood bond, and convert them into a vampire, in spite of their master?"
+ denial_response = "You choose not to continue with the Embrace, and permit [T] to keep serving their master."
- if(draining_vamp.status & VAMP_ISTHRALL)
- var/choice_text = ""
- var/denial_response = ""
- if(draining_vamp.master == src)
- choice_text = "[T] is your thrall. Do you wish to release them from the blood bond and give them the chance to become your equal?"
- denial_response = "You opt against giving [T] a chance to ascend, and choose to keep them as a servant."
+ if(alert(src, choice_text, "Choices", "Yes", "No") == "No")
+ to_chat(src, SPAN_NOTICE("[denial_response]"))
+ return
+
+ thralls.remove_antagonist(T.mind, 0, 0)
+ qdel(draining_vamp)
+ draining_vamp = null
else
- choice_text = "You can feel the taint of another master running in the veins of [T]. Do you wish to release them of their blood bond, and convert them into a vampire, in spite of their master?"
- denial_response = "You choose not to continue with the Embrace, and permit [T] to keep serving their master."
-
- if(alert(src, choice_text, "Choices", "Yes", "No") == "No")
- to_chat(src, SPAN_NOTICE("[denial_response]"))
+ to_chat(src, SPAN_WARNING("You feel corruption running in [T]'s blood. Much like yourself, [T.get_pronoun("he")] is already a spawn of the Veil, and cannot be Embraced."))
return
- thralls.remove_antagonist(T.mind, 0, 0)
- qdel(draining_vamp)
- draining_vamp = null
- else
- to_chat(src, SPAN_WARNING("You feel corruption running in [T]'s blood. Much like yourself, [T.get_pronoun("he")] is already a spawn of the Veil, and cannot be Embraced."))
- return
-
vampire.status |= VAMP_DRAINING
visible_message(SPAN_DANGER("[src] bites [T]'s neck!"), SPAN_DANGER("You bite [T]'s neck and begin to drain their blood, as the first step of introducing the corruption of the Veil to them."), SPAN_NOTICE("You hear a soft puncture and a wet sucking noise."))
@@ -915,7 +916,7 @@
var/drained_all_blood = FALSE
while(do_mob(src, T, 50))
- if(!mind.vampire)
+ if(!vampire)
to_chat(src, SPAN_WARNING("Your fangs have disappeared!"))
return
if(!T.vessel.get_reagent_amount(/datum/reagent/blood))
@@ -948,8 +949,9 @@
to_chat(T, SPAN_DANGER("You awaken. Moments ago, you were dead, your conciousness still forced stuck inside your body. Now you live. You feel different, a strange, dark force now present within you. You have an insatiable desire to drain the blood of mortals, and to grow in power."))
to_chat(src, SPAN_WARNING("You have corrupted another mortal with the taint of the Veil. Beware: they will awaken hungry and maddened; not bound to any master."))
- T.mind.vampire.blood_usable = 0
- T.mind.vampire.frenzy = 250
+ var/datum/vampire/T_vampire = T.mind.antag_datums[MODE_VAMPIRE]
+ T_vampire.blood_usable = 0
+ T_vampire.frenzy = 250
T.vampire_check_frenzy()
vampire.status &= ~VAMP_DRAINING
@@ -1016,4 +1018,4 @@
G.synch()
verbs -= /mob/living/carbon/human/proc/grapple
- ADD_VERB_IN_IF(src, 800, /mob/living/carbon/human/proc/grapple, CALLBACK(src, .proc/finish_vamp_timeout, VAMP_FRENZIED))
\ No newline at end of file
+ ADD_VERB_IN_IF(src, 800, /mob/living/carbon/human/proc/grapple, CALLBACK(src, .proc/finish_vamp_timeout, VAMP_FRENZIED))
diff --git a/code/game/jobs/job/science.dm b/code/game/jobs/job/science.dm
index 91a89136ffb..d029ccecce3 100644
--- a/code/game/jobs/job/science.dm
+++ b/code/game/jobs/job/science.dm
@@ -157,9 +157,9 @@
shoes = /obj/item/clothing/shoes/black
headset = /obj/item/device/radio/headset/headset_sci
bowman = /obj/item/device/radio/headset/headset_sci/alt
- tab_pda = /obj/item/modular_computer/handheld/pda/research
- wristbound = /obj/item/modular_computer/handheld/wristbound/preset/pda/research
- tablet = /obj/item/modular_computer/handheld/preset/research
+ tab_pda = /obj/item/modular_computer/handheld/pda/research/robotics
+ wristbound = /obj/item/modular_computer/handheld/wristbound/preset/advanced/research/robotics
+ tablet = /obj/item/modular_computer/handheld/preset/research/robotics
id = /obj/item/card/id/white
belt = /obj/item/storage/belt/utility
diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm
index 87e0dc99e4c..2e628452aa1 100644
--- a/code/game/machinery/atmoalter/canister.dm
+++ b/code/game/machinery/atmoalter/canister.dm
@@ -329,7 +329,7 @@ update_flag
..()
update_icon()
- SSnanoui.update_uis(src) // Update all NanoUIs attached to src
+ SSvueui.check_uis_for_change(src) // Update all VueUIs attached to src
/obj/machinery/portable_atmospherics/canister/attack_ai(var/mob/user as mob)
if(!ai_can_interact(user))
@@ -339,37 +339,36 @@ update_flag
/obj/machinery/portable_atmospherics/canister/attack_hand(var/mob/user as mob)
return src.ui_interact(user)
-/obj/machinery/portable_atmospherics/canister/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
- if (src.destroyed)
+/obj/machinery/portable_atmospherics/canister/ui_interact(mob/user)
+ if(src.destroyed)
return
-
- // this is the data which will be sent to the ui
- var/data[0]
- data["name"] = name
- data["canLabel"] = can_label ? 1 : 0
- data["portConnected"] = connected_port ? 1 : 0
- data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0)
- data["releasePressure"] = round(release_pressure ? release_pressure : 0)
- data["minReleasePressure"] = round(ONE_ATMOSPHERE/10)
- data["maxReleasePressure"] = round(10*ONE_ATMOSPHERE)
- data["valveOpen"] = valve_open ? 1 : 0
-
- data["hasHoldingTank"] = holding ? 1 : 0
- if (holding)
- data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure()))
-
// update the ui if it exists, returns null if no ui is passed/found
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
+ var/datum/vueui/ui = SSvueui.get_open_ui(user, src)
if (!ui)
// the ui does not exist, so we'll create a new() one
- // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
- ui = new(user, src, ui_key, "canister.tmpl", "Canister", 480, 400)
- // when the ui is first opened this is the data it will use
- ui.set_initial_data(data)
+ ui = new(user, src, "machinery-atmospherics-canister", 480, 400, "Canister", state = interactive_state)
// open the new ui window
ui.open()
// auto update every Master Controller tick
- ui.set_auto_update(1)
+ ui.auto_update_content = TRUE
+
+/obj/machinery/portable_atmospherics/canister/vueui_data_change(list/data, mob/user, datum/vueui/ui)
+ data = ..() || list()
+
+ // this is the data which will be sent to the ui
+ data["name"] = name
+ data["canLabel"] = can_label
+ data["portConnected"] = !!connected_port
+ data["tankPressure"] = round(air_contents.return_pressure() || 0)
+ data["releasePressure"] = round(release_pressure || 0)
+ data["minReleasePressure"] = round(ONE_ATMOSPHERE/10)
+ data["maxReleasePressure"] = round(10*ONE_ATMOSPHERE)
+ data["valveOpen"] = valve_open
+
+ data["hasHoldingTank"] = !!holding
+ if (holding)
+ data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure()))
+ return data
/obj/machinery/portable_atmospherics/canister/Topic(href, href_list)
@@ -379,19 +378,20 @@ update_flag
return 0
if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) // exploit protection -walter0o
- usr << browse(null, "window=canister")
+ var/datum/vueui/ui = href_list["vueui"]
+ ui?.close()
onclose(usr, "canister")
return
if(href_list["toggle"])
if (valve_open)
if (holding)
- release_log += "Valve was closed by [usr] ([usr.ckey]), stopping the transfer into the [holding] "
+ release_log += "Valve was closed by [usr] ([usr.ckey]), stopping the transfer into [holding] "
else
release_log += "Valve was closed by [usr] ([usr.ckey]), stopping the transfer into the air "
else
if (holding)
- release_log += "Valve was opened by [usr] ([usr.ckey]), starting the transfer into the [holding] "
+ release_log += "Valve was opened by [usr] ([usr.ckey]), starting the transfer into [holding] "
else
release_log += "Valve was opened by [usr] ([usr.ckey]), starting the transfer into the air "
log_open()
@@ -401,18 +401,14 @@ update_flag
if(holding)
if (valve_open)
valve_open = 0
- release_log += "Valve was closed by [usr] ([usr.ckey]), stopping the transfer into the [holding] "
+ release_log += "Valve was closed by [usr] ([usr.ckey]), stopping the transfer into [holding] "
if(istype(holding, /obj/item/tank))
holding.manipulated_by = usr.real_name
usr.put_in_hands(holding)
holding = null
- if (href_list["pressure_adj"])
- var/diff = text2num(href_list["pressure_adj"])
- if(diff > 0)
- release_pressure = min(10*ONE_ATMOSPHERE, release_pressure+diff)
- else
- release_pressure = max(ONE_ATMOSPHERE/10, release_pressure+diff)
+ if (href_list["pressure_set"])
+ release_pressure = between(ONE_ATMOSPHERE/10, text2num(href_list["pressure_set"]), 10*ONE_ATMOSPHERE)
if (href_list["relabel"])
if (can_label)
diff --git a/code/game/machinery/bioprinter.dm b/code/game/machinery/bioprinter.dm
index 95d757fddec..9d1870a2ae0 100644
--- a/code/game/machinery/bioprinter.dm
+++ b/code/game/machinery/bioprinter.dm
@@ -41,7 +41,7 @@
var/obj/item/organ/O = new new_organ(get_turf(src))
if(prints_prosthetics)
- O.robotic = 2
+ O.robotic = ROBOTIC_MECHANICAL
else if(loaded_dna)
visible_message("The printer injects the stored DNA into the biomass..")
O.transplant_data = list()
diff --git a/code/game/machinery/body_scanner.dm b/code/game/machinery/body_scanner.dm
index a41eab9c9ec..8d19315ae8b 100644
--- a/code/game/machinery/body_scanner.dm
+++ b/code/game/machinery/body_scanner.dm
@@ -753,9 +753,9 @@
for(var/obj/item/organ/internal/i in occ["internal_organs"])
var/mech = ""
- if(i.robotic == 1)
+ if(i.robotic == ROBOTIC_ASSISTED)
mech = "Assisted:"
- if(i.robotic == 2)
+ if(i.robotic == ROBOTIC_MECHANICAL)
mech = "Mechanical:"
var/infection = get_infection_level(i.germ_level)
diff --git a/code/game/machinery/buttons.dm b/code/game/machinery/buttons.dm
index 213a9c1a162..fac84c244e0 100644
--- a/code/game/machinery/buttons.dm
+++ b/code/game/machinery/buttons.dm
@@ -65,6 +65,9 @@
/obj/machinery/button/switch/update_icon()
icon_state = "light[active]"
+/obj/machinery/button/switch/attack_hand()
+ playsound(src, /decl/sound_category/switch_sound, 30)
+
//alternate button with the same functionality, except has a door control sprite instead
/obj/machinery/button/alternate
icon = 'icons/obj/stationobjs.dmi'
diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm
index c9fd1c146a0..0ad3ab515e1 100644
--- a/code/game/machinery/camera/camera.dm
+++ b/code/game/machinery/camera/camera.dm
@@ -207,7 +207,7 @@
if (istype(W, /obj/item)) //is it even possible to get into attackby() with non-items?
var/obj/item/I = W
if (I.hitsound)
- playsound(loc, I.hitsound, 50, 1, -1)
+ playsound(loc, I.hitsound, I.get_clamped_volume(), 1, -1)
take_damage(W.force)
else
@@ -450,4 +450,3 @@
wires.MendAll()
update_icon()
update_coverage()
-
diff --git a/code/game/machinery/deployable.dm b/code/game/machinery/deployable.dm
index 6682cca4dae..49ff1a159d6 100644
--- a/code/game/machinery/deployable.dm
+++ b/code/game/machinery/deployable.dm
@@ -106,7 +106,7 @@ for reference:
src.health -= W.force * 0.75
else
animate_shake()
- playsound(src.loc, material.hitsound, 100, 1)
+ playsound(src.loc, material.hitsound, W.get_clamped_volume(), 1)
if (src.health <= 0)
visible_message("The barricade is smashed apart!")
dismantle()
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index 4872019393e..3d417ab4585 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -152,7 +152,7 @@
var/mob/M = AM
if(world.time - M.last_bumped <= 10) return //Can bump-open one airlock per second. This is to prevent shock spam.
M.last_bumped = world.time
- if(!M.restrained() && (!issmall(M) || ishuman(M)))
+ if(!M.restrained() && (!issmall(M) || ishuman(M) || istype(M, /mob/living/silicon/robot/drone/mining)))
bumpopen(M)
return
@@ -365,7 +365,7 @@
user.visible_message("\The [user] hits \the [src] with \the [W] with no visible effect.")
else
user.visible_message("\The [user] forcefully strikes \the [src] with \the [W]!")
- playsound(src.loc, hitsound, 100, 1)
+ playsound(src.loc, hitsound, W.get_clamped_volume(), 1)
take_damage(W.force)
return
diff --git a/code/game/machinery/holosign.dm b/code/game/machinery/holosign.dm
index 6e7701de2b5..22a5618f817 100644
--- a/code/game/machinery/holosign.dm
+++ b/code/game/machinery/holosign.dm
@@ -51,16 +51,13 @@
on_icon = "surgery"
////////////////////SWITCH///////////////////////////////////////
-/obj/machinery/button/holosign
+/obj/machinery/button/switch/holosign
name = "holosign switch"
desc = "A remote control switch for holosign."
icon = 'icons/obj/power.dmi'
icon_state = "holosign_switch"
-/obj/machinery/button/holosign/update_icon()
- return
-
-/obj/machinery/button/holosign/attack_hand(mob/user as mob)
+/obj/machinery/button/switch/holosign/attack_hand(mob/user as mob)
if(..())
return
add_fingerprint(user)
@@ -68,8 +65,7 @@
use_power(5)
active = !active
- icon_state = "light[active]"
-
+ update_icon()
for(var/obj/machinery/holosign/M in SSmachinery.all_machines)
if (M.id == src.id)
INVOKE_ASYNC(M, /obj/machinery/holosign/proc/toggle)
diff --git a/code/game/machinery/jukebox.dm b/code/game/machinery/jukebox.dm
index 4e328b42e96..f393b6d8fca 100644
--- a/code/game/machinery/jukebox.dm
+++ b/code/game/machinery/jukebox.dm
@@ -95,7 +95,7 @@ datum/track/New(var/title_name, var/audio)
for(var/mob/living/carbon/M in ohearers(6, src))
if(istype(M, /mob/living/carbon/human))
var/mob/living/carbon/human/H = M
- if(istype(H.l_ear, /obj/item/clothing/ears/earmuffs) || istype(H.r_ear, /obj/item/clothing/ears/earmuffs))
+ if(H.protected_from_sound())
continue
M.sleeping = 0
M.stuttering += 20
diff --git a/code/game/machinery/lightswitch.dm b/code/game/machinery/lightswitch.dm
index 4a180b4f845..96f50b3e377 100644
--- a/code/game/machinery/lightswitch.dm
+++ b/code/game/machinery/lightswitch.dm
@@ -30,7 +30,7 @@
/obj/machinery/light_switch/update_icon()
cut_overlays()
if(!(stat & NOPOWER))
- holographic_overlay(src, icon, "light_switch[on]-overlay")
+ holographic_overlay(src, icon, "light[on]-overlay")
if (!light_range || light_color != on ? "#82ff4c" : "#f86060")
set_light(2, 0.3, on ? "#82ff4c" : "#f86060")
else if (light_range)
diff --git a/code/game/machinery/rechargestation.dm b/code/game/machinery/rechargestation.dm
index 5aaebfd54ed..70f9dc7566d 100644
--- a/code/game/machinery/rechargestation.dm
+++ b/code/game/machinery/rechargestation.dm
@@ -76,40 +76,50 @@
return ..()
if(!has_cell_power())
- return 0
+ return FALSE
if(src.use_power == 1)
cell.use(idle_power_usage * CELLRATE)
else if(src.use_power >= 2)
cell.use(active_power_usage * CELLRATE)
- return 1
+ return TRUE
//Processes the occupant, drawing from the internal power cell if needed.
/obj/machinery/recharge_station/proc/process_occupant()
+ if(!isrobot(occupant) && !ishuman(occupant))
+ return
+
+ var/obj/item/cell/target
if(isrobot(occupant))
var/mob/living/silicon/robot/R = occupant
if(R.module)
R.module.respawn_consumable(R, charging_power * CELLRATE / 250) //consumables are magical, apparently
- if(R.cell && !R.cell.fully_charged())
- var/diff = min(R.cell.maxcharge - R.cell.charge, charging_power * CELLRATE) // Capped by charging_power / tick
- var/charge_used = cell.use(diff)
- R.cell.give(charge_used*charging_efficiency)
+ target = R.cell
//Lastly, attempt to repair the cyborg if enabled
if(weld_rate && R.getBruteLoss() && cell.checked_use(weld_power_use * weld_rate * CELLRATE))
R.adjustBruteLoss(-weld_rate)
if(wire_rate && R.getFireLoss() && cell.checked_use(wire_power_use * wire_rate * CELLRATE))
R.adjustFireLoss(-wire_rate)
- else if(ishuman(occupant))
- var/mob/living/carbon/human/H = occupant
- if(!isnull(H.internal_organs_by_name[BP_CELL]) && H.nutrition < H.max_nutrition)
- H.adjustNutritionLoss(-10)
- cell.use(7000/H.max_nutrition*10)
+ if(ishuman(occupant))
+ var/mob/living/carbon/human/H = occupant
+ var/obj/item/organ/internal/cell/IC = H.internal_organs_by_name[BP_CELL]
+ if(IC)
+ target = IC.cell
+ if((!target || target.percent() > 95) && istype(H.back, /obj/item/rig))
+ var/obj/item/rig/R = H.back
+ if(R.cell && !R.cell.fully_charged())
+ target = R.cell
+
+ if(target && !target.fully_charged())
+ var/diff = min(target.maxcharge - target.charge, charging_power * CELLRATE) // Capped by charging_power / tick
+ var/charge_used = cell.use(diff)
+ target.give(charge_used*charging_efficiency)
/obj/machinery/recharge_station/examine(mob/user)
..(user)
- to_chat(user, "The charge meter reads: [round(chargepercentage())]%")
+ to_chat(user, "The charge meter reads: [round(chargepercentage())]%.")
/obj/machinery/recharge_station/proc/chargepercentage()
if(!cell)
@@ -120,7 +130,6 @@
if(user.stat)
return
go_out()
- return
/obj/machinery/recharge_station/emp_act(severity)
if(occupant)
@@ -220,12 +229,16 @@
if(isrobot(M))
var/mob/living/silicon/robot/R = M
if(R.cell)
- return 1
+ return TRUE
if(ishuman(M))
var/mob/living/carbon/human/H = M
if(!isnull(H.internal_organs_by_name[BP_CELL]))
- return 1
- return 0
+ return TRUE
+ if(istype(H.back, /obj/item/rig))
+ var/obj/item/rig/R = H.back
+ if(R.get_cell())
+ return TRUE
+ return FALSE
/obj/machinery/recharge_station/proc/go_out()
if(!occupant)
diff --git a/code/game/machinery/vending_types.dm b/code/game/machinery/vending_types.dm
index d1beea37f7c..b68899065de 100644
--- a/code/game/machinery/vending_types.dm
+++ b/code/game/machinery/vending_types.dm
@@ -456,7 +456,7 @@
/obj/item/reagent_containers/glass/bottle/antitoxin = 4,
/obj/item/reagent_containers/glass/bottle/inaprovaline = 4,
/obj/item/reagent_containers/glass/bottle/perconol = 3,
- /obj/item/reagent_containers/glass/bottle/toxin = 4,
+ /obj/item/reagent_containers/glass/bottle/toxin = 1,
/obj/item/reagent_containers/glass/bottle/coughsyrup = 4,
/obj/item/reagent_containers/syringe = 12,
/obj/item/device/healthanalyzer = 5,
diff --git a/code/game/objects/effects/chem/foam.dm b/code/game/objects/effects/chem/foam.dm
index bbdd55ab807..cc3c2d821a0 100644
--- a/code/game/objects/effects/chem/foam.dm
+++ b/code/game/objects/effects/chem/foam.dm
@@ -11,6 +11,7 @@
layer = OBJ_LAYER + 0.9
mouse_opacity = 0
animate_movement = 0
+ var/solid_time = 120
var/amount = 3
var/expand = 1
var/metal = 0
@@ -21,7 +22,7 @@
metal = ismetal
playsound(src, 'sound/effects/bubbles2.ogg', 80, 1, -3)
addtimer(CALLBACK(src, .proc/tick), 3 + metal * 3)
- addtimer(CALLBACK(src, .proc/post), 120)
+ addtimer(CALLBACK(src, .proc/post), solid_time)
/obj/effect/effect/foam/proc/tick()
process()
@@ -81,6 +82,13 @@
var/mob/living/M = AM
M.slip("the foam", 6)
+/obj/effect/effect/foam/spray
+ amount = 1 // only spread a little bit
+ solid_time = 3 SECONDS
+
+/obj/effect/effect/foam/spray/initial
+ amount = 0 // don't spread
+
/datum/effect/effect/system/foam_spread
var/amount = 5 // the size of the foam spread.
var/list/carried_reagents // the IDs of reagents present when the foam was mixed
@@ -157,27 +165,72 @@
qdel(src)
/obj/structure/foamedmetal/attack_hand(var/mob/user)
+ user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ user.do_attack_animation(src, FIST_ATTACK_ANIMATION)
if ((HULK in user.mutations) || (prob(75 - metal * 25)))
- user.visible_message("[user] smashes through the foamed metal.", "You smash through the metal foam wall.")
+ user.visible_message(SPAN_WARNING("[user] smashes through the foamed metal."), SPAN_NOTICE("You smash through the metal foam wall."))
qdel(src)
else
- to_chat(user, "You hit the metal foam but bounce off it.")
- return
+ to_chat(user, SPAN_NOTICE("You hit the metal foam but bounce off it."))
+ animate_shake()
/obj/structure/foamedmetal/attackby(var/obj/item/I, var/mob/user)
+ user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
if(istype(I, /obj/item/grab))
var/obj/item/grab/G = I
+ if(G.state < GRAB_AGGRESSIVE)
+ to_chat(user, SPAN_WARNING("You need a stronger grip to do that!"))
+ return
G.affecting.forceMove(src.loc)
- visible_message("[G.assailant] smashes [G.affecting] through the foamed metal wall.")
+ visible_message(SPAN_WARNING("[G.assailant] smashes [G.affecting] through the foamed metal wall."))
+ G.affecting.take_overall_damage(15)
qdel(I)
qdel(src)
return
+ else if(istype(I, /obj/item/stack/material))
+ var/obj/item/stack/material/S = I
+ if(S.get_amount() < 4)
+ to_chat(user, SPAN_NOTICE("There isn't enough material here to construct a wall."))
+ return
+
+ var/material/M = SSmaterials.get_material_by_name(S.default_type)
+ if(!istype(M))
+ return
+ if(M.integrity < 50)
+ to_chat(user, SPAN_NOTICE("This material is too soft for use in wall construction."))
+ return
+ user.visible_message("[user] starts slotting material into \the [src]...", SPAN_NOTICE("You start slotting material into \the [src], forming it into a wall..."))
+ if(!do_after(user, 10 SECONDS) || !S.use(4))
+ return
+ var/turf/Tsrc = get_turf(src)
+ var/original_type = Tsrc.type
+ Tsrc.ChangeTurf(/turf/simulated/wall)
+ var/turf/simulated/wall/T = Tsrc
+ T.under_turf = original_type
+ T.set_material(M)
+ T.add_hiddenprint(usr)
+ qdel(src)
+ return
+
+ else if(istype(I, /obj/item/stack/tile/floor))
+ var/turf/T = get_turf(src)
+ if(T.type != /turf/space && !isopenturf(T)) // need to do a hard check here because transit turfs are also space turfs
+ to_chat(user, SPAN_WARNING("The tile below \the [src] isn't an open space, or space itself!"))
+ return
+ var/obj/item/stack/tile/floor/S = I
+ S.use(1)
+ T.ChangeTurf(/turf/simulated/floor/airless)
+ qdel(src)
+ return
+
+ user.do_attack_animation(src, I)
if(prob(I.force * 20 - metal * 25))
- user.visible_message("[user] smashes through the foamed metal.", "You smash through the foamed metal with \the [I].")
+ user.visible_message(SPAN_WARNING("[user] smashes through the foamed metal."), SPAN_NOTICE("You smash through the foamed metal with \the [I]."))
qdel(src)
else
- to_chat(user, "You hit the metal foam to no effect.")
+ to_chat(user, SPAN_NOTICE("You hit the metal foam to no effect."))
+ animate_shake()
/obj/structure/foamedmetal/CanPass(atom/movable/mover, turf/target, height=1.5, air_group = 0)
if(air_group)
diff --git a/code/game/objects/effects/decals/Cleanable/fuel.dm b/code/game/objects/effects/decals/Cleanable/fuel.dm
index 9b4900c1ff6..dcf3fee2682 100644
--- a/code/game/objects/effects/decals/Cleanable/fuel.dm
+++ b/code/game/objects/effects/decals/Cleanable/fuel.dm
@@ -46,7 +46,6 @@
amount *= 0.75
/obj/effect/decal/cleanable/liquid_fuel/flamethrower_fuel
- icon_state = "mustard"
anchored = 0
/obj/effect/decal/cleanable/liquid_fuel/flamethrower_fuel/Initialize(mapload, amt = 1, d = 0)
@@ -55,17 +54,17 @@
/obj/effect/decal/cleanable/liquid_fuel/flamethrower_fuel/Spread()
//The spread for flamethrower fuel is much more precise, to create a wide fire pattern.
- if(amount < 0.1) return
+ if(amount < 1) return
var/turf/simulated/S = loc
if(!istype(S)) return
- for(var/d in list(turn(dir,90),turn(dir,-90), dir))
+ for(var/d in list(turn(dir, 45), turn(dir, -45), dir))
var/turf/simulated/O = get_step(S,d)
if(locate(/obj/effect/decal/cleanable/liquid_fuel/flamethrower_fuel) in O)
continue
if(O.CanPass(null, S, 0, 0) && S.CanPass(null, O, 0, 0))
- new/obj/effect/decal/cleanable/liquid_fuel/flamethrower_fuel(O,amount*0.25,d)
- O.hotspot_expose((T20C*2) + 380,500) //Light flamethrower fuel on fire immediately.
+ new/obj/effect/decal/cleanable/liquid_fuel/flamethrower_fuel(O,amount * 0.25,d)
+ O.hotspot_expose((T20C*2) + 400, 500) //Light flamethrower fuel on fire immediately.
amount *= 0.25
@@ -153,4 +152,4 @@
other_foam.Spread(exclude)
else
new/obj/effect/decal/cleanable/foam(target, amount*0.25,1)
- amount *= 0.75
\ No newline at end of file
+ amount *= 0.75
diff --git a/code/game/objects/effects/decals/Cleanable/humans.dm b/code/game/objects/effects/decals/Cleanable/humans.dm
index f12efa31123..a3a9de78022 100644
--- a/code/game/objects/effects/decals/Cleanable/humans.dm
+++ b/code/game/objects/effects/decals/Cleanable/humans.dm
@@ -110,7 +110,7 @@
perp.update_inv_shoes(1)
amount--
- if(amount > 2 && prob(perp.slip_chance(perp.m_intent == "run" ? 20 : 5)))
+ if(amount > 2 && prob(perp.slip_chance(perp.m_intent == M_RUN ? 20 : 5)))
perp.slip(src, 4)
/obj/effect/decal/cleanable/blood/proc/dry()
diff --git a/code/game/objects/effects/decals/Cleanable/misc.dm b/code/game/objects/effects/decals/Cleanable/misc.dm
index 1b3f7f5390d..84af8110334 100644
--- a/code/game/objects/effects/decals/Cleanable/misc.dm
+++ b/code/game/objects/effects/decals/Cleanable/misc.dm
@@ -138,4 +138,18 @@
anchored = TRUE
icon = 'icons/effects/blood.dmi'
icon_state = "mfloor1"
- random_icon_states = list("mfloor1", "mfloor2", "mfloor3", "mfloor4", "mfloor5", "mfloor6", "mfloor7")
\ No newline at end of file
+ random_icon_states = list("mfloor1", "mfloor2", "mfloor3", "mfloor4", "mfloor5", "mfloor6", "mfloor7")
+
+/obj/effect/decal/cleanable/confetti
+ name = "confetti"
+ desc = "Tiny bits of colored paper thrown about for the janitor to enjoy!"
+ gender = PLURAL
+ density = FALSE
+ anchored = TRUE
+ icon = 'icons/effects/effects.dmi'
+ icon_state = "confetti"
+
+/obj/effect/decal/cleanable/confetti/attack_hand(mob/user)
+ to_chat(user, SPAN_NOTICE("You start to meticulously pick up the confetti."))
+ if(do_after(user, 60))
+ qdel(src)
diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm
index bce5331e928..55f32745cd1 100644
--- a/code/game/objects/effects/spiders.dm
+++ b/code/game/objects/effects/spiders.dm
@@ -79,6 +79,7 @@
name = "egg cluster"
desc = "They seem to pulse slightly with an inner life."
icon_state = "eggs"
+ health = 10
var/amount_grown = 0
var/last_itch = 0
@@ -119,6 +120,14 @@
last_itch = world.time
to_chat(O.owner, "Your [O.name] itches.")
+/obj/effect/spider/eggcluster/proc/take_damage(var/damage)
+ health -= damage
+ if(health <= 0)
+ var/obj/item/organ/external/O = loc
+ if(istype(O) && O.owner)
+ to_chat(O.owner, SPAN_WARNING("You feel something dissolve in your [O.name]..."))
+ qdel(src)
+
/obj/effect/spider/spiderling
name = "spiderling"
desc = "It never stays still for long."
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index fe2563c8172..d606eb28ede 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -444,7 +444,7 @@ var/list/global/slot_flags_enumeration = list(
if(!istype(src, /obj/item/handcuffs))
return 0
if(slot_legcuffed)
- if(!istype(src, /obj/item/legcuffs))
+ if(!istype(src, /obj/item/handcuffs))
return 0
if(slot_in_backpack) //used entirely for equipping spawned mobs or at round start
var/allow = 0
diff --git a/code/game/objects/items/devices/flash.dm b/code/game/objects/items/devices/flash.dm
index 4518a8b4ce1..de62db7b6cb 100644
--- a/code/game/objects/items/devices/flash.dm
+++ b/code/game/objects/items/devices/flash.dm
@@ -178,7 +178,8 @@
return
/obj/item/device/flash/emp_act(severity)
- if(broken) return
+ if(broken)
+ return
flash_recharge()
switch(times_used)
if(0 to 5)
@@ -187,13 +188,10 @@
icon_state = "flashburnt"
return
times_used++
- if(istype(loc, /mob/living/carbon))
+ if(iscarbon(loc))
var/mob/living/carbon/M = loc
- var/safety = M.eyecheck(TRUE)
- if(safety < FLASH_PROTECTION_MODERATE)
- M.flash_eyes()
- for(var/mob/O in viewers(M, null))
- O.show_message("[M] is blinded by the flash!")
+ M.flash_eyes()
+ to_chat(M, SPAN_WARNING("Your [name] goes off!"))
..()
/obj/item/device/flash/synthetic
diff --git a/code/game/objects/items/devices/lighting/flare.dm b/code/game/objects/items/devices/lighting/flare.dm
index 25cce20b6f1..e4cdb850f1e 100644
--- a/code/game/objects/items/devices/lighting/flare.dm
+++ b/code/game/objects/items/devices/lighting/flare.dm
@@ -55,4 +55,69 @@
user.visible_message(SPAN_NOTICE("\The [user] activates the flare."), SPAN_NOTICE("You pull the cord on the flare, activating it!"))
src.force = on_damage
src.damtype = "fire"
- START_PROCESSING(SSprocessing, src)
\ No newline at end of file
+ START_PROCESSING(SSprocessing, src)
+
+/obj/item/device/flashlight/flare/torch
+ name = "torch"
+ desc = "A rustic source of light."
+ w_class = ITEMSIZE_LARGE
+ brightness_on = 2
+ light_power = 3
+ light_color = LIGHT_COLOR_FIRE
+ icon_state = "torch"
+ item_state = "torch"
+ uv_intensity = 40
+ produce_heat = 1000
+ on_damage = 10
+
+ drop_sound = 'sound/items/drop/woodweapon.ogg'
+ pickup_sound = 'sound/items/pickup/woodweapon.ogg'
+
+/obj/item/device/flashlight/flare/torch/attack_self(mob/user)
+ return
+
+/obj/item/device/flashlight/flare/torch/update_icon()
+ ..()
+ item_state = icon_state
+ if(ismob(src.loc))
+ var/mob/H = src.loc
+ H.update_inv_l_hand()
+ H.update_inv_r_hand()
+
+
+/obj/item/device/flashlight/flare/torch/proc/light(mob/user)
+ user.visible_message(SPAN_NOTICE("\The [user] lights \the [src]."), SPAN_NOTICE("You light \the [src]."))
+ force = on_damage
+ damtype = "fire"
+ on = TRUE
+ START_PROCESSING(SSprocessing, src)
+ update_icon()
+
+/obj/item/device/flashlight/flare/torch/attackby(var/obj/item/W, mob/user)
+ if(W.isFlameSource() && !on && fuel)
+ light(user)
+ else
+ ..()
+
+/obj/item/torch
+ name = "torch handle"
+ desc = "A torch handle without its head."
+ w_class = ITEMSIZE_NORMAL
+ icon_state = "torch-empty"
+ item_state = "torch-empty"
+ icon = 'icons/obj/lighting.dmi'
+ item_icons = list(
+ slot_l_hand_str = 'icons/mob/items/lefthand_lighting.dmi',
+ slot_r_hand_str = 'icons/mob/items/righthand_lighting.dmi',
+ )
+
+ drop_sound = 'sound/items/drop/woodweapon.ogg'
+ pickup_sound = 'sound/items/pickup/woodweapon.ogg'
+
+/obj/item/torch/attackby(obj/item/I, mob/user)
+ if(istype(I, /obj/item/reagent_containers/glass/rag))
+ to_chat(user, SPAN_NOTICE("You add \the [I] to \the [src]."))
+ var/obj/item/device/flashlight/flare/torch/T = new()
+ qdel(I)
+ user.put_in_hands(T)
+ qdel(src)
diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm
index 3f44991f230..c338d9d7a97 100644
--- a/code/game/objects/items/devices/radio/headset.dm
+++ b/code/game/objects/items/devices/radio/headset.dm
@@ -71,6 +71,18 @@
return ..(freq, level)
return -1
+/obj/item/device/radio/headset/attack_hand(mob/user)
+ if(ishuman(user))
+ var/mob/living/carbon/human/H = user
+ if(H.l_ear != src && H.r_ear != src)
+ return ..()
+
+ if(slot_flags & SLOT_TWOEARS)
+ var/obj/item/clothing/ears/OE = (H.l_ear == src ? H.r_ear : H.l_ear)
+ qdel(OE)
+
+ ..()
+
/obj/item/device/radio/headset/attackby(obj/item/W, mob/user)
if(W.isscrewdriver())
if(keyslot1 || keyslot2)
@@ -388,6 +400,16 @@
translate_hivenet = 1
ks2type = /obj/item/device/encryptionkey/hivenet
+/obj/item/device/radio/headset/earmuff
+ name = "earmuffs"
+ desc = "Protects your hearing from loud noises, and quiet ones as well."
+ desc_antag = "This set of earmuffs has a secret compartment housing radio gear, allowing it to function as a standard headset."
+ icon = 'icons/obj/clothing/ears.dmi'
+ icon_state = "earmuffs"
+ item_state = "earmuffs"
+ item_flags = SOUNDPROTECTION
+ slot_flags = SLOT_EARS | SLOT_TWOEARS
+
/obj/item/device/radio/headset/syndicate
name = "military headset"
icon_state = "syn_headset"
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index e35c0fd440b..80cae247231 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -206,6 +206,18 @@ BREATH ANALYZER
limb_result = "[limb_result] \[[get_severity(org.burn_dam, TRUE)] burns\]"
if(org.status & ORGAN_BLEEDING)
limb_result = "[limb_result] \[bleeding\]"
+ var/is_bandaged = org.is_bandaged()
+ var/is_salved = org.is_salved()
+ if(is_bandaged && is_salved)
+ var/icon/B = icon('icons/obj/stacks/medical.dmi', "bandaged")
+ var/icon/S = icon('icons/obj/stacks/medical.dmi', "salved")
+ limb_result = "[limb_result] \[[icon2html(B, user)] | [icon2html(S, user)]\]"
+ else if(is_bandaged)
+ var/icon/B = icon('icons/obj/stacks/medical.dmi', "bandaged")
+ limb_result = "[limb_result] \[[icon2html(B, user)]\]"
+ else if(is_salved)
+ var/icon/S = icon('icons/obj/stacks/medical.dmi', "salved")
+ limb_result = "[limb_result] \[[icon2html(S, user)]\]"
dat += limb_result
else
dat += "No detectable limb injuries."
diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm
index c8d3d52757f..3da36be519f 100644
--- a/code/game/objects/items/stacks/medical.dm
+++ b/code/game/objects/items/stacks/medical.dm
@@ -157,6 +157,7 @@ Contains:
else
to_chat(user, "\The [src] is used up, but there are more wounds to treat on \the [affecting.name].")
use(used)
+ H.update_bandages(TRUE)
else
if (can_operate(H)) //Checks if mob is lying down on table for surgery
if (do_surgery(H,user,src))
@@ -282,6 +283,7 @@ Contains:
else
to_chat(user, "\The [src] is used up, but there are more wounds to treat on \the [affecting.name].")
use(used)
+ H.update_bandages(TRUE)
else
if (can_operate(H)) //Checks if mob is lying down on table for surgery
if (do_surgery(H,user,src))
diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm
index 3a686cb8151..542b646c1de 100644
--- a/code/game/objects/items/toys.dm
+++ b/code/game/objects/items/toys.dm
@@ -504,7 +504,7 @@
/obj/item/toy/snappop/Crossed(H as mob|obj)
if((ishuman(H))) //i guess carp and shit shouldn't set them off
var/mob/living/carbon/M = H
- if(M.m_intent == "run")
+ if(M.m_intent == M_RUN)
to_chat(M, SPAN_WARNING("You step on the snap pop!"))
do_pop()
@@ -979,18 +979,6 @@
attack_verb = list("attacked", "slashed", "stabbed", "poked")
contained_sprite = TRUE
-/* NYET.
-/obj/item/toddler
- icon_state = "toddler"
- name = "toddler"
- desc = "This baby looks almost real. Wait, did it just burp?"
- force = 5
- w_class = ITEMSIZE_LARGE
- slot_flags = SLOT_BACK
-*/
-
-//This should really be somewhere else but I don't know where. w/e
-
/obj/item/inflatable_duck
name = "inflatable duck"
desc = "No bother to sink or swim when you can just float!"
@@ -1011,3 +999,24 @@
throwforce = 1
drop_sound = 'sound/items/drop/cardboardbox.ogg'
pickup_sound = 'sound/items/pickup/cardboardbox.ogg'
+
+/obj/item/toy/partypopper
+ name = "party popper"
+ desc = "Instructions : Aim away from face. Wait for appropriate timing. Pull cord, enjoy confetti."
+ icon_state = "partypopper"
+ w_class = ITEMSIZE_TINY
+ drop_sound = 'sound/items/drop/cardboardbox.ogg'
+ pickup_sound = 'sound/items/pickup/cardboardbox.ogg'
+
+/obj/item/toy/partypopper/attack_self(mob/user)
+ if(icon_state == "partypopper")
+ spark(src, 1, user.dir)
+ user.visible_message(SPAN_NOTICE("[user] pulls on the string, releasing a burst of confetti!"), SPAN_NOTICE("You pull on the string, releasing a burst of confetti!"))
+ playsound(src, 'sound/effects/snap.ogg', 50, TRUE)
+ icon_state = "partypopper_e"
+ var/turf/T = get_step(src, user.dir)
+ if(!turf_clear(T))
+ T = get_turf(src)
+ new /obj/effect/decal/cleanable/confetti(T)
+ else
+ to_chat(user, SPAN_NOTICE("The [src] is already spent!"))
diff --git a/code/game/objects/items/weapons/RFD.dm b/code/game/objects/items/weapons/RFD.dm
index 271d70b6ff1..ed8dd7180cc 100644
--- a/code/game/objects/items/weapons/RFD.dm
+++ b/code/game/objects/items/weapons/RFD.dm
@@ -441,13 +441,16 @@ RFD Mining-Class
icon_state = "rfd-m"
item_state = "rfd-m"
-/obj/item/rfd/mining/afterattack(atom/A, mob/user, proximity)
+/obj/item/rfd/mining/attack_self(mob/user)
+ return
+
+/obj/item/rfd/mining/afterattack(atom/A, mob/user, proximity, click_parameters, var/report_duplicate = TRUE)
if(!proximity)
return
if(isrobot(user))
var/mob/living/silicon/robot/R = user
- if(R.stat || !R.cell || R.cell.charge <= 500)
+ if(R.stat || !R.cell || R.cell.charge <= 200)
if(last_fail <= world.time - 20) //Spam limiter.
last_fail = world.time
to_chat(user, SPAN_WARNING("You are unable to produce enough charge to use \the [src]!"))
@@ -467,7 +470,8 @@ RFD Mining-Class
return
if(locate(/obj/structure/track) in A)
- to_chat(user, SPAN_WARNING("There is already a track on \the [A]!"))
+ if(report_duplicate)
+ to_chat(user, SPAN_WARNING("There is already a track on \the [A]!"))
return
playsound(src.loc, 'sound/machines/click.ogg', 10, 1)
@@ -480,7 +484,7 @@ RFD Mining-Class
if(isrobot(user))
var/mob/living/silicon/robot/R = user
if(R.cell)
- R.cell.use(500)
+ R.cell.use(200)
else
stored_matter--
to_chat(user, SPAN_NOTICE("The RFD now holds [stored_matter]/30 fabrication-units."))
diff --git a/code/game/objects/items/weapons/cards_ids.dm b/code/game/objects/items/weapons/cards_ids.dm
index 4c2fa1cb96d..6001ee141ad 100644
--- a/code/game/objects/items/weapons/cards_ids.dm
+++ b/code/game/objects/items/weapons/cards_ids.dm
@@ -425,7 +425,7 @@ var/const/NO_EMAG_ACT = -50
assignment = "Minedrone"
/obj/item/card/id/minedrone/New()
- access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_qm, access_mining, access_mining_station)
+ access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_qm, access_mining, access_mining_station, access_external_airlocks)
..()
/obj/item/card/id/centcom
diff --git a/code/game/objects/items/weapons/flamethrower.dm b/code/game/objects/items/weapons/flamethrower.dm
index e1ffa736074..8edf6e71d0e 100644
--- a/code/game/objects/items/weapons/flamethrower.dm
+++ b/code/game/objects/items/weapons/flamethrower.dm
@@ -1,6 +1,6 @@
/obj/item/flamethrower
name = "flamethrower"
- desc = "You are a firestarter!"
+ desc = "A flamethrower created by modifying a welding tool to fit an external gas tank."
icon = 'icons/obj/flamethrower.dmi'
icon_state = "flamethrowerbase"
item_state = "flamethrower_0"
@@ -10,18 +10,29 @@
throwforce = 10.0
throw_speed = 1
throw_range = 5
- w_class = ITEMSIZE_NORMAL
+ w_class = ITEMSIZE_LARGE
origin_tech = list(TECH_COMBAT = 1, TECH_PHORON = 1)
matter = list(DEFAULT_WALL_MATERIAL = 500)
- var/status = 0
+ var/secured = FALSE // Whether we have an igniter secured (screwdrivered) to us or not
var/throw_amount = 100
- var/lit = 0 //on or off
- var/operating = 0//cooldown
+ var/lit = FALSE //on or off
+ var/operating = FALSE //cooldown
var/turf/previousturf = null
var/obj/item/weldingtool/weldtool = null
var/obj/item/device/assembly/igniter/igniter = null
var/obj/item/tank/phoron/ptank = null
+/obj/item/flamethrower/examine(mob/user)
+ ..()
+ if(Adjacent(user))
+ if(ptank)
+ to_chat(user, SPAN_NOTICE("Release pressure is set to [throw_amount] kPa. The tank has about [round(ptank.air_contents.return_pressure(), 10)] kPa left in it."))
+ else
+ to_chat(user, SPAN_WARNING("It has no gas tank installed."))
+ if(igniter)
+ to_chat(user, SPAN_NOTICE("It has \an [igniter] installed."))
+ else
+ to_chat(user, SPAN_WARNING("It has no igniter installed."))
/obj/item/flamethrower/Destroy()
if(weldtool)
@@ -45,13 +56,11 @@
location = M.loc
if(isturf(location)) //start a fire if possible
location.hotspot_expose(700, 2)
- return
-
/obj/item/flamethrower/update_icon()
cut_overlays()
if(igniter)
- add_overlay("+igniter[status]")
+ add_overlay("+igniter[secured]")
if(ptank)
add_overlay("+ptank")
if(lit)
@@ -59,10 +68,18 @@
item_state = "flamethrower_1"
else
item_state = "flamethrower_0"
- return
+
+/obj/item/flamethrower/isFlameSource()
+ return lit
/obj/item/flamethrower/afterattack(atom/target, mob/user, proximity)
- if(!proximity) return
+ if(proximity)
+ return
+ if(!ptank)
+ return
+ if(ptank.air_contents.get_by_flag(XGM_GAS_FUEL) < 1)
+ to_chat(user, SPAN_WARNING("\The [src] doesn't have enough fuel left to throw!"))
+ return
// Make sure our user is still holding us
if(user && user.get_active_hand() == src)
var/turf/target_turf = get_turf(target)
@@ -70,9 +87,11 @@
var/turflist = getline(user, target_turf)
flame_turf(turflist)
-/obj/item/flamethrower/attackby(obj/item/W as obj, mob/user as mob)
- if(user.stat || user.restrained() || user.lying) return
- if(W.iswrench() && !status)//Taking this apart
+/obj/item/flamethrower/attackby(obj/item/W, mob/user)
+ if(use_check_and_message(user))
+ return
+
+ if(W.iswrench() && !secured)//Taking this apart
var/turf/T = get_turf(src)
if(weldtool)
weldtool.forceMove(T)
@@ -87,85 +106,117 @@
qdel(src)
return
- if(W.isscrewdriver() && igniter && !lit)
- status = !status
- to_chat(user, "[igniter] is now [status ? "secured" : "unsecured"]!")
+ else if(W.isscrewdriver() && igniter && !lit)
+ secured = !secured
+ to_chat(user, SPAN_NOTICE("[igniter] is now [secured ? "secured" : "unsecured"]!"))
update_icon()
return
- if(isigniter(W))
+ else if(isigniter(W))
var/obj/item/device/assembly/igniter/I = W
- if(I.secured) return
- if(igniter) return
- user.drop_from_inventory(I,src)
+ if(I.secured)
+ to_chat(user, SPAN_WARNING("\The [I] is not ready to attach yet! Use a screwdriver on it first."))
+ return
+ if(igniter)
+ to_chat(user, SPAN_WARNING("\The [src] already has an igniter installed."))
+ return
+ user.drop_from_inventory(I, src)
igniter = I
update_icon()
return
- if(istype(W,/obj/item/tank/phoron))
+ else if(istype(W, /obj/item/tank/phoron))
if(ptank)
- to_chat(user, "There appears to already be a phoron tank loaded in [src]!")
+ to_chat(user, SPAN_WARNING("There appears to already be a tank loaded in \the [src]!"))
return
- user.drop_from_inventory(W,src)
+ user.drop_from_inventory(W, src)
ptank = W
update_icon()
return
- if(istype(W, /obj/item/device/analyzer))
+ else if(istype(W, /obj/item/device/analyzer))
var/obj/item/device/analyzer/A = W
A.analyze_gases(src, user)
return
+
+ else if(W.isFlameSource()) // you can light it with external input, even without an igniter
+ attempt_lighting(user, TRUE)
+ update_icon()
+ return
..()
- return
-/obj/item/flamethrower/attack_self(mob/user as mob)
- if(user.stat || user.restrained() || user.lying) return
- user.set_machine(src)
+/obj/item/flamethrower/attack_self(mob/user)
+ if(use_check_and_message(user))
+ return
if(!ptank)
- to_chat(user, "Attach a phoron tank first!")
+ to_chat(user, SPAN_WARNING("Attach a phoron tank first!"))
return
- var/dat = text("Flamethrower ([lit ? "Lit" : "Unlit"]) \n Tank Pressure: [ptank.air_contents.return_pressure()] \nAmount to throw: --- [throw_amount] +++ \nRemove phorontank - Close")
- user << browse(dat, "window=flamethrower;size=600x300")
- onclose(user, "flamethrower")
- return
-
-
-/obj/item/flamethrower/Topic(href,href_list[])
- if(href_list["close"])
- usr.unset_machine()
- usr << browse(null, "window=flamethrower")
+ var/list/options = list(
+ "Eject Tank" = image('icons/obj/tank.dmi', "phoron"),
+ "Light" = image('icons/effects/effects.dmi', "exhaust"),
+ "Lower Pressure" = image('icons/mob/screen/radial.dmi', "radial_sub"),
+ "Raise Pressure" = image('icons/mob/screen/radial.dmi', "radial_add")
+ )
+ var/handle = show_radial_menu(user, user, options, radius = 42, tooltips=TRUE)
+ if(!handle)
return
- if(usr.stat || usr.restrained() || usr.lying) return
- usr.set_machine(src)
- if(href_list["light"])
- if(!ptank) return
- if(ptank.air_contents.gas[GAS_PHORON] < 1) return
- if(!status) return
- lit = !lit
- if(lit)
- START_PROCESSING(SSprocessing, src)
- if(href_list["amount"])
- throw_amount = throw_amount + text2num(href_list["amount"])
- throw_amount = max(50, min(5000, throw_amount))
- if(href_list["remove"])
- if(!ptank) return
- usr.put_in_hands(ptank)
- ptank = null
- lit = 0
- usr.unset_machine()
- usr << browse(null, "window=flamethrower")
- for(var/mob/M in viewers(1, loc))
- if((M.client && M.machine == src))
- attack_self(M)
+ switch(handle)
+ if("Eject Tank")
+ if(!ptank)
+ return
+ user.put_in_hands(ptank)
+ ptank = null
+ lit = FALSE
+ if("Light")
+ attempt_lighting(user)
+ if("Lower Pressure")
+ change_pressure(-50, user)
+ if("Raise Pressure")
+ change_pressure(50, user)
+ else
+ return
+
update_icon()
- return
+/obj/item/flamethrower/proc/attempt_lighting(var/mob/user, var/external)
+ if(!external) // if it's external input, we can't unlight it, but we don't need to check for an igniter either
+ if(lit) // you can extinguish the flamethrower without an igniter
+ lit = FALSE
+ to_chat(user, SPAN_NOTICE("You extinguish \the [src]."))
+ return
+ if(!secured) // can't light via the flamethrower unless we have an igniter secured
+ if(igniter)
+ to_chat(user, SPAN_WARNING("\The [igniter] isn't secured, you need to use a screwdriver on it first."))
+ else
+ to_chat(user, SPAN_WARNING("\The [src] doesn't have a secured igniter installed."))
+ return
+ if(lit)
+ to_chat(user, SPAN_WARNING("\The [src] is already lit."))
+ return
+ if(!ptank)
+ to_chat(user, SPAN_WARNING("\The [src] doesn't have a tank installed."))
+ return
+ if(ptank.air_contents.get_by_flag(XGM_GAS_FUEL) < 1)
+ to_chat(user, SPAN_WARNING("\The [src] doesn't have any flammable fuel to light!"))
+ return
+ lit = TRUE
+ to_chat(user, SPAN_NOTICE("You light \the [src]."))
+ if(lit)
+ START_PROCESSING(SSprocessing, src)
+
+/obj/item/flamethrower/proc/change_pressure(var/pressure, var/mob/user)
+ if(!pressure)
+ return
+ throw_amount += pressure
+ throw_amount = Clamp(50, throw_amount, 5000)
+ if(ismob(user))
+ to_chat(user, SPAN_NOTICE("Pressure has been adjusted to [throw_amount] kPa."))
//Called from turf.dm turf/dblclick
/obj/item/flamethrower/proc/flame_turf(turflist)
if(!lit || operating) return
- operating = 1
+ operating = TRUE
playsound(src, fire_sound, 70, 1)
for(var/turf/T in turflist)
if(T.density || istype(T, /turf/space))
@@ -178,32 +229,23 @@
ignite_turf(T)
sleep(1)
previousturf = null
- operating = 0
- for(var/mob/M in viewers(1, loc))
- if((M.client && M.machine == src))
- attack_self(M)
- return
+ operating = FALSE
/obj/item/flamethrower/proc/ignite_turf(turf/target)
- //TODO: DEFERRED Consider checking to make sure tank pressure is high enough before doing this...
- //Transfer 5% of current tank air contents to turf
- var/datum/gas_mixture/air_transfer = ptank.air_contents.remove_ratio(0.02*(throw_amount/100))
- new/obj/effect/decal/cleanable/liquid_fuel/flamethrower_fuel(target,air_transfer.gas[GAS_PHORON]*15,get_dir(loc,target))
- air_transfer.gas[GAS_PHORON] = 0
+ var/datum/gas_mixture/air_transfer = ptank.air_contents.remove_ratio(0.02 * (throw_amount / 100))
+ new /obj/effect/decal/cleanable/liquid_fuel/flamethrower_fuel(target, air_transfer.get_by_flag(XGM_GAS_FUEL) * 15, get_dir(loc, target))
+ for(var/g in air_transfer.gas)
+ if(gas_data.flags[g] & XGM_GAS_FUEL)
+ air_transfer.gas[g] = 0
target.assume_air(air_transfer)
- //Burn it based on transfered gas
- //target.hotspot_expose(part4.air_contents.temperature*2,300)
- target.hotspot_expose((ptank.air_contents.temperature*2) + 380,500) // -- More of my "how do I shot fire?" dickery. -- TLE
- //location.hotspot_expose(1000,500,1)
- return
+ target.hotspot_expose((ptank.air_contents.temperature*2) + 400, 500)
-/obj/item/flamethrower/full/New(var/loc)
- ..()
+/obj/item/flamethrower/full/Initialize()
+ . = ..()
weldtool = new /obj/item/weldingtool(src)
- weldtool.status = 0
+ weldtool.status = FALSE
igniter = new /obj/item/device/assembly/igniter(src)
- igniter.secured = 0
- status = 1
- update_icon()
- return
+ igniter.secured = FALSE
+ secured = TRUE
+ update_icon()
\ No newline at end of file
diff --git a/code/game/objects/items/weapons/gift_wrappaper.dm b/code/game/objects/items/weapons/gift_wrappaper.dm
index 5f184b42062..24cda0dc90f 100644
--- a/code/game/objects/items/weapons/gift_wrappaper.dm
+++ b/code/game/objects/items/weapons/gift_wrappaper.dm
@@ -129,13 +129,14 @@
desc = "PRESENTS!!!! eek!"
icon = 'icons/obj/items.dmi'
icon_state = "gift1"
- item_state = "gift1"
+ item_state = "gift"
w_class = ITEMSIZE_TINY
+ randpixel = 12
+ var/gift_type
/obj/item/xmasgift/Initialize()
..()
- pixel_x = rand(-10,10)
- pixel_y = rand(-10,10)
+ randpixel_xy()
var/gift_benefactor = pick("the NanoTrasen Department of Christmas Affairs", "Miranda Trasen", "Joseph Dorne", "Isaac Asimov", "Baal D. Griffon", "the Sol Alliance (Sorry about the blockade!)",
"Hephaestus Industries", "Idris Incorporated", "Glorsh Omega II", "the Jargon Federation", "the People's Republic of Adhomai", "the Adhomai Liberation Army", "the Izweski Hegemony",
"the Zo'ra Hive","the Coalition of Colonies", "Digital Dingo", "Optimum Jeffrey", "Lemmy and the Clockworks", "President Hadii", "King Azunja","Supreme Commander Nated'Hakhan",
@@ -148,14 +149,19 @@
"respect","begrudging respect","love", "seasonal obligation")
desc = "To: The [station_name()] From: [gift_benefactor], with [pick_emotion]"
+ if(!gift_type)
+ gift_type = get_gift_type()
+
+ if(ispath(gift_type, /mob/living/simple_animal))
+ if(ispath(gift_type, /mob/living/simple_animal/schlorrgo))
+ icon_state = "strangeschlorrgo"
+ else
+ icon_state = "strangepet"
+
return
-/obj/item/xmasgift/ex_act(var/severity = 2.0)
- qdel(src)
- return
-
-/obj/item/xmasgift/small/attack_self(mob/user)
- var/gift_type = pick(
+/obj/item/xmasgift/proc/get_gift_type()
+ var/picked_gift_type = pick(
/obj/random/action_figure,
/obj/random/coin,
/obj/random/spacecash,
@@ -215,8 +221,16 @@
/obj/item/ore/coal,
/obj/item/stamp/clown,
/obj/item/organ/internal/heart/skrell,
- /obj/item/toy/balloon/color)
+ /obj/item/toy/balloon/color,
+ /obj/item/storage/box/partypopper)
+ return picked_gift_type
+
+/obj/item/xmasgift/ex_act(var/severity = 2.0)
+ qdel(src)
+ return
+
+/obj/item/xmasgift/attack_self(mob/user)
var/atom/movable/I = new gift_type(get_turf(user))
user.remove_from_mob(src)
user.put_in_hands(I)
@@ -226,11 +240,10 @@
/obj/item/xmasgift/medium
icon_state = "gift2"
- item_state = "gift2"
w_class = ITEMSIZE_SMALL
-/obj/item/xmasgift/medium/attack_self(mob/user)
- var/gift_type = pick(
+/obj/item/xmasgift/medium/get_gift_type()
+ var/picked_gift_type = pick(
/obj/item/sord,
/obj/random/booze,
/obj/random/random_flag,
@@ -263,7 +276,7 @@
/mob/living/simple_animal/rat/brown,
/mob/living/simple_animal/rat/gray,
/mob/living/simple_animal/rat/white,
- /obj/item/xmasgift/small,
+ /obj/item/xmasgift,
/obj/item/tank/jetpack/void,
/obj/item/xmasgift/large,
/obj/item/reagent_containers/food/snacks/pudding,
@@ -286,21 +299,14 @@
/obj/item/device/megaphone,
/obj/item/device/violin)
- var/atom/movable/I = new gift_type(get_turf(user))
- user.remove_from_mob(src)
- if (!user.put_in_hands(I))
- user.forceMove(get_turf(src))
- to_chat(user, SPAN_NOTICE("You open the gift, revealing your new [I.name]! Just what you always wanted!"))
- qdel(src)
- return
+ return picked_gift_type
/obj/item/xmasgift/large
icon_state = "gift3"
- item_state = "gift3"
w_class = ITEMSIZE_NORMAL
-/obj/item/xmasgift/large/attack_self(mob/user)
- var/gift_type = pick(
+/obj/item/xmasgift/large/get_gift_type()
+ var/picked_gift_type = pick(
/obj/random/plushie,
/obj/random/backpack,
/obj/item/inflatable_duck,
@@ -317,6 +323,7 @@
/mob/living/simple_animal/mushroom,
/mob/living/simple_animal/ice_tunneler,
/mob/living/simple_animal/carp/baby,
+ /mob/living/simple_animal/schlorrgo,
/mob/living/carbon/human/monkey/nupnup,
/obj/item/xmasgift/medium,
/obj/item/tank/jetpack,
@@ -329,11 +336,15 @@
/mob/living/simple_animal/crab,
/mob/living/simple_animal/parrot,
/mob/living/simple_animal/hostile/commanded/dog/pug,
- /obj/item/target/alien)
+ /obj/item/target/alien,
+ /obj/item/storage/box/candy)
- var/atom/movable/I = new gift_type(get_turf(user))
- user.remove_from_mob(src)
- user.put_in_hands(I)
- to_chat(user, SPAN_NOTICE("You open the gift, revealing your new [I.name]! Just what you always wanted!"))
- qdel(src)
- return
+ return picked_gift_type
+
+/obj/item/xmasgift/schlorrgo
+ gift_type = /mob/living/simple_animal/schlorrgo
+ w_class = ITEMSIZE_NORMAL
+
+/obj/item/xmasgift/viscerator
+ gift_type = /mob/living/simple_animal/hostile/viscerator
+ w_class = ITEMSIZE_NORMAL
\ No newline at end of file
diff --git a/code/game/objects/items/weapons/grenades/flashbang.dm b/code/game/objects/items/weapons/grenades/flashbang.dm
index 2ea699b0c8b..8f4b57841cc 100644
--- a/code/game/objects/items/weapons/grenades/flashbang.dm
+++ b/code/game/objects/items/weapons/grenades/flashbang.dm
@@ -41,7 +41,7 @@
eye_safety = M.eyecheck(TRUE)
if(ishuman(M))
var/mob/living/carbon/human/H = M
- if(istype(H.l_ear, /obj/item/clothing/ears/earmuffs) || istype(H.r_ear, /obj/item/clothing/ears/earmuffs))
+ if(H.protected_from_sound())
ear_safety += 2
if(HULK in H.mutations)
ear_safety += 1
diff --git a/code/game/objects/items/weapons/handcuffs.dm b/code/game/objects/items/weapons/handcuffs.dm
index ea7947c5533..446c59c5341 100644
--- a/code/game/objects/items/weapons/handcuffs.dm
+++ b/code/game/objects/items/weapons/handcuffs.dm
@@ -13,16 +13,16 @@
origin_tech = list(TECH_MATERIAL = 1)
matter = list(DEFAULT_WALL_MATERIAL = 500)
recyclable = TRUE
- var/elastic
+ var/legcuff = FALSE
+ var/elastic = FALSE
var/dispenser = 0
- var/breakouttime = 1200 //Deciseconds = 120s = 2 minutes
+ var/breakouttime = 2 MINUTES
var/cuff_sound = 'sound/weapons/handcuffs.ogg'
var/cuff_type = "handcuffs"
drop_sound = 'sound/items/drop/accessory.ogg'
pickup_sound = 'sound/items/pickup/accessory.ogg'
/obj/item/handcuffs/attack(var/mob/living/carbon/C, var/mob/living/user)
-
if(!user.IsAdvancedToolUser())
return
@@ -31,7 +31,7 @@
place_handcuffs(user, user)
return
- if(!C.handcuffed)
+ if((!legcuff && !C.handcuffed) || (legcuff && !C.legcuffed))
if (C == user)
place_handcuffs(user, user)
return
@@ -57,15 +57,20 @@
if(!istype(H))
return FALSE
- if (!H.has_organ_for_slot(slot_handcuffed))
+ if((!legcuff && !H.has_organ_for_slot(slot_handcuffed)) || (legcuff && !H.has_organ_for_slot(slot_legcuffed)))
if(user)
- to_chat(user, SPAN_DANGER("\The [H] needs at least two wrists before you can cuff them together!"))
+ to_chat(user, SPAN_DANGER("\The [H] needs at least two [legcuff ? "ankles" : "wrists"] before you can cuff them together!"))
return FALSE
- if(istype(H.gloves,/obj/item/clothing/gloves/rig) && !elastic) // Can't cuff someone who's in a deployed hardsuit.
- if(user)
- to_chat(user, SPAN_DANGER("\The [src] won't fit around \the [H.gloves]!"))
- return FALSE
+ if(!elastic) // Can't cuff someone who's in a deployed hardsuit.
+ if(!legcuff && istype(H.gloves, /obj/item/clothing/gloves/rig))
+ if(user)
+ to_chat(user, SPAN_DANGER("\The [src] won't fit around \the [H.gloves]!"))
+ return FALSE
+ if(legcuff && istype(H.shoes, /obj/item/clothing/shoes/magboots/rig))
+ if(user)
+ to_chat(user, SPAN_DANGER("\The [src] won't fit around \the [H.shoes]!"))
+ return FALSE
if(user)
user.visible_message(SPAN_DANGER("\The [user] is attempting to put [cuff_type] on \the [H]!"))
@@ -85,8 +90,9 @@
user.do_attack_animation(H)
user.visible_message(SPAN_DANGER("\The [user] has put [cuff_type] on \the [H]!"))
- target.drop_r_hand()
- target.drop_l_hand()
+ if(!legcuff)
+ target.drop_r_hand()
+ target.drop_l_hand()
// Apply cuffs.
var/obj/item/handcuffs/cuffs = src
@@ -96,8 +102,13 @@
user.drop_from_inventory(cuffs,target)
else
cuffs.forceMove(target)
- target.handcuffed = cuffs
- target.update_inv_handcuffed()
+
+ if(!legcuff)
+ target.handcuffed = cuffs
+ target.update_inv_handcuffed()
+ else
+ target.legcuffed = cuffs
+ target.update_inv_legcuffed()
return TRUE
/mob/living/carbon/human/RestrainedClickOn(var/atom/A)
@@ -137,9 +148,9 @@
slot_l_hand_str = 'icons/mob/items/stacks/lefthand_materials.dmi',
slot_r_hand_str = 'icons/mob/items/stacks/righthand_materials.dmi',
)
- breakouttime = 300 //Deciseconds = 30s
+ breakouttime = 30 SECONDS
cuff_sound = 'sound/weapons/cablecuff.ogg'
- cuff_type = "cable restraints"
+ cuff_type = "cable restraint handcuffs"
var/can_be_cut = TRUE
elastic = TRUE
build_from_parts = TRUE
@@ -155,6 +166,13 @@
color = pick(COLOR_RED, COLOR_BLUE, COLOR_LIME, COLOR_ORANGE, COLOR_WHITE, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN)
add_overlay(overlay_image(icon, "[initial(icon_state)]_end", flags=RESET_COLOR))
+/obj/item/handcuffs/cable/attack_self(mob/user)
+ legcuff = !legcuff
+ cuff_type = "cable restraint [legcuff ? "leg" : "hand"]cuffs"
+ to_chat(user, SPAN_NOTICE("You twist the cable into [cuff_type]."))
+ icon_state = "cable[legcuff ? "leg" : ""]cuff"
+ breakouttime = legcuff ? 10 SECONDS : 30 SECONDS
+
/obj/item/handcuffs/cable/yellow
color = COLOR_YELLOW
@@ -169,6 +187,9 @@
desc = "A set of handcuffs made out of vines. How devilish!"
can_be_cut = FALSE
+/obj/item/handcuffs/cable/green/vines/attack_self(mob/user)
+ return
+
/obj/item/handcuffs/cable/pink
color = COLOR_PINK
@@ -211,14 +232,22 @@
icon_state = "tape_cross"
item_state = null
icon = 'icons/obj/bureaucracy.dmi'
- breakouttime = 200
+ breakouttime = 20 SECONDS
cuff_type = "duct tape"
/obj/item/handcuffs/ziptie
name = "ziptie"
desc = " A sturdy and reliable plastic ziptie for binding the wrists."
icon_state = "ziptie"
- breakouttime = 600
+ breakouttime = 1 MINUTE
cuff_sound = 'sound/weapons/cablecuff.ogg'
cuff_type = "zipties"
elastic = TRUE
+
+/obj/item/handcuffs/legcuffs
+ name = "legcuffs"
+ desc = "Use this to keep prisoners in line."
+ icon_state = "legcuff"
+ legcuff = TRUE
+ breakouttime = 30 SECONDS
+ cuff_type = "legcuffs"
\ No newline at end of file
diff --git a/code/game/objects/items/weapons/material/ashtray.dm b/code/game/objects/items/weapons/material/ashtray.dm
index de751d6da2a..efa3911323c 100644
--- a/code/game/objects/items/weapons/material/ashtray.dm
+++ b/code/game/objects/items/weapons/material/ashtray.dm
@@ -17,6 +17,24 @@
update_icon()
return
+/obj/item/material/ashtray/shatter()
+ if(emptyout(get_turf(src)))
+ visible_message(SPAN_DANGER("The contents of [src] spill everywhere!"))
+
+/obj/item/material/ashtray/proc/emptyout(atom/dest)
+ var/count = 0
+ for (var/obj/item/clothing/mask/smokable/cigarette/O in contents)
+ count++
+ O.forceMove(dest)
+ return count > 0
+
+/obj/item/material/ashtray/attack_self(mob/user)
+ var/turf/dest = get_turf(src)
+ if(emptyout(dest))
+ user.visible_message("[user] pours [src] out onto [dest].", SPAN_NOTICE("You pour [src] out onto [dest]."))
+ return
+ to_chat(user, SPAN_WARNING("[src] is empty, there's nothing to pour out!"))
+
/obj/item/material/ashtray/update_icon()
color = null
cut_overlays()
@@ -50,7 +68,7 @@
if (istype(W,/obj/item/clothing/mask/smokable/cigarette))
var/obj/item/clothing/mask/smokable/cigarette/cig = W
if (cig.lit == TRUE)
- src.visible_message("[user] crushes [cig] in \the [src], putting it out.")
+ user.visible_message("[user] crushes [cig] in \the [src], putting it out.", SPAN_NOTICE(""))
playsound(src.loc, 'sound/items/cigs_lighters/cig_snuff.ogg', 50, 1)
STOP_PROCESSING(SSprocessing, cig)
var/obj/item/butt = new cig.type_butt(src)
@@ -60,7 +78,10 @@
//spawn(1)
// TemperatureAct(150)
else if (cig.lit == FALSE)
- to_chat(user, "You place [cig] in [src] without even smoking it. Why would you do that?")
+ user.visible_message(
+ "[user] places [cig] in [src] without even smoking it.",
+ SPAN_NOTICE("You place [cig] in [src] without even smoking it. Why would you do that?")
+ )
src.visible_message("[user] places [W] in [src].")
user.update_inv_l_hand()
@@ -69,7 +90,8 @@
update_icon()
else
health = max(0,health - W.force)
- to_chat(user, "You hit [src] with [W].")
+ user.visible_message(user, SPAN_DANGER("[user] hits [src] with [W]!"), SPAN_DANGER("You hit [src] with [W]."))
+ playsound(get_turf(src), material.hitsound, 25)
if (health < 1)
shatter()
return
@@ -77,10 +99,8 @@
/obj/item/material/ashtray/throw_impact(atom/hit_atom)
if (health > 0)
health = max(0,health - 3)
- if (contents.len)
- src.visible_message("\The [src] slams into [hit_atom], spilling its contents!")
- for (var/obj/item/clothing/mask/smokable/cigarette/O in contents)
- O.forceMove(src.loc)
+ if(emptyout(get_turf(src)))
+ visible_message(SPAN_DANGER("[src] slams into [hit_atom], spilling its contents everywhere!"))
if (health < 1)
shatter()
return
diff --git a/code/game/objects/items/weapons/material/caltrops.dm b/code/game/objects/items/weapons/material/caltrops.dm
index 64d02e1b3ad..55724b94202 100644
--- a/code/game/objects/items/weapons/material/caltrops.dm
+++ b/code/game/objects/items/weapons/material/caltrops.dm
@@ -35,7 +35,7 @@
if(H.shoes || H.wear_suit?.body_parts_covered & FEET)
damage_coef -= 0.1
- if (H.m_intent == "run")
+ if (H.m_intent == M_RUN)
damage_coef += 0.4
diff --git a/code/game/objects/items/weapons/policetape.dm b/code/game/objects/items/weapons/policetape.dm
index fcfdb6ca0de..f3ec465eaaf 100644
--- a/code/game/objects/items/weapons/policetape.dm
+++ b/code/game/objects/items/weapons/policetape.dm
@@ -17,9 +17,15 @@ var/list/tape_roll_applications = list()
icon = 'icons/policetape.dmi'
anchored = 1
var/lifted = 0
+ var/list/crumplers
var/crumpled = 0
var/icon_base
+/obj/item/tape/examine(mob/user, distance)
+ . = ..()
+ if(LAZYLEN(crumplers) && Adjacent(user))
+ to_chat(user, SPAN_WARNING("\The [initial(name)] has been crumpled by [english_list(crumplers)]."))
+
/obj/item/tape/New()
..()
if(!hazard_overlays)
@@ -42,6 +48,19 @@ var/list/tape_roll_applications = list()
req_access = list(access_security)
icon_base = "police"
+/obj/item/taperoll/medical
+ name = "medical tape"
+ desc = "A roll of medical tape used to prevent overcrowding of hallways during emergencies."
+ icon_state = "medical_start"
+ tape_type = /obj/item/tape/medical
+ icon_base = "medical"
+
+/obj/item/tape/medical
+ name = "medical tape"
+ desc = "A length of medical tape. Better not cross it."
+ req_one_access = list(access_medical_equip)
+ icon_base = "medical"
+
/obj/item/taperoll/engineering
name = "engineering tape"
desc = "A roll of engineering tape used to block off working areas from the public."
@@ -137,41 +156,50 @@ var/list/tape_roll_applications = list()
tape_roll_applications[F] |= direction
return
-/obj/item/tape/proc/crumple()
+/obj/item/tape/proc/crumple(var/mob/user)
+ if(ishuman(user))
+ var/mob/living/carbon/human/H = user
+ var/obj/item/card/id/ID = H.GetIdCard(TRUE)
+ if(ID && ID.registered_name)
+ LAZYDISTINCTADD(crumplers, ID.registered_name)
if(!crumpled)
- crumpled = 1
+ crumpled = TRUE
icon_state = "[icon_state]_c"
name = "crumpled [name]"
/obj/item/tape/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if(!lifted && ismob(mover))
var/mob/M = mover
- add_fingerprint(M)
- if (!allowed(M)) //only select few learn art of not crumpling the tape
- to_chat(M, "You are not supposed to go past [src]...")
- crumple()
- return ..(mover)
+ if(!allowed(M)) //only select few learn art of not crumpling the tape
+ if(M.a_intent != I_HURT && M.m_intent != M_RUN)
+ return FALSE
+ to_chat(M, SPAN_WARNING("You aren't supposed to go past the [initial(name)]..."))
+ crumple(mover)
+ return ..()
-/obj/item/tape/attackby(obj/item/W as obj, mob/user as mob)
+/obj/item/tape/attackby(obj/item/W, mob/user)
breaktape(W, user)
-/obj/item/tape/attack_hand(mob/user as mob)
- if (user.a_intent == I_HELP && src.allowed(user))
- user.show_viewers(SPAN_NOTICE("[user] lifts [src], allowing passage."))
- crumple()
- lifted = 1
- spawn(200)
- lifted = 0
+/obj/item/tape/attack_hand(mob/user)
+ if(user.a_intent == I_HELP)
+ lifted = !lifted
+ user.visible_message("[user] [lifted ? "lifts" : "drops"] the [initial(name)], [lifted ? "allowing" : "blocking"] passage.", SPAN_NOTICE("You [lifted ? "lift" : "drop"] the [initial(name)], [lifted ? "allowing" : "blocking"] passage."))
+ var/shake_time = animate_shake()
+ sleep(shake_time)
+ if(!allowed(user))
+ crumple(user)
+ if(lifted)
+ animate(src, 1 SECOND, alpha = 150)
+ else
+ animate(src, 1 SECOND, alpha = initial(alpha))
else
breaktape(null, user)
-
-
/obj/item/tape/proc/breaktape(obj/item/W as obj, mob/user as mob)
if(user.a_intent == I_HELP && ((!can_puncture(W) && src.allowed(user))))
- to_chat(user, "You can't break the [src] with that!")
+ to_chat(user, SPAN_NOTICE("You can't break \the [src] with that!"))
return
- user.show_viewers(SPAN_NOTICE("[user] breaks the [src]!"))
+ user.visible_message(SPAN_NOTICE("[user] breaks \the [src]!"))
var/dir[2]
var/icon_dir = src.icon_state
@@ -193,5 +221,4 @@ var/list/tape_roll_applications = list()
qdel(P)
cur = get_step(cur,dir[i])
- qdel(src)
- return
+ qdel(src)
\ No newline at end of file
diff --git a/code/game/objects/items/weapons/storage/boxes.dm b/code/game/objects/items/weapons/storage/boxes.dm
index 5b628db4aa3..98f825ae87a 100644
--- a/code/game/objects/items/weapons/storage/boxes.dm
+++ b/code/game/objects/items/weapons/storage/boxes.dm
@@ -512,6 +512,11 @@
desc_antag = "These snap pops have an extra compound added that will deploy a tiny smokescreen when snapped."
starts_with = list(/obj/item/toy/snappop/syndi = 8)
+/obj/item/storage/box/partypopper
+ name = "party popper box"
+ desc = "Six cones of confetti conflagarating fun!"
+ starts_with = list(/obj/item/toy/partypopper = 6)
+
/obj/item/storage/box/autoinjectors
name = "box of empty injectors"
desc = "Contains empty autoinjectors."
diff --git a/code/game/objects/items/weapons/tanks/tank_types.dm b/code/game/objects/items/weapons/tanks/tank_types.dm
index 50fcce53dac..58f571cc225 100644
--- a/code/game/objects/items/weapons/tanks/tank_types.dm
+++ b/code/game/objects/items/weapons/tanks/tank_types.dm
@@ -95,7 +95,7 @@
if (istype(W, /obj/item/flamethrower))
var/obj/item/flamethrower/F = W
- if ((!F.status)||(F.ptank)) return
+ if ((!F.secured)||(F.ptank)) return
src.master = F
F.ptank = src
user.remove_from_mob(src)
diff --git a/code/game/objects/items/weapons/tether.dm b/code/game/objects/items/weapons/tether.dm
index 337d0db4efe..edbe35bd5ee 100644
--- a/code/game/objects/items/weapons/tether.dm
+++ b/code/game/objects/items/weapons/tether.dm
@@ -35,10 +35,8 @@ var/list/global/all_tethers = list()
/obj/item/tethering_device/process()
var/turf/our_turf = get_turf(src)
- for(var/tether in all_tethers)
+ for(var/tether in all_tethers - src)
var/obj/item/tethering_device/TD = tether
- if(TD == src)
- continue
if(!TD.active)
continue
var/turf/target_turf = get_turf(TD)
diff --git a/code/game/objects/items/weapons/vaurca_items.dm b/code/game/objects/items/weapons/vaurca_items.dm
index 805ce7a7d48..c167dc0ffb1 100644
--- a/code/game/objects/items/weapons/vaurca_items.dm
+++ b/code/game/objects/items/weapons/vaurca_items.dm
@@ -391,13 +391,6 @@
accuracy_wielded = -1
fire_delay_wielded = 1
-/obj/item/gun/launcher/crossbow/vaurca/update_icon()
- if(wielded)
- item_state = "gaussrifle-wielded"
- else
- item_state = "gaussrifle"
- update_held_icon()
-
/obj/item/gun/launcher/crossbow/vaurca/consume_next_projectile(mob/user=null)
return bolt
diff --git a/code/game/objects/items/weapons/weaponry.dm b/code/game/objects/items/weapons/weaponry.dm
index 049c303f921..387e00b723c 100644
--- a/code/game/objects/items/weapons/weaponry.dm
+++ b/code/game/objects/items/weapons/weaponry.dm
@@ -8,145 +8,107 @@
var/net_type = /obj/effect/energy_net
/obj/item/energy_net/dropped()
- spawn(10)
- if(src) qdel(src)
+ if(!QDELETED(src))
+ QDEL_IN(src, 1)
/obj/item/energy_net/throw_impact(atom/hit_atom)
..()
var/mob/living/M = hit_atom
-
- if(!istype(M) || locate(/obj/effect/energy_net) in M.loc)
+ if(!istype(M))
qdel(src)
- return 0
+ return
+ var/obj/effect/energy_net/EN = locate() in get_turf(M)
+ if(EN)
+ qdel(EN)
var/turf/T = get_turf(M)
if(T)
var/obj/effect/energy_net/net = new net_type(T)
- net.layer = M.layer+1
- M.captured = 1
+ net.layer = M.layer + 1
+ M.captured = TRUE
+ M.update_canmove()
net.affecting = M
- T.visible_message("[M] was caught in an energy net!")
+ M.visible_message(SPAN_DANGER("\The [M] was caught in \the [net]!"), SPAN_DANGER("You get caught in \the [net]!"))
qdel(src)
- // If we miss or hit an obstacle, we still want to delete the net.
- spawn(10)
- if(src) qdel(src)
-
/obj/effect/energy_net
name = "energy net"
desc = "It's a net made of green energy."
icon = 'icons/effects/effects.dmi'
icon_state = "energynet"
- density = 1
- opacity = 0
- mouse_opacity = 1
- anchored = 1
+ density = TRUE
+ opacity = FALSE
+ anchored = TRUE
- var/health = 25
+ var/health = 50
var/mob/living/affecting = null //Who it is currently affecting, if anyone.
- var/mob/living/master = null //Who shot web. Will let this person know if the net was successful.
- var/countdown = -1
-
-/obj/effect/energy_net/teleport
- countdown = 60
/obj/effect/energy_net/Initialize()
. = ..()
START_PROCESSING(SSprocessing, src)
/obj/effect/energy_net/Destroy()
+ STOP_PROCESSING(SSprocessing, src)
if(affecting)
- var/mob/living/carbon/M = affecting
- M.anchored = initial(affecting.anchored)
- M.captured = 0
- to_chat(M, "You are free of the net!")
+ affecting.anchored = initial(affecting.anchored)
+ affecting.captured = FALSE
+ affecting.update_canmove()
+ to_chat(affecting, SPAN_GOOD("You are free of the net!"))
+ affecting = null
- STOP_PROCESSING(SSprocessing, src)
return ..()
-/obj/effect/energy_net/proc/healthcheck()
-
- if(health <=0)
- density = 0
- src.visible_message("The energy net is torn apart!")
+/obj/effect/energy_net/proc/health_check()
+ if(health <= 0)
+ density = FALSE
+ src.visible_message(SPAN_WARNING("\The [src] is torn apart!"))
qdel(src)
- return
/obj/effect/energy_net/process()
-
- if(isnull(affecting) || affecting.loc != loc)
+ if(isnull(affecting) || get_turf(src) != get_turf(affecting))
qdel(src)
return
- // Countdown begin set to -1 will stop the teleporter from firing.
- // Clientless mobs can be netted but they will not teleport or decrement the timer.
- var/mob/living/M = affecting
- if(countdown == -1 || (istype(M) && !M.client))
- return
-
- if(countdown > 0)
- countdown--
- return
-
- // TODO: consider removing or altering this; energy nets are useful on their own
- // merits and the teleportation was never properly implemented; it's halfassed.
- density = 0
- invisibility = 101 //Make the net invisible so all the animations can play out.
- health = INFINITY //Make the net invincible so that an explosion/something else won't kill it during anims.
-
- playsound(affecting.loc, 'sound/effects/sparks4.ogg', 50, 1)
- anim(affecting.loc,affecting,'icons/mob/mob.dmi',,"phaseout",,affecting.dir)
-
- affecting.visible_message("[affecting] vanishes in a flare of light!")
-
- if(holdingfacility.len)
- affecting.forceMove(pick(holdingfacility))
-
- to_chat(affecting, "You appear in a strange place!")
-
- playsound(affecting.loc, 'sound/effects/phasein.ogg', 25, 1)
- playsound(affecting.loc, 'sound/effects/sparks2.ogg', 50, 1)
- anim(affecting.loc,affecting,'icons/mob/mob.dmi',,"phasein",,affecting.dir)
-
- qdel(src)
-
/obj/effect/energy_net/bullet_act(var/obj/item/projectile/Proj)
health -= Proj.get_structure_damage()
- healthcheck()
- return 0
+ health_check()
+ return FALSE
/obj/effect/energy_net/ex_act(var/severity = 2.0)
health = 0
- healthcheck()
+ health_check()
/obj/effect/energy_net/attack_hand(var/mob/user)
-
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ user.do_attack_animation(src, FIST_ATTACK_ANIMATION)
+ if(user == affecting)
+ to_chat(user, SPAN_WARNING("You can't claw at \the [src] while trapped inside it! You need to use a weapon."))
+ return
var/mob/living/carbon/human/H = user
if(istype(H))
if(H.species.can_shred(H))
playsound(src.loc, 'sound/weapons/slash.ogg', 80, 1)
health -= rand(10, 20)
else
- health -= rand(1,3)
-
+ health -= rand(1, 3)
else if (HULK in user.mutations)
health = 0
else
- health -= rand(5,8)
+ health -= rand(5, 8)
- to_chat(H, "You claw at the energy net.")
+ user.visible_message(SPAN_WARNING("\The [user] claws at \the [src]!"), SPAN_WARNING("You claw at \the [src]!"))
+ health_check()
- healthcheck()
- return
-
-/obj/effect/energy_net/attackby(obj/item/W as obj, mob/user as mob)
- health -= W.force
- healthcheck()
- ..()
+/obj/effect/energy_net/attackby(obj/item/W, mob/user)
+ user.do_attack_animation(src, W)
+ var/attack_force = W.force
+ if(user == affecting)
+ attack_force /= 2
+ health -= attack_force
+ health_check()
/obj/item/canesword
name = "thin sword"
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index 868c480acfb..bd1fd47444f 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -223,6 +223,7 @@
var/shake_dir = pick(-1, 1)
animate(src, transform = turn(matrix(), 8*shake_dir), pixel_x = init_px + 2*shake_dir, time = 1)
animate(transform = null, pixel_x = init_px, time = 6, easing = ELASTIC_EASING)
+ return 7 // how long it takes to do this
/obj/proc/rotate(var/mob/user, var/anchored_ignore = FALSE)
if(use_check_and_message(user))
diff --git a/code/game/objects/random/random.dm b/code/game/objects/random/random.dm
index e54b3deb86f..8ff5b77ac46 100644
--- a/code/game/objects/random/random.dm
+++ b/code/game/objects/random/random.dm
@@ -1220,9 +1220,8 @@
/obj/item/eightball/broken = 1,
/obj/item/spirit_board = 5,
/obj/item/device/laser_pointer = 1,
- /obj/item/clothing/accessory/badge/press/plastic = 2
-
-
+ /obj/item/clothing/accessory/badge/press/plastic = 2,
+ /obj/item/storage/box/partypopper = 11
)
/obj/random/arcade/orion
@@ -1495,7 +1494,7 @@
icon = 'icons/obj/items.dmi'
icon_state = "gift1"
spawnlist = list(
- /obj/item/xmasgift/small = 0.5,
+ /obj/item/xmasgift = 0.5,
/obj/item/xmasgift/medium = 0.3,
/obj/item/xmasgift/large = 0.2
)
@@ -1690,3 +1689,16 @@
/obj/structure/reagent_dispensers/keg/xuizikeg = 0.5,
/obj/structure/reagent_dispensers/keg/mead = 0.5
)
+
+/obj/random/pizzabox
+ name = "random pizzabox"
+ desc = "Contains a random pizzabox."
+ icon = 'icons/obj/food.dmi'
+ icon_state = "pizzabox1"
+ spawnlist = list(
+ /obj/item/pizzabox/margherita,
+ /obj/item/pizzabox/vegetable,
+ /obj/item/pizzabox/mushroom,
+ /obj/item/pizzabox/meat,
+ /obj/item/pizzabox/pineapple
+ )
\ No newline at end of file
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index fbfeadb660c..21dbccd6306 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -132,9 +132,9 @@
dump_contents()
icon_state = icon_opened
- opened = 1
+ opened = TRUE
playsound(loc, open_sound, 25, 0, -3)
- density = 0
+ density = FALSE
return 1
/obj/structure/closet/proc/close()
@@ -153,7 +153,7 @@
stored_units += store_mobs(stored_units)
icon_state = icon_closed
- opened = 0
+ opened = FALSE
if(linked_teleporter)
if(linked_teleporter.last_use + 600 > world.time)
return
@@ -162,7 +162,7 @@
linked_teleporter.last_use = world.time
playsound(get_turf(src), close_sound, 25, 0, -3)
- density = TRUE
+ density = initial(density)
return TRUE
//Cham Projector Exception
diff --git a/code/game/objects/structures/crates_lockers/closets/fitness.dm b/code/game/objects/structures/crates_lockers/closets/fitness.dm
index 1fbee573020..74174128e33 100644
--- a/code/game/objects/structures/crates_lockers/closets/fitness.dm
+++ b/code/game/objects/structures/crates_lockers/closets/fitness.dm
@@ -52,6 +52,8 @@
icon_closed = "red"
/obj/structure/closet/lasertag/red/fill()
+ new /obj/item/clothing/ears/earmuffs(src)
+ new /obj/item/clothing/ears/earmuffs(src)
new /obj/item/gun/energy/lasertag/red(src)
new /obj/item/gun/energy/lasertag/red(src)
new /obj/item/clothing/suit/redtag(src)
@@ -65,7 +67,9 @@
icon_closed = "blue"
/obj/structure/closet/lasertag/blue/fill()
+ new /obj/item/clothing/ears/earmuffs(src)
+ new /obj/item/clothing/ears/earmuffs(src)
new /obj/item/gun/energy/lasertag/blue(src)
new /obj/item/gun/energy/lasertag/blue(src)
new /obj/item/clothing/suit/bluetag(src)
- new /obj/item/clothing/suit/bluetag(src)
+ new /obj/item/clothing/suit/bluetag(src)
\ No newline at end of file
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
index 57489fa2c3d..df5f5834a62 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm
@@ -9,30 +9,24 @@
icon_off = "capsecureoff"
/obj/structure/closet/secure_closet/captains/fill()
- //Appearance
+ // Backpack
if(prob(50))
new /obj/item/storage/backpack/captain(src)
else
new /obj/item/storage/backpack/satchel_cap(src)
- new /obj/item/clothing/suit/captunic(src)
- new /obj/item/clothing/suit/captunic/capjacket(src)
- new /obj/item/clothing/head/caphat/cap(src)
- new /obj/item/clothing/head/bandana/captain(src)
- new /obj/item/clothing/under/rank/captain(src)
- new /obj/item/clothing/suit/storage/vest(src)
- new /obj/item/clothing/gloves/captain(src)
+ // Armor
new /obj/item/clothing/head/helmet(src)
- new /obj/item/clothing/shoes/brown(src)
- new /obj/item/clothing/under/dress/dress_cap(src)
- new /obj/item/clothing/head/caphat/formal(src)
- new /obj/item/clothing/under/captainformal(src)
+ new /obj/item/clothing/suit/storage/vest(src)
//Tools
new /obj/item/device/radio/headset/heads/captain(src)
new /obj/item/device/radio/headset/heads/captain/alt(src)
new /obj/item/gun/energy/disruptorpistol(src)
new /obj/item/device/flash(src)
new /obj/item/melee/telebaton(src)
- // acting captain briefcase
+ // uniform briefcases
+ new /obj/item/storage/briefcase/nt/captain(src)
+ new /obj/item/storage/briefcase/nt/captain_white(src)
+ new /obj/item/storage/briefcase/nt/captain_formal(src)
new /obj/item/storage/briefcase/nt/acap(src)
/obj/structure/closet/secure_closet/captains2
diff --git a/code/game/objects/structures/crates_lockers/closets/walllocker.dm b/code/game/objects/structures/crates_lockers/closets/walllocker.dm
index 033b14a0b57..a90f758663d 100644
--- a/code/game/objects/structures/crates_lockers/closets/walllocker.dm
+++ b/code/game/objects/structures/crates_lockers/closets/walllocker.dm
@@ -6,40 +6,25 @@
name = "Wall Locker"
icon = 'icons/obj/walllocker.dmi'
icon_state = "wall-locker"
- density = 0
- anchored = 1
+ density = FALSE
+ anchored = TRUE
store_mobs = FALSE
icon_closed = "wall-locker"
icon_opened = "wall-lockeropen"
-//spawns endless (3 sets) amounts of breathmask, emergency oxy tank and crowbar
-
/obj/structure/closet/walllocker/emerglocker
name = "emergency locker"
desc = "A wall mounted locker with emergency supplies."
- var/list/spawnitems = list(/obj/item/tank/emergency_oxygen,/obj/item/clothing/mask/breath)
- var/amount = 2 // spawns each items X times.
icon_state = "emerg"
+ icon_closed = "emerg"
+ icon_opened = "emerg-open"
-/obj/structure/closet/walllocker/emerglocker/toggle(mob/user as mob)
- attack_hand(user)
- return
-
-/obj/structure/closet/walllocker/emerglocker/attackby(obj/item/W as obj, mob/user as mob)
- return
-
-/obj/structure/closet/walllocker/emerglocker/attack_hand(mob/user as mob)
- if (istype(user, /mob/living/silicon/ai)) //Added by Strumpetplaya - AI shouldn't be able to
- return //activate emergency lockers. This fixes that. (Does this make sense, the AI can't call attack_hand, can it? --Mloc)
- if(!amount)
- to_chat(usr, "It's empty..")
- return
- if(amount)
- to_chat(usr, "You take out some items from \the [src].")
- for(var/path in spawnitems)
- new path(loc)
- amount--
- return
+/obj/structure/closet/walllocker/emerglocker/fill()
+ for(var/i = 1 to 3)
+ new /obj/item/tank/emergency_oxygen(src)
+ new /obj/item/clothing/mask/breath(src)
+ if(prob(20))
+ new /obj/item/reagent_containers/hypospray/autoinjector/emergency(src)
/obj/structure/closet/walllocker/emerglocker/north
pixel_y = 32
diff --git a/code/game/objects/structures/gore/tendrils.dm b/code/game/objects/structures/gore/tendrils.dm
index 22445ee431a..bb87d1b1fc7 100644
--- a/code/game/objects/structures/gore/tendrils.dm
+++ b/code/game/objects/structures/gore/tendrils.dm
@@ -32,7 +32,7 @@
return TRUE
if(ishuman(mover))
var/mob/living/carbon/human/H = mover
- if(H.mind.changeling)
+ if(H.mind.antag_datums[MODE_CHANGELING])
return TRUE
/obj/structure/gore/tendrils/Destroy()
diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm
index da01aae40b8..97936455986 100644
--- a/code/game/objects/structures/mirror.dm
+++ b/code/game/objects/structures/mirror.dm
@@ -10,11 +10,13 @@
var/list/ui_users = list()
/obj/structure/mirror/attack_hand(mob/user as mob)
+ if(shattered)
+ return
- if(shattered) return
-
- if(user.mind && user.mind.vampire && (!(user.mind.vampire.status & VAMP_ISTHRALL)))
- to_chat(user, "Your reflection appears distorted on the surface of \the [src].")
+ if(user.mind)
+ var/datum/vampire/vampire = user.mind.antag_datums[MODE_VAMPIRE]
+ if(vampire && !(vampire.status & VAMP_ISTHRALL))
+ to_chat(user, "Your reflection appears distorted on the surface of \the [src].")
if(ishuman(user))
var/datum/nano_module/appearance_changer/AC = ui_users[user]
@@ -82,9 +84,10 @@
var/list/ui_users = list()
/obj/item/mirror/attack_self(mob/user as mob)
-
- if(user.mind && user.mind.vampire && (!(user.mind.vampire.status & VAMP_ISTHRALL)))
- to_chat(user, "Your reflection appears distorted on the surface of \the [src].")
+ if(user.mind)
+ var/datum/vampire/vampire = user.mind.antag_datums[MODE_VAMPIRE]
+ if(vampire && !(vampire.status & VAMP_ISTHRALL))
+ to_chat(user, "Your reflection appears distorted on the surface of \the [src].")
if(ishuman(user))
var/datum/nano_module/appearance_changer/AC = ui_users[user]
diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm
index 9532714c0bd..0e8c95065ce 100644
--- a/code/game/objects/structures/morgue.dm
+++ b/code/game/objects/structures/morgue.dm
@@ -405,7 +405,7 @@
name = "crematorium igniter"
desc = "Burn baby burn!"
icon = 'icons/obj/power.dmi'
- icon_state = "crema_switch"
+ icon_state = "light0"
req_access = list(access_crematorium)
id = 1
var/cremate_dir // something for mappers, setting will make a crematorium in one step in this direction toggle
@@ -414,6 +414,7 @@
return
/obj/machinery/button/crematorium/attack_hand(mob/user)
+ playsound(src, /decl/sound_category/switch_sound, 30)
if(!allowed(user))
to_chat(user, SPAN_WARNING("Access denied."))
return
@@ -433,4 +434,4 @@
for(var/thing in crematoriums)
var/obj/structure/crematorium/C = thing
if(!C.cremating)
- C.cremate(user)
\ No newline at end of file
+ C.cremate(user)
diff --git a/code/game/objects/structures/simple_doors.dm b/code/game/objects/structures/simple_doors.dm
index 2513a9332b1..caa7dab348d 100644
--- a/code/game/objects/structures/simple_doors.dm
+++ b/code/game/objects/structures/simple_doors.dm
@@ -175,7 +175,7 @@
user.do_attack_animation(src)
animate_shake()
user.visible_message("\The [user] forcefully strikes \the [src] with \the [I]!")
- playsound(src.loc, material.hitsound, 100, 1)
+ playsound(src.loc, material.hitsound, W.get_clamped_volume(), 1)
src.health -= W.force * 1
CheckHealth()
@@ -242,4 +242,4 @@
/obj/structure/simple_door/cult/New(var/newloc, var/material_name)
..(newloc, MATERIAL_CULT)
- color = COLOR_CULT_DOOR // looks better than the standard cult colours
\ No newline at end of file
+ color = COLOR_CULT_DOOR // looks better than the standard cult colours
diff --git a/code/game/objects/structures/stool_bed_chair_nest/bed.dm b/code/game/objects/structures/stool_bed_chair_nest/bed.dm
index ecd994671e7..1e47b651d3d 100644
--- a/code/game/objects/structures/stool_bed_chair_nest/bed.dm
+++ b/code/game/objects/structures/stool_bed_chair_nest/bed.dm
@@ -24,19 +24,17 @@
var/material/padding_material
var/base_icon = "bed"
+ var/material_alteration = MATERIAL_ALTERATION_ALL
+ var/buckling_sound = 'sound/effects/buckle.ogg'
+
var/can_dismantle = TRUE
gfi_layer_rotation = GFI_ROTATION_DEFDIR
- var/apply_material_color = TRUE
var/makes_rolling_sound = TRUE
- var/buckle_sound = 'sound/effects/buckle.ogg'
-
slowdown = 5
-/obj/structure/bed/Initialize(mapload, var/new_material, var/new_padding_material)
- . = ..()
+/obj/structure/bed/New(newloc, new_material = MATERIAL_STEEL, new_padding_material)
+ ..(newloc)
color = null
- if(!new_material)
- new_material = DEFAULT_WALL_MATERIAL
material = SSmaterials.get_material_by_name(new_material)
if(!istype(material))
qdel(src)
@@ -47,41 +45,42 @@
/obj/structure/bed/buckle_mob(mob/living/M)
. = ..()
- if(. && buckle_sound)
- playsound(src, buckle_sound, 20)
+ if(. && buckling_sound)
+ playsound(src, buckling_sound, 20)
// Reuse the cache/code from stools, todo maybe unify.
/obj/structure/bed/update_icon()
// Prep icon.
icon_state = ""
cut_overlays()
- var/list/stool_cache = SSicon_cache.stool_cache
// Base icon.
+ var/list/stool_cache = SSicon_cache.stool_cache
+
var/cache_key = "[base_icon]-[material.name]"
if(!stool_cache[cache_key])
var/image/I = image('icons/obj/furniture.dmi', base_icon)
- if(apply_material_color)
+ if(material_alteration & MATERIAL_ALTERATION_COLOR)
I.color = material.icon_colour
stool_cache[cache_key] = I
add_overlay(stool_cache[cache_key])
// Padding overlay.
if(padding_material)
- var/padding_cache_key = "[base_icon]-padding-[padding_material.name]"
+ var/padding_cache_key = "[base_icon]-[padding_material.name]-padding"
if(!stool_cache[padding_cache_key])
var/image/I = image(icon, "[base_icon]_padding")
- if(apply_material_color)
+ if(material_alteration & MATERIAL_ALTERATION_COLOR)
I.color = padding_material.icon_colour
stool_cache[padding_cache_key] = I
add_overlay(stool_cache[padding_cache_key])
// Strings.
- desc = initial(desc)
- if(padding_material)
- name = "[padding_material.display_name] [initial(name)]" //this is not perfect but it will do for now.
- desc += " It's made of [material.use_name] and covered with [padding_material.use_name]."
- else
- name = "[material.display_name] [initial(name)]"
- desc += " It's made of [material.use_name]."
+ if(material_alteration & MATERIAL_ALTERATION_NAME)
+ name = padding_material ? "[padding_material.adjective_name] [initial(name)]" : "[material.adjective_name] [initial(name)]" //this is not perfect but it will do for now.
+
+ if(material_alteration & MATERIAL_ALTERATION_DESC)
+ desc = initial(desc)
+ desc += padding_material ? " It's made of [material.use_name] and covered with [padding_material.use_name]." : " It's made of [material.use_name]."
+
/obj/structure/bed/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if(istype(mover) && mover.checkpass(PASSTABLE))
diff --git a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm
index e07181d415e..ee098df1d86 100644
--- a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm
+++ b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm
@@ -2,8 +2,8 @@
name = "chair"
desc = "You sit in this. Either by will or force."
icon_state = "chair_preview"
- color = "#666666"
base_icon = "chair"
+ color = "#666666"
build_amt = 1
@@ -34,6 +34,7 @@
/obj/structure/bed/chair/post_buckle_mob()
update_icon()
+ return ..()
/obj/structure/bed/chair/update_icon()
..()
@@ -41,33 +42,42 @@
var/list/stool_cache = SSicon_cache.stool_cache
var/cache_key = "[base_icon]-[material.name]-over"
- if(isnull(stool_cache[cache_key]))
+ if(!stool_cache[cache_key])
var/image/I = image('icons/obj/furniture.dmi', "[base_icon]_over")
- if(apply_material_color)
+ if(material_alteration & MATERIAL_ALTERATION_COLOR)
I.color = material.icon_colour
I.layer = FLY_LAYER
stool_cache[cache_key] = I
add_overlay(stool_cache[cache_key])
// Padding overlay.
if(padding_material)
- var/padding_cache_key = "[base_icon]-padding-[padding_material.name]-over"
- if(isnull(stool_cache[padding_cache_key]))
+ var/padding_cache_key = "[base_icon]-[padding_material.name]-padding-over"
+ if(!stool_cache[padding_cache_key])
var/image/I = image(icon, "[base_icon]_padding_over")
- if(apply_material_color)
+ if(material_alteration & MATERIAL_ALTERATION_COLOR)
I.color = padding_material.icon_colour
I.layer = FLY_LAYER
stool_cache[padding_cache_key] = I
add_overlay(stool_cache[padding_cache_key])
- if(buckled_mob && padding_material)
- cache_key = "[base_icon]-armrest-[padding_material.name]"
- if(isnull(stool_cache[cache_key]))
+ if(buckled_mob)
+ cache_key = "[base_icon]-[material.name]-armrest"
+ if(!stool_cache[cache_key])
var/image/I = image(icon, "[base_icon]_armrest")
- I.layer = MOB_LAYER + 0.1
- if(apply_material_color)
- I.color = padding_material.icon_colour
+ I.layer = FLY_LAYER
+ if(material_alteration & MATERIAL_ALTERATION_COLOR)
+ I.color = material.icon_colour
stool_cache[cache_key] = I
add_overlay(stool_cache[cache_key])
+ if(padding_material)
+ cache_key = "[base_icon]-[padding_material.name]-padding-armrest"
+ if(!stool_cache[cache_key])
+ var/image/I = image(icon, "[base_icon]_padding_armrest")
+ I.layer = FLY_LAYER
+ if(material_alteration & MATERIAL_ALTERATION_COLOR)
+ I.color = padding_material.icon_colour
+ stool_cache[cache_key] = I
+ add_overlay(stool_cache[cache_key])
/obj/structure/bed/chair/set_dir()
. = ..()
@@ -76,45 +86,46 @@
// Leaving this in for the sake of compilation.
/obj/structure/bed/chair/comfy
+ name = "comfy chair"
desc = "It's a chair. It looks comfy."
icon_state = "comfychair_preview"
+ base_icon = "comfychair"
build_amt = 2
-/obj/structure/bed/chair/comfy/brown/Initialize(mapload,var/newmaterial)
- . = ..(mapload, MATERIAL_STEEL, MATERIAL_LEATHER)
+/obj/structure/bed/chair/comfy/brown/New(var/newloc)
+ ..(newloc, MATERIAL_STEEL, MATERIAL_LEATHER)
-/obj/structure/bed/chair/comfy/red/Initialize(var/mapload,var/newmaterial)
- . = ..(mapload, MATERIAL_STEEL, MATERIAL_CARPET)
+/obj/structure/bed/chair/comfy/red/New(var/newloc)
+ ..(newloc, MATERIAL_STEEL, MATERIAL_CARPET)
-/obj/structure/bed/chair/comfy/teal/Initialize(var/mapload,var/newmaterial)
- . = ..(mapload, MATERIAL_STEEL, MATERIAL_CLOTH_TEAL)
+/obj/structure/bed/chair/comfy/teal/New(var/newloc)
+ ..(newloc, MATERIAL_STEEL, MATERIAL_CLOTH_TEAL)
-/obj/structure/bed/chair/comfy/black/Initialize(var/mapload,var/newmaterial)
- . = ..(mapload, MATERIAL_STEEL, MATERIAL_CLOTH_BLACK)
+/obj/structure/bed/chair/comfy/black/New(var/newloc)
+ ..(newloc, MATERIAL_STEEL, MATERIAL_CLOTH_BLACK)
-/obj/structure/bed/chair/comfy/green/Initialize(var/mapload,var/newmaterial)
- . = ..(mapload, MATERIAL_STEEL, MATERIAL_CLOTH_GREEN)
+/obj/structure/bed/chair/comfy/green/New(var/newloc)
+ ..(newloc, MATERIAL_STEEL, MATERIAL_CLOTH_GREEN)
-/obj/structure/bed/chair/comfy/purp/Initialize(var/mapload,var/newmaterial)
- . = ..(mapload, MATERIAL_STEEL, MATERIAL_CLOTH_PURPLE)
+/obj/structure/bed/chair/comfy/purp/New(var/newloc)
+ ..(newloc, MATERIAL_STEEL, MATERIAL_CLOTH_PURPLE)
-/obj/structure/bed/chair/comfy/blue/Initialize(var/mapload,var/newmaterial)
- . = ..(mapload, MATERIAL_STEEL, MATERIAL_CLOTH_BLUE)
+/obj/structure/bed/chair/comfy/blue/New(var/newloc)
+ ..(newloc, MATERIAL_STEEL, MATERIAL_CLOTH_BLUE)
-/obj/structure/bed/chair/comfy/beige/Initialize(var/mapload,var/newmaterial)
- . = ..(mapload, MATERIAL_STEEL, MATERIAL_CLOTH_BEIGE)
+/obj/structure/bed/chair/comfy/beige/New(var/newloc)
+ ..(newloc, MATERIAL_STEEL, MATERIAL_CLOTH_BEIGE)
-/obj/structure/bed/chair/comfy/lime/Initialize(var/mapload,var/newmaterial)
- . = ..(mapload, MATERIAL_STEEL, MATERIAL_CLOTH_LIME)
+/obj/structure/bed/chair/comfy/lime/New(var/newloc)
+ ..(newloc, MATERIAL_STEEL, MATERIAL_CLOTH_LIME)
/obj/structure/bed/chair/office
+ name = "office chair"
+ material_alteration = MATERIAL_ALTERATION_NAME || MATERIAL_ALTERATION_DESC
anchored = 0
buckle_movable = 1
build_amt = 5
-/obj/structure/bed/chair/office/update_icon()
- return
-
/obj/structure/bed/chair/office/attackby(obj/item/W as obj, mob/user as mob)
if(istype(W,/obj/item/stack) || W.iswirecutter())
return
@@ -164,29 +175,35 @@
occupant.visible_message("[occupant] crashed into \the [A]!")
/obj/structure/bed/chair/office/light
- icon_state = "officechair_white"
+ icon_state = "officechair_white_preview"
+ base_icon = "officechair_white"
/obj/structure/bed/chair/office/dark
- icon_state = "officechair_dark"
+ icon_state = "officechair_dark_preview"
+ base_icon = "officechair_dark"
/obj/structure/bed/chair/office/bridge
name = "command chair"
desc = "It exudes authority... and looks about as comfortable as a brick."
- icon_state = "bridge"
+ icon_state = "bridge_preview"
+ base_icon = "bridge"
anchored = 1
/obj/structure/bed/chair/office/bridge/legion
name = "legion pilot seat"
desc = "A comfortable seat for a pilot."
- icon_state = "bridge_legion"
+ icon_state = "bridge_legion_preview"
+ base_icon = "bridge_legion"
/obj/structure/bed/chair/office/bridge/generic
- icon_state = "bridge_generic"
+ icon_state = "bridge_generic_preview"
+ base_icon = "bridge_generic"
/obj/structure/bed/chair/office/bridge/pilot
name = "pilot seat"
desc = "A comfortable seat for a pilot."
- icon_state = "pilot"
+ icon_state = "pilot_preview"
+ base_icon = "pilot"
/obj/structure/bed/chair/office/hover
name = "hoverchair"
@@ -202,83 +219,60 @@
/obj/structure/bed/chair/office/hover/command
icon_state = "hover_command"
-/obj/structure/bed/chair/office/Initialize()
- . = ..()
- var/image/I = image(icon, "[icon_state]_over")
- I.layer = FLY_LAYER
- add_overlay(I)
-
// Chair types
/obj/structure/bed/chair/plastic
- color = "#CCCCCC"
-/obj/structure/bed/chair/plastic/Initialize(mapload)
- . = ..(mapload, MATERIAL_PLASTIC)
+/obj/structure/bed/chair/plastic/New(var/newloc)
+ ..(newloc, MATERIAL_PLASTIC)
/obj/structure/bed/chair/wood
- name = "wooden chair"
+ name = "classic chair"
desc = "Old is never too old to not be in fashion."
- icon_state = "wooden_chair"
+ icon_state = "wooden_chair_preview"
+ base_icon = "wooden_chair"
+ material_alteration = MATERIAL_ALTERATION_NAME || MATERIAL_ALTERATION_DESC
build_amt = 3
-/obj/structure/bed/chair/wood/update_icon()
- return
-
/obj/structure/bed/chair/wood/attackby(obj/item/W as obj, mob/user as mob)
if(istype(W,/obj/item/stack) || W.iswirecutter())
return
..()
-/obj/structure/bed/chair/wood/Initialize(mapload)
- . = ..(mapload, "wood")
- var/image/I = image(icon, "[icon_state]_over")
- I.layer = FLY_LAYER
- add_overlay(I)
+/obj/structure/bed/chair/wood/New(var/newloc)
+ ..(newloc, MATERIAL_WOOD)
/obj/structure/bed/chair/wood/wings
- icon_state = "wooden_chair_wings"
+ name = "winged chair"
+ icon_state = "wooden_chair_wings_preview"
+ base_icon = "wooden_chair_wings"
/obj/structure/bed/chair/unmovable
can_dismantle = 0
/obj/structure/bed/chair/shuttle
- icon_state = "shuttlechair"
+ name = "shuttle chair"
+ icon_state = "shuttlechair_preview"
base_icon = "shuttlechair"
+ buckling_sound = 'sound/effects/metal_close.ogg'
+ material_alteration = MATERIAL_ALTERATION_NAME || MATERIAL_ALTERATION_DESC
can_dismantle = FALSE
- apply_material_color = FALSE
anchored = TRUE
-/obj/structure/bed/chair/shuttle/update_icon()
- cut_overlays()
-
- var/image/O = image(icon, "[icon_state]_over")
- O.layer = FLY_LAYER
- add_overlay(O)
-
- var/list/stool_cache = SSicon_cache.stool_cache
-
- var/cache_key = "[base_icon]_over"
- if(isnull(stool_cache[cache_key]))
- var/image/I = image('icons/obj/furniture.dmi', "[base_icon]_over")
- if(apply_material_color)
- I.color = material.icon_colour
- I.layer = FLY_LAYER
- stool_cache[cache_key] = I
- add_overlay(stool_cache[cache_key])
-
+/obj/structure/bed/chair/shuttle/post_buckle_mob()
if(buckled_mob)
- icon_state = "[base_icon]_down"
- cache_key = "[base_icon]-armrest"
- if(isnull(stool_cache[cache_key]))
- var/image/I = image(icon, "[base_icon]_armrest")
- I.layer = MOB_LAYER + 0.1
- if(apply_material_color)
- I.color = padding_material.icon_colour
- stool_cache[cache_key] = I
- add_overlay(stool_cache[cache_key])
-
+ base_icon = "shuttlechair-b"
else
- icon_state = initial(icon_state)
+ base_icon = "shuttlechair"
+ ..()
+
+/obj/structure/bed/chair/shuttle/update_icon()
+ ..()
+ if(!buckled_mob)
+ var/image/I = image(icon, "[base_icon]_special")
+ I.layer = ABOVE_MOB_LAYER
+ if(material_alteration & MATERIAL_ALTERATION_COLOR)
+ I.color = material.icon_colour
+ overlays |= I
// pool chair, to sit with your feet in the water. only works when facing south, because water overlays weirdly otherwise
/obj/structure/bed/chair/pool
@@ -299,4 +293,4 @@
if(M == buckled_mob)
M.pixel_y = -6
else
- M.pixel_y = initial(M.pixel_y)
\ No newline at end of file
+ M.pixel_y = initial(M.pixel_y)
diff --git a/code/game/objects/structures/trash_pile.dm b/code/game/objects/structures/trash_pile.dm
index 79ea0029347..bbafe855c85 100644
--- a/code/game/objects/structures/trash_pile.dm
+++ b/code/game/objects/structures/trash_pile.dm
@@ -198,7 +198,7 @@
/obj/item/clothing/gloves/brassknuckles = 3,
/obj/item/reagent_containers/syringe/drugs = 3,
/obj/item/handcuffs = 2,
- /obj/item/legcuffs = 2,
+ /obj/item/handcuffs/legcuffs = 2,
/obj/item/grenade/chem_grenade/gas = 2,
/obj/item/clothing/suit/storage/vest/heavy = 1,
/obj/item/device/radiojammer = 1,
diff --git a/code/game/objects/structures/vr/_remote_chair.dm b/code/game/objects/structures/vr/_remote_chair.dm
index 1dffe1da220..205c29514b1 100644
--- a/code/game/objects/structures/vr/_remote_chair.dm
+++ b/code/game/objects/structures/vr/_remote_chair.dm
@@ -1,7 +1,8 @@
/obj/structure/bed/chair/remote
name = "virtual reality centre"
desc = "A comfortable chair with full audio-visual transposition centres."
- icon_state = "shuttlechair_down"
+ icon_state = "shuttlechair_preview"
+ base_icon = "shuttlechair"
var/portable_type
can_dismantle = FALSE
var/remote_network // Which network does this remote control belong to?
@@ -45,7 +46,7 @@
if(H.old_mob)
to_chat(H, SPAN_WARNING("The chair rejects you! You cannot recursively control bodies."))
return
- add_overlay(image('icons/obj/furniture.dmi', src, "vr_helmet", MOB_LAYER + 1))
+ add_overlay(image('icons/obj/furniture.dmi', src, "vr_helmet", FLY_LAYER))
START_PROCESSING(SSprocessing, src)
@@ -64,4 +65,4 @@
M.vr_mob.body_return()
cut_overlays()
STOP_PROCESSING(SSprocessing, src)
- ..()
\ No newline at end of file
+ ..()
diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm
index 4c4056cf82c..7eb4cb2518e 100644
--- a/code/game/objects/structures/watercloset.dm
+++ b/code/game/objects/structures/watercloset.dm
@@ -554,7 +554,7 @@
if(!I) return //Item's been destroyed while washing
if(user.get_active_hand() != I) return //Person has switched hands or the item in their hands
- O.clean_blood()
+ I.clean_blood()
user.visible_message( \
SPAN_NOTICE("[user] washes \a [I] using \the [src]."), \
SPAN_NOTICE("You wash \a [I] using \the [src]."))
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index 5a5efa01697..16564efe830 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -529,20 +529,18 @@
/obj/structure/window/reinforced/crescent/shatter()
return
-/obj/machinery/button/windowtint
+/obj/machinery/button/switch/windowtint
name = "window tint control"
- icon = 'icons/obj/power.dmi'
- icon_state = "light0"
desc = "A remote control switch for polarized windows."
var/range = 16
-/obj/machinery/button/windowtint/attack_hand(mob/user as mob)
+/obj/machinery/button/switch/windowtint/attack_hand(mob/user as mob)
if(..())
return 1
toggle_tint()
-/obj/machinery/button/windowtint/proc/toggle_tint()
+/obj/machinery/button/switch/windowtint/proc/toggle_tint()
use_power(5)
active = !active
@@ -554,10 +552,10 @@
W.toggle()
return
-/obj/machinery/button/windowtint/power_change()
+/obj/machinery/button/switch/windowtint/power_change()
..()
if(active && !powered(power_channel))
toggle_tint()
-/obj/machinery/button/windowtint/update_icon()
+/obj/machinery/button/switch/windowtint/update_icon()
icon_state = "light[active]"
diff --git a/code/game/sound.dm b/code/game/sound.dm
index 36554906d67..ffd6cd7139b 100644
--- a/code/game/sound.dm
+++ b/code/game/sound.dm
@@ -164,6 +164,19 @@
else
return ..()
+/mob/living/carbon/human/playsound_get_environment(pressure_factor = 1.0)
+ if(protected_from_sound())
+ return PADDED_CELL
+ return ..()
+
+/mob/proc/check_sound_equipment_volume()
+ return 1
+
+/mob/living/carbon/human/check_sound_equipment_volume()
+ if(protected_from_sound())
+ return 0.6
+ return 1
+
/mob/proc/playsound_to(turf/source_turf, sound/original_sound, use_random_freq, modify_environment = TRUE, use_pressure = TRUE, required_preferences = 0, required_asfx_toggles = 0)
var/sound/S = copy_sound(original_sound)
@@ -209,8 +222,9 @@
if (modify_environment)
S.environment = playsound_get_environment(pressure_factor)
- sound_to(src, S)
+ S.volume *= check_sound_equipment_volume()
+ sound_to(src, S)
return S.volume
/mob/proc/playsound_simple(source, soundin, volume, use_random_freq = FALSE, frequency = 0, falloff = 0, use_pressure = TRUE, required_preferences = 0, required_asfx_toggles = 0)
diff --git a/code/game/turfs/simulated.dm b/code/game/turfs/simulated.dm
index 8df97c9a342..b25c6cf30f4 100644
--- a/code/game/turfs/simulated.dm
+++ b/code/game/turfs/simulated.dm
@@ -79,7 +79,7 @@
if(istype(A,/mob/living))
var/mob/living/M = A
if(src.wet_type && src.wet_amount)
- if(M.buckled || (src.wet_type == 1 && M.m_intent == "walk"))
+ if(M.buckled || (src.wet_type == 1 && M.m_intent == M_WALK))
return
//Water
diff --git a/code/game/turfs/simulated/wall_attacks.dm b/code/game/turfs/simulated/wall_attacks.dm
index 63d622c3593..36e422fade6 100644
--- a/code/game/turfs/simulated/wall_attacks.dm
+++ b/code/game/turfs/simulated/wall_attacks.dm
@@ -134,7 +134,7 @@
user.visible_message("[user] starts scraping the rot away with \the [W].", SPAN_NOTICE("You start scraping the rot away with \the [W]."))
if(do_after(user, rand(3 SECONDS, 5 SECONDS), TRUE))
user.visible_message("[user] scrapes away the rot with \the [W].", SPAN_NOTICE("You start scraping away the rot with \the [W]."))
- playsound(src, W.hitsound, 10, TRUE)
+ playsound(src, W.hitsound, W.get_clamped_volume(), TRUE)
for(var/obj/effect/overlay/wallrot/WR in src)
WR.scrape(user)
return
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 339271f7320..051c954fa58 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -254,7 +254,7 @@ var/const/enterloopsanity = 100
if(H.shoes)
var/obj/item/clothing/shoes/S = H.shoes
if(istype(S))
- S.handle_movement(src, H.m_intent == "run" ? TRUE : FALSE)
+ S.handle_movement(src, H.m_intent == M_RUN ? TRUE : FALSE)
if(S.track_footprint)
if(S.blood_DNA)
footprint_DNA = S.blood_DNA
diff --git a/code/modules/cargo/bounties/assistant.dm b/code/modules/cargo/bounties/assistant.dm
index 21878a24973..44a009e6964 100644
--- a/code/modules/cargo/bounties/assistant.dm
+++ b/code/modules/cargo/bounties/assistant.dm
@@ -80,7 +80,7 @@
reward_low = 2300
reward_high = 4000
required_count = 2
- wanted_types = list(/obj/structure/bed/chair/wheelchair)
+ wanted_types = list(/obj/structure/bed/chair/wheelchair, /obj/item/wheelchair)
/datum/bounty/item/assistant/film
name = "Camera Film"
diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm
index bfaf8cff39c..26f23e5d5a1 100644
--- a/code/modules/client/client procs.dm
+++ b/code/modules/client/client procs.dm
@@ -752,8 +752,16 @@
return
if(istype(M) && !M.incapacitated())
- var/obj/item/gun/gun = mob.get_active_hand()
- if(istype(gun) && gun.can_autofire())
- M.set_dir(get_dir(M, over_object))
- gun.Fire(get_turf(over_object), mob, params, (get_dist(over_object, mob) <= 1), FALSE)
- CHECK_TICK
+ var/obj/item/I = M.get_active_hand()
+ if(istype(I, /obj/item/gun))
+ var/obj/item/gun/gun = I
+ if(gun.can_autofire())
+ M.set_dir(get_dir(M, over_object))
+ gun.Fire(get_turf(over_object), M, params, (get_dist(over_object, M) <= 1), FALSE)
+
+ if(istype(I, /obj/item/rfd/mining) && isturf(over_object))
+ var/proximity = M.Adjacent(over_object)
+ var/obj/item/rfd/mining/RFDM = I
+ RFDM.afterattack(over_object, M, proximity, params, FALSE)
+
+ CHECK_TICK
\ No newline at end of file
diff --git a/code/modules/client/preference_setup/loadout/loadout_accessories.dm b/code/modules/client/preference_setup/loadout/loadout_accessories.dm
index 5ed1d968b2b..40459ee626d 100644
--- a/code/modules/client/preference_setup/loadout/loadout_accessories.dm
+++ b/code/modules/client/preference_setup/loadout/loadout_accessories.dm
@@ -251,7 +251,7 @@
/datum/gear/accessory/detbadge
display_name = "badge, detective"
path = /obj/item/clothing/accessory/badge/dia
- allowed_roles = list("Detective, Forensic Technician")
+ allowed_roles = list("Detective", "Forensic Technician")
/datum/gear/accessory/badge
display_name = "badge selection"
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index fdad81021d9..db8928b2826 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -88,8 +88,8 @@ datum/preferences
var/static/list/preview_screen_locs = list(
"1" = "character_preview_map:1,5:-12",
"2" = "character_preview_map:1,3:15",
- "4" = "character_preview_map:1:7,2:10",
- "8" = "character_preview_map:1:-7,1:5",
+ "4" = "character_preview_map:1:0,2:10",
+ "8" = "character_preview_map:1:0,1:5",
"BG" = "character_preview_map:1,1 to 1,5"
)
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index 3d62b21fd28..c980b4f2578 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -81,7 +81,7 @@
wearable = 1
if(!wearable && !(slot in list(slot_l_store, slot_r_store, slot_s_store)))
- to_chat(H, "Your species cannot wear [src].")
+ to_chat(H, SPAN_DANGER("Your species cannot wear [src]."))
return 0
return 1
@@ -190,7 +190,7 @@
if(!material)
return
var/turf/T = get_turf(src)
- T.visible_message("\The [src] [material.destruction_desc]!")
+ T.visible_message(SPAN_DANGER("\The [src] [material.destruction_desc]!"))
if(istype(loc, /mob/living))
var/mob/living/M = loc
if(material.shard_type == SHARD_SHARD) // Wearing glass armor is a bad idea.
@@ -212,7 +212,7 @@
if(!(def_zone in list(BP_CHEST, BP_GROIN)))
reflectchance /= 2
if(P.starting && prob(reflectchance))
- visible_message("\The [user]'s [src.name] reflects [attack_text]!")
+ visible_message(SPAN_DANGER("\The [user]'s [src.name] reflects [attack_text]!"))
// Find a turf near or on the original location to bounce to
var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2)
@@ -284,24 +284,13 @@
if(!canremove)
return
- var/obj/item/clothing/ears/O
- if(slot_flags & SLOT_TWOEARS )
- O = (H.l_ear == src ? H.r_ear : H.l_ear)
- user.u_equip(O)
- if(!istype(src,/obj/item/clothing/ears/offear))
- qdel(O)
- O = src
- else
- O = src
+ if(slot_flags & SLOT_TWOEARS)
+ var/obj/item/clothing/ears/OE = (H.l_ear == src ? H.r_ear : H.l_ear)
+ qdel(OE)
user.u_equip(src)
-
- if (O)
- user.put_in_hands(O)
- O.add_fingerprint(user)
-
- if(istype(src,/obj/item/clothing/ears/offear))
- qdel(src)
+ user.put_in_hands(src)
+ src.add_fingerprint(user)
/obj/item/clothing/ears/update_clothing_icon()
if (ismob(src.loc))
@@ -315,12 +304,22 @@
icon_state = "blocked"
slot_flags = SLOT_EARS | SLOT_TWOEARS
- New(var/obj/O)
- name = O.name
- desc = O.desc
- icon = O.icon
- icon_state = O.icon_state
- set_dir(O.dir)
+/obj/item/clothing/ears/offear/proc/copy_ear(var/obj/O)
+ name = O.name
+ desc = O.desc
+ icon = O.icon
+ icon_state = O.icon_state
+ set_dir(O.dir)
+
+/obj/item/clothing/ears/offear/attack_hand(mob/living/carbon/human/H)
+ var/obj/item/clothing/ears/OE = (H.l_ear == src ? H.r_ear : H.l_ear)
+ OE.attack_hand(H)
+ qdel(src)
+
+/obj/item/clothing/ears/offear/attackby(obj/item/I, mob/user)
+ var/mob/living/carbon/human/H = loc // we will never not be on a humanoid
+ var/obj/item/clothing/ears/OE = (H.l_ear == src ? H.r_ear : H.l_ear)
+ OE.attackby(I, user)
///////////////////////////////////////////////////////////////////////
//Gloves
@@ -372,12 +371,12 @@
..()
if(W.iswirecutter() || istype(W, /obj/item/surgery/scalpel))
if (clipped)
- to_chat(user, "\The [src] have already been clipped!")
+ to_chat(user, SPAN_NOTICE("\The [src] have already been clipped!"))
update_icon()
return
playsound(src.loc, 'sound/items/wirecutter.ogg', 100, 1)
- user.visible_message("[user] cuts the fingertips off of \the [src].","You cut the fingertips off of \the [src].")
+ user.visible_message(SPAN_WARNING("[user] cuts the fingertips off of \the [src]."),SPAN_WARNING("You cut the fingertips off of \the [src]."))
clipped = 1
siemens_coefficient += 0.25
@@ -505,9 +504,9 @@
if(!success)
return 0
else if(success == 2)
- to_chat(user, "You are already wearing a hat.")
+ to_chat(user, SPAN_WARNING("You are already wearing a hat."))
else if(success == 1)
- to_chat(user, "You crawl under \the [src].")
+ to_chat(user, SPAN_NOTICE("You crawl under \the [src]."))
return 1
/obj/item/clothing/head/return_own_image()
@@ -663,6 +662,7 @@
siemens_coefficient = 0.9
body_parts_covered = FEET
slot_flags = SLOT_FEET
+ var/blood_overlay_type = "shoe"
drop_sound = 'sound/items/drop/shoes.ogg'
pickup_sound = 'sound/items/pickup/shoes.ogg'
@@ -689,11 +689,11 @@
holding.forceMove(get_turf(usr))
if(usr.put_in_hands(holding))
- usr.visible_message("\The [usr] pulls \a [holding] out of their boot!")
+ usr.visible_message(SPAN_DANGER("\The [usr] pulls \a [holding] out of their boot!"))
holding = null
playsound(get_turf(src), 'sound/weapons/holster/unholster_knife.ogg', 25)
else
- to_chat(usr, "Your need an empty, unbroken hand to do that.")
+ to_chat(usr, SPAN_WARNING("Your need an empty, unbroken hand to do that."))
holding.forceMove(src)
if(!holding)
@@ -706,12 +706,13 @@
/obj/item/clothing/shoes/attackby(var/obj/item/I, var/mob/user)
if(can_hold_knife && is_type_in_list(I, list(/obj/item/material/shard, /obj/item/material/kitchen/utensil, /obj/item/material/knife)))
if(holding)
- to_chat(user, "\The [src] is already holding \a [holding].")
+ to_chat(user, SPAN_WARNING("\The [src] is already holding \a [holding]."))
return
user.unEquip(I)
I.forceMove(src)
holding = I
- user.visible_message("\The [user] shoves \the [I] into \the [src].")
+ user.visible_message(SPAN_NOTICE("\The [user] shoves \the [I] into \the [src]."))
+ playsound(get_turf(src), 'sound/weapons/holster/holster_knife.ogg', 25)
verbs |= /obj/item/clothing/shoes/proc/draw_knife
update_icon()
else
@@ -720,28 +721,28 @@
/obj/item/clothing/shoes/verb/toggle_layer()
set name = "Switch Shoe Layer"
set category = "Object"
+ set src in usr
+
+ if(use_check_and_message(usr))
+ return 0
if(shoes_under_pants == -1)
- usr << "\The [src] cannot be worn above your suit!"
+ to_chat(usr, SPAN_NOTICE("[src] cannot be worn above your suit!"))
return
shoes_under_pants = !shoes_under_pants
update_icon()
/obj/item/clothing/shoes/update_icon()
- overlays.Cut()
+ cut_overlays()
+ worn_overlay = null
if(holding)
- overlays += image(icon, "[icon_state]_knife")
+ worn_overlay = "knife"
+ add_overlay(overlay_image(icon, "[initial(icon_state)]_[worn_overlay]", flags=RESET_COLOR))
if(ismob(usr))
var/mob/M = usr
M.update_inv_shoes()
return ..()
-/obj/item/clothing/shoes/update_icon()
- cut_overlays()
- if(holding)
- add_overlay("[icon_state]_knife")
- return ..()
-
/obj/item/clothing/shoes/proc/handle_movement(var/turf/walking, var/running)
return
@@ -960,7 +961,7 @@
switch(sensor_mode)
if(0)
for(var/mob/V in viewers(usr, 1))
- V.show_message("[usr] disables [src.loc]'s remote sensing equipment.", 1)
+ V.show_message(SPAN_WARNING("[usr] disables [src.loc]'s remote sensing equipment."), 1)
if(1)
for(var/mob/V in viewers(usr, 1))
V.show_message("[usr] turns [src.loc]'s remote sensors to binary.", 1)
@@ -986,7 +987,7 @@
update_rolldown_status()
if(rolled_down == -1)
- to_chat(usr, "You cannot roll down [src]!")
+ to_chat(usr, SPAN_NOTICE("You cannot roll down [src]!"))
if((rolled_sleeves == 1) && !(rolled_down))
rolled_sleeves = 0
return
@@ -1015,10 +1016,10 @@
update_rollsleeves_status()
if(rolled_sleeves == -1)
- to_chat(usr, "You cannot roll up your [src]'s sleeves!")
+ to_chat(usr, SPAN_NOTICE("You cannot roll up your [src]'s sleeves!"))
return
if(rolled_down == 1)
- to_chat(usr, "You must roll up your [src] first!")
+ to_chat(usr, SPAN_NOTICE("You must roll up your [src] first!"))
return
rolled_sleeves = !rolled_sleeves
@@ -1028,14 +1029,14 @@
item_state = "[initial(item_state)]_r"
else
item_state_slots[slot_w_uniform_str] = "[worn_state]_r"
- to_chat(usr, "You roll up your [src]'s sleeves.")
+ to_chat(usr, SPAN_NOTICE("You roll up your [src]'s sleeves."))
else
body_parts_covered = initial(body_parts_covered)
if(contained_sprite)
item_state = initial(item_state)
else
item_state_slots[slot_w_uniform_str] = "[worn_state]"
- to_chat(usr, "You roll down your [src]'s sleeves.")
+ to_chat(usr, SPAN_NOTICE("You roll down your [src]'s sleeves."))
update_clothing_icon()
/obj/item/clothing/under/rank/Initialize()
diff --git a/code/modules/clothing/ears/earmuffs.dm b/code/modules/clothing/ears/earmuffs.dm
index 7a7a0e7e67b..c3925077b24 100644
--- a/code/modules/clothing/ears/earmuffs.dm
+++ b/code/modules/clothing/ears/earmuffs.dm
@@ -4,6 +4,7 @@
icon = 'icons/obj/clothing/ears.dmi'
icon_state = "earmuffs"
item_state = "earmuffs"
+ item_flags = SOUNDPROTECTION
slot_flags = SLOT_EARS | SLOT_TWOEARS
/obj/item/clothing/ears/earmuffs/headphones
@@ -12,7 +13,6 @@
var/headphones_on = 0
icon_state = "headphones_off"
item_state = "headphones"
- slot_flags = SLOT_EARS | SLOT_TWOEARS
/obj/item/clothing/ears/earmuffs/headphones/attack_self(mob/user)
togglemusic()
diff --git a/code/modules/clothing/gloves/miscellaneous.dm b/code/modules/clothing/gloves/miscellaneous.dm
index 411c9d33efa..600f9fb677a 100644
--- a/code/modules/clothing/gloves/miscellaneous.dm
+++ b/code/modules/clothing/gloves/miscellaneous.dm
@@ -1,9 +1,3 @@
-/obj/item/clothing/gloves/captain
- desc = "Regal blue gloves, with a nice gold trim. Swanky."
- name = "captain's gloves"
- icon_state = "captain"
- item_state = "captain"
-
/obj/item/clothing/gloves/cyborg
desc = "beep boop borp"
name = "cyborg gloves"
diff --git a/code/modules/clothing/head/bandanas.dm b/code/modules/clothing/head/bandanas.dm
index 5a2c81518c3..ae62a802af0 100644
--- a/code/modules/clothing/head/bandanas.dm
+++ b/code/modules/clothing/head/bandanas.dm
@@ -29,13 +29,6 @@
item_state = "bandana_red"
// Departmental bandanas. By Wowzewow (Wezzy).
-
-/obj/item/clothing/head/bandana/captain
- name = "captain's bandana"
- desc = "It's the captain's bandana with some fine nanotech lining. All hands on deck!"
- icon_state = "bandana_captain"
- item_state = "bandana_captain"
-
/obj/item/clothing/head/bandana/security
name = "security bandana"
desc = "It's a security bandana with some fine nanotech lining. Great for holding back your thick mullet while chasing perps."
diff --git a/code/modules/clothing/head/jobs.dm b/code/modules/clothing/head/jobs.dm
index 377e8293835..0c3547e3d0b 100644
--- a/code/modules/clothing/head/jobs.dm
+++ b/code/modules/clothing/head/jobs.dm
@@ -5,26 +5,6 @@
icon_state = "chefhat"
item_state = "chefhat"
-//Captain
-/obj/item/clothing/head/caphat
- name = "captain's hat"
- icon_state = "captain"
- desc = "It's good being the king."
- item_state_slots = list(
- slot_l_hand_str = "caphat",
- slot_r_hand_str = "caphat"
- )
-
-/obj/item/clothing/head/caphat/cap
- name = "captain's cap"
- desc = "You fear to wear it for the negligence it brings."
- icon_state = "capcap"
-
-/obj/item/clothing/head/caphat/formal
- name = "parade hat"
- desc = "No one in a commanding position should be without a perfect, white hat of ultimate authority."
- icon_state = "officercap"
-
//HOP
/obj/item/clothing/head/caphat/hop
name = "crew resource's hat"
diff --git a/code/modules/clothing/sets/captain.dm b/code/modules/clothing/sets/captain.dm
new file mode 100644
index 00000000000..9c3a806ba42
--- /dev/null
+++ b/code/modules/clothing/sets/captain.dm
@@ -0,0 +1,207 @@
+// Standard
+/obj/item/clothing/head/caphat
+ name = "captain's hat"
+ desc = "It's good being the king."
+ icon_state = "captain"
+ item_state_slots = list(
+ slot_l_hand_str = "caphat",
+ slot_r_hand_str = "caphat"
+ )
+
+/obj/item/clothing/head/caphat/cap
+ name = "captain's cap"
+ desc = "You fear to wear it for the negligence it brings."
+ icon = 'icons/clothing/kit/captain.dmi'
+ icon_state = "cap"
+ item_state = "cap"
+ item_state_slots = null
+ contained_sprite = TRUE
+
+/obj/item/clothing/head/bandana/captain
+ name = "captain's bandana"
+ desc = "It's the captain's bandana with some fine nanotech lining. All hands on deck!"
+ icon_state = "bandana_captain"
+ item_state = "bandana_captain"
+
+/obj/item/clothing/under/rank/captain
+ name = "captain's jumpsuit"
+ desc = "It's a blue jumpsuit with some gold markings denoting the rank of \"Captain\"."
+ icon = 'icons/clothing/kit/captain.dmi'
+ icon_state = "uniform"
+ item_state = "uniform"
+ worn_state = "uniform"
+ contained_sprite = TRUE
+ var/is_open = FALSE
+
+/obj/item/clothing/under/rank/captain/verb/fold_open()
+ set name = "Fold Collar"
+ set category = "Object"
+ set src in usr
+
+ if(use_check_and_message(usr))
+ return
+
+ if(is_open == -1)
+ to_chat(usr, SPAN_WARNING("The collar on \the [src] cannot be folded."))
+ return
+
+ is_open = !is_open
+ if(!is_open)
+ to_chat(usr, SPAN_NOTICE("You fold the collar of \the [src] closed."))
+ icon_state = initial(icon_state)
+ item_state = initial(item_state)
+ else
+ to_chat(usr, SPAN_NOTICE("You fold the collar of \the [src] open."))
+ icon_state = "[initial(icon_state)]_open"
+ item_state = "[initial(item_state)]_open"
+ update_clothing_icon()
+
+/obj/item/clothing/under/dress/dress_cap
+ name = "captain's dress uniform"
+ desc = "Feminine fashion for the style concious captain."
+ icon = 'icons/clothing/kit/captain.dmi'
+ icon_state = "uniform_fem"
+ item_state = "uniform_fem"
+ worn_state = "uniform_fem"
+ contained_sprite = TRUE
+ body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS
+ var/is_open = FALSE
+
+/obj/item/clothing/under/dress/dress_cap/verb/fold_open()
+ set name = "Fold Collar"
+ set category = "Object"
+ set src in usr
+
+ if(use_check_and_message(usr))
+ return
+
+ if(is_open == -1)
+ to_chat(usr, SPAN_WARNING("The collar on \the [src] cannot be folded."))
+ return
+
+ is_open = !is_open
+ if(!is_open)
+ to_chat(usr, SPAN_NOTICE("You fold the collar of \the [src] closed."))
+ icon_state = initial(icon_state)
+ item_state = initial(item_state)
+ else
+ to_chat(usr, SPAN_NOTICE("You fold the collar of \the [src] open."))
+ icon_state = "[initial(icon_state)]_open"
+ item_state = "[initial(item_state)]_open"
+ update_clothing_icon()
+
+/obj/item/clothing/suit/captunic/capjacket
+ name = "captain's uniform jacket"
+ desc = "A less formal jacket for everyday captain use."
+ icon = 'icons/clothing/kit/captain.dmi'
+ icon_state = "jacket"
+ item_state = "jacket"
+ contained_sprite = TRUE
+ body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS
+ flags_inv = 0
+
+/obj/item/clothing/gloves/captain
+ name = "captain's gloves"
+ desc = "Regal blue gloves, with a nice gold trim. Swanky."
+ icon = 'icons/clothing/kit/captain.dmi'
+ icon_state = "gloves"
+ item_state = "gloves"
+ contained_sprite = TRUE
+
+/obj/item/clothing/shoes/captain
+ name = "captain's shoes"
+ desc = "Super comfortable blue shoes, capable of keeping you on your feet during any station-wide disaster."
+ icon = 'icons/clothing/kit/captain.dmi'
+ icon_state = "shoes"
+ item_state = "shoes"
+ contained_sprite = TRUE
+
+/obj/item/storage/briefcase/nt/captain
+ name = "captain uniform briefcase"
+ desc = "An NT-branded briefcase containing various pieces of the captain's uniform, for use by stylish captains."
+ starts_with = list(
+ /obj/item/clothing/head/caphat = 1,
+ /obj/item/clothing/head/caphat/cap = 1,
+ /obj/item/clothing/head/bandana/captain = 1,
+ /obj/item/clothing/under/rank/captain = 1,
+ /obj/item/clothing/under/dress/dress_cap = 1,
+ /obj/item/clothing/suit/captunic/capjacket = 1,
+ /obj/item/clothing/gloves/captain = 1,
+ /obj/item/clothing/shoes/captain = 1
+ )
+
+// White
+/obj/item/clothing/head/caphat/cap/beret
+ name = "captain's white beret"
+ desc = "A beret, worn to passively instill authority at the audacity of wearing a puffy hat."
+ icon = 'icons/clothing/kit/captain_white.dmi'
+ icon_state = "beret"
+ item_state = "beret"
+
+/obj/item/clothing/head/caphat/cap/white
+ name = "captain's white cap"
+ desc = "No one in a commanding position should be without a perfect, white hat of ultimate authority."
+ icon = 'icons/clothing/kit/captain_white.dmi'
+
+/obj/item/clothing/under/rank/captain/white
+ name = "captain's white jumpsuit"
+ desc = "It's a white jumpsuit with some gold markings denoting the rank of \"Captain\"."
+ icon = 'icons/clothing/kit/captain_white.dmi'
+ is_open = -1
+
+/obj/item/clothing/gloves/captain/white
+ name = "captain's white gloves"
+ desc = "Shiny white gloves, with a nice gold trim. Swanky."
+ icon = 'icons/clothing/kit/captain_white.dmi'
+
+/obj/item/clothing/shoes/captain/white
+ name = "captain's white shoes"
+ desc = "Super comfortable white shoes, capable of keeping you on your feet during any station-wide disaster."
+ icon = 'icons/clothing/kit/captain_white.dmi'
+
+/obj/item/storage/briefcase/nt/captain_white
+ name = "captain white uniform briefcase"
+ desc = "An NT-branded briefcase containing various pieces of the captain's white uniform, for use when wine spillage is unlikely."
+ starts_with = list(
+ /obj/item/clothing/head/caphat/cap/beret = 1,
+ /obj/item/clothing/head/caphat/cap/white = 1,
+ /obj/item/clothing/under/rank/captain/white = 1,
+ /obj/item/clothing/gloves/captain/white = 1,
+ /obj/item/clothing/shoes/captain/white = 1
+ )
+
+// Formal
+/obj/item/clothing/under/captainformal
+ name = "captain's formal uniform"
+ desc = "A captain's formal-wear, for special occasions."
+ icon = 'icons/clothing/kit/captain_formal.dmi'
+ icon_state = "uniform"
+ item_state = "uniform"
+ worn_state = "uniform"
+ contained_sprite = TRUE
+
+/obj/item/clothing/suit/captunic
+ name = "captain's parade jacket"
+ desc = "Worn by a Captain to show their class."
+ icon = 'icons/clothing/kit/captain_formal.dmi'
+ icon_state = "jacket"
+ item_state = "jacket"
+ contained_sprite = TRUE
+ body_parts_covered = UPPER_TORSO|ARMS
+
+/obj/item/clothing/shoes/captain/formal
+ name = "captain's formal boots"
+ desc = "Polished boots made of strong synthleather, capable of kicking through a wall without a scratch."
+ icon = 'icons/clothing/kit/captain_formal.dmi'
+ icon_state = "boots"
+ item_state = "boots"
+ contained_sprite = TRUE
+
+/obj/item/storage/briefcase/nt/captain_formal
+ name = "captain formal uniform briefcase"
+ desc = "An NT-branded briefcase containing various pieces of the captain's formal uniform, for use to style on the peasantry."
+ starts_with = list(
+ /obj/item/clothing/under/captainformal = 1,
+ /obj/item/clothing/suit/captunic = 1,
+ /obj/item/clothing/shoes/captain/formal = 1
+ )
\ No newline at end of file
diff --git a/code/modules/clothing/shoes/jobs.dm b/code/modules/clothing/shoes/jobs.dm
index a8ef4cd27b2..f9542fd32d5 100644
--- a/code/modules/clothing/shoes/jobs.dm
+++ b/code/modules/clothing/shoes/jobs.dm
@@ -18,7 +18,8 @@
force = 3
armor = list(melee = 20, bullet = 5, laser = 5, energy = 0, bomb = 0, bio = 0, rad = 0)
siemens_coefficient = 0.75
- can_hold_knife = 1
+ can_hold_knife = TRUE
+ build_from_parts = TRUE
drop_sound = 'sound/items/drop/boots.ogg'
pickup_sound = 'sound/items/pickup/boots.ogg'
@@ -60,7 +61,8 @@
force = 3
armor = list(melee = 40, bullet = 0, laser = 0, energy = 15, bomb = 20, bio = 0, rad = 20)
siemens_coefficient = 0.75
- can_hold_knife = 1
+ can_hold_knife = TRUE
+ build_from_parts = TRUE
drop_sound = 'sound/items/drop/boots.ogg'
pickup_sound = 'sound/items/pickup/boots.ogg'
@@ -93,4 +95,3 @@
desc = "A pair of toeless dark work boots designed for use in industrial settings. Modified for species whose toes have claws."
icon_state = "workboots_dark_toeless"
item_state = "workboots_dark"
-
diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm
index b30135df142..663ea3009cd 100644
--- a/code/modules/clothing/shoes/miscellaneous.dm
+++ b/code/modules/clothing/shoes/miscellaneous.dm
@@ -26,7 +26,8 @@
armor = list(melee = 80, bullet = 60, laser = 50,energy = 25, bomb = 50, bio = 10, rad = 0)
item_flags = NOSLIP
siemens_coefficient = 0.5
- can_hold_knife = 1
+ can_hold_knife = TRUE
+ build_from_parts = TRUE
/obj/item/clothing/shoes/swat/ert
species_restricted = null
@@ -40,7 +41,8 @@
armor = list(melee = 80, bullet = 60, laser = 50,energy = 25, bomb = 50, bio = 10, rad = 0)
item_flags = NOSLIP
siemens_coefficient = 0.35
- can_hold_knife = 1
+ can_hold_knife = TRUE
+ build_from_parts = TRUE
cold_protection = FEET
min_cold_protection_temperature = SHOE_MIN_COLD_PROTECTION_TEMPERATURE
@@ -206,7 +208,8 @@ obj/item/clothing/shoes/sandal/clogs
max_heat_protection_temperature = SHOE_MAX_HEAT_PROTECTION_TEMPERATURE
armor = list(melee = 10, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 10, rad = 0)
siemens_coefficient = 0.75
- can_hold_knife = 1
+ can_hold_knife = TRUE
+ build_from_parts = TRUE
/obj/item/clothing/shoes/winter/toeless
name = "toe-less winter boots"
diff --git a/code/modules/clothing/spacesuits/rig/modules/ninja.dm b/code/modules/clothing/spacesuits/rig/modules/ninja.dm
index 302a896df99..066b00eff05 100644
--- a/code/modules/clothing/spacesuits/rig/modules/ninja.dm
+++ b/code/modules/clothing/spacesuits/rig/modules/ninja.dm
@@ -165,8 +165,8 @@
category = MODULE_SPECIAL
/obj/item/rig_module/fabricator/energy_net/engage(atom/target, mob/user)
- if(holder && holder.wearer)
- if(..(target) && target)
+ if(holder?.wearer && target)
+ if(..())
holder.wearer.Beam(target, "n_beam", , 10)
return TRUE
return FALSE
diff --git a/code/modules/clothing/spacesuits/rig/modules/utility.dm b/code/modules/clothing/spacesuits/rig/modules/utility.dm
index f68db6cc2e1..f92b5ba500c 100644
--- a/code/modules/clothing/spacesuits/rig/modules/utility.dm
+++ b/code/modules/clothing/spacesuits/rig/modules/utility.dm
@@ -490,8 +490,6 @@
jets.holder = null
jets.ion_trail.bind(jets)
-/obj/item/rig_module/foam_sprayer
-
/obj/item/rig_module/device/paperdispenser
name = "hardsuit paper dispenser"
desc = "Crisp sheets."
@@ -817,3 +815,47 @@ var/global/list/lattice_users = list()
var/mob/living/carbon/human/H = holder.wearer
to_chat(H, SPAN_NOTICE("Neural lattice disengaged. Pain receptors restored."))
lattice_users.Remove(H)
+
+/obj/item/rig_module/foam_sprayer
+ name = "mounted foam sprayer"
+ desc = "A shoulder-mounted metal foam sprayer."
+ selectable = TRUE
+ icon_state = "actuators"
+
+ interface_name = "integrated foam sprayer"
+ interface_desc = "Projects a line of metal foam where the user selects."
+
+ use_power_cost = 150
+
+ var/spray_distance = 5
+
+ category = MODULE_GENERAL
+
+/obj/item/rig_module/foam_sprayer/engage(atom/target, mob/user)
+ if(!..())
+ return FALSE
+
+ if(!target)
+ return FALSE
+
+ target = get_turf(target)
+ holder.wearer.visible_message("[user] sprays metal foam at \the [target]!", SPAN_NOTICE("You spray metal foam at \the [target]."))
+ var/list/turf_list = getline(user, get_turf(target))
+ turf_list -= turf_list[1] // remove the tile we're on
+ var/counter = spray_distance
+ var/turf/previous_turf
+ for(var/t in turf_list)
+ var/turf/T = t
+ if(!counter)
+ break
+ if(T.contains_dense_objects() || istype(T, /turf/space))
+ break
+ if(previous_turf && LinkBlocked(previous_turf, T))
+ break
+ if(counter == spray_distance)
+ new /obj/effect/effect/foam/spray/initial(t, TRUE)
+ else
+ new /obj/effect/effect/foam/spray(t, TRUE)
+ counter--
+ previous_turf = T
+ sleep(1)
\ No newline at end of file
diff --git a/code/modules/clothing/spacesuits/rig/rig.dm b/code/modules/clothing/spacesuits/rig/rig.dm
index 5b24377e2ec..a8a528911bb 100644
--- a/code/modules/clothing/spacesuits/rig/rig.dm
+++ b/code/modules/clothing/spacesuits/rig/rig.dm
@@ -539,6 +539,11 @@
wearer.update_inv_back()
return
+/obj/item/rig/get_cell()
+ if(cell)
+ return cell
+ return ..()
+
/obj/item/rig/proc/check_suit_access(var/mob/living/carbon/human/user)
if(!security_check_enabled)
diff --git a/code/modules/clothing/suits/jobs.dm b/code/modules/clothing/suits/jobs.dm
index 7305e3b1b8f..a6cc8fad029 100644
--- a/code/modules/clothing/suits/jobs.dm
+++ b/code/modules/clothing/suits/jobs.dm
@@ -41,23 +41,6 @@ obj/item/clothing/suit/apron/overalls/blue
allowed = list(/obj/item/stack/medical, /obj/item/reagent_containers/dropper, /obj/item/reagent_containers/hypospray, /obj/item/reagent_containers/syringe, \
/obj/item/device/healthanalyzer, /obj/item/device/flashlight, /obj/item/device/radio, /obj/item/tank/emergency_oxygen, /obj/item/device/breath_analyzer, /obj/item/reagent_containers/blood)
-//Captain
-/obj/item/clothing/suit/captunic
- name = "captain's parade tunic"
- desc = "Worn by a Captain to show their class."
- icon_state = "captunic"
- item_state = "captunic"
- body_parts_covered = UPPER_TORSO|ARMS
- flags_inv = HIDEJUMPSUIT
-
-/obj/item/clothing/suit/captunic/capjacket
- name = "captain's uniform jacket"
- desc = "A less formal jacket for everyday captain use."
- icon_state = "capjacket"
- item_state = "capjacket"
- body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS
- flags_inv = 0
-
//Chaplain
/obj/item/clothing/suit/chaplain_hoodie
name = "chaplain hoodie"
diff --git a/code/modules/clothing/under/jobs/civilian.dm b/code/modules/clothing/under/jobs/civilian.dm
index 3e9e663abcb..9b7bdb617a4 100644
--- a/code/modules/clothing/under/jobs/civilian.dm
+++ b/code/modules/clothing/under/jobs/civilian.dm
@@ -7,13 +7,6 @@
item_state = "ba_suit"
worn_state = "ba_suit"
-/obj/item/clothing/under/rank/captain //Alright, technically not a 'civilian' but its better then giving a .dm file for a single define.
- desc = "It's a blue jumpsuit with some gold markings denoting the rank of \"Captain\"."
- name = "captain's jumpsuit"
- icon_state = "captain"
- item_state = "b_suit"
- worn_state = "captain"
-
/obj/item/clothing/under/rank/quartermaster
name = "quartermaster's jumpsuit"
desc = "It's a jumpsuit worn by the quartermaster. It's specially designed to prevent back injuries caused by pushing paper."
diff --git a/code/modules/clothing/under/miscellaneous.dm b/code/modules/clothing/under/miscellaneous.dm
index 186c18ec640..2b7a596ab91 100644
--- a/code/modules/clothing/under/miscellaneous.dm
+++ b/code/modules/clothing/under/miscellaneous.dm
@@ -260,14 +260,6 @@
item_state = "y_suit"
worn_state = "dress_yellow"
-/obj/item/clothing/under/dress/dress_cap
- name = "captain's dress uniform"
- desc = "Feminine fashion for the style concious captain."
- icon_state = "dress_cap"
- item_state = "b_suit"
- worn_state = "dress_cap"
- body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS
-
/obj/item/clothing/under/dress/dress_hop
name = "head of personnel dress uniform"
desc = "Feminine fashion for the style concious HoP."
@@ -327,13 +319,6 @@
icon_state = "crdress"
worn_state = "crdress"
-/obj/item/clothing/under/captainformal
- name = "captain's formal uniform"
- desc = "A captain's formal-wear, for special occasions."
- icon_state = "captain_formal"
- item_state = "b_suit"
- worn_state = "captain_formal"
-
/obj/item/clothing/under/hosformalmale
name = "head of security's formal uniform"
desc = "A male head of security's formal-wear, for special occasions."
diff --git a/code/modules/custom_ka/core.dm b/code/modules/custom_ka/core.dm
index e7b8e73c73d..25835cbc80b 100644
--- a/code/modules/custom_ka/core.dm
+++ b/code/modules/custom_ka/core.dm
@@ -73,14 +73,6 @@
/obj/item/gun/custom_ka/can_wield()
return 1
-/obj/item/gun/custom_ka/toggle_wield()
- ..()
- if(wielded)
- item_state = "[initial(item_state)]_w"
- else
- item_state = initial(item_state)
- update_held_icon()
-
/obj/item/gun/custom_ka/pickup(mob/user)
..()
if(can_wield())
@@ -312,12 +304,6 @@
else
name = initial(name)
- if(wielded)
- item_state = "[initial(item_state)]_w"
- else
- item_state = initial(item_state)
- update_held_icon()
-
/obj/item/gun/custom_ka/proc/update_stats()
//pls don't bully me for this code
damage_increase = initial(damage_increase)
diff --git a/code/modules/customitems/item_defines.dm b/code/modules/customitems/item_defines.dm
index 4a2ea700257..5a314d2fa94 100644
--- a/code/modules/customitems/item_defines.dm
+++ b/code/modules/customitems/item_defines.dm
@@ -485,6 +485,8 @@ All custom items with worn sprites must follow the contained sprite system: http
which smells of the barrel it was aged in. The region and producer are on the label."
icon = 'icons/obj/custom_items/leonce_cognac.dmi'
icon_state = "leonce_cognac"
+ empty_icon_state = "leonce_cognac_empty"
+ drink_flags = UNIQUE_EMPTY_ICON_FILE | IS_GLASS
/obj/item/clothing/suit/storage/fluff/sonorous_mantle //Maraziite Throw Over - Sonorous Zouzoror - sleepywolf
diff --git a/code/modules/events/event_container.dm b/code/modules/events/event_container.dm
index da15f9470ff..c1c49ecbe01 100644
--- a/code/modules/events/event_container.dm
+++ b/code/modules/events/event_container.dm
@@ -144,6 +144,7 @@ var/global/list/severity_to_string = list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Golem Rune", /datum/event/supply_drop/golem, 20),
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "CCIA General Notice", /datum/event/ccia_general_notice, 300),
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Mundane Vermin Infestation",/datum/event/infestation, 60, list(ASSIGNMENT_JANITOR = 15, ASSIGNMENT_SECURITY = 15, ASSIGNMENT_MEDICAL = 15)),
+ new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Drone Malfunction", /datum/event/rogue_maint_drones, 10, list(ASSIGNMENT_ENGINEER = 30)),
new /datum/event_meta(EVENT_LEVEL_MUNDANE, "Visitor", /datum/event/visitor, 50, is_one_shot = TRUE)
)
@@ -166,6 +167,7 @@ var/global/list/severity_to_string = list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT
new /datum/event_meta(EVENT_LEVEL_MODERATE, "Rogue Drones", /datum/event/rogue_drone, 15, list(ASSIGNMENT_SECURITY = 15)),
new /datum/event_meta(EVENT_LEVEL_MODERATE, "Moderate Spider Infestation", /datum/event/spider_infestation/moderate, 50, list(ASSIGNMENT_SECURITY = 10)),
new /datum/event_meta(EVENT_LEVEL_MODERATE, "Moderate Vermin Infestation", /datum/event/infestation/moderate, 30, list(ASSIGNMENT_JANITOR = 15, ASSIGNMENT_SECURITY = 15, ASSIGNMENT_MEDICAL = 10)),
+ new /datum/event_meta(EVENT_LEVEL_MODERATE, "Drone Uprising", /datum/event/rogue_maint_drones, 25, list(ASSIGNMENT_ENGINEER = 30)),
new /datum/event_meta(EVENT_LEVEL_MODERATE, "Rescue Pod", /datum/event/rescue_pod, 50, list(ASSIGNMENT_MEDICAL = 10, ASSIGNMENT_SECURITY = 2), is_one_shot = TRUE),
new /datum/event_meta(EVENT_LEVEL_MODERATE, "Burglar Pod", /datum/event/rescue_pod/burglar, 50, list(ASSIGNMENT_SECURITY = 5), is_one_shot = TRUE, excluded_roundtypes = list("Extended"))
)
@@ -180,7 +182,8 @@ var/global/list/severity_to_string = list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT
new /datum/event_meta(EVENT_LEVEL_MAJOR, "Downed Ship", /datum/event/meteor_wave/downed_ship, 5, list(ASSIGNMENT_ENGINEER = 10),1),
new /datum/event_meta(EVENT_LEVEL_MAJOR, "Space Vines", /datum/event/spacevine, 75, list(ASSIGNMENT_ENGINEER = 10, ASSIGNMENT_GARDENER = 20), 1),
new /datum/event_meta(EVENT_LEVEL_MAJOR, "Spider Infestation", /datum/event/spider_infestation, 25, list(ASSIGNMENT_SECURITY = 10, ASSIGNMENT_MEDICAL = 5), 1),
- new /datum/event_meta(EVENT_LEVEL_MAJOR, "Major Vermin Infestation", /datum/event/infestation/major, 15, list(ASSIGNMENT_SECURITY = 15, ASSIGNMENT_MEDICAL = 5))
+ new /datum/event_meta(EVENT_LEVEL_MAJOR, "Major Vermin Infestation", /datum/event/infestation/major, 15, list(ASSIGNMENT_SECURITY = 15, ASSIGNMENT_MEDICAL = 5)),
+ new /datum/event_meta(EVENT_LEVEL_MAJOR, "Drone Revolution", /datum/event/rogue_maint_drones, 0, list(ASSIGNMENT_ENGINEER = 10, ASSIGNMENT_MEDICAL = 5, ASSIGNMENT_SECURITY = 5))
)
#undef ASSIGNMENT_ANY
diff --git a/code/modules/events/maint_drones.dm b/code/modules/events/maint_drones.dm
new file mode 100644
index 00000000000..0e3d2ec20dd
--- /dev/null
+++ b/code/modules/events/maint_drones.dm
@@ -0,0 +1,39 @@
+/datum/event/rogue_maint_drones/start()
+ var/drons = severity * 2 - 1
+ var/groups = rand(3, 8)
+
+ for(var/i = 0 to groups)
+ var/list/spots = get_infestation_turfs()
+ if(!LAZYLEN(spots))
+ break
+ var/turf/T = pick(spots)
+ for(var/j = 0 to drons)
+ new /mob/living/simple_animal/hostile/rogue_drone(T)
+
+/datum/event/rogue_maint_drones/announce()
+ var/stealth_chance = 70 - 20*severity
+ if(prob(stealth_chance))
+ return
+ var/naming
+ switch(severity)
+ if(EVENT_LEVEL_MUNDANE)
+ naming = "minor"
+ if(EVENT_LEVEL_MODERATE)
+ naming = "serious"
+ if(EVENT_LEVEL_MAJOR)
+ naming = "major"
+ command_announcement.Announce("A [naming] maintenance drone hijacking has occurred. The hacked drones are aggressive and dangerous. Caution is advised when entering maintenance tunnels.", "Drone Behaviour Control")
+
+/datum/event/rogue_maint_drones/proc/get_infestation_turfs()
+ var/area/location = pick_area(list(/proc/is_not_space_area, /proc/is_station_area, /proc/is_maint_area))
+ if(!location)
+ log_debug("Drone infestation failed to find a viable area. Aborting.")
+ kill()
+ return
+
+ var/list/dron_turfs = get_area_turfs(location, list(/proc/not_turf_contains_dense_objects))
+ if(!dron_turfs.len)
+ log_debug("Drone infestation failed to find viable turfs in \the [location].")
+ kill()
+ return
+ return dron_turfs
\ No newline at end of file
diff --git a/code/modules/ghostroles/spawner/atom/mining_drone.dm b/code/modules/ghostroles/spawner/atom/mining_drone.dm
index d72e9f13f87..55581fefb59 100644
--- a/code/modules/ghostroles/spawner/atom/mining_drone.dm
+++ b/code/modules/ghostroles/spawner/atom/mining_drone.dm
@@ -2,6 +2,7 @@
short_name = "mining_drone"
name = "Mining Drone"
desc = "Join in as a Mining Drone, assist the miners, get lost on the asteroid and cry synthetic tears."
+ show_on_job_select = FALSE
tags = list("Stationbound")
respawn_flag = MINISYNTH //Flag to check for when trying to spawn someone of that type (CREW, ANIMAL, MINISYNTH)
@@ -9,4 +10,4 @@
loc_type = GS_LOC_ATOM
atom_add_message = "Someone is attempting to reboot a mining drone!"
- spawn_mob = /mob/living/silicon/robot/drone/mining
+ spawn_mob = /mob/living/silicon/robot/drone/mining
\ No newline at end of file
diff --git a/code/modules/item_worth/reagents.dm b/code/modules/item_worth/reagents.dm
index 5017fda9fce..7ba4bd151cc 100644
--- a/code/modules/item_worth/reagents.dm
+++ b/code/modules/item_worth/reagents.dm
@@ -495,6 +495,45 @@
/datum/reagent/alcohol/ethanol/tallrussian
value = 0.15
+/datum/reagent/alcohol/ethanol/solarian_white
+ value = 0.125
+
+/datum/reagent/alcohol/ethanol/solarian_marine
+ value = 0.2
+
+/datum/reagent/alcohol/ethanol/cloudyoran
+ value = 0.12
+
+/datum/reagent/alcohol/ethanol/djinntea
+ value = 0.12
+
+/datum/reagent/alcohol/ethanol/permanentrevolution
+ value = 0.13
+
+/datum/reagent/alcohol/ethanol/internationale
+ value = 0.13
+
+/datum/reagent/alcohol/ethanol/dionamama
+ value = 0.2
+
+/datum/reagent/alcohol/ethanol/jovianstorm
+ value = 0.13
+
+/datum/reagent/alcohol/ethanol/primeminister
+ value = 0.13
+
+/datum/reagent/alcohol/ethanol/peacetreaty
+ value = 0.15
+
+/datum/reagent/alcohol/ethanol/fiscream
+ value = 0.14
+
+/datum/reagent/alcohol/ethanol/coffee/fiscoffee
+ value = 0.13
+
+/datum/reagent/alcohol/ethanol/fisfirebomb
+ value = 0.15
+
/datum/reagent/inaprovaline
value = 2.5
diff --git a/code/modules/materials/material_recipes.dm b/code/modules/materials/material_recipes.dm
index d10088e7f2f..be1c104c5b9 100644
--- a/code/modules/materials/material_recipes.dm
+++ b/code/modules/materials/material_recipes.dm
@@ -175,7 +175,8 @@
new /datum/stack_recipe("ore box", /obj/structure/ore_box, BUILD_AMT, time = 15, one_per_turf = 1, on_floor = 1),
new /datum/stack_recipe("wooden bucket", /obj/item/reagent_containers/glass/bucket/wood, 2, time = 4, one_per_turf = 0, on_floor = 0),
new /datum/stack_recipe("shaft", /obj/item/material/shaft, 10, time = 25, one_per_turf = 0, on_floor = 0),
- new /datum/stack_recipe("buckler donut", /obj/item/material/woodenshield, 20, time = 25, one_per_turf = 0, on_floor = 0)
+ new /datum/stack_recipe("buckler donut", /obj/item/material/woodenshield, 20, time = 25, one_per_turf = 0, on_floor = 0),
+ new /datum/stack_recipe("torch handle", /obj/item/torch, 3, time = 5, one_per_turf = 0, on_floor = 0)
))
/material/stone/generate_recipes()
diff --git a/code/modules/materials/materials.dm b/code/modules/materials/materials.dm
index 1df42efb08e..ee0b122b280 100644
--- a/code/modules/materials/materials.dm
+++ b/code/modules/materials/materials.dm
@@ -38,6 +38,7 @@
/material
var/name // Unique name for use in indexing the list.
var/display_name // Prettier name for display.
+ var/adjective_name // To override the name for subtypes and stuff.
var/use_name
var/flags = 0 // Various status modifiers.
var/sheet_singular_name = "sheet"
@@ -144,6 +145,8 @@
display_name = name
if(!use_name)
use_name = display_name
+ if(!adjective_name)
+ adjective_name = display_name
if(!shard_icon)
shard_icon = shard_type
diff --git a/code/modules/mining/mine_items.dm b/code/modules/mining/mine_items.dm
index affc6cedd32..d9d5be2f1c3 100644
--- a/code/modules/mining/mine_items.dm
+++ b/code/modules/mining/mine_items.dm
@@ -25,7 +25,7 @@
new /obj/item/shovel(src)
new /obj/item/pickaxe(src)
new /obj/item/gun/custom_ka/frame01/prebuilt(src)
- new /obj/item/ore_radar(src)
+ new /obj/item/ore_detector(src)
new /obj/item/key/minecarts(src)
new /obj/item/device/gps/mining(src)
new /obj/item/book/manual/ka_custom(src)
@@ -1144,13 +1144,14 @@ var/list/total_extraction_beacons = list()
/******************************Sculpting*******************************/
/obj/item/autochisel
name = "auto-chisel"
+ desc = "With an integrated AI chip and hair-trigger precision, this baby makes sculpting almost automatic!"
icon = 'icons/obj/contained_items/tools/drills.dmi'
- icon_state = "jackhammer"
+ icon_state = "chisel"
item_state = "jackhammer"
contained_sprite = TRUE
origin_tech = list(TECH_MATERIAL = 3, TECH_POWER = 2, TECH_ENGINEERING = 2)
- desc = "With an integrated AI chip and hair-trigger precision, this baby makes sculpting almost automatic!"
+#define TRUE_QDEL 3
/obj/structure/sculpting_block
name = "sculpting block"
desc = "A finely chiselled sculpting block, it is ready to be your canvas."
@@ -1159,89 +1160,162 @@ var/list/total_extraction_beacons = list()
density = TRUE
opacity = TRUE
anchored = FALSE
- obj_flags = OBJ_FLAG_ROTATABLE
var/sculpted = FALSE
var/mob/living/T
var/times_carved = 0
- var/last_struck = 0
+ var/busy_sculpting = FALSE
-/obj/structure/sculpting_block/attackby(obj/item/C as obj, mob/user as mob)
+/obj/structure/sculpting_block/attackby(obj/item/C, mob/user)
+ if(C.iswrench())
+ visible_message("[user] starts to [anchored ? "un" : ""]anchor \the [src].", SPAN_NOTICE("You start to [anchored ? "un" : ""]anchor \the [src]."))
+ if(do_after(user, 5 SECONDS, TRUE))
+ playsound(src.loc, C.usesound, 100, 1)
+ anchored = !anchored
- if (C.iswrench())
- playsound(src.loc, C.usesound, 100, 1)
- to_chat(user, "You [anchored ? "un" : ""]anchor the [name].")
- anchored = !anchored
+ else if(istype(C, /obj/item/autochisel))
+ if(sculpted)
+ to_chat(user, SPAN_WARNING("\The [src] has already been sculpted!"))
+ return
+ if(busy_sculpting)
+ to_chat(user, SPAN_WARNING("Someone's already busy sculpting \the [src]!"))
+ return
- if(istype(C, /obj/item/autochisel))
- if(!sculpted)
- if(last_struck)
- return
+ busy_sculpting = TRUE
+
+ var/choice = input(user, "What would you like to sculpt?", "Sculpting Options") as null|anything in list("Sculpture", "Ladder")
+ if(!choice)
+ busy_sculpting = FALSE
+ return
+ choice = lowertext(choice)
+ var/result = sculpture_options(choice, user)
+ if(!result)
+ busy_sculpting = FALSE
+ return
+
+ user.visible_message(SPAN_NOTICE("\The [user] begins sculpting."), SPAN_NOTICE("You begin sculpting."))
+
+ if(prob(25))
+ playsound(loc, 'sound/items/screwdriver.ogg', 20, TRUE)
+ else
+ playsound(loc, /decl/sound_category/pickaxe_sound, 20, TRUE)
+
+ var/successfully_sculpted = FALSE
+ while(do_after(user, 2 SECONDS) && sculpture_process_check(choice, user))
+ if(times_carved <= 9)
+ times_carved++
+ playsound(loc, /decl/sound_category/pickaxe_sound, 20, TRUE)
+ continue
+ successfully_sculpted = TRUE
+ break
+
+ busy_sculpting = FALSE
+
+ if(!successfully_sculpted)
+ return
+
+ user.visible_message(SPAN_NOTICE("\The [user] finishes sculpting their magnum opus!"), SPAN_NOTICE("You finish sculpting a masterpiece."))
+ sculpted = finish_sculpture(choice, user)
+ if(sculpted == TRUE_QDEL)
+ qdel(src)
+
+/obj/structure/sculpting_block/proc/sculpture_options(var/choice, var/mob/user)
+ switch(choice)
+ if("sculpture")
+ var/mob/living/old_T
+ if(T)
+ old_T = T
+
+ var/list/choices = list()
+ for(var/mob/living/M in view(7,user))
+ choices += M
+ T = input(user, "Who do you wish to sculpt?", "Sculpt Options") as null|anything in choices
if(!T)
- var/list/choices = list()
- for(var/mob/living/M in view(7,user))
- choices += M
- T = input(user,"Who do you wish to sculpt?") as null|anything in choices
- if(!T)
- to_chat(user, SPAN_NOTICE("You decide against sculpting for now."))
- user.visible_message(SPAN_NOTICE("\The [user] begins sculpting."),
- SPAN_NOTICE("You begin sculpting."))
-
- var/sculpting_coefficient = get_dist(user,T)
- if(sculpting_coefficient <= 0)
- sculpting_coefficient = 1
+ to_chat(user, SPAN_NOTICE("You decide against sculpting for now."))
+ return FALSE
+ var/sculpting_coefficient = max(get_dist(user, T), 1)
if(sculpting_coefficient >= 7)
to_chat(user, SPAN_WARNING("You hardly remember what \the [T] really looks like! Bah!"))
T = null
+ return
- user.visible_message(SPAN_NOTICE("\The [user] carves away at the sculpting block!"),
- SPAN_NOTICE("You continue sculpting."))
+ if(old_T && T != old_T)
+ times_carved = 0
- if(prob(25))
- playsound(user, 'sound/items/screwdriver.ogg', 20, TRUE)
+ return TRUE
+ if("ladder")
+ var/turf/above = GET_ABOVE(src)
+ if(!above)
+ to_chat(user, SPAN_WARNING("There is nothing above you to make a ladder towards."))
+ return FALSE
+ if(!isopenturf(above))
+ to_chat(user, SPAN_WARNING("The tile above you isn't open and can't accomodate a ladder."))
+ return FALSE
+ return TRUE
+
+/obj/structure/sculpting_block/proc/sculpture_process_check(var/choice, var/mob/user)
+ switch(choice)
+ if("sculpture")
+ if(!QDELETED(T) && get_dist(user, T) < 8)
+ return TRUE
+ return FALSE
+ if("ladder")
+ var/turf/above = GET_ABOVE(src)
+ if(!above)
+ to_chat(user, SPAN_WARNING("There is nothing above you to make a ladder towards."))
+ return FALSE
+ if(!isopenturf(above))
+ to_chat(user, SPAN_WARNING("The tile above you isn't open and can't accomodate a ladder."))
+ return FALSE
+ return TRUE
+
+/obj/structure/sculpting_block/proc/finish_sculpture(var/choice, var/mob/user)
+ switch(choice)
+ if("sculpture")
+ appearance = T
+ color = list( // for anyone interested, this is called a color matrix
+ 0.35, 0.3, 0.25,
+ 0.35, 0.3, 0.25,
+ 0.35, 0.3, 0.25
+ )
+ pixel_y += 8
+
+ var/image/pedestal_underlay = image('icons/obj/mining.dmi', icon_state = "pedestal")
+ pedestal_underlay.appearance_flags = RESET_COLOR
+ pedestal_underlay.pixel_y -= 8
+ underlays += pedestal_underlay
+
+ obj_flags = OBJ_FLAG_ROTATABLE
+
+ var/title = sanitize(input(usr, "If you would like to name your art, do so here.", "Christen Your Sculpture", "") as text|null)
+ if(title)
+ name = title
else
- playsound(user, "sound/weapons/chisel[rand(1,2)].ogg", 20, TRUE)
- spawn(3)
- playsound(user, "sound/weapons/chisel[rand(1,2)].ogg", 20, TRUE)
- spawn(3)
- playsound(user, "sound/weapons/chisel[rand(1,2)].ogg", 20, TRUE)
+ name = T.name
- last_struck = 1
- if(do_after(user,(20)))
- last_struck = 0
- if(times_carved <= 9)
- times_carved += 1
- if(times_carved < 1)
- to_chat(user, SPAN_NOTICE("You review your work and see there is more to do."))
- return
- else
- sculpted = TRUE
- user.visible_message(SPAN_NOTICE("\The [user] finishes sculpting their magnum opus!"),
- SPAN_NOTICE("You finish sculpting a masterpiece."))
- src.appearance = T
- src.color = list(
- 0.35, 0.3, 0.25,
- 0.35, 0.3, 0.25,
- 0.35, 0.3, 0.25
- )
- src.pixel_y += 8
- var/image/pedestal_underlay = image('icons/obj/mining.dmi', icon_state = "pedestal")
- pedestal_underlay.appearance_flags = RESET_COLOR
- pedestal_underlay.pixel_y -= 8
- src.underlays += pedestal_underlay
- var/title = sanitize(input(usr, "If you would like to name your art, do so here.", "Christen Your Sculpture", "") as text|null)
- if(title)
- name = title
- else
- name = "*[T.name]*"
- var/legend = sanitize(input(usr, "If you would like to describe your art, do so here.", "Story Your Sculpture", "") as message|null)
- if(legend)
- desc = legend
- else
- desc = "This is a sculpture of [T.name]. All craftsmanship is of the highest quality. It is decorated with rock and more rock. It is covered with rock. On the item is an image of a rock. The rock is [T.name]."
+ var/legend = sanitize(input(usr, "If you would like to describe your art, do so here.", "Story Your Sculpture", "") as message|null)
+ if(legend)
+ desc = legend
else
- last_struck = 0
- return
+ desc = "This is a sculpture of [T.name]. All craftsmanship is of the highest quality. It is decorated with rock and more rock. It is covered with rock. On the item is an image of a rock. The rock is [T.name]."
+
+ T = null // null T out, we don't need the ref to them anymore
+
+ return TRUE
+ if("ladder")
+ var/turf/above = GET_ABOVE(src)
+ if(!above)
+ to_chat(user, SPAN_WARNING("There is nothing above you to make a ladder towards."))
+ return FALSE
+ if(!isopenturf(above))
+ to_chat(user, SPAN_WARNING("The tile above you isn't open and can't accomodate a ladder."))
+ return FALSE
+
+ new /obj/structure/ladder/up/mining(get_turf(src))
+ new /obj/structure/ladder/mining(above)
+ return TRUE_QDEL
+
+#undef TRUE_QDEL
/******************************Gains Boroughs*******************************/
diff --git a/code/modules/mining/minebot.dm b/code/modules/mining/minebot.dm
index 24b217b814d..5e692f2080a 100644
--- a/code/modules/mining/minebot.dm
+++ b/code/modules/mining/minebot.dm
@@ -116,6 +116,12 @@
start_self_destruct(TRUE)
return TRUE
+/mob/living/silicon/robot/drone/mining/update_robot_light()
+ if(lights_on)
+ set_light(5, 1, LIGHT_COLOR_FIRE, angle = LIGHT_OMNI)
+ else
+ set_light(0)
+
/**********************Minebot Upgrades**********************/
/obj/item/device/mine_bot_upgrade
@@ -185,3 +191,10 @@
M.recalculate_synth_capacities()
qdel(src)
return TRUE
+
+/mob/living/silicon/robot/drone/mining/roundstart/Initialize()
+ . = ..()
+ if(SSticker.current_state == GAME_STATE_PLAYING)
+ request_player()
+ else
+ LAZYADD(SSatoms.late_misc_firers, src)
\ No newline at end of file
diff --git a/code/modules/mining/ore_detector.dm b/code/modules/mining/ore_detector.dm
new file mode 100644
index 00000000000..be8fdc39a64
--- /dev/null
+++ b/code/modules/mining/ore_detector.dm
@@ -0,0 +1,119 @@
+/obj/item/ore_detector
+ name = "ore detector"
+ desc = "A device capable of locating and displaying ores to the average untrained hole explorer."
+ icon = 'icons/obj/contained_items/tools/ore_scanner.dmi'
+ icon_state = "ore_scanner"
+ item_state = "ore_scanner"
+ w_class = ITEMSIZE_SMALL
+ slot_flags = SLOT_BELT
+ force = 1
+ var/active = FALSE
+ var/datum/weakref/our_user
+ var/list/search_ores = list()
+ var/ping_rate = 4 SECONDS
+ var/last_ping = 0
+
+ var/list/ore_names
+
+/obj/item/ore_detector/examine(mob/user, distance)
+ if(..(user, 1))
+ to_chat(user, FONT_SMALL(SPAN_NOTICE("Alt-click to set the ore you wish to search for.")))
+
+/obj/item/ore_detector/Destroy()
+ deactivate()
+ return ..()
+
+/obj/item/ore_detector/update_icon()
+ icon_state = "ore_scanner[active ? "-active" : ""]"
+
+/obj/item/ore_detector/attack_self(mob/user)
+ if(!length(search_ores))
+ to_chat(user, SPAN_WARNING("You haven't set an ore to search for yet!"))
+ return
+ active = !active
+ var/msg = "You [active ? "activate" : "deactivate"] \the [src]."
+ to_chat(user, SPAN_NOTICE(msg))
+ if(active)
+ activate(user)
+ else
+ deactivate()
+
+/obj/item/ore_detector/AltClick(mob/user)
+ if(!length(ore_names))
+ ore_names = list()
+ for(var/ore_n in ore_data)
+ var/ore/O = ore_data[ore_n]
+ var/ore_name = O.display_name
+ ore_names += ore_name
+ if(loc == user)
+ var/datum/vueui/ui = SSvueui.get_open_ui(user, src)
+ if(!ui)
+ ui = new(user, src, "devices-oredetector", 320, 220, capitalize_first_letters(name))
+ ui.open()
+ else
+ return ..()
+
+/obj/item/ore_detector/vueui_data_change(var/list/data, var/mob/user, var/datum/vueui/ui)
+ if(!data)
+ data = list()
+
+ VUEUI_SET_CHECK_LIST(data["ore_names"], ore_names, ., data)
+ VUEUI_SET_CHECK_LIST(data["selected_ores"], search_ores, ., data)
+
+/obj/item/ore_detector/Topic(href, href_list)
+ ..()
+
+ if(href_list["chosen_ore"])
+ if(href_list["chosen_ore"] in search_ores)
+ search_ores -= href_list["chosen_ore"]
+ else
+ search_ores += href_list["chosen_ore"]
+
+ SSvueui.check_uis_for_change(src)
+
+/obj/item/ore_detector/process()
+ if(last_ping + ping_rate > world.time)
+ return
+ if(isnull(our_user))
+ deactivate()
+ return
+ var/mob/M = our_user.resolve()
+ if(loc != M && loc.loc != M)
+ deactivate()
+ return
+ last_ping = world.time
+ var/turf/our_turf = get_turf(src)
+ for(var/turf/simulated/mineral/mine_turf in RANGE_TURFS(7, our_turf))
+ if(isnull(our_user)) // in the event it's dropped midsweep
+ return
+ if(mine_turf.mineral && (mine_turf.mineral.display_name in search_ores))
+ var/image/ore_ping = image(icon = 'icons/obj/contained_items/tools/ore_scanner.dmi', icon_state = "signal_overlay", loc = our_turf, layer = OBFUSCATION_LAYER + 0.1)
+ pixel_shift_to_turf(ore_ping, our_turf, mine_turf)
+ M << ore_ping
+ QDEL_IN(ore_ping, 4 SECONDS)
+
+/obj/item/ore_detector/emp_act()
+ deactivate()
+
+/obj/item/ore_detector/proc/activate(var/mob/user)
+ if(!length(search_ores))
+ return
+ START_PROCESSING(SSprocessing, src)
+ our_user = WEAKREF(user)
+ update_icon()
+
+/obj/item/ore_detector/proc/deactivate()
+ STOP_PROCESSING(SSprocessing, src)
+ our_user = null
+ update_icon()
+
+/obj/item/ore_detector/throw_at()
+ ..()
+ deactivate()
+
+/obj/item/ore_detector/dropped()
+ ..()
+ deactivate()
+
+/obj/item/ore_detector/on_give()
+ deactivate()
\ No newline at end of file
diff --git a/code/modules/mining/satchel_ore_boxdm.dm b/code/modules/mining/satchel_ore_boxdm.dm
index dda70db2d5c..02860845b76 100644
--- a/code/modules/mining/satchel_ore_boxdm.dm
+++ b/code/modules/mining/satchel_ore_boxdm.dm
@@ -16,16 +16,16 @@
/obj/structure/ore_box/attackby(obj/item/W, mob/user)
if(istype(W, /obj/item/ore))
user.drop_from_inventory(W, src)
- if(istype(W, /obj/item/storage/bag/ore))
- var/obj/item/storage/bag/ore/satchel = W
- if(satchel.linked_beacon)
- if(!warp_core)
- to_chat(user, SPAN_WARNING("\The [src] doesn't have a warp beacon!"))
+ else if(istype(W, /obj/item/storage))
+ if(istype(W, /obj/item/storage/bag/ore))
+ var/obj/item/storage/bag/ore/satchel = W
+ if(satchel.linked_beacon)
+ if(!warp_core)
+ to_chat(user, SPAN_WARNING("\The [src] doesn't have a warp beacon!"))
+ return
+ satchel.linked_box = src
+ to_chat(user, SPAN_NOTICE("You link \the [satchel] to \the [src]."))
return
- satchel.linked_box = src
- to_chat(user, SPAN_NOTICE("You link \the [satchel] to \the [src]."))
- return
- if(istype(W, /obj/item/storage))
var/obj/item/storage/S = W
S.hide_from(user)
for(var/obj/item/ore/O in S.contents)
@@ -33,13 +33,23 @@
CHECK_TICK
S.post_remove_from_storage_deferred(loc, user)
to_chat(user, SPAN_NOTICE("You empty the satchel into the box."))
- if(istype(W, /obj/item/warp_core))
+ else if(istype(W, /obj/item/warp_core))
if(warp_core)
to_chat(user, SPAN_WARNING("\The [src] already has a warp core attached!"))
return
user.drop_from_inventory(W, src)
warp_core = W
to_chat(user, SPAN_NOTICE("You carefully attach \the [W] to \the [src], connecting it to the bluespace network."))
+ else if(istype(W, /obj/item/gripper/miner)) // myazaki's gonna be so mad at me
+ var/obj/item/gripper/miner/GM = W
+ if(!warp_core)
+ to_chat(user, SPAN_WARNING("\The [src] has no warp core to detach."))
+ return
+ // we don't need to check if it has a held item because the gripper code attacks with the held item if it has one, not the gripper itself
+ warp_core.forceMove(get_turf(src))
+ GM.grip_item(warp_core, user, FALSE)
+ to_chat(user, SPAN_NOTICE("You detach \the [warp_core] from \the [src], disconnecting it from the bluespace network."))
+ warp_core = null
update_ore_count()
return
diff --git a/code/modules/mob/abstract/new_player/sprite_accessories.dm b/code/modules/mob/abstract/new_player/sprite_accessories.dm
index 1df9893615b..04c8a4de958 100644
--- a/code/modules/mob/abstract/new_player/sprite_accessories.dm
+++ b/code/modules/mob/abstract/new_player/sprite_accessories.dm
@@ -2943,6 +2943,13 @@ Follow by example and make good judgement based on length which list to include
species_allowed = list(/datum/species/skrell,/datum/species/zombie/skrell)
do_colouration = FALSE
+ skr_axiori_eyes
+ name = "Axiori Eyes"
+ icon_state = "skr_axiori_eyes"
+ body_parts = list(BP_HEAD)
+ species_allowed = list(/datum/species/skrell, /datum/species/zombie/skrell)
+ do_colouration = FALSE
+
skr_arms
name = "Skrell Arms"
icon_state = "skrell_arms"
diff --git a/code/modules/mob/floating_messages.dm b/code/modules/mob/floating_messages.dm
index 50f391bd53f..7778072ba12 100644
--- a/code/modules/mob/floating_messages.dm
+++ b/code/modules/mob/floating_messages.dm
@@ -52,13 +52,7 @@ var/list/floating_chat_colors = list()
I.plane = FLOAT_PLANE
I.layer = HUD_LAYER - 0.01
I.pixel_x = -round(I.maptext_width/2) + 16
-
- if(ishuman(holder))
- var/mob/living/carbon/human/H = holder
- if(H.lying)
- var/matrix/M = matrix()
- M.Turn(-90)
- animate(I, transform = M, time = 1)
+ I.appearance_flags = RESET_COLOR|RESET_ALPHA|RESET_TRANSFORM
style = "font-family: 'Small Fonts'; -dm-text-outline: 1 black; font-size: [size]px; [style]"
I.maptext = "
[message]
"
@@ -75,13 +69,4 @@ var/list/floating_chat_colors = list()
/proc/remove_floating_text(atom/movable/holder, image/I)
animate(I, 2, pixel_y = I.pixel_y + 10, alpha = 0)
- LAZYREMOVE(holder.stored_chat_text, I)
-
-/mob/proc/handle_floating_message_orientation()
- return
-
-/mob/living/carbon/human/handle_floating_message_orientation()
- var/matrix/M = matrix()
- M.Turn(lying ? -90 : 0)
- for(var/thing in stored_chat_text)
- animate(thing, transform = M, time = ANIM_LYING_TIME)
\ No newline at end of file
+ LAZYREMOVE(holder.stored_chat_text, I)
\ No newline at end of file
diff --git a/code/modules/mob/language/outsider.dm b/code/modules/mob/language/outsider.dm
index 921dcb3db61..6645192969c 100644
--- a/code/modules/mob/language/outsider.dm
+++ b/code/modules/mob/language/outsider.dm
@@ -7,11 +7,12 @@
flags = RESTRICTED | HIVEMIND
/datum/language/ling/broadcast(var/mob/living/speaker,var/message,var/speaker_mask)
-
- if(speaker.mind && speaker.mind.changeling)
- ..(speaker,message,speaker.mind.changeling.changelingID)
- else
- ..(speaker,message)
+ if(speaker.mind)
+ var/datum/changeling/changeling = speaker.mind.antag_datums[MODE_CHANGELING]
+ if(changeling)
+ ..(speaker,message, changeling.changelingID)
+ return
+ ..(speaker,message)
/datum/language/corticalborer
name = LANGUAGE_BORER
diff --git a/code/modules/mob/living/carbon/alien/diona/diona_nymph.dm b/code/modules/mob/living/carbon/alien/diona/diona_nymph.dm
index bfc02df877a..789c3c7a52d 100644
--- a/code/modules/mob/living/carbon/alien/diona/diona_nymph.dm
+++ b/code/modules/mob/living/carbon/alien/diona/diona_nymph.dm
@@ -76,9 +76,9 @@
/mob/living/carbon/alien/diona/movement_delay()
. = ..()
switch(m_intent)
- if("walk")
+ if(M_WALK)
. += 3
- if("run")
+ if(M_RUN)
species.handle_sprint_cost(src,.+config.walk_speed)
/mob/living/carbon/alien/diona/ex_act(severity)
diff --git a/code/modules/mob/living/carbon/brain/brain_item.dm b/code/modules/mob/living/carbon/brain/brain_item.dm
index df875e02753..e3103e0d359 100644
--- a/code/modules/mob/living/carbon/brain/brain_item.dm
+++ b/code/modules/mob/living/carbon/brain/brain_item.dm
@@ -19,7 +19,7 @@
/obj/item/organ/internal/brain/slime
name = "slime core"
desc = "A complex, organic knot of jelly and crystalline particles."
- robotic = 2
+ robotic = ROBOTIC_MECHANICAL
icon = 'icons/mob/npc/slimes.dmi'
icon_state = "green slime extract"
can_lobotomize = 0
@@ -27,7 +27,7 @@
/obj/item/organ/internal/brain/golem
name = "chelm"
desc = "A tightly furled roll of paper, covered with indecipherable runes."
- robotic = 2
+ robotic = ROBOTIC_MECHANICAL
icon = 'icons/obj/library.dmi'
icon_state = "scroll"
item_state = "scroll"
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index 3d5a1d006e3..1f55822d256 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -62,7 +62,7 @@
if(src.hydration)
adjustHydrationLoss(hydration_loss*0.1)
- if((FAT in src.mutations) && src.m_intent == "run" && src.bodytemperature <= 360)
+ if((FAT in src.mutations) && src.m_intent == M_RUN && src.bodytemperature <= 360)
src.bodytemperature += 2
// Moving around increases germ_level faster
diff --git a/code/modules/mob/living/carbon/carbon_defense.dm b/code/modules/mob/living/carbon/carbon_defense.dm
index 81de9ccfe1b..df5cb343ee4 100644
--- a/code/modules/mob/living/carbon/carbon_defense.dm
+++ b/code/modules/mob/living/carbon/carbon_defense.dm
@@ -173,7 +173,7 @@
user.visible_message("\The [user] cuts [src]'s neck open with \the [W]!")
if(W.hitsound)
- playsound(loc, W.hitsound, 50, 1, -1)
+ playsound(loc, W.hitsound, W.get_clamped_volume(), 1, -1)
if(ishuman(src))
var/mob/living/carbon/human/H = src
@@ -207,4 +207,4 @@
/mob/living/carbon/Resting(amount)
help_up_offer = 0
- ..()
\ No newline at end of file
+ ..()
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index f2055d9ea3e..763e2df0802 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -45,8 +45,17 @@
if(istype(w_uniform,/obj/item/clothing/under))
var/obj/item/clothing/under/U = w_uniform
if(LAZYLEN(U.accessories))
- tie_msg += ". Attached to it is [lowertext(english_list(U.accessories))]"
- tie_msg_warn += "! Attached to it is [lowertext(english_list(U.accessories))]"
+ tie_msg += ". Attached to it is"
+ tie_msg_warn += "! Attached to it is"
+ var/list/accessory_descs = list()
+ for(var/accessory in U.accessories)
+ if(istype(accessory, /obj/item/clothing/accessory/holster)) //so you can't see what kind of gun a holster is holding from afar
+ accessory_descs += "\a [accessory]"
+ else
+ accessory_descs += "\a [accessory]"
+
+ tie_msg += " [lowertext(english_list(accessory_descs))]"
+ tie_msg_warn += " [lowertext(english_list(accessory_descs))]"
if(w_uniform.blood_color)
msg += "[get_pronoun("He")] [get_pronoun("is")] wearing [icon2html(w_uniform, user)] [w_uniform.gender==PLURAL?"some":"a"] [fluid_color_type_map(w_uniform.blood_color)]-stained [w_uniform.name][tie_msg_warn].\n"
@@ -71,8 +80,14 @@
if(istype(wear_suit,/obj/item/clothing/suit))
var/obj/item/clothing/suit/U = wear_suit
if(LAZYLEN(U.accessories))
- tie_msg += ". Attached to it is [lowertext(english_list(U.accessories))]"
- tie_msg_warn += "! Attached to it is [lowertext(english_list(U.accessories))]"
+ tie_msg += ". Attached to it is"
+ tie_msg_warn += "! Attached to it is"
+ var/list/accessory_descs = list()
+ for(var/accessory in U.accessories)
+ accessory_descs += "\a [accessory]"
+
+ tie_msg += " [lowertext(english_list(accessory_descs))]"
+ tie_msg_warn += " [lowertext(english_list(accessory_descs))]"
if(wear_suit.blood_color)
msg += "[get_pronoun("He")] [get_pronoun("is")] wearing [icon2html(wear_suit, user)] [wear_suit.gender==PLURAL?"some":"a"] [fluid_color_type_map(wear_suit.blood_color)]-stained [wear_suit.name][tie_msg_warn].\n"
@@ -124,10 +139,11 @@
//handcuffed?
if(handcuffed)
- if(istype(handcuffed, /obj/item/handcuffs/cable))
- msg += "[get_pronoun("He")] [get_pronoun("is")] [icon2html(handcuffed, user)] restrained with cable!\n"
- else
- msg += "[get_pronoun("He")] [get_pronoun("is")] [icon2html(handcuffed, user)] handcuffed!\n"
+ msg += "[get_pronoun("He")] [get_pronoun("is")] [icon2html(handcuffed, user)] handcuffed!\n"
+
+ //handcuffed?
+ if(legcuffed)
+ msg += "[get_pronoun("He")] [get_pronoun("is")] [icon2html(legcuffed, user)] legcuffed!\n"
//buckled
if(buckled)
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 6f84d3e79c9..2735ffd527f 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -198,6 +198,10 @@
stat("Internal Atmosphere Info", internal.name)
stat("Tank Pressure", internal.air_contents.return_pressure())
stat("Distribution Pressure", internal.distribute_pressure)
+
+ var/obj/item/organ/internal/cell/IC = internal_organs_by_name[BP_CELL]
+ if(IC && IC.cell)
+ stat("Battery charge:", "[IC.get_charge()]/[IC.cell.maxcharge]")
if(back && istype(back,/obj/item/rig))
var/obj/item/rig/suit = back
@@ -206,12 +210,14 @@
stat(null, "Suit charge: [cell_status]")
if(mind)
- if(mind.vampire)
- stat("Usable Blood", mind.vampire.blood_usable)
- stat("Total Blood", mind.vampire.blood_total)
- if(mind.changeling)
- stat("Chemical Storage", mind.changeling.chem_charges)
- stat("Genetic Damage Time", mind.changeling.geneticdamage)
+ var/datum/vampire/vampire = mind.antag_datums[MODE_VAMPIRE]
+ if(vampire)
+ stat("Usable Blood", vampire.blood_usable)
+ stat("Total Blood", vampire.blood_total)
+ var/datum/changeling/changeling = mind.antag_datums[MODE_CHANGELING]
+ if(changeling)
+ stat("Chemical Storage", changeling.chem_charges)
+ stat("Genetic Damage Time", changeling.geneticdamage)
/mob/living/carbon/human/ex_act(severity)
if(!blinded)
@@ -1336,16 +1342,17 @@
if(organ.status & ORGAN_SPLINTED) //Splints prevent movement.
continue
for(var/obj/item/O in organ.implants)
- if(!istype(O,/obj/item/implant) && prob(5)) //Moving with things stuck in you could be bad.
- // All kinds of embedded objects cause bleeding.
+ if(m_intent == "run" && !istype(O, /obj/item/implant) && prob(5)) //Moving quickly with things stuck in you could be bad.
if(!can_feel_pain())
to_chat(src, SPAN_WARNING("You feel [O] moving inside your [organ.name]."))
else
var/msg = pick( \
SPAN_WARNING("A spike of pain jolts your [organ.name] as you bump [O] inside."), \
SPAN_WARNING("Your movement jostles [O] in your [organ.name] painfully."), \
- SPAN_WARNING("Your movement jostles [O] in your [organ.name] painfully."))
+ SPAN_WARNING("Your movement jostles [O] in your [organ.name] painfully.") \
+ )
custom_pain(msg, 10, 10, organ)
+ organ.take_damage(rand(1, 3), 0, DAM_EDGE)
/mob/living/carbon/human/verb/check_pulse()
set category = "Object"
@@ -2012,8 +2019,10 @@
/mob/living/carbon/human/get_accent_icon(var/datum/language/speaking, var/mob/hearer, var/force_accent)
var/used_accent = accent //starts with the mob's default accent
- if(mind?.changeling)
- used_accent = mind.changeling.mimiced_accent
+ if(mind)
+ var/datum/changeling/changeling = mind.antag_datums[MODE_CHANGELING]
+ if(changeling?.mimiced_accent)
+ used_accent = changeling.mimiced_accent
if(istype(back,/obj/item/rig)) //checks for the rig voice changer module
var/obj/item/rig/rig = back
diff --git a/code/modules/mob/living/carbon/human/human_attackhand.dm b/code/modules/mob/living/carbon/human/human_attackhand.dm
index 4fac2a1d9f5..375f0d7056d 100644
--- a/code/modules/mob/living/carbon/human/human_attackhand.dm
+++ b/code/modules/mob/living/carbon/human/human_attackhand.dm
@@ -345,12 +345,18 @@
if(I_DISARM)
var/disarm_cost
var/usesStamina
+ var/obj/item/organ/internal/cell/cell = M.internal_organs_by_name[BP_CELL]
+ var/obj/item/cell/potato
+ if(cell)
+ potato = cell.cell
if(M.max_stamina > 0)
disarm_cost = M.max_stamina / 6
usesStamina = TRUE
else if(M.max_stamina <= 0)
- disarm_cost = M.max_nutrition / 8
+ if(M.isSynthetic())
+ disarm_cost = potato.maxcharge / 24
+ disarm_cost = M.max_nutrition / 6
usesStamina = FALSE
if(usesStamina)
@@ -378,14 +384,15 @@
if(usesStamina)
M.stamina = M.stamina - disarm_cost //attempting to knock something out of someone's hands, or pushing them over, is exhausting!
M.stamina = Clamp(M.stamina, 0, M.max_stamina)
+ else if(M.isSynthetic())
+ cell.use(disarm_cost)
else
- M.nutrition = M.nutrition - disarm_cost
- M.nutrition = Clamp(M.nutrition, 0, M.max_nutrition)
+ M.nutrition = Clamp(M.nutrition - disarm_cost, 0, M.max_nutrition)
if(w_uniform)
w_uniform.add_fingerprint(M)
- var/obj/item/organ/external/affecting = get_organ(ran_zone(M.zone_sel.selecting))
+ var/obj/item/organ/external/affecting = get_organ(ran_zone(M.zone_sel.selecting))
var/list/holding = list(get_active_hand() = 40, get_inactive_hand() = 20)
//See if they have any weapons to retaliate with
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 1aba1476965..11513f6a0f5 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -187,31 +187,28 @@ emp_act
if(s.surge_left)
visible_message("[src] was not affected by EMP pulse.", "Warning: EMP detected, integrated surge prevention module activated. There are [s.surge_left] preventions left.")
else
- s.broken = 1
+ s.broken = TRUE
s.icon_state = "surge_ipc_broken"
visible_message("[src] was not affected by EMP pulse.", "Warning: EMP detected, integrated surge prevention module activated. The surge prevention module is fried, replacement recommended.")
- return 1
+ return TRUE
else if(s.surge_left == 0.5)
to_chat(src, "Warning: EMP detected, integrated surge prevention module is damaged and was unable to fully protect from EMP. Half of the damage taken. Replacement recommended.")
for(var/obj/O in src)
- if(!O) continue
+ if(!O)
+ continue
O.emp_act(severity * 2) // EMP act takes reverse numbers
for(var/obj/item/organ/external/O in organs)
O.emp_act(severity)
for(var/obj/item/organ/I in O.internal_organs)
- if(I.robotic == 0) continue
+ if(I.robotic == ROBOTIC_NONE)
+ continue
I.emp_act(severity * 2) // EMP act takes reverse numbers
- return 1
+ return TRUE
else
to_chat(src, "Warning: EMP detected, integrated surge prevention module is fried and unable to protect from EMP. Replacement recommended.")
+
for(var/obj/O in src)
- if(!O) continue
O.emp_act(severity)
- for(var/obj/item/organ/external/O in organs)
- O.emp_act(severity)
- for(var/obj/item/organ/I in O.internal_organs)
- if(I.robotic == 0) continue
- I.emp_act(severity)
..()
/mob/living/carbon/human/resolve_item_attack(obj/item/I, mob/living/user, var/target_zone)
@@ -447,6 +444,11 @@ emp_act
visible_message("[src] is pinned to the wall by [O]!","You are pinned to the wall by [O]!")
src.anchored = 1
src.pinned += O
+ else if(ishuman(AM))
+ var/mob/living/carbon/human/H = AM
+ H.Weaken(3)
+ Weaken(3)
+ visible_message(SPAN_WARNING("[src] get knocked over by [H]!"), SPAN_WARNING("You get knocked over by [H]!"))
/mob/living/carbon/human/embed(var/obj/O, var/def_zone=null)
if(!def_zone) ..()
diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm
index 2fbc6571cd9..ef7dc248d5f 100644
--- a/code/modules/mob/living/carbon/human/human_helpers.dm
+++ b/code/modules/mob/living/carbon/human/human_helpers.dm
@@ -256,4 +256,7 @@
return species.icon_x_offset
/mob/living/carbon/human/get_standard_pixel_y()
- return species.icon_y_offset
\ No newline at end of file
+ return species.icon_y_offset
+
+/mob/living/carbon/human/proc/protected_from_sound()
+ return (l_ear?.item_flags & SOUNDPROTECTION) || (r_ear?.item_flags & SOUNDPROTECTION) || (head?.item_flags & SOUNDPROTECTION)
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm
index ffc585b1392..85861c8d008 100644
--- a/code/modules/mob/living/carbon/human/human_movement.dm
+++ b/code/modules/mob/living/carbon/human/human_movement.dm
@@ -87,8 +87,12 @@
tally = max(0, tally-3)
var/turf/T = get_turf(src)
- if(T && !mind.changeling) // changelings don't get movement costs
- tally += T.movement_cost
+ if(T) // changelings don't get movement costs
+ var/datum/changeling/changeling
+ if(mind)
+ changeling = mind.antag_datums[MODE_CHANGELING]
+ if(!changeling)
+ tally += T.movement_cost
tally += config.human_delay
@@ -161,7 +165,7 @@
return
last_x = x
last_y = y
- if (m_intent == "run")
+ if (m_intent == M_RUN)
playsound(src, footsound, 70, 1, required_asfx_toggles = ASFX_FOOTSTEPS)
else
footstep++
diff --git a/code/modules/mob/living/carbon/human/human_organs.dm b/code/modules/mob/living/carbon/human/human_organs.dm
index 907b7adb529..139ab5350fc 100644
--- a/code/modules/mob/living/carbon/human/human_organs.dm
+++ b/code/modules/mob/living/carbon/human/human_organs.dm
@@ -91,7 +91,7 @@
//malfunctioning only happens intermittently so treat it as a missing limb when it procs
stance_damage += 2
if(prob(10))
- visible_message("\The [src]'s [E.name] [pick("twitches", "shudders")] and sparks!")
+ visible_message(SPAN_WARNING("\The [src]'s [E.name] [pick("twitches", "shudders")] and sparks!"))
spark(src, 5)
else if (E.is_broken() || !E.is_usable())
stance_damage += 1
@@ -108,10 +108,11 @@
// standing is poor
if(stance_damage >= 4 || (stance_damage >= 2 && prob(5)))
- if(lying || resting)
- return
- emote("scream")
- emote("collapse")
+ if(!(lying || resting))
+ emote("scream")
+ if(!weakened)
+ custom_emote(VISIBLE_MESSAGE, "collapses!")
+ Weaken(3)
next_stance_collapse = world.time + (rand(8, 16) SECONDS)
/mob/living/carbon/human/proc/handle_grasp()
@@ -204,7 +205,12 @@
return FALSE
/mob/living/carbon/human/is_asystole()
- if(species.has_organ[BP_HEART] && !isSynthetic())
+ if(isSynthetic())
+ var/obj/item/organ/internal/cell/C = internal_organs_by_name[BP_CELL]
+ if(istype(C))
+ if(!C.is_usable() || !C.percent())
+ return TRUE
+ else if(should_have_organ(BP_HEART))
var/obj/item/organ/internal/heart/heart = internal_organs_by_name[BP_HEART]
if(!istype(heart) || !heart.is_working())
return TRUE
diff --git a/code/modules/mob/living/carbon/human/human_powers.dm b/code/modules/mob/living/carbon/human/human_powers.dm
index 77a420d8db8..585059e9b55 100644
--- a/code/modules/mob/living/carbon/human/human_powers.dm
+++ b/code/modules/mob/living/carbon/human/human_powers.dm
@@ -787,11 +787,8 @@ mob/living/carbon/human/proc/change_monitor()
var/list/victims = list()
- for (var/mob/living/carbon/human/T in hearers(2, src))
- if (T == src)
- continue
-
- if (istype(T) && (T:l_ear || T:r_ear) && istype((T:l_ear || T:r_ear), /obj/item/clothing/ears/earmuffs))
+ for (var/mob/living/carbon/human/T in hearers(2, src) - src)
+ if(T.protected_from_sound())
continue
to_chat(T, "You hear an ear piercing shriek and feel your senses go dull!")
@@ -1038,7 +1035,11 @@ mob/living/carbon/human/proc/change_monitor()
output += "Internal Temperature: [convert_k2c(bodytemperature)] Degrees Celsius\n"
- output += "Current Charge Level: [nutrition]\n"
+ var/obj/item/organ/internal/cell/C = internal_organs_by_name[BP_CELL]
+ if(!C || !C.cell)
+ output += SPAN_DANGER("ERROR: NO BATTERY DETECTED")
+ else
+ output += "Current Charge Level: [C.percent()]\n"
var/toxDam = getToxLoss()
if(toxDam)
diff --git a/code/modules/mob/living/carbon/human/human_species.dm b/code/modules/mob/living/carbon/human/human_species.dm
index e7aeb04acc3..d2fd57bf346 100644
--- a/code/modules/mob/living/carbon/human/human_species.dm
+++ b/code/modules/mob/living/carbon/human/human_species.dm
@@ -35,12 +35,6 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy/mannequin)
h_style = "blue IPC screen"
. = ..(mapload, SPECIES_IPC)
-/mob/living/carbon/human/machine/Stat()
- ..()
-
- if(client?.statpanel == "Status")
- stat("Battery Charge: ", "[nutrition]/[max_nutrition]")
-
/mob/living/carbon/human/monkey/Initialize(mapload)
. = ..(mapload, SPECIES_MONKEY)
@@ -110,39 +104,15 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy/mannequin)
/mob/living/carbon/human/industrial/Initialize(mapload)
. = ..(mapload, SPECIES_IPC_G1)
-/mob/living/carbon/human/industrial/Stat()
- ..()
-
- if(client?.statpanel == "Status")
- stat("Battery Charge: ", "[nutrition]/[max_nutrition]")
-
/mob/living/carbon/human/shell/Initialize(mapload)
. = ..(mapload, SPECIES_IPC_SHELL)
-/mob/living/carbon/human/shell/Stat()
- ..()
-
- if(client?.statpanel == "Status")
- stat("Battery Charge: ", "[nutrition]/[max_nutrition]")
-
/mob/living/carbon/human/industrial_hephaestus/Initialize(mapload)
. = ..(mapload, SPECIES_IPC_G2)
-/mob/living/carbon/human/industrial_hephaestus/Stat()
- ..()
-
- if(client?.statpanel == "Status")
- stat("Battery Charge: ", "[nutrition]/[max_nutrition]")
-
/mob/living/carbon/human/industrial_xion/Initialize(mapload)
. = ..(mapload, SPECIES_IPC_XION)
-/mob/living/carbon/human/industrial_xion/Stat()
- ..()
-
- if(client?.statpanel == "Status")
- stat("Battery Charge: ", "[nutrition]/[max_nutrition]")
-
/mob/living/carbon/human/industrial_xion_remote/Initialize(mapload)
. = ..(mapload, SPECIES_IPC_XION_REMOTE)
@@ -156,12 +126,6 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy/mannequin)
remote_network = REMOTE_GENERIC_ROBOT
SSvirtualreality.add_robot(src, remote_network)
-/mob/living/carbon/human/industrial_xion_remote/Stat()
- ..()
-
- if(client?.statpanel == "Status")
- stat("Battery Charge: ", "[nutrition]/[max_nutrition]")
-
/mob/living/carbon/human/industrial_xion_remote_mech/Initialize(mapload)
. = ..(mapload, SPECIES_IPC_XION_REMOTE)
@@ -172,12 +136,6 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy/mannequin)
mind.name = real_name
status_flags |= NO_ANTAG
-/mob/living/carbon/human/industrial_xion_remote_mech/Stat()
- ..()
-
- if(client?.statpanel == "Status")
- stat("Battery Charge: ", "[nutrition]/[max_nutrition]")
-
/mob/living/carbon/human/industrial_xion_remote_bunker/Initialize(mapload)
. = ..(mapload, SPECIES_IPC_XION_REMOTE)
@@ -195,12 +153,6 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy/mannequin)
remote_network = REMOTE_BUNKER_ROBOT
SSvirtualreality.add_robot(src, remote_network)
-/mob/living/carbon/human/industrial_xion_remote_bunker/Stat()
- ..()
-
- if(client?.statpanel == "Status")
- stat("Battery Charge: ", "[nutrition]/[max_nutrition]")
-
/mob/living/carbon/human/industrial_xion_remote_penal/Initialize(mapload)
. = ..(mapload, "Remote Xion Industrial Frame")
@@ -216,12 +168,6 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy/mannequin)
remote_network = REMOTE_PRISON_ROBOT
SSvirtualreality.add_robot(src, remote_network)
-/mob/living/carbon/human/industrial_xion_remote_penal/Stat()
- ..()
-
- if(client?.statpanel == "Status")
- stat(null, "Battery Charge: [nutrition]/[max_nutrition]")
-
/mob/living/carbon/human/industrial_xion_remote_warden/Initialize(mapload)
. = ..(mapload, "Remote Xion Industrial Frame")
@@ -244,39 +190,15 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy/mannequin)
remote_network = REMOTE_WARDEN_ROBOT
SSvirtualreality.add_robot(src, remote_network)
-/mob/living/carbon/human/industrial_xion_remote_warden/Stat()
- ..()
-
- if(client?.statpanel == "Status")
- stat(null, "Battery Charge: [nutrition]/[max_nutrition]")
-
/mob/living/carbon/human/industrial_zenghu/Initialize(mapload)
. = ..(mapload, SPECIES_IPC_ZENGHU)
-/mob/living/carbon/human/industrial_zenghu/Stat()
- ..()
-
- if(client?.statpanel == "Status")
- stat("Battery Charge: ", "[nutrition]/[max_nutrition]")
-
/mob/living/carbon/human/industrial_bishop/Initialize(mapload)
. = ..(mapload, SPECIES_IPC_BISHOP)
-/mob/living/carbon/human/industrial_bishop/Stat()
- ..()
-
- if(client?.statpanel == "Status")
- stat("Battery Charge: ", "[nutrition]/[max_nutrition]")
-
/mob/living/carbon/human/unbranded_frame/Initialize(mapload)
. = ..(mapload, SPECIES_IPC_UNBRANDED)
-/mob/living/carbon/human/unbranded_frame/Stat()
- ..()
-
- if(client?.statpanel == "Status")
- stat("Battery Charge: ", "[nutrition]/[max_nutrition]")
-
/mob/living/carbon/human/unbranded_frame_remote/Initialize(mapload)
. = ..(mapload, SPECIES_IPC_UNBRANDED_REMOTE)
@@ -290,12 +212,6 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy/mannequin)
remote_network = REMOTE_GENERIC_ROBOT
SSvirtualreality.add_robot(src, remote_network)
-/mob/living/carbon/human/unbranded_frame_remote/Stat()
- ..()
-
- if(client?.statpanel == "Status")
- stat("Battery Charge: ", "[nutrition]/[max_nutrition]")
-
/mob/living/carbon/human/unbranded_frame_remote_bunker/Initialize(mapload)
. = ..(mapload, SPECIES_IPC_UNBRANDED_REMOTE)
@@ -313,12 +229,6 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy/mannequin)
remote_network = REMOTE_BUNKER_ROBOT
SSvirtualreality.add_robot(src, remote_network)
-/mob/living/carbon/human/unbranded_frame_remote_bunker/Stat()
- ..()
-
- if(client?.statpanel == "Status")
- stat("Battery Charge: ", "[nutrition]/[max_nutrition]")
-
/mob/living/carbon/human/terminator/Initialize(mapload)
. = ..(mapload, SPECIES_IPC_TERMINATOR)
add_language(LANGUAGE_SOL_COMMON, 1)
@@ -347,12 +257,6 @@ INITIALIZE_IMMEDIATE(/mob/living/carbon/human/dummy/mannequin)
src.equip_to_slot_or_del(commando_belt, slot_belt)
src.gender = NEUTER
-/mob/living/carbon/human/terminator/Stat()
- ..()
-
- if(client?.statpanel == "Status")
- stat("Battery Charge: ", "[nutrition]/[max_nutrition]")
-
/mob/living/carbon/human/terminator
mob_size = 30
diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm
index c3ee862b0ad..0ef82d26ed3 100644
--- a/code/modules/mob/living/carbon/human/inventory.dm
+++ b/code/modules/mob/living/carbon/human/inventory.dm
@@ -232,8 +232,8 @@ This saves us from having to call add_fingerprint() any time something is put in
if(slot_l_ear)
src.l_ear = W
if(l_ear.slot_flags & SLOT_TWOEARS)
- var/obj/item/clothing/ears/offear/O = new(W)
- O.forceMove(src)
+ var/obj/item/clothing/ears/offear/O = new /obj/item/clothing/ears/offear(src)
+ O.copy_ear(W)
src.r_ear = O
O.layer = SCREEN_LAYER+0.01
W.equipped(src, slot)
@@ -241,8 +241,8 @@ This saves us from having to call add_fingerprint() any time something is put in
if(slot_r_ear)
src.r_ear = W
if(r_ear.slot_flags & SLOT_TWOEARS)
- var/obj/item/clothing/ears/offear/O = new(W)
- O.forceMove(src)
+ var/obj/item/clothing/ears/offear/O = new /obj/item/clothing/ears/offear(src)
+ O.copy_ear(W)
src.l_ear = O
O.layer = SCREEN_LAYER+0.01
W.equipped(src, slot)
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index 0c91f0b75f5..9c41987da86 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -84,8 +84,10 @@
//Update our name based on whether our face is obscured/disfigured
name = get_visible_name()
- if(mind?.vampire)
- handle_vampire()
+ if(mind)
+ var/datum/vampire/vampire = mind.antag_datums[MODE_VAMPIRE]
+ if(vampire)
+ handle_vampire()
/mob/living/carbon/human/think()
..()
@@ -727,10 +729,11 @@
if(paralysis || sleeping || InStasis())
blinded = TRUE
stat = UNCONSCIOUS
-
adjustHalLoss(-3)
if (species.tail)
animate_tail_reset()
+ if(prob(2) && is_asystole() && isSynthetic())
+ visible_message("[src] [pick("emits low pitched whirr","beeps urgently")]")
if(paralysis)
AdjustParalysis(-1)
@@ -793,6 +796,11 @@
var/tmp/last_frenzy_state
var/tmp/last_oxy_overlay
+/mob/living/carbon/human/can_update_hud()
+ if((!client && !bg) || QDELETED(src))
+ return FALSE
+ return TRUE
+
/mob/living/carbon/human/handle_regular_hud_updates()
if(hud_updateflag) // update our mob's hud overlays, AKA what others see flaoting above our head
handle_hud_list()
@@ -872,6 +880,8 @@
// Apply a fire overlay if we're burning.
if(on_fire)
var/image/burning_image = image('icons/mob/screen1_health.dmi', "burning", pixel_x = species.healths_overlay_x)
+ var/midway_point = FIRE_MAX_STACKS / 2
+ burning_image.color = color_rotation((midway_point - fire_stacks) * 3)
health_images += burning_image
// Show a general pain/crit indicator if needed.
@@ -910,6 +920,7 @@
var/new_val = "[isSynthetic() ? "charge" : "nutrition"][nut_icon]"
if (nutrition_icon.icon_state != new_val)
nutrition_icon.icon_state = new_val
+
if(hydration_icon)
var/hyd_factor = max_hydration ? Clamp(hydration / max_hydration, 0, 1) : 1
var/hyd_icon = 5
@@ -927,6 +938,14 @@
if (hydration_icon.icon_state != new_val)
hydration_icon.icon_state = new_val
+ if(isSynthetic())
+ var/obj/item/organ/internal/cell/IC = internal_organs_by_name[BP_CELL]
+ if (istype(IC))
+ var/chargeNum = Clamp(Ceiling(IC.percent()/25), 0, 4) //0-100 maps to 0-4, but give it a paranoid clamp just in case.
+ cells.icon_state = "charge[chargeNum]"
+ else
+ cells.icon_state = "charge-empty"
+
if(pressure)
var/new_pressure = "pressure[pressure_alert]"
if (pressure.icon_state != new_pressure)
@@ -1029,8 +1048,10 @@
playsound_simple(null, pick(scarySounds), 50, TRUE)
/mob/living/carbon/human/proc/handle_changeling()
- if(mind && mind.changeling)
- mind.changeling.regenerate()
+ if(mind)
+ var/datum/changeling/changeling = mind.antag_datums[MODE_CHANGELING]
+ if(changeling)
+ changeling.regenerate()
/mob/living/carbon/human/proc/handle_shock()
if(status_flags & GODMODE)
@@ -1282,9 +1303,9 @@
if(ear_deaf <= 1 && (sdisabilities & DEAF) && has_hearing_aid())
setEarDamage(-1, max(ear_deaf-1, 0))
- if(istype(l_ear, /obj/item/clothing/ears/earmuffs) || istype(r_ear, /obj/item/clothing/ears/earmuffs)) //resting your ears with earmuffs heals ear damage faster
+ if(protected_from_sound()) // resting your ears make them heal faster
adjustEarDamage(-0.15, 0)
- setEarDamage(-1, max(ear_deaf, 1))
+ setEarDamage(-1)
else if(ear_damage < HEARING_DAMAGE_SLOW_HEAL) //ear damage heals slowly under this threshold. otherwise you'll need earmuffs
adjustEarDamage(-0.05, 0)
diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm
index 28134a14fd2..1eed969ce7f 100644
--- a/code/modules/mob/living/carbon/human/say.dm
+++ b/code/modules/mob/living/carbon/human/say.dm
@@ -79,8 +79,10 @@
var/obj/item/organ/external/head/face = organs_by_name[BP_HEAD]
if(face?.disfigured) // if your face is ruined, your ability to vocalize is also ruined
return "Unknown" // above ling voice mimicing so they don't get caught out immediately
- if(mind?.changeling?.mimicing)
- return mind.changeling.mimicing
+ if(mind)
+ var/datum/changeling/changeling = mind.antag_datums[MODE_CHANGELING]
+ if(changeling?.mimicing)
+ return changeling.mimicing
if(GetSpecialVoice())
return GetSpecialVoice()
return real_name
diff --git a/code/modules/mob/living/carbon/human/species/species.dm b/code/modules/mob/living/carbon/human/species/species.dm
index 09a62fd14ed..fee31909504 100644
--- a/code/modules/mob/living/carbon/human/species/species.dm
+++ b/code/modules/mob/living/carbon/human/species/species.dm
@@ -21,6 +21,7 @@
var/icobase = 'icons/mob/human_races/human/r_human.dmi' // Normal icon set.
var/deform = 'icons/mob/human_races/human/r_def_human.dmi' // Mutated icon set.
var/preview_icon = 'icons/mob/human_races/human/human_preview.dmi'
+ var/bandages_icon
// Damage overlay and masks.
var/damage_overlays = 'icons/mob/human_races/masks/dam_human.dmi'
@@ -495,7 +496,7 @@
if(!H.client)//no client, no screen to update
return 1
- H.set_fullscreen(H.eye_blind && !H.equipment_prescription, "blind", /obj/screen/fullscreen/blind)
+ H.set_fullscreen(H.eye_blind, "blind", /obj/screen/fullscreen/blind)
H.set_fullscreen(H.stat == UNCONSCIOUS, "blackout", /obj/screen/fullscreen/blackout)
if(config.welder_vision)
@@ -567,7 +568,7 @@
H.flash_pain(H.get_shock())
if ((H.get_shock() + H.getOxyLoss()) >= (exhaust_threshold * 0.8))
- H.m_intent = "walk"
+ H.m_intent = M_WALK
H.hud_used.move_intent.update_move_icon(H)
to_chat(H, SPAN_DANGER("You're too exhausted to run anymore!"))
H.flash_pain(H.get_shock())
diff --git a/code/modules/mob/living/carbon/human/species/species_hud.dm b/code/modules/mob/living/carbon/human/species/species_hud.dm
index 2749a864eed..76c6322b332 100644
--- a/code/modules/mob/living/carbon/human/species/species_hud.dm
+++ b/code/modules/mob/living/carbon/human/species/species_hud.dm
@@ -1,18 +1,19 @@
/datum/hud_data
- var/icon // If set, overrides ui_style.
- var/has_a_intent = 1 // Set to draw intent box.
- var/has_m_intent = 1 // Set to draw move intent box.
- var/has_warnings = 1 // Set to draw environment warnings.
- var/has_pressure = 1 // Draw the pressure indicator.
- var/has_nutrition = 1 // Draw the nutrition indicator.
- var/has_hydration = 1 // Draw the hydration indicator.
- var/has_bodytemp = 1 // Draw the bodytemp indicator.
- var/has_hands = 1 // Set to draw hands.
- var/has_drop = 1 // Set to draw drop button.
- var/has_throw = 1 // Set to draw throw button.
- var/has_resist = 1 // Set to draw resist button.
- var/has_internals = 1 // Set to draw the internals toggle button.
- var/has_up_hint = 1 // Set to draw the "look-up" hint icon.
+ var/icon // If set, overrides ui_style.
+ var/has_a_intent = TRUE // Set to draw intent box.
+ var/has_m_intent = TRUE // Set to draw move intent box.
+ var/has_warnings = TRUE // Set to draw environment warnings.
+ var/has_pressure = TRUE // Draw the pressure indicator.
+ var/has_nutrition = TRUE // Draw the nutrition indicator.
+ var/has_hydration = TRUE // Draw the hydration indicator.
+ var/has_bodytemp = TRUE // Draw the bodytemp indicator.
+ var/has_hands = TRUE // Set to draw hands.
+ var/has_drop = TRUE // Set to draw drop button.
+ var/has_throw = TRUE // Set to draw throw button.
+ var/has_resist = TRUE // Set to draw resist button.
+ var/has_internals = TRUE // Set to draw the internals toggle button.
+ var/has_up_hint = TRUE // Set to draw the "look-up" hint icon.
+ var/has_cell = FALSE //Set if the species has a cell.
var/list/equip_slots = list() // Checked by mob_can_equip().
// Contains information on the position and tag for all inventory slots
@@ -23,10 +24,10 @@
"o_clothing" = list("loc" = ui_oclothing, "name" = "Suit", "slot" = slot_wear_suit, "state" = "suit", "toggle" = 1),
"mask" = list("loc" = ui_mask, "name" = "Mask", "slot" = slot_wear_mask, "state" = "mask", "toggle" = 1),
"gloves" = list("loc" = ui_gloves, "name" = "Gloves", "slot" = slot_gloves, "state" = "gloves", "toggle" = 1),
- BP_EYES = list("loc" = ui_glasses, "name" = "Glasses", "slot" = slot_glasses, "state" = "glasses","toggle" = 1),
+ "eyes" = list("loc" = ui_glasses, "name" = "Glasses", "slot" = slot_glasses, "state" = "glasses","toggle" = 1),
"l_ear" = list("loc" = ui_l_ear, "name" = "Left Ear", "slot" = slot_l_ear, "state" = "ears", "toggle" = 1),
"r_ear" = list("loc" = ui_r_ear, "name" = "Right Ear", "slot" = slot_r_ear, "state" = "ears", "toggle" = 1),
- BP_HEAD = list("loc" = ui_head, "name" = "Hat", "slot" = slot_head, "state" = "hair", "toggle" = 1),
+ "head" = list("loc" = ui_head, "name" = "Hat", "slot" = slot_head, "state" = "hair", "toggle" = 1),
"shoes" = list("loc" = ui_shoes, "name" = "Shoes", "slot" = slot_shoes, "state" = "shoes", "toggle" = 1),
"suit storage" = list("loc" = ui_sstore1, "name" = "Suit Storage", "slot" = slot_s_store, "state" = "suitstore"),
"back" = list("loc" = ui_back, "name" = "Back", "slot" = slot_back, "state" = "back"),
@@ -59,14 +60,14 @@
equip_slots |= slot_legcuffed
/datum/hud_data/diona
- has_hydration = 0
- has_internals = 0
+ has_hydration = FALSE
+ has_internals = FALSE
gear = list(
"i_clothing" = list("loc" = ui_iclothing, "name" = "Uniform", "slot" = slot_w_uniform, "state" = "center", "toggle" = 1),
"o_clothing" = list("loc" = ui_shoes, "name" = "Suit", "slot" = slot_wear_suit, "state" = "suit", "toggle" = 1),
"l_ear" = list("loc" = ui_glasses, "name" = "Left Ear", "slot" = slot_l_ear, "state" = "ears", "toggle" = 1),
"r_ear" = list("loc" = ui_l_ear, "name" = "Right Ear", "slot" = slot_r_ear, "state" = "ears", "toggle" = 1),
- BP_HEAD = list("loc" = ui_mask, "name" = "Hat", "slot" = slot_head, "state" = "hair", "toggle" = 1),
+ "head" = list("loc" = ui_mask, "name" = "Hat", "slot" = slot_head, "state" = "hair", "toggle" = 1),
"suit storage" = list("loc" = ui_sstore1, "name" = "Suit Storage", "slot" = slot_s_store, "state" = "suitstore"),
"back" = list("loc" = ui_back, "name" = "Back", "slot" = slot_back, "state" = "back"),
"id" = list("loc" = ui_id, "name" = "ID", "slot" = slot_wear_id, "state" = "id"),
@@ -74,7 +75,7 @@
"storage2" = list("loc" = ui_storage2, "name" = "Right Pocket", "slot" = slot_r_store, "state" = "pocket"),
"belt" = list("loc" = ui_belt, "name" = "Belt", "slot" = slot_belt, "state" = "belt"),
"mask" = list("loc" = ui_oclothing, "name" = "Mask", "slot" = slot_wear_mask, "state" = "mask", "toggle" = 1),
- BP_EYES = list("loc" = ui_gloves, "name" = "Glasses", "slot" = slot_glasses, "state" = "glasses","toggle" = 1)
+ "eyes" = list("loc" = ui_gloves, "name" = "Glasses", "slot" = slot_glasses, "state" = "glasses","toggle" = 1)
)
/datum/hud_data/monkey
@@ -85,8 +86,10 @@
/datum/hud_data/ipc
- has_hydration = 0
+ has_hydration = FALSE
+ has_nutrition = FALSE
+ has_cell = TRUE
/datum/hud_data/construct
- has_hydration = 0
- has_nutrition = 0
\ No newline at end of file
+ has_hydration = FALSE
+ has_nutrition = FALSE
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/human/species/station/diona/diona.dm b/code/modules/mob/living/carbon/human/species/station/diona/diona.dm
index 2909ee7e79d..6e94de22733 100644
--- a/code/modules/mob/living/carbon/human/species/station/diona/diona.dm
+++ b/code/modules/mob/living/carbon/human/species/station/diona/diona.dm
@@ -12,6 +12,7 @@
icobase = 'icons/mob/human_races/diona/r_diona.dmi'
deform = 'icons/mob/human_races/diona/r_def_plant.dmi'
preview_icon = 'icons/mob/human_races/diona/diona_preview.dmi'
+ bandages_icon = 'icons/mob/bandage.dmi'
language = LANGUAGE_ROOTSONG
secondary_langs = list(LANGUAGE_SKRELLIAN, LANGUAGE_AZAZIBA)
unarmed_types = list(
@@ -150,7 +151,7 @@
DS.stored_energy = 0
H.adjustHalLoss(remainder*5, 1)
H.updatehealth()
- H.m_intent = "walk"
+ H.m_intent = M_WALK
H.hud_used.move_intent.update_move_icon(H)
to_chat(H, SPAN_DANGER("We have expended our energy reserves, and cannot continue to move at such a pace. We must find light!"))
return 0
diff --git a/code/modules/mob/living/carbon/human/species/station/human/human.dm b/code/modules/mob/living/carbon/human/species/station/human/human.dm
index 900b212ca55..5889162490c 100644
--- a/code/modules/mob/living/carbon/human/species/station/human/human.dm
+++ b/code/modules/mob/living/carbon/human/species/station/human/human.dm
@@ -7,6 +7,7 @@
bodytype = BODYTYPE_HUMAN
age_max = 125
economic_modifier = 12
+ bandages_icon = 'icons/mob/bandage.dmi'
primitive_form = SPECIES_MONKEY
unarmed_types = list(
diff --git a/code/modules/mob/living/carbon/human/species/station/ipc/ipc.dm b/code/modules/mob/living/carbon/human/species/station/ipc/ipc.dm
index 0cc04f41171..2718d84d2c7 100644
--- a/code/modules/mob/living/carbon/human/species/station/ipc/ipc.dm
+++ b/code/modules/mob/living/carbon/human/species/station/ipc/ipc.dm
@@ -82,11 +82,11 @@
has_organ = list(
BP_BRAIN = /obj/item/organ/internal/mmi_holder/posibrain,
BP_CELL = /obj/item/organ/internal/cell,
- BP_OPTICS = /obj/item/organ/internal/eyes/optical_sensor,
+ BP_EYES = /obj/item/organ/internal/eyes/optical_sensor,
BP_IPCTAG = /obj/item/organ/internal/ipc_tag
)
- vision_organ = BP_OPTICS
+ vision_organ = BP_EYES
has_limbs = list(
BP_CHEST = list("path" = /obj/item/organ/external/chest/ipc),
@@ -125,24 +125,14 @@
var/sprint_temperature_factor = 1.15
var/sprint_charge_factor = 0.65
-datum/species/machine/handle_post_spawn(var/mob/living/carbon/human/H)
+/datum/species/machine/handle_post_spawn(var/mob/living/carbon/human/H)
. = ..()
check_tag(H, H.client)
/datum/species/machine/handle_sprint_cost(var/mob/living/carbon/human/H, var/cost)
if (H.stat == CONSCIOUS)
H.bodytemperature += cost * sprint_temperature_factor
- H.adjustNutritionLoss(cost * sprint_charge_factor)
- if(H.nutrition <= 0 && H.max_nutrition > 0)
- H.Weaken(15)
- H.m_intent = "walk"
- H.hud_used.move_intent.update_move_icon(H)
- to_chat(H, SPAN_DANGER("ERROR: Power reserves depleted, emergency shutdown engaged. Backup power will come online in approximately 30 seconds, initiate charging as primary directive."))
- playsound(H.loc, 'sound/machines/buzz-two.ogg', 100, 0)
- else
- return 1
-
- return 0
+ return TRUE
/datum/species/machine/handle_death(var/mob/living/carbon/human/H)
..()
@@ -302,10 +292,10 @@ datum/species/machine/handle_post_spawn(var/mob/living/carbon/human/H)
. = ..()
check_tag(H, H.client)
+/datum/species/machine/has_psi_potential()
+ return FALSE
+
/datum/species/machine/handle_death_check(var/mob/living/carbon/human/H)
if(H.get_total_health() <= config.health_threshold_dead)
return TRUE
return FALSE
-
-/datum/species/machine/has_psi_potential()
- return FALSE
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/human/species/station/ipc/ipc_subspecies.dm b/code/modules/mob/living/carbon/human/species/station/ipc/ipc_subspecies.dm
index a220feda602..f97fce081b5 100644
--- a/code/modules/mob/living/carbon/human/species/station/ipc/ipc_subspecies.dm
+++ b/code/modules/mob/living/carbon/human/species/station/ipc/ipc_subspecies.dm
@@ -94,7 +94,7 @@
has_organ = list(
BP_BRAIN = /obj/item/organ/internal/mmi_holder/posibrain,
BP_CELL = /obj/item/organ/internal/cell,
- BP_OPTICS = /obj/item/organ/internal/eyes/optical_sensor,
+ BP_EYES = /obj/item/organ/internal/eyes/optical_sensor,
"surge" = /obj/item/organ/internal/surge/advanced
)
@@ -245,7 +245,7 @@
has_organ = list(
BP_BRAIN = /obj/item/organ/internal/mmi_holder/posibrain/terminator,
"shielded cell" = /obj/item/organ/internal/cell/terminator,
- BP_OPTICS = /obj/item/organ/internal/eyes/optical_sensor/terminator,
+ BP_EYES = /obj/item/organ/internal/eyes/optical_sensor/terminator,
"data core" = /obj/item/organ/internal/data,
"surge" = /obj/item/organ/internal/surge/advanced
)
@@ -393,7 +393,7 @@
has_organ = list(
BP_BRAIN = /obj/item/organ/internal/mmi_holder/circuit,
BP_CELL = /obj/item/organ/internal/cell,
- BP_OPTICS = /obj/item/organ/internal/eyes/optical_sensor,
+ BP_EYES = /obj/item/organ/internal/eyes/optical_sensor,
BP_IPCTAG = /obj/item/organ/internal/ipc_tag
)
@@ -547,7 +547,7 @@
has_organ = list(
BP_BRAIN = /obj/item/organ/internal/mmi_holder/circuit,
BP_CELL = /obj/item/organ/internal/cell,
- BP_OPTICS = /obj/item/organ/internal/eyes/optical_sensor,
+ BP_EYES = /obj/item/organ/internal/eyes/optical_sensor,
BP_IPCTAG = /obj/item/organ/internal/ipc_tag
)
diff --git a/code/modules/mob/living/carbon/human/species/station/skrell/skrell.dm b/code/modules/mob/living/carbon/human/species/station/skrell/skrell.dm
index d01a34ff000..1aea53b69a7 100644
--- a/code/modules/mob/living/carbon/human/species/station/skrell/skrell.dm
+++ b/code/modules/mob/living/carbon/human/species/station/skrell/skrell.dm
@@ -10,6 +10,7 @@
icobase = 'icons/mob/human_races/skrell/r_skrell.dmi'
deform = 'icons/mob/human_races/skrell/r_def_skrell.dmi'
preview_icon = 'icons/mob/human_races/skrell/skrell_preview.dmi'
+ bandages_icon = 'icons/mob/bandage.dmi'
eyes = "skrell_eyes_s"
primitive_form = SPECIES_MONKEY_SKRELL
unarmed_types = list(/datum/unarmed_attack/punch, /datum/unarmed_attack/stomp, /datum/unarmed_attack/kick)
diff --git a/code/modules/mob/living/carbon/human/species/station/tajara/tajara.dm b/code/modules/mob/living/carbon/human/species/station/tajara/tajara.dm
index 3929136a1e6..3465905e937 100644
--- a/code/modules/mob/living/carbon/human/species/station/tajara/tajara.dm
+++ b/code/modules/mob/living/carbon/human/species/station/tajara/tajara.dm
@@ -7,6 +7,7 @@
icobase = 'icons/mob/human_races/tajara/r_tajaran.dmi'
deform = 'icons/mob/human_races/tajara/r_def_tajaran.dmi'
preview_icon = 'icons/mob/human_races/tajara/tajaran_preview.dmi'
+ bandages_icon = 'icons/mob/bandage.dmi'
tail = "tajtail"
tail_animation = 'icons/mob/species/tajaran/tail.dmi'
unarmed_types = list(
@@ -109,4 +110,4 @@
return
var/obj/item/clothing/shoes/sandal/S = new /obj/item/clothing/shoes/sandal(H)
if(H.equip_to_slot_or_del(S,slot_shoes))
- S.autodrobe_no_remove = 1
\ No newline at end of file
+ S.autodrobe_no_remove = TRUE
diff --git a/code/modules/mob/living/carbon/human/species/station/unathi/unathi.dm b/code/modules/mob/living/carbon/human/species/station/unathi/unathi.dm
index da04f664a9d..dbcae8db13f 100644
--- a/code/modules/mob/living/carbon/human/species/station/unathi/unathi.dm
+++ b/code/modules/mob/living/carbon/human/species/station/unathi/unathi.dm
@@ -7,6 +7,7 @@
icobase = 'icons/mob/human_races/unathi/r_lizard.dmi'
deform = 'icons/mob/human_races/unathi/r_def_lizard.dmi'
preview_icon = 'icons/mob/human_races/unathi/unathi_preview.dmi'
+ bandages_icon = 'icons/mob/bandage.dmi'
tail = "sogtail"
tail_animation = 'icons/mob/species/unathi/tail.dmi'
unarmed_types = list(
@@ -114,4 +115,4 @@
return
var/obj/item/clothing/shoes/sandal/S = new /obj/item/clothing/shoes/sandal(H)
if(H.equip_to_slot_or_del(S,slot_shoes))
- S.autodrobe_no_remove = 1
\ No newline at end of file
+ S.autodrobe_no_remove = TRUE
diff --git a/code/modules/mob/living/carbon/human/species/station/vaurca/vaurca.dm b/code/modules/mob/living/carbon/human/species/station/vaurca/vaurca.dm
index fa0bcd2b896..7774bd50387 100644
--- a/code/modules/mob/living/carbon/human/species/station/vaurca/vaurca.dm
+++ b/code/modules/mob/living/carbon/human/species/station/vaurca/vaurca.dm
@@ -14,6 +14,7 @@
icobase = 'icons/mob/human_races/vaurca/r_vaurca.dmi'
deform = 'icons/mob/human_races/vaurca/r_vaurca.dmi'
preview_icon = 'icons/mob/human_races/vaurca/vaurca_preview.dmi'
+ bandages_icon = 'icons/mob/bandage.dmi'
name_language = LANGUAGE_VAURCA
unarmed_types = list(
/datum/unarmed_attack/stomp,
@@ -153,4 +154,4 @@
return ..()
/datum/species/bug/has_psi_potential()
- return FALSE
\ No newline at end of file
+ return FALSE
diff --git a/code/modules/mob/living/carbon/human/stripping.dm b/code/modules/mob/living/carbon/human/stripping.dm
index 76094b14c7c..57830a9252d 100644
--- a/code/modules/mob/living/carbon/human/stripping.dm
+++ b/code/modules/mob/living/carbon/human/stripping.dm
@@ -7,6 +7,8 @@
return FALSE
var/obj/item/target_slot = get_equipped_item(text2num(slot_to_strip))
+ if(istype(target_slot, /obj/item/clothing/ears/offear))
+ target_slot = (l_ear == target_slot ? r_ear : l_ear)
switch(slot_to_strip)
// Handle things that are part of this interface but not removing/replacing a given item.
@@ -88,6 +90,9 @@
if(stripping)
admin_attack_log(user, src, "Attempted to remove \a [target_slot]", "Target of an attempt to remove \a [target_slot].", "attempted to remove \a [target_slot] from")
+ if((l_ear == target_slot || r_ear == target_slot) && (target_slot.slot_flags & SLOT_TWOEARS))
+ var/obj/item/clothing/ears/OE = (l_ear == target_slot ? r_ear : l_ear)
+ qdel(OE)
unEquip(target_slot)
else if(user.unEquip(held))
equip_to_slot_if_possible(held, text2num(slot_to_strip), 0, 1, 1)
diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm
index c0e2fcb6f38..6dce5109b36 100644
--- a/code/modules/mob/living/carbon/human/update_icons.dm
+++ b/code/modules/mob/living/carbon/human/update_icons.dm
@@ -174,7 +174,6 @@ There are several things that need to be remembered:
M.Scale(size_multiplier)
M.Translate(0, 16*(size_multiplier-1))
animate(src, transform = M, time = ANIM_LYING_TIME)
- handle_floating_message_orientation()
compile_overlays()
lying_prev = lying
@@ -221,6 +220,24 @@ There are several things that need to be remembered:
LAZYADD(ovr, DI)
overlays_raw[DAMAGE_LAYER] = ovr
+ update_bandages(update_icons)
+ if(update_icons)
+ update_icon()
+
+/mob/living/carbon/human/proc/update_bandages(var/update_icons = TRUE)
+ var/bandage_icon = species.bandages_icon
+ if(!bandage_icon)
+ return
+ var/image/standing_image = overlays_raw[DAMAGE_LAYER]
+ if(standing_image)
+ for(var/obj/item/organ/external/O in organs)
+ if(O.is_stump())
+ continue
+ var/bandage_level = O.bandage_level()
+ if(bandage_level)
+ standing_image += image(bandage_icon, "[O.icon_name][bandage_level]")
+
+ overlays_raw[DAMAGE_LAYER] = standing_image
if(update_icons)
update_icon()
@@ -241,7 +258,6 @@ There are several things that need to be remembered:
return
var/husk_color_mod = rgb(96,88,80)
- var/hulk_color_mod = rgb(48,224,40)
var/husk = (HUSK in mutations)
var/fat = (FAT in mutations)
@@ -260,7 +276,12 @@ There are several things that need to be remembered:
qdel(stand_icon)
stand_icon = new(species.icon_template ? species.icon_template : 'icons/mob/human.dmi',"blank")
- var/icon_key = "[species.race_key][g][s_tone][r_skin][g_skin][b_skin][lip_style || "nolips"][!!husk][!!fat][!!hulk][!!skeleton]"
+ var/is_frenzied = FALSE
+ if(mind)
+ var/datum/vampire/vampire = mind.antag_datums[MODE_VAMPIRE]
+ if(vampire && (vampire.status & VAMP_FRENZIED))
+ is_frenzied = TRUE
+ var/icon_key = "[species.race_key][g][s_tone][r_skin][g_skin][b_skin][lip_style || "nolips"][!!husk][!!fat][!!hulk][!!skeleton][is_frenzied]"
var/obj/item/organ/internal/eyes/eyes = get_eyes()
if(eyes)
icon_key += "[rgb(eyes.eye_colour[1], eyes.eye_colour[2], eyes.eye_colour[3])]"
@@ -303,12 +324,14 @@ There are several things that need to be remembered:
else
base_icon.Blend(temp, ICON_OVERLAY)
+ var/list/add_images = part.get_additional_images(src)
+ if(add_images)
+ add_overlay(add_images, TRUE)
+ compile_overlays()
+
if(!(species.flags & NO_SCAN))
if(husk)
base_icon.ColorTone(husk_color_mod)
- else if(hulk)
- var/list/tone = ReadRGB(hulk_color_mod)
- base_icon.MapColors(rgb(tone[1],0,0),rgb(0,tone[2],0),rgb(0,0,tone[3]))
//Handle husk overlay.
if(husk && ("overlay_husk" in icon_states(species.icobase)))
@@ -727,43 +750,56 @@ There are several things that need to be remembered:
if (QDELING(src))
return
- overlays_raw[SHOES_LAYER] = null
- overlays_raw[SHOES_LAYER_ALT] = null
if(check_draw_shoes())
- var/image/standing
+ shoes.screen_loc = ui_shoes
+
+ var/image/result_layer = null
+
+ //Determine the icon to use
+ var/t_icon = INV_SHOES_DEF_ICON
if(shoes.contained_sprite)
shoes.auto_adapt_species(src)
- var/state = "[UNDERSCORE_OR_NULL(shoes.icon_species_tag)][shoes.item_state][WORN_SHOES]"
-
- standing = image(shoes.icon_override || shoes.icon, state)
+ var/t_state = "[UNDERSCORE_OR_NULL(shoes.icon_species_tag)][shoes.item_state][WORN_SHOES]"
+ result_layer = image(shoes.icon_override || shoes.icon, t_state)
else if(shoes.icon_override)
- standing = image(shoes.icon_override, "[shoes.icon_state]")
+ t_icon = shoes.icon_override
else if(shoes.sprite_sheets && shoes.sprite_sheets[GET_BODY_TYPE])
- standing = image("icon" = shoes.sprite_sheets[GET_BODY_TYPE], "icon_state" = "[shoes.icon_state]")
+ t_icon = shoes.sprite_sheets[GET_BODY_TYPE]
+ else if(shoes.item_icons && (slot_shoes_str in shoes.item_icons))
+ t_icon = shoes.item_icons[slot_shoes_str]
else
- standing = image("icon" = 'icons/mob/feet.dmi', "icon_state" = "[shoes.icon_state]")
+ t_icon = INV_SHOES_DEF_ICON
+
+ if(!result_layer) //Create the image
+ result_layer = image(t_icon, shoes.icon_state)
+
+ if(shoes.color)
+ result_layer.color = shoes.color
+
+ var/image/worn_overlays = shoes.worn_overlays(t_icon)
+ if(worn_overlays)
+ result_layer.overlays.Add(worn_overlays)
+
+ var/list/ovr
+
+ if(shoes.blood_DNA)
+ var/obj/item/clothing/shoes/S = shoes
+ var/image/bloodsies = image(species.blood_mask, "[S.blood_overlay_type]blood")
+ bloodsies.color = shoes.blood_color
+ ovr = list(result_layer, bloodsies)
//Shoe layer stuff from Polaris v1.0333a
var/shoe_layer = SHOES_LAYER
if(istype(shoes, /obj/item/clothing/shoes))
- var/obj/item/clothing/shoes/ushoes = shoes
- if(ushoes.shoes_under_pants == 1)
+ var/obj/item/clothing/shoes/S = shoes
+ if(S.shoes_under_pants == TRUE)
shoe_layer = SHOES_LAYER_ALT
- standing.color = shoes.color
-
- var/list/ovr
-
- if(shoes.blood_color)
- var/image/bloodsies = image("icon" = species.blood_mask, "icon_state" = "shoeblood")
- bloodsies.color = shoes.blood_color
- ovr = list(standing, bloodsies)
-
- overlays_raw[shoe_layer] = ovr || standing
+ overlays_raw[shoe_layer] = ovr || result_layer
else
- if(footprint_color)
- var/image/bloodsies = image("icon" = species.blood_mask, "icon_state" = "shoeblood")
+ if(footprint_color) // Handles bloody feet.
+ var/image/bloodsies = image(species.blood_mask, "shoeblood")
bloodsies.color = footprint_color
overlays_raw[SHOES_LAYER] = bloodsies
else
@@ -1126,8 +1162,8 @@ There are several things that need to be remembered:
standing = image('icons/mob/mob.dmi', "legcuff1")
overlays_raw[LEGCUFF_LAYER] = standing
- if(m_intent != "walk")
- m_intent = "walk"
+ if(m_intent != M_WALK)
+ m_intent = M_WALK
if(hud_used && hud_used.move_intent)
hud_used.move_intent.icon_state = "walking"
diff --git a/code/modules/mob/living/carbon/resist.dm b/code/modules/mob/living/carbon/resist.dm
index 2964e3613be..0564a058f8b 100644
--- a/code/modules/mob/living/carbon/resist.dm
+++ b/code/modules/mob/living/carbon/resist.dm
@@ -146,7 +146,7 @@
break_legcuffs()
return
- var/obj/item/legcuffs/HC = legcuffed
+ var/obj/item/handcuffs/HC = legcuffed
//A default in case you are somehow legcuffed with something that isn't an obj/item/legcuffs type
var/breakouttime = 1200
diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm
index ff6bc3493ab..b49d0cf9e9b 100644
--- a/code/modules/mob/living/life.dm
+++ b/code/modules/mob/living/life.dm
@@ -113,12 +113,18 @@
//this handles hud updates. Calls update_vision() and handle_hud_icons()
/mob/living/proc/handle_regular_hud_updates()
- if(!client || QDELETED(src)) return 0
+ if(!can_update_hud())
+ return FALSE
handle_hud_icons()
handle_vision()
- return 1
+ return TRUE
+
+/mob/living/proc/can_update_hud()
+ if(!client || QDELETED(src))
+ return FALSE
+ return TRUE
/mob/living/proc/handle_vision()
update_sight()
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index f1088867170..da6ff292471 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -159,7 +159,7 @@
var/blocked = run_armor_check(hit_zone, "melee")
standard_weapon_hit_effects(I, user, effective_force, blocked, hit_zone)
- if(I.damtype == BRUTE && prob(33)) // Added blood for whacking non-humans too
+ if(I.damtype == BRUTE && prob(33) && I.force) // Added blood for whacking non-humans too
var/turf/simulated/location = get_turf(src)
if(istype(location)) location.add_blood_floor(src)
diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm
index a86279eb46d..990265b527f 100644
--- a/code/modules/mob/living/say.dm
+++ b/code/modules/mob/living/say.dm
@@ -284,6 +284,7 @@ proc/get_radio_key_from_channel(var/channel)
var/speech_bubble_test = say_test(message)
var/image/speech_bubble = image('icons/mob/talk.dmi',src,"h[speech_bubble_test]")
+ speech_bubble.appearance_flags = RESET_COLOR|RESET_ALPHA
INVOKE_ASYNC(GLOBAL_PROC, /proc/animate_speechbubble, speech_bubble, hear_clients, 30)
do_animate_chat(message, speaking, italics, hear_clients, 30)
diff --git a/code/modules/mob/living/silicon/robot/analyzer.dm b/code/modules/mob/living/silicon/robot/analyzer.dm
index 3f61e306c34..bcec23bcfa1 100644
--- a/code/modules/mob/living/silicon/robot/analyzer.dm
+++ b/code/modules/mob/living/silicon/robot/analyzer.dm
@@ -69,7 +69,11 @@
var/mob/living/carbon/human/H = M
to_chat(user, SPAN_NOTICE("Analyzing Results for \the [H]:"))
to_chat(user, "Key: Electronics/Brute")
-
+ var/obj/item/organ/internal/cell/IC = H.internal_organs_by_name[BP_CELL]
+ if(IC)
+ to_chat(user, SPAN_NOTICE("Cell charge: [IC.percent()] %"))
+ else
+ to_chat(user, SPAN_NOTICE("Cell charge: ERROR - Cell not present"))
to_chat(user, SPAN_NOTICE("External prosthetics:"))
var/organ_found
if(length(H.internal_organs))
diff --git a/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm b/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm
index 8a70b27baf8..f54ad99ab03 100644
--- a/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm
+++ b/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm
@@ -32,6 +32,8 @@
. = ..()
if(SSticker.current_state == GAME_STATE_PLAYING)
enable_drone_spawn()
+ else
+ LAZYADD(SSatoms.late_misc_firers, src)
/obj/machinery/drone_fabricator/Destroy()
. = ..()
diff --git a/code/modules/mob/living/silicon/robot/items/gripper.dm b/code/modules/mob/living/silicon/robot/items/gripper.dm
index 4c16a0dee21..61d2437eda8 100644
--- a/code/modules/mob/living/silicon/robot/items/gripper.dm
+++ b/code/modules/mob/living/silicon/robot/items/gripper.dm
@@ -57,6 +57,8 @@
if(istype(I,typepath))
if(feedback)
to_chat(user, SPAN_NOTICE("You collect \the [I]."))
+ if(isturf(I.loc) && I.Adjacent(user))
+ I.do_pickup_animation(user)
I.forceMove(src)
wrapped = I
wrapped.pixel_x = 0
@@ -179,13 +181,15 @@
// VEEEEERY limited version for mining borgs. Basically only for swapping cells, upgrading the drills, and upgrading custom KAs.
/obj/item/gripper/miner
name = "drill maintenance gripper"
- desc = "A simple grasping tool for the maintenance of heavy drilling machines."
+ desc = "A simple grasping tool for the maintenance and upgrade of heavy drilling machines."
icon_state = "gripper-mining"
can_hold = list(
/obj/item/cell,
/obj/item/stock_parts,
- /obj/item/custom_ka_upgrade
+ /obj/item/custom_ka_upgrade,
+ /obj/item/warp_core,
+ /obj/item/extraction_pack
)
/obj/item/gripper/paperwork
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 33e0640b996..ae76a6a73ff 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -57,7 +57,6 @@
var/overclock_available = FALSE // if the overclock is available for use
// HUD Stuff
- var/obj/screen/cells
var/obj/screen/inv1
var/obj/screen/inv2
var/obj/screen/inv3
diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm
index b050c4325ae..a412e247d48 100644
--- a/code/modules/mob/living/silicon/robot/robot_modules.dm
+++ b/code/modules/mob/living/silicon/robot/robot_modules.dm
@@ -119,7 +119,7 @@ var/global/list/robot_modules = list(
else if(F.times_used)
F.times_used--
- if(E?.reagents.total_volume < E.reagents.maximum_volume)
+ if(E && E.reagents.total_volume < E.reagents.maximum_volume)
E.reagents.add_reagent(/datum/reagent/toxin/fertilizer/monoammoniumphosphate, E.max_water * 0.2)
if(!synths.len)
@@ -221,6 +221,7 @@ var/global/list/robot_modules = list(
src.modules += new /obj/item/device/mass_spectrometer(src)
src.modules += new /obj/item/autopsy_scanner(src)
src.modules += new /obj/item/device/breath_analyzer(src)
+ modules += new /obj/item/taperoll/medical(src)
src.modules += new /obj/item/taperoll/engineering(src) // To enable 'borgs to telegraph danger visually.
src.modules += new /obj/item/inflatable_dispenser(src) // To enable 'borgs to protect Crew from danger in direct hazards.
src.modules += new /obj/item/device/gps(src) // For being located while disabled and coordinating with life sensor consoles.
@@ -294,6 +295,7 @@ var/global/list/robot_modules = list(
src.modules += new /obj/item/gripper/chemistry(src)
src.modules += new /obj/item/tank/jetpack/carbondioxide/synthetic(src)
src.modules += new /obj/item/borg/rescue/mobility(src)
+ modules += new /obj/item/taperoll/medical(src)
src.modules += new /obj/item/taperoll/engineering(src) // To enable 'borgs to telegraph danger visually.
src.modules += new /obj/item/inflatable_dispenser(src) // To enable 'borgs to protect Crew from danger in direct hazards.
src.modules += new /obj/item/device/gps(src) // For being located while disabled and coordinating with life sensor consoles.
@@ -690,6 +692,7 @@ var/global/list/robot_modules = list(
src.modules += new /obj/item/storage/bag/sheetsnatcher/borg(src)
src.modules += new /obj/item/gripper/miner(src)
src.modules += new /obj/item/rfd/mining(src)
+ src.modules += new /obj/item/ore_detector(src)
src.modules += new /obj/item/mining_scanner(src)
src.modules += new /obj/item/ore_radar(src)
src.modules += new /obj/item/pen/robopen(src)
@@ -975,13 +978,21 @@ var/global/list/robot_modules = list(
modules += new /obj/item/mining_scanner(src)
modules += new /obj/item/device/gps/mining(src)
modules += new /obj/item/tank/jetpack/carbondioxide(src)
+ modules += new /obj/item/rfd/mining(src)
+ modules += new /obj/item/tethering_device(src)
+ modules += new /obj/item/ore_detector(src)
var/datum/matter_synth/metal = new /datum/matter_synth/metal(20000)
synths += metal
+
var/obj/item/stack/rods/cyborg/rods = new /obj/item/stack/rods/cyborg(src)
rods.synths = list(metal)
modules += rods
+ var/obj/item/stack/flag/purple/borg/beacons = new /obj/item/stack/flag/purple/borg(src)
+ beacons.synths = list(metal)
+ modules += beacons
+
emag = new /obj/item/gun/energy/plasmacutter/mounted(src)
emag.name = "Mounted Plasma Cutter"
diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm
index 7d3a5527d01..ea1114046b8 100644
--- a/code/modules/mob/living/silicon/silicon.dm
+++ b/code/modules/mob/living/silicon/silicon.dm
@@ -56,7 +56,7 @@
var/id_card_type = /obj/item/card/id/synthetic
var/list/possible_accents = list(ACCENT_TTS, ACCENT_CETI, ACCENT_GIBSON, ACCENT_SOL, ACCENT_LUNA, ACCENT_MARTIAN, ACCENT_VENUS, ACCENT_VENUSJIN, ACCENT_JUPITER, ACCENT_COC, ACCENT_ELYRA, ACCENT_ERIDANI,
- ACCENT_SILVERSUN, ACCENT_KONYAN, ACCENT_EARTH, ACCENT_REPUBICLANSIIK)
+ ACCENT_SILVERSUN, ACCENT_KONYAN, ACCENT_EARTH)
// Misc
uv_intensity = 175 //Lights cast by robots have reduced effect on diona
diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
index b6cb5506849..0cfdcc42efa 100644
--- a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
+++ b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
@@ -39,6 +39,7 @@
speed = 3
mob_size = 6
+ attacktext = "bitten"
attack_emote = "skitters toward"
emote_sounds = list('sound/effects/creatures/spider_critter.ogg')
diff --git a/code/modules/mob/living/simple_animal/hostile/krampus.dm b/code/modules/mob/living/simple_animal/hostile/krampus.dm
index 5da9cc29a56..ad6719862c6 100644
--- a/code/modules/mob/living/simple_animal/hostile/krampus.dm
+++ b/code/modules/mob/living/simple_animal/hostile/krampus.dm
@@ -197,6 +197,6 @@
new /obj/item/xmasgift/large(get_turf(src))
else
- new /obj/item/xmasgift/small(get_turf(src))
+ new /obj/item/xmasgift(get_turf(src))
qdel(src)
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_animal/hostile/morph.dm b/code/modules/mob/living/simple_animal/hostile/morph.dm
index dbf72fef369..77aad032bd2 100644
--- a/code/modules/mob/living/simple_animal/hostile/morph.dm
+++ b/code/modules/mob/living/simple_animal/hostile/morph.dm
@@ -111,9 +111,9 @@
/mob/living/simple_animal/hostile/morph/proc/update_speed()
switch(m_intent)
- if("run")
+ if(M_RUN)
speed = 1.5
- if("walk")
+ if(M_WALK)
speed = 2.5
/mob/living/simple_animal/hostile/morph/proc/allowed(atom/movable/A)
diff --git a/code/modules/mob/living/simple_animal/hostile/rogue_maint_drone.dm b/code/modules/mob/living/simple_animal/hostile/rogue_maint_drone.dm
new file mode 100644
index 00000000000..43b46a0a1e7
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/hostile/rogue_maint_drone.dm
@@ -0,0 +1,55 @@
+/mob/living/simple_animal/hostile/rogue_drone
+ name = "maintenance drone"
+ desc = "A small robot. It looks angry."
+ icon = 'icons/mob/robots.dmi'
+ icon_state = "repairbot"
+ blood_type = "#000000"
+ speak = list("Removing organic waste.", "Pest control in progress.", "Engaging self-preservation protocols.", "Moving to eject unauthorized personnel.")
+ speak_emote = list("blares", "buzzes", "beeps")
+ speak_chance = 1
+ density = FALSE
+ health = 50
+ maxHealth = 50
+ melee_damage_lower = 5
+ melee_damage_upper = 8
+ attacktext = "sliced"
+ faction = "silicon"
+ min_oxy = 0
+ minbodytemp = 0
+ speed = 4
+ mob_size = MOB_TINY
+ var/image/eye_overlay
+
+/mob/living/simple_animal/hostile/rogue_drone/Initialize()
+ . = ..()
+ name = "[initial(name)] ([rand(100, 999)])"
+ eye_overlay = image(icon, "eyes-[icon_state]-emag", layer = EFFECTS_ABOVE_LIGHTING_LAYER)
+ eye_overlay.appearance_flags = KEEP_APART
+ add_overlay(eye_overlay)
+
+/mob/living/simple_animal/hostile/rogue_drone/CanPass(atom/movable/mover, turf/target, height, air_group)
+ . = ..()
+ if(.)
+ if(istype(mover, /mob/living/simple_animal/hostile/rogue_drone))
+ return FALSE
+
+/mob/living/simple_animal/hostile/rogue_drone/death()
+ ..(null, "blows apart!")
+ var/T = get_turf(src)
+ new /obj/effect/gibspawner/robot(T)
+ spark(T, 1, alldirs)
+ qdel(src)
+
+/mob/living/simple_animal/hostile/rogue_drone/validator_living(var/mob/living/L, var/atom/current)
+ . = ..()
+ if(.)
+ if(issilicon(L))
+ return FALSE
+ if(ishuman(L))
+ var/mob/living/carbon/human/H = L
+ if(H.isSynthetic() && !H.isShell())
+ return FALSE
+ if(istype(H.head, /obj/item/holder/drone))
+ return FALSE
+ if(istype(H.wear_suit, /obj/item/clothing/suit/cardborg) && istype(H.head, /obj/item/clothing/head/cardborg))
+ return FALSE
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_animal/hostile/viscerator.dm b/code/modules/mob/living/simple_animal/hostile/viscerator.dm
index 84e95860e00..4b4728ca2d7 100644
--- a/code/modules/mob/living/simple_animal/hostile/viscerator.dm
+++ b/code/modules/mob/living/simple_animal/hostile/viscerator.dm
@@ -36,6 +36,12 @@
spark(T, 3, alldirs)
qdel(src)
+/mob/living/simple_animal/hostile/viscerator/CanPass(atom/movable/mover, turf/target, height, air_group)
+ . = ..()
+ if(.)
+ if(istype(mover, /mob/living/simple_animal/hostile/viscerator))
+ return FALSE
+
/mob/living/simple_animal/hostile/viscerator/proc/wakeup()
stance = HOSTILE_STANCE_IDLE
diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm
index df93a1cd6d3..fc5edb9e50f 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -9,6 +9,7 @@
var/stat = 0 //Whether a mob is alive or dead. TODO: Move this to living - Nodrak
var/can_buckle = TRUE
+ var/obj/screen/cells = null
var/obj/screen/flash = null
var/obj/screen/blind = null
var/obj/screen/hands = null
@@ -132,7 +133,7 @@
var/intent = null//Living
var/shakecamera = 0
var/a_intent = I_HELP//Living
- var/m_intent = "walk"//Living
+ var/m_intent = M_WALK //Living
var/lastKnownIP = null
var/obj/buckled = null//Living
var/obj/item/l_hand = null//Living
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index 0c7393daa64..e489734dd9d 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -19,6 +19,9 @@
/mob/living/carbon/human/proc/isFBP()
return species && (species.appearance_flags & HAS_FBP)
+/mob/living/carbon/human/proc/isShell()
+ return species && (species.name in list(SPECIES_IPC_SHELL, SPECIES_IPC_SHELL_ROGUE))
+
/proc/isMMI(A)
if(isbrain(A))
var/mob/living/carbon/brain/B = A
diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm
index e139ea06ce2..7962fb8805c 100644
--- a/code/modules/mob/mob_movement.dm
+++ b/code/modules/mob/mob_movement.dm
@@ -302,7 +302,7 @@
if (mob_is_human)
var/mob/living/carbon/human/H = mob
//If we're sprinting and able to continue sprinting, then apply the sprint bonus ontop of this
- if (H.m_intent == "run" && (H.status_flags & GODMODE || H.species.handle_sprint_cost(H, tally))) //This will return false if we collapse from exhaustion
+ if (H.m_intent == M_RUN && (H.status_flags & GODMODE || H.species.handle_sprint_cost(H, tally))) //This will return false if we collapse from exhaustion
tally = (tally / (1 + H.sprint_speed_factor)) * config.run_delay_multiplier
else
tally = max(tally * config.walk_delay_multiplier, H.min_walk_delay) //clamp walking speed if its limited
diff --git a/code/modules/modular_computers/NTNet/NTNRC/conversation.dm b/code/modules/modular_computers/NTNet/NTNRC/conversation.dm
index 0fe40f4dae4..8e8c4867a7d 100644
--- a/code/modules/modular_computers/NTNet/NTNRC/conversation.dm
+++ b/code/modules/modular_computers/NTNet/NTNRC/conversation.dm
@@ -26,9 +26,8 @@ var/global/ntnrc_uid = 0
if(C.program_state > PROGRAM_STATE_KILLED)
C.computer.output_message("([get_title(C)]) [username]: [message] (Reply)", 0)
if(!C.silent && C.username != username && C.program_state == PROGRAM_STATE_BACKGROUND)
- for (var/mob/O in hearers(2, get_turf(C.computer)))
- playsound(C.computer, 'sound/machines/twobeep.ogg', 50, 1)
- C.computer.output_message(text("[icon2html(C.computer, O)] *[C.ringtone]*"))
+ playsound(C.computer, 'sound/machines/twobeep.ogg', 50, 1)
+ C.computer.output_message("*[C.ringtone]*")
else if(C.username == username)
ntnet_global.add_log(message, C.computer.network_card, TRUE)
diff --git a/code/modules/modular_computers/computers/subtypes/preset_handheld.dm b/code/modules/modular_computers/computers/subtypes/preset_handheld.dm
index 1d131b9a07f..699dcc712d0 100644
--- a/code/modules/modular_computers/computers/subtypes/preset_handheld.dm
+++ b/code/modules/modular_computers/computers/subtypes/preset_handheld.dm
@@ -116,6 +116,9 @@
. = ..()
card_slot.stored_item = new /obj/item/pen/white
+/obj/item/modular_computer/handheld/preset/research/robotics
+ _app_preset_type = /datum/modular_computer_app_presets/research/robotics
+
/obj/item/modular_computer/handheld/preset/research/rd
_app_preset_type = /datum/modular_computer_app_presets/research/rd
diff --git a/code/modules/modular_computers/computers/subtypes/preset_pda.dm b/code/modules/modular_computers/computers/subtypes/preset_pda.dm
index f877f73321f..ea1db38fb34 100644
--- a/code/modules/modular_computers/computers/subtypes/preset_pda.dm
+++ b/code/modules/modular_computers/computers/subtypes/preset_pda.dm
@@ -120,6 +120,9 @@
. = ..()
card_slot.stored_item = new /obj/item/pen/white
+/obj/item/modular_computer/handheld/pda/research/robotics
+ _app_preset_type = /datum/modular_computer_app_presets/research/robotics
+
/obj/item/modular_computer/handheld/pda/research/rd
_app_preset_type = /datum/modular_computer_app_presets/research/rd
icon_add = "rd"
diff --git a/code/modules/modular_computers/computers/subtypes/preset_wristbound.dm b/code/modules/modular_computers/computers/subtypes/preset_wristbound.dm
index 6344e0c3867..f38b5f76aa0 100644
--- a/code/modules/modular_computers/computers/subtypes/preset_wristbound.dm
+++ b/code/modules/modular_computers/computers/subtypes/preset_wristbound.dm
@@ -46,6 +46,9 @@
icon_state = "wristbound_science"
_app_preset_type = /datum/modular_computer_app_presets/research
+/obj/item/modular_computer/handheld/wristbound/preset/advanced/research/robotics
+ _app_preset_type = /datum/modular_computer_app_presets/research/robotics
+
/obj/item/modular_computer/handheld/wristbound/preset/advanced/command
icon_state = "wristbound_command"
_app_preset_type = /datum/modular_computer_app_presets/command
diff --git a/code/modules/modular_computers/file_system/programs/app_presets.dm b/code/modules/modular_computers/file_system/programs/app_presets.dm
index 7cd95d23a84..75ae31ac5fe 100644
--- a/code/modules/modular_computers/file_system/programs/app_presets.dm
+++ b/code/modules/modular_computers/file_system/programs/app_presets.dm
@@ -166,6 +166,29 @@
)
return _prg_list
+/datum/modular_computer_app_presets/research/robotics
+ name = "research_robotics"
+ display_name = "Research - Robotics"
+ description = "Contains the most common research programs and medical record software."
+ available = TRUE
+
+/datum/modular_computer_app_presets/research/robotics/return_install_programs(obj/item/modular_computer/comp)
+ var/list/_prg_list = list(
+ new /datum/computer_file/program/ntnetdownload(comp),
+ new /datum/computer_file/program/newsbrowser(comp),
+ new /datum/computer_file/program/manifest(comp),
+ new /datum/computer_file/program/filemanager(comp),
+ new /datum/computer_file/program/chatclient(comp),
+ new /datum/computer_file/program/civilian/cargoorder(comp),
+ new /datum/computer_file/program/ntnetmonitor(comp),
+ new /datum/computer_file/program/aidiag(comp),
+ new /datum/computer_file/program/ntsl2_interpreter(comp),
+ new /datum/computer_file/program/records/medical(comp),
+ new /datum/computer_file/program/scanner/science(comp),
+ new /datum/computer_file/program/scanner/gas(comp)
+ )
+ return _prg_list
+
/datum/modular_computer_app_presets/research/rd
name = "research_head"
display_name = "Research - RD"
@@ -182,7 +205,6 @@
new /datum/computer_file/program/civilian/cargoorder(comp),
new /datum/computer_file/program/comm(comp, FALSE),
new /datum/computer_file/program/ntnetmonitor(comp),
- new /datum/computer_file/program/aidiag(comp),
new /datum/computer_file/program/records/employment(comp),
new /datum/computer_file/program/ntsl2_interpreter(comp),
new /datum/computer_file/program/chemistry_codex(comp),
diff --git a/code/modules/modular_computers/file_system/programs/generic/records.dm b/code/modules/modular_computers/file_system/programs/generic/records.dm
index f946dce8b78..a674a884f87 100644
--- a/code/modules/modular_computers/file_system/programs/generic/records.dm
+++ b/code/modules/modular_computers/file_system/programs/generic/records.dm
@@ -37,7 +37,7 @@
extended_desc = "Used to view, edit and maintain medical records."
record_prefix = "Medical "
- required_access_run = list(access_medical_equip, access_forensics_lockers, access_detective, access_hop)
+ required_access_run = list(access_medical_equip, access_forensics_lockers, access_detective, access_robotics, access_hop)
required_access_download = access_heads
available_on_ntnet = TRUE
diff --git a/code/modules/modular_computers/items/paper_scanner.dm b/code/modules/modular_computers/items/paper_scanner.dm
index 5b05c2d92ec..623c6da7393 100644
--- a/code/modules/modular_computers/items/paper_scanner.dm
+++ b/code/modules/modular_computers/items/paper_scanner.dm
@@ -88,8 +88,9 @@
var/data_to_save = ""
for(var/thing in pages_to_scan)
var/obj/item/paper/page = thing
- data_to_save += page.info
+ var/p_info = html2pencode(page.info)
+ data_to_save += strip_html_properly(p_info)
data_to_save += "\[br\]"
F.stored_data = data_to_save
if(!drive.store_file(F))
- to_chat(usr, SPAN_WARNING("\The [drive] does not have enough space to store the latest scanned file."))
+ to_chat(user, SPAN_WARNING("\The [drive] does not have enough space to store the latest scanned file."))
diff --git a/code/modules/multiz/structures.dm b/code/modules/multiz/structures.dm
index 0d5dd22765a..2854d69b934 100644
--- a/code/modules/multiz/structures.dm
+++ b/code/modules/multiz/structures.dm
@@ -17,7 +17,18 @@
var/base_icon = "ladder"
var/const/climb_time = 2 SECONDS
- var/static/list/climbsounds = list('sound/effects/ladder1.ogg','sound/effects/ladder2.ogg','sound/effects/ladder3.ogg','sound/effects/ladder4.ogg')
+ var/list/climbsounds = list('sound/effects/ladder1.ogg','sound/effects/ladder2.ogg','sound/effects/ladder3.ogg','sound/effects/ladder4.ogg')
+ var/climb_sound_vol = 50
+ var/climb_sound_vary = FALSE
+
+ var/list/destroy_tools
+
+/obj/structure/ladder/mining
+ icon = 'icons/obj/mining.dmi'
+ climbsounds = list('sound/effects/stonedoor_openclose.ogg')
+ climb_sound_vol = 30
+ climb_sound_vary = TRUE
+ destroy_tools = list(/obj/item/pickaxe, /obj/item/gun/energy/plasmacutter)
/obj/structure/ladder/Initialize()
. = ..()
@@ -42,7 +53,14 @@
target_up = null
return ..()
-/obj/structure/ladder/attackby(obj/item/C as obj, mob/user as mob)
+/obj/structure/ladder/attackby(obj/item/C, mob/user)
+ if(LAZYLEN(destroy_tools))
+ if(is_type_in_list(C, destroy_tools))
+ user.visible_message("[user] starts breaking down \the [src] with \the [C]!", SPAN_NOTICE("You start breaking down \the [src] with \the [C]."))
+ if(do_after(user, 10 SECONDS, TRUE))
+ user.visible_message("[user] breaks down \the [src] with \the [C]!", SPAN_NOTICE("You break down \the [src] with \the [C]."))
+ qdel(src)
+ return
attack_hand(user)
/obj/structure/ladder/attack_robot(mob/user)
@@ -130,6 +148,8 @@
return TRUE
/obj/structure/ladder/proc/climbLadder(var/mob/M, var/target_ladder)
+ if(!target_ladder)
+ return
var/turf/T = get_turf(target_ladder)
var/turf/LAD = get_turf(src)
var/direction = UP
@@ -146,8 +166,8 @@
if(!A.CanPass(M, M.loc, 1.5, 0))
to_chat(M, "\The [A] is blocking \the [src].")
return FALSE
- playsound(src, pick(climbsounds), 50)
- playsound(target_ladder, pick(climbsounds), 50)
+ playsound(src, pick(climbsounds), climb_sound_vol, climb_sound_vary)
+ playsound(target_ladder, pick(climbsounds), climb_sound_vol, climb_sound_vary)
var/obj/item/grab/G = M.l_hand
if (!istype(G))
G = M.r_hand
@@ -165,6 +185,13 @@
allowed_directions = UP
icon_state = "ladder10"
+/obj/structure/ladder/up/mining
+ icon = 'icons/obj/mining.dmi'
+ climbsounds = list('sound/effects/stonedoor_openclose.ogg')
+ climb_sound_vol = 30
+ climb_sound_vary = TRUE
+ destroy_tools = list(/obj/item/pickaxe, /obj/item/gun/energy/plasmacutter)
+
/obj/structure/ladder/updown
allowed_directions = UP|DOWN
icon_state = "ladder11"
diff --git a/code/modules/organs/blood.dm b/code/modules/organs/blood.dm
index dbcc8bf3417..87dd535d373 100644
--- a/code/modules/organs/blood.dm
+++ b/code/modules/organs/blood.dm
@@ -213,7 +213,10 @@
if (!injected || !our)
return
- if(blood_incompatible(injected.data["blood_type"],our.data["blood_type"],injected.data["species"],our.data["species"]) && !(mind && mind.vampire))
+ var/datum/vampire/vampire
+ if(mind)
+ vampire = mind.antag_datums[MODE_VAMPIRE]
+ if(!vampire && blood_incompatible(injected.data["blood_type"],our.data["blood_type"],injected.data["species"],our.data["species"]))
reagents.add_reagent(/datum/reagent/toxin,amount * 0.5)
reagents.update_total()
else
diff --git a/code/modules/organs/internal/_internal.dm b/code/modules/organs/internal/_internal.dm
index bc6015b9e48..0f8fa376ce5 100644
--- a/code/modules/organs/internal/_internal.dm
+++ b/code/modules/organs/internal/_internal.dm
@@ -121,6 +121,7 @@
if(istype(owner) && (toxin_type in owner.chem_effects))
take_damage(owner.chem_effects[toxin_type] * 0.1 * PROCESS_ACCURACY, prob(1))
handle_regeneration()
+ tick_surge_damage() //Yes, this is intentional.
/obj/item/organ/internal/proc/handle_regeneration()
if(!damage || BP_IS_ROBOTIC(src) || !owner || owner.chem_effects[CE_TOXIN] || owner.is_asystole())
diff --git a/code/modules/organs/internal/eyes.dm b/code/modules/organs/internal/eyes.dm
index 0bf62b842a1..17617f60798 100644
--- a/code/modules/organs/internal/eyes.dm
+++ b/code/modules/organs/internal/eyes.dm
@@ -54,6 +54,12 @@
if(is_broken())
owner.eye_blind = 20
+/obj/item/organ/internal/eyes/do_surge_effects()
+ owner.overlay_fullscreen("noise", /obj/screen/fullscreen/flash/noise)
+
+/obj/item/organ/internal/eyes/clear_surge_effects()
+ owner.clear_fullscreen("noise")
+
/obj/item/organ/internal/eyes/robotize()
..()
verbs |= /obj/item/organ/internal/eyes/proc/change_eye_color
diff --git a/code/modules/organs/internal/heart.dm b/code/modules/organs/internal/heart.dm
index 1555ac84851..6863423ac26 100644
--- a/code/modules/organs/internal/heart.dm
+++ b/code/modules/organs/internal/heart.dm
@@ -194,6 +194,12 @@
return pulse > PULSE_NONE || BP_IS_ROBOTIC(src) || (owner.status_flags & FAKEDEATH)
+/obj/item/organ/internal/heart/do_surge_effects()
+ var/obj/item/organ/external/E = owner.organs_by_name[parent_organ]
+ if(prob(surge_damage))
+ owner.custom_pain(SPAN_DANGER("Your [E.name] stings horribly!"), 15, FALSE, E)
+ sound_to(owner, sound('sound/effects/singlebeat.ogg', 0, 0, 0, 100))
+
/obj/item/organ/internal/heart/listen()
if(BP_IS_ROBOTIC(src) && is_working())
if(is_bruised())
diff --git a/code/modules/organs/internal/lungs.dm b/code/modules/organs/internal/lungs.dm
index e1b3382c27a..2102db897e6 100644
--- a/code/modules/organs/internal/lungs.dm
+++ b/code/modules/organs/internal/lungs.dm
@@ -46,7 +46,7 @@
/obj/item/organ/internal/lungs/process()
..()
- if(!owner)
+ if(!owner || owner.stat == DEAD)
return
if(germ_level > INFECTION_LEVEL_ONE)
diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm
index 2882be85ee6..1695309d415 100644
--- a/code/modules/organs/organ.dm
+++ b/code/modules/organs/organ.dm
@@ -16,6 +16,8 @@
//Organ damage stats.
var/damage = 0 // amount of damage to the organ
+ var/surge_damage = 0 // EMP damage counter.
+ var/surge_time = 0
var/min_broken_damage = 30
var/min_bruised_damage = 10 // Damage before considered bruised
var/max_damage = 30
@@ -113,6 +115,7 @@
blood_DNA[dna.unique_enzymes] = dna.b_type
if(internal)
holder.internal_organs |= src
+ START_PROCESSING(SSprocessing, src)
/obj/item/organ/proc/set_dna(var/datum/dna/new_dna)
if(new_dna)
@@ -142,7 +145,6 @@
return max_damage > 0
/obj/item/organ/process()
-
if(loc != owner)
owner = null
@@ -164,6 +166,9 @@
germ_level = 0
return
+ if((status & ORGAN_ASSISTED) && surge_damage)
+ tick_surge_damage()
+
if(!owner)
if (QDELETED(reagents))
log_debug("Organ [DEBUG_REF(src)] had QDELETED reagents! Regenerating.")
@@ -194,6 +199,22 @@
if(damage >= max_damage)
die()
+/obj/item/organ/proc/tick_surge_damage()
+ if(surge_damage)
+ do_surge_effects()
+ if(surge_time + 1 SECOND < world.time)
+ surge_damage = max(0, surge_damage - 10)
+ surge_time = world.time
+ if(!surge_damage)
+ surge_time = 0
+ clear_surge_effects()
+
+/obj/item/organ/proc/do_surge_effects()
+ return
+
+/obj/item/organ/proc/clear_surge_effects()
+ return
+
/obj/item/organ/examine(mob/user)
..(user)
if(status & ORGAN_DEAD)
@@ -307,7 +328,7 @@
owner.custom_pain("Something inside your [parent.name] hurts a lot.", 1)
/obj/item/organ/proc/robotize() //Being used to make robutt hearts, etc
- robotic = 2
+ robotic = ROBOTIC_MECHANICAL
status = ORGAN_ROBOT
status |= ORGAN_ASSISTED
if(robotic_name)
@@ -323,7 +344,7 @@
name = initial(name)
icon_state = initial(icon_state)
-/obj/item/organ/emp_act(severity)
+/obj/item/organ/emp_act(var/severity)
if(!(status & ORGAN_ASSISTED))
return
@@ -334,14 +355,23 @@
switch (severity)
if (1.0)
- take_damage(rand(7,20) * emp_coeff * organ_fragility)
+ take_surge_damage(15 * emp_coeff * organ_fragility)
if (2.0)
- take_damage(rand(3,7) * emp_coeff * organ_fragility)
+ take_surge_damage(8 * emp_coeff * organ_fragility)
if(3.0)
- take_damage(rand(3) * emp_coeff * organ_fragility)
+ take_surge_damage(4 * emp_coeff * organ_fragility)
+
+ return TRUE
+
+#define MAXIMUM_SURGE_DAMAGE 100
+/obj/item/organ/proc/take_surge_damage(var/surge)
+ if(!(status & ORGAN_ASSISTED))
+ return //We check earlier, but just to make sure.
+
+ surge_damage = Clamp(0, surge + surge_damage, MAXIMUM_SURGE_DAMAGE) //We want X seconds at most of hampered movement or what have you.
+ surge_time = world.time
/obj/item/organ/proc/removed(var/mob/living/carbon/human/target,var/mob/living/user)
-
if(!istype(owner))
return
diff --git a/code/modules/organs/organ_external.dm b/code/modules/organs/organ_external.dm
index bab9b92b1f7..86b223a44b5 100644
--- a/code/modules/organs/organ_external.dm
+++ b/code/modules/organs/organ_external.dm
@@ -7,6 +7,8 @@
#define DROPLIMB_THRESHOLD_TEAROFF 2
#define DROPLIMB_THRESHOLD_DESTROY 1
+#define FRACTURE_AND_TENDON_DAM_THRESHOLD 30 // if a weapon does more than this amount of damage, it's powerful enough to sever the tendon AND fracture the bone, if it's a sharp weapon
+
/obj/item/organ/external
name = "external"
min_broken_damage = 30
@@ -279,7 +281,12 @@
handle_limb_gibbing(used_weapon, brute, burn)
if(brute_dam + brute > min_broken_damage && prob(brute_dam + brute * (1 + blunt)))
- fracture()
+ if(damage_flags & DAM_EDGE)
+ sever_tendon()
+ if(brute_dam > FRACTURE_AND_TENDON_DAM_THRESHOLD)
+ fracture()
+ else
+ fracture()
// High brute damage or sharp objects may damage internal organs
if(length(internal_organs))
@@ -313,6 +320,8 @@
update_damages()
if(owner)
owner.updatehealth() //droplimb will call updatehealth() again if it does end up being called
+ if(status & ORGAN_BLEEDING)
+ owner.update_bandages()
return update_icon()
@@ -322,12 +331,8 @@
var/damage_amt = brute
var/cur_damage = brute_dam
- var/laser = (damage_flags & DAM_LASER)
var/sharp = (damage_flags & DAM_SHARP)
- if(laser)
- return FALSE
-
if(BP_IS_ROBOTIC(src))
damage_amt += burn
cur_damage += burn_dam
@@ -529,26 +534,22 @@ This function completely restores a damaged organ to perfect condition.
//Determines if we even need to process this organ.
/obj/item/organ/external/proc/need_process()
if(status & (ORGAN_CUT_AWAY|ORGAN_BLEEDING|ORGAN_BROKEN|ORGAN_DESTROYED|ORGAN_SPLINTED|ORGAN_DEAD|ORGAN_MUTATED))
- return 1
+ return TRUE
+ if(surge_damage)
+ return TRUE
if(brute_dam || burn_dam)
- return 1
+ return TRUE
if(last_dam != brute_dam + burn_dam) // Process when we are fully healed up.
last_dam = brute_dam + burn_dam
- return 1
+ return TRUE
else
last_dam = brute_dam + burn_dam
if(germ_level)
- return 1
- return 0
+ return TRUE
+ return FALSE
/obj/item/organ/external/process()
if(owner)
- //Dismemberment
- //if(parent && parent.is_stump()) //should never happen
- // warning("\The [src] ([src.type]) belonging to [owner] ([owner.type]) was attached to a stump")
- // remove()
- // return
-
// Process wounds, doing healing etc. Only do this every few ticks to save processing power
if(owner.life_tick % wound_update_accuracy == 0)
update_wounds()
@@ -560,13 +561,12 @@ This function completely restores a damaged organ to perfect condition.
if(trace_chemicals[chemID] <= 0)
trace_chemicals.Remove(chemID)
- //Bone fractures
- if(config.bones_can_break && !(status & ORGAN_ROBOT) && !(status & ORGAN_PLANT) && brute_dam > min_broken_damage * config.organ_health_multiplier)
- src.fracture()
-
if(!(status & ORGAN_BROKEN))
perma_injury = 0
+ if(surge_damage && (status & ORGAN_ASSISTED))
+ tick_surge_damage() //Yes, this being here is intentional since this proc does not call ..() unless the owner is null.
+
//Infections
update_germs()
@@ -575,6 +575,10 @@ This function completely restores a damaged organ to perfect condition.
else
..()
+/obj/item/organ/external/do_surge_effects()
+ if(prob(surge_damage))
+ owner.custom_pain("The artificial nerves in your [name] scream out in pain!", surge_damage/6)
+
/obj/item/organ/external/proc/check_rigsplints()
if((status & ORGAN_BROKEN) && !(status & ORGAN_SPLINTED))
if(istype(owner,/mob/living/carbon/human))
@@ -587,7 +591,7 @@ This function completely restores a damaged organ to perfect condition.
var/obj/item/clothing/suit/space/suit = H.wear_suit
if(isnull(suit.supporting_limbs))
return
- to_chat(owner, "You feel \the [suit] constrict about your [name], supporting it.")
+ to_chat(owner, SPAN_WARNING("You feel \the [suit] constrict about your [name], supporting it."))
status |= ORGAN_SPLINTED
suit.supporting_limbs |= src
owner.update_hud_hands()
@@ -744,6 +748,7 @@ Note that amputating the affected organ does in fact remove the infection from t
number_wounds = 0
brute_dam = 0
burn_dam = 0
+ var/cut_dam = 0
status &= ~ORGAN_BLEEDING
var/clamped = 0
@@ -757,6 +762,8 @@ Note that amputating the affected organ does in fact remove the infection from t
burn_dam += W.damage
else
brute_dam += W.damage
+ if(W.damage_type == CUT)
+ cut_dam += W.damage
if(!(status & ORGAN_ROBOT) && W.bleeding() && (H && !(H.species.flags & NO_BLOOD)))
W.bleed_timer--
@@ -772,7 +779,10 @@ Note that amputating the affected organ does in fact remove the infection from t
//Bone fractures
if(config.bones_can_break && brute_dam > min_broken_damage * config.organ_health_multiplier && !(status & ORGAN_ROBOT))
- src.fracture()
+ if(cut_dam > brute_dam * 0.5)
+ sever_tendon()
+ else
+ fracture()
update_damage_ratios()
/obj/item/organ/external/proc/update_damage_ratios()
@@ -923,7 +933,7 @@ Note that amputating the affected organ does in fact remove the infection from t
****************************************************/
/obj/item/organ/external/proc/is_stump()
- return 0
+ return FALSE
/obj/item/organ/external/proc/release_restraints(var/mob/living/carbon/human/holder)
if(!holder)
@@ -1012,7 +1022,7 @@ Note that amputating the affected organ does in fact remove the infection from t
playsound(src.loc, /decl/sound_category/fracture_sound, 100, 1, -2)
status |= ORGAN_BROKEN
- broken_description = pick("Broken","Fracture","Hairline fracture")
+ broken_description = pick("Broken","Fracture","Hairline Fracture")
perma_injury = brute_dam
// Fractures have a chance of getting you out of restraints
@@ -1094,10 +1104,22 @@ Note that amputating the affected organ does in fact remove the infection from t
return 0
/obj/item/organ/external/is_usable()
- return ..() && !is_dislocated() && !(status & ORGAN_TENDON_CUT) && (!can_feel_pain() || get_pain() < pain_disability_threshold) && brute_ratio < 1 && burn_ratio < 1
+ if(is_dislocated())
+ return FALSE
+ if(status & ORGAN_TENDON_CUT)
+ return FALSE
+ if(parent && (parent.status & ORGAN_TENDON_CUT))
+ return FALSE
+ if(can_feel_pain() && get_pain() > pain_disability_threshold)
+ return FALSE
+ if(brute_ratio > 1)
+ return FALSE
+ if(burn_ratio > 1)
+ return FALSE
+ return ..()
/obj/item/organ/external/proc/is_malfunctioning()
- if(BP_IS_ROBOTIC(src) && (brute_ratio + burn_ratio) >= 0.3 && prob(brute_dam + burn_dam))
+ if(BP_IS_ROBOTIC(src) && (brute_ratio + burn_ratio) >= 0.3 && prob(brute_dam + burn_dam) || (surge_damage > (MAXIMUM_SURGE_DAMAGE * 0.25)))
return TRUE
if(robotize_type)
var/datum/robolimb/R = all_robolimbs[robotize_type]
@@ -1293,9 +1315,19 @@ Note that amputating the affected organ does in fact remove the infection from t
/obj/item/organ/external/proc/sever_tendon()
if(!(limb_flags & ORGAN_HAS_TENDON) || (status & ORGAN_ROBOT) || (status & ORGAN_TENDON_CUT))
return FALSE
- else
- status |= ORGAN_TENDON_CUT
- return TRUE
+
+ var/message = pick("tore apart", "ripped away")
+ owner.visible_message(\
+ "You hear a loud snapping sound coming from \the [owner]!",\
+ "Something feels like it [message] in your [name]!",\
+ "You hear a sickening snap!")
+ if(owner.species && owner.can_feel_pain())
+ owner.emote("scream")
+ owner.flash_strong_pain()
+
+ playsound(src.loc, 'sound/effects/snap.ogg', 40, 1, -2)
+ status |= ORGAN_TENDON_CUT
+ return TRUE
// Damage procs
/obj/item/organ/external/proc/get_brute_damage()
diff --git a/code/modules/organs/organ_icon.dm b/code/modules/organs/organ_icon.dm
index f1eb43dbf94..c3fd7fce6c9 100644
--- a/code/modules/organs/organ_icon.dm
+++ b/code/modules/organs/organ_icon.dm
@@ -59,9 +59,14 @@
cut_overlays()
if(!owner || !owner.species)
return
+ var/is_frenzied = FALSE
+ if(owner.mind)
+ var/datum/vampire/vampire = owner.mind.antag_datums[MODE_VAMPIRE]
+ if(vampire && (vampire.status & VAMP_FRENZIED))
+ is_frenzied = TRUE
if(owner.species.has_organ[owner.species.vision_organ])
var/obj/item/organ/internal/eyes/eyes = owner.get_eyes()
- if(eyes && species.eyes)
+ if(eyes && species.eyes && !is_frenzied)
var/eyecolor
if (eyes.eye_colour)
eyecolor = rgb(eyes.eye_colour[1], eyes.eye_colour[2], eyes.eye_colour[3])
@@ -100,6 +105,15 @@
return mob_icon
+/obj/item/organ/external/head/get_additional_images(var/mob/living/carbon/human/H)
+ if(!H.mind)
+ return
+ var/datum/vampire/vampire = H.mind.antag_datums[MODE_VAMPIRE]
+ if(vampire && (vampire.status & VAMP_FRENZIED))
+ var/image/return_image = image(H.species.eyes_icons, H, "[H.species.eyes]_frenzy", EFFECTS_ABOVE_LIGHTING_LAYER)
+ return_image.appearance_flags = KEEP_APART
+ return list(return_image)
+
/obj/item/organ/external/proc/apply_markings(restrict_to_robotic = FALSE)
if (!cached_markings)
update_marking_cache()
@@ -206,6 +220,9 @@
return mob_icon
+/obj/item/organ/external/proc/get_additional_images(var/mob/living/carbon/human/H)
+ return
+
// new damage icon system
// adjusted to set damage_state to brute/burn code only (without r_name0 as before)
/obj/item/organ/external/update_icon()
@@ -286,3 +303,17 @@ var/list/robot_hud_colours = list("#ffffff","#cccccc","#aaaaaa","#888888","#6666
var/list/hud_colours = !BP_IS_ROBOTIC(src) ? flesh_hud_colours : robot_hud_colours
hud_damage_image.color = hud_colours[max(1,min(Ceiling(dam_state*hud_colours.len),hud_colours.len))]
return hud_damage_image
+
+/obj/item/organ/external/proc/bandage_level()
+ if(damage_state_text() == "00")
+ return 0
+ if(!is_bandaged())
+ return 0
+ if(burn_dam + brute_dam == 0)
+ . = 0
+ else if (burn_dam + brute_dam < (max_damage * 0.25 / 2))
+ . = 1
+ else if (burn_dam + brute_dam < (max_damage * 0.75 / 2))
+ . = 2
+ else
+ . = 3
diff --git a/code/modules/organs/subtypes/augment.dm b/code/modules/organs/subtypes/augment.dm
index bd5160a1583..9c6da3f17b6 100644
--- a/code/modules/organs/subtypes/augment.dm
+++ b/code/modules/organs/subtypes/augment.dm
@@ -3,7 +3,7 @@
icon_state = "augment"
parent_organ = BP_CHEST
organ_tag = "augment"
- robotic = 2
+ robotic = ROBOTIC_MECHANICAL
emp_coeff = 2
is_augment = TRUE
species_restricted = list(SPECIES_HUMAN,SPECIES_HUMAN_OFFWORLD,
diff --git a/code/modules/organs/subtypes/autakh.dm b/code/modules/organs/subtypes/autakh.dm
index 9d831c4be5d..aebad9d3c59 100644
--- a/code/modules/organs/subtypes/autakh.dm
+++ b/code/modules/organs/subtypes/autakh.dm
@@ -72,7 +72,7 @@
icon_state = "anchor"
organ_tag = "anchor"
parent_organ = BP_HEAD
- robotic = 2
+ robotic = ROBOTIC_MECHANICAL
var/suffered_revelation = FALSE
/obj/item/organ/internal/anchor/Initialize()
diff --git a/code/modules/organs/subtypes/machine.dm b/code/modules/organs/subtypes/machine.dm
index 2ffe9981511..96e1ec11c29 100644
--- a/code/modules/organs/subtypes/machine.dm
+++ b/code/modules/organs/subtypes/machine.dm
@@ -62,26 +62,92 @@
desc = "A small, powerful cell for use in fully prosthetic bodies."
icon = 'icons/obj/power.dmi'
icon_state = "scell"
- organ_tag = "cell"
+ organ_tag = BP_CELL
parent_organ = BP_CHEST
- vital = 1
- var/emp_counter = 0
+ vital = TRUE
+ max_damage = 80
+ relative_size = 80
+ var/open = FALSE
+ var/obj/item/cell/cell = /obj/item/cell/super
+ //at 0.8 completely depleted after 60ish minutes of constant walking or 130 minutes of standing still
+ var/servo_cost = 0.8
/obj/item/organ/internal/cell/Initialize()
robotize()
+ cell = new cell(src)
. = ..()
+/obj/item/organ/internal/cell/proc/percent()
+ if(!cell)
+ return 0
+ return get_charge()/cell.maxcharge * 100
+
+/obj/item/organ/internal/cell/proc/get_charge()
+ if(!cell)
+ return 0
+ if(status & ORGAN_DEAD)
+ return 0
+ return round(cell.charge*(1 - damage/max_damage))
+
+/obj/item/organ/internal/cell/proc/checked_use(var/amount)
+ if(!is_usable() || !cell)
+ return FALSE
+ return cell.checked_use(amount)
+
+/obj/item/organ/internal/cell/proc/use(var/amount)
+ if(!is_usable() || !cell)
+ return
+ return cell.use(amount)
+
/obj/item/organ/internal/cell/process()
..()
- if(emp_counter)
- emp_counter--
+ if(!owner)
+ return
+ if(owner.stat == DEAD) //not a drain anymore
+ return
+ var/cost = get_power_drain()
+ if(world.time - owner.l_move_time < 15)
+ cost *= 2
+ if(!checked_use(cost) && owner.isSynthetic())
+ if(!owner.lying && !owner.buckled)
+ to_chat(owner, "You don't have enough energy to function!")
+ owner.Paralyse(3)
+
+/obj/item/organ/internal/cell/proc/get_power_drain()
+ return servo_cost
/obj/item/organ/internal/cell/emp_act(severity)
- emp_counter += 30/severity
- if(emp_counter >= 30)
- owner.Paralyse(emp_counter/6)
- to_chat(owner, "%#/ERR: Power leak detected!$%^/")
+ ..()
+ if(cell)
+ cell.emp_act(severity)
+/obj/item/organ/internal/cell/attackby(obj/item/W, mob/user)
+ if(isscrewdriver(W))
+ if(open)
+ open = FALSE
+ to_chat(user, SPAN_NOTICE("You screw the battery panel in place."))
+ else
+ open = TRUE
+ to_chat(user, SPAN_NOTICE("You unscrew the battery panel."))
+
+ if(iscrowbar(W))
+ if(open)
+ if(cell)
+ user.put_in_hands(cell)
+ to_chat(user, SPAN_NOTICE("You remove \the [cell] from \the [src]."))
+ cell = null
+
+ if (istype(W, /obj/item/cell))
+ if(open)
+ if(cell)
+ to_chat(user, SPAN_WARNING("There is a power cell already installed."))
+ else if(user.unEquip(W, src))
+ cell = W
+ to_chat(user, SPAN_NOTICE("You insert \the [cell]."))
+
+/obj/item/organ/internal/cell/listen()
+ if(get_charge())
+ return "faint hum of the power bank"
/obj/item/organ/internal/surge
name = "surge preventor"
@@ -125,7 +191,7 @@
/obj/item/organ/internal/eyes/optical_sensor
name = "optical sensor"
singular_name = "optical sensor"
- organ_tag = "optics"
+ organ_tag = BP_EYES
icon = 'icons/obj/robot_component.dmi'
icon_state = "camera"
dead_icon = "camera_broken"
@@ -136,7 +202,7 @@
/obj/item/organ/internal/ipc_tag
name = "identification tag"
- organ_tag = "ipc tag"
+ organ_tag = BP_IPCTAG
parent_organ = BP_HEAD
icon = 'icons/obj/ipc_utilities.dmi'
icon_state = "ipc_tag"
@@ -211,7 +277,7 @@
name = "brain"
organ_tag = BP_BRAIN
parent_organ = BP_HEAD
- vital = 1
+ vital = TRUE
var/obj/item/device/mmi/stored_mmi
/obj/item/organ/internal/mmi_holder/proc/update_from_mmi()
@@ -299,7 +365,7 @@
icon_state = "scell"
organ_tag = "shielded cell"
parent_organ = BP_CHEST
- vital = 1
+ vital = TRUE
emp_coeff = 0.1
/obj/item/organ/internal/cell/Initialize()
diff --git a/code/modules/organs/subtypes/vaurca.dm b/code/modules/organs/subtypes/vaurca.dm
index f450ce59bfc..2e292474f1e 100644
--- a/code/modules/organs/subtypes/vaurca.dm
+++ b/code/modules/organs/subtypes/vaurca.dm
@@ -15,14 +15,14 @@
/obj/item/organ/internal/kidneys/vaurca/robo
icon_state = "kidney_vaurca"
organ_tag = BP_VAURCA_KIDNEYS
- robotic = 2
+ robotic = ROBOTIC_MECHANICAL
robotic_name = null
robotic_sprite = null
/obj/item/organ/internal/liver/vaurca/robo
icon_state = "liver_vaurca"
organ_tag = BP_VAURCA_LIVER
- robotic = 2
+ robotic = ROBOTIC_MECHANICAL
robotic_name = null
robotic_sprite = null
@@ -37,21 +37,21 @@
organ_tag = BP_PHORON_RESERVOIR
parent_organ = BP_CHEST
icon_state = "phoron_reservoir"
- robotic = 1
+ robotic = ROBOTIC_ASSISTED
/obj/item/organ/vaurca/filtrationbit
name = BP_FILTRATION_BIT
organ_tag = BP_FILTRATION_BIT
parent_organ = BP_HEAD
icon_state = "filter"
- robotic = 2
+ robotic = ROBOTIC_MECHANICAL
/obj/item/organ/vaurca/neuralsocket
name = BP_NEURAL_SOCKET
organ_tag = BP_NEURAL_SOCKET
icon_state = "neural_socket"
parent_organ = BP_HEAD
- robotic = 2
+ robotic = ROBOTIC_MECHANICAL
obj/item/organ/vaurca/neuralsocket/process()
if (is_broken())
@@ -81,7 +81,7 @@ obj/item/organ/vaurca/neuralsocket/process()
organ_tag = BP_PHORON_RESERVE
parent_organ = BP_CHEST
icon_state = "breathing_app"
- robotic = 1
+ robotic = ROBOTIC_ASSISTED
var/datum/gas_mixture/air_contents = null
var/distribute_pressure = ((2*ONE_ATMOSPHERE)*O2STANDARD)
var/volume = 50
diff --git a/code/modules/power/antimatter/computer.dm b/code/modules/power/antimatter/computer.dm
deleted file mode 100644
index 470ab4242ec..00000000000
--- a/code/modules/power/antimatter/computer.dm
+++ /dev/null
@@ -1,98 +0,0 @@
-//This file was auto-corrected by findeclaration.exe on 29/05/2012 15:03:05
-#define STATE_DEFAULT 1
-#define STATE_INJECTOR 2
-#define STATE_ENGINE 3
-
-
-/obj/machinery/computer/am_engine
- name = "Antimatter Engine Console"
- icon = 'icons/obj/stationobjs.dmi'
- icon_state = "comm_computer"
- req_access = list(ACCESS_ENGINE)
- var/engine_id = 0
- var/authenticated = 0
- var/obj/machinery/power/am_engine/engine/connected_E = null
- var/obj/machinery/power/am_engine/injector/connected_I = null
- var/state = STATE_DEFAULT
-
-/obj/machinery/computer/am_engine/New()
- ..()
- spawn( 24 )
- for(var/obj/machinery/power/am_engine/engine/E in machines)
- if(E.engine_id == src.engine_id)
- src.connected_E = E
- for(var/obj/machinery/power/am_engine/injector/I in machines)
- if(I.engine_id == src.engine_id)
- src.connected_I = I
- return
-
-/obj/machinery/computer/am_engine/Topic(href, href_list)
- if(..())
- return
- usr.machine = src
-
- if(!href_list["operation"])
- return
- switch(href_list["operation"])
- // main interface
- if("activate")
- src.connected_E.engine_process()
- if("engine")
- src.state = STATE_ENGINE
- if("injector")
- src.state = STATE_INJECTOR
- if("main")
- src.state = STATE_DEFAULT
- if("login")
- var/mob/M = usr
- var/obj/item/card/id/I = M.equipped()
- if (I && istype(I))
- if(src.check_access(I))
- authenticated = 1
- if("deactivate")
- src.connected_E.stopping = 1
- if("logout")
- authenticated = 0
-
- src.updateUsrDialog()
-
-/obj/machinery/computer/am_engine/attack_ai(var/mob/user as mob)
- if(!ai_can_interact(user))
- return
- src.add_hiddenprint(user)
- return src.attack_hand(user)
-
-/obj/machinery/computer/am_engine/attack_paw(var/mob/user as mob)
- return src.attack_hand(user)
-
-/obj/machinery/computer/am_engine/attack_hand(var/mob/user as mob)
- if(..())
- return
- user.machine = src
- var/dat = "Engine Computer"
- switch(src.state)
- if(STATE_DEFAULT)
- if (src.authenticated)
- dat += " \[ Log Out \] "
- dat += " \[ Engine Menu \]"
- dat += " \[ Injector Menu \]"
- else
- dat += " \[ Log In \]"
- if(STATE_INJECTOR)
- if(src.connected_I.injecting)
- dat += " \[ Injecting \] "
- else
- dat += " \[ Injecting not in progress \] "
- if(STATE_ENGINE)
- if(src.connected_E.stopping)
- dat += " \[ STOPPING \]"
- else if(src.connected_E.operating && !src.connected_E.stopping)
- dat += " \[ Emergency Stop \]"
- else
- dat += " \[ Activate Engine \]"
- dat += " Contents: [src.connected_E.H_fuel]kg of Hydrogen [src.connected_E.antiH_fuel]kg of Anti-Hydrogen "
-
- dat += " \[ [(src.state != STATE_DEFAULT) ? "Main Menu | " : ""]Close \]"
- user << browse(dat, "window=communications;size=400x500")
- onclose(user, "communications")
-
diff --git a/code/modules/power/antimatter/containment_jar.dm b/code/modules/power/antimatter/containment_jar.dm
index 7555b8dedbf..de6596e60f8 100644
--- a/code/modules/power/antimatter/containment_jar.dm
+++ b/code/modules/power/antimatter/containment_jar.dm
@@ -4,25 +4,22 @@
desc_antag = "Antimatter is extremely volatile, and containment jars are not particularly strong. Weak explosions will reduce the container's integrity, and larger ones will cause it to explode immediately."
icon = 'icons/obj/machines/antimatter.dmi'
icon_state = "jar"
- density = 0
- anchored = 0
force = 8
throwforce = 10
throw_speed = 1
throw_range = 2
- var/fuel = 1000 // WAS ORIGINALLY 10000
- var/fuel_max = 1000//Lets try this for now
- var/stability = 100//TODO: add all the stability things to this so its not very safe if you keep hitting in on things
- var/exploded = 0
+ var/fuel = 1000
+ var/stability = 100 //TODO: add all the stability things to this so its not very safe if you keep hitting in on things
+ var/exploded = FALSE
/obj/item/am_containment/proc/boom()
var/percent = 0
if(fuel)
- percent = (fuel / fuel_max) * 100
+ percent = (fuel / initial(fuel)) * 100
if(!exploded && percent >= 10)
explosion(get_turf(src), 1, 2, 3, 5)//Should likely be larger but this works fine for now I guess
- exploded=1
+ exploded = TRUE
if(src)
qdel(src)
@@ -31,16 +28,19 @@
if(1.0)
boom()
if(2.0)
- if(prob((fuel/10)-stability))
+ if(prob((fuel / 10) - stability))
boom()
stability -= 40
if(3.0)
stability -= 20
- //check_stability()
- return
+ check_stability()
+
+/obj/item/am_containment/proc/check_stability()
+ if(stability <= 0)
+ boom()
/obj/item/am_containment/proc/usefuel(var/wanted)
if(fuel < wanted)
wanted = fuel
fuel -= wanted
- return wanted
+ return wanted
\ No newline at end of file
diff --git a/code/modules/power/antimatter/control.dm b/code/modules/power/antimatter/control.dm
index 42105cfd397..bf1221fba3e 100644
--- a/code/modules/power/antimatter/control.dm
+++ b/code/modules/power/antimatter/control.dm
@@ -6,9 +6,8 @@
icon = 'icons/obj/machines/new_ame.dmi'
icon_state = "control"
var/icon_mod = "on" // on, critical, or fuck
- var/old_icon_mod = "on"
- anchored = 0
- density = 1
+ anchored = FALSE
+ density = TRUE
use_power = 1
idle_power_usage = 100
active_power_usage = 1000
@@ -16,28 +15,22 @@
var/list/obj/machinery/am_shielding/linked_shielding
var/list/obj/machinery/am_shielding/linked_cores
var/obj/item/am_containment/fueljar
- var/update_shield_icons = 0
+ var/update_shield_icons = FALSE
var/stability = 100
- var/exploding = 0
- var/exploded = 0
+ var/exploding = FALSE
+ var/exploded = FALSE
- var/active = 0 //On or not
- var/fuel_injection = 2 //How much fuel to inject
- var/shield_icon_delay = 0 //delays resetting for a short time
+ var/active = FALSE //On or not
+ var/fuel_injection = 2 //How much fuel to inject
+ var/shield_icon_delay = FALSE //delays resetting for a short time
var/reported_core_efficiency = 0
var/power_cycle = 0
- var/power_cycle_delay = 4 //How many ticks till produce_power is called
+ var/power_cycle_delay = 4 //How many ticks till produce_power is called
var/stored_core_stability = 0
var/stored_core_stability_delay = 0
- var/stored_power = 0 //Power to deploy per tick
-
-
-/obj/machinery/power/am_control_unit/New()
- ..()
- linked_shielding = list()
- linked_cores = list()
+ var/stored_power = 0 //Power to deploy per tick
/obj/machinery/power/am_control_unit/Destroy()//Perhaps damage and run stability checks rather than just del on the others
for(var/obj/machinery/am_shielding/AMS in linked_shielding)
@@ -47,11 +40,9 @@
AMS.control_unit = null
qdel(AMS)
- qdel(fueljar)
- fueljar = null
+ QDEL_NULL(fueljar)
return ..()
-
/obj/machinery/power/am_control_unit/machinery_process()
if(exploding && !exploded)
message_admins("AME explosion at ([x],[y],[z] - JMP) - Last touched by [fingerprintslast]",0,1)
@@ -82,29 +73,28 @@
power_cycle = 0
/obj/machinery/power/am_control_unit/proc/produce_power()
- playsound(get_turf(src), 'sound/effects/bang.ogg', 25, 1)
- for (var/thing in linked_cores)
+ playsound(get_turf(src), 'sound/effects/air_seal.ogg', 25, TRUE, environment = SEWER_PIPE)
+ for(var/thing in linked_cores)
flick("core2", thing)
- var/core_power = reported_core_efficiency //Effectively how much fuel we can safely deal with
- if(core_power <= 0)
- return 0//Something is wrong
+ if(reported_core_efficiency <= 0)
+ return FALSE //Something is wrong
var/core_damage = 0
var/fuel = fueljar.usefuel(fuel_injection)
- stored_power = (fuel / core_power) * fuel * AM_POWER_FACTOR // Was 200000, was too much. New value run past Aurx. - N3X
- //Now check if the cores could deal with it safely, this is done after so you can overload for more power if needed, still a bad idea
- if(fuel > (2*core_power)) //More fuel has been put in than the current cores can deal with
+ stored_power = fuel * AM_POWER_FACTOR
+ // Now check if the cores could deal with it safely, this is done after so you can overload for more power if needed, still a bad idea
+ if(fuel > (2 * reported_core_efficiency)) //More fuel has been put in than the current cores can deal with
if(prob(50))
core_damage = 1 //Small chance of damage
- if((fuel-core_power) > 5)
+ if((fuel - reported_core_efficiency) > 5)
core_damage = 5 //Now its really starting to overload the cores
- if((fuel-core_power) > 10)
+ if((fuel - reported_core_efficiency) > 10)
core_damage = 20 //Welp now you did it, they wont stand much of this
if(core_damage == 0)
return
for(var/obj/machinery/am_shielding/AMS in linked_cores)
AMS.stability -= core_damage
- AMS.check_stability(1)
+ AMS.check_stability(TRUE)
playsound(get_turf(src), 'sound/effects/bang.ogg', 50, 1)
/obj/machinery/power/am_control_unit/emp_act(severity)
@@ -112,11 +102,12 @@
if(1)
if(active)
toggle_power()
- stability -= rand(15,30)
+ stability -= rand(15, 30)
if(2)
if(active)
toggle_power()
- stability -= rand(10,20)
+ stability -= rand(10, 20)
+ check_stability()
..()
/obj/machinery/power/am_control_unit/ex_act(severity)
@@ -128,150 +119,134 @@
if(3.0)
stability -= 20
check_stability()
- return
/obj/machinery/power/am_control_unit/power_change()
..()
if(stat & NOPOWER && active)
toggle_power()
- return
/obj/machinery/power/am_control_unit/update_icon()
if(active)
icon_state = "control_[icon_mod]"
else
icon_state = "control"
- //No other icons for it atm
/obj/machinery/power/am_control_unit/attackby(obj/item/W, mob/user)
- if(!istype(W) || !user)
- return
if(W.iswrench())
if(!anchored)
playsound(get_turf(src), W.usesound, 75, 1)
- user.visible_message("[user.name] secures the [src.name] to the floor.", \
- "You secure the anchor bolts to the floor.", \
- "You hear a ratchet")
- src.anchored = 1
+ user.visible_message("[user.name] secures \the [src] to the floor.", \
+ SPAN_NOTICE("You secure the anchor bolts to the floor."), \
+ SPAN_NOTICE("You hear a ratcheting noise."))
+ anchored = TRUE
update_shield_icons = 2
check_shield_icons()
connect_to_network()
- else if(!linked_shielding.len > 0)
+ else if(!LAZYLEN(linked_shielding))
playsound(get_turf(src), W.usesound, 75, 1)
- user.visible_message("[user.name] unsecures the [src.name].", \
- "You remove the anchor bolts.", \
- "You hear a ratchet")
- src.anchored = 0
+ user.visible_message("[user] unsecures \the [src].", \
+ SPAN_NOTICE("You remove the anchor bolts."), \
+ SPAN_NOTICE("You hear a ratcheting noise."))
+ anchored = FALSE
disconnect_from_network()
else
- to_chat(user, "Once bolted and linked to a shielding unit it the [src.name] is unable to be moved!")
+ to_chat(user, SPAN_WARNING("Once bolted and linked to a shielding unit it \the [src] is unable to be moved!"))
return
if(istype(W, /obj/item/am_containment))
if(fueljar)
- to_chat(user, "There is already a [fueljar] inside!")
+ to_chat(user, SPAN_WARNING("There is already \a [fueljar] loaded!"))
return
fueljar = W
user.drop_from_inventory(W, src)
message_admins("AME loaded with fuel by [user.real_name] ([user.key]) at ([x],[y],[z] - JMP)",0,1)
- user.visible_message("[user.name] loads an [W.name] into the [src.name].", \
- "You load an [W.name].", \
- "You hear a thunk.")
+ user.visible_message("[user] loads \the [W] into \the [src].", \
+ SPAN_NOTICE("You load \the [W] into \the [src]."), \
+ SPAN_NOTICE("You hear a thunk."))
return
if(W.force >= 20)
- stability -= W.force/2
+ user.do_attack_animation(src, W)
+ stability -= W.force / 2
check_stability()
- ..()
- return
+ return ..()
-
-/obj/machinery/power/am_control_unit/attack_hand(mob/user as mob)
+/obj/machinery/power/am_control_unit/attack_hand(mob/user)
if(anchored)
interact(user)
- return
-
/obj/machinery/power/am_control_unit/proc/add_shielding(var/obj/machinery/am_shielding/AMS, var/AMS_linking = 0)
if(!istype(AMS))
- return 0
+ return FALSE
if(!anchored)
- return 0
+ return FALSE
if(!AMS_linking && !AMS.link_control(src))
- return 0
- if(!(AMS in linked_shielding))
- linked_shielding.Add(AMS)
+ return FALSE
+ LAZYDISTINCTADD(linked_shielding, AMS)
update_shield_icons = 1
- return 1
-
+ return TRUE
/obj/machinery/power/am_control_unit/proc/remove_shielding(var/obj/machinery/am_shielding/AMS)
if(!istype(AMS))
- return 0
- linked_shielding.Remove(AMS)
+ return FALSE
+ LAZYREMOVE(linked_shielding, AMS)
update_shield_icons = 2
if(active)
toggle_power()
- return 1
-
+ return TRUE
/obj/machinery/power/am_control_unit/proc/check_stability()//TODO: make it break when low also might want to add a way to fix it like a part or such that can be replaced
if(stability <= 0)
qdel(src)
- return
-
/obj/machinery/power/am_control_unit/proc/toggle_power()
active = !active
if(active)
use_power = 2
- visible_message("The [src.name] starts up.")
+ visible_message(SPAN_NOTICE("\The [src] starts up."))
else
use_power = 1
- visible_message("The [src.name] shuts down.")
+ visible_message(SPAN_NOTICE("\The [src] shuts down."))
for(var/obj/machinery/am_shielding/AMS in linked_cores)
AMS.update_icon()
update_icon()
- return
-
/obj/machinery/power/am_control_unit/proc/check_shield_icons()//Forces icon_update for all shields
if(shield_icon_delay)
return
- shield_icon_delay = 1
+ shield_icon_delay = TRUE
if(update_shield_icons == 2)//2 means to clear everything and rebuild
for(var/obj/machinery/am_shielding/neighbor in cardinalrange(loc))
if(!neighbor.control_unit)
- linked_shielding += neighbor
+ LAZYADD(linked_shielding, neighbor)
for(var/obj/machinery/am_shielding/AMS in linked_shielding)
if(AMS.processing)
AMS.shutdown_core()
AMS.control_unit = null
- spawn(10)
- AMS.controllerscan()
- AMS.assimilate()
- linked_shielding = list()
-
+ addtimer(CALLBACK(src, .proc/ams_do_scan, AMS), 1 SECOND)
+ LAZYCLEARLIST(linked_shielding)
else
for(var/obj/machinery/am_shielding/AMS in linked_shielding)
AMS.update_icon()
- sleep(20)
- shield_icon_delay = 0
+ addtimer(CALLBACK(src, .proc/clear_shield_icon_delay), 2 SECONDS)
+/obj/machinery/power/am_control_unit/proc/ams_do_scan(var/obj/machinery/am_shielding/AMS)
+ AMS.controllerscan()
+ AMS.assimilate()
+
+/obj/machinery/power/am_control_unit/proc/clear_shield_icon_delay()
+ shield_icon_delay = FALSE
/obj/machinery/power/am_control_unit/proc/check_core_stability()
- //if(stored_core_stability_delay || linked_cores.len <= 0) return
- if(linked_cores.len <= 0)
+ if(!LAZYLEN(linked_cores))
return
- //stored_core_stability_delay = 1
stored_core_stability = 0
for(var/thing in linked_cores)
var/obj/machinery/am_shielding/AMS = thing
- if (QDELETED(AMS))
+ if(QDELETED(AMS))
continue
-
stored_core_stability += AMS.stability
- stored_core_stability /= linked_cores.len
+ stored_core_stability /= LAZYLEN(linked_cores)
switch(stored_core_stability)
if(0 to 24)
icon_mod = "fuck"
@@ -279,17 +254,11 @@
icon_mod = "critical"
if(50 to INFINITY)
icon_mod = "on"
-
- if(icon_mod != old_icon_mod)
- old_icon_mod = icon_mod
- update_icon()
-
- //spawn(40)
- // stored_core_stability_delay = 0
+ update_icon()
/obj/machinery/power/am_control_unit/interact(mob/user)
if((get_dist(src, user) > 1) || (stat & (BROKEN|NOPOWER)))
- if(!istype(user, /mob/living/silicon/ai))
+ if(!issilicon(user))
user.unset_machine()
user << browse(null, "window=AMcontrol")
return
@@ -299,24 +268,23 @@
if(!user)
return
- var/list/fueljar_data=null
+ var/list/fueljar_data = null
if(fueljar)
- fueljar_data=list(
- "fuel"=fueljar.fuel,
- "fuel_max"=fueljar.fuel_max,
- "injecting"=fuel_injection
+ fueljar_data = list(
+ "fuel" = fueljar.fuel,
+ "fuel_max" = initial(fueljar.fuel),
+ "injecting" = fuel_injection
)
var/list/data = list(
"active" = active,
- //"stability" = stability,
- "linked_shields" = linked_shielding.len,
- "linked_cores" = linked_cores.len,
+ "linked_shields" = LAZYLEN(linked_shielding),
+ "linked_cores" = LAZYLEN(linked_cores),
"efficiency" = reported_core_efficiency,
"stability" = stored_core_stability,
"stored_power" = stored_power,
"fueljar" = fueljar_data,
- "siliconUser" = istype(user, /mob/living/silicon)
+ "siliconUser" = issilicon(user)
)
var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, ui_key)
@@ -327,20 +295,19 @@
ui.set_initial_data(data)
ui.open()
// Auto update every Master Controller tick
- ui.set_auto_update(1)
+ ui.set_auto_update(TRUE)
else
// The UI is already open so push the new data to it
ui.push_data(data)
- return
/obj/machinery/power/am_control_unit/Topic(href, href_list)
if(..())
- return 1
+ return TRUE
if(href_list["close"])
if(usr.machine == src)
usr.unset_machine()
- return 1
+ return TRUE
//Ignore input if we are broken or guy is not touching us, AI can control from a ways away
if(stat & (BROKEN|NOPOWER))
usr.unset_machine()
@@ -355,11 +322,11 @@
if(href_list["togglestatus"])
toggle_power()
message_admins("AME toggled [active?"on":"off"] by [usr.real_name] ([usr.key]) at ([x],[y],[z] - JMP)",0,1)
- return 1
+ return TRUE
if(href_list["refreshicons"])
update_shield_icons = 2 // Fuck it
- return 1
+ return TRUE
if(href_list["ejectjar"])
if(fueljar)
@@ -368,21 +335,20 @@
fueljar = null
//fueljar.control_unit = null currently it does not care where it is
//update_icon() when we have the icon for it
- return 1
+ return TRUE
if(href_list["set_strength"])
var/newval = input("Enter new injection strength") as num|null
if(isnull(newval))
return
- fuel_injection=newval
- fuel_injection=max(1,fuel_injection)
+ fuel_injection = newval
+ fuel_injection = max(1, fuel_injection)
message_admins("AME injection strength set to [fuel_injection] by [usr.real_name] ([usr.key]) at ([x],[y],[z] - JMP)",0,1)
- return 1
+ return TRUE
if(href_list["refreshstability"])
check_core_stability()
- return 1
+ return TRUE
updateDialog()
- return 1
-
+ return TRUE
\ No newline at end of file
diff --git a/code/modules/power/antimatter/engine.dm b/code/modules/power/antimatter/engine.dm
deleted file mode 100644
index bf14d1fac72..00000000000
--- a/code/modules/power/antimatter/engine.dm
+++ /dev/null
@@ -1,212 +0,0 @@
-/obj/machinery/power/am_engine
- icon = 'icons/am_engine.dmi'
- density = 1
- anchored = 1.0
- flags = ON_BORDER
-
-/obj/machinery/power/am_engine/bits
- name = "Antimatter Engine"
- icon_state = "1"
-
-/obj/machinery/power/am_engine/engine
- name = "Antimatter Engine"
- icon_state = "am_engine"
- var/engine_id = 0
- var/H_fuel = 0
- var/antiH_fuel = 0
- var/operating = 0
- var/stopping = 0
- var/obj/machinery/power/am_engine/injector/connected = null
-
-/obj/machinery/power/am_engine/injector
- name = "Injector"
- icon_state = "injector"
- var/engine_id = 0
- var/injecting = 0
- var/fuel = 0
- var/obj/machinery/power/am_engine/engine/connected = null
-
-//injector
-
-/obj/machinery/power/am_engine/injector/New()
- ..()
- spawn( 13 )
- var/loc = get_step(src, NORTH)
- src.connected = locate(/obj/machinery/power/am_engine/engine, get_step(loc, NORTH))
- return
- return
-
-
-/obj/machinery/power/am_engine/injector/attackby(obj/item/fuel/F, mob/user)
- if( (stat & BROKEN) || !connected)
- return
-
- if(istype(F, /obj/item/fuel/H))
- if(injecting)
- to_chat(user, "There's already a fuel rod in the injector!")
- return
- to_chat(user, "You insert the rod into the injector.")
- injecting = 1
- var/fuel = F.fuel
- qdel(F)
- F = null
- spawn( 300 )
- injecting = 0
- new/obj/item/fuel(src.loc)
- connected.H_fuel += fuel
-
- if(istype(F, /obj/item/fuel/antiH))
- if(injecting)
- to_chat(user, "There's already a fuel rod in the injector!")
- return
- to_chat(user, "You insert the rod into the injector.")
- injecting = 1
- var/fuel = F.fuel
- qdel(F)
- F = null
- spawn( 300 )
- injecting = 0
- new /obj/item/fuel(src.loc)
- connected.antiH_fuel += fuel
-
- return
-
-
-//engine
-
-
-/obj/machinery/power/am_engine/engine/New()
- ..()
- spawn( 7 )
- var/loc = get_step(src, SOUTH)
- src.connected = locate(/obj/machinery/power/am_engine/injector, get_step(loc, SOUTH))
- return
- return
-
-
-/obj/machinery/power/am_engine/engine/proc/engine_go()
-
-
- if( (!src.connected) || (stat & BROKEN) )
- return
-
- if(!antiH_fuel || !H_fuel)
- return
-
- operating = 1
- var/energy = 0
-
- if(antiH_fuel == H_fuel)
- var/mass = antiH_fuel + H_fuel
- energy = convert2energy(mass)
- H_fuel = 0
- antiH_fuel = 0
- else
- var/residual_matter = modulus(H_fuel - antiH_fuel)
- var/mass = antiH_fuel + H_fuel - residual_matter
- energy = convert2energy(mass)
- if( H_fuel > antiH_fuel )
- H_fuel = residual_matter
- antiH_fuel = 0
- else
- H_fuel = 0
- antiH_fuel = residual_matter
-
- for(var/mob/M in hearers(src, null))
- M.show_message(text("You hear a loud bang!"))
-
- //Q = k x (delta T)
-
- energy = energy*0.75
- operating = 0
-
- //TODO: DEFERRED Heat tile
-
- return
-
-
-/obj/machinery/power/am_engine/engine/proc/engine_process()
-
-
- do
- if( (!src.connected) || (stat & BROKEN) )
- return
-
- if(!antiH_fuel || !H_fuel)
- return
-
- if(operating)
- return
-
- operating = 1
-
- sleep(50)
-
- var/energy //energy from the reaction
- var/H //residual matter if H
- var/antiH //residual matter if antiH
- var/mass //total mass
-
- if(antiH_fuel == H_fuel) //if they're equal then convert the whole mass to energy
- mass = antiH_fuel + H_fuel
- energy = convert2energy(mass)
-
- else //else if they're not equal determine which isn't equal
- //and set it equal to either H or antiH so we don't lose anything
-
- var/residual_matter = modulus(H_fuel - antiH_fuel)
- mass = antiH_fuel + H_fuel - residual_matter
- energy = convert2energy(mass)
-
- if( H_fuel > antiH_fuel )
- H = residual_matter
- else
- antiH = residual_matter
-
-
- if(energy > convert2energy(8e-12)) //TOO MUCH ENERGY
- for(var/mob/M in hearers(src, null))
- M.show_message(text("You hear a loud whirring!"))
- sleep(20)
-
- //Q = k x (delta T)
- //Too much energy so machine panics and dissapates half of it as heat
- //The rest of the energetic photons then form into H and anti H particles again!
-
- H_fuel -= H
- antiH_fuel -= antiH
- antiH_fuel = antiH_fuel/2
- H_fuel = H_fuel/2
-
- energy = convert2energy(H_fuel + antiH_fuel)
-
- H_fuel += H
- antiH_fuel += antiH
-
- if(energy > convert2energy(8e-12)) //FAR TOO MUCH ENERGY STILL
- for(var/mob/M in hearers(src, null))
- M.show_message(text("BANG!"))
- new /obj/effect/bhole(src.loc)
-
- else //this amount of energy is okay so it does the proper output thing
-
- sleep(60)
- //E = Pt
- //Lets say its 86% efficient
- var/output = 0.86*energy/20
- add_avail(output)
- //yeah the machine realises that something isn't right and accounts for it if H or antiH
- H_fuel -= H
- antiH_fuel -= antiH
- antiH_fuel = antiH_fuel/4
- H_fuel = H_fuel/4
- H_fuel += H
- antiH_fuel += antiH
- operating = 0
- sleep(100)
-
- while(!stopping)
-
- stopping = 0
-
- return
diff --git a/code/modules/power/antimatter/fuel.dm b/code/modules/power/antimatter/fuel.dm
deleted file mode 100644
index 8ba81cca42c..00000000000
--- a/code/modules/power/antimatter/fuel.dm
+++ /dev/null
@@ -1,103 +0,0 @@
-/obj/item/fuel
- name = "nagnetic storage ring"
- desc = "A magnetic storage ring."
- icon = 'icons/obj/tools.dmi'
- icon_state = "rcdammo"
- opacity = 0
- density = 0
- anchored = 0.0
- var/fuel = 0
- var/s_time = 1.0
- var/content = null
-
-/obj/item/fuel/H
- name = "hydrogen storage ring"
- content = "Hydrogen"
- fuel = 1e-12 //pico-kilogram
-
-/obj/item/fuel/antiH
- name = "anti-hydrogen storage ring"
- content = "Anti-Hydrogen"
- fuel = 1e-12 //pico-kilogram
-
-/obj/item/fuel/attackby(obj/item/fuel/F, mob/user)
- ..()
- if(istype(src, /obj/item/fuel/antiH))
- if(istype(F, /obj/item/fuel/antiH))
- src.fuel += F.fuel
- F.fuel = 0
- to_chat(user, "You add the anti-Hydrogen to the storage ring. It now contains [src.fuel]kg.")
- if(istype(F, /obj/item/fuel/H))
- src.fuel += F.fuel
- qdel(F)
- F = null
- src:annihilation(src.fuel)
- if(istype(src, /obj/item/fuel/H))
- if(istype(F, /obj/item/fuel/H))
- src.fuel += F.fuel
- F.fuel = 0
- to_chat(user, "You add the Hydrogen to the storage ring. It now contains [src.fuel]kg")
- if(istype(F, /obj/item/fuel/antiH))
- src.fuel += F.fuel
- qdel(src)
- F:annihilation(F.fuel)
-
-/obj/item/fuel/antiH/proc/annihilation(var/mass)
-
-
- var/strength = convert2energy(mass)
-
- if (strength < 773.0)
- var/turf/T = get_turf(src)
-
- if (strength > (450+T0C))
- explosion(T, 0, 1, 2, 4)
- else
- if (strength > (300+T0C))
- explosion(T, 0, 0, 2, 3)
-
- qdel(src)
- return
-
- var/turf/ground_zero = get_turf(loc)
-
- var/ground_zero_range = round(strength / 387)
- explosion(ground_zero, ground_zero_range, ground_zero_range*2, ground_zero_range*3, ground_zero_range*4)
-
- //SN src = null
- qdel(src)
- return
-
-
-/obj/item/fuel/examine()
- ..()
- to_chat(user, "A magnetic storage ring containing [fuel]kg of [content ? content : "nothing"].")
-
-/obj/item/fuel/proc/injest(mob/M as mob)
- switch(content)
- if("Anti-Hydrogen")
- to_chat(mob, SPAN_NOTICE("That was not a very bright idea."))
- M.gib()
- if("Hydrogen")
- to_chat(M, "You feel very light, as if you might just float away...")
- qdel(src)
- return
-
-/obj/item/fuel/attack(mob/M as mob, mob/user as mob)
- if (user != M)
- //If you come from the distant future and happen to find this unincluded and derelict file, you may be wondering what this is. In truth, it's better that you don't know.
- var/obj/effect/equip_e/human/O = new /obj/effect/equip_e/human( )
- O.source = user
- O.target = M
- O.item = src
- O.s_loc = user.loc
- O.t_loc = M.loc
- O.place = "fuel"
- M.requests += O
- spawn( 0 )
- O.process()
- return
- else
- for(var/mob/O in viewers(M, null))
- O.show_message(text("\The [M] eats the [content ? content : "empty canister"]!"), 1)
- src.injest(M)
diff --git a/code/modules/power/antimatter/shielding.dm b/code/modules/power/antimatter/shielding.dm
index a74a1749076..c7c064e4cb8 100644
--- a/code/modules/power/antimatter/shielding.dm
+++ b/code/modules/power/antimatter/shielding.dm
@@ -13,31 +13,29 @@ proc/cardinalrange(var/center)
desc = "A shielding component for an antimatter reactor. Looks delicate."
desc_info = "Antimatter shielding sections must be beside an anchored control unit or another shielding section. If either are destroyed, the section will disappear."
desc_antag = "Antimatter shielding sections are delicate. Attacking the shielding unit with a damaging object will reduce its stability, as will explosions. If the stability hits zero, the reactor may explode."
-
- //icon = 'icons/obj/machines/antimatter.dmi'
icon = 'icons/obj/machines/new_ame.dmi'
icon_state = "shield"
- anchored = 1
- density = 1
- dir = 1
- use_power = 0//Living things generally dont use power
+ anchored = TRUE
+ density = TRUE
+ dir = NORTH
+ use_power = 0
idle_power_usage = 0
active_power_usage = 0
var/obj/machinery/power/am_control_unit/control_unit = null
- var/processing = 0//To track if we are in the update list or not, we need to be when we are damaged and if we ever
- var/stability = 100//If this gets low bad things tend to happen
- var/efficiency = 1//How many cores this core counts for when doing power processing, phoron in the air and stability could affect this
+ var/processing = FALSE //To track if we are in the update list or not, we need to be when we are damaged and if we ever
+ var/stability = 100 //If this gets low bad things tend to happen
+ var/efficiency = 1 //How many cores this core counts for when doing power processing, phoron in the air and stability could affect this
var/coredirs = 0
var/dirs = 0
- var/mapped = 0 //Set to 1 to ignore usual suicide if it doesn't immediately find a control_unit
+ var/mapped = FALSE //Set to 1 to ignore usual suicide if it doesn't immediately find a control_unit
// Stupidly easy way to use it in maps
/obj/machinery/am_shielding/map
- mapped = 1
+ mapped = TRUE
/obj/machinery/am_shielding/Initialize(mapload, var/obj/machinery/power/am_control_unit/AMC)
- . = ..()
+ ..()
if(!AMC)
if (!mapped)
WARNING("AME sector somehow created without a parent control unit!")
@@ -45,39 +43,34 @@ proc/cardinalrange(var/center)
return
link_control(AMC)
remove_machine(src, FALSE)
- if (mapload)
- . = INITIALIZE_HINT_LATELOAD
- else
- addtimer(CALLBACK(src, /atom/.proc/update_icon), 1 SECOND)
+ return INITIALIZE_HINT_LATELOAD
/obj/machinery/am_shielding/LateInitialize()
update_icon()
/obj/machinery/am_shielding/proc/link_control(var/obj/machinery/power/am_control_unit/AMC)
if(!istype(AMC))
- return 0
+ return FALSE
if(control_unit && control_unit != AMC)
- return 0//Already have one
+ return FALSE // already have one
control_unit = AMC
- return control_unit.add_shielding(src,1)
+ return control_unit.add_shielding(src, TRUE)
/obj/machinery/am_shielding/Destroy()
if(control_unit)
control_unit.remove_shielding(src)
if(processing)
shutdown_core()
- visible_message("\The [src.name] melts!")
- //Might want to have it leave a mess on the floor but no sprites for now
+ visible_message(SPAN_WARNING("\The [src] melts!"))
+ // Might want to have it leave a mess on the floor but no sprites for now
return ..()
-/obj/machinery/am_shielding/proc/controllerscan(var/priorscan = 0)
+/obj/machinery/am_shielding/proc/controllerscan(var/priorscan = FALSE)
//Make sure we are the only one here
- if(!istype(src.loc, /turf))
+ if(!isturf(loc))
qdel(src)
return
- for(var/obj/machinery/am_shielding/AMS in loc.contents)
- if(AMS == src)
- continue
+ for(var/obj/machinery/am_shielding/AMS in loc.contents - src)
qdel(src)
return
@@ -93,14 +86,14 @@ proc/cardinalrange(var/center)
if(!mapped) // Prevent rescanning and suicide if it's part of the map
if(!priorscan)
sleep(20)
- controllerscan(1)//Last chance
+ controllerscan(TRUE)//Last chance
return
qdel(src)
// Find surrounding unconnected shielding and add them to our controller
/obj/machinery/am_shielding/proc/assimilate()
if(!control_unit)
- return // nothing to share :'^(
+ return // nothing to share :'^[
for(var/obj/machinery/am_shielding/neighbor in cardinalrange(src))
if(neighbor && !neighbor.control_unit)
neighbor.link_control(control_unit)
@@ -108,9 +101,8 @@ proc/cardinalrange(var/center)
/obj/machinery/am_shielding/Cross(atom/movable/mover, turf/target, height=1.5, air_group = 0)
if(air_group || (height==0))
- return 1
- return 0
-
+ return TRUE
+ return FALSE
/obj/machinery/am_shielding/machinery_process()
if(!processing)
@@ -120,8 +112,7 @@ proc/cardinalrange(var/center)
/obj/machinery/am_shielding/emp_act()//Immune due to not really much in the way of electronics.
- return 0
-
+ return FALSE
/obj/machinery/am_shielding/ex_act(severity)
switch(severity)
@@ -131,86 +122,86 @@ proc/cardinalrange(var/center)
stability -= 40
if(3.0)
stability -= 20
+ check_stability()
/obj/machinery/am_shielding/update_icon()
- overlays.len = 0
+ cut_overlays()
coredirs = 0
dirs = 0
for(var/direction in alldirs)
var/turf/T = get_step(loc, direction)
for(var/obj/machinery/machine in T)
// Detect cores
- if((istype(machine, /obj/machinery/am_shielding) && machine:control_unit == control_unit && machine:processing))
- coredirs |= direction
+ if(istype(machine, /obj/machinery/am_shielding))
+ var/obj/machinery/am_shielding/AMS = machine
+ if(AMS.control_unit == control_unit && AMS.processing)
+ coredirs |= direction
// Detect cores, shielding, and control boxen.
if(direction in cardinal)
- if((istype(machine, /obj/machinery/am_shielding) && machine:control_unit == control_unit) || (istype(machine, /obj/machinery/power/am_control_unit) && machine == control_unit))
- dirs |= direction
+ if(istype(machine, /obj/machinery/am_shielding))
+ var/obj/machinery/am_shielding/AMS = machine
+ if(AMS.control_unit == control_unit)
+ dirs |= direction
+ else if(istype(machine, /obj/machinery/power/am_control_unit))
+ var/obj/machinery/power/am_control_unit/AMCU = machine
+ if(AMCU == control_unit)
+ dirs |= direction
- // If we're next to a core, set the prefix.
- var/prefix = ""
- var/icondirs = dirs
-
- if(coredirs)
- prefix="core"
+ var/is_core = core_check()
// Set our overlay
- icon_state = "[prefix]shield_[icondirs]"
-
- if(core_check())
+ if(!is_core)
+ icon_state = "shield_[dirs]"
+ if(is_core)
icon_state = "core[control_unit && control_unit.active]"
if(!processing)
setup_core()
else if(processing)
shutdown_core()
-
/obj/machinery/am_shielding/attackby(obj/item/W, mob/user)
if(!istype(W) || !user)
return
if(W.force > 10)
- stability -= W.force/2
+ user.do_attack_animation(src, W)
+ stability -= W.force / 2
check_stability()
- ..()
- return
+ return ..()
//Scans cards for shields or the control unit and if all there it
/obj/machinery/am_shielding/proc/core_check()
for(var/direction in alldirs)
- var/found_am_device=0
+ var/found_am_device = FALSE
for(var/obj/machinery/machine in get_step(loc, direction))
if(!machine)
continue
if(istype(machine, /obj/machinery/am_shielding) || istype(machine, /obj/machinery/power/am_control_unit))
- found_am_device=1
+ found_am_device = TRUE
break
if(!found_am_device)
- return 0
- return 1
-
+ return FALSE
+ return TRUE
/obj/machinery/am_shielding/proc/setup_core()
- processing = 1
+ processing = TRUE
if(!control_unit)
return
- control_unit.linked_cores.Add(src)
+ LAZYADD(control_unit.linked_cores, src)
control_unit.reported_core_efficiency += efficiency
-
/obj/machinery/am_shielding/proc/shutdown_core()
processing = 0
if(!control_unit)
return
- control_unit.linked_cores.Remove(src)
+ LAZYREMOVE(control_unit.linked_cores, src)
control_unit.reported_core_efficiency -= efficiency
-
-/obj/machinery/am_shielding/proc/check_stability(var/injecting_fuel = 0)
+/obj/machinery/am_shielding/proc/check_stability(var/injecting_fuel = FALSE)
if(stability > 0)
return
if(injecting_fuel && control_unit)
- control_unit.exploding = 1
+ control_unit.exploding = TRUE
qdel(src)
/obj/machinery/am_shielding/proc/recalc_efficiency(var/new_efficiency)//tbh still not 100% sure how I want to deal with efficiency so this is likely temp
@@ -221,8 +212,6 @@ proc/cardinalrange(var/center)
control_unit.reported_core_efficiency += (new_efficiency - efficiency)
efficiency = new_efficiency
-
-
/obj/item/device/am_shielding_container
name = "packaged antimatter reactor section"
desc = "A section of antimatter reactor shielding. Do not eat."
@@ -230,31 +219,30 @@ proc/cardinalrange(var/center)
icon = 'icons/obj/machines/antimatter.dmi'
icon_state = "box"
item_state = "electronic"
- siemens_coefficient = 1
throwforce = 5
throw_speed = 1
throw_range = 2
/obj/item/device/am_shielding_container/attackby(var/obj/item/I, var/mob/user)
if(I.ismultitool() && isturf(loc))
- if(locate(/obj/machinery/am_shielding/) in loc)
- to_chat(user, "[icon2html(src, user)]There is already an antimatter reactor section there.")
+ if(locate(/obj/machinery/am_shielding) in loc)
+ to_chat(user, SPAN_WARNING("There is already an antimatter reactor section there."))
return
//Search for shielding first
for(var/obj/machinery/am_shielding/AMS in cardinalrange(src))
if(AMS.control_unit)
- new/obj/machinery/am_shielding(src.loc, AMS.control_unit)
+ new /obj/machinery/am_shielding(loc, AMS.control_unit)
qdel(src)
return
//No other guys nearby, look for a control unit
var/obj/machinery/power/am_control_unit/AMC = locate() in cardinalrange(src)
- if(AMC && AMC.anchored)
- new/obj/machinery/am_shielding(src.loc, AMC)
+ if(AMC?.anchored)
+ new /obj/machinery/am_shielding(loc, AMC)
qdel(src)
else //Stranded & Alone
- to_chat(user, "[icon2html(src, user)]Couldn't connect to an Antimatter Control Unit.")
+ to_chat(user, SPAN_WARNING("\The [src] couldn't connect to an Antimatter Control Unit."))
return
- ..()
+ ..()
\ No newline at end of file
diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm
index 41e2637dbac..d1e89c39cda 100644
--- a/code/modules/power/apc.dm
+++ b/code/modules/power/apc.dm
@@ -834,20 +834,16 @@
if(issilicon(hacker))
to_chat(hacker, SPAN_NOTICE("Corrupt files transfered to [H]. They are now under your control until they are repaired."))
else if(cell && cell.charge > 0)
- if(H.max_nutrition > 0 && H.nutrition < H.max_nutrition)
- if(cell.charge >= H.max_nutrition)
- H.adjustNutritionLoss(-50)
- cell.charge -= 500
- else
- H.adjustNutritionLoss( -cell.charge / 10)
- cell.charge = 0
-
+ var/obj/item/organ/internal/cell/C = H.internal_organs_by_name[BP_CELL]
+ var/obj/item/cell/HC
+ if(C)
+ HC = C.cell
+ if(HC && HC.percent() < 95)
+ var/used = cell.use(500)
+ HC.give(used)
to_chat(user, SPAN_NOTICE("You slot your fingers into the APC interface and siphon off some of the stored charge for your own use."))
-
if (cell.charge < 0)
cell.charge = 0
- if (H.nutrition > H.max_nutrition)
- H.nutrition = H.max_nutrition
if (prob(0.5))
spark(src, 5, alldirs)
to_chat(H, SPAN_DANGER("The APC power currents surge eratically, damaging your chassis!"))
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index ddb416ed2b8..7ffa56d8eb5 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -169,6 +169,16 @@
safety_overlay = image(gun_gui_icons,"[safety()]")
add_overlay(safety_overlay, TRUE)
+ if(is_wieldable)
+ if(wielded)
+ var/list/gun_states = icon_states(icon)
+ if(("[item_state]-wielded_lh" in gun_states) || ("[item_state]-wielded_rh" in gun_states))
+ item_state = "[item_state]-wielded"
+ else
+ item_state = replacetext(item_state,"-wielded","")
+
+ update_held_icon()
+
//Checks whether a given mob can use the gun
//Any checks that shouldn't result in handle_click_empty() being called if they fail should go here.
//Otherwise, if you want handle_click_empty() to be called, check in consume_next_projectile() and return null there.
@@ -246,11 +256,6 @@
action_button_name = ""
verbs -= /obj/item/gun/verb/wield_gun
-
-/obj/item/gun/emp_act(severity)
- for(var/obj/O in contents)
- O.emp_act(severity)
-
/obj/item/gun/afterattack(atom/A, mob/living/user, adjacent, params)
if(adjacent) return //A is adjacent, is the user, or is on the user's person
diff --git a/code/modules/projectiles/guns/energy.dm b/code/modules/projectiles/guns/energy.dm
index f476c2f3486..4b3824e34f0 100644
--- a/code/modules/projectiles/guns/energy.dm
+++ b/code/modules/projectiles/guns/energy.dm
@@ -43,10 +43,29 @@
if(.)
update_icon()
-/obj/item/gun/energy/emp_act(severity)
+/obj/item/gun/energy/emp_act(var/severity)
..()
+ disable_cell_temp(severity)
queue_icon_update()
+/obj/item/gun/energy/proc/disable_cell_temp(var/severity)
+ set waitfor = FALSE
+ if(!power_supply)
+ return
+ var/mob/M
+ if(ismob(loc))
+ M = loc
+ to_chat(M, SPAN_DANGER("[src] locks up!"))
+ playsound(M, 'sound/weapons/smg_empty_alarm.ogg', 30)
+ var/initial_charge = power_supply.charge
+ power_supply.charge = 0
+ sleep(severity * 20)
+ power_supply.give(initial_charge)
+ update_maptext()
+ update_icon()
+ if(M && loc == M)
+ playsound(M, 'sound/weapons/laser_safetyoff.ogg', 30)
+
/obj/item/gun/energy/get_cell()
return power_supply
@@ -118,7 +137,6 @@
return
/obj/item/gun/energy/update_icon()
- ..()
if(charge_meter && power_supply && power_supply.maxcharge)
var/ratio = power_supply.charge / power_supply.maxcharge
var/icon_state_ratio = ""
@@ -141,7 +159,8 @@
else
icon_state = "[initial(icon_state)][icon_state_ratio]"
item_state = "[initial(item_state)][item_state_ratio]"
- update_held_icon()
+
+ ..()
/obj/item/gun/energy/handle_post_fire()
..()
diff --git a/code/modules/projectiles/guns/energy/blaster.dm b/code/modules/projectiles/guns/energy/blaster.dm
index 6aae284bd71..47131da5545 100644
--- a/code/modules/projectiles/guns/energy/blaster.dm
+++ b/code/modules/projectiles/guns/energy/blaster.dm
@@ -103,14 +103,6 @@
is_wieldable = TRUE
-/obj/item/gun/energy/blaster/rifle/update_icon()
- ..()
- if(wielded)
- item_state = "blaster_rifle-wielded"
- else
- item_state = initial(item_state)
- update_held_icon()
-
/obj/item/gun/energy/blaster/rifle/verb/scope()
set category = "Object"
set name = "Use Scope"
diff --git a/code/modules/projectiles/guns/energy/crank.dm b/code/modules/projectiles/guns/energy/crank.dm
index 0a2e65493f4..8610f5d000e 100644
--- a/code/modules/projectiles/guns/energy/crank.dm
+++ b/code/modules/projectiles/guns/energy/crank.dm
@@ -4,6 +4,7 @@
icon = 'icons/obj/guns/icelance.dmi'
icon_state = "icelance"
item_state = "icelance"
+ has_item_ratio = FALSE
fire_sound = 'sound/weapons/laser1.ogg'
max_shots = 3
accuracy = -1
@@ -51,12 +52,4 @@
is_charging = FALSE
/obj/item/gun/energy/rifle/icelance/get_cell()
- return DEVICE_NO_CELL
-
-/obj/item/gun/energy/rifle/icelance/update_icon()
- ..()
- if(wielded)
- item_state = "icelance-wielded"
- else
- item_state = initial(item_state)
- update_held_icon()
+ return DEVICE_NO_CELL
\ No newline at end of file
diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm
index 6930cb1b2ff..c990ee22712 100644
--- a/code/modules/projectiles/guns/energy/laser.dm
+++ b/code/modules/projectiles/guns/energy/laser.dm
@@ -202,14 +202,6 @@ obj/item/gun/energy/retro
turret_is_lethal = TRUE
turret_sprite_set = "laser"
-/obj/item/gun/energy/laser/shotgun/update_icon()
- ..()
- if(wielded)
- item_state = "[initial(icon_state)]-wielded"
- else
- item_state = initial(item_state)
- update_held_icon()
-
/obj/item/gun/energy/laser/shotgun/research
name = "expedition shotgun"
desc = "A Nanotrasen designed laser weapon, designed to split a single amplified beam four times. This one is marked for expeditionary use."
diff --git a/code/modules/projectiles/guns/energy/modular.dm b/code/modules/projectiles/guns/energy/modular.dm
index f9d0e301cad..2de908788f6 100644
--- a/code/modules/projectiles/guns/energy/modular.dm
+++ b/code/modules/projectiles/guns/energy/modular.dm
@@ -52,17 +52,11 @@
/obj/item/gun/energy/laser/prototype/update_icon()
..()
- if(origin_chassis == CHASSIS_LARGE)
- if(wielded)
- item_state = "large_3_wielded"
- else
- item_state = "large_3"
underlays.Cut()
if(length(gun_mods))
for(var/obj/item/laser_components/mod in gun_mods)
if(mod.gun_overlay)
underlays += mod.gun_overlay
- update_held_icon()
underlays.Cut()
for(var/v in gun_mods)
var/obj/item/laser_components/modifier/mod = v
diff --git a/code/modules/projectiles/guns/energy/rifle.dm b/code/modules/projectiles/guns/energy/rifle.dm
index 2cfebfd0f76..835f6a0c750 100644
--- a/code/modules/projectiles/guns/energy/rifle.dm
+++ b/code/modules/projectiles/guns/energy/rifle.dm
@@ -3,8 +3,8 @@
desc = "A Nanotrasen designed energy-based rifle with two settings: Stun and Kill."
desc_fluff = "The NT ER-2 is an energy rifle developed and produced by Nanotrasen. Widely produced and sold across the galaxy. Designed to both stun and kill with concentrated energy blasts of varying strengths based on the fire mode, focused through a crystal lens. Considered to be a dual-purpose rifle with prolonged combat capability."
icon = 'icons/obj/guns/erifle.dmi'
- icon_state = "eriflestun100"
- icon_state = "eriflestun100"
+ icon_state = "eriflestun"
+ item_state = "eriflestun"
fire_sound = 'sound/weapons/Taser.ogg'
slot_flags = SLOT_BACK
w_class = ITEMSIZE_LARGE
@@ -35,7 +35,6 @@
list(mode_name="lethal", projectile_type=/obj/item/projectile/beam, modifystate="eriflekill", fire_sound='sound/weapons/laser1.ogg')
)
-
/obj/item/gun/energy/rifle/laser
name = "laser rifle"
desc = "A Nanotrasen designed laser weapon, designed to kill with concentrated energy blasts."
@@ -59,14 +58,6 @@
firemodes = list()
modifystate = null
-/obj/item/gun/energy/rifle/laser/update_icon()
- ..()
- if(wielded)
- item_state = "[initial(icon_state)]-wielded"
- else
- item_state = initial(item_state)
- update_held_icon()
-
/obj/item/gun/energy/rifle/laser/heavy
name = "laser cannon"
desc = "A nanotrasen designed laser cannon capable of acting as a powerful support weapon."
diff --git a/code/modules/projectiles/guns/energy/special.dm b/code/modules/projectiles/guns/energy/special.dm
index 0b0fda37d74..6bcbf4d2a45 100644
--- a/code/modules/projectiles/guns/energy/special.dm
+++ b/code/modules/projectiles/guns/energy/special.dm
@@ -1,6 +1,6 @@
/obj/item/gun/energy/rifle/ionrifle
name = "ion rifle"
- desc = "The NT Mk70 EW Halicon is a man portable anti-armor weapon designed to disable mechanical threats, produced by NT. Has two settings: Stun and Lethal."
+ desc = "The NT Mk70 EW Halicon is a man portable anti-armor weapon designed to disable mechanical threats, produced by Nanotrasen."
icon = 'icons/obj/guns/ionrifle.dmi'
icon_state = "ionriflestun100"
item_state = "ionriflestun100" // so the human update icon uses the icon_state instead.
@@ -14,17 +14,10 @@
flags = CONDUCT
slot_flags = SLOT_BACK
charge_cost = 300
- max_shots = 10
- secondary_projectile_type = /obj/item/projectile/ion
- secondary_fire_sound = 'sound/weapons/laser1.ogg'
+ max_shots = 4
can_turret = 1
- can_switch_modes = 1
turret_sprite_set = "ion"
-
- firemodes = list(
- list(mode_name="stun", projectile_type=/obj/item/projectile/ion/stun, modifystate="ionriflestun", fire_sound='sound/weapons/laser1.ogg', charge_cost = 300),
- list(mode_name="lethal", projectile_type=/obj/item/projectile/ion, modifystate="ionriflekill", fire_sound='sound/weapons/laser1.ogg', charge_cost = 450)
- )
+ firemodes = list()
/obj/item/gun/energy/rifle/ionrifle/emp_act(severity)
..(max(severity, 2)) //so it doesn't EMP itself, I guess
@@ -39,12 +32,8 @@
else
ratio = max(round(ratio, 0.25) * 100, 25)
- if(modifystate)
- icon_state = "[modifystate][ratio]"
- item_state = "[modifystate][ratio]"
- else
- icon_state = "[initial(icon_state)][ratio]"
- item_state = "[initial(icon_state)][ratio]"
+ icon_state = "[modifystate][ratio]"
+ item_state = "[modifystate][ratio]"
update_held_icon()
/obj/item/gun/energy/rifle/ionrifle/mounted
@@ -199,13 +188,19 @@
/obj/item/gun/energy/mousegun/emag_act(var/remaining_charges, var/mob/user)
if(!emagged)
to_chat(user, SPAN_WARNING("You overload \the [src]'s shock modulator."))
- max_shots = 10
+ max_shots = initial(max_shots) + 4
projectile_type = /obj/item/projectile/beam/mousegun/emag
emagged = TRUE
QDEL_NULL(power_supply)
power_supply = new /obj/item/cell/device/variable(src, max_shots * charge_cost)
return TRUE
+/obj/item/gun/energy/mousegun/xenofauna
+ name = "xenofauna gun"
+ desc = "The NT \"Xenovermino\" Zap-Blast is a highly sophisticated and probably safe beamgun designed to deal with hostile xenofauna."
+ projectile_type = /obj/item/projectile/beam/mousegun/xenofauna
+ max_shots = 12
+
/obj/item/gun/energy/net
name = "net gun"
desc = "A gun designed to deploy energy nets to capture animals or unruly crew members."
diff --git a/code/modules/projectiles/guns/launcher/harpoon.dm b/code/modules/projectiles/guns/launcher/harpoon.dm
index e4a281f5b01..0df2a246b8c 100644
--- a/code/modules/projectiles/guns/launcher/harpoon.dm
+++ b/code/modules/projectiles/guns/launcher/harpoon.dm
@@ -32,11 +32,8 @@
/obj/item/gun/launcher/harpoon/update_icon()
icon_state = "[initial(icon_state)]-[harpoons.len]"
- if(wielded)
- item_state = "[initial(item_state)]-[harpoons.len]-w"
- else
- item_state = "[initial(icon_state)]-[harpoons.len]"
- update_held_icon()
+ item_state = "[initial(item_state)]-[harpoons.len]"
+ ..()
/obj/item/gun/launcher/harpoon/special_check(mob/user)
if(!wielded)
diff --git a/code/modules/projectiles/guns/projectile/automatic.dm b/code/modules/projectiles/guns/projectile/automatic.dm
index 3c6f8eec6c4..820229bbaf1 100644
--- a/code/modules/projectiles/guns/projectile/automatic.dm
+++ b/code/modules/projectiles/guns/projectile/automatic.dm
@@ -143,11 +143,6 @@
/obj/item/gun/projectile/automatic/rifle/sts35/update_icon()
..()
icon_state = (ammo_magazine)? "arifle" : "arifle-empty"
- if(wielded)
- item_state = (ammo_magazine)? "arifle-wielded" : "arifle-wielded-empty"
- else
- item_state = (ammo_magazine)? "arifle" : "arifle-empty"
- update_held_icon()
/obj/item/gun/projectile/automatic/rifle/sol
name = "battle rifle"
@@ -167,12 +162,6 @@
icon_state = "battlerifle"
else
icon_state = "battlerifle-empty"
- if(wielded)
- item_state = "battlerifle-wielded"
- else
- item_state = "battlerifle"
- update_held_icon()
- return
/datum/firemode/z8
var/use_launcher = 0
@@ -240,12 +229,6 @@
icon_state = "carbine"
else
icon_state = "carbine-empty"
- if(wielded)
- item_state = "carbine-wielded"
- else
- item_state = "carbine"
- update_held_icon()
- return
/obj/item/gun/projectile/automatic/rifle/z8/examine(mob/user)
..()
@@ -310,10 +293,7 @@
/obj/item/gun/projectile/automatic/rifle/l6_saw/update_icon()
icon_state = "l6[cover_open ? "open" : "closed"][ammo_magazine ? round(ammo_magazine.stored_ammo.len*2, 25) : "-empty"]"
- if(wielded)
- item_state = "l6closedmag-wielded"
- else
- item_state = initial(item_state)
+ ..()
/obj/item/gun/projectile/automatic/rifle/l6_saw/load_ammo(var/obj/item/A, mob/user)
if(!cover_open)
@@ -355,14 +335,6 @@
knife_x_offset = 23
knife_y_offset = 14
-/obj/item/gun/projectile/automatic/rifle/adhomian/update_icon()
- ..()
- if(wielded)
- item_state = "tsarrayut-wielded"
- else
- item_state = "tsarrayut"
- update_held_icon()
-
/obj/item/gun/projectile/automatic/tommygun
name = "submachine gun"
desc = "An adhomian made submachine gun. Uses .45 rounds."
@@ -485,4 +457,4 @@
/obj/item/gun/projectile/automatic/rifle/shotgun/update_icon()
..()
- icon_state = (ammo_magazine)? "assaultshotgun" : "assaultshotgun-empty"
+ icon_state = (ammo_magazine)? "assaultshotgun" : "assaultshotgun-empty"
\ No newline at end of file
diff --git a/code/modules/projectiles/guns/projectile/cannon.dm b/code/modules/projectiles/guns/projectile/cannon.dm
index f3d528de411..0cebef4f98b 100644
--- a/code/modules/projectiles/guns/projectile/cannon.dm
+++ b/code/modules/projectiles/guns/projectile/cannon.dm
@@ -22,14 +22,6 @@
found in museums and forgotten warehouses were adapted into portable weapons, combining modern and ancient tajaran technology. This weapon is usually found in the hands of the \
sailors and marines of the Royal Navy."
-/obj/item/gun/projectile/cannon/update_icon()
- if(wielded)
- item_state = "cannon-wielded"
- else
- item_state = "cannon"
- update_held_icon()
-
-
/obj/item/gun/projectile/cannon/special_check(mob/user)
if(!wielded)
to_chat(user, "You can't fire without stabilizing \the [src]!")
@@ -62,18 +54,12 @@
causing the annihilation of the military base of Quizosa. The nuclear launcher was created by republican scientists as way to deploy this destructive force while on the field."
/obj/item/gun/projectile/nuke/update_icon()
+ ..()
if(ammo_magazine)
icon_state = "blockbuster-[(ammo_magazine.stored_ammo.len)]"
else
icon_state = "blockbuster-0"
- if(wielded)
- item_state = "blockbuster-wielded"
- else
- item_state = "blockbuster"
-
- update_held_icon()
-
/obj/item/gun/projectile/nuke/special_check(mob/user)
if(!wielded)
to_chat(user, "You can't fire without stabilizing \the [src]!")
diff --git a/code/modules/projectiles/guns/projectile/musket.dm b/code/modules/projectiles/guns/projectile/musket.dm
index b59377a1bb2..1342c72ce46 100644
--- a/code/modules/projectiles/guns/projectile/musket.dm
+++ b/code/modules/projectiles/guns/projectile/musket.dm
@@ -35,13 +35,6 @@
var/has_powder = FALSE
-/obj/item/gun/projectile/musket/update_icon()
- if(wielded)
- item_state = "musket-wielded"
- else
- item_state = "musket"
- update_held_icon()
-
/obj/item/gun/projectile/musket/special_check(mob/user)
if(!has_powder)
to_chat(user, SPAN_WARNING("\The [src] is not loaded with gunpowder!"))
@@ -82,4 +75,4 @@
amount_per_transfer_from_this = 5
possible_transfer_amounts = list(5)
volume = 30
- reagents_to_add = list(/datum/reagent/gunpowder = 30)
+ reagents_to_add = list(/datum/reagent/gunpowder = 30)
\ No newline at end of file
diff --git a/code/modules/projectiles/guns/projectile/plasma.dm b/code/modules/projectiles/guns/projectile/plasma.dm
index 66c0bd417e6..824f8650fa2 100644
--- a/code/modules/projectiles/guns/projectile/plasma.dm
+++ b/code/modules/projectiles/guns/projectile/plasma.dm
@@ -22,7 +22,6 @@
else
icon_state = "[initial(icon_state)]-empty"
-
/obj/item/gun/projectile/plasma/bolter
name = "plasma bolter"
desc = "A miniaturized, less efficient version of the infamous plasma slammer. Sacrifices much of its power for a more compact frame."
@@ -47,4 +46,3 @@
use_external_power = TRUE
self_recharge = TRUE
projectile_type = /obj/item/projectile/plasma
-
diff --git a/code/modules/projectiles/guns/projectile/rifle.dm b/code/modules/projectiles/guns/projectile/rifle.dm
index 69a397143f2..42b1dbf79cf 100644
--- a/code/modules/projectiles/guns/projectile/rifle.dm
+++ b/code/modules/projectiles/guns/projectile/rifle.dm
@@ -13,7 +13,6 @@
rack_sound = 'sound/weapons/riflebolt.ogg'
rack_verb = "pull back the bolt on"
- has_wield_state = TRUE
can_bayonet = TRUE
knife_x_offset = 23
@@ -31,7 +30,6 @@
slot_flags &= ~SLOT_BACK
slot_flags |= (SLOT_BELT|SLOT_HOLSTER)
can_bayonet = FALSE
- has_wield_state = FALSE
if(bayonet)
qdel(bayonet)
bayonet = null
@@ -51,7 +49,6 @@
accuracy = -2
slot_flags = SLOT_BELT|SLOT_HOLSTER
can_bayonet = FALSE
- has_wield_state = FALSE
/obj/item/gun/projectile/contender
name = "pocket rifle"
@@ -254,14 +251,6 @@
..()
icon_state = (ammo_magazine)? "gauss_thumper" : "gauss_thumper-e"
- if(wielded)
- item_state = "gauss_thumper-wielded"
- else
- item_state = "gauss_thumper"
-
- update_held_icon()
- return
-
/obj/item/gun/energy/gauss/mounted/mech
name = "heavy gauss cannon"
desc = "An outdated and power hungry gauss cannon, modified to deliver high explosive rounds at high velocities."
diff --git a/code/modules/projectiles/guns/projectile/shotgun.dm b/code/modules/projectiles/guns/projectile/shotgun.dm
index 1da5c2c571f..e02a26a9ca9 100644
--- a/code/modules/projectiles/guns/projectile/shotgun.dm
+++ b/code/modules/projectiles/guns/projectile/shotgun.dm
@@ -56,7 +56,6 @@
fire_sound = 'sound/weapons/gunshot/gunshot_shotgun2.ogg'
is_wieldable = TRUE
var/recentpump = 0 // to prevent spammage
- var/has_wield_state = TRUE
var/rack_sound = 'sound/weapons/shotgun_pump.ogg'
var/rack_verb = "pump"
@@ -91,14 +90,6 @@
update_maptext()
update_icon()
-/obj/item/gun/projectile/shotgun/pump/update_icon()
- ..()
- if(wielded && has_wield_state)
- item_state = "[icon_state]-wielded"
- else
- item_state = "[icon_state]"
- update_held_icon()
-
/obj/item/gun/projectile/shotgun/pump/combat
name = "combat shotgun"
desc = "Built for close quarters combat, the Hephaestus Industries KS-40 is widely regarded as a weapon of choice for repelling boarders."
@@ -160,14 +151,6 @@
if(new_mode)
to_chat(user, SPAN_NOTICE("\The [src] is now set to [new_mode.name]."))
-/obj/item/gun/projectile/shotgun/doublebarrel/update_icon()
- ..()
- if(wielded && has_wield_state)
- item_state = "[icon_state]-wielded"
- else
- item_state = "[icon_state]"
- update_held_icon()
-
/obj/item/gun/projectile/shotgun/doublebarrel/pellet
ammo_type = /obj/item/ammo_casing/shotgun/pellet
@@ -249,4 +232,4 @@
if(folded)
toggle_folded(user)
return FALSE
- return ..()
+ return ..()
\ No newline at end of file
diff --git a/code/modules/projectiles/guns/projectile/sniper.dm b/code/modules/projectiles/guns/projectile/sniper.dm
index 607ae04b515..b3cceb25542 100644
--- a/code/modules/projectiles/guns/projectile/sniper.dm
+++ b/code/modules/projectiles/guns/projectile/sniper.dm
@@ -32,15 +32,11 @@
accuracy_wielded = -1
/obj/item/gun/projectile/heavysniper/update_icon()
+ ..()
if(bolt_open)
icon_state = "heavysniper-open"
else
icon_state = "heavysniper"
- if(wielded)
- item_state = "heavysniper-wielded"
- else
- item_state = "heavysniper"
- update_held_icon()
/obj/item/gun/projectile/heavysniper/attack_self(mob/user as mob)
bolt_open = !bolt_open
@@ -108,17 +104,13 @@
has_scope = FALSE
/obj/item/gun/projectile/heavysniper/unathi/update_icon()
+ ..()
if(bolt_open && length(loaded))
icon_state = "slugger-open-loaded"
else if(bolt_open && !length(loaded))
icon_state = "slugger-open"
else
icon_state = "slugger"
- if(wielded)
- item_state = "slugger-wielded"
- else
- item_state = "slugger"
- update_held_icon()
/obj/item/gun/projectile/heavysniper/unathi/handle_post_fire(mob/user)
..()
@@ -169,15 +161,11 @@
accuracy_wielded = 2
/obj/item/gun/projectile/heavysniper/tranq/update_icon()
+ ..()
if(bolt_open)
icon_state = "tranqsniper-open"
else
icon_state = "tranqsniper"
- if(wielded)
- item_state = "tranqsniper-wielded"
- else
- item_state = "tranqsniper"
- update_held_icon()
/obj/item/gun/projectile/dragunov
name = "marksman rifle"
@@ -211,19 +199,12 @@
accuracy_wielded = 1
/obj/item/gun/projectile/dragunov/update_icon()
-
+ ..()
if(ammo_magazine)
icon_state = "dragunov"
else
icon_state = "dragunov-empty"
- if(wielded)
- item_state = "dragunov-wielded"
- else
- item_state = "dragunov"
-
- update_held_icon()
-
/obj/item/gun/projectile/dragunov/special_check(mob/user)
if(!wielded)
to_chat(user, "You can't fire without stabilizing the rifle!")
@@ -278,10 +259,3 @@
toggle_scope(2.0, usr)
else
to_chat(usr, "You can't look through the scope without stabilizing the rifle!")
-
-/obj/item/gun/projectile/automatic/rifle/w556/update_icon()
- if(wielded)
- item_state = "w556rifle-wielded"
- else
- item_state = "w556rifle"
- update_held_icon()
diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm
index 780fd7b0696..d4fbc56be90 100644
--- a/code/modules/projectiles/projectile/beams.dm
+++ b/code/modules/projectiles/projectile/beams.dm
@@ -280,6 +280,16 @@
to_chat(M, 'sound/effects/basscannon.ogg')
return TRUE
+/obj/item/projectile/beam/mousegun/xenofauna
+ nodamage = FALSE
+ damage = 10
+
+/obj/item/projectile/beam/mousegun/xenofauna/mousepulse(atom/target, range, log)
+ if(is_type_in_list(target, list(/mob/living/simple_animal/hostile/retaliate/cavern_dweller, /mob/living/simple_animal/hostile/carp, /mob/living/simple_animal/carp, /mob/living/simple_animal/hostile/giant_spider)))
+ var/mob/living/simple_animal/SA = target
+ SA.take_organ_damage(0, 20)
+ return TRUE
+
/obj/item/projectile/beam/shotgun
name = "diffuse laser"
icon_state = "laser"
diff --git a/code/modules/projectiles/projectile/special.dm b/code/modules/projectiles/projectile/special.dm
index 338054ddeba..48fa4b7f632 100644
--- a/code/modules/projectiles/projectile/special.dm
+++ b/code/modules/projectiles/projectile/special.dm
@@ -3,14 +3,12 @@
icon_state = "ion"
damage = 0
damage_type = BURN
- nodamage = 1
+ impact_sounds = list(BULLET_IMPACT_MEAT = SOUNDS_ION_ANY, BULLET_IMPACT_METAL = SOUNDS_ION_ANY)
check_armor = "energy"
var/pulse_range = 1
/obj/item/projectile/ion/on_impact(var/atom/A)
empulse(A, pulse_range, pulse_range)
- return 1
-
/obj/item/projectile/ion/stun/on_impact(var/atom/A)
if(isipc(A))
@@ -26,12 +24,10 @@
s.broken = 1
s.icon_state = "surge_ipc_broken"
to_chat(H, "Warning: EMP detected, integrated surge prevention module activated. The surge prevention module is fried, replacement recommended.")
- return 1
+ return
else
to_chat(src, "Warning: EMP detected, integrated surge prevention module is fried and unable to protect from EMP. Replacement recommended.")
- H.Weaken(5)
- to_chat(H, "ERROR: detected low setting EMP, acutators experience temporary power loss. Attempting to restore power.")
- else if (isrobot(A))
+ if (isrobot(A))
var/mob/living/silicon/robot/R = A
var/datum/robot_component/surge/C = R.components["surge"]
if(C && C.installed)
@@ -50,8 +46,8 @@
R.emp_act(2) // Borgs emp_act is 1-2
else
- A.emp_act(3) // Deals less EMP damage then lethal setting, and not areal pulse
- return 1
+ A.emp_act(3)
+ return
/obj/item/projectile/ion/small
name = "ion pulse"
diff --git a/code/modules/psionics/faculties/coercion.dm b/code/modules/psionics/faculties/coercion.dm
index 67f46da631e..92f3d5483d7 100644
--- a/code/modules/psionics/faculties/coercion.dm
+++ b/code/modules/psionics/faculties/coercion.dm
@@ -296,15 +296,15 @@
if(!do_after(user, 3 SECONDS))
return
var/list/dirs = list()
+ var/turf/our_turf = get_turf(user)
for(var/mob/living/L in range(20))
var/turf/T = get_turf(L)
if(!T || L == user || L.stat == DEAD || L.invisibility == INVISIBILITY_LEVEL_TWO)
continue
if(L.is_psi_blocked())
continue
- var/image/ping_image = image(icon = 'icons/effects/effects.dmi', icon_state = "sonar_ping", loc = T)
- ping_image.plane = LIGHTING_LAYER+1
- ping_image.layer = LIGHTING_LAYER+1
+ var/image/ping_image = image(icon = 'icons/effects/effects.dmi', icon_state = "sonar_ping", loc = our_turf, layer = OBFUSCATION_LAYER + 0.1)
+ pixel_shift_to_turf(ping_image, our_turf, T)
user << ping_image
addtimer(CALLBACK(GLOBAL_PROC, /proc/qdel, ping_image), 8)
var/direction = num2text(get_dir(user, L))
diff --git a/code/modules/random_map/automata/diona.dm b/code/modules/random_map/automata/diona.dm
index 478f31cc057..aa180b559d7 100644
--- a/code/modules/random_map/automata/diona.dm
+++ b/code/modules/random_map/automata/diona.dm
@@ -35,7 +35,7 @@
user.do_attack_animation(src)
if(W.force)
user.visible_message(SPAN_DANGER("\The [user] [pick(W.attack_verb)] \the [src] with \the [W]!"), SPAN_NOTICE("You [pick(W.attack_verb)] \the [src] with \the [W]!"))
- playsound(loc, W.hitsound, 60, TRUE)
+ playsound(loc, W.hitsound, W.get_clamped_volume(), TRUE)
playsound(loc, /decl/sound_category/wood_break_sound, 50, TRUE)
health -= W.force
if(health <= 0)
diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Core.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Core.dm
index f81bff4e486..5e25284a09f 100644
--- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Core.dm
+++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Core.dm
@@ -48,11 +48,12 @@
/datum/reagent/blood/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed)
if(ishuman(M))
- if (M.mind && M.mind.vampire)
+ if(M.mind)
+ var/datum/vampire/vampire = M.mind.antag_datums[MODE_VAMPIRE]
if(M.dna.unique_enzymes == data["blood_DNA"]) //so vampires can't drink their own blood
return
- M.mind.vampire.blood_usable += removed
- to_chat(M, "You have accumulated [M.mind.vampire.blood_usable] [M.mind.vampire.blood_usable > 1 ? "units" : "unit"] of usable blood. It tastes quite stale.")
+ vampire.blood_usable += removed
+ to_chat(M, "You have accumulated [vampire.blood_usable] [vampire.blood_usable > 1 ? "units" : "unit"] of usable blood. It tastes quite stale.")
return
if(dose > 5)
M.adjustToxLoss(removed)
diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm
index eca06cfde6c..65dc41508e2 100644
--- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm
+++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm
@@ -2183,6 +2183,17 @@
glass_desc = "It has an oily smell and doesn't taste like typical gin."
glass_center_of_mass = list("x"=16, "y"=12)
+/datum/reagent/alcohol/ethanol/djinntea
+ name = "Djinn Tea"
+ description = "A mildly alcoholic spin on a popular Skrell drink."
+ color = "#84C0C0"
+ strength = 20
+ taste_description = "fizzy mint tea"
+
+ glass_icon_state = "djinnteaglass"
+ glass_name = "glass of Djinn Tea"
+ glass_desc = "A mildly alcoholic spin on a popular Skrell drink. Less good for you than the original."
+
//Base type for alchoholic drinks containing coffee
/datum/reagent/alcohol/ethanol/coffee
overdose = 45
@@ -2261,6 +2272,18 @@
glass_desc = "A glass of sake."
glass_center_of_mass = list("x"=16, "y"=12)
+/datum/reagent/alcohol/ethanol/cloudyoran
+ name = "Cloudy Oran"
+ description = "Reminds Suits of home. Dregs, not so much."
+ color = "#F6F6F6"
+ strength = 15
+ taste_description = "soy milk putting on airs"
+
+ glass_icon_state = "cloudyoranglass"
+ glass_name = "glass of Cloudy Oran"
+ glass_desc = "A frothy white beverage. Reminds Suits of home. Dregs, not so much."
+ glass_center_of_mass = list("x"=16, "y"=5)
+
/datum/reagent/alcohol/ethanol/soju
name = "Soju"
description = "A mild Konyanger spirit that is best described as rice vodka."
@@ -2489,6 +2512,31 @@
glass_desc = "Tropical cocktail"
glass_center_of_mass = list("x"=16, "y"=5)
+/datum/reagent/alcohol/ethanol/diona_mama
+ name = "Diona Mama"
+ description = "Lightly irradiated."
+ color = "#56A05D"
+ strength = 25
+ druggy = 25
+ taste_description = "tangy, irradiated licorice"
+
+ glass_icon_state = "dionamamaglass"
+ glass_name = "glass of Diona Mama"
+ glass_desc = "Lightly irradiated, just the way Dionae like it."
+ glass_center_of_mass = list("x"=16, "y"=5)
+
+/datum/reagent/alcohol/ethanol/jovian_storm
+ name = "Jovian Storm"
+ description = "Named after Jupiter’s storm. It’ll blow you away."
+ color = "#AA856A"
+ strength = 15
+ taste_description = "stormy sweetness"
+
+ glass_icon_state = "jovianstormglass"
+ glass_name = "glass of Jovian Storm"
+ glass_desc = "A classic Callistean drink named after Jupiter’s storm. It’ll blow you away."
+ glass_center_of_mass = list("x"=16, "y"=5)
+
/datum/reagent/alcohol/ethanol/bananahonk
name = "Banana Mama"
description = "A drink from Clown Heaven."
@@ -2664,6 +2712,19 @@
glass_desc = "A classic mix of rum, cola, and lime."
glass_center_of_mass = list("x"=16, "y"=8)
+/datum/reagent/alcohol/ethanol/rumandcola
+ name = "Rum and Cola"
+ description = "A classic cocktail consisting of rum and cola."
+ color = "#3E1B00"
+ strength = 10
+ taste_description = "cola"
+ carbonated = TRUE
+
+ glass_icon_state = "rumandcolaglass"
+ glass_name = "glass of Rum and Cola"
+ glass_desc = "A classic mix of rum and cola."
+ glass_center_of_mass = list("x"=16, "y"=8)
+
/datum/reagent/alcohol/ethanol/demonsblood
name = "Demons Blood"
description = "AHHHH!!!!"
@@ -2842,6 +2903,19 @@
glass_desc = "An irish car bomb."
glass_center_of_mass = list("x"=16, "y"=8)
+/datum/reagent/alcohol/ethanol/fisfirebomb
+ name = "Fisanduhian Firebomb"
+ description = "Mmm, tastes like spicy chocolate..."
+ color = "#320C00"
+ strength = 50
+ taste_description = "anti-dominian sentiment"
+ carbonated = TRUE
+
+ glass_icon_state = "fisfirebombglass"
+ glass_name = "glass of Fisanduhian Firebomb"
+ glass_desc = "The somewhat spicier cousin to the Irish Car Bomb."
+ glass_center_of_mass = list("x"=16, "y"=8)
+
/datum/reagent/alcohol/ethanol/coffee/irishcoffee
name = "Irish Coffee"
description = "Coffee, and alcohol. More fun than a Mimosa to drink in the morning."
@@ -2855,6 +2929,19 @@
glass_desc = "Coffee and alcohol. More fun than a Mimosa to drink in the morning."
glass_center_of_mass = list("x"=15, "y"=10)
+/datum/reagent/alcohol/ethanol/coffee/fiscoffee
+ name = "Fisanduhian Coffee"
+ description = "Coffee, and spicy alcohol. Popular among people who dislike Dominians."
+ color = "#A9501C"
+ strength = 50
+ caffeine = 0.3
+ taste_description = "giving up on peaceful coexistence"
+
+ glass_icon_state = "giscoffeeglass"
+ glass_name = "glass of Fisanduhian coffee"
+ glass_desc = "It's like an Irish coffee, but spicy and angry about Dominia."
+ glass_center_of_mass = list("x"=15, "y"=10)
+
/datum/reagent/alcohol/ethanol/irishcream
name = "Irish Cream"
description = "Whiskey-imbued cream, what else would you expect from the Irish."
@@ -2867,6 +2954,18 @@
glass_desc = "It's cream, mixed with whiskey. What else would you expect from the Irish?"
glass_center_of_mass = list("x"=16, "y"=9)
+/datum/reagent/alcohol/ethanol/fiscream
+ name = "Fisanduhian Cream"
+ description = "A sweet, slightly spicy alcoholic cream. Fisanduh is not yet lost."
+ color = "#C8AC97"
+ strength = 25
+ taste_description = "creamy spiced alcohol"
+
+ glass_icon_state = "irishcreamglass"
+ glass_name = "glass of Fisanduhian cream"
+ glass_desc = "A sweet, slightly spicy alcoholic cream. Fisanduh is not yet lost."
+ glass_center_of_mass = list("x"=16, "y"=9)
+
/datum/reagent/alcohol/ethanol/longislandicedtea
name = "Long Island Iced Tea"
description = "The liquor cabinet, brought together in a delicious mix. Intended for middle-aged alcoholic women only."
@@ -3280,6 +3379,59 @@
glass_desc = "A very nice looking drink. But that's just, like, your opinion, man."
glass_center_of_mass = list("x"=16, "y"=9)
+/datum/reagent/alcohol/ethanol/solarian_white
+ name = "Solarian White"
+ description = "Despite the name, this is not a security officer."
+ color = "#C3D1D4"
+ strength = 30
+ taste_description = "creamy vodka and lime"
+
+ glass_icon_state = "solarianwhiteglass"
+ glass_name = "glass of Solarian White"
+ glass_desc = "A classic Solarian cocktail. Despite the name, this is not a security officer."
+
+ glass_center_of_mass = list("x"=16, "y"=9)
+
+/datum/reagent/alcohol/ethanol/solarian_marine
+ name = "Solarian Marine"
+ description = "Drink too many of these, and you'll wake up invading Tau Ceti."
+ reagent_state = LIQUID
+ color = "#33567A"
+ strength = 35
+ taste_description = "polished boots and nationalism"
+
+ glass_icon_state = "solarianmarineglass"
+ glass_name = "Solarian Marine"
+ glass_desc = "Drink too many of these, and you'll wake up invading Tau Ceti."
+
+ glass_center_of_mass = list("x"=16, "y"=9)
+
+/datum/reagent/alcohol/ethanol/permanent_revolution
+ name = "Permanent Revolution"
+ description = "You have nothing to lose but your sobriety."
+ color = "#A7AA60"
+ strength = 65
+ taste_description = "strong, earthy licorice"
+
+ glass_icon_state = "permanentrevolutionglass"
+ glass_name = "glass of Permanent Revolution"
+ glass_desc = "A Himean cocktail, so named for its tendency to make the room spin. You have nothing to lose but your sobriety."
+
+ glass_center_of_mass = list("x"=16, "y"=9)
+
+/datum/reagent/alcohol/ethanol/internationale
+ name = "Solarian White"
+ description = "The subversive's choice."
+ color = "#D9CCAA"
+ strength = 28
+ taste_description = "earthy, oily unity"
+
+ glass_icon_state = "internationaleglass"
+ glass_name = "glass of Internationale"
+ glass_desc = "The nearest thing the Orion Spur has to left unity. The subversive's choice."
+
+ glass_center_of_mass = list("x"=16, "y"=9)
+
/datum/reagent/alcohol/ethanol/whiskeycola
name = "Whiskey Cola"
description = "Whiskey, mixed with cola. Surprisingly refreshing."
@@ -3437,6 +3589,28 @@
glass_name = "glass of Metropolitan"
glass_desc = "What more could you ask for?"
+/datum/reagent/alcohol/ethanol/primeminister
+ name = "Prime Minister"
+ description = "All the fun of power, none of the assassination risk!"
+ color = "#FF3C00"
+ strength = 30
+ taste_description = "political power"
+
+ glass_icon_state = "primeministerglass"
+ glass_name = "glass of Prime Minister"
+ glass_desc = "All the fun of power, none of the assassination risk!"
+
+/datum/reagent/alcohol/ethanol/peacetreaty
+ name = "Peace Treaty"
+ description = "A diplomatic overture in a glass."
+ color = "#DFDF93"
+ strength = 21
+ taste_description = "tart, oily honey"
+
+ glass_icon_state = "peacetreatyglass"
+ glass_name = "glass of Peace Treaty"
+ glass_desc = "A diplomatic overture in a glass"
+
/datum/reagent/alcohol/ethanol/caruso
name = "Caruso"
description = "Green, almost alien."
@@ -4437,16 +4611,3 @@
condiment_desc = "A vegetarian friendly way to add a little extra pizazz to any dish."
condiment_icon_state = "dionaepowder"
condiment_center_of_mass = list("x"=16, "y"=10)
-
-/datum/reagent/alcohol/ethanol/rumandcola
- name = "Rum and Cola"
- description = "A classic cocktail consisting of rum and cola."
- color = "#3E1B00"
- strength = 10
- taste_description = "cola"
- carbonated = TRUE
-
- glass_icon_state = "rumandcolaglass"
- glass_name = "glass of Rum and Cola"
- glass_desc = "A classic mix of rum and cola."
- glass_center_of_mass = list("x"=16, "y"=8)
diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Other.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Other.dm
index 76d52ddd33f..644fad9499a 100644
--- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Other.dm
+++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Other.dm
@@ -218,11 +218,12 @@
/datum/reagent/water/holywater/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed)
..()
if(ishuman(M))
- if (M.mind && M.mind.vampire)
- var/datum/vampire/vampire = M.mind.vampire
- vampire.frenzy += removed * 5
- else if(M.mind && cult.is_antagonist(M.mind) && prob(10))
- cult.remove_antagonist(M.mind)
+ if(M.mind)
+ var/datum/vampire/vampire = M.mind.antag_datums[MODE_VAMPIRE]
+ if(vampire)
+ vampire.frenzy += removed * 5
+ if(cult.is_antagonist(M.mind) && prob(10))
+ cult.remove_antagonist(M.mind)
if(alien && alien == IS_UNDEAD)
M.adjust_fire_stacks(10)
M.IgniteMob()
diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm
index 264a9e58618..7ab0ebcb521 100644
--- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm
+++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm
@@ -16,18 +16,24 @@
/datum/reagent/toxin/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
if(strength && alien != IS_DIONA)
var/dam = (strength * removed)
- if(target_organ && ishuman(M))
+ if(ishuman(M))
var/mob/living/carbon/human/H = M
- var/obj/item/organ/internal/I = H.internal_organs_by_name[target_organ]
- if(I)
- var/can_damage = I.max_damage - I.damage
- if(can_damage > 0)
- if(dam > can_damage)
- I.take_internal_damage(can_damage, silent=TRUE)
- dam -= can_damage
- else
- I.take_internal_damage(dam, silent=TRUE)
- dam = 0
+ if(target_organ)
+ var/obj/item/organ/internal/I = H.internal_organs_by_name[target_organ]
+ if(I)
+ var/can_damage = I.max_damage - I.damage
+ if(can_damage > 0)
+ if(dam > can_damage)
+ I.take_internal_damage(can_damage, silent=TRUE)
+ dam -= can_damage
+ else
+ I.take_internal_damage(dam, silent=TRUE)
+ dam = 0
+ for(var/organ in H.organs)
+ var/obj/item/organ/external/O = organ
+ var/obj/effect/spider/eggcluster/C = locate() in O
+ if(C)
+ C.take_damage(removed * 2)
if(dam)
M.adjustToxLoss(target_organ ? (dam * 0.5) : dam)
diff --git a/code/modules/reagents/Chemistry-Recipes.dm b/code/modules/reagents/Chemistry-Recipes.dm
index 638095bbec3..cd81585e89b 100644
--- a/code/modules/reagents/Chemistry-Recipes.dm
+++ b/code/modules/reagents/Chemistry-Recipes.dm
@@ -794,7 +794,7 @@
/datum/chemical_reaction/plastication/on_reaction(var/datum/reagents/holder, var/created_volume)
new /obj/item/stack/material/plastic(get_turf(holder.my_atom), created_volume)
return
-
+
/datum/chemical_reaction/uraniumsolidification
name = "Uranium"
id = "soliduranium"
@@ -3673,5 +3673,96 @@
name = "Cuba Libre"
id = "cubalibre"
result = /datum/reagent/alcohol/ethanol/cubalibre
- required_reagents = list(/datum/reagent/alcohol/ethanol/rumandcola = 5, /datum/reagent/drink/limejuice = 1)
+ required_reagents = list(/datum/reagent/alcohol/ethanol/rumandcola = 2, /datum/reagent/drink/limejuice = 1)
+ result_amount = 3
+
+/datum/chemical_reaction/drink/solarian_white
+ name = "Solarian White"
+ id = "solarian_white"
+ result = /datum/reagent/alcohol/ethanol/solarian_white
+ required_reagents = list(/datum/reagent/alcohol/ethanol/vodka = 1, /datum/reagent/drink/milk/cream = 1, /datum/reagent/drink/limejuice =1)
+ result_amount = 3
+
+/datum/chemical_reaction/drink/solarian_marine
+ name = "Solarian Marine"
+ id = "solarian_marine"
+ result = /datum/reagent/alcohol/ethanol/solarian_marine
+ required_reagents = list(/datum/reagent/drink/tea/securitea = 1, /datum/reagent/alcohol/ethanol/whiskey = 1)
+ result_amount = 2
+
+/datum/chemical_reaction/drink/cloudyoran
+ name = "Cloudy Oran"
+ id = "cloudyoran"
+ result = /datum/reagent/alcohol/ethanol/cloudyoran
+ required_reagents = list(/datum/reagent/alcohol/ethanol/sake = 1, /datum/reagent/drink/tea/greentea = 1, /datum/reagent/drink/milk/soymilk = 1)
+ result_amount = 3
+
+/datum/chemical_reaction/drink/djinntea
+ name = "Djinn Tea"
+ id = "djinntea"
+ result = /datum/reagent/alcohol/ethanol/djinntea
+ required_reagents = list(/datum/reagent/drink/dynjuice/cold = 1, /datum/reagent/alcohol/ethanol/gin = 1)
+ result_amount = 2
+
+/datum/chemical_reaction/drink/permanent_revolution
+ name = "Permanent Revolution"
+ id = "permanent_revolution"
+ result = /datum/reagent/alcohol/ethanol/permanent_revolution
+ required_reagents = list(/datum/reagent/alcohol/ethanol/absinthe = 1, /datum/reagent/alcohol/ethanol/vodka/mushroom = 1)
+ result_amount = 2
+
+/datum/chemical_reaction/drink/internationale
+ name = "Internationale"
+ id = "internationale"
+ result = /datum/reagent/alcohol/ethanol/internationale
+ required_reagents = list(/datum/reagent/alcohol/ethanol/victorygin = 1, /datum/reagent/alcohol/ethanol/vodka/mushroom = 1)
+ result_amount = 2
+
+/datum/chemical_reaction/drink/diona_mama
+ name = "Diona Mama"
+ id = "diona_mama"
+ result = /datum/reagent/alcohol/ethanol/diona_mama
+ required_reagents = list(/datum/reagent/alcohol/ethanol/absinthe = 2, /datum/reagent/drink/limejuice = 2, /datum/reagent/radium = 1, /datum/reagent/drink/ice = 1)
result_amount = 6
+
+/datum/chemical_reaction/drink/jovian_storm
+ name = "Jovian Storm"
+ id = "jovian_storm"
+ result = /datum/reagent/alcohol/ethanol/jovian_storm
+ required_reagents = list(/datum/reagent/alcohol/ethanol/rum = 2, /datum/reagent/drink/grenadine = 2, /datum/reagent/drink/lemonjuice = 1, /datum/reagent/drink/ice = 1)
+ result_amount = 6
+
+/datum/chemical_reaction/drink/primeminister
+ name = "Prime Minister"
+ id = "primeminister"
+ result = /datum/reagent/alcohol/ethanol/primeminister
+ required_reagents = list(/datum/reagent/alcohol/ethanol/rum = 4, /datum/reagent/alcohol/ethanol/vermouth = 1, /datum/reagent/drink/grenadine = 1)
+ result_amount = 6
+
+/datum/chemical_reaction/drink/peacetreaty
+ name = "Peace Treaty"
+ id = "peacetreaty"
+ result = /datum/reagent/alcohol/ethanol/peacetreaty
+ required_reagents = list(/datum/reagent/alcohol/ethanol/victorygin = 1, /datum/reagent/alcohol/ethanol/messa_mead = 1, /datum/reagent/drink/lemonjuice = 1)
+ result_amount = 3
+
+/datum/chemical_reaction/drink/fiscream
+ name = "Fisanduhian Cream"
+ id = "fiscream"
+ result = /datum/reagent/alcohol/ethanol/fiscream
+ required_reagents = list(/datum/reagent/alcohol/ethanol/fireball = 2, /datum/reagent/drink/milk/cream = 1)
+ result_amount = 3
+
+/datum/chemical_reaction/drink/fiscoffee
+ name = "Fisanduhian Coffee"
+ id = "fiscoffee"
+ result = /datum/reagent/alcohol/ethanol/coffee/fiscoffee
+ required_reagents = list(/datum/reagent/alcohol/ethanol/fiscream = 1, /datum/reagent/drink/coffee = 1)
+ result_amount = 2
+
+/datum/chemical_reaction/drink/fisfirebomb
+ name = "Fisanduhian Firebomb"
+ id = "fiscarbomb"
+ result = /datum/reagent/alcohol/ethanol/fisfirebomb
+ required_reagents = list(/datum/reagent/alcohol/ethanol/ale = 1, /datum/reagent/alcohol/ethanol/fiscream = 1)
+ result_amount = 2
diff --git a/code/modules/reagents/dispenser/dispenser2.dm b/code/modules/reagents/dispenser/dispenser2.dm
index 4153588d78e..7b90dc438db 100644
--- a/code/modules/reagents/dispenser/dispenser2.dm
+++ b/code/modules/reagents/dispenser/dispenser2.dm
@@ -33,51 +33,51 @@
/obj/machinery/chemical_dispenser/proc/add_cartridge(obj/item/reagent_containers/chem_disp_cartridge/C, mob/user)
if(!istype(C))
if(user)
- to_chat(user, "\The [C] will not fit in \the [src]!")
+ to_chat(user, SPAN_WARNING("[C] will not fit in [src]!"))
return
if(cartridges.len >= DISPENSER_MAX_CARTRIDGES)
if(user)
- to_chat(user, "\The [src] does not have any slots open for \the [C] to fit into!")
+ to_chat(user, SPAN_WARNING("[src] does not have any slots open for [C] to fit into!"))
return
if(!C.label)
if(user)
- to_chat(user, "\The [C] does not have a label!")
+ to_chat(user, SPAN_WARNING("[C] does not have a label!"))
return
if(cartridges[C.label])
if(user)
- to_chat(user, "\The [src] already contains a cartridge with that label!")
+ to_chat(user, SPAN_WARNING("[src] already contains a cartridge with that label!"))
return
if(user)
user.drop_from_inventory(C,src)
- to_chat(user, "You add \the [C] to \the [src].")
+ to_chat(user, SPAN_NOTICE("You add [C] to [src]."))
else
C.forceMove(src)
cartridges[C.label] = C
sortTim(cartridges, /proc/cmp_text_asc)
- SSnanoui.update_uis(src)
+ SSvueui.check_uis_for_change(src)
/obj/machinery/chemical_dispenser/proc/remove_cartridge(label)
. = cartridges[label]
cartridges -= label
- SSnanoui.update_uis(src)
+ SSvueui.check_uis_for_change(src)
/obj/machinery/chemical_dispenser/attackby(obj/item/W, mob/user)
if(W.iswrench())
playsound(src.loc, W.usesound, 50, 1)
- to_chat(user, "You begin to [anchored ? "un" : ""]fasten \the [src].")
+ to_chat(user, SPAN_NOTICE("You begin to [anchored ? "un" : ""]fasten [src]."))
if (do_after(user, 20))
user.visible_message(
- "\The [user] [anchored ? "un" : ""]fastens \the [src].",
- "You have [anchored ? "un" : ""]fastened \the [src].",
+ SPAN_NOTICE("[user] [anchored ? "un" : ""]fastens [src]."),
+ SPAN_NOTICE("You have [anchored ? "un" : ""]fastened [src]."),
"You hear a ratchet.")
anchored = !anchored
else
- to_chat(user, "You decide not to [anchored ? "un" : ""]fasten \the [src].")
+ to_chat(user, SPAN_NOTICE("You decide not to [anchored ? "un" : ""]fasten [src]."))
else if(istype(W, /obj/item/reagent_containers/chem_disp_cartridge))
add_cartridge(W, user)
@@ -87,82 +87,75 @@
if(!label) return
var/obj/item/reagent_containers/chem_disp_cartridge/C = remove_cartridge(label)
if(C)
- to_chat(user, "You remove \the [C] from \the [src].")
+ to_chat(user, SPAN_NOTICE("You remove [C] from [src]."))
C.forceMove(loc)
else if(istype(W, /obj/item/reagent_containers/glass) || istype(W, /obj/item/reagent_containers/food))
if(container)
- to_chat(user, "There is already \a [container] on \the [src]!")
+ to_chat(user, SPAN_WARNING("There is already \a [container] on [src]!"))
return
var/obj/item/reagent_containers/RC = W
if(!accept_drinking && istype(RC,/obj/item/reagent_containers/food))
- to_chat(user, "This machine only accepts beakers!")
+ to_chat(user, SPAN_WARNING("This machine only accepts beakers!"))
return
if(!RC.is_open_container())
- to_chat(user, "You don't see how \the [src] could dispense reagents into \the [RC].")
+ to_chat(user, SPAN_WARNING("You don't see how [src] could dispense reagents into [RC]."))
return
container = RC
user.drop_from_inventory(RC,src)
- to_chat(user, "You set \the [RC] on \the [src].")
- SSnanoui.update_uis(src) // update all UIs attached to src
+ to_chat(user, SPAN_NOTICE("You set [RC] on [src]."))
+ SSvueui.check_uis_for_change(src) // update all UIs attached to src
if(icon_state_active)
icon_state = icon_state_active
else
return ..()
-/obj/machinery/chemical_dispenser/ui_interact(mob/user, ui_key = "main",var/datum/nanoui/ui = null, var/force_open = 1)
+/obj/machinery/chemical_dispenser/vueui_data_change(list/data, mob/user, datum/vueui/ui)
+ data = ..() || data || list()
// this is the data which will be sent to the ui
- if(container && !container.reagents) //sanity check in case you destroyed the container... such as if you dispensed acid into an acidable bucket.
- container = null
- var/data[0]
data["amount"] = amount
- data["isBeakerLoaded"] = container ? 1 : 0
data["glass"] = accept_drinking
+ data["isBeakerLoaded"] = !!container
+ data["beakerMaxVolume"] = container?.reagents?.maximum_volume
+ data["beakerCurrentVolume"] = container?.reagents?.total_volume
var beakerD[0]
if(container && container.reagents && container.reagents.reagent_list.len)
for(var/datum/reagent/R in container.reagents.reagent_list)
beakerD[++beakerD.len] = list("name" = R.name, "volume" = R.volume)
data["beakerContents"] = beakerD
-
- if(container)
- data["beakerCurrentVolume"] = container.reagents.total_volume
- data["beakerMaxVolume"] = container.reagents.maximum_volume
- else
- data["beakerCurrentVolume"] = null
- data["beakerMaxVolume"] = null
-
var chemicals[0]
for(var/label in cartridges)
var/obj/item/reagent_containers/chem_disp_cartridge/C = cartridges[label]
chemicals[++chemicals.len] = list("label" = label, "amount" = C.reagents.total_volume)
data["chemicals"] = chemicals
+ return data
- // update the ui if it exists, returns null if no ui is passed/found
- ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
+/obj/machinery/chemical_dispenser/ui_interact(mob/user)
+ var/datum/vueui/ui = SSvueui.get_open_ui(user, src)
if(!ui)
- ui = new(user, src, ui_key, "chem_disp.tmpl", ui_title, 390, 680)
- ui.set_initial_data(data)
- ui.open()
+ ui = new(user, src, "machinery-chemdisp", 390, 680, ui_title, state = interactive_state)
+ ui.open()
/obj/machinery/chemical_dispenser/Topic(href, href_list)
if(..())
- return 1
+ return TOPIC_NOACTION
if(href_list["amount"])
amount = round(text2num(href_list["amount"]), 1) // round to nearest 1
- amount = max(0, min(120, amount)) // Since the user can actually type the commands himself, some sanity checking
+ amount = between(amount, 1, container?.reagents?.maximum_volume || 120) // Since the user can actually type the commands himself, some sanity checking
else if(href_list["dispense"])
var/label = href_list["dispense"]
- if(cartridges[label] && container && container.is_open_container())
+ if(cartridges[label] && container?.is_open_container())
var/obj/item/reagent_containers/chem_disp_cartridge/C = cartridges[label]
playsound(src.loc, 'sound/machines/reagent_dispense.ogg', 25, 1)
C.reagents.trans_to(container, amount)
+ addtimer(CALLBACK(SSvueui, /datum/controller/subsystem/processing/vueui/proc/check_uis_for_change, src), 2 SECONDS) //Just in case we get no new data
else if(href_list["ejectBeaker"])
if(container)
@@ -171,9 +164,10 @@
container = null
if(icon_state_active)
icon_state = initial(icon_state)
+ SSvueui.check_uis_for_change(src)
add_fingerprint(usr)
- return 1 // update UIs attached to this object
+ return TOPIC_REFRESH // update UIs attached to this object
/obj/machinery/chemical_dispenser/attack_ai(mob/user as mob)
if(!ai_can_interact(user))
diff --git a/code/modules/reagents/reagent_containers/blood_pack.dm b/code/modules/reagents/reagent_containers/blood_pack.dm
index f9130b5d14a..96dce3cca3e 100644
--- a/code/modules/reagents/reagent_containers/blood_pack.dm
+++ b/code/modules/reagents/reagent_containers/blood_pack.dm
@@ -44,32 +44,34 @@
if(51 to INFINITY) icon_state = "full"
/obj/item/reagent_containers/blood/attack(mob/living/carbon/human/M as mob, mob/living/carbon/human/user as mob, var/target_zone)
- if (user == M && (user.mind.vampire))
- if (being_feed)
- to_chat(user, "You are already feeding on \the [src].")
- return
- if (reagents.get_reagent_amount(/datum/reagent/blood))
- user.visible_message("[user] raises \the [src] up to their mouth and bites into it.", "You raise \the [src] up to your mouth and bite into it, starting to drain its contents. You need to stand still.")
- being_feed = TRUE
- vampire_marks = TRUE
- if (!LAZYLEN(src.other_DNA))
- LAZYADD(src.other_DNA, M.dna.unique_enzymes)
- src.other_DNA_type = "saliva"
+ if(user == M && user.mind)
+ var/datum/vampire/vampire = user.mind.antag_datums[MODE_VAMPIRE]
+ if(vampire)
+ if (being_feed)
+ to_chat(user, "You are already feeding on \the [src].")
+ return
+ if (reagents.get_reagent_amount(/datum/reagent/blood))
+ user.visible_message("[user] raises \the [src] up to their mouth and bites into it.", "You raise \the [src] up to your mouth and bite into it, starting to drain its contents. You need to stand still.")
+ being_feed = TRUE
+ vampire_marks = TRUE
+ if (!LAZYLEN(src.other_DNA))
+ LAZYADD(src.other_DNA, M.dna.unique_enzymes)
+ src.other_DNA_type = "saliva"
- while (do_after(user, 25, 5, 1))
- var/blood_taken = 0
- blood_taken = min(5, reagents.get_reagent_amount(/datum/reagent/blood)/4)
+ while (do_after(user, 25, 5, 1))
+ var/blood_taken = 0
+ blood_taken = min(5, reagents.get_reagent_amount(/datum/reagent/blood)/4)
- reagents.remove_reagent(/datum/reagent/blood, blood_taken*4)
- user.mind.vampire.blood_usable += blood_taken
+ reagents.remove_reagent(/datum/reagent/blood, blood_taken*4)
+ vampire.blood_usable += blood_taken
- if (blood_taken)
- to_chat(user, "You have accumulated [user.mind.vampire.blood_usable] [user.mind.vampire.blood_usable > 1 ? "units" : "unit"] of usable blood. It tastes quite stale.")
+ if (blood_taken)
+ to_chat(user, "You have accumulated [vampire.blood_usable] [vampire.blood_usable > 1 ? "units" : "unit"] of usable blood. It tastes quite stale.")
- if (reagents.get_reagent_amount(/datum/reagent/blood) < 1)
- break
- user.visible_message("[user] licks [user.get_pronoun("his")] fangs dry, lowering \the [src].", "You lick your fangs clean of the tasteless blood.")
- being_feed = FALSE
+ if (reagents.get_reagent_amount(/datum/reagent/blood) < 1)
+ break
+ user.visible_message("[user] licks [user.get_pronoun("his")] fangs dry, lowering \the [src].", "You lick your fangs clean of the tasteless blood.")
+ being_feed = FALSE
else
..()
diff --git a/code/modules/reagents/reagent_containers/food/drinks.dm b/code/modules/reagents/reagent_containers/food/drinks.dm
index f9a364d97b3..5c3c124a5e1 100644
--- a/code/modules/reagents/reagent_containers/food/drinks.dm
+++ b/code/modules/reagents/reagent_containers/food/drinks.dm
@@ -1,8 +1,3 @@
-//drink_flags defines
-#define NO_EMPTY_ICON 1 //does NOT have an iconstate_empty icon. If adding empty icons for a drink, make sure it does not have this flag
-#define UNIQUE_EMPTY_ICON 2 //Uses the empty_icon_state listed. Should really only be used when one trash state applies to multiple drinks. Remove if one is added
-#define IS_GLASS 4 //Container is glass. Affects shattering, unacidable, etc.
-
/*
Standards for trash/empty states under the /drinks path:
Adding Empty States: Trash/Empty states should be placed in the drinks_empty.dmi and should be the drink's icon_state name followed by _empty (ex: whiskeybottle_empty) and the NO_EMPTY_ICON flag should be removed.
@@ -44,11 +39,11 @@ If you add a drink with no empty icon sprite, ensure it is flagged as NO_EMPTY_I
if(drink_flags & UNIQUE_EMPTY_ICON)
icon = 'icons/obj/drinks_empty.dmi'
icon_state = empty_icon_state
- return
- if(!(drink_flags & NO_EMPTY_ICON))
+ else if(drink_flags & UNIQUE_EMPTY_ICON_FILE)
+ icon_state = empty_icon_state
+ else if(!(drink_flags & NO_EMPTY_ICON))
icon = 'icons/obj/drinks_empty.dmi'
icon_state = "[initial(icon_state)]_empty"
- return
else
icon = initial(icon) //Necessary for refilling empty drinks
icon_state = initial(icon_state)
diff --git a/code/modules/reagents/reagent_containers/food/snacks.dm b/code/modules/reagents/reagent_containers/food/snacks.dm
index cc0fcbb574a..1009c628b6c 100644
--- a/code/modules/reagents/reagent_containers/food/snacks.dm
+++ b/code/modules/reagents/reagent_containers/food/snacks.dm
@@ -3204,9 +3204,16 @@
var/open = 0 // Is the box open?
var/ismessy = 0 // Fancy mess on the lid
var/obj/item/reagent_containers/food/snacks/sliceable/pizza/pizza // Content pizza
+ var/pizza_type
var/list/boxes = list() // If the boxes are stacked, they come here
var/boxtag = ""
+/obj/item/pizzabox/Initialize()
+ . = ..()
+ if(pizza_type)
+ pizza = new pizza_type(src)
+ update_icon()
+
/obj/item/pizzabox/update_icon()
cut_overlays()
@@ -3351,22 +3358,26 @@
return
..()
-/obj/item/pizzabox/margherita/New()
- pizza = new /obj/item/reagent_containers/food/snacks/sliceable/pizza/margherita(src)
+/obj/item/pizzabox/margherita
+ pizza_type = /obj/item/reagent_containers/food/snacks/sliceable/pizza/margherita
boxtag = "Margherita Deluxe"
-/obj/item/pizzabox/vegetable/New()
- pizza = new /obj/item/reagent_containers/food/snacks/sliceable/pizza/vegetablepizza(src)
+/obj/item/pizzabox/vegetable
+ pizza_type = /obj/item/reagent_containers/food/snacks/sliceable/pizza/vegetablepizza
boxtag = "Gourmet Vegatable"
-/obj/item/pizzabox/mushroom/New()
- pizza = new /obj/item/reagent_containers/food/snacks/sliceable/pizza/mushroompizza(src)
+/obj/item/pizzabox/mushroom
+ pizza_type = /obj/item/reagent_containers/food/snacks/sliceable/pizza/mushroompizza
boxtag = "Mushroom Special"
-/obj/item/pizzabox/meat/New()
- pizza = new /obj/item/reagent_containers/food/snacks/sliceable/pizza/meatpizza(src)
+/obj/item/pizzabox/meat
+ pizza_type = /obj/item/reagent_containers/food/snacks/sliceable/pizza/meatpizza
boxtag = "Meatlover's Supreme"
+/obj/item/pizzabox/pineapple
+ pizza_type = /obj/item/reagent_containers/food/snacks/sliceable/pizza/pineapple
+ boxtag = "Silversun Sunrise"
+
/obj/item/reagent_containers/food/snacks/sliceable/dionaroast
name = "roast diona"
desc = "It's like an enormous, leathery carrot. With an eye."
diff --git a/code/modules/reagents/reagent_containers/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm
index 8c7f6c1f0ba..93a17351abb 100644
--- a/code/modules/reagents/reagent_containers/hypospray.dm
+++ b/code/modules/reagents/reagent_containers/hypospray.dm
@@ -169,6 +169,14 @@
reagents.add_reagent(/datum/reagent/inaprovaline, 5)
update_icon()
+/obj/item/reagent_containers/hypospray/autoinjector/emergency
+ name = "autoinjector (emergency)"
+ reagents_to_add = list(/datum/reagent/inaprovaline = 2.5, /datum/reagent/dexalin = 2.5)
+
+/obj/item/reagent_containers/hypospray/autoinjector/emergency/Initialize()
+ . = ..()
+ desc += " This auto-injector is to be used in emergencies. It contains a small amount of inaprovaline and dexalin."
+
/obj/item/reagent_containers/hypospray/autoinjector/sideeffectbgone
name = "sideeffects-be-gone! autoinjector"
desc = "A special cocktail designed to counter the side-effects of various drugs. Has 2 uses."
diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm
index 70eae980c47..fc7f853d3a3 100644
--- a/code/modules/recycling/disposal.dm
+++ b/code/modules/recycling/disposal.dm
@@ -163,6 +163,12 @@
update()
return
+ else if (istype (I, /obj/item/material/ashtray) && user.a_intent != I_HURT)
+ var/obj/item/material/ashtray/A = I
+ if(A.emptyout(get_turf(src)))
+ user.visible_message("[user] pours [I] out into [src].", SPAN_NOTICE("You pour [I] out into [src]."))
+ return
+
else if (istype (I, /obj/item/device/lightreplacer))
var/count = 0
var/obj/item/device/lightreplacer/R = I
diff --git a/code/modules/research/designs/mechfab/hardsuit/modules.dm b/code/modules/research/designs/mechfab/hardsuit/modules.dm
index 0a667b3b3f8..7b157ed04bb 100644
--- a/code/modules/research/designs/mechfab/hardsuit/modules.dm
+++ b/code/modules/research/designs/mechfab/hardsuit/modules.dm
@@ -106,4 +106,11 @@
desc = "A heat sink with liquid cooled radiator."
req_tech = list(TECH_MATERIAL = 2, TECH_POWER = 3, TECH_ENGINEERING = 3)
materials = list(DEFAULT_WALL_MATERIAL = 7000, MATERIAL_GLASS = 5500)
- build_path = /obj/item/rig_module/cooling_unit
\ No newline at end of file
+ build_path = /obj/item/rig_module/cooling_unit
+
+/datum/design/hardsuitmodules/foam_sprayer
+ name = "Mounted Foam Sprayer"
+ desc = "A shoulder-mounted metal foam sprayer."
+ req_tech = list(TECH_MATERIAL = 3, TECH_ENGINEERING = 3, TECH_POWER = 2)
+ materials = list(DEFAULT_WALL_MATERIAL = 15000, MATERIAL_GLASS = 6000)
+ build_path = /obj/item/rig_module/foam_sprayer
\ No newline at end of file
diff --git a/code/modules/shareddream/brainghost.dm b/code/modules/shareddream/brainghost.dm
index de9d59b5612..8cc059fa994 100644
--- a/code/modules/shareddream/brainghost.dm
+++ b/code/modules/shareddream/brainghost.dm
@@ -20,6 +20,12 @@
loc = pick(dream_entries)
body = form
+ if(client)
+ client.screen |= body.healths
+
+/mob/living/brain_ghost/LateLogin()
+ ..()
+ client.screen |= body.healths
/mob/living/brain_ghost/verb/awaken()
set name = "Awaken"
diff --git a/code/modules/tables/interactions.dm b/code/modules/tables/interactions.dm
index 9607c1da3e3..da9f5be361a 100644
--- a/code/modules/tables/interactions.dm
+++ b/code/modules/tables/interactions.dm
@@ -63,7 +63,7 @@
..()
if(ishuman(am))
var/mob/living/carbon/human/H = am
- if(H.a_intent != I_HELP || H.m_intent == "run")
+ if(H.a_intent != I_HELP || H.m_intent == M_RUN)
throw_things(H)
else if(H.is_diona() || H.species.get_bodytype() == BODYTYPE_IPC_INDUSTRIAL)
throw_things(H)
@@ -119,7 +119,7 @@
)
if(ishuman(user))
var/mob/living/carbon/human/H = user
- if(H.a_intent != I_HELP || H.m_intent == "run")
+ if(H.a_intent != I_HELP || H.m_intent == M_RUN)
throw_things(H)
else if(H.is_diona() || H.species.get_bodytype() == BODYTYPE_IPC_INDUSTRIAL)
throw_things(H)
diff --git a/code/modules/tgs/LICENSE b/code/modules/tgs/LICENSE
new file mode 100644
index 00000000000..221f9e1deb2
--- /dev/null
+++ b/code/modules/tgs/LICENSE
@@ -0,0 +1,24 @@
+The MIT License
+
+Copyright (c) 2017 Jordan Brown
+
+Permission is hereby granted, free of charge,
+to any person obtaining a copy of this software and
+associated documentation files (the "Software"), to
+deal in the Software without restriction, including
+without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom
+the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/code/modules/tgs/README.md b/code/modules/tgs/README.md
new file mode 100644
index 00000000000..445cee41f57
--- /dev/null
+++ b/code/modules/tgs/README.md
@@ -0,0 +1,13 @@
+# DMAPI Internals
+
+This folder should be placed on it's own inside a codebase that wishes to use the TGS DMAPI. Warranty void if modified.
+
+- [includes.dm](./includes.dm) is the file that should be included by DM code, it handles including the rest.
+- The [core](./core) folder includes all code not directly part of any API version.
+- The other versioned folders contain code for the different DMAPI versions.
+ - [v3210](./v3210) contains the final TGS3 API.
+ - [v4](./v4) is the legacy DMAPI 4 (Used in TGS 4.0.X versions).
+ - [v5](./v5) is the current DMAPI version used by TGS4 >=4.1.
+- [LICENSE](./LICENSE) is the MIT license for the DMAPI.
+
+APIs communicate with TGS in two ways. All versions implement TGS -> DM communication using /world/Topic. DM -> TGS communication, called the bridge method, is different for each version.
diff --git a/code/modules/tgs/core/README.md b/code/modules/tgs/core/README.md
new file mode 100644
index 00000000000..aa3c7a9c9db
--- /dev/null
+++ b/code/modules/tgs/core/README.md
@@ -0,0 +1,8 @@
+# Core DMAPI functions
+
+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
diff --git a/code/modules/tgs/core/core.dm b/code/modules/tgs/core/core.dm
index d24bc2c9ae2..f75e7837af0 100644
--- a/code/modules/tgs/core/core.dm
+++ b/code/modules/tgs/core/core.dm
@@ -1,48 +1,74 @@
-/world/TgsNew(datum/tgs_event_handler/event_handler)
- var/tgs_version = world.params[TGS_VERSION_PARAMETER]
- if(!tgs_version)
+/world/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SECURITY_ULTRASAFE)
+ var/current_api = TGS_READ_GLOBAL(tgs)
+ if(current_api)
+ TGS_ERROR_LOG("API datum already set (\ref[current_api] ([current_api]))! Was TgsNew() called more than once?")
return
- var/path = SelectTgsApi(tgs_version)
- if(!path)
- TGS_ERROR_LOG("Found unsupported API version: [tgs_version]. If this is a valid version please report this, backporting is done on demand.")
+ if(!(minimum_required_security_level in list(TGS_SECURITY_ULTRASAFE, TGS_SECURITY_SAFE, TGS_SECURITY_TRUSTED)))
+ TGS_ERROR_LOG("Invalid minimum_required_security_level: [minimum_required_security_level]!")
return
- TGS_INFO_LOG("Activating API for version [tgs_version]")
- var/datum/tgs_api/new_api = new path
+#ifdef TGS_V3_API
+ if(minimum_required_security_level != TGS_SECURITY_TRUSTED)
+ TGS_WARNING_LOG("V3 DMAPI requires trusted security!")
+ minimum_required_security_level = TGS_SECURITY_TRUSTED
+#endif
+ var/raw_parameter = world.params[TGS_VERSION_PARAMETER]
+ if(!raw_parameter)
+ return
- var/result = new_api.OnWorldNew(event_handler ? event_handler : new /datum/tgs_event_handler/tgs_default)
- if(result && result != TGS_UNIMPLEMENTED)
- TGS_WRITE_GLOBAL(tgs, new_api)
- else
+ var/datum/tgs_version/version = new(raw_parameter)
+ if(!version.Valid(FALSE))
+ TGS_ERROR_LOG("Failed to validate DMAPI version parameter: [raw_parameter]!")
+ return
+
+ var/api_datum
+ switch(version.suite)
+ if(3)
+#ifndef TGS_V3_API
+ TGS_ERROR_LOG("Detected V3 API but TGS_V3_API isn't defined!")
+ return
+#else
+ switch(version.minor)
+ if(2)
+ api_datum = /datum/tgs_api/v3210
+#endif
+ if(4)
+ switch(version.minor)
+ if(0)
+ api_datum = /datum/tgs_api/v4
+ if(5)
+ api_datum = /datum/tgs_api/v5
+
+ var/datum/tgs_version/max_api_version = TgsMaximumAPIVersion();
+ if(version.suite != null && version.minor != null && version.patch != null && version.deprecated_patch != null && version.deprefixed_parameter > max_api_version.deprefixed_parameter)
+ TGS_ERROR_LOG("Detected unknown API version! Defaulting to latest. Update the DMAPI to fix this problem.")
+ api_datum = /datum/tgs_api/latest
+
+ if(!api_datum)
+ TGS_ERROR_LOG("Found unsupported API version: [raw_parameter]. If this is a valid version please report this, backporting is done on demand.")
+ return
+
+ TGS_INFO_LOG("Activating API for version [version.deprefixed_parameter]")
+
+ if(event_handler && !istype(event_handler))
+ TGS_ERROR_LOG("Invalid parameter for event_handler: [event_handler]")
+ event_handler = null
+
+ var/datum/tgs_api/new_api = new api_datum(event_handler, version)
+
+ TGS_WRITE_GLOBAL(tgs, new_api)
+
+ var/result = new_api.OnWorldNew(minimum_required_security_level)
+ if(!result || result == TGS_UNIMPLEMENTED)
+ TGS_WRITE_GLOBAL(tgs, null)
TGS_ERROR_LOG("Failed to activate API!")
-/world/proc/SelectTgsApi(tgs_version)
- //remove the old 3.0 header
- tgs_version = replacetext(tgs_version, "/tg/station 13 Server v", "")
-
- var/list/version_bits = splittext(tgs_version, ".")
-
- var/super = text2num(version_bits[1])
- var/major = text2num(version_bits[2])
- var/minor = text2num(version_bits[3])
- var/patch = text2num(version_bits[4])
-
- switch(super)
- if(3)
- switch(major)
- if(2)
- return /datum/tgs_api/v3210
-
- if(super != null && major != null && minor != null && patch != null && tgs_version > TgsMaximumAPIVersion())
- TGS_ERROR_LOG("Detected unknown API version! Defaulting to latest. Update the DMAPI to fix this problem.")
- return /datum/tgs_api/latest
-
/world/TgsMaximumAPIVersion()
- return "4.0.0.0"
+ return new /datum/tgs_version("5.x.x")
/world/TgsMinimumAPIVersion()
- return "3.2.0.0"
+ return new /datum/tgs_version("3.2.x")
/world/TgsInitializationComplete()
var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
@@ -72,7 +98,14 @@
return TGS_READ_GLOBAL(tgs) != null
/world/TgsVersion()
- return world.params[TGS_VERSION_PARAMETER]
+ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
+ if(api)
+ return api.version
+
+/world/TgsApiVersion()
+ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
+ if(api)
+ return api.ApiVersion()
/world/TgsInstanceName()
var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
@@ -117,29 +150,7 @@
if(api)
api.ChatPrivateMessage(message, user)
-/*
-The MIT License
-
-Copyright (c) 2017 Jordan Brown
-
-Permission is hereby granted, free of charge,
-to any person obtaining a copy of this software and
-associated documentation files (the "Software"), to
-deal in the Software without restriction, including
-without limitation the rights to use, copy, modify,
-merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom
-the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice
-shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
-ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
+/world/TgsSecurityLevel()
+ var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
+ if(api)
+ api.SecurityLevel()
diff --git a/code/modules/tgs/core/datum.dm b/code/modules/tgs/core/datum.dm
index 6af39a75df4..4d37ed662d1 100644
--- a/code/modules/tgs/core/datum.dm
+++ b/code/modules/tgs/core/datum.dm
@@ -1,9 +1,16 @@
TGS_DEFINE_AND_SET_GLOBAL(tgs, null)
/datum/tgs_api
+ var/datum/tgs_version/version
+ var/datum/tgs_event_handler/event_handler
+
+/datum/tgs_api/New(datum/tgs_event_handler/event_handler, datum/tgs_version/version)
+ . = ..()
+ src.event_handler = event_handler
+ src.version = version
/datum/tgs_api/latest
- parent_type = /datum/tgs_api/v3210
+ parent_type = /datum/tgs_api/v5
TGS_PROTECT_DATUM(/datum/tgs_api)
@@ -43,32 +50,8 @@ TGS_PROTECT_DATUM(/datum/tgs_api)
/datum/tgs_api/proc/ChatTargetedBroadcast(message, admin_only)
return TGS_UNIMPLEMENTED
-/datum/tgs_api/proc/ChatPrivateMessage(message, admin_only)
+/datum/tgs_api/proc/ChatPrivateMessage(message, datum/tgs_chat_user/user)
return TGS_UNIMPLEMENTED
-/*
-The MIT License
-
-Copyright (c) 2017 Jordan Brown
-
-Permission is hereby granted, free of charge,
-to any person obtaining a copy of this software and
-associated documentation files (the "Software"), to
-deal in the Software without restriction, including
-without limitation the rights to use, copy, modify,
-merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom
-the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice
-shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
-ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
+/datum/tgs_api/proc/SecurityLevel()
+ return TGS_UNIMPLEMENTED
diff --git a/code/modules/tgs/core/tgs_version.dm b/code/modules/tgs/core/tgs_version.dm
new file mode 100644
index 00000000000..a5dae1241a3
--- /dev/null
+++ b/code/modules/tgs/core/tgs_version.dm
@@ -0,0 +1,28 @@
+/datum/tgs_version/New(raw_parameter)
+ src.raw_parameter = raw_parameter
+ deprefixed_parameter = replacetext(raw_parameter, "/tg/station 13 Server v", "")
+ var/list/version_bits = splittext(deprefixed_parameter, ".")
+
+ suite = text2num(version_bits[1])
+ if(version_bits.len > 1)
+ minor = text2num(version_bits[2])
+ if(version_bits.len > 2)
+ patch = text2num(version_bits[3])
+ if(version_bits.len == 4)
+ deprecated_patch = text2num(version_bits[4])
+
+/datum/tgs_version/proc/Valid(allow_wildcards = FALSE)
+ if(suite == null)
+ return FALSE
+ if(allow_wildcards)
+ return TRUE
+ return !Wildcard()
+
+/datum/tgs_version/Wildcard()
+ return minor == null || patch == null
+
+/datum/tgs_version/Equals(datum/tgs_version/other_version)
+ if(!istype(other_version))
+ return FALSE
+
+ return suite == other_version.suite && minor == other_version.minor && patch == other_version.patch && deprecated_patch == other_version.deprecated_patch
diff --git a/code/modules/tgs/includes.dm b/code/modules/tgs/includes.dm
index 7ca906c840d..c803cf7ac26 100644
--- a/code/modules/tgs/includes.dm
+++ b/code/modules/tgs/includes.dm
@@ -1,6 +1,17 @@
#include "core\_definitions.dm"
#include "core\core.dm"
#include "core\datum.dm"
-#include "core\default_event_handler.dm"
+#include "core\tgs_version.dm"
+
+#ifdef TGS_V3_API
#include "v3210\api.dm"
#include "v3210\commands.dm"
+#endif
+
+#include "v4\api.dm"
+#include "v4\commands.dm"
+
+#include "v5\_defines.dm"
+#include "v5\api.dm"
+#include "v5\commands.dm"
+#include "v5\undef.dm"
diff --git a/code/modules/tgs/v3210/README.md b/code/modules/tgs/v3210/README.md
new file mode 100644
index 00000000000..f96e7cf3b31
--- /dev/null
+++ b/code/modules/tgs/v3210/README.md
@@ -0,0 +1,6 @@
+# DMAPI V3
+
+This DMAPI implements bridge using file output which TGS monitors for.
+
+- [api.dm](./api.dm) contains the bulk of the API code.
+- [commands.dm](./commands.dm) contains functions relating to `/datum/tgs_chat_command`s.
diff --git a/code/modules/tgs/v3210/api.dm b/code/modules/tgs/v3210/api.dm
index 23022497632..e0d00ad15d1 100644
--- a/code/modules/tgs/v3210/api.dm
+++ b/code/modules/tgs/v3210/api.dm
@@ -39,7 +39,7 @@
var/warned_custom_commands = FALSE
/datum/tgs_api/v3210/ApiVersion()
- return "3.2.1.0"
+ return new /datum/tgs_version("3.2.1.0")
/datum/tgs_api/v3210/proc/trim_left(text)
for (var/i = 1 to length(text))
@@ -56,7 +56,7 @@
/datum/tgs_api/v3210/proc/file2list(filename)
return splittext(trim_left(trim_right(file2text(filename))), "\n")
-/datum/tgs_api/v3210/OnWorldNew(datum/tgs_event_handler/event_handler) //don't use event handling in this version
+/datum/tgs_api/v3210/OnWorldNew(minimum_required_security_level)
. = FALSE
comms_key = world.params[SERVICE_WORLD_PARAM]
@@ -76,7 +76,8 @@
TGS_ERROR_LOG("This API version is only supported on Windows. Not running on Windows. Aborting initialization!")
return
ListServiceCustomCommands(TRUE)
- ExportService("[SERVICE_REQUEST_API_VERSION] [ApiVersion()]", TRUE)
+ var/datum/tgs_version/api_version = ApiVersion()
+ ExportService("[SERVICE_REQUEST_API_VERSION] [api_version.deprefixed_parameter]", TRUE)
return TRUE
//nothing to do for v3
@@ -166,11 +167,13 @@
/datum/tgs_api/v3210/Revision()
if(!warned_revison)
- TGS_ERROR_LOG("Use of TgsRevision on [ApiVersion()] origin_commit only points to master!")
+ var/datum/tgs_version/api_version = ApiVersion()
+ TGS_ERROR_LOG("Use of TgsRevision on [api_version.deprefixed_parameter] origin_commit only points to master!")
warned_revison = TRUE
var/datum/tgs_revision_information/ri = new
ri.commit = commit
ri.origin_commit = originmastercommit
+ return ri
/datum/tgs_api/v3210/EndProcess()
sleep(world.tick_lag) //flush the buffers
@@ -188,9 +191,12 @@
/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/ChatPrivateMessage(message, admin_only)
+/datum/tgs_api/v3210/ChatPrivateMessage(message, datum/tgs_chat_user/user)
return TGS_UNIMPLEMENTED
+/datum/tgs_api/v3210/SecurityLevel()
+ return TGS_SECURITY_TRUSTED
+
#undef REBOOT_MODE_NORMAL
#undef REBOOT_MODE_HARD
#undef REBOOT_MODE_SHUTDOWN
@@ -220,30 +226,3 @@
#undef SERVICE_REQUEST_API_VERSION
#undef SERVICE_RETURN_SUCCESS
-
-/*
-The MIT License
-
-Copyright (c) 2017 Jordan Brown
-
-Permission is hereby granted, free of charge,
-to any person obtaining a copy of this software and
-associated documentation files (the "Software"), to
-deal in the Software without restriction, including
-without limitation the rights to use, copy, modify,
-merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom
-the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice
-shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
-ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
diff --git a/code/modules/tgs/v3210/commands.dm b/code/modules/tgs/v3210/commands.dm
index 50466319813..a1949da444d 100644
--- a/code/modules/tgs/v3210/commands.dm
+++ b/code/modules/tgs/v3210/commands.dm
@@ -19,7 +19,7 @@
TGS_ERROR_LOG("Custom command [command_name] can't be used as it is empty or contains illegal characters!")
warned_command_names[command_name] = TRUE
continue
-
+
if(command_name_types[command_name])
if(warnings_only)
TGS_ERROR_LOG("Custom commands [command_name_types[command_name]] and [stc] have the same name, only [command_name_types[command_name]] will be available!")
@@ -44,35 +44,3 @@
user.friendly_name = sender
user.mention = sender
return stc.Run(user, params) || TRUE
-
-/*
-
-#undef SERVICE_JSON_PARAM_HELPTEXT
-#undef SERVICE_JSON_PARAM_ADMINONLY
-#undef SERVICE_JSON_PARAM_REQUIREDPARAMETERS
-
-The MIT License
-
-Copyright (c) 2017 Jordan Brown
-
-Permission is hereby granted, free of charge,
-to any person obtaining a copy of this software and
-associated documentation files (the "Software"), to
-deal in the Software without restriction, including
-without limitation the rights to use, copy, modify,
-merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom
-the Software is furnished to do so,
-subject to the following conditions:
-
-The above copyright notice and this permission notice
-shall be included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
-ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
-TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
-SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-*/
diff --git a/code/modules/tgs/v4/README.md b/code/modules/tgs/v4/README.md
new file mode 100644
index 00000000000..78191447b27
--- /dev/null
+++ b/code/modules/tgs/v4/README.md
@@ -0,0 +1,6 @@
+# DMAPI V4
+
+This DMAPI implements bridge requests using file output which TGS monitors for. It has a safe mode restriction.
+
+- [api.dm](./api.dm) contains the bulk of the API code.
+- [commands.dm](./commands.dm) contains functions relating to `/datum/tgs_chat_command`s.
diff --git a/code/modules/tgs/v4/api.dm b/code/modules/tgs/v4/api.dm
new file mode 100644
index 00000000000..5e7f3c60bed
--- /dev/null
+++ b/code/modules/tgs/v4/api.dm
@@ -0,0 +1,318 @@
+#define TGS4_PARAM_INFO_JSON "tgs_json"
+
+#define TGS4_INTEROP_ACCESS_IDENTIFIER "tgs_tok"
+
+#define TGS4_RESPONSE_SUCCESS "tgs_succ"
+
+#define TGS4_TOPIC_CHANGE_PORT "tgs_port"
+#define TGS4_TOPIC_CHANGE_REBOOT_MODE "tgs_rmode"
+#define TGS4_TOPIC_CHAT_COMMAND "tgs_chat_comm"
+#define TGS4_TOPIC_EVENT "tgs_event"
+#define TGS4_TOPIC_INTEROP_RESPONSE "tgs_interop"
+
+#define TGS4_COMM_NEW_PORT "tgs_new_port"
+#define TGS4_COMM_VALIDATE "tgs_validate"
+#define TGS4_COMM_SERVER_PRIMED "tgs_prime"
+#define TGS4_COMM_WORLD_REBOOT "tgs_reboot"
+#define TGS4_COMM_END_PROCESS "tgs_kill"
+#define TGS4_COMM_CHAT "tgs_chat_send"
+
+#define TGS4_PARAMETER_COMMAND "tgs_com"
+#define TGS4_PARAMETER_DATA "tgs_data"
+
+#define TGS4_PORT_CRITFAIL_MESSAGE " Must exit to let watchdog reboot..."
+
+#define EXPORT_TIMEOUT_DS 200
+
+/datum/tgs_api/v4
+ var/access_identifier
+ var/instance_name
+ var/json_path
+ var/chat_channels_json_path
+ var/chat_commands_json_path
+ var/server_commands_json_path
+ var/reboot_mode = TGS_REBOOT_MODE_NORMAL
+ var/security_level
+
+ var/requesting_new_port = FALSE
+
+ var/list/intercepted_message_queue
+
+ var/list/custom_commands
+
+ var/list/cached_test_merges
+ var/datum/tgs_revision_information/cached_revision
+
+ var/export_lock = FALSE
+ var/list/last_interop_response
+
+/datum/tgs_api/v4/ApiVersion()
+ return new /datum/tgs_version("4.0.0.0")
+
+/datum/tgs_api/v4/OnWorldNew(minimum_required_security_level)
+ if(minimum_required_security_level == TGS_SECURITY_ULTRASAFE)
+ TGS_WARNING_LOG("V4 DMAPI requires safe security!")
+ minimum_required_security_level = TGS_SECURITY_SAFE
+
+ json_path = world.params[TGS4_PARAM_INFO_JSON]
+ if(!json_path)
+ TGS_ERROR_LOG("Missing [TGS4_PARAM_INFO_JSON] world parameter!")
+ return
+ var/json_file = file2text(json_path)
+ if(!json_file)
+ TGS_ERROR_LOG("Missing specified json file: [json_path]")
+ return
+ var/cached_json = json_decode(json_file)
+ if(!cached_json)
+ TGS_ERROR_LOG("Failed to decode info json: [json_file]")
+ return
+
+ access_identifier = cached_json["accessIdentifier"]
+ server_commands_json_path = cached_json["serverCommandsJson"]
+
+ if(cached_json["apiValidateOnly"])
+ TGS_INFO_LOG("Validating API and exiting...")
+ Export(TGS4_COMM_VALIDATE, list(TGS4_PARAMETER_DATA = "[minimum_required_security_level]"))
+ del(world)
+
+ security_level = cached_json["securityLevel"]
+ chat_channels_json_path = cached_json["chatChannelsJson"]
+ chat_commands_json_path = cached_json["chatCommandsJson"]
+ instance_name = cached_json["instanceName"]
+
+ ListCustomCommands()
+
+ var/list/revisionData = cached_json["revision"]
+ if(revisionData)
+ cached_revision = new
+ cached_revision.commit = revisionData["commitSha"]
+ cached_revision.origin_commit = revisionData["originCommitSha"]
+
+ cached_test_merges = list()
+ var/list/json = cached_json["testMerges"]
+ for(var/entry in json)
+ var/datum/tgs_revision_information/test_merge/tm = new
+ tm.time_merged = text2num(entry["timeMerged"])
+
+ var/list/revInfo = entry["revision"]
+ if(revInfo)
+ tm.commit = revInfo["commitSha"]
+ tm.origin_commit = revInfo["originCommitSha"]
+
+ tm.title = entry["titleAtMerge"]
+ tm.body = entry["bodyAtMerge"]
+ tm.url = entry["url"]
+ tm.author = entry["author"]
+ tm.number = entry["number"]
+ tm.pull_request_commit = entry["pullRequestRevision"]
+ tm.comment = entry["comment"]
+
+ cached_test_merges += tm
+
+ return TRUE
+
+/datum/tgs_api/v4/OnInitializationComplete()
+ Export(TGS4_COMM_SERVER_PRIMED)
+
+ var/tgs4_secret_sleep_offline_sauce = 29051994
+ var/old_sleep_offline = world.sleep_offline
+ world.sleep_offline = tgs4_secret_sleep_offline_sauce
+ sleep(1)
+ if(world.sleep_offline == tgs4_secret_sleep_offline_sauce) //if not someone changed it
+ world.sleep_offline = old_sleep_offline
+ else
+ TGS_WARNING_LOG("world.sleep_offline unexpectedly changed!")
+
+/datum/tgs_api/v4/OnTopic(T)
+ var/list/params = params2list(T)
+ var/their_sCK = params[TGS4_INTEROP_ACCESS_IDENTIFIER]
+ if(!their_sCK)
+ return FALSE //continue world/Topic
+
+ if(their_sCK != access_identifier)
+ return "Invalid comms key!";
+
+ var/command = params[TGS4_PARAMETER_COMMAND]
+ if(!command)
+ return "No command!"
+
+ . = TGS4_RESPONSE_SUCCESS
+
+ switch(command)
+ if(TGS4_TOPIC_CHAT_COMMAND)
+ var/result = HandleCustomCommand(params[TGS4_PARAMETER_DATA])
+ if(result == null)
+ result = "Error running chat command!"
+ return result
+ if(TGS4_TOPIC_EVENT)
+ intercepted_message_queue = list()
+ var/list/event_notification = json_decode(params[TGS4_PARAMETER_DATA])
+ var/list/event_parameters = event_notification["Parameters"]
+
+ var/list/event_call = list(event_notification["Type"])
+ if(event_parameters)
+ event_call += event_parameters
+
+ if(event_handler != null)
+ event_handler.HandleEvent(arglist(event_call))
+
+ . = json_encode(intercepted_message_queue)
+ intercepted_message_queue = null
+ return
+ if(TGS4_TOPIC_INTEROP_RESPONSE)
+ last_interop_response = json_decode(params[TGS4_PARAMETER_DATA])
+ return
+ if(TGS4_TOPIC_CHANGE_PORT)
+ var/new_port = text2num(params[TGS4_PARAMETER_DATA])
+ if (!(new_port > 0))
+ return "Invalid port: [new_port]"
+
+ //the topic still completes, miraculously
+ //I honestly didn't believe byond could do it
+ if(event_handler != null)
+ event_handler.HandleEvent(TGS_EVENT_PORT_SWAP, new_port)
+ if(!world.OpenPort(new_port))
+ return "Port change failed!"
+ return
+ if(TGS4_TOPIC_CHANGE_REBOOT_MODE)
+ var/new_reboot_mode = text2num(params[TGS4_PARAMETER_DATA])
+ if(event_handler != null)
+ event_handler.HandleEvent(TGS_EVENT_REBOOT_MODE_CHANGE, reboot_mode, new_reboot_mode)
+ reboot_mode = new_reboot_mode
+ return
+
+ return "Unknown command: [command]"
+
+/datum/tgs_api/v4/proc/Export(command, list/data, override_requesting_new_port = FALSE)
+ if(!data)
+ data = list()
+ data[TGS4_PARAMETER_COMMAND] = command
+ var/json = json_encode(data)
+
+ while(requesting_new_port && !override_requesting_new_port)
+ sleep(1)
+
+ //we need some port open at this point to facilitate return communication
+ if(!world.port)
+ requesting_new_port = TRUE
+ if(!world.OpenPort(0)) //open any port
+ TGS_ERROR_LOG("Unable to open random port to retrieve new port![TGS4_PORT_CRITFAIL_MESSAGE]")
+ del(world)
+
+ //request a new port
+ export_lock = FALSE
+ var/list/new_port_json = Export(TGS4_COMM_NEW_PORT, list(TGS4_PARAMETER_DATA = "[world.port]"), TRUE) //stringify this on purpose
+
+ if(!new_port_json)
+ TGS_ERROR_LOG("No new port response from server![TGS4_PORT_CRITFAIL_MESSAGE]")
+ del(world)
+
+ var/new_port = new_port_json[TGS4_PARAMETER_DATA]
+ if(!isnum(new_port) || new_port <= 0)
+ TGS_ERROR_LOG("Malformed new port json ([json_encode(new_port_json)])![TGS4_PORT_CRITFAIL_MESSAGE]")
+ del(world)
+
+ if(new_port != world.port && !world.OpenPort(new_port))
+ TGS_ERROR_LOG("Unable to open port [new_port]![TGS4_PORT_CRITFAIL_MESSAGE]")
+ del(world)
+ requesting_new_port = FALSE
+
+ while(export_lock)
+ sleep(1)
+ export_lock = TRUE
+
+ last_interop_response = null
+ fdel(server_commands_json_path)
+ text2file(json, server_commands_json_path)
+
+ for(var/I = 0; I < EXPORT_TIMEOUT_DS && !last_interop_response; ++I)
+ sleep(1)
+
+ if(!last_interop_response)
+ TGS_ERROR_LOG("Failed to get export result for: [json]")
+ else
+ . = last_interop_response
+
+ export_lock = FALSE
+
+/datum/tgs_api/v4/OnReboot()
+ var/list/result = Export(TGS4_COMM_WORLD_REBOOT)
+ if(!result)
+ return
+
+ //okay so the standard TGS4 proceedure is: right before rebooting change the port to whatever was sent to us in the above json's data parameter
+
+ var/port = result[TGS4_PARAMETER_DATA]
+ if(!isnum(port))
+ return //this is valid, server may just want use to reboot
+
+ if(port == 0)
+ //to byond 0 means any port and "none" means close vOv
+ port = "none"
+
+ if(!world.OpenPort(port))
+ TGS_ERROR_LOG("Unable to set port to [port]!")
+
+/datum/tgs_api/v4/InstanceName()
+ return instance_name
+
+/datum/tgs_api/v4/TestMerges()
+ return cached_test_merges
+
+/datum/tgs_api/v4/EndProcess()
+ Export(TGS4_COMM_END_PROCESS)
+
+/datum/tgs_api/v4/Revision()
+ return cached_revision
+
+/datum/tgs_api/v4/ChatBroadcast(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)
+ if(intercepted_message_queue)
+ intercepted_message_queue += list(message)
+ else
+ Export(TGS4_COMM_CHAT, message)
+
+/datum/tgs_api/v4/ChatTargetedBroadcast(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)
+ 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))
+ if(intercepted_message_queue)
+ intercepted_message_queue += list(message)
+ else
+ Export(TGS4_COMM_CHAT, message)
+
+/datum/tgs_api/v4/ChatChannelInfo()
+ . = list()
+ //no caching cause tgs may change this
+ var/list/json = json_decode(file2text(chat_channels_json_path))
+ for(var/I in json)
+ . += DecodeChannel(I)
+
+/datum/tgs_api/v4/proc/DecodeChannel(channel_json)
+ var/datum/tgs_chat_channel/channel = new
+ channel.id = channel_json["id"]
+ channel.friendly_name = channel_json["friendlyName"]
+ channel.connection_name = channel_json["connectionName"]
+ channel.is_admin_channel = channel_json["isAdminChannel"]
+ channel.is_private_channel = channel_json["isPrivateChannel"]
+ channel.custom_tag = channel_json["tag"]
+ return channel
+
+/datum/tgs_api/v4/SecurityLevel()
+ return security_level
diff --git a/code/modules/tgs/v4/commands.dm b/code/modules/tgs/v4/commands.dm
new file mode 100644
index 00000000000..4ca1500167b
--- /dev/null
+++ b/code/modules/tgs/v4/commands.dm
@@ -0,0 +1,41 @@
+/datum/tgs_api/v4/proc/ListCustomCommands()
+ var/results = list()
+ custom_commands = list()
+ for(var/I in typesof(/datum/tgs_chat_command) - /datum/tgs_chat_command)
+ var/datum/tgs_chat_command/stc = new I
+ 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!")
+ continue
+
+ if(results[command_name])
+ var/datum/other = custom_commands[command_name]
+ 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("name" = command_name, "help_text" = stc.help_text, "admin_only" = stc.admin_only))
+ custom_commands[command_name] = stc
+
+ var/commands_file = chat_commands_json_path
+ if(!commands_file)
+ return
+ text2file(json_encode(results), commands_file)
+
+/datum/tgs_api/v4/proc/HandleCustomCommand(command_json)
+ var/list/data = json_decode(command_json)
+ var/command = data["command"]
+ var/user = data["user"]
+ var/params = data["params"]
+
+ var/datum/tgs_chat_user/u = new
+ u.id = user["id"]
+ u.friendly_name = user["friendlyName"]
+ u.mention = user["mention"]
+ u.channel = DecodeChannel(user["channel"])
+
+ var/datum/tgs_chat_command/sc = custom_commands[command]
+ if(sc)
+ var/result = sc.Run(u, params)
+ if(result == null)
+ result = ""
+ return result
+ return "Unknown command: [command]!"
diff --git a/code/modules/tgs/v5/_defines.dm b/code/modules/tgs/v5/_defines.dm
new file mode 100644
index 00000000000..8fd84e19fa3
--- /dev/null
+++ b/code/modules/tgs/v5/_defines.dm
@@ -0,0 +1,98 @@
+#define DMAPI5_PARAM_SERVER_PORT "tgs_port"
+#define DMAPI5_PARAM_ACCESS_IDENTIFIER "tgs_key"
+
+#define DMAPI5_BRIDGE_DATA "data"
+#define DMAPI5_TOPIC_DATA "tgs_data"
+
+#define DMAPI5_BRIDGE_COMMAND_PORT_UPDATE 0
+#define DMAPI5_BRIDGE_COMMAND_STARTUP 1
+#define DMAPI5_BRIDGE_COMMAND_PRIME 2
+#define DMAPI5_BRIDGE_COMMAND_REBOOT 3
+#define DMAPI5_BRIDGE_COMMAND_KILL 4
+#define DMAPI5_BRIDGE_COMMAND_CHAT_SEND 5
+
+#define DMAPI5_PARAMETER_ACCESS_IDENTIFIER "accessIdentifier"
+#define DMAPI5_PARAMETER_CUSTOM_COMMANDS "customCommands"
+
+#define DMAPI5_RESPONSE_ERROR_MESSAGE "errorMessage"
+
+#define DMAPI5_BRIDGE_PARAMETER_COMMAND_TYPE "commandType"
+#define DMAPI5_BRIDGE_PARAMETER_CURRENT_PORT "currentPort"
+#define DMAPI5_BRIDGE_PARAMETER_VERSION "version"
+#define DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE "chatMessage"
+#define DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL "minimumSecurityLevel"
+
+#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"
+#define DMAPI5_RUNTIME_INFORMATION_SERVER_VERSION "serverVersion"
+#define DMAPI5_RUNTIME_INFORMATION_SERVER_PORT "serverPort"
+#define DMAPI5_RUNTIME_INFORMATION_API_VALIDATE_ONLY "apiValidateOnly"
+#define DMAPI5_RUNTIME_INFORMATION_INSTANCE_NAME "instanceName"
+#define DMAPI5_RUNTIME_INFORMATION_REVISION "revision"
+#define DMAPI5_RUNTIME_INFORMATION_TEST_MERGES "testMerges"
+#define DMAPI5_RUNTIME_INFORMATION_SECURITY_LEVEL "securityLevel"
+
+#define DMAPI5_CHAT_UPDATE_CHANNELS "channels"
+
+#define DMAPI5_TEST_MERGE_TIME_MERGED "timeMerged"
+#define DMAPI5_TEST_MERGE_REVISION "revision"
+#define DMAPI5_TEST_MERGE_TITLE_AT_MERGE "titleAtMerge"
+#define DMAPI5_TEST_MERGE_BODY_AT_MERGE "bodyAtMerge"
+#define DMAPI5_TEST_MERGE_URL "url"
+#define DMAPI5_TEST_MERGE_AUTHOR "author"
+#define DMAPI5_TEST_MERGE_NUMBER "number"
+#define DMAPI5_TEST_MERGE_PULL_REQUEST_REVISION "pullRequestRevision"
+#define DMAPI5_TEST_MERGE_COMMENT "comment"
+
+#define DMAPI5_CHAT_COMMAND_NAME "name"
+#define DMAPI5_CHAT_COMMAND_PARAMS "params"
+#define DMAPI5_CHAT_COMMAND_USER "user"
+
+#define DMAPI5_EVENT_NOTIFICATION_TYPE "type"
+#define DMAPI5_EVENT_NOTIFICATION_PARAMETERS "parameters"
+
+#define DMAPI5_TOPIC_COMMAND_CHAT_COMMAND 0
+#define DMAPI5_TOPIC_COMMAND_EVENT_NOTIFICATION 1
+#define DMAPI5_TOPIC_COMMAND_CHANGE_PORT 2
+#define DMAPI5_TOPIC_COMMAND_CHANGE_REBOOT_STATE 3
+#define DMAPI5_TOPIC_COMMAND_INSTANCE_RENAMED 4
+#define DMAPI5_TOPIC_COMMAND_CHAT_CHANNELS_UPDATE 5
+#define DMAPI5_TOPIC_COMMAND_SERVER_PORT_UPDATE 6
+#define DMAPI5_TOPIC_COMMAND_HEARTBEAT 7
+#define DMAPI5_TOPIC_COMMAND_WATCHDOG_REATTACH 8
+
+#define DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE "commandType"
+#define DMAPI5_TOPIC_PARAMETER_CHAT_COMMAND "chatCommand"
+#define DMAPI5_TOPIC_PARAMETER_EVENT_NOTIFICATION "eventNotification"
+#define DMAPI5_TOPIC_PARAMETER_NEW_PORT "newPort"
+#define DMAPI5_TOPIC_PARAMETER_NEW_REBOOT_STATE "newRebootState"
+#define DMAPI5_TOPIC_PARAMETER_NEW_INSTANCE_NAME "newInstanceName"
+#define DMAPI5_TOPIC_PARAMETER_CHAT_UPDATE "chatUpdate"
+#define DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION "newServerVersion"
+
+#define DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE_MESSAGE "commandResponseMessage"
+#define DMAPI5_TOPIC_RESPONSE_CHAT_RESPONSES "chatResponses"
+
+#define DMAPI5_REVISION_INFORMATION_COMMIT_SHA "commitSha"
+#define DMAPI5_REVISION_INFORMATION_ORIGIN_COMMIT_SHA "originCommitSha"
+
+#define DMAPI5_CHAT_USER_ID "id"
+#define DMAPI5_CHAT_USER_FRIENDLY_NAME "friendlyName"
+#define DMAPI5_CHAT_USER_MENTION "mention"
+#define DMAPI5_CHAT_USER_CHANNEL "channel"
+
+#define DMAPI5_CHAT_CHANNEL_ID "id"
+#define DMAPI5_CHAT_CHANNEL_FRIENDLY_NAME "friendlyName"
+#define DMAPI5_CHAT_CHANNEL_CONNECTION_NAME "connectionName"
+#define DMAPI5_CHAT_CHANNEL_IS_ADMIN_CHANNEL "isAdminChannel"
+#define DMAPI5_CHAT_CHANNEL_IS_PRIVATE_CHANNEL "isPrivateChannel"
+#define DMAPI5_CHAT_CHANNEL_TAG "tag"
+
+#define DMAPI5_CUSTOM_CHAT_COMMAND_NAME "name"
+#define DMAPI5_CUSTOM_CHAT_COMMAND_HELP_TEXT "helpText"
+#define DMAPI5_CUSTOM_CHAT_COMMAND_ADMIN_ONLY "adminOnly"
diff --git a/code/modules/tgs/v5/api.dm b/code/modules/tgs/v5/api.dm
new file mode 100644
index 00000000000..3791cbde843
--- /dev/null
+++ b/code/modules/tgs/v5/api.dm
@@ -0,0 +1,357 @@
+/datum/tgs_api/v5
+ var/server_port
+ var/access_identifier
+
+ var/instance_name
+ var/security_level
+
+ var/reboot_mode = TGS_REBOOT_MODE_NORMAL
+
+ var/list/intercepted_message_queue
+
+ var/list/custom_commands
+
+ var/list/test_merges
+ var/datum/tgs_revision_information/revision
+ var/list/chat_channels
+
+/datum/tgs_api/v5/ApiVersion()
+ return new /datum/tgs_version("5.2.2")
+
+/datum/tgs_api/v5/OnWorldNew(minimum_required_security_level)
+ server_port = world.params[DMAPI5_PARAM_SERVER_PORT]
+ access_identifier = world.params[DMAPI5_PARAM_ACCESS_IDENTIFIER]
+
+ var/datum/tgs_version/api_version = ApiVersion()
+ version = null
+ var/list/bridge_response = Bridge(DMAPI5_BRIDGE_COMMAND_STARTUP, list(DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL = minimum_required_security_level, DMAPI5_BRIDGE_PARAMETER_VERSION = api_version.raw_parameter, DMAPI5_PARAMETER_CUSTOM_COMMANDS = ListCustomCommands()))
+ if(!istype(bridge_response))
+ TGS_ERROR_LOG("Failed initial bridge request!")
+ return FALSE
+
+ var/list/runtime_information = bridge_response[DMAPI5_BRIDGE_RESPONSE_RUNTIME_INFORMATION]
+ if(!istype(runtime_information))
+ TGS_ERROR_LOG("Failed to decode runtime information from bridge response: [json_encode(bridge_response)]!")
+ return FALSE
+
+ if(runtime_information[DMAPI5_RUNTIME_INFORMATION_API_VALIDATE_ONLY])
+ TGS_INFO_LOG("DMAPI validation, exiting...")
+ del(world)
+
+ version = new /datum/tgs_version(runtime_information[DMAPI5_RUNTIME_INFORMATION_SERVER_VERSION])
+ security_level = runtime_information[DMAPI5_RUNTIME_INFORMATION_SECURITY_LEVEL]
+ instance_name = runtime_information[DMAPI5_RUNTIME_INFORMATION_INSTANCE_NAME]
+
+ var/list/revisionData = runtime_information[DMAPI5_RUNTIME_INFORMATION_REVISION]
+ if(istype(revisionData))
+ revision = new
+ revision.commit = revisionData[DMAPI5_REVISION_INFORMATION_COMMIT_SHA]
+ revision.origin_commit = revisionData[DMAPI5_REVISION_INFORMATION_ORIGIN_COMMIT_SHA]
+ else
+ TGS_ERROR_LOG("Failed to decode [DMAPI5_RUNTIME_INFORMATION_REVISION] from runtime information!")
+
+ test_merges = list()
+ var/list/test_merge_json = runtime_information[DMAPI5_RUNTIME_INFORMATION_TEST_MERGES]
+ if(istype(test_merge_json))
+ for(var/entry in test_merge_json)
+ var/datum/tgs_revision_information/test_merge/tm = new
+ tm.number = entry[DMAPI5_TEST_MERGE_NUMBER]
+
+ var/list/revInfo = entry[DMAPI5_TEST_MERGE_REVISION]
+ if(revInfo)
+ tm.commit = revisionData[DMAPI5_REVISION_INFORMATION_COMMIT_SHA]
+ tm.origin_commit = revisionData[DMAPI5_REVISION_INFORMATION_ORIGIN_COMMIT_SHA]
+ else
+ TGS_WARNING_LOG("Failed to decode [DMAPI5_TEST_MERGE_REVISION] from test merge #[tm.number]!")
+
+ tm.time_merged = text2num(entry[DMAPI5_TEST_MERGE_TIME_MERGED])
+ tm.title = entry[DMAPI5_TEST_MERGE_TITLE_AT_MERGE]
+ tm.body = entry[DMAPI5_TEST_MERGE_BODY_AT_MERGE]
+ tm.url = entry[DMAPI5_TEST_MERGE_URL]
+ tm.author = entry[DMAPI5_TEST_MERGE_AUTHOR]
+ tm.pull_request_commit = entry[DMAPI5_TEST_MERGE_PULL_REQUEST_REVISION]
+ tm.comment = entry[DMAPI5_TEST_MERGE_COMMENT]
+
+ test_merges += tm
+ else
+ TGS_WARNING_LOG("Failed to decode [DMAPI5_RUNTIME_INFORMATION_TEST_MERGES] from runtime information!")
+
+ chat_channels = list()
+ DecodeChannels(runtime_information)
+
+ return TRUE
+
+/datum/tgs_api/v5/proc/RequireInitialBridgeResponse()
+ while(!version)
+ sleep(1)
+
+/datum/tgs_api/v5/OnInitializationComplete()
+ Bridge(DMAPI5_BRIDGE_COMMAND_PRIME)
+
+ var/tgs4_secret_sleep_offline_sauce = 29051994
+ var/old_sleep_offline = world.sleep_offline
+ world.sleep_offline = tgs4_secret_sleep_offline_sauce
+ sleep(1)
+ if(world.sleep_offline == tgs4_secret_sleep_offline_sauce) //if not someone changed it
+ world.sleep_offline = old_sleep_offline
+ else
+ TGS_WARNING_LOG("world.sleep_offline unexpectedly changed!")
+
+/datum/tgs_api/v5/proc/TopicResponse(error_message = null)
+ var/list/response = list()
+ response[DMAPI5_RESPONSE_ERROR_MESSAGE] = error_message
+
+ return json_encode(response)
+
+/datum/tgs_api/v5/OnTopic(T)
+ var/list/params = params2list(T)
+ var/json = params[DMAPI5_TOPIC_DATA]
+ if(!json)
+ return FALSE //continue world/Topic
+
+ var/list/topic_parameters = json_decode(json)
+ if(!topic_parameters)
+ return TopicResponse("Invalid topic parameters json!");
+
+ var/their_sCK = topic_parameters[DMAPI5_PARAMETER_ACCESS_IDENTIFIER]
+ if(their_sCK != access_identifier)
+ return TopicResponse("Failed to decode [DMAPI5_PARAMETER_ACCESS_IDENTIFIER] from: [json]!");
+
+ var/command = topic_parameters[DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE]
+ if(!isnum(command))
+ return TopicResponse("Failed to decode [DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE] from: [json]!")
+
+ switch(command)
+ if(DMAPI5_TOPIC_COMMAND_CHAT_COMMAND)
+ var/result = HandleCustomCommand(topic_parameters[DMAPI5_TOPIC_PARAMETER_CHAT_COMMAND])
+ if(!result)
+ result = TopicResponse("Error running chat command!")
+ return result
+ if(DMAPI5_TOPIC_COMMAND_EVENT_NOTIFICATION)
+ intercepted_message_queue = list()
+ var/list/event_notification = topic_parameters[DMAPI5_TOPIC_PARAMETER_EVENT_NOTIFICATION]
+ if(!istype(event_notification))
+ return TopicResponse("Invalid [DMAPI5_TOPIC_PARAMETER_EVENT_NOTIFICATION]!")
+
+ var/event_type = event_notification[DMAPI5_EVENT_NOTIFICATION_TYPE]
+ if(!isnum(event_type))
+ return TopicResponse("Invalid or missing [DMAPI5_EVENT_NOTIFICATION_TYPE]!")
+
+ var/list/event_parameters = event_notification[DMAPI5_EVENT_NOTIFICATION_PARAMETERS]
+ if(event_parameters && !istype(event_parameters))
+ return TopicResponse("Invalid or missing [DMAPI5_EVENT_NOTIFICATION_PARAMETERS]!")
+
+ var/list/event_call = list(event_type)
+ if(event_parameters)
+ event_call += event_parameters
+
+ if(event_handler != null)
+ event_handler.HandleEvent(arglist(event_call))
+
+ var/list/response = list()
+ response[DMAPI5_TOPIC_RESPONSE_CHAT_RESPONSES] = intercepted_message_queue
+ intercepted_message_queue = null
+ return json_encode(response)
+ if(DMAPI5_TOPIC_COMMAND_CHANGE_PORT)
+ var/new_port = topic_parameters[DMAPI5_TOPIC_PARAMETER_NEW_PORT]
+ if (!isnum(new_port) || !(new_port > 0))
+ return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_PORT]]")
+
+ if(event_handler != null)
+ event_handler.HandleEvent(TGS_EVENT_PORT_SWAP, new_port)
+
+ //the topic still completes, miraculously
+ //I honestly didn't believe byond could do it without exploding
+ if(!world.OpenPort(new_port))
+ return TopicResponse("Port change failed!")
+
+ return TopicResponse()
+ if(DMAPI5_TOPIC_COMMAND_CHANGE_REBOOT_STATE)
+ var/new_reboot_mode = topic_parameters[DMAPI5_TOPIC_PARAMETER_NEW_REBOOT_STATE]
+ if(!isnum(new_reboot_mode))
+ return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_REBOOT_STATE]!")
+
+ if(event_handler != null)
+ event_handler.HandleEvent(TGS_EVENT_REBOOT_MODE_CHANGE, reboot_mode, new_reboot_mode)
+
+ reboot_mode = new_reboot_mode
+ return TopicResponse()
+ if(DMAPI5_TOPIC_COMMAND_INSTANCE_RENAMED)
+ var/new_instance_name = topic_parameters[DMAPI5_TOPIC_PARAMETER_NEW_INSTANCE_NAME]
+ if(!istext(new_instance_name))
+ return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_INSTANCE_NAME]!")
+
+ if(event_handler != null)
+ event_handler.HandleEvent(TGS_EVENT_INSTANCE_RENAMED, new_instance_name)
+
+ instance_name = new_instance_name
+ return TopicResponse()
+ if(DMAPI5_TOPIC_COMMAND_CHAT_CHANNELS_UPDATE)
+ var/list/chat_update_json = topic_parameters[DMAPI5_TOPIC_PARAMETER_CHAT_UPDATE]
+ if(!istype(chat_update_json))
+ return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_CHAT_UPDATE]!")
+
+ DecodeChannels(chat_update_json)
+ return TopicResponse()
+ if(DMAPI5_TOPIC_COMMAND_SERVER_PORT_UPDATE)
+ var/new_port = topic_parameters[DMAPI5_TOPIC_PARAMETER_NEW_PORT]
+ if (!isnum(new_port) || !(new_port > 0))
+ return TopicResponse("Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_PORT]]")
+
+ server_port = new_port
+ return TopicResponse()
+ if(DMAPI5_TOPIC_COMMAND_HEARTBEAT)
+ return TopicResponse()
+ if(DMAPI5_TOPIC_COMMAND_WATCHDOG_REATTACH)
+ var/new_port = topic_parameters[DMAPI5_TOPIC_PARAMETER_NEW_PORT]
+ var/error_message = null
+ if (new_port != null)
+ if (!isnum(new_port) || !(new_port > 0))
+ error_message = "Invalid [DMAPI5_TOPIC_PARAMETER_NEW_PORT]]"
+ else
+ server_port = new_port
+
+ var/new_version_string = topic_parameters[DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION]
+ if (!istext(new_version_string))
+ if(error_message != null)
+ error_message += ", "
+ error_message += "Invalid or missing [DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION]]"
+ else
+ var/datum/tgs_version/new_version = new(new_version_string)
+ if (event_handler)
+ event_handler.HandleEvent(TGS_EVENT_WATCHDOG_REATTACH, new_version)
+
+ version = new_version
+
+ return json_encode(list(DMAPI5_RESPONSE_ERROR_MESSAGE = error_message, DMAPI5_PARAMETER_CUSTOM_COMMANDS = ListCustomCommands()))
+
+ return TopicResponse("Unknown command: [command]")
+
+/datum/tgs_api/v5/proc/Bridge(command, list/data)
+ if(!data)
+ data = list()
+
+ data[DMAPI5_BRIDGE_PARAMETER_COMMAND_TYPE] = command
+ data[DMAPI5_PARAMETER_ACCESS_IDENTIFIER] = access_identifier
+
+ var/json = json_encode(data)
+ var/encoded_json = url_encode(json)
+
+ // This is an infinite sleep until we get a response
+ var/export_response = world.Export("http://127.0.0.1:[server_port]/Bridge?[DMAPI5_BRIDGE_DATA]=[encoded_json]")
+ if(!export_response)
+ TGS_ERROR_LOG("Failed export request: [json]")
+ return
+
+ var/response_json = file2text(export_response["CONTENT"])
+ if(!response_json)
+ TGS_ERROR_LOG("Failed export request, missing content!")
+ return
+
+ var/list/bridge_response = json_decode(response_json)
+ if(!bridge_response)
+ TGS_ERROR_LOG("Failed export request, bad json: [response_json]")
+ return
+
+ var/error = bridge_response[DMAPI5_RESPONSE_ERROR_MESSAGE]
+ if(error)
+ TGS_ERROR_LOG("Failed export request, bad request: [error]")
+ return
+
+ return bridge_response
+
+/datum/tgs_api/v5/OnReboot()
+ var/list/result = Bridge(DMAPI5_BRIDGE_COMMAND_REBOOT)
+ if(!result)
+ return
+
+ //okay so the standard TGS4 proceedure is: right before rebooting change the port to whatever was sent to us in the above json's data parameter
+
+ var/port = result[DMAPI5_BRIDGE_RESPONSE_NEW_PORT]
+ if(!isnum(port))
+ return //this is valid, server may just want use to reboot
+
+ if(port == 0)
+ //to byond 0 means any port and "none" means close vOv
+ port = "none"
+
+ if(!world.OpenPort(port))
+ TGS_ERROR_LOG("Unable to set port to [port]!")
+
+/datum/tgs_api/v5/InstanceName()
+ RequireInitialBridgeResponse()
+ return instance_name
+
+/datum/tgs_api/v5/TestMerges()
+ RequireInitialBridgeResponse()
+ return test_merges
+
+/datum/tgs_api/v5/EndProcess()
+ Bridge(DMAPI5_BRIDGE_COMMAND_KILL)
+
+/datum/tgs_api/v5/Revision()
+ RequireInitialBridgeResponse()
+ return revision
+
+/datum/tgs_api/v5/ChatBroadcast(message, list/channels)
+ if(!length(channels))
+ channels = ChatChannelInfo()
+
+ var/list/ids = list()
+ for(var/I in channels)
+ var/datum/tgs_chat_channel/channel = I
+ ids += channel.id
+
+ message = list(DMAPI5_CHAT_MESSAGE_TEXT = 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)
+ 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)
+ 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))
+ 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/ChatChannelInfo()
+ RequireInitialBridgeResponse()
+ return chat_channels
+
+/datum/tgs_api/v5/proc/DecodeChannels(chat_update_json)
+ var/list/chat_channels_json = chat_update_json[DMAPI5_CHAT_UPDATE_CHANNELS]
+ if(istype(chat_channels_json))
+ chat_channels.Cut()
+ for(var/channel_json in chat_channels_json)
+ var/datum/tgs_chat_channel/channel = DecodeChannel(channel_json)
+ if(channel)
+ chat_channels += channel
+ else
+ TGS_WARNING_LOG("Failed to decode [DMAPI5_CHAT_UPDATE_CHANNELS] from channel update!")
+
+/datum/tgs_api/v5/proc/DecodeChannel(channel_json)
+ var/datum/tgs_chat_channel/channel = new
+ channel.id = channel_json[DMAPI5_CHAT_CHANNEL_ID]
+ channel.friendly_name = channel_json[DMAPI5_CHAT_CHANNEL_FRIENDLY_NAME]
+ channel.connection_name = channel_json[DMAPI5_CHAT_CHANNEL_CONNECTION_NAME]
+ 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]
+ return channel
+
+/datum/tgs_api/v5/SecurityLevel()
+ RequireInitialBridgeResponse()
+ return security_level
diff --git a/code/modules/tgs/v5/commands.dm b/code/modules/tgs/v5/commands.dm
new file mode 100644
index 00000000000..6d31dd3422d
--- /dev/null
+++ b/code/modules/tgs/v5/commands.dm
@@ -0,0 +1,40 @@
+/datum/tgs_api/v5/proc/ListCustomCommands()
+ var/results = list()
+ custom_commands = list()
+ for(var/I in typesof(/datum/tgs_chat_command) - /datum/tgs_chat_command)
+ var/datum/tgs_chat_command/stc = new I
+ 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!")
+ 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!")
+ 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
+
+ return results
+
+/datum/tgs_api/v5/proc/HandleCustomCommand(list/command_json)
+ var/command = command_json[DMAPI5_CHAT_COMMAND_NAME]
+ var/user = command_json[DMAPI5_CHAT_COMMAND_USER]
+ var/params = command_json[DMAPI5_CHAT_COMMAND_PARAMS]
+
+ var/datum/tgs_chat_user/u = new
+ u.id = user[DMAPI5_CHAT_USER_ID]
+ u.friendly_name = user[DMAPI5_CHAT_USER_FRIENDLY_NAME]
+ u.mention = user[DMAPI5_CHAT_USER_MENTION]
+ u.channel = DecodeChannel(user[DMAPI5_CHAT_USER_CHANNEL])
+
+ var/datum/tgs_chat_command/sc = custom_commands[command]
+ if(sc)
+ var/text_response = sc.Run(u, params)
+ 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
+ return json_encode(topic_response)
+ return TopicResponse("Unknown custom chat command: [command]!")
diff --git a/code/modules/tgs/v5/undef.dm b/code/modules/tgs/v5/undef.dm
new file mode 100644
index 00000000000..34fc481d36e
--- /dev/null
+++ b/code/modules/tgs/v5/undef.dm
@@ -0,0 +1,98 @@
+#undef DMAPI5_PARAM_SERVER_PORT
+#undef DMAPI5_PARAM_ACCESS_IDENTIFIER
+
+#undef DMAPI5_BRIDGE_DATA
+#undef DMAPI5_TOPIC_DATA
+
+#undef DMAPI5_BRIDGE_COMMAND_PORT_UPDATE
+#undef DMAPI5_BRIDGE_COMMAND_STARTUP
+#undef DMAPI5_BRIDGE_COMMAND_PRIME
+#undef DMAPI5_BRIDGE_COMMAND_REBOOT
+#undef DMAPI5_BRIDGE_COMMAND_KILL
+#undef DMAPI5_BRIDGE_COMMAND_CHAT_SEND
+
+#undef DMAPI5_PARAMETER_ACCESS_IDENTIFIER
+#undef DMAPI5_PARAMETER_CUSTOM_COMMANDS
+
+#undef DMAPI5_RESPONSE_ERROR_MESSAGE
+
+#undef DMAPI5_BRIDGE_PARAMETER_COMMAND_TYPE
+#undef DMAPI5_BRIDGE_PARAMETER_CURRENT_PORT
+#undef DMAPI5_BRIDGE_PARAMETER_VERSION
+#undef DMAPI5_BRIDGE_PARAMETER_CHAT_MESSAGE
+#undef DMAPI5_BRIDGE_PARAMETER_MINIMUM_SECURITY_LEVEL
+
+#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
+#undef DMAPI5_RUNTIME_INFORMATION_SERVER_VERSION
+#undef DMAPI5_RUNTIME_INFORMATION_SERVER_PORT
+#undef DMAPI5_RUNTIME_INFORMATION_API_VALIDATE_ONLY
+#undef DMAPI5_RUNTIME_INFORMATION_INSTANCE_NAME
+#undef DMAPI5_RUNTIME_INFORMATION_REVISION
+#undef DMAPI5_RUNTIME_INFORMATION_TEST_MERGES
+#undef DMAPI5_RUNTIME_INFORMATION_SECURITY_LEVEL
+
+#undef DMAPI5_CHAT_UPDATE_CHANNELS
+
+#undef DMAPI5_TEST_MERGE_TIME_MERGED
+#undef DMAPI5_TEST_MERGE_REVISION
+#undef DMAPI5_TEST_MERGE_TITLE_AT_MERGE
+#undef DMAPI5_TEST_MERGE_BODY_AT_MERGE
+#undef DMAPI5_TEST_MERGE_URL
+#undef DMAPI5_TEST_MERGE_AUTHOR
+#undef DMAPI5_TEST_MERGE_NUMBER
+#undef DMAPI5_TEST_MERGE_PULL_REQUEST_REVISION
+#undef DMAPI5_TEST_MERGE_COMMENT
+
+#undef DMAPI5_CHAT_COMMAND_NAME
+#undef DMAPI5_CHAT_COMMAND_PARAMS
+#undef DMAPI5_CHAT_COMMAND_USER
+
+#undef DMAPI5_EVENT_NOTIFICATION_TYPE
+#undef DMAPI5_EVENT_NOTIFICATION_PARAMETERS
+
+#undef DMAPI5_TOPIC_COMMAND_CHAT_COMMAND
+#undef DMAPI5_TOPIC_COMMAND_EVENT_NOTIFICATION
+#undef DMAPI5_TOPIC_COMMAND_CHANGE_PORT
+#undef DMAPI5_TOPIC_COMMAND_CHANGE_REBOOT_STATE
+#undef DMAPI5_TOPIC_COMMAND_INSTANCE_RENAMED
+#undef DMAPI5_TOPIC_COMMAND_CHAT_CHANNELS_UPDATE
+#undef DMAPI5_TOPIC_COMMAND_SERVER_PORT_UPDATE
+#undef DMAPI5_TOPIC_COMMAND_HEARTBEAT
+#undef DMAPI5_TOPIC_COMMAND_WATCHDOG_REATTACH
+
+#undef DMAPI5_TOPIC_PARAMETER_COMMAND_TYPE
+#undef DMAPI5_TOPIC_PARAMETER_CHAT_COMMAND
+#undef DMAPI5_TOPIC_PARAMETER_EVENT_NOTIFICATION
+#undef DMAPI5_TOPIC_PARAMETER_NEW_PORT
+#undef DMAPI5_TOPIC_PARAMETER_NEW_REBOOT_STATE
+#undef DMAPI5_TOPIC_PARAMETER_NEW_INSTANCE_NAME
+#undef DMAPI5_TOPIC_PARAMETER_CHAT_UPDATE
+#undef DMAPI5_TOPIC_PARAMETER_NEW_SERVER_VERSION
+
+#undef DMAPI5_TOPIC_RESPONSE_COMMAND_RESPONSE_MESSAGE
+#undef DMAPI5_TOPIC_RESPONSE_CHAT_RESPONSES
+
+#undef DMAPI5_REVISION_INFORMATION_COMMIT_SHA
+#undef DMAPI5_REVISION_INFORMATION_ORIGIN_COMMIT_SHA
+
+#undef DMAPI5_CHAT_USER_ID
+#undef DMAPI5_CHAT_USER_FRIENDLY_NAME
+#undef DMAPI5_CHAT_USER_MENTION
+#undef DMAPI5_CHAT_USER_CHANNEL
+
+#undef DMAPI5_CHAT_CHANNEL_ID
+#undef DMAPI5_CHAT_CHANNEL_FRIENDLY_NAME
+#undef DMAPI5_CHAT_CHANNEL_CONNECTION_NAME
+#undef DMAPI5_CHAT_CHANNEL_IS_ADMIN_CHANNEL
+#undef DMAPI5_CHAT_CHANNEL_IS_PRIVATE_CHANNEL
+#undef DMAPI5_CHAT_CHANNEL_TAG
+
+#undef DMAPI5_CUSTOM_CHAT_COMMAND_NAME
+#undef DMAPI5_CUSTOM_CHAT_COMMAND_HELP_TEXT
+#undef DMAPI5_CUSTOM_CHAT_COMMAND_ADMIN_ONLY
diff --git a/code/modules/vehicles/cargo_train.dm b/code/modules/vehicles/cargo_train.dm
index d924176db1a..9f21f0c91cf 100644
--- a/code/modules/vehicles/cargo_train.dm
+++ b/code/modules/vehicles/cargo_train.dm
@@ -12,6 +12,8 @@
load_offset_x = 0
mob_offset_y = 7
+ var/vueui_template = "trainengine"
+
var/car_limit = 3 //how many cars an engine can pull before performance degrades
active_engines = 1
var/obj/item/key/cargo_train/key
@@ -47,6 +49,59 @@
add_overlay(I)
turn_off() //so engine verbs are correctly set
+/obj/vehicle/train/cargo/engine/attack_hand(mob/user)
+ if(use_check_and_message(user))
+ return
+ if(!load || user == load) // no driver, or the user is the driver
+ ui_interact(user)
+ return
+ ..()
+
+/obj/vehicle/train/cargo/engine/ui_interact(mob/user)
+ var/datum/vueui/ui = SSvueui.get_open_ui(user, src)
+
+ if(!ui)
+ ui = new(user, src, "vehicles-[vueui_template]", 600, 400, capitalize_first_letters(initial(name)))
+ ui.auto_update_content = TRUE
+
+ ui.open()
+
+/obj/vehicle/train/cargo/engine/vueui_data_change(list/data, mob/user, datum/vueui/ui)
+ if(!length(data))
+ data = list()
+
+ data["is_on"] = on
+ data["has_key"] = !!key
+ data["has_cell"] = !!cell
+ if(cell)
+ data["cell_charge"] = cell.charge
+ data["cell_max_charge"] = cell.maxcharge
+ data["is_towing"] = !!tow
+ if(tow)
+ data["tow"] = tow.name
+
+ return data
+
+/obj/vehicle/train/cargo/engine/Topic(href, href_list, state)
+ . = ..()
+ if(.)
+ return TRUE
+
+ if(load && usr != load)
+ to_chat(usr, SPAN_WARNING("You can't interact with \the [src] unless you're the driver, or you're adjacent to it while it has no driver."))
+ return TRUE
+
+ if(href_list["toggle_engine"])
+ if(!on)
+ start_engine()
+ else
+ stop_engine()
+ if(href_list["key"])
+ do_remove_key(usr)
+ if(href_list["unlatch"])
+ tow.unattach(usr)
+ SSvueui.check_uis_for_change(src)
+
/obj/vehicle/train/cargo/engine/Move(var/turf/destination)
if(on && cell.charge < charge_use)
turn_off()
@@ -73,9 +128,12 @@
/obj/vehicle/train/cargo/engine/attackby(obj/item/W as obj, mob/user as mob)
if(istype(W, /obj/item/key/cargo_train))
if(!key)
- user.drop_from_inventory(W,src)
+ user.drop_from_inventory(W, src)
key = W
+ to_chat(user, SPAN_NOTICE("You slide the key into the ignition."))
verbs += /obj/vehicle/train/cargo/engine/verb/remove_key
+ else
+ to_chat(user, SPAN_WARNING("\The [src] already has a key inserted."))
return
..()
@@ -122,6 +180,7 @@
//-------------------------------------------
/obj/vehicle/train/cargo/engine/turn_on()
if(!key)
+ audible_message("\The [src] whirrs, but the lack of a key causes it to shut down.")
return
else
..()
@@ -220,7 +279,7 @@
set category = "Vehicle"
set src in view(0)
- if(!istype(usr, /mob/living/carbon/human))
+ if(!ishuman(usr))
return
if(on)
@@ -228,13 +287,10 @@
return
turn_on()
- if (on)
+ if(on)
to_chat(usr, "You start [src]'s engine.")
- else
- if(cell.charge < charge_use)
- to_chat(usr, "[src] is out of power.")
- else
- to_chat(usr, "[src]'s engine won't start.")
+ else if(cell.charge < charge_use)
+ to_chat(usr, "[src] is out of power.")
/obj/vehicle/train/cargo/engine/verb/stop_engine()
set name = "Stop engine"
@@ -260,19 +316,28 @@
if(!istype(usr, /mob/living/carbon/human))
return
- if(!key || (load && load != usr))
+ if(!key)
+ to_chat(usr, SPAN_WARNING("\The [src] doesn't have a key inserted!"))
+ return
+ if(load && load != usr)
return
+ do_remove_key(usr)
+
+/obj/vehicle/train/cargo/engine/proc/do_remove_key(var/mob/user)
if(on)
turn_off()
- key.loc = usr.loc
- if(!usr.get_active_hand())
- usr.put_in_hands(key)
+ user.put_in_hands(key)
key = null
verbs -= /obj/vehicle/train/cargo/engine/verb/remove_key
+/obj/vehicle/train/cargo/engine/emag_act(var/remaining_charges, mob/user)
+ . = ..()
+ if(.)
+ update_car(train_length, active_engines)
+
//-------------------------------------------
// Loading/unloading procs
//-------------------------------------------
@@ -376,6 +441,8 @@
move_delay *= (1 / max(1, active_engines)) * 2 //overweight penalty (scaled by the number of engines)
move_delay += config.walk_speed //base reference speed
move_delay *= config.vehicle_delay_multiplier //makes cargo trains 10% slower than running when not overweight
+ if(emagged)
+ move_delay -= 2
/obj/vehicle/train/cargo/trolley/update_car(var/train_length, var/active_engines)
src.train_length = train_length
diff --git a/code/modules/vehicles/pussywagon.dm b/code/modules/vehicles/pussywagon.dm
index 58db1c1d5f5..daca0d8c3da 100644
--- a/code/modules/vehicles/pussywagon.dm
+++ b/code/modules/vehicles/pussywagon.dm
@@ -7,6 +7,7 @@
mob_offset_y = 7
load_offset_x = -13
+ vueui_template = "pussywagon"
var/cart_icon = "janicart"
@@ -21,20 +22,59 @@
. = ..()
cell = new /obj/item/cell/high(src)
key = null
+ verbs -= /obj/vehicle/train/cargo/engine/verb/remove_key
update_icon()
turn_off() //so engine verbs are correctly set
+/obj/vehicle/train/cargo/engine/pussywagon/vueui_data_change(list/data, mob/user, datum/vueui/ui)
+ data = ..()
+
+ data["has_proper_trolley"] = FALSE
+ if(istype(tow, /obj/vehicle/train/cargo/trolley/pussywagon))
+ data["has_proper_trolley"] = TRUE
+ var/obj/vehicle/train/cargo/trolley/pussywagon/PT = tow
+ data["is_hoovering"] = PT.hoover
+ data["vacuum_capacity"] = PT.vacuum_capacity
+ data["max_vacuum_capacity"] = PT.max_vacuum_capacity
+ data["is_mopping"] = PT.mopping
+ data["has_bucket"] = !!PT.bucket
+ data["bucket_capacity"] = 0
+ if(PT.bucket)
+ var/obj/item/reagent_containers/B = PT.bucket
+ data["bucket_capacity"] = B.reagents.total_volume
+ data["max_bucket_capacity"] = B.volume
+
+ return data
+
+/obj/vehicle/train/cargo/engine/pussywagon/Topic(href, href_list, datum/topic_state/state)
+ . = ..()
+ if(.)
+ return
+
+ if(href_list["toggle_hoover"])
+ toggle_hoover()
+ if(href_list["empty_hoover"])
+ var/obj/vehicle/train/cargo/trolley/pussywagon/PT = tow
+ PT.empty_hoover(usr)
+ if(href_list["toggle_mops"])
+ toggle_mop()
+ SSvueui.check_uis_for_change(src)
+
/obj/vehicle/train/cargo/engine/pussywagon/attackby(obj/item/W as obj, mob/user as mob)
if(istype(W, /obj/item/key/janicart))
if(!key)
- user.drop_from_inventory(W,src)
+ user.drop_from_inventory(W, src)
key = W
+ to_chat(user, SPAN_NOTICE("You slide the key into the ignition."))
verbs += /obj/vehicle/train/cargo/engine/verb/remove_key
+ else
+ to_chat(user, SPAN_WARNING("\The [src] already has a key inserted."))
return
..()
/obj/vehicle/train/cargo/engine/pussywagon/turn_on()
if(!key)
+ audible_message("\The [src] whirrs, but the lack of a key causes the engine to shut down.")
return
else
..()
@@ -47,12 +87,10 @@
..()
if(tow)
- if(istype(tow,/obj/vehicle/train/cargo/trolley/pussywagon))
+ if(istype(tow, /obj/vehicle/train/cargo/trolley/pussywagon))
var/obj/vehicle/train/cargo/trolley/pussywagon/PW = tow
- if(PW.mopping)
- PW.mop_toggle()
- if(PW.hoover)
- PW.hoover_toggle()
+ PW.engine_off()
+
verbs -= /obj/vehicle/train/cargo/engine/pussywagon/verb/toggle_mop
verbs -= /obj/vehicle/train/cargo/engine/pussywagon/verb/toggle_hoover
@@ -68,7 +106,7 @@
if(istype(tow,/obj/vehicle/train/cargo/trolley/pussywagon))
var/obj/vehicle/train/cargo/trolley/pussywagon/PW = tow
if(!PW.bucket)
- to_chat(usr, "You must insert a reagent container first!")
+ to_chat(usr, SPAN_WARNING("You must insert a reagent container first!"))
return
PW.mop_toggle()
@@ -128,36 +166,51 @@
var/obj/item/reagent_containers/bucket
var/list/hoovered = list()
+ var/max_vacuum_capacity = 125
var/vacuum_capacity = 125
var/mopping = 0
var/hoover = 0
-/obj/vehicle/train/cargo/trolley/pussywagon/attackby(obj/item/W as obj, mob/user as mob)
- if(istype(W, /obj/item/reagent_containers) && open)
+/obj/vehicle/train/cargo/trolley/pussywagon/attackby(obj/item/W, mob/user)
+ if(istype(W, /obj/item/reagent_containers))
+ if(!open)
+ to_chat(user, SPAN_WARNING("\The [src]'s panel isn't open."))
+ return
if(!bucket)
user.drop_from_inventory(W,src)
bucket = W
- to_chat(user, "You replace \the [src]'s reagent reservoir.")
+ to_chat(user, SPAN_NOTICE("You replace \the [src]'s reagent reservoir."))
return
- if(W.iswrench() && open)
+ if(W.iswrench())
+ if(!open)
+ to_chat(user, SPAN_WARNING("\The [src]'s panel isn't open."))
+ return
if(bucket)
bucket.forceMove(user.loc)
bucket = null
- to_chat(user, "You remove \the [src]'s reagent reservoir.")
+ to_chat(user, SPAN_NOTICE("You remove \the [src]'s reagent reservoir."))
+ return
+ else
+ to_chat(user, SPAN_WARNING("\The [src] doesn't have a reagent reservoir installed."))
return
- if(W.iscrowbar() && !open)
- if(bucket)
- for(var/obj/item/I in hoovered)
- I.forceMove(user.loc)
- hoovered -= I
- vacuum_capacity = 125
- to_chat(user, "You empty \the [src]'s vacuum cleaner.")
+ if(W.iscrowbar())
+ if(!open)
+ to_chat(user, SPAN_WARNING("\The [src]'s panel isn't open."))
return
+ empty_hoover(user)
+ to_chat(user, SPAN_NOTICE("You empty \the [src]'s vacuum cleaner."))
+ return
..()
+/obj/vehicle/train/cargo/trolley/pussywagon/proc/empty_hoover(var/mob/user)
+ for(var/obj/item/I in hoovered)
+ I.forceMove(user.loc)
+ hoovered -= I
+ vacuum_capacity = max_vacuum_capacity
+
/obj/vehicle/train/cargo/trolley/pussywagon/Move(var/turf/destination)
if(lead)
var/turf/tile = loc
@@ -182,25 +235,30 @@
playsound(src, 'sound/machines/disposalflush.ogg', 100, 1)
return ..()
+/obj/vehicle/train/cargo/trolley/pussywagon/proc/engine_off()
+ if(mopping)
+ mop_toggle()
+ if(hoover)
+ hoover_toggle()
+
/obj/vehicle/train/cargo/trolley/pussywagon/proc/mop_toggle()
if(!mopping)
mopping = 1
- src.visible_message("\The [src]'s mop-o-matic rumbles to life.", "You hear something rumble deeply.")
+ audible_message("\The [src]'s mop-o-matic rumbles to life.", "You hear something rumble deeply.")
playsound(src, 'sound/machines/hydraulic_long.ogg', 100, 1)
else
mopping = 0
- src.visible_message("\The [src]'s mop-o-matic putters before turning off.", "You hear something putter slowly.")
+ audible_message("\The [src]'s mop-o-matic putters before turning off.", "You hear something putter slowly.")
update_icon()
-
/obj/vehicle/train/cargo/trolley/pussywagon/proc/hoover_toggle()
if(!hoover)
hoover = 1
- src.visible_message("\The [src]'s space hoover rumbles to life.", "You hear something rumble deeply.")
+ audible_message("\The [src]'s space hoover rumbles to life.", "You hear something rumble deeply.")
playsound(src, 'sound/machines/hydraulic_long.ogg', 100, 1)
else
hoover = 0
- src.visible_message("\The [src]'s space hoover putters before turning off.", "You hear something putter slowly.")
+ audible_message("\The [src]'s space hoover putters before turning off.", "You hear something putter slowly.")
update_icon()
/obj/vehicle/train/cargo/trolley/pussywagon/update_icon()
diff --git a/code/modules/vehicles/train.dm b/code/modules/vehicles/train.dm
index 63d1a30d202..e353d9f5e7e 100644
--- a/code/modules/vehicles/train.dm
+++ b/code/modules/vehicles/train.dm
@@ -118,10 +118,6 @@
user.forceMove(loc) //for handling players stuck in src
else if(load)
unload(user) //unload if loaded
- else if(!load && !user.buckled)
- load(user) //else try climbing on board
- else
- return 0
/obj/vehicle/train/verb/unlatch_v()
set name = "Unlatch"
diff --git a/html/changelog.html b/html/changelog.html
index 0e912f8ab06..d853a0b9651 100644
--- a/html/changelog.html
+++ b/html/changelog.html
@@ -35,6 +35,250 @@
-->
+
24 December 2020
+
Alberyk updated:
+
+
Removed antag drafting. People that voted for the round won't be selected for antag if they don't have their options enabled.
+
+
Geeves updated:
+
+
Added an extension to skrell eyes for Axiori under the body markings menu.
+
+
+
23 December 2020
+
Alberyk updated:
+
+
Added craftable torches.
+
+
Ferner updated:
+
+
You can now also examine a person's accessories individually when you examine someone.
+
+
Geeves updated:
+
+
Cleaned up the antimatter engine's backend.
+
Simplified the antimatter engine's power production formula. One unit of injected fuel now equals a megawatt of power produced.
+
Fixes antimatter reactor shields sometimes having an invisible sprite.
+
+
+
22 December 2020
+
Geeves updated:
+
+
HUD icons are no longer coloured if you're blasted by paint, or are slightly transparent.
+
Fixed the wonkiness of floating chat when transformations are applied on something.
+
Converted how Vampire and Changeling variables are handled on the backend, let me know if there's something funky.
+
Cultists can now memorize up to three runes, and can scribe them at will without a tome, at the cost of taking three times longer to do so. Check the Cultists tab in the top right. Only specific runes can be memorized.
+
You can now open and close emergency wall lockers like you would any other locker / closet.
+
Emergency wall lockers now have a chance to spawn with emergency auto-injectors. These contain a small amount of dexalin and inaprovaline.
+
Computer ringtones no longer amplify according to the amount of mobs around you.
+
Refactored sculpting block code, it should work a bit nicer now.
+
You can now turn sculpting blocks into ladders, but only upwards. This ladder can be knocked out with a pickaxe or a plasma cutter.
+
Anchoring or unanchoring a sculpting block now takes 5 seconds.
+
Shrapnel now does damage when you move, again. This only occurs if you're running.
+
When vampires frenzy, their eyes will start glowing bright red.
+
Going hulk now really never turns someone green.
+
Mining drone and stationbound drill maintenance grippers can now pick up warp extraction gear, pack and core. They can also be used to remove the core from an ore box.
+
Gripping an item with a gripper now plays a pick-up animation.
+
Robotocists have gained medical records on their PDAs.
+
Roboticists no longer have a chemistry codex, scientists no longer have an AI maintenance utility.
+
Skrell in Srom now get a health indicator that reflects that of their body, which gives them a rough idea of which state they're in.
+
Health analyzers now report whether the limbs have been bandaged or salved as well.
+
Fixed mining satchels not being able to put ore in ore boxes.
+
+
Wowzewow (Wezzy) updated:
+
+
New lightswitch and door control sprites.
+
Adds bandage overlays.
+
+
+
20 December 2020
+
Ferner updated:
+
+
Fixed an issue which caused the rogue maint drone event to spawn all the drones in the same area.
+
Fixed the energy rifle and some other wieldable guns occasionally going invisible.
+
+
Geeves updated:
+
+
Added a colour based fire system to the health indicator, which displays just how on fire you are. Green-ish means you're only a bit on fire, while red-purplish means even firesuits won't protect you anymore.
+
+
+
19 December 2020
+
Doxxmedearly updated:
+
+
Folded wheelchairs now properly count for cargo bounties.
+
+
Geeves updated:
+
+
Added a new event where hostile (non-player) drones spawn in maintenance.
+
Items that occupy both ear slots, such as earmuffs or headphones, have had their double-ear code improved, making it less likely to bug out and keep one fake item on the ear.
+
Earmuffs no longer make you unable to hear things such as speech, instead it just reduces the volume of actual in-game sounds by 60%, as well as giving it a padded room reverb.
+
Added two earmuffs to the laser tag locker.
+
Added a traitor set of earmuffs to the uplink for 2 TC, which work like earmuffs and a headset, but the latter functionality is kept hidden from non-antags.
+
+
MattAtlas updated:
+
+
EMPs no longer deal permanent damage. Instead, they deal a temporary kind of damage called surge damage, that lasts for 10 seconds at maximum.
+
Surge damage makes mechanical or assisted limbs count as broken and nonfunctional.
+
Surge damage has special interactions with the mechanical heart and eyes.
+
EMP damage is no longer randomized. It is now much more consistent.
+
IPC battery is no longer represented as nutrition. Instead, the microbattery organ contains a super-capacity cell that is used for all their actions. This is represented with a new HUD icon.
+
Hardsuits can now be charged in robot chargers if you are wearing them.
+
Ion bolts now have a hit sound.
+
Removed the ion rifle lethal mode.
+
The ion rifle now has four shots.
+
EMP'd guns no longer get discharged. Instead, they lock up for an amount of time depending on the strength of the EMP.
+
+
MoondancerPony updated:
+
+
Fixes the changelog.
+
Prevents uses of < and >, particularly when referencing things like <style> tags, from breaking changelogs in the future.
+
+
+
18 December 2020
+
Furrycactus updated:
+
+
AI and Stationbounds can no longer select Republican Siik as an accent.
+
+
Wowzewow (Wezzy) updated:
+
+
Fixes invisible remote control chairs.
+
Fixes detective badge job whitelists in the loadout.
+
+
+
17 December 2020
+
Furrycactus, Ferner, Happy_Fox updated:
+
+
NanoTrasen finally began decorating for Christmas time, everyone's favourite commercial holiday. There were some delays due to the whole war and everything, but nothing will stop the Christmas cheer. Not even the mix-up from the fake-snow guy that's led to piles of Moghean sand filling the greenery areas. Whatever. Surely Unathi and people who usually celebrate the holiday during the Summer will be pleased.
+
Added some additional gift sprites by Happy_Fox
+
+
Geeves updated:
+
+
Fixed a maths error that caused flamethrower minimum fuel requirement to be too high.
+
Increased the size of flamethrowers, they no longer fit in your backpack.
+
Made lit flamethrowers work as flame sources, which means they can now light cigarettes.
+
Added more useful failure messages to make the operation of a flamethrower a bit easier.
+
You can now light a flamethrower without an igniter, by lighting it with an external flame source.
+
Legcuff sprites work again.
+
Legcuffing now works the same as handcuffing.
+
You can use cable restraints in-hand to turn them into weak legcuffs.
+
Sharp and edged weapons now sever tendons instead of breaking bones. Strong enough sharp and edged weapons sever tendons AND break bones.
+
Energy net projectors can now directly fire energy nets again.
+
Improved energy net code a little. It should be more reliable when firing at people.
+
Raised energy net health to 50, up from 25.
+
You can no longer damage an energy net with an empty hand while trapped inside it.
+
Damaging an energy net with a weapon while trapped inside it will do reduced damage.
+
Added some medical taperolls to the medical sub-level equipment area and EMT room. Also gave medical borgs medical taperolls.
+
Crumpled tape now stores who crumpled it, provided the crumpler had an ID to track them with.
+
Tape now physically blocks passage, unless the mover has their intent set to harm, or their move intent set to run.
+
Lifting tape now actually works as you'd expect it to. It plays a little animation and goes transparent. If you don't have access, it crumples, if you do, it doesn't. People can now pass over without crumpling it or needing intents set.
+
+
MoondancerPony updated:
+
+
Makes canisters use VueUI.
+
Makes labels on vui-item components optional.
+
Includes core-js for IE/ECMAscript compatibility shims..
+
Tweaks the numeric VueUI input to look more like the old NanoUI equivalent, with one plus/minus sign per power of 10 (+, ++, +++ for 1, 10, 100) instead of just identical buttons side-by-side.
+
Converts the chemical dispenser to use VueUI.
+
Adds a numeric input selector to the chemical dispenser.
+
You can now empty ashtrays into disposal units by clicking the disposal unit with the ashtray in hand. On harm intent, you will disposal the ashtray instead.
+
Tweaks messages when ashtrays are emptied out, hit, and used.
+
Ashtrays spill their contents when destroyed.
+
Using an ashtray in hand will dump its contents onto the ground.
+
Hitting an ashtray will now produce a sound and a visible message.
+
Fixes contagious UI styling due to an erroneous unscoped <style> tag.
+
Adds a check to ensure this never happens again.
+
Makes all VueUI view components use scoped styles. This shouldn't change anything except for fixing the aforementioned bug.
+
+
Wowzewow (Wezzy) updated:
+
+
Fixes chair overlays and coloring once and for all. Also, added more accurate previews which help in mapping and examining.
+
Added a new buckling sound for shuttle chairs.
+
Jackboots and any shoes which can hold knives now also show up on the mob icon. Keep your eyes peeled!
+
Refactors how shoes update their icons.
+
You can hit people with everything, even non-weapons. They won't do any damage, though...
+
Volume of hit sounds are now proportional to force applied. High damage, more loud.
+
+
+
16 December 2020
+
Chevy updated:
+
+
Added thirteen new cocktail recipes with unique sprites inspired by the lore. Now, you too can taste a Cloudy Oran or a Jovian Storm!
+
Changed the cuba libre recipe I changed a while back. It should now be simpler to make in large quantities.
+
New energy rifle sprites.
+
+
Geeves updated:
+
+
Fixed paper scanners causing text files to appear with unreadable black text.
+
The character preview directions are now aligned correctly.
+
Added a mounted foam sprayer to the mechfab hardsuit modules tab. It's a hardsuit module that consumes 1500 cell charge to spray a line of metal foam.
+
Added some effects to metal foam walls that make them a bit more flashy when destroyed, as well as adding a click cooldown so they can't be spammed down.
+
Aggressively slamming someone through a metal foam wall now does some damage to them.
+
Clicking on a metal foam wall with building materials will now attempt to turn it into a wall, taking a bit longer than a standard girder would. Clicking on it with a floor tile will now turn it into plating, if the space below is an open space or actual space.
+
Throwing a humanoid at another humanoid will knock both over.
+
Gave the Captain's uniform new sprites.
+
Viscerators can no longer be on the same tile as other viscerators.
+
Toxin now does damage to any egg clusters that a nurse spider injected into someone.
+
The amount of toxin bottles in medical vendors have been lowered to 1, down from 4.
+
Talking indicators now appear on the right, in order for them to not appear behind other HUD elements.
+
Thralls now get an icon next to their master to easier see and remember who they are. Likewise, vampires who have thralls now get little icons next to their thralls.
+
+
Geeves, Happy Fox updated:
+
+
Replaced ore radars with ore detectors. It display pings in the darkness when it spots the ore, even through walls.
+
Mining stationbounds and drones now get ore detectors.
+
You can now see psi-pings through walls again.
+
+
Wowzewow (Wezzy) updated:
+
+
Adds party poppers and confetti. Hooray!
+
+
+
15 December 2020
+
Ferner updated:
+
+
Fixed burglars starting with a less required number of enemies than intended.
+
+
Geeves updated:
+
+
Pulling things down stairs will maintain the pull.
+
Fixed the Old Earth Luxury Cognac lacking an empty sprite.
+
+
+
13 December 2020
+
Geeves updated:
+
+
Cargo Tugs and Janicarts now get a UI when you click on them.
+
Made interacting with the Pussywagon a bit nicer.
+
Emagging a cargo tug or janicart now makes it move faster.
+
Added an orderable pineapple pizza box, as well as a cheaper randomized pizza box.
+
Mining drones now come equipped with an RFD-M, a tethering device, and a stack of purple flags.
+
Mining drones can now bump against doors to open them.
+
Mining drones now have external airlock access.
+
You can now click-drag with the RFD-M to lay down track.
+
The charge cost for robots using the RFD-M has been lowered to 200, down from 500.
+
Mining drones no longer appear as a unique role on the round join menu.
+
Mining drone lights are now much brighter, and illuminate in a circle around the drone.
+
Flamethrowers can no longer fire with empty fuel tanks.
+
Flamethrowers now work with any flammable gas.
+
+
Wowzewow (Wezzy) updated:
+
+
Fixes beds spawning with the wrong materials.
+
Fixes chair materials.
+
+
+
12 December 2020
+
Geeves updated:
+
+
Added two xenofauna guns to the surface level checkpoint. They only do damage to carp, cavern dwellers, and spiders, inflicting pain on any other targets.
+
+
JohnWildkins updated:
+
+
Flamethrowers can now launch fire more than one tile away from the user.
+
The cone of the flamethrower has been narrowed somewhat, and its spread pattern somewhat randomized.
+
Flamethrowers now use a radial menu system rather than a basic HTML UI.
\n Welcome to the NanoTrasen™ computer configuration utility.\n Please consult your system administrator if you have any questions about your device.\n
\n\n\n","import mod from \"-!../../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../../node_modules/thread-loader/dist/cjs.js!../../../../../node_modules/babel-loader/lib/index.js!../../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./config.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../../node_modules/thread-loader/dist/cjs.js!../../../../../node_modules/babel-loader/lib/index.js!../../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./config.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./config.vue?vue&type=template&id=6036d02c&\"\nimport script from \"./config.vue?vue&type=script&lang=js&\"\nexport * from \"./config.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","export * from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js??ref--8-oneOf-1-0!../../../node_modules/css-loader/dist/cjs.js??ref--8-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/src/index.js??ref--8-oneOf-1-2!../../../node_modules/sass-loader/dist/cjs.js??ref--8-oneOf-1-3!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./tooltip.vue?vue&type=style&index=0&id=4ea2a2b3&lang=scss&scoped=true&\"","var DESCRIPTORS = require('../internals/descriptors');\nvar fails = require('../internals/fails');\nvar createElement = require('../internals/document-create-element');\n\n// Thank's IE8 for his funny defineProperty\nmodule.exports = !DESCRIPTORS && !fails(function () {\n return Object.defineProperty(createElement('div'), 'a', {\n get: function () { return 7; }\n }).a != 7;\n});\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.passcode),expression:\"passcode\"}],attrs:{\"placeholder\":\"Enter passcode...\",\"autofocus\":\"\"},domProps:{\"value\":(_vm.passcode)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.passcode=$event.target.value}}}),_c('vui-button',{attrs:{\"params\":{ set_passcode: _vm.passcode }}},[_vm._v(\"Set\")]),_c('vui-button',{attrs:{\"params\":{ lock: 1 }}},[_vm._v(\"Lock\")])],1)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n
\n \n Set\n Lock\n
\n\n\n\n\n","import mod from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./maglock-config.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./maglock-config.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./maglock-config.vue?vue&type=template&id=081a5cf1&\"\nimport script from \"./maglock-config.vue?vue&type=script&lang=js&\"\nexport * from \"./maglock-config.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('span',{staticClass:\"tooltip label\"},[_vm._t(\"label\",[_vm._v(_vm._s(_vm.label))]),_c('div',{staticClass:\"tooltip content\"},[_vm._t(\"default\")],2)],2)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n \n {{label}}
\n \n\n\n\n\n\n","import mod from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./tooltip.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./tooltip.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./tooltip.vue?vue&type=template&id=4ea2a2b3&scoped=true&\"\nimport script from \"./tooltip.vue?vue&type=script&lang=js&\"\nexport * from \"./tooltip.vue?vue&type=script&lang=js&\"\nimport style0 from \"./tooltip.vue?vue&type=style&index=0&id=4ea2a2b3&lang=scss&scoped=true&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"4ea2a2b3\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('input',{ref:\"input\",style:({width: _vm.width}),attrs:{\"type\":\"range\",\"min\":_vm.min,\"max\":_vm.max},domProps:{\"value\":_vm.val},on:{\"change\":function($event){return _vm.onFieldUpdate($event.target)}}})])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n
\n \n
\n\n\n\n\n\n","import mod from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./slider.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./slider.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./slider.vue?vue&type=template&id=1c07adff&scoped=true&\"\nimport script from \"./slider.vue?vue&type=script&lang=js&\"\nexport * from \"./slider.vue?vue&type=script&lang=js&\"\nimport style0 from \"./slider.vue?vue&type=style&index=0&id=1c07adff&lang=scss&scoped=true&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"1c07adff\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[(_vm.invalid)?_c('div',{staticClass:\"center\"},[(_vm.nocons)?_c('span',{staticClass:\"red\"},[_vm._v(\"Error: No scanner bed detected!\")]):(!_vm.occupied)?_c('span',{staticClass:\"red\"},[_vm._v(\"No occupant detected.\")]):(_vm.ipc)?_c('span',{staticClass:\"red\"},[_vm._v(\"Error: Object in scanner bed interfering with sensor array.\")]):(_vm.noscan)?_c('span',{staticClass:\"red\"},[_vm._v(\"Error: No diagnostics profile installed for this species.\")]):_c('span',{staticClass:\"red\"},[_vm._v(\"Error: Unknown error.\")])]):_c('table',{staticClass:\"bodyscanner\"},[_c('div',{staticClass:\"row\"},[_c('div',{staticClass:\"column\"},[_c('h3',[_vm._v(\"Patient Status\")]),_c('hr'),_c('vui-group',[_c('vui-group-item',{attrs:{\"label\":\"Name:\"}},[_vm._v(_vm._s(_vm.name))]),_c('vui-group-item',{attrs:{\"label\":\"Status:\"}},[_c('span',{style:({color:_vm.consciousnessLabel(_vm.stat)})},[_vm._v(_vm._s(_vm.consciousnessText(_vm.stat)))])]),_c('vui-group-item',{attrs:{\"label\":\"Species:\"}},[_vm._v(_vm._s(_vm.species))]),_c('vui-group-item',{attrs:{\"label\":\"Brain Activity:\"}},[_c('vui-progress',{class:_vm.progressClass(_vm.brain_activity),attrs:{\"value\":_vm.brain_activity}},[_vm._v(_vm._s(_vm.brain_activity)+\"%\")])],1),(_vm.stat < 2)?[_c('vui-group-item',{attrs:{\"label\":\"Physical Trauma:\"}},[_c('span',{style:({color:_vm.damageLabel(_vm.bruteLoss)})},[_vm._v(_vm._s(_vm.bruteLoss))])]),_c('vui-group-item',{attrs:{\"label\":\"Oxygen Deprivation:\"}},[_c('span',{style:({color:_vm.damageLabel(_vm.oxyLoss)})},[_vm._v(_vm._s(_vm.oxyLoss))])]),_c('vui-group-item',{attrs:{\"label\":\"Organ Failure:\"}},[_c('span',{style:({color:_vm.damageLabel(_vm.toxLoss)})},[_vm._v(_vm._s(_vm.toxLoss))])]),_c('vui-group-item',{attrs:{\"label\":\"Burn Severity:\"}},[_c('span',{style:({color:_vm.damageLabel(_vm.fireLoss)})},[_vm._v(_vm._s(_vm.fireLoss))])])]:_vm._e()],2),_c('hr'),_c('vui-group',[_c('vui-group-item',{attrs:{\"label\":\"Radiation Level:\"}},[_vm._v(_vm._s(Math.round(_vm.rads)))]),_c('vui-group-item',{attrs:{\"label\":\"Genetic Damage:\"}},[_vm._v(_vm._s(_vm.cloneLoss))]),_c('vui-group-item',{attrs:{\"label\":\"Est. Paralysis Level:\"}},[_vm._v(_vm._s(_vm.paralysis)),(_vm.paralysis)?_c('span',[_vm._v(\" (~\"+_vm._s(Math.round(_vm.paralysis/4))+\" Seconds Left)\")]):_vm._e()]),_c('vui-group-item',{attrs:{\"label\":\"Body Temperature:\"}},[_vm._v(_vm._s(_vm.bodytemp)+\" K (~ \"+_vm._s(Math.round(_vm.bodytemp - 273.15))+\" C)\")])],1),_c('hr'),_c('h3',[_vm._v(\"Blood Status\")]),_c('hr'),_c('vui-group',[_c('vui-group-item',{style:({color:_vm.getPressureClass(_vm.blood_pressure_level)}),attrs:{\"label\":\"BP:\"}},[_vm._v(\" \"+_vm._s(_vm.blood_pressure)+\" \")]),_c('vui-group-item',{attrs:{\"label\":\"Blood Oxygenation:\"}},[_c('vui-progress',{class:_vm.progressClass(_vm.brain_activity),attrs:{\"value\":Math.round(_vm.blood_o2)}},[_vm._v(_vm._s(Math.round(_vm.blood_o2))+\"%\")])],1),(Math.round(_vm.norepiAmt))?_c('vui-group-item',{attrs:{\"label\":\"Inaprovaline:\"}},[_vm._v(\" \"+_vm._s(Math.round(_vm.norepiAmt))+\" unit(s)\")]):_vm._e(),(Math.round(_vm.soporAmt))?_c('vui-group-item',{attrs:{\"label\":\"Soporific:\"}},[_vm._v(\" \"+_vm._s(Math.round(_vm.soporAmt))+\" unit(s)\")]):_vm._e(),(Math.round(_vm.bicardAmt))?_c('vui-group-item',{attrs:{\"label\":\"Bicaridine:\"}},[_vm._v(\" \"+_vm._s(Math.round(_vm.bicardAmt))+\" unit(s)\")]):_vm._e(),(Math.round(_vm.dermAmt))?_c('vui-group-item',{attrs:{\"label\":\"Dermaline:\"}},[_vm._v(\" \"+_vm._s(Math.round(_vm.dermAmt))+\" unit(s)\")]):_vm._e(),(Math.round(_vm.dexAmt))?_c('vui-group-item',{attrs:{\"label\":\"Dexalin:\"}},[_vm._v(\" \"+_vm._s(Math.round(_vm.dexAmt))+\" unit(s)\")]):_vm._e(),(Math.round(_vm.otherAmt))?_c('vui-group-item',{attrs:{\"label\":\"Other:\"}},[_vm._v(\" \"+_vm._s(Math.round(_vm.otherAmt))+\" unit(s)\")]):_vm._e()],1)],1),_c('div',{staticClass:\"column\"},[(_vm.hasmissing)?_vm._l((_vm.missingparts),function(organ){return _c('div',{key:organ.name},[_c('vui-group-item',[_vm._v(\" \"+_vm._s(organ)+\" \")])],1)}):_vm._e(),_c('h3',[_vm._v(\"Internal Organ Status\")]),_c('hr'),_c('table',{staticClass:\"injury\"},[(_vm.hasinternalinjury)?[_vm._m(0)]:[_vm._m(1)],_vm._l((_vm.organs),function(organ){return _c('tr',{key:organ.name},[(organ.damage != 'None' || organ.hasWounds)?[_c('td',[_vm._v(\" \"+_vm._s(organ.name)+\" \")]),_c('td',[_c('span',{style:({color:_vm.damageLabel(organ.damage)})},[_vm._v(\" \"+_vm._s(organ.damage))])]),_c('td',_vm._l((organ.wounds),function(wound){return _c('div',{key:wound.name,staticStyle:{\"color\":\"Tomato\"}},[_vm._v(\" \"+_vm._s(wound)+\" \")])}),0),_c('td',[_vm._v(\" \"+_vm._s(organ.location)+\" \")])]:_vm._e()],2)})],2),_c('hr'),_c('h3',[_vm._v(\"External Bodypart Status\")]),_c('hr'),_c('table',{staticClass:\"injury\"},[(_vm.hasexternalinjury)?[_vm._m(2)]:[_vm._m(3)],_vm._l((_vm.bodyparts),function(organ){return _c('tr',{key:organ.name},[(organ.bruteDmg != 'None' || organ.burnDmg != 'None' || organ.hasWounds)?[_c('td',[_vm._v(\" \"+_vm._s(organ.name)+\" \")]),_c('td',[_c('span',{style:({color:_vm.damageLabel(organ.bruteDmg)})},[_vm._v(\" \"+_vm._s(organ.bruteDmg)+\" \")]),_vm._v(\" / \"),_c('span',{style:({color:_vm.damageLabel(organ.burnDmg)})},[_vm._v(\" \"+_vm._s(organ.burnDmg)+\" \")])]),_c('td',_vm._l((organ.wounds),function(wound){return _c('div',{key:wound.name,staticStyle:{\"color\":\"Tomato\"}},[_vm._v(\" \"+_vm._s(wound)+\" \")])}),0)]:_vm._e()],2)})],2)],2),_c('h3',[_vm._v(\"Actions\")]),_c('vui-button',{staticClass:\"center\",attrs:{\"params\":{print: 1}}},[_vm._v(\"Print Report\")]),_c('vui-button',{staticClass:\"center\",attrs:{\"params\":{eject: 1}}},[_vm._v(\"Eject Occupant\")])],1)])])}\nvar staticRenderFns = [function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('tr',[_c('th',[_vm._v(\"Organ\")]),_c('th',[_vm._v(\"Trauma\")]),_c('th',[_vm._v(\"Wounds\")]),_c('th',[_vm._v(\"Location\")])])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('tr',[_c('th',[_c('span',{staticStyle:{\"color\":\"LimeGreen\"}},[_vm._v(\"The occupant has no internal injuries.\")])])])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('tr',[_c('th',[_vm._v(\"Organ\")]),_c('th',[_vm._v(\"Physical / Burn Trauma\")]),_c('th',[_vm._v(\"Wounds\")])])},function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('tr',[_c('th',[_c('span',{staticStyle:{\"color\":\"LimeGreen\"}},[_vm._v(\"The occupant has no external injuries.\")])])])}]\n\nexport { render, staticRenderFns }","\n
\n\n \n
\n Error: No scanner bed detected!\n No occupant detected.\n Error: Object in scanner bed interfering with sensor array.\n Error: No diagnostics profile installed for this species.\n Error: Unknown error.\n
\n\n\n\n\n\n","import mod from \"-!../../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../../node_modules/thread-loader/dist/cjs.js!../../../../../node_modules/babel-loader/lib/index.js!../../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./main.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../../node_modules/thread-loader/dist/cjs.js!../../../../../node_modules/babel-loader/lib/index.js!../../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./main.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./main.vue?vue&type=template&id=0f00c828&scoped=true&\"\nimport script from \"./main.vue?vue&type=script&lang=js&\"\nexport * from \"./main.vue?vue&type=script&lang=js&\"\nimport style0 from \"./main.vue?vue&type=style&index=0&id=0f00c828&lang=scss&scoped=true&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"0f00c828\",\n null\n \n)\n\nexport default component.exports","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[(_vm.forumuserui_enabled)?_c('div',{staticClass:\"notice\"},[_c('center',[_vm._v(\"Modifications done do not outlast the round! Use the forums/WI to modify ranks.\")])],1):_vm._e(),_c('table',[_c('tr',[_c('th',[_vm._v(\"Ckey \"),_c('vui-button',{attrs:{\"params\":{ action: 'add' }}},[_vm._v(\"Add\")])],1),_c('th',[_vm._v(\"Rank\")]),_c('th',[_vm._v(\"Permissions\")])]),_vm._l((_vm.admins),function(admin){return _c('tr',{key:admin.ckey},[_c('td',[_c('vui-button',{attrs:{\"params\":{ action: 'remove', ckey: admin.ckey }}},[_vm._v(\"Edit\")]),_vm._v(\" \"+_vm._s(admin.ckey)+\" \")],1),_c('td',[_c('vui-button',{attrs:{\"params\":{ action: 'rank', ckey: admin.ckey }}},[_vm._v(\"Edit\")]),_vm._v(\" \"+_vm._s(admin.rank)+\" \")],1),_c('td',[_c('vui-button',{attrs:{\"params\":{ action: 'rights', ckey: admin.ckey }}},[_vm._v(\"Edit\")]),_vm._v(\" \"+_vm._s(admin.rights)+\" \")],1)])})],2)])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n
\n
\n
Modifications done do not outlast the round! Use the forums/WI to modify ranks.
\n
\n
\n
\n
Ckey Add
\n
Rank
\n
Permissions
\n
\n\n
\n
\n Edit {{admin.ckey}}\n
\n
\n Edit {{admin.rank}}\n
\n
\n Edit {{admin.rights}}\n
\n
\n
\n
\n\n\n\n","import mod from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./permissions-panel.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./permissions-panel.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./permissions-panel.vue?vue&type=template&id=45d39eba&\"\nimport script from \"./permissions-panel.vue?vue&type=script&lang=js&\"\nexport * from \"./permissions-panel.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n null,\n null\n \n)\n\nexport default component.exports","var store = require('../internals/shared-store');\n\nvar functionToString = Function.toString;\n\n// this helper broken in `3.4.1-3.4.4`, so we can't use `shared` helper\nif (typeof store.inspectSource != 'function') {\n store.inspectSource = function (it) {\n return functionToString.call(it);\n };\n}\n\nmodule.exports = store.inspectSource;\n","export * from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js??ref--8-oneOf-1-0!../../../../node_modules/css-loader/dist/cjs.js??ref--8-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/src/index.js??ref--8-oneOf-1-2!../../../../node_modules/sass-loader/dist/cjs.js??ref--8-oneOf-1-3!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./voting.vue?vue&type=style&index=0&id=8bcc2668&lang=scss&scoped=true&\"","'use strict';\nvar $ = require('../internals/export');\nvar getOwnPropertyDescriptor = require('../internals/object-get-own-property-descriptor').f;\nvar toLength = require('../internals/to-length');\nvar notARegExp = require('../internals/not-a-regexp');\nvar requireObjectCoercible = require('../internals/require-object-coercible');\nvar correctIsRegExpLogic = require('../internals/correct-is-regexp-logic');\nvar IS_PURE = require('../internals/is-pure');\n\nvar nativeEndsWith = ''.endsWith;\nvar min = Math.min;\n\nvar CORRECT_IS_REGEXP_LOGIC = correctIsRegExpLogic('endsWith');\n// https://github.com/zloirock/core-js/pull/702\nvar MDN_POLYFILL_BUG = !IS_PURE && !CORRECT_IS_REGEXP_LOGIC && !!function () {\n var descriptor = getOwnPropertyDescriptor(String.prototype, 'endsWith');\n return descriptor && !descriptor.writable;\n}();\n\n// `String.prototype.endsWith` method\n// https://tc39.github.io/ecma262/#sec-string.prototype.endswith\n$({ target: 'String', proto: true, forced: !MDN_POLYFILL_BUG && !CORRECT_IS_REGEXP_LOGIC }, {\n endsWith: function endsWith(searchString /* , endPosition = @length */) {\n var that = String(requireObjectCoercible(this));\n notARegExp(searchString);\n var endPosition = arguments.length > 1 ? arguments[1] : undefined;\n var len = toLength(that.length);\n var end = endPosition === undefined ? len : min(toLength(endPosition), len);\n var search = String(searchString);\n return nativeEndsWith\n ? nativeEndsWith.call(that, search, end)\n : that.slice(end - search.length, end) === search;\n }\n});\n","'use strict';\nvar charAt = require('../internals/string-multibyte').charAt;\n\n// `AdvanceStringIndex` abstract operation\n// https://tc39.github.io/ecma262/#sec-advancestringindex\nmodule.exports = function (S, index, unicode) {\n return index + (unicode ? charAt(S, index).length : 1);\n};\n","export * from \"-!../../../../../node_modules/mini-css-extract-plugin/dist/loader.js??ref--8-oneOf-1-0!../../../../../node_modules/css-loader/dist/cjs.js??ref--8-oneOf-1-1!../../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../../node_modules/postcss-loader/src/index.js??ref--8-oneOf-1-2!../../../../../node_modules/sass-loader/dist/cjs.js??ref--8-oneOf-1-3!../../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./downloader.vue?vue&type=style&index=0&id=f45f2cac&lang=scss&scoped=true&\"","var id = 0;\nvar postfix = Math.random();\n\nmodule.exports = function (key) {\n return 'Symbol(' + String(key === undefined ? '' : key) + ')_' + (++id + postfix).toString(36);\n};\n","var DESCRIPTORS = require('../internals/descriptors');\nvar definePropertyModule = require('../internals/object-define-property');\nvar createPropertyDescriptor = require('../internals/create-property-descriptor');\n\nmodule.exports = DESCRIPTORS ? function (object, key, value) {\n return definePropertyModule.f(object, key, createPropertyDescriptor(1, value));\n} : function (object, key, value) {\n object[key] = value;\n return object;\n};\n","'use strict';\nvar regexpFlags = require('./regexp-flags');\nvar stickyHelpers = require('./regexp-sticky-helpers');\n\nvar nativeExec = RegExp.prototype.exec;\n// This always refers to the native implementation, because the\n// String#replace polyfill uses ./fix-regexp-well-known-symbol-logic.js,\n// which loads this file before patching the method.\nvar nativeReplace = String.prototype.replace;\n\nvar patchedExec = nativeExec;\n\nvar UPDATES_LAST_INDEX_WRONG = (function () {\n var re1 = /a/;\n var re2 = /b*/g;\n nativeExec.call(re1, 'a');\n nativeExec.call(re2, 'a');\n return re1.lastIndex !== 0 || re2.lastIndex !== 0;\n})();\n\nvar UNSUPPORTED_Y = stickyHelpers.UNSUPPORTED_Y || stickyHelpers.BROKEN_CARET;\n\n// nonparticipating capturing group, copied from es5-shim's String#split patch.\nvar NPCG_INCLUDED = /()??/.exec('')[1] !== undefined;\n\nvar PATCH = UPDATES_LAST_INDEX_WRONG || NPCG_INCLUDED || UNSUPPORTED_Y;\n\nif (PATCH) {\n patchedExec = function exec(str) {\n var re = this;\n var lastIndex, reCopy, match, i;\n var sticky = UNSUPPORTED_Y && re.sticky;\n var flags = regexpFlags.call(re);\n var source = re.source;\n var charsAdded = 0;\n var strCopy = str;\n\n if (sticky) {\n flags = flags.replace('y', '');\n if (flags.indexOf('g') === -1) {\n flags += 'g';\n }\n\n strCopy = String(str).slice(re.lastIndex);\n // Support anchored sticky behavior.\n if (re.lastIndex > 0 && (!re.multiline || re.multiline && str[re.lastIndex - 1] !== '\\n')) {\n source = '(?: ' + source + ')';\n strCopy = ' ' + strCopy;\n charsAdded++;\n }\n // ^(? + rx + ) is needed, in combination with some str slicing, to\n // simulate the 'y' flag.\n reCopy = new RegExp('^(?:' + source + ')', flags);\n }\n\n if (NPCG_INCLUDED) {\n reCopy = new RegExp('^' + source + '$(?!\\\\s)', flags);\n }\n if (UPDATES_LAST_INDEX_WRONG) lastIndex = re.lastIndex;\n\n match = nativeExec.call(sticky ? reCopy : re, strCopy);\n\n if (sticky) {\n if (match) {\n match.input = match.input.slice(charsAdded);\n match[0] = match[0].slice(charsAdded);\n match.index = re.lastIndex;\n re.lastIndex += match[0].length;\n } else re.lastIndex = 0;\n } else if (UPDATES_LAST_INDEX_WRONG && match) {\n re.lastIndex = re.global ? match.index + match[0].length : lastIndex;\n }\n if (NPCG_INCLUDED && match && match.length > 1) {\n // Fix browsers whose `exec` methods don't consistently return `undefined`\n // for NPCG, like IE8. NOTE: This doesn' work for /(.?)?/\n nativeReplace.call(match[0], reCopy, function () {\n for (i = 1; i < arguments.length - 2; i++) {\n if (arguments[i] === undefined) match[i] = undefined;\n }\n });\n }\n\n return match;\n };\n}\n\nmodule.exports = patchedExec;\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('p',[_vm._v(\"Please configure your pAI personality's options. Remember, what you enter here could determine whether or not the user requesting a personality chooses you!\")]),_c('vui-group',[_c('vui-group-item',{attrs:{\"label\":\"Name:\"}},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.name),expression:\"name\"}],attrs:{\"type\":\"text\"},domProps:{\"value\":(_vm.name)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.name=$event.target.value}}}),_c('p',[_vm._v(\" What you plan to call yourself. Suggestions: Any character name you would choose for a station character OR an AI. \")])]),_c('vui-group-item',{attrs:{\"label\":\"Description:\"}},[_c('textarea',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.description),expression:\"description\"}],domProps:{\"value\":(_vm.description)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.description=$event.target.value}}}),_vm._v(\" \"),_c('p',[_vm._v(\" What sort of pAI you typically play; your mannerisms, your quirks, etc. This can be as sparse or as detailed as you like. \")])]),_c('vui-group-item',{attrs:{\"label\":\"Preferred Role:\"}},[_c('input',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.role),expression:\"role\"}],attrs:{\"type\":\"text\"},domProps:{\"value\":(_vm.role)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.role=$event.target.value}}}),_c('p',[_vm._v(\" Do you like to partner with sneaky social ninjas? Like to help security hunt down thugs? Enjoy watching an engineer's back while he saves the station yet again? This doesn't have to be limited to just station jobs. Pretty much any general descriptor for what you'd like to be doing works here. \")])]),_c('vui-group-item',{attrs:{\"label\":\"OOC Comments:\"}},[_c('textarea',{directives:[{name:\"model\",rawName:\"v-model\",value:(_vm.comments),expression:\"comments\"}],domProps:{\"value\":(_vm.comments)},on:{\"input\":function($event){if($event.target.composing){ return; }_vm.comments=$event.target.value}}}),_vm._v(\" Anything you'd like to address specifically to the player reading this in an OOC manner. \\\"I prefer more serious RP.\\\", \\\"I'm still learning the interface!\\\", etc. Feel free to leave this blank if you want. \")])],1),_c('vui-button',{attrs:{\"disabled\":!_vm.canSubmit,\"params\":{submit_candidate: {name: _vm.name, description: _vm.description, role: _vm.role, comments: _vm.comments}}}},[_vm._v(\"Submit Personality\")])],1)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n
\n
Please configure your pAI personality's options. Remember, what you enter here could determine whether or not the user requesting a personality chooses you!
\n\n\n \n \n \n
\n What you plan to call yourself. Suggestions: Any character name you would choose for a station character OR an AI.\n
\n \n \n \n \n \n
\n Do you like to partner with sneaky social ninjas? Like to help security hunt down thugs? Enjoy watching an engineer's back while he saves the station yet again? This doesn't have to be limited to just station jobs. Pretty much any general descriptor for what you'd like to be doing works here.\n
\n \n \n \n \n Submit Personality\n
\n\n\n\n\n\n","import mod from \"-!../../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../../node_modules/thread-loader/dist/cjs.js!../../../../../node_modules/babel-loader/lib/index.js!../../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./recruit.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../../node_modules/thread-loader/dist/cjs.js!../../../../../node_modules/babel-loader/lib/index.js!../../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./recruit.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./recruit.vue?vue&type=template&id=c4dbe0aa&scoped=true&\"\nimport script from \"./recruit.vue?vue&type=script&lang=js&\"\nexport * from \"./recruit.vue?vue&type=script&lang=js&\"\nimport style0 from \"./recruit.vue?vue&type=style&index=0&id=c4dbe0aa&lang=scss&scoped=true&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"c4dbe0aa\",\n null\n \n)\n\nexport default component.exports","var fails = require('../internals/fails');\n\nvar replacement = /#|\\.prototype\\./;\n\nvar isForced = function (feature, detection) {\n var value = data[normalize(feature)];\n return value == POLYFILL ? true\n : value == NATIVE ? false\n : typeof detection == 'function' ? fails(detection)\n : !!detection;\n};\n\nvar normalize = isForced.normalize = function (string) {\n return String(string).replace(replacement, '.').toLowerCase();\n};\n\nvar data = isForced.data = {};\nvar NATIVE = isForced.NATIVE = 'N';\nvar POLYFILL = isForced.POLYFILL = 'P';\n\nmodule.exports = isForced;\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',[_c('div',{staticClass:\"item\"},[_c('div',{staticClass:\"itemLabel\"},[_vm._v(\"Confirm identity:\")]),_c('div',{staticClass:\"itemContent\"},[_c('vui-button',{attrs:{\"params\":{ scan: 1 },\"icon\":\"eject\"}},[(_vm.state.idname)?_c('span',[_vm._v(_vm._s(_vm.state.idname))]):_c('span',[_vm._v(\"--------\")])])],1)]),(_vm.state.auth)?_c('div',{staticStyle:{\"margin-top\":\"24px\",\"clear\":\"both\"}},[_c('b',[_vm._v(\"Logged in to:\")]),_vm._v(\" \"+_vm._s(_vm.state.bossname)+\" Quantum Entanglement Network \"),(_vm.remaining_cooldown <= 0)?[_c('vui-item',{attrs:{\"label\":\"Sending to:\"}},[_c('select',{directives:[{name:\"model\",rawName:\"v-model.lazy\",value:(_vm.state.destination),expression:\"state.destination\",modifiers:{\"lazy\":true}}],staticClass:\"button\",on:{\"change\":function($event){var $$selectedVal = Array.prototype.filter.call($event.target.options,function(o){return o.selected}).map(function(o){var val = \"_value\" in o ? o._value : o.value;return val}); _vm.$set(_vm.state, \"destination\", $event.target.multiple ? $$selectedVal : $$selectedVal[0])}}},_vm._l((_vm.state.departiments),function(dep){return _c('option',{key:dep,domProps:{\"value\":dep}},[_vm._v(_vm._s(dep))])}),0)]),(_vm.state.paper)?[_c('vui-item',{attrs:{\"label\":\"Currently sending:\"}},[_vm._v(_vm._s(_vm.state.paper))]),_c('vui-button',{attrs:{\"push-state\":\"\",\"params\":{ send: 1}}},[_vm._v(\"Send\")])]:_c('span',[_vm._v(\"Please insert paper to send via secure connection.\")])]:_c('span',[_c('b',[_vm._v(\"Transmitter arrays realigning. Please stand by. \"+_vm._s(_vm._f(\"roundRemaining\")(_vm.remaining_cooldown))+\" seconds remaining.\")])])],2):_vm._e(),(_vm.state.paper)?_c('vui-button',{attrs:{\"params\":{ remove: 1}}},[_vm._v(\"Remove item\")]):_vm._e(),_c('h3',[_vm._v(\"PDAs to notify:\")]),_vm._l((_vm.state.alertpdas),function(pda){return _c('div',{key:pda.ref},[_vm._v(\" \"+_vm._s(pda.name)+\" \"),_c('vui-button',{attrs:{\"params\":{ unlink: pda.ref }}},[_vm._v(\"Unlink\")])],1)}),(_vm.state.alertpdas.length <= 0)?_c('div',[_vm._v(\"No PDAs are linked.\")]):_vm._e(),_c('vui-button',{attrs:{\"params\":{ linkpda: 1 }}},[_vm._v(\"Add PDA to Notify\")])],2)}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n
\n
\n
Confirm identity:
\n
\n {{ state.idname }}--------\n
\n
\n
\n Logged in to: {{ state.bossname }} Quantum Entanglement Network\n \n \n \n \n \n {{ state.paper }}\n Send\n \n Please insert paper to send via secure connection.\n \n Transmitter arrays realigning. Please stand by. {{ remaining_cooldown | roundRemaining}} seconds remaining.\n
\n Remove item\n \n
PDAs to notify:
\n
\n {{ pda.name }} Unlink\n
\n
No PDAs are linked.
\n Add PDA to Notify\n
\n\n\n\n\n","import mod from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./fax.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../../node_modules/thread-loader/dist/cjs.js!../../../../node_modules/babel-loader/lib/index.js!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./fax.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./fax.vue?vue&type=template&id=4b0e320e&scoped=true&\"\nimport script from \"./fax.vue?vue&type=script&lang=js&\"\nexport * from \"./fax.vue?vue&type=script&lang=js&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"4b0e320e\",\n null\n \n)\n\nexport default component.exports","export * from \"-!../../../node_modules/mini-css-extract-plugin/dist/loader.js??ref--8-oneOf-1-0!../../../node_modules/css-loader/dist/cjs.js??ref--8-oneOf-1-1!../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../node_modules/postcss-loader/src/index.js??ref--8-oneOf-1-2!../../../node_modules/sass-loader/dist/cjs.js??ref--8-oneOf-1-3!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./handles.vue?vue&type=style&index=0&id=01c02ad8&lang=scss&scoped=true&\"","'use strict';\nvar $ = require('../internals/export');\nvar fails = require('../internals/fails');\nvar isArray = require('../internals/is-array');\nvar isObject = require('../internals/is-object');\nvar toObject = require('../internals/to-object');\nvar toLength = require('../internals/to-length');\nvar createProperty = require('../internals/create-property');\nvar arraySpeciesCreate = require('../internals/array-species-create');\nvar arrayMethodHasSpeciesSupport = require('../internals/array-method-has-species-support');\nvar wellKnownSymbol = require('../internals/well-known-symbol');\nvar V8_VERSION = require('../internals/engine-v8-version');\n\nvar IS_CONCAT_SPREADABLE = wellKnownSymbol('isConcatSpreadable');\nvar MAX_SAFE_INTEGER = 0x1FFFFFFFFFFFFF;\nvar MAXIMUM_ALLOWED_INDEX_EXCEEDED = 'Maximum allowed index exceeded';\n\n// We can't use this feature detection in V8 since it causes\n// deoptimization and serious performance degradation\n// https://github.com/zloirock/core-js/issues/679\nvar IS_CONCAT_SPREADABLE_SUPPORT = V8_VERSION >= 51 || !fails(function () {\n var array = [];\n array[IS_CONCAT_SPREADABLE] = false;\n return array.concat()[0] !== array;\n});\n\nvar SPECIES_SUPPORT = arrayMethodHasSpeciesSupport('concat');\n\nvar isConcatSpreadable = function (O) {\n if (!isObject(O)) return false;\n var spreadable = O[IS_CONCAT_SPREADABLE];\n return spreadable !== undefined ? !!spreadable : isArray(O);\n};\n\nvar FORCED = !IS_CONCAT_SPREADABLE_SUPPORT || !SPECIES_SUPPORT;\n\n// `Array.prototype.concat` method\n// https://tc39.github.io/ecma262/#sec-array.prototype.concat\n// with adding support of @@isConcatSpreadable and @@species\n$({ target: 'Array', proto: true, forced: FORCED }, {\n concat: function concat(arg) { // eslint-disable-line no-unused-vars\n var O = toObject(this);\n var A = arraySpeciesCreate(O, 0);\n var n = 0;\n var i, k, length, len, E;\n for (i = -1, length = arguments.length; i < length; i++) {\n E = i === -1 ? O : arguments[i];\n if (isConcatSpreadable(E)) {\n len = toLength(E.length);\n if (n + len > MAX_SAFE_INTEGER) throw TypeError(MAXIMUM_ALLOWED_INDEX_EXCEEDED);\n for (k = 0; k < len; k++, n++) if (k in E) createProperty(A, n, E[k]);\n } else {\n if (n >= MAX_SAFE_INTEGER) throw TypeError(MAXIMUM_ALLOWED_INDEX_EXCEEDED);\n createProperty(A, n++, E);\n }\n }\n A.length = n;\n return A;\n }\n});\n","var render = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{attrs:{\"id\":\"resizehandles\"}},[_c('div',{staticClass:\"resizeHandle__e\",on:{\"mousedown\":function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,\"left\",37,$event.key,[\"Left\",\"ArrowLeft\"])){ return null; }if('button' in $event && $event.button !== 0){ return null; }return _vm.startResizing(1, 0, $event)}}}),_c('div',{staticClass:\"resizeHandle__s\",on:{\"mousedown\":function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,\"left\",37,$event.key,[\"Left\",\"ArrowLeft\"])){ return null; }if('button' in $event && $event.button !== 0){ return null; }return _vm.startResizing(0, 1, $event)}}}),_c('div',{staticClass:\"resizeHandle__se\",on:{\"mousedown\":function($event){if(!$event.type.indexOf('key')&&_vm._k($event.keyCode,\"left\",37,$event.key,[\"Left\",\"ArrowLeft\"])){ return null; }if('button' in $event && $event.button !== 0){ return null; }return _vm.startResizing(1, 1, $event)}}})])}\nvar staticRenderFns = []\n\nexport { render, staticRenderFns }","\n
\n \n \n \n
\n\n\n\n\n\n","import mod from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./handles.vue?vue&type=script&lang=js&\"; export default mod; export * from \"-!../../../node_modules/cache-loader/dist/cjs.js??ref--12-0!../../../node_modules/thread-loader/dist/cjs.js!../../../node_modules/babel-loader/lib/index.js!../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./handles.vue?vue&type=script&lang=js&\"","import { render, staticRenderFns } from \"./handles.vue?vue&type=template&id=01c02ad8&scoped=true&\"\nimport script from \"./handles.vue?vue&type=script&lang=js&\"\nexport * from \"./handles.vue?vue&type=script&lang=js&\"\nimport style0 from \"./handles.vue?vue&type=style&index=0&id=01c02ad8&lang=scss&scoped=true&\"\n\n\n/* normalize component */\nimport normalizer from \"!../../../node_modules/vue-loader/lib/runtime/componentNormalizer.js\"\nvar component = normalizer(\n script,\n render,\n staticRenderFns,\n false,\n null,\n \"01c02ad8\",\n null\n \n)\n\nexport default component.exports","export * from \"-!../../../../node_modules/mini-css-extract-plugin/dist/loader.js??ref--8-oneOf-1-0!../../../../node_modules/css-loader/dist/cjs.js??ref--8-oneOf-1-1!../../../../node_modules/vue-loader/lib/loaders/stylePostLoader.js!../../../../node_modules/postcss-loader/src/index.js??ref--8-oneOf-1-2!../../../../node_modules/sass-loader/dist/cjs.js??ref--8-oneOf-1-3!../../../../node_modules/cache-loader/dist/cjs.js??ref--0-0!../../../../node_modules/vue-loader/lib/index.js??vue-loader-options!./janitor.vue?vue&type=style&index=0&id=1021c617&lang=scss&scoped=true&\"","var anObject = require('../internals/an-object');\nvar iteratorClose = require('../internals/iterator-close');\n\n// call something on iterator step with safe closing on error\nmodule.exports = function (iterator, fn, value, ENTRIES) {\n try {\n return ENTRIES ? fn(anObject(value)[0], value[1]) : fn(value);\n // 7.4.6 IteratorClose(iterator, completion)\n } catch (error) {\n iteratorClose(iterator);\n throw error;\n }\n};\n","var DESCRIPTORS = require('../internals/descriptors');\nvar IE8_DOM_DEFINE = require('../internals/ie8-dom-define');\nvar anObject = require('../internals/an-object');\nvar toPrimitive = require('../internals/to-primitive');\n\nvar nativeDefineProperty = Object.defineProperty;\n\n// `Object.defineProperty` method\n// https://tc39.github.io/ecma262/#sec-object.defineproperty\nexports.f = DESCRIPTORS ? nativeDefineProperty : function defineProperty(O, P, Attributes) {\n anObject(O);\n P = toPrimitive(P, true);\n anObject(Attributes);\n if (IE8_DOM_DEFINE) try {\n return nativeDefineProperty(O, P, Attributes);\n } catch (error) { /* empty */ }\n if ('get' in Attributes || 'set' in Attributes) throw TypeError('Accessors not supported');\n if ('value' in Attributes) O[P] = Attributes.value;\n return O;\n};\n","var root = require('./_root');\n\n/** Built-in value references. */\nvar Symbol = root.Symbol;\n\nmodule.exports = Symbol;\n","'use strict';\nvar IteratorPrototype = require('../internals/iterators-core').IteratorPrototype;\nvar create = require('../internals/object-create');\nvar createPropertyDescriptor = require('../internals/create-property-descriptor');\nvar setToStringTag = require('../internals/set-to-string-tag');\nvar Iterators = require('../internals/iterators');\n\nvar returnThis = function () { return this; };\n\nmodule.exports = function (IteratorConstructor, NAME, next) {\n var TO_STRING_TAG = NAME + ' Iterator';\n IteratorConstructor.prototype = create(IteratorPrototype, { next: createPropertyDescriptor(1, next) });\n setToStringTag(IteratorConstructor, TO_STRING_TAG, false, true);\n Iterators[TO_STRING_TAG] = returnThis;\n return IteratorConstructor;\n};\n","'use strict';\n\nvar fails = require('./fails');\n\n// babel-minify transpiles RegExp('a', 'y') -> /a/y and it causes SyntaxError,\n// so we use an intermediate function.\nfunction RE(s, f) {\n return RegExp(s, f);\n}\n\nexports.UNSUPPORTED_Y = fails(function () {\n // babel-minify transpiles RegExp('a', 'y') -> /a/y and it causes SyntaxError\n var re = RE('a', 'y');\n re.lastIndex = 2;\n return re.exec('abcd') != null;\n});\n\nexports.BROKEN_CARET = fails(function () {\n // https://bugzilla.mozilla.org/show_bug.cgi?id=773687\n var re = RE('^r', 'gy');\n re.lastIndex = 2;\n return re.exec('str') != null;\n});\n","/*!\n * Vue.js v2.6.12\n * (c) 2014-2020 Evan You\n * Released under the MIT License.\n */\n/* */\n\nvar emptyObject = Object.freeze({});\n\n// These helpers produce better VM code in JS engines due to their\n// explicitness and function inlining.\nfunction isUndef (v) {\n return v === undefined || v === null\n}\n\nfunction isDef (v) {\n return v !== undefined && v !== null\n}\n\nfunction isTrue (v) {\n return v === true\n}\n\nfunction isFalse (v) {\n return v === false\n}\n\n/**\n * Check if value is primitive.\n */\nfunction isPrimitive (value) {\n return (\n typeof value === 'string' ||\n typeof value === 'number' ||\n // $flow-disable-line\n typeof value === 'symbol' ||\n typeof value === 'boolean'\n )\n}\n\n/**\n * Quick object check - this is primarily used to tell\n * Objects from primitive values when we know the value\n * is a JSON-compliant type.\n */\nfunction isObject (obj) {\n return obj !== null && typeof obj === 'object'\n}\n\n/**\n * Get the raw type string of a value, e.g., [object Object].\n */\nvar _toString = Object.prototype.toString;\n\nfunction toRawType (value) {\n return _toString.call(value).slice(8, -1)\n}\n\n/**\n * Strict object type check. Only returns true\n * for plain JavaScript objects.\n */\nfunction isPlainObject (obj) {\n return _toString.call(obj) === '[object Object]'\n}\n\nfunction isRegExp (v) {\n return _toString.call(v) === '[object RegExp]'\n}\n\n/**\n * Check if val is a valid array index.\n */\nfunction isValidArrayIndex (val) {\n var n = parseFloat(String(val));\n return n >= 0 && Math.floor(n) === n && isFinite(val)\n}\n\nfunction isPromise (val) {\n return (\n isDef(val) &&\n typeof val.then === 'function' &&\n typeof val.catch === 'function'\n )\n}\n\n/**\n * Convert a value to a string that is actually rendered.\n */\nfunction toString (val) {\n return val == null\n ? ''\n : Array.isArray(val) || (isPlainObject(val) && val.toString === _toString)\n ? JSON.stringify(val, null, 2)\n : String(val)\n}\n\n/**\n * Convert an input value to a number for persistence.\n * If the conversion fails, return original string.\n */\nfunction toNumber (val) {\n var n = parseFloat(val);\n return isNaN(n) ? val : n\n}\n\n/**\n * Make a map and return a function for checking if a key\n * is in that map.\n */\nfunction makeMap (\n str,\n expectsLowerCase\n) {\n var map = Object.create(null);\n var list = str.split(',');\n for (var i = 0; i < list.length; i++) {\n map[list[i]] = true;\n }\n return expectsLowerCase\n ? function (val) { return map[val.toLowerCase()]; }\n : function (val) { return map[val]; }\n}\n\n/**\n * Check if a tag is a built-in tag.\n */\nvar isBuiltInTag = makeMap('slot,component', true);\n\n/**\n * Check if an attribute is a reserved attribute.\n */\nvar isReservedAttribute = makeMap('key,ref,slot,slot-scope,is');\n\n/**\n * Remove an item from an array.\n */\nfunction remove (arr, item) {\n if (arr.length) {\n var index = arr.indexOf(item);\n if (index > -1) {\n return arr.splice(index, 1)\n }\n }\n}\n\n/**\n * Check whether an object has the property.\n */\nvar hasOwnProperty = Object.prototype.hasOwnProperty;\nfunction hasOwn (obj, key) {\n return hasOwnProperty.call(obj, key)\n}\n\n/**\n * Create a cached version of a pure function.\n */\nfunction cached (fn) {\n var cache = Object.create(null);\n return (function cachedFn (str) {\n var hit = cache[str];\n return hit || (cache[str] = fn(str))\n })\n}\n\n/**\n * Camelize a hyphen-delimited string.\n */\nvar camelizeRE = /-(\\w)/g;\nvar camelize = cached(function (str) {\n return str.replace(camelizeRE, function (_, c) { return c ? c.toUpperCase() : ''; })\n});\n\n/**\n * Capitalize a string.\n */\nvar capitalize = cached(function (str) {\n return str.charAt(0).toUpperCase() + str.slice(1)\n});\n\n/**\n * Hyphenate a camelCase string.\n */\nvar hyphenateRE = /\\B([A-Z])/g;\nvar hyphenate = cached(function (str) {\n return str.replace(hyphenateRE, '-$1').toLowerCase()\n});\n\n/**\n * Simple bind polyfill for environments that do not support it,\n * e.g., PhantomJS 1.x. Technically, we don't need this anymore\n * since native bind is now performant enough in most browsers.\n * But removing it would mean breaking code that was able to run in\n * PhantomJS 1.x, so this must be kept for backward compatibility.\n */\n\n/* istanbul ignore next */\nfunction polyfillBind (fn, ctx) {\n function boundFn (a) {\n var l = arguments.length;\n return l\n ? l > 1\n ? fn.apply(ctx, arguments)\n : fn.call(ctx, a)\n : fn.call(ctx)\n }\n\n boundFn._length = fn.length;\n return boundFn\n}\n\nfunction nativeBind (fn, ctx) {\n return fn.bind(ctx)\n}\n\nvar bind = Function.prototype.bind\n ? nativeBind\n : polyfillBind;\n\n/**\n * Convert an Array-like object to a real Array.\n */\nfunction toArray (list, start) {\n start = start || 0;\n var i = list.length - start;\n var ret = new Array(i);\n while (i--) {\n ret[i] = list[i + start];\n }\n return ret\n}\n\n/**\n * Mix properties into target object.\n */\nfunction extend (to, _from) {\n for (var key in _from) {\n to[key] = _from[key];\n }\n return to\n}\n\n/**\n * Merge an Array of Objects into a single Object.\n */\nfunction toObject (arr) {\n var res = {};\n for (var i = 0; i < arr.length; i++) {\n if (arr[i]) {\n extend(res, arr[i]);\n }\n }\n return res\n}\n\n/* eslint-disable no-unused-vars */\n\n/**\n * Perform no operation.\n * Stubbing args to make Flow happy without leaving useless transpiled code\n * with ...rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/).\n */\nfunction noop (a, b, c) {}\n\n/**\n * Always return false.\n */\nvar no = function (a, b, c) { return false; };\n\n/* eslint-enable no-unused-vars */\n\n/**\n * Return the same value.\n */\nvar identity = function (_) { return _; };\n\n/**\n * Generate a string containing static keys from compiler modules.\n */\nfunction genStaticKeys (modules) {\n return modules.reduce(function (keys, m) {\n return keys.concat(m.staticKeys || [])\n }, []).join(',')\n}\n\n/**\n * Check if two values are loosely equal - that is,\n * if they are plain objects, do they have the same shape?\n */\nfunction looseEqual (a, b) {\n if (a === b) { return true }\n var isObjectA = isObject(a);\n var isObjectB = isObject(b);\n if (isObjectA && isObjectB) {\n try {\n var isArrayA = Array.isArray(a);\n var isArrayB = Array.isArray(b);\n if (isArrayA && isArrayB) {\n return a.length === b.length && a.every(function (e, i) {\n return looseEqual(e, b[i])\n })\n } else if (a instanceof Date && b instanceof Date) {\n return a.getTime() === b.getTime()\n } else if (!isArrayA && !isArrayB) {\n var keysA = Object.keys(a);\n var keysB = Object.keys(b);\n return keysA.length === keysB.length && keysA.every(function (key) {\n return looseEqual(a[key], b[key])\n })\n } else {\n /* istanbul ignore next */\n return false\n }\n } catch (e) {\n /* istanbul ignore next */\n return false\n }\n } else if (!isObjectA && !isObjectB) {\n return String(a) === String(b)\n } else {\n return false\n }\n}\n\n/**\n * Return the first index at which a loosely equal value can be\n * found in the array (if value is a plain object, the array must\n * contain an object of the same shape), or -1 if it is not present.\n */\nfunction looseIndexOf (arr, val) {\n for (var i = 0; i < arr.length; i++) {\n if (looseEqual(arr[i], val)) { return i }\n }\n return -1\n}\n\n/**\n * Ensure a function is called only once.\n */\nfunction once (fn) {\n var called = false;\n return function () {\n if (!called) {\n called = true;\n fn.apply(this, arguments);\n }\n }\n}\n\nvar SSR_ATTR = 'data-server-rendered';\n\nvar ASSET_TYPES = [\n 'component',\n 'directive',\n 'filter'\n];\n\nvar LIFECYCLE_HOOKS = [\n 'beforeCreate',\n 'created',\n 'beforeMount',\n 'mounted',\n 'beforeUpdate',\n 'updated',\n 'beforeDestroy',\n 'destroyed',\n 'activated',\n 'deactivated',\n 'errorCaptured',\n 'serverPrefetch'\n];\n\n/* */\n\n\n\nvar config = ({\n /**\n * Option merge strategies (used in core/util/options)\n */\n // $flow-disable-line\n optionMergeStrategies: Object.create(null),\n\n /**\n * Whether to suppress warnings.\n */\n silent: false,\n\n /**\n * Show production mode tip message on boot?\n */\n productionTip: process.env.NODE_ENV !== 'production',\n\n /**\n * Whether to enable devtools\n */\n devtools: process.env.NODE_ENV !== 'production',\n\n /**\n * Whether to record perf\n */\n performance: false,\n\n /**\n * Error handler for watcher errors\n */\n errorHandler: null,\n\n /**\n * Warn handler for watcher warns\n */\n warnHandler: null,\n\n /**\n * Ignore certain custom elements\n */\n ignoredElements: [],\n\n /**\n * Custom user key aliases for v-on\n */\n // $flow-disable-line\n keyCodes: Object.create(null),\n\n /**\n * Check if a tag is reserved so that it cannot be registered as a\n * component. This is platform-dependent and may be overwritten.\n */\n isReservedTag: no,\n\n /**\n * Check if an attribute is reserved so that it cannot be used as a component\n * prop. This is platform-dependent and may be overwritten.\n */\n isReservedAttr: no,\n\n /**\n * Check if a tag is an unknown element.\n * Platform-dependent.\n */\n isUnknownElement: no,\n\n /**\n * Get the namespace of an element\n */\n getTagNamespace: noop,\n\n /**\n * Parse the real tag name for the specific platform.\n */\n parsePlatformTagName: identity,\n\n /**\n * Check if an attribute must be bound using property, e.g. value\n * Platform-dependent.\n */\n mustUseProp: no,\n\n /**\n * Perform updates asynchronously. Intended to be used by Vue Test Utils\n * This will significantly reduce performance if set to false.\n */\n async: true,\n\n /**\n * Exposed for legacy reasons\n */\n _lifecycleHooks: LIFECYCLE_HOOKS\n});\n\n/* */\n\n/**\n * unicode letters used for parsing html tags, component names and property paths.\n * using https://www.w3.org/TR/html53/semantics-scripting.html#potentialcustomelementname\n * skipping \\u10000-\\uEFFFF due to it freezing up PhantomJS\n */\nvar unicodeRegExp = /a-zA-Z\\u00B7\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u203F-\\u2040\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD/;\n\n/**\n * Check if a string starts with $ or _\n */\nfunction isReserved (str) {\n var c = (str + '').charCodeAt(0);\n return c === 0x24 || c === 0x5F\n}\n\n/**\n * Define a property.\n */\nfunction def (obj, key, val, enumerable) {\n Object.defineProperty(obj, key, {\n value: val,\n enumerable: !!enumerable,\n writable: true,\n configurable: true\n });\n}\n\n/**\n * Parse simple path.\n */\nvar bailRE = new RegExp((\"[^\" + (unicodeRegExp.source) + \".$_\\\\d]\"));\nfunction parsePath (path) {\n if (bailRE.test(path)) {\n return\n }\n var segments = path.split('.');\n return function (obj) {\n for (var i = 0; i < segments.length; i++) {\n if (!obj) { return }\n obj = obj[segments[i]];\n }\n return obj\n }\n}\n\n/* */\n\n// can we use __proto__?\nvar hasProto = '__proto__' in {};\n\n// Browser environment sniffing\nvar inBrowser = typeof window !== 'undefined';\nvar inWeex = typeof WXEnvironment !== 'undefined' && !!WXEnvironment.platform;\nvar weexPlatform = inWeex && WXEnvironment.platform.toLowerCase();\nvar UA = inBrowser && window.navigator.userAgent.toLowerCase();\nvar isIE = UA && /msie|trident/.test(UA);\nvar isIE9 = UA && UA.indexOf('msie 9.0') > 0;\nvar isEdge = UA && UA.indexOf('edge/') > 0;\nvar isAndroid = (UA && UA.indexOf('android') > 0) || (weexPlatform === 'android');\nvar isIOS = (UA && /iphone|ipad|ipod|ios/.test(UA)) || (weexPlatform === 'ios');\nvar isChrome = UA && /chrome\\/\\d+/.test(UA) && !isEdge;\nvar isPhantomJS = UA && /phantomjs/.test(UA);\nvar isFF = UA && UA.match(/firefox\\/(\\d+)/);\n\n// Firefox has a \"watch\" function on Object.prototype...\nvar nativeWatch = ({}).watch;\n\nvar supportsPassive = false;\nif (inBrowser) {\n try {\n var opts = {};\n Object.defineProperty(opts, 'passive', ({\n get: function get () {\n /* istanbul ignore next */\n supportsPassive = true;\n }\n })); // https://github.com/facebook/flow/issues/285\n window.addEventListener('test-passive', null, opts);\n } catch (e) {}\n}\n\n// this needs to be lazy-evaled because vue may be required before\n// vue-server-renderer can set VUE_ENV\nvar _isServer;\nvar isServerRendering = function () {\n if (_isServer === undefined) {\n /* istanbul ignore if */\n if (!inBrowser && !inWeex && typeof global !== 'undefined') {\n // detect presence of vue-server-renderer and avoid\n // Webpack shimming the process\n _isServer = global['process'] && global['process'].env.VUE_ENV === 'server';\n } else {\n _isServer = false;\n }\n }\n return _isServer\n};\n\n// detect devtools\nvar devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__;\n\n/* istanbul ignore next */\nfunction isNative (Ctor) {\n return typeof Ctor === 'function' && /native code/.test(Ctor.toString())\n}\n\nvar hasSymbol =\n typeof Symbol !== 'undefined' && isNative(Symbol) &&\n typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys);\n\nvar _Set;\n/* istanbul ignore if */ // $flow-disable-line\nif (typeof Set !== 'undefined' && isNative(Set)) {\n // use native Set when available.\n _Set = Set;\n} else {\n // a non-standard Set polyfill that only works with primitive keys.\n _Set = /*@__PURE__*/(function () {\n function Set () {\n this.set = Object.create(null);\n }\n Set.prototype.has = function has (key) {\n return this.set[key] === true\n };\n Set.prototype.add = function add (key) {\n this.set[key] = true;\n };\n Set.prototype.clear = function clear () {\n this.set = Object.create(null);\n };\n\n return Set;\n }());\n}\n\n/* */\n\nvar warn = noop;\nvar tip = noop;\nvar generateComponentTrace = (noop); // work around flow check\nvar formatComponentName = (noop);\n\nif (process.env.NODE_ENV !== 'production') {\n var hasConsole = typeof console !== 'undefined';\n var classifyRE = /(?:^|[-_])(\\w)/g;\n var classify = function (str) { return str\n .replace(classifyRE, function (c) { return c.toUpperCase(); })\n .replace(/[-_]/g, ''); };\n\n warn = function (msg, vm) {\n var trace = vm ? generateComponentTrace(vm) : '';\n\n if (config.warnHandler) {\n config.warnHandler.call(null, msg, vm, trace);\n } else if (hasConsole && (!config.silent)) {\n console.error((\"[Vue warn]: \" + msg + trace));\n }\n };\n\n tip = function (msg, vm) {\n if (hasConsole && (!config.silent)) {\n console.warn(\"[Vue tip]: \" + msg + (\n vm ? generateComponentTrace(vm) : ''\n ));\n }\n };\n\n formatComponentName = function (vm, includeFile) {\n if (vm.$root === vm) {\n return ''\n }\n var options = typeof vm === 'function' && vm.cid != null\n ? vm.options\n : vm._isVue\n ? vm.$options || vm.constructor.options\n : vm;\n var name = options.name || options._componentTag;\n var file = options.__file;\n if (!name && file) {\n var match = file.match(/([^/\\\\]+)\\.vue$/);\n name = match && match[1];\n }\n\n return (\n (name ? (\"<\" + (classify(name)) + \">\") : \"\") +\n (file && includeFile !== false ? (\" at \" + file) : '')\n )\n };\n\n var repeat = function (str, n) {\n var res = '';\n while (n) {\n if (n % 2 === 1) { res += str; }\n if (n > 1) { str += str; }\n n >>= 1;\n }\n return res\n };\n\n generateComponentTrace = function (vm) {\n if (vm._isVue && vm.$parent) {\n var tree = [];\n var currentRecursiveSequence = 0;\n while (vm) {\n if (tree.length > 0) {\n var last = tree[tree.length - 1];\n if (last.constructor === vm.constructor) {\n currentRecursiveSequence++;\n vm = vm.$parent;\n continue\n } else if (currentRecursiveSequence > 0) {\n tree[tree.length - 1] = [last, currentRecursiveSequence];\n currentRecursiveSequence = 0;\n }\n }\n tree.push(vm);\n vm = vm.$parent;\n }\n return '\\n\\nfound in\\n\\n' + tree\n .map(function (vm, i) { return (\"\" + (i === 0 ? '---> ' : repeat(' ', 5 + i * 2)) + (Array.isArray(vm)\n ? ((formatComponentName(vm[0])) + \"... (\" + (vm[1]) + \" recursive calls)\")\n : formatComponentName(vm))); })\n .join('\\n')\n } else {\n return (\"\\n\\n(found in \" + (formatComponentName(vm)) + \")\")\n }\n };\n}\n\n/* */\n\nvar uid = 0;\n\n/**\n * A dep is an observable that can have multiple\n * directives subscribing to it.\n */\nvar Dep = function Dep () {\n this.id = uid++;\n this.subs = [];\n};\n\nDep.prototype.addSub = function addSub (sub) {\n this.subs.push(sub);\n};\n\nDep.prototype.removeSub = function removeSub (sub) {\n remove(this.subs, sub);\n};\n\nDep.prototype.depend = function depend () {\n if (Dep.target) {\n Dep.target.addDep(this);\n }\n};\n\nDep.prototype.notify = function notify () {\n // stabilize the subscriber list first\n var subs = this.subs.slice();\n if (process.env.NODE_ENV !== 'production' && !config.async) {\n // subs aren't sorted in scheduler if not running async\n // we need to sort them now to make sure they fire in correct\n // order\n subs.sort(function (a, b) { return a.id - b.id; });\n }\n for (var i = 0, l = subs.length; i < l; i++) {\n subs[i].update();\n }\n};\n\n// The current target watcher being evaluated.\n// This is globally unique because only one watcher\n// can be evaluated at a time.\nDep.target = null;\nvar targetStack = [];\n\nfunction pushTarget (target) {\n targetStack.push(target);\n Dep.target = target;\n}\n\nfunction popTarget () {\n targetStack.pop();\n Dep.target = targetStack[targetStack.length - 1];\n}\n\n/* */\n\nvar VNode = function VNode (\n tag,\n data,\n children,\n text,\n elm,\n context,\n componentOptions,\n asyncFactory\n) {\n this.tag = tag;\n this.data = data;\n this.children = children;\n this.text = text;\n this.elm = elm;\n this.ns = undefined;\n this.context = context;\n this.fnContext = undefined;\n this.fnOptions = undefined;\n this.fnScopeId = undefined;\n this.key = data && data.key;\n this.componentOptions = componentOptions;\n this.componentInstance = undefined;\n this.parent = undefined;\n this.raw = false;\n this.isStatic = false;\n this.isRootInsert = true;\n this.isComment = false;\n this.isCloned = false;\n this.isOnce = false;\n this.asyncFactory = asyncFactory;\n this.asyncMeta = undefined;\n this.isAsyncPlaceholder = false;\n};\n\nvar prototypeAccessors = { child: { configurable: true } };\n\n// DEPRECATED: alias for componentInstance for backwards compat.\n/* istanbul ignore next */\nprototypeAccessors.child.get = function () {\n return this.componentInstance\n};\n\nObject.defineProperties( VNode.prototype, prototypeAccessors );\n\nvar createEmptyVNode = function (text) {\n if ( text === void 0 ) text = '';\n\n var node = new VNode();\n node.text = text;\n node.isComment = true;\n return node\n};\n\nfunction createTextVNode (val) {\n return new VNode(undefined, undefined, undefined, String(val))\n}\n\n// optimized shallow clone\n// used for static nodes and slot nodes because they may be reused across\n// multiple renders, cloning them avoids errors when DOM manipulations rely\n// on their elm reference.\nfunction cloneVNode (vnode) {\n var cloned = new VNode(\n vnode.tag,\n vnode.data,\n // #7975\n // clone children array to avoid mutating original in case of cloning\n // a child.\n vnode.children && vnode.children.slice(),\n vnode.text,\n vnode.elm,\n vnode.context,\n vnode.componentOptions,\n vnode.asyncFactory\n );\n cloned.ns = vnode.ns;\n cloned.isStatic = vnode.isStatic;\n cloned.key = vnode.key;\n cloned.isComment = vnode.isComment;\n cloned.fnContext = vnode.fnContext;\n cloned.fnOptions = vnode.fnOptions;\n cloned.fnScopeId = vnode.fnScopeId;\n cloned.asyncMeta = vnode.asyncMeta;\n cloned.isCloned = true;\n return cloned\n}\n\n/*\n * not type checking this file because flow doesn't play well with\n * dynamically accessing methods on Array prototype\n */\n\nvar arrayProto = Array.prototype;\nvar arrayMethods = Object.create(arrayProto);\n\nvar methodsToPatch = [\n 'push',\n 'pop',\n 'shift',\n 'unshift',\n 'splice',\n 'sort',\n 'reverse'\n];\n\n/**\n * Intercept mutating methods and emit events\n */\nmethodsToPatch.forEach(function (method) {\n // cache original method\n var original = arrayProto[method];\n def(arrayMethods, method, function mutator () {\n var args = [], len = arguments.length;\n while ( len-- ) args[ len ] = arguments[ len ];\n\n var result = original.apply(this, args);\n var ob = this.__ob__;\n var inserted;\n switch (method) {\n case 'push':\n case 'unshift':\n inserted = args;\n break\n case 'splice':\n inserted = args.slice(2);\n break\n }\n if (inserted) { ob.observeArray(inserted); }\n // notify change\n ob.dep.notify();\n return result\n });\n});\n\n/* */\n\nvar arrayKeys = Object.getOwnPropertyNames(arrayMethods);\n\n/**\n * In some cases we may want to disable observation inside a component's\n * update computation.\n */\nvar shouldObserve = true;\n\nfunction toggleObserving (value) {\n shouldObserve = value;\n}\n\n/**\n * Observer class that is attached to each observed\n * object. Once attached, the observer converts the target\n * object's property keys into getter/setters that\n * collect dependencies and dispatch updates.\n */\nvar Observer = function Observer (value) {\n this.value = value;\n this.dep = new Dep();\n this.vmCount = 0;\n def(value, '__ob__', this);\n if (Array.isArray(value)) {\n if (hasProto) {\n protoAugment(value, arrayMethods);\n } else {\n copyAugment(value, arrayMethods, arrayKeys);\n }\n this.observeArray(value);\n } else {\n this.walk(value);\n }\n};\n\n/**\n * Walk through all properties and convert them into\n * getter/setters. This method should only be called when\n * value type is Object.\n */\nObserver.prototype.walk = function walk (obj) {\n var keys = Object.keys(obj);\n for (var i = 0; i < keys.length; i++) {\n defineReactive$$1(obj, keys[i]);\n }\n};\n\n/**\n * Observe a list of Array items.\n */\nObserver.prototype.observeArray = function observeArray (items) {\n for (var i = 0, l = items.length; i < l; i++) {\n observe(items[i]);\n }\n};\n\n// helpers\n\n/**\n * Augment a target Object or Array by intercepting\n * the prototype chain using __proto__\n */\nfunction protoAugment (target, src) {\n /* eslint-disable no-proto */\n target.__proto__ = src;\n /* eslint-enable no-proto */\n}\n\n/**\n * Augment a target Object or Array by defining\n * hidden properties.\n */\n/* istanbul ignore next */\nfunction copyAugment (target, src, keys) {\n for (var i = 0, l = keys.length; i < l; i++) {\n var key = keys[i];\n def(target, key, src[key]);\n }\n}\n\n/**\n * Attempt to create an observer instance for a value,\n * returns the new observer if successfully observed,\n * or the existing observer if the value already has one.\n */\nfunction observe (value, asRootData) {\n if (!isObject(value) || value instanceof VNode) {\n return\n }\n var ob;\n if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {\n ob = value.__ob__;\n } else if (\n shouldObserve &&\n !isServerRendering() &&\n (Array.isArray(value) || isPlainObject(value)) &&\n Object.isExtensible(value) &&\n !value._isVue\n ) {\n ob = new Observer(value);\n }\n if (asRootData && ob) {\n ob.vmCount++;\n }\n return ob\n}\n\n/**\n * Define a reactive property on an Object.\n */\nfunction defineReactive$$1 (\n obj,\n key,\n val,\n customSetter,\n shallow\n) {\n var dep = new Dep();\n\n var property = Object.getOwnPropertyDescriptor(obj, key);\n if (property && property.configurable === false) {\n return\n }\n\n // cater for pre-defined getter/setters\n var getter = property && property.get;\n var setter = property && property.set;\n if ((!getter || setter) && arguments.length === 2) {\n val = obj[key];\n }\n\n var childOb = !shallow && observe(val);\n Object.defineProperty(obj, key, {\n enumerable: true,\n configurable: true,\n get: function reactiveGetter () {\n var value = getter ? getter.call(obj) : val;\n if (Dep.target) {\n dep.depend();\n if (childOb) {\n childOb.dep.depend();\n if (Array.isArray(value)) {\n dependArray(value);\n }\n }\n }\n return value\n },\n set: function reactiveSetter (newVal) {\n var value = getter ? getter.call(obj) : val;\n /* eslint-disable no-self-compare */\n if (newVal === value || (newVal !== newVal && value !== value)) {\n return\n }\n /* eslint-enable no-self-compare */\n if (process.env.NODE_ENV !== 'production' && customSetter) {\n customSetter();\n }\n // #7981: for accessor properties without setter\n if (getter && !setter) { return }\n if (setter) {\n setter.call(obj, newVal);\n } else {\n val = newVal;\n }\n childOb = !shallow && observe(newVal);\n dep.notify();\n }\n });\n}\n\n/**\n * Set a property on an object. Adds the new property and\n * triggers change notification if the property doesn't\n * already exist.\n */\nfunction set (target, key, val) {\n if (process.env.NODE_ENV !== 'production' &&\n (isUndef(target) || isPrimitive(target))\n ) {\n warn((\"Cannot set reactive property on undefined, null, or primitive value: \" + ((target))));\n }\n if (Array.isArray(target) && isValidArrayIndex(key)) {\n target.length = Math.max(target.length, key);\n target.splice(key, 1, val);\n return val\n }\n if (key in target && !(key in Object.prototype)) {\n target[key] = val;\n return val\n }\n var ob = (target).__ob__;\n if (target._isVue || (ob && ob.vmCount)) {\n process.env.NODE_ENV !== 'production' && warn(\n 'Avoid adding reactive properties to a Vue instance or its root $data ' +\n 'at runtime - declare it upfront in the data option.'\n );\n return val\n }\n if (!ob) {\n target[key] = val;\n return val\n }\n defineReactive$$1(ob.value, key, val);\n ob.dep.notify();\n return val\n}\n\n/**\n * Delete a property and trigger change if necessary.\n */\nfunction del (target, key) {\n if (process.env.NODE_ENV !== 'production' &&\n (isUndef(target) || isPrimitive(target))\n ) {\n warn((\"Cannot delete reactive property on undefined, null, or primitive value: \" + ((target))));\n }\n if (Array.isArray(target) && isValidArrayIndex(key)) {\n target.splice(key, 1);\n return\n }\n var ob = (target).__ob__;\n if (target._isVue || (ob && ob.vmCount)) {\n process.env.NODE_ENV !== 'production' && warn(\n 'Avoid deleting properties on a Vue instance or its root $data ' +\n '- just set it to null.'\n );\n return\n }\n if (!hasOwn(target, key)) {\n return\n }\n delete target[key];\n if (!ob) {\n return\n }\n ob.dep.notify();\n}\n\n/**\n * Collect dependencies on array elements when the array is touched, since\n * we cannot intercept array element access like property getters.\n */\nfunction dependArray (value) {\n for (var e = (void 0), i = 0, l = value.length; i < l; i++) {\n e = value[i];\n e && e.__ob__ && e.__ob__.dep.depend();\n if (Array.isArray(e)) {\n dependArray(e);\n }\n }\n}\n\n/* */\n\n/**\n * Option overwriting strategies are functions that handle\n * how to merge a parent option value and a child option\n * value into the final value.\n */\nvar strats = config.optionMergeStrategies;\n\n/**\n * Options with restrictions\n */\nif (process.env.NODE_ENV !== 'production') {\n strats.el = strats.propsData = function (parent, child, vm, key) {\n if (!vm) {\n warn(\n \"option \\\"\" + key + \"\\\" can only be used during instance \" +\n 'creation with the `new` keyword.'\n );\n }\n return defaultStrat(parent, child)\n };\n}\n\n/**\n * Helper that recursively merges two data objects together.\n */\nfunction mergeData (to, from) {\n if (!from) { return to }\n var key, toVal, fromVal;\n\n var keys = hasSymbol\n ? Reflect.ownKeys(from)\n : Object.keys(from);\n\n for (var i = 0; i < keys.length; i++) {\n key = keys[i];\n // in case the object is already observed...\n if (key === '__ob__') { continue }\n toVal = to[key];\n fromVal = from[key];\n if (!hasOwn(to, key)) {\n set(to, key, fromVal);\n } else if (\n toVal !== fromVal &&\n isPlainObject(toVal) &&\n isPlainObject(fromVal)\n ) {\n mergeData(toVal, fromVal);\n }\n }\n return to\n}\n\n/**\n * Data\n */\nfunction mergeDataOrFn (\n parentVal,\n childVal,\n vm\n) {\n if (!vm) {\n // in a Vue.extend merge, both should be functions\n if (!childVal) {\n return parentVal\n }\n if (!parentVal) {\n return childVal\n }\n // when parentVal & childVal are both present,\n // we need to return a function that returns the\n // merged result of both functions... no need to\n // check if parentVal is a function here because\n // it has to be a function to pass previous merges.\n return function mergedDataFn () {\n return mergeData(\n typeof childVal === 'function' ? childVal.call(this, this) : childVal,\n typeof parentVal === 'function' ? parentVal.call(this, this) : parentVal\n )\n }\n } else {\n return function mergedInstanceDataFn () {\n // instance merge\n var instanceData = typeof childVal === 'function'\n ? childVal.call(vm, vm)\n : childVal;\n var defaultData = typeof parentVal === 'function'\n ? parentVal.call(vm, vm)\n : parentVal;\n if (instanceData) {\n return mergeData(instanceData, defaultData)\n } else {\n return defaultData\n }\n }\n }\n}\n\nstrats.data = function (\n parentVal,\n childVal,\n vm\n) {\n if (!vm) {\n if (childVal && typeof childVal !== 'function') {\n process.env.NODE_ENV !== 'production' && warn(\n 'The \"data\" option should be a function ' +\n 'that returns a per-instance value in component ' +\n 'definitions.',\n vm\n );\n\n return parentVal\n }\n return mergeDataOrFn(parentVal, childVal)\n }\n\n return mergeDataOrFn(parentVal, childVal, vm)\n};\n\n/**\n * Hooks and props are merged as arrays.\n */\nfunction mergeHook (\n parentVal,\n childVal\n) {\n var res = childVal\n ? parentVal\n ? parentVal.concat(childVal)\n : Array.isArray(childVal)\n ? childVal\n : [childVal]\n : parentVal;\n return res\n ? dedupeHooks(res)\n : res\n}\n\nfunction dedupeHooks (hooks) {\n var res = [];\n for (var i = 0; i < hooks.length; i++) {\n if (res.indexOf(hooks[i]) === -1) {\n res.push(hooks[i]);\n }\n }\n return res\n}\n\nLIFECYCLE_HOOKS.forEach(function (hook) {\n strats[hook] = mergeHook;\n});\n\n/**\n * Assets\n *\n * When a vm is present (instance creation), we need to do\n * a three-way merge between constructor options, instance\n * options and parent options.\n */\nfunction mergeAssets (\n parentVal,\n childVal,\n vm,\n key\n) {\n var res = Object.create(parentVal || null);\n if (childVal) {\n process.env.NODE_ENV !== 'production' && assertObjectType(key, childVal, vm);\n return extend(res, childVal)\n } else {\n return res\n }\n}\n\nASSET_TYPES.forEach(function (type) {\n strats[type + 's'] = mergeAssets;\n});\n\n/**\n * Watchers.\n *\n * Watchers hashes should not overwrite one\n * another, so we merge them as arrays.\n */\nstrats.watch = function (\n parentVal,\n childVal,\n vm,\n key\n) {\n // work around Firefox's Object.prototype.watch...\n if (parentVal === nativeWatch) { parentVal = undefined; }\n if (childVal === nativeWatch) { childVal = undefined; }\n /* istanbul ignore if */\n if (!childVal) { return Object.create(parentVal || null) }\n if (process.env.NODE_ENV !== 'production') {\n assertObjectType(key, childVal, vm);\n }\n if (!parentVal) { return childVal }\n var ret = {};\n extend(ret, parentVal);\n for (var key$1 in childVal) {\n var parent = ret[key$1];\n var child = childVal[key$1];\n if (parent && !Array.isArray(parent)) {\n parent = [parent];\n }\n ret[key$1] = parent\n ? parent.concat(child)\n : Array.isArray(child) ? child : [child];\n }\n return ret\n};\n\n/**\n * Other object hashes.\n */\nstrats.props =\nstrats.methods =\nstrats.inject =\nstrats.computed = function (\n parentVal,\n childVal,\n vm,\n key\n) {\n if (childVal && process.env.NODE_ENV !== 'production') {\n assertObjectType(key, childVal, vm);\n }\n if (!parentVal) { return childVal }\n var ret = Object.create(null);\n extend(ret, parentVal);\n if (childVal) { extend(ret, childVal); }\n return ret\n};\nstrats.provide = mergeDataOrFn;\n\n/**\n * Default strategy.\n */\nvar defaultStrat = function (parentVal, childVal) {\n return childVal === undefined\n ? parentVal\n : childVal\n};\n\n/**\n * Validate component names\n */\nfunction checkComponents (options) {\n for (var key in options.components) {\n validateComponentName(key);\n }\n}\n\nfunction validateComponentName (name) {\n if (!new RegExp((\"^[a-zA-Z][\\\\-\\\\.0-9_\" + (unicodeRegExp.source) + \"]*$\")).test(name)) {\n warn(\n 'Invalid component name: \"' + name + '\". Component names ' +\n 'should conform to valid custom element name in html5 specification.'\n );\n }\n if (isBuiltInTag(name) || config.isReservedTag(name)) {\n warn(\n 'Do not use built-in or reserved HTML elements as component ' +\n 'id: ' + name\n );\n }\n}\n\n/**\n * Ensure all props option syntax are normalized into the\n * Object-based format.\n */\nfunction normalizeProps (options, vm) {\n var props = options.props;\n if (!props) { return }\n var res = {};\n var i, val, name;\n if (Array.isArray(props)) {\n i = props.length;\n while (i--) {\n val = props[i];\n if (typeof val === 'string') {\n name = camelize(val);\n res[name] = { type: null };\n } else if (process.env.NODE_ENV !== 'production') {\n warn('props must be strings when using array syntax.');\n }\n }\n } else if (isPlainObject(props)) {\n for (var key in props) {\n val = props[key];\n name = camelize(key);\n res[name] = isPlainObject(val)\n ? val\n : { type: val };\n }\n } else if (process.env.NODE_ENV !== 'production') {\n warn(\n \"Invalid value for option \\\"props\\\": expected an Array or an Object, \" +\n \"but got \" + (toRawType(props)) + \".\",\n vm\n );\n }\n options.props = res;\n}\n\n/**\n * Normalize all injections into Object-based format\n */\nfunction normalizeInject (options, vm) {\n var inject = options.inject;\n if (!inject) { return }\n var normalized = options.inject = {};\n if (Array.isArray(inject)) {\n for (var i = 0; i < inject.length; i++) {\n normalized[inject[i]] = { from: inject[i] };\n }\n } else if (isPlainObject(inject)) {\n for (var key in inject) {\n var val = inject[key];\n normalized[key] = isPlainObject(val)\n ? extend({ from: key }, val)\n : { from: val };\n }\n } else if (process.env.NODE_ENV !== 'production') {\n warn(\n \"Invalid value for option \\\"inject\\\": expected an Array or an Object, \" +\n \"but got \" + (toRawType(inject)) + \".\",\n vm\n );\n }\n}\n\n/**\n * Normalize raw function directives into object format.\n */\nfunction normalizeDirectives (options) {\n var dirs = options.directives;\n if (dirs) {\n for (var key in dirs) {\n var def$$1 = dirs[key];\n if (typeof def$$1 === 'function') {\n dirs[key] = { bind: def$$1, update: def$$1 };\n }\n }\n }\n}\n\nfunction assertObjectType (name, value, vm) {\n if (!isPlainObject(value)) {\n warn(\n \"Invalid value for option \\\"\" + name + \"\\\": expected an Object, \" +\n \"but got \" + (toRawType(value)) + \".\",\n vm\n );\n }\n}\n\n/**\n * Merge two option objects into a new one.\n * Core utility used in both instantiation and inheritance.\n */\nfunction mergeOptions (\n parent,\n child,\n vm\n) {\n if (process.env.NODE_ENV !== 'production') {\n checkComponents(child);\n }\n\n if (typeof child === 'function') {\n child = child.options;\n }\n\n normalizeProps(child, vm);\n normalizeInject(child, vm);\n normalizeDirectives(child);\n\n // Apply extends and mixins on the child options,\n // but only if it is a raw options object that isn't\n // the result of another mergeOptions call.\n // Only merged options has the _base property.\n if (!child._base) {\n if (child.extends) {\n parent = mergeOptions(parent, child.extends, vm);\n }\n if (child.mixins) {\n for (var i = 0, l = child.mixins.length; i < l; i++) {\n parent = mergeOptions(parent, child.mixins[i], vm);\n }\n }\n }\n\n var options = {};\n var key;\n for (key in parent) {\n mergeField(key);\n }\n for (key in child) {\n if (!hasOwn(parent, key)) {\n mergeField(key);\n }\n }\n function mergeField (key) {\n var strat = strats[key] || defaultStrat;\n options[key] = strat(parent[key], child[key], vm, key);\n }\n return options\n}\n\n/**\n * Resolve an asset.\n * This function is used because child instances need access\n * to assets defined in its ancestor chain.\n */\nfunction resolveAsset (\n options,\n type,\n id,\n warnMissing\n) {\n /* istanbul ignore if */\n if (typeof id !== 'string') {\n return\n }\n var assets = options[type];\n // check local registration variations first\n if (hasOwn(assets, id)) { return assets[id] }\n var camelizedId = camelize(id);\n if (hasOwn(assets, camelizedId)) { return assets[camelizedId] }\n var PascalCaseId = capitalize(camelizedId);\n if (hasOwn(assets, PascalCaseId)) { return assets[PascalCaseId] }\n // fallback to prototype chain\n var res = assets[id] || assets[camelizedId] || assets[PascalCaseId];\n if (process.env.NODE_ENV !== 'production' && warnMissing && !res) {\n warn(\n 'Failed to resolve ' + type.slice(0, -1) + ': ' + id,\n options\n );\n }\n return res\n}\n\n/* */\n\n\n\nfunction validateProp (\n key,\n propOptions,\n propsData,\n vm\n) {\n var prop = propOptions[key];\n var absent = !hasOwn(propsData, key);\n var value = propsData[key];\n // boolean casting\n var booleanIndex = getTypeIndex(Boolean, prop.type);\n if (booleanIndex > -1) {\n if (absent && !hasOwn(prop, 'default')) {\n value = false;\n } else if (value === '' || value === hyphenate(key)) {\n // only cast empty string / same name to boolean if\n // boolean has higher priority\n var stringIndex = getTypeIndex(String, prop.type);\n if (stringIndex < 0 || booleanIndex < stringIndex) {\n value = true;\n }\n }\n }\n // check default value\n if (value === undefined) {\n value = getPropDefaultValue(vm, prop, key);\n // since the default value is a fresh copy,\n // make sure to observe it.\n var prevShouldObserve = shouldObserve;\n toggleObserving(true);\n observe(value);\n toggleObserving(prevShouldObserve);\n }\n if (\n process.env.NODE_ENV !== 'production' &&\n // skip validation for weex recycle-list child component props\n !(false)\n ) {\n assertProp(prop, key, value, vm, absent);\n }\n return value\n}\n\n/**\n * Get the default value of a prop.\n */\nfunction getPropDefaultValue (vm, prop, key) {\n // no default, return undefined\n if (!hasOwn(prop, 'default')) {\n return undefined\n }\n var def = prop.default;\n // warn against non-factory defaults for Object & Array\n if (process.env.NODE_ENV !== 'production' && isObject(def)) {\n warn(\n 'Invalid default value for prop \"' + key + '\": ' +\n 'Props with type Object/Array must use a factory function ' +\n 'to return the default value.',\n vm\n );\n }\n // the raw prop value was also undefined from previous render,\n // return previous default value to avoid unnecessary watcher trigger\n if (vm && vm.$options.propsData &&\n vm.$options.propsData[key] === undefined &&\n vm._props[key] !== undefined\n ) {\n return vm._props[key]\n }\n // call factory function for non-Function types\n // a value is Function if its prototype is function even across different execution context\n return typeof def === 'function' && getType(prop.type) !== 'Function'\n ? def.call(vm)\n : def\n}\n\n/**\n * Assert whether a prop is valid.\n */\nfunction assertProp (\n prop,\n name,\n value,\n vm,\n absent\n) {\n if (prop.required && absent) {\n warn(\n 'Missing required prop: \"' + name + '\"',\n vm\n );\n return\n }\n if (value == null && !prop.required) {\n return\n }\n var type = prop.type;\n var valid = !type || type === true;\n var expectedTypes = [];\n if (type) {\n if (!Array.isArray(type)) {\n type = [type];\n }\n for (var i = 0; i < type.length && !valid; i++) {\n var assertedType = assertType(value, type[i]);\n expectedTypes.push(assertedType.expectedType || '');\n valid = assertedType.valid;\n }\n }\n\n if (!valid) {\n warn(\n getInvalidTypeMessage(name, value, expectedTypes),\n vm\n );\n return\n }\n var validator = prop.validator;\n if (validator) {\n if (!validator(value)) {\n warn(\n 'Invalid prop: custom validator check failed for prop \"' + name + '\".',\n vm\n );\n }\n }\n}\n\nvar simpleCheckRE = /^(String|Number|Boolean|Function|Symbol)$/;\n\nfunction assertType (value, type) {\n var valid;\n var expectedType = getType(type);\n if (simpleCheckRE.test(expectedType)) {\n var t = typeof value;\n valid = t === expectedType.toLowerCase();\n // for primitive wrapper objects\n if (!valid && t === 'object') {\n valid = value instanceof type;\n }\n } else if (expectedType === 'Object') {\n valid = isPlainObject(value);\n } else if (expectedType === 'Array') {\n valid = Array.isArray(value);\n } else {\n valid = value instanceof type;\n }\n return {\n valid: valid,\n expectedType: expectedType\n }\n}\n\n/**\n * Use function string name to check built-in types,\n * because a simple equality check will fail when running\n * across different vms / iframes.\n */\nfunction getType (fn) {\n var match = fn && fn.toString().match(/^\\s*function (\\w+)/);\n return match ? match[1] : ''\n}\n\nfunction isSameType (a, b) {\n return getType(a) === getType(b)\n}\n\nfunction getTypeIndex (type, expectedTypes) {\n if (!Array.isArray(expectedTypes)) {\n return isSameType(expectedTypes, type) ? 0 : -1\n }\n for (var i = 0, len = expectedTypes.length; i < len; i++) {\n if (isSameType(expectedTypes[i], type)) {\n return i\n }\n }\n return -1\n}\n\nfunction getInvalidTypeMessage (name, value, expectedTypes) {\n var message = \"Invalid prop: type check failed for prop \\\"\" + name + \"\\\".\" +\n \" Expected \" + (expectedTypes.map(capitalize).join(', '));\n var expectedType = expectedTypes[0];\n var receivedType = toRawType(value);\n var expectedValue = styleValue(value, expectedType);\n var receivedValue = styleValue(value, receivedType);\n // check if we need to specify expected value\n if (expectedTypes.length === 1 &&\n isExplicable(expectedType) &&\n !isBoolean(expectedType, receivedType)) {\n message += \" with value \" + expectedValue;\n }\n message += \", got \" + receivedType + \" \";\n // check if we need to specify received value\n if (isExplicable(receivedType)) {\n message += \"with value \" + receivedValue + \".\";\n }\n return message\n}\n\nfunction styleValue (value, type) {\n if (type === 'String') {\n return (\"\\\"\" + value + \"\\\"\")\n } else if (type === 'Number') {\n return (\"\" + (Number(value)))\n } else {\n return (\"\" + value)\n }\n}\n\nfunction isExplicable (value) {\n var explicitTypes = ['string', 'number', 'boolean'];\n return explicitTypes.some(function (elem) { return value.toLowerCase() === elem; })\n}\n\nfunction isBoolean () {\n var args = [], len = arguments.length;\n while ( len-- ) args[ len ] = arguments[ len ];\n\n return args.some(function (elem) { return elem.toLowerCase() === 'boolean'; })\n}\n\n/* */\n\nfunction handleError (err, vm, info) {\n // Deactivate deps tracking while processing error handler to avoid possible infinite rendering.\n // See: https://github.com/vuejs/vuex/issues/1505\n pushTarget();\n try {\n if (vm) {\n var cur = vm;\n while ((cur = cur.$parent)) {\n var hooks = cur.$options.errorCaptured;\n if (hooks) {\n for (var i = 0; i < hooks.length; i++) {\n try {\n var capture = hooks[i].call(cur, err, vm, info) === false;\n if (capture) { return }\n } catch (e) {\n globalHandleError(e, cur, 'errorCaptured hook');\n }\n }\n }\n }\n }\n globalHandleError(err, vm, info);\n } finally {\n popTarget();\n }\n}\n\nfunction invokeWithErrorHandling (\n handler,\n context,\n args,\n vm,\n info\n) {\n var res;\n try {\n res = args ? handler.apply(context, args) : handler.call(context);\n if (res && !res._isVue && isPromise(res) && !res._handled) {\n res.catch(function (e) { return handleError(e, vm, info + \" (Promise/async)\"); });\n // issue #9511\n // avoid catch triggering multiple times when nested calls\n res._handled = true;\n }\n } catch (e) {\n handleError(e, vm, info);\n }\n return res\n}\n\nfunction globalHandleError (err, vm, info) {\n if (config.errorHandler) {\n try {\n return config.errorHandler.call(null, err, vm, info)\n } catch (e) {\n // if the user intentionally throws the original error in the handler,\n // do not log it twice\n if (e !== err) {\n logError(e, null, 'config.errorHandler');\n }\n }\n }\n logError(err, vm, info);\n}\n\nfunction logError (err, vm, info) {\n if (process.env.NODE_ENV !== 'production') {\n warn((\"Error in \" + info + \": \\\"\" + (err.toString()) + \"\\\"\"), vm);\n }\n /* istanbul ignore else */\n if ((inBrowser || inWeex) && typeof console !== 'undefined') {\n console.error(err);\n } else {\n throw err\n }\n}\n\n/* */\n\nvar isUsingMicroTask = false;\n\nvar callbacks = [];\nvar pending = false;\n\nfunction flushCallbacks () {\n pending = false;\n var copies = callbacks.slice(0);\n callbacks.length = 0;\n for (var i = 0; i < copies.length; i++) {\n copies[i]();\n }\n}\n\n// Here we have async deferring wrappers using microtasks.\n// In 2.5 we used (macro) tasks (in combination with microtasks).\n// However, it has subtle problems when state is changed right before repaint\n// (e.g. #6813, out-in transitions).\n// Also, using (macro) tasks in event handler would cause some weird behaviors\n// that cannot be circumvented (e.g. #7109, #7153, #7546, #7834, #8109).\n// So we now use microtasks everywhere, again.\n// A major drawback of this tradeoff is that there are some scenarios\n// where microtasks have too high a priority and fire in between supposedly\n// sequential events (e.g. #4521, #6690, which have workarounds)\n// or even between bubbling of the same event (#6566).\nvar timerFunc;\n\n// The nextTick behavior leverages the microtask queue, which can be accessed\n// via either native Promise.then or MutationObserver.\n// MutationObserver has wider support, however it is seriously bugged in\n// UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It\n// completely stops working after triggering a few times... so, if native\n// Promise is available, we will use it:\n/* istanbul ignore next, $flow-disable-line */\nif (typeof Promise !== 'undefined' && isNative(Promise)) {\n var p = Promise.resolve();\n timerFunc = function () {\n p.then(flushCallbacks);\n // In problematic UIWebViews, Promise.then doesn't completely break, but\n // it can get stuck in a weird state where callbacks are pushed into the\n // microtask queue but the queue isn't being flushed, until the browser\n // needs to do some other work, e.g. handle a timer. Therefore we can\n // \"force\" the microtask queue to be flushed by adding an empty timer.\n if (isIOS) { setTimeout(noop); }\n };\n isUsingMicroTask = true;\n} else if (!isIE && typeof MutationObserver !== 'undefined' && (\n isNative(MutationObserver) ||\n // PhantomJS and iOS 7.x\n MutationObserver.toString() === '[object MutationObserverConstructor]'\n)) {\n // Use MutationObserver where native Promise is not available,\n // e.g. PhantomJS, iOS7, Android 4.4\n // (#6466 MutationObserver is unreliable in IE11)\n var counter = 1;\n var observer = new MutationObserver(flushCallbacks);\n var textNode = document.createTextNode(String(counter));\n observer.observe(textNode, {\n characterData: true\n });\n timerFunc = function () {\n counter = (counter + 1) % 2;\n textNode.data = String(counter);\n };\n isUsingMicroTask = true;\n} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {\n // Fallback to setImmediate.\n // Technically it leverages the (macro) task queue,\n // but it is still a better choice than setTimeout.\n timerFunc = function () {\n setImmediate(flushCallbacks);\n };\n} else {\n // Fallback to setTimeout.\n timerFunc = function () {\n setTimeout(flushCallbacks, 0);\n };\n}\n\nfunction nextTick (cb, ctx) {\n var _resolve;\n callbacks.push(function () {\n if (cb) {\n try {\n cb.call(ctx);\n } catch (e) {\n handleError(e, ctx, 'nextTick');\n }\n } else if (_resolve) {\n _resolve(ctx);\n }\n });\n if (!pending) {\n pending = true;\n timerFunc();\n }\n // $flow-disable-line\n if (!cb && typeof Promise !== 'undefined') {\n return new Promise(function (resolve) {\n _resolve = resolve;\n })\n }\n}\n\n/* */\n\nvar mark;\nvar measure;\n\nif (process.env.NODE_ENV !== 'production') {\n var perf = inBrowser && window.performance;\n /* istanbul ignore if */\n if (\n perf &&\n perf.mark &&\n perf.measure &&\n perf.clearMarks &&\n perf.clearMeasures\n ) {\n mark = function (tag) { return perf.mark(tag); };\n measure = function (name, startTag, endTag) {\n perf.measure(name, startTag, endTag);\n perf.clearMarks(startTag);\n perf.clearMarks(endTag);\n // perf.clearMeasures(name)\n };\n }\n}\n\n/* not type checking this file because flow doesn't play well with Proxy */\n\nvar initProxy;\n\nif (process.env.NODE_ENV !== 'production') {\n var allowedGlobals = makeMap(\n 'Infinity,undefined,NaN,isFinite,isNaN,' +\n 'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' +\n 'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,' +\n 'require' // for Webpack/Browserify\n );\n\n var warnNonPresent = function (target, key) {\n warn(\n \"Property or method \\\"\" + key + \"\\\" is not defined on the instance but \" +\n 'referenced during render. Make sure that this property is reactive, ' +\n 'either in the data option, or for class-based components, by ' +\n 'initializing the property. ' +\n 'See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.',\n target\n );\n };\n\n var warnReservedPrefix = function (target, key) {\n warn(\n \"Property \\\"\" + key + \"\\\" must be accessed with \\\"$data.\" + key + \"\\\" because \" +\n 'properties starting with \"$\" or \"_\" are not proxied in the Vue instance to ' +\n 'prevent conflicts with Vue internals. ' +\n 'See: https://vuejs.org/v2/api/#data',\n target\n );\n };\n\n var hasProxy =\n typeof Proxy !== 'undefined' && isNative(Proxy);\n\n if (hasProxy) {\n var isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta,exact');\n config.keyCodes = new Proxy(config.keyCodes, {\n set: function set (target, key, value) {\n if (isBuiltInModifier(key)) {\n warn((\"Avoid overwriting built-in modifier in config.keyCodes: .\" + key));\n return false\n } else {\n target[key] = value;\n return true\n }\n }\n });\n }\n\n var hasHandler = {\n has: function has (target, key) {\n var has = key in target;\n var isAllowed = allowedGlobals(key) ||\n (typeof key === 'string' && key.charAt(0) === '_' && !(key in target.$data));\n if (!has && !isAllowed) {\n if (key in target.$data) { warnReservedPrefix(target, key); }\n else { warnNonPresent(target, key); }\n }\n return has || !isAllowed\n }\n };\n\n var getHandler = {\n get: function get (target, key) {\n if (typeof key === 'string' && !(key in target)) {\n if (key in target.$data) { warnReservedPrefix(target, key); }\n else { warnNonPresent(target, key); }\n }\n return target[key]\n }\n };\n\n initProxy = function initProxy (vm) {\n if (hasProxy) {\n // determine which proxy handler to use\n var options = vm.$options;\n var handlers = options.render && options.render._withStripped\n ? getHandler\n : hasHandler;\n vm._renderProxy = new Proxy(vm, handlers);\n } else {\n vm._renderProxy = vm;\n }\n };\n}\n\n/* */\n\nvar seenObjects = new _Set();\n\n/**\n * Recursively traverse an object to evoke all converted\n * getters, so that every nested property inside the object\n * is collected as a \"deep\" dependency.\n */\nfunction traverse (val) {\n _traverse(val, seenObjects);\n seenObjects.clear();\n}\n\nfunction _traverse (val, seen) {\n var i, keys;\n var isA = Array.isArray(val);\n if ((!isA && !isObject(val)) || Object.isFrozen(val) || val instanceof VNode) {\n return\n }\n if (val.__ob__) {\n var depId = val.__ob__.dep.id;\n if (seen.has(depId)) {\n return\n }\n seen.add(depId);\n }\n if (isA) {\n i = val.length;\n while (i--) { _traverse(val[i], seen); }\n } else {\n keys = Object.keys(val);\n i = keys.length;\n while (i--) { _traverse(val[keys[i]], seen); }\n }\n}\n\n/* */\n\nvar normalizeEvent = cached(function (name) {\n var passive = name.charAt(0) === '&';\n name = passive ? name.slice(1) : name;\n var once$$1 = name.charAt(0) === '~'; // Prefixed last, checked first\n name = once$$1 ? name.slice(1) : name;\n var capture = name.charAt(0) === '!';\n name = capture ? name.slice(1) : name;\n return {\n name: name,\n once: once$$1,\n capture: capture,\n passive: passive\n }\n});\n\nfunction createFnInvoker (fns, vm) {\n function invoker () {\n var arguments$1 = arguments;\n\n var fns = invoker.fns;\n if (Array.isArray(fns)) {\n var cloned = fns.slice();\n for (var i = 0; i < cloned.length; i++) {\n invokeWithErrorHandling(cloned[i], null, arguments$1, vm, \"v-on handler\");\n }\n } else {\n // return handler return value for single handlers\n return invokeWithErrorHandling(fns, null, arguments, vm, \"v-on handler\")\n }\n }\n invoker.fns = fns;\n return invoker\n}\n\nfunction updateListeners (\n on,\n oldOn,\n add,\n remove$$1,\n createOnceHandler,\n vm\n) {\n var name, def$$1, cur, old, event;\n for (name in on) {\n def$$1 = cur = on[name];\n old = oldOn[name];\n event = normalizeEvent(name);\n if (isUndef(cur)) {\n process.env.NODE_ENV !== 'production' && warn(\n \"Invalid handler for event \\\"\" + (event.name) + \"\\\": got \" + String(cur),\n vm\n );\n } else if (isUndef(old)) {\n if (isUndef(cur.fns)) {\n cur = on[name] = createFnInvoker(cur, vm);\n }\n if (isTrue(event.once)) {\n cur = on[name] = createOnceHandler(event.name, cur, event.capture);\n }\n add(event.name, cur, event.capture, event.passive, event.params);\n } else if (cur !== old) {\n old.fns = cur;\n on[name] = old;\n }\n }\n for (name in oldOn) {\n if (isUndef(on[name])) {\n event = normalizeEvent(name);\n remove$$1(event.name, oldOn[name], event.capture);\n }\n }\n}\n\n/* */\n\nfunction mergeVNodeHook (def, hookKey, hook) {\n if (def instanceof VNode) {\n def = def.data.hook || (def.data.hook = {});\n }\n var invoker;\n var oldHook = def[hookKey];\n\n function wrappedHook () {\n hook.apply(this, arguments);\n // important: remove merged hook to ensure it's called only once\n // and prevent memory leak\n remove(invoker.fns, wrappedHook);\n }\n\n if (isUndef(oldHook)) {\n // no existing hook\n invoker = createFnInvoker([wrappedHook]);\n } else {\n /* istanbul ignore if */\n if (isDef(oldHook.fns) && isTrue(oldHook.merged)) {\n // already a merged invoker\n invoker = oldHook;\n invoker.fns.push(wrappedHook);\n } else {\n // existing plain hook\n invoker = createFnInvoker([oldHook, wrappedHook]);\n }\n }\n\n invoker.merged = true;\n def[hookKey] = invoker;\n}\n\n/* */\n\nfunction extractPropsFromVNodeData (\n data,\n Ctor,\n tag\n) {\n // we are only extracting raw values here.\n // validation and default values are handled in the child\n // component itself.\n var propOptions = Ctor.options.props;\n if (isUndef(propOptions)) {\n return\n }\n var res = {};\n var attrs = data.attrs;\n var props = data.props;\n if (isDef(attrs) || isDef(props)) {\n for (var key in propOptions) {\n var altKey = hyphenate(key);\n if (process.env.NODE_ENV !== 'production') {\n var keyInLowerCase = key.toLowerCase();\n if (\n key !== keyInLowerCase &&\n attrs && hasOwn(attrs, keyInLowerCase)\n ) {\n tip(\n \"Prop \\\"\" + keyInLowerCase + \"\\\" is passed to component \" +\n (formatComponentName(tag || Ctor)) + \", but the declared prop name is\" +\n \" \\\"\" + key + \"\\\". \" +\n \"Note that HTML attributes are case-insensitive and camelCased \" +\n \"props need to use their kebab-case equivalents when using in-DOM \" +\n \"templates. You should probably use \\\"\" + altKey + \"\\\" instead of \\\"\" + key + \"\\\".\"\n );\n }\n }\n checkProp(res, props, key, altKey, true) ||\n checkProp(res, attrs, key, altKey, false);\n }\n }\n return res\n}\n\nfunction checkProp (\n res,\n hash,\n key,\n altKey,\n preserve\n) {\n if (isDef(hash)) {\n if (hasOwn(hash, key)) {\n res[key] = hash[key];\n if (!preserve) {\n delete hash[key];\n }\n return true\n } else if (hasOwn(hash, altKey)) {\n res[key] = hash[altKey];\n if (!preserve) {\n delete hash[altKey];\n }\n return true\n }\n }\n return false\n}\n\n/* */\n\n// The template compiler attempts to minimize the need for normalization by\n// statically analyzing the template at compile time.\n//\n// For plain HTML markup, normalization can be completely skipped because the\n// generated render function is guaranteed to return Array. There are\n// two cases where extra normalization is needed:\n\n// 1. When the children contains components - because a functional component\n// may return an Array instead of a single root. In this case, just a simple\n// normalization is needed - if any child is an Array, we flatten the whole\n// thing with Array.prototype.concat. It is guaranteed to be only 1-level deep\n// because functional components already normalize their own children.\nfunction simpleNormalizeChildren (children) {\n for (var i = 0; i < children.length; i++) {\n if (Array.isArray(children[i])) {\n return Array.prototype.concat.apply([], children)\n }\n }\n return children\n}\n\n// 2. When the children contains constructs that always generated nested Arrays,\n// e.g. , , v-for, or when the children is provided by user\n// with hand-written render functions / JSX. In such cases a full normalization\n// is needed to cater to all possible types of children values.\nfunction normalizeChildren (children) {\n return isPrimitive(children)\n ? [createTextVNode(children)]\n : Array.isArray(children)\n ? normalizeArrayChildren(children)\n : undefined\n}\n\nfunction isTextNode (node) {\n return isDef(node) && isDef(node.text) && isFalse(node.isComment)\n}\n\nfunction normalizeArrayChildren (children, nestedIndex) {\n var res = [];\n var i, c, lastIndex, last;\n for (i = 0; i < children.length; i++) {\n c = children[i];\n if (isUndef(c) || typeof c === 'boolean') { continue }\n lastIndex = res.length - 1;\n last = res[lastIndex];\n // nested\n if (Array.isArray(c)) {\n if (c.length > 0) {\n c = normalizeArrayChildren(c, ((nestedIndex || '') + \"_\" + i));\n // merge adjacent text nodes\n if (isTextNode(c[0]) && isTextNode(last)) {\n res[lastIndex] = createTextVNode(last.text + (c[0]).text);\n c.shift();\n }\n res.push.apply(res, c);\n }\n } else if (isPrimitive(c)) {\n if (isTextNode(last)) {\n // merge adjacent text nodes\n // this is necessary for SSR hydration because text nodes are\n // essentially merged when rendered to HTML strings\n res[lastIndex] = createTextVNode(last.text + c);\n } else if (c !== '') {\n // convert primitive to vnode\n res.push(createTextVNode(c));\n }\n } else {\n if (isTextNode(c) && isTextNode(last)) {\n // merge adjacent text nodes\n res[lastIndex] = createTextVNode(last.text + c.text);\n } else {\n // default key for nested array children (likely generated by v-for)\n if (isTrue(children._isVList) &&\n isDef(c.tag) &&\n isUndef(c.key) &&\n isDef(nestedIndex)) {\n c.key = \"__vlist\" + nestedIndex + \"_\" + i + \"__\";\n }\n res.push(c);\n }\n }\n }\n return res\n}\n\n/* */\n\nfunction initProvide (vm) {\n var provide = vm.$options.provide;\n if (provide) {\n vm._provided = typeof provide === 'function'\n ? provide.call(vm)\n : provide;\n }\n}\n\nfunction initInjections (vm) {\n var result = resolveInject(vm.$options.inject, vm);\n if (result) {\n toggleObserving(false);\n Object.keys(result).forEach(function (key) {\n /* istanbul ignore else */\n if (process.env.NODE_ENV !== 'production') {\n defineReactive$$1(vm, key, result[key], function () {\n warn(\n \"Avoid mutating an injected value directly since the changes will be \" +\n \"overwritten whenever the provided component re-renders. \" +\n \"injection being mutated: \\\"\" + key + \"\\\"\",\n vm\n );\n });\n } else {\n defineReactive$$1(vm, key, result[key]);\n }\n });\n toggleObserving(true);\n }\n}\n\nfunction resolveInject (inject, vm) {\n if (inject) {\n // inject is :any because flow is not smart enough to figure out cached\n var result = Object.create(null);\n var keys = hasSymbol\n ? Reflect.ownKeys(inject)\n : Object.keys(inject);\n\n for (var i = 0; i < keys.length; i++) {\n var key = keys[i];\n // #6574 in case the inject object is observed...\n if (key === '__ob__') { continue }\n var provideKey = inject[key].from;\n var source = vm;\n while (source) {\n if (source._provided && hasOwn(source._provided, provideKey)) {\n result[key] = source._provided[provideKey];\n break\n }\n source = source.$parent;\n }\n if (!source) {\n if ('default' in inject[key]) {\n var provideDefault = inject[key].default;\n result[key] = typeof provideDefault === 'function'\n ? provideDefault.call(vm)\n : provideDefault;\n } else if (process.env.NODE_ENV !== 'production') {\n warn((\"Injection \\\"\" + key + \"\\\" not found\"), vm);\n }\n }\n }\n return result\n }\n}\n\n/* */\n\n\n\n/**\n * Runtime helper for resolving raw children VNodes into a slot object.\n */\nfunction resolveSlots (\n children,\n context\n) {\n if (!children || !children.length) {\n return {}\n }\n var slots = {};\n for (var i = 0, l = children.length; i < l; i++) {\n var child = children[i];\n var data = child.data;\n // remove slot attribute if the node is resolved as a Vue slot node\n if (data && data.attrs && data.attrs.slot) {\n delete data.attrs.slot;\n }\n // named slots should only be respected if the vnode was rendered in the\n // same context.\n if ((child.context === context || child.fnContext === context) &&\n data && data.slot != null\n ) {\n var name = data.slot;\n var slot = (slots[name] || (slots[name] = []));\n if (child.tag === 'template') {\n slot.push.apply(slot, child.children || []);\n } else {\n slot.push(child);\n }\n } else {\n (slots.default || (slots.default = [])).push(child);\n }\n }\n // ignore slots that contains only whitespace\n for (var name$1 in slots) {\n if (slots[name$1].every(isWhitespace)) {\n delete slots[name$1];\n }\n }\n return slots\n}\n\nfunction isWhitespace (node) {\n return (node.isComment && !node.asyncFactory) || node.text === ' '\n}\n\n/* */\n\nfunction normalizeScopedSlots (\n slots,\n normalSlots,\n prevSlots\n) {\n var res;\n var hasNormalSlots = Object.keys(normalSlots).length > 0;\n var isStable = slots ? !!slots.$stable : !hasNormalSlots;\n var key = slots && slots.$key;\n if (!slots) {\n res = {};\n } else if (slots._normalized) {\n // fast path 1: child component re-render only, parent did not change\n return slots._normalized\n } else if (\n isStable &&\n prevSlots &&\n prevSlots !== emptyObject &&\n key === prevSlots.$key &&\n !hasNormalSlots &&\n !prevSlots.$hasNormal\n ) {\n // fast path 2: stable scoped slots w/ no normal slots to proxy,\n // only need to normalize once\n return prevSlots\n } else {\n res = {};\n for (var key$1 in slots) {\n if (slots[key$1] && key$1[0] !== '$') {\n res[key$1] = normalizeScopedSlot(normalSlots, key$1, slots[key$1]);\n }\n }\n }\n // expose normal slots on scopedSlots\n for (var key$2 in normalSlots) {\n if (!(key$2 in res)) {\n res[key$2] = proxyNormalSlot(normalSlots, key$2);\n }\n }\n // avoriaz seems to mock a non-extensible $scopedSlots object\n // and when that is passed down this would cause an error\n if (slots && Object.isExtensible(slots)) {\n (slots)._normalized = res;\n }\n def(res, '$stable', isStable);\n def(res, '$key', key);\n def(res, '$hasNormal', hasNormalSlots);\n return res\n}\n\nfunction normalizeScopedSlot(normalSlots, key, fn) {\n var normalized = function () {\n var res = arguments.length ? fn.apply(null, arguments) : fn({});\n res = res && typeof res === 'object' && !Array.isArray(res)\n ? [res] // single vnode\n : normalizeChildren(res);\n return res && (\n res.length === 0 ||\n (res.length === 1 && res[0].isComment) // #9658\n ) ? undefined\n : res\n };\n // this is a slot using the new v-slot syntax without scope. although it is\n // compiled as a scoped slot, render fn users would expect it to be present\n // on this.$slots because the usage is semantically a normal slot.\n if (fn.proxy) {\n Object.defineProperty(normalSlots, key, {\n get: normalized,\n enumerable: true,\n configurable: true\n });\n }\n return normalized\n}\n\nfunction proxyNormalSlot(slots, key) {\n return function () { return slots[key]; }\n}\n\n/* */\n\n/**\n * Runtime helper for rendering v-for lists.\n */\nfunction renderList (\n val,\n render\n) {\n var ret, i, l, keys, key;\n if (Array.isArray(val) || typeof val === 'string') {\n ret = new Array(val.length);\n for (i = 0, l = val.length; i < l; i++) {\n ret[i] = render(val[i], i);\n }\n } else if (typeof val === 'number') {\n ret = new Array(val);\n for (i = 0; i < val; i++) {\n ret[i] = render(i + 1, i);\n }\n } else if (isObject(val)) {\n if (hasSymbol && val[Symbol.iterator]) {\n ret = [];\n var iterator = val[Symbol.iterator]();\n var result = iterator.next();\n while (!result.done) {\n ret.push(render(result.value, ret.length));\n result = iterator.next();\n }\n } else {\n keys = Object.keys(val);\n ret = new Array(keys.length);\n for (i = 0, l = keys.length; i < l; i++) {\n key = keys[i];\n ret[i] = render(val[key], key, i);\n }\n }\n }\n if (!isDef(ret)) {\n ret = [];\n }\n (ret)._isVList = true;\n return ret\n}\n\n/* */\n\n/**\n * Runtime helper for rendering \n */\nfunction renderSlot (\n name,\n fallback,\n props,\n bindObject\n) {\n var scopedSlotFn = this.$scopedSlots[name];\n var nodes;\n if (scopedSlotFn) { // scoped slot\n props = props || {};\n if (bindObject) {\n if (process.env.NODE_ENV !== 'production' && !isObject(bindObject)) {\n warn(\n 'slot v-bind without argument expects an Object',\n this\n );\n }\n props = extend(extend({}, bindObject), props);\n }\n nodes = scopedSlotFn(props) || fallback;\n } else {\n nodes = this.$slots[name] || fallback;\n }\n\n var target = props && props.slot;\n if (target) {\n return this.$createElement('template', { slot: target }, nodes)\n } else {\n return nodes\n }\n}\n\n/* */\n\n/**\n * Runtime helper for resolving filters\n */\nfunction resolveFilter (id) {\n return resolveAsset(this.$options, 'filters', id, true) || identity\n}\n\n/* */\n\nfunction isKeyNotMatch (expect, actual) {\n if (Array.isArray(expect)) {\n return expect.indexOf(actual) === -1\n } else {\n return expect !== actual\n }\n}\n\n/**\n * Runtime helper for checking keyCodes from config.\n * exposed as Vue.prototype._k\n * passing in eventKeyName as last argument separately for backwards compat\n */\nfunction checkKeyCodes (\n eventKeyCode,\n key,\n builtInKeyCode,\n eventKeyName,\n builtInKeyName\n) {\n var mappedKeyCode = config.keyCodes[key] || builtInKeyCode;\n if (builtInKeyName && eventKeyName && !config.keyCodes[key]) {\n return isKeyNotMatch(builtInKeyName, eventKeyName)\n } else if (mappedKeyCode) {\n return isKeyNotMatch(mappedKeyCode, eventKeyCode)\n } else if (eventKeyName) {\n return hyphenate(eventKeyName) !== key\n }\n}\n\n/* */\n\n/**\n * Runtime helper for merging v-bind=\"object\" into a VNode's data.\n */\nfunction bindObjectProps (\n data,\n tag,\n value,\n asProp,\n isSync\n) {\n if (value) {\n if (!isObject(value)) {\n process.env.NODE_ENV !== 'production' && warn(\n 'v-bind without argument expects an Object or Array value',\n this\n );\n } else {\n if (Array.isArray(value)) {\n value = toObject(value);\n }\n var hash;\n var loop = function ( key ) {\n if (\n key === 'class' ||\n key === 'style' ||\n isReservedAttribute(key)\n ) {\n hash = data;\n } else {\n var type = data.attrs && data.attrs.type;\n hash = asProp || config.mustUseProp(tag, type, key)\n ? data.domProps || (data.domProps = {})\n : data.attrs || (data.attrs = {});\n }\n var camelizedKey = camelize(key);\n var hyphenatedKey = hyphenate(key);\n if (!(camelizedKey in hash) && !(hyphenatedKey in hash)) {\n hash[key] = value[key];\n\n if (isSync) {\n var on = data.on || (data.on = {});\n on[(\"update:\" + key)] = function ($event) {\n value[key] = $event;\n };\n }\n }\n };\n\n for (var key in value) loop( key );\n }\n }\n return data\n}\n\n/* */\n\n/**\n * Runtime helper for rendering static trees.\n */\nfunction renderStatic (\n index,\n isInFor\n) {\n var cached = this._staticTrees || (this._staticTrees = []);\n var tree = cached[index];\n // if has already-rendered static tree and not inside v-for,\n // we can reuse the same tree.\n if (tree && !isInFor) {\n return tree\n }\n // otherwise, render a fresh tree.\n tree = cached[index] = this.$options.staticRenderFns[index].call(\n this._renderProxy,\n null,\n this // for render fns generated for functional component templates\n );\n markStatic(tree, (\"__static__\" + index), false);\n return tree\n}\n\n/**\n * Runtime helper for v-once.\n * Effectively it means marking the node as static with a unique key.\n */\nfunction markOnce (\n tree,\n index,\n key\n) {\n markStatic(tree, (\"__once__\" + index + (key ? (\"_\" + key) : \"\")), true);\n return tree\n}\n\nfunction markStatic (\n tree,\n key,\n isOnce\n) {\n if (Array.isArray(tree)) {\n for (var i = 0; i < tree.length; i++) {\n if (tree[i] && typeof tree[i] !== 'string') {\n markStaticNode(tree[i], (key + \"_\" + i), isOnce);\n }\n }\n } else {\n markStaticNode(tree, key, isOnce);\n }\n}\n\nfunction markStaticNode (node, key, isOnce) {\n node.isStatic = true;\n node.key = key;\n node.isOnce = isOnce;\n}\n\n/* */\n\nfunction bindObjectListeners (data, value) {\n if (value) {\n if (!isPlainObject(value)) {\n process.env.NODE_ENV !== 'production' && warn(\n 'v-on without argument expects an Object value',\n this\n );\n } else {\n var on = data.on = data.on ? extend({}, data.on) : {};\n for (var key in value) {\n var existing = on[key];\n var ours = value[key];\n on[key] = existing ? [].concat(existing, ours) : ours;\n }\n }\n }\n return data\n}\n\n/* */\n\nfunction resolveScopedSlots (\n fns, // see flow/vnode\n res,\n // the following are added in 2.6\n hasDynamicKeys,\n contentHashKey\n) {\n res = res || { $stable: !hasDynamicKeys };\n for (var i = 0; i < fns.length; i++) {\n var slot = fns[i];\n if (Array.isArray(slot)) {\n resolveScopedSlots(slot, res, hasDynamicKeys);\n } else if (slot) {\n // marker for reverse proxying v-slot without scope on this.$slots\n if (slot.proxy) {\n slot.fn.proxy = true;\n }\n res[slot.key] = slot.fn;\n }\n }\n if (contentHashKey) {\n (res).$key = contentHashKey;\n }\n return res\n}\n\n/* */\n\nfunction bindDynamicKeys (baseObj, values) {\n for (var i = 0; i < values.length; i += 2) {\n var key = values[i];\n if (typeof key === 'string' && key) {\n baseObj[values[i]] = values[i + 1];\n } else if (process.env.NODE_ENV !== 'production' && key !== '' && key !== null) {\n // null is a special value for explicitly removing a binding\n warn(\n (\"Invalid value for dynamic directive argument (expected string or null): \" + key),\n this\n );\n }\n }\n return baseObj\n}\n\n// helper to dynamically append modifier runtime markers to event names.\n// ensure only append when value is already string, otherwise it will be cast\n// to string and cause the type check to miss.\nfunction prependModifier (value, symbol) {\n return typeof value === 'string' ? symbol + value : value\n}\n\n/* */\n\nfunction installRenderHelpers (target) {\n target._o = markOnce;\n target._n = toNumber;\n target._s = toString;\n target._l = renderList;\n target._t = renderSlot;\n target._q = looseEqual;\n target._i = looseIndexOf;\n target._m = renderStatic;\n target._f = resolveFilter;\n target._k = checkKeyCodes;\n target._b = bindObjectProps;\n target._v = createTextVNode;\n target._e = createEmptyVNode;\n target._u = resolveScopedSlots;\n target._g = bindObjectListeners;\n target._d = bindDynamicKeys;\n target._p = prependModifier;\n}\n\n/* */\n\nfunction FunctionalRenderContext (\n data,\n props,\n children,\n parent,\n Ctor\n) {\n var this$1 = this;\n\n var options = Ctor.options;\n // ensure the createElement function in functional components\n // gets a unique context - this is necessary for correct named slot check\n var contextVm;\n if (hasOwn(parent, '_uid')) {\n contextVm = Object.create(parent);\n // $flow-disable-line\n contextVm._original = parent;\n } else {\n // the context vm passed in is a functional context as well.\n // in this case we want to make sure we are able to get a hold to the\n // real context instance.\n contextVm = parent;\n // $flow-disable-line\n parent = parent._original;\n }\n var isCompiled = isTrue(options._compiled);\n var needNormalization = !isCompiled;\n\n this.data = data;\n this.props = props;\n this.children = children;\n this.parent = parent;\n this.listeners = data.on || emptyObject;\n this.injections = resolveInject(options.inject, parent);\n this.slots = function () {\n if (!this$1.$slots) {\n normalizeScopedSlots(\n data.scopedSlots,\n this$1.$slots = resolveSlots(children, parent)\n );\n }\n return this$1.$slots\n };\n\n Object.defineProperty(this, 'scopedSlots', ({\n enumerable: true,\n get: function get () {\n return normalizeScopedSlots(data.scopedSlots, this.slots())\n }\n }));\n\n // support for compiled functional template\n if (isCompiled) {\n // exposing $options for renderStatic()\n this.$options = options;\n // pre-resolve slots for renderSlot()\n this.$slots = this.slots();\n this.$scopedSlots = normalizeScopedSlots(data.scopedSlots, this.$slots);\n }\n\n if (options._scopeId) {\n this._c = function (a, b, c, d) {\n var vnode = createElement(contextVm, a, b, c, d, needNormalization);\n if (vnode && !Array.isArray(vnode)) {\n vnode.fnScopeId = options._scopeId;\n vnode.fnContext = parent;\n }\n return vnode\n };\n } else {\n this._c = function (a, b, c, d) { return createElement(contextVm, a, b, c, d, needNormalization); };\n }\n}\n\ninstallRenderHelpers(FunctionalRenderContext.prototype);\n\nfunction createFunctionalComponent (\n Ctor,\n propsData,\n data,\n contextVm,\n children\n) {\n var options = Ctor.options;\n var props = {};\n var propOptions = options.props;\n if (isDef(propOptions)) {\n for (var key in propOptions) {\n props[key] = validateProp(key, propOptions, propsData || emptyObject);\n }\n } else {\n if (isDef(data.attrs)) { mergeProps(props, data.attrs); }\n if (isDef(data.props)) { mergeProps(props, data.props); }\n }\n\n var renderContext = new FunctionalRenderContext(\n data,\n props,\n children,\n contextVm,\n Ctor\n );\n\n var vnode = options.render.call(null, renderContext._c, renderContext);\n\n if (vnode instanceof VNode) {\n return cloneAndMarkFunctionalResult(vnode, data, renderContext.parent, options, renderContext)\n } else if (Array.isArray(vnode)) {\n var vnodes = normalizeChildren(vnode) || [];\n var res = new Array(vnodes.length);\n for (var i = 0; i < vnodes.length; i++) {\n res[i] = cloneAndMarkFunctionalResult(vnodes[i], data, renderContext.parent, options, renderContext);\n }\n return res\n }\n}\n\nfunction cloneAndMarkFunctionalResult (vnode, data, contextVm, options, renderContext) {\n // #7817 clone node before setting fnContext, otherwise if the node is reused\n // (e.g. it was from a cached normal slot) the fnContext causes named slots\n // that should not be matched to match.\n var clone = cloneVNode(vnode);\n clone.fnContext = contextVm;\n clone.fnOptions = options;\n if (process.env.NODE_ENV !== 'production') {\n (clone.devtoolsMeta = clone.devtoolsMeta || {}).renderContext = renderContext;\n }\n if (data.slot) {\n (clone.data || (clone.data = {})).slot = data.slot;\n }\n return clone\n}\n\nfunction mergeProps (to, from) {\n for (var key in from) {\n to[camelize(key)] = from[key];\n }\n}\n\n/* */\n\n/* */\n\n/* */\n\n/* */\n\n// inline hooks to be invoked on component VNodes during patch\nvar componentVNodeHooks = {\n init: function init (vnode, hydrating) {\n if (\n vnode.componentInstance &&\n !vnode.componentInstance._isDestroyed &&\n vnode.data.keepAlive\n ) {\n // kept-alive components, treat as a patch\n var mountedNode = vnode; // work around flow\n componentVNodeHooks.prepatch(mountedNode, mountedNode);\n } else {\n var child = vnode.componentInstance = createComponentInstanceForVnode(\n vnode,\n activeInstance\n );\n child.$mount(hydrating ? vnode.elm : undefined, hydrating);\n }\n },\n\n prepatch: function prepatch (oldVnode, vnode) {\n var options = vnode.componentOptions;\n var child = vnode.componentInstance = oldVnode.componentInstance;\n updateChildComponent(\n child,\n options.propsData, // updated props\n options.listeners, // updated listeners\n vnode, // new parent vnode\n options.children // new children\n );\n },\n\n insert: function insert (vnode) {\n var context = vnode.context;\n var componentInstance = vnode.componentInstance;\n if (!componentInstance._isMounted) {\n componentInstance._isMounted = true;\n callHook(componentInstance, 'mounted');\n }\n if (vnode.data.keepAlive) {\n if (context._isMounted) {\n // vue-router#1212\n // During updates, a kept-alive component's child components may\n // change, so directly walking the tree here may call activated hooks\n // on incorrect children. Instead we push them into a queue which will\n // be processed after the whole patch process ended.\n queueActivatedComponent(componentInstance);\n } else {\n activateChildComponent(componentInstance, true /* direct */);\n }\n }\n },\n\n destroy: function destroy (vnode) {\n var componentInstance = vnode.componentInstance;\n if (!componentInstance._isDestroyed) {\n if (!vnode.data.keepAlive) {\n componentInstance.$destroy();\n } else {\n deactivateChildComponent(componentInstance, true /* direct */);\n }\n }\n }\n};\n\nvar hooksToMerge = Object.keys(componentVNodeHooks);\n\nfunction createComponent (\n Ctor,\n data,\n context,\n children,\n tag\n) {\n if (isUndef(Ctor)) {\n return\n }\n\n var baseCtor = context.$options._base;\n\n // plain options object: turn it into a constructor\n if (isObject(Ctor)) {\n Ctor = baseCtor.extend(Ctor);\n }\n\n // if at this stage it's not a constructor or an async component factory,\n // reject.\n if (typeof Ctor !== 'function') {\n if (process.env.NODE_ENV !== 'production') {\n warn((\"Invalid Component definition: \" + (String(Ctor))), context);\n }\n return\n }\n\n // async component\n var asyncFactory;\n if (isUndef(Ctor.cid)) {\n asyncFactory = Ctor;\n Ctor = resolveAsyncComponent(asyncFactory, baseCtor);\n if (Ctor === undefined) {\n // return a placeholder node for async component, which is rendered\n // as a comment node but preserves all the raw information for the node.\n // the information will be used for async server-rendering and hydration.\n return createAsyncPlaceholder(\n asyncFactory,\n data,\n context,\n children,\n tag\n )\n }\n }\n\n data = data || {};\n\n // resolve constructor options in case global mixins are applied after\n // component constructor creation\n resolveConstructorOptions(Ctor);\n\n // transform component v-model data into props & events\n if (isDef(data.model)) {\n transformModel(Ctor.options, data);\n }\n\n // extract props\n var propsData = extractPropsFromVNodeData(data, Ctor, tag);\n\n // functional component\n if (isTrue(Ctor.options.functional)) {\n return createFunctionalComponent(Ctor, propsData, data, context, children)\n }\n\n // extract listeners, since these needs to be treated as\n // child component listeners instead of DOM listeners\n var listeners = data.on;\n // replace with listeners with .native modifier\n // so it gets processed during parent component patch.\n data.on = data.nativeOn;\n\n if (isTrue(Ctor.options.abstract)) {\n // abstract components do not keep anything\n // other than props & listeners & slot\n\n // work around flow\n var slot = data.slot;\n data = {};\n if (slot) {\n data.slot = slot;\n }\n }\n\n // install component management hooks onto the placeholder node\n installComponentHooks(data);\n\n // return a placeholder vnode\n var name = Ctor.options.name || tag;\n var vnode = new VNode(\n (\"vue-component-\" + (Ctor.cid) + (name ? (\"-\" + name) : '')),\n data, undefined, undefined, undefined, context,\n { Ctor: Ctor, propsData: propsData, listeners: listeners, tag: tag, children: children },\n asyncFactory\n );\n\n return vnode\n}\n\nfunction createComponentInstanceForVnode (\n vnode, // we know it's MountedComponentVNode but flow doesn't\n parent // activeInstance in lifecycle state\n) {\n var options = {\n _isComponent: true,\n _parentVnode: vnode,\n parent: parent\n };\n // check inline-template render functions\n var inlineTemplate = vnode.data.inlineTemplate;\n if (isDef(inlineTemplate)) {\n options.render = inlineTemplate.render;\n options.staticRenderFns = inlineTemplate.staticRenderFns;\n }\n return new vnode.componentOptions.Ctor(options)\n}\n\nfunction installComponentHooks (data) {\n var hooks = data.hook || (data.hook = {});\n for (var i = 0; i < hooksToMerge.length; i++) {\n var key = hooksToMerge[i];\n var existing = hooks[key];\n var toMerge = componentVNodeHooks[key];\n if (existing !== toMerge && !(existing && existing._merged)) {\n hooks[key] = existing ? mergeHook$1(toMerge, existing) : toMerge;\n }\n }\n}\n\nfunction mergeHook$1 (f1, f2) {\n var merged = function (a, b) {\n // flow complains about extra args which is why we use any\n f1(a, b);\n f2(a, b);\n };\n merged._merged = true;\n return merged\n}\n\n// transform component v-model info (value and callback) into\n// prop and event handler respectively.\nfunction transformModel (options, data) {\n var prop = (options.model && options.model.prop) || 'value';\n var event = (options.model && options.model.event) || 'input'\n ;(data.attrs || (data.attrs = {}))[prop] = data.model.value;\n var on = data.on || (data.on = {});\n var existing = on[event];\n var callback = data.model.callback;\n if (isDef(existing)) {\n if (\n Array.isArray(existing)\n ? existing.indexOf(callback) === -1\n : existing !== callback\n ) {\n on[event] = [callback].concat(existing);\n }\n } else {\n on[event] = callback;\n }\n}\n\n/* */\n\nvar SIMPLE_NORMALIZE = 1;\nvar ALWAYS_NORMALIZE = 2;\n\n// wrapper function for providing a more flexible interface\n// without getting yelled at by flow\nfunction createElement (\n context,\n tag,\n data,\n children,\n normalizationType,\n alwaysNormalize\n) {\n if (Array.isArray(data) || isPrimitive(data)) {\n normalizationType = children;\n children = data;\n data = undefined;\n }\n if (isTrue(alwaysNormalize)) {\n normalizationType = ALWAYS_NORMALIZE;\n }\n return _createElement(context, tag, data, children, normalizationType)\n}\n\nfunction _createElement (\n context,\n tag,\n data,\n children,\n normalizationType\n) {\n if (isDef(data) && isDef((data).__ob__)) {\n process.env.NODE_ENV !== 'production' && warn(\n \"Avoid using observed data object as vnode data: \" + (JSON.stringify(data)) + \"\\n\" +\n 'Always create fresh vnode data objects in each render!',\n context\n );\n return createEmptyVNode()\n }\n // object syntax in v-bind\n if (isDef(data) && isDef(data.is)) {\n tag = data.is;\n }\n if (!tag) {\n // in case of component :is set to falsy value\n return createEmptyVNode()\n }\n // warn against non-primitive key\n if (process.env.NODE_ENV !== 'production' &&\n isDef(data) && isDef(data.key) && !isPrimitive(data.key)\n ) {\n {\n warn(\n 'Avoid using non-primitive value as key, ' +\n 'use string/number value instead.',\n context\n );\n }\n }\n // support single function children as default scoped slot\n if (Array.isArray(children) &&\n typeof children[0] === 'function'\n ) {\n data = data || {};\n data.scopedSlots = { default: children[0] };\n children.length = 0;\n }\n if (normalizationType === ALWAYS_NORMALIZE) {\n children = normalizeChildren(children);\n } else if (normalizationType === SIMPLE_NORMALIZE) {\n children = simpleNormalizeChildren(children);\n }\n var vnode, ns;\n if (typeof tag === 'string') {\n var Ctor;\n ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag);\n if (config.isReservedTag(tag)) {\n // platform built-in elements\n if (process.env.NODE_ENV !== 'production' && isDef(data) && isDef(data.nativeOn)) {\n warn(\n (\"The .native modifier for v-on is only valid on components but it was used on <\" + tag + \">.\"),\n context\n );\n }\n vnode = new VNode(\n config.parsePlatformTagName(tag), data, children,\n undefined, undefined, context\n );\n } else if ((!data || !data.pre) && isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {\n // component\n vnode = createComponent(Ctor, data, context, children, tag);\n } else {\n // unknown or unlisted namespaced elements\n // check at runtime because it may get assigned a namespace when its\n // parent normalizes children\n vnode = new VNode(\n tag, data, children,\n undefined, undefined, context\n );\n }\n } else {\n // direct component options / constructor\n vnode = createComponent(tag, data, context, children);\n }\n if (Array.isArray(vnode)) {\n return vnode\n } else if (isDef(vnode)) {\n if (isDef(ns)) { applyNS(vnode, ns); }\n if (isDef(data)) { registerDeepBindings(data); }\n return vnode\n } else {\n return createEmptyVNode()\n }\n}\n\nfunction applyNS (vnode, ns, force) {\n vnode.ns = ns;\n if (vnode.tag === 'foreignObject') {\n // use default namespace inside foreignObject\n ns = undefined;\n force = true;\n }\n if (isDef(vnode.children)) {\n for (var i = 0, l = vnode.children.length; i < l; i++) {\n var child = vnode.children[i];\n if (isDef(child.tag) && (\n isUndef(child.ns) || (isTrue(force) && child.tag !== 'svg'))) {\n applyNS(child, ns, force);\n }\n }\n }\n}\n\n// ref #5318\n// necessary to ensure parent re-render when deep bindings like :style and\n// :class are used on slot nodes\nfunction registerDeepBindings (data) {\n if (isObject(data.style)) {\n traverse(data.style);\n }\n if (isObject(data.class)) {\n traverse(data.class);\n }\n}\n\n/* */\n\nfunction initRender (vm) {\n vm._vnode = null; // the root of the child tree\n vm._staticTrees = null; // v-once cached trees\n var options = vm.$options;\n var parentVnode = vm.$vnode = options._parentVnode; // the placeholder node in parent tree\n var renderContext = parentVnode && parentVnode.context;\n vm.$slots = resolveSlots(options._renderChildren, renderContext);\n vm.$scopedSlots = emptyObject;\n // bind the createElement fn to this instance\n // so that we get proper render context inside it.\n // args order: tag, data, children, normalizationType, alwaysNormalize\n // internal version is used by render functions compiled from templates\n vm._c = function (a, b, c, d) { return createElement(vm, a, b, c, d, false); };\n // normalization is always applied for the public version, used in\n // user-written render functions.\n vm.$createElement = function (a, b, c, d) { return createElement(vm, a, b, c, d, true); };\n\n // $attrs & $listeners are exposed for easier HOC creation.\n // they need to be reactive so that HOCs using them are always updated\n var parentData = parentVnode && parentVnode.data;\n\n /* istanbul ignore else */\n if (process.env.NODE_ENV !== 'production') {\n defineReactive$$1(vm, '$attrs', parentData && parentData.attrs || emptyObject, function () {\n !isUpdatingChildComponent && warn(\"$attrs is readonly.\", vm);\n }, true);\n defineReactive$$1(vm, '$listeners', options._parentListeners || emptyObject, function () {\n !isUpdatingChildComponent && warn(\"$listeners is readonly.\", vm);\n }, true);\n } else {\n defineReactive$$1(vm, '$attrs', parentData && parentData.attrs || emptyObject, null, true);\n defineReactive$$1(vm, '$listeners', options._parentListeners || emptyObject, null, true);\n }\n}\n\nvar currentRenderingInstance = null;\n\nfunction renderMixin (Vue) {\n // install runtime convenience helpers\n installRenderHelpers(Vue.prototype);\n\n Vue.prototype.$nextTick = function (fn) {\n return nextTick(fn, this)\n };\n\n Vue.prototype._render = function () {\n var vm = this;\n var ref = vm.$options;\n var render = ref.render;\n var _parentVnode = ref._parentVnode;\n\n if (_parentVnode) {\n vm.$scopedSlots = normalizeScopedSlots(\n _parentVnode.data.scopedSlots,\n vm.$slots,\n vm.$scopedSlots\n );\n }\n\n // set parent vnode. this allows render functions to have access\n // to the data on the placeholder node.\n vm.$vnode = _parentVnode;\n // render self\n var vnode;\n try {\n // There's no need to maintain a stack because all render fns are called\n // separately from one another. Nested component's render fns are called\n // when parent component is patched.\n currentRenderingInstance = vm;\n vnode = render.call(vm._renderProxy, vm.$createElement);\n } catch (e) {\n handleError(e, vm, \"render\");\n // return error render result,\n // or previous vnode to prevent render error causing blank component\n /* istanbul ignore else */\n if (process.env.NODE_ENV !== 'production' && vm.$options.renderError) {\n try {\n vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e);\n } catch (e) {\n handleError(e, vm, \"renderError\");\n vnode = vm._vnode;\n }\n } else {\n vnode = vm._vnode;\n }\n } finally {\n currentRenderingInstance = null;\n }\n // if the returned array contains only a single node, allow it\n if (Array.isArray(vnode) && vnode.length === 1) {\n vnode = vnode[0];\n }\n // return empty vnode in case the render function errored out\n if (!(vnode instanceof VNode)) {\n if (process.env.NODE_ENV !== 'production' && Array.isArray(vnode)) {\n warn(\n 'Multiple root nodes returned from render function. Render function ' +\n 'should return a single root node.',\n vm\n );\n }\n vnode = createEmptyVNode();\n }\n // set parent\n vnode.parent = _parentVnode;\n return vnode\n };\n}\n\n/* */\n\nfunction ensureCtor (comp, base) {\n if (\n comp.__esModule ||\n (hasSymbol && comp[Symbol.toStringTag] === 'Module')\n ) {\n comp = comp.default;\n }\n return isObject(comp)\n ? base.extend(comp)\n : comp\n}\n\nfunction createAsyncPlaceholder (\n factory,\n data,\n context,\n children,\n tag\n) {\n var node = createEmptyVNode();\n node.asyncFactory = factory;\n node.asyncMeta = { data: data, context: context, children: children, tag: tag };\n return node\n}\n\nfunction resolveAsyncComponent (\n factory,\n baseCtor\n) {\n if (isTrue(factory.error) && isDef(factory.errorComp)) {\n return factory.errorComp\n }\n\n if (isDef(factory.resolved)) {\n return factory.resolved\n }\n\n var owner = currentRenderingInstance;\n if (owner && isDef(factory.owners) && factory.owners.indexOf(owner) === -1) {\n // already pending\n factory.owners.push(owner);\n }\n\n if (isTrue(factory.loading) && isDef(factory.loadingComp)) {\n return factory.loadingComp\n }\n\n if (owner && !isDef(factory.owners)) {\n var owners = factory.owners = [owner];\n var sync = true;\n var timerLoading = null;\n var timerTimeout = null\n\n ;(owner).$on('hook:destroyed', function () { return remove(owners, owner); });\n\n var forceRender = function (renderCompleted) {\n for (var i = 0, l = owners.length; i < l; i++) {\n (owners[i]).$forceUpdate();\n }\n\n if (renderCompleted) {\n owners.length = 0;\n if (timerLoading !== null) {\n clearTimeout(timerLoading);\n timerLoading = null;\n }\n if (timerTimeout !== null) {\n clearTimeout(timerTimeout);\n timerTimeout = null;\n }\n }\n };\n\n var resolve = once(function (res) {\n // cache resolved\n factory.resolved = ensureCtor(res, baseCtor);\n // invoke callbacks only if this is not a synchronous resolve\n // (async resolves are shimmed as synchronous during SSR)\n if (!sync) {\n forceRender(true);\n } else {\n owners.length = 0;\n }\n });\n\n var reject = once(function (reason) {\n process.env.NODE_ENV !== 'production' && warn(\n \"Failed to resolve async component: \" + (String(factory)) +\n (reason ? (\"\\nReason: \" + reason) : '')\n );\n if (isDef(factory.errorComp)) {\n factory.error = true;\n forceRender(true);\n }\n });\n\n var res = factory(resolve, reject);\n\n if (isObject(res)) {\n if (isPromise(res)) {\n // () => Promise\n if (isUndef(factory.resolved)) {\n res.then(resolve, reject);\n }\n } else if (isPromise(res.component)) {\n res.component.then(resolve, reject);\n\n if (isDef(res.error)) {\n factory.errorComp = ensureCtor(res.error, baseCtor);\n }\n\n if (isDef(res.loading)) {\n factory.loadingComp = ensureCtor(res.loading, baseCtor);\n if (res.delay === 0) {\n factory.loading = true;\n } else {\n timerLoading = setTimeout(function () {\n timerLoading = null;\n if (isUndef(factory.resolved) && isUndef(factory.error)) {\n factory.loading = true;\n forceRender(false);\n }\n }, res.delay || 200);\n }\n }\n\n if (isDef(res.timeout)) {\n timerTimeout = setTimeout(function () {\n timerTimeout = null;\n if (isUndef(factory.resolved)) {\n reject(\n process.env.NODE_ENV !== 'production'\n ? (\"timeout (\" + (res.timeout) + \"ms)\")\n : null\n );\n }\n }, res.timeout);\n }\n }\n }\n\n sync = false;\n // return in case resolved synchronously\n return factory.loading\n ? factory.loadingComp\n : factory.resolved\n }\n}\n\n/* */\n\nfunction isAsyncPlaceholder (node) {\n return node.isComment && node.asyncFactory\n}\n\n/* */\n\nfunction getFirstComponentChild (children) {\n if (Array.isArray(children)) {\n for (var i = 0; i < children.length; i++) {\n var c = children[i];\n if (isDef(c) && (isDef(c.componentOptions) || isAsyncPlaceholder(c))) {\n return c\n }\n }\n }\n}\n\n/* */\n\n/* */\n\nfunction initEvents (vm) {\n vm._events = Object.create(null);\n vm._hasHookEvent = false;\n // init parent attached events\n var listeners = vm.$options._parentListeners;\n if (listeners) {\n updateComponentListeners(vm, listeners);\n }\n}\n\nvar target;\n\nfunction add (event, fn) {\n target.$on(event, fn);\n}\n\nfunction remove$1 (event, fn) {\n target.$off(event, fn);\n}\n\nfunction createOnceHandler (event, fn) {\n var _target = target;\n return function onceHandler () {\n var res = fn.apply(null, arguments);\n if (res !== null) {\n _target.$off(event, onceHandler);\n }\n }\n}\n\nfunction updateComponentListeners (\n vm,\n listeners,\n oldListeners\n) {\n target = vm;\n updateListeners(listeners, oldListeners || {}, add, remove$1, createOnceHandler, vm);\n target = undefined;\n}\n\nfunction eventsMixin (Vue) {\n var hookRE = /^hook:/;\n Vue.prototype.$on = function (event, fn) {\n var vm = this;\n if (Array.isArray(event)) {\n for (var i = 0, l = event.length; i < l; i++) {\n vm.$on(event[i], fn);\n }\n } else {\n (vm._events[event] || (vm._events[event] = [])).push(fn);\n // optimize hook:event cost by using a boolean flag marked at registration\n // instead of a hash lookup\n if (hookRE.test(event)) {\n vm._hasHookEvent = true;\n }\n }\n return vm\n };\n\n Vue.prototype.$once = function (event, fn) {\n var vm = this;\n function on () {\n vm.$off(event, on);\n fn.apply(vm, arguments);\n }\n on.fn = fn;\n vm.$on(event, on);\n return vm\n };\n\n Vue.prototype.$off = function (event, fn) {\n var vm = this;\n // all\n if (!arguments.length) {\n vm._events = Object.create(null);\n return vm\n }\n // array of events\n if (Array.isArray(event)) {\n for (var i$1 = 0, l = event.length; i$1 < l; i$1++) {\n vm.$off(event[i$1], fn);\n }\n return vm\n }\n // specific event\n var cbs = vm._events[event];\n if (!cbs) {\n return vm\n }\n if (!fn) {\n vm._events[event] = null;\n return vm\n }\n // specific handler\n var cb;\n var i = cbs.length;\n while (i--) {\n cb = cbs[i];\n if (cb === fn || cb.fn === fn) {\n cbs.splice(i, 1);\n break\n }\n }\n return vm\n };\n\n Vue.prototype.$emit = function (event) {\n var vm = this;\n if (process.env.NODE_ENV !== 'production') {\n var lowerCaseEvent = event.toLowerCase();\n if (lowerCaseEvent !== event && vm._events[lowerCaseEvent]) {\n tip(\n \"Event \\\"\" + lowerCaseEvent + \"\\\" is emitted in component \" +\n (formatComponentName(vm)) + \" but the handler is registered for \\\"\" + event + \"\\\". \" +\n \"Note that HTML attributes are case-insensitive and you cannot use \" +\n \"v-on to listen to camelCase events when using in-DOM templates. \" +\n \"You should probably use \\\"\" + (hyphenate(event)) + \"\\\" instead of \\\"\" + event + \"\\\".\"\n );\n }\n }\n var cbs = vm._events[event];\n if (cbs) {\n cbs = cbs.length > 1 ? toArray(cbs) : cbs;\n var args = toArray(arguments, 1);\n var info = \"event handler for \\\"\" + event + \"\\\"\";\n for (var i = 0, l = cbs.length; i < l; i++) {\n invokeWithErrorHandling(cbs[i], vm, args, vm, info);\n }\n }\n return vm\n };\n}\n\n/* */\n\nvar activeInstance = null;\nvar isUpdatingChildComponent = false;\n\nfunction setActiveInstance(vm) {\n var prevActiveInstance = activeInstance;\n activeInstance = vm;\n return function () {\n activeInstance = prevActiveInstance;\n }\n}\n\nfunction initLifecycle (vm) {\n var options = vm.$options;\n\n // locate first non-abstract parent\n var parent = options.parent;\n if (parent && !options.abstract) {\n while (parent.$options.abstract && parent.$parent) {\n parent = parent.$parent;\n }\n parent.$children.push(vm);\n }\n\n vm.$parent = parent;\n vm.$root = parent ? parent.$root : vm;\n\n vm.$children = [];\n vm.$refs = {};\n\n vm._watcher = null;\n vm._inactive = null;\n vm._directInactive = false;\n vm._isMounted = false;\n vm._isDestroyed = false;\n vm._isBeingDestroyed = false;\n}\n\nfunction lifecycleMixin (Vue) {\n Vue.prototype._update = function (vnode, hydrating) {\n var vm = this;\n var prevEl = vm.$el;\n var prevVnode = vm._vnode;\n var restoreActiveInstance = setActiveInstance(vm);\n vm._vnode = vnode;\n // Vue.prototype.__patch__ is injected in entry points\n // based on the rendering backend used.\n if (!prevVnode) {\n // initial render\n vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */);\n } else {\n // updates\n vm.$el = vm.__patch__(prevVnode, vnode);\n }\n restoreActiveInstance();\n // update __vue__ reference\n if (prevEl) {\n prevEl.__vue__ = null;\n }\n if (vm.$el) {\n vm.$el.__vue__ = vm;\n }\n // if parent is an HOC, update its $el as well\n if (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {\n vm.$parent.$el = vm.$el;\n }\n // updated hook is called by the scheduler to ensure that children are\n // updated in a parent's updated hook.\n };\n\n Vue.prototype.$forceUpdate = function () {\n var vm = this;\n if (vm._watcher) {\n vm._watcher.update();\n }\n };\n\n Vue.prototype.$destroy = function () {\n var vm = this;\n if (vm._isBeingDestroyed) {\n return\n }\n callHook(vm, 'beforeDestroy');\n vm._isBeingDestroyed = true;\n // remove self from parent\n var parent = vm.$parent;\n if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {\n remove(parent.$children, vm);\n }\n // teardown watchers\n if (vm._watcher) {\n vm._watcher.teardown();\n }\n var i = vm._watchers.length;\n while (i--) {\n vm._watchers[i].teardown();\n }\n // remove reference from data ob\n // frozen object may not have observer.\n if (vm._data.__ob__) {\n vm._data.__ob__.vmCount--;\n }\n // call the last hook...\n vm._isDestroyed = true;\n // invoke destroy hooks on current rendered tree\n vm.__patch__(vm._vnode, null);\n // fire destroyed hook\n callHook(vm, 'destroyed');\n // turn off all instance listeners.\n vm.$off();\n // remove __vue__ reference\n if (vm.$el) {\n vm.$el.__vue__ = null;\n }\n // release circular reference (#6759)\n if (vm.$vnode) {\n vm.$vnode.parent = null;\n }\n };\n}\n\nfunction mountComponent (\n vm,\n el,\n hydrating\n) {\n vm.$el = el;\n if (!vm.$options.render) {\n vm.$options.render = createEmptyVNode;\n if (process.env.NODE_ENV !== 'production') {\n /* istanbul ignore if */\n if ((vm.$options.template && vm.$options.template.charAt(0) !== '#') ||\n vm.$options.el || el) {\n warn(\n 'You are using the runtime-only build of Vue where the template ' +\n 'compiler is not available. Either pre-compile the templates into ' +\n 'render functions, or use the compiler-included build.',\n vm\n );\n } else {\n warn(\n 'Failed to mount component: template or render function not defined.',\n vm\n );\n }\n }\n }\n callHook(vm, 'beforeMount');\n\n var updateComponent;\n /* istanbul ignore if */\n if (process.env.NODE_ENV !== 'production' && config.performance && mark) {\n updateComponent = function () {\n var name = vm._name;\n var id = vm._uid;\n var startTag = \"vue-perf-start:\" + id;\n var endTag = \"vue-perf-end:\" + id;\n\n mark(startTag);\n var vnode = vm._render();\n mark(endTag);\n measure((\"vue \" + name + \" render\"), startTag, endTag);\n\n mark(startTag);\n vm._update(vnode, hydrating);\n mark(endTag);\n measure((\"vue \" + name + \" patch\"), startTag, endTag);\n };\n } else {\n updateComponent = function () {\n vm._update(vm._render(), hydrating);\n };\n }\n\n // we set this to vm._watcher inside the watcher's constructor\n // since the watcher's initial patch may call $forceUpdate (e.g. inside child\n // component's mounted hook), which relies on vm._watcher being already defined\n new Watcher(vm, updateComponent, noop, {\n before: function before () {\n if (vm._isMounted && !vm._isDestroyed) {\n callHook(vm, 'beforeUpdate');\n }\n }\n }, true /* isRenderWatcher */);\n hydrating = false;\n\n // manually mounted instance, call mounted on self\n // mounted is called for render-created child components in its inserted hook\n if (vm.$vnode == null) {\n vm._isMounted = true;\n callHook(vm, 'mounted');\n }\n return vm\n}\n\nfunction updateChildComponent (\n vm,\n propsData,\n listeners,\n parentVnode,\n renderChildren\n) {\n if (process.env.NODE_ENV !== 'production') {\n isUpdatingChildComponent = true;\n }\n\n // determine whether component has slot children\n // we need to do this before overwriting $options._renderChildren.\n\n // check if there are dynamic scopedSlots (hand-written or compiled but with\n // dynamic slot names). Static scoped slots compiled from template has the\n // \"$stable\" marker.\n var newScopedSlots = parentVnode.data.scopedSlots;\n var oldScopedSlots = vm.$scopedSlots;\n var hasDynamicScopedSlot = !!(\n (newScopedSlots && !newScopedSlots.$stable) ||\n (oldScopedSlots !== emptyObject && !oldScopedSlots.$stable) ||\n (newScopedSlots && vm.$scopedSlots.$key !== newScopedSlots.$key)\n );\n\n // Any static slot children from the parent may have changed during parent's\n // update. Dynamic scoped slots may also have changed. In such cases, a forced\n // update is necessary to ensure correctness.\n var needsForceUpdate = !!(\n renderChildren || // has new static slots\n vm.$options._renderChildren || // has old static slots\n hasDynamicScopedSlot\n );\n\n vm.$options._parentVnode = parentVnode;\n vm.$vnode = parentVnode; // update vm's placeholder node without re-render\n\n if (vm._vnode) { // update child tree's parent\n vm._vnode.parent = parentVnode;\n }\n vm.$options._renderChildren = renderChildren;\n\n // update $attrs and $listeners hash\n // these are also reactive so they may trigger child update if the child\n // used them during render\n vm.$attrs = parentVnode.data.attrs || emptyObject;\n vm.$listeners = listeners || emptyObject;\n\n // update props\n if (propsData && vm.$options.props) {\n toggleObserving(false);\n var props = vm._props;\n var propKeys = vm.$options._propKeys || [];\n for (var i = 0; i < propKeys.length; i++) {\n var key = propKeys[i];\n var propOptions = vm.$options.props; // wtf flow?\n props[key] = validateProp(key, propOptions, propsData, vm);\n }\n toggleObserving(true);\n // keep a copy of raw propsData\n vm.$options.propsData = propsData;\n }\n\n // update listeners\n listeners = listeners || emptyObject;\n var oldListeners = vm.$options._parentListeners;\n vm.$options._parentListeners = listeners;\n updateComponentListeners(vm, listeners, oldListeners);\n\n // resolve slots + force update if has children\n if (needsForceUpdate) {\n vm.$slots = resolveSlots(renderChildren, parentVnode.context);\n vm.$forceUpdate();\n }\n\n if (process.env.NODE_ENV !== 'production') {\n isUpdatingChildComponent = false;\n }\n}\n\nfunction isInInactiveTree (vm) {\n while (vm && (vm = vm.$parent)) {\n if (vm._inactive) { return true }\n }\n return false\n}\n\nfunction activateChildComponent (vm, direct) {\n if (direct) {\n vm._directInactive = false;\n if (isInInactiveTree(vm)) {\n return\n }\n } else if (vm._directInactive) {\n return\n }\n if (vm._inactive || vm._inactive === null) {\n vm._inactive = false;\n for (var i = 0; i < vm.$children.length; i++) {\n activateChildComponent(vm.$children[i]);\n }\n callHook(vm, 'activated');\n }\n}\n\nfunction deactivateChildComponent (vm, direct) {\n if (direct) {\n vm._directInactive = true;\n if (isInInactiveTree(vm)) {\n return\n }\n }\n if (!vm._inactive) {\n vm._inactive = true;\n for (var i = 0; i < vm.$children.length; i++) {\n deactivateChildComponent(vm.$children[i]);\n }\n callHook(vm, 'deactivated');\n }\n}\n\nfunction callHook (vm, hook) {\n // #7573 disable dep collection when invoking lifecycle hooks\n pushTarget();\n var handlers = vm.$options[hook];\n var info = hook + \" hook\";\n if (handlers) {\n for (var i = 0, j = handlers.length; i < j; i++) {\n invokeWithErrorHandling(handlers[i], vm, null, vm, info);\n }\n }\n if (vm._hasHookEvent) {\n vm.$emit('hook:' + hook);\n }\n popTarget();\n}\n\n/* */\n\nvar MAX_UPDATE_COUNT = 100;\n\nvar queue = [];\nvar activatedChildren = [];\nvar has = {};\nvar circular = {};\nvar waiting = false;\nvar flushing = false;\nvar index = 0;\n\n/**\n * Reset the scheduler's state.\n */\nfunction resetSchedulerState () {\n index = queue.length = activatedChildren.length = 0;\n has = {};\n if (process.env.NODE_ENV !== 'production') {\n circular = {};\n }\n waiting = flushing = false;\n}\n\n// Async edge case #6566 requires saving the timestamp when event listeners are\n// attached. However, calling performance.now() has a perf overhead especially\n// if the page has thousands of event listeners. Instead, we take a timestamp\n// every time the scheduler flushes and use that for all event listeners\n// attached during that flush.\nvar currentFlushTimestamp = 0;\n\n// Async edge case fix requires storing an event listener's attach timestamp.\nvar getNow = Date.now;\n\n// Determine what event timestamp the browser is using. Annoyingly, the\n// timestamp can either be hi-res (relative to page load) or low-res\n// (relative to UNIX epoch), so in order to compare time we have to use the\n// same timestamp type when saving the flush timestamp.\n// All IE versions use low-res event timestamps, and have problematic clock\n// implementations (#9632)\nif (inBrowser && !isIE) {\n var performance = window.performance;\n if (\n performance &&\n typeof performance.now === 'function' &&\n getNow() > document.createEvent('Event').timeStamp\n ) {\n // if the event timestamp, although evaluated AFTER the Date.now(), is\n // smaller than it, it means the event is using a hi-res timestamp,\n // and we need to use the hi-res version for event listener timestamps as\n // well.\n getNow = function () { return performance.now(); };\n }\n}\n\n/**\n * Flush both queues and run the watchers.\n */\nfunction flushSchedulerQueue () {\n currentFlushTimestamp = getNow();\n flushing = true;\n var watcher, id;\n\n // Sort queue before flush.\n // This ensures that:\n // 1. Components are updated from parent to child. (because parent is always\n // created before the child)\n // 2. A component's user watchers are run before its render watcher (because\n // user watchers are created before the render watcher)\n // 3. If a component is destroyed during a parent component's watcher run,\n // its watchers can be skipped.\n queue.sort(function (a, b) { return a.id - b.id; });\n\n // do not cache length because more watchers might be pushed\n // as we run existing watchers\n for (index = 0; index < queue.length; index++) {\n watcher = queue[index];\n if (watcher.before) {\n watcher.before();\n }\n id = watcher.id;\n has[id] = null;\n watcher.run();\n // in dev build, check and stop circular updates.\n if (process.env.NODE_ENV !== 'production' && has[id] != null) {\n circular[id] = (circular[id] || 0) + 1;\n if (circular[id] > MAX_UPDATE_COUNT) {\n warn(\n 'You may have an infinite update loop ' + (\n watcher.user\n ? (\"in watcher with expression \\\"\" + (watcher.expression) + \"\\\"\")\n : \"in a component render function.\"\n ),\n watcher.vm\n );\n break\n }\n }\n }\n\n // keep copies of post queues before resetting state\n var activatedQueue = activatedChildren.slice();\n var updatedQueue = queue.slice();\n\n resetSchedulerState();\n\n // call component updated and activated hooks\n callActivatedHooks(activatedQueue);\n callUpdatedHooks(updatedQueue);\n\n // devtool hook\n /* istanbul ignore if */\n if (devtools && config.devtools) {\n devtools.emit('flush');\n }\n}\n\nfunction callUpdatedHooks (queue) {\n var i = queue.length;\n while (i--) {\n var watcher = queue[i];\n var vm = watcher.vm;\n if (vm._watcher === watcher && vm._isMounted && !vm._isDestroyed) {\n callHook(vm, 'updated');\n }\n }\n}\n\n/**\n * Queue a kept-alive component that was activated during patch.\n * The queue will be processed after the entire tree has been patched.\n */\nfunction queueActivatedComponent (vm) {\n // setting _inactive to false here so that a render function can\n // rely on checking whether it's in an inactive tree (e.g. router-view)\n vm._inactive = false;\n activatedChildren.push(vm);\n}\n\nfunction callActivatedHooks (queue) {\n for (var i = 0; i < queue.length; i++) {\n queue[i]._inactive = true;\n activateChildComponent(queue[i], true /* true */);\n }\n}\n\n/**\n * Push a watcher into the watcher queue.\n * Jobs with duplicate IDs will be skipped unless it's\n * pushed when the queue is being flushed.\n */\nfunction queueWatcher (watcher) {\n var id = watcher.id;\n if (has[id] == null) {\n has[id] = true;\n if (!flushing) {\n queue.push(watcher);\n } else {\n // if already flushing, splice the watcher based on its id\n // if already past its id, it will be run next immediately.\n var i = queue.length - 1;\n while (i > index && queue[i].id > watcher.id) {\n i--;\n }\n queue.splice(i + 1, 0, watcher);\n }\n // queue the flush\n if (!waiting) {\n waiting = true;\n\n if (process.env.NODE_ENV !== 'production' && !config.async) {\n flushSchedulerQueue();\n return\n }\n nextTick(flushSchedulerQueue);\n }\n }\n}\n\n/* */\n\n\n\nvar uid$2 = 0;\n\n/**\n * A watcher parses an expression, collects dependencies,\n * and fires callback when the expression value changes.\n * This is used for both the $watch() api and directives.\n */\nvar Watcher = function Watcher (\n vm,\n expOrFn,\n cb,\n options,\n isRenderWatcher\n) {\n this.vm = vm;\n if (isRenderWatcher) {\n vm._watcher = this;\n }\n vm._watchers.push(this);\n // options\n if (options) {\n this.deep = !!options.deep;\n this.user = !!options.user;\n this.lazy = !!options.lazy;\n this.sync = !!options.sync;\n this.before = options.before;\n } else {\n this.deep = this.user = this.lazy = this.sync = false;\n }\n this.cb = cb;\n this.id = ++uid$2; // uid for batching\n this.active = true;\n this.dirty = this.lazy; // for lazy watchers\n this.deps = [];\n this.newDeps = [];\n this.depIds = new _Set();\n this.newDepIds = new _Set();\n this.expression = process.env.NODE_ENV !== 'production'\n ? expOrFn.toString()\n : '';\n // parse expression for getter\n if (typeof expOrFn === 'function') {\n this.getter = expOrFn;\n } else {\n this.getter = parsePath(expOrFn);\n if (!this.getter) {\n this.getter = noop;\n process.env.NODE_ENV !== 'production' && warn(\n \"Failed watching path: \\\"\" + expOrFn + \"\\\" \" +\n 'Watcher only accepts simple dot-delimited paths. ' +\n 'For full control, use a function instead.',\n vm\n );\n }\n }\n this.value = this.lazy\n ? undefined\n : this.get();\n};\n\n/**\n * Evaluate the getter, and re-collect dependencies.\n */\nWatcher.prototype.get = function get () {\n pushTarget(this);\n var value;\n var vm = this.vm;\n try {\n value = this.getter.call(vm, vm);\n } catch (e) {\n if (this.user) {\n handleError(e, vm, (\"getter for watcher \\\"\" + (this.expression) + \"\\\"\"));\n } else {\n throw e\n }\n } finally {\n // \"touch\" every property so they are all tracked as\n // dependencies for deep watching\n if (this.deep) {\n traverse(value);\n }\n popTarget();\n this.cleanupDeps();\n }\n return value\n};\n\n/**\n * Add a dependency to this directive.\n */\nWatcher.prototype.addDep = function addDep (dep) {\n var id = dep.id;\n if (!this.newDepIds.has(id)) {\n this.newDepIds.add(id);\n this.newDeps.push(dep);\n if (!this.depIds.has(id)) {\n dep.addSub(this);\n }\n }\n};\n\n/**\n * Clean up for dependency collection.\n */\nWatcher.prototype.cleanupDeps = function cleanupDeps () {\n var i = this.deps.length;\n while (i--) {\n var dep = this.deps[i];\n if (!this.newDepIds.has(dep.id)) {\n dep.removeSub(this);\n }\n }\n var tmp = this.depIds;\n this.depIds = this.newDepIds;\n this.newDepIds = tmp;\n this.newDepIds.clear();\n tmp = this.deps;\n this.deps = this.newDeps;\n this.newDeps = tmp;\n this.newDeps.length = 0;\n};\n\n/**\n * Subscriber interface.\n * Will be called when a dependency changes.\n */\nWatcher.prototype.update = function update () {\n /* istanbul ignore else */\n if (this.lazy) {\n this.dirty = true;\n } else if (this.sync) {\n this.run();\n } else {\n queueWatcher(this);\n }\n};\n\n/**\n * Scheduler job interface.\n * Will be called by the scheduler.\n */\nWatcher.prototype.run = function run () {\n if (this.active) {\n var value = this.get();\n if (\n value !== this.value ||\n // Deep watchers and watchers on Object/Arrays should fire even\n // when the value is the same, because the value may\n // have mutated.\n isObject(value) ||\n this.deep\n ) {\n // set new value\n var oldValue = this.value;\n this.value = value;\n if (this.user) {\n try {\n this.cb.call(this.vm, value, oldValue);\n } catch (e) {\n handleError(e, this.vm, (\"callback for watcher \\\"\" + (this.expression) + \"\\\"\"));\n }\n } else {\n this.cb.call(this.vm, value, oldValue);\n }\n }\n }\n};\n\n/**\n * Evaluate the value of the watcher.\n * This only gets called for lazy watchers.\n */\nWatcher.prototype.evaluate = function evaluate () {\n this.value = this.get();\n this.dirty = false;\n};\n\n/**\n * Depend on all deps collected by this watcher.\n */\nWatcher.prototype.depend = function depend () {\n var i = this.deps.length;\n while (i--) {\n this.deps[i].depend();\n }\n};\n\n/**\n * Remove self from all dependencies' subscriber list.\n */\nWatcher.prototype.teardown = function teardown () {\n if (this.active) {\n // remove self from vm's watcher list\n // this is a somewhat expensive operation so we skip it\n // if the vm is being destroyed.\n if (!this.vm._isBeingDestroyed) {\n remove(this.vm._watchers, this);\n }\n var i = this.deps.length;\n while (i--) {\n this.deps[i].removeSub(this);\n }\n this.active = false;\n }\n};\n\n/* */\n\nvar sharedPropertyDefinition = {\n enumerable: true,\n configurable: true,\n get: noop,\n set: noop\n};\n\nfunction proxy (target, sourceKey, key) {\n sharedPropertyDefinition.get = function proxyGetter () {\n return this[sourceKey][key]\n };\n sharedPropertyDefinition.set = function proxySetter (val) {\n this[sourceKey][key] = val;\n };\n Object.defineProperty(target, key, sharedPropertyDefinition);\n}\n\nfunction initState (vm) {\n vm._watchers = [];\n var opts = vm.$options;\n if (opts.props) { initProps(vm, opts.props); }\n if (opts.methods) { initMethods(vm, opts.methods); }\n if (opts.data) {\n initData(vm);\n } else {\n observe(vm._data = {}, true /* asRootData */);\n }\n if (opts.computed) { initComputed(vm, opts.computed); }\n if (opts.watch && opts.watch !== nativeWatch) {\n initWatch(vm, opts.watch);\n }\n}\n\nfunction initProps (vm, propsOptions) {\n var propsData = vm.$options.propsData || {};\n var props = vm._props = {};\n // cache prop keys so that future props updates can iterate using Array\n // instead of dynamic object key enumeration.\n var keys = vm.$options._propKeys = [];\n var isRoot = !vm.$parent;\n // root instance props should be converted\n if (!isRoot) {\n toggleObserving(false);\n }\n var loop = function ( key ) {\n keys.push(key);\n var value = validateProp(key, propsOptions, propsData, vm);\n /* istanbul ignore else */\n if (process.env.NODE_ENV !== 'production') {\n var hyphenatedKey = hyphenate(key);\n if (isReservedAttribute(hyphenatedKey) ||\n config.isReservedAttr(hyphenatedKey)) {\n warn(\n (\"\\\"\" + hyphenatedKey + \"\\\" is a reserved attribute and cannot be used as component prop.\"),\n vm\n );\n }\n defineReactive$$1(props, key, value, function () {\n if (!isRoot && !isUpdatingChildComponent) {\n warn(\n \"Avoid mutating a prop directly since the value will be \" +\n \"overwritten whenever the parent component re-renders. \" +\n \"Instead, use a data or computed property based on the prop's \" +\n \"value. Prop being mutated: \\\"\" + key + \"\\\"\",\n vm\n );\n }\n });\n } else {\n defineReactive$$1(props, key, value);\n }\n // static props are already proxied on the component's prototype\n // during Vue.extend(). We only need to proxy props defined at\n // instantiation here.\n if (!(key in vm)) {\n proxy(vm, \"_props\", key);\n }\n };\n\n for (var key in propsOptions) loop( key );\n toggleObserving(true);\n}\n\nfunction initData (vm) {\n var data = vm.$options.data;\n data = vm._data = typeof data === 'function'\n ? getData(data, vm)\n : data || {};\n if (!isPlainObject(data)) {\n data = {};\n process.env.NODE_ENV !== 'production' && warn(\n 'data functions should return an object:\\n' +\n 'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',\n vm\n );\n }\n // proxy data on instance\n var keys = Object.keys(data);\n var props = vm.$options.props;\n var methods = vm.$options.methods;\n var i = keys.length;\n while (i--) {\n var key = keys[i];\n if (process.env.NODE_ENV !== 'production') {\n if (methods && hasOwn(methods, key)) {\n warn(\n (\"Method \\\"\" + key + \"\\\" has already been defined as a data property.\"),\n vm\n );\n }\n }\n if (props && hasOwn(props, key)) {\n process.env.NODE_ENV !== 'production' && warn(\n \"The data property \\\"\" + key + \"\\\" is already declared as a prop. \" +\n \"Use prop default value instead.\",\n vm\n );\n } else if (!isReserved(key)) {\n proxy(vm, \"_data\", key);\n }\n }\n // observe data\n observe(data, true /* asRootData */);\n}\n\nfunction getData (data, vm) {\n // #7573 disable dep collection when invoking data getters\n pushTarget();\n try {\n return data.call(vm, vm)\n } catch (e) {\n handleError(e, vm, \"data()\");\n return {}\n } finally {\n popTarget();\n }\n}\n\nvar computedWatcherOptions = { lazy: true };\n\nfunction initComputed (vm, computed) {\n // $flow-disable-line\n var watchers = vm._computedWatchers = Object.create(null);\n // computed properties are just getters during SSR\n var isSSR = isServerRendering();\n\n for (var key in computed) {\n var userDef = computed[key];\n var getter = typeof userDef === 'function' ? userDef : userDef.get;\n if (process.env.NODE_ENV !== 'production' && getter == null) {\n warn(\n (\"Getter is missing for computed property \\\"\" + key + \"\\\".\"),\n vm\n );\n }\n\n if (!isSSR) {\n // create internal watcher for the computed property.\n watchers[key] = new Watcher(\n vm,\n getter || noop,\n noop,\n computedWatcherOptions\n );\n }\n\n // component-defined computed properties are already defined on the\n // component prototype. We only need to define computed properties defined\n // at instantiation here.\n if (!(key in vm)) {\n defineComputed(vm, key, userDef);\n } else if (process.env.NODE_ENV !== 'production') {\n if (key in vm.$data) {\n warn((\"The computed property \\\"\" + key + \"\\\" is already defined in data.\"), vm);\n } else if (vm.$options.props && key in vm.$options.props) {\n warn((\"The computed property \\\"\" + key + \"\\\" is already defined as a prop.\"), vm);\n }\n }\n }\n}\n\nfunction defineComputed (\n target,\n key,\n userDef\n) {\n var shouldCache = !isServerRendering();\n if (typeof userDef === 'function') {\n sharedPropertyDefinition.get = shouldCache\n ? createComputedGetter(key)\n : createGetterInvoker(userDef);\n sharedPropertyDefinition.set = noop;\n } else {\n sharedPropertyDefinition.get = userDef.get\n ? shouldCache && userDef.cache !== false\n ? createComputedGetter(key)\n : createGetterInvoker(userDef.get)\n : noop;\n sharedPropertyDefinition.set = userDef.set || noop;\n }\n if (process.env.NODE_ENV !== 'production' &&\n sharedPropertyDefinition.set === noop) {\n sharedPropertyDefinition.set = function () {\n warn(\n (\"Computed property \\\"\" + key + \"\\\" was assigned to but it has no setter.\"),\n this\n );\n };\n }\n Object.defineProperty(target, key, sharedPropertyDefinition);\n}\n\nfunction createComputedGetter (key) {\n return function computedGetter () {\n var watcher = this._computedWatchers && this._computedWatchers[key];\n if (watcher) {\n if (watcher.dirty) {\n watcher.evaluate();\n }\n if (Dep.target) {\n watcher.depend();\n }\n return watcher.value\n }\n }\n}\n\nfunction createGetterInvoker(fn) {\n return function computedGetter () {\n return fn.call(this, this)\n }\n}\n\nfunction initMethods (vm, methods) {\n var props = vm.$options.props;\n for (var key in methods) {\n if (process.env.NODE_ENV !== 'production') {\n if (typeof methods[key] !== 'function') {\n warn(\n \"Method \\\"\" + key + \"\\\" has type \\\"\" + (typeof methods[key]) + \"\\\" in the component definition. \" +\n \"Did you reference the function correctly?\",\n vm\n );\n }\n if (props && hasOwn(props, key)) {\n warn(\n (\"Method \\\"\" + key + \"\\\" has already been defined as a prop.\"),\n vm\n );\n }\n if ((key in vm) && isReserved(key)) {\n warn(\n \"Method \\\"\" + key + \"\\\" conflicts with an existing Vue instance method. \" +\n \"Avoid defining component methods that start with _ or $.\"\n );\n }\n }\n vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm);\n }\n}\n\nfunction initWatch (vm, watch) {\n for (var key in watch) {\n var handler = watch[key];\n if (Array.isArray(handler)) {\n for (var i = 0; i < handler.length; i++) {\n createWatcher(vm, key, handler[i]);\n }\n } else {\n createWatcher(vm, key, handler);\n }\n }\n}\n\nfunction createWatcher (\n vm,\n expOrFn,\n handler,\n options\n) {\n if (isPlainObject(handler)) {\n options = handler;\n handler = handler.handler;\n }\n if (typeof handler === 'string') {\n handler = vm[handler];\n }\n return vm.$watch(expOrFn, handler, options)\n}\n\nfunction stateMixin (Vue) {\n // flow somehow has problems with directly declared definition object\n // when using Object.defineProperty, so we have to procedurally build up\n // the object here.\n var dataDef = {};\n dataDef.get = function () { return this._data };\n var propsDef = {};\n propsDef.get = function () { return this._props };\n if (process.env.NODE_ENV !== 'production') {\n dataDef.set = function () {\n warn(\n 'Avoid replacing instance root $data. ' +\n 'Use nested data properties instead.',\n this\n );\n };\n propsDef.set = function () {\n warn(\"$props is readonly.\", this);\n };\n }\n Object.defineProperty(Vue.prototype, '$data', dataDef);\n Object.defineProperty(Vue.prototype, '$props', propsDef);\n\n Vue.prototype.$set = set;\n Vue.prototype.$delete = del;\n\n Vue.prototype.$watch = function (\n expOrFn,\n cb,\n options\n ) {\n var vm = this;\n if (isPlainObject(cb)) {\n return createWatcher(vm, expOrFn, cb, options)\n }\n options = options || {};\n options.user = true;\n var watcher = new Watcher(vm, expOrFn, cb, options);\n if (options.immediate) {\n try {\n cb.call(vm, watcher.value);\n } catch (error) {\n handleError(error, vm, (\"callback for immediate watcher \\\"\" + (watcher.expression) + \"\\\"\"));\n }\n }\n return function unwatchFn () {\n watcher.teardown();\n }\n };\n}\n\n/* */\n\nvar uid$3 = 0;\n\nfunction initMixin (Vue) {\n Vue.prototype._init = function (options) {\n var vm = this;\n // a uid\n vm._uid = uid$3++;\n\n var startTag, endTag;\n /* istanbul ignore if */\n if (process.env.NODE_ENV !== 'production' && config.performance && mark) {\n startTag = \"vue-perf-start:\" + (vm._uid);\n endTag = \"vue-perf-end:\" + (vm._uid);\n mark(startTag);\n }\n\n // a flag to avoid this being observed\n vm._isVue = true;\n // merge options\n if (options && options._isComponent) {\n // optimize internal component instantiation\n // since dynamic options merging is pretty slow, and none of the\n // internal component options needs special treatment.\n initInternalComponent(vm, options);\n } else {\n vm.$options = mergeOptions(\n resolveConstructorOptions(vm.constructor),\n options || {},\n vm\n );\n }\n /* istanbul ignore else */\n if (process.env.NODE_ENV !== 'production') {\n initProxy(vm);\n } else {\n vm._renderProxy = vm;\n }\n // expose real self\n vm._self = vm;\n initLifecycle(vm);\n initEvents(vm);\n initRender(vm);\n callHook(vm, 'beforeCreate');\n initInjections(vm); // resolve injections before data/props\n initState(vm);\n initProvide(vm); // resolve provide after data/props\n callHook(vm, 'created');\n\n /* istanbul ignore if */\n if (process.env.NODE_ENV !== 'production' && config.performance && mark) {\n vm._name = formatComponentName(vm, false);\n mark(endTag);\n measure((\"vue \" + (vm._name) + \" init\"), startTag, endTag);\n }\n\n if (vm.$options.el) {\n vm.$mount(vm.$options.el);\n }\n };\n}\n\nfunction initInternalComponent (vm, options) {\n var opts = vm.$options = Object.create(vm.constructor.options);\n // doing this because it's faster than dynamic enumeration.\n var parentVnode = options._parentVnode;\n opts.parent = options.parent;\n opts._parentVnode = parentVnode;\n\n var vnodeComponentOptions = parentVnode.componentOptions;\n opts.propsData = vnodeComponentOptions.propsData;\n opts._parentListeners = vnodeComponentOptions.listeners;\n opts._renderChildren = vnodeComponentOptions.children;\n opts._componentTag = vnodeComponentOptions.tag;\n\n if (options.render) {\n opts.render = options.render;\n opts.staticRenderFns = options.staticRenderFns;\n }\n}\n\nfunction resolveConstructorOptions (Ctor) {\n var options = Ctor.options;\n if (Ctor.super) {\n var superOptions = resolveConstructorOptions(Ctor.super);\n var cachedSuperOptions = Ctor.superOptions;\n if (superOptions !== cachedSuperOptions) {\n // super option changed,\n // need to resolve new options.\n Ctor.superOptions = superOptions;\n // check if there are any late-modified/attached options (#4976)\n var modifiedOptions = resolveModifiedOptions(Ctor);\n // update base extend options\n if (modifiedOptions) {\n extend(Ctor.extendOptions, modifiedOptions);\n }\n options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions);\n if (options.name) {\n options.components[options.name] = Ctor;\n }\n }\n }\n return options\n}\n\nfunction resolveModifiedOptions (Ctor) {\n var modified;\n var latest = Ctor.options;\n var sealed = Ctor.sealedOptions;\n for (var key in latest) {\n if (latest[key] !== sealed[key]) {\n if (!modified) { modified = {}; }\n modified[key] = latest[key];\n }\n }\n return modified\n}\n\nfunction Vue (options) {\n if (process.env.NODE_ENV !== 'production' &&\n !(this instanceof Vue)\n ) {\n warn('Vue is a constructor and should be called with the `new` keyword');\n }\n this._init(options);\n}\n\ninitMixin(Vue);\nstateMixin(Vue);\neventsMixin(Vue);\nlifecycleMixin(Vue);\nrenderMixin(Vue);\n\n/* */\n\nfunction initUse (Vue) {\n Vue.use = function (plugin) {\n var installedPlugins = (this._installedPlugins || (this._installedPlugins = []));\n if (installedPlugins.indexOf(plugin) > -1) {\n return this\n }\n\n // additional parameters\n var args = toArray(arguments, 1);\n args.unshift(this);\n if (typeof plugin.install === 'function') {\n plugin.install.apply(plugin, args);\n } else if (typeof plugin === 'function') {\n plugin.apply(null, args);\n }\n installedPlugins.push(plugin);\n return this\n };\n}\n\n/* */\n\nfunction initMixin$1 (Vue) {\n Vue.mixin = function (mixin) {\n this.options = mergeOptions(this.options, mixin);\n return this\n };\n}\n\n/* */\n\nfunction initExtend (Vue) {\n /**\n * Each instance constructor, including Vue, has a unique\n * cid. This enables us to create wrapped \"child\n * constructors\" for prototypal inheritance and cache them.\n */\n Vue.cid = 0;\n var cid = 1;\n\n /**\n * Class inheritance\n */\n Vue.extend = function (extendOptions) {\n extendOptions = extendOptions || {};\n var Super = this;\n var SuperId = Super.cid;\n var cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {});\n if (cachedCtors[SuperId]) {\n return cachedCtors[SuperId]\n }\n\n var name = extendOptions.name || Super.options.name;\n if (process.env.NODE_ENV !== 'production' && name) {\n validateComponentName(name);\n }\n\n var Sub = function VueComponent (options) {\n this._init(options);\n };\n Sub.prototype = Object.create(Super.prototype);\n Sub.prototype.constructor = Sub;\n Sub.cid = cid++;\n Sub.options = mergeOptions(\n Super.options,\n extendOptions\n );\n Sub['super'] = Super;\n\n // For props and computed properties, we define the proxy getters on\n // the Vue instances at extension time, on the extended prototype. This\n // avoids Object.defineProperty calls for each instance created.\n if (Sub.options.props) {\n initProps$1(Sub);\n }\n if (Sub.options.computed) {\n initComputed$1(Sub);\n }\n\n // allow further extension/mixin/plugin usage\n Sub.extend = Super.extend;\n Sub.mixin = Super.mixin;\n Sub.use = Super.use;\n\n // create asset registers, so extended classes\n // can have their private assets too.\n ASSET_TYPES.forEach(function (type) {\n Sub[type] = Super[type];\n });\n // enable recursive self-lookup\n if (name) {\n Sub.options.components[name] = Sub;\n }\n\n // keep a reference to the super options at extension time.\n // later at instantiation we can check if Super's options have\n // been updated.\n Sub.superOptions = Super.options;\n Sub.extendOptions = extendOptions;\n Sub.sealedOptions = extend({}, Sub.options);\n\n // cache constructor\n cachedCtors[SuperId] = Sub;\n return Sub\n };\n}\n\nfunction initProps$1 (Comp) {\n var props = Comp.options.props;\n for (var key in props) {\n proxy(Comp.prototype, \"_props\", key);\n }\n}\n\nfunction initComputed$1 (Comp) {\n var computed = Comp.options.computed;\n for (var key in computed) {\n defineComputed(Comp.prototype, key, computed[key]);\n }\n}\n\n/* */\n\nfunction initAssetRegisters (Vue) {\n /**\n * Create asset registration methods.\n */\n ASSET_TYPES.forEach(function (type) {\n Vue[type] = function (\n id,\n definition\n ) {\n if (!definition) {\n return this.options[type + 's'][id]\n } else {\n /* istanbul ignore if */\n if (process.env.NODE_ENV !== 'production' && type === 'component') {\n validateComponentName(id);\n }\n if (type === 'component' && isPlainObject(definition)) {\n definition.name = definition.name || id;\n definition = this.options._base.extend(definition);\n }\n if (type === 'directive' && typeof definition === 'function') {\n definition = { bind: definition, update: definition };\n }\n this.options[type + 's'][id] = definition;\n return definition\n }\n };\n });\n}\n\n/* */\n\n\n\nfunction getComponentName (opts) {\n return opts && (opts.Ctor.options.name || opts.tag)\n}\n\nfunction matches (pattern, name) {\n if (Array.isArray(pattern)) {\n return pattern.indexOf(name) > -1\n } else if (typeof pattern === 'string') {\n return pattern.split(',').indexOf(name) > -1\n } else if (isRegExp(pattern)) {\n return pattern.test(name)\n }\n /* istanbul ignore next */\n return false\n}\n\nfunction pruneCache (keepAliveInstance, filter) {\n var cache = keepAliveInstance.cache;\n var keys = keepAliveInstance.keys;\n var _vnode = keepAliveInstance._vnode;\n for (var key in cache) {\n var cachedNode = cache[key];\n if (cachedNode) {\n var name = getComponentName(cachedNode.componentOptions);\n if (name && !filter(name)) {\n pruneCacheEntry(cache, key, keys, _vnode);\n }\n }\n }\n}\n\nfunction pruneCacheEntry (\n cache,\n key,\n keys,\n current\n) {\n var cached$$1 = cache[key];\n if (cached$$1 && (!current || cached$$1.tag !== current.tag)) {\n cached$$1.componentInstance.$destroy();\n }\n cache[key] = null;\n remove(keys, key);\n}\n\nvar patternTypes = [String, RegExp, Array];\n\nvar KeepAlive = {\n name: 'keep-alive',\n abstract: true,\n\n props: {\n include: patternTypes,\n exclude: patternTypes,\n max: [String, Number]\n },\n\n created: function created () {\n this.cache = Object.create(null);\n this.keys = [];\n },\n\n destroyed: function destroyed () {\n for (var key in this.cache) {\n pruneCacheEntry(this.cache, key, this.keys);\n }\n },\n\n mounted: function mounted () {\n var this$1 = this;\n\n this.$watch('include', function (val) {\n pruneCache(this$1, function (name) { return matches(val, name); });\n });\n this.$watch('exclude', function (val) {\n pruneCache(this$1, function (name) { return !matches(val, name); });\n });\n },\n\n render: function render () {\n var slot = this.$slots.default;\n var vnode = getFirstComponentChild(slot);\n var componentOptions = vnode && vnode.componentOptions;\n if (componentOptions) {\n // check pattern\n var name = getComponentName(componentOptions);\n var ref = this;\n var include = ref.include;\n var exclude = ref.exclude;\n if (\n // not included\n (include && (!name || !matches(include, name))) ||\n // excluded\n (exclude && name && matches(exclude, name))\n ) {\n return vnode\n }\n\n var ref$1 = this;\n var cache = ref$1.cache;\n var keys = ref$1.keys;\n var key = vnode.key == null\n // same constructor may get registered as different local components\n // so cid alone is not enough (#3269)\n ? componentOptions.Ctor.cid + (componentOptions.tag ? (\"::\" + (componentOptions.tag)) : '')\n : vnode.key;\n if (cache[key]) {\n vnode.componentInstance = cache[key].componentInstance;\n // make current key freshest\n remove(keys, key);\n keys.push(key);\n } else {\n cache[key] = vnode;\n keys.push(key);\n // prune oldest entry\n if (this.max && keys.length > parseInt(this.max)) {\n pruneCacheEntry(cache, keys[0], keys, this._vnode);\n }\n }\n\n vnode.data.keepAlive = true;\n }\n return vnode || (slot && slot[0])\n }\n};\n\nvar builtInComponents = {\n KeepAlive: KeepAlive\n};\n\n/* */\n\nfunction initGlobalAPI (Vue) {\n // config\n var configDef = {};\n configDef.get = function () { return config; };\n if (process.env.NODE_ENV !== 'production') {\n configDef.set = function () {\n warn(\n 'Do not replace the Vue.config object, set individual fields instead.'\n );\n };\n }\n Object.defineProperty(Vue, 'config', configDef);\n\n // exposed util methods.\n // NOTE: these are not considered part of the public API - avoid relying on\n // them unless you are aware of the risk.\n Vue.util = {\n warn: warn,\n extend: extend,\n mergeOptions: mergeOptions,\n defineReactive: defineReactive$$1\n };\n\n Vue.set = set;\n Vue.delete = del;\n Vue.nextTick = nextTick;\n\n // 2.6 explicit observable API\n Vue.observable = function (obj) {\n observe(obj);\n return obj\n };\n\n Vue.options = Object.create(null);\n ASSET_TYPES.forEach(function (type) {\n Vue.options[type + 's'] = Object.create(null);\n });\n\n // this is used to identify the \"base\" constructor to extend all plain-object\n // components with in Weex's multi-instance scenarios.\n Vue.options._base = Vue;\n\n extend(Vue.options.components, builtInComponents);\n\n initUse(Vue);\n initMixin$1(Vue);\n initExtend(Vue);\n initAssetRegisters(Vue);\n}\n\ninitGlobalAPI(Vue);\n\nObject.defineProperty(Vue.prototype, '$isServer', {\n get: isServerRendering\n});\n\nObject.defineProperty(Vue.prototype, '$ssrContext', {\n get: function get () {\n /* istanbul ignore next */\n return this.$vnode && this.$vnode.ssrContext\n }\n});\n\n// expose FunctionalRenderContext for ssr runtime helper installation\nObject.defineProperty(Vue, 'FunctionalRenderContext', {\n value: FunctionalRenderContext\n});\n\nVue.version = '2.6.12';\n\n/* */\n\n// these are reserved for web because they are directly compiled away\n// during template compilation\nvar isReservedAttr = makeMap('style,class');\n\n// attributes that should be using props for binding\nvar acceptValue = makeMap('input,textarea,option,select,progress');\nvar mustUseProp = function (tag, type, attr) {\n return (\n (attr === 'value' && acceptValue(tag)) && type !== 'button' ||\n (attr === 'selected' && tag === 'option') ||\n (attr === 'checked' && tag === 'input') ||\n (attr === 'muted' && tag === 'video')\n )\n};\n\nvar isEnumeratedAttr = makeMap('contenteditable,draggable,spellcheck');\n\nvar isValidContentEditableValue = makeMap('events,caret,typing,plaintext-only');\n\nvar convertEnumeratedValue = function (key, value) {\n return isFalsyAttrValue(value) || value === 'false'\n ? 'false'\n // allow arbitrary string value for contenteditable\n : key === 'contenteditable' && isValidContentEditableValue(value)\n ? value\n : 'true'\n};\n\nvar isBooleanAttr = makeMap(\n 'allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,' +\n 'default,defaultchecked,defaultmuted,defaultselected,defer,disabled,' +\n 'enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,' +\n 'muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,' +\n 'required,reversed,scoped,seamless,selected,sortable,translate,' +\n 'truespeed,typemustmatch,visible'\n);\n\nvar xlinkNS = 'http://www.w3.org/1999/xlink';\n\nvar isXlink = function (name) {\n return name.charAt(5) === ':' && name.slice(0, 5) === 'xlink'\n};\n\nvar getXlinkProp = function (name) {\n return isXlink(name) ? name.slice(6, name.length) : ''\n};\n\nvar isFalsyAttrValue = function (val) {\n return val == null || val === false\n};\n\n/* */\n\nfunction genClassForVnode (vnode) {\n var data = vnode.data;\n var parentNode = vnode;\n var childNode = vnode;\n while (isDef(childNode.componentInstance)) {\n childNode = childNode.componentInstance._vnode;\n if (childNode && childNode.data) {\n data = mergeClassData(childNode.data, data);\n }\n }\n while (isDef(parentNode = parentNode.parent)) {\n if (parentNode && parentNode.data) {\n data = mergeClassData(data, parentNode.data);\n }\n }\n return renderClass(data.staticClass, data.class)\n}\n\nfunction mergeClassData (child, parent) {\n return {\n staticClass: concat(child.staticClass, parent.staticClass),\n class: isDef(child.class)\n ? [child.class, parent.class]\n : parent.class\n }\n}\n\nfunction renderClass (\n staticClass,\n dynamicClass\n) {\n if (isDef(staticClass) || isDef(dynamicClass)) {\n return concat(staticClass, stringifyClass(dynamicClass))\n }\n /* istanbul ignore next */\n return ''\n}\n\nfunction concat (a, b) {\n return a ? b ? (a + ' ' + b) : a : (b || '')\n}\n\nfunction stringifyClass (value) {\n if (Array.isArray(value)) {\n return stringifyArray(value)\n }\n if (isObject(value)) {\n return stringifyObject(value)\n }\n if (typeof value === 'string') {\n return value\n }\n /* istanbul ignore next */\n return ''\n}\n\nfunction stringifyArray (value) {\n var res = '';\n var stringified;\n for (var i = 0, l = value.length; i < l; i++) {\n if (isDef(stringified = stringifyClass(value[i])) && stringified !== '') {\n if (res) { res += ' '; }\n res += stringified;\n }\n }\n return res\n}\n\nfunction stringifyObject (value) {\n var res = '';\n for (var key in value) {\n if (value[key]) {\n if (res) { res += ' '; }\n res += key;\n }\n }\n return res\n}\n\n/* */\n\nvar namespaceMap = {\n svg: 'http://www.w3.org/2000/svg',\n math: 'http://www.w3.org/1998/Math/MathML'\n};\n\nvar isHTMLTag = makeMap(\n 'html,body,base,head,link,meta,style,title,' +\n 'address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,hgroup,nav,section,' +\n 'div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,' +\n 'a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,rtc,ruby,' +\n 's,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,' +\n 'embed,object,param,source,canvas,script,noscript,del,ins,' +\n 'caption,col,colgroup,table,thead,tbody,td,th,tr,' +\n 'button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,' +\n 'output,progress,select,textarea,' +\n 'details,dialog,menu,menuitem,summary,' +\n 'content,element,shadow,template,blockquote,iframe,tfoot'\n);\n\n// this map is intentionally selective, only covering SVG elements that may\n// contain child elements.\nvar isSVG = makeMap(\n 'svg,animate,circle,clippath,cursor,defs,desc,ellipse,filter,font-face,' +\n 'foreignObject,g,glyph,image,line,marker,mask,missing-glyph,path,pattern,' +\n 'polygon,polyline,rect,switch,symbol,text,textpath,tspan,use,view',\n true\n);\n\nvar isPreTag = function (tag) { return tag === 'pre'; };\n\nvar isReservedTag = function (tag) {\n return isHTMLTag(tag) || isSVG(tag)\n};\n\nfunction getTagNamespace (tag) {\n if (isSVG(tag)) {\n return 'svg'\n }\n // basic support for MathML\n // note it doesn't support other MathML elements being component roots\n if (tag === 'math') {\n return 'math'\n }\n}\n\nvar unknownElementCache = Object.create(null);\nfunction isUnknownElement (tag) {\n /* istanbul ignore if */\n if (!inBrowser) {\n return true\n }\n if (isReservedTag(tag)) {\n return false\n }\n tag = tag.toLowerCase();\n /* istanbul ignore if */\n if (unknownElementCache[tag] != null) {\n return unknownElementCache[tag]\n }\n var el = document.createElement(tag);\n if (tag.indexOf('-') > -1) {\n // http://stackoverflow.com/a/28210364/1070244\n return (unknownElementCache[tag] = (\n el.constructor === window.HTMLUnknownElement ||\n el.constructor === window.HTMLElement\n ))\n } else {\n return (unknownElementCache[tag] = /HTMLUnknownElement/.test(el.toString()))\n }\n}\n\nvar isTextInputType = makeMap('text,number,password,search,email,tel,url');\n\n/* */\n\n/**\n * Query an element selector if it's not an element already.\n */\nfunction query (el) {\n if (typeof el === 'string') {\n var selected = document.querySelector(el);\n if (!selected) {\n process.env.NODE_ENV !== 'production' && warn(\n 'Cannot find element: ' + el\n );\n return document.createElement('div')\n }\n return selected\n } else {\n return el\n }\n}\n\n/* */\n\nfunction createElement$1 (tagName, vnode) {\n var elm = document.createElement(tagName);\n if (tagName !== 'select') {\n return elm\n }\n // false or null will remove the attribute but undefined will not\n if (vnode.data && vnode.data.attrs && vnode.data.attrs.multiple !== undefined) {\n elm.setAttribute('multiple', 'multiple');\n }\n return elm\n}\n\nfunction createElementNS (namespace, tagName) {\n return document.createElementNS(namespaceMap[namespace], tagName)\n}\n\nfunction createTextNode (text) {\n return document.createTextNode(text)\n}\n\nfunction createComment (text) {\n return document.createComment(text)\n}\n\nfunction insertBefore (parentNode, newNode, referenceNode) {\n parentNode.insertBefore(newNode, referenceNode);\n}\n\nfunction removeChild (node, child) {\n node.removeChild(child);\n}\n\nfunction appendChild (node, child) {\n node.appendChild(child);\n}\n\nfunction parentNode (node) {\n return node.parentNode\n}\n\nfunction nextSibling (node) {\n return node.nextSibling\n}\n\nfunction tagName (node) {\n return node.tagName\n}\n\nfunction setTextContent (node, text) {\n node.textContent = text;\n}\n\nfunction setStyleScope (node, scopeId) {\n node.setAttribute(scopeId, '');\n}\n\nvar nodeOps = /*#__PURE__*/Object.freeze({\n createElement: createElement$1,\n createElementNS: createElementNS,\n createTextNode: createTextNode,\n createComment: createComment,\n insertBefore: insertBefore,\n removeChild: removeChild,\n appendChild: appendChild,\n parentNode: parentNode,\n nextSibling: nextSibling,\n tagName: tagName,\n setTextContent: setTextContent,\n setStyleScope: setStyleScope\n});\n\n/* */\n\nvar ref = {\n create: function create (_, vnode) {\n registerRef(vnode);\n },\n update: function update (oldVnode, vnode) {\n if (oldVnode.data.ref !== vnode.data.ref) {\n registerRef(oldVnode, true);\n registerRef(vnode);\n }\n },\n destroy: function destroy (vnode) {\n registerRef(vnode, true);\n }\n};\n\nfunction registerRef (vnode, isRemoval) {\n var key = vnode.data.ref;\n if (!isDef(key)) { return }\n\n var vm = vnode.context;\n var ref = vnode.componentInstance || vnode.elm;\n var refs = vm.$refs;\n if (isRemoval) {\n if (Array.isArray(refs[key])) {\n remove(refs[key], ref);\n } else if (refs[key] === ref) {\n refs[key] = undefined;\n }\n } else {\n if (vnode.data.refInFor) {\n if (!Array.isArray(refs[key])) {\n refs[key] = [ref];\n } else if (refs[key].indexOf(ref) < 0) {\n // $flow-disable-line\n refs[key].push(ref);\n }\n } else {\n refs[key] = ref;\n }\n }\n}\n\n/**\n * Virtual DOM patching algorithm based on Snabbdom by\n * Simon Friis Vindum (@paldepind)\n * Licensed under the MIT License\n * https://github.com/paldepind/snabbdom/blob/master/LICENSE\n *\n * modified by Evan You (@yyx990803)\n *\n * Not type-checking this because this file is perf-critical and the cost\n * of making flow understand it is not worth it.\n */\n\nvar emptyNode = new VNode('', {}, []);\n\nvar hooks = ['create', 'activate', 'update', 'remove', 'destroy'];\n\nfunction sameVnode (a, b) {\n return (\n a.key === b.key && (\n (\n a.tag === b.tag &&\n a.isComment === b.isComment &&\n isDef(a.data) === isDef(b.data) &&\n sameInputType(a, b)\n ) || (\n isTrue(a.isAsyncPlaceholder) &&\n a.asyncFactory === b.asyncFactory &&\n isUndef(b.asyncFactory.error)\n )\n )\n )\n}\n\nfunction sameInputType (a, b) {\n if (a.tag !== 'input') { return true }\n var i;\n var typeA = isDef(i = a.data) && isDef(i = i.attrs) && i.type;\n var typeB = isDef(i = b.data) && isDef(i = i.attrs) && i.type;\n return typeA === typeB || isTextInputType(typeA) && isTextInputType(typeB)\n}\n\nfunction createKeyToOldIdx (children, beginIdx, endIdx) {\n var i, key;\n var map = {};\n for (i = beginIdx; i <= endIdx; ++i) {\n key = children[i].key;\n if (isDef(key)) { map[key] = i; }\n }\n return map\n}\n\nfunction createPatchFunction (backend) {\n var i, j;\n var cbs = {};\n\n var modules = backend.modules;\n var nodeOps = backend.nodeOps;\n\n for (i = 0; i < hooks.length; ++i) {\n cbs[hooks[i]] = [];\n for (j = 0; j < modules.length; ++j) {\n if (isDef(modules[j][hooks[i]])) {\n cbs[hooks[i]].push(modules[j][hooks[i]]);\n }\n }\n }\n\n function emptyNodeAt (elm) {\n return new VNode(nodeOps.tagName(elm).toLowerCase(), {}, [], undefined, elm)\n }\n\n function createRmCb (childElm, listeners) {\n function remove$$1 () {\n if (--remove$$1.listeners === 0) {\n removeNode(childElm);\n }\n }\n remove$$1.listeners = listeners;\n return remove$$1\n }\n\n function removeNode (el) {\n var parent = nodeOps.parentNode(el);\n // element may have already been removed due to v-html / v-text\n if (isDef(parent)) {\n nodeOps.removeChild(parent, el);\n }\n }\n\n function isUnknownElement$$1 (vnode, inVPre) {\n return (\n !inVPre &&\n !vnode.ns &&\n !(\n config.ignoredElements.length &&\n config.ignoredElements.some(function (ignore) {\n return isRegExp(ignore)\n ? ignore.test(vnode.tag)\n : ignore === vnode.tag\n })\n ) &&\n config.isUnknownElement(vnode.tag)\n )\n }\n\n var creatingElmInVPre = 0;\n\n function createElm (\n vnode,\n insertedVnodeQueue,\n parentElm,\n refElm,\n nested,\n ownerArray,\n index\n ) {\n if (isDef(vnode.elm) && isDef(ownerArray)) {\n // This vnode was used in a previous render!\n // now it's used as a new node, overwriting its elm would cause\n // potential patch errors down the road when it's used as an insertion\n // reference node. Instead, we clone the node on-demand before creating\n // associated DOM element for it.\n vnode = ownerArray[index] = cloneVNode(vnode);\n }\n\n vnode.isRootInsert = !nested; // for transition enter check\n if (createComponent(vnode, insertedVnodeQueue, parentElm, refElm)) {\n return\n }\n\n var data = vnode.data;\n var children = vnode.children;\n var tag = vnode.tag;\n if (isDef(tag)) {\n if (process.env.NODE_ENV !== 'production') {\n if (data && data.pre) {\n creatingElmInVPre++;\n }\n if (isUnknownElement$$1(vnode, creatingElmInVPre)) {\n warn(\n 'Unknown custom element: <' + tag + '> - did you ' +\n 'register the component correctly? For recursive components, ' +\n 'make sure to provide the \"name\" option.',\n vnode.context\n );\n }\n }\n\n vnode.elm = vnode.ns\n ? nodeOps.createElementNS(vnode.ns, tag)\n : nodeOps.createElement(tag, vnode);\n setScope(vnode);\n\n /* istanbul ignore if */\n {\n createChildren(vnode, children, insertedVnodeQueue);\n if (isDef(data)) {\n invokeCreateHooks(vnode, insertedVnodeQueue);\n }\n insert(parentElm, vnode.elm, refElm);\n }\n\n if (process.env.NODE_ENV !== 'production' && data && data.pre) {\n creatingElmInVPre--;\n }\n } else if (isTrue(vnode.isComment)) {\n vnode.elm = nodeOps.createComment(vnode.text);\n insert(parentElm, vnode.elm, refElm);\n } else {\n vnode.elm = nodeOps.createTextNode(vnode.text);\n insert(parentElm, vnode.elm, refElm);\n }\n }\n\n function createComponent (vnode, insertedVnodeQueue, parentElm, refElm) {\n var i = vnode.data;\n if (isDef(i)) {\n var isReactivated = isDef(vnode.componentInstance) && i.keepAlive;\n if (isDef(i = i.hook) && isDef(i = i.init)) {\n i(vnode, false /* hydrating */);\n }\n // after calling the init hook, if the vnode is a child component\n // it should've created a child instance and mounted it. the child\n // component also has set the placeholder vnode's elm.\n // in that case we can just return the element and be done.\n if (isDef(vnode.componentInstance)) {\n initComponent(vnode, insertedVnodeQueue);\n insert(parentElm, vnode.elm, refElm);\n if (isTrue(isReactivated)) {\n reactivateComponent(vnode, insertedVnodeQueue, parentElm, refElm);\n }\n return true\n }\n }\n }\n\n function initComponent (vnode, insertedVnodeQueue) {\n if (isDef(vnode.data.pendingInsert)) {\n insertedVnodeQueue.push.apply(insertedVnodeQueue, vnode.data.pendingInsert);\n vnode.data.pendingInsert = null;\n }\n vnode.elm = vnode.componentInstance.$el;\n if (isPatchable(vnode)) {\n invokeCreateHooks(vnode, insertedVnodeQueue);\n setScope(vnode);\n } else {\n // empty component root.\n // skip all element-related modules except for ref (#3455)\n registerRef(vnode);\n // make sure to invoke the insert hook\n insertedVnodeQueue.push(vnode);\n }\n }\n\n function reactivateComponent (vnode, insertedVnodeQueue, parentElm, refElm) {\n var i;\n // hack for #4339: a reactivated component with inner transition\n // does not trigger because the inner node's created hooks are not called\n // again. It's not ideal to involve module-specific logic in here but\n // there doesn't seem to be a better way to do it.\n var innerNode = vnode;\n while (innerNode.componentInstance) {\n innerNode = innerNode.componentInstance._vnode;\n if (isDef(i = innerNode.data) && isDef(i = i.transition)) {\n for (i = 0; i < cbs.activate.length; ++i) {\n cbs.activate[i](emptyNode, innerNode);\n }\n insertedVnodeQueue.push(innerNode);\n break\n }\n }\n // unlike a newly created component,\n // a reactivated keep-alive component doesn't insert itself\n insert(parentElm, vnode.elm, refElm);\n }\n\n function insert (parent, elm, ref$$1) {\n if (isDef(parent)) {\n if (isDef(ref$$1)) {\n if (nodeOps.parentNode(ref$$1) === parent) {\n nodeOps.insertBefore(parent, elm, ref$$1);\n }\n } else {\n nodeOps.appendChild(parent, elm);\n }\n }\n }\n\n function createChildren (vnode, children, insertedVnodeQueue) {\n if (Array.isArray(children)) {\n if (process.env.NODE_ENV !== 'production') {\n checkDuplicateKeys(children);\n }\n for (var i = 0; i < children.length; ++i) {\n createElm(children[i], insertedVnodeQueue, vnode.elm, null, true, children, i);\n }\n } else if (isPrimitive(vnode.text)) {\n nodeOps.appendChild(vnode.elm, nodeOps.createTextNode(String(vnode.text)));\n }\n }\n\n function isPatchable (vnode) {\n while (vnode.componentInstance) {\n vnode = vnode.componentInstance._vnode;\n }\n return isDef(vnode.tag)\n }\n\n function invokeCreateHooks (vnode, insertedVnodeQueue) {\n for (var i$1 = 0; i$1 < cbs.create.length; ++i$1) {\n cbs.create[i$1](emptyNode, vnode);\n }\n i = vnode.data.hook; // Reuse variable\n if (isDef(i)) {\n if (isDef(i.create)) { i.create(emptyNode, vnode); }\n if (isDef(i.insert)) { insertedVnodeQueue.push(vnode); }\n }\n }\n\n // set scope id attribute for scoped CSS.\n // this is implemented as a special case to avoid the overhead\n // of going through the normal attribute patching process.\n function setScope (vnode) {\n var i;\n if (isDef(i = vnode.fnScopeId)) {\n nodeOps.setStyleScope(vnode.elm, i);\n } else {\n var ancestor = vnode;\n while (ancestor) {\n if (isDef(i = ancestor.context) && isDef(i = i.$options._scopeId)) {\n nodeOps.setStyleScope(vnode.elm, i);\n }\n ancestor = ancestor.parent;\n }\n }\n // for slot content they should also get the scopeId from the host instance.\n if (isDef(i = activeInstance) &&\n i !== vnode.context &&\n i !== vnode.fnContext &&\n isDef(i = i.$options._scopeId)\n ) {\n nodeOps.setStyleScope(vnode.elm, i);\n }\n }\n\n function addVnodes (parentElm, refElm, vnodes, startIdx, endIdx, insertedVnodeQueue) {\n for (; startIdx <= endIdx; ++startIdx) {\n createElm(vnodes[startIdx], insertedVnodeQueue, parentElm, refElm, false, vnodes, startIdx);\n }\n }\n\n function invokeDestroyHook (vnode) {\n var i, j;\n var data = vnode.data;\n if (isDef(data)) {\n if (isDef(i = data.hook) && isDef(i = i.destroy)) { i(vnode); }\n for (i = 0; i < cbs.destroy.length; ++i) { cbs.destroy[i](vnode); }\n }\n if (isDef(i = vnode.children)) {\n for (j = 0; j < vnode.children.length; ++j) {\n invokeDestroyHook(vnode.children[j]);\n }\n }\n }\n\n function removeVnodes (vnodes, startIdx, endIdx) {\n for (; startIdx <= endIdx; ++startIdx) {\n var ch = vnodes[startIdx];\n if (isDef(ch)) {\n if (isDef(ch.tag)) {\n removeAndInvokeRemoveHook(ch);\n invokeDestroyHook(ch);\n } else { // Text node\n removeNode(ch.elm);\n }\n }\n }\n }\n\n function removeAndInvokeRemoveHook (vnode, rm) {\n if (isDef(rm) || isDef(vnode.data)) {\n var i;\n var listeners = cbs.remove.length + 1;\n if (isDef(rm)) {\n // we have a recursively passed down rm callback\n // increase the listeners count\n rm.listeners += listeners;\n } else {\n // directly removing\n rm = createRmCb(vnode.elm, listeners);\n }\n // recursively invoke hooks on child component root node\n if (isDef(i = vnode.componentInstance) && isDef(i = i._vnode) && isDef(i.data)) {\n removeAndInvokeRemoveHook(i, rm);\n }\n for (i = 0; i < cbs.remove.length; ++i) {\n cbs.remove[i](vnode, rm);\n }\n if (isDef(i = vnode.data.hook) && isDef(i = i.remove)) {\n i(vnode, rm);\n } else {\n rm();\n }\n } else {\n removeNode(vnode.elm);\n }\n }\n\n function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {\n var oldStartIdx = 0;\n var newStartIdx = 0;\n var oldEndIdx = oldCh.length - 1;\n var oldStartVnode = oldCh[0];\n var oldEndVnode = oldCh[oldEndIdx];\n var newEndIdx = newCh.length - 1;\n var newStartVnode = newCh[0];\n var newEndVnode = newCh[newEndIdx];\n var oldKeyToIdx, idxInOld, vnodeToMove, refElm;\n\n // removeOnly is a special flag used only by \n // to ensure removed elements stay in correct relative positions\n // during leaving transitions\n var canMove = !removeOnly;\n\n if (process.env.NODE_ENV !== 'production') {\n checkDuplicateKeys(newCh);\n }\n\n while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {\n if (isUndef(oldStartVnode)) {\n oldStartVnode = oldCh[++oldStartIdx]; // Vnode has been moved left\n } else if (isUndef(oldEndVnode)) {\n oldEndVnode = oldCh[--oldEndIdx];\n } else if (sameVnode(oldStartVnode, newStartVnode)) {\n patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx);\n oldStartVnode = oldCh[++oldStartIdx];\n newStartVnode = newCh[++newStartIdx];\n } else if (sameVnode(oldEndVnode, newEndVnode)) {\n patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx);\n oldEndVnode = oldCh[--oldEndIdx];\n newEndVnode = newCh[--newEndIdx];\n } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right\n patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx);\n canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm));\n oldStartVnode = oldCh[++oldStartIdx];\n newEndVnode = newCh[--newEndIdx];\n } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left\n patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx);\n canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm);\n oldEndVnode = oldCh[--oldEndIdx];\n newStartVnode = newCh[++newStartIdx];\n } else {\n if (isUndef(oldKeyToIdx)) { oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx); }\n idxInOld = isDef(newStartVnode.key)\n ? oldKeyToIdx[newStartVnode.key]\n : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx);\n if (isUndef(idxInOld)) { // New element\n createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx);\n } else {\n vnodeToMove = oldCh[idxInOld];\n if (sameVnode(vnodeToMove, newStartVnode)) {\n patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx);\n oldCh[idxInOld] = undefined;\n canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm);\n } else {\n // same key but different element. treat as new element\n createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx);\n }\n }\n newStartVnode = newCh[++newStartIdx];\n }\n }\n if (oldStartIdx > oldEndIdx) {\n refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm;\n addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue);\n } else if (newStartIdx > newEndIdx) {\n removeVnodes(oldCh, oldStartIdx, oldEndIdx);\n }\n }\n\n function checkDuplicateKeys (children) {\n var seenKeys = {};\n for (var i = 0; i < children.length; i++) {\n var vnode = children[i];\n var key = vnode.key;\n if (isDef(key)) {\n if (seenKeys[key]) {\n warn(\n (\"Duplicate keys detected: '\" + key + \"'. This may cause an update error.\"),\n vnode.context\n );\n } else {\n seenKeys[key] = true;\n }\n }\n }\n }\n\n function findIdxInOld (node, oldCh, start, end) {\n for (var i = start; i < end; i++) {\n var c = oldCh[i];\n if (isDef(c) && sameVnode(node, c)) { return i }\n }\n }\n\n function patchVnode (\n oldVnode,\n vnode,\n insertedVnodeQueue,\n ownerArray,\n index,\n removeOnly\n ) {\n if (oldVnode === vnode) {\n return\n }\n\n if (isDef(vnode.elm) && isDef(ownerArray)) {\n // clone reused vnode\n vnode = ownerArray[index] = cloneVNode(vnode);\n }\n\n var elm = vnode.elm = oldVnode.elm;\n\n if (isTrue(oldVnode.isAsyncPlaceholder)) {\n if (isDef(vnode.asyncFactory.resolved)) {\n hydrate(oldVnode.elm, vnode, insertedVnodeQueue);\n } else {\n vnode.isAsyncPlaceholder = true;\n }\n return\n }\n\n // reuse element for static trees.\n // note we only do this if the vnode is cloned -\n // if the new node is not cloned it means the render functions have been\n // reset by the hot-reload-api and we need to do a proper re-render.\n if (isTrue(vnode.isStatic) &&\n isTrue(oldVnode.isStatic) &&\n vnode.key === oldVnode.key &&\n (isTrue(vnode.isCloned) || isTrue(vnode.isOnce))\n ) {\n vnode.componentInstance = oldVnode.componentInstance;\n return\n }\n\n var i;\n var data = vnode.data;\n if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) {\n i(oldVnode, vnode);\n }\n\n var oldCh = oldVnode.children;\n var ch = vnode.children;\n if (isDef(data) && isPatchable(vnode)) {\n for (i = 0; i < cbs.update.length; ++i) { cbs.update[i](oldVnode, vnode); }\n if (isDef(i = data.hook) && isDef(i = i.update)) { i(oldVnode, vnode); }\n }\n if (isUndef(vnode.text)) {\n if (isDef(oldCh) && isDef(ch)) {\n if (oldCh !== ch) { updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly); }\n } else if (isDef(ch)) {\n if (process.env.NODE_ENV !== 'production') {\n checkDuplicateKeys(ch);\n }\n if (isDef(oldVnode.text)) { nodeOps.setTextContent(elm, ''); }\n addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue);\n } else if (isDef(oldCh)) {\n removeVnodes(oldCh, 0, oldCh.length - 1);\n } else if (isDef(oldVnode.text)) {\n nodeOps.setTextContent(elm, '');\n }\n } else if (oldVnode.text !== vnode.text) {\n nodeOps.setTextContent(elm, vnode.text);\n }\n if (isDef(data)) {\n if (isDef(i = data.hook) && isDef(i = i.postpatch)) { i(oldVnode, vnode); }\n }\n }\n\n function invokeInsertHook (vnode, queue, initial) {\n // delay insert hooks for component root nodes, invoke them after the\n // element is really inserted\n if (isTrue(initial) && isDef(vnode.parent)) {\n vnode.parent.data.pendingInsert = queue;\n } else {\n for (var i = 0; i < queue.length; ++i) {\n queue[i].data.hook.insert(queue[i]);\n }\n }\n }\n\n var hydrationBailed = false;\n // list of modules that can skip create hook during hydration because they\n // are already rendered on the client or has no need for initialization\n // Note: style is excluded because it relies on initial clone for future\n // deep updates (#7063).\n var isRenderedModule = makeMap('attrs,class,staticClass,staticStyle,key');\n\n // Note: this is a browser-only function so we can assume elms are DOM nodes.\n function hydrate (elm, vnode, insertedVnodeQueue, inVPre) {\n var i;\n var tag = vnode.tag;\n var data = vnode.data;\n var children = vnode.children;\n inVPre = inVPre || (data && data.pre);\n vnode.elm = elm;\n\n if (isTrue(vnode.isComment) && isDef(vnode.asyncFactory)) {\n vnode.isAsyncPlaceholder = true;\n return true\n }\n // assert node match\n if (process.env.NODE_ENV !== 'production') {\n if (!assertNodeMatch(elm, vnode, inVPre)) {\n return false\n }\n }\n if (isDef(data)) {\n if (isDef(i = data.hook) && isDef(i = i.init)) { i(vnode, true /* hydrating */); }\n if (isDef(i = vnode.componentInstance)) {\n // child component. it should have hydrated its own tree.\n initComponent(vnode, insertedVnodeQueue);\n return true\n }\n }\n if (isDef(tag)) {\n if (isDef(children)) {\n // empty element, allow client to pick up and populate children\n if (!elm.hasChildNodes()) {\n createChildren(vnode, children, insertedVnodeQueue);\n } else {\n // v-html and domProps: innerHTML\n if (isDef(i = data) && isDef(i = i.domProps) && isDef(i = i.innerHTML)) {\n if (i !== elm.innerHTML) {\n /* istanbul ignore if */\n if (process.env.NODE_ENV !== 'production' &&\n typeof console !== 'undefined' &&\n !hydrationBailed\n ) {\n hydrationBailed = true;\n console.warn('Parent: ', elm);\n console.warn('server innerHTML: ', i);\n console.warn('client innerHTML: ', elm.innerHTML);\n }\n return false\n }\n } else {\n // iterate and compare children lists\n var childrenMatch = true;\n var childNode = elm.firstChild;\n for (var i$1 = 0; i$1 < children.length; i$1++) {\n if (!childNode || !hydrate(childNode, children[i$1], insertedVnodeQueue, inVPre)) {\n childrenMatch = false;\n break\n }\n childNode = childNode.nextSibling;\n }\n // if childNode is not null, it means the actual childNodes list is\n // longer than the virtual children list.\n if (!childrenMatch || childNode) {\n /* istanbul ignore if */\n if (process.env.NODE_ENV !== 'production' &&\n typeof console !== 'undefined' &&\n !hydrationBailed\n ) {\n hydrationBailed = true;\n console.warn('Parent: ', elm);\n console.warn('Mismatching childNodes vs. VNodes: ', elm.childNodes, children);\n }\n return false\n }\n }\n }\n }\n if (isDef(data)) {\n var fullInvoke = false;\n for (var key in data) {\n if (!isRenderedModule(key)) {\n fullInvoke = true;\n invokeCreateHooks(vnode, insertedVnodeQueue);\n break\n }\n }\n if (!fullInvoke && data['class']) {\n // ensure collecting deps for deep class bindings for future updates\n traverse(data['class']);\n }\n }\n } else if (elm.data !== vnode.text) {\n elm.data = vnode.text;\n }\n return true\n }\n\n function assertNodeMatch (node, vnode, inVPre) {\n if (isDef(vnode.tag)) {\n return vnode.tag.indexOf('vue-component') === 0 || (\n !isUnknownElement$$1(vnode, inVPre) &&\n vnode.tag.toLowerCase() === (node.tagName && node.tagName.toLowerCase())\n )\n } else {\n return node.nodeType === (vnode.isComment ? 8 : 3)\n }\n }\n\n return function patch (oldVnode, vnode, hydrating, removeOnly) {\n if (isUndef(vnode)) {\n if (isDef(oldVnode)) { invokeDestroyHook(oldVnode); }\n return\n }\n\n var isInitialPatch = false;\n var insertedVnodeQueue = [];\n\n if (isUndef(oldVnode)) {\n // empty mount (likely as component), create new root element\n isInitialPatch = true;\n createElm(vnode, insertedVnodeQueue);\n } else {\n var isRealElement = isDef(oldVnode.nodeType);\n if (!isRealElement && sameVnode(oldVnode, vnode)) {\n // patch existing root node\n patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly);\n } else {\n if (isRealElement) {\n // mounting to a real element\n // check if this is server-rendered content and if we can perform\n // a successful hydration.\n if (oldVnode.nodeType === 1 && oldVnode.hasAttribute(SSR_ATTR)) {\n oldVnode.removeAttribute(SSR_ATTR);\n hydrating = true;\n }\n if (isTrue(hydrating)) {\n if (hydrate(oldVnode, vnode, insertedVnodeQueue)) {\n invokeInsertHook(vnode, insertedVnodeQueue, true);\n return oldVnode\n } else if (process.env.NODE_ENV !== 'production') {\n warn(\n 'The client-side rendered virtual DOM tree is not matching ' +\n 'server-rendered content. This is likely caused by incorrect ' +\n 'HTML markup, for example nesting block-level elements inside ' +\n '
, or missing
. Bailing hydration and performing ' +\n 'full client-side render.'\n );\n }\n }\n // either not server-rendered, or hydration failed.\n // create an empty node and replace it\n oldVnode = emptyNodeAt(oldVnode);\n }\n\n // replacing existing element\n var oldElm = oldVnode.elm;\n var parentElm = nodeOps.parentNode(oldElm);\n\n // create new node\n createElm(\n vnode,\n insertedVnodeQueue,\n // extremely rare edge case: do not insert if old element is in a\n // leaving transition. Only happens when combining transition +\n // keep-alive + HOCs. (#4590)\n oldElm._leaveCb ? null : parentElm,\n nodeOps.nextSibling(oldElm)\n );\n\n // update parent placeholder node element, recursively\n if (isDef(vnode.parent)) {\n var ancestor = vnode.parent;\n var patchable = isPatchable(vnode);\n while (ancestor) {\n for (var i = 0; i < cbs.destroy.length; ++i) {\n cbs.destroy[i](ancestor);\n }\n ancestor.elm = vnode.elm;\n if (patchable) {\n for (var i$1 = 0; i$1 < cbs.create.length; ++i$1) {\n cbs.create[i$1](emptyNode, ancestor);\n }\n // #6513\n // invoke insert hooks that may have been merged by create hooks.\n // e.g. for directives that uses the \"inserted\" hook.\n var insert = ancestor.data.hook.insert;\n if (insert.merged) {\n // start at index 1 to avoid re-invoking component mounted hook\n for (var i$2 = 1; i$2 < insert.fns.length; i$2++) {\n insert.fns[i$2]();\n }\n }\n } else {\n registerRef(ancestor);\n }\n ancestor = ancestor.parent;\n }\n }\n\n // destroy old node\n if (isDef(parentElm)) {\n removeVnodes([oldVnode], 0, 0);\n } else if (isDef(oldVnode.tag)) {\n invokeDestroyHook(oldVnode);\n }\n }\n }\n\n invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch);\n return vnode.elm\n }\n}\n\n/* */\n\nvar directives = {\n create: updateDirectives,\n update: updateDirectives,\n destroy: function unbindDirectives (vnode) {\n updateDirectives(vnode, emptyNode);\n }\n};\n\nfunction updateDirectives (oldVnode, vnode) {\n if (oldVnode.data.directives || vnode.data.directives) {\n _update(oldVnode, vnode);\n }\n}\n\nfunction _update (oldVnode, vnode) {\n var isCreate = oldVnode === emptyNode;\n var isDestroy = vnode === emptyNode;\n var oldDirs = normalizeDirectives$1(oldVnode.data.directives, oldVnode.context);\n var newDirs = normalizeDirectives$1(vnode.data.directives, vnode.context);\n\n var dirsWithInsert = [];\n var dirsWithPostpatch = [];\n\n var key, oldDir, dir;\n for (key in newDirs) {\n oldDir = oldDirs[key];\n dir = newDirs[key];\n if (!oldDir) {\n // new directive, bind\n callHook$1(dir, 'bind', vnode, oldVnode);\n if (dir.def && dir.def.inserted) {\n dirsWithInsert.push(dir);\n }\n } else {\n // existing directive, update\n dir.oldValue = oldDir.value;\n dir.oldArg = oldDir.arg;\n callHook$1(dir, 'update', vnode, oldVnode);\n if (dir.def && dir.def.componentUpdated) {\n dirsWithPostpatch.push(dir);\n }\n }\n }\n\n if (dirsWithInsert.length) {\n var callInsert = function () {\n for (var i = 0; i < dirsWithInsert.length; i++) {\n callHook$1(dirsWithInsert[i], 'inserted', vnode, oldVnode);\n }\n };\n if (isCreate) {\n mergeVNodeHook(vnode, 'insert', callInsert);\n } else {\n callInsert();\n }\n }\n\n if (dirsWithPostpatch.length) {\n mergeVNodeHook(vnode, 'postpatch', function () {\n for (var i = 0; i < dirsWithPostpatch.length; i++) {\n callHook$1(dirsWithPostpatch[i], 'componentUpdated', vnode, oldVnode);\n }\n });\n }\n\n if (!isCreate) {\n for (key in oldDirs) {\n if (!newDirs[key]) {\n // no longer present, unbind\n callHook$1(oldDirs[key], 'unbind', oldVnode, oldVnode, isDestroy);\n }\n }\n }\n}\n\nvar emptyModifiers = Object.create(null);\n\nfunction normalizeDirectives$1 (\n dirs,\n vm\n) {\n var res = Object.create(null);\n if (!dirs) {\n // $flow-disable-line\n return res\n }\n var i, dir;\n for (i = 0; i < dirs.length; i++) {\n dir = dirs[i];\n if (!dir.modifiers) {\n // $flow-disable-line\n dir.modifiers = emptyModifiers;\n }\n res[getRawDirName(dir)] = dir;\n dir.def = resolveAsset(vm.$options, 'directives', dir.name, true);\n }\n // $flow-disable-line\n return res\n}\n\nfunction getRawDirName (dir) {\n return dir.rawName || ((dir.name) + \".\" + (Object.keys(dir.modifiers || {}).join('.')))\n}\n\nfunction callHook$1 (dir, hook, vnode, oldVnode, isDestroy) {\n var fn = dir.def && dir.def[hook];\n if (fn) {\n try {\n fn(vnode.elm, dir, vnode, oldVnode, isDestroy);\n } catch (e) {\n handleError(e, vnode.context, (\"directive \" + (dir.name) + \" \" + hook + \" hook\"));\n }\n }\n}\n\nvar baseModules = [\n ref,\n directives\n];\n\n/* */\n\nfunction updateAttrs (oldVnode, vnode) {\n var opts = vnode.componentOptions;\n if (isDef(opts) && opts.Ctor.options.inheritAttrs === false) {\n return\n }\n if (isUndef(oldVnode.data.attrs) && isUndef(vnode.data.attrs)) {\n return\n }\n var key, cur, old;\n var elm = vnode.elm;\n var oldAttrs = oldVnode.data.attrs || {};\n var attrs = vnode.data.attrs || {};\n // clone observed objects, as the user probably wants to mutate it\n if (isDef(attrs.__ob__)) {\n attrs = vnode.data.attrs = extend({}, attrs);\n }\n\n for (key in attrs) {\n cur = attrs[key];\n old = oldAttrs[key];\n if (old !== cur) {\n setAttr(elm, key, cur);\n }\n }\n // #4391: in IE9, setting type can reset value for input[type=radio]\n // #6666: IE/Edge forces progress value down to 1 before setting a max\n /* istanbul ignore if */\n if ((isIE || isEdge) && attrs.value !== oldAttrs.value) {\n setAttr(elm, 'value', attrs.value);\n }\n for (key in oldAttrs) {\n if (isUndef(attrs[key])) {\n if (isXlink(key)) {\n elm.removeAttributeNS(xlinkNS, getXlinkProp(key));\n } else if (!isEnumeratedAttr(key)) {\n elm.removeAttribute(key);\n }\n }\n }\n}\n\nfunction setAttr (el, key, value) {\n if (el.tagName.indexOf('-') > -1) {\n baseSetAttr(el, key, value);\n } else if (isBooleanAttr(key)) {\n // set attribute for blank value\n // e.g. \n if (isFalsyAttrValue(value)) {\n el.removeAttribute(key);\n } else {\n // technically allowfullscreen is a boolean attribute for