mirror of
https://github.com/yogstation13/Yogstation.git
synced 2025-02-26 09:04:50 +00:00
* TGUI Preferences Menu + total rewrite of the preferences backend (#17368) * It compiles * It opens * Sync 1 * Add asset caching * Sync 2 * It opens without dev now * Update a few packages * Sync 3 * Sync 4 keybind fix * start of dehardcoded species * Small fixes * Add more individual preferences * ASS sync * Automatic changelog generation #17368 [ci skip] * AHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH * e * Fix some TS stuff * Sort quirks starting from good * Fix skin tone selector * Jamie Fixes * Update moth.dm * Fix latejoin menu + tweaks * Some fixes * Finally fix job selection * e * Ling * MORE * config * Convert pref: ooccolor * Convert pref: asay color * Convert pref: tooltips * Convert pref: ui style * Convert pref: buttons locked * Convert pref: hotkeys * Convert pref: tgui stuff * Convert pref: windowflashing * Convert pref: ghost stuff * Convert pref: map & antag * Convert pref: PDA stuff * Convert pref: credits & glasses 1 * Convert pref: name * Convert pref: appearances 1 * Convert pref: appearances 2 * Convert pref: jobless role * Convert pref: runechat * Convert pref: yogtoggles + tail wagging 1 * Convert pref: client fps * Convert pref: graphic settings * Convert pref: pda uplink & menuoptions * Convert pref: map & flare * Convert pref: Bar choice * Fix setup character button * Convert pref: alt announcer * Fix * Add cycle background button * Convert pref: disable balloon alert * fix * Clean savefile * Fix backpack pref * Fix underwear selection * Fixes some shit * Updates * Fix computer runtime * Fix pref names * Convert pref: donor item & hat * More computer fixes * Convert pref: borg hat * Convert pref: donor pda (broken) * Convert pref: purrbation * Convert pref: afreeze * Convert pref: accent * Various savefile improvements * Convert pref: persistent scars 1 * A few pref fixes * Some more fixes * Various SSoverlays improvements * Add IPC appearances * Add polysmorph appearances * No icons for ipc and polysmorph * Podpeople deserve death * Add plasmaman appearance * h * fix * fix2 * asdf * fsdf * aaaaa * FUCK MOTHS * Preternis color * e * e * Update human.dmi * icon fix * un snowflake * fix underwear icon * remove color from here * donor ree * aa * maybe * Restore a bunch of TGUI files * More TGUI fixes * test * Fix more errors * a * test * e? * a;lso this * maybe * Fix * Revert "maybe" This reverts commit14d044a7e3. * fuck off m8 * e * fak off m8 * e2? * AHHH * AHA * AHHH * fix linter 2 * debug * fix runtime * Update dynamic.json * Revert "debug" This reverts commit18681432bd. * 2 * who sleeps in an async? * Hack * e * Fix a few blocking calls * Oh bother * Stay dead * fuck * Update jobs.dm * move debugging * Update jobs.dm * Test * YEET * Revert "YEET" This reverts commit4082e3b133. * Update jobs.dm * Update jobs.dm * e * Fix sechailer runtime * Fix human hair color * d * Ports part of that job refactor * Convert latejoin to new departments * Fix ghost form * Quirk validation * Hopefully pod color fix * oops * Prayge job fix * test * Better unit test asset loading * Remove print * Add error just in case * Remove brief outfit and bypass centcom deadmin * Remove broadcast login/logout * Remove darkened flash * Remove fov darkness * Remove ghost lighting * Remove some tgui prefs * Typo fix * Small fixes * IPC name fix * IPC and pod colors * Jobless fix * Donor item fix * Oopsie * Quirk bandaid * Misc * Move new prefs to Preferences tab for now * Add skillcape * FUCK THIS SHIT * Remove /tg/ gamer cloak * Restrict some job related preferences to clean up UI * Remove useless client var * e * Small tweaks * Dont allow selecting mood quirks if mood is disabled * AHHH * Filter ckey-locked donor items * stupid jamie * AI core display fix * Move donor stuff back to the top * Remove TODOs * Clean up perks * Linter fixes * e * WORKS * LORE * Fix skillcape list * Backpack why * Fixes * Fix cargo console * Remove these * Add horns, frills and mark * Fix not applying all features * Add some missing mutant bodyparts * Update numberinput * Makes animatednumber cooler * Oops * Add default ghost orbit * Default to normal backpack * Fixes skillcapes not being filtered * Donor fix prayge * yep * Adds fallback latejoin menu * Rework donor stuff a bit * Fix donor tgui logic * Delete unused proc * Update FA * Un-yogify quirks * Better checking of quirks * Update tgfont * Fix quirk icons * Fix backup name * Fix donor stuff * A few runtime fixes * Fix another runtime * Give fallback latejoin verb upon connecting * Update AirAlarm interface * maybe this works * Test * Sentient * Remove AI core display preview icons * Fix resetting plasmaman helmet style * Equip plasman in preview * Fix plasmaman preview icon * fuck keybind fix * Extra keybind sanity --------- Co-authored-by: Yogbot-13 <admin@yogstation.net> Co-authored-by: Jamie D <993128+JamieD1@users.noreply.github.com> Co-authored-by: TheGamerdk <5618080+TheGamerdk@users.noreply.github.com> Co-authored-by: adamsong <adamsong@users.noreply.github.com>
425 lines
12 KiB
Plaintext
425 lines
12 KiB
Plaintext
|
|
///////////
|
|
// EASEL //
|
|
///////////
|
|
|
|
/obj/structure/easel
|
|
name = "easel"
|
|
desc = "Only for the finest of art!"
|
|
icon = 'icons/obj/artstuff.dmi'
|
|
icon_state = "easel"
|
|
density = TRUE
|
|
resistance_flags = FLAMMABLE
|
|
max_integrity = 60
|
|
var/obj/item/canvas/painting = null
|
|
|
|
//Adding canvases
|
|
/obj/structure/easel/attackby(obj/item/I, mob/user, params)
|
|
if(istype(I, /obj/item/canvas))
|
|
var/obj/item/canvas/C = I
|
|
user.dropItemToGround(C)
|
|
painting = C
|
|
C.forceMove(get_turf(src))
|
|
C.layer = layer+0.1
|
|
user.visible_message(span_notice("[user] puts \the [C] on \the [src]."),span_notice("You place \the [C] on \the [src]."))
|
|
else
|
|
return ..()
|
|
|
|
|
|
//Stick to the easel like glue
|
|
/obj/structure/easel/Move()
|
|
var/turf/T = get_turf(src)
|
|
. = ..()
|
|
if(painting && painting.loc == T) //Only move if it's near us.
|
|
painting.forceMove(get_turf(src))
|
|
else
|
|
painting = null
|
|
|
|
/obj/item/canvas
|
|
name = "canvas"
|
|
desc = "Draw out your soul on this canvas!"
|
|
icon = 'icons/obj/artstuff.dmi'
|
|
icon_state = "11x11"
|
|
resistance_flags = FLAMMABLE
|
|
var/width = 11
|
|
var/height = 11
|
|
var/list/grid
|
|
var/canvas_color = "#ffffff" //empty canvas color
|
|
var/used = FALSE
|
|
var/painting_name = "Untitled Artwork" //Painting name, this is set after framing.
|
|
var/finalized = FALSE //Blocks edits
|
|
var/author_ckey
|
|
var/icon_generated = FALSE
|
|
var/icon/generated_icon
|
|
///boolean that blocks persistence from saving it. enabled from printing copies, because we do not want to save copies.
|
|
var/no_save = FALSE
|
|
|
|
// Painting overlay offset when framed
|
|
var/framed_offset_x = 11
|
|
var/framed_offset_y = 10
|
|
|
|
pixel_x = 10
|
|
pixel_y = 9
|
|
|
|
/obj/item/canvas/Initialize()
|
|
. = ..()
|
|
reset_grid()
|
|
|
|
/obj/item/canvas/proc/reset_grid()
|
|
grid = new/list(width,height)
|
|
for(var/x in 1 to width)
|
|
for(var/y in 1 to height)
|
|
grid[x][y] = canvas_color
|
|
|
|
/obj/item/canvas/attack_self(mob/user)
|
|
. = ..()
|
|
ui_interact(user)
|
|
|
|
/obj/item/canvas/ui_state(mob/user)
|
|
if(finalized)
|
|
return GLOB.physical_obscured_state
|
|
else
|
|
return GLOB.default_state
|
|
|
|
/obj/item/canvas/ui_interact(mob/user, datum/tgui/ui)
|
|
ui = SStgui.try_update_ui(user, src, ui)
|
|
if(!ui)
|
|
ui = new(user, src, "Canvas", name)
|
|
ui.set_autoupdate(FALSE)
|
|
ui.open()
|
|
|
|
/obj/item/canvas/attackby(obj/item/I, mob/living/user, params)
|
|
if(user.a_intent == INTENT_HELP)
|
|
ui_interact(user)
|
|
else
|
|
return ..()
|
|
|
|
/obj/item/canvas/ui_data(mob/user)
|
|
. = ..()
|
|
.["grid"] = grid
|
|
.["name"] = painting_name
|
|
.["finalized"] = finalized
|
|
|
|
/obj/item/canvas/examine(mob/user)
|
|
. = ..()
|
|
ui_interact(user)
|
|
|
|
/obj/item/canvas/ui_act(action, params)
|
|
. = ..()
|
|
if(. || finalized)
|
|
return
|
|
var/mob/user = usr
|
|
switch(action)
|
|
if("paint")
|
|
var/obj/item/I = user.get_active_held_item()
|
|
var/color = get_paint_tool_color(I)
|
|
if(!color)
|
|
return FALSE
|
|
var/x = text2num(params["x"])
|
|
var/y = text2num(params["y"])
|
|
grid[x][y] = color
|
|
used = TRUE
|
|
update_icon()
|
|
. = TRUE
|
|
if("finalize")
|
|
. = TRUE
|
|
if(!finalized)
|
|
finalize(user)
|
|
|
|
/obj/item/canvas/proc/finalize(mob/user)
|
|
finalized = TRUE
|
|
author_ckey = user.ckey
|
|
generate_proper_overlay()
|
|
try_rename(user)
|
|
|
|
/obj/item/canvas/update_icon()
|
|
cut_overlays()
|
|
if(!icon_generated)
|
|
if(used)
|
|
var/mutable_appearance/detail = mutable_appearance(icon,"[icon_state]wip")
|
|
detail.pixel_x = 1
|
|
detail.pixel_y = 1
|
|
add_overlay(detail)
|
|
else
|
|
var/mutable_appearance/detail = mutable_appearance(generated_icon)
|
|
detail.pixel_x = 1
|
|
detail.pixel_y = 1
|
|
add_overlay(detail)
|
|
|
|
/obj/item/canvas/proc/generate_proper_overlay()
|
|
if(icon_generated)
|
|
return
|
|
var/png_filename = "data/paintings/temp_painting.png"
|
|
var/result = rustg_dmi_create_png(png_filename,"[width]","[height]",get_data_string())
|
|
if(result)
|
|
CRASH("Error generating painting png : [result]")
|
|
generated_icon = new(png_filename)
|
|
icon_generated = TRUE
|
|
update_icon()
|
|
|
|
/obj/item/canvas/proc/get_data_string()
|
|
var/list/data = list()
|
|
for(var/y in 1 to height)
|
|
for(var/x in 1 to width)
|
|
data += grid[x][y]
|
|
return data.Join("")
|
|
|
|
//Todo make this element ?
|
|
/obj/item/canvas/proc/get_paint_tool_color(obj/item/I)
|
|
if(!I)
|
|
return
|
|
if(istype(I, /obj/item/toy/crayon))
|
|
var/obj/item/toy/crayon/C = I
|
|
return C.paint_color
|
|
else if(istype(I, /obj/item/pen))
|
|
var/obj/item/pen/P = I
|
|
switch(P.colour)
|
|
if("black")
|
|
return "#000000"
|
|
if("blue")
|
|
return "#0000ff"
|
|
if("red")
|
|
return "#ff0000"
|
|
return P.colour
|
|
else if(istype(I, /obj/item/soap) || istype(I, /obj/item/reagent_containers/glass/rag))
|
|
return canvas_color
|
|
|
|
/obj/item/canvas/proc/try_rename(mob/user)
|
|
var/new_name = stripped_input(user,"What do you want to name the painting?")
|
|
if(new_name != painting_name && new_name && user.canUseTopic(src,BE_CLOSE))
|
|
painting_name = new_name
|
|
SStgui.update_uis(src)
|
|
|
|
/obj/item/canvas/nineteenXnineteen
|
|
icon_state = "19x19"
|
|
width = 19
|
|
height = 19
|
|
pixel_x = 6
|
|
pixel_y = 9
|
|
framed_offset_x = 8
|
|
framed_offset_y = 9
|
|
|
|
/obj/item/canvas/twentythreeXnineteen
|
|
icon_state = "23x19"
|
|
width = 23
|
|
height = 19
|
|
pixel_x = 4
|
|
pixel_y = 10
|
|
framed_offset_x = 6
|
|
framed_offset_y = 8
|
|
|
|
/obj/item/canvas/twentythreeXtwentythree
|
|
icon_state = "23x23"
|
|
width = 23
|
|
height = 23
|
|
pixel_x = 5
|
|
pixel_y = 9
|
|
framed_offset_x = 5
|
|
framed_offset_y = 6
|
|
|
|
/obj/item/canvas/twentyfour_twentyfour
|
|
name = "ai universal standard canvas"
|
|
desc = "Besides being very large, the AI can accept these as a display from their internal database after you've hung it up."
|
|
icon_state = "24x24"
|
|
width = 24
|
|
height = 24
|
|
pixel_x = 2
|
|
pixel_y = 1
|
|
framed_offset_x = 4
|
|
framed_offset_y = 5
|
|
|
|
/obj/item/wallframe/painting
|
|
name = "painting frame"
|
|
desc = "The perfect showcase for your favorite deathtrap memories."
|
|
icon = 'icons/obj/decals.dmi'
|
|
flags_1 = 0
|
|
icon_state = "frame-empty"
|
|
result_path = /obj/structure/sign/painting
|
|
|
|
/obj/structure/sign/painting
|
|
name = "Painting"
|
|
desc = "Art or \"Art\"? You decide."
|
|
icon = 'icons/obj/decals.dmi'
|
|
icon_state = "frame-empty"
|
|
buildable_sign = FALSE
|
|
var/obj/item/canvas/C
|
|
var/persistence_id
|
|
|
|
/obj/structure/sign/painting/Initialize(mapload, dir, building)
|
|
. = ..()
|
|
SSpersistent_paintings.painting_frames += src
|
|
AddComponent(/datum/component/art, 20)
|
|
if(dir)
|
|
setDir(dir)
|
|
if(building)
|
|
pixel_x = (dir & 3)? 0 : (dir == 4 ? -30 : 30)
|
|
pixel_y = (dir & 3)? (dir ==1 ? -30 : 30) : 0
|
|
|
|
/obj/structure/sign/painting/Destroy()
|
|
. = ..()
|
|
SSpersistent_paintings.painting_frames -= src
|
|
|
|
/obj/structure/sign/painting/attackby(obj/item/I, mob/user, params)
|
|
if(!C && istype(I, /obj/item/canvas))
|
|
frame_canvas(user,I)
|
|
else if(C && C.painting_name == initial(C.painting_name) && istype(I,/obj/item/pen))
|
|
try_rename(user)
|
|
else
|
|
return ..()
|
|
|
|
/obj/structure/sign/painting/examine(mob/user)
|
|
. = ..()
|
|
if(persistence_id)
|
|
. += span_notice("Any painting placed here will be archived at the end of the shift.")
|
|
if(C)
|
|
C.ui_interact(user)
|
|
. += span_notice("Use wirecutters to remove the painting.")
|
|
|
|
/obj/structure/sign/painting/wirecutter_act(mob/living/user, obj/item/I)
|
|
. = ..()
|
|
if(C)
|
|
C.forceMove(drop_location())
|
|
C = null
|
|
to_chat(user, span_notice("You remove the painting from the frame."))
|
|
update_icon()
|
|
return TRUE
|
|
|
|
/obj/structure/sign/painting/proc/frame_canvas(mob/user,obj/item/canvas/new_canvas)
|
|
if(user.transferItemToLoc(new_canvas,src))
|
|
C = new_canvas
|
|
if(!C.finalized)
|
|
C.finalize(user)
|
|
to_chat(user,span_notice("You frame [C]."))
|
|
update_icon()
|
|
|
|
/obj/structure/sign/painting/proc/try_rename(mob/user)
|
|
if(C.painting_name == initial(C.painting_name))
|
|
C.try_rename(user)
|
|
|
|
/obj/structure/sign/painting/update_icon()
|
|
. = ..()
|
|
|
|
if(C && C.generated_icon)
|
|
icon_state = null
|
|
else
|
|
icon_state = "frame-empty"
|
|
|
|
cut_overlays()
|
|
if(C && C.generated_icon)
|
|
var/mutable_appearance/MA = mutable_appearance(C.generated_icon)
|
|
MA.pixel_x = C.framed_offset_x
|
|
MA.pixel_y = C.framed_offset_y
|
|
add_overlay(MA)
|
|
var/mutable_appearance/frame = mutable_appearance(C.icon,"[C.icon_state]frame")
|
|
frame.pixel_x = C.framed_offset_x - 1
|
|
frame.pixel_y = C.framed_offset_y - 1
|
|
add_overlay(frame)
|
|
|
|
/**
|
|
* Loads a painting from SSpersistence. Called globally by said subsystem when it inits
|
|
*
|
|
* Deleting paintings leaves their json, so this proc will remove the json and try again if it finds one of those.
|
|
*/
|
|
/obj/structure/sign/painting/proc/load_persistent()
|
|
if(!persistence_id || !SSpersistent_paintings.paintings[persistence_id])
|
|
return
|
|
var/list/painting_category = SSpersistent_paintings.paintings[persistence_id]
|
|
var/list/painting
|
|
while(!painting)
|
|
if(!length(SSpersistent_paintings.paintings[persistence_id]))
|
|
return //aborts loading anything this category has no usable paintings
|
|
var/list/chosen = pick(painting_category)
|
|
if(!fexists("data/paintings/[persistence_id]/[chosen["md5"]].png")) //shitmin deleted this art, lets remove json entry to avoid errors
|
|
painting_category -= list(chosen)
|
|
continue //and try again
|
|
painting = chosen
|
|
var/title = painting["title"]
|
|
var/author = painting["ckey"]
|
|
var/png = "data/paintings/[persistence_id]/[painting["md5"]].png"
|
|
if(!title)
|
|
title = "Untitled Artwork" //legacy artwork allowed null names which was bad for the json, lets fix that
|
|
painting["title"] = title
|
|
var/icon/I = new(png)
|
|
var/obj/item/canvas/new_canvas
|
|
var/w = I.Width()
|
|
var/h = I.Height()
|
|
for(var/T in typesof(/obj/item/canvas))
|
|
new_canvas = T
|
|
if(initial(new_canvas.width) == w && initial(new_canvas.height) == h)
|
|
new_canvas = new T(src)
|
|
break
|
|
new_canvas.fill_grid_from_icon(I)
|
|
new_canvas.generated_icon = I
|
|
new_canvas.icon_generated = TRUE
|
|
new_canvas.finalized = TRUE
|
|
new_canvas.painting_name = title
|
|
new_canvas.author_ckey = author
|
|
new_canvas.name = "painting - [title]"
|
|
C = new_canvas
|
|
update_icon()
|
|
|
|
/obj/structure/sign/painting/proc/save_persistent()
|
|
if(!persistence_id || !C || C.no_save)
|
|
return
|
|
if(sanitize_filename(persistence_id) != persistence_id)
|
|
stack_trace("Invalid persistence_id - [persistence_id]")
|
|
return
|
|
if(!C.painting_name)
|
|
C.painting_name = "Untitled Artwork"
|
|
var/data = C.get_data_string()
|
|
var/md5 = md5(lowertext(data))
|
|
var/list/current = SSpersistent_paintings.paintings[persistence_id]
|
|
if(!current)
|
|
current = list()
|
|
for(var/list/entry in current)
|
|
if(entry["md5"] == md5)
|
|
return
|
|
var/png_directory = "data/paintings/[persistence_id]/"
|
|
var/png_path = png_directory + "[md5].png"
|
|
var/result = rustg_dmi_create_png(png_path,"[C.width]","[C.height]",data)
|
|
if(result)
|
|
CRASH("Error saving persistent painting: [result]")
|
|
current += list(list("title" = C.painting_name , "md5" = md5, "ckey" = C.author_ckey))
|
|
SSpersistent_paintings.paintings[persistence_id] = current
|
|
|
|
/obj/item/canvas/proc/fill_grid_from_icon(icon/I)
|
|
var/h = I.Height() + 1
|
|
for(var/x in 1 to width)
|
|
for(var/y in 1 to height)
|
|
grid[x][y] = I.GetPixel(x,h-y)
|
|
|
|
//Presets for art gallery mapping, for paintings to be shared across stations
|
|
/obj/structure/sign/painting/library
|
|
persistence_id = "library"
|
|
|
|
/obj/structure/sign/painting/library_secure
|
|
persistence_id = "library_secure"
|
|
|
|
/obj/structure/sign/painting/library_private // keep your smut away from prying eyes, or non-librarians at least
|
|
persistence_id = "library_private"
|
|
|
|
/obj/structure/sign/painting/vv_get_dropdown()
|
|
. = ..()
|
|
VV_DROPDOWN_OPTION(VV_HK_REMOVE_PAINTING, "Remove Persistent Painting")
|
|
|
|
/obj/structure/sign/painting/vv_do_topic(list/href_list)
|
|
. = ..()
|
|
var/mob/user = usr
|
|
if(!persistence_id || !C)
|
|
to_chat(user,span_warning("This is not a persistent painting."))
|
|
return
|
|
var/md5 = md5(C.get_data_string())
|
|
var/author = C.author_ckey
|
|
var/list/current = SSpersistent_paintings.paintings[persistence_id]
|
|
if(current)
|
|
for(var/list/entry in current)
|
|
if(entry["md5"] == md5)
|
|
current -= entry
|
|
var/png = "data/paintings/[persistence_id]/[md5].png"
|
|
fdel(png)
|
|
for(var/obj/structure/sign/painting/painting in SSpersistent_paintings.painting_frames)
|
|
if(painting.C && md5(painting.C.get_data_string()) == md5)
|
|
QDEL_NULL(painting.C)
|
|
log_admin("[key_name(user)] has deleted a persistent painting made by [author].")
|
|
message_admins(span_notice("[key_name_admin(user)] has deleted persistent painting made by [author]."))
|