Merge pull request #825 from ArchieBeepBoop/updatesaycode

Saycode Update Package, durathread golems, loom updates, new traumas, science powergaming inc
This commit is contained in:
QuoteFox
2021-01-06 14:18:03 +00:00
committed by GitHub
99 changed files with 18027 additions and 17546 deletions
+205 -204
View File
@@ -1,204 +1,205 @@
/*ALL DEFINES RELATED TO COMBAT GO HERE*/
//Damage and status effect defines
//Damage defines //TODO: merge these down to reduce on defines
#define BRUTE "brute"
#define BURN "fire"
#define TOX "tox"
#define OXY "oxy"
#define CLONE "clone"
#define STAMINA "stamina"
#define BRAIN "brain"
//bitflag damage defines used for suicide_act
#define BRUTELOSS (1<<0)
#define FIRELOSS (1<<1)
#define TOXLOSS (1<<2)
#define OXYLOSS (1<<3)
#define SHAME (1<<4)
#define MANUAL_SUICIDE (1<<5) //suicide_act will do the actual killing.
#define EFFECT_STUN "stun"
#define EFFECT_KNOCKDOWN "knockdown"
#define EFFECT_UNCONSCIOUS "unconscious"
#define EFFECT_IRRADIATE "irradiate"
#define EFFECT_STUTTER "stutter"
#define EFFECT_SLUR "slur"
#define EFFECT_EYE_BLUR "eye_blur"
#define EFFECT_DROWSY "drowsy"
#define EFFECT_JITTER "jitter"
//Bitflags defining which status effects could be or are inflicted on a mob
#define CANSTUN (1<<0)
#define CANKNOCKDOWN (1<<1)
#define CANUNCONSCIOUS (1<<2)
#define CANPUSH (1<<3)
#define GODMODE (1<<4)
//Health Defines
#define HEALTH_THRESHOLD_CRIT 0
#define HEALTH_THRESHOLD_FULLCRIT -30
#define HEALTH_THRESHOLD_DEAD -100
//Actual combat defines
//click cooldowns, in tenths of a second, used for various combat actions
#define CLICK_CD_MELEE 8
#define CLICK_CD_RANGE 4
#define CLICK_CD_RAPID 2
#define CLICK_CD_CLICK_ABILITY 6
#define CLICK_CD_BREAKOUT 100
#define CLICK_CD_HANDCUFFED 10
#define CLICK_CD_RESIST 20
#define CLICK_CD_GRABBING 10
//Cuff resist speeds
#define FAST_CUFFBREAK 1
#define INSTANT_CUFFBREAK 2
//Grab levels
#define GRAB_PASSIVE 0
#define GRAB_AGGRESSIVE 1
#define GRAB_NECK 2
#define GRAB_KILL 3
//slowdown when in softcrit
#define SOFTCRIT_ADD_SLOWDOWN 6
//Attack types for checking shields/hit reactions
#define MELEE_ATTACK 1
#define UNARMED_ATTACK 2
#define PROJECTILE_ATTACK 3
#define THROWN_PROJECTILE_ATTACK 4
#define LEAP_ATTACK 5
//attack visual effects
#define ATTACK_EFFECT_PUNCH "punch"
#define ATTACK_EFFECT_KICK "kick"
#define ATTACK_EFFECT_SMASH "smash"
#define ATTACK_EFFECT_CLAW "claw"
#define ATTACK_EFFECT_DISARM "disarm"
#define ATTACK_EFFECT_ASS_SLAP "ass_slap"
#define ATTACK_EFFECT_FACE_SLAP "face_slap"
#define ATTACK_EFFECT_BITE "bite"
#define ATTACK_EFFECT_MECHFIRE "mech_fire"
#define ATTACK_EFFECT_MECHTOXIN "mech_toxin"
#define ATTACK_EFFECT_BOOP "boop" //Honk
//intent defines
#define INTENT_HELP "help"
#define INTENT_GRAB "grab"
#define INTENT_DISARM "disarm"
#define INTENT_HARM "harm"
//NOTE: INTENT_HOTKEY_* defines are not actual intents!
//they are here to support hotkeys
#define INTENT_HOTKEY_LEFT "left"
#define INTENT_HOTKEY_RIGHT "right"
//the define for visible message range in combat
#define COMBAT_MESSAGE_RANGE 3
//Shove knockdown lengths (deciseconds)
#define SHOVE_KNOCKDOWN_SOLID 30
#define SHOVE_KNOCKDOWN_HUMAN 30
#define SHOVE_KNOCKDOWN_TABLE 30
#define SHOVE_KNOCKDOWN_COLLATERAL 10
//Shove slowdown
#define SHOVE_SLOWDOWN_ID "shove_slowdown"
#define SHOVE_SLOWDOWN_LENGTH 30
#define SHOVE_SLOWDOWN_STRENGTH 0.85 //multiplier
//Shove disarming item list
GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(
/obj/item/gun)))
//Combat object defines
//Embedded objects
#define EMBEDDED_PAIN_CHANCE 15 //Chance for embedded objects to cause pain (damage user)
#define EMBEDDED_ITEM_FALLOUT 5 //Chance for embedded object to fall out (causing pain but removing the object)
#define EMBED_CHANCE 45 //Chance for an object to embed into somebody when thrown (if it's sharp)
#define EMBEDDED_PAIN_MULTIPLIER 2 //Coefficient of multiplication for the damage the item does while embedded (this*item.w_class)
#define EMBEDDED_FALL_PAIN_MULTIPLIER 5 //Coefficient of multiplication for the damage the item does when it falls out (this*item.w_class)
#define EMBEDDED_IMPACT_PAIN_MULTIPLIER 4 //Coefficient of multiplication for the damage the item does when it first embeds (this*item.w_class)
#define EMBED_THROWSPEED_THRESHOLD 4 //The minimum value of an item's throw_speed for it to embed (Unless it has embedded_ignore_throwspeed_threshold set to 1)
#define EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER 8 //Coefficient of multiplication for the damage the item does when removed without a surgery (this*item.w_class)
#define EMBEDDED_UNSAFE_REMOVAL_TIME 150 //A Time in ticks, total removal time = (this/item.w_class)
//Gun weapon weight
#define WEAPON_LIGHT 1
#define WEAPON_MEDIUM 2
#define WEAPON_HEAVY 3
//Gun trigger guards
#define TRIGGER_GUARD_ALLOW_ALL -1
#define TRIGGER_GUARD_NONE 0
#define TRIGGER_GUARD_NORMAL 1
//E-gun self-recharge values
#define EGUN_NO_SELFCHARGE 0
#define EGUN_SELFCHARGE 1
#define EGUN_SELFCHARGE_BORG 2
//Object/Item sharpness
#define IS_BLUNT 0
#define IS_SHARP 1
#define IS_SHARP_ACCURATE 2
//His Grace.
#define HIS_GRACE_SATIATED 0 //He hungers not. If bloodthirst is set to this, His Grace is asleep.
#define HIS_GRACE_PECKISH 20 //Slightly hungry.
#define HIS_GRACE_HUNGRY 60 //Getting closer. Increases damage up to a minimum of 20.
#define HIS_GRACE_FAMISHED 100 //Dangerous. Increases damage up to a minimum of 25 and cannot be dropped.
#define HIS_GRACE_STARVING 120 //Incredibly close to breaking loose. Increases damage up to a minimum of 30.
#define HIS_GRACE_CONSUME_OWNER 140 //His Grace consumes His owner at this point and becomes aggressive.
#define HIS_GRACE_FALL_ASLEEP 160 //If it reaches this point, He falls asleep and resets.
#define HIS_GRACE_FORCE_BONUS 4 //How much force is gained per kill.
#define EXPLODE_NONE 0 //Don't even ask me why we need this.
#define EXPLODE_DEVASTATE 1
#define EXPLODE_HEAVY 2
#define EXPLODE_LIGHT 3
#define EMP_HEAVY 1
#define EMP_LIGHT 2
#define GRENADE_CLUMSY_FUMBLE 1
#define GRENADE_NONCLUMSY_FUMBLE 2
#define GRENADE_NO_FUMBLE 3
#define BODY_ZONE_HEAD "head"
#define BODY_ZONE_CHEST "chest"
#define BODY_ZONE_L_ARM "l_arm"
#define BODY_ZONE_R_ARM "r_arm"
#define BODY_ZONE_L_LEG "l_leg"
#define BODY_ZONE_R_LEG "r_leg"
#define BODY_ZONE_PRECISE_EYES "eyes"
#define BODY_ZONE_PRECISE_MOUTH "mouth"
#define BODY_ZONE_PRECISE_GROIN "groin"
#define BODY_ZONE_PRECISE_L_HAND "l_hand"
#define BODY_ZONE_PRECISE_R_HAND "r_hand"
#define BODY_ZONE_PRECISE_L_FOOT "l_foot"
#define BODY_ZONE_PRECISE_R_FOOT "r_foot"
//We will round to this value in damage calculations.
#define DAMAGE_PRECISION 0.1
//bullet_act() return values
#define BULLET_ACT_HIT "HIT" //It's a successful hit, whatever that means in the context of the thing it's hitting.
#define BULLET_ACT_BLOCK "BLOCK" //It's a blocked hit, whatever that means in the context of the thing it's hitting.
#define BULLET_ACT_FORCE_PIERCE "PIERCE" //It pierces through the object regardless of the bullet being piercing by default.
#define BULLET_ACT_TURF "TURF" //It hit us but it should hit something on the same turf too. Usually used for turfs.
//items total mass, used to calculate their attacks' stamina costs. If not defined, the cost will be (w_class * 1.25)
#define TOTAL_MASS_TINY_ITEM 1.25
#define TOTAL_MASS_SMALL_ITEM 2.5
#define TOTAL_MASS_NORMAL_ITEM 3.75
#define TOTAL_MASS_BULKY_ITEM 5
#define TOTAL_MASS_HUGE_ITEM 6.25
#define TOTAL_MASS_GIGANTIC_ITEM 7.5
#define TOTAL_MASS_HAND_REPLACEMENT 5 //standard punching stamina cost. most hand replacements are huge items anyway.
#define TOTAL_MASS_MEDIEVAL_WEAPON 3.6 //very, very generic average sword/warpick/etc. weight in pounds.
#define TOTAL_MASS_TOY_SWORD 1.5
/*ALL DEFINES RELATED TO COMBAT GO HERE*/
//Damage and status effect defines
//Damage defines //TODO: merge these down to reduce on defines
#define BRUTE "brute"
#define BURN "fire"
#define TOX "tox"
#define OXY "oxy"
#define CLONE "clone"
#define STAMINA "stamina"
#define BRAIN "brain"
//bitflag damage defines used for suicide_act
#define BRUTELOSS (1<<0)
#define FIRELOSS (1<<1)
#define TOXLOSS (1<<2)
#define OXYLOSS (1<<3)
#define SHAME (1<<4)
#define MANUAL_SUICIDE (1<<5) //suicide_act will do the actual killing.
#define EFFECT_STUN "stun"
#define EFFECT_KNOCKDOWN "knockdown"
#define EFFECT_UNCONSCIOUS "unconscious"
#define EFFECT_IRRADIATE "irradiate"
#define EFFECT_STUTTER "stutter"
#define EFFECT_SLUR "slur"
#define EFFECT_EYE_BLUR "eye_blur"
#define EFFECT_DROWSY "drowsy"
#define EFFECT_JITTER "jitter"
//Bitflags defining which status effects could be or are inflicted on a mob
#define CANSTUN (1<<0)
#define CANKNOCKDOWN (1<<1)
#define CANUNCONSCIOUS (1<<2)
#define CANPUSH (1<<3)
#define GODMODE (1<<4)
//Health Defines
#define HEALTH_THRESHOLD_CRIT 0
#define HEALTH_THRESHOLD_FULLCRIT -30
#define HEALTH_THRESHOLD_DEAD -100
//Actual combat defines
//click cooldowns, in tenths of a second, used for various combat actions
#define CLICK_CD_MELEE 8
#define CLICK_CD_RANGE 4
#define CLICK_CD_RAPID 2
#define CLICK_CD_CLICK_ABILITY 6
#define CLICK_CD_BREAKOUT 100
#define CLICK_CD_HANDCUFFED 10
#define CLICK_CD_RESIST 20
#define CLICK_CD_GRABBING 10
//Cuff resist speeds
#define FAST_CUFFBREAK 1
#define INSTANT_CUFFBREAK 2
//Grab levels
#define GRAB_PASSIVE 0
#define GRAB_AGGRESSIVE 1
#define GRAB_NECK 2
#define GRAB_KILL 3
//slowdown when in softcrit
#define SOFTCRIT_ADD_SLOWDOWN 6
//Attack types for checking shields/hit reactions
#define MELEE_ATTACK 1
#define UNARMED_ATTACK 2
#define PROJECTILE_ATTACK 3
#define THROWN_PROJECTILE_ATTACK 4
#define LEAP_ATTACK 5
//attack visual effects
#define ATTACK_EFFECT_PUNCH "punch"
#define ATTACK_EFFECT_KICK "kick"
#define ATTACK_EFFECT_SMASH "smash"
#define ATTACK_EFFECT_CLAW "claw"
#define ATTACK_EFFECT_DISARM "disarm"
#define ATTACK_EFFECT_ASS_SLAP "ass_slap"
#define ATTACK_EFFECT_FACE_SLAP "face_slap"
#define ATTACK_EFFECT_BITE "bite"
#define ATTACK_EFFECT_MECHFIRE "mech_fire"
#define ATTACK_EFFECT_MECHTOXIN "mech_toxin"
#define ATTACK_EFFECT_BOOP "boop" //Honk
//intent defines
#define INTENT_HELP "help"
#define INTENT_GRAB "grab"
#define INTENT_DISARM "disarm"
#define INTENT_HARM "harm"
//NOTE: INTENT_HOTKEY_* defines are not actual intents!
//they are here to support hotkeys
#define INTENT_HOTKEY_LEFT "left"
#define INTENT_HOTKEY_RIGHT "right"
//the define for visible message range in combat
#define COMBAT_MESSAGE_RANGE 3
#define DEFAULT_MESSAGE_RANGE 7
//Shove knockdown lengths (deciseconds)
#define SHOVE_KNOCKDOWN_SOLID 30
#define SHOVE_KNOCKDOWN_HUMAN 30
#define SHOVE_KNOCKDOWN_TABLE 30
#define SHOVE_KNOCKDOWN_COLLATERAL 10
//Shove slowdown
#define SHOVE_SLOWDOWN_ID "shove_slowdown"
#define SHOVE_SLOWDOWN_LENGTH 30
#define SHOVE_SLOWDOWN_STRENGTH 0.85 //multiplier
//Shove disarming item list
GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list(
/obj/item/gun)))
//Combat object defines
//Embedded objects
#define EMBEDDED_PAIN_CHANCE 15 //Chance for embedded objects to cause pain (damage user)
#define EMBEDDED_ITEM_FALLOUT 5 //Chance for embedded object to fall out (causing pain but removing the object)
#define EMBED_CHANCE 45 //Chance for an object to embed into somebody when thrown (if it's sharp)
#define EMBEDDED_PAIN_MULTIPLIER 2 //Coefficient of multiplication for the damage the item does while embedded (this*item.w_class)
#define EMBEDDED_FALL_PAIN_MULTIPLIER 5 //Coefficient of multiplication for the damage the item does when it falls out (this*item.w_class)
#define EMBEDDED_IMPACT_PAIN_MULTIPLIER 4 //Coefficient of multiplication for the damage the item does when it first embeds (this*item.w_class)
#define EMBED_THROWSPEED_THRESHOLD 4 //The minimum value of an item's throw_speed for it to embed (Unless it has embedded_ignore_throwspeed_threshold set to 1)
#define EMBEDDED_UNSAFE_REMOVAL_PAIN_MULTIPLIER 8 //Coefficient of multiplication for the damage the item does when removed without a surgery (this*item.w_class)
#define EMBEDDED_UNSAFE_REMOVAL_TIME 150 //A Time in ticks, total removal time = (this/item.w_class)
//Gun weapon weight
#define WEAPON_LIGHT 1
#define WEAPON_MEDIUM 2
#define WEAPON_HEAVY 3
//Gun trigger guards
#define TRIGGER_GUARD_ALLOW_ALL -1
#define TRIGGER_GUARD_NONE 0
#define TRIGGER_GUARD_NORMAL 1
//E-gun self-recharge values
#define EGUN_NO_SELFCHARGE 0
#define EGUN_SELFCHARGE 1
#define EGUN_SELFCHARGE_BORG 2
//Object/Item sharpness
#define IS_BLUNT 0
#define IS_SHARP 1
#define IS_SHARP_ACCURATE 2
//His Grace.
#define HIS_GRACE_SATIATED 0 //He hungers not. If bloodthirst is set to this, His Grace is asleep.
#define HIS_GRACE_PECKISH 20 //Slightly hungry.
#define HIS_GRACE_HUNGRY 60 //Getting closer. Increases damage up to a minimum of 20.
#define HIS_GRACE_FAMISHED 100 //Dangerous. Increases damage up to a minimum of 25 and cannot be dropped.
#define HIS_GRACE_STARVING 120 //Incredibly close to breaking loose. Increases damage up to a minimum of 30.
#define HIS_GRACE_CONSUME_OWNER 140 //His Grace consumes His owner at this point and becomes aggressive.
#define HIS_GRACE_FALL_ASLEEP 160 //If it reaches this point, He falls asleep and resets.
#define HIS_GRACE_FORCE_BONUS 4 //How much force is gained per kill.
#define EXPLODE_NONE 0 //Don't even ask me why we need this.
#define EXPLODE_DEVASTATE 1
#define EXPLODE_HEAVY 2
#define EXPLODE_LIGHT 3
#define EMP_HEAVY 1
#define EMP_LIGHT 2
#define GRENADE_CLUMSY_FUMBLE 1
#define GRENADE_NONCLUMSY_FUMBLE 2
#define GRENADE_NO_FUMBLE 3
#define BODY_ZONE_HEAD "head"
#define BODY_ZONE_CHEST "chest"
#define BODY_ZONE_L_ARM "l_arm"
#define BODY_ZONE_R_ARM "r_arm"
#define BODY_ZONE_L_LEG "l_leg"
#define BODY_ZONE_R_LEG "r_leg"
#define BODY_ZONE_PRECISE_EYES "eyes"
#define BODY_ZONE_PRECISE_MOUTH "mouth"
#define BODY_ZONE_PRECISE_GROIN "groin"
#define BODY_ZONE_PRECISE_L_HAND "l_hand"
#define BODY_ZONE_PRECISE_R_HAND "r_hand"
#define BODY_ZONE_PRECISE_L_FOOT "l_foot"
#define BODY_ZONE_PRECISE_R_FOOT "r_foot"
//We will round to this value in damage calculations.
#define DAMAGE_PRECISION 0.1
//bullet_act() return values
#define BULLET_ACT_HIT "HIT" //It's a successful hit, whatever that means in the context of the thing it's hitting.
#define BULLET_ACT_BLOCK "BLOCK" //It's a blocked hit, whatever that means in the context of the thing it's hitting.
#define BULLET_ACT_FORCE_PIERCE "PIERCE" //It pierces through the object regardless of the bullet being piercing by default.
#define BULLET_ACT_TURF "TURF" //It hit us but it should hit something on the same turf too. Usually used for turfs.
//items total mass, used to calculate their attacks' stamina costs. If not defined, the cost will be (w_class * 1.25)
#define TOTAL_MASS_TINY_ITEM 1.25
#define TOTAL_MASS_SMALL_ITEM 2.5
#define TOTAL_MASS_NORMAL_ITEM 3.75
#define TOTAL_MASS_BULKY_ITEM 5
#define TOTAL_MASS_HUGE_ITEM 6.25
#define TOTAL_MASS_GIGANTIC_ITEM 7.5
#define TOTAL_MASS_HAND_REPLACEMENT 5 //standard punching stamina cost. most hand replacements are huge items anyway.
#define TOTAL_MASS_MEDIEVAL_WEAPON 3.6 //very, very generic average sword/warpick/etc. weight in pounds.
#define TOTAL_MASS_TOY_SWORD 1.5
+104 -104
View File
@@ -1,104 +1,104 @@
#define SEND_SIGNAL(target, sigtype, arguments...) ( !target.comp_lookup || !target.comp_lookup[sigtype] ? NONE : target._SendSignal(sigtype, list(target, ##arguments)) )
#define SEND_GLOBAL_SIGNAL(sigtype, arguments...) ( SEND_SIGNAL(SSdcs, sigtype, ##arguments) )
#define COMPONENT_INCOMPATIBLE 1
#define COMPONENT_NOTRANSFER 2
// How multiple components of the exact same type are handled in the same datum
#define COMPONENT_DUPE_HIGHLANDER 0 //old component is deleted (default)
#define COMPONENT_DUPE_ALLOWED 1 //duplicates allowed
#define COMPONENT_DUPE_UNIQUE 2 //new component is deleted
#define COMPONENT_DUPE_UNIQUE_PASSARGS 4 //old component is given the initialization args of the new
// All signals. Format:
// When the signal is called: (signal arguments)
// All signals send the source datum of the signal as the first argument
// global signals
// These are signals which can be listened to by any component on any parent
// start global signals with "!", this used to be necessary but now it's just a formatting choice
//////////////////////////////////////////////////////////////////
// /datum signals
// /atom signals
//Positions for overrides list
//End positions
#define COMSIG_ATOM_RATVAR_ACT "atom_ratvar_act" //from base of atom/ratvar_act(): ()
/////////////////
// /area signals
// /turf signals
// /atom/movable signals
// /mob/living signals
// /mob/living/carbon signals
// /obj signals
#define COMSIG_OBJ_BREAK "obj_break" //from base of /obj/obj_break(): (damage_flag)
// /obj/item signals
// /obj/item/clothing signals
// /obj/item/implant signals
//This uses all return values of COMSIG_IMPLANT_OTHER
// /obj/item/pda signals
// /obj/item/radio signals
// /obj/item/pen signals
// /mob/living/carbon/human signals
// /datum/species signals
/*******Component Specific Signals*******/
//Janitor
//Food
//Mood
#define COMSIG_INCREASE_SANITY "decrease_sanity" //Called when you want to increase sanity from anywhere in the code.
#define COMSIG_DECREASE_SANITY "increase_sanity" //Same as above but to decrease sanity instead.
//NTnet
//Nanites
// /datum/component/storage signals
// /datum/action signals
/*******Non-Signal Component Related Defines*******/
//Redirection component init flags
#define REDIRECT_TRANSFER_WITH_TURF 1
//Arch
#define ARCH_PROB "probability" //Probability for each item
#define ARCH_MAXDROP "max_drop_amount" //each item's max drop amount
//Ouch my toes!
#define CALTROP_BYPASS_SHOES 1
#define CALTROP_IGNORE_WALKERS 2
#define ELEMENT_INCOMPATIBLE 1 // Return value to cancel attaching
// /datum/element flags
/// Causes the detach proc to be called when the host object is being deleted
#define ELEMENT_DETACH (1 << 0)
/**
* Only elements created with the same arguments given after `id_arg_index` share an element instance
* The arguments are the same when the text and number values are the same and all other values have the same ref
*/
#define ELEMENT_BESPOKE (1 << 1)
#define COMSIG_MOVABLE_UPDATE_GLIDE_SIZE "movable_glide_size" //Called when the movable's glide size is updated: (new_glide_size)
#define COMSIG_LIVING_FULLY_HEAL "living_fully_healed" //from base of /mob/living/fully_heal(): (admin_revive)
#define SEND_SIGNAL(target, sigtype, arguments...) ( !target.comp_lookup || !target.comp_lookup[sigtype] ? NONE : target._SendSignal(sigtype, list(target, ##arguments)) )
#define SEND_GLOBAL_SIGNAL(sigtype, arguments...) ( SEND_SIGNAL(SSdcs, sigtype, ##arguments) )
#define COMPONENT_INCOMPATIBLE 1
#define COMPONENT_NOTRANSFER 2
// How multiple components of the exact same type are handled in the same datum
#define COMPONENT_DUPE_HIGHLANDER 0 //old component is deleted (default)
#define COMPONENT_DUPE_ALLOWED 1 //duplicates allowed
#define COMPONENT_DUPE_UNIQUE 2 //new component is deleted
#define COMPONENT_DUPE_UNIQUE_PASSARGS 4 //old component is given the initialization args of the new
// All signals. Format:
// When the signal is called: (signal arguments)
// All signals send the source datum of the signal as the first argument
// global signals
// These are signals which can be listened to by any component on any parent
// start global signals with "!", this used to be necessary but now it's just a formatting choice
//////////////////////////////////////////////////////////////////
// /datum signals
// /atom signals
//Positions for overrides list
//End positions
#define COMSIG_ATOM_RATVAR_ACT "atom_ratvar_act" //from base of atom/ratvar_act(): ()
/////////////////
// /area signals
// /turf signals
// /atom/movable signals
// /mob/living signals
// /mob/living/carbon signals
// /obj signals
#define COMSIG_OBJ_BREAK "obj_break" //from base of /obj/obj_break(): (damage_flag)
// /obj/item signals
// /obj/item/clothing signals
// /obj/item/implant signals
//This uses all return values of COMSIG_IMPLANT_OTHER
// /obj/item/pda signals
// /obj/item/radio signals
// /obj/item/pen signals
// /mob/living/carbon/human signals
// /datum/species signals
/*******Component Specific Signals*******/
//Janitor
//Food
//Mood
#define COMSIG_INCREASE_SANITY "decrease_sanity" //Called when you want to increase sanity from anywhere in the code.
#define COMSIG_DECREASE_SANITY "increase_sanity" //Same as above but to decrease sanity instead.
//NTnet
//Nanites
// /datum/component/storage signals
// /datum/action signals
/*******Non-Signal Component Related Defines*******/
//Redirection component init flags
#define REDIRECT_TRANSFER_WITH_TURF 1
//Arch
#define ARCH_PROB "probability" //Probability for each item
#define ARCH_MAXDROP "max_drop_amount" //each item's max drop amount
//Ouch my toes!
#define CALTROP_BYPASS_SHOES 1
#define CALTROP_IGNORE_WALKERS 2
#define ELEMENT_INCOMPATIBLE 1 // Return value to cancel attaching
// /datum/element flags
/// Causes the detach proc to be called when the host object is being deleted
#define ELEMENT_DETACH (1 << 0)
/**
* Only elements created with the same arguments given after `id_arg_index` share an element instance
* The arguments are the same when the text and number values are the same and all other values have the same ref
*/
#define ELEMENT_BESPOKE (1 << 1)
#define COMSIG_MOVABLE_UPDATE_GLIDE_SIZE "movable_glide_size" //Called when the movable's glide size is updated: (new_glide_size)
#define COMSIG_LIVING_FULLY_HEAL "living_fully_healed" //from base of /mob/living/fully_heal(): (admin_revive)
+6
View File
@@ -23,6 +23,8 @@
#define MODE_WHISPER "whisper"
#define MODE_WHISPER_CRIT "whispercrit"
#define MODE_CUSTOM_SAY "custom_say"
#define MODE_DEPARTMENT "department"
#define MODE_KEY_DEPARTMENT "h"
#define MODE_TOKEN_DEPARTMENT ":h"
@@ -81,3 +83,7 @@
#define MAX_NAME_LEN 42
#define MAX_BROADCAST_LEN 512
#define MAX_CHARTER_LEN 80
// Audio/Visual Flags. Used to determine what sense are required to notice a message.
#define MSG_VISUAL (1<<0)
#define MSG_AUDIBLE (1<<1)
+5
View File
@@ -7,6 +7,8 @@
#define STATUS_EFFECT_REPLACE 2 //if it allows only one, but new instances replace
#define STATUS_EFFECT_REFRESH 3 // if it only allows one, and new instances just instead refresh the timer
///////////
// BUFFS //
///////////
@@ -56,6 +58,8 @@
#define MAX_MANIA_SEVERITY 100 //how high the mania severity can go
#define MANIA_DAMAGE_TO_CONVERT 90 //how much damage is required before it'll convert affected targets
#define STATUS_EFFECT_CHOKINGSTRAND /datum/status_effect/strandling //Choking Strand
#define STATUS_EFFECT_HISWRATH /datum/status_effect/his_wrath //His Wrath.
#define STATUS_EFFECT_SUMMONEDGHOST /datum/status_effect/cultghost //is a cult ghost and can't use manifest runes
@@ -75,6 +79,7 @@
#define STATUS_EFFECT_ICHORIAL_STAIN /datum/status_effect/ichorial_stain //Prevents a servant from being revived by vitality matrices for one minute.
#define STATUS_EFFECT_STASIS /datum/status_effect/incapacitating/stasis //Halts biological functions like bleeding, chemical processing, blood regeneration, walking, etc
#define STATUS_EFFECT_SPASMS /datum/status_effect/spasms //causes random muscle spasms
#define STATUS_EFFECT_NO_COMBAT_MODE /datum/status_effect/no_combat_mode //Wont allow combat mode and will disable it
#define STATUS_EFFECT_MESMERIZE /datum/status_effect/no_combat_mode/mesmerize //Just reskinned no_combat_mode
+4
View File
@@ -112,6 +112,7 @@
#define TRAIT_NOHARDCRIT "nohardcrit"
#define TRAIT_NOSOFTCRIT "nosoftcrit"
#define TRAIT_MINDSHIELD "mindshield"
#define TRAIT_SIXTHSENSE "sixthsense"
#define TRAIT_DISSECTED "dissected"
#define TRAIT_FEARLESS "fearless"
#define TRAIT_UNSTABLE "unstable"
@@ -121,9 +122,11 @@
#define TRAIT_PARALYSIS_R_LEG "para-r-leg"
#define TRAIT_DISK_VERIFIER "disk-verifier"
#define TRAIT_UNINTELLIGIBLE_SPEECH "unintelligible-speech"
#define TRAIT_SOOTHED_THROAT "soothed-throat"
#define TRAIT_LAW_ENFORCEMENT_METABOLISM "law-enforcement-metabolism"
#define TRAIT_STRONG_GRABBER "strong_grabber"
#define TRAIT_CALCIUM_HEALER "calcium_healer"
#define TRAIT_MAGIC_CHOKE "magic_choke"
#define TRAIT_ALCOHOL_LIGHTWEIGHT "alcohol_lightweight" //Skyrat port
#define TRAIT_CURSED_BLOOD "cursed_blood" //Yo dawg I heard you like bloodborne references so I put a
#define TRAIT_HEAT_DETECT "heat_detect"
@@ -206,6 +209,7 @@
#define CULT_TRAIT "cult"
#define CURSED_ITEM_TRAIT "cursed-item" // The item is magically cursed
#define ABSTRACT_ITEM_TRAIT "abstract-item"
#define STATUS_EFFECT_TRAIT "status-effect"
#define ROUNDSTART_TRAIT "roundstart" //cannot be removed without admin intervention
// unique trait sources, still defines
+600 -598
View File
File diff suppressed because it is too large Load Diff
+796 -796
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -13,6 +13,8 @@
#define POLL_IGNORE_GOLEM "golem"
#define POLL_IGNORE_SWARMER "swarmer"
#define POLL_IGNORE_DRONE "drone"
#define POLL_IGNORE_IMAGINARYFRIEND "imaginary_friend"
#define POLL_IGNORE_SPLITPERSONALITY "split_personality"
#define POLL_IGNORE_DEMON "demon"
#define POLL_IGNORE_WIZARD "wizard"
@@ -30,6 +32,8 @@ GLOBAL_LIST_INIT(poll_ignore_desc, list(
POLL_IGNORE_GOLEM = "Golems",
POLL_IGNORE_SWARMER = "Swarmer shells",
POLL_IGNORE_DRONE = "Drone shells",
POLL_IGNORE_IMAGINARYFRIEND = "Imaginary Friend",
POLL_IGNORE_SPLITPERSONALITY = "Split Personality",
POLL_IGNORE_DEMON = "Demons",
POLL_IGNORE_WIZARD = "Wizards",
))
+6
View File
@@ -213,6 +213,12 @@ or something covering your eyes."
desc = "Whoa man, you're tripping balls! Careful you don't get addicted... if you aren't already."
icon_state = "high"
/obj/screen/alert/hypnosis
name = "Hypnosis"
desc = "Something's hypnotizing you, but you're not really sure about what."
icon_state = "hypnosis"
var/phrase
/obj/screen/alert/drunk //Not implemented
name = "Drunk"
desc = "All that alcohol you've been drinking is impairing your speech, motor skills, and mental cognition. Make sure to act like it."
+20 -5
View File
@@ -1,10 +1,12 @@
//Brain Traumas are the new actual brain damage. Brain damage itself acts as a way to acquire traumas: every time brain damage is dealt, there's a chance of receiving a trauma.
//This chance gets higher the higher the mob's brainloss is. Removing traumas is a separate thing from removing brain damage: you can get restored to full brain operativity,
//but keep the quirks, until repaired by mannitol (for mild/special ones) or brain surgery (for severe ones).
// but keep the quirks, until repaired by neurine, surgery, lobotomy or magic; depending on the resilience
// of the trauma.
/datum/brain_trauma
var/name = "Brain Trauma"
var/desc = "A trauma caused by brain damage, which causes issues to the patient."
var/scan_desc = "a generic brain trauma" //description when detected by a health scanner
var/scan_desc = "generic brain trauma" //description when detected by a health scanner
var/mob/living/carbon/owner //the poor bastard
var/obj/item/organ/brain/brain //the poor bastard's brain
var/gain_text = "<span class='notice'>You feel traumatized.</span>"
@@ -12,15 +14,21 @@
var/can_gain = TRUE
var/random_gain = TRUE //can this be gained through random traumas?
var/resilience = TRAUMA_RESILIENCE_BASIC //how hard is this to cure?
var/clonable = TRUE // will this transfer if the brain is cloned?
/datum/brain_trauma/Destroy()
brain.traumas -= src
if(brain && brain.traumas)
brain.traumas -= src
if(owner)
on_lose()
brain = null
owner = null
return ..()
/datum/brain_trauma/proc/on_clone()
if(clonable)
return new type
//Called on life ticks
/datum/brain_trauma/proc/on_life()
return
@@ -33,17 +41,24 @@
/datum/brain_trauma/proc/on_gain()
to_chat(owner, gain_text)
RegisterSignal(owner, COMSIG_MOB_SAY, .proc/handle_speech)
RegisterSignal(owner, COMSIG_MOVABLE_HEAR, .proc/handle_hearing)
//Called when removed from a mob
/datum/brain_trauma/proc/on_lose(silent)
if(!silent)
to_chat(owner, lose_text)
UnregisterSignal(owner, COMSIG_MOB_SAY)
UnregisterSignal(owner, COMSIG_MOVABLE_HEAR)
//Called when hearing a spoken message
/datum/brain_trauma/proc/on_hear(message, speaker, message_language, raw_message, radio_freq)
return message
/datum/brain_trauma/proc/handle_hearing(datum/source, list/hearing_args)
UnregisterSignal(owner, COMSIG_MOVABLE_HEAR)
//Called when speaking
/datum/brain_trauma/proc/handle_speech(datum/source, list/speech_args)
UnregisterSignal(owner, COMSIG_MOB_SAY)
//Called when hugging. expand into generally interacting, where future coders could switch the intent?
/datum/brain_trauma/proc/on_hug(mob/living/hugger, mob/living/hugged)
return
+8 -9
View File
@@ -5,6 +5,7 @@
gain_text = ""
lose_text = ""
resilience = TRAUMA_RESILIENCE_SURGERY
var/hypnotic_phrase = ""
var/regex/target_phrase
@@ -44,18 +45,17 @@
"You feel a part of your mind repeating this over and over. You need to follow these words.",\
"Something about this sounds... right, for some reason. You feel like you should follow these words.",\
"These words keep echoing in your mind. You find yourself completely fascinated by them.")]</span>")
if(!HAS_TRAIT(owner, "hypnotherapy"))
to_chat(owner, "<span class='boldwarning'>You've been hypnotized by this sentence. You must follow these words. If it isn't a clear order, you can freely interpret how to do so,\
to_chat(owner, "<span class='boldwarning'>You've been hypnotized by this sentence. You must follow these words. If it isn't a clear order, you can freely interpret how to do so,\
as long as you act like the words are your highest priority.</span>")
else
to_chat(owner, "<span class='boldwarning'>You've been hypnotized by this sentence. You feel an incredible desire to follow these words, but are able to resist it somewhat. If it isn't a clear order, you can freely interpret how to do so,\
however this does not take precedence over your other objectives.</span>")
var/obj/screen/alert/hypnosis/hypno_alert = owner.throw_alert("hypnosis", /obj/screen/alert/hypnosis)
hypno_alert.desc = "\"[hypnotic_phrase]\"... your mind seems to be fixated on this concept."
..()
/datum/brain_trauma/hypnosis/on_lose()
message_admins("[ADMIN_LOOKUPFLW(owner)] is no longer hypnotized with the phrase '[hypnotic_phrase]'.")
log_game("[key_name(owner)] is no longer hypnotized with the phrase '[hypnotic_phrase]'.")
to_chat(owner, "<span class='userdanger'>You suddenly snap out of your fixation. The phrase '[hypnotic_phrase]' no longer feels important to you.</span>")
to_chat(owner, "<span class='userdanger'>You suddenly snap out of your hypnosis. The phrase '[hypnotic_phrase]' no longer feels important to you.</span>")
owner.clear_alert("hypnosis")
..()
/datum/brain_trauma/hypnosis/on_life()
@@ -67,6 +67,5 @@
if(2)
new /datum/hallucination/chat(owner, TRUE, FALSE, "<span class='hypnophrase'>[hypnotic_phrase]</span>")
/datum/brain_trauma/hypnosis/on_hear(message, speaker, message_language, raw_message, radio_freq)
message = target_phrase.Replace(message, "<span class='hypnophrase'>$1</span>")
return message
/datum/brain_trauma/hypnosis/handle_hearing(datum/source, list/hearing_args)
hearing_args[HEARING_MESSAGE] = target_phrase.Replace(hearing_args[HEARING_MESSAGE], "<span class='hypnophrase'>$1</span>")
+62 -12
View File
@@ -8,6 +8,10 @@
var/friend_initialized = FALSE
/datum/brain_trauma/special/imaginary_friend/on_gain()
var/mob/living/M = owner
if(M.stat == DEAD || !M.client)
qdel(src)
return
..()
make_friend()
get_ghost()
@@ -43,7 +47,7 @@
/datum/brain_trauma/special/imaginary_friend/proc/get_ghost()
set waitfor = FALSE
var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s imaginary friend?", ROLE_PAI, null, null, 75, friend)
var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s imaginary friend?", ROLE_PAI, null, null, 75, friend, POLL_IGNORE_IMAGINARYFRIEND)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
friend.key = C.key
@@ -74,26 +78,34 @@
/mob/camera/imaginary_friend/Login()
..()
to_chat(src, "<span class='notice'><b>You are the imaginary friend of [owner]!</b></span>")
to_chat(src, "<span class='notice'>You are absolutely loyal to your friend, no matter what.</span>")
to_chat(src, "<span class='notice'>You cannot directly influence the world around you, but you can see what [owner] cannot.</span>")
greet()
Show()
/mob/camera/imaginary_friend/proc/greet()
to_chat(src, "<span class='notice'><b>You are the imaginary friend of [owner]!</b></span>")
to_chat(src, "<span class='notice'>You are absolutely loyal to your friend, no matter what.</span>")
to_chat(src, "<span class='notice'>You cannot directly influence the world around you, but you can see what [owner] cannot.</span>")
/mob/camera/imaginary_friend/Initialize(mapload, _trauma)
. = ..()
var/gender = pick(MALE, FEMALE)
real_name = random_unique_name(gender)
name = real_name
trauma = _trauma
owner = trauma.owner
copy_known_languages_from(owner, TRUE)
human_image = get_flat_human_icon(null, pick(SSjob.occupations))
setup_friend()
join = new
join.Grant(src)
hide = new
hide.Grant(src)
/mob/camera/imaginary_friend/proc/setup_friend()
var/gender = pick(MALE, FEMALE)
real_name = random_unique_name(gender)
name = real_name
human_image = get_flat_human_icon(null, pick(SSjob.occupations))
/mob/camera/imaginary_friend/proc/Show()
if(!client) //nobody home
return
@@ -132,15 +144,15 @@
if(client.prefs.muted & MUTE_IC)
to_chat(src, "You cannot send IC messages (muted).")
return
if (src.client.handle_spam_prevention(message,MUTE_IC))
if (!(ignore_spam || forced) && src.client.handle_spam_prevention(message,MUTE_IC))
return
friend_talk(message)
/mob/camera/imaginary_friend/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode)
/mob/camera/imaginary_friend/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
if (client?.prefs.chat_on_map && (client.prefs.see_chat_non_mob || ismob(speaker)))
create_chat_message(speaker, message_language, raw_message, spans, message_mode)
to_chat(src, compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode))
to_chat(src, compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode, FALSE, source))
/mob/camera/imaginary_friend/proc/friend_talk(message)
message = capitalize(trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)))
@@ -220,4 +232,42 @@
var/mob/camera/imaginary_friend/I = owner
I.hidden = !I.hidden
I.Show()
update_status()
update_status()
//down here is the trapped mind
//like imaginary friend but a lot less imagination and more like mind prison//
/datum/brain_trauma/special/imaginary_friend/trapped_owner
name = "Trapped Victim"
desc = "Patient appears to be targeted by an invisible entity."
gain_text = ""
lose_text = ""
random_gain = FALSE
/datum/brain_trauma/special/imaginary_friend/trapped_owner/make_friend()
friend = new /mob/camera/imaginary_friend/trapped(get_turf(owner), src)
/datum/brain_trauma/special/imaginary_friend/trapped_owner/reroll_friend() //no rerolling- it's just the last owner's hell
if(friend.client) //reconnected
return
friend_initialized = FALSE
QDEL_NULL(friend)
qdel(src)
/datum/brain_trauma/special/imaginary_friend/trapped_owner/get_ghost() //no randoms
return
/mob/camera/imaginary_friend/trapped
name = "figment of imagination?"
real_name = "figment of imagination?"
desc = "The previous host of this body."
/mob/camera/imaginary_friend/trapped/greet()
to_chat(src, "<span class='notice'><b>You have managed to hold on as a figment of the new host's imagination!</b></span>")
to_chat(src, "<span class='notice'>All hope is lost for you, but at least you may interact with your host. You do not have to be loyal to them.</span>")
to_chat(src, "<span class='notice'>You cannot directly influence the world around you, but you can see what the host cannot.</span>")
/mob/camera/imaginary_friend/trapped/setup_friend()
real_name = "[owner.real_name]?"
name = real_name
human_image = icon('icons/mob/lavaland/lavaland_monsters.dmi', icon_state = "curseblob")
+109 -55
View File
@@ -64,21 +64,21 @@
name = "Speech Impediment"
desc = "Patient is unable to form coherent sentences."
scan_desc = "communication disorder"
gain_text = "" //mutation will handle the text
lose_text = ""
gain_text = "<span class='danger'>You can't seem to form any coherent thoughts!</span>"
lose_text = "<span class='danger'>Your mind feels more clear.</span>"
/datum/brain_trauma/mild/speech_impediment/on_gain()
ADD_TRAIT(owner, TRAIT_UNINTELLIGIBLE_SPEECH, TRAUMA_TRAIT)
. = ..()
..()
/datum/brain_trauma/mild/speech_impediment/on_lose()
REMOVE_TRAIT(owner, TRAIT_UNINTELLIGIBLE_SPEECH, TRAUMA_TRAIT)
. = ..()
..()
/datum/brain_trauma/mild/concussion
name = "Concussion"
desc = "Patient's brain is concussed."
scan_desc = "a concussion"
scan_desc = "concussion"
gain_text = "<span class='warning'>Your head hurts!</span>"
lose_text = "<span class='notice'>The pressure inside your head starts fading.</span>"
@@ -157,54 +157,108 @@
gain_text = "<span class='warning'>Your muscles feel oddly faint.</span>"
lose_text = "<span class='notice'>You feel in control of your muscles again.</span>"
/datum/brain_trauma/mild/muscle_spasms/on_life()
if(prob(7))
switch(rand(1,5))
if(1)
if(owner.canmove && !isspaceturf(owner.loc))
to_chat(owner, "<span class='warning'>Your leg spasms!</span>")
step(owner, pick(GLOB.cardinals))
if(2)
if(owner.incapacitated())
return
var/obj/item/I = owner.get_active_held_item()
if(I)
to_chat(owner, "<span class='warning'>Your fingers spasm!</span>")
owner.log_message("used [I] due to a Muscle Spasm", LOG_ATTACK)
I.attack_self(owner)
if(3)
var/prev_intent = owner.a_intent
owner.a_intent = INTENT_HARM
var/range = 1
if(istype(owner.get_active_held_item(), /obj/item/gun)) //get targets to shoot at
range = 7
var/list/mob/living/targets = list()
for(var/mob/M in oview(owner, range))
if(isliving(M))
targets += M
if(LAZYLEN(targets))
to_chat(owner, "<span class='warning'>Your arm spasms!</span>")
owner.log_message(" attacked someone due to a Muscle Spasm") //the following attack will log itself
owner.ClickOn(pick(targets))
owner.a_intent = prev_intent
if(4)
var/prev_intent = owner.a_intent
owner.a_intent = INTENT_HARM
to_chat(owner, "<span class='warning'>Your arm spasms!</span>")
owner.log_message("attacked [owner.p_them()]self to a Muscle Spasm", LOG_ATTACK)
owner.ClickOn(owner)
owner.a_intent = prev_intent
if(5)
if(owner.incapacitated())
return
var/obj/item/I = owner.get_active_held_item()
var/list/turf/targets = list()
for(var/turf/T in oview(owner, 3))
targets += T
if(LAZYLEN(targets) && I)
to_chat(owner, "<span class='warning'>Your arm spasms!</span>")
owner.log_message("threw [I] due to a Muscle Spasm", LOG_ATTACK)
owner.throw_item(pick(targets))
/datum/brain_trauma/mild/muscle_spasms/on_gain()
owner.apply_status_effect(STATUS_EFFECT_SPASMS)
..()
/datum/brain_trauma/mild/muscle_spasms/on_lose()
owner.remove_status_effect(STATUS_EFFECT_SPASMS)
..()
/datum/brain_trauma/mild/nervous_cough
name = "Nervous Cough"
desc = "Patient feels a constant need to cough."
scan_desc = "nervous cough"
gain_text = "<span class='warning'>Your throat itches incessantly...</span>"
lose_text = "<span class='notice'>Your throat stops itching.</span>"
/datum/brain_trauma/mild/nervous_cough/on_life()
if(prob(12) && !HAS_TRAIT(owner, TRAIT_SOOTHED_THROAT))
if(prob(5))
to_chat(owner, "<span notice='warning'>[pick("You have a coughing fit!", "You can't stop coughing!")]</span>")
owner.Stun(20)
owner.emote("cough")
addtimer(CALLBACK(owner, /mob/.proc/emote, "cough"), 6)
addtimer(CALLBACK(owner, /mob/.proc/emote, "cough"), 12)
owner.emote("cough")
..()
/datum/brain_trauma/mild/expressive_aphasia
name = "Expressive Aphasia"
desc = "Patient is affected by partial loss of speech leading to a reduced vocabulary."
scan_desc = "inability to form complex sentences"
gain_text = "<span class='warning'>You lose your grasp on complex words.</span>"
lose_text = "<span class='notice'>You feel your vocabulary returning to normal again.</span>"
var/static/list/common_words = world.file2list("strings/1000_most_common.txt")
/datum/brain_trauma/mild/expressive_aphasia/handle_speech(datum/source, list/speech_args)
var/message = speech_args[SPEECH_MESSAGE]
if(message)
var/list/message_split = splittext(message, " ")
var/list/new_message = list()
for(var/word in message_split)
var/suffix = copytext(word,-1)
// Check if we have a suffix and break it out of the word
if(suffix in list("." , "," , ";" , "!" , ":" , "?"))
word = copytext(word,1,-1)
else
suffix = ""
word = html_decode(word)
if(lowertext(word) in common_words)
new_message += word + suffix
else
if(prob(30) && message_split.len > 2)
new_message += pick("uh","erm")
break
else
var/list/charlist = string2charlist(word) // Stupid shit code
shuffle_inplace(charlist)
charlist.len = round(charlist.len * 0.5,1)
new_message += html_encode(jointext(charlist,"")) + suffix
message = jointext(new_message, " ")
speech_args[SPEECH_MESSAGE] = trim(message)
/datum/brain_trauma/mild/mind_echo
name = "Mind Echo"
desc = "Patient's language neurons do not terminate properly, causing previous speech patterns to occasionally resurface spontaneously."
scan_desc = "looping neural pattern"
gain_text = "<span class='warning'>You feel a faint echo of your thoughts...</span>"
lose_text = "<span class='notice'>The faint echo fades away.</span>"
var/list/hear_dejavu = list()
var/list/speak_dejavu = list()
/datum/brain_trauma/mild/mind_echo/handle_hearing(datum/source, list/hearing_args)
if(owner == hearing_args[HEARING_SPEAKER])
return
if(hear_dejavu.len >= 5)
if(prob(25))
var/deja_vu = pick_n_take(hear_dejavu)
var/static/regex/quoted_spoken_message = regex("\".+\"", "gi")
hearing_args[HEARING_MESSAGE] = quoted_spoken_message.Replace(hearing_args[HEARING_MESSAGE], "\"[deja_vu]\"") //Quotes included to avoid cases where someone says part of their name
return
if(hear_dejavu.len >= 15)
if(prob(50))
popleft(hear_dejavu) //Remove the oldest
hear_dejavu += hearing_args[HEARING_RAW_MESSAGE]
else
hear_dejavu += hearing_args[HEARING_RAW_MESSAGE]
/datum/brain_trauma/mild/mind_echo/handle_speech(datum/source, list/speech_args)
if(speak_dejavu.len >= 5)
if(prob(25))
var/deja_vu = pick_n_take(speak_dejavu)
speech_args[SPEECH_MESSAGE] = deja_vu
return
if(speak_dejavu.len >= 15)
if(prob(50))
popleft(speak_dejavu) //Remove the oldest
speak_dejavu += speech_args[SPEECH_MESSAGE]
else
speak_dejavu += speech_args[SPEECH_MESSAGE]
+99 -20
View File
@@ -2,8 +2,8 @@
name = "Phobia"
desc = "Patient is unreasonably afraid of something."
scan_desc = "phobia"
gain_text = ""
lose_text = ""
gain_text = "<span class='warning'>You start finding default values very unnerving...</span>"
lose_text = "<span class='notice'>You no longer feel afraid of default values.</span>"
var/phobia_type
var/next_check = 0
var/next_scare = 0
@@ -14,8 +14,10 @@
var/list/trigger_turfs
var/list/trigger_species
/datum/brain_trauma/mild/phobia/New(specific_type)
phobia_type = specific_type
/datum/brain_trauma/mild/phobia/New(new_phobia_type)
if(new_phobia_type)
phobia_type = new_phobia_type
if(!phobia_type)
phobia_type = pick(SStraumas.phobia_types)
@@ -29,6 +31,11 @@
trigger_species = SStraumas.phobia_species[phobia_type]
..()
/datum/brain_trauma/mild/phobia/on_clone()
if(clonable)
return new type(phobia_type)
/datum/brain_trauma/mild/phobia/on_life()
..()
if(HAS_TRAIT(owner, TRAIT_FEARLESS))
@@ -44,6 +51,12 @@
if(is_type_in_typecache(O, trigger_objs))
freak_out(O)
return
for(var/mob/living/carbon/human/HU in seen_atoms) //check equipment for trigger items
for(var/X in HU.get_all_slots() | HU.held_items)
var/obj/I = X
if(!QDELETED(I) && is_type_in_typecache(I, trigger_objs))
freak_out(I)
return
if(LAZYLEN(trigger_turfs))
for(var/turf/T in seen_atoms)
@@ -51,45 +64,41 @@
freak_out(T)
return
if(LAZYLEN(trigger_mobs) || LAZYLEN(trigger_objs))
seen_atoms -= owner //make sure they aren't afraid of themselves.
if(LAZYLEN(trigger_mobs) || LAZYLEN(trigger_species))
for(var/mob/M in seen_atoms)
if(is_type_in_typecache(M, trigger_mobs))
freak_out(M)
return
else if(ishuman(M)) //check their equipment for trigger items
else if(ishuman(M)) //check their species
var/mob/living/carbon/human/H = M
if(LAZYLEN(trigger_species) && H.dna && H.dna.species && is_type_in_typecache(H.dna.species, trigger_species))
freak_out(H)
return
for(var/X in H.get_all_slots() | H.held_items)
var/obj/I = X
if(!QDELETED(I) && is_type_in_typecache(I, trigger_objs))
freak_out(I)
return
/datum/brain_trauma/mild/phobia/on_hear(message, speaker, message_language, raw_message, radio_freq)
/datum/brain_trauma/mild/phobia/handle_hearing(datum/source, list/hearing_args)
if(!owner.can_hear() || world.time < next_scare) //words can't trigger you if you can't hear them *taps head*
return message
return
if(HAS_TRAIT(owner, TRAIT_FEARLESS))
return message
return
for(var/word in trigger_words)
var/reg = regex("(\\b|\\A)[REGEX_QUOTE(word)]'?s*(\\b|\\Z)", "i")
var/regex/reg = regex("(\\b|\\A)[REGEX_QUOTE(word)]'?s*(\\b|\\Z)", "i")
if(findtext(raw_message, reg))
if(findtext(hearing_args[HEARING_RAW_MESSAGE], reg))
addtimer(CALLBACK(src, .proc/freak_out, null, word), 10) //to react AFTER the chat message
hearing_args[HEARING_MESSAGE] = reg.Replace(hearing_args[HEARING_MESSAGE], "<span class='phobia'>$1</span>")
break
return message
/datum/brain_trauma/mild/phobia/handle_speech(datum/source, list/speech_args)
if(HAS_TRAIT(owner, TRAIT_FEARLESS))
return
for(var/word in trigger_words)
var/reg = regex("(\\b|\\A)[REGEX_QUOTE(word)]'?s*(\\b|\\Z)", "i")
var/regex/reg = regex("(\\b|\\A)[REGEX_QUOTE(word)]'?s*(\\b|\\Z)", "i")
if(findtext(speech_args[SPEECH_MESSAGE], reg))
to_chat(owner, "<span class='warning'>You can't bring yourself to say the word \"[word]\"!</span>")
to_chat(owner, "<span class='warning'>You can't bring yourself to say the word \"<span class='phobia'>[word]</span>\"!</span>")
speech_args[SPEECH_MESSAGE] = ""
/datum/brain_trauma/mild/phobia/proc/freak_out(atom/reason, trigger_word)
@@ -125,6 +134,76 @@
owner.Jitter(10)
owner.stuttering += 10
// Defined phobia types for badminry, not included in the RNG trauma pool to avoid diluting.
/datum/brain_trauma/mild/phobia/spiders
phobia_type = "spiders"
random_gain = FALSE
/datum/brain_trauma/mild/phobia/space
phobia_type = "space"
random_gain = FALSE
/datum/brain_trauma/mild/phobia/security
phobia_type = "security"
random_gain = FALSE
/datum/brain_trauma/mild/phobia/clowns
phobia_type = "clowns"
random_gain = FALSE
/datum/brain_trauma/mild/phobia/greytide
phobia_type = "greytide"
random_gain = FALSE
/datum/brain_trauma/mild/phobia/lizards
phobia_type = "lizards"
random_gain = FALSE
/datum/brain_trauma/mild/phobia/skeletons
phobia_type = "skeletons"
random_gain = FALSE
/datum/brain_trauma/mild/phobia/snakes
phobia_type = "snakes"
random_gain = FALSE
/datum/brain_trauma/mild/phobia/robots
phobia_type = "robots"
random_gain = FALSE
/datum/brain_trauma/mild/phobia/doctors
phobia_type = "doctors"
random_gain = FALSE
/datum/brain_trauma/mild/phobia/authority
phobia_type = "authority"
random_gain = FALSE
/datum/brain_trauma/mild/phobia/supernatural
phobia_type = "the supernatural"
random_gain = FALSE
/datum/brain_trauma/mild/phobia/aliens
phobia_type = "aliens"
random_gain = FALSE
/datum/brain_trauma/mild/phobia/strangers
phobia_type = "strangers"
random_gain = FALSE
/datum/brain_trauma/mild/phobia/birds
phobia_type = "birds"
random_gain = FALSE
/datum/brain_trauma/mild/phobia/falling
phobia_type = "falling"
random_gain = FALSE
/datum/brain_trauma/mild/phobia/anime
phobia_type = "anime"
random_gain = FALSE
/datum/brain_trauma/mild/phobia/conspiracies
phobia_type = "conspiracies"
random_gain = FALSE
+3 -3
View File
@@ -119,7 +119,7 @@
owner.update_disabled_bodyparts()
/datum/brain_trauma/severe/paralysis/paraplegic
//can_gain = FALSE maybe breaks.
random_gain = FALSE
paralysis_type = "legs"
resilience = TRAUMA_RESILIENCE_ABSOLUTE
@@ -149,7 +149,7 @@
/datum/brain_trauma/severe/monophobia
name = "Monophobia"
desc = "Patient feels sick and distressed when not around other people, leading to potentially lethal levels of stress."
scan_desc = "severe monophobia"
scan_desc = "monophobia"
gain_text = ""
lose_text = "<span class='notice'>You feel like you could be safe on your own.</span>"
var/stress = 0
@@ -168,7 +168,7 @@
if(stress > 10 && (prob(5)))
stress_reaction()
else
stress -= 4
stress = max(stress - 4, 0)
/datum/brain_trauma/severe/monophobia/proc/check_alone()
if(HAS_TRAIT(owner, TRAIT_BLIND))
+106 -3
View File
@@ -22,6 +22,14 @@
else
speak("neutral", prob(25))
/datum/brain_trauma/special/godwoken/on_gain()
ADD_TRAIT(owner, TRAIT_HOLY, TRAUMA_TRAIT)
..()
/datum/brain_trauma/special/godwoken/on_lose()
REMOVE_TRAIT(owner, TRAIT_HOLY, TRAUMA_TRAIT)
..()
/datum/brain_trauma/special/godwoken/proc/speak(type, include_owner = FALSE)
var/message
switch(type)
@@ -36,7 +44,7 @@
else
message = pick_list_replacements(BRAIN_DAMAGE_FILE, "god_neutral")
playsound(get_turf(owner), 'sound/magic/clockwork/invoke_general.ogg', 200, 1, 5)
playsound(get_turf(owner), 'sound/magic/clockwork/invoke_general.ogg', 200, TRUE, 5)
voice_of_god(message, owner, list("colossus","yell"), 2.5, include_owner, FALSE)
/datum/brain_trauma/special/bluespace_prophet
@@ -134,7 +142,101 @@
/datum/brain_trauma/special/psychotic_brawling/bath_salts
name = "Chemical Violent Psychosis"
random_gain = FALSE
clonable = FALSE
/datum/brain_trauma/special/tenacity
name = "Tenacity"
desc = "Patient is psychologically unaffected by pain and injuries, and can remain standing far longer than a normal person."
scan_desc = "traumatic neuropathy"
gain_text = "<span class='warning'>You suddenly stop feeling pain.</span>"
lose_text = "<span class='warning'>You realize you can feel pain again.</span>"
/datum/brain_trauma/special/tenacity/on_gain()
ADD_TRAIT(owner, TRAIT_NOSOFTCRIT, TRAUMA_TRAIT)
ADD_TRAIT(owner, TRAIT_NOHARDCRIT, TRAUMA_TRAIT)
..()
/datum/brain_trauma/special/tenacity/on_lose()
REMOVE_TRAIT(owner, TRAIT_NOSOFTCRIT, TRAUMA_TRAIT)
REMOVE_TRAIT(owner, TRAIT_NOHARDCRIT, TRAUMA_TRAIT)
..()
/datum/brain_trauma/special/death_whispers
name = "Functional Cerebral Necrosis"
desc = "Patient's brain is stuck in a functional near-death state, causing occasional moments of lucid hallucinations, which are often interpreted as the voices of the dead."
scan_desc = "chronic functional necrosis"
gain_text = "<span class='warning'>You feel dead inside.</span>"
lose_text = "<span class='notice'>You feel alive again.</span>"
var/active = FALSE
/datum/brain_trauma/special/death_whispers/on_life()
..()
if(!active && prob(2))
whispering()
/datum/brain_trauma/special/death_whispers/on_lose()
if(active)
cease_whispering()
..()
/datum/brain_trauma/special/death_whispers/proc/whispering()
ADD_TRAIT(owner, TRAIT_SIXTHSENSE, TRAUMA_TRAIT)
active = TRUE
addtimer(CALLBACK(src, .proc/cease_whispering), rand(50, 300))
/datum/brain_trauma/special/death_whispers/proc/cease_whispering()
REMOVE_TRAIT(owner, TRAIT_SIXTHSENSE, TRAUMA_TRAIT)
active = FALSE
/datum/brain_trauma/special/existential_crisis
name = "Existential Crisis"
desc = "Patient's hold on reality becomes faint, causing occasional bouts of non-existence."
scan_desc = "existential crisis"
gain_text = "<span class='notice'>You feel less real.</span>"
lose_text = "<span class='warning'>You feel more substantial again.</span>"
var/obj/effect/abstract/sync_holder/veil/veil
var/next_crisis = 0
/datum/brain_trauma/special/existential_crisis/on_life()
..()
if(!veil && world.time > next_crisis && prob(3))
if(isturf(owner.loc))
fade_out()
/datum/brain_trauma/special/existential_crisis/on_lose()
if(veil)
fade_in()
..()
/datum/brain_trauma/special/existential_crisis/proc/fade_out()
if(veil)
return
var/duration = rand(50, 450)
veil = new(owner.drop_location())
to_chat(owner, "<span class='warning'>[pick("You stop thinking for a moment. Therefore you are not.",\
"To be or not to be...",\
"Why exist?",\
"You stop keeping it real.",\
"Your grip on existence slips.",\
"Do you even exist?",\
"You simply fade away.")]</span>")
owner.forceMove(veil)
SEND_SIGNAL(owner, COMSIG_MOVABLE_SECLUDED_LOCATION)
for(var/thing in owner)
var/atom/movable/AM = thing
SEND_SIGNAL(AM, COMSIG_MOVABLE_SECLUDED_LOCATION)
next_crisis = world.time + 600
addtimer(CALLBACK(src, .proc/fade_in), duration)
/datum/brain_trauma/special/existential_crisis/proc/fade_in()
QDEL_NULL(veil)
to_chat(owner, "<span class='notice'>You fade back into reality.</span>")
next_crisis = world.time + 600
//base sync holder is in desynchronizer.dm
/obj/effect/abstract/sync_holder/veil
name = "non-existence"
desc = "Existence is just a state of mind."
/datum/brain_trauma/special/beepsky
name = "Criminal"
@@ -142,6 +244,7 @@
scan_desc = "criminal mind"
gain_text = "<span class='warning'>Justice is coming for you.</span>"
lose_text = "<span class='notice'>You were absolved for your crimes.</span>"
clonable = FALSE
random_gain = FALSE
var/obj/effect/hallucination/simple/securitron/beepsky
@@ -201,4 +304,4 @@
/obj/effect/hallucination/simple/securitron/Destroy()
STOP_PROCESSING(SSfastprocess,src)
return ..()
return ..()
+10 -6
View File
@@ -13,6 +13,10 @@
var/mob/living/split_personality/owner_backseat
/datum/brain_trauma/severe/split_personality/on_gain()
var/mob/living/M = owner
if(M.stat == DEAD) //No use assigning people to a corpse
qdel(src)
return
..()
make_backseats()
get_ghost()
@@ -23,7 +27,7 @@
/datum/brain_trauma/severe/split_personality/proc/get_ghost()
set waitfor = FALSE
var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s split personality?", ROLE_PAI, null, null, 75, stranger_backseat)
var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s split personality?", ROLE_PAI, null, null, 75, stranger_backseat, POLL_IGNORE_SPLITPERSONALITY)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
stranger_backseat.key = C.key
@@ -191,13 +195,13 @@
/datum/brain_trauma/severe/split_personality/brainwashing/on_life()
return //no random switching
/datum/brain_trauma/severe/split_personality/brainwashing/on_hear(message, speaker, message_language, raw_message, radio_freq)
if(HAS_TRAIT(owner, TRAIT_DEAF) || owner == speaker)
return message
/datum/brain_trauma/severe/split_personality/brainwashing/handle_hearing(datum/source, list/hearing_args)
if(HAS_TRAIT(owner, TRAIT_DEAF) || owner == hearing_args[HEARING_SPEAKER])
return
var/message = hearing_args[HEARING_MESSAGE]
if(findtext(message, codeword))
message = replacetext(message, codeword, "<span class='warning'>[codeword]</span>")
hearing_args[HEARING_MESSAGE] = replacetext(message, codeword, "<span class='warning'>[codeword]</span>")
addtimer(CALLBACK(src, /datum/brain_trauma/severe/split_personality.proc/switch_personalities), 10)
return message
/datum/brain_trauma/severe/split_personality/brainwashing/handle_speech(datum/source, list/speech_args)
if(findtext(speech_args[SPEECH_MESSAGE], codeword))
-6
View File
@@ -53,7 +53,6 @@
RegisterSignal(parent, COMSIG_MOB_ALLOWED, .proc/check_access)
RegisterSignal(parent, COMSIG_LIVING_ELECTROCUTE_ACT, .proc/on_shock)
RegisterSignal(parent, COMSIG_LIVING_MINOR_SHOCK, .proc/on_minor_shock)
RegisterSignal(parent, COMSIG_MOVABLE_HEAR, .proc/on_hear)
RegisterSignal(parent, COMSIG_SPECIES_GAIN, .proc/check_viable_biotype)
RegisterSignal(parent, COMSIG_NANITE_SIGNAL, .proc/receive_signal)
@@ -191,11 +190,6 @@
var/datum/nanite_program/NP = X
NP.on_death(gibbed)
/datum/component/nanites/proc/on_hear(datum/source, message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode)
for(var/X in programs)
var/datum/nanite_program/NP = X
NP.on_hear(message, speaker, message_language, raw_message, radio_freq, spans, message_mode)
/datum/component/nanites/proc/receive_signal(datum/source, code, source = "an unidentified source")
for(var/X in programs)
var/datum/nanite_program/NP = X
+2 -1
View File
@@ -8,6 +8,7 @@
if(!ismovableatom(parent))
return COMPONENT_INCOMPATIBLE
RegisterSignal(parent, list(COMSIG_MOVABLE_Z_CHANGED), .proc/check_in_bounds)
RegisterSignal(parent, list(COMSIG_MOVABLE_SECLUDED_LOCATION), .proc/relocate)
RegisterSignal(parent, list(COMSIG_PARENT_PREQDELETED), .proc/check_deletion)
RegisterSignal(parent, list(COMSIG_ITEM_IMBUE_SOUL), .proc/check_soul_imbue)
src.inform_admins = inform_admins
@@ -32,6 +33,7 @@
var/atom/movable/AM = parent
AM.forceMove(targetturf)
to_chat(get(parent, /mob), "<span class='danger'>You can't help but feel that you just lost something back there...</span>")
// move the disc, so ghosts remain orbiting it even if it's "destroyed"
return targetturf
@@ -40,7 +42,6 @@
return
else
var/turf/currentturf = get_turf(src)
to_chat(get(parent, /mob), "<span class='danger'>You can't help but feel that you just lost something back there...</span>")
var/turf/targetturf = relocate()
log_game("[parent] has been moved out of bounds in [loc_name(currentturf)]. Moving it to [loc_name(targetturf)].")
if(inform_admins)
+6 -2
View File
@@ -58,6 +58,10 @@
var/screen_start_y = 2
//End
var/limited_random_access = FALSE //Quick if statement in accessible_items to determine if we care at all about what people can access at once.
var/limited_random_access_stack_position = 0 //If >0, can only access top <x> items
var/limited_random_access_stack_bottom_up = FALSE //If TRUE, above becomes bottom <x> items
/datum/component/storage/Initialize(datum/component/storage/concrete/master)
if(!isatom(parent))
return COMPONENT_INCOMPATIBLE
@@ -626,9 +630,9 @@
if(M == viewing)
to_chat(usr, "<span class='notice'>You put [I] [insert_preposition]to [parent].</span>")
else if(in_range(M, viewing)) //If someone is standing close enough, they can tell what it is...
viewing.show_message("<span class='notice'>[M] puts [I] [insert_preposition]to [parent].</span>", 1)
viewing.show_message("<span class='notice'>[M] puts [I] [insert_preposition]to [parent].</span>", MSG_VISUAL)
else if(I && I.w_class >= 3) //Otherwise they can only see large or normal items from a distance...
viewing.show_message("<span class='notice'>[M] puts [I] [insert_preposition]to [parent].</span>", 1)
viewing.show_message("<span class='notice'>[M] puts [I] [insert_preposition]to [parent].</span>", MSG_VISUAL)
/datum/component/storage/proc/update_icon()
if(isobj(parent))
+95 -34
View File
@@ -609,6 +609,38 @@
icon_state = "ichorial_stain"
alerttooltipstyle = "clockcult"
//GOLEM GANG
/datum/status_effect/strandling //get it, strand as in durathread strand + strangling = strandling hahahahahahahahahahhahahaha i want to die
id = "strandling"
status_type = STATUS_EFFECT_UNIQUE
alert_type = /obj/screen/alert/status_effect/strandling
/datum/status_effect/strandling/on_apply()
ADD_TRAIT(owner, TRAIT_MAGIC_CHOKE, "dumbmoron")
return ..()
/datum/status_effect/strandling/on_remove()
REMOVE_TRAIT(owner, TRAIT_MAGIC_CHOKE, "dumbmoron")
return ..()
/obj/screen/alert/status_effect/strandling
name = "Choking strand"
desc = "A magical strand of Durathread is wrapped around your neck, preventing you from breathing! Click this icon to remove the strand."
icon_state = "his_grace"
alerttooltipstyle = "hisgrace"
/obj/screen/alert/status_effect/strandling/Click(location, control, params)
. = ..()
to_chat(mob_viewer, "<span class='notice'>You attempt to remove the durathread strand from around your neck.</span>")
if(do_after(mob_viewer, 35, null, mob_viewer))
if(isliving(mob_viewer))
var/mob/living/L = mob_viewer
to_chat(mob_viewer, "<span class='notice'>You succesfuly remove the durathread strand.</span>")
L.remove_status_effect(STATUS_EFFECT_CHOKINGSTRAND)
datum/status_effect/pacify
id = "pacify"
status_type = STATUS_EFFECT_REPLACE
@@ -635,8 +667,7 @@ datum/status_effect/pacify
tick_interval = 10
examine_text = "<span class='warning'>SUBJECTPRONOUN seems slow and unfocused.</span>"
var/stun = TRUE
var/triggered = FALSE
alert_type = null
alert_type = /obj/screen/alert/status_effect/trance
/obj/screen/alert/status_effect/trance
name = "Trance"
@@ -644,17 +675,6 @@ datum/status_effect/pacify
icon_state = "high"
/datum/status_effect/trance/tick()
if(HAS_TRAIT(owner, "hypnotherapy"))
if(triggered == TRUE)
UnregisterSignal(owner, COMSIG_MOVABLE_HEAR)
RegisterSignal(owner, COMSIG_MOVABLE_HEAR, .proc/hypnotize)
ADD_TRAIT(owner, TRAIT_MUTE, "trance")
if(!owner.has_quirk(/datum/quirk/monochromatic))
owner.add_client_colour(/datum/client_colour/monochrome)
to_chat(owner, "<span class='warning'>[pick("You feel your thoughts slow down...", "You suddenly feel extremely dizzy...", "You feel like you're in the middle of a dream...","You feel incredibly relaxed...")]</span>")
triggered = FALSE
else
return
if(stun)
owner.Stun(60, TRUE, TRUE)
owner.dizziness = 20
@@ -662,48 +682,33 @@ datum/status_effect/pacify
/datum/status_effect/trance/on_apply()
if(!iscarbon(owner))
return FALSE
if(HAS_TRAIT(owner, "hypnotherapy"))
RegisterSignal(owner, COMSIG_MOVABLE_HEAR, .proc/listen)
return TRUE
alert_type = /obj/screen/alert/status_effect/trance
RegisterSignal(owner, COMSIG_MOVABLE_HEAR, .proc/hypnotize)
ADD_TRAIT(owner, TRAIT_MUTE, "trance")
if(!owner.has_quirk(/datum/quirk/monochromatic))
owner.add_client_colour(/datum/client_colour/monochrome)
owner.add_client_colour(/datum/client_colour/monochrome/trance)
owner.visible_message("[stun ? "<span class='warning'>[owner] stands still as [owner.p_their()] eyes seem to focus on a distant point.</span>" : ""]", \
"<span class='warning'>[pick("You feel your thoughts slow down...", "You suddenly feel extremely dizzy...", "You feel like you're in the middle of a dream...","You feel incredibly relaxed...")]</span>")
return TRUE
/datum/status_effect/trance/on_creation(mob/living/new_owner, _duration, _stun = TRUE, source_quirk = FALSE)//hypnoquirk makes no visible message, prevents self antag messages, and places phrase below objectives.
/datum/status_effect/trance/on_creation(mob/living/new_owner, _duration, _stun = TRUE)
duration = _duration
stun = _stun
if(source_quirk == FALSE && HAS_TRAIT(owner, "hypnotherapy"))
REMOVE_TRAIT(owner, "hypnotherapy", ROUNDSTART_TRAIT)
return ..()
/datum/status_effect/trance/on_remove()
UnregisterSignal(owner, COMSIG_MOVABLE_HEAR)
REMOVE_TRAIT(owner, TRAIT_MUTE, "trance")
owner.dizziness = 0
if(!owner.has_quirk(/datum/quirk/monochromatic))
owner.remove_client_colour(/datum/client_colour/monochrome)
owner.remove_client_colour(/datum/client_colour/monochrome/trance)
to_chat(owner, "<span class='warning'>You snap out of your trance!</span>")
/datum/status_effect/trance/proc/listen(datum/source, message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode)
to_chat(owner, "<span class='notice'><i>[speaker] accidentally sets off your implanted trigger, sending you into a hypnotic daze!</i></span>")
triggered = TRUE
/datum/status_effect/trance/proc/hypnotize(datum/source, message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode)
/datum/status_effect/trance/proc/hypnotize(datum/source, list/hearing_args)
if(!owner.can_hear())
return
if(speaker == owner)
if(hearing_args[HEARING_SPEAKER] == owner)
return
var/mob/living/carbon/C = owner
C.cure_trauma_type(/datum/brain_trauma/hypnosis, TRAUMA_RESILIENCE_SURGERY) //clear previous hypnosis
if(HAS_TRAIT(C, "hypnotherapy"))
addtimer(CALLBACK(C, /mob/living/carbon.proc/gain_trauma, /datum/brain_trauma/hypnosis, TRAUMA_RESILIENCE_SURGERY, raw_message, TRUE), 10)
else
addtimer(CALLBACK(C, /mob/living/carbon.proc/gain_trauma, /datum/brain_trauma/hypnosis, TRAUMA_RESILIENCE_SURGERY, raw_message), 10)
addtimer(CALLBACK(C, /mob/living/carbon.proc/gain_trauma, /datum/brain_trauma/hypnosis, TRAUMA_RESILIENCE_SURGERY, hearing_args[HEARING_RAW_MESSAGE]), 10)
addtimer(CALLBACK(C, /mob/living.proc/Stun, 60, TRUE, TRUE), 15) //Take some time to think about it
qdel(src)
@@ -729,3 +734,59 @@ datum/status_effect/pacify
name = "Genetic Breakdown"
desc = "I don't feel so good. Your body can't handle the mutations! You have one minute to remove your mutations, or you will be met with a horrible fate."
icon_state = "dna_melt"
/datum/status_effect/spasms
id = "spasms"
status_type = STATUS_EFFECT_MULTIPLE
alert_type = null
/datum/status_effect/spasms/tick()
if(prob(15))
switch(rand(1,5))
if(1)
if((!owner.lying && !owner.buckled) && isturf(owner.loc))
to_chat(owner, "<span class='warning'>Your leg spasms!</span>")
step(owner, pick(GLOB.cardinals))
if(2)
if(owner.incapacitated())
return
var/obj/item/I = owner.get_active_held_item()
if(I)
to_chat(owner, "<span class='warning'>Your fingers spasm!</span>")
owner.log_message("used [I] due to a Muscle Spasm", LOG_ATTACK)
I.attack_self(owner)
if(3)
var/prev_intent = owner.a_intent
owner.a_intent = INTENT_HARM
var/range = 1
if(istype(owner.get_active_held_item(), /obj/item/gun)) //get targets to shoot at
range = 7
var/list/mob/living/targets = list()
for(var/mob/M in oview(owner, range))
if(isliving(M))
targets += M
if(LAZYLEN(targets))
to_chat(owner, "<span class='warning'>Your arm spasms!</span>")
owner.log_message(" attacked someone due to a Muscle Spasm", LOG_ATTACK) //the following attack will log itself
owner.ClickOn(pick(targets))
owner.a_intent = prev_intent
if(4)
var/prev_intent = owner.a_intent
owner.a_intent = INTENT_HARM
to_chat(owner, "<span class='warning'>Your arm spasms!</span>")
owner.log_message("attacked [owner.p_them()]self to a Muscle Spasm", LOG_ATTACK)
owner.ClickOn(owner)
owner.a_intent = prev_intent
if(5)
if(owner.incapacitated())
return
var/obj/item/I = owner.get_active_held_item()
var/list/turf/targets = list()
for(var/turf/T in oview(owner, 3))
targets += T
if(LAZYLEN(targets) && I)
to_chat(owner, "<span class='warning'>Your arm spasms!</span>")
owner.log_message("threw [I] due to a Muscle Spasm", LOG_ATTACK)
owner.throw_item(pick(targets))
+14
View File
@@ -69,3 +69,17 @@
/datum/status_effect/in_love/tick()
if(date)
new /obj/effect/temp_visual/love_heart/invisible(get_turf(date.loc), owner)
/datum/status_effect/throat_soothed
id = "throat_soothed"
duration = 60 SECONDS
status_type = STATUS_EFFECT_REFRESH
alert_type = null
/datum/status_effect/throat_soothed/on_apply()
. = ..()
ADD_TRAIT(owner, TRAIT_SOOTHED_THROAT, "[STATUS_EFFECT_TRAIT]_[id]")
/datum/status_effect/throat_soothed/on_remove()
. = ..()
REMOVE_TRAIT(owner, TRAIT_SOOTHED_THROAT, "[STATUS_EFFECT_TRAIT]_[id]")
@@ -64,6 +64,12 @@
owner = null
qdel(src)
/datum/status_effect/proc/refresh()
var/original_duration = initial(duration)
if(original_duration == -1)
return
duration = world.time + original_duration
//clickdelay/nextmove modifiers!
/datum/status_effect/proc/nextmove_modifier()
return 1
@@ -92,6 +98,9 @@
if(S.id == initial(S1.id) && S.status_type)
if(S.status_type == STATUS_EFFECT_REPLACE)
S.be_replaced()
else if(S.status_type == STATUS_EFFECT_REFRESH)
S.refresh()
return
else
return
var/list/arguments = args.Copy()
+870 -870
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -22,7 +22,7 @@
if(voice_activated)
flags_1 |= HEAR_1
/obj/machinery/door/password/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode)
/obj/machinery/door/password/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
. = ..()
if(!density || !voice_activated || radio_freq)
return
File diff suppressed because it is too large Load Diff
+368 -368
View File
@@ -1,368 +1,368 @@
// Status display
// (formerly Countdown timer display)
#define CHARS_PER_LINE 5
#define FONT_SIZE "5pt"
#define FONT_COLOR "#09f"
#define FONT_STYLE "Arial Black"
#define SCROLL_SPEED 2
#define SD_BLANK 0 // 0 = Blank
#define SD_EMERGENCY 1 // 1 = Emergency Shuttle timer
#define SD_MESSAGE 2 // 2 = Arbitrary message(s)
#define SD_PICTURE 3 // 3 = alert picture
#define SD_AI_EMOTE 1 // 1 = AI emoticon
#define SD_AI_BSOD 2 // 2 = Blue screen of death
/// Status display which can show images and scrolling text.
/obj/machinery/status_display
name = "status display"
desc = null
icon = 'icons/obj/status_display.dmi'
icon_state = "frame"
density = FALSE
use_power = IDLE_POWER_USE
idle_power_usage = 10
maptext_height = 26
maptext_width = 32
var/message1 = "" // message line 1
var/message2 = "" // message line 2
var/index1 // display index for scrolling messages or 0 if non-scrolling
var/index2
/// Immediately blank the display.
/obj/machinery/status_display/proc/remove_display()
cut_overlays()
if(maptext)
maptext = ""
/// Immediately change the display to the given picture.
/obj/machinery/status_display/proc/set_picture(state)
remove_display()
add_overlay(state)
/// Immediately change the display to the given two lines.
/obj/machinery/status_display/proc/update_display(line1, line2)
var/new_text = {"<div style="font-size:[FONT_SIZE];color:[FONT_COLOR];font:'[FONT_STYLE]';text-align:center;" valign="top">[line1]<br>[line2]</div>"}
if(maptext != new_text)
maptext = new_text
/// Prepare the display to marquee the given two lines.
///
/// Call with no arguments to disable.
/obj/machinery/status_display/proc/set_message(m1, m2)
if(m1)
index1 = (length(m1) > CHARS_PER_LINE)
message1 = m1
else
message1 = ""
index1 = 0
if(m2)
index2 = (length(m2) > CHARS_PER_LINE)
message2 = m2
else
message2 = ""
index2 = 0
// Timed process - performs default marquee action if so needed.
/obj/machinery/status_display/process()
if(stat & NOPOWER)
// No power, no processing.
remove_display()
return PROCESS_KILL
var/line1 = message1
if(index1)
line1 = copytext("[message1]|[message1]", index1, index1+CHARS_PER_LINE)
var/message1_len = length(message1)
index1 += SCROLL_SPEED
if(index1 > message1_len)
index1 -= message1_len
var/line2 = message2
if(index2)
line2 = copytext("[message2]|[message2]", index2, index2+CHARS_PER_LINE)
var/message2_len = length(message2)
index2 += SCROLL_SPEED
if(index2 > message2_len)
index2 -= message2_len
update_display(line1, line2)
if (!index1 && !index2)
// No marquee, no processing.
return PROCESS_KILL
/// Update the display and, if necessary, re-enable processing.
/obj/machinery/status_display/proc/update()
if (process() != PROCESS_KILL)
START_PROCESSING(SSmachines, src)
/obj/machinery/status_display/power_change()
. = ..()
update()
/obj/machinery/status_display/emp_act(severity)
. = ..()
if(stat & (NOPOWER|BROKEN) || . & EMP_PROTECT_SELF)
return
set_picture("ai_bsod")
/obj/machinery/status_display/examine(mob/user)
. = ..()
if (message1 || message2)
. += "The display says:"
if (message1)
. += "\t<tt>[html_encode(message1)]</tt>"
if (message2)
. += "\t<tt>[html_encode(message2)]</tt>"
// Helper procs for child display types.
/obj/machinery/status_display/proc/display_shuttle_status(obj/docking_port/mobile/shuttle)
if(!shuttle)
// the shuttle is missing - no processing
update_display("shutl?","")
return PROCESS_KILL
else if(shuttle.timer)
var/line1 = "-[shuttle.getModeStr()]-"
var/line2 = shuttle.getTimerStr()
if(length(line2) > CHARS_PER_LINE)
line2 = "error"
update_display(line1, line2)
else
// don't kill processing, the timer might turn back on
remove_display()
/obj/machinery/status_display/proc/examine_shuttle(mob/user, obj/docking_port/mobile/shuttle)
if (shuttle)
var/modestr = shuttle.getModeStr()
if (modestr)
if (shuttle.timer)
modestr = "<br>\t<tt>[modestr]: [shuttle.getTimerStr()]</tt>"
else
modestr = "<br>\t<tt>[modestr]</tt>"
return "The display says:<br>\t<tt>[shuttle.name]</tt>[modestr]"
else
return "The display says:<br>\t<tt>Shuttle missing!</tt>"
/// Evac display which shows shuttle timer or message set by Command.
/obj/machinery/status_display/evac
var/frequency = FREQ_STATUS_DISPLAYS
var/mode = SD_EMERGENCY
var/friendc = FALSE // track if Friend Computer mode
var/last_picture // For when Friend Computer mode is undone
/obj/machinery/status_display/evac/Initialize()
. = ..()
// register for radio system
SSradio.add_object(src, frequency)
/obj/machinery/status_display/evac/Destroy()
SSradio.remove_object(src,frequency)
return ..()
/obj/machinery/status_display/evac/process()
if(stat & NOPOWER)
// No power, no processing.
remove_display()
return PROCESS_KILL
if(friendc) //Makes all status displays except supply shuttle timer display the eye -- Urist
set_picture("ai_friend")
return PROCESS_KILL
switch(mode)
if(SD_BLANK)
remove_display()
return PROCESS_KILL
if(SD_EMERGENCY)
return display_shuttle_status(SSshuttle.emergency)
if(SD_MESSAGE)
return ..()
if(SD_PICTURE)
set_picture(last_picture)
return PROCESS_KILL
/obj/machinery/status_display/evac/examine(mob/user)
. = ..()
if(mode == SD_EMERGENCY)
. += examine_shuttle(user, SSshuttle.emergency)
else if(!message1 && !message2)
. += "The display is blank."
/obj/machinery/status_display/evac/receive_signal(datum/signal/signal)
switch(signal.data["command"])
if("blank")
mode = SD_BLANK
set_message(null, null)
if("shuttle")
mode = SD_EMERGENCY
set_message(null, null)
if("message")
mode = SD_MESSAGE
set_message(signal.data["msg1"], signal.data["msg2"])
if("alert")
mode = SD_PICTURE
last_picture = signal.data["picture_state"]
set_picture(last_picture)
if("friendcomputer")
friendc = !friendc
update()
/// Supply display which shows the status of the supply shuttle.
/obj/machinery/status_display/supply
name = "supply display"
/obj/machinery/status_display/supply/process()
if(stat & NOPOWER)
// No power, no processing.
remove_display()
return PROCESS_KILL
var/line1
var/line2
if(!SSshuttle.supply)
// Might be missing in our first update on initialize before shuttles
// have loaded. Cross our fingers that it will soon return.
line1 = "CARGO"
line2 = "shutl?"
else if(SSshuttle.supply.mode == SHUTTLE_IDLE)
if(is_station_level(SSshuttle.supply.z))
line1 = "CARGO"
line2 = "Docked"
else
line1 = "CARGO"
line2 = SSshuttle.supply.getTimerStr()
if(length(line2) > CHARS_PER_LINE)
line2 = "Error"
update_display(line1, line2)
/obj/machinery/status_display/supply/examine(mob/user)
. = ..()
var/obj/docking_port/mobile/shuttle = SSshuttle.supply
var/shuttleMsg = null
if (shuttle.mode == SHUTTLE_IDLE)
if (is_station_level(shuttle.z))
shuttleMsg = "Docked"
else
shuttleMsg = "[shuttle.getModeStr()]: [shuttle.getTimerStr()]"
if (shuttleMsg)
. += "The display says:<br>\t<tt>[shuttleMsg]</tt>"
else
. += "The display is blank."
/// General-purpose shuttle status display.
/obj/machinery/status_display/shuttle
name = "shuttle display"
var/shuttle_id
/obj/machinery/status_display/shuttle/process()
if(!shuttle_id || (stat & NOPOWER))
// No power, no processing.
remove_display()
return PROCESS_KILL
return display_shuttle_status(SSshuttle.getShuttle(shuttle_id))
/obj/machinery/status_display/shuttle/examine(mob/user)
. = ..()
if(shuttle_id)
. += examine_shuttle(user, SSshuttle.getShuttle(shuttle_id))
else
. += "The display is blank."
/obj/machinery/status_display/shuttle/vv_edit_var(var_name, var_value)
. = ..()
if(!.)
return
switch(var_name)
if("shuttle_id")
update()
/obj/machinery/status_display/shuttle/proc/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock, idnum, override)
if (port && (shuttle_id == initial(shuttle_id) || override))
shuttle_id = port.id
update()
/// Pictograph display which the AI can use to emote.
/obj/machinery/status_display/ai
name = "\improper AI display"
desc = "A small screen which the AI can use to present itself."
var/mode = SD_BLANK
var/emotion = "Neutral"
/obj/machinery/status_display/ai/Initialize()
. = ..()
GLOB.ai_status_displays.Add(src)
/obj/machinery/status_display/ai/Destroy()
GLOB.ai_status_displays.Remove(src)
. = ..()
/obj/machinery/status_display/ai/attack_ai(mob/living/silicon/ai/user)
if(isAI(user))
user.ai_statuschange()
/obj/machinery/status_display/ai/process()
if(mode == SD_BLANK || (stat & NOPOWER))
remove_display()
return PROCESS_KILL
if(mode == SD_AI_EMOTE)
switch(emotion)
if("Very Happy")
set_picture("ai_veryhappy")
if("Happy")
set_picture("ai_happy")
if("Neutral")
set_picture("ai_neutral")
if("Unsure")
set_picture("ai_unsure")
if("Confused")
set_picture("ai_confused")
if("Sad")
set_picture("ai_sad")
if("BSOD")
set_picture("ai_bsod")
if("Blank")
set_picture("ai_off")
if("Problems?")
set_picture("ai_trollface")
if("Awesome")
set_picture("ai_awesome")
if("Dorfy")
set_picture("ai_urist")
if("Thinking")
set_picture("ai_thinking")
if("Facepalm")
set_picture("ai_facepalm")
if("Friend Computer")
set_picture("ai_friend")
if("Blue Glow")
set_picture("ai_sal")
if("Red Glow")
set_picture("ai_hal")
return PROCESS_KILL
if(mode == SD_AI_BSOD)
set_picture("ai_bsod")
return PROCESS_KILL
#undef CHARS_PER_LINE
#undef FONT_SIZE
#undef FONT_COLOR
#undef FONT_STYLE
#undef SCROLL_SPEED
// Status display
// (formerly Countdown timer display)
#define CHARS_PER_LINE 5
#define FONT_SIZE "5pt"
#define FONT_COLOR "#09f"
#define FONT_STYLE "Arial Black"
#define SCROLL_SPEED 2
#define SD_BLANK 0 // 0 = Blank
#define SD_EMERGENCY 1 // 1 = Emergency Shuttle timer
#define SD_MESSAGE 2 // 2 = Arbitrary message(s)
#define SD_PICTURE 3 // 3 = alert picture
#define SD_AI_EMOTE 1 // 1 = AI emoticon
#define SD_AI_BSOD 2 // 2 = Blue screen of death
/// Status display which can show images and scrolling text.
/obj/machinery/status_display
name = "status display"
desc = null
icon = 'icons/obj/status_display.dmi'
icon_state = "frame"
density = FALSE
use_power = IDLE_POWER_USE
idle_power_usage = 10
maptext_height = 26
maptext_width = 32
var/message1 = "" // message line 1
var/message2 = "" // message line 2
var/index1 // display index for scrolling messages or 0 if non-scrolling
var/index2
/// Immediately blank the display.
/obj/machinery/status_display/proc/remove_display()
cut_overlays()
if(maptext)
maptext = ""
/// Immediately change the display to the given picture.
/obj/machinery/status_display/proc/set_picture(state)
remove_display()
add_overlay(state)
/// Immediately change the display to the given two lines.
/obj/machinery/status_display/proc/update_display(line1, line2)
var/new_text = {"<div style="font-size:[FONT_SIZE];color:[FONT_COLOR];font:'[FONT_STYLE]';text-align:center;" valign="top">[line1]<br>[line2]</div>"}
if(maptext != new_text)
maptext = new_text
/// Prepare the display to marquee the given two lines.
///
/// Call with no arguments to disable.
/obj/machinery/status_display/proc/set_message(m1, m2)
if(m1)
index1 = (length(m1) > CHARS_PER_LINE)
message1 = m1
else
message1 = ""
index1 = 0
if(m2)
index2 = (length(m2) > CHARS_PER_LINE)
message2 = m2
else
message2 = ""
index2 = 0
// Timed process - performs default marquee action if so needed.
/obj/machinery/status_display/process()
if(stat & NOPOWER)
// No power, no processing.
remove_display()
return PROCESS_KILL
var/line1 = message1
if(index1)
line1 = copytext("[message1]|[message1]", index1, index1+CHARS_PER_LINE)
var/message1_len = length(message1)
index1 += SCROLL_SPEED
if(index1 > message1_len)
index1 -= message1_len
var/line2 = message2
if(index2)
line2 = copytext("[message2]|[message2]", index2, index2+CHARS_PER_LINE)
var/message2_len = length(message2)
index2 += SCROLL_SPEED
if(index2 > message2_len)
index2 -= message2_len
update_display(line1, line2)
if (!index1 && !index2)
// No marquee, no processing.
return PROCESS_KILL
/// Update the display and, if necessary, re-enable processing.
/obj/machinery/status_display/proc/update()
if (process() != PROCESS_KILL)
START_PROCESSING(SSmachines, src)
/obj/machinery/status_display/power_change()
. = ..()
update()
/obj/machinery/status_display/emp_act(severity)
. = ..()
if(stat & (NOPOWER|BROKEN) || . & EMP_PROTECT_SELF)
return
set_picture("ai_bsod")
/obj/machinery/status_display/examine(mob/user)
. = ..()
if (message1 || message2)
. += "The display says:"
if (message1)
. += "\t<tt>[html_encode(message1)]</tt>"
if (message2)
. += "\t<tt>[html_encode(message2)]</tt>"
// Helper procs for child display types.
/obj/machinery/status_display/proc/display_shuttle_status(obj/docking_port/mobile/shuttle)
if(!shuttle)
// the shuttle is missing - no processing
update_display("shutl?","")
return PROCESS_KILL
else if(shuttle.timer)
var/line1 = "-[shuttle.getModeStr()]-"
var/line2 = shuttle.getTimerStr()
if(length(line2) > CHARS_PER_LINE)
line2 = "error"
update_display(line1, line2)
else
// don't kill processing, the timer might turn back on
remove_display()
/obj/machinery/status_display/proc/examine_shuttle(mob/user, obj/docking_port/mobile/shuttle)
if (shuttle)
var/modestr = shuttle.getModeStr()
if (modestr)
if (shuttle.timer)
modestr = "<br>\t<tt>[modestr]: [shuttle.getTimerStr()]</tt>"
else
modestr = "<br>\t<tt>[modestr]</tt>"
return "The display says:<br>\t<tt>[shuttle.name]</tt>[modestr]"
else
return "The display says:<br>\t<tt>Shuttle missing!</tt>"
/// Evac display which shows shuttle timer or message set by Command.
/obj/machinery/status_display/evac
var/frequency = FREQ_STATUS_DISPLAYS
var/mode = SD_EMERGENCY
var/friendc = FALSE // track if Friend Computer mode
var/last_picture // For when Friend Computer mode is undone
/obj/machinery/status_display/evac/Initialize()
. = ..()
// register for radio system
SSradio.add_object(src, frequency)
/obj/machinery/status_display/evac/Destroy()
SSradio.remove_object(src,frequency)
return ..()
/obj/machinery/status_display/evac/process()
if(stat & NOPOWER)
// No power, no processing.
remove_display()
return PROCESS_KILL
if(friendc) //Makes all status displays except supply shuttle timer display the eye -- Urist
set_picture("ai_friend")
return PROCESS_KILL
switch(mode)
if(SD_BLANK)
remove_display()
return PROCESS_KILL
if(SD_EMERGENCY)
return display_shuttle_status(SSshuttle.emergency)
if(SD_MESSAGE)
return ..()
if(SD_PICTURE)
set_picture(last_picture)
return PROCESS_KILL
/obj/machinery/status_display/evac/examine(mob/user)
. = ..()
if(mode == SD_EMERGENCY)
. += examine_shuttle(user, SSshuttle.emergency)
else if(!message1 && !message2)
. += "The display is blank."
/obj/machinery/status_display/evac/receive_signal(datum/signal/signal)
switch(signal.data["command"])
if("blank")
mode = SD_BLANK
set_message(null, null)
if("shuttle")
mode = SD_EMERGENCY
set_message(null, null)
if("message")
mode = SD_MESSAGE
set_message(signal.data["msg1"], signal.data["msg2"])
if("alert")
mode = SD_PICTURE
last_picture = signal.data["picture_state"]
set_picture(last_picture)
if("friendcomputer")
friendc = !friendc
update()
/// Supply display which shows the status of the supply shuttle.
/obj/machinery/status_display/supply
name = "supply display"
/obj/machinery/status_display/supply/process()
if(stat & NOPOWER)
// No power, no processing.
remove_display()
return PROCESS_KILL
var/line1
var/line2
if(!SSshuttle.supply)
// Might be missing in our first update on initialize before shuttles
// have loaded. Cross our fingers that it will soon return.
line1 = "CARGO"
line2 = "shutl?"
else if(SSshuttle.supply.mode == SHUTTLE_IDLE)
if(is_station_level(SSshuttle.supply.z))
line1 = "CARGO"
line2 = "Docked"
else
line1 = "CARGO"
line2 = SSshuttle.supply.getTimerStr()
if(length(line2) > CHARS_PER_LINE)
line2 = "Error"
update_display(line1, line2)
/obj/machinery/status_display/supply/examine(mob/user)
. = ..()
var/obj/docking_port/mobile/shuttle = SSshuttle.supply
var/shuttleMsg = null
if (shuttle.mode == SHUTTLE_IDLE)
if (is_station_level(shuttle.z))
shuttleMsg = "Docked"
else
shuttleMsg = "[shuttle.getModeStr()]: [shuttle.getTimerStr()]"
if (shuttleMsg)
. += "The display says:<br>\t<tt>[shuttleMsg]</tt>"
else
. += "The display is blank."
/// General-purpose shuttle status display.
/obj/machinery/status_display/shuttle
name = "shuttle display"
var/shuttle_id
/obj/machinery/status_display/shuttle/process()
if(!shuttle_id || (stat & NOPOWER))
// No power, no processing.
remove_display()
return PROCESS_KILL
return display_shuttle_status(SSshuttle.getShuttle(shuttle_id))
/obj/machinery/status_display/shuttle/examine(mob/user)
. = ..()
if(shuttle_id)
. += examine_shuttle(user, SSshuttle.getShuttle(shuttle_id))
else
. += "The display is blank."
/obj/machinery/status_display/shuttle/vv_edit_var(var_name, var_value)
. = ..()
if(!.)
return
switch(var_name)
if("shuttle_id")
update()
/obj/machinery/status_display/shuttle/proc/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock, idnum, override)
if (port && (shuttle_id == initial(shuttle_id) || override))
shuttle_id = port.id
update()
/// Pictograph display which the AI can use to emote.
/obj/machinery/status_display/ai
name = "\improper AI display"
desc = "A small screen which the AI can use to present itself."
var/mode = SD_BLANK
var/emotion = "Neutral"
/obj/machinery/status_display/ai/Initialize()
. = ..()
GLOB.ai_status_displays.Add(src)
/obj/machinery/status_display/ai/Destroy()
GLOB.ai_status_displays.Remove(src)
. = ..()
/obj/machinery/status_display/ai/attack_ai(mob/living/silicon/ai/user)
if(isAI(user))
user.ai_statuschange()
/obj/machinery/status_display/ai/process()
if(mode == SD_BLANK || (stat & NOPOWER))
remove_display()
return PROCESS_KILL
if(mode == SD_AI_EMOTE)
switch(emotion)
if("Very Happy")
set_picture("ai_veryhappy")
if("Happy")
set_picture("ai_happy")
if("Neutral")
set_picture("ai_neutral")
if("Unsure")
set_picture("ai_unsure")
if("Confused")
set_picture("ai_confused")
if("Sad")
set_picture("ai_sad")
if("BSOD")
set_picture("ai_bsod")
if("Blank")
set_picture("ai_off")
if("Problems?")
set_picture("ai_trollface")
if("Awesome")
set_picture("ai_awesome")
if("Dorfy")
set_picture("ai_urist")
if("Thinking")
set_picture("ai_thinking")
if("Facepalm")
set_picture("ai_facepalm")
if("Friend Computer")
set_picture("ai_friend")
if("Blue Glow")
set_picture("ai_sal")
if("Red Glow")
set_picture("ai_hal")
return PROCESS_KILL
if(mode == SD_AI_BSOD)
set_picture("ai_bsod")
return PROCESS_KILL
#undef CHARS_PER_LINE
#undef FONT_SIZE
#undef FONT_COLOR
#undef FONT_STYLE
#undef SCROLL_SPEED
+1087 -1087
View File
File diff suppressed because it is too large Load Diff
@@ -315,6 +315,11 @@
randomdir = 0
duration = 6
/obj/effect/temp_visual/desynchronizer
name = "desynchronizer field"
icon_state = "chronofield"
duration = 3
/obj/effect/temp_visual/impact_effect
icon_state = "impact_bullet"
duration = 5
+1 -1
View File
@@ -1042,7 +1042,7 @@ GLOBAL_LIST_EMPTY(PDAs)
if (ismob(loc))
var/mob/M = loc
M.show_message("<span class='userdanger'>Your [src] explodes!</span>", 1)
M.show_message("<span class='userdanger'>Your [src] explodes!</span>", MSG_VISUAL, "<span class='warning'>You hear a loud *pop*!</span>", MSG_AUDIBLE)
else
visible_message("<span class='danger'>[src] explodes!</span>", "<span class='warning'>You hear a loud *pop*!</span>")
@@ -72,12 +72,12 @@
difficulty += 2
var/datum/component/uplink/hidden_uplink = target.GetComponent(/datum/component/uplink)
if(!target.detonatable || prob(difficulty * 15) || (hidden_uplink))
U.show_message("<span class='danger'>An error flashes on your [src].</span>", 1)
U.show_message("<span class='danger'>An error flashes on your [src].</span>", MSG_VISUAL)
else
message_admins("[!is_special_character(U) ? "Non-antag " : ""][ADMIN_LOOKUPFLW(U)] triggered a PDA explosion on [target.name] at [ADMIN_VERBOSEJMP(target)].")
var/message_log = "triggered a PDA explosion on [target.name] at [AREACOORD(target)]."
U.log_message(message_log, LOG_ATTACK)
U.show_message("<span class='notice'>Success!</span>", 1)
U.show_message("<span class='notice'>Success!</span>", MSG_VISUAL)
target.explode()
else
to_chat(U, "PDA not found.")
+150 -150
View File
@@ -1,150 +1,150 @@
/obj/item/radio/intercom
name = "station intercom"
desc = "Talk through this."
icon_state = "intercom"
anchored = TRUE
w_class = WEIGHT_CLASS_BULKY
canhear_range = 2
var/number = 0
var/anyai = 1
var/mob/living/silicon/ai/ai = list()
var/last_tick //used to delay the powercheck
dog_fashion = null
var/unfastened = FALSE
/obj/item/radio/intercom/unscrewed
unfastened = TRUE
/obj/item/radio/intercom/ratvar
name = "hierophant intercom"
desc = "A modified intercom that uses the Hierophant network instead of subspace tech. Can listen to and broadcast on any frequency."
icon_state = "intercom_ratvar"
freerange = TRUE
/obj/item/radio/intercom/ratvar/attackby(obj/item/I, mob/living/user, params)
if(istype(I, /obj/item/screwdriver))
to_chat(user, "<span class='danger'>[src] is fastened to the wall with [is_servant_of_ratvar(user) ? "replicant alloy" : "some material you've never seen"], and can't be removed.</span>")
return //no unfastening!
. = ..()
/obj/item/radio/intercom/ratvar/process()
if(!istype(SSticker.mode, /datum/game_mode/clockwork_cult))
invisibility = INVISIBILITY_OBSERVER
alpha = 125
emped = TRUE
else
invisibility = initial(invisibility)
alpha = initial(alpha)
emped = FALSE
..()
/obj/item/radio/intercom/Initialize(mapload, ndir, building)
. = ..()
if(building)
setDir(ndir)
START_PROCESSING(SSobj, src)
/obj/item/radio/intercom/Destroy()
STOP_PROCESSING(SSobj, src)
return ..()
/obj/item/radio/intercom/examine(mob/user)
. = ..()
if(!unfastened)
. += "<span class='notice'>It's <b>screwed</b> and secured to the wall.</span>"
else
. += "<span class='notice'>It's <i>unscrewed</i> from the wall, and can be <b>detached</b>.</span>"
/obj/item/radio/intercom/attackby(obj/item/I, mob/living/user, params)
if(istype(I, /obj/item/screwdriver))
if(unfastened)
user.visible_message("<span class='notice'>[user] starts tightening [src]'s screws...</span>", "<span class='notice'>You start screwing in [src]...</span>")
if(I.use_tool(src, user, 30, volume=50))
user.visible_message("<span class='notice'>[user] tightens [src]'s screws!</span>", "<span class='notice'>You tighten [src]'s screws.</span>")
unfastened = FALSE
else
user.visible_message("<span class='notice'>[user] starts loosening [src]'s screws...</span>", "<span class='notice'>You start unscrewing [src]...</span>")
if(I.use_tool(src, user, 40, volume=50))
user.visible_message("<span class='notice'>[user] loosens [src]'s screws!</span>", "<span class='notice'>You unscrew [src], loosening it from the wall.</span>")
unfastened = TRUE
return
else if(istype(I, /obj/item/wrench))
if(!unfastened)
to_chat(user, "<span class='warning'>You need to unscrew [src] from the wall first!</span>")
return
user.visible_message("<span class='notice'>[user] starts unsecuring [src]...</span>", "<span class='notice'>You start unsecuring [src]...</span>")
I.play_tool_sound(src)
if(I.use_tool(src, user, 80))
user.visible_message("<span class='notice'>[user] unsecures [src]!</span>", "<span class='notice'>You detach [src] from the wall.</span>")
playsound(src, 'sound/items/deconstruct.ogg', 50, 1)
new/obj/item/wallframe/intercom(get_turf(src))
qdel(src)
return
return ..()
/obj/item/radio/intercom/attack_ai(mob/user)
interact(user)
/obj/item/radio/intercom/attack_hand(mob/user)
. = ..()
if(.)
return
interact(user)
/obj/item/radio/intercom/interact(mob/user)
..()
ui_interact(user, state = GLOB.default_state)
/obj/item/radio/intercom/can_receive(freq, level)
if(!on)
return FALSE
if(wires.is_cut(WIRE_RX))
return FALSE
if(!(0 in level))
var/turf/position = get_turf(src)
if(isnull(position) || !(position.z in level))
return FALSE
if(!src.listening)
return FALSE
if(freq == FREQ_SYNDICATE)
if(!(src.syndie))
return FALSE//Prevents broadcast of messages over devices lacking the encryption
return TRUE
/obj/item/radio/intercom/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans, message_mode)
. = ..()
if (message_mode == MODE_INTERCOM)
return // Avoid hearing the same thing twice
if(!anyai && !(speaker in ai))
return
..()
/obj/item/radio/intercom/process()
if(((world.timeofday - last_tick) > 30) || ((world.timeofday - last_tick) < 0))
last_tick = world.timeofday
var/area/A = get_area(src)
if(!A || emped)
on = FALSE
else
on = A.powered(EQUIP) // set "on" to the power status
if(!on)
icon_state = "intercom-p"
else
icon_state = initial(icon_state)
/obj/item/radio/intercom/add_blood_DNA(list/blood_dna)
return FALSE
//Created through the autolathe or through deconstructing intercoms. Can be applied to wall to make a new intercom on it!
/obj/item/wallframe/intercom
name = "intercom frame"
desc = "A ready-to-go intercom. Just slap it on a wall and screw it in!"
icon_state = "intercom"
result_path = /obj/item/radio/intercom/unscrewed
pixel_shift = 29
inverse = TRUE
materials = list(MAT_METAL = 75, MAT_GLASS = 25)
/obj/item/radio/intercom
name = "station intercom"
desc = "Talk through this."
icon_state = "intercom"
anchored = TRUE
w_class = WEIGHT_CLASS_BULKY
canhear_range = 2
var/number = 0
var/anyai = 1
var/mob/living/silicon/ai/ai = list()
var/last_tick //used to delay the powercheck
dog_fashion = null
var/unfastened = FALSE
/obj/item/radio/intercom/unscrewed
unfastened = TRUE
/obj/item/radio/intercom/ratvar
name = "hierophant intercom"
desc = "A modified intercom that uses the Hierophant network instead of subspace tech. Can listen to and broadcast on any frequency."
icon_state = "intercom_ratvar"
freerange = TRUE
/obj/item/radio/intercom/ratvar/attackby(obj/item/I, mob/living/user, params)
if(istype(I, /obj/item/screwdriver))
to_chat(user, "<span class='danger'>[src] is fastened to the wall with [is_servant_of_ratvar(user) ? "replicant alloy" : "some material you've never seen"], and can't be removed.</span>")
return //no unfastening!
. = ..()
/obj/item/radio/intercom/ratvar/process()
if(!istype(SSticker.mode, /datum/game_mode/clockwork_cult))
invisibility = INVISIBILITY_OBSERVER
alpha = 125
emped = TRUE
else
invisibility = initial(invisibility)
alpha = initial(alpha)
emped = FALSE
..()
/obj/item/radio/intercom/Initialize(mapload, ndir, building)
. = ..()
if(building)
setDir(ndir)
START_PROCESSING(SSobj, src)
/obj/item/radio/intercom/Destroy()
STOP_PROCESSING(SSobj, src)
return ..()
/obj/item/radio/intercom/examine(mob/user)
. = ..()
if(!unfastened)
. += "<span class='notice'>It's <b>screwed</b> and secured to the wall.</span>"
else
. += "<span class='notice'>It's <i>unscrewed</i> from the wall, and can be <b>detached</b>.</span>"
/obj/item/radio/intercom/attackby(obj/item/I, mob/living/user, params)
if(istype(I, /obj/item/screwdriver))
if(unfastened)
user.visible_message("<span class='notice'>[user] starts tightening [src]'s screws...</span>", "<span class='notice'>You start screwing in [src]...</span>")
if(I.use_tool(src, user, 30, volume=50))
user.visible_message("<span class='notice'>[user] tightens [src]'s screws!</span>", "<span class='notice'>You tighten [src]'s screws.</span>")
unfastened = FALSE
else
user.visible_message("<span class='notice'>[user] starts loosening [src]'s screws...</span>", "<span class='notice'>You start unscrewing [src]...</span>")
if(I.use_tool(src, user, 40, volume=50))
user.visible_message("<span class='notice'>[user] loosens [src]'s screws!</span>", "<span class='notice'>You unscrew [src], loosening it from the wall.</span>")
unfastened = TRUE
return
else if(istype(I, /obj/item/wrench))
if(!unfastened)
to_chat(user, "<span class='warning'>You need to unscrew [src] from the wall first!</span>")
return
user.visible_message("<span class='notice'>[user] starts unsecuring [src]...</span>", "<span class='notice'>You start unsecuring [src]...</span>")
I.play_tool_sound(src)
if(I.use_tool(src, user, 80))
user.visible_message("<span class='notice'>[user] unsecures [src]!</span>", "<span class='notice'>You detach [src] from the wall.</span>")
playsound(src, 'sound/items/deconstruct.ogg', 50, 1)
new/obj/item/wallframe/intercom(get_turf(src))
qdel(src)
return
return ..()
/obj/item/radio/intercom/attack_ai(mob/user)
interact(user)
/obj/item/radio/intercom/attack_hand(mob/user)
. = ..()
if(.)
return
interact(user)
/obj/item/radio/intercom/interact(mob/user)
..()
ui_interact(user, state = GLOB.default_state)
/obj/item/radio/intercom/can_receive(freq, level)
if(!on)
return FALSE
if(wires.is_cut(WIRE_RX))
return FALSE
if(!(0 in level))
var/turf/position = get_turf(src)
if(isnull(position) || !(position.z in level))
return FALSE
if(!src.listening)
return FALSE
if(freq == FREQ_SYNDICATE)
if(!(src.syndie))
return FALSE//Prevents broadcast of messages over devices lacking the encryption
return TRUE
/obj/item/radio/intercom/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
. = ..()
if (message_mode == MODE_INTERCOM)
return // Avoid hearing the same thing twice
if(!anyai && !(speaker in ai))
return
..()
/obj/item/radio/intercom/process()
if(((world.timeofday - last_tick) > 30) || ((world.timeofday - last_tick) < 0))
last_tick = world.timeofday
var/area/A = get_area(src)
if(!A || emped)
on = FALSE
else
on = A.powered(EQUIP) // set "on" to the power status
if(!on)
icon_state = "intercom-p"
else
icon_state = initial(icon_state)
/obj/item/radio/intercom/add_blood_DNA(list/blood_dna)
return FALSE
//Created through the autolathe or through deconstructing intercoms. Can be applied to wall to make a new intercom on it!
/obj/item/wallframe/intercom
name = "intercom frame"
desc = "A ready-to-go intercom. Just slap it on a wall and screw it in!"
icon_state = "intercom"
result_path = /obj/item/radio/intercom/unscrewed
pixel_shift = 29
inverse = TRUE
materials = list(MAT_METAL = 75, MAT_GLASS = 25)
+428 -428
View File
@@ -1,428 +1,428 @@
/obj/item/radio
icon = 'icons/obj/radio.dmi'
name = "station bounced radio"
icon_state = "walkietalkie"
item_state = "walkietalkie"
desc = "A basic handheld radio that communicates with local telecommunication networks."
dog_fashion = /datum/dog_fashion/back
flags_1 = CONDUCT_1 | HEAR_1
slot_flags = ITEM_SLOT_BELT
throw_speed = 3
throw_range = 7
w_class = WEIGHT_CLASS_SMALL
materials = list(MAT_METAL=75, MAT_GLASS=25)
obj_flags = USES_TGUI
var/on = TRUE
var/frequency = FREQ_COMMON
var/canhear_range = 3 // The range around the radio in which mobs can hear what it receives.
var/emped = 0 // Tracks the number of EMPs currently stacked.
var/broadcasting = FALSE // Whether the radio will transmit dialogue it hears nearby.
var/listening = TRUE // Whether the radio is currently receiving.
var/prison_radio = FALSE // If true, the transmit wire starts cut.
var/unscrewed = FALSE // Whether wires are accessible. Toggleable by screwdrivering.
var/freerange = FALSE // If true, the radio has access to the full spectrum.
var/subspace_transmission = FALSE // If true, the radio transmits and receives on subspace exclusively.
var/subspace_switchable = FALSE // If true, subspace_transmission can be toggled at will.
var/freqlock = FALSE // Frequency lock to stop the user from untuning specialist radios.
var/use_command = FALSE // If true, broadcasts will be large and BOLD.
var/command = FALSE // If true, use_command can be toggled at will.
// Encryption key handling
var/obj/item/encryptionkey/keyslot
var/translate_binary = FALSE // If true, can hear the special binary channel.
var/independent = FALSE // If true, can say/hear on the special CentCom channel.
var/syndie = FALSE // If true, hears all well-known channels automatically, and can say/hear on the Syndicate channel.
var/list/channels = list() // Map from name (see communications.dm) to on/off. First entry is current department (:h).
var/list/secure_radio_connections
var/const/FREQ_LISTENING = 1
//FREQ_BROADCASTING = 2
/obj/item/radio/suicide_act(mob/living/user)
user.visible_message("<span class='suicide'>[user] starts bouncing [src] off [user.p_their()] head! It looks like [user.p_theyre()] trying to commit suicide!</span>")
return BRUTELOSS
/obj/item/radio/proc/set_frequency(new_frequency)
SEND_SIGNAL(src, COMSIG_RADIO_NEW_FREQUENCY, args)
remove_radio(src, frequency)
frequency = add_radio(src, new_frequency)
/obj/item/radio/proc/recalculateChannels()
channels = list()
translate_binary = FALSE
syndie = FALSE
independent = FALSE
if(keyslot)
for(var/ch_name in keyslot.channels)
if(!(ch_name in channels))
channels[ch_name] = keyslot.channels[ch_name]
if(keyslot.translate_binary)
translate_binary = TRUE
if(keyslot.syndie)
syndie = TRUE
if(keyslot.independent)
independent = TRUE
for(var/ch_name in channels)
secure_radio_connections[ch_name] = add_radio(src, GLOB.radiochannels[ch_name])
/obj/item/radio/proc/make_syndie() // Turns normal radios into Syndicate radios!
qdel(keyslot)
keyslot = new /obj/item/encryptionkey/syndicate
syndie = 1
recalculateChannels()
/obj/item/radio/Destroy()
remove_radio_all(src) //Just to be sure
QDEL_NULL(wires)
QDEL_NULL(keyslot)
return ..()
/obj/item/radio/Initialize()
wires = new /datum/wires/radio(src)
if(prison_radio)
wires.cut(WIRE_TX) // OH GOD WHY
secure_radio_connections = new
. = ..()
frequency = sanitize_frequency(frequency, freerange)
set_frequency(frequency)
for(var/ch_name in channels)
secure_radio_connections[ch_name] = add_radio(src, GLOB.radiochannels[ch_name])
/obj/item/radio/ComponentInitialize()
. = ..()
AddComponent(/datum/component/empprotection, EMP_PROTECT_WIRES)
/obj/item/radio/interact(mob/user)
if(unscrewed && !isAI(user))
wires.interact(user)
add_fingerprint(user)
else
..()
/obj/item/radio/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.inventory_state)
. = ..()
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "radio", name, 370, 220 + channels.len * 22, master_ui, state)
ui.open()
/obj/item/radio/ui_data(mob/user)
var/list/data = list()
data["broadcasting"] = broadcasting
data["listening"] = listening
data["frequency"] = frequency
data["minFrequency"] = freerange ? MIN_FREE_FREQ : MIN_FREQ
data["maxFrequency"] = freerange ? MAX_FREE_FREQ : MAX_FREQ
data["freqlock"] = freqlock
data["channels"] = list()
for(var/channel in channels)
data["channels"][channel] = channels[channel] & FREQ_LISTENING
data["command"] = command
data["useCommand"] = use_command
data["subspace"] = subspace_transmission
data["subspaceSwitchable"] = subspace_switchable
data["headset"] = istype(src, /obj/item/radio/headset)
return data
/obj/item/radio/ui_act(action, params, datum/tgui/ui)
if(..())
return
switch(action)
if("frequency")
if(freqlock)
return
var/tune = params["tune"]
var/adjust = text2num(params["adjust"])
if(tune == "input")
var/min = format_frequency(freerange ? MIN_FREE_FREQ : MIN_FREQ)
var/max = format_frequency(freerange ? MAX_FREE_FREQ : MAX_FREQ)
tune = input("Tune frequency ([min]-[max]):", name, format_frequency(frequency)) as null|num
if(!isnull(tune) && !..())
if (tune < MIN_FREE_FREQ && tune <= MAX_FREE_FREQ / 10)
// allow typing 144.7 to get 1447
tune *= 10
. = TRUE
else if(adjust)
tune = frequency + adjust * 10
. = TRUE
else if(text2num(tune) != null)
tune = tune * 10
. = TRUE
if(.)
set_frequency(sanitize_frequency(tune, freerange))
if("listen")
listening = !listening
. = TRUE
if("broadcast")
broadcasting = !broadcasting
. = TRUE
if("channel")
var/channel = params["channel"]
if(!(channel in channels))
return
if(channels[channel] & FREQ_LISTENING)
channels[channel] &= ~FREQ_LISTENING
else
channels[channel] |= FREQ_LISTENING
. = TRUE
if("command")
use_command = !use_command
. = TRUE
if("subspace")
if(subspace_switchable)
subspace_transmission = !subspace_transmission
if(!subspace_transmission)
channels = list()
else
recalculateChannels()
. = TRUE
/obj/item/radio/talk_into(atom/movable/M, message, channel, list/spans, datum/language/language)
if(!spans)
spans = list(M.speech_span)
if(!language)
language = M.get_default_language()
INVOKE_ASYNC(src, .proc/talk_into_impl, M, message, channel, spans.Copy(), language)
return ITALICS | REDUCE_RANGE
/obj/item/radio/proc/talk_into_impl(atom/movable/M, message, channel, list/spans, datum/language/language)
if(!on)
return // the device has to be on
if(!M || !message)
return
if(wires.is_cut(WIRE_TX)) // Permacell and otherwise tampered-with radios
return
if(!M.IsVocal())
return
if(use_command)
spans |= SPAN_COMMAND
/*
Roughly speaking, radios attempt to make a subspace transmission (which
is received, processed, and rebroadcast by the telecomms satellite) and
if that fails, they send a mundane radio transmission.
Headsets cannot send/receive mundane transmissions, only subspace.
Syndicate radios can hear transmissions on all well-known frequencies.
CentCom radios can hear the CentCom frequency no matter what.
*/
// From the channel, determine the frequency and get a reference to it.
var/freq
if(channel && channels && channels.len > 0)
if(channel == MODE_DEPARTMENT)
channel = channels[1]
freq = secure_radio_connections[channel]
if (!channels[channel]) // if the channel is turned off, don't broadcast
return
else
freq = frequency
channel = null
// Nearby active jammers severely gibberish the message
var/turf/position = get_turf(src)
for(var/obj/item/jammer/jammer in GLOB.active_jammers)
var/turf/jammer_turf = get_turf(jammer)
if(position.z == jammer_turf.z && (get_dist(position, jammer_turf) < jammer.range))
message = Gibberish(message,100)
break
// Determine the identity information which will be attached to the signal.
var/atom/movable/virtualspeaker/speaker = new(null, M, src)
// Construct the signal
var/datum/signal/subspace/vocal/signal = new(src, freq, speaker, language, message, spans)
// Independent radios, on the CentCom frequency, reach all independent radios
if (independent && (freq == FREQ_CENTCOM || freq == FREQ_CTF_RED || freq == FREQ_CTF_BLUE))
signal.data["compression"] = 0
signal.transmission_method = TRANSMISSION_SUPERSPACE
signal.levels = list(0) // reaches all Z-levels
signal.broadcast()
return
// All radios make an attempt to use the subspace system first
signal.send_to_receivers()
// If the radio is subspace-only, that's all it can do
if (subspace_transmission)
return
// Non-subspace radios will check in a couple of seconds, and if the signal
// was never received, send a mundane broadcast (no headsets).
addtimer(CALLBACK(src, .proc/backup_transmission, signal), 20)
/obj/item/radio/proc/backup_transmission(datum/signal/subspace/vocal/signal)
var/turf/T = get_turf(src)
if (signal.data["done"] && (T.z in signal.levels))
return
// Okay, the signal was never processed, send a mundane broadcast.
signal.data["compression"] = 0
signal.transmission_method = TRANSMISSION_RADIO
signal.levels = list(T.z)
signal.broadcast()
/obj/item/radio/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode)
. = ..()
if(radio_freq || !broadcasting || get_dist(src, speaker) > canhear_range)
return
if(message_mode == MODE_WHISPER || message_mode == MODE_WHISPER_CRIT)
// radios don't pick up whispers very well
raw_message = stars(raw_message)
else if(message_mode == MODE_L_HAND || message_mode == MODE_R_HAND)
// try to avoid being heard double
if (loc == speaker && ismob(speaker))
var/mob/M = speaker
var/idx = M.get_held_index_of_item(src)
// left hands are odd slots
if (idx && (idx % 2) == (message_mode == MODE_L_HAND))
return
talk_into(speaker, raw_message, , spans, language=message_language)
// Checks if this radio can receive on the given frequency.
/obj/item/radio/proc/can_receive(freq, level)
// deny checks
if (!on || !listening || wires.is_cut(WIRE_RX))
return FALSE
if (freq == FREQ_SYNDICATE && !syndie)
return FALSE
if (freq == FREQ_CENTCOM)
return independent // hard-ignores the z-level check
if (!(0 in level))
var/turf/position = get_turf(src)
if(!position || !(position.z in level))
return FALSE
// allow checks: are we listening on that frequency?
if (freq == frequency)
return TRUE
for(var/ch_name in channels)
if(channels[ch_name] & FREQ_LISTENING)
//the GLOB.radiochannels list is located in communications.dm
if(GLOB.radiochannels[ch_name] == text2num(freq) || syndie)
return TRUE
return FALSE
/obj/item/radio/examine(mob/user)
. = ..()
if (unscrewed)
. += "<span class='notice'>It can be attached and modified.</span>"
else
. += "<span class='notice'>It cannot be modified or attached.</span>"
/obj/item/radio/attackby(obj/item/W, mob/user, params)
add_fingerprint(user)
if(istype(W, /obj/item/screwdriver))
unscrewed = !unscrewed
if(unscrewed)
to_chat(user, "<span class='notice'>The radio can now be attached and modified!</span>")
else
to_chat(user, "<span class='notice'>The radio can no longer be modified or attached!</span>")
else
return ..()
/obj/item/radio/emp_act(severity)
. = ..()
if (. & EMP_PROTECT_SELF)
return
emped++ //There's been an EMP; better count it
var/curremp = emped //Remember which EMP this was
if (listening && ismob(loc)) // if the radio is turned on and on someone's person they notice
to_chat(loc, "<span class='warning'>\The [src] overloads.</span>")
broadcasting = FALSE
listening = FALSE
for (var/ch_name in channels)
channels[ch_name] = 0
on = FALSE
spawn(200)
if(emped == curremp) //Don't fix it if it's been EMP'd again
emped = 0
if (!istype(src, /obj/item/radio/intercom)) // intercoms will turn back on on their own
on = TRUE
///////////////////////////////
//////////Borg Radios//////////
///////////////////////////////
//Giving borgs their own radio to have some more room to work with -Sieve
/obj/item/radio/borg
name = "cyborg radio"
subspace_switchable = TRUE
dog_fashion = null
/obj/item/radio/borg/Initialize(mapload)
. = ..()
/obj/item/radio/borg/syndicate
syndie = 1
keyslot = new /obj/item/encryptionkey/syndicate
/obj/item/radio/borg/syndicate/Initialize()
. = ..()
set_frequency(FREQ_SYNDICATE)
/obj/item/radio/borg/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/screwdriver))
if(keyslot)
for(var/ch_name in channels)
SSradio.remove_object(src, GLOB.radiochannels[ch_name])
secure_radio_connections[ch_name] = null
if(keyslot)
var/turf/T = get_turf(user)
if(T)
keyslot.forceMove(T)
keyslot = null
recalculateChannels()
to_chat(user, "<span class='notice'>You pop out the encryption key in the radio.</span>")
else
to_chat(user, "<span class='warning'>This radio doesn't have any encryption keys!</span>")
else if(istype(W, /obj/item/encryptionkey/))
if(keyslot)
to_chat(user, "<span class='warning'>The radio can't hold another key!</span>")
return
if(!keyslot)
if(!user.transferItemToLoc(W, src))
return
keyslot = W
recalculateChannels()
/obj/item/radio/off // Station bounced radios, their only difference is spawning with the speakers off, this was made to help the lag.
listening = 0 // And it's nice to have a subtype too for future features.
dog_fashion = /datum/dog_fashion/back
/obj/item/radio/internal
var/obj/item/implant/radio/implant
/obj/item/radio/internal/Initialize(mapload, obj/item/implant/radio/_implant)
. = ..()
implant = _implant
/obj/item/radio/internal/Destroy()
if(implant?.imp_in)
qdel(implant)
else
return ..()
/obj/item/radio
icon = 'icons/obj/radio.dmi'
name = "station bounced radio"
icon_state = "walkietalkie"
item_state = "walkietalkie"
desc = "A basic handheld radio that communicates with local telecommunication networks."
dog_fashion = /datum/dog_fashion/back
flags_1 = CONDUCT_1 | HEAR_1
slot_flags = ITEM_SLOT_BELT
throw_speed = 3
throw_range = 7
w_class = WEIGHT_CLASS_SMALL
materials = list(MAT_METAL=75, MAT_GLASS=25)
obj_flags = USES_TGUI
var/on = TRUE
var/frequency = FREQ_COMMON
var/canhear_range = 3 // The range around the radio in which mobs can hear what it receives.
var/emped = 0 // Tracks the number of EMPs currently stacked.
var/broadcasting = FALSE // Whether the radio will transmit dialogue it hears nearby.
var/listening = TRUE // Whether the radio is currently receiving.
var/prison_radio = FALSE // If true, the transmit wire starts cut.
var/unscrewed = FALSE // Whether wires are accessible. Toggleable by screwdrivering.
var/freerange = FALSE // If true, the radio has access to the full spectrum.
var/subspace_transmission = FALSE // If true, the radio transmits and receives on subspace exclusively.
var/subspace_switchable = FALSE // If true, subspace_transmission can be toggled at will.
var/freqlock = FALSE // Frequency lock to stop the user from untuning specialist radios.
var/use_command = FALSE // If true, broadcasts will be large and BOLD.
var/command = FALSE // If true, use_command can be toggled at will.
// Encryption key handling
var/obj/item/encryptionkey/keyslot
var/translate_binary = FALSE // If true, can hear the special binary channel.
var/independent = FALSE // If true, can say/hear on the special CentCom channel.
var/syndie = FALSE // If true, hears all well-known channels automatically, and can say/hear on the Syndicate channel.
var/list/channels = list() // Map from name (see communications.dm) to on/off. First entry is current department (:h).
var/list/secure_radio_connections
var/const/FREQ_LISTENING = 1
//FREQ_BROADCASTING = 2
/obj/item/radio/suicide_act(mob/living/user)
user.visible_message("<span class='suicide'>[user] starts bouncing [src] off [user.p_their()] head! It looks like [user.p_theyre()] trying to commit suicide!</span>")
return BRUTELOSS
/obj/item/radio/proc/set_frequency(new_frequency)
SEND_SIGNAL(src, COMSIG_RADIO_NEW_FREQUENCY, args)
remove_radio(src, frequency)
frequency = add_radio(src, new_frequency)
/obj/item/radio/proc/recalculateChannels()
channels = list()
translate_binary = FALSE
syndie = FALSE
independent = FALSE
if(keyslot)
for(var/ch_name in keyslot.channels)
if(!(ch_name in channels))
channels[ch_name] = keyslot.channels[ch_name]
if(keyslot.translate_binary)
translate_binary = TRUE
if(keyslot.syndie)
syndie = TRUE
if(keyslot.independent)
independent = TRUE
for(var/ch_name in channels)
secure_radio_connections[ch_name] = add_radio(src, GLOB.radiochannels[ch_name])
/obj/item/radio/proc/make_syndie() // Turns normal radios into Syndicate radios!
qdel(keyslot)
keyslot = new /obj/item/encryptionkey/syndicate
syndie = 1
recalculateChannels()
/obj/item/radio/Destroy()
remove_radio_all(src) //Just to be sure
QDEL_NULL(wires)
QDEL_NULL(keyslot)
return ..()
/obj/item/radio/Initialize()
wires = new /datum/wires/radio(src)
if(prison_radio)
wires.cut(WIRE_TX) // OH GOD WHY
secure_radio_connections = new
. = ..()
frequency = sanitize_frequency(frequency, freerange)
set_frequency(frequency)
for(var/ch_name in channels)
secure_radio_connections[ch_name] = add_radio(src, GLOB.radiochannels[ch_name])
/obj/item/radio/ComponentInitialize()
. = ..()
AddComponent(/datum/component/empprotection, EMP_PROTECT_WIRES)
/obj/item/radio/interact(mob/user)
if(unscrewed && !isAI(user))
wires.interact(user)
add_fingerprint(user)
else
..()
/obj/item/radio/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.inventory_state)
. = ..()
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "radio", name, 370, 220 + channels.len * 22, master_ui, state)
ui.open()
/obj/item/radio/ui_data(mob/user)
var/list/data = list()
data["broadcasting"] = broadcasting
data["listening"] = listening
data["frequency"] = frequency
data["minFrequency"] = freerange ? MIN_FREE_FREQ : MIN_FREQ
data["maxFrequency"] = freerange ? MAX_FREE_FREQ : MAX_FREQ
data["freqlock"] = freqlock
data["channels"] = list()
for(var/channel in channels)
data["channels"][channel] = channels[channel] & FREQ_LISTENING
data["command"] = command
data["useCommand"] = use_command
data["subspace"] = subspace_transmission
data["subspaceSwitchable"] = subspace_switchable
data["headset"] = istype(src, /obj/item/radio/headset)
return data
/obj/item/radio/ui_act(action, params, datum/tgui/ui)
if(..())
return
switch(action)
if("frequency")
if(freqlock)
return
var/tune = params["tune"]
var/adjust = text2num(params["adjust"])
if(tune == "input")
var/min = format_frequency(freerange ? MIN_FREE_FREQ : MIN_FREQ)
var/max = format_frequency(freerange ? MAX_FREE_FREQ : MAX_FREQ)
tune = input("Tune frequency ([min]-[max]):", name, format_frequency(frequency)) as null|num
if(!isnull(tune) && !..())
if (tune < MIN_FREE_FREQ && tune <= MAX_FREE_FREQ / 10)
// allow typing 144.7 to get 1447
tune *= 10
. = TRUE
else if(adjust)
tune = frequency + adjust * 10
. = TRUE
else if(text2num(tune) != null)
tune = tune * 10
. = TRUE
if(.)
set_frequency(sanitize_frequency(tune, freerange))
if("listen")
listening = !listening
. = TRUE
if("broadcast")
broadcasting = !broadcasting
. = TRUE
if("channel")
var/channel = params["channel"]
if(!(channel in channels))
return
if(channels[channel] & FREQ_LISTENING)
channels[channel] &= ~FREQ_LISTENING
else
channels[channel] |= FREQ_LISTENING
. = TRUE
if("command")
use_command = !use_command
. = TRUE
if("subspace")
if(subspace_switchable)
subspace_transmission = !subspace_transmission
if(!subspace_transmission)
channels = list()
else
recalculateChannels()
. = TRUE
/obj/item/radio/talk_into(atom/movable/M, message, channel, list/spans, datum/language/language)
if(!spans)
spans = list(M.speech_span)
if(!language)
language = M.get_default_language()
INVOKE_ASYNC(src, .proc/talk_into_impl, M, message, channel, spans.Copy(), language)
return ITALICS | REDUCE_RANGE
/obj/item/radio/proc/talk_into_impl(atom/movable/M, message, channel, list/spans, datum/language/language)
if(!on)
return // the device has to be on
if(!M || !message)
return
if(wires.is_cut(WIRE_TX)) // Permacell and otherwise tampered-with radios
return
if(!M.IsVocal())
return
if(use_command)
spans |= SPAN_COMMAND
/*
Roughly speaking, radios attempt to make a subspace transmission (which
is received, processed, and rebroadcast by the telecomms satellite) and
if that fails, they send a mundane radio transmission.
Headsets cannot send/receive mundane transmissions, only subspace.
Syndicate radios can hear transmissions on all well-known frequencies.
CentCom radios can hear the CentCom frequency no matter what.
*/
// From the channel, determine the frequency and get a reference to it.
var/freq
if(channel && channels && channels.len > 0)
if(channel == MODE_DEPARTMENT)
channel = channels[1]
freq = secure_radio_connections[channel]
if (!channels[channel]) // if the channel is turned off, don't broadcast
return
else
freq = frequency
channel = null
// Nearby active jammers severely gibberish the message
var/turf/position = get_turf(src)
for(var/obj/item/jammer/jammer in GLOB.active_jammers)
var/turf/jammer_turf = get_turf(jammer)
if(position.z == jammer_turf.z && (get_dist(position, jammer_turf) < jammer.range))
message = Gibberish(message,100)
break
// Determine the identity information which will be attached to the signal.
var/atom/movable/virtualspeaker/speaker = new(null, M, src)
// Construct the signal
var/datum/signal/subspace/vocal/signal = new(src, freq, speaker, language, message, spans)
// Independent radios, on the CentCom frequency, reach all independent radios
if (independent && (freq == FREQ_CENTCOM || freq == FREQ_CTF_RED || freq == FREQ_CTF_BLUE))
signal.data["compression"] = 0
signal.transmission_method = TRANSMISSION_SUPERSPACE
signal.levels = list(0) // reaches all Z-levels
signal.broadcast()
return
// All radios make an attempt to use the subspace system first
signal.send_to_receivers()
// If the radio is subspace-only, that's all it can do
if (subspace_transmission)
return
// Non-subspace radios will check in a couple of seconds, and if the signal
// was never received, send a mundane broadcast (no headsets).
addtimer(CALLBACK(src, .proc/backup_transmission, signal), 20)
/obj/item/radio/proc/backup_transmission(datum/signal/subspace/vocal/signal)
var/turf/T = get_turf(src)
if (signal.data["done"] && (T.z in signal.levels))
return
// Okay, the signal was never processed, send a mundane broadcast.
signal.data["compression"] = 0
signal.transmission_method = TRANSMISSION_RADIO
signal.levels = list(T.z)
signal.broadcast()
/obj/item/radio/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
. = ..()
if(radio_freq || !broadcasting || get_dist(src, speaker) > canhear_range)
return
if(message_mode == MODE_WHISPER || message_mode == MODE_WHISPER_CRIT)
// radios don't pick up whispers very well
raw_message = stars(raw_message)
else if(message_mode == MODE_L_HAND || message_mode == MODE_R_HAND)
// try to avoid being heard double
if (loc == speaker && ismob(speaker))
var/mob/M = speaker
var/idx = M.get_held_index_of_item(src)
// left hands are odd slots
if (idx && (idx % 2) == (message_mode == MODE_L_HAND))
return
talk_into(speaker, raw_message, , spans, language=message_language)
// Checks if this radio can receive on the given frequency.
/obj/item/radio/proc/can_receive(freq, level)
// deny checks
if (!on || !listening || wires.is_cut(WIRE_RX))
return FALSE
if (freq == FREQ_SYNDICATE && !syndie)
return FALSE
if (freq == FREQ_CENTCOM)
return independent // hard-ignores the z-level check
if (!(0 in level))
var/turf/position = get_turf(src)
if(!position || !(position.z in level))
return FALSE
// allow checks: are we listening on that frequency?
if (freq == frequency)
return TRUE
for(var/ch_name in channels)
if(channels[ch_name] & FREQ_LISTENING)
//the GLOB.radiochannels list is located in communications.dm
if(GLOB.radiochannels[ch_name] == text2num(freq) || syndie)
return TRUE
return FALSE
/obj/item/radio/examine(mob/user)
. = ..()
if (unscrewed)
. += "<span class='notice'>It can be attached and modified.</span>"
else
. += "<span class='notice'>It cannot be modified or attached.</span>"
/obj/item/radio/attackby(obj/item/W, mob/user, params)
add_fingerprint(user)
if(istype(W, /obj/item/screwdriver))
unscrewed = !unscrewed
if(unscrewed)
to_chat(user, "<span class='notice'>The radio can now be attached and modified!</span>")
else
to_chat(user, "<span class='notice'>The radio can no longer be modified or attached!</span>")
else
return ..()
/obj/item/radio/emp_act(severity)
. = ..()
if (. & EMP_PROTECT_SELF)
return
emped++ //There's been an EMP; better count it
var/curremp = emped //Remember which EMP this was
if (listening && ismob(loc)) // if the radio is turned on and on someone's person they notice
to_chat(loc, "<span class='warning'>\The [src] overloads.</span>")
broadcasting = FALSE
listening = FALSE
for (var/ch_name in channels)
channels[ch_name] = 0
on = FALSE
spawn(200)
if(emped == curremp) //Don't fix it if it's been EMP'd again
emped = 0
if (!istype(src, /obj/item/radio/intercom)) // intercoms will turn back on on their own
on = TRUE
///////////////////////////////
//////////Borg Radios//////////
///////////////////////////////
//Giving borgs their own radio to have some more room to work with -Sieve
/obj/item/radio/borg
name = "cyborg radio"
subspace_switchable = TRUE
dog_fashion = null
/obj/item/radio/borg/Initialize(mapload)
. = ..()
/obj/item/radio/borg/syndicate
syndie = 1
keyslot = new /obj/item/encryptionkey/syndicate
/obj/item/radio/borg/syndicate/Initialize()
. = ..()
set_frequency(FREQ_SYNDICATE)
/obj/item/radio/borg/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/screwdriver))
if(keyslot)
for(var/ch_name in channels)
SSradio.remove_object(src, GLOB.radiochannels[ch_name])
secure_radio_connections[ch_name] = null
if(keyslot)
var/turf/T = get_turf(user)
if(T)
keyslot.forceMove(T)
keyslot = null
recalculateChannels()
to_chat(user, "<span class='notice'>You pop out the encryption key in the radio.</span>")
else
to_chat(user, "<span class='warning'>This radio doesn't have any encryption keys!</span>")
else if(istype(W, /obj/item/encryptionkey/))
if(keyslot)
to_chat(user, "<span class='warning'>The radio can't hold another key!</span>")
return
if(!keyslot)
if(!user.transferItemToLoc(W, src))
return
keyslot = W
recalculateChannels()
/obj/item/radio/off // Station bounced radios, their only difference is spawning with the speakers off, this was made to help the lag.
listening = 0 // And it's nice to have a subtype too for future features.
dog_fashion = /datum/dog_fashion/back
/obj/item/radio/internal
var/obj/item/implant/radio/implant
/obj/item/radio/internal/Initialize(mapload, obj/item/implant/radio/_implant)
. = ..()
implant = _implant
/obj/item/radio/internal/Destroy()
if(implant?.imp_in)
qdel(implant)
else
return ..()
+290 -290
View File
@@ -1,290 +1,290 @@
/obj/item/taperecorder
name = "universal recorder"
desc = "A device that can record to cassette tapes, and play them. It automatically translates the content in playback."
icon = 'icons/obj/device.dmi'
icon_state = "taperecorder_empty"
item_state = "analyzer"
lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
w_class = WEIGHT_CLASS_SMALL
flags_1 = HEAR_1
slot_flags = ITEM_SLOT_BELT
materials = list(MAT_METAL=60, MAT_GLASS=30)
force = 2
throwforce = 0
var/recording = 0
var/playing = 0
var/playsleepseconds = 0
var/obj/item/tape/mytape
var/starting_tape_type = /obj/item/tape/random
var/open_panel = 0
var/canprint = 1
/obj/item/taperecorder/Initialize(mapload)
. = ..()
if(starting_tape_type)
mytape = new starting_tape_type(src)
update_icon()
/obj/item/taperecorder/examine(mob/user)
. = ..()
. += "The wire panel is [open_panel ? "opened" : "closed"]."
/obj/item/taperecorder/attackby(obj/item/I, mob/user, params)
if(!mytape && istype(I, /obj/item/tape))
if(!user.transferItemToLoc(I,src))
return
mytape = I
to_chat(user, "<span class='notice'>You insert [I] into [src].</span>")
update_icon()
/obj/item/taperecorder/proc/eject(mob/user)
if(mytape)
to_chat(user, "<span class='notice'>You remove [mytape] from [src].</span>")
stop()
user.put_in_hands(mytape)
mytape = null
update_icon()
/obj/item/taperecorder/fire_act(exposed_temperature, exposed_volume)
mytape.ruin() //Fires destroy the tape
..()
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/item/taperecorder/attack_hand(mob/user)
if(loc == user)
if(mytape)
if(!user.is_holding(src))
return ..()
eject(user)
else
return ..()
/obj/item/taperecorder/proc/can_use(mob/user)
if(user && ismob(user))
if(!user.incapacitated())
return TRUE
return FALSE
/obj/item/taperecorder/verb/ejectverb()
set name = "Eject Tape"
set category = "Object"
if(!can_use(usr))
return
if(!mytape)
return
eject(usr)
/obj/item/taperecorder/update_icon()
if(!mytape)
icon_state = "taperecorder_empty"
else if(recording)
icon_state = "taperecorder_recording"
else if(playing)
icon_state = "taperecorder_playing"
else
icon_state = "taperecorder_idle"
/obj/item/taperecorder/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, spans, message_mode)
. = ..()
if(mytape && recording)
mytape.timestamp += mytape.used_capacity
mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] [message]"
/obj/item/taperecorder/verb/record()
set name = "Start Recording"
set category = "Object"
if(!can_use(usr))
return
if(!mytape || mytape.ruined)
return
if(recording)
return
if(playing)
return
if(mytape.used_capacity < mytape.max_capacity)
to_chat(usr, "<span class='notice'>Recording started.</span>")
recording = 1
update_icon()
mytape.timestamp += mytape.used_capacity
mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] Recording started."
var/used = mytape.used_capacity //to stop runtimes when you eject the tape
var/max = mytape.max_capacity
while(recording && used < max)
mytape.used_capacity++
used++
sleep(10)
recording = 0
update_icon()
else
to_chat(usr, "<span class='notice'>The tape is full.</span>")
/obj/item/taperecorder/verb/stop()
set name = "Stop"
set category = "Object"
if(!can_use(usr))
return
if(recording)
recording = 0
mytape.timestamp += mytape.used_capacity
mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] Recording stopped."
to_chat(usr, "<span class='notice'>Recording stopped.</span>")
return
else if(playing)
playing = 0
var/turf/T = get_turf(src)
T.visible_message("<font color=Maroon><B>Tape Recorder</B>: Playback stopped.</font>")
update_icon()
/obj/item/taperecorder/verb/play()
set name = "Play Tape"
set category = "Object"
if(!can_use(usr))
return
if(!mytape || mytape.ruined)
return
if(recording)
return
if(playing)
return
playing = 1
update_icon()
to_chat(usr, "<span class='notice'>Playing started.</span>")
var/used = mytape.used_capacity //to stop runtimes when you eject the tape
var/max = mytape.max_capacity
for(var/i = 1, used < max, sleep(10 * playsleepseconds))
if(!mytape)
break
if(playing == 0)
break
if(mytape.storedinfo.len < i)
break
say(mytape.storedinfo[i])
if(mytape.storedinfo.len < i + 1)
playsleepseconds = 1
sleep(10)
say("End of recording.")
else
playsleepseconds = mytape.timestamp[i + 1] - mytape.timestamp[i]
if(playsleepseconds > 14)
sleep(10)
say("Skipping [playsleepseconds] seconds of silence")
playsleepseconds = 1
i++
playing = 0
update_icon()
/obj/item/taperecorder/attack_self(mob/user)
if(!mytape || mytape.ruined)
return
if(recording)
stop()
else
record()
/obj/item/taperecorder/verb/print_transcript()
set name = "Print Transcript"
set category = "Object"
if(!can_use(usr))
return
if(!mytape)
return
if(!canprint)
to_chat(usr, "<span class='notice'>The recorder can't print that fast!</span>")
return
if(recording || playing)
return
to_chat(usr, "<span class='notice'>Transcript printed.</span>")
var/obj/item/paper/P = new /obj/item/paper(get_turf(src))
var/t1 = "<B>Transcript:</B><BR><BR>"
for(var/i = 1, mytape.storedinfo.len >= i, i++)
t1 += "[mytape.storedinfo[i]]<BR>"
P.info = t1
P.name = "paper- 'Transcript'"
usr.put_in_hands(P)
canprint = 0
sleep(300)
canprint = 1
//empty tape recorders
/obj/item/taperecorder/empty
starting_tape_type = null
/obj/item/tape
name = "tape"
desc = "A magnetic tape that can hold up to ten minutes of content."
icon_state = "tape_white"
icon = 'icons/obj/device.dmi'
item_state = "analyzer"
lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
w_class = WEIGHT_CLASS_TINY
materials = list(MAT_METAL=20, MAT_GLASS=5)
force = 1
throwforce = 0
var/max_capacity = 600
var/used_capacity = 0
var/list/storedinfo = list()
var/list/timestamp = list()
var/ruined = 0
/obj/item/tape/fire_act(exposed_temperature, exposed_volume)
ruin()
..()
/obj/item/tape/attack_self(mob/user)
if(!ruined)
to_chat(user, "<span class='notice'>You pull out all the tape!</span>")
ruin()
/obj/item/tape/proc/ruin()
//Lets not add infinite amounts of overlays when our fireact is called
//repeatedly
if(!ruined)
add_overlay("ribbonoverlay")
ruined = 1
/obj/item/tape/proc/fix()
cut_overlay("ribbonoverlay")
ruined = 0
/obj/item/tape/attackby(obj/item/I, mob/user, params)
if(ruined && istype(I, /obj/item/screwdriver) || istype(I, /obj/item/pen))
to_chat(user, "<span class='notice'>You start winding the tape back in...</span>")
if(I.use_tool(src, user, 120))
to_chat(user, "<span class='notice'>You wound the tape back in.</span>")
fix()
//Random colour tapes
/obj/item/tape/random
icon_state = "random_tape"
/obj/item/tape/random/New()
icon_state = "tape_[pick("white", "blue", "red", "yellow", "purple")]"
..()
/obj/item/taperecorder
name = "universal recorder"
desc = "A device that can record to cassette tapes, and play them. It automatically translates the content in playback."
icon = 'icons/obj/device.dmi'
icon_state = "taperecorder_empty"
item_state = "analyzer"
lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
w_class = WEIGHT_CLASS_SMALL
flags_1 = HEAR_1
slot_flags = ITEM_SLOT_BELT
materials = list(MAT_METAL=60, MAT_GLASS=30)
force = 2
throwforce = 0
var/recording = 0
var/playing = 0
var/playsleepseconds = 0
var/obj/item/tape/mytape
var/starting_tape_type = /obj/item/tape/random
var/open_panel = 0
var/canprint = 1
/obj/item/taperecorder/Initialize(mapload)
. = ..()
if(starting_tape_type)
mytape = new starting_tape_type(src)
update_icon()
/obj/item/taperecorder/examine(mob/user)
. = ..()
. += "The wire panel is [open_panel ? "opened" : "closed"]."
/obj/item/taperecorder/attackby(obj/item/I, mob/user, params)
if(!mytape && istype(I, /obj/item/tape))
if(!user.transferItemToLoc(I,src))
return
mytape = I
to_chat(user, "<span class='notice'>You insert [I] into [src].</span>")
update_icon()
/obj/item/taperecorder/proc/eject(mob/user)
if(mytape)
to_chat(user, "<span class='notice'>You remove [mytape] from [src].</span>")
stop()
user.put_in_hands(mytape)
mytape = null
update_icon()
/obj/item/taperecorder/fire_act(exposed_temperature, exposed_volume)
mytape.ruin() //Fires destroy the tape
..()
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/item/taperecorder/attack_hand(mob/user)
if(loc == user)
if(mytape)
if(!user.is_holding(src))
return ..()
eject(user)
else
return ..()
/obj/item/taperecorder/proc/can_use(mob/user)
if(user && ismob(user))
if(!user.incapacitated())
return TRUE
return FALSE
/obj/item/taperecorder/verb/ejectverb()
set name = "Eject Tape"
set category = "Object"
if(!can_use(usr))
return
if(!mytape)
return
eject(usr)
/obj/item/taperecorder/update_icon()
if(!mytape)
icon_state = "taperecorder_empty"
else if(recording)
icon_state = "taperecorder_recording"
else if(playing)
icon_state = "taperecorder_playing"
else
icon_state = "taperecorder_idle"
/obj/item/taperecorder/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, spans, message_mode, atom/movable/source)
. = ..()
if(mytape && recording)
mytape.timestamp += mytape.used_capacity
mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] [message]"
/obj/item/taperecorder/verb/record()
set name = "Start Recording"
set category = "Object"
if(!can_use(usr))
return
if(!mytape || mytape.ruined)
return
if(recording)
return
if(playing)
return
if(mytape.used_capacity < mytape.max_capacity)
to_chat(usr, "<span class='notice'>Recording started.</span>")
recording = 1
update_icon()
mytape.timestamp += mytape.used_capacity
mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] Recording started."
var/used = mytape.used_capacity //to stop runtimes when you eject the tape
var/max = mytape.max_capacity
while(recording && used < max)
mytape.used_capacity++
used++
sleep(10)
recording = 0
update_icon()
else
to_chat(usr, "<span class='notice'>The tape is full.</span>")
/obj/item/taperecorder/verb/stop()
set name = "Stop"
set category = "Object"
if(!can_use(usr))
return
if(recording)
recording = 0
mytape.timestamp += mytape.used_capacity
mytape.storedinfo += "\[[time2text(mytape.used_capacity * 10,"mm:ss")]\] Recording stopped."
to_chat(usr, "<span class='notice'>Recording stopped.</span>")
return
else if(playing)
playing = 0
var/turf/T = get_turf(src)
T.visible_message("<font color=Maroon><B>Tape Recorder</B>: Playback stopped.</font>")
update_icon()
/obj/item/taperecorder/verb/play()
set name = "Play Tape"
set category = "Object"
if(!can_use(usr))
return
if(!mytape || mytape.ruined)
return
if(recording)
return
if(playing)
return
playing = 1
update_icon()
to_chat(usr, "<span class='notice'>Playing started.</span>")
var/used = mytape.used_capacity //to stop runtimes when you eject the tape
var/max = mytape.max_capacity
for(var/i = 1, used < max, sleep(10 * playsleepseconds))
if(!mytape)
break
if(playing == 0)
break
if(mytape.storedinfo.len < i)
break
say(mytape.storedinfo[i])
if(mytape.storedinfo.len < i + 1)
playsleepseconds = 1
sleep(10)
say("End of recording.")
else
playsleepseconds = mytape.timestamp[i + 1] - mytape.timestamp[i]
if(playsleepseconds > 14)
sleep(10)
say("Skipping [playsleepseconds] seconds of silence")
playsleepseconds = 1
i++
playing = 0
update_icon()
/obj/item/taperecorder/attack_self(mob/user)
if(!mytape || mytape.ruined)
return
if(recording)
stop()
else
record()
/obj/item/taperecorder/verb/print_transcript()
set name = "Print Transcript"
set category = "Object"
if(!can_use(usr))
return
if(!mytape)
return
if(!canprint)
to_chat(usr, "<span class='notice'>The recorder can't print that fast!</span>")
return
if(recording || playing)
return
to_chat(usr, "<span class='notice'>Transcript printed.</span>")
var/obj/item/paper/P = new /obj/item/paper(get_turf(src))
var/t1 = "<B>Transcript:</B><BR><BR>"
for(var/i = 1, mytape.storedinfo.len >= i, i++)
t1 += "[mytape.storedinfo[i]]<BR>"
P.info = t1
P.name = "paper- 'Transcript'"
usr.put_in_hands(P)
canprint = 0
sleep(300)
canprint = 1
//empty tape recorders
/obj/item/taperecorder/empty
starting_tape_type = null
/obj/item/tape
name = "tape"
desc = "A magnetic tape that can hold up to ten minutes of content."
icon_state = "tape_white"
icon = 'icons/obj/device.dmi'
item_state = "analyzer"
lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
w_class = WEIGHT_CLASS_TINY
materials = list(MAT_METAL=20, MAT_GLASS=5)
force = 1
throwforce = 0
var/max_capacity = 600
var/used_capacity = 0
var/list/storedinfo = list()
var/list/timestamp = list()
var/ruined = 0
/obj/item/tape/fire_act(exposed_temperature, exposed_volume)
ruin()
..()
/obj/item/tape/attack_self(mob/user)
if(!ruined)
to_chat(user, "<span class='notice'>You pull out all the tape!</span>")
ruin()
/obj/item/tape/proc/ruin()
//Lets not add infinite amounts of overlays when our fireact is called
//repeatedly
if(!ruined)
add_overlay("ribbonoverlay")
ruined = 1
/obj/item/tape/proc/fix()
cut_overlay("ribbonoverlay")
ruined = 0
/obj/item/tape/attackby(obj/item/I, mob/user, params)
if(ruined && istype(I, /obj/item/screwdriver) || istype(I, /obj/item/pen))
to_chat(user, "<span class='notice'>You start winding the tape back in...</span>")
if(I.use_tool(src, user, 120))
to_chat(user, "<span class='notice'>You wound the tape back in.</span>")
fix()
//Random colour tapes
/obj/item/tape/random
icon_state = "random_tape"
/obj/item/tape/random/New()
icon_state = "tape_[pick("white", "blue", "red", "yellow", "purple")]"
..()
+1 -1
View File
@@ -122,7 +122,7 @@
interact(user)
return ..()
/obj/item/toy/eightball/haunted/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, spans, message_mode)
/obj/item/toy/eightball/haunted/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, spans, message_mode, atom/movable/source)
. = ..()
last_message = raw_message
+36 -36
View File
@@ -1,36 +1,36 @@
/obj/item/grenade/flashbang
name = "flashbang"
icon_state = "flashbang"
item_state = "flashbang"
lefthand_file = 'icons/mob/inhands/equipment/security_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi'
var/flashbang_range = 7 //how many tiles away the mob will be stunned.
/obj/item/grenade/flashbang/prime()
update_mob()
var/flashbang_turf = get_turf(src)
if(!flashbang_turf)
return
do_sparks(rand(5, 9), FALSE, src)
playsound(flashbang_turf, 'sound/weapons/flashbang.ogg', 100, TRUE, 8, 0.9)
new /obj/effect/dummy/lighting_obj (flashbang_turf, LIGHT_COLOR_WHITE, (flashbang_range + 2), 4, 2)
for(var/mob/living/M in get_hearers_in_view(flashbang_range, flashbang_turf))
bang(get_turf(M), M)
qdel(src)
/obj/item/grenade/flashbang/proc/bang(turf/T , mob/living/M)
if(M.stat == DEAD) //They're dead!
return
M.show_message("<span class='warning'>BANG</span>", 2)
var/distance = max(0,get_dist(get_turf(src),T))
//Flash
if(M.flash_act(affect_silicon = 1))
M.Knockdown(max(200/max(1,distance), 60))
//Bang
if(!distance || loc == M || loc == M.loc) //Stop allahu akbarring rooms with this.
M.Knockdown(200)
M.soundbang_act(1, 200, 10, 15)
else
M.soundbang_act(1, max(200/max(1,distance), 60), rand(0, 5))
/obj/item/grenade/flashbang
name = "flashbang"
icon_state = "flashbang"
item_state = "flashbang"
lefthand_file = 'icons/mob/inhands/equipment/security_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi'
var/flashbang_range = 7 //how many tiles away the mob will be stunned.
/obj/item/grenade/flashbang/prime()
update_mob()
var/flashbang_turf = get_turf(src)
if(!flashbang_turf)
return
do_sparks(rand(5, 9), FALSE, src)
playsound(flashbang_turf, 'sound/weapons/flashbang.ogg', 100, TRUE, 8, 0.9)
new /obj/effect/dummy/lighting_obj (flashbang_turf, LIGHT_COLOR_WHITE, (flashbang_range + 2), 4, 2)
for(var/mob/living/M in get_hearers_in_view(flashbang_range, flashbang_turf))
bang(get_turf(M), M)
qdel(src)
/obj/item/grenade/flashbang/proc/bang(turf/T , mob/living/M)
if(M.stat == DEAD) //They're dead!
return
M.show_message("<span class='warning'>BANG</span>", MSG_AUDIBLE)
var/distance = max(0,get_dist(get_turf(src),T))
//Flash
if(M.flash_act(affect_silicon = 1))
M.Knockdown(max(200/max(1,distance), 60))
//Bang
if(!distance || loc == M || loc == M.loc) //Stop allahu akbarring rooms with this.
M.Knockdown(200)
M.soundbang_act(1, 200, 10, 15)
else
M.soundbang_act(1, max(200/max(1,distance), 60), rand(0, 5))
+4 -4
View File
@@ -158,7 +158,7 @@
//we are not catholic
if(young == TRUE || Kisser.young == TRUE)
user.show_message("<span class='notice'>[src] plays tag with [Kisser].</span>", 1,
user.show_message("<span class='notice'>[src] plays tag with [Kisser].</span>", MSG_VISUAL,
"<span class='notice'>They're happy.</span>", 0)
Kisser.cheer_up()
cheer_up()
@@ -166,10 +166,10 @@
//never again
else if(Kisser in scorned)
//message, visible, alternate message, neither visible nor audible
user.show_message("<span class='notice'>[src] rejects the advances of [Kisser]!</span>", 1,
user.show_message("<span class='notice'>[src] rejects the advances of [Kisser]!</span>", MSG_VISUAL,
"<span class='notice'>That didn't feel like it worked.</span>", 0)
else if(src in Kisser.scorned)
user.show_message("<span class='notice'>[Kisser] realises who [src] is and turns away.</span>", 1,
user.show_message("<span class='notice'>[Kisser] realises who [src] is and turns away.</span>", MSG_VISUAL,
"<span class='notice'>That didn't feel like it worked.</span>", 0)
//first comes love
@@ -190,7 +190,7 @@
new_lover(Kisser)
Kisser.new_lover(src)
else
user.show_message("<span class='notice'>[src] rejects the advances of [Kisser], maybe next time?</span>", 1,
user.show_message("<span class='notice'>[src] rejects the advances of [Kisser], maybe next time?</span>", MSG_VISUAL,
"<span class='notice'>That didn't feel like it worked, this time.</span>", 0)
//then comes marriage
File diff suppressed because it is too large Load Diff
+240 -240
View File
@@ -1,240 +1,240 @@
#define SOURCE_PORTAL 1
#define DESTINATION_PORTAL 2
/* Teleportation devices.
* Contains:
* Locator
* Hand-tele
*/
/*
* Locator
*/
/obj/item/locator
name = "bluespace locator"
desc = "Used to track portable teleportation beacons and targets with embedded tracking implants."
icon = 'icons/obj/device.dmi'
icon_state = "locator"
var/temp = null
flags_1 = CONDUCT_1
w_class = WEIGHT_CLASS_SMALL
item_state = "electronic"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
throw_speed = 3
throw_range = 7
materials = list(MAT_METAL=400)
/obj/item/locator/attack_self(mob/user)
user.set_machine(src)
var/dat
if (temp)
dat = "[temp]<BR><BR><A href='byond://?src=[REF(src)];temp=1'>Clear</A>"
else
dat = {"
<B>Persistent Signal Locator</B><HR>
<A href='?src=[REF(src)];refresh=1'>Refresh</A>"}
user << browse(dat, "window=radio")
onclose(user, "radio")
return
/obj/item/locator/Topic(href, href_list)
..()
if (usr.stat || usr.restrained())
return
var/turf/current_location = get_turf(usr)//What turf is the user on?
if(!current_location || is_centcom_level(current_location.z))//If turf was not found or they're on CentCom
to_chat(usr, "[src] is malfunctioning.")
return
if(usr.contents.Find(src) || (in_range(src, usr) && isturf(loc)))
usr.set_machine(src)
if (href_list["refresh"])
temp = "<B>Persistent Signal Locator</B><HR>"
var/turf/sr = get_turf(src)
if (sr)
temp += "<B>Beacon Signals:</B><BR>"
for(var/obj/item/beacon/W in GLOB.teleportbeacons)
if (!W.renamed)
continue
var/turf/tr = get_turf(W)
if (tr.z == sr.z && tr)
var/direct = max(abs(tr.x - sr.x), abs(tr.y - sr.y))
if (direct < 5)
direct = "very strong"
else
if (direct < 10)
direct = "strong"
else
if (direct < 20)
direct = "weak"
else
direct = "very weak"
temp += "[W.name]-[dir2text(get_dir(sr, tr))]-[direct]<BR>"
temp += "<B>Implant Signals:</B><BR>"
for (var/obj/item/implant/tracking/W in GLOB.tracked_implants)
if (!isliving(W.imp_in))
continue
var/mob/living/M = W.imp_in
if (M.stat == DEAD)
if (M.timeofdeath + W.lifespan_postmortem < world.time)
continue
var/turf/tr = get_turf(M)
if (tr.z == sr.z && tr)
var/direct = max(abs(tr.x - sr.x), abs(tr.y - sr.y))
if (direct < 20)
if (direct < 5)
direct = "very strong"
else
if (direct < 10)
direct = "strong"
else
direct = "weak"
temp += "[W.imp_in.name]-[dir2text(get_dir(sr, tr))]-[direct]<BR>"
temp += "<B>You are at \[[sr.x],[sr.y],[sr.z]\]</B> in orbital coordinates.<BR><BR><A href='byond://?src=[REF(src)];refresh=1'>Refresh</A><BR>"
else
temp += "<B><FONT color='red'>Processing Error:</FONT></B> Unable to locate orbital position.<BR>"
else
if (href_list["temp"])
temp = null
if (ismob(src.loc))
attack_self(src.loc)
else
for(var/mob/M in viewers(1, src))
if (M.client)
src.attack_self(M)
return
/*
* Hand-tele
*/
/obj/item/hand_tele
name = "hand tele"
desc = "A portable item using blue-space technology."
icon = 'icons/obj/device.dmi'
icon_state = "hand_tele"
item_state = "electronic"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
throwforce = 0
w_class = WEIGHT_CLASS_SMALL
throw_speed = 3
throw_range = 5
materials = list(MAT_METAL=10000)
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100)
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
var/list/active_portal_pairs
var/max_portal_pairs = 3
var/atmos_link_override
/obj/item/hand_tele/Initialize()
. = ..()
active_portal_pairs = list()
/obj/item/hand_tele/pre_attack(atom/target, mob/user, params)
if(try_dispel_portal(target, user))
return FALSE
return ..()
/obj/item/hand_tele/proc/try_dispel_portal(atom/target, mob/user)
if(is_parent_of_portal(target))
qdel(target)
to_chat(user, "<span class='notice'>You dispel [target] with \the [src]!</span>")
return TRUE
return FALSE
/obj/item/hand_tele/afterattack(atom/target, mob/user)
try_dispel_portal(target, user)
. = ..()
/obj/item/hand_tele/attack_self(mob/user)
var/turf/current_location = get_turf(user)//What turf is the user on?
var/area/current_area = current_location.loc
if(!current_location || current_area.noteleport || is_away_level(current_location.z) || !isturf(user.loc))//If turf was not found or they're on z level 2 or >7 which does not currently exist. or if user is not located on a turf
to_chat(user, "<span class='notice'>\The [src] is malfunctioning.</span>")
return
var/list/L = list( )
for(var/obj/machinery/computer/teleporter/com in GLOB.machines)
if(com.target)
var/area/A = get_area(com.target)
if(!A || A.noteleport)
continue
if(com.power_station && com.power_station.teleporter_hub && com.power_station.engaged)
L["[get_area(com.target)] (Active)"] = com.target
else
L["[get_area(com.target)] (Inactive)"] = com.target
var/list/turfs = list( )
for(var/turf/T in urange(10, orange=1))
if(T.x>world.maxx-8 || T.x<8)
continue //putting them at the edge is dumb
if(T.y>world.maxy-8 || T.y<8)
continue
var/area/A = T.loc
if(A.noteleport)
continue
turfs += T
if(turfs.len)
L["None (Dangerous)"] = pick(turfs)
var/t1 = input(user, "Please select a teleporter to lock in on.", "Hand Teleporter") as null|anything in L
if (!t1 || user.get_active_held_item() != src || user.incapacitated())
return
if(active_portal_pairs.len >= max_portal_pairs)
user.show_message("<span class='notice'>\The [src] is recharging!</span>")
return
var/atom/T = L[t1]
var/implantcheckmate = FALSE
if(isliving(T))
var/mob/living/M = T
if(!locate(/obj/item/implant/tracking) in M.implants) //The user was too slow and let the target mob's tracking implant expire or get removed.
implantcheckmate = TRUE
var/area/A = get_area(T)
if(A.noteleport || implantcheckmate)
to_chat(user, "<span class='notice'>\The [src] is malfunctioning.</span>")
return
current_location = get_turf(user) //Recheck.
current_area = current_location.loc
if(!current_location || current_area.noteleport || is_away_level(current_location.z) || !isturf(user.loc))//If turf was not found or they're on z level 2 or >7 which does not currently exist. or if user is not located on a turf
to_chat(user, "<span class='notice'>\The [src] is malfunctioning.</span>")
return
user.show_message("<span class='notice'>Locked In.</span>", 2)
var/list/obj/effect/portal/created = create_portal_pair(current_location, get_teleport_turf(get_turf(T)), src, 300, 1, null, atmos_link_override)
if(!(LAZYLEN(created) == 2))
return
try_move_adjacent(created[1])
active_portal_pairs[created[1]] = created[2]
var/obj/effect/portal/c1 = created[1]
var/obj/effect/portal/c2 = created[2]
investigate_log("was used by [key_name(user)] at [AREACOORD(user)] to create a portal pair with destinations [AREACOORD(c1)] and [AREACOORD(c2)].", INVESTIGATE_PORTAL)
add_fingerprint(user)
/obj/item/hand_tele/proc/on_portal_destroy(obj/effect/portal/P)
active_portal_pairs -= P //If this portal pair is made by us it'll be erased along with the other portal by the portal.
/obj/item/hand_tele/proc/is_parent_of_portal(obj/effect/portal/P)
if(!istype(P))
return FALSE
if(active_portal_pairs[P])
return SOURCE_PORTAL
for(var/i in active_portal_pairs)
if(active_portal_pairs[i] == P)
return DESTINATION_PORTAL
return FALSE
/obj/item/hand_tele/suicide_act(mob/user)
if(iscarbon(user))
user.visible_message("<span class='suicide'>[user] is creating a weak portal and sticking [user.p_their()] head through! It looks like [user.p_theyre()] trying to commit suicide!</span>")
var/mob/living/carbon/itemUser = user
var/obj/item/bodypart/head/head = itemUser.get_bodypart(BODY_ZONE_HEAD)
if(head)
head.drop_limb()
var/list/safeLevels = SSmapping.levels_by_any_trait(list(ZTRAIT_SPACE_RUINS, ZTRAIT_LAVA_RUINS, ZTRAIT_STATION, ZTRAIT_MINING))
head.forceMove(locate(rand(1, world.maxx), rand(1, world.maxy), pick(safeLevels)))
itemUser.visible_message("<span class='suicide'>The portal snaps closed taking [user]'s head with it!</span>")
else
itemUser.visible_message("<span class='suicide'>[user] looks even further depressed as they realize they do not have a head...and suddenly dies of shame!</span>")
return (BRUTELOSS)
#define SOURCE_PORTAL 1
#define DESTINATION_PORTAL 2
/* Teleportation devices.
* Contains:
* Locator
* Hand-tele
*/
/*
* Locator
*/
/obj/item/locator
name = "bluespace locator"
desc = "Used to track portable teleportation beacons and targets with embedded tracking implants."
icon = 'icons/obj/device.dmi'
icon_state = "locator"
var/temp = null
flags_1 = CONDUCT_1
w_class = WEIGHT_CLASS_SMALL
item_state = "electronic"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
throw_speed = 3
throw_range = 7
materials = list(MAT_METAL=400)
/obj/item/locator/attack_self(mob/user)
user.set_machine(src)
var/dat
if (temp)
dat = "[temp]<BR><BR><A href='byond://?src=[REF(src)];temp=1'>Clear</A>"
else
dat = {"
<B>Persistent Signal Locator</B><HR>
<A href='?src=[REF(src)];refresh=1'>Refresh</A>"}
user << browse(dat, "window=radio")
onclose(user, "radio")
return
/obj/item/locator/Topic(href, href_list)
..()
if (usr.stat || usr.restrained())
return
var/turf/current_location = get_turf(usr)//What turf is the user on?
if(!current_location || is_centcom_level(current_location.z))//If turf was not found or they're on CentCom
to_chat(usr, "[src] is malfunctioning.")
return
if(usr.contents.Find(src) || (in_range(src, usr) && isturf(loc)))
usr.set_machine(src)
if (href_list["refresh"])
temp = "<B>Persistent Signal Locator</B><HR>"
var/turf/sr = get_turf(src)
if (sr)
temp += "<B>Beacon Signals:</B><BR>"
for(var/obj/item/beacon/W in GLOB.teleportbeacons)
if (!W.renamed)
continue
var/turf/tr = get_turf(W)
if (tr.z == sr.z && tr)
var/direct = max(abs(tr.x - sr.x), abs(tr.y - sr.y))
if (direct < 5)
direct = "very strong"
else
if (direct < 10)
direct = "strong"
else
if (direct < 20)
direct = "weak"
else
direct = "very weak"
temp += "[W.name]-[dir2text(get_dir(sr, tr))]-[direct]<BR>"
temp += "<B>Implant Signals:</B><BR>"
for (var/obj/item/implant/tracking/W in GLOB.tracked_implants)
if (!isliving(W.imp_in))
continue
var/mob/living/M = W.imp_in
if (M.stat == DEAD)
if (M.timeofdeath + W.lifespan_postmortem < world.time)
continue
var/turf/tr = get_turf(M)
if (tr.z == sr.z && tr)
var/direct = max(abs(tr.x - sr.x), abs(tr.y - sr.y))
if (direct < 20)
if (direct < 5)
direct = "very strong"
else
if (direct < 10)
direct = "strong"
else
direct = "weak"
temp += "[W.imp_in.name]-[dir2text(get_dir(sr, tr))]-[direct]<BR>"
temp += "<B>You are at \[[sr.x],[sr.y],[sr.z]\]</B> in orbital coordinates.<BR><BR><A href='byond://?src=[REF(src)];refresh=1'>Refresh</A><BR>"
else
temp += "<B><FONT color='red'>Processing Error:</FONT></B> Unable to locate orbital position.<BR>"
else
if (href_list["temp"])
temp = null
if (ismob(src.loc))
attack_self(src.loc)
else
for(var/mob/M in viewers(1, src))
if (M.client)
src.attack_self(M)
return
/*
* Hand-tele
*/
/obj/item/hand_tele
name = "hand tele"
desc = "A portable item using blue-space technology."
icon = 'icons/obj/device.dmi'
icon_state = "hand_tele"
item_state = "electronic"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
throwforce = 0
w_class = WEIGHT_CLASS_SMALL
throw_speed = 3
throw_range = 5
materials = list(MAT_METAL=10000)
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 100)
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
var/list/active_portal_pairs
var/max_portal_pairs = 3
var/atmos_link_override
/obj/item/hand_tele/Initialize()
. = ..()
active_portal_pairs = list()
/obj/item/hand_tele/pre_attack(atom/target, mob/user, params)
if(try_dispel_portal(target, user))
return FALSE
return ..()
/obj/item/hand_tele/proc/try_dispel_portal(atom/target, mob/user)
if(is_parent_of_portal(target))
qdel(target)
to_chat(user, "<span class='notice'>You dispel [target] with \the [src]!</span>")
return TRUE
return FALSE
/obj/item/hand_tele/afterattack(atom/target, mob/user)
try_dispel_portal(target, user)
. = ..()
/obj/item/hand_tele/attack_self(mob/user)
var/turf/current_location = get_turf(user)//What turf is the user on?
var/area/current_area = current_location.loc
if(!current_location || current_area.noteleport || is_away_level(current_location.z) || !isturf(user.loc))//If turf was not found or they're on z level 2 or >7 which does not currently exist. or if user is not located on a turf
to_chat(user, "<span class='notice'>\The [src] is malfunctioning.</span>")
return
var/list/L = list( )
for(var/obj/machinery/computer/teleporter/com in GLOB.machines)
if(com.target)
var/area/A = get_area(com.target)
if(!A || A.noteleport)
continue
if(com.power_station && com.power_station.teleporter_hub && com.power_station.engaged)
L["[get_area(com.target)] (Active)"] = com.target
else
L["[get_area(com.target)] (Inactive)"] = com.target
var/list/turfs = list( )
for(var/turf/T in urange(10, orange=1))
if(T.x>world.maxx-8 || T.x<8)
continue //putting them at the edge is dumb
if(T.y>world.maxy-8 || T.y<8)
continue
var/area/A = T.loc
if(A.noteleport)
continue
turfs += T
if(turfs.len)
L["None (Dangerous)"] = pick(turfs)
var/t1 = input(user, "Please select a teleporter to lock in on.", "Hand Teleporter") as null|anything in L
if (!t1 || user.get_active_held_item() != src || user.incapacitated())
return
if(active_portal_pairs.len >= max_portal_pairs)
user.show_message("<span class='notice'>\The [src] is recharging!</span>")
return
var/atom/T = L[t1]
var/implantcheckmate = FALSE
if(isliving(T))
var/mob/living/M = T
if(!locate(/obj/item/implant/tracking) in M.implants) //The user was too slow and let the target mob's tracking implant expire or get removed.
implantcheckmate = TRUE
var/area/A = get_area(T)
if(A.noteleport || implantcheckmate)
to_chat(user, "<span class='notice'>\The [src] is malfunctioning.</span>")
return
current_location = get_turf(user) //Recheck.
current_area = current_location.loc
if(!current_location || current_area.noteleport || is_away_level(current_location.z) || !isturf(user.loc))//If turf was not found or they're on z level 2 or >7 which does not currently exist. or if user is not located on a turf
to_chat(user, "<span class='notice'>\The [src] is malfunctioning.</span>")
return
user.show_message("<span class='notice'>Locked In.</span>", MSG_AUDIBLE)
var/list/obj/effect/portal/created = create_portal_pair(current_location, get_teleport_turf(get_turf(T)), src, 300, 1, null, atmos_link_override)
if(!(LAZYLEN(created) == 2))
return
try_move_adjacent(created[1])
active_portal_pairs[created[1]] = created[2]
var/obj/effect/portal/c1 = created[1]
var/obj/effect/portal/c2 = created[2]
investigate_log("was used by [key_name(user)] at [AREACOORD(user)] to create a portal pair with destinations [AREACOORD(c1)] and [AREACOORD(c2)].", INVESTIGATE_PORTAL)
add_fingerprint(user)
/obj/item/hand_tele/proc/on_portal_destroy(obj/effect/portal/P)
active_portal_pairs -= P //If this portal pair is made by us it'll be erased along with the other portal by the portal.
/obj/item/hand_tele/proc/is_parent_of_portal(obj/effect/portal/P)
if(!istype(P))
return FALSE
if(active_portal_pairs[P])
return SOURCE_PORTAL
for(var/i in active_portal_pairs)
if(active_portal_pairs[i] == P)
return DESTINATION_PORTAL
return FALSE
/obj/item/hand_tele/suicide_act(mob/user)
if(iscarbon(user))
user.visible_message("<span class='suicide'>[user] is creating a weak portal and sticking [user.p_their()] head through! It looks like [user.p_theyre()] trying to commit suicide!</span>")
var/mob/living/carbon/itemUser = user
var/obj/item/bodypart/head/head = itemUser.get_bodypart(BODY_ZONE_HEAD)
if(head)
head.drop_limb()
var/list/safeLevels = SSmapping.levels_by_any_trait(list(ZTRAIT_SPACE_RUINS, ZTRAIT_LAVA_RUINS, ZTRAIT_STATION, ZTRAIT_MINING))
head.forceMove(locate(rand(1, world.maxx), rand(1, world.maxy), pick(safeLevels)))
itemUser.visible_message("<span class='suicide'>The portal snaps closed taking [user]'s head with it!</span>")
else
itemUser.visible_message("<span class='suicide'>[user] looks even further depressed as they realize they do not have a head...and suddenly dies of shame!</span>")
return (BRUTELOSS)
+143 -134
View File
@@ -1,135 +1,144 @@
/obj/item/wirecutters
name = "wirecutters"
desc = "This cuts wires."
icon = 'icons/obj/tools.dmi'
icon_state = "cutters_map"
item_state = "cutters"
lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
flags_1 = CONDUCT_1
slot_flags = ITEM_SLOT_BELT
force = 6
throw_speed = 3
throw_range = 7
w_class = WEIGHT_CLASS_SMALL
materials = list(MAT_METAL=80)
attack_verb = list("pinched", "nipped")
hitsound = 'sound/items/wirecutter.ogg'
usesound = 'sound/items/wirecutter.ogg'
tool_behaviour = TOOL_WIRECUTTER
toolspeed = 1
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 30)
var/random_color = TRUE
var/static/list/wirecutter_colors = list(
"blue" = "#1861d5",
"red" = "#951710",
"pink" = "#d5188d",
"brown" = "#a05212",
"green" = "#0e7f1b",
"cyan" = "#18a2d5",
"yellow" = "#d58c18"
)
/obj/item/wirecutters/Initialize()
. = ..()
if(random_color) //random colors!
icon_state = "cutters"
var/our_color = pick(wirecutter_colors)
add_atom_colour(wirecutter_colors[our_color], FIXED_COLOUR_PRIORITY)
update_icon()
/obj/item/wirecutters/update_icon()
if(!random_color) //icon override
return
cut_overlays()
var/mutable_appearance/base_overlay = mutable_appearance(icon, "cutters_cutty_thingy")
base_overlay.appearance_flags = RESET_COLOR
add_overlay(base_overlay)
/obj/item/wirecutters/attack(mob/living/carbon/C, mob/user)
if(istype(C) && C.handcuffed && istype(C.handcuffed, /obj/item/restraints/handcuffs/cable))
user.visible_message("<span class='notice'>[user] cuts [C]'s restraints with [src]!</span>")
qdel(C.handcuffed)
return
else
..()
/obj/item/wirecutters/suicide_act(mob/user)
user.visible_message("<span class='suicide'>[user] is cutting at [user.p_their()] arteries with [src]! It looks like [user.p_theyre()] trying to commit suicide!</span>")
playsound(loc, usesound, 50, 1, -1)
return (BRUTELOSS)
/obj/item/wirecutters/brass
name = "brass wirecutters"
desc = "A pair of wirecutters made of brass. The handle feels freezing cold to the touch."
resistance_flags = FIRE_PROOF | ACID_PROOF
icon_state = "cutters_brass"
random_color = FALSE
toolspeed = 0.5
/obj/item/wirecutters/bronze
name = "bronze plated wirecutters"
desc = "A pair of wirecutters plated with bronze."
icon_state = "cutters_brass"
random_color = FALSE
toolspeed = 0.95 //Wire cutters have 0 time bars though
/obj/item/wirecutters/abductor
name = "alien wirecutters"
desc = "Extremely sharp wirecutters, made out of a silvery-green metal."
icon = 'icons/obj/abductor.dmi'
icon_state = "cutters"
toolspeed = 0.1
random_color = FALSE
/obj/item/wirecutters/cyborg
name = "wirecutters"
desc = "This cuts wires."
toolspeed = 0.5
/obj/item/wirecutters/power
name = "jaws of life"
desc = "A set of jaws of life, compressed through the magic of science. It's fitted with a cutting head."
icon_state = "jaws_cutter"
item_state = "jawsoflife"
materials = list(MAT_METAL=150,MAT_SILVER=50,MAT_TITANIUM=25)
usesound = 'sound/items/jaws_cut.ogg'
toolspeed = 0.25
random_color = FALSE
/obj/item/wirecutters/power/suicide_act(mob/user)
user.visible_message("<span class='suicide'>[user] is wrapping \the [src] around [user.p_their()] neck. It looks like [user.p_theyre()] trying to rip [user.p_their()] head off!</span>")
playsound(loc, 'sound/items/jaws_cut.ogg', 50, 1, -1)
if(iscarbon(user))
var/mob/living/carbon/C = user
var/obj/item/bodypart/BP = C.get_bodypart(BODY_ZONE_HEAD)
if(BP)
BP.drop_limb()
playsound(loc,pick('sound/misc/desceration-01.ogg','sound/misc/desceration-02.ogg','sound/misc/desceration-01.ogg') ,50, 1, -1)
return (BRUTELOSS)
/obj/item/wirecutters/power/attack_self(mob/user)
playsound(get_turf(user), 'sound/items/change_jaws.ogg', 50, 1)
var/obj/item/crowbar/power/pryjaws = new /obj/item/crowbar/power(drop_location())
to_chat(user, "<span class='notice'>You attach the pry jaws to [src].</span>")
qdel(src)
user.put_in_active_hand(pryjaws)
/obj/item/wirecutters/power/attack(mob/living/carbon/C, mob/user)
if(istype(C) && C.handcuffed)
user.visible_message("<span class='notice'>[user] cuts [C]'s restraints with [src]!</span>")
qdel(C.handcuffed)
return
else
..()
/obj/item/wirecutters/advanced
name = "advanced wirecutters"
desc = "A set of reproduction alien wirecutters, they have a silver handle with an exceedingly sharp blade."
icon = 'icons/obj/advancedtools.dmi'
icon_state = "cutters"
toolspeed = 0.2
/obj/item/wirecutters
name = "wirecutters"
desc = "This cuts wires."
icon = 'icons/obj/tools.dmi'
icon_state = "cutters_map"
item_state = "cutters"
lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
flags_1 = CONDUCT_1
slot_flags = ITEM_SLOT_BELT
force = 6
throw_speed = 3
throw_range = 7
w_class = WEIGHT_CLASS_SMALL
materials = list(MAT_METAL=80)
attack_verb = list("pinched", "nipped")
hitsound = 'sound/items/wirecutter.ogg'
usesound = 'sound/items/wirecutter.ogg'
tool_behaviour = TOOL_WIRECUTTER
toolspeed = 1
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 30)
var/random_color = TRUE
var/static/list/wirecutter_colors = list(
"blue" = "#1861d5",
"red" = "#951710",
"pink" = "#d5188d",
"brown" = "#a05212",
"green" = "#0e7f1b",
"cyan" = "#18a2d5",
"yellow" = "#d58c18"
)
/obj/item/wirecutters/Initialize()
. = ..()
if(random_color) //random colors!
icon_state = "cutters"
var/our_color = pick(wirecutter_colors)
add_atom_colour(wirecutter_colors[our_color], FIXED_COLOUR_PRIORITY)
update_icon()
/obj/item/wirecutters/update_icon()
if(!random_color) //icon override
return
cut_overlays()
var/mutable_appearance/base_overlay = mutable_appearance(icon, "cutters_cutty_thingy")
base_overlay.appearance_flags = RESET_COLOR
add_overlay(base_overlay)
/obj/item/wirecutters/attack(mob/living/carbon/C, mob/user)
if(istype(C) && C.handcuffed && istype(C.handcuffed, /obj/item/restraints/handcuffs/cable))
user.visible_message("<span class='notice'>[user] cuts [C]'s restraints with [src]!</span>")
qdel(C.handcuffed)
return
else
..()
/obj/item/wirecutters/suicide_act(mob/user)
user.visible_message("<span class='suicide'>[user] is cutting at [user.p_their()] arteries with [src]! It looks like [user.p_theyre()] trying to commit suicide!</span>")
playsound(loc, usesound, 50, 1, -1)
return (BRUTELOSS)
/obj/item/wirecutters/brass
name = "brass wirecutters"
desc = "A pair of wirecutters made of brass. The handle feels freezing cold to the touch."
resistance_flags = FIRE_PROOF | ACID_PROOF
icon_state = "cutters_brass"
random_color = FALSE
toolspeed = 0.5
/obj/item/wirecutters/bronze
name = "bronze plated wirecutters"
desc = "A pair of wirecutters plated with bronze."
icon_state = "cutters_brass"
random_color = FALSE
toolspeed = 0.95 //Wire cutters have 0 time bars though
/obj/item/wirecutters/abductor
name = "alien wirecutters"
desc = "Extremely sharp wirecutters, made out of a silvery-green metal."
icon = 'icons/obj/abductor.dmi'
icon_state = "cutters"
toolspeed = 0.1
random_color = FALSE
/obj/item/wirecutters/cyborg
name = "wirecutters"
desc = "This cuts wires."
toolspeed = 0.5
/obj/item/wirecutters/power
name = "jaws of life"
desc = "A set of jaws of life, compressed through the magic of science. It's fitted with a cutting head."
icon_state = "jaws_cutter"
item_state = "jawsoflife"
materials = list(MAT_METAL=150,MAT_SILVER=50,MAT_TITANIUM=25)
usesound = 'sound/items/jaws_cut.ogg'
toolspeed = 0.25
random_color = FALSE
/obj/item/wirecutters/power/suicide_act(mob/user)
user.visible_message("<span class='suicide'>[user] is wrapping \the [src] around [user.p_their()] neck. It looks like [user.p_theyre()] trying to rip [user.p_their()] head off!</span>")
playsound(loc, 'sound/items/jaws_cut.ogg', 50, 1, -1)
if(iscarbon(user))
var/mob/living/carbon/C = user
var/obj/item/bodypart/BP = C.get_bodypart(BODY_ZONE_HEAD)
if(BP)
BP.drop_limb()
playsound(loc,pick('sound/misc/desceration-01.ogg','sound/misc/desceration-02.ogg','sound/misc/desceration-01.ogg') ,50, 1, -1)
return (BRUTELOSS)
/obj/item/wirecutters/power/attack_self(mob/user)
playsound(get_turf(user), 'sound/items/change_jaws.ogg', 50, 1)
var/obj/item/crowbar/power/pryjaws = new /obj/item/crowbar/power(drop_location())
to_chat(user, "<span class='notice'>You attach the pry jaws to [src].</span>")
qdel(src)
user.put_in_active_hand(pryjaws)
/obj/item/wirecutters/power/attack(mob/living/carbon/C, mob/user)
if(istype(C))
if(C.handcuffed)
user.visible_message("<span class='notice'>[user] cuts [C]'s restraints with [src]!</span>")
qdel(C.handcuffed)
return
else if(C.has_status_effect(STATUS_EFFECT_CHOKINGSTRAND))
var/man = C == user ? "your" : "[C]'\s"
user.visible_message("<span class='notice'>[user] attempts to remove the durathread strand from around [man] neck.</span>", \
"<span class='notice'>You attempt to remove the durathread strand from around [man] neck.</span>")
if(do_after(user, 15, null, C))
user.visible_message("<span class='notice'>[user] succesfuly removes the durathread strand.</span>",
"<span class='notice'>You succesfuly remove the durathread strand.</span>")
C.remove_status_effect(STATUS_EFFECT_CHOKINGSTRAND)
return
..()
/obj/item/wirecutters/advanced
name = "advanced wirecutters"
desc = "A set of reproduction alien wirecutters, they have a silver handle with an exceedingly sharp blade."
icon = 'icons/obj/advancedtools.dmi'
icon_state = "cutters"
toolspeed = 0.2
random_color = FALSE
+1 -1
View File
@@ -182,7 +182,7 @@
return
src.add_fingerprint(user)
if (src.bullets < 1)
user.show_message("<span class='warning'>*click*</span>", 2)
user.show_message("<span class='warning'>*click*</span>", MSG_AUDIBLE)
playsound(src, "gun_dry_fire", 30, 1)
return
playsound(user, 'sound/weapons/gunshot.ogg', 100, 1)
@@ -154,6 +154,9 @@
log_admin("[key_name(new_spawn)] possessed a golem shell enslaved to [key_name(owner)].")
if(ishuman(new_spawn))
var/mob/living/carbon/human/H = new_spawn
if(has_owner)
var/datum/species/golem/G = H.dna.species
G.owner = owner
H.set_cloned_appearance()
if(!name)
if(has_owner)
+44 -56
View File
@@ -1,56 +1,44 @@
//Loom, turns raw cotton and durathread into their respective fabrics.
/obj/structure/loom
name = "loom"
desc = "A simple device used to weave cloth and other thread-based fabrics together into usable material."
icon = 'icons/obj/hydroponics/equipment.dmi'
icon_state = "loom"
density = TRUE
anchored = TRUE
/obj/structure/loom/attackby(obj/item/I, mob/user)
if (user.a_intent != INTENT_HELP)
return ..()
if (istype(I, /obj/item/stack/sheet))
if (!anchored)
return to_chat(user, "<span class='notice'>You have to anchor [src] first!</span>")
var/obj/item/stack/sheet/W = I
if(W.is_fabric && W.amount > 1)
user.visible_message("<span class='notice'[user] starts weaving [W.name] through the loom.</span>",
"<span class='notice'>You start weaving the [W.name] through the loom.</span>")
if(W.use_tool(src, user, W.pull_effort))
new W.loom_result(drop_location())
to_chat(user, "<span class='notice'>You weave the [W.name] into a workable fabric.</span>")
W.amount = (W.amount - 2)
if(W.amount < 1)
qdel(W)
else if(W.is_fabric)
to_chat("<span class='notice'>You need at least 2 [W.name] to loom into fabric!</span>")
return
else
switch (I.tool_behaviour)
if (TOOL_SCREWDRIVER)
user.visible_message("<span class='info'>[user] starts disassembling [src]...</span>",
"<span class='info'>You start disassembling [src]...</span>")
I.play_tool_sound(src)
if(I.use_tool(src, user, 60))
playsound(src.loc, 'sound/items/deconstruct.ogg', 50, TRUE)
deconstruct(TRUE)
return
if (TOOL_WRENCH)
if (anchored)
to_chat(user, "<span class='notice'>You unsecure the [src].</span>")
I.play_tool_sound(src)
anchored = FALSE
else
to_chat(user, "<span class='notice'>You secure the [src].</span>")
I.play_tool_sound(src)
anchored = TRUE
return
return ..()
/obj/structure/loom/deconstruct(disassembled = TRUE)
new /obj/item/stack/sheet/mineral/wood (get_turf(src), 10)
qdel(src)
#define FABRIC_PER_SHEET 4
///This is a loom. It's usually made out of wood and used to weave fabric like durathread or cotton into their respective cloth types.
/obj/structure/loom
name = "loom"
desc = "A simple device used to weave cloth and other thread-based fabrics together into usable material."
icon = 'icons/obj/hydroponics/equipment.dmi'
icon_state = "loom"
density = TRUE
anchored = TRUE
/obj/structure/loom/attackby(obj/item/I, mob/user)
if(weave(I, user))
return
return ..()
/obj/structure/loom/wrench_act(mob/living/user, obj/item/I)
..()
default_unfasten_wrench(user, I, 5)
return TRUE
///Handles the weaving.
/obj/structure/loom/proc/weave(obj/item/stack/sheet/S, mob/user)
if(!istype(S) || !S.is_fabric)
return FALSE
if(!anchored)
user.show_message("<span class='notice'>The loom needs to be wrenched down.</span>", 1)
return FALSE
if(S.amount < FABRIC_PER_SHEET)
user.show_message("<span class='notice'>You need at least [FABRIC_PER_SHEET] units of fabric before using this.</span>", 1)
return FALSE
user.show_message("<span class='notice'>You start weaving \the [S.name] through the loom..</span>", 1)
if(S.use_tool(src, user, S.pull_effort))
if(S.amount >= FABRIC_PER_SHEET)
new S.loom_result(drop_location())
S.use(FABRIC_PER_SHEET)
user.show_message("<span class='notice'>You weave \the [S.name] into a workable fabric.</span>", 1)
return TRUE
/obj/structure/loom/unanchored
anchored = FALSE
#undef FABRIC_PER_SHEET
+381 -381
View File
@@ -1,381 +1,381 @@
#define MUSICIAN_HEARCHECK_MINDELAY 4
#define MUSIC_MAXLINES 600
#define MUSIC_MAXLINECHARS 50
/datum/song
var/name = "Untitled"
var/list/lines = new()
var/tempo = 5 // delay between notes
var/playing = 0 // if we're playing
var/help = 0 // if help is open
var/edit = 1 // if we're in editing mode
var/repeat = 0 // number of times remaining to repeat
var/max_repeats = 10 // maximum times we can repeat
var/instrumentDir = "piano" // the folder with the sounds
var/instrumentExt = "ogg" // the file extension
var/obj/instrumentObj = null // the associated obj playing the sound
var/last_hearcheck = 0
var/list/hearing_mobs
/datum/song/New(dir, obj, ext = "ogg")
tempo = sanitize_tempo(tempo)
instrumentDir = dir
instrumentObj = obj
instrumentExt = ext
/datum/song/Destroy()
instrumentObj = null
return ..()
// note is a number from 1-7 for A-G
// acc is either "b", "n", or "#"
// oct is 1-8 (or 9 for C)
/datum/song/proc/playnote(note, acc as text, oct)
// handle accidental -> B<>C of E<>F
if(acc == "b" && (note == 3 || note == 6)) // C or F
if(note == 3)
oct--
note--
acc = "n"
else if(acc == "#" && (note == 2 || note == 5)) // B or E
if(note == 2)
oct++
note++
acc = "n"
else if(acc == "#" && (note == 7)) //G#
note = 1
acc = "b"
else if(acc == "#") // mass convert all sharps to flats, octave jump already handled
acc = "b"
note++
// check octave, C is allowed to go to 9
if(oct < 1 || (note == 3 ? oct > 9 : oct > 8))
return
// now generate name
var/soundfile = "sound/instruments/[instrumentDir]/[ascii2text(note+64)][acc][oct].[instrumentExt]"
soundfile = file(soundfile)
// make sure the note exists
if(!fexists(soundfile))
return
// and play
var/turf/source = get_turf(instrumentObj)
if((world.time - MUSICIAN_HEARCHECK_MINDELAY) > last_hearcheck)
LAZYCLEARLIST(hearing_mobs)
for(var/mob/M in get_hearers_in_view(15, source))
if(!M.client || !(M.client.prefs.toggles & SOUND_INSTRUMENTS))
continue
LAZYADD(hearing_mobs, M)
last_hearcheck = world.time
var/sound/music_played = sound(soundfile)
for(var/i in hearing_mobs)
var/mob/M = i
M.playsound_local(source, null, 100, falloff = 5, S = music_played)
/datum/song/proc/updateDialog(mob/user)
instrumentObj.updateDialog() // assumes it's an object in world, override if otherwise
/datum/song/proc/shouldStopPlaying(mob/user)
if(instrumentObj)
if(!user.canUseTopic(instrumentObj))
return TRUE
return !instrumentObj.anchored // add special cases to stop in subclasses
else
return TRUE
/datum/song/proc/playsong(mob/user)
while(repeat >= 0)
var/cur_oct[7]
var/cur_acc[7]
for(var/i = 1 to 7)
cur_oct[i] = 3
cur_acc[i] = "n"
for(var/line in lines)
for(var/beat in splittext(lowertext(line), ","))
var/list/notes = splittext(beat, "/")
for(var/note in splittext(notes[1], "-"))
if(!playing || shouldStopPlaying(user))//If the instrument is playing, or special case
playing = FALSE
hearing_mobs = null
return
if(!length(note))
continue
var/cur_note = text2ascii(note) - 96
if(cur_note < 1 || cur_note > 7)
continue
for(var/i=2 to length(note))
var/ni = copytext(note,i,i+1)
if(!text2num(ni))
if(ni == "#" || ni == "b" || ni == "n")
cur_acc[cur_note] = ni
else if(ni == "s")
cur_acc[cur_note] = "#" // so shift is never required
else
cur_oct[cur_note] = text2num(ni)
if(user.dizziness > 0 && prob(user.dizziness / 2))
cur_note = CLAMP(cur_note + rand(round(-user.dizziness / 10), round(user.dizziness / 10)), 1, 7)
if(user.dizziness > 0 && prob(user.dizziness / 5))
if(prob(30))
cur_acc[cur_note] = "#"
else if(prob(42))
cur_acc[cur_note] = "b"
else if(prob(75))
cur_acc[cur_note] = "n"
playnote(cur_note, cur_acc[cur_note], cur_oct[cur_note])
if(notes.len >= 2 && text2num(notes[2]))
sleep(sanitize_tempo(tempo / text2num(notes[2])))
else
sleep(tempo)
repeat--
hearing_mobs = null
playing = FALSE
repeat = 0
updateDialog(user)
/datum/song/proc/interact(mob/user)
var/dat = ""
if(lines.len > 0)
dat += "<H3>Playback</H3>"
if(!playing)
dat += "<A href='?src=[REF(src)];play=1'>Play</A> <SPAN CLASS='linkOn'>Stop</SPAN><BR><BR>"
dat += "Repeat Song: "
dat += repeat > 0 ? "<A href='?src=[REF(src)];repeat=-10'>-</A><A href='?src=[REF(src)];repeat=-1'>-</A>" : "<SPAN CLASS='linkOff'>-</SPAN><SPAN CLASS='linkOff'>-</SPAN>"
dat += " [repeat] times "
dat += repeat < max_repeats ? "<A href='?src=[REF(src)];repeat=1'>+</A><A href='?src=[REF(src)];repeat=10'>+</A>" : "<SPAN CLASS='linkOff'>+</SPAN><SPAN CLASS='linkOff'>+</SPAN>"
dat += "<BR>"
else
dat += "<SPAN CLASS='linkOn'>Play</SPAN> <A href='?src=[REF(src)];stop=1'>Stop</A><BR>"
dat += "Repeats left: <B>[repeat]</B><BR>"
if(!edit)
dat += "<BR><B><A href='?src=[REF(src)];edit=2'>Show Editor</A></B><BR>"
else
dat += "<H3>Editing</H3>"
dat += "<B><A href='?src=[REF(src)];edit=1'>Hide Editor</A></B>"
dat += " <A href='?src=[REF(src)];newsong=1'>Start a New Song</A>"
dat += " <A href='?src=[REF(src)];import=1'>Import a Song</A><BR><BR>"
var/bpm = round(600 / tempo)
dat += "Tempo: <A href='?src=[REF(src)];tempo=[world.tick_lag]'>-</A> [bpm] BPM <A href='?src=[REF(src)];tempo=-[world.tick_lag]'>+</A><BR><BR>"
var/linecount = 0
for(var/line in lines)
linecount += 1
dat += "Line [linecount]: <A href='?src=[REF(src)];modifyline=[linecount]'>Edit</A> <A href='?src=[REF(src)];deleteline=[linecount]'>X</A> [line]<BR>"
dat += "<A href='?src=[REF(src)];newline=1'>Add Line</A><BR><BR>"
if(help)
dat += "<B><A href='?src=[REF(src)];help=1'>Hide Help</A></B><BR>"
dat += {"
Lines are a series of chords, separated by commas (,), each with notes separated by hyphens (-).<br>
Every note in a chord will play together, with chord timed by the tempo.<br>
<br>
Notes are played by the names of the note, and optionally, the accidental, and/or the octave number.<br>
By default, every note is natural and in octave 3. Defining otherwise is remembered for each note.<br>
Example: <i>C,D,E,F,G,A,B</i> will play a C major scale.<br>
After a note has an accidental placed, it will be remembered: <i>C,C4,C,C3</i> is <i>C3,C4,C4,C3</i><br>
Chords can be played simply by seperating each note with a hyphon: <i>A-C#,Cn-E,E-G#,Gn-B</i><br>
A pause may be denoted by an empty chord: <i>C,E,,C,G</i><br>
To make a chord be a different time, end it with /x, where the chord length will be length<br>
defined by tempo / x: <i>C,G/2,E/4</i><br>
Combined, an example is: <i>E-E4/4,F#/2,G#/8,B/8,E3-E4/4</i>
<br>
Lines may be up to [MUSIC_MAXLINECHARS] characters.<br>
A song may only contain up to [MUSIC_MAXLINES] lines.<br>
"}
else
dat += "<B><A href='?src=[REF(src)];help=2'>Show Help</A></B><BR>"
var/datum/browser/popup = new(user, "instrument", instrumentObj.name, 700, 500)
popup.set_content(dat)
popup.set_title_image(user.browse_rsc_icon(instrumentObj.icon, instrumentObj.icon_state))
popup.open()
/datum/song/proc/ParseSong(text)
set waitfor = FALSE
//split into lines
lines = splittext(text, "\n")
if(lines.len)
if(copytext(lines[1],1,6) == "BPM: ")
tempo = sanitize_tempo(600 / text2num(copytext(lines[1],6)))
lines.Cut(1,2)
else
tempo = sanitize_tempo(5) // default 120 BPM
if(lines.len > MUSIC_MAXLINES)
to_chat(usr, "Too many lines!")
lines.Cut(MUSIC_MAXLINES + 1)
var/linenum = 1
for(var/l in lines)
if(length(l) > MUSIC_MAXLINECHARS)
to_chat(usr, "Line [linenum] too long!")
lines.Remove(l)
else
linenum++
updateDialog(usr) // make sure updates when complete
/datum/song/Topic(href, href_list)
if(!usr.canUseTopic(instrumentObj))
usr << browse(null, "window=instrument")
usr.unset_machine()
return
instrumentObj.add_fingerprint(usr)
if(href_list["newsong"])
lines = new()
tempo = sanitize_tempo(5) // default 120 BPM
name = ""
else if(href_list["import"])
var/t = ""
do
t = html_encode(input(usr, "Please paste the entire song, formatted:", text("[]", name), t) as message)
if(!in_range(instrumentObj, usr))
return
if(length(t) >= MUSIC_MAXLINES * MUSIC_MAXLINECHARS)
var/cont = input(usr, "Your message is too long! Would you like to continue editing it?", "", "yes") in list("yes", "no")
if(cont == "no")
break
while(length(t) > MUSIC_MAXLINES * MUSIC_MAXLINECHARS)
ParseSong(t)
else if(href_list["help"])
help = text2num(href_list["help"]) - 1
else if(href_list["edit"])
edit = text2num(href_list["edit"]) - 1
if(href_list["repeat"]) //Changing this from a toggle to a number of repeats to avoid infinite loops.
if(playing)
return //So that people cant keep adding to repeat. If the do it intentionally, it could result in the server crashing.
repeat += round(text2num(href_list["repeat"]))
if(repeat < 0)
repeat = 0
if(repeat > max_repeats)
repeat = max_repeats
else if(href_list["tempo"])
tempo = sanitize_tempo(tempo + text2num(href_list["tempo"]))
else if(href_list["play"])
playing = TRUE
spawn()
playsong(usr)
else if(href_list["newline"])
var/newline = html_encode(input("Enter your line: ", instrumentObj.name) as text|null)
if(!newline || !in_range(instrumentObj, usr))
return
if(lines.len > MUSIC_MAXLINES)
return
if(length(newline) > MUSIC_MAXLINECHARS)
newline = copytext(newline, 1, MUSIC_MAXLINECHARS)
lines.Add(newline)
else if(href_list["deleteline"])
var/num = round(text2num(href_list["deleteline"]))
if(num > lines.len || num < 1)
return
lines.Cut(num, num+1)
else if(href_list["modifyline"])
var/num = round(text2num(href_list["modifyline"]),1)
var/content = html_encode(input("Enter your line: ", instrumentObj.name, lines[num]) as text|null)
if(!content || !in_range(instrumentObj, usr))
return
if(length(content) > MUSIC_MAXLINECHARS)
content = copytext(content, 1, MUSIC_MAXLINECHARS)
if(num > lines.len || num < 1)
return
lines[num] = content
else if(href_list["stop"])
playing = FALSE
hearing_mobs = null
updateDialog(usr)
return
/datum/song/proc/sanitize_tempo(new_tempo)
new_tempo = abs(new_tempo)
return max(round(new_tempo, world.tick_lag), world.tick_lag)
// subclass for handheld instruments, like violin
/datum/song/handheld
/datum/song/handheld/updateDialog(mob/user)
instrumentObj.interact(user)
/datum/song/handheld/shouldStopPlaying()
if(instrumentObj)
return !isliving(instrumentObj.loc)
else
return TRUE
//////////////////////////////////////////////////////////////////////////
/obj/structure/piano
name = "space minimoog"
icon = 'icons/obj/musician.dmi'
icon_state = "minimoog"
anchored = TRUE
density = TRUE
var/datum/song/song
/obj/structure/piano/unanchored
anchored = FALSE
/obj/structure/piano/New()
..()
song = new("piano", src)
if(prob(50) && icon_state == initial(icon_state))
name = "space minimoog"
desc = "This is a minimoog, like a space piano, but more spacey!"
icon_state = "minimoog"
else
name = "space piano"
desc = "This is a space piano, like a regular piano, but always in tune! Even if the musician isn't."
icon_state = "piano"
/obj/structure/piano/Destroy()
qdel(song)
song = null
return ..()
/obj/structure/piano/Initialize(mapload)
. = ..()
if(mapload)
song.tempo = song.sanitize_tempo(song.tempo) // tick_lag isn't set when the map is loaded
/obj/structure/piano/attack_hand(mob/user)
. = ..()
if(.)
return
interact(user)
/obj/structure/piano/attack_paw(mob/user)
return attack_hand(user)
/obj/structure/piano/interact(mob/user)
ui_interact(user)
/obj/structure/piano/ui_interact(mob/user)
if(!user || !anchored)
return
if(!user.IsAdvancedToolUser())
to_chat(user, "<span class='warning'>You don't have the dexterity to do this!</span>")
return 1
user.set_machine(src)
song.interact(user)
/obj/structure/piano/wrench_act(mob/living/user, obj/item/I)
default_unfasten_wrench(user, I, 40)
return TRUE
#define MUSICIAN_HEARCHECK_MINDELAY 4
#define MUSIC_MAXLINES 600
#define MUSIC_MAXLINECHARS 50
/datum/song
var/name = "Untitled"
var/list/lines = new()
var/tempo = 5 // delay between notes
var/playing = 0 // if we're playing
var/help = 0 // if help is open
var/edit = 1 // if we're in editing mode
var/repeat = 0 // number of times remaining to repeat
var/max_repeats = 10 // maximum times we can repeat
var/instrumentDir = "piano" // the folder with the sounds
var/instrumentExt = "ogg" // the file extension
var/obj/instrumentObj = null // the associated obj playing the sound
var/last_hearcheck = 0
var/list/hearing_mobs
/datum/song/New(dir, obj, ext = "ogg")
tempo = sanitize_tempo(tempo)
instrumentDir = dir
instrumentObj = obj
instrumentExt = ext
/datum/song/Destroy()
instrumentObj = null
return ..()
// note is a number from 1-7 for A-G
// acc is either "b", "n", or "#"
// oct is 1-8 (or 9 for C)
/datum/song/proc/playnote(note, acc as text, oct)
// handle accidental -> B<>C of E<>F
if(acc == "b" && (note == 3 || note == 6)) // C or F
if(note == 3)
oct--
note--
acc = "n"
else if(acc == "#" && (note == 2 || note == 5)) // B or E
if(note == 2)
oct++
note++
acc = "n"
else if(acc == "#" && (note == 7)) //G#
note = 1
acc = "b"
else if(acc == "#") // mass convert all sharps to flats, octave jump already handled
acc = "b"
note++
// check octave, C is allowed to go to 9
if(oct < 1 || (note == 3 ? oct > 9 : oct > 8))
return
// now generate name
var/soundfile = "sound/instruments/[instrumentDir]/[ascii2text(note+64)][acc][oct].[instrumentExt]"
soundfile = file(soundfile)
// make sure the note exists
if(!fexists(soundfile))
return
// and play
var/turf/source = get_turf(instrumentObj)
if((world.time - MUSICIAN_HEARCHECK_MINDELAY) > last_hearcheck)
LAZYCLEARLIST(hearing_mobs)
for(var/mob/M in get_hearers_in_view(15, source))
if(!M.client || !(M.client.prefs.toggles & SOUND_INSTRUMENTS))
continue
LAZYADD(hearing_mobs, M)
last_hearcheck = world.time
var/sound/music_played = sound(soundfile)
for(var/i in hearing_mobs)
var/mob/M = i
M.playsound_local(source, null, 100, falloff = 5, S = music_played)
/datum/song/proc/updateDialog(mob/user)
instrumentObj.updateDialog() // assumes it's an object in world, override if otherwise
/datum/song/proc/shouldStopPlaying(mob/user)
if(instrumentObj)
if(!user.canUseTopic(instrumentObj))
return TRUE
return !instrumentObj.anchored // add special cases to stop in subclasses
else
return TRUE
/datum/song/proc/playsong(mob/user)
while(repeat >= 0)
var/cur_oct[7]
var/cur_acc[7]
for(var/i = 1 to 7)
cur_oct[i] = 3
cur_acc[i] = "n"
for(var/line in lines)
for(var/beat in splittext(lowertext(line), ","))
var/list/notes = splittext(beat, "/")
for(var/note in splittext(notes[1], "-"))
if(!playing || shouldStopPlaying(user))//If the instrument is playing, or special case
playing = FALSE
hearing_mobs = null
return
if(!length(note))
continue
var/cur_note = text2ascii(note) - 96
if(cur_note < 1 || cur_note > 7)
continue
for(var/i=2 to length(note))
var/ni = copytext(note,i,i+1)
if(!text2num(ni))
if(ni == "#" || ni == "b" || ni == "n")
cur_acc[cur_note] = ni
else if(ni == "s")
cur_acc[cur_note] = "#" // so shift is never required
else
cur_oct[cur_note] = text2num(ni)
if(user.dizziness > 0 && prob(user.dizziness / 2))
cur_note = CLAMP(cur_note + rand(round(-user.dizziness / 10), round(user.dizziness / 10)), 1, 7)
if(user.dizziness > 0 && prob(user.dizziness / 5))
if(prob(30))
cur_acc[cur_note] = "#"
else if(prob(42))
cur_acc[cur_note] = "b"
else if(prob(75))
cur_acc[cur_note] = "n"
playnote(cur_note, cur_acc[cur_note], cur_oct[cur_note])
if(notes.len >= 2 && text2num(notes[2]))
sleep(sanitize_tempo(tempo / text2num(notes[2])))
else
sleep(tempo)
repeat--
hearing_mobs = null
playing = FALSE
repeat = 0
updateDialog(user)
/datum/song/proc/interact(mob/user)
var/dat = ""
if(lines.len > 0)
dat += "<H3>Playback</H3>"
if(!playing)
dat += "<A href='?src=[REF(src)];play=1'>Play</A> <SPAN CLASS='linkOn'>Stop</SPAN><BR><BR>"
dat += "Repeat Song: "
dat += repeat > 0 ? "<A href='?src=[REF(src)];repeat=-10'>-</A><A href='?src=[REF(src)];repeat=-1'>-</A>" : "<SPAN CLASS='linkOff'>-</SPAN><SPAN CLASS='linkOff'>-</SPAN>"
dat += " [repeat] times "
dat += repeat < max_repeats ? "<A href='?src=[REF(src)];repeat=1'>+</A><A href='?src=[REF(src)];repeat=10'>+</A>" : "<SPAN CLASS='linkOff'>+</SPAN><SPAN CLASS='linkOff'>+</SPAN>"
dat += "<BR>"
else
dat += "<SPAN CLASS='linkOn'>Play</SPAN> <A href='?src=[REF(src)];stop=1'>Stop</A><BR>"
dat += "Repeats left: <B>[repeat]</B><BR>"
if(!edit)
dat += "<BR><B><A href='?src=[REF(src)];edit=2'>Show Editor</A></B><BR>"
else
dat += "<H3>Editing</H3>"
dat += "<B><A href='?src=[REF(src)];edit=1'>Hide Editor</A></B>"
dat += " <A href='?src=[REF(src)];newsong=1'>Start a New Song</A>"
dat += " <A href='?src=[REF(src)];import=1'>Import a Song</A><BR><BR>"
var/bpm = round(600 / tempo)
dat += "Tempo: <A href='?src=[REF(src)];tempo=[world.tick_lag]'>-</A> [bpm] BPM <A href='?src=[REF(src)];tempo=-[world.tick_lag]'>+</A><BR><BR>"
var/linecount = 0
for(var/line in lines)
linecount += 1
dat += "Line [linecount]: <A href='?src=[REF(src)];modifyline=[linecount]'>Edit</A> <A href='?src=[REF(src)];deleteline=[linecount]'>X</A> [line]<BR>"
dat += "<A href='?src=[REF(src)];newline=1'>Add Line</A><BR><BR>"
if(help)
dat += "<B><A href='?src=[REF(src)];help=1'>Hide Help</A></B><BR>"
dat += {"
Lines are a series of chords, separated by commas (,), each with notes separated by hyphens (-).<br>
Every note in a chord will play together, with chord timed by the tempo.<br>
<br>
Notes are played by the names of the note, and optionally, the accidental, and/or the octave number.<br>
By default, every note is natural and in octave 3. Defining otherwise is remembered for each note.<br>
Example: <i>C,D,E,F,G,A,B</i> will play a C major scale.<br>
After a note has an accidental placed, it will be remembered: <i>C,C4,C,C3</i> is <i>C3,C4,C4,C3</i><br>
Chords can be played simply by seperating each note with a hyphon: <i>A-C#,Cn-E,E-G#,Gn-B</i><br>
A pause may be denoted by an empty chord: <i>C,E,,C,G</i><br>
To make a chord be a different time, end it with /x, where the chord length will be length<br>
defined by tempo / x: <i>C,G/2,E/4</i><br>
Combined, an example is: <i>E-E4/4,F#/2,G#/8,B/8,E3-E4/4</i>
<br>
Lines may be up to [MUSIC_MAXLINECHARS] characters.<br>
A song may only contain up to [MUSIC_MAXLINES] lines.<br>
"}
else
dat += "<B><A href='?src=[REF(src)];help=2'>Show Help</A></B><BR>"
var/datum/browser/popup = new(user, "instrument", instrumentObj.name, 700, 500)
popup.set_content(dat)
popup.set_title_image(user.browse_rsc_icon(instrumentObj.icon, instrumentObj.icon_state))
popup.open()
/datum/song/proc/ParseSong(text)
set waitfor = FALSE
//split into lines
lines = splittext(text, "\n")
if(lines.len)
if(copytext(lines[1],1,6) == "BPM: ")
tempo = sanitize_tempo(600 / text2num(copytext(lines[1],6)))
lines.Cut(1,2)
else
tempo = sanitize_tempo(5) // default 120 BPM
if(lines.len > MUSIC_MAXLINES)
to_chat(usr, "Too many lines!")
lines.Cut(MUSIC_MAXLINES + 1)
var/linenum = 1
for(var/l in lines)
if(length(l) > MUSIC_MAXLINECHARS)
to_chat(usr, "Line [linenum] too long!")
lines.Remove(l)
else
linenum++
updateDialog(usr) // make sure updates when complete
/datum/song/Topic(href, href_list)
if(!usr.canUseTopic(instrumentObj))
usr << browse(null, "window=instrument")
usr.unset_machine()
return
instrumentObj.add_fingerprint(usr)
if(href_list["newsong"])
lines = new()
tempo = sanitize_tempo(5) // default 120 BPM
name = ""
else if(href_list["import"])
var/t = ""
do
t = html_encode(input(usr, "Please paste the entire song, formatted:", text("[]", name), t) as message)
if(!in_range(instrumentObj, usr))
return
if(length(t) >= MUSIC_MAXLINES * MUSIC_MAXLINECHARS)
var/cont = input(usr, "Your message is too long! Would you like to continue editing it?", "", "yes") in list("yes", "no")
if(cont == "no")
break
while(length(t) > MUSIC_MAXLINES * MUSIC_MAXLINECHARS)
ParseSong(t)
else if(href_list["help"])
help = text2num(href_list["help"]) - 1
else if(href_list["edit"])
edit = text2num(href_list["edit"]) - 1
if(href_list["repeat"]) //Changing this from a toggle to a number of repeats to avoid infinite loops.
if(playing)
return //So that people cant keep adding to repeat. If the do it intentionally, it could result in the server crashing.
repeat += round(text2num(href_list["repeat"]))
if(repeat < 0)
repeat = 0
if(repeat > max_repeats)
repeat = max_repeats
else if(href_list["tempo"])
tempo = sanitize_tempo(tempo + text2num(href_list["tempo"]))
else if(href_list["play"])
playing = TRUE
spawn()
playsong(usr)
else if(href_list["newline"])
var/newline = html_encode(input("Enter your line: ", instrumentObj.name) as text|null)
if(!newline || !in_range(instrumentObj, usr))
return
if(lines.len > MUSIC_MAXLINES)
return
if(length(newline) > MUSIC_MAXLINECHARS)
newline = copytext(newline, 1, MUSIC_MAXLINECHARS)
lines.Add(newline)
else if(href_list["deleteline"])
var/num = round(text2num(href_list["deleteline"]))
if(num > lines.len || num < 1)
return
lines.Cut(num, num+1)
else if(href_list["modifyline"])
var/num = round(text2num(href_list["modifyline"]),1)
var/content = html_encode(input("Enter your line: ", instrumentObj.name, lines[num]) as text|null)
if(!content || !in_range(instrumentObj, usr))
return
if(length(content) > MUSIC_MAXLINECHARS)
content = copytext(content, 1, MUSIC_MAXLINECHARS)
if(num > lines.len || num < 1)
return
lines[num] = content
else if(href_list["stop"])
playing = FALSE
hearing_mobs = null
updateDialog(usr)
return
/datum/song/proc/sanitize_tempo(new_tempo)
new_tempo = abs(new_tempo)
return max(round(new_tempo, world.tick_lag), world.tick_lag)
// subclass for handheld instruments, like violin
/datum/song/handheld
/datum/song/handheld/updateDialog(mob/user)
instrumentObj.interact(user)
/datum/song/handheld/shouldStopPlaying()
if(instrumentObj)
return !isliving(instrumentObj.loc)
else
return TRUE
//////////////////////////////////////////////////////////////////////////
/obj/structure/piano
name = "space minimoog"
icon = 'icons/obj/musician.dmi'
icon_state = "minimoog"
anchored = TRUE
density = TRUE
var/datum/song/song
/obj/structure/piano/unanchored
anchored = FALSE
/obj/structure/piano/New()
..()
song = new("piano", src)
if(prob(50) && icon_state == initial(icon_state))
name = "space minimoog"
desc = "This is a minimoog, like a space piano, but more spacey!"
icon_state = "minimoog"
else
name = "space piano"
desc = "This is a space piano, like a regular piano, but always in tune! Even if the musician isn't."
icon_state = "piano"
/obj/structure/piano/Destroy()
qdel(song)
song = null
return ..()
/obj/structure/piano/Initialize(mapload)
. = ..()
if(mapload)
song.tempo = song.sanitize_tempo(song.tempo) // tick_lag isn't set when the map is loaded
/obj/structure/piano/attack_hand(mob/user)
. = ..()
if(.)
return
interact(user)
/obj/structure/piano/attack_paw(mob/user)
return attack_hand(user)
/obj/structure/piano/interact(mob/user)
ui_interact(user)
/obj/structure/piano/ui_interact(mob/user)
if(!user || !anchored)
return
if(!user.IsAdvancedToolUser())
to_chat(user, "<span class='warning'>You don't have the dexterity to do this!</span>")
return 1
user.set_machine(src)
song.interact(user)
/obj/structure/piano/wrench_act(mob/living/user, obj/item/I)
default_unfasten_wrench(user, I, 40)
return TRUE
+16 -8
View File
@@ -28,19 +28,21 @@ GLOBAL_LIST_INIT(freqtospan, list(
language = get_default_language()
send_speech(message, 7, src, , spans, message_language=language)
/atom/movable/proc/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode)
SEND_SIGNAL(src, COMSIG_MOVABLE_HEAR, message, speaker, message_language, raw_message, radio_freq, spans, message_mode)
/atom/movable/proc/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
SEND_SIGNAL(src, COMSIG_MOVABLE_HEAR, args)
/atom/movable/proc/can_speak()
return 1
/atom/movable/proc/send_speech(message, range = 7, obj/source = src, bubble_type, list/spans, datum/language/message_language = null, message_mode)
var/rendered = compose_message(src, message_language, message, , spans, message_mode)
/atom/movable/proc/send_speech(message, range = 7, atom/movable/source = src, bubble_type, list/spans, datum/language/message_language = null, message_mode)
var/rendered = compose_message(src, message_language, message, , spans, message_mode, source)
for(var/_AM in get_hearers_in_view(range, source))
var/atom/movable/AM = _AM
AM.Hear(rendered, src, message_language, message, , spans, message_mode)
AM.Hear(rendered, src, message_language, message, , spans, message_mode, source)
/atom/movable/proc/compose_message(atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode, face_name = FALSE)
/atom/movable/proc/compose_message(atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode, face_name = FALSE, atom/movable/source)
if(!source)
source = speaker
//This proc uses text() because it is faster than appending strings. Thanks BYOND.
//Basic span
var/spanpart1 = "<span class='[radio_freq ? get_radio_span(radio_freq) : "game say"]'>"
@@ -136,8 +138,14 @@ GLOBAL_LIST_INIT(freqtospan, list(
return returntext
return "[copytext("[freq]", 1, 4)].[copytext("[freq]", 4, 5)]"
/proc/attach_spans(input, list/spans)
return "[message_spans_start(spans)][input]</span>"
/atom/movable/proc/attach_spans(input, list/spans)
var/customsayverb = findtext(input, "*")
if(customsayverb)
input = capitalize(copytext(input, customsayverb+1))
if(input)
return "[message_spans_start(spans)][input]</span>"
else
return
/proc/message_spans_start(list/spans)
var/output = "<span class='"
+2991 -2991
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -98,7 +98,7 @@
else
to_chat(M, message)
/mob/camera/eminence/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode)
/mob/camera/eminence/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
. = ..()
if(is_reebe(z) || is_servant_of_ratvar(speaker) || GLOB.ratvar_approaches || GLOB.ratvar_awakens) //Away from Reebe, the Eminence can't hear anything
to_chat(src, message)
@@ -118,7 +118,7 @@ the new instance inside the host to be updated to the template's stats.
follow_next(Dir & NORTHWEST)
last_move_tick = world.time
/mob/camera/disease/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode)
/mob/camera/disease/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
. = ..()
var/atom/movable/to_follow = speaker
if(radio_freq)
@@ -130,7 +130,7 @@ the new instance inside the host to be updated to the template's stats.
else
link = ""
// Recompose the message, because it's scrambled by default
message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode)
message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode, FALSE, source)
to_chat(src, "[link] [message]")
@@ -49,6 +49,12 @@
A.malf_picker.remove_malf_verbs(A)
qdel(A.malf_picker)
/datum/antagonist/traitor/proc/handle_hearing(datum/source, list/hearing_args)
var/message = hearing_args[HEARING_MESSAGE]
message = GLOB.syndicate_code_phrase_regex.Replace(message, "<span class='blue'>$1</span>")
message = GLOB.syndicate_code_response_regex.Replace(message, "<span class='red'>$1</span>")
hearing_args[HEARING_MESSAGE] = message
SSticker.mode.traitors -= owner
if(!silent && owner.current)
to_chat(owner.current,"<span class='userdanger'> You are no longer the [special_role]! </span>")
+101 -101
View File
@@ -1,101 +1,101 @@
#define INCLUSIVE_MODE 1
#define EXCLUSIVE_MODE 2
#define RECOGNIZER_MODE 3
#define VOICE_SENSOR_MODE 4
/obj/item/assembly/voice
name = "voice analyzer"
desc = "A small electronic device able to record a voice sample, and send a signal when that sample is repeated."
icon_state = "voice"
materials = list(MAT_METAL=500, MAT_GLASS=50)
flags_1 = HEAR_1
attachable = TRUE
verb_say = "beeps"
verb_ask = "beeps"
verb_exclaim = "beeps"
var/listening = FALSE
var/recorded = "" //the activation message
var/mode = 1
var/static/list/modes = list("inclusive",
"exclusive",
"recognizer",
"voice sensor")
/obj/item/assembly/voice/examine(mob/user)
. = ..()
. += "<span class='notice'>Use a multitool to swap between \"inclusive\", \"exclusive\", \"recognizer\", and \"voice sensor\" mode.</span>"
/obj/item/assembly/voice/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode)
. = ..()
if(speaker == src)
return
if(listening && !radio_freq)
record_speech(speaker, raw_message, message_language)
else
if(check_activation(speaker, raw_message))
addtimer(CALLBACK(src, .proc/pulse, 0), 10)
/obj/item/assembly/voice/proc/record_speech(atom/movable/speaker, raw_message, datum/language/message_language)
switch(mode)
if(INCLUSIVE_MODE)
recorded = raw_message
listening = FALSE
say("Activation message is '[recorded]'.", message_language)
if(EXCLUSIVE_MODE)
recorded = raw_message
listening = FALSE
say("Activation message is '[recorded]'.", message_language)
if(RECOGNIZER_MODE)
recorded = speaker.GetVoice()
listening = FALSE
say("Your voice pattern is saved.", message_language)
if(VOICE_SENSOR_MODE)
if(length(raw_message))
addtimer(CALLBACK(src, .proc/pulse, 0), 10)
/obj/item/assembly/voice/proc/check_activation(atom/movable/speaker, raw_message)
. = FALSE
switch(mode)
if(INCLUSIVE_MODE)
if(findtext(raw_message, recorded))
. = TRUE
if(EXCLUSIVE_MODE)
if(raw_message == recorded)
. = TRUE
if(RECOGNIZER_MODE)
if(speaker.GetVoice() == recorded)
. = TRUE
if(VOICE_SENSOR_MODE)
if(length(raw_message))
. = TRUE
/obj/item/assembly/voice/multitool_act(mob/living/user, obj/item/I)
mode %= modes.len
mode++
to_chat(user, "<span class='notice'>You set [src] into [modes[mode]] mode.</span>")
listening = FALSE
recorded = ""
return TRUE
/obj/item/assembly/voice/activate()
if(!secured || holder)
return FALSE
listening = !listening
say("[listening ? "Now" : "No longer"] recording input.")
return TRUE
/obj/item/assembly/voice/attack_self(mob/user)
if(!user)
return FALSE
activate()
return TRUE
/obj/item/assembly/voice/toggle_secure()
. = ..()
listening = FALSE
#undef INCLUSIVE_MODE
#undef EXCLUSIVE_MODE
#undef RECOGNIZER_MODE
#undef VOICE_SENSOR_MODE
#define INCLUSIVE_MODE 1
#define EXCLUSIVE_MODE 2
#define RECOGNIZER_MODE 3
#define VOICE_SENSOR_MODE 4
/obj/item/assembly/voice
name = "voice analyzer"
desc = "A small electronic device able to record a voice sample, and send a signal when that sample is repeated."
icon_state = "voice"
materials = list(MAT_METAL=500, MAT_GLASS=50)
flags_1 = HEAR_1
attachable = TRUE
verb_say = "beeps"
verb_ask = "beeps"
verb_exclaim = "beeps"
var/listening = FALSE
var/recorded = "" //the activation message
var/mode = 1
var/static/list/modes = list("inclusive",
"exclusive",
"recognizer",
"voice sensor")
/obj/item/assembly/voice/examine(mob/user)
. = ..()
. += "<span class='notice'>Use a multitool to swap between \"inclusive\", \"exclusive\", \"recognizer\", and \"voice sensor\" mode.</span>"
/obj/item/assembly/voice/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
. = ..()
if(speaker == src)
return
if(listening && !radio_freq)
record_speech(speaker, raw_message, message_language)
else
if(check_activation(speaker, raw_message))
addtimer(CALLBACK(src, .proc/pulse, 0), 10)
/obj/item/assembly/voice/proc/record_speech(atom/movable/speaker, raw_message, datum/language/message_language)
switch(mode)
if(INCLUSIVE_MODE)
recorded = raw_message
listening = FALSE
say("Activation message is '[recorded]'.", message_language)
if(EXCLUSIVE_MODE)
recorded = raw_message
listening = FALSE
say("Activation message is '[recorded]'.", message_language)
if(RECOGNIZER_MODE)
recorded = speaker.GetVoice()
listening = FALSE
say("Your voice pattern is saved.", message_language)
if(VOICE_SENSOR_MODE)
if(length(raw_message))
addtimer(CALLBACK(src, .proc/pulse, 0), 10)
/obj/item/assembly/voice/proc/check_activation(atom/movable/speaker, raw_message)
. = FALSE
switch(mode)
if(INCLUSIVE_MODE)
if(findtext(raw_message, recorded))
. = TRUE
if(EXCLUSIVE_MODE)
if(raw_message == recorded)
. = TRUE
if(RECOGNIZER_MODE)
if(speaker.GetVoice() == recorded)
. = TRUE
if(VOICE_SENSOR_MODE)
if(length(raw_message))
. = TRUE
/obj/item/assembly/voice/multitool_act(mob/living/user, obj/item/I)
mode %= modes.len
mode++
to_chat(user, "<span class='notice'>You set [src] into [modes[mode]] mode.</span>")
listening = FALSE
recorded = ""
return TRUE
/obj/item/assembly/voice/activate()
if(!secured || holder)
return FALSE
listening = !listening
say("[listening ? "Now" : "No longer"] recording input.")
return TRUE
/obj/item/assembly/voice/attack_self(mob/user)
if(!user)
return FALSE
activate()
return TRUE
/obj/item/assembly/voice/toggle_secure()
. = ..()
listening = FALSE
#undef INCLUSIVE_MODE
#undef EXCLUSIVE_MODE
#undef RECOGNIZER_MODE
#undef VOICE_SENSOR_MODE
+1 -1
View File
@@ -195,7 +195,7 @@
name = "Loom"
desc = "A large pre-made loom."
cost = 1000
contains = list(/obj/structure/loom)
contains = list(/obj/structure/loom/unanchored)
crate_name = "loom crate"
crate_type = /obj/structure/closet/crate/large
+3
View File
@@ -111,3 +111,6 @@
/datum/client_colour/monochrome
colour = list(rgb(77,77,77), rgb(150,150,150), rgb(28,28,28), rgb(0,0,0))
priority = INFINITY //we can't see colors anyway!
/datum/client_colour/monochrome/trance
priority = 1
+1 -1
View File
@@ -65,7 +65,7 @@
/obj/item/clothing/head/helmet/space/hardsuit/proc/display_visor_message(var/msg)
var/mob/wearer = loc
if(msg && ishuman(wearer))
wearer.show_message("[icon2html(src, wearer)]<b><span class='robot'>[msg]</span></b>", 1)
wearer.show_message("[icon2html(src, wearer)]<b><span class='robot'>[msg]</span></b>", MSG_VISUAL)
/obj/item/clothing/head/helmet/space/hardsuit/rad_act(severity)
. = ..()
@@ -160,6 +160,28 @@
tastes = list("brains" = 1, "meat" = 1)
foodtype = RAW | MEAT | TOXIC
/obj/item/reagent_containers/food/snacks/carpmeat/aquatic
name = "fillet"
desc = "A fillet of one of the local water dwelling species."
/obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/ipc
icon_state = "ipcmeat"
desc = "Gross robot meat."
filling_color = "#000000"
tastes = list("metal" = 1)
/obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/avian
desc = "Tastes like chicken, that's because it is!"
icon_state = "birdmeat"
filling_color = "#BF896B"
tastes = list("chicken" = 1)
/obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/mammal
desc = "Tastes sweet... reminds you vaguely of chicken."
filling_color = "#6B8E23"
tastes = list("brains" = 1, "meat" = 1)
////////////////////////////////////// OTHER MEATS ////////////////////////////////////////////////////////
+1 -1
View File
@@ -32,7 +32,7 @@
var/cotton_name = "raw cotton"
/obj/item/grown/cotton/attack_self(mob/user)
user.show_message("<span class='notice'>You pull some [cotton_name] out of the [name]!</span>", 1)
user.show_message("<span class='notice'>You pull some [cotton_name] out of the [name]!</span>", MSG_VISUAL)
var/seed_modifier = 0
if(seed)
seed_modifier = round(seed.potency / 25)
+2 -2
View File
@@ -28,8 +28,8 @@
wine_power = 20
/obj/item/reagent_containers/food/snacks/grown/pumpkin/attackby(obj/item/W as obj, mob/user as mob, params)
if(W.is_sharp())
user.show_message("<span class='notice'>You carve a face into [src]!</span>", 1)
if(W.get_sharpness())
user.show_message("<span class='notice'>You carve a face into [src]!</span>", MSG_VISUAL)
new /obj/item/clothing/head/hardhat/pumpkinhead(user.loc)
qdel(src)
return
+1 -1
View File
@@ -51,7 +51,7 @@
/obj/item/grown/log/attackby(obj/item/W, mob/user, params)
if(W.sharpness)
user.show_message("<span class='notice'>You make [plank_name] out of \the [src]!</span>", 1)
user.show_message("<span class='notice'>You make [plank_name] out of \the [src]!</span>", MSG_VISUAL)
var/seed_modifier = 0
if(seed)
seed_modifier = round(seed.potency / 25)
@@ -859,7 +859,7 @@
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
power_draw_per_use = 5
/obj/item/integrated_circuit/input/microphone/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, spans, message_mode)
/obj/item/integrated_circuit/input/microphone/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, spans, message_mode, atom/movable/source)
. = ..()
var/translated = FALSE
if(speaker && message)
+43 -43
View File
@@ -1,43 +1,43 @@
/mob/dead/observer/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN))
if (!message)
return
var/message_mode = get_message_mode(message)
if(client && (message_mode == MODE_ADMIN || message_mode == MODE_DEADMIN))
message = copytext(message, 3)
if(findtext(message, " ", 1, 2))
message = copytext(message, 2)
if(message_mode == MODE_ADMIN)
client.cmd_admin_say(message)
else if(message_mode == MODE_DEADMIN)
client.dsay(message)
return
src.log_talk(message, LOG_SAY, tag="ghost")
if(check_emote(message))
return
. = say_dead(message)
/mob/dead/observer/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode)
. = ..()
var/atom/movable/to_follow = speaker
if(radio_freq)
var/atom/movable/virtualspeaker/V = speaker
if(isAI(V.source))
var/mob/living/silicon/ai/S = V.source
to_follow = S.eyeobj
else
to_follow = V.source
var/link = FOLLOW_LINK(src, to_follow)
// Create map text prior to modifying message for goonchat
if (client?.prefs.chat_on_map && (client.prefs.see_chat_non_mob || ismob(speaker)))
create_chat_message(speaker, message_language, raw_message, spans, message_mode)
// Recompose the message, because it's scrambled by default
message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode)
to_chat(src, "[link] [message]")
/mob/dead/observer/say(message, bubble_type, var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN))
if (!message)
return
var/message_mode = get_message_mode(message)
if(client && (message_mode == MODE_ADMIN || message_mode == MODE_DEADMIN))
message = copytext(message, 3)
if(findtext(message, " ", 1, 2))
message = copytext(message, 2)
if(message_mode == MODE_ADMIN)
client.cmd_admin_say(message)
else if(message_mode == MODE_DEADMIN)
client.dsay(message)
return
src.log_talk(message, LOG_SAY, tag="ghost")
if(check_emote(message))
return
. = say_dead(message)
/mob/dead/observer/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
. = ..()
var/atom/movable/to_follow = speaker
if(radio_freq)
var/atom/movable/virtualspeaker/V = speaker
if(isAI(V.source))
var/mob/living/silicon/ai/S = V.source
to_follow = S.eyeobj
else
to_follow = V.source
var/link = FOLLOW_LINK(src, to_follow)
// Create map text prior to modifying message for goonchat
if (client?.prefs.chat_on_map && (client.prefs.see_chat_non_mob || ismob(speaker)))
create_chat_message(speaker, message_language, raw_message, spans, message_mode)
// Recompose the message, because it's scrambled by default
message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode, FALSE, source)
to_chat(src, "[link] [message]")
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+116 -116
View File
@@ -1,116 +1,116 @@
/mob/living/carbon/human/say_mod(input, message_mode)
verb_say = dna.species.say_mod
switch(slurring)
if(10 to 25)
return "jumbles"
if(25 to 50)
return "slurs"
if(50 to INFINITY)
return "garbles"
else
. = ..()
/mob/living/carbon/human/GetVoice()
if(istype(wear_mask, /obj/item/clothing/mask/chameleon))
var/obj/item/clothing/mask/chameleon/V = wear_mask
if(V.vchange && wear_id)
var/obj/item/card/id/idcard = wear_id.GetID()
if(istype(idcard))
return idcard.registered_name
else
return real_name
else
return real_name
if(mind)
var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling)
if(changeling && changeling.mimicing )
return changeling.mimicing
if(GetSpecialVoice())
return GetSpecialVoice()
return real_name
/mob/living/carbon/human/IsVocal()
// how do species that don't breathe talk? magic, that's what.
if(!HAS_TRAIT_FROM(src, TRAIT_NOBREATH, SPECIES_TRAIT) && !getorganslot(ORGAN_SLOT_LUNGS))
return FALSE
if(mind)
return !mind.miming
return TRUE
/mob/living/carbon/human/proc/SetSpecialVoice(new_voice)
if(new_voice)
special_voice = new_voice
return
/mob/living/carbon/human/proc/UnsetSpecialVoice()
special_voice = ""
return
/mob/living/carbon/human/proc/GetSpecialVoice()
return special_voice
/mob/living/carbon/human/binarycheck()
if(ears)
var/obj/item/radio/headset/dongle = ears
if(!istype(dongle))
return FALSE
if(dongle.translate_binary)
return TRUE
/mob/living/carbon/human/radio(message, message_mode, list/spans, language)
. = ..()
if(.)
return
switch(message_mode)
if(MODE_HEADSET)
if (ears)
ears.talk_into(src, message, , spans, language)
return ITALICS | REDUCE_RANGE
if(MODE_DEPARTMENT)
if (ears)
ears.talk_into(src, message, message_mode, spans, language)
return ITALICS | REDUCE_RANGE
if(message_mode in GLOB.radiochannels)
if(ears)
ears.talk_into(src, message, message_mode, spans, language)
return ITALICS | REDUCE_RANGE
return 0
/mob/living/carbon/human/get_alt_name()
if(name != GetVoice())
return " (as [get_id_name("Unknown")])"
/mob/living/carbon/human/proc/forcesay(list/append) //this proc is at the bottom of the file because quote fuckery makes notepad++ cri
if(stat == CONSCIOUS)
if(client)
var/virgin = 1 //has the text been modified yet?
var/temp = winget(client, "input", "text")
if(findtextEx(temp, "Say \"", 1, 7) && length(temp) > 5) //"case sensitive means
temp = replacetext(temp, ";", "") //general radio
if(findtext(trim_left(temp), ":", 6, 7)) //dept radio
temp = copytext(trim_left(temp), 8)
virgin = 0
if(virgin)
temp = copytext(trim_left(temp), 6) //normal speech
virgin = 0
while(findtext(trim_left(temp), ":", 1, 2)) //dept radio again (necessary)
temp = copytext(trim_left(temp), 3)
if(findtext(temp, "*", 1, 2)) //emotes
return
var/trimmed = trim_left(temp)
if(length(trimmed))
if(append)
temp += pick(append)
say(temp)
winset(client, "input", "text=[null]")
/mob/living/carbon/human/say_mod(input, message_mode)
verb_say = dna.species.say_mod
. = ..()
if(message_mode != MODE_CUSTOM_SAY && message_mode != MODE_WHISPER_CRIT)
switch(slurring)
if(10 to 25)
return "jumbles"
if(25 to 50)
return "slurs"
if(50 to INFINITY)
return "garbles"
/mob/living/carbon/human/GetVoice()
if(istype(wear_mask, /obj/item/clothing/mask/chameleon))
var/obj/item/clothing/mask/chameleon/V = wear_mask
if(V.vchange && wear_id)
var/obj/item/card/id/idcard = wear_id.GetID()
if(istype(idcard))
return idcard.registered_name
else
return real_name
else
return real_name
if(mind)
var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling)
if(changeling && changeling.mimicing )
return changeling.mimicing
if(GetSpecialVoice())
return GetSpecialVoice()
return real_name
/mob/living/carbon/human/IsVocal()
// how do species that don't breathe talk? magic, that's what.
if(!HAS_TRAIT_FROM(src, TRAIT_NOBREATH, SPECIES_TRAIT) && !getorganslot(ORGAN_SLOT_LUNGS))
return FALSE
if(mind)
return !mind.miming
return TRUE
/mob/living/carbon/human/proc/SetSpecialVoice(new_voice)
if(new_voice)
special_voice = new_voice
return
/mob/living/carbon/human/proc/UnsetSpecialVoice()
special_voice = ""
return
/mob/living/carbon/human/proc/GetSpecialVoice()
return special_voice
/mob/living/carbon/human/binarycheck()
if(ears)
var/obj/item/radio/headset/dongle = ears
if(!istype(dongle))
return FALSE
if(dongle.translate_binary)
return TRUE
/mob/living/carbon/human/radio(message, message_mode, list/spans, language)
. = ..()
if(.)
return
switch(message_mode)
if(MODE_HEADSET)
if (ears)
ears.talk_into(src, message, , spans, language)
return ITALICS | REDUCE_RANGE
if(MODE_DEPARTMENT)
if (ears)
ears.talk_into(src, message, message_mode, spans, language)
return ITALICS | REDUCE_RANGE
if(message_mode in GLOB.radiochannels)
if(ears)
ears.talk_into(src, message, message_mode, spans, language)
return ITALICS | REDUCE_RANGE
return 0
/mob/living/carbon/human/get_alt_name()
if(name != GetVoice())
return " (as [get_id_name("Unknown")])"
/mob/living/carbon/human/proc/forcesay(list/append) //this proc is at the bottom of the file because quote fuckery makes notepad++ cri
if(stat == CONSCIOUS)
if(client)
var/virgin = 1 //has the text been modified yet?
var/temp = winget(client, "input", "text")
if(findtextEx(temp, "Say \"", 1, 7) && length(temp) > 5) //"case sensitive means
temp = replacetext(temp, ";", "") //general radio
if(findtext(trim_left(temp), ":", 6, 7)) //dept radio
temp = copytext(trim_left(temp), 8)
virgin = 0
if(virgin)
temp = copytext(trim_left(temp), 6) //normal speech
virgin = 0
while(findtext(trim_left(temp), ":", 1, 2)) //dept radio again (necessary)
temp = copytext(trim_left(temp), 3)
if(findtext(temp, "*", 1, 2)) //emotes
return
var/trimmed = trim_left(temp)
if(length(trimmed))
if(append)
temp += pick(append)
say(temp)
winset(client, "input", "text=[null]")
@@ -1519,6 +1519,7 @@ GLOBAL_LIST_EMPTY(roundstart_races)
target.lastattacker = user.real_name
target.lastattackerckey = user.ckey
user.dna.species.spec_unarmedattacked(user, target)
if(user.limb_destroyer)
target.dismembering_strike(user, affecting.body_zone)
@@ -1532,6 +1533,9 @@ GLOBAL_LIST_EMPTY(roundstart_races)
else if(target.lying)
target.forcesay(GLOB.hit_appends)
/datum/species/proc/spec_unarmedattacked(mob/living/carbon/human/user, mob/living/carbon/human/target)
return
/datum/species/proc/disarm(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
// CITADEL EDIT slap mouthy gits and booty
var/aim_for_mouth = user.zone_selected == "mouth"
@@ -861,27 +861,27 @@
if(M.stat == DEAD) //F
return
if(M == H)
H.show_message("<span class='narsiesmall'>You cringe with pain as your body rings around you!</span>", 2)
H.show_message("<span class='narsiesmall'>You cringe with pain as your body rings around you!</span>", MSG_AUDIBLE)
H.playsound_local(H, 'sound/effects/gong.ogg', 100, TRUE)
H.soundbang_act(2, 0, 100, 1)
H.jitteriness += 7
var/distance = max(0,get_dist(get_turf(H),get_turf(M)))
switch(distance)
if(0 to 1)
M.show_message("<span class='narsiesmall'>GONG!</span>", 2)
M.show_message("<span class='narsiesmall'>GONG!</span>", MSG_AUDIBLE)
M.playsound_local(H, 'sound/effects/gong.ogg', 100, TRUE)
M.soundbang_act(1, 0, 30, 3)
M.confused += 10
M.jitteriness += 4
SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "gonged", /datum/mood_event/loud_gong)
if(2 to 3)
M.show_message("<span class='cult'>GONG!</span>", 2)
M.show_message("<span class='cult'>GONG!</span>", MSG_AUDIBLE)
M.playsound_local(H, 'sound/effects/gong.ogg', 75, TRUE)
M.soundbang_act(1, 0, 15, 2)
M.jitteriness += 3
SEND_SIGNAL(M, COMSIG_ADD_MOOD_EVENT, "gonged", /datum/mood_event/loud_gong)
else
M.show_message("<span class='warning'>GONG!</span>", 2)
M.show_message("<span class='warning'>GONG!</span>", MSG_AUDIBLE)
M.playsound_local(H, 'sound/effects/gong.ogg', 50, TRUE)
@@ -943,6 +943,21 @@
grab_sound = 'sound/weapons/whipgrab.ogg'
attack_sound = 'sound/weapons/whip.ogg'
/datum/species/golem/durathread
name = "Durathread Golem"
id = "durathread golem"
prefix = "Durathread"
limbs_id = "d_golem"
special_names = list("Boll","Weave")
species_traits = list(NOBLOOD,NO_UNDERWEAR,NOEYES)
fixed_mut_color = null
inherent_traits = list(TRAIT_NOBREATH, TRAIT_RESISTCOLD,TRAIT_RESISTHIGHPRESSURE,TRAIT_RESISTLOWPRESSURE,TRAIT_NOGUNS,TRAIT_RADIMMUNE,TRAIT_PIERCEIMMUNE,TRAIT_NODISMEMBER)
info_text = "As a <span class='danger'>Durathread Golem</span>, your strikes will cause those your targets to start choking, but your woven body won't withstand fire as well."
/datum/species/golem/durathread/spec_unarmedattacked(mob/living/carbon/human/user, mob/living/carbon/human/target)
. = ..()
target.apply_status_effect(STATUS_EFFECT_CHOKINGSTRAND)
/datum/species/golem/bone
name = "Bone Golem"
id = "bone golem"
+1 -1
View File
@@ -109,7 +109,7 @@
var/datum/gas_mixture/breath
if(!getorganslot(ORGAN_SLOT_BREATHING_TUBE))
if(health <= HEALTH_THRESHOLD_FULLCRIT || (pulledby && pulledby.grab_state >= GRAB_KILL) || lungs.organ_flags & ORGAN_FAILING)
if(health <= HEALTH_THRESHOLD_FULLCRIT || (pulledby && pulledby.grab_state >= GRAB_KILL) || HAS_TRAIT(src, TRAIT_MAGIC_CHOKE) || (lungs && lungs.organ_flags & ORGAN_FAILING))
losebreath++ //You can't breath at all when in critical or when being choked, so you're going to miss a breath
else if(health <= crit_threshold)
+1 -15
View File
@@ -17,18 +17,4 @@
if(T)
. = T.could_speak_in_language(dt)
else
. = initial(dt.flags) & TONGUELESS_SPEECH
/mob/living/carbon/hear_intercept(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode)
. = ..()
if(!client)
return
for(var/T in get_traumas())
var/datum/brain_trauma/trauma = T
message = trauma.on_hear(message, speaker, message_language, raw_message, radio_freq)
if (src.mind.has_antag_datum(/datum/antagonist/traitor))
message = GLOB.syndicate_code_phrase_regex.Replace(message, "<span class='blue'>$1</span>")
message = GLOB.syndicate_code_response_regex.Replace(message, "<span class='red'>$1</span>")
return message
. = initial(dt.flags) & TONGUELESS_SPEECH
File diff suppressed because it is too large Load Diff
+437 -431
View File
@@ -1,431 +1,437 @@
GLOBAL_LIST_INIT(department_radio_prefixes, list(":", "."))
GLOBAL_LIST_INIT(department_radio_keys, list(
// Location
MODE_KEY_R_HAND = MODE_R_HAND,
MODE_KEY_L_HAND = MODE_L_HAND,
MODE_KEY_INTERCOM = MODE_INTERCOM,
// Department
MODE_KEY_DEPARTMENT = MODE_DEPARTMENT,
RADIO_KEY_COMMAND = RADIO_CHANNEL_COMMAND,
RADIO_KEY_SCIENCE = RADIO_CHANNEL_SCIENCE,
RADIO_KEY_MEDICAL = RADIO_CHANNEL_MEDICAL,
RADIO_KEY_ENGINEERING = RADIO_CHANNEL_ENGINEERING,
RADIO_KEY_SECURITY = RADIO_CHANNEL_SECURITY,
RADIO_KEY_SUPPLY = RADIO_CHANNEL_SUPPLY,
RADIO_KEY_SERVICE = RADIO_CHANNEL_SERVICE,
// Faction
RADIO_KEY_SYNDICATE = RADIO_CHANNEL_SYNDICATE,
RADIO_KEY_CENTCOM = RADIO_CHANNEL_CENTCOM,
// Admin
MODE_KEY_ADMIN = MODE_ADMIN,
MODE_KEY_DEADMIN = MODE_DEADMIN,
// Misc
RADIO_KEY_AI_PRIVATE = RADIO_CHANNEL_AI_PRIVATE, // AI Upload channel
MODE_KEY_VOCALCORDS = MODE_VOCALCORDS, // vocal cords, used by Voice of God
//kinda localization -- rastaf0
//same keys as above, but on russian keyboard layout. This file uses cp1251 as encoding.
// Location
"ê" = MODE_R_HAND,
"ä" = MODE_L_HAND,
"ø" = MODE_INTERCOM,
// Department
"ð" = MODE_DEPARTMENT,
"ñ" = RADIO_CHANNEL_COMMAND,
"ò" = RADIO_CHANNEL_SCIENCE,
"ü" = RADIO_CHANNEL_MEDICAL,
"ó" = RADIO_CHANNEL_ENGINEERING,
"û" = RADIO_CHANNEL_SECURITY,
"ã" = RADIO_CHANNEL_SUPPLY,
"ì" = RADIO_CHANNEL_SERVICE,
// Faction
"å" = RADIO_CHANNEL_SYNDICATE,
"í" = RADIO_CHANNEL_CENTCOM,
// Admin
"ç" = MODE_ADMIN,
"â" = MODE_ADMIN,
// Misc
"ù" = RADIO_CHANNEL_AI_PRIVATE,
"÷" = MODE_VOCALCORDS
))
/mob/living/proc/Ellipsis(original_msg, chance = 50, keep_words)
if(chance <= 0)
return "..."
if(chance >= 100)
return original_msg
var/list/words = splittext(original_msg," ")
var/list/new_words = list()
var/new_msg = ""
for(var/w in words)
if(prob(chance))
new_words += "..."
if(!keep_words)
continue
new_words += w
new_msg = jointext(new_words," ")
return new_msg
/mob/living/say(message, bubble_type,var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
var/static/list/crit_allowed_modes = list(MODE_WHISPER = TRUE, MODE_CHANGELING = TRUE, MODE_ALIEN = TRUE)
var/static/list/unconscious_allowed_modes = list(MODE_CHANGELING = TRUE, MODE_ALIEN = TRUE)
var/talk_key = get_key(message)
var/static/list/one_character_prefix = list(MODE_HEADSET = TRUE, MODE_ROBOT = TRUE, MODE_WHISPER = TRUE, MODE_SING = TRUE)
if(sanitize)
message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN))
if(!message || message == "")
return
var/datum/saymode/saymode = SSradio.saymodes[talk_key]
var/message_mode = get_message_mode(message)
var/original_message = message
var/in_critical = InCritical()
if(one_character_prefix[message_mode])
message = copytext(message, 2)
else if(message_mode || saymode)
message = copytext(message, 3)
if(findtext(message, " ", 1, 2))
message = copytext(message, 2)
if(message_mode == MODE_ADMIN)
if(client)
client.cmd_admin_say(message)
return
if(message_mode == MODE_DEADMIN)
if(client)
client.dsay(message)
return
if(stat == DEAD)
say_dead(original_message)
return
if(check_emote(original_message) || !can_speak_basic(original_message, ignore_spam))
return
if(in_critical)
if(!(crit_allowed_modes[message_mode]))
return
else if(stat == UNCONSCIOUS)
if(!(unconscious_allowed_modes[message_mode]))
return
// language comma detection.
var/datum/language/message_language = get_message_language(message)
if(message_language)
// No, you cannot speak in xenocommon just because you know the key
if(can_speak_in_language(message_language))
language = message_language
message = copytext(message, 3)
// Trim the space if they said ",0 I LOVE LANGUAGES"
if(findtext(message, " ", 1, 2))
message = copytext(message, 2)
if(!language)
language = get_default_language()
// Detection of language needs to be before inherent channels, because
// AIs use inherent channels for the holopad. Most inherent channels
// ignore the language argument however.
if(saymode && !saymode.handle_message(src, message, language))
return
if(!can_speak_vocal(message))
to_chat(src, "<span class='warning'>You find yourself unable to speak!</span>")
return
var/message_range = 7
var/succumbed = FALSE
var/fullcrit = InFullCritical()
if((InCritical() && !fullcrit) || message_mode == MODE_WHISPER)
message_range = 1
message_mode = MODE_WHISPER
src.log_talk(message, LOG_WHISPER)
if(fullcrit)
var/health_diff = round(-HEALTH_THRESHOLD_DEAD + health)
// If we cut our message short, abruptly end it with a-..
var/message_len = length(message)
message = copytext(message, 1, health_diff) + "[message_len > health_diff ? "-.." : "..."]"
message = Ellipsis(message, 10, 1)
message_mode = MODE_WHISPER_CRIT
succumbed = TRUE
else
src.log_talk(message, LOG_SAY, forced_by=forced)
message = treat_message(message) // unfortunately we still need this
var/sigreturn = SEND_SIGNAL(src, COMSIG_MOB_SAY, args)
if (sigreturn & COMPONENT_UPPERCASE_SPEECH)
message = uppertext(message)
if(!message)
return
last_words = message
spans |= speech_span
if(language)
var/datum/language/L = GLOB.language_datum_instances[language]
spans |= L.spans
if(message_mode == MODE_SING)
#if DM_VERSION < 513
var/randomnote = "~"
#else
var/randomnote = pick("\u2669", "\u266A", "\u266B")
#endif
spans |= SPAN_SINGING
message = "[randomnote] [message] [randomnote]"
var/radio_return = radio(message, message_mode, spans, language)
if(radio_return & ITALICS)
spans |= SPAN_ITALICS
if(radio_return & REDUCE_RANGE)
message_range = 1
if(radio_return & NOPASS)
return 1
//No screams in space, unless you're next to someone.
var/turf/T = get_turf(src)
var/datum/gas_mixture/environment = T.return_air()
var/pressure = (environment)? environment.return_pressure() : 0
if(pressure < SOUND_MINIMUM_PRESSURE)
message_range = 1
if(pressure < ONE_ATMOSPHERE*0.4) //Thin air, let's italicise the message
spans |= SPAN_ITALICS
send_speech(message, message_range, src, bubble_type, spans, language, message_mode)
if(succumbed)
succumb()
to_chat(src, compose_message(src, language, message, , spans, message_mode))
return 1
/mob/living/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode)
. = ..()
if(!client)
return
var/deaf_message
var/deaf_type
if(speaker != src)
if(!radio_freq) //These checks have to be seperate, else people talking on the radio will make "You can't hear yourself!" appear when hearing people over the radio while deaf.
deaf_message = "<span class='name'>[speaker]</span> [speaker.verb_say] something but you cannot hear [speaker.p_them()]."
deaf_type = 1
else
deaf_message = "<span class='notice'>You can't hear yourself!</span>"
deaf_type = 2 // Since you should be able to hear yourself without looking
// Create map text prior to modifying message for goonchat
if (client?.prefs.chat_on_map && stat != UNCONSCIOUS && (client.prefs.see_chat_non_mob || ismob(speaker)) && can_hear())
create_chat_message(speaker, message_language, raw_message, spans, message_mode)
if (client?.prefs.radiosounds && stat != UNCONSCIOUS && can_hear() && radio_freq)
playsound_local(src,'sound/voice/radio.ogg', 30, 1)
// Recompose message for AI hrefs, language incomprehension.
message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode)
message = hear_intercept(message, speaker, message_language, raw_message, radio_freq, spans, message_mode)
show_message(message, 2, deaf_message, deaf_type)
return message
/mob/living/proc/hear_intercept(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode)
return message
/mob/living/send_speech(message, message_range = 6, obj/source = src, bubble_type = bubble_icon, list/spans, datum/language/message_language=null, message_mode)
var/static/list/eavesdropping_modes = list(MODE_WHISPER = TRUE, MODE_WHISPER_CRIT = TRUE)
var/eavesdrop_range = 0
if(eavesdropping_modes[message_mode])
eavesdrop_range = EAVESDROP_EXTRA_RANGE
var/list/listening = get_hearers_in_view(message_range+eavesdrop_range, source)
var/list/the_dead = list()
var/list/yellareas //CIT CHANGE - adds the ability for yelling to penetrate walls and echo throughout areas
if(say_test(message) == "2") //CIT CHANGE - ditto
yellareas = get_areas_in_range(message_range*0.5,src) //CIT CHANGE - ditto
for(var/_M in GLOB.player_list)
var/mob/M = _M
if(M.stat != DEAD) //not dead, not important
if(yellareas) //CIT CHANGE - see above. makes yelling penetrate walls
var/area/A = get_area(M) //CIT CHANGE - ditto
if(istype(A) && A.ambientsounds != SPACE && A in yellareas) //CIT CHANGE - ditto
listening |= M //CIT CHANGE - ditto
continue
if(!M.client || !client) //client is so that ghosts don't have to listen to mice
continue
if(get_dist(M, src) > 7 || M.z != z) //they're out of range of normal hearing
if(eavesdropping_modes[message_mode] && !(M.client.prefs.chat_toggles & CHAT_GHOSTWHISPER)) //they're whispering and we have hearing whispers at any range off
continue
if(!(M.client.prefs.chat_toggles & CHAT_GHOSTEARS)) //they're talking normally and we have hearing at any range off
continue
listening |= M
the_dead[M] = TRUE
var/eavesdropping
var/eavesrendered
if(eavesdrop_range)
eavesdropping = stars(message)
eavesrendered = compose_message(src, message_language, eavesdropping, , spans, message_mode)
var/rendered = compose_message(src, message_language, message, , spans, message_mode)
for(var/_AM in listening)
var/atom/movable/AM = _AM
if(eavesdrop_range && get_dist(source, AM) > message_range && !(the_dead[AM]))
AM.Hear(eavesrendered, src, message_language, eavesdropping, , spans, message_mode)
else
AM.Hear(rendered, src, message_language, message, , spans, message_mode)
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_LIVING_SAY_SPECIAL, src, message)
//speech bubble
var/list/speech_bubble_recipients = list()
for(var/mob/M in listening)
if(M.client && !M.client.prefs.chat_on_map)
speech_bubble_recipients.Add(M.client)
var/image/I = image('icons/mob/talk.dmi', src, "[bubble_type][say_test(message)]", FLY_LAYER)
I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
INVOKE_ASYNC(GLOBAL_PROC, /.proc/flick_overlay, I, speech_bubble_recipients, 30)
/mob/proc/binarycheck()
return FALSE
/mob/living/can_speak(message) //For use outside of Say()
if(can_speak_basic(message) && can_speak_vocal(message))
return 1
/mob/living/proc/can_speak_basic(message, ignore_spam = FALSE) //Check BEFORE handling of xeno and ling channels
if(client)
if(client.prefs.muted & MUTE_IC)
to_chat(src, "<span class='danger'>You cannot speak in IC (muted).</span>")
return 0
if(!ignore_spam && client.handle_spam_prevention(message,MUTE_IC))
return 0
return 1
/mob/living/proc/can_speak_vocal(message) //Check AFTER handling of xeno and ling channels
if(HAS_TRAIT(src, TRAIT_MUTE))
return 0
if(is_muzzled())
return 0
if(!IsVocal())
return 0
return 1
/mob/living/proc/get_key(message)
var/key = copytext(message, 1, 2)
if(key in GLOB.department_radio_prefixes)
return lowertext(copytext(message, 2, 3))
/mob/living/proc/get_message_language(message)
if(copytext(message, 1, 2) == ",")
var/key = copytext(message, 2, 3)
for(var/ld in GLOB.all_languages)
var/datum/language/LD = ld
if(initial(LD.key) == key)
return LD
return null
/mob/living/proc/treat_message(message)
if(HAS_TRAIT(src, TRAIT_UNINTELLIGIBLE_SPEECH))
message = unintelligize(message)
if(derpspeech)
message = derpspeech(message, stuttering)
if(stuttering)
message = stutter(message)
if(slurring)
message = slur(message,slurring)
if(cultslurring)
message = cultslur(message)
message = capitalize(message)
return message
/mob/living/proc/radio(message, message_mode, list/spans, language)
var/obj/item/implant/radio/imp = locate() in implants
if(imp?.radio.on)
if(message_mode == MODE_HEADSET)
imp.radio.talk_into(src, message, , spans, language)
return ITALICS | REDUCE_RANGE
if(message_mode == MODE_DEPARTMENT || message_mode in GLOB.radiochannels)
imp.radio.talk_into(src, message, message_mode, spans, language)
return ITALICS | REDUCE_RANGE
switch(message_mode)
if(MODE_WHISPER)
return ITALICS
if(MODE_R_HAND)
for(var/obj/item/r_hand in get_held_items_for_side("r", all = TRUE))
if (r_hand)
return r_hand.talk_into(src, message, , spans, language)
return ITALICS | REDUCE_RANGE
if(MODE_L_HAND)
for(var/obj/item/l_hand in get_held_items_for_side("l", all = TRUE))
if (l_hand)
return l_hand.talk_into(src, message, , spans, language)
return ITALICS | REDUCE_RANGE
if(MODE_INTERCOM)
for (var/obj/item/radio/intercom/I in view(1, null))
I.talk_into(src, message, , spans, language)
return ITALICS | REDUCE_RANGE
if(MODE_BINARY)
return ITALICS | REDUCE_RANGE //Does not return 0 since this is only reached by humans, not borgs or AIs.
return 0
/mob/living/say_mod(input, message_mode)
if(message_mode == MODE_WHISPER)
. = verb_whisper
else if(message_mode == MODE_WHISPER_CRIT)
. = "[verb_whisper] in [p_their()] last breath"
else if(stuttering)
. = "stammers"
else if(derpspeech)
. = "gibbers"
else if(message_mode == MODE_SING)
. = verb_sing
else
. = ..()
/mob/living/whisper(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
say("#[message]", bubble_type, spans, sanitize, language, ignore_spam, forced)
/mob/living/get_language_holder(shadow=TRUE)
if(mind && shadow)
// Mind language holders shadow mob holders.
. = mind.get_language_holder()
if(.)
return .
. = ..()
GLOBAL_LIST_INIT(department_radio_prefixes, list(":", "."))
GLOBAL_LIST_INIT(department_radio_keys, list(
// Location
MODE_KEY_R_HAND = MODE_R_HAND,
MODE_KEY_L_HAND = MODE_L_HAND,
MODE_KEY_INTERCOM = MODE_INTERCOM,
// Department
MODE_KEY_DEPARTMENT = MODE_DEPARTMENT,
RADIO_KEY_COMMAND = RADIO_CHANNEL_COMMAND,
RADIO_KEY_SCIENCE = RADIO_CHANNEL_SCIENCE,
RADIO_KEY_MEDICAL = RADIO_CHANNEL_MEDICAL,
RADIO_KEY_ENGINEERING = RADIO_CHANNEL_ENGINEERING,
RADIO_KEY_SECURITY = RADIO_CHANNEL_SECURITY,
RADIO_KEY_SUPPLY = RADIO_CHANNEL_SUPPLY,
RADIO_KEY_SERVICE = RADIO_CHANNEL_SERVICE,
// Faction
RADIO_KEY_SYNDICATE = RADIO_CHANNEL_SYNDICATE,
RADIO_KEY_CENTCOM = RADIO_CHANNEL_CENTCOM,
// Admin
MODE_KEY_ADMIN = MODE_ADMIN,
MODE_KEY_DEADMIN = MODE_DEADMIN,
// Misc
RADIO_KEY_AI_PRIVATE = RADIO_CHANNEL_AI_PRIVATE, // AI Upload channel
MODE_KEY_VOCALCORDS = MODE_VOCALCORDS, // vocal cords, used by Voice of God
//kinda localization -- rastaf0
//same keys as above, but on russian keyboard layout. This file uses cp1251 as encoding.
// Location
"ê" = MODE_R_HAND,
"ä" = MODE_L_HAND,
"ø" = MODE_INTERCOM,
// Department
"ð" = MODE_DEPARTMENT,
"ñ" = RADIO_CHANNEL_COMMAND,
"ò" = RADIO_CHANNEL_SCIENCE,
"ü" = RADIO_CHANNEL_MEDICAL,
"ó" = RADIO_CHANNEL_ENGINEERING,
"û" = RADIO_CHANNEL_SECURITY,
"ã" = RADIO_CHANNEL_SUPPLY,
"ì" = RADIO_CHANNEL_SERVICE,
// Faction
"å" = RADIO_CHANNEL_SYNDICATE,
"í" = RADIO_CHANNEL_CENTCOM,
// Admin
"ç" = MODE_ADMIN,
"â" = MODE_ADMIN,
// Misc
"ù" = RADIO_CHANNEL_AI_PRIVATE,
"÷" = MODE_VOCALCORDS
))
/mob/living/proc/Ellipsis(original_msg, chance = 50, keep_words)
if(chance <= 0)
return "..."
if(chance >= 100)
return original_msg
var/list/words = splittext(original_msg," ")
var/list/new_words = list()
var/new_msg = ""
for(var/w in words)
if(prob(chance))
new_words += "..."
if(!keep_words)
continue
new_words += w
new_msg = jointext(new_words," ")
return new_msg
/mob/living/say(message, bubble_type,var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
var/static/list/crit_allowed_modes = list(MODE_WHISPER = TRUE, MODE_CHANGELING = TRUE, MODE_ALIEN = TRUE)
var/static/list/unconscious_allowed_modes = list(MODE_CHANGELING = TRUE, MODE_ALIEN = TRUE)
var/talk_key = get_key(message)
var/static/list/one_character_prefix = list(MODE_HEADSET = TRUE, MODE_ROBOT = TRUE, MODE_WHISPER = TRUE, MODE_SING = TRUE)
if(sanitize)
message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN))
if(!message || message == "")
return
var/datum/saymode/saymode = SSradio.saymodes[talk_key]
var/message_mode = get_message_mode(message)
var/original_message = message
var/in_critical = InCritical()
if(one_character_prefix[message_mode])
message = copytext(message, 2)
else if(message_mode || saymode)
message = copytext(message, 3)
if(findtext(message, " ", 1, 2))
message = copytext(message, 2)
if(message_mode == MODE_ADMIN)
if(client)
client.cmd_admin_say(message)
return
if(message_mode == MODE_DEADMIN)
if(client)
client.dsay(message)
return
if(stat == DEAD)
say_dead(original_message)
return
if(check_emote(original_message) || !can_speak_basic(original_message, ignore_spam))
return
if(in_critical)
if(!(crit_allowed_modes[message_mode]))
return
else if(stat == UNCONSCIOUS)
if(!(unconscious_allowed_modes[message_mode]))
return
// language comma detection.
var/datum/language/message_language = get_message_language(message)
if(message_language)
// No, you cannot speak in xenocommon just because you know the key
if(can_speak_in_language(message_language))
language = message_language
message = copytext(message, 3)
// Trim the space if they said ",0 I LOVE LANGUAGES"
if(findtext(message, " ", 1, 2))
message = copytext(message, 2)
if(!language)
language = get_default_language()
// Detection of language needs to be before inherent channels, because
// AIs use inherent channels for the holopad. Most inherent channels
// ignore the language argument however.
if(saymode && !saymode.handle_message(src, message, language))
return
if(!can_speak_vocal(message))
to_chat(src, "<span class='warning'>You find yourself unable to speak!</span>")
return
var/message_range = 7
var/succumbed = FALSE
var/fullcrit = InFullCritical()
if((InCritical() && !fullcrit) || message_mode == MODE_WHISPER)
message_range = 1
message_mode = MODE_WHISPER
src.log_talk(message, LOG_WHISPER)
if(fullcrit)
var/health_diff = round(-HEALTH_THRESHOLD_DEAD + health)
// If we cut our message short, abruptly end it with a-..
var/message_len = length(message)
message = copytext(message, 1, health_diff) + "[message_len > health_diff ? "-.." : "..."]"
message = Ellipsis(message, 10, 1)
message_mode = MODE_WHISPER_CRIT
succumbed = TRUE
else
src.log_talk(message, LOG_SAY, forced_by=forced)
message = treat_message(message) // unfortunately we still need this
var/sigreturn = SEND_SIGNAL(src, COMSIG_MOB_SAY, args)
if (sigreturn & COMPONENT_UPPERCASE_SPEECH)
message = uppertext(message)
if(!message)
return
last_words = message
spans |= speech_span
if(language)
var/datum/language/L = GLOB.language_datum_instances[language]
spans |= L.spans
if(message_mode == MODE_SING)
#if DM_VERSION < 513
var/randomnote = "~"
#else
var/randomnote = pick("\u2669", "\u266A", "\u266B")
#endif
spans |= SPAN_SINGING
message = "[randomnote] [message] [randomnote]"
var/radio_return = radio(message, message_mode, spans, language)
if(radio_return & ITALICS)
spans |= SPAN_ITALICS
if(radio_return & REDUCE_RANGE)
message_range = 1
if(radio_return & NOPASS)
return 1
//No screams in space, unless you're next to someone.
var/turf/T = get_turf(src)
var/datum/gas_mixture/environment = T.return_air()
var/pressure = (environment)? environment.return_pressure() : 0
if(pressure < SOUND_MINIMUM_PRESSURE)
message_range = 1
if(pressure < ONE_ATMOSPHERE*0.4) //Thin air, let's italicise the message
spans |= SPAN_ITALICS
send_speech(message, message_range, src, bubble_type, spans, language, message_mode)
if(succumbed)
succumb()
to_chat(src, compose_message(src, language, message, null, spans, message_mode))
return 1
/mob/living/compose_message(atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode, face_name = FALSE, atom/movable/source)
. = ..()
if(isliving(speaker))
var/turf/sourceturf = get_turf(source)
var/turf/T = get_turf(src)
if(sourceturf && T && !(sourceturf in get_hear(5, T)))
. = "<span class='small'>[.]</span>"
/mob/living/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
. = ..()
if(!client)
return
var/deaf_message
var/deaf_type
if(speaker != src)
if(!radio_freq) //These checks have to be seperate, else people talking on the radio will make "You can't hear yourself!" appear when hearing people over the radio while deaf.
deaf_message = "<span class='name'>[speaker]</span> [speaker.verb_say] something but you cannot hear [speaker.p_them()]."
deaf_type = 1
else
deaf_message = "<span class='notice'>You can't hear yourself!</span>"
deaf_type = 2 // Since you should be able to hear yourself without looking
// Create map text prior to modifying message for goonchat
if (client?.prefs.chat_on_map && stat != UNCONSCIOUS && (client.prefs.see_chat_non_mob || ismob(speaker)) && can_hear())
create_chat_message(speaker, message_language, raw_message, spans, message_mode)
if (client?.prefs.radiosounds && stat != UNCONSCIOUS && can_hear() && radio_freq)
playsound_local(src,'sound/voice/radio.ogg', 30, 1)
// Recompose message for AI hrefs, language incomprehension.
message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode, FALSE, source)
message = hear_intercept(message, speaker, message_language, raw_message, radio_freq, spans, message_mode, FALSE, source)
show_message(message, MSG_AUDIBLE, deaf_message, deaf_type)
return message
/mob/living/proc/hear_intercept(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode)
return message
/mob/living/send_speech(message, message_range = 6, obj/source = src, bubble_type = bubble_icon, list/spans, datum/language/message_language=null, message_mode)
var/static/list/eavesdropping_modes = list(MODE_WHISPER = TRUE, MODE_WHISPER_CRIT = TRUE)
var/eavesdrop_range = 0
if(eavesdropping_modes[message_mode])
eavesdrop_range = EAVESDROP_EXTRA_RANGE
var/list/listening = get_hearers_in_view(message_range+eavesdrop_range, source)
var/list/the_dead = list()
var/list/yellareas //CIT CHANGE - adds the ability for yelling to penetrate walls and echo throughout areas
if(!eavesdrop_range && say_test(message) == "2") //CIT CHANGE - ditto
yellareas = get_areas_in_range(message_range*0.5, source) //CIT CHANGE - ditto
for(var/_M in GLOB.player_list)
var/mob/M = _M
if(M.stat != DEAD) //not dead, not important
if(yellareas) //CIT CHANGE - see above. makes yelling penetrate walls
var/area/A = get_area(M) //CIT CHANGE - ditto
if(istype(A) && A.ambientsounds != SPACE && A in yellareas) //CIT CHANGE - ditto
listening |= M //CIT CHANGE - ditto
continue
if(!M.client || !client) //client is so that ghosts don't have to listen to mice
continue
if(get_dist(M, source) > 7 || M.z != z) //they're out of range of normal hearing
if(eavesdropping_modes[message_mode] && !(M.client.prefs.chat_toggles & CHAT_GHOSTWHISPER)) //they're whispering and we have hearing whispers at any range off
continue
if(!(M.client.prefs.chat_toggles & CHAT_GHOSTEARS)) //they're talking normally and we have hearing at any range off
continue
listening |= M
the_dead[M] = TRUE
var/eavesdropping
var/eavesrendered
if(eavesdrop_range)
eavesdropping = stars(message)
eavesrendered = compose_message(src, message_language, eavesdropping, null, spans, message_mode, FALSE, source)
var/rendered = compose_message(src, message_language, message, null, spans, message_mode, FALSE, source)
for(var/_AM in listening)
var/atom/movable/AM = _AM
if(eavesdrop_range && get_dist(source, AM) > message_range && !(the_dead[AM]))
AM.Hear(eavesrendered, src, message_language, eavesdropping, null, spans, message_mode, source)
else
AM.Hear(rendered, src, message_language, message, null, spans, message_mode, source)
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_LIVING_SAY_SPECIAL, src, message)
//speech bubble
var/list/speech_bubble_recipients = list()
for(var/mob/M in listening)
if(M.client && !M.client.prefs.chat_on_map)
speech_bubble_recipients.Add(M.client)
var/image/I = image('icons/mob/talk.dmi', src, "[bubble_type][say_test(message)]", FLY_LAYER)
I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
INVOKE_ASYNC(GLOBAL_PROC, /.proc/flick_overlay, I, speech_bubble_recipients, 30)
/mob/proc/binarycheck()
return FALSE
/mob/living/can_speak(message) //For use outside of Say()
if(can_speak_basic(message) && can_speak_vocal(message))
return 1
/mob/living/proc/can_speak_basic(message, ignore_spam = FALSE) //Check BEFORE handling of xeno and ling channels
if(client)
if(client.prefs.muted & MUTE_IC)
to_chat(src, "<span class='danger'>You cannot speak in IC (muted).</span>")
return 0
if(!ignore_spam && client.handle_spam_prevention(message,MUTE_IC))
return 0
return 1
/mob/living/proc/can_speak_vocal(message) //Check AFTER handling of xeno and ling channels
if(HAS_TRAIT(src, TRAIT_MUTE))
return 0
if(is_muzzled())
return 0
if(!IsVocal())
return 0
return 1
/mob/living/proc/get_key(message)
var/key = copytext(message, 1, 2)
if(key in GLOB.department_radio_prefixes)
return lowertext(copytext(message, 2, 3))
/mob/living/proc/get_message_language(message)
if(copytext(message, 1, 2) == ",")
var/key = copytext(message, 2, 3)
for(var/ld in GLOB.all_languages)
var/datum/language/LD = ld
if(initial(LD.key) == key)
return LD
return null
/mob/living/proc/treat_message(message)
if(HAS_TRAIT(src, TRAIT_UNINTELLIGIBLE_SPEECH))
message = unintelligize(message)
if(derpspeech)
message = derpspeech(message, stuttering)
if(stuttering)
message = stutter(message)
if(slurring)
message = slur(message,slurring)
if(cultslurring)
message = cultslur(message)
message = capitalize(message)
return message
/mob/living/proc/radio(message, message_mode, list/spans, language)
var/obj/item/implant/radio/imp = locate() in implants
if(imp?.radio.on)
if(message_mode == MODE_HEADSET)
imp.radio.talk_into(src, message, , spans, language)
return ITALICS | REDUCE_RANGE
if(message_mode == MODE_DEPARTMENT || message_mode in GLOB.radiochannels)
imp.radio.talk_into(src, message, message_mode, spans, language)
return ITALICS | REDUCE_RANGE
switch(message_mode)
if(MODE_WHISPER)
return ITALICS
if(MODE_R_HAND)
for(var/obj/item/r_hand in get_held_items_for_side("r", all = TRUE))
if (r_hand)
return r_hand.talk_into(src, message, , spans, language)
return ITALICS | REDUCE_RANGE
if(MODE_L_HAND)
for(var/obj/item/l_hand in get_held_items_for_side("l", all = TRUE))
if (l_hand)
return l_hand.talk_into(src, message, , spans, language)
return ITALICS | REDUCE_RANGE
if(MODE_INTERCOM)
for (var/obj/item/radio/intercom/I in view(1, null))
I.talk_into(src, message, , spans, language)
return ITALICS | REDUCE_RANGE
if(MODE_BINARY)
return ITALICS | REDUCE_RANGE //Does not return 0 since this is only reached by humans, not borgs or AIs.
return 0
/mob/living/say_mod(input, message_mode)
. = ..()
if(message_mode == MODE_WHISPER_CRIT)
. = "[verb_whisper] in [p_their()] last breath"
else if(message_mode != MODE_CUSTOM_SAY)
if(message_mode == MODE_WHISPER)
. = verb_whisper
else if(stuttering)
. = "stammers"
else if(derpspeech)
. = "gibbers"
/mob/living/whisper(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null)
say("#[message]", bubble_type, spans, sanitize, language, ignore_spam, forced)
/mob/living/get_language_holder(shadow=TRUE)
if(mind && shadow)
// Mind language holders shadow mob holders.
. = mind.get_language_holder()
if(.)
return .
. = ..()
File diff suppressed because it is too large Load Diff
+197 -197
View File
@@ -1,197 +1,197 @@
// AI EYE
//
// An invisible (no icon) mob that the AI controls to look around the station with.
// It streams chunks as it moves around, which will show it what the AI can and cannot see.
/mob/camera/aiEye
name = "Inactive AI Eye"
invisibility = INVISIBILITY_MAXIMUM
hud_possible = list(ANTAG_HUD, AI_DETECT_HUD = HUD_LIST_LIST)
var/list/visibleCameraChunks = list()
var/mob/living/silicon/ai/ai = null
var/relay_speech = FALSE
var/use_static = USE_STATIC_OPAQUE
var/static_visibility_range = 16
var/ai_detector_visible = TRUE
var/ai_detector_color = COLOR_RED
/mob/camera/aiEye/Initialize()
. = ..()
GLOB.aiEyes += src
update_ai_detect_hud()
setLoc(loc, TRUE)
/mob/camera/aiEye/proc/update_ai_detect_hud()
var/datum/atom_hud/ai_detector/hud = GLOB.huds[DATA_HUD_AI_DETECT]
var/list/old_images = hud_list[AI_DETECT_HUD]
if(!ai_detector_visible)
hud.remove_from_hud(src)
QDEL_LIST(old_images)
return
if(!hud.hudusers.len)
//no one is watching, do not bother updating anything
return
hud.remove_from_hud(src)
var/static/list/vis_contents_objects = list()
var/obj/effect/overlay/ai_detect_hud/hud_obj = vis_contents_objects[ai_detector_color]
if(!hud_obj)
hud_obj = new /obj/effect/overlay/ai_detect_hud()
hud_obj.color = ai_detector_color
vis_contents_objects[ai_detector_color] = hud_obj
var/list/new_images = list()
var/list/turfs = get_visible_turfs()
for(var/T in turfs)
var/image/I = (old_images.len > new_images.len) ? old_images[new_images.len + 1] : image(null, T)
I.loc = T
I.vis_contents += hud_obj
new_images += I
for(var/i in (new_images.len + 1) to old_images.len)
qdel(old_images[i])
hud_list[AI_DETECT_HUD] = new_images
hud.add_to_hud(src)
/mob/camera/aiEye/proc/get_visible_turfs()
if(!isturf(loc))
return list()
var/client/C = GetViewerClient()
var/view = C ? getviewsize(C.view) : getviewsize(world.view)
var/turf/lowerleft = locate(max(1, x - (view[1] - 1)/2), max(1, y - (view[2] - 1)/2), z)
var/turf/upperright = locate(min(world.maxx, lowerleft.x + (view[1] - 1)), min(world.maxy, lowerleft.y + (view[2] - 1)), lowerleft.z)
return block(lowerleft, upperright)
// Use this when setting the aiEye's location.
// It will also stream the chunk that the new loc is in.
/mob/camera/aiEye/proc/setLoc(T, force_update = FALSE)
if(ai)
if(!isturf(ai.loc))
return
T = get_turf(T)
if(!force_update && (T == get_turf(src)) )
return //we are already here!
if (T)
forceMove(T)
else
moveToNullspace()
if(use_static != USE_STATIC_NONE)
ai.camera_visibility(src)
if(ai.client && !ai.multicam_on)
ai.client.eye = src
update_ai_detect_hud()
update_parallax_contents()
//Holopad
if(istype(ai.current, /obj/machinery/holopad))
var/obj/machinery/holopad/H = ai.current
H.move_hologram(ai, T)
if(ai.camera_light_on)
ai.light_cameras()
if(ai.master_multicam)
ai.master_multicam.refresh_view()
/mob/camera/aiEye/Move()
return 0
/mob/camera/aiEye/proc/GetViewerClient()
if(ai)
return ai.client
return null
/mob/camera/aiEye/Destroy()
if(ai)
ai.all_eyes -= src
ai = null
for(var/V in visibleCameraChunks)
var/datum/camerachunk/c = V
c.remove(src)
GLOB.aiEyes -= src
if(ai_detector_visible)
var/datum/atom_hud/ai_detector/hud = GLOB.huds[DATA_HUD_AI_DETECT]
hud.remove_from_hud(src)
var/list/L = hud_list[AI_DETECT_HUD]
QDEL_LIST(L)
return ..()
/atom/proc/move_camera_by_click()
if(isAI(usr))
var/mob/living/silicon/ai/AI = usr
if(AI.eyeobj && (AI.multicam_on || (AI.client.eye == AI.eyeobj)) && (AI.eyeobj.z == z))
AI.cameraFollow = null
if (isturf(loc) || isturf(src))
AI.eyeobj.setLoc(src)
// This will move the AIEye. It will also cause lights near the eye to light up, if toggled.
// This is handled in the proc below this one.
/client/proc/AIMove(n, direct, mob/living/silicon/ai/user)
var/initial = initial(user.sprint)
var/max_sprint = 50
if(user.cooldown && user.cooldown < world.timeofday) // 3 seconds
user.sprint = initial
for(var/i = 0; i < max(user.sprint, initial); i += 20)
var/turf/step = get_turf(get_step(user.eyeobj, direct))
if(step)
user.eyeobj.setLoc(step)
user.cooldown = world.timeofday + 5
if(user.acceleration)
user.sprint = min(user.sprint + 0.5, max_sprint)
else
user.sprint = initial
if(!user.tracking)
user.cameraFollow = null
// Return to the Core.
/mob/living/silicon/ai/proc/view_core()
if(istype(current,/obj/machinery/holopad))
var/obj/machinery/holopad/H = current
H.clear_holo(src)
else
current = null
cameraFollow = null
unset_machine()
if(!eyeobj || !eyeobj.loc || QDELETED(eyeobj))
to_chat(src, "ERROR: Eyeobj not found. Creating new eye...")
create_eye()
eyeobj.setLoc(loc)
/mob/living/silicon/ai/proc/create_eye()
if(eyeobj)
return
eyeobj = new /mob/camera/aiEye()
all_eyes += eyeobj
eyeobj.ai = src
eyeobj.setLoc(loc)
eyeobj.name = "[name] (AI Eye)"
/mob/living/silicon/ai/verb/toggle_acceleration()
set category = "AI Commands"
set name = "Toggle Camera Acceleration"
if(incapacitated())
return
acceleration = !acceleration
to_chat(usr, "Camera acceleration has been toggled [acceleration ? "on" : "off"].")
/mob/camera/aiEye/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode)
. = ..()
if(relay_speech && speaker && ai && !radio_freq && speaker != ai && near_camera(speaker))
ai.relay_speech(message, speaker, message_language, raw_message, radio_freq, spans, message_mode)
/obj/effect/overlay/ai_detect_hud
name = ""
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
icon = 'icons/effects/alphacolors.dmi'
icon_state = ""
alpha = 100
layer = ABOVE_ALL_MOB_LAYER
plane = GAME_PLANE
// AI EYE
//
// An invisible (no icon) mob that the AI controls to look around the station with.
// It streams chunks as it moves around, which will show it what the AI can and cannot see.
/mob/camera/aiEye
name = "Inactive AI Eye"
invisibility = INVISIBILITY_MAXIMUM
hud_possible = list(ANTAG_HUD, AI_DETECT_HUD = HUD_LIST_LIST)
var/list/visibleCameraChunks = list()
var/mob/living/silicon/ai/ai = null
var/relay_speech = FALSE
var/use_static = USE_STATIC_OPAQUE
var/static_visibility_range = 16
var/ai_detector_visible = TRUE
var/ai_detector_color = COLOR_RED
/mob/camera/aiEye/Initialize()
. = ..()
GLOB.aiEyes += src
update_ai_detect_hud()
setLoc(loc, TRUE)
/mob/camera/aiEye/proc/update_ai_detect_hud()
var/datum/atom_hud/ai_detector/hud = GLOB.huds[DATA_HUD_AI_DETECT]
var/list/old_images = hud_list[AI_DETECT_HUD]
if(!ai_detector_visible)
hud.remove_from_hud(src)
QDEL_LIST(old_images)
return
if(!hud.hudusers.len)
//no one is watching, do not bother updating anything
return
hud.remove_from_hud(src)
var/static/list/vis_contents_objects = list()
var/obj/effect/overlay/ai_detect_hud/hud_obj = vis_contents_objects[ai_detector_color]
if(!hud_obj)
hud_obj = new /obj/effect/overlay/ai_detect_hud()
hud_obj.color = ai_detector_color
vis_contents_objects[ai_detector_color] = hud_obj
var/list/new_images = list()
var/list/turfs = get_visible_turfs()
for(var/T in turfs)
var/image/I = (old_images.len > new_images.len) ? old_images[new_images.len + 1] : image(null, T)
I.loc = T
I.vis_contents += hud_obj
new_images += I
for(var/i in (new_images.len + 1) to old_images.len)
qdel(old_images[i])
hud_list[AI_DETECT_HUD] = new_images
hud.add_to_hud(src)
/mob/camera/aiEye/proc/get_visible_turfs()
if(!isturf(loc))
return list()
var/client/C = GetViewerClient()
var/view = C ? getviewsize(C.view) : getviewsize(world.view)
var/turf/lowerleft = locate(max(1, x - (view[1] - 1)/2), max(1, y - (view[2] - 1)/2), z)
var/turf/upperright = locate(min(world.maxx, lowerleft.x + (view[1] - 1)), min(world.maxy, lowerleft.y + (view[2] - 1)), lowerleft.z)
return block(lowerleft, upperright)
// Use this when setting the aiEye's location.
// It will also stream the chunk that the new loc is in.
/mob/camera/aiEye/proc/setLoc(T, force_update = FALSE)
if(ai)
if(!isturf(ai.loc))
return
T = get_turf(T)
if(!force_update && (T == get_turf(src)) )
return //we are already here!
if (T)
forceMove(T)
else
moveToNullspace()
if(use_static != USE_STATIC_NONE)
ai.camera_visibility(src)
if(ai.client && !ai.multicam_on)
ai.client.eye = src
update_ai_detect_hud()
update_parallax_contents()
//Holopad
if(istype(ai.current, /obj/machinery/holopad))
var/obj/machinery/holopad/H = ai.current
H.move_hologram(ai, T)
if(ai.camera_light_on)
ai.light_cameras()
if(ai.master_multicam)
ai.master_multicam.refresh_view()
/mob/camera/aiEye/Move()
return 0
/mob/camera/aiEye/proc/GetViewerClient()
if(ai)
return ai.client
return null
/mob/camera/aiEye/Destroy()
if(ai)
ai.all_eyes -= src
ai = null
for(var/V in visibleCameraChunks)
var/datum/camerachunk/c = V
c.remove(src)
GLOB.aiEyes -= src
if(ai_detector_visible)
var/datum/atom_hud/ai_detector/hud = GLOB.huds[DATA_HUD_AI_DETECT]
hud.remove_from_hud(src)
var/list/L = hud_list[AI_DETECT_HUD]
QDEL_LIST(L)
return ..()
/atom/proc/move_camera_by_click()
if(isAI(usr))
var/mob/living/silicon/ai/AI = usr
if(AI.eyeobj && (AI.multicam_on || (AI.client.eye == AI.eyeobj)) && (AI.eyeobj.z == z))
AI.cameraFollow = null
if (isturf(loc) || isturf(src))
AI.eyeobj.setLoc(src)
// This will move the AIEye. It will also cause lights near the eye to light up, if toggled.
// This is handled in the proc below this one.
/client/proc/AIMove(n, direct, mob/living/silicon/ai/user)
var/initial = initial(user.sprint)
var/max_sprint = 50
if(user.cooldown && user.cooldown < world.timeofday) // 3 seconds
user.sprint = initial
for(var/i = 0; i < max(user.sprint, initial); i += 20)
var/turf/step = get_turf(get_step(user.eyeobj, direct))
if(step)
user.eyeobj.setLoc(step)
user.cooldown = world.timeofday + 5
if(user.acceleration)
user.sprint = min(user.sprint + 0.5, max_sprint)
else
user.sprint = initial
if(!user.tracking)
user.cameraFollow = null
// Return to the Core.
/mob/living/silicon/ai/proc/view_core()
if(istype(current,/obj/machinery/holopad))
var/obj/machinery/holopad/H = current
H.clear_holo(src)
else
current = null
cameraFollow = null
unset_machine()
if(!eyeobj || !eyeobj.loc || QDELETED(eyeobj))
to_chat(src, "ERROR: Eyeobj not found. Creating new eye...")
create_eye()
eyeobj.setLoc(loc)
/mob/living/silicon/ai/proc/create_eye()
if(eyeobj)
return
eyeobj = new /mob/camera/aiEye()
all_eyes += eyeobj
eyeobj.ai = src
eyeobj.setLoc(loc)
eyeobj.name = "[name] (AI Eye)"
/mob/living/silicon/ai/verb/toggle_acceleration()
set category = "AI Commands"
set name = "Toggle Camera Acceleration"
if(incapacitated())
return
acceleration = !acceleration
to_chat(usr, "Camera acceleration has been toggled [acceleration ? "on" : "off"].")
/mob/camera/aiEye/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
. = ..()
if(relay_speech && speaker && ai && !radio_freq && speaker != ai && near_camera(speaker))
ai.relay_speech(message, speaker, message_language, raw_message, radio_freq, spans, message_mode)
/obj/effect/overlay/ai_detect_hud
name = ""
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
icon = 'icons/effects/alphacolors.dmi'
icon_state = ""
alpha = 100
layer = ABOVE_ALL_MOB_LAYER
plane = GAME_PLANE
@@ -378,7 +378,7 @@ Difficulty: Very Hard
. += observer_desc
. += "It is activated by [activation_method]."
/obj/machinery/anomalous_crystal/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, spans, message_mode)
/obj/machinery/anomalous_crystal/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, spans, message_mode, atom/movable/source)
..()
if(isliving(speaker))
ActivationReaction(speaker, ACTIVATE_SPEECH)
@@ -80,7 +80,7 @@ Difficulty: Medium
return FALSE
return ..()
/mob/living/simple_animal/hostile/megafauna/dragon/visible_message()
/mob/living/simple_animal/hostile/megafauna/dragon/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs)
if(swooping & SWOOP_INVULNERABLE) //to suppress attack messages without overriding every single proc that could send a message saying we got hit
return
return ..()
File diff suppressed because it is too large Load Diff
@@ -1,4 +1,4 @@
/mob/living/simple_animal/slime/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, spans, message_mode)
/mob/living/simple_animal/slime/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, spans, message_mode, atom/movable/source)
. = ..()
if(speaker != src && !radio_freq && !stat)
if (speaker in Friends)
+118 -75
View File
@@ -87,106 +87,120 @@
msg = copytext(msg, 1, MAX_MESSAGE_LEN)
if(type)
if(type & 1 && eye_blind )//Vision related
if(type & MSG_VISUAL && eye_blind )//Vision related
if(!alt_msg)
return
else
msg = alt_msg
type = alt_type
if(type & 2 && !can_hear())//Hearing related
if(type & MSG_AUDIBLE && !can_hear())//Hearing related
if(!alt_msg)
return
else
msg = alt_msg
type = alt_type
if(type & 1 && eye_blind)
if(type & MSG_VISUAL && eye_blind)
return
// voice muffling
if(stat == UNCONSCIOUS)
if(type & 2) //audio
if(type & MSG_AUDIBLE) //audio
to_chat(src, "<I>... You can almost hear something ...</I>")
else
to_chat(src, msg)
return
to_chat(src, msg)
// Show a message to all player mobs who sees this atom
// Show a message to the src mob (if the src is a mob)
// Use for atoms performing visible actions
// message is output to anyone who can see, e.g. "The [src] does something!"
// self_message (optional) is what the src mob sees e.g. "You do something!"
// blind_message (optional) is what blind people will hear e.g. "You hear something!"
// vision_distance (optional) define how many tiles away the message can be seen.
// ignored_mob (optional) doesn't show any message to a given mob if TRUE.
/atom/proc/visible_message(message, self_message, blind_message, vision_distance, list/ignored_mobs, no_ghosts = FALSE)
/**
* Generate a visible message from this atom
*
* Show a message to all player mobs who sees this atom
*
* Show a message to the src mob (if the src is a mob)
*
* Use for atoms performing visible actions
*
* message is output to anyone who can see, e.g. "The [src] does something!"
*
* Vars:
* * self_message (optional) is what the src mob sees e.g. "You do something!"
* * blind_message (optional) is what blind people will hear e.g. "You hear something!"
* * vision_distance (optional) define how many tiles away the message can be seen.
* * ignored_mobs (optional) doesn't show any message to any given mob in the list.
*/
/atom/proc/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs)
var/turf/T = get_turf(src)
if(!T)
return
var/list/hearers = get_hearers_in_view(vision_distance, src) //caches the hearers and then removes ignored mobs.
if(!length(hearers))
return
if(!islist(ignored_mobs))
ignored_mobs = list(ignored_mobs)
var/range = 7
if(vision_distance)
range = vision_distance
for(var/mob/M in get_hearers_in_view(range, src))
hearers -= ignored_mobs
if(self_message)
hearers -= src
for(var/mob/M in hearers)
if(!M.client)
continue
if(M in ignored_mobs)
continue
//This entire if/else chain could be in two lines but isn't for readibilties sake.
var/msg = message
if(isobserver(M) && no_ghosts)
//CITADEL EDIT, required for vore code to remove (T != loc && T != src)) as a check
if(M.see_invisible<invisibility) //if src is invisible to us,
msg = blind_message
else if(T.lighting_object && T.lighting_object.invisibility <= M.see_invisible && T.is_softly_lit()) //the light object is dark and not invisible to us
msg = blind_message
if(!msg)
continue
if(M == src) //the src always see the main message or self message
if(self_message)
msg = self_message
else //CITADEL EDIT, required for vore code to remove (T != loc && T != src)) as a check
if(M.see_invisible<invisibility) //if src is invisible to us,
if(blind_message) // then people see blind message if there is one, otherwise nothing.
msg = blind_message
else
continue
M.show_message(msg, MSG_VISUAL,blind_message, MSG_AUDIBLE)
else if(T.lighting_object)
if(T.lighting_object.invisibility <= M.see_invisible && T.is_softly_lit()) //the light object is dark and not invisible to us
if(blind_message)
msg = blind_message
else
continue
///Adds the functionality to self_message.
mob/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs)
. = ..()
if(self_message)
show_message(self_message, MSG_VISUAL, blind_message, MSG_AUDIBLE)
M.show_message(msg,1,blind_message,2)
/**
* Show a message to all mobs in earshot of this atom
*
* Use for objects performing audible actions
*
* vars:
* * message is the message output to anyone who can hear.
* * deaf_message (optional) is what deaf people will see.
* * hearing_distance (optional) is the range, how many tiles away the message can be heard.
* * ignored_mobs (optional) doesn't show any message to any given mob in the list.
*/
/atom/proc/audible_message(message, deaf_message, hearing_distance = DEFAULT_MESSAGE_RANGE, self_message, list/ignored_mobs)
var/turf/T = get_turf(src)
if(!T)
return
var/list/hearers = get_hearers_in_view(hearing_distance, src)
if(!length(hearers))
return
if(!islist(ignored_mobs))
ignored_mobs = list(ignored_mobs)
hearers -= ignored_mobs
if(self_message)
hearers -= src
for(var/mob/M in hearers)
M.show_message(message, MSG_AUDIBLE, deaf_message, MSG_VISUAL)
// Show a message to all mobs in earshot of this one
// This would be for audible actions by the src mob
// message is the message output to anyone who can hear.
// self_message (optional) is what the src mob hears.
// deaf_message (optional) is what deaf people will see.
// hearing_distance (optional) is the range, how many tiles away the message can be heard.
/mob/audible_message(message, deaf_message, hearing_distance, self_message, no_ghosts = FALSE)
var/range = 7
if(hearing_distance)
range = hearing_distance
for(var/mob/M in get_hearers_in_view(range, src))
var/msg = message
if(self_message && M==src)
msg = self_message
if(no_ghosts && isobserver(M))
continue
M.show_message( msg, 2, deaf_message, 1)
// Show a message to all mobs in earshot of this atom
// Use for objects performing audible actions
// message is the message output to anyone who can hear.
// deaf_message (optional) is what deaf people will see.
// hearing_distance (optional) is the range, how many tiles away the message can be heard.
/atom/proc/audible_message(message, deaf_message, hearing_distance, no_ghosts = FALSE)
var/range = 7
if(hearing_distance)
range = hearing_distance
for(var/mob/M in get_hearers_in_view(range, src))
if(no_ghosts && isobserver(M))
continue
M.show_message( message, 2, deaf_message, 1)
/**
* Show a message to all mobs in earshot of this one
*
* This would be for audible actions by the src mob
*
* vars:
* * message is the message output to anyone who can hear.
* * self_message (optional) is what the src mob hears.
* * deaf_message (optional) is what deaf people will see.
* * hearing_distance (optional) is the range, how many tiles away the message can be heard.
* * ignored_mobs (optional) doesn't show any message to any given mob in the list.
*/
/mob/audible_message(message, deaf_message, hearing_distance = DEFAULT_MESSAGE_RANGE, self_message, list/ignored_mobs)
. = ..()
if(self_message)
show_message(self_message, MSG_AUDIBLE, deaf_message, MSG_VISUAL)
/mob/proc/Life()
set waitfor = FALSE
@@ -390,9 +404,6 @@
if(I)
I.attack_self(src)
update_inv_hands()
if(!I)//CIT CHANGE - allows "using" empty hands
use_that_empty_hand() //CIT CHANGE - ditto
update_inv_hands() // CIT CHANGE - ditto.
/mob/verb/memory()
set name = "Notes"
@@ -712,6 +723,38 @@
client.last_turn = world.time + MOB_FACE_DIRECTION_DELAY
return TRUE
/mob/verb/eastshift()
set hidden = TRUE
if(!canface())
return FALSE
if(pixel_x <= 16)
pixel_x++
is_shifted = TRUE
/mob/verb/westshift()
set hidden = TRUE
if(!canface())
return FALSE
if(pixel_x >= -16)
pixel_x--
is_shifted = TRUE
/mob/verb/northshift()
set hidden = TRUE
if(!canface())
return FALSE
if(pixel_y <= 16)
pixel_y++
is_shifted = TRUE
/mob/verb/southshift()
set hidden = TRUE
if(!canface())
return FALSE
if(pixel_y >= -16)
pixel_y--
is_shifted = TRUE
/mob/proc/IsAdvancedToolUser()//This might need a rename but it should replace the can this mob use things check
return FALSE
+1 -1
View File
@@ -88,7 +88,7 @@
if(newletter==" ")
newletter="...huuuhhh..."
if(newletter==".")
newletter=" *BURP*."
newletter=" BURP!"
if(rand(1,100) <= strength*0.5)
if(rand(1,5) == 1)
newletter+="'"
+138 -130
View File
@@ -1,130 +1,138 @@
//Speech verbs.
// the _keybind verbs uses "as text" versus "as text|null" to force a popup when pressed by a keybind.
/mob/verb/say_typing_indicator()
set name = "say_indicator"
set hidden = TRUE
set category = "IC"
display_typing_indicator()
var/message = input(usr, "", "say") as text|null
// If they don't type anything just drop the message.
clear_typing_indicator() // clear it immediately!
if(!length(message))
return
return say_verb(message)
/mob/verb/say_verb(message as text)
set name = "say"
set category = "IC"
if(!length(message))
return
if(GLOB.say_disabled) //This is here to try to identify lag problems
to_chat(usr, "<span class='danger'>Speech is currently admin-disabled.</span>")
return
clear_typing_indicator() // clear it immediately!
say(message)
/mob/verb/me_typing_indicator()
set name = "me_indicator"
set hidden = TRUE
set category = "IC"
display_typing_indicator()
var/message = input(usr, "", "me") as message|null
// If they don't type anything just drop the message.
clear_typing_indicator() // clear it immediately!
if(!length(message))
return
return me_verb(message)
/mob/verb/me_verb(message as message)
set name = "me"
set category = "IC"
if(!length(message))
return
if(GLOB.say_disabled) //This is here to try to identify lag problems
to_chat(usr, "<span class='danger'>Speech is currently admin-disabled.</span>")
return
message = trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN))
clear_typing_indicator() // clear it immediately!
usr.emote("me",1,message,TRUE)
/mob/verb/whisper_verb(message as text)
set name = "Whisper"
set category = "IC"
if(!length(message))
return
if(GLOB.say_disabled) //This is here to try to identify lag problems
to_chat(usr, "<span class='danger'>Speech is currently admin-disabled.</span>")
return
whisper(message)
/mob/proc/whisper(message, datum/language/language=null)
say(message, language) //only living mobs actually whisper, everything else just talks
/mob/proc/say_dead(var/message)
var/name = real_name
var/alt_name = ""
if(GLOB.say_disabled) //This is here to try to identify lag problems
to_chat(usr, "<span class='danger'>Speech is currently admin-disabled.</span>")
return
var/jb = jobban_isbanned(src, "OOC")
if(QDELETED(src))
return
if(jb)
to_chat(src, "<span class='danger'>You have been banned from deadchat.</span>")
return
if (src.client)
if(src.client.prefs.muted & MUTE_DEADCHAT)
to_chat(src, "<span class='danger'>You cannot talk in deadchat (muted).</span>")
return
if(src.client.handle_spam_prevention(message,MUTE_DEADCHAT))
return
var/mob/dead/observer/O = src
if(isobserver(src) && O.deadchat_name)
name = "[O.deadchat_name]"
else
if(mind && mind.name)
name = "[mind.name]"
else
name = real_name
if(name != real_name)
alt_name = " (died as [real_name])"
var/spanned = say_quote(say_emphasis(message))
message = emoji_parse(message)
var/rendered = "<span class='game deadsay'><span class='prefix'>DEAD:</span> <span class='name'>[name]</span>[alt_name] <span class='message'>[emoji_parse(spanned)]</span></span>"
log_talk(message, LOG_SAY, tag="DEAD")
deadchat_broadcast(rendered, follow_target = src, speaker_key = key)
/mob/proc/check_emote(message)
if(copytext(message, 1, 2) == "*")
emote(copytext(message, 2), intentional = TRUE)
return 1
/mob/proc/hivecheck()
return 0
/mob/proc/lingcheck()
return LINGHIVE_NONE
/mob/proc/get_message_mode(message)
var/key = copytext(message, 1, 2)
if(key == "#")
return MODE_WHISPER
else if(key == "%")
return MODE_SING
else if(key == ";")
return MODE_HEADSET
else if(length(message) > 2 && (key in GLOB.department_radio_prefixes))
var/key_symbol = lowertext(copytext(message, 2, 3))
return GLOB.department_radio_keys[key_symbol]
//Speech verbs.
// the _keybind verbs uses "as text" versus "as text|null" to force a popup when pressed by a keybind.
/mob/verb/say_typing_indicator()
set name = "say_indicator"
set hidden = TRUE
set category = "IC"
display_typing_indicator()
var/message = input(usr, "", "say") as text|null
// If they don't type anything just drop the message.
clear_typing_indicator() // clear it immediately!
if(!length(message))
return
return say_verb(message)
/mob/verb/say_verb(message as text)
set name = "say"
set category = "IC"
if(!length(message))
return
if(GLOB.say_disabled) //This is here to try to identify lag problems
to_chat(usr, "<span class='danger'>Speech is currently admin-disabled.</span>")
return
clear_typing_indicator() // clear it immediately!
say(message)
/mob/say_mod(input, message_mode)
var/customsayverb = findtext(input, "*")
if(customsayverb && message_mode != MODE_WHISPER_CRIT)
message_mode = MODE_CUSTOM_SAY
return lowertext(copytext(input, 1, customsayverb))
else
return ..()
/mob/verb/me_typing_indicator()
set name = "me_indicator"
set hidden = TRUE
set category = "IC"
display_typing_indicator()
var/message = input(usr, "", "me") as message|null
// If they don't type anything just drop the message.
clear_typing_indicator() // clear it immediately!
if(!length(message))
return
return me_verb(message)
/mob/verb/me_verb(message as message)
set name = "me"
set category = "IC"
if(!length(message))
return
if(GLOB.say_disabled) //This is here to try to identify lag problems
to_chat(usr, "<span class='danger'>Speech is currently admin-disabled.</span>")
return
message = trim(copytext_char(sanitize(message), 1, MAX_MESSAGE_LEN))
clear_typing_indicator() // clear it immediately!
usr.emote("me",1,message,TRUE)
/mob/verb/whisper_verb(message as text)
set name = "Whisper"
set category = "IC"
if(!length(message))
return
if(GLOB.say_disabled) //This is here to try to identify lag problems
to_chat(usr, "<span class='danger'>Speech is currently admin-disabled.</span>")
return
whisper(message)
/mob/proc/whisper(message, datum/language/language=null)
say(message, language) //only living mobs actually whisper, everything else just talks
/mob/proc/say_dead(var/message)
var/name = real_name
var/alt_name = ""
if(GLOB.say_disabled) //This is here to try to identify lag problems
to_chat(usr, "<span class='danger'>Speech is currently admin-disabled.</span>")
return
var/jb = jobban_isbanned(src, "OOC")
if(QDELETED(src))
return
if(jb)
to_chat(src, "<span class='danger'>You have been banned from deadchat.</span>")
return
if (src.client)
if(src.client.prefs.muted & MUTE_DEADCHAT)
to_chat(src, "<span class='danger'>You cannot talk in deadchat (muted).</span>")
return
if(src.client.handle_spam_prevention(message,MUTE_DEADCHAT))
return
var/mob/dead/observer/O = src
if(isobserver(src) && O.deadchat_name)
name = "[O.deadchat_name]"
else
if(mind && mind.name)
name = "[mind.name]"
else
name = real_name
if(name != real_name)
alt_name = " (died as [real_name])"
var/spanned = say_quote(say_emphasis(message))
message = emoji_parse(message)
var/rendered = "<span class='game deadsay'><span class='prefix'>DEAD:</span> <span class='name'>[name]</span>[alt_name] <span class='message'>[emoji_parse(spanned)]</span></span>"
log_talk(message, LOG_SAY, tag="DEAD")
deadchat_broadcast(rendered, follow_target = src, speaker_key = key)
/mob/proc/check_emote(message)
if(copytext(message, 1, 2) == "*")
emote(copytext(message, 2), intentional = TRUE)
return 1
/mob/proc/hivecheck()
return 0
/mob/proc/lingcheck()
return LINGHIVE_NONE
/mob/proc/get_message_mode(message)
var/key = copytext(message, 1, 2)
if(key == "#")
return MODE_WHISPER
else if(key == "%")
return MODE_SING
else if(key == ";")
return MODE_HEADSET
else if(length(message) > 2 && (key in GLOB.department_radio_prefixes))
var/key_symbol = lowertext(copytext(message, 2, 3))
return GLOB.department_radio_keys[key_symbol]
+2 -2
View File
@@ -170,9 +170,9 @@ proc/get_top_level_mob(var/mob/S)
message = "<b>[user]</b> " + "<i>[user.say_emphasis(message)]</i>"
if(emote_type == EMOTE_AUDIBLE)
user.audible_message(message=message,hearing_distance=1, no_ghosts = TRUE)
user.audible_message(message=message,hearing_distance=1, ignored_mobs = GLOB.dead_mob_list)
else
user.visible_message(message=message,self_message=message,vision_distance=1, no_ghosts = TRUE)
user.visible_message(message=message,self_message=message,vision_distance=1, ignored_mobs = GLOB.dead_mob_list)
log_emote("[key_name(user)] : (SUBTLER) [message]")
message = null
@@ -719,10 +719,10 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
for(var/mob/living/L in range(10))
investigate_log("has irradiated [key_name(L)] after consuming [AM].", INVESTIGATE_SUPERMATTER)
if(L in view())
L.show_message("<span class='danger'>As \the [src] slowly stops resonating, you find your skin covered in new radiation burns.</span>", 1,\
"<span class='danger'>The unearthly ringing subsides and you notice you have new radiation burns.</span>", 2)
L.show_message("<span class='danger'>As \the [src] slowly stops resonating, you find your skin covered in new radiation burns.</span>", MSG_VISUAL,\
"<span class='danger'>The unearthly ringing subsides and you notice you have new radiation burns.</span>", MSG_AUDIBLE)
else
L.show_message("<span class='italics'>You hear an unearthly ringing and notice your skin is covered in fresh radiation burns.</span>", 2)
L.show_message("<span class='italics'>You hear an unearthly ringing and notice your skin is covered in fresh radiation burns.</span>", MSG_AUDIBLE)
//Do not blow up our internal radio
/obj/machinery/power/supermatter_crystal/contents_explosion(severity, target)
+2 -2
View File
@@ -58,10 +58,10 @@
/obj/item/firing_pin/proc/auth_fail(mob/living/user)
if(user)
user.show_message(fail_message, 1)
user.show_message(fail_message, MSG_VISUAL)
if(selfdestruct)
if(user)
user.show_message("<span class='danger'>SELF-DESTRUCTING...</span><br>", 1)
user.show_message("<span class='danger'>SELF-DESTRUCTING...</span><br>", MSG_VISUAL)
to_chat(user, "<span class='userdanger'>[gun] explodes!</span>")
explosion(get_turf(gun), -1, 0, 2, 3)
if(gun)
@@ -1743,6 +1743,7 @@ datum/reagent/consumable/ethanol/creme_de_coconut
value = 2
/datum/reagent/consumable/ethanol/peppermint_patty/on_mob_life(mob/living/carbon/M)
M.apply_status_effect(/datum/status_effect/throat_soothed)
M.adjust_bodytemperature(5 * TEMPERATURE_DAMAGE_COEFFICIENT, 0, BODYTEMP_NORMAL)
..()
@@ -760,6 +760,10 @@
glass_name = "glass of menthol"
glass_desc = "Tastes naturally minty, and imparts a very mild numbing sensation."
/datum/reagent/consumable/menthol/on_mob_life(mob/living/L)
L.apply_status_effect(/datum/status_effect/throat_soothed)
..()
/datum/reagent/consumable/grenadine
name = "Grenadine"
description = "Not cherry flavored!"
+1 -1
View File
@@ -115,7 +115,7 @@
if(user.incapacitated())
return
for(var/mob/M in range(5, get_turf(src)))
M.show_message("<FONT size=[max(0, 5 - get_dist(src, M))]>CLONG, clong!</FONT>", 2)
M.show_message("<FONT size=[max(0, 5 - get_dist(src, M))]>CLONG, clong!</FONT>", MSG_AUDIBLE)
playsound(src.loc, 'sound/effects/clang.ogg', 50, 0, 0)
// called to vent all gas in holder to a location
@@ -1,76 +1,86 @@
/////////////////////////////////////////
//////////////Blue Space/////////////////
/////////////////////////////////////////
/datum/design/beacon
name = "Tracking Beacon"
desc = "A blue space tracking beacon."
id = "beacon"
build_type = PROTOLATHE
materials = list(MAT_METAL = 150, MAT_GLASS = 100)
build_path = /obj/item/beacon
category = list("Bluespace Designs")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_SECURITY
/datum/design/bag_holding
name = "Bag of Holding"
desc = "A backpack that opens into a localized pocket of bluespace."
id = "bag_holding"
build_type = PROTOLATHE
materials = list(MAT_GOLD = 3000, MAT_DIAMOND = 1500, MAT_URANIUM = 250, MAT_BLUESPACE = 2000)
build_path = /obj/item/storage/backpack/holding
category = list("Bluespace Designs")
dangerous_construction = TRUE
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
/datum/design/satchel_holding
name = "Satchel of Holding"
desc = "A satchel that opens into a localized pocket of bluespace."
id = "satchel_holding"
build_type = PROTOLATHE
materials = list(MAT_GOLD = 3000, MAT_DIAMOND = 1500, MAT_URANIUM = 250, MAT_BLUESPACE = 2000)
build_path = /obj/item/storage/backpack/holding/satchel
category = list("Bluespace Designs")
dangerous_construction = TRUE
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
/datum/design/biobag_holding
name = "Bio Bag of Holding"
desc = "A chemical holding thingy. Mostly used for xenobiology."
id = "biobag_holding"
build_type = PROTOLATHE
materials = list(MAT_GOLD = 1500, MAT_DIAMOND = 750, MAT_URANIUM = 250, MAT_BLUESPACE = 1000)
build_path = /obj/item/storage/bag/bio/holding
category = list("Bluespace Designs")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
/datum/design/bluespace_crystal
name = "Artificial Bluespace Crystal"
desc = "A small blue crystal with mystical properties."
id = "bluespace_crystal"
build_type = PROTOLATHE
materials = list(MAT_DIAMOND = 1500, MAT_PLASMA = 1500)
build_path = /obj/item/stack/ore/bluespace_crystal/artificial
category = list("Bluespace Designs")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
/datum/design/telesci_gps
name = "GPS Device"
desc = "Little thingie that can track its position at all times."
id = "telesci_gps"
build_type = PROTOLATHE
materials = list(MAT_METAL = 500, MAT_GLASS = 1000)
build_path = /obj/item/gps
category = list("Bluespace Designs")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_CARGO
/datum/design/miningsatchel_holding
name = "Mining Satchel of Holding"
desc = "A mining satchel that can hold an infinite amount of ores."
id = "minerbag_holding"
build_type = PROTOLATHE
materials = list(MAT_GOLD = 250, MAT_URANIUM = 500) //quite cheap, for more convenience
build_path = /obj/item/storage/bag/ore/holding
category = list("Bluespace Designs")
departmental_flags = DEPARTMENTAL_FLAG_CARGO
/////////////////////////////////////////
//////////////Blue Space/////////////////
/////////////////////////////////////////
/datum/design/beacon
name = "Tracking Beacon"
desc = "A blue space tracking beacon."
id = "beacon"
build_type = PROTOLATHE
materials = list(MAT_METAL = 150, MAT_GLASS = 100)
build_path = /obj/item/beacon
category = list("Bluespace Designs")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_CARGO | DEPARTMENTAL_FLAG_SECURITY
/datum/design/bag_holding
name = "Bag of Holding"
desc = "A backpack that opens into a localized pocket of bluespace."
id = "bag_holding"
build_type = PROTOLATHE
materials = list(MAT_GOLD = 3000, MAT_DIAMOND = 1500, MAT_URANIUM = 250, MAT_BLUESPACE = 2000)
build_path = /obj/item/storage/backpack/holding
category = list("Bluespace Designs")
dangerous_construction = TRUE
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
/datum/design/satchel_holding
name = "Satchel of Holding"
desc = "A satchel that opens into a localized pocket of bluespace."
id = "satchel_holding"
build_type = PROTOLATHE
materials = list(MAT_GOLD = 3000, MAT_DIAMOND = 1500, MAT_URANIUM = 250, MAT_BLUESPACE = 2000)
build_path = /obj/item/storage/backpack/holding/satchel
category = list("Bluespace Designs")
dangerous_construction = TRUE
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
/datum/design/biobag_holding
name = "Bio Bag of Holding"
desc = "A chemical holding thingy. Mostly used for xenobiology."
id = "biobag_holding"
build_type = PROTOLATHE
materials = list(MAT_GOLD = 1500, MAT_DIAMOND = 750, MAT_URANIUM = 250, MAT_BLUESPACE = 1000)
build_path = /obj/item/storage/bag/bio/holding
category = list("Bluespace Designs")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
/datum/design/bluespace_crystal
name = "Artificial Bluespace Crystal"
desc = "A small blue crystal with mystical properties."
id = "bluespace_crystal"
build_type = PROTOLATHE
materials = list(MAT_DIAMOND = 1500, MAT_PLASMA = 1500)
build_path = /obj/item/stack/ore/bluespace_crystal/artificial
category = list("Bluespace Designs")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
/datum/design/telesci_gps
name = "GPS Device"
desc = "Little thingie that can track its position at all times."
id = "telesci_gps"
build_type = PROTOLATHE
materials = list(MAT_METAL = 500, MAT_GLASS = 1000)
build_path = /obj/item/gps
category = list("Bluespace Designs")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE | DEPARTMENTAL_FLAG_ENGINEERING | DEPARTMENTAL_FLAG_CARGO
/datum/design/desynchronizer
name = "Desynchronizer"
desc = "A device that can desynchronize the user from spacetime."
id = "desynchronizer"
build_type = PROTOLATHE
materials = list(MAT_METAL = 1000, MAT_GLASS = 500, MAT_SILVER = 1500, MAT_BLUESPACE = 1000)
build_path = /obj/item/desynchronizer
category = list("Bluespace Designs")
departmental_flags = DEPARTMENTAL_FLAG_SCIENCE
/datum/design/miningsatchel_holding
name = "Mining Satchel of Holding"
desc = "A mining satchel that can hold an infinite amount of ores."
id = "minerbag_holding"
build_type = PROTOLATHE
materials = list(MAT_GOLD = 250, MAT_URANIUM = 500) //quite cheap, for more convenience
build_path = /obj/item/storage/bag/ore/holding
category = list("Bluespace Designs")
departmental_flags = DEPARTMENTAL_FLAG_CARGO
@@ -208,9 +208,6 @@ datum/nanite_program/proc/on_mob_remove()
/datum/nanite_program/proc/on_death()
return
/datum/nanite_program/proc/on_hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode)
return
/datum/nanite_program/proc/software_error(type)
if(!type)
type = rand(1,5)
@@ -345,6 +345,14 @@
var/sentence = ""
var/inclusive = TRUE
/datum/nanite_program/sensor/voice/on_mob_add()
. = ..()
RegisterSignal(host_mob, COMSIG_MOVABLE_HEAR, .proc/on_hear)
/datum/nanite_program/sensor/voice/on_mob_remove()
UnregisterSignal(host_mob, COMSIG_MOVABLE_HEAR, .proc/on_hear)
/datum/nanite_program/sensor/voice/set_extra_setting(user, setting)
if(setting == "Sent Code")
var/new_code = input(user, "Set the sent code (1-9999):", name, null) as null|num
@@ -378,15 +386,12 @@
target.sentence = sentence
target.inclusive = inclusive
/datum/nanite_program/sensor/voice/on_hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode)
/datum/nanite_program/sensor/voice/proc/on_hear(datum/source, list/hearing_args)
if(!sentence)
return
//To make it not case sensitive
var/low_message = lowertext(raw_message)
var/low_sentence = lowertext(sentence)
if(inclusive)
if(findtext(low_message, low_sentence))
if(findtextEx(hearing_args[HEARING_RAW_MESSAGE], sentence))
send_code()
else
if(low_message == low_sentence)
if(hearing_args[HEARING_RAW_MESSAGE] == sentence)
send_code()
@@ -323,6 +323,15 @@
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 5000
/datum/techweb_node/unregulated_bluespace
id = "unregulated_bluespace"
display_name = "Unregulated Bluespace Research"
description = "Bluespace technology using unstable or unbalanced procedures, prone to damaging the fabric of bluespace. Outlawed by galactic conventions."
prereq_ids = list("bluespace_warping", "syndicate_basic")
design_ids = list("desynchronizer")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500)
export_price = 2500
/////////////////////////plasma tech/////////////////////////
/datum/techweb_node/basic_plasma
id = "basic_plasma"
+2 -1
View File
@@ -85,7 +85,8 @@
/obj/item/stack/tile/bronze = /datum/species/golem/bronze,
/obj/item/stack/sheet/cardboard = /datum/species/golem/cardboard,
/obj/item/stack/sheet/leather = /datum/species/golem/leather,
/obj/item/stack/sheet/bone = /datum/species/golem/bone)
/obj/item/stack/sheet/bone = /datum/species/golem/bone,
/obj/item/stack/sheet/cotton/durathread = /datum/species/golem/durathread)
if(istype(I, /obj/item/stack))
var/obj/item/stack/O = I