mirror of
https://github.com/WWhiteDreamProject/wwdpublic.git
synced 2026-04-23 08:37:48 +03:00
# Description Adds **almost two hundred** (192) new items to Loadouts, vastly increasing the fashion content available to players. Most of them are colorable, allowing for an insane amount of customization. Most of the new clothing comes from Aurora Station, with ports from World Server and Nuclear 14 as well. Also added were some unused clothes to loadouts. New loadout items count: **Uniform:** 85 **Outer:** 31 **Head:** 18 **Neck:** 17 **Eyes:** 14 **Shoes:** 14 **Belt:** 7 **Security Eyewear:** 3 **Back:** 2 **Hands:** 1 ## Technical Details I had to touch C# for high heels, because I had to make sure they were hiding the left foot and right foot layers and avoid a bug where the Shitmed body part system enabled the feet layers again after the high heels were equipped by the loadout system. I added two new layers to all species, `innerBelt` and `innerNeck` which are rendered below outer clothing so that the new belts and the neck tie aren't rendered above jackets, coats and such. ## Lookbook ### New Outfits  Outfit information: <details><summary> [Top Left] Summer Days </summary> <b>Head:</b> fedora (colorable) <b>Neck:</b> necklace (colorable) <b>Outer:</b> slim cardigan, opened (colorable) <b>Uniform:</b> long-sleeve A-line dress (colorable) <b>Belt:</b> waist belt (colorable) <b>Back:</b> purse, flipped (colorable) <b>Shoes:</b> flip flops, alternative (colorable) Note how the waist belt is covered by the slim cardigan, due to being in the `innerBelt` layer. </details> <details><summary> [Top Middle] Formalwear </summary> <b>Head:</b> small hair bow (colorable) <b>Neck:</b> necklace (colorable) <b>Uniform:</b> asymmetric dress (colorable) <b>Hands:</b> evening gloves (colorable) <b>Belt:</b> thin waist belt (colorable) <b>Back:</b> purse (colorable) <b>Shoes:</b> high heels (colorable) </details> <details><summary> [Top Right] Boho-Chic </summary> <b>Head:</b> hair flower (colorable) <b>Neck:</b> small necklace (colorable) <b>Outer:</b> asymmetric coat, opened (colorable) <b>Uniform:</b> midi dress (colorable) <b>Belt:</b> waist belt (colorable) <b>Back:</b> purse (colorable) <b>Shoes:</b> flats (colorable) </details> <details><summary> [Bottom Left] Nurse </summary> <b>Head:</b> nurse hat <b>Neck:</b> round necklace (colorable) <b>Uniform:</b> club dress (colorable) <b>Hands:</b> evening gloves (colorable) <b>Back:</b> purse (colorable) <b>Shoes:</b> flats (colorable) </details> <details><summary> [Bottom Middle] Smart Casual </summary> <b>Outer:</b> modern coat (colorable) <b>Uniform:</b> pencil skirt and gym bra (colorable) <b>Back:</b> purse (colorable) <b>Shoes:</b> high heels (colorable) </details> <details><summary> [Bottom Right] Slaycurity Officer </summary> <b>Head:</b> security beret <b>Neck:</b> gold necklace <b>Uniform:</b> evening gown (colorable) <b>Hands:</b> evening glovs (colorable) <b>Belt:</b> security belt <b>Back:</b> purse, flipped (colorable) <b>Shoes:</b> high heels (colorable) </details> ### High Heels Footsteps https://github.com/user-attachments/assets/c4b99407-0f9b-4fb5-910e-603c024d9598 The high heels footsteps are also present on the high-heeled boots, long high heels and flats. Pets are now colorable too (look at the pink mothroach in the video). Reasons why they'd be a specific color could be a natural variation, being dyed by their owner, or genetic modifications (however unethical the last two may be). <b>Cowboy</b>  <b>Head:</b> slouch hat (colorable) <b>Eyes:</b> hipster glasses (colorable) <b>Neck:</b> suit tie (colorable) <b>Outer:</b> vest (colorable) <b>Uniform:</b> grey cowboy outfit (from Nuclear 14!) <b>Hands:</b> gloves (colorable) <b>Belt:</b> belt (colorable) <b>Back:</b> leather satchel <b>Shoes:</b> workboots (colorable ### Security Glasses Skins <img width=240px src="https://github.com/user-attachments/assets/b7c5dd03-ab00-4cff-9018-145271e48229"> Security now have three alternative skins for the security glasses to personalize their looks. Thus, they should no longer have the problem where every sec member's face looks samey because everyone had the same security glasses.  From left to right: base security glasses, security aviators, big security glasses, security visor glasses <details><summary> More Images </summary> <b>Skubsuit</b>  <b>Head:</b> artist beret (colorable) <b>Eyes:</b> monocle (colorable) <b>Neck:</b> suit tie (colorable) <b>Outer:</b> blazer (colorable) <b>Uniform:</b> amish suit (colorable) <b>Belt:</b> belt (colorable) <b>Shoes:</b> ankle boots (colorable) Huh, skub. <b>Rugged</b>  <b>Head:</b> green beanie (from Nuclear 14!) <b>Eyes:</b> eyepatch (colorable) <b>Neck:</b> small necklace (colorable) <b>Outer:</b> sweater cardigan, opened (colorable) <b>Uniform:</b> black flannel shirt and jeans (from Nuclear 14) <b>Back:</b> grey satchel (colorable) <b>Shoes:</b> workboots (colorable) <b>The Rig Was Gamed From The Start</b>  <b>Eyes:</b> eyepatch, flipped (colorable) <b>Neck:</b> short tie (colorable) <b>Uniform:</b> checkered suit (from Nuclear 14!) <b>Shoes:</b> fashion jackboots (colorable) <b>New Dresses</b> <img width=300px src="https://github.com/user-attachments/assets/5289465a-b297-4bb2-b9be-27695ee36d2b"> <img width=300px src="https://github.com/user-attachments/assets/e39e0acb-69ee-447b-ba78-5ccb9eac2497"> <img width=300px src="https://github.com/user-attachments/assets/01c39422-3571-43ea-987d-e13e21dd6078"> <img width=300px src="https://github.com/user-attachments/assets/02dc10f2-56da-440f-9b19-aa38e9cb9942"> About 48 new dresses/jumpskirts were added to loadouts. </details> # Changelog <!-- You can add an author after the `🆑` to change the name that appears in the changelog (ex: `🆑 Death`) Leaving it blank will default to your GitHub display name This includes all available types for the changelog --> 🆑 Skubman - add: The Fashion Update is here. Almost two hundred brand-new clothes have been added to Loadouts, like colorable suits, dresses, coats, blazers, boots, necklaces, hats, glasses (including new Security glasses), belts, a purse and more! - add: Pets in the loadouts are now colorable. Go wild with pink mothroaches, blue mice, green cockroaches and more! (cherry picked from commit 6ba53e2ab9d648c05a943713af3d597347def3da)
309 lines
12 KiB
C#
309 lines
12 KiB
C#
using Content.Shared.Body.Part;
|
|
using Content.Shared.Body.Systems;
|
|
using Content.Shared.Clothing.Components;
|
|
using Content.Shared.Hands.Components;
|
|
using Content.Shared.Hands.EntitySystems;
|
|
using Content.Shared.Humanoid;
|
|
using Content.Shared.Interaction.Events;
|
|
using Content.Shared.Inventory;
|
|
using Content.Shared.Inventory.Events;
|
|
using Content.Shared.Item;
|
|
using Robust.Shared.Containers;
|
|
using Robust.Shared.GameStates;
|
|
|
|
namespace Content.Shared.Clothing.EntitySystems;
|
|
|
|
public abstract class ClothingSystem : EntitySystem
|
|
{
|
|
[Dependency] private readonly SharedItemSystem _itemSys = default!;
|
|
[Dependency] private readonly SharedContainerSystem _containerSys = default!;
|
|
[Dependency] private readonly SharedHumanoidAppearanceSystem _humanoidSystem = default!;
|
|
[Dependency] private readonly InventorySystem _invSystem = default!;
|
|
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
|
|
|
|
public override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
|
|
SubscribeLocalEvent<ClothingComponent, UseInHandEvent>(OnUseInHand);
|
|
SubscribeLocalEvent<ClothingComponent, ComponentGetState>(OnGetState);
|
|
SubscribeLocalEvent<ClothingComponent, ComponentHandleState>(OnHandleState);
|
|
SubscribeLocalEvent<ClothingComponent, GotEquippedEvent>(OnGotEquipped);
|
|
SubscribeLocalEvent<ClothingComponent, GotUnequippedEvent>(OnGotUnequipped);
|
|
SubscribeLocalEvent<ClothingComponent, ItemMaskToggledEvent>(OnMaskToggled);
|
|
SubscribeLocalEvent<ClothingComponent, GettingPickedUpAttemptEvent>(OnPickedUp);
|
|
SubscribeLocalEvent<HumanoidAppearanceComponent, BodyPartAddedEvent>(OnPartAttachedToBody, after: [typeof(SharedBodySystem)]);
|
|
|
|
SubscribeLocalEvent<ClothingComponent, ClothingEquipDoAfterEvent>(OnEquipDoAfter);
|
|
SubscribeLocalEvent<ClothingComponent, ClothingUnequipDoAfterEvent>(OnUnequipDoAfter);
|
|
}
|
|
|
|
private void OnUseInHand(Entity<ClothingComponent> ent, ref UseInHandEvent args)
|
|
{
|
|
if (args.Handled || !ent.Comp.QuickEquip)
|
|
return;
|
|
|
|
var user = args.User;
|
|
if (!TryComp(user, out InventoryComponent? inv) ||
|
|
!TryComp(user, out HandsComponent? hands))
|
|
return;
|
|
|
|
QuickEquip(ent, (user, inv, hands));
|
|
args.Handled = true;
|
|
args.ApplyDelay = false;
|
|
}
|
|
|
|
private void QuickEquip(
|
|
Entity<ClothingComponent> toEquipEnt,
|
|
Entity<InventoryComponent, HandsComponent> userEnt)
|
|
{
|
|
foreach (var slotDef in userEnt.Comp1.Slots)
|
|
{
|
|
// Do not attempt to quick-equip clothing in pocket slots.
|
|
// We should probably add a special flag to SlotDefinition to skip quick equip if more similar slots get added.
|
|
if (slotDef.SlotFlags.HasFlag(SlotFlags.POCKET))
|
|
continue;
|
|
|
|
if (!_invSystem.CanEquip(userEnt, toEquipEnt, slotDef.Name, out _, slotDef, userEnt, toEquipEnt))
|
|
continue;
|
|
|
|
if (_invSystem.TryGetSlotEntity(userEnt, slotDef.Name, out var slotEntity, userEnt))
|
|
{
|
|
// Item in slot has to be quick equipable as well
|
|
if (TryComp(slotEntity, out ClothingComponent? item) && !item.QuickEquip)
|
|
continue;
|
|
|
|
if (!_invSystem.TryUnequip(userEnt, slotDef.Name, true, inventory: userEnt, checkDoafter: true))
|
|
continue;
|
|
|
|
if (!_invSystem.TryEquip(userEnt, toEquipEnt, slotDef.Name, true, inventory: userEnt, clothing: toEquipEnt, checkDoafter: true))
|
|
continue;
|
|
|
|
_handsSystem.PickupOrDrop(userEnt, slotEntity.Value, handsComp: userEnt);
|
|
}
|
|
else
|
|
{
|
|
if (!_invSystem.TryEquip(userEnt, toEquipEnt, slotDef.Name, true, inventory: userEnt, clothing: toEquipEnt, checkDoafter: true))
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void ToggleVisualLayers(EntityUid equipee, HashSet<HumanoidVisualLayers> layers, HashSet<HumanoidVisualLayers> appearanceLayers, bool force = false)
|
|
{
|
|
foreach (HumanoidVisualLayers layer in layers)
|
|
{
|
|
if (!force && !appearanceLayers.Contains(layer))
|
|
break;
|
|
|
|
InventorySystem.InventorySlotEnumerator enumerator = _invSystem.GetSlotEnumerator(equipee);
|
|
|
|
bool shouldLayerShow = true;
|
|
while (enumerator.NextItem(out EntityUid item))
|
|
{
|
|
if (TryComp(item, out HideLayerClothingComponent? comp))
|
|
{
|
|
if (comp.Slots.Contains(layer))
|
|
{
|
|
//Checks for mask toggling. TODO: Make a generic system for this
|
|
if (comp.HideOnToggle && TryComp(item, out MaskComponent? mask) && TryComp(item, out ClothingComponent? clothing))
|
|
{
|
|
if (clothing.EquippedPrefix != mask.EquippedPrefix)
|
|
{
|
|
shouldLayerShow = false;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
shouldLayerShow = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_humanoidSystem.SetLayerVisibility(equipee, layer, shouldLayerShow);
|
|
}
|
|
}
|
|
|
|
protected virtual void OnGotEquipped(EntityUid uid, ClothingComponent component, GotEquippedEvent args)
|
|
{
|
|
component.InSlot = args.Slot;
|
|
CheckEquipmentForLayerHide(args.Equipment, args.Equipee);
|
|
|
|
if ((component.Slots & args.SlotFlags) != SlotFlags.NONE)
|
|
{
|
|
var gotEquippedEvent = new ClothingGotEquippedEvent(args.Equipee, component);
|
|
RaiseLocalEvent(uid, ref gotEquippedEvent);
|
|
|
|
var didEquippedEvent = new ClothingDidEquippedEvent((uid, component));
|
|
RaiseLocalEvent(args.Equipee, ref didEquippedEvent);
|
|
}
|
|
}
|
|
|
|
protected virtual void OnGotUnequipped(EntityUid uid, ClothingComponent component, GotUnequippedEvent args)
|
|
{
|
|
if ((component.Slots & args.SlotFlags) != SlotFlags.NONE)
|
|
{
|
|
var gotUnequippedEvent = new ClothingGotUnequippedEvent(args.Equipee, component);
|
|
RaiseLocalEvent(uid, ref gotUnequippedEvent);
|
|
|
|
var didUnequippedEvent = new ClothingDidUnequippedEvent((uid, component));
|
|
RaiseLocalEvent(args.Equipee, ref didUnequippedEvent);
|
|
}
|
|
|
|
component.InSlot = null;
|
|
CheckEquipmentForLayerHide(args.Equipment, args.Equipee);
|
|
}
|
|
|
|
private void OnGetState(EntityUid uid, ClothingComponent component, ref ComponentGetState args)
|
|
{
|
|
args.State = new ClothingComponentState(component.EquippedPrefix);
|
|
}
|
|
|
|
private void OnHandleState(EntityUid uid, ClothingComponent component, ref ComponentHandleState args)
|
|
{
|
|
if (args.Current is ClothingComponentState state)
|
|
{
|
|
SetEquippedPrefix(uid, state.EquippedPrefix, component);
|
|
if (component.InSlot != null && _containerSys.TryGetContainingContainer((uid, null, null), out var container))
|
|
{
|
|
CheckEquipmentForLayerHide(uid, container.Owner);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnMaskToggled(Entity<ClothingComponent> ent, ref ItemMaskToggledEvent args)
|
|
{
|
|
//TODO: sprites for 'pulled down' state. defaults to invisible due to no sprite with this prefix
|
|
SetEquippedPrefix(ent, args.IsToggled ? args.equippedPrefix : null, ent);
|
|
CheckEquipmentForLayerHide(ent.Owner, args.Wearer);
|
|
}
|
|
|
|
private void OnPickedUp(Entity<ClothingComponent> ent, ref GettingPickedUpAttemptEvent args)
|
|
{
|
|
// If this clothing is equipped by the performer of this action, and the clothing has an unequip delay, stop the attempt
|
|
if (ent.Comp.UnequipDelay <= TimeSpan.Zero
|
|
|| !_invSystem.TryGetContainingSlot(ent.Owner, out var slot)
|
|
|| !_containerSys.TryGetContainingContainer(ent, out var container)
|
|
|| container.Owner != args.User)
|
|
return;
|
|
|
|
args.Cancel();
|
|
}
|
|
|
|
// Yes, this is exclusive C# just so that high heels selected from loadouts still hide the feet layers
|
|
// after Shitmed (SharedBodySystem.PartAppearance) initializes the feet parts setting their layer visibility to true.
|
|
private void OnPartAttachedToBody(Entity<HumanoidAppearanceComponent> ent, ref BodyPartAddedEvent args)
|
|
{
|
|
var enumerator = _invSystem.GetSlotEnumerator(ent.Owner);
|
|
while (enumerator.NextItem(out var item))
|
|
{
|
|
if (!TryComp<HideLayerClothingComponent>(item, out var comp))
|
|
continue;
|
|
|
|
CheckEquipmentForLayerHide(item, ent.Owner);
|
|
}
|
|
}
|
|
|
|
private void OnEquipDoAfter(Entity<ClothingComponent> ent, ref ClothingEquipDoAfterEvent args)
|
|
{
|
|
if (args.Handled || args.Cancelled || args.Target is not { } target)
|
|
return;
|
|
args.Handled = _invSystem.TryEquip(args.User, target, ent, args.Slot, clothing: ent.Comp, predicted: true, checkDoafter: false);
|
|
}
|
|
|
|
private void OnUnequipDoAfter(Entity<ClothingComponent> ent, ref ClothingUnequipDoAfterEvent args)
|
|
{
|
|
if (args.Handled || args.Cancelled || args.Target is not { } target)
|
|
return;
|
|
args.Handled = _invSystem.TryUnequip(args.User, target, args.Slot, clothing: ent.Comp, predicted: true, checkDoafter: false);
|
|
if (args.Handled)
|
|
_handsSystem.TryPickup(args.User, ent);
|
|
}
|
|
|
|
private void CheckEquipmentForLayerHide(EntityUid equipment, EntityUid equipee)
|
|
{
|
|
if (TryComp(equipment, out HideLayerClothingComponent? clothesComp) && TryComp(equipee, out HumanoidAppearanceComponent? appearanceComp))
|
|
ToggleVisualLayers(equipee, clothesComp.Slots, appearanceComp.HideLayersOnEquip, clothesComp.Force);
|
|
}
|
|
|
|
#region Public API
|
|
|
|
public void SetEquippedPrefix(EntityUid uid, string? prefix, ClothingComponent? clothing = null)
|
|
{
|
|
if (!Resolve(uid, ref clothing, false))
|
|
return;
|
|
|
|
if (clothing.EquippedPrefix == prefix)
|
|
return;
|
|
|
|
clothing.EquippedPrefix = prefix;
|
|
_itemSys.VisualsChanged(uid);
|
|
Dirty(uid, clothing);
|
|
}
|
|
|
|
public void SetSlots(EntityUid uid, SlotFlags slots, ClothingComponent? clothing = null)
|
|
{
|
|
if (!Resolve(uid, ref clothing))
|
|
return;
|
|
|
|
clothing.Slots = slots;
|
|
Dirty(uid, clothing);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copy all clothing specific visuals from another item.
|
|
/// </summary>
|
|
public void CopyVisuals(EntityUid uid, ClothingComponent otherClothing, ClothingComponent? clothing = null)
|
|
{
|
|
if (!Resolve(uid, ref clothing))
|
|
return;
|
|
|
|
clothing.ClothingVisuals = otherClothing.ClothingVisuals;
|
|
clothing.EquippedPrefix = otherClothing.EquippedPrefix;
|
|
clothing.Sprite = otherClothing.Sprite;
|
|
clothing.FemaleMask = otherClothing.FemaleMask;
|
|
|
|
_itemSys.VisualsChanged(uid);
|
|
Dirty(uid, clothing);
|
|
}
|
|
|
|
public void SetLayerColor(ClothingComponent clothing, string slot, string mapKey, Color? color)
|
|
{
|
|
if (clothing.ClothingVisuals == null)
|
|
return;
|
|
|
|
foreach (var layer in clothing.ClothingVisuals[slot])
|
|
{
|
|
if (layer.MapKeys == null)
|
|
return;
|
|
|
|
if (!layer.MapKeys.Contains(mapKey))
|
|
continue;
|
|
|
|
layer.Color = color;
|
|
}
|
|
}
|
|
public void SetLayerState(ClothingComponent clothing, string slot, string mapKey, string state)
|
|
{
|
|
if (clothing.ClothingVisuals == null)
|
|
return;
|
|
|
|
foreach (var layer in clothing.ClothingVisuals[slot])
|
|
{
|
|
if (layer.MapKeys == null)
|
|
return;
|
|
|
|
if (!layer.MapKeys.Contains(mapKey))
|
|
continue;
|
|
|
|
layer.State = state;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|