mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-26 06:29:23 +01:00
Trophy case update (#71015)
## About The Pull Request I have been chipping away/procrastinating at this since May, but after several years, I have finally updated how Trophy Cases work. So, what this PR does is the following: - Standardized everything in persistence.dm to use snake case, and added basic autodocs - Automatically moves trophies from data/npc_saves/TrophyItems.json to data/trophy_items.json. Removed legacy .sav conversion by request, it has been a long time. - Trophy cases are opened and loaded the same way you would open a regular ID locked display case (used curator access, relevant access autodoc has been updated) - Instead of cheap plastic replicas that turn to dust anyways, trophy cases use holograms, which can be dispelled by hand - Trophy data gets saved if an item stays in the trophy case when the shuttle arrives to centcom, and the item has a description set. This is in line with paintings, which has to still hang on the wall at round end. - You can edit the description of new trophies by using the librarian's key to unlock History Mode - When you click on a closed trophy case, it will open a tgui, and will not display the case description. It will still do for open cases. Vendatrays have been updated to do the same. - The UI's icon uses icon2base64(getFlatIcon(showpiece, no_anim=TRUE)). Vendatrays have been updated similarly, so items with directions and animations are displayed properly. The base64 strings are updated in update_static_data. - Fixes vendatrays from displaying some characters in strange ways, such as displaying /improper. - Renames some one letter, or nonindicate argument and var names in trophy case code - Adds a trophy management admin panel, where admins can finally delete all the curator ID cards swallowed over the years. Or, they can replace the paths with funny new paths. - If an entry has an incorrect, no longer existing path, it will be marked red in the management panel - Adds MAX_PLAQUE_LEN define, which 144 characters - Removes start_showpieces from trophy cases, as it was completely unused. The start_showpiece_type var is still around. - Moves trophy_message var to trophy cases. Only a dice collector display case used them in the Snowdin map. What this PR does not do - Sadly, it still only saves the base image of an item, and no layers or altered image states. This has to come in the future. <details> <summary>Click here to see various states of the trophy tgUI</summary>  Locked history mode, existing item.  Unlocked history mode, but holographic trophy is present.  Locked history mode, no item.  Unlocked history mode, no item.  Unlocked history mode, item placed, default text. (Note: this picture is out of date. The typo has been fixed, and "record a message" is now "record a description" for consistency)  Unlocked history mode, item placed, new text. </details> <details> <summary>Click here to see the admin panel</summary>  </details> ## Why It's Good For The Game Less curator ID's stuck in the Trophy Cases, and the existing ones can be cleaned up. A more immersive Trophy Case user experience, in general. ## Changelog 🆑 refactor: refactored trophy cases, to be more user friendly admin: created a trophy managment admin panel /🆑
This commit is contained in:
@@ -3689,7 +3689,7 @@
|
||||
/obj/effect/turf_decal/tile/blue/fourcorners,
|
||||
/obj/structure/displaycase{
|
||||
start_showpiece_type = /obj/item/dice/d6/space;
|
||||
trophy_message = "Stolen from dice collector before he could enjoy his day."
|
||||
desc = "Contains an artifact stolen from dice collector before he could enjoy his day."
|
||||
},
|
||||
/turf/open/floor/iron/dark,
|
||||
/area/awaymission/cabin/caves/mountain)
|
||||
|
||||
@@ -128,7 +128,7 @@
|
||||
#define ACCESS_CHAPEL_OFFICE "chapel_office"
|
||||
/// Access to the chapel's crematorium.
|
||||
#define ACCESS_CREMATORIUM "crematorium"
|
||||
/// Access to the curator's private rooms in the Library, as well as access both into and out of the Library via Maintenance.
|
||||
/// Access to the curator's private rooms in the Library and the trophy display cases, as well as access both into and out of the Library via Maintenance.
|
||||
#define ACCESS_LIBRARY "library"
|
||||
/// Access to the Bar, the Bar's Backroom, the bar sign, the bar robot portal, and the bar's vending machines. Some other bar-things too.
|
||||
#define ACCESS_BAR "bar"
|
||||
|
||||
@@ -94,6 +94,7 @@
|
||||
#define MAX_NAME_LEN 42
|
||||
#define MAX_BROADCAST_LEN 512
|
||||
#define MAX_CHARTER_LEN 80
|
||||
#define MAX_PLAQUE_LEN 144
|
||||
|
||||
// Audio/Visual Flags. Used to determine what sense are required to notice a message.
|
||||
#define MSG_VISUAL (1<<0)
|
||||
|
||||
@@ -25,35 +25,38 @@ SUBSYSTEM_DEF(persistence)
|
||||
var/rounds_since_engine_exploded = 0
|
||||
|
||||
/datum/controller/subsystem/persistence/Initialize()
|
||||
LoadPoly()
|
||||
load_poly()
|
||||
load_wall_engravings()
|
||||
load_prisoner_tattoos()
|
||||
LoadTrophies()
|
||||
LoadRecentMaps()
|
||||
LoadPhotoPersistence()
|
||||
LoadRandomizedRecipes()
|
||||
load_trophies()
|
||||
load_recent_maps()
|
||||
load_photo_persistence()
|
||||
load_randomized_recipes()
|
||||
load_custom_outfits()
|
||||
load_delamination_counter()
|
||||
|
||||
load_adventures()
|
||||
return SS_INIT_SUCCESS
|
||||
|
||||
///Collects all data to persist.
|
||||
/datum/controller/subsystem/persistence/proc/collect_data()
|
||||
save_wall_engravings()
|
||||
save_prisoner_tattoos()
|
||||
CollectTrophies()
|
||||
CollectMaps()
|
||||
SavePhotoPersistence() //THIS IS PERSISTENCE, NOT THE LOGGING PORTION.
|
||||
SaveRandomizedRecipes()
|
||||
SaveScars()
|
||||
collect_trophies()
|
||||
collect_maps()
|
||||
save_photo_persistence() //THIS IS PERSISTENCE, NOT THE LOGGING PORTION.
|
||||
save_randomized_recipes()
|
||||
save_scars()
|
||||
save_custom_outfits()
|
||||
save_delamination_counter()
|
||||
|
||||
/datum/controller/subsystem/persistence/proc/LoadPoly()
|
||||
///Loads up Poly's speech buffer.
|
||||
/datum/controller/subsystem/persistence/proc/load_poly()
|
||||
for(var/mob/living/simple_animal/parrot/poly/P in GLOB.alive_mob_list)
|
||||
twitterize(P.speech_buffer, "polytalk")
|
||||
break //Who's been duping the bird?!
|
||||
|
||||
///Loads all engravings, and places a select amount in maintenance and the prison.
|
||||
/datum/controller/subsystem/persistence/proc/load_wall_engravings()
|
||||
var/json_file = file(ENGRAVING_SAVE_FILE)
|
||||
if(!fexists(json_file))
|
||||
@@ -95,6 +98,7 @@ SUBSYSTEM_DEF(persistence)
|
||||
|
||||
log_world("Loaded [successfully_loaded_engravings] engraved messages on map [SSmapping.config.map_name]")
|
||||
|
||||
///Saves all new engravings in the world.
|
||||
/datum/controller/subsystem/persistence/proc/save_wall_engravings()
|
||||
var/list/saved_data = list()
|
||||
|
||||
@@ -134,6 +138,7 @@ SUBSYSTEM_DEF(persistence)
|
||||
|
||||
return json
|
||||
|
||||
///Loads all tattoos, and select a few based on the amount of prisoner spawn positions.
|
||||
/datum/controller/subsystem/persistence/proc/load_prisoner_tattoos()
|
||||
var/json_file = file(PRISONER_TATTOO_SAVE_FILE)
|
||||
if(!fexists(json_file))
|
||||
@@ -157,6 +162,7 @@ SUBSYSTEM_DEF(persistence)
|
||||
|
||||
log_world("Loaded [prison_tattoos_to_use.len] prison tattoos")
|
||||
|
||||
///Saves all tattoos, so they can appear on prisoners in future rounds
|
||||
/datum/controller/subsystem/persistence/proc/save_prisoner_tattoos()
|
||||
var/json_file = file(PRISONER_TATTOO_SAVE_FILE)
|
||||
var/list/saved_data = list()
|
||||
@@ -188,27 +194,68 @@ SUBSYSTEM_DEF(persistence)
|
||||
|
||||
return json
|
||||
|
||||
|
||||
/datum/controller/subsystem/persistence/proc/LoadTrophies()
|
||||
if(fexists("data/npc_saves/TrophyItems.sav")) //legacy compatability to convert old format to new
|
||||
var/savefile/S = new /savefile("data/npc_saves/TrophyItems.sav")
|
||||
var/saved_json
|
||||
S >> saved_json
|
||||
if(!saved_json)
|
||||
return
|
||||
saved_trophies = json_decode(saved_json)
|
||||
fdel("data/npc_saves/TrophyItems.sav")
|
||||
else
|
||||
/// Loads the trophies from the source file, and places a few in trophy display cases.
|
||||
/datum/controller/subsystem/persistence/proc/load_trophies()
|
||||
var/list/raw_saved_trophies = list()
|
||||
if(fexists("data/npc_saves/TrophyItems.json"))
|
||||
var/json_file = file("data/npc_saves/TrophyItems.json")
|
||||
if(!fexists(json_file))
|
||||
return
|
||||
var/list/json = json_decode(file2text(json_file))
|
||||
if(!json)
|
||||
return
|
||||
saved_trophies = json["data"]
|
||||
SetUpTrophies(saved_trophies.Copy())
|
||||
raw_saved_trophies = json["data"]
|
||||
fdel("data/npc_saves/TrophyItems.json")
|
||||
else
|
||||
var/json_file = file("data/trophy_items.json")
|
||||
if(!fexists(json_file))
|
||||
return
|
||||
var/list/json = json_decode(file2text(json_file))
|
||||
if(!json)
|
||||
return
|
||||
raw_saved_trophies = json["data"]
|
||||
|
||||
/datum/controller/subsystem/persistence/proc/LoadRecentMaps()
|
||||
for(var/raw_json in raw_saved_trophies)
|
||||
var/datum/trophy_data/parsed_trophy_data = new
|
||||
parsed_trophy_data.load_from_json(raw_json)
|
||||
saved_trophies += parsed_trophy_data
|
||||
|
||||
set_up_trophies()
|
||||
|
||||
///trophy data datum, for admin manipulation
|
||||
/datum/trophy_data
|
||||
///path of the item the trophy will try to mimic, null if path_string is invalid
|
||||
var/path
|
||||
///the message that appears under the item
|
||||
var/message
|
||||
///the key of the one who placed the item in the trophy case
|
||||
var/placer_key
|
||||
|
||||
/datum/trophy_data/proc/load_from_json(list/json_data)
|
||||
path = json_data["path"]
|
||||
message = json_data["message"]
|
||||
placer_key = json_data["placer_key"]
|
||||
|
||||
/datum/trophy_data/proc/to_json()
|
||||
var/list/new_data = list()
|
||||
new_data["path"] = path
|
||||
new_data["message"] = message
|
||||
new_data["placer_key"] = placer_key
|
||||
new_data["is_valid"] = text2path(path) ? TRUE : FALSE
|
||||
return new_data
|
||||
|
||||
/// Returns a list for the admin trophy panel.
|
||||
/datum/controller/subsystem/persistence/proc/trophy_ui_data()
|
||||
var/list/ui_data = list()
|
||||
for(var/datum/trophy_data/data in saved_trophies)
|
||||
var/list/pdata = data.to_json()
|
||||
pdata["ref"] = REF(data)
|
||||
ui_data += list(pdata)
|
||||
|
||||
return ui_data
|
||||
|
||||
/// Loads up the amount of times maps appeared to alter their appearance in voting and rotation.
|
||||
/datum/controller/subsystem/persistence/proc/load_recent_maps()
|
||||
var/map_sav = FILE_RECENT_MAPS
|
||||
if(!fexists(FILE_RECENT_MAPS))
|
||||
return
|
||||
@@ -229,44 +276,45 @@ SUBSYSTEM_DEF(persistence)
|
||||
if(run >= 2) //If run twice in the last KEEP_ROUNDS_MAP + 1 (including current) rounds, disable map for voting and rotation.
|
||||
blocked_maps += VM.map_name
|
||||
|
||||
/datum/controller/subsystem/persistence/proc/SetUpTrophies(list/trophy_items)
|
||||
for(var/A in GLOB.trophy_cases)
|
||||
var/obj/structure/displaycase/trophy/T = A
|
||||
if (T.showpiece)
|
||||
continue
|
||||
T.added_roundstart = TRUE
|
||||
/// Puts trophies into trophy cases.
|
||||
/datum/controller/subsystem/persistence/proc/set_up_trophies()
|
||||
|
||||
var/trophy_data = pick_n_take(trophy_items)
|
||||
var/list/valid_trophies = list()
|
||||
|
||||
if(!islist(trophy_data))
|
||||
for(var/datum/trophy_data/data in saved_trophies)
|
||||
|
||||
if(!data) //sanity for incorrect deserialization
|
||||
continue
|
||||
|
||||
var/list/chosen_trophy = trophy_data
|
||||
|
||||
if(!length(chosen_trophy)) //Malformed
|
||||
var/path = text2path(data.path)
|
||||
if(!path) //If the item no longer exist, ignore it
|
||||
continue
|
||||
|
||||
var/path = text2path(chosen_trophy["path"]) //If the item no longer exist, this returns null
|
||||
if(!path)
|
||||
valid_trophies += data
|
||||
|
||||
for(var/obj/structure/displaycase/trophy/trophy_case in GLOB.trophy_cases)
|
||||
if(!valid_trophies.len)
|
||||
break
|
||||
|
||||
if(trophy_case.showpiece)
|
||||
continue
|
||||
|
||||
T.showpiece = new /obj/item/showpiece_dummy(T, path)
|
||||
T.trophy_message = chosen_trophy["message"]
|
||||
T.placer_key = chosen_trophy["placer_key"]
|
||||
T.update_appearance()
|
||||
trophy_case.set_up_trophy(pick_n_take(valid_trophies))
|
||||
|
||||
/datum/controller/subsystem/persistence/proc/GetPhotoAlbums()
|
||||
///Loads up the photo album source file.
|
||||
/datum/controller/subsystem/persistence/proc/get_photo_albums()
|
||||
var/album_path = file("data/photo_albums.json")
|
||||
if(fexists(album_path))
|
||||
return json_decode(file2text(album_path))
|
||||
|
||||
/datum/controller/subsystem/persistence/proc/GetPhotoFrames()
|
||||
///Loads up the photo frames source file.
|
||||
/datum/controller/subsystem/persistence/proc/get_photo_frames()
|
||||
var/frame_path = file("data/photo_frames.json")
|
||||
if(fexists(frame_path))
|
||||
return json_decode(file2text(frame_path))
|
||||
|
||||
/// Removes the identifier of a persitent photo frame from the json.
|
||||
/datum/controller/subsystem/persistence/proc/RemovePhotoFrame(identifier)
|
||||
/// Removes the identifier of a persistent photo frame from the json.
|
||||
/datum/controller/subsystem/persistence/proc/remove_photo_frames(identifier)
|
||||
var/frame_path = file("data/photo_frames.json")
|
||||
if(!fexists(frame_path))
|
||||
return
|
||||
@@ -278,7 +326,8 @@ SUBSYSTEM_DEF(persistence)
|
||||
fdel(frame_path)
|
||||
WRITE_FILE(frame_path, frame_json)
|
||||
|
||||
/datum/controller/subsystem/persistence/proc/LoadPhotoPersistence()
|
||||
///Loads photo albums, and populates them; also loads and applies frames to picture frames.
|
||||
/datum/controller/subsystem/persistence/proc/load_photo_persistence()
|
||||
var/album_path = file("data/photo_albums.json")
|
||||
var/frame_path = file("data/photo_frames.json")
|
||||
if(fexists(album_path))
|
||||
@@ -301,7 +350,8 @@ SUBSYSTEM_DEF(persistence)
|
||||
if(json[PF.persistence_id])
|
||||
PF.load_from_id(json[PF.persistence_id])
|
||||
|
||||
/datum/controller/subsystem/persistence/proc/SavePhotoPersistence()
|
||||
///Saves the contents of photo albums and the picture frames.
|
||||
/datum/controller/subsystem/persistence/proc/save_photo_persistence()
|
||||
var/album_path = file("data/photo_albums.json")
|
||||
var/frame_path = file("data/photo_frames.json")
|
||||
|
||||
@@ -337,13 +387,25 @@ SUBSYSTEM_DEF(persistence)
|
||||
|
||||
WRITE_FILE(frame_path, frame_json)
|
||||
|
||||
/datum/controller/subsystem/persistence/proc/CollectTrophies()
|
||||
var/json_file = file("data/npc_saves/TrophyItems.json")
|
||||
///Collects trophies from all existing trophy cases.
|
||||
/datum/controller/subsystem/persistence/proc/collect_trophies()
|
||||
for(var/trophy_case in GLOB.trophy_cases)
|
||||
save_trophy(trophy_case)
|
||||
|
||||
var/json_file = file("data/trophy_items.json")
|
||||
var/list/file_data = list()
|
||||
file_data["data"] = remove_duplicate_trophies(saved_trophies)
|
||||
var/list/converted_data = list()
|
||||
|
||||
for(var/datum/trophy_data/data in saved_trophies)
|
||||
converted_data += list(data.to_json())
|
||||
|
||||
converted_data = remove_duplicate_trophies(converted_data)
|
||||
|
||||
file_data["data"] = converted_data
|
||||
fdel(json_file)
|
||||
WRITE_FILE(json_file, json_encode(file_data))
|
||||
|
||||
///gets the list of json trophies, and deletes the ones with an identical path and message
|
||||
/datum/controller/subsystem/persistence/proc/remove_duplicate_trophies(list/trophies)
|
||||
var/list/ukeys = list()
|
||||
. = list()
|
||||
@@ -355,15 +417,17 @@ SUBSYSTEM_DEF(persistence)
|
||||
. += list(trophy)
|
||||
ukeys[tkey] = TRUE
|
||||
|
||||
/datum/controller/subsystem/persistence/proc/SaveTrophy(obj/structure/displaycase/trophy/T)
|
||||
if(!T.added_roundstart && T.showpiece)
|
||||
var/list/data = list()
|
||||
data["path"] = T.showpiece.type
|
||||
data["message"] = T.trophy_message
|
||||
data["placer_key"] = T.placer_key
|
||||
saved_trophies += list(data)
|
||||
///If there is a trophy in the trophy case, saved it, if the trophy was not a holo trophy and has a message attached.
|
||||
/datum/controller/subsystem/persistence/proc/save_trophy(obj/structure/displaycase/trophy/trophy_case)
|
||||
if(!trophy_case.holographic_showpiece && trophy_case.showpiece && trophy_case.trophy_message)
|
||||
var/datum/trophy_data/data = new
|
||||
data.path = trophy_case.showpiece.type
|
||||
data.message = trophy_case.trophy_message
|
||||
data.placer_key = trophy_case.placer_key
|
||||
saved_trophies += data
|
||||
|
||||
/datum/controller/subsystem/persistence/proc/CollectMaps()
|
||||
///Updates the list of the most recent maps.
|
||||
/datum/controller/subsystem/persistence/proc/collect_maps()
|
||||
if(length(saved_maps) > KEEP_ROUNDS_MAP) //Get rid of extras from old configs.
|
||||
saved_maps.Cut(KEEP_ROUNDS_MAP+1)
|
||||
var/mapstosave = min(length(saved_maps)+1, KEEP_ROUNDS_MAP)
|
||||
@@ -378,7 +442,8 @@ SUBSYSTEM_DEF(persistence)
|
||||
fdel(json_file)
|
||||
WRITE_FILE(json_file, json_encode(file_data))
|
||||
|
||||
/datum/controller/subsystem/persistence/proc/LoadRandomizedRecipes()
|
||||
///Loads all randomized recipes.
|
||||
/datum/controller/subsystem/persistence/proc/load_randomized_recipes()
|
||||
var/json_file = file("data/RandomizedChemRecipes.json")
|
||||
var/json
|
||||
if(fexists(json_file))
|
||||
@@ -402,7 +467,8 @@ SUBSYSTEM_DEF(persistence)
|
||||
else
|
||||
log_game("Randomized recipe [randomized_type] resulted in conflicting recipes.")
|
||||
|
||||
/datum/controller/subsystem/persistence/proc/SaveRandomizedRecipes()
|
||||
///Saves all randomized recipes.
|
||||
/datum/controller/subsystem/persistence/proc/save_randomized_recipes()
|
||||
var/json_file = file("data/RandomizedChemRecipes.json")
|
||||
var/list/file_data = list()
|
||||
|
||||
@@ -416,7 +482,8 @@ SUBSYSTEM_DEF(persistence)
|
||||
fdel(json_file)
|
||||
WRITE_FILE(json_file, json_encode(file_data))
|
||||
|
||||
/datum/controller/subsystem/persistence/proc/SaveScars()
|
||||
///Saves all scars for everyone's original characters
|
||||
/datum/controller/subsystem/persistence/proc/save_scars()
|
||||
for(var/i in GLOB.joined_player_list)
|
||||
var/mob/living/carbon/human/ending_human = get_mob_by_ckey(i)
|
||||
if(!istype(ending_human) || !ending_human.mind?.original_character_slot_index || !ending_human.client?.prefs.read_preference(/datum/preference/toggle/persistent_scars))
|
||||
@@ -432,7 +499,7 @@ SUBSYSTEM_DEF(persistence)
|
||||
else
|
||||
original_human.save_persistent_scars()
|
||||
|
||||
|
||||
///Loads the custom outfits of every admin.
|
||||
/datum/controller/subsystem/persistence/proc/load_custom_outfits()
|
||||
var/file = file("data/custom_outfits.json")
|
||||
if(!fexists(file))
|
||||
@@ -454,6 +521,7 @@ SUBSYSTEM_DEF(persistence)
|
||||
continue
|
||||
GLOB.custom_outfits += outfit
|
||||
|
||||
///Saves each admin's custom outfit list
|
||||
/datum/controller/subsystem/persistence/proc/save_custom_outfits()
|
||||
var/file = file("data/custom_outfits.json")
|
||||
fdel(file)
|
||||
|
||||
@@ -9,28 +9,28 @@
|
||||
armor = list(MELEE = 30, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 10, BIO = 0, FIRE = 70, ACID = 100)
|
||||
max_integrity = 200
|
||||
integrity_failure = 0.25
|
||||
///The showpiece item inside the case
|
||||
var/obj/item/showpiece = null
|
||||
var/obj/item/showpiece_type = null //This allows for showpieces that can only hold items if they're the same istype as this.
|
||||
///This allows for showpieces that can only hold items if they're the same istype as this.
|
||||
var/obj/item/showpiece_type = null
|
||||
///Is the displaycase hooked up to a burglar alarm?
|
||||
var/alert = TRUE
|
||||
///Is the displaycase open at the moment?
|
||||
var/open = FALSE
|
||||
var/openable = TRUE
|
||||
var/custom_glass_overlay = FALSE ///If we have a custom glass overlay to use.
|
||||
///If we have a custom glass overlay to use.
|
||||
var/custom_glass_overlay = FALSE
|
||||
var/obj/item/electronics/airlock/electronics
|
||||
var/start_showpiece_type = null //add type for items on display
|
||||
var/list/start_showpieces = list() //Takes sublists in the form of list("type" = /obj/item/bikehorn, "trophy_message" = "henk")
|
||||
var/trophy_message = ""
|
||||
///Add type for items on display
|
||||
var/start_showpiece_type = null
|
||||
///Displaycase is fixed by glass
|
||||
var/glass_fix = TRUE
|
||||
///Represents a signel source of screaming when broken
|
||||
var/datum/alarm_handler/alarm_manager
|
||||
///Used for subtypes that have a UI in them. The examine on click while adjecent will not fire, as we already get a popup
|
||||
var/autoexamine_while_closed = TRUE
|
||||
|
||||
/obj/structure/displaycase/Initialize(mapload)
|
||||
. = ..()
|
||||
if(start_showpieces.len && !start_showpiece_type)
|
||||
var/list/showpiece_entry = pick(start_showpieces)
|
||||
if (showpiece_entry && showpiece_entry["type"])
|
||||
start_showpiece_type = showpiece_entry["type"]
|
||||
if (showpiece_entry["trophy_message"])
|
||||
trophy_message = showpiece_entry["trophy_message"]
|
||||
if(start_showpiece_type)
|
||||
showpiece = new start_showpiece_type (src)
|
||||
update_appearance()
|
||||
@@ -61,9 +61,8 @@
|
||||
. += span_notice("Hooked up with an anti-theft system.")
|
||||
if(showpiece)
|
||||
. += span_notice("There's \a [showpiece] inside.")
|
||||
if(trophy_message)
|
||||
. += "The plaque reads:\n [trophy_message]"
|
||||
|
||||
///Removes the showpiece from the displaycase
|
||||
/obj/structure/displaycase/proc/dump()
|
||||
if(QDELETED(showpiece))
|
||||
return
|
||||
@@ -124,27 +123,27 @@
|
||||
. += "[initial(icon_state)]_closed"
|
||||
return
|
||||
|
||||
/obj/structure/displaycase/attackby(obj/item/W, mob/living/user, params)
|
||||
if(W.GetID() && !broken && openable)
|
||||
/obj/structure/displaycase/attackby(obj/item/tool, mob/living/user, params)
|
||||
if(tool.GetID() && !broken)
|
||||
if(allowed(user))
|
||||
to_chat(user, span_notice("You [open ? "close":"open"] [src]."))
|
||||
to_chat(user, span_notice("You [open ? "close":"open"] [src]."))
|
||||
toggle_lock(user)
|
||||
else
|
||||
to_chat(user, span_alert("Access denied."))
|
||||
else if(W.tool_behaviour == TOOL_WELDER && !user.combat_mode && !broken)
|
||||
else if(tool.tool_behaviour == TOOL_WELDER && !user.combat_mode && !broken)
|
||||
if(atom_integrity < max_integrity)
|
||||
if(!W.tool_start_check(user, amount=5))
|
||||
if(!tool.tool_start_check(user, amount=5))
|
||||
return
|
||||
|
||||
to_chat(user, span_notice("You begin repairing [src]..."))
|
||||
if(W.use_tool(src, user, 40, amount=5, volume=50))
|
||||
if(tool.use_tool(src, user, 40, amount=5, volume=50))
|
||||
atom_integrity = max_integrity
|
||||
update_appearance()
|
||||
to_chat(user, span_notice("You repair [src]."))
|
||||
else
|
||||
to_chat(user, span_warning("[src] is already in good condition!"))
|
||||
return
|
||||
else if(!alert && W.tool_behaviour == TOOL_CROWBAR && openable) //Only applies to the lab cage and player made display cases
|
||||
else if(!alert && tool.tool_behaviour == TOOL_CROWBAR) //Only applies to the lab cage and player made display cases
|
||||
if(broken)
|
||||
if(showpiece)
|
||||
to_chat(user, span_warning("Remove the displayed object first!"))
|
||||
@@ -153,35 +152,39 @@
|
||||
qdel(src)
|
||||
else
|
||||
to_chat(user, span_notice("You start to [open ? "close":"open"] [src]..."))
|
||||
if(W.use_tool(src, user, 20))
|
||||
to_chat(user, span_notice("You [open ? "close":"open"] [src]."))
|
||||
if(tool.use_tool(src, user, 20))
|
||||
to_chat(user, span_notice("You [open ? "close":"open"] [src]."))
|
||||
toggle_lock(user)
|
||||
else if(open && !showpiece)
|
||||
insert_showpiece(W, user)
|
||||
else if(glass_fix && broken && istype(W, /obj/item/stack/sheet/glass))
|
||||
var/obj/item/stack/sheet/glass/G = W
|
||||
if(G.get_amount() < 2)
|
||||
insert_showpiece(tool, user)
|
||||
return TRUE //cancel the attack chain, wether we successfully placed an item or not
|
||||
else if(glass_fix && broken && istype(tool, /obj/item/stack/sheet/glass))
|
||||
var/obj/item/stack/sheet/glass/glass_sheet = tool
|
||||
if(glass_sheet.get_amount() < 2)
|
||||
to_chat(user, span_warning("You need two glass sheets to fix the case!"))
|
||||
return
|
||||
to_chat(user, span_notice("You start fixing [src]..."))
|
||||
if(do_after(user, 20, target = src))
|
||||
G.use(2)
|
||||
glass_sheet.use(2)
|
||||
broken = FALSE
|
||||
atom_integrity = max_integrity
|
||||
update_appearance()
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/structure/displaycase/proc/insert_showpiece(obj/item/wack, mob/user)
|
||||
if(showpiece_type && !istype(wack, showpiece_type))
|
||||
///Handles placing an item into the display case. Returns TRUE if the item failed to be placed inside the container, useful for descendants
|
||||
/obj/structure/displaycase/proc/insert_showpiece(obj/item/new_showpiece, mob/user)
|
||||
if(showpiece_type && !istype(new_showpiece, showpiece_type))
|
||||
to_chat(user, span_notice("This doesn't belong in this kind of display."))
|
||||
return TRUE
|
||||
if(user.transferItemToLoc(wack, src))
|
||||
showpiece = wack
|
||||
to_chat(user, span_notice("You put [wack] on display."))
|
||||
if(user.transferItemToLoc(new_showpiece, src))
|
||||
showpiece = new_showpiece
|
||||
to_chat(user, span_notice("You put [new_showpiece] on display."))
|
||||
update_appearance()
|
||||
|
||||
///Opens and closes the display case
|
||||
/obj/structure/displaycase/proc/toggle_lock(mob/user)
|
||||
playsound(src, 'sound/machines/click.ogg', 20, TRUE)
|
||||
open = !open
|
||||
update_appearance()
|
||||
|
||||
@@ -200,10 +203,12 @@
|
||||
add_fingerprint(user)
|
||||
return
|
||||
else
|
||||
//prevents remote "kicks" with TK
|
||||
//prevents remote "kicks" with TK
|
||||
if (!Adjacent(user))
|
||||
return
|
||||
if (!user.combat_mode)
|
||||
if(!open && !autoexamine_while_closed)
|
||||
return
|
||||
if(!user.is_blind())
|
||||
user.examinate(src)
|
||||
return
|
||||
@@ -290,11 +295,17 @@
|
||||
/obj/structure/displaycase/trophy
|
||||
name = "trophy display case"
|
||||
desc = "Store your trophies of accomplishment in here, and they will stay forever."
|
||||
var/placer_key = ""
|
||||
var/added_roundstart = TRUE
|
||||
var/is_locked = TRUE
|
||||
integrity_failure = 0
|
||||
openable = FALSE
|
||||
req_access = list(ACCESS_LIBRARY)
|
||||
autoexamine_while_closed = FALSE
|
||||
///the key of the player who placed the item in the case
|
||||
var/placer_key = ""
|
||||
///is the trophy a hologram, not a real item placed by a player?
|
||||
var/holographic_showpiece = FALSE
|
||||
///are we about to edit
|
||||
var/historian_mode = FALSE
|
||||
///the trophy message
|
||||
var/trophy_message = ""
|
||||
|
||||
/obj/structure/displaycase/trophy/Initialize(mapload)
|
||||
. = ..()
|
||||
@@ -304,93 +315,121 @@
|
||||
GLOB.trophy_cases -= src
|
||||
return ..()
|
||||
|
||||
///Creates a showpiece dummy to display, using persistent data
|
||||
/obj/structure/displaycase/trophy/proc/set_up_trophy(datum/trophy_data/chosen_trophy)
|
||||
showpiece = new /obj/item/showpiece_dummy(src, text2path(chosen_trophy.path))
|
||||
trophy_message = trim(chosen_trophy.message, MAX_PLAQUE_LEN)
|
||||
if(trophy_message == "")
|
||||
trophy_message = trim(showpiece.desc, MAX_PLAQUE_LEN)
|
||||
placer_key = trim(chosen_trophy.placer_key)
|
||||
holographic_showpiece = TRUE
|
||||
update_appearance()
|
||||
|
||||
/obj/structure/displaycase/trophy/attackby(obj/item/W, mob/living/user, params)
|
||||
|
||||
if(!user.Adjacent(src)) //no TK museology
|
||||
if(istype(W, /obj/item/key/displaycase))
|
||||
toggle_historian_mode(user)
|
||||
return
|
||||
if(user.combat_mode)
|
||||
return ..()
|
||||
if(W.tool_behaviour == TOOL_WELDER && !broken)
|
||||
return ..()
|
||||
|
||||
if(user.is_holding_item_of_type(/obj/item/key/displaycase))
|
||||
if(added_roundstart)
|
||||
is_locked = !is_locked
|
||||
to_chat(user, span_notice("You [!is_locked ? "un" : ""]lock the case."))
|
||||
else
|
||||
to_chat(user, span_warning("The lock is stuck shut!"))
|
||||
return
|
||||
|
||||
if(is_locked)
|
||||
to_chat(user, span_warning("The case is shut tight with an old-fashioned physical lock. Maybe you should ask the curator for the key?"))
|
||||
return
|
||||
|
||||
if(!added_roundstart)
|
||||
to_chat(user, span_warning("You've already put something new in this case!"))
|
||||
return
|
||||
|
||||
if(is_type_in_typecache(W, GLOB.blacklisted_cargo_types))
|
||||
to_chat(user, span_warning("The case rejects the [W]!"))
|
||||
return
|
||||
|
||||
for(var/a in W.get_all_contents())
|
||||
if(is_type_in_typecache(a, GLOB.blacklisted_cargo_types))
|
||||
to_chat(user, span_warning("The case rejects the [W]!"))
|
||||
return
|
||||
|
||||
if(user.transferItemToLoc(W, src))
|
||||
|
||||
if(showpiece)
|
||||
to_chat(user, span_notice("You press a button, and [showpiece] descends into the floor of the case."))
|
||||
QDEL_NULL(showpiece)
|
||||
|
||||
to_chat(user, span_notice("You insert [W] into the case."))
|
||||
showpiece = W
|
||||
added_roundstart = FALSE
|
||||
update_appearance()
|
||||
|
||||
placer_key = user.ckey
|
||||
|
||||
trophy_message = W.desc //default value
|
||||
|
||||
var/chosen_plaque = tgui_input_text(user, "What would you like the plaque to say? Default value is item's description.", "Trophy Plaque", trophy_message)
|
||||
if(chosen_plaque)
|
||||
if(user.Adjacent(src))
|
||||
trophy_message = chosen_plaque
|
||||
to_chat(user, span_notice("You set the plaque's text."))
|
||||
else
|
||||
to_chat(user, span_warning("You are too far to set the plaque's text!"))
|
||||
|
||||
SSpersistence.SaveTrophy(src)
|
||||
return TRUE
|
||||
|
||||
else
|
||||
to_chat(user, span_warning("\The [W] is stuck to your hand, you can't put it in the [src.name]!"))
|
||||
|
||||
return
|
||||
return ..()
|
||||
|
||||
/obj/structure/displaycase/trophy/dump()
|
||||
if (showpiece)
|
||||
if(added_roundstart)
|
||||
visible_message(span_danger("The [showpiece] crumbles to dust!"))
|
||||
new /obj/effect/decal/cleanable/ash(loc)
|
||||
if(holographic_showpiece)
|
||||
visible_message(span_danger("[showpiece] fizzles and vanishes!"))
|
||||
do_sparks(number = 1, cardinal_only = FALSE, source = src)
|
||||
QDEL_NULL(showpiece)
|
||||
holographic_showpiece = FALSE
|
||||
else
|
||||
return ..()
|
||||
..()
|
||||
placer_key = ""
|
||||
trophy_message = null
|
||||
|
||||
/obj/structure/displaycase/trophy/insert_showpiece(obj/item/new_showpiece, mob/user)
|
||||
if(..())
|
||||
return TRUE
|
||||
if(showpiece == new_showpiece)
|
||||
placer_key = user.ckey
|
||||
|
||||
///Toggles the mode that shows the historian panel on the UI, enabling saving the looks and the trophy message of the current trophy
|
||||
/obj/structure/displaycase/trophy/proc/toggle_historian_mode(mob/user)
|
||||
historian_mode = !historian_mode
|
||||
balloon_alert(user, "[historian_mode ? "enabled" : "disabled"] historian mode.")
|
||||
playsound(src, 'sound/machines/twobeep.ogg', vary = 50)
|
||||
SStgui.update_uis(src)
|
||||
|
||||
/obj/structure/displaycase/trophy/toggle_lock(mob/user)
|
||||
..()
|
||||
SStgui.close_uis(src)
|
||||
|
||||
/obj/structure/displaycase/trophy/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["historian_mode"] = historian_mode
|
||||
data["holographic_showpiece"] = holographic_showpiece
|
||||
data["max_length"] = MAX_PLAQUE_LEN
|
||||
data["has_showpiece"] = showpiece ? TRUE : FALSE
|
||||
if(showpiece)
|
||||
data["showpiece_name"] = capitalize(format_text(showpiece.name))
|
||||
data["showpiece_description"] = trophy_message ? format_text(trophy_message) : null
|
||||
return data
|
||||
|
||||
/obj/structure/displaycase/trophy/ui_static_data(mob/user)
|
||||
var/list/data = list()
|
||||
if(showpiece)
|
||||
data["showpiece_icon"] = icon2base64(getFlatIcon(showpiece, no_anim=TRUE))
|
||||
return data
|
||||
|
||||
/obj/structure/displaycase/trophy/ui_act(action, params)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
switch(action)
|
||||
if("insert_key")
|
||||
if(historian_mode)
|
||||
return
|
||||
var/obj/item/key/displaycase/trophy_key = usr.get_active_held_item()
|
||||
if(istype(trophy_key))
|
||||
toggle_historian_mode(usr)
|
||||
return TRUE
|
||||
return
|
||||
if("change_message")
|
||||
if(showpiece && !holographic_showpiece)
|
||||
var/new_trophy_message = tgui_input_text(usr, "Let's make history!", "Trophy Message", trophy_message, MAX_PLAQUE_LEN)
|
||||
if(!new_trophy_message)
|
||||
return
|
||||
trophy_message = new_trophy_message
|
||||
return TRUE
|
||||
if("lock")
|
||||
if(!historian_mode)
|
||||
return
|
||||
toggle_historian_mode(usr)
|
||||
return TRUE
|
||||
|
||||
/obj/structure/displaycase/trophy/ui_interact(mob/user, datum/tgui/ui)
|
||||
if(open)
|
||||
return
|
||||
if(isliving(usr))
|
||||
var/mob/living/living_usr = usr
|
||||
if(living_usr.combat_mode)
|
||||
return
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "Trophycase", name)
|
||||
ui.set_autoupdate(FALSE)
|
||||
ui.open()
|
||||
|
||||
/obj/item/key/displaycase
|
||||
name = "display case key"
|
||||
desc = "The key to the curator's display cases."
|
||||
|
||||
/obj/item/showpiece_dummy
|
||||
name = "Cheap replica"
|
||||
name = "holographic replica"
|
||||
|
||||
/obj/item/showpiece_dummy/Initialize(mapload, path)
|
||||
. = ..()
|
||||
var/obj/item/I = path
|
||||
name = initial(I.name)
|
||||
icon = initial(I.icon)
|
||||
icon_state = initial(I.icon_state)
|
||||
var/obj/item/item_path = path
|
||||
name = initial(item_path.name)
|
||||
desc = initial(item_path.desc)
|
||||
icon = initial(item_path.icon)
|
||||
icon_state = initial(item_path.icon_state)
|
||||
|
||||
/obj/structure/displaycase/forsale
|
||||
name = "vend-a-tray"
|
||||
@@ -403,6 +442,7 @@
|
||||
alert = FALSE //No, we're not calling the fire department because someone stole your cookie.
|
||||
glass_fix = FALSE //Fixable with tools instead.
|
||||
pass_flags = PASSTABLE ///Can be placed and moved onto a table.
|
||||
autoexamine_while_closed = FALSE
|
||||
///The price of the item being sold. Altered by grab intent ID use.
|
||||
var/sale_price = 20
|
||||
///The Account which will receive payment for purchases. Set by the first ID to swipe the tray.
|
||||
@@ -417,6 +457,19 @@
|
||||
if(!broken && !open)
|
||||
. += "[initial(icon_state)]_overlay"
|
||||
|
||||
/obj/structure/displaycase/forsale/insert_showpiece(obj/item/new_showpiece, mob/user)
|
||||
if(..())
|
||||
return TRUE
|
||||
update_static_data_for_all_viewers()
|
||||
|
||||
/obj/structure/displaycase/forsale/dump()
|
||||
..()
|
||||
update_static_data_for_all_viewers()
|
||||
|
||||
/obj/structure/displaycase/forsale/toggle_lock()
|
||||
..()
|
||||
SStgui.update_uis(src)
|
||||
|
||||
/obj/structure/displaycase/forsale/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
@@ -426,19 +479,18 @@
|
||||
|
||||
/obj/structure/displaycase/forsale/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
var/register = FALSE
|
||||
if(payments_acc)
|
||||
register = TRUE
|
||||
data["owner_name"] = payments_acc.account_holder
|
||||
if(showpiece)
|
||||
data["product_name"] = capitalize(showpiece.name)
|
||||
var/base64 = icon2base64(icon(showpiece.icon, showpiece.icon_state))
|
||||
data["product_icon"] = base64
|
||||
data["registered"] = register
|
||||
data["owner_name"] = payments_acc ? payments_acc.account_holder : null
|
||||
data["product_name"] = showpiece ?capitalize(format_text(showpiece.name)) : null
|
||||
data["registered"] = payments_acc ? TRUE : FALSE
|
||||
data["product_cost"] = sale_price
|
||||
data["tray_open"] = open
|
||||
return data
|
||||
|
||||
/obj/structure/displaycase/forsale/ui_static_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["product_icon"] = showpiece ? icon2base64(getFlatIcon(showpiece, no_anim=TRUE)) : null
|
||||
return data
|
||||
|
||||
/obj/structure/displaycase/forsale/ui_act(action, params)
|
||||
. = ..()
|
||||
if(.)
|
||||
@@ -471,16 +523,16 @@
|
||||
to_chat(usr, span_notice("You do not possess the funds to purchase this."))
|
||||
return TRUE
|
||||
else
|
||||
account.adjust_money(-sale_price, "Display Case: [capitalize(showpiece)]")
|
||||
account.adjust_money(-sale_price, "Display Case: [capitalize(showpiece.name)]")
|
||||
if(payments_acc)
|
||||
payments_acc.adjust_money(sale_price, "Display Case: [capitalize(showpiece)]")
|
||||
payments_acc.adjust_money(sale_price, "Display Case: [capitalize(showpiece.name)]")
|
||||
usr.put_in_hands(showpiece)
|
||||
to_chat(usr, span_notice("You purchase [showpiece] for [sale_price] credits."))
|
||||
playsound(src, 'sound/effects/cashregister.ogg', 40, TRUE)
|
||||
flick("[initial(icon_state)]_vend", src)
|
||||
showpiece = null
|
||||
update_appearance()
|
||||
SStgui.update_uis(src)
|
||||
update_static_data_for_all_viewers()
|
||||
return TRUE
|
||||
if("Open")
|
||||
if(!payments_acc)
|
||||
@@ -492,7 +544,6 @@
|
||||
playsound(src, 'sound/machines/buzz-sigh.ogg', 50, TRUE)
|
||||
return
|
||||
toggle_lock()
|
||||
SStgui.update_uis(src)
|
||||
if("Register")
|
||||
if(payments_acc)
|
||||
return
|
||||
@@ -522,6 +573,7 @@
|
||||
SStgui.update_uis(src)
|
||||
return TRUE
|
||||
. = TRUE
|
||||
|
||||
/obj/structure/displaycase/forsale/attackby(obj/item/I, mob/living/user, params)
|
||||
if(isidcard(I))
|
||||
//Card Registration
|
||||
@@ -530,18 +582,16 @@
|
||||
to_chat(user, span_warning("This ID card has no account registered!"))
|
||||
return
|
||||
if(payments_acc == potential_acc.registered_account)
|
||||
playsound(src, 'sound/machines/click.ogg', 20, TRUE)
|
||||
toggle_lock()
|
||||
return
|
||||
if(istype(I, /obj/item/modular_computer))
|
||||
return TRUE
|
||||
SStgui.update_uis(src)
|
||||
. = ..()
|
||||
|
||||
return ..()
|
||||
|
||||
/obj/structure/displaycase/forsale/multitool_act(mob/living/user, obj/item/I)
|
||||
. = ..()
|
||||
if(atom_integrity <= (integrity_failure * max_integrity))
|
||||
if(atom_integrity <= (integrity_failure * max_integrity))
|
||||
to_chat(user, span_notice("You start recalibrating [src]'s hover field..."))
|
||||
if(do_after(user, 20, target = src))
|
||||
broken = FALSE
|
||||
|
||||
@@ -86,6 +86,7 @@ GLOBAL_PROTECT(admin_verbs_admin)
|
||||
/client/proc/list_dna,
|
||||
/client/proc/list_fingerprints,
|
||||
/client/proc/message_pda, /*send a message to somebody on PDA*/
|
||||
/datum/admins/proc/trophy_manager,
|
||||
)
|
||||
GLOBAL_LIST_INIT(admin_verbs_ban, list(/client/proc/unban_panel, /client/proc/ban_panel, /client/proc/stickybanpanel))
|
||||
GLOBAL_PROTECT(admin_verbs_ban)
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/datum/admins/proc/trophy_manager()
|
||||
set name = "Trophy Manager"
|
||||
set category = "Admin"
|
||||
|
||||
if(!check_rights(R_ADMIN))
|
||||
return
|
||||
var/datum/trophy_manager/ui = new(usr)
|
||||
ui.ui_interact(usr)
|
||||
|
||||
/// Trophy Admin Management Panel
|
||||
/datum/trophy_manager
|
||||
|
||||
/datum/trophy_manager/ui_state(mob/user)
|
||||
return GLOB.admin_state
|
||||
|
||||
/datum/trophy_manager/ui_close(mob/user)
|
||||
qdel(src)
|
||||
|
||||
/datum/trophy_manager/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "TrophyAdminPanel")
|
||||
ui.open()
|
||||
|
||||
/datum/trophy_manager/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["trophies"] = SSpersistence.trophy_ui_data()
|
||||
return data
|
||||
|
||||
/datum/trophy_manager/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
if(..())
|
||||
return
|
||||
if (!check_rights(R_ADMIN))
|
||||
return
|
||||
var/mob/user = usr
|
||||
var/datum/trophy_data/trophy = locate(params["ref"]) in SSpersistence.saved_trophies
|
||||
if(!trophy)
|
||||
return
|
||||
switch(action)
|
||||
if("delete")
|
||||
SSpersistence.saved_trophies -= trophy
|
||||
log_admin("[key_name(user)] has deleted a trophy made by [trophy.placer_key].")
|
||||
message_admins(span_notice("[key_name_admin(user)] has deleted trophy made by [trophy.placer_key]."))
|
||||
return TRUE
|
||||
if("edit_message")
|
||||
var/old_message = trophy.message
|
||||
var/new_message = tgui_input_text(user, "New trophy message?", "Message Editing", trophy.message, max_length = MAX_PLAQUE_LEN)
|
||||
if(!new_message)
|
||||
return
|
||||
trophy.message = new_message
|
||||
log_admin("[key_name(user)] has edited the message of trophy made by [trophy.placer_key] from \"[old_message]\" to \"[new_message]\".")
|
||||
return TRUE
|
||||
if("edit_path")
|
||||
var/old_path = trophy.path
|
||||
var/new_path = tgui_input_text(user, "New trophy path?", "Path Editing", trophy.path)
|
||||
if(!new_path)
|
||||
return
|
||||
if(!text2path(new_path))
|
||||
to_chat(user, span_warning("Invalid path selected"))
|
||||
return
|
||||
trophy.path = new_path
|
||||
log_admin("[key_name(user)] has edited the item path of trophy made by [trophy.placer_key] from \"[old_path]\" to \"[new_path]\".")
|
||||
return TRUE
|
||||
@@ -41,7 +41,7 @@
|
||||
|
||||
//Manual loading, DO NOT USE FOR HARDCODED/MAPPED IN ALBUMS. This is for if an album needs to be loaded mid-round from an ID.
|
||||
/obj/item/storage/photo_album/proc/persistence_load()
|
||||
var/list/data = SSpersistence.GetPhotoAlbums()
|
||||
var/list/data = SSpersistence.get_photo_albums()
|
||||
if(data[persistence_id])
|
||||
populate_from_id_list(data[persistence_id])
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
/obj/structure/sign/picture_frame/Destroy()
|
||||
LAZYREMOVE(SSpersistence.photo_frames, src)
|
||||
if(persistence_id && del_id_on_destroy)
|
||||
SSpersistence.RemovePhotoFrame(persistence_id)
|
||||
SSpersistence.remove_photo_frames(persistence_id)
|
||||
return ..()
|
||||
|
||||
/obj/structure/sign/picture_frame/proc/get_photo_id()
|
||||
@@ -97,7 +97,7 @@
|
||||
|
||||
//Manual loading, DO NOT USE FOR HARDCODED/MAPPED IN ALBUMS. This is for if an album needs to be loaded mid-round from an ID.
|
||||
/obj/structure/sign/picture_frame/proc/persistence_load()
|
||||
var/list/data = SSpersistence.GetPhotoFrames()
|
||||
var/list/data = SSpersistence.get_photo_frames()
|
||||
if(data[persistence_id])
|
||||
load_from_id(data[persistence_id])
|
||||
|
||||
|
||||
@@ -2095,6 +2095,7 @@
|
||||
#include "code\modules\admin\tag.dm"
|
||||
#include "code\modules\admin\team_panel.dm"
|
||||
#include "code\modules\admin\topic.dm"
|
||||
#include "code\modules\admin\trophy_manager.dm"
|
||||
#include "code\modules\admin\whitelist.dm"
|
||||
#include "code\modules\admin\callproc\callproc.dm"
|
||||
#include "code\modules\admin\smites\bad_luck.dm"
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
import { decodeHtmlEntities } from 'common/string';
|
||||
import { useBackend } from '../backend';
|
||||
import { Button, Table } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
export const TrophyAdminPanel = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const { trophies } = data;
|
||||
return (
|
||||
<Window title="Trophies Admin Panel" width={800} height={600}>
|
||||
<Window.Content scrollable>
|
||||
<Table>
|
||||
<Table.Row header>
|
||||
<Table.Cell color="label">Path</Table.Cell>
|
||||
<Table.Cell color="label" />
|
||||
<Table.Cell color="label">Message</Table.Cell>
|
||||
<Table.Cell color="label" />
|
||||
<Table.Cell color="label">Placer Key</Table.Cell>
|
||||
<Table.Cell color="label" />
|
||||
</Table.Row>
|
||||
{!!trophies &&
|
||||
trophies.map((trophy) => (
|
||||
<Table.Row key={trophy.ref} className="candystripe">
|
||||
<Table.Cell
|
||||
style={{
|
||||
'word-break': 'break-all',
|
||||
'word-wrap': 'break-word',
|
||||
'color': !trophy.is_valid
|
||||
? 'rgba(255, 0, 0, 0.5)'
|
||||
: 'inherit',
|
||||
}}>
|
||||
{decodeHtmlEntities(trophy.path)}
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Button
|
||||
icon="edit"
|
||||
tooltip={'Edit path'}
|
||||
tooltipPosition="bottom"
|
||||
onClick={() => act('edit_path', { ref: trophy.ref })}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell
|
||||
style={{
|
||||
'word-break': 'break-all',
|
||||
'word-wrap': 'break-word',
|
||||
}}>
|
||||
{decodeHtmlEntities(trophy.message)}
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Button
|
||||
icon="edit"
|
||||
tooltip={'Edit message'}
|
||||
tooltipPosition="bottom"
|
||||
onClick={() => act('edit_message', { ref: trophy.ref })}
|
||||
/>
|
||||
</Table.Cell>
|
||||
<Table.Cell
|
||||
style={{
|
||||
'word-break': 'break-all',
|
||||
'word-wrap': 'break-word',
|
||||
}}>
|
||||
{decodeHtmlEntities(trophy.placer_key)}
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Button
|
||||
icon="trash"
|
||||
tooltip={'Delete trophy'}
|
||||
tooltipPosition="bottom"
|
||||
onClick={() => act('delete', { ref: trophy.ref })}
|
||||
/>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
))}
|
||||
</Table>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,159 @@
|
||||
import { decodeHtmlEntities } from 'common/string';
|
||||
import { useBackend } from '../backend';
|
||||
import { Icon, Box, Button, Dimmer, Section, Stack } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
export const Trophycase = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
return (
|
||||
<Window width={300} height={380}>
|
||||
<Window.Content>
|
||||
<Stack vertical fill>
|
||||
<Stack.Item>
|
||||
<ShowpieceName />
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<ShowpieceImage />
|
||||
</Stack.Item>
|
||||
<Stack.Item grow>
|
||||
<ShowpieceDescription />
|
||||
</Stack.Item>
|
||||
<Stack.Divider />
|
||||
<Stack.Item>
|
||||
<HistorianPanel />
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
const HistorianPanel = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
has_showpiece,
|
||||
historian_mode,
|
||||
holographic_showpiece,
|
||||
showpiece_description,
|
||||
} = data;
|
||||
|
||||
return (
|
||||
<Section align="left">
|
||||
{!historian_mode && (
|
||||
<Button
|
||||
icon="key"
|
||||
content="Insert key for historian mode"
|
||||
onClick={() => act('insert_key')}
|
||||
/>
|
||||
)}
|
||||
{!!historian_mode && (
|
||||
<div>
|
||||
<Button
|
||||
icon="times"
|
||||
content="Lock historian mode"
|
||||
onClick={() => act('lock')}
|
||||
/>
|
||||
<Button
|
||||
icon="pencil"
|
||||
content="Edit description"
|
||||
disabled={!has_showpiece || holographic_showpiece}
|
||||
onClick={() => act('change_message')}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{!!historian_mode && !!holographic_showpiece && (
|
||||
<Box>
|
||||
A holographic trophy is already present. Replace it with a new trophy
|
||||
to create a new recording.
|
||||
</Box>
|
||||
)}
|
||||
{!!historian_mode && !has_showpiece && <Box>No trophies located.</Box>}
|
||||
{!!historian_mode &&
|
||||
!!has_showpiece &&
|
||||
!holographic_showpiece &&
|
||||
!!showpiece_description && (
|
||||
<Box>
|
||||
Recording has begun. Trophy data will be saved overnight, as long as
|
||||
the trophy stays within an intact case.
|
||||
</Box>
|
||||
)}
|
||||
{!!historian_mode &&
|
||||
!!has_showpiece &&
|
||||
!holographic_showpiece &&
|
||||
!showpiece_description && (
|
||||
<Box>
|
||||
New trophy detected. Please record a description to begin archival.
|
||||
</Box>
|
||||
)}
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const ShowpieceDescription = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
has_showpiece,
|
||||
holographic_showpiece,
|
||||
historian_mode,
|
||||
max_length,
|
||||
showpiece_description,
|
||||
} = data;
|
||||
return (
|
||||
<Section fill align="center">
|
||||
{!has_showpiece && (
|
||||
<Box fill className="Trophycase-description">
|
||||
<b>This exhibit is empty. History awaits your contribution!</b>
|
||||
</Box>
|
||||
)}
|
||||
{!!holographic_showpiece && <b>{showpiece_description}</b>}
|
||||
{!holographic_showpiece && !!has_showpiece && (
|
||||
<Box fill className="Trophycase-description">
|
||||
{showpiece_description
|
||||
? decodeHtmlEntities(showpiece_description)
|
||||
: "This exhibit is under construction. Get the curator's key to finalize your contribution!"}
|
||||
</Box>
|
||||
)}
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const ShowpieceImage = (props, context) => {
|
||||
const { data } = useBackend(context);
|
||||
const { showpiece_icon } = data;
|
||||
return showpiece_icon ? (
|
||||
<Section align="center">
|
||||
<Box
|
||||
as="img"
|
||||
m={1}
|
||||
src={`data:image/jpeg;base64,${showpiece_icon}`}
|
||||
height="96px"
|
||||
width="96px"
|
||||
style={{
|
||||
'-ms-interpolation-mode': 'nearest-neighbor',
|
||||
}}
|
||||
/>
|
||||
</Section>
|
||||
) : (
|
||||
<Section align="center">
|
||||
<Box height="96px" width="96px">
|
||||
<Dimmer fontSize="32px">
|
||||
<Icon name="landmark" />
|
||||
</Dimmer>
|
||||
</Box>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const ShowpieceName = (props, context) => {
|
||||
const { data } = useBackend(context);
|
||||
const { showpiece_name } = data;
|
||||
return (
|
||||
<Section align="center">
|
||||
<b>
|
||||
{showpiece_name
|
||||
? decodeHtmlEntities(showpiece_name)
|
||||
: 'Under construction.'}
|
||||
</b>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
.Trophycase-description {
|
||||
word-break: break-word;
|
||||
word-wrap: break-word;
|
||||
font-style: italic;
|
||||
}
|
||||
@@ -67,6 +67,7 @@
|
||||
@include meta.load-css('./interfaces/Safe.scss');
|
||||
@include meta.load-css('./interfaces/TachyonArray.scss');
|
||||
@include meta.load-css('./interfaces/Techweb.scss');
|
||||
@include meta.load-css('./interfaces/Trophycase.scss');
|
||||
@include meta.load-css('./interfaces/RequestManager.scss');
|
||||
@include meta.load-css('./interfaces/UtilityModulesPane.scss');
|
||||
@include meta.load-css('./interfaces/Fabricator.scss');
|
||||
|
||||
Reference in New Issue
Block a user