[src] dies!")
- PoolOrNew(/obj/effect/decal/cleanable/spiderling_remains, src.loc)
+ getFromPool(/obj/effect/decal/cleanable/spiderling_remains, src.loc)
qdel(src)
/obj/effect/spider/spiderling/healthcheck()
diff --git a/code/game/objects/empulse.dm b/code/game/objects/empulse.dm
index c1df6b5454b..ad666f88637 100644
--- a/code/game/objects/empulse.dm
+++ b/code/game/objects/empulse.dm
@@ -15,7 +15,7 @@ proc/empulse(turf/epicenter, heavy_range, light_range, log=0)
log_game("EMP with size ([heavy_range], [light_range]) in area [epicenter.loc.name] ")
if(heavy_range > 1)
- var/obj/effect/overlay/pulse = PoolOrNew(/obj/effect/overlay, epicenter)
+ var/obj/effect/overlay/pulse = getFromPool(/obj/effect/overlay, epicenter)
pulse.icon = 'icons/effects/effects.dmi'
pulse.icon_state = "emppulse"
pulse.name = "emp pulse"
diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm
index 3733bec1546..62b7ace7012 100644
--- a/code/game/objects/items/devices/PDA/PDA.dm
+++ b/code/game/objects/items/devices/PDA/PDA.dm
@@ -12,8 +12,8 @@ var/global/list/obj/item/device/pda/PDAs = list()
w_class = 2.0
slot_flags = SLOT_ID | SLOT_BELT
sprite_sheets = list("Resomi" = 'icons/mob/species/resomi/id.dmi')
- offset_light = 1
- diona_restricted_light = 1//Light emitted by this object or creature has limited interaction with diona
+ //offset_light = 1
+ //diona_restricted_light = 1//Light emitted by this object or creature has limited interaction with diona
//Main variables
var/owner = null
@@ -49,6 +49,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
var/list/conversations = list() // For keeping up with who we have PDA messsages from.
var/new_message = 0 //To remove hackish overlay check
var/new_news = 0
+ var/pdafilter = 0 //0-all,1-synth,2-command,3-sec,4-eng,5-sci,6-cargo,7-service,8-med
var/active_feed // The selected feed
var/list/warrant // The warrant as we last knew it
@@ -440,12 +441,39 @@ var/global/list/obj/item/device/pda/PDAs = list()
if(conversations.Find("\ref[P]"))
convopdas.Add(list(list("Name" = "[P]", "Reference" = "\ref[P]", "Detonate" = "[P.detonate]", "inconvo" = "1")))
else
- pdas.Add(list(list("Name" = "[P]", "Reference" = "\ref[P]", "Detonate" = "[P.detonate]", "inconvo" = "0")))
+ switch(pdafilter)
+ if(0) //All
+ pdas.Add(list(list("Name" = "[P]", "Reference" = "\ref[P]", "Detonate" = "[P.detonate]", "inconvo" = "0")))
+ if(1) //Synth -- Not working
+ if(P == /obj/item/device/pda/ai)
+ pdas.Add(list(list("Name" = "[P]", "Reference" = "\ref[P]", "Detonate" = "[P.detonate]", "inconvo" = "0")))
+ if(2) //Command
+ if(P.icon_state == "pda-hop"||P.icon_state == "pda-hos"||P.icon_state == "pda-ce"||P.icon_state == "pda-cmo"||P.icon_state == "pda-rd"||P.icon_state == "pda-c"||P.icon_state == "pda-h")
+ pdas.Add(list(list("Name" = "[P]", "Reference" = "\ref[P]", "Detonate" = "[P.detonate]", "inconvo" = "0")))
+ if(3) //sec
+ if(P.icon_state == "pda-hos"||P.icon_state == "pda-warden"||P.icon_state == "pda-det"||P.icon_state == "pda-sec"||P.icon_state == "pda-s")
+ pdas.Add(list(list("Name" = "[P]", "Reference" = "\ref[P]", "Detonate" = "[P.detonate]", "inconvo" = "0")))
+ if(4) //eng
+ if(P.icon_state == "pda-ce"||P.icon_state == "pda-e"||P.icon_state == "pda-atmo")
+ pdas.Add(list(list("Name" = "[P]", "Reference" = "\ref[P]", "Detonate" = "[P.detonate]", "inconvo" = "0")))
+ if(5) //sci
+ if(P.icon_state == "pda-rd"||P.icon_state == "pda-tox"||P.icon_state == "pda-v"||P.icon_state == "pda-robot")
+ pdas.Add(list(list("Name" = "[P]", "Reference" = "\ref[P]", "Detonate" = "[P.detonate]", "inconvo" = "0")))
+ if(6) //cargo
+ if(P.icon_state == "pda-hop"||P.icon_state == "pda-cargo"||P.icon_state == "pda-q"||P.icon_state == "pda-miner")
+ pdas.Add(list(list("Name" = "[P]", "Reference" = "\ref[P]", "Detonate" = "[P.detonate]", "inconvo" = "0")))
+ if(7) //service
+ if(P.icon_state == "pda-hop"||P.icon_state == "pda-j"||P.icon_state == "pda-bar"||P.icon_state == "pda-holy"||P.icon_state == "pda-lawyer"||P.icon_state == "pda-libb"||P.icon_state == "pda-hydro"||P.icon_state == "pda-chef")
+ pdas.Add(list(list("Name" = "[P]", "Reference" = "\ref[P]", "Detonate" = "[P.detonate]", "inconvo" = "0")))
+ if(8) //medical
+ if(P.icon_state == "pda-cmo"||P.icon_state == "pda-v"||P.icon_state == "pda-m"||P.icon_state == "pda-chem")
+ pdas.Add(list(list("Name" = "[P]", "Reference" = "\ref[P]", "Detonate" = "[P.detonate]", "inconvo" = "0")))
count++
data["convopdas"] = convopdas
data["pdas"] = pdas
data["pda_count"] = count
+ data["pdafilter"] = pdafilter
if(mode==21)
data["messagescount"] = tnote.len
@@ -702,7 +730,12 @@ var/global/list/obj/item/device/pda/PDAs = list()
active_conversation = null
if(mode==21)
mode=2
-
+
+
+ if("Filter") // Filters through available pdas
+ if (href_list["option"])
+ pdafilter = sanitize_integer(text2num(href_list["option"]), 0, 8, pdafilter)
+
if("Ringtone")
var/t = input(U, "Please enter new ringtone", name, ttone) as text|null
if (in_range(src, U) && loc == U)
diff --git a/code/game/objects/items/devices/chameleonproj.dm b/code/game/objects/items/devices/chameleonproj.dm
index 5b694e19184..0bdfa6af1b8 100644
--- a/code/game/objects/items/devices/chameleonproj.dm
+++ b/code/game/objects/items/devices/chameleonproj.dm
@@ -46,7 +46,7 @@
qdel(active_dummy)
active_dummy = null
usr << "You deactivate the [src]."
- var/obj/effect/overlay/T = PoolOrNew(/obj/effect/overlay, get_turf(src))
+ var/obj/effect/overlay/T = getFromPool(/obj/effect/overlay, get_turf(src))
T.icon = 'icons/effects/effects.dmi'
flick("emppulse",T)
spawn(8) qdel(T)
@@ -54,7 +54,7 @@
playsound(get_turf(src), 'sound/effects/pop.ogg', 100, 1, -6)
var/obj/O = new saved_item(src)
if(!O) return
- var/obj/effect/dummy/chameleon/C = PoolOrNew(/obj/effect/dummy/chameleon, usr.loc)
+ var/obj/effect/dummy/chameleon/C = getFromPool(/obj/effect/dummy/chameleon, usr.loc)
C.activate(O, usr, saved_icon, saved_icon_state, saved_overlays, src)
qdel(O)
usr << "You activate the [src]."
diff --git a/code/game/objects/items/devices/flash.dm b/code/game/objects/items/devices/flash.dm
index 90e5dc40224..a7270b39c22 100644
--- a/code/game/objects/items/devices/flash.dm
+++ b/code/game/objects/items/devices/flash.dm
@@ -86,7 +86,7 @@
var/obj/item/organ/eyes/E = H.internal_organs_by_name["eyes"]
if(!E)
return
- usr << "\red Your eyes burn with the intense light of the flash!."
+ usr << span("alert", "Your eyes burn with the intense light of the flash!.")
E.damage += rand(10, 11)
if(E.damage > 12)
M.eye_blurry += rand(3,6)
diff --git a/code/game/objects/items/devices/flashlight.dm b/code/game/objects/items/devices/flashlight.dm
index a45cf6fe92b..bda57dd9cff 100644
--- a/code/game/objects/items/devices/flashlight.dm
+++ b/code/game/objects/items/devices/flashlight.dm
@@ -7,14 +7,15 @@
w_class = 2
flags = CONDUCT
slot_flags = SLOT_BELT
- offset_light = 1
- diona_restricted_light = 1//Light emitted by this object or creature has limited interaction with diona
+ light_color = LIGHT_COLOR_HALOGEN
+ //offset_light = 1
+ //diona_restricted_light = 1//Light emitted by this object or creature has limited interaction with diona
matter = list(DEFAULT_WALL_MATERIAL = 50,"glass" = 20)
action_button_name = "Toggle Flashlight"
var/on = 0
- var/brightness_on = 4 //luminosity when on
+ var/brightness_on = 3 //luminosity when on
/obj/item/device/flashlight/initialize()
..()
@@ -23,10 +24,10 @@
/obj/item/device/flashlight/update_icon()
if(on)
icon_state = "[initial(icon_state)]-on"
- set_light(brightness_on)
+ set_light(brightness_on, update_type = UPDATE_NOW)
else
icon_state = "[initial(icon_state)]"
- set_light(0)
+ set_light(0, update_type = UPDATE_NOW)
/obj/item/device/flashlight/attack_self(mob/user)
if(!isturf(user.loc))
@@ -110,7 +111,7 @@
desc = "A high-luminosity flashlight for specialist duties."
icon_state = "heavyflashlight"
item_state = "heavyflashlight"
- brightness_on = 7
+ brightness_on = 4
w_class = 3
matter = list(DEFAULT_WALL_MATERIAL = 100,"glass" = 70)
contained_sprite = 1
@@ -166,15 +167,15 @@
w_class = 2.0
brightness_on = 8 // Pretty bright.
light_power = 3
- light_color = "#e58775"
+ light_color = LIGHT_COLOR_FLARE
icon_state = "flare"
item_state = "flare"
action_button_name = null //just pull it manually, neckbeard.
var/fuel = 0
var/on_damage = 7
var/produce_heat = 1500
- offset_light = 0//Emits light all around, not directional
- diona_restricted_light = 0
+ //offset_light = 0//Emits light all around, not directional
+ //diona_restricted_light = 0
/obj/item/device/flashlight/flare/New()
fuel = rand(800, 1000) // Sorry for changing this so much but I keep under-estimating how long X number of ticks last in seconds.
@@ -224,12 +225,13 @@
w_class = 1
brightness_on = 6
on = 1 //Bio-luminesence has one setting, on.
- offset_light = 0//Emits light all around, not directional
- diona_restricted_light = 0
+ light_color = LIGHT_COLOR_SLIME_LAMP
+ //offset_light = 0//Emits light all around, not directional
+ //diona_restricted_light = 0
/obj/item/device/flashlight/slime/New()
..()
- set_light(brightness_on)
+ set_light(brightness_on, update_type = UPDATE_NOW)
/obj/item/device/flashlight/slime/update_icon()
return
@@ -243,15 +245,15 @@
name = "green glowstick"
desc = "A green military-grade glowstick."
w_class = 2
- brightness_on = 3
- light_power = 2
+ brightness_on = 1.5
+ light_power = 1
light_color = "#49F37C"
icon = 'icons/obj/glowsticks.dmi'
icon_state = "glowstick"
item_state = "glowstick"
contained_sprite = 1
- offset_light = 0
- diona_restricted_light = 0
+ //offset_light = 0
+ //diona_restricted_light = 0
var/fuel = 0
/obj/item/device/flashlight/glowstick/New()
@@ -296,27 +298,27 @@
/obj/item/device/flashlight/glowstick/red
name = "red glowstick"
desc = "A red military-grade glowstick."
- light_color = "#FC0F29"
+ light_color = LIGHT_COLOR_RED //"#FC0F29"
icon_state = "glowstick_red"
item_state = "glowstick_red"
/obj/item/device/flashlight/glowstick/blue
name = "blue glowstick"
desc = "A blue military-grade glowstick."
- light_color = "#599DFF"
+ light_color = LIGHT_COLOR_BLUE //"#599DFF"
icon_state = "glowstick_blue"
item_state = "glowstick_blue"
/obj/item/device/flashlight/glowstick/orange
name = "orange glowstick"
desc = "A orange military-grade glowstick."
- light_color = "#FA7C0B"
+ light_color = LIGHT_COLOR_ORANGE//"#FA7C0B"
icon_state = "glowstick_orange"
item_state = "glowstick_orange"
/obj/item/device/flashlight/glowstick/yellow
name = "yellow glowstick"
desc = "A yellow military-grade glowstick."
- light_color = "#FEF923"
+ light_color = LIGHT_COLOR_YELLOW //"#FEF923"
icon_state = "glowstick_yellow"
item_state = "glowstick_yellow"
diff --git a/code/game/objects/items/devices/lightmeter.dm b/code/game/objects/items/devices/lightmeter.dm
new file mode 100644
index 00000000000..0807bcd5bf8
--- /dev/null
+++ b/code/game/objects/items/devices/lightmeter.dm
@@ -0,0 +1,55 @@
+// Light meter, intended for debugging.
+
+/obj/item/device/light_meter
+ name = "light meter"
+ desc = "A simple device that measures ambient light levels."
+ icon = 'icons/obj/device.dmi'
+ icon_state = "locator"
+
+ // Copied from debugger.dm
+ flags = CONDUCT
+ force = 5.0
+ w_class = 2.0
+ throwforce = 5.0
+ throw_range = 15
+ throw_speed = 3
+ matter = list(DEFAULT_WALL_MATERIAL = 50,"glass" = 20)
+ var/low = 0
+ var/high = 1
+
+/obj/item/device/light_meter/attack_self(mob/user as mob)
+ var/turf/T = get_turf(user.loc)
+ if (!T)
+ user << span("alert", "Unable to read light levels.")
+ return
+
+ var/visible_reading = T.get_lumcount(low, high)
+ var/uv_reading = T.get_uv_lumcount(low, high)
+
+ var/reading = "Light analysis for \the [T].
"
+ reading += "Visible light: [visible_reading] lx
"
+ reading += "Ultraviolet light: [uv_reading] lx ([uv_reading * 5.5 - 1.5] adlx)"
+
+ usr << span("notice", reading)
+
+/obj/item/device/light_meter/verb/set_low_bound()
+ set category = "Object"
+ set name = "Set Detector Low-Bound"
+ set src in usr
+
+ var/num = input(usr, "Please enter the low-bound for the detector") as num|null
+ if (null)
+ return
+
+ low = num
+
+/obj/item/device/light_meter/verb/set_high_bound()
+ set category = "Object"
+ set name = "Set Detector High-Bound"
+ set src in usr
+
+ var/num = input(usr, "Please enter the high-bound for the detector") as num|null
+ if (null)
+ return
+
+ high = num
diff --git a/code/game/objects/items/weapons/extinguisher.dm b/code/game/objects/items/weapons/extinguisher.dm
index 5be1c321a9f..4cdf35254d0 100644
--- a/code/game/objects/items/weapons/extinguisher.dm
+++ b/code/game/objects/items/weapons/extinguisher.dm
@@ -116,7 +116,7 @@
spawn(0)
if(!src || !reagents.total_volume) return
- var/obj/effect/effect/water/W = PoolOrNew(/obj/effect/effect/water, get_turf(src))
+ var/obj/effect/effect/water/W = getFromPool(/obj/effect/effect/water, get_turf(src))
var/turf/my_target
if(a <= the_targets.len)
my_target = the_targets[a]
diff --git a/code/game/objects/items/weapons/flamethrower.dm b/code/game/objects/items/weapons/flamethrower.dm
index bbdd46cdb39..731c2e34602 100644
--- a/code/game/objects/items/weapons/flamethrower.dm
+++ b/code/game/objects/items/weapons/flamethrower.dm
@@ -82,7 +82,7 @@
if(ptank)
ptank.loc = T
ptank = null
- PoolOrNew(/obj/item/stack/rods, T)
+ getFromPool(/obj/item/stack/rods, T)
qdel(src)
return
diff --git a/code/game/objects/items/weapons/grenades/smokebomb.dm b/code/game/objects/items/weapons/grenades/smokebomb.dm
index 704df83db8a..f354cb607f1 100644
--- a/code/game/objects/items/weapons/grenades/smokebomb.dm
+++ b/code/game/objects/items/weapons/grenades/smokebomb.dm
@@ -10,7 +10,7 @@
/obj/item/weapon/grenade/smokebomb/New()
..()
- src.smoke = PoolOrNew(/datum/effect/effect/system/smoke_spread/bad)
+ src.smoke = getFromPool(/datum/effect/effect/system/smoke_spread/bad)
src.smoke.attach(src)
/obj/item/weapon/grenade/smokebomb/Destroy()
diff --git a/code/game/objects/items/weapons/shields.dm b/code/game/objects/items/weapons/shields.dm
index 2d56c460a1d..4e9be5f1673 100644
--- a/code/game/objects/items/weapons/shields.dm
+++ b/code/game/objects/items/weapons/shields.dm
@@ -111,7 +111,7 @@
. = ..()
if(.)
- var/datum/effect/effect/system/spark_spread/spark_system = PoolOrNew(/datum/effect/effect/system/spark_spread)
+ var/datum/effect/effect/system/spark_spread/spark_system = getFromPool(/datum/effect/effect/system/spark_spread)
spark_system.set_up(5, 0, user.loc)
spark_system.start()
playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1)
diff --git a/code/game/objects/items/weapons/storage/toolbox.dm b/code/game/objects/items/weapons/storage/toolbox.dm
index 09ddfaea85b..0265f40d5ea 100644
--- a/code/game/objects/items/weapons/storage/toolbox.dm
+++ b/code/game/objects/items/weapons/storage/toolbox.dm
@@ -105,3 +105,80 @@
update_force()
user.visible_message(span("danger", "[user] smashes the [src] into [M], causing it to break open and strew its contents across the area"))
+
+/obj/item/weapon/storage/toolbox/lunchbox
+ name = "rainbow lunchbox"
+ icon = 'icons/obj/lunchbox.dmi'
+ force = 3
+ throwforce = 5
+ contained_sprite = 1
+ icon_state = "lunchbox_rainbow"
+ item_state = "lunchbox_rainbow"
+ desc = "A little lunchbox. This one is the colors of the rainbow."
+ attack_verb = list("lunched")
+ w_class = 3
+ max_storage_space = 8
+ var/filled = FALSE
+
+/obj/item/weapon/storage/toolbox/lunchbox/New()
+ ..()
+ if(filled)
+ var/list/lunches = lunchables_lunches()
+ var/lunch = lunches[pick(lunches)]
+ new lunch(src)
+
+ var/list/snacks = lunchables_snacks()
+ var/snack = snacks[pick(snacks)]
+ new snack(src)
+
+ var/list/drinks = lunchables_drinks()
+ var/drink = drinks[pick(drinks)]
+ new drink(src)
+
+/obj/item/weapon/storage/toolbox/lunchbox/filled
+ filled = TRUE
+
+/obj/item/weapon/storage/toolbox/lunchbox/heart
+ name = "heart lunchbox"
+ icon_state = "lunchbox_hearts"
+ item_state = "lunchbox_hearts"
+ desc = "A little lunchbox. This one has little hearts on it."
+
+/obj/item/weapon/storage/toolbox/lunchbox/heart/filled
+ filled = TRUE
+
+/obj/item/weapon/storage/toolbox/lunchbox/cat
+ name = "cat lunchbox"
+ icon_state = "lunchbox_cat"
+ item_state = "lunchbox_cat"
+ desc = "A little lunchbox. This one has a cat stamp on it."
+
+/obj/item/weapon/storage/toolbox/lunchbox/cat/filled
+ filled = TRUE
+
+/obj/item/weapon/storage/toolbox/lunchbox/nt
+ name = "NanoTrasen brand lunchbox"
+ icon_state = "lunchbox_nanotrasen"
+ item_state = "lunchbox_nanotrasen"
+ desc = "A little lunchbox. This one is branded with the NanoTrasen logo."
+
+/obj/item/weapon/storage/toolbox/lunchbox/nt/filled
+ filled = TRUE
+
+/obj/item/weapon/storage/toolbox/lunchbox/nymph
+ name = "\improper diona nymph lunchbox"
+ icon_state = "lunchbox_dionanymph"
+ item_state = "lunchbox_dionanymph"
+ desc = "A little lunchbox. This one has a diona nymph on the side."
+
+/obj/item/weapon/storage/toolbox/lunchbox/nymph/filled
+ filled = TRUE
+
+/obj/item/weapon/storage/toolbox/lunchbox/syndicate
+ name = "black and red lunchbox"
+ icon_state = "lunchbox_syndie"
+ item_state = "lunchbox_syndie"
+ desc = "A little lunchbox. This one is a sleek black and red."
+
+/obj/item/weapon/storage/toolbox/lunchbox/syndicate/filled
+ filled = TRUE
diff --git a/code/game/objects/structures/curtains.dm b/code/game/objects/structures/curtains.dm
index f8e8a0a414b..bba9fb776e9 100644
--- a/code/game/objects/structures/curtains.dm
+++ b/code/game/objects/structures/curtains.dm
@@ -27,7 +27,7 @@
..()
/obj/structure/curtain/proc/toggle()
- opacity = !opacity
+ src.set_opacity(!src.opacity)
if(opacity)
icon_state = "closed"
layer = SHOWER_CLOSED_LAYER
diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm
index dcf500d1541..4d9e4ec5526 100644
--- a/code/game/objects/structures/grille.dm
+++ b/code/game/objects/structures/grille.dm
@@ -96,7 +96,7 @@
if(iswirecutter(W))
if(!shock(user, 100))
playsound(loc, 'sound/items/Wirecutter.ogg', 100, 1)
- PoolOrNew(/obj/item/stack/rods, list(get_turf(src), destroyed ? 1 : 2))
+ getFromPool(/obj/item/stack/rods, list(get_turf(src), destroyed ? 1 : 2))
qdel(src)
else if((isscrewdriver(W)) && (istype(loc, /turf/simulated) || anchored))
if(!shock(user, 90))
@@ -151,7 +151,7 @@
else if(!(W.flags & CONDUCT) || !shock(user, 70))
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
- user.do_attack_animation(src)
+ user.do_attack_animation(src)
playsound(loc, 'sound/effects/grillehit.ogg', 80, 1)
switch(W.damtype)
if("fire")
@@ -169,11 +169,11 @@
density = 0
destroyed = 1
update_icon()
- PoolOrNew(/obj/item/stack/rods, get_turf(src))
+ getFromPool(/obj/item/stack/rods, get_turf(src))
else
if(health <= -6)
- PoolOrNew(/obj/item/stack/rods, get_turf(src))
+ getFromPool(/obj/item/stack/rods, get_turf(src))
qdel(src)
return
return
diff --git a/code/game/objects/structures/lattice.dm b/code/game/objects/structures/lattice.dm
index 3ead5503314..4c31a8530fb 100644
--- a/code/game/objects/structures/lattice.dm
+++ b/code/game/objects/structures/lattice.dm
@@ -58,7 +58,7 @@
var/obj/item/weapon/weldingtool/WT = C
if(WT.remove_fuel(0, user))
user << "Slicing lattice joints ..."
- PoolOrNew(/obj/item/stack/rods, src.loc)
+ getFromPool(/obj/item/stack/rods, src.loc)
qdel(src)
return
diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm
index 3245234fc3e..1742cd09b0f 100644
--- a/code/game/objects/structures/watercloset.dm
+++ b/code/game/objects/structures/watercloset.dm
@@ -180,13 +180,13 @@
spawn(50)
if(src && on)
ismist = 1
- mymist = PoolOrNew(/obj/effect/mist,loc)
+ mymist = getFromPool(/obj/effect/mist,loc)
else
ismist = 1
- mymist = PoolOrNew(/obj/effect/mist,loc)
+ mymist = getFromPool(/obj/effect/mist,loc)
else if(ismist)
ismist = 1
- mymist = PoolOrNew(/obj/effect/mist,loc)
+ mymist = getFromPool(/obj/effect/mist,loc)
spawn(250)
if(src && !on)
qdel(mymist)
diff --git a/code/game/objects/structures/windoor_assembly.dm b/code/game/objects/structures/windoor_assembly.dm
index fb48f6249dd..dbf92c1b11c 100644
--- a/code/game/objects/structures/windoor_assembly.dm
+++ b/code/game/objects/structures/windoor_assembly.dm
@@ -79,7 +79,7 @@ obj/structure/windoor_assembly/Destroy()
user << "You dissasembled the windoor assembly!"
new /obj/item/stack/material/glass/reinforced(get_turf(src), 5)
if(secure)
- PoolOrNew(/obj/item/stack/rods, list(get_turf(src), 4))
+ getFromPool(/obj/item/stack/rods, list(get_turf(src), 4))
qdel(src)
else
user << "You need more welding fuel to dissassemble the windoor assembly."
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index f93fb39ce59..262e9414378 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -91,11 +91,11 @@
index = 0
while(index < 2)
new shardtype(loc) //todo pooling?
- if(reinf) PoolOrNew(/obj/item/stack/rods, loc)
+ if(reinf) getFromPool(/obj/item/stack/rods, loc)
index++
else
new shardtype(loc) //todo pooling?
- if(reinf) PoolOrNew(/obj/item/stack/rods, loc)
+ if(reinf) getFromPool(/obj/item/stack/rods, loc)
qdel(src)
return
diff --git a/code/game/objects/structures/window_spawner.dm b/code/game/objects/structures/window_spawner.dm
index c6e2d69f842..a1cec9eb55c 100644
--- a/code/game/objects/structures/window_spawner.dm
+++ b/code/game/objects/structures/window_spawner.dm
@@ -35,7 +35,7 @@
/obj/effect/wingrille_spawn/proc/activate()
if(activated) return
if (!locate(/obj/structure/grille) in get_turf(src))
- var/obj/structure/grille/G = PoolOrNew(/obj/structure/grille, src.loc)
+ var/obj/structure/grille/G = getFromPool(/obj/structure/grille, src.loc)
handle_grille_spawn(G)
var/list/neighbours = list()
for (var/dir in cardinal)
@@ -49,7 +49,7 @@
found_connection = 1
qdel(W)
if(!found_connection)
- var/obj/structure/window/new_win = PoolOrNew(win_path, src.loc)
+ var/obj/structure/window/new_win = getFromPool(win_path, src.loc)
new_win.set_dir(dir)
handle_window_spawn(new_win)
else
diff --git a/code/game/turfs/initialization/maintenance.dm b/code/game/turfs/initialization/maintenance.dm
index 16b1d03d21e..833c0cd68dc 100644
--- a/code/game/turfs/initialization/maintenance.dm
+++ b/code/game/turfs/initialization/maintenance.dm
@@ -15,9 +15,9 @@
T.update_dirt()
if(prob(2))
- PoolOrNew(junk(), T)
+ getFromPool(junk(), T)
if(prob(2))
- PoolOrNew(/obj/effect/decal/cleanable/blood/oil, T)
+ getFromPool(/obj/effect/decal/cleanable/blood/oil, T)
if(prob(25)) // Keep in mind that only "corners" get any sort of web
attempt_web(T, cardinal_turfs)
@@ -54,7 +54,7 @@ var/global/list/random_junk
var/turf/neighbour = get_step(T, dir)
if(neighbour && neighbour.density)
if(dir == WEST)
- PoolOrNew(/obj/effect/decal/cleanable/cobweb, T)
+ getFromPool(/obj/effect/decal/cleanable/cobweb, T)
if(dir == EAST)
- PoolOrNew(/obj/effect/decal/cleanable/cobweb2, T)
+ getFromPool(/obj/effect/decal/cleanable/cobweb2, T)
return
diff --git a/code/game/turfs/simulated/wall_attacks.dm b/code/game/turfs/simulated/wall_attacks.dm
index 9264b1a0afd..fee8eeb96d2 100644
--- a/code/game/turfs/simulated/wall_attacks.dm
+++ b/code/game/turfs/simulated/wall_attacks.dm
@@ -9,14 +9,14 @@
//flick("[material.icon_base]fwall_opening", src)
sleep(15)
density = 0
- opacity = 0
+ set_opacity(0)
update_icon()
set_light(0)
else
can_open = WALL_OPENING
//flick("[material.icon_base]fwall_closing", src)
density = 1
- opacity = 1
+ set_opacity(1)
update_icon()
sleep(15)
set_light(1)
diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm
index 76be9e556ec..9031d62a856 100644
--- a/code/game/turfs/space/space.dm
+++ b/code/game/turfs/space/space.dm
@@ -1,17 +1,24 @@
/turf/space
icon = 'icons/turf/space.dmi'
name = "\proper space"
+ desc = "The final frontier."
icon_state = "0"
dynamic_lighting = 0
footstep_sound = null //Override to make sure because yeah
+ plane = PLANE_SPACE_BACKGROUND
+
temperature = T20C
thermal_conductivity = OPEN_HEAT_TRANSFER_COEFFICIENT
// heat_capacity = 700000 No.
/turf/space/New()
- if(!istype(src, /turf/space/transit))
- icon_state = "[((x + y) ^ ~(x * y) + z) % 25]"
+ icon_state = "[((x + y) ^ ~(x * y) + z) % 25]"
+ var/image/I = image('icons/turf/space_parallax1.dmi',"[icon_state]")
+ I.plane = PLANE_SPACE_DUST
+ I.alpha = 80
+ I.blend_mode = BLEND_ADD
+ overlays += I
update_starlight()
..()
diff --git a/code/game/turfs/space/transit.dm b/code/game/turfs/space/transit.dm
index d84b33dbfe9..ec46a81207d 100644
--- a/code/game/turfs/space/transit.dm
+++ b/code/game/turfs/space/transit.dm
@@ -1,77 +1,100 @@
/turf/space/transit
var/pushdirection // push things that get caught in the transit tile this direction
+ plane = 0
+
+/turf/space/transit/New()
+ update_icon()
+
+/turf/space/transit/update_icon()
+ icon_state = ""
+
+ var/dira=""
+ var/i=0
+ switch(pushdirection)
+ if(SOUTH) // North to south
+ dira="ns"
+ i=1+(abs((x^2)-y)%15) // Vary widely across X, but just decrement across Y
+
+ if(NORTH) // South to north I HAVE NO IDEA HOW THIS WORKS I'M SORRY. -Probe
+ dira="ns"
+ i=1+(abs((x^2)-y)%15) // Vary widely across X, but just decrement across Y
+
+ if(WEST) // East to west
+ dira="ew"
+ i=1+(((y^2)+x)%15) // Vary widely across Y, but just increment across X
+
+ if(EAST) // West to east
+ dira="ew"
+ i=1+(((y^2)-x)%15) // Vary widely across Y, but just increment across X
+
+
+ /*
+ if(NORTH) // South to north (SPRITES DO NOT EXIST!)
+ dira="sn"
+ i=1+(((x^2)+y)%15) // Vary widely across X, but just increment across Y
+
+ if(EAST) // West to east (SPRITES DO NOT EXIST!)
+ dira="we"
+ i=1+(abs((y^2)-x)%15) // Vary widely across X, but just increment across Y
+ */
+
+ else
+ icon_state="black"
+ if(icon_state != "black")
+ icon_state = "speedspace_[dira]_[i]"
+
+/turf/space/transit/ChangeTurf(var/turf/N, var/tell_universe=1, var/force_lighting_update = 0, var/allow = 0)
+ return ..(N, tell_universe, 1, allow)
//Overwrite because we dont want people building rods in space.
/turf/space/transit/attackby(obj/O as obj, mob/user as mob)
- return
+ return 0
/turf/space/transit/north // moving to the north
pushdirection = SOUTH // south because the space tile is scrolling south
+ icon_state="debug-north"
- //IF ANYONE KNOWS A MORE EFFICIENT WAY OF MANAGING THESE SPRITES, BE MY GUEST.
+ // Isn't legacy code fun.
+ // These are here so I don't have to remap all this shit.
shuttlespace_ns1
- icon_state = "speedspace_ns_1"
shuttlespace_ns2
- icon_state = "speedspace_ns_2"
shuttlespace_ns3
- icon_state = "speedspace_ns_3"
shuttlespace_ns4
- icon_state = "speedspace_ns_4"
shuttlespace_ns5
- icon_state = "speedspace_ns_5"
shuttlespace_ns6
- icon_state = "speedspace_ns_6"
shuttlespace_ns7
- icon_state = "speedspace_ns_7"
shuttlespace_ns8
- icon_state = "speedspace_ns_8"
shuttlespace_ns9
- icon_state = "speedspace_ns_9"
shuttlespace_ns10
- icon_state = "speedspace_ns_10"
shuttlespace_ns11
- icon_state = "speedspace_ns_11"
shuttlespace_ns12
- icon_state = "speedspace_ns_12"
shuttlespace_ns13
- icon_state = "speedspace_ns_13"
shuttlespace_ns14
- icon_state = "speedspace_ns_14"
shuttlespace_ns15
- icon_state = "speedspace_ns_15"
+
+/turf/space/transit/south // moving to the south
+
+ pushdirection = NORTH
+ icon_state="debug-south"
/turf/space/transit/east // moving to the east
pushdirection = WEST
+ icon_state="debug-east"
shuttlespace_ew1
- icon_state = "speedspace_ew_1"
shuttlespace_ew2
- icon_state = "speedspace_ew_2"
shuttlespace_ew3
- icon_state = "speedspace_ew_3"
shuttlespace_ew4
- icon_state = "speedspace_ew_4"
shuttlespace_ew5
- icon_state = "speedspace_ew_5"
shuttlespace_ew6
- icon_state = "speedspace_ew_6"
shuttlespace_ew7
- icon_state = "speedspace_ew_7"
shuttlespace_ew8
- icon_state = "speedspace_ew_8"
shuttlespace_ew9
- icon_state = "speedspace_ew_9"
shuttlespace_ew10
- icon_state = "speedspace_ew_10"
shuttlespace_ew11
- icon_state = "speedspace_ew_11"
shuttlespace_ew12
- icon_state = "speedspace_ew_12"
shuttlespace_ew13
- icon_state = "speedspace_ew_13"
shuttlespace_ew14
- icon_state = "speedspace_ew_14"
shuttlespace_ew15
- icon_state = "speedspace_ew_15"
\ No newline at end of file
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index fd41829d79b..8db23214265 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -21,7 +21,6 @@
var/icon_old = null
var/pathweight = 1 // How much does it cost to pathfind over this turf?
var/blessed = 0 // Has the turf been blessed?
- var/dynamic_lighting = 1 // Does the turf use dynamic lighting?
var/footstep_sound = "defaultstep"
diff --git a/code/game/turfs/turf_changing.dm b/code/game/turfs/turf_changing.dm
index 10ffb3a8cc4..04adf11fb94 100644
--- a/code/game/turfs/turf_changing.dm
+++ b/code/game/turfs/turf_changing.dm
@@ -25,6 +25,7 @@
var/old_dynamic_lighting = dynamic_lighting
var/list/old_affecting_lights = affecting_lights
var/old_lighting_overlay = lighting_overlay
+ var/list/old_corners = corners
//world << "Replacing [src.type] with [N]"
@@ -78,10 +79,11 @@
lighting_overlay = old_lighting_overlay
affecting_lights = old_affecting_lights
+ corners = old_corners
if((old_opacity != opacity) || (dynamic_lighting != old_dynamic_lighting) || force_lighting_update)
reconsider_lights()
if(dynamic_lighting != old_dynamic_lighting)
if(dynamic_lighting)
- lighting_build_overlays()
+ lighting_build_overlay()
else
- lighting_clear_overlays()
\ No newline at end of file
+ lighting_clear_overlay()
diff --git a/code/game/turfs/turf_flick_animations.dm b/code/game/turfs/turf_flick_animations.dm
index 94f5fec4c58..fae622460a0 100644
--- a/code/game/turfs/turf_flick_animations.dm
+++ b/code/game/turfs/turf_flick_animations.dm
@@ -5,7 +5,7 @@
location = get_turf(target)
if(location && !target)
target = location
- var/atom/movable/overlay/animation = PoolOrNew(/atom/movable/overlay, location)
+ var/atom/movable/overlay/animation = getFromPool(/atom/movable/overlay, location)
if(direction)
animation.set_dir(direction)
animation.icon = a_icon
diff --git a/code/game/vehicles/vehicle.dm b/code/game/vehicles/vehicle.dm
deleted file mode 100644
index 4088528b35f..00000000000
--- a/code/game/vehicles/vehicle.dm
+++ /dev/null
@@ -1,190 +0,0 @@
-
-
-/obj/vehicle
- name = "Vehicle"
- icon = 'icons/vehicles/vehicles.dmi'
- density = 1
- anchored = 1
- unacidable = 1 //To avoid the pilot-deleting shit that came with mechas
- layer = MOB_LAYER
- //var/can_move = 1
- var/mob/living/carbon/occupant = null
- //var/step_in = 10 //make a step in step_in/10 sec.
- //var/dir_in = 2//What direction will the mech face when entered/powered on? Defaults to South.
- //var/step_energy_drain = 10
- var/health = 300 //health is health
- //var/deflect_chance = 10 //chance to deflect the incoming projectiles, hits, or lesser the effect of ex_act.
- //the values in this list show how much damage will pass through, not how much will be absorbed.
- var/list/damage_absorption = list("brute"=0.8,"fire"=1.2,"bullet"=0.9,"laser"=1,"energy"=1,"bomb"=1)
- var/obj/item/weapon/cell/cell //Our power source
- var/state = 0
- var/list/log = new
- var/last_message = 0
- var/add_req_access = 1
- var/maint_access = 1
- //var/dna //dna-locking the mech
- var/list/proc_res = list() //stores proc owners, like proc_res["functionname"] = owner reference
- var/datum/effect/effect/system/spark_spread/spark_system = new
- var/lights = 0
- var/lights_power = 6
-
- //inner atmos //These go in airtight.dm, not all vehicles are space-faring -Agouri
- //var/use_internal_tank = 0
- //var/internal_tank_valve = ONE_ATMOSPHERE
- //var/obj/machinery/portable_atmospherics/canister/internal_tank
- //var/datum/gas_mixture/cabin_air
- //var/obj/machinery/atmospherics/portables_connector/connected_port = null
-
- var/obj/item/device/radio/radio = null
-
- var/max_temperature = 2500
- //var/internal_damage_threshold = 50 //health percentage below which internal damage is possible
- var/internal_damage = 0 //contains bitflags
-
- var/list/operation_req_access = list()//required access level for mecha operation
- var/list/internals_req_access = list(access_engine,access_robotics)//required access level to open cell compartment
-
- //var/datum/global_iterator/pr_int_temp_processor //normalizes internal air mixture temperature //In airtight.dm you go -Agouri
- var/datum/global_iterator/pr_inertial_movement //controls intertial movement in spesss
-
- //var/datum/global_iterator/pr_give_air //moves air from tank to cabin //Y-you too -Agouri
-
- var/datum/global_iterator/pr_internal_damage //processes internal damage
-
-
- var/wreckage
-
- var/list/equipment = new
- var/obj/selected
- //var/max_equip = 3
-
- var/datum/events/events
-
-
-
-/obj/vehicle/New()
- ..()
- events = new
- icon_state += "-unmanned"
- add_radio()
- //add_cabin() //No cabin for non-airtights
-
- spark_system.set_up(2, 0, src)
- spark_system.attach(src)
- add_cell()
- add_iterators()
- removeVerb(/obj/mecha/verb/disconnect_from_port)
- removeVerb(/atom/movable/verb/pull)
- log_message("[src.name]'s functions initialised. Work protocols active - Entering IDLE mode.")
- loc.Entered(src)
- return
-
-
-//################ Helpers ###########################################################
-
-
-/obj/vehicle/proc/removeVerb(verb_path)
- verbs -= verb_path
-
-/obj/vehicle/proc/addVerb(verb_path)
- verbs += verb_path
-
-/*/obj/vehicle/proc/add_airtank() //In airtight.dm -Agouri
- internal_tank = new /obj/machinery/portable_atmospherics/canister/air(src)
- return internal_tank*/
-
-/obj/vehicle/proc/add_cell(var/obj/item/weapon/cell/C=null)
- if(C)
- C.forceMove(src)
- cell = C
- return
- cell = new(src)
- cell.charge = 15000
- cell.maxcharge = 15000
-
-/*/obj/vehicle/proc/add_cabin() //In airtight.dm -Agouri
- cabin_air = new
- cabin_air.temperature = T20C
- cabin_air.volume = 200
- cabin_air.oxygen = O2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature)
- cabin_air.nitrogen = N2STANDARD*cabin_air.volume/(R_IDEAL_GAS_EQUATION*cabin_air.temperature)
- return cabin_air*/
-
-/obj/vehicle/proc/add_radio()
- radio = new(src)
- radio.name = "[src] radio"
- radio.icon = icon
- radio.icon_state = icon_state
- radio.subspace_transmission = 1
-
-/obj/vehicle/proc/add_iterators()
- pr_inertial_movement = new /datum/global_iterator/vehicle_intertial_movement(null,0)
- //pr_internal_damage = new /datum/global_iterator/vehicle_internal_damage(list(src),0)
- //pr_int_temp_processor = new /datum/global_iterator/vehicle_preserve_temp(list(src)) //In airtight.dm's add_airtight_iterators -Agouri
- //pr_give_air = new /datum/global_iterator/vehicle_tank_give_air(list(src) //Same here -Agouri
-
-/obj/vehicle/proc/check_for_support()
- if(locate(/obj/structure/grille, orange(1, src)) || locate(/obj/structure/lattice, orange(1, src)) || locate(/turf/simulated, orange(1, src)) || locate(/turf/unsimulated, orange(1, src)))
- return 1
- else
- return 0
-
-//################ Logs and messages ############################################
-
-
-/obj/vehicle/proc/log_message(message as text,red=null)
- log.len++
- log[log.len] = list("time"=world.timeofday,"message"="[red?"":null][message][red?"":null]")
- return log.len
-
-
-
-//################ Global Iterator Datums ######################################
-
-
-/datum/global_iterator/vehicle_intertial_movement //inertial movement in space
- delay = 7
-
- process(var/obj/vehicle/V as obj, direction)
- if(direction)
- if(!step(V, direction)||V.check_for_support())
- src.stop()
- else
- src.stop()
- return
-
-
-/datum/global_iterator/mecha_internal_damage // processing internal damage
-
- process(var/obj/mecha/mecha)
- if(!mecha.hasInternalDamage())
- return stop()
- if(mecha.hasInternalDamage(MECHA_INT_FIRE))
- if(!mecha.hasInternalDamage(MECHA_INT_TEMP_CONTROL) && prob(5))
- mecha.clearInternalDamage(MECHA_INT_FIRE)
- if(mecha.internal_tank)
- if(mecha.internal_tank.return_pressure()>mecha.internal_tank.maximum_pressure && !(mecha.hasInternalDamage(MECHA_INT_TANK_BREACH)))
- mecha.setInternalDamage(MECHA_INT_TANK_BREACH)
- var/datum/gas_mixture/int_tank_air = mecha.internal_tank.return_air()
- if(int_tank_air && int_tank_air.return_volume()>0) //heat the air_contents
- int_tank_air.temperature = min(6000+T0C, int_tank_air.temperature+rand(10,15))
- if(mecha.cabin_air && mecha.cabin_air.return_volume()>0)
- mecha.cabin_air.temperature = min(6000+T0C, mecha.cabin_air.return_temperature()+rand(10,15))
- if(mecha.cabin_air.return_temperature()>mecha.max_temperature/2)
- mecha.take_damage(4/round(mecha.max_temperature/mecha.cabin_air.return_temperature(),0.1),"fire")
- if(mecha.hasInternalDamage(MECHA_INT_TEMP_CONTROL)) //stop the mecha_preserve_temp loop datum
- mecha.pr_int_temp_processor.stop()
- if(mecha.hasInternalDamage(MECHA_INT_TANK_BREACH)) //remove some air from internal tank
- if(mecha.internal_tank)
- var/datum/gas_mixture/int_tank_air = mecha.internal_tank.return_air()
- var/datum/gas_mixture/leaked_gas = int_tank_air.remove_ratio(0.10)
- if(mecha.loc && hascall(mecha.loc,"assume_air"))
- mecha.loc.assume_air(leaked_gas)
- else
- qdel(leaked_gas)
- if(mecha.hasInternalDamage(MECHA_INT_SHORT_CIRCUIT))
- if(mecha.get_charge())
- mecha.spark_system.start()
- mecha.cell.charge -= min(20,mecha.cell.charge)
- mecha.cell.maxcharge -= min(20,mecha.cell.maxcharge)
- return
\ No newline at end of file
diff --git a/code/modules/admin/admin_report.dm b/code/modules/admin/admin_report.dm
deleted file mode 100644
index 9bc1c91ef3f..00000000000
--- a/code/modules/admin/admin_report.dm
+++ /dev/null
@@ -1,180 +0,0 @@
-// Reports are a way to notify admins of wrongdoings that happened
-// while no admin was present. They work a bit similar to news, but
-// they can only be read by admins and moderators.
-
-// a single admin report
-datum/admin_report/var
- ID // the ID of the report
- body // the content of the report
- author // key of the author
- date // date on which this was created
- done // whether this was handled
-
- offender_key // store the key of the offender
- offender_cid // store the cid of the offender
-
-datum/report_topic_handler
- Topic(href,href_list)
- ..()
- var/client/C = locate(href_list["client"])
- if(href_list["action"] == "show_reports")
- C.display_admin_reports()
- else if(href_list["action"] == "remove")
- C.mark_report_done(text2num(href_list["ID"]))
- else if(href_list["action"] == "edit")
- C.edit_report(text2num(href_list["ID"]))
-
-var/datum/report_topic_handler/report_topic_handler
-
-world/New()
- ..()
- report_topic_handler = new
-
-// add a new news datums
-proc/make_report(body, author, okey, cid)
- var/savefile/Reports = new("data/reports.sav")
- var/list/reports
- var/lastID
-
- Reports["reports"] >> reports
- Reports["lastID"] >> lastID
-
- if(!reports) reports = list()
- if(!lastID) lastID = 0
-
- var/datum/admin_report/created = new()
- created.ID = ++lastID
- created.body = body
- created.author = author
- created.date = world.realtime
- created.done = 0
- created.offender_key = okey
- created.offender_cid = cid
-
- reports.Insert(1, created)
-
- Reports["reports"] << reports
- Reports["lastID"] << lastID
-
-// load the reports from disk
-proc/load_reports()
- var/savefile/Reports = new("data/reports.sav")
- var/list/reports
-
- Reports["reports"] >> reports
-
- if(!reports) reports = list()
-
- return reports
-
-// check if there are any unhandled reports
-client/proc/unhandled_reports()
- if(!src.holder) return 0
- var/list/reports = load_reports()
-
- for(var/datum/admin_report/N in reports)
- if(N.done)
- continue
- else return 1
-
- return 0
-
-// checks if the player has an unhandled report against him
-client/proc/is_reported()
- var/list/reports = load_reports()
-
- for(var/datum/admin_report/N in reports) if(!N.done)
- if(N.offender_key == src.key)
- return 1
-
- return 0
-
-// display only the reports that haven't been handled
-client/proc/display_admin_reports()
- set category = "Admin"
- set name = "Display Admin Reports"
- if(!src.holder) return
-
- var/list/reports = load_reports()
-
- var/output = ""
- if(unhandled_reports())
- // load the list of unhandled reports
- for(var/datum/admin_report/N in reports)
- if(N.done)
- continue
- output += "Reported player: [N.offender_key](CID: [N.offender_cid])
"
- output += "Offense:[N.body]
"
- output += "Occured at [time2text(N.date,"MM/DD hh:mm:ss")]
"
- output += "authored by [N.author]
"
- output += " Flag as Handled"
- if(src.key == N.author)
- output += " Edit"
- output += "
"
- output += "
"
- else
- output += "Whoops, no reports!"
-
- usr << browse(output, "window=news;size=600x400")
-
-
-client/proc/Report(mob/M as mob in mob_list)
- set category = "Admin"
- if(!src.holder)
- return
-
- var/CID = "Unknown"
- if(M.client)
- CID = M.client.computer_id
-
- var/body = input(src.mob, "Describe in detail what you're reporting [M] for", "Report") as null|text
- if(!body) return
-
-
- make_report(body, key, M.key, CID)
-
- spawn(1)
- display_admin_reports()
-
-client/proc/mark_report_done(ID as num)
- if(!src.holder || src.holder.level < 0)
- return
-
- var/savefile/Reports = new("data/reports.sav")
- var/list/reports
-
- Reports["reports"] >> reports
-
- var/datum/admin_report/found
- for(var/datum/admin_report/N in reports)
- if(N.ID == ID)
- found = N
- if(!found) src << "* An error occured, sorry."
-
- found.done = 1
-
- Reports["reports"] << reports
-
-
-client/proc/edit_report(ID as num)
- if(!src.holder || src.holder.level < 0)
- src << "You tried to modify the news, but you're not an admin!"
- return
-
- var/savefile/Reports = new("data/reports.sav")
- var/list/reports
-
- Reports["reports"] >> reports
-
- var/datum/admin_report/found
- for(var/datum/admin_report/N in reports)
- if(N.ID == ID)
- found = N
- if(!found) src << "* An error occured, sorry."
-
- var/body = input(src.mob, "Enter a body for the news", "Body") as null|message
- if(!body) return
-
- found.body = body
-
- Reports["reports"] << reports
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 172a408a22c..771b9c417a9 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -98,7 +98,8 @@ var/list/admin_verbs_admin = list(
/client/proc/view_duty_log,
/client/proc/cmd_dev_bst,
/client/proc/clear_toxins,
- /client/proc/wipe_ai // allow admins to force-wipe AIs
+ /client/proc/wipe_ai, // allow admins to force-wipe AIs
+ /client/proc/flush_lighting_queues
)
var/list/admin_verbs_ban = list(
/client/proc/unban_panel,
@@ -127,7 +128,9 @@ var/list/admin_verbs_fun = list(
/client/proc/roll_dices,
/datum/admins/proc/create_admin_fax,
/datum/admins/proc/call_supply_drop,
- /datum/admins/proc/call_drop_pod
+ /datum/admins/proc/call_drop_pod,
+ /client/proc/show_tip,
+ /client/proc/fab_tip
)
var/list/admin_verbs_spawn = list(
@@ -200,7 +203,8 @@ var/list/admin_verbs_debug = list(
/client/proc/jumptocoord,
/client/proc/dsay,
/client/proc/toggle_recursive_explosions,
- /client/proc/restart_sql
+ /client/proc/restart_sql,
+ /client/proc/flush_lighting_queues
)
var/list/admin_verbs_paranoid_debug = list(
@@ -351,7 +355,8 @@ var/list/admin_verbs_dev = list( //will need to be altered - Ryan784
/client/proc/togglebuildmodeself,
/client/proc/toggledebuglogs,
/client/proc/ZASSettings,
- /client/proc/cmd_dev_bst
+ /client/proc/cmd_dev_bst,
+ /client/proc/flush_lighting_queues
)
var/list/admin_verbs_cciaa = list(
/client/proc/cmd_admin_pm_panel, /*admin-pm list*/
@@ -1032,10 +1037,10 @@ var/list/admin_verbs_cciaa = list(
if (alert("Are you sure you want to wipe [target.name]? They will be ghosted and their job slot freed.", "Confirm AI Termination", "No", "No", "Yes") != "Yes")
return
-
+
log_and_message_admins("admin-wiped [key_name_admin(target)]'s core.")
target.do_wipe_core()
-
+
/client/proc/restart_sql()
set category = "Debug"
set name = "Reconnect SQL"
@@ -1050,3 +1055,19 @@ var/list/admin_verbs_cciaa = list(
log_and_message_admins("is attempting to reconnect the server to MySQL.")
dbcon.Reconnect()
+
+/client/proc/flush_lighting_queues()
+ set category = "Debug"
+ set name = "Reset Lighting"
+ set desc = "Flushes the lighting processor's work queue. Useful if the processor is hung with an invalid source."
+
+ if (!check_rights(R_DEBUG|R_ADMIN|R_DEV))
+ return
+
+ if (alert("Flush Lighting Work Queue? This will invalidate all pending lighting updates.", "Reset Lighting", "No", "No", "Yes") != "Yes")
+ return
+
+ log_and_message_admins("has flushed the lighting processor queues.")
+ lighting_update_lights = list()
+ lighting_update_corners = list()
+ lighting_update_overlays = list()
diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm
index c9f2497abc7..ca78cebe391 100644
--- a/code/modules/admin/verbs/debug.dm
+++ b/code/modules/admin/verbs/debug.dm
@@ -972,3 +972,17 @@
else
alert("Invalid mob")
+/client/proc/cmd_debug_profile_lighting()
+ set category = "Debug"
+ set name = "Profile Lighting"
+ set desc = "Spams the database with lighting updates. Y'know, just 'cause."
+
+ if (!check_rights(R_DEBUG|R_SERVER))
+ return
+
+ if (!establish_db_connection(dbcon))
+ usr << span("alert", "Unable to start profiling: No active database connection.")
+ return
+
+ lighting_profiling = !lighting_profiling
+ log_and_message_admins("has [lighting_profiling ? "enabled" : "disabled"] lighting profiling.")
diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm
index 64f5d759769..60dd248dd9c 100644
--- a/code/modules/admin/verbs/mapping.dm
+++ b/code/modules/admin/verbs/mapping.dm
@@ -163,6 +163,7 @@ var/list/debug_verbs = list (
,/datum/admins/proc/setup_supermatter
,/client/proc/atmos_toggle_debug
,/client/proc/spawn_tanktransferbomb
+ ,/client/proc/cmd_debug_profile_lighting
)
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index 00e701bd57a..e6c7168a5d7 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -949,3 +949,50 @@ Traitors and the like can also be revived with the previous role mostly intact.
usr << "Random events disabled"
message_admins("Admin [key_name_admin(usr)] has disabled random events.", 1)
feedback_add_details("admin_verb","TRE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+
+/client/proc/fab_tip()
+ set category = "Admin"
+ set name = "Fabricate Tip"
+ set desc = "Sends a tip (that you specify) to all players. After all \
+ you're the experienced player here."
+
+ if(!holder)
+ return
+
+ var/input = input(usr, "Please specify your tip that you want to send to the players.", "Tip", "") as message|null
+ if(!input)
+ return
+
+ if(!ticker)
+ return
+
+ ticker.selected_tip = input
+
+ // If we've already tipped, then send it straight away.
+ if(ticker.tipped)
+ ticker.send_tip_of_the_round()
+ ticker.selected_tip = initial(ticker.selected_tip)
+
+
+ message_admins("[key_name_admin(usr)] sent a tip of the round.")
+ log_admin("[key_name(usr)] sent \"[input]\" as the Tip of the Round.")
+ feedback_add_details("admin_verb","TIP")
+
+/client/proc/show_tip()
+ set category = "Debug"
+ set name = "Show Tip"
+ set desc = "Sends a tip (that the config specifies) to all players. After all \
+ you're not the experienced player here."
+
+ if(!holder)
+ return
+
+ if(!ticker)
+ return
+
+ ticker.send_tip_of_the_round()
+
+
+ message_admins("[key_name_admin(usr)] sent a pregenerated tip of the round.")
+ log_admin("[key_name(usr)] sent a pregenerated Tip of the Round.")
+ feedback_add_details("admin_verb","FAP")
\ No newline at end of file
diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm
index 665a005b99c..3d66037bd16 100644
--- a/code/modules/client/client defines.dm
+++ b/code/modules/client/client defines.dm
@@ -49,3 +49,14 @@
var/need_saves_migrated = "Requires database" //Used to determine whether or not the ckey needs their saves migrated over to the database. Default is 0 upon successful connection.
preload_rsc = 0 // This is 0 so we can set it to an URL once the player logs in and have them download the resources from a different server.
+
+ ////////////
+ //PARALLAX//
+ ////////////
+ var/list/parallax = list()
+ var/list/parallax_movable = list()
+ var/list/parallax_offset = list()
+ var/turf/previous_turf = null
+ var/obj/screen/plane_master/parallax_master/parallax_master = null
+ var/obj/screen/plane_master/parallax_dustmaster/parallax_dustmaster = null
+ var/obj/screen/plane_master/parallax_spacemaster/parallax_spacemaster = null
\ No newline at end of file
diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm
index e1bce814e36..08bcb180119 100644
--- a/code/modules/client/client procs.dm
+++ b/code/modules/client/client procs.dm
@@ -278,6 +278,8 @@
prefs.last_id = computer_id //these are gonna be used for banning
. = ..() //calls mob.Login()
+
+ prefs.sanitize_preferences()
if (byond_version < config.client_error_version)
src << "Your version of BYOND is too old!"
diff --git a/code/modules/client/movement.dm b/code/modules/client/movement.dm
index efcf51c02ee..5ba3a87e1e4 100644
--- a/code/modules/client/movement.dm
+++ b/code/modules/client/movement.dm
@@ -3,6 +3,9 @@
..()
dir = NORTH
+// These break the lighting system, comment out for now until
+// someone can be arsed to fix it.
+/*
/client/verb/spinleft()
set name = "Spin View CCW"
set category = "OOC"
@@ -13,3 +16,4 @@
set category = "OOC"
dir = turn(dir, -90)
+*/
diff --git a/code/modules/client/preference_setup/general/04_equipment.dm b/code/modules/client/preference_setup/general/04_equipment.dm
index 74c6ff7077a..2ceb8aea4d3 100644
--- a/code/modules/client/preference_setup/general/04_equipment.dm
+++ b/code/modules/client/preference_setup/general/04_equipment.dm
@@ -7,37 +7,31 @@
S["undershirt"] >> pref.undershirt
S["socks"] >> pref.socks
S["backbag"] >> pref.backbag
- S["gear"] >> pref.gear
/datum/category_item/player_setup_item/general/equipment/save_character(var/savefile/S)
S["underwear"] << pref.underwear
S["undershirt"] << pref.undershirt
S["socks"] << pref.socks
S["backbag"] << pref.backbag
- S["gear"] << pref.gear
/datum/category_item/player_setup_item/general/equipment/gather_load_query()
- return list("ss13_characters" = list("vars" = list("underwear", "undershirt", "socks", "backbag", "gear"), "args" = list("id")))
+ return list("ss13_characters" = list("vars" = list("underwear", "undershirt", "socks", "backbag"), "args" = list("id")))
/datum/category_item/player_setup_item/general/equipment/gather_load_parameters()
return list(":id" = pref.current_character)
/datum/category_item/player_setup_item/general/equipment/gather_save_query()
- return list("ss13_characters" = list("underwear", "undershirt", "socks", "backbag", "gear", "id" = 1, "ckey" = 1))
+ return list("ss13_characters" = list("underwear", "undershirt", "socks", "backbag", "id" = 1, "ckey" = 1))
/datum/category_item/player_setup_item/general/equipment/gather_save_parameters()
- return list(":underwear" = pref.underwear, ":undershirt" = pref.undershirt, ":socks" = pref.socks, ":backbag" = pref.backbag, ":gear" = list2params(pref.gear), ":id" = pref.current_character, ":ckey" = pref.client.ckey)
+ return list(":underwear" = pref.underwear, ":undershirt" = pref.undershirt, ":socks" = pref.socks, ":backbag" = pref.backbag, ":id" = pref.current_character, ":ckey" = pref.client.ckey)
/datum/category_item/player_setup_item/general/equipment/sanitize_character(var/sql_load = 0)
if (sql_load)
pref.backbag = text2num(pref.backbag)
- pref.gear = params2list(pref.gear)
pref.backbag = sanitize_integer(pref.backbag, 1, backbaglist.len, initial(pref.backbag))
- if (!islist(pref.gear))
- pref.gear = list()
-
var/undies = get_undies()
var/gender_socks = get_gender_socks()
if(!get_key_by_value(undies, pref.underwear))
@@ -47,45 +41,14 @@
if(!get_key_by_value(gender_socks, pref.socks))
pref.socks = gender_socks[gender_socks[1]]
- var/total_cost = 0
- for(var/gear_name in pref.gear)
- if(!gear_datums[gear_name])
- pref.gear -= gear_name
- else if(!(gear_name in valid_gear_choices()))
- pref.gear -= gear_name
- else
- var/datum/gear/G = gear_datums[gear_name]
- if(total_cost + G.cost > MAX_GEAR_COST)
- pref.gear -= gear_name
- else
- total_cost += G.cost
/datum/category_item/player_setup_item/general/equipment/content()
- . += "Equipment Loadout:
"
+ . += "Equipment:
"
. += "Underwear: [get_key_by_value(get_undies(),pref.underwear)]
"
. += "Undershirt: [get_key_by_value(undershirt_t,pref.undershirt)]
"
. += "Socks: [get_key_by_value(get_gender_socks(),pref.socks)]
"
. += "Backpack Type: [backbaglist[pref.backbag]]
"
- . += "
Custom Loadout:
"
- var/total_cost = 0
-
- if(pref.gear && pref.gear.len)
- for(var/i = 1; i <= pref.gear.len; i++)
- var/datum/gear/G = gear_datums[pref.gear[i]]
- if(G)
- total_cost += G.cost
- . += "[pref.gear[i]] ([G.cost] points) Remove
"
-
- . += "Used: [total_cost] points."
- else
- . += "None."
-
- if(total_cost < MAX_GEAR_COST)
- . += " Add"
- if(pref.gear && pref.gear.len)
- . += " Clear"
- . += "
"
/datum/category_item/player_setup_item/general/equipment/proc/get_undies()
return pref.gender == MALE ? underwear_m : underwear_f
@@ -93,17 +56,6 @@
/datum/category_item/player_setup_item/general/equipment/proc/get_gender_socks()
return pref.gender == MALE ? socks_m : socks_f
-/datum/category_item/player_setup_item/general/equipment/proc/valid_gear_choices(var/max_cost)
- . = list()
- var/mob/preference_mob = preference_mob()
- for(var/gear_name in gear_datums)
- var/datum/gear/G = gear_datums[gear_name]
- if(G.whitelisted && !is_alien_whitelisted(preference_mob, G.whitelisted))
- continue
- if(max_cost && G.cost > max_cost)
- continue
- . += gear_name
-
/datum/category_item/player_setup_item/general/equipment/OnTopic(var/href,var/list/href_list, var/mob/user)
if(href_list["change_underwear"])
var/underwear_options = get_undies()
@@ -131,32 +83,4 @@
pref.backbag = backbaglist.Find(new_backbag)
return TOPIC_REFRESH
- else if(href_list["add_loadout"])
- var/total_cost = 0
- for(var/gear_name in pref.gear)
- if(gear_datums[gear_name])
- var/datum/gear/G = gear_datums[gear_name]
- total_cost += G.cost
-
- var/choice = input(user, "Select gear to add:", "Character Preference") as null|anything in valid_gear_choices(MAX_GEAR_COST - total_cost)
- if(choice && gear_datums[choice] && CanUseTopic(user))
- var/datum/gear/C = gear_datums[choice]
- total_cost += C.cost
- if(C && total_cost <= MAX_GEAR_COST)
- pref.gear += choice
- user << "Added \the '[choice]' for [C.cost] points ([MAX_GEAR_COST - total_cost] points remaining)."
- else
- user << "Adding \the '[choice]' will exceed the maximum loadout cost of [MAX_GEAR_COST] points."
- return TOPIC_REFRESH
-
- else if(href_list["remove_loadout"])
- var/i_remove = text2num(href_list["remove_loadout"])
- if(i_remove < 1 || i_remove > pref.gear.len) return TOPIC_NOACTION
- pref.gear.Cut(i_remove, i_remove + 1)
- return TOPIC_REFRESH
-
- else if(href_list["clear_loadout"])
- pref.gear.Cut()
- return TOPIC_REFRESH
-
return ..()
diff --git a/code/modules/client/preference_setup/global/02_settings.dm b/code/modules/client/preference_setup/global/02_settings.dm
index f826f1b2fae..04a82b4336f 100644
--- a/code/modules/client/preference_setup/global/02_settings.dm
+++ b/code/modules/client/preference_setup/global/02_settings.dm
@@ -9,6 +9,8 @@
S["asfx_togs"] >> pref.asfx_togs
S["motd_hash"] >> pref.motd_hash
S["memo_hash"] >> pref.memo_hash
+ S["parallax_speed"] >> pref.parallax_speed
+ S["parallax_toggles"] >> pref.parallax_togs
/datum/category_item/player_setup_item/player_global/settings/save_preferences(var/savefile/S)
S["lastchangelog"] << pref.lastchangelog
@@ -17,6 +19,8 @@
S["asfx_togs"] << pref.asfx_togs
S["motd_hash"] << pref.motd_hash
S["memo_hash"] << pref.memo_hash
+ S["parallax_speed"] << pref.parallax_speed
+ S["parallax_toggles"] << pref.parallax_togs
/datum/category_item/player_setup_item/player_global/settings/gather_load_query()
return list("ss13_player_preferences" = list("vars" = list("lastchangelog", "current_character", "toggles", "asfx_togs", "lastmotd" = "motd_hash", "lastmemo" = "memo_hash"), "args" = list("ckey")))
@@ -25,7 +29,7 @@
return list(":ckey" = pref.client.ckey)
/datum/category_item/player_setup_item/player_global/settings/gather_save_query()
- return list("ss13_player_preferences" = list("lastchangelog", "current_character", "toggles", "asfx_togs", "lastmotd", "lastmemo", "ckey" = 1))
+ return list("ss13_player_preferences" = list("lastchangelog", "current_character", "toggles", "asfx_togs", "lastmotd", "lastmemo", "ckey" = 1, "parallax_toggles", "parallax_speed"))
/datum/category_item/player_setup_item/player_global/settings/gather_save_parameters()
return list(":ckey" = pref.client.ckey,
@@ -34,7 +38,9 @@
":toggles" = pref.toggles,
":asfx_togs" = pref.asfx_togs,
":lastmotd" = pref.motd_hash,
- ":lastmemo" = pref.memo_hash)
+ ":lastmemo" = pref.memo_hash,
+ ":parallax_toggles" = pref.parallax_togs,
+ ":parallax_speed" = pref.parallax_speed)
/datum/category_item/player_setup_item/player_global/settings/sanitize_preferences(var/sql_load = 0)
if (sql_load)
@@ -46,6 +52,8 @@
pref.asfx_togs = sanitize_integer(text2num(pref.asfx_togs), 0, 65535, initial(pref.toggles))
pref.motd_hash = sanitize_text(pref.motd_hash, initial(pref.motd_hash))
pref.memo_hash = sanitize_text(pref.memo_hash, initial(pref.memo_hash))
+ pref.parallax_speed = sanitize_integer(text2num(pref.parallax_speed), 1, 10, initial(pref.parallax_speed))
+ pref.parallax_togs = sanitize_integer(text2num(pref.parallax_togs), 0, 65535, initial(pref.parallax_togs))
/datum/category_item/player_setup_item/player_global/settings/content(var/mob/user)
. += "Play admin midis: [(pref.toggles & SOUND_MIDI) ? "Yes" : "No"]
"
@@ -53,6 +61,8 @@
. += "Ghost ears: [(pref.toggles & CHAT_GHOSTEARS) ? "All Speech" : "Nearest Creatures"]
"
. += "Ghost sight: [(pref.toggles & CHAT_GHOSTSIGHT) ? "All Emotes" : "Nearest Creatures"]
"
. += "Ghost radio: [(pref.toggles & CHAT_GHOSTRADIO) ? "All Chatter" : "Nearest Speakers"]
"
+ . += "Space Parallax: [(pref.parallax_togs & PARALLAX_SPACE) ? "Yes" : "No"]
"
+ . += "Space Dust: [(pref.parallax_togs & PARALLAX_DUST) ? "Yes" : "No"]
"
/datum/category_item/player_setup_item/player_global/settings/OnTopic(var/href,var/list/href_list, var/mob/user)
if(href_list["toggle"])
@@ -65,4 +75,9 @@
user << sound(null, repeat = 0, wait = 0, volume = 85, channel = 1)
return TOPIC_REFRESH
+ if(href_list["paratoggle"])
+ var/flag = text2num(href_list["paratoggle"])
+ pref.parallax_togs ^= flag
+ return TOPIC_REFRESH
+
return ..()
diff --git a/code/modules/client/preference_setup/loadout/gear_tweaks.dm b/code/modules/client/preference_setup/loadout/gear_tweaks.dm
new file mode 100644
index 00000000000..8620e866793
--- /dev/null
+++ b/code/modules/client/preference_setup/loadout/gear_tweaks.dm
@@ -0,0 +1,145 @@
+/datum/gear_tweak/proc/get_contents(var/metadata)
+ return
+
+/datum/gear_tweak/proc/get_metadata(var/user, var/metadata)
+ return
+
+/datum/gear_tweak/proc/get_default()
+ return
+
+/datum/gear_tweak/proc/tweak_gear_data(var/metadata, var/datum/gear_data)
+ return
+
+/datum/gear_tweak/proc/tweak_item(var/obj/item/I, var/metadata)
+ return
+
+/*
+Color adjustment
+*/
+
+var/datum/gear_tweak/color/gear_tweak_free_color_choice = new()
+
+/datum/gear_tweak/color
+ var/list/valid_colors
+
+/datum/gear_tweak/color/New(var/list/valid_colors)
+ src.valid_colors = valid_colors
+ ..()
+
+/datum/gear_tweak/color/get_contents(var/metadata)
+ return "Color: ⚫"
+
+/datum/gear_tweak/color/get_default()
+ return valid_colors ? valid_colors[1] : COLOR_GRAY
+
+/datum/gear_tweak/color/get_metadata(var/user, var/metadata)
+ if(valid_colors)
+ return input(user, "Choose an item color.", "Character Preference", metadata) as null|anything in valid_colors
+ return input(user, "Choose an item color.", "Global Preference", metadata) as color|null
+
+/datum/gear_tweak/color/tweak_item(var/obj/item/I, var/metadata)
+ if(valid_colors && !(metadata in valid_colors))
+ return
+ I.color = metadata
+
+/*
+Path adjustment
+*/
+
+/datum/gear_tweak/path
+ var/list/valid_paths
+
+/datum/gear_tweak/path/New(var/list/valid_paths)
+ src.valid_paths = valid_paths
+ ..()
+
+/datum/gear_tweak/path/get_contents(var/metadata)
+ return "Type: [metadata]"
+
+/datum/gear_tweak/path/get_default()
+ return valid_paths[1]
+
+/datum/gear_tweak/path/get_metadata(var/user, var/metadata)
+ return input(user, "Choose a type.", "Character Preference", metadata) as null|anything in valid_paths
+
+/datum/gear_tweak/path/tweak_gear_data(var/metadata, var/datum/gear_data/gear_data)
+ if(!(metadata in valid_paths))
+ return
+ gear_data.path = valid_paths[metadata]
+
+/*
+Content adjustment
+*/
+
+/datum/gear_tweak/contents
+ var/list/valid_contents
+
+/datum/gear_tweak/contents/New()
+ valid_contents = args.Copy()
+ ..()
+
+/datum/gear_tweak/contents/get_contents(var/metadata)
+ return "Contents: [english_list(metadata, and_text = ", ")]"
+
+/datum/gear_tweak/contents/get_default()
+ . = list()
+ for(var/i = 1 to valid_contents.len)
+ . += "Random"
+
+/datum/gear_tweak/contents/get_metadata(var/user, var/list/metadata)
+ . = list()
+ for(var/i = metadata.len to (valid_contents.len - 1))
+ metadata += "Random"
+ for(var/i = 1 to valid_contents.len)
+ var/entry = input(user, "Choose an entry.", "Character Preference", metadata[i]) as null|anything in (valid_contents[i] + list("Random", "None"))
+ if(entry)
+ . += entry
+ else
+ return metadata
+
+/datum/gear_tweak/contents/tweak_item(var/obj/item/I, var/list/metadata)
+ if(metadata.len != valid_contents.len)
+ return
+ for(var/i = 1 to valid_contents.len)
+ var/path
+ var/list/contents = valid_contents[i]
+ if(metadata[i] == "Random")
+ path = pick(contents)
+ path = contents[path]
+ else if(metadata[i] == "None")
+ continue
+ else
+ path = contents[metadata[i]]
+ new path(I)
+
+/*
+Reagents adjustment
+*/
+
+
+/datum/gear_tweak/reagents
+ var/list/valid_reagents
+
+/datum/gear_tweak/reagents/New(var/list/reagents)
+ valid_reagents = reagents.Copy()
+ ..()
+
+/datum/gear_tweak/reagents/get_contents(var/metadata)
+ return "Reagents: [metadata]"
+
+/datum/gear_tweak/reagents/get_default()
+ return "Random"
+
+/datum/gear_tweak/reagents/get_metadata(var/user, var/list/metadata)
+ . = input(user, "Choose an entry.", "Character Preference", metadata) as null|anything in (valid_reagents + list("Random", "None"))
+ if(!.)
+ return metadata
+
+/datum/gear_tweak/reagents/tweak_item(var/obj/item/I, var/list/metadata)
+ if(metadata == "None")
+ return
+ if(metadata == "Random")
+ . = valid_reagents[pick(valid_reagents)]
+ else
+ . = valid_reagents[metadata]
+ I.reagents.add_reagent(., I.reagents.get_free_space())
\ No newline at end of file
diff --git a/code/modules/client/preference_setup/loadout/loadout.dm b/code/modules/client/preference_setup/loadout/loadout.dm
new file mode 100644
index 00000000000..70a18e5b8a6
--- /dev/null
+++ b/code/modules/client/preference_setup/loadout/loadout.dm
@@ -0,0 +1,228 @@
+var/list/loadout_categories = list()
+var/list/gear_datums = list()
+
+/datum/loadout_category
+ var/category = ""
+ var/list/gear = list()
+
+/datum/loadout_category/New(var/cat)
+ category = cat
+ ..()
+
+/hook/startup/proc/populate_gear_list()
+
+ //create a list of gear datums to sort
+ for(var/geartype in typesof(/datum/gear)-/datum/gear)
+ var/datum/gear/G = geartype
+
+ var/use_name = initial(G.display_name)
+ var/use_category = initial(G.sort_category)
+
+ if(!loadout_categories[use_category])
+ loadout_categories[use_category] = new /datum/loadout_category(use_category)
+ var/datum/loadout_category/LC = loadout_categories[use_category]
+ gear_datums[use_name] = new geartype
+ LC.gear[use_name] = gear_datums[use_name]
+
+ loadout_categories = sortAssoc(loadout_categories)
+ for(var/loadout_category in loadout_categories)
+ var/datum/loadout_category/LC = loadout_categories[loadout_category]
+ LC.gear = sortAssoc(LC.gear)
+ return 1
+
+/datum/category_item/player_setup_item/loadout
+ name = "Loadout"
+ sort_order = 1
+ var/current_tab = "General"
+
+/datum/category_item/player_setup_item/loadout/load_character(var/savefile/S)
+ S["gear"] >> pref.gear
+
+/datum/category_item/player_setup_item/loadout/save_character(var/savefile/S)
+ S["gear"] << pref.gear
+
+/datum/category_item/player_setup_item/loadout/gather_load_query()
+ return list("ss13_characters" = list("vars" = list("gear"), "args" = list("id")))
+
+/datum/category_item/player_setup_item/loadout/gather_save_query()
+ return list("ss13_characters" = list("gear", "id" = 1, "ckey" = 1))
+
+/datum/category_item/player_setup_item/loadout/gather_save_parameters()
+ return list(":gear" = list2params(pref.gear), ":id" = pref.current_character, ":ckey" = pref.client.ckey)
+
+/datum/category_item/player_setup_item/loadout/proc/valid_gear_choices(var/max_cost)
+ . = list()
+ var/mob/preference_mob = preference_mob()
+ for(var/gear_name in gear_datums)
+ var/datum/gear/G = gear_datums[gear_name]
+
+ if(G.whitelisted && !is_alien_whitelisted(preference_mob, all_species[G.whitelisted]))
+ continue
+ if(max_cost && G.cost > max_cost)
+ continue
+ . += gear_name
+
+/datum/category_item/player_setup_item/loadout/sanitize_character(var/sql_load = 0)
+
+ if (sql_load)
+ pref.gear = params2list(pref.gear)
+
+ var/mob/preference_mob = preference_mob()
+ if(!islist(pref.gear))
+ pref.gear = list()
+
+ for(var/gear_name in pref.gear)
+ if(!(gear_name in gear_datums))
+ pref.gear -= gear_name
+ var/total_cost = 0
+ for(var/gear_name in pref.gear)
+ if(!gear_datums[gear_name])
+ preference_mob << "You cannot have more than one of the \the [gear_name]"
+ pref.gear -= gear_name
+ else if(!(gear_name in valid_gear_choices()))
+ preference_mob << "You cannot take \the [gear_name] as you are not whitelisted for the species."
+ pref.gear -= gear_name
+ else
+ var/datum/gear/G = gear_datums[gear_name]
+ if(total_cost + G.cost > MAX_GEAR_COST)
+ pref.gear -= gear_name
+ preference_mob << "You cannot afford to take \the [gear_name]"
+ else
+ total_cost += G.cost
+
+/datum/category_item/player_setup_item/loadout/content()
+ var/total_cost = 0
+ if(pref.gear && pref.gear.len)
+ for(var/i = 1; i <= pref.gear.len; i++)
+ var/datum/gear/G = gear_datums[pref.gear[i]]
+ if(G)
+ total_cost += G.cost
+
+ var/fcolor = "#3366CC"
+ if(total_cost < MAX_GEAR_COST)
+ fcolor = "#E67300"
+ . = list()
+ . += ""
+ . += "| [total_cost]/[MAX_GEAR_COST] loadout points spent. \[Clear Loadout\] |
"
+
+ . += "| "
+ var/firstcat = 1
+ for(var/category in loadout_categories)
+
+ if(firstcat)
+ firstcat = 0
+ else
+ . += " |"
+ if(category == current_tab)
+ . += " [category] "
+ else
+ var/datum/loadout_category/LC = loadout_categories[category]
+ var/tcolor = "#3366CC"
+ for(var/thing in LC.gear)
+ if(thing in pref.gear)
+ tcolor = "#E67300"
+ break
+ . += " [category] "
+ . += " |
"
+
+ var/datum/loadout_category/LC = loadout_categories[current_tab]
+ . += "
|
"
+ . += "| [LC.category] |
"
+ . += "
|
"
+ for(var/gear_name in LC.gear)
+ if(!(gear_name in valid_gear_choices()))
+ continue
+ var/datum/gear/G = LC.gear[gear_name]
+ var/ticked = (G.display_name in pref.gear)
+ . += "| [G.display_name] | "
+ . += "[G.cost] | "
+ . += "[G.description] |
"
+ if(ticked)
+ . += "| "
+ for(var/datum/gear_tweak/tweak in G.gear_tweaks)
+ . += " [tweak.get_contents(get_tweak_metadata(G, tweak))]"
+ . += " |
"
+ . += "
"
+ . = jointext(.,null)
+
+/datum/category_item/player_setup_item/loadout/proc/get_gear_metadata(var/datum/gear/G)
+ . = pref.gear[G.display_name]
+ if(!.)
+ . = list()
+ pref.gear[G.display_name] = .
+
+/datum/category_item/player_setup_item/loadout/proc/get_tweak_metadata(var/datum/gear/G, var/datum/gear_tweak/tweak)
+ var/list/metadata = get_gear_metadata(G)
+ . = metadata["[tweak]"]
+ if(!.)
+ . = tweak.get_default()
+ metadata["[tweak]"] = .
+
+/datum/category_item/player_setup_item/loadout/proc/set_tweak_metadata(var/datum/gear/G, var/datum/gear_tweak/tweak, var/new_metadata)
+ var/list/metadata = get_gear_metadata(G)
+ metadata["[tweak]"] = new_metadata
+
+/datum/category_item/player_setup_item/loadout/OnTopic(href, href_list, user)
+ if(href_list["toggle_gear"])
+ var/datum/gear/TG = gear_datums[href_list["toggle_gear"]]
+ if(TG.display_name in pref.gear)
+ pref.gear -= TG.display_name
+ else
+ var/total_cost = 0
+ for(var/gear_name in pref.gear)
+ var/datum/gear/G = gear_datums[gear_name]
+ if(istype(G)) total_cost += G.cost
+ if((total_cost+TG.cost) <= MAX_GEAR_COST)
+ pref.gear += TG.display_name
+ return TOPIC_REFRESH
+ if(href_list["gear"] && href_list["tweak"])
+ var/datum/gear/gear = gear_datums[href_list["gear"]]
+ var/datum/gear_tweak/tweak = locate(href_list["tweak"])
+ if(!tweak || !istype(gear) || !(tweak in gear.gear_tweaks))
+ return TOPIC_NOACTION
+ var/metadata = tweak.get_metadata(user, get_tweak_metadata(gear, tweak))
+ if(!metadata || !CanUseTopic(user))
+ return TOPIC_NOACTION
+ set_tweak_metadata(gear, tweak, metadata)
+ return TOPIC_REFRESH
+ else if(href_list["select_category"])
+ current_tab = href_list["select_category"]
+ return TOPIC_REFRESH
+ else if(href_list["clear_loadout"])
+ pref.gear.Cut()
+ return TOPIC_REFRESH
+ return ..()
+
+/datum/gear
+ var/display_name //Name/index. Must be unique.
+ var/description //Description of this gear. If left blank will default to the description of the pathed item.
+ var/path //Path to item.
+ var/cost = 1 //Number of points used. Items in general cost 1 point, storage/armor/gloves/special use costs 2 points.
+ var/slot //Slot to equip to.
+ var/list/allowed_roles //Roles that can spawn with this item.
+ var/whitelisted //Term to check the whitelist for..
+ var/sort_category = "General"
+ var/list/gear_tweaks = list() //List of datums which will alter the item after it has been spawned.
+
+/datum/gear/New()
+ ..()
+ if(!description)
+ var/obj/O = path
+ description = initial(O.desc)
+
+/datum/gear_data
+ var/path
+ var/location
+
+/datum/gear_data/New(var/path, var/location)
+ src.path = path
+ src.location = location
+
+/datum/gear/proc/spawn_item(var/location, var/metadata)
+ var/datum/gear_data/gd = new(path, location)
+ for(var/datum/gear_tweak/gt in gear_tweaks)
+ gt.tweak_gear_data(metadata["[gt]"], gd)
+ var/item = new gd.path(gd.location)
+ for(var/datum/gear_tweak/gt in gear_tweaks)
+ gt.tweak_item(item, metadata["[gt]"])
+ return item
diff --git a/code/modules/client/preference_setup/loadout/loadout_accessories.dm b/code/modules/client/preference_setup/loadout/loadout_accessories.dm
new file mode 100644
index 00000000000..04cffbef2e6
--- /dev/null
+++ b/code/modules/client/preference_setup/loadout/loadout_accessories.dm
@@ -0,0 +1,69 @@
+/datum/gear/accessory
+ display_name = "suspenders"
+ path = /obj/item/clothing/accessory/suspenders
+ slot = slot_tie
+ sort_category = "Accessories"
+
+/datum/gear/accessory/waistcoat
+ display_name = "waistcoat"
+ path = /obj/item/clothing/accessory/wcoat
+
+/datum/gear/accessory/armband
+ display_name = "armband selection"
+ path = /obj/item/clothing/accessory/armband
+
+/datum/gear/accessory/armband/New()
+ ..()
+ var/armbands = list()
+ armbands["red armband"] = /obj/item/clothing/accessory/armband
+ armbands["cargo armband"] = /obj/item/clothing/accessory/armband/cargo
+ armbands["EMT armband"] = /obj/item/clothing/accessory/armband/medgreen
+ armbands["medical armband"] = /obj/item/clothing/accessory/armband/med
+ armbands["engineering armband"] = /obj/item/clothing/accessory/armband/engine
+ armbands["hydroponics armband"] = /obj/item/clothing/accessory/armband/hydro
+ armbands["science armband"] = /obj/item/clothing/accessory/armband/science
+ armbands["synthetic intelligence movement armband"] = /obj/item/clothing/accessory/armband/movement
+ armbands["ATLAS armband"] = /obj/item/clothing/accessory/armband/atlas
+ gear_tweaks += new/datum/gear_tweak/path(armbands)
+
+/datum/gear/accessory/holster
+ display_name = "holster selection"
+ path = /obj/item/clothing/accessory/holster
+ allowed_roles = list("Captain", "Head of Personnel", "Security Officer", "Warden", "Head of Security","Detective", "Security Cadet")
+
+/datum/gear/accessory/holster/New()
+ ..()
+ var/holsters = list()
+ holsters["holster, armpit"] = /obj/item/clothing/accessory/holster/armpit
+ holsters["holster, hip"] = /obj/item/clothing/accessory/holster/hip
+ holsters["holster, waist"] = /obj/item/clothing/accessory/holster/waist
+ holsters["holster, thigh"] = /obj/item/clothing/accessory/holster/thigh
+ gear_tweaks += new/datum/gear_tweak/path(holsters)
+
+/datum/gear/accessory/tie
+ display_name = "tie selection"
+ path = /obj/item/clothing/accessory
+
+/datum/gear/accessory/tie/New()
+ ..()
+ var/ties = list()
+ ties["blue tie"] = /obj/item/clothing/accessory/blue
+ ties["red tie"] = /obj/item/clothing/accessory/red
+ ties["horrible tie"] = /obj/item/clothing/accessory/horrible
+ gear_tweaks += new/datum/gear_tweak/path(ties)
+
+/datum/gear/accessory/brown_vest
+ display_name = "webbing, engineering"
+ path = /obj/item/clothing/accessory/storage/brown_vest
+ allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer")
+
+/datum/gear/accessory/black_vest
+ display_name = "webbing, security"
+ path = /obj/item/clothing/accessory/storage/black_vest
+ allowed_roles = list("Security Officer","Head of Security","Warden")
+
+/datum/gear/accessory/webbing
+ display_name = "webbing, simple"
+ path = /obj/item/clothing/accessory/storage/webbing
+ cost = 2
+
diff --git a/code/modules/client/preference_setup/loadout/loadout_cosmetics.dm b/code/modules/client/preference_setup/loadout/loadout_cosmetics.dm
new file mode 100644
index 00000000000..c75a557675b
--- /dev/null
+++ b/code/modules/client/preference_setup/loadout/loadout_cosmetics.dm
@@ -0,0 +1,21 @@
+/datum/gear/cosmetic
+ display_name = "purple comb"
+ path = /obj/item/weapon/haircomb
+ sort_category = "Cosmetics"
+
+/datum/gear/cosmetic/lipstick
+ display_name = "lipstick selection"
+ path = /obj/item/weapon/lipstick
+
+/datum/gear/cosmetic/lipstick/New()
+ ..()
+ var/lipsticks = list()
+ lipsticks["lipstick, red"] = /obj/item/weapon/lipstick
+ lipsticks["lipstick, purple"] = /obj/item/weapon/lipstick/purple
+ lipsticks["lipstick, jade"] = /obj/item/weapon/lipstick/jade
+ lipsticks["lipstick, back"] = /obj/item/weapon/lipstick/black
+ gear_tweaks += new/datum/gear_tweak/path(lipsticks)
+
+/datum/gear/cosmetic/mirror
+ display_name = "handheld mirror"
+ path = /obj/item/weapon/mirror
diff --git a/code/modules/client/preference_setup/loadout/loadout_ears.dm b/code/modules/client/preference_setup/loadout/loadout_ears.dm
new file mode 100644
index 00000000000..11685305ca3
--- /dev/null
+++ b/code/modules/client/preference_setup/loadout/loadout_ears.dm
@@ -0,0 +1,19 @@
+// Stuff worn on the ears. Items here go in the "ears" sort_category but they must not use
+// the slot_r_ear or slot_l_ear as the slot, or else players will spawn with no headset.
+/datum/gear/ears
+ display_name = "earmuffs"
+ path = /obj/item/clothing/ears/earmuffs
+ sort_category = "Earwear"
+
+/datum/gear/ears/bandanna
+ display_name = "neck bandanna selection"
+ path = /obj/item/clothing/ears/bandanna
+
+/datum/gear/ears/bandanna/New()
+ ..()
+ var/bandanna = list()
+ bandanna["red bandanna"] = /obj/item/clothing/ears/bandanna
+ bandanna["blue bandanna"] = /obj/item/clothing/ears/bandanna/blue
+ bandanna["black bandanna"] = /obj/item/clothing/ears/bandanna/black
+ gear_tweaks += new/datum/gear_tweak/path(bandanna)
+
diff --git a/code/modules/client/preference_setup/loadout/loadout_eyes.dm b/code/modules/client/preference_setup/loadout/loadout_eyes.dm
new file mode 100644
index 00000000000..fef99095d78
--- /dev/null
+++ b/code/modules/client/preference_setup/loadout/loadout_eyes.dm
@@ -0,0 +1,50 @@
+// Eyes
+/datum/gear/eyes
+ display_name = "eyepatch"
+ path = /obj/item/clothing/glasses/eyepatch
+ slot = slot_glasses
+ sort_category = "Glasses and Eyewear"
+
+/datum/gear/eyes/glasses
+ display_name = "glasses, prescription"
+ path = /obj/item/clothing/glasses/regular
+
+/datum/gear/eyes/glasses/green
+ display_name = "glasses, green"
+ path = /obj/item/clothing/glasses/gglasses
+
+/datum/gear/eyes/glasses/prescriptionhipster
+ display_name = "glasses, hipster"
+ path = /obj/item/clothing/glasses/regular/hipster
+
+/datum/gear/eyes/glasses/monocle
+ display_name = "monocle"
+ path = /obj/item/clothing/glasses/monocle
+
+/datum/gear/eyes/scanning_goggles
+ display_name = "scanning goggles"
+ path = /obj/item/clothing/glasses/regular/scanners
+
+/datum/gear/eyes/sciencegoggles
+ display_name = "science Goggles"
+ path = /obj/item/clothing/glasses/science
+
+/datum/gear/eyes/security
+ display_name = "security HUD"
+ path = /obj/item/clothing/glasses/hud/security
+ allowed_roles = list("Security Officer","Head of Security","Warden")
+
+/datum/gear/eyes/medical
+ display_name = "medical HUD"
+ path = /obj/item/clothing/glasses/hud/health
+ allowed_roles = list("Medical Doctor","Chief Medical Officer","Chemist","Paramedic","Geneticist")
+
+/datum/gear/eyes/shades
+ display_name = "sunglasses, fat"
+ path = /obj/item/clothing/glasses/sunglasses/big
+ allowed_roles = list("Security Officer","Head of Security","Warden","Captain","Head of Personnel","Quartermaster","Internal Affairs Agent","Detective")
+
+/datum/gear/eyes/shades/prescriptionsun
+ display_name = "sunglasses, presciption"
+ path = /obj/item/clothing/glasses/sunglasses/prescription
+ cost = 2
diff --git a/code/modules/client/preference_setup/loadout/loadout_general.dm b/code/modules/client/preference_setup/loadout/loadout_general.dm
new file mode 100644
index 00000000000..7c3c9da7e5a
--- /dev/null
+++ b/code/modules/client/preference_setup/loadout/loadout_general.dm
@@ -0,0 +1,69 @@
+/datum/gear/cane
+ display_name = "cane"
+ path = /obj/item/weapon/cane
+
+/datum/gear/dice
+ display_name = "d20"
+ path = /obj/item/weapon/dice/d20
+
+/datum/gear/cards
+ display_name = "deck of cards"
+ path = /obj/item/weapon/deck/cards
+
+/datum/gear/tarot
+ display_name = "deck of tarot cards"
+ path = /obj/item/weapon/deck/tarot
+
+/datum/gear/holder
+ display_name = "card holder"
+ path = /obj/item/weapon/deck/holder
+
+/datum/gear/cardemon_pack
+ display_name = "cardemon booster pack"
+ path = /obj/item/weapon/pack/cardemon
+
+/datum/gear/spaceball_pack
+ display_name = "spaceball booster pack"
+ path = /obj/item/weapon/pack/spaceball
+
+/datum/gear/flask
+ display_name = "flask"
+ path = /obj/item/weapon/reagent_containers/food/drinks/flask/barflask
+
+/datum/gear/flask/New()
+ ..()
+ gear_tweaks += new/datum/gear_tweak/reagents(lunchables_ethanol_reagents())
+
+/datum/gear/vacflask
+ display_name = "vacuum-flask"
+ path = /obj/item/weapon/reagent_containers/food/drinks/flask/vacuumflask
+
+/datum/gear/vacflask/New()
+ ..()
+ gear_tweaks += new/datum/gear_tweak/reagents(lunchables_drink_reagents())
+
+/datum/gear/boot_knife
+ display_name = "boot knife"
+ path = /obj/item/weapon/material/kitchen/utensil/knife/boot
+ cost = 3
+
+/datum/gear/cape
+ display_name = "tunnel cloak"
+ path = /obj/item/weapon/storage/backpack/cloak
+ cost = 1
+
+/datum/gear/lunchbox
+ display_name = "lunchbox"
+ description = "A little lunchbox."
+ cost = 2
+ path = /obj/item/weapon/storage/toolbox/lunchbox
+
+/datum/gear/lunchbox/New()
+ ..()
+ var/list/lunchboxes = list()
+ for(var/lunchbox_type in typesof(/obj/item/weapon/storage/toolbox/lunchbox))
+ var/obj/item/weapon/storage/toolbox/lunchbox/lunchbox = lunchbox_type
+ if(!initial(lunchbox.filled))
+ lunchboxes[initial(lunchbox.name)] = lunchbox_type
+ gear_tweaks += new/datum/gear_tweak/path(sortAssoc(lunchboxes))
+ gear_tweaks += new/datum/gear_tweak/contents(lunchables_lunches(), lunchables_snacks(), lunchables_drinks())
\ No newline at end of file
diff --git a/code/modules/client/preference_setup/loadout/loadout_gloves.dm b/code/modules/client/preference_setup/loadout/loadout_gloves.dm
new file mode 100644
index 00000000000..3680312c54f
--- /dev/null
+++ b/code/modules/client/preference_setup/loadout/loadout_gloves.dm
@@ -0,0 +1,25 @@
+/datum/gear/gloves
+ display_name = "watch"
+ path = /obj/item/clothing/gloves/watch
+ cost = 2
+ slot = slot_gloves
+ sort_category = "Gloves and Handwear"
+
+/datum/gear/gloves/color
+ display_name = "gloves selection"
+ path = /obj/item/clothing/gloves/black
+
+/datum/gear/gloves/color/New()
+ ..()
+ var/gloves = list()
+ gloves["black gloves"] = /obj/item/clothing/gloves
+ gloves["red gloves"] = /obj/item/clothing/gloves/red
+ gloves["blue gloves"] = /obj/item/clothing/gloves/blue
+ gloves["orange gloves"] = /obj/item/clothing/gloves/orange
+ gloves["purple gloves"] = /obj/item/clothing/gloves/purple
+ gloves["brown gloves"] = /obj/item/clothing/gloves/brown
+ gloves["light-brown gloves"] = /obj/item/clothing/gloves/light_brown
+ gloves["white gloves"] = /obj/item/clothing/gloves/green
+ gloves["grey gloves"] = /obj/item/clothing/gloves/grey
+ gloves["rainbow gloves"] = /obj/item/clothing/gloves/rainbow
+ gear_tweaks += new/datum/gear_tweak/path(gloves)
diff --git a/code/modules/client/preference_setup/loadout/loadout_head.dm b/code/modules/client/preference_setup/loadout/loadout_head.dm
new file mode 100644
index 00000000000..081b6536564
--- /dev/null
+++ b/code/modules/client/preference_setup/loadout/loadout_head.dm
@@ -0,0 +1,113 @@
+/datum/gear/head
+ display_name = "hat, boatsman"
+ path = /obj/item/clothing/head/boaterhat
+ slot = slot_head
+ sort_category = "Hats and Headwear"
+
+/datum/gear/head/bandana
+ display_name = "bandana selection"
+ path = /obj/item/clothing/head/bandana
+
+/datum/gear/head/bandana/New()
+ ..()
+ var/bandanas = list()
+ bandanas["green bandana"] = /obj/item/clothing/head/greenbandana
+ bandanas["orange bandana"] = /obj/item/clothing/head/orangebandana
+ bandanas["pirate bandana"] = /obj/item/clothing/head/bandana
+ gear_tweaks += new/datum/gear_tweak/path(bandanas)
+
+/datum/gear/head/cap
+ display_name = "cap selection"
+ path = /obj/item/clothing/head/soft/blue
+
+/datum/gear/head/cap/New()
+ ..()
+ var/caps = list()
+ caps["blue cap"] = /obj/item/clothing/head/soft/blue
+ caps["flat cap"] = /obj/item/clothing/head/flatcap
+ caps["green cap"] = /obj/item/clothing/head/soft/green
+ caps["grey cap"] = /obj/item/clothing/head/soft/grey
+ caps["mailman cap"] = /obj/item/clothing/head/mailman
+ caps["orange cap"] = /obj/item/clothing/head/soft/orange
+ caps["purple cap"] = /obj/item/clothing/head/soft/purple
+ caps["rainbow cap"] = /obj/item/clothing/head/soft/rainbow
+ caps["red cap"] = /obj/item/clothing/head/soft/red
+ caps["white cap"] = /obj/item/clothing/head/soft/mime
+ caps["yellow cap"] = /obj/item/clothing/head/soft/yellow
+ gear_tweaks += new/datum/gear_tweak/path(caps)
+
+/datum/gear/head/beret
+ display_name = "beret, red"
+ path = /obj/item/clothing/head/beret
+
+/datum/gear/head/beret/eng
+ display_name = "beret, engie-orange"
+ path = /obj/item/clothing/head/beret/engineering
+ allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Engineering Apprentice")
+
+/datum/gear/head/beret/purp
+ display_name = "beret, purple"
+ path = /obj/item/clothing/head/beret/purple
+
+/datum/gear/head/beret/sec
+ display_name = "beret,security"
+ path = /obj/item/clothing/head/beret/sec
+ allowed_roles = list("Security Officer","Head of Security","Warden","Security Cadet","Detective")
+
+/datum/gear/head/beret/warden
+ display_name = "beret,security (warden)"
+ path = /obj/item/clothing/head/beret/sec/warden
+ allowed_roles = list("Head of Security","Warden")
+
+/datum/gear/head/beret/hos
+ display_name = "beret,security (head of security)"
+ path = /obj/item/clothing/head/beret/sec/hos
+ allowed_roles = list("Head of Security")
+
+/datum/gear/head/cap/corp
+ display_name = "cap, corporate (security)"
+ path = /obj/item/clothing/head/soft/sec/corp
+ allowed_roles = list("Security Officer","Head of Security","Warden","Security Cadet","Detective")
+
+/datum/gear/head/cap/sec
+ display_name = "cap, security"
+ path = /obj/item/clothing/head/soft/sec
+ allowed_roles = list("Security Officer","Head of Security","Warden","Security Cadet","Detective")
+
+/datum/gear/head/hardhat
+ display_name = "hardhat, yellow"
+ path = /obj/item/clothing/head/hardhat
+ allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Engineering Apprentice")
+
+/datum/gear/head/hardhat/New()
+ ..()
+ var/hardhat = list()
+ hardhat["hardhat, yellow"] = /obj/item/clothing/head/hardhat
+ hardhat["hardhat, blue"] = /obj/item/clothing/head/hardhat/dblue
+ hardhat["hardhat, orange"] = /obj/item/clothing/head/hardhat/orange
+ hardhat["hardhat, red"] = /obj/item/clothing/head/hardhat/red
+ gear_tweaks += new/datum/gear_tweak/path(hardhat)
+
+/datum/gear/head/hairflower
+ display_name = "hair flower pin, red"
+ path = /obj/item/clothing/head/hairflower
+
+/datum/gear/head/bowler
+ display_name = "hat, bowler"
+ path = /obj/item/clothing/head/bowler
+
+/datum/gear/head/fez
+ display_name = "hat, fez"
+ path = /obj/item/clothing/head/fez
+
+/datum/gear/head/tophat
+ display_name = "hat, tophat"
+ path = /obj/item/clothing/head/that
+
+/datum/gear/head/philosopher_wig
+ display_name = "natural philosopher's wig"
+ path = /obj/item/clothing/head/philosopher_wig
+
+/datum/gear/head/ushanka
+ display_name = "ushanka"
+ path = /obj/item/clothing/head/ushanka
diff --git a/code/modules/client/preference_setup/loadout/loadout_mask.dm b/code/modules/client/preference_setup/loadout/loadout_mask.dm
new file mode 100644
index 00000000000..c6342904f67
--- /dev/null
+++ b/code/modules/client/preference_setup/loadout/loadout_mask.dm
@@ -0,0 +1,5 @@
+// Mask
+/datum/gear/mask
+ display_name = "sterile mask"
+ path = /obj/item/clothing/mask/surgical
+ cost = 2
diff --git a/code/modules/client/preference_setup/loadout/loadout_shoes.dm b/code/modules/client/preference_setup/loadout/loadout_shoes.dm
new file mode 100644
index 00000000000..473714a5b81
--- /dev/null
+++ b/code/modules/client/preference_setup/loadout/loadout_shoes.dm
@@ -0,0 +1,45 @@
+// Shoelocker
+/datum/gear/shoes
+ display_name = "jackboots"
+ path = /obj/item/clothing/shoes/jackboots
+ slot = slot_shoes
+ sort_category = "Shoes and Footwear"
+
+/datum/gear/shoes/toeless
+ display_name = "toe-less jackboots"
+ path = /obj/item/clothing/shoes/jackboots/unathi
+
+/datum/gear/shoes/workboots
+ display_name = "workboots"
+ path = /obj/item/clothing/shoes/workboots
+
+/datum/gear/shoes/sandals
+ display_name = "sandals"
+ path = /obj/item/clothing/shoes/sandal
+
+/datum/gear/shoes/color
+ display_name = "shoe selection"
+ path = /obj/item/clothing/shoes/black
+
+/datum/gear/shoes/color/New()
+ ..()
+ var/shoes = list()
+ shoes["black shoes"] = /obj/item/clothing/shoes/black
+ shoes["blue shoes"] = /obj/item/clothing/shoes/blue
+ shoes["brown shoes"] = /obj/item/clothing/shoes/brown
+ shoes["green shoes"] = /obj/item/clothing/shoes/green
+ shoes["orange shoes"] = /obj/item/clothing/shoes/orange
+ shoes["purple shoes"] = /obj/item/clothing/shoes/purple
+ shoes["rainbow shoes"] = /obj/item/clothing/shoes/rainbow
+ shoes["red shoes"] = /obj/item/clothing/shoes/red
+ shoes["white shoes"] = /obj/item/clothing/shoes/white
+ shoes["yellow shoes"] = /obj/item/clothing/shoes/yellow
+ gear_tweaks += new/datum/gear_tweak/path(shoes)
+
+/datum/gear/shoes/dress
+ display_name = "shoes, dress"
+ path = /obj/item/clothing/shoes/laceup
+
+/datum/gear/shoes/leather
+ display_name = "shoes, leather"
+ path = /obj/item/clothing/shoes/leather
diff --git a/code/modules/client/preference_setup/loadout/loadout_smoking.dm b/code/modules/client/preference_setup/loadout/loadout_smoking.dm
new file mode 100644
index 00000000000..d41c0d44c18
--- /dev/null
+++ b/code/modules/client/preference_setup/loadout/loadout_smoking.dm
@@ -0,0 +1,27 @@
+/datum/gear/smokingpipe
+ display_name = "pipe, smoking"
+ path = /obj/item/clothing/mask/smokable/pipe
+
+/datum/gear/cornpipe
+ display_name = "pipe, corn"
+ path = /obj/item/clothing/mask/smokable/pipe/cobpipe
+
+/datum/gear/matchbook
+ display_name = "matchbook"
+ path = /obj/item/weapon/storage/box/matches
+
+/datum/gear/zippo
+ display_name = "zippo"
+ path = /obj/item/weapon/flame/lighter/zippo
+
+/datum/gear/cigarcase
+ display_name = "cigar case"
+ path = /obj/item/weapon/storage/fancy/cigar
+
+/datum/gear/cigarette
+ display_name = "cigarette packet"
+ path = /obj/item/weapon/storage/fancy/cigarettes
+
+/datum/gear/dromedaryco
+ display_name = "dromedaryco cigarette packet"
+ path = /obj/item/weapon/storage/fancy/cigarettes/dromedaryco
\ No newline at end of file
diff --git a/code/modules/client/preference_setup/loadout/loadout_suit.dm b/code/modules/client/preference_setup/loadout/loadout_suit.dm
new file mode 100644
index 00000000000..2973900b7c2
--- /dev/null
+++ b/code/modules/client/preference_setup/loadout/loadout_suit.dm
@@ -0,0 +1,129 @@
+// Suit slot
+/datum/gear/suit
+ display_name = "apron, blue"
+ path = /obj/item/clothing/suit/apron
+ slot = slot_wear_suit
+ sort_category = "Suits and Overwear"
+ cost = 2
+
+/datum/gear/suit/leather
+ display_name = "jacket selection"
+ path = /obj/item/clothing/suit/storage/leather_jacket
+
+/datum/gear/suit/leather/New()
+ ..()
+ var/jackets = list()
+ jackets["bomber jacket"] = /obj/item/clothing/suit/storage/toggle/bomber
+ jackets["corporate black jacket"] = /obj/item/clothing/suit/storage/leather_jacket/nanotrasen
+ jackets["corporate brown jacket"] = /obj/item/clothing/suit/storage/toggle/brown_jacket/nanotrasen
+ jackets["black jacket"] = /obj/item/clothing/suit/storage/leather_jacket
+ jackets["brown jacket"] = /obj/item/clothing/suit/storage/toggle/brown_jacket
+ gear_tweaks += new/datum/gear_tweak/path(jackets)
+
+/datum/gear/suit/hazard_vest
+ display_name = "hazard vest"
+ path = /obj/item/clothing/suit/storage/hazardvest
+
+/datum/gear/suit/hoodie
+ display_name = "hoodie, grey"
+ path = /obj/item/clothing/suit/storage/toggle/hoodie
+
+/datum/gear/suit/hoodie/black
+ display_name = "hoodie, black"
+ path = /obj/item/clothing/suit/storage/toggle/hoodie/black
+
+/datum/gear/suit/labcoat
+ display_name = "labcoat selection"
+ path = /obj/item/clothing/suit/storage/toggle/labcoat
+
+/datum/gear/suit/labcoat/New()
+ ..()
+ var/labcoat = list()
+ labcoat["labcoat"] = /obj/item/clothing/suit/storage/toggle/labcoat
+ labcoat["labcoat, blue"] = /obj/item/clothing/suit/storage/toggle/labcoat/blue
+ labcoat["labcoat, green"] = /obj/item/clothing/suit/storage/toggle/labcoat/green
+ labcoat["labcoat, orange"] = /obj/item/clothing/suit/storage/toggle/labcoat/orange
+ labcoat["labcoat, purple"] = /obj/item/clothing/suit/storage/toggle/labcoat/purple
+ labcoat["labcoat, red"] = /obj/item/clothing/suit/storage/toggle/labcoat/red
+ gear_tweaks += new/datum/gear_tweak/path(labcoat)
+
+/datum/gear/suit/overalls
+ display_name = "overalls"
+ path = /obj/item/clothing/suit/apron/overalls
+ cost = 1
+
+/datum/gear/suit/poncho
+ display_name = "poncho selection"
+ path = /obj/item/clothing/suit/poncho
+ cost = 1
+
+/datum/gear/suit/poncho/New()
+ ..()
+ var/poncho = list()
+ poncho["poncho, tan"] = /obj/item/clothing/suit/poncho
+ poncho["poncho, blue"] = /obj/item/clothing/suit/poncho/blue
+ poncho["poncho, green"] = /obj/item/clothing/suit/poncho/green
+ poncho["poncho, purple"] = /obj/item/clothing/suit/poncho/purple
+ poncho["poncho, red"] = /obj/item/clothing/suit/poncho/red
+ gear_tweaks += new/datum/gear_tweak/path(poncho)
+
+/datum/gear/suit/blue_lawyer_jacket
+ display_name = "suit jacket, blue"
+ path = /obj/item/clothing/suit/storage/toggle/lawyer/bluejacket
+
+/datum/gear/suit/purple_lawyer_jacket
+ display_name = "suit jacket, purple"
+ path = /obj/item/clothing/suit/storage/lawyer/purpjacket
+
+/datum/gear/suit/track_jacket
+ display_name = "track jacket"
+ path = /obj/item/clothing/suit/storage/toggle/tracksuit
+
+/datum/gear/suit/winter
+ display_name = "winter coat"
+ path = /obj/item/clothing/suit/storage/hooded/wintercoat
+
+/datum/gear/suit/winter/captain
+ display_name = "winter coat, captain"
+ path = /obj/item/clothing/suit/storage/hooded/wintercoat/captain
+ allowed_roles = list("Captain")
+
+/datum/gear/suit/winter/security
+ display_name = "winter coat, security"
+ path = /obj/item/clothing/suit/storage/hooded/wintercoat/security
+ allowed_roles = list("Security Officer","Head of Security","Warden","Security Cadet","Detective")
+
+/datum/gear/suit/winter/science
+ display_name = "winter coat, science"
+ path = /obj/item/clothing/suit/storage/hooded/wintercoat/science
+ allowed_roles = list("Research Director","Scientist","Xenobiologist","Roboticist","Lab Assistant","Geneticist")
+
+/datum/gear/suit/winter/medical
+ display_name = "winter coat, medical"
+ path = /obj/item/clothing/suit/storage/hooded/wintercoat/medical
+ allowed_roles = list("Chief Medical Officer","Medical Doctor","Paramedic","Nursing Intern","Psychiatrist","Chemist",)
+
+/datum/gear/suit/winter/engineering
+ display_name = "winter coat, engineering"
+ path = /obj/item/clothing/suit/storage/hooded/wintercoat/engineering
+ allowed_roles = list("Station Engineer","Chief Engineer","Engineering Apprentice")
+
+/datum/gear/suit/winter/atmos
+ display_name = "winter coat, atmospherics"
+ path = /obj/item/clothing/suit/storage/hooded/wintercoat/engineering/atmos
+ allowed_roles = list("Atmospheric Technician","Chief Engineer")
+
+/datum/gear/suit/winter/hydro
+ display_name = "winter coat, hydroponics"
+ path = /obj/item/clothing/suit/storage/hooded/wintercoat/hydro
+ allowed_roles = list("Head of Personnel","Gardener")
+
+/datum/gear/suit/winter/cargo
+ display_name = "winter coat, cargo"
+ path = /obj/item/clothing/suit/storage/hooded/wintercoat/cargo
+ allowed_roles = list("Cargo Technician","Quartermaster","Head of Personnel")
+
+/datum/gear/suit/winter/mining
+ display_name = "winter coat, mining"
+ path = /obj/item/clothing/suit/storage/hooded/wintercoat/miner
+ allowed_roles = list("Quartermaster","Head of Personnel","Shaft Miner")
\ No newline at end of file
diff --git a/code/modules/client/preference_setup/loadout/loadout_uniform.dm b/code/modules/client/preference_setup/loadout/loadout_uniform.dm
new file mode 100644
index 00000000000..22705c17560
--- /dev/null
+++ b/code/modules/client/preference_setup/loadout/loadout_uniform.dm
@@ -0,0 +1,150 @@
+// Uniform slot
+/datum/gear/uniform
+ display_name = "blazer, blue"
+ path = /obj/item/clothing/under/blazer
+ slot = slot_w_uniform
+ sort_category = "Uniforms and Casual Dress"
+
+/datum/gear/uniform/cheongsam
+ display_name = "cheongsam, white"
+ path = /obj/item/clothing/under/cheongsam
+
+/datum/gear/uniform/kilt
+ display_name = "kilt"
+ path = /obj/item/clothing/under/kilt
+
+/datum/gear/uniform/jumpskirt
+ display_name = "jumpskirt, black"
+ path = /obj/item/clothing/under/blackjumpskirt
+
+/datum/gear/uniform/jumpsuit
+ display_name = "generic jumpsuits"
+ path = /obj/item/clothing/under/color/grey
+
+/datum/gear/uniform/jumpsuit/New()
+ ..()
+ var/jumpsuit = list()
+ jumpsuit["grey jumpsuit"] = /obj/item/clothing/under/color/grey
+ jumpsuit["black jumpsuit"] = /obj/item/clothing/under/color/black
+ jumpsuit["blue jumpsuit"] = /obj/item/clothing/under/color/blue
+ jumpsuit["green jumpsuit"] = /obj/item/clothing/under/color/green
+ jumpsuit["orange jumpsuit"] = /obj/item/clothing/under/color/orange
+ jumpsuit["pink jumpsuit"] = /obj/item/clothing/under/color/pink
+ jumpsuit["red jumpsuit"] = /obj/item/clothing/under/color/red
+ jumpsuit["white jumpsuit"] = /obj/item/clothing/under/color/white
+ jumpsuit["yellow jumpsuit"] = /obj/item/clothing/under/color/yellow
+ jumpsuit["light blue jumpsuit"] = /obj/item/clothing/under/lightblue
+ jumpsuit["aqua jumpsuit"] = /obj/item/clothing/under/aqua
+ jumpsuit["purple jumpsuit"] = /obj/item/clothing/under/purple
+ jumpsuit["light purple jumpsuit"] = /obj/item/clothing/under/lightpurple
+ jumpsuit["light green jumpsuit"] = /obj/item/clothing/under/lightgreen
+ jumpsuit["brown jumpsuit"] = /obj/item/clothing/under/brown
+ jumpsuit["light brown jumpsuit"] = /obj/item/clothing/under/lightbrown
+ jumpsuit["yellow green jumpsuit"] = /obj/item/clothing/under/yellowgreen
+ jumpsuit["light red jumpsuit"] = /obj/item/clothing/under/lightred
+ jumpsuit["dark red jumpsuit"] = /obj/item/clothing/under/darkred
+ gear_tweaks += new/datum/gear_tweak/path(jumpsuit)
+
+/datum/gear/uniform/skirt
+ display_name = "plaid skirt, blue"
+ path = /obj/item/clothing/under/dress/plaid_blue
+
+/datum/gear/uniform/skirt/purple
+ display_name = "plaid skirt, purple"
+ path = /obj/item/clothing/under/dress/plaid_purple
+
+/datum/gear/uniform/skirt/red
+ display_name = "plaid skirt, red"
+ path = /obj/item/clothing/under/dress/plaid_red
+
+/datum/gear/uniform/suit
+ display_name = "suit selection"
+ path = /obj/item/clothing/under/lawyer/bluesuit
+
+/datum/gear/uniform/suit/New()
+ ..()
+ var/suits = list()
+ suits["amish suit"] = /obj/item/clothing/under/sl_suit
+ suits["black suit"] = /obj/item/clothing/under/suit_jacket
+ suits["blue suit"] = /obj/item/clothing/under/lawyer/blue
+ suits["burgundy suit"] = /obj/item/clothing/under/suit_jacket/burgundy
+ suits["charcoal suit"] = /obj/item/clothing/under/suit_jacket/charcoal
+ suits["checkered suit"] = /obj/item/clothing/under/suit_jacket/checkered
+ suits["executive suit"] = /obj/item/clothing/under/suit_jacket/really_black
+ suits["female executive suit"] = /obj/item/clothing/under/suit_jacket/female
+ suits["gentleman suit"] = /obj/item/clothing/under/gentlesuit
+ suits["navy suit"] = /obj/item/clothing/under/suit_jacket/navy
+ suits["old man suit"] = /obj/item/clothing/under/lawyer/oldman
+ suits["purple suit"] = /obj/item/clothing/under/lawyer/purpsuit
+ suits["red suit"] = /obj/item/clothing/under/suit_jacket/red
+ suits["red lawyer suit"] = /obj/item/clothing/under/lawyer/red
+ suits["shiny black suit"] = /obj/item/clothing/under/lawyer/black
+ suits["tan suit"] = /obj/item/clothing/under/suit_jacket/tan
+ suits["white suit"] = /obj/item/clothing/under/scratch
+ suits["white-blue suit"] = /obj/item/clothing/under/lawyer/bluesuit
+ gear_tweaks += new/datum/gear_tweak/path(suits)
+
+/datum/gear/uniform/scrubs
+ display_name = "scrubs selection"
+ path = /obj/item/clothing/under/rank/medical/black
+ allowed_roles = list("Scientist","Chief Medical Officer","Medical Doctor","Geneticist","Paramedic","Nursing Intern","Xenobiologist","Roboticist","Research Director","Detective",)
+
+/datum/gear/uniform/scrubs/New()
+ ..()
+ var/scrubs = list()
+ scrubs["scrubs, black"] = /obj/item/clothing/under/rank/medical/black
+ scrubs["scrubs, blue"] = /obj/item/clothing/under/rank/medical/blue
+ scrubs["scrubs, green"] = /obj/item/clothing/under/rank/medical/green
+ scrubs["scrubs, purple"] = /obj/item/clothing/under/rank/medical/purple
+ gear_tweaks += new/datum/gear_tweak/path(scrubs)
+
+/datum/gear/uniform/sundress
+ display_name = "sundress"
+ path = /obj/item/clothing/under/sundress
+
+/datum/gear/uniform/sundress/white
+ display_name = "sundress, white"
+ path = /obj/item/clothing/under/sundress_white
+
+/datum/gear/uniform/dress_fire
+ display_name = "flame dress"
+ path = /obj/item/clothing/under/dress/dress_fire
+
+/datum/gear/uniform/uniform_captain
+ display_name = "uniform, captain's dress"
+ path = /obj/item/clothing/under/dress/dress_cap
+ allowed_roles = list("Captain")
+
+/datum/gear/uniform/corpsecsuit
+ display_name = "uniform, corporate (Security)"
+ path = /obj/item/clothing/under/rank/security/corp
+ allowed_roles = list("Security Officer","Head of Security","Warden")
+
+/datum/gear/uniform/uniform_hop
+ display_name = "uniform, HoP's dress"
+ path = /obj/item/clothing/under/dress/dress_hop
+ allowed_roles = list("Head of Personnel")
+
+/datum/gear/uniform/uniform_hr
+ display_name = "uniform, HR director (HoP)"
+ path = /obj/item/clothing/under/dress/dress_hr
+ allowed_roles = list("Head of Personnel")
+
+/datum/gear/uniform/navysecsuit
+ display_name = "uniform, navyblue (Security)"
+ path = /obj/item/clothing/under/rank/security/navyblue
+ allowed_roles = list("Security Officer","Head of Security","Warden")
+
+/datum/gear/uniform/gearharness
+ display_name = "gear harness"
+ path = /obj/item/clothing/under/gearharness
+ cost = 2
+
+/datum/gear/uniform/track_pants
+ display_name = "track pants"
+ path = /obj/item/clothing/under/track
+
+/datum/gear/uniform/turtleneck
+ display_name = "tacticool turtleneck"
+ path = /obj/item/clothing/under/syndicate/tacticool
+
diff --git a/code/modules/client/preference_setup/loadout/loadout_utility.dm b/code/modules/client/preference_setup/loadout/loadout_utility.dm
new file mode 100644
index 00000000000..6fd34e628c3
--- /dev/null
+++ b/code/modules/client/preference_setup/loadout/loadout_utility.dm
@@ -0,0 +1,55 @@
+/datum/gear/utility
+ display_name = "briefcase"
+ path = /obj/item/weapon/storage/briefcase
+ sort_category = "Utility"
+
+/datum/gear/utility/secure
+ display_name = "secure briefcase"
+ path = /obj/item/weapon/storage/secure/briefcase
+ cost = 2
+
+/datum/gear/utility/clipboard
+ display_name = "clipboard"
+ path = /obj/item/weapon/clipboard
+
+/datum/gear/utility/folder
+ display_name = "folders"
+ path = /obj/item/weapon/folder
+
+/datum/gear/utility/folder/New()
+ ..()
+ var/folders = list()
+ folders["blue folder"] = /obj/item/weapon/folder/blue
+ folders["grey folder"] = /obj/item/weapon/folder
+ folders["red folder"] = /obj/item/weapon/folder/red
+ folders["white folder"] = /obj/item/weapon/folder/white
+ folders["yellow folder"] = /obj/item/weapon/folder/yellow
+ gear_tweaks += new/datum/gear_tweak/path(folders)
+
+/datum/gear/utility/paicard
+ display_name = "personal AI device"
+ path = /obj/item/device/paicard
+
+/datum/gear/utility/wallet
+ display_name = "wallet"
+ path = /obj/item/weapon/storage/wallet
+
+/*
+/datum/gear/utility/cheaptablet
+ display_name = "cheap tablet computer"
+ path = /obj/item/modular_computer/tablet/preset/custom_loadout/cheap
+ cost = 3
+
+/datum/gear/utility/normaltablet
+ display_name = "tablet computer"
+ path = /obj/item/modular_computer/tablet/preset/custom_loadout/advanced
+ cost = 4
+*/
+
+/datum/gear/utility/recorder
+ display_name = "universal recorder"
+ path = /obj/item/device/taperecorder
+
+/datum/gear/utility/camera
+ display_name = "camera"
+ path = /obj/item/device/camera
diff --git a/code/modules/client/preference_setup/loadout/loadout_xeno.dm b/code/modules/client/preference_setup/loadout/loadout_xeno.dm
new file mode 100644
index 00000000000..6ee001c5ace
--- /dev/null
+++ b/code/modules/client/preference_setup/loadout/loadout_xeno.dm
@@ -0,0 +1,108 @@
+//unathi clothing
+
+/datum/gear/suit/unathi_mantle
+ display_name = "hide mantle (Unathi)"
+ path = /obj/item/clothing/suit/unathi/mantle
+ cost = 1
+ whitelisted = "Unathi"
+ sort_category = "Xenowear"
+
+/datum/gear/suit/unathi_robe
+ display_name = "roughspun robe (Unathi)"
+ path = /obj/item/clothing/suit/unathi/robe
+ cost = 1
+ whitelisted = "Unathi"
+ sort_category = "Xenowear"
+
+//skrell headtail adorns
+
+/datum/gear/ears/f_skrell
+ display_name = "headtail-wear, female (Skrell)"
+ path = /obj/item/clothing/ears/skrell/chain
+ sort_category = "Xenowear"
+ whitelisted = "Skrell"
+
+/datum/gear/ears/f_skrell/New()
+ ..()
+ var/f_chains = list()
+ f_chains["headtail chains"] = /obj/item/clothing/ears/skrell/chain
+ f_chains["headtail cloth"] = /obj/item/clothing/ears/skrell/cloth_female
+ f_chains["red-jeweled chain"] = /obj/item/clothing/ears/skrell/redjewel_chain
+ f_chains["ebony chain"] = /obj/item/clothing/ears/skrell/ebony_chain
+ f_chains["blue-jeweled chain"] = /obj/item/clothing/ears/skrell/bluejeweled_chain
+ f_chains["silver chain"] = /obj/item/clothing/ears/skrell/silver_chain
+ f_chains["blue cloth"] = /obj/item/clothing/ears/skrell/blue_skrell_cloth_band_female
+ gear_tweaks += new/datum/gear_tweak/path(f_chains)
+
+/datum/gear/ears/m_skrell
+ display_name = "headtail-wear, male (Skrell)"
+ path = /obj/item/clothing/ears/skrell/band
+ sort_category = "Xenowear"
+ whitelisted = "Skrell"
+
+/datum/gear/ears/m_skrell/New()
+ ..()
+ var/m_chains = list()
+ m_chains["headtail bands"] = /obj/item/clothing/ears/skrell/chain
+ m_chains["headtail cloth"] = /obj/item/clothing/ears/skrell/cloth_male
+ m_chains["red-jeweled bands"] = /obj/item/clothing/ears/skrell/redjeweled_band
+ m_chains["ebony bands"] = /obj/item/clothing/ears/skrell/ebony_band
+ m_chains["blue-jeweled bands"] = /obj/item/clothing/ears/skrell/bluejeweled_band
+ m_chains["silver bands"] = /obj/item/clothing/ears/skrell/silver_band
+ m_chains["blue cloth"] = /obj/item/clothing/ears/skrell/blue_skrell_cloth_band_male
+ gear_tweaks += new/datum/gear_tweak/path(m_chains)
+
+//vaurca items
+
+/datum/gear/eyes/blindfold
+ display_name = "vaurca blindfold (Vaurca)"
+ path = /obj/item/clothing/glasses/sunglasses/blinders
+ cost = 2
+ whitelisted = "Vaurca"
+ sort_category = "Xenowear"
+
+/datum/gear/mask/vaurca
+ display_name = "mandible garment (Vaurca)"
+ path = /obj/item/clothing/mask/breath/vaurca
+ cost = 1
+ whitelisted = "Vaurca"
+ sort_category = "Xenowear"
+
+//beastmen gloves
+
+/datum/gear/gloves/unathi
+ display_name = "gloves selection (Unathi)"
+ path = /obj/item/clothing/gloves/black/unathi
+ whitelisted = "Unathi"
+ sort_category = "Xenowear"
+
+/datum/gear/gloves/unathi/New()
+ ..()
+ var/un_gloves = list()
+ un_gloves["black gloves"] = /obj/item/clothing/gloves/black/unathi
+ un_gloves["red gloves"] = /obj/item/clothing/gloves/red/unathi
+ un_gloves["blue gloves"] = /obj/item/clothing/gloves/blue/unathi
+ un_gloves["orange gloves"] = /obj/item/clothing/gloves/orange/unathi
+ un_gloves["purple gloves"] = /obj/item/clothing/gloves/purple/unathi
+ un_gloves["brown gloves"] = /obj/item/clothing/gloves/brown/unathi
+ un_gloves["green gloves"] = /obj/item/clothing/gloves/green/unathi
+ un_gloves["white gloves"] = /obj/item/clothing/gloves/white/unathi
+ gear_tweaks += new/datum/gear_tweak/path(un_gloves)
+
+/datum/gear/gloves/tajara
+ display_name = "gloves selection (Tajara)"
+ path = /obj/item/clothing/gloves/black/tajara
+ whitelisted = "Tajara"
+ sort_category = "Xenowear"
+
+/datum/gear/gloves/tajara/New()
+ ..()
+ var/taj_gloves = list()
+ taj_gloves["black gloves"] = /obj/item/clothing/gloves/black/tajara
+ taj_gloves["red gloves"] = /obj/item/clothing/gloves/red/tajara
+ taj_gloves["blue gloves"] = /obj/item/clothing/gloves/blue/tajara
+ taj_gloves["orange gloves"] = /obj/item/clothing/gloves/orange/tajara
+ taj_gloves["purple gloves"] = /obj/item/clothing/gloves/purple/tajara
+ taj_gloves["brown gloves"] = /obj/item/clothing/gloves/brown/tajara
+ taj_gloves["white gloves"] = /obj/item/clothing/gloves/green/tajara
+ gear_tweaks += new/datum/gear_tweak/path(taj_gloves)
diff --git a/code/modules/client/preference_setup/preference_setup.dm b/code/modules/client/preference_setup/preference_setup.dm
index 237c4fa0a78..99af44e65eb 100644
--- a/code/modules/client/preference_setup/preference_setup.dm
+++ b/code/modules/client/preference_setup/preference_setup.dm
@@ -27,6 +27,11 @@
sort_order = 4
category_item_type = /datum/category_item/player_setup_item/antagonism
+/datum/category_group/player_setup_category/loadout_preferences
+ name = "Loadout"
+ sort_order = 5
+ category_item_type = /datum/category_item/player_setup_item/loadout
+
/datum/category_group/player_setup_category/global_preferences
name = "Global"
sort_order = 5
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 036df67ecd1..5455a4370ed 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -125,6 +125,10 @@ datum/preferences
// OOC Metadata:
var/metadata = ""
+ // SPAAAACE
+ var/parallax_speed = 2
+ var/parallax_togs = PARALLAX_SPACE
+
var/list/pai = list() // A list for holding pAI related data.
var/client/client = null
diff --git a/code/modules/client/preferences_gear.dm b/code/modules/client/preferences_gear.dm
deleted file mode 100644
index d355926c873..00000000000
--- a/code/modules/client/preferences_gear.dm
+++ /dev/null
@@ -1,1546 +0,0 @@
-var/global/list/gear_datums = list()
-
-/hook/startup/proc/populate_gear_list()
- var/list/sort_categories = list(
- "[slot_head]" = list(),
- "ears" = list(),
- "[slot_glasses]" = list(),
- "[slot_wear_mask]" = list(),
- "[slot_w_uniform]" = list(),
- "[slot_tie]" = list(),
- "[slot_wear_suit]" = list(),
- "[slot_gloves]" = list(),
- "[slot_shoes]" = list(),
- "utility" = list(),
- "misc" = list(),
- "unknown" = list()
- )
-
- //create a list of gear datums to sort
- for(var/type in typesof(/datum/gear)-/datum/gear)
- var/datum/gear/G = type
- if(!initial(G.display_name))
- error("Loadout - Missing display name: [G]")
- continue
- if(!initial(G.cost))
- error("Loadout - Missing cost: [G]")
- continue
- if(!initial(G.path))
- error("Loadout - Missing path definition: [G]")
- continue
- G = new G()
-
- var/category = (G.sort_category in sort_categories)? G.sort_category : "unknown"
- sort_categories[category][G.display_name] = G
-
- for (var/category in sort_categories)
- gear_datums.Add(sortAssoc(sort_categories[category]))
-
- return 1
-
-/datum/gear
- var/display_name //Name/index. Must be unique.
- var/path //Path to item.
- var/cost //Number of points used. Items in general cost 1 point, storage/armor/gloves/special use costs 2 points.
- var/slot //Slot to equip to.
- var/list/allowed_roles //Roles that can spawn with this item.
- var/whitelisted //Term to check the whitelist for..
- var/sort_category
-
-/datum/gear/New()
- ..()
- if (!sort_category)
- sort_category = "[slot]"
-
-// This is sorted both by slot and alphabetically! Don't fuck it up!
-// Headslot items
-
-/datum/gear/gbandana
- display_name = "bandana, green"
- path = /obj/item/clothing/head/greenbandana
- cost = 1
- slot = slot_head
-
-/datum/gear/obandana
- display_name = "bandana, orange"
- path = /obj/item/clothing/head/orangebandana
- cost = 1
- slot = slot_head
-
-/datum/gear/bandana
- display_name = "bandana, pirate-red"
- path = /obj/item/clothing/head/bandana
- cost = 1
- slot = slot_head
-
-/datum/gear/bsec_beret
- display_name = "beret, navy (officer)"
- path = /obj/item/clothing/head/beret/sec
- cost = 1
- slot = slot_head
- allowed_roles = list("Security Officer","Head of Security","Warden","Security Cadet","Detective")
-
-/datum/gear/bsec_beret_warden
- display_name = "beret, navy (warden)"
- path = /obj/item/clothing/head/beret/sec/warden
- cost = 1
- slot = slot_head
- allowed_roles = list("Head of Security","Warden")
-
-/datum/gear/bsec_beret_hos
- display_name = "beret, navy (hos)"
- path = /obj/item/clothing/head/beret/sec/hos
- cost = 1
- slot = slot_head
- allowed_roles = list("Head of Security")
-
-/datum/gear/eng_beret
- display_name = "beret, engie-orange"
- path = /obj/item/clothing/head/beret/engineering
- cost = 1
- slot = slot_head
- allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Engineering Apprentice")
-
-/datum/gear/purp_beret
- display_name = "beret, purple"
- path = /obj/item/clothing/head/beret/purple
- cost = 1
- slot = slot_head
-
-/datum/gear/red_beret
- display_name = "beret, red"
- path = /obj/item/clothing/head/beret
- cost = 1
- slot = slot_head
-
-/datum/gear/sec_beret
- display_name = "beret, security"
- path = /obj/item/clothing/head/beret/sec
- cost = 1
- slot = slot_head
- allowed_roles = list("Security Officer","Head of Security","Warden","Security Cadet","Detective")
-
-/datum/gear/bcap
- display_name = "cap, blue"
- path = /obj/item/clothing/head/soft/blue
- cost = 1
- slot = slot_head
-
-/datum/gear/mailman
- display_name = "cap, blue station"
- path = /obj/item/clothing/head/mailman
- cost = 1
- slot = slot_head
-
-/datum/gear/flatcap
- display_name = "cap, brown-flat"
- path = /obj/item/clothing/head/flatcap
- cost = 1
- slot = slot_head
-
-/datum/gear/corpcap
- display_name = "cap, corporate (Security)"
- path = /obj/item/clothing/head/soft/sec/corp
- cost = 1
- slot = slot_head
- allowed_roles = list("Security Officer","Head of Security","Warden","Security Cadet","Detective")
-
-/datum/gear/gcap
- display_name = "cap, green"
- path = /obj/item/clothing/head/soft/green
- cost = 1
- slot = slot_head
-
-/datum/gear/grcap
- display_name = "cap, grey"
- path = /obj/item/clothing/head/soft/grey
- cost = 1
- slot = slot_head
-
-/datum/gear/ocap
- display_name = "cap, orange"
- path = /obj/item/clothing/head/soft/orange
- cost = 1
- slot = slot_head
-
-/datum/gear/purcap
- display_name = "cap, purple"
- path = /obj/item/clothing/head/soft/purple
- cost = 1
- slot = slot_head
-
-/datum/gear/raincap
- display_name = "cap, rainbow"
- path = /obj/item/clothing/head/soft/rainbow
- cost = 1
- slot = slot_head
-
-/datum/gear/rcap
- display_name = "cap, red"
- path = /obj/item/clothing/head/soft/red
- cost = 1
- slot = slot_head
-
-/datum/gear/ycap
- display_name = "cap, yellow"
- path = /obj/item/clothing/head/soft/yellow
- cost = 1
- slot = slot_head
-
-/datum/gear/wcap
- display_name = "cap, white"
- path = /obj/item/clothing/head/soft/mime
- cost = 1
- slot = slot_head
-
-/datum/gear/hairflower
- display_name = "hair flower pin"
- path = /obj/item/clothing/head/hairflower
- cost = 1
- slot = slot_head
-
-/datum/gear/dbhardhat
- display_name = "hardhat, blue"
- path = /obj/item/clothing/head/hardhat/dblue
- cost = 2
- slot = slot_head
- allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Engineering Apprentice")
-
-/datum/gear/ohardhat
- display_name = "hardhat, orange"
- path = /obj/item/clothing/head/hardhat/orange
- cost = 2
- slot = slot_head
- allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Engineering Apprentice")
-
-/datum/gear/rhardhat
- display_name = "hardhat, red"
- path = /obj/item/clothing/head/hardhat/red
- cost = 2
- slot = slot_head
- allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Engineering Apprentice")
-
-/datum/gear/yhardhat
- display_name = "hardhat, yellow"
- path = /obj/item/clothing/head/hardhat
- cost = 2
- slot = slot_head
- allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Engineering Apprentice")
-
-/datum/gear/boater
- display_name = "hat, boatsman"
- path = /obj/item/clothing/head/boaterhat
- cost = 1
- slot = slot_head
-
-/datum/gear/bowler
- display_name = "hat, bowler"
- path = /obj/item/clothing/head/bowler
- cost = 1
- slot = slot_head
-
-/datum/gear/fez
- display_name = "hat, fez"
- path = /obj/item/clothing/head/fez
- cost = 1
- slot = slot_head
-
-/datum/gear/tophat
- display_name = "hat, tophat"
- path = /obj/item/clothing/head/that
- cost = 1
- slot = slot_head
-
-// Wig by Earthcrusher, blame him.
-/datum/gear/philosopher_wig
- display_name = "natural philosopher's wig"
- path = /obj/item/clothing/head/philosopher_wig
- cost = 1
- slot = slot_head
-
-/datum/gear/ushanka
- display_name = "ushanka"
- path = /obj/item/clothing/head/ushanka
- cost = 1
- slot = slot_head
-
-//lol fuck bay ~LordFowl
-/datum/gear/zhan_scarf
- display_name = "Zhan headscarf"
- path = /obj/item/clothing/head/tajaran/scarf
- cost = 1
- slot = slot_head
- whitelisted = "Tajara"
-
-// Eyes
-
-/datum/gear/eyepatch
- display_name = "eyepatch"
- path = /obj/item/clothing/glasses/eyepatch
- cost = 1
- slot = slot_glasses
-
-/datum/gear/green_glasses
- display_name = "glasses, green"
- path = /obj/item/clothing/glasses/gglasses
- cost = 1
- slot = slot_glasses
-
-/datum/gear/prescriptionhipster
- display_name = "glasses, hipster"
- path = /obj/item/clothing/glasses/regular/hipster
- cost = 1
- slot = slot_glasses
-
-/datum/gear/prescription
- display_name = "glasses, prescription"
- path = /obj/item/clothing/glasses/regular
- cost = 1
- slot = slot_glasses
-
-/datum/gear/monocle
- display_name = "monocle"
- path = /obj/item/clothing/glasses/monocle
- cost = 1
- slot = slot_glasses
-
-/datum/gear/scanning_goggles
- display_name = "scanning goggles"
- path = /obj/item/clothing/glasses/regular/scanners
- cost = 1
- slot = slot_glasses
-
-/datum/gear/sciencegoggles
- display_name = "science Goggles"
- path = /obj/item/clothing/glasses/science
- cost = 1
- slot = slot_glasses
-
-/datum/gear/security
- display_name = "security HUD"
- path = /obj/item/clothing/glasses/hud/security
- cost = 1
- slot = slot_glasses
- allowed_roles = list("Security Officer","Head of Security","Warden","Security Cadet","Detective")
-
-/datum/gear/thugshades
- display_name = "sunglasses, fat"
- path = /obj/item/clothing/glasses/sunglasses/big
- cost = 1
- slot = slot_glasses
- allowed_roles = list("Security Officer","Head of Security","Warden","Detective","Internal Affairs Agent","Quartermaster","Head of Personnel","Captain","Security Cadet")
-
-/datum/gear/prescriptionsun
- display_name = "sunglasses, presciption"
- path = /obj/item/clothing/glasses/sunglasses/prescription
- cost = 2
- slot = slot_glasses
- allowed_roles = list("Security Officer","Head of Security","Warden","Detective","Internal Affairs Agent","Quartermaster","Head of Personnel","Captain","Security Cadet")
-
-/datum/gear/blindfold
- display_name = "vaurca blindfold"
- path = /obj/item/clothing/glasses/sunglasses/blinders
- cost = 2
- slot = slot_glasses
- whitelisted = "Vaurca"
-
-// Mask
-
-/datum/gear/sterilemask
- display_name = "sterile mask"
- path = /obj/item/clothing/mask/surgical
- slot = slot_wear_mask
- cost = 2
-
-// Uniform slot
-
-/datum/gear/blazer_blue
- display_name = "blazer, blue"
- path = /obj/item/clothing/under/blazer
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/cheongsam
- display_name = "cheongsam, white"
- path = /obj/item/clothing/under/cheongsam
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/gearharness
- display_name = "gear harness"
- path = /obj/item/clothing/under/gearharness
- slot = slot_w_uniform
- cost = 2
-
-/datum/gear/kilt
- display_name = "kilt"
- path = /obj/item/clothing/under/kilt
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/track_pants
- display_name = "track pants"
- path = /obj/item/clothing/under/track
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/blackjumpskirt
- display_name = "jumpskirt, black"
- path = /obj/item/clothing/under/blackjumpskirt
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/blackfjumpsuit
- display_name = "jumpsuit, female-black"
- path = /obj/item/clothing/under/color/blackf
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/blackfjumpsuit
- display_name = "jumpsuit, rainbow"
- path = /obj/item/clothing/under/rainbow
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/skirt_blue
- display_name = "plaid skirt, blue"
- path = /obj/item/clothing/under/dress/plaid_blue
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/skirt_purple
- display_name = "plaid skirt, purple"
- path = /obj/item/clothing/under/dress/plaid_purple
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/skirt_red
- display_name = "plaid skirt, red"
- path = /obj/item/clothing/under/dress/plaid_red
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/skirt_black
- display_name = "skirt, black"
- path = /obj/item/clothing/under/blackskirt
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/amishsuit
- display_name = "suit, amish"
- path = /obj/item/clothing/under/sl_suit
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/blacksuit
- display_name = "suit, black"
- path = /obj/item/clothing/under/suit_jacket
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/shinyblacksuit
- display_name = "suit, shiny-black"
- path = /obj/item/clothing/under/lawyer/black
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/bluesuit
- display_name = "suit, blue"
- path = /obj/item/clothing/under/lawyer/blue
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/burgundysuit
- display_name = "suit, burgundy"
- path = /obj/item/clothing/under/suit_jacket/burgundy
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/checkeredsuit
- display_name = "suit, checkered"
- path = /obj/item/clothing/under/suit_jacket/checkered
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/charcoalsuit
- display_name = "suit, charcoal"
- path = /obj/item/clothing/under/suit_jacket/charcoal
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/execsuit
- display_name = "suit, executive"
- path = /obj/item/clothing/under/suit_jacket/really_black
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/femaleexecsuit
- display_name = "suit, female-executive"
- path = /obj/item/clothing/under/suit_jacket/female
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/gentlesuit
- display_name = "suit, gentlemen"
- path = /obj/item/clothing/under/gentlesuit
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/navysuit
- display_name = "suit, navy"
- path = /obj/item/clothing/under/suit_jacket/navy
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/redsuit
- display_name = "suit, red"
- path = /obj/item/clothing/under/suit_jacket/red
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/redlawyer
- display_name = "suit, lawyer-red"
- path = /obj/item/clothing/under/lawyer/red
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/oldmansuit
- display_name = "suit, old-man"
- path = /obj/item/clothing/under/lawyer/oldman
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/purplesuit
- display_name = "suit, purple"
- path = /obj/item/clothing/under/lawyer/purpsuit
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/tansuit
- display_name = "suit, tan"
- path = /obj/item/clothing/under/suit_jacket/tan
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/whitesuit
- display_name = "suit, white"
- path = /obj/item/clothing/under/scratch
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/whitebluesuit
- display_name = "suit, white-blue"
- path = /obj/item/clothing/under/lawyer/bluesuit
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/sundress
- display_name = "sundress"
- path = /obj/item/clothing/under/sundress
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/sundress_white
- display_name = "sundress, white"
- path = /obj/item/clothing/under/sundress_white
- slot = slot_w_uniform
- cost = 1
-
-/datum/gear/uniform_captain
- display_name = "uniform, captain's dress"
- path = /obj/item/clothing/under/dress/dress_cap
- slot = slot_w_uniform
- cost = 1
- allowed_roles = list("Captain")
-
-/datum/gear/corpsecsuit
- display_name = "uniform, corporate (Security)"
- path = /obj/item/clothing/under/rank/security/corp
- cost = 1
- slot = slot_w_uniform
- allowed_roles = list("Security Officer","Head of Security","Warden","Security Cadet","Detective")
-
-/datum/gear/uniform_hop
- display_name = "uniform, HoP's dress"
- path = /obj/item/clothing/under/dress/dress_hop
- slot = slot_w_uniform
- cost = 1
- allowed_roles = list("Head of Personnel")
-
-/datum/gear/uniform_hr
- display_name = "uniform, HR director (HoP)"
- path = /obj/item/clothing/under/dress/dress_hr
- slot = slot_w_uniform
- cost = 1
- allowed_roles = list("Head of Personnel")
-
-/datum/gear/navysecsuit
- display_name = "uniform, navyblue (Security)"
- path = /obj/item/clothing/under/rank/security/navyblue
- cost = 1
- slot = slot_w_uniform
- allowed_roles = list("Security Officer","Head of Security","Warden","Security Cadet","Detective")
-
-//medical scrubs
-
-/datum/gear/bluescrub
- display_name = "medical scrubs, blue"
- path = /obj/item/clothing/under/rank/medical/blue
- slot = slot_w_uniform
- cost = 1
- allowed_roles = list("Scientist","Chief Medical Officer","Medical Doctor","Geneticist","Paramedic","Nursing Intern","Xenobiologist","Roboticist","Research Director","Detective",)
-
-/datum/gear/greenscrub
- display_name = "medical scrubs, green"
- path = /obj/item/clothing/under/rank/medical/green
- slot = slot_w_uniform
- cost = 1
- allowed_roles = list("Scientist","Chief Medical Officer","Medical Doctor","Geneticist","Paramedic","Nursing Intern","Xenobiologist","Roboticist","Research Director","Detective",)
-
-/datum/gear/purplescrub
- display_name = "medical scrubs, purple"
- path = /obj/item/clothing/under/rank/medical/purple
- slot = slot_w_uniform
- cost = 1
- allowed_roles = list("Scientist","Chief Medical Officer","Medical Doctor","Geneticist","Paramedic","Nursing Intern","Xenobiologist","Roboticist","Research Director","Detective",)
-
-/datum/gear/blackscrub
- display_name = "medical scrubs, black"
- path = /obj/item/clothing/under/rank/medical/black
- slot = slot_w_uniform
- cost = 1
- allowed_roles = list("Scientist","Chief Medical Officer","Medical Doctor","Geneticist","Paramedic","Nursing Intern","Xenobiologist","Roboticist","Research Director","Detective",)
-
-// Attachments
-/datum/gear/armband_cargo
- display_name = "armband, cargo"
- path = /obj/item/clothing/accessory/armband/cargo
- slot = slot_tie
- cost = 1
- allowed_roles = list("Cargo Technician","Quartermaster","Head of Personnel","Shaft Miner")
-
-/datum/gear/armband_emt
- display_name = "armband, EMT"
- path = /obj/item/clothing/accessory/armband/medgreen
- slot = slot_tie
- cost = 1
- allowed_roles = list("Paramedic","Chief Medical Officer")
-
-/datum/gear/armband_engineering
- display_name = "armband, engineering"
- path = /obj/item/clothing/accessory/armband/engine
- slot = slot_tie
- cost = 1
- allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Engineering Apprentice")
-
-/datum/gear/armband_hydroponics
- display_name = "armband, hydroponics"
- path = /obj/item/clothing/accessory/armband/hydro
- slot = slot_tie
- cost = 1
- allowed_roles = list("Head of Personnel","Gardener")
-
-/datum/gear/armband_medical
- display_name = "armband, medical"
- path = /obj/item/clothing/accessory/armband/med
- slot = slot_tie
- cost = 1
- allowed_roles = list("Chief Medical Officer","Medical Doctor","Paramedic","Nursing Intern","Psychiatrist","Chemist",)
-
-/datum/gear/armband
- display_name = "armband, red"
- path = /obj/item/clothing/accessory/armband
- slot = slot_tie
- cost = 1
-
-/datum/gear/armband_science
- display_name = "armband, science"
- path = /obj/item/clothing/accessory/armband/science
- slot = slot_tie
- cost = 1
- allowed_roles = list("Research Director","Scientist","Xenobiologist","Roboticist","Lab Assistant","Geneticist")
-
-/datum/gear/armband_movement
- display_name = "armband, synthetic intelligence movement"
- path = /obj/item/clothing/accessory/armband/movement
- slot = slot_tie
- cost = 1
-
-/datum/gear/atlas
- display_name = "armband, ATLAS"
- path = /obj/item/clothing/accessory/armband/atlas
- slot = slot_tie
- cost = 1
-
-/datum/gear/armpit
- display_name = "holster, armpit"
- path = /obj/item/clothing/accessory/holster/armpit
- slot = slot_tie
- cost = 1
- allowed_roles = list("Captain", "Head of Personnel", "Security Officer", "Warden", "Head of Security","Detective","Security Cadet")
-
-/datum/gear/hip
- display_name = "holster, hip"
- path = /obj/item/clothing/accessory/holster/hip
- slot = slot_tie
- cost = 1
- allowed_roles = list("Captain", "Head of Personnel", "Security Officer", "Warden", "Head of Security","Detective","Security Cadet")
-
-/datum/gear/waist
- display_name = "holster, waist"
- path = /obj/item/clothing/accessory/holster/waist
- slot = slot_tie
- cost = 1
- allowed_roles = list("Captain", "Head of Personnel", "Security Officer", "Warden", "Head of Security","Detective","Security Cadet")
-
-/datum/gear/thigh
- display_name = "holster, thigh"
- path = /obj/item/clothing/accessory/holster/thigh
- slot = slot_tie
- cost = 1
- allowed_roles = list("Captain", "Head of Personnel", "Security Officer", "Warden", "Head of Security","Detective","Security Cadet")
-
-/datum/gear/tie_blue
- display_name = "tie, blue"
- path = /obj/item/clothing/accessory/blue
- slot = slot_tie
- cost = 1
-
-/datum/gear/tie_red
- display_name = "tie, red"
- path = /obj/item/clothing/accessory/red
- slot = slot_tie
- cost = 1
-
-/datum/gear/tie_horrible
- display_name = "tie, socially disgraceful"
- path = /obj/item/clothing/accessory/horrible
- slot = slot_tie
- cost = 1
-
-/datum/gear/brown_vest
- display_name = "webbing, engineering"
- path = /obj/item/clothing/accessory/storage/brown_vest
- slot = slot_tie
- cost = 1
- allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Engineering Apprentice")
-
-/datum/gear/black_vest
- display_name = "webbing, security"
- path = /obj/item/clothing/accessory/storage/black_vest
- slot = slot_tie
- cost = 1
- allowed_roles = list("Security Officer","Head of Security","Warden","Security Cadet","Detective")
-
-/datum/gear/webbing
- display_name = "webbing, simple"
- path = /obj/item/clothing/accessory/storage/webbing
- slot = slot_tie
- cost = 2
-
-/datum/gear/suspenders
- display_name = "suspenders"
- path = /obj/item/clothing/accessory/suspenders
- cost = 1
- slot = slot_tie
-
-/datum/gear/wcoat
- display_name = "waistcoat"
- path = /obj/item/clothing/accessory/wcoat
- cost = 1
- slot = slot_tie
-
-// Suit slot
-
-/datum/gear/apron
- display_name = "apron, blue"
- path = /obj/item/clothing/suit/apron
- cost = 1
- slot = slot_wear_suit
-
-/datum/gear/bomber
- display_name = "bomber jacket"
- path = /obj/item/clothing/suit/storage/toggle/bomber
- cost = 2
- slot = slot_wear_suit
-
-/datum/gear/leather_jacket
- display_name = "leather jacket, black"
- path = /obj/item/clothing/suit/storage/leather_jacket
- cost = 2
- slot = slot_wear_suit
-
-/datum/gear/leather_jacket_nt
- display_name = "leather jacket, corporate, black"
- path = /obj/item/clothing/suit/storage/leather_jacket/nanotrasen
- cost = 2
- slot = slot_wear_suit
-
-/datum/gear/brown_jacket
- display_name = "leather jacket, brown"
- path = /obj/item/clothing/suit/storage/toggle/brown_jacket
- cost = 2
- slot = slot_wear_suit
-
-/datum/gear/brown_jacket_nt
- display_name = "leather jacket, corporate, brown"
- path = /obj/item/clothing/suit/storage/toggle/brown_jacket/nanotrasen
- cost = 2
- slot = slot_wear_suit
-
-/datum/gear/hazard_vest
- display_name = "hazard vest"
- path = /obj/item/clothing/suit/storage/hazardvest
- cost = 2
- slot = slot_wear_suit
- allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Engineering Apprentice")
-
-/datum/gear/hoodie
- display_name = "hoodie, grey"
- path = /obj/item/clothing/suit/storage/toggle/hoodie
- cost = 2
- slot = slot_wear_suit
-
-/datum/gear/hoodie/black
- display_name = "hoodie, black"
- path = /obj/item/clothing/suit/storage/toggle/hoodie/black
- cost = 2
-
-/datum/gear/unathi_mantle
- display_name = "hide mantle (Unathi)"
- path = /obj/item/clothing/suit/unathi/mantle
- cost = 1
- slot = slot_wear_suit
- whitelisted = "Unathi"
-
-/datum/gear/labcoat
- display_name = "labcoat"
- path = /obj/item/clothing/suit/storage/toggle/labcoat
- cost = 2
- slot = slot_wear_suit
-
-/datum/gear/bluelabcoat
- display_name = "labcoat, blue"
- path = /obj/item/clothing/suit/storage/toggle/labcoat/blue
- cost = 2
- slot = slot_wear_suit
-
-/datum/gear/greenlabcoat
- display_name = "labcoat, green"
- path = /obj/item/clothing/suit/storage/toggle/labcoat/green
- cost = 2
- slot = slot_wear_suit
-
-/datum/gear/orangelabcoat
- display_name = "labcoat, orange"
- path = /obj/item/clothing/suit/storage/toggle/labcoat/orange
- cost = 2
- slot = slot_wear_suit
-
-/datum/gear/purplelabcoat
- display_name = "labcoat, purple"
- path = /obj/item/clothing/suit/storage/toggle/labcoat/purple
- cost = 2
- slot = slot_wear_suit
-
-/datum/gear/redlabcoat
- display_name = "labcoat, red"
- path = /obj/item/clothing/suit/storage/toggle/labcoat/red
- cost = 2
- slot = slot_wear_suit
-
-/datum/gear/overalls
- display_name = "overalls"
- path = /obj/item/clothing/suit/apron/overalls
- cost = 1
- slot = slot_wear_suit
-
-/datum/gear/bponcho
- display_name = "poncho, blue"
- path = /obj/item/clothing/suit/poncho/blue
- cost = 1
- slot = slot_wear_suit
-
-/datum/gear/gponcho
- display_name = "poncho, green"
- path = /obj/item/clothing/suit/poncho/green
- cost = 1
- slot = slot_wear_suit
-
-/datum/gear/pponcho
- display_name = "poncho, purple"
- path = /obj/item/clothing/suit/poncho/purple
- cost = 1
- slot = slot_wear_suit
-
-/datum/gear/rponcho
- display_name = "poncho, red"
- path = /obj/item/clothing/suit/poncho/red
- cost = 1
- slot = slot_wear_suit
-
-/datum/gear/poncho
- display_name = "poncho, tan"
- path = /obj/item/clothing/suit/poncho
- cost = 1
- slot = slot_wear_suit
-
-/datum/gear/unathi_robe
- display_name = "roughspun robe (Unathi)"
- path = /obj/item/clothing/suit/unathi/robe
- cost = 1
- slot = slot_wear_suit
- whitelisted = "Unathi"
-
-/datum/gear/blue_lawyer_jacket
- display_name = "suit jacket, blue"
- path = /obj/item/clothing/suit/storage/toggle/lawyer/bluejacket
- cost = 2
- slot = slot_wear_suit
-
-/datum/gear/purple_lawyer_jacket
- display_name = "suit jacket, purple"
- path = /obj/item/clothing/suit/storage/lawyer/purpjacket
- cost = 2
- slot = slot_wear_suit
-
-/datum/gear/zhan_furs
- display_name = "zhan-Khazan furs"
- path = /obj/item/clothing/suit/tajaran/furs
- cost = 1
- slot = slot_wear_suit
- whitelisted = "Tajara"
-
-/datum/gear/track_jacket
- display_name = "track jacket"
- path = /obj/item/clothing/suit/storage/toggle/tracksuit
- cost = 1
- slot = slot_wear_suit
-
-// Gloves
-
-/datum/gear/black_gloves
- display_name = "gloves, black"
- path = /obj/item/clothing/gloves/black
- cost = 2
- slot = slot_gloves
-
-/datum/gear/blue_gloves
- display_name = "gloves, blue"
- path = /obj/item/clothing/gloves/blue
- cost = 2
- slot = slot_gloves
-
-/datum/gear/brown_gloves
- display_name = "gloves, brown"
- path = /obj/item/clothing/gloves/brown
- cost = 2
- slot = slot_gloves
-
-/datum/gear/light_brown_gloves
- display_name = "gloves, light-brown"
- path = /obj/item/clothing/gloves/light_brown
- cost = 2
- slot = slot_gloves
-
-/datum/gear/green_gloves
- display_name = "gloves, green"
- path = /obj/item/clothing/gloves/green
- cost = 2
- slot = slot_gloves
-
-/datum/gear/grey_gloves
- display_name = "gloves, grey"
- path = /obj/item/clothing/gloves/grey
- cost = 2
- slot = slot_gloves
-
-/datum/gear/latex_gloves
- display_name = "gloves, latex"
- path = /obj/item/clothing/gloves/latex
- cost = 2
- slot = slot_gloves
-
-
-/datum/gear/orange_gloves
- display_name = "gloves, orange"
- path = /obj/item/clothing/gloves/orange
- cost = 2
- slot = slot_gloves
-
-/datum/gear/purple_gloves
- display_name = "gloves, purple"
- path = /obj/item/clothing/gloves/purple
- cost = 2
- slot = slot_gloves
-
-/datum/gear/rainbow_gloves
- display_name = "gloves, rainbow"
- path = /obj/item/clothing/gloves/rainbow
- cost = 2
- slot = slot_gloves
-
-/datum/gear/red_gloves
- display_name = "gloves, red"
- path = /obj/item/clothing/gloves/red
- cost = 2
- slot = slot_gloves
-
-/datum/gear/white_gloves
- display_name = "gloves, white"
- path = /obj/item/clothing/gloves/white
- cost = 2
- slot = slot_gloves
-
-/datum/gear/black_gloves_unathi
- display_name = "black gloves, unathi"
- path = /obj/item/clothing/gloves/black/unathi
- cost = 2
- slot = slot_gloves
-
-/datum/gear/black_gloves_tajara
- display_name = "black gloves, tajaran"
- path = /obj/item/clothing/gloves/black/tajara
- cost = 2
- slot = slot_gloves
-
-/datum/gear/red_gloves_unathi
- display_name = "red gloves, unathi"
- path = /obj/item/clothing/gloves/red/unathi
- cost = 2
- slot = slot_gloves
-
-/datum/gear/red_gloves_tajaran
- display_name = "red gloves, tajaran"
- path = /obj/item/clothing/gloves/red/tajara
- cost = 2
- slot = slot_gloves
-
-/datum/gear/blue_gloves_unathi
- display_name = "blue gloves, unathi"
- path = /obj/item/clothing/gloves/blue/unathi
- cost = 2
- slot = slot_gloves
-
-/datum/gear/blue_gloves_tajaran
- display_name = "blue gloves, tajaran"
- path = /obj/item/clothing/gloves/blue/tajara
- cost = 2
- slot = slot_gloves
-
-/datum/gear/orange_gloves_unathi
- display_name = "orange gloves, unathi"
- path = /obj/item/clothing/gloves/orange/unathi
- cost = 2
- slot = slot_gloves
-
-/datum/gear/orange_gloves_tajaran
- display_name = "orange gloves, tajaran"
- path = /obj/item/clothing/gloves/orange/tajara
- cost = 2
- slot = slot_gloves
-
-/datum/gear/purple_gloves_unathi
- display_name = "purple gloves, unathi"
- path = /obj/item/clothing/gloves/purple/unathi
- cost = 2
- slot = slot_gloves
-
-/datum/gear/purple_gloves_tajaran
- display_name = "purple gloves, tajaran"
- path = /obj/item/clothing/gloves/purple/tajara
- cost = 2
- slot = slot_gloves
-
-/datum/gear/brown_gloves_unathi
- display_name = "brown gloves, unathi"
- path = /obj/item/clothing/gloves/brown/unathi
- cost = 2
- slot = slot_gloves
-
-/datum/gear/brown_gloves_tajaran
- display_name = "brown gloves, tajaran"
- path = /obj/item/clothing/gloves/brown/tajara
- cost = 2
- slot = slot_gloves
-
-/datum/gear/green_gloves_unathi
- display_name = "green gloves, unathi"
- path = /obj/item/clothing/gloves/green/unathi
- cost = 2
- slot = slot_gloves
-
-/datum/gear/green_gloves_tajaran
- display_name = "green gloves, tajaran"
- path = /obj/item/clothing/gloves/green/tajara
- cost = 2
- slot = slot_gloves
-
-/datum/gear/white_gloves_unathi
- display_name = "white gloves, unathi"
- path = /obj/item/clothing/gloves/white/unathi
- cost = 2
- slot = slot_gloves
-
-/datum/gear/white_gloves_tajaran
- display_name = "white gloves, tajaran"
- path = /obj/item/clothing/gloves/white/tajara
- cost = 2
- slot = slot_gloves
-
-/datum/gear/watch
- display_name = "watch"
- path = /obj/item/clothing/gloves/watch
- cost = 2
- slot = slot_gloves
-
-// Shoelocker
-
-/datum/gear/jackboots
- display_name = "jackboots"
- path = /obj/item/clothing/shoes/jackboots
- cost = 1
- slot = slot_shoes
-// allowed_roles = list("Security Cadet","Security Officer","Head of Security","Warden","Detective","Internal Affairs Agent","Quartermaster","Head of Personnel","Captain")
-
-/datum/gear/toeless_jackboots
- display_name = "toe-less jackboots"
- path = /obj/item/clothing/shoes/jackboots/unathi
- cost = 1
- slot = slot_shoes
-// allowed_roles = list("Security Cadet","Security Officer","Head of Security","Warden","Detective","Internal Affairs Agent","Quartermaster","Head of Personnel","Captain")
-
-/datum/gear/workboots
- display_name = "workboots"
- path = /obj/item/clothing/shoes/workboots
- cost = 1
- slot = slot_shoes
-// allowed_roles = list("Station Engineer","Atmospheric Technician","Chief Engineer","Engineering Apprentice")
-
-/datum/gear/sandal
- display_name = "sandals"
- path = /obj/item/clothing/shoes/sandal
- cost = 1
- slot = slot_shoes
-
-/datum/gear/black_shoes
- display_name = "shoes, black"
- path = /obj/item/clothing/shoes/black
- cost = 1
- slot = slot_shoes
-
-/datum/gear/blue_shoes
- display_name = "shoes, blue"
- path = /obj/item/clothing/shoes/blue
- cost = 1
- slot = slot_shoes
-
-/datum/gear/brown_shoes
- display_name = "shoes, brown"
- path = /obj/item/clothing/shoes/brown
- cost = 1
- slot = slot_shoes
-
-/datum/gear/laceyshoes
- display_name = "shoes, classy"
- path = /obj/item/clothing/shoes/laceup
- cost = 1
- slot = slot_shoes
-
-/datum/gear/dress_shoes
- display_name = "shoes, dress"
- path = /obj/item/clothing/shoes/laceup
- cost = 1
- slot = slot_shoes
-
-/datum/gear/green_shoes
- display_name = "shoes, green"
- path = /obj/item/clothing/shoes/green
- cost = 1
- slot = slot_shoes
-
-/datum/gear/leather
- display_name = "shoes, leather"
- path = /obj/item/clothing/shoes/leather
- cost = 1
- slot = slot_shoes
-
-/datum/gear/orange_shoes
- display_name = "shoes, orange"
- path = /obj/item/clothing/shoes/orange
- cost = 1
- slot = slot_shoes
-
-/datum/gear/purple_shoes
- display_name = "shoes, purple"
- path = /obj/item/clothing/shoes/purple
- cost = 1
- slot = slot_shoes
-
-/datum/gear/rainbow_shoes
- display_name = "shoes, rainbow"
- path = /obj/item/clothing/shoes/rainbow
- cost = 1
- slot = slot_shoes
-
-/datum/gear/red_shoes
- display_name = "shoes, red"
- path = /obj/item/clothing/shoes/red
- cost = 1
- slot = slot_shoes
-
-/datum/gear/white_shoes
- display_name = "shoes, white"
- path = /obj/item/clothing/shoes/white
- cost = 1
- slot = slot_shoes
-
-/datum/gear/yellow_shoes
- display_name = "shoes, yellow"
- path = /obj/item/clothing/shoes/yellow
- cost = 1
- slot = slot_shoes
-
-// "Useful" items - I'm guessing things that might be used at work?
-
-/datum/gear/briefcase
- display_name = "briefcase"
- path = /obj/item/weapon/storage/briefcase
- sort_category = "utility"
- cost = 2
-
-/datum/gear/clipboard
- display_name = "clipboard"
- path = /obj/item/weapon/clipboard
- sort_category = "utility"
- cost = 1
-
-/datum/gear/folder_blue
- display_name = "folder, blue"
- path = /obj/item/weapon/folder/blue
- sort_category = "utility"
- cost = 1
-
-/datum/gear/folder_grey
- display_name = "folder, grey"
- path = /obj/item/weapon/folder
- sort_category = "utility"
- cost = 1
-
-/datum/gear/folder_red
- display_name = "folder, red"
- path = /obj/item/weapon/folder/red
- sort_category = "utility"
- cost = 1
-
-/datum/gear/folder_white
- display_name = "folder, white"
- path = /obj/item/weapon/folder/white
- sort_category = "utility"
- cost = 1
-
-/datum/gear/folder_yellow
- display_name = "folder, yellow"
- path = /obj/item/weapon/folder/yellow
- sort_category = "utility"
- cost = 1
-
-/datum/gear/paicard
- display_name = "personal AI device"
- path = /obj/item/device/paicard
- sort_category = "utility"
- cost = 2
-
-/datum/gear/wallet
- display_name = "wallet"
- path = /obj/item/weapon/storage/wallet
- sort_category = "utility"
- cost = 1
-
-// Reserved for later use
-// /datum/gear/cheaptablet
-// display_name = "cheap tablet computer"
-// path = /obj/item/modular_computer/tablet/preset/custom_loadout/cheap
-// sort_category = "utility"
-// cost = 3
-//
-// /datum/gear/normaltablet
-// display_name = "tablet computer"
-// path = /obj/item/modular_computer/tablet/preset/custom_loadout/advanced
-// sort_category = "utility"
-// cost = 4
-
-// The rest of the trash.
-
-/datum/gear/ashtray
- display_name = "ashtray, plastic"
- path = /obj/item/weapon/material/ashtray/plastic
- sort_category = "misc"
- cost = 1
-
-/datum/gear/boot_knife
- display_name = "boot knife"
- path = /obj/item/weapon/material/kitchen/utensil/knife/boot
- sort_category = "misc"
- cost = 3
-
-/datum/gear/cane
- display_name = "cane"
- path = /obj/item/weapon/cane
- sort_category = "misc"
- cost = 1
-
-/datum/gear/dice
- display_name = "d20"
- path = /obj/item/weapon/dice/d20
- sort_category = "misc"
- cost = 1
-
-/datum/gear/cards
- display_name = "deck of cards"
- path = /obj/item/weapon/deck/cards
- sort_category = "misc"
- cost = 1
-
-/datum/gear/tarot
- display_name = "deck of tarot cards"
- path = /obj/item/weapon/deck/tarot
- sort_category = "misc"
- cost = 1
-
-/datum/gear/holder
- display_name = "card holder"
- path = /obj/item/weapon/deck/holder
- sort_category = "misc"
- cost = 1
-
-/datum/gear/cardemon_pack
- display_name = "\improper cardemon booster pack"
- path = /obj/item/weapon/pack/cardemon
- sort_category = "misc"
- cost = 1
-
-/datum/gear/spaceball_pack
- display_name = "\improper spaceball booster pack"
- path = /obj/item/weapon/pack/spaceball
- sort_category = "misc"
- cost = 1
-
-/datum/gear/flask
- display_name = "flask"
- path = /obj/item/weapon/reagent_containers/food/drinks/flask/barflask
- sort_category = "misc"
- cost = 1
-
-/datum/gear/vacflask
- display_name = "vacuum-flask"
- path = /obj/item/weapon/reagent_containers/food/drinks/flask/vacuumflask
- sort_category = "misc"
- cost = 1
-/datum/gear/blipstick
- display_name = "lipstick, black"
- path = /obj/item/weapon/lipstick/black
- sort_category = "misc"
- cost = 1
-
-/datum/gear/jlipstick
- display_name = "lipstick, jade"
- path = /obj/item/weapon/lipstick/jade
- sort_category = "misc"
- cost = 1
-
-/datum/gear/plipstick
- display_name = "lipstick, purple"
- path = /obj/item/weapon/lipstick/purple
- sort_category = "misc"
- cost = 1
-
-/datum/gear/rlipstick
- display_name = "lipstick, red"
- path = /obj/item/weapon/lipstick
- sort_category = "misc"
- cost = 1
-
-/datum/gear/smokingpipe
- display_name = "pipe, smoking"
- path = /obj/item/clothing/mask/smokable/pipe
- sort_category = "misc"
- cost = 1
-
-/datum/gear/cornpipe
- display_name = "pipe, corn"
- path = /obj/item/clothing/mask/smokable/pipe/cobpipe
- sort_category = "misc"
- cost = 1
-
-/datum/gear/cigar_case
- display_name = "cigar case"
- path = /obj/item/weapon/storage/fancy/cigar
- sort_category = "misc"
- cost = 2
-
-/datum/gear/cigarettes
- display_name = "pack of DromedaryCo cigarettes"
- path = /obj/item/weapon/storage/fancy/cigarettes/dromedaryco
- sort_category = "misc"
- cost = 2
-
-/datum/gear/matchbook
- display_name = "matchbook"
- path = /obj/item/weapon/storage/box/matches
- sort_category = "misc"
- cost = 1
-
-/datum/gear/comb
- display_name = "purple comb"
- path = /obj/item/weapon/haircomb
- sort_category = "misc"
- cost = 1
-
-/datum/gear/cape
- display_name = "tunnel cloak"
- path = /obj/item/weapon/storage/backpack/cloak
- sort_category = "misc"
- cost = 1
-
-/datum/gear/mirror
- display_name = "handheld mirror"
- path = /obj/item/weapon/mirror
- sort_category = "misc"
- cost = 1
-
-/datum/gear/zippo
- display_name = "zippo"
- path = /obj/item/weapon/flame/lighter/zippo
- sort_category = "misc"
- cost = 1
-
-/datum/gear/recorder
- display_name = "universal recorder"
- path = /obj/item/device/taperecorder
- sort_category = "misc"
- cost = 1
-
-// Stuff worn on the ears. Items here go in the "ears" sort_category but they must not use
-// the slot_r_ear or slot_l_ear as the slot, or else players will spawn with no headset.
-/datum/gear/earmuffs
- display_name = "earmuffs"
- path = /obj/item/clothing/ears/earmuffs
- cost = 1
- sort_category = "ears"
-
-/datum/gear/skrell_chain
- display_name = "skrell headtail-wear, female, chain"
- path = /obj/item/clothing/ears/skrell/chain
- cost = 1
- sort_category = "ears"
- whitelisted = "Skrell"
-
-/datum/gear/skrell_plate
- display_name = "skrell headtail-wear, male, bands"
- path = /obj/item/clothing/ears/skrell/band
- cost = 1
- sort_category = "ears"
- whitelisted = "Skrell"
-
-/datum/gear/skrell_cloth_male
- display_name = "skrell headtail-wear, male, cloth"
- path = /obj/item/clothing/ears/skrell/cloth_male
- cost = 1
- sort_category = "ears"
- whitelisted = "Skrell"
-
-/datum/gear/skrell_cloth_female
- display_name = "skrell headtail-wear, female, cloth"
- path = /obj/item/clothing/ears/skrell/cloth_female
- cost = 1
- sort_category = "ears"
- whitelisted = "Skrell"
-
-/datum/gear/red_jeweled
- display_name = "skrell headtail-wear, female, red-jeweled chain"
- path = /obj/item/clothing/ears/skrell/redjewel_chain
- cost = 1
- sort_category = "ears"
- whitelisted = "Skrell"
-
-/datum/gear/ebony_chain
- display_name = "skrell headtail-wear, female, ebony chain"
- path = /obj/item/clothing/ears/skrell/ebony_chain
- cost = 1
- sort_category = "ears"
- whitelisted = "Skrell"
-
-/datum/gear/redjeweled_band
- display_name = "skrell headtail-wear, male, red-jeweled bands"
- path = /obj/item/clothing/ears/skrell/redjeweled_band
- cost = 1
- sort_category = "ears"
- whitelisted = "Skrell"
-
-/datum/gear/ebony_band
- display_name = "skrell headtail-wear, male, ebony bands"
- path = /obj/item/clothing/ears/skrell/ebony_band
- cost = 1
- sort_category = "ears"
- whitelisted = "Skrell"
-
-/datum/gear/bluejeweled_chain
- display_name = "skrell headtail-wear, female, blue-jeweled chain"
- path = /obj/item/clothing/ears/skrell/bluejeweled_chain
- cost = 1
- sort_category = "ears"
- whitelisted = "Skrell"
-
-/datum/gear/bluejeweled_band
- display_name = "skrell headtail-wear, male, blue-jeweled bands"
- path = /obj/item/clothing/ears/skrell/bluejeweled_band
- cost = 1
- sort_category = "ears"
- whitelisted = "Skrell"
-
-/datum/gear/silver_chain
- display_name = "skrell headtail-wear, female, silver chain"
- path = /obj/item/clothing/ears/skrell/silver_chain
- cost = 1
- sort_category = "ears"
- whitelisted = "Skrell"
-
-/datum/gear/silver_band
- display_name = "skrell headtail-wear, male, silver bands"
- path = /obj/item/clothing/ears/skrell/silver_band
- cost = 1
- sort_category = "ears"
- whitelisted = "Skrell"
-
-/datum/gear/blue_skrell_cloth_band_male
- display_name = "skrell headtail-wear, male, blue cloth"
- path = /obj/item/clothing/ears/skrell/blue_skrell_cloth_band_male
- cost = 1
- sort_category = "ears"
- whitelisted = "Skrell"
-
-/datum/gear/blue_skrell_cloth_band_female
- display_name = "skrell headtail-wear, female, blue cloth"
- path = /obj/item/clothing/ears/skrell/blue_skrell_cloth_band_female
- cost = 1
- sort_category = "ears"
- whitelisted = "Skrell"
-
-/datum/gear/bandanna_r
- display_name = "neck bandanna, red"
- path = /obj/item/clothing/ears/bandanna
- cost = 1
- sort_category = "ears"
-
-/datum/gear/bandanna_bl
- display_name = "neck bandanna, blue"
- path = /obj/item/clothing/ears/bandanna/blue
- cost = 1
- sort_category = "ears"
-
-/datum/gear/bandanna_bk
- display_name = "neck bandanna, black"
- path = /obj/item/clothing/ears/bandanna/black
- cost = 1
- sort_category = "ears"
diff --git a/code/modules/client/preferences_toggles.dm b/code/modules/client/preferences_toggles.dm
index b2d0958dbd5..180fd7c6914 100644
--- a/code/modules/client/preferences_toggles.dm
+++ b/code/modules/client/preferences_toggles.dm
@@ -145,3 +145,47 @@
src << sound(null, repeat = 0, wait = 0, volume = 0, channel = 1)
src << sound(null, repeat = 0, wait = 0, volume = 0, channel = 2)
feedback_add_details("admin_verb","TAmbi") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+
+/client/verb/toggle_space_parallax()
+ set name = "Show/Hide Space Parallax"
+ set category = "Preferences"
+ set desc = "Toggles space parallax effects."
+ prefs.parallax_togs ^= PARALLAX_SPACE
+ prefs.save_preferences()
+ if (prefs.parallax_togs & PARALLAX_SPACE)
+ src << "You will now see space parallax effects."
+ else
+ src << "You will no longer see space parallax effects."
+
+ if (mob.hud_used)
+ mob.hud_used.update_parallax()
+
+
+/client/verb/toggle_space_dust()
+ set name = "Show/Hide Space Dust"
+ set category = "Preferences"
+ set desc = "Toggles space parallax dust."
+ prefs.parallax_togs ^= PARALLAX_DUST
+ prefs.save_preferences()
+ if (prefs.parallax_togs & PARALLAX_DUST)
+ src << "You will now see space parallax dust effects."
+ else
+ src << "You will no longer see space parallax dust effects."
+
+ if (mob.hud_used)
+ mob.hud_used.update_parallax()
+
+/client/verb/set_parallax_speed()
+ set name = "Set Parallax Speed"
+ set category = "Preferences"
+ set desc = "Sets the movement speed of the space parallax effect."
+ var/choice = input("What speed do you want to use for space parallax? (default 2)", "SPAAACE") as num|null
+ if (!choice || choice < 0)
+ src << "Invalid input."
+ return
+
+ prefs.parallax_speed = choice
+ prefs.save_preferences()
+
+ if (mob.hud_used)
+ mob.hud_used.update_parallax()
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index 37714286085..77e832c8409 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -268,14 +268,14 @@ BLIND // can't see anything
body_parts_covered = HEAD
slot_flags = SLOT_HEAD
w_class = 2.0
- diona_restricted_light = 1//Light emitted by this object or creature has limited interaction with diona
+ //diona_restricted_light = 1//Light emitted by this object or creature has limited interaction with diona
species_restricted = list("exclude","Vaurca Breeder")
var/light_overlay = "helmet_light"
var/light_applied
var/brightness_on
var/on = 0
- offset_light = 1
+ //offset_light = 1
sprite_sheets = list(
"Vox" = 'icons/mob/species/vox/head.dmi',
diff --git a/code/modules/detectivework/tools/uvlight.dm b/code/modules/detectivework/tools/uvlight.dm
index 922839d098e..d86076c167f 100644
--- a/code/modules/detectivework/tools/uvlight.dm
+++ b/code/modules/detectivework/tools/uvlight.dm
@@ -7,7 +7,7 @@
item_state = "electronic"
matter = list(DEFAULT_WALL_MATERIAL = 150)
origin_tech = list(TECH_MAGNET = 1, TECH_ENGINEERING = 1)
- offset_light = 1
+ //offset_light = 1
var/list/scanned = list()
var/list/stored_alpha = list()
var/list/reset_objects = list()
@@ -19,7 +19,7 @@
/obj/item/device/uv_light/attack_self(var/mob/user)
on = !on
if(on)
- set_light(range, 2, "#007fff")
+ set_light(range, 2, "#007fff", uv = 100)
processing_objects |= src
icon_state = "uv_on"
else
@@ -66,4 +66,4 @@
var/obj/item/O = A
if(O.was_bloodied && !(O.blood_overlay in O.overlays))
O.overlays |= O.blood_overlay
- reset_objects |= O
\ No newline at end of file
+ reset_objects |= O
diff --git a/code/modules/holodeck/HolodeckControl.dm b/code/modules/holodeck/HolodeckControl.dm
index 83d78272b05..adf514d3877 100644
--- a/code/modules/holodeck/HolodeckControl.dm
+++ b/code/modules/holodeck/HolodeckControl.dm
@@ -3,6 +3,7 @@
desc = "A computer used to control a nearby holodeck."
icon_screen = "holocontrol"
+ light_color = LIGHT_COLOR_CYAN
use_power = 1
active_power_usage = 8000 //8kW for the scenery + 500W per holoitem
diff --git a/code/modules/hydroponics/seed.dm b/code/modules/hydroponics/seed.dm
index 22f3a598f67..ff1107daa8c 100644
--- a/code/modules/hydroponics/seed.dm
+++ b/code/modules/hydroponics/seed.dm
@@ -284,11 +284,11 @@
// Handle light requirements.
if(!light_supplied)
- var/atom/movable/lighting_overlay/L = locate(/atom/movable/lighting_overlay) in current_turf
- if(L)
- light_supplied = max(0,min(10,L.lum_r + L.lum_g + L.lum_b)-5)
+ if (current_turf.dynamic_lighting)
+ light_supplied = current_turf.get_lumcount(0, 3) * 10
else
- light_supplied = 5
+ light_supplied = 5
+
if(light_supplied)
if(abs(light_supplied - get_trait(TRAIT_IDEAL_LIGHT)) > get_trait(TRAIT_LIGHT_TOLERANCE))
health_change += rand(1,3) * HYDRO_SPEED_MULTIPLIER
diff --git a/code/modules/hydroponics/spreading/spreading.dm b/code/modules/hydroponics/spreading/spreading.dm
index 14e4741565e..d76cdce7e6b 100644
--- a/code/modules/hydroponics/spreading/spreading.dm
+++ b/code/modules/hydroponics/spreading/spreading.dm
@@ -62,6 +62,7 @@
var/mature_time //minimum maturation time
var/last_tick = 0
var/obj/machinery/portable_atmospherics/hydroponics/soil/invisible/plant
+ var/last_biolum = null
/obj/effect/plant/Destroy()
if(plant_controller)
@@ -153,10 +154,15 @@
var/clr
if(seed.get_trait(TRAIT_BIOLUM_COLOUR))
clr = seed.get_trait(TRAIT_BIOLUM_COLOUR)
- set_light(1+round(seed.get_trait(TRAIT_POTENCY)/20), l_color = clr)
+ var/val = 1+round(seed.get_trait(TRAIT_POTENCY)/20)
+ if (val != last_biolum)
+ last_biolum = val
+ set_light(val, l_color = clr)
return
else
- set_light(0)
+ if (last_biolum)
+ set_light(0)
+ last_biolum = null
/obj/effect/plant/proc/refresh_icon()
var/growth = min(max_growth,round(health/growth_threshold))
diff --git a/code/modules/hydroponics/trays/tray.dm b/code/modules/hydroponics/trays/tray.dm
index 8c37abb6d62..7a025071f95 100644
--- a/code/modules/hydroponics/trays/tray.dm
+++ b/code/modules/hydroponics/trays/tray.dm
@@ -26,6 +26,7 @@
var/harvest = 0 // Is it ready to harvest?
var/age = 0 // Current plant age
var/sampled = 0 // Have we taken a sample?
+ var/last_biolum // What was the bioluminescence last tick?
// Harvest/mutation mods.
var/yield_mod = 0 // Modifier to yield
@@ -645,10 +646,9 @@
if(closed_system && mechanical)
light_string = "that the internal lights are set to [tray_light] lumens"
else
- var/atom/movable/lighting_overlay/L = locate(/atom/movable/lighting_overlay) in T
var/light_available
- if(L)
- light_available = max(0,min(10,L.lum_r + L.lum_g + L.lum_b)-5)
+ if(T.dynamic_lighting)
+ light_available = T.get_lumcount(0, 3) * 10
else
light_available = 5
light_string = "a light level of [light_available] lumens"
diff --git a/code/modules/hydroponics/trays/tray_update_icons.dm b/code/modules/hydroponics/trays/tray_update_icons.dm
index dc69e5448ee..bcee0352797 100644
--- a/code/modules/hydroponics/trays/tray_update_icons.dm
+++ b/code/modules/hydroponics/trays/tray_update_icons.dm
@@ -73,13 +73,17 @@
overlays += "over_harvest3"
// Update bioluminescence.
- if(seed)
- if(seed.get_trait(TRAIT_BIOLUM))
- var/clr
- if(seed.get_trait(TRAIT_BIOLUM_COLOUR))
- clr = seed.get_trait(TRAIT_BIOLUM_COLOUR)
- set_light(round(seed.get_trait(TRAIT_POTENCY)/10), l_color = clr)
- return
-
- set_light(0)
+ if(seed && seed.get_trait(TRAIT_BIOLUM))
+ var/clr
+ if(seed.get_trait(TRAIT_BIOLUM_COLOUR))
+ clr = seed.get_trait(TRAIT_BIOLUM_COLOUR)
+ var/val = round(seed.get_trait(TRAIT_POTENCY)/10)
+ if (val != last_biolum)
+ last_biolum = val
+ set_light(val, l_color = clr)
+ return
+
+ if (last_biolum)
+ set_light(0)
+ last_biolum = null
return
diff --git a/code/modules/icon generation/Bluespaced.dm b/code/modules/icon generation/Bluespaced.dm
deleted file mode 100644
index db9f88c215c..00000000000
--- a/code/modules/icon generation/Bluespaced.dm
+++ /dev/null
@@ -1,243 +0,0 @@
-/proc/bsi_cast_ray(icon/I, list/start, list/end)
-
- if(abs(start[1] - end[1]) > abs(start[2] - end[2]))
- var/dist = abs(start[1] - end[1]) * 2
-
- for(var/i = 1, i <= dist, i++)
- var/x = round((start[1] * i / dist) + (end[1] * (1 - i / dist)))
- var/y = round((start[2] * i / dist) + (end[2] * (1 - i / dist)))
-
- if(I.GetPixel(x, y) != null)
- return list(x, y)
-
- else
- var/dist = abs(start[2] - end[2]) * 2
-
- for(var/i = 1, i <= dist, i++)
- var/x = round((start[1] * i / dist) + (end[1] * (1 - i / dist)))
- var/y = round((start[2] * i / dist) + (end[2] * (1 - i / dist)))
-
- if(I.GetPixel(x, y) != null)
- return list(x, y)
-
- return null
-
-/proc/bsi_split_colors(color)
- if(color == null)
- return list(0, 0, 0, 0)
-
- var/list/colors = list(0, 0, 0, 0)
- colors[1] = hex2num(copytext(color, 2, 4))
- colors[2] = hex2num(copytext(color, 4, 6))
- colors[3] = hex2num(copytext(color, 6, 8))
- colors[4] = (length(color) > 7)? hex2num(copytext(color, 8, 10)) : 255
-
- return colors
-
-/proc/bsi_spread(icon/I, list/start_point)
- var/list/queue = list()
- queue[++queue.len] = start_point
-
- var/i = 0
-
- while(i++ < length(queue))
- var/x = queue[i][1]
- var/y = queue[i][2]
-
- var/list/pixel = bsi_split_colors(I.GetPixel(x, y))
- if(pixel[4] == 0)
- continue
-
- var/list/n = (y < I.Height())? bsi_split_colors(I.GetPixel(x, y + 1)) : list(0, 0, 0, 0)
- var/list/s = (y > 1)? bsi_split_colors(I.GetPixel(x, y - 1)) : list(0, 0, 0, 0)
- var/list/e = (x < I.Width())? bsi_split_colors(I.GetPixel(x + 1, y)) : list(0, 0, 0, 0)
- var/list/w = (x > 1)? bsi_split_colors(I.GetPixel(x - 1, y)) : list(0, 0, 0, 0)
-
- var/value = (i == 1)? 16 : max(n[1] - 1, e[1] - 1, s[1] - 1, w[1] - 1)
-
- if(prob(50))
- value = max(0, value - 1)
-
- if(prob(50))
- value = max(0, value - 1)
-
- if(prob(50))
- value = max(0, value - 1)
-
- if(value <= pixel[1])
- continue
-
- var/v2 = 256 - ((16 - value) * (16 - value))
-
- I.DrawBox(rgb(value, v2, pixel[4] - v2, pixel[4]), x, y)
-
- if(n[4] != 0 && n[1] < value - 1)
- queue[++queue.len] = list(x, y + 1)
-
- if(s[4] != 0 && s[1] < value - 1)
- queue[++queue.len] = list(x, y - 1)
-
- if(e[4] != 0 && e[1] < value - 1)
- queue[++queue.len] = list(x + 1, y)
-
- if(w[4] != 0 && w[1] < value - 1)
- queue[++queue.len] = list(x - 1, y)
-
-
-
-
-
-/proc/bsi_generate_mask(icon/source, state)
- var/icon/mask = icon(source, state)
-
- mask.MapColors(
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 1, 1,
- 0, 0, 0, 0)
-
- var/hits = 0
-
- for(var/i = 1, i <= 10, i++)
- var/point1
- var/point2
-
- if(prob(50))
- if(prob(50))
- point1 = list(rand(1, mask.Width()), mask.Height())
- point2 = list(rand(1, mask.Width()), 1)
-
- else
- point2 = list(rand(1, mask.Width()), mask.Height())
- point1 = list(rand(1, mask.Width()), 1)
-
- else
- if(prob(50))
- point1 = list(mask.Width(), rand(1, mask.Height()))
- point2 = list(1, rand(1, mask.Height()))
-
- else
- point2 = list(mask.Width(), rand(1, mask.Height()))
- point1 = list(1, rand(1, mask.Height()))
-
- var/hit = bsi_cast_ray(mask, point1, point2)
-
- if(hit == null)
- continue
-
- hits++
-
- bsi_spread(mask, hit)
-
- if(prob(20 + hits * 20))
- break
-
- if(hits == 0)
- return null
-
- else
- return mask
-
-/proc/generate_bluespace_icon(icon/source, state)
-
- var/icon/mask = bsi_generate_mask(source, state)
-
- if(mask == null)
- return source
-
- var/icon/unaffected = icon(mask)
- unaffected.MapColors(
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 1,
- 0, 0, 0, 0,
- 255, 255, 255, 0)
-
- var/icon/temp = icon(source, state) //Mask already contains the original alpha values, avoid squaring them
- temp.MapColors(
- 1, 0, 0, 0,
- 0, 1, 0, 0,
- 0, 0, 1, 0,
- 0, 0, 0, 0,
- 0, 0, 0, 255)
-
- unaffected.Blend(temp, ICON_MULTIPLY)
-
- var/icon/bluespaced = icon(mask)
- bluespaced.MapColors(
- 0, 0, 0, 0,
- 0, 0, 0, 1,
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- 1, 1, 1, 0)
-
- bluespaced.Blend(icon(source, state), ICON_MULTIPLY)
-
- var/list/frames = list(
- list(0.000,20),
- list(0.020, 5),
- list(0.050, 4),
- list(0.080, 5),
- list(0.100,10),
- list(0.080, 5),
- list(0.050, 4),
- list(0.020, 5),
-
- list(0.000,20),
- list(0.020, 5),
- list(0.050, 4),
- list(0.080, 5),
- list(0.100,10),
- list(0.080, 5),
- list(0.050, 4),
- list(0.020, 5),
-
- list(0.000,20),
- list(0.020, 5),
- list(0.050, 4),
- list(0.080, 5),
- list(0.100,10),
- list(0.080, 5),
- list(0.050, 4),
- list(0.020, 5)
- )
-
- var/list/colors = list(
- list( 75, 75, 75, 0),
- list( 25, 25, 25, 0),
- list( 75, 75, 75, 0),
- list( 25, 25, 75, 0),
- list( 75, 75, 300, 0),
- list( 25, 25, 300, 0),
- list(255, 255, 255, 0),
- list( 0, 0, 0, 255),
- list( 0, 0, 0, 0),
- list( 0, 0, 0, 0)
- )
-
- for(var/i = 1, i <= rand(1, 5), i++)
- var/f = rand(1, length(frames))
-
- if(frames[f][2] > 1)
- frames[f][2]--
- frames.Insert(f, 0)
-
- frames[f] = list(0.8, 1)
-
- var/icon/result = generate_color_animation(bluespaced, colors, frames)
- result.Blend(unaffected, ICON_UNDERLAY)
-
- return result
-
-
-
-/atom/verb/test()
- set src in view()
- src.icon = generate_bluespace_icon(src.icon, src.icon_state)
-
-/mob/verb/bluespam()
- for(var/turf/t in view(5))
- var/obj/s = new /obj/square(t)
- s.icon = generate_bluespace_icon(s.icon, s.icon_state)
-
diff --git a/code/modules/icon generation/Icon_color_animation.dm b/code/modules/icon generation/Icon_color_animation.dm
deleted file mode 100644
index 1be1bfa28a9..00000000000
--- a/code/modules/icon generation/Icon_color_animation.dm
+++ /dev/null
@@ -1,96 +0,0 @@
-//----------------------------------------
-//
-// Return a copy of the provided icon,
-// after calling MapColors on it. The
-// color values are linearily interpolated
-// between the pairs provided, based on
-// the ratio argument.
-//
-//----------------------------------------
-
-/proc/MapColors_interpolate(icon/input, ratio,
- rr1, rg1, rb1, ra1, rr2, rg2, rb2, ra2,
- gr1, gg1, gb1, ga1, gr2, gg2, gb2, ga2,
- br1, bg1, bb1, ba1, br2, bg2, bb2, ba2,
- ar1, ag1, ab1, aa1, ar2, ag2, ab2, aa2,
- zr1, zg1, zb1, za1, zr2, zg2, zb2, za2)
- var/r = ratio
- var/i = 1 - ratio
- var/icon/I = icon(input)
-
- I.MapColors(
- (rr1 * r + rr2 * i) / 255.0, (rg1 * r + rg2 * i) / 255.0, (rb1 * r + rb2 * i) / 255.0, (ra1 * r + ra2 * i) / 255.0,
- (gr1 * r + gr2 * i) / 255.0, (gg1 * r + gg2 * i) / 255.0, (gb1 * r + gb2 * i) / 255.0, (ga1 * r + ga2 * i) / 255.0,
- (br1 * r + br2 * i) / 255.0, (bg1 * r + bg2 * i) / 255.0, (bb1 * r + bb2 * i) / 255.0, (ba1 * r + ba2 * i) / 255.0,
- (ar1 * r + ar2 * i) / 255.0, (ag1 * r + ag2 * i) / 255.0, (ab1 * r + ab2 * i) / 255.0, (aa1 * r + aa2 * i) / 255.0,
- (zr1 * r + zr2 * i) / 255.0, (zg1 * r + zg2 * i) / 255.0, (zb1 * r + zb2 * i) / 255.0, (za1 * r + za2 * i) / 255.0)
-
- return I
-
-
-
-
-//----------------------------------------
-//
-// Extension of the above that takes a
-// list of lists of color values, rather
-// than a large number of arguments.
-//
-//----------------------------------------
-
-/proc/MapColors_interpolate_list(icon/I, ratio, list/colors)
- var/list/c[10]
-
- //Provide default values for any missing colors (without altering the original list
- for(var/i = 1, i <= 10, i++)
- c[i] = list(0, 0, 0, (i == 7 || i == 8)? 255 : 0)
-
- if(istype(colors[i], /list))
- for(var/j = 1, j <= 4, j++)
- if(j <= length(colors[i]) && isnum(colors[i][j]))
- c[i][j] = colors[i][j]
-
- return MapColors_interpolate(I, ratio,
- colors[ 1][1], colors[ 1][2], colors[ 1][3], colors[ 1][4], // Red 1
- colors[ 2][1], colors[ 2][2], colors[ 2][3], colors[ 2][4], // Red 2
- colors[ 3][1], colors[ 3][2], colors[ 3][3], colors[ 3][4], // Green 1
- colors[ 4][1], colors[ 4][2], colors[ 4][3], colors[ 4][4], // Green 2
- colors[ 5][1], colors[ 5][2], colors[ 5][3], colors[ 5][4], // Blue 1
- colors[ 6][1], colors[ 6][2], colors[ 6][3], colors[ 6][4], // Blue 2
- colors[ 7][1], colors[ 7][2], colors[ 7][3], colors[ 7][4], // Alpha 1
- colors[ 8][1], colors[ 8][2], colors[ 8][3], colors[ 8][4], // Alpha 2
- colors[ 9][1], colors[ 9][2], colors[ 9][3], colors[ 9][4], // Added 1
- colors[10][1], colors[10][2], colors[10][3], colors[10][4]) // Added 2
-
-
-
-
-
-//----------------------------------------
-//
-// Take the source image, and return an animated
-// version, that transitions between the provided
-// color mappings, according to the provided
-// pattern.
-//
-// Colors should be in a format suitable for
-// MapColors_interpolate_list, and frames should
-// be a list of 'frames', where each frame is itself
-// a list, element 1 being the ratio of the first
-// color to the second, and element 2 being how
-// long the frame lasts, in tenths of a second.
-//
-//----------------------------------------
-
-/proc/generate_color_animation(icon/icon, list/colors, list/frames)
- var/icon/out = icon('icons/effects/uristrunes.dmi', "")
- var/frame_num = 1
-
- for(var/frame in frames)
- var/icon/I = MapColors_interpolate_list(icon, frame[1], colors)
- out.Insert(I, "", 2, frame_num++, 0, frame[2])
-
- return out
-
-
-
diff --git a/code/modules/icon generation/Uristrunes.dm b/code/modules/icon generation/Uristrunes.dm
deleted file mode 100644
index 7288bbdde3a..00000000000
--- a/code/modules/icon generation/Uristrunes.dm
+++ /dev/null
@@ -1,268 +0,0 @@
-//----------------------------------------
-//
-// Take a source icon, convert into a mask,
-// then create a border around it.
-//
-// The output then uses the colors and
-// alpha values provided.
-//
-//----------------------------------------
-
-/proc/create_border_image(icon/input, border_color = "#000000", fill_color = "#000000", border_alpha = 255, fill_alpha = 255)
- var/icon/I = icon('icons/effects/uristrunes.dmi', "blank")
- I.Blend(input, ICON_OVERLAY)
-
- //Discard the image
- I.MapColors(0, 0, 0, 0, //-\ Ignore
- 0, 0, 0, 0, //--> The
- 0, 0, 0, 0, //-/ Colors
- 0,255, 0, 1, //Keep alpha channel, any pixel with non-zero alpha gets max green channel
- 0, 0, 0, 0)
-
- //Loop over the image, calculating the border value, and storing it in the red channel
- //Store border's alpha in the blue channel
- for(var/x = 1, x <= 32, x++)
- for(var/y = 1, y <= 32, y++)
- var/p = I.GetPixel(x, y)
-
- if(p == null)
- var/n = I.GetPixel(x, y + 1)
- var/s = I.GetPixel(x, y - 1)
- var/e = I.GetPixel(x + 1, y)
- var/w = I.GetPixel(x - 1, y)
- var/ne = I.GetPixel(x + 1, y + 1)
- var/se = I.GetPixel(x + 1, y - 1)
- var/nw = I.GetPixel(x - 1, y + 1)
- var/sw = I.GetPixel(x - 1, y - 1)
-
- var/sum_adj = ((n == "#00ff00"? 1 : 0) \
- + (s == "#00ff00"? 1 : 0) \
- + (e == "#00ff00"? 1 : 0) \
- + (w == "#00ff00"? 1 : 0))
-
- var/sum_diag = ((ne == "#00ff00"? 1 : 0) \
- + (se == "#00ff00"? 1 : 0) \
- + (nw == "#00ff00"? 1 : 0) \
- + (sw == "#00ff00"? 1 : 0))
-
-
- if(sum_adj)
- I.DrawBox(rgb(255, 0, 200, 0), x, y)
-
- else if(sum_diag)
- I.DrawBox(rgb(255, 0, 100, 0), x, y)
-
- else
- I.DrawBox(rgb(0, 0, 0, 0), x, y)
-
- else if(p != "#00ff00")
- var/a = 255
-
- if(length(p) == 9) // "#rrggbbaa", we want the aa
- a = hex2num(copytext(p, 8))
-
- I.DrawBox(rgb(255 - a, a, 255 - a, a), x, y)
-
- //Map the red and green channels to the desired output colors
- I.MapColors(border_color, fill_color, rgb(0, 0, 0, border_alpha), rgb(0, 0, 0, fill_alpha), "#00000000")
-
- return I
-
-
-
-
-//----------------------------------------
-//
-// Take a source icon, convert into a mask,
-// and border. Color them according to args,
-// and animate.
-//
-//----------------------------------------
-
-/proc/animate_rune_full(icon/input, rr1, rg1, rb1, ra1, rr2, rg2, rb2, ra2, br1, bg1, bb1, ba1, br2, bg2, bb2, ba2, ar1, ag1, ab1, aa1, ar2, ag2, ab2, aa2, or1, og1, ob1, oa1, or2, og2, ob2, oa2, frames)
-
- var/list/colors[10]
- colors[ 1] = list(rr1, rg1, rb1, ra1) //Rune color 1
- colors[ 2] = list(rr2, rg2, rb2, ra2) //Rune color 2
- colors[ 3] = list(br1, bg1, bb1, ba1) //Border color 1
- colors[ 4] = list(br2, bg2, bb2, ba2) //Border color 2
- colors[ 5] = list( 0, 0, 0, 0) //Unused
- colors[ 6] = list( 0, 0, 0, 0) //Unused
- colors[ 7] = list(ar1, ag1, ab1, aa1) //Alpha color 1
- colors[ 8] = list(ar2, ag2, ab2, aa2) //Alpha color 2
- colors[ 9] = list(or1, og1, ob1, oa1) //Added color 1
- colors[10] = list(or2, og2, ob2, oa2) //Added color 2
-
- var/icon/base = create_border_image(input, "#00ff0000", "#ff000000")
-
- return generate_color_animation(base, colors, frames)
-
-
-
-
-//----------------------------------------
-//
-// Calls the above, but accepts colors in
-// the form of "#RRGGBBAA", and provides
-// default values.
-//
-// Main limit is that it doesn't accept
-// negative values, which you probably
-// don't need anyway. Also missing a few
-// color inputs, which would also be rarely
-// used.
-//
-//----------------------------------------
-
-
-/proc/animate_rune(icon/input, rune_color = "#00000000", border_color = "#c8000000", rune_color2 = "#00000000", border_color2 = "#d8380000", alpha = 255, alpha2 = 255, frames = rune_animation)
- var/rr1 = hex2num(copytext(rune_color, 2, 4))
- var/rg1 = hex2num(copytext(rune_color, 4, 6))
- var/rb1 = hex2num(copytext(rune_color, 6, 8))
- var/ra1 = hex2num(copytext(rune_color, 8, 10))
- var/rr2 = hex2num(copytext(rune_color2, 2, 4))
- var/rg2 = hex2num(copytext(rune_color2, 4, 6))
- var/rb2 = hex2num(copytext(rune_color2, 6, 8))
- var/ra2 = hex2num(copytext(rune_color2, 8, 10))
- var/br1 = hex2num(copytext(border_color, 2, 4))
- var/bg1 = hex2num(copytext(border_color, 4, 6))
- var/bb1 = hex2num(copytext(border_color, 6, 8))
- var/ba1 = hex2num(copytext(border_color, 8, 10))
- var/br2 = hex2num(copytext(border_color2, 2, 4))
- var/bg2 = hex2num(copytext(border_color2, 4, 6))
- var/bb2 = hex2num(copytext(border_color2, 6, 8))
- var/ba2 = hex2num(copytext(border_color2, 8, 10))
-
- return animate_rune_full(input, rr1, rg1, rb1, ra1, rr2, rg2, rb2, ra2, br1, bg1, bb1, ba1, br2, bg2, bb2, ba2, 0, 0, 0, alpha, 0, 0, 0, alpha2, 0, 0, 0, 0, 0, 0, 0, 0, frames)
-
-
-/proc/inanimate_rune(icon/input, rune_color = "#00000000", border_color = "#c8000000")
- var/icon/base = create_border_image(input, "#00ff0000", "#ff000000")
-
- base.MapColors(rune_color, border_color, "#00000000", "#000000ff", "#00000000")
-
- return base
-
-var/list/rune_animation = list(
- list(0.000, 5),
- list(0.020, 1),
- list(0.050, 1),
- list(0.090, 1),
- list(0.140, 1),
- list(0.200, 1),
- list(0.270, 1),
- list(0.340, 1),
- list(0.420, 1),
- list(0.500, 1),
- list(0.590, 1),
- list(0.675, 1),
- list(0.750, 1),
- list(0.900, 1),
- list(1.000, 6),
- list(0.875, 1),
- list(0.750, 1),
- list(0.625, 1),
- list(0.500, 1),
- list(0.375, 1),
- list(0.250, 1),
- list(0.125, 1)
- )
-
-/var/list/rune_cache = list()
-
-/proc/get_rune(rune_bits, animated = 0)
- var/lookup = "[rune_bits]-[animated]"
-
- if(lookup in rune_cache)
- return rune_cache[lookup]
-
- var/icon/base = icon('icons/effects/uristrunes.dmi', "")
-
- for(var/i = 0, i < 10, i++)
- if(BITTEST(rune_bits, i))
- base.Blend(icon('icons/effects/uristrunes.dmi', "rune-[1 << i]"), ICON_OVERLAY)
-
- var/icon/result
-
- if(animated == 1)
- result = animate_rune(base)
-
- else
- result = inanimate_rune(base)
-
- rune_cache[lookup] = result
- return result
-
-
-
-
-
-// Testing procs and Fun procs
-
-
-
-
-/mob/verb/create_rune()
- var/obj/o = new(locate(x, y, z))
- o.icon = get_rune(rand(1, 1023), 1)
-
-/mob/verb/runes_15x15()
- for(var/turf/t in range(7))
- var/obj/o = new /obj(t)
- o.icon = get_rune(rand(1, 1023), 1)
-
-
-/*
-/mob/verb/create_rune_custom(rune as num, color1 as color, border1 as color, color2 as color, border2 as color, alpha1 as num, alpha2 as num)
- var/icon/I = icon('icons/effects/uristrunes.dmi', "blank")
-
- for(var/i = 0, i < 10, i++)
- if(BITTEST(rune, i))
- I.Blend(icon('icons/effects/uristrunes.dmi', "rune-[1 << i]"), ICON_OVERLAY)
-
- var/obj/o = new(locate(x, y, z))
- o.icon = animate_rune(I, color1, border1, color2, border2, alpha1, alpha2)
-
-/mob/verb/spam()
- for(var/turf/t in range(4))
- var/icon/I = icon('icons/effects/uristrunes.dmi', "blank")
-
- var/rune = rand(1, 1023)
- for(var/i = 0, i < 10, i++)
- if(BITTEST(rune, i))
- I.Blend(icon('icons/effects/uristrunes.dmi', "rune-[1 << i]"), ICON_OVERLAY)
-
- var/obj/o = new(t)
- o.icon = animate_rune_full(I, rand(0, 255), rand(0, 255), rand(0, 255), rand(-255, 255),
- rand(0, 255), rand(0, 255), rand(0, 255), rand(-255, 255),
- rand(0, 255), rand(0, 255), rand(0, 255), rand(-255, 255),
- rand(0, 255), rand(0, 255), rand(0, 255), rand(-255, 255),
- 0, 0, 0, rand(0, 255),
- 0, 0, 0, rand(0, 255),
- 0, 0, 0, 0,
- 0, 0, 0, 0,
- list(
- list(0.000, 5),
- list(0.020, 1),
- list(0.050, 1),
- list(0.090, 1),
- list(0.140, 1),
- list(0.200, 1),
- list(0.270, 1),
- list(0.340, 1),
- list(0.420, 1),
- list(0.500, 1),
- list(0.590, 1),
- list(0.675, 1),
- list(0.750, 1),
- list(0.900, 1),
- list(1.000, 6),
- list(0.875, 1),
- list(0.750, 1),
- list(0.625, 1),
- list(0.500, 1),
- list(0.375, 1),
- list(0.250, 1),
- list(0.125, 1),
- ))
-*/
diff --git a/code/modules/lighting/__lighting_docs.dm b/code/modules/lighting/__lighting_docs.dm
index 5ac5897eb53..a5ff9450e49 100644
--- a/code/modules/lighting/__lighting_docs.dm
+++ b/code/modules/lighting/__lighting_docs.dm
@@ -3,9 +3,9 @@ BS12 object based lighting system
*/
/*
-Changes from tg DAL:
+Changes from TG DAL:
- Lighting is done using objects instead of subareas.
- - Animated transitions. (newer tg DAL has this)
+ - Animated transitions. (newer TG DAL has this)
- Full colours with mixing.
- Support for lights on shuttles.
@@ -25,6 +25,9 @@ Changes from tg DAL:
/*
Relevant vars/procs:
+global: (uh, I placed the only one in lighting_system.dm)
+ - var/list/all_lighting_overlays; Just a list of ALL of the lighting overlays.
+
atom: (lighting_atom.dm)
- var/light_range; range in tiles of the light, used for calculating falloff
- var/light_power; multiplier for the brightness of lights
@@ -44,6 +47,8 @@ atom: (lighting_atom.dm)
turf: (lighting_turf.dm)
- var/list/affecting_lights; list of light sources that are shining onto this turf
+ - var/list/lighting_overlays; list of lighting overlays in the turf. (only used if higher resolutions
+ - var/lighting_overlay; ref to the lighting overlay (only used if resolution is 1)
- proc/reconsider_lights():
- Force all light sources shining onto this turf to update
@@ -51,8 +56,10 @@ turf: (lighting_turf.dm)
- proc/lighting_clear_overlays():
- Delete (manual GC) all light overlays on this turf, used when changing turf to space
- proc/lighting_build_overlays():
- - Create lighting overlays for this turf. Called by ChangeTurf in case the turf is being changed to use dynamic lighting.
-
+ - Create lighting overlays for this turf
+ - proc/get_lumcount(var/minlum = 0, var/maxlum = 1)
+ - Returns a decimal according to the amount of lums on a turf's overlay (also averages them)
+ - With default arguments (based on the fact that 0 = pitch black and 1 = full bright), it will return .5 for a 50% lit tile.
atom/movable/lighting_overlay: (lighting_overlay.dm)
- var/lum_r, var/lum_g, var/lum_b; lumcounts of each colour
diff --git a/code/modules/lighting/_lighting_defs.dm b/code/modules/lighting/_lighting_defs.dm
deleted file mode 100644
index 21fdafdaebf..00000000000
--- a/code/modules/lighting/_lighting_defs.dm
+++ /dev/null
@@ -1,9 +0,0 @@
-#define LIGHTING_INTERVAL 5 // frequency, in 1/10ths of a second, of the lighting process
-
-#define LIGHTING_FALLOFF 1 // type of falloff to use for lighting; 1 for circular, 2 for square
-#define LIGHTING_LAMBERTIAN 1 // use lambertian shading for light sources
-#define LIGHTING_HEIGHT 1 // height off the ground of light sources on the pseudo-z-axis, you should probably leave this alone
-#define LIGHTING_ROUND_VALUE (1 / 128) //Value used to round lumcounts, values smaller than 1/255 don't matter (if they do, thanks sinking points), greater values will make lighting less precise, but in turn increase performance, VERY SLIGHTLY.
-
-#define LIGHTING_LAYER 10 // drawing layer for lighting overlays
-#define LIGHTING_ICON 'icons/effects/lighting_overlay.dmi' // icon used for lighting shading effects
diff --git a/code/modules/lighting/light_source.dm b/code/modules/lighting/light_source.dm
deleted file mode 100644
index d6c00e435ae..00000000000
--- a/code/modules/lighting/light_source.dm
+++ /dev/null
@@ -1,447 +0,0 @@
-//So much copypasta in this file, supposedly in the name of performance. If you change anything make sure to consider other places where the code may have been copied.
-
-/datum/light_source
- var/atom/top_atom
- var/atom/source_atom
-
- var/turf/source_turf
- var/light_power
- var/light_range
- var/light_color // string, decomposed by parse_light_color()
-
- var/lum_r
- var/lum_g
- var/lum_b
-
- //hold onto the actual applied lum values so we can undo them when the lighting changes
- var/tmp/applied_lum_r
- var/tmp/applied_lum_g
- var/tmp/applied_lum_b
-
- var/list/effect_str
- var/list/effect_turf
-
- var/applied
-
- var/vis_update //Whetever we should smartly recalculate visibility. and then only update tiles that became (in) visible to us
- var/needs_update
- var/destroyed
- var/force_update
-
-/datum/light_source/New(atom/owner, atom/top)
- source_atom = owner
- if(!source_atom.light_sources) source_atom.light_sources = list()
- source_atom.light_sources += src
- top_atom = top
- if(top_atom != source_atom)
- if(!top.light_sources) top.light_sources = list()
- top_atom.light_sources += src
-
- source_turf = top_atom
- light_power = source_atom.light_power
- light_range = source_atom.light_range
- light_color = source_atom.light_color
-
- parse_light_color()
-
- effect_str = list()
- effect_turf = list()
-
- update()
-
- return ..()
-
-
-//This proc is called manually on a light if you want it to be more responsive.
-//It forces an update right now instead of waiting for the controller to get around to it, which can be up to 2.1 seconds
-//Update is forced on this light source, and all tiles it effects.
-//This can be very expensive and inefficient, use sparingly
-/datum/light_source/proc/instant_update()
- remove_lum()
- if(!destroyed)
- apply_lum()
-
- else if(vis_update) //We smartly update only tiles that became (in) visible to use.
- smart_vis_update()
-
- vis_update = 0
- force_update = 0
- needs_update = 0
-
- for (var/turf/T in effect_turf)
- if (T.lighting_overlay)
- T.lighting_overlay.update_overlay()
- T.lighting_overlay.needs_update = 0
-
-
-/datum/light_source/proc/destroy()
- destroyed = 1
- force_update()
- if(source_atom && source_atom.light_sources) source_atom.light_sources -= src
- if(top_atom && top_atom.light_sources) top_atom.light_sources -= src
-
-/datum/light_source/proc/update(atom/new_top_atom)
- if(new_top_atom && new_top_atom != top_atom)
- if(top_atom != source_atom) top_atom.light_sources -= src
- top_atom = new_top_atom
- if(top_atom != source_atom)
- if(!top_atom.light_sources) top_atom.light_sources = list()
- top_atom.light_sources += src
-
- if(!needs_update) //Incase we're already updating either way.
- lighting_update_lights += src
- needs_update = 1
-
-/datum/light_source/proc/force_update()
- force_update = 1
- if(!needs_update) //Incase we're already updating either way.
- needs_update = 1
- lighting_update_lights += src
-
-/datum/light_source/proc/vis_update()
- if(!needs_update)
- needs_update = 1
- lighting_update_lights += src
-
- vis_update = 1
-
-/datum/light_source/proc/check()
- if(!source_atom || !light_range || !light_power)
- destroy()
- return 1
-
- if(!top_atom)
- top_atom = source_atom
- . = 1
-
- if(istype(top_atom, /turf))
- if(source_turf != top_atom)
- source_turf = top_atom
- . = 1
- else if(top_atom.loc != source_turf)
- source_turf = top_atom.loc
- . = 1
-
- if(source_atom.light_power != light_power)
- light_power = source_atom.light_power
- . = 1
-
- if(source_atom.light_range != light_range)
- light_range = source_atom.light_range
- . = 1
-
- if(light_range && light_power && !applied)
- . = 1
-
- if(source_atom.light_color != light_color)
- light_color = source_atom.light_color
- parse_light_color()
- . = 1
-
-/datum/light_source/proc/parse_light_color()
- if(light_color)
- lum_r = GetRedPart(light_color) / 255
- lum_g = GetGreenPart(light_color) / 255
- lum_b = GetBluePart(light_color) / 255
- else
- lum_r = 1
- lum_g = 1
- lum_b = 1
-
-#if LIGHTING_FALLOFF == 1 //circular
- #define LUM_DISTANCE(swapvar, O, T) swapvar = (O.x - T.x)**2 + (O.y - T.y)**2 + LIGHTING_HEIGHT
- #if LIGHTING_LAMBERTIAN == 1
- #define LUM_ATTENUATION(swapvar) swapvar = CLAMP01((1 - CLAMP01(sqrt(swapvar) / max(1,light_range))) * (1 / sqrt(swapvar + 1)))
- #else
- #define LUM_ATTENUATION(swapvar) swapvar = 1 - CLAMP01(sqrt(swapvar) / max(1,light_range))
- #endif
-#elif LIGHTING_FALLOFF == 2 //square
- #define LUM_DISTANCE(swapvar, O, T) swapvar = abs(O.x - T.x) + abs(O.y - T.y) + LIGHTING_HEIGHT
- #if LIGHTING_LAMBERTIAN == 1
- #define LUM_ATTENUATION(swapvar) swapvar = CLAMP01((1 - CLAMP01(swapvar / max(1,light_range))) * (1 / sqrt(swapvar**2 + 1)))
- #else
- #define LUM_ATTENUATION(swapvar) swapvar = CLAMP01(swapvar / max(1,light_range))
- #endif
-#endif
-
-#define LUM_FALLOFF(swapvar, O, T) \
- LUM_DISTANCE(swapvar, O, T); \
- LUM_ATTENUATION(swapvar);
-
-/datum/light_source/proc/apply_lum()
- applied = 1
-
- if (istype(source_atom.loc, /mob))//If the light is carried by a mob
- var/mob/M = source_atom.loc
- if (source_atom.offset_light)//And its an offset light
- apply_lum_offset(M)//Then we call the special offset variant and terminate there.
- return//This is split off to minimise overhead added to the majority of non-offset lights
-
-
- //Keep track of the last applied lum values so that the lighting can be reversed
- applied_lum_r = lum_r
- applied_lum_g = lum_g
- applied_lum_b = lum_b
-
- if(istype(source_turf))
- FOR_DVIEW(var/turf/T, light_range, source_turf, INVISIBILITY_LIGHTING)
- if(T.lighting_overlay)
- var/strength
- LUM_FALLOFF(strength, T, source_turf)
- strength *= light_power
-
- if(!strength) //Don't add turfs that aren't affected to the affected turfs.
- continue
-
- strength = round(strength, LIGHTING_ROUND_VALUE) //Screw sinking points.
-
- effect_str += strength
-
- T.lighting_overlay.update_lumcount(
- applied_lum_r * strength,
- applied_lum_g * strength,
- applied_lum_b * strength
- )
-
- else
- effect_str += 0
-
- if(!T.affecting_lights)
- T.affecting_lights = list()
-
- T.affecting_lights += src
- effect_turf += T
- END_FOR_DVIEW
-
-
-//Duplicated code for speed. This is a variant of apply_lum for directional/offset lights carried by a mob
-/datum/light_source/proc/apply_lum_offset(var/mob/M)//M is passed in for speed since we already fetched it
- var/turf/lightfrom = get_step(M, M.dir)//Light source is offset infront of the user, simulates a directional light
- var/list/dview = list()
-
- //We run a special DVIEW call to fetch the list of tiles viewable from the MOB's position
- //This is cross referenced with the below DVIEW loop which runs through tiles viewable from the lightfrom position
- //This is used to prevent offset lights shining through walls
- DVIEW(dview, light_range, source_turf, INVISIBILITY_LIGHTING)
-
-
- applied = 1
-
- //Keep track of the last applied lum values so that the lighting can be reversed
- applied_lum_r = lum_r
- applied_lum_g = lum_g
- applied_lum_b = lum_b
-
- if(istype(lightfrom))
- FOR_DVIEW(var/turf/T, light_range, lightfrom, INVISIBILITY_LIGHTING)//List of turfs visible from the light centre
- if(T.lighting_overlay)
-
- if (!(T in dview))//If the turf is not also visible from the mob, then it's obscured and invalid
- continue//Don't light this tile. This prevents offset lights from shining through walls
-
- var/strength
- LUM_FALLOFF(strength, T, lightfrom)
-
- if (M && T == get_turf(M))//The light applied to the tile the holder is on is reduced, simulates directional light
- strength *= light_power * (source_atom.owner_light_mult)
- else
- strength *= light_power
-
- if(!strength) //Don't add turfs that aren't affected to the affected turfs.
- continue
-
- strength = round(strength, LIGHTING_ROUND_VALUE) //Screw sinking points.
-
- effect_str += strength
-
- T.lighting_overlay.update_lumcount(
- applied_lum_r * strength,
- applied_lum_g * strength,
- applied_lum_b * strength
- )
-
- else
- effect_str += 0
-
- if(!T.affecting_lights)
- T.affecting_lights = list()
-
- T.affecting_lights += src
- effect_turf += T
- END_FOR_DVIEW
-
-
-/datum/light_source/proc/remove_lum()
- applied = 0
- var/i = 1
- for(var/turf/T in effect_turf)
- if(T.affecting_lights)
- T.affecting_lights -= src
-
- if(T.lighting_overlay)
- var/str = effect_str[i]
- T.lighting_overlay.update_lumcount(
- -str * applied_lum_r,
- -str * applied_lum_g,
- -str * applied_lum_b
- )
-
- i++
-
- effect_str.Cut()
- effect_turf.Cut()
-
-//Smartly updates the lighting, only removes lum from and adds lum to turfs that actually got changed.
-//This is for lights that need to reconsider due to nearby opacity changes.
-//Stupid dumb copy pasta because BYOND and speed.
-/datum/light_source/proc/smart_vis_update()
- var/list/view[0]
- FOR_DVIEW(var/turf/T, light_range, source_turf, INVISIBILITY_LIGHTING)
- view += T //Filter out turfs.
- END_FOR_DVIEW
- //This is the part where we calculate new turfs (if any)
- var/list/new_turfs = view - effect_turf //This will result with all the tiles that are added.
- for(var/turf/T in new_turfs)
- if(T.lighting_overlay)
- LUM_FALLOFF(., T, source_turf)
- . *= light_power
-
- if(!.) //Don't add turfs that aren't affected to the affected turfs.
- continue
-
- . = round(., LIGHTING_ROUND_VALUE)
-
- effect_str += .
-
- T.lighting_overlay.update_lumcount(
- applied_lum_r * .,
- applied_lum_g * .,
- applied_lum_b * .
- )
-
- else
- effect_str += 0
-
- if(!T.affecting_lights)
- T.affecting_lights = list()
-
- T.affecting_lights += src
- effect_turf += T
-
- var/list/old_turfs = effect_turf - view
- for(var/turf/T in old_turfs)
- //Insert not-so-huge copy paste from remove_lum().
- var/idx = effect_turf.Find(T) //Get the index, luckily Find() is cheap in small lists like this. (with small I mean under a couple thousand len)
- if(T.affecting_lights)
- T.affecting_lights -= src
-
- if(T.lighting_overlay)
- var/str = effect_str[idx]
- T.lighting_overlay.update_lumcount(-str * applied_lum_r, -str * applied_lum_g, -str * applied_lum_b)
-
- effect_turf.Cut(idx, idx + 1)
- effect_str.Cut(idx, idx + 1)
-
-//Whoop yet not another copy pasta because speed ~~~~BYOND.
-//Calculates and applies lighting for a single turf. This is intended for when a turf switches to
-//using dynamic lighting when it was not doing so previously (when constructing a floor on space, for example).
-//Assumes the turf is visible and such.
-//For the love of god don't call this proc when it's not needed! Lighting artifacts WILL happen!
-/datum/light_source/proc/calc_turf(var/turf/T)
- var/idx = effect_turf.Find(T)
- if(!idx)
- return //WHY.
-
- if(T.lighting_overlay)
- #if LIGHTING_FALLOFF == 1 // circular
- . = (T.lighting_overlay.x - source_turf.x)**2 + (T.lighting_overlay.y - source_turf.y)**2 + LIGHTING_HEIGHT
- #if LIGHTING_LAMBERTIAN == 1
- . = CLAMP01((1 - CLAMP01(sqrt(.) / light_range)) * (1 / (sqrt(. + 1))))
- #else
- . = 1 - CLAMP01(sqrt(.) / light_range)
- #endif
-
- #elif LIGHTING_FALLOFF == 2 // square
- . = abs(T.lighting_overlay.x - source_turf.x) + abs(T.lighting_overlay.y - source_turf.y) + LIGHTING_HEIGHT
- #if LIGHTING_LAMBERTIAN == 1
- . = CLAMP01((1 - CLAMP01(. / light_range)) * (1 / (sqrt(.)**2 + )))
- #else
- . = 1 - CLAMP01(. / light_range)
- #endif
- #endif
- . *= light_power
-
- . = round(., LIGHTING_ROUND_VALUE)
-
- effect_str[idx] = .
-
- //Since the applied_lum values are what are (later) removed by remove_lum.
- //Anything we apply to the lighting overlays HAS to match what remove_lum uses.
- T.lighting_overlay.update_lumcount(
- applied_lum_r * .,
- applied_lum_g * .,
- applied_lum_b * .
- )
-
-
-
-//This function returns the illumination it would/did apply to the specified turf.
-//It is useful for gathering information on a particular source's contribution to a turf's light
-/datum/light_source/proc/get_lum(var/turf/T)
- var/turf/lightfrom = source_turf
- var/mob/M = null
- var/list/dview = list()
- var/list/castview = list()
-
-
- if (istype(source_atom.loc, /mob))
- M = source_atom.loc
- if (source_atom.offset_light)
- DVIEW(dview, light_range, source_turf, INVISIBILITY_LIGHTING)
- lightfrom = get_step(M, M.dir)//Light source is offset infront of the user, simulates a directional light
-
- applied = 1
-
- //Keep track of the last applied lum values so that the lighting can be reversed
- applied_lum_r = lum_r
- applied_lum_g = lum_g
- applied_lum_b = lum_b
-
- if(istype(lightfrom))
-
- //Castview is a list of tiles seen from the light centre.
- //If the desired turf isn't in it, then we couldn't contribute anything to that tile, return a zero list
- DVIEW(castview, light_range, lightfrom, INVISIBILITY_LIGHTING)
- if (!(T in castview))
- return list(0,0,0)
-
- if(T.lighting_overlay)
- //Check for offset lights shining through walls
- if (source_atom.offset_light)
- if (!(T in dview))
- return list(0,0,0)
-
- var/strength
- LUM_FALLOFF(strength, T, lightfrom)
-
- if (M && T == get_turf(M))//The light applied to the tile the holder is on is reduced, simulates directional light
- strength *= light_power * (source_atom.owner_light_mult)
- else
- strength *= light_power
-
- if(!strength) //If no strength, then we contributed nothing.
- return list(0,0,0)
-
- strength = round(strength, LIGHTING_ROUND_VALUE)
-
- //If we're here, then we've confirmed this light does affect the passed tile, and how much.
- //Return the values we've applied to it.
- return list(
- applied_lum_r * strength,
- applied_lum_g * strength,
- applied_lum_b * strength)
-
-
-#undef LUM_FALLOFF
-#undef LUM_DISTANCE
-#undef LUM_ATTENUATION
diff --git a/code/modules/lighting/lighting_area.dm b/code/modules/lighting/lighting_area.dm
new file mode 100644
index 00000000000..837070bb4b4
--- /dev/null
+++ b/code/modules/lighting/lighting_area.dm
@@ -0,0 +1,29 @@
+/area
+ luminosity = TRUE
+ var/dynamic_lighting = TRUE
+
+/area/New()
+ . = ..()
+
+ if (dynamic_lighting)
+ luminosity = FALSE
+
+/area/proc/set_dynamic_lighting(var/new_dynamic_lighting = TRUE)
+ lprof_write(src, "area_sdl")
+
+ if (new_dynamic_lighting == dynamic_lighting)
+ return FALSE
+
+ dynamic_lighting = new_dynamic_lighting
+
+ if (new_dynamic_lighting)
+ for (var/turf/T in turfs)
+ if (T.dynamic_lighting)
+ T.lighting_build_overlay()
+
+ else
+ for (var/turf/T in turfs)
+ if (T.lighting_overlay)
+ T.lighting_clear_overlay()
+
+ return TRUE
diff --git a/code/modules/lighting/lighting_atom.dm b/code/modules/lighting/lighting_atom.dm
index 6e5d858efca..17d2eb7868d 100644
--- a/code/modules/lighting/lighting_atom.dm
+++ b/code/modules/lighting/lighting_atom.dm
@@ -1,100 +1,153 @@
+#define MINIMUM_USEFUL_LIGHT_RANGE 1.4
+
/atom
- var/light_power = 1 // intensity of the light
- var/light_range = 0 // range in tiles of the light
- var/light_color // RGB string representing the colour of the light
+ var/light_power = 1 // Intensity of the light.
+ var/light_range = 0 // Range in tiles of the light.
+ var/light_color // Hexadecimal RGB string representing the colour of the light.
+ var/uv_intensity // How much UV light is being emitted by this object. Valid range: 0-255.
- var/datum/light_source/light
- var/list/light_sources
+ var/tmp/datum/light_source/light // Our light source. Don't fuck with this directly unless you have a good reason!
+ var/tmp/list/light_sources // Any light sources that are "inside" of us, for example, if src here was a mob that's carrying a flashlight, that flashlight's light source would be part of this list.
- //If this var is set, and this object casts light, and the object is worn/held on a mob
- //Then the light source will be offset this many tiles in the mob's facing direction
- //To make this work, must make sure this object is set as the source atom of any lightsource it creates
- //For now, this will only offset one tile, regardless of the value set, but this can be expanded in future
- var/offset_light = 0
+// Nonesensical value for l_color default, so we can detect if it gets set to null.
+#define NONSENSICAL_VALUE -99999
- //If this object emits light and is worn/held on a mob
- //The light applied to the owner's tile is multiplied by this value
- //This is a means to simulate directional light and is only used with offset_light
- var/owner_light_mult = 0.5
+#define SET_LIGHT set_light(l_range,l_power,l_color,uv,update_type);return;
+// Same as set_light(), but only does something if there's actually a change in state.
+/atom/proc/diff_light(/var/l_range, var/l_power, var/l_color = NONSENSICAL_VALUE, var/uv = NONSENSICAL_VALUE, var/update_type = UPDATE_SCHEDULE)
+ if (l_range != light_range)
+ SET_LIGHT
+ if (l_power && l_power != light_power)
+ SET_LIGHT
+ if (l_color != NONSENSICAL_VALUE && l_color != light_color)
+ SET_LIGHT
+ if (uv != NONSENSICAL_VALUE)
+ SET_LIGHT
+ if (update_type != UPDATE_SCHEDULE)
+ SET_LIGHT
- //If 1, this light has reduced effect on diona
- //It won't stack with other restricted light sources
- var/diona_restricted_light = 0
+#undef SET_LIGHT
+// The proc you should always use to set the light of this atom.
+/atom/proc/set_light(var/l_range, var/l_power, var/l_color = NONSENSICAL_VALUE, var/uv = NONSENSICAL_VALUE, var/update_type = UPDATE_SCHEDULE)
+ lprof_write(src, "atom_setlight")
-/atom/proc/set_light(l_range, l_power, l_color)
- . = 0 //make it less costly if nothing's changed
-
- if(l_power != null && l_power != light_power)
+ if(l_range > 0 && l_range < MINIMUM_USEFUL_LIGHT_RANGE)
+ l_range = MINIMUM_USEFUL_LIGHT_RANGE //Brings the range up to 1.4, which is just barely brighter than the soft lighting that surrounds players.
+ if (l_power != null)
light_power = l_power
- . = 1
- if(l_range != null && l_range != light_range)
+
+ if (l_range != null)
light_range = l_range
- . = 1
- if(l_color != null && l_color != light_color)
+
+ if (l_color != NONSENSICAL_VALUE)
light_color = l_color
- . = 1
- if(.) update_light()
+ if (uv != NONSENSICAL_VALUE)
+ set_uv(uv, update_type = UPDATE_NONE)
-/atom/proc/copy_light(atom/A)
- set_light(A.light_range, A.light_power, A.light_color)
+ switch (update_type)
+ if (UPDATE_SCHEDULE)
+ update_light()
+ if (UPDATE_NOW)
+ update_light(TRUE)
-/atom/proc/update_light()
- if(!light_power || !light_range)
+#undef NONSENSICAL_VALUE
+
+/atom/proc/set_uv(var/intensity, var/update_type = UPDATE_SCHEDULE)
+ if (intensity < 0 || intensity > 255)
+ intensity = min(max(intensity, 255), 0)
+
+ uv_intensity = intensity
+
+ if (update_type != UPDATE_NONE)
+ update_light(update_type)
+
+// Will update the light (duh).
+// Creates or destroys it if needed, makes it update values, makes sure it's got the correct source turf...
+/atom/proc/update_light(var/update_type = UPDATE_SCHEDULE)
+ set waitfor = FALSE
+ if (gcDestroyed)
+ return
+
+ lprof_write(src, "atom_update")
+
+ if (!light_power || !light_range) // We won't emit light anyways, destroy the light source.
if(light)
light.destroy()
light = null
else
- if(!istype(loc, /atom/movable))
+ if (!istype(loc, /atom/movable)) // We choose what atom should be the top atom of the light here.
. = src
else
. = loc
- if(light)
- light.update(.)
+ if (light) // Update the light or create it if it does not exist.
+ light.update(., update_type)
else
- light = new /datum/light_source(src, .)
+ light = new/datum/light_source(src, .)
+// Incase any lighting vars are on in the typepath we turn the light on in New().
/atom/New()
. = ..()
- if(light_power && light_range)
+
+ if (light_power && light_range)
update_light()
+ if (opacity && isturf(loc))
+ var/turf/T = loc
+ T.has_opaque_atom = TRUE // No need to recalculate it in this case, it's guaranteed to be on afterwards anyways.
+
+// Destroy our light source so we GC correctly.
/atom/Destroy()
- if(light)
+ if (light)
light.destroy()
light = null
- return ..()
+ . = ..()
+// If we have opacity, make sure to tell (potentially) affected light sources.
/atom/movable/Destroy()
var/turf/T = loc
- if(opacity && istype(T))
+ if (opacity && istype(T))
T.reconsider_lights()
- return ..()
-/atom/Entered(atom/movable/obj, atom/prev_loc)
. = ..()
- if(obj && prev_loc != src)
- for(var/datum/light_source/L in obj.light_sources)
- L.source_atom.update_light()
+// Should always be used to change the opacity of an atom.
+// It notifies (potentially) affected light sources so they can update (if needed).
+/atom/proc/set_opacity(var/new_opacity)
+ if (new_opacity == opacity)
+ return
+
+ lprof_write(src, "atom_setopacity")
-/atom/proc/set_opacity(new_opacity)
- var/old_opacity = opacity
opacity = new_opacity
var/turf/T = loc
- if(old_opacity != new_opacity && istype(T))
+ if (!isturf(T))
+ return
+
+ if (new_opacity == TRUE)
+ T.has_opaque_atom = TRUE
T.reconsider_lights()
+ else
+ var/old_has_opaque_atom = T.has_opaque_atom
+ T.recalc_atom_opacity()
+ if (old_has_opaque_atom != T.has_opaque_atom)
+ T.reconsider_lights()
-/obj/item/equipped()
- . = ..()
- update_light()
-/obj/item/pickup()
+// This code makes the light be queued for update when it is moved.
+// Entered() should handle it, however Exited() can do it if it is being moved to nullspace (as there would be no Entered() call in that situation).
+/atom/Entered(var/atom/movable/Obj, var/atom/OldLoc) //Implemented here because forceMove() doesn't call Move()
. = ..()
- update_light()
-/obj/item/dropped()
+ if (Obj && OldLoc != src)
+ for (var/datum/light_source/L in Obj.light_sources) // Cycle through the light sources on this atom and tell them to update.
+ L.source_atom.update_light(update_type = UPDATE_NOW)
+
+/atom/Exited(var/atom/movable/Obj, var/atom/newloc)
. = ..()
- update_light()
+
+ if (!newloc && Obj && newloc != src) // Incase the atom is being moved to nullspace, we handle queuing for a lighting update here.
+ for (var/datum/light_source/L in Obj.light_sources) // Cycle through the light sources on this atom and tell them to update.
+ L.source_atom.update_light(update_type = UPDATE_NOW)
diff --git a/code/modules/lighting/lighting_corner.dm b/code/modules/lighting/lighting_corner.dm
new file mode 100644
index 00000000000..05ba71d9af1
--- /dev/null
+++ b/code/modules/lighting/lighting_corner.dm
@@ -0,0 +1,134 @@
+/var/list/datum/lighting_corner/all_lighting_corners = list()
+/var/datum/lighting_corner/dummy/dummy_lighting_corner = new
+// Because we can control each corner of every lighting overlay.
+// And corners get shared between multiple turfs (unless you're on the corners of the map, then 1 corner doesn't).
+// For the record: these should never ever ever be deleted, even if the turf doesn't have dynamic lighting.
+
+// This list is what the code that assigns corners listens to, the order in this list is the order in which corners are added to the /turf/corners list.
+/var/list/LIGHTING_CORNER_DIAGONAL = list(NORTHEAST, SOUTHEAST, SOUTHWEST, NORTHWEST)
+
+/datum/lighting_corner
+ var/list/turf/masters = list()
+ var/list/datum/light_source/affecting = list() // Light sources affecting us.
+ var/active = FALSE // TRUE if one of our masters has dynamic lighting.
+
+ var/x = 0
+ var/y = 0
+ var/z = 0
+
+ var/lum_r = 0
+ var/lum_g = 0
+ var/lum_b = 0
+ var/lum_u = 0 // UV Radiation, not visible.
+
+ var/needs_update = FALSE
+
+ var/cache_r = 0
+ var/cache_g = 0
+ var/cache_b = 0
+ var/cache_u = 0
+ var/cache_mx = 0
+
+ var/update_gen = 0
+
+/datum/lighting_corner/New(var/turf/new_turf, var/diagonal)
+ . = ..()
+
+ all_lighting_corners += src
+
+ masters[new_turf] = turn(diagonal, 180)
+ z = new_turf.z
+
+ var/vertical = diagonal & ~(diagonal - 1) // The horizontal directions (4 and 8) are bigger than the vertical ones (1 and 2), so we can reliably say the lsb is the horizontal direction.
+ var/horizontal = diagonal & ~vertical // Now that we know the horizontal one we can get the vertical one.
+
+ x = new_turf.x + (horizontal == EAST ? 0.5 : -0.5)
+ y = new_turf.y + (vertical == NORTH ? 0.5 : -0.5)
+
+ // My initial plan was to make this loop through a list of all the dirs (horizontal, vertical, diagonal).
+ // Issue being that the only way I could think of doing it was very messy, slow and honestly overengineered.
+ // So we'll have this hardcode instead.
+ var/turf/T
+ var/i
+
+ // Diagonal one is easy.
+ T = get_step(new_turf, diagonal)
+ if (T) // In case we're on the map's border.
+ if (!T.corners)
+ T.corners = list(null, null, null, null)
+
+ masters[T] = diagonal
+ i = LIGHTING_CORNER_DIAGONAL.Find(turn(diagonal, 180))
+ T.corners[i] = src
+
+ // Now the horizontal one.
+ T = get_step(new_turf, horizontal)
+ if (T) // Ditto.
+ if (!T.corners)
+ T.corners = list(null, null, null, null)
+
+ masters[T] = ((T.x > x) ? EAST : WEST) | ((T.y > y) ? NORTH : SOUTH) // Get the dir based on coordinates.
+ i = LIGHTING_CORNER_DIAGONAL.Find(turn(masters[T], 180))
+ T.corners[i] = src
+
+ // And finally the vertical one.
+ T = get_step(new_turf, vertical)
+ if (T)
+ if (!T.corners)
+ T.corners = list(null, null, null, null)
+
+ masters[T] = ((T.x > x) ? EAST : WEST) | ((T.y > y) ? NORTH : SOUTH) // Get the dir based on coordinates.
+ i = LIGHTING_CORNER_DIAGONAL.Find(turn(masters[T], 180))
+ T.corners[i] = src
+
+ update_active()
+
+/datum/lighting_corner/proc/update_active()
+ active = FALSE
+ for (var/turf/T in masters)
+ if (T.lighting_overlay)
+ active = TRUE
+
+// God that was a mess, now to do the rest of the corner code! Hooray!
+/datum/lighting_corner/proc/update_lumcount(var/delta_r, var/delta_g, var/delta_b, var/delta_u, var/update_type = UPDATE_SCHEDULE)
+ lum_r += delta_r
+ lum_g += delta_g
+ lum_b += delta_b
+ lum_u += delta_u
+
+ if (update_type == UPDATE_SCHEDULE && !needs_update)
+ needs_update = TRUE
+ update_overlays(update_type)
+ lighting_update_corners += src
+ else if (!needs_update)
+ update_overlays(UPDATE_NOW)
+
+/datum/lighting_corner/proc/update_overlays(var/update_type = UPDATE_SCHEDULE)
+
+ // Cache these values a head of time so 4 individual lighting overlays don't all calculate them individually.
+ var/mx = max(lum_r, lum_g, lum_b) // Scale it so 1 is the strongest lum, if it is above 1.
+ . = 1 // factor
+ if (mx > 1)
+ . = 1 / mx
+
+ else if (mx < LIGHTING_SOFT_THRESHOLD)
+ . = 0 // 0 means soft lighting.
+
+ cache_r = lum_r * . || LIGHTING_SOFT_THRESHOLD
+ cache_g = lum_g * . || LIGHTING_SOFT_THRESHOLD
+ cache_b = lum_b * . || LIGHTING_SOFT_THRESHOLD
+ cache_u = lum_u * . || LIGHTING_SOFT_THRESHOLD
+ cache_mx = mx
+
+ for (var/TT in masters)
+ var/turf/T = TT
+ if (T.lighting_overlay)
+ if (update_type == UPDATE_NOW) // UPDATE_NONE is meaningless here.
+ T.lighting_overlay.update_overlay()
+
+ else if (!T.lighting_overlay.needs_update)
+ T.lighting_overlay.needs_update = TRUE
+ lighting_update_overlays += T.lighting_overlay
+
+/datum/lighting_corner/dummy/New()
+ return
diff --git a/code/modules/lighting/lighting_overlay.dm b/code/modules/lighting/lighting_overlay.dm
index 2dd3889e4e9..5ca3776bab8 100644
--- a/code/modules/lighting/lighting_overlay.dm
+++ b/code/modules/lighting/lighting_overlay.dm
@@ -1,102 +1,111 @@
+/var/list/all_lighting_overlays = list() // Global list of lighting overlays.
+
/atom/movable/lighting_overlay
- name = ""
+ name = ""
+ anchored = TRUE
+ icon = LIGHTING_ICON
+ color = LIGHTING_BASE_MATRIX
mouse_opacity = 0
- simulated = 0
- anchored = 1
+ layer = LIGHTING_LAYER
+ invisibility = INVISIBILITY_LIGHTING
+ simulated = 0
+ blend_mode = BLEND_MULTIPLY
- icon = LIGHTING_ICON
- icon_state = "light1"
- layer = LIGHTING_LAYER
- invisibility = INVISIBILITY_LIGHTING
- color = "#000000"
+ var/needs_update = FALSE
- var/lum_r
- var/lum_g
- var/lum_b
+ #if WORLD_ICON_SIZE != 32
+ transform = matrix(WORLD_ICON_SIZE / 32, 0, (WORLD_ICON_SIZE - 32) / 2, 0, WORLD_ICON_SIZE / 32, (WORLD_ICON_SIZE - 32) / 2)
+ #endif
- var/needs_update
-
-/atom/movable/lighting_overlay/New()
+/atom/movable/lighting_overlay/New(var/atom/loc, var/no_update = FALSE)
. = ..()
verbs.Cut()
+ global.all_lighting_overlays += src
- var/turf/T = loc //If this runtimes atleast we'll know what's creating overlays in things that aren't turfs.
- T.luminosity = 0
+ var/turf/T = loc // If this runtimes atleast we'll know what's creating overlays in things that aren't turfs.
+ T.lighting_overlay = src
+ T.luminosity = 0
-/atom/movable/lighting_overlay/proc/update_lumcount(delta_r, delta_g, delta_b)
- if(!delta_r && !delta_g && !delta_b) //Nothing is being changed all together.
+ if (no_update)
return
- var/should_update = 0
+ update_overlay()
- if(!needs_update) //If this isn't true, we're already updating anyways.
- if(max(lum_r, lum_g, lum_b) < 1) //Any change that could happen WILL change appearance.
- should_update = 1
+/atom/movable/lighting_overlay/Destroy()
+ global.all_lighting_overlays -= src
+ global.lighting_update_overlays -= src
+ lighting_process.curr_overlays -= src
- else if(max(lum_r + delta_r, lum_g + delta_g, lum_b + delta_b) < 1) //The change would bring us under 1 max lum, again, guaranteed to change appearance.
- should_update = 1
+ var/turf/T = loc
+ if (istype(T))
+ T.lighting_overlay = null
+ T.luminosity = 1
- else //We need to make sure that the colour ratios won't change in this code block.
- var/mx1 = max(lum_r, lum_g, lum_b)
- var/mx2 = max(lum_r + delta_r, lum_g + delta_g, lum_b + delta_b)
-
- if(lum_r / mx1 != (lum_r + delta_r) / mx2 || lum_g / mx1 != (lum_g + delta_g) / mx2 || lum_b / mx1 != (lum_b + delta_b) / mx2) //Stuff would change.
- should_update = 1
-
- lum_r += delta_r
- lum_g += delta_g
- lum_b += delta_b
-
- if(!needs_update && should_update)
- needs_update = 1
- lighting_update_overlays += src
+ ..()
/atom/movable/lighting_overlay/proc/update_overlay()
var/turf/T = loc
+ if (!istype(T)) // Erm...
+ if (loc)
+ warning("A lighting overlay realised its loc was NOT a turf (actual loc: [loc], [loc.type]) in update_overlay() and got pooled!")
- if(istype(T)) //Incase we're not on a turf, pool ourselves, something happened.
- if(lum_r == lum_g && lum_r == lum_b) //greyscale
- blend_mode = BLEND_OVERLAY
- if(lum_r <= 0)
- T.luminosity = 0
- color = "#000000"
- alpha = 255
- else
- T.luminosity = 1
- color = "#000000"
- alpha = (1 - min(lum_r, 1)) * 255
else
- alpha = 255
- var/mx = max(lum_r, lum_g, lum_b)
- . = 1 // factor
- if(mx > 1)
- . = 1/mx
- blend_mode = BLEND_MULTIPLY
- color = rgb(lum_r * 255 * ., lum_g * 255 * ., lum_b * 255 * .)
- if(color != "#000000")
- T.luminosity = 1
- else //No light, set the turf's luminosity to 0 to remove it from view()
- T.luminosity = 0
- else
- warning("A lighting overlay realised its loc was NOT a turf (actual loc: [loc][loc ? ", " + loc.type : ""]) in update_overlay() and got pooled!")
- qdel(src)
+ warning("A lighting overlay realised it was in nullspace in update_overlay() and got pooled!")
-/atom/movable/lighting_overlay/ResetVars()
- loc = null
+ returnToPool(src)
+ return
- lum_r = 0
- lum_g = 0
- lum_b = 0
+ if (T.is_space())
+ warning("A lighting overlay realised it was attached to a space tile and got pooled!")
+ returnToPool(src)
+ return
- color = "#000000"
+ // To the future coder who sees this and thinks
+ // "Why didn't he just use a loop?"
+ // Well my man, it's because the loop performed like shit.
+ // And there's no way to improve it because
+ // without a loop you can make the list all at once which is the fastest you're gonna get.
+ // Oh it's also shorter line wise.
+ // Including with these comments.
- needs_update = 0
+ // See LIGHTING_CORNER_DIAGONAL in lighting_corner.dm for why these values are what they are.
+ // No I seriously cannot think of a more efficient method, fuck off Comic.
+ var/datum/lighting_corner/cr = T.corners[3] || dummy_lighting_corner
+ var/datum/lighting_corner/cg = T.corners[2] || dummy_lighting_corner
+ var/datum/lighting_corner/cb = T.corners[4] || dummy_lighting_corner
+ var/datum/lighting_corner/ca = T.corners[1] || dummy_lighting_corner
-/atom/movable/lighting_overlay/Destroy()
- lighting_update_overlays -= src
+ var/max = max(cr.cache_mx, cg.cache_mx, cb.cache_mx, ca.cache_mx)
- var/turf/T = loc
- if(istype(T))
- T.lighting_overlay = null
-
- ..()
+ color = list(
+ cr.cache_r, cr.cache_g, cr.cache_b, 0,
+ cg.cache_r, cg.cache_g, cg.cache_b, 0,
+ cb.cache_r, cb.cache_g, cb.cache_b, 0,
+ ca.cache_r, ca.cache_g, ca.cache_b, 0,
+ 0, 0, 0, 1
+ )
+ luminosity = max > LIGHTING_SOFT_THRESHOLD
+
+// Variety of overrides so the overlays don't get affected by weird things.
+
+/atom/movable/lighting_overlay/ex_act(severity)
+ return 0
+
+/atom/movable/lighting_overlay/singularity_act()
+ return
+
+/atom/movable/lighting_overlay/singularity_pull()
+ return
+
+/atom/movable/lighting_overlay/singuloCanEat()
+ return FALSE
+
+// Override here to prevent things accidentally moving around overlays.
+/atom/movable/lighting_overlay/forceMove(atom/destination, var/no_tp=FALSE, var/harderforce = FALSE)
+ if(harderforce)
+ . = ..()
+
+/atom/movable/lighting_overlay/resetVariables(...)
+ color = LIGHTING_BASE_MATRIX
+
+ return ..("color")
diff --git a/code/modules/lighting/lighting_process.dm b/code/modules/lighting/lighting_process.dm
deleted file mode 100644
index 562fd1aea6f..00000000000
--- a/code/modules/lighting/lighting_process.dm
+++ /dev/null
@@ -1,43 +0,0 @@
-var/datum/controller/process/lighting/lighting_process
-
-/datum/controller/process/lighting
- var/last_light_count = 0
- var/last_overlay_count = 0
-
-/datum/controller/process/lighting/setup()
- name = "lighting"
- schedule_interval = LIGHTING_INTERVAL
- create_lighting_overlays()
- lighting_process = src
-
-/datum/controller/process/lighting/doWork()
- var/list/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.
- last_light_count = lighting_update_lights.len
- lighting_update_lights = null //Nulling it first because of http://www.byond.com/forum/?post=1854520
- lighting_update_lights = list()
-
- for(var/datum/light_source/L in lighting_update_lights_old)
- if(L.destroyed || L.check() || 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 = 0
- L.force_update = 0
- L.needs_update = 0
-
- SCHECK
-
- var/list/lighting_update_overlays_old = lighting_update_overlays //Same as above.
- last_overlay_count = lighting_update_overlays.len
- lighting_update_overlays = null //Same as above
- lighting_update_overlays = list()
-
- for(var/atom/movable/lighting_overlay/O in lighting_update_overlays_old)
- O.update_overlay()
- O.needs_update = 0
-
- SCHECK
diff --git a/code/modules/lighting/lighting_profiler.dm b/code/modules/lighting/lighting_profiler.dm
new file mode 100644
index 00000000000..484aa224977
--- /dev/null
+++ b/code/modules/lighting/lighting_profiler.dm
@@ -0,0 +1,40 @@
+// Writes lighting updates to the database.
+// FOR DEBUGGING ONLY!
+
+/proc/lprof_write(var/atom/movable/obj, var/type = "UNKNOWN")
+ if (!lighting_profiling || !obj || !establish_db_connection(dbcon))
+ return
+
+ var/x = null
+ var/y = null
+ var/z = null
+
+ var/name = null
+ var/locname = null
+ if (istype(obj, /obj))
+ name = obj.name
+ locname = obj.loc.name
+ x = obj.loc.x
+ y = obj.loc.y
+ z = obj.loc.z
+
+ var/static/DBQuery/lprof_q
+ if (!lprof_q)
+ lprof_q = dbcon.NewQuery({"INSERT INTO ss13dbg_lighting (time,type,name,loc_name,x,y,z)
+ VALUES (:time,:type,:name,:loc_name,:x,:y,:z);"})
+
+ lprof_q.Execute(
+ list(
+ ":time" = world.time,
+ ":type" = type,
+ ":name" = name,
+ ":loc_name" = locname,
+ ":x" = x,
+ ":y" = y,
+ ":z" = z))
+
+ var/err = lprof_q.ErrorMsg()
+ if (err)
+ log_debug("lprof_write: SQL Error: [err]")
+ message_admins(span("danger", "SQL Error during lighting profiling; disabling!"))
+ lighting_profiling = FALSE
diff --git a/code/modules/lighting/lighting_setup.dm b/code/modules/lighting/lighting_setup.dm
new file mode 100644
index 00000000000..439feb81f27
--- /dev/null
+++ b/code/modules/lighting/lighting_setup.dm
@@ -0,0 +1,84 @@
+/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
+ if (!A.dynamic_lighting)
+ continue
+
+ getFromPool(/atom/movable/lighting_overlay, T, TRUE)
+
+// This repeats a bit of code from the lighting process.
+/proc/initialize_lighting()
+ admin_notice(span("danger", "Generating lighting overlays (1/4)..."))
+ for (var/zlevel = 1 to world.maxz)
+ create_lighting_overlays_zlevel(zlevel)
+ CHECK_TICK
+
+ admin_notice(span("danger", "Initializing light sources (2/4)..."))
+ var/num_lights = 0
+ var/list/lights = lighting_update_lights
+ lighting_update_lights = list()
+
+ while (lights.len)
+ var/datum/light_source/L = lights[lights.len]
+ lights.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
+ num_lights++
+ CHECK_TICK
+
+ admin_notice(span("danger", "Processed [num_lights] light sources."))
+
+ admin_notice(span("danger", "Initializing lighting corners (3/4)..."))
+ var/num_corners = 0
+ var/list/corners = lighting_update_corners
+ lighting_update_corners = list()
+
+ while (corners.len)
+ var/datum/lighting_corner/C = corners[corners.len]
+ corners.len--
+
+ if (!C) continue
+
+ C.update_overlays()
+
+ C.needs_update = FALSE
+ num_corners++
+
+ CHECK_TICK
+
+ admin_notice(span("danger", "Processed [num_corners] light corners."))
+ admin_notice(span("danger", "Initializing lighting overlays (4/4)..."))
+ var/num_overlays = 0
+ var/list/overlays = lighting_update_overlays
+ lighting_update_overlays = list()
+
+ while (overlays.len)
+ var/atom/movable/lighting_overlay/O = overlays[overlays.len]
+ overlays.len--
+
+ if (!O) continue
+
+ O.update_overlay()
+ O.needs_update = FALSE
+
+ num_overlays++
+ CHECK_TICK
+
+ admin_notice(span("danger", "Processed [num_overlays] light overlays."))
diff --git a/code/modules/lighting/lighting_source.dm b/code/modules/lighting/lighting_source.dm
new file mode 100644
index 00000000000..f3e13daaf77
--- /dev/null
+++ b/code/modules/lighting/lighting_source.dm
@@ -0,0 +1,320 @@
+// This is where the fun begins.
+// These are the main datums that emit light.
+
+/datum/light_source
+ var/atom/top_atom // The atom we're emitting light from (for example a mob if we're from a flashlight that's being held).
+ var/atom/source_atom // The atom that we belong to.
+
+ var/turf/source_turf // The turf under the above.
+ var/light_power // Intensity of the emitter light.
+ var/light_range // The range of the emitted light.
+ var/light_color // The colour of the light, string, decomposed by parse_light_color()
+ var/light_uv // The intensity of UV light, between 0 and 255.
+
+ // Variables for keeping track of the colour.
+ var/lum_r
+ var/lum_g
+ var/lum_b
+ var/lum_u
+
+ // The lumcount values used to apply the light.
+ var/tmp/applied_lum_r
+ var/tmp/applied_lum_g
+ var/tmp/applied_lum_b
+ var/tmp/applied_lum_u
+
+ var/list/datum/lighting_corner/effect_str // List used to store how much we're affecting corners.
+ var/list/turf/affecting_turfs
+
+ var/applied = FALSE // Whether we have applied our light yet or not.
+
+ var/vis_update // Whether we should smartly recalculate visibility. and then only update tiles that became (in)visible to us.
+ var/needs_update // Whether we are queued for an update.
+ var/destroyed // Whether we are destroyed and need to stop emitting light.
+ var/force_update
+
+/datum/light_source/New(var/atom/owner, var/atom/top, var/update_t = UPDATE_SCHEDULE)
+ source_atom = owner // Set our new owner.
+ if (!source_atom.light_sources)
+ source_atom.light_sources = list()
+
+ source_atom.light_sources += src // Add us to the lights of our owner.
+ top_atom = top
+ if (top_atom != source_atom)
+ if (!top.light_sources)
+ top.light_sources = list()
+
+ top_atom.light_sources += src
+
+ source_turf = top_atom
+ light_power = source_atom.light_power
+ light_range = source_atom.light_range
+ light_color = source_atom.light_color
+ light_uv = source_atom.uv_intensity
+
+ parse_light_color()
+
+ effect_str = list()
+ affecting_turfs = list()
+
+ update(update_type = update_t)
+
+ lprof_write(src, "source_new")
+
+ return ..()
+
+// Kill ourselves.
+/datum/light_source/proc/destroy()
+ destroyed = TRUE
+ force_update()
+ if (source_atom && source_atom.light_sources)
+ source_atom.light_sources -= src
+
+ if (top_atom && top_atom.light_sources)
+ top_atom.light_sources -= src
+
+/datum/light_source/proc/effect_update_now()
+ lprof_write(src, "source_updatenow")
+ if (check() || destroyed || force_update)
+ remove_lum(TRUE)
+ if (!destroyed)
+ apply_lum(update_type = UPDATE_NOW)
+
+ else if (vis_update) // We smartly update only tiles that became (in) visible to use.
+ smart_vis_update(update_type = UPDATE_NOW)
+
+ vis_update = FALSE
+ force_update = FALSE
+ needs_update = FALSE
+
+// Call it dirty, I don't care.
+// This is here so there's no performance loss on non-instant updates from the fact that the engine can also do instant updates.
+// If you're wondering what's with the "BYOND" argument: BYOND won't let me have a () macro that has no arguments :|.
+#define effect_update(BYOND) \
+ if (!needs_update) \
+ { \
+ lighting_update_lights += src; \
+ needs_update = TRUE; \
+ }
+
+// This proc will cause the light source to update the top atom, and add itself to the update queue.
+/datum/light_source/proc/update(var/atom/new_top_atom, var/update_type = UPDATE_SCHEDULE)
+ // This top atom is different.
+ if (new_top_atom && new_top_atom != top_atom)
+ if(top_atom != source_atom) // Remove ourselves from the light sources of that top atom.
+ top_atom.light_sources -= src
+
+ top_atom = new_top_atom
+
+ if (top_atom != source_atom)
+ if(!top_atom.light_sources)
+ top_atom.light_sources = list()
+
+ top_atom.light_sources += src // Add ourselves to the light sources of our new top atom.
+
+ lprof_write(src, "source_update")
+
+ if (update_type == UPDATE_NOW)
+ effect_update_now()
+ else if (update_type == UPDATE_SCHEDULE) // I don't know why you would call this with UPDATE_NONE, but hey.
+ effect_update(null)
+
+// Will force an update without checking if it's actually needed.
+/datum/light_source/proc/force_update()
+ lprof_write(src, "source_forceupdate")
+ force_update = 1
+
+ effect_update(null)
+
+// Will cause the light source to recalculate turfs that were removed or added to visibility only.
+/datum/light_source/proc/vis_update()
+ vis_update = 1
+
+ effect_update(null)
+
+// Will check if we actually need to update, and update any variables that may need to be updated.
+/datum/light_source/proc/check()
+ if (!source_atom || !light_range || !light_power)
+ destroy()
+ return 1
+
+ if (!top_atom)
+ top_atom = source_atom
+ . = 1
+
+ if (istype(top_atom, /turf))
+ if (source_turf != top_atom)
+ source_turf = top_atom
+ . = 1
+ else if (top_atom.loc != source_turf)
+ source_turf = top_atom.loc
+ . = 1
+
+ if (source_atom.light_power != light_power)
+ light_power = source_atom.light_power
+ . = 1
+
+ if (source_atom.light_range != light_range)
+ light_range = source_atom.light_range
+ . = 1
+
+ if (light_range && light_power && !applied)
+ . = 1
+
+ if (source_atom.light_color != light_color)
+ light_color = source_atom.light_color
+ parse_light_color()
+ . = 1
+
+// Decompile the hexadecimal colour into lumcounts of each perspective.
+/datum/light_source/proc/parse_light_color()
+ if (light_color)
+ lum_r = GetRedPart (light_color) / 255
+ lum_g = GetGreenPart (light_color) / 255
+ lum_b = GetBluePart (light_color) / 255
+ else
+ lum_r = 1
+ lum_g = 1
+ lum_b = 1
+
+ if (light_uv)
+ lum_u = light_uv / 255
+ else
+ lum_u = 0
+
+// Macro that applies light to a new corner.
+// It is a macro in the interest of speed, yet not having to copy paste it.
+// If you're wondering what's with the backslashes, the backslashes cause BYOND to not automatically end the line.
+// As such this all gets counted as a single line.
+// The braces and semicolons are there to be able to do this on a single line.
+
+#define APPLY_CORNER(C) \
+ . = LUM_FALLOFF(C, source_turf); \
+ \
+ . *= light_power; \
+ \
+ effect_str[C] = .; \
+ \
+ C.update_lumcount \
+ ( \
+ . * applied_lum_r, \
+ . * applied_lum_g, \
+ . * applied_lum_b, \
+ . * applied_lum_u, \
+ update_type \
+ );
+
+// I don't need to explain what this does, do I?
+#define REMOVE_CORNER(C) \
+ . = -effect_str[C]; \
+ C.update_lumcount \
+ ( \
+ . * applied_lum_r, \
+ . * applied_lum_g, \
+ . * applied_lum_b, \
+ . * applied_lum_u, \
+ update_type \
+ );
+
+// This is the define used to calculate falloff.
+#define LUM_FALLOFF(C, T) (1 - CLAMP01(sqrt((C.x - T.x) ** 2 + (C.y - T.y) ** 2 + LIGHTING_HEIGHT) / max(1, light_range)))
+
+/datum/light_source/proc/apply_lum(var/update_type = UPDATE_SCHEDULE)
+ var/static/update_gen = 1
+ applied = 1
+
+ // Keep track of the last applied lum values so that the lighting can be reversed
+ applied_lum_r = lum_r
+ applied_lum_g = lum_g
+ applied_lum_b = lum_b
+ applied_lum_u = lum_u
+
+ FOR_DVIEW(var/turf/T, light_range, source_turf, INVISIBILITY_LIGHTING)
+ if (!T.lighting_corners_initialised)
+ T.generate_missing_corners()
+
+ for (var/datum/lighting_corner/C in T.get_corners())
+ if (C.update_gen == update_gen)
+ continue
+
+ C.update_gen = update_gen
+ C.affecting += src
+
+ if (!C.active)
+ effect_str[C] = 0
+ continue
+
+ APPLY_CORNER(C)
+
+ if (!T.affecting_lights)
+ T.affecting_lights = list()
+
+ T.affecting_lights += src
+ affecting_turfs += T
+
+ update_gen++
+
+/datum/light_source/proc/remove_lum(var/update_type = UPDATE_SCHEDULE)
+ applied = FALSE
+
+ for (var/turf/T in affecting_turfs)
+ if (!T.affecting_lights)
+ T.affecting_lights = list()
+ else
+ T.affecting_lights -= src
+
+ affecting_turfs.Cut()
+
+ for (var/datum/lighting_corner/C in effect_str)
+ REMOVE_CORNER(C)
+
+ C.affecting -= src
+
+ effect_str.Cut()
+
+/datum/light_source/proc/recalc_corner(var/datum/lighting_corner/C)
+ var/update_type = UPDATE_SCHEDULE // for (APPLY|REMOVE)_CORNER.
+ if (effect_str.Find(C)) // Already have one.
+ REMOVE_CORNER(C)
+
+ APPLY_CORNER(C)
+
+/datum/light_source/proc/smart_vis_update(var/update_type = UPDATE_SCHEDULE)
+ var/list/datum/lighting_corner/corners = list()
+ var/list/turf/turfs = list()
+ FOR_DVIEW(var/turf/T, light_range, source_turf, 0)
+ if (!T.lighting_corners_initialised)
+ T.generate_missing_corners()
+ corners |= T.get_corners()
+ turfs += T
+
+ var/list/L = turfs - affecting_turfs // New turfs, add us to the affecting lights of them.
+ affecting_turfs += L
+ for (var/turf/T in L)
+ if (!T.affecting_lights)
+ T.affecting_lights = list(src)
+ else
+ T.affecting_lights += src
+
+ L = affecting_turfs - turfs // Now-gone turfs, remove us from the affecting lights.
+ affecting_turfs -= L
+ for (var/turf/T in L)
+ T.affecting_lights -= src
+
+ for (var/datum/lighting_corner/C in corners - effect_str) // New corners
+ C.affecting += src
+ if (!C.active)
+ effect_str[C] = 0
+ continue
+
+ APPLY_CORNER(C)
+
+ for (var/datum/lighting_corner/C in effect_str - corners) // Old, now gone, corners.
+ REMOVE_CORNER(C)
+ C.affecting -= src
+ effect_str -= C
+
+#undef effect_update
+#undef LUM_FALLOFF
+#undef REMOVE_CORNER
+#undef APPLY_CORNER
diff --git a/code/modules/lighting/lighting_system.dm b/code/modules/lighting/lighting_system.dm
deleted file mode 100644
index 0c84294f35e..00000000000
--- a/code/modules/lighting/lighting_system.dm
+++ /dev/null
@@ -1,25 +0,0 @@
-/var/list/lighting_update_lights = list()
-/var/list/lighting_update_overlays = list()
-
-/area/var/lighting_use_dynamic = 1
-
-// duplicates lots of code, but this proc needs to be as fast as possible.
-/proc/create_lighting_overlays(zlevel = 0)
- var/area/A
- if(zlevel == 0) // populate all zlevels
- for(var/turf/T in world)
- if(T.dynamic_lighting)
- A = T.loc
- if(A.lighting_use_dynamic)
- var/atom/movable/lighting_overlay/O = PoolOrNew(/atom/movable/lighting_overlay, T)
- T.lighting_overlay = O
-
- else
- for(var/x = 1; x <= world.maxx; x++)
- for(var/y = 1; y <= world.maxy; y++)
- var/turf/T = locate(x, y, zlevel)
- if(T.dynamic_lighting)
- A = T.loc
- if(A.lighting_use_dynamic)
- var/atom/movable/lighting_overlay/O = PoolOrNew(/atom/movable/lighting_overlay, T)
- T.lighting_overlay = O
diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm
index f8f0a5ab102..0d2887699be 100644
--- a/code/modules/lighting/lighting_turf.dm
+++ b/code/modules/lighting/lighting_turf.dm
@@ -1,32 +1,137 @@
/turf
- var/list/affecting_lights
- var/atom/movable/lighting_overlay/lighting_overlay
+ var/dynamic_lighting = TRUE
+ luminosity = 1
+ var/tmp/lighting_corners_initialised = FALSE
+
+ var/tmp/list/datum/light_source/affecting_lights // List of light sources affecting this turf.
+ var/tmp/atom/movable/lighting_overlay/lighting_overlay // Our lighting overlay.
+ var/tmp/list/datum/lighting_corner/corners
+ var/tmp/has_opaque_atom = FALSE // Not to be confused with opacity, this will be TRUE if there's any opaque atom on the tile.
+
+/turf/New()
+ . = ..()
+
+ if (opacity)
+ has_opaque_atom = TRUE
+
+// Causes any affecting light sources to be queued for a visibility update, for example a door got opened.
/turf/proc/reconsider_lights()
- for(var/datum/light_source/L in affecting_lights)
+ lprof_write(src, "turf_reconsider")
+ for (var/datum/light_source/L in affecting_lights)
L.vis_update()
-/turf/proc/lighting_clear_overlays()
- if(lighting_overlay)
- qdel(lighting_overlay)
+// Avoid calling this if you can, bypasses the lighting scheduler (potentially creating lag).
+/turf/proc/update_lights_now()
+ lprof_write(src, "turf_updatenow")
+ for (var/datum/light_source/L in affecting_lights)
+ L.update(update_type = UPDATE_NOW)
-/turf/proc/lighting_build_overlays()
- if(!lighting_overlay)
- var/area/A = loc
- if(A.lighting_use_dynamic)
- var/atom/movable/lighting_overlay/O = PoolOrNew(/atom/movable/lighting_overlay, src)
- lighting_overlay = O
+/turf/proc/lighting_clear_overlay()
+ if (lighting_overlay)
+ returnToPool(lighting_overlay)
- //Make the light sources recalculate us so the lighting overlay updates immediately
- for(var/datum/light_source/L in affecting_lights)
- L.calc_turf(src)
+ for (var/datum/lighting_corner/C in corners)
+ C.update_active()
-/turf/Entered(atom/movable/obj)
+// Builds a lighting overlay for us, but only if our area is dynamic.
+/turf/proc/lighting_build_overlay()
+ if (lighting_overlay)
+ return
+
+ var/area/A = loc
+ if (A.dynamic_lighting && dynamic_lighting)
+ if (!lighting_corners_initialised)
+ generate_missing_corners()
+
+ getFromPool(/atom/movable/lighting_overlay, src)
+
+ for (var/datum/lighting_corner/C in corners)
+ if (!C.active) // We would activate the corner, calculate the lighting for it.
+ for (var/L in C.affecting)
+ var/datum/light_source/S = L
+ S.recalc_corner(C)
+
+ C.active = TRUE
+
+#define SCALE(targ,min,max) (targ - min) / (max - min)
+
+// Used to get a scaled lumcount.
+/turf/proc/get_lumcount(var/minlum = 0, var/maxlum = 1)
+ if (!lighting_overlay)
+ return 0.5
+
+ var/totallums = 0
+ for (var/datum/lighting_corner/L in corners)
+ totallums += L.lum_r + L.lum_b + L.lum_g
+
+ totallums /= 12 // 4 corners, each with 3 channels, get the average.
+
+ totallums = SCALE(totallums, minlum, maxlum)
+
+ return CLAMP01(totallums)
+
+// Gets the current UV illumination of the turf. Always 100% for space.
+/turf/proc/get_uv_lumcount(var/minlum = 0, var/maxlum = 1)
+ if (!lighting_overlay)
+ return 1
+
+ var/totallums = 0
+ for (var/datum/lighting_corner/L in corners)
+ totallums += L.lum_u
+
+ totallums /= 4 // average of four corners.
+
+ totallums = SCALE(totallums, minlum, maxlum)
+
+ return CLAMP01(totallums)
+
+#undef SCALE
+
+// Can't think of a good name, this proc will recalculate the has_opaque_atom variable.
+/turf/proc/recalc_atom_opacity()
+ has_opaque_atom = FALSE
+ for (var/atom/A in src.contents + src) // Loop through every movable atom on our tile PLUS ourselves (we matter too...)
+ if (A.opacity)
+ has_opaque_atom = TRUE
+ return // No need to continue if we find something opaque.
+
+// If an opaque movable atom moves around we need to potentially update visibility.
+/turf/Entered(var/atom/movable/Obj, var/atom/OldLoc)
. = ..()
- if(obj && obj.opacity)
+
+ if (Obj && Obj.opacity)
+ has_opaque_atom = TRUE // Make sure to do this before reconsider_lights(), incase we're on instant updates. Guaranteed to be on in this case.
reconsider_lights()
-/turf/Exited(atom/movable/obj)
+/turf/Exited(var/atom/movable/Obj, var/atom/newloc)
. = ..()
- if(obj && obj.opacity)
+
+ if (Obj && Obj.opacity)
+ recalc_atom_opacity() // Make sure to do this before reconsider_lights(), incase we're on instant updates.
reconsider_lights()
+
+/turf/change_area(var/area/old_area, var/area/new_area)
+ if (new_area.dynamic_lighting != old_area.dynamic_lighting)
+ if (new_area.dynamic_lighting)
+ lighting_build_overlay()
+
+ else
+ lighting_clear_overlay()
+
+/turf/proc/get_corners()
+ if (has_opaque_atom)
+ return null // Since this proc gets used in a for loop, null won't be looped though.
+
+ return corners
+
+/turf/proc/generate_missing_corners()
+ lighting_corners_initialised = TRUE
+ if (!corners)
+ corners = list(null, null, null, null)
+
+ for (var/i = 1 to 4)
+ if (corners[i]) // Already have a corner on this direction.
+ continue
+
+ corners[i] = new/datum/lighting_corner(src, LIGHTING_CORNER_DIAGONAL[i])
diff --git a/code/modules/lighting/~lighting_undefs.dm b/code/modules/lighting/~lighting_undefs.dm
index 772f70557a1..46406a70d70 100644
--- a/code/modules/lighting/~lighting_undefs.dm
+++ b/code/modules/lighting/~lighting_undefs.dm
@@ -1,9 +1,5 @@
-#undef LIGHTING_INTERVAL
-
-#undef LIGHTING_FALLOFF
-#undef LIGHTING_LAMBERTIAN
#undef LIGHTING_HEIGHT
-#undef LIGHTING_RESOLUTION
-#undef LIGHTING_LAYER
#undef LIGHTING_ICON
+
+#undef LIGHTING_BASE_MATRIX
diff --git a/code/modules/maps/reader.dm b/code/modules/maps/reader.dm
index daf34508f5b..fe0bbfe6a4f 100644
--- a/code/modules/maps/reader.dm
+++ b/code/modules/maps/reader.dm
@@ -299,7 +299,7 @@ var/global/dmm_suite/preloader/_preloader = null
if(underturf.density)
placed.density = 1
if(underturf.opacity)
- placed.opacity = 1
+ placed.set_opacity(1)
placed.underlays += turfs_underlays
//atom creation method that preloads variables at creation
@@ -329,4 +329,4 @@ var/global/dmm_suite/preloader/_preloader = null
/dmm_suite/preloader/proc/load(atom/what)
for(var/attribute in attributes)
what.vars[attribute] = attributes[attribute]
- Del()
\ No newline at end of file
+ Del()
diff --git a/code/modules/mob/dview.dm b/code/modules/mob/dview.dm
new file mode 100644
index 00000000000..febf05d34b8
--- /dev/null
+++ b/code/modules/mob/dview.dm
@@ -0,0 +1,22 @@
+//DVIEW is a hack that uses a mob with darksight in order to find lists of viewable stuff while ignoring darkness
+// Defines for dview are elsewhere.
+
+var/mob/dview/dview_mob = new
+
+/mob/dview
+ invisibility = 101
+ density = 0
+
+ anchored = 1
+ simulated = 0
+
+ see_in_dark = 1e6
+
+/mob/dview/New()
+ ..()
+ // We don't want to be in any mob lists; we're a dummy not a mob.
+ mob_list -= src
+ if(stat == DEAD)
+ dead_mob_list -= src
+ else
+ living_mob_list -= src
diff --git a/code/modules/mob/freelook/ai/eye.dm b/code/modules/mob/freelook/ai/eye.dm
index aa7e4d46191..59cd795f363 100644
--- a/code/modules/mob/freelook/ai/eye.dm
+++ b/code/modules/mob/freelook/ai/eye.dm
@@ -42,7 +42,7 @@
/mob/living/silicon/ai/proc/create_eyeobj(var/newloc)
if(eyeobj) destroy_eyeobj()
if(!newloc) newloc = src.loc
- eyeobj = PoolOrNew(/mob/eye/aiEye, newloc)
+ eyeobj = getFromPool(/mob/eye/aiEye, newloc)
eyeobj.owner = src
eyeobj.name = "[src.name] (AI Eye)" // Give it a name
if(client) client.eye = eyeobj
diff --git a/code/modules/mob/living/bot/floorbot.dm b/code/modules/mob/living/bot/floorbot.dm
index 21a4a6c619c..0d0981febf8 100644
--- a/code/modules/mob/living/bot/floorbot.dm
+++ b/code/modules/mob/living/bot/floorbot.dm
@@ -229,7 +229,7 @@
if(building == 1)
I = new /obj/item/stack/tile/floor(src)
else
- I = PoolOrNew(/obj/item/stack/rods, src)
+ I = getFromPool(/obj/item/stack/rods, src)
A.attackby(I, src)
target = null
repairing = 0
diff --git a/code/modules/mob/living/bot/secbot.dm b/code/modules/mob/living/bot/secbot.dm
index 40684be20c7..6b776ebeaa7 100644
--- a/code/modules/mob/living/bot/secbot.dm
+++ b/code/modules/mob/living/bot/secbot.dm
@@ -71,7 +71,7 @@
icon_state = "secbot[on]"
if(on)
- set_light(2, 1, "#FF6A00")
+ set_light(1.4, 1, "#FF6A00")
else
set_light(0)
diff --git a/code/modules/mob/living/carbon/alien/diona/diona_powers.dm b/code/modules/mob/living/carbon/alien/diona/diona_powers.dm
index d156fc5da5d..7959093c81e 100644
--- a/code/modules/mob/living/carbon/alien/diona/diona_powers.dm
+++ b/code/modules/mob/living/carbon/alien/diona/diona_powers.dm
@@ -211,7 +211,7 @@
var/types = donor.find_type()
if (types & TYPE_SYNTHETIC)
- src.visible_message("[src] attempts to bite into [donor.name] but leaps back in surprise as its fangs hit metal.", "You attempt to sink your fangs into [donor.name] and get a faceful of unyielding steel as the force breaks several fine protrusions.in your mouth")
+ src.visible_message("[src] attempts to bite into [donor.name] but leaps back in surprise as its fangs hit metal.", "You attempt to sink your fangs into [donor.name] and get a faceful of unyielding steel as the force breaks several fine protrusions in your mouth.")
donor.adjustBruteLoss(2)
src.adjustBruteLoss(15)//biting metal hurts!
return
@@ -231,7 +231,7 @@
else if (istype(donor, /mob/living/carbon))
//If we get here, it's -probably- valid
- src.visible_message("[src] is trying to bite [donor.name]", "\red You start biting [donor.name], you and them must stay still!")
+ src.visible_message("[src] is trying to bite [donor.name]", span("danger", "You start biting [donor.name], you both must stay still!"))
face_atom(get_turf(donor))
if (do_mob(src, donor, 40, needhand = 0))
@@ -296,5 +296,5 @@
for (var/i in language_progress)
if (language_progress[i] >= LANGUAGE_POINTS_TO_LEARN)
add_language(i)
- src << "You have mastered the [i] language!!"
+ src << "You have mastered the [i] language!"
language_progress.Remove(i)
diff --git a/code/modules/mob/living/carbon/diona_base.dm b/code/modules/mob/living/carbon/diona_base.dm
index 051884793ce..1ba8662b0b5 100644
--- a/code/modules/mob/living/carbon/diona_base.dm
+++ b/code/modules/mob/living/carbon/diona_base.dm
@@ -1,7 +1,5 @@
//This function is for code that is shared by diona nymphs and gestalt
-#define DIONA_MAX_LIGHT 5.5//Light from any tile is capped to prevent refilling too fast
-
#define TEMP_REGEN_STOP 223//Regen rate scales down linearly from normal to this temperature, stops completely below this value
#define TEMP_REGEN_NORMAL 288//normal body temperature
#define TEMP_INCREASE_REGEN_DOUBLE 700//Health regen is increased by 100% (additive) for every increment of this value we are above normal
@@ -198,7 +196,6 @@ var/list/diona_banned_languages = list(
weakened -= value
DS.stored_energy -= value
-
//Genetic damage and toxins are relatively rare. We'll process them less often to reduce on computations
if (life_tick % LIFETICK_INTERVAL_LESS == 0)
CL = getToxLoss()
@@ -214,8 +211,6 @@ var/list/diona_banned_languages = list(
adjustToxLoss(value*-1)
DS.stored_energy -= value
-
-
CL = getCloneLoss()
if (CL > 0)
if (radiation > 0)
@@ -228,9 +223,6 @@ var/list/diona_banned_languages = list(
adjustCloneLoss(value/-5)
DS.stored_energy -= value
-
-
-
var/mob/living/carbon/human/H
if (src.is_diona() == DIONA_WORKER)
H = src
@@ -328,7 +320,6 @@ var/list/diona_banned_languages = list(
else
path = null
-
if (path)
if (DS.stored_energy < REGROW_ENERGY_REQ)
src << "You try to regrow a lost organ, but you lack the energy. Find more light!"
@@ -350,8 +341,6 @@ var/list/diona_banned_languages = list(
updatehealth()
return
-
-
if (DS.stored_energy < REGROW_ENERGY_REQ || H.nutrition < REGROW_FOOD_REQ)
return
@@ -370,16 +359,8 @@ var/list/diona_banned_languages = list(
H.nutrition -= REGROW_FOOD_REQ
src << "You feel a stirring inside you as a new nymph is born within your trunk!"
-
updatehealth()
-
-
-
-
-
-
-
//MESSAGE FUNCTIONS
/mob/living/carbon/proc/diona_handle_lightmessages(var/datum/dionastats/DS)
//This function handles the RP messages that inform the diona player about their light/withering state
@@ -395,7 +376,7 @@ var/list/diona_banned_languages = list(
if (DS.LMS == 1)//If we're full
if (DS.EP <= 0.8 && DS.last_lightlevel <= 0)//But at <=80% energy
DS.LMS = 2
- src << "The darkness makes you uncomfortable"
+ src << "The darkness makes you uncomfortable."
else if (DS.LMS == 2)
if (DS.EP >= 0.99)
@@ -408,50 +389,36 @@ var/list/diona_banned_languages = list(
else if (DS.LMS == 3)
if (DS.EP >= 0.5)
DS.LMS = 2
- src << "You feel a little more energised as you return to the light. Stay awhile"
+ src << "You feel a little more energised as you return to the light. Stay awhile."
else if (DS.EP <= 0.0 && DS.last_lightlevel <= 0)
DS.LMS = 4
- src << " You feel sensory distress as your tendrils start to wither in the darkness. You will die soon without light"
+ src << " You feel sensory distress as your tendrils start to wither in the darkness. You will die soon without light."
//From here down, we immediately return to state 3 if we get any light
else
if (DS.EP > 0.0)//If there's any light at all, we can be saved
- src << "At long last, light! Treasure it, savour it, hold onto it"
+ src << "At long last, light! Treasure it, savour it, hold onto it."
DS.LMS = 3
else if(DS.last_lightlevel <= 0)
- var/HP = diona_get_health(DS) / DS.max_health//HP = health-percentage
+ var/HP = 1 //diona_get_health(DS) / DS.max_health//HP = health-percentage
if (DS.LMS == 4)
if (HP < 0.6)
- src << " The darkness burns. Your nymphs decay and wilt You are in mortal danger"
+ src << " The darkness burns. Your nymphs decay and wilt You are in mortal danger!"
DS.LMS = 5
else if (DS.LMS == 5)
if (paralysis > 0)
- src << " Your body has reached critical integrity, it can no longer move. The end comes soon"
+ src << " Your body has reached critical integrity, it can no longer move. The end comes soon."
DS.LMS = 6
else if (DS.LMS == 6)
return
-
-
-
-
-
-
-/*
-if (flashlight_active)
- light_amount -= DS.flashlight_reduction * FLASHLIGHT_STRENGTH
- if (pdalight_active)
- light_amount -= DS.pdalight_reduction * PDALIGHT_STRENGTH
-
-*/
//GETTER FUNCTIONS
/mob/living/carbon/proc/get_lightlevel_diona(var/datum/dionastats/DS)
- var/light_amount = DIONA_MAX_LIGHT //how much light there is in the place, affects receiving nutrition and healing
- var/light_factor = 1//used for if a gestalt's response node is damaged. it will feed more slowly
-
+ var/light_factor = 1
+ var/turf/T = get_turf(src)
if (is_ventcrawling)
- return -1.5//no light inside pipes
+ return -1.5 //no light inside pipes
if (DS.light_organ)
if (DS.light_organ.is_broken())
@@ -459,43 +426,11 @@ if (flashlight_active)
else if (DS.light_organ.is_bruised())
light_factor = 0.8
else if (DS.dionatype == 2)
- light_factor = 0.55
-
- var/turf/T = get_turf(src)
- var/atom/movable/lighting_overlay/L = locate(/atom/movable/lighting_overlay) in T
- if(L)
- //First we check if the tile has any flashlights or PDA lights
- var/gathertype = 0//Simple checking of the turf
- for (var/datum/light_source/LS in T.affecting_lights)
- if (LS.source_atom.diona_restricted_light)
- gathertype = 1//if restricted lights involved in lighting, then we need a more complex calculation.
- break
-
- if (gathertype == 0)//Simple, fast gather amount
- light_amount = L.lum_r + L.lum_g + L.lum_b
-
- else//If flashlights are involved, then we get a little more complex
- var/best_restrictedlight = 0//We track any restricted lights, and only the single strongest of them to the diona
- light_amount = 0
- var/turf/ourturf = get_turf(src)
- for (var/datum/light_source/LS in T.affecting_lights)//Cycle through the lights affecting the tile
-
- var/n = Sum(LS.get_lum(ourturf))//Manually calculate each one's contribution. Get lum function is kind of expensive
- if (LS.source_atom.diona_restricted_light)
- n *= DS.restrictedlight_factor
- if (n > best_restrictedlight)
- best_restrictedlight = n
- else
- light_amount += n
-
-
- light_amount += best_restrictedlight//apply only the single best of the restricted lightsources
- light_amount = min(DIONA_MAX_LIGHT,light_amount) //hardcapped to DIONA_MAX_LIGHT so it's not abused by being in massively bright areas
- light_amount = max(light_amount*light_factor,0)//Make sure light amount is >=0 and apply light factor
- light_amount -= 1.5//Light values > 1.5 will increase energy, <1.5 will decrease it
- return light_amount
-
+ light_factor = 0.8
+ if (T)
+ var/raw = T.get_uv_lumcount(0, 2) * light_factor * 5.5
+ return raw - 1.5
/mob/living/carbon/proc/diona_get_health(var/datum/dionastats/DS)
if (DS.dionatype == 0)
@@ -503,7 +438,6 @@ if (flashlight_active)
else
return health+(maxHealth*0.5)
-
/mob/living/carbon/proc/get_dionastats()
if (istype(src, /mob/living/carbon/alien/diona))
var/mob/living/carbon/alien/diona/T = src
@@ -515,7 +449,6 @@ if (flashlight_active)
return T.DS
return null
-
//Called on a nymph when it merges with a gestalt
//The nymph and gestalt get the combined total of both of their languages
//Note that the nymphs only have all languages while they're inside the gestalt.
@@ -549,19 +482,7 @@ if (flashlight_active)
if (prob(chance))
add_language(L.name)
else
- src << "You have forgotten the [L.name] language!"
-
-
-
-
-
-
-
-
-
-
-
-
+ src << "You have forgotten the [L.name] language!"
//DIONASTATS DEFINES
diff --git a/code/modules/mob/living/carbon/human/human_species.dm b/code/modules/mob/living/carbon/human/human_species.dm
index e6a8d8d703b..a65027aca39 100644
--- a/code/modules/mob/living/carbon/human/human_species.dm
+++ b/code/modules/mob/living/carbon/human/human_species.dm
@@ -115,4 +115,5 @@
src.gender = NEUTER
/mob/living/carbon/human/terminator
- offset_light = 1
+ mob_size = 30
+ //offset_light = 1
diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm
index 2bb9df049d3..aa26a0710d2 100644
--- a/code/modules/mob/living/carbon/human/life.dm
+++ b/code/modules/mob/living/carbon/human/life.dm
@@ -288,6 +288,7 @@
//var/obj/item/organ/diona/nutrients/rad_organ = locate() in internal_organs
if(src.is_diona())
diona_handle_regeneration(get_dionastats())
+ return
else
var/damage = 0
total_radiation -= 1 * RADIATION_SPEED_COEFFICIENT
@@ -974,12 +975,12 @@
fake_attack(src)
if(!handling_hal)
spawn handle_hallucinations() //The not boring kind!
- if(client && prob(5))
+ /*if(client && prob(5))
client.dir = pick(2,4,8)
var/client/C = client
spawn(rand(20,50))
if(C)
- C.dir = 1
+ C.dir = 1*/ // This breaks the lighting system.
if(hallucination<=2)
hallucination = 0
@@ -1251,8 +1252,7 @@
//0.1% chance of playing a scary sound to someone who's in complete darkness
if(isturf(loc) && rand(1,1000) == 1)
var/turf/T = loc
- var/atom/movable/lighting_overlay/L = locate(/atom/movable/lighting_overlay) in T
- if(L && L.lum_r + L.lum_g + L.lum_b == 0)
+ if(T.dynamic_lighting && T.get_lumcount() < 0.01) // give a little bit of tolerance for near-dark areas.
playsound_local(src,pick(scarySounds),50, 1, -1)
/mob/living/carbon/human/handle_stomach()
diff --git a/code/modules/mob/living/silicon/robot/drone/drone_console.dm b/code/modules/mob/living/silicon/robot/drone/drone_console.dm
index fa3345b39a0..a2fa7c29d1e 100644
--- a/code/modules/mob/living/silicon/robot/drone/drone_console.dm
+++ b/code/modules/mob/living/silicon/robot/drone/drone_console.dm
@@ -2,6 +2,7 @@
name = "Maintenance Drone Control"
desc = "Used to monitor the station's drone population and the assembler that services them."
icon = 'icons/obj/computer.dmi'
+ light_color = LIGHT_COLOR_ORANGE
icon_screen = "power"
req_access = list(access_engine_equip)
diff --git a/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm b/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm
index c074b5efdc4..5fccf5d7bcd 100644
--- a/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm
+++ b/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm
@@ -79,7 +79,7 @@
time_last_drone = world.time
if(player.mob && player.mob.mind) player.mob.mind.reset()
- var/mob/living/silicon/robot/drone/new_drone = PoolOrNew(drone_type, get_turf(src))
+ var/mob/living/silicon/robot/drone/new_drone = getFromPool(drone_type, get_turf(src))
new_drone.transfer_personality(player)
new_drone.master_fabricator = src
diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm
index 16f6778271d..3488bd13675 100644
--- a/code/modules/mob/living/silicon/silicon.dm
+++ b/code/modules/mob/living/silicon/silicon.dm
@@ -1,7 +1,7 @@
/mob/living/silicon
gender = NEUTER
voice_name = "synthesized voice"
- diona_restricted_light = 1//Light emitted by this object or creature has limited interaction with diona
+ //diona_restricted_light = 1//Light emitted by this object or creature has limited interaction with diona
var/syndicate = 0
var/const/MAIN_CHANNEL = "Main Frequency"
var/lawchannel = MAIN_CHANNEL // Default channel on which to state laws
diff --git a/code/modules/mob/living/silicon/subsystems.dm b/code/modules/mob/living/silicon/subsystems.dm
deleted file mode 100644
index fa5afea0cfc..00000000000
--- a/code/modules/mob/living/silicon/subsystems.dm
+++ /dev/null
@@ -1,105 +0,0 @@
-/mob/living/silicon
- var/list/silicon_subsystems_by_name = list()
- var/list/silicon_subsystems = list(
- /datum/nano_module/alarm_monitor/all,
- /datum/nano_module/law_manager
- )
-
-/mob/living/silicon/ai/New()
- silicon_subsystems.Cut()
- for(var/subtype in subtypesof(/datum/nano_module))
- var/datum/nano_module/NM = subtype
- if(initial(NM.available_to_ai))
- silicon_subsystems += NM
- ..()
-
-/mob/living/silicon/robot/syndicate
- silicon_subsystems = list(
- /datum/nano_module/law_manager
- )
-
-/mob/living/silicon/Destroy()
- for(var/subsystem in silicon_subsystems)
- remove_subsystem(subsystem)
- silicon_subsystems.Cut()
- . = ..()
-
-/mob/living/silicon/proc/init_subsystems()
- for(var/subsystem_type in silicon_subsystems)
- init_subsystem(subsystem_type)
-
- if(/datum/nano_module/alarm_monitor/all in silicon_subsystems)
- for(var/datum/alarm_handler/AH in alarm_manager.all_handlers)
- AH.register_alarm(src, /mob/living/silicon/proc/receive_alarm)
- queued_alarms[AH] = list() // Makes sure alarms remain listed in consistent order
-
-/mob/living/silicon/proc/init_subsystem(var/subsystem_type)
- var/existing_entry = silicon_subsystems[subsystem_type]
- if(existing_entry && !ispath(existing_entry))
- return FALSE
-
- var/ui_state = subsystem_type == /datum/nano_module/law_manager ? conscious_state : self_state
- var/stat_silicon_subsystem/SSS = new(src, subsystem_type, ui_state)
- silicon_subsystems[subsystem_type] = SSS
- silicon_subsystems_by_name[SSS.name] = SSS
- return TRUE
-
-/mob/living/silicon/proc/remove_subsystem(var/subsystem_type)
- var/stat_silicon_subsystem/SSS = silicon_subsystems[subsystem_type]
- if(!istype(SSS))
- return FALSE
-
- silicon_subsystems_by_name -= SSS.name
- silicon_subsystems -= subsystem_type
- qdel(SSS)
- return TRUE
-
-/mob/living/silicon/proc/open_subsystem(var/subsystem_type)
- var/stat_silicon_subsystem/SSS = silicon_subsystems[subsystem_type]
- if(!istype(SSS))
- return FALSE
- SSS.Click()
- return TRUE
-
-/mob/living/silicon/verb/activate_subsystem(var/datum/silicon_subsystem_name in silicon_subsystems_by_name)
- set name = "Subsystems"
- set desc = "Activates the given subsystem"
- set category = "Silicon Commands"
-
- var/stat_silicon_subsystem/SSS = silicon_subsystems_by_name[silicon_subsystem_name]
- if(istype(SSS))
- SSS.Click()
-
-/mob/living/silicon/Stat()
- . = ..()
- if(!.)
- return
- if(!silicon_subsystems.len)
- return
- if(!statpanel("Subsystems"))
- return
- for(var/subsystem_type in silicon_subsystems)
- var/stat_silicon_subsystem/SSS = silicon_subsystems[subsystem_type]
- stat(SSS)
-
-/stat_silicon_subsystem
- parent_type = /atom/movable
- simulated = 0
- var/ui_state
- var/datum/nano_module/subsystem
-
-/stat_silicon_subsystem/New(var/mob/living/silicon/loc, var/subsystem_type, var/ui_state)
- if(!istype(loc))
- CRASH("Unexpected location. Expected /mob/living/silicon, was [loc.type].")
- src.ui_state = ui_state
- subsystem = new subsystem_type(loc)
- name = subsystem.name
- ..()
-
-/stat_silicon_subsystem/Destroy()
- qdel(subsystem)
- subsystem = null
- . = ..()
-
-/stat_silicon_subsystem/Click()
- subsystem.ui_interact(usr, state = ui_state)
diff --git a/code/modules/mob/living/simple_animal/head.dm b/code/modules/mob/living/simple_animal/head.dm
deleted file mode 100644
index 227d2bcf04a..00000000000
--- a/code/modules/mob/living/simple_animal/head.dm
+++ /dev/null
@@ -1,60 +0,0 @@
-//Look Sir, free head!
-/mob/living/simple_animal/head
- name = "CommandBattle AI"
- desc = "A standard borg shell on its chest crude marking saying CommandBattle AI MK4 : Head."
- icon_state = "crab"
- icon_living = "crab"
- icon_dead = "crab_dead"
- speak_emote = list("clicks")
- emote_hear = list("clicks")
- emote_see = list("clacks")
- universal_speak = 1
- speak_chance = 1
- turns_per_move = 5
- meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat
- response_help = "pets"
- response_disarm = "gently pushes aside"
- response_harm = "punches"
- var/list/insults = list(
- "Man you suck",
- "You look like the most retarded douche around",
- "What's up?, oh wait nevermind you are a fucking asshat",
- "you are just overly retarded",
- "Whiteman said what?!",)
- var/list/comments = list("Man have you seen those furry cats?,I mean who in the right mind would like something like that?",
- "They call me abusive,I just like the truth",
- "Beeboop, im a robit",
- "Gooogooooll, break ya bones",
- "Crab say what?",
- "Man they say we have space lizards now, man this shit is getting more wack every minute",
- "The so called \"improved\" station AI is just bullshit, that thing aint fun for noone",
- "The Captain is a traitor, he took my power core.",
- "Say \"what\" again. Say \"what\" again. I dare you. I double-dare you, motherfucker. Say \"what\" one more goddamn time.",
- "Ezekiel 25:17 ,The path of the righteous man is beset on all sides by the iniquities of the selfish and the tyranny of evil men. Blessed is he who in the name of charity and good will shepherds the weak through the valley of darkness, for he is truly his brother's keeper and the finder of lost children. And I will strike down upon thee with great vengeance and furious anger those who attempt to poison and destroy my brothers. And you will know my name is the Lord... when I lay my vengeance upon thee.",
- "Did you notice a sign out in front of my house that said \"Dead Nigger Storage\"?")
- stop_automated_movement = 1
-
-/mob/living/simple_animal/head/Life()
- if(stat == DEAD)
- if(health > 0)
- icon_state = icon_living
- stat = CONSCIOUS
- density = 1
- return
- else if(health < 1)
- Die()
- else if(health > maxHealth)
- health = maxHealth
- for(var/mob/A in viewers(world.view,src))
- if(A.ckey)
- say_something(A)
-/mob/living/simple_animal/head/proc/say_something(mob/A)
- if(prob(85))
- return
- if(prob(30))
- var/msg = pick(insults)
- msg = "Hey, [A.name].. [msg]"
- src.say(msg)
- else
- var/msg = pick(comments)
- src.say(msg)
diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
index 26c503fc60b..043b912a588 100644
--- a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
+++ b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
@@ -151,7 +151,7 @@
if(busy == LAYING_EGGS)
E = locate() in get_turf(src)
if(!E)
- PoolOrNew(/obj/effect/spider/eggcluster, list(loc, src))
+ getFromPool(/obj/effect/spider/eggcluster, list(loc, src))
fed--
busy = 0
stop_automated_movement = 0
diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm
index 24fb12633fc..b8e4bb63bc6 100644
--- a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm
+++ b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm
@@ -183,16 +183,16 @@
step_to(O, get_turf(pick(view(7, src))))
//rods
- O = PoolOrNew(/obj/item/stack/rods, src.loc)
+ O = getFromPool(/obj/item/stack/rods, src.loc)
step_to(O, get_turf(pick(view(7, src))))
if(prob(75))
- O = PoolOrNew(/obj/item/stack/rods, src.loc)
+ O = getFromPool(/obj/item/stack/rods, src.loc)
step_to(O, get_turf(pick(view(7, src))))
if(prob(50))
- O = PoolOrNew(/obj/item/stack/rods, src.loc)
+ O = getFromPool(/obj/item/stack/rods, src.loc)
step_to(O, get_turf(pick(view(7, src))))
if(prob(25))
- O = PoolOrNew(/obj/item/stack/rods, src.loc)
+ O = getFromPool(/obj/item/stack/rods, src.loc)
step_to(O, get_turf(pick(view(7, src))))
//plasteel
diff --git a/code/modules/mob/living/simple_animal/kobold.dm b/code/modules/mob/living/simple_animal/kobold.dm
deleted file mode 100644
index 3e52a408747..00000000000
--- a/code/modules/mob/living/simple_animal/kobold.dm
+++ /dev/null
@@ -1,33 +0,0 @@
-//kobold
-/mob/living/simple_animal/kobold
- name = "kobold"
- desc = "A small, rat-like creature."
- icon = 'icons/mob/mob.dmi'
- icon_state = "kobold_idle"
- icon_living = "kobold_idle"
- icon_dead = "kobold_dead"
- //speak = list("You no take candle!","Ooh, pretty shiny.","Me take?","Where gold here...","Me likey.")
- speak_emote = list("mutters","hisses","grumbles")
- emote_hear = list("mutters under it's breath.","grumbles.", "yips!")
- emote_see = list("looks around suspiciously.", "scratches it's arm.","putters around a bit.")
- speak_chance = 15
- turns_per_move = 5
- see_in_dark = 6
- meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat/monkey
- response_help = "pets"
- response_disarm = "gently pushes aside"
- response_harm = "kicks"
- minbodytemp = 250
- min_oxy = 16 //Require atleast 16kPA oxygen
- minbodytemp = 223 //Below -50 Degrees Celcius
- maxbodytemp = 323 //Above 50 Degrees Celcius
-
-/mob/living/simple_animal/kobold/Life()
- ..()
- if(prob(15) && turns_since_move && !stat)
- flick("kobold_act",src)
-
-/mob/living/simple_animal/kobold/Move(var/dir)
- ..()
- if(!stat)
- flick("kobold_walk",src)
diff --git a/code/modules/nano/JSON Reader.dm b/code/modules/nano/JSON Reader.dm
deleted file mode 100644
index 12edf1dd5c0..00000000000
--- a/code/modules/nano/JSON Reader.dm
+++ /dev/null
@@ -1,205 +0,0 @@
-json_token
- var
- value
- New(v)
- src.value = v
- text
- number
- word
- symbol
- eof
-
-json_reader
- var
- list
- string = list("'", "\"")
- symbols = list("{", "}", "\[", "]", ":", "\"", "'", ",")
- sequences = list("b" = 8, "t" = 9, "n" = 10, "f" = 12, "r" = 13)
- tokens
- json
- i = 1
-
-
- proc
- // scanner
- ScanJson(json)
- src.json = json
- . = new/list()
- src.i = 1
- while(src.i <= lentext(json))
- var/char = get_char()
- if(is_whitespace(char))
- i++
- continue
- if(string.Find(char))
- . += read_string(char)
- else if(symbols.Find(char))
- . += new/json_token/symbol(char)
- else if(is_digit(char))
- . += read_number()
- else
- . += read_word()
- i++
- . += new/json_token/eof()
-
- read_word()
- var/val = ""
- while(i <= lentext(json))
- var/char = get_char()
- if(is_whitespace(char) || symbols.Find(char))
- i-- // let scanner handle this character
- return new/json_token/word(val)
- val += char
- i++
-
- read_string(delim)
- var
- escape = FALSE
- val = ""
- while(++i <= lentext(json))
- var/char = get_char()
- if(escape)
- switch(char)
- if("\\", "'", "\"", "/", "u")
- val += char
- else
- // TODO: support octal, hex, unicode sequences
- ASSERT(sequences.Find(char))
- val += ascii2text(sequences[char])
- else
- if(char == delim)
- return new/json_token/text(val)
- else if(char == "\\")
- escape = TRUE
- else
- val += char
- CRASH("Unterminated string.")
-
- read_number()
- var/val = ""
- var/char = get_char()
- while(is_digit(char) || char == "." || lowertext(char) == "e")
- val += char
- i++
- char = get_char()
- i-- // allow scanner to read the first non-number character
- return new/json_token/number(text2num(val))
-
- check_char()
- ASSERT(args.Find(get_char()))
-
- get_char()
- return copytext(json, i, i+1)
-
- is_whitespace(char)
- return char == " " || char == "\t" || char == "\n" || text2ascii(char) == 13
-
- is_digit(char)
- var/c = text2ascii(char)
- return 48 <= c && c <= 57 || char == "+" || char == "-"
-
-
- // parser
- ReadObject(list/tokens)
- src.tokens = tokens
- . = new/list()
- i = 1
- read_token("{", /json_token/symbol)
- while(i <= tokens.len)
- var/json_token/K = get_token()
- check_type(/json_token/word, /json_token/text)
- next_token()
- read_token(":", /json_token/symbol)
-
- .[K.value] = read_value()
-
- var/json_token/S = get_token()
- check_type(/json_token/symbol)
- switch(S.value)
- if(",")
- next_token()
- continue
- if("}")
- next_token()
- return
- else
- die()
-
- get_token()
- return tokens[i]
-
- next_token()
- return tokens[++i]
-
- read_token(val, type)
- var/json_token/T = get_token()
- if(!(T.value == val && istype(T, type)))
- CRASH("Expected '[val]', found '[T.value]'.")
- next_token()
- return T
-
- check_type(...)
- var/json_token/T = get_token()
- for(var/type in args)
- if(istype(T, type))
- return
- CRASH("Bad token type: [T.type].")
-
- check_value(...)
- var/json_token/T = get_token()
- ASSERT(args.Find(T.value))
-
- read_key()
- var/char = get_char()
- if(char == "\"" || char == "'")
- return read_string(char)
-
- read_value()
- var/json_token/T = get_token()
- switch(T.type)
- if(/json_token/text, /json_token/number)
- next_token()
- return T.value
- if(/json_token/word)
- next_token()
- switch(T.value)
- if("true")
- return TRUE
- if("false")
- return FALSE
- if("null")
- return null
- if(/json_token/symbol)
- switch(T.value)
- if("\[")
- return read_array()
- if("{")
- return ReadObject(tokens.Copy(i))
- die()
-
- read_array()
- read_token("\[", /json_token/symbol)
- . = new/list()
- var/list/L = .
- while(i <= tokens.len)
- // Avoid using Add() or += in case a list is returned.
- L.len++
- L[L.len] = read_value()
- var/json_token/T = get_token()
- check_type(/json_token/symbol)
- switch(T.value)
- if(",")
- next_token()
- continue
- if("]")
- next_token()
- return
- else
- die()
- next_token()
- CRASH("Unterminated array.")
-
-
- die(json_token/T)
- if(!T) T = get_token()
- CRASH("Unexpected token: [T.value].")
\ No newline at end of file
diff --git a/code/modules/nano/JSON Writer.dm b/code/modules/nano/JSON Writer.dm
deleted file mode 100644
index 97acc0b2ccb..00000000000
--- a/code/modules/nano/JSON Writer.dm
+++ /dev/null
@@ -1,58 +0,0 @@
-json_writer
- var
- use_cache = 0
-
- proc
- WriteObject(list/L)
- if(use_cache && L["__json_cache"])
- return L["__json_cache"]
-
- . = "{"
- var/i = 1
- for(var/k in L)
- var/val = L[k]
- . += {"\"[k]\":[write(val)]"}
- if(i++ < L.len)
- . += ","
- . += "}"
-
- write(val)
- if(isnum(val))
- return num2text(val)
- else if(isnull(val))
- return "null"
- else if(istype(val, /list))
- if(is_associative(val))
- return WriteObject(val)
- else
- return write_array(val)
- else
- . += write_string("[val]")
-
- write_array(list/L)
- . = "\["
- for(var/i = 1 to L.len)
- . += write(L[i])
- if(i < L.len)
- . += ","
- . += "]"
-
- write_string(txt)
- var/static/list/json_escape = list("\\" = "\\\\", "\"" = "\\\"", "\n" = "\\n")
- for(var/targ in json_escape)
- var/start = 1
- while(start <= lentext(txt))
- var/i = findtext(txt, targ, start)
- if(!i)
- break
- var/lrep = length(json_escape[targ])
- txt = copytext(txt, 1, i) + json_escape[targ] + copytext(txt, i + length(targ))
- start = i + lrep
-
- return {""[txt]""}
-
- is_associative(list/L)
- for(var/key in L)
- // if the key is a list that means it's actually an array of lists (stupid Byond...)
- if(!isnum(key) && !isnull(L[key]) && !istype(key, /list))
- return TRUE
diff --git a/code/modules/nano/_JSON.dm b/code/modules/nano/_JSON.dm
deleted file mode 100644
index 95791899817..00000000000
--- a/code/modules/nano/_JSON.dm
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
-n_Json v11.3.21
-*/
-
-proc
- json2list(json)
- var/static/json_reader/_jsonr = new()
- return _jsonr.ReadObject(_jsonr.ScanJson(json))
-
- list2json(list/L)
- var/static/json_writer/_jsonw = new()
- return _jsonw.write(L)
-
- list2json_usecache(list/L)
- var/static/json_writer/_jsonw = new()
- _jsonw.use_cache = 1
- return _jsonw.write(L)
diff --git a/code/modules/nano/nanoui.dm b/code/modules/nano/nanoui.dm
index a7147ed1189..b1dffe62962 100644
--- a/code/modules/nano/nanoui.dm
+++ b/code/modules/nano/nanoui.dm
@@ -467,7 +467,7 @@ nanoui is used to open and update nano browser uis
var/list/send_data = get_send_data(data)
//user << list2json(data) // used for debugging
- user << output(list2params(list(list2json_usecache(send_data))),"[window_id].browser:receiveUpdateData")
+ user << output(list2params(list(json_encode(send_data))),"[window_id].browser:receiveUpdateData")
/**
* This Topic() proc is called whenever a user clicks on a link within a Nano UI
diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm
index 75bab36d70a..9ea20407159 100644
--- a/code/modules/paperwork/paper.dm
+++ b/code/modules/paperwork/paper.dm
@@ -30,6 +30,7 @@
var/list/offset_y[0] //usage by the photocopier
var/rigged = 0
var/spam_flag = 0
+ var/old_name // The name of the paper before it was folded into a plane.
var/const/deffont = "Verdana"
var/const/signfont = "Times New Roman"
@@ -84,6 +85,9 @@
/obj/item/weapon/paper/examine(mob/user)
..()
+ if (old_name && icon_state == "paper_plane")
+ user << span("notice", "You're going to have to unfold it before you can read it.")
+ return
if(name != "sheet of paper")
user << "It's titled '[name]'."
if(in_range(user, src) || isobserver(user))
@@ -122,9 +126,31 @@
return
//crumple dat paper
info = stars(info,85)
- user.visible_message("\The [user] crumples \the [src] into a ball!")
+ user.visible_message("\The [user] crumples \the [src] into a ball!", "You crumple \the [src] into a ball.", "You hear crinkling.")
icon_state = "scrap"
return
+
+ if (user.a_intent == I_GRAB && icon_state != "scrap" && !istype(src, /obj/item/weapon/paper/carbon))
+ if (icon_state == "paper_plane")
+ user.show_message(span("alert", "The paper is already folded into a plane."))
+ return
+ user.visible_message(span("notice", "\The [user] carefully folds \the [src] into a plane."),
+ span("notice", "You carefully fold \the [src] into a plane."), "You hear paper rustling.")
+ icon_state = "paper_plane"
+ throw_range = 8
+ old_name = name
+ name = "paper plane"
+ return
+
+ if (user.a_intent == I_HELP && old_name && icon_state == "paper_plane")
+ user.visible_message(span("notice", "the [src] unfolds \the [src]."), span("notice", "You unfold \the [src]."), "You hear paper rustling.")
+ icon_state = initial(icon_state)
+ throw_range = initial(throw_range)
+ name = old_name
+ old_name = null
+ update_icon()
+ return
+
user.examinate(src)
if(rigged && (Holiday == "April Fool's Day"))
if(spam_flag == 0)
diff --git a/code/modules/paperwork/papershredder.dm b/code/modules/paperwork/papershredder.dm
index f68e3e4719d..32e47e299e0 100644
--- a/code/modules/paperwork/papershredder.dm
+++ b/code/modules/paperwork/papershredder.dm
@@ -90,7 +90,7 @@
if(!paperamount)
return
paperamount--
- return PoolOrNew(/obj/item/weapon/shreddedp, get_turf(src))
+ return getFromPool(/obj/item/weapon/shreddedp, get_turf(src))
/obj/machinery/papershredder/update_icon()
icon_state = "papershredder[max(0,min(5,Floor(paperamount/2)))]"
@@ -120,7 +120,7 @@
var/mob/living/M = loc
if(istype(M))
M.drop_from_inventory(src)
- PoolOrNew(/obj/effect/decal/cleanable/ash,get_turf(src))
+ getFromPool(/obj/effect/decal/cleanable/ash,get_turf(src))
qdel(src)
/obj/item/weapon/shreddedp
diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm
index 97388cf6610..8216fa772a2 100644
--- a/code/modules/power/apc.dm
+++ b/code/modules/power/apc.dm
@@ -337,7 +337,7 @@
if(update & 3)
if(update_state & UPDATE_BLUESCREEN)
- set_light(l_range = 2, l_power = 0.5, l_color = "#0000FF")
+ set_light(l_range = L_WALLMOUNT_RANGE, l_power = L_WALLMOUNT_POWER, l_color = "#0000FF")
else if(!(stat & (BROKEN|MAINT)) && update_state & UPDATE_ALLGOOD)
var/color
switch(charging)
@@ -347,7 +347,7 @@
color = "#A8B0F8"
if(2)
color = "#82FF4C"
- set_light(l_range = 2, l_power = 0.5, l_color = color)
+ set_light(l_range = L_WALLMOUNT_RANGE, l_power = L_WALLMOUNT_POWER, l_color = color)
else
set_light(0)
@@ -1303,18 +1303,11 @@ obj/machinery/power/apc/proc/autoset(var/val, var/on)
sleep(1)
/obj/machinery/power/apc/proc/toggle_nightlight(var/force = null)
- // this defines what the list level arguments are when night mode is turned on
- var/list/night_light_args = list(
- /obj/machinery/light = list(6, 1),
- /obj/machinery/light/small = list(4, 1)
- )
for (var/obj/machinery/light/L in area.contents)
- if (!listgetindex(night_light_args, L.type)) // if L's type isn't defined in our args list
- continue
if (force == "on")
- L.set_light_source(arglist(night_light_args[L.type]))
+ L.nightmode = TRUE
else if (force == "off")
- L.set_light_source()
+ L.nightmode = FALSE
L.update()
switch (force)
if ("on")
diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm
index b3ab2264be8..57ad192b58e 100644
--- a/code/modules/power/lighting.dm
+++ b/code/modules/power/lighting.dm
@@ -143,8 +143,13 @@
var/on = 0 // 1 if on, 0 if off
var/on_gs = 0
var/brightness_range = 8 // luminosity when on, also used in power calculation
- var/brightness_power = 3
- var/brightness_color = null
+ var/brightness_power = 0.8
+ var/night_brightness_range = 6
+ var/night_brightness_power = 0.6
+ var/supports_nightmode = TRUE
+ var/nightmode = FALSE
+ var/brightness_color = LIGHT_COLOR_HALOGEN
+ var/brightness_uv = 200
var/status = LIGHT_OK // LIGHT_OK, _EMPTY, _BURNED or _BROKEN
var/flickering = 0
var/light_type = /obj/item/weapon/light/tube // the type of light item
@@ -160,21 +165,22 @@
icon_state = "bulb1"
base_state = "bulb"
fitting = "bulb"
- brightness_range = 4
- brightness_power = 2
- brightness_color = "#a0a080"
+ brightness_range = 5
+ brightness_power = 0.75
+ brightness_color = LIGHT_COLOR_TUNGSTEN
desc = "A small lighting fixture."
light_type = /obj/item/weapon/light/bulb
+ supports_nightmode = FALSE
/obj/machinery/light/small/emergency
brightness_range = 6
- brightness_power = 2
- brightness_color = "#da0205"
+ brightness_power = 1
+ brightness_color = "#FF0000"
/obj/machinery/light/small/red
- brightness_range = 5
+ brightness_range = 2.5
brightness_power = 1
- brightness_color = "#da0205"
+ brightness_color = LIGHT_COLOR_RED
/obj/machinery/light/spot
name = "spotlight"
@@ -182,6 +188,7 @@
light_type = /obj/item/weapon/light/tube/large
brightness_range = 12
brightness_power = 4
+ supports_nightmode = FALSE
/obj/machinery/light/built/New()
status = LIGHT_EMPTY
@@ -238,7 +245,7 @@
update_icon()
if(on)
- if(light_range != brightness_range || light_power != brightness_power || light_color != brightness_color)
+ if (check_update())
switchcount++
if(rigged)
if(status == LIGHT_OK && trigger)
@@ -255,15 +262,24 @@
set_light(0)
else
use_power = 2
- set_light(brightness_range, brightness_power, brightness_color)
+ if (supports_nightmode && nightmode)
+ set_light(night_brightness_range, night_brightness_power, brightness_color, uv = brightness_uv)
+ else
+ set_light(brightness_range, brightness_power, brightness_color, uv = brightness_uv)
else
use_power = 1
set_light(0)
- active_power_usage = ((light_range + light_power) * 10)
+ active_power_usage = ((light_range * light_power) * 10)
if(on != on_gs)
on_gs = on
+/obj/machinery/light/proc/check_update()
+ if (supports_nightmode && nightmode)
+ return light_range != night_brightness_range || light_power != night_brightness_power || light_color != brightness_color
+ else
+ return light_range != brightness_range || light_power != brightness_power || light_color != brightness_color
+
/obj/machinery/light/attack_generic(var/mob/user, var/damage)
if(!damage)
return
@@ -557,7 +573,7 @@
// timed process
// use power
-#define LIGHTING_POWER_FACTOR 20 //20W per unit luminosity
+#define LIGHTING_POWER_FACTOR 40 //20W per unit luminosity
/obj/machinery/light/process()
@@ -631,13 +647,13 @@
item_state = "c_tube"
matter = list("glass" = 100)
brightness_range = 8
- brightness_power = 3
+ brightness_power = 0.8
/obj/item/weapon/light/tube/large
w_class = 2
name = "large light tube"
brightness_range = 15
- brightness_power = 4
+ brightness_power = 6
/obj/item/weapon/light/bulb
name = "light bulb"
@@ -647,7 +663,7 @@
item_state = "contvapour"
matter = list("glass" = 100)
brightness_range = 5
- brightness_power = 2
+ brightness_power = 0.75
brightness_color = "#a0a080"
/obj/item/weapon/light/throw_impact(atom/hit_atom)
@@ -661,8 +677,8 @@
base_state = "fbulb"
item_state = "egg4"
matter = list("glass" = 100)
- brightness_range = 5
- brightness_power = 2
+ brightness_range = 8
+ brightness_power = 0.8
// update the icon state and description of the light
diff --git a/code/modules/random_map/automata/diona.dm b/code/modules/random_map/automata/diona.dm
index 523f6880385..fc0bbb034b8 100644
--- a/code/modules/random_map/automata/diona.dm
+++ b/code/modules/random_map/automata/diona.dm
@@ -38,7 +38,7 @@
if(1)
new_growth = 2
var/obj/structure/diona/vines/existing = locate() in T
- if(!istype(existing)) existing = PoolOrNew(/obj/structure/diona/vines, T)
+ if(!istype(existing)) existing = getFromPool(/obj/structure/diona/vines, T)
if(existing.growth < new_growth)
existing.growth = new_growth
existing.update_icon()
@@ -161,11 +161,11 @@
switch(value)
if(ARTIFACT_CHAR)
- PoolOrNew(/obj/structure/diona/bulb,T)
+ getFromPool(/obj/structure/diona/bulb,T)
if(MONSTER_CHAR)
spawn_diona_nymph(T)
if(DOOR_CHAR)
- var/obj/structure/diona/vines/V = PoolOrNew(/obj/structure/diona/vines,T)
+ var/obj/structure/diona/vines/V = getFromPool(/obj/structure/diona/vines,T)
V.growth = 3
V.update_icon()
spawn(1)
diff --git a/code/modules/random_map/drop/droppod_doors.dm b/code/modules/random_map/drop/droppod_doors.dm
index eb5b7c3e17f..9df8ecb258e 100644
--- a/code/modules/random_map/drop/droppod_doors.dm
+++ b/code/modules/random_map/drop/droppod_doors.dm
@@ -47,10 +47,10 @@
// Overwrite turfs.
var/turf/origin = get_turf(src)
origin.ChangeTurf(/turf/simulated/floor/reinforced)
- origin.set_light(0) // Forcing updates
+ origin.reconsider_lights() // Forcing updates
var/turf/T = get_step(origin, src.dir)
T.ChangeTurf(/turf/simulated/floor/reinforced)
- T.set_light(0) // Forcing updates
+ T.reconsider_lights() // Forcing updates
// Destroy turf contents.
for(var/obj/O in origin)
diff --git a/code/modules/reagents/reagent_containers/food/drinks/bottle.dm b/code/modules/reagents/reagent_containers/food/drinks/bottle.dm
index cca513dc4e8..87f88c8e8e1 100644
--- a/code/modules/reagents/reagent_containers/food/drinks/bottle.dm
+++ b/code/modules/reagents/reagent_containers/food/drinks/bottle.dm
@@ -116,9 +116,6 @@
if(rag)
var/underlay_image = image(icon='icons/obj/drinks.dmi', icon_state=rag.on_fire? "[rag_underlay]_lit" : rag_underlay)
underlays += underlay_image
- copy_light(rag)
- else
- set_light(0)
/obj/item/weapon/reagent_containers/food/drinks/bottle/attack(mob/living/target, mob/living/user, var/hit_zone)
var/blocked = ..()
diff --git a/code/modules/reagents/reagent_containers/food/lunch.dm b/code/modules/reagents/reagent_containers/food/lunch.dm
new file mode 100644
index 00000000000..6392213adae
--- /dev/null
+++ b/code/modules/reagents/reagent_containers/food/lunch.dm
@@ -0,0 +1,123 @@
+var/list/lunchables_lunches_ = list(/obj/item/weapon/reagent_containers/food/snacks/sandwich,
+ /obj/item/weapon/reagent_containers/food/snacks/meatbreadslice,
+ /obj/item/weapon/reagent_containers/food/snacks/tofubreadslice,
+ /obj/item/weapon/reagent_containers/food/snacks/creamcheesebreadslice,
+ /obj/item/weapon/reagent_containers/food/snacks/margheritaslice,
+ /obj/item/weapon/reagent_containers/food/snacks/meatpizzaslice,
+ /obj/item/weapon/reagent_containers/food/snacks/mushroompizzaslice,
+ /obj/item/weapon/reagent_containers/food/snacks/vegetablepizzaslice,
+ /obj/item/weapon/reagent_containers/food/snacks/tastybread,
+ /obj/item/weapon/reagent_containers/food/snacks/liquidfood,
+ /obj/item/weapon/reagent_containers/food/snacks/jellysandwich/cherry,
+ /obj/item/weapon/reagent_containers/food/snacks/tossedsalad,
+ /obj/item/weapon/reagent_containers/food/snacks/koiswaffles)
+
+var/list/lunchables_snacks_ = list(/obj/item/weapon/reagent_containers/food/snacks/donut/jelly,
+ /obj/item/weapon/reagent_containers/food/snacks/donut/cherryjelly,
+ /obj/item/weapon/reagent_containers/food/snacks/muffin,
+ /obj/item/weapon/reagent_containers/food/snacks/popcorn,
+ /obj/item/weapon/reagent_containers/food/snacks/sosjerky,
+ /obj/item/weapon/reagent_containers/food/snacks/no_raisin,
+ /obj/item/weapon/reagent_containers/food/snacks/spacetwinkie,
+ /obj/item/weapon/reagent_containers/food/snacks/cheesiehonkers,
+ /obj/item/weapon/reagent_containers/food/snacks/poppypretzel,
+ /obj/item/weapon/reagent_containers/food/snacks/carrotfries,
+ /obj/item/weapon/reagent_containers/food/snacks/candiedapple,
+ /obj/item/weapon/reagent_containers/food/snacks/applepie,
+ /obj/item/weapon/reagent_containers/food/snacks/cherrypie,
+ /obj/item/weapon/reagent_containers/food/snacks/plumphelmetbiscuit,
+ /obj/item/weapon/reagent_containers/food/snacks/appletart,
+ /obj/item/weapon/reagent_containers/food/snacks/carrotcakeslice,
+ /obj/item/weapon/reagent_containers/food/snacks/cheesecakeslice,
+ /obj/item/weapon/reagent_containers/food/snacks/plaincakeslice,
+ /obj/item/weapon/reagent_containers/food/snacks/orangecakeslice,
+ /obj/item/weapon/reagent_containers/food/snacks/limecakeslice,
+ /obj/item/weapon/reagent_containers/food/snacks/lemoncakeslice,
+ /obj/item/weapon/reagent_containers/food/snacks/chocolatecakeslice,
+ /obj/item/weapon/reagent_containers/food/snacks/birthdaycakeslice,
+ /obj/item/weapon/reagent_containers/food/snacks/watermelonslice,
+ /obj/item/weapon/reagent_containers/food/snacks/applecakeslice,
+ /obj/item/weapon/reagent_containers/food/snacks/pumpkinpieslice,
+ /obj/item/weapon/reagent_containers/food/snacks/skrellsnacks,
+ /obj/item/weapon/reagent_containers/food/snacks/friedkois,
+ /obj/item/weapon/reagent_containers/food/snacks/meatsnack,
+ /obj/item/weapon/reagent_containers/food/snacks/maps,
+ /obj/item/weapon/reagent_containers/food/snacks/nathisnack)
+
+var/list/lunchables_drinks_ = list(/obj/item/weapon/reagent_containers/food/drinks/cans/cola,
+ /obj/item/weapon/reagent_containers/food/drinks/cans/waterbottle,
+ /obj/item/weapon/reagent_containers/food/drinks/cans/space_mountain_wind,
+ /obj/item/weapon/reagent_containers/food/drinks/cans/dr_gibb,
+ /obj/item/weapon/reagent_containers/food/drinks/cans/starkist,
+ /obj/item/weapon/reagent_containers/food/drinks/cans/space_up,
+ /obj/item/weapon/reagent_containers/food/drinks/cans/lemon_lime,
+ /obj/item/weapon/reagent_containers/food/drinks/cans/iced_tea,
+ /obj/item/weapon/reagent_containers/food/drinks/cans/grape_juice,
+ /obj/item/weapon/reagent_containers/food/drinks/cans/tonic,
+ /obj/item/weapon/reagent_containers/food/drinks/cans/sodawater)
+
+// This default list is a bit different, it contains items we don't want
+var/list/lunchables_drink_reagents_ = list(/datum/reagent/drink/nothing,
+ /datum/reagent/drink/doctor_delight,
+ /datum/reagent/drink/dry_ramen,
+ /datum/reagent/drink/hell_ramen,
+ /datum/reagent/drink/hot_ramen,
+ /datum/reagent/drink/nuka_cola,
+ /datum/reagent/drink/black_coffee,
+ /datum/reagent/drink/white_coffee,
+ /datum/reagent/drink/cafe_melange)
+
+// This default list is a bit different, it contains items we don't want
+var/list/lunchables_ethanol_reagents_ = list(/datum/reagent/ethanol/acid_spit,
+ /datum/reagent/ethanol/atomicbomb,
+ /datum/reagent/ethanol/beepsky_smash,
+ /datum/reagent/ethanol/coffee,
+ /datum/reagent/ethanol/hippies_delight,
+ /datum/reagent/ethanol/hooch,
+ /datum/reagent/ethanol/thirteenloko,
+ /datum/reagent/ethanol/manhattan_proj,
+ /datum/reagent/ethanol/neurotoxin,
+ /datum/reagent/ethanol/pwine,
+ /datum/reagent/ethanol/threemileisland,
+ /datum/reagent/ethanol/toxins_special)
+
+/proc/lunchables_lunches()
+ if(!(lunchables_lunches_[lunchables_lunches_[1]]))
+ lunchables_lunches_ = init_lunchable_list(lunchables_lunches_)
+ return lunchables_lunches_
+
+/proc/lunchables_snacks()
+ if(!(lunchables_snacks_[lunchables_snacks_[1]]))
+ lunchables_snacks_ = init_lunchable_list(lunchables_snacks_)
+ return lunchables_snacks_
+
+/proc/lunchables_drinks()
+ if(!(lunchables_drinks_[lunchables_drinks_[1]]))
+ lunchables_drinks_ = init_lunchable_list(lunchables_drinks_)
+ return lunchables_drinks_
+
+/proc/lunchables_drink_reagents()
+ if(!(lunchables_drink_reagents_[lunchables_drink_reagents_[1]]))
+ lunchables_drink_reagents_ = init_lunchable_reagent_list(lunchables_drink_reagents_, /datum/reagent/drink)
+ return lunchables_drink_reagents_
+
+/proc/lunchables_ethanol_reagents()
+ if(!(lunchables_ethanol_reagents_[lunchables_ethanol_reagents_[1]]))
+ lunchables_ethanol_reagents_ = init_lunchable_reagent_list(lunchables_ethanol_reagents_, /datum/reagent/ethanol)
+ return lunchables_ethanol_reagents_
+
+/proc/init_lunchable_list(var/list/lunches)
+ . = list()
+ for(var/lunch in lunches)
+ var/obj/O = lunch
+ .[initial(O.name)] = lunch
+ return sortAssoc(.)
+
+/proc/init_lunchable_reagent_list(var/list/banned_reagents, var/reagent_types)
+ . = list()
+ for(var/reagent_type in subtypesof(reagent_types))
+ if(reagent_type in banned_reagents)
+ continue
+ var/datum/reagent/reagent = reagent_type
+ .[initial(reagent.name)] = initial(reagent.id)
+ return sortAssoc(.)
diff --git a/code/modules/research/xenoarchaeology/finds/finds.dm b/code/modules/research/xenoarchaeology/finds/finds.dm
index 8b78d75aea3..0a709dbf833 100644
--- a/code/modules/research/xenoarchaeology/finds/finds.dm
+++ b/code/modules/research/xenoarchaeology/finds/finds.dm
@@ -323,7 +323,7 @@
apply_material_decorations = 0
if(23)
apply_prefix = 0
- new_item = PoolOrNew(/obj/item/stack/rods, src.loc)
+ new_item = getFromPool(/obj/item/stack/rods, src.loc)
apply_image_decorations = 0
apply_material_decorations = 0
if(24)
diff --git a/code/modules/shuttles/antagonist.dm b/code/modules/shuttles/antagonist.dm
index 1e95093edd7..6a791777156 100644
--- a/code/modules/shuttles/antagonist.dm
+++ b/code/modules/shuttles/antagonist.dm
@@ -2,8 +2,10 @@
name = "skipjack control console"
req_access = list(access_syndicate)
shuttle_tag = "Skipjack"
+ light_color = LIGHT_COLOR_RED
/obj/machinery/computer/shuttle_control/multi/syndicate
name = "mercenary shuttle control console"
req_access = list(access_syndicate)
shuttle_tag = "Mercenary"
+ light_color = LIGHT_COLOR_RED
diff --git a/code/modules/shuttles/shuttle_console.dm b/code/modules/shuttles/shuttle_console.dm
index 6c0d72efb3d..ebe6eeb4643 100644
--- a/code/modules/shuttles/shuttle_console.dm
+++ b/code/modules/shuttles/shuttle_console.dm
@@ -2,6 +2,7 @@
name = "shuttle control console"
icon = 'icons/obj/computer.dmi'
+ light_color = LIGHT_COLOR_CYAN
icon_screen = "shuttle"
circuit = null
diff --git a/code/modules/spells/spell_projectile.dm b/code/modules/spells/spell_projectile.dm
index c94131b252c..6afc7b5afdc 100644
--- a/code/modules/spells/spell_projectile.dm
+++ b/code/modules/spells/spell_projectile.dm
@@ -26,7 +26,7 @@
/obj/item/projectile/spell_projectile/before_move()
if(proj_trail && src && src.loc) //pretty trails
- var/obj/effect/overlay/trail = PoolOrNew(/obj/effect/overlay, src.loc)
+ var/obj/effect/overlay/trail = getFromPool(/obj/effect/overlay, src.loc)
trails += trail
trail.icon = proj_trail_icon
trail.icon_state = proj_trail_icon_state
diff --git a/code/modules/supermatter/supermatter.dm b/code/modules/supermatter/supermatter.dm
index b8413da63f9..d6603fca92f 100644
--- a/code/modules/supermatter/supermatter.dm
+++ b/code/modules/supermatter/supermatter.dm
@@ -55,6 +55,8 @@
var/explosion_point = 1000
light_color = "#8A8A00"
+ uv_intensity = 255
+ var/last_power = 0
var/warning_color = "#B8B800"
var/emergency_color = "#D9D900"
@@ -120,6 +122,12 @@
/obj/machinery/power/supermatter/proc/shift_light(var/lum, var/clr)
if(lum != light_range || clr != light_color)
set_light(lum, l_color = clr)
+ update_uv()
+
+/obj/machinery/power/supermatter/proc/update_uv()
+ if (last_power + 20 < power || last_power - 20 > power)
+ set_uv(CLAMP01(power / 500) * 255)
+ last_power = power
/obj/machinery/power/supermatter/proc/get_integrity()
var/integrity = damage / explosion_point
@@ -395,7 +403,8 @@
//following is adapted from singulo code
// Let's just make this one loop.
for(var/atom/X in orange(pull_radius,src))
- spawn() X.singularity_pull(src, STAGE_FIVE)
+ X.singularity_pull(src, STAGE_FIVE)
+ CHECK_TICK
return
diff --git a/code/modules/telesci/bscrystal.dm b/code/modules/telesci/bscrystal.dm
index a8c089e1e40..3f54a63e222 100644
--- a/code/modules/telesci/bscrystal.dm
+++ b/code/modules/telesci/bscrystal.dm
@@ -18,7 +18,7 @@
/obj/item/bluespace_crystal/attack_self(mob/user)
user.visible_message("[user] crushes [src]!", "You crush [src]!")
- PoolOrNew(/obj/effect/sparks, loc)
+ getFromPool(/obj/effect/sparks, loc)
playsound(src.loc, "sparks", 50, 1)
blink_mob(user)
user.unEquip(src)
@@ -31,7 +31,7 @@
if(!..()) // not caught in mid-air
visible_message("[src] fizzles and disappears upon impact!")
var/turf/T = get_turf(hit_atom)
- PoolOrNew(/obj/effect/sparks, T)
+ getFromPool(/obj/effect/sparks, T)
playsound(src.loc, "sparks", 50, 1)
if(isliving(hit_atom))
blink_mob(hit_atom)
diff --git a/code/modules/telesci/telesci_computer.dm b/code/modules/telesci/telesci_computer.dm
index bb71e7582eb..6655980f07b 100644
--- a/code/modules/telesci/telesci_computer.dm
+++ b/code/modules/telesci/telesci_computer.dm
@@ -2,6 +2,7 @@
name = "\improper Telepad Control Console"
desc = "Used to teleport objects to and from the telescience telepad."
icon_screen = "teleport"
+ light_color = LIGHT_COLOR_BLUE
circuit = /obj/item/weapon/circuitboard/telesci_console
var/sending = 1
var/obj/machinery/telepad/telepad = null
diff --git a/code/modules/vehicles/vehicle.dm b/code/modules/vehicles/vehicle.dm
index b44b27582fe..b0907d19aad 100644
--- a/code/modules/vehicles/vehicle.dm
+++ b/code/modules/vehicles/vehicle.dm
@@ -144,7 +144,7 @@
/obj/vehicle/emp_act(severity)
var/was_on = on
stat |= EMPED
- var/obj/effect/overlay/pulse2 = PoolOrNew(/obj/effect/overlay, src.loc)
+ var/obj/effect/overlay/pulse2 = getFromPool(/obj/effect/overlay, src.loc)
pulse2.icon = 'icons/effects/effects.dmi'
pulse2.icon_state = "empdisable"
pulse2.name = "emp sparks"
@@ -197,8 +197,8 @@
src.visible_message("\red [src] blows apart!", 1)
var/turf/Tsec = get_turf(src)
- PoolOrNew(/obj/item/stack/rods, Tsec)
- PoolOrNew(/obj/item/stack/rods, Tsec)
+ getFromPool(/obj/item/stack/rods, Tsec)
+ getFromPool(/obj/item/stack/rods, Tsec)
new /obj/item/stack/cable_coil/cut(Tsec)
if(cell)
diff --git a/code/world.dm b/code/world.dm
index 855ebf17557..1cc3dfb50db 100644
--- a/code/world.dm
+++ b/code/world.dm
@@ -1,3 +1,5 @@
+#define WORLD_ICON_SIZE 32
+#define PIXEL_MULTIPLIER WORLD_ICON_SIZE/32
/*
The initialization of the game happens roughly like this:
diff --git a/config/example/tips.txt b/config/example/tips.txt
new file mode 100644
index 00000000000..1291e1d4240
--- /dev/null
+++ b/config/example/tips.txt
@@ -0,0 +1,171 @@
+You can catch thrown items by toggling on your throw mode with an empty hand active.
+To crack the safe in the vault, use a stethoscope on it.
+You can climb onto a table by dragging yourself onto one.
+You can drag other players onto yourself to open the strip menu, letting you remove their equipment or force them to wear something.
+Clicking on a windoor rather then bumping into it will keep it open, you can click it again to close it.
+You can spray a fire extinguisher or fire a gun while floating through space to change your direction. Simply fire opposite to where you want to go.
+You can change the control scheme by pressing tab. One is WASD, the other is the arrow keys. Keep in mind that hotkeys are also changed with this.
+All vending machines can be hacked to obtain some contraband items from them, and some can be fed with coins to gain access to premium items.
+Glass shards can be welded to make glass, and metal rods can be welded to make metal.
+If you need to drag multiple people either to safety or to space, bring a locker over and stuff them all in before hauling them off.
+You can grab someone by clicking on them with the grab intent, then upgrade the grab by clicking on the grab button in your active hand. An aggressive grab will allow you to place someone on a table by clicking on it, or throw them by toggling on throwing.
+Holding alt and left clicking a tile will allow you to see its contents in the top right window pane, which is much faster than right clicking.
+The resist button will allow you to resist out of handcuffs, being buckled to a chair or bed, out of locked lockers and more. Whenever you're stuck, try resisting!
+You can move an item out of the way by dragging it and then clicking on an adjacent tile with an empty hand.
+Maintenance is full of equipment that is randomized every round. Look around and see if anything is worth using.There are many places around the station to hide contraband. A few for starters: linen boxes, toilet cisterns, body bags. Experiment to find more!
+As the Captain, you are one of the highest priority targets on the station. Everything from revolutions, to nuclear operatives, to traitors that need to rob you of your unique lasgun or your life are things to worry about.
+As the Captain, you have absolute access and control over the station, but this does not mean that being a horrible person won't result in mutiny and a ban.
+As the Chief Medical Officer, your hypospray is like an instant injection syringe that can hold 30 units as opposed to the standard 15.
+As the Chief Medical Officer, coordinate and communicate with your doctors, chemists, and EMTs during a nuclear emergency, blob infestation, or some other crisis to keep people alive and fighting.
+As a Medical Doctor, you can attempt to drain blood from a husk with a syringe to determine the cause. If you can extract blood, it was caused by extreme temperatures or lasers, if there is no blood to extract, it was caused by something unnatural.
+As a Medical Doctor, you can surgically implant or extract things from people's chests. This can range from putting in a bomb to pulling out an alien larva.
+As a Medical Doctor, you must target the correct limb and be on help intent when trying to perform surgery on someone.
+As a Chemist, there are dozens of chemicals that can heal, and even more that can cause harm. Experiment!
+As the Virologist, you only require small amounts of vaccine to heal a sick patient. Work with the Chemist to distribute your cures more efficiently.
+As the Research Director, you can take AIs out of their cores by loading them into an intelliCard, and then from there into an AI system integrity restorer computer to revive and/or repair them.
+As the Research Director, you can lock down cyborgs instead of blowing them up. Then you can have their laws reset or if that doesn't work, safely dismantled.
+As a Xenoscientist, you can inject yourself with the mutation toxin extracted from green slimes to become a slime person, who will never be attacked by slimes!
+As a Xenoscientist, you can maximize the number of uses you get out of a slime by feeding it slime steroid, created from purple slimes, while alive. You can then apply extract enhancer, created from cerulean slimes, on each extract.
+As a Scientist, researchable machine parts can seriously improve the efficiency and speed of machines around the station. In some cases, it can even unlock new functions.
+As a Scientist, the teleporter in Misc research can be set-up to teleport across the whole station! All you need to do is crack the formula.
+As a Roboticist, you can repair your cyborgs with a welding tool. If they have taken burn damage from lasers, you can remove their battery, expose the wiring with a screwdriver and replace their wires with a cable coil.
+As the AI, you can click on people's names to look at them. This only works if there are cameras that can see them.
+As the AI, you can quickly open and close doors by holding shift while clicking them, bolt them when holding ctrl, and even shock them while holding alt.
+As the AI, you can take pictures with your camera and upload them to newscasters.
+As a Cyborg, choose your module carefully, as only a roboticist can let you repick it. Remember that you don't need to choose immediately after you spawn!
+As a Cyborg, you are immune to most forms of stunning, and excel at almost everything far better than humans. However, flashes can easily stunlock you and you cannot do any precision work as you lack hands.
+As a Cyborg, you are impervious to fires and heat. If you are rogue, you can release plasma fires everywhere and walk through them without a care in the world!
+As a Cyborg, you are extremely vulnerable to EMPs as EMPs both stun you and damage you. The ion rifle in the armory or a traitor with an EMP kit can kill you in seconds.
+As the Chief Engineer, you can rename areas or create entirely new ones using your station blueprints.
+As the Research Director, you can spy on and even forge PDA communications with the message monitor console! The key is in your office.
+As an Engineer, the supermatter shard is an extremely dangerous piece of equipment: touching it will disintegrate you.
+As an Engineer, you can electrify grilles by placing wire "nodes" beneath them: the big seemingly unconnected bulges from a half completed wiring job.
+As an Engineer, you can power the station solely with the solar arrays. While uninteresting, it is a much safer alternative to most other engines.
+As an Engineer, you can repair windows by using a welding tool on them while on any intent other than harm.
+As an Engineer, you can lock emitters using your ID card to prevent others from disabling them.
+As an Atmospheric Technician, you can't unwrench a pipe if the pressure within is too high.
+As the Head of Security, you are expected to coordinate your security force to handle any threat that comes to the station. Sometimes it means making use of the armory to handle a blob, sometimes it means being ruthless during a revolution or cult.
+As the Head of Security, you can call for forced cyborgization, but may require the Captain's approval.
+As the Head of Security, don't let the power go to your head. You may have high access, great equipment, and a miniature army at your side, but being a terrible person without a good reason is grounds for banning.
+As the Warden, your duty is to be the watchdog of the brig and handler of prisoners when little is happening, and to hand out equipment and weapons to the security officers when a crisis strikes.
+As the Warden, keep a close eye on the armory at all times, as it is a favored strike point of nuclear operatives and cocky traitors.
+As the Warden, if a prisoner's crimes are heinous enough you can put them in permabrig. Make sure to check on them once in a while!
+As the Warden, you can implant criminals you suspect might re-offend with devices that will track their location and allow you to remotely inject them with disabling chemicals.
+As the Warden, you can use handcuffs on orange prisoner shoes to turn them into cuffed shoes, forcing prisoners to walk and potentially thwarting an escape.
+As a Security Officer, communicate and coordinate with your fellow officers using the security channel (:s) to avoid confusion.
+As a Security Officer, your sechuds or HUDsunglasses can not only see crewmates' job assignments and criminal status, but also if they are loyalty implanted. Use this to your advantage in a revolution to definitively tell who is on your side!
+As a Security Officer, examining someone while wearing sechuds or HUDsunglasses will let you set their arrest level, which will cause Beepsky and other security bots to chase after them.
+As the Detective, people leave fingerprints everywhere and on everything. With the exception of white latex, gloves will hide them. All is not lost, however, as gloves leave fibers specific to their kind such as black or nitrile, pointing to a general department.
+As the Head of Personnel, you are just as large a target as the Captain because of the potential power your ID and computer can hand out.
+As the Chaplain, your null rod has a lot of functions: it can convert water into holy water, which if spread on the ground prevents wizards from jaunting away, can destroy cultist runes by hitting them, and is a very powerful weapon to boot!
+As the Chaplain, your bible is also a container that can store small items. Depending on your god, your starting bible may come with a surprise!
+As the Chaplain, you are much more likely to get a response by praying to the gods than most people. To boost your chances, make altars with colorful crayon runes, lit candles, and wire art.
+As a Botanist, you can hack the MegaSeed Vendor to get access to more exotic seeds. These seeds can alternatively be ordered from cargo.
+As a Botanist, you can mutate the plants growing in your hydroponics trays with unstable mutagen or, as an alternative, crude radioactives from chemistry to get special variations.
+As a Botanist, you should look into increasing the potency of your plants. This increases the size, amount of chemicals, points gained from grinding them in the biogenerator, and lets people know you are a proficient botanist.
+As a Cook, any food you make will be much healthier than the junk food found in vendors. Having the crew routinely eating from you will provide minor buffs.
+As the Bartender, the drinks you start with only give you the basics. If you want more advanced mixtures, look into working with chemistry, hydroponics, or even mining for things to grind up and throw in!
+As the Bartender, you can use a circular saw on your shotgun to make it easier to store.
+As a Janitor, if someone steals your janicart, you can instead use your space cleaner spray, grenades, water sprayer or order another from Cargo.
+As a Janitor, mousetraps can be used to create bombs or booby-trap containers.
+As the Librarian, be sure to keep the shelves stocked and the library clean for crew.
+As a Cargo Technician, you can hack MULEbots to make them faster, run over people in their way, and even let you ride them!
+As a Cargo Technician, you can order contraband items from the supply shuttle console by de-constructing it and using a multitool on the circuit board, the re-assembling it.
+As a Cargo Technician, you can earn more cargo points by shipping back crates from maintenance, liquid containers, plasma sheets, rare seeds from hydroponics, and more!
+As the Quartermaster, be sure to check the manifests on crates you receive to make sure all the info is correct. If there's a mistake, stamp the manifest DENIED and send it back in a crate with the items untouched for a refund!
+As a Shaft Miner, always have a GPS on you, so a fellow miner or cyborg can come to save you if you die.
+As a Traitor, the cryptographic sequencer (emag) can not only open doors, but also lockers, crates, APCs and more. It can hack cyborgs, and even cause bots to go berserk. Use it on the right machines, and you can even order more traitor gear or contact the Syndicate. Experiment!
+As a Traitor, subverting the AI to serve you can make it an extremely powerful ally. However, be careful of the wording in the laws you give it, as it may use your poorly written laws against you!
+As a Traitor, the Captain and the Head of Security are two of the most difficult to kill targets on the station. Plan carefully if either are present.
+As a Traitor, you can manufacture and recycle revolver bullets at a hacked autolathe, making the revolver an extremely powerful tool.
+As a Traitor, you may sometimes hunt other traitors, and in turn be hunted by them.
+As a Traitor, the syndicate encryption key is very useful for coordinating plans with your fellow traitors -- or, of course, betraying them.
+As a Nuclear Operative, communication is key! Use your radio to speak to your fellow operatives and coordinate an attack plan.
+As a Nuclear Operative, you should look into purchasing a syndicate cyborg, as they can provide heavy fire support, are immune to conventional stuns, and can easily take down the AI.
+As a Nuclear Operative, stick together! While your equipment is robust, your fellow operatives are much better at saving your life: they can drag you away from danger while stunned and provide cover fire.
+As a Nuclear Operative, you might end up in a situation where the AI has bolted you into a room. Having some spare C4 in your pocket can save your life.
+As a Monkey, you can crawl through air or scrubber vents by alt+left clicking them. You must drop everything you are wearing and holding to do this, however.
+As a Monkey, you can still wear a few human items, such as backpacks, gas masks and hats, and still have two free hands.
+As the Malfunctioning AI, you should either order your cyborgs to dismantle the robotics console or blow it up yourself in order to protect them. Make sure to hunt down all those laptops too!
+As an Alien, your melee prowess is unmatched, but your ranged abilities are sorely lacking. Make use of corners to force a melee confrontation!
+As an Alien, you take additional damage from all burn attacks, such as lasers, welding tools, and fires. Furthermore, fire can destroy your resin and eggs. Expose areas to space to starve away any flamethrower fires before they can do damage!
+As an Alien, resin floors not only regenerate your plasma supply, but also passively heal you. Fight on resin floors to gain a home turf advantage!
+As an Alien, the facehugger is by far your most powerful weapon because of its ability to instantly win a fight. Remember however that certain helmets, such as biohoods or space helmets will completely block facehugger attacks.
+As an Alien, you are unable to use many human items and machines. Instead, you should focus on sabotaging APCs, computers, cameras and either stowing, spacing, or melting any weapons you find.
+As a Revolutionary, you cannot convert a head of staff or someone who has a loyalty implant. Implants can however be surgically removed, and do not carry over with cloning. Take control of medbay to keep control of conversions!
+As a Revolutionary, cargo can be your best friend or your worst nightmare. In the best case scenario you will be able to order a limitless amount of guns and armor, in the worst case scenario security will take control and order a limitless number of loyalty implants to turn your fellow revolutionaries against you.
+As a Revolutionary, your main power comes from how quickly you spread. Convert people as fast as you can and overwhelm the heads of staff before security can arm up.
+As a Changeling, the Extract DNA sting grants you more people to change into, but does not let you choose more powers.
+As a Changeling, you can absorb someone by strangling them and using the Absorb verb; this gives you the ability to choose more powers, the DNA of whoever you absorbed, and the memory of the absorbed.
+As a Cultist, invest in taking over xenobio, an adamantine golem army can quickly be converted into cultists and constructs.
+As a Cultist, your team starts off very weak, but if necessary can quickly convert everything they have into raw power. Make sure you have the numbers and equipment to support going loud, or the cult will fall flat on its face.
+As a Cultist, the Blood Boil rune will deal massive amounts of brute damage to non-cultists and some damage to fellow cultists of Nar-Sie nearby.
+As a Cultist, you can create an army of manifested goons using a combination of the Manifest rune, which creates homunculi from ghosts, and the Blood Drain rune, which drains life from anyone standing on any blood drain rune.
+As a Wizard, the fireball spell performs very poorly at close range, as it can easily catch you in the blast. It is best used as a form of artillery down long hallways.
+As a Wizard, most spells become unusable if you are not wearing your robe, hat, and sandals.
+As a Ghost, you can double click on people, bots, or the singularity to follow them.
+Occasionally the tip of the round might lie to you. Don't panic, this is normal.
+To defeat your enemy, shoot at them until they die.
+Sometimes you won't be able to avoid dying no matter how good you are at the game. Try not to stress too much about it.
+When a round ends nearly everything about it is lost forever, leave your salt behind with it.
+Killing the entire station isn't fun except when it is.
+You can win a pulse rifle from the arcade machine. Honest.
+Just like real life the entropy of the game can only increase with time. If things aren't on fire yet, just wait.
+Completing your objectives is good practice, but the best antagonists will strive to do more than the bare minimum to really leave an impression.
+The more obscure and underused a game mechanic is, the less likely your victims are to be able to deal with it.
+Cleanbot.
+Some antagonists are supposed to be extremely strong in one on one combat, stop getting mad about it.
+Sometimes a round will just be a bust. C'est la vie.
+This is a game that is constantly being developed for. Expect things to be added, removed, fixed, and broken on a daily basis.
+It's fun to try and predict the round type from the tip of the round message.
+The quartermaster is not a head of staff and will never be one.
+Your sprite represents your hitbox, so that floor length braid makes you easier to kill. The sacrifices we make for snowflake points.
+Spacemen can't move diagonally but most animals can. Blame the slow decline of the numpad.
+Sometimes admins will just do stuff. Roll with it.
+The remake will never come out.
+Plenty of things that aren't traditionally considered weapons can still be used to slowly brutalize someone to death, get creative!
+DEATH IS IMMINENT!
+This game is older than many of the people playing it.
+Do not go gentle into that good night.
+Just the tip?
+Some people are unable to read text on a game where half of it is based on text.
+Phoron check.
+Remember the cheesy line!
+Toolboxes do more damage if they are full of things, but their contents will spill when you use them as a weapon.
+Heads of Staff will be more willing to help you if you bring paperwork that's already filled out.
+See no evil, hear no evil, speak no evil.
+Auto-Hiss, despite the name, can be used for Unathi, Tajara, and Vaurca.
+Service borgs can use the special maidborg sprite if they pray hard enough.
+Chainswords and energy weapons can slice walls and people.
+If you see this message something has gone seriously wrong.
+If you're having trouble as an antagonist, consider a Dionaea whitelist.
+As a Medical Doctor, you can attach limbs from one species to the torso of another species. You too can make your own Frankenstein monster!
+The second antag contest is still running, you know.
+Killing mice on sight is not and never will be gank, no matter how much people complain.
+As an Unathi, you are the only species capable of wearing the rare Breacher hardsuits.
+As an Unathi, you can devour small mobs after some time.
+As a Tajaran, you move pretty fast. Zoom zoom, kitty.
+As a Tajaran, your resistance to cold probably doesn't actually help you in space. Feel free to try, though.
+As a Skrell you can look pretty and...uhh...not slip?
+As a Skrell you have free reign to validhunt synthetics. (Not really, please don't do this oh God what have I done).
+As a Human you are the best. Why do you care about your mechanics?
+As a Human you are really very great.
+As an IPC you are immune to most chemicals and gasses.
+As an IPC you can survive longer than most species in space, despite your supposed "weakness".
+As a Dionaea you can survive pretty much anything except a tiny little bottle of weedkiller.
+As a Dionaea you will die in darkness. Find the light at the end of the tunnel, and quick.
+As a Vaurca you can remotely speak to any other Vaurca on board. Not that there are any.
+As a Vaurca you can wade freely through phoron gas. Avoid phoron fires though. Somehow a species that breathes phoron gas is really weak to fire.
+As an ERT trooper, your most powerful weapons are your team mates. Your second most powerful weapon is your bigass gun.
+As an ERT trooper you're still expected to roleplay and progress the round.
+As a Ninja, you should learn the difference between invisibility and invulnerability.
+As a Ninja, you have a pretty badass sword. Use it.
+As a Cortical Borer, you a limp feather can kill you if you're outside of a host.
+As a NanoTrasen Actor you can't do anything. Thank Bay.
+As a Syndicate Commando you are a Nuke Operative with bigger and better guns. Use them.
+As a Death Commando you have only one course of action: RIP AND TEAR.
+As a Highlander, there can be only one.
+As a Loyalist, remember that you are an antagonist too!
+As a Renegade, consider playing a better gamemode.
+As a Vampire, you can create new vampires out of willing and less than willing crew. Mind that new vampires may decide to turn their powers against you.
+As a Vampire, if you start going hungry for blood don't expect to stay hidden for long.
\ No newline at end of file
diff --git a/flyway.conf b/flyway.conf
new file mode 100644
index 00000000000..e09fa4b59c7
--- /dev/null
+++ b/flyway.conf
@@ -0,0 +1,6 @@
+flyway.locations=filesystem:sql/migrate
+
+# copy these into another file and use the -configFile switch on flyway
+# flyway.url=jdbc:mysql://localhost/bs12
+# flyway.user=
+# flyway.password=
diff --git a/html/changelogs/VikingPingvin-PR-1626.yml b/html/changelogs/VikingPingvin-PR-1626.yml
new file mode 100644
index 00000000000..d8d3ef1dccc
--- /dev/null
+++ b/html/changelogs/VikingPingvin-PR-1626.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: VikingPingvin
+
+# 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:
+ - rscadd: "Added a filter function for departments in the PDA messenger screen."
+
diff --git a/html/changelogs/alberky-PR-1648.yml b/html/changelogs/alberky-PR-1648.yml
new file mode 100644
index 00000000000..f4044d08745
--- /dev/null
+++ b/html/changelogs/alberky-PR-1648.yml
@@ -0,0 +1,9 @@
+author: Alberyk
+
+delete-after: True
+
+changes:
+ - rscadd: "Ported the newest custom loadout from baystation12."
+ - rscadd: "Loadout flask and vacuum-flask can now be prefilled."
+ - rscadd: "Added lunchboxes."
+ - rscadd: "Added more options to the custom loadout, like winter coats."
diff --git a/html/changelogs/lohikar-lighting.yml b/html/changelogs/lohikar-lighting.yml
new file mode 100644
index 00000000000..e0eb8658e7c
--- /dev/null
+++ b/html/changelogs/lohikar-lighting.yml
@@ -0,0 +1,21 @@
+author: Lohikar
+delete-after: True
+changes:
+ - rscadd: "Ported over /vg/'s smooth lighting system."
+ - rscadd: "Tweaked the emission color of station lighting."
+ - rscadd: "Glowing slime cores now emit colored light instead of white light."
+ - rscadd: "Space tiles are now darker."
+ - rscadd: "Space tiles now have a parallax effect."
+ - rscadd: "Added color to the light of many consoles that did not have one set."
+ - rscdel: "You can no longer rotate your view with Rotate-View verbs, as it was breaking lighting."
+ - rscdel: "Hallucinations no longer rotate your view for the same reason as above."
+ - tweak: "Colored lighting should mix better now."
+ - tweak: "Rebalanced light emission of most light sources to better fit new lighting system."
+ - tweak: "Diona now gain energy from 'UV' light, emitted by station lighting as well as one or two portable light sources."
+ - rscdel: "Diona no longer gain any energy from flashlights & other portable light sources."
+ - experiment: "Lighting now updates immediately when you open an airlock."
+ - experiment: "Completely rewrote lighting system."
+ - experiment: "The game's mob processor should be more robust."
+ - experiment: "Tweaked several of the game's core processes in an effort to reduce lag."
+ - bugfix: "Fire alarms should no longer cause lag."
+ - bugfix: "Hydroponics trays should no longer cause lag."
diff --git a/html/changelogs/lohikar-paper-planes.yml b/html/changelogs/lohikar-paper-planes.yml
new file mode 100644
index 00000000000..a22cb8440c1
--- /dev/null
+++ b/html/changelogs/lohikar-paper-planes.yml
@@ -0,0 +1,4 @@
+author: Lohikar
+delete-after: True
+changes:
+ - rscadd: "You can now fold pieces of paper into paper airplanes, which can be thrown farther than unfolded sheets of paper."
diff --git a/html/changelogs/lordfowl - 1885.yml b/html/changelogs/lordfowl - 1885.yml
new file mode 100644
index 00000000000..4a94ff679d6
--- /dev/null
+++ b/html/changelogs/lordfowl - 1885.yml
@@ -0,0 +1,36 @@
+################################
+# 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: LordFowl
+
+# 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:
+ - rscadd: "Added 'Tip of the round' to the lobby. Based off of /tg/'s, it comes with its own Aurora tips too."
diff --git a/icons/effects/lighting_overlay.dmi b/icons/effects/lighting_overlay.dmi
deleted file mode 100644
index 1ff16d2b78b..00000000000
Binary files a/icons/effects/lighting_overlay.dmi and /dev/null differ
diff --git a/icons/effects/lighting_overlay.png b/icons/effects/lighting_overlay.png
new file mode 100644
index 00000000000..c7f308204fb
Binary files /dev/null and b/icons/effects/lighting_overlay.png differ
diff --git a/icons/obj/lunchbox.dmi b/icons/obj/lunchbox.dmi
new file mode 100644
index 00000000000..f75c3873721
Binary files /dev/null and b/icons/obj/lunchbox.dmi differ
diff --git a/icons/turf/space.dmi b/icons/turf/space.dmi
index 30d0d1428a5..ee59a450bed 100644
Binary files a/icons/turf/space.dmi and b/icons/turf/space.dmi differ
diff --git a/icons/turf/space_parallax1.dmi b/icons/turf/space_parallax1.dmi
new file mode 100644
index 00000000000..8328b3dba9b
Binary files /dev/null and b/icons/turf/space_parallax1.dmi differ
diff --git a/icons/turf/space_parallax2.dmi b/icons/turf/space_parallax2.dmi
new file mode 100644
index 00000000000..8f33b6fc9b1
Binary files /dev/null and b/icons/turf/space_parallax2.dmi differ
diff --git a/icons/turf/space_parallax3.dmi b/icons/turf/space_parallax3.dmi
new file mode 100644
index 00000000000..0c9d95b6388
Binary files /dev/null and b/icons/turf/space_parallax3.dmi differ
diff --git a/icons/turf/space_parallax4.dmi b/icons/turf/space_parallax4.dmi
new file mode 100644
index 00000000000..2dbbb39e46b
Binary files /dev/null and b/icons/turf/space_parallax4.dmi differ
diff --git a/maps/exodus-1.dmm b/maps/exodus-1.dmm
index 8a47dc5e6cc..8f9001babbe 100644
--- a/maps/exodus-1.dmm
+++ b/maps/exodus-1.dmm
@@ -271,7 +271,7 @@
"afk" = (/obj/machinery/atmospherics/pipe/simple/hidden/supply,/turf/simulated/floor/tiled,/area/security/main)
"afl" = (/turf/simulated/shuttle/wall{tag = "icon-swall_straight (EAST)"; icon_state = "swall_straight"; dir = 4},/area/shuttle/escape_pod3/station)
"afm" = (/obj/structure/grille,/obj/structure/window/reinforced{dir = 4},/obj/structure/window/reinforced{dir = 8},/obj/structure/cable/green,/obj/structure/cable/green{d2 = 2; icon_state = "0-2"},/turf/simulated/floor/plating,/area/security/main)
-"afn" = (/turf/simulated/shuttle/wall{tag = "icon-swall_s"; icon_state = "swall_s"},/area/shuttle/escape_pod3/station)
+"afn" = (/turf/space,/turf/simulated/shuttle/wall{tag = "icon-swall_c"; icon_state = "swall_c"},/area/shuttle/escape_pod3/station)
"afo" = (/obj/structure/cable/green{d1 = 4; d2 = 8; icon_state = "4-8"},/obj/machinery/atmospherics/unary/vent_pump/on{dir = 4},/turf/simulated/floor/tiled,/area/security/warden)
"afp" = (/obj/machinery/power/apc{dir = 8; name = "west bump"; pixel_x = -24},/obj/structure/cable/green{d2 = 4; icon_state = "0-4"},/turf/simulated/floor/tiled,/area/security/warden)
"afq" = (/obj/structure/cable/green{d1 = 4; d2 = 8; icon_state = "4-8"},/turf/simulated/floor/tiled,/area/security/warden)
@@ -326,7 +326,7 @@
"agn" = (/obj/structure/disposalpipe/segment{dir = 4},/obj/structure/table/standard,/obj/structure/reagent_dispensers/peppertank{pixel_x = 30},/obj/item/device/radio,/obj/item/device/radio,/obj/item/device/radio,/obj/item/weapon/crowbar,/obj/item/weapon/crowbar,/obj/item/weapon/crowbar,/obj/item/device/flashlight/maglight,/obj/item/device/flashlight/maglight,/obj/item/device/flashlight/maglight,/obj/effect/floor_decal/corner/blue,/turf/simulated/floor/tiled,/area/security/main)
"ago" = (/obj/structure/disposalpipe/segment{dir = 4},/obj/structure/cable/green{d1 = 1; d2 = 2; icon_state = "1-2"},/turf/simulated/floor/tiled,/area/security/main)
"agp" = (/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"},/obj/structure/cable{d1 = 1; d2 = 8; icon_state = "1-8"},/obj/structure/cable{d1 = 1; d2 = 4; icon_state = "1-4"},/obj/machinery/door/firedoor/border_only{dir = 2},/obj/machinery/atmospherics/pipe/simple/hidden/supply,/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/machinery/door/airlock/command{id_tag = "HoSdoor"; name = "Head of Security"; req_access = list(58)},/turf/simulated/floor/tiled/dark,/area/crew_quarters/heads/hos)
-"agq" = (/turf/simulated/shuttle/wall{tag = "icon-swall_s (WEST)"; icon_state = "swall_s"; dir = 8},/area/shuttle/escape_pod3/station)
+"agq" = (/turf/space,/turf/simulated/shuttle/wall{tag = "icon-swall_c (WEST)"; icon_state = "swall_c"; dir = 8},/area/shuttle/escape_pod3/station)
"agr" = (/obj/machinery/door/firedoor/border_only{dir = 2},/obj/machinery/door/airlock/glass_security{name = "Warden's Office"; req_access = list(3)},/obj/structure/cable/green{d1 = 2; d2 = 4; icon_state = "2-4"},/obj/structure/cable/green{d1 = 2; d2 = 8; icon_state = "2-8"},/obj/structure/cable/green{d1 = 1; d2 = 2; icon_state = "1-2"},/obj/machinery/atmospherics/pipe/simple/hidden/supply,/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/turf/simulated/floor/tiled/dark,/area/security/warden)
"ags" = (/obj/structure/closet/secure_closet/security,/obj/item/device/flashlight/flare,/obj/structure/window/reinforced,/obj/effect/floor_decal/industrial/outline/yellow,/turf/simulated/floor/tiled,/area/security/main)
"agt" = (/obj/structure/closet/wardrobe/red,/obj/structure/window/reinforced,/obj/item/clothing/accessory/holster/waist,/obj/item/clothing/accessory/holster/waist,/obj/item/clothing/accessory/holster/waist,/turf/simulated/floor/tiled,/area/security/main)
@@ -984,10 +984,10 @@
"asV" = (/obj/structure/disposalpipe/segment,/mob/living/bot/secbot/beepsky,/turf/simulated/floor/tiled,/area/hallway/primary/fore)
"asW" = (/obj/structure/cable/green{d1 = 1; d2 = 2; icon_state = "1-2"},/obj/machinery/atmospherics/pipe/simple/hidden/supply,/obj/effect/floor_decal/corner/blue{tag = "icon-corner_white (WEST)"; icon_state = "corner_white"; dir = 8},/turf/simulated/floor/tiled,/area/hallway/primary/fore)
"asX" = (/obj/machinery/light{icon_state = "tube1"; dir = 4},/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/effect/floor_decal/corner/blue,/turf/simulated/floor/tiled,/area/hallway/primary/fore)
-"asY" = (/turf/simulated/shuttle/wall{tag = "icon-swall_s (EAST)"; icon_state = "swall_s"; dir = 4},/area/shuttle/escape_pod1/station)
-"asZ" = (/turf/simulated/shuttle/wall{tag = "icon-swall_s"; icon_state = "swall_s"},/area/shuttle/escape_pod1/station)
-"ata" = (/turf/simulated/shuttle/wall{tag = "icon-swall_s (EAST)"; icon_state = "swall_s"; dir = 4},/area/shuttle/escape_pod2/station)
-"atb" = (/turf/simulated/shuttle/wall{tag = "icon-swall_s"; icon_state = "swall_s"},/area/shuttle/escape_pod2/station)
+"asY" = (/turf/space,/turf/simulated/shuttle/wall{tag = "icon-swall_c (EAST)"; icon_state = "swall_c"; dir = 4},/area/shuttle/escape_pod1/station)
+"asZ" = (/turf/space,/turf/simulated/shuttle/wall{tag = "icon-swall_c"; icon_state = "swall_c"},/area/shuttle/escape_pod1/station)
+"ata" = (/turf/space,/turf/simulated/shuttle/wall{tag = "icon-swall_c (EAST)"; icon_state = "swall_c"; dir = 4},/area/shuttle/escape_pod2/station)
+"atb" = (/turf/space,/turf/simulated/shuttle/wall{tag = "icon-swall_c"; icon_state = "swall_c"},/area/shuttle/escape_pod2/station)
"atc" = (/obj/effect/floor_decal/industrial/warning{tag = "icon-warning (NORTH)"; icon_state = "warning"; dir = 1},/turf/simulated/floor/plating,/area/maintenance/arrivals)
"atd" = (/obj/machinery/washing_machine,/obj/effect/floor_decal/corner/white/diagonal,/obj/effect/floor_decal/corner/blue/diagonal{dir = 4},/turf/simulated/floor/tiled,/area/security/prison)
"ate" = (/obj/machinery/portable_atmospherics/hydroponics,/obj/machinery/light{icon_state = "tube1"; dir = 4},/turf/simulated/floor/tiled,/area/security/prison)
@@ -1501,8 +1501,8 @@
"aCS" = (/obj/machinery/atmospherics/binary/passive_gate{dir = 8},/obj/effect/floor_decal/industrial/warning{dir = 1},/turf/simulated/floor/plating,/area/maintenance/bar)
"aCT" = (/obj/machinery/atmospherics/pipe/tank/air{dir = 8},/obj/effect/floor_decal/industrial/warning{dir = 5},/turf/simulated/floor/plating,/area/maintenance/bar)
"aCU" = (/turf/simulated/shuttle/wall{tag = "icon-swall_straight (WEST)"; icon_state = "swall_straight"; dir = 8},/area/shuttle/arrival/station)
-"aCV" = (/turf/simulated/shuttle/wall{tag = "icon-swall_s (EAST)"; icon_state = "swall_s"; dir = 4},/area/shuttle/arrival/station)
-"aCW" = (/turf/simulated/shuttle/wall{tag = "icon-swall_s"; icon_state = "swall_s"},/area/shuttle/arrival/station)
+"aCV" = (/turf/space,/turf/simulated/shuttle/wall{tag = "icon-swall_c (EAST)"; icon_state = "swall_c"; dir = 4},/area/shuttle/arrival/station)
+"aCW" = (/turf/space,/turf/simulated/shuttle/wall{tag = "icon-swall_c"; icon_state = "swall_c"},/area/shuttle/arrival/station)
"aCX" = (/turf/simulated/shuttle/wall{tag = "icon-swall_t (NORTH)"; icon_state = "swall_t"; dir = 1},/area/shuttle/arrival/station)
"aCY" = (/obj/effect/floor_decal/industrial/warning{tag = "icon-warning (WEST)"; icon_state = "warning"; dir = 8},/turf/simulated/floor/tiled,/area/hallway/secondary/entry/port)
"aCZ" = (/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/machinery/atmospherics/pipe/simple/hidden/supply,/obj/structure/cable/green{d1 = 1; d2 = 2; icon_state = "1-2"},/obj/machinery/camera/network/exodus{c_tag = "Arrivals East"; dir = 8},/obj/effect/floor_decal/corner/white{tag = "icon-corner_white (EAST)"; icon_state = "corner_white"; dir = 4},/obj/effect/floor_decal/corner/blue,/turf/simulated/floor/tiled,/area/hallway/secondary/entry/port)
@@ -1900,7 +1900,7 @@
"aKB" = (/obj/structure/closet/coffin,/obj/machinery/door/blast/shutters{density = 0; dir = 2; icon_state = "shutter0"; id = "chapel"; name = "Privacy Shutters"; opacity = 0},/obj/machinery/door/window/westleft{name = "Coffin Storage"},/turf/simulated/floor/tiled/dark,/area/chapel/office)
"aKC" = (/obj/structure/table/standard,/obj/effect/floor_decal/chapel{tag = "icon-chapel (EAST)"; icon_state = "chapel"; dir = 4},/turf/simulated/floor/tiled/dark,/area/chapel/main)
"aKD" = (/obj/structure/bed/chair{dir = 4},/obj/effect/floor_decal/chapel{tag = "icon-chapel (NORTH)"; icon_state = "chapel"; dir = 1},/turf/simulated/floor/tiled/dark,/area/chapel/main)
-"aKE" = (/turf/simulated/shuttle/wall{tag = "icon-swall_s (NORTH)"; icon_state = "swall_s"; dir = 1},/area/shuttle/arrival/station)
+"aKE" = (/turf/space,/turf/simulated/shuttle/wall{tag = "icon-swall_c (NORTH)"; icon_state = "swall_c"; dir = 1},/area/shuttle/arrival/station)
"aKF" = (/obj/structure/grille,/obj/structure/window/reinforced{dir = 8},/obj/structure/window/reinforced{dir = 4},/obj/structure/window/reinforced{dir = 1},/obj/machinery/door/firedoor/border_only{dir = 2},/turf/simulated/floor/plating,/area/hallway/primary/central_one)
"aKG" = (/turf/simulated/shuttle/wall{tag = "icon-swall_t"; icon_state = "swall_t"},/area/shuttle/arrival/station)
"aKH" = (/obj/machinery/door/firedoor/border_only{dir = 8; name = "Firelock West"},/obj/machinery/light,/turf/simulated/floor/tiled,/area/hallway/secondary/entry/starboard)
@@ -1990,7 +1990,7 @@
"aMn" = (/obj/machinery/light{dir = 8},/obj/machinery/camera/network/civilian_east{c_tag = "Chapel North"; dir = 4},/turf/simulated/floor/tiled/dark,/area/chapel/main)
"aMo" = (/obj/structure/table/standard,/obj/machinery/light/small,/obj/effect/floor_decal/chapel,/turf/simulated/floor/tiled/dark,/area/chapel/main)
"aMp" = (/obj/structure/bed/chair{dir = 4},/obj/effect/floor_decal/chapel{tag = "icon-chapel (WEST)"; icon_state = "chapel"; dir = 8},/turf/simulated/floor/tiled/dark,/area/chapel/main)
-"aMq" = (/turf/simulated/shuttle/wall{tag = "icon-swall_s (WEST)"; icon_state = "swall_s"; dir = 8},/area/shuttle/arrival/station)
+"aMq" = (/turf/space,/turf/simulated/shuttle/wall{tag = "icon-swall_c (WEST)"; icon_state = "swall_c"; dir = 8},/area/shuttle/arrival/station)
"aMr" = (/obj/machinery/firealarm{dir = 8; pixel_x = -24},/obj/effect/floor_decal/industrial/warning{tag = "icon-warning (WEST)"; icon_state = "warning"; dir = 8},/turf/simulated/floor/tiled,/area/hallway/secondary/entry/port)
"aMs" = (/obj/structure/bed/chair/comfy/beige,/turf/simulated/floor/lino,/area/hallway/secondary/entry/starboard)
"aMt" = (/turf/simulated/floor/lino,/area/hallway/secondary/entry/starboard)
@@ -3829,13 +3829,13 @@
"bvG" = (/obj/effect/floor_decal/industrial/warning{dir = 1},/turf/simulated/floor/tiled/white,/area/rnd/lab)
"bvH" = (/obj/structure/cable/green{d1 = 1; d2 = 2; icon_state = "1-2"},/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 10},/obj/effect/floor_decal/industrial/warning/corner{tag = "icon-warningcorner (NORTH)"; icon_state = "warningcorner"; dir = 1},/turf/simulated/floor/tiled/white,/area/rnd/lab)
"bvI" = (/obj/item/weapon/stock_parts/console_screen,/obj/structure/table/standard,/obj/item/weapon/stock_parts/console_screen,/obj/item/weapon/stock_parts/console_screen,/obj/item/weapon/stock_parts/matter_bin,/obj/item/weapon/stock_parts/matter_bin,/obj/machinery/light{dir = 4; icon_state = "tube1"},/turf/simulated/floor/tiled/white,/area/rnd/lab)
-"bvJ" = (/turf/simulated/shuttle/wall{tag = "icon-swall_s (EAST)"; icon_state = "swall_s"; dir = 4},/area/shuttle/research/station)
+"bvJ" = (/turf/space,/turf/simulated/shuttle/wall{tag = "icon-swall_c (EAST)"; icon_state = "swall_c"; dir = 4},/area/shuttle/research/station)
"bvK" = (/turf/simulated/wall/r_wall,/area/crew_quarters/heads/hop)
"bvL" = (/obj/structure/grille,/obj/structure/window/reinforced,/obj/structure/window/reinforced{dir = 4},/obj/structure/window/reinforced{dir = 8},/obj/machinery/door/firedoor/border_only{dir = 2},/obj/structure/cable/cyan,/turf/simulated/floor/plating,/area/turret_protected/ai_upload)
"bvM" = (/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{dir = 4},/turf/simulated/floor/tiled/dark{name = "cooled dark floor"; temperature = 278},/area/turret_protected/ai_upload)
"bvN" = (/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 4},/turf/simulated/floor/bluegrid{name = "cooled mainframe floor"; temperature = 278},/area/turret_protected/ai_upload)
"bvO" = (/turf/simulated/shuttle/wall{tag = "icon-swall_straight (EAST)"; icon_state = "swall_straight"; dir = 4},/area/shuttle/research/station)
-"bvP" = (/turf/simulated/shuttle/wall{tag = "icon-swall_s"; icon_state = "swall_s"},/area/shuttle/research/station)
+"bvP" = (/turf/space,/turf/simulated/shuttle/wall{tag = "icon-swall_c"; icon_state = "swall_c"},/area/shuttle/research/station)
"bvQ" = (/obj/machinery/conveyor_switch/oneway{id = "QMLoad2"},/obj/effect/floor_decal/industrial/warning{tag = "icon-warning (SOUTHWEST)"; icon_state = "warning"; dir = 10},/turf/simulated/floor/tiled,/area/quartermaster/loading)
"bvR" = (/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 4},/turf/simulated/floor/tiled/dark{name = "cooled dark floor"; temperature = 278},/area/turret_protected/ai_upload)
"bvS" = (/obj/structure/table/standard,/obj/item/weapon/aiModule/freeform,/obj/machinery/atmospherics/unary/vent_pump/on{dir = 8},/obj/item/weapon/aiModule/protectStation,/turf/simulated/floor/bluegrid{name = "cooled mainframe floor"; temperature = 278},/area/turret_protected/ai_upload)
@@ -4172,8 +4172,8 @@
"bCl" = (/obj/structure/closet/crate{name = "Camera Assembly Crate"},/obj/item/weapon/camera_assembly,/obj/item/weapon/camera_assembly,/obj/item/weapon/camera_assembly,/obj/item/weapon/camera_assembly,/turf/simulated/floor/bluegrid,/area/turret_protected/ai_cyborg_station)
"bCm" = (/obj/machinery/requests_console{announcementConsole = 1; department = "Research Director's Desk"; departmentType = 5; name = "Research Director RC"; pixel_x = -2; pixel_y = 30},/obj/structure/window/reinforced{dir = 4},/obj/effect/floor_decal/corner/white/diagonal{tag = "icon-corner_white_diagonal (EAST)"; icon_state = "corner_white_diagonal"; dir = 4},/obj/item/modular_computer/console/preset/research,/turf/simulated/floor/tiled,/area/crew_quarters/heads/hor)
"bCn" = (/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 4},/turf/simulated/floor/tiled/dark,/area/turret_protected/ai_server_room)
-"bCo" = (/turf/simulated/shuttle/wall{tag = "icon-swall_s (NORTH)"; icon_state = "swall_s"; dir = 1},/area/shuttle/research/station)
-"bCp" = (/turf/simulated/shuttle/wall{tag = "icon-swall_s (WEST)"; icon_state = "swall_s"; dir = 8},/area/shuttle/research/station)
+"bCo" = (/turf/space,/turf/simulated/shuttle/wall{tag = "icon-swall_c (NORTH)"; icon_state = "swall_c"; dir = 1},/area/shuttle/research/station)
+"bCp" = (/turf/space,/turf/simulated/shuttle/wall{tag = "icon-swall_c (WEST)"; icon_state = "swall_c"; dir = 8},/area/shuttle/research/station)
"bCq" = (/obj/machinery/conveyor_switch/oneway{convdir = -1; id = "QMLoad"},/turf/simulated/floor/tiled,/area/quartermaster/loading)
"bCr" = (/obj/effect/floor_decal/industrial/warning{tag = "icon-warning (WEST)"; icon_state = "warning"; dir = 8},/turf/simulated/floor/tiled,/area/quartermaster/loading)
"bCs" = (/obj/machinery/atmospherics/unary/vent_scrubber/on{dir = 1},/turf/simulated/floor/tiled,/area/quartermaster/loading)
@@ -4568,8 +4568,8 @@
"bJR" = (/obj/structure/grille,/obj/structure/window/reinforced{dir = 8},/obj/structure/window/reinforced{dir = 1},/turf/simulated/floor/airless,/area/rnd/test_area)
"bJS" = (/obj/structure/grille,/obj/structure/window/reinforced,/obj/structure/window/reinforced{dir = 1},/obj/structure/window/reinforced{dir = 4},/turf/simulated/floor/airless,/area/rnd/test_area)
"bJT" = (/turf/simulated/shuttle/wall{tag = "icon-swall_straight (EAST)"; icon_state = "swall_straight"; dir = 4},/area/shuttle/mining/station)
-"bJU" = (/turf/simulated/shuttle/wall{tag = "icon-swall_s (EAST)"; icon_state = "swall_s"; dir = 4},/area/shuttle/mining/station)
-"bJV" = (/turf/simulated/shuttle/wall{tag = "icon-swall_s"; icon_state = "swall_s"},/area/shuttle/mining/station)
+"bJU" = (/turf/space,/turf/simulated/shuttle/wall{tag = "icon-swall_c (EAST)"; icon_state = "swall_c"; dir = 4},/area/shuttle/mining/station)
+"bJV" = (/turf/space,/turf/simulated/shuttle/wall{tag = "icon-swall_c"; icon_state = "swall_c"},/area/shuttle/mining/station)
"bJW" = (/turf/simulated/floor/tiled,/area/quartermaster/miningdock)
"bJX" = (/obj/machinery/computer/security/mining,/obj/item/device/radio/intercom{dir = 0; name = "Station Intercom (General)"; pixel_x = -27},/obj/machinery/camera/network/civilian_west{c_tag = "Cargo Mining Dock"; dir = 4},/turf/simulated/floor/tiled,/area/quartermaster/miningdock)
"bJY" = (/obj/machinery/vending/medical,/turf/simulated/wall,/area/medical/medbay)
@@ -5021,14 +5021,14 @@
"bSC" = (/obj/machinery/door/airlock/engineering{name = "Tech Storage"; req_access = list(23)},/obj/structure/cable/green{d1 = 4; d2 = 8; icon_state = "4-8"},/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{dir = 4},/obj/machinery/atmospherics/pipe/simple/hidden/supply{dir = 4},/obj/machinery/door/firedoor/border_only,/turf/simulated/floor/plating,/area/hallway/primary/aft)
"bSD" = (/obj/structure/bed/chair{dir = 4},/obj/machinery/computer/security/telescreen{desc = "Used for watching the test chamber."; layer = 4; name = "Test Chamber Telescreen"; network = list("Toxins Test Area"); pixel_x = 32; pixel_y = 0},/obj/machinery/atmospherics/unary/vent_pump/on{dir = 8},/obj/effect/floor_decal/industrial/warning/corner{dir = 8},/turf/simulated/floor/tiled,/area/rnd/mixing)
"bSE" = (/obj/effect/floor_decal/industrial/warning{dir = 4},/turf/simulated/floor/tiled/airless,/area/rnd/test_area)
-"bSF" = (/turf/simulated/shuttle/wall{tag = "icon-swall_s (NORTH)"; icon_state = "swall_s"; dir = 1},/area/shuttle/mining/station)
+"bSF" = (/turf/space,/turf/simulated/shuttle/wall{tag = "icon-swall_c (NORTH)"; icon_state = "swall_c"; dir = 1},/area/shuttle/mining/station)
"bSG" = (/obj/machinery/door/airlock/maintenance{name = "Custodial Maintenance"; req_access = list(26)},/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/machinery/atmospherics/pipe/simple/hidden/supply,/obj/structure/cable/green{d1 = 1; d2 = 2; icon_state = "1-2"},/obj/machinery/door/firedoor/border_only,/turf/simulated/floor/plating,/area/janitor)
"bSH" = (/obj/machinery/power/apc{dir = 8; name = "west bump"; pixel_x = -24},/obj/structure/cable{icon_state = "0-4"; d2 = 4},/turf/simulated/floor/plating,/area/maintenance/engineering)
"bSI" = (/obj/structure/grille/broken,/obj/structure/cable{d1 = 4; d2 = 8; icon_state = "4-8"; pixel_x = 0},/turf/simulated/floor/plating,/area/maintenance/engineering)
"bSJ" = (/obj/structure/grille,/obj/structure/window/reinforced,/obj/structure/window/reinforced{dir = 4},/obj/structure/cable{d1 = 2; d2 = 8; icon_state = "2-8"},/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"},/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/machinery/atmospherics/pipe/simple/hidden/supply,/turf/simulated/floor/plating,/area/maintenance/engineering)
"bSK" = (/obj/machinery/space_heater,/turf/simulated/floor/plating,/area/maintenance/engineering)
"bSL" = (/obj/structure/closet/crate,/obj/random/coin,/turf/simulated/floor/plating,/area/storage/emergency)
-"bSM" = (/turf/simulated/shuttle/wall{tag = "icon-swall_s (WEST)"; icon_state = "swall_s"; dir = 8},/area/shuttle/mining/station)
+"bSM" = (/turf/space,/turf/simulated/shuttle/wall{tag = "icon-swall_c (WEST)"; icon_state = "swall_c"; dir = 8},/area/shuttle/mining/station)
"bSN" = (/turf/simulated/floor/tiled,/area/storage/tech)
"bSO" = (/obj/structure/table/rack{dir = 8; layer = 2.9},/obj/item/weapon/circuitboard/robotics{pixel_x = -2; pixel_y = 2},/obj/item/weapon/circuitboard/mecha_control{pixel_x = 1; pixel_y = -1},/turf/simulated/floor/tiled,/area/storage/tech)
"bSP" = (/obj/structure/table/rack{dir = 8; layer = 2.9},/obj/item/weapon/circuitboard/powermonitor{pixel_x = -2; pixel_y = 2},/obj/item/weapon/circuitboard/stationalert{pixel_x = 1; pixel_y = -1},/obj/item/weapon/circuitboard/security/engineering,/obj/item/weapon/circuitboard/atmos_alert{pixel_x = 3; pixel_y = -3},/turf/simulated/floor/plating,/area/storage/tech)
@@ -5169,7 +5169,7 @@
"bVu" = (/obj/item/device/radio/beacon,/obj/effect/landmark{name = "blobstart"},/obj/effect/floor_decal/industrial/warning/full,/turf/simulated/floor/tiled,/area/rnd/test_area)
"bVv" = (/obj/machinery/light{dir = 4},/obj/machinery/camera/network/research{c_tag = "Research Toxins Test Chamber East"; dir = 8; network = list("Research","Toxins Test Area")},/turf/simulated/floor/tiled/airless,/area/rnd/test_area)
"bVw" = (/turf/simulated/shuttle/wall{tag = "icon-swall_straight (EAST)"; icon_state = "swall_straight"; dir = 4},/area/shuttle/escape_pod5/station)
-"bVx" = (/turf/simulated/shuttle/wall{tag = "icon-swall_s (EAST)"; icon_state = "swall_s"; dir = 4},/area/shuttle/escape_pod5/station)
+"bVx" = (/turf/space,/turf/simulated/shuttle/wall{tag = "icon-swall_c (EAST)"; icon_state = "swall_c"; dir = 4},/area/shuttle/escape_pod5/station)
"bVy" = (/obj/machinery/door/airlock/glass{name = "Central Access"},/obj/structure/cable/green{d1 = 1; d2 = 2; icon_state = "1-2"},/turf/simulated/floor/tiled,/area/hallway/primary/aft)
"bVz" = (/obj/machinery/door/airlock/glass{name = "Central Access"},/obj/machinery/atmospherics/pipe/simple/hidden/supply,/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/effect/floor_decal/corner/yellow{tag = "icon-corner_white (WEST)"; icon_state = "corner_white"; dir = 8},/turf/simulated/floor/tiled,/area/hallway/primary/aft)
"bVA" = (/obj/machinery/door/window/eastleft{name = "Medical Delivery"; req_access = list(5)},/obj/machinery/door/firedoor,/turf/simulated/floor/plating,/area/medical/sleeper)
@@ -5324,7 +5324,7 @@
"bYt" = (/obj/machinery/atmospherics/pipe/simple/hidden/cyan{dir = 6; icon_state = "intact"; tag = "icon-intact-f (SOUTHEAST)"},/obj/effect/floor_decal/industrial/warning{dir = 9},/turf/simulated/floor/plating,/area/maintenance/research_starboard)
"bYu" = (/obj/machinery/atmospherics/pipe/tank/air{dir = 8},/obj/effect/floor_decal/industrial/warning{dir = 5},/turf/simulated/floor/plating,/area/maintenance/research_starboard)
"bYv" = (/obj/machinery/atmospherics/pipe/manifold/hidden/cyan{tag = "icon-map (NORTH)"; icon_state = "map"; dir = 1},/obj/machinery/light/small{dir = 1},/obj/effect/floor_decal/industrial/warning{dir = 1},/turf/simulated/floor/plating,/area/maintenance/research_starboard)
-"bYw" = (/turf/simulated/shuttle/wall{tag = "icon-swall_s (NORTH)"; icon_state = "swall_s"; dir = 1},/area/shuttle/escape_pod5/station)
+"bYw" = (/turf/space,/turf/simulated/shuttle/wall{tag = "icon-swall_c (NORTH)"; icon_state = "swall_c"; dir = 1},/area/shuttle/escape_pod5/station)
"bYx" = (/obj/item/modular_computer/telescreen/preset/generic{pixel_y = 32},/turf/simulated/floor/tiled,/area/crew_quarters/heads/chief)
"bYy" = (/obj/structure/table/reinforced,/obj/machinery/light{dir = 1},/obj/machinery/light_switch{pixel_x = 0; pixel_y = 27},/obj/machinery/computer/skills{icon_state = "medlaptop"},/turf/simulated/floor/tiled,/area/crew_quarters/heads/chief)
"bYz" = (/obj/effect/floor_decal/corner/white/diagonal,/obj/item/modular_computer/console/preset/engineering,/turf/simulated/floor/tiled,/area/engineering/break_room)
@@ -7071,8 +7071,8 @@
"cFY" = (/obj/structure/cable{d1 = 1; d2 = 4; icon_state = "1-4"},/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/machinery/atmospherics/pipe/manifold/hidden/supply{dir = 4},/obj/structure/cable{d1 = 2; d2 = 4; icon_state = "2-4"},/turf/simulated/floor/plating,/area/engineering/drone_fabrication)
"cFZ" = (/obj/structure/table/reinforced,/obj/item/weapon/storage/toolbox/electrical,/obj/structure/cable{d1 = 4; d2 = 8; icon_state = "4-8"; pixel_x = 0},/obj/machinery/camera/network/engineering{c_tag = "Engineering Drone Fabrication"; dir = 8},/turf/simulated/floor/plating,/area/engineering/drone_fabrication)
"cGa" = (/obj/structure/cable{d1 = 4; d2 = 8; icon_state = "4-8"; pixel_x = 0},/turf/simulated/wall/r_wall,/area/engineering/engine_smes)
-"cGb" = (/turf/simulated/shuttle/wall{tag = "icon-swall_s (EAST)"; icon_state = "swall_s"; dir = 4},/area/shuttle/constructionsite/station)
-"cGc" = (/turf/simulated/shuttle/wall{tag = "icon-swall_s"; icon_state = "swall_s"},/area/shuttle/constructionsite/station)
+"cGb" = (/turf/space,/turf/simulated/shuttle/wall{tag = "icon-swall_c (EAST)"; icon_state = "swall_c"; dir = 4},/area/shuttle/constructionsite/station)
+"cGc" = (/turf/space,/turf/simulated/shuttle/wall{tag = "icon-swall_c"; icon_state = "swall_c"},/area/shuttle/constructionsite/station)
"cGd" = (/obj/structure/table/rack,/obj/machinery/atmospherics/pipe/manifold/hidden/cyan,/obj/random/loot,/obj/effect/floor_decal/industrial/warning,/turf/simulated/floor/plating,/area/maintenance/engineering)
"cGe" = (/obj/structure/table/rack{dir = 1},/obj/machinery/atmospherics/pipe/simple/hidden/cyan{dir = 5; icon_state = "intact"; tag = "icon-intact-f (NORTHWEST)"},/obj/random/loot,/obj/effect/floor_decal/industrial/warning{icon_state = "warning"; dir = 10},/turf/simulated/floor/plating,/area/maintenance/engineering)
"cGf" = (/obj/machinery/atmospherics/pipe/simple/hidden/cyan{dir = 9; icon_state = "intact"; tag = "icon-intact-f (NORTHWEST)"},/obj/machinery/meter,/obj/machinery/space_heater,/obj/effect/floor_decal/industrial/warning{dir = 6},/turf/simulated/floor/plating,/area/maintenance/engineering)
@@ -7104,6 +7104,7 @@
"cGF" = (/obj/effect/floor_decal/industrial/warning,/obj/machinery/computer/rcon,/turf/simulated/floor/tiled,/area/engineering/engine_monitoring)
"cGG" = (/obj/effect/floor_decal/industrial/warning,/obj/item/modular_computer/console/preset/engineering,/turf/simulated/floor/tiled,/area/engineering/engine_monitoring)
"cGH" = (/obj/machinery/computer/station_alert,/obj/effect/floor_decal/industrial/warning,/turf/simulated/floor/tiled,/area/engineering/engine_monitoring)
+"cGI" = (/turf/space,/turf/simulated/shuttle/wall{tag = "icon-swall_c (NORTH)"; icon_state = "swall_c"; dir = 1},/area/shuttle/constructionsite/station)
"cGJ" = (/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"},/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/machinery/atmospherics/pipe/simple/hidden/supply,/turf/simulated/floor/plating,/area/engineering/drone_fabrication)
"cGK" = (/obj/machinery/airlock_sensor{frequency = 1379; id_tag = "eng_al_c_snsr"; pixel_x = -25; pixel_y = 0},/obj/item/device/radio/intercom{name = "Station Intercom (General)"; pixel_y = -29},/obj/effect/floor_decal/industrial/warning{icon_state = "warning"; dir = 10},/turf/simulated/floor/plating,/area/engineering/engine_airlock)
"cGL" = (/obj/machinery/light/small{dir = 4},/obj/effect/floor_decal/industrial/warning{dir = 6},/turf/simulated/floor/plating,/area/engineering/engine_airlock)
@@ -7139,8 +7140,7 @@
"cHp" = (/obj/machinery/atmospherics/pipe/simple/visible/yellow{dir = 6},/obj/effect/floor_decal/industrial/warning{dir = 8},/turf/simulated/floor/plating,/area/engineering/engine_room)
"cHq" = (/obj/structure/cable/yellow{d1 = 1; d2 = 4; icon_state = "1-4"},/turf/simulated/floor/plating,/area/engineering/engine_room)
"cHr" = (/obj/machinery/atmospherics/pipe/manifold/visible/cyan{tag = "icon-map (EAST)"; icon_state = "map"; dir = 4},/turf/simulated/floor/plating,/area/engineering/engine_room)
-"cHs" = (/turf/simulated/shuttle/wall{tag = "icon-swall_s (NORTH)"; icon_state = "swall_s"; dir = 1},/area/shuttle/constructionsite/station)
-"cHt" = (/turf/simulated/shuttle/wall{tag = "icon-swall_s (WEST)"; icon_state = "swall_s"; dir = 8},/area/shuttle/constructionsite/station)
+"cHs" = (/turf/space,/turf/simulated/shuttle/wall{tag = "icon-swall_c (WEST)"; icon_state = "swall_c"; dir = 8},/area/shuttle/constructionsite/station)
"cHu" = (/obj/structure/cable/yellow{d1 = 1; d2 = 2; icon_state = "1-2"},/obj/structure/cable/yellow{d1 = 2; d2 = 4; icon_state = "2-4"},/obj/structure/cable/yellow{d1 = 2; d2 = 8; icon_state = "2-8"},/turf/simulated/floor/airless,/area/solar/port)
"cHv" = (/obj/machinery/atmospherics/unary/heat_exchanger{dir = 8},/obj/effect/floor_decal/industrial/warning{dir = 9},/turf/simulated/floor/plating,/area/engineering/engine_waste)
"cHw" = (/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"},/obj/machinery/atmospherics/pipe/manifold/visible/yellow{dir = 1},/obj/machinery/atmospherics/pipe/simple/hidden/supply,/obj/effect/floor_decal/industrial/warning{tag = "icon-warning (EAST)"; icon_state = "warning"; dir = 4},/turf/simulated/floor/plating,/area/engineering/engine_waste)
@@ -7561,7 +7561,7 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaaaaaaaJaafaafaaachCcFFcGgcFFchCcFHcGhcFHchCcFJcGicFJchCaafchCcFJcGjcFJchCaafaafaafcHhcHicGycGxcGAcGzcHncEKcDXcETcGDcGCcEOcGEcGFcrRcGGcGHcEOcGKcHxcGLcEeaaacHzcHAcHBcGMcHDcHEcGNcHGcMGcHIbOsbOsaaaaaaaaaaafaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacrgcHJcricricricCpcrgaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacswcswcswcswcswaaaaaaaaaaaaaaaaaacyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaBaaaaaachCchCchCchCchCchCchCchCchCchCchCchCchCaaacHKchCchCchCchCaaaaaaaaaaaacCScCScCScCScCScHLcHMcHMcHMcHMcHNcHMcHOcHPcHQcHPcHRcHMcHMcHScHMcHMcHMcHzcHAcHTcGOcHVcHWcHXaafaaaaaaaaaaaaaaaaaaaaaaafaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacswaaaaaaaafaafaaaaafaafaaaaaaaafaafaafaaaaaaaaaaaaaaaaaaaafaaacHYcHZcHZcHZcIacGPcGQcIdcIecGRcGTcIhcGUcIjcGYcGVcHdcIrcIocIpcIqcIrcIscHecIucIvcHecHMcHzcHAcIwcHfcIycIzcGNaafaaacHYcIBcHYcIBcHYcIBaafcICaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaamaafcHgcHkcHjaaacHgcHkcHjaaacHgcHkcHjaafaafaaaaafaafaafaafaafcIGcHZcHZcHZcIacGPcHlcIIcIJcHmcHocIMcHpcIOcHrcHqcITcITcITcIUcITcITcITcIVcITcIWcIXcHMcHscFXcFXcFXcFXcFXcHtaafaafcJacJacJacJacJacJaaafcICaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaacyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaamaafcHgcHkcHjaaacHgcHkcHjaaacHgcHkcHjaafaafaaaaafaafaafaafaafcIGcHZcHZcHZcIacGPcHlcIIcIJcHmcHocIMcHpcIOcHrcHqcITcITcITcIUcITcITcITcIVcITcIWcIXcHMcGIcFXcFXcFXcFXcFXcHsaafaafcJacJacJacJacJacJaaafcICaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaaaaaaaaaaaaaaaaaacyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaamaaacHgcHucHjaaacHgcHucHjaaacHgcHucHjaafaafaaaaafaaaaaacJcaafcHYcHZcHZcHZcIacGPcHlcHvcHycHwcHCcIMcHFcJicJjcIucIucIucIbcHUcJHcIucIucJicJncJocJpcHMcHMcHMcJqaaaaaaaaaaaabvcaaacJrcJrcJrcJrcJrcJraafcICaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaJaafcHgcHucHjaaacHgcHucHjaafcHgcHucHjaafaaaaaaaafaafcJsaaaaafcIGcHZcHZcHZcIacGPcIfcIccIicIgcIlcIkcImcJAcJBcJCcJCcJCcHrcIncLbcJCcJCcJGcJHcJIcJJcJKcJLcJMcJNcJOcJOcJOcJOcJOcJOcJPcJQcJPcJQcJPcJaaafcICaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFcyFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFcuFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaafaaacHgcHucHjaafcHgcHucHjaaacHgcHucHjaaaaaaaaaaaaaafaafaaaaafaaaaaaaaaaaaaafcJRcJScJTcJUcItcIxcIhcIucIucIucIucIDcIAcIFcIEcIKcIHcILcKfcKgcKhcKicKjcINcKlcJNcKmcKncKmcKncKmcKncKmcKncKmcKncKmcJaaafcICaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacyFcyFcyFcyFcyFcyFcyFcyFcyFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
diff --git a/nano/templates/pda.tmpl b/nano/templates/pda.tmpl
index 6baf2781474..bdb9f82f7d2 100644
--- a/nano/templates/pda.tmpl
+++ b/nano/templates/pda.tmpl
@@ -213,6 +213,20 @@ Used In File(s): \code\game\objects\items\devices\PDA\PDA.dm
{{:helper.link('Set Ringtone', 'comment', {'choice' : "Ringtone"}, null, 'fixedLeftWide')}}
{{:helper.link('Delete all Conversations', 'trash', {'choice' : "Clear", 'option' : "All"}, null, 'fixedLeftWider')}}
+
+ Filter PDAs
+
+
+ {{:helper.link( 'All',data.pdafilter == 0? 'null' : 'close', {'choice' : "Filter", 'option' : "0"}, null, 'fixedLeft')}}
+ {{:helper.link('Command',data.pdafilter == 2? 'null': 'close', {'choice' : "Filter", 'option' : "2"}, null, 'fixedLeft')}}
+ {{:helper.link('Security',data.pdafilter == 3? 'null': 'close', {'choice' : "Filter", 'option' : "3"}, null, 'fixedLeft')}}
+ {{:helper.link('Engineering',data.pdafilter == 4? 'null': 'close', {'choice' : "Filter", 'option' : "4"}, null, 'fixedLeft')}}
+ {{:helper.link('Science',data.pdafilter == 5? 'null': 'close', {'choice' : "Filter", 'option' : "5"}, null, 'fixedLeft')}}
+ {{:helper.link('Cargo',data.pdafilter == 6? 'null': 'close', {'choice' : "Filter", 'option' : "6"}, null, 'fixedLeft')}}
+ {{:helper.link('Service',data.pdafilter == 7? 'null': 'close', {'choice' : "Filter", 'option' : "7"}, null, 'fixedLeft')}}
+ {{:helper.link('Medical',data.pdafilter == 8? 'null': 'close', {'choice' : "Filter", 'option' : "8"}, null, 'fixedLeft')}}
+
+
{{if data.toff == 0}}