if (layerData.State is not null && inventory.SpeciesId is not null && layerData.State.EndsWith(inventory.SpeciesId))
continue;
- if (_displacement.TryAddDisplacement(displacementData, sprite, index, key, revealedLayers))
+ if (_displacement.TryAddDisplacement(displacementData, sprite, index, key, out var displacementKey))
+ {
+ revealedLayers.Add(displacementKey);
index++;
+ }
}
}
{
[Dependency] private readonly ISerializationManager _serialization = default!;
- public bool TryAddDisplacement(DisplacementData data, SpriteComponent sprite, int index, string key, HashSet<string> revealedLayers)
+ /// <summary>
+ /// Attempting to apply a displacement map to a specific layer of SpriteComponent
+ /// </summary>
+ /// <param name="data">Information package for applying the displacement map</param>
+ /// <param name="sprite">SpriteComponent</param>
+ /// <param name="index">Index of the layer where the new map layer will be added</param>
+ /// <param name="key">Unique layer key, which will determine which layer to apply displacement map to</param>
+ /// <param name="displacementKey">The key of the new displacement map layer added by this function.</param>
+ /// <returns></returns>
+ public bool TryAddDisplacement(DisplacementData data,
+ SpriteComponent sprite,
+ int index,
+ object key,
+ out string displacementKey)
{
+ displacementKey = $"{key}-displacement";
+
+ if (key.ToString() is null)
+ return false;
+
if (data.ShaderOverride != null)
sprite.LayerSetShader(index, data.ShaderOverride);
- var displacementKey = $"{key}-displacement";
- if (!revealedLayers.Add(displacementKey))
- {
- Log.Warning($"Duplicate key for DISPLACEMENT: {displacementKey}.");
- return false;
- }
+ if (sprite.LayerMapTryGet(displacementKey, out var oldIndex))
+ sprite.RemoveLayer(oldIndex);
//allows you not to write it every time in the YML
foreach (var pair in data.SizeMaps)
{
- pair.Value.CopyToShaderParameters??= new()
+ pair.Value.CopyToShaderParameters ??= new()
{
LayerKey = "dummy",
ParameterTexture = "displacementMap",
if (actualRSI is not null)
{
if (actualRSI.Size.X != actualRSI.Size.Y)
- Log.Warning($"DISPLACEMENT: {displacementKey} has a resolution that is not 1:1, things can look crooked");
+ {
+ Log.Warning(
+ $"DISPLACEMENT: {displacementKey} has a resolution that is not 1:1, things can look crooked");
+ }
var layerSize = actualRSI.Size.X;
- if (data.SizeMaps.ContainsKey(layerSize))
- displacementDataLayer = data.SizeMaps[layerSize];
+ if (data.SizeMaps.TryGetValue(layerSize, out var map))
+ displacementDataLayer = map;
}
var displacementLayer = _serialization.CreateCopy(displacementDataLayer, notNullableOverride: true);
- displacementLayer.CopyToShaderParameters!.LayerKey = key;
+ displacementLayer.CopyToShaderParameters!.LayerKey = key.ToString() ?? "this is impossible";
sprite.AddLayer(displacementLayer, index);
sprite.LayerMapSet(displacementKey, index);
- revealedLayers.Add(displacementKey);
-
return true;
}
}
sprite.LayerSetData(index, layerData);
- //Add displacement maps
- if (hand.Location == HandLocation.Left && handComp.LeftHandDisplacement is not null)
- _displacement.TryAddDisplacement(handComp.LeftHandDisplacement, sprite, index, key, revealedLayers);
- else if (hand.Location == HandLocation.Right && handComp.RightHandDisplacement is not null)
- _displacement.TryAddDisplacement(handComp.RightHandDisplacement, sprite, index, key, revealedLayers);
- //Fallback to default displacement map
- else if (handComp.HandDisplacement is not null)
- _displacement.TryAddDisplacement(handComp.HandDisplacement, sprite, index, key, revealedLayers);
+ // Add displacement maps
+ var displacement = hand.Location switch
+ {
+ HandLocation.Left => handComp.LeftHandDisplacement,
+ HandLocation.Right => handComp.RightHandDisplacement,
+ _ => handComp.HandDisplacement
+ };
+
+ if (displacement is not null && _displacement.TryAddDisplacement(displacement, sprite, index, key, out var displacementKey))
+ revealedLayers.Add(displacementKey);
}
RaiseLocalEvent(held, new HeldVisualsUpdatedEvent(uid, revealedLayers), true);
+using Content.Client.DisplacementMap;
using Content.Shared.CCVar;
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Markings;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly MarkingManager _markingManager = default!;
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
+ [Dependency] private readonly DisplacementMapSystem _displacement = default!;
public override void Initialize()
{
{
sprite.LayerSetColor(layerId, Color.White);
}
+
+ if (humanoid.MarkingsDisplacement.TryGetValue(markingPrototype.BodyPart, out var displacementData) && markingPrototype.CanBeDisplaced)
+ {
+ _displacement.TryAddDisplacement(displacementData, sprite, targetLayer + j + 1, layerId, out _);
+ }
}
}
+using Content.Shared.DisplacementMap;
using Content.Shared.Humanoid.Markings;
using Content.Shared.Humanoid.Prototypes;
using Content.Shared.Inventory;
[DataField]
public ProtoId<MarkingPrototype>? UndergarmentBottom = new ProtoId<MarkingPrototype>("UndergarmentBottomBoxers");
+
+ /// <summary>
+ /// The displacement maps that will be applied to specific layers of the humanoid.
+ /// </summary>
+ [DataField]
+ public Dictionary<HumanoidVisualLayers, DisplacementData> MarkingsDisplacement = new();
}
[DataDefinition]
string species)
{
var speciesProto = _prototypeManager.Index<SpeciesPrototype>(species);
- var onlyWhitelisted = _prototypeManager.Index(speciesProto.MarkingPoints).OnlyWhitelisted;
+ var markingPoints = _prototypeManager.Index(speciesProto.MarkingPoints);
var res = new Dictionary<string, MarkingPrototype>();
foreach (var (key, marking) in MarkingsByCategory(category))
{
- if (onlyWhitelisted && marking.SpeciesRestrictions == null)
+ if ((markingPoints.OnlyWhitelisted || markingPoints.Points[category].OnlyWhitelisted) && marking.SpeciesRestrictions == null)
{
continue;
}
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
namespace Content.Shared.Humanoid.Markings;
[Serializable, NetSerializable]
public sealed partial class MarkingPoints
{
- [DataField("points", required: true)]
+ [DataField(required: true)]
public int Points = 0;
- [DataField("required", required: true)]
- public bool Required = false;
+
+ [DataField(required: true)]
+ public bool Required;
+
+ /// <summary>
+ /// If the user of this marking point set is only allowed to
+ /// use whitelisted markings, and not globally usable markings.
+ /// Only used for validation and profile construction. Ignored anywhere else.
+ /// </summary>
+ [DataField]
+ public bool OnlyWhitelisted;
+
// Default markings for this layer.
- [DataField("defaultMarkings", customTypeSerializer:typeof(PrototypeIdListSerializer<MarkingPrototype>))]
- public List<string> DefaultMarkings = new();
+ [DataField]
+ public List<ProtoId<MarkingPrototype>> DefaultMarkings = new();
public static Dictionary<MarkingCategories, MarkingPoints> CloneMarkingPointDictionary(Dictionary<MarkingCategories, MarkingPoints> self)
{
{
Points = points.Points,
Required = points.Required,
+ OnlyWhitelisted = points.OnlyWhitelisted,
DefaultMarkings = points.DefaultMarkings
};
}
/// use whitelisted markings, and not globally usable markings.
/// Only used for validation and profile construction. Ignored anywhere else.
/// </summary>
- [DataField("onlyWhitelisted")] public bool OnlyWhitelisted;
+ [DataField]
+ public bool OnlyWhitelisted;
- [DataField("points", required: true)]
+ [DataField(required: true)]
public Dictionary<MarkingCategories, MarkingPoints> Points { get; private set; } = default!;
}
[DataField("coloring")]
public MarkingColors Coloring { get; private set; } = new();
+ /// <summary>
+ /// Do we need to apply any displacement maps to this marking? Set to false if your marking is incompatible
+ /// with a standard human doll, and is used for some special races with unusual shapes
+ /// </summary>
+ [DataField]
+ public bool CanBeDisplaced { get; private set; } = true;
+
[DataField("sprites", required: true)]
public List<SpriteSpecifier> Sprites { get; private set; } = default!;
id: VoxFacialHairBeard
bodyPart: FacialHair
markingCategory: FacialHair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_facial_hair.rsi
id: VoxFacialHairColonel
bodyPart: FacialHair
markingCategory: FacialHair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_facial_hair.rsi
id: VoxFacialHairFu
bodyPart: FacialHair
markingCategory: FacialHair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_facial_hair.rsi
id: VoxFacialHairMane
bodyPart: FacialHair
markingCategory: FacialHair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_facial_hair.rsi
id: VoxFacialHairManeSmall
bodyPart: FacialHair
markingCategory: FacialHair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_facial_hair.rsi
id: VoxFacialHairNeck
bodyPart: FacialHair
markingCategory: FacialHair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_facial_hair.rsi
id: VoxFacialHairTufts
bodyPart: FacialHair
markingCategory: FacialHair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_facial_hair.rsi
id: VoxHairAfro
bodyPart: Hair
markingCategory: Hair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_hair.rsi
id: VoxHairBraids
bodyPart: Hair
markingCategory: Hair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_hair.rsi
id: VoxHairCrestedQuills
bodyPart: Hair
markingCategory: Hair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_hair.rsi
id: VoxHairEmperorQuills
bodyPart: Hair
markingCategory: Hair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_hair.rsi
id: VoxHairFlowing
bodyPart: Hair
markingCategory: Hair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_hair.rsi
id: VoxHairHawk
bodyPart: Hair
markingCategory: Hair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_hair.rsi
id: VoxHairHorns
bodyPart: Hair
markingCategory: Hair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_hair.rsi
id: VoxHairKeelQuills
bodyPart: Hair
markingCategory: Hair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_hair.rsi
id: VoxHairKeetQuills
bodyPart: Hair
markingCategory: Hair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_hair.rsi
id: VoxHairKingly
bodyPart: Hair
markingCategory: Hair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_hair.rsi
id: VoxHairLongBraid
bodyPart: Hair
markingCategory: Hair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_hair.rsi
id: VoxHairMange
bodyPart: Hair
markingCategory: Hair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_hair.rsi
id: VoxHairMohawk
bodyPart: Hair
markingCategory: Hair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_hair.rsi
id: VoxHairNights
bodyPart: Hair
markingCategory: Hair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_hair.rsi
id: VoxHairPony
bodyPart: Hair
markingCategory: Hair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_hair.rsi
id: VoxHairRazorClipped
bodyPart: Hair
markingCategory: Hair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_hair.rsi
id: VoxHairRazor
bodyPart: Hair
markingCategory: Hair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_hair.rsi
id: VoxHairSortBraid
bodyPart: Hair
markingCategory: Hair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_hair.rsi
id: VoxHairShortQuills
bodyPart: Hair
markingCategory: Hair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_hair.rsi
id: VoxHairSpotty
bodyPart: Hair
markingCategory: Hair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_hair.rsi
id: VoxHairSurf
bodyPart: Hair
markingCategory: Hair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_hair.rsi
id: VoxHairTielQuills
bodyPart: Hair
markingCategory: Hair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_hair.rsi
id: VoxHairWiseBraid
bodyPart: Hair
markingCategory: Hair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_hair.rsi
id: VoxHairYasu
bodyPart: Hair
markingCategory: Hair
+ canBeDisplaced: false
speciesRestriction: [Vox]
sprites:
- sprite: Mobs/Customization/vox_hair.rsi
- type: Body
prototype: Vox
requiredLegs: 2
- - type: HumanoidAppearance
- species: Vox
- undergarmentTop: UndergarmentTopTanktopVox
- undergarmentBottom: UndergarmentBottomBoxersVox
#- type: VoxAccent # Not yet coded
- type: Speech
speechVerb: Vox
sprite: "Effects/creampie.rsi"
state: "creampie_vox" # Not default
visible: false
+ - type: HumanoidAppearance
+ species: Vox
+ undergarmentTop: UndergarmentTopTanktopVox
+ undergarmentBottom: UndergarmentBottomBoxersVox
+ markingsDisplacement:
+ Hair:
+ sizeMaps:
+ 32:
+ sprite: Mobs/Species/Vox/displacement.rsi
+ state: hair
- type: Inventory
speciesId: vox
displacements:
species: Vox
undergarmentTop: UndergarmentTopTanktopVox
undergarmentBottom: UndergarmentBottomBoxersVox
+ markingsDisplacement:
+ Hair:
+ sizeMaps:
+ 32:
+ sprite: Mobs/Species/Vox/displacement.rsi
+ state: hair
- type: Body
prototype: Vox
- type: Inventory
- type: markingPoints
id: MobVoxMarkingLimits
- onlyWhitelisted: true
points:
Hair:
points: 1
FacialHair:
points: 1
required: false
+ onlyWhitelisted: true
Head:
points: 1
required: true
+ onlyWhitelisted: true
Snout:
points: 1
required: true
defaultMarkings: [ VoxBeak ]
+ onlyWhitelisted: true
Arms:
points: 4
required: true
defaultMarkings: [ VoxLArmScales, VoxRArmScales, VoxRHandScales, VoxLHandScales ]
+ onlyWhitelisted: true
Legs:
points: 4
required: true
defaultMarkings: [ VoxLLegScales, VoxRLegScales, VoxRFootScales, VoxLFootScales ]
+ onlyWhitelisted: true
UndergarmentTop:
points: 1
required: false
+ onlyWhitelisted: true
UndergarmentBottom:
points: 1
required: false
+ onlyWhitelisted: true
Chest:
points: 1
required: false
+ onlyWhitelisted: true
Tail:
points: 1
required: true
defaultMarkings: [ VoxTail ]
+ onlyWhitelisted: true
- type: humanoidBaseSprite
id: MobVoxEyes
{
"version": 1,
"license": "CC-BY-SA-3.0",
- "copyright": "jumpsuit state made by PJB3005. back, hand, head, and eyes states made by Flareguy, ears, hand_l, hand_r and shoes made by TheShuEd",
+ "copyright": "jumpsuit state made by PJB3005. back, hand, head, and eyes states made by Flareguy, ears, hand_l, hand_r, hair and shoes made by TheShuEd",
"size": {
"x": 32,
"y": 32
"name": "shoes",
"directions": 4
},
+ {
+ "name": "hair",
+ "directions": 4
+ },
{
"name": "hand_l",
"directions": 4