Files
Yogstation/code/game/objects/structures/artstuff.dm
Ling a6d7e3fd15 TGUI Preferences Menu + total rewrite of the preferences backend (#17381)
* 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 commit 14d044a7e3.

* fuck off m8

* e

* fak off m8

* e2?

* AHHH

* AHA

* AHHH

* fix linter 2

* debug

* fix runtime

* Update dynamic.json

* Revert "debug"

This reverts commit 18681432bd.

* 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 commit 4082e3b133.

* 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>
2023-01-28 15:26:19 +00:00

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]."))