diff --git a/js/003-data/gameVariableData.js b/js/003-data/gameVariableData.js
index 2746b34ae4bf16e857610de827bede78fc20b8b3..2baa9a734b1055a93b0184970bbdd2258a1c41aa 100644
--- a/js/003-data/gameVariableData.js
+++ b/js/003-data/gameVariableData.js
@@ -104,6 +104,8 @@ App.Data.defaultGameStateVariables = {
 	debugMode: 0,
 	debugModeCustomFunction: 0,
 	debugModeEventSelection: 0,
+	/** @type {FC.Bool} If true then the patching system will report any changes that happen */
+	reportVerificationChanges: 0,
 	difficultySwitch: 0,
 	disableLisping: 0,
 	displayAssignments: 1,
diff --git a/src/data/patches/patch.js b/src/data/patches/patch.js
index 12995ef6a75330d24787e27d2196257199f7c4df..3abc2d7c05925f0b6ac86e5d6a472e06a3214596 100644
--- a/src/data/patches/patch.js
+++ b/src/data/patches/patch.js
@@ -1,4 +1,4 @@
-// cSpell:words TLDR
+// cSpell:words divs
 
 /**
  * Need to know how to apply a patch? Go to the line with `====== How to apply a patch ======`.
@@ -76,6 +76,7 @@
  */
 
 // TODO:@franklygeorge add a way to force apply a list of patch versions and types in order
+// TODO:@franklygeorge move the "Open All Reports" code from App.EndWeek.slaveAssignmentReport to App.UI.DOM and then utilize it in the patch system
 
 /**
  * registers a patch to the database
@@ -123,216 +124,297 @@ App.Patch.log = (message, type="info") => {
  * @returns {DocumentFragment} a document fragment with the results of the patching
  */
 App.Patch.applyAll = () => {
-	// console.time('Patching All');
-	// TODO:@franklygeorge on error, provide a human parsable patch log with valid markdown formatting for directly pasteing into a gitlab issue
-	// TODO:@franklygeorge include a save in the above in text form (do we need to change active passage?)
-	// TODO:@franklygeorge put each patches info in a collapsed div with a number for how many times `App.Patch.log` was called. `Log (call times)`
-	// TODO:@franklygeorge remove said collapsed div if it has no content
-	const f = document.createDocumentFragment();
-	let header = App.UI.DOM.appendNewElement("div", f);
-
-	const patchVersions = Object.keys(App.Patch.Patches).sort();
-
-	// check that the last patch (largest releaseID) is equal to App.Version.release
-	if (Number(patchVersions.at(-1)) > App.Version.release) {
-		throw new Error(`Patch exists for release ${patchVersions.at(-1)}, but App.Version.release is at ${App.Version.release}! Did you forget to increment release in '/src/002-config/fc-version.js'?`);
-	} else if (!(App.Version.release in App.Patch.Patches)) {
+	// create a loading screen
+	App.Patch.Utils.loadingScreen.start();
+
+	// create our document fragment and our main divs
+	const frag = document.createDocumentFragment();
+	const head = App.UI.DOM.appendNewElement("div", frag);
+	head.id = "patch-header";
+	const cDiv = App.UI.DOM.appendNewElement("div", frag);
+	cDiv.id = "patch-content";
+
+	// get all available patches in reverse order so that when we call .pop we will get the smallest patch version
+	let patchVersions = Object.keys(App.Patch.Patches).sort((a, b)=> +b - +a);
+
+	// check that the first patch in patchVersions (largest releaseID) is equal to App.Version.release
+	if (Number(patchVersions[0]) > App.Version.release) {
+		throw new Error(`Patch exists for release ${patchVersions[0]}, but App.Version.release is at ${App.Version.release}! Did you forget to increment release in '/src/002-config/fc-version.js'?`);
+	} else if (Number(patchVersions[0]) < App.Version.release) {
 		throw new Error(`No patch exists for releaseID ${App.Version.release}! Did you make a patch? Instructions are in '/src/data/patches/patch.js'.`);
 	}
 
-	/**
-	 * @typedef {object} App.Patch.applyAllWombsObj
-	 * @property {string} identifier
-	 * @property {FC.HumanState} actor
-	 * @property {HTMLDivElement} div
-	 */
-
-	let i;
-	/** @type {number} */
-	let patchID;
-	/** @type {App.Patch.applyAllWombsObj[]} */
-	let wombs = [];
-
-	try {
-		for (const ID in patchVersions) {
-			patchID = Number(patchVersions[ID]);
-			App.Patch.Utils.current.patch = patchID;
-			if (patchID <= V.releaseID) { continue; }
-			console.time(`Patch ${patchID}`);
-
-			App.UI.DOM.appendNewElement("h3", f, `Applying Patch ${patchID}`);
-			// console.log(`Applying Patch ${patchID}`);
-
-			App.Patch.Utils.current.div = App.UI.DOM.appendNewElement("div", f);
-
-			// pre patch
-			// console.time(`Patch ${patchID} Pre`);
-			App.Patch.Utils.gameVariables("pre", App.Patch.Utils.current.div, patchID); // This should always be the first patch
-			// console.timeEnd(`Patch ${patchID} Pre`);
-
-			// PlayerState
-			// console.time(`Patch ${patchID} PlayerState`);
-			App.Patch.Utils.playerState("V.PC", V.PC, "V.PC", App.Patch.Utils.current.div, patchID);
-			wombs.push({identifier: `V.PC`, actor: V.PC, div: App.Patch.Utils.current.div});
-			// console.timeEnd(`Patch ${patchID} PlayerState`);
-
-			// SlaveState
-			// console.time(`Patch ${patchID} SlaveState`);
-			for (i in V.slaves ?? []) {
-				App.Patch.Utils.slaveState(
-					`V.slaves[${i}]`, V.slaves[i], "V.slaves", App.Patch.Utils.current.div, patchID
-				);
-				wombs.push({identifier: `V.slaves[${i}]`, actor: V.slaves[i], div: App.Patch.Utils.current.div});
-			}
+	// remove all unneeded patch versions from the array
+	patchVersions = patchVersions.filter((patchID) => { return Number(patchID) > V.releaseID; });
+	// get the amount of the patches we are going to apply for later use
+	const patchCount = patchVersions.length;
 
-			if (V.hostage) {
-				App.Patch.Utils.slaveState(
-					`V.hostage`, V.hostage, "V.hostage", App.Patch.Utils.current.div, patchID
-				);
-				wombs.push({identifier: `V.hostage`, actor: V.hostage, div: App.Patch.Utils.current.div});
-			}
+	// We use setTimeout here to give the App.Patch.applyAll() function time to return frag and SugarCube the time to change passages
+	// The execution flow skips this setTimeout call temporarily
+	setTimeout((() => {
+		// get the elements we created earlier
+		let f = document.getElementById("patch-content");
+		let header = document.getElementById("patch-header");
+		// this element was created by the App.Patch.Utils.loadingScreen.start() call
+		let message = document.getElementById("patch-message");
+
+		let verificationStart;
 
-			if (V.boomerangSlave) {
-				App.Patch.Utils.slaveState(
-					`V.boomerangSlave`, V.boomerangSlave, "V.boomerangSlave",
-					App.Patch.Utils.current.div, patchID
-				);
-				wombs.push({identifier: `V.boomerangSlave`, actor: V.boomerangSlave, div: App.Patch.Utils.current.div});
+		/** This is called by App.Verify.everything() when it is done */
+		const finalize = () => {
+			if (getProp(V, "reportVerificationChanges", 0) === 0) {
+				App.Patch.Utils.current.div.append(App.UI.DOM.makeElement('div', "Change detection was skipped because it is disabled"));
 			}
 
-			if (V.shelterSlave) {
-				App.Patch.Utils.slaveState(
-					`V.shelterSlave`, V.shelterSlave, "V.shelterSlave",
-					App.Patch.Utils.current.div, patchID
-				);
-				wombs.push({identifier: `V.shelterSlave`, actor: V.shelterSlave, div: App.Patch.Utils.current.div});
+			// report the time taken and the amount of content added to current div
+			const verificationTime = (new Date().getTime() - verificationStart).toLocaleString();
+			const divLength = App.Patch.Utils.current.div.childNodes.length;
+
+			f.append(App.UI.DOM.accordion(
+				`Cleanup and Verification (${divLength}) (${verificationTime}ms)`,
+				App.Patch.Utils.current.div,
+				(divLength === 0 || getProp(V, "useAccordion", 1) > 0)
+			));
+
+			// handle App.Verify.Utils.verificationError
+			if (App.Verify.Utils.verificationError) {
+				header.innerHTML += `<span class="error">Verification failed!</span>`;
+				console.error("Verification failed!");
+				// @ts-ignore
+				State.restore(); // restore the state to before patching
+			} else {
+				if (patchCount !== 0) {
+					header.innerHTML += `<span class="green">${patchCount} patches applied! You are on release ${V.releaseID}</span>`;
+				} else {
+					header.innerHTML += `<span class="green">No patches needed to be applied! You are on release ${V.releaseID}</span>`;
+				}
+				App.UI.DOM.appendNewElement("h3", f, "Done!");
+				console.log("Done!");
 			}
 
-			if (V.traitor) {
-				App.Patch.Utils.slaveState(
-					`V.traitor`, V.traitor, "V.traitor", App.Patch.Utils.current.div, patchID
-				);
-				wombs.push({identifier: `V.traitor`, actor: V.traitor, div: App.Patch.Utils.current.div});
+			header.innerHTML += `<br><span>See below for details</span><hr>`;
+
+			// update the sidebar to remove any potential errors that were cause by being on an outdated save
+			App.Utils.scheduleSidebarRefresh();
+
+			// remove the loading screen so the user can see the results
+			App.Patch.Utils.loadingScreen.end();
+
+			App.Patch.Utils.current.div = undefined;
+			// this is where the execution actually stops for the patching system
+		};
+
+		/**
+		 * This function is called later in the code.
+		 * It does verification and final cleanup after the patching is done
+		 * @returns {undefined}
+		 */
+		const verifyAndCleanup = () => {
+			// make a new div
+			App.Patch.Utils.current.div = App.UI.DOM.makeElement("div");
+
+			// Cleanup
+			console.log(`Cleanup`);
+
+			V.NaNArray = findNaN(); // reset NaNArray
+			App.UI.SlaveSummary.settingsChanged(); // let slave summary know that settings may have changed
+
+			// verification
+			console.log(`Verification`);
+
+			App.Verify.Utils.verificationError = false;
+			try {
+				verificationStart = new Date().getTime();
+				// does the actual verification
+				App.Verify.everything(App.Patch.Utils.current.div, finalize);
+			} catch (e) {
+				header.innerHTML += `<span class="error">Verification failed!</span>`;
+				console.error("Verification failed!");
+				App.Patch.Utils.current.div.append(App.UI.DOM.formatException(e));
+				// @ts-ignore
+				State.restore(); // restore the state to before patching
+				header.innerHTML += `<br><span>See below for details</span><hr>`;
+				// close the loading screen so that the user can see the error
+				App.Patch.Utils.loadingScreen.end();
+				return;
 			}
-			// console.timeEnd(`Patch ${patchID} SlaveState`);
-
-			// TankSlaveState
-			// console.time(`Patch ${patchID} TankSlaveState`);
-			for (i in V.incubator.tanks ?? []) {
-				App.Patch.Utils.tankSlaveState(
-					`V.incubator.tanks[${i}]`, V.incubator.tanks[i], App.Patch.Utils.current.div, patchID
-				);
-				wombs.push({
-					identifier: `V.incubator.tanks[${i}]`,
-					actor: V.incubator.tanks[i],
-					div: App.Patch.Utils.current.div
+		};
+
+		const patchingStart = new Date().getTime();
+		let i;
+
+		/**
+		 * This function is called later in the code.
+		 * It applies a single patch.
+		 * If there are more patches to apply it calls itself, if not then it calls verifyAndCleanup().
+		 * @returns {undefined}
+		 */
+		const applyPatch = () => {
+			const patchID = Number(patchVersions.pop());
+
+			/** @type {{identifier: string, actor: FC.HumanState, div: HTMLDivElement}[]} */
+			const wombs = [];
+
+			const patchStart = new Date().getTime();
+
+			// patch
+			try {
+				// make a new div
+				App.Patch.Utils.current.div = App.UI.DOM.makeElement("div");
+
+				// Pre patch; This should always be the first patch
+				App.Patch.Utils.gameVariables("pre", App.Patch.Utils.current.div, patchID);
+
+				// PlayerState
+				App.Patch.Utils.playerState("V.PC", V.PC, "V.PC", App.Patch.Utils.current.div, patchID);
+				wombs.push({identifier: `V.PC`, actor: V.PC, div: App.Patch.Utils.current.div});
+
+				// SlaveState
+				for (i in V.slaves ?? []) {
+					App.Patch.Utils.slaveState(
+						`V.slaves[${i}]`, V.slaves[i], "V.slaves", App.Patch.Utils.current.div, patchID
+					);
+					wombs.push({identifier: `V.slaves[${i}]`, actor: V.slaves[i], div: App.Patch.Utils.current.div});
+				}
+
+				if (V.hostage) {
+					App.Patch.Utils.slaveState(
+						`V.hostage`, V.hostage, "V.hostage", App.Patch.Utils.current.div, patchID
+					);
+					wombs.push({identifier: `V.hostage`, actor: V.hostage, div: App.Patch.Utils.current.div});
+				}
+
+				if (V.boomerangSlave) {
+					App.Patch.Utils.slaveState(
+						`V.boomerangSlave`, V.boomerangSlave, "V.boomerangSlave",
+						App.Patch.Utils.current.div, patchID
+					);
+					wombs.push({identifier: `V.boomerangSlave`, actor: V.boomerangSlave, div: App.Patch.Utils.current.div});
+				}
+
+				if (V.shelterSlave) {
+					App.Patch.Utils.slaveState(
+						`V.shelterSlave`, V.shelterSlave, "V.shelterSlave",
+						App.Patch.Utils.current.div, patchID
+					);
+					wombs.push({identifier: `V.shelterSlave`, actor: V.shelterSlave, div: App.Patch.Utils.current.div});
+				}
+
+				if (V.traitor) {
+					App.Patch.Utils.slaveState(
+						`V.traitor`, V.traitor, "V.traitor", App.Patch.Utils.current.div, patchID
+					);
+					wombs.push({identifier: `V.traitor`, actor: V.traitor, div: App.Patch.Utils.current.div});
+				}
+
+				// TankSlaveState
+				for (i in V.incubator.tanks ?? []) {
+					App.Patch.Utils.tankSlaveState(
+						`V.incubator.tanks[${i}]`, V.incubator.tanks[i], App.Patch.Utils.current.div, patchID
+					);
+					wombs.push({
+						identifier: `V.incubator.tanks[${i}]`,
+						actor: V.incubator.tanks[i],
+						div: App.Patch.Utils.current.div
+					});
+				}
+
+				// InfantState
+				for (i in V.cribs ?? []) {
+					App.Patch.Utils.infantState(
+						`V.cribs[${i}]`, V.cribs[i], "V.cribs", App.Patch.Utils.current.div, patchID
+					);
+					wombs.push({identifier: `V.cribs[${i}]`, actor: V.cribs[i], div: App.Patch.Utils.current.div}); // shouldn't be needed, but InfantStates are HumanStates so might as well make sure it is right
+				}
+
+				// ChildState
+				// TODO:@franklygeorge this is waiting on FC.ChildState to be implemented
+				const children = [];
+				for (i in children) {
+					App.Patch.Utils.childState(
+						// @ts-ignore
+						`children[${i}]`, children[i], "children", App.Patch.Utils.current.div, patchID
+					);
+					wombs.push({identifier: `children[${i}]`, actor: children[i], div: App.Patch.Utils.current.div});
+				}
+
+				// Wombs/Fetuses; This should always be below all the HumanState patches
+				wombs.forEach((womb) => {
+					App.Patch.Utils.patchWomb(womb.identifier, womb.actor, womb.div, patchID);
 				});
+
+				// CustomOrderSlave
+				if (V.customSlave) {
+					App.Patch.Utils.customSlaveOrder(
+						"V.customSlave", V.customSlave, App.Patch.Utils.current.div, patchID
+					);
+				}
+				if (V.huskSlave) {
+					App.Patch.Utils.customSlaveOrder(
+						"V.huskSlave", V.huskSlave, App.Patch.Utils.current.div, patchID
+					);
+				}
+
+				// Post patch; This should always be the last patch
+				App.Patch.Utils.gameVariables("post", App.Patch.Utils.current.div, patchID);
+			} catch (e) {
+				header.innerHTML += `<span class="error">Patching Failed when applying patch ${App.Patch.Utils.current.patch}["${App.Patch.Utils.current.type}"] to ${App.Patch.Utils.current.identifier}!</span>`;
+				App.Patch.Utils.current.div.append(App.UI.DOM.formatException(e));
+				// @ts-ignore
+				State.restore(); // restore the state to before patching
+				header.innerHTML += `<br><span>See below for details</span><hr>`;
+				// close the loading screen so that the user can see the error
+				App.Patch.Utils.loadingScreen.end();
+				return;
 			}
-			// console.timeEnd(`Patch ${patchID} TankSlaveState`);
-
-			// InfantState
-			// console.time(`Patch ${patchID} InfantState`);
-			for (i in V.cribs ?? []) {
-				App.Patch.Utils.infantState(
-					`V.cribs[${i}]`, V.cribs[i], "V.cribs", App.Patch.Utils.current.div, patchID
-				);
-				wombs.push({identifier: `V.cribs[${i}]`, actor: V.cribs[i], div: App.Patch.Utils.current.div}); // shouldn't be needed, but InfantStates are HumanStates so might as well make sure it is right
-			}
-			// console.timeEnd(`Patch ${patchID} InfantState`);
-
-			// ChildState
-			// console.time(`Patch ${patchID} ChildState`);
-			// TODO:@franklygeorge this is waiting on FC.ChildState to be implemented
-			const children = [];
-			for (i in children) {
-				App.Patch.Utils.childState(
-					// @ts-ignore
-					`children[${i}]`, children[i], "children", App.Patch.Utils.current.div, patchID
-				);
-				wombs.push({identifier: `children[${i}]`, actor: children[i], div: App.Patch.Utils.current.div});
-			}
-			// console.timeEnd(`Patch ${patchID} ChildState`);
-
-			// Wombs
-			// console.time(`Patch ${patchID} Fetuses`);
-			wombs.forEach((womb) => {
-				App.Patch.Utils.patchWomb(womb.identifier, womb.actor, womb.div, patchID);
-			});
-			// console.timeEnd(`Patch ${patchID} Fetuses`);
-
-			// CustomOrderSlave
-			// console.time(`Patch ${patchID} CustomOrderSlave`);
-			if (V.customSlave) {
-				App.Patch.Utils.customSlaveOrder(
-					"V.customSlave", V.customSlave, App.Patch.Utils.current.div, patchID
-				);
-			}
-			if (V.huskSlave) {
-				App.Patch.Utils.customSlaveOrder(
-					"V.huskSlave", V.huskSlave, App.Patch.Utils.current.div, patchID
-				);
+
+			// report the time taken and the amount of content added to current div
+			const patchTime = (new Date().getTime() - patchStart).toLocaleString();
+			const divLength = App.Patch.Utils.current.div.childNodes.length;
+
+			f.append(App.UI.DOM.accordion(
+				`Patch ${patchID} (${divLength}) (${patchTime}ms)`,
+				App.Patch.Utils.current.div,
+				(divLength === 0 || getProp(V, "useAccordion", 1) > 0)
+			));
+
+			// continue
+			if (patchVersions.length !== 0) {
+				// let the user know what we are doing, by changing the loading screen message
+				message.innerHTML = `Applying Patch ${patchVersions.last()}...`;
+				// give the DOM time to update and then call applyPatch() again
+				setTimeout(applyPatch, 0);
+			} else {
+				const patchingTime = (new Date().getTime() - patchingStart).toLocaleString();
+				f.append(App.UI.DOM.accordion(
+					`Patching results (${patchCount} patches) (${patchingTime}ms)`,
+					App.UI.DOM.makeElement('div', `Patching completed successfully, applying ${patchCount} patches in ${patchingTime}ms`),
+					true,
+				));
+				// let the user know what we are doing, by changing the loading screen message
+				message.innerHTML = `Cleaning up and verifying...`;
+				// give the DOM time to update and then call verifyAndCleanup()
+				setTimeout(verifyAndCleanup, 0);
 			}
-			// console.timeEnd(`Patch ${patchID} CustomOrderSlave`);
+		};
 
-			// post patch
-			// console.time(`Patch ${patchID} Post`);
-			App.Patch.Utils.gameVariables("post", App.Patch.Utils.current.div, patchID); // This should always be the last patch
-			// console.timeEnd(`Patch ${patchID} Post`);
-			console.timeEnd(`Patch ${patchID}`);
+		// start the patching
+		if (patchVersions.length !== 0) {
+			// let the user know what we are doing, by changing the loading screen message
+			message.innerHTML = `Applying Patch ${patchVersions.last()}...`;
+			// give the DOM time to update and then call applyPatch()
+			setTimeout(applyPatch, 0);
+		} else {
+			// we didn't have any patches to apply but we still run verification
+			App.UI.DOM.appendNewElement('div', f, `No patches needed to be applied`);
+			// let the user know what we are doing, by changing the loading screen message
+			message.innerHTML = `Cleaning up and verifying...`;
+			// give the DOM time to update and then call verifyAndCleanup()
+			setTimeout(verifyAndCleanup, 0);
 		}
-	} catch (e) {
-		header.innerHTML += `<span class="error">Patching Failed when applying patch ${App.Patch.Utils.current.patch}["${App.Patch.Utils.current.type}"] to ${App.Patch.Utils.current.identifier}!</span>`;
-		App.Patch.Utils.current.div.append(App.UI.DOM.formatException(e));
-		// @ts-ignore
-		State.restore(); // restore the state to before patching
-		header.innerHTML += `<br><span>See below for details</span><hr>`;
-		// report the results
-		return f;
-	}
-
-	App.Patch.Utils.current.div = App.UI.DOM.appendNewElement("div", f);
-
-	// verification
-	App.UI.DOM.appendNewElement("h3", App.Patch.Utils.current.div, `Verification`);
-	console.log(`Verification`);
-	// console.time(`Patching verification`);
-	App.Verify.Utils.verificationError = false;
-	try {
-		App.Verify.everything(App.Patch.Utils.current.div);
-	} catch (e) {
-		header.innerHTML += `<span class="error">Verification failed!</span>`;
-		console.error("Verification failed!");
-		App.Patch.Utils.current.div.append(App.UI.DOM.formatException(e));
-		// @ts-ignore
-		State.restore(); // restore the state to before patching
-		header.innerHTML += `<br><span>See below for details</span><hr>`;
-		// report the results
-		return f;
-	}
-	// console.timeEnd(`Patching verification`);
-
-	// Cleanup
-	// console.time(`Patching cleanup`);
-	App.UI.DOM.appendNewElement("h3", App.Patch.Utils.current.div, `Cleanup`);
-	console.log(`Cleanup`);
-	V.NaNArray = findNaN(); // reset NaNArray
-	App.UI.SlaveSummary.settingsChanged(); // let slave summary know that settings may have changed
-	// console.timeEnd(`Patching cleanup`);
-
-	// handle App.Verify.Utils.verificationError
-	if (App.Verify.Utils.verificationError) {
-		header.innerHTML += `<span class="error">Verification failed!</span>`;
-		console.error("Verification failed!");
-		// @ts-ignore
-		State.restore(); // restore the state to before patching
-	} else {
-		header.innerHTML += `<span class="green">Patches applied! You are on release ${V.releaseID}</span>`;
-		App.UI.DOM.appendNewElement("h3", f, "Patches Applied!");
-		console.log("Patches Applied!");
-	}
+	}), 0);
 
-	header.innerHTML += `<br><span>See below for details</span><hr>`;
-	// console.timeEnd('Patching All');
-	// report the results
-	return f;
+	// return the empty document
+	// the first setTimeout above will execute soon after the passage change happens
+	return frag;
 };
diff --git a/src/data/patches/patchUtils.js b/src/data/patches/patchUtils.js
index 5b81a8e9cac06a65ff7cdf2c3356d7aff5ced5d6..3fefb1a596084e2f2b5f56746a9e6284a3ad583f 100644
--- a/src/data/patches/patchUtils.js
+++ b/src/data/patches/patchUtils.js
@@ -41,7 +41,7 @@ App.Patch.Utils.patch = (patchType, patchUpTo, identifier, div=undefined, obj, e
 		console.log(`\\/ \\/ \\/ Patching of '${patchType}' started for '${identifier}'  \\/ \\/ \\/`);
 	}
 	const patchVersions = Object.keys(App.Patch.Patches).sort().filter((version) => {
-		return Number(version) <= patchUpTo;
+		return (Number(version) <= patchUpTo && Number(version) > obj.releaseID);
 	});
 
 	/** @type {number} */
@@ -375,3 +375,43 @@ App.Patch.Utils.current = {
  * @property {(div: HTMLDivElement, child: FC.ChildState) => FC.ChildState} [childState]
  * @property {(div: HTMLDivElement, order: FC.CustomSlaveOrder, location: "V.customSlave"|"V.huskSlave") => FC.CustomSlaveOrder} [customSlaveOrder]
  */
+
+// put on a loading screen
+// this was copied from endWeekAnim.js and modified
+App.Patch.Utils.loadingScreen = (function() {
+	let loadLockID = -1;
+	let infoDiv = null;
+
+	function makeInfoDiv() {
+		infoDiv = $(`
+			<div class="endweek-titleblock">
+				<div class="endweek-maintitle">Applying patches...</div>
+				<div class="endweek-subtitle" id="patch-message"></div>
+			</div>
+		`);
+	}
+
+	function start() {
+		if (loadLockID === -1) {
+			makeInfoDiv();
+			$("#init-screen").append(infoDiv);
+			loadLockID = LoadScreen.lock();
+		}
+	}
+
+	function end() {
+		if (loadLockID !== -1) {
+			setTimeout(() => {
+				LoadScreen.unlock(loadLockID);
+				infoDiv.remove();
+				infoDiv = null;
+				loadLockID = -1;
+			}, 0);
+		}
+	}
+
+	return {
+		start,
+		end
+	};
+})();
diff --git a/src/data/verification/verifyUtils.js b/src/data/verification/verifyUtils.js
index 5f3d138b1b7f3bb28c4cc1dd0912ab4533fd5fa9..c0d867715224a77d410ddc7ebb7562902bc12fdc 100644
--- a/src/data/verification/verifyUtils.js
+++ b/src/data/verification/verifyUtils.js
@@ -187,38 +187,41 @@ App.Verify.Utils.verify = (setKey, identifier, obj, extra, div) => {
 				return original;
 			}
 		}
-		// log any changes
-		const changes = App.Verify.Utils.changeSummary(original, obj, setKey, instructionID, identifier);
-		if (changes) {
-			changes.forEach((record) => {
-				if (record.path === "V.IDNumber") {
-					return;
-				}
-				let level = "error";
-				if (
-					record.description.includes(`'App.Verify.instructions.gameVariables.genePool'`) ||
-					record.type === "key"
-				) {
-					// The genePool verification is one of the (maybe the only) times that properties should actually be deleted/created
-					level = "orange";
-				}
-				if (["key", "type"].includes(record.type)) {
-					if (div) {
-						$(div).append(`<div><span class="${level}">${record.description}</span></div>`);
-					} else if (level === "error") {
-						console.error(record.description);
-					} else {
-						console.warn(record.description);
+
+		// report changes if V.logVerificationChanges is set to 1
+		if (getProp(V, "reportVerificationChanges", 0) === 1) {
+			const changes = App.Verify.Utils.changeSummary(original, obj, setKey, instructionID, identifier);
+			if (changes) {
+				changes.forEach((record) => {
+					if (record.path === "V.IDNumber") {
+						return;
+					}
+					let level = "error";
+					if (
+						record.description.includes(`'App.Verify.instructions.gameVariables.genePool'`) ||
+						record.type === "key"
+					) {
+						// The genePool verification is one of the (maybe the only) times that properties should actually be deleted/created
+						level = "orange";
 					}
-				} else {
-					if (div) {
-						$(div).append(`<div><span>${record.description}</span></div>`);
+					if (["key", "type"].includes(record.type)) {
+						if (div) {
+							$(div).append(`<div><span class="${level}">${record.description}</span></div>`);
+						} else if (level === "error") {
+							console.error(record.description);
+						} else {
+							console.warn(record.description);
+						}
 					} else {
-						console.warn(record.description);
-						console.warn("original val:", record.origVal, "new val:", record.newVal);
+						if (div) {
+							$(div).append(`<div><span>${record.description}</span></div>`);
+						} else {
+							console.warn(record.description);
+							console.warn("original val:", record.origVal, "new val:", record.newVal);
+						}
 					}
-				}
-			});
+				});
+			}
 		}
 	}
 	if (DEBUG) {
diff --git a/src/data/verification/zVerify.js b/src/data/verification/zVerify.js
index ac5e256927baad54ca9b8966d17c359c27e70db0..5972d8589c1e6095c2fdcb24c43eae3791b208c4 100644
--- a/src/data/verification/zVerify.js
+++ b/src/data/verification/zVerify.js
@@ -243,14 +243,67 @@ App.Verify.instructions = {
  * Runs verification for everything.
  * This is resource intensive. If you only need to verify a single data structure, use its verification function(s) instead.
  * @param {HTMLDivElement} [div] if provided then info may be appended to this div
+ * @param {Function} [callback] Called when verification finishes if provided
  */
-App.Verify.everything = (div) => {
-	App.Verify.slaveStates(div);
-	App.Verify.tankSlaveStates(div);
-	App.Verify.childStates(div);
-	App.Verify.infantStates(div);
-	App.Verify.fetuses(div);
-	App.Verify.playerState(V.PC, "V.PC", div);
-	App.Verify.customSlaveOrders(div);
-	App.Verify.gameVariables(div); // game variables should always be verified last
+App.Verify.everything = (div, callback) => {
+	// a list of verifications to run
+	const funcs = [
+		{func: App.Verify.slaveStates, params: [div], message: "Verifying SlaveStates..."},
+		{func: App.Verify.tankSlaveStates, params: [div], message: "Verifying TankSlaveStates..."},
+		{func: App.Verify.childStates, params: [div], message: "Verifying ChildStates..."},
+		{func: App.Verify.infantStates, params: [div], message: "Verifying InfantStates..."},
+		{func: App.Verify.fetuses, params: [div], message: `Verifying ${V.seePreg === 1 ? 'Fetuses' : 'FStates'}...`},
+		{func: App.Verify.playerState, params: [V.PC, "V.PC", div], message: "Verifying PlayerState..."},
+		{func: App.Verify.customSlaveOrders, params: [div], message: "Verifying CustomSlaveOrders..."},
+		{func: App.Verify.gameVariables, params: [div], message: "Verifying GameVariables (V)..."}, // game variables should always be verified last
+	].reverse();
+
+	// attempt to get the element, use a dummy if it doesn't exist
+	const message = document.getElementById("patch-message") ?? App.UI.DOM.makeElement("div");
+
+	const verify = () => {
+		// get the verification to run
+		const func = funcs.pop();
+		// run the verification
+		try {
+			func.func.apply(null, func.params);
+		} catch (e) {
+			if (div) {
+				// attempt to get the element, use a dummy if it doesn't exist
+				const header = document.getElementById("patch-header") ?? App.UI.DOM.makeElement("div");
+				header.innerHTML += `<span class="error">Verification failed!</span>`;
+				console.error("Verification failed!");
+				if (App.Patch.Utils.current.div) {
+					App.Patch.Utils.current.div.append(App.UI.DOM.formatException(e));
+				} else {
+					div.append(App.UI.DOM.formatException(e));
+				}
+				header.innerHTML += `<br><span>See below for details</span><hr>`;
+			} else {
+				console.error(e);
+				console.error("Verification failed!");
+			}
+			// @ts-ignore
+			State.restore(); // restore the state to before patching
+			// close the loading screen (if it is open) so that the user can see the error
+			App.Patch.Utils.loadingScreen.end();
+			return;
+		}
+
+		// check if there are more to run
+		if (funcs.length !== 0) {
+			// set the message the player sees
+			message.innerHTML = funcs.last().message;
+			// wait for the DOM to update then call verify
+			setTimeout(verify, 0);
+		} else if (callback) {
+			callback();
+		}
+	};
+	if (funcs.length !== 0) {
+		// set the message the player sees
+		message.innerHTML = funcs.last().message;
+		// wait for the DOM to update then call verify
+		setTimeout(verify, 0);
+	}
 };
diff --git a/src/gui/options/options.js b/src/gui/options/options.js
index 62142773afdfe313de22cf4d97fb5d5eef0da5d6..8e8d310e0bb1e61a3e873c0364fee0ad8e06e876 100644
--- a/src/gui/options/options.js
+++ b/src/gui/options/options.js
@@ -517,6 +517,10 @@ App.UI.optionsPassage = function() {
 				.addValue("Manual", 1).on().addValue("Automatic", 0).off();
 		}
 
+		options.addOption("Reporting changes caused by verification is", "reportVerificationChanges")
+			.addValue("Enabled", 1).on().addValue("Disabled", 0).off()
+			.addComment("This will report most changes caused by the verification system. While helpful it does make verification (and patching by extension) slower");
+
 		option = options.addCustomOption("Genetics array");
 		if (V.cheatMode === 1) {
 			option.addButton("Edit Genetics", () => { }, "Edit Genetics");
diff --git a/src/js/utilsDOM.js b/src/js/utilsDOM.js
index 63e33b3d7d351747da37548e9ca86b9287544cd1..98a7c3de853cc9719cef87a16666f317f9f1e412 100644
--- a/src/js/utilsDOM.js
+++ b/src/js/utilsDOM.js
@@ -527,9 +527,11 @@ App.UI.DOM.formatException = function formatException(ex, recursion = false) {
 
 		const header = document.createElement("p");
 		header.classList.add("error");
+		App.UI.DOM.appendNewElement("div", header, `Please provide a text copy (or screenshot) of this error message starting with \`\`\` and ending with 'End of error message' below, a save file, and any other relevant information.`);
+		fragment.append(App.UI.DOM.makeElement("br"));
 		App.UI.DOM.appendNewElement("div", header, `\`\`\``);
+		fragment.append(App.UI.DOM.makeElement("br"));
 		App.UI.DOM.appendNewElement("div", header, `Apologies! An error has occurred. Please report this.`, ["bold"]);
-		App.UI.DOM.appendNewElement("div", header, `Please provide a screenshot of the error message, a save file and any other relevant information.`);
 		fragment.append(header);
 
 		const eventLabel = () => {
@@ -564,7 +566,29 @@ App.UI.DOM.formatException = function formatException(ex, recursion = false) {
 			App.UI.DOM.appendNewElement("div", error, ex.toString(), ["bold"]);
 		}
 
-		fragment.append(error, `\`\`\``);
+		fragment.append(error);
+
+		fragment.append(App.UI.DOM.makeElement("br"));
+
+		fragment.append(`\`\`\``);
+
+		fragment.append(App.UI.DOM.makeElement("br"));
+
+		fragment.append(App.UI.DOM.makeElement("div", `End of error message`));
+
+		fragment.append(App.UI.DOM.makeElement("br"));
+
+		const reportDiv = App.UI.DOM.makeElement("div", `Please report this error `);
+
+		const reportLink = App.UI.DOM.makeElement("a", "here");
+
+		reportLink.classList.add("link-external");
+		reportLink.href = "https://gitgud.io/pregmodfan/fc-pregmod/issues/";
+		reportLink.target = "_blank";
+
+		reportDiv.append(reportLink);
+
+		fragment.append(reportDiv);
 
 		fragment.append(App.UI.DOM.makeElement("br"));