diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm
index 49825bcb50..5102eb4802 100644
--- a/code/__HELPERS/roundend.dm
+++ b/code/__HELPERS/roundend.dm
@@ -3,6 +3,9 @@
#define POPCOUNT_SHUTTLE_ESCAPEES "shuttle_escapees" //Emergency shuttle only.
/datum/controller/subsystem/ticker/proc/gather_roundend_feedback()
+ var/datum/station_state/end_state = new /datum/station_state()
+ end_state.count()
+ station_integrity = min(PERCENT(GLOB.start_state.score(end_state)), 100)
gather_antag_data()
record_nuke_disk_location()
var/json_file = file("[GLOB.log_directory]/round_end_data.json")
@@ -71,9 +74,6 @@
mob_data += list("name" = m.name, "typepath" = m.type)
var/pos = length(file_data["[escaped]"]["[category]"]) + 1
file_data["[escaped]"]["[category]"]["[pos]"] = mob_data
- var/datum/station_state/end_state = new /datum/station_state()
- end_state.count()
- var/station_integrity = min(PERCENT(GLOB.start_state.score(end_state)), 100)
file_data["additional data"]["station integrity"] = station_integrity
WRITE_FILE(json_file, json_encode(file_data))
SSblackbox.record_feedback("nested tally", "round_end_stats", num_survivors, list("survivors", "total"))
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index c9562bcfd1..ceb003210e 100755
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -68,6 +68,8 @@ SUBSYSTEM_DEF(ticker)
var/modevoted = FALSE //Have we sent a vote for the gamemode?
+ var/station_integrity = 100 // stored at roundend for use in some antag goals
+
/datum/controller/subsystem/ticker/Initialize(timeofday)
load_mode()
diff --git a/code/datums/traits/negative.dm b/code/datums/traits/negative.dm
index d556d02ebb..f1d394ee2e 100644
--- a/code/datums/traits/negative.dm
+++ b/code/datums/traits/negative.dm
@@ -39,6 +39,8 @@
var/obj/item/heirloom
var/where
+GLOBAL_LIST_EMPTY(family_heirlooms)
+
/datum/quirk/family_heirloom/on_spawn()
var/mob/living/carbon/human/H = quirk_holder
var/obj/item/heirloom_type
@@ -77,6 +79,7 @@
/obj/item/lighter,
/obj/item/dice/d20)
heirloom = new heirloom_type(get_turf(quirk_holder))
+ GLOB.family_heirlooms += heirloom
var/list/slots = list(
"in your left pocket" = SLOT_L_STORE,
"in your right pocket" = SLOT_R_STORE,
diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm
index bb18b89678..4b7eebb5b9 100644
--- a/code/game/gamemodes/objective.dm
+++ b/code/game/gamemodes/objective.dm
@@ -366,6 +366,28 @@ GLOBAL_LIST_EMPTY(objectives)
return FALSE
return TRUE
+/datum/objective/breakout
+ name = "breakout"
+ martyr_compatible = 1
+ var/target_role_type = 0
+ var/human_check = TRUE
+
+/datum/objective/breakout/check_completion()
+ return !target || considered_escaped(target, enforce_human = human_check)
+
+/datum/objective/breakout/find_target_by_role(role, role_type=0, invert=0)
+ if(!invert)
+ target_role_type = role_type
+ ..()
+ return target
+
+/datum/objective/breakout/update_explanation_text()
+ ..()
+ if(target && target.current)
+ explanation_text = "Make sure [target.name], the [!target_role_type ? target.assigned_role : target.special_role] escapes on the shuttle or an escape pod alive and without being in custody."
+ else
+ explanation_text = "Free Objective"
+
/datum/objective/escape/escape_with_identity
name = "escape with identity"
var/target_real_name // Has to be stored because the target's real_name can change over the course of the round
@@ -583,7 +605,6 @@ GLOBAL_LIST_EMPTY(possible_items_special)
explanation_text = "Do not give up or lose [targetinfo.name]."
steal_target = targetinfo.targetitem
-
/datum/objective/download
name = "download"
@@ -998,3 +1019,68 @@ GLOBAL_LIST_EMPTY(possible_items_special)
if(I == horded_item)
return TRUE
return FALSE
+
+/datum/objective/horde/heirloom
+ name = "steal heirloom"
+
+/datum/objective/horde/heirloom/proc/find_target()
+ set_target(pick(GLOB.family_heirlooms))
+
+GLOBAL_LIST_EMPTY(possible_sabotages)
+// For saboteurs. Go in and cause some trouble somewhere. Not necessarily breaking things, just sufficiently troublemaking.
+/datum/objective/sabotage
+ name = "sabotage"
+ var/datum/sabotage_objective/targetinfo = null //composition > inheritance.
+
+/datum/objective/sabotage/get_target()
+ return targetinfo.sabotage_type
+
+/datum/objective/sabotage/New()
+ ..()
+ if(!GLOB.possible_sabotages.len)//Only need to fill the list when it's needed.
+ for(var/I in subtypesof(/datum/sabotage_objective))
+ new I
+
+/datum/objective/sabotage/find_target()
+ var/list/datum/mind/owners = get_owners()
+ var/approved_targets = list()
+ check_sabotages:
+ for(var/datum/sabotage_objective/possible_sabotage in GLOB.possible_sabotages)
+ if(!is_unique_objective(possible_sabotage.sabotage_type) || possible_sabotage.check_conditions())
+ continue
+ for(var/datum/mind/M in owners)
+ if(M.current.mind.assigned_role in possible_sabotage.excludefromjob)
+ continue check_sabotages
+ approved_targets += possible_sabotage
+ return set_target(safepick(approved_targets))
+
+/datum/objective/sabotage/proc/set_target(datum/sabotage_objective/sabo)
+ if(sabo)
+ targetinfo = sabo
+ explanation_text = "[targetinfo.name]"
+ give_special_equipment(targetinfo.special_equipment)
+ return sabo
+ else
+ explanation_text = "Free objective"
+ return
+
+/datum/objective/sabotage/check_completion()
+ return targetinfo.check_conditions()
+
+/datum/objective/flavor
+ name = "Flavor"
+ completable = FALSE
+
+/datum/objective/flavor/proc/forge_objective()
+
+/datum/objective/flavor/traitor/forge_objective()
+ explanation_text = pickweight(
+ "Teach the heads of staff a lesson they will never forget." = 1,
+ "Show Nanotrasen the utility of a pure oxygen atmosphere." = 2,
+ "The Donk Corporation has hired you with the task to maim the crew in whatever way you can. Strain the resources of medical staff, and create a hostile working enviroment for human resources." = 3,
+ "The Waffle Corporation has given you the task to create the biggest prank the station's security force has seen! Harass security, and don't stop while you can still honk!" = 4,
+ "Kill one of the station's beloved pets. Make a show of it." = 3,
+ "Get illegal technology spread through the station." = 3,
+ "Slow down the process of research as much as possible." = (owner.assigned_role in list("Research Director", "Scientist", "Roboticist") ? 6 : 0),
+ "Channel your inner rat. Cut wires throughout the station" = (owner.assigned_role in list("Station Engineer", "Atmos Technician", "Assistant") ? 6 : 0),
+ )
diff --git a/code/game/gamemodes/objective_sabotage.dm b/code/game/gamemodes/objective_sabotage.dm
new file mode 100644
index 0000000000..0ede317626
--- /dev/null
+++ b/code/game/gamemodes/objective_sabotage.dm
@@ -0,0 +1,106 @@
+/datum/sabotage_objective
+ var/name = "Free Objective"
+ var/sabotage_type = "nothing"
+ var/special_equipment = list()
+ var/list/excludefromjob = list()
+
+/datum/sabotage_objective/New()
+ ..()
+ if(sabotage_type!="nothing")
+ GLOB.possible_sabotages += src
+
+/datum/sabotage_objective/proc/check_conditions()
+ return TRUE
+
+/datum/sabotage_objective/processing
+ var/won = FALSE
+
+/datum/sabotage_objective/processing/New()
+ ..()
+ START_PROCESSING(SSprocessing, src)
+
+/datum/sabotage_objective/processing/proc/check_condition_processing()
+ return TRUE
+
+/datum/sabotage_objective/processing/process()
+ won = check_condition_processing()
+ if(won)
+ STOP_PROCESSING(SSprocessing,src)
+
+/datum/sabotage_objective/processing/power_sink
+ name = "Drain at least 1 gigajoule of power using a power sink."
+ sabotage_type = "powersink"
+ special_equipment = list(/obj/item/powersink)
+ var/sink_found = FALSE
+ var/count = 0
+
+/datum/sabotage_objective/processing/power_sink/check_condition_processing()
+ count += 1
+ if(count==10 || sink_found) // doesn't need to fire that often unless a sink exists
+ var/sink_found_this_time = FALSE
+ for(var/datum/powernet/PN in GLOB.powernets)
+ for(var/obj/item/powersink/sink in PN.nodes)
+ sink_found_this_time = TRUE
+ if(sink.power_drained>1e9)
+ return TRUE
+ sink_found = sink_found_this_time
+ count = 0
+ return FALSE
+
+/obj/item/paper/guides/antag/supermatter_sabotage
+ info = "Ways to sabotage a supermatter:
\
+