diff --git a/code/__defines/subsystems.dm b/code/__defines/subsystems.dm
index 8c546c9135a..75780be3eb8 100644
--- a/code/__defines/subsystems.dm
+++ b/code/__defines/subsystems.dm
@@ -11,3 +11,5 @@
var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_GAME, RUNLEVEL_POSTGAME)
#define RUNLEVEL_FLAG_TO_INDEX(flag) (log(2, flag) + 1) // Convert from the runlevel bitfield constants to index in runlevel_flags list
+
+#define INIT_ORDER_LIGHTING 0
diff --git a/code/controllers/Processes/lighting.dm b/code/controllers/Processes/lighting.dm
deleted file mode 100644
index d46cab1d19f..00000000000
--- a/code/controllers/Processes/lighting.dm
+++ /dev/null
@@ -1,98 +0,0 @@
-/var/lighting_overlays_initialised = FALSE
-
-/var/list/lighting_update_lights = list() // List of lighting sources queued for update.
-/var/list/lighting_update_corners = list() // List of lighting corners queued for update.
-/var/list/lighting_update_overlays = list() // List of lighting overlays queued for update.
-
-/var/list/lighting_update_lights_old = list() // List of lighting sources currently being updated.
-/var/list/lighting_update_corners_old = list() // List of lighting corners currently being updated.
-/var/list/lighting_update_overlays_old = list() // List of lighting overlays currently being updated.
-
-
-/datum/controller/process/lighting
- // Queues of update counts, waiting to be rolled into stats lists
- var/list/stats_queues = list(
- "Source" = list(), "Corner" = list(), "Overlay" = list())
- // Stats lists
- var/list/stats_lists = list(
- "Source" = list(), "Corner" = list(), "Overlay" = list())
- var/update_stats_every = (1 SECONDS)
- var/next_stats_update = 0
- var/stat_updates_to_keep = 5
-
-/datum/controller/process/lighting/setup()
- name = "lighting"
-
- schedule_interval = 0 // run as fast as you possibly can
- sleep_interval = 10 // Yield every 10% of a tick
- defer_usage = 80 // Defer at 80% of a tick
- create_all_lighting_overlays()
- lighting_overlays_initialised = TRUE
-
- // Pre-process lighting once before the round starts. Wait 30 seconds so the away mission has time to load.
- spawn(300)
- doWork(1)
-
-/datum/controller/process/lighting/doWork(roundstart)
-
- lighting_update_lights_old = lighting_update_lights //We use a different list so any additions to the update lists during a delay from scheck() don't cause things to be cut from the list without being updated.
- lighting_update_lights = list()
- for(var/datum/light_source/L in lighting_update_lights_old)
-
- if(L.check() || L.destroyed || L.force_update)
- L.remove_lum()
- if(!L.destroyed)
- L.apply_lum()
-
- else if(L.vis_update) //We smartly update only tiles that became (in) visible to use.
- L.smart_vis_update()
-
- L.vis_update = FALSE
- L.force_update = FALSE
- L.needs_update = FALSE
-
- SCHECK
-
- lighting_update_corners_old = lighting_update_corners //Same as above.
- lighting_update_corners = list()
- for(var/A in lighting_update_corners_old)
- var/datum/lighting_corner/C = A
-
- C.update_overlays()
-
- C.needs_update = FALSE
-
- SCHECK
-
- lighting_update_overlays_old = lighting_update_overlays //Same as above.
- lighting_update_overlays = list()
-
- for(var/A in lighting_update_overlays_old)
- var/atom/movable/lighting_overlay/O = A
- O.update_overlay()
- O.needs_update = 0
- SCHECK
-
- stats_queues["Source"] += lighting_update_lights_old.len
- stats_queues["Corner"] += lighting_update_corners_old.len
- stats_queues["Overlay"] += lighting_update_overlays_old.len
-
- if(next_stats_update <= world.time)
- next_stats_update = world.time + update_stats_every
- for(var/stat_name in stats_queues)
- var/stat_sum = 0
- var/list/stats_queue = stats_queues[stat_name]
- for(var/count in stats_queue)
- stat_sum += count
- stats_queue.Cut()
-
- var/list/stats_list = stats_lists[stat_name]
- stats_list.Insert(1, stat_sum)
- if(stats_list.len > stat_updates_to_keep)
- stats_list.Cut(stats_list.len)
-
-/datum/controller/process/lighting/statProcess()
- ..()
- stat(null, "[total_lighting_sources] sources, [total_lighting_corners] corners, [total_lighting_overlays] overlays")
- for(var/stat_type in stats_lists)
- stat(null, "[stat_type] updates: [jointext(stats_lists[stat_type], " | ")]")
diff --git a/code/controllers/subsystems/lighting.dm b/code/controllers/subsystems/lighting.dm
new file mode 100644
index 00000000000..dd59d0629cd
--- /dev/null
+++ b/code/controllers/subsystems/lighting.dm
@@ -0,0 +1,166 @@
+/*
+** Lighting Subsystem - Process the lighting! Do it!
+*/
+
+#define SSLIGHTING_STAGE_LIGHTS 1
+#define SSLIGHTING_STAGE_CORNERS 2
+#define SSLIGHTING_STAGE_OVERLAYS 3
+#define SSLIGHTING_STAGE_DONE 4
+// This subsystem's fire() method also gets called once during Master.Initialize().
+// During this fire we need to use CHECK_TICK to sleep and continue, but in all other fires we need to use MC_CHECK_TICK to pause and return.
+// This leads us to a rather annoying little tidbit of code that I have stuffed into this macro so I don't have to see it.
+#define DUAL_TICK_CHECK if (init_tick_checks) { CHECK_TICK; } else if (MC_TICK_CHECK) { return; }
+
+// Globals
+/var/lighting_overlays_initialised = FALSE
+/var/list/lighting_update_lights = list() // List of lighting sources queued for update.
+/var/list/lighting_update_corners = list() // List of lighting corners queued for update.
+/var/list/lighting_update_overlays = list() // List of lighting overlays queued for update.
+
+SUBSYSTEM_DEF(lighting)
+ name = "Lighting"
+ wait = 2 // Ticks, not deciseconds
+ init_order = INIT_ORDER_LIGHTING
+ flags = SS_TICKER
+
+ var/list/currentrun = list()
+ var/stage = null
+
+ var/cost_lights = 0
+ var/cost_corners = 0
+ var/cost_overlays = 0
+
+/datum/controller/subsystem/lighting/Initialize(timeofday)
+ if(!lighting_overlays_initialised)
+ // TODO - TG initializes starlight here.
+ create_all_lighting_overlays()
+ lighting_overlays_initialised = TRUE
+
+ // Pre-process lighting once before the round starts.
+ internal_process_lights(FALSE, TRUE)
+ internal_process_corners(FALSE, TRUE)
+ internal_process_overlays(FALSE, TRUE)
+ return ..()
+
+/datum/controller/subsystem/lighting/fire(resumed = FALSE)
+ var/timer
+ if(!resumed)
+ ASSERT(LAZYLEN(currentrun) == 0) // Santity checks to make sure we don't somehow have items left over from last cycle
+ ASSERT(stage == null) // Or somehow didn't finish all the steps from last cycle
+ stage = SSLIGHTING_STAGE_LIGHTS // Start with Step 1 of course
+
+ if(stage == SSLIGHTING_STAGE_LIGHTS)
+ timer = world.tick_usage
+ internal_process_lights(resumed)
+ cost_lights = MC_AVERAGE(cost_lights, TICK_DELTA_TO_MS(world.tick_usage - timer))
+ if(state != SS_RUNNING)
+ return
+ resumed = 0
+ stage = SSLIGHTING_STAGE_CORNERS
+
+ if(stage == SSLIGHTING_STAGE_CORNERS)
+ timer = world.tick_usage
+ internal_process_corners(resumed)
+ cost_corners = MC_AVERAGE(cost_corners, TICK_DELTA_TO_MS(world.tick_usage - timer))
+ if(state != SS_RUNNING)
+ return
+ resumed = 0
+ stage = SSLIGHTING_STAGE_OVERLAYS
+
+ if(stage == SSLIGHTING_STAGE_OVERLAYS)
+ timer = world.tick_usage
+ internal_process_overlays(resumed)
+ cost_overlays = MC_AVERAGE(cost_overlays, TICK_DELTA_TO_MS(world.tick_usage - timer))
+ if(state != SS_RUNNING)
+ return
+ resumed = 0
+ stage = SSLIGHTING_STAGE_DONE
+
+ // Okay, we're done! Woo! Got thru a whole air_master cycle!
+ ASSERT(LAZYLEN(currentrun) == 0) // Sanity checks to make sure there are really none left
+ ASSERT(stage == SSLIGHTING_STAGE_DONE) // And that we didn't somehow skip past the last step
+ currentrun = null
+ stage = null
+
+/datum/controller/subsystem/lighting/proc/internal_process_lights(resumed = FALSE, init_tick_checks = FALSE)
+ if (!resumed)
+ // We swap out the lists so any additions to the global list during a pause don't make things wierd.
+ src.currentrun = global.lighting_update_lights
+ global.lighting_update_lights = list()
+
+ //cache for sanic speed (lists are references anyways)
+ var/list/currentrun = src.currentrun
+ while(currentrun.len)
+ var/datum/light_source/L = currentrun[currentrun.len]
+ currentrun.len--
+
+ if(!L) continue
+ if(L.check() || L.destroyed || L.force_update)
+ L.remove_lum()
+ if(!L.destroyed)
+ L.apply_lum()
+
+ else if(L.vis_update) //We smartly update only tiles that became (in) visible to use.
+ L.smart_vis_update()
+
+ L.vis_update = FALSE
+ L.force_update = FALSE
+ L.needs_update = FALSE
+
+ DUAL_TICK_CHECK
+
+/datum/controller/subsystem/lighting/proc/internal_process_corners(resumed = FALSE, init_tick_checks = FALSE)
+ if (!resumed)
+ // We swap out the lists so any additions to the global list during a pause don't make things wierd.
+ src.currentrun = global.lighting_update_corners
+ global.lighting_update_corners = list()
+
+ //cache for sanic speed (lists are references anyways)
+ var/list/currentrun = src.currentrun
+ while(currentrun.len)
+ var/datum/lighting_corner/C = currentrun[currentrun.len]
+ currentrun.len--
+
+ if(!C) continue
+ C.update_overlays()
+ C.needs_update = FALSE
+
+ DUAL_TICK_CHECK
+
+/datum/controller/subsystem/lighting/proc/internal_process_overlays(resumed = FALSE, init_tick_checks = FALSE)
+ if (!resumed)
+ // We swap out the lists so any additions to the global list during a pause don't make things wierd.
+ src.currentrun = global.lighting_update_overlays
+ global.lighting_update_overlays = list()
+
+ //cache for sanic speed (lists are references anyways)
+ var/list/currentrun = src.currentrun
+ while(currentrun.len)
+ var/atom/movable/lighting_overlay/O = currentrun[currentrun.len]
+ currentrun.len--
+
+ if(!O) continue
+ O.update_overlay()
+ O.needs_update = FALSE
+
+ DUAL_TICK_CHECK
+
+/datum/controller/subsystem/lighting/stat_entry(msg_prefix)
+ var/list/msg = list(msg_prefix)
+ msg += "T:{"
+ msg += "S [total_lighting_sources] | "
+ msg += "C [total_lighting_corners] | "
+ msg += "O [total_lighting_overlays]"
+ msg += "}"
+ msg += "C:{"
+ msg += "S [round(cost_lights, 1)] | "
+ msg += "C [round(cost_corners, 1)] | "
+ msg += "O [round(cost_overlays, 1)]"
+ msg += "}"
+ ..(msg.Join())
+
+#undef DUAL_TICK_CHECK
+#undef SSLIGHTING_STAGE_LIGHTS
+#undef SSLIGHTING_STAGE_CORNERS
+#undef SSLIGHTING_STAGE_OVERLAYS
+#undef SSLIGHTING_STAGE_STATS
diff --git a/code/modules/lighting/lighting_overlay.dm b/code/modules/lighting/lighting_overlay.dm
index 690eba93264..e02f9762810 100644
--- a/code/modules/lighting/lighting_overlay.dm
+++ b/code/modules/lighting/lighting_overlay.dm
@@ -125,7 +125,7 @@
if (force)
total_lighting_overlays--
global.lighting_update_overlays -= src
- global.lighting_update_overlays_old -= src
+ LAZYREMOVE(SSlighting.currentrun, src)
var/turf/T = loc
if(istype(T))
diff --git a/code/modules/lighting/lighting_setup.dm b/code/modules/lighting/lighting_setup.dm
index 981de658649..5d1208f8ef8 100644
--- a/code/modules/lighting/lighting_setup.dm
+++ b/code/modules/lighting/lighting_setup.dm
@@ -1,16 +1,11 @@
+// Create lighting overlays on all turfs with dynamic lighting in areas with dynamic lighting.
/proc/create_all_lighting_overlays()
- for(var/zlevel = 1 to world.maxz)
- create_lighting_overlays_zlevel(zlevel)
-
-/proc/create_lighting_overlays_zlevel(var/zlevel)
- ASSERT(zlevel)
-
- for(var/turf/T in block(locate(1, 1, zlevel), locate(world.maxx, world.maxy, zlevel)))
- if(!T.dynamic_lighting)
- continue
-
- var/area/A = T.loc
+ for(var/area/A in world)
if(!A.dynamic_lighting)
continue
-
- new /atom/movable/lighting_overlay(T, TRUE)
+ for(var/turf/T in A)
+ if(!T.dynamic_lighting)
+ continue
+ new /atom/movable/lighting_overlay(T, TRUE)
+ CHECK_TICK
+ CHECK_TICK
diff --git a/code/modules/vore/appearance/sprite_accessories_vr.dm b/code/modules/vore/appearance/sprite_accessories_vr.dm
index 635f7b6bacb..1cda4165034 100644
--- a/code/modules/vore/appearance/sprite_accessories_vr.dm
+++ b/code/modules/vore/appearance/sprite_accessories_vr.dm
@@ -578,7 +578,13 @@
/datum/sprite_accessory/tail/rosey
name = "tritail kitsune tails (Rosey)"
desc = ""
- icon_state = "rosey"
+ icon_state = "rosey_three"
+ ckeys_allowed = list("joey4298")
+
+/datum/sprite_accessory/tail/rosey2
+ name = "pentatail kitsune tails (Rosey)" //I predict seven tails next. ~CK
+ desc = ""
+ icon_state = "rosey_five"
ckeys_allowed = list("joey4298")
/datum/sprite_accessory/tail/scree
diff --git a/code/modules/vore/eating/belly_vr.dm b/code/modules/vore/eating/belly_vr.dm
index 09d81144adc..b62229bf6a8 100644
--- a/code/modules/vore/eating/belly_vr.dm
+++ b/code/modules/vore/eating/belly_vr.dm
@@ -183,6 +183,8 @@
var/raw_message = pick(examine_messages)
formatted_message = replacetext(raw_message,"%belly",lowertext(name))
+ formatted_message = replacetext(formatted_message,"%pred",owner)
+ formatted_message = replacetext(formatted_message,"%prey",english_list(internal_contents))
return("[formatted_message]
")
diff --git a/code/modules/vore/eating/vorepanel_vr.dm b/code/modules/vore/eating/vorepanel_vr.dm
index 911f956781c..b73e021eb34 100644
--- a/code/modules/vore/eating/vorepanel_vr.dm
+++ b/code/modules/vore/eating/vorepanel_vr.dm
@@ -522,7 +522,7 @@
selected.set_messages(new_message,"smi")
if("Examine Message (when full)")
- var/new_message = input(user,"These are sent to people who examine you when this belly has contents. Write them in 3rd person ('Their %belly is bulging'). Do not use %pred or %prey in this type."+help,"Examine Message (when full)",selected.get_messages("em")) as message
+ var/new_message = input(user,"These are sent to people who examine you when this belly has contents. Write them in 3rd person ('Their %belly is bulging')."+help,"Examine Message (when full)",selected.get_messages("em")) as message
if(new_message)
selected.set_messages(new_message,"em")
diff --git a/icons/mob/vore/tails_vr.dmi b/icons/mob/vore/tails_vr.dmi
index e2def812c6b..b6b1d6e41f1 100644
Binary files a/icons/mob/vore/tails_vr.dmi and b/icons/mob/vore/tails_vr.dmi differ
diff --git a/vorestation.dme b/vorestation.dme
index a26c0256367..abd0b5fe553 100644
--- a/vorestation.dme
+++ b/vorestation.dme
@@ -171,7 +171,6 @@
#include "code\controllers\Processes\event.dm"
#include "code\controllers\Processes\game_master.dm"
#include "code\controllers\Processes\inactivity.dm"
-#include "code\controllers\Processes\lighting.dm"
#include "code\controllers\Processes\machinery.dm"
#include "code\controllers\Processes\nanoui.dm"
#include "code\controllers\Processes\obj.dm"
@@ -188,6 +187,7 @@
#include "code\controllers\ProcessScheduler\core\processScheduler.dm"
#include "code\controllers\subsystems\garbage.dm"
#include "code\controllers\subsystems\mobs.dm"
+#include "code\controllers\subsystems\lighting.dm"
#include "code\controllers\subsystems\transcore_vr.dm"
#include "code\datums\ai_law_sets.dm"
#include "code\datums\ai_laws.dm"