diff --git a/code/__DEFINES/dcs/signals/atom/signals_item.dm b/code/__DEFINES/dcs/signals/atom/signals_item.dm
index f038d4c5dbce..5ba79960657b 100644
--- a/code/__DEFINES/dcs/signals/atom/signals_item.dm
+++ b/code/__DEFINES/dcs/signals/atom/signals_item.dm
@@ -29,8 +29,10 @@
#define COMSIG_ITEM_PICKUP "item_pickup"
-///from /obj/item/device/camera/broadcasting/attack_self
+///from /obj/item/device/camera/broadcasting
#define COMSIG_BROADCAST_GO_LIVE "broadcast_live"
+#define COMSIG_BROADCAST_HEAR_TALK "broadcast_hear_talk"
+#define COMSIG_BROADCAST_SEE_EMOTE "broadcast_see_emote"
/// from /obj/item/proc/mob_can_equip
#define COMSIG_ITEM_ATTEMPTING_EQUIP "item_attempting_equip"
diff --git a/code/__DEFINES/equipment.dm b/code/__DEFINES/equipment.dm
index 4368bf304a48..b37ae0d59d64 100644
--- a/code/__DEFINES/equipment.dm
+++ b/code/__DEFINES/equipment.dm
@@ -44,6 +44,8 @@
#define USES_HEARING (1<<17)
/// Should we use the initial icon for display? Mostly used by overlay only objects
#define HTML_USE_INITAL_ICON (1<<18)
+// Whether or not the object sees emotes
+#define USES_SEEING (1<<19)
//==========================================================================================
diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm
index 63e79cdf676d..ee958d87f580 100644
--- a/code/__DEFINES/layers.dm
+++ b/code/__DEFINES/layers.dm
@@ -11,6 +11,9 @@
//#define AREA_LAYER 1
+#define DISPLACEMENT_PLATE_RENDER_LAYER 1
+#define DISPLACEMENT_PLATE_RENDER_TARGET "*DISPLACEMENT_PLATE_RENDER_TARGET"
+
#define UNDER_TURF_LAYER 1.99
#define TURF_LAYER 2
diff --git a/code/_onclick/hud/rendering/plane_master.dm b/code/_onclick/hud/rendering/plane_master.dm
index 6625120d1514..c4f070bdd842 100644
--- a/code/_onclick/hud/rendering/plane_master.dm
+++ b/code/_onclick/hud/rendering/plane_master.dm
@@ -189,3 +189,10 @@
plane = ESCAPE_MENU_PLANE
appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
render_relay_plane = RENDER_PLANE_MASTER
+
+/atom/movable/screen/plane_master/displacement
+ name = "displacement plane"
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ plane = DISPLACEMENT_PLATE_RENDER_LAYER
+ render_target = DISPLACEMENT_PLATE_RENDER_TARGET
+ render_relay_plane = null
diff --git a/code/_onclick/hud/rendering/render_plate.dm b/code/_onclick/hud/rendering/render_plate.dm
index 18236c6ee759..cb579eb4ff6a 100644
--- a/code/_onclick/hud/rendering/render_plate.dm
+++ b/code/_onclick/hud/rendering/render_plate.dm
@@ -39,6 +39,10 @@
plane = RENDER_PLANE_GAME
render_relay_plane = RENDER_PLANE_MASTER
+/atom/movable/screen/plane_master/rendering_plate/game_world/Initialize(mapload, datum/hud/hud_owner)
+ . = ..()
+ add_filter("displacer", 1, displacement_map_filter(render_source = DISPLACEMENT_PLATE_RENDER_TARGET, size = 10))
+
///render plate for OOC stuff like ghosts, hud-screen effects, etc
/atom/movable/screen/plane_master/rendering_plate/non_game
name = "non-game rendering plate"
diff --git a/code/datums/autocells/explosion.dm b/code/datums/autocells/explosion.dm
index 367567a6d40d..ecc6f9925800 100644
--- a/code/datums/autocells/explosion.dm
+++ b/code/datums/autocells/explosion.dm
@@ -282,6 +282,9 @@ as having entered the turf.
if(QDELETED(E))
return
+ if(power >= 150) //shockwave for anything over 150 power
+ new /obj/effect/shockwave(epicenter, power/60)
+
E.power = power
E.power_falloff = falloff
E.falloff_shape = falloff_shape
diff --git a/code/datums/effects/bleeding.dm b/code/datums/effects/bleeding.dm
index 2171580a94db..f56efbb3c69d 100644
--- a/code/datums/effects/bleeding.dm
+++ b/code/datums/effects/bleeding.dm
@@ -71,6 +71,11 @@
if(affected_mob.reagents.get_reagent_amount("thwei"))
blood_loss -= THWEI_BLOOD_REDUCTION
+ if(affected_mob.bodytemperature < T0C && (affected_mob.reagents.get_reagent_amount("cryoxadone") || affected_mob.reagents.get_reagent_amount("clonexadone")))
+ var/obj/structure/machinery/cryo_cell/cryo = affected_mob.loc
+ if(istype(cryo) && cryo.on && cryo.operable())
+ blood_loss -= CRYO_BLOOD_REDUCTION
+
var/mob/living/carbon/human/affected_human = affected_mob
if(istype(affected_human))
if(affected_human.chem_effect_flags & CHEM_EFFECT_NO_BLEEDING)
@@ -95,18 +100,19 @@
if(affected_mob.in_stasis == STASIS_IN_BAG)
return FALSE
- if(affected_mob.bodytemperature < T0C && (affected_mob.reagents && affected_mob.reagents.get_reagent_amount("cryoxadone") || affected_mob.reagents.get_reagent_amount("clonexadone")))
- blood_loss -= CRYO_BLOOD_REDUCTION
-
if(affected_mob.reagents) // Annoying QC check
if(affected_mob.reagents.get_reagent_amount("thwei"))
blood_loss -= THWEI_BLOOD_REDUCTION
+ if(affected_mob.bodytemperature < T0C && (affected_mob.reagents.get_reagent_amount("cryoxadone") || affected_mob.reagents.get_reagent_amount("clonexadone")))
+ blood_loss -= CRYO_BLOOD_REDUCTION
+
var/mob/living/carbon/human/affected_human = affected_mob
if(istype(affected_human))
if(affected_human.chem_effect_flags & CHEM_EFFECT_NO_BLEEDING)
return FALSE
+ blood_loss = max(blood_loss, 0) // Bleeding shouldn't give extra blood even if its only 1 tick
affected_mob.blood_volume = max(affected_mob.blood_volume - blood_loss, 0)
return TRUE
diff --git a/code/datums/emotes.dm b/code/datums/emotes.dm
index b691d87a2169..6e84052720d4 100644
--- a/code/datums/emotes.dm
+++ b/code/datums/emotes.dm
@@ -112,6 +112,7 @@
var/paygrade = user.get_paygrade()
var/formatted_message = "[paygrade][user] [msg]"
var/user_turf = get_turf(user)
+ var/list/seeing_obj = list()
if (user.client)
for(var/mob/ghost as anything in GLOB.dead_mob_list)
if(!ghost.client || isnewplayer(ghost))
@@ -132,12 +133,18 @@
if(emote_type & EMOTE_VISIBLE)
var/list/viewers = get_mobs_in_view(7, user)
for(var/mob/current_mob in viewers)
+ for(var/obj/object in current_mob.contents)
+ if((object.flags_atom & USES_SEEING))
+ seeing_obj |= object
if(!(current_mob.client?.prefs.toggles_langchat & LANGCHAT_SEE_EMOTES))
viewers -= current_mob
run_langchat(user, viewers)
else if(emote_type & EMOTE_AUDIBLE)
var/list/heard = get_mobs_in_view(7, user)
for(var/mob/current_mob in heard)
+ for(var/obj/object in current_mob.contents)
+ if((object.flags_atom & USES_HEARING))
+ seeing_obj |= object
if(current_mob.ear_deaf)
heard -= current_mob
continue
@@ -145,6 +152,9 @@
heard -= current_mob
run_langchat(user, heard)
+ for(var/obj/object as anything in seeing_obj)
+ object.see_emote(user, msg, (emote_type & EMOTE_AUDIBLE))
+
SEND_SIGNAL(user, COMSIG_MOB_EMOTED(key))
diff --git a/code/datums/supply_packs/ammo.dm b/code/datums/supply_packs/ammo.dm
index 0929f24f7c95..2e81d8fed164 100644
--- a/code/datums/supply_packs/ammo.dm
+++ b/code/datums/supply_packs/ammo.dm
@@ -135,7 +135,7 @@
group = "Ammo"
/datum/supply_packs/ammo_m4ra_mag_box_ap
- name = "Magazine box (MRRA, 16x AP mags)"
+ name = "Magazine box (M4RA, 16x AP mags)"
contains = list(
/obj/item/ammo_box/magazine/m4ra/ap,
)
diff --git a/code/datums/tutorial/xenomorph/xenomorph_basic.dm b/code/datums/tutorial/xenomorph/xenomorph_basic.dm
index 965f7b55d3c0..276d2ac824f0 100644
--- a/code/datums/tutorial/xenomorph/xenomorph_basic.dm
+++ b/code/datums/tutorial/xenomorph/xenomorph_basic.dm
@@ -160,7 +160,16 @@
add_highlight(hugger, COLOR_YELLOW)
message_to_player("This is a facehugger, highlighted in yellow. Pick up the facehugger by clicking it.")
message_to_player("Stand next to the downed human and click them to apply the facehugger. Or drop the facehugger near them to see it leap onto their face automatically.")
- RegisterSignal(human_dummy, COMSIG_HUMAN_IMPREGNATE, PROC_REF(nest_cap_phase))
+ RegisterSignal(hugger, COMSIG_PARENT_QDELETING, PROC_REF(on_hugger_deletion))
+ RegisterSignal(human_dummy, COMSIG_HUMAN_IMPREGNATE, PROC_REF(nest_cap_phase), override = TRUE)
+
+/datum/tutorial/xenomorph/basic/proc/on_hugger_deletion(hugger)
+ SIGNAL_HANDLER
+ TUTORIAL_ATOM_FROM_TRACKING(/obj/effect/alien/resin/special/eggmorph, morpher)
+ morpher.stored_huggers = 1
+ add_highlight(morpher, COLOR_YELLOW)
+ message_to_player("Click the egg morpher to take a facehugger.")
+ RegisterSignal(xeno, COMSIG_XENO_TAKE_HUGGER_FROM_MORPHER, PROC_REF(take_facehugger_phase))
/datum/tutorial/xenomorph/basic/proc/nest_cap_phase()
SIGNAL_HANDLER
@@ -168,6 +177,7 @@
TUTORIAL_ATOM_FROM_TRACKING(/obj/item/clothing/mask/facehugger, hugger)
UnregisterSignal(human_dummy, COMSIG_MOB_TAKE_DAMAGE)
UnregisterSignal(human_dummy, COMSIG_HUMAN_IMPREGNATE)
+ UnregisterSignal(hugger, COMSIG_PARENT_QDELETING)
remove_highlight(hugger)
message_to_player("We should nest the infected human to make sure they don't get away.")
diff --git a/code/game/machinery/ARES/ARES_interface.dm b/code/game/machinery/ARES/ARES_interface.dm
index 6cc7c220fd04..341e6a05acf4 100644
--- a/code/game/machinery/ARES/ARES_interface.dm
+++ b/code/game/machinery/ARES/ARES_interface.dm
@@ -368,6 +368,10 @@
new_title = "[record.title] at [record.time]"
new_details = record.details
datacore.records_tech -= record
+ if(ARES_RECORD_FLIGHT)
+ new_title = "[record.title] at [record.time]"
+ new_details = record.details
+ datacore.records_flight -= record
new_delete.details = new_details
new_delete.user = last_login
diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm
index 948d83e76148..6943544e30d4 100644
--- a/code/game/machinery/camera/camera.dm
+++ b/code/game/machinery/camera/camera.dm
@@ -312,8 +312,8 @@ GLOBAL_LIST_EMPTY_TYPED(all_cameras, /obj/structure/machinery/camera)
. = ..()
if(!camera_item)
return INITIALIZE_HINT_QDEL
- c_tag = camera_item.get_broadcast_name()
linked_broadcasting = camera_item
+ c_tag = linked_broadcasting.get_broadcast_name()
/obj/structure/machinery/camera/mortar
alpha = 0
diff --git a/code/game/machinery/computer/camera_console.dm b/code/game/machinery/computer/camera_console.dm
index cd0ee780f478..1e2cb427cab4 100644
--- a/code/game/machinery/computer/camera_console.dm
+++ b/code/game/machinery/computer/camera_console.dm
@@ -17,6 +17,7 @@
var/colony_camera_mapload = TRUE
var/admin_console = FALSE
+ var/stay_connected = FALSE
/obj/structure/machinery/computer/cameras/Initialize(mapload)
. = ..()
@@ -33,7 +34,7 @@
/obj/structure/machinery/computer/cameras/Destroy()
SStgui.close_uis(src)
- QDEL_NULL(current)
+ current = null
UnregisterSignal(src, COMSIG_CAMERA_MAPNAME_ASSIGNED)
last_camera_turf = null
concurrent_users = null
@@ -147,7 +148,7 @@
// Unregister map objects
SEND_SIGNAL(src, COMSIG_CAMERA_UNREGISTER_UI, user)
// Turn off the console
- if(length(concurrent_users) == 0 && is_living)
+ if(length(concurrent_users) == 0 && is_living && !stay_connected)
current = null
SEND_SIGNAL(src, COMSIG_CAMERA_CLEAR)
last_camera_turf = null
@@ -206,6 +207,8 @@
name = "Television Set"
desc = "An old TV hooked up to a video cassette recorder, you can even use it to time shift WOW."
network = list(CAMERA_NET_CORRESPONDENT)
+ stay_connected = TRUE
+ circuit = /obj/item/circuitboard/computer/cameras/tv
var/obj/item/device/camera/broadcasting/broadcastingcamera = null
/obj/structure/machinery/computer/cameras/wooden_tv/broadcast/Destroy()
@@ -213,38 +216,76 @@
return ..()
/obj/structure/machinery/computer/cameras/wooden_tv/broadcast/ui_state(mob/user)
- return GLOB.default_state
+ return GLOB.in_view
/obj/structure/machinery/computer/cameras/wooden_tv/broadcast/ui_act(action, params)
. = ..()
if(action != "switch_camera")
return
- broadcastingcamera = null
- if (!istype(current, /obj/structure/machinery/camera/correspondent))
+ if(broadcastingcamera)
+ clear_camera()
+ if(!istype(current, /obj/structure/machinery/camera/correspondent))
return
var/obj/structure/machinery/camera/correspondent/corr_cam = current
- if (!corr_cam.linked_broadcasting)
+ if(!corr_cam.linked_broadcasting)
return
broadcastingcamera = corr_cam.linked_broadcasting
RegisterSignal(broadcastingcamera, COMSIG_BROADCAST_GO_LIVE, PROC_REF(go_back_live))
+ RegisterSignal(broadcastingcamera, COMSIG_COMPONENT_ADDED, PROC_REF(handle_rename))
RegisterSignal(broadcastingcamera, COMSIG_PARENT_QDELETING, PROC_REF(clear_camera))
+ RegisterSignal(broadcastingcamera, COMSIG_BROADCAST_HEAR_TALK, PROC_REF(transfer_talk))
+ RegisterSignal(broadcastingcamera, COMSIG_BROADCAST_SEE_EMOTE, PROC_REF(transfer_emote))
/obj/structure/machinery/computer/cameras/wooden_tv/broadcast/ui_close(mob/user)
. = ..()
- if (!current && broadcastingcamera)
+ if(!broadcastingcamera)
+ return
+ if(!current)
clear_camera()
/obj/structure/machinery/computer/cameras/wooden_tv/broadcast/proc/clear_camera()
SIGNAL_HANDLER
- UnregisterSignal(broadcastingcamera, list(COMSIG_BROADCAST_GO_LIVE, COMSIG_PARENT_QDELETING))
+ UnregisterSignal(broadcastingcamera, list(COMSIG_BROADCAST_GO_LIVE, COMSIG_PARENT_QDELETING, COMSIG_COMPONENT_ADDED, COMSIG_BROADCAST_HEAR_TALK, COMSIG_BROADCAST_SEE_EMOTE))
broadcastingcamera = null
/obj/structure/machinery/computer/cameras/wooden_tv/broadcast/proc/go_back_live(obj/item/device/camera/broadcasting/broadcastingcamera)
SIGNAL_HANDLER
- if (current.c_tag == broadcastingcamera.get_broadcast_name())
+ if(current.c_tag == broadcastingcamera.get_broadcast_name())
current = broadcastingcamera.linked_cam
SEND_SIGNAL(src, COMSIG_CAMERA_SET_TARGET, broadcastingcamera.linked_cam, broadcastingcamera.linked_cam.view_range, broadcastingcamera.linked_cam.view_range)
+/obj/structure/machinery/computer/cameras/wooden_tv/broadcast/proc/transfer_talk(obj/item/camera, mob/living/sourcemob, message, verb = "says", datum/language/language, italics = FALSE, show_message_above_tv = FALSE)
+ SIGNAL_HANDLER
+ if(inoperable())
+ return
+ if(show_message_above_tv)
+ langchat_speech(message, get_mobs_in_view(7, src), language, sourcemob.langchat_color, FALSE, LANGCHAT_FAST_POP, list(sourcemob.langchat_styles))
+ for(var/datum/weakref/user_ref in concurrent_users)
+ var/mob/user = user_ref.resolve()
+ if(user?.client?.prefs && !user.client.prefs.lang_chat_disabled && !user.ear_deaf && user.say_understands(sourcemob, language))
+ sourcemob.langchat_display_image(user)
+
+/obj/structure/machinery/computer/cameras/wooden_tv/broadcast/proc/transfer_emote(obj/item/camera, mob/living/sourcemob, emote, audible = FALSE, show_message_above_tv = FALSE)
+ SIGNAL_HANDLER
+ if(inoperable())
+ return
+ if(show_message_above_tv)
+ langchat_speech(emote, get_mobs_in_view(7, src), null, null, TRUE, LANGCHAT_FAST_POP, list("emote"))
+ for(var/datum/weakref/user_ref in concurrent_users)
+ var/mob/user = user_ref.resolve()
+ if(user?.client?.prefs && (user.client.prefs.toggles_langchat & LANGCHAT_SEE_EMOTES) && (!audible || !user.ear_deaf))
+ sourcemob.langchat_display_image(user)
+
+/obj/structure/machinery/computer/cameras/wooden_tv/broadcast/examine(mob/user)
+ . = ..()
+ attack_hand(user) //watch tv on examine
+
+/obj/structure/machinery/computer/cameras/wooden_tv/broadcast/proc/handle_rename(obj/item/camera, datum/component/label)
+ SIGNAL_HANDLER
+ if(!istype(label, /datum/component/label))
+ return
+ current.c_tag = broadcastingcamera.get_broadcast_name()
+
/obj/structure/machinery/computer/cameras/wooden_tv/ot
name = "Mortar Monitoring Set"
desc = "A Console linked to Mortar launched cameras."
diff --git a/code/game/objects/effects/temporary_visuals.dm b/code/game/objects/effects/temporary_visuals.dm
index 4dc07b76f3cb..d05e7789b1d5 100644
--- a/code/game/objects/effects/temporary_visuals.dm
+++ b/code/game/objects/effects/temporary_visuals.dm
@@ -96,3 +96,26 @@
splatter_type = "csplatter"
color = BLOOD_COLOR_SYNTHETIC
+//------------------------------------------
+//Shockwaves
+//------------------------------------------
+
+/obj/effect/shockwave
+ icon = 'icons/effects/light_overlays/shockwave.dmi'
+ icon_state = "shockwave"
+ plane = DISPLACEMENT_PLATE_RENDER_LAYER
+ pixel_x = -496
+ pixel_y = -496
+
+/obj/effect/shockwave/Initialize(mapload, radius, speed, easing_type = LINEAR_EASING, y_offset, x_offset)
+ . = ..()
+ if(!speed)
+ speed = 1
+ if(y_offset)
+ pixel_y += y_offset
+ if(x_offset)
+ pixel_x += x_offset
+ QDEL_IN(src, 0.5 * radius * speed)
+ transform = matrix().Scale(32 / 1024, 32 / 1024)
+ animate(src, time = 0.5 * radius * speed, transform=matrix().Scale((32 / 1024) * radius * 1.5, (32 / 1024) * radius * 1.5), easing = easing_type)
+
diff --git a/code/game/objects/items/storage/firstaid.dm b/code/game/objects/items/storage/firstaid.dm
index 3e7c00f3d0ff..201e34654624 100644
--- a/code/game/objects/items/storage/firstaid.dm
+++ b/code/game/objects/items/storage/firstaid.dm
@@ -510,6 +510,34 @@
/obj/item/storage/pill_bottle/proc/error_idlock(mob/user)
to_chat(user, SPAN_WARNING("It must have some kind of ID lock..."))
+/obj/item/storage/pill_bottle/proc/choose_color(mob/user)
+ if(!user)
+ user = usr
+ var/static/list/possible_colors = list(
+ "Orange" = "",
+ "Blue" = "1",
+ "Yellow" = "2",
+ "Light Purple" = "3",
+ "Light Grey" = "4",
+ "White" = "5",
+ "Light Green" = "6",
+ "Cyan" = "7",
+ "Bordeaux" = "8",
+ "Aquamarine" = "9",
+ "Grey" = "10",
+ "Red" = "11",
+ "Black" = "12",
+ )
+ var/selected_color = tgui_input_list(user, "Select a color.", "Color choice", possible_colors)
+ if(!selected_color)
+ return
+
+ selected_color = possible_colors[selected_color]
+
+ icon_state = "pill_canister" + selected_color
+ to_chat(user, SPAN_NOTICE("You color [src]."))
+ update_icon()
+
/obj/item/storage/pill_bottle/verb/set_maptext()
set category = "Object"
set name = "Set short label (on-sprite)"
diff --git a/code/game/objects/items/tools/misc_tools.dm b/code/game/objects/items/tools/misc_tools.dm
index 06f42aacd56c..b016f0e67b33 100644
--- a/code/game/objects/items/tools/misc_tools.dm
+++ b/code/game/objects/items/tools/misc_tools.dm
@@ -58,6 +58,10 @@
if(isturf(A))
to_chat(user, SPAN_WARNING("The label won't stick to that."))
return
+ if(istype(A, /obj/item/storage/pill_bottle))
+ var/obj/item/storage/pill_bottle/target_pill_bottle = A
+ target_pill_bottle.choose_color(user)
+
if(!label || !length(label))
remove_label(A, user)
return
diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm
index a3febb11dddb..66b41fff9127 100644
--- a/code/game/objects/objs.dm
+++ b/code/game/objects/objs.dm
@@ -208,6 +208,9 @@
/obj/proc/hear_talk(mob/living/M as mob, msg, verb="says", datum/language/speaking, italics = 0)
return
+/obj/proc/see_emote(mob/living/M as mob, emote, audible = FALSE)
+ return
+
/obj/attack_hand(mob/user)
if(can_buckle) manual_unbuckle(user)
else . = ..()
diff --git a/code/game/objects/structures/reagent_dispensers.dm b/code/game/objects/structures/reagent_dispensers.dm
index a89f35ce38f3..d0f9f513e7f8 100644
--- a/code/game/objects/structures/reagent_dispensers.dm
+++ b/code/game/objects/structures/reagent_dispensers.dm
@@ -129,6 +129,25 @@
if(N)
amount_per_transfer_from_this = N
+/obj/structure/reagent_dispensers/clicked(mob/user, list/mods)
+ if(!Adjacent(user))
+ return ..()
+
+ if(!ishuman(user))
+ return ..()
+
+ if(!reagents || reagents.locked)
+ return ..()
+
+ if(mods["alt"])
+ dispensing = !dispensing
+ if(dispensing)
+ to_chat(user, SPAN_NOTICE("[src] is now dispensing"))
+ else
+ to_chat(user, SPAN_NOTICE("[src] is now filling"))
+ return TRUE
+ return ..()
+
/obj/structure/reagent_dispensers/attackby(obj/item/hit_item, mob/living/user)
if(istype(hit_item, /obj/item/reagent_container))
return
diff --git a/code/modules/cm_marines/dropship_equipment.dm b/code/modules/cm_marines/dropship_equipment.dm
index bd40076ea500..af06f468adcd 100644
--- a/code/modules/cm_marines/dropship_equipment.dm
+++ b/code/modules/cm_marines/dropship_equipment.dm
@@ -524,12 +524,6 @@
if(light_on)
set_light(0)
-/obj/structure/dropship_equipment/electronics/spotlights/on_launch()
- set_light(0)
-
-/obj/structure/dropship_equipment/electronics/spotlights/on_arrival()
- set_light(brightness)
-
/obj/structure/dropship_equipment/electronics/spotlights/ui_data(mob/user)
. = list()
var/is_deployed = light_on
diff --git a/code/modules/gear_presets/clf.dm b/code/modules/gear_presets/clf.dm
index 9c05ff8fa5fc..143a2271f00e 100644
--- a/code/modules/gear_presets/clf.dm
+++ b/code/modules/gear_presets/clf.dm
@@ -900,6 +900,54 @@
list("Whistle", 5, /obj/item/device/whistle, null, VENDOR_ITEM_REGULAR),
)
+/datum/equipment_preset/clf/synth/combat
+ name = "CLF Combat Synthetic"
+ flags = EQUIPMENT_PRESET_EXTRA
+
+/datum/equipment_preset/clf/synth/combat/load_skills(mob/living/carbon/human/new_human)
+ . = ..()
+ new_human.allow_gun_usage = TRUE
+
+/datum/equipment_preset/clf/synth/combat/load_gear(mob/living/carbon/human/new_human)
+ //back
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/marine/engineerpack/ert, WEAR_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/extinguisher/mini, WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/device/defibrillator, WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/roller, WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/explosive/plastic, WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/explosive/plastic, WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/crowbar, WEAR_IN_BACK)
+ //face
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/distress/CLF/command(new_human), WEAR_L_EAR)
+ new_human.equip_to_slot_or_del(new /obj/item/attachable/bayonet/upp, WEAR_FACE)
+ if(new_human.disabilities & NEARSIGHTED)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/glasses/hud/health/prescription(new_human), WEAR_EYES)
+ else
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/glasses/hud/health(new_human), WEAR_EYES)
+ //head
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/head/beret/jan, WEAR_HEAD)
+ //body
+ var/obj/item/clothing/under/colonist/clf/CLF = new()
+ var/obj/item/clothing/accessory/storage/webbing/webbing = new()
+ CLF.attach_accessory(new_human, webbing)
+ new_human.equip_to_slot_or_del(CLF, WEAR_BODY)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/mar40/extended, WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/mar40/extended, WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/mar40/extended, WEAR_IN_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/militia, WEAR_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/mar40/extended, WEAR_IN_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/rifle/mar40/extended, WEAR_IN_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/weapon/gun/rifle/mar40/carbine, WEAR_J_STORE)
+ //waist
+ new_human.equip_to_slot_or_del(new /obj/item/storage/belt/medical/full/with_suture_and_graft, WEAR_WAIST)
+ new_human.equip_to_slot_or_del(new /obj/item/device/healthanalyzer(new_human), WEAR_IN_BELT)
+ //limbs
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/gloves/combat, WEAR_HANDS)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/combat, WEAR_FEET)
+ //pockets
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/tools/synth, WEAR_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/construction/full_barbed_wire, WEAR_R_STORE)
+
//*****************************************************************************************************/
/datum/equipment_preset/clf/commander
diff --git a/code/modules/gear_presets/upp.dm b/code/modules/gear_presets/upp.dm
index 39aed17a1fad..cdb955bd9696 100644
--- a/code/modules/gear_presets/upp.dm
+++ b/code/modules/gear_presets/upp.dm
@@ -2806,6 +2806,62 @@
list("Whistle", 5, /obj/item/device/whistle, null, VENDOR_ITEM_REGULAR),
)
+
+/datum/equipment_preset/upp/synth/combat
+ name = "UPP Combat Synthetic"
+ flags = EQUIPMENT_PRESET_EXTRA
+
+ assignment = JOB_UPP_COMBAT_SYNTH
+ rank = JOB_UPP_COMBAT_SYNTH
+
+/datum/equipment_preset/upp/synth/combat/load_skills(mob/living/carbon/human/new_human)
+ . = ..()
+ new_human.allow_gun_usage = TRUE
+
+/datum/equipment_preset/upp/synth/combat/load_gear(mob/living/carbon/human/new_human)
+ //back
+ new_human.equip_to_slot_or_del(new /obj/item/storage/backpack/lightpack/upp, WEAR_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/surgery/surgical_line, WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/surgery/synthgraft, WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/device/defibrillator/compact, WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/reagent_container/hypospray/epinephrine, WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/reagent_container/glass/bottle/epinephrine, WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/reagent_container/blood/OMinus, WEAR_IN_BACK)
+ new_human.equip_to_slot_or_del(new /obj/item/roller/surgical, WEAR_IN_BACK)
+ //face
+ new_human.equip_to_slot_or_del(new /obj/item/device/radio/headset/distress/UPP/command, WEAR_L_EAR)
+ new_human.equip_to_slot_or_del(new /obj/item/tool/screwdriver, WEAR_R_EAR)
+ if(new_human.disabilities & NEARSIGHTED)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/glasses/hud/health/prescription(new_human), WEAR_EYES)
+ else
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/glasses/hud/health(new_human), WEAR_EYES)
+ //head
+ var/hat = pick(/obj/item/clothing/head/uppcap, /obj/item/clothing/head/uppcap/beret, /obj/item/clothing/head/uppcap/ushanka)
+ new_human.equip_to_slot_or_del(new hat, WEAR_HEAD)
+ //body
+ var/obj/item/clothing/under/marine/veteran/UPP/UPP = new()
+ var/obj/item/clothing/accessory/storage/surg_vest/drop_green/upp/webbing = new()
+ UPP.attach_accessory(new_human, webbing)
+ new_human.equip_to_slot_or_del(UPP, WEAR_BODY)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/marine/faction/UPP/support, WEAR_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/weapon/telebaton, WEAR_IN_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/smg/bizon, WEAR_IN_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/smg/bizon, WEAR_IN_JACKET)
+ new_human.equip_to_slot_or_del(new /obj/item/weapon/gun/smg/bizon/upp, WEAR_J_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/accessory/patch/upp, WEAR_ACCESSORY)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/accessory/patch/upp/naval, WEAR_ACCESSORY)
+ //waist
+ new_human.equip_to_slot_or_del(new /obj/item/storage/belt/medical/lifesaver/upp/synth, WEAR_WAIST)
+ //limbs
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/shoes/marine/upp/knife, WEAR_FEET)
+ new_human.equip_to_slot_or_del(new /obj/item/clothing/gloves/marine/veteran/upp, WEAR_HANDS)
+ //pockets
+ var/obj/item/storage/pouch/magazine/large/ppouch = new()
+ new_human.equip_to_slot_or_del(ppouch, WEAR_L_STORE)
+ for(var/i = 1 to ppouch.storage_slots)
+ new_human.equip_to_slot_or_del(new /obj/item/ammo_magazine/smg/bizon, WEAR_IN_L_STORE)
+ new_human.equip_to_slot_or_del(new /obj/item/storage/pouch/tools/tactical/upp, WEAR_R_STORE)
+
//*****************************************************************************************************/
/datum/equipment_preset/upp/conscript
diff --git a/code/modules/mob/living/carbon/xenomorph/Abilities.dm b/code/modules/mob/living/carbon/xenomorph/Abilities.dm
index 3bf0c67ef721..ec024c3b5605 100644
--- a/code/modules/mob/living/carbon/xenomorph/Abilities.dm
+++ b/code/modules/mob/living/carbon/xenomorph/Abilities.dm
@@ -142,7 +142,7 @@
playsound(xeno.loc, pick(xeno.screech_sound_effect_list), 75, 0, status = 0)
xeno.visible_message(SPAN_XENOHIGHDANGER("[xeno] emits an ear-splitting guttural roar!"))
- xeno.create_shriekwave() //Adds the visual effect. Wom wom wom
+ xeno.create_shriekwave(14) //Adds the visual effect. Wom wom wom, 14 shriekwaves
for(var/mob/mob in view())
if(mob && mob.client)
diff --git a/code/modules/mob/living/carbon/xenomorph/Facehuggers.dm b/code/modules/mob/living/carbon/xenomorph/Facehuggers.dm
index 8aa3000092f5..bf88acd2b790 100644
--- a/code/modules/mob/living/carbon/xenomorph/Facehuggers.dm
+++ b/code/modules/mob/living/carbon/xenomorph/Facehuggers.dm
@@ -413,9 +413,6 @@
M.stored_huggers++
qdel(src)
return
- // Tutorial facehuggers never time out
- if(hivenumber == XENO_HIVE_TUTORIAL)
- return
die()
/obj/item/clothing/mask/facehugger/proc/die()
@@ -441,12 +438,15 @@
playsound(src.loc, 'sound/voice/alien_facehugger_dies.ogg', 25, 1)
if(ismob(loc)) //Make it fall off the person so we can update their icons. Won't update if they're in containers thou
- var/mob/M = loc
- M.drop_inv_item_on_ground(src)
+ var/mob/holder_mob = loc
+ holder_mob.drop_inv_item_on_ground(src)
layer = TURF_LAYER //so dead hugger appears below live hugger if stacked on same tile. (and below nested hosts)
- addtimer(CALLBACK(src, PROC_REF(decay)), 3 MINUTES)
+ if(hivenumber == XENO_HIVE_TUTORIAL)
+ addtimer(CALLBACK(src, PROC_REF(decay)), 5 SECONDS)
+ else
+ addtimer(CALLBACK(src, PROC_REF(decay)), 3 MINUTES)
/obj/item/clothing/mask/facehugger/proc/decay()
visible_message("[icon2html(src, viewers(src))] \The [src] decays into a mass of acid and chitin.")
diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm b/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm
index f62bb9e17421..f36e23394eef 100644
--- a/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm
+++ b/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm
@@ -296,6 +296,9 @@
/datum/action/xeno_action/onclick/toggle_long_range/use_ability(atom/target)
var/mob/living/carbon/xenomorph/xeno = owner
+ if (!xeno.check_state())
+ return
+
if(xeno.observed_xeno)
return
diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_powers.dm
index 6e6fef86a2f4..3ec4855f9c3a 100644
--- a/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_powers.dm
+++ b/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_powers.dm
@@ -12,8 +12,7 @@
playsound(xeno.loc, pick(predalien_roar), 75, 0, status = 0)
xeno.visible_message(SPAN_XENOHIGHDANGER("[xeno] emits a guttural roar!"))
- xeno.create_shriekwave(color = "#FF0000")
-
+ xeno.create_shriekwave(7) //Adds the visual effect. Wom wom wom, 7 shriekwaves
for(var/mob/living/carbon/carbon in view(7, xeno))
if(ishuman(carbon))
var/mob/living/carbon/human/human = carbon
diff --git a/code/modules/mob/living/carbon/xenomorph/update_icons.dm b/code/modules/mob/living/carbon/xenomorph/update_icons.dm
index cf84312657a3..ac0c381f5ed4 100644
--- a/code/modules/mob/living/carbon/xenomorph/update_icons.dm
+++ b/code/modules/mob/living/carbon/xenomorph/update_icons.dm
@@ -207,31 +207,46 @@
overlays_standing[X_LEGCUFF_LAYER] = image("icon" = 'icons/mob/xenos/effects.dmi', "icon_state" = "legcuff", "layer" =-X_LEGCUFF_LAYER)
apply_overlay(X_LEGCUFF_LAYER)
-/mob/living/carbon/xenomorph/proc/create_shriekwave(color = null)
- var/image/screech_image
-
- var/offset_x = 0
- var/offset_y = 0
- if(mob_size <= MOB_SIZE_XENO)
- offset_x = -7
- offset_y = -10
-
- if (color)
- screech_image = image("icon"='icons/mob/xenos/overlay_effects64x64.dmi', "icon_state" = "shriek_waves_greyscale") // For Praetorian screech
- screech_image.color = color
- else
- screech_image = image("icon"='icons/mob/xenos/overlay_effects64x64.dmi', "icon_state" = "shriek_waves") //Ehh, suit layer's not being used.
-
- screech_image.pixel_x = offset_x
- screech_image.pixel_y = offset_y
-
- screech_image.appearance_flags |= RESET_COLOR
-
- remove_suit_layer()
-
- overlays_standing[X_SUIT_LAYER] = screech_image
- apply_overlay(X_SUIT_LAYER)
- addtimer(CALLBACK(src, PROC_REF(remove_overlay), X_SUIT_LAYER), 30)
+/mob/living/carbon/xenomorph/proc/create_shriekwave(shriekwaves_left)
+ var/offset_y = 8
+ if(mob_size == MOB_SIZE_XENO)
+ offset_y = 24
+ if(mob_size == MOB_SIZE_IMMOBILE)
+ offset_y = 28
+
+ //the shockwave center is updated eachtime shockwave is called and offset relative to the mob_size.
+ //due to the speed of the shockwaves, it isn't required to be tied to the exact mob movements
+ var/epicenter = loc //center of the shockwave, set at the center of the tile that the mob is currently standing on
+ var/easing = QUAD_EASING | EASE_OUT
+ var/stage1_radius = rand(11, 12)
+ var/stage2_radius = rand(9, 11)
+ var/stage3_radius = rand(8, 10)
+ var/stage4_radius = 7.5
+
+ //shockwaves are iterated, counting down once per shriekwave, with the total amount being determined on the respective xeno ability tile
+ if(shriekwaves_left > 12)
+ shriekwaves_left--
+ new /obj/effect/shockwave(epicenter, stage1_radius, 0.5, easing, offset_y)
+ addtimer(CALLBACK(src, PROC_REF(create_shriekwave), shriekwaves_left), 2)
+ return
+ if(shriekwaves_left > 8)
+ shriekwaves_left--
+ new /obj/effect/shockwave(epicenter, stage2_radius, 0.5, easing, offset_y)
+ addtimer(CALLBACK(src, PROC_REF(create_shriekwave), shriekwaves_left), 3)
+ return
+ if(shriekwaves_left > 4)
+ shriekwaves_left--
+ new /obj/effect/shockwave(epicenter, stage3_radius, 0.5, easing, offset_y)
+ addtimer(CALLBACK(src, PROC_REF(create_shriekwave), shriekwaves_left), 3)
+ return
+ if(shriekwaves_left > 1)
+ shriekwaves_left--
+ new /obj/effect/shockwave(epicenter, stage4_radius, 0.5, easing, offset_y)
+ addtimer(CALLBACK(src, PROC_REF(create_shriekwave), shriekwaves_left), 3)
+ return
+ if(shriekwaves_left == 1)
+ shriekwaves_left--
+ new /obj/effect/shockwave(epicenter, 10.5, 0.6, easing, offset_y)
/mob/living/carbon/xenomorph/proc/create_stomp()
remove_suit_layer()
diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm
index 08711f295085..0d5eca1dd9a1 100644
--- a/code/modules/paperwork/photocopier.dm
+++ b/code/modules/paperwork/photocopier.dm
@@ -1,7 +1,9 @@
+/// Normal Photocopier, made by Seegson
/obj/structure/machinery/photocopier
name = "photocopier"
icon = 'icons/obj/structures/machinery/library.dmi'
icon_state = "bigscanner"
+ desc = "A photocopier used for copying... you know, photos! Also useful for copying documents on paper. This specific model has been manufactured by Seegson in a cheaper frame than most modern photocopiers. It uses more primitive copying technology resulting in more toner waste and less printing capabilities. Nonetheless, its cheap construction means cheaper costs, and for people that only need to print a paper or two most of the time, it becomes cost-effective."
anchored = TRUE
density = TRUE
use_power = USE_POWER_IDLE
@@ -11,9 +13,15 @@
var/obj/item/paper/copy = null //what's in the copier!
var/obj/item/photo/photocopy = null
var/obj/item/paper_bundle/bundle = null
- var/copies = 1 //how many copies to print!
- var/toner = 30 //how much toner is left! woooooo~
- var/maxcopies = 10 //how many copies can be copied at once- idea shamelessly stolen from bs12's copier!
+ ///how many copies to print!
+ var/copies = 1
+ ///how much toner is left! woooooo~
+ var/toner = 45
+ ///how many copies can be copied at once- idea shamelessly stolen from bs12's copier!
+ var/maxcopies = 10
+ ///the flick state to use when inserting paper into the machine
+ var/animate_state = "bigscanner1"
+
/obj/structure/machinery/photocopier/attack_remote(mob/user as mob)
return attack_hand(user)
@@ -116,7 +124,7 @@
if(user.drop_inv_item_to_loc(O, src))
copy = O
to_chat(user, SPAN_NOTICE("You insert the paper into \the [src]."))
- flick("bigscanner1", src)
+ flick(animate_state, src)
updateUsrDialog()
else
to_chat(user, SPAN_NOTICE("There is already something in \the [src]."))
@@ -125,7 +133,7 @@
if(user.drop_inv_item_to_loc(O, src))
photocopy = O
to_chat(user, SPAN_NOTICE("You insert the photo into \the [src]."))
- flick("bigscanner1", src)
+ flick(animate_state, src)
updateUsrDialog()
else
to_chat(user, SPAN_NOTICE("There is already something in \the [src]."))
@@ -134,13 +142,13 @@
if(user.drop_inv_item_to_loc(O, src))
bundle = O
to_chat(user, SPAN_NOTICE("You insert the bundle into \the [src]."))
- flick("bigscanner1", src)
+ flick(animate_state, src)
updateUsrDialog()
else if(istype(O, /obj/item/device/toner))
if(toner == 0)
if(user.temp_drop_inv_item(O))
qdel(O)
- toner = 30
+ toner = initial(toner)
to_chat(user, SPAN_NOTICE("You insert the toner cartridge into \the [src]."))
updateUsrDialog()
else
@@ -239,6 +247,21 @@
return p
+/// Upgraded photocopier, straight upgrade from the normal photocopier, made by Weyland-Yutani
+/obj/structure/machinery/photocopier/wyphotocopier
+ name = "photocopier"
+ icon = 'icons/obj/structures/machinery/library.dmi'
+ icon_state = "bigscannerpro"
+ desc = "A photocopier used for copying... you know, photos! Also useful for copying documents on paper. This specific model has been manufactured by Weyland-Yutani in a more modern and robust frame than the average photocopiers you see from smaller companies. It uses some of the most advanced technologies in the area of paper-printing such as bigger toner economy and much higher printing capabilities. All that makes it the favorite among consumers that need to print high amounts of paperwork for their daily duties."
+ idle_power_usage = 50
+ active_power_usage = 300
+ copies = 1
+ toner = 180
+ maxcopies = 30
+ animate_state = "bigscannerpro1"
+
+
+/// The actual toner cartridge used in photcopiers
/obj/item/device/toner
name = "toner cartridge"
icon_state = "tonercartridge"
diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm
index 5614a4ffe52b..df39248e343a 100644
--- a/code/modules/paperwork/photography.dm
+++ b/code/modules/paperwork/photography.dm
@@ -359,6 +359,12 @@
flags_equip_slot = NO_FLAGS //cannot be equiped
var/obj/structure/machinery/camera/correspondent/linked_cam
+/obj/item/device/camera/broadcasting/Initialize(mapload, ...)
+ . = ..()
+ linked_cam = new(loc, src)
+ linked_cam.status = FALSE
+ RegisterSignal(src, COMSIG_COMPONENT_ADDED, PROC_REF(handle_rename))
+
/obj/item/device/camera/broadcasting/Destroy()
clear_broadcast()
return ..()
@@ -367,13 +373,25 @@
. = ..()
if(!.)
return
- linked_cam = new(loc, src)
+ flags_atom |= (USES_HEARING|USES_SEEING)
+ if(!linked_cam || QDELETED(linked_cam))
+ linked_cam = new(loc, src)
+ else
+ linked_cam.status = TRUE
+ linked_cam.forceMove(loc)
SEND_SIGNAL(src, COMSIG_BROADCAST_GO_LIVE)
to_chat(user, SPAN_NOTICE("[src] begins to buzz softly as you go live."))
/obj/item/device/camera/broadcasting/unwield(mob/user)
. = ..()
- clear_broadcast()
+ flags_atom &= ~(USES_HEARING|USES_SEEING)
+ linked_cam.status = FALSE
+
+/obj/item/device/camera/broadcasting/proc/handle_rename(obj/item/camera, datum/component/label)
+ SIGNAL_HANDLER
+ if(!istype(label, /datum/component/label))
+ return
+ linked_cam.c_tag = get_broadcast_name()
/obj/item/device/camera/broadcasting/proc/clear_broadcast()
if(!QDELETED(linked_cam))
@@ -385,6 +403,12 @@
return src_label_component.label_name
return "Broadcast [serial_number]"
+/obj/item/device/camera/broadcasting/hear_talk(mob/living/sourcemob, message, verb = "says", datum/language/language, italics = FALSE)
+ SEND_SIGNAL(src, COMSIG_BROADCAST_HEAR_TALK, sourcemob, message, verb, language, italics, get_dist(sourcemob, src) < 3)
+
+/obj/item/device/camera/broadcasting/see_emote(mob/living/sourcemob, emote, audible = FALSE)
+ SEND_SIGNAL(src, COMSIG_BROADCAST_SEE_EMOTE, sourcemob, emote, audible, get_dist(sourcemob, src) < 3 && audible)
+
/obj/item/photo/proc/construct(datum/picture/P)
icon = P.fields["icon"]
tiny = P.fields["tiny"]
diff --git a/code/modules/reagents/chemistry_machinery/chem_master.dm b/code/modules/reagents/chemistry_machinery/chem_master.dm
index dc5206bb2df5..5b145f75940f 100644
--- a/code/modules/reagents/chemistry_machinery/chem_master.dm
+++ b/code/modules/reagents/chemistry_machinery/chem_master.dm
@@ -141,7 +141,15 @@
if(length(label) < 3)
loaded_pill_bottle.maptext_label = label
loaded_pill_bottle.update_icon()
+ else if(href_list["setcolor"])
+ // Checking for state changes
+ if(!loaded_pill_bottle)
+ return
+
+ if(!Adjacent(usr))
+ return
+ loaded_pill_bottle.choose_color()
else if(href_list["close"])
close_browser(user, "chemmaster")
@@ -355,7 +363,8 @@
if(pill_maker)
if(loaded_pill_bottle)
dat += "Eject [loaded_pill_bottle] \[[loaded_pill_bottle.contents.len]/[loaded_pill_bottle.max_storage_space]\]
"
- dat += "Add label to [loaded_pill_bottle] \[[loaded_pill_bottle.contents.len]/[loaded_pill_bottle.max_storage_space]\]
"
+ dat += "Add label to [loaded_pill_bottle] \[[loaded_pill_bottle.contents.len]/[loaded_pill_bottle.max_storage_space]\]
"
+ dat += "Set color to [loaded_pill_bottle] \[[loaded_pill_bottle.contents.len]/[loaded_pill_bottle.max_storage_space]\]
"
dat += "Transfer [loaded_pill_bottle] \[[loaded_pill_bottle.contents.len]/[loaded_pill_bottle.max_storage_space]\] to the smartfridge
"
else
dat += "No pill bottle inserted.
"
diff --git a/code/modules/tgui/states.dm b/code/modules/tgui/states.dm
index 0ec570ca9244..d826840dc495 100644
--- a/code/modules/tgui/states.dm
+++ b/code/modules/tgui/states.dm
@@ -105,3 +105,26 @@
return UI_DISABLED
// Otherwise, we got nothing.
return UI_CLOSE
+
+/**
+ * public
+ *
+ * Check if in view. Can interact only if adjacent, updates within max distance, otherwise closes
+ *
+ * required src_object atom/movable The object which owns the UI.
+ *
+ * return UI_state The state of the UI.
+ */
+/mob/living/proc/shared_living_ui_in_view(atom/movable/src_object, viewcheck = TRUE, max_distance = 7)
+ // If the object is obscured, close it.
+ if(viewcheck && !(src_object in view(src)))
+ return UI_CLOSE
+ var/dist = get_dist(src_object, src)
+ // Open and interact if 1-0 tiles away.
+ if(dist <= 1)
+ return UI_INTERACTIVE
+ // View only if within distance.
+ else if(dist <= max_distance)
+ return UI_UPDATE
+ // Otherwise, we got nothing.
+ return UI_CLOSE
diff --git a/code/modules/tgui/states/in_view.dm b/code/modules/tgui/states/in_view.dm
new file mode 100644
index 000000000000..76ab16e8c794
--- /dev/null
+++ b/code/modules/tgui/states/in_view.dm
@@ -0,0 +1,15 @@
+//If in view and within view distance
+GLOBAL_DATUM_INIT(in_view, /datum/ui_state/in_view, new)
+/datum/ui_state/in_view/can_use_topic(src_object, mob/user)
+ return user.in_view_can_use_topic(src_object) // Call the individual mob-overridden procs.
+
+/mob/proc/in_view_can_use_topic(src_object)
+ return UI_CLOSE // Don't allow interaction by default.
+
+/mob/ghost/in_view_can_use_topic(src_object)
+ return UI_UPDATE //ghost can just watch
+
+/mob/living/in_view_can_use_topic(src_object)
+ . = shared_ui_interaction(src_object)
+ if(. > UI_CLOSE && loc) //must not be in nullspace.
+ . = min(., shared_living_ui_in_view(src_object)) // Check the distance and view...
diff --git a/colonialmarines.dme b/colonialmarines.dme
index b69a03136b78..9392e74febea 100644
--- a/colonialmarines.dme
+++ b/colonialmarines.dme
@@ -2362,6 +2362,7 @@
#include "code\modules\tgui\states\default.dm"
#include "code\modules\tgui\states\hands.dm"
#include "code\modules\tgui\states\human_adjacent.dm"
+#include "code\modules\tgui\states\in_view.dm"
#include "code\modules\tgui\states\inventory.dm"
#include "code\modules\tgui\states\never.dm"
#include "code\modules\tgui\states\new_player.dm"
diff --git a/html/changelogs/AutoChangeLog-pr-6124.yml b/html/changelogs/AutoChangeLog-pr-6124.yml
deleted file mode 100644
index 1e02f4897a69..000000000000
--- a/html/changelogs/AutoChangeLog-pr-6124.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "realforest2001"
-delete-after: True
-changes:
- - rscadd: "Added ID Modification Log to the Access Report printout from an ID Console."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-6165.yml b/html/changelogs/AutoChangeLog-pr-6165.yml
deleted file mode 100644
index 0f67210d4ce4..000000000000
--- a/html/changelogs/AutoChangeLog-pr-6165.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Git-Nivrak"
-delete-after: True
-changes:
- - rscdel: "Reverted back to old throw logic"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-6166.yml b/html/changelogs/AutoChangeLog-pr-6166.yml
deleted file mode 100644
index 273b212dbda3..000000000000
--- a/html/changelogs/AutoChangeLog-pr-6166.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "realforest2001"
-delete-after: True
-changes:
- - bugfix: "Delaying round start now shows on the timer again."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-6167.yml b/html/changelogs/AutoChangeLog-pr-6167.yml
deleted file mode 100644
index 0406b100623e..000000000000
--- a/html/changelogs/AutoChangeLog-pr-6167.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Zonespace27"
-delete-after: True
-changes:
- - admin: "Unmarked tickets now mark themselves when an admin starts responding to one"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-6168.yml b/html/changelogs/AutoChangeLog-pr-6168.yml
deleted file mode 100644
index 3f1451a862c5..000000000000
--- a/html/changelogs/AutoChangeLog-pr-6168.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "harryob"
-delete-after: True
-changes:
- - bugfix: "you can bind to space again"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-6171.yml b/html/changelogs/AutoChangeLog-pr-6171.yml
deleted file mode 100644
index d9cd7b85fa1a..000000000000
--- a/html/changelogs/AutoChangeLog-pr-6171.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "iloveloopers"
-delete-after: True
-changes:
- - bugfix: "custom flamer fuels no longer work for increasing OT assembly's caps"
\ No newline at end of file
diff --git a/html/changelogs/archive/2024-04.yml b/html/changelogs/archive/2024-04.yml
index b3f9ffa64195..9aab61922f48 100644
--- a/html/changelogs/archive/2024-04.yml
+++ b/html/changelogs/archive/2024-04.yml
@@ -299,3 +299,53 @@
- rscadd: Added an ARES Log for chambering the OB Cannon.
vero5123:
- bugfix: toggling ghost vision now keeps the user's mob visible.
+2024-04-22:
+ Git-Nivrak:
+ - rscdel: Reverted back to old throw logic
+ GrrrKitten:
+ - rscadd: Adds shockwave VFX to powerful explosions
+ - rscadd: Adds shockwave VFX to Queen + Predalien screech
+ LC4492:
+ - rscadd: Adds a new, better variation of the normal photocopier.
+ - imageadd: Adds a new sprite to the new photocopier variation and updates the sprites
+ of both normal, and new photocopiers to look slightly better.
+ - maptweak: Adds the new photocopiers to the CC and CL offices.
+ Zonespace27:
+ - admin: Unmarked tickets now mark themselves when an admin starts responding to
+ one
+ harryob:
+ - bugfix: you can bind to space again
+ iloveloopers:
+ - bugfix: custom flamer fuels no longer work for increasing OT assembly's caps
+ realforest2001:
+ - bugfix: Delaying round start now shows on the timer again.
+ - rscadd: Added ID Modification Log to the Access Report printout from an ID Console.
+2024-04-23:
+ Drulikar:
+ - balance: Cryotubes now also cure external bleeding
+ - bugfix: Fixed internal bleeding granting blood on its last tick
+2024-04-24:
+ Git-Nivrak:
+ - bugfix: Fixes boilers slowing themselves by using long range sight while resting.
+ iloveloopers:
+ - qol: Alt clicking a reagent tank will now toggle whether its dispensing or filling
+2024-04-25:
+ Drathek:
+ - bugfix: 'Fixed hugger handling in drone tutorial: Now the hugger dying will put
+ another hugger into the tutorial morpher.'
+ Steelpoint:
+ - rscadd: UPP and CLF Combat Synthetic preset is now available for admins to spawn
+ them in.
+ ihatethisengine:
+ - bugfix: spotlight no longer breaks after every flight
+ - rscadd: Combat Correspondent can broadcast speech and emotes.
+ - rscadd: Speech close enough to the camera will be shown above connected TVs as
+ abovehead messages
+ realforest2001:
+ - bugfix: You can delete flight logs from ARES Interface again as intended.
+2024-04-26:
+ Git-Nivrak:
+ - qol: You can now change the color of pill bottles in the chem master or with a
+ hand labeler.
+ realforest2001:
+ - spellcheck: Fixes a typo for M4RA AP Ammo in the ASRS menu.
diff --git a/icons/obj/structures/machinery/library.dmi b/icons/obj/structures/machinery/library.dmi
index 3efeeef8b9f5..b2e62dc1bb93 100644
Binary files a/icons/obj/structures/machinery/library.dmi and b/icons/obj/structures/machinery/library.dmi differ
diff --git a/maps/map_files/USS_Almayer/USS_Almayer.dmm b/maps/map_files/USS_Almayer/USS_Almayer.dmm
index 84eb6b3b142a..6bc6bfeb4a22 100644
--- a/maps/map_files/USS_Almayer/USS_Almayer.dmm
+++ b/maps/map_files/USS_Almayer/USS_Almayer.dmm
@@ -56403,10 +56403,10 @@
},
/area/almayer/living/briefing)
"phj" = (
-/obj/structure/machinery/photocopier,
/obj/structure/machinery/light/small{
dir = 1
},
+/obj/structure/machinery/photocopier/wyphotocopier,
/turf/open/floor/almayer{
icon_state = "plate"
},
@@ -69589,12 +69589,10 @@
/obj/structure/machinery/light{
dir = 1
},
-/obj/structure/machinery/photocopier{
- anchored = 0
- },
/obj/structure/sign/poster/art{
pixel_y = 32
},
+/obj/structure/machinery/photocopier/wyphotocopier,
/turf/open/floor/wood/ship,
/area/almayer/command/corporateliaison)
"tHk" = (
diff --git a/tgui/packages/tgui/interfaces/AresInterface.jsx b/tgui/packages/tgui/interfaces/AresInterface.jsx
index be9106e31c25..bcd200d45d74 100644
--- a/tgui/packages/tgui/interfaces/AresInterface.jsx
+++ b/tgui/packages/tgui/interfaces/AresInterface.jsx
@@ -66,7 +66,7 @@ const Login = (props) => {
WY-DOS Executive
- Version 8.2.3
+ Version 8.3.4
Copyright © 2182, Weyland Yutani Corp.