diff --git a/SQL/feedback_schema.sql b/SQL/feedback_schema.sql
index 09657498b9..cd4b5f07f3 100644
--- a/SQL/feedback_schema.sql
+++ b/SQL/feedback_schema.sql
@@ -246,7 +246,7 @@ CREATE TABLE IF NOT EXISTS `chatlogs_ckeys` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE IF NOT EXISTS `chatlogs_logs` (
- `id` int(11) NOT NULL AUTO_INCREMENT,
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
`round_id` int(11) NOT NULL DEFAULT -1,
`target` varchar(45) NOT NULL,
`text` mediumtext NOT NULL,
@@ -257,3 +257,40 @@ CREATE TABLE IF NOT EXISTS `chatlogs_logs` (
KEY `chatlogs_ckeys_FK` (`target`),
CONSTRAINT `chatlogs_ckeys_FK` FOREIGN KEY (`target`) REFERENCES `chatlogs_ckeys` (`ckey`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
+CREATE EVENT `chatlogs_logs_clear_old_logs`
+ ON SCHEDULE
+ EVERY 1 MONTH STARTS '2025-01-01 04:00:00'
+ ON COMPLETION PRESERVE
+ ENABLE
+ COMMENT 'This event periodically clears the chatlog logs of very old logs'
+ DO BEGIN
+
+DELETE FROM chatlogs_logs WHERE created_at < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 3 MONTH)) * 1000;
+
+END
+
+CREATE TABLE `chatlogs_rounds` (
+ `round_id` BIGINT(20) NOT NULL DEFAULT -1,
+ `ckey` VARCHAR(45) NOT NULL COLLATE 'utf8mb4_uca1400_ai_ci',
+ PRIMARY KEY (`round_id`, `ckey`) USING BTREE
+) ENGINE=InnoDB COLLATE='utf8mb4_uca1400_ai_ci';
+
+CREATE PROCEDURE `chatlogs_rounds_insert`(
+ IN `p_round_id` BIGINT,
+ IN `p_ckey` VARCHAR(45)
+)
+LANGUAGE SQL
+NOT DETERMINISTIC
+CONTAINS SQL
+SQL SECURITY INVOKER
+COMMENT 'Inserts a new row into \'chatlogs_rounds\' and deletes the oldest entry, if the ckey already has 10 round id\'s stored.'
+BEGIN
+
+INSERT IGNORE INTO chatlogs_rounds(round_id, ckey) VALUES (p_round_id, p_ckey);
+
+IF (SELECT COUNT(*) FROM chatlogs_rounds WHERE ckey = p_ckey) > 10 THEN
+ DELETE FROM chatlogs_rounds WHERE ckey = p_ckey ORDER BY round_id ASC LIMIT 1;
+END IF;
+
+END
diff --git a/code/__defines/vchatlog.dm b/code/__defines/vchatlog.dm
index 7b53cd1fbd..a466767cf4 100644
--- a/code/__defines/vchatlog.dm
+++ b/code/__defines/vchatlog.dm
@@ -17,7 +17,7 @@
* * ckey - Ckey of the message receiver
* * token - Randomized token
*/
-#define vchatlog_generate_token(ckey) VCHATLOG_CALL("generate_token", ckey)
+#define vchatlog_generate_token(ckey, round_id) VCHATLOG_CALL("generate_token", ckey, round_id)
/**
* Writes a new chatlog entry to the database. This function does not return anything.
diff --git a/code/game/objects/items/devices/scanners/gene.dm b/code/game/objects/items/devices/scanners/gene.dm
index 368b8a0944..7471f545d4 100644
--- a/code/game/objects/items/devices/scanners/gene.dm
+++ b/code/game/objects/items/devices/scanners/gene.dm
@@ -98,6 +98,8 @@
output += span_infoplain("Bloodtype: [I.buf.dna.b_type]") + "
"
output += span_boldnotice("Detected genes:") + "
"
for(var/datum/gene/G in GLOB.dna_genes)
+ if(I.block && G.block != I.block)
+ continue
if(!I.buf.dna.GetSEState(G.block))
continue
var/dna_val = I.buf.dna.GetSEValue(G.block)
diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm
index 37c5ef784c..eaa98f36e2 100644
--- a/code/modules/client/client procs.dm
+++ b/code/modules/client/client procs.dm
@@ -245,7 +245,7 @@
GLOB.directory[ckey] = src
if (CONFIG_GET(flag/chatlog_database_backend))
- chatlog_token = vchatlog_generate_token(ckey)
+ chatlog_token = vchatlog_generate_token(ckey, GLOB.round_id)
// Instantiate stat panel
stat_panel = new(src, "statbrowser")
diff --git a/code/modules/mob/living/carbon/human/species/shadekin/shadekin.dm b/code/modules/mob/living/carbon/human/species/shadekin/shadekin.dm
index 30cb28fee6..fa011c5662 100644
--- a/code/modules/mob/living/carbon/human/species/shadekin/shadekin.dm
+++ b/code/modules/mob/living/carbon/human/species/shadekin/shadekin.dm
@@ -131,6 +131,7 @@
/datum/species/shadekin/handle_environment_special(var/mob/living/carbon/human/H)
handle_shade(H)
+ ..()
/datum/species/shadekin/add_inherent_verbs(var/mob/living/carbon/human/H)
..()
diff --git a/code/modules/mob/living/carbon/human/species/station/alraune.dm b/code/modules/mob/living/carbon/human/species/station/alraune.dm
index e981df33ba..3c344f7657 100644
--- a/code/modules/mob/living/carbon/human/species/station/alraune.dm
+++ b/code/modules/mob/living/carbon/human/species/station/alraune.dm
@@ -134,7 +134,7 @@
if(H.does_not_breathe)
H.failed_last_breath = 0
H.adjustOxyLoss(-5)
- return // if somehow they don't breathe, abort breathing.
+ return ..()// if somehow they don't breathe, abort breathing.
if(!breath || (breath.total_moles == 0))
H.failed_last_breath = 1
@@ -145,7 +145,7 @@
H.throw_alert("pressure", /obj/screen/alert/lowpressure)
- return // skip air processing if there's no air
+ return ..() // skip air processing if there's no air
else
H.clear_alert("pressure")
@@ -304,7 +304,7 @@
get_environment_discomfort(src,"cold")
breath.update_values()
- return 1
+ ..()
/obj/item/organ/internal/brain/alraune
icon = 'icons/mob/species/alraune/organs.dmi'
diff --git a/code/modules/mob/living/carbon/human/species/station/station.dm b/code/modules/mob/living/carbon/human/species/station/station.dm
index 7facdc2c99..536f346b35 100644
--- a/code/modules/mob/living/carbon/human/species/station/station.dm
+++ b/code/modules/mob/living/carbon/human/species/station/station.dm
@@ -707,6 +707,7 @@
//traumatic_shock is updated every tick, incrementing that is pointless - shock_stage is the counter.
//Not that it matters much for diona, who have NO_PAIN.
H.shock_stage++
+ ..()
@@ -1577,7 +1578,7 @@
coldshock = 16
H.eye_blurry = 5
H.shock_stage = min(H.shock_stage + coldshock, 160) //cold hurts and gives them pain messages, eventually weakening and paralysing, but doesn't damage.
- return
+ ..()
/datum/species/werebeast
name = SPECIES_WEREBEAST
@@ -1738,6 +1739,7 @@
if(temp_diff >= 50)
H.shock_stage = min(H.shock_stage + (temp_diff/20), 160) // Divided by 20 is the same as previous numbers, but a full scale
H.eye_blurry = max(5,H.eye_blurry)
+ ..()
/datum/species/xenochimera/get_race_key()
var/datum/species/real = GLOB.all_species[base_species]
diff --git a/code/modules/mob/living/carbon/human/species/virtual_reality/avatar.dm b/code/modules/mob/living/carbon/human/species/virtual_reality/avatar.dm
index e049ce6f78..49529e6c5b 100644
--- a/code/modules/mob/living/carbon/human/species/virtual_reality/avatar.dm
+++ b/code/modules/mob/living/carbon/human/species/virtual_reality/avatar.dm
@@ -49,6 +49,7 @@
return
/datum/species/shapeshifter/promethean/avatar/handle_environment_special(var/mob/living/carbon/human/H)
+ //Traits like anxiety won't apply here, but that's the issue with them being a subtype of Promethean.
return
/mob/living/carbon/human/proc/shapeshifter_change_opacity()
diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm
index 2b5f3fe0e4..177b33e0e8 100644
--- a/code/modules/reagents/reagent_containers/syringes.dm
+++ b/code/modules/reagents/reagent_containers/syringes.dm
@@ -166,7 +166,7 @@
to_chat(user, span_notice("[target] is empty."))
return
- if(!target.is_open_container() && !istype(target, /obj/structure/reagent_dispensers) && !istype(target, /obj/item/slime_extract) && !istype(target, /obj/item/reagent_containers/food))
+ if(!target.is_open_container() && !istype(target, /obj/structure/reagent_dispensers) && !istype(target, /obj/item/slime_extract) && !istype(target, /obj/item/reagent_containers/food) && !istype(target, /obj/item/reagent_containers/blood))
to_chat(user, span_notice("You cannot directly remove reagents from this object."))
return
diff --git a/code/modules/resleeving/computers.dm b/code/modules/resleeving/computers.dm
index 5626c87fd6..70979922d9 100644
--- a/code/modules/resleeving/computers.dm
+++ b/code/modules/resleeving/computers.dm
@@ -463,6 +463,7 @@
I.desc = "Resequences structural enzymes to match the body record this was created from."
I.buf = active_br.mydna.copy()
I.buf.types = DNA2_BUF_SE
+ I.has_radiation = FALSE // SAFE!
atom_say("Beginning injector synthesis.")
addtimer(CALLBACK(src, PROC_REF(dispense_injector), I), 10 SECONDS, TIMER_DELETE_ME)
. = TRUE
diff --git a/code/unit_tests/reagent_tests.dm b/code/unit_tests/reagent_tests.dm
index f5b147da7c..cebd399d37 100644
--- a/code/unit_tests/reagent_tests.dm
+++ b/code/unit_tests/reagent_tests.dm
@@ -28,9 +28,11 @@
log_unit_test("[Rpath]: Reagents - Reagent ID must be all lowercase.")
failed = TRUE
- if(collection_name[R.name] && !(R.wiki_flag & WIKI_SPOILER)) // If wiki hidden it's probably intentional!
- log_unit_test("[Rpath]: Reagents - WARNING - reagent name \"[R.name]\" is not unique, used first in [collection_name[R.name]]. Is this intentional?")
- collection_name[R.name] = R.type
+ if(!(R.wiki_flag & WIKI_SPOILER)) // If wiki hidden then don't conflict test it against name, used for intentionally copied names like beer2's
+ if(collection_name[R.name])
+ log_unit_test("[Rpath]: Reagents - reagent name \"[R.name]\" is not unique, used first in [collection_name[R.name]].")
+ failed = TRUE
+ collection_name[R.name] = R.type
if(collection_id[R.id])
log_unit_test("[Rpath]: Reagents - reagent ID \"[R.id]\" is not unique, used first in [collection_id[R.id]].")
diff --git a/html/changelogs/AutoChangeLog-pr-17904.yml b/html/changelogs/AutoChangeLog-pr-17904.yml
new file mode 100644
index 0000000000..49e84206f8
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-17904.yml
@@ -0,0 +1,4 @@
+author: "TheCaramelion"
+delete-after: True
+changes:
+ - qol: "You can extract blood from blood bags using a syringe"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-17906.yml b/html/changelogs/AutoChangeLog-pr-17906.yml
new file mode 100644
index 0000000000..eccc3f06b6
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-17906.yml
@@ -0,0 +1,4 @@
+author: "Willburd"
+delete-after: True
+changes:
+ - code_imp: "Reagent unit-test is more strict about duplicated reagent names"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-17919.yml b/html/changelogs/AutoChangeLog-pr-17919.yml
new file mode 100644
index 0000000000..f97ec88d13
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-17919.yml
@@ -0,0 +1,4 @@
+author: "Diana"
+delete-after: True
+changes:
+ - bugfix: "Xenochimera, spider, diona, shadekin, and alraune now process traits properly."
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-17921.yml b/html/changelogs/AutoChangeLog-pr-17921.yml
new file mode 100644
index 0000000000..1bb2eda904
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-17921.yml
@@ -0,0 +1,5 @@
+author: "Willburd"
+delete-after: True
+changes:
+ - bugfix: "dna block injectors no longer show their entire genome, and instead just the relevant block. Doesn't affect full sequence injectors."
+ - bugfix: "dna resequencers from the body record console should not do radiation damage"
\ No newline at end of file
diff --git a/libvchatlog.so b/libvchatlog.so
index a0b04f523e..1fa6cb6071 100755
Binary files a/libvchatlog.so and b/libvchatlog.so differ
diff --git a/vchatlog.dll b/vchatlog.dll
index d43fbb7482..15c47ac639 100644
Binary files a/vchatlog.dll and b/vchatlog.dll differ