diff --git a/code/game/objects/effects/decals/contraband.dm b/code/game/objects/effects/decals/contraband.dm
index 5dd0707fcb9..1e62b6a5a9e 100644
--- a/code/game/objects/effects/decals/contraband.dm
+++ b/code/game/objects/effects/decals/contraband.dm
@@ -352,12 +352,12 @@ obj/structure/sign/poster/attackby(obj/item/I, mob/user, params)
if(!P.resulting_poster) return
var/stuff_on_wall = 0
- for(var/obj/O in contents) //Let's see if it already has a poster on it or too much stuff
- if(istype(O,/obj/structure/sign/poster))
+ for(var/obj/O in user.loc.contents) //Let's see if it already has a poster on it or too much stuff
+ if(istype(O,/obj/structure/sign))
user << "The wall is far too cluttered to place a poster!"
return
stuff_on_wall++
- if(stuff_on_wall == 3)
+ if(stuff_on_wall >= 4)
user << "The wall is far too cluttered to place a poster!"
return
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index 11db68ec671..f90fe9cbb02 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -152,6 +152,7 @@ var/global/list/datum/stack_recipe/wood_recipes = list ( \
new/datum/stack_recipe("coffin", /obj/structure/closet/coffin, 5, time = 15, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("apiary", /obj/item/apiary, 10, time = 25, one_per_turf = 0, on_floor = 0), \
new/datum/stack_recipe("easel", /obj/structure/easel, 3, one_per_turf = 1, on_floor = 1), \
+ new/datum/stack_recipe("wooden picture frame", /obj/item/weapon/picture_frame/wooden, 1), \
new/datum/stack_recipe("wooden buckler", /obj/item/weapon/shield/riot/buckler, 20, time = 40), \
)
diff --git a/code/game/objects/structures/artstuff.dm b/code/game/objects/structures/artstuff.dm
index 90792b54602..6604649ad4c 100644
--- a/code/game/objects/structures/artstuff.dm
+++ b/code/game/objects/structures/artstuff.dm
@@ -111,14 +111,16 @@ var/global/list/globalBlankCanvases[AMT_OF_CANVASES]
if(thePix != theOriginalPix) //colour changed
DrawPixelOn(theOriginalPix,pixX,pixY)
qdel(masterpiece)
- return
+ return 1
//Drawing one pixel with a crayon
if(istype(I, /obj/item/toy/crayon))
var/obj/item/toy/crayon/C = I
- if(masterpiece.GetPixel(pixX, pixY)) // if the located pixel isn't blank (null))
+ var/pix = masterpiece.GetPixel(pixX, pixY)
+ if(pix && pix != C.colour) // if the located pixel isn't blank (null))
DrawPixelOn(C.colour, pixX, pixY)
- return
+ qdel(masterpiece)
+ return 1
..()
diff --git a/code/modules/paperwork/frames.dm b/code/modules/paperwork/frames.dm
new file mode 100644
index 00000000000..f8592e35c5d
--- /dev/null
+++ b/code/modules/paperwork/frames.dm
@@ -0,0 +1,315 @@
+/obj/item/weapon/picture_frame
+ name = "picture frame"
+ desc = "Its patented design allows it to be folded larger or smaller to accommodate standard paper, photo, and poster, and canvas sizes."
+ icon = 'icons/obj/bureaucracy.dmi'
+
+ var/icon_base
+ var/obj/displayed
+
+ var/list/wide_posters = list(
+ "poster22_legit", "poster23", "poster23_legit", "poster24", "poster24_legit",
+ "poster25", "poster27_legit", "poster28", "poster29")
+
+/obj/item/weapon/picture_frame/New(loc, obj/item/weapon/D)
+ ..()
+ if(D)
+ insert(D)
+ update_icon()
+
+/obj/item/weapon/picture_frame/Destroy()
+ if(displayed)
+ displayed = null
+ for(var/A in contents)
+ qdel(A)
+ return ..()
+
+/obj/item/weapon/picture_frame/update_icon()
+ overlays.Cut()
+
+ if(displayed)
+ overlays |= getFlatIcon(displayed)
+
+ if(istype(displayed, /obj/item/weapon/photo))
+ icon_state = "[icon_base]-photo"
+ else if(istype(displayed, /obj/structure/sign/poster))
+ icon_state = "[icon_base]-[(displayed.icon_state in wide_posters) ? "wposter" : "poster"]"
+ else if(istype(displayed, /obj/item/weapon/canvas))
+ icon_state = "[icon_base]-canvas-[displayed.icon_state]"
+ else
+ icon_state = "[icon_base]-paper"
+
+ overlays |= icon_state
+
+/obj/item/weapon/picture_frame/proc/insert(obj/D)
+ if(istype(D, /obj/item/weapon/contraband/poster))
+ var/obj/item/weapon/contraband/poster/P = D
+ displayed = P.resulting_poster
+ P.resulting_poster = null
+ else
+ displayed = D
+
+ name = displayed.name
+ displayed.pixel_x = 0
+ displayed.pixel_y = 0
+ displayed.forceMove(src)
+ if(istype(D, /obj/item/weapon/contraband/poster))
+ qdel(D)
+
+/obj/item/weapon/picture_frame/attackby(obj/item/I, mob/user)
+ if(istype(I, /obj/item/weapon/screwdriver))
+ if(displayed)
+ playsound(src, 'sound/items/Screwdriver.ogg', 100, 1)
+ user.visible_message("[user] unfastens \the [displayed] out of \the [src].", "You unfasten \the [displayed] out of \the [src].")
+
+ if(istype(displayed, /obj/structure/sign/poster))
+ var/obj/structure/sign/poster/P = displayed
+ P.roll_and_drop(user.loc)
+ else
+ displayed.forceMove(user.loc)
+ displayed = null
+ name = initial(name)
+ update_icon()
+ else
+ user << "There is nothing to remove from \the [src]."
+ else if(istype(I, /obj/item/weapon/crowbar))
+ playsound(src, 'sound/items/Crowbar.ogg', 100, 1)
+ user.visible_message("[user] breaks down \the [src].", "You break down \the [src].")
+ for(var/A in contents)
+ if(istype(A, /obj/structure/sign/poster))
+ var/obj/structure/sign/poster/P = A
+ P.roll_and_drop(user.loc)
+ else
+ var/obj/O = A
+ O.forceMove(user.loc)
+ displayed = null
+ qdel(src)
+ else if(istype(I, /obj/item/weapon/paper) || istype(I, /obj/item/weapon/photo) || istype(I, /obj/item/weapon/contraband/poster) || istype(I, /obj/item/weapon/canvas))
+ if(!displayed)
+ user.unEquip(I)
+ insert(I)
+ update_icon()
+ else
+ user << "\The [src] already contains \a [displayed]."
+ else
+ return ..()
+
+/obj/item/weapon/picture_frame/afterattack(atom/target, mob/user, proximity_flag)
+ if(proximity_flag && istype(target, /turf/simulated/wall))
+ place(target, user)
+ else
+ ..()
+
+/obj/item/weapon/picture_frame/proc/place(turf/T, mob/user)
+ var/stuff_on_wall = 0
+ for(var/obj/O in user.loc.contents) //Let's see if it already has a poster on it or too much stuff
+ if(istype(O, /obj/structure/sign))
+ user << "\The [T] is far too cluttered to place \a [src]!"
+ return
+ stuff_on_wall++
+ if(stuff_on_wall >= 4)
+ user << "\The [T] is far too cluttered to place \a [src]!"
+ return
+
+ user << "You start place \the [src] on \the [T]."
+
+ var/px = 0
+ var/py = 0
+ var/newdir = getRelativeDirection(user, T)
+
+ switch(newdir)
+ if(NORTH)
+ py = 32
+ if(EAST)
+ px = 32
+ if(SOUTH)
+ py = -32
+ if(WEST)
+ px = -32
+ else
+ user << "You cannot reach \the [T] from here!"
+ return
+
+ user.unEquip(src)
+ var/obj/structure/sign/picture_frame/PF = new(user.loc, src)
+ PF.dir = newdir
+ PF.pixel_x = px
+ PF.pixel_y = py
+
+ playsound(PF.loc, 'sound/items/Deconstruct.ogg', 100, 1)
+
+/obj/item/weapon/picture_frame/examine(mob/user, var/distance = -1, var/infix = "", var/suffix = "")
+ ..()
+ if(displayed)
+ displayed.examine(user, distance, infix, suffix)
+
+/obj/item/weapon/picture_frame/attack_self(mob/user)
+ if(displayed)
+ if(istype(displayed, /obj/item))
+ var/obj/item/I = displayed
+ I.attack_self(user)
+ else
+ ..()
+
+
+
+/obj/item/weapon/picture_frame/glass
+ icon_base = "glass"
+ icon_state = "glass-poster"
+ materials = list(MAT_METAL = 25, MAT_GLASS = 75)
+
+/obj/item/weapon/picture_frame/wooden
+ icon_base = "wood"
+ icon_state = "wood-poster"
+
+/obj/item/weapon/picture_frame/wooden/New()
+ ..()
+ new /obj/item/stack/sheet/wood(src, 1)
+
+
+
+/obj/structure/sign/picture_frame
+ icon = 'icons/obj/bureaucracy.dmi'
+ icon_state = "glass-poster"
+
+ var/obj/item/weapon/picture_frame/frame
+ var/obj/item/weapon/explosive
+
+ var/tilted = 0
+ var/tilt_transform = null
+
+/obj/structure/sign/picture_frame/New(loc, F)
+ ..()
+ frame = F
+ frame.pixel_x = 0
+ frame.pixel_y = 0
+ frame.forceMove(src)
+ name = frame.name
+ update_icon()
+
+ if(!tilt_transform)
+ tilt_transform = turn(matrix(), -10)
+
+ if(tilted)
+ transform = tilt_transform
+ verbs |= /obj/structure/sign/picture_frame/proc/untilt
+ else
+ verbs |= /obj/structure/sign/picture_frame/proc/tilt
+
+/obj/structure/sign/picture_frame/Destroy()
+ if(frame)
+ qdel(frame)
+ frame = null
+ return ..()
+
+/obj/structure/sign/picture_frame/update_icon()
+ overlays.Cut()
+ if(frame)
+ icon = null
+ icon_state = null
+ overlays |= getFlatIcon(frame)
+ else
+ icon = initial(icon)
+ icon_state = initial(icon_state)
+
+/obj/structure/sign/picture_frame/attackby(obj/item/I, mob/user)
+ if(istype(I, /obj/item/weapon/screwdriver))
+ playsound(src, 'sound/items/Screwdriver.ogg', 100, 1)
+ user.visible_message("[user] begins to unfasten \the [src] from the wall.", "You begin to unfasten \the [src] from the wall.")
+ if(do_after(user, 100, target = src))
+ playsound(src, 'sound/items/Deconstruct.ogg', 100, 1)
+ user.visible_message("[user] unfastens \the [src] from the wall.", "You unfasten \the [src] from the wall.")
+ frame.forceMove(user.loc)
+ frame = null
+ if(explosive)
+ explosive.forceMove(user.loc)
+ explosive = null
+ qdel(src)
+ if(istype(I, /obj/item/weapon/grenade) || istype(I, /obj/item/weapon/c4))
+ if(explosive)
+ user << "There is already a device attached behind \the [src], remove it first."
+ return 1
+ if(!tilted)
+ user << "\The [src] needs to be already tilted before being rigged with \the [I]."
+ return 1
+ user.visible_message("[user] is fiddling around behind \the [src].", "You begin to secure \the [I] behind \the [src].")
+ if(do_after(user, 150, target = src))
+ if(explosive || !tilted)
+ return
+ playsound(src, 'sound/weapons/handcuffs.ogg', 50, 1)
+ user.unEquip(I)
+ explosive = I
+ I.forceMove(src)
+ user.visible_message("[user] fiddles with the back of \the [src].", "You secure \the [I] behind \the [src].")
+
+ message_admins("[key_name_admin(user)] attached [I] to a picture frame.")
+ log_game("[key_name_admin(user)] attached [I] to a picture frame.")
+ return 1
+ else
+ return ..()
+
+/obj/structure/sign/picture_frame/examine(mob/user, var/distance = -1, var/infix = "", var/suffix = "")
+ if(frame)
+ frame.examine(user, distance, infix, suffix)
+ else
+ ..()
+
+/obj/structure/sign/picture_frame/attack_hand(mob/user)
+ if(frame)
+ frame.attack_self(user)
+ else
+ ..()
+
+/obj/structure/sign/picture_frame/ex_act(severity)
+ explode()
+ ..(severity)
+
+/obj/structure/sign/picture_frame/proc/explode()
+ if(istype(explosive, /obj/item/weapon/grenade))
+ var/obj/item/weapon/grenade/G = explosive
+ explosive = null
+ G.prime()
+ else if(istype(explosive, /obj/item/weapon/c4))
+ var/obj/item/weapon/c4/C = explosive
+ explosive = null
+ C.target = get_step(get_turf(src), dir)
+ C.explode(get_turf(loc))
+
+/obj/structure/sign/picture_frame/proc/toggle_tilt(mob/user)
+ if(!isliving(usr) || usr.stat)
+ return
+
+ tilted = !tilted
+
+ if(tilted)
+ animate(src, transform = tilt_transform, time = 10, easing = BOUNCE_EASING)
+ verbs -= /obj/structure/sign/picture_frame/proc/tilt
+ verbs |= /obj/structure/sign/picture_frame/proc/untilt
+ else
+ animate(src, transform = matrix(), time = 10, easing = CUBIC_EASING | EASE_IN)
+ verbs -= /obj/structure/sign/picture_frame/proc/untilt
+ verbs |= /obj/structure/sign/picture_frame/proc/tilt
+ explode()
+
+/obj/structure/sign/picture_frame/proc/tilt()
+ set name = "Tilt Picture"
+ set category = "Object"
+ set src in oview(1)
+
+ toggle_tilt(usr)
+
+/obj/structure/sign/picture_frame/proc/untilt()
+ set name = "Straighten Picture"
+ set category = "Object"
+ set src in oview(1)
+
+ toggle_tilt(usr)
+
+/obj/structure/sign/picture_frame/hear_talk(mob/living/M as mob, msg)
+ ..()
+ for(var/obj/O in contents)
+ O.hear_talk(M, msg)
+
+/obj/structure/sign/picture_frame/hear_message(mob/living/M as mob, msg)
+ ..()
+ for(var/obj/O in contents)
+ O.hear_message(M, msg)
\ No newline at end of file
diff --git a/code/modules/research/designs/autolathe_designs.dm b/code/modules/research/designs/autolathe_designs.dm
index 6e5bcf0389b..68b0090e317 100644
--- a/code/modules/research/designs/autolathe_designs.dm
+++ b/code/modules/research/designs/autolathe_designs.dm
@@ -273,6 +273,14 @@
materials = list(MAT_METAL = 100)
build_path = /obj/item/weapon/canvas/twentythreeXtwentythree
category = list("initial", "Miscellaneous")
+
+/datum/design/glass_picture_frame
+ name = "Glass Picture Frame"
+ id = "glass_picture_frame"
+ build_type = AUTOLATHE
+ materials = list(MAT_METAL = 25, MAT_GLASS = 75)
+ build_path = /obj/item/weapon/picture_frame/glass
+ category = list("initial", "Miscellaneous")
/datum/design/camera_assembly
name = "Camera Assembly"
diff --git a/icons/obj/bureaucracy.dmi b/icons/obj/bureaucracy.dmi
index 159d2f572f2..1bd8373670c 100644
Binary files a/icons/obj/bureaucracy.dmi and b/icons/obj/bureaucracy.dmi differ
diff --git a/icons/obj/contraband.dmi b/icons/obj/contraband.dmi
index 4a187225cd3..91432c14d1a 100644
Binary files a/icons/obj/contraband.dmi and b/icons/obj/contraband.dmi differ
diff --git a/paradise.dme b/paradise.dme
index 4c6959933ec..b611994a112 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -1585,6 +1585,7 @@
#include "code\modules\paperwork\faxmachine.dm"
#include "code\modules\paperwork\filingcabinet.dm"
#include "code\modules\paperwork\folders.dm"
+#include "code\modules\paperwork\frames.dm"
#include "code\modules\paperwork\handlabeler.dm"
#include "code\modules\paperwork\paper.dm"
#include "code\modules\paperwork\paper_bundle.dm"