PORT: Allows Markings to Set Custom Layers for Specific Sprites (#2516)
* multilayer markings * vestigial reptilian tail sprites added * i fix :) --------- Co-authored-by: mq <113324899+mqole@users.noreply.github.com>
This commit is contained in:
@@ -146,7 +146,7 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
|
|||||||
|
|
||||||
var customBaseLayers = new Dictionary<HumanoidVisualLayers, CustomBaseLayerInfo>();
|
var customBaseLayers = new Dictionary<HumanoidVisualLayers, CustomBaseLayerInfo>();
|
||||||
|
|
||||||
var speciesPrototype = _prototypeManager.Index<SpeciesPrototype>(profile.Species);
|
var speciesPrototype = _prototypeManager.Index(profile.Species); // Floof
|
||||||
var markings = new MarkingSet(speciesPrototype.MarkingPoints, _markingManager, _prototypeManager);
|
var markings = new MarkingSet(speciesPrototype.MarkingPoints, _markingManager, _prototypeManager);
|
||||||
|
|
||||||
// Add markings that doesn't need coloring. We store them until we add all other markings that doesn't need it.
|
// Add markings that doesn't need coloring. We store them until we add all other markings that doesn't need it.
|
||||||
@@ -343,32 +343,116 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
|
|||||||
bool visible,
|
bool visible,
|
||||||
Entity<HumanoidAppearanceComponent, SpriteComponent> entity)
|
Entity<HumanoidAppearanceComponent, SpriteComponent> entity)
|
||||||
{
|
{
|
||||||
|
// hi! mq here.
|
||||||
|
// if youre dealing with upstream merge conflicts, and you see a bunch of stuff up here, chances are its been moved in between some other shit.
|
||||||
|
// i love the documentation on this file but i can only look at layer maps for so long before i feel myself becoming ben affleck.
|
||||||
|
// sorry for any confusion.
|
||||||
|
|
||||||
|
// FLOOF ADD START
|
||||||
|
// make a handy dict of filename -> colors
|
||||||
|
// cus we might need to access it by filename to link
|
||||||
|
// one sprite's colors to another
|
||||||
|
var colorDict = new Dictionary<string, Color>();
|
||||||
|
for (var i = 0; i < markingPrototype.Sprites.Count; i++)
|
||||||
|
{
|
||||||
|
var spriteName = markingPrototype.Sprites[i] switch
|
||||||
|
{
|
||||||
|
SpriteSpecifier.Rsi rsi => rsi.RsiState,
|
||||||
|
SpriteSpecifier.Texture texture => texture.TexturePath.Filename,
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
|
||||||
|
if (spriteName != null)
|
||||||
|
{
|
||||||
|
if (colors != null && i < colors.Count)
|
||||||
|
colorDict.Add(spriteName, colors[i]);
|
||||||
|
else
|
||||||
|
colorDict.Add(spriteName, Color.White);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// now, rearrange them, copying any parented colors to children set to
|
||||||
|
// inherit them
|
||||||
|
if (markingPrototype.ColorLinks != null)
|
||||||
|
{
|
||||||
|
foreach (var (child, parent) in markingPrototype.ColorLinks)
|
||||||
|
{
|
||||||
|
if (colorDict.TryGetValue(parent, out var color))
|
||||||
|
{
|
||||||
|
colorDict[child] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// and, since we can't rely on the iterator knowing where the heck to put
|
||||||
|
// each sprite when we have one marking setting multiple layers,
|
||||||
|
// lets just kinda sorta do that ourselves
|
||||||
|
var layerDict = new Dictionary<string, int>();
|
||||||
|
// FLOOF ADD END
|
||||||
|
for (var j = 0; j < markingPrototype.Sprites.Count; j++)
|
||||||
|
{
|
||||||
|
var markingSprite = markingPrototype.Sprites[j];
|
||||||
|
if (markingSprite is not SpriteSpecifier.Rsi rsi)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FLOOF CHANGE START
|
||||||
|
var layerSlot = markingPrototype.BodyPart;
|
||||||
|
// first, try to see if there are any custom layers for this marking
|
||||||
|
if (markingPrototype.Layering != null)
|
||||||
|
{
|
||||||
|
var name = rsi.RsiState;
|
||||||
|
if (markingPrototype.Layering.TryGetValue(name, out var layerName))
|
||||||
|
{
|
||||||
|
layerSlot = Enum.Parse<HumanoidVisualLayers>(layerName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// update the layerDict
|
||||||
|
// if it doesnt have this, add it at 0, otherwise increment it
|
||||||
|
if (layerDict.TryGetValue(layerSlot.ToString(), out var layerIndex))
|
||||||
|
{
|
||||||
|
layerDict[layerSlot.ToString()] = layerIndex + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
layerDict.Add(layerSlot.ToString(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// THIS IS THE UPSTREAM STUFF!!! RIGHT HERE!!!
|
||||||
var humanoid = entity.Comp1;
|
var humanoid = entity.Comp1;
|
||||||
var sprite = entity.Comp2;
|
var sprite = entity.Comp2;
|
||||||
|
|
||||||
if (!_sprite.LayerMapTryGet((entity.Owner, sprite), markingPrototype.BodyPart, out var targetLayer, false))
|
if (!_sprite.LayerMapTryGet((entity.Owner, sprite), layerSlot, out var targetLayer, false))
|
||||||
return;
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
visible &= !IsHidden(humanoid, markingPrototype.BodyPart);
|
visible &= !IsHidden(humanoid, markingPrototype.BodyPart);
|
||||||
visible &= humanoid.BaseLayers.TryGetValue(markingPrototype.BodyPart, out var setting)
|
visible &= humanoid.BaseLayers.TryGetValue(markingPrototype.BodyPart, out var setting)
|
||||||
&& setting.AllowsMarkings;
|
&& setting.AllowsMarkings;
|
||||||
|
// FLOOF CHANGE END
|
||||||
for (var j = 0; j < markingPrototype.Sprites.Count; j++)
|
|
||||||
{
|
|
||||||
var markingSprite = markingPrototype.Sprites[j];
|
|
||||||
|
|
||||||
if (markingSprite is not SpriteSpecifier.Rsi rsi)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var layerId = $"{markingPrototype.ID}-{rsi.RsiState}";
|
var layerId = $"{markingPrototype.ID}-{rsi.RsiState}";
|
||||||
|
|
||||||
if (!_sprite.LayerMapTryGet((entity.Owner, sprite), layerId, out _, false))
|
if (!_sprite.LayerMapTryGet((entity.Owner, sprite), layerId, out layerIndex, false)) // imp layerindex
|
||||||
{
|
{
|
||||||
var layer = _sprite.AddLayer((entity.Owner, sprite), markingSprite, targetLayer + j + 1);
|
// for layers that are supposed to be behind everything,
|
||||||
|
// adding 1 to the layer index makes it not be behind
|
||||||
|
// everything. fun! FLOOF ADD =3
|
||||||
|
// var targLayerAdj = targetLayer == 0 ? 0 + j : targetLayer + j + 1;
|
||||||
|
var targLayerAdj = targetLayer + layerDict[layerSlot.ToString()] + 1;
|
||||||
|
var layer = _sprite.AddLayer((entity.Owner, sprite), markingSprite, targLayerAdj);
|
||||||
_sprite.LayerMapSet((entity.Owner, sprite), layerId, layer);
|
_sprite.LayerMapSet((entity.Owner, sprite), layerId, layer);
|
||||||
_sprite.LayerSetSprite((entity.Owner, sprite), layerId, rsi);
|
_sprite.LayerSetSprite((entity.Owner, sprite), layerId, rsi);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// imp special via beck. check if there's a shader defined in the markingPrototype's shader datafield, and if there is...
|
||||||
|
if (markingPrototype.Shader != null)
|
||||||
|
{
|
||||||
|
// use spriteComponent's layersetshader function to set the layer's shader to that which is specified.
|
||||||
|
sprite.LayerSetShader(layerId, markingPrototype.Shader);
|
||||||
|
}
|
||||||
|
// end imp special
|
||||||
|
|
||||||
_sprite.LayerSetVisible((entity.Owner, sprite), layerId, visible);
|
_sprite.LayerSetVisible((entity.Owner, sprite), layerId, visible);
|
||||||
|
|
||||||
if (!visible || setting == null) // this is kinda implied
|
if (!visible || setting == null) // this is kinda implied
|
||||||
@@ -377,10 +461,18 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
|
|||||||
// Okay so if the marking prototype is modified but we load old marking data this may no longer be valid
|
// Okay so if the marking prototype is modified but we load old marking data this may no longer be valid
|
||||||
// and we need to check the index is correct.
|
// and we need to check the index is correct.
|
||||||
// So if that happens just default to white?
|
// So if that happens just default to white?
|
||||||
if (colors != null && j < colors.Count)
|
// FLOOF ADD =3
|
||||||
_sprite.LayerSetColor((entity.Owner, sprite), layerId, colors[j]);
|
_sprite.LayerSetColor((entity.Owner, sprite), layerId, colorDict.TryGetValue(rsi.RsiState, out var color) ? color : Color.White);
|
||||||
else
|
|
||||||
_sprite.LayerSetColor((entity.Owner, sprite), layerId, Color.White);
|
// FLOOF REMOVE
|
||||||
|
// if (colors != null && j < colors.Count)
|
||||||
|
// {
|
||||||
|
// _sprite.LayerSetColor((entity.Owner, sprite), layerId, colors[j]);
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// _sprite.LayerSetColor((entity.Owner, sprite), layerId, Color.White);
|
||||||
|
// }
|
||||||
|
|
||||||
if (humanoid.MarkingsDisplacement.TryGetValue(markingPrototype.BodyPart, out var displacementData) && markingPrototype.CanBeDisplaced)
|
if (humanoid.MarkingsDisplacement.TryGetValue(markingPrototype.BodyPart, out var displacementData) && markingPrototype.CanBeDisplaced)
|
||||||
_displacement.TryAddDisplacement(displacementData, (entity.Owner, sprite), targetLayer + j + 1, layerId, out _);
|
_displacement.TryAddDisplacement(displacementData, (entity.Owner, sprite), targetLayer + j + 1, layerId, out _);
|
||||||
|
|||||||
@@ -408,11 +408,36 @@ public sealed partial class MarkingPicker : Control
|
|||||||
List<ColorSelectorSliders> colorSliders = new();
|
List<ColorSelectorSliders> colorSliders = new();
|
||||||
for (int i = 0; i < prototype.Sprites.Count; i++)
|
for (int i = 0; i < prototype.Sprites.Count; i++)
|
||||||
{
|
{
|
||||||
|
// first, check if the coloration is parented to another marking
|
||||||
|
// and if so, just kinda sorta dont display it
|
||||||
|
var skipdraw = false;
|
||||||
|
if (prototype.ColorLinks?.Count > 0)
|
||||||
|
{
|
||||||
|
var name = prototype.Sprites[i] switch
|
||||||
|
{
|
||||||
|
SpriteSpecifier.Rsi rsi => rsi.RsiState,
|
||||||
|
SpriteSpecifier.Texture texture => texture.TexturePath.Filename,
|
||||||
|
_ => null
|
||||||
|
};
|
||||||
|
|
||||||
|
if (name != null && prototype.ColorLinks.ContainsKey(name))
|
||||||
|
{
|
||||||
|
// dont show it, cus its parented to another marking
|
||||||
|
skipdraw = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
var colorContainer = new BoxContainer
|
var colorContainer = new BoxContainer
|
||||||
{
|
{
|
||||||
Orientation = LayoutOrientation.Vertical,
|
Orientation = LayoutOrientation.Vertical,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// so.
|
||||||
|
// the color selector sliders decide which destination color to modify
|
||||||
|
// based on its index in the list of color selectors.
|
||||||
|
// this is a problem if we, say, want to *not* show a certain slider
|
||||||
|
// cus then it'll modify the wrong color, unless the color happened to
|
||||||
|
// be in index 0.
|
||||||
|
if(!skipdraw)
|
||||||
CMarkingColors.AddChild(colorContainer);
|
CMarkingColors.AddChild(colorContainer);
|
||||||
|
|
||||||
ColorSelectorSliders colorSelector = new ColorSelectorSliders();
|
ColorSelectorSliders colorSelector = new ColorSelectorSliders();
|
||||||
|
|||||||
@@ -28,7 +28,11 @@ public sealed class PrototypeSaveTest
|
|||||||
{
|
{
|
||||||
// The only prototypes that should get ignored are those that REQUIRE setup to get a sprite. At that point it is
|
// The only prototypes that should get ignored are those that REQUIRE setup to get a sprite. At that point it is
|
||||||
// the responsibility of the spawner to ensure that a valid sprite is set.
|
// the responsibility of the spawner to ensure that a valid sprite is set.
|
||||||
"VirtualItem"
|
"VirtualItem",
|
||||||
|
"MobGoblin", // imp edits - these four prototypes are species and do not receive their sprite data from ItemComponent, causing a false positive
|
||||||
|
"MobGoblinStowaway",
|
||||||
|
"MobGoblinKnight",
|
||||||
|
"MobGoblinCastaway",
|
||||||
};
|
};
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
|||||||
@@ -3,6 +3,10 @@ using Robust.Shared.Serialization;
|
|||||||
|
|
||||||
namespace Content.Shared.Humanoid
|
namespace Content.Shared.Humanoid
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// These are the layer defines for the humanoid sprite system.
|
||||||
|
/// If you want to add a new layer slot to species? Scroll down~
|
||||||
|
/// </summary>
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public enum HumanoidVisualLayers : byte
|
public enum HumanoidVisualLayers : byte
|
||||||
{
|
{
|
||||||
@@ -18,6 +22,9 @@ namespace Content.Shared.Humanoid
|
|||||||
SnoutCover, // things layered over snouts (i.e. noses)
|
SnoutCover, // things layered over snouts (i.e. noses)
|
||||||
HeadSide, // side parts (i.e., frills)
|
HeadSide, // side parts (i.e., frills)
|
||||||
HeadTop, // top parts (i.e., ears)
|
HeadTop, // top parts (i.e., ears)
|
||||||
|
TailBehind, // FLOOF - add tails that dont have to go through a brutal cookiecutter to work
|
||||||
|
TailBehindBackpack, // imp - to layer behind backpacks, treat this like an oversuit
|
||||||
|
TailOversuit, // FLOOF - add tails that dont have to go through a brutal cookiecutter to work
|
||||||
Eyes,
|
Eyes,
|
||||||
RArm,
|
RArm,
|
||||||
LArm,
|
LArm,
|
||||||
@@ -34,3 +41,39 @@ namespace Content.Shared.Humanoid
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* HOW 2 ADD A NEW LAYER FOR MOB NON-EQUIPMENT APPEARANCE STUFF
|
||||||
|
* by Dank Elly
|
||||||
|
* Lets say you want to add something like socks to be selectable through markings
|
||||||
|
* Since people tend to have two feet, you'll actually need two new layers! one for each foot.
|
||||||
|
* First, add in the enum above a new layer slot, like so:
|
||||||
|
* LSock,
|
||||||
|
* RSock,
|
||||||
|
* Easy huh? Note that this enum does *not* define which layer in relation to other layers it will be layered!
|
||||||
|
* Next, since these'll be a whole new marking type, we'll need to add in a new marking category.
|
||||||
|
* in Content.Shared/Humanoid/Markings/MarkingCategories.cs, add in a new category for the socks
|
||||||
|
* lets assume we'll let the player pick two different socks, which means two different categories
|
||||||
|
* Its done the same way as this enum here
|
||||||
|
* Next, go to Content.Shared/Humanoid/HumanoidVisualLayersExtension.cs
|
||||||
|
* In here, find Sublayers() and add in the new layers to the switch statement
|
||||||
|
* case HumanoidVisualLayers.LFoot:
|
||||||
|
* yield return HumanoidVisualLayers.LSock;
|
||||||
|
* and so on. This will make it so if your leg/foot stops being visible, the sock will vanish as well! i think
|
||||||
|
* Next, we'll have to go through a lot of species files, to change a few things:
|
||||||
|
* speciesBaseSprites
|
||||||
|
* I'm not sure what this does, but you'll want to add in an entry under sprites for the new layer
|
||||||
|
* LSock: MobHumanoidAnyMarking <- I think this makes it selectable in the editor, and blank by default?
|
||||||
|
* RSock: MobHumanoidAnyMarking
|
||||||
|
* layers
|
||||||
|
* This is part of the species entity prototype, and this is where the sprite layering is defined.
|
||||||
|
* Note that this includes equipment layers, which means you can totally, say, have a tail sit between your suit and your backpack!
|
||||||
|
* - map: [ "enum.HumanoidVisualLayers.LFoot" ]
|
||||||
|
* - map: [ "enum.HumanoidVisualLayers.LSock" ]
|
||||||
|
* - map: [ "enum.HumanoidVisualLayers.RFoot" ]
|
||||||
|
* - map: [ "enum.HumanoidVisualLayers.RSock" ]
|
||||||
|
* This will make it so the socks are drawn on top of the feet, and not behind them.
|
||||||
|
* I'd list which files these are in, but there are a million different species files, all with the same name, in different folders
|
||||||
|
* So just search for "speciesBaseSprites" and "enum.HumanoidVisualLayers.Chest" and you'll find them all
|
||||||
|
* If you miss some, dont sweat it, someone'll point it out and then you can fix it! Hotties like you make the hottest hotfixes~
|
||||||
|
*/
|
||||||
|
|||||||
@@ -67,6 +67,9 @@ namespace Content.Shared.Humanoid
|
|||||||
case HumanoidVisualLayers.Chest:
|
case HumanoidVisualLayers.Chest:
|
||||||
yield return HumanoidVisualLayers.Chest;
|
yield return HumanoidVisualLayers.Chest;
|
||||||
yield return HumanoidVisualLayers.Tail;
|
yield return HumanoidVisualLayers.Tail;
|
||||||
|
yield return HumanoidVisualLayers.TailBehind;
|
||||||
|
yield return HumanoidVisualLayers.TailBehindBackpack;
|
||||||
|
yield return HumanoidVisualLayers.TailOversuit;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
yield break;
|
yield break;
|
||||||
|
|||||||
@@ -26,12 +26,12 @@ public static class MarkingColoring
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns list of colors for marking layers
|
/// Returns list of colors for marking layers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static List<Color> GetMarkingLayerColors
|
public static List<Color> GetMarkingLayerColors(
|
||||||
(
|
|
||||||
MarkingPrototype prototype,
|
MarkingPrototype prototype,
|
||||||
Color? skinColor,
|
Color? skinColor,
|
||||||
Color? eyeColor,
|
Color? eyeColor,
|
||||||
MarkingSet markingSet
|
MarkingSet markingSet,
|
||||||
|
List<string>? ignores = null // Floof
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
var colors = new List<Color>();
|
var colors = new List<Color>();
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using Robust.Shared.Utility;
|
|||||||
|
|
||||||
namespace Content.Shared.Humanoid.Markings
|
namespace Content.Shared.Humanoid.Markings
|
||||||
{
|
{
|
||||||
[Prototype]
|
[Prototype("marking")] // Floof
|
||||||
public sealed partial class MarkingPrototype : IPrototype
|
public sealed partial class MarkingPrototype : IPrototype
|
||||||
{
|
{
|
||||||
[IdDataField]
|
[IdDataField]
|
||||||
@@ -42,9 +42,111 @@ namespace Content.Shared.Humanoid.Markings
|
|||||||
[DataField("sprites", required: true)]
|
[DataField("sprites", required: true)]
|
||||||
public List<SpriteSpecifier> Sprites { get; private set; } = default!;
|
public List<SpriteSpecifier> Sprites { get; private set; } = default!;
|
||||||
|
|
||||||
|
[DataField("shader")]
|
||||||
|
public string? Shader { get; private set; } = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allows specific images to be put into any arbitrary layer on the mob.
|
||||||
|
/// Whole point of this is to have things like tails be able to be
|
||||||
|
/// behind the mob when facing south-east-west, but in front of the mob
|
||||||
|
/// when facing north. This requires two+ sprites, each in a different
|
||||||
|
/// layer.
|
||||||
|
/// Is a dictionary: sprite name -> layer name,
|
||||||
|
/// e.g. "tail-cute-vulp" -> "tail-back", "tail-cute-vulp-oversuit" -> "tail-oversuit"
|
||||||
|
/// also, FLOOF ADD =3
|
||||||
|
/// </summary>
|
||||||
|
[DataField("layering")]
|
||||||
|
public Dictionary<string, string>? Layering { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allows you to link a specific sprite's coloring to another sprite's coloring.
|
||||||
|
/// This is useful for things like tails, which while they have two sets of sprites,
|
||||||
|
/// the two sets of sprites should be treated as one sprite for the purposes of
|
||||||
|
/// coloring. Just more intuitive that way~
|
||||||
|
/// Format: spritename getting colored -> spritename which colors it
|
||||||
|
/// so if we have a Tail Behind with 'cooltail' as the sprite name, and a Tail Oversuit
|
||||||
|
/// with 'cooltail-oversuit' as the sprite name, and we want to have the Tail Behind
|
||||||
|
/// inherit the color of the Tail Oversuit, we would do:
|
||||||
|
/// cooltail -> cooltail-oversuit
|
||||||
|
/// cooltail will be hidden from the color picker, and just use whatevers set for
|
||||||
|
/// cooltail-oversuit. Easy huh?
|
||||||
|
/// also, FLOOF ADD =3
|
||||||
|
/// </summary>
|
||||||
|
[DataField("colorLinks")]
|
||||||
|
public Dictionary<string, string>? ColorLinks { get; private set; }
|
||||||
|
|
||||||
public Marking AsMarking()
|
public Marking AsMarking()
|
||||||
{
|
{
|
||||||
return new Marking(ID, Sprites.Count);
|
return new Marking(ID, Sprites.Count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
* HOW 2 MAKE MARKINGS WITH MULTIPLE LAYERS
|
||||||
|
* by Dank Elly
|
||||||
|
*
|
||||||
|
* So, since the dawn of SS14, markings have been a single layer.
|
||||||
|
* This is fine, since most markings are simple in terms of how they appear on a mob.
|
||||||
|
* Just a cool skin color, or a pattern, or something.
|
||||||
|
*
|
||||||
|
* Then, tails were added.
|
||||||
|
* Tails are different, if you think about it!
|
||||||
|
* They tend to hang from the body, and appear behind the mob from some angles, and
|
||||||
|
* in front of the mob from others. No real way around it, otherwise it'll look wierd!
|
||||||
|
*
|
||||||
|
* But, markings are still a single layer, which means that all the images sit in the
|
||||||
|
* stacked deck of cards that is our sprite layers. If a marking is set to be in the
|
||||||
|
* Underwear layer, that marking will only ever be in the Underwear layer, no matter
|
||||||
|
* which way you turn! Fine and all for most markings, but not for tails.
|
||||||
|
*
|
||||||
|
* The previous solution was to just cookie-cut out a human shape from some directions
|
||||||
|
* of the tail sprites, then layer then on top of the mob, over clothes and such.
|
||||||
|
* This worked! Sort of! It wouldnt support missing legs, it only worked if you were
|
||||||
|
* wearing skin-tight jumpsuits, and it made it so that you can't have any fancy leg
|
||||||
|
* shapes (digilegs my beloved~)
|
||||||
|
*
|
||||||
|
* However, SS13 had solved this years ago, which I am totally taking credit for cus
|
||||||
|
* I made roughly this system for Goonstation! The solution here was to just use
|
||||||
|
* two layers lmao! A layer for the tail from most angles, and a layer for the tail
|
||||||
|
* when youre facing away. This is known as the Behind and Oversuit layers, cus one
|
||||||
|
* goes behind the mob, the other goes over your suit (and ideally under the backpack).
|
||||||
|
*
|
||||||
|
* LOREDUMP OVER HOW TO MAKE IT DO THIS
|
||||||
|
*
|
||||||
|
* First, make your marking prototype as normal.
|
||||||
|
* Note, you'll need extra sprites for every layer you want to add.
|
||||||
|
* This includes things like colorable accessories, which will need to be
|
||||||
|
* added to the new layers as well.
|
||||||
|
* Next, add in a 'layering' entry to the marking prototype.
|
||||||
|
* Then, add in a new entry to the 'layering' dictionary, like so:
|
||||||
|
* layering:
|
||||||
|
* name_of_image: LayerToPutItIn
|
||||||
|
* name_of_another_image: LayerToPutItIn
|
||||||
|
* I'll have a complete example later, but this is the basic idea.
|
||||||
|
* The first part of the entry is what you put for the state of that sprite
|
||||||
|
* Its how the game knows which sprite to mess with!
|
||||||
|
* The second part is the layer you want to put it in.
|
||||||
|
* This points to an entry in the enum stored in this file:
|
||||||
|
* Content.Shared/Humanoid/HumanoidVisualLayers.cs
|
||||||
|
* Capitalization matters!
|
||||||
|
* todo: a way to link the colorations between layers
|
||||||
|
*
|
||||||
|
* Heres an example from Resources/Prototypes/Floof/Entities/Mobs/Customization/Markings/debug.yml
|
||||||
|
*
|
||||||
|
- type: marking
|
||||||
|
id: TailDebugPro
|
||||||
|
bodyPart: Tail
|
||||||
|
markingCategory: Tail
|
||||||
|
speciesRestriction: [Reptilian, SlimePerson, IPC, Rodentia, Vulpkanin, Felinid, Human, Oni]
|
||||||
|
layering:
|
||||||
|
tail_oversuit: TailOversuit <--------------\
|
||||||
|
tail_behind: TailBehind <----------------+--------\
|
||||||
|
sprites: | |
|
||||||
|
- sprite: _Floof/Mobs/Customization/debug.rsi | |
|
||||||
|
state: tail_oversuit >-------------------/ |
|
||||||
|
- sprite: _Floof/Mobs/Customization/debug.rsi |
|
||||||
|
state: tail_behind >------------------------------/
|
||||||
|
*
|
||||||
|
* (dont include the arrows lol)
|
||||||
|
*/
|
||||||
|
|||||||
@@ -131,7 +131,16 @@
|
|||||||
bodyPart: Tail
|
bodyPart: Tail
|
||||||
markingCategory: Tail
|
markingCategory: Tail
|
||||||
speciesRestriction: [Reptilian]
|
speciesRestriction: [Reptilian]
|
||||||
|
# imp edit below
|
||||||
|
layering:
|
||||||
|
tail_smooth_front: TailBehindBackpack
|
||||||
|
tail_smooth_primary: TailBehind
|
||||||
|
tail_smooth_secondary: TailBehind
|
||||||
|
colorLinks:
|
||||||
|
tail_smooth_primary: tail_smooth_front
|
||||||
sprites:
|
sprites:
|
||||||
|
- sprite: Mobs/Customization/reptilian_parts.rsi
|
||||||
|
state: tail_smooth_front
|
||||||
- sprite: Mobs/Customization/reptilian_parts.rsi
|
- sprite: Mobs/Customization/reptilian_parts.rsi
|
||||||
state: tail_smooth_primary
|
state: tail_smooth_primary
|
||||||
- sprite: Mobs/Customization/reptilian_parts.rsi
|
- sprite: Mobs/Customization/reptilian_parts.rsi
|
||||||
@@ -142,36 +151,68 @@
|
|||||||
bodyPart: Tail
|
bodyPart: Tail
|
||||||
markingCategory: Tail
|
markingCategory: Tail
|
||||||
speciesRestriction: [Reptilian]
|
speciesRestriction: [Reptilian]
|
||||||
|
# imp edit below
|
||||||
|
layering:
|
||||||
|
tail_large_front: TailBehindBackpack
|
||||||
|
tail_large_behind: TailBehind
|
||||||
|
colorLinks:
|
||||||
|
tail_large_behind: tail_large_front
|
||||||
sprites:
|
sprites:
|
||||||
- sprite: Mobs/Customization/reptilian_parts.rsi
|
- sprite: Mobs/Customization/reptilian_parts.rsi
|
||||||
state: tail_large
|
state: tail_large_front
|
||||||
|
- sprite: Mobs/Customization/reptilian_parts.rsi
|
||||||
|
state: tail_large_behind
|
||||||
|
|
||||||
- type: marking
|
- type: marking
|
||||||
id: LizardTailSpikes
|
id: LizardTailSpikes
|
||||||
bodyPart: Tail
|
bodyPart: Tail
|
||||||
markingCategory: Tail
|
markingCategory: Tail
|
||||||
speciesRestriction: [Reptilian]
|
speciesRestriction: [Reptilian]
|
||||||
|
# imp edit below
|
||||||
|
layering:
|
||||||
|
tail_spikes_front: TailBehindBackpack
|
||||||
|
tail_spikes_behind: TailBehind
|
||||||
|
colorLinks:
|
||||||
|
tail_spikes_behind: tail_spikes_front
|
||||||
sprites:
|
sprites:
|
||||||
- sprite: Mobs/Customization/reptilian_parts.rsi
|
- sprite: Mobs/Customization/reptilian_parts.rsi
|
||||||
state: tail_spikes
|
state: tail_spikes_behind
|
||||||
|
- sprite: Mobs/Customization/reptilian_parts.rsi
|
||||||
|
state: tail_spikes_front
|
||||||
|
|
||||||
- type: marking
|
- type: marking
|
||||||
id: LizardTailLTiger
|
id: LizardTailLTiger
|
||||||
bodyPart: Tail
|
bodyPart: Tail
|
||||||
markingCategory: Tail
|
markingCategory: Tail
|
||||||
speciesRestriction: [Reptilian]
|
speciesRestriction: [Reptilian]
|
||||||
|
# imp edit below
|
||||||
|
layering:
|
||||||
|
tail_ltiger_front: TailBehindBackpack
|
||||||
|
tail_ltiger_behind: TailBehind
|
||||||
|
colorLinks:
|
||||||
|
tail_ltiger_behind: tail_ltiger_front
|
||||||
sprites:
|
sprites:
|
||||||
- sprite: Mobs/Customization/reptilian_parts.rsi
|
- sprite: Mobs/Customization/reptilian_parts.rsi
|
||||||
state: tail_ltiger
|
state: tail_ltiger_front
|
||||||
|
- sprite: Mobs/Customization/reptilian_parts.rsi
|
||||||
|
state: tail_ltiger_behind
|
||||||
|
|
||||||
- type: marking
|
- type: marking
|
||||||
id: LizardTailDTiger
|
id: LizardTailDTiger
|
||||||
bodyPart: Tail
|
bodyPart: Tail
|
||||||
markingCategory: Tail
|
markingCategory: Tail
|
||||||
speciesRestriction: [Reptilian]
|
speciesRestriction: [Reptilian]
|
||||||
|
# imp edit below
|
||||||
|
layering:
|
||||||
|
tail_dtiger_front: TailBehindBackpack
|
||||||
|
tail_dtiger_behind: TailBehind
|
||||||
|
colorLinks:
|
||||||
|
tail_dtiger_behind: tail_dtiger_front
|
||||||
sprites:
|
sprites:
|
||||||
- sprite: Mobs/Customization/reptilian_parts.rsi
|
- sprite: Mobs/Customization/reptilian_parts.rsi
|
||||||
state: tail_dtiger
|
state: tail_dtiger_front
|
||||||
|
- sprite: Mobs/Customization/reptilian_parts.rsi
|
||||||
|
state: tail_dtiger_behind
|
||||||
|
|
||||||
- type: marking
|
- type: marking
|
||||||
id: LizardTailAquatic
|
id: LizardTailAquatic
|
||||||
@@ -422,12 +463,3 @@
|
|||||||
sprites:
|
sprites:
|
||||||
- sprite: Mobs/Customization/reptilian_parts.rsi
|
- sprite: Mobs/Customization/reptilian_parts.rsi
|
||||||
state: tail_dtiger_wagging
|
state: tail_dtiger_wagging
|
||||||
|
|
||||||
- type: marking
|
|
||||||
id: LizardTailAquaticAnimated
|
|
||||||
bodyPart: Tail
|
|
||||||
markingCategory: Tail
|
|
||||||
speciesRestriction: []
|
|
||||||
sprites:
|
|
||||||
- sprite: Mobs/Customization/reptilian_parts.rsi
|
|
||||||
state: tail_aquatic_wagging
|
|
||||||
|
|||||||
@@ -72,6 +72,7 @@
|
|||||||
proto: spider
|
proto: spider
|
||||||
- type: Sprite # I'd prefer if these maps were better. Insert map pun here.
|
- type: Sprite # I'd prefer if these maps were better. Insert map pun here.
|
||||||
layers:
|
layers:
|
||||||
|
- map: [ "enum.HumanoidVisualLayers.TailBehind" ] # Floof
|
||||||
- map: [ "enum.HumanoidVisualLayers.Chest" ]
|
- map: [ "enum.HumanoidVisualLayers.Chest" ]
|
||||||
- map: [ "enum.HumanoidVisualLayers.Head" ]
|
- map: [ "enum.HumanoidVisualLayers.Head" ]
|
||||||
- map: [ "enum.HumanoidVisualLayers.Snout" ]
|
- map: [ "enum.HumanoidVisualLayers.Snout" ]
|
||||||
@@ -94,6 +95,7 @@
|
|||||||
- map: [ "outerClothing" ]
|
- map: [ "outerClothing" ]
|
||||||
- map: [ "enum.HumanoidVisualLayers.Tail" ] # Mentioned in moth code: This needs renaming lol.
|
- map: [ "enum.HumanoidVisualLayers.Tail" ] # Mentioned in moth code: This needs renaming lol.
|
||||||
- map: [ "back" ]
|
- map: [ "back" ]
|
||||||
|
- map: [ "enum.HumanoidVisualLayers.TailOversuit" ] # Floof
|
||||||
- map: [ "neck" ]
|
- map: [ "neck" ]
|
||||||
- map: [ "enum.HumanoidVisualLayers.SnoutCover" ]
|
- map: [ "enum.HumanoidVisualLayers.SnoutCover" ]
|
||||||
- map: [ "enum.HumanoidVisualLayers.FacialHair" ]
|
- map: [ "enum.HumanoidVisualLayers.FacialHair" ]
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
layers:
|
layers:
|
||||||
|
- map: [ "enum.HumanoidVisualLayers.TailBehind" ] # Floof
|
||||||
- map: [ "enum.HumanoidVisualLayers.Chest" ]
|
- map: [ "enum.HumanoidVisualLayers.Chest" ]
|
||||||
- map: [ "enum.HumanoidVisualLayers.Head" ]
|
- map: [ "enum.HumanoidVisualLayers.Head" ]
|
||||||
- map: [ "enum.HumanoidVisualLayers.Snout" ]
|
- map: [ "enum.HumanoidVisualLayers.Snout" ]
|
||||||
@@ -36,7 +37,9 @@
|
|||||||
- map: [ "belt" ]
|
- map: [ "belt" ]
|
||||||
- map: [ "id" ]
|
- map: [ "id" ]
|
||||||
- map: [ "outerClothing" ]
|
- map: [ "outerClothing" ]
|
||||||
|
- map: [ "enum.HumanoidVisualLayers.TailBehindBackpack" ] # imp
|
||||||
- map: [ "back" ]
|
- map: [ "back" ]
|
||||||
|
- map: [ "enum.HumanoidVisualLayers.TailOversuit" ] # Floof
|
||||||
- map: [ "neck" ]
|
- map: [ "neck" ]
|
||||||
- map: [ "enum.HumanoidVisualLayers.SnoutCover" ]
|
- map: [ "enum.HumanoidVisualLayers.SnoutCover" ]
|
||||||
- map: [ "enum.HumanoidVisualLayers.FacialHair" ]
|
- map: [ "enum.HumanoidVisualLayers.FacialHair" ]
|
||||||
@@ -306,6 +309,7 @@
|
|||||||
noRot: true
|
noRot: true
|
||||||
# TODO BODY Turn these into individual body parts?
|
# TODO BODY Turn these into individual body parts?
|
||||||
layers:
|
layers:
|
||||||
|
- map: [ "enum.HumanoidVisualLayers.TailBehind" ]
|
||||||
- map: [ "enum.HumanoidVisualLayers.Chest" ]
|
- map: [ "enum.HumanoidVisualLayers.Chest" ]
|
||||||
- map: [ "enum.HumanoidVisualLayers.Head" ]
|
- map: [ "enum.HumanoidVisualLayers.Head" ]
|
||||||
- map: [ "enum.HumanoidVisualLayers.Snout" ]
|
- map: [ "enum.HumanoidVisualLayers.Snout" ]
|
||||||
@@ -334,6 +338,7 @@
|
|||||||
- map: [ "id" ]
|
- map: [ "id" ]
|
||||||
- map: [ "outerClothing" ]
|
- map: [ "outerClothing" ]
|
||||||
- map: [ "back" ]
|
- map: [ "back" ]
|
||||||
|
- map: [ "enum.HumanoidVisualLayers.TailOversuit" ]
|
||||||
- map: [ "neck" ]
|
- map: [ "neck" ]
|
||||||
- map: [ "enum.HumanoidVisualLayers.SnoutCover" ]
|
- map: [ "enum.HumanoidVisualLayers.SnoutCover" ]
|
||||||
- map: [ "enum.HumanoidVisualLayers.FacialHair" ]
|
- map: [ "enum.HumanoidVisualLayers.FacialHair" ]
|
||||||
|
|||||||
@@ -75,6 +75,7 @@
|
|||||||
noRot: true
|
noRot: true
|
||||||
drawdepth: Mobs
|
drawdepth: Mobs
|
||||||
layers:
|
layers:
|
||||||
|
- map: [ "enum.HumanoidVisualLayers.TailBehind" ] # Floof
|
||||||
- map: [ "enum.HumanoidVisualLayers.Chest" ]
|
- map: [ "enum.HumanoidVisualLayers.Chest" ]
|
||||||
- map: [ "enum.HumanoidVisualLayers.Head" ]
|
- map: [ "enum.HumanoidVisualLayers.Head" ]
|
||||||
- map: [ "enum.HumanoidVisualLayers.Snout" ]
|
- map: [ "enum.HumanoidVisualLayers.Snout" ]
|
||||||
@@ -104,6 +105,7 @@
|
|||||||
- map: [ "outerClothing" ]
|
- map: [ "outerClothing" ]
|
||||||
- map: [ "enum.HumanoidVisualLayers.Tail" ] #in the utopian future we should probably have a wings enum inserted here so everyhting doesn't break
|
- map: [ "enum.HumanoidVisualLayers.Tail" ] #in the utopian future we should probably have a wings enum inserted here so everyhting doesn't break
|
||||||
- map: [ "back" ]
|
- map: [ "back" ]
|
||||||
|
- map: [ "enum.HumanoidVisualLayers.TailOversuit" ] # Floof
|
||||||
- map: [ "neck" ]
|
- map: [ "neck" ]
|
||||||
- map: [ "enum.HumanoidVisualLayers.SnoutCover" ]
|
- map: [ "enum.HumanoidVisualLayers.SnoutCover" ]
|
||||||
- map: [ "enum.HumanoidVisualLayers.FacialHair" ]
|
- map: [ "enum.HumanoidVisualLayers.FacialHair" ]
|
||||||
|
|||||||
@@ -109,6 +109,7 @@
|
|||||||
Slash: 5 # Reduce?
|
Slash: 5 # Reduce?
|
||||||
- type: Sprite # Need to redefine the whole order to draw the tail over their gas tank
|
- type: Sprite # Need to redefine the whole order to draw the tail over their gas tank
|
||||||
layers:
|
layers:
|
||||||
|
- map: [ "enum.HumanoidVisualLayers.TailBehind" ] # Floof
|
||||||
- map: [ "enum.HumanoidVisualLayers.Chest" ]
|
- map: [ "enum.HumanoidVisualLayers.Chest" ]
|
||||||
- map: [ "enum.HumanoidVisualLayers.Head" ]
|
- map: [ "enum.HumanoidVisualLayers.Head" ]
|
||||||
- map: [ "enum.HumanoidVisualLayers.Snout" ]
|
- map: [ "enum.HumanoidVisualLayers.Snout" ]
|
||||||
@@ -132,6 +133,7 @@
|
|||||||
- map: [ "id" ]
|
- map: [ "id" ]
|
||||||
- map: [ "outerClothing" ]
|
- map: [ "outerClothing" ]
|
||||||
- map: [ "back" ]
|
- map: [ "back" ]
|
||||||
|
- map: [ "enum.HumanoidVisualLayers.TailOversuit" ] # Floof
|
||||||
- map: [ "neck" ]
|
- map: [ "neck" ]
|
||||||
- map: [ "suitstorage" ] # This is not in the default order
|
- map: [ "suitstorage" ] # This is not in the default order
|
||||||
- map: [ "enum.HumanoidVisualLayers.SnoutCover" ]
|
- map: [ "enum.HumanoidVisualLayers.SnoutCover" ]
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
- type: speciesBaseSprites
|
- type: speciesBaseSprites
|
||||||
id: MobArachnidSprites
|
id: MobArachnidSprites
|
||||||
sprites:
|
sprites:
|
||||||
|
TailBehind: MobHumanoidAnyMarking # floof
|
||||||
|
TailOversuit: MobHumanoidAnyMarking # floof
|
||||||
Head: MobArachnidHead
|
Head: MobArachnidHead
|
||||||
Snout: MobHumanoidAnyMarking
|
Snout: MobHumanoidAnyMarking
|
||||||
UndergarmentTop: MobHumanoidAnyMarking
|
UndergarmentTop: MobHumanoidAnyMarking
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
- type: speciesBaseSprites
|
- type: speciesBaseSprites
|
||||||
id: MobDionaSprites
|
id: MobDionaSprites
|
||||||
sprites:
|
sprites:
|
||||||
|
TailBehind: MobHumanoidAnyMarking # floof
|
||||||
|
TailOversuit: MobHumanoidAnyMarking # floof
|
||||||
Head: MobDionaHead
|
Head: MobDionaHead
|
||||||
HeadTop: MobHumanoidAnyMarking
|
HeadTop: MobHumanoidAnyMarking
|
||||||
HeadSide: MobHumanoidAnyMarking
|
HeadSide: MobHumanoidAnyMarking
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
- type: speciesBaseSprites
|
- type: speciesBaseSprites
|
||||||
id: MobGingerbreadSprites
|
id: MobGingerbreadSprites
|
||||||
sprites:
|
sprites:
|
||||||
|
TailBehind: MobHumanoidAnyMarking # floof. just in case i guess?
|
||||||
|
TailOversuit: MobHumanoidAnyMarking # floof
|
||||||
Head: MobGingerbreadHead
|
Head: MobGingerbreadHead
|
||||||
HeadTop: MobHumanoidAnyMarking
|
HeadTop: MobHumanoidAnyMarking
|
||||||
HeadSide: MobHumanoidAnyMarking
|
HeadSide: MobHumanoidAnyMarking
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
- type: speciesBaseSprites
|
- type: speciesBaseSprites
|
||||||
id: MobHumanSprites
|
id: MobHumanSprites
|
||||||
sprites:
|
sprites:
|
||||||
|
TailBehind: MobHumanoidAnyMarking # floof
|
||||||
|
TailOversuit: MobHumanoidAnyMarking # floof
|
||||||
Special: MobHumanoidAnyMarking
|
Special: MobHumanoidAnyMarking
|
||||||
Head: MobHumanHead
|
Head: MobHumanHead
|
||||||
Hair: MobHumanoidAnyMarking
|
Hair: MobHumanoidAnyMarking
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
- type: speciesBaseSprites
|
- type: speciesBaseSprites
|
||||||
id: MobMothSprites
|
id: MobMothSprites
|
||||||
sprites:
|
sprites:
|
||||||
|
TailBehind: MobHumanoidAnyMarking # floof
|
||||||
|
TailOversuit: MobHumanoidAnyMarking # floof
|
||||||
Head: MobMothHead
|
Head: MobMothHead
|
||||||
Snout: MobHumanoidAnyMarking
|
Snout: MobHumanoidAnyMarking
|
||||||
UndergarmentTop: MobHumanoidAnyMarking
|
UndergarmentTop: MobHumanoidAnyMarking
|
||||||
|
|||||||
@@ -15,6 +15,9 @@
|
|||||||
- type: speciesBaseSprites
|
- type: speciesBaseSprites
|
||||||
id: MobReptilianSprites
|
id: MobReptilianSprites
|
||||||
sprites:
|
sprites:
|
||||||
|
TailBehind: MobHumanoidAnyMarking # floof
|
||||||
|
TailOversuit: MobHumanoidAnyMarking # floof
|
||||||
|
TailBehindBackpack: MobHumanoidAnyMarking # imp
|
||||||
Head: MobReptilianHead
|
Head: MobReptilianHead
|
||||||
Snout: MobHumanoidAnyMarking
|
Snout: MobHumanoidAnyMarking
|
||||||
UndergarmentTop: MobHumanoidAnyMarking
|
UndergarmentTop: MobHumanoidAnyMarking
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
- type: speciesBaseSprites
|
- type: speciesBaseSprites
|
||||||
id: MobSlimeSprites
|
id: MobSlimeSprites
|
||||||
sprites:
|
sprites:
|
||||||
|
TailBehind: MobHumanoidAnyMarking # floof
|
||||||
|
TailOversuit: MobHumanoidAnyMarking # floof
|
||||||
Head: MobSlimeHead
|
Head: MobSlimeHead
|
||||||
Hair: MobSlimeMarkingFollowSkin
|
Hair: MobSlimeMarkingFollowSkin
|
||||||
FacialHair: MobSlimeMarkingFollowSkin
|
FacialHair: MobSlimeMarkingFollowSkin
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
- type: speciesBaseSprites
|
- type: speciesBaseSprites
|
||||||
id: MobVoxSprites
|
id: MobVoxSprites
|
||||||
sprites:
|
sprites:
|
||||||
|
TailBehind: MobHumanoidAnyMarking # floof
|
||||||
|
TailOversuit: MobHumanoidAnyMarking # floof
|
||||||
Head: MobVoxHead
|
Head: MobVoxHead
|
||||||
Snout: MobHumanoidAnyMarking
|
Snout: MobHumanoidAnyMarking
|
||||||
Hair: MobHumanoidAnyMarking
|
Hair: MobHumanoidAnyMarking
|
||||||
|
|||||||
Reference in New Issue
Block a user