diff --git a/_maps/map_files/MetaStation/MetaStation.v41I.dmm b/_maps/map_files/MetaStation/MetaStation.v41I.dmm
index fe15b19a5ec..2d9c1567195 100644
--- a/_maps/map_files/MetaStation/MetaStation.v41I.dmm
+++ b/_maps/map_files/MetaStation/MetaStation.v41I.dmm
@@ -60469,7 +60469,7 @@
pixel_y = 6
},
/obj/structure/table/wood,
-/obj/item/toy/AI{
+/obj/item/toy/talking/AI{
name = "NanoTrasen-brand toy AI";
pixel_y = 6
},
diff --git a/_maps/map_files/generic/z2.dmm b/_maps/map_files/generic/z2.dmm
index a3b5fe4f31f..646669f2f6c 100644
--- a/_maps/map_files/generic/z2.dmm
+++ b/_maps/map_files/generic/z2.dmm
@@ -2716,7 +2716,7 @@
/area/centcom/evac)
"io" = (
/obj/structure/table/reinforced,
-/obj/item/toy/AI,
+/obj/item/toy/talking/AI,
/obj/structure/window/reinforced{
dir = 4
},
diff --git a/code/game/machinery/computer/arcade.dm b/code/game/machinery/computer/arcade.dm
index d35ba8c6bb8..3b137c133e3 100644
--- a/code/game/machinery/computer/arcade.dm
+++ b/code/game/machinery/computer/arcade.dm
@@ -5,7 +5,7 @@
icon_keyboard = null
icon_screen = "invaders"
var/list/prizes = list( /obj/item/weapon/storage/box/snappops = 2,
- /obj/item/toy/AI = 2,
+ /obj/item/toy/talking/AI = 2,
/obj/item/clothing/under/syndicate/tacticool = 2,
/obj/item/toy/sword = 2,
/obj/item/toy/gun = 2,
@@ -33,6 +33,7 @@
/obj/item/toy/redbutton = 2,
/obj/item/toy/talking/owl = 2,
/obj/item/toy/talking/griffin = 2,
+ /obj/item/toy/talking/skeleton = 2,
/obj/item/weapon/coin/antagtoken = 2,
/obj/item/stack/tile/fakespace/loaded = 2,
/obj/item/toy/toy_xeno = 2,
diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm
index f7ffd73e691..62710525deb 100755
--- a/code/game/objects/items/toys.dm
+++ b/code/game/objects/items/toys.dm
@@ -673,53 +673,50 @@
icon_state = "reticenceprize"
quiet = 1
-/*
- * AI core prizes
- */
-/obj/item/toy/AI
- name = "toy AI"
- desc = "A little toy model AI core with real law announcing action!"
- icon = 'icons/obj/toy.dmi'
- icon_state = "AI"
- w_class = 2
- var/cooldown = 0
-
-/obj/item/toy/AI/attack_self(mob/user)
- if(!cooldown) //for the sanity of everyone
- var/message = generate_ion_law()
- user << "You press the button on [src]."
- playsound(user, 'sound/machines/click.ogg', 20, 1)
- src.loc.visible_message("\icon[src] [message]")
- cooldown = 1
- spawn(30) cooldown = 0
- return
- ..()
-
/obj/item/toy/talking
name = "talking action figure"
desc = "A generic action figure modeled after nothing in particular."
icon = 'icons/obj/toy.dmi'
icon_state = "owlprize"
w_class = 2
- var/cooldown = 0
+ var/cooldown = FALSE
var/messages = list("I'm super generic!", "Mathematics class is of variable difficulty!")
var/span = "danger"
var/recharge_time = 30
+// Talking toys are language universal, and thus all species can use them
+/obj/item/toy/talking/attack_alien(mob/user)
+ . = attack_hand(user)
+
/obj/item/toy/talking/attack_self(mob/user)
if(!cooldown)
- var/message = pick(messages)
- user << "You pull the string on the [src]."
- toy_talk(user, message)
- cooldown = 1
- spawn(recharge_time) cooldown = 0
+ var/message = generate_message()
+ user.visible_message("[user] pulls the string on \the [src].", "You pull the string on \the [src].", "You hear a string being pulled.")
+ toy_talk(user.loc, message)
+ cooldown = TRUE
+ spawn(recharge_time)
+ cooldown = FALSE
return
..()
-/obj/item/toy/talking/proc/toy_talk(user, message)
- playsound(user, 'sound/machines/click.ogg', 20, 1)
+/obj/item/toy/talking/proc/generate_message()
+ return pick(messages)
+
+/obj/item/toy/talking/proc/toy_talk(loc, message)
+ playsound(loc, 'sound/machines/click.ogg', 20, 1)
src.loc.visible_message("\icon[src] [message]")
+/*
+ * AI core prizes
+ */
+/obj/item/toy/talking/AI
+ name = "toy AI"
+ desc = "A little toy model AI core with real law announcing action!"
+ icon_state = "AI"
+
+/obj/item/toy/talking/AI/generate_message()
+ return generate_ion_law()
+
/obj/item/toy/talking/owl
name = "owl action figure"
desc = "An action figure modeled after 'The Owl', defender of justice."
@@ -732,6 +729,57 @@
icon_state = "griffinprize"
messages = list("You can't stop me, Owl!", "My plan is flawless! The vault is mine!", "Caaaawwww!", "You will never catch me!")
+/obj/item/toy/talking/skeleton
+ name = "skeleton action figure"
+ desc = "An action figure modeled after 'Oh-cee', the original content \
+ skeleton.\nNot suitable for infants or assistants under 36 months \
+ of age."
+ icon_state = "skeletonprize"
+ attack_verb = list("boned", "dunked on", "worked down to the bone")
+
+ var/phomeme
+
+ var/list/papyrus_messages = list(
+ "That's the disposal bin. Feel free to visit it at any time.",
+ "I can't just let anyone ERP with me, I'm a skeleton with standards!",
+ "Sorry. Can't talk. I'm busy being popular on a video game.",
+ "You can't spell 'robust' without several letters from my name!!!")
+ var/list/sans_messages = list(
+ "You feel your redtext crawling on your back.",
+ "On days like these, references like this should be BURNING IN HELL.")
+
+/obj/item/toy/talking/skeleton/suicide_act(mob/user)
+ user.visible_message("[user] is trying to commit \
+ suicide with \the [src].")
+
+ if(ishuman(user))
+ var/mob/living/carbon/human/H = user
+ H.set_species(/datum/species/skeleton)
+
+ set_phomeme("sans")
+ toy_talk(user, "You feel like you're going to have a bad time.")
+
+ user.Stun(5)
+ sleep(20)
+ return OXYLOSS
+
+/obj/item/toy/talking/skeleton/generate_message()
+ return pick(papyrus_messages + sans_messages)
+
+/obj/item/toy/talking/skeleton/proc/set_phomeme(a_phomeme)
+ phomeme = a_phomeme
+ span = "danger [a_phomeme]"
+
+/obj/item/toy/talking/skeleton/toy_talk(mob/user, message)
+ // change spans dynamically depending on the message
+ if(sans_messages.Find(message))
+ set_phomeme("sans")
+ if(papyrus_messages.Find(message))
+ set_phomeme("papyrus")
+
+ // If message isn't in the defined lists, just use whatever was set last
+ ..(user.loc, message)
+ chatter(message, phomeme, user)
/*
|| A Deck of Cards for playing various games of chance ||
diff --git a/code/modules/cargo/packs.dm b/code/modules/cargo/packs.dm
index ef1c1ef85cd..ed82afb3029 100644
--- a/code/modules/cargo/packs.dm
+++ b/code/modules/cargo/packs.dm
@@ -1346,9 +1346,10 @@
contains = list(/obj/item/toy/spinningtoy,
/obj/item/toy/sword,
/obj/item/toy/foamblade,
- /obj/item/toy/AI,
+ /obj/item/toy/talking/AI,
/obj/item/toy/talking/owl,
/obj/item/toy/talking/griffin,
+ /obj/item/toy/talking/skeleton,
/obj/item/toy/nuke,
/obj/item/toy/minimeteor,
/obj/item/toy/carpplushie,
diff --git a/code/modules/chatter/chatter.dm b/code/modules/chatter/chatter.dm
new file mode 100644
index 00000000000..bc607ad8706
--- /dev/null
+++ b/code/modules/chatter/chatter.dm
@@ -0,0 +1,52 @@
+/proc/chatter(message, phomeme, mob/user)
+ // We want to transform any message into a list of numbers
+ // and punctuation marks
+ // For example:
+ // "Hi." -> [2, '.']
+ // "HALP GEROGE MELLONS, that swine, is GRIFFIN ME!"
+ // -> [4, 6, 7, ',', 4, 5, ',', '2', 7, 2, '!']
+ // "fuck,thissentenceissquashed" -> [4, ',', 21]
+
+ var/list/punctuation = list(",",":",";",".","?","!","\'","-")
+ var/regex/R = regex("(\[\\l\\d]*)(\[^\\l\\d\\s])?", "g")
+ var/list/letter_count = list()
+ while(R.next <= length(message))
+ R.Find(message)
+ if(R.group[1])
+ letter_count += length(R.group[1])
+ if(R.group[2])
+ letter_count += R.group[2]
+
+ spawn(0)
+ for(var/item in letter_count)
+ if (item in punctuation)
+ // simulate pausing in talking
+ // ignore semi-colons because of their use in HTML escaping
+ if (item in list(",", ":"))
+ sleep(3)
+ if (item in list("!", "?", "."))
+ sleep(6)
+ continue
+
+ if(isnum(item))
+ var/length = min(item, 10)
+ if (length == 0)
+ // "verbalise" long spaces
+ sleep(1)
+ chatter_speak_word(user, phomeme, length)
+
+/proc/chatter_speak_word(mob/user, phomeme, length)
+ var/path = "sound/chatter/[phomeme]_[length].ogg"
+
+ playsound(user.loc, path,
+ vol = 40, vary = 0, extrarange = 3, falloff = 1, surround = 1)
+
+ sleep((length + 1) * chatter_get_sleep_multiplier(phomeme))
+
+/proc/chatter_get_sleep_multiplier(phomeme)
+ if(phomeme == "papyrus")
+ . = 0.5
+ else if(phomeme == "sans")
+ . = 0.7
+ else
+ . = 1
diff --git a/icons/obj/toy.dmi b/icons/obj/toy.dmi
index 5d3b690b1b8..29fa57fe523 100644
Binary files a/icons/obj/toy.dmi and b/icons/obj/toy.dmi differ
diff --git a/sound/chatter/papyrus.wav b/sound/chatter/papyrus.wav
new file mode 100644
index 00000000000..256dcf76fac
Binary files /dev/null and b/sound/chatter/papyrus.wav differ
diff --git a/sound/chatter/papyrus_1.ogg b/sound/chatter/papyrus_1.ogg
new file mode 100644
index 00000000000..2a6a9027008
Binary files /dev/null and b/sound/chatter/papyrus_1.ogg differ
diff --git a/sound/chatter/papyrus_10.ogg b/sound/chatter/papyrus_10.ogg
new file mode 100644
index 00000000000..a5b8ae693c1
Binary files /dev/null and b/sound/chatter/papyrus_10.ogg differ
diff --git a/sound/chatter/papyrus_2.ogg b/sound/chatter/papyrus_2.ogg
new file mode 100644
index 00000000000..8ae0acc99c6
Binary files /dev/null and b/sound/chatter/papyrus_2.ogg differ
diff --git a/sound/chatter/papyrus_3.ogg b/sound/chatter/papyrus_3.ogg
new file mode 100644
index 00000000000..09339eab48b
Binary files /dev/null and b/sound/chatter/papyrus_3.ogg differ
diff --git a/sound/chatter/papyrus_4.ogg b/sound/chatter/papyrus_4.ogg
new file mode 100644
index 00000000000..df862b92ee0
Binary files /dev/null and b/sound/chatter/papyrus_4.ogg differ
diff --git a/sound/chatter/papyrus_5.ogg b/sound/chatter/papyrus_5.ogg
new file mode 100644
index 00000000000..58df04c1ebe
Binary files /dev/null and b/sound/chatter/papyrus_5.ogg differ
diff --git a/sound/chatter/papyrus_6.ogg b/sound/chatter/papyrus_6.ogg
new file mode 100644
index 00000000000..6b5fe758718
Binary files /dev/null and b/sound/chatter/papyrus_6.ogg differ
diff --git a/sound/chatter/papyrus_7.ogg b/sound/chatter/papyrus_7.ogg
new file mode 100644
index 00000000000..b18b5a8952a
Binary files /dev/null and b/sound/chatter/papyrus_7.ogg differ
diff --git a/sound/chatter/papyrus_8.ogg b/sound/chatter/papyrus_8.ogg
new file mode 100644
index 00000000000..dd9f132bf3d
Binary files /dev/null and b/sound/chatter/papyrus_8.ogg differ
diff --git a/sound/chatter/papyrus_9.ogg b/sound/chatter/papyrus_9.ogg
new file mode 100644
index 00000000000..15cd9d08e57
Binary files /dev/null and b/sound/chatter/papyrus_9.ogg differ
diff --git a/sound/chatter/sans.wav b/sound/chatter/sans.wav
new file mode 100644
index 00000000000..90bbfb5203f
Binary files /dev/null and b/sound/chatter/sans.wav differ
diff --git a/sound/chatter/sans_1.ogg b/sound/chatter/sans_1.ogg
new file mode 100644
index 00000000000..0312114e0e4
Binary files /dev/null and b/sound/chatter/sans_1.ogg differ
diff --git a/sound/chatter/sans_10.ogg b/sound/chatter/sans_10.ogg
new file mode 100644
index 00000000000..51218ff3bb2
Binary files /dev/null and b/sound/chatter/sans_10.ogg differ
diff --git a/sound/chatter/sans_2.ogg b/sound/chatter/sans_2.ogg
new file mode 100644
index 00000000000..bf1eabc7c0b
Binary files /dev/null and b/sound/chatter/sans_2.ogg differ
diff --git a/sound/chatter/sans_3.ogg b/sound/chatter/sans_3.ogg
new file mode 100644
index 00000000000..297d5d2257c
Binary files /dev/null and b/sound/chatter/sans_3.ogg differ
diff --git a/sound/chatter/sans_4.ogg b/sound/chatter/sans_4.ogg
new file mode 100644
index 00000000000..faadea9462a
Binary files /dev/null and b/sound/chatter/sans_4.ogg differ
diff --git a/sound/chatter/sans_5.ogg b/sound/chatter/sans_5.ogg
new file mode 100644
index 00000000000..e90cfb2f6b2
Binary files /dev/null and b/sound/chatter/sans_5.ogg differ
diff --git a/sound/chatter/sans_6.ogg b/sound/chatter/sans_6.ogg
new file mode 100644
index 00000000000..5483166ed3a
Binary files /dev/null and b/sound/chatter/sans_6.ogg differ
diff --git a/sound/chatter/sans_7.ogg b/sound/chatter/sans_7.ogg
new file mode 100644
index 00000000000..7e661ce4e81
Binary files /dev/null and b/sound/chatter/sans_7.ogg differ
diff --git a/sound/chatter/sans_8.ogg b/sound/chatter/sans_8.ogg
new file mode 100644
index 00000000000..1b01e2000e3
Binary files /dev/null and b/sound/chatter/sans_8.ogg differ
diff --git a/sound/chatter/sans_9.ogg b/sound/chatter/sans_9.ogg
new file mode 100644
index 00000000000..f5e33fb0b51
Binary files /dev/null and b/sound/chatter/sans_9.ogg differ
diff --git a/tgstation.dme b/tgstation.dme
index 667c869ebc4..aa6d4eccfb9 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -936,6 +936,7 @@
#include "code\modules\cargo\exports\sheets.dm"
#include "code\modules\cargo\exports\tools.dm"
#include "code\modules\cargo\exports\weapons.dm"
+#include "code\modules\chatter\chatter.dm"
#include "code\modules\client\asset_cache.dm"
#include "code\modules\client\client defines.dm"
#include "code\modules\client\client procs.dm"