Merge branch 'master' into placeholder

# Conflicts:
#	icons/obj/furniture.dmi
This commit is contained in:
Ferner
2020-12-24 17:56:02 +01:00
357 changed files with 107089 additions and 94669 deletions
+7
View File
@@ -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"
+4
View File
@@ -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.
+1
View File
@@ -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
+2
View File
@@ -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"
+1 -3
View File
@@ -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
+5
View File
@@ -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)
+9
View File
@@ -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"
+275 -105
View File
@@ -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 << "<b>TGS Notification</b>: [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
+5
View File
@@ -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
+6 -1
View File
@@ -546,6 +546,11 @@
t = replacetext(t, "</I>", "\[/i\]")
t = replacetext(t, "<U>", "\[u\]")
t = replacetext(t, "</U>", "\[/u\]")
t = replacetext(t, "<BR>", "\[br\]")
t = replacetext(t, "<HR>", "\[hr\]")
t = replacetext(t, "<ul>", "\[list\]")
t = replacetext(t, "</ul>", "\[/list\]")
t = replacetext(t, "<li>", "\[*\]")
t = replacetext(t, "<font size=\"4\">", "\[large\]")
t = replacetext(t, "</font>", "\[/large\]")
t = replacetext(t, "<font size = \"1\">", "\[small\]")
@@ -632,4 +637,4 @@
var/ending = copytext(string, length(string), (length(string) + 1))
if(ending && !correct_punctuation[ending])
string += "."
return string
return string
+1
View File
@@ -115,6 +115,7 @@
/obj/screen/fullscreen/flash/noise
icon_state = "noise"
alpha = 127
/obj/screen/fullscreen/noise
icon = 'icons/effects/static.dmi'
+9 -1
View File
@@ -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'
+1 -1
View File
@@ -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()
+1 -1
View File
@@ -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
@@ -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"
+10 -10
View File
@@ -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, "<span class='notice'>You are legcuffed! You cannot run until you get [C.legcuffed] removed!</span>")
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)
+21 -5
View File
@@ -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, "<span class='warning'>You don't want to harm other living beings!</span>")
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
+2 -1
View File
@@ -25,7 +25,8 @@
runes_by_name[R.name] = rune
tome_data += "<div class='rune-block'>"
tome_data += "<b>[capitalize_first_letters(R.name)]</b>: <i>[R.desc]</i><br>"
tome_data += "This rune <b><i>[R.can_be_talisman() ? "can" : "cannot"]</i></b> be turned into a talisman.<br><hr>"
tome_data += "This rune <b><i>[R.can_be_talisman() ? "can" : "cannot"]</i></b> be turned into a talisman.<br>"
tome_data += "This rune <b><i>[R.can_memorize() ? "can" : "cannot"]</i></b> be memorized to be scribed without a tome.<br><hr>"
tome_data += "</div>"
/datum/controller/subsystem/cult/proc/add_rune(var/datum/rune/R)
+1 -1
View File
@@ -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()
@@ -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
@@ -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)
+1 -1
View File
@@ -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, "<span class='warning'>Your leg gives out!</span>")
+1 -1
View File
@@ -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
+5 -5
View File
@@ -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()
+1 -1
View File
@@ -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"
+7 -1
View File
@@ -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
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
-12
View File
@@ -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."
+1
View File
@@ -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)
+1 -1
View File
@@ -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"
+2 -1
View File
@@ -26,7 +26,8 @@
faction = "Changeling"
/datum/antagonist/changeling/get_special_objective_text(var/datum/mind/player)
return "<br><b>Changeling ID:</b> [player.changeling.changelingID].<br><b>Genomes Absorbed:</b> [player.changeling.absorbedcount]"
var/datum/changeling/changeling = player.antag_datums[MODE_CHANGELING]
return "<br><b>Changeling ID:</b> [changeling.changelingID].<br><b>Genomes Absorbed:</b> [changeling.absorbedcount]"
/datum/antagonist/changeling/update_antag_mob(var/datum/mind/player)
..()
+9 -1
View File
@@ -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))
+13
View File
@@ -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 ..()
+20 -10
View File
@@ -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"
+2 -2
View File
@@ -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
+1 -1
View File
@@ -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."
@@ -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
@@ -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
@@ -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; \
} \
} \
}
@@ -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."))
@@ -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("<span class='danger'>[M] writhes and contorts, their body expanding to inhuman proportions!</span>", \
"<span class='danger'>We begin our transformation to our true form!</span>")
@@ -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
@@ -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
@@ -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 <b>[mind.changeling.prepared_sting.name]</b>."))
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 <b>[changeling.prepared_sting.name]</b>."))
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 <b>[mind.changeling.prepared_sting.name]</b>."))
to_chat(src, SPAN_NOTICE("You prepare to fire the <b>[changeling.prepared_sting.name]</b>."))
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
return TRUE
@@ -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
+112
View File
@@ -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("<b>[H]</b> 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)
+1 -17
View File
@@ -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."))
+5 -1
View File
@@ -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
@@ -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
+1 -1
View File
@@ -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
@@ -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
@@ -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)
+1 -1
View File
@@ -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))
@@ -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
+1 -1
View File
@@ -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
@@ -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."))
+1
View File
@@ -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() ? "<span class='cult'><b><i>can</i></b></span>" : "<span class='warning'><b><i>cannot</i></b></span>"] be turned into a talisman.")
to_chat(user, "This rune [rune.can_memorize() ? "<span class='cult'><b><i>can</i></b></span>" : "<span class='warning'><b><i>cannot</i></b></span>"] be memorized to be scribed without a tome.")
else
to_chat(user, rune.get_normal_fluff_text())
+1 -1
View File
@@ -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()
@@ -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)
@@ -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"
-14
View File
@@ -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.
+1 -1
View File
@@ -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
+4 -2
View File
@@ -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
@@ -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
+90 -65
View File
@@ -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, "<span class='warning'>Your connection with the Veil is not strong enough to affect a man as devout as them.</span>")
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, "<span class='warning'>You lack the power required to affect another creature of the Veil.</span>")
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, "<span class='warning'>You lack the power required to affect another creature of the Veil.</span>")
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, "<span class='warning'>\The [T]'s mind is too strong to be affected by our powers!</span>")
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
+34 -32
View File
@@ -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.<br><br>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.<br><br>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))
ADD_VERB_IN_IF(src, 800, /mob/living/carbon/human/proc/grapple, CALLBACK(src, .proc/finish_vamp_timeout, VAMP_FRENZIED))
+3 -3
View File
@@ -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
+31 -35
View File
@@ -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 <b>closed</b> by [usr] ([usr.ckey]), stopping the transfer into the [holding]<br>"
release_log += "Valve was <b>closed</b> by [usr] ([usr.ckey]), stopping the transfer into [holding]<br>"
else
release_log += "Valve was <b>closed</b> by [usr] ([usr.ckey]), stopping the transfer into the <span class='warning'><b>air</b></span><br>"
else
if (holding)
release_log += "Valve was <b>opened</b> by [usr] ([usr.ckey]), starting the transfer into the [holding]<br>"
release_log += "Valve was <b>opened</b> by [usr] ([usr.ckey]), starting the transfer into [holding]<br>"
else
release_log += "Valve was <b>opened</b> by [usr] ([usr.ckey]), starting the transfer into the <span class='warning'><b>air</b></span><br>"
log_open()
@@ -401,18 +401,14 @@ update_flag
if(holding)
if (valve_open)
valve_open = 0
release_log += "Valve was <b>closed</b> by [usr] ([usr.ckey]), stopping the transfer into the [holding]<br>"
release_log += "Valve was <b>closed</b> by [usr] ([usr.ckey]), stopping the transfer into [holding]<br>"
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)
+1 -1
View File
@@ -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("<span class='notice'>The printer injects the stored DNA into the biomass.</span>.")
O.transplant_data = list()
+2 -2
View File
@@ -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)
+3
View File
@@ -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'
+1 -2
View File
@@ -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()
+1 -1
View File
@@ -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("<span class='danger'>The barricade is smashed apart!</span>")
dismantle()
+2 -2
View File
@@ -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("<span class='danger'>\The [user] hits \the [src] with \the [W] with no visible effect.</span>")
else
user.visible_message("<span class='danger'>\The [user] forcefully strikes \the [src] with \the [W]!</span>")
playsound(src.loc, hitsound, 100, 1)
playsound(src.loc, hitsound, W.get_clamped_volume(), 1)
take_damage(W.force)
return
+3 -7
View File
@@ -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)
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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)
+29 -16
View File
@@ -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)
+1 -1
View File
@@ -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,
+60 -7
View File
@@ -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("<span class='warning'>[user] smashes through the foamed metal.</span>", "<span class='notice'>You smash through the metal foam wall.</span>")
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, "<span class='notice'>You hit the metal foam but bounce off it.</span>")
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("<span class='warning'>[G.assailant] smashes [G.affecting] through the foamed metal wall.</span>")
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("<b>[user]</b> 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("<span class='warning'>[user] smashes through the foamed metal.</span>", "<span class='notice'>You smash through the foamed metal with \the [I].</span>")
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, "<span class='notice'>You hit the metal foam to no effect.</span>")
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)
@@ -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
amount *= 0.75
@@ -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()
@@ -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")
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)
+9
View File
@@ -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, "<span class='notice'>Your [O.name] itches.</span>")
/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."
+1 -1
View File
@@ -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
+5 -7
View File
@@ -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("<span class='disarm'>[M] is blinded by the flash!</span>")
M.flash_eyes()
to_chat(M, SPAN_WARNING("Your [name] goes off!"))
..()
/obj/item/device/flash/synthetic
@@ -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)
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)
@@ -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"
@@ -206,6 +206,18 @@ BREATH ANALYZER
limb_result = "[limb_result] \[<font color = '#ffa500'><b>[get_severity(org.burn_dam, TRUE)] burns</b></font>\]"
if(org.status & ORGAN_BLEEDING)
limb_result = "[limb_result] \[<span class='scan_danger'>bleeding</span>\]"
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."
@@ -157,6 +157,7 @@ Contains:
else
to_chat(user, "<span class='warning'>\The [src] is used up, but there are more wounds to treat on \the [affecting.name].</span>")
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, "<span class='warning'>\The [src] is used up, but there are more wounds to treat on \the [affecting.name].</span>")
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))
+22 -13
View File
@@ -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!"))
+8 -4
View File
@@ -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 <b>[stored_matter]/30</b> fabrication-units."))
+1 -1
View File
@@ -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
+127 -85
View File
@@ -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, "<span class='notice'>[igniter] is now [status ? "secured" : "unsecured"]!</span>")
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, "<span class='notice'>There appears to already be a phoron tank loaded in [src]!</span>")
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, "<span class='notice'>Attach a phoron tank first!</span>")
to_chat(user, SPAN_WARNING("Attach a phoron tank first!"))
return
var/dat = text("<TT><B>Flamethrower (<A HREF='?src=\ref[src];light=1'>[lit ? "<span class='warning'>Lit</span>" : "Unlit"]</a>)</B><BR>\n Tank Pressure: [ptank.air_contents.return_pressure()]<BR>\nAmount to throw: <A HREF='?src=\ref[src];amount=-100'>-</A> <A HREF='?src=\ref[src];amount=-10'>-</A> <A HREF='?src=\ref[src];amount=-1'>-</A> [throw_amount] <A HREF='?src=\ref[src];amount=1'>+</A> <A HREF='?src=\ref[src];amount=10'>+</A> <A HREF='?src=\ref[src];amount=100'>+</A><BR>\n<A HREF='?src=\ref[src];remove=1'>Remove phorontank</A> - <A HREF='?src=\ref[src];close=1'>Close</A></TT>")
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()
@@ -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: <i>The [station_name()]</i><BR>From: <i>[gift_benefactor], with [pick_emotion]</i>"
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
@@ -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
+47 -18
View File
@@ -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"
@@ -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("<b>[user]</b> 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("<b>[user]</b> 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(
"<b>[user]</b> 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("<span class='danger'>\The [src] slams into [hit_atom], spilling its contents!</span>")
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
@@ -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
+48 -21
View File
@@ -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, "<span class='warning'>You are not supposed to go past [src]...</span>")
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("<b>[user]</b> [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)
@@ -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."

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