mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-19 19:18:50 +01:00
## About The Pull Request This PR kills the abstract internal and external typepaths for organs, now replaced by an EXTERNAL_ORGAN flag to distinguish the two kinds. This PR also fixes fox ears (from #87162, no tail is added) and mushpeople's caps (they should be red, the screenshot is a tad outdated). And yes, you can now use a hair dye spray to recolor body parts like most tails, podpeople hair, mushpeople caps and cat ears. The process can be reversed by using the spray again. ## Why It's Good For The Game Time-Green put some effort during the last few months to untie functions and mechanics from external/internal organ pathing. Now, all that this pathing is good for are a few typechecks, easily replaceable with bitflags. Also podpeople and mushpeople need a way to recolor their "hair". This kind of applies to fish tails from the fish infusion, which colors can't be selected right now. The rest is just there if you ever want to recolor your lizard tail for some reason. Proof of testing btw (screenshot taken before mushpeople cap fix, right side has dyed body parts, moth can't be dyed, they're already fabolous):  ## Changelog 🆑 code: Removed internal/external pathing from organs in favor of a bit flag. Hopefully this shouldn't break anything about organs. fix: Fixed invisible fox ears. fix: Fixed mushpeople caps not being colored red by default. add: You can now dye most tails, podpeople hair, mushpeople caps etc. with a hair dye spray. /🆑
228 lines
7.9 KiB
Plaintext
228 lines
7.9 KiB
Plaintext
ADMIN_VERB_ONLY_CONTEXT_MENU(select_equipment, R_FUN, "Select Equipment", mob/target in world)
|
|
var/datum/select_equipment/ui = new(user, target)
|
|
ui.ui_interact(user.mob)
|
|
|
|
/*
|
|
* This is the datum housing the select equipment UI.
|
|
*
|
|
* You may notice some oddities about the way outfits are passed to the UI and vice versa here.
|
|
* That's because it handles both outfit typepaths (for normal outfits) *and* outfit objects (for custom outfits).
|
|
*
|
|
* Custom outfits need to be objects as they're created in runtime.
|
|
* "Then just handle the normal outfits as objects too and simplify the handling" - you may say.
|
|
* There are about 300 outfit types at the time of writing this. Initializing all of these to objects would be a huge waste.
|
|
*
|
|
*/
|
|
|
|
/datum/select_equipment
|
|
var/client/user
|
|
var/mob/target_mob
|
|
|
|
var/dummy_key
|
|
|
|
//static list to share all the outfit typepaths between all instances of this datum.
|
|
var/static/list/cached_outfits
|
|
|
|
//a typepath if the selected outfit is a normal outfit;
|
|
//an object if the selected outfit is a custom outfit
|
|
var/datum/outfit/selected_outfit = /datum/outfit
|
|
//serializable string for the UI to keep track of which outfit is selected
|
|
var/selected_identifier = "/datum/outfit"
|
|
|
|
/datum/select_equipment/New(_user, mob/target)
|
|
user = CLIENT_FROM_VAR(_user)
|
|
|
|
if(!ishuman(target) && !isobserver(target))
|
|
tgui_alert(usr,"Invalid mob")
|
|
return
|
|
target_mob = target
|
|
|
|
/datum/select_equipment/ui_interact(mob/user, datum/tgui/ui)
|
|
ui = SStgui.try_update_ui(user, src, ui)
|
|
if(!ui)
|
|
ui = new(user, src, "SelectEquipment", "Select Equipment")
|
|
ui.open()
|
|
ui.set_autoupdate(FALSE)
|
|
|
|
/datum/select_equipment/ui_state(mob/user)
|
|
return GLOB.admin_state
|
|
|
|
/datum/select_equipment/ui_status(mob/user, datum/ui_state/state)
|
|
if(QDELETED(target_mob))
|
|
return UI_CLOSE
|
|
return ..()
|
|
|
|
/datum/select_equipment/ui_close(mob/user)
|
|
clear_human_dummy(dummy_key)
|
|
qdel(src)
|
|
|
|
/datum/select_equipment/proc/init_dummy()
|
|
dummy_key = "selectequipmentUI_[target_mob]"
|
|
generate_dummy_lookalike(dummy_key, target_mob)
|
|
unset_busy_human_dummy(dummy_key)
|
|
return
|
|
|
|
/**
|
|
* Packs up data about an outfit as an assoc list to send to the UI as an outfit entry.
|
|
*
|
|
* Args:
|
|
* * category (string) - The tab it will be under
|
|
*
|
|
* * identifier (typepath or ref) - This will sent this back to ui_act to preview or spawn in an outfit.
|
|
* * Must be unique between all entries.
|
|
*
|
|
* * name (string) - Will be the text on the button
|
|
*
|
|
* * priority (bool)(optional) - If True, the UI will sort the entry to the top, right below favorites.
|
|
*
|
|
* * custom_entry (bool)(optional) - Send the identifier with a "ref" keyword instead of "path",
|
|
* * for the UI to tell apart custom outfits from normal ones.
|
|
*
|
|
* Returns (list) An outfit entry
|
|
*/
|
|
|
|
/datum/select_equipment/proc/outfit_entry(category, identifier, name, priority=FALSE, custom_entry=FALSE)
|
|
if(custom_entry)
|
|
return list("category" = category, "ref" = identifier, "name" = name, "priority" = priority)
|
|
return list("category" = category, "path" = identifier, "name" = name, "priority" = priority)
|
|
|
|
/datum/select_equipment/proc/make_outfit_entries(category="General", list/outfit_list)
|
|
var/list/entries = list()
|
|
for(var/path as anything in outfit_list)
|
|
var/datum/outfit/outfit = path
|
|
entries += list(outfit_entry(category, path, initial(outfit.name)))
|
|
return entries
|
|
|
|
//GLOB.custom_outfits lists outfit *objects* so we'll need to do some custom handling for it
|
|
/datum/select_equipment/proc/make_custom_outfit_entries(list/outfit_list)
|
|
var/list/entries = list()
|
|
for(var/datum/outfit/outfit as anything in outfit_list)
|
|
entries += list(outfit_entry("Custom", REF(outfit), outfit.name, custom_entry=TRUE)) //it's either this or special handling on the UI side
|
|
return entries
|
|
|
|
/datum/select_equipment/ui_data(mob/user)
|
|
var/list/data = list()
|
|
if(!dummy_key)
|
|
init_dummy()
|
|
|
|
var/icon/dummysprite = get_flat_human_icon(null,
|
|
dummy_key = dummy_key,
|
|
outfit_override = selected_outfit)
|
|
data["icon64"] = icon2base64(dummysprite)
|
|
data["name"] = target_mob
|
|
|
|
var/datum/preferences/prefs = user?.client?.prefs
|
|
data["favorites"] = list()
|
|
if(prefs)
|
|
data["favorites"] = prefs.favorite_outfits
|
|
|
|
var/list/custom
|
|
custom += make_custom_outfit_entries(GLOB.custom_outfits)
|
|
data["custom_outfits"] = custom
|
|
data["current_outfit"] = selected_identifier
|
|
return data
|
|
|
|
|
|
/datum/select_equipment/ui_static_data(mob/user)
|
|
var/list/data = list()
|
|
if(!cached_outfits)
|
|
cached_outfits = list()
|
|
cached_outfits += list(outfit_entry("General", /datum/outfit, "Naked", priority=TRUE))
|
|
cached_outfits += make_outfit_entries("General", subtypesof(/datum/outfit) - typesof(/datum/outfit/job) - typesof(/datum/outfit/plasmaman))
|
|
cached_outfits += make_outfit_entries("Jobs", typesof(/datum/outfit/job))
|
|
cached_outfits += make_outfit_entries("Plasmamen Outfits", typesof(/datum/outfit/plasmaman))
|
|
|
|
data["outfits"] = cached_outfits
|
|
return data
|
|
|
|
|
|
/datum/select_equipment/proc/resolve_outfit(text)
|
|
|
|
var/path = text2path(text)
|
|
if(ispath(path, /datum/outfit))
|
|
return path
|
|
|
|
else //don't bail yet - could be a custom outfit
|
|
var/datum/outfit/custom_outfit = locate(text)
|
|
if(istype(custom_outfit))
|
|
return custom_outfit
|
|
|
|
|
|
/datum/select_equipment/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
|
|
if(..())
|
|
return
|
|
. = TRUE
|
|
switch(action)
|
|
if("preview")
|
|
var/datum/outfit/new_outfit = resolve_outfit(params["path"])
|
|
|
|
if(ispath(new_outfit)) //got a typepath - that means we're dealing with a normal outfit
|
|
selected_identifier = new_outfit //these are keyed by type
|
|
//by the way, no, they can't be keyed by name because many of them have duplicate names
|
|
|
|
else if(istype(new_outfit)) //got an initialized object - means it's a custom outfit
|
|
selected_identifier = REF(new_outfit) //and the outfit will be keyed by its ref (cause its type will always be /datum/outfit)
|
|
|
|
else //we got nothing and should bail
|
|
return
|
|
|
|
selected_outfit = new_outfit
|
|
|
|
if("applyoutfit")
|
|
var/datum/outfit/new_outfit = resolve_outfit(params["path"])
|
|
if(new_outfit && ispath(new_outfit)) //initialize it
|
|
new_outfit = new new_outfit
|
|
if(!istype(new_outfit))
|
|
return
|
|
user.admin_apply_outfit(target_mob, new_outfit)
|
|
|
|
if("customoutfit")
|
|
return SSadmin_verbs.dynamic_invoke_verb(ui.user, /datum/admin_verb/outfit_manager)
|
|
|
|
if("togglefavorite")
|
|
var/datum/outfit/outfit_path = resolve_outfit(params["path"])
|
|
if(!ispath(outfit_path)) //we do *not* want custom outfits (i.e objects) here, they're not even persistent
|
|
return
|
|
|
|
if(user.prefs.favorite_outfits.Find(outfit_path)) //already there, remove it
|
|
user.prefs.favorite_outfits -= outfit_path
|
|
else //not there, add it
|
|
user.prefs.favorite_outfits += outfit_path
|
|
user.prefs.save_preferences()
|
|
|
|
/client/proc/admin_apply_outfit(mob/target, dresscode)
|
|
if(!ishuman(target) && !isobserver(target))
|
|
tgui_alert(usr,"Invalid mob")
|
|
return
|
|
|
|
if(!dresscode)
|
|
return
|
|
|
|
var/delete_pocket
|
|
var/mob/living/carbon/human/human_target
|
|
if(isobserver(target))
|
|
human_target = target.change_mob_type(/mob/living/carbon/human, delete_old_mob = TRUE)
|
|
else
|
|
human_target = target
|
|
if(human_target.l_store || human_target.r_store || human_target.s_store) //saves a lot of time for admins and coders alike
|
|
if(tgui_alert(usr,"Do you need the items in your pockets?", "Pocket Items", list("Delete Them", "Drop Them")) == "Delete Them")
|
|
delete_pocket = TRUE
|
|
|
|
BLACKBOX_LOG_ADMIN_VERB("Select Equipment")
|
|
var/includes_flags = delete_pocket ? INCLUDE_POCKETS : NONE
|
|
for(var/obj/item/item in human_target.get_equipped_items(includes_flags))
|
|
qdel(item)
|
|
|
|
var/obj/item/organ/brain/human_brain = human_target.get_organ_slot(BRAIN)
|
|
human_brain.destroy_all_skillchips() // get rid of skillchips to prevent runtimes
|
|
|
|
if(dresscode != "Naked")
|
|
human_target.equipOutfit(dresscode)
|
|
|
|
human_target.regenerate_icons()
|
|
|
|
log_admin("[key_name(usr)] changed the equipment of [key_name(human_target)] to [dresscode].")
|
|
message_admins(span_adminnotice("[key_name_admin(usr)] changed the equipment of [ADMIN_LOOKUPFLW(human_target)] to [dresscode]."))
|
|
|
|
return dresscode
|