Files
Bubberstation/code/controllers/subsystem/persistent_paintings.dm
Gandalf e4b4d4d3c0 MISSED MIRROR [Lints Against Unmanaged Local Defines] (#20204)
* https://github.com/tgstation/tgstation/pull/74333

https: //github.com/tgstation/tgstation/pull/74333
Co-Authored-By: tattle <66640614+dragomagol@users.noreply.github.com>

* var stuff

Co-Authored-By: tattle <66640614+dragomagol@users.noreply.github.com>

* vars

* Update sol_fed.dm

---------

Co-authored-by: tattle <66640614+dragomagol@users.noreply.github.com>
2023-04-01 01:15:22 +01:00

317 lines
11 KiB
Plaintext

#define PAINTINGS_DATA_FORMAT_VERSION 2
// Patronage thresholds for paintings. Different cosmetic frames become available as more credits are spent on the patronage.
#define PATRONAGE_OK_FRAME (PAYCHECK_CREW * 3) // 150 credits, as of march 2022
#define PATRONAGE_NICE_FRAME (PATRONAGE_OK_FRAME * 2.5)
#define PATRONAGE_GREAT_FRAME (PATRONAGE_NICE_FRAME * 2)
#define PATRONAGE_EXCELLENT_FRAME (PATRONAGE_GREAT_FRAME * 2)
#define PATRONAGE_AMAZING_FRAME (PATRONAGE_EXCELLENT_FRAME * 2)
#define PATRONAGE_SUPERB_FRAME (PATRONAGE_AMAZING_FRAME * 2)
#define PATRONAGE_LEGENDARY_FRAME (PATRONAGE_SUPERB_FRAME * 2)
/*
{
"version":2
"paintings":[
{
"md5":"2e117d9d372fb6823bd81d3542a419d6", //unique identifier
"creator_ckey" : "example",
"creator_name" : "example",
"creation_date" : "YYYY-MM-DD hh:mm:ss",
"creation_round_id": 222,
"title": "example title",
"tags": ["library","library_private"],
"patron_ckey" : "example",
"patron_name" : "example",
"credit_value" : 999,
"width" : 24,
"height" : 24,
"medium" : "Oil on canvas",
"frame_type" : "bones"
},
]
}
*/
/datum/painting
/// md5 of the png file, also the filename.
var/md5
/// Title
var/title
/// Author's ckey
var/creator_ckey
/// Author's name
var/creator_name
/// Timestamp when painting was made (finalized ?)
var/creation_date
/// Round if when the painting was made
var/creation_round_id
/// List of this painting string tags if any
var/list/tags
/// Patron ckey
var/patron_ckey
/// Patron name
var/patron_name
/// The appearance of the frame overlay that appears when the painting if framed.
var/frame_type = "simple"
/// Amount paid by last patron for this painting
var/credit_value = 0
/// painting width
var/width
/// painting height
var/height
/// short painting medium description
var/medium
/// Was the painting loaded from json or created this round
var/loaded_from_json = FALSE
/datum/painting/proc/load_from_json(list/json_data)
md5 = json_data["md5"]
title = json_data["title"]
creator_ckey = json_data["creator_ckey"]
creator_name = json_data["creator_name"]
creation_date = json_data["creation_date"]
creation_round_id = json_data["creation_round_id"]
tags = json_data["tags"]
patron_ckey = json_data["patron_ckey"]
patron_name = json_data["patron_name"]
credit_value = json_data["credit_value"]
width = json_data["width"]
height = json_data["height"]
medium = json_data["medium"]
frame_type = json_data["frame_type"]
loaded_from_json = TRUE
/datum/painting/proc/to_json()
var/list/new_data = list()
new_data["md5"] = md5
new_data["title"] = title
new_data["creator_ckey"] = creator_ckey
new_data["creator_name"] = creator_name
new_data["creation_date"] = creation_date
new_data["creation_round_id"] = creation_round_id
new_data["tags"] = tags
new_data["patron_ckey"] = patron_ckey
new_data["patron_name"] = patron_name
new_data["credit_value"] = credit_value
new_data["width"] = width
new_data["height"] = height
new_data["medium"] = medium
new_data["frame_type"] = frame_type
return new_data
SUBSYSTEM_DEF(persistent_paintings)
name = "Persistent Paintings"
init_order = INIT_ORDER_PERSISTENT_PAINTINGS
flags = SS_NO_FIRE
/// A list of painting frames that this controls
var/list/obj/structure/sign/painting/painting_frames = list()
/// Hashes of paintings deleted this round
var/list/deleted_paintings_md5s = list()
/// A list of /datum/paintings saved or ready to be saved this round.
var/list/paintings = list()
///The list of available frame reskins (they are purely cosmetic) and the associated patronage amount required for them.
var/list/frame_types_by_patronage_tier = list(
"simple" = 0,
"iron" = PATRONAGE_OK_FRAME,
"bamboo" = PATRONAGE_OK_FRAME,
"bones" = PATRONAGE_OK_FRAME,
"bronze" = PATRONAGE_NICE_FRAME,
"clown" = PATRONAGE_NICE_FRAME,
"frog" = PATRONAGE_NICE_FRAME,
"silver" = PATRONAGE_GREAT_FRAME,
"necropolis" = PATRONAGE_GREAT_FRAME,
"gold" = PATRONAGE_EXCELLENT_FRAME,
"diamond" = PATRONAGE_AMAZING_FRAME,
"rainbow" = PATRONAGE_SUPERB_FRAME,
"supermatter" = PATRONAGE_LEGENDARY_FRAME
)
/datum/controller/subsystem/persistent_paintings/Initialize()
var/json_file = file("data/paintings.json")
if(fexists(json_file))
var/list/raw_data = update_format(json_decode(file2text(json_file)))
for(var/list/painting_data as anything in raw_data["paintings"])
var/datum/painting/loaded_painting = new
loaded_painting.load_from_json(painting_data)
paintings += loaded_painting
for(var/obj/structure/sign/painting/painting_frame as anything in painting_frames)
painting_frame.load_persistent()
return SS_INIT_SUCCESS
/**
* Generates painting data ready to be consumed by ui.
* Args:
* * filter: a bitfield argument is used to filter out paintings that don't match certain requisites.
* * admin : whether all the json data of the painting is added to the return value or only the more IC details
* * search_text : text to search for if the PAINTINGS_FILTER_SEARCH_TITLE or PAINTINGS_FILTER_SEARCH_CREATOR filters are enabled.
*/
/datum/controller/subsystem/persistent_paintings/proc/painting_ui_data(filter=NONE, admin=FALSE, search_text)
. = list()
var/searching = filter & (PAINTINGS_FILTER_SEARCH_TITLE|PAINTINGS_FILTER_SEARCH_CREATOR) && search_text
for(var/datum/painting/painting as anything in paintings)
if(filter & PAINTINGS_FILTER_AI_PORTRAIT && ((painting.width != 24 && painting.width != 23) || (painting.height != 24 && painting.height != 23)))
continue
if(searching)
var/haystack_text = ""
if(filter & PAINTINGS_FILTER_SEARCH_TITLE)
haystack_text = painting.title
else if(filter & PAINTINGS_FILTER_SEARCH_CREATOR)
haystack_text = painting.creator_name
if(!findtext(haystack_text, search_text))
continue
if(admin)
var/list/pdata = painting.to_json()
pdata["ref"] = REF(painting)
. += list(pdata)
else
. += list(list(
"title" = painting.title,
"creator" = painting.creator_name,
"md5" = painting.md5,
"ref" = REF(painting),
"ratio" = painting.width/painting.height,
))
/// Returns paintings with given tag.
/datum/controller/subsystem/persistent_paintings/proc/get_paintings_with_tag(tag_name)
. = list()
for(var/datum/painting/painting as anything in paintings)
if(!painting.tags || !(tag_name in painting.tags))
continue
. += painting
/// Updates paintings data format to latest if necessary
/datum/controller/subsystem/persistent_paintings/proc/update_format(current_data)
if(current_data["version"] && current_data["version"] == PAINTINGS_DATA_FORMAT_VERSION)
return current_data
var/version = current_data["version"]
if(version < 1)
current_data = migrate_to_version_1(current_data)
if(version < 2) //Makes sure old paintings get a cosmetic frame type from their patronage tiers.
current_data = migrate_to_version_2(current_data)
return current_data
/datum/controller/subsystem/persistent_paintings/proc/migrate_to_version_1(list/current_data)
fcopy("data/paintings.json","data/paintings_migration_backup_0.json") //Better safe than losing all metadata
var/list/result = list()
result["version"] = 1
var/list/data = list()
// Squash categories into tags
for(var/category in current_data)
for(var/old_data in current_data[category])
var/duplicate_found = FALSE
for(var/list/entry in data)
if(entry["md5"] == old_data["md5"])
entry["tags"] |= category
duplicate_found = TRUE
break
if(duplicate_found)
continue
var/old_png_path = "data/paintings/[category]/[old_data["md5"]].png"
var/new_png_path = "data/paintings/images/[old_data["md5"]].png"
fcopy(old_png_path,new_png_path)
fdel(old_png_path)
var/icon/painting_icon = new(new_png_path)
var/width = painting_icon.Width()
var/height = painting_icon.Height()
var/list/new_data = list()
new_data["md5"] = old_data["md5"]
new_data["title"] = old_data["title"] || "Untitled Artwork"
new_data["creator_ckey"] = old_data["ckey"] || ""
new_data["creator_name"] = "Anonymous"
new_data["creation_date"] = time2text(world.realtime) // Could use creation/modified file helpers in rustg
new_data["creation_round_id"] = GLOB.round_id
new_data["tags"] = list(category,"Migrated from version 0")
new_data["patron_ckey"] = ""
new_data["patron_name"] = ""
new_data["credit_value"] = 0
new_data["width"] = width
new_data["height"] = height
new_data["medium"] = "Spraypaint on canvas" //Let's go with most common tool.
data += list(new_data)
result["paintings"] = data
//We're going to save this immidiately this is non-recoverable operation
var/json_file = file("data/paintings.json")
fdel(json_file)
WRITE_FILE(json_file, json_encode(result))
return result
/datum/controller/subsystem/persistent_paintings/proc/migrate_to_version_2(list/current_data)
fcopy("data/paintings.json","data/paintings_migration_backup_1.json") //Better safe than sorry
current_data["version"] = 2
for(var/painting_data in current_data["paintings"])
painting_data["tags"] |= "Migrated from version 1"
var/credit_value = painting_data["credit_value"]
var/list/possible_frame_types = get_available_frames(credit_value, only_current_tier = TRUE)
painting_data["frame_type"] = pick(possible_frame_types) || "simple"
return current_data
/**
* returns a list of cosmetic frames which patronage tier values are are within credit_value.
* If only_current_tier is TRUE, the list will be populated only by the ones from the highest tier.
*/
/datum/controller/subsystem/persistent_paintings/proc/get_available_frames(credit_value, only_current_tier = FALSE)
var/list/frame_candidates = list()
var/current_tier_patronage = 0
for(var/frametype in frame_types_by_patronage_tier)
var/required_patronage = frame_types_by_patronage_tier[frametype]
if(credit_value >= required_patronage)
if(only_current_tier && current_tier_patronage < required_patronage)
current_tier_patronage = required_patronage
frame_candidates.Cut()
frame_candidates += frametype
return frame_candidates
/// Saves all persistent paintings
/datum/controller/subsystem/persistent_paintings/proc/save_paintings()
// Collect new painting data
for(var/obj/structure/sign/painting/painting_frame as anything in painting_frames)
painting_frame.save_persistent()
save_to_file()
/// Saves all currently tracked painting data to file
/datum/controller/subsystem/persistent_paintings/proc/save_to_file()
var/json_file = file("data/paintings.json")
var/list/collated_data = list()
if(fexists(json_file))
var/list/old_data = json_decode(file2text(json_file))
for(var/list/painting_data as anything in old_data["paintings"])
collated_data[painting_data["md5"]] = painting_data
var/list/painting_data = list()
for(var/datum/painting/painting as anything in paintings)
collated_data[painting.md5] = painting.to_json() //Current data has priority over old data
// Remove deleted paintings from the list
collated_data -= deleted_paintings_md5s
// Flatten the resulting list
for(var/key in collated_data)
painting_data += list(collated_data[key])
var/list/all_data = list("version" = PAINTINGS_DATA_FORMAT_VERSION)
all_data["paintings"] = painting_data
var/payload = json_encode(all_data)
fdel(json_file)
WRITE_FILE(json_file, payload)
#undef PAINTINGS_DATA_FORMAT_VERSION
#undef PATRONAGE_OK_FRAME
#undef PATRONAGE_NICE_FRAME
#undef PATRONAGE_GREAT_FRAME
#undef PATRONAGE_EXCELLENT_FRAME
#undef PATRONAGE_AMAZING_FRAME
#undef PATRONAGE_SUPERB_FRAME
#undef PATRONAGE_LEGENDARY_FRAME