Files
Bubberstation/code/modules/loadout/loadout_helpers.dm
Rimi Nosha 6d8f4d1805 [SEMI-MODULAR] Multiple Loadout Presets! (#2540)
## About The Pull Request

Tin. Also made some QoL tweaks while I was at it!

Comprehensively:
- Removes the defunct job checkbox and replaces it with a dropdown.
- Adds a preference update message, cause it's otherwise silent.
- Stops the TGUI prefs migration message from showing when changing to a
brand new character.
- Adds the ability to create (nameable) loadout presets.
- The default preset can't be renamed or deleted. This was to make my
life easier, or I'd spend another day on this, which I'm not prepared to
do.
- Automatically COPIES your old loadout to the new system's default
loadout. The old preference value is ENTIRELY UNCHANGED, which should
mean this is entirely safe. **Back up the server playersave data,
regardless, though.**
- Fixes a funny bug where the blacklisted and whitelisted roles text was
the same.

## There's a default limit of 12 entries (including default.) I'm happy
to bump this up at maintainer request.

## Why It's Good For The Game

This is a thing folk have always wanted since the dawn of loadouts, and
the new loadout system is *surprisingly* hackable, so I did it!

Now you can set up multiple outfits for whatever occasion you want!

## Proof Of Testing
<details>
<summary>Screenshots/Videos</summary>


![image](https://github.com/user-attachments/assets/6cddb219-0c8e-44f1-aefe-d3f36a261555)



https://github.com/user-attachments/assets/9d4bd6f3-0dc7-4aaa-a6ad-989fc9cb987a

The total allowed loadout count, added after the video, so here's a
screencap instead!

![image](https://github.com/user-attachments/assets/0ffd72a4-ed4c-4b6d-8d77-83c0545e1171)

Renaming


https://github.com/user-attachments/assets/236d99c2-1bcf-4836-b32c-4bb4dd6227e0

</details>

## Changelog
🆑
add: Loadout presets! Make multiple loadouts, be able to switch between
them in a couple of clicks!
qol: Replaced the defunct job checkbox on the loadout page with a
working dropdown.
/🆑
2024-12-07 20:53:57 +01:00

144 lines
5.3 KiB
Plaintext

/**
* Equips this mob with a given outfit and loadout items as per the passed preferences.
*
* Loadout items override the pre-existing item in the corresponding slot of the job outfit.
* Some job items are preserved after being overridden - belt items, ear items, and glasses.
* The rest of the slots, the items are overridden completely and deleted.
*
* Species with special outfits are snowflaked to have loadout items placed in their bags instead of overriding the outfit.
*
* * outfit - the job outfit we're equipping
* * preference_source - the preferences to draw loadout items from.
* * visuals_only - whether we call special equipped procs, or if we just look like we equipped it
*/
/mob/living/carbon/human/proc/equip_outfit_and_loadout(
datum/outfit/outfit = /datum/outfit,
datum/preferences/preference_source,
visuals_only = FALSE,
datum/job/equipping,
) // SKYRAT EDIT CHANGE - Added equipping param
if(isnull(preference_source))
return equipOutfit(outfit, visuals_only)
var/datum/outfit/equipped_outfit
if(ispath(outfit, /datum/outfit))
equipped_outfit = new outfit()
else if(istype(outfit, /datum/outfit))
equipped_outfit = outfit
else
CRASH("Invalid outfit passed to equip_outfit_and_loadout ([outfit])")
var/list/preference_list = preference_source.read_preference(/datum/preference/loadout)
preference_list = preference_list[preference_source.read_preference(/datum/preference/loadout_index)] // BUBBER EDIT ADDITION: Multiple loadout presets
var/list/loadout_datums = loadout_list_to_datums(preference_list)
// SKYRAT EDIT ADDITION BEGIN
var/obj/item/storage/briefcase/empty/travel_suitcase
var/loadout_placement_preference = preference_source.read_preference(/datum/preference/choiced/loadout_override_preference)
// Slap our things into the outfit given
for(var/datum/loadout_item/item as anything in loadout_datums)
if(item.restricted_roles && equipping && !(equipping.title in item.restricted_roles))
if(preference_source.parent)
to_chat(preference_source.parent, span_warning("You were unable to get a loadout item([initial(item.item_path.name)]) due to job restrictions!"))
continue
if(item.blacklisted_roles && equipping && (equipping.title in item.blacklisted_roles))
if(preference_source.parent)
to_chat(preference_source.parent, span_warning("You were unable to get a loadout item([initial(item.item_path.name)]) due to job blacklists!"))
continue
if(item.restricted_species && !(dna.species.id in item.restricted_species))
if(preference_source.parent)
to_chat(preference_source.parent, span_warning("You were unable to get a loadout item ([initial(item.item_path.name)]) due to species restrictions!"))
continue
if(item.ckeywhitelist && !(preference_source?.parent?.ckey in item.ckeywhitelist)) // Sanity checking
if(preference_source.parent)
to_chat(preference_source.parent, span_warning("You were unable to get a loadout item ([initial(item.item_path.name)]) due to CKEY restrictions!"))
continue
if(loadout_placement_preference == LOADOUT_OVERRIDE_CASE && !visuals_only)
if(!travel_suitcase)
travel_suitcase = new(loc)
new item.item_path(travel_suitcase)
else // SKYRAT EDIT END
item.insert_path_into_outfit(equipped_outfit, src, visuals_only, loadout_placement_preference)
// Equip the outfit loadout items included
if(!equipped_outfit.equip(src, visuals_only))
return FALSE
// SKYRAT EDIT ADDITION
if(travel_suitcase)
put_in_hands(travel_suitcase)
// SKYRAT EDIT END
// Handle any snowflake on_equips.
var/list/new_contents = get_all_gear()
var/update = NONE
for(var/datum/loadout_item/item as anything in loadout_datums)
var/obj/item/equipped = locate(item.item_path) in new_contents
if(isnull(equipped))
continue
update |= item.on_equip_item(
equipped_item = equipped,
preference_source = preference_source,
preference_list = preference_list,
equipper = src,
visuals_only = visuals_only,
)
if(update)
update_clothing(update)
return TRUE
/**
* Takes a list of paths (such as a loadout list)
* and returns a list of their singleton loadout item datums
*
* loadout_list - the list being checked
*
* Returns a list of singleton datums
*/
/proc/loadout_list_to_datums(list/loadout_list) as /list
var/list/datums = list()
if(!length(GLOB.all_loadout_datums))
CRASH("No loadout datums in the global loadout list!")
for(var/path in loadout_list)
var/actual_datum = GLOB.all_loadout_datums[path]
if(!istype(actual_datum, /datum/loadout_item))
stack_trace("Could not find ([path]) loadout item in the global list of loadout datums!")
continue
datums += actual_datum
return datums
// SKYRAT EDIT ADDITION
/*
* Removes all invalid paths from loadout lists.
*
* passed_list - the loadout list we're sanitizing.
*
* returns a list
*/
/proc/sanitize_loadout_list(list/passed_list)
RETURN_TYPE(/list)
var/list/list_to_clean = LAZYLISTDUPLICATE(passed_list)
for(var/path in list_to_clean)
if(!ispath(path))
stack_trace("invalid path found in loadout list! (Path: [path])")
LAZYREMOVE(list_to_clean, path)
else if(!(path in GLOB.all_loadout_datums))
stack_trace("invalid loadout slot found in loadout list! Path: [path]")
LAZYREMOVE(list_to_clean, path)
return list_to_clean
/obj/item/storage/briefcase/empty/PopulateContents()
return
// SKYRAT EDIT END