")
text = replacetext(text, "\[logo\]", "")
text = replacetext(text, "\[time\]", "[station_time_timestamp()]") // TO DO
- if(P)
- text = "[text]"
- else
- text = "[text]"
+ if(!no_font)
+ if(P)
+ text = "[text]"
+ else
+ text = "[text]"
+
text = copytext(text, 1, MAX_PAPER_MESSAGE_LEN)
return text
diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm
index 9e8b9b9c3a6..e36d5c027f5 100644
--- a/code/_onclick/item_attack.dm
+++ b/code/_onclick/item_attack.dm
@@ -7,6 +7,8 @@
// Called when the item is in the active hand, and clicked; alternately, there is an 'activate held object' verb or you can hit pagedown.
/obj/item/proc/attack_self(mob/user)
+ if(SEND_SIGNAL(src, COMSIG_ITEM_ATTACK_SELF, user) & COMPONENT_NO_INTERACT)
+ return
return
/obj/item/proc/pre_attackby(atom/A, mob/living/user, params) //do stuff before attackby!
@@ -28,9 +30,12 @@
return I.attack(src, user)
/obj/item/proc/attack(mob/living/M, mob/living/user, def_zone)
+ SEND_SIGNAL(src, COMSIG_ITEM_ATTACK, M, user)
+ SEND_SIGNAL(user, COMSIG_MOB_ITEM_ATTACK, M, user)
if(flags & (NOBLUDGEON))
return 0
-
+ if(check_martial_counter(M, user))
+ return 0
if(can_operate(M)) //Checks if mob is lying down on table for surgery
if(istype(src,/obj/item/robot_parts))//popup override for direct attach
if(!attempt_initiate_surgery(src, M, user,1))
@@ -74,6 +79,8 @@
//the equivalent of the standard version of attack() but for object targets.
/obj/item/proc/attack_obj(obj/O, mob/living/user)
+ if(SEND_SIGNAL(src, COMSIG_ITEM_ATTACK_OBJ, O, user) & COMPONENT_NO_ATTACK_OBJ)
+ return
if(flags & (NOBLUDGEON))
return
user.changeNext_move(CLICK_CD_MELEE)
diff --git a/code/datums/components/squeak.dm b/code/datums/components/squeak.dm
new file mode 100644
index 00000000000..e9b6bb6e42a
--- /dev/null
+++ b/code/datums/components/squeak.dm
@@ -0,0 +1,88 @@
+// Squeak component ported over from tg
+
+/datum/component/squeak
+ var/static/list/default_squeak_sounds = list('sound/items/toysqueak1.ogg'=1, 'sound/items/toysqueak2.ogg'=1, 'sound/items/toysqueak3.ogg'=1)
+ var/list/override_squeak_sounds
+ var/squeak_chance = 100
+ var/volume = 30
+
+ // This is so shoes don't squeak every step
+ var/steps = 0
+ var/step_delay = 1
+
+ // This is to stop squeak spam from inhand usage
+ var/last_use = 0
+ var/use_delay = 20
+
+/datum/component/squeak/Initialize(custom_sounds, volume_override, chance_override, step_delay_override, use_delay_override)
+ if(!isatom(parent))
+ return COMPONENT_INCOMPATIBLE
+ RegisterSignal(parent, list(COMSIG_ATOM_ENTERED, COMSIG_ATOM_BLOB_ACT, COMSIG_ATOM_HULK_ATTACK, COMSIG_PARENT_ATTACKBY), .proc/play_squeak)
+ if(ismovableatom(parent))
+ RegisterSignal(parent, list(COMSIG_MOVABLE_BUMP, COMSIG_MOVABLE_IMPACT), .proc/play_squeak)
+ RegisterSignal(parent, COMSIG_MOVABLE_CROSSED, .proc/play_squeak_crossed)
+ RegisterSignal(parent, COMSIG_MOVABLE_DISPOSING, .proc/disposing_react)
+ if(isitem(parent))
+ RegisterSignal(parent, list(COMSIG_ITEM_ATTACK, COMSIG_ITEM_ATTACK_OBJ, COMSIG_ITEM_HIT_REACT), .proc/play_squeak)
+ RegisterSignal(parent, COMSIG_ITEM_ATTACK_SELF, .proc/use_squeak)
+ RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, .proc/on_equip)
+ RegisterSignal(parent, COMSIG_ITEM_DROPPED, .proc/on_drop)
+ if(istype(parent, /obj/item/clothing/shoes))
+ RegisterSignal(parent, COMSIG_SHOES_STEP_ACTION, .proc/step_squeak)
+
+ override_squeak_sounds = custom_sounds
+ if(chance_override)
+ squeak_chance = chance_override
+ if(volume_override)
+ volume = volume_override
+ if(isnum(step_delay_override))
+ step_delay = step_delay_override
+ if(isnum(use_delay_override))
+ use_delay = use_delay_override
+
+/datum/component/squeak/proc/play_squeak()
+ if(prob(squeak_chance))
+ if(!override_squeak_sounds)
+ playsound(parent, pickweight(default_squeak_sounds), volume, 1, -1)
+ else
+ playsound(parent, pickweight(override_squeak_sounds), volume, 1, -1)
+
+/datum/component/squeak/proc/step_squeak()
+ if(steps > step_delay)
+ play_squeak()
+ steps = 0
+ else
+ steps++
+
+/datum/component/squeak/proc/on_equip(datum/source, mob/equipper, slot)
+ RegisterSignal(equipper, COMSIG_MOVABLE_DISPOSING, .proc/disposing_react, TRUE)
+
+/datum/component/squeak/proc/on_drop(datum/source, mob/user)
+ UnregisterSignal(user, COMSIG_MOVABLE_DISPOSING)
+
+/datum/component/squeak/proc/play_squeak_crossed(atom/movable/AM)
+ if(isitem(AM))
+ var/obj/item/I = AM
+ if(I.flags & ABSTRACT)
+ return
+ else if(istype(AM, /obj/item/projectile))
+ var/obj/item/projectile/P = AM
+ if(P.original != parent)
+ return
+ var/atom/current_parent = parent
+ if(isturf(current_parent.loc))
+ play_squeak()
+
+/datum/component/squeak/proc/use_squeak()
+ if(last_use + use_delay < world.time)
+ last_use = world.time
+ play_squeak()
+
+/datum/component/squeak/proc/disposing_react(datum/source, obj/structure/disposalholder/holder, obj/machinery/disposal/source)
+ //We don't need to worry about unregistering this signal as it will happen for us automaticaly when the holder is qdeleted
+ RegisterSignal(holder, COMSIG_ATOM_DIR_CHANGE, .proc/holder_dir_change)
+
+/datum/component/squeak/proc/holder_dir_change(datum/source, old_dir, new_dir)
+ //If the dir changes it means we're going through a bend in the pipes, let's pretend we bumped the wall
+ if(old_dir != new_dir)
+ play_squeak()
\ No newline at end of file
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 6f1fb59e8f8..18f229bdc4c 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -58,6 +58,10 @@
var/rev_cooldown = 0
+ var/isholy = FALSE // is this person a chaplain or admin role allowed to use bibles
+ var/isblessed = FALSE // is this person blessed by a chaplain?
+ var/num_blessed = 0 // for prayers
+
// the world.time since the mob has been brigged, or -1 if not at all
var/brigged_since = -1
var/suicided = FALSE
@@ -375,9 +379,9 @@
/** SILICON ***/
if(issilicon(current))
sections["silicon"] = memory_edit_silicon()
- /*
- This prioritizes antags relevant to the current round to make them appear at the top of the panel.
- Traitorchan and traitorvamp are snowflaked in because they have multiple sections.
+ /*
+ This prioritizes antags relevant to the current round to make them appear at the top of the panel.
+ Traitorchan and traitorvamp are snowflaked in because they have multiple sections.
*/
if(ticker.mode.config_tag == "traitorchan")
if(sections["traitor"])
diff --git a/code/datums/outfits/outfit_admin.dm b/code/datums/outfits/outfit_admin.dm
index bf296acd589..2761a8063ee 100644
--- a/code/datums/outfits/outfit_admin.dm
+++ b/code/datums/outfits/outfit_admin.dm
@@ -64,34 +64,14 @@
if(istype(R))
R.set_frequency(SYND_FREQ)
-/datum/outfit/admin/syndicate/infiltrator
+
+/datum/outfit/admin/syndicate_infiltrator
name = "Syndicate Infiltrator"
- uniform = /obj/item/clothing/under/chameleon
- glasses = /obj/item/clothing/glasses/hud/security/chameleon
- shoes = /obj/item/clothing/shoes/syndigaloshes
- r_pocket = null
- pda = /obj/item/pda
+/datum/outfit/admin/syndicate_infiltrator/equip(mob/living/carbon/human/H, visualsOnly = FALSE)
+ . = H.equip_syndicate_infiltrator(0, 20, FALSE)
+ H.sec_hud_set_ID()
- backpack_contents = list(
- /obj/item/storage/box/engineer = 1,
- /obj/item/flashlight = 1,
- /obj/item/reagent_containers/food/snacks/syndidonkpocket = 1
- )
-
-/datum/outfit/admin/syndicate/infiltrator/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
- . = ..()
- if(visualsOnly)
- return
- if(H.gloves)
- H.gloves.name = "black gloves"
-
- var/obj/item/implant/uplink/U = new /obj/item/implant/uplink(H)
- U.implant(H)
- U.hidden_uplink.uses = uplink_uses
-
- var/obj/item/implant/dust/D = new /obj/item/implant/dust(H)
- D.implant(H)
/datum/outfit/admin/syndicate/operative
name = "Syndicate Nuclear Operative"
@@ -126,17 +106,20 @@
var/obj/item/implant/explosive/E = new(H)
E.implant(H)
+
/datum/outfit/admin/syndicate/operative/freedom
name = "Syndicate Freedom Operative"
suit = /obj/item/clothing/suit/space/hardsuit/syndi/freedom
head = /obj/item/clothing/head/helmet/space/hardsuit/syndi/freedom
+
/datum/outfit/admin/syndicate_strike_team
name = "Syndicate Strike Team"
/datum/outfit/admin/syndicate_strike_team/equip(mob/living/carbon/human/H, visualsOnly = FALSE)
return H.equip_syndicate_commando()
+
/datum/outfit/admin/syndicate/officer
name = "Syndicate Officer"
@@ -156,7 +139,9 @@
/obj/item/clothing/shoes/magboots/syndie/advance = 1,
/obj/item/lighter/zippo/gonzofist = 1
)
-
+ implants = list(
+ /obj/item/implant/dust
+ )
id_icon = "commander"
id_access = "Syndicate Operative Leader"
@@ -169,8 +154,17 @@
U.implant(H)
U.hidden_uplink.uses = 500
- var/obj/item/implant/dust/D = new(H)
- D.implant(H)
+
+/datum/outfit/admin/syndicate/spy
+ name = "Syndicate Spy"
+ uniform = /obj/item/clothing/under/suit_jacket/really_black
+ shoes = /obj/item/clothing/shoes/syndigaloshes/black
+ uplink_uses = 40
+ id_access = "Syndicate Agent"
+
+ implants = list(
+ /obj/item/implant/dust
+ )
/datum/outfit/admin/nt_vip
@@ -196,7 +190,7 @@
var/obj/item/card/id/I = H.wear_id
if(istype(I))
apply_to_card(I, H, get_centcom_access("VIP Guest"), "VIP Guest")
-
+ H.sec_hud_set_ID()
/datum/outfit/admin/nt_navy_captain
name = "NT Navy Captain"
@@ -228,6 +222,7 @@
var/obj/item/card/id/I = H.wear_id
if(istype(I))
apply_to_card(I, H, get_centcom_access("Nanotrasen Navy Captain"), "Nanotrasen Navy Captain")
+ H.sec_hud_set_ID()
/datum/outfit/admin/nt_diplomat
name = "NT Diplomat"
@@ -258,39 +253,35 @@
var/obj/item/card/id/I = H.wear_id
if(istype(I))
apply_to_card(I, H, get_centcom_access("Nanotrasen Navy Representative"), "Nanotrasen Diplomat")
-
+ // Will show as ? on sec huds, as this is not a recognized rank.
/datum/outfit/admin/nt_undercover
name = "NT Undercover Operative"
- // Disguised NT special forces, sent to quietly eliminate mutinous people in high positions (e.g: captain)
+ // Disguised NT special forces, sent to quietly eliminate or keep tabs on people in high positions (e.g: captain)
- uniform = /obj/item/clothing/under/chameleon
+ uniform = /obj/item/clothing/under/color/black
back = /obj/item/storage/backpack
belt = /obj/item/storage/belt/utility/full/multitool
gloves = /obj/item/clothing/gloves/combat
- shoes = /obj/item/clothing/shoes/syndigaloshes
- l_ear = /obj/item/radio/headset/heads/captain
- id = /obj/item/card/id/syndicate
+ shoes = /obj/item/clothing/shoes/syndigaloshes/black
+ l_ear = /obj/item/radio/headset/centcom
+ id = /obj/item/card/id
pda = /obj/item/pda
backpack_contents = list(
/obj/item/storage/box/engineer = 1,
/obj/item/flashlight = 1,
- /obj/item/gun/projectile/automatic/proto = 1, // NT saber SMG
- /obj/item/suppressor = 1, // silencer for SMG
- /obj/item/ammo_box/magazine/smgm9mm = 3, // SMG ammo
- /obj/item/door_remote/omni = 1,
- /obj/item/implanter/mindshield = 1, // not implanted by default, as that would make them suspicious
- /obj/item/implanter/storage = 1 // do not auto-implant this, that causes a bug
+ /obj/item/pinpointer/crew = 1
)
implants = list(
/obj/item/implant/dust
)
cybernetic_implants = list(
+ /obj/item/organ/internal/cyberimp/eyes/shield,
/obj/item/organ/internal/cyberimp/eyes/hud/security,
/obj/item/organ/internal/cyberimp/eyes/xray,
- /obj/item/organ/internal/cyberimp/brain/anti_drop,
/obj/item/organ/internal/cyberimp/brain/anti_stun,
- /obj/item/organ/internal/cyberimp/chest/nutriment/plus
+ /obj/item/organ/internal/cyberimp/chest/nutriment/plus,
+ /obj/item/organ/internal/cyberimp/arm/combat/centcom
)
/datum/outfit/admin/nt_undercover/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
@@ -300,16 +291,14 @@
var/obj/item/card/id/I = H.wear_id
if(istype(I))
- apply_to_card(I, H, get_all_accesses(), "Civilian")
- I.icon_state = "deathsquad"
-
+ apply_to_card(I, H, get_centcom_access("NT Undercover Operative"), "Civilian")
+ H.sec_hud_set_ID() // Force it to show as Civ on sec huds
var/obj/item/radio/R = H.l_ear
if(istype(R))
R.name = "radio headset"
R.icon_state = "headset"
-
/datum/outfit/admin/death_commando
name = "NT Death Commando"
@@ -321,11 +310,15 @@
uniform = /obj/item/clothing/under/pirate
back = /obj/item/storage/backpack/satchel
+ belt = /obj/item/storage/belt/utility/full/multitool
+ gloves = /obj/item/clothing/gloves/combat
shoes = /obj/item/clothing/shoes/brown
+ l_ear = /obj/item/radio/headset
id = /obj/item/card/id
r_hand = /obj/item/melee/energy/sword/pirate
backpack_contents = list(
- /obj/item/storage/box/survival = 1
+ /obj/item/storage/box/survival = 1,
+ /obj/item/flashlight = 1
)
/datum/outfit/admin/pirate/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
@@ -405,7 +398,11 @@
backpack_contents = list(
/obj/item/storage/box/survival = 1,
/obj/item/flashlight = 1,
- /obj/item/reagent_containers/food/drinks/bottle/bottleofbanana = 1
+ /obj/item/reagent_containers/food/drinks/bottle/bottleofbanana = 1,
+ /obj/item/grenade/clown_grenade = 1,
+ /obj/item/melee/baton/cattleprod = 1,
+ /obj/item/stock_parts/cell/super = 1,
+ /obj/item/bikehorn/rubberducky = 1
)
/datum/outfit/admin/tunnel_clown/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
@@ -436,8 +433,10 @@
/obj/item/storage/box/survival = 1,
/obj/item/reagent_containers/food/drinks/bottle/bottleofnothing = 1,
/obj/item/toy/crayon/mime = 1,
- /obj/item/storage/box/syndie_kit/caneshotgun = 1,
- /obj/item/ammo_casing/shotgun/incendiary/dragonsbreath = 2,
+ /obj/item/gun/projectile/automatic/pistol = 1,
+ /obj/item/ammo_box/magazine/m10mm = 1,
+ /obj/item/suppressor = 1,
+ /obj/item/card/emag = 1,
/obj/item/radio/uplink = 1,
/obj/item/reagent_containers/food/snacks/syndidonkpocket = 1,
/obj/item/flashlight = 1
@@ -463,6 +462,7 @@
var/obj/item/card/id/I = H.wear_id
if(istype(I))
apply_to_card(I, H, list(access_mime, access_theatre, access_maint_tunnels), "Mime")
+ H.sec_hud_set_ID()
/datum/outfit/admin/greytide
name = "Greytide"
@@ -538,6 +538,45 @@
if(istype(I))
apply_to_card(I, H, list(access_maint_tunnels), "Legit Xenomorph")
+
+
+/datum/outfit/admin/musician
+ name = "Musician"
+
+ uniform = /obj/item/clothing/under/singerb
+ back = /obj/item/storage/backpack
+ shoes = /obj/item/clothing/shoes/singerb
+ gloves = /obj/item/clothing/gloves/color/white
+ l_ear = /obj/item/radio/headset
+ r_ear = /obj/item/clothing/ears/headphones
+ pda = /obj/item/pda
+ id = /obj/item/card/id
+ backpack_contents = list(
+ /obj/item/storage/box/survival = 1,
+ /obj/item/flashlight = 1,
+ /obj/item/instrument/violin = 1,
+ /obj/item/instrument/piano_synth = 1,
+ /obj/item/instrument/guitar = 1,
+ /obj/item/instrument/eguitar = 1,
+ /obj/item/instrument/accordion = 1,
+ /obj/item/instrument/saxophone = 1,
+ /obj/item/instrument/trombone = 1,
+ /obj/item/instrument/harmonica = 1
+ )
+
+/datum/outfit/admin/musician/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
+ . = ..()
+ if(visualsOnly)
+ return
+
+ var/obj/item/card/id/I = H.wear_id
+ if(istype(I))
+ apply_to_card(I, H, list(access_maint_tunnels), "Bard")
+
+ var/obj/item/clothing/ears/headphones/P = r_ear
+ if(istype(P))
+ P.attack_self(H) // activate them, display musical notes effect
+
/datum/outfit/admin/soviet
gloves = /obj/item/clothing/gloves/combat
@@ -594,7 +633,6 @@
l_ear = /obj/item/radio/headset/syndicate
r_ear = null
glasses = /obj/item/clothing/glasses/thermal/eyepatch
- id = null
l_pocket = null
r_pocket = null
suit_store = null
@@ -930,7 +968,8 @@
uniform = /obj/item/clothing/under/syndicate/combat
suit = /obj/item/clothing/suit/space/hardsuit/singuloth
- back = /obj/item/twohanded/knighthammer
+ back = /obj/item/storage/backpack/satchel
+ l_hand = /obj/item/twohanded/knighthammer
belt = /obj/item/claymore/ceremonial
gloves = /obj/item/clothing/gloves/combat
shoes = /obj/item/clothing/shoes/magboots
@@ -940,6 +979,10 @@
glasses = /obj/item/clothing/glasses/meson/cyber
id = /obj/item/card/id
suit_store = /obj/item/tank/oxygen
+ backpack_contents = list(
+ /obj/item/storage/box/survival = 1,
+ /obj/item/flashlight = 1
+ )
/datum/outfit/admin/singuloth_knight/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
. = ..()
@@ -950,95 +993,14 @@
if(istype(I))
apply_to_card(I, H, get_all_accesses(), "Singuloth Knight")
-/datum/outfit/admin/assassin
- name = "Syndicate Assassin"
-
- uniform = /obj/item/clothing/under/suit_jacket
- suit = /obj/item/clothing/suit/wcoat
- back = /obj/item/storage/backpack
- gloves = /obj/item/clothing/gloves/color/black
- shoes = /obj/item/clothing/shoes/black
- l_ear = /obj/item/radio/headset/syndicate
- glasses = /obj/item/clothing/glasses/sunglasses
- id = /obj/item/card/id/syndicate
- l_pocket = /obj/item/melee/energy/sword/saber
- l_hand = /obj/item/storage/secure/briefcase/reaper
- pda = /obj/item/pda/heads
- backpack_contents = list(
- /obj/item/storage/box/survival = 1,
- /obj/item/flashlight = 1
- )
- implants = list(
- /obj/item/implant/dust
- )
-
-/datum/outfit/admin/assassin/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
- . = ..()
- if(visualsOnly)
- return
-
- var/obj/item/pda/PDA = H.wear_pda
- if(istype(PDA))
- PDA.owner = H.real_name
- PDA.ownjob = "Reaper"
- PDA.name = "PDA-[H.real_name] ([PDA.ownjob])"
-
- var/obj/item/card/id/I = H.wear_id
- if(istype(I))
- apply_to_card(I, H, get_all_accesses(), "Reaper", "syndie")
-
-/datum/outfit/admin/spy
- name = "Spy"
-
- uniform = /obj/item/clothing/under/suit_jacket/really_black
- back = /obj/item/storage/backpack
- gloves = /obj/item/clothing/gloves/combat
- shoes = /obj/item/clothing/shoes/black
- l_ear = /obj/item/radio/headset/syndicate
- glasses = /obj/item/clothing/glasses/hud/security/chameleon
- id = /obj/item/card/id/syndicate
- l_pocket = /obj/item/melee/energy/sword/saber
- r_pocket = /obj/item/pen/sleepy
- pda = /obj/item/pda/heads
- backpack_contents = list(
- /obj/item/storage/box/survival = 1,
- /obj/item/gun/projectile/automatic/pistol = 1,
- /obj/item/suppressor = 1,
- /obj/item/card/emag = 1,
- /obj/item/flashlight = 1,
- /obj/item/implanter/storage = 1 // do not auto-implant this, that causes a bug
- )
- implants = list(
- /obj/item/implant/dust
- )
-
-/datum/outfit/admin/spy/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
- . = ..()
- if(visualsOnly)
- return
-
- var/obj/item/clothing/gloves/combat/G = H.gloves
- if(istype(G))
- G.name = "black gloves"
-
- var/obj/item/pda/PDA = H.wear_pda
- if(istype(PDA))
- PDA.owner = H.real_name
- PDA.ownjob = "Spy"
- PDA.name = "PDA-[H.real_name] ([PDA.ownjob])"
-
- var/obj/item/card/id/I = H.wear_id
- if(istype(I))
- apply_to_card(I, H, list(access_maint_tunnels), "Spy", "syndie")
-
/datum/outfit/admin/dark_lord
name = "Dark Lord"
uniform = /obj/item/clothing/under/color/black
suit = /obj/item/clothing/suit/hooded/chaplain_hoodie
back = /obj/item/storage/backpack
- gloves = /obj/item/clothing/gloves/color/black
- shoes = /obj/item/clothing/shoes/black
+ gloves = /obj/item/clothing/gloves/combat
+ shoes = /obj/item/clothing/shoes/syndigaloshes/black
l_ear = /obj/item/radio/headset/syndicate
id = /obj/item/card/id/syndicate
l_hand = /obj/item/twohanded/dualsaber/red
@@ -1062,6 +1024,55 @@
apply_to_card(I, H, get_all_accesses(), "Dark Lord", "syndie")
+/datum/outfit/admin/ancient_vampire
+ name = "Ancient Vampire"
+
+ uniform = /obj/item/clothing/under/victsuit/red
+ suit = /obj/item/clothing/suit/draculacoat
+ back = /obj/item/storage/backpack
+ gloves = /obj/item/clothing/gloves/combat
+ shoes = /obj/item/clothing/shoes/syndigaloshes/black
+ l_ear = /obj/item/radio/headset/syndicate
+ id = /obj/item/card/id
+ backpack_contents = list(
+ /obj/item/storage/box/survival = 1,
+ /obj/item/flashlight = 1,
+ /obj/item/clothing/under/color/black = 1
+ )
+
+/datum/outfit/admin/ancient_vampire/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
+ . = ..()
+ if(visualsOnly)
+ return
+
+ var/obj/item/clothing/suit/hooded/chaplain_hoodie/C = new(H.loc)
+ if(istype(C))
+ C.name = "ancient robes"
+ C.hood.name = "ancient hood"
+ H.equip_to_slot_or_del(C, slot_in_backpack)
+
+ var/obj/item/card/id/I = H.wear_id
+ if(istype(I))
+ apply_to_card(I, H, get_all_accesses(), "Ancient One", "data")
+
+ if(H.mind)
+ if(!H.mind.vampire)
+ H.make_vampire()
+ if(H.mind.vampire)
+ H.mind.vampire.bloodusable = 9999
+ H.mind.vampire.bloodtotal = 9999
+ H.mind.vampire.check_vampire_upgrade(0)
+ H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/shapeshift/bats)
+ to_chat(H, "You have gained the ability to shapeshift into bat form. This is a weak form with no abilities, only useful for stealth.")
+ H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/shapeshift/hellhound)
+ to_chat(H, "You have gained the ability to shapeshift into lesser hellhound form. This is a combat form with different abilities, tough but not invincible. It can regenerate itself over time by resting.")
+ H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/raise_vampires)
+ to_chat(H, "You have gained the ability to Raise Vampires. This extremely powerful AOE ability affects all humans near you. Vampires/thralls are healed. Corpses are raised as vampires. Others are stunned, then brain damaged, then killed.")
+ H.dna.SetSEState(JUMPBLOCK, 1)
+ genemutcheck(H, JUMPBLOCK, null, MUTCHK_FORCED)
+ H.update_mutations()
+ H.gene_stability = 100
+
/datum/outfit/admin/wizard
name = "Blue Wizard"
uniform = /obj/item/clothing/under/color/lightpurple
diff --git a/code/datums/spells/chaplain.dm b/code/datums/spells/chaplain.dm
new file mode 100644
index 00000000000..f9c2641c50e
--- /dev/null
+++ b/code/datums/spells/chaplain.dm
@@ -0,0 +1,73 @@
+
+/obj/effect/proc_holder/spell/targeted/chaplain_bless
+ name = "Bless"
+ desc = "Blesses a single person."
+
+ school = "transmutation"
+ charge_max = 60
+ clothes_req = 0
+ invocation = "none"
+ invocation_type = "none"
+
+ max_targets = 1
+ include_user = 0
+ humans_only = 1
+
+ range = 1
+ cooldown_min = 20
+ action_icon_state = "shield"
+
+
+/obj/effect/proc_holder/spell/targeted/chaplain_bless/cast(list/targets, mob/living/user = usr, distanceoverride)
+
+ if(!istype(user))
+ to_chat(user, "Somehow, you are not a living mob. This should never happen. Report this bug.")
+ revert_cast()
+ return
+
+ if(!user.mind)
+ to_chat(user, "Somehow, you are mindless. This should never happen. Report this bug.")
+ revert_cast()
+ return
+
+ if(!user.mind.isholy)
+ to_chat(user, "Somehow, you are not holy enough to use this ability. This should never happen. Report this bug.")
+ revert_cast()
+ return
+
+ var/mob/living/carbon/human/target = targets[range]
+
+ if(!istype(target))
+ to_chat(user, "No target.")
+ revert_cast()
+ return
+
+ if(!(target in oview(range)) && !distanceoverride)//If they are not in overview after selection. Do note that !() is necessary for in to work because ! takes precedence over it.
+ to_chat(user, "[target] is too far away!")
+ revert_cast()
+ return
+
+ if(!target.mind)
+ to_chat(user, "[target] appears to be catatonic. Your blessing would have no effect.")
+ revert_cast()
+ return
+
+ if(!target.ckey)
+ to_chat(user, "[target] appears to be too out of it to benefit from this.")
+ revert_cast()
+ return
+
+ if(target.stat == DEAD)
+ to_chat(user, "[target] is already dead. There is no point.")
+ revert_cast()
+ return
+
+ spawn(0) // allows cast to complete even if recipient ignores the prompt
+ if(alert(target, "[user] wants to bless you, in the name of [user.p_their()] religion. Accept?", "Accept Blessing?", "Yes", "No") == "Yes") // prevents forced conversions
+ user.visible_message("[user] starts blessing [target] in the name of [ticker.Bible_deity_name].", "You start blessing [target] in the name of [ticker.Bible_deity_name].")
+ if(do_after(user, 150, target = target))
+ user.visible_message("[user] has blessed [target] in the name of [ticker.Bible_deity_name].", "You have blessed [target] in the name of [ticker.Bible_deity_name].")
+ if(!target.mind.isblessed)
+ target.mind.isblessed = TRUE
+ user.mind.num_blessed++
+
diff --git a/code/datums/spells/mime.dm b/code/datums/spells/mime.dm
index 19b30674bf6..7f1ca22b709 100644
--- a/code/datums/spells/mime.dm
+++ b/code/datums/spells/mime.dm
@@ -1,5 +1,5 @@
/obj/effect/proc_holder/spell/aoe_turf/conjure/mime_wall
- name = "Invisible wall"
+ name = "Invisible Wall"
desc = "The mime's performance transmutates into physical reality."
school = "mime"
panel = "Mime"
@@ -12,7 +12,7 @@
range = 0
cast_sound = null
human_req = 1
-
+
action_icon_state = "mime"
action_background_icon_state = "bg_mime"
@@ -60,3 +60,107 @@
to_chat(H, "You make a vow of silence.")
else
to_chat(H, "You break your vow of silence.")
+
+//Advanced Mimery traitor item spells
+
+/obj/effect/proc_holder/spell/targeted/forcewall/mime
+ name = "Invisible Greater Wall"
+ desc = "Form an invisible three tile wide blockade."
+ school = "mime"
+ panel = "Mime"
+ wall_type = /obj/effect/forcefield/mime/advanced
+ invocation_type = "emote"
+ invocation_emote_self = "You form a blockade in front of yourself."
+ charge_max = 600
+ sound = null
+ clothes_req = FALSE
+ range = -1
+ include_user = TRUE
+
+ action_icon_state = "mime"
+ action_background_icon_state = "bg_mime"
+
+/obj/effect/proc_holder/spell/targeted/forcewall/mime/Click()
+ if(usr && usr.mind)
+ if(!usr.mind.miming)
+ to_chat(usr, "You must dedicate yourself to silence first.")
+ return
+ invocation = "[usr.real_name] looks as if a blockade is in front of [usr.p_them()]."
+ else
+ invocation_type ="none"
+ ..()
+
+/obj/effect/proc_holder/spell/targeted/mime/fingergun
+ name = "Finger Gun"
+ desc = "Shoot stunning, invisible bullets out of your fingers! 6 bullets available per cast. Use your fingers to holster them manually."
+ school = "mime"
+ panel = "Mime"
+ clothes_req = 0
+ charge_max = 600
+ range = -1
+ include_user = 1
+ human_req = 1
+
+ action_icon_state = "fingergun"
+ action_background_icon_state = "bg_mime"
+ var/gun = /obj/item/gun/projectile/revolver/fingergun
+
+/obj/effect/proc_holder/spell/targeted/mime/fingergun/cast(list/targets, mob/user = usr)
+ for(var/mob/living/carbon/human/C in targets)
+ if(!istype(C.get_active_hand(), gun) && !istype(C.get_inactive_hand(), gun))
+ to_chat(user, "You draw your fingers!")
+ C.drop_item()
+ C.put_in_hands(new gun)
+ else
+ to_chat(user, "Holster your fingers first.")
+ revert_cast(user)
+
+/obj/effect/proc_holder/spell/targeted/mime/fingergun/fake
+ desc = "Pretend you're shooting bullets out of your fingers! 6 bullets available per cast. Use your fingers to holster them manually."
+ gun = /obj/item/gun/projectile/revolver/fingergun/fake
+
+// Mime Spellbooks
+
+/obj/item/spellbook/oneuse/mime
+ spell = /obj/effect/proc_holder/spell/aoe_turf/conjure/mime_wall
+ spellname = "Invisible Wall"
+ name = "Miming Manual : "
+ desc = "It contains various pictures of mimes mid-performance, aswell as some illustrated tutorials."
+ icon_state = "bookmime"
+
+/obj/item/spellbook/oneuse/mime/attack_self(mob/user)
+ var/obj/effect/proc_holder/spell/S = new spell
+ for(var/obj/effect/proc_holder/spell/knownspell in user.mind.spell_list)
+ if(knownspell.type == S.type)
+ if(user.mind)
+ to_chat(user, "You've already read this one.")
+ return
+ if(used)
+ recoil(user)
+ else
+ user.mind.AddSpell(S)
+ to_chat(user, "You flip through the pages. Your understanding of the boundaries of reality increases. You can cast [spellname]!")
+ user.create_attack_log("[key_name(user)] learned the spell [spellname] ([S]).")
+ onlearned(user)
+
+/obj/item/spellbook/oneuse/mime/recoil(mob/user)
+ to_chat(user, "You flip through the pages. Nothing of interest to you.")
+
+/obj/item/spellbook/oneuse/mime/onlearned(mob/user)
+ used = 1
+ if(!locate(/obj/effect/proc_holder/spell/targeted/mime/speak) in user.mind.spell_list) //add vow of silence if not known by user
+ user.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/mime/speak)
+ to_chat(user, "You have learned how to use silence to improve your performance.")
+
+/obj/item/spellbook/oneuse/mime/fingergun
+ spell = /obj/effect/proc_holder/spell/targeted/mime/fingergun
+ spellname = "Finger Gun"
+ desc = "It contains illustrations of guns and how to mime them."
+
+/obj/item/spellbook/oneuse/mime/fingergun/fake
+ spell = /obj/effect/proc_holder/spell/targeted/mime/fingergun/fake
+
+/obj/item/spellbook/oneuse/mime/greaterwall
+ spell = /obj/effect/proc_holder/spell/targeted/forcewall/mime
+ spellname = "Invisible Greater Wall"
+ desc = "It contains illustrations of the great walls of human history."
\ No newline at end of file
diff --git a/code/datums/spells/shapeshift.dm b/code/datums/spells/shapeshift.dm
index a814c84fa59..5dd388779ce 100644
--- a/code/datums/spells/shapeshift.dm
+++ b/code/datums/spells/shapeshift.dm
@@ -79,4 +79,36 @@
shapeshift_type = /mob/living/simple_animal/hostile/megafauna/dragon/lesser
list/current_shapes = list(/mob/living/simple_animal/hostile/megafauna/dragon/lesser)
list/current_casters = list()
- list/possible_shapes = list(/mob/living/simple_animal/hostile/megafauna/dragon/lesser)
\ No newline at end of file
+ list/possible_shapes = list(/mob/living/simple_animal/hostile/megafauna/dragon/lesser)
+
+/obj/effect/proc_holder/spell/targeted/shapeshift/bats
+ name = "Bat Form"
+ desc = "Take on the shape of a swarm of bats."
+ invocation = "none"
+ invocation_type = "none"
+ action_icon_state = "vampire_bats"
+
+ shapeshift_type = /mob/living/simple_animal/hostile/scarybat/batswarm
+ list/current_shapes = list(/mob/living/simple_animal/hostile/scarybat/batswarm)
+ list/current_casters = list()
+ list/possible_shapes = list(/mob/living/simple_animal/hostile/scarybat/batswarm)
+
+/obj/effect/proc_holder/spell/targeted/shapeshift/hellhound
+ name = "Lesser Hellhound Form"
+ desc = "Take on the shape of a Hellhound."
+ invocation = "none"
+ invocation_type = "none"
+ action_background_icon_state = "bg_demon"
+ action_icon_state = "glare"
+
+ shapeshift_type = /mob/living/simple_animal/hostile/hellhound
+ list/current_shapes = list(/mob/living/simple_animal/hostile/hellhound)
+ list/current_casters = list()
+ list/possible_shapes = list(/mob/living/simple_animal/hostile/hellhound)
+
+/obj/effect/proc_holder/spell/targeted/shapeshift/hellhound/greater
+ name = "Greater Hellhound Form"
+ shapeshift_type = /mob/living/simple_animal/hostile/hellhound/greater
+ list/current_shapes = list(/mob/living/simple_animal/hostile/hellhound/greater)
+ list/current_casters = list()
+ list/possible_shapes = list(/mob/living/simple_animal/hostile/hellhound/greater)
\ No newline at end of file
diff --git a/code/datums/spells/wizard.dm b/code/datums/spells/wizard.dm
index 0816659e414..dffde794d51 100644
--- a/code/datums/spells/wizard.dm
+++ b/code/datums/spells/wizard.dm
@@ -166,6 +166,32 @@
action_icon_state = "shield"
cast_sound = 'sound/magic/ForceWall.ogg'
+/obj/effect/proc_holder/spell/targeted/forcewall
+ name = "Greater Forcewall"
+ desc = "Create a magical barrier that only you can pass through."
+
+ school = "transmutation"
+ charge_max = 100
+ clothes_req = FALSE
+ invocation = "TARCOL MINTI ZHERI"
+ invocation_type = "shout"
+ sound = 'sound/magic/ForceWall.ogg'
+ action_icon_state = "shield"
+ range = -1
+ include_user = TRUE
+ cooldown_min = 50 //12 deciseconds reduction per rank
+ var/wall_type = /obj/effect/forcefield/wizard
+
+/obj/effect/proc_holder/spell/targeted/forcewall/cast(list/targets,mob/user = usr)
+ new wall_type(get_turf(user),user)
+ if(user.dir == SOUTH || user.dir == NORTH)
+ new wall_type(get_step(user, EAST),user)
+ new wall_type(get_step(user, WEST),user)
+ else
+ new wall_type(get_step(user, NORTH),user)
+ new wall_type(get_step(user, SOUTH),user)
+
+
/obj/effect/proc_holder/spell/aoe_turf/conjure/timestop
name = "Stop Time"
desc = "This spell stops time for everyone except for you, allowing you to move freely while your enemies and even projectiles are frozen."
diff --git a/code/datums/supplypacks.dm b/code/datums/supplypacks.dm
index 4d8bd9432f4..89108071b09 100644
--- a/code/datums/supplypacks.dm
+++ b/code/datums/supplypacks.dm
@@ -737,6 +737,15 @@ var/list/all_supply_groups = list(supply_emergency,supply_security,supply_engine
cost = 10
containername = "advanced first aid kits crate"
+/datum/supply_packs/medical/firstaidmachine
+ name = "Machine First Aid Kits Crate"
+ contains = list(/obj/item/storage/firstaid/machine,
+ /obj/item/storage/firstaid/machine,
+ /obj/item/storage/firstaid/machine,
+ /obj/item/storage/firstaid/machine)
+ cost = 10
+ containername = "machine first aid kits crate"
+
/datum/supply_packs/medical/firstaibrute
name = "Brute Treatment Kits Crate"
contains = list(/obj/item/storage/firstaid/brute,
diff --git a/code/datums/uplink_item.dm b/code/datums/uplink_item.dm
index d2d56fa9f3b..8d871447cd3 100644
--- a/code/datums/uplink_item.dm
+++ b/code/datums/uplink_item.dm
@@ -2,6 +2,8 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
/proc/get_uplink_items(var/job = null)
var/list/uplink_items = list()
+ var/list/sales_items = list()
+ var/newreference = 1
if(!uplink_items.len)
var/list/last = list()
@@ -22,6 +24,8 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
uplink_items[I.category] = list()
uplink_items[I.category] += I
+ if(I.limited_stock < 0 && !I.cant_discount && I.item && I.cost > 1)
+ sales_items += I
for(var/datum/uplink_item/I in last)
if(!uplink_items[I.category])
@@ -29,6 +33,30 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
uplink_items[I.category] += I
+ for(var/i in 1 to 3)
+ var/datum/uplink_item/I = pick_n_take(sales_items)
+ var/datum/uplink_item/A = new I.type
+ var/discount = 0.5
+ A.limited_stock = 1
+ I.refundable = FALSE
+ A.refundable = FALSE
+ if(A.cost >= 20)
+ discount *= 0.5 // If the item costs 20TC or more, it's only 25% off.
+ A.cost = max(round(A.cost * (1-discount)),1)
+ A.category = "Discounted Gear"
+ A.name += " ([round(((initial(A.cost)-A.cost)/initial(A.cost))*100)]% off!)"
+ A.job = null // If you get a job specific item selected, actually lets you buy it in the discount section
+ A.reference = "DIS[newreference]"
+ A.desc += " Limit of [A.limited_stock] per uplink. Normally costs [initial(A.cost)] TC."
+ A.surplus = 0 // stops the surplus crate potentially giving out a bit too much
+ A.item = I.item
+ newreference++
+
+ if(!uplink_items[A.category])
+ uplink_items[A.category] = list()
+
+ uplink_items[A.category] += A
+
return uplink_items
/datum/nano_item_lists
@@ -51,22 +79,27 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
var/list/excludefrom = list() //Empty list does nothing. Place the name of gamemode you don't want this item to be available in here. This is so you dont have to list EVERY mode to exclude something.
var/list/job = null
var/surplus = 100 //Chance of being included in the surplus crate (when pick() selects it)
+ var/cant_discount = FALSE
+ var/limited_stock = -1 // Can you only buy so many? -1 allows for infinite purchases
var/hijack_only = FALSE //can this item be purchased only during hijackings?
var/refundable = FALSE
var/refund_path = null // Alternative path for refunds, in case the item purchased isn't what is actually refunded (ie: holoparasites).
var/refund_amount // specified refund amount in case there needs to be a TC penalty for refunds.
/datum/uplink_item/proc/spawn_item(var/turf/loc, var/obj/item/uplink/U)
+
if(hijack_only)
if(!(locate(/datum/objective/hijack) in usr.mind.objectives))
to_chat(usr, "The Syndicate lacks resources to provide you with this item.")
return
+
if(item)
U.uses -= max(cost, 0)
U.used_TC += cost
feedback_add_details("traitor_uplink_items_bought", name)
return new item(loc)
+
/datum/uplink_item/proc/description()
if(!desc)
// Fallback description
@@ -97,7 +130,10 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
if(I)
if(ishuman(user))
var/mob/living/carbon/human/A = user
- log_game("[key_name(user)] purchased [name]")
+ if(limited_stock > 0)
+ log_game("[key_name(user)] purchased [name]. [name] was discounted to [cost].")
+ else
+ log_game("[key_name(user)] purchased [name].")
A.put_in_any_hand_if_possible(I)
if(istype(I,/obj/item/storage/box/) && I.contents.len>0)
@@ -117,8 +153,16 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
*/
//Work in Progress, job specific antag tools
+//Discounts (dynamically filled above)
+
+/datum/uplink_item/discounts
+ category = "Discounted Gear"
+
+//Job specific gear
+
/datum/uplink_item/jobspecific
category = "Job Specific Tools"
+ cant_discount = TRUE
//Clown
/datum/uplink_item/jobspecific/clowngrenade
@@ -146,6 +190,14 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
cost = 10
job = list("Mime")
+/datum/uplink_item/jobspecific/mimery
+ name = "Guide to Advanced Mimery Series"
+ desc = "Contains two manuals to teach you advanced Mime skills. You will be able to shoot stunning bullets out of your fingers, and create large walls that can block an entire hallway!"
+ reference = "AM"
+ item = /obj/item/storage/box/syndie_kit/mimery
+ cost = 10
+ job = list("Mime")
+
//Chef
/datum/uplink_item/jobspecific/specialsauce
name = "Chef Excellence's Special Sauce"
@@ -556,6 +608,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
gamemodes = list(/datum/game_mode/nuclear)
surplus = 0
refundable = TRUE
+ cant_discount = TRUE
/datum/uplink_item/dangerous/foamsmg
name = "Toy Submachine Gun"
@@ -586,12 +639,14 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
/datum/uplink_item/dangerous/guardian
name = "Holoparasites"
- desc = "Though capable of near sorcerous feats via use of hardlight holograms and nanomachines, they require an organic host as a home base and source of fuel."
+ desc = "Though capable of near sorcerous feats via use of hardlight holograms and nanomachines, they require an organic host as a home base and source of fuel. \
+ The holoparasites are unable to incoporate themselves to changeling and vampire agents."
item = /obj/item/storage/box/syndie_kit/guardian
excludefrom = list(/datum/game_mode/nuclear)
cost = 12
refund_path = /obj/item/guardiancreator/tech/choose
refundable = TRUE
+ cant_discount = TRUE
// Ammunition
@@ -755,11 +810,21 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
/datum/uplink_item/stealthy_weapons/martialarts
name = "Martial Arts Scroll"
desc = "This scroll contains the secrets of an ancient martial arts technique. You will master unarmed combat, \
- deflecting all ranged weapon fire, but you also refuse to use dishonorable ranged weaponry."
+ deflecting all ranged weapon fire, but you also refuse to use dishonorable ranged weaponry. \
+ Unable to be understood by vampire and changeling agents."
reference = "SCS"
item = /obj/item/sleeping_carp_scroll
cost = 17
excludefrom = list(/datum/game_mode/nuclear)
+ refundable = TRUE
+ cant_discount = TRUE
+
+/datum/uplink_item/stealthy_weapons/cqc
+ name = "CQC Manual"
+ desc = "A manual that teaches a single user tactical Close-Quarters Combat before self-destructing. Does not restrict weapon usage, but cannot be used alongside Gloves of the North Star."
+ reference = "CQC"
+ item = /obj/item/CQC_manual
+ cost = 9
/datum/uplink_item/stealthy_weapons/throwingweapons
name = "Box of Throwing Weapons"
@@ -1183,8 +1248,17 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
sends you a small beacon that will teleport the larger beacon to your location upon activation."
reference = "SNGB"
item = /obj/item/radio/beacon/syndicate
- cost = 12
+ cost = 10
surplus = 0
+ hijack_only = TRUE //This is an item only useful for a hijack traitor, as such, it should only be available in those scenarios.
+ cant_discount = TRUE
+ excludefrom = list(/datum/game_mode/nuclear)
+
+/datum/uplink_item/device_tools/singularity_beacon/nuke
+ reference = "SNGBN"
+ hijack_only = FALSE // This inherited version exists so nukies can use it while keeping the original hijack only
+ excludefrom = list()
+ gamemodes = list(/datum/game_mode/nuclear)
/datum/uplink_item/device_tools/syndicate_bomb
name = "Syndicate Bomb"
@@ -1224,6 +1298,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
item = /obj/item/stack/telecrystal
cost = 1
surplus = 0
+ cant_discount = TRUE
/datum/uplink_item/device_tools/telecrystal/five
name = "5 Raw Telecrystals"
@@ -1301,6 +1376,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
item = /obj/item/implanter/uplink
cost = 14
surplus = 0
+ cant_discount = TRUE
/datum/uplink_item/implants/storage
name = "Storage Implant"
@@ -1405,6 +1481,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
item = /obj/item/storage/box/syndicate
cost = 20
excludefrom = list(/datum/game_mode/nuclear)
+ cant_discount = TRUE
/datum/uplink_item/badass/syndiecards
name = "Syndicate Playing Cards"
@@ -1438,6 +1515,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
reference = "BABA"
item = /obj/item/toy/syndicateballoon
cost = 20
+ cant_discount = TRUE
/datum/uplink_item/implants/macrobomb
name = "Macrobomb Implant"
@@ -1480,6 +1558,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
cost = 20
item = /obj/item/storage/box/syndicate
excludefrom = list(/datum/game_mode/nuclear)
+ cant_discount = TRUE // You fucking wish
/datum/uplink_item/badass/surplus_crate/spawn_item(turf/loc, obj/item/uplink/U)
var/obj/structure/closet/crate/C = new(loc)
diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm
index 49bc85959f5..7c66666af04 100644
--- a/code/game/area/Space Station 13 areas.dm
+++ b/code/game/area/Space Station 13 areas.dm
@@ -1505,7 +1505,7 @@ var/list/ghostteleportlocs = list()
/area/medical/biostorage
- name = "\improper Secondary Storage"
+ name = "\improper Medical Storage"
icon_state = "medbaysecstorage"
music = 'sound/ambience/signal.ogg'
@@ -1553,8 +1553,8 @@ var/list/ghostteleportlocs = list()
icon_state = "CMO"
/area/medical/cmostore
- name = "\improper Secure Storage"
- icon_state = "CMO"
+ name = "\improper Medical Secondary Storage"
+ icon_state = "medbaysecstorage"
/area/medical/robotics
name = "\improper Robotics"
diff --git a/code/game/asteroid.dm b/code/game/asteroid.dm
index 9abe6c2bc92..c6edd09575e 100644
--- a/code/game/asteroid.dm
+++ b/code/game/asteroid.dm
@@ -63,7 +63,7 @@ var/global/max_secret_rooms = 6
treasureitems = list(/mob/living/simple_animal/bot/medbot/mysterious=1, /obj/item/circular_saw=1, /obj/structure/closet/critter/cat=2)
fluffitems = list(/obj/effect/decal/cleanable/blood=5,/obj/item/organ/internal/appendix=2,/obj/structure/closet/crate/freezer=2,
/obj/machinery/optable=1,/obj/item/scalpel=1,/obj/item/storage/firstaid/regular=3,
- /obj/item/tank/anesthetic=1, /obj/item/surgical_drapes=2, /obj/item/mass_spectrometer/adv=1,/obj/item/clothing/glasses/hud/health=1)
+ /obj/item/tank/anesthetic=1, /obj/item/surgical_drapes=2, /obj/item/reagent_scanner/adv=1,/obj/item/clothing/glasses/hud/health=1)
if("cult")
theme = "cult"
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 5ca34ceb57e..30b901875b3 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -15,6 +15,7 @@
var/atom_say_verb = "says"
var/dont_save = 0 // For atoms that are temporary by necessity - like lighting overlays
+
///Chemistry.
var/container_type = NONE
var/datum/reagents/reagents = null
@@ -150,9 +151,11 @@
//Hook for running code when a dir change occurs
/atom/proc/setDir(newdir)
+ SEND_SIGNAL(src, COMSIG_ATOM_DIR_CHANGE, dir, newdir)
dir = newdir
/atom/proc/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE)
+ SEND_SIGNAL(src, COMSIG_ATOM_HULK_ATTACK, user)
if(does_attack_animation)
user.changeNext_move(CLICK_CD_MELEE)
add_attack_logs(user, src, "Punched with hulk powers")
@@ -305,8 +308,8 @@
/atom/proc/ex_act()
return
-/atom/proc/blob_act()
- return
+/atom/proc/blob_act(obj/structure/blob/B)
+ SEND_SIGNAL(src, COMSIG_ATOM_BLOB_ACT, B)
/atom/proc/fire_act()
return
@@ -696,6 +699,9 @@ var/list/blood_splatter_icons = list()
/atom/proc/ratvar_act()
return
+/atom/proc/handle_ricochet(obj/item/projectile/P)
+ return
+
//This proc is called on the location of an atom when the atom is Destroy()'d
/atom/proc/handle_atom_del(atom/A)
return
@@ -734,3 +740,14 @@ var/list/blood_splatter_icons = list()
if(!L)
return null
return L.AllowDrop() ? L : get_turf(L)
+
+/atom/Entered(atom/movable/AM, atom/oldLoc)
+ SEND_SIGNAL(src, COMSIG_ATOM_ENTERED, AM, oldLoc)
+
+/atom/Exit(atom/movable/AM, atom/newLoc)
+ . = ..()
+ if(SEND_SIGNAL(src, COMSIG_ATOM_EXIT, AM, newLoc) & COMPONENT_ATOM_BLOCK_EXIT)
+ return FALSE
+
+/atom/Exited(atom/movable/AM, atom/newLoc)
+ SEND_SIGNAL(src, COMSIG_ATOM_EXITED, AM, newLoc)
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 36d3ce81ea2..a5d2366c46c 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -126,10 +126,11 @@
// Previously known as HasEntered()
// This is automatically called when something enters your square
/atom/movable/Crossed(atom/movable/AM)
- return
+ SEND_SIGNAL(src, COMSIG_MOVABLE_CROSSED, AM)
/atom/movable/Bump(atom/A, yes) //the "yes" arg is to differentiate our Bump proc from byond's, without it every Bump() call would become a double Bump().
if(A && yes)
+ SEND_SIGNAL(src, COMSIG_MOVABLE_BUMP, A)
if(throwing)
throwing.hit_atom(A)
. = 1
@@ -213,6 +214,7 @@
//called when src is thrown into hit_atom
/atom/movable/proc/throw_impact(atom/hit_atom, throwingdatum)
set waitfor = 0
+ SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, hit_atom, throwingdatum)
if(!QDELETED(hit_atom))
return hit_atom.hitby(src)
diff --git a/code/game/gamemodes/blob/blobs/shield.dm b/code/game/gamemodes/blob/blobs/shield.dm
index 5ae1a040ab6..1fb293be2cf 100644
--- a/code/game/gamemodes/blob/blobs/shield.dm
+++ b/code/game/gamemodes/blob/blobs/shield.dm
@@ -3,9 +3,9 @@
icon = 'icons/mob/blob.dmi'
icon_state = "blob_idle"
desc = "Some blob creature thingy"
- health = 75
+ health = 75
fire_resist = 2
-
+ var/maxHealth = 75
/obj/structure/blob/shield/update_icon()
if(health <= 0)
@@ -19,3 +19,34 @@
/obj/structure/blob/shield/CanPass(atom/movable/mover, turf/target, height=0)
if(istype(mover) && mover.checkpass(PASSBLOB)) return 1
return 0
+
+/obj/structure/blob/shield/reflective
+ name = "reflective blob"
+ desc = "A solid wall of slightly twitching tendrils with a reflective glow."
+ icon_state = "blob_idle_glow"
+ brute_resist = 0
+ health = 50
+ maxHealth = 50
+ flags_2 = CHECK_RICOCHET_1
+ var/reflect_chance = 80 //80% chance to reflect
+
+/obj/structure/blob/shield/reflective/handle_ricochet(obj/item/projectile/P)
+ if(P.is_reflectable && prob(reflect_chance))
+ var/P_turf = get_turf(P)
+ var/face_direction = get_dir(src, P_turf)
+ var/face_angle = dir2angle(face_direction)
+ var/incidence_s = GET_ANGLE_OF_INCIDENCE(face_angle, (P.Angle + 180))
+ if(abs(incidence_s) > 90 && abs(incidence_s) < 270)
+ return FALSE
+ var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s)
+ P.setAngle(new_angle_s)
+ P.firer = src //so people who fired the lasers are not immune to them when it reflects
+ visible_message("[P] reflects off [src]!")
+ return -1// complete projectile permutation
+ else
+ playsound(src, P.hitsound, 50, 1)
+ visible_message("[src] is hit by \a [P]!")
+ take_damage(P.damage, P.damage_type)
+
+/obj/structure/blob/shield/reflective/bullet_act()
+ return
diff --git a/code/game/gamemodes/blob/overmind.dm b/code/game/gamemodes/blob/overmind.dm
index d65fcb24ee6..c0f2e4a4a9e 100644
--- a/code/game/gamemodes/blob/overmind.dm
+++ b/code/game/gamemodes/blob/overmind.dm
@@ -56,6 +56,7 @@
to_chat(src, "You are the overmind and can control the blob! You can expand, which will attack people, and place new blob pieces such as...")
to_chat(src, "Normal Blob will expand your reach and allow you to upgrade into special blobs that perform certain functions.")
to_chat(src, "Shield Blob is a strong and expensive blob which can take more damage. It is fireproof and can block air, use this to protect yourself from station fires.")
+ to_chat(src, "Reflective Blobis an upgraded Shield Blob which has a high chance of deflecting energy projectiles, but is vulnerable to ballistics and brute damage.")
to_chat(src, "Resource Blob is a blob which will collect more resources for you, try to build these earlier to get a strong income. It will benefit from being near your core or multiple nodes, by having an increased resource rate; put it alone and it won't create resources at all.")
to_chat(src, "Node Blob is a blob which will grow, like the core. Unlike the core it won't give you a small income but it can power resource and factory blobs to increase their rate.")
to_chat(src, "Factory Blob is a blob which will spawn blob spores which will attack nearby food. Putting this nearby nodes and your core will increase the spawn rate; put it alone and it will not spawn any spores.")
diff --git a/code/game/gamemodes/blob/powers.dm b/code/game/gamemodes/blob/powers.dm
index 35f3c935013..4491b80d2e4 100644
--- a/code/game/gamemodes/blob/powers.dm
+++ b/code/game/gamemodes/blob/powers.dm
@@ -33,34 +33,51 @@
/mob/camera/blob/verb/create_shield_power()
set category = "Blob"
- set name = "Create Shield Blob (10)"
- set desc = "Create a shield blob."
+ set name = "Create/Upgrade Shield Blob (15)"
+ set desc = "Create/Upgrade a shield blob. Using this on an existing shield blob turns it into a reflective blob, capable of reflecting most energy projectiles but making it much weaker than usual to brute attacks."
var/turf/T = get_turf(src)
create_shield(T)
/mob/camera/blob/proc/create_shield(var/turf/T)
- var/obj/structure/blob/B = (locate(/obj/structure/blob) in T)
+ var/obj/structure/blob/B = locate(/obj/structure/blob) in T
+ var/obj/structure/blob/shield/S = locate(/obj/structure/blob/shield) in T
+
+ if(!S)
+ if(!B)//We are on a blob
+ to_chat(src, "There is no blob here!")
+ return
- if(!B)//We are on a blob
- to_chat(src, "There is no blob here!")
- return
+ else if(!istype(B, /obj/structure/blob/normal))
+ to_chat(src, "Unable to use this blob, find a normal one.")
+ return
- if(!istype(B, /obj/structure/blob/normal))
- to_chat(src, "Unable to use this blob, find a normal one.")
- return
+ else if(!can_buy(15))
+ return
- if(!can_buy(10))
- return
+ B.color = blob_reagent_datum.color
+ B.change_to(/obj/structure/blob/shield)
+ else
+
+ if(istype(S, /obj/structure/blob/shield/reflective))
+ to_chat(src, "There's already a reflector blob here!")
+ return
- B.color = blob_reagent_datum.color
- B.change_to(/obj/structure/blob/shield)
+ else if(S.health < S.maxHealth * 0.5)
+ to_chat(src, "This shield blob is too damaged to be modified properly!")
+ return
+
+ else if (!can_buy(15))
+ return
+
+ to_chat(src, "You secrete a reflective ooze over the shield blob, allowing it to reflect energy projectiles at the cost of reduced intregrity.")
+
+ S.change_to(/obj/structure/blob/shield/reflective)
+ S.color = blob_reagent_datum.color
return
-
-
/mob/camera/blob/verb/create_resource()
set category = "Blob"
set name = "Create Resource Blob (40)"
diff --git a/code/game/gamemodes/blob/theblob.dm b/code/game/gamemodes/blob/theblob.dm
index 6d0084b9000..493c0da90c9 100644
--- a/code/game/gamemodes/blob/theblob.dm
+++ b/code/game/gamemodes/blob/theblob.dm
@@ -19,7 +19,7 @@
src.update_icon()
..(loc)
for(var/atom/A in loc)
- A.blob_act()
+ A.blob_act(src)
return
@@ -126,7 +126,7 @@
qdel(B)
for(var/atom/A in T)//Hit everything in the turf
- A.blob_act()
+ A.blob_act(src)
return 1
/obj/structure/blob/ex_act(severity)
@@ -141,7 +141,7 @@
/obj/structure/blob/Crossed(var/mob/living/L)
..()
- L.blob_act()
+ L.blob_act(src)
/obj/structure/blob/tesla_act(power)
..()
diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm
index 477a0de2573..6f3f62fc3ba 100644
--- a/code/game/gamemodes/changeling/changeling.dm
+++ b/code/game/gamemodes/changeling/changeling.dm
@@ -131,6 +131,7 @@ var/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon"
return
/datum/game_mode/proc/greet_changeling(datum/mind/changeling, you_are=1)
+ SEND_SOUND(changeling.current, 'sound/ambience/antag/ling_aler.ogg')
if(you_are)
to_chat(changeling.current, "You are a changeling!")
to_chat(changeling.current, "Use say \":g message\" to communicate with your fellow changelings. Remember: you get all of their absorbed DNA if you absorb them.")
diff --git a/code/game/gamemodes/cult/cult.dm b/code/game/gamemodes/cult/cult.dm
index 441292366f0..da7cb876fc5 100644
--- a/code/game/gamemodes/cult/cult.dm
+++ b/code/game/gamemodes/cult/cult.dm
@@ -114,6 +114,7 @@ var/global/list/all_cults = list()
summon_spots += summon
for(var/datum/mind/cult_mind in cult)
+ SEND_SOUND(cult_mind.current, 'sound/ambience/antag/bloodcult.ogg')
equip_cultist(cult_mind.current)
cult_mind.current.faction |= "cult"
var/datum/action/innate/cultcomm/C = new()
diff --git a/code/game/gamemodes/miniantags/borer/borer.dm b/code/game/gamemodes/miniantags/borer/borer.dm
index 6cdfdc736d9..5ba8d1360b0 100644
--- a/code/game/gamemodes/miniantags/borer/borer.dm
+++ b/code/game/gamemodes/miniantags/borer/borer.dm
@@ -3,7 +3,6 @@
real_name = "host brain"
/mob/living/captive_brain/say(message)
-
if(client)
if(client.prefs.muted & MUTE_IC)
to_chat(src, "You cannot speak in IC (muted).")
@@ -152,13 +151,13 @@
stat("Chemicals", chemicals)
/mob/living/simple_animal/borer/say(var/message)
- var/datum/language/dialect = parse_language(message)
- if(!dialect)
- dialect = get_default_language()
- if(!istype(dialect, /datum/language/corticalborer) && loc == host && !talk_inside_host)
- to_chat(src, "You've disabled audible speech while inside a host! Re-enable it under the borer tab, or stick to borer communications.")
- return
- ..()
+ var/list/message_pieces = parse_languages(message)
+ for(var/datum/multilingual_say_piece/S in message_pieces)
+ if(!istype(S.speaking, /datum/language/corticalborer) && loc == host && !talk_inside_host)
+ to_chat(src, "You've disabled audible speech while inside a host! Re-enable it under the borer tab, or stick to borer communications.")
+ return
+
+ . = ..()
/mob/living/simple_animal/borer/verb/Communicate()
set category = "Borer"
diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm
index 9c72ffc9bef..cc96f206aba 100644
--- a/code/game/gamemodes/nuclear/nuclear.dm
+++ b/code/game/gamemodes/nuclear/nuclear.dm
@@ -216,6 +216,7 @@ proc/issyndicate(mob/living/M as mob)
/datum/game_mode/proc/greet_syndicate(var/datum/mind/syndicate, var/you_are=1)
+ SEND_SOUND(syndicate.current, 'sound/ambience/antag/ops.ogg')
if(you_are)
to_chat(syndicate.current, "You are a [syndicate_name()] agent!")
var/obj_count = 1
diff --git a/code/game/gamemodes/nuclear/pinpointer.dm b/code/game/gamemodes/nuclear/pinpointer.dm
index ec667bbad86..61986b34f94 100644
--- a/code/game/gamemodes/nuclear/pinpointer.dm
+++ b/code/game/gamemodes/nuclear/pinpointer.dm
@@ -391,3 +391,12 @@
/obj/item/pinpointer/crew/examine(mob/user)
..(user)
+
+/obj/item/pinpointer/crew/centcom
+ name = "centcom pinpointer"
+ desc = "A handheld tracking device that tracks crew based on remote centcom sensors."
+
+/obj/item/pinpointer/crew/centcom/trackable(mob/living/carbon/human/H)
+ var/turf/here = get_turf(src)
+ var/turf/there = get_turf(H)
+ return there && there.z == here.z
\ No newline at end of file
diff --git a/code/game/gamemodes/shadowling/shadowling.dm b/code/game/gamemodes/shadowling/shadowling.dm
index 5b00868dc3d..317f9e55b80 100644
--- a/code/game/gamemodes/shadowling/shadowling.dm
+++ b/code/game/gamemodes/shadowling/shadowling.dm
@@ -51,7 +51,8 @@ Made by Xhuis
var/shadowling_ascended = 0 //If at least one shadowling has ascended
var/shadowling_dead = 0 //is shadowling kill
var/objective_explanation
-
+ var/warning_threshold
+ var/victory_warning_announced = FALSE
/proc/is_thrall(var/mob/living/M)
return istype(M) && M.mind && ticker && ticker.mode && (M.mind in ticker.mode.shadowling_thralls)
@@ -101,6 +102,8 @@ Made by Xhuis
var/thrall_scaling = round(num_players() / 3)
required_thralls = Clamp(thrall_scaling, 15, 25)
+ warning_threshold = round(0.66 * required_thralls)
+
..()
return 1
@@ -164,7 +167,9 @@ Made by Xhuis
to_chat(new_thrall_mind.current, "You may communicate with your allies by speaking in the Shadowling Hivemind (:8).")
if(jobban_isbanned(new_thrall_mind.current, ROLE_SHADOWLING) || jobban_isbanned(new_thrall_mind.current, ROLE_SYNDICATE))
replace_jobbanned_player(new_thrall_mind.current, ROLE_SHADOWLING)
-
+ if(!victory_warning_announced && (length(shadowling_thralls) >= warning_threshold))//are the slings very close to winning?
+ victory_warning_announced = TRUE //then let's give the station a warning
+ command_announcement.Announce("Large concentration of psychic bluespace energy detected by long-ranged scanners. Shadowling ascension event imminent. Prevent it at all costs!", "Central Command Higher Dimensional Affairs", 'sound/AI/spanomalies.ogg')
return 1
/datum/game_mode/proc/remove_thrall(datum/mind/thrall_mind, var/kill = 0)
diff --git a/code/game/gamemodes/traitor/traitor.dm b/code/game/gamemodes/traitor/traitor.dm
index d79dbc3c224..c4f39867115 100644
--- a/code/game/gamemodes/traitor/traitor.dm
+++ b/code/game/gamemodes/traitor/traitor.dm
@@ -178,6 +178,10 @@
/datum/game_mode/proc/greet_traitor(var/datum/mind/traitor)
+ if(istype(traitor.current, /mob/living/silicon))
+ SEND_SOUND(traitor.current, 'sound/ambience/antag/malf.ogg')
+ else
+ SEND_SOUND(traitor.current, 'sound/ambience/antag/tatoralert.ogg')
to_chat(traitor.current, "You are the traitor.")
var/obj_count = 1
for(var/datum/objective/objective in traitor.objectives)
diff --git a/code/game/gamemodes/vampire/vampire_powers.dm b/code/game/gamemodes/vampire/vampire_powers.dm
index c45da8ac279..28dc6b29a45 100644
--- a/code/game/gamemodes/vampire/vampire_powers.dm
+++ b/code/game/gamemodes/vampire/vampire_powers.dm
@@ -76,8 +76,8 @@
//Vampires who have reached their full potential can affect nearly everything
if(user.mind.vampire.get_ability(/datum/vampire_passive/full))
return 1
- //Chaplains are resistant to vampire powers
- if(target.mind && target.mind.assigned_role == "Chaplain")
+ //Holy characters are resistant to vampire powers
+ if(target.mind && target.mind.isholy)
return 0
return 1
@@ -535,3 +535,73 @@
/datum/vampire_passive/full
gain_desc = "You have reached your full potential and are no longer weak to the effects of anything holy and your vision has been improved greatly."
+
+
+/obj/effect/proc_holder/spell/targeted/raise_vampires
+ name = "Raise Vampires"
+ desc = "Summons deadly vampires from bluespace."
+ school = "transmutation"
+ charge_max = 100
+ clothes_req = 0
+ human_req = 1
+ invocation = "none"
+ invocation_type = "none"
+ max_targets = 0
+ range = 3
+ cooldown_min = 20
+ action_icon_state = "revive_thrall"
+ sound = 'sound/magic/WandODeath.ogg'
+
+/obj/effect/proc_holder/spell/targeted/raise_vampires/cast(list/targets, mob/user = usr)
+ new /obj/effect/temp_visual/cult/sparks(user.loc)
+ var/turf/T = get_turf(user)
+ to_chat(user, "You call out within bluespace, summoning more vampiric spirits to aid you!")
+ for(var/mob/living/carbon/human/H in targets)
+ T.Beam(H, "sendbeam", 'icons/effects/effects.dmi', time=30, maxdistance=7, beam_type=/obj/effect/ebeam)
+ new /obj/effect/temp_visual/cult/sparks(H.loc)
+ H.raise_vampire(user)
+
+
+/mob/living/carbon/human/proc/raise_vampire(var/mob/M)
+ if(!istype(M))
+ log_debug("human/proc/raise_vampire called with invalid argument.")
+ return
+ if(!mind)
+ visible_message("[src] looks to be too stupid to understand what is going on.")
+ return
+ if(dna && (NO_BLOOD in dna.species.species_traits) || dna.species.exotic_blood || !blood_volume)
+ visible_message("[src] looks unfazed!")
+ return
+ if(mind.vampire || mind.special_role == SPECIAL_ROLE_VAMPIRE || mind.special_role == SPECIAL_ROLE_VAMPIRE_THRALL)
+ visible_message("[src] looks refreshed!")
+ adjustBruteLoss(-60)
+ adjustFireLoss(-60)
+ for(var/obj/item/organ/external/E in bodyparts)
+ if(prob(25))
+ if(E.mend_fracture())
+ E.perma_injury = 0
+ return
+ if(stat != DEAD)
+ if(weakened)
+ visible_message("[src] looks to be in pain!")
+ adjustBrainLoss(60)
+ else
+ visible_message("[src] looks to be stunned by the energy!")
+ Weaken(20)
+ return
+ for(var/obj/item/implant/mindshield/L in src)
+ if(L && L.implanted)
+ qdel(L)
+ for(var/obj/item/implant/traitor/T in src)
+ if(T && T.implanted)
+ qdel(T)
+ visible_message("[src] gets an eerie red glow in their eyes!")
+ var/datum/objective/protect/protect_objective = new
+ protect_objective.owner = mind
+ protect_objective.target = M.mind
+ protect_objective.explanation_text = "Protect [M.real_name]."
+ mind.objectives += protect_objective
+ add_attack_logs(M, src, "Vampire-sired")
+ mind.make_Vampire()
+ revive()
+ Weaken(20)
\ No newline at end of file
diff --git a/code/game/gamemodes/wizard/wizard.dm b/code/game/gamemodes/wizard/wizard.dm
index d6657ef98c1..5951814660b 100644
--- a/code/game/gamemodes/wizard/wizard.dm
+++ b/code/game/gamemodes/wizard/wizard.dm
@@ -105,6 +105,7 @@
/datum/game_mode/proc/greet_wizard(var/datum/mind/wizard, var/you_are=1)
+ addtimer(CALLBACK(wizard.current, /mob/.proc/playsound_local, null, 'sound/ambience/antag/ragesmages.ogg', 100, 0), 30)
if(you_are)
to_chat(wizard.current, "You are the Space Wizard!")
to_chat(wizard.current, "The Space Wizards Federation has given you the following tasks:")
diff --git a/code/game/jobs/access.dm b/code/game/jobs/access.dm
index 4bf351019ea..22a27a3b94c 100644
--- a/code/game/jobs/access.dm
+++ b/code/game/jobs/access.dm
@@ -84,7 +84,7 @@
/var/const/access_cent_storage = 105//Storage areas.
/var/const/access_cent_shuttles = 106//Shuttle docks.
/var/const/access_cent_telecomms = 107//Telecomms.
-/var/const/access_cent_teleporter = 108//Telecomms.
+/var/const/access_cent_teleporter = 108//Teleporter
/var/const/access_cent_specops = 109//Special Ops.
/var/const/access_cent_specops_commander = 110//Special Ops Commander.
/var/const/access_cent_blackops = 111//Black Ops.
@@ -207,6 +207,8 @@ var/const/access_trade_sol = 160
return list(access_cent_general, access_cent_living, access_cent_medical, access_cent_security, access_cent_storage, access_cent_specops, access_cent_specops_commander, access_cent_blackops) + get_all_accesses()
if("Deathsquad Officer")
return get_all_centcom_access() + get_all_accesses()
+ if("NT Undercover Operative")
+ return get_all_centcom_access() + get_all_accesses()
if("Special Operations Officer")
return get_all_centcom_access() + get_all_accesses()
if("Nanotrasen Navy Representative")
@@ -224,6 +226,8 @@ var/const/access_trade_sol = 160
return list(access_syndicate)
if("Syndicate Operative Leader")
return list(access_syndicate, access_syndicate_leader)
+ if("Syndicate Agent")
+ return list(access_syndicate, access_maint_tunnels)
if("Vox Raider")
return list(access_vox)
if("Vox Trader")
diff --git a/code/game/jobs/job/central.dm b/code/game/jobs/job/central.dm
index 6ee7c9af634..6f6ea6ca67a 100644
--- a/code/game/jobs/job/central.dm
+++ b/code/game/jobs/job/central.dm
@@ -35,7 +35,9 @@
)
backpack = /obj/item/storage/backpack/satchel
box = /obj/item/storage/box/centcomofficer
-
+ cybernetic_implants = list(
+ /obj/item/organ/internal/cyberimp/chest/nutriment/plus
+ )
// CC Officials who lead ERTs, Death Squads, etc.
/datum/job/ntspecops
@@ -61,7 +63,7 @@
uniform = /obj/item/clothing/under/rank/centcom_commander
suit = /obj/item/clothing/suit/space/deathsquad/officer
back = /obj/item/storage/backpack/security
- belt = /obj/item/gun/energy/pulse/pistol/m1911
+ belt = /obj/item/storage/belt/military/assault
gloves = /obj/item/clothing/gloves/combat
shoes = /obj/item/clothing/shoes/combat
mask = /obj/item/clothing/mask/cigarette/cigar/cohiba
@@ -75,7 +77,6 @@
backpack = /obj/item/storage/backpack/satchel
backpack_contents = list(
/obj/item/clothing/shoes/magboots/advance = 1,
- /obj/item/twohanded/dualsaber/red = 1,
/obj/item/storage/box/zipties = 1
)
implants = list(
@@ -84,7 +85,7 @@
)
cybernetic_implants = list(
/obj/item/organ/internal/cyberimp/eyes/xray,
- /obj/item/organ/internal/cyberimp/brain/anti_drop,
/obj/item/organ/internal/cyberimp/brain/anti_stun,
- /obj/item/organ/internal/cyberimp/chest/nutriment/plus
+ /obj/item/organ/internal/cyberimp/chest/nutriment/plus,
+ /obj/item/organ/internal/cyberimp/arm/combat/centcom
)
\ No newline at end of file
diff --git a/code/game/jobs/job/medical.dm b/code/game/jobs/job/medical.dm
index d7bd9c1c4cb..cfb2de566ce 100644
--- a/code/game/jobs/job/medical.dm
+++ b/code/game/jobs/job/medical.dm
@@ -112,7 +112,7 @@
backpack_contents = list(
/obj/item/clothing/head/surgery/black = 1,
/obj/item/autopsy_scanner = 1,
- /obj/item/mass_spectrometer = 1,
+ /obj/item/reagent_scanner = 1,
/obj/item/storage/box/bodybags = 1)
/datum/outfit/job/doctor/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
diff --git a/code/game/jobs/job/support_chaplain.dm b/code/game/jobs/job/support_chaplain.dm
index 6b13b78c2bb..18edfc1e6c2 100644
--- a/code/game/jobs/job/support_chaplain.dm
+++ b/code/game/jobs/job/support_chaplain.dm
@@ -25,6 +25,7 @@
backpack_contents = list(
/obj/item/camera/spooky = 1
)
+ r_hand = /obj/item/nullrod
/datum/outfit/job/chaplain/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE)
. = ..()
@@ -32,13 +33,16 @@
if(visualsOnly)
return
- var/obj/item/storage/bible/B = new /obj/item/storage/bible(H)
+ if(H.mind)
+ H.mind.isholy = TRUE
spawn()
+
+ var/obj/item/storage/bible/B = new /obj/item/storage/bible(H)
H.equip_to_slot_or_del(B, slot_l_hand)
var/religion_name = "Christianity"
- var/new_religion = sanitize(copytext(input(H, "You are the crew services officer. Would you like to change your religion? Default is Christianity, in SPACE.", "Name change", religion_name),1,MAX_NAME_LEN))
+ var/new_religion = sanitize(copytext(input(H, "You are the Chaplain. What name do you give your beliefs? Default is Christianity.", "Name change", religion_name),1,MAX_NAME_LEN))
if(!new_religion)
new_religion = religion_name
@@ -72,12 +76,14 @@
feedback_set_details("religion_name","[new_religion]")
var/deity_name = "Space Jesus"
- var/new_deity = sanitize(copytext(input(H, "Would you like to change your deity? Default is Space Jesus.", "Name change", deity_name),1,MAX_NAME_LEN))
+ var/new_deity = sanitize(copytext(input(H, "Who or what do you worship? Default is Space Jesus.", "Name change", deity_name),1,MAX_NAME_LEN))
if((length(new_deity) == 0) || (new_deity == "Space Jesus") )
new_deity = deity_name
B.deity_name = new_deity
+ H.AddSpell(new /obj/effect/proc_holder/spell/targeted/chaplain_bless(null))
+
var/accepted = 0
var/outoftime = 0
spawn(200) // 20 seconds to choose
@@ -85,8 +91,9 @@
var/new_book_style = "Bible"
while(!accepted)
- if(!B) break // prevents possible runtime errors
- new_book_style = input(H,"Which bible style would you like?") in list("Bible", "Koran", "Scrapbook", "Creeper", "White Bible", "Holy Light", "Athiest", "Tome", "The King in Yellow", "Ithaqua", "Scientology", "the bible melts", "Necronomicon", "Greentext")
+ if(!B)
+ break // prevents possible runtime errors
+ new_book_style = input(H,"Which bible style would you like?") in list("Bible", "Koran", "Scrapbook", "Creeper", "White Bible", "Holy Light", "PlainRed", "Tome", "The King in Yellow", "Ithaqua", "Scientology", "the bible melts", "Necronomicon", "Greentext")
switch(new_book_style)
if("Koran")
B.icon_state = "koran"
@@ -107,13 +114,9 @@
if("Holy Light")
B.icon_state = "holylight"
B.item_state = "syringe_kit"
- if("Athiest")
+ if("PlainRed")
B.icon_state = "athiest"
B.item_state = "syringe_kit"
- for(var/area/chapel/main/A in world)
- for(var/turf/T in A.contents)
- if(T.icon_state == "carpetsymbol")
- T.dir = 10
if("Tome")
B.icon_state = "tome"
B.item_state = "syringe_kit"
@@ -165,3 +168,5 @@
ticker.Bible_deity_name = B.deity_name
feedback_set_details("religion_deity","[new_deity]")
feedback_set_details("religion_book","[new_book_style]")
+
+
diff --git a/code/game/machinery/atmo_control.dm b/code/game/machinery/atmo_control.dm
index 2e41ec49c0a..61547f270bf 100644
--- a/code/game/machinery/atmo_control.dm
+++ b/code/game/machinery/atmo_control.dm
@@ -57,14 +57,14 @@
output &= ~bitflag_value
else//can't not be off
output |= bitflag_value
- return MT_UPDATE
+ return TRUE
if("toggle_bolts" in href_list)
bolts = !bolts
if(bolts)
visible_message("You hear a quite click as the [src] bolts to the floor", "You hear a quite click")
else
visible_message("You hear a quite click as the [src]'s floor bolts raise", "You hear a quite click")
- return MT_UPDATE
+ return TRUE
/obj/machinery/air_sensor/attackby(var/obj/item/W as obj, var/mob/user as mob)
if(istype(W, /obj/item/multitool))
@@ -301,19 +301,19 @@
sensor_list|=G.id_tag
if(!sensor_list.len)
to_chat(user, "No sensors on this frequency.")
- return MT_ERROR
+ return FALSE
// Have the user pick one of them and name its label
var/sensor = input(user, "Select a sensor:", "Sensor Data") as null|anything in sensor_list
if(!sensor)
- return MT_ERROR
+ return FALSE
var/label = reject_bad_name( input(user, "Choose a sensor label:", "Sensor Label") as text|null, allow_numbers=1)
if(!label)
- return MT_ERROR
+ return FALSE
// Add the sensor's information to general_air_controler
sensors[sensor] = label
- return MT_UPDATE
+ return TRUE
if("edit_sensor" in href_list)
var/list/sensor_list = list()
@@ -322,14 +322,14 @@
sensor_list|=G.id_tag
if(!sensor_list.len)
to_chat(user, "No sensors on this frequency.")
- return MT_ERROR
+ return FALSE
var/label = sensors[href_list["edit_sensor"]]
var/sensor = input(user, "Select a sensor:", "Sensor Data", href_list["edit_sensor"]) as null|anything in sensor_list
if(!sensor)
- return MT_ERROR
+ return FALSE
sensors.Remove(href_list["edit_sensor"])
sensors[sensor] = label
- return MT_UPDATE
+ return TRUE
/obj/machinery/computer/general_air_control/unlinkFrom(mob/user, obj/O)
..()
diff --git a/code/game/machinery/atmoalter/meter.dm b/code/game/machinery/atmoalter/meter.dm
index d3d23e96bba..dba1d942e2d 100644
--- a/code/game/machinery/atmoalter/meter.dm
+++ b/code/game/machinery/atmoalter/meter.dm
@@ -4,17 +4,17 @@
icon = 'icons/obj/meter.dmi'
icon_state = "meterX"
var/obj/machinery/atmospherics/pipe/target = null
- anchored = 1
+ anchored = TRUE
armor = list(melee = 0, bullet = 0, laser = 0, energy = 100, bomb = 0, bio = 100, rad = 100)
power_channel = ENVIRON
- var/frequency = 0
+ var/frequency = ATMOS_DISTRO_FREQ
var/id
var/id_tag
use_power = IDLE_POWER_USE
idle_power_usage = 2
active_power_usage = 5
req_one_access_txt = "24;10"
- Mtoollink = 1
+ Mtoollink = TRUE
settagwhitelist = list("id_tag")
/obj/machinery/meter/New()
diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm
index 8655c8e0602..49a05371050 100644
--- a/code/game/machinery/hologram.dm
+++ b/code/game/machinery/hologram.dm
@@ -374,19 +374,19 @@ var/list/holopads = list()
/*This is the proc for special two-way communication between AI and holopad/people talking near holopad.
For the other part of the code, check silicon say.dm. Particularly robot talk.*/
-/obj/machinery/hologram/holopad/hear_talk(atom/movable/speaker, message, verb, datum/language/message_language)
+/obj/machinery/hologram/holopad/hear_talk(atom/movable/speaker, list/message_pieces, verb)
if(speaker && masters.len)//Master is mostly a safety in case lag hits or something. Radio_freq so AIs dont hear holopad stuff through radios.
for(var/mob/living/silicon/ai/master in masters)
if(masters[master] && speaker != master)
- master.relay_speech(speaker, message, verb, message_language)
+ master.relay_speech(speaker, message_pieces, verb)
for(var/I in holo_calls)
var/datum/holocall/HC = I
if(HC.connected_holopad == src && speaker != HC.hologram)
- HC.user.hear_say(message, verb, message_language, speaker = speaker)
+ HC.user.hear_say(message_pieces, verb, speaker = speaker)
if(outgoing_call && speaker == outgoing_call.user)
- outgoing_call.hologram.atom_say(message)
+ outgoing_call.hologram.atom_say(multilingual_to_message(message_pieces))
diff --git a/code/game/machinery/iv_drip.dm b/code/game/machinery/iv_drip.dm
index 4ccaa246578..7890110aabc 100644
--- a/code/game/machinery/iv_drip.dm
+++ b/code/game/machinery/iv_drip.dm
@@ -65,6 +65,9 @@
overlays += filling
/obj/machinery/iv_drip/MouseDrop(mob/living/target)
+ if(usr.incapacitated())
+ return
+
if(!ishuman(usr) || !iscarbon(target))
return
diff --git a/code/game/machinery/machinery.dm b/code/game/machinery/machinery.dm
index b0ab09be91b..553016400ad 100644
--- a/code/game/machinery/machinery.dm
+++ b/code/game/machinery/machinery.dm
@@ -222,11 +222,11 @@ Class Procs:
var/newid = copytext(reject_bad_text(input(usr, "Specify the new ID tag for this machine", src, src:id_tag) as null|text),1,MAX_MESSAGE_LEN)
if(newid)
src:id_tag = newid
- return MT_UPDATE|MT_REINIT
+ return TRUE
if("set_freq" in href_list)
if(!("frequency" in vars))
warning("set_freq: [type] has no frequency var.")
- return 0
+ return FALSE
var/newfreq=src:frequency
if(href_list["set_freq"]!="-1")
newfreq=text2num(href_list["set_freq"])
@@ -235,90 +235,81 @@ Class Procs:
if(newfreq)
if(findtext(num2text(newfreq), "."))
newfreq *= 10 // shift the decimal one place
- if(newfreq < 10000)
- src:frequency = newfreq
- return MT_UPDATE|MT_REINIT
- return 0
+ src:frequency = sanitize_frequency(newfreq, RADIO_LOW_FREQ, RADIO_HIGH_FREQ)
+ return TRUE
+ return FALSE
/obj/machinery/proc/handle_multitool_topic(var/href, var/list/href_list, var/mob/user)
if(!allowed(user))//no, not even HREF exploits
- return 0
+ return FALSE
var/obj/item/multitool/P = get_multitool(usr)
if(P && istype(P))
- var/update_mt_menu=0
- var/re_init=0
+ var/update_mt_menu = FALSE
if("set_tag" in href_list)
if(!(href_list["set_tag"] in settagwhitelist))//I see you're trying Href exploits, I see you're failing, I SEE ADMIN WARNING. (seriously though, this is a powerfull HREF, I originally found this loophole, I'm not leaving it in on my PR)
message_admins("set_tag HREF (var attempted to edit: [href_list["set_tag"]]) exploit attempted by [key_name_admin(user)] on [src] (JMP)")
- return 1
+ return FALSE
if(!(href_list["set_tag"] in vars))
to_chat(usr, "Something went wrong: Unable to find [href_list["set_tag"]] in vars!")
- return 1
+ return FALSE
var/current_tag = vars[href_list["set_tag"]]
var/newid = copytext(reject_bad_text(input(usr, "Specify the new value", src, current_tag) as null|text),1,MAX_MESSAGE_LEN)
if(newid)
vars[href_list["set_tag"]] = newid
- re_init=1
+ update_mt_menu = TRUE
if("unlink" in href_list)
var/idx = text2num(href_list["unlink"])
if(!idx)
- return 1
+ return FALSE
var/obj/O = getLink(idx)
if(!O)
- return 1
+ return FALSE
if(!canLink(O))
to_chat(usr, "You can't link with that device.")
- return 1
+ return FALSE
if(unlinkFrom(usr, O))
to_chat(usr, "A green light flashes on \the [P], confirming the link was removed.")
else
to_chat(usr, "A red light flashes on \the [P]. It appears something went wrong when unlinking the two devices.")
- update_mt_menu=1
+ update_mt_menu = TRUE
if("link" in href_list)
var/obj/O = P.buffer
if(!O)
- return 1
+ return FALSE
if(!canLink(O,href_list))
to_chat(usr, "You can't link with that device.")
- return 1
+ return FALSE
if(isLinkedWith(O))
to_chat(usr, "A red light flashes on \the [P]. The two devices are already linked.")
- return 1
+ return FALSE
if(linkWith(usr, O, href_list))
to_chat(usr, "A green light flashes on \the [P], confirming the link was added.")
else
to_chat(usr, "A red light flashes on \the [P]. It appears something went wrong when linking the two devices.")
- update_mt_menu=1
+ update_mt_menu = TRUE
if("buffer" in href_list)
P.buffer = src
to_chat(usr, "A green light flashes, and the device appears in the multitool buffer.")
- update_mt_menu=1
+ update_mt_menu = TRUE
if("flush" in href_list)
to_chat(usr, "A green light flashes, and the device disappears from the multitool buffer.")
P.buffer = null
- update_mt_menu=1
+ update_mt_menu = TRUE
var/ret = multitool_topic(usr,href_list,P.buffer)
- if(ret == MT_ERROR)
- return 1
- if(ret & MT_UPDATE)
- update_mt_menu=1
- if(ret & MT_REINIT)
- re_init=1
+ if(ret)
+ update_mt_menu = TRUE
- if(re_init)
- Initialize()
if(update_mt_menu)
- //usr.set_machine(src)
update_multitool_menu(usr)
- return 1
+ return TRUE
/obj/machinery/Topic(href, href_list, var/nowindow = 0, var/datum/topic_state/state = default_state)
if(..(href, href_list, nowindow, state))
diff --git a/code/game/machinery/telecomms/broadcaster.dm b/code/game/machinery/telecomms/broadcaster.dm
index 10451c758be..8e34e2e19bc 100644
--- a/code/game/machinery/telecomms/broadcaster.dm
+++ b/code/game/machinery/telecomms/broadcaster.dm
@@ -57,7 +57,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
signal.data["name"], signal.data["job"],
signal.data["realname"], signal.data["vname"],,
signal.data["compression"], signal.data["level"], signal.frequency,
- signal.data["verb"], signal.data["language"] )
+ signal.data["verb"] )
/** #### - Simple Broadcast - #### **/
@@ -83,7 +83,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
signal.data["radio"], signal.data["message"],
signal.data["name"], signal.data["job"],
signal.data["realname"], signal.data["vname"], 4, signal.data["compression"], signal.data["level"], signal.frequency,
- signal.data["verb"], signal.data["language"])
+ signal.data["verb"])
if(!message_delay)
message_delay = 1
@@ -146,7 +146,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
signal.data["radio"], signal.data["message"],
signal.data["name"], signal.data["job"],
signal.data["realname"], signal.data["vname"],, signal.data["compression"], list(0), connection.frequency,
- signal.data["verb"], signal.data["language"])
+ signal.data["verb"])
else
if(intercept)
Broadcast_Message(signal.data["connection"], signal.data["mob"],
@@ -154,7 +154,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
signal.data["radio"], signal.data["message"],
signal.data["name"], signal.data["job"],
signal.data["realname"], signal.data["vname"], 3, signal.data["compression"], list(0), connection.frequency,
- signal.data["verb"], signal.data["language"])
+ signal.data["verb"])
#define CREW_RADIO_TYPE 0
#define CENTCOMM_RADIO_TYPE 1
@@ -234,9 +234,9 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
**/
/proc/Broadcast_Message(var/datum/radio_frequency/connection, var/mob/M,
- var/vmask, var/vmessage, var/obj/item/radio/radio,
- var/message, var/name, var/job, var/realname, var/vname,
- var/data, var/compression, var/list/level, var/freq, var/verbage = "says", var/datum/language/speaking = null,
+ var/vmask, list/vmessage_pieces, var/obj/item/radio/radio,
+ list/message_pieces, var/name, var/job, var/realname, var/vname,
+ var/data, var/compression, var/list/level, var/freq, var/verbage = "says",
var/atom/follow_target = null)
@@ -339,7 +339,7 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
else
// - The speaker has a prespecified "voice message" to display if not understood -
- if(vmessage)
+ if(vmessage_pieces)
heard_voice += R
// - Just display a garbled message -
@@ -364,12 +364,11 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
// --- Filter the message; place it in quotes apply a verb ---
-
var/quotedmsg = null
if(M)
- quotedmsg = M.say_quote(message)
+ quotedmsg = "[M.say_quote(multilingual_to_message(message_pieces))], \"[multilingual_to_message(message_pieces)]\""
else
- quotedmsg = "says, \"[message]\""
+ quotedmsg = "says, \"[multilingual_to_message(message_pieces)]\""
// --- This following recording is intended for research and feedback in the use of department radio channels ---
@@ -414,38 +413,38 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
if(length(heard_masked))
for(var/mob/R in heard_masked)
- R.hear_radio(message,verbage, speaking, part_a, part_b, M, 0, name, follow_target=follow_target)
+ R.hear_radio(message_pieces, verbage, part_a, part_b, M, 0, name, follow_target=follow_target)
/* --- Process all the mobs that heard the voice normally (understood) --- */
if(length(heard_normal))
for(var/mob/R in heard_normal)
- R.hear_radio(message, verbage, speaking, part_a, part_b, M, 0, realname, follow_target=follow_target)
+ R.hear_radio(message_pieces, verbage, part_a, part_b, M, 0, realname, follow_target=follow_target)
/* --- Process all the mobs that heard the voice normally (did not understand) --- */
if(length(heard_voice))
for(var/mob/R in heard_voice)
- R.hear_radio(message,verbage, speaking, part_a, part_b, M,0, vname, follow_target=follow_target)
+ R.hear_radio(message_pieces, verbage, part_a, part_b, M,0, vname, follow_target=follow_target)
/* --- Process all the mobs that heard a garbled voice (did not understand) --- */
// Displays garbled message (ie "f*c* **u, **i*er!")
if(length(heard_garbled))
for(var/mob/R in heard_garbled)
- R.hear_radio(message, verbage, speaking, part_a, part_b, M, 1, vname, follow_target=follow_target)
+ R.hear_radio(message_pieces, verbage, part_a, part_b, M, 1, vname, follow_target=follow_target)
/* --- Complete gibberish. Usually happens when there's a compressed message --- */
if(length(heard_gibberish))
for(var/mob/R in heard_gibberish)
- R.hear_radio(message, verbage, speaking, part_a, part_b, M, 1, follow_target=follow_target)
+ R.hear_radio(message_pieces, verbage, part_a, part_b, M, 1, follow_target=follow_target)
return 1
-/proc/Broadcast_SimpleMessage(var/source, var/frequency, var/text, var/data, var/mob/M, var/compression, var/level)
-
+/proc/Broadcast_SimpleMessage(var/source, var/frequency, list/message_pieces, var/data, var/mob/M, var/compression, var/level)
+ var/text = multilingual_to_message(message_pieces)
/* ###### Prepare the radio connection ###### */
var/mob/living/carbon/human/H
diff --git a/code/game/machinery/telecomms/ntsl2.dm b/code/game/machinery/telecomms/ntsl2.dm
index ee9fabd272a..d24b51389ec 100644
--- a/code/game/machinery/telecomms/ntsl2.dm
+++ b/code/game/machinery/telecomms/ntsl2.dm
@@ -1,3 +1,7 @@
+#define JOB_STYLE_1 "Name (Job)"
+#define JOB_STYLE_2 "Name - Job"
+#define JOB_STYLE_3 "\[Job\] Name"
+#define JOB_STYLE_4 "(Job) Name"
GLOBAL_DATUM_INIT(nttc_config, /datum/nttc_configuration, new())
// Custom Implementations for NTTC
/* NTTC Configuration Datum
@@ -5,16 +9,125 @@ GLOBAL_DATUM_INIT(nttc_config, /datum/nttc_configuration, new())
* as well as allowing users to save and load configurations.
*/
/datum/nttc_configuration
+ // ALL OF THE JOB CRAP
+ // Dict of all jobs and their colors
+ var/all_jobs = list(
+ // AI
+ "AI" = "#FF00FF",
+ "Android" = "#FF00FF",
+ "Cyborg" = "#FF00FF",
+ "Personal AI" = "#FF00FF",
+ "Robot" = "#FF00FF",
+ // Civilian + Varients
+ "Assistant" = "#408010",
+ "Businessman" = "#408010",
+ "Civilian" = "#408010",
+ "Tourist" = "#408010",
+ "Trader" = "#408010",
+ // Command (Solo command, not department heads)
+ "Blueshield" = "#204090",
+ "Captain" = "#204090",
+ "Head of Personnel" = "#204090",
+ "Nanotrasen Representative" = "#204090",
+ // Engineeering
+ "Atmospheric Technician" = "#A66300",
+ "Chief Engineer" = "#A66300",
+ "Electrician" = "#A66300",
+ "Engine Technician" = "#A66300",
+ "Life Support Specialist" = "#A66300",
+ "Maintenance Technician" = "#A66300",
+ "Mechanic" = "#A66300",
+ "Station Engineer" = "#A66300",
+ // ERT
+ "Emergency Response Team Engineer" = "#5C5C7C",
+ "Emergency Response Team Leader" = "#5C5C7C",
+ "Emergency Response Team Medic" = "#5C5C7C",
+ "Emergency Response Team Member" = "#5C5C7C",
+ "Emergency Response Team Officer" = "#5C5C7C",
+ // Medical
+ "Chemist" = "#009190",
+ "Chief Medical Officer" = "#009190",
+ "Coroner" = "#009190",
+ "Medical Doctor" = "#009190",
+ "Microbiologist" = "#009190",
+ "Nurse" = "#009190",
+ "Paramedic" = "#009190",
+ "Pharmacologist" = "#009190",
+ "Pharmacist" = "#009190",
+ "Psychiatrist" = "#009190",
+ "Psychologist" = "#009190",
+ "Surgeon" = "#009190",
+ "Therapist" = "#009190",
+ "Virologist" = "#009190",
+ // Science
+ "Anomalist" = "#993399",
+ "Biomechanical Engineer" = "#993399",
+ "Chemical Researcher" = "#993399",
+ "Geneticist" = "#993399",
+ "Mechatronic Engineer" = "#993399",
+ "Plasma Researcher" = "#993399",
+ "Research Director" = "#993399",
+ "Roboticist" = "#993399",
+ "Scientist" = "#993399",
+ "Xenoarcheologist" = "#993399",
+ "Xenobiologist" = "#993399",
+ // Security
+ "Brig Physician" = "#A30000",
+ "Detective" = "#A30000",
+ "Forensic Technician" = "#A30000",
+ "Head of Security" = "#A30000",
+ "Human Resources Agent" = "#A30000",
+ "Internal Affairs Agent" = "#A30000",
+ "Magistrate" = "#A30000",
+ "Security Officer" = "#A30000",
+ "Security Pod Pilot" = "#A30000",
+ "Warden" = "#A30000",
+ // Supply
+ "Quartermaster" = "#7F6539",
+ "Cargo Technician" = "#7F6539",
+ "Shaft Miner" = "#7F6539",
+ "Spelunker" = "#7F6539",
+ // Service
+ "Barber" = "#80A000",
+ "Bartender" = "#80A000",
+ "Beautician" = "#80A000",
+ "Botanical Researcher" = "#80A000",
+ "Botanist" = "#80A000",
+ "Butcher" = "#80A000",
+ "Chaplain" = "#80A000",
+ "Chef" = "#80A000",
+ "Clown" = "#80A000",
+ "Cook" = "#80A000",
+ "Culinary Artist" = "#80A000",
+ "Custodial Technician" = "#80A000",
+ "Hair Stylist" = "#80A000",
+ "Hydroponicist" = "#80A000",
+ "Janitor" = "#80A000",
+ "Journalist" = "#80A000",
+ "Librarian" = "#80A000",
+ "Mime" = "#80A000",
+ )
+ // Just command members
+ var/heads = list("Captain", "Head of Personnel", "Nanotrasen Representative", "Blueshield", "Chief Engineer", "Chief Medical Officer", "Research Director", "Head of Security")
+ // Just ERT
+ var/ert_jobs = list("Emergency Response Team Officer", "Emergency Response Team Engineer", "Emergency Response Team Medic", "Emergency Response Team Leader", "Emergency Response Team Member")
+ // Defined so code compiles and incase someone has a non-standard job
+ var/job_color = "#000000"
+ // NOW FOR ACTUAL TOGGLES
/* Simple Toggles */
var/toggle_activated = TRUE
var/toggle_jobs = FALSE
+ var/toggle_job_color = FALSE
+ var/toggle_name_color = FALSE
var/toggle_timecode = FALSE
+ var/toggle_command_bold = FALSE
// Hack section
var/toggle_gibberish = FALSE
var/toggle_honk = FALSE
/* Strings */
var/setting_language = null
+ var/job_indicator_type = null
/* Tables */
var/list/regex = list()
@@ -38,7 +151,11 @@ GLOBAL_DATUM_INIT(nttc_config, /datum/nttc_configuration, new())
var/list/to_serialize = list(
"toggle_activated",
"toggle_jobs",
+ "toggle_job_color",
+ "toggle_name_color",
+ "job_indicator_type",
"toggle_timecode",
+ "toggle_command_bold",
"toggle_gibberish",
"toggle_honk",
"setting_language",
@@ -50,7 +167,11 @@ GLOBAL_DATUM_INIT(nttc_config, /datum/nttc_configuration, new())
var/list/serialize_sanitize = list(
"toggle_activated" = "bool",
"toggle_jobs" = "bool",
+ "toggle_job_color" = "bool",
+ "toggle_name_color" = "bool",
+ "job_indicator_type" = "string",
"toggle_timecode" = "bool",
+ "toggle_command_bold" = "bool",
"toggle_gibberish" = "bool",
"toggle_honk" = "bool",
"setting_language" = "string",
@@ -58,6 +179,10 @@ GLOBAL_DATUM_INIT(nttc_config, /datum/nttc_configuration, new())
"firewall" = "array"
)
+ // These are the job card styles
+ var/list/job_card_styles = list(
+ JOB_STYLE_1, JOB_STYLE_2, JOB_STYLE_3, JOB_STYLE_4
+ )
// Used to determine what languages are allowable for conversion. Generated during runtime.
var/list/valid_languages = list("--DISABLE--")
@@ -65,13 +190,17 @@ GLOBAL_DATUM_INIT(nttc_config, /datum/nttc_configuration, new())
/* Simple Toggles */
toggle_activated = initial(toggle_activated)
toggle_jobs = initial(toggle_jobs)
+ toggle_job_color = initial(toggle_job_color)
+ toggle_name_color = initial(toggle_name_color)
toggle_timecode = initial(toggle_timecode)
+ toggle_command_bold = initial(toggle_command_bold)
// Hack section
toggle_gibberish = initial(toggle_gibberish)
toggle_honk = initial(toggle_honk)
/* Strings */
setting_language = initial(setting_language)
+ job_indicator_type = initial(job_indicator_type)
/* Tables */
regex = list()
@@ -142,10 +271,42 @@ GLOBAL_DATUM_INIT(nttc_config, /datum/nttc_configuration, new())
if(firewall.Find(signal.data["name"]))
signal.data["reject"] = 1
- // These two stack properly.
- // Simple job indicator switch.
+ // All job and coloring shit
+ if(toggle_job_color || toggle_name_color)
+ var/job = signal.data["job"]
+ job_color = all_jobs[job]
+
+ if(toggle_name_color)
+ var/new_name = "" + signal.data["name"] + ""
+ signal.data["name"] = new_name
+ signal.data["realname"] = new_name // this is required because the broadcaster uses this directly if the speaker doesn't have a voice changer on
+
if(toggle_jobs)
- var/new_name = signal.data["name"] + " ([signal.data["job"]]) "
+ var/new_name = ""
+ var/job = signal.data["job"]
+ if(job in ert_jobs)
+ job = "ERT"
+ if(toggle_job_color)
+ switch(job_indicator_type)
+ if(JOB_STYLE_1)
+ new_name = signal.data["name"] + " ([job]) "
+ if(JOB_STYLE_2)
+ new_name = signal.data["name"] + " - [job] "
+ if(JOB_STYLE_3)
+ new_name = "\[[job]\] " + signal.data["name"] + " "
+ if(JOB_STYLE_4)
+ new_name = "([job]) " + signal.data["name"] + " "
+ else
+ switch(job_indicator_type)
+ if(JOB_STYLE_1)
+ new_name = signal.data["name"] + " ([job]) "
+ if(JOB_STYLE_2)
+ new_name = signal.data["name"] + " - [job] "
+ if(JOB_STYLE_3)
+ new_name = "\[[job]\] " + signal.data["name"] + " "
+ if(JOB_STYLE_4)
+ new_name = "([job]) " + signal.data["name"] + " "
+
signal.data["name"] = new_name
signal.data["realname"] = new_name // this is required because the broadcaster uses this directly if the speaker doesn't have a voice changer on
@@ -155,10 +316,20 @@ GLOBAL_DATUM_INIT(nttc_config, /datum/nttc_configuration, new())
signal.data["name"] = new_name
signal.data["realname"] = new_name // this is required because the broadcaster uses this directly if the speaker doesn't have a voice changer on
+ // This is hacky stuff for multilingual messages...
+ var/list/message_pieces = signal.data["message"]
+
+ // Makes heads of staff bold
+ if(toggle_command_bold)
+ var/job = signal.data["job"]
+ if((job in ert_jobs) || (job in heads))
+ for(var/datum/multilingual_say_piece/S in message_pieces)
+ S.message = "[S.message]"
+
// Hacks!
// Censor dat shit like nobody's business
if(toggle_gibberish)
- signal.data["message"] = Gibberish(signal.data["message"], 80)
+ Gibberish_all(message_pieces, 80)
// Replace everything with HONK!
if(toggle_honk)
@@ -167,25 +338,25 @@ GLOBAL_DATUM_INIT(nttc_config, /datum/nttc_configuration, new())
var/new_message = ""
for(var/i in 1 to honklength)
new_message += pick("HoNK!", "HONK", "HOOOoONK", "HONKHONK!", "HoNnnkKK!!!", "HOOOOOOOOOOONK!!!!11!", "henk!") + " "
- signal.data["message"] = new_message
-
+ signal.data["message"] = message_to_multilingual(new_message)
// Language Conversion
if(setting_language && valid_languages[setting_language])
if(setting_language == "--DISABLE--")
setting_language = null
else
- signal.data["language"] = GLOB.all_languages[setting_language]
+ for(var/datum/multilingual_say_piece/S in message_pieces)
+ S.speaking = GLOB.all_languages[setting_language]
// Regex replacements
if(islist(regex) && regex.len > 0)
- var/original = signal.data["message"]
- var/new_message = original
- for(var/reg in regex)
- var/replacePattern = pencode_to_html(regex[reg])
- var/regex/start = regex(reg, "gi")
- new_message = start.Replace(new_message, replacePattern)
- signal.data["message"] = new_message
+ for(var/datum/multilingual_say_piece/S in message_pieces)
+ var/new_message = S.message
+ for(var/reg in regex)
+ var/replacePattern = pencode_to_html(regex[reg])
+ var/regex/start = regex("[reg]", "gi")
+ new_message = start.Replace(new_message, replacePattern)
+ S.message = new_message
// Make sure the message is valid after we tinkered with it, otherwise reject it
if(signal.data["message"] == "" || !signal.data["message"])
@@ -202,6 +373,15 @@ GLOBAL_DATUM_INIT(nttc_config, /datum/nttc_configuration, new())
vars[var_to_toggle] = !vars[var_to_toggle]
log_action(user, "toggled NTTC variable [var_to_toggle] [vars[var_to_toggle] ? "on" : "off"]")
+ // Job Format
+ if(href_list["setting_job_card_style"])
+ var/card_style = input(user, "Pick a job card format.", "Job Card Format") as null|anything in job_card_styles
+ if(!card_style)
+ return
+ job_indicator_type = card_style
+ to_chat(user, "Jobs will now have the style of [card_style].")
+ log_action(user, "has set NTTC job card format to [card_style]", TRUE)
+
// Strings
if(href_list["setting_language"])
var/new_language = input(user, "Pick a language to convert messages to.", "Language Conversion") as null|anything in valid_languages
@@ -252,7 +432,7 @@ GLOBAL_DATUM_INIT(nttc_config, /datum/nttc_configuration, new())
var/list/array = vars[href_list["array"]]
array.Add(new_value)
to_chat(user, "Added row [new_value].")
- log_action(user, "updated [href_list["array"]] - new value [new_value]")
+ log_action(user, "updated [href_list["array"]] - new value [new_value]", TRUE)
if(href_list["delete_item"])
if(href_list["array"] && href_list["array"] in arrays)
@@ -316,3 +496,4 @@ GLOBAL_DATUM_INIT(nttc_config, /datum/nttc_configuration, new())
dat += "window.updateConfig = function(config) { window.config = JSON.parse(config); window.reload_tab() };"
dat += ""
return dat
+
diff --git a/code/game/machinery/telecomms/telecomunications.dm b/code/game/machinery/telecomms/telecomunications.dm
index b38eca98817..06eacdd8da1 100644
--- a/code/game/machinery/telecomms/telecomunications.dm
+++ b/code/game/machinery/telecomms/telecomunications.dm
@@ -438,7 +438,6 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
var/encryption = "null" // encryption key: ie "password"
var/salt = "null" // encryption salt: ie "123comsat"
// would add up to md5("password123comsat")
- var/language = "human"
var/obj/item/radio/headset/server_radio = null
/obj/machinery/telecomms/server/Initialize()
@@ -463,11 +462,10 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
log.parameters["mobtype"] = signal.data["mobtype"]
log.parameters["race"] = signal.data["race"]
log.parameters["job"] = signal.data["job"]
- log.parameters["language"] = signal.data["language"]
log.parameters["key"] = signal.data["key"]
- log.parameters["vmessage"] = signal.data["message"]
+ log.parameters["vmessage"] = multilingual_to_message(signal.data["message"])
log.parameters["vname"] = signal.data["vname"]
- log.parameters["message"] = signal.data["message"]
+ log.parameters["message"] = multilingual_to_message(signal.data["message"])
log.parameters["name"] = signal.data["name"]
log.parameters["realname"] = signal.data["realname"]
@@ -478,7 +476,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
// If the signal is still compressed, make the log entry gibberish
if(signal.data["compression"] > 0)
- log.parameters["message"] = Gibberish(signal.data["message"], signal.data["compression"] + 50)
+ log.parameters["message"] = Gibberish(multilingual_to_message(signal.data["message"]), signal.data["compression"] + 50)
log.parameters["job"] = Gibberish(signal.data["job"], signal.data["compression"] + 50)
log.parameters["name"] = Gibberish(signal.data["name"], signal.data["compression"] + 50)
log.parameters["realname"] = Gibberish(signal.data["realname"], signal.data["compression"] + 50)
diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm
index 2205a9f6a72..998228beee5 100644
--- a/code/game/mecha/mecha.dm
+++ b/code/game/mecha/mecha.dm
@@ -171,9 +171,9 @@
for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment)
to_chat(user, "[bicon(ME)] [ME]")
-/obj/mecha/hear_talk(mob/M, text)
+/obj/mecha/hear_talk(mob/M, list/message_pieces)
if(M == occupant && radio.broadcasting)
- radio.talk_into(M, text)
+ radio.talk_into(M, message_pieces)
/obj/mecha/proc/click_action(atom/target, mob/user, params)
if(!occupant || occupant != user )
diff --git a/code/game/objects/effects/forcefields.dm b/code/game/objects/effects/forcefields.dm
index 2305e33249e..71219b44d16 100644
--- a/code/game/objects/effects/forcefields.dm
+++ b/code/game/objects/effects/forcefields.dm
@@ -11,6 +11,18 @@
/obj/effect/forcefield/CanAtmosPass(turf/T)
return !density
+/obj/effect/forcefield/wizard
+ var/mob/wizard
+
+/obj/effect/forcefield/wizard/Initialize(mapload, mob/summoner)
+ . = ..()
+ wizard = summoner
+
+/obj/effect/forcefield/wizard/CanPass(atom/movable/mover, turf/target)
+ if(mover == wizard)
+ return TRUE
+ return FALSE
+
///////////Mimewalls///////////
/obj/effect/forcefield/mime
@@ -22,4 +34,9 @@
/obj/effect/forcefield/mime/New()
..()
if(lifetime)
- QDEL_IN(src, lifetime)
\ No newline at end of file
+ QDEL_IN(src, lifetime)
+
+/obj/effect/forcefield/mime/advanced
+ name = "invisible blockade"
+ desc = "You might be here a while."
+ lifetime = 60 SECONDS
\ No newline at end of file
diff --git a/code/game/objects/effects/spawners/lootdrop.dm b/code/game/objects/effects/spawners/lootdrop.dm
index 4d901261bbd..048396e4eed 100644
--- a/code/game/objects/effects/spawners/lootdrop.dm
+++ b/code/game/objects/effects/spawners/lootdrop.dm
@@ -151,6 +151,7 @@
/obj/item/implanter/storage = 1,
/obj/item/toy/cards/deck/syndicate = 2,
/obj/item/storage/secure/briefcase/syndie = 2,
+ /obj/item/storage/fancy/cigarettes/cigpack_syndicate = 2,
"" = 70
)
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 9af8b04580a..f8798d77c5d 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -310,6 +310,7 @@ var/global/image/fire_overlay = image("icon" = 'icons/goonstation/effects/fire.d
return ..()
/obj/item/proc/hit_reaction(mob/living/carbon/human/owner, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
+ SEND_SIGNAL(src, COMSIG_ITEM_HIT_REACT, args)
if(prob(final_block_chance))
owner.visible_message("[owner] blocks [attack_text] with [src]!")
return 1
@@ -324,6 +325,7 @@ var/global/image/fire_overlay = image("icon" = 'icons/goonstation/effects/fire.d
A.Remove(user)
if(flags & DROPDEL)
qdel(src)
+ SEND_SIGNAL(src, COMSIG_ITEM_DROPPED,user)
// called just as an item is picked up (loc is not yet changed)
/obj/item/proc/pickup(mob/user)
@@ -351,6 +353,7 @@ var/global/image/fire_overlay = image("icon" = 'icons/goonstation/effects/fire.d
// for items that can be placed in multiple slots
// note this isn't called during the initial dressing of a player
/obj/item/proc/equipped(var/mob/user, var/slot)
+ SEND_SIGNAL(src, COMSIG_ITEM_EQUIPPED, user, slot)
for(var/X in actions)
var/datum/action/A = X
if(item_action_slot_check(slot, user)) //some items only give their actions buttons when in a specific slot.
@@ -498,6 +501,7 @@ var/global/image/fire_overlay = image("icon" = 'icons/goonstation/effects/fire.d
/obj/item/throw_impact(atom/A)
if(A && !QDELETED(A))
+ SEND_SIGNAL(src, COMSIG_MOVABLE_IMPACT, A)
var/itempush = 1
if(w_class < WEIGHT_CLASS_BULKY)
itempush = 0 // too light to push anything
@@ -548,6 +552,7 @@ var/global/image/fire_overlay = image("icon" = 'icons/goonstation/effects/fire.d
return I == src
/obj/item/Crossed(atom/movable/AM)
+ . = ..()
if(prob(trip_chance) && ishuman(AM))
var/mob/living/carbon/human/H = AM
on_trip(H)
@@ -558,3 +563,10 @@ var/global/image/fire_overlay = image("icon" = 'icons/goonstation/effects/fire.d
/obj/item/attack_hulk(mob/living/carbon/human/user)
return FALSE
+
+/obj/item/proc/check_martial_counter(mob/living/carbon/human/target, mob/living/carbon/human/user) //handles block for CQC
+ if(target.check_block())
+ target.visible_message("[target.name] blocks [src] and twists [user]'s arm behind [user.p_their()] back!",
+ "You block the attack!")
+ user.Stun(2)
+ return TRUE
\ No newline at end of file
diff --git a/code/game/objects/items/devices/flash.dm b/code/game/objects/items/devices/flash.dm
index 5056c90793f..c24fcc3c68a 100644
--- a/code/game/objects/items/devices/flash.dm
+++ b/code/game/objects/items/devices/flash.dm
@@ -194,7 +194,7 @@
/obj/item/flash/armimplant
name = "photon projector"
- desc = "A high-powered photon projector implant normally used for lighting purposes, but also doubles as a flashbulb weapon. Self-repair protocals fix the flashbulb if it ever burns out."
+ desc = "A high-powered photon projector implant normally used for lighting purposes, but also doubles as a flashbulb weapon. Self-repair protocols fix the flashbulb if it ever burns out."
var/flashcd = 20
var/overheat = 0
var/obj/item/organ/internal/cyberimp/arm/flash/I = null
diff --git a/code/game/objects/items/devices/megaphone.dm b/code/game/objects/items/devices/megaphone.dm
index 43515ed5241..59a7a650383 100644
--- a/code/game/objects/items/devices/megaphone.dm
+++ b/code/game/objects/items/devices/megaphone.dm
@@ -47,8 +47,10 @@
message = sanitize(copytext(message, 1, MAX_MESSAGE_LEN))
if(!message)
return
- message = user.handle_speech_problems(message)[1]
message = capitalize(message)
+ var/list/message_pieces = message_to_multilingual(message)
+ user.handle_speech_problems(message_pieces)
+ message = multilingual_to_message(message_pieces)
if((loc == user && !user.incapacitated()))
if(emagged)
if(insults)
@@ -69,7 +71,7 @@
audible_message("[user.GetVoice()] [user.GetAltName()] broadcasts, \"[message]\"", hearing_distance = 14)
log_say(message, user)
for(var/obj/O in oview(14, get_turf(src)))
- O.hear_talk(user, "[message]")
+ O.hear_talk(user, message_to_multilingual("[message]"))
/obj/item/megaphone/emag_act(user as mob)
if(!emagged)
diff --git a/code/game/objects/items/devices/radio/encryptionkey.dm b/code/game/objects/items/devices/radio/encryptionkey.dm
index fb5e41ec663..31945743ad2 100644
--- a/code/game/objects/items/devices/radio/encryptionkey.dm
+++ b/code/game/objects/items/devices/radio/encryptionkey.dm
@@ -10,6 +10,7 @@
var/translate_binary = FALSE
var/translate_hive = FALSE
var/syndie = FALSE
+ var/change_voice = FALSE
var/list/channels = list()
@@ -20,7 +21,7 @@
channels = list("Syndicate" = 1)
origin_tech = "syndicate=1;engineering=3;bluespace=2"
syndie = TRUE //Signifies that it de-crypts Syndicate transmissions
- var/change_voice = TRUE
+ change_voice = TRUE
var/fake_name = "Agent ALERT_A_CODER"
var/static/list/fakename_list
diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm
index 4bd3896c7a5..1e3acfc7812 100644
--- a/code/game/objects/items/devices/radio/headset.dm
+++ b/code/game/objects/items/devices/radio/headset.dm
@@ -50,15 +50,15 @@
to_chat(user, "The following channels are available:")
to_chat(user, radio_desc)
-/obj/item/radio/headset/handle_message_mode(mob/living/M as mob, message, channel)
+/obj/item/radio/headset/handle_message_mode(mob/living/M as mob, list/message_pieces, channel)
if(channel == "special")
if(translate_binary)
var/datum/language/binary = GLOB.all_languages["Robot Talk"]
- binary.broadcast(M, message)
+ binary.broadcast(M, strip_prefixes(multilingual_to_message(message_pieces)))
return RADIO_CONNECTION_NON_SUBSPACE
if(translate_hive)
var/datum/language/hivemind = GLOB.all_languages["Hivemind"]
- hivemind.broadcast(M, message)
+ hivemind.broadcast(M, strip_prefixes(multilingual_to_message(message_pieces)))
return RADIO_CONNECTION_NON_SUBSPACE
return RADIO_CONNECTION_FAIL
diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm
index 1409c463000..68d31f075b6 100644
--- a/code/game/objects/items/devices/radio/radio.dm
+++ b/code/game/objects/items/devices/radio/radio.dm
@@ -285,7 +285,7 @@ var/global/list/default_medbay_channels = list(
qdel(src)
// Interprets the message mode when talking into a radio, possibly returning a connection datum
-/obj/item/radio/proc/handle_message_mode(mob/living/M as mob, message, message_mode)
+/obj/item/radio/proc/handle_message_mode(mob/living/M as mob, list/message_pieces, message_mode)
// If a channel isn't specified, send to common.
if(!message_mode || message_mode == "headset")
return radio_connection
@@ -301,11 +301,11 @@ var/global/list/default_medbay_channels = list(
// If we were to send to a channel we don't have, drop it.
return RADIO_CONNECTION_FAIL
-/obj/item/radio/talk_into(mob/living/M as mob, message, channel, var/verb = "says", var/datum/language/speaking = null)
+/obj/item/radio/talk_into(mob/living/M as mob, list/message_pieces, channel, var/verb = "says")
if(!on)
return 0 // the device has to be on
// Fix for permacell radios, but kinda eh about actually fixing them.
- if(!M || !message)
+ if(!M || !message_pieces)
return 0
// Uncommenting this. To the above comment:
@@ -328,7 +328,7 @@ var/global/list/default_medbay_channels = list(
*/
//#### Grab the connection datum ####//
- var/message_mode = handle_message_mode(M, message, channel)
+ var/message_mode = handle_message_mode(M, message_pieces, channel)
switch(message_mode) //special cases
if(RADIO_CONNECTION_FAIL)
return 0
@@ -361,7 +361,7 @@ var/global/list/default_medbay_channels = list(
var/jobname // the mob's "job"
if(jammed)
- message = Gibberish(message, 100)
+ Gibberish_all(message_pieces, 100)
// --- Human: use their actual job ---
if(ishuman(M))
@@ -432,7 +432,7 @@ var/global/list/default_medbay_channels = list(
// Other tags:
"compression" = rand(45,50), // compressed radio signal
- "message" = message, // the actual sent message
+ "message" = message_pieces, // the actual sent message
"connection" = connection, // the radio connection to use
"radio" = src, // stores the radio used for transmission
"slow" = 0, // how much to sleep() before broadcasting - simulates net lag
@@ -441,7 +441,6 @@ var/global/list/default_medbay_channels = list(
"server" = null, // the last server to log this signal
"reject" = 0, // if nonzero, the signal will not be accepted by any broadcasting machinery
"level" = position.z, // The source's z level
- "language" = speaking,
"verb" = verb
)
signal.frequency = connection.frequency // Quick frequency set
@@ -490,7 +489,7 @@ var/global/list/default_medbay_channels = list(
"vmask" = voicemask, // 1 if the mob is using a voice gas mas
"compression" = 0, // uncompressed radio signal
- "message" = message, // the actual sent message
+ "message" = message_pieces, // the actual sent message
"connection" = connection, // the radio connection to use
"radio" = src, // stores the radio used for transmission
"slow" = 0,
@@ -499,7 +498,6 @@ var/global/list/default_medbay_channels = list(
"server" = null,
"reject" = 0,
"level" = position.z,
- "language" = speaking,
"verb" = verb
)
signal.frequency = connection.frequency // Quick frequency set
@@ -522,15 +520,14 @@ var/global/list/default_medbay_channels = list(
if(!connection) return 0 //~Carn
return Broadcast_Message(connection, M, voicemask, pick(M.speak_emote),
- src, message, displayname, jobname, real_name, M.voice_name,
- filter_type, signal.data["compression"], list(position.z), connection.frequency,verb,speaking)
+ src, message_pieces, displayname, jobname, real_name, M.voice_name,
+ filter_type, signal.data["compression"], list(position.z), connection.frequency,verb)
-/obj/item/radio/hear_talk(mob/M as mob, msg, var/verb = "says", var/datum/language/speaking = null)
-
+/obj/item/radio/hear_talk(mob/M as mob, list/message_pieces, var/verb = "says")
if(broadcasting)
if(get_dist(src, M) <= canhear_range)
- talk_into(M, msg,null,verb,speaking)
+ talk_into(M, message_pieces, null, verb)
/*
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index aece125c657..cf05ceb0bac 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -5,7 +5,6 @@ DETECTIVE SCANNER
HEALTH ANALYZER
GAS ANALYZER
PLANT ANALYZER
-MASS SPECTROMETER
REAGENT SCANNER
*/
/obj/item/t_scanner
@@ -432,95 +431,9 @@ REAGENT SCANNER
amount += inaccurate
return DisplayTimeText(max(1, amount))
-/obj/item/mass_spectrometer
- desc = "A hand-held mass spectrometer which identifies trace chemicals in a blood sample. Inject sample with syringe."
- name = "mass-spectrometer"
- icon = 'icons/obj/device.dmi'
- icon_state = "spectrometer"
- item_state = "analyzer"
- w_class = WEIGHT_CLASS_SMALL
- flags = CONDUCT
- container_type = OPENCONTAINER
- slot_flags = SLOT_BELT
- throwforce = 5
- throw_speed = 4
- throw_range = 20
- materials = list(MAT_METAL=150, MAT_GLASS=100)
- origin_tech = "magnets=2;biotech=1;plasmatech=2"
- var/details = 0
- var/datatoprint = ""
- var/scanning = TRUE
- actions_types = list(/datum/action/item_action/print_report)
-
-/obj/item/mass_spectrometer/New()
- ..()
- create_reagents(5)
-
-/obj/item/mass_spectrometer/on_reagent_change()
- if(reagents.total_volume)
- icon_state = initial(icon_state) + "_s"
- else
- icon_state = initial(icon_state)
-
-/obj/item/mass_spectrometer/attack_self(mob/user as mob)
- if(user.stat)
- return
- if(!user.IsAdvancedToolUser())
- to_chat(user, "You don't have the dexterity to do this!")
- return
- if(reagents.total_volume)
- var/list/blood_traces = list()
- for(var/datum/reagent/R in reagents.reagent_list)
- if(R.id != "blood")
- to_chat(user, "The sample was contaminated! Please insert another sample.")
- reagents.clear_reagents()
- return
- else
- blood_traces = params2list(R.data["trace_chem"])
- break
- var/dat = ""
- for(var/R in blood_traces)
- if(details)
- dat += "[R] ([blood_traces[R]] units) "
- else
- dat += "[R] "
- to_chat(user, "Analysis completed. Chemicals found: [dat]")
- scanning = FALSE
- datatoprint = dat
- reagents.clear_reagents()
- return
-
-/obj/item/mass_spectrometer/adv
- name = "advanced mass-spectrometer"
- icon_state = "adv_spectrometer"
- details = 1
- origin_tech = "magnets=4;biotech=3;plasmatech=3"
-
-/obj/item/mass_spectrometer/proc/print_report()
- if(!scanning)
- scanning = TRUE
- usr.visible_message("[src] rattles and prints out a sheet of paper.")
- playsound(loc, 'sound/goonstation/machines/printer_thermal.ogg', 50, 1)
- sleep(50)
-
- var/obj/item/paper/P = new(get_turf(src))
- P.name = "Mass Spectrometer Scanner Report: [station_time_timestamp()]"
- P.info = "
Mass Spectrometer
Data Analysis:
Trace chemicals detected: [datatoprint] "
-
- if(ismob(loc))
- var/mob/M = loc
- M.put_in_hands(P)
- to_chat(M, "Report printed. Log cleared.")
- datatoprint = ""
- else
- to_chat(usr, "[src] has no logs or is already in use.")
-
-/obj/item/mass_spectrometer/ui_action_click()
- print_report()
-
/obj/item/reagent_scanner
name = "reagent scanner"
- desc = "A hand-held reagent scanner which identifies chemical agents."
+ desc = "A hand-held reagent scanner which identifies chemical agents and blood types."
icon = 'icons/obj/device.dmi'
icon_state = "spectrometer"
item_state = "analyzer"
@@ -532,9 +445,9 @@ REAGENT SCANNER
throw_range = 20
materials = list(MAT_METAL=30, MAT_GLASS=20)
origin_tech = "magnets=2;biotech=1;plasmatech=2"
- var/details = 0
+ var/details = FALSE
var/datatoprint = ""
- var/scanning = 1
+ var/scanning = TRUE
actions_types = list(/datum/action/item_action/print_report)
/obj/item/reagent_scanner/afterattack(obj/O, mob/user as mob)
@@ -548,14 +461,19 @@ REAGENT SCANNER
if(!isnull(O.reagents))
var/dat = ""
+ var/blood_type = ""
if(O.reagents.reagent_list.len > 0)
var/one_percent = O.reagents.total_volume / 100
for(var/datum/reagent/R in O.reagents.reagent_list)
- dat += " [TAB][R][details ? ": [R.volume / one_percent]%" : ""]"
+ if(R.id != "blood")
+ dat += " [TAB][R][details ? ": [R.volume / one_percent]%" : ""]"
+ else
+ blood_type = R.data["blood_type"]
+ dat += " [TAB][R][blood_type ? " [blood_type]" : ""][details ? ": [R.volume / one_percent]%" : ""]"
if(dat)
to_chat(user, "Chemicals found: [dat]")
datatoprint = dat
- scanning = 0
+ scanning = FALSE
else
to_chat(user, "No active chemical agents found in [O].")
else
@@ -565,7 +483,7 @@ REAGENT SCANNER
/obj/item/reagent_scanner/adv
name = "advanced reagent scanner"
icon_state = "adv_spectrometer"
- details = 1
+ details = TRUE
origin_tech = "magnets=4;biotech=3;plasmatech=3"
/obj/item/reagent_scanner/proc/print_report()
@@ -583,7 +501,7 @@ REAGENT SCANNER
M.put_in_hands(P)
to_chat(M, "Report printed. Log cleared.")
datatoprint = ""
- scanning = 1
+ scanning = TRUE
else
to_chat(usr, "[src] has no logs or is already in use.")
@@ -673,7 +591,7 @@ REAGENT SCANNER
icon_state = "bodyanalyzer_1"
else
icon_state = "bodyanalyzer_2"
-
+
var/overlayid = round(percent / 10)
overlayid = "bodyanalyzer_charge[overlayid]"
overlays += icon(icon, overlayid)
@@ -684,18 +602,18 @@ REAGENT SCANNER
/obj/item/bodyanalyzer/attack(mob/living/M, mob/living/carbon/human/user)
if(user.incapacitated() || !user.Adjacent(M))
return
-
+
if(!ready)
to_chat(user, "The scanner beeps angrily at you! It's currently recharging - [round((time_to_use - world.time) * 0.1)] seconds remaining.")
playsound(user.loc, 'sound/machines/buzz-sigh.ogg', 50, 1)
return
-
+
if(power_supply.charge >= usecharge)
mobScan(M, user)
else
to_chat(user, "The scanner beeps angrily at you! It's out of charge!")
playsound(user.loc, 'sound/machines/buzz-sigh.ogg', 50, 1)
-
+
/obj/item/bodyanalyzer/proc/mobScan(mob/living/M, mob/user)
if(ishuman(M))
var/report = generate_printing_text(M, user)
@@ -721,12 +639,12 @@ REAGENT SCANNER
time_to_use = world.time + 600
else
to_chat(user, "Scanning error detected. Invalid specimen.")
-
+
//Unashamedly ripped from adv_med.dm
/obj/item/bodyanalyzer/proc/generate_printing_text(mob/living/M, mob/user)
var/dat = ""
var/mob/living/carbon/human/target = M
-
+
dat = "Target Statistics: "
var/t1
switch(target.stat) // obvious, see what their status is
@@ -881,5 +799,5 @@ REAGENT SCANNER
dat += "Photoreceptor abnormalities detected. "
if(target.disabilities & NEARSIGHTED)
dat += "Retinal misalignment detected. "
-
+
return dat
diff --git a/code/game/objects/items/devices/taperecorder.dm b/code/game/objects/items/devices/taperecorder.dm
index 9b36b70fdf2..4eaf0e79dce 100644
--- a/code/game/objects/items/devices/taperecorder.dm
+++ b/code/game/objects/items/devices/taperecorder.dm
@@ -85,7 +85,8 @@
icon_state = "taperecorder_idle"
-/obj/item/taperecorder/hear_talk(mob/living/M as mob, msg)
+/obj/item/taperecorder/hear_talk(mob/living/M as mob, list/message_pieces)
+ var/msg = multilingual_to_message(message_pieces)
if(mytape && recording)
var/ending = copytext(msg, length(msg))
mytape.timestamp += mytape.used_capacity
diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm
index dbd7701ce93..371c6248a6e 100644
--- a/code/game/objects/items/devices/transfer_valve.dm
+++ b/code/game/objects/items/devices/transfer_valve.dm
@@ -75,10 +75,10 @@
return
attached_device.HasProximity(AM)
-/obj/item/transfer_valve/hear_talk(mob/living/M, msg)
+/obj/item/transfer_valve/hear_talk(mob/living/M, list/message_pieces)
..()
for(var/obj/O in contents)
- O.hear_talk(M, msg)
+ O.hear_talk(M, message_pieces)
/obj/item/transfer_valve/hear_message(mob/living/M, msg)
..()
diff --git a/code/game/objects/items/devices/uplinks.dm b/code/game/objects/items/devices/uplinks.dm
index 1c3124a74cb..d0c0622d1e5 100644
--- a/code/game/objects/items/devices/uplinks.dm
+++ b/code/game/objects/items/devices/uplinks.dm
@@ -109,7 +109,7 @@ var/list/world_uplinks = list()
var/list/random_items = new
for(var/IR in ItemsReference)
var/datum/uplink_item/UI = ItemsReference[IR]
- if(UI.cost <= uses)
+ if(UI.cost <= uses && UI.limited_stock != 0)
random_items += UI
return pick(random_items)
@@ -132,7 +132,12 @@ var/list/world_uplinks = list()
/obj/item/uplink/proc/buy(var/datum/uplink_item/UI, var/reference)
if(!UI)
return
+ if(UI.limited_stock == 0)
+ to_chat(usr, "You have redeemed this discount already.")
+ return
UI.buy(src,usr)
+ if(UI.limited_stock > 0) // only decrement it if it's actually limited
+ UI.limited_stock--
SSnanoui.update_uis(src)
/* var/list/L = UI.spawn_item(get_turf(usr),src)
diff --git a/code/game/objects/items/weapons/clown_items.dm b/code/game/objects/items/weapons/clown_items.dm
index 165a61baa75..1143954faef 100644
--- a/code/game/objects/items/weapons/clown_items.dm
+++ b/code/game/objects/items/weapons/clown_items.dm
@@ -18,35 +18,21 @@
hitsound = null
throwforce = 3
w_class = WEIGHT_CLASS_TINY
+ var/list/honk_sounds = list('sound/items/bikehorn.ogg' = 1)
throw_speed = 3
throw_range = 15
attack_verb = list("HONKED")
- var/spam_flag = 0
- var/honk_sound = 'sound/items/bikehorn.ogg'
- var/cooldowntime = 20
-
-/obj/item/bikehorn/attack(mob/living/carbon/M, mob/living/carbon/user)
- if(!spam_flag)
- playsound(loc, honk_sound, 50, 1, -1) //plays instead of tap.ogg!
- return ..()
-
-/obj/item/bikehorn/attack_self(mob/user)
- if(!spam_flag)
- spam_flag = 1
- playsound(src.loc, honk_sound, 50, 1)
- src.add_fingerprint(user)
- spawn(cooldowntime)
- spam_flag = 0
- return
+/obj/item/bikehorn/Initialize()
+ . = ..()
+ AddComponent(/datum/component/squeak, honk_sounds, 50)
/obj/item/bikehorn/airhorn
name = "air horn"
desc = "Damn son, where'd you find this?"
icon_state = "air_horn"
- honk_sound = 'sound/items/AirHorn2.ogg'
- cooldowntime = 50
origin_tech = "materials=4;engineering=4"
+ honk_sounds = list('sound/items/airhorn2.ogg' = 1)
/obj/item/bikehorn/golden
name = "golden bike horn"
@@ -63,11 +49,10 @@
..()
/obj/item/bikehorn/golden/proc/flip_mobs(mob/living/carbon/M, mob/user)
- if(!spam_flag)
- var/turf/T = get_turf(src)
- for(M in ohearers(7, T))
- if(istype(M, /mob/living/carbon/human))
- var/mob/living/carbon/human/H = M
- if(!H.can_hear())
- continue
- M.emote("flip")
+ var/turf/T = get_turf(src)
+ for(M in ohearers(7, T))
+ if(istype(M, /mob/living/carbon/human))
+ var/mob/living/carbon/human/H = M
+ if(!H.can_hear())
+ continue
+ M.emote("flip")
diff --git a/code/game/objects/items/weapons/grenades/chem_grenade.dm b/code/game/objects/items/weapons/grenades/chem_grenade.dm
index 8419f0b7537..2f5b48ff329 100644
--- a/code/game/objects/items/weapons/grenades/chem_grenade.dm
+++ b/code/game/objects/items/weapons/grenades/chem_grenade.dm
@@ -247,9 +247,9 @@
if(nadeassembly)
nadeassembly.on_found(finder)
-/obj/item/grenade/chem_grenade/hear_talk(mob/living/M, msg)
+/obj/item/grenade/chem_grenade/hear_talk(mob/living/M, list/message_pieces)
if(nadeassembly)
- nadeassembly.hear_talk(M, msg)
+ nadeassembly.hear_talk(M, message_pieces)
/obj/item/grenade/chem_grenade/hear_message(mob/living/M, msg)
if(nadeassembly)
diff --git a/code/game/objects/items/weapons/holy_weapons.dm b/code/game/objects/items/weapons/holy_weapons.dm
index 56205b17997..86160328891 100644
--- a/code/game/objects/items/weapons/holy_weapons.dm
+++ b/code/game/objects/items/weapons/holy_weapons.dm
@@ -11,6 +11,7 @@
var/reskinned = FALSE
var/reskin_selectable = TRUE //set to FALSE if a subtype is meant to not normally be available as a reskin option (fluff ones will get re-added through their list)
var/list/fluff_transformations = list() //does it have any special transformations only accessible to it? Should only be subtypes of /obj/item/nullrod
+ var/sanctify_force = 0
/obj/item/nullrod/suicide_act(mob/user)
user.visible_message("[user] is killing [user.p_them()]self with \the [src.name]! It looks like [user.p_theyre()] trying to get closer to god!")
@@ -25,12 +26,29 @@
to_chat(M, "The nullrod's power interferes with your own!")
M.mind.vampire.nullified = max(5, M.mind.vampire.nullified + 2)
+/obj/item/nullrod/pickup(mob/living/user)
+ . = ..()
+ if(sanctify_force)
+ if(!user.mind || !user.mind.isholy)
+ user.adjustBruteLoss(force)
+ user.adjustFireLoss(sanctify_force)
+ user.Weaken(5)
+ user.unEquip(src, 1)
+ user.visible_message("[src] slips out of the grip of [user] as they try to pick it up, bouncing upwards and smacking [user.p_them()] in the face!", \
+ "[src] slips out of your grip as you pick it up, bouncing upwards and smacking you in the face!")
+ playsound(get_turf(user), 'sound/effects/hit_punch.ogg', 50, 1, -1)
+ throw_at(get_edge_target_turf(user, pick(alldirs)), rand(1, 3), 5)
+
+
/obj/item/nullrod/attack_self(mob/user)
- if(reskinned)
- return
- if(user.mind && (user.mind.assigned_role == "Chaplain" || user.mind.special_role == SPECIAL_ROLE_ERT))
+ if(user.mind && (user.mind.isholy) && !reskinned)
reskin_holy_weapon(user)
+/obj/item/nullrod/examine(mob/living/user)
+ . = ..()
+ if(sanctify_force)
+ to_chat(user, "It bears the inscription: 'Sanctified weapon of the inquisitors. Only the worthy may wield. Nobody shall expect us.'")
+
/obj/item/nullrod/proc/reskin_holy_weapon(mob/M)
var/list/holy_weapons_list = typesof(/obj/item/nullrod)
for(var/entry in holy_weapons_list)
@@ -60,11 +78,27 @@
holy_weapon.reskinned = TRUE
M.unEquip(src)
M.put_in_active_hand(holy_weapon)
+ if(sanctify_force)
+ holy_weapon.sanctify_force = sanctify_force
+ holy_weapon.name = "sanctified " + holy_weapon.name
qdel(src)
-/obj/item/nullrod/fluff //fluff subtype to be used for all donator nullrods
+/obj/item/nullrod/afterattack(atom/movable/AM, mob/user, proximity)
+ . = ..()
+ if(!sanctify_force)
+ return
+ if(isliving(AM))
+ var/mob/living/L = AM
+ L.adjustFireLoss(sanctify_force) // Bonus fire damage for sanctified (ERT) versions of nullrod
+
+/obj/item/nullrod/fluff // fluff subtype to be used for all donator nullrods
reskin_selectable = FALSE
+/obj/item/nullrod/ert // ERT subtype, applies sanctified property to any derived rod
+ name = "inquisitor null rod"
+ reskin_selectable = FALSE
+ sanctify_force = 10
+
/obj/item/nullrod/godhand
name = "god hand"
icon_state = "disintegrate"
@@ -126,7 +160,7 @@
hitsound = 'sound/weapons/chainsaw.ogg'
/obj/item/nullrod/claymore/glowing
- name = "force weapon"
+ name = "force blade"
icon_state = "swordon"
item_state = "swordon"
desc = "The blade glows with the power of faith. Or possibly a battery."
@@ -147,7 +181,7 @@
slot_flags = SLOT_BELT
/obj/item/nullrod/claymore/saber
- name = "light energy sword"
+ name = "light energy blade"
hitsound = 'sound/weapons/blade1.ogg'
icon_state = "swordblue"
item_state = "swordblue"
@@ -155,13 +189,13 @@
slot_flags = SLOT_BELT
/obj/item/nullrod/claymore/saber/red
- name = "dark energy sword"
+ name = "dark energy blade"
icon_state = "swordred"
item_state = "swordred"
desc = "Woefully ineffective when used on steep terrain."
/obj/item/nullrod/claymore/saber/pirate
- name = "nautical energy sword"
+ name = "nautical energy cutlass"
icon_state = "cutlass1"
item_state = "cutlass1"
desc = "Convincing HR that your religion involved piracy was no mean feat."
@@ -279,7 +313,7 @@
/obj/item/nullrod/whip
name = "holy whip"
- desc = "What a terrible night to be in spess"
+ desc = "A whip, blessed with the power to banish evil shadowy creatures. What a terrible night to be in spess."
icon_state = "chain"
item_state = "chain"
slot_flags = SLOT_BELT
@@ -298,11 +332,11 @@
if(is_shadow(H))
var/phrase = pick("Die monster! You don't belong in this world!!!", "You steal men's souls and make them your slaves!!!", "Your words are as empty as your soul!!!", "Mankind ill needs a savior such as you!!!")
user.say("[phrase]")
- H.adjustBruteLoss(8) //Bonus damage
+ H.adjustBruteLoss(12) //Bonus damage
/obj/item/nullrod/fedora
- name = "athiest's fedora"
- desc = "The brim of the hat is as sharp as your wit. Throwing it at someone would hurt almost as much as disproving the existence of God."
+ name = "binary fedora"
+ desc = "The brim of the hat is as sharp as the division between 0 and 1. It makes a mighty throwing weapon."
icon_state = "fedora"
item_state = "fedora"
slot_flags = SLOT_HEAD
@@ -310,7 +344,7 @@
force = 0
throw_speed = 4
throw_range = 7
- throwforce = 20
+ throwforce = 25 // Yes, this is high, since you can typically only use it once in a fight.
/obj/item/nullrod/armblade
name = "dark blessing"
@@ -335,7 +369,7 @@
/obj/item/nullrod/carp/attack_self(mob/living/user)
if(used_blessing)
return
- if(user.mind && (user.mind.assigned_role != "Chaplain" && user.mind.special_role != SPECIAL_ROLE_ERT))
+ if(user.mind && !user.mind.isholy)
return
to_chat(user, "You are blessed by Carp-Sie. Wild space carp will no longer attack you.")
user.faction |= "carp"
@@ -408,7 +442,7 @@
if(!iscarbon(M))
return ..()
- if(!user.mind || (user.mind.assigned_role != "Chaplain" && user.mind.special_role != SPECIAL_ROLE_ERT))
+ if(!user.mind || !user.mind.isholy)
to_chat(user, "You are not close enough with [ticker.Bible_deity_name] to use [src].")
return
@@ -420,7 +454,7 @@
praying = 1
if(do_after(user, 150, target = M))
- if(ishuman(M)) // This probably should not work on vulps. They're unholy abominations.
+ if(ishuman(M))
var/mob/living/carbon/human/target = M
if(target.mind)
@@ -470,7 +504,7 @@
/obj/item/nullrod/salt/attack_self(mob/user)
- if(!user.mind || (user.mind.assigned_role != "Chaplain" && user.mind.special_role != SPECIAL_ROLE_ERT ))
+ if(!user.mind || !user.mind.isholy)
to_chat(user, "You are not close enough with [ticker.Bible_deity_name] to use [src].")
return
diff --git a/code/game/objects/items/weapons/storage/bible.dm b/code/game/objects/items/weapons/storage/bible.dm
index 03787385e1c..16ecd7cc72c 100644
--- a/code/game/objects/items/weapons/storage/bible.dm
+++ b/code/game/objects/items/weapons/storage/bible.dm
@@ -38,10 +38,6 @@
return
/obj/item/storage/bible/attack(mob/living/M as mob, mob/living/user as mob)
- var/chaplain = 0
- if(user.mind && (user.mind.assigned_role == "Chaplain"))
- chaplain = 1
-
add_attack_logs(user, M, "Hit with [src]")
if(!iscarbon(user))
M.LAssailant = null
@@ -51,7 +47,7 @@
if(!(istype(user, /mob/living/carbon/human) || ticker) && ticker.mode.name != "monkey")
to_chat(user, "You don't have the dexterity to do this!")
return
- if(!chaplain)
+ if(!user.mind || !user.mind.isholy)
to_chat(user, "The book sizzles in your hands.")
user.take_organ_damage(0,10)
return
@@ -62,14 +58,7 @@
user.Paralyse(20)
return
-// if(..() == BLOCKED)
-// return
-
if(M.stat !=2)
- /*if((M.mind in ticker.mode.cult) && (prob(20)))
- to_chat(M, "The power of [src.deity_name] clears your mind of heresy!")
- to_chat(user, "You see how [M]'s eyes become clear, the cult no longer holds control over [M.p_them()]!")
- ticker.mode.remove_cultist(M.mind)*/
if((istype(M, /mob/living/carbon/human) && prob(60)))
bless(M)
for(var/mob/O in viewers(M, null))
@@ -94,11 +83,11 @@
return
if(istype(A, /turf/simulated/floor))
to_chat(user, "You hit the floor with the bible.")
- if(user.mind && (user.mind.assigned_role == "Chaplain"))
+ if(user.mind && (user.mind.isholy))
for(var/obj/effect/rune/R in A)
if(R.invisibility)
R.talismanreveal()
- if(user.mind && (user.mind.assigned_role == "Chaplain"))
+ if(user.mind && (user.mind.isholy))
if(A.reagents && A.reagents.has_reagent("water")) //blesses all the water in the holder
to_chat(user, "You bless [A].")
var/water2holy = A.reagents.get_reagent_amount("water")
diff --git a/code/game/objects/items/weapons/storage/boxes.dm b/code/game/objects/items/weapons/storage/boxes.dm
index 9d0289f7fc3..1d23049064f 100644
--- a/code/game/objects/items/weapons/storage/boxes.dm
+++ b/code/game/objects/items/weapons/storage/boxes.dm
@@ -879,7 +879,9 @@
/obj/item/storage/box/centcomofficer
name = "officer kit"
icon_state = "box_ert"
-
+ storage_slots = 14
+ max_combined_w_class = 20
+
/obj/item/storage/box/centcomofficer/New()
..()
contents = list()
@@ -893,7 +895,8 @@
new /obj/item/implanter/death_alarm(src)
new /obj/item/reagent_containers/hypospray/combat/nanites(src)
- new /obj/item/pinpointer/advpinpointer(src)
+ new /obj/item/pinpointer(src)
+ new /obj/item/pinpointer/crew/centcom(src)
/obj/item/storage/box/responseteam
name = "boxed survival kit"
diff --git a/code/game/objects/items/weapons/storage/firstaid.dm b/code/game/objects/items/weapons/storage/firstaid.dm
index 3bed8e15333..f484d8c001b 100644
--- a/code/game/objects/items/weapons/storage/firstaid.dm
+++ b/code/game/objects/items/weapons/storage/firstaid.dm
@@ -158,6 +158,28 @@
/obj/item/storage/firstaid/adv/empty
empty = 1
+/obj/item/storage/firstaid/machine
+ name = "machine repair kit"
+ desc = "A kit that contains supplies to repair IPCs on the go."
+ icon_state = "machinefirstaid"
+ item_state = "firstaid-machine"
+ med_bot_skin = "machine"
+
+/obj/item/storage/firstaid/machine/New()
+ ..()
+ if(empty)
+ return
+ new /obj/item/weldingtool(src)
+ new /obj/item/stack/cable_coil(src)
+ new /obj/item/stack/cable_coil(src)
+ new /obj/item/stack/cable_coil(src)
+ new /obj/item/reagent_containers/food/drinks/oilcan/full(src)
+ new /obj/item/robotanalyzer(src)
+
+/obj/item/storage/firstaid/machine/empty
+ empty = 1
+
+
/obj/item/storage/firstaid/tactical
name = "first-aid kit"
icon_state = "bezerk"
diff --git a/code/game/objects/items/weapons/storage/lockbox.dm b/code/game/objects/items/weapons/storage/lockbox.dm
index d461700695d..c080715a035 100644
--- a/code/game/objects/items/weapons/storage/lockbox.dm
+++ b/code/game/objects/items/weapons/storage/lockbox.dm
@@ -67,7 +67,7 @@
origin_tech = null //wipe out any origin tech if it's unlocked in any way so you can't double-dip tech levels at R&D.
return
-/obj/item/storage/lockbox/hear_talk(mob/living/M as mob, msg)
+/obj/item/storage/lockbox/hear_talk(mob/living/M as mob, list/message_pieces)
/obj/item/storage/lockbox/hear_message(mob/living/M as mob, msg)
diff --git a/code/game/objects/items/weapons/storage/secure.dm b/code/game/objects/items/weapons/storage/secure.dm
index 0a256136d2e..aeaa7991420 100644
--- a/code/game/objects/items/weapons/storage/secure.dm
+++ b/code/game/objects/items/weapons/storage/secure.dm
@@ -158,7 +158,7 @@
to_chat(usr, "[src] is locked!")
return 0
-/obj/item/storage/secure/hear_talk(mob/living/M as mob, msg)
+/obj/item/storage/secure/hear_talk(mob/living/M as mob, list/message_pieces)
return
/obj/item/storage/secure/hear_message(mob/living/M as mob, msg)
@@ -214,14 +214,6 @@
for(var/i = 0, i < storage_slots - 2, i++)
handle_item_insertion(new /obj/item/stack/spacecash/c1000, 1)
-/obj/item/storage/secure/briefcase/reaper/New()
- ..()
- handle_item_insertion(new /obj/item/gun/energy/kinetic_accelerator/crossbow, 1)
- handle_item_insertion(new /obj/item/gun/projectile/revolver/mateba, 1)
- handle_item_insertion(new /obj/item/ammo_box/a357, 1)
- handle_item_insertion(new /obj/item/grenade/plastic/c4, 1)
-
-
// -----------------------------
// Secure Safe
// -----------------------------
diff --git a/code/game/objects/items/weapons/storage/storage.dm b/code/game/objects/items/weapons/storage/storage.dm
index 2d7c6842547..99590fe1d13 100644
--- a/code/game/objects/items/weapons/storage/storage.dm
+++ b/code/game/objects/items/weapons/storage/storage.dm
@@ -473,10 +473,10 @@
O.emp_act(severity)
..()
-/obj/item/storage/hear_talk(mob/living/M as mob, msg)
+/obj/item/storage/hear_talk(mob/living/M as mob, list/message_pieces)
..()
for(var/obj/O in contents)
- O.hear_talk(M, msg)
+ O.hear_talk(M, message_pieces)
/obj/item/storage/hear_message(mob/living/M as mob, msg)
..()
diff --git a/code/game/objects/items/weapons/storage/uplink_kits.dm b/code/game/objects/items/weapons/storage/uplink_kits.dm
index 3a821264822..8f72b7c9e04 100644
--- a/code/game/objects/items/weapons/storage/uplink_kits.dm
+++ b/code/game/objects/items/weapons/storage/uplink_kits.dm
@@ -215,6 +215,13 @@
new /obj/item/ammo_casing/shotgun/dart/assassination(src)
new /obj/item/gun/projectile/revolver/doublebarrel/improvised/cane(src)
+/obj/item/storage/box/syndie_kit/mimery
+ name = "advanced mimery kit"
+
+/obj/item/storage/box/syndie_kit/mimery/New()
+ ..()
+ new /obj/item/spellbook/oneuse/mime/greaterwall(src)
+ new /obj/item/spellbook/oneuse/mime/fingergun(src)
/obj/item/storage/box/syndie_kit/atmosgasgrenades
name = "Atmos Grenades"
diff --git a/code/game/objects/items/weapons/stunbaton.dm b/code/game/objects/items/weapons/stunbaton.dm
index 231645ed758..f16ca21682f 100644
--- a/code/game/objects/items/weapons/stunbaton.dm
+++ b/code/game/objects/items/weapons/stunbaton.dm
@@ -166,6 +166,8 @@
if(ishuman(L))
var/mob/living/carbon/human/H = L
+ if(check_martial_counter(L, user))
+ return
if(H.check_shields(0, "[user]'s [name]", src, MELEE_ATTACK)) //No message; check_shields() handles that
playsound(L, 'sound/weapons/Genhit.ogg', 50, 1)
return
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index cc2fa8d1d69..f13806b90f1 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -173,7 +173,7 @@
return
-/obj/proc/hear_talk(mob/M as mob, text)
+/obj/proc/hear_talk(mob/M, list/message_pieces)
return
/obj/proc/hear_message(mob/M as mob, text)
diff --git a/code/game/objects/structures/crates_lockers/closets/secure/chaplain.dm b/code/game/objects/structures/crates_lockers/closets/secure/chaplain.dm
index 0a44e818264..5287397a3e9 100644
--- a/code/game/objects/structures/crates_lockers/closets/secure/chaplain.dm
+++ b/code/game/objects/structures/crates_lockers/closets/secure/chaplain.dm
@@ -20,9 +20,10 @@
new /obj/item/clothing/head/witchhunter_hat(src)
new /obj/item/clothing/suit/holidaypriest(src)
new /obj/item/clothing/under/wedding/bride_white(src)
- new /obj/item/storage/backpack/cultpack (src)
+ new /obj/item/storage/backpack/cultpack(src)
new /obj/item/clothing/head/helmet/riot/knight/templar(src)
new /obj/item/clothing/suit/armor/riot/knight/templar(src)
+ new /obj/item/soulstone/anybody/chaplain(src)
new /obj/item/storage/fancy/candle_box/eternal(src)
new /obj/item/storage/fancy/candle_box/eternal(src)
new /obj/item/storage/fancy/candle_box/eternal(src)
diff --git a/code/game/objects/structures/guillotine.dm b/code/game/objects/structures/guillotine.dm
index 3c4875978a2..32b2bfcce40 100644
--- a/code/game/objects/structures/guillotine.dm
+++ b/code/game/objects/structures/guillotine.dm
@@ -194,6 +194,28 @@
return TRUE
else
current_action = 0
+ if(iswelder(W))
+ var/obj/item/weldingtool/WT = W
+ if(!WT.remove_fuel(0, user))
+ return
+ to_chat(user, "You begin cutting [src] apart...")
+ playsound(loc, WT.usesound, 40, 1)
+ if(do_after(user, 150 * WT.toolspeed, 1, target = src))
+ if(!WT.isOn())
+ return
+ playsound(loc, WT.usesound, 50, 1)
+ visible_message("[user] slices apart [src].",
+ "You cut [src] apart with [WT].",
+ "You hear welding.")
+ var/turf/T = get_turf(src)
+ if(blade_sharpness == GUILLOTINE_BLADE_MAX_SHARP)
+ new /obj/item/stack/sheet/plasteel(T, 3)
+ else
+ new /obj/item/stack/sheet/plasteel(T, 2) //prevents reconstructing to sharpen the guillotine without additional plasteel
+ new /obj/item/stack/sheet/wood(T, 20)
+ new /obj/item/stack/cable_coil(T, 10)
+ qdel(src)
+ return
else
return ..()
diff --git a/code/game/objects/structures/safe.dm b/code/game/objects/structures/safe.dm
index a2b438c10d2..8e9b478382a 100644
--- a/code/game/objects/structures/safe.dm
+++ b/code/game/objects/structures/safe.dm
@@ -312,7 +312,7 @@ GLOBAL_LIST_EMPTY(safes)
/obj/structure/safe/Destroy()
GLOB.safes -= src
- drill.soundloop.stop()
+ drill?.soundloop?.stop()
return ..()
/obj/structure/safe/process()
diff --git a/code/game/objects/structures/statues.dm b/code/game/objects/structures/statues.dm
index 14ef2b08f2b..ba015c843fa 100644
--- a/code/game/objects/structures/statues.dm
+++ b/code/game/objects/structures/statues.dm
@@ -172,31 +172,24 @@
if(exposed_temperature > 300)
PlasmaBurn(exposed_temperature)
-/obj/structure/statue/plasma/bullet_act(obj/item/projectile/Proj)
- var/burn = FALSE
- if(Proj.damage == 0)//lasertag guns and so on don't set off plasma anymore. can't use nodamage here because lasertag guns actually don't have it.
- return
- if(istype(Proj,/obj/item/projectile/beam))
- PlasmaBurn(2500)
- burn = TRUE
- else if(istype(Proj,/obj/item/projectile/ion))
- PlasmaBurn(500)
- burn = TRUE
- if(burn)
- if(Proj.firer)
- message_admins("Plasma statue ignited by [key_name_admin(Proj.firer)]([ADMIN_QUE(Proj.firer,"?")]) ([ADMIN_FLW(Proj.firer,"FLW")]) in ([x],[y],[z] - JMP)",0,1)
- log_game("Plasma statue ignited by [key_name(Proj.firer)] in ([x],[y],[z])")
- investigate_log("was ignited by [key_name(Proj.firer)]","atmos")
- else
- message_admins("Plasma statue ignited by [Proj]. No known firer.([ADMIN_QUE(Proj.firer,"?")]) ([ADMIN_FLW(Proj.firer,"FLW")]) in ([x],[y],[z] - JMP)",0,1)
- log_game("Plasma statue ignited by [Proj] in ([x],[y],[z]). No known firer.")
+/obj/structure/statue/plasma/bullet_act(obj/item/projectile/P)
+ if(!QDELETED(src)) //wasn't deleted by the projectile's effects.
+ if(!P.nodamage && ((P.damage_type == BURN) || (P.damage_type == BRUTE)))
+ if(P.firer)
+ message_admins("[key_name_admin(P.firer)] ignited a plasma statue with [P.name] at [COORD(loc)]")
+ log_game("[key_name(P.firer)] ignited a plasma statue with [P.name] at [COORD(loc)]")
+ investigate_log("[key_name(P.firer)] ignited a plasma statue with [P.name] at [COORD(loc)]", "atmos")
+ else
+ message_admins("A plasma statue was ignited with [P.name] at [COORD(loc)]. No known firer.")
+ log_game("A plasma statue was ignited with [P.name] at [COORD(loc)]. No known firer.")
+ PlasmaBurn()
..()
/obj/structure/statue/plasma/attackby(obj/item/W, mob/user, params)
if(is_hot(W) > 300)//If the temperature of the object is over 300, then ignite
- message_admins("Plasma statue ignited by [key_name_admin(user)]([ADMIN_QUE(user,"?")]) ([ADMIN_FLW(user,"FLW")]) in ([x],[y],[z] - JMP)",0,1)
- log_game("Plasma statue ignited by [key_name(user)] in ([x],[y],[z])")
- investigate_log("was ignited by [key_name(user)]","atmos")
+ message_admins("[key_name_admin(user)] ignited a plasma statue at [COORD(loc)]")
+ log_game("[key_name(user)] ignited plasma a statue at [COORD(loc)]")
+ investigate_log("[key_name(user)] ignited a plasma statue at [COORD(loc)]", "atmos")
ignite(is_hot(W))
return
..()
diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm
index e657e9f8538..b48774eb046 100644
--- a/code/game/objects/structures/watercloset.dm
+++ b/code/game/objects/structures/watercloset.dm
@@ -507,10 +507,8 @@
icon = 'icons/obj/watercloset.dmi'
icon_state = "rubberducky"
item_state = "rubberducky"
+ honk_sounds = list('sound/items/squeaktoy.ogg' = 1)
attack_verb = list("quacked", "squeaked")
- honk_sound = 'sound/items/squeaktoy.ogg' //credit to DANMITCH3LL of freesound for this
-
-
/obj/structure/sink
name = "sink"
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index d7ed44c3c9a..7eb81c589e1 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -136,6 +136,7 @@
/turf/Entered(atom/movable/M, atom/OL, ignoreRest = 0)
+ ..()
if(ismob(M))
var/mob/O = M
if(!O.lastarea)
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 841cfb69036..5c6bf7038a1 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -1699,7 +1699,7 @@
if(!istype(H))
to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human")
return
- var/etypes = list("Borgification","Corgification","Death By Fire","Total Brain Death","Honk Tumor","Cluwne","Demotion Notice")
+ var/etypes = list("Borgification", "Corgification", "Death By Fire", "Total Brain Death", "Honk Tumor", "Cluwne", "Demote", "Demote with Bot", "Revoke Fax Access", "Angry Fax Machine")
var/eviltype = input(src.owner, "Which type of evil fax do you wish to send [H]?","Its good to be baaaad...", "") as null|anything in etypes
if(!(eviltype in etypes))
return
@@ -1733,8 +1733,8 @@
P.overlays += stampoverlay
P.stamps += ""
P.update_icon()
- //fax.receivefax(P) // this does not work, it does not preserve the type, we have to physically teleport the fax paper instead
- P.loc = fax.loc
+ P.faxmachineid = fax.UID()
+ P.loc = fax.loc // Do not use fax.receivefax(P) here, as it won't preserve the type. Physically teleporting the fax paper is required.
if(istype(H) && H.stat == CONSCIOUS && (istype(H.l_ear, /obj/item/radio/headset) || istype(H.r_ear, /obj/item/radio/headset)))
to_chat(H, "Your headset pings, notifying you that a reply to your fax has arrived.")
to_chat(src.owner, "You sent a [eviltype] fax to [H]")
@@ -1756,7 +1756,8 @@
btypes += "Super Powers"
btypes += "Scarab Guardian"
btypes += "Human Protector"
- btypes += "Pet"
+ btypes += "Sentient Pet"
+ btypes += "All Access"
var/blessing = input(owner, "How would you like to bless [M]?", "Its good to be good...", "") as null|anything in btypes
if(!(blessing in btypes))
return
@@ -1806,28 +1807,56 @@
spawn(700)
qdel(scarab)
logmsg = "scarab guardian."
- if("Pet")
- var/pets = subtypesof(/mob/living/simple_animal/pet)
+ if("Sentient Pet")
+ var/pets = subtypesof(/mob/living/simple_animal)
var/petchoice = input("Select pet type", "Pets") as null|anything in pets
if(isnull(petchoice))
return
- var/mob/living/simple_animal/pet/P = new petchoice(H.loc)
- var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as [P], pet of [H]?", poll_time = 100, min_hours = 10)
+ var/list/mob/dead/observer/candidates = pollCandidates("Play as the special event pet [H]?", poll_time = 200, min_hours = 10)
var/mob/dead/observer/theghost = null
if(candidates.len)
+ var/mob/living/simple_animal/pet/P = new petchoice(H.loc)
theghost = pick(candidates)
P.key = theghost.key
P.master_commander = H
+ P.universal_speak = 1
+ P.universal_understand = 1
+ P.can_collar = 1
+ P.faction = list("neutral")
+ var/obj/item/clothing/accessory/petcollar/C = new /obj/item/clothing/accessory/petcollar(P)
+ P.collar = C
+ C.equipped(P)
+ var/obj/item/card/id/I = H.wear_id
+ if(I)
+ var/obj/item/card/id/D = new /obj/item/card/id(C)
+ D.access = I.access
+ D.registered_name = P.name
+ D.assignment = "Pet"
+ C.access_id = D
spawn(30)
- var/newname = sanitize(copytext(input(P, "You are [P], pet of [H]. Would you like to change your name to something else?", "Name change", P.name) as null|text,1,MAX_NAME_LEN))
+ var/newname = sanitize(copytext(input(P, "You are [P], special event pet of [H]. Change your name to something else?", "Name change", P.name) as null|text,1,MAX_NAME_LEN))
if(newname && newname != P.name)
P.name = newname
if(P.mind)
P.mind.name = newname
- logmsg = "pet ([P])."
+ logmsg = "pet ([P])."
+ else
+ to_chat(usr, "WARNING: Nobody volunteered to play the special event pet.")
+ logmsg = "pet (no volunteers)."
if("Human Protector")
usr.client.create_eventmob_for(H, 0)
logmsg = "syndie protector."
+ if("All Access")
+ var/obj/item/card/id/I = H.wear_id
+ if(I)
+ var/list/access_to_give = get_all_accesses()
+ for(var/this_access in access_to_give)
+ if(!(this_access in I.access))
+ // don't have it - add it
+ I.access |= this_access
+ else
+ to_chat(usr, "ERROR: [H] is not wearing an ID card.")
+ logmsg = "all access."
if(logmsg)
log_admin("[key_name(owner)] answered [key_name(M)]'s prayer with a blessing: [logmsg]")
message_admins("[key_name_admin(owner)] answered [key_name_admin(M)]'s prayer with a blessing: [logmsg]")
@@ -1850,10 +1879,10 @@
ptypes += "Cluwne"
ptypes += "Mutagen Cookie"
ptypes += "Hellwater Cookie"
- ptypes += "Assassin"
ptypes += "Hunter"
ptypes += "Crew Traitor"
ptypes += "Floor Cluwne"
+ ptypes += "Shamebrero"
var/punishment = input(owner, "How would you like to smite [M]?", "Its good to be baaaad...", "") as null|anything in ptypes
if(!(punishment in ptypes))
return
@@ -1866,15 +1895,19 @@
M.Weaken(5)
to_chat(M, "The gods have punished you for your sins!")
logmsg = "a lightning bolt."
- if("Brain Damage")
- H.adjustBrainLoss(75)
- logmsg = "75 brain damage."
if("Fire Death")
to_chat(M,"You feel hotter than usual. Maybe you should lowe-wait, is that your hand melting?")
var/turf/simulated/T = get_turf(M)
new /obj/effect/hotspot(T)
M.adjustFireLoss(150)
logmsg = "a firey death."
+ if("Gib")
+ M.gib(FALSE)
+ logmsg = "gibbed."
+
+ if("Brain Damage")
+ H.adjustBrainLoss(75)
+ logmsg = "75 brain damage."
if("Honk Tumor")
if(!H.get_int_organ(/obj/item/organ/internal/honktumor))
var/obj/item/organ/internal/organ = new /obj/item/organ/internal/honktumor
@@ -1884,14 +1917,16 @@
if("Hallucinate")
H.Hallucinate(1000)
logmsg = "hallucinations."
- if("Hunger")
- H.nutrition = NUTRITION_LEVEL_CURSED
- logmsg = "starvation."
if("Cold")
H.reagents.add_reagent("frostoil", 40)
H.reagents.add_reagent("ice", 40)
+ logmsg = "cold."
+ if("Hunger")
+ H.nutrition = NUTRITION_LEVEL_CURSED
+ logmsg = "starvation."
if("Cluwne")
H.makeCluwne()
+ H.mutations |= NOCLONE
logmsg = "cluwned."
if("Mutagen Cookie")
var/obj/item/reagent_containers/food/snacks/cookie/evilcookie = new /obj/item/reagent_containers/food/snacks/cookie
@@ -1912,11 +1947,10 @@
H.equip_to_slot_or_del(evilcookie, slot_l_hand)
logmsg = "a hellwater cookie."
if("Hunter")
- logmsg = "hunter."
H.mutations |= NOCLONE
usr.client.create_eventmob_for(H, 1)
+ logmsg = "hunter."
if("Crew Traitor")
- logmsg = "crew traitor."
var/list/possible_traitors = list()
for(var/mob/living/player in GLOB.living_mob_list)
if(player.client && player.mind && !player.mind.special_role && player.stat != DEAD && player != H)
@@ -1947,29 +1981,22 @@
else
to_chat(usr, "ERROR: Failed to create a traitor.")
return
- if("Lynch")
- logmsg = "lynch."
- for(var/datum/mind/crew in ticker.minds)
- if(!crew.current)
- continue
- if(!isliving(crew.current))
- continue
- if(crew == H.mind)
- continue
- to_chat(crew.current, " The gods have given you a task: find [H.real_name], located in [get_area(H.loc)], and slay them!");
- to_chat(crew.current, "Do not harm anyone other than [H.real_name] while carrying out this task. ");
- if("Gib")
- logmsg = "gibbed."
- M.gib(FALSE)
+ logmsg = "crew traitor."
if("Floor Cluwne")
- logmsg = "floor cluwne"
var/turf/T = get_turf(M)
var/mob/living/simple_animal/hostile/floor_cluwne/FC = new /mob/living/simple_animal/hostile/floor_cluwne(T)
FC.smiting = TRUE
FC.Acquire_Victim(M)
+ logmsg = "floor cluwne"
+ if("Shamebrero")
+ if(H.head)
+ H.unEquip(H.head, TRUE)
+ var/obj/item/clothing/head/sombrero/shamebrero/S = new(H.loc)
+ H.equip_to_slot_or_del(S, slot_head)
+ logmsg = "shamebrero"
if(logmsg)
- log_admin("[key_name(owner)] answered [key_name(M)]'s prayer with a smiting: [logmsg]")
- message_admins("[key_name_admin(owner)] answered [key_name_admin(M)]'s prayer with a smiting: [logmsg]")
+ log_admin("[key_name(owner)] smited [key_name(M)] with: [logmsg]")
+ message_admins("[key_name_admin(owner)] smited [key_name_admin(M)] with: [logmsg]")
else if(href_list["cryossd"])
if(!check_rights(R_ADMIN))
return
diff --git a/code/modules/admin/verbs/pray.dm b/code/modules/admin/verbs/pray.dm
index b92b304fd57..1c75421f4b7 100644
--- a/code/modules/admin/verbs/pray.dm
+++ b/code/modules/admin/verbs/pray.dm
@@ -3,7 +3,8 @@
set name = "Pray"
msg = sanitize(copytext(msg, 1, MAX_MESSAGE_LEN))
- if(!msg) return
+ if(!msg)
+ return
if(usr.client)
if(usr.client.prefs.muted & MUTE_PRAY)
@@ -29,7 +30,7 @@
deity = ticker.cultdat.entity_name
log_say("(PRAYER) [msg]", usr)
- msg = "[bicon(cross)][prayer_type][deity ? " (to [deity])" : ""]:[key_name(src, 1)] ([ADMIN_QUE(src,"?")]) ([ADMIN_PP(src,"PP")]) ([ADMIN_VV(src,"VV")]) ([ADMIN_SM(src,"SM")]) ([admin_jump_link(src)]) (CA) ([ADMIN_SC(src,"SC")]) (BLESS) (SMITE): [msg]"
+ msg = "[bicon(cross)][prayer_type][deity ? " (to [deity])" : ""][mind && mind.isholy ? " (blessings: [mind.num_blessed])" : ""]: [key_name(src, 1)] ([ADMIN_QUE(src,"?")]) ([ADMIN_PP(src,"PP")]) ([ADMIN_VV(src,"VV")]) ([ADMIN_SM(src,"SM")]) ([admin_jump_link(src)]) (CA) ([ADMIN_SC(src,"SC")]) (BLESS) (SMITE): [msg]"
for(var/client/X in GLOB.admins)
if(check_rights(R_EVENT,0,X.mob))
diff --git a/code/modules/arcade/prize_datums.dm b/code/modules/arcade/prize_datums.dm
index 7d8e90068c6..51356569af9 100644
--- a/code/modules/arcade/prize_datums.dm
+++ b/code/modules/arcade/prize_datums.dm
@@ -222,6 +222,12 @@ var/global/datum/prizes/global_prizes = new
typepath = /obj/item/spellbook/oneuse/fake_gib
cost = 100
+/datum/prize_item/fakefingergun
+ name = "Miming Manual : Finger Gun"
+ desc = "..."
+ typepath = /obj/item/spellbook/oneuse/mime/fingergun/fake
+ cost = 100
+
/datum/prize_item/magic_conch
name = "Magic Conch Shell"
desc = "All hail the magic conch!"
diff --git a/code/modules/assembly/bomb.dm b/code/modules/assembly/bomb.dm
index 25f9f6fe285..2c5a2d28883 100644
--- a/code/modules/assembly/bomb.dm
+++ b/code/modules/assembly/bomb.dm
@@ -85,9 +85,9 @@
if(bombassembly)
bombassembly.on_found(finder)
-/obj/item/onetankbomb/hear_talk(mob/living/M, msg)
+/obj/item/onetankbomb/hear_talk(mob/living/M, list/message_pieces)
if(bombassembly)
- bombassembly.hear_talk(M, msg)
+ bombassembly.hear_talk(M, message_pieces)
/obj/item/onetankbomb/hear_message(mob/living/M, msg)
if(bombassembly)
diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm
index 19683743b3e..bc36a0543e8 100644
--- a/code/modules/assembly/holder.dm
+++ b/code/modules/assembly/holder.dm
@@ -98,11 +98,11 @@
a_right.on_found(finder)
-/obj/item/assembly_holder/hear_talk(mob/living/M, msg)
+/obj/item/assembly_holder/hear_talk(mob/living/M, list/message_pieces)
if(a_left)
- a_left.hear_talk(M, msg)
+ a_left.hear_talk(M, message_pieces)
if(a_right)
- a_right.hear_talk(M, msg)
+ a_right.hear_talk(M, message_pieces)
/obj/item/assembly_holder/hear_message(mob/living/M, msg)
if(a_left)
diff --git a/code/modules/assembly/voice.dm b/code/modules/assembly/voice.dm
index e8462884cd4..3c7fdfa8fef 100644
--- a/code/modules/assembly/voice.dm
+++ b/code/modules/assembly/voice.dm
@@ -16,11 +16,11 @@
else
return "[src] is deactivated."
-/obj/item/assembly/voice/hear_talk(mob/living/M as mob, msg)
- hear_input(M, msg, 0)
+/obj/item/assembly/voice/hear_talk(mob/living/M as mob, list/message_pieces)
+ hear_input(M, multilingual_to_message(message_pieces), 0)
/obj/item/assembly/voice/hear_message(mob/living/M as mob, msg)
- hear_input(M, msg, 1)
+ hear_input(M, msg, 1)
/obj/item/assembly/voice/proc/hear_input(mob/living/M as mob, msg, type)
if(!istype(M,/mob/living))
@@ -68,7 +68,7 @@
/obj/item/assembly/voice/noise/describe()
return "[src] does not appear to have any controls."
-/obj/item/assembly/voice/noise/hear_talk(mob/living/M as mob, msg)
+/obj/item/assembly/voice/noise/hear_talk(mob/living/M as mob, list/message_pieces)
return
/obj/item/assembly/voice/noise/hear_message(mob/living/M as mob, msg)
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index bc6f7259ebc..855c092f3ae 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -411,6 +411,7 @@ BLIND // can't see anything
..()
/obj/item/clothing/shoes/proc/step_action(var/mob/living/carbon/human/H) //squeek squeek
+ SEND_SIGNAL(src, COMSIG_SHOES_STEP_ACTION)
if(shoe_sound)
var/turf/T = get_turf(H)
@@ -584,6 +585,10 @@ BLIND // can't see anything
var/rolled_down = 0
var/basecolor
+/obj/item/clothing/under/rank/New()
+ sensor_mode = pick(0,1,2,3)
+ ..()
+
/obj/item/clothing/under/Destroy()
QDEL_LIST(accessories)
return ..()
@@ -687,34 +692,47 @@ BLIND // can't see anything
else
to_chat(usr, "You cannot roll down the uniform!")
-/obj/item/clothing/under/proc/remove_accessory(mob/user, obj/item/clothing/accessory/A)
- if(!(A in accessories))
- return
-
- A.on_removed(user)
- accessories -= A
- usr.update_inv_w_uniform()
-
/obj/item/clothing/under/verb/removetie()
set name = "Remove Accessory"
set category = "Object"
set src in usr
- if(!istype(usr, /mob/living)) return
- if(usr.stat) return
- if(!accessories.len) return
+ handle_accessories_removal()
+
+/obj/item/clothing/under/proc/handle_accessories_removal()
+ if(!isliving(usr))
+ return
+ if(usr.incapacitated())
+ return
+ if(!Adjacent(usr))
+ return
+ if(!accessories.len)
+ return
var/obj/item/clothing/accessory/A
if(accessories.len > 1)
A = input("Select an accessory to remove from [src]") as null|anything in accessories
else
A = accessories[1]
- src.remove_accessory(usr,A)
+ remove_accessory(usr,A)
-/obj/item/clothing/under/rank/New()
- sensor_mode = pick(0,1,2,3)
- ..()
+/obj/item/clothing/under/proc/remove_accessory(mob/user, obj/item/clothing/accessory/A)
+ if(!(A in accessories))
+ return
+ if(!isliving(user))
+ return
+ if(user.incapacitated())
+ return
+ if(!Adjacent(user))
+ return
+ A.on_removed(user)
+ accessories -= A
+ to_chat(user, "You remove [A] from [src].")
+ usr.update_inv_w_uniform()
/obj/item/clothing/under/emp_act(severity)
if(accessories.len)
for(var/obj/item/clothing/accessory/A in accessories)
A.emp_act(severity)
..()
+
+/obj/item/clothing/under/AltClick()
+ handle_accessories_removal()
diff --git a/code/modules/clothing/masks/miscellaneous.dm b/code/modules/clothing/masks/miscellaneous.dm
index c10ccf32f89..789215a0f19 100644
--- a/code/modules/clothing/masks/miscellaneous.dm
+++ b/code/modules/clothing/masks/miscellaneous.dm
@@ -80,12 +80,6 @@
to_chat(usr, "You lack the ability to manipulate the lock.")
-/obj/item/clothing/mask/muzzle/gag
- name = "gag"
- desc = "Stick this in their mouth to stop the noise."
- icon_state = "gag"
- w_class = WEIGHT_CLASS_TINY
-
/obj/item/clothing/mask/muzzle/tapegag
name = "tape gag"
desc = "MHPMHHH!"
@@ -182,9 +176,9 @@
trigger.HasProximity(AM)
-/obj/item/clothing/mask/muzzle/safety/shock/hear_talk(mob/living/M as mob, msg)
+/obj/item/clothing/mask/muzzle/safety/shock/hear_talk(mob/living/M as mob, list/message_pieces)
if(trigger)
- trigger.hear_talk(M, msg)
+ trigger.hear_talk(M, message_pieces)
/obj/item/clothing/mask/muzzle/safety/shock/hear_message(mob/living/M as mob, msg)
if(trigger)
diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm
index 84b62bfd85c..304a7332bf6 100644
--- a/code/modules/clothing/shoes/miscellaneous.dm
+++ b/code/modules/clothing/shoes/miscellaneous.dm
@@ -10,6 +10,12 @@
var/list/clothing_choices = list()
silence_steps = 1
+/obj/item/clothing/shoes/syndigaloshes/black
+ name = "black shoes"
+ icon_state = "black"
+ item_color = "black"
+ desc = "A pair of black shoes. They seem to have extra grip."
+
/obj/item/clothing/shoes/mime
name = "mime shoes"
icon_state = "mime"
diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm
index dea27a5b946..ba61c78b368 100644
--- a/code/modules/clothing/spacesuits/hardsuit.dm
+++ b/code/modules/clothing/spacesuits/hardsuit.dm
@@ -69,6 +69,7 @@
armor = list(melee = 10, bullet = 5, laser = 10, energy = 5, bomb = 10, bio = 100, rad = 75)
allowed = list(/obj/item/flashlight,/obj/item/tank,/obj/item/t_scanner, /obj/item/rcd)
siemens_coefficient = 0
+ actions_types = list(/datum/action/item_action/toggle_helmet)
hide_tail_by_species = list("Vox" , "Vulpkanin" , "Unathi" , "Tajaran")
species_restricted = list("exclude","Diona","Wryn")
@@ -156,6 +157,10 @@
H.unEquip(boots)
boots.forceMove(src)
+/obj/item/clothing/suit/space/hardsuit/ui_action_click()
+ ..()
+ toggle_helmet()
+
/obj/item/clothing/suit/space/hardsuit/verb/toggle_helmet()
set name = "Toggle Helmet"
set category = "Object"
@@ -201,6 +206,9 @@
else
to_chat(user, "You detach \the [helmet] from \the [src]'s helmet mount.")
helmet.loc = get_turf(src)
+ if(istype(helmet,/obj/item/clothing/head/helmet/space/hardsuit/syndi))
+ var/obj/item/clothing/head/helmet/space/hardsuit/syndi/S = helmet
+ S.linkedsuit = null
src.helmet = null
return
if(!boots)
@@ -221,7 +229,12 @@
to_chat(user, "You attach \the [W] to \the [src]'s helmet mount.")
user.drop_item()
W.loc = src
- src.helmet = W
+ helmet = W
+ if(istype(helmet,/obj/item/clothing/head/helmet/space/hardsuit/syndi))
+ var/obj/item/clothing/head/helmet/space/hardsuit/syndi/S = W
+ S.forceMove(src)
+ helmet = S
+ S.link_suit()
return
else if(istype(W,/obj/item/clothing/shoes/magboots) && can_modify(user))
@@ -312,6 +325,7 @@
item_color = "syndi"
armor = list(melee = 40, bullet = 50, laser = 30, energy = 15, bomb = 35, bio = 100, rad = 50)
on = 1
+ var/obj/item/clothing/suit/space/hardsuit/syndi/linkedsuit = null
actions_types = list(/datum/action/item_action/toggle_helmet_mode)
flags = BLOCKHAIR | STOPSPRESSUREDMAGE | THICKMATERIAL
visor_flags_inv = HIDEMASK|HIDEEYES|HIDEFACE|HIDETAIL
@@ -319,7 +333,17 @@
/obj/item/clothing/head/helmet/space/hardsuit/syndi/update_icon()
icon_state = "hardsuit[on]-[item_color]"
+/obj/item/clothing/head/helmet/space/hardsuit/syndi/proc/link_suit()
+ . = ..()
+ if(istype(loc,/obj/item/clothing/suit/space/hardsuit/syndi))
+ linkedsuit = loc
+
/obj/item/clothing/head/helmet/space/hardsuit/syndi/attack_self(mob/user)
+
+ if(!linkedsuit)
+ to_chat(user, "You must attach the helmet to a syndicate hardsuit to toggle combat mode!")
+ return
+
on = !on
if(on)
to_chat(user, "You switch your helmet to travel mode. It will allow you to stand in zero pressure environments, at the cost of speed.")
@@ -342,12 +366,38 @@
update_icon()
playsound(src.loc, 'sound/mecha/mechmove03.ogg', 50, 1)
+ toggle_hardsuit_mode(user)
user.update_inv_head()
for(var/X in actions)
var/datum/action/A = X
A.UpdateButtonIcon()
+/obj/item/clothing/head/helmet/space/hardsuit/syndi/proc/toggle_hardsuit_mode(mob/user) //Helmet Toggles Suit Mode
+ if(linkedsuit)
+ if(on)
+ linkedsuit.name = initial(linkedsuit.name)
+ linkedsuit.desc = initial(linkedsuit.desc)
+ linkedsuit.slowdown = 1
+ linkedsuit.flags |= STOPSPRESSUREDMAGE | THICKMATERIAL
+ linkedsuit.flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL
+ linkedsuit.cold_protection |= UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS
+ else
+ linkedsuit.name += " (combat)"
+ linkedsuit.desc = linkedsuit.alt_desc
+ linkedsuit.slowdown = 0
+ linkedsuit.flags = THICKMATERIAL
+ linkedsuit.cold_protection &= ~(UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS)
+ linkedsuit.flags_inv &= ~(HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL)
+
+ linkedsuit.update_icon()
+ user.update_inv_wear_suit()
+ user.update_inv_w_uniform()
+
+ for(var/X in actions)
+ var/datum/action/A = X
+ A.UpdateButtonIcon()
+
/obj/item/clothing/head/helmet/space/hardsuit/syndi/freedom
name = "eagle helmet"
desc = "An advanced, space-proof helmet. It appears to be modeled after an old-world eagle."
@@ -360,6 +410,7 @@
/obj/item/clothing/suit/space/hardsuit/syndi
name = "blood-red hardsuit"
desc = "A dual-mode advanced hardsuit designed for work in special operations. It is in travel mode. Property of Gorlex Marauders."
+ alt_desc = "A dual-mode advanced hardsuit designed for work in special operations. It is in combat mode. Property of Gorlex Marauders."
icon_state = "hardsuit1-syndi"
item_state = "syndie_hardsuit"
item_color = "syndi"
@@ -372,34 +423,6 @@
/obj/item/clothing/suit/space/hardsuit/syndi/update_icon()
icon_state = "hardsuit[on]-[item_color]"
-/obj/item/clothing/suit/space/hardsuit/syndi/attack_self(mob/user)
- on = !on
- if(on)
- to_chat(user, "You switch your hardsuit to travel mode. It will allow you to stand in zero pressure environments, at the cost of speed.")
- name = "blood-red hardsuit"
- desc = "A dual-mode advanced hardsuit designed for work in special operations. It is in travel mode. Property of Gorlex Marauders."
- slowdown = 1
- flags = STOPSPRESSUREDMAGE | THICKMATERIAL
- flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL
- cold_protection = UPPER_TORSO | LOWER_TORSO | LEGS | FEET | ARMS | HANDS
- else
- to_chat(user, "You switch your hardsuit to combat mode. You will take damage in zero pressure environments, but you are more suited for a fight.")
- name = "blood-red hardsuit (combat)"
- desc = "A dual-mode advanced hardsuit designed for work in special operations. It is in combat mode. Property of Gorlex Marauders."
- slowdown = 0
- flags = THICKMATERIAL
- flags_inv = null
- cold_protection = null
-
- update_icon()
- playsound(src.loc, 'sound/mecha/mechmove03.ogg', 50, 1)
- user.update_inv_wear_suit()
- user.update_inv_w_uniform()
-
- for(var/X in actions)
- var/datum/action/A = X
- A.UpdateButtonIcon()
-
//Elite Syndie suit
/obj/item/clothing/head/helmet/space/hardsuit/syndi/elite
name = "elite syndicate hardsuit helmet"
diff --git a/code/modules/clothing/suits/storage.dm b/code/modules/clothing/suits/storage.dm
index f64ae7ca6c0..0c77183b578 100644
--- a/code/modules/clothing/suits/storage.dm
+++ b/code/modules/clothing/suits/storage.dm
@@ -28,8 +28,8 @@
pockets.emp_act(severity)
..()
-/obj/item/clothing/suit/storage/hear_talk(mob/M, var/msg)
- pockets.hear_talk(M, msg)
+/obj/item/clothing/suit/storage/hear_talk(mob/M, list/message_pieces)
+ pockets.hear_talk(M, message_pieces)
..()
/obj/item/clothing/suit/storage/hear_message(mob/M, var/msg)
diff --git a/code/modules/clothing/under/accessories/storage.dm b/code/modules/clothing/under/accessories/storage.dm
index ae771a0dffd..3b2e9c75a20 100644
--- a/code/modules/clothing/under/accessories/storage.dm
+++ b/code/modules/clothing/under/accessories/storage.dm
@@ -40,8 +40,8 @@
hold.emp_act(severity)
..()
-/obj/item/clothing/accessory/storage/hear_talk(mob/M, var/msg, verb, datum/language/speaking)
- hold.hear_talk(M, msg, verb, speaking)
+/obj/item/clothing/accessory/storage/hear_talk(mob/M, list/message_pieces, verb)
+ hold.hear_talk(M, message_pieces, verb)
..()
/obj/item/clothing/accessory/storage/hear_message(mob/M, var/msg, verb, datum/language/speaking)
diff --git a/code/modules/clothing/under/chameleon.dm b/code/modules/clothing/under/chameleon.dm
index 8828f7a4c2e..b320237996e 100644
--- a/code/modules/clothing/under/chameleon.dm
+++ b/code/modules/clothing/under/chameleon.dm
@@ -12,11 +12,12 @@
New()
..()
- for(var/U in subtypesof(/obj/item/clothing/under/color))
+ var/blocked = list(/obj/item/clothing/under/color/random, /obj/item/clothing/under/rank/centcom) // Stops random coloured jumpsuit and undefined centcomm suit appearing in the list.
+ for(var/U in subtypesof(/obj/item/clothing/under/color) - blocked)
var/obj/item/clothing/under/V = new U
src.clothing_choices += V
- for(var/U in subtypesof(/obj/item/clothing/under/rank))
+ for(var/U in subtypesof(/obj/item/clothing/under/rank) - blocked)
var/obj/item/clothing/under/V = new U
src.clothing_choices += V
return
diff --git a/code/modules/clothing/under/jobs/civilian.dm b/code/modules/clothing/under/jobs/civilian.dm
index 76bef3e056b..62ced953ae2 100644
--- a/code/modules/clothing/under/jobs/civilian.dm
+++ b/code/modules/clothing/under/jobs/civilian.dm
@@ -69,15 +69,17 @@
item_state = "clown"
item_color = "clown"
flags_size = ONESIZEFITSALL
- var/honk_sound = 'sound/items/bikehorn.ogg'
+
+/obj/item/clothing/under/rank/clown/Initialize()
+ . = ..()
+ AddComponent(/datum/component/squeak, list('sound/items/bikehorn.ogg' = 1), 50)
/obj/item/clothing/under/rank/clown/hit_reaction()
- playsound(loc, honk_sound, 50, 1, -1)
if(ishuman(loc))
var/mob/living/carbon/human/H = loc
if(H.mind && H.mind.assigned_role == "Clown")
score_clownabuse++
- return 0
+ return ..()
/obj/item/clothing/under/rank/head_of_personnel
desc = "It's a jumpsuit worn by someone who works in the position of \"Head of Personnel\"."
diff --git a/code/modules/clothing/under/pants.dm b/code/modules/clothing/under/pants.dm
index e39691d857f..8fd4e55f57e 100644
--- a/code/modules/clothing/under/pants.dm
+++ b/code/modules/clothing/under/pants.dm
@@ -79,11 +79,4 @@
name = "camo pants"
desc = "A pair of woodland camouflage pants. Probably not the best choice for a space station."
icon_state = "camopants"
- item_color = "camopants"
-
-/obj/item/clothing/under/pants/chaps
- name = "black leather assless chaps"
- desc = "For those brave enough to weather the breeze."
- icon_state = "chaps"
- item_color = "chaps"
- flags_size = ONESIZEFITSALL
\ No newline at end of file
+ item_color = "camopants"
\ No newline at end of file
diff --git a/code/modules/customitems/item_defines.dm b/code/modules/customitems/item_defines.dm
index ada24f85259..127656ece55 100644
--- a/code/modules/customitems/item_defines.dm
+++ b/code/modules/customitems/item_defines.dm
@@ -1490,9 +1490,9 @@
icon = 'icons/obj/custom_items.dmi'
lefthand_file = 'icons/mob/inhands/fluff_lefthand.dmi'
righthand_file = 'icons/mob/inhands/fluff_righthand.dmi'
+ honk_sounds = list('sound/items/teri_horn.ogg' = 1)
icon_state = "teri_horn"
item_state = "teri_horn"
- honk_sound = 'sound/items/teri_horn.ogg'
/obj/item/clothing/accessory/medal/fluff/elo //V-Force_Bomber: E.L.O.
name = "distinguished medal of loyalty and excellence"
diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm
index f9f147e9bf8..5273bcb768b 100644
--- a/code/modules/flufftext/Hallucination.dm
+++ b/code/modules/flufftext/Hallucination.dm
@@ -728,7 +728,7 @@ var/list/non_fakeattack_weapons = list(/obj/item/gun/projectile, /obj/item/ammo_
people += H
if(person) //Basic talk
var/image/speech_overlay = image('icons/mob/talk.dmi', person, "h0", layer = ABOVE_MOB_LAYER)
- target.hear_say(pick(speak_messages),language = pick(person.languages),speaker = person)
+ target.hear_say(message_to_multilingual(pick(speak_messages), pick(person.languages)), speaker = person)
if(target.client)
target.client.images |= speech_overlay
sleep(30)
@@ -738,7 +738,7 @@ var/list/non_fakeattack_weapons = list(/obj/item/gun/projectile, /obj/item/ammo_
for(var/mob/living/carbon/human/H in GLOB.living_mob_list)
humans += H
person = pick(humans)
- target.hear_radio(pick(radio_messages),language = pick(person.languages),speaker = person, part_a = "\[[get_frequency_name(PUB_FREQ)]\]", part_b = "")
+ target.hear_radio(message_to_multilingual(pick(radio_messages), pick(person.languages)), speaker = person, part_a = "\[[get_frequency_name(PUB_FREQ)]\]", part_b = "")
qdel(src)
/obj/effect/hallucination/message
@@ -754,7 +754,7 @@ var/list/non_fakeattack_weapons = list(/obj/item/gun/projectile, /obj/item/ammo_
"You feel a tiny prick!",
"[target] sneezes.",
"You feel faint.",
- "You hear a strange, alien voice in your head...[pick("Hiss","Ssss")]",
+ "You hear a strange, alien voice in your head... [pick("Hiss","Ssss")]",
"You can see...everything!")
to_chat(target, chosen)
qdel(src)
diff --git a/code/modules/food_and_drinks/drinks/drinks.dm b/code/modules/food_and_drinks/drinks/drinks.dm
index c481df1ca42..b72ddea8be1 100644
--- a/code/modules/food_and_drinks/drinks/drinks.dm
+++ b/code/modules/food_and_drinks/drinks/drinks.dm
@@ -392,4 +392,7 @@
desc = "Contains oil intended for use on cyborgs, robots, and other synthetics."
icon = 'icons/goonstation/objects/oil.dmi'
icon_state = "oilcan"
- volume = 100
\ No newline at end of file
+ volume = 100
+
+/obj/item/reagent_containers/food/drinks/oilcan/full
+ list_reagents = list("oil" = 100)
diff --git a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm
index bd80823addd..c5bcd22a885 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/gibber.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/gibber.dm
@@ -75,8 +75,7 @@
to_chat(user, "Wait for [occupant.name] to finish being loaded!")
return
- else
- startgibbing(user)
+ startgibbing(user)
/obj/machinery/gibber/attackby(obj/item/P, mob/user, params)
if(istype(P, /obj/item/grab))
@@ -113,7 +112,7 @@
move_into_gibber(user,target)
-/obj/machinery/gibber/proc/move_into_gibber(var/mob/user,var/mob/living/victim)
+/obj/machinery/gibber/proc/move_into_gibber(mob/user, mob/living/victim)
if(occupant)
to_chat(user, "The [src] is full, empty it first!")
return
@@ -215,7 +214,7 @@
qdel(holder2) //get rid of holder object
locked = 0 //unlock
-/obj/machinery/gibber/proc/startgibbing(var/mob/user, var/UserOverride=0)
+/obj/machinery/gibber/proc/startgibbing(mob/user, UserOverride=0)
if(!istype(user) && !UserOverride)
log_debug("Some shit just went down with the gibber at X[x], Y[y], Z[z] with an invalid user. (JMP)")
return
@@ -249,11 +248,11 @@
for(var/i=1 to slab_count)
var/obj/item/reagent_containers/food/snacks/meat/new_meat = new slab_type(src)
new_meat.name = "[slab_name] [new_meat.name]"
- new_meat.reagents.add_reagent("nutriment",slab_nutrition)
+ new_meat.reagents.add_reagent("nutriment", slab_nutrition)
if(occupant.reagents)
- occupant.reagents.trans_to(new_meat, round(occupant.reagents.total_volume/slab_count,1))
+ occupant.reagents.trans_to(new_meat, round(occupant.reagents.total_volume/slab_count, 1))
if(ishuman(occupant))
var/mob/living/carbon/human/H = occupant
@@ -279,11 +278,10 @@
occupant.death(1)
occupant.ghostize()
- qdel(occupant)
+ QDEL_NULL(occupant)
spawn(gibtime)
playsound(get_turf(src), 'sound/effects/splat.ogg', 50, 1)
- operating = 0
if(stealthmode)
for(var/atom/movable/AM in contents)
@@ -292,12 +290,12 @@
else
for(var/obj/item/thing in contents) //Meat is spawned inside the gibber and thrown out afterwards.
thing.loc = get_turf(thing) // Drop it onto the turf for throwing.
- thing.throw_at(get_edge_target_turf(src,gib_throw_dir),rand(1,5),15) // Being pelted with bits of meat and bone would hurt.
+ thing.throw_at(get_edge_target_turf(src, gib_throw_dir), rand(1, 5), 15) // Being pelted with bits of meat and bone would hurt.
sleep(1)
for(var/obj/effect/gibs in contents) //throw out the gibs too
gibs.loc = get_turf(gibs) //drop onto turf for throwing
- gibs.throw_at(get_edge_target_turf(src,gib_throw_dir),rand(1,5),15)
+ gibs.throw_at(get_edge_target_turf(src, gib_throw_dir), rand(1, 5), 15)
sleep(1)
pixel_x = initial(pixel_x) //return to it's spot after shaking
@@ -358,7 +356,7 @@
break
victim_targets.Cut()
-/obj/machinery/gibber/autogibber/proc/force_move_into_gibber(var/mob/living/carbon/human/victim)
+/obj/machinery/gibber/autogibber/proc/force_move_into_gibber(mob/living/carbon/human/victim)
if(!istype(victim)) return 0
visible_message("\The [victim.name] gets sucked into \the [src]!")
@@ -385,7 +383,7 @@
qdel(O) //they are already dead by now
H.unEquip(O)
O.loc = loc
- O.throw_at(get_edge_target_turf(src,gib_throw_dir),rand(1,5),15)
+ O.throw_at(get_edge_target_turf(src, gib_throw_dir), rand(1, 5), 15)
sleep(1)
for(var/obj/item/clothing/C in H)
@@ -393,7 +391,7 @@
qdel(C)
H.unEquip(C)
C.loc = loc
- C.throw_at(get_edge_target_turf(src,gib_throw_dir),rand(1,5),15)
+ C.throw_at(get_edge_target_turf(src, gib_throw_dir), rand(1, 5), 15)
sleep(1)
visible_message("\The [src] spits out \the [H.name]'s possessions!")
@@ -405,7 +403,7 @@
qdel(O)
else if(istype(O))
O.loc = loc
- O.throw_at(get_edge_target_turf(src,gib_throw_dir),rand(1,5),15)
+ O.throw_at(get_edge_target_turf(src, gib_throw_dir), rand(1, 5), 15)
spats++
sleep(1)
if(spats)
diff --git a/code/modules/martial_arts/cqc.dm b/code/modules/martial_arts/cqc.dm
new file mode 100644
index 00000000000..54f9ac97cd9
--- /dev/null
+++ b/code/modules/martial_arts/cqc.dm
@@ -0,0 +1,204 @@
+#define SLAM_COMBO "HG"
+#define KICK_COMBO "HH"
+#define RESTRAIN_COMBO "GG"
+#define PRESSURE_COMBO "DG"
+#define CONSECUTIVE_COMBO "DDH"
+
+/datum/martial_art/cqc
+ name = "CQC"
+ help_verb = /mob/living/carbon/human/proc/CQC_help
+ block_chance = 75
+
+/datum/martial_art/cqc/can_use(mob/living/carbon/human/H)
+ if(istype(H.gloves, /obj/item/clothing/gloves/fingerless/rapid))
+ return FALSE
+ return ..()
+
+/datum/martial_art/cqc/proc/drop_restraining()
+ restraining = FALSE
+
+/datum/martial_art/cqc/proc/check_streak(mob/living/carbon/human/A, mob/living/carbon/human/D)
+ if(!can_use(A))
+ return FALSE
+ if(findtext(streak, SLAM_COMBO))
+ streak = ""
+ Slam(A, D)
+ return TRUE
+ if(findtext(streak, KICK_COMBO))
+ streak = ""
+ Kick(A, D)
+ return TRUE
+ if(findtext(streak, RESTRAIN_COMBO))
+ streak = ""
+ Restrain(A, D)
+ return TRUE
+ if(findtext(streak, PRESSURE_COMBO))
+ streak = ""
+ Pressure(A, D)
+ return TRUE
+ if(findtext(streak, CONSECUTIVE_COMBO))
+ streak = ""
+ Consecutive(A, D)
+ return FALSE
+
+/datum/martial_art/cqc/proc/Slam(mob/living/carbon/human/A, mob/living/carbon/human/D)
+ if(!can_use(A))
+ return FALSE
+ if(!D.stat || !D.weakened)
+ D.visible_message("[A] slams [D] into the ground!", \
+ "[A] slams you into the ground!")
+ playsound(get_turf(A), 'sound/weapons/slam.ogg', 50, 1, -1)
+ D.apply_damage(10, BRUTE)
+ D.Weaken(6)
+ add_attack_logs(A, D, "Melee attacked with martial-art [src] : Slam", ATKLOG_ALL)
+ else //if target can't be slammed, do a regular grab attack then clear the streak
+ streak = ""
+ grab_act(A, D)
+ streak = ""
+ return TRUE
+
+/datum/martial_art/cqc/proc/Kick(mob/living/carbon/human/A, mob/living/carbon/human/D)
+ if(!can_use(A))
+ return FALSE
+ if(!D.stat || !D.weakened)
+ D.visible_message("[A] kicks [D] back!", \
+ "[A] kicks you back!")
+ playsound(get_turf(A), 'sound/weapons/cqchit1.ogg', 50, 1, -1)
+ var/atom/throw_target = get_edge_target_turf(D, A.dir)
+ D.throw_at(throw_target, 1, 14, A)
+ D.apply_damage(10, BRUTE)
+ add_attack_logs(A, D, "Melee attacked with martial-art [src] : Kick", ATKLOG_ALL)
+ if(D.weakened && !D.stat)
+ D.visible_message("[A] kicks [D]'s head, knocking [D.p_them()] out!", \
+ "[A] kicks your head, knocking you out!")
+ playsound(get_turf(A), 'sound/weapons/genhit1.ogg', 50, 1, -1)
+ D.SetSleeping(15)
+ D.adjustBrainLoss(15, 150)
+ add_attack_logs(A, D, "Knocked out with martial-art [src] : Kick", ATKLOG_ALL)
+ else //if target can't be kicked, do a regular harm attack then clear the streak
+ streak = ""
+ harm_act(A, D)
+ streak = ""
+ return TRUE
+
+/datum/martial_art/cqc/proc/Pressure(mob/living/carbon/human/A, mob/living/carbon/human/D)
+ if(!can_use(A))
+ return FALSE
+ D.visible_message("[A] forces their arm on [D]'s neck!")
+ D.adjustStaminaLoss(60)
+ playsound(get_turf(A), 'sound/weapons/cqchit1.ogg', 50, 1, -1)
+ add_attack_logs(A, D, "Melee attacked with martial-art [src] : Pressure", ATKLOG_ALL)
+ return TRUE
+
+/datum/martial_art/cqc/proc/Restrain(mob/living/carbon/human/A, mob/living/carbon/human/D)
+ if(restraining)
+ return
+ if(!can_use(A))
+ return FALSE
+ if(!D.stat)
+ D.visible_message("[A] locks [D] into a restraining position!", \
+ "[A] locks you into a restraining position!")
+ D.adjustStaminaLoss(20)
+ D.Stun(5)
+ restraining = TRUE
+ addtimer(CALLBACK(src, .proc/drop_restraining), 50, TIMER_UNIQUE)
+ add_attack_logs(A, D, "Melee attacked with martial-art [src] : Restrain", ATKLOG_ALL)
+ return TRUE
+
+/datum/martial_art/cqc/proc/Consecutive(mob/living/carbon/human/A, mob/living/carbon/human/D)
+ if(!can_use(A))
+ return FALSE
+ if(!D.stat)
+ D.visible_message("[A] strikes [D]'s abdomen, neck and back consecutively", \
+ "[A] strikes your abdomen, neck and back consecutively!")
+ playsound(get_turf(D), 'sound/weapons/cqchit2.ogg', 50, 1, -1)
+ var/obj/item/I = D.get_active_hand()
+ if(I && D.drop_item())
+ A.put_in_hands(I)
+ D.adjustStaminaLoss(50)
+ D.apply_damage(25, BRUTE)
+ add_attack_logs(A, D, "Melee attacked with martial-art [src] : Consecutive", ATKLOG_ALL)
+ else //if target can't be hit, do a regular harm attack then clear the streak
+ streak = ""
+ harm_act(A, D)
+ streak = ""
+ return TRUE
+
+/datum/martial_art/cqc/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
+ if(!can_use(A))
+ return FALSE
+ add_to_streak("G", D)
+ if(check_streak(A, D))
+ return TRUE
+ var/obj/item/grab/G = D.grabbedby(A, 1)
+ if(G)
+ G.state = GRAB_AGGRESSIVE //Instant aggressive grab
+ return TRUE
+
+/datum/martial_art/cqc/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
+ if(!can_use(A))
+ return FALSE
+ add_to_streak("H", D)
+ if(check_streak(A, D))
+ return TRUE
+ A.do_attack_animation(D)
+ var/picked_hit_type = pick("CQC'd", "neck chopped", "gut punched")
+ var/bonus_damage = 13
+ if(D.weakened || D.resting || D.lying)
+ bonus_damage += 5
+ picked_hit_type = "stomps on"
+ D.apply_damage(bonus_damage, BRUTE)
+ if(picked_hit_type == "kicks" || picked_hit_type == "stomps on")
+ playsound(get_turf(D), 'sound/weapons/cqchit2.ogg', 50, 1, -1)
+ else
+ playsound(get_turf(D), 'sound/weapons/cqchit1.ogg', 50, 1, -1)
+ D.visible_message("[A] [picked_hit_type] [D]!", \
+ "[A] [picked_hit_type] you!")
+ add_attack_logs(A, D, "Melee attacked with martial-art [src]", ATKLOG_ALL)
+ if(A.resting && !D.stat && !D.weakened)
+ D.visible_message("[A] leg sweeps [D]!", \
+ "[A] leg sweeps you!")
+ playsound(get_turf(A), 'sound/effects/hit_kick.ogg', 50, 1, -1)
+ D.apply_damage(10, BRUTE)
+ D.Weaken(3)
+ add_attack_logs(A, D, "Melee attacked with martial-art [src] : Leg sweep", ATKLOG_ALL)
+ return TRUE
+
+/datum/martial_art/cqc/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
+ if(!can_use(A))
+ return FALSE
+ add_to_streak("D", D)
+ if(check_streak(A, D))
+ return TRUE
+ if(!restraining)
+ D.visible_message("[A] strikes [D]'s jaw with their hand!", \
+ "[A] strikes your jaw, disorienting you!")
+ playsound(get_turf(D), 'sound/weapons/cqchit1.ogg', 50, 1, -1)
+ D.Jitter(2)
+ D.apply_damage(5, BRUTE)
+ add_attack_logs(A, D, "Melee attacked with martial-art [src] : Disarm", ATKLOG_ALL)
+ if(restraining)
+ D.visible_message("[A] puts [D] into a chokehold!", \
+ "[A] puts you into a chokehold!")
+ D.SetSleeping(20)
+ restraining = FALSE
+ add_attack_logs(A, D, "Knocked out with martial-art [src] : Choke hold", ATKLOG_ALL)
+ else
+ restraining = FALSE
+ return FALSE
+ return TRUE
+
+/mob/living/carbon/human/proc/CQC_help()
+ set name = "Remember The Basics"
+ set desc = "You try to remember some of the basics of CQC."
+ set category = "CQC"
+ to_chat(usr, "You try to remember some of the basics of CQC.")
+
+ to_chat(usr, "Slam: Harm Grab. Slam opponent into the ground, knocking them down.")
+ to_chat(usr, "CQC Kick: Harm Harm. Knocks opponent away. Knocks out stunned or knocked down opponents.")
+ to_chat(usr, "Restrain: Grab Switch Hand Grab. Locks opponent into a restraining position, stunning them.")
+ to_chat(usr, "Choke Hold: Restrain Disarm. Knocks out an opponent you have restrained.")
+ to_chat(usr, "Pressure: Disarm Grab. Decent stamina damage.")
+ to_chat(usr, "Consecutive CQC: Disarm Disarm Harm. Mainly offensive move, huge damage and decent stamina damage.")
+
+ to_chat(usr, "In addition, by having your throw mode on when being attacked, you enter an active defense mode where you have a chance to block and sometimes even counter melee attacks done to you.")
\ No newline at end of file
diff --git a/code/modules/martial_arts/martial.dm b/code/modules/martial_arts/martial.dm
index 3e3af7963b0..aed823b372b 100644
--- a/code/modules/martial_arts/martial.dm
+++ b/code/modules/martial_arts/martial.dm
@@ -6,6 +6,8 @@
var/temporary = 0
var/datum/martial_art/base = null // The permanent style
var/deflection_chance = 0 //Chance to deflect projectiles
+ var/block_chance = 0 //Chance to block melee attacks using items while on throw mode.
+ var/restraining = 0 //used in cqc's disarm_act to check if the disarmed is being restrained and so whether they should be put in a chokehold or not
var/help_verb = null
var/no_guns = FALSE //set to TRUE to prevent users of this style from using guns (sleeping carp, highlander). They can still pick them up, but not fire them.
var/no_guns_message = "" //message to tell the style user if they try and use a gun while no_guns = TRUE (DISHONORABRU!)
@@ -22,6 +24,9 @@
/datum/martial_art/proc/help_act(var/mob/living/carbon/human/A, var/mob/living/carbon/human/D)
return 0
+/datum/martial_art/proc/can_use(mob/living/carbon/human/H)
+ return TRUE
+
/datum/martial_art/proc/add_to_streak(var/element,var/mob/living/carbon/human/D)
if(D != current_target)
current_target = D
@@ -143,6 +148,7 @@
/obj/item/plasma_fist_scroll/attack_self(mob/user as mob)
if(!ishuman(user))
return
+
if(!used)
var/mob/living/carbon/human/H = user
var/datum/martial_art/plasma_fist/F = new/datum/martial_art/plasma_fist(null)
@@ -162,6 +168,14 @@
/obj/item/sleeping_carp_scroll/attack_self(mob/living/carbon/human/user as mob)
if(!istype(user) || !user)
return
+ if(user.mind && (user.mind.changeling || user.mind.vampire)) //Prevents changelings and vampires from being able to learn it
+ if(user.mind && user.mind.changeling) //Changelings
+ to_chat(user, "We try multiple times, but we are not able to comprehend the contents of the scroll!")
+ return
+ else //Vampires
+ to_chat(user, "Your blood lust distracts you too much to be able to concentrate on the contents of the scroll!")
+ return
+
to_chat(user, "You have learned the ancient martial art of the Sleeping Carp! \
Your hand-to-hand combat has become much more effective, and you are now able to deflect any projectiles directed toward you. \
However, you are also unable to use any ranged weaponry. \
@@ -175,6 +189,24 @@
new /obj/effect/decal/cleanable/ash(get_turf(src))
qdel(src)
+/obj/item/CQC_manual
+ name = "old manual"
+ desc = "A small, black manual. There are drawn instructions of tactical hand-to-hand combat."
+ icon = 'icons/obj/library.dmi'
+ icon_state = "cqcmanual"
+
+/obj/item/CQC_manual/attack_self(mob/living/carbon/human/user)
+ if(!istype(user) || !user)
+ return
+ to_chat(user, "You remember the basics of CQC.")
+
+ var/datum/martial_art/cqc/CQC = new(null)
+ CQC.teach(user)
+ user.drop_item()
+ visible_message("[src] beeps ominously, and a moment later it bursts up in flames.")
+ new /obj/effect/decal/cleanable/ash(get_turf(src))
+ qdel(src)
+
/obj/item/twohanded/bostaff
name = "bo staff"
desc = "A long, tall staff made of polished wood. Traditionally used in ancient old-Earth martial arts. Can be wielded to both kill and incapacitate."
diff --git a/code/modules/mining/coins.dm b/code/modules/mining/coins.dm
index 96fc0aa2e7f..70d920cd38f 100644
--- a/code/modules/mining/coins.dm
+++ b/code/modules/mining/coins.dm
@@ -18,7 +18,7 @@
pixel_x = rand(0,16)-8
pixel_y = rand(0,8)-8
- icon_state = "coin_[cmineral]_heads"
+ icon_state = "coin_[cmineral]_[sideslist[1]]"
if(cmineral)
name = "[cmineral] coin"
diff --git a/code/modules/mining/equipment_locker.dm b/code/modules/mining/equipment_locker.dm
index c6749528908..11bafb24e26 100644
--- a/code/modules/mining/equipment_locker.dm
+++ b/code/modules/mining/equipment_locker.dm
@@ -335,6 +335,8 @@
var/datum/design/alloy = files.FindDesignByID(alloy_id)
if((check_access(inserted_id) || allowed(usr)) && alloy)
var/desired = input("How many sheets?", "How many sheets would you like to smelt?", 1) as null|num
+ if(desired < 1) // Stops an exploit that lets you build negative alloys and get free materials
+ return
var/smelt_amount = can_smelt_alloy(alloy)
var/amount = round(min(desired,50,smelt_amount))
materials.use_amount(alloy.materials, amount)
diff --git a/code/modules/mining/lavaland/loot/colossus_loot.dm b/code/modules/mining/lavaland/loot/colossus_loot.dm
index 4c748067a5b..78dcbca2cf4 100644
--- a/code/modules/mining/lavaland/loot/colossus_loot.dm
+++ b/code/modules/mining/lavaland/loot/colossus_loot.dm
@@ -112,9 +112,9 @@
activation_method = pick("touch","laser","bullet","energy","bomb","mob_bump","weapon","speech") // "heat" removed due to lack of is_hot()
..()
-/obj/machinery/anomalous_crystal/hear_talk(mob/speaker, message)
+/obj/machinery/anomalous_crystal/hear_talk(mob/speaker, list/message_pieces)
..()
- if(isliving(speaker) && message)
+ if(isliving(speaker) && LAZYLEN(message_pieces))
ActivationReaction(speaker, "speech")
/obj/machinery/anomalous_crystal/attack_hand(mob/user)
diff --git a/code/modules/mob/dead/dead.dm b/code/modules/mob/dead/dead.dm
new file mode 100644
index 00000000000..922f3181c27
--- /dev/null
+++ b/code/modules/mob/dead/dead.dm
@@ -0,0 +1,23 @@
+/mob/dead/forceMove(atom/destination)
+ // Overriden from code/game/atoms_movable.dm#141 to prevent things like mice squeaking when ghosts walk on them.
+ // Same as parent, except that it does not include Uncrossed or Crossed calls.
+ var/turf/old_loc = loc
+ loc = destination
+
+ if(old_loc)
+ old_loc.Exited(src, destination)
+
+ if(destination)
+ destination.Entered(src)
+
+ if(isturf(destination) && opacity)
+ var/turf/new_loc = destination
+ new_loc.reconsider_lights()
+
+ if(isturf(old_loc) && opacity)
+ old_loc.reconsider_lights()
+
+ for(var/datum/light_source/L in light_sources)
+ L.source_atom.update_light()
+
+ return 1
diff --git a/code/modules/mob/dead/observer/say.dm b/code/modules/mob/dead/observer/say.dm
index b780ac0d381..ab47a84ee76 100644
--- a/code/modules/mob/dead/observer/say.dm
+++ b/code/modules/mob/dead/observer/say.dm
@@ -38,7 +38,7 @@
. = src.emote_dead(message)
-/mob/dead/observer/handle_track(var/message, var/verb = "says", var/datum/language/language, var/mob/speaker = null, var/speaker_name, var/atom/follow_target, var/hard_to_hear)
+/mob/dead/observer/handle_track(var/message, var/verb = "says", var/mob/speaker = null, var/speaker_name, var/atom/follow_target, var/hard_to_hear)
return "[speaker_name] ([ghost_follow_link(follow_target, ghost=src)])"
/mob/dead/observer/handle_speaker_name(var/mob/speaker = null, var/vname, var/hard_to_hear)
diff --git a/code/modules/mob/hear_say.dm b/code/modules/mob/hear_say.dm
index 2593d2eebaf..382eb94b4db 100644
--- a/code/modules/mob/hear_say.dm
+++ b/code/modules/mob/hear_say.dm
@@ -1,6 +1,53 @@
// At minimum every mob has a hear_say proc.
-/mob/proc/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol)
+/mob/proc/combine_message(var/list/message_pieces, var/verb, var/mob/speaker, always_stars = FALSE)
+ var/iteration_count = 0
+ var/msg = "" // This is to make sure that the pieces have actually added something
+ . = "[verb], \""
+ for(var/datum/multilingual_say_piece/SP in message_pieces)
+ iteration_count++
+ var/piece = SP.message
+ if(piece == "")
+ continue
+
+ if(SP.speaking && SP.speaking.flags & INNATE) // Fucking snowflake noise lang
+ return SP.speaking.format_message(piece)
+
+ if(iteration_count == 1)
+ piece = capitalize(piece)
+
+ if(SP.speaking)
+ if(!say_understands(speaker, SP.speaking))
+ if(isanimal(speaker))
+ var/mob/living/simple_animal/S = speaker
+ if(LAZYLEN(S.speak))
+ piece = pick(S.speak)
+ else
+ piece = stars(piece)
+ else
+ piece = SP.speaking.scramble(piece)
+ if(always_stars)
+ piece = stars(piece)
+ piece = SP.speaking.format_message(piece)
+ else
+ if(!say_understands(speaker, null))
+ piece = stars(piece)
+ if(isanimal(speaker))
+ var/mob/living/simple_animal/S = speaker
+ if(LAZYLEN(S.speak))
+ piece = pick(S.speak)
+ if(always_stars)
+ piece = stars(piece)
+ piece = "[piece]"
+ msg += (piece + " ")
+ if(msg == "")
+ // There is literally no content left in this message, we need to shut this shit down
+ . = "" // hear_say will suppress it
+ else
+ . = trim(. + trim(msg))
+ . += "\""
+
+/mob/proc/hear_say(var/list/message_pieces, var/verb = "says", var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol)
if(!client)
return 0
@@ -23,35 +70,18 @@
sound_vol *= 0.5
if(sleeping || stat == UNCONSCIOUS)
- hear_sleep(message)
+ hear_sleep(message_pieces)
return 0
- //non-verbal languages are garbled if you can't see the speaker. Yes, this includes if they are inside a closet.
- if(language && (language.flags & NONVERBAL))
- if(!has_vision(information_only = TRUE)) //blind people can't see dumbass
- message = stars(message)
-
- if(!speaker || !(speaker in view(src)))
- message = stars(message)
-
- if(!say_understands(speaker, language))
- if(isanimal(speaker))
- var/mob/living/simple_animal/S = speaker
- if(S.speak.len)
- message = pick(S.speak)
- else
- message = stars(message)
- else
- if(language)
- message = language.scramble(message)
- else
- message = stars(message)
-
var/speaker_name = speaker.name
if(ishuman(speaker))
var/mob/living/carbon/human/H = speaker
speaker_name = H.GetVoice()
+ var/message = combine_message(message_pieces, verb, speaker)
+ if(message == "")
+ return
+
if(italics)
message = "[message]"
@@ -66,74 +96,47 @@
message = "[message]"
if(!can_hear())
- if(!language || !(language.flags & INNATE)) // INNATE is the flag for audible-emote-language, so we don't want to show an "x talks but you cannot hear them" message if it's set
- if(speaker == src)
- to_chat(src, "You cannot hear yourself speak!")
- else
- to_chat(src, "[speaker_name][speaker.GetAltName()] talks but you cannot hear [speaker.p_them()].")
- else
- if(language)
- to_chat(src, "[speaker_name][speaker.GetAltName()] [track][language.format_message(message, verb)]")
+ // INNATE is the flag for audible-emote-language, so we don't want to show an "x talks but you cannot hear them" message if it's set
+ // if(!language || !(language.flags & INNATE))
+ if(speaker == src)
+ to_chat(src, "You cannot hear yourself speak!")
else
- to_chat(src, "[speaker_name][speaker.GetAltName()] [track][verb], \"[message]\"")
+ to_chat(src, "[speaker_name][speaker.GetAltName()] talks but you cannot hear [speaker.p_them()].")
+ else
+ to_chat(src, "[speaker_name][speaker.GetAltName()] [track][message]")
if(speech_sound && (get_dist(speaker, src) <= world.view && src.z == speaker.z))
var/turf/source = speaker? get_turf(speaker) : get_turf(src)
src.playsound_local(source, speech_sound, sound_vol, 1)
-/mob/proc/hear_radio(var/message, var/verb = "says", var/datum/language/language = null, var/part_a, var/part_b, var/mob/speaker = null, var/hard_to_hear = 0, var/vname = "", var/atom/follow_target)
+/mob/proc/hear_radio(list/message_pieces, verb = "says", part_a, part_b, mob/speaker = null, hard_to_hear = 0, vname = "", atom/follow_target)
if(!client)
return
if(sleeping || stat == UNCONSCIOUS) //If unconscious or sleeping
- hear_sleep(message)
+ hear_sleep(multilingual_to_message(message_pieces))
+ return
+
+ var/message = combine_message(message_pieces, verb, speaker, always_stars = hard_to_hear)
+ if(message == "")
return
var/track = null
if(!follow_target)
follow_target = speaker
- //non-verbal languages are garbled if you can't see the speaker. Yes, this includes if they are inside a closet.
- if(language && (language.flags & NONVERBAL))
- if(!has_vision(information_only=TRUE)) //blind people can't see dumbass
- message = stars(message)
-
- if(!speaker || !(speaker in view(src)))
- message = stars(message)
-
- if(!say_understands(speaker, language))
- if(isanimal(speaker))
- var/mob/living/simple_animal/S = speaker
- if(S.speak && S.speak.len)
- message = pick(S.speak)
- else
- return
- else
- if(language)
- message = language.scramble(message)
- else
- message = stars(message)
-
- if(hard_to_hear)
- message = stars(message)
-
var/speaker_name = handle_speaker_name(speaker, vname, hard_to_hear)
- track = handle_track(message, verb, language, speaker, speaker_name, follow_target, hard_to_hear)
+ track = handle_track(message, verb, speaker, speaker_name, follow_target, hard_to_hear)
- var/formatted
- if(language)
- formatted = language.format_message_radio(message, verb)
- else
- formatted = "[verb], \"[message]\""
if(!can_hear())
if(prob(20))
to_chat(src, "You feel your headset vibrate but can hear nothing from it!")
else if(track)
- to_chat(src, "[part_a][track][part_b][formatted]")
+ to_chat(src, "[part_a][track][part_b][message]")
else
- to_chat(src, "[part_a][speaker_name][part_b][formatted]")
+ to_chat(src, "[part_a][speaker_name][part_b][message]")
-/mob/proc/handle_speaker_name(var/mob/speaker = null, var/vname, var/hard_to_hear)
+/mob/proc/handle_speaker_name(mob/speaker = null, vname, hard_to_hear)
var/speaker_name = "unknown"
if(speaker)
speaker_name = speaker.name
@@ -146,26 +149,10 @@
return speaker_name
-/mob/proc/handle_track(var/message, var/verb = "says", var/datum/language/language, var/mob/speaker = null, var/speaker_name, var/atom/follow_target, var/hard_to_hear)
+/mob/proc/handle_track(message, verb = "says", mob/speaker = null, speaker_name, atom/follow_target, hard_to_hear)
return
-/mob/proc/hear_signlang(var/message, var/verb = "gestures", var/datum/language/language, var/mob/speaker = null)
- if(!client)
- return
-
- if(say_understands(speaker, language))
- message = "[src] [verb], \"[message]\""
- else
- message = "[src] [verb]."
-
- if(src.status_flags & PASSEMOTES)
- for(var/obj/item/holder/H in src.contents)
- H.show_message(message)
- for(var/mob/living/M in src.contents)
- M.show_message(message)
- src.show_message(message)
-
-/mob/proc/hear_sleep(var/message)
+/mob/proc/hear_sleep(message)
var/heard = ""
if(prob(15))
message = strip_html_properly(message)
@@ -177,9 +164,19 @@
heardword = copytext(heardword,2)
if(copytext(heardword,-1) in punctuation)
heardword = copytext(heardword,1,lentext(heardword))
- heard = "...You hear something about...[heardword]"
+ heard = "...You hear something about... '[heardword]'..."
else
- heard = "...You almost hear someone talking..."
+ heard = "...You almost hear someone talking..."
to_chat(src, heard)
+
+/mob/proc/hear_holopad_talk(list/message_pieces, var/verb = "says", var/mob/speaker = null)
+ var/message = combine_message(message_pieces, verb, speaker)
+
+ var/name = speaker.name
+ if(!say_understands(speaker))
+ name = speaker.voice_name
+
+ var/rendered = "[name] [message]"
+ to_chat(src, rendered)
\ No newline at end of file
diff --git a/code/modules/mob/language.dm b/code/modules/mob/language.dm
index 0a1f2d322ce..2d4b555448b 100644
--- a/code/modules/mob/language.dm
+++ b/code/modules/mob/language.dm
@@ -11,7 +11,6 @@
var/ask_verb = "asks" // Used when sentence ends in a ?
var/exclaim_verb = "exclaims" // Used when sentence ends in a !
var/whisper_verb // Optional. When not specified speech_verb + quietly/softly is used instead.
- var/signlang_verb = list() // list of emotes that might be displayed if this language has NONVERBAL or SIGNLANG flags
var/colour = "body" // CSS style to use for strings in this language.
var/key = "x" // Character used to speak in language eg. :o for Unathi.
var/flags = 0 // Various language flags.
@@ -85,11 +84,11 @@
return scrambled_text
-/datum/language/proc/format_message(message, verb)
- return "[verb], \"[capitalize(message)]\""
+/datum/language/proc/format_message(message)
+ return "[message]"
-/datum/language/proc/format_message_radio(message, verb)
- return "[verb], \"[capitalize(message)]\""
+/datum/language/proc/format_message_radio(message)
+ return "[message]"
/datum/language/proc/get_talkinto_msg_range(message)
// if you yell, you'll be heard from two tiles over instead of one
@@ -103,11 +102,11 @@
if(!speaker_mask)
speaker_mask = speaker.name
- var/msg = "[name], [speaker_mask] [format_message(message, get_spoken_verb(message))]"
+ var/msg = "[name], [speaker_mask] [get_spoken_verb(message)], [format_message(message)]"
for(var/mob/player in GLOB.player_list)
if(istype(player,/mob/dead) && follow)
- var/msg_dead = "[name], [speaker_mask] ([ghost_follow_link(speaker, ghost=player)]) [format_message(message, get_spoken_verb(message))]"
+ var/msg_dead = "[name], [speaker_mask] ([ghost_follow_link(speaker, ghost=player)]) [get_spoken_verb(message)], [format_message(message)]"
to_chat(player, msg_dead)
continue
@@ -135,10 +134,10 @@
key = ""
flags = RESTRICTED|NONGLOBAL|INNATE|NO_TALK_MSG|NO_STUTTER
-/datum/language/noise/format_message(message, verb)
+/datum/language/noise/format_message(message)
return "[message]"
-/datum/language/noise/format_message_radio(message, verb)
+/datum/language/noise/format_message_radio(message)
return "[message]"
/datum/language/noise/get_talkinto_msg_range(message)
@@ -635,10 +634,9 @@
// Language handling.
/mob/proc/add_language(language)
-
var/datum/language/new_language = GLOB.all_languages[language]
- if(!istype(new_language) || new_language in languages)
+ if(!istype(new_language) || (new_language in languages))
return FALSE
languages |= new_language
@@ -657,8 +655,7 @@
// Can we speak this language, as opposed to just understanding it?
/mob/proc/can_speak_language(datum/language/speaking)
-
- return (universal_speak || (speaking && speaking.flags & INNATE) || speaking in languages)
+ return universal_speak || (speaking && speaking.flags & INNATE) || (speaking in languages)
//TBD
/mob/proc/check_lang_data()
diff --git a/code/modules/mob/living/autohiss.dm b/code/modules/mob/living/autohiss.dm
index df1bde34306..286901baf5c 100644
--- a/code/modules/mob/living/autohiss.dm
+++ b/code/modules/mob/living/autohiss.dm
@@ -1,4 +1,4 @@
-/mob/living/proc/handle_autohiss(message, datum/language/L)
+/mob/proc/handle_autohiss(message, datum/language/L)
return message // no autohiss at this level
/mob/living/carbon/human/handle_autohiss(message, datum/language/L)
diff --git a/code/modules/mob/living/carbon/brain/say.dm b/code/modules/mob/living/carbon/brain/say.dm
index f2a15119156..eb4750c9780 100644
--- a/code/modules/mob/living/carbon/brain/say.dm
+++ b/code/modules/mob/living/carbon/brain/say.dm
@@ -29,7 +29,7 @@
to_chat(usr, "You cannot speak, as your internal speaker is turned off.")
. = FALSE
-/mob/living/carbon/brain/handle_message_mode(var/message_mode, var/message, var/verb, var/speaking, var/used_radios)
+/mob/living/carbon/brain/handle_message_mode(var/message_mode, list/message_pieces, var/verb, var/used_radios)
switch(message_mode)
if("headset")
var/radio_worked = 0 // If any of the radios our brainmob could use functioned, this is set true so that we don't use any others
@@ -39,12 +39,12 @@
if(!radio_worked && c.mecha)
var/obj/mecha/metalgear = c.mecha
if(metalgear.radio)
- radio_worked = metalgear.radio.talk_into(src, message, message_mode, verb, speaking)
+ radio_worked = metalgear.radio.talk_into(src, message_pieces, message_mode, verb)
else if(!radio_worked && c.radio)
- radio_worked = c.radio.talk_into(src, message, message_mode, verb, speaking)
+ radio_worked = c.radio.talk_into(src, message_pieces, message_mode, verb)
return radio_worked
if("whisper")
- whisper_say(message, speaking)
+ whisper_say(message_pieces)
return 1
else return 0
diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm
index 9201075bd56..11961dac08f 100644
--- a/code/modules/mob/living/carbon/human/emote.dm
+++ b/code/modules/mob/living/carbon/human/emote.dm
@@ -238,7 +238,7 @@
start_tail_wagging(1)
else if(dna.species.bodyflags & TAIL_WAGGING)
- if(!wear_suit || !(wear_suit.flags_inv & HIDETAIL) && !istype(wear_suit, /obj/item/clothing/suit/space))
+ if(!wear_suit || !(wear_suit.flags_inv & HIDETAIL))
message = "[src] starts wagging [p_their()] tail."
start_tail_wagging(1)
else
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index b3d4ab3306b..09d0a84f729 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -705,7 +705,8 @@
if(href_list["criminal"])
if(hasHUD(usr,"security"))
-
+ if(usr.incapacitated())
+ return
var/found_record = 0
var/perpname = "wot"
if(wear_id)
@@ -752,6 +753,8 @@
if(href_list["secrecord"])
if(hasHUD(usr,"security"))
+ if(usr.incapacitated())
+ return
var/perpname = "wot"
var/read = 0
@@ -782,6 +785,8 @@
if(href_list["secrecordComment"])
if(hasHUD(usr,"security"))
+ if(usr.incapacitated())
+ return
var/perpname = "wot"
var/read = 0
@@ -811,6 +816,8 @@
if(href_list["secrecordadd"])
if(hasHUD(usr,"security"))
+ if(usr.incapacitated())
+ return
var/perpname = "wot"
if(wear_id)
if(istype(wear_id,/obj/item/card/id))
@@ -840,6 +847,8 @@
if(href_list["medical"])
if(hasHUD(usr,"medical"))
+ if(usr.incapacitated())
+ return
var/perpname = "wot"
var/modified = 0
@@ -873,6 +882,8 @@
if(href_list["medrecord"])
if(hasHUD(usr,"medical"))
+ if(usr.incapacitated())
+ return
var/perpname = "wot"
var/read = 0
@@ -904,6 +915,8 @@
if(href_list["medrecordComment"])
if(hasHUD(usr,"medical"))
+ if(usr.incapacitated())
+ return
var/perpname = "wot"
var/read = 0
@@ -933,6 +946,8 @@
if(href_list["medrecordadd"])
if(hasHUD(usr,"medical"))
+ if(usr.incapacitated())
+ return
var/perpname = "wot"
if(wear_id)
if(istype(wear_id,/obj/item/card/id))
diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm
index d4c14bda32a..b858cca130a 100644
--- a/code/modules/mob/living/carbon/human/human_damage.dm
+++ b/code/modules/mob/living/carbon/human/human_damage.dm
@@ -233,7 +233,6 @@
var/obj/item/organ/external/picked = pick(parts)
if(picked.receive_damage(brute, burn, sharp, updating_health))
UpdateDamageIcon()
- speech_problem_flag = 1
//Heal MANY external organs, in random order
@@ -256,7 +255,6 @@
if(updating_health)
updatehealth("heal overall damage")
- speech_problem_flag = 1
if(update)
UpdateDamageIcon()
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index 98274ea7e1f..a7d04c7f0c4 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -156,6 +156,10 @@ emp_act
return 1
return 0
+/mob/living/carbon/human/check_block()
+ if(martial_art && prob(martial_art.block_chance) && martial_art.can_use(src) && in_throw_mode && !incapacitated(FALSE, TRUE))
+ return TRUE
+
/mob/living/carbon/human/emp_act(severity)
for(var/obj/O in src)
if(!O) continue
diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm
index 96693a0d086..22a9d40f6fd 100644
--- a/code/modules/mob/living/carbon/human/human_defines.dm
+++ b/code/modules/mob/living/carbon/human/human_defines.dm
@@ -42,8 +42,6 @@ var/global/default_martial_art = new/datum/martial_art
var/voice = "" //Instead of new say code calling GetVoice() over and over and over, we're just going to ask this variable, which gets updated in Life()
- var/speech_problem_flag = 0
-
var/datum/personal_crafting/handcrafting
var/datum/martial_art/martial_art = null
diff --git a/code/modules/mob/living/carbon/human/interactive/interactive.dm b/code/modules/mob/living/carbon/human/interactive/interactive.dm
index 3eb2f4e65fd..bb9ae87bdc7 100644
--- a/code/modules/mob/living/carbon/human/interactive/interactive.dm
+++ b/code/modules/mob/living/carbon/human/interactive/interactive.dm
@@ -580,14 +580,14 @@
return FALSE
saveVoice()
-/mob/living/carbon/human/interactive/hear_say(message, verb = "says", datum/language/language = null, italics = 0, mob/speaker = null, sound/speech_sound, sound_vol)
+/mob/living/carbon/human/interactive/hear_say(list/message_pieces, verb = "says", italics = 0, mob/speaker = null, sound/speech_sound, sound_vol)
if(!istype(speaker, /mob/living/carbon/human/interactive))
- knownStrings |= html_decode(message)
+ knownStrings |= html_decode(multilingual_to_message(message_pieces))
..()
-/mob/living/carbon/human/interactive/hear_radio(message, verb = "says", datum/language/language=null, part_a, part_b, mob/speaker = null, hard_to_hear = 0, vname = "", atom/follow_target)
+/mob/living/carbon/human/interactive/hear_radio(list/message_pieces, verb = "says", part_a, part_b, mob/speaker = null, hard_to_hear = 0, vname = "", atom/follow_target)
if(!istype(speaker, /mob/living/carbon/human/interactive))
- knownStrings |= html_decode(message)
+ knownStrings |= html_decode(multilingual_to_message(message_pieces))
..()
/mob/living/carbon/human/interactive/proc/doProcess()
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index ce1d9a7f56e..8d0261528ed 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -71,7 +71,6 @@
drop_item()
emote("cough")
if(disabilities & TOURETTES)
- speech_problem_flag = 1
if((prob(10) && paralysis <= 1))
Stun(10)
switch(rand(1, 3))
@@ -86,12 +85,10 @@
animate(pixel_x = initial(pixel_x) , pixel_y = initial(pixel_y), time = 1)
if(disabilities & NERVOUS)
- speech_problem_flag = 1
if(prob(10))
Stuttering(10)
if(getBrainLoss() >= 60 && stat != DEAD)
- speech_problem_flag = 1
if(prob(3))
var/list/s1 = list("IM A [pick("PONY","LIZARD","taJaran","kitty","Vulpakin","drASK","BIRDIE","voxxie","race car","combat meCH","SPESSSHIP")] [pick("NEEEEEEIIIIIIIIIGH","sKREEEEEE","MEOW","NYA~","rawr","Barkbark","Hissssss","vROOOOOM","pewpew","choo Choo")]!",
"without oxigen blob don't evoluate?",
@@ -158,7 +155,6 @@
if(!gene.block)
continue
if(gene.is_active(src))
- speech_problem_flag = 1
gene.OnMobLife(src)
if(!ignore_gene_stability && gene_stability < GENETIC_DAMAGE_STAGE_1)
var/instability = DEFAULT_GENE_STABILITY - gene_stability
@@ -741,7 +737,6 @@
stat = UNCONSCIOUS
else if(sleeping)
- speech_problem_flag = 1
stat = UNCONSCIOUS
@@ -1003,29 +998,6 @@
This proc below is only called when those HUD elements need to change as determined by the mobs hud_updateflag.
*/
-
-/mob/living/carbon/human/handle_silent()
- if(..())
- speech_problem_flag = 1
- return silent
-
-/mob/living/carbon/human/handle_slurring()
- if(..())
- speech_problem_flag = 1
- return slurring
-
-/mob/living/carbon/human/handle_stunned()
- if(..())
- speech_problem_flag = 1
- return stunned
-
-/mob/living/carbon/human/handle_stuttering()
- if(..())
- speech_problem_flag = 1
- return stuttering
-
-
-
/mob/living/carbon/human/proc/can_heartattack()
if(NO_BLOOD in dna.species.species_traits)
return FALSE
diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm
index 70cab4751e2..8d9355c1f26 100644
--- a/code/modules/mob/living/carbon/human/say.dm
+++ b/code/modules/mob/living/carbon/human/say.dm
@@ -107,65 +107,58 @@
/mob/living/carbon/human/proc/GetSpecialVoice()
return special_voice
-/mob/living/carbon/human/handle_speech_problems(var/message, var/verb)
- var/list/returns[3]
- var/speech_problem_flag = 0
+/mob/living/carbon/human/handle_speech_problems(list/message_pieces, var/verb)
var/span = ""
if(mind)
span = mind.speech_span
- if(silent || (disabilities & MUTE))
- message = ""
- speech_problem_flag = 1
+ if((COMIC in mutations) \
+ || (locate(/obj/item/organ/internal/cyberimp/brain/clown_voice) in internal_organs) \
+ || istype(get_item_by_slot(slot_wear_mask), /obj/item/clothing/mask/gas/voice/clown))
+ span = "sans"
+
+ var/list/parent = ..()
+ verb = parent["verb"]
- if(istype(wear_mask, /obj/item/clothing/mask/horsehead))
- var/obj/item/clothing/mask/horsehead/hoers = wear_mask
- if(hoers.voicechange)
- message = pick("NEEIIGGGHHHH!", "NEEEIIIIGHH!", "NEIIIGGHH!", "HAAWWWWW!", "HAAAWWW!")
- verb = pick("whinnies","neighs", "says")
- speech_problem_flag = 1
+ for(var/datum/multilingual_say_piece/S in message_pieces)
+ if(S.speaking && S.speaking.flags & NO_STUTTER)
+ continue
- if(dna)
- for(var/datum/dna/gene/gene in dna_genes)
- if(!gene.block)
- continue
- if(gene.is_active(src))
- message = gene.OnSay(src,message)
- speech_problem_flag = 1
+ if(silent || (disabilities & MUTE))
+ S.message = ""
- if(message != "")
- var/list/parent = ..()
- message = parent[1]
- verb = parent[2]
- if(parent[3])
- speech_problem_flag = 1
+ if(istype(wear_mask, /obj/item/clothing/mask/horsehead))
+ var/obj/item/clothing/mask/horsehead/hoers = wear_mask
+ if(hoers.voicechange)
+ S.message = pick("NEEIIGGGHHHH!", "NEEEIIIIGHH!", "NEIIIGGHH!", "HAAWWWWW!", "HAAAWWW!")
+ verb = pick("whinnies", "neighs", "says")
+
+ if(dna)
+ for(var/datum/dna/gene/gene in dna_genes)
+ if(!gene.block)
+ continue
+ if(gene.is_active(src))
+ S.message = gene.OnSay(src, S.message)
var/braindam = getBrainLoss()
if(braindam >= 60)
- speech_problem_flag = 1
- if(prob(braindam/4))
- message = stutter(message)
+ if(prob(braindam / 4))
+ S.message = stutter(S.message)
verb = "gibbers"
if(prob(braindam))
- message = uppertext(message)
+ S.message = uppertext(S.message)
verb = "yells loudly"
- if((COMIC in mutations) || (locate(/obj/item/organ/internal/cyberimp/brain/clown_voice) in internal_organs) || istype(get_item_by_slot(slot_wear_mask), /obj/item/clothing/mask/gas/voice/clown))
- span = "sans"
+ if(span)
+ S.message = "[S.message]"
+ return list("verb" = verb)
- if(span)
- message = "[message]"
- returns[1] = message
- returns[2] = verb
- returns[3] = speech_problem_flag
- return returns
-
-/mob/living/carbon/human/handle_message_mode(var/message_mode, var/message, var/verb, var/speaking, var/used_radios)
+/mob/living/carbon/human/handle_message_mode(var/message_mode, list/message_pieces, var/verb, var/used_radios)
switch(message_mode)
if("intercom")
for(var/obj/item/radio/intercom/I in view(1, src))
spawn(0)
- I.talk_into(src, message, null, verb, speaking)
+ I.talk_into(src, message_pieces, null, verb)
used_radios += I
if("headset")
@@ -173,13 +166,13 @@
if(isradio(l_ear))
R = l_ear
used_radios += R
- if(R.talk_into(src, message, null, verb, speaking))
+ if(R.talk_into(src, message_pieces, null, verb))
return
if(isradio(r_ear))
R = r_ear
used_radios += R
- if(R.talk_into(src, message, null, verb, speaking))
+ if(R.talk_into(src, message_pieces, null, verb))
return
if("right ear")
@@ -190,7 +183,7 @@
R = r_hand
if(R)
used_radios += R
- R.talk_into(src, message, null, verb, speaking)
+ R.talk_into(src, message_pieces, null, verb)
if("left ear")
var/obj/item/radio/R
@@ -200,21 +193,21 @@
R = l_hand
if(R)
used_radios += R
- R.talk_into(src, message, null, verb, speaking)
+ R.talk_into(src, message_pieces, null, verb)
if("whisper")
- whisper_say(message, speaking)
+ whisper_say(message_pieces)
return 1
else
if(message_mode)
if(isradio(l_ear))
used_radios += l_ear
- if(l_ear.talk_into(src, message, message_mode, verb, speaking))
+ if(l_ear.talk_into(src, message_pieces, message_mode, verb))
return
if(isradio(r_ear))
used_radios += r_ear
- if(r_ear.talk_into(src, message, message_mode, verb, speaking))
+ if(r_ear.talk_into(src, message_pieces, message_mode, verb))
return
/mob/living/carbon/human/handle_speech_sound()
diff --git a/code/modules/mob/living/carbon/human/species/_species.dm b/code/modules/mob/living/carbon/human/species/_species.dm
index 37b67d40f29..06d45056ac4 100644
--- a/code/modules/mob/living/carbon/human/species/_species.dm
+++ b/code/modules/mob/living/carbon/human/species/_species.dm
@@ -198,8 +198,9 @@
var/obj/item/organ/internal/ears/ears = H.get_int_organ(/obj/item/organ/internal/ears)
if(ears)
qdel(ears)
-
- ears = new mutantears(H)
+
+ if(mutantears)
+ ears = new mutantears(H)
/datum/species/proc/breathe(mob/living/carbon/human/H)
if((NO_BREATHE in species_traits) || (BREATHLESS in H.mutations))
@@ -318,6 +319,9 @@
user.do_cpr(target)
/datum/species/proc/grab(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
+ if(target.check_block()) //cqc
+ target.visible_message("[target] blocks [user]'s grab attempt!")
+ return FALSE
if(attacker_style && attacker_style.grab_act(user, target))
return TRUE
else
@@ -344,6 +348,9 @@
add_attack_logs(user, target, "vampirebit")
return
//end vampire codes
+ if(target.check_block()) //cqc
+ target.visible_message("[target] blocks [user]'s attack!")
+ return FALSE
if(attacker_style && attacker_style.harm_act(user, target))
return TRUE
else
@@ -382,6 +389,9 @@
target.forcesay(GLOB.hit_appends)
/datum/species/proc/disarm(mob/living/carbon/human/user, mob/living/carbon/human/target, datum/martial_art/attacker_style)
+ if(target.check_block()) //cqc
+ target.visible_message("[target] blocks [user]'s disarm attempt!")
+ return FALSE
if(attacker_style && attacker_style.disarm_act(user, target))
return TRUE
else
@@ -716,3 +726,9 @@ It'll return null if the organ doesn't correspond, so include null checks when u
var/picked_species = pick(random_species)
var/datum/species/selected_species = GLOB.all_species[picked_species]
return species_name ? picked_species : selected_species.type
+
+/datum/species/proc/can_hear(mob/living/carbon/human/H)
+ . = FALSE
+ var/obj/item/organ/internal/ears/ears = H.get_int_organ(/obj/item/organ/internal/ears)
+ if(istype(ears) && !ears.deaf)
+ . = TRUE
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/human/species/machine.dm b/code/modules/mob/living/carbon/human/species/machine.dm
index a3c0295243c..88ed0a626cb 100644
--- a/code/modules/mob/living/carbon/human/species/machine.dm
+++ b/code/modules/mob/living/carbon/human/species/machine.dm
@@ -55,6 +55,7 @@
)
vision_organ = /obj/item/organ/internal/eyes/optical_sensor
+ mutantears = /obj/item/organ/internal/ears/microphone
has_limbs = list(
"chest" = list("path" = /obj/item/organ/external/chest/ipc),
"groin" = list("path" = /obj/item/organ/external/groin/ipc),
diff --git a/code/modules/mob/living/carbon/human/species/slime.dm b/code/modules/mob/living/carbon/human/species/slime.dm
index 583835b7358..5631f600afc 100644
--- a/code/modules/mob/living/carbon/human/species/slime.dm
+++ b/code/modules/mob/living/carbon/human/species/slime.dm
@@ -42,6 +42,7 @@
has_organ = list(
"brain" = /obj/item/organ/internal/brain/slime
)
+ mutantears = null
suicide_messages = list(
"is melting into a puddle!",
@@ -82,6 +83,9 @@
H.update_body()
..()
+/datum/species/slime/can_hear() // fucking snowflakes
+ . = TRUE
+
/datum/action/innate/slimecolor
name = "Toggle Recolor"
check_flags = AB_CHECK_CONSCIOUS
diff --git a/code/modules/mob/living/carbon/human/species/station.dm b/code/modules/mob/living/carbon/human/species/station.dm
deleted file mode 100644
index ec8076cf37b..00000000000
--- a/code/modules/mob/living/carbon/human/species/station.dm
+++ /dev/null
@@ -1,1040 +0,0 @@
-/datum/species/human
- name = "Human"
- name_plural = "Humans"
- icobase = 'icons/mob/human_races/r_human.dmi'
- deform = 'icons/mob/human_races/r_def_human.dmi'
- primitive_form = /datum/species/monkey
- language = "Sol Common"
- species_traits = list(LIPS, CAN_BE_FAT)
- skinned_type = /obj/item/stack/sheet/animalhide/human
- clothing_flags = HAS_UNDERWEAR | HAS_UNDERSHIRT | HAS_SOCKS
- bodyflags = HAS_SKIN_TONE | HAS_BODY_MARKINGS
- dietflags = DIET_OMNI
- blurb = "Humanity originated in the Sol system, and over the last five centuries has spread \
- colonies across a wide swathe of space. They hold a wide range of forms and creeds.
\
- While the central Sol government maintains control of its far-flung people, powerful corporate \
- interests, rampant cyber and bio-augmentation and secretive factions make life on most human \
- worlds tumultous at best."
-
- reagent_tag = PROCESS_ORG
- //Has standard darksight of 2.
-
-/datum/species/unathi
- name = "Unathi"
- name_plural = "Unathi"
- icobase = 'icons/mob/human_races/r_lizard.dmi'
- deform = 'icons/mob/human_races/r_def_lizard.dmi'
- language = "Sinta'unathi"
- tail = "sogtail"
- skinned_type = /obj/item/stack/sheet/animalhide/lizard
- unarmed_type = /datum/unarmed_attack/claws
- primitive_form = /datum/species/monkey/unathi
-
- blurb = "A heavily reptillian species, Unathi (or 'Sinta as they call themselves) hail from the \
- Uuosa-Eso system, which roughly translates to 'burning mother'.
Coming from a harsh, radioactive \
- desert planet, they mostly hold ideals of honesty, virtue, martial combat and bravery above all \
- else, frequently even their own lives. They prefer warmer temperatures than most species and \
- their native tongue is a heavy hissing laungage called Sinta'Unathi."
-
- species_traits = list(LIPS)
- clothing_flags = HAS_UNDERWEAR | HAS_UNDERSHIRT | HAS_SOCKS
- bodyflags = HAS_TAIL | HAS_HEAD_ACCESSORY | HAS_BODY_MARKINGS | HAS_HEAD_MARKINGS | HAS_SKIN_COLOR | HAS_ALT_HEADS | TAIL_WAGGING
- dietflags = DIET_CARN
-
- cold_level_1 = 280 //Default 260 - Lower is better
- cold_level_2 = 220 //Default 200
- cold_level_3 = 140 //Default 120
-
- heat_level_1 = 380 //Default 360 - Higher is better
- heat_level_2 = 420 //Default 400
- heat_level_3 = 480 //Default 460
-
- flesh_color = "#34AF10"
- reagent_tag = PROCESS_ORG
- base_color = "#066000"
- //Default styles for created mobs.
- default_headacc = "Simple"
- default_headacc_colour = "#404040"
- butt_sprite = "unathi"
- brute_mod = 1.05
-
- has_organ = list(
- "heart" = /obj/item/organ/internal/heart,
- "lungs" = /obj/item/organ/internal/lungs,
- "liver" = /obj/item/organ/internal/liver/unathi,
- "kidneys" = /obj/item/organ/internal/kidneys,
- "brain" = /obj/item/organ/internal/brain,
- "appendix" = /obj/item/organ/internal/appendix,
- "eyes" = /obj/item/organ/internal/eyes/unathi //3 darksight.
- )
-
- allowed_consumed_mobs = list(/mob/living/simple_animal/mouse, /mob/living/simple_animal/lizard, /mob/living/simple_animal/chick, /mob/living/simple_animal/chicken,
- /mob/living/simple_animal/crab, /mob/living/simple_animal/butterfly, /mob/living/simple_animal/parrot, /mob/living/simple_animal/tribble)
-
- suicide_messages = list(
- "is attempting to bite their tongue off!",
- "is jamming their claws into their eye sockets!",
- "is twisting their own neck!",
- "is holding their breath!")
-
- var/datum/action/innate/tail_lash/lash = new()
-
-
-/datum/species/unathi/handle_post_spawn(var/mob/living/carbon/human/H)
- lash.Grant(H)
- ..()
-
-/datum/action/innate/tail_lash
- name = "Tail lash"
- icon_icon = 'icons/effects/effects.dmi'
- button_icon_state = "tail"
-
-/datum/action/innate/tail_lash/Activate()
- var/mob/living/carbon/human/user = owner
- if(!user.restrained() || !user.buckled)
- to_chat(user, "You need freedom of movement to tail lash!")
- return
- if(user.getStaminaLoss() >= 50)
- to_chat(user, "Rest before tail lashing again!")
- return
- for(var/mob/living/carbon/human/C in orange(1))
- var/obj/item/organ/external/E = C.get_organ(pick("l_leg", "r_leg", "l_foot", "r_foot", "groin"))
- if(E)
- user.changeNext_move(CLICK_CD_MELEE)
- user.visible_message("[src] smacks [C] in [E] with their tail! ", "You hit [C] in [E] with your tail!")
- user.adjustStaminaLoss(15)
- C.apply_damage(5, BRUTE, E)
- user.spin(20, 1)
- playsound(user.loc, 'sound/weapons/slash.ogg', 50, 0)
-
-
-
-/datum/species/unathi/handle_death(var/mob/living/carbon/human/H)
- H.stop_tail_wagging(1)
-
-/datum/species/tajaran
- name = "Tajaran"
- name_plural = "Tajaran"
- icobase = 'icons/mob/human_races/r_tajaran.dmi'
- deform = 'icons/mob/human_races/r_def_tajaran.dmi'
- language = "Siik'tajr"
- tail = "tajtail"
- skinned_type = /obj/item/stack/sheet/fur
- unarmed_type = /datum/unarmed_attack/claws
-
- blurb = "The Tajaran race is a species of feline-like bipeds hailing from the planet of Ahdomai in the \
- S'randarr system. They have been brought up into the space age by the Humans and Skrell, and have been \
- influenced heavily by their long history of Slavemaster rule. They have a structured, clan-influenced way \
- of family and politics. They prefer colder environments, and speak a variety of languages, mostly Siik'Maas, \
- using unique inflections their mouths form."
-
- cold_level_1 = 240
- cold_level_2 = 180
- cold_level_3 = 100
-
- heat_level_1 = 340
- heat_level_2 = 380
- heat_level_3 = 440
-
- primitive_form = /datum/species/monkey/tajaran
-
- species_traits = list(LIPS, CAN_BE_FAT)
- clothing_flags = HAS_UNDERWEAR | HAS_UNDERSHIRT | HAS_SOCKS
- bodyflags = HAS_TAIL | HAS_HEAD_ACCESSORY | HAS_HEAD_MARKINGS | HAS_BODY_MARKINGS | HAS_SKIN_COLOR | TAIL_WAGGING
- dietflags = DIET_OMNI
- taste_sensitivity = TASTE_SENSITIVITY_SHARP
- reagent_tag = PROCESS_ORG
- flesh_color = "#AFA59E"
- base_color = "#424242"
- butt_sprite = "tajaran"
-
- has_organ = list(
- "heart" = /obj/item/organ/internal/heart,
- "lungs" = /obj/item/organ/internal/lungs,
- "liver" = /obj/item/organ/internal/liver/tajaran,
- "kidneys" = /obj/item/organ/internal/kidneys,
- "brain" = /obj/item/organ/internal/brain,
- "appendix" = /obj/item/organ/internal/appendix,
- "eyes" = /obj/item/organ/internal/eyes/tajaran /*Most Tajara see in full colour as a result of genetic augmentation, although it cost them their darksight (darksight = 2)
- unless they choose otherwise by selecting the colourblind disability in character creation (darksight = 8 but colourblind).*/
- )
-
- allowed_consumed_mobs = list(/mob/living/simple_animal/mouse, /mob/living/simple_animal/chick, /mob/living/simple_animal/butterfly, /mob/living/simple_animal/parrot,
- /mob/living/simple_animal/tribble)
-
- suicide_messages = list(
- "is attempting to bite their tongue off!",
- "is jamming their claws into their eye sockets!",
- "is twisting their own neck!",
- "is holding their breath!")
-
-/datum/species/tajaran/handle_death(var/mob/living/carbon/human/H)
- H.stop_tail_wagging(1)
-
-/datum/species/vulpkanin
- name = "Vulpkanin"
- name_plural = "Vulpkanin"
- icobase = 'icons/mob/human_races/r_vulpkanin.dmi'
- deform = 'icons/mob/human_races/r_vulpkanin.dmi'
- language = "Canilunzt"
- primitive_form = /datum/species/monkey/vulpkanin
- tail = "vulptail"
- skinned_type = /obj/item/stack/sheet/fur
- unarmed_type = /datum/unarmed_attack/claws
-
- blurb = "Vulpkanin are a species of sharp-witted canine-pideds residing on the planet Altam just barely within the \
- dual-star Vazzend system. Their politically de-centralized society and independent natures have led them to become a species and \
- culture both feared and respected for their scientific breakthroughs. Discovery, loyalty, and utilitarianism dominates their lifestyles \
- to the degree it can cause conflict with more rigorous and strict authorities. They speak a guttural language known as 'Canilunzt' \
- which has a heavy emphasis on utilizing tail positioning and ear twitches to communicate intent."
-
- species_traits = list(LIPS)
- clothing_flags = HAS_UNDERWEAR | HAS_UNDERSHIRT | HAS_SOCKS
- bodyflags = HAS_TAIL | TAIL_WAGGING | TAIL_OVERLAPPED | HAS_HEAD_ACCESSORY | HAS_MARKINGS | HAS_SKIN_COLOR
- dietflags = DIET_OMNI
- hunger_drain = 0.11
- taste_sensitivity = TASTE_SENSITIVITY_SHARP
- reagent_tag = PROCESS_ORG
- flesh_color = "#966464"
- base_color = "#CF4D2F"
- butt_sprite = "vulp"
-
- scream_verb = "yelps"
-
- has_organ = list(
- "heart" = /obj/item/organ/internal/heart,
- "lungs" = /obj/item/organ/internal/lungs,
- "liver" = /obj/item/organ/internal/liver/vulpkanin,
- "kidneys" = /obj/item/organ/internal/kidneys,
- "brain" = /obj/item/organ/internal/brain,
- "appendix" = /obj/item/organ/internal/appendix,
- "eyes" = /obj/item/organ/internal/eyes/vulpkanin /*Most Vulpkanin see in full colour as a result of genetic augmentation, although it cost them their darksight (darksight = 2)
- unless they choose otherwise by selecting the colourblind disability in character creation (darksight = 8 but colourblind).*/
- )
-
- allowed_consumed_mobs = list(/mob/living/simple_animal/mouse, /mob/living/simple_animal/lizard, /mob/living/simple_animal/chick, /mob/living/simple_animal/chicken,
- /mob/living/simple_animal/crab, /mob/living/simple_animal/butterfly, /mob/living/simple_animal/parrot, /mob/living/simple_animal/tribble)
-
- suicide_messages = list(
- "is attempting to bite their tongue off!",
- "is jamming their claws into their eye sockets!",
- "is twisting their own neck!",
- "is holding their breath!")
-
-/datum/species/vulpkanin/handle_death(var/mob/living/carbon/human/H)
- H.stop_tail_wagging(1)
-
-/datum/species/skrell
- name = "Skrell"
- name_plural = "Skrell"
- icobase = 'icons/mob/human_races/r_skrell.dmi'
- deform = 'icons/mob/human_races/r_def_skrell.dmi'
- language = "Skrellian"
- primitive_form = /datum/species/monkey/skrell
-
- blurb = "An amphibious species, Skrell come from the star system known as Qerr'Vallis, which translates to 'Star of \
- the royals' or 'Light of the Crown'.
Skrell are a highly advanced and logical race who live under the rule \
- of the Qerr'Katish, a caste within their society which keeps the empire of the Skrell running smoothly. Skrell are \
- herbivores on the whole and tend to be co-operative with the other species of the galaxy, although they rarely reveal \
- the secrets of their empire to their allies."
-
- species_traits = list(LIPS)
- clothing_flags = HAS_UNDERWEAR | HAS_UNDERSHIRT | HAS_SOCKS
- bodyflags = HAS_SKIN_COLOR | HAS_BODY_MARKINGS
- dietflags = DIET_HERB
- taste_sensitivity = TASTE_SENSITIVITY_DULL
- flesh_color = "#8CD7A3"
- blood_color = "#1D2CBF"
- base_color = "#38b661" //RGB: 56, 182, 97.
- default_hair_colour = "#38b661"
- eyes = "skrell_eyes_s"
- //Default styles for created mobs.
- default_hair = "Skrell Male Tentacles"
- reagent_tag = PROCESS_ORG
- butt_sprite = "skrell"
-
- has_organ = list(
- "heart" = /obj/item/organ/internal/heart,
- "lungs" = /obj/item/organ/internal/lungs,
- "liver" = /obj/item/organ/internal/liver/skrell,
- "kidneys" = /obj/item/organ/internal/kidneys,
- "brain" = /obj/item/organ/internal/brain,
- "appendix" = /obj/item/organ/internal/appendix,
- "eyes" = /obj/item/organ/internal/eyes, //Default darksight of 2.
- "headpocket" = /obj/item/organ/internal/headpocket
- )
-
- suicide_messages = list(
- "is attempting to bite their tongue off!",
- "is jamming their thumbs into their eye sockets!",
- "is twisting their own neck!",
- "makes like a fish and suffocates!",
- "is strangling themselves with their own tendrils!")
-
-/datum/species/vox
- name = "Vox"
- name_plural = "Vox"
- icobase = 'icons/mob/human_races/vox/r_vox.dmi'
- deform = 'icons/mob/human_races/vox/r_def_vox.dmi'
- language = "Vox-pidgin"
- tail = "voxtail"
- speech_sounds = list('sound/voice/shriek1.ogg')
- speech_chance = 20
- unarmed_type = /datum/unarmed_attack/claws //I dont think it will hurt to give vox claws too.
-
- blurb = "The Vox are the broken remnants of a once-proud race, now reduced to little more than \
- scavenging vermin who prey on isolated stations, ships or planets to keep their own ancient arkships \
- alive. They are four to five feet tall, reptillian, beaked, tailed and quilled; human crews often \
- refer to them as 'shitbirds' for their violent and offensive nature, as well as their horrible \
- smell.
Most humans will never meet a Vox raider, instead learning of this insular species through \
- dealing with their traders and merchants; those that do rarely enjoy the experience."
-
- brute_mod = 1.2 //20% more brute damage. Fragile bird bones.
-
- warning_low_pressure = 50
- hazard_low_pressure = 0
-
- cold_level_1 = 80
- cold_level_2 = 50
- cold_level_3 = 0
-
- breathid = "n2"
-
- eyes = "vox_eyes_s"
-
- species_traits = list(NO_SCAN, IS_WHITELISTED, NOTRANSSTING)
- clothing_flags = HAS_SOCKS
- dietflags = DIET_OMNI
- bodyflags = HAS_ICON_SKIN_TONE | HAS_TAIL | TAIL_WAGGING | TAIL_OVERLAPPED | HAS_BODY_MARKINGS | HAS_TAIL_MARKINGS
-
- blood_color = "#2299FC"
- flesh_color = "#808D11"
- //Default styles for created mobs.
- default_hair = "Short Vox Quills"
- default_hair_colour = "#614f19" //R: 97, G: 79, B: 25
- butt_sprite = "vox"
-
- reagent_tag = PROCESS_ORG
- scream_verb = "shrieks"
- male_scream_sound = 'sound/voice/shriek1.ogg'
- female_scream_sound = 'sound/voice/shriek1.ogg'
- male_cough_sounds = list('sound/voice/shriekcough.ogg')
- female_cough_sounds = list('sound/voice/shriekcough.ogg')
- male_sneeze_sound = 'sound/voice/shrieksneeze.ogg'
- female_sneeze_sound = 'sound/voice/shrieksneeze.ogg'
-
- icon_skin_tones = list(
- 1 = "Default Green",
- 2 = "Dark Green",
- 3 = "Brown",
- 4 = "Grey",
- 5 = "Emerald",
- 6 = "Azure"
- )
-
- has_organ = list(
- "heart" = /obj/item/organ/internal/heart,
- "lungs" = /obj/item/organ/internal/lungs/vox,
- "liver" = /obj/item/organ/internal/liver/vox,
- "kidneys" = /obj/item/organ/internal/kidneys,
- "brain" = /obj/item/organ/internal/brain,
- "appendix" = /obj/item/organ/internal/appendix,
- "eyes" = /obj/item/organ/internal/eyes, //Default darksight of 2.
- "stack" = /obj/item/organ/internal/stack //Not the same as the cortical stack implant Vox Raiders spawn with. The cortical stack implant is used
- ) //for determining the success of the heist game-mode's 'leave nobody behind' objective, while this is just an organ.
-
- suicide_messages = list(
- "is attempting to bite their tongue off!",
- "is jamming their claws into their eye sockets!",
- "is twisting their own neck!",
- "is holding their breath!",
- "is deeply inhaling oxygen!")
-
-/datum/species/vox/handle_death(var/mob/living/carbon/human/H)
- H.stop_tail_wagging(1)
-
-/datum/species/vox/after_equip_job(datum/job/J, mob/living/carbon/human/H)
- if(!H.mind || !H.mind.assigned_role || H.mind.assigned_role != "Clown" && H.mind.assigned_role != "Mime")
- H.unEquip(H.wear_mask)
- H.unEquip(H.l_hand)
-
- H.equip_or_collect(new /obj/item/clothing/mask/breath/vox(H), slot_wear_mask)
- var/tank_pref = H.client && H.client.prefs ? H.client.prefs.speciesprefs : null
- if(tank_pref)//Diseasel, here you go
- H.equip_or_collect(new /obj/item/tank/nitrogen(H), slot_l_hand)
- else
- H.equip_or_collect(new /obj/item/tank/emergency_oxygen/vox(H), slot_l_hand)
- to_chat(H, "You are now running on nitrogen internals from the [H.l_hand] in your hand. Your species finds oxygen toxic, so you must breathe nitrogen only.")
- H.internal = H.l_hand
- H.update_action_buttons_icon()
-
-/datum/species/vox/handle_post_spawn(var/mob/living/carbon/human/H)
- updatespeciescolor(H)
- H.update_icons()
- //H.verbs += /mob/living/carbon/human/proc/leap
- ..()
-
-/datum/species/vox/updatespeciescolor(var/mob/living/carbon/human/H, var/owner_sensitive = 1) //Handling species-specific skin-tones for the Vox race.
- if(H.dna.species.bodyflags & HAS_ICON_SKIN_TONE) //Making sure we don't break Armalis.
- var/new_icobase = 'icons/mob/human_races/vox/r_vox.dmi' //Default Green Vox.
- var/new_deform = 'icons/mob/human_races/vox/r_def_vox.dmi' //Default Green Vox.
- switch(H.s_tone)
- if(6) //Azure Vox.
- new_icobase = 'icons/mob/human_races/vox/r_voxazu.dmi'
- new_deform = 'icons/mob/human_races/vox/r_def_voxazu.dmi'
- H.tail = "voxtail_azu"
- if(5) //Emerald Vox.
- new_icobase = 'icons/mob/human_races/vox/r_voxemrl.dmi'
- new_deform = 'icons/mob/human_races/vox/r_def_voxemrl.dmi'
- H.tail = "voxtail_emrl"
- if(4) //Grey Vox.
- new_icobase = 'icons/mob/human_races/vox/r_voxgry.dmi'
- new_deform = 'icons/mob/human_races/vox/r_def_voxgry.dmi'
- H.tail = "voxtail_gry"
- if(3) //Brown Vox.
- new_icobase = 'icons/mob/human_races/vox/r_voxbrn.dmi'
- new_deform = 'icons/mob/human_races/vox/r_def_voxbrn.dmi'
- H.tail = "voxtail_brn"
- if(2) //Dark Green Vox.
- new_icobase = 'icons/mob/human_races/vox/r_voxdgrn.dmi'
- new_deform = 'icons/mob/human_races/vox/r_def_voxdgrn.dmi'
- H.tail = "voxtail_dgrn"
- else //Default Green Vox.
- H.tail = "voxtail" //Ensures they get an appropriately coloured tail depending on the skin-tone.
-
- H.change_icobase(new_icobase, new_deform, owner_sensitive) //Update the icobase/deform of all our organs, but make sure we don't mess with frankenstein limbs in doing so.
- H.update_dna()
-
-/datum/species/vox/handle_reagents(var/mob/living/carbon/human/H, var/datum/reagent/R)
- if(R.id == "oxygen") //Armalis are above such petty things.
- H.adjustToxLoss(1*REAGENTS_EFFECT_MULTIPLIER) //Same as plasma.
- H.reagents.remove_reagent(R.id, REAGENTS_METABOLISM)
- return 0 //Handling reagent removal on our own.
-
- return ..()
-
-/datum/species/vox/armalis/handle_post_spawn(var/mob/living/carbon/human/H)
- H.verbs += /mob/living/carbon/human/proc/leap
- H.verbs += /mob/living/carbon/human/proc/gut
- ..()
-
-/datum/species/vox/armalis
- name = "Vox Armalis"
- name_plural = "Vox Armalis"
- icobase = 'icons/mob/human_races/r_armalis.dmi'
- deform = 'icons/mob/human_races/r_armalis.dmi'
- unarmed_type = /datum/unarmed_attack/claws/armalis
-
- warning_low_pressure = 50
- hazard_low_pressure = 0
-
- cold_level_1 = 80
- cold_level_2 = 50
- cold_level_3 = 0
-
- heat_level_1 = 2000
- heat_level_2 = 3000
- heat_level_3 = 4000
-
- brute_mod = 0.2
- burn_mod = 0.2
-
- eyes = "blank_eyes"
-
- species_traits = list(NO_SCAN, NO_BLOOD, NO_PAIN, IS_WHITELISTED)
- bodyflags = HAS_TAIL
- dietflags = DIET_OMNI //should inherit this from vox, this is here just in case
-
- blood_color = "#2299FC"
- flesh_color = "#808D11"
-
- reagent_tag = PROCESS_ORG
-
- tail = "armalis_tail"
- icon_template = 'icons/mob/human_races/r_armalis.dmi'
-
- has_organ = list(
- "heart" = /obj/item/organ/internal/heart,
- "lungs" = /obj/item/organ/internal/lungs/vox,
- "liver" = /obj/item/organ/internal/liver,
- "kidneys" = /obj/item/organ/internal/kidneys,
- "brain" = /obj/item/organ/internal/brain,
- "eyes" = /obj/item/organ/internal/eyes, //Default darksight of 2.
- "stack" = /obj/item/organ/internal/stack //Not the same as the cortical stack implant Vox Raiders spawn with. The cortical stack implant is used
- ) //for determining the success of the heist game-mode's 'leave nobody behind' objective, while this is just an organ.
-
- suicide_messages = list(
- "is attempting to bite their tongue off!",
- "is jamming their claws into their eye sockets!",
- "is twisting their own neck!",
- "is holding their breath!",
- "is huffing oxygen!")
-
-/datum/species/vox/armalis/handle_reagents() //Skip the Vox oxygen reagent toxicity. Armalis are above such things.
- return 1
-
-/datum/species/kidan
- name = "Kidan"
- name_plural = "Kidan"
- icobase = 'icons/mob/human_races/r_kidan.dmi'
- deform = 'icons/mob/human_races/r_def_kidan.dmi'
- language = "Chittin"
- unarmed_type = /datum/unarmed_attack/claws
-
- brute_mod = 0.8
-
- species_traits = list(IS_WHITELISTED)
- clothing_flags = HAS_UNDERWEAR | HAS_UNDERSHIRT | HAS_SOCKS
- bodyflags = HAS_HEAD_ACCESSORY | HAS_HEAD_MARKINGS | HAS_BODY_MARKINGS
- eyes = "kidan_eyes_s"
- dietflags = DIET_HERB
- blood_color = "#FB9800"
- reagent_tag = PROCESS_ORG
- //Default styles for created mobs.
- default_headacc = "Normal Antennae"
- butt_sprite = "kidan"
-
- has_organ = list(
- "heart" = /obj/item/organ/internal/heart,
- "lungs" = /obj/item/organ/internal/lungs,
- "liver" = /obj/item/organ/internal/liver/kidan,
- "kidneys" = /obj/item/organ/internal/kidneys,
- "brain" = /obj/item/organ/internal/brain,
- "appendix" = /obj/item/organ/internal/appendix,
- "eyes" = /obj/item/organ/internal/eyes, //Default darksight of 2.
- "lantern" = /obj/item/organ/internal/lantern
- )
-
- allowed_consumed_mobs = list(/mob/living/simple_animal/diona)
-
- suicide_messages = list(
- "is attempting to bite their antenna off!",
- "is jamming their claws into their eye sockets!",
- "is twisting their own neck!",
- "is cracking their exoskeleton!",
- "is stabbing themselves with their mandibles!",
- "is holding their breath!")
-
-/datum/species/slime
- name = "Slime People"
- name_plural = "Slime People"
- language = "Bubblish"
- icobase = 'icons/mob/human_races/r_slime.dmi'
- deform = 'icons/mob/human_races/r_slime.dmi'
- remains_type = /obj/effect/decal/remains/slime
-
- // More sensitive to the cold
- cold_level_1 = 280
- cold_level_2 = 240
- cold_level_3 = 200
- coldmod = 3
-
- oxy_mod = 0
- brain_mod = 2.5
-
- male_cough_sounds = list('sound/effects/slime_squish.ogg')
- female_cough_sounds = list('sound/effects/slime_squish.ogg')
-
- species_traits = list(LIPS, IS_WHITELISTED, NO_BREATHE, NO_INTORGANS, NO_SCAN)
- clothing_flags = HAS_UNDERWEAR | HAS_UNDERSHIRT | HAS_SOCKS
- bodyflags = HAS_SKIN_COLOR | NO_EYES
- dietflags = DIET_CARN
- reagent_tag = PROCESS_ORG
-
- blood_color = "#0064C8"
- exotic_blood = "water"
- blood_damage_type = TOX
-
- butt_sprite = "slime"
- //Has default darksight of 2.
-
- has_organ = list(
- "brain" = /obj/item/organ/internal/brain/slime
- )
-
- suicide_messages = list(
- "is melting into a puddle!",
- "is ripping out their own core!",
- "is turning a dull, brown color and melting into a puddle!")
-
- var/reagent_skin_coloring = FALSE
-
- var/datum/action/innate/regrow/grow = new()
-
- species_abilities = list(
- /mob/living/carbon/human/verb/toggle_recolor_verb,
- /mob/living/carbon/human/proc/regrow_limbs
- )
-
-/datum/species/slime/handle_post_spawn(var/mob/living/carbon/human/H)
- grow.Grant(H)
- ..()
-
-/datum/action/innate/regrow
- name = "Regrow limbs"
- icon_icon = 'icons/effects/effects.dmi'
- button_icon_state = "greenglow"
-
-/datum/action/innate/regrow/Activate()
- var/mob/living/carbon/human/user = owner
- user.regrow_limbs()
-
-
-/datum/species/slime/handle_life(var/mob/living/carbon/human/H)
-//This is allegedly for code "style". Like a plaid sweater?
-#define SLIMEPERSON_COLOR_SHIFT_TRIGGER 0.1
-#define SLIMEPERSON_ICON_UPDATE_PERIOD 200 // 20 seconds
-#define SLIMEPERSON_BLOOD_SCALING_FACTOR 5 // Used to adjust how much of an effect the blood has on the rate of color change. Higher is slower.
- // Slowly shifting to the color of the reagents
- if(reagent_skin_coloring && H.reagents.total_volume > SLIMEPERSON_COLOR_SHIFT_TRIGGER)
- var/blood_amount = H.blood_volume
- var/r_color = mix_color_from_reagents(H.reagents.reagent_list)
- var/new_body_color = BlendRGB(r_color, H.skin_colour, (blood_amount*SLIMEPERSON_BLOOD_SCALING_FACTOR)/((blood_amount*SLIMEPERSON_BLOOD_SCALING_FACTOR)+(H.reagents.total_volume)))
- H.skin_colour = new_body_color
- if(world.time % SLIMEPERSON_ICON_UPDATE_PERIOD > SLIMEPERSON_ICON_UPDATE_PERIOD - 20) // The 20 is because this gets called every 2 seconds, from the mob controller
- for(var/organname in H.bodyparts_by_name)
- var/obj/item/organ/external/E = H.bodyparts_by_name[organname]
- if(istype(E) && E.dna && istype(E.dna.species, /datum/species/slime))
- E.sync_colour_to_human(H)
- H.update_hair(0)
- H.update_body()
- ..()
-
-#undef SLIMEPERSON_COLOR_SHIFT_TRIGGER
-#undef SLIMEPERSON_ICON_UPDATE_PERIOD
-#undef SLIMEPERSON_BLOOD_SCALING_FACTOR
-
-/mob/living/carbon/human/proc/toggle_recolor(silent = FALSE)
- if(!isslimeperson(src))
- if(!silent)
- to_chat(src, "You're not a slime person!")
- return
-
- var/datum/species/slime/S = dna.species
- if(S.reagent_skin_coloring)
- S.reagent_skin_coloring = TRUE
- if(!silent)
- to_chat(src, "You adjust your internal chemistry to filter out pigments from things you consume.")
- else
- S.reagent_skin_coloring = TRUE
- if(!silent)
- to_chat(src, "You adjust your internal chemistry to permit pigments in chemicals you consume to tint you.")
-
-/mob/living/carbon/human/verb/toggle_recolor_verb()
- set category = "IC"
- set name = "Toggle Reagent Recoloring"
- set desc = "While active, you'll slowly adjust your body's color to that of the reagents inside of you, moderated by how much blood you have."
-
- toggle_recolor()
-
-
-/mob/living/carbon/human/proc/regrow_limbs()
- set category = "IC"
- set name = "Regrow Limbs"
- set desc = "Regrow one of your missing limbs at the cost of a large amount of hunger"
-
-#define SLIMEPERSON_HUNGERCOST 50
-#define SLIMEPERSON_MINHUNGER 250
-#define SLIMEPERSON_REGROWTHDELAY 450 // 45 seconds
-
- if(stat || paralysis || stunned)
- to_chat(src, "You cannot regenerate missing limbs in your current state.")
- return
-
- if(nutrition < SLIMEPERSON_MINHUNGER)
- to_chat(src, "You're too hungry to regenerate a limb!")
- return
-
- var/list/missing_limbs = list()
- for(var/l in bodyparts_by_name)
- var/obj/item/organ/external/E = bodyparts_by_name[l]
- if(!istype(E))
- var/list/limblist = dna.species.has_limbs[l]
- var/obj/item/organ/external/limb = limblist["path"]
- var/parent_organ = initial(limb.parent_organ)
- var/obj/item/organ/external/parentLimb = bodyparts_by_name[parent_organ]
- if(!istype(parentLimb))
- continue
- missing_limbs[initial(limb.name)] = l
-
- if(!missing_limbs.len)
- to_chat(src, "You're not missing any limbs!")
- return
-
- var/limb_select = input(src, "Choose a limb to regrow", "Limb Regrowth") as null|anything in missing_limbs
- var/chosen_limb = missing_limbs[limb_select]
-
- visible_message("[src] begins to hold still and concentrate on [p_their()] missing [limb_select]...", "You begin to focus on regrowing your missing [limb_select]... (This will take [round(SLIMEPERSON_REGROWTHDELAY/10)] seconds, and you must hold still.)")
- if(do_after(src, SLIMEPERSON_REGROWTHDELAY, needhand=0, target = src))
- if(stat || paralysis || stunned)
- to_chat(src, "You cannot regenerate missing limbs in your current state.")
- return
-
- if(nutrition < SLIMEPERSON_MINHUNGER)
- to_chat(src, "You're too hungry to regenerate a limb!")
- return
-
- var/obj/item/organ/external/O = bodyparts_by_name[chosen_limb]
-
- var/stored_brute = 0
- var/stored_burn = 0
- if(istype(O))
- to_chat(src, "You distribute the damaged tissue around your body, out of the way of your new pseudopod!")
- var/obj/item/organ/external/doomedStump = O
- stored_brute = doomedStump.brute_dam
- stored_burn = doomedStump.burn_dam
- qdel(O)
-
- var/limb_list = dna.species.has_limbs[chosen_limb]
- var/obj/item/organ/external/limb_path = limb_list["path"]
- // Parent check
- var/obj/item/organ/external/potential_parent = bodyparts_by_name[initial(limb_path.parent_organ)]
- if(!istype(potential_parent))
- to_chat(src, "You've lost the organ that you've been growing your new part on!")
- return // No rayman for you
- // Grah this line will leave a "not used" warning, in spite of the fact that the new() proc WILL do the thing.
- // Bothersome.
- var/obj/item/organ/external/new_limb = new limb_path(src)
- new_limb.open = 0 // This is just so that the compiler won't think that new_limb is unused, because the compiler is horribly stupid.
- adjustBruteLoss(stored_brute)
- adjustFireLoss(stored_burn)
- update_body()
- updatehealth("slime person regrowth")
- UpdateDamageIcon()
- nutrition -= SLIMEPERSON_HUNGERCOST
- visible_message("[src] finishes regrowing [p_their()] missing [new_limb]!", "You finish regrowing your [limb_select]")
- else
- to_chat(src, "You need to hold still in order to regrow a limb!")
- return
-
-#undef SLIMEPERSON_HUNGERCOST
-#undef SLIMEPERSON_MINHUNGER
-#undef SLIMEPERSON_REGROWTHDELAY
-
-/datum/species/slime/handle_pre_change(mob/living/carbon/human/H)
- ..()
- if(reagent_skin_coloring)
- H.toggle_recolor(silent = 1)
-
-/datum/species/grey
- name = "Grey"
- name_plural = "Greys"
- icobase = 'icons/mob/human_races/r_grey.dmi'
- deform = 'icons/mob/human_races/r_def_grey.dmi'
- language = "Psionic Communication"
- eyes = "grey_eyes_s"
- butt_sprite = "grey"
-
- has_organ = list(
- "heart" = /obj/item/organ/internal/heart,
- "lungs" = /obj/item/organ/internal/lungs,
- "liver" = /obj/item/organ/internal/liver/grey,
- "kidneys" = /obj/item/organ/internal/kidneys,
- "brain" = /obj/item/organ/internal/brain/grey,
- "appendix" = /obj/item/organ/internal/appendix,
- "eyes" = /obj/item/organ/internal/eyes/grey //5 darksight.
- )
-
- brute_mod = 1.25 //greys are fragile
-
- default_genes = list(REMOTE_TALK)
-
-
- species_traits = list(LIPS, IS_WHITELISTED, CAN_BE_FAT)
- clothing_flags = HAS_UNDERWEAR | HAS_UNDERSHIRT | HAS_SOCKS
- bodyflags = HAS_BODY_MARKINGS
- dietflags = DIET_HERB
- reagent_tag = PROCESS_ORG
- blood_color = "#A200FF"
-
-/datum/species/grey/handle_dna(var/mob/living/carbon/C, var/remove)
- if(!remove)
- C.dna.SetSEState(REMOTETALKBLOCK,1,1)
- genemutcheck(C,REMOTETALKBLOCK,null,MUTCHK_FORCED)
- else
- C.dna.SetSEState(REMOTETALKBLOCK,0,1)
- genemutcheck(C,REMOTETALKBLOCK,null,MUTCHK_FORCED)
- ..()
-
-/datum/species/grey/water_act(var/mob/living/carbon/C, volume, temperature, source)
- ..()
- C.take_organ_damage(5,min(volume,20))
- C.emote("scream")
-
-/datum/species/grey/after_equip_job(datum/job/J, mob/living/carbon/human/H)
- var/speech_pref = H.client.prefs.speciesprefs
- if(speech_pref)
- H.mind.speech_span = "wingdings"
-
-/datum/species/grey/handle_reagents(mob/living/carbon/human/H, datum/reagent/R)
- if(R.id == "sacid")
- H.reagents.del_reagent(R.id)
- return 0
- return ..()
-
-/datum/species/diona
- name = "Diona"
- name_plural = "Dionaea"
- icobase = 'icons/mob/human_races/r_diona.dmi'
- deform = 'icons/mob/human_races/r_def_plant.dmi'
- language = "Rootspeak"
- speech_sounds = list('sound/voice/dionatalk1.ogg') //Credit https://www.youtube.com/watch?v=ufnvlRjsOTI [0:13 - 0:16]
- speech_chance = 20
- unarmed_type = /datum/unarmed_attack/diona
- //primitive_form = "Nymph"
- slowdown = 5
- remains_type = /obj/effect/decal/cleanable/ash
-
-
- warning_low_pressure = 50
- hazard_low_pressure = -1
-
- cold_level_1 = 50
- cold_level_2 = -1
- cold_level_3 = -1
-
- heat_level_1 = 300
- heat_level_2 = 340
- heat_level_3 = 400
-
- blurb = "Commonly referred to (erroneously) as 'plant people', the Dionaea are a strange space-dwelling collective \
- species hailing from Epsilon Ursae Minoris. Each 'diona' is a cluster of numerous cat-sized organisms called nymphs; \
- there is no effective upper limit to the number that can fuse in gestalt, and reports exist of the Epsilon Ursae \
- Minoris primary being ringed with a cloud of singing space-station-sized entities.
The Dionaea coexist peacefully with \
- all known species, especially the Skrell. Their communal mind makes them slow to react, and they have difficulty understanding \
- even the simplest concepts of other minds. Their alien physiology allows them survive happily off a diet of nothing but light, \
- water and other radiation."
-
- species_traits = list(NO_BREATHE, RADIMMUNE, IS_PLANT, NO_BLOOD, NO_PAIN)
- clothing_flags = HAS_SOCKS
- default_hair_colour = "#000000"
- dietflags = 0 //Diona regenerate nutrition in light and water, no diet necessary
- taste_sensitivity = TASTE_SENSITIVITY_NO_TASTE
- skinned_type = /obj/item/stack/sheet/wood
-
- oxy_mod = 0
-
- body_temperature = T0C + 15 //make the plant people have a bit lower body temperature, why not
- blood_color = "#004400"
- flesh_color = "#907E4A"
- butt_sprite = "diona"
-
- reagent_tag = PROCESS_ORG
-
- has_organ = list(
- "nutrient channel" = /obj/item/organ/internal/liver/diona,
- "neural strata" = /obj/item/organ/internal/heart/diona,
- "receptor node" = /obj/item/organ/internal/eyes/diona, //Default darksight of 2.
- "gas bladder" = /obj/item/organ/internal/brain/diona,
- "polyp segment" = /obj/item/organ/internal/kidneys/diona,
- "anchoring ligament" = /obj/item/organ/internal/appendix/diona
- )
-
- vision_organ = /obj/item/organ/internal/eyes/diona
- has_limbs = list(
- "chest" = list("path" = /obj/item/organ/external/chest/diona),
- "groin" = list("path" = /obj/item/organ/external/groin/diona),
- "head" = list("path" = /obj/item/organ/external/head/diona),
- "l_arm" = list("path" = /obj/item/organ/external/arm/diona),
- "r_arm" = list("path" = /obj/item/organ/external/arm/right/diona),
- "l_leg" = list("path" = /obj/item/organ/external/leg/diona),
- "r_leg" = list("path" = /obj/item/organ/external/leg/right/diona),
- "l_hand" = list("path" = /obj/item/organ/external/hand/diona),
- "r_hand" = list("path" = /obj/item/organ/external/hand/right/diona),
- "l_foot" = list("path" = /obj/item/organ/external/foot/diona),
- "r_foot" = list("path" = /obj/item/organ/external/foot/right/diona)
- )
-
- suicide_messages = list(
- "is losing branches!",
- "pulls out a secret stash of herbicide and takes a hearty swig!",
- "is pulling themselves apart!")
-
-/datum/species/diona/can_understand(var/mob/other)
- if(istype(other, /mob/living/simple_animal/diona))
- return 1
- return 0
-
-/datum/species/diona/handle_post_spawn(var/mob/living/carbon/human/H)
- H.gender = NEUTER
-
- return ..()
-
-/datum/species/diona/handle_life(var/mob/living/carbon/human/H)
- H.radiation = Clamp(H.radiation, 0, 100) //We have to clamp this first, then decrease it, or there's a few edge cases of massive heals if we clamp and decrease at the same time.
- var/rads = H.radiation / 25
- H.radiation = max(H.radiation-rads, 0)
- H.nutrition = min(H.nutrition+rads, NUTRITION_LEVEL_WELL_FED+10)
- H.adjustBruteLoss(-(rads))
- H.adjustToxLoss(-(rads))
-
- var/light_amount = 0 //how much light there is in the place, affects receiving nutrition and healing
- if(isturf(H.loc)) //else, there's considered to be no light
- var/turf/T = H.loc
- light_amount = min(T.get_lumcount() * 10, 5) //hardcapped so it's not abused by having a ton of flashlights
- H.nutrition = min(H.nutrition+light_amount, NUTRITION_LEVEL_WELL_FED+10)
-
- if(light_amount > 0)
- H.clear_alert("nolight")
- else
- H.throw_alert("nolight", /obj/screen/alert/nolight)
-
- if((light_amount >= 5) && !H.suiciding) //if there's enough light, heal
-
- H.adjustBruteLoss(-(light_amount/2))
- H.adjustFireLoss(-(light_amount/4))
- if(H.nutrition < NUTRITION_LEVEL_STARVING+50)
- H.take_overall_damage(10,0)
- ..()
-
-/datum/species/machine
- name = "Machine"
- name_plural = "Machines"
-
- blurb = "Positronic intelligence really took off in the 26th century, and it is not uncommon to see independant, free-willed \
- robots on many human stations, particularly in fringe systems where standards are slightly lax and public opinion less relevant \
- to corporate operations. IPCs (Integrated Positronic Chassis) are a loose category of self-willed robots with a humanoid form, \
- generally self-owned after being 'born' into servitude; they are reliable and dedicated workers, albeit more than slightly \
- inhuman in outlook and perspective."
-
- icobase = 'icons/mob/human_races/r_machine.dmi'
- deform = 'icons/mob/human_races/r_machine.dmi'
- language = "Trinary"
- remains_type = /obj/effect/decal/remains/robot
- skinned_type = /obj/item/stack/sheet/metal // Let's grind up IPCs for station resources!
-
- eyes = "blank_eyes"
- brute_mod = 2.5 // 100% * 2.5 * 0.6 (robolimbs) ~= 150%
- burn_mod = 2.5 // So they take 50% extra damage from brute/burn overall.
- tox_mod = 0
- clone_mod = 0
- oxy_mod = 0
- death_message = "gives one shrill beep before falling limp, their monitor flashing blue before completely shutting off..."
-
- species_traits = list(IS_WHITELISTED, NO_BREATHE, NO_SCAN, NO_BLOOD, NO_PAIN, NO_DNA, RADIMMUNE, VIRUSIMMUNE, NOTRANSSTING)
- clothing_flags = HAS_UNDERWEAR | HAS_UNDERSHIRT | HAS_SOCKS
- bodyflags = HAS_SKIN_COLOR | HAS_HEAD_MARKINGS | HAS_HEAD_ACCESSORY | ALL_RPARTS
- dietflags = 0 //IPCs can't eat, so no diet
- taste_sensitivity = TASTE_SENSITIVITY_NO_TASTE
- blood_color = "#1F181F"
- flesh_color = "#AAAAAA"
- //Default styles for created mobs.
- default_hair = "Blue IPC Screen"
- can_revive_by_healing = 1
- has_gender = FALSE
- reagent_tag = PROCESS_SYN
- male_scream_sound = 'sound/goonstation/voice/robot_scream.ogg'
- female_scream_sound = 'sound/goonstation/voice/robot_scream.ogg'
- male_cough_sounds = list('sound/effects/mob_effects/m_machine_cougha.ogg','sound/effects/mob_effects/m_machine_coughb.ogg', 'sound/effects/mob_effects/m_machine_coughc.ogg')
- female_cough_sounds = list('sound/effects/mob_effects/f_machine_cougha.ogg','sound/effects/mob_effects/f_machine_coughb.ogg')
- male_sneeze_sound = 'sound/effects/mob_effects/machine_sneeze.ogg'
- female_sneeze_sound = 'sound/effects/mob_effects/f_machine_sneeze.ogg'
- butt_sprite = "machine"
-
- has_organ = list(
- "brain" = /obj/item/organ/internal/brain/mmi_holder/posibrain,
- "cell" = /obj/item/organ/internal/cell,
- "optics" = /obj/item/organ/internal/eyes/optical_sensor, //Default darksight of 2.
- "charger" = /obj/item/organ/internal/cyberimp/arm/power_cord
- )
-
- vision_organ = /obj/item/organ/internal/eyes/optical_sensor
- has_limbs = list(
- "chest" = list("path" = /obj/item/organ/external/chest/ipc),
- "groin" = list("path" = /obj/item/organ/external/groin/ipc),
- "head" = list("path" = /obj/item/organ/external/head/ipc),
- "l_arm" = list("path" = /obj/item/organ/external/arm/ipc),
- "r_arm" = list("path" = /obj/item/organ/external/arm/right/ipc),
- "l_leg" = list("path" = /obj/item/organ/external/leg/ipc),
- "r_leg" = list("path" = /obj/item/organ/external/leg/right/ipc),
- "l_hand" = list("path" = /obj/item/organ/external/hand/ipc),
- "r_hand" = list("path" = /obj/item/organ/external/hand/right/ipc),
- "l_foot" = list("path" = /obj/item/organ/external/foot/ipc),
- "r_foot" = list("path" = /obj/item/organ/external/foot/right/ipc)
- )
-
- suicide_messages = list(
- "is powering down!",
- "is smashing their own monitor!",
- "is twisting their own neck!",
- "is downloading extra RAM!",
- "is frying their own circuits!",
- "is blocking their ventilation port!")
-
- species_abilities = list(
- /mob/living/carbon/human/proc/change_monitor
- )
-
-/datum/species/machine/handle_death(var/mob/living/carbon/human/H)
- var/obj/item/organ/external/head/head_organ = H.get_organ("head")
- if(!head_organ)
- return
- head_organ.h_style = "Bald"
- head_organ.f_style = "Shaved"
- spawn(100)
- if(H)
- H.update_hair()
- H.update_fhair()
-
-/datum/species/drask
- name = "Drask"
- name_plural = "Drask"
- icobase = 'icons/mob/human_races/r_drask.dmi'
- deform = 'icons/mob/human_races/r_drask.dmi'
- language = "Orluum"
- eyes = "drask_eyes_s"
-
- speech_sounds = list('sound/voice/DraskTalk.ogg')
- speech_chance = 20
- male_scream_sound = 'sound/voice/DraskTalk2.ogg'
- female_scream_sound = 'sound/voice/DraskTalk2.ogg'
- male_cough_sounds = 'sound/voice/DraskCough.ogg'
- female_cough_sounds = 'sound/voice/DraskCough.ogg'
- male_sneeze_sound = 'sound/voice/DraskSneeze.ogg'
- female_sneeze_sound = 'sound/voice/DraskSneeze.ogg'
-
- burn_mod = 2
- //exotic_blood = "cryoxadone"
- body_temperature = 273
-
- blurb = "Hailing from Hoorlm, planet outside what is usually considered a habitable \
- orbit, the Drask evolved to live in extreme cold. Their strange bodies seem \
- to operate better the colder their surroundings are, and can regenerate rapidly \
- when breathing supercooled gas.
On their homeworld, the Drask live long lives \
- in their labyrinthine settlements, carved out beneath Hoorlm's icy surface, where the air \
- is of breathable density."
-
- suicide_messages = list(
- "is self-warming with friction!",
- "is jamming fingers through their big eyes!",
- "is sucking in warm air!",
- "is holding their breath!")
-
- species_traits = list(LIPS, IS_WHITELISTED)
- clothing_flags = HAS_UNDERWEAR | HAS_UNDERSHIRT
- bodyflags = HAS_SKIN_TONE | HAS_BODY_MARKINGS
- dietflags = DIET_OMNI
-
- cold_level_1 = -1 //Default 260 - Lower is better
- cold_level_2 = -1 //Default 200
- cold_level_3 = -1 //Default 120
- coldmod = -1
-
- heat_level_1 = 300 //Default 360 - Higher is better
- heat_level_2 = 340 //Default 400
- heat_level_3 = 400 //Default 460
- heatmod = 2
-
- flesh_color = "#a3d4eb"
- reagent_tag = PROCESS_ORG
- base_color = "#a3d4eb"
- blood_color = "#a3d4eb"
- butt_sprite = "drask"
-
- has_organ = list(
- "heart" = /obj/item/organ/internal/heart/drask,
- "lungs" = /obj/item/organ/internal/lungs/drask,
- "metabolic strainer" = /obj/item/organ/internal/liver/drask,
- "eyes" = /obj/item/organ/internal/eyes/drask, //5 darksight.
- "brain" = /obj/item/organ/internal/brain/drask
- )
diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm
index 4357584a18e..12efa71860a 100644
--- a/code/modules/mob/living/carbon/human/update_icons.dm
+++ b/code/modules/mob/living/carbon/human/update_icons.dm
@@ -1082,7 +1082,7 @@ var/global/list/damage_icon_parts = list()
overlays_standing[TAIL_LAYER] = tail
else if(tail && dna.species.bodyflags & HAS_TAIL) //no tailless tajaran
- if(!wear_suit || !(wear_suit.flags_inv & HIDETAIL) && !istype(wear_suit, /obj/item/clothing/suit/space))
+ if(!wear_suit || !(wear_suit.flags_inv & HIDETAIL))
var/icon/tail_s = new/icon("icon" = 'icons/effects/species.dmi', "icon_state" = "[tail]_s")
if(dna.species.bodyflags & HAS_SKIN_COLOR)
tail_s.Blend(skin_colour, ICON_ADD)
diff --git a/code/modules/mob/living/carbon/human/update_stat.dm b/code/modules/mob/living/carbon/human/update_stat.dm
index e079328206e..d8c26a91964 100644
--- a/code/modules/mob/living/carbon/human/update_stat.dm
+++ b/code/modules/mob/living/carbon/human/update_stat.dm
@@ -24,4 +24,10 @@
if((disabilities & NEARSIGHTED) && (!istype(G) || !G.prescription))
overlay_fullscreen("nearsighted", /obj/screen/fullscreen/impaired, 1)
else
- clear_fullscreen("nearsighted")
\ No newline at end of file
+ clear_fullscreen("nearsighted")
+
+
+/mob/living/carbon/human/can_hear()
+ . = TRUE // Fallback if we don't have a species
+ if(dna.species)
+ . = dna.species.can_hear(src)
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/human/whisper.dm b/code/modules/mob/living/carbon/human/whisper.dm
deleted file mode 100644
index 4e74688e10f..00000000000
--- a/code/modules/mob/living/carbon/human/whisper.dm
+++ /dev/null
@@ -1,21 +0,0 @@
-//Lallander was here
-/mob/living/carbon/human/whisper(message as text)
- message = trim_strip_html_properly(message) //bit of duplicate code, acceptable because the workaround would be annoying
-
- //parse the language code and consume it
- var/datum/language/speaking = parse_language(message)
- if(speaking)
- message = copytext(message, 2 + length(speaking.key))
- else
- speaking = get_default_language()
-
- // This is broadcast to all mobs with the language,
- // irrespective of distance or anything else.
- if(speaking && (speaking.flags & HIVEMIND))
- speaking.broadcast(src,trim(message))
- return 1
-
- message = trim_left(message)
- message = handle_autohiss(message, speaking)
-
- whisper_say(message, speaking)
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/slime/say.dm b/code/modules/mob/living/carbon/slime/say.dm
index 04416e945ee..2096da01360 100644
--- a/code/modules/mob/living/carbon/slime/say.dm
+++ b/code/modules/mob/living/carbon/slime/say.dm
@@ -14,16 +14,16 @@
return 1
return ..()
-/mob/living/carbon/slime/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol)
+/mob/living/carbon/slime/hear_say(list/message_pieces, var/verb = "says", var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol)
if(speaker in Friends)
speech_buffer = list()
speech_buffer.Add(speaker)
- speech_buffer.Add(lowertext(html_decode(message)))
+ speech_buffer.Add(lowertext(html_decode(multilingual_to_message(message_pieces))))
..()
-/mob/living/carbon/slime/hear_radio(var/message, var/verb="says", var/datum/language/language=null, var/part_a, var/part_b, var/mob/speaker = null, var/hard_to_hear = 0, var/vname ="", var/atom/follow_target)
+/mob/living/carbon/slime/hear_radio(list/message_pieces, var/verb="says", var/part_a, var/part_b, var/mob/speaker = null, var/hard_to_hear = 0, var/vname ="", var/atom/follow_target)
if(speaker in Friends)
speech_buffer = list()
speech_buffer.Add(speaker)
- speech_buffer.Add(lowertext(html_decode(message)))
+ speech_buffer.Add(lowertext(html_decode(multilingual_to_message(message_pieces))))
..()
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index 92a9a6b7b47..f766a1f53ce 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -337,3 +337,7 @@
if(INTENT_DISARM)
M.do_attack_animation(src, ATTACK_EFFECT_DISARM)
return TRUE
+
+//defined here, overridden for humans in human_defense. By default, living mobs don't get to block anything
+/mob/living/proc/check_block()
+ return FALSE
\ No newline at end of file
diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm
index cb08ebff021..b32925badb4 100644
--- a/code/modules/mob/living/say.dm
+++ b/code/modules/mob/living/say.dm
@@ -54,51 +54,48 @@ proc/get_radio_key_from_channel(var/channel)
/mob/living/proc/binarycheck()
return FALSE
-/mob/living/proc/get_default_language()
+/mob/proc/get_default_language()
+ return null
+
+/mob/living/get_default_language()
return default_language
-/mob/living/proc/handle_speech_problems(var/message, var/verb)
- var/list/returns[3]
- var/speech_problem_flag = 0
+/mob/living/proc/handle_speech_problems(list/message_pieces, var/verb)
var/robot = isSynthetic()
+ for(var/datum/multilingual_say_piece/S in message_pieces)
+ if(S.speaking && S.speaking.flags & NO_STUTTER)
+ continue
+ if((HULK in mutations) && health >= 25)
+ S.message = "[uppertext(S.message)]!!!"
+ verb = pick("yells", "roars", "hollers")
- if((HULK in mutations) && health >= 25 && length(message))
- message = "[uppertext(message)]!!!"
- verb = pick("yells","roars","hollers")
- speech_problem_flag = 1
+ if(slurring)
+ if(robot)
+ S.message = slur(S.message, list("@", "!", "#", "$", "%", "&", "?"))
+ else
+ S.message = slur(S.message)
+ verb = "slurs"
- if(slurring)
- if(robot)
- message = slur(message, list("@", "!", "#", "$", "%", "&", "?"))
- else
- message = slur(message)
- verb = "slurs"
- speech_problem_flag = 1
- if(stuttering)
- if(robot)
- message = robostutter(message)
- else
- message = stutter(message)
- verb = "stammers"
- speech_problem_flag = 1
- if(cultslurring)
- message = cultslur(message)
- verb = "slurs"
- speech_problem_flag = 1
- if(!IsVocal())
- message = ""
- speech_problem_flag = 1
+ if(stuttering)
+ if(robot)
+ S.message = robostutter(S.message)
+ else
+ S.message = stutter(S.message)
+ verb = "stammers"
- returns[1] = message
- returns[2] = verb
- returns[3] = speech_problem_flag
- return returns
+ if(cultslurring)
+ S.message = cultslur(S.message)
+ verb = "slurs"
-/mob/living/proc/handle_message_mode(message_mode, message, verb, speaking, used_radios)
+ if(!IsVocal())
+ S.message = ""
+ return list("verb" = verb)
+
+/mob/living/proc/handle_message_mode(message_mode, list/message_pieces, verb, used_radios)
switch(message_mode)
if("whisper") //all mobs can whisper by default
- whisper_say(message, speaking)
+ whisper_say(message_pieces)
return 1
return 0
@@ -109,7 +106,7 @@ proc/get_radio_key_from_channel(var/channel)
return returns
-/mob/living/say(var/message, var/datum/language/speaking = null, var/verb = "says", var/sanitize = TRUE, var/ignore_speech_problems = FALSE, var/ignore_atmospherics = FALSE)
+/mob/living/say(var/message, var/verb = "says", var/sanitize = TRUE, var/ignore_speech_problems = FALSE, var/ignore_atmospherics = FALSE)
if(client)
if(client.prefs.muted & MUTE_IC)
to_chat(src, "You cannot speak in IC (Muted).")
@@ -138,19 +135,17 @@ proc/get_radio_key_from_channel(var/channel)
message = trim_left(message)
//parse the language code and consume it
- if(!speaking)
- speaking = parse_language(message)
- if(speaking)
- message = copytext(message, 2 + length(speaking.key))
- else
- speaking = get_default_language()
-
- // This is broadcast to all mobs with the language,
- // irrespective of distance or anything else.
- if(speaking && (speaking.flags & HIVEMIND))
- speaking.broadcast(src,trim(message))
+ var/list/message_pieces = parse_languages(message)
+ if(istype(message_pieces, /datum/multilingual_say_piece)) // Little quirk to just easily deal with HIVEMIND languages
+ var/datum/multilingual_say_piece/S = message_pieces // Yay BYOND's hilarious typecasting
+ S.speaking.broadcast(src, S.message)
return 1
+
+ if(!LAZYLEN(message_pieces))
+ log_runtime(EXCEPTION("Message failed to generate pieces. [message] - [json_encode(message_pieces)]"))
+ return 0
+
if(message_mode == "cords")
if(iscarbon(src))
var/mob/living/carbon/C = src
@@ -160,7 +155,8 @@ proc/get_radio_key_from_channel(var/channel)
V.speak_with(message) //words come before actions
return 1
- verb = say_quote(message, speaking)
+ var/datum/multilingual_say_piece/first_piece = message_pieces[1]
+ verb = say_quote(message, first_piece.speaking)
if(is_muzzled())
var/obj/item/clothing/mask/muzzle/G = wear_mask
@@ -168,25 +164,19 @@ proc/get_radio_key_from_channel(var/channel)
to_chat(src, "You're muzzled and cannot speak!")
return
else if(G.mute == MUZZLE_MUTE_MUFFLE)
- message = muffledspeech(message)
+ muffledspeech_all(message_pieces)
verb = "mumbles"
- message = trim_left(message)
+ if(!ignore_speech_problems)
+ var/list/hsp = handle_speech_problems(message_pieces, verb)
+ verb = hsp["verb"]
- message = handle_autohiss(message, speaking)
-
- if(!ignore_speech_problems && (speaking && !(speaking.flags & NO_STUTTER)))
- var/list/handle_s = handle_speech_problems(message, verb)
- message = handle_s[1]
- verb = handle_s[2]
-
- if(!message || message == "")
- return 0
var/list/used_radios = list()
- if(handle_message_mode(message_mode, message, verb, speaking, used_radios))
+ if(handle_message_mode(message_mode, message_pieces, verb, used_radios))
return 1
+
var/list/handle_v = handle_speech_sound()
var/sound/speech_sound = handle_v[1]
var/sound_vol = handle_v[2]
@@ -198,11 +188,12 @@ proc/get_radio_key_from_channel(var/channel)
if(used_radios.len)
italics = 1
message_range = 1
- if(speaking)
- message_range = speaking.get_talkinto_msg_range(message)
+ if(first_piece.speaking)
+ message_range = first_piece.speaking.get_talkinto_msg_range(message)
+
var/msg
- if(!speaking || !(speaking.flags & NO_TALK_MSG))
- msg = "\The [src] talks into \the [used_radios[1]]"
+ if(!first_piece.speaking || !(first_piece.speaking.flags & NO_TALK_MSG))
+ msg = "[src] talks into [used_radios[1]]"
if(msg)
for(var/mob/living/M in hearers(5, src) - src)
@@ -213,16 +204,6 @@ proc/get_radio_key_from_channel(var/channel)
var/turf/T = get_turf(src)
-
- //handle nonverbal and sign languages here
- if(speaking)
- if(speaking.flags & NONVERBAL)
- if(prob(30))
- custom_emote(1, "[pick(speaking.signlang_verb)].")
-
- if(speaking.flags & SIGNLANG)
- return say_signlang(message, pick(speaking.signlang_verb), speaking)
-
var/list/listening = list()
var/list/listening_obj = list()
@@ -268,7 +249,7 @@ proc/get_radio_key_from_channel(var/channel)
var/speech_bubble_test = say_test(message)
for(var/mob/M in listening)
- M.hear_say(message, verb, speaking, italics, src, speech_sound, sound_vol)
+ M.hear_say(message_pieces, verb, italics, src, speech_sound, sound_vol)
if(M.client)
speech_bubble_recipients.Add(M.client)
spawn(0)
@@ -281,19 +262,13 @@ proc/get_radio_key_from_channel(var/channel)
for(var/obj/O in listening_obj)
spawn(0)
if(O) //It's possible that it could be deleted in the meantime.
- O.hear_talk(src, message, verb, speaking)
+ O.hear_talk(src, message_pieces, verb)
//Log of what we've said, plain message, no spans or junk
say_log += message
-
log_say(message, src)
return 1
-/mob/living/proc/say_signlang(var/message, var/verb="gestures", var/datum/language/language)
- for(var/mob/O in viewers(src, null))
- O.hear_signlang(message, verb, language, src)
- return 1
-
/obj/effect/speech_bubble
var/mob/parent
@@ -338,28 +313,19 @@ proc/get_radio_key_from_channel(var/channel)
message = trim_strip_html_properly(message)
//parse the language code and consume it
- var/datum/language/speaking = parse_language(message)
- if(speaking)
- message = copytext(message, 2 + length(speaking.key))
- else
- speaking = get_default_language()
-
- // This is broadcast to all mobs with the language,
- // irrespective of distance or anything else.
- if(speaking && (speaking.flags & HIVEMIND))
- speaking.broadcast(src,trim(message))
+ var/list/message_pieces = parse_languages(message)
+ if(istype(message_pieces, /datum/multilingual_say_piece)) // Little quirk to just easily deal with HIVEMIND languages
+ var/datum/multilingual_say_piece/S = message_pieces // Yay BYOND's hilarious typecasting
+ S.speaking.broadcast(src, S.message)
return 1
- message = trim_left(message)
- message = handle_autohiss(message, speaking)
-
- whisper_say(message, speaking)
+ whisper_say(message_pieces)
// for weird circumstances where you're inside an atom that is also you, like pai's
/mob/living/proc/get_whisper_loc()
return src
-/mob/living/proc/whisper_say(var/message, var/datum/language/speaking = null, var/verb="whispers")
+/mob/living/proc/whisper_say(list/message_pieces, verb = "whispers")
if(client)
if(client.prefs.muted & MUTE_IC)
to_chat(src, "You cannot speak in IC (Muted).")
@@ -367,7 +333,7 @@ proc/get_radio_key_from_channel(var/channel)
if(stat)
if(stat == DEAD)
- return say_dead(message)
+ return say_dead(message_pieces)
return
if(is_muzzled())
@@ -382,37 +348,29 @@ proc/get_radio_key_from_channel(var/channel)
var/watching_range = 5
var/italics = 1
var/adverb_added = FALSE
-
var/not_heard //the message displayed to people who could not hear the whispering
- if(speaking)
- if(speaking.whisper_verb)
- verb = speaking.whisper_verb
+
+ var/datum/multilingual_say_piece/first_piece = message_pieces[1]
+ if(first_piece.speaking)
+ if(first_piece.speaking.whisper_verb)
+ verb = first_piece.speaking.whisper_verb
not_heard = "[verb] something"
else
var/adverb = pick("quietly", "softly")
adverb_added = TRUE
- verb = "[speaking.speech_verb] [adverb]"
- not_heard = "[speaking.speech_verb] something [adverb]"
+ verb = "[first_piece.speaking.speech_verb] [adverb]"
+ not_heard = "[first_piece.speaking.speech_verb] something [adverb]"
else
not_heard = "[verb] something"
- message = trim(message)
-
- var/speech_problem_flag = 0
- var/list/handle_s = handle_speech_problems(message, verb)
- message = handle_s[1]
- verb = handle_s[2]
- speech_problem_flag = handle_s[3]
+ var/list/hsp = handle_speech_problems(message_pieces, verb)
+ verb = hsp["verb"]
if(verb == "yells loudly")
verb = "slurs emphatically"
-
- else if(speech_problem_flag && !adverb_added)
+ else if(!adverb_added)
var/adverb = pick("quietly", "softly")
verb = "[verb] [adverb]"
- if(!message)
- return
-
var/atom/whisper_loc = get_whisper_loc()
var/list/listening = hear(message_range, whisper_loc)
listening |= src
@@ -450,7 +408,7 @@ proc/get_radio_key_from_channel(var/channel)
for(var/obj/O in view(message_range, whisper_loc))
spawn(0)
if(O)
- O.hear_talk(src, message, verb, speaking)
+ O.hear_talk(src, message_pieces, verb)
var/list/eavesdropping = hearers(eavesdropping_range, whisper_loc)
eavesdropping -= src
@@ -463,17 +421,17 @@ proc/get_radio_key_from_channel(var/channel)
//now mobs
var/list/speech_bubble_recipients = list()
- var/speech_bubble_test = say_test(message)
+ var/speech_bubble_test = say_test(multilingual_to_message(message_pieces))
for(var/mob/M in listening)
- M.hear_say(message, verb, speaking, italics, src)
+ M.hear_say(message_pieces, verb, italics, src)
if(M.client)
speech_bubble_recipients.Add(M.client)
if(eavesdropping.len)
- var/new_message = stars(message) //hopefully passing the message twice through stars() won't hurt... I guess if you already don't understand the language, when they speak it too quietly to hear normally you would be able to catch even less.
+ stars_all(message_pieces) //hopefully passing the message twice through stars() won't hurt... I guess if you already don't understand the language, when they speak it too quietly to hear normally you would be able to catch even less.
for(var/mob/M in eavesdropping)
- M.hear_say(new_message, verb, speaking, italics, src)
+ M.hear_say(message_pieces, verb, italics, src)
if(M.client)
speech_bubble_recipients.Add(M.client)
@@ -487,7 +445,7 @@ proc/get_radio_key_from_channel(var/channel)
for(var/mob/M in watching)
M.show_message(rendered, 2)
- log_whisper(message, src)
+ log_whisper(multilingual_to_message(message_pieces), src)
return 1
/mob/living/speech_bubble(var/bubble_state = "",var/bubble_loc = src, var/list/bubble_recipients = list())
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index 67bbc6c5388..a6400142086 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -1184,19 +1184,11 @@ var/list/ai_verbs_default = list(
var/list/viewscale = getviewsize(client.view)
return get_dist(src, A) <= max(viewscale[1]*0.5,viewscale[2]*0.5)
-/mob/living/silicon/ai/proc/relay_speech(mob/living/M, text, verb, datum/language/speaking)
- if(!say_understands(M, speaking))//The AI will be able to understand most mobs talking through the holopad.
- if(speaking)
- text = speaking.scramble(text)
- else
- text = stars(text)
+/mob/living/silicon/ai/proc/relay_speech(mob/living/M, list/message_pieces, verb)
+ var/message = combine_message(message_pieces, verb, M)
var/name_used = M.GetVoice()
//This communication is imperfect because the holopad "filters" voices and is only designed to connect to the master only.
- var/rendered
- if(speaking)
- rendered = "Relayed Speech: [name_used] [speaking.format_message(text, verb)]"
- else
- rendered = "Relayed Speech: [name_used] [verb], \"[text]\""
+ var/rendered = "Relayed Speech: [name_used] [message]"
show_message(rendered, 2)
/mob/living/silicon/ai/proc/malfhacked(obj/machinery/power/apc/apc)
diff --git a/code/modules/mob/living/silicon/ai/freelook/eye.dm b/code/modules/mob/living/silicon/ai/freelook/eye.dm
index 1037ec7fe61..98d010b878c 100644
--- a/code/modules/mob/living/silicon/ai/freelook/eye.dm
+++ b/code/modules/mob/living/silicon/ai/freelook/eye.dm
@@ -141,6 +141,10 @@
acceleration = !acceleration
to_chat(usr, "Camera acceleration has been toggled [acceleration ? "on" : "off"].")
-/mob/camera/aiEye/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol)
+/mob/camera/aiEye/hear_say(list/message_pieces, var/verb = "says", var/italics = 0, var/mob/speaker = null, var/sound/speech_sound, var/sound_vol)
if(relay_speech)
- ai.relay_speech(speaker, message, verb, language)
+ if(istype(ai))
+ ai.relay_speech(speaker, message_pieces, verb)
+ else
+ var/mob/M = ai
+ M.hear_say(message_pieces, verb, italics, speaker, speech_sound, sound_vol)
diff --git a/code/modules/mob/living/silicon/ai/say.dm b/code/modules/mob/living/silicon/ai/say.dm
index 431cf44a331..6b1306f94d1 100644
--- a/code/modules/mob/living/silicon/ai/say.dm
+++ b/code/modules/mob/living/silicon/ai/say.dm
@@ -3,7 +3,7 @@
*/
-/mob/living/silicon/ai/handle_track(var/message, var/verb = "says", var/datum/language/language, var/mob/speaker = null, var/speaker_name, var/atom/follow_target, var/hard_to_hear)
+/mob/living/silicon/ai/handle_track(var/message, var/verb = "says", var/mob/speaker = null, var/speaker_name, var/atom/follow_target, var/hard_to_hear)
if(hard_to_hear)
return
diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm
index 76ac7ab9886..2ec1505741b 100644
--- a/code/modules/mob/living/silicon/robot/drone/drone.dm
+++ b/code/modules/mob/living/silicon/robot/drone/drone.dm
@@ -17,7 +17,6 @@
ventcrawler = 2
magpulse = 1
mob_size = MOB_SIZE_SMALL
- default_language = "Drone"
// We need to keep track of a few module items so we don't need to do list operations
// every time we need them. These get set in New() after the module is chosen.
@@ -38,7 +37,6 @@
/mob/living/silicon/robot/drone/New()
-
..()
remove_language("Robot Talk")
diff --git a/code/modules/mob/living/silicon/robot/drone/drone_say.dm b/code/modules/mob/living/silicon/robot/drone/drone_say.dm
index 741207d6f2d..7b51952a685 100644
--- a/code/modules/mob/living/silicon/robot/drone/drone_say.dm
+++ b/code/modules/mob/living/silicon/robot/drone/drone_say.dm
@@ -1,15 +1,14 @@
-/mob/living/silicon/robot/drone/say(var/message, var/datum/language/speaking = null)
+/mob/living/silicon/robot/drone/say(var/message)
if(copytext(message, 1, 2) == "*")
return emote(copytext(message, 2))
- if(!speaking)
- speaking = parse_language(message)
- if(!speaking)
- speaking = istype(get_default_language(), /datum/language) ? get_default_language() : GLOB.all_languages[get_default_language()]
- message = speaking.key + " " + message; // Prepend key to prevent the message from getting trimmed
- if(speaking)
- return ..()
+ return ..()
-/mob/living/silicon/robot/drone/whisper_say(var/message, var/datum/language/speaking = null, var/verb="whispers")
- say(message) //drones do not get to whisper, only speak normally
- return 1
\ No newline at end of file
+/mob/living/silicon/robot/drone/whisper_say(list/message_pieces)
+ say(multilingual_to_message(message_pieces)) //drones do not get to whisper, only speak normally
+ return 1
+
+/mob/living/silicon/robot/drone/get_default_language()
+ if(default_language)
+ return default_language
+ return GLOB.all_languages["Drone"]
\ No newline at end of file
diff --git a/code/modules/mob/living/silicon/say.dm b/code/modules/mob/living/silicon/say.dm
index 48297dbb401..17a7e67adf7 100644
--- a/code/modules/mob/living/silicon/say.dm
+++ b/code/modules/mob/living/silicon/say.dm
@@ -1,9 +1,9 @@
-/mob/living/silicon/handle_message_mode(message_mode, message, verb, speaking, used_radios)
- log_say(message, src)
+/mob/living/silicon/handle_message_mode(message_mode, list/message_pieces, verb, used_radios)
+ log_say(multilingual_to_message(message_pieces), src)
if(..())
return 1
-/mob/living/silicon/robot/handle_message_mode(message_mode, message, verb, speaking, used_radios)
+/mob/living/silicon/robot/handle_message_mode(message_mode, list/message_pieces, verb, used_radios)
if(..())
return 1
if(message_mode)
@@ -13,14 +13,14 @@
return 0
if(message_mode == "general")
message_mode = null
- return radio.talk_into(src,message,message_mode,verb,speaking)
+ return radio.talk_into(src,message_pieces,message_mode,verb)
-/mob/living/silicon/ai/handle_message_mode(message_mode, message, verb, speaking, used_radios)
+/mob/living/silicon/ai/handle_message_mode(message_mode, list/message_pieces, verb, used_radios)
if(..())
return 1
if(message_mode == "department")
used_radios += aiRadio
- return holopad_talk(message, verb, speaking)
+ return holopad_talk(message_pieces, verb)
else if(message_mode)
used_radios += aiRadio
if(aiRadio.disabledAi || aiRestorePowerRoutine || stat)
@@ -28,19 +28,19 @@
return 0
if(message_mode == "general")
message_mode = null
- return aiRadio.talk_into(src,message,message_mode,verb,speaking)
+ return aiRadio.talk_into(src, message_pieces, message_mode, verb)
-/mob/living/silicon/pai/handle_message_mode(message_mode, message, verb, speaking, used_radios)
+/mob/living/silicon/pai/handle_message_mode(message_mode, list/message_pieces, verb, used_radios)
if(..())
return 1
else if(message_mode == "whisper")
- whisper_say(message, speaking)
+ whisper_say(message_pieces)
return 1
else if(message_mode)
if(message_mode == "general")
message_mode = null
used_radios += radio
- return radio.talk_into(src,message,message_mode,verb,speaking)
+ return radio.talk_into(src, message_pieces, message_mode, verb)
/mob/living/silicon/say_quote(var/text)
var/ending = copytext(text, length(text))
@@ -70,41 +70,14 @@
return ..()
//For holopads only. Usable by AI.
-/mob/living/silicon/ai/proc/holopad_talk(var/message, verb, datum/language/speaking)
- log_say("(HPAD) [message]", src)
-
- message = trim(message)
-
- if(!message)
- return
+/mob/living/silicon/ai/proc/holopad_talk(list/message_pieces, verb)
+ log_say("(HPAD) [multilingual_to_message(message_pieces)]", src)
var/obj/machinery/hologram/holopad/T = current
if(istype(T) && T.masters[src])
-
- //Human-like, sorta, heard by those who understand humans.
- var/rendered_a
- //Speach distorted, heard by those who do not understand AIs.
- var/message_stars = stars(message)
- var/rendered_b
-
- if(speaking)
- rendered_a = "[name] [speaking.format_message(message, verb)]"
- rendered_b = "[voice_name] [speaking.format_message(message_stars, verb)]"
- to_chat(src, "Holopad transmitted, [real_name] [speaking.format_message(message, verb)]")//The AI can "hear" its own message.
-
- else
- rendered_a = "[name] [verb], \"[message]\""
- rendered_b = "[voice_name] [verb], \"[message_stars]\""
- to_chat(src, "Holopad transmitted, [real_name] [verb], \"[message]\"")//The AI can "hear" its own message.
-
-
for(var/mob/M in hearers(T.loc))//The location is the object, default distance.
- if(M.say_understands(src))//If they understand AI speak. Humans and the like will be able to.
- M.show_message(rendered_a, 2)
- else//If they do not.
- M.show_message(rendered_b, 2)
- /*Radios "filter out" this conversation channel so we don't need to account for them.
- This is another way of saying that we won't bother dealing with them.*/
+ M.hear_holopad_talk(message_pieces, verb, src)
+ to_chat(src, "Holopad transmitted, [real_name] [combine_message(message_pieces, verb, src)]")
else
to_chat(src, "No holopad connected.")
return
diff --git a/code/modules/mob/living/simple_animal/bot/medbot.dm b/code/modules/mob/living/simple_animal/bot/medbot.dm
index 070a7d60e84..1c7cb902be3 100644
--- a/code/modules/mob/living/simple_animal/bot/medbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/medbot.dm
@@ -65,6 +65,9 @@
/mob/living/simple_animal/bot/medbot/fish
skin = "fish"
+/mob/living/simple_animal/bot/medbot/machine
+ skin = "machine"
+
/mob/living/simple_animal/bot/medbot/mysterious
name = "\improper Mysterious Medibot"
desc = "International Medibot of mystery."
@@ -578,6 +581,8 @@
T.syndicate_aligned = syndicate_aligned //This is a special case since Syndicate medibots and the mysterious medibot look the same; we also dont' want crew building Syndicate medibots if the mysterious medibot blows up.
if("fish")
new /obj/item/storage/firstaid/aquatic_kit(Tsec)
+ if("machine")
+ new /obj/item/storage/firstaid/machine/empty(Tsec)
else
new /obj/item/storage/firstaid(Tsec)
diff --git a/code/modules/mob/living/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm
index 3ed45508b87..5ebaf47afb2 100644
--- a/code/modules/mob/living/simple_animal/friendly/mouse.dm
+++ b/code/modules/mob/living/simple_animal/friendly/mouse.dm
@@ -33,6 +33,9 @@
can_collar = 1
gold_core_spawnable = CHEM_MOB_SPAWN_FRIENDLY
+/mob/living/simple_animal/mouse/Initialize()
+ . = ..()
+
/mob/living/simple_animal/mouse/handle_automated_speech()
..()
if(prob(speak_chance))
@@ -81,39 +84,16 @@
get_scooped(M)
..()
-//make mice fit under tables etc? this was hacky, and not working
-/*
-/mob/living/simple_animal/mouse/Move(var/dir)
-
- var/turf/target_turf = get_step(src,dir)
- //CanReachThrough(src.loc, target_turf, src)
- var/can_fit_under = 0
- if(target_turf.ZCanPass(get_turf(src),1))
- can_fit_under = 1
-
- ..(dir)
- if(can_fit_under)
- src.loc = target_turf
- for(var/d in cardinal)
- var/turf/O = get_step(T,d)
- //Simple pass check.
- if(O.ZCanPass(T, 1) && !(O in open) && !(O in closed) && O in possibles)
- open += O
- */
-
-///mob/living/simple_animal/mouse/restrained() //Hotfix to stop mice from doing things with MouseDrop
-// return 1
-
/mob/living/simple_animal/mouse/start_pulling(var/atom/movable/AM)//Prevents mouse from pulling things
to_chat(src, "You are too small to pull anything.")
return
/mob/living/simple_animal/mouse/Crossed(AM as mob|obj)
- if( ishuman(AM) )
- if(!stat)
+ if(ishuman(AM))
+ if(stat == CONSCIOUS)
var/mob/M = AM
to_chat(M, "[bicon(src)] Squeek!")
- M << 'sound/effects/mousesqueek.ogg'
+ SEND_SOUND(M, 'sound/effects/mousesqueek.ogg')
..()
/mob/living/simple_animal/mouse/death(gibbed)
diff --git a/code/modules/mob/living/simple_animal/hostile/bat.dm b/code/modules/mob/living/simple_animal/hostile/bat.dm
index 2bb792fbb81..1e8d5eeb609 100644
--- a/code/modules/mob/living/simple_animal/hostile/bat.dm
+++ b/code/modules/mob/living/simple_animal/hostile/bat.dm
@@ -60,4 +60,18 @@
if(istype(L))
if(prob(15))
L.Stun(1)
- L.visible_message("\the [src] scares \the [L]!")
\ No newline at end of file
+ L.visible_message("\the [src] scares \the [L]!")
+
+
+/mob/living/simple_animal/hostile/scarybat/batswarm
+ name = "bat swarm"
+ desc = "A swarm of vicious, angry-looking space bats."
+ speed = 1
+ harm_intent_damage = 25
+ maxHealth = 300
+ melee_damage_lower = 10
+ melee_damage_upper = 30
+ a_intent = INTENT_HARM
+ pass_flags = PASSTABLE
+ universal_speak = 1
+ universal_understand = 1
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_animal/hostile/hellhound.dm b/code/modules/mob/living/simple_animal/hostile/hellhound.dm
new file mode 100644
index 00000000000..65a2391acb1
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/hostile/hellhound.dm
@@ -0,0 +1,131 @@
+// Hellhound
+/mob/living/simple_animal/hostile/hellhound
+ // Sprites by FoS: http://nanotrasen.se/phpBB3/memberlist.php?mode=viewprofile&u=386
+ name = "Lesser Hellhound"
+ desc = "A horrifying, black canine monster, with glowing red eyes and vicious-looking teeth. A firey, lava-like substance drips from it."
+ icon_state = "hellhound"
+ icon_living = "hellhound"
+ icon_dead = "hellhound_dead"
+ icon_resting = "hellhound_rest"
+ mutations = list(BREATHLESS)
+ gold_core_spawnable = CHEM_MOB_SPAWN_HOSTILE
+ atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
+ minbodytemp = 0
+ maxbodytemp = INFINITY
+ melee_damage_lower = 10 // slightly higher than araneus
+ melee_damage_upper = 30
+ a_intent = INTENT_HARM
+ environment_smash = 1
+ speak_chance = 0
+ speed = 0
+ maxHealth = 250 // same as sgt araneus
+ health = 250
+ obj_damage = 50
+ robust_searching = 1
+ stat_attack = 1
+ attacktext = "savages"
+ attack_sound = 'sound/effects/bite.ogg'
+ speak_emote = list("growls")
+ see_in_dark = 9
+ universal_understand = 1
+ wander = 0
+ var/life_regen_cycles = 0
+ var/life_regen_cycle_trigger = 10 // heal once for every X number of cycles spent resting
+ var/life_regen_amount = -10 // negative, because negative = healing
+ var/smoke_lastuse = 0
+ var/smoke_freq = 300 // 30 seconds
+ var/datum/action/innate/demon/whisper/whisper_action
+
+/mob/living/simple_animal/hostile/hellhound/New()
+ . = ..()
+ whisper_action = new()
+ whisper_action.Grant(src)
+
+/mob/living/simple_animal/hostile/hellhound/handle_automated_action()
+ . = ..()
+ if(resting)
+ if(!wants_to_rest())
+ custom_emote(1, "growls, and gets up.")
+ playsound(get_turf(src), 'sound/hallucinations/growl2.ogg', 50, 1)
+ icon_state = "[icon_living]"
+ resting = 0
+ update_canmove()
+ else if(wants_to_rest())
+ custom_emote(1, "lays down, and starts to lick their wounds.")
+ icon_state = "[icon_resting]"
+ resting = 1
+ update_canmove()
+
+/mob/living/simple_animal/hostile/hellhound/examine(mob/user)
+ . = ..()
+ if(stat != DEAD)
+ var/list/msgs = list()
+ if(key)
+ msgs += "Its eyes have the spark of intelligence."
+ if(health > (maxHealth*0.95))
+ msgs += "It appears to be in excellent health."
+ else if(health > (maxHealth*0.75))
+ msgs += "It has a few injuries."
+ else if(health > (maxHealth*0.55))
+ msgs += "It has many injuries."
+ else if(health > (maxHealth*0.25))
+ msgs += "It is covered in wounds!"
+ if(resting)
+ if(bruteloss > 0 || fireloss > 0)
+ msgs += "It is currently licking its wounds, regenerating the damage to its body!"
+ else
+ msgs += "It is currently resting."
+ to_chat(usr,msgs.Join(" "))
+
+/mob/living/simple_animal/hostile/hellhound/Life(seconds, times_fired)
+ . = ..()
+ if(stat != DEAD && resting && (bruteloss > 0) || (fireloss > 0))
+ if(life_regen_cycles >= life_regen_cycle_trigger)
+ life_regen_cycles = 0
+ to_chat(src, "You lick your wounds, helping them close.")
+ adjustBruteLoss(life_regen_amount)
+ adjustFireLoss(life_regen_amount)
+ else
+ life_regen_cycles++
+
+/mob/living/simple_animal/hostile/hellhound/proc/wants_to_rest()
+ if(target)
+ return FALSE
+ if(bruteloss > 0 || fireloss > 0)
+ return TRUE
+ return FALSE
+
+/mob/living/simple_animal/hostile/hellhound/AttackingTarget()
+ . = ..()
+ if(ishuman(target))
+ special_aoe()
+
+/mob/living/simple_animal/hostile/hellhound/attackby(obj/item/C, mob/user, params)
+ . = ..()
+ if(target && isliving(target))
+ var/mob/living/L = target
+ if(L.stat != CONSCIOUS)
+ target = user
+
+/mob/living/simple_animal/hostile/hellhound/proc/special_aoe()
+ if(world.time < (smoke_lastuse + smoke_freq))
+ return
+ smoke_lastuse = world.time
+ var/datum/effect_system/smoke_spread/sleeping/smoke = new
+ smoke.set_up(10, 0, loc)
+ smoke.start()
+
+/mob/living/simple_animal/hostile/hellhound/greater
+ name = "Greater Hellhound"
+ icon_state = "hellhoundgreater"
+ icon_living = "hellhoundgreater"
+ icon_resting = "hellhoundgreater_sit"
+ maxHealth = 400
+ health = 400
+ force_threshold = 5 // no punching
+ smoke_freq = 200
+ life_regen_cycle_trigger = 5
+ melee_damage_lower = 20
+ melee_damage_upper = 30
+ environment_smash = 2
+ gold_core_spawnable = CHEM_MOB_SPAWN_INVALID
diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm
index a60ae1d323e..c8e43a936d2 100644
--- a/code/modules/mob/living/simple_animal/parrot.dm
+++ b/code/modules/mob/living/simple_animal/parrot.dm
@@ -704,21 +704,21 @@
available_channels = list(":e")
..()
-/mob/living/simple_animal/parrot/handle_message_mode(var/message_mode, var/message, var/verb, var/speaking, var/used_radios)
+/mob/living/simple_animal/parrot/handle_message_mode(var/message_mode, list/message_pieces, var/verb, var/used_radios)
if(message_mode && istype(ears))
- ears.talk_into(src, message, message_mode, verb, speaking)
+ ears.talk_into(src, message_pieces, message_mode, verb)
used_radios += ears
-/mob/living/simple_animal/parrot/hear_say(var/message, var/verb = "says", var/datum/language/language = null, var/italics = 0, var/mob/speaker = null)
+/mob/living/simple_animal/parrot/hear_say(list/message_pieces, var/verb = "says", var/italics = 0, var/mob/speaker = null)
if(speaker != src && prob(50))
- parrot_hear(html_decode(message))
+ parrot_hear(html_decode(multilingual_to_message(message_pieces)))
..()
-/mob/living/simple_animal/parrot/hear_radio(var/message, var/verb="says", var/datum/language/language=null, var/part_a, var/part_b, var/mob/speaker = null, var/hard_to_hear = 0, var/atom/follow_target)
+/mob/living/simple_animal/parrot/hear_radio(list/message_pieces, var/verb="says", var/part_a, var/part_b, var/mob/speaker = null, var/hard_to_hear = 0, var/atom/follow_target)
if(speaker != src && prob(50))
- parrot_hear(html_decode(message))
+ parrot_hear(html_decode(multilingual_to_message(message_pieces)))
..()
diff --git a/code/modules/mob/living/update_status.dm b/code/modules/mob/living/update_status.dm
index 94769baf94b..daf058bb41b 100644
--- a/code/modules/mob/living/update_status.dm
+++ b/code/modules/mob/living/update_status.dm
@@ -44,7 +44,7 @@
. = !(disabilities & DEAF)
// Whether the mob is able to see
-// `information_only` is for stuff that's purely informational - like understanding nonverbal languages, or blindness overlays
+// `information_only` is for stuff that's purely informational - like blindness overlays
// This flag exists because certain things like angel statues expect this to be false for dead people
/mob/living/has_vision(information_only = FALSE)
return (information_only && stat == DEAD) || !(eye_blind || (disabilities & BLIND) || stat)
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index 659a50880a0..46b7d1141b8 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -177,6 +177,10 @@
p++
return t
+/proc/stars_all(list/message_pieces, pr)
+ for(var/datum/multilingual_say_piece/S in message_pieces)
+ S.message = stars(S.message, pr)
+
/proc/slur(phrase, var/list/slurletters = ("'"))//use a different list as an input if you want to make robots slur with $#@%! characters
phrase = html_decode(phrase)
var/leng=lentext(phrase)
@@ -266,6 +270,11 @@
return returntext
+/proc/Gibberish_all(list/message_pieces, p)
+ for(var/datum/multilingual_say_piece/S in message_pieces)
+ S.message = Gibberish(S.message, p)
+
+
proc/muffledspeech(phrase)
phrase = html_decode(phrase)
var/leng=lentext(phrase)
@@ -284,6 +293,10 @@ proc/muffledspeech(phrase)
counter-=1
return newphrase
+/proc/muffledspeech_all(list/message_pieces)
+ for(var/datum/multilingual_say_piece/S in message_pieces)
+ S.message = muffledspeech(S.message)
+
/proc/shake_camera(mob/M, duration, strength=1)
if(!M || !M.client || M.shakecamera)
diff --git a/code/modules/mob/new_player/sprite_accessories/unathi/unathi_head_markings.dm b/code/modules/mob/new_player/sprite_accessories/unathi/unathi_head_markings.dm
index 25d74d605e2..b410148b0c9 100644
--- a/code/modules/mob/new_player/sprite_accessories/unathi/unathi_head_markings.dm
+++ b/code/modules/mob/new_player/sprite_accessories/unathi/unathi_head_markings.dm
@@ -32,6 +32,7 @@
/datum/sprite_accessory/body_markings/head/unathi/points_una //Companion marking for Unathi Points.
name = "Unathi Points Head"
icon_state = "pointsface"
+ heads_allowed = list("All")
/datum/sprite_accessory/body_markings/head/unathi/sharp
heads_allowed = list("Unathi Sharp Snout")
@@ -47,7 +48,3 @@
/datum/sprite_accessory/body_markings/head/unathi/sharp/snout_narrow_una_sharp //Companion marking for Unathi Narrow Belly.
name = "Unathi Sharp Snout 2"
icon_state = "facesharp"
-
-/datum/sprite_accessory/body_markings/head/unathi/sharp/points_una_sharp //Companion marking for Unathi Points.
- name = "Unathi Sharp Points Head"
- icon_state = "pointsface"
\ No newline at end of file
diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm
index a1236e9c719..aeb6d8f14bf 100644
--- a/code/modules/mob/say.dm
+++ b/code/modules/mob/say.dm
@@ -132,17 +132,77 @@
return null
-//parses the language code (e.g. :j) from text, such as that supplied to say.
-//returns the language object only if the code corresponds to a language that src can speak, otherwise null.
-/mob/proc/parse_language(var/message)
- var/prefix = copytext(message, 1, 2)
- if(length(message) >= 1 && prefix == "!")
- return GLOB.all_languages["Noise"]
+/datum/multilingual_say_piece
+ var/datum/language/speaking = null
+ var/message = ""
- if(length(message) >= 2)
- var/language_prefix = trim_right(lowertext(copytext(message, 1 ,4)))
- var/datum/language/L = GLOB.language_keys[language_prefix]
- if(can_speak_language(L))
- return L
+/datum/multilingual_say_piece/New(datum/language/new_speaking, new_message)
+ . = ..()
+ speaking = new_speaking
+ if(new_message)
+ message = new_message
- return null
+/mob/proc/find_valid_prefixes(message)
+ var/list/prefixes = list() // [["Common", start, end], ["Gutter", start, end]]
+ for(var/i in 1 to length(message))
+ var/selection = trim_right(lowertext(copytext(message, i, i + 3)))
+ var/datum/language/L = GLOB.language_keys[selection]
+ if(L != null && can_speak_language(L)) // What the fuck... remove the L != null check if you ever find out what the fuck is adding `null` to the languages list on absolutely random mobs... seriously what the hell...
+ prefixes[++prefixes.len] = list(L, i, i + length(selection))
+ else if(!L && i == 1)
+ prefixes[++prefixes.len] = list(get_default_language(), i, i)
+ else
+ return prefixes
+
+/proc/strip_prefixes(message)
+ . = ""
+ var/last_index = 1
+ for(var/i in 1 to length(message))
+ var/selection = trim_right(lowertext(copytext(message, i, i + 3)))
+ var/datum/language/L = GLOB.language_keys[selection]
+ if(L)
+ . += copytext(message, last_index, i)
+ last_index = i + 3
+ if(i + 1 > length(message))
+ . += copytext(message, last_index)
+
+// this returns a structured message with language sections
+// list(/datum/multilingual_say_piece(common, "hi"), /datum/multilingual_say_piece(farwa, "squik"), /datum/multilingual_say_piece(common, "meow!"))
+/mob/proc/parse_languages(message)
+ . = list()
+
+ // Noise language is a snowflake
+ if(copytext(message, 1, 2) == "!" && length(message) > 1)
+ return list(new /datum/multilingual_say_piece(GLOB.all_languages["Noise"], trim(strip_prefixes(copytext(message, 2)))))
+
+ // Scan the message for prefixes
+ var/list/prefix_locations = find_valid_prefixes(message)
+ if(!LAZYLEN(prefix_locations)) // There are no prefixes... or at least, no _valid_ prefixes.
+ . += new /datum/multilingual_say_piece(get_default_language(), trim(strip_prefixes(message))) // So we'll just strip those pesky things and still make the message.
+
+ for(var/i in 1 to length(prefix_locations))
+ var/current = prefix_locations[i] // ["Common", keypos]
+
+ // There are a few things that will make us want to ignore all other languages in - namely, HIVEMIND languages.
+ var/datum/language/L = current[1]
+ if(L && L.flags & HIVEMIND)
+ . = new /datum/multilingual_say_piece(L, trim(strip_prefixes(message)))
+ break
+
+ if(i + 1 > length(prefix_locations)) // We are out of lookaheads, that means the rest of the message is in cur lang
+ var/spoke_message = handle_autohiss(trim(copytext(message, current[3])), L)
+ . += new /datum/multilingual_say_piece(current[1], spoke_message)
+ else
+ var/next = prefix_locations[i + 1] // We look ahead at the next message to see where we need to stop.
+ var/spoke_message = handle_autohiss(trim(copytext(message, current[3], next[2])), L)
+ . += new /datum/multilingual_say_piece(current[1], spoke_message)
+
+/* These are here purely because it would be hell to try to convert everything over to using the multi-lingual system at once */
+/proc/message_to_multilingual(message, datum/language/speaking = null)
+ . = list(new /datum/multilingual_say_piece(speaking, message))
+
+/proc/multilingual_to_message(list/message_pieces)
+ . = ""
+ for(var/datum/multilingual_say_piece/S in message_pieces)
+ . += S.message + " "
+ . = trim_right(.)
\ No newline at end of file
diff --git a/code/modules/paperwork/faxmachine.dm b/code/modules/paperwork/faxmachine.dm
index cd0832bd714..47e98b8e1b9 100644
--- a/code/modules/paperwork/faxmachine.dm
+++ b/code/modules/paperwork/faxmachine.dm
@@ -2,6 +2,7 @@ var/list/obj/machinery/photocopier/faxmachine/allfaxes = list()
var/list/admin_departments = list("Central Command")
var/list/hidden_admin_departments = list("Syndicate")
var/list/alldepartments = list()
+var/global/list/fax_blacklist = list()
/obj/machinery/photocopier/faxmachine
name = "fax machine"
@@ -160,7 +161,9 @@ var/list/alldepartments = list()
if(href_list["auth"])
if(!is_authenticated && scan)
- if(check_access(scan))
+ if(scan.registered_name in fax_blacklist)
+ playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
+ else if(check_access(scan))
authenticated = 1
else if(is_authenticated)
authenticated = 0
@@ -322,3 +325,9 @@ var/list/alldepartments = list()
to_chat(C, msg)
if(C.prefs.sound & SOUND_ADMINHELP)
C << 'sound/effects/adminhelp.ogg'
+
+/obj/machinery/photocopier/faxmachine/proc/become_mimic()
+ if(scan)
+ scan.forceMove(get_turf(src))
+ var/mob/living/simple_animal/hostile/mimic/copy/M = new(loc, src, null, 1) // it will delete src on creation and override any machine checks
+ M.name = "angry fax machine"
\ No newline at end of file
diff --git a/code/modules/paperwork/frames.dm b/code/modules/paperwork/frames.dm
index 4203b5abfff..cbffb1cd117 100644
--- a/code/modules/paperwork/frames.dm
+++ b/code/modules/paperwork/frames.dm
@@ -297,10 +297,10 @@
toggle_tilt(usr)
-/obj/structure/sign/picture_frame/hear_talk(mob/living/M as mob, msg)
+/obj/structure/sign/picture_frame/hear_talk(mob/living/M as mob, list/message_pieces)
..()
for(var/obj/O in contents)
- O.hear_talk(M, msg)
+ O.hear_talk(M, message_pieces)
/obj/structure/sign/picture_frame/hear_message(mob/living/M as mob, msg)
..()
diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm
index 06921bce252..40cc2bcc534 100644
--- a/code/modules/paperwork/paper.dm
+++ b/code/modules/paperwork/paper.dm
@@ -596,6 +596,7 @@
var/used = 0
var/countdown = 60
var/activate_on_timeout = 0
+ var/faxmachineid = null
/obj/item/paper/evilfax/show_content(var/mob/user, var/forceshow = 0, var/forcestars = 0, var/infolinks = 0, var/view = 1)
if(user == mytarget)
@@ -642,7 +643,8 @@
/obj/item/paper/evilfax/proc/evilpaper_specialaction(var/mob/living/carbon/target)
spawn(30)
- if(istype(target,/mob/living/carbon))
+ if(istype(target, /mob/living/carbon))
+ var/obj/machinery/photocopier/faxmachine/fax = locateUID(faxmachineid)
if(myeffect == "Borgification")
to_chat(target,"You seem to comprehend the AI a little better. Why are your muscles so stiff?")
target.ForceContractDisease(new /datum/disease/transformation/robot(0))
@@ -668,8 +670,25 @@
var/mob/living/carbon/human/H = target
to_chat(H, "You feel surrounded by sadness. Sadness... and HONKS!")
H.makeCluwne()
- else if(myeffect == "Demotion Notice")
- event_announcement.Announce("[mytarget] is hereby demoted to the rank of Civilian. Process this demotion immediately. Failure to comply with these orders is grounds for termination.","CC Demotion Order")
+ else if(myeffect == "Demote")
+ event_announcement.Announce("[target.real_name] is hereby demoted to the rank of Civilian. Process this demotion immediately. Failure to comply with these orders is grounds for termination.","CC Demotion Order")
+ else if(myeffect == "Demote with Bot")
+ event_announcement.Announce("[target.real_name] is hereby demoted to the rank of Civilian. Process this demotion immediately. Failure to comply with these orders is grounds for termination.","CC Demotion Order")
+ for(var/datum/data/record/R in sortRecord(data_core.security))
+ if(R.fields["name"] == target.real_name)
+ R.fields["criminal"] = "*Arrest*"
+ update_all_mob_security_hud()
+ if(fax)
+ var/turf/T = get_turf(fax)
+ new /obj/effect/portal(T)
+ new /mob/living/simple_animal/bot/secbot(T)
+ else if(myeffect == "Revoke Fax Access")
+ fax_blacklist += target.real_name
+ if(fax)
+ fax.authenticated = 0
+ else if(myeffect == "Angry Fax Machine")
+ if(fax)
+ fax.become_mimic()
else
message_admins("Evil paper [src] was activated without a proper effect set! This is a bug.")
used = 1
diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm
index 3b37e551e0f..688bffa8ebc 100644
--- a/code/modules/paperwork/photography.dm
+++ b/code/modules/paperwork/photography.dm
@@ -541,7 +541,8 @@ var/list/SpookyGhosts = list("ghost","shade","shade2","ghost-narsie","horror","s
if(..(user, 1))
to_chat(user, "This video camera can send live feeds to the entertainment network. It's [camera ? "" : "in"]active.")
-/obj/item/videocam/hear_talk(mob/M as mob, msg)
+/obj/item/videocam/hear_talk(mob/M as mob, list/message_pieces)
+ var/msg = multilingual_to_message(message_pieces)
if(camera && on)
if(get_dist(src, M) <= canhear_range)
talk_into(M, msg)
diff --git a/code/modules/pda/utilities.dm b/code/modules/pda/utilities.dm
index a03a36e005a..4b2d02c33d4 100644
--- a/code/modules/pda/utilities.dm
+++ b/code/modules/pda/utilities.dm
@@ -109,8 +109,12 @@
if(A.reagents.reagent_list.len > 0)
var/reagents_length = A.reagents.reagent_list.len
to_chat(user, "[reagents_length] chemical agent[reagents_length > 1 ? "s" : ""] found.")
- for(var/re in A.reagents.reagent_list)
- to_chat(user, "\t [re]")
+ for(var/datum/reagent/R in A.reagents.reagent_list)
+ if(R.id != "blood")
+ to_chat(user, "\t [R]")
+ else
+ var/blood_type = R.data["blood_type"]
+ to_chat(user, "\t [R] [blood_type]")
else
to_chat(user, "No active chemical agents found in [A].")
else
diff --git a/code/modules/projectiles/ammunition/ammo_casings.dm b/code/modules/projectiles/ammunition/ammo_casings.dm
index 43112467f33..c525ef92ee1 100644
--- a/code/modules/projectiles/ammunition/ammo_casings.dm
+++ b/code/modules/projectiles/ammunition/ammo_casings.dm
@@ -29,6 +29,12 @@
icon_state = "r-casing"
projectile_type = /obj/item/projectile/bullet/weakbullet2
+/obj/item/ammo_casing/c38/invisible
+ projectile_type = /obj/item/projectile/bullet/weakbullet2/invisible
+
+/obj/item/ammo_casing/c38/invisible/fake
+ projectile_type = /obj/item/projectile/bullet/weakbullet2/invisible/fake
+
/obj/item/ammo_casing/c10mm
desc = "A 10mm bullet casing."
caliber = "10mm"
diff --git a/code/modules/projectiles/ammunition/magazines.dm b/code/modules/projectiles/ammunition/magazines.dm
index e621b1906b4..6d6e28092c1 100644
--- a/code/modules/projectiles/ammunition/magazines.dm
+++ b/code/modules/projectiles/ammunition/magazines.dm
@@ -61,6 +61,14 @@
caliber = "38"
max_ammo = 6
+/obj/item/ammo_box/magazine/internal/cylinder/rev38/invisible
+ name = "finger gun cylinder"
+ desc = "Wait, what?"
+ ammo_type = /obj/item/ammo_casing/c38/invisible
+
+/obj/item/ammo_box/magazine/internal/cylinder/rev38/invisible/fake
+ ammo_type = /obj/item/ammo_casing/c38/invisible/fake
+
/obj/item/ammo_box/magazine/internal/cylinder/rev762
name = "nagant revolver cylinder"
ammo_type = /obj/item/ammo_casing/n762
diff --git a/code/modules/projectiles/gun_attachments.dm b/code/modules/projectiles/gun_attachments.dm
new file mode 100644
index 00000000000..9a5156b4546
--- /dev/null
+++ b/code/modules/projectiles/gun_attachments.dm
@@ -0,0 +1,19 @@
+//Put all your tacticool gun gadgets here. So far it's pretty empty
+
+
+/obj/item/suppressor
+ name = "suppressor"
+ desc = "A universal syndicate small-arms suppressor for maximum espionage."
+ icon = 'icons/obj/guns/projectile.dmi'
+ icon_state = "suppressor"
+ item_state = "suppressor"
+ w_class = WEIGHT_CLASS_SMALL
+ var/oldsound = null
+ var/initial_w_class = null
+ origin_tech = "combat=2;engineering=2"
+
+/obj/item/suppressor/specialoffer
+ name = "cheap suppressor"
+ desc = "A foreign knock-off suppressor, it feels flimsy, cheap, and brittle. Still fits all weapons."
+ icon = 'icons/obj/guns/projectile.dmi'
+ icon_state = "suppressor"
diff --git a/code/modules/projectiles/guns/projectile.dm b/code/modules/projectiles/guns/projectile.dm
index 0ff5e57e7c5..39654c96a2b 100644
--- a/code/modules/projectiles/guns/projectile.dm
+++ b/code/modules/projectiles/guns/projectile.dm
@@ -183,20 +183,4 @@
for(var/obj/item/ammo_casing/AC in magazine.stored_ammo)
if(AC.BB)
process_fire(user, user,0)
- . = 1
-
-/obj/item/suppressor
- name = "suppressor"
- desc = "A universal syndicate small-arms suppressor for maximum espionage."
- icon = 'icons/obj/guns/projectile.dmi'
- icon_state = "suppressor"
- item_state = "suppressor"
- w_class = WEIGHT_CLASS_SMALL
- var/oldsound = null
- var/initial_w_class = null
-
-/obj/item/suppressor/specialoffer
- name = "cheap suppressor"
- desc = "A foreign knock-off suppressor, it feels flimsy, cheap, and brittle. Still fits all weapons."
- icon = 'icons/obj/guns/projectile.dmi'
- icon_state = "suppressor"
+ . = 1
\ No newline at end of file
diff --git a/code/modules/projectiles/guns/projectile/revolver.dm b/code/modules/projectiles/guns/projectile/revolver.dm
index c4c6f76a963..2f7a7fc1cd9 100644
--- a/code/modules/projectiles/guns/projectile/revolver.dm
+++ b/code/modules/projectiles/guns/projectile/revolver.dm
@@ -142,6 +142,48 @@
desc = initial(desc)
to_chat(user, "You remove the modifications on [src]. Now it will fire .38 rounds.")
+/obj/item/gun/projectile/revolver/fingergun //Summoned by the Finger Gun spell, from advanced mimery traitor item
+ name = "\improper finger gun"
+ desc = "Bang bang bang!"
+ icon_state = "fingergun"
+ mag_type = /obj/item/ammo_box/magazine/internal/cylinder/rev38/invisible
+ origin_tech = ""
+ flags = ABSTRACT | NODROP | DROPDEL
+ slot_flags = null
+ fire_sound = null
+ fire_sound_text = null
+ lefthand_file = null
+ righthand_file = null
+ clumsy_check = 0 //Stole your uplink! Honk!
+ needs_permit = 0 //go away beepsky
+
+/obj/item/gun/projectile/revolver/fingergun/fake
+ desc = "Pew pew pew!"
+ mag_type = /obj/item/ammo_box/magazine/internal/cylinder/rev38/invisible/fake
+
+/obj/item/gun/projectile/revolver/fingergun/New()
+ ..()
+ verbs -= /obj/item/gun/projectile/revolver/verb/spin
+
+/obj/item/gun/projectile/revolver/fingergun/shoot_with_empty_chamber(/*mob/living/user as mob|obj*/)
+ to_chat(usr, "You are out of ammo! You holster your fingers.")
+ qdel(src)
+ return
+
+/obj/item/gun/projectile/revolver/fingergun/afterattack(atom/target, mob/living/user, flag, params)
+ if(!user.mind.miming)
+ to_chat(usr, "You must dedicate yourself to silence first. Use your fingers if you wish to holster them.")
+ return
+ ..()
+
+/obj/item/gun/projectile/revolver/fingergun/attackby(obj/item/A, mob/user, params)
+ return
+
+/obj/item/gun/projectile/revolver/fingergun/attack_self(mob/living/user)
+ to_chat(usr, "You holster your fingers. Another time.")
+ qdel(src)
+ return
+
/obj/item/gun/projectile/revolver/mateba
name = "\improper Unica 6 auto-revolver"
desc = "A retro high-powered autorevolver typically used by officers of the New Russia military. Uses .357 ammo." //>10mm hole >.357
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index 39f25c5ef89..949983d45ba 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -30,7 +30,9 @@
var/spread = 0 //amount (in degrees) of projectile spread
var/legacy = FALSE //legacy projectile system
animate_movement = 0
-
+
+ var/ignore_source_check = FALSE
+
var/damage = 10
var/tile_dropoff = 0 //how much damage should be decremented as the bullet moves
var/tile_dropoff_s = 0 //same as above but for stamina
@@ -54,6 +56,9 @@
var/jitter = 0
var/forcedodge = 0 //to pass through everything
var/dismemberment = 0 //The higher the number, the greater the bonus to dismembering. 0 will not dismember at all.
+ var/ricochets = 0
+ var/ricochets_max = 2
+ var/ricochet_chance = 0
var/log_override = FALSE //whether print to admin attack logs or just keep it in the diary
@@ -62,15 +67,15 @@
return ..()
/obj/item/projectile/proc/Range()
- range--
- if(damage && tile_dropoff)
- damage = max(0, damage - tile_dropoff) // decrement projectile damage based on dropoff value for each tile it moves
- if(stamina && tile_dropoff_s)
- stamina = max(0, stamina - tile_dropoff_s) // as above, but with stamina
- if(range <= 0 && loc)
- on_range()
- if(!damage && !stamina && !nodamage)
- on_range()
+ range--
+ if(damage && tile_dropoff)
+ damage = max(0, damage - tile_dropoff) // decrement projectile damage based on dropoff value for each tile it moves
+ if(stamina && tile_dropoff_s)
+ stamina = max(0, stamina - tile_dropoff_s) // as above, but with stamina
+ if(range <= 0 && loc)
+ on_range()
+ if(!damage && !stamina && (tile_dropoff || tile_dropoff_s))
+ on_range()
/obj/item/projectile/proc/on_range() //if we want there to be effects when they reach the end of their range
qdel(src)
@@ -157,6 +162,14 @@
/obj/item/projectile/Bump(atom/A, yes)
if(!yes) //prevents double bumps.
return
+
+ if(check_ricochet(A) && check_ricochet_flag(A) && ricochets < ricochets_max)
+ ricochets++
+ if(A.handle_ricochet(src))
+ on_ricochet(A)
+ ignore_source_check = TRUE
+ range = initial(range)
+ return TRUE
if(firer)
if(A == firer || (A == firer.loc && istype(A, /obj/mecha))) //cannot shoot yourself or your mech
loc = A.loc
@@ -280,3 +293,21 @@ obj/item/projectile/Crossed(atom/movable/AM) //A mob moving on a tile with a pro
/obj/item/projectile/proc/dumbfire(var/dir)
current = get_ranged_target_turf(src, dir, world.maxx) //world.maxx is the range. Not sure how to handle this better.
fire()
+
+
+/obj/item/projectile/proc/on_ricochet(atom/A)
+ return
+
+/obj/item/projectile/proc/check_ricochet()
+ if(prob(ricochet_chance))
+ return TRUE
+ return FALSE
+
+/obj/item/projectile/proc/check_ricochet_flag(atom/A)
+ if(A.flags_2 & CHECK_RICOCHET_1)
+ return TRUE
+ return FALSE
+
+/obj/item/projectile/proc/setAngle(new_angle) //wrapper for overrides.
+ Angle = new_angle
+ return TRUE
diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm
index b7854d90e13..b34364c4c93 100644
--- a/code/modules/projectiles/projectile/beams.dm
+++ b/code/modules/projectiles/projectile/beams.dm
@@ -79,7 +79,7 @@
name = "laser tag beam"
icon_state = "omnilaser"
hitsound = 'sound/weapons/tap.ogg'
- damage = 0
+ nodamage = 1
damage_type = STAMINA
flag = "laser"
var/suit_types = list(/obj/item/clothing/suit/redtag, /obj/item/clothing/suit/bluetag)
diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm
index c4d9fe2c39f..c55f367d8c3 100644
--- a/code/modules/projectiles/projectile/bullets.dm
+++ b/code/modules/projectiles/projectile/bullets.dm
@@ -37,6 +37,18 @@
stamina = 60
icon_state = "bullet-r"
+/obj/item/projectile/bullet/weakbullet2/invisible //finger gun bullets
+ name = "invisible bullet"
+ damage = 0
+ icon_state = null
+ hitsound_wall = null
+
+/obj/item/projectile/bullet/weakbullet2/invisible/fake
+ weaken = 0
+ stamina = 0
+ nodamage = 1
+ log_override = TRUE
+
/obj/item/projectile/bullet/weakbullet3
damage = 20
diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
index 88d7318e962..c838c5469c7 100644
--- a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
@@ -64,7 +64,7 @@
options[/obj/item/stock_parts/manipulator/pico] = "Upgrade to a pico manipulator to fix it."
options[/obj/item/stock_parts/matter_bin/super] = "Give it a super matter bin to fix it."
options[/obj/item/stock_parts/cell/super] = "Replace the reagent synthesizer with a super capacity cell to fix it."
- options[/obj/item/mass_spectrometer/adv] = "Replace the reagent scanner with an advanced mass spectrometer to fix it"
+ options[/obj/item/reagent_scanner/adv] = "Replace the reagent scanner with an advanced reagent scanner to fix it"
options[/obj/item/stock_parts/micro_laser/high] = "Repair the reagent synthesizer with an high-power micro-laser to fix it"
options[/obj/item/reagent_scanner/adv] = "Replace the reagent scanner with an advanced reagent scanner to fix it"
options[/obj/item/stack/nanopaste] = "Apply some nanopaste to the broken nozzles to fix it."
diff --git a/code/modules/reagents/reagent_containers/glass_containers.dm b/code/modules/reagents/reagent_containers/glass_containers.dm
index e82dbda249d..db70bf255e6 100644
--- a/code/modules/reagents/reagent_containers/glass_containers.dm
+++ b/code/modules/reagents/reagent_containers/glass_containers.dm
@@ -245,9 +245,9 @@
if(assembly)
assembly.on_found(finder)
-/obj/item/reagent_containers/glass/beaker/hear_talk(mob/living/M, msg)
+/obj/item/reagent_containers/glass/beaker/hear_talk(mob/living/M, list/message_pieces)
if(assembly)
- assembly.hear_talk(M, msg)
+ assembly.hear_talk(M, message_pieces)
/obj/item/reagent_containers/glass/beaker/hear_message(mob/living/M, msg)
if(assembly)
diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm
index edbc32f36a1..4f4277980e0 100644
--- a/code/modules/reagents/reagent_dispenser.dm
+++ b/code/modules/reagents/reagent_dispenser.dm
@@ -181,9 +181,9 @@
if(rig)
rig.Crossed(AM)
-/obj/structure/reagent_dispensers/fueltank/hear_talk(mob/living/M, msg)
+/obj/structure/reagent_dispensers/fueltank/hear_talk(mob/living/M, list/message_pieces)
if(rig)
- rig.hear_talk(M, msg)
+ rig.hear_talk(M, message_pieces)
/obj/structure/reagent_dispensers/fueltank/hear_message(mob/living/M, msg)
if(rig)
diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm
index f6474c4a16b..d0007ab7266 100644
--- a/code/modules/recycling/disposal.dm
+++ b/code/modules/recycling/disposal.dm
@@ -538,6 +538,7 @@
// note AM since can contain mobs or objs
for(var/atom/movable/AM in D)
AM.loc = src
+ SEND_SIGNAL(AM, COMSIG_MOVABLE_DISPOSING, src, D)
if(istype(AM, /mob/living/carbon/human))
var/mob/living/carbon/human/H = AM
if(FAT in H.mutations) // is a human and fat?
diff --git a/code/modules/research/designs/mechfabricator_designs.dm b/code/modules/research/designs/mechfabricator_designs.dm
index 0253bb32055..315a2e70aa0 100644
--- a/code/modules/research/designs/mechfabricator_designs.dm
+++ b/code/modules/research/designs/mechfabricator_designs.dm
@@ -657,6 +657,7 @@
name = "Exosuit Medical Equipment (Rescue Jaw)"
id = "mech_medical_jaw"
build_type = MECHFAB
+ build_path = /obj/item/mecha_parts/mecha_equipment/medical/rescue_jaw
req_tech = list("materials" = 4, "engineering" = 6, "magnets" = 6) //now same as jaws of life
materials = list(MAT_METAL=5000,MAT_SILVER=2000,MAT_TITANIUM=1500)
construction_time = 200
@@ -1165,6 +1166,15 @@
construction_time = 200
category = list("Misc")
+/datum/design/ipc_microphone
+ name = "IPC Microphone"
+ id = "ipc_microphone"
+ build_type = MECHFAB
+ build_path = /obj/item/organ/internal/ears/microphone
+ materials = list(MAT_METAL = 1000, MAT_GLASS = 2500)
+ construction_time = 200
+ category = list("Misc")
+
/datum/design/synthetic_flash
name = "Synthetic Flash"
desc = "A synthetic flash used mostly in borg construction."
diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm
index b5840d82265..ce98cdd4c1e 100644
--- a/code/modules/research/designs/medical_designs.dm
+++ b/code/modules/research/designs/medical_designs.dm
@@ -1,15 +1,6 @@
/////////////////////////////////////////
////////////Medical Tools////////////////
/////////////////////////////////////////
-/datum/design/adv_mass_spectrometer
- name = "Advanced Mass-Spectrometer"
- desc = "A device for analyzing chemicals in the blood and their quantities."
- id = "adv_mass_spectrometer"
- req_tech = list("biotech" = 3, "magnets" = 4, "plasmatech" = 3)
- build_type = PROTOLATHE
- materials = list(MAT_METAL = 500, MAT_GLASS = 200)
- build_path = "/obj/item/mass_spectrometer/adv"
- category = list("Medical")
/datum/design/adv_reagent_scanner
name = "Advanced Reagent Scanner"
@@ -93,16 +84,6 @@
build_path = /obj/item/mmi
category = list("Misc","Medical")
-/datum/design/mass_spectrometer
- name = "Mass-Spectrometer"
- desc = "A device for analyzing chemicals in the blood."
- id = "mass_spectrometer"
- req_tech = list("magnets" = 2, "plasmatech" = 2)
- build_type = PROTOLATHE
- materials = list(MAT_METAL = 300, MAT_GLASS = 100)
- build_path = /obj/item/mass_spectrometer
- category = list("Medical")
-
/datum/design/robotic_brain
name = "Robotic Brain"
desc = "The latest in non-sentient Artificial Intelligences."
diff --git a/code/modules/research/experimentor.dm b/code/modules/research/experimentor.dm
index 9e72a12c98e..2ae4e3ac828 100644
--- a/code/modules/research/experimentor.dm
+++ b/code/modules/research/experimentor.dm
@@ -619,7 +619,7 @@
/obj/item/relic/New()
icon_state = pick("shock_kit","armor-igniter-analyzer","infra-igniter0","infra-igniter1","radio-multitool","prox-radio1","radio-radio","timer-multitool0","radio-igniter-tank")
- realName = "[pick("broken","twisted","spun","improved","silly","regular","badly made")] [pick("device","object","toy","illegal tech","weapon")]"
+ realName = "[pick("broken","twisted","spun","improved","silly","regular","badly made")] [pick("device","object","toy","suspicious tech","gear")]"
floof = pick(/mob/living/simple_animal/pet/corgi, /mob/living/simple_animal/pet/cat, /mob/living/simple_animal/pet/fox, /mob/living/simple_animal/mouse, /mob/living/simple_animal/pet/pug, /mob/living/simple_animal/lizard, /mob/living/simple_animal/diona, /mob/living/simple_animal/butterfly, /mob/living/carbon/human/monkey)
diff --git a/code/modules/response_team/ert.dm b/code/modules/response_team/ert.dm
index 0c2688cc3f1..e8c91920cb8 100644
--- a/code/modules/response_team/ert.dm
+++ b/code/modules/response_team/ert.dm
@@ -279,6 +279,7 @@ var/ert_request_answered = FALSE
paranormal_slots -= 1
M.equipOutfit(paranormal_outfit)
M.job = "ERT Paranormal"
+ M.mind.isholy = TRUE
if("Commander")
command_slots = 0
diff --git a/code/modules/response_team/ert_outfits.dm b/code/modules/response_team/ert_outfits.dm
index be0a6808c39..8948fd80dbb 100644
--- a/code/modules/response_team/ert_outfits.dm
+++ b/code/modules/response_team/ert_outfits.dm
@@ -146,7 +146,7 @@
/obj/item/organ/internal/cyberimp/chest/nutriment,
/obj/item/organ/internal/cyberimp/eyes/hud/security
)
-
+
backpack_contents = list(
/obj/item/clothing/head/helmet/space/hardsuit/ert/security = 1,
/obj/item/clothing/mask/gas/sechailer = 1,
@@ -379,8 +379,7 @@
pda = /obj/item/pda/centcom
backpack_contents = list(
/obj/item/clothing/mask/gas/sechailer/swat = 1,
- /obj/item/storage/box/zipties = 1,
- /obj/item/flashlight/seclite = 1
+ /obj/item/storage/box/zipties = 1
)
/datum/outfit/job/centcom/response_team/paranormal/amber
@@ -395,7 +394,7 @@
suit = /obj/item/clothing/suit/space/hardsuit/ert/paranormal/inquisitor
head = /obj/item/clothing/head/helmet/space/hardsuit/ert/paranormal/inquisitor
suit_store = /obj/item/gun/energy/gun
- r_pocket = /obj/item/nullrod
+ r_pocket = /obj/item/nullrod/ert
glasses = /obj/item/clothing/glasses/sunglasses
cybernetic_implants = list(
@@ -411,7 +410,7 @@
l_pocket = /obj/item/grenade/clusterbuster/holy
shoes = /obj/item/clothing/shoes/magboots/advance
glasses = /obj/item/clothing/glasses/night
- r_pocket = /obj/item/nullrod
+ r_pocket = /obj/item/nullrod/ert
cybernetic_implants = list(
/obj/item/organ/internal/cyberimp/chest/nutriment/plus,
diff --git a/code/modules/spacepods/spacepod.dm b/code/modules/spacepods/spacepod.dm
index bb9c2d41fa1..715adb668ab 100644
--- a/code/modules/spacepods/spacepod.dm
+++ b/code/modules/spacepods/spacepod.dm
@@ -522,8 +522,8 @@ obj/spacepod/proc/add_equipment(mob/user, var/obj/item/spacepod_equipment/SPE, v
to_chat(user, "You need an open hand to do that.")
-/obj/spacepod/hear_talk/hear_talk(mob/M, var/msg)
- cargo_hold.hear_talk(M, msg)
+/obj/spacepod/hear_talk/hear_talk(mob/M, list/message_pieces)
+ cargo_hold.hear_talk(M, message_pieces)
..()
/obj/spacepod/hear_message(mob/M, var/msg)
diff --git a/code/modules/surgery/organs/augments_arms.dm b/code/modules/surgery/organs/augments_arms.dm
index 2616c6c6c7e..d9a572c1a0e 100644
--- a/code/modules/surgery/organs/augments_arms.dm
+++ b/code/modules/surgery/organs/augments_arms.dm
@@ -238,6 +238,14 @@
var/obj/item/flash/armimplant/F = locate(/obj/item/flash/armimplant) in items_list
F.I = src
+/obj/item/organ/internal/cyberimp/arm/combat/centcom
+ name = "NT specops cybernetics implant"
+ desc = "An extremely powerful cybernetic implant that contains combat and utility modules used by NT special forces."
+ contents = newlist(/obj/item/gun/energy/pulse/pistol/m1911, /obj/item/door_remote/omni, /obj/item/melee/energy/blade/hardlight, /obj/item/reagent_containers/hypospray/combat/nanites, /obj/item/gun/medbeam, /obj/item/borg/stun, /obj/item/implanter/mindshield, /obj/item/flash/armimplant)
+ icon = 'icons/obj/guns/energy.dmi'
+ icon_state = "m1911"
+ emp_proof = 1
+
/obj/item/organ/internal/cyberimp/arm/surgery
name = "surgical toolset implant"
desc = "A set of surgical tools hidden behind a concealed panel on the user's arm"
diff --git a/code/modules/surgery/organs/ears.dm b/code/modules/surgery/organs/ears.dm
index d04e5a95279..4f23bf18243 100644
--- a/code/modules/surgery/organs/ears.dm
+++ b/code/modules/surgery/organs/ears.dm
@@ -47,6 +47,9 @@
/obj/item/organ/internal/ears/proc/MinimumDeafTicks(value)
deaf = max(deaf, value)
+/obj/item/organ/internal/ears/surgeryize()
+ RestoreEars()
+
// Mob procs
/mob/living/carbon/RestoreEars()
var/obj/item/organ/internal/ears/ears = get_int_organ(/obj/item/organ/internal/ears)
@@ -61,4 +64,5 @@
/mob/living/carbon/MinimumDeafTicks(value)
var/obj/item/organ/internal/ears/ears = get_int_organ(/obj/item/organ/internal/ears)
if(ears)
- ears.MinimumDeafTicks(value)
\ No newline at end of file
+ ears.MinimumDeafTicks(value)
+
diff --git a/code/modules/surgery/organs/subtypes/machine.dm b/code/modules/surgery/organs/subtypes/machine.dm
index b66d13612a3..837e9b5ffb7 100644
--- a/code/modules/surgery/organs/subtypes/machine.dm
+++ b/code/modules/surgery/organs/subtypes/machine.dm
@@ -133,4 +133,16 @@
stored_mmi.icon_state = "posibrain-occupied"
if(!stored_mmi.brainmob.dna)
stored_mmi.brainmob.dna = dna.Clone()
+ . = ..()
+
+/obj/item/organ/internal/ears/microphone
+ name = "microphone"
+ icon = 'icons/obj/device.dmi'
+ icon_state = "taperecorder_idle"
+ status = ORGAN_ROBOT
+ dead_icon = "taperecorder_empty"
+
+/obj/item/organ/internal/ears/microphone/remove(mob/living/user, special = FALSE)
+ if(!special)
+ to_chat(owner, "BZZZZZZZZZZZZZZT! Microphone error!")
. = ..()
\ No newline at end of file
diff --git a/code/modules/surgery/organs/subtypes/skrell.dm b/code/modules/surgery/organs/subtypes/skrell.dm
index fd8a02f18d3..20eae88139c 100644
--- a/code/modules/surgery/organs/subtypes/skrell.dm
+++ b/code/modules/surgery/organs/subtypes/skrell.dm
@@ -52,8 +52,8 @@
pocket.emp_act(severity)
..()
-/obj/item/organ/internal/headpocket/hear_talk(mob/living/M as mob, msg)
- pocket.hear_talk(M, msg)
+/obj/item/organ/internal/headpocket/hear_talk(mob/living/M as mob, list/message_pieces)
+ pocket.hear_talk(M, message_pieces)
..()
/obj/item/organ/internal/headpocket/hear_message(mob/living/M as mob, msg)
diff --git a/code/modules/surgery/organs/vocal_cords.dm b/code/modules/surgery/organs/vocal_cords.dm
index eedcb52be00..6e630fe7438 100644
--- a/code/modules/surgery/organs/vocal_cords.dm
+++ b/code/modules/surgery/organs/vocal_cords.dm
@@ -136,8 +136,8 @@ var/static/regex/multispin_words = regex("like a record baby")
var/power_multiplier = base_multiplier
if(owner.mind)
- //Chaplains are very good at speaking with the voice of god
- if(owner.mind.assigned_role == "Chaplain")
+ //Holy characters are very good at speaking with the voice of god
+ if(owner.mind.isholy)
power_multiplier *= 2
//Command staff has authority
if(owner.mind.assigned_role in command_positions)
diff --git a/html/changelog.html b/html/changelog.html
index 8ace5ded9d0..99337b40878 100644
--- a/html/changelog.html
+++ b/html/changelog.html
@@ -56,6 +56,282 @@
-->
+
06 December 2018
+
Shadow-Quill updated:
+
+
Fixed protocol spelling in the Photon Projector description.
+
+
TDSSS updated:
+
+
Reverted holopad sorting to make holopads work again.
+
+
+
01 December 2018
+
Kyep updated:
+
+
Fixed a couple of bugs with the special event pets triggered by admin bless command.
+
+
TDSSS updated:
+
+
Ancient Station asteroids now contain minerals and can be mined.
+
+
datlo updated:
+
+
Added 3 carrot seed packets to perma.
+
+
+
30 November 2018
+
Azule Utama updated:
+
+
Fixed an ORM exploit which would let you smelt invalid stacks of alloys.
+
+
TDSSS updated:
+
+
Removed ballgag and assless chaps
+
+
+
29 November 2018
+
Kyep updated:
+
+
Updated chaplains. They now spawn with their nullrod in their hand, and their soulstone in their locker. They now have the ability to bless other players. The more players they bless, the more likely admins are to answer their prayers. There are no longer any atheist chaplains. The chaplain items that reference atheism have been tweaked accordingly, e.g the atheist fedora is now the binary fedora. A few other items have also been tweaked. Paranormal ERT members now count as holy, like the chaplain, and red/gamma paranormal ERTs get a more powerful version of the nullrod, so they have a reason to use a weapon other than their gun.
+
+
TDSSS updated:
+
+
No more making infinite meat and crashing the server with gibbers
+
+
+
28 November 2018
+
Kyep and FoS updated:
+
+
Updated hellhound with new sprites by FoS, slightly better AI, and more fixes/tweaks.
+
+
MrKicker updated:
+
+
Sort callable holopads by name
+
+
tigercat2000 updated:
+
+
You can now speak in multiple languages in the same message! This works for all forms of speech, including radios. Minus megaphones because fuck em.
+
Some mild formatting errors and inconsistencies that have historically been present within mob speech.
+
Holopads behaving a bit weirdly with how they interact with the typical say system
+
+
+
27 November 2018
+
Azule Utama updated:
+
+
Power beacon made purchasable again in Nuke Ops uplinks.
+
+
Dovydas12345 updated:
+
+
Fixes being able to attach IV drips when incapacitated
+
+
Farie82 updated:
+
+
You can now actually make the rescue jaws
+
+
+
26 November 2018
+
Citinited updated:
+
+
Adds the rubbish bin in front of the kitchen again
+
+
TDSSS updated:
+
+
Medbay secondary storage is now accessible to everyone with medbay access, hardsuits were put behind a windoor with CMO access to keep them at the same level of accessability.
+
Added and removed some items from medbay secondary storage.
+
Medical storage and secondary storage were renamed to be more accurate.
+
+
datlo updated:
+
+
Updated description of finger gun to point out you can holster the gun manually by using it in hand.
+
Fixed a bug where you couldn't learn finger gun if you already knew fake finger gun.
+
Power beacon cost reduced to 10 TC, and is now hijack-exclusive.
+
Guillotines can now be deconstructed with a welder.
+
+
+
25 November 2018
+
Azule Utama updated:
+
+
Job-specific gear can no longer be discounted.
+
Discounted items now get an alternate log entry stating the item's new price.
+
+
KasparoVy updated:
+
+
Fixes Unathi Snout 2 not using the correct sprite.
+
Fixes Unathi Points Head not using the correct sprite.
+
Fixes Unathi Sharp Tiger Head and Face not using the correct sprite.
+
Fixes Unathi Tiger Head and Face not using the correct sprite.
+
Removes Unathi Sharp Points Head and enables Unathi Points Head marking for use on all Unathi head types.
+
+
MarsM0nd updated:
+
+
Coins will now always show a side on being spawned.
+
+
Reagent Scanner Changes updated:
+
+
Removed Mass Spectrometer and Advanced Mass Spectrometer
+
Reagent Scanner and Advanced Reagent Scanners can now identify blood types
+
+
datlo updated:
+
+
Added the Advanced Mimery Series to Syndicate Mime uplink.
+
Added a fake Finger Gun manual to the arcade prize machine.
+
Changed the look of the teleporter hallway from maintenance to dirty hallway.
+
+
+
24 November 2018
+
Azule Utama updated:
+
+
Added uplink discounts. Traitor and Nuclear Operative uplinks now have a discounted gear section! 3 random items will be chosen to be 50% off (rounded down), 25% if they usually cost 20 or more TC. You can only buy a discounted item once per uplink. Ported from TG.
+
+
Eschess updated:
+
+
hardsuit helmets now unlink from the hardsuits upon being detached
+
syndicate hardsuit helmet combat mode can no longer be toggled on hand
Space ruin/gateway hardsuits now should all come with helmets
+
Telecom setup in wizard gateway can now be completed with the present stock parts.
+
+
Ty-Omaha updated:
+
+
Added reflective blobs, these blobs have a high chance to deflect energy projectiles while being vulnerable to ballistics and brute damage. Reflective blobs can be obtained by upgrading a shield blob.
+
+
tigercat2000 updated:
+
+
Fancier Transpara-corner Nano take 2.
+
Nano's build-watching works on 512 again.
+
Nano scrolling issues with the power monitor (they existed before I touched this goddamnit I swear!!!)
+
Rewrote Nano's buildscripts and upgraded all the dependencies (again)
+
+
+
23 November 2018
+
Azule Utama updated:
+
+
Additional check added to damage falloff code, will only delete bullets that reach 0 damage if they actually have a falloff value.
+
+
Kyep updated:
+
+
Dead mice no longer squeak.
+
Ghosts walking over living mice no longer makes them squeak.
+
+
TDSSS updated:
+
+
no more runtime spam each time a simple mob is attacked
+
+
datlo updated:
+
+
Replaced "illegal" and "weapon" in strange objects name
+
+
+
22 November 2018
+
AlAtEX updated:
+
+
re-adds sprites for boxes that disappeared
+
+
AlAtEx updated:
+
+
Added a new medkit for IPCs
+
added a purple medkit
+
Added a repair kit box to start with for IPC's
+
Re-added the crowbar and extended tank to miner's boxes
+
added three new box images
+
+
Azule Utama updated:
+
+
Made laser tag guns work again.
+
Syndicate Cigarettes added to maintenance loot.
+
+
Eschess updated:
+
+
Adds ui button to toggle hardsuit helmet
+
syndicate hardsuit combat mode is activated only by the helmet button
+
+
Kyep updated:
+
+
Added hellhound, a dangerous new monster.
+
+
datlo updated:
+
+
Added the CQC manual to nuke ops and traitor uplinks, letting them remember some of the basics of CQC.
+
+
+
21 November 2018
+
MrKicker updated:
+
+
Added generated sound files to sound/vox_fem , then added references to them in vox_sounds.dm
+
+
+
20 November 2018
+
AffectedArc07 updated:
+
+
NTTC now has multiple job tag styles
+
NTTC now has option to make job tags colored
+
NTTC now has option to make nametags colored
+
NTTC now has options to make command members louder (bold)
+
The NTTC window title is no longer NTSL.
+
+
Azule Utama updated:
+
+
Fixed chameleon suit having two invalid options.
+
+
TDSSS updated:
+
+
Silencers now have a tech origin, give research levels when deconstructed
+
Moved gun attachments to their own folder, code-wise.
+
+
name here updated:
+
+
Fixed a runtime in a syndieteam radio.
+
+
tigercat2000 updated:
+
+
Nano scrolling is 95% less buggy
+
NanoUI's Fancy Mode now less broken as hell remove: 512 support for Nano dev tools
+
+
+
19 November 2018
+
Birdtalon updated:
+
+
Fixes a runtime in safe.dm
+
+
Citinited updated:
+
+
Alt-clicking a jumpsuit removes accessories from it
+
Removing an accessory now shows a small message
+
+
KasparoVy updated:
+
+
Brings the logging of plasma statue ignition up to the same spec at as welder tank explosions.
+
You can no longer ignite plasma statues with disablers.
+
+
Kyep updated:
+
+
Tweaked many of the special admin outfits. This means centcom officers are less likely to be seen wielding illegal syndicate gear, and subtle flaws with many other outfits (such as pirates lacking radio, singulo knights lacking a backpack, several outfits not showing up on secHUDs correctly, etc etc etc) have been addressed.
+
Added several new admin options for fax responses, blessings, and smiting.
+
+
Quantum-M updated:
+
+
Sleeping carp uplink description now points out that it is unable to be learned by vampires or changelings.
+
Holoparasite kit uplink description now points out that it doesn't work on vampires or changelings.
+
Attempting to learn sleeping carp as a changeling or vampire gives you a red warning message.
+
You can now refund the sleeping carp scroll to the uplink.
+
+
TDSSS updated:
+
+
A warning if slings are very close to ascension, so the crew can potentially still stop them
+
+
craftxbox updated:
+
+
add observer/incapacitated check for all sec/medhud actions
+
+
datlo updated:
+
+
Added antag alert sounds that play at roundstart for most major antagonists, ported from TG.
+
+
17 November 2018
uc_guy updated:
diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml
index b540fc52b55..10fddd7d8e9 100644
--- a/html/changelogs/.all_changelog.yml
+++ b/html/changelogs/.all_changelog.yml
@@ -8094,3 +8094,201 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py.
2018-11-17:
uc_guy:
- bugfix: Syndicate borgs are no longer pseudonymous on syndie radio.
+2018-11-19:
+ Birdtalon:
+ - bugfix: Fixes a runtime in safe.dm
+ Citinited:
+ - tweak: Alt-clicking a jumpsuit removes accessories from it
+ - tweak: Removing an accessory now shows a small message
+ KasparoVy:
+ - tweak: Brings the logging of plasma statue ignition up to the same spec at as
+ welder tank explosions.
+ - bugfix: You can no longer ignite plasma statues with disablers.
+ Kyep:
+ - tweak: Tweaked many of the special admin outfits. This means centcom officers
+ are less likely to be seen wielding illegal syndicate gear, and subtle flaws
+ with many other outfits (such as pirates lacking radio, singulo knights lacking
+ a backpack, several outfits not showing up on secHUDs correctly, etc etc etc)
+ have been addressed.
+ - rscadd: Added several new admin options for fax responses, blessings, and smiting.
+ Quantum-M:
+ - tweak: Sleeping carp uplink description now points out that it is unable to be
+ learned by vampires or changelings.
+ - tweak: Holoparasite kit uplink description now points out that it doesn't work
+ on vampires or changelings.
+ - tweak: Attempting to learn sleeping carp as a changeling or vampire gives you
+ a red warning message.
+ - tweak: You can now refund the sleeping carp scroll to the uplink.
+ TDSSS:
+ - rscadd: A warning if slings are very close to ascension, so the crew can potentially
+ still stop them
+ craftxbox:
+ - bugfix: add observer/incapacitated check for all sec/medhud actions
+ datlo:
+ - rscadd: Added antag alert sounds that play at roundstart for most major antagonists,
+ ported from TG.
+2018-11-20:
+ AffectedArc07:
+ - rscadd: NTTC now has multiple job tag styles
+ - rscadd: NTTC now has option to make job tags colored
+ - rscadd: NTTC now has option to make nametags colored
+ - rscadd: NTTC now has options to make command members louder (bold)
+ - spellcheck: The NTTC window title is no longer NTSL.
+ Azule Utama:
+ - bugfix: Fixed chameleon suit having two invalid options.
+ TDSSS:
+ - tweak: Silencers now have a tech origin, give research levels when deconstructed
+ - tweak: Moved gun attachments to their own folder, code-wise.
+ name here:
+ - bugfix: Fixed a runtime in a syndieteam radio.
+ tigercat2000:
+ - bugfix: Nano scrolling is 95% less buggy
+ - bugfix: 'NanoUI''s Fancy Mode now less broken as hell remove: 512 support for
+ Nano dev tools'
+2018-11-21:
+ MrKicker:
+ - soundadd: Added generated sound files to sound/vox_fem , then added references
+ to them in vox_sounds.dm
+2018-11-22:
+ AlAtEX:
+ - bugfix: re-adds sprites for boxes that disappeared
+ AlAtEx:
+ - rscadd: Added a new medkit for IPCs
+ - imageadd: added a purple medkit
+ - rscadd: Added a repair kit box to start with for IPC's
+ - rscadd: Re-added the crowbar and extended tank to miner's boxes
+ - imageadd: added three new box images
+ Azule Utama:
+ - bugfix: Made laser tag guns work again.
+ - rscadd: Syndicate Cigarettes added to maintenance loot.
+ Eschess:
+ - rscadd: Adds ui button to toggle hardsuit helmet
+ - tweak: syndicate hardsuit combat mode is activated only by the helmet button
+ Kyep:
+ - rscadd: Added hellhound, a dangerous new monster.
+ datlo:
+ - rscadd: Added the CQC manual to nuke ops and traitor uplinks, letting them remember
+ some of the basics of CQC.
+2018-11-23:
+ Azule Utama:
+ - bugfix: Additional check added to damage falloff code, will only delete bullets
+ that reach 0 damage if they actually have a falloff value.
+ Kyep:
+ - bugfix: Dead mice no longer squeak.
+ - bugfix: Ghosts walking over living mice no longer makes them squeak.
+ TDSSS:
+ - bugfix: no more runtime spam each time a simple mob is attacked
+ datlo:
+ - tweak: Replaced "illegal" and "weapon" in strange objects name
+2018-11-24:
+ Azule Utama:
+ - rscadd: Added uplink discounts. Traitor and Nuclear Operative uplinks now have
+ a discounted gear section! 3 random items will be chosen to be 50% off (rounded
+ down), 25% if they usually cost 20 or more TC. You can only buy a discounted
+ item once per uplink. Ported from TG.
+ Eschess:
+ - bugfix: hardsuit helmets now unlink from the hardsuits upon being detached
+ - bugfix: syndicate hardsuit helmet combat mode can no longer be toggled on hand
+ - bugfix: fixes non-syndicate hardsuit helmet attaching/detaching runtimes
+ TDSSS:
+ - tweak: Space ruin/gateway hardsuits now should all come with helmets
+ - tweak: Telecom setup in wizard gateway can now be completed with the present stock
+ parts.
+ Ty-Omaha:
+ - rscadd: Added reflective blobs, these blobs have a high chance to deflect energy
+ projectiles while being vulnerable to ballistics and brute damage. Reflective
+ blobs can be obtained by upgrading a shield blob.
+ tigercat2000:
+ - rscadd: Fancier Transpara-corner Nano take 2.
+ - rscadd: Nano's build-watching works on 512 again.
+ - bugfix: Nano scrolling issues with the power monitor (they existed before I touched
+ this goddamnit I swear!!!)
+ - tweak: Rewrote Nano's buildscripts and upgraded all the dependencies (again)
+2018-11-25:
+ Azule Utama:
+ - tweak: Job-specific gear can no longer be discounted.
+ - tweak: Discounted items now get an alternate log entry stating the item's new
+ price.
+ KasparoVy:
+ - bugfix: Fixes Unathi Snout 2 not using the correct sprite.
+ - bugfix: Fixes Unathi Points Head not using the correct sprite.
+ - bugfix: Fixes Unathi Sharp Tiger Head and Face not using the correct sprite.
+ - bugfix: Fixes Unathi Tiger Head and Face not using the correct sprite.
+ - tweak: Removes Unathi Sharp Points Head and enables Unathi Points Head marking
+ for use on all Unathi head types.
+ MarsM0nd:
+ - bugfix: Coins will now always show a side on being spawned.
+ Reagent Scanner Changes:
+ - rscdel: Removed Mass Spectrometer and Advanced Mass Spectrometer
+ - tweak: Reagent Scanner and Advanced Reagent Scanners can now identify blood types
+ datlo:
+ - rscadd: Added the Advanced Mimery Series to Syndicate Mime uplink.
+ - rscadd: Added a fake Finger Gun manual to the arcade prize machine.
+ - tweak: Changed the look of the teleporter hallway from maintenance to dirty hallway.
+2018-11-26:
+ Citinited:
+ - rscadd: Adds the rubbish bin in front of the kitchen again
+ TDSSS:
+ - tweak: Medbay secondary storage is now accessible to everyone with medbay access,
+ hardsuits were put behind a windoor with CMO access to keep them at the same
+ level of accessability.
+ - tweak: Added and removed some items from medbay secondary storage.
+ - tweak: Medical storage and secondary storage were renamed to be more accurate.
+ datlo:
+ - tweak: Updated description of finger gun to point out you can holster the gun
+ manually by using it in hand.
+ - bugfix: Fixed a bug where you couldn't learn finger gun if you already knew fake
+ finger gun.
+ - tweak: Power beacon cost reduced to 10 TC, and is now hijack-exclusive.
+ - rscadd: Guillotines can now be deconstructed with a welder.
+2018-11-27:
+ Azule Utama:
+ - bugfix: Power beacon made purchasable again in Nuke Ops uplinks.
+ Dovydas12345:
+ - bugfix: Fixes being able to attach IV drips when incapacitated
+ Farie82:
+ - bugfix: You can now actually make the rescue jaws
+2018-11-28:
+ Kyep and FoS:
+ - rscadd: Updated hellhound with new sprites by FoS, slightly better AI, and more
+ fixes/tweaks.
+ MrKicker:
+ - rscadd: Sort callable holopads by name
+ tigercat2000:
+ - rscadd: You can now speak in multiple languages in the same message! This works
+ for all forms of speech, including radios. Minus megaphones because fuck em.
+ - bugfix: Some mild formatting errors and inconsistencies that have historically
+ been present within mob speech.
+ - bugfix: Holopads behaving a bit weirdly with how they interact with the typical
+ say system
+2018-11-29:
+ Kyep:
+ - rscadd: Updated chaplains. They now spawn with their nullrod in their hand, and
+ their soulstone in their locker. They now have the ability to bless other players.
+ The more players they bless, the more likely admins are to answer their prayers.
+ There are no longer any atheist chaplains. The chaplain items that reference
+ atheism have been tweaked accordingly, e.g the atheist fedora is now the binary
+ fedora. A few other items have also been tweaked. Paranormal ERT members now
+ count as holy, like the chaplain, and red/gamma paranormal ERTs get a more powerful
+ version of the nullrod, so they have a reason to use a weapon other than their
+ gun.
+ TDSSS:
+ - bugfix: No more making infinite meat and crashing the server with gibbers
+2018-11-30:
+ Azule Utama:
+ - bugfix: Fixed an ORM exploit which would let you smelt invalid stacks of alloys.
+ TDSSS:
+ - rscdel: Removed ballgag and assless chaps
+2018-12-01:
+ Kyep:
+ - bugfix: Fixed a couple of bugs with the special event pets triggered by admin
+ bless command.
+ TDSSS:
+ - rscadd: Ancient Station asteroids now contain minerals and can be mined.
+ datlo:
+ - rscadd: Added 3 carrot seed packets to perma.
+2018-12-06:
+ Shadow-Quill:
+ - spellcheck: Fixed protocol spelling in the Photon Projector description.
+ TDSSS:
+ - rscdel: Reverted holopad sorting to make holopads work again.
diff --git a/html/nttc/dist/index.html b/html/nttc/dist/index.html
index 80e7a40ac81..a8a7a17f1e3 100644
--- a/html/nttc/dist/index.html
+++ b/html/nttc/dist/index.html
@@ -3,7 +3,7 @@
- NTSL
+ NTTC
@@ -12,7 +12,7 @@