diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 41ed12bcc6d..bde0c1db5c8 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -442,16 +442,12 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
//find a viable mouse candidate
var/mob/living/simple_animal/mouse/host
- var/obj/machinery/atmospherics/unary/vent_pump/vent_found
- var/list/found_vents = list()
- for(var/obj/machinery/atmospherics/unary/vent_pump/v in machines)
- if(!v.welded && v.z == T.z)
- found_vents.Add(v)
- if(found_vents.len)
- vent_found = pick(found_vents)
- host = new /mob/living/simple_animal/mouse(vent_found.loc)
+ var/obj/machinery/atmospherics/unary/vent_pump/spawnpoint = find_mouse_spawnpoint(T.z)
+
+ if (spawnpoint)
+ host = new /mob/living/simple_animal/mouse(spawnpoint.loc)
else
- src << "Unable to find any unwelded vents to spawn mice at."
+ src << "Unable to find any safe, unwelded vents to spawn mice at. The station must be quite a mess! Trying again might work, if you think there's still a safe place. "
if(host)
if(config.uneducated_mice)
@@ -460,6 +456,100 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
host.ckey = src.ckey
host << "You are now a mouse. Try to avoid interaction with players, and do not give hints away that you are more than a simple rodent."
+/proc/find_mouse_spawnpoint(var/ZLevel)
+ //This function will attempt to find a good spawnpoint for mice, and prevent them from spawning in closed vent systems with no escape
+ //It does this by bruteforce: Picks a random vent, tests if it has enough connections, if not, repeat
+ //Continues either until a valid one is found (in which case we return it), or until we hit a limit on attempts..
+ //If we hit the limit without finding a valid one, then the best one we found is selected
+
+ var/list/found_vents = list()
+ for(var/obj/machinery/atmospherics/unary/vent_pump/v in machines)
+ if(!v.welded && v.z == ZLevel)
+ found_vents.Add(v)
+
+ if (found_vents.len == 0)
+ return null//Every vent on the map is welded? Sucks to be a mouse
+
+ var/attempts = 0
+ var/max_attempts = min(20, found_vents.len)
+ var/target_connections = 30//Any vent with at least this many connections is good enough
+
+ var/obj/machinery/atmospherics/unary/vent_pump/bestvent = null
+ var/best_connections = 0
+ while (attempts < max_attempts)
+ attempts++
+ var/obj/machinery/atmospherics/unary/vent_pump/testvent = pick(found_vents)
+
+ if (!testvent.network)//this prevents runtime errors
+ continue
+
+ var/turf/T = get_turf(testvent)
+
+
+
+ //We test the environment of the tile, to see if its habitable for a mouse
+ //-----------------------------------
+ var/atmos_suitable = 1
+
+ var/maxtemp = 390
+ var/mintemp = 210
+ var/min_oxy = 5
+ var/max_phoron = 1
+ var/max_co2 = 5
+ var/min_pressure = 80
+
+ var/datum/gas_mixture/Environment = T.return_air()
+ if(Environment)
+
+ if(Environment.temperature > maxtemp)
+ atmos_suitable = 0
+ else if (Environment.temperature < mintemp)
+ atmos_suitable = 0
+ else if(Environment.gas["oxygen"] < min_oxy)
+ atmos_suitable = 0
+ else if(Environment.gas["phoron"] > max_phoron)
+ atmos_suitable = 0
+ else if(Environment.gas["carbon_dioxide"] > max_co2)
+ atmos_suitable = 0
+ else if(Environment.return_pressure() < min_pressure)
+ atmos_suitable = 0
+ else
+ atmos_suitable = 0
+
+ if (!atmos_suitable)
+ continue
+ //----------------------
+
+
+
+
+ //Now we test the vent connections, and ensure the vent we spawn at is connected enough to give the mouse free movement
+ var/list/connections = list()
+ for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in testvent.network.normal_members)
+ if(temp_vent.welded)
+ continue
+ if(temp_vent == testvent)//Our testvent shouldn't count itself as a connection
+ continue
+
+ connections += temp_vent
+
+ if(connections.len > best_connections)
+ best_connections = connections.len
+ bestvent = testvent
+
+ if (connections.len >= target_connections)
+ return testvent
+ //If we've found one that's good enough, then we stop looking
+
+
+ //IF we get here, then we hit the limit without finding a valid one.
+ //This would probably only be likely to happen if the station is full of holes and pipes are broken everywhere
+ if (bestvent == null)
+ //If bestvent is null, then every vent we checked was either welded or unsafe to spawn at. The user will be given a message reflecting this.
+ return null
+ else
+ return bestvent
+
/mob/dead/observer/verb/view_manfiest()
set name = "View Crew Manifest"
set category = "Ghost"
diff --git a/html/changelogs/Nanako-MouseSpawning.yml b/html/changelogs/Nanako-MouseSpawning.yml
new file mode 100644
index 00000000000..fc4ab6e906a
--- /dev/null
+++ b/html/changelogs/Nanako-MouseSpawning.yml
@@ -0,0 +1,37 @@
+################################
+# Example Changelog File
+#
+# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
+#
+# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
+# When it is, any changes listed below will disappear.
+#
+# Valid Prefixes:
+# bugfix
+# wip (For works in progress)
+# tweak
+# soundadd
+# sounddel
+# rscadd (general adding of nice things)
+# rscdel (general deleting of nice things)
+# imageadd
+# imagedel
+# maptweak
+# spellcheck (typo fixes)
+# experiment
+#################################
+
+# Your name.
+author: Nanako
+
+# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
+delete-after: True
+
+# Any changes you've made. See valid prefix list above.
+# INDENT WITH TWO SPACES. NOT TABS. SPACES.
+# SCREW THIS UP AND IT WON'T WORK.
+# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries.
+# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog.
+changes:
+ - bugfix: "Mice will no longer spawn in closed systems with nowhere to ventcrawl to"
+ - bugfix: "Mice can no longer spawn in breached areas and die immediately. A spawnpoint with a safe environment will always be chosen"