support reskinning body colors
This commit is contained in:
parent
584ca0a0fc
commit
91bbbb4cb4
|
@ -19,6 +19,10 @@ AVALI_SKIN_DASH3=Three Dashes
|
|||
AVALI_SKIN_DASH4=Four Dashes
|
||||
AVALI_SKIN_DASH5=Five+ Dashes
|
||||
|
||||
AVALI_SKIN_BODY=Body colors
|
||||
AVALI_SKIN_LIGHTBODY=Light
|
||||
AVALI_SKIN_DARKBODY=Dark
|
||||
|
||||
AVALI_SKIN_RED=Red
|
||||
AVALI_SKIN_GREEN=Green
|
||||
AVALI_SKIN_BLUE=Blue
|
||||
|
|
|
@ -9,27 +9,87 @@
|
|||
#define SAMPLE_TEXTURE(Name, texCoord) tex2D(Name##Sampler, texCoord)
|
||||
|
||||
|
||||
DECLARE_TEXTURE(text, 0); // The texture to be recolored
|
||||
// https://web.archive.org/web/20200207113336/http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
|
||||
float4 rgb2hsv(float4 c) {
|
||||
float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
|
||||
float4 p = lerp(float4(c.bg, K.wz), float4(c.gb, K.xy), step(c.b, c.g));
|
||||
float4 q = lerp(float4(p.xyw, c.r), float4(c.r, p.yzx), step(p.x, c.r));
|
||||
|
||||
uniform float4 color_replace_from; // #1ad589
|
||||
uniform float4 color_replace_to;
|
||||
uniform float threshold; // 0.01
|
||||
|
||||
|
||||
// hsls provides distance instead of this...
|
||||
// float fast_distance4(float4 a, float4 b) {
|
||||
// float4 diff = a - b;
|
||||
// return diff.x * diff.x + diff.y * diff.y + diff.z * diff.z + diff.w * diff.w;
|
||||
// }
|
||||
|
||||
|
||||
float4 ps_recolor(float4 pos: SV_Position, float4 in_color: COLOR0, float2 uv: TEXCOORD0): COLOR {
|
||||
float4 color = SAMPLE_TEXTURE(text, uv);
|
||||
return (distance(color, color_replace_from) < threshold ? color_replace_to : color) * in_color;
|
||||
float d = q.x - min(q.w, q.y);
|
||||
float e = 1.0e-10;
|
||||
return float4(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x, c.a);
|
||||
}
|
||||
|
||||
float4 hsv2rgb(float4 c) {
|
||||
float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
||||
float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www);
|
||||
float3 h = c.z * lerp(K.xxx, saturate(p - K.xxx), c.y);
|
||||
return float4(h.rgb, c.a);
|
||||
}
|
||||
|
||||
|
||||
DECLARE_TEXTURE(sprite, 0); // The sprite texture
|
||||
|
||||
uniform float recolor1_threshold;
|
||||
uniform float4 recolor1_rgb_from;
|
||||
uniform float4 recolor1_rgb_to;
|
||||
|
||||
uniform float rehue1_threshold;
|
||||
uniform float4 rehue1_threshold_mul;
|
||||
uniform float4 rehue1_rgb_from;
|
||||
uniform float4 rehue1_rgb_to;
|
||||
static const float rehue1_threshold_norm = rehue1_threshold * length(rehue1_threshold_mul);
|
||||
static const float4 rehue1_hsv_from = rgb2hsv(rehue1_rgb_from);
|
||||
static const float4 rehue1_hsv_to = rgb2hsv(rehue1_rgb_to);
|
||||
|
||||
uniform float rehue2_threshold;
|
||||
uniform float4 rehue2_threshold_mul;
|
||||
uniform float4 rehue2_rgb_from;
|
||||
uniform float4 rehue2_rgb_to;
|
||||
static const float rehue2_threshold_norm = rehue2_threshold * length(rehue2_threshold_mul);
|
||||
static const float4 rehue2_hsv_from = rgb2hsv(rehue2_rgb_from);
|
||||
static const float4 rehue2_hsv_to = rgb2hsv(rehue2_rgb_to);
|
||||
|
||||
|
||||
float4 hueshift(
|
||||
float4 hsv_color, float4 hsv_from, float4 hsv_to
|
||||
) {
|
||||
float4 hsv = hsv_to - hsv_from + hsv_color;
|
||||
float4 hsv_clamp = float4((hsv.r + 1.0) % 1.0, saturate(hsv.gba));
|
||||
return hsv2rgb(hsv_clamp);
|
||||
}
|
||||
|
||||
|
||||
float4 ps_main(float4 pos: SV_Position, float4 sprite_color: COLOR0, float2 uv: TEXCOORD0): COLOR {
|
||||
float4 tex_rgb = SAMPLE_TEXTURE(sprite, uv);
|
||||
float4 tex_hsv = rgb2hsv(tex_rgb);
|
||||
|
||||
// recolor the sprite color if it is normal
|
||||
if (distance(sprite_color, recolor1_rgb_from) < recolor1_threshold) {
|
||||
sprite_color = recolor1_rgb_to;
|
||||
}
|
||||
|
||||
// replace recolor1_rgb_from with recolor1_rgb_to if in threshold
|
||||
if (distance(tex_rgb, recolor1_rgb_from) < recolor1_threshold) {
|
||||
return recolor1_rgb_to * sprite_color;
|
||||
}
|
||||
|
||||
// hue-shift tex_hsv by the difference between rehue1_hsv_to - rehue1_hsv_from,
|
||||
// only if it is in threshold scaled by the threshold multipler
|
||||
if (distance(tex_hsv * rehue1_threshold_mul, rehue1_hsv_from * rehue1_threshold_mul) < rehue1_threshold_norm) {
|
||||
return hueshift(tex_hsv, rehue1_hsv_from, rehue1_hsv_to) * sprite_color;
|
||||
}
|
||||
|
||||
if (distance(tex_hsv * rehue2_threshold_mul, rehue2_hsv_from * rehue2_threshold_mul) < rehue2_threshold_norm) {
|
||||
return hueshift(tex_hsv, rehue2_hsv_from, rehue2_hsv_to) * sprite_color;
|
||||
}
|
||||
|
||||
return tex_rgb * sprite_color;
|
||||
}
|
||||
|
||||
|
||||
technique Recolor {
|
||||
pass {
|
||||
PixelShader = compile ps_2_0 ps_recolor();
|
||||
PixelShader = compile ps_2_0 ps_main();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,9 +29,19 @@ namespace Celeste.Mod.AvaliSkin {
|
|||
Enabled = Settings.Enabled,
|
||||
ColorMode = Settings.ColorModeOpt,
|
||||
ManualPreset = Settings.DashPreset,
|
||||
ManualRGB = Settings.DashRGBColor
|
||||
ManualRGB = Settings.DashRGBColor,
|
||||
LightBody = Settings.ColorModeOpt == ColorMode.ManualPreset ? ColorUtil.SettingToColor(Settings.LightBodyPreset) : Settings.LightBodyRGBColor,
|
||||
DarkBody = Settings.ColorModeOpt == ColorMode.ManualPreset ? ColorUtil.SettingToColor(Settings.DarkBodyPreset) : Settings.DarkBodyRGBColor,
|
||||
};
|
||||
}
|
||||
public static AvaliConfig EveryoneHasSkinConfig {
|
||||
get => new AvaliConfig {
|
||||
Enabled = true,
|
||||
ColorMode = ColorMode.ExternalDash,
|
||||
LightBody = PlayerConfig.LightBody,
|
||||
DarkBody = PlayerConfig.DarkBody,
|
||||
}
|
||||
};
|
||||
private static Effect FxRecolor;
|
||||
|
||||
public static EverestModuleMetadata CelesteNetMeta = new EverestModuleMetadata() {
|
||||
|
@ -53,7 +63,11 @@ namespace Celeste.Mod.AvaliSkin {
|
|||
On.Celeste.PlayerSprite.Render += onPlayerSpriteRenderCelestenet;
|
||||
On.Celeste.PlayerSprite.Render += onPlayerSpriteRenderCelestenetMisc;
|
||||
} else {
|
||||
On.Celeste.PlayerSprite.Render += onPlayerSpriteRenderMisc;
|
||||
using (new DetourContext("AvaliSkinModule") {
|
||||
After = { "Hyperline" }
|
||||
}) {
|
||||
On.Celeste.PlayerSprite.Render += onPlayerSpriteRenderMisc;
|
||||
}
|
||||
}
|
||||
|
||||
using (new DetourContext("AvaliSkinModule") {
|
||||
|
@ -150,29 +164,60 @@ namespace Celeste.Mod.AvaliSkin {
|
|||
|
||||
// CreateOn doesn't preserve some of the properties that we need to keep here!
|
||||
// Copy them over manually instead...
|
||||
Vector2 pos = sprite.RenderPosition;
|
||||
Vector2 pos = sprite.Position;
|
||||
Color color = sprite.Color;
|
||||
string currentAnimationID = sprite.CurrentAnimationID;
|
||||
int CurrentAnimationFrame = sprite.CurrentAnimationFrame;
|
||||
int currentAnimationFrame = sprite.CurrentAnimationFrame;
|
||||
|
||||
GFX.SpriteBank.CreateOn(sprite, spriteID);
|
||||
|
||||
sprite.RenderPosition = pos;
|
||||
sprite.Position = pos;
|
||||
sprite.SetColor(color);
|
||||
if (currentAnimationID != "") {
|
||||
sprite.Play(currentAnimationID);
|
||||
sprite.SetAnimationFrame(CurrentAnimationFrame);
|
||||
sprite.SetAnimationFrame(currentAnimationFrame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void spriteRecolor(Color dashColor) {
|
||||
private void spriteRecolor(Color dashColor, Color? lightColor = null, Color? darkColor = null) {
|
||||
Color lightColor2 = lightColor != null
|
||||
? (Color) lightColor : ColorUtil.HexToColor("a2885c");
|
||||
Color darkColor2 = darkColor != null
|
||||
? (Color) darkColor : ColorUtil.HexToColor("4e4e4e");
|
||||
|
||||
// apply the recolor effect to the player:
|
||||
// replace the color #1ad589 in the sprite with color
|
||||
FxRecolor.Parameters["threshold"].SetValue(0.03f);
|
||||
FxRecolor.Parameters["color_replace_from"].SetValue(
|
||||
FxRecolor.Parameters["recolor1_threshold"].SetValue(0.03f);
|
||||
FxRecolor.Parameters["recolor1_rgb_from"].SetValue(
|
||||
(new Color((byte) 0x1a, 0xd5, 0x89, 0xff)).ToVector4()
|
||||
);
|
||||
FxRecolor.Parameters["color_replace_to"].SetValue(dashColor.ToVector4());
|
||||
FxRecolor.Parameters["recolor1_rgb_to"].SetValue(dashColor.ToVector4());
|
||||
|
||||
// works at 0.12 minus the dark brown
|
||||
FxRecolor.Parameters["rehue1_threshold"].SetValue(0.03f);
|
||||
FxRecolor.Parameters["rehue1_threshold_mul"].SetValue(
|
||||
new Vector4(1f, 0.1f, 0.1f, 0f)
|
||||
);
|
||||
FxRecolor.Parameters["rehue1_rgb_from"].SetValue(
|
||||
(new Color((byte) 0xa2, 0x88, 0x5c, 0xff)).ToVector4() // #a2885c
|
||||
);
|
||||
FxRecolor.Parameters["rehue1_rgb_to"].SetValue(
|
||||
lightColor2.ToVector4()
|
||||
); // #433722
|
||||
|
||||
FxRecolor.Parameters["rehue2_threshold"].SetValue(0.07f);
|
||||
FxRecolor.Parameters["rehue2_threshold_mul"].SetValue(
|
||||
new Vector4(1f, .8f, .8f, 0f)
|
||||
);
|
||||
FxRecolor.Parameters["rehue2_rgb_from"].SetValue(
|
||||
(new Color((byte) 0x4e, 0x4e, 0x4e, 0xff)).ToVector4() // #4e4e4e
|
||||
);
|
||||
FxRecolor.Parameters["rehue2_rgb_to"].SetValue(
|
||||
darkColor2.ToVector4()
|
||||
);
|
||||
|
||||
FxRecolor.CurrentTechnique = FxRecolor.Techniques["Recolor"];
|
||||
}
|
||||
|
||||
|
@ -200,7 +245,7 @@ namespace Celeste.Mod.AvaliSkin {
|
|||
Color color = PlayerConfig.GetColor(self);
|
||||
|
||||
// apply the recolor effect to the player
|
||||
spriteRecolor(color);
|
||||
spriteRecolor(color, PlayerConfig.LightBody, PlayerConfig.DarkBody);
|
||||
|
||||
Draw.SpriteBatch.End();
|
||||
Draw.SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointWrap, DepthStencilState.None, RasterizerState.CullNone, FxRecolor, (self.Scene as Level).GameplayRenderer.Camera.Matrix);
|
||||
|
@ -245,7 +290,7 @@ namespace Celeste.Mod.AvaliSkin {
|
|||
}
|
||||
|
||||
// apply the recolor effect to the sprite
|
||||
spriteRecolor(color);
|
||||
spriteRecolor(color, PlayerConfig.LightBody, PlayerConfig.DarkBody);
|
||||
|
||||
Draw.SpriteBatch.End();
|
||||
Draw.SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointWrap, DepthStencilState.None, RasterizerState.CullNone, FxRecolor, (self.Scene as Level).GameplayRenderer.Camera.Matrix);
|
||||
|
@ -287,9 +332,8 @@ namespace Celeste.Mod.AvaliSkin {
|
|||
// Ghosts have their own custom hair and sprite which we are able to recolor like the player.
|
||||
// We need to be really paranoid here cuz celestenet jank...
|
||||
CelesteNetClient client = CelesteNetClientModule.Instance.Client;
|
||||
AvaliConfig everyoneHasSkin = new AvaliConfig { Enabled = true, ColorMode = ColorMode.ExternalDash};
|
||||
Color color;
|
||||
Ghost ghost;
|
||||
AvaliConfig config;
|
||||
if (
|
||||
self.Scene != null && self.Entity != null
|
||||
&& self.Entity is Ghost ghost2 && ghost2.PlayerInfo != null
|
||||
|
@ -301,15 +345,15 @@ namespace Celeste.Mod.AvaliSkin {
|
|||
&& data.Config.IsEnabled(ghost2)
|
||||
) {
|
||||
ghost = ghost2;
|
||||
color = data.Config.GetColor(ghost);
|
||||
config = data.Config;
|
||||
} else if (
|
||||
Settings.CelesteNetEveryoneHasSkin
|
||||
&& self.Scene != null && self.Entity != null
|
||||
&& self.Entity is Ghost ghost3
|
||||
&& everyoneHasSkin.IsEnabled(ghost3)
|
||||
&& EveryoneHasSkinConfig.IsEnabled(ghost3)
|
||||
) {
|
||||
ghost = ghost3;
|
||||
color = everyoneHasSkin.GetColor(ghost);
|
||||
config = EveryoneHasSkinConfig;
|
||||
} else if (
|
||||
self.Scene != null && self.Entity != null
|
||||
&& self.Entity is Ghost _
|
||||
|
@ -326,7 +370,7 @@ namespace Celeste.Mod.AvaliSkin {
|
|||
trySpriteSwap(self, true);
|
||||
|
||||
// apply the recolor effect to the ghost
|
||||
spriteRecolor(color);
|
||||
spriteRecolor(config.GetColor(ghost), config.LightBody, config.DarkBody);
|
||||
|
||||
Draw.SpriteBatch.End();
|
||||
Draw.SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointWrap, DepthStencilState.None, RasterizerState.CullNone, FxRecolor, (self.Scene as Level).GameplayRenderer.Camera.Matrix);
|
||||
|
@ -348,10 +392,13 @@ namespace Celeste.Mod.AvaliSkin {
|
|||
if (sprite.Scene != null && PlayerConfig.IsEnabled(player)) {
|
||||
// swap out the body's spritebank if the enabled state changed
|
||||
trySpriteSwap(sprite, true);
|
||||
Color color = PlayerConfig.GetColor(player);
|
||||
|
||||
// apply the recolor effect to the body
|
||||
spriteRecolor(color);
|
||||
spriteRecolor(
|
||||
PlayerConfig.GetColor(player),
|
||||
PlayerConfig.LightBody,
|
||||
PlayerConfig.DarkBody
|
||||
);
|
||||
|
||||
Draw.SpriteBatch.End();
|
||||
Draw.SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.PointWrap, DepthStencilState.None, RasterizerState.CullNone, FxRecolor, (self.Scene as Level).GameplayRenderer.Camera.Matrix);
|
||||
|
@ -390,7 +437,6 @@ namespace Celeste.Mod.AvaliSkin {
|
|||
|
||||
|
||||
private Color onPlayerGetTrailColor(On.Celeste.Player.orig_GetCurrentTrailColor orig, Player self) {
|
||||
Color orig_color = orig(self);
|
||||
if (PlayerConfig.IsEnabled(self)) {
|
||||
// Don't change the trail color if another mod is in control of it!
|
||||
// The hair mod should do be doing that instead of us.
|
||||
|
@ -403,7 +449,7 @@ namespace Celeste.Mod.AvaliSkin {
|
|||
}
|
||||
|
||||
// skin disabled, keep original colors
|
||||
return orig_color;
|
||||
return orig(self);
|
||||
}
|
||||
|
||||
|
||||
|
@ -445,8 +491,8 @@ namespace Celeste.Mod.AvaliSkin {
|
|||
Color = color,
|
||||
Color2 = color,
|
||||
};
|
||||
Player.P_DashB = Player.P_DashA;
|
||||
Player.P_DashBadB = Player.P_DashA;
|
||||
Player.P_DashB = new ParticleType(Player.P_DashA);
|
||||
Player.P_DashBadB = new ParticleType(Player.P_DashA);
|
||||
|
||||
// run vanilla code: if it emits particles, it will use recolored ones.
|
||||
int result = orig(self);
|
||||
|
@ -459,6 +505,7 @@ namespace Celeste.Mod.AvaliSkin {
|
|||
return result;
|
||||
}
|
||||
|
||||
// todo: apply shader to these entities
|
||||
|
||||
private void onPayphoneConstructor(On.Celeste.Payphone.orig_ctor orig, Payphone self, Vector2 pos) {
|
||||
orig(self, pos);
|
||||
|
|
|
@ -37,12 +37,12 @@ namespace Celeste.Mod.AvaliSkin {
|
|||
ColorSubmenuItem.Visible = ColorModeOpt != ColorMode.ExternalDash;
|
||||
|
||||
// disable and change item visibility based on main toggle or colormode
|
||||
foreach (var item in DashRGBItems) {
|
||||
foreach (var item in RGBItems) {
|
||||
item.Disabled = !Enabled;
|
||||
item.Visible = ColorModeOpt == ColorMode.ManualRGB;
|
||||
}
|
||||
|
||||
foreach (var item in DashPresetItems) {
|
||||
foreach (var item in PresetItems) {
|
||||
item.Disabled = !Enabled;
|
||||
item.Visible = ColorModeOpt == ColorMode.ManualPreset;
|
||||
}
|
||||
|
@ -125,25 +125,24 @@ namespace Celeste.Mod.AvaliSkin {
|
|||
ColorModeOptNote = (TextMenu.SubHeader) items[items.IndexOf(ColorModeOptItem) + 1];
|
||||
}
|
||||
|
||||
#region AvaliColorSettings
|
||||
|
||||
// ColorSubmenu is a dummy setting that is only used to position the
|
||||
// Submenu. This setting is never used, hence why it is Void!
|
||||
[YamlIgnore]
|
||||
public Void ColorSubmenu { get; set; }
|
||||
private TextMenuExt.OptionSubMenu ColorSubmenuItem;
|
||||
private TextMenuExt.OptionSubMenu ColorSubmenuItem, BodyColorSubmenuItem;
|
||||
public void CreateColorSubmenuEntry(TextMenu menu, bool inGame) {
|
||||
DashRGBItems.Clear();
|
||||
DashPresetItems.Clear();
|
||||
RGBItems.Clear();
|
||||
PresetItems.Clear();
|
||||
|
||||
ColorSubmenuItem = new TextMenuExt.OptionSubMenu(
|
||||
"AVALI_SKIN_COLORS".DialogOrKey()
|
||||
);
|
||||
|
||||
// This generates n submenus, one for each dash
|
||||
TextMenuExt.IntSlider DashRItem, DashGItem, DashBItem;
|
||||
TextMenu.Option<ColorChoice> DashColorItem;
|
||||
for (int i = 0; i < DashRGBColor.Count; i++) {
|
||||
TextMenuExt.IntSlider DashRItem, DashGItem, DashBItem;
|
||||
TextMenu.Option<ColorChoice> DashColorItem;
|
||||
|
||||
int j = i; // C# lambda are wierd: capturing i directly mutates the captured variable
|
||||
|
||||
|
@ -156,19 +155,19 @@ namespace Celeste.Mod.AvaliSkin {
|
|||
).Change(
|
||||
// C# is stupidly pendatic and doesn't support property assignment in List elements
|
||||
// so we have to do this ugly shit to avoid breaking up this expression into two
|
||||
c => DashRGBColor[j] = new Color((byte) c, DashRGBColor[j].B, DashRGBColor[j].G)
|
||||
c => DashRGBColor[j] = new Color((byte) c, DashRGBColor[j].G, DashRGBColor[j].B)
|
||||
)),
|
||||
(DashGItem = new TextMenuExt.IntSlider(
|
||||
"AVALI_SKIN_BLUE".DialogOrKey(),
|
||||
"AVALI_SKIN_GREEN".DialogOrKey(),
|
||||
0, 255, DashRGBColor[j].G
|
||||
).Change(
|
||||
c => DashRGBColor[j] = new Color(DashRGBColor[j].R, (byte) c, DashRGBColor[j].G)
|
||||
c => DashRGBColor[j] = new Color(DashRGBColor[j].R, (byte) c, DashRGBColor[j].B)
|
||||
)),
|
||||
(DashBItem = new TextMenuExt.IntSlider(
|
||||
"AVALI_SKIN_GREEN".DialogOrKey(),
|
||||
"AVALI_SKIN_BLUE".DialogOrKey(),
|
||||
0, 255, DashRGBColor[j].B
|
||||
).Change(
|
||||
c => DashRGBColor[j] = new Color(DashRGBColor[j].R, DashRGBColor[j].B, (byte) c)
|
||||
c => DashRGBColor[j] = new Color(DashRGBColor[j].R, DashRGBColor[j].G, (byte) c)
|
||||
)),
|
||||
(DashColorItem = new TextMenuExt.EnumSlider<ColorChoice>(
|
||||
"AVALI_SKIN_COLOR".DialogOrKey(),
|
||||
|
@ -177,13 +176,84 @@ namespace Celeste.Mod.AvaliSkin {
|
|||
}
|
||||
);
|
||||
|
||||
DashRGBItems.Add(DashRItem);
|
||||
DashRGBItems.Add(DashGItem);
|
||||
DashRGBItems.Add(DashBItem);
|
||||
DashPresetItems.Add(DashColorItem);
|
||||
RGBItems.Add(DashRItem);
|
||||
RGBItems.Add(DashGItem);
|
||||
RGBItems.Add(DashBItem);
|
||||
PresetItems.Add(DashColorItem);
|
||||
}
|
||||
|
||||
menu.Add(ColorSubmenuItem);
|
||||
|
||||
|
||||
BodyColorSubmenuItem = new TextMenuExt.OptionSubMenu(
|
||||
"AVALI_SKIN_BODY".DialogOrKey()
|
||||
);
|
||||
|
||||
BodyColorSubmenuItem.Add(
|
||||
"AVALI_SKIN_LIGHTBODY".DialogOrKey(),
|
||||
new List<TextMenu.Item> {
|
||||
(DashRItem = new TextMenuExt.IntSlider(
|
||||
"AVALI_SKIN_RED".DialogOrKey(),
|
||||
0, 255, LightBodyRGBColor.R
|
||||
).Change(
|
||||
c => LightBodyRGBColor = new Color((byte) c, LightBodyRGBColor.G, LightBodyRGBColor.B)
|
||||
)),
|
||||
(DashGItem = new TextMenuExt.IntSlider(
|
||||
"AVALI_SKIN_GREEN".DialogOrKey(),
|
||||
0, 255, LightBodyRGBColor.G
|
||||
).Change(
|
||||
c => LightBodyRGBColor = new Color(LightBodyRGBColor.R, (byte) c, LightBodyRGBColor.B)
|
||||
)),
|
||||
(DashBItem = new TextMenuExt.IntSlider(
|
||||
"AVALI_SKIN_BLUE".DialogOrKey(),
|
||||
0, 255, LightBodyRGBColor.B
|
||||
).Change(
|
||||
c => LightBodyRGBColor = new Color(LightBodyRGBColor.R, LightBodyRGBColor.G, (byte) c)
|
||||
)),
|
||||
(DashColorItem = new TextMenuExt.EnumSlider<ColorChoice>(
|
||||
"AVALI_SKIN_COLOR".DialogOrKey(),
|
||||
LightBodyPreset
|
||||
).Change(c => LightBodyPreset = c)),
|
||||
}
|
||||
);
|
||||
RGBItems.Add(DashRItem);
|
||||
RGBItems.Add(DashGItem);
|
||||
RGBItems.Add(DashBItem);
|
||||
PresetItems.Add(DashColorItem);
|
||||
|
||||
BodyColorSubmenuItem.Add(
|
||||
"AVALI_SKIN_DARKBODY".DialogOrKey(),
|
||||
new List<TextMenu.Item> {
|
||||
(DashRItem = new TextMenuExt.IntSlider(
|
||||
"AVALI_SKIN_RED".DialogOrKey(),
|
||||
0, 255, DarkBodyRGBColor.R
|
||||
).Change(
|
||||
c => DarkBodyRGBColor = new Color((byte) c, DarkBodyRGBColor.G, DarkBodyRGBColor.B)
|
||||
)),
|
||||
(DashGItem = new TextMenuExt.IntSlider(
|
||||
"AVALI_SKIN_GREEN".DialogOrKey(),
|
||||
0, 255, DarkBodyRGBColor.G
|
||||
).Change(
|
||||
c => DarkBodyRGBColor = new Color(DarkBodyRGBColor.R, (byte) c, DarkBodyRGBColor.B)
|
||||
)),
|
||||
(DashBItem = new TextMenuExt.IntSlider(
|
||||
"AVALI_SKIN_BLUE".DialogOrKey(),
|
||||
0, 255, DarkBodyRGBColor.B
|
||||
).Change(
|
||||
c => DarkBodyRGBColor = new Color(DarkBodyRGBColor.R, DarkBodyRGBColor.G, (byte) c)
|
||||
)),
|
||||
(DashColorItem = new TextMenuExt.EnumSlider<ColorChoice>(
|
||||
"AVALI_SKIN_COLOR".DialogOrKey(),
|
||||
DarkBodyPreset
|
||||
).Change(c => DarkBodyPreset = c)),
|
||||
}
|
||||
);
|
||||
RGBItems.Add(DashRItem);
|
||||
RGBItems.Add(DashGItem);
|
||||
RGBItems.Add(DashBItem);
|
||||
PresetItems.Add(DashColorItem);
|
||||
|
||||
menu.Add(BodyColorSubmenuItem);
|
||||
}
|
||||
|
||||
|
||||
|
@ -199,6 +269,7 @@ namespace Celeste.Mod.AvaliSkin {
|
|||
ColorUtil.SettingToColor(ColorChoice.Yellow),
|
||||
ColorUtil.SettingToColor(ColorChoice.Red),
|
||||
};
|
||||
|
||||
// this will get (de)serialized from/into a yaml list
|
||||
[SettingIgnore]
|
||||
public IEnumerable<string> DashRGB {
|
||||
|
@ -207,8 +278,30 @@ namespace Celeste.Mod.AvaliSkin {
|
|||
DashRGBColor = value.Select(c => ColorUtil.HexToColor(c)).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
[SettingIgnore]
|
||||
[YamlIgnore]
|
||||
public Color LightBodyRGBColor { get; set; } = ColorUtil.HexToColor("a2885c");
|
||||
|
||||
[SettingIgnore]
|
||||
public string LightBodyRGB {
|
||||
get => ColorUtil.ColorToHex(LightBodyRGBColor);
|
||||
set { LightBodyRGBColor = ColorUtil.HexToColor(value); }
|
||||
}
|
||||
|
||||
[SettingIgnore]
|
||||
[YamlIgnore]
|
||||
public Color DarkBodyRGBColor { get; set; } = ColorUtil.HexToColor("4e4e4e");
|
||||
|
||||
[SettingIgnore]
|
||||
public string DarkBodyRGB {
|
||||
get => ColorUtil.ColorToHex(DarkBodyRGBColor);
|
||||
set { DarkBodyRGBColor = ColorUtil.HexToColor(value); }
|
||||
}
|
||||
|
||||
// Stores submenu items that are enabled/disabled when colormode is RGB
|
||||
private List<TextMenuExt.IntSlider> DashRGBItems = new List<TextMenuExt.IntSlider>();
|
||||
private List<TextMenuExt.IntSlider> RGBItems = new List<TextMenuExt.IntSlider>();
|
||||
|
||||
|
||||
|
||||
[SettingIgnore]
|
||||
|
@ -220,8 +313,15 @@ namespace Celeste.Mod.AvaliSkin {
|
|||
ColorChoice.Yellow,
|
||||
ColorChoice.Red,
|
||||
};
|
||||
|
||||
[SettingIgnore]
|
||||
public ColorChoice LightBodyPreset { get; set; } = ColorChoice.Brown;
|
||||
|
||||
[SettingIgnore]
|
||||
public ColorChoice DarkBodyPreset { get; set; } = ColorChoice.GreyDark;
|
||||
|
||||
// Stores submenu items that are enabled/disabled when colormode is preset
|
||||
private List<TextMenu.Option<ColorChoice>> DashPresetItems = new List<TextMenu.Option<ColorChoice>>();
|
||||
private List<TextMenu.Option<ColorChoice>> PresetItems = new List<TextMenu.Option<ColorChoice>>();
|
||||
|
||||
|
||||
|
||||
|
@ -297,7 +397,6 @@ namespace Celeste.Mod.AvaliSkin {
|
|||
updateOptions();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public enum ColorChoice
|
||||
{
|
||||
|
|
|
@ -65,13 +65,17 @@ namespace Celeste.Mod.AvaliSkin {
|
|||
protected override void Read(CelesteNetBinaryReader reader) {
|
||||
Config = new AvaliConfig {
|
||||
Enabled = reader.ReadBoolean(),
|
||||
ColorMode = ColorMode.ExternalDash
|
||||
ColorMode = ColorMode.ExternalDash,
|
||||
LightBody = reader.ReadColorNoA(),
|
||||
DarkBody = reader.ReadColorNoA(),
|
||||
};
|
||||
}
|
||||
|
||||
protected override void Write(CelesteNetBinaryWriter writer) {
|
||||
writer.Write(Config.Enabled);
|
||||
if (Config.Enabled) {
|
||||
writer.WriteNoA(Config.LightBody);
|
||||
writer.WriteNoA(Config.DarkBody);
|
||||
// byte lengthRGB = Math.Min(ManualRGB.Count, 5);
|
||||
// writer.Write((byte) ManualRGB.Count);
|
||||
// foreach (var color in ManualRGB) {
|
||||
|
|
|
@ -19,6 +19,8 @@ namespace Celeste.Mod.AvaliSkin {
|
|||
public ColorMode ColorMode;
|
||||
public List<ColorChoice> ManualPreset = new List<ColorChoice>();
|
||||
public List<Color> ManualRGB = new List<Color>();
|
||||
public Color LightBody;
|
||||
public Color DarkBody;
|
||||
|
||||
|
||||
public bool IsEnabled(Player player) {
|
||||
|
@ -46,6 +48,8 @@ namespace Celeste.Mod.AvaliSkin {
|
|||
if (player.Hair != null && player.Sprite.HairCount > 0) {
|
||||
return player.Hair.GetHairColor(0);
|
||||
} else {
|
||||
Logger.Log(LogLevel.Warn, "AvaliSkin", $"Player hair is missing!");
|
||||
|
||||
// If the player does have no hair, then just default to
|
||||
// whatever preset... this only happens momentarily anyways
|
||||
return ColorUtil.SettingToColor(
|
||||
|
|
Loading…
Reference in New Issue