градиенты волос (#2652)
This commit is contained in:
parent
d6aa7be5bf
commit
d1ed5109e2
33 changed files with 6274 additions and 69 deletions
|
|
@ -23,15 +23,29 @@ public sealed class DisplacementMapSystem : EntitySystem
|
|||
Entity<SpriteComponent> sprite,
|
||||
int index,
|
||||
object key,
|
||||
out string displacementKey)
|
||||
out string displacementKey,
|
||||
ShaderInstance? shaderOverride = null) // Sunrise-Edit
|
||||
{
|
||||
displacementKey = $"{key}-displacement";
|
||||
|
||||
if (key.ToString() is null)
|
||||
return false;
|
||||
|
||||
// Sunrise-Start
|
||||
// sprite.Comp.LayerSetShader(index, data.ShaderOverride);
|
||||
// TODO: костыль пиздец, когда появится возможность устанавливать 2 шейдера на один леер - удалить эту хуйню
|
||||
if (data.ShaderOverride != null)
|
||||
sprite.Comp.LayerSetShader(index, data.ShaderOverride);
|
||||
{
|
||||
if (shaderOverride != null)
|
||||
{
|
||||
shaderOverride.SetParameter("useDisplacement", true);
|
||||
shaderOverride.SetParameter("displacementSize", 127);
|
||||
sprite.Comp.LayerSetShader(index, shaderOverride);
|
||||
}
|
||||
else
|
||||
sprite.Comp.LayerSetShader(index, data.ShaderOverride);
|
||||
}
|
||||
// Sunrise-End
|
||||
|
||||
_sprite.RemoveLayer(sprite.AsNullable(), displacementKey, false);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,19 @@
|
|||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Client._Sunrise.MarkingEffectsClient;
|
||||
using Content.Client.DisplacementMap;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared._Sunrise;
|
||||
using Content.Shared._Sunrise.MarkingEffects;
|
||||
using Content.Shared.DisplacementMap;
|
||||
using Content.Shared.Humanoid.Markings;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Preferences;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
|
@ -181,14 +185,25 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
|
|||
var hairColor = _markingManager.MustMatchSkin(profile.Species, HumanoidVisualLayers.Hair, out var hairAlpha, _prototypeManager)
|
||||
? profile.Appearance.SkinColor.WithAlpha(hairAlpha)
|
||||
: profile.Appearance.HairColor;
|
||||
|
||||
var hairMarkingEffects = profile.Appearance.HairMarkingEffect != null
|
||||
? new List<MarkingEffect> { profile.Appearance.HairMarkingEffect }
|
||||
: new List<MarkingEffect>();
|
||||
|
||||
var hair = new Marking(profile.Appearance.HairStyleId,
|
||||
new[] { hairColor });
|
||||
new[] { hairColor },
|
||||
hairMarkingEffects);
|
||||
|
||||
var facialHairMarkingEffects = profile.Appearance.FacialHairMarkingEffect != null
|
||||
? new List<MarkingEffect> { profile.Appearance.FacialHairMarkingEffect }
|
||||
: new List<MarkingEffect>();
|
||||
|
||||
var facialHairColor = _markingManager.MustMatchSkin(profile.Species, HumanoidVisualLayers.FacialHair, out var facialHairAlpha, _prototypeManager)
|
||||
? profile.Appearance.SkinColor.WithAlpha(facialHairAlpha)
|
||||
: profile.Appearance.FacialHairColor;
|
||||
var facialHair = new Marking(profile.Appearance.FacialHairStyleId,
|
||||
new[] { facialHairColor });
|
||||
new[] { facialHairColor },
|
||||
facialHairMarkingEffects);
|
||||
|
||||
if (_markingManager.CanBeApplied(profile.Species, profile.Sex, hair, _prototypeManager))
|
||||
{
|
||||
|
|
@ -258,7 +273,7 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
|
|||
{
|
||||
if (_markingManager.TryGetMarking(marking, out var markingPrototype))
|
||||
{
|
||||
ApplyMarking(markingPrototype, marking.MarkingColors, marking.Visible, entity);
|
||||
ApplyMarking(markingPrototype, marking.MarkingColors, marking.Visible, entity, marking.MarkingEffects); // Sunrise-Edit
|
||||
//if (markingPrototype.BodyPart == HumanoidVisualLayers.UndergarmentTop)
|
||||
// applyUndergarmentTop = false;
|
||||
//else if (markingPrototype.BodyPart == HumanoidVisualLayers.UndergarmentBottom)
|
||||
|
|
@ -350,7 +365,8 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
|
|||
public void ApplyMarking(MarkingPrototype markingPrototype, // Sunrise-Edit
|
||||
IReadOnlyList<Color>? colors,
|
||||
bool visible,
|
||||
Entity<HumanoidAppearanceComponent, SpriteComponent> entity)
|
||||
Entity<HumanoidAppearanceComponent, SpriteComponent> entity,
|
||||
IReadOnlyList<MarkingEffect>? markingEffects = null)
|
||||
{
|
||||
var humanoid = entity.Comp1;
|
||||
var sprite = entity.Comp2;
|
||||
|
|
@ -389,22 +405,55 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
|
|||
continue;
|
||||
}
|
||||
|
||||
// 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.
|
||||
// So if that happens just default to white?
|
||||
if (colors != null && j < colors.Count)
|
||||
// Sunrise-Edit-Start
|
||||
// // 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.
|
||||
// // So if that happens just default to white?
|
||||
// if (colors != null && j < colors.Count)
|
||||
// {
|
||||
// _sprite.LayerSetColor((entity.Owner, sprite), layerId, colors[j]);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// _sprite.LayerSetColor((entity.Owner, sprite), layerId, Color.White);
|
||||
// }
|
||||
|
||||
ShaderInstance? shaderOverride = null;
|
||||
|
||||
|
||||
if (markingEffects != null && j < markingEffects.Count && markingEffects[j].Type != MarkingEffectType.Color)
|
||||
{
|
||||
_sprite.LayerSetColor((entity.Owner, sprite), layerId, colors[j]);
|
||||
float texWidth = sprite.AllLayers.Max(x => x.PixelSize.X);
|
||||
float texHeight = sprite.AllLayers.Max(x => x.PixelSize.Y);
|
||||
var shaderName = markingEffects[j].Type.ToString();
|
||||
var instance = _prototypeManager.Index<ShaderPrototype>(shaderName).InstanceUnique();
|
||||
shaderOverride = instance;
|
||||
|
||||
instance.ApplyShaderParams(markingEffects[j], new Vector2(texWidth, texHeight));
|
||||
|
||||
sprite.LayerSetShader(layerId, instance);
|
||||
_sprite.LayerSetColor((entity.Owner, sprite), layerId, Color.White);
|
||||
}
|
||||
else
|
||||
{
|
||||
_sprite.LayerSetColor((entity.Owner, sprite), layerId, Color.White);
|
||||
if (colors != null && j < colors.Count)
|
||||
{
|
||||
_sprite.LayerSetColor((entity.Owner, sprite), layerId, colors[j]);
|
||||
}
|
||||
else
|
||||
{
|
||||
_sprite.LayerSetColor((entity.Owner, sprite), layerId, Color.White);
|
||||
}
|
||||
}
|
||||
//Sunrise-Edit-End
|
||||
|
||||
var displacementData = GetMarkingDisplacement(entity.Owner, markingPrototype.BodyPart, humanoid);
|
||||
if (displacementData != null && markingPrototype.CanBeDisplaced)
|
||||
{
|
||||
_displacement.TryAddDisplacement(displacementData, (entity.Owner, sprite), targetLayer + j + 1, layerId, out _);
|
||||
// TODO: в шейдер нужно ещё вставлять displacementSize, сейчас в нём хардкод 127
|
||||
|
||||
// TODO: костыль пиздец, когда появится возможность устанавливать 2 шейдера на один леер - удалить эту хуйню (shaderOverride)
|
||||
_displacement.TryAddDisplacement(displacementData, (entity.Owner, sprite), targetLayer + j + 1, layerId, out _, shaderOverride); // Sunrise-Edit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -498,7 +547,7 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
|
|||
foreach (var marking in markingList)
|
||||
{
|
||||
if (_markingManager.TryGetMarking(marking, out var markingPrototype) && markingPrototype.BodyPart == layer)
|
||||
ApplyMarking(markingPrototype, marking.MarkingColors, marking.Visible, (ent, ent.Comp, sprite));
|
||||
ApplyMarking(markingPrototype, marking.MarkingColors, marking.Visible, (ent, ent.Comp, sprite), marking.MarkingEffects); // Sunrise-Edit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,9 +17,11 @@
|
|||
<ItemList Name="MarkingList" VerticalExpand="True" />
|
||||
</ScrollContainer>
|
||||
|
||||
<!-- Sunrise-Edit -->
|
||||
<!-- Color sliders -->
|
||||
<ScrollContainer MinHeight="200" HorizontalExpand="True">
|
||||
<BoxContainer Name="ColorSelectorContainer" HorizontalExpand="True" />
|
||||
</ScrollContainer>
|
||||
<BoxContainer Name="ColorSelectorContainer" HorizontalExpand="True" />
|
||||
<!-- <ScrollContainer MinHeight="200" HorizontalExpand="True"> -->
|
||||
<!-- <BoxContainer Name="ColorSelectorContainer" HorizontalExpand="True" /> -->
|
||||
<!-- </ScrollContainer> -->
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
using System.Linq;
|
||||
using Content.Client._Sunrise.UserInterface.Controls;
|
||||
using Content.Shared._Sunrise.MarkingEffects;
|
||||
using Content.Shared.Humanoid.Markings;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
|
|
@ -41,6 +43,10 @@ public sealed partial class SingleMarkingPicker : BoxContainer
|
|||
/// </summary>
|
||||
public Action<(int slot, Marking marking)>? OnColorChanged;
|
||||
|
||||
// sunrise gradient edit start
|
||||
public Action<(int slot, Marking marking)>? OnExtendedColorChanged;
|
||||
// sunrise gradient edit end
|
||||
|
||||
// current selected slot
|
||||
private int _slot = -1;
|
||||
private int Slot
|
||||
|
|
@ -229,23 +235,40 @@ public sealed partial class SingleMarkingPicker : BoxContainer
|
|||
ColorSelectorContainer.DisposeAllChildren();
|
||||
ColorSelectorContainer.RemoveAllChildren();
|
||||
|
||||
if (marking.MarkingColors.Count != proto.Sprites.Count)
|
||||
if (marking.MarkingColors.Count != proto.Sprites.Count ||
|
||||
marking.MarkingEffects.Count != proto.Sprites.Count)
|
||||
{
|
||||
marking = new Marking(marking.MarkingId, proto.Sprites.Count);
|
||||
}
|
||||
|
||||
for (var i = 0; i < marking.MarkingColors.Count; i++)
|
||||
{
|
||||
var selector = new ColorSelectorSliders
|
||||
MarkingEffect selectorColor;
|
||||
var selectorType = MarkingEffectType.Color;
|
||||
if(marking.MarkingEffects == null)
|
||||
selectorColor = new ColorMarkingEffect(marking.MarkingColors[i]);
|
||||
else
|
||||
{
|
||||
selectorColor = marking.MarkingEffects[i];
|
||||
selectorType = marking.MarkingEffects[i].Type;
|
||||
}
|
||||
|
||||
var selector = new MarkingEffectSelectorSliders(selectorColor)
|
||||
{
|
||||
HorizontalExpand = true
|
||||
};
|
||||
selector.Color = marking.MarkingColors[i];
|
||||
selector.CurrentType = selectorType;
|
||||
|
||||
var colorIndex = i;
|
||||
selector.OnColorChanged += color =>
|
||||
{
|
||||
marking.SetColor(colorIndex, color);
|
||||
var newCol = color.Colors["base"];
|
||||
if (marking.MarkingColors[colorIndex] != newCol)
|
||||
marking.SetColor(colorIndex, newCol);
|
||||
|
||||
if(marking.MarkingEffects?[colorIndex].Equals(color) != true)
|
||||
marking.SetMarkingEffect(colorIndex, color.Clone());
|
||||
|
||||
OnColorChanged!((_slot, marking));
|
||||
};
|
||||
|
||||
|
|
@ -272,6 +295,7 @@ public sealed partial class SingleMarkingPicker : BoxContainer
|
|||
for (var i = 0; i < _markings[Slot].MarkingColors.Count && i < oldMarking.MarkingColors.Count; i++)
|
||||
{
|
||||
_markings[Slot].SetColor(i, oldMarking.MarkingColors[i]);
|
||||
_markings[Slot].SetMarkingEffect(i, oldMarking.MarkingEffects[i]); // Sunrise-Edit
|
||||
}
|
||||
|
||||
PopulateColors();
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ using Content.Client.Sprite;
|
|||
using Content.Client.Stylesheets;
|
||||
using Content.Client.UserInterface.Systems.Guidebook;
|
||||
using Content.Shared._Sunrise;
|
||||
using Content.Shared._Sunrise.MarkingEffects;
|
||||
using Content.Shared._Sunrise.SunriseCCVars;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Clothing;
|
||||
|
|
@ -332,6 +333,30 @@ namespace Content.Client.Lobby.UI
|
|||
|
||||
#region Hair
|
||||
|
||||
// sunrise gradient edit start
|
||||
|
||||
HairStylePicker.OnExtendedColorChanged += newColor =>
|
||||
{
|
||||
if (Profile is null)
|
||||
return;
|
||||
Profile = Profile.WithCharacterAppearance(
|
||||
Profile.Appearance.WithHairExtendedColor(newColor.marking.MarkingEffects[0]));
|
||||
UpdateCMarkingsHair();
|
||||
ReloadPreview();
|
||||
};
|
||||
|
||||
FacialHairPicker.OnExtendedColorChanged += newColor =>
|
||||
{
|
||||
if (Profile is null)
|
||||
return;
|
||||
Profile = Profile.WithCharacterAppearance(
|
||||
Profile.Appearance.WithFacialHairExtendedColor(newColor.marking.MarkingEffects[0]));
|
||||
UpdateCMarkingsFacialHair();
|
||||
ReloadPreview();
|
||||
};
|
||||
|
||||
// sunrise gradient edit end
|
||||
|
||||
HairStylePicker.OnMarkingSelect += newStyle =>
|
||||
{
|
||||
if (Profile is null)
|
||||
|
|
@ -345,8 +370,9 @@ namespace Content.Client.Lobby.UI
|
|||
{
|
||||
if (Profile is null)
|
||||
return;
|
||||
var newExtended = newColor.marking.MarkingEffects[0].Clone();
|
||||
Profile = Profile.WithCharacterAppearance(
|
||||
Profile.Appearance.WithHairColor(newColor.marking.MarkingColors[0]));
|
||||
Profile.Appearance.WithHairColor(newColor.marking.MarkingColors[0], newExtended)); // sunrise gradient edit
|
||||
UpdateCMarkingsHair();
|
||||
ReloadPreview();
|
||||
};
|
||||
|
|
@ -364,8 +390,9 @@ namespace Content.Client.Lobby.UI
|
|||
{
|
||||
if (Profile is null)
|
||||
return;
|
||||
var newExtended = newColor.marking.MarkingEffects[0].Clone();
|
||||
Profile = Profile.WithCharacterAppearance(
|
||||
Profile.Appearance.WithFacialHairColor(newColor.marking.MarkingColors[0]));
|
||||
Profile.Appearance.WithFacialHairColor(newColor.marking.MarkingColors[0], newExtended)); // sunrise gradient edit
|
||||
UpdateCMarkingsFacialHair();
|
||||
ReloadPreview();
|
||||
};
|
||||
|
|
@ -1657,13 +1684,29 @@ namespace Content.Client.Lobby.UI
|
|||
var hairMarking = Profile.Appearance.HairStyleId switch
|
||||
{
|
||||
HairStyles.DefaultHairStyle => new List<Marking>(),
|
||||
_ => new() { new(Profile.Appearance.HairStyleId, new List<Color>() { Profile.Appearance.HairColor }) },
|
||||
_ => new List<Marking>
|
||||
{
|
||||
new(
|
||||
Profile.Appearance.HairStyleId,
|
||||
new[] { Profile.Appearance.HairColor },
|
||||
Profile.Appearance.HairMarkingEffect is { } hairExt
|
||||
? new List<MarkingEffect> { hairExt.Clone() }
|
||||
: null)
|
||||
}
|
||||
};
|
||||
|
||||
var facialHairMarking = Profile.Appearance.FacialHairStyleId switch
|
||||
{
|
||||
HairStyles.DefaultFacialHairStyle => new List<Marking>(),
|
||||
_ => new() { new(Profile.Appearance.FacialHairStyleId, new List<Color>() { Profile.Appearance.FacialHairColor }) },
|
||||
_ => new List<Marking>
|
||||
{
|
||||
new(
|
||||
Profile.Appearance.FacialHairStyleId,
|
||||
new[] { Profile.Appearance.FacialHairColor },
|
||||
Profile.Appearance.FacialHairMarkingEffect is { } facialExt
|
||||
? new List<MarkingEffect> { facialExt.Clone() }
|
||||
: null)
|
||||
}
|
||||
};
|
||||
|
||||
HairStylePicker.UpdateData(
|
||||
|
|
@ -1703,7 +1746,12 @@ namespace Content.Client.Lobby.UI
|
|||
}
|
||||
if (hairColor != null)
|
||||
{
|
||||
Markings.HairMarking = new (Profile.Appearance.HairStyleId, new List<Color>() { hairColor.Value });
|
||||
Markings.HairMarking = new (
|
||||
Profile.Appearance.HairStyleId,
|
||||
new List<Color>() { hairColor.Value },
|
||||
Profile.Appearance.HairMarkingEffect is { } hairExt
|
||||
? new List<MarkingEffect> { hairExt.Clone() }
|
||||
: null);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1737,7 +1785,12 @@ namespace Content.Client.Lobby.UI
|
|||
}
|
||||
if (facialHairColor != null)
|
||||
{
|
||||
Markings.FacialHairMarking = new (Profile.Appearance.FacialHairStyleId, new List<Color>() { facialHairColor.Value });
|
||||
Markings.FacialHairMarking = new(
|
||||
Profile.Appearance.FacialHairStyleId,
|
||||
new List<Color>() { facialHairColor.Value },
|
||||
Profile.Appearance.FacialHairMarkingEffect is { } facialExt
|
||||
? new List<MarkingEffect> { facialExt.Clone() }
|
||||
: null);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1912,7 +1965,6 @@ namespace Content.Client.Lobby.UI
|
|||
}
|
||||
|
||||
CBodyTypesButton.Select(_bodyTypes.FindIndex(x => x.ID == Profile.BodyType));
|
||||
IsDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
using Content.Client._Sunrise.UserInterface.Controls;
|
||||
using Content.Shared._Sunrise.MarkingEffects;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client._Sunrise.MarkingEffectsClient;
|
||||
|
||||
public sealed class ColorMarkingEffectUiBuilder : IMarkingEffectUiBuilder
|
||||
{
|
||||
public void BuildUI(MarkingEffect effect, MarkingEffectSelectorSliders parent)
|
||||
{
|
||||
if (effect is not ColorMarkingEffect)
|
||||
return;
|
||||
|
||||
parent.CreateSelector(type: MarkingEffectType.Color);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
using Content.Client._Sunrise.UserInterface.Controls;
|
||||
using Content.Shared._Sunrise.MarkingEffects;
|
||||
|
||||
namespace Content.Client._Sunrise.MarkingEffectsClient;
|
||||
|
||||
public sealed class GradientMarkingEffectUiBuilder : IMarkingEffectUiBuilder
|
||||
{
|
||||
private const float ToIntScaling = 100;
|
||||
|
||||
private const int OffsetMin = -200;
|
||||
private const int OffsetMax = 100;
|
||||
|
||||
private const int SizeMin = 30;
|
||||
private const int SizeMax = 500;
|
||||
|
||||
private const int RotationMin = 0;
|
||||
private const int RotationMax = 360;
|
||||
|
||||
public void BuildUI(MarkingEffect effect, MarkingEffectSelectorSliders parent)
|
||||
{
|
||||
if (effect is not GradientMarkingEffect gradient)
|
||||
return;
|
||||
|
||||
parent.CreateSelector(type: MarkingEffectType.Gradient);
|
||||
parent.CreateSelector("gradient", MarkingEffectType.Gradient);
|
||||
|
||||
parent.CreateSlider(Loc.GetString("marking-effect-gradient-parameter-offset"),
|
||||
(int)(gradient.Offset.Y * ToIntScaling), OffsetMin, OffsetMax,
|
||||
v => gradient.Offset.Y = v / ToIntScaling
|
||||
);
|
||||
parent.CreateSlider(Loc.GetString("marking-effect-gradient-parameter-size"),
|
||||
(int)(gradient.Size.Y * ToIntScaling), SizeMin, SizeMax,
|
||||
v => gradient.Size.Y = v / ToIntScaling);
|
||||
parent.CreateSlider(Loc.GetString("marking-effect-gradient-parameter-rotation"),
|
||||
(int)gradient.Rotation, RotationMin, RotationMax,
|
||||
v => gradient.Rotation = v);
|
||||
|
||||
parent.CreateToggle(Loc.GetString("marking-effect-gradient-parameter-pixelation"),
|
||||
gradient.Pixelated,
|
||||
v => gradient.Pixelated = v);
|
||||
parent.CreateToggle(Loc.GetString("marking-effect-gradient-parameter-mirror"),
|
||||
gradient.Mirrored,
|
||||
v => gradient.Mirrored = v);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
using Content.Client._Sunrise.UserInterface.Controls;
|
||||
using Content.Shared._Sunrise.MarkingEffects;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client._Sunrise.MarkingEffectsClient;
|
||||
|
||||
public interface IMarkingEffectUiBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds UI elements to customize <see cref="MarkingEffect"/>
|
||||
/// </summary>
|
||||
void BuildUI(MarkingEffect effect, MarkingEffectSelectorSliders parent);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
using System.Numerics;
|
||||
using Content.Shared._Sunrise.MarkingEffects;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
|
||||
namespace Content.Client._Sunrise.MarkingEffectsClient;
|
||||
|
||||
public static class MarkingEffectShaders
|
||||
{
|
||||
|
||||
public static Robust.Shared.Maths.Vector3 ColorToVec(Color col)
|
||||
{
|
||||
return new Robust.Shared.Maths.Vector3(col.R, col.G, col.B);
|
||||
}
|
||||
|
||||
public static void ApplyShaderParams(this ShaderInstance instance, MarkingEffect color, Vector2 texScale)
|
||||
{
|
||||
instance.SetParameter("useDisplacement", false);
|
||||
|
||||
switch (color.Type)
|
||||
{
|
||||
case MarkingEffectType.Gradient:
|
||||
if (color is not GradientMarkingEffect gradient)
|
||||
return;
|
||||
|
||||
instance.SetParameter("color1", ColorToVec(gradient.Colors["base"]));
|
||||
instance.SetParameter("color2", ColorToVec(gradient.Colors["gradient"]));
|
||||
instance.SetParameter("texScale", texScale);
|
||||
instance.SetParameter("offset", gradient.Offset);
|
||||
instance.SetParameter("size", gradient.Size);
|
||||
instance.SetParameter("rotation", gradient.Rotation);
|
||||
instance.SetParameter("pixelated", gradient.Pixelated);
|
||||
instance.SetParameter("mirrored", gradient.Mirrored);
|
||||
break;
|
||||
case MarkingEffectType.RoughGradient:
|
||||
if (color is not RoughGradientMarkingEffect roughGradient)
|
||||
return;
|
||||
instance.SetParameter("color1", ColorToVec(roughGradient.Colors["base"]));
|
||||
instance.SetParameter("color2", ColorToVec(roughGradient.Colors["gradient"]));
|
||||
instance.SetParameter("horizontal", roughGradient.Horizontal);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
using Content.Client._Sunrise.UserInterface.Controls;
|
||||
using Content.Shared._Sunrise.MarkingEffects;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client._Sunrise.MarkingEffectsClient;
|
||||
|
||||
public sealed class RoughGradientMarkingEffectUiBuilder : IMarkingEffectUiBuilder
|
||||
{
|
||||
public void BuildUI(MarkingEffect effect, MarkingEffectSelectorSliders parent)
|
||||
{
|
||||
if (effect is not RoughGradientMarkingEffect rough)
|
||||
return;
|
||||
|
||||
parent.CreateSelector(type: MarkingEffectType.RoughGradient);
|
||||
parent.CreateSelector("gradient", type: MarkingEffectType.RoughGradient);
|
||||
|
||||
parent.CreateToggle(Loc.GetString("marking-effect-roughgradient-parameter-horizontal"),
|
||||
rough.Horizontal,
|
||||
val => rough.Horizontal = val);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,445 @@
|
|||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
namespace Content.Client._Sunrise.UserInterface.Controls;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
// condensed version of the original ColorSlider set
|
||||
public sealed class CustomColorSelectorSliders : Control
|
||||
{
|
||||
public Color Color
|
||||
{
|
||||
get => _currentColor;
|
||||
set
|
||||
{
|
||||
_currentColor = value;
|
||||
switch (SelectorType)
|
||||
{
|
||||
case ColorSelectorType.Rgb:
|
||||
_colorData = new Vector4(_currentColor.R, _currentColor.G, _currentColor.B, _currentColor.A);
|
||||
break;
|
||||
case ColorSelectorType.Hsv:
|
||||
_colorData = Color.ToHsv(value);
|
||||
break;
|
||||
}
|
||||
Update();
|
||||
}
|
||||
}
|
||||
|
||||
public ColorSelectorType SelectorType
|
||||
{
|
||||
get => _currentType;
|
||||
set
|
||||
{
|
||||
switch ((_currentType, value))
|
||||
{
|
||||
case (ColorSelectorType.Rgb, ColorSelectorType.Hsv):
|
||||
_colorData = Color.ToHsv(Color);
|
||||
break;
|
||||
case (ColorSelectorType.Hsv, ColorSelectorType.Rgb):
|
||||
_colorData = new Vector4(_currentColor.R, _currentColor.G, _currentColor.B, _currentColor.A);
|
||||
break;
|
||||
}
|
||||
_currentType = value;
|
||||
UpdateType();
|
||||
Update();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsAlphaVisible
|
||||
{
|
||||
get => _isAlphaVisible;
|
||||
set
|
||||
{
|
||||
_isAlphaVisible = value;
|
||||
|
||||
_alphaSliderBox.Visible = _isAlphaVisible;
|
||||
}
|
||||
}
|
||||
|
||||
public Action<Color>? OnColorChanged;
|
||||
public Action<Color>? OnColorReleased;
|
||||
|
||||
private bool _updating = false;
|
||||
private Color _currentColor = Color.White;
|
||||
private Vector4 _colorData;
|
||||
private ColorSelectorType _currentType = ColorSelectorType.Rgb;
|
||||
private bool _isAlphaVisible = false;
|
||||
|
||||
private ColorableSlider _topColorSlider;
|
||||
private ColorableSlider _middleColorSlider;
|
||||
private ColorableSlider _bottomColorSlider;
|
||||
private Slider _alphaSlider;
|
||||
|
||||
private BoxContainer _alphaSliderBox = new();
|
||||
|
||||
private SpinBox _topInputBox;
|
||||
private SpinBox _middleInputBox;
|
||||
private SpinBox _bottomInputBox;
|
||||
private SpinBox _alphaInputBox;
|
||||
|
||||
private Label _topSliderLabel = new();
|
||||
private Label _middleSliderLabel = new();
|
||||
private Label _bottomSliderLabel = new();
|
||||
private Label _alphaSliderLabel = new();
|
||||
|
||||
private OptionButton _typeSelector;
|
||||
private List<ColorSelectorType> _types = new();
|
||||
|
||||
private ColorSelectorStyleBox _topStyle;
|
||||
private ColorSelectorStyleBox _middleStyle;
|
||||
private ColorSelectorStyleBox _bottomStyle;
|
||||
|
||||
public CustomColorSelectorSliders() : this(ColorSelectorType.Rgb){} // Sunrise-Edit
|
||||
|
||||
|
||||
public CustomColorSelectorSliders(ColorSelectorType type = ColorSelectorType.Rgb, string? label = null) // Sunrise-Edit
|
||||
{
|
||||
_topColorSlider = new ColorableSlider
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
BackgroundStyleBoxOverride = _topStyle = new(),
|
||||
MaxValue = 1.0f
|
||||
};
|
||||
|
||||
_middleColorSlider = new ColorableSlider
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
BackgroundStyleBoxOverride = _middleStyle = new(),
|
||||
MaxValue = 1.0f
|
||||
};
|
||||
|
||||
_bottomColorSlider = new ColorableSlider
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
BackgroundStyleBoxOverride = _bottomStyle = new(),
|
||||
MaxValue = 1.0f
|
||||
};
|
||||
|
||||
_alphaSlider = new Slider
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
MaxValue = 1.0f,
|
||||
};
|
||||
|
||||
_topColorSlider.OnValueChanged += _ => { OnColorSet(); };
|
||||
_middleColorSlider.OnValueChanged += _ => { OnColorSet(); };
|
||||
_bottomColorSlider.OnValueChanged += _ => { OnColorSet(); };
|
||||
_alphaSlider.OnValueChanged += _ => { OnColorSet(); };
|
||||
|
||||
_topColorSlider.OnReleased += _ => { OnColorSet(true); };
|
||||
_middleColorSlider.OnReleased += _ => { OnColorSet(true); };
|
||||
_bottomColorSlider.OnReleased += _ => { OnColorSet(true); };
|
||||
_alphaSlider.OnReleased += _ => { OnColorSet(true); };
|
||||
|
||||
_topInputBox = new SpinBox
|
||||
{
|
||||
IsValid = value => IsSpinBoxValid(value, ColorSliderOrder.Top)
|
||||
};
|
||||
_topInputBox.InitDefaultButtons();
|
||||
|
||||
_middleInputBox = new SpinBox
|
||||
{
|
||||
IsValid = value => IsSpinBoxValid(value, ColorSliderOrder.Middle)
|
||||
};
|
||||
_middleInputBox.InitDefaultButtons();
|
||||
|
||||
_bottomInputBox = new SpinBox
|
||||
{
|
||||
IsValid = value => IsSpinBoxValid(value, ColorSliderOrder.Bottom)
|
||||
};
|
||||
_bottomInputBox.InitDefaultButtons();
|
||||
|
||||
_alphaInputBox = new SpinBox
|
||||
{
|
||||
IsValid = value => IsSpinBoxValid(value, ColorSliderOrder.Alpha)
|
||||
};
|
||||
_alphaInputBox.InitDefaultButtons();
|
||||
|
||||
_topInputBox.ValueChanged += value =>
|
||||
{
|
||||
_topColorSlider.Value = value.Value / GetColorValueDivisor(ColorSliderOrder.Top);
|
||||
};
|
||||
|
||||
_middleInputBox.ValueChanged += value =>
|
||||
{
|
||||
_middleColorSlider.Value = value.Value / GetColorValueDivisor(ColorSliderOrder.Middle);
|
||||
};
|
||||
|
||||
_bottomInputBox.ValueChanged += value =>
|
||||
{
|
||||
_bottomColorSlider.Value = value.Value / GetColorValueDivisor(ColorSliderOrder.Bottom);
|
||||
};
|
||||
|
||||
_alphaInputBox.ValueChanged += value =>
|
||||
{
|
||||
_alphaSlider.Value = value.Value / GetColorValueDivisor(ColorSliderOrder.Alpha);
|
||||
};
|
||||
|
||||
_alphaSliderLabel.Text = Loc.GetString("color-selector-sliders-alpha");
|
||||
|
||||
_typeSelector = new OptionButton();
|
||||
foreach (var ty in Enum.GetValues<ColorSelectorType>())
|
||||
{
|
||||
_typeSelector.AddItem(Loc.GetString($"color-selector-sliders-{ty.ToString().ToLower()}"));
|
||||
_types.Add(ty);
|
||||
}
|
||||
|
||||
_typeSelector.OnItemSelected += args =>
|
||||
{
|
||||
SelectorType = _types[args.Id];
|
||||
_typeSelector.Select(args.Id);
|
||||
};
|
||||
|
||||
// TODO: Maybe some engine widgets could be laid out in XAML?
|
||||
|
||||
var rootBox = new BoxContainer
|
||||
{
|
||||
Orientation = BoxContainer.LayoutOrientation.Vertical
|
||||
};
|
||||
AddChild(rootBox);
|
||||
|
||||
var headerBox = new BoxContainer();
|
||||
rootBox.AddChild(headerBox);
|
||||
|
||||
headerBox.AddChild(_typeSelector);
|
||||
|
||||
//Sunrise-Edit-Start
|
||||
if (label != null)
|
||||
{
|
||||
var textLabel = new Label
|
||||
{
|
||||
Text = label,
|
||||
};
|
||||
headerBox.AddChild(textLabel);
|
||||
}
|
||||
//Sunrise-Edit-End
|
||||
|
||||
var bodyBox = new BoxContainer()
|
||||
{
|
||||
Orientation = BoxContainer.LayoutOrientation.Vertical
|
||||
};
|
||||
|
||||
// pita
|
||||
var topSliderBox = new BoxContainer();
|
||||
|
||||
topSliderBox.AddChild(_topSliderLabel);
|
||||
topSliderBox.AddChild(_topColorSlider);
|
||||
topSliderBox.AddChild(_topInputBox);
|
||||
|
||||
var middleSliderBox = new BoxContainer();
|
||||
|
||||
middleSliderBox.AddChild(_middleSliderLabel);
|
||||
middleSliderBox.AddChild(_middleColorSlider);
|
||||
middleSliderBox.AddChild(_middleInputBox);
|
||||
|
||||
var bottomSliderBox = new BoxContainer();
|
||||
|
||||
bottomSliderBox.AddChild(_bottomSliderLabel);
|
||||
bottomSliderBox.AddChild(_bottomColorSlider);
|
||||
bottomSliderBox.AddChild(_bottomInputBox);
|
||||
|
||||
_alphaSliderBox.Visible = IsAlphaVisible;
|
||||
_alphaSliderBox.AddChild(_alphaSliderLabel);
|
||||
_alphaSliderBox.AddChild(_alphaSlider);
|
||||
_alphaSliderBox.AddChild(_alphaInputBox);
|
||||
|
||||
bodyBox.AddChild(topSliderBox);
|
||||
bodyBox.AddChild(middleSliderBox);
|
||||
bodyBox.AddChild(bottomSliderBox);
|
||||
bodyBox.AddChild(_alphaSliderBox);
|
||||
|
||||
rootBox.AddChild(bodyBox);
|
||||
|
||||
SelectorType = type; // Sunrise-Edit
|
||||
_typeSelector.TrySelect(_types.IndexOf(_currentType)); // Sunrise-Edit
|
||||
|
||||
UpdateType();
|
||||
Color = _currentColor;
|
||||
}
|
||||
|
||||
private void UpdateType()
|
||||
{
|
||||
(string topLabel, string middleLabel, string bottomLabel) labels = GetSliderLabels();
|
||||
|
||||
_topSliderLabel.Text = labels.topLabel;
|
||||
_middleSliderLabel.Text = labels.middleLabel;
|
||||
_bottomSliderLabel.Text = labels.bottomLabel;
|
||||
|
||||
bool hsv = SelectorType == ColorSelectorType.Hsv;
|
||||
_topStyle.ConfigureSlider( hsv ? ColorSelectorStyleBox.ColorSliderPreset.Hue : ColorSelectorStyleBox.ColorSliderPreset.Red);
|
||||
_middleStyle.ConfigureSlider( hsv ? ColorSelectorStyleBox.ColorSliderPreset.Saturation : ColorSelectorStyleBox.ColorSliderPreset.Green);
|
||||
_bottomStyle.ConfigureSlider( hsv ? ColorSelectorStyleBox.ColorSliderPreset.Value : ColorSelectorStyleBox.ColorSliderPreset.Blue);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// This code is a mess of UI events causing stack overflows. Also, updating one slider triggers all sliders to
|
||||
// update, which due to rounding errors causes them to actually change values, specifically for HSV sliders.
|
||||
if (_updating)
|
||||
return;
|
||||
|
||||
_updating = true;
|
||||
_topStyle.SetBaseColor(_colorData);
|
||||
_middleStyle.SetBaseColor(_colorData);
|
||||
_bottomStyle.SetBaseColor(_colorData);
|
||||
|
||||
switch (SelectorType)
|
||||
{
|
||||
case ColorSelectorType.Rgb:
|
||||
_topColorSlider.Value = _colorData.X;
|
||||
_middleColorSlider.Value = _colorData.Y;
|
||||
_bottomColorSlider.Value = _colorData.Z;
|
||||
|
||||
_topInputBox.Value = (int)(_colorData.X * 255.0f);
|
||||
_middleInputBox.Value = (int)(_colorData.Y * 255.0f);
|
||||
_bottomInputBox.Value = (int)(_colorData.Z * 255.0f);
|
||||
|
||||
break;
|
||||
case ColorSelectorType.Hsv:
|
||||
// dumb workaround because the formula for
|
||||
// HSV calculation results in a negative
|
||||
// number in any value past 300 degrees
|
||||
if (_colorData.X > 0)
|
||||
{
|
||||
_topColorSlider.Value = _colorData.X;
|
||||
_topInputBox.Value = (int)(_colorData.X * 360.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
_topInputBox.Value = (int)(_topColorSlider.Value * 360.0f);
|
||||
}
|
||||
|
||||
_middleColorSlider.Value = _colorData.Y;
|
||||
_bottomColorSlider.Value = _colorData.Z;
|
||||
|
||||
_middleInputBox.Value = (int)(_colorData.Y * 100.0f);
|
||||
_bottomInputBox.Value = (int)(_colorData.Z * 100.0f);
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
_alphaSlider.Value = Color.A;
|
||||
_alphaInputBox.Value = (int)(Color.A * 100.0f);
|
||||
_updating = false;
|
||||
}
|
||||
|
||||
private bool IsSpinBoxValid(int value, ColorSliderOrder ordering)
|
||||
{
|
||||
if (value < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ordering == ColorSliderOrder.Alpha)
|
||||
{
|
||||
return value <= 100;
|
||||
}
|
||||
|
||||
switch (SelectorType)
|
||||
{
|
||||
case ColorSelectorType.Rgb:
|
||||
return value <= byte.MaxValue;
|
||||
case ColorSelectorType.Hsv:
|
||||
switch (ordering)
|
||||
{
|
||||
case ColorSliderOrder.Top:
|
||||
return value <= 360;
|
||||
default:
|
||||
return value <= 100;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private (string, string, string) GetSliderLabels()
|
||||
{
|
||||
switch (SelectorType)
|
||||
{
|
||||
case ColorSelectorType.Rgb:
|
||||
return (
|
||||
Loc.GetString("color-selector-sliders-red"),
|
||||
Loc.GetString("color-selector-sliders-green"),
|
||||
Loc.GetString("color-selector-sliders-blue")
|
||||
);
|
||||
case ColorSelectorType.Hsv:
|
||||
return (
|
||||
Loc.GetString("color-selector-sliders-hue"),
|
||||
Loc.GetString("color-selector-sliders-saturation"),
|
||||
Loc.GetString("color-selector-sliders-value")
|
||||
);
|
||||
}
|
||||
|
||||
return ("ERR", "ERR", "ERR");
|
||||
}
|
||||
|
||||
private float GetColorValueDivisor(ColorSliderOrder order)
|
||||
{
|
||||
if (order == ColorSliderOrder.Alpha)
|
||||
{
|
||||
return 100.0f;
|
||||
}
|
||||
|
||||
switch (SelectorType)
|
||||
{
|
||||
case ColorSelectorType.Rgb:
|
||||
return 255.0f;
|
||||
case ColorSelectorType.Hsv:
|
||||
switch (order)
|
||||
{
|
||||
case ColorSliderOrder.Top:
|
||||
return 360.0f;
|
||||
default:
|
||||
return 100.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
private void OnColorSet(bool release = false)
|
||||
{
|
||||
// stack overflow otherwise due to value sets
|
||||
if (_updating)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_colorData = new Vector4(_topColorSlider.Value, _middleColorSlider.Value, _bottomColorSlider.Value, _alphaSlider.Value);
|
||||
|
||||
_currentColor = SelectorType switch
|
||||
{
|
||||
ColorSelectorType.Hsv => Color.FromHsv(_colorData),
|
||||
_ => new Color(_colorData.X, _colorData.Y, _colorData.Z, _colorData.W)
|
||||
};
|
||||
|
||||
Update();
|
||||
if (release)
|
||||
OnColorReleased?.Invoke(_currentColor);
|
||||
else
|
||||
OnColorChanged?.Invoke(_currentColor);
|
||||
}
|
||||
|
||||
private enum ColorSliderOrder
|
||||
{
|
||||
Top,
|
||||
Middle,
|
||||
Bottom,
|
||||
Alpha
|
||||
}
|
||||
|
||||
public enum ColorSelectorType
|
||||
{
|
||||
Rgb,
|
||||
Hsv,
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
using Content.Client._Sunrise.MarkingEffectsClient;
|
||||
using Content.Shared._Sunrise.MarkingEffects;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
namespace Content.Client._Sunrise.UserInterface.Controls;
|
||||
|
||||
public sealed class MarkingEffectSelectorSliders : Control
|
||||
{
|
||||
private MarkingEffect Effect { get; set; }
|
||||
|
||||
private static readonly Dictionary<MarkingEffectType, IMarkingEffectUiBuilder> UiBuilders = new()
|
||||
{
|
||||
{ MarkingEffectType.Color, new ColorMarkingEffectUiBuilder() },
|
||||
{ MarkingEffectType.Gradient, new GradientMarkingEffectUiBuilder() },
|
||||
{ MarkingEffectType.RoughGradient, new RoughGradientMarkingEffectUiBuilder() },
|
||||
};
|
||||
|
||||
private readonly Dictionary<string, CustomColorSelectorSliders> _colorSelectors = new();
|
||||
|
||||
private readonly OptionButton _typeSelector;
|
||||
private readonly List<MarkingEffectType> _types = new();
|
||||
|
||||
private MarkingEffectType _currentType;
|
||||
|
||||
private readonly BoxContainer _selectorsContainer;
|
||||
private readonly BoxContainer _slidersContainer;
|
||||
private readonly BoxContainer _toggleContainer;
|
||||
|
||||
public Action<MarkingEffect>? OnColorChanged;
|
||||
|
||||
public MarkingEffectType CurrentType
|
||||
{
|
||||
get => _currentType;
|
||||
set
|
||||
{
|
||||
if (_currentType == value)
|
||||
return;
|
||||
|
||||
_currentType = value;
|
||||
Populate(_currentType);
|
||||
}
|
||||
}
|
||||
|
||||
public MarkingEffectSelectorSliders(MarkingEffect? defaultEffect = null)
|
||||
{
|
||||
defaultEffect ??= ColorMarkingEffect.White;
|
||||
|
||||
_typeSelector = new OptionButton();
|
||||
foreach (var type in Enum.GetValues<MarkingEffectType>())
|
||||
{
|
||||
_typeSelector.AddItem(Loc.GetString($"marking-effect-type-{type.ToString().ToLower()}"));
|
||||
_types.Add(type);
|
||||
}
|
||||
|
||||
_typeSelector.OnItemSelected += args =>
|
||||
{
|
||||
CurrentType = _types[args.Id];
|
||||
_typeSelector.Select(args.Id);
|
||||
OnColorsChanged();
|
||||
};
|
||||
|
||||
var rootBox = new BoxContainer
|
||||
{ Orientation = BoxContainer.LayoutOrientation.Vertical };
|
||||
AddChild(rootBox);
|
||||
|
||||
var headerBox = new BoxContainer();
|
||||
rootBox.AddChild(headerBox);
|
||||
headerBox.AddChild(_typeSelector);
|
||||
|
||||
var bodyBox = new BoxContainer
|
||||
{ Orientation = BoxContainer.LayoutOrientation.Vertical };
|
||||
rootBox.AddChild(bodyBox);
|
||||
|
||||
_selectorsContainer = new BoxContainer();
|
||||
bodyBox.AddChild(_selectorsContainer);
|
||||
|
||||
_slidersContainer = new BoxContainer
|
||||
{ Orientation = BoxContainer.LayoutOrientation.Vertical };
|
||||
bodyBox.AddChild(_slidersContainer);
|
||||
|
||||
_toggleContainer = new BoxContainer();
|
||||
bodyBox.AddChild(_toggleContainer);
|
||||
|
||||
|
||||
_currentType = defaultEffect.Type;
|
||||
_typeSelector.TrySelect(_types.IndexOf(_currentType));
|
||||
Effect = defaultEffect;
|
||||
Populate(_currentType, defaultEffect);
|
||||
}
|
||||
|
||||
public CustomColorSelectorSliders CreateSelector(string key = "base", MarkingEffectType type = MarkingEffectType.Color)
|
||||
{
|
||||
var colorSelector = new CustomColorSelectorSliders(
|
||||
CustomColorSelectorSliders.ColorSelectorType.Hsv,
|
||||
Loc.GetString($"marking-effect-{type.ToString().ToLower()}-color-{key}"));
|
||||
|
||||
colorSelector.HorizontalExpand = true;
|
||||
colorSelector.HorizontalAlignment = HAlignment.Stretch;
|
||||
|
||||
if (Effect.Colors.TryGetValue(key, out var defaultColor))
|
||||
colorSelector.Color = defaultColor;
|
||||
|
||||
colorSelector.OnColorReleased += _ => OnColorsChanged();
|
||||
|
||||
_colorSelectors.Add(key, colorSelector);
|
||||
|
||||
var selectorContainer = new BoxContainer
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
HorizontalAlignment = HAlignment.Stretch,
|
||||
};
|
||||
|
||||
_selectorsContainer.AddChild(selectorContainer);
|
||||
selectorContainer.AddChild(colorSelector);
|
||||
|
||||
return colorSelector;
|
||||
}
|
||||
|
||||
public void CreateSlider(string label,
|
||||
int defaultValue,
|
||||
int minValue,
|
||||
int maxValue,
|
||||
Action<float> onValueChanged)
|
||||
{
|
||||
var slider = new Slider
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
};
|
||||
|
||||
slider.MinValue = minValue;
|
||||
slider.MaxValue = maxValue;
|
||||
slider.Value = defaultValue;
|
||||
|
||||
var sliderContainer = new BoxContainer();
|
||||
|
||||
var sliderLabel = new Label();
|
||||
sliderLabel.Text = label;
|
||||
|
||||
var spinBox = new SpinBox
|
||||
{
|
||||
IsValid = value => IsSpinBoxValid(value, minValue, maxValue)
|
||||
};
|
||||
spinBox.InitDefaultButtons();
|
||||
spinBox.Value = defaultValue;
|
||||
|
||||
|
||||
|
||||
sliderContainer.AddChild(sliderLabel);
|
||||
sliderContainer.AddChild(slider);
|
||||
sliderContainer.AddChild(spinBox);
|
||||
_slidersContainer.AddChild(sliderContainer);
|
||||
|
||||
BindSlider(slider, spinBox, onValueChanged);
|
||||
}
|
||||
|
||||
private void BindSlider(Slider slider, SpinBox spinBox, Action<float> setValue)
|
||||
{
|
||||
slider.OnReleased += val =>
|
||||
{
|
||||
setValue(val.Value);
|
||||
spinBox.Value = (int)(val.Value);
|
||||
OnColorsChanged();
|
||||
};
|
||||
|
||||
spinBox.ValueChanged += val =>
|
||||
{
|
||||
setValue(val.Value);
|
||||
slider.SetValueWithoutEvent(val.Value);
|
||||
OnColorsChanged();
|
||||
};
|
||||
}
|
||||
|
||||
public void CreateToggle(string label, bool defaultValue, Action<bool> onValueChanged)
|
||||
{
|
||||
var button = new Button
|
||||
{
|
||||
Text = label,
|
||||
ToggleMode = true,
|
||||
Pressed = defaultValue,
|
||||
};
|
||||
|
||||
button.OnToggled += _ => OnColorsChanged();
|
||||
|
||||
_toggleContainer.AddChild(button);
|
||||
|
||||
BindToggle(button, onValueChanged);
|
||||
}
|
||||
|
||||
private void BindToggle(Button toggle, Action<bool> setValue)
|
||||
{
|
||||
toggle.OnToggled += val =>
|
||||
{
|
||||
setValue(val.Pressed);
|
||||
OnColorsChanged();
|
||||
};
|
||||
}
|
||||
|
||||
private bool IsSpinBoxValid(int value, float min, float max)
|
||||
{
|
||||
return (value >= min) && (value <= max);
|
||||
}
|
||||
|
||||
private void OnColorsChanged()
|
||||
{
|
||||
foreach (var (key, selector) in _colorSelectors)
|
||||
{
|
||||
Effect.Colors[key] = selector.Color;
|
||||
}
|
||||
|
||||
OnColorChanged?.Invoke(Effect);
|
||||
}
|
||||
|
||||
private void Populate(MarkingEffectType type, MarkingEffect? defaultEffect = null)
|
||||
{
|
||||
_colorSelectors.Clear();
|
||||
_selectorsContainer.DisposeAllChildren();
|
||||
_slidersContainer.DisposeAllChildren();
|
||||
_toggleContainer.DisposeAllChildren();
|
||||
|
||||
defaultEffect ??= type switch
|
||||
{
|
||||
MarkingEffectType.Color => ColorMarkingEffect.White,
|
||||
MarkingEffectType.Gradient => new GradientMarkingEffect(),
|
||||
MarkingEffectType.RoughGradient => new RoughGradientMarkingEffect(),
|
||||
_ => ColorMarkingEffect.White,
|
||||
};
|
||||
|
||||
Effect = defaultEffect;
|
||||
|
||||
if (UiBuilders.TryGetValue(type, out var builder))
|
||||
builder.BuildUI(Effect, this);
|
||||
else
|
||||
Logger.Warning($"No UI builder for marking effect: {type}");
|
||||
}
|
||||
}
|
||||
|
||||
2193
Content.Server.Database/Migrations/Postgres/20250724212329_Gradient.Designer.cs
generated
Normal file
2193
Content.Server.Database/Migrations/Postgres/20250724212329_Gradient.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,62 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Content.Server.Database.Migrations.Postgres
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Gradient : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "facial_hair_color_type",
|
||||
table: "profile",
|
||||
type: "integer",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "facial_hair_extended_color",
|
||||
table: "profile",
|
||||
type: "text",
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "hair_color_type",
|
||||
table: "profile",
|
||||
type: "integer",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "hair_extended_color",
|
||||
table: "profile",
|
||||
type: "text",
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "facial_hair_color_type",
|
||||
table: "profile");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "facial_hair_extended_color",
|
||||
table: "profile");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "hair_color_type",
|
||||
table: "profile");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "hair_extended_color",
|
||||
table: "profile");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -895,6 +895,15 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||
.HasColumnType("text")
|
||||
.HasColumnName("facial_hair_color");
|
||||
|
||||
b.Property<int>("FacialHairColorType")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("facial_hair_color_type");
|
||||
|
||||
b.Property<string>("FacialHairExtendedColor")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("facial_hair_extended_color");
|
||||
|
||||
b.Property<string>("FacialHairName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
|
|
@ -915,6 +924,15 @@ namespace Content.Server.Database.Migrations.Postgres
|
|||
.HasColumnType("text")
|
||||
.HasColumnName("hair_color");
|
||||
|
||||
b.Property<int>("HairColorType")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("hair_color_type");
|
||||
|
||||
b.Property<string>("HairExtendedColor")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("hair_extended_color");
|
||||
|
||||
b.Property<string>("HairName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
|
|
|
|||
2114
Content.Server.Database/Migrations/Sqlite/20250724212307_Gradient.Designer.cs
generated
Normal file
2114
Content.Server.Database/Migrations/Sqlite/20250724212307_Gradient.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,62 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Content.Server.Database.Migrations.Sqlite
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Gradient : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "facial_hair_color_type",
|
||||
table: "profile",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "facial_hair_extended_color",
|
||||
table: "profile",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "hair_color_type",
|
||||
table: "profile",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "hair_extended_color",
|
||||
table: "profile",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "facial_hair_color_type",
|
||||
table: "profile");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "facial_hair_extended_color",
|
||||
table: "profile");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "hair_color_type",
|
||||
table: "profile");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "hair_extended_color",
|
||||
table: "profile");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -844,6 +844,15 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||
.HasColumnType("TEXT")
|
||||
.HasColumnName("facial_hair_color");
|
||||
|
||||
b.Property<int>("FacialHairColorType")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("facial_hair_color_type");
|
||||
|
||||
b.Property<string>("FacialHairExtendedColor")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("facial_hair_extended_color");
|
||||
|
||||
b.Property<string>("FacialHairName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
|
|
@ -864,6 +873,15 @@ namespace Content.Server.Database.Migrations.Sqlite
|
|||
.HasColumnType("TEXT")
|
||||
.HasColumnName("hair_color");
|
||||
|
||||
b.Property<int>("HairColorType")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("hair_color_type");
|
||||
|
||||
b.Property<string>("HairExtendedColor")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("hair_extended_color");
|
||||
|
||||
b.Property<string>("HairName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
|
|
|
|||
|
|
@ -420,6 +420,12 @@ namespace Content.Server.Database
|
|||
public string HairColor { get; set; } = null!;
|
||||
public string FacialHairName { get; set; } = null!;
|
||||
public string FacialHairColor { get; set; } = null!;
|
||||
// sunrise gradient start
|
||||
public int HairColorType { get; set; } = 0;
|
||||
public string HairExtendedColor { get; set; } = null!;
|
||||
public int FacialHairColorType { get; set; } = 0;
|
||||
public string FacialHairExtendedColor { get; set; } = null!;
|
||||
// sunrise gradient end
|
||||
public string EyeColor { get; set; } = null!;
|
||||
public string SkinColor { get; set; } = null!;
|
||||
public int SpawnPriority { get; set; } = 0;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Administration.Managers;
|
||||
using Content.Shared._Sunrise.MarkingEffects;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Construction.Prototypes;
|
||||
using Content.Shared.Database;
|
||||
|
|
@ -283,6 +284,12 @@ namespace Content.Server.Database
|
|||
Color.FromHex(profile.EyeColor),
|
||||
Color.FromHex(profile.SkinColor),
|
||||
markings,
|
||||
//sunrise gradient start
|
||||
(MarkingEffectType)profile.HairColorType,
|
||||
MarkingEffect.Parse(profile.HairExtendedColor),
|
||||
(MarkingEffectType)profile.FacialHairColorType,
|
||||
MarkingEffect.Parse(profile.FacialHairExtendedColor),
|
||||
//sunrise gradient end
|
||||
profile.Width,
|
||||
profile.Height
|
||||
),
|
||||
|
|
@ -320,6 +327,12 @@ namespace Content.Server.Database
|
|||
profile.HairColor = appearance.HairColor.ToHex();
|
||||
profile.FacialHairName = appearance.FacialHairStyleId;
|
||||
profile.FacialHairColor = appearance.FacialHairColor.ToHex();
|
||||
// sunrise gradient start
|
||||
profile.HairColorType = (int)appearance.HairMarkingEffectType;
|
||||
profile.HairExtendedColor = appearance.HairMarkingEffect?.ToString() ?? "";
|
||||
profile.FacialHairColorType = (int)appearance.FacialHairMarkingEffectType;
|
||||
profile.FacialHairExtendedColor = appearance.FacialHairMarkingEffect?.ToString() ?? "";
|
||||
// sunrise gradient end
|
||||
profile.EyeColor = appearance.EyeColor.ToHex();
|
||||
profile.SkinColor = appearance.SkinColor.ToHex();
|
||||
profile.SpawnPriority = (int) humanoid.SpawnPriority;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Linq;
|
||||
using Content.Shared._Sunrise.MarkingEffects;
|
||||
using System.Numerics;
|
||||
using Content.Shared.Humanoid.Markings;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
|
|
@ -24,6 +25,22 @@ public sealed partial class HumanoidCharacterAppearance : ICharacterAppearance,
|
|||
[DataField]
|
||||
public Color FacialHairColor { get; set; } = Color.Black;
|
||||
|
||||
// sunrise gradient edit start
|
||||
|
||||
[DataField]
|
||||
public MarkingEffectType HairMarkingEffectType { get; set; } = MarkingEffectType.Color;
|
||||
|
||||
[DataField]
|
||||
public MarkingEffect? HairMarkingEffect { get; set; }
|
||||
|
||||
[DataField]
|
||||
public MarkingEffectType FacialHairMarkingEffectType { get; set; } = MarkingEffectType.Color;
|
||||
|
||||
[DataField]
|
||||
public MarkingEffect? FacialHairMarkingEffect { get; set; }
|
||||
|
||||
// sunrise gradient edit end
|
||||
|
||||
[DataField]
|
||||
public Color EyeColor { get; set; } = Color.Black;
|
||||
|
||||
|
|
@ -46,6 +63,12 @@ public sealed partial class HumanoidCharacterAppearance : ICharacterAppearance,
|
|||
Color eyeColor,
|
||||
Color skinColor,
|
||||
List<Marking> markings,
|
||||
//sunrise gradient start
|
||||
MarkingEffectType hairMarkingEffectType = MarkingEffectType.Color,
|
||||
MarkingEffect? hairMarkingEffect = null,
|
||||
MarkingEffectType facialHairMarkingEffectType = MarkingEffectType.Color,
|
||||
MarkingEffect? facialHairMarkingEffect = null,
|
||||
//sunrise gradient end
|
||||
float width, //Sunrise
|
||||
float height) //Sunrise
|
||||
{
|
||||
|
|
@ -56,10 +79,19 @@ public sealed partial class HumanoidCharacterAppearance : ICharacterAppearance,
|
|||
EyeColor = ClampColor(eyeColor);
|
||||
SkinColor = ClampColor(skinColor);
|
||||
Markings = markings;
|
||||
//sunrise gradient start
|
||||
HairMarkingEffectType = hairMarkingEffectType;
|
||||
HairMarkingEffect = hairMarkingEffect;
|
||||
FacialHairMarkingEffectType = facialHairMarkingEffectType;
|
||||
FacialHairMarkingEffect = facialHairMarkingEffect;
|
||||
Width = width; //Sunrise
|
||||
Height = height; //Sunrise
|
||||
//sunrise gradient end
|
||||
}
|
||||
|
||||
public HumanoidCharacterAppearance(HumanoidCharacterAppearance other) :
|
||||
this(other.HairStyleId, other.HairColor, other.FacialHairStyleId, other.FacialHairColor, other.EyeColor, other.SkinColor, new(other.Markings), other.HairMarkingEffectType, other.HairMarkingEffect, other.FacialHairMarkingEffectType, other.FacialHairMarkingEffect, other.Width, other.Height); // sunrise gradient edit
|
||||
|
||||
public HumanoidCharacterAppearance(HumanoidCharacterAppearance other) :
|
||||
this(other.HairStyleId, other.HairColor, other.FacialHairStyleId, other.FacialHairColor, other.EyeColor, other.SkinColor, new(other.Markings), other.Width, other.Height)
|
||||
{
|
||||
|
|
@ -68,50 +100,49 @@ public sealed partial class HumanoidCharacterAppearance : ICharacterAppearance,
|
|||
|
||||
public HumanoidCharacterAppearance WithHairStyleName(string newName)
|
||||
{
|
||||
return new(newName, HairColor, FacialHairStyleId, FacialHairColor, EyeColor, SkinColor, Markings, Width, Height);
|
||||
return new(newName, HairColor, FacialHairStyleId, FacialHairColor, EyeColor, SkinColor, Markings, HairMarkingEffectType, HairMarkingEffect, FacialHairMarkingEffectType, FacialHairMarkingEffect, Width, Height); // sunrise gradient edit
|
||||
}
|
||||
|
||||
public HumanoidCharacterAppearance WithHairColor(Color newColor)
|
||||
public HumanoidCharacterAppearance WithHairColor(Color newColor, MarkingEffect? newExtendedColor = null)
|
||||
{
|
||||
return new(HairStyleId, newColor, FacialHairStyleId, FacialHairColor, EyeColor, SkinColor, Markings, Width, Height);
|
||||
return new(HairStyleId, newColor, FacialHairStyleId, FacialHairColor, EyeColor, SkinColor, Markings, newExtendedColor?.Type ?? HairMarkingEffectType, newExtendedColor ?? HairMarkingEffect, FacialHairMarkingEffectType, FacialHairMarkingEffect, Width, Height); // sunrise gradient edit
|
||||
}
|
||||
|
||||
public HumanoidCharacterAppearance WithFacialHairStyleName(string newName)
|
||||
{
|
||||
return new(HairStyleId, HairColor, newName, FacialHairColor, EyeColor, SkinColor, Markings, Width, Height);
|
||||
return new(HairStyleId, HairColor, newName, FacialHairColor, EyeColor, SkinColor, Markings, HairMarkingEffectType, HairMarkingEffect, FacialHairMarkingEffectType, FacialHairMarkingEffect, Width, Height);
|
||||
}
|
||||
|
||||
public HumanoidCharacterAppearance WithFacialHairColor(Color newColor)
|
||||
public HumanoidCharacterAppearance WithFacialHairColor(Color newColor, MarkingEffect? newFacialExtendedColor = null)
|
||||
{
|
||||
return new(HairStyleId, HairColor, FacialHairStyleId, newColor, EyeColor, SkinColor, Markings, Width, Height);
|
||||
return new(HairStyleId, HairColor, FacialHairStyleId, newColor, EyeColor, SkinColor, Markings, HairMarkingEffectType, HairMarkingEffect, newFacialExtendedColor?.Type ?? FacialHairMarkingEffectType, newFacialExtendedColor ?? FacialHairMarkingEffect, Width, Height); // sunrise gradient edit
|
||||
}
|
||||
|
||||
public HumanoidCharacterAppearance WithEyeColor(Color newColor)
|
||||
{
|
||||
return new(HairStyleId, HairColor, FacialHairStyleId, FacialHairColor, newColor, SkinColor, Markings, Width, Height);
|
||||
return new(HairStyleId, HairColor, FacialHairStyleId, FacialHairColor, newColor, SkinColor, Markings, HairMarkingEffectType, HairMarkingEffect, FacialHairMarkingEffectType, FacialHairMarkingEffect, Width, Height); // sunrise gradient edit
|
||||
}
|
||||
|
||||
public HumanoidCharacterAppearance WithSkinColor(Color newColor)
|
||||
{
|
||||
return new(HairStyleId, HairColor, FacialHairStyleId, FacialHairColor, EyeColor, newColor, Markings, Width, Height);
|
||||
return new(HairStyleId, HairColor, FacialHairStyleId, FacialHairColor, EyeColor, newColor, Markings, HairMarkingEffectType, HairMarkingEffect, FacialHairMarkingEffectType, FacialHairMarkingEffect, Width, Height); // sunrise gradient edit
|
||||
}
|
||||
|
||||
public HumanoidCharacterAppearance WithMarkings(List<Marking> newMarkings)
|
||||
{
|
||||
return new(HairStyleId, HairColor, FacialHairStyleId, FacialHairColor, EyeColor, SkinColor, newMarkings, Width, Height);
|
||||
return new(HairStyleId, HairColor, FacialHairStyleId, FacialHairColor, EyeColor, SkinColor, newMarkings, HairMarkingEffectType, HairMarkingEffect, FacialHairMarkingEffectType, FacialHairMarkingEffect, Width, Height); // sunrise gradient edit
|
||||
}
|
||||
|
||||
//Sunrise start
|
||||
public HumanoidCharacterAppearance WithWidth(float newWidth)
|
||||
// sunrise gradient edit start
|
||||
public HumanoidCharacterAppearance WithHairExtendedColor(MarkingEffect? newExtendedColor)
|
||||
{
|
||||
return new(HairStyleId, HairColor, FacialHairStyleId, FacialHairColor, EyeColor, SkinColor, Markings, newWidth, Height);
|
||||
return new(HairStyleId, HairColor, FacialHairStyleId, FacialHairColor, EyeColor, SkinColor, Markings, newExtendedColor?.Type ?? MarkingEffectType.Color, newExtendedColor, FacialHairMarkingEffectType, FacialHairMarkingEffect, Width, Height); // sunrise gradient edit
|
||||
}
|
||||
|
||||
public HumanoidCharacterAppearance WithHeight(float newHeight)
|
||||
public HumanoidCharacterAppearance WithFacialHairExtendedColor(MarkingEffect? newFacialExtendedColor)
|
||||
{
|
||||
return new(HairStyleId, HairColor, FacialHairStyleId, FacialHairColor, EyeColor, SkinColor, Markings, Width, newHeight);
|
||||
return new(HairStyleId, HairColor, FacialHairStyleId, FacialHairColor, EyeColor, SkinColor, Markings, HairMarkingEffectType, HairMarkingEffect, newFacialExtendedColor?.Type ?? MarkingEffectType.Color, newFacialExtendedColor, Width, Height); // sunrise gradient edit
|
||||
}
|
||||
//Sunrise end
|
||||
// sunrise gradient edit end
|
||||
|
||||
public static HumanoidCharacterAppearance DefaultWithSpecies(string species)
|
||||
{
|
||||
|
|
@ -135,6 +166,12 @@ public sealed partial class HumanoidCharacterAppearance : ICharacterAppearance,
|
|||
Color.Black,
|
||||
skinColor,
|
||||
new (),
|
||||
// sunrise gradient edit start
|
||||
MarkingEffectType.Color,
|
||||
null,
|
||||
MarkingEffectType.Color,
|
||||
null,
|
||||
// sunrise gradient edit end
|
||||
speciesPrototype.DefaultWidth, //Sunrise
|
||||
speciesPrototype.DefaultHeight //Sunrise
|
||||
);
|
||||
|
|
@ -315,7 +352,7 @@ public sealed partial class HumanoidCharacterAppearance : ICharacterAppearance,
|
|||
//Sunrise end
|
||||
|
||||
// at the end of all that, we should have new values for each of these, so we set the character appearance to these new values.
|
||||
return new HumanoidCharacterAppearance(newHairStyle, newHairColor, newFacialHairStyle, newHairColor, newEyeColor, newSkinColor, newMarkings, newWidth, newHeight); // Sunrise edit
|
||||
return new HumanoidCharacterAppearance(newHairStyle, newHairColor, newFacialHairStyle, newHairColor, newEyeColor, newSkinColor, newMarkings, MarkingEffectType.Color, null, MarkingEffectType.Color, null, newWidth, newHeight);
|
||||
|
||||
// helper functions:
|
||||
float RandomizeColor(float channel)
|
||||
|
|
@ -446,6 +483,24 @@ public sealed partial class HumanoidCharacterAppearance : ICharacterAppearance,
|
|||
markingSet.FilterSponsor(sponsorPrototypes, markingManager); // Sunrise-Sponsors
|
||||
}
|
||||
|
||||
// sunrise gradient start
|
||||
MarkingEffect? hairExtendedColor = null;
|
||||
if (appearance.HairMarkingEffect != null)
|
||||
{
|
||||
hairExtendedColor = appearance.HairMarkingEffect;
|
||||
foreach (var (key, value) in hairExtendedColor.Colors)
|
||||
hairExtendedColor.Colors[key] = ClampColor(value);
|
||||
}
|
||||
|
||||
MarkingEffect? facialHairExtendedColor = null;
|
||||
if (appearance.FacialHairMarkingEffect != null)
|
||||
{
|
||||
facialHairExtendedColor = appearance.FacialHairMarkingEffect;
|
||||
foreach (var (key, value) in facialHairExtendedColor.Colors)
|
||||
facialHairExtendedColor.Colors[key] = ClampColor(value);
|
||||
}
|
||||
// sunrise gradient end
|
||||
|
||||
return new HumanoidCharacterAppearance(
|
||||
hairStyleId,
|
||||
hairColor,
|
||||
|
|
@ -454,10 +509,13 @@ public sealed partial class HumanoidCharacterAppearance : ICharacterAppearance,
|
|||
eyeColor,
|
||||
skinColor,
|
||||
markingSet.GetForwardEnumerator().ToList(),
|
||||
appearance.HairMarkingEffectType,
|
||||
hairExtendedColor,
|
||||
appearance.FacialHairMarkingEffectType,
|
||||
facialHairExtendedColor,
|
||||
width,
|
||||
height);
|
||||
}
|
||||
|
||||
public bool MemberwiseEquals(ICharacterAppearance maybeOther)
|
||||
{
|
||||
if (maybeOther is not HumanoidCharacterAppearance other) return false;
|
||||
|
|
@ -468,6 +526,12 @@ public sealed partial class HumanoidCharacterAppearance : ICharacterAppearance,
|
|||
if (!EyeColor.Equals(other.EyeColor)) return false;
|
||||
if (!SkinColor.Equals(other.SkinColor)) return false;
|
||||
if (!Markings.SequenceEqual(other.Markings)) return false;
|
||||
// sunrise gradient edit start
|
||||
if (!HairMarkingEffectType.Equals(other.HairMarkingEffectType)) return false;
|
||||
if (!Equals(HairMarkingEffect, other.HairMarkingEffect)) return false;
|
||||
if (!FacialHairMarkingEffectType.Equals(other.FacialHairMarkingEffectType)) return false;
|
||||
if (!Equals(FacialHairMarkingEffect, other.FacialHairMarkingEffect)) return false;
|
||||
// sunrise gradient edit end
|
||||
if (Width != other.Width) return false; //Sunrise
|
||||
if (Height != other.Height) return false; //Sunrise
|
||||
return true;
|
||||
|
|
@ -484,6 +548,11 @@ public sealed partial class HumanoidCharacterAppearance : ICharacterAppearance,
|
|||
EyeColor.Equals(other.EyeColor) &&
|
||||
SkinColor.Equals(other.SkinColor) &&
|
||||
Markings.SequenceEqual(other.Markings) &&
|
||||
// sunrise gradient edit start
|
||||
HairMarkingEffectType.Equals(other.HairMarkingEffectType) &&
|
||||
Equals(HairMarkingEffect, other.HairMarkingEffect) &&
|
||||
FacialHairMarkingEffectType.Equals(other.FacialHairMarkingEffectType) &&
|
||||
Equals(FacialHairMarkingEffect, other.FacialHairMarkingEffect) &&
|
||||
Width == other.Width && //starlight
|
||||
Height == other.Height;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Linq;
|
||||
using Content.Shared._Sunrise.MarkingEffects;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
|
|
@ -12,20 +13,32 @@ namespace Content.Shared.Humanoid.Markings
|
|||
[DataField("markingColor")]
|
||||
private List<Color> _markingColors = new();
|
||||
|
||||
// sunrise gradient edit start
|
||||
[DataField("markingEffects")]
|
||||
public List<MarkingEffect> MarkingEffects = new();
|
||||
// sunrise gradient edit end
|
||||
|
||||
|
||||
private Marking()
|
||||
{
|
||||
}
|
||||
|
||||
public Marking(string markingId,
|
||||
List<Color> markingColors)
|
||||
List<Color> markingColors,
|
||||
List<MarkingEffect>? markingEffects = null)
|
||||
{
|
||||
MarkingId = markingId;
|
||||
_markingColors = markingColors;
|
||||
MarkingEffects = markingEffects ?? new(); // sunrise gradient edit
|
||||
}
|
||||
|
||||
public Marking(string markingId,
|
||||
IReadOnlyList<Color> markingColors)
|
||||
: this(markingId, new List<Color>(markingColors))
|
||||
IReadOnlyList<Color> markingColors,
|
||||
IReadOnlyList<MarkingEffect>? markingEffects = null)
|
||||
: this(
|
||||
markingId,
|
||||
new List<Color>(markingColors),
|
||||
markingEffects is not null ? new List<MarkingEffect>(markingEffects) : new List<MarkingEffect>())
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -34,7 +47,11 @@ namespace Content.Shared.Humanoid.Markings
|
|||
MarkingId = markingId;
|
||||
List<Color> colors = new();
|
||||
for (int i = 0; i < colorCount; i++)
|
||||
{
|
||||
colors.Add(Color.White);
|
||||
MarkingEffects.Add(ColorMarkingEffect.White);
|
||||
}
|
||||
|
||||
_markingColors = colors;
|
||||
}
|
||||
|
||||
|
|
@ -42,6 +59,7 @@ namespace Content.Shared.Humanoid.Markings
|
|||
{
|
||||
MarkingId = other.MarkingId;
|
||||
_markingColors = new(other.MarkingColors);
|
||||
MarkingEffects = other.MarkingEffects.Select(e => e.Clone()).ToList();
|
||||
Visible = other.Visible;
|
||||
Forced = other.Forced;
|
||||
}
|
||||
|
|
@ -81,6 +99,17 @@ namespace Content.Shared.Humanoid.Markings
|
|||
}
|
||||
}
|
||||
|
||||
public void SetMarkingEffect(int colorIndex, MarkingEffect effect) =>
|
||||
MarkingEffects[colorIndex] = effect;
|
||||
|
||||
public void SetMarkingEffect(MarkingEffect effect)
|
||||
{
|
||||
for (int i = 0; i < MarkingEffects.Count; i++)
|
||||
{
|
||||
MarkingEffects[i] = effect;
|
||||
}
|
||||
}
|
||||
|
||||
public int CompareTo(Marking? marking)
|
||||
{
|
||||
if (marking == null)
|
||||
|
|
@ -122,27 +151,53 @@ namespace Content.Shared.Humanoid.Markings
|
|||
// doesn't seem to have compatible interfaces? this 'works'
|
||||
// for now but should eventually be improved so that this can,
|
||||
// in fact just be serialized through a convenient interface
|
||||
new public string ToString()
|
||||
public new string ToString()
|
||||
{
|
||||
// reserved character
|
||||
string sanitizedName = this.MarkingId.Replace('@', '_');
|
||||
List<string> colorStringList = new();
|
||||
foreach (Color color in _markingColors)
|
||||
colorStringList.Add(color.ToHex());
|
||||
|
||||
return $"{sanitizedName}@{String.Join(',', colorStringList)}";
|
||||
var colorStringList = _markingColors.Select(c => c.ToHex()).ToList();
|
||||
if (MarkingEffects == null || MarkingEffects.Count == 0)
|
||||
return $"{sanitizedName}@{string.Join(',', colorStringList)}";
|
||||
|
||||
var extColorsList = MarkingEffects.Select(ext => ext.ToString());
|
||||
|
||||
var extColorsString = string.Join(";", extColorsList);
|
||||
return $"{sanitizedName}@{string.Join(',', colorStringList)}@{extColorsString}";
|
||||
}
|
||||
|
||||
public static Marking? ParseFromDbString(string input)
|
||||
{
|
||||
if (input.Length == 0) return null;
|
||||
var split = input.Split('@');
|
||||
if (split.Length != 2) return null;
|
||||
List<Color> colorList = new();
|
||||
foreach (string color in split[1].Split(','))
|
||||
colorList.Add(Color.FromHex(color));
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
return null;
|
||||
|
||||
return new Marking(split[0], colorList);
|
||||
var split = input.Split('@');
|
||||
if (split.Length < 2)
|
||||
return null;
|
||||
|
||||
var name = split[0];
|
||||
var colorsRaw = split[1];
|
||||
|
||||
var colorList = new List<Color>();
|
||||
foreach (var colorHex in colorsRaw.Split(','))
|
||||
{
|
||||
colorList.Add(Color.FromHex(colorHex));
|
||||
}
|
||||
|
||||
if (split.Length == 2)
|
||||
return new Marking(name, colorList);
|
||||
|
||||
var extColorsRaw = split[2];
|
||||
var markingEffects = new List<MarkingEffect>();
|
||||
|
||||
foreach (var extColorStr in extColorsRaw.Split(';'))
|
||||
{
|
||||
var parsed = MarkingEffect.Parse(extColorStr);
|
||||
if (parsed != null)
|
||||
markingEffects.Add(parsed);
|
||||
}
|
||||
|
||||
return new Marking(name, colorList, markingEffects);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using System.Linq;
|
|||
using Content.Shared._Sunrise;
|
||||
using Content.Shared._Sunrise.TTS;
|
||||
using System.Numerics;
|
||||
using Content.Shared._Sunrise.MarkingEffects;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Decals;
|
||||
using Content.Shared.Examine;
|
||||
|
|
@ -456,13 +457,13 @@ public abstract class SharedHumanoidAppearanceSystem : EntitySystem
|
|||
if (_markingManager.Markings.TryGetValue(profile.Appearance.HairStyleId, out var hairPrototype) &&
|
||||
_markingManager.CanBeApplied(profile.Species, profile.Sex, hairPrototype, _proto))
|
||||
{
|
||||
AddMarking(uid, profile.Appearance.HairStyleId, hairColor, false);
|
||||
AddMarking(uid, profile.Appearance.HairStyleId, hairColor, false, markingEffect: profile.Appearance.HairMarkingEffect);
|
||||
}
|
||||
|
||||
if (_markingManager.Markings.TryGetValue(profile.Appearance.FacialHairStyleId, out var facialHairPrototype) &&
|
||||
_markingManager.CanBeApplied(profile.Species, profile.Sex, facialHairPrototype, _proto))
|
||||
{
|
||||
AddMarking(uid, profile.Appearance.FacialHairStyleId, facialHairColor, false);
|
||||
AddMarking(uid, profile.Appearance.FacialHairStyleId, facialHairColor, false, markingEffect: profile.Appearance.FacialHairMarkingEffect);
|
||||
}
|
||||
|
||||
humanoid.MarkingSet.EnsureSpecies(profile.Species, profile.Appearance.SkinColor, _markingManager, _proto);
|
||||
|
|
@ -506,7 +507,7 @@ public abstract class SharedHumanoidAppearanceSystem : EntitySystem
|
|||
/// <param name="sync">Whether to immediately sync this marking or not</param>
|
||||
/// <param name="forced">If this marking was forced (ignores marking points)</param>
|
||||
/// <param name="humanoid">Humanoid component of the entity</param>
|
||||
public void AddMarking(EntityUid uid, string marking, Color? color = null, bool sync = true, bool forced = false, HumanoidAppearanceComponent? humanoid = null)
|
||||
public void AddMarking(EntityUid uid, string marking, Color? color = null, bool sync = true, bool forced = false, HumanoidAppearanceComponent? humanoid = null, MarkingEffect? markingEffect = null)
|
||||
{
|
||||
if (!Resolve(uid, ref humanoid)
|
||||
|| !_markingManager.Markings.TryGetValue(marking, out var prototype))
|
||||
|
|
@ -521,6 +522,8 @@ public abstract class SharedHumanoidAppearanceSystem : EntitySystem
|
|||
for (var i = 0; i < prototype.Sprites.Count; i++)
|
||||
{
|
||||
markingObject.SetColor(i, color.Value);
|
||||
if(markingEffect != null)
|
||||
markingObject.SetMarkingEffect(i, markingEffect);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
62
Content.Shared/_Sunrise/MarkingEffects/ColorMarkingEffect.cs
Normal file
62
Content.Shared/_Sunrise/MarkingEffects/ColorMarkingEffect.cs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
using System.Linq;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._Sunrise.MarkingEffects;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class ColorMarkingEffect : MarkingEffect
|
||||
{
|
||||
public override MarkingEffectType Type => MarkingEffectType.Color;
|
||||
|
||||
public Color GetColor()
|
||||
=> Colors.TryGetValue("base", out var col) ? col : Color.White;
|
||||
|
||||
#region Constructors
|
||||
|
||||
public ColorMarkingEffect(Color color) : base(color) { }
|
||||
public static ColorMarkingEffect White => new(Color.White);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Parsers
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
Dictionary<string, string> dict = new();
|
||||
|
||||
var color = GetColor();
|
||||
dict.Add($"color.base", color.ToHex());
|
||||
|
||||
var result = string.Join(",", dict.Select(kvp => $"{kvp.Key}={kvp.Value}"));
|
||||
return $"{Type.ToString()}=={result}";
|
||||
}
|
||||
|
||||
public static ColorMarkingEffect? Parse(Dictionary<string, string> dict)
|
||||
{
|
||||
var color = Color.White;
|
||||
|
||||
foreach (var (type, value) in dict)
|
||||
{
|
||||
if (type == "color.base")
|
||||
color = Color.TryFromHex(value) ?? Color.White;
|
||||
}
|
||||
|
||||
return new ColorMarkingEffect(color);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Other methods
|
||||
public override ColorMarkingEffect Clone()
|
||||
{
|
||||
return new ColorMarkingEffect(Colors["base"]);
|
||||
}
|
||||
|
||||
public override bool Equals(MarkingEffect? maybeOther)
|
||||
{
|
||||
if (maybeOther is not ColorMarkingEffect other)
|
||||
return false;
|
||||
|
||||
return DictionaryEquals(Colors, other.Colors);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
139
Content.Shared/_Sunrise/MarkingEffects/GradientMarkingEffect.cs
Normal file
139
Content.Shared/_Sunrise/MarkingEffects/GradientMarkingEffect.cs
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._Sunrise.MarkingEffects;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class GradientMarkingEffect : MarkingEffect
|
||||
{
|
||||
public override MarkingEffectType Type => MarkingEffectType.Gradient;
|
||||
|
||||
public Vector2 Offset = new(0, -190/100f);
|
||||
public Vector2 Size = new(1, 33/100f);
|
||||
public float Rotation = 0;
|
||||
public float Speed = 1;
|
||||
public bool Pixelated = true;
|
||||
public bool Mirrored = false;
|
||||
|
||||
#region Parsing
|
||||
public override string ToString()
|
||||
{
|
||||
var dict = new Dictionary<string, string>();
|
||||
|
||||
dict.Add("offset", ParamToString(Offset));
|
||||
dict.Add("size", ParamToString(Size));
|
||||
dict.Add("rotation", ParamToString(Rotation));
|
||||
dict.Add("speed", ParamToString(Speed));
|
||||
dict.Add("pixelated", ParamToString(Pixelated));
|
||||
dict.Add("mirrored", ParamToString(Mirrored));
|
||||
|
||||
foreach (var (k, v) in Colors)
|
||||
dict.Add($"color.{k}", $"{v.ToHex()}");
|
||||
|
||||
var result = string.Join(",", dict.Select(kvp => $"{kvp.Key}={kvp.Value}"));
|
||||
return $"{Type.ToString()}=={result}";
|
||||
}
|
||||
|
||||
public static GradientMarkingEffect? Parse(Dictionary<string, string> dict)
|
||||
{
|
||||
var colors = new Dictionary<string, Color>();
|
||||
|
||||
var offset = new Vector2(0, -190/100f);
|
||||
var size = new Vector2(1, 33/100f);
|
||||
var rotation = 0f;
|
||||
var speed = 1f;
|
||||
var pixelated = true;
|
||||
var mirrored = false;
|
||||
|
||||
foreach (var (type, value) in dict)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case "offset":
|
||||
TryParseParam(value, out offset);
|
||||
break;
|
||||
case "size":
|
||||
TryParseParam(value, out size);
|
||||
break;
|
||||
case "rotation":
|
||||
TryParseParam(value, out rotation);
|
||||
break;
|
||||
case "speed":
|
||||
TryParseParam(value, out speed);
|
||||
break;
|
||||
case "pixelated":
|
||||
TryParseParam(value, out pixelated);
|
||||
break;
|
||||
case "mirrored":
|
||||
TryParseParam(value, out mirrored);
|
||||
break;
|
||||
default:
|
||||
{
|
||||
if (type.StartsWith("color."))
|
||||
colors[type["color.".Length..]] = Color.TryFromHex(value) ?? Color.White;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new GradientMarkingEffect(colors, offset, size, rotation, speed, pixelated, mirrored);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public GradientMarkingEffect()
|
||||
{
|
||||
Colors = new Dictionary<string, Color>()
|
||||
{
|
||||
{ "base", Color.White }
|
||||
};
|
||||
}
|
||||
|
||||
public GradientMarkingEffect(Color color)
|
||||
{
|
||||
Colors = new Dictionary<string, Color>
|
||||
{
|
||||
{"base", color }
|
||||
};
|
||||
}
|
||||
|
||||
public GradientMarkingEffect(Dictionary<string, Color> colors,
|
||||
Vector2 offset,
|
||||
Vector2 size,
|
||||
float rotation,
|
||||
float speed,
|
||||
bool pixelated,
|
||||
bool mirrored)
|
||||
{
|
||||
Colors = colors;
|
||||
Offset = offset;
|
||||
Size = size;
|
||||
Rotation = rotation;
|
||||
Speed = speed;
|
||||
Pixelated = pixelated;
|
||||
Mirrored = mirrored;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Other methods
|
||||
public override GradientMarkingEffect Clone()
|
||||
{
|
||||
return new GradientMarkingEffect(new(Colors), new(Offset.X, Offset.Y), new(Size.X, Size.Y), Rotation, Speed, Pixelated, Mirrored);
|
||||
}
|
||||
|
||||
public override bool Equals(MarkingEffect? maybeOther)
|
||||
{
|
||||
if (maybeOther is not GradientMarkingEffect other)
|
||||
return false;
|
||||
|
||||
return DictionaryEquals(Colors, other.Colors)
|
||||
&& Offset.Equals(other.Offset)
|
||||
&& Size.Equals(other.Size)
|
||||
&& Rotation.Equals(other.Rotation)
|
||||
&& Speed.Equals(other.Speed)
|
||||
&& Pixelated.Equals(other.Pixelated)
|
||||
&& Mirrored.Equals(other.Mirrored);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
150
Content.Shared/_Sunrise/MarkingEffects/MarkingEffect.cs
Normal file
150
Content.Shared/_Sunrise/MarkingEffects/MarkingEffect.cs
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._Sunrise.MarkingEffects;
|
||||
|
||||
[ImplicitDataDefinitionForInheritors, Serializable, NetSerializable]
|
||||
public abstract partial class MarkingEffect
|
||||
{
|
||||
public abstract MarkingEffectType Type { get; }
|
||||
public Dictionary<string, Color> Colors;
|
||||
|
||||
public abstract override string ToString();
|
||||
public abstract MarkingEffect Clone();
|
||||
public abstract bool Equals(MarkingEffect? other);
|
||||
|
||||
#region Constructors
|
||||
|
||||
protected MarkingEffect()
|
||||
{
|
||||
Colors = new Dictionary<string, Color>
|
||||
{
|
||||
{ "base", Color.White }
|
||||
};
|
||||
}
|
||||
|
||||
protected MarkingEffect(Color color)
|
||||
{
|
||||
Colors = new Dictionary<string, Color>
|
||||
{
|
||||
{ "base", color }
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Parsers
|
||||
|
||||
protected static Dictionary<string, string>? ParseToDict(string input)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input))
|
||||
return null;
|
||||
|
||||
var spl = input.Split("==");
|
||||
if (spl.Length > 1)
|
||||
input = spl[1];
|
||||
|
||||
var lines = input.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
|
||||
|
||||
if (lines.Length == 0)
|
||||
return null;
|
||||
|
||||
return lines
|
||||
.Select(line => line.Split('=', 2))
|
||||
.Where(parts => parts.Length == 2 && !string.IsNullOrWhiteSpace(parts[0]) && !string.IsNullOrWhiteSpace(parts[1]))
|
||||
.ToDictionary(parts => parts[0], parts => parts[1]);
|
||||
}
|
||||
|
||||
public static MarkingEffect? Parse(string input)
|
||||
{
|
||||
var pair = input.Split("==");
|
||||
|
||||
if (pair.Length != 2 || !Enum.TryParse<MarkingEffectType>(pair[0], true, out var type))
|
||||
return null;
|
||||
|
||||
var dict = ParseToDict(input);
|
||||
|
||||
if (dict == null)
|
||||
return null;
|
||||
|
||||
return MarkingEffectTypes.TypeParsers.TryGetValue(type, out var parser) ? parser(dict) : null;
|
||||
}
|
||||
|
||||
public static string ParamToString<T>(T param)
|
||||
{
|
||||
if (param == null)
|
||||
return "";
|
||||
|
||||
return param switch
|
||||
{
|
||||
null => "",
|
||||
float f => f.ToString(CultureInfo.InvariantCulture),
|
||||
bool b => b.ToString(),
|
||||
Vector2 v => Vector2ToString(v),
|
||||
_ => "",
|
||||
};
|
||||
}
|
||||
|
||||
public static bool TryParseParam<T>(string input, out T param)
|
||||
{
|
||||
param = default!;
|
||||
|
||||
if (typeof(T) == typeof(float))
|
||||
{
|
||||
if (!float.TryParse(input, CultureInfo.InvariantCulture, out var floatResult))
|
||||
return false;
|
||||
|
||||
param = (T)(object)floatResult;
|
||||
}
|
||||
else if (typeof(T) == typeof(bool))
|
||||
{
|
||||
if (!bool.TryParse(input, out var boolResult))
|
||||
return false;
|
||||
|
||||
param = (T)(object)boolResult;
|
||||
}
|
||||
else if (typeof(T) == typeof(Vector2))
|
||||
param = (T)(object)ParseVector2(input);
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Static methods
|
||||
|
||||
public static Vector2 ParseVector2(string str)
|
||||
{
|
||||
str = str.Trim('(', ')');
|
||||
var parts = str.Split('=');
|
||||
|
||||
if (parts.Length != 2
|
||||
|| !float.TryParse(parts[0], CultureInfo.InvariantCulture, out var x)
|
||||
|| !float.TryParse(parts[1], CultureInfo.InvariantCulture, out var y))
|
||||
return new(0, 0);
|
||||
|
||||
return new Vector2(x, y);
|
||||
}
|
||||
|
||||
public static bool DictionaryEquals<TKey, TValue>(
|
||||
Dictionary<TKey, TValue>? a,
|
||||
Dictionary<TKey, TValue>? b)
|
||||
where TKey : notnull
|
||||
{
|
||||
if (a == b)
|
||||
return true;
|
||||
if (a == null || b == null)
|
||||
return false;
|
||||
|
||||
return a.Count == b.Count && !a.Except(b).Any();
|
||||
}
|
||||
|
||||
public static string Vector2ToString(Vector2 v)
|
||||
{
|
||||
return $"({v.X.ToString(CultureInfo.InvariantCulture)}={v.Y.ToString(CultureInfo.InvariantCulture)})";
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
20
Content.Shared/_Sunrise/MarkingEffects/MarkingEffectTypes.cs
Normal file
20
Content.Shared/_Sunrise/MarkingEffects/MarkingEffectTypes.cs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
namespace Content.Shared._Sunrise.MarkingEffects;
|
||||
|
||||
// какой-то прям костыль, надо как-то по-другому парсер реализовать
|
||||
|
||||
public enum MarkingEffectType
|
||||
{
|
||||
Color,
|
||||
Gradient,
|
||||
RoughGradient,
|
||||
}
|
||||
|
||||
public static class MarkingEffectTypes
|
||||
{
|
||||
public static readonly Dictionary<MarkingEffectType, Func<Dictionary<string, string>, MarkingEffect?>> TypeParsers = new()
|
||||
{
|
||||
{ MarkingEffectType.Color, ColorMarkingEffect.Parse },
|
||||
{ MarkingEffectType.Gradient, GradientMarkingEffect.Parse },
|
||||
{ MarkingEffectType.RoughGradient, RoughGradientMarkingEffect.Parse },
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._Sunrise.MarkingEffects;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class RoughGradientMarkingEffect : MarkingEffect
|
||||
{
|
||||
public override MarkingEffectType Type => MarkingEffectType.RoughGradient;
|
||||
|
||||
public bool Horizontal = false;
|
||||
|
||||
#region Parsing
|
||||
public override string ToString()
|
||||
{
|
||||
var dict = new Dictionary<string, string>();
|
||||
|
||||
dict.Add("horizontal", ParamToString(Horizontal));
|
||||
|
||||
foreach (var (k, v) in Colors)
|
||||
dict.Add($"color.{k}", $"{v.ToHex()}");
|
||||
|
||||
var result = string.Join(",", dict.Select(kvp => $"{kvp.Key}={kvp.Value}"));
|
||||
return $"{Type.ToString()}=={result}";
|
||||
}
|
||||
|
||||
public static RoughGradientMarkingEffect? Parse(Dictionary<string, string> dict)
|
||||
{
|
||||
var colors = new Dictionary<string, Color>();
|
||||
|
||||
var horizontal = false;
|
||||
|
||||
foreach (var (type, value) in dict)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case "horizontal":
|
||||
TryParseParam(value, out horizontal);
|
||||
break;
|
||||
default:
|
||||
{
|
||||
if (type.StartsWith("color."))
|
||||
colors[type["color.".Length..]] = Color.TryFromHex(value) ?? Color.White;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new RoughGradientMarkingEffect(colors, horizontal);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public RoughGradientMarkingEffect()
|
||||
{
|
||||
Colors = new Dictionary<string, Color>()
|
||||
{
|
||||
{ "base", Color.White }
|
||||
};
|
||||
}
|
||||
|
||||
public RoughGradientMarkingEffect(Color color)
|
||||
{
|
||||
Colors = new Dictionary<string, Color>
|
||||
{
|
||||
{"base", color }
|
||||
};
|
||||
}
|
||||
|
||||
public RoughGradientMarkingEffect(Dictionary<string, Color> colors, bool horizontal)
|
||||
{
|
||||
Colors = colors;
|
||||
Horizontal = horizontal;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Other methods
|
||||
public override RoughGradientMarkingEffect Clone()
|
||||
{
|
||||
return new RoughGradientMarkingEffect(new(Colors), Horizontal);
|
||||
}
|
||||
|
||||
public override bool Equals(MarkingEffect? maybeOther)
|
||||
{
|
||||
if (maybeOther is not RoughGradientMarkingEffect other)
|
||||
return false;
|
||||
|
||||
return DictionaryEquals(Colors, other.Colors)
|
||||
&& Equals(Horizontal, other.Horizontal);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
marking-effect-type-color = Стандартный
|
||||
marking-effect-color-color-base = Цвет
|
||||
|
||||
|
||||
marking-effect-type-gradient = Градиент
|
||||
marking-effect-gradient-color-base = Начало
|
||||
marking-effect-gradient-color-gradient = Конец
|
||||
|
||||
marking-effect-gradient-parameter-offset = Позиция
|
||||
marking-effect-gradient-parameter-size = Размер
|
||||
marking-effect-gradient-parameter-rotation = Поворот
|
||||
marking-effect-gradient-parameter-pixelation = Пикселизация
|
||||
marking-effect-gradient-parameter-mirror = Отражение эффекта
|
||||
|
||||
|
||||
marking-effect-type-roughgradient = Неровный градиент
|
||||
marking-effect-roughgradient-color-base = Начало
|
||||
marking-effect-roughgradient-color-gradient = Конец
|
||||
marking-effect-roughgradient-parameter-horizontal = Горизонтальный
|
||||
|
|
@ -28,7 +28,7 @@
|
|||
hideLayersOnEquip:
|
||||
- Hair
|
||||
- Snout
|
||||
# MARKINGS START
|
||||
# MARKINGS START
|
||||
bodyTypeMarkingsDisplacement:
|
||||
body-fat-m:
|
||||
Hair:
|
||||
|
|
@ -557,7 +557,7 @@
|
|||
categories: [ HideSpawnMenu ]
|
||||
components:
|
||||
- type: HumanoidAppearance
|
||||
# MARKINGS START
|
||||
# MARKINGS START
|
||||
bodyTypeMarkingsDisplacement:
|
||||
body-fat-m:
|
||||
Hair:
|
||||
|
|
@ -1071,4 +1071,4 @@
|
|||
sizeMaps:
|
||||
32:
|
||||
sprite: _Sunrise/Mobs/Species/Human/Distrofik/displacement.rsi
|
||||
state: back
|
||||
state: back
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
- type: shader
|
||||
- type: shader
|
||||
id: NVDDisplay
|
||||
kind: source
|
||||
path: "/Textures/_Sunrise/Shaders/NVDDisplay.swsl"
|
||||
|
|
@ -34,3 +34,13 @@
|
|||
NightVisionBoost: 2.0
|
||||
NightVisionThreshold: 0.3
|
||||
BlueTintIntensity: 0.6
|
||||
|
||||
- type: shader
|
||||
id: Gradient
|
||||
kind: source
|
||||
path: "/Textures/_Sunrise/Shaders/gradient.swsl"
|
||||
|
||||
- type: shader
|
||||
id: RoughGradient
|
||||
kind: source
|
||||
path: "/Textures/_Sunrise/Shaders/gradient_rough.swsl"
|
||||
|
|
|
|||
66
Resources/Textures/_Sunrise/Shaders/gradient.swsl
Normal file
66
Resources/Textures/_Sunrise/Shaders/gradient.swsl
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
uniform highp vec3 color1;
|
||||
uniform highp vec3 color2;
|
||||
uniform highp vec2 texScale;
|
||||
uniform highp vec2 offset;
|
||||
uniform highp vec2 size;
|
||||
uniform highp float rotation;
|
||||
uniform bool pixelated;
|
||||
uniform bool mirrored;
|
||||
|
||||
// DISPLACEMENT START
|
||||
uniform sampler2D displacementMap;
|
||||
uniform highp float displacementSize;
|
||||
uniform highp vec4 displacementUV;
|
||||
varying highp vec2 displacementUVOut;
|
||||
|
||||
void vertex() {
|
||||
if(useDisplacement == true) {
|
||||
displacementUVOut = mix(displacementUV.xy, displacementUV.zw, tCoord2);
|
||||
}
|
||||
}
|
||||
|
||||
uniform bool useDisplacement;
|
||||
// DISPLACEMENT END
|
||||
|
||||
highp float mirror(highp float x) {
|
||||
return mix(x, abs(fract(x) * 2.0 - 1.0), float(mirrored));
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
highp vec4 color = texture2D(TEXTURE, UV);
|
||||
|
||||
highp vec2 uv = UV2;
|
||||
highp float rad = radians(rotation);
|
||||
highp vec2 center = vec2(0.5);
|
||||
uv -= center;
|
||||
|
||||
highp float cos_a = cos(rad);
|
||||
highp float sin_a = sin(rad);
|
||||
highp mat2 rot = mat2(
|
||||
cos_a, -sin_a,
|
||||
sin_a, cos_a
|
||||
);
|
||||
|
||||
if (pixelated) {
|
||||
uv = floor(uv * texScale) / texScale;
|
||||
}
|
||||
|
||||
uv = rot * uv;
|
||||
uv += center;
|
||||
|
||||
highp float t = mirror(uv.y / size.y + offset.y);
|
||||
t = clamp(t, 0.0, 1.0);
|
||||
|
||||
highp vec3 gradient = mix(color2, color1, t);
|
||||
|
||||
// DISPLACEMENT START
|
||||
if(useDisplacement == true) {
|
||||
highp vec4 displacementSample = texture2D(displacementMap, displacementUVOut);
|
||||
highp vec2 displacementValue = (displacementSample.xy - vec2(128.0 / 255.0)) / (1.0 - 128.0 / 255.0);
|
||||
color = zTexture(UV + displacementValue * TEXTURE_PIXEL_SIZE * displacementSize * vec2(1.0, -1.0));
|
||||
color.a *= displacementSample.a;
|
||||
}
|
||||
// DISPLACEMENT END
|
||||
|
||||
COLOR = color * vec4(gradient, 1);
|
||||
}
|
||||
69
Resources/Textures/_Sunrise/Shaders/gradient_rough.swsl
Normal file
69
Resources/Textures/_Sunrise/Shaders/gradient_rough.swsl
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
uniform highp vec3 color1;
|
||||
uniform highp vec3 color2;
|
||||
uniform bool horizontal;
|
||||
|
||||
// DISPLACEMENT START
|
||||
uniform sampler2D displacementMap;
|
||||
uniform highp float displacementSize;
|
||||
uniform highp vec4 displacementUV;
|
||||
varying highp vec2 displacementUVOut;
|
||||
|
||||
void vertex() {
|
||||
if(useDisplacement == true) {
|
||||
displacementUVOut = mix(displacementUV.xy, displacementUV.zw, tCoord2);
|
||||
}
|
||||
}
|
||||
|
||||
uniform bool useDisplacement;
|
||||
// DISPLACEMENT END
|
||||
|
||||
highp float find_bound(bool forward, bool horizontal) {
|
||||
highp float step = horizontal ? TEXTURE_PIXEL_SIZE.x : TEXTURE_PIXEL_SIZE.y;
|
||||
highp float current = horizontal ? UV.x : UV.y;
|
||||
|
||||
const highp int MAX_ITERATIONS = 256;
|
||||
highp int iterations = 0;
|
||||
|
||||
highp vec4 result = texture2D(TEXTURE, UV);
|
||||
|
||||
while ((forward ? current < 1.0 : current > 0.0) && result.a > 0.0 && iterations < MAX_ITERATIONS) {
|
||||
current += forward ? step : -step;
|
||||
iterations++;
|
||||
|
||||
highp vec2 probeUV = UV;
|
||||
if (horizontal)
|
||||
probeUV.x = current;
|
||||
else
|
||||
probeUV.y = current;
|
||||
|
||||
result = texture2D(TEXTURE, probeUV);
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
|
||||
void fragment() {
|
||||
highp vec4 color = texture2D(TEXTURE, UV);
|
||||
|
||||
highp float a = find_bound(false, horizontal);
|
||||
highp float b = find_bound(true, horizontal);
|
||||
|
||||
highp float diff = max(0.001, b - a);
|
||||
highp float pos = horizontal ? UV.x : UV.y;
|
||||
|
||||
highp float norm = clamp((pos - a) / diff, 0.0, 1.0);
|
||||
highp vec3 gradient = mix(color2, color1, norm);
|
||||
|
||||
// DISPLACEMENT START
|
||||
if(useDisplacement == true) {
|
||||
highp vec4 displacementSample = texture2D(displacementMap, displacementUVOut);
|
||||
highp vec2 displacementValue = (displacementSample.xy - vec2(128.0 / 255.0)) / (1.0 - 128.0 / 255.0);
|
||||
color = zTexture(UV + displacementValue * TEXTURE_PIXEL_SIZE * displacementSize * vec2(1.0, -1.0));
|
||||
color.a *= displacementSample.a;
|
||||
}
|
||||
// DISPLACEMENT END
|
||||
|
||||
COLOR = vec4(color.rgb * gradient, color.a);
|
||||
}
|
||||
|
||||
Loading…
Add table
Reference in a new issue