diff --git a/code/controllers/subsystems/assets.dm b/code/controllers/subsystems/assets.dm
index cd531db614..2041104a2b 100644
--- a/code/controllers/subsystems/assets.dm
+++ b/code/controllers/subsystems/assets.dm
@@ -6,9 +6,11 @@ SUBSYSTEM_DEF(assets)
var/list/preload = list()
/datum/controller/subsystem/assets/Initialize(timeofday)
- for(var/type in typesof(/datum/asset) - list(/datum/asset, /datum/asset/simple))
- var/datum/asset/A = new type()
- A.register()
+ for(var/type in typesof(/datum/asset))
+ var/datum/asset/A = type
+ if (type != initial(A._abstract))
+ get_asset_datum(type)
+
preload = cache.Copy() //don't preload assets generated during the round
diff --git a/code/modules/client/asset_cache.dm b/code/modules/client/asset_cache.dm
index ef64cd4acd..0206b901dc 100644
--- a/code/modules/client/asset_cache.dm
+++ b/code/modules/client/asset_cache.dm
@@ -137,8 +137,12 @@ You can set verify to TRUE if you want send() to sleep until the client has the
return new type()
return asset_datums[type]
+/datum/asset
+ var/_abstract = /datum/asset // Marker so we don't instanatiate abstract types
+
/datum/asset/New()
asset_datums[type] = src
+ register()
/datum/asset/proc/register()
return
@@ -148,6 +152,7 @@ You can set verify to TRUE if you want send() to sleep until the client has the
//If you don't need anything complicated.
/datum/asset/simple
+ _abstract = /datum/asset/simple
var/assets = list()
var/verify = FALSE
@@ -157,6 +162,65 @@ You can set verify to TRUE if you want send() to sleep until the client has the
/datum/asset/simple/send(client)
send_asset_list(client,assets,verify)
+//
+// iconsheet Assets - For making lots of icon states available at once without sending a thousand tiny files.
+//
+/datum/asset/iconsheet
+ _abstract = /datum/asset/iconsheet
+ var/name // Name of the iconsheet. Asset will be named after this.
+ var/verify = FALSE
+
+/datum/asset/iconsheet/register(var/list/sprites)
+ if (!name)
+ CRASH("iconsheet [type] cannot register without a name")
+ if (!islist(sprites))
+ CRASH("iconsheet [type] cannot register without a sprites list")
+
+ var/res_name = "iconsheet_[name].css"
+ var/fname = "data/iconsheets/[res_name]"
+ fdel(fname)
+ text2file(generate_css(sprites), fname)
+ register_asset(res_name, fcopy_rsc(fname))
+ fdel(fname)
+
+/datum/asset/iconsheet/send(client/C)
+ if (!name)
+ return
+ send_asset_list(C, list("iconsheet_[name].css"), verify)
+
+/datum/asset/iconsheet/proc/generate_css(var/list/sprites)
+ var/list/out = list(".[name]{display:inline-block;}")
+ for(var/sprite_id in sprites)
+ var/icon/I = sprites[sprite_id]
+ var/data_url = "'data:image/png;base64,[icon2base64(I)]'"
+ out += ".[name].[sprite_id]{width:[I.Width()]px;height:[I.Height()]px;background-image:url([data_url]);}"
+ return out.Join("\n")
+
+/datum/asset/iconsheet/proc/build_sprite_list(icon/I, list/directions, prefix = null)
+ if (length(prefix))
+ prefix = "[prefix]-"
+
+ if (!directions)
+ directions = list(SOUTH)
+
+ var/sprites = list()
+ for (var/icon_state_name in icon_states(I))
+ for (var/direction in directions)
+ var/suffix = (directions.len > 1) ? "-[dir2text(direction)]" : ""
+ var/sprite_name = "[prefix][icon_state_name][suffix]"
+ var/icon/sprite = icon(I, icon_state=icon_state_name, dir=direction, frame=1, moving=FALSE)
+ if (!sprite || !length(icon_states(sprite))) // that direction or state doesn't exist
+ continue
+ sprites[sprite_name] = sprite
+ return sprites
+
+// Get HTML link tag for including the iconsheet css file.
+/datum/asset/iconsheet/proc/css_tag()
+ return ""
+
+// get HTML tag for showing an icon
+/datum/asset/iconsheet/proc/icon_tag(icon_state, dir = SOUTH)
+ return ""
//DEFINITIONS FOR ASSET DATUMS START HERE.
/datum/asset/simple/pda