diff --git a/code/__defines/color.dm b/code/__defines/color.dm
index ba9efe20de..d439958d76 100644
--- a/code/__defines/color.dm
+++ b/code/__defines/color.dm
@@ -14,4 +14,6 @@
#define COLOR_ASSEMBLY_LBLUE "#5D99BE"
#define COLOR_ASSEMBLY_BLUE "#38559E"
#define COLOR_ASSEMBLY_PURPLE "#6F6192"
-#define COLOR_ASSEMBLY_HOT_PINK "#FF69B4"
\ No newline at end of file
+#define COLOR_ASSEMBLY_HOT_PINK "#FF69B4"
+
+#define COLOR_ASTEROID_ROCK "#735555"
diff --git a/code/__defines/overmap.dm b/code/__defines/overmap.dm
new file mode 100644
index 0000000000..503675b44e
--- /dev/null
+++ b/code/__defines/overmap.dm
@@ -0,0 +1,16 @@
+ //How far from the edge of overmap zlevel could randomly placed objects spawn
+#define OVERMAP_EDGE 2
+
+#define SHIP_SIZE_TINY 1
+#define SHIP_SIZE_SMALL 2
+#define SHIP_SIZE_LARGE 3
+
+//multipliers for max_speed to find 'slow' and 'fast' speeds for the ship
+#define SHIP_SPEED_SLOW 1/(40 SECONDS)
+#define SHIP_SPEED_FAST 3/(20 SECONDS)// 15 speed
+
+#define OVERMAP_WEAKNESS_NONE 0
+#define OVERMAP_WEAKNESS_FIRE 1
+#define OVERMAP_WEAKNESS_EMP 2
+#define OVERMAP_WEAKNESS_MINING 4
+#define OVERMAP_WEAKNESS_EXPLOSIVE 8
diff --git a/code/_helpers/game.dm b/code/_helpers/game.dm
index 6b924ef082..27c5231145 100644
--- a/code/_helpers/game.dm
+++ b/code/_helpers/game.dm
@@ -26,6 +26,15 @@
max_z = max(z, max_z)
return max_z
+/proc/living_observers_present(var/list/zlevels)
+ if(LAZYLEN(zlevels))
+ for(var/mob/M in player_list) //if a tree ticks on the empty zlevel does it really tick
+ if(M.stat != DEAD) //(no it doesn't)
+ var/turf/T = get_turf(M)
+ if(T && (T.z in zlevels))
+ return TRUE
+ return FALSE
+
/proc/get_area(atom/A)
if(isarea(A))
return A
diff --git a/code/_helpers/turfs.dm b/code/_helpers/turfs.dm
index e8ca671c11..a9cede52de 100644
--- a/code/_helpers/turfs.dm
+++ b/code/_helpers/turfs.dm
@@ -33,6 +33,20 @@
available_turfs = start_turfs
return pick(available_turfs)
+// Picks a turf that is clearance tiles away from the map edge given by dir, on z-level Z
+/proc/pick_random_edge_turf(var/dir, var/Z, var/clearance = TRANSITIONEDGE + 1)
+ if(!dir)
+ return
+ switch(dir)
+ if(NORTH)
+ return locate(rand(clearance, world.maxx - clearance), world.maxy - clearance, Z)
+ if(SOUTH)
+ return locate(rand(clearance, world.maxx - clearance), clearance, Z)
+ if(EAST)
+ return locate(world.maxx - clearance, rand(clearance, world.maxy - clearance), Z)
+ if(WEST)
+ return locate(clearance, rand(clearance, world.maxy - clearance), Z)
+
/proc/is_below_sound_pressure(var/turf/T)
var/datum/gas_mixture/environment = T ? T.return_air() : null
var/pressure = environment ? environment.return_pressure() : 0
diff --git a/code/controllers/subsystems/events.dm b/code/controllers/subsystems/events.dm
index 8b8bc1d8b0..6de40f464d 100644
--- a/code/controllers/subsystems/events.dm
+++ b/code/controllers/subsystems/events.dm
@@ -1,6 +1,8 @@
SUBSYSTEM_DEF(events)
name = "Events"
- wait = 20
+ wait = 2 SECONDS
+
+ var/tmp/list/currentrun = null
var/list/datum/event/active_events = list()
var/list/datum/event/finished_events = list()
@@ -11,23 +13,37 @@ SUBSYSTEM_DEF(events)
var/datum/event_meta/new_event = new
/datum/controller/subsystem/events/Initialize()
+ allEvents = typesof(/datum/event) - /datum/event
event_containers = list(
EVENT_LEVEL_MUNDANE = new/datum/event_container/mundane,
EVENT_LEVEL_MODERATE = new/datum/event_container/moderate,
EVENT_LEVEL_MAJOR = new/datum/event_container/major
)
- allEvents = typesof(/datum/event) - /datum/event
+ if(global.using_map.use_overmap)
+ GLOB.overmap_event_handler.create_events(global.using_map.overmap_z, global.using_map.overmap_size, global.using_map.overmap_event_areas)
return ..()
/datum/controller/subsystem/events/fire(resumed)
- for(var/datum/event/E in active_events)
+ if (!resumed)
+ src.currentrun = active_events.Copy()
+
+ //cache for sanic speed (lists are references anyways)
+ var/list/currentrun = src.currentrun
+ while (currentrun.len)
+ var/datum/event/E = currentrun[currentrun.len]
+ currentrun.len--
if(E.processing_active)
E.process()
+ if (MC_TICK_CHECK)
+ return
for(var/i = EVENT_LEVEL_MUNDANE to EVENT_LEVEL_MAJOR)
var/list/datum/event_container/EC = event_containers[i]
EC.process()
+/datum/controller/subsystem/events/stat_entry()
+ ..("E:[active_events.len]")
+
/datum/controller/subsystem/events/Recover()
if(SSevents.active_events)
active_events |= SSevents.active_events
@@ -35,6 +51,8 @@ SUBSYSTEM_DEF(events)
finished_events |= SSevents.finished_events
/datum/controller/subsystem/events/proc/event_complete(var/datum/event/E)
+ active_events -= E
+
if(!E.event_meta || !E.severity) // datum/event is used here and there for random reasons, maintaining "backwards compatibility"
log_debug("Event of '[E.type]' with missing meta-data has completed.")
return
@@ -50,7 +68,7 @@ SUBSYSTEM_DEF(events)
log_debug("Event '[EM.name]' has completed at [stationtime2text()].")
/datum/controller/subsystem/events/proc/delay_events(var/severity, var/delay)
- var/list/datum/event_container/EC = event_containers[severity]
+ var/datum/event_container/EC = event_containers[severity]
EC.next_event_time += delay
/datum/controller/subsystem/events/proc/RoundEnd()
diff --git a/code/controllers/subsystems/skybox.dm b/code/controllers/subsystems/skybox.dm
index 59e841ee52..aadc086b08 100644
--- a/code/controllers/subsystems/skybox.dm
+++ b/code/controllers/subsystems/skybox.dm
@@ -81,10 +81,10 @@ SUBSYSTEM_DEF(skybox)
overmap.appearance_flags = RESET_COLOR
res.overlays += overmap
- // TODO - Allow events to apply custom overlays to skybox! (Awesome!)
- //for(var/datum/event/E in SSevent.active_events)
- // if(E.has_skybox_image && E.isRunning && (z in E.affecting_z))
- // res.overlays += E.get_skybox_image()
+ // Allow events to apply custom overlays to skybox! (Awesome!)
+ for(var/datum/event/E in SSevents.active_events)
+ if(E.has_skybox_image && E.isRunning && (z in E.affecting_z))
+ res.overlays += E.get_skybox_image()
return res
diff --git a/code/game/gamemodes/events/dust.dm b/code/game/gamemodes/events/dust.dm
index f41e040ee5..400ae1058d 100644
--- a/code/game/gamemodes/events/dust.dm
+++ b/code/game/gamemodes/events/dust.dm
@@ -7,27 +7,57 @@ No command report on the common version of this event.
The "dust" will damage the hull of the station causin minor hull breaches.
*/
-/proc/dust_swarm(var/strength = "weak")
+/proc/dust_swarm(var/strength = "weak", var/list/affecting_z)
var/numbers = 1
+ var/dust_type = /obj/effect/space_dust
switch(strength)
if("weak")
- numbers = rand(2,4)
- for(var/i = 0 to numbers)
- new/obj/effect/space_dust/weak()
+ numbers = rand(2,4)
+ dust_type = /obj/effect/space_dust/weak
if("norm")
- numbers = rand(5,10)
- for(var/i = 0 to numbers)
- new/obj/effect/space_dust()
+ numbers = rand(5,10)
+ dust_type = /obj/effect/space_dust
if("strong")
- numbers = rand(10,15)
- for(var/i = 0 to numbers)
- new/obj/effect/space_dust/strong()
+ numbers = rand(10,15)
+ dust_type = /obj/effect/space_dust/strong
if("super")
- numbers = rand(15,25)
- for(var/i = 0 to numbers)
- new/obj/effect/space_dust/super()
- return
+ numbers = rand(15,25)
+ dust_type = /obj/effect/space_dust/super
+ var/startside = pick(cardinal)
+ for(var/i = 0 to numbers)
+ var/startx = 0
+ var/starty = 0
+ var/endy = 0
+ var/endx = 0
+ switch(startside)
+ if(NORTH)
+ starty = world.maxy-TRANSITIONEDGE-1
+ startx = rand(TRANSITIONEDGE+1, world.maxx-TRANSITIONEDGE-1)
+ endy = TRANSITIONEDGE
+ endx = rand(TRANSITIONEDGE+1, world.maxx-TRANSITIONEDGE-1)
+ if(EAST)
+ starty = rand(TRANSITIONEDGE+1, world.maxy-TRANSITIONEDGE-1)
+ startx = world.maxx-TRANSITIONEDGE-1
+ endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE)
+ endx = TRANSITIONEDGE
+ if(SOUTH)
+ starty = TRANSITIONEDGE+1
+ startx = rand(TRANSITIONEDGE+1, world.maxx-TRANSITIONEDGE-1)
+ endy = world.maxy-TRANSITIONEDGE
+ endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE)
+ if(WEST)
+ starty = rand(TRANSITIONEDGE+1, world.maxy-TRANSITIONEDGE-1)
+ startx = TRANSITIONEDGE+1
+ endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE)
+ endx = world.maxx-TRANSITIONEDGE
+
+ var/randomz = pick(affecting_z)
+ var/turf/startloc = locate(startx, starty, randomz)
+ var/turf/endloc = locate(endx, endy, randomz)
+ var/obj/effect/space_dust/D = new dust_type(startloc)
+ D.set_dir(GLOB.reverse_dir[startside])
+ walk_towards(D, endloc, 1)
/obj/effect/space_dust
name = "Space Dust"
@@ -39,96 +69,51 @@ The "dust" will damage the hull of the station causin minor hull breaches.
var/strength = 2 //ex_act severity number
var/life = 2 //how many things we hit before qdel(src)
- weak
- strength = 3
- life = 1
+/obj/effect/space_dust/weak
+ strength = 3
+ life = 1
- strong
- strength = 1
- life = 6
+/obj/effect/space_dust/strong
+ strength = 1
+ life = 6
- super
- strength = 1
- life = 40
+/obj/effect/space_dust/super
+ strength = 1
+ life = 40
+
+/obj/effect/space_dust/Destroy()
+ walk(src, 0) // Because we might have called walk_towards, we must stop the walk loop or BYOND keeps an internal reference to us forever.
+ return ..()
+
+/obj/effect/space_dust/touch_map_edge()
+ qdel(src)
+
+/obj/effect/space_dust/Bump(atom/A)
+ spawn(0)
+ if(prob(50))
+ for(var/mob/M in range(10, src))
+ if(!M.stat && !istype(M, /mob/living/silicon/ai))
+ shake_camera(M, 3, 1)
+ if (A)
+ playsound(src.loc, 'sound/effects/meteorimpact.ogg', 40, 1)
+
+ if(ismob(A))
+ A.ex_act(strength)//This should work for now I guess
+ else if(!istype(A,/obj/machinery/power/emitter) && !istype(A,/obj/machinery/field_generator)) //Protect the singularity from getting released every round!
+ A.ex_act(strength) //Changing emitter/field gen ex_act would make it immune to bombs and C4
+
+ life--
+ if(life <= 0)
+ walk(src,0)
+ qdel(src)
+ return 0
+ return
- New()
- ..()
- var/startx = 0
- var/starty = 0
- var/endy = 0
- var/endx = 0
- var/startside = pick(cardinal)
+/obj/effect/space_dust/Bumped(atom/A)
+ Bump(A)
+ return
- switch(startside)
- if(NORTH)
- starty = world.maxy-(TRANSITIONEDGE+1)
- startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1))
- endy = TRANSITIONEDGE
- endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE)
- if(EAST)
- starty = rand((TRANSITIONEDGE+1),world.maxy-(TRANSITIONEDGE+1))
- startx = world.maxx-(TRANSITIONEDGE+1)
- endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE)
- endx = TRANSITIONEDGE
- if(SOUTH)
- starty = (TRANSITIONEDGE+1)
- startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1))
- endy = world.maxy-TRANSITIONEDGE
- endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE)
- if(WEST)
- starty = rand((TRANSITIONEDGE+1), world.maxy-(TRANSITIONEDGE+1))
- startx = (TRANSITIONEDGE+1)
- endy = rand(TRANSITIONEDGE,world.maxy-TRANSITIONEDGE)
- endx = world.maxx-TRANSITIONEDGE
- //VOREStation Edit - No space dust outside of space
- var/list/z_levels = using_map.station_levels.Copy()
- for(var/datum/planet/P in SSplanets.planets)
- z_levels.Remove(P.expected_z_levels)
- var/z_level = pick(z_levels)
- //VOREStation Edit End
- var/goal = locate(endx, endy, z_level)
- src.x = startx
- src.y = starty
- src.z = z_level
- spawn(0)
- walk_towards(src, goal, 1)
- return
-
- Destroy()
- walk(src, 0) // Because we might have called walk_towards, we must stop the walk loop or BYOND keeps an internal reference to us forever.
- return ..()
-
- touch_map_edge()
- qdel(src)
-
- Bump(atom/A)
- spawn(0)
- if(prob(50))
- for(var/mob/M in range(10, src))
- if(!M.stat && !istype(M, /mob/living/silicon/ai))
- shake_camera(M, 3, 1)
- if (A)
- playsound(src.loc, 'sound/effects/meteorimpact.ogg', 40, 1)
-
- if(ismob(A))
- A.ex_act(strength)//This should work for now I guess
- else if(!istype(A,/obj/machinery/power/emitter) && !istype(A,/obj/machinery/field_generator)) //Protect the singularity from getting released every round!
- A.ex_act(strength) //Changing emitter/field gen ex_act would make it immune to bombs and C4
-
- life--
- if(life <= 0)
- walk(src,0)
- qdel(src)
- return 0
- return
-
-
- Bumped(atom/A)
- Bump(A)
- return
-
-
- ex_act(severity)
- qdel(src)
- return
+/obj/effect/space_dust/ex_act(severity)
+ qdel(src)
+ return
diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm
index 1ac4779414..453d848e8d 100644
--- a/code/game/gamemodes/meteor/meteors.dm
+++ b/code/game/gamemodes/meteor/meteors.dm
@@ -18,21 +18,18 @@
//Meteor spawning global procs
///////////////////////////////
-/proc/pick_meteor_start(var/startSide = pick(cardinal))
- var/startLevel = pick(using_map.station_levels - using_map.sealed_levels)
- var/pickedstart = spaceDebrisStartLoc(startSide, startLevel)
-
- return list(startLevel, pickedstart)
-
-/proc/spawn_meteors(var/number = 10, var/list/meteortypes, var/startSide)
+/proc/spawn_meteors(var/number = 10, var/list/meteortypes, var/startSide, var/zlevel)
+ log_debug("Spawning [number] meteors on the [dir2text(startSide)] of [zlevel]")
for(var/i = 0; i < number; i++)
- spawn_meteor(meteortypes, startSide)
+ spawn_meteor(meteortypes, startSide, zlevel)
-/proc/spawn_meteor(var/list/meteortypes, var/startSide)
- var/start = pick_meteor_start(startSide)
+/proc/spawn_meteor(var/list/meteortypes, var/startSide, var/startLevel)
+ if(isnull(startSide))
+ startSide = pick(cardinal)
+ if(isnull(startLevel))
+ startLevel = pick(using_map.station_levels - using_map.sealed_levels)
- var/startLevel = start[1]
- var/turf/pickedstart = start[2]
+ var/turf/pickedstart = spaceDebrisStartLoc(startSide, startLevel)
var/turf/pickedgoal = spaceDebrisFinishLoc(startSide, startLevel)
var/Me = pickweight(meteortypes)
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 1447a37997..ca177403ac 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -269,20 +269,23 @@ var/const/enterloopsanity = 100
for(var/obj/O in src)
O.hide(O.hides_under_flooring() && !is_plating())
-/turf/proc/AdjacentTurfs()
- var/L[] = new()
- for(var/turf/simulated/t in oview(src,1))
- if(!t.density)
- if(!LinkBlocked(src, t) && !TurfBlockedNonWindow(t))
- L.Add(t)
- return L
+/turf/proc/AdjacentTurfs(var/check_blockage = TRUE)
+ . = list()
+ for(var/t in (trange(1,src) - src))
+ var/turf/T = t
+ if(check_blockage)
+ if(!T.density)
+ if(!LinkBlocked(src, T) && !TurfBlockedNonWindow(T))
+ . += t
+ else
+ . += t
-/turf/proc/CardinalTurfs()
- var/L[] = new()
- for(var/turf/simulated/T in AdjacentTurfs())
+/turf/proc/CardinalTurfs(var/check_blockage = TRUE)
+ . = list()
+ for(var/ad in AdjacentTurfs(check_blockage))
+ var/turf/T = ad
if(T.x == src.x || T.y == src.y)
- L.Add(T)
- return L
+ . += T
/turf/proc/Distance(turf/t)
if(get_dist(src,t) == 1)
diff --git a/code/modules/events/carp_migration.dm b/code/modules/events/carp_migration.dm
index b30a5d4898..53efc99e8d 100644
--- a/code/modules/events/carp_migration.dm
+++ b/code/modules/events/carp_migration.dm
@@ -1,51 +1,75 @@
/datum/event/carp_migration
- announceWhen = 50
- endWhen = 900
-
+ startWhen = 0 // Start immediately
+ announceWhen = 45 // Adjusted by setup
+ endWhen = 75 // Adjusted by setup
+ var/carp_cap = 10
var/list/spawned_carp = list()
/datum/event/carp_migration/setup()
- announceWhen = rand(40, 60)
- endWhen = rand(600,1200)
+ announceWhen = rand(30, 60) // 1 to 2 minutes
+ endWhen += severity * 25
+ carp_cap = 2 + 3 ** severity // No more than this many at once regardless of waves. (5, 11, 29)
/datum/event/carp_migration/announce()
var/announcement = ""
if(severity == EVENT_LEVEL_MAJOR)
- announcement = "Massive migration of unknown biological entities has been detected near [station_name()], please stand-by."
+ announcement = "Massive migration of unknown biological entities has been detected near [location_name()], please stand-by."
else
- announcement = "Unknown biological [spawned_carp.len == 1 ? "entity has" : "entities have"] been detected near [station_name()], please stand-by."
+ announcement = "Unknown biological [spawned_carp.len == 1 ? "entity has" : "entities have"] been detected near [location_name()], please stand-by."
command_announcement.Announce(announcement, "Lifesign Alert")
-/datum/event/carp_migration/start()
- if(severity == EVENT_LEVEL_MAJOR)
- spawn_fish(landmarks_list.len)
- else if(severity == EVENT_LEVEL_MODERATE)
- spawn_fish(rand(4, 6)) //12 to 30 carp, in small groups
- else
- spawn_fish(rand(1, 3), 1, 2) //1 to 6 carp, alone or in pairs
+/datum/event/carp_migration/tick()
+ if(activeFor % 4 != 0)
+ return // Only process every 8 seconds.
+ if(count_spawned_carps() < carp_cap && living_observers_present(affecting_z))
+ spawn_fish(rand(1, severity * 2) - 1, severity, severity * 2)
-/datum/event/carp_migration/proc/spawn_fish(var/num_groups, var/group_size_min=3, var/group_size_max=5)
- var/list/spawn_locations = list()
-
- for(var/obj/effect/landmark/C in landmarks_list)
- if(C.name == "carpspawn")
- spawn_locations.Add(C.loc)
- spawn_locations = shuffle(spawn_locations)
- num_groups = min(num_groups, spawn_locations.len)
+/datum/event/carp_migration/proc/spawn_fish(var/num_groups, var/group_size_min, var/group_size_max, var/dir)
+ if(isnull(dir))
+ dir = (victim && prob(80)) ? victim.fore_dir : pick(GLOB.cardinal)
var/i = 1
while (i <= num_groups)
+ var/Z = pick(affecting_z)
var/group_size = rand(group_size_min, group_size_max)
- for (var/j = 1, j <= group_size, j++)
- spawned_carp.Add(new /mob/living/simple_mob/animal/space/carp/event(spawn_locations[i]))
+ var/turf/map_center = locate(round(world.maxx/2), round(world.maxy/2), Z)
+ var/turf/group_center = pick_random_edge_turf(dir, Z, TRANSITIONEDGE + 2)
+ var/list/turfs = getcircle(group_center, 2)
+ for (var/j = 0, j < group_size, j++)
+ var/mob/living/simple_mob/animal/M = new /mob/living/simple_mob/animal/space/carp/event(turfs[(i % turfs.len) + 1])
+ GLOB.destroyed_event.register(M, src, .proc/on_carp_destruction)
+ spawned_carp.Add(M)
+ M.ai_holder?.give_destination(map_center) // Ask carp to swim towards the middle of the map
+ // TODO - Our throwing systems don't support callbacks. If it ever does, we can throw first to simulate ship speed.
i++
+// Counts living carp spawned by this event.
+/datum/event/carp_migration/proc/count_spawned_carps()
+ . = 0
+ for(var/I in spawned_carp)
+ var/mob/living/simple_mob/animal/M = I
+ if(!QDELETED(M) && M.stat != DEAD)
+ . += 1
+
+// If carp is bomphed, remove it from the list.
+/datum/event/carp_migration/proc/on_carp_destruction(var/mob/M)
+ spawned_carp -= M
+ GLOB.destroyed_event.unregister(M, src, .proc/on_carp_destruction)
+
/datum/event/carp_migration/end()
+ . = ..()
+ // Clean up carp that died in space for some reason.
spawn(0)
for(var/mob/living/simple_mob/SM in spawned_carp)
- if(!SM.stat)
+ if(SM.stat == DEAD)
var/turf/T = get_turf(SM)
if(istype(T, /turf/space))
if(prob(75))
qdel(SM)
- sleep(1)
\ No newline at end of file
+ CHECK_TICK
+
+//
+// Overmap version of the event!
+//
+/datum/event/carp_migration/overmap
+ announceWhen = 1 // Announce much faster!
diff --git a/code/modules/events/dust.dm b/code/modules/events/dust.dm
index 3c7a116827..54a1ec8baf 100644
--- a/code/modules/events/dust.dm
+++ b/code/modules/events/dust.dm
@@ -3,13 +3,17 @@
endWhen = 30
/datum/event/dust/announce()
- command_announcement.Announce("Debris resulting from activity on another nearby asteroid is approaching \the [station_name()]", "Dust Alert")
+ if(victim)
+ command_announcement.Announce("The [location_name()] is now passing through a belt of space dust.", "[location_name()] Sensor Array")
+ else
+ command_announcement.Announce("Debris resulting from activity on another nearby asteroid is approaching \the [location_name()]", "Dust Alert")
-/datum/event/dust/start()
- dust_swarm(get_severity())
+/datum/event/dust/tick()
+ if(prob(10))
+ dust_swarm(severity, affecting_z)
/datum/event/dust/end()
- command_announcement.Announce("\The [station_name()] is no longer in danger of impact from space debris.", "Dust Notice")
+ command_announcement.Announce("\The [location_name()] is no longer in danger of impact from space debris.", "Dust Notice")
/datum/event/dust/proc/get_severity()
switch(severity)
diff --git a/code/modules/events/electrical_storm.dm b/code/modules/events/electrical_storm.dm
index 73198dec9b..dbf935326f 100644
--- a/code/modules/events/electrical_storm.dm
+++ b/code/modules/events/electrical_storm.dm
@@ -1,28 +1,61 @@
/datum/event/electrical_storm
- var/lightsoutAmount = 1
- var/lightsoutRange = 25
+ announceWhen = 0 // Warn them shortly before it begins.
+ startWhen = 30 // 1 minute
+ endWhen = 60 // Set in setup()
+ has_skybox_image = TRUE
+ var/tmp/lightning_color
+ var/tmp/list/valid_apcs // List of valid APCs.
+/datum/event/electrical_storm/get_skybox_image()
+ if(!lightning_color)
+ lightning_color = pick("#ffd98c", "#ebc7ff", "#bdfcff", "#bdd2ff", "#b0ffca", "#ff8178", "#ad74cc")
+ var/image/res = image('icons/skybox/electrobox.dmi', "lightning")
+ res.color = lightning_color
+ res.appearance_flags = RESET_COLOR
+ res.blend_mode = BLEND_ADD
+ return res
/datum/event/electrical_storm/announce()
- command_announcement.Announce("An electrical issue has been detected in your area, please repair potential electronic overloads.", "Electrical Alert")
-
+ ..()
+ switch(severity)
+ if(EVENT_LEVEL_MUNDANE)
+ command_announcement.Announce("A minor electrical storm has been detected near the [location_name()]. Please watch out for possible electrical discharges.", "[location_name()] Sensor Array")
+ if(EVENT_LEVEL_MODERATE)
+ command_announcement.Announce("The [location_name()] is about to pass through an electrical storm. Please secure sensitive electrical equipment until the storm passes.", "[location_name()] Sensor Array")
+ if(EVENT_LEVEL_MAJOR)
+ command_announcement.Announce("Alert. A strong electrical storm has been detected in proximity of the [location_name()]. It is recommended to immediately secure sensitive electrical equipment until the storm passes.", "[location_name()] Sensor Array")
/datum/event/electrical_storm/start()
- var/list/epicentreList = list()
+ ..()
+ valid_apcs = list()
+ for(var/obj/machinery/power/apc/A in global.machines)
+ if(A.z in affecting_z)
+ valid_apcs.Add(A)
+ endWhen = (severity * 60) + startWhen
- for(var/i=1, i <= lightsoutAmount, i++)
- var/list/possibleEpicentres = list()
- for(var/obj/effect/landmark/newEpicentre in landmarks_list)
- if(newEpicentre.name == "lightsout" && !(newEpicentre in epicentreList))
- possibleEpicentres += newEpicentre
- if(possibleEpicentres.len)
- epicentreList += pick(possibleEpicentres)
- else
- break
+/datum/event/electrical_storm/tick()
+ ..()
+ // See if shields can stop it first (It would be nice to port baystation's cooler shield gens perhaps)
+ // TODO - We need a better shield generator system to handle this properly.
+ if(!valid_apcs.len)
+ log_debug("No valid APCs found for electrical storm event ship=[victim]!")
+ return
+ var/list/picked_apcs = list()
+ for(var/i=0, i< severity * 2, i++) // up to 2/4/6 APCs per tick depending on severity
+ picked_apcs |= pick(valid_apcs)
+ for(var/obj/machinery/power/apc/T in picked_apcs)
+ affect_apc(T)
- if(!epicentreList.len)
+/datum/event/electrical_storm/proc/affect_apc(var/obj/machinery/power/apc/T)
+ // Main breaker is turned off. Consider this APC protected.
+ if(!T.operating)
return
- for(var/obj/effect/landmark/epicentre in epicentreList)
- for(var/obj/machinery/power/apc/apc in range(epicentre,lightsoutRange))
- apc.overload_lighting()
+ // Decent chance to overload lighting circuit.
+ if(prob(3 * severity))
+ T.overload_lighting()
+
+ // Relatively small chance to emag the apc as apc_damage event does.
+ if(prob(0.2 * severity))
+ T.emagged = 1
+ T.update_icon()
diff --git a/code/modules/events/event.dm b/code/modules/events/event.dm
index e44e161a1a..9718f821b2 100644
--- a/code/modules/events/event.dm
+++ b/code/modules/events/event.dm
@@ -1,3 +1,4 @@
+// Event Meta instances represent choices for the event manager to choose for random events.
/datum/event_meta
var/name = ""
var/enabled = 1 // Whether or not the event is available for random selection at all
@@ -39,6 +40,10 @@
return total_weight
+/datum/event_meta/no_overmap/get_weight() //these events have overmap equivalents, and shouldn't fire randomly if overmap is used
+ return global.using_map.use_overmap ? 0 : ..()
+
+// Event datums define and execute the actual events themselves.
/datum/event //NOTE: Times are measured in master controller ticks!
var/startWhen = 0 //When in the lifetime to call start().
var/announceWhen = 0 //When in the lifetime to call announce().
@@ -51,6 +56,9 @@
var/endedAt = 0 //When this event ended.
var/processing_active = TRUE
var/datum/event_meta/event_meta = null
+ var/list/affecting_z = null // List of z-levels to affect, null lets the event choose (usally station_levels)
+ var/has_skybox_image = FALSE // True if SSskybox should query this event for an image to put in the skybox.
+ var/obj/effect/overmap/visitable/ship/victim = null // Ship that triggered this event on itself. Some messages might be different etc.
/datum/event/nothing
@@ -65,6 +73,8 @@
//Allows you to start before announcing or vice versa.
//Only called once.
/datum/event/proc/start()
+ if(has_skybox_image)
+ SSskybox.rebuild_skyboxes(affecting_z)
return
//Called when the tick is equal to the announceWhen variable.
@@ -87,6 +97,8 @@
//For example: if(activeFor == myOwnVariable + 30) doStuff()
//Only called once.
/datum/event/proc/end()
+ if(has_skybox_image)
+ SSskybox.rebuild_skyboxes(affecting_z)
return
//Returns the latest point of event processing.
@@ -132,9 +144,12 @@
end()
endedAt = world.time
- SSevents.active_events -= src
SSevents.event_complete(src)
+//Called during building of skybox to get overlays
+/datum/event/proc/get_skybox_image()
+ return
+
/datum/event/New(var/datum/event_meta/EM)
// event needs to be responsible for this, as stuff like APLUs currently make their own events for curious reasons
SSevents.active_events += src
@@ -146,5 +161,17 @@
startedAt = world.time
+ if(!affecting_z)
+ affecting_z = using_map.station_levels
+
setup()
..()
+
+/datum/event/Destroy()
+ victim = null
+ . = ..()
+
+/datum/event/proc/location_name()
+ if(victim)
+ return victim.name
+ return station_name()
diff --git a/code/modules/events/ion_storm.dm b/code/modules/events/ion_storm.dm
index abfa4407fe..592c963be8 100644
--- a/code/modules/events/ion_storm.dm
+++ b/code/modules/events/ion_storm.dm
@@ -1,20 +1,51 @@
//This file was auto-corrected by findeclaration.exe on 29/05/2012 15:03:04
/datum/event/ionstorm
+ has_skybox_image = TRUE
var/botEmagChance = 0 //VOREStation Edit
+ var/cloud_hueshift
var/list/players = list()
-/datum/event/ionstorm/announce()
+/datum/event/ionstorm/get_skybox_image()
+ if(!cloud_hueshift)
+ cloud_hueshift = color_rotation(rand(-3, 3) * 15)
+ var/image/res = image('icons/skybox/ionbox.dmi', "ions")
+ res.color = cloud_hueshift
+ res.appearance_flags = RESET_COLOR
+ res.blend_mode = BLEND_ADD
+ return res
+
+/datum/event/ionstorm/setup()
endWhen = rand(500, 1500)
+
+// Interestingly enough, announce() actually *DOES* this event for some reason.
+/datum/event/ionstorm/announce()
// command_alert("The station has entered an ion storm. Monitor all electronic equipment for malfunctions", "Anomaly Alert")
for (var/mob/living/carbon/human/player in player_list)
if( !player.mind || player_is_antag(player.mind, only_offstation_roles = 1) || player.client.inactivity > MinutesToTicks(10))
continue
players += player.real_name
+ // Flomph synthetics
+ for(var/mob/living/carbon/S in living_mob_list)
+ if (!S.isSynthetic())
+ continue
+ if(!(S.z in affecting_z))
+ continue
+ var/area/A = get_area(S)
+ if(!A || A.flags & RAD_SHIELDED) // Rad shielding will protect from ions too
+ continue
+ to_chat(S, "Your integrated sensors detect an ionospheric anomaly. Your systems will be impacted as you begin a partial restart.")
+ var/ionbug = rand(3, 9)
+ S.confused += ionbug
+ S.eye_blurry += (ionbug - 1)
+
+ // Ionize silicon mobs
for (var/mob/living/silicon/ai/target in silicon_mob_list)
+ if(!(target.z in affecting_z))
+ continue
var/law = target.generate_ion_law()
- to_chat(target, "You have detected a change in your laws information:")
+ to_chat(target, "You have detected a change in your laws information:")
to_chat(target, law)
target.add_ion_law(law)
target.show_laws()
@@ -31,13 +62,15 @@
/datum/event/ionstorm/tick()
if(botEmagChance)
for(var/mob/living/bot/bot in mob_list)
+ if(!(bot.z in affecting_z))
+ continue
if(prob(botEmagChance))
bot.emag_act(1)
/datum/event/ionstorm/end()
- spawn(rand(5000,8000))
- if(prob(50))
- ion_storm_announcement()
+ if(prob(50))
+ spawn(rand(5000,8000))
+ command_announcement.Announce("It has come to our attention that \the [location_name()] passed through an ion storm. Please monitor all electronic equipment for malfunctions.", "Anomaly Alert")
/*
/proc/IonStorm(botEmagChance = 10)
diff --git a/code/modules/events/meteors.dm b/code/modules/events/meteors.dm
index b4f202722c..a57ca87576 100644
--- a/code/modules/events/meteors.dm
+++ b/code/modules/events/meteors.dm
@@ -1,30 +1,45 @@
/datum/event/meteor_wave
- startWhen = 5
- endWhen = 7
+ startWhen = 30 // About one minute early warning
+ endWhen = 60 // Adjusted automatically in tick()
+ has_skybox_image = TRUE
var/next_meteor = 6
var/waves = 1
var/start_side
+ var/next_meteor_lower = 10
+ var/next_meteor_upper = 20
+
+/datum/event/meteor_wave/get_skybox_image()
+ var/image/res = image('icons/skybox/rockbox.dmi', "rockbox")
+ res.color = COLOR_ASTEROID_ROCK
+ res.appearance_flags = RESET_COLOR
+ return res
/datum/event/meteor_wave/setup()
- waves = 2 + rand(1, severity) //EVENT_LEVEL_MAJOR is 3-5 waves
+ waves = (2 + rand(1, severity)) * severity
start_side = pick(cardinal)
endWhen = worst_case_end()
/datum/event/meteor_wave/announce()
switch(severity)
if(EVENT_LEVEL_MAJOR)
- command_announcement.Announce("Meteors have been detected on collision course with \the [station_name()].", "Meteor Alert", new_sound = 'sound/AI/meteors.ogg')
+ command_announcement.Announce("Meteors have been detected on collision course with \the [location_name()].", "Meteor Alert", new_sound = 'sound/AI/meteors.ogg')
else
- command_announcement.Announce("\The [station_name()] is now in a meteor shower.", "Meteor Alert")
+ command_announcement.Announce("\The [location_name()] is now in a meteor shower.", "Meteor Alert")
/datum/event/meteor_wave/tick()
if(waves && activeFor >= next_meteor)
- var/pick_side = prob(80) ? start_side : (prob(50) ? turn(start_side, 90) : turn(start_side, -90))
+ send_wave()
- spawn() spawn_meteors(severity * rand(1,2), get_meteors(), pick_side)
- next_meteor += rand(15, 30) / severity
- waves--
- endWhen = worst_case_end()
+/datum/event/meteor_wave/proc/send_wave()
+ var/pick_side = prob(80) ? start_side : (prob(50) ? turn(start_side, 90) : turn(start_side, -90))
+
+ spawn() spawn_meteors(get_wave_size(), get_meteors(), pick_side, pick(affecting_z))
+ next_meteor += rand(next_meteor_lower, next_meteor_upper) / severity
+ waves--
+ endWhen = worst_case_end()
+
+/datum/event/meteor_wave/proc/get_wave_size()
+ return severity * rand(2, 3)
/datum/event/meteor_wave/proc/worst_case_end()
return activeFor + ((30 / severity) * waves) + 10
@@ -32,9 +47,9 @@
/datum/event/meteor_wave/end()
switch(severity)
if(EVENT_LEVEL_MAJOR)
- command_announcement.Announce("\The [station_name()] has cleared the meteor storm.", "Meteor Alert")
+ command_announcement.Announce("\The [location_name()] has cleared the meteor storm.", "Meteor Alert")
else
- command_announcement.Announce("\The [station_name()] has cleared the meteor shower", "Meteor Alert")
+ command_announcement.Announce("\The [location_name()] has cleared the meteor shower", "Meteor Alert")
/datum/event/meteor_wave/proc/get_meteors()
if(EVENT_LEVEL_MAJOR)
@@ -44,3 +59,41 @@
return meteors_threatening
else
return meteors_normal
+
+
+/datum/event/meteor_wave/overmap
+ next_meteor_lower = 5
+ next_meteor_upper = 10
+ next_meteor = 0
+
+/datum/event/meteor_wave/overmap/tick()
+ if(victim && !victim.is_still()) // Meteors mostly fly in your face
+ start_side = prob(90) ? victim.fore_dir : pick(GLOB.cardinal)
+ else //Unless you're standing still
+ start_side = pick(GLOB.cardinal)
+ ..()
+
+/datum/event/meteor_wave/overmap/get_wave_size()
+ . = ..()
+ if(!victim)
+ return
+ var/skill = victim.get_helm_skill()
+ var/speed = victim.get_speed()
+ if(skill >= SKILL_PROF)
+ . = round(. * 0.5)
+ if(victim.is_still()) //Standing still means less shit flies your way
+ . = round(. * 0.1)
+ if(speed < SHIP_SPEED_SLOW) //Slow and steady
+ . = round(. * 0.5)
+ if(speed > SHIP_SPEED_FAST) //Sanic stahp
+ . *= 2
+
+ //Smol ship evasion
+ if(victim.vessel_size < SHIP_SIZE_LARGE && speed < SHIP_SPEED_FAST)
+ var/skill_needed = SKILL_PROF
+ if(speed < SHIP_SPEED_SLOW)
+ skill_needed = SKILL_ADEPT
+ if(victim.vessel_size < SHIP_SIZE_SMALL)
+ skill_needed = skill_needed - 1
+ if(skill >= max(skill_needed, victim.skill_needed))
+ . = round(. * 0.5)
diff --git a/code/modules/mob/new_player/skill.dm b/code/modules/mob/new_player/skill.dm
index dc7e5c354e..b2d061d5a6 100644
--- a/code/modules/mob/new_player/skill.dm
+++ b/code/modules/mob/new_player/skill.dm
@@ -3,6 +3,7 @@ var/global/const
SKILL_BASIC = 1
SKILL_ADEPT = 2
SKILL_EXPERT = 3
+ SKILL_PROF = 4
/datum/skill/var
ID = "none" // ID of the skill, used in code
diff --git a/code/modules/overmap/_defines.dm b/code/modules/overmap/_defines.dm
deleted file mode 100644
index 6cfde46793..0000000000
--- a/code/modules/overmap/_defines.dm
+++ /dev/null
@@ -1,145 +0,0 @@
-//How far from the edge of overmap zlevel could randomly placed objects spawn
-#define OVERMAP_EDGE 2
-
-#define SHIP_SIZE_TINY 1
-#define SHIP_SIZE_SMALL 2
-#define SHIP_SIZE_LARGE 3
-
-//multipliers for max_speed to find 'slow' and 'fast' speeds for the ship
-#define SHIP_SPEED_SLOW 1/(40 SECONDS)
-#define SHIP_SPEED_FAST 3/(20 SECONDS)// 15 speed
-
-#define OVERMAP_WEAKNESS_NONE 0
-#define OVERMAP_WEAKNESS_FIRE 1
-#define OVERMAP_WEAKNESS_EMP 2
-#define OVERMAP_WEAKNESS_MINING 4
-#define OVERMAP_WEAKNESS_EXPLOSIVE 8
-
-//Dimension of overmap (squares 4 lyfe)
-var/global/list/map_sectors = list()
-
-/area/overmap/
- name = "System Map"
- icon_state = "start"
- requires_power = 0
- base_turf = /turf/unsimulated/map
-
-/turf/unsimulated/map
- icon = 'icons/turf/space.dmi'
- icon_state = "map"
- initialized = FALSE // TODO - Fix unsimulated turf initialization so this override is not necessary!
-
-/turf/unsimulated/map/edge
- opacity = 1
- density = 1
-
-/turf/unsimulated/map/Initialize()
- . = ..()
- name = "[x]-[y]"
- var/list/numbers = list()
-
- if(x == 1 || x == global.using_map.overmap_size)
- numbers += list("[round(y/10)]","[round(y%10)]")
- if(y == 1 || y == global.using_map.overmap_size)
- numbers += "-"
- if(y == 1 || y == global.using_map.overmap_size)
- numbers += list("[round(x/10)]","[round(x%10)]")
-
- for(var/i = 1 to numbers.len)
- var/image/I = image('icons/effects/numbers.dmi',numbers[i])
- I.pixel_x = 5*i - 2
- I.pixel_y = world.icon_size/2 - 3
- if(y == 1)
- I.pixel_y = 3
- I.pixel_x = 5*i + 4
- if(y == global.using_map.overmap_size)
- I.pixel_y = world.icon_size - 9
- I.pixel_x = 5*i + 4
- if(x == 1)
- I.pixel_x = 5*i - 2
- if(x == global.using_map.overmap_size)
- I.pixel_x = 5*i + 2
- add_overlay(I)
-
-//list used to track which zlevels are being 'moved' by the proc below
-var/list/moving_levels = list()
-//Proc to 'move' stars in spess
-//yes it looks ugly, but it should only fire when state actually change.
-//null direction stops movement
-proc/toggle_move_stars(zlevel, direction)
- if(!zlevel)
- return
-
- if (moving_levels["[zlevel]"] != direction)
- moving_levels["[zlevel]"] = direction
-
- var/list/spaceturfs = block(locate(1, 1, zlevel), locate(world.maxx, world.maxy, zlevel))
- for(var/turf/space/T in spaceturfs)
- T.toggle_transit(direction)
- CHECK_TICK
-/*
-//list used to cache empty zlevels to avoid nedless map bloat
-var/list/cached_space = list()
-
-proc/overmap_spacetravel(var/turf/space/T, var/atom/movable/A)
- var/obj/effect/map/M = map_sectors["[T.z]"]
- if (!M)
- return
- var/mapx = M.x
- var/mapy = M.y
- var/nx = 1
- var/ny = 1
- var/nz = M.map_z
-
- if(T.x <= TRANSITIONEDGE)
- nx = world.maxx - TRANSITIONEDGE - 2
- ny = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2)
- mapx = max(1, mapx-1)
-
- else if (A.x >= (world.maxx - TRANSITIONEDGE - 1))
- nx = TRANSITIONEDGE + 2
- ny = rand(TRANSITIONEDGE + 2, world.maxy - TRANSITIONEDGE - 2)
- mapx = min(world.maxx, mapx+1)
-
- else if (T.y <= TRANSITIONEDGE)
- ny = world.maxy - TRANSITIONEDGE -2
- nx = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2)
- mapy = max(1, mapy-1)
-
- else if (A.y >= (world.maxy - TRANSITIONEDGE - 1))
- ny = TRANSITIONEDGE + 2
- nx = rand(TRANSITIONEDGE + 2, world.maxx - TRANSITIONEDGE - 2)
- mapy = min(world.maxy, mapy+1)
-
- testing("[A] moving from [M] ([M.x], [M.y]) to ([mapx],[mapy]).")
-
- var/turf/map = locate(mapx,mapy,OVERMAP_ZLEVEL)
- var/obj/effect/map/TM = locate() in map
- if(TM)
- nz = TM.map_z
- testing("Destination: [TM]")
- else
- if(cached_space.len)
- var/obj/effect/map/sector/temporary/cache = cached_space[cached_space.len]
- cached_space -= cache
- nz = cache.map_z
- cache.x = mapx
- cache.y = mapy
- testing("Destination: *cached* [TM]")
- else
- world.maxz++
- nz = world.maxz
- TM = new /obj/effect/map/sector/temporary(mapx, mapy, nz)
- testing("Destination: *new* [TM]")
-
- var/turf/dest = locate(nx,ny,nz)
- if(dest)
- A.loc = dest
-
- if(istype(M, /obj/effect/map/sector/temporary))
- var/obj/effect/map/sector/temporary/source = M
- if (source.can_die())
- testing("Catching [M] for future use")
- source.loc = null
- cached_space += source
-*/
\ No newline at end of file
diff --git a/code/modules/overmap/events/event_handler.dm b/code/modules/overmap/events/event_handler.dm
new file mode 100644
index 0000000000..67785e95ac
--- /dev/null
+++ b/code/modules/overmap/events/event_handler.dm
@@ -0,0 +1,163 @@
+GLOBAL_DATUM_INIT(overmap_event_handler, /decl/overmap_event_handler, new)
+
+/decl/overmap_event_handler
+ var/list/hazard_by_turf
+ var/list/ship_events
+
+/decl/overmap_event_handler/New()
+ ..()
+ hazard_by_turf = list()
+ ship_events = list()
+
+// Populates overmap with random events! Should be called once at startup at some point.
+/decl/overmap_event_handler/proc/create_events(var/z_level, var/overmap_size, var/number_of_events)
+ // Acquire the list of not-yet utilized overmap turfs on this Z-level
+ var/list/overmap_turfs = block(locate(OVERMAP_EDGE, OVERMAP_EDGE, z_level), locate(overmap_size - OVERMAP_EDGE, overmap_size - OVERMAP_EDGE, z_level))
+ var/list/candidate_turfs = list()
+ for(var/Trf in overmap_turfs)
+ var/turf/T = Trf
+ if(!(locate(/obj/effect/overmap/visitable) in T))
+ candidate_turfs += T
+
+ for(var/i = 1 to number_of_events)
+ if(!candidate_turfs.len)
+ break
+ var/overmap_event_type = pick(subtypesof(/datum/overmap_event))
+ var/datum/overmap_event/datum_spawn = new overmap_event_type
+ log_debug("Generating cloud of [datum_spawn.count] [datum_spawn] overmap event hazards")
+
+ var/list/event_turfs = acquire_event_turfs(datum_spawn.count, datum_spawn.radius, candidate_turfs, datum_spawn.continuous)
+ candidate_turfs -= event_turfs
+
+ for(var/event_turf in event_turfs)
+ var/type = pick(datum_spawn.hazards)
+ new type(event_turf)
+
+ qdel(datum_spawn)//idk help how do I do this better?
+
+/decl/overmap_event_handler/proc/acquire_event_turfs(var/number_of_turfs, var/distance_from_origin, var/list/candidate_turfs, var/continuous = TRUE)
+ number_of_turfs = min(number_of_turfs, candidate_turfs.len)
+ candidate_turfs = candidate_turfs.Copy() // Not this proc's responsibility to adjust the given lists
+
+ var/origin_turf = pick(candidate_turfs)
+ var/list/selected_turfs = list(origin_turf)
+ var/list/selection_turfs = list(origin_turf)
+ candidate_turfs -= origin_turf
+
+ while(selection_turfs.len && selected_turfs.len < number_of_turfs)
+ var/selection_turf = pick(selection_turfs)
+ var/random_neighbour = get_random_neighbour(selection_turf, candidate_turfs, continuous, distance_from_origin)
+
+ if(random_neighbour)
+ candidate_turfs -= random_neighbour
+ selected_turfs += random_neighbour
+ if(get_dist(origin_turf, random_neighbour) < distance_from_origin)
+ selection_turfs += random_neighbour
+ else
+ selection_turfs -= selection_turf
+
+ return selected_turfs
+
+/decl/overmap_event_handler/proc/get_random_neighbour(var/turf/origin_turf, var/list/candidate_turfs, var/continuous = TRUE, var/range)
+ var/fitting_turfs
+ if(continuous)
+ fitting_turfs = origin_turf.CardinalTurfs(FALSE)
+ else
+ fitting_turfs = trange(range, origin_turf)
+ fitting_turfs = shuffle(fitting_turfs)
+ for(var/turf/T in fitting_turfs)
+ if(T in candidate_turfs)
+ return T
+
+/decl/overmap_event_handler/proc/start_hazard(var/obj/effect/overmap/visitable/ship/ship, var/obj/effect/overmap/event/hazard)//make these accept both hazards or events
+ if(!(ship in ship_events))
+ ship_events += ship
+
+ for(var/event_type in hazard.events)
+ if(is_event_active(ship, event_type, hazard.difficulty))//event's already active, don't bother
+ continue
+ var/datum/event_meta/EM = new(hazard.difficulty, "Overmap event - [hazard.name]", event_type, add_to_queue = FALSE, is_one_shot = TRUE)
+ var/datum/event/E = new event_type(EM)
+ E.startWhen = 0
+ E.endWhen = INFINITY
+ // TODO - Leshana - Note: event.setup() is called before these are set!
+ E.affecting_z = ship.map_z
+ E.victim = ship
+ LAZYADD(ship_events[ship], E)
+
+/decl/overmap_event_handler/proc/stop_hazard(var/obj/effect/overmap/visitable/ship/ship, var/obj/effect/overmap/event/hazard)
+ for(var/event_type in hazard.events)
+ var/datum/event/E = is_event_active(ship, event_type, hazard.difficulty)
+ if(E)
+ E.kill()
+ LAZYREMOVE(ship_events[ship], E)
+
+/decl/overmap_event_handler/proc/is_event_active(var/ship, var/event_type, var/severity)
+ if(!ship_events[ship]) return
+ for(var/datum/event/E in ship_events[ship])
+ if(E.type == event_type && E.severity == severity)
+ return E
+
+/decl/overmap_event_handler/proc/on_turf_entered(var/turf/new_loc, var/obj/effect/overmap/visitable/ship/ship, var/old_loc)
+ if(!istype(ship))
+ return
+ if(new_loc == old_loc)
+ return
+
+ for(var/obj/effect/overmap/event/E in hazard_by_turf[new_loc])
+ start_hazard(ship, E)
+
+/decl/overmap_event_handler/proc/on_turf_exited(var/turf/old_loc, var/obj/effect/overmap/visitable/ship/ship, var/new_loc)
+ if(!istype(ship))
+ return
+ if(new_loc == old_loc)
+ return
+
+ for(var/obj/effect/overmap/event/E in hazard_by_turf[old_loc])
+ if(is_event_included(hazard_by_turf[new_loc], E))
+ continue // If new turf has the same event as well... keep it going!
+ stop_hazard(ship, E)
+
+/decl/overmap_event_handler/proc/update_hazards(var/turf/T)//catch all updater
+ if(!istype(T))
+ return
+
+ var/list/active_hazards = list()
+ for(var/obj/effect/overmap/event/E in T)
+ if(is_event_included(active_hazards, E, TRUE))
+ continue
+ active_hazards += E
+
+ if(!active_hazards.len)
+ hazard_by_turf -= T
+ else
+ hazard_by_turf |= T
+ hazard_by_turf[T] = active_hazards
+
+ for(var/obj/effect/overmap/visitable/ship/ship in T)
+ for(var/datum/event/E in ship_events[ship])
+ if(is_event_in_turf(E, T))
+ continue
+ E.kill()
+ LAZYREMOVE(ship_events[ship], E)
+
+ for(var/obj/effect/overmap/event/E in active_hazards)
+ start_hazard(ship, E)
+
+/decl/overmap_event_handler/proc/is_event_in_turf(var/datum/event/E, var/turf/T)
+ for(var/obj/effect/overmap/event/hazard in hazard_by_turf[T])
+ if(E in hazard.events && E.severity == hazard.difficulty)
+ return TRUE
+
+/decl/overmap_event_handler/proc/is_event_included(var/list/hazards, var/obj/effect/overmap/event/E, var/equal_or_better)//this proc is only used so it can break out of 2 loops cleanly
+ for(var/obj/effect/overmap/event/A in hazards)
+ if(istype(A, E.type) || istype(E, A.type))
+ if(same_entries(A.events, E.events))
+ if(equal_or_better)
+ if(A.difficulty >= E.difficulty)
+ return TRUE
+ else
+ hazards -= A // TODO - Improve this SPAGHETTI CODE! Done only when called from update_hazards. ~Leshana
+ else
+ if(A.difficulty == E.difficulty)
+ return TRUE
diff --git a/code/modules/overmap/events/generation.dm b/code/modules/overmap/events/generation.dm
new file mode 100644
index 0000000000..9b70308866
--- /dev/null
+++ b/code/modules/overmap/events/generation.dm
@@ -0,0 +1,49 @@
+/*
+** /datum/overmap_event - Descriptors of how/what to spawn during overmap event generation
+*/
+
+//These now are basically only used to spawn hazards. Will be useful when we need to spawn group of moving hazards
+/datum/overmap_event
+ var/name = "map event"
+ var/radius = 2 // Radius of the spawn circle around chosen epicenter
+ var/count = 6 // How many hazards to spawn
+ var/hazards // List (or single) typepath of hazard to spawn
+ var/continuous = TRUE //if it should form continous blob, or can have gaps
+
+/datum/overmap_event/meteor
+ name = "asteroid field"
+ count = 15
+ radius = 4
+ continuous = FALSE
+ hazards = /obj/effect/overmap/event/meteor
+
+/datum/overmap_event/electric
+ name = "electrical storm"
+ count = 11
+ radius = 3
+ hazards = /obj/effect/overmap/event/electric
+
+/datum/overmap_event/dust
+ name = "dust cloud"
+ count = 16
+ radius = 4
+ hazards = /obj/effect/overmap/event/dust
+
+/datum/overmap_event/ion
+ name = "ion cloud"
+ count = 8
+ radius = 3
+ hazards = /obj/effect/overmap/event/ion
+
+/datum/overmap_event/carp
+ name = "carp shoal"
+ count = 8
+ radius = 3
+ continuous = FALSE
+ hazards = /obj/effect/overmap/event/carp
+
+/datum/overmap_event/carp/major
+ name = "carp school"
+ count = 5
+ radius = 4
+ hazards = /obj/effect/overmap/event/carp/major
\ No newline at end of file
diff --git a/code/modules/overmap/events/overmap_event.dm b/code/modules/overmap/events/overmap_event.dm
new file mode 100644
index 0000000000..091146dfd9
--- /dev/null
+++ b/code/modules/overmap/events/overmap_event.dm
@@ -0,0 +1,86 @@
+/*
+** /obj/effect/overmap/event - Actual instances of event hazards on the overmap map
+*/
+
+// We don't subtype /obj/effect/overmap/visitable because that'll create sections one can travel to
+// And with them "existing" on the overmap Z-level things quickly get odd.
+/obj/effect/overmap/event
+ name = "event"
+ icon = 'icons/obj/overmap.dmi'
+ icon_state = "event"
+ opacity = 1
+ var/list/events // List of event datum paths
+ var/list/event_icon_states // Randomly picked from
+ var/difficulty = EVENT_LEVEL_MODERATE
+ var/weaknesses //if the BSA can destroy them and with what
+ var/list/victims //basically cached events on which Z level
+
+/obj/effect/overmap/event/Initialize()
+ . = ..()
+ icon_state = pick(event_icon_states)
+ GLOB.overmap_event_handler.update_hazards(loc)
+
+/obj/effect/overmap/event/Move()
+ var/turf/old_loc = loc
+ . = ..()
+ if(.)
+ GLOB.overmap_event_handler.update_hazards(old_loc)
+ GLOB.overmap_event_handler.update_hazards(loc)
+
+/obj/effect/overmap/event/forceMove(atom/destination)
+ var/old_loc = loc
+ . = ..()
+ if(.)
+ GLOB.overmap_event_handler.update_hazards(old_loc)
+ GLOB.overmap_event_handler.update_hazards(loc)
+
+/obj/effect/overmap/event/Destroy()//takes a look at this one as well, make sure everything is A-OK
+ var/turf/T = loc
+ . = ..()
+ GLOB.overmap_event_handler.update_hazards(T)
+
+//
+// Definitions for specific types!
+//
+
+/obj/effect/overmap/event/meteor
+ name = "asteroid field"
+ events = list(/datum/event/meteor_wave/overmap)
+ event_icon_states = list("meteor1", "meteor2", "meteor3", "meteor4")
+ difficulty = EVENT_LEVEL_MAJOR
+ weaknesses = OVERMAP_WEAKNESS_MINING | OVERMAP_WEAKNESS_EXPLOSIVE
+
+/obj/effect/overmap/event/electric
+ name = "electrical storm"
+ events = list(/datum/event/electrical_storm)
+ opacity = 0
+ event_icon_states = list("electrical1", "electrical2", "electrical3", "electrical4")
+ difficulty = EVENT_LEVEL_MAJOR
+ weaknesses = OVERMAP_WEAKNESS_EMP
+
+/obj/effect/overmap/event/dust
+ name = "dust cloud"
+ events = list(/datum/event/dust)
+ event_icon_states = list("dust1", "dust2", "dust3", "dust4")
+ weaknesses = OVERMAP_WEAKNESS_MINING | OVERMAP_WEAKNESS_EXPLOSIVE | OVERMAP_WEAKNESS_FIRE
+
+/obj/effect/overmap/event/ion
+ name = "ion cloud"
+ events = list(/datum/event/ionstorm)
+ opacity = 0
+ event_icon_states = list("ion1", "ion2", "ion3", "ion4")
+ difficulty = EVENT_LEVEL_MAJOR
+ weaknesses = OVERMAP_WEAKNESS_EMP
+
+/obj/effect/overmap/event/carp
+ name = "carp shoal"
+ events = list(/datum/event/carp_migration/overmap)
+ opacity = 0
+ difficulty = EVENT_LEVEL_MODERATE
+ event_icon_states = list("carp1", "carp2")
+ weaknesses = OVERMAP_WEAKNESS_EXPLOSIVE | OVERMAP_WEAKNESS_FIRE
+
+/obj/effect/overmap/event/carp/major
+ name = "carp school"
+ difficulty = EVENT_LEVEL_MAJOR
+ event_icon_states = list("carp3", "carp4")
diff --git a/code/modules/overmap/turfs.dm b/code/modules/overmap/turfs.dm
new file mode 100644
index 0000000000..952865878e
--- /dev/null
+++ b/code/modules/overmap/turfs.dm
@@ -0,0 +1,72 @@
+//Dimension of overmap (squares 4 lyfe)
+var/global/list/map_sectors = list()
+
+/area/overmap/
+ name = "System Map"
+ icon_state = "start"
+ requires_power = 0
+ base_turf = /turf/unsimulated/map
+
+/turf/unsimulated/map
+ icon = 'icons/turf/space.dmi'
+ icon_state = "map"
+ initialized = FALSE // TODO - Fix unsimulated turf initialization so this override is not necessary!
+
+/turf/unsimulated/map/edge
+ opacity = 1
+ density = 1
+
+/turf/unsimulated/map/Initialize()
+ . = ..()
+ name = "[x]-[y]"
+ var/list/numbers = list()
+
+ if(x == 1 || x == global.using_map.overmap_size)
+ numbers += list("[round(y/10)]","[round(y%10)]")
+ if(y == 1 || y == global.using_map.overmap_size)
+ numbers += "-"
+ if(y == 1 || y == global.using_map.overmap_size)
+ numbers += list("[round(x/10)]","[round(x%10)]")
+
+ for(var/i = 1 to numbers.len)
+ var/image/I = image('icons/effects/numbers.dmi',numbers[i])
+ I.pixel_x = 5*i - 2
+ I.pixel_y = world.icon_size/2 - 3
+ if(y == 1)
+ I.pixel_y = 3
+ I.pixel_x = 5*i + 4
+ if(y == global.using_map.overmap_size)
+ I.pixel_y = world.icon_size - 9
+ I.pixel_x = 5*i + 4
+ if(x == 1)
+ I.pixel_x = 5*i - 2
+ if(x == global.using_map.overmap_size)
+ I.pixel_x = 5*i + 2
+ add_overlay(I)
+
+/turf/unsimulated/map/Entered(var/atom/movable/O, var/atom/oldloc)
+ ..()
+ if(istype(O, /obj/effect/overmap/visitable/ship))
+ GLOB.overmap_event_handler.on_turf_entered(src, O, oldloc)
+
+/turf/unsimulated/map/Exited(var/atom/movable/O, var/atom/newloc)
+ ..()
+ if(istype(O, /obj/effect/overmap/visitable/ship))
+ GLOB.overmap_event_handler.on_turf_exited(src, O, newloc)
+
+//list used to track which zlevels are being 'moved' by the proc below
+var/list/moving_levels = list()
+//Proc to 'move' stars in spess
+//yes it looks ugly, but it should only fire when state actually change.
+//null direction stops movement
+proc/toggle_move_stars(zlevel, direction)
+ if(!zlevel)
+ return
+
+ if (moving_levels["[zlevel]"] != direction)
+ moving_levels["[zlevel]"] = direction
+
+ var/list/spaceturfs = block(locate(1, 1, zlevel), locate(world.maxx, world.maxy, zlevel))
+ for(var/turf/space/T in spaceturfs)
+ T.toggle_transit(direction)
+ CHECK_TICK
diff --git a/vorestation.dme b/vorestation.dme
index b2390f9998..7dba23bc88 100644
--- a/vorestation.dme
+++ b/vorestation.dme
@@ -62,6 +62,7 @@
#include "code\__defines\mobs_vr.dm"
#include "code\__defines\nifsoft.dm"
#include "code\__defines\objects.dm"
+#include "code\__defines\overmap.dm"
#include "code\__defines\planets.dm"
#include "code\__defines\process_scheduler.dm"
#include "code\__defines\qdel.dm"
@@ -2872,11 +2873,14 @@
#include "code\modules\organs\subtypes\vox.dm"
#include "code\modules\organs\subtypes\vox_vr.dm"
#include "code\modules\organs\subtypes\xenos.dm"
-#include "code\modules\overmap\_defines.dm"
#include "code\modules\overmap\overmap_object.dm"
#include "code\modules\overmap\overmap_shuttle.dm"
#include "code\modules\overmap\sectors.dm"
#include "code\modules\overmap\spacetravel.dm"
+#include "code\modules\overmap\turfs.dm"
+#include "code\modules\overmap\events\event_handler.dm"
+#include "code\modules\overmap\events\generation.dm"
+#include "code\modules\overmap\events\overmap_event.dm"
#include "code\modules\overmap\ships\landable.dm"
#include "code\modules\overmap\ships\ship.dm"
#include "code\modules\overmap\ships\computers\computer_shims.dm"