mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-18 13:43:27 +00:00
* Items in your hands can catch fire (#83867) ## About The Pull Request Recently we allowed items held in your hands to catch fire if you catch fire. This makes sense but the code had a few oversights, then we reverted it. This PR reintroduces the feature, but with a few refinements. The basic feature is simple: If you are on fire then items you are holding will also catch fire, in the same vein as items you are wearing on your head or hands. There are also a few caveats we forgot about the first time we added this: - If your gloves cannot catch fire, your held items will not catch fire (because your hands aren't on fire). - If you are extinguished, your held items will also be extinguished. - Stopping, Dropping, and Rolling on top of any items will also extinguish those items. As part of this change, after an argument about whether or not this is an oversight in coding-general, I've made the proc `get_equipped_items` take a bitflag instead of a series of booleans as an argument and added a new one for "include held items", so that we need no longer argue about whether holding something counts as "equipping" it (in all other parts of the game than this proc, it does). This is what gives the PR most of its code footprint, don't be scared. ## Why It's Good For The Game Items you are holding in your hands _should_ catch fire if everything else on your person is on fire, and taking an item off of your body to put it in your hands shouldn't protect it from fire, because those things don't make intuitive sense. If we want an item to be able to catch fire when worn, then it should do so. This might expose some issues where we were improperly setting the flammability flags on items, but any weapon which will burn in your hands now would also have burned if you were wearing it on your belt or back, so making those issues more visible should be a bonus (we'll also stop them from burning on your back or belt). If you see someone holding a piece of paper that you really don't want them to read you can now set them on fire to stop them from reading it, whereas previously they would deftly hold the very flammable object out of reach of their flaming body. ## Changelog 🆑 balance: Items held in your hands can catch fire. balance: Items you are holding won't catch fire if your hands cannot catch fire. balance: When you stop being on fire so will items you are holding. balance: If you roll around on your burning items they will stop being on fire. /🆑 * Items in your hands can catch fire --------- Co-authored-by: Jacquerel <hnevard@gmail.com>
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/internal/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
|