diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 775fbab091..5084aa89f9 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -774,4 +774,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/say.dm b/code/__DEFINES/say.dm index 7d815600d3..c46f30d328 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 efa314d05d..8d1c1434ef 100644 --- a/code/__DEFINES/sound.dm +++ b/code/__DEFINES/sound.dm @@ -59,6 +59,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 @@ -400,6 +405,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 5d7e558e70..3783b5cdd2 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/__DEFINES/voreconstants.dm b/code/__DEFINES/voreconstants.dm index 43f71a8859..80941031b5 100644 --- a/code/__DEFINES/voreconstants.dm +++ b/code/__DEFINES/voreconstants.dm @@ -307,3 +307,5 @@ GLOBAL_LIST_INIT(prey_release_sounds, list( #define RADIOPREF_HEAR_RADIO_STATIC "hear_staticky_clicks" #define ADMIN_CHAT_FILTER_DMS "ADMIN_CHAT_FILTER_DMS" + +#define SEE_FANCY_OFF_SCREEN_RUNECHAT "SEE_FANCY_OFF_SCREEN_RUNECHAT" diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index dad45e1771..4942f5ae2a 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,141 @@ 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_close_pathable = list() + var/list/hidden_inaccessible = list() + var/ready = TRUE + +/datum/chatchud/proc/putback() + visible_close.Cut() + visible_far.Cut() + hidden_close_pathable.Cut() + ready = TRUE + +/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 + while(numb > 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 + +/// 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/list/see_close = hearers(source, close_range) + var/list/see_far = hearers(source, long_range) - see_close + var/debug_i = 0 + dingus: + for(var/client/C in GLOB.clients) + var/mob/M = C.mob + if(M.z != source.z) + continue dingus + if(get_dist(M, source) > long_range) + continue dingus + var/is_far = (M in see_far) + var/is_close = (M in see_close) + if(is_far) + if(private) + continue dingus + CC.visible_far[M] = TRUE + continue dingus + else if(is_close) + CC.visible_close[M] = TRUE + continue dingus + // if(get_dist(M, source) > long_range) + // continue dingus // they're too far away to hear + // now the fun begins. Try to find a path to them + // now the real fun begins + var/list/soundwalk = get_path_to(source, M, long_range, use_visibility = TRUE) + if(!islist(soundwalk)) + CC.hidden_inaccessible[M] = TRUE + continue dingus + if(!LAZYLEN(soundwalk) || LAZYLEN(soundwalk) > long_range) + CC.hidden_inaccessible[M] = TRUE + continue dingus + // now walk through the path and find the first tile that can see the source + donger: + for(var/turf/T as anything in soundwalk) + var/list/seeline = getline(T, M) + debug_i = 0 + var/cole = pick("#FF0000", "#00FF00", "#0000FF", "#FFFF00", "#FF00FF", "#00FFFF") + for(var/turf/TT as anything in seeline) // beeg american TTs + if(SSchat.debug_chud) + new /obj/effect/temp_visual/numbers/backgrounded(T, debug_i, cole) + debug_i++ + if(TT.opacity) + continue donger + for(var/atom/AM as anything in TT.contents) + if(AM.opacity) + continue donger + if(SSchat.debug_chud) + new /obj/effect/temp_visual/debug_heart(T) + CC.hidden_close_pathable[M] = T + continue dingus + // couldnt find anything! mark them as hidden + CC.hidden_inaccessible[M] = TRUE + return CC //viewers() but with a signal, for blacklisting. /proc/fov_viewers(depth = world.view, atom/center) diff --git a/code/__HELPERS/path.dm b/code/__HELPERS/path.dm index 96becc1a59..e644bf6b4e 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,14 @@ unwind_path(possible_child_node) return +/datum/pathfind/proc/checkvis(turf/destination_turf) + if(destination_turf.opacity) + return FALSE + for(var/atom/iter_atom in destination_turf) + if(iter_atom.opacity) + 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 e78db6b41c..cfafdbb7c6 100644 --- a/code/controllers/subsystem/chat.dm +++ b/code/controllers/subsystem/chat.dm @@ -10,6 +10,22 @@ SUBSYSTEM_DEF(chat) priority = FIRE_PRIORITY_CHAT init_order = INIT_ORDER_CHAT + /* + ** 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() @@ -39,6 +55,8 @@ SUBSYSTEM_DEF(chat) var/flirt_cooldown_time = 5 SECONDS var/debug_character_directory = 0 + var/debug_chud = FALSE + /datum/controller/subsystem/chat/Initialize(start_timeofday) setup_emoticon_cache() build_flirt_datums() diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm index 4fe4c0dcb0..86a3c55f9d 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 b56392bda4..23cb7e78b8 100644 --- a/code/controllers/subsystem/prefbreak.dm +++ b/code/controllers/subsystem/prefbreak.dm @@ -252,6 +252,14 @@ 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 + diff --git a/code/datums/chatmessage.dm b/code/datums/chatmessage.dm index b9f2b4b78d..d4b7225c92 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()) . = ..() if (!istype(target)) CRASH("Invalid target given for chatmessage") @@ -42,6 +50,10 @@ stack_trace("/datum/chatmessage created with [isnull(owner) ? "null" : "invalid"] mob owner") qdel(src) return + alt_display = data["display_turf"] || null + if((get_dist(owner, (alt_display || target)) > 6 || data["is_far"]) && CHECK_PREFS(owner, SEE_FANCY_OFF_SCREEN_RUNECHAT)) // 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 +64,7 @@ owned_by = null message_loc = null message = null + alt_display = null return ..() /** @@ -138,38 +151,74 @@ approx_lines = max(1, mheight / CHAT_MESSAGE_APPROX_LHEIGHT) // Translate any existing messages upwards, apply exponential decay factors to timers - message_loc = target + var/atom/remembered_location = alt_display || target + message_loc = alt_display || target + if(offscreen) // if its offscreen, put it somewhere they can see it + var/turf/ownerturf = get_turf(owner) + message_loc = ownerturf + var/angle_to_source = Get_Angle(ownerturf, message_loc) + // cus the damn angles are rotated 90 degrees clockwise, gotta change the angle 90 degrees counter + // // get us a verbal punch laser + // var/i = 1 + // var/datum/point/vector/punch_laser + // while(!punch_laser) + // punch_laser = GLOB.verbal_punch_lasers[i] + // if(!punch_laser) + // punch_laser = new /datum/point/vector() + // GLOB.verbal_punch_lasers[i] = punch_laser + // else if(punch_laser.inuse) + // i++ + // punch_laser = null + // punch_laser.initialize_location(ownerturf.x, ownerturf.y, ownerturf.z, 0, 0) + // punch_laser.initialize_trajectory(32*6, angle_to_source) // 32 pixels per tile, 6 tiles away + // punch_laser.increment(1) + var/turf/displayloc = get_turf_in_angle(angle_to_source, ownerturf, 6) + if(!displayloc) + displayloc = ownerturf // whatevs + message_loc = displayloc + if(SSchat.debug_chud) + ownerturf.Beam(displayloc, icon_state = "g_beam", time = 3 SECONDS) + if(!owned_by) return 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) // Build message image message = image(loc = message_loc, layer = CHAT_LAYER) message.plane = CHAT_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) + message.alpha /= 2 + if(offscreen) + message.alpha /= 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) @@ -191,7 +240,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()) // 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() @@ -208,9 +257,9 @@ // 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) 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) // Tweak these defines to change the available color ranges diff --git a/code/datums/position_point_vector.dm b/code/datums/position_point_vector.dm index 0f6968a80e..1ff80c7a93 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 d5ff96d5d4..1426f0595e 100644 --- a/code/game/area/areas.dm +++ b/code/game/area/areas.dm @@ -20,6 +20,7 @@ GLOBAL_VAR_INIT(areas_kill_ambience_for_players_when_players_move_from_one_area_ invisibility = INVISIBILITY_LIGHTING alpha = 50 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 31fab33c83..1746f673a2 100644 --- a/code/game/objects/effects/sound_emitter.dm +++ b/code/game/objects/effects/sound_emitter.dm @@ -52,7 +52,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." @@ -85,4 +85,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 f7bfba1a45..5cc542c465 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 728ac69259..75b17ae83d 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 e49bca4e79..e1bf084124 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 @@ -62,7 +71,8 @@ And the base of the send_speech() proc, which is the core of saycode. /atom/movable/proc/say_mod(input, message_mode) 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 else if(message_mode == MODE_SING) . = verb_sing @@ -72,6 +82,8 @@ And the base of the send_speech() proc, which is the core of saycode. . = verb_ask else if(ending == "!") . = verb_exclaim + else if(beginning == "$") + . = verb_yell else . = verb_say return get_random_if_list(.) diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index a14de68ab5..84b4e38675 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/client/preferences.dm b/code/modules/client/preferences.dm index 4b8130e636..680861628a 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -419,6 +419,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 766edd5a45..94af82a534 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -1015,7 +1015,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 6efefc9884..9c8a1b0d9f 100644 --- a/code/modules/fallout/areas/area.dm +++ b/code/modules/fallout/areas/area.dm @@ -286,6 +286,12 @@ ) 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" diff --git a/code/modules/instruments/songs/_song.dm b/code/modules/instruments/songs/_song.dm index c0f15228e4..794146272e 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/living/say.dm b/code/modules/mob/living/say.dm index 26eabbb5ca..adfa08076c 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -33,7 +33,7 @@ var/static/list/unconscious_allowed_modes = list(MODE_CHANGELING = TRUE, MODE_ALIEN = TRUE) var/talk_key = get_key(message) - var/static/list/one_character_prefix = list(MODE_HEADSET = TRUE, MODE_ROBOT = TRUE, MODE_WHISPER = TRUE, MODE_SING = TRUE) + var/static/list/one_character_prefix = list(MODE_HEADSET = TRUE, MODE_ROBOT = TRUE, MODE_WHISPER = TRUE, MODE_SING = TRUE, "$" = TRUE, "#" = TRUE) var/ic_blocked = FALSE @@ -112,23 +112,16 @@ to_chat(src, span_warning("I 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) @@ -151,7 +144,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 @@ -186,7 +182,7 @@ if(sourceturf && T && !(sourceturf in get_hear(5, T))) . = span_small("[.]") -/mob/living/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source, just_chat = FALSE, list/data) +/mob/living/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source, just_chat = FALSE, list/data = list()) SEND_SIGNAL(src, COMSIG_MOVABLE_HEAR, args) //parent calls can't overwrite the current proc args. if(!client) return @@ -202,7 +198,8 @@ // Create map text prior to modifying message for goonchat if (client?.prefs?.chat_on_map && stat != UNCONSCIOUS && (client.prefs.see_chat_non_mob || ismob(speaker)) && can_hear()) - create_chat_message(speaker, message_language, raw_message, spans) + data["message_mode"] = message_mode + create_chat_message(speaker, message_language, raw_message, spans, NONE, data) if(just_chat) return @@ -214,26 +211,57 @@ show_message(message, MSG_AUDIBLE, deaf_message, deaf_type) 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) blurbler.play_AC_typing_indicator(raw_message, src, src, TRUE) return message -/mob/living/send_speech(message, message_range = 6, obj/source = src, bubble_type = bubble_icon, list/spans, datum/language/message_language=null, message_mode, just_chat) - //var/stutter_chance = max(0, 40-special_c*10)//SPECIAL Integration - //if(prob(stutter_chance)) - // stuttering += 5 +/mob/living/send_speech( + message, + message_range = 6, + obj/source = src, + bubble_type = bubble_icon, + list/spans, + datum/language/message_language=null, + message_mode, + just_chat +) + var/max_range = 15 var/static/list/eavesdropping_modes = list(MODE_WHISPER = TRUE, MODE_WHISPER_CRIT = TRUE) - var/eavesdrop_range = 0 - if(eavesdropping_modes[message_mode]) - eavesdrop_range = EAVESDROP_EXTRA_RANGE - var/list/listening = get_hearers_in_view(message_range+eavesdrop_range, source) + var/quietness = eavesdropping_modes[message_mode] + // okay just throw out the message range + switch(message_mode) + if(MODE_WHISPER) + quietness = TRUE + message_range = SSchat.base_whisper_distance + max_range = SSchat.extended_whisper_distance + if(MODE_YELL) + quietness = FALSE + message_range = SSchat.base_yell_distance + max_range = SSchat.extended_yell_distance + if(MODE_SING) + quietness = FALSE + message_range = SSchat.base_sing_distance + max_range = SSchat.extended_sing_distance + else + quietness = FALSE + message_range = SSchat.base_say_distance + max_range = SSchat.extended_say_distance + + var/list/listening = get_hearers_in_view(message_range, src, TRUE) + var/datum/chatchud/CC = get_listening(src, message_range, max_range, quietness) + var/list/visible_close = CC.visible_close.Copy() + var/list/visible_far = CC.visible_far.Copy() + var/list/hidden_close_pathable = CC.hidden_close_pathable.Copy() + CC.putback() + + var/list/the_dead = list() -// var/list/yellareas //CIT CHANGE - adds the ability for yelling to penetrate walls and echo throughout areas -// if(!eavesdrop_range && say_test(message) == "2") //CIT CHANGE - ditto -// yellareas = get_areas_in_range(message_range*0.5, source) //CIT CHANGE - ditto + // var/list/yellareas //CIT CHANGE - adds the ability for yelling to penetrate walls and echo throughout areas + // if(!eavesdrop_range && say_test(message) == "2") //CIT CHANGE - ditto + // yellareas = get_areas_in_range(message_range*0.5, source) //CIT CHANGE - ditto for(var/_M in GLOB.player_list) var/mob/M = _M if(QDELETED(M)) //Some times nulls and deleteds stay in this list. This is a workaround to prevent ic chat breaking for everyone when they do. @@ -247,34 +275,92 @@ continue if(!(M.client?.prefs.chat_toggles & CHAT_GHOSTEARS)) //they're talking normally and we have hearing at any range off continue - listening |= M + CC.visible_close[M] = TRUE the_dead[M] = TRUE - - var/eavesdropping + var/eavesdropping var/eavesrendered - if(eavesdrop_range) - eavesdropping = stars(message) + if(!quietness) + eavesdropping = dots(message) eavesrendered = compose_message(src, message_language, eavesdropping, null, spans, message_mode, FALSE, source) var/rendered = compose_message(src, message_language, message, null, spans, message_mode, FALSE, source) + /// non-players for(var/_AM in listening) var/atom/movable/AM = _AM - if(eavesdrop_range && get_dist(source, AM) > message_range && !(the_dead[AM])) + if(!quietness && get_dist(source, AM) > message_range && !(the_dead[AM])) AM.Hear(eavesrendered, src, message_language, eavesdropping, null, spans, message_mode, source, just_chat) else AM.Hear(rendered, src, message_language, message, null, spans, message_mode, source, just_chat) + + var/list/sblistening = list() + /// players + for(var/mob/mvc in visible_close) + mvc.Hear(rendered, src, message_language, message, null, spans, message_mode, source, just_chat) + sblistening |= mvc.client + for(var/mob/mvf in visible_far) + var/list/coolspans = spans + coolspans += SPAN_SMALL + var/list/data = list() + data["is_eaves"] = TRUE + data["is_far"] = TRUE + mvf.Hear(eavesrendered, src, message_language, eavesdropping, null, coolspans, message_mode, source, just_chat, data) + sblistening |= mvf.client + for(var/mob/mhp in hidden_close_pathable) + var/turf/hearfrom = hidden_close_pathable[mhp] + var/list/cooler_spans = spans + cooler_spans += SPAN_SMALLER + var/list/data = list() + data["is_eaves"] = TRUE + data["display_turf"] = hearfrom + mhp.Hear(eavesrendered, src, message_language, eavesdropping, null, cooler_spans, message_mode, source, just_chat, data) + sblistening |= mhp.client + 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 cc7aeb03b7..e60ca3be80 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -68,6 +68,32 @@ . += "*" return sanitize(.) +/** + * Convert random words of a passed in message to ellipses + * + * * phrase - the string to convert + * * probability - probability any character gets changed + * + * This proc is not laggy at all, and is better in every way =3 + */ +/proc/dots(phrase, probability = 25) + if(probability <= 0) + return phrase + phrase = html_decode(phrase) + var/list/words = splittext(phrase, " ") + . = "" + var/indes = 1 + var/has_multiple_words = LAZYLEN(words) > 1 + for(var/word in words) + if(prob(probability)) + . += "..." + else + if(has_multiple_words && indes < LAZYLEN(words)) + . += "[word] " + else + . += "[word]" + return sanitize(.) + /** * Makes you speak like you're drunk */ diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index e2b1b164cf..4cdfd753d4 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -182,6 +182,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 2679889653..3153f3c8ca 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 3c44bd7d41..14024d45e8 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 0055f7c8ee..72544fd049 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 0000000000..8ca8c66171 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 eba5362afd..26ed30561b 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 a16ea2aaf2..e96469d020 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%; }