diff --git a/baystation12.dme b/baystation12.dme
index 14b6b5dae8..e0cbae0994 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -32,9 +32,11 @@
#include "code\__defines\research.dm"
#include "code\__defines\species_languages.dm"
#include "code\__defines\turfs.dm"
+#include "code\_helpers\areas.dm"
#include "code\_helpers\atmospherics.dm"
#include "code\_helpers\datum_pool.dm"
#include "code\_helpers\files.dm"
+#include "code\_helpers\functional.dm"
#include "code\_helpers\game.dm"
#include "code\_helpers\global_lists.dm"
#include "code\_helpers\icons.dm"
diff --git a/code/_helpers/areas.dm b/code/_helpers/areas.dm
new file mode 100644
index 0000000000..b5987c0ce1
--- /dev/null
+++ b/code/_helpers/areas.dm
@@ -0,0 +1,21 @@
+//Takes: Area type as text string or as typepath OR an instance of the area.
+//Returns: A list of all turfs in areas of that type in the world.
+/proc/get_area_turfs(var/areatype, var/list/predicates)
+ if(!areatype) return null
+ if(istext(areatype)) areatype = text2path(areatype)
+ if(isarea(areatype))
+ var/area/areatemp = areatype
+ areatype = areatemp.type
+
+ var/list/turfs = new/list()
+ for(var/areapath in typesof(areatype))
+ var/area/A = locate(areapath)
+ for(var/turf/T in A.contents)
+ if(!predicates || all_predicates_true(T, predicates))
+ turfs += T
+ return turfs
+
+/proc/pick_area_turf(var/areatype, var/list/predicates)
+ var/list/turfs = get_area_turfs(areatype, predicates)
+ if(turfs && turfs.len)
+ return pick(turfs)
diff --git a/code/_helpers/functional.dm b/code/_helpers/functional.dm
new file mode 100644
index 0000000000..c5ecfb631a
--- /dev/null
+++ b/code/_helpers/functional.dm
@@ -0,0 +1,14 @@
+/proc/all_predicates_true(var/input, var/list/predicates)
+ for(var/i = 1 to predicates.len)
+ if(!call(predicates[i])(input))
+ return FALSE
+ return TRUE
+
+/proc/any_predicate_true(var/input, var/list/predicates)
+ if(!predicates.len)
+ return TRUE
+
+ for(var/i = 1 to predicates.len)
+ if(call(predicates[i])(input))
+ return TRUE
+ return FALSE
diff --git a/code/_helpers/turfs.dm b/code/_helpers/turfs.dm
index 8583628114..ca5c60be9d 100644
--- a/code/_helpers/turfs.dm
+++ b/code/_helpers/turfs.dm
@@ -32,3 +32,12 @@
if(!available_turfs.len)
available_turfs = start_turfs
return pick(available_turfs)
+
+/proc/turf_contains_dense_objects(var/turf/T)
+ return T.contains_dense_objects()
+
+/proc/not_turf_contains_dense_objects(var/turf/T)
+ return !turf_contains_dense_objects(T)
+
+/proc/is_station_turf(var/turf/T)
+ return T && isStationLevel(T.z)
diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm
index fb79b29cb9..61193e2754 100644
--- a/code/_helpers/unsorted.dm
+++ b/code/_helpers/unsorted.dm
@@ -229,14 +229,14 @@ Turf and target are seperate in case you want to teleport some distance from a t
#define LOCATE_COORDS(X, Y, Z) locate(between(1, X, world.maxx), between(1, Y, world.maxy), Z)
/proc/getcircle(turf/center, var/radius) //Uses a fast Bresenham rasterization algorithm to return the turfs in a thin circle.
if(!radius) return list(center)
-
+
var/x = 0
var/y = radius
var/p = 3 - 2 * radius
-
+
. = list()
while(y >= x) // only formulate 1/8 of circle
-
+
. += LOCATE_COORDS(center.x - x, center.y - y, center.z) //upper left left
. += LOCATE_COORDS(center.x - y, center.y - x, center.z) //upper upper left
. += LOCATE_COORDS(center.x + y, center.y - x, center.z) //upper upper right
@@ -247,7 +247,7 @@ Turf and target are seperate in case you want to teleport some distance from a t
. += LOCATE_COORDS(center.x + x, center.y + y, center.z) //lower right right
if(p < 0)
- p += 4*x++ + 6;
+ p += 4*x++ + 6;
else
p += 4*(x++ - y--) + 10;
@@ -718,21 +718,6 @@ proc/GaussRandRound(var/sigma,var/roundto)
if(istype(N, areatype)) areas += N
return areas
-//Takes: Area type as text string or as typepath OR an instance of the area.
-//Returns: A list of all turfs in areas of that type of that type in the world.
-/proc/get_area_turfs(var/areatype)
- if(!areatype) return null
- if(istext(areatype)) areatype = text2path(areatype)
- if(isarea(areatype))
- var/area/areatemp = areatype
- areatype = areatemp.type
-
- var/list/turfs = new/list()
- for(var/area/N in world)
- if(istype(N, areatype))
- for(var/turf/T in N) turfs += T
- return turfs
-
//Takes: Area type as text string or as typepath OR an instance of the area.
//Returns: A list of all atoms (objs, turfs, mobs) in areas of that type of that type in the world.
/proc/get_area_all_atoms(var/areatype)
diff --git a/code/game/area/Space Station 13 areas.dm b/code/game/area/Space Station 13 areas.dm
index 7cd0a708d8..87e54a9ada 100755
--- a/code/game/area/Space Station 13 areas.dm
+++ b/code/game/area/Space Station 13 areas.dm
@@ -61,8 +61,8 @@ var/list/teleportlocs = list()
for(var/area/AR in world)
if(istype(AR, /area/shuttle) || istype(AR, /area/syndicate_station) || istype(AR, /area/wizard_station)) continue
if(teleportlocs.Find(AR.name)) continue
- var/turf/picked = pick(get_area_turfs(AR.type))
- if (picked.z in config.station_levels)
+ var/turf/picked = pick_area_turf(AR.type, list(/proc/is_station_turf))
+ if (picked)
teleportlocs += AR.name
teleportlocs[AR.name] = AR
@@ -78,8 +78,8 @@ var/list/ghostteleportlocs = list()
if(istype(AR, /area/turret_protected/aisat) || istype(AR, /area/derelict) || istype(AR, /area/tdome) || istype(AR, /area/shuttle/specops/centcom))
ghostteleportlocs += AR.name
ghostteleportlocs[AR.name] = AR
- var/turf/picked = pick(get_area_turfs(AR.type))
- if (picked.z in config.player_levels)
+ var/turf/picked = pick_area_turf(AR.type, list(/proc/is_station_turf))
+ if (picked)
ghostteleportlocs += AR.name
ghostteleportlocs[AR.name] = AR
diff --git a/code/game/machinery/nuclear_bomb.dm b/code/game/machinery/nuclear_bomb.dm
index d02d3522a4..76710309ee 100644
--- a/code/game/machinery/nuclear_bomb.dm
+++ b/code/game/machinery/nuclear_bomb.dm
@@ -418,10 +418,14 @@ obj/machinery/nuclearbomb/proc/nukehack_win(mob/user as mob)
nuke_disks |= src
/obj/item/weapon/disk/nuclear/Destroy()
- if(!nuke_disks.len && blobstart.len > 0)
- var/obj/D = new /obj/item/weapon/disk/nuclear(pick(blobstart))
- message_admins("[src], the last authentication disk, has been destroyed. Spawning [D] at ([D.x], [D.y], [D.z]).")
- log_game("[src], the last authentication disk, has been destroyed. Spawning [D] at ([D.x], [D.y], [D.z]).")
+ nuke_disks -= src
+ if(!nuke_disks.len)
+ var/turf/T = pick_area_turf(/area/maintenance, /proc/not_turf_contains_dense_objects)
+ if(T)
+ var/obj/D = new /obj/item/weapon/disk/nuclear(T)
+ log_and_message_admins_with_location("[src], the last authentication disk, has been destroyed. Spawning [D] at ([D.x], [D.y], [D.z]).", T.x, T.y, T.z)
+ else
+ log_and_message_admins("[src], the last authentication disk, has been destroyed. Failed to respawn disc!")
..()
/obj/item/weapon/disk/nuclear/touch_map_edge()
diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm
index 9dfe1c3b19..ed518a6f53 100644
--- a/code/game/objects/effects/landmarks.dm
+++ b/code/game/objects/effects/landmarks.dm
@@ -52,10 +52,6 @@
prisonsecuritywarp += loc
qdel(src)
return
- if("blobstart")
- blobstart += loc
- qdel(src)
- return
if("xeno_spawn")
xeno_spawn += loc
qdel(src)
diff --git a/code/global.dm b/code/global.dm
index a460518726..ea2be0b044 100644
--- a/code/global.dm
+++ b/code/global.dm
@@ -71,7 +71,6 @@ var/list/tdomeobserve = list()
var/list/tdomeadmin = list()
var/list/prisonsecuritywarp = list() // Prison security goes to these.
var/list/prisonwarped = list() // List of players already warped.
-var/list/blobstart = list()
var/list/ninjastart = list()
var/list/cardinal = list(NORTH, SOUTH, EAST, WEST)
diff --git a/code/modules/admin/admin_attack_log.dm b/code/modules/admin/admin_attack_log.dm
index 967a4375da..918739ec8f 100644
--- a/code/modules/admin/admin_attack_log.dm
+++ b/code/modules/admin/admin_attack_log.dm
@@ -1,6 +1,9 @@
/mob/var/lastattacker = null
/mob/var/lastattacked = null
-/mob/var/attack_log = list( )
+/mob/var/attack_log = list()
+
+proc/log_and_message_admins_with_location(var/message, var/x, var/y, var/z, var/mob/user = usr)
+ log_and_message_admins("[message] (JMP)", user)
proc/log_and_message_admins(var/message as text, var/mob/user = usr)
log_admin(user ? "[key_name(user)] [message]" : "EVENT [message]")
diff --git a/code/modules/events/blob.dm b/code/modules/events/blob.dm
index bc1888f14d..7ca66a8260 100644
--- a/code/modules/events/blob.dm
+++ b/code/modules/events/blob.dm
@@ -1,6 +1,5 @@
/datum/event/blob
announceWhen = 12
- endWhen = 120
var/obj/effect/blob/core/Blob
@@ -8,10 +7,13 @@
level_seven_announcement()
/datum/event/blob/start()
- var/turf/T = pick(blobstart)
+ var/turf/T = pick_area_turf(/area/maintenance, list(/proc/is_station_turf, /proc/not_turf_contains_dense_objects))
if(!T)
+ log_and_message_admins("Blob failed to find a viable turf.")
kill()
return
+
+ log_and_message_admins_with_location("Event: Blob spawned at \the [get_area(T)] ([T.x],[T.y],[T.z])", T.x, T.y, T.z)
Blob = new /obj/effect/blob/core(T)
for(var/i = 1; i < rand(3, 4), i++)
Blob.process()
diff --git a/code/modules/hydroponics/spreading/spreading.dm b/code/modules/hydroponics/spreading/spreading.dm
index e8f2cfa918..d7f5aef71a 100644
--- a/code/modules/hydroponics/spreading/spreading.dm
+++ b/code/modules/hydroponics/spreading/spreading.dm
@@ -3,15 +3,8 @@
/proc/spacevine_infestation(var/potency_min=70, var/potency_max=100, var/maturation_min=5, var/maturation_max=15)
spawn() //to stop the secrets panel hanging
- var/list/turf/simulated/floor/turfs = list() //list of all the empty floor turfs in the hallway areas
- for(var/areapath in typesof(/area/hallway))
- var/area/A = locate(areapath)
- for(var/turf/simulated/floor/F in A.contents)
- if(turf_clear(F))
- turfs += F
-
- if(turfs.len) //Pick a turf to spawn at if we can
- var/turf/simulated/floor/T = pick(turfs)
+ var/turf/T = pick_area_turf(/area/hallway, list(/proc/is_station_turf, /proc/not_turf_contains_dense_objects))
+ if(T)
var/datum/seed/seed = plant_controller.create_random_seed(1)
seed.set_trait(TRAIT_SPREAD,2) // So it will function properly as vines.
seed.set_trait(TRAIT_POTENCY,rand(potency_min, potency_max)) // 70-100 potency will help guarantee a wide spread and powerful effects.
@@ -23,9 +16,9 @@
vine.mature_time = 0
vine.process()
- message_admins("Event: Spacevines spawned at [T.loc] ([T.x],[T.y],[T.z])")
+ log_and_message_admins_with_location("Event: Spacevines spawned at [T.loc] ([T.x],[T.y],[T.z])", T.x, T.y, T.z)
return
- message_admins("Event: Spacevines failed to find a viable turf.")
+ log_and_message_admins("Event: Spacevines failed to find a viable turf.")
/obj/effect/dead_plant
anchored = 1