diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index c5621236057..de6c853df1a 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -773,4 +773,8 @@
#define COMSIG_MOB_IS_IMPORTANT "COMSIG_MOB_IS_IMPORTANT" // ()
+#define COMSIG_UPDATE_SOUND_BLOCKERS "COMSIG_UPDATE_SOUND_BLOCKERS" // ()
+#define COMSIG_CHECK_SOUND_BLOCKERS "COMSIG_CHECK_SOUND_BLOCKERS" // ()
+
+
diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm
index f69fe1e7c79..ef14c168d4b 100644
--- a/code/__DEFINES/layers.dm
+++ b/code/__DEFINES/layers.dm
@@ -28,11 +28,12 @@
#define MOB_PLANE -3 // Used for mobs rendering.
#define FIELD_OF_VISION_VISUAL_PLANE -2 //Yea, FoV does require quite a few planes to work with 513 filters to a decent degree.
-#define CHAT_PLANE -1 //We don't want heard messages to be hidden by FoV.
#define CHAT_LAYER 12.1 //Legacy, it doesn't matter that much because we are displayed above the game plane anyway.
#define BLACKNESS_PLANE 0 //To keep from conflicts with SEE_BLACKNESS internals
+#define CHAT_PLANE 2 //We don't want heard messages to be hidden by FoV.
+
#define SPACE_LAYER 1.8
//#define TURF_LAYER 2 //For easy recordkeeping; this is a byond define
#define MID_TURF_LAYER 2.02
@@ -122,6 +123,7 @@
///Normal 1 per turf dynamic lighting objects
#define LIGHTING_PLANE 100
+#define RUNECHAT_PLANE (LIGHTING_PLANE + 1) // GOSH DARN IT LIGHTING, STOP EATING MY CHAT THINGS!
#define RAD_TEXT_LAYER 15.1
diff --git a/code/__DEFINES/maths.dm b/code/__DEFINES/maths.dm
index ce0dcdf6549..8ebc881ad6b 100644
--- a/code/__DEFINES/maths.dm
+++ b/code/__DEFINES/maths.dm
@@ -270,6 +270,11 @@
var/roundie = 1 * (0.1**decimals)
return "[round(number, roundie)][unit]"
+/// checks if a given turf's x/y coordinates fall within a given rectangle
+/// xwest, ynorth, xeast, ysouth are the bounds of the rectangle
+/// N/S is Y, Y increases as you go north
+/// E/W is X, X increases as you go east
+#define TURF_IN_RECTANGLE(turf, xwest, ynorth, xeast, ysouth) (turf.x >= xwest && turf.x <= xeast && turf.y >= ynorth && turf.y <= ysouth)
#define RANDOM(min, max) (rand(min*1000, max*1000)*0.001)
diff --git a/code/__DEFINES/preferences.dm b/code/__DEFINES/preferences.dm
index 3d6878efa23..dcae8051d10 100644
--- a/code/__DEFINES/preferences.dm
+++ b/code/__DEFINES/preferences.dm
@@ -148,6 +148,7 @@ GLOBAL_LIST_INIT(undie_position_strings, list("Under Clothes", "Over Clothes", "
#define PMC_UNBREAK_FAVORITE_PLAPS "/datum/interaction/bang/datum/interaction/funch" // Player Master Changelog
#define PMC_FENNY_FINISHED_124_QUESTS "and_killed_the_server" // Player Master Changelog
#define PMC_MY_PDA_FLIES_IN_FULL_COLOR "nekooooooooo" // Player Master Changelog
+#define PMC_MOMMYCHAT_IS_COOL "ill be your mommy tonight uwu" // Player Master Changelog
/// The master Preferences Changelog to check the player's prefs against.
/// includes a list of actions that need to be taken to update the player's prefs.
@@ -157,6 +158,7 @@ GLOBAL_LIST_INIT(undie_position_strings, list("Under Clothes", "Over Clothes", "
PMC_DAN_MESSED_UP_WHO_STUFF,\
PMC_FENNY_FINISHED_124_QUESTS,\
PMC_MY_PDA_FLIES_IN_FULL_COLOR,\
+ PMC_MOMMYCHAT_IS_COOL,\
)
#define PMR_WHY_DOES_EVERYTHING_DEFAULT_TO_OFF "lookingatyouwiretap" // Player Master Changelog
diff --git a/code/__DEFINES/say.dm b/code/__DEFINES/say.dm
index 383c3d7db22..b83c4c9d8b0 100644
--- a/code/__DEFINES/say.dm
+++ b/code/__DEFINES/say.dm
@@ -12,6 +12,8 @@
#define SPAN_SANS "sans"
#define SPAN_PAPYRUS "papyrus"
#define SPAN_REALLYBIG "reallybig"
+#define SPAN_SMALL "small"
+#define SPAN_SMALLER "small"
#define SPAN_COMMAND "command_headset"
#define SPAN_CLOWN "clown"
#define SPAN_SINGING "singing"
@@ -140,6 +142,7 @@
#define ONLY_OVERHEAD (1<<1)
// Append the player's name to the front
#define PUT_NAME_IN (1<<2)
+#define IS_EAVESDROP (1<<3)
#define EMOTE_HEADER_TEXT "\
The Following Chat Functions Exist \n\
diff --git a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm
index b1d51df6155..6a93c6dbe02 100644
--- a/code/__DEFINES/sound.dm
+++ b/code/__DEFINES/sound.dm
@@ -56,6 +56,11 @@
#define SOUND_MINIMUM_PRESSURE 10
#define FALLOFF_SOUNDS 1
+#define CLEAR_SOUND (1 << 0)
+#define BLOCK_SOUND_COMPLETE (1 << 1)
+#define BLOCK_SOUND_PARTIAL (1 << 2)
+#define SOUND_BLOCK_CORNER (1 << 3)
+
//default byond sound environments
#define SOUND_ENVIRONMENT_NONE -1
#define SOUND_ENVIRONMENT_GENERIC 0
@@ -397,6 +402,8 @@
#define CSP_INDEX_DISTANT_RANGE "dsr"
/// they dont have to be 3 letters, I just like it
+/// FUCK YOU COPILOT WHY THE FUCK ARE YOU CENSORING THE WORD TERRORIST FUCK YOU FUCK YOU
+#define RADIO_STATIC_SOUND 'sound/effects/counter_terrorists_win.ogg'
/*
gun_sound_properties = list(
diff --git a/code/__DEFINES/span.dm b/code/__DEFINES/span.dm
index 5d7e558e70a..3783b5cdd2f 100644
--- a/code/__DEFINES/span.dm
+++ b/code/__DEFINES/span.dm
@@ -151,6 +151,7 @@
#define span_singing(str) ("" + str + "")
#define span_slime(str) ("" + str + "")
#define span_small(str) ("" + str + "")
+#define span_smaller(str) ("" + str + "")
#define span_smalldanger(str) ("" + str + "")
#define span_smallnotice(str) ("" + str + "")
#define span_smallnoticeital(str) ("" + str + "")
diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index 4a450ec481c..4a72d32cb90 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -197,7 +197,7 @@
return
-// Better recursive loop, technically sort of not actually recursive cause that shit is stupid, enjoy.
+// Better recursive loop, technically sort of not actually recursive cause that sh1t is stupid, enjoy.
//No need for a recursive limit either
/proc/recursive_mob_check(atom/O,client_check=1,sight_check=1,include_radio=1)
@@ -238,7 +238,35 @@
return found_mobs
-/proc/get_hearers_in_view(R, atom/source)
+/obj/soundblocker
+ name = "Sound Blocker"
+ icon = 'icons/effects/landmarks_static.dmi'
+ icon_state = "tdome_admin" // trust me it makes sense
+ invisibility = INVISIBILITY_ABSTRACT
+ var/flag = BLOCK_SOUND_COMPLETE
+
+/obj/soundblocker/partial
+ name = "Partial Sound Blocker"
+ icon_state = "tdome_observer"
+ flag = BLOCK_SOUND_PARTIAL
+
+/obj/soundblocker/corner
+ name = "Corner Sound Blocker"
+ icon_state = "tdome_corner"
+ flag = BLOCK_SOUND_PARTIAL | SOUND_BLOCK_CORNER
+
+/obj/soundblocker/Initialize()
+ . = ..()
+ RegisterSignal(get_turf(src), COMSIG_UPDATE_SOUND_BLOCKERS, PROC_REF(UpdateSoundBlockers))
+ RegisterSignal(get_turf(src), COMSIG_CHECK_SOUND_BLOCKERS, PROC_REF(CheckSoundBlockers))
+
+/obj/soundblocker/proc/UpdateSoundBlockers()
+
+/obj/soundblocker/proc/CheckSoundBlockers()
+
+
+
+/proc/get_hearers_in_view(R, atom/source, exclude_players)
var/turf/T = get_turf(source)
. = list()
if(!T)
@@ -258,10 +286,193 @@
var/i = 0
while(i < length(processing))
var/atom/A = processing[++i]
+ processing += A.contents
+ if(exclude_players && istype(A, /mob))
+ var/mob/M = A
+ if(M.client)
+ continue // we'll get to them
if(A.flags_1 & HEAR_1)
. += A
SEND_SIGNAL(A, COMSIG_ATOM_HEARER_IN_VIEW, processing, .)
- processing += A.contents
+
+GLOBAL_LIST_EMPTY(chat_chuds)
+
+/proc/get_chatchud(atom/source)
+ for(var/i in 1 to LAZYLEN(GLOB.chat_chuds))
+ var/datum/chatchud/chud = GLOB.chat_chuds[i]
+ if(chud.ready)
+ return chud
+ var/datum/chatchud/chud = new /datum/chatchud()
+ GLOB.chat_chuds += chud
+ return chud
+
+/datum/chatchud
+ var/list/visible_close = list()
+ var/list/visible_far = list()
+ var/list/hidden_pathable = list()
+ var/list/hidden_inaccessible = list()
+ var/ready = TRUE
+
+/datum/chatchud/proc/putback()
+ visible_close.Cut()
+ visible_far.Cut()
+ hidden_pathable.Cut()
+ ready = TRUE // snip snip no jutsu wuz here
+
+/obj/effect/temp_visual/debug_heart
+ name = "love heart"
+ icon = 'icons/effects/effects.dmi'
+ icon_state = "heart"
+ duration = 2 SECONDS
+
+/obj/effect/temp_visual/numbers
+ name = "numberwang"
+ icon = 'icons/effects/numbers.dmi'
+ icon_state = "blank"
+ duration = 2 SECONDS
+
+/obj/effect/temp_visual/numbers/backgrounded
+ name = "numberwang"
+ icon = 'icons/effects/numbers.dmi'
+ icon_state = "blank_ish"
+ duration = 3 SECONDS
+
+/obj/effect/temp_visual/numbers/Initialize(mapload, numb, coler)
+ . = ..()
+ numericate(numb, coler)
+
+/obj/effect/temp_visual/numbers/proc/numericate(numb, coler)
+ if(numb > 99999999)
+ numb = 99999999
+ var/list/splitnumbers = list()
+ /// splits numb into its digits, from most to least significant
+ if(numb <= 0)
+ splitnumbers += 0
+ else
+ var/tries = 10
+ while(numb > 0 && tries-- > 0)
+ splitnumbers += numb % 10
+ numb /= 10
+ numb = floor(numb)
+ /// now we have to reverse the list
+ splitnumbers = reverseList(splitnumbers)
+ var/offset = 0
+ /// now we can display the numbers
+ for(var/i in 1 to LAZYLEN(splitnumbers))
+ var/digy = clamp(LAZYACCESS(splitnumbers, i), 0, 9)
+ var/image/numbie = image('icons/effects/numbers.dmi', src, "[digy]")
+ numbie.pixel_x = offset
+ overlays += numbie
+ offset += 9
+ if(coler)
+ color = coler
+
+#define IS_IN_VIEWER_RECT(turf) TURF_IN_RECTANGLE(turf, westest, northest, eastest, southest)
+
+/// returns a datum of players and how well they can hear the source
+/proc/get_listening(atom/source, close_range, long_range, quiet)
+ var/area/A = get_area(source)
+ var/private = A.private
+ var/datum/chatchud/CC = get_chatchud(source)
+ var/turf/source_turf = get_turf(source)
+ var/debug_i = 0
+ dingus:
+ for(var/client/C in GLOB.clients)
+ var/mob/M = C.mob
+ if(isnewplayer(M))
+ continue dingus // quit talkin to ghosts, unless ur an admeme
+ var/turf/viewer_turf = get_turf(M)
+ if(source_turf.z != viewer_turf.z) // TODO: let people yell up stairs
+ continue dingus
+ var/am_widescreen = C.prefs.widescreenpref
+ var/westest
+ if(am_widescreen)
+ westest = max(viewer_turf.x - 8, 1)
+ else
+ westest = max(viewer_turf.x - 6, 1)
+ var/eastest
+ if(am_widescreen)
+ eastest = min(viewer_turf.x + 8, world.maxx)
+ else
+ eastest = min(viewer_turf.x + 6, world.maxx)
+ var/northest
+ if(am_widescreen)
+ northest = max(viewer_turf.y - 7, 1)
+ else
+ northest = max(viewer_turf.y - 6, 1)
+ var/southest
+ if(am_widescreen)
+ southest = min(viewer_turf.y + 6, world.maxy)
+ else
+ southest = min(viewer_turf.y + 6, world.maxy)
+ var/list/things_in_viewer_los = view(7, viewer_turf)
+ if(SSchat.debug_chud)
+ var/turf/t_northwest = locate(westest, northest, viewer_turf.z)
+ var/turf/t_southeast = locate(eastest, southest, viewer_turf.z)
+ var/turf/t_northeast = locate(eastest, northest, viewer_turf.z)
+ var/turf/t_southwest = locate(westest, southest, viewer_turf.z)
+ /// draw a beam box!
+ t_northeast.Beam(t_northwest, icon_state = "1-full", time = 3 SECONDS, show_to = list(C))
+ t_northeast.Beam(t_southeast, icon_state = "1-full", time = 3 SECONDS, show_to = list(C))
+ t_southeast.Beam(t_southwest, icon_state = "1-full", time = 3 SECONDS, show_to = list(C))
+ t_southwest.Beam(t_northwest, icon_state = "1-full", time = 3 SECONDS, show_to = list(C))
+ var/in_close_view
+ if(SSchat.debug_use_cool_los_proc)
+ in_close_view = isInSight(source_turf, viewer_turf)
+ else
+ in_close_view = (source_turf in things_in_viewer_los)
+ var/in_rect = IS_IN_VIEWER_RECT(source_turf)
+ if(!in_rect && get_dist(source_turf, viewer_turf) > long_range)
+ continue dingus
+ // basic visibility, fulfills these conditions:
+ // 1. must be in the box of visibility, so we dont have to play with pathing nonsense
+ // 2. must be in the line of sight of the hearer, so it shouldnt be over darkness
+ // basically if they're on screen, and either of the ranges are met, they're visible and we can skip the pathing
+ if(in_rect && in_close_view)
+ if(get_dist(source_turf, viewer_turf) <= close_range)
+ CC.visible_close[M] = TRUE
+ continue dingus
+ // else if(get_dist(source_turf, viewer_turf) <= long_range)
+ // CC.visible_far[M] = TRUE
+ // continue dingus
+ // if the source is in a Private area,
+ // and the viewer is either not in the line of sight or not in the box of visibility,
+ // then they're hidden, so we dont bleat out a bunch of horny moaning to the whole world
+ if(private)
+ continue dingus
+ if(in_rect && !quiet)
+ CC.hidden_pathable[M] = source_turf // close enough
+ continue dingus
+ // now the fun begins. Try to find a path to them
+ var/list/soundwalk = get_path_to(source_turf, viewer_turf, long_range, use_visibility = TRUE)
+ // they're closed off, no path to them, but they're still within long range
+ if(!islist(soundwalk))
+ CC.hidden_inaccessible[M] = TRUE // mark them as hidden
+ continue dingus
+ // the path from source to viewer is too long, so we consider them out of range
+ if(!LAZYLEN(soundwalk) || LAZYLEN(soundwalk) > long_range)
+ continue dingus
+ // now walk through the path and find the first tile that can fulfill all of these conditions:
+ // 1. must be in the box of visibility
+ // 2. must be in the line of sight of the hearer
+ debug_i = 0
+ var/cole = SSchat.debug_chud && safepick("#FF0000", "#00FF00", "#0000FF", "#FFFF00", "#FF00FF", "#00FFFF")
+ for(var/turf/T as anything in soundwalk) // for each step we take...
+ if(SSchat.debug_chud)
+ new /obj/effect/temp_visual/numbers/backgrounded(T, debug_i, cole)
+ debug_i++
+ if(!IS_IN_VIEWER_RECT(T)) // ...check if our turf is in the viewer's box of visibility
+ continue // we can't see them
+ // if(!(T in things_in_viewer_los)) // if they're in the box but not in the line of sight,
+ // continue // we can't see them
+ // at this point, we have met these conditions
+ if(SSchat.debug_chud)
+ new /obj/effect/temp_visual/debug_heart(T)
+ CC.hidden_pathable[M] = T
+ continue dingus // move along, dingus
+ // couldnt find anything! mark them as hidden
+ CC.hidden_inaccessible[M] = TRUE
+ return CC
GLOBAL_LIST_EMPTY(chat_chuds)
diff --git a/code/__HELPERS/path.dm b/code/__HELPERS/path.dm
index 96becc1a590..ecec789cfa1 100644
--- a/code/__HELPERS/path.dm
+++ b/code/__HELPERS/path.dm
@@ -16,7 +16,7 @@
* * simulated_only: Whether we consider turfs without atmos simulation (AKA do we want to ignore space)
* * exclude: If we want to avoid a specific turf, like if we're a mulebot who already got blocked by some turf
*/
-/proc/get_path_to(caller, end, max_distance = 30, mintargetdist, id=null, simulated_only = TRUE, turf/exclude)
+/proc/get_path_to(caller, end, max_distance = 30, mintargetdist, id=null, simulated_only = TRUE, turf/exclude, use_visibility)
if(!caller || !get_turf(end))
return
@@ -26,7 +26,7 @@
l = SSpathfinder.mobs.getfree(caller)
var/list/path
- var/datum/pathfind/pathfind_datum = new(caller, end, id, max_distance, mintargetdist, simulated_only, exclude)
+ var/datum/pathfind/pathfind_datum = new(caller, end, id, max_distance, mintargetdist, simulated_only, exclude, use_visibility)
path = pathfind_datum.search()
qdel(pathfind_datum)
@@ -40,7 +40,7 @@
* Note that this can only be used inside the [datum/pathfind][pathfind datum] since it uses variables from said datum
* If you really want to optimize things, optimize this, cuz this gets called a lot
*/
-#define CAN_STEP(cur_turf, next) (next && !next.density && cur_turf.Adjacent(next) && !(simulated_only && SSpathfinder.space_type_cache[next.type]) && !cur_turf.LinkBlockedWithAccess(next,caller, id) && (next != avoid))
+#define CAN_STEP(cur_turf, next) (next && (use_visibility ? (checkvis(next)) : (!next.density && cur_turf.Adjacent(next) && !cur_turf.LinkBlockedWithAccess(next,caller, id,use_visibility))) && !(simulated_only && SSpathfinder.space_type_cache[next.type]) && (next != avoid))
/// Another helper macro for JPS, for telling when a node has forced neighbors that need expanding
#define STEP_NOT_HERE_BUT_THERE(cur_turf, dirA, dirB) ((!CAN_STEP(cur_turf, get_step(cur_turf, dirA)) && CAN_STEP(cur_turf, get_step(cur_turf, dirB))))
@@ -116,8 +116,10 @@
var/simulated_only
/// A specific turf we're avoiding, like if a mulebot is being blocked by someone t-posing in a doorway we're trying to get through
var/turf/avoid
+ /// whether to use opacity checks instead of density checks
+ var/use_visibility
-/datum/pathfind/New(atom/movable/caller, atom/goal, id, max_distance, mintargetdist, simulated_only, avoid)
+/datum/pathfind/New(atom/movable/caller, atom/goal, id, max_distance, mintargetdist, simulated_only, avoid, use_visibility)
src.caller = caller
end = get_turf(goal)
open = new /datum/heap(/proc/HeapPathWeightCompare)
@@ -127,6 +129,7 @@
src.mintargetdist = mintargetdist
src.simulated_only = simulated_only
src.avoid = avoid
+ src.use_visibility = use_visibility
/// The proc you use to run the search, returns FALSE if it's invalid, an empty list if no path could be found, or a valid path to the target
/datum/pathfind/proc/search()
@@ -319,6 +322,18 @@
unwind_path(possible_child_node)
return
+/datum/pathfind/proc/checkvis(turf/destination_turf)
+ if(locate(/mob/living) in destination_turf)
+ return TRUE
+ if(destination_turf.opacity)
+ return FALSE
+ for(var/atom/iter_atom as anything in destination_turf)
+ if(iter_atom.opacity)
+ if(istype(iter_atom, /obj/structure/window) || istype(iter_atom, /obj/machinery/door/window))
+ continue
+ return FALSE
+ return TRUE
+
/**
* For seeing if we can actually move between 2 given turfs while accounting for our access and the caller's pass_flags
*
diff --git a/code/controllers/subsystem/chat.dm b/code/controllers/subsystem/chat.dm
index 791f796f0e8..556c23bf7c3 100644
--- a/code/controllers/subsystem/chat.dm
+++ b/code/controllers/subsystem/chat.dm
@@ -60,6 +60,24 @@ SUBSYSTEM_DEF(chat)
priority = FIRE_PRIORITY_CHAT
init_order = INIT_ORDER_CHAT
+ var/forbid_ghosting = FALSE
+ var/chat_display_plane = RUNECHAT_PLANE
+ /*
+ ** Base
+ */
+ var/base_say_distance = 7
+ var/extended_say_distance = 16
+
+ var/base_whisper_distance = 1
+ var/extended_whisper_distance = 3
+
+ var/base_sing_distance = 15
+ var/extended_sing_distance = INFINITY
+ var/base_yell_distance = 15
+ var/extended_yell_distance = INFINITY
+ var/far_distance = 6 // how far until they're considered offscreen
+
+
var/list/payload_by_client = list()
/// All the lookups for translating emotes to say prefixes
var/list/emoticon_cache = list()
@@ -145,6 +163,7 @@ SUBSYSTEM_DEF(chat)
var/img_size = 125
var/headspace = 4
var/debug_chud = FALSE
+ var/debug_use_cool_los_proc = FALSE
var/list/colorable_keys = list(
"TopBoxGradient1",
"TopBoxGradient2",
@@ -199,7 +218,7 @@ SUBSYSTEM_DEF(chat)
// build_stock_image_packs()
. = ..()
spawn(5 SECONDS)
- to_chat(world, span_boldnotice("Initialized [LAZYLEN(emoticon_cache)] emoticons! ;D"))
+ to_chat(world, span_boldnotice("Initialized [LAZYLEN(emoticon_cache)] (non-functional) emoticons! ;D"))
to_chat(world, span_boldnotice("Initialized [LAZYLEN(flirts)] flirty messages! <3"))
to_chat(world, span_boldnotice("VisualChat engaged! Have a very visual day! <3"))
// to_chat(world, span_boldnotice("Initialized [LAZYLEN(stock_image_packs)] stock image packs! 'w'"))
diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm
index 01fdf6657f5..b57c057bda9 100644
--- a/code/controllers/subsystem/job.dm
+++ b/code/controllers/subsystem/job.dm
@@ -31,20 +31,20 @@ SUBSYSTEM_DEF(job)
return ..()
/datum/controller/subsystem/job/proc/set_overflow_role(new_overflow_role)
- var/datum/job/new_overflow = GetJob(new_overflow_role)
- var/cap = CONFIG_GET(number/overflow_cap)
-
- new_overflow.allow_bureaucratic_error = FALSE
- new_overflow.spawn_positions = cap
- new_overflow.total_positions = cap
-
- if(new_overflow_role != overflow_role)
- var/datum/job/old_overflow = GetJob(overflow_role)
- old_overflow.allow_bureaucratic_error = initial(old_overflow.allow_bureaucratic_error)
- old_overflow.spawn_positions = initial(old_overflow.spawn_positions)
- old_overflow.total_positions = initial(old_overflow.total_positions)
- overflow_role = new_overflow_role
- JobDebug("Overflow role set to : [new_overflow_role]")
+ // var/datum/job/new_overflow = GetJob(new_overflow_role)
+ // var/cap = CONFIG_GET(number/overflow_cap)
+
+ // new_overflow.allow_bureaucratic_error = FALSE
+ // new_overflow.spawn_positions = cap
+ // new_overflow.total_positions = cap
+
+ // if(new_overflow_role != overflow_role)
+ // var/datum/job/old_overflow = GetJob(overflow_role)
+ // old_overflow.allow_bureaucratic_error = initial(old_overflow.allow_bureaucratic_error)
+ // old_overflow.spawn_positions = initial(old_overflow.spawn_positions)
+ // old_overflow.total_positions = initial(old_overflow.total_positions)
+ // overflow_role = new_overflow_role
+ // JobDebug("Overflow role set to : [new_overflow_role]")
/datum/controller/subsystem/job/proc/SetupOccupations(faction = "Station")
occupations = list()
diff --git a/code/controllers/subsystem/prefbreak.dm b/code/controllers/subsystem/prefbreak.dm
index cfcfe1df84d..e9c5d39dcc4 100644
--- a/code/controllers/subsystem/prefbreak.dm
+++ b/code/controllers/subsystem/prefbreak.dm
@@ -252,6 +252,23 @@ SUBSYSTEM_DEF(prefbreak) // ALL ABOARD THE S.S. PREFBREAK OFF TO **** YOUR *****
PREFBROKEN
return consumer.admin_wire_tap // kinda vital here
+/datum/prefcheck/see_fancy_offscreen_runechat
+ index = SEE_FANCY_OFF_SCREEN_RUNECHAT
+
+/datum/prefcheck/see_fancy_offscreen_runechat/allowed(datum/preferences/consumer)
+ PREFBROKEN
+ return TRUE
+ // return consumer.see_fancy_offscreen_runechat // kinda vital here
+
+
+/datum/prefcheck/see_horny_furry_stuff
+ index = SHOW_ME_HORNY_FURRIES
+
+/datum/prefcheck/see_horny_furry_stuff/allowed(datum/preferences/consumer)
+ PREFBROKEN
+ return CHECK_BITFIELD(consumer.chat_toggles, CHAT_SEE_COOLCHAT) // kinda vital here
+ // return consumer.see_fancy_offscreen_runechat // kinda vital here
+
/datum/prefcheck/see_horny_furry_stuff
index = SHOW_ME_HORNY_FURRIES
diff --git a/code/datums/beam.dm b/code/datums/beam.dm
index 3b3bbd33849..b7ddfdc1aff 100644
--- a/code/datums/beam.dm
+++ b/code/datums/beam.dm
@@ -11,12 +11,13 @@
var/finished = 0
var/turf/target_oldloc
var/turf/origin_oldloc
+ var/list/showing_to = list()
var/static_beam = 0
var/beam_type = /obj/effect/ebeam //must be subtype
var/timing_id = null
var/recalculating = FALSE
-/datum/beam/New(beam_origin,beam_target,beam_icon='icons/effects/beam.dmi',beam_icon_state="b_beam",time=50,maxdistance=10,btype = /obj/effect/ebeam,beam_sleep_time=3)
+/datum/beam/New(beam_origin,beam_target,beam_icon='icons/effects/beam.dmi',beam_icon_state="b_beam",time=50,maxdistance=10,btype = /obj/effect/ebeam,beam_sleep_time=3,list/show_to)
origin = beam_origin
origin_oldloc = get_turf(origin)
target = beam_target
@@ -29,6 +30,12 @@
icon = beam_icon
icon_state = beam_icon_state
beam_type = btype
+ if(!isnull(show_to))
+ var/list/to_show = islist(show_to) ? show_to : list(show_to)
+ for(var/thing in to_show)
+ var/client/C = extract_client(thing)
+ if(C)
+ showing_to += C
if(time < INFINITY)
addtimer(CALLBACK(src,PROC_REF(End)), time)
@@ -83,6 +90,11 @@
/datum/beam/proc/Reset()
for(var/obj/effect/ebeam/B in elements)
+ if(B.mypic && LAZYLEN(showing_to))
+ for(var/client/C in showing_to)
+ if(!C)
+ continue
+ C.images -= B.mypic
qdel(B)
elements.Cut()
@@ -109,7 +121,7 @@
for(N in 0 to length-1 step 32)//-1 as we want < not <=, but we want the speed of X in Y to Z and step X
if(finished)
break
- var/obj/effect/ebeam/X = new beam_type(origin_oldloc)
+ var/obj/effect/ebeam/X = new beam_type(origin_oldloc, showing_to)
X.owner = src
elements += X
@@ -148,6 +160,14 @@
X.pixel_x = Pixel_x
X.pixel_y = Pixel_y
+ if(showing_to)
+ // snapshot no jutsu
+ var/image/II = image(X.icon, X.loc, X.icon_state, X.layer, X.dir, X.pixel_x, X.pixel_y)
+ II.transform = X.transform
+ X.mypic = II
+ for(var/client/C in showing_to)
+ if(C)
+ C.images += II
CHECK_TICK
afterDraw()
@@ -155,6 +175,12 @@
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
anchored = TRUE
var/datum/beam/owner
+ var/image/mypic
+
+/obj/effect/ebeam/Initialize(mapload, shows)
+ . = ..()
+ if(shows)
+ invisibility = INVISIBILITY_ABSTRACT
/obj/effect/ebeam/Destroy()
owner = null
@@ -165,7 +191,16 @@
/obj/effect/ebeam/singularity_act()
return
-/atom/proc/Beam(atom/BeamTarget,icon_state="b_beam",icon='icons/effects/beam.dmi',time=50, maxdistance=10,beam_type=/obj/effect/ebeam,beam_sleep_time = 3)
- var/datum/beam/newbeam = new(src,BeamTarget,icon,icon_state,time,maxdistance,beam_type,beam_sleep_time)
+/atom/proc/Beam(
+ atom/BeamTarget,
+ icon_state="b_beam",
+ icon='icons/effects/beam.dmi',
+ time=50,
+ maxdistance=10,
+ beam_type=/obj/effect/ebeam,
+ beam_sleep_time = 3,
+ list/show_to = list()
+)
+ var/datum/beam/newbeam = new(src,BeamTarget,icon,icon_state,time,maxdistance,beam_type,beam_sleep_time,show_to)
INVOKE_ASYNC(newbeam, TYPE_PROC_REF(/datum/beam/,Start))
return newbeam
diff --git a/code/datums/chatmessage.dm b/code/datums/chatmessage.dm
index bab4167dda6..85e241d5023 100644
--- a/code/datums/chatmessage.dm
+++ b/code/datums/chatmessage.dm
@@ -7,6 +7,8 @@
#define CHAT_MESSAGE_WIDTH 100 // pixels
#define CHAT_MESSAGE_MAX_LENGTH 200 // characters
+// GLOBAL_LIST_EMPTY(verbal_punch_lasers)
+
/**
* # Chat Message Overlay
*
@@ -23,6 +25,12 @@
var/scheduled_destruction
/// Contains the approximate amount of lines for height decay
var/approx_lines
+ /// is offscreen? put it somewhere they can see it then
+ var/offscreen
+ /// is eavesdrop? if so, make it smaller
+ var/eavesdrop
+ /// an alternative turf to display the message at
+ var/turf/alt_display
/**
* Constructs a chat message overlay
@@ -34,7 +42,7 @@
* * extra_classes - Extra classes to apply to the span that holds the text
* * lifespan - The lifespan of the message in deciseconds
*/
-/datum/chatmessage/New(text, atom/target, mob/owner, list/extra_classes = list(), lifespan = CHAT_MESSAGE_LIFESPAN)
+/datum/chatmessage/New(text, atom/target, mob/owner, list/extra_classes = list(), lifespan = CHAT_MESSAGE_LIFESPAN, list/data = list(), datum/rental_mommy/chat/mommy = null)
. = ..()
if (!istype(target))
CRASH("Invalid target given for chatmessage")
@@ -42,6 +50,16 @@
stack_trace("/datum/chatmessage created with [isnull(owner) ? "null" : "invalid"] mob owner")
qdel(src)
return
+ if(CHECK_PREFS(owner, SEE_FANCY_OFF_SCREEN_RUNECHAT))
+ if(mommy)
+ if(mommy.display_turf && mommy.display_turf != target)
+ alt_display = mommy.display_turf
+ offscreen = TRUE
+ else
+ alt_display = data["display_turf"] || null
+ if((get_dist(owner, (alt_display || target)) > 6 || data["is_far"])) // SD screens are 7 radius, but the UI covers a bit of that
+ offscreen = TRUE
+ eavesdrop = data["is_eaves"] || FALSE
INVOKE_ASYNC(src,PROC_REF(generate_image), text, target, owner, extra_classes, lifespan)
/datum/chatmessage/Destroy()
@@ -52,6 +70,7 @@
owned_by = null
message_loc = null
message = null
+ alt_display = null
return ..()
/**
@@ -127,7 +146,6 @@
// We dim italicized text to make it more distinguishable from regular text
var/tgt_color = extra_classes.Find("italics") ? target.chat_color_darkened : target.chat_color
-
// Approximate text height
// Note we have to replace HTML encoded metacharacters otherwise MeasureText will return a zero height
// BYOND Bug #2563917
@@ -138,12 +156,13 @@
approx_lines = max(1, mheight / CHAT_MESSAGE_APPROX_LHEIGHT)
// Translate any existing messages upwards, apply exponential decay factors to timers
+ var/atom/remembered_location = alt_display || target
message_loc = alt_display || target
- if(offscreen) // if its offscreen, put it somewhere they can see it
+ if(offscreen && get_dist(owner, target) > 6) // SD screens are 7 radius, but the UI covers a bit of that
var/turf/ownerturf = get_turf(owner)
var/turf/targetturf = get_turf(message_loc)
- var/westest = max(ownerturf.x - 9, 1)
- var/eastest = min(ownerturf.x + 9, world.maxx)
+ var/westest = max(ownerturf.x - 7, 1)
+ var/eastest = min(ownerturf.x + 7, world.maxx)
var/northest = max(ownerturf.y - 7, 1)
var/southest = min(ownerturf.y + 6, world.maxy)
var/list/turfe = getline(targetturf, ownerturf)
@@ -167,36 +186,58 @@
if (owned_by.seen_messages)
var/idx = 1
var/combined_height = approx_lines
- for(var/msg in owned_by.seen_messages[message_loc])
+ for(var/msg in owned_by.seen_messages[remembered_location])
var/datum/chatmessage/m = msg
- animate(m.message, pixel_y = m.message.pixel_y + mheight, time = CHAT_MESSAGE_SPAWN_TIME)
- combined_height += m.approx_lines
- var/sched_remaining = m.scheduled_destruction - world.time
- if (sched_remaining > CHAT_MESSAGE_SPAWN_TIME)
- var/remaining_time = (sched_remaining) * (CHAT_MESSAGE_EXP_DECAY ** idx++) * (CHAT_MESSAGE_HEIGHT_DECAY ** combined_height)
- m.scheduled_destruction = world.time + remaining_time
- addtimer(CALLBACK(m,PROC_REF(end_of_life)), remaining_time, TIMER_UNIQUE|TIMER_OVERRIDE)
+ if(m.message)
+ animate(m.message, pixel_y = m.message.pixel_y + mheight, time = CHAT_MESSAGE_SPAWN_TIME)
+ combined_height += m.approx_lines
+ var/sched_remaining = m.scheduled_destruction - world.time
+ if (sched_remaining > CHAT_MESSAGE_SPAWN_TIME)
+ var/remaining_time = (sched_remaining) * (CHAT_MESSAGE_EXP_DECAY ** idx++) * (CHAT_MESSAGE_HEIGHT_DECAY ** combined_height)
+ m.scheduled_destruction = world.time + remaining_time
+ addtimer(CALLBACK(m,PROC_REF(end_of_life)), remaining_time, TIMER_UNIQUE|TIMER_OVERRIDE)
+
+ if(SSchat.debug_chud)
+ var/turf/ownerturf = get_turf(owner)
+ var/turf/targetturf = get_turf(message_loc)
+ ownerturf.Beam(targetturf, icon_state = "g_beam", time = 3 SECONDS)
+ ownerturf.Beam(get_turf(target), icon_state = "1-full", time = 3 SECONDS)
// Build message image
message = image(loc = message_loc, layer = CHAT_LAYER)
- message.plane = CHAT_PLANE
+ message.plane = SSchat.chat_display_plane
message.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA | KEEP_APART
message.alpha = 0
- message.pixel_y = owner.bound_height * 0.95
+ var/hight = (alt_display || offscreen) ? 0 : owner.bound_height
+ message.pixel_y = hight * 0.95
message.maptext_width = CHAT_MESSAGE_WIDTH
message.maptext_height = mheight
message.maptext_x = (CHAT_MESSAGE_WIDTH - owner.bound_width) * -0.5
message.maptext = complete_text
+ var/alphatomakeit = 255
+ if(eavesdrop)
+ alphatomakeit /= 2
+ if(offscreen)
+ alphatomakeit /= 2
+ if(SPAN_SMALL in extra_classes)
+ alphatomakeit /= 2
+ if(SPAN_SMALLER in extra_classes)
+ alphatomakeit /= 2
+
+ // message.pixel_x = rand(-40, 40)
+ // message.pixel_y = rand(-40, 40)
// View the message
- LAZYADDASSOC(owned_by.seen_messages, message_loc, src)
+ LAZYADDASSOC(owned_by.seen_messages, remembered_location, src)
owned_by.images |= message
- animate(message, alpha = 255, time = CHAT_MESSAGE_SPAWN_TIME)
+ animate(message, alpha = alphatomakeit, time = CHAT_MESSAGE_SPAWN_TIME)
// Prepare for destruction
scheduled_destruction = world.time + (lifespan - CHAT_MESSAGE_EOL_FADE)
addtimer(CALLBACK(src,PROC_REF(end_of_life)), lifespan - CHAT_MESSAGE_EOL_FADE, TIMER_UNIQUE|TIMER_OVERRIDE)
+ if(offscreen)
+ new /obj/effect/buildmode_line(owner.client, get_turf(owner), message_loc, "I'm a line!", lifespan - CHAT_MESSAGE_EOL_FADE)
/**
* Applies final animations to overlay CHAT_MESSAGE_EOL_FADE deciseconds prior to message deletion
@@ -214,7 +255,7 @@
* * raw_message - The text content of the message
* * spans - Additional classes to be added to the message
*/
-/mob/proc/create_chat_message(atom/movable/speaker, datum/language/message_language, raw_message, list/spans, runechat_flags = NONE)
+/mob/proc/create_chat_message(atom/movable/speaker, datum/language/message_language, raw_message, list/spans, runechat_flags = NONE, list/data = list(), datum/rental_mommy/chat/mommy = null)
// Ensure the list we are using, if present, is a copy so we don't modify the list provided to us
spans = spans ? spans.Copy() : list()
@@ -244,9 +285,9 @@
break math // mathematical
// Display visual above source
if(runechat_flags & EMOTE_MESSAGE)
- new /datum/chatmessage(raw_message, speaker, src, list("emote", "italics"))
+ new /datum/chatmessage(raw_message, speaker, src, list("emote", "italics"), null, data, mommy)
else
- new /datum/chatmessage(lang_treat(speaker, message_language, raw_message, spans, null, TRUE), speaker, src, spans)
+ new /datum/chatmessage(lang_treat(speaker, message_language, raw_message, spans, null, TRUE), speaker, src, spans, null, data, mommy)
// Tweak these defines to change the available color ranges
diff --git a/code/datums/emotes.dm b/code/datums/emotes.dm
index a739f234866..0210262c221 100644
--- a/code/datums/emotes.dm
+++ b/code/datums/emotes.dm
@@ -100,7 +100,7 @@
user.audible_message(msg, deaf_message = msg, audible_message_flags = message_flags, hearing_distance = message_range, data = list("mom" = mommy))
else
user.visible_message(msg, blind_message = msg, visible_message_flags = message_flags, vision_distance = message_range, data = list("mom" = mommy))
- if(mommy)
+ if(mommy && !mommy.available)
mommy.checkin()
// OI WHAT IF YE MUM WERE BUILT ON BA'REYS
diff --git a/code/datums/position_point_vector.dm b/code/datums/position_point_vector.dm
index 0f6968a80e7..1ff80c7a933 100644
--- a/code/datums/position_point_vector.dm
+++ b/code/datums/position_point_vector.dm
@@ -138,6 +138,7 @@
var/starting_x = 0 //just like before, pixels from EDGE of map! This is set in initialize_location().
var/starting_y = 0
var/starting_z = 0
+ var/inuse = FALSE
/datum/point/vector/New(_x, _y, _z, _pixel_x = 0, _pixel_y = 0, _angle, _speed, initial_increment = 0)
..()
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index b36116edc9a..3e05b95cfda 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -18,6 +18,7 @@ GLOBAL_LIST_INIT(area_weather_list, list(WEATHER_ALL))
invisibility = INVISIBILITY_LIGHTING
var/safe_town
+ var/private = FALSE
/// Set in New(); preserves the name set by the map maker, even if renamed by the Blueprints.
var/map_name
diff --git a/code/game/objects/effects/sound_emitter.dm b/code/game/objects/effects/sound_emitter.dm
index 78dd38035f5..bd7a2a7f96e 100644
--- a/code/game/objects/effects/sound_emitter.dm
+++ b/code/game/objects/effects/sound_emitter.dm
@@ -53,7 +53,7 @@
invisibility = 0
snd = /datum/looping_sound/ambient/debug3
-/*
+
/obj/effect/sound_emitter/frogs
name = "sound emitter (frogs)"
desc = "Sound emitter for frog noises, even if no frogs."
@@ -86,4 +86,4 @@
icon_state = "rock"
snd = /datum/looping_sound/soundrock/creek
synchronize = TRUE
-*/
+
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index f62477da473..ee6495675a3 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -58,6 +58,8 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
var/pokesound = 'sound/weapons/tap.ogg'
var/usesound = null
var/throwhitsound = null
+ var/equipsound = null
+ var/tableplacesound = null
/// Weight class for how much storage capacity it uses and how big it physically is meaning storages can't hold it if their maximum weight class isn't as high as it.
var/w_class = WEIGHT_CLASS_NORMAL
@@ -227,6 +229,11 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
if(!special_transform && transform != initial(transform))
special_transform = transform
+ if(!isnull(equipsound))
+ listify(equipsound)
+ if(!isnull(tableplacesound))
+ listify(tableplacesound)
+
/// CB Dual Wielding
if(force != 0)
if(w_class < DUAL_WIELDING_MAX_WEIGHT_ALLOWED)
@@ -557,6 +564,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
/obj/item/proc/pickup(mob/user)
SHOULD_CALL_PARENT(TRUE)
SEND_SIGNAL(src, COMSIG_ITEM_PICKUP, user)
+ play_equip_sound()
item_flags |= IN_INVENTORY
add_hud_actions(user)
@@ -1298,3 +1306,13 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
var/mob/living/target = over
L.do_give(target)
return ..()
+
+/obj/item/proc/play_equip_sound(volume=50)
+ if(!LAZYLEN(equipsound))
+ return
+ playsound(src, safepick(equipsound), volume, TRUE)
+
+/obj/item/proc/after_placed_on_table(obj/structure/table, volume=50)
+ if(!LAZYLEN(tableplacesound))
+ return
+ playsound(src, safepick(tableplacesound), volume, TRUE)
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index 86fb523b2e9..4d6ec7465c3 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -211,6 +211,8 @@
return ..()
/obj/structure/table/proc/AfterPutItemOnTable(obj/item/I, mob/living/user)
+ if(istype(I))
+ I.after_placed_on_table(src, user)
return
/obj/structure/table/alt_attack_hand(mob/user)
diff --git a/code/game/say.dm b/code/game/say.dm
index 0a6da57e058..458ca1c8584 100644
--- a/code/game/say.dm
+++ b/code/game/say.dm
@@ -20,7 +20,16 @@ And the base of the send_speech() proc, which is the core of saycode.
/atom/movable/proc/can_speak()
return 1
-/atom/movable/proc/send_speech(message, range = 7, atom/movable/source = src, bubble_type, list/spans, datum/language/message_language = null, message_mode, just_chat)
+/atom/movable/proc/send_speech(
+ message,
+ range = 7,
+ atom/movable/source = src,
+ bubble_type,
+ list/spans,
+ datum/language/message_language = null,
+ message_mode,
+ just_chat,
+)
var/rendered = compose_message(src, message_language, message, , spans, message_mode, source)
for(var/_AM in get_hearers_in_view(range, source))
var/atom/movable/AM = _AM
@@ -101,7 +110,8 @@ And the base of the send_speech() proc, which is the core of saycode.
/atom/movable/proc/say_mod(input, message_mode, datum/rental_mommy/chat/momchat)
var/ending = copytext_char(input, -1)
- if(message_mode == MODE_WHISPER)
+ var/beginning = copytext_char(input, 1)
+ if(message_mode == MODE_WHISPER || beginning == "#")
. = verb_whisper
momchat?.message_mode = MODE_WHISPER
else if(message_mode == MODE_SING)
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 3b3cac263ac..16d79d1a70a 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -53,6 +53,7 @@
var/list/atom/movable/opacity_sources
var/radiation_turf = 0
+ var/sound_flags = 0
/turf/vv_edit_var(var_name, var_value)
diff --git a/code/modules/buildmode/effects/line.dm b/code/modules/buildmode/effects/line.dm
index d21c0787fa3..6c288461bd0 100644
--- a/code/modules/buildmode/effects/line.dm
+++ b/code/modules/buildmode/effects/line.dm
@@ -2,7 +2,7 @@
var/image/I
var/client/cl
-/obj/effect/buildmode_line/New(client/C, atom/atom_a, atom/atom_b, linename)
+/obj/effect/buildmode_line/New(client/C, atom/atom_a, atom/atom_b, linename, duration)
name = linename
loc = get_turf(atom_a)
I = image('icons/misc/mark.dmi', src, "line", 19.0)
@@ -18,6 +18,8 @@
transform = mat
cl = C
cl.images += I
+ if(duration)
+ QDEL_IN(src, duration)
/obj/effect/buildmode_line/Destroy()
if(I)
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 6a5462e5037..ff971fae93f 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -464,6 +464,10 @@ GLOBAL_LIST_EMPTY(preferences_datums)
/// Button to switch from input bar to hotkey mode.
var/input_mode_hotkey = "Tab"
+ /// lets the user see runechat that's offscreen
+ var/see_fancy_offscreen_runechat = TRUE
+ /// lets the user see runechat that's hidden behind a wall
+ var/see_hidden_runechat = TRUE
/datum/preferences/New(client/C)
parent = C
diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm
index 39de2f67982..86b50ccd4f0 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -125,6 +125,10 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
pda_skin = "Random!"
WRITE_FILE(S["pda_skin"], pda_skin)
current_version |= PMC_MY_PDA_FLIES_IN_FULL_COLOR
+ if(PMC_MOMMYCHAT_IS_COOL) // i broke it =3
+ pda_skin = "Random!"
+ WRITE_FILE(S["pda_skin"], pda_skin)
+ current_version |= PMC_MY_PDA_FLIES_IN_FULL_COLOR
if(PMC_FENNY_FINISHED_124_QUESTS) // i broke it =3
current_version |= PMC_FENNY_FINISHED_124_QUESTS
var/list/huge_quest_list = list()
@@ -1027,7 +1031,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
number_of_finished_quests = sanitize_integer(number_of_finished_quests, 0, INFINITY, initial(number_of_finished_quests))
historical_banked_points = sanitize_integer(historical_banked_points, 0, INFINITY, initial(historical_banked_points))
last_quest_login = sanitize_integer(last_quest_login, 5, INFINITY, world.realtime)
-
+ if(features["chat_color"] == "whoopsie")
+ features["chat_color"] = random_color()
//Sanitize
diff --git a/code/modules/fallout/areas/area.dm b/code/modules/fallout/areas/area.dm
index ced0ddbd66d..8e7b0432345 100644
--- a/code/modules/fallout/areas/area.dm
+++ b/code/modules/fallout/areas/area.dm
@@ -299,6 +299,49 @@
icon_state = "alcohol"
outdoors = FALSE
+// /area/f13/fb/private
+// name = "Foxy Private"
+// icon_state = "house"
+// outdoors = FALSE
+// private = TRUE
+
+// /area/f13/fb/bar
+// name = "Foxy Bar"
+// icon_state = "alcohol"
+
+// ambience_area = list(
+// /datum/looping_sound/ambient/fb/bar,
+// /datum/looping_sound/ambient/woodcreak,
+// /datum/looping_sound/ambient/lightbulb,
+// /datum/looping_sound/ambient/fb/bar_bacon,
+// /datum/looping_sound/ambient/fb/brownnoise,
+// /datum/looping_sound/ambient/fb/fannoise
+// )
+// outdoors = FALSE
+
+// /area/f13/fb/park
+// name = "Foxy Park"
+// icon_state = "green"
+// ambientsounds = list(
+// AREA_SOUND('sound/f13ambience/dog_distant_1.ogg', 10 SECONDS),
+// AREA_SOUND('sound/f13ambience/dog_distant_2.ogg', 10 SECONDS),
+// AREA_SOUND('sound/f13ambience/dog_distant_3.ogg', 10 SECONDS),
+// AREA_SOUND('sound/f13ambience/bird_1.ogg', 10 SECONDS),
+// AREA_SOUND('sound/f13ambience/bird_2.ogg', 10 SECONDS),
+// AREA_SOUND('sound/f13ambience/bird_3.ogg', 10 SECONDS),
+// AREA_SOUND('sound/f13ambience/bird_4.ogg', 10 SECONDS),
+// AREA_SOUND('sound/f13ambience/bird_5.ogg', 10 SECONDS),
+// AREA_SOUND('sound/f13ambience/bird_6.ogg', 10 SECONDS),
+// AREA_SOUND('sound/f13ambience/bird_7.ogg', 10 SECONDS),
+// AREA_SOUND('sound/f13ambience/bird_8.ogg', 10 SECONDS))
+
+// ambience_area = list(
+// /datum/looping_sound/ambient/swamp/quiet,
+// /datum/looping_sound/ambient/critters/birds,
+// /datum/looping_sound/ambient/critters/birds/crow,
+// )
+
+
/area/f13/wasteland/city/newboston/bank
name = "New Boston Bank"
icon_state = "bank"
diff --git a/code/modules/instruments/songs/_song.dm b/code/modules/instruments/songs/_song.dm
index c0f15228e4a..794146272e9 100644
--- a/code/modules/instruments/songs/_song.dm
+++ b/code/modules/instruments/songs/_song.dm
@@ -38,9 +38,9 @@
var/max_repeats = 10
/// Our volume
- var/volume = 75
+ var/volume = 50
/// Max volume
- var/max_volume = 75
+ var/max_volume = 50
/// Min volume - This is so someone doesn't decide it's funny to set it to 0 and play invisible songs.
var/min_volume = 1
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index e0f7d70f0d1..150fe68cf5c 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -138,6 +138,20 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
grant_all_languages()
show_data_huds()
data_huds_on = 1
+ INVOKE_ASYNC(src, PROC_REF(slam_dunk_to_main_menu))
+
+/mob/dead/observer/proc/slam_dunk_to_main_menu()
+ if(!SSchat.forbid_ghosting)
+ return
+ if(IsAdminGhost(src, TRUE))
+ return TRUE
+ if(client)
+ if(check_rights_for(client, R_ADMIN))
+ return TRUE
+ abandon_mob()
+ return TRUE
+ sleep(0.5 SECONDS)
+ INVOKE_ASYNC(src, PROC_REF(slam_dunk_to_main_menu))
/mob/dead/observer/get_photo_description(obj/item/camera/camera)
if(!invisibility || camera.see_ghosts)
@@ -416,6 +430,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
destination = get_step(destination, WEST)
abstract_move(destination)//Get out of closets and such as a ghost
+ slam_dunk_to_main_menu()
/mob/dead/observer/verb/reenter_corpse()
set category = "Ghost"
diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm
index 516ee66fd08..447128f3314 100644
--- a/code/modules/mob/living/living_defines.dm
+++ b/code/modules/mob/living/living_defines.dm
@@ -1,7 +1,7 @@
/mob/living
flags_1 = HEAR_1 | CRITICAL_ATOM_1
see_invisible = SEE_INVISIBLE_LIVING
- sight = 0
+ sight = SEE_PIXELS
see_in_dark = 2
hud_possible = list(HEALTH_HUD,STATUS_HUD,ANTAG_HUD,NANITE_HUD,DIAG_NANITE_FULL_HUD,RAD_HUD,ONLINE_HUD)
pressure_resistance = 10
diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm
index efa749f8f56..fc7e7a8206a 100644
--- a/code/modules/mob/living/say.dm
+++ b/code/modules/mob/living/say.dm
@@ -117,23 +117,16 @@
to_chat(src, span_warning("You find yourself unable to speak!"))
return
- var/message_range = 7
+ var/message_range = SSchat.base_say_distance
//var/succumbed = FALSE
//var/fullcrit = InFullCritical()
if(in_critical || message_mode == MODE_WHISPER)
- message_range = 1 + (!!in_critical * 2)
+ if(!in_critical)
+ message_range = SSchat.base_whisper_distance
spans |= SPAN_ITALICS
src.log_talk(message, LOG_WHISPER)
- /* if(fullcrit) // no more dying for you!
- var/health_diff = round(-HEALTH_THRESHOLD_DEAD + health)
- // If we cut our message short, abruptly end it with a-..
- var/message_len = length_char(message)
- message = copytext_char(message, 1, health_diff) + "[message_len > health_diff ? "-.." : "..."]"
- message = Ellipsis(message, 10, 1)
- message_mode = MODE_WHISPER_CRIT
- succumbed = TRUE */
else
src.log_talk(message, LOG_SAY, forced_by=forced)
@@ -156,7 +149,10 @@
var/randomnote = pick("\u2669", "\u266A", "\u266B")
message = "[randomnote] [message] [randomnote]"
spans |= SPAN_SINGING
-
+
+ if(message_mode == MODE_YELL)
+ spans |= SPAN_YELL
+
var/radio_return = radio(message, message_mode, spans, language)
if(radio_return & ITALICS)
spans |= SPAN_ITALICS
@@ -226,6 +222,8 @@
mom3.display_turf = null
mom3.is_eavesdropping = FALSE
create_chat_message(speaker, message_language, raw_message, spans, NONE, null, mom3)
+ if(!mom3.available)
+ mom3.checkin()
create_chat_message(speaker, message_language, raw_message, spans, NONE, null, momchat)
else
data["message_mode"] = message_mode
@@ -241,7 +239,7 @@
if(just_chat)
return
// Recompose message for AI hrefs, language incomprehension.
- message = compose_message(speaker, message_language, raw_message, radio_freq, spans, message_mode, FALSE, source, data)
+ message = compose_message(speaker, message_language, momchat.original_message, radio_freq, spans, message_mode, FALSE, source, data)
var/client/C = client
if(C.prefs.color_chat_log)
var/base_chat_color = speaker.get_chat_color()
@@ -257,7 +255,7 @@
show_message(message, MSG_AUDIBLE, deaf_message, deaf_type, null, momchat)
if(islist(data) && LAZYACCESS(data, "is_radio") && (data["ckey"] in GLOB.directory) && !SSchat.debug_block_radio_blurbles)
if(CHECK_PREFS(src, RADIOPREF_HEAR_RADIO_STATIC))
- playsound(src, 'sound/effects/counter_terrorists_win.ogg', 20, FALSE, SOUND_DISTANCE(2), ignore_walls = TRUE)
+ playsound(src, RADIO_STATIC_SOUND, 20, FALSE, SOUND_DISTANCE(2), ignore_walls = TRUE)
if(CHECK_PREFS(src, RADIOPREF_HEAR_RADIO_BLURBLES))
var/mob/blurbler = ckey2mob(data["ckey"])
if(blurbler && blurbler != src)
@@ -302,7 +300,6 @@
var/list/visible_close = CC.visible_close.Copy()
// var/list/visible_far = CC.visible_far.Copy()
var/list/hidden_pathable = CC.hidden_pathable.Copy()
- // var/list/hidden_inaccessible = CC.hidden_inaccessible.Copy()
CC.putback()
var/list/the_dead = list()
@@ -389,27 +386,54 @@
mom3.runechat_mode = "hidden_pathable"
mhp.Hear(msg_rerendered, src, message_language, msg_rerendered, null, spans, message_mode, source, just_chat, list("mommy" = mom3))
sblistening |= mhp.client
- // for(var/mob/mhp in hidden_inaccessible)
- // var/turf/hearfrom = hidden_inaccessible[mhp]
- // var/list/cooler_spans = spans
- // cooler_spans += SPAN_SMALLER
- // var/list/data = list()
- // data["is_eaves"] = TRUE
- // data["is_far"] = TRUE
- // mhp.Hear(eavesrendered, src, message_language, eavesdropping, null, cooler_spans, message_mode, source, just_chat, data)
- // sblistening |= mhp.client
+ if(!mom3.available)
+ mom3.checkin()
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_LIVING_SAY_SPECIAL, src, message)
//speech bubble
var/list/speech_bubble_recipients = list()
- for(var/mob/M in listening)
+ for(var/mob/M in sblistening)
if(M.client?.prefs && !M.client.prefs.chat_on_map)
speech_bubble_recipients.Add(M.client)
+
var/image/I = image('icons/mob/talk.dmi', src, "[bubble_type][say_test(message)]", FLY_LAYER)
I.appearance_flags = APPEARANCE_UI_IGNORE_ALPHA
INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(flick_overlay), I, speech_bubble_recipients, 30)
+/mob/living/simple_animal/debug_chatterboy
+ name = "Chatterboy"
+ desc = "A debug chatterboy. He's here to help you debug your chatterboys. He's not actually a chatterboy, though. He's just a rock."
+ icon = 'modular_coyote/icons/objects/c13ammo.dmi'
+ icon_state = "rock"
+ maxHealth = 1
+ wander = FALSE
+ var/speak_cooldown = 0
+
+/mob/living/simple_animal/debug_chatterboy/BiologicalLife(seconds, times_fired)
+ . = ..()
+ // if(speak_cooldown > world.time)
+ // return
+ // speak_cooldown = world.time + 2 SECONDS
+ /// various longwinded "RPer" messages
+ var/speech = pick(
+ "Hello, I am a chatterboy. I am here to help you debug your chatterboys. I am not actually a chatterboy, though. I am just a rock.",
+ "Wow! I am a chatterboy! Your chatterboys are so cool! I wish I was a chatterboy! But I am just a rock. :(",
+ "$AAAAAAAAAAAAAAAA I AM A CHATTERBOY I AM HERE TO HELP YOU DEBUG YOUR CHATTERBOYS I AM NOT ACTUALLY A CHATTERBOY THOUGH I AM JUST A ROCK",
+ "$NOOOOOOOOO, NO NO NO NO, MY MOTHER WAS A CHATTERBOY, MY FATHER WAS A CHATTERBOY, I AM A CHATTERBOY, I AM HERE TO HELP YOU DEBUG YOUR CHATTERBOYS, I AM NOT ACTUALLY A CHATTERBOY THOUGH, I AM JUST A ROCK",
+ "I gave away our wikipedia article to the chatterboys. Citations needed.",
+ "#My character would actually screw over the party and steal the loot. It's what my character would do.",
+ "#Actually, my character is a pacifist. They would never kill anyone. They would just steal the loot and run away.",
+ "We dong have any brass windows.",
+ "%Big fat dongs, i wanna devour them all.",
+ "%Hey, heeeeyyy, wow",
+ "I dont know what to say, I'm just a rock.",
+ "SCREW OFF, CORRY YOU WANNA BE CHATTERBOY",
+ "I stuff all the cheeseburgers in my mouth and swallow them whole.",
+ )
+ playsound(src, 'sound/effects/bwoing.ogg', 100, TRUE)
+ say(speech)
+
/mob/proc/binarycheck()
return FALSE
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index b04e9874ac0..634d0d88211 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -93,7 +93,10 @@
var/has_multiple_words = LAZYLEN(words) > 1
for(var/word in words)
if(prob(probability))
- . += "..."
+ for(var/i in 1 to min(3, length(word)))
+ . += "."
+ // if(has_multiple_words && indes < LAZYLEN(words))
+ // . += " "
else
if(has_multiple_words && indes < LAZYLEN(words))
. += "[word] "
diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm
index b31406cfa70..be4acde419d 100644
--- a/code/modules/mob/mob_movement.dm
+++ b/code/modules/mob/mob_movement.dm
@@ -48,7 +48,7 @@
if(!isliving(mob))
return mob.Move(n, direction)
if(mob.stat == DEAD)
- mob.ghostize()
+ // mob.ghostize()
return FALSE
if(mob.force_moving)
return FALSE
diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm
index 56674652dce..809990f61e3 100644
--- a/code/modules/mob/say.dm
+++ b/code/modules/mob/say.dm
@@ -186,6 +186,8 @@
return MODE_SING
else if(key == ";")
return MODE_HEADSET
+ else if(key == "$")
+ return MODE_YELL
else if((length(message) > (length(key) + 1)) && (key in GLOB.department_radio_prefixes))
var/key_symbol = lowertext(message[length(key) + 1])
return GLOB.department_radio_keys[key_symbol]
diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm
index 2c633c2cf6e..60804108429 100644
--- a/code/modules/power/lighting.dm
+++ b/code/modules/power/lighting.dm
@@ -281,12 +281,12 @@
switch(fitting)
if("tube")
brightness = 9
- if(prob(2))
- break_light_tube(1)
+ // if(prob(2))
+ // break_light_tube(1)
if("bulb")
brightness = 5
- if(prob(5))
- break_light_tube(1)
+ // if(prob(5))
+ // break_light_tube(1)
spawn(1)
update(0)
if(flicker_chance)
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index 720b8698d7c..b0df541a752 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -125,7 +125,7 @@ ATTACHMENTS
var/suppressor_x_offset = 0
var/suppressor_y_offset = 0
- var/equipsound = 'sound/f13weapons/equipsounds/pistolequip.ogg'
+ equipsound = 'sound/f13weapons/equipsounds/pistolequip.ogg'
//Zooming
var/zoomable = FALSE //whether the gun generates a Zoom action on creation
@@ -373,7 +373,6 @@ ATTACHMENTS
/obj/item/gun/pickup(mob/living/user)
. = ..()
weapondraw(src, user)
- play_equip_sound(src)
/obj/item/gun/emp_act(severity)
. = ..()
@@ -915,14 +914,6 @@ ATTACHMENTS
user.show_message(span_notice("\The [src] is ready to fire."))
playsound(get_turf(user), "sound/weapons/lockedandloaded.ogg", 100, 1)
-/obj/item/gun/proc/play_equip_sound(src, volume=50)
- if(src && equipsound && volume)
- var/played_sound = equipsound
-
- if(islist(equipsound))
- played_sound = pick(equipsound)
-
- playsound(src, played_sound, volume, 1)
/*
/// Takes the current recoil, adds on some more recoil from the bullet and modded by the gun
diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm
index 714a7c2bada..04b80ee06ca 100644
--- a/code/modules/reagents/reagent_containers.dm
+++ b/code/modules/reagents/reagent_containers.dm
@@ -4,6 +4,7 @@
icon = 'icons/obj/chemical.dmi'
icon_state = null
w_class = WEIGHT_CLASS_TINY
+ tableplacesound = 'sound/machines/glassclink.ogg'
var/amount_per_transfer_from_this = 5
var/list/possible_transfer_amounts = list(5,10,15,20,25,30)
var/volume = 30
diff --git a/icons/effects/numbers.dmi b/icons/effects/numbers.dmi
new file mode 100644
index 00000000000..8ca8c661712
Binary files /dev/null and b/icons/effects/numbers.dmi differ
diff --git a/tgui/packages/tgui-panel/styles/goon/chat-dark.scss b/tgui/packages/tgui-panel/styles/goon/chat-dark.scss
index eba5362afd6..26ed30561b0 100644
--- a/tgui/packages/tgui-panel/styles/goon/chat-dark.scss
+++ b/tgui/packages/tgui-panel/styles/goon/chat-dark.scss
@@ -915,6 +915,10 @@ em {
font-size: 60%;
}
+.smaller {
+ font-size: 40%;
+}
+
.big {
font-size: 185%;
}
diff --git a/tgui/packages/tgui-panel/styles/goon/chat-light.scss b/tgui/packages/tgui-panel/styles/goon/chat-light.scss
index a16ea2aaf28..e96469d0205 100644
--- a/tgui/packages/tgui-panel/styles/goon/chat-light.scss
+++ b/tgui/packages/tgui-panel/styles/goon/chat-light.scss
@@ -952,6 +952,10 @@ h1.alert, h2.alert {
font-size: 60%;
}
+.smaller {
+ font-size: 40%;
+}
+
.big {
font-size: 185%;
}