From 207e4596a7e09d6d23c28821df8107aa1befb769 Mon Sep 17 00:00:00 2001 From: SoulSilverJD <165866563+SoulSilverJD@users.noreply.github.com> Date: Fri, 13 Feb 2026 10:23:09 -0500 Subject: [PATCH 1/7] Add files via upload --- src/Main.cs | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/ModConfig.cs | 7 +++++++ 2 files changed, 51 insertions(+) diff --git a/src/Main.cs b/src/Main.cs index 4ef0ac5..78f6398 100644 --- a/src/Main.cs +++ b/src/Main.cs @@ -27,7 +27,12 @@ internal sealed class Nightshade : Mod private static bool usingColorizeUI = false; private static bool usingDepthOfField = false; + + // Depth of field directional fading (Tilt-Shift style) + private static float dofDirectionalTarget = 0f; // -1 up, +1 down, 0 none + private static float dofDirectionalCurrent = 0f; public static ModConfig Config; + private static ModConfig AppliedConfig; public static Nightshade instance; @@ -75,6 +80,7 @@ public override void Entry(IModHelper helper) public void ApplyConfig(ModConfig conf) { + AppliedConfig = conf; int index = conf.ColorizerActiveProfile; if (conf.ColorizeBySeason) { index = Game1.currentLocation?.GetSeasonIndex() ?? Game1.seasonIndex; @@ -275,6 +281,44 @@ public void OnRenderedWorld(object sender, RenderedWorldEventArgs e) float ypos = Game1.player.getLocalPosition(Game1.viewport).Y; ypos += Game1.player.GetBoundingBox().Height / 2; ypos /= Game1.viewport.Height; + + // Directional fading (Tilt-Shift style): bias the focus band toward facing direction. + // Note: Nightshade's DoF shader supports asymmetric focus via Center around 0.5. + { + var dof = AppliedConfig?.DepthOfFieldSettings; + bool enabled = usingDepthOfField && (dof?.DirectionalFadingEnabled ?? false); + bool inEvent = Game1.eventUp || Game1.CurrentEvent != null; + if (enabled && inEvent && !(dof?.DirectionalFadingDuringEvents ?? false)) + enabled = false; + + float target = 0f; + if (enabled) { + // Stardew facing directions: 0 up, 1 right, 2 down, 3 left + int fd = Game1.player.FacingDirection; + if (fd == 0) target = -1f; + else if (fd == 2) target = 1f; + } + + dofDirectionalTarget = target; + + float fadeTime = Math.Clamp(dof?.DirectionalFadingTime ?? 0.5f, 0f, 1f); + if (!enabled || fadeTime <= 0f) { + dofDirectionalCurrent = dofDirectionalTarget; + } else { + float dt = (float)Game1.currentGameTime.ElapsedGameTime.TotalSeconds; + float t = Math.Clamp(dt / fadeTime, 0f, 1f); + dofDirectionalCurrent = MathHelper.Lerp(dofDirectionalCurrent, dofDirectionalTarget, t); + } + + if (enabled && dofDirectionalCurrent != 0f) { + float minStrength = Math.Clamp(dof?.DirectionalFadingMinStrength ?? 0.30f, 0f, 1f); + float shiftPct = 1f - minStrength; // 0=no effect, 1=max effect + float field = Math.Clamp(dof?.Field ?? 0.6f, 0f, 1f); + float maxShift = field / 2f; + ypos = Math.Clamp(ypos + (dofDirectionalCurrent * maxShift * shiftPct), 0f, 1f); + } + } + if (ypos < 0f || ypos > 1.0f) { ypos = 0.5f; } diff --git a/src/ModConfig.cs b/src/ModConfig.cs index 149059e..058edbc 100644 --- a/src/ModConfig.cs +++ b/src/ModConfig.cs @@ -45,8 +45,15 @@ public sealed class DepthOfFieldPreset { public float Field = 0.6f; public float Intensity = 6.0f; + + // Tilt-Shift style "Directional fading" + public bool DirectionalFadingEnabled = false; + public bool DirectionalFadingDuringEvents = false; + public float DirectionalFadingTime = 0.5f; + public float DirectionalFadingMinStrength = 0.30f; } + public enum LumaType { BT709 = 0, BT601 = 1, From c2299ad2dd25be9d32b907c33bfd483a9ebb71ad Mon Sep 17 00:00:00 2001 From: SoulSilverJD <165866563+SoulSilverJD@users.noreply.github.com> Date: Fri, 13 Feb 2026 10:27:47 -0500 Subject: [PATCH 2/7] Add files via upload --- ui/ShaderMenu.cs | 89 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 3 deletions(-) diff --git a/ui/ShaderMenu.cs b/ui/ShaderMenu.cs index 8b46f95..24751dc 100644 --- a/ui/ShaderMenu.cs +++ b/ui/ShaderMenu.cs @@ -1,3 +1,4 @@ +using System; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; @@ -189,12 +190,49 @@ private void AddChildWidgets() var sld_intensity = new Slider(this, new Rectangle(126, y, 201, 20), name: "Intensity", range: new int[]{0, 100}); sld_intensity.ValueDelegate = sld_intensity.FloatRenderer(denom:10f); - y += lbl_intensity.Bounds.Height + 16; + y += lbl_intensity.Bounds.Height + 8; + + var chk_directionalFading = new Checkbox(this, 20, y, "DirectionalFadingEnabled"); + var lbl_directionalFading = new Label(this, + new Rectangle(56, y, 0, 27), + text: TR.Get("menu.DirectionalFading.Text"), + hoverText: TR.Get("menu.DirectionalFading.Hover"), + activate: chk_directionalFading); + y += chk_directionalFading.Bounds.Height + 8; + + var chk_directionalFadingEvents = new Checkbox(this, 20, y, "DirectionalFadingDuringEvents"); + var lbl_directionalFadingEvents = new Label(this, + new Rectangle(56, y, 0, 27), + text: TR.Get("menu.DirectionalFadingDuringEvents.Text"), + hoverText: TR.Get("menu.DirectionalFadingDuringEvents.Hover"), + activate: chk_directionalFadingEvents); + y += chk_directionalFadingEvents.Bounds.Height + 16; + + var lbl_directionalFadingTime = new Label(this, + new Rectangle(20, y, 96, 20), + text: TR.Get("menu.DirectionalFadingTime.Text"), + hoverText: TR.Get("menu.DirectionalFadingTime.Hover")); + var sld_directionalFadingTime = new Slider(this, new Rectangle(126, y, 201, 20), + name: "DirectionalFadingTime", range: new int[]{0, 100}); + sld_directionalFadingTime.ValueDelegate = sld_directionalFadingTime.FloatRenderer(denom:100f); + y += lbl_directionalFadingTime.Bounds.Height + 8; + + var lbl_directionalFadingMin = new Label(this, + new Rectangle(20, y, 96, 20), + text: TR.Get("menu.DirectionalFadingMinStrength.Text"), + hoverText: TR.Get("menu.DirectionalFadingMinStrength.Hover")); + var sld_directionalFadingMin = new Slider(this, new Rectangle(126, y, 201, 20), + name: "DirectionalFadingMinStrength", range: new int[]{0, 100}); + sld_directionalFadingMin.ValueDelegate = sld_directionalFadingMin.FloatRenderer(denom:100f); + y += lbl_directionalFadingMin.Bounds.Height + 16; var btn_save = new TextButton(this, 0, 0, text: TR.Get("menu.Save.Text"), onClick: SaveSettings); btn_save.Bounds.X = defaultWidth/2 - btn_save.Bounds.Width/2; - btn_save.Bounds.Y = defaultHeight - btn_save.Bounds.Height - 8; + btn_save.Bounds.Y = y; + + // expand the menu height so the Save button never overlaps controls + this.height = btn_save.Bounds.Y + btn_save.Bounds.Height + 8; this.children.AddRange(new List() { lbl_colorizer, lbl_colorizeWorld, chk_colorizeWorld, @@ -215,6 +253,10 @@ private void AddChildWidgets() lbl_enableDepthOfField, chk_enableDepthOfField, lbl_field, sld_field, lbl_intensity, sld_intensity, + lbl_directionalFading, chk_directionalFading, + lbl_directionalFadingEvents, chk_directionalFadingEvents, + lbl_directionalFadingTime, sld_directionalFadingTime, + lbl_directionalFadingMin, sld_directionalFadingMin, btn_save, }); } @@ -305,7 +347,7 @@ public void LoadColorizerPreset(ColorizerPreset set) public void LoadDepthOfFieldPreset(DepthOfFieldPreset set) { foreach (var child in this.children) { - if ((child is Slider ch)) { + if (child is Slider ch) { switch (ch.Name) { case "Field": ch.Value = (int)(set.Field * 100); @@ -313,11 +355,28 @@ public void LoadDepthOfFieldPreset(DepthOfFieldPreset set) case "Intensity": ch.Value = (int)(set.Intensity * 10); break; + case "DirectionalFadingTime": + ch.Value = (int)(Math.Clamp(set.DirectionalFadingTime, 0f, 1f) * 100); + break; + case "DirectionalFadingMinStrength": + ch.Value = (int)(Math.Clamp(set.DirectionalFadingMinStrength, 0f, 1f) * 100); + break; + } + } + else if (child is Checkbox cb) { + switch (cb.Name) { + case "DirectionalFadingEnabled": + cb.Value = set.DirectionalFadingEnabled; + break; + case "DirectionalFadingDuringEvents": + cb.Value = set.DirectionalFadingDuringEvents; + break; } } } } + public void SaveSettings() { ModConfig built = new(); @@ -339,6 +398,18 @@ public void SaveSettings() case "Intensity": built.DepthOfFieldSettings.Intensity = (float)(ch as Slider).Value / 10f; break; + case "DirectionalFadingEnabled": + built.DepthOfFieldSettings.DirectionalFadingEnabled = (ch as Checkbox).Value; + break; + case "DirectionalFadingDuringEvents": + built.DepthOfFieldSettings.DirectionalFadingDuringEvents = (ch as Checkbox).Value; + break; + case "DirectionalFadingTime": + built.DepthOfFieldSettings.DirectionalFadingTime = (float)(ch as Slider).Value / 100f; + break; + case "DirectionalFadingMinStrength": + built.DepthOfFieldSettings.DirectionalFadingMinStrength = (float)(ch as Slider).Value / 100f; + break; } } for (int i = 0; i < ColorizerActiveStates.Length; ++i) { @@ -507,6 +578,18 @@ public void onChildChange(Widget child) case "Intensity": built.DepthOfFieldSettings.Intensity = (float)(ch as Slider).Value / 10f; break; + case "DirectionalFadingEnabled": + built.DepthOfFieldSettings.DirectionalFadingEnabled = (ch as Checkbox).Value; + break; + case "DirectionalFadingDuringEvents": + built.DepthOfFieldSettings.DirectionalFadingDuringEvents = (ch as Checkbox).Value; + break; + case "DirectionalFadingTime": + built.DepthOfFieldSettings.DirectionalFadingTime = (float)(ch as Slider).Value / 100f; + break; + case "DirectionalFadingMinStrength": + built.DepthOfFieldSettings.DirectionalFadingMinStrength = (float)(ch as Slider).Value / 100f; + break; } } // TODO use initial state if missing toggle is off From a9216c0e3f76c362e0bcf868deda880a6160aa65 Mon Sep 17 00:00:00 2001 From: SoulSilverJD <165866563+SoulSilverJD@users.noreply.github.com> Date: Fri, 13 Feb 2026 10:31:31 -0500 Subject: [PATCH 3/7] Add files via upload --- i18n/default.json | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/i18n/default.json b/i18n/default.json index c1d098f..5648fc6 100644 --- a/i18n/default.json +++ b/i18n/default.json @@ -6,7 +6,6 @@ "menu.ColorizeUI.Hover": "Apply the color shader to the UI layer (menus, HUD).", "menu.ColorizeBySeason.Text": "Colorize By Season", "menu.ColorizeBySeason.Hover": "When enabled, the four profiles will be used automatically for Spring, Summer, Fall, and Winter, respectively.", - "menu.Saturation.Text": "Saturation", "menu.Lightness.Text": "Lightness", "menu.Contrast.Text": "Contrast", @@ -16,17 +15,22 @@ "menu.CopyButton.Hover": "Copy^Copy the state of the color sliders.", "menu.PasteButton.Hover": "Paste^Paste the state of the color sliders.", "menu.PreviewToggle.Hover": "Preview^When active, applies the current state of the color sliders (toggle this to swap between your starting state and current settings).", - "menu.EnableDepthOfField.Text": "Depth of Field", "menu.Field.Text": "Field", "menu.Field.Hover": "How deep the field is: i.e. how much of the screen is fully in focus (no blur).", "menu.Intensity.Text": "Intensity", "menu.Intensity.Hover": "How powerful the blur effect is (at maximum).", "menu.Save.Text": "Save", - "gmcm.MenuKeybind.name": "MenuKeybind", "gmcm.MenuKeybind.tooltip": "The keybinding to open the Nightshade menu.", "gmcm.Explainer.text": "Nightshade has its own config menu, which provides live preview of the slider settings. Please use that menu after configuring an appropriate keybinding.", - - "EOF": "" -} + "EOF": "", + "menu.DirectionalFading.Text": "Directional fading", + "menu.DirectionalFading.Hover": "Bias the in-focus band based on facing direction (Up/Down).", + "menu.DirectionalFadingDuringEvents.Text": "Enable during events", + "menu.DirectionalFadingDuringEvents.Hover": "If unchecked, directional fading is disabled during cutscenes/events.", + "menu.DirectionalFadingTime.Text": "Fade time", + "menu.DirectionalFadingTime.Hover": "How quickly the focus bias changes when you turn (seconds).", + "menu.DirectionalFadingMinStrength.Text": "Minimum strength", + "menu.DirectionalFadingMinStrength.Hover": "Lower = stronger directional effect. Higher = more subtle." +} \ No newline at end of file From dc02ce2ea09544b5293234e1acab3116c7d1e017 Mon Sep 17 00:00:00 2001 From: SoulSilverJD <165866563+SoulSilverJD@users.noreply.github.com> Date: Fri, 13 Feb 2026 10:47:14 -0500 Subject: [PATCH 4/7] Add files via upload --- i18n/default.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/i18n/default.json b/i18n/default.json index 5648fc6..1cdaf56 100644 --- a/i18n/default.json +++ b/i18n/default.json @@ -6,6 +6,7 @@ "menu.ColorizeUI.Hover": "Apply the color shader to the UI layer (menus, HUD).", "menu.ColorizeBySeason.Text": "Colorize By Season", "menu.ColorizeBySeason.Hover": "When enabled, the four profiles will be used automatically for Spring, Summer, Fall, and Winter, respectively.", + "menu.Saturation.Text": "Saturation", "menu.Lightness.Text": "Lightness", "menu.Contrast.Text": "Contrast", @@ -15,16 +16,20 @@ "menu.CopyButton.Hover": "Copy^Copy the state of the color sliders.", "menu.PasteButton.Hover": "Paste^Paste the state of the color sliders.", "menu.PreviewToggle.Hover": "Preview^When active, applies the current state of the color sliders (toggle this to swap between your starting state and current settings).", + "menu.EnableDepthOfField.Text": "Depth of Field", "menu.Field.Text": "Field", "menu.Field.Hover": "How deep the field is: i.e. how much of the screen is fully in focus (no blur).", "menu.Intensity.Text": "Intensity", "menu.Intensity.Hover": "How powerful the blur effect is (at maximum).", "menu.Save.Text": "Save", + "gmcm.MenuKeybind.name": "MenuKeybind", "gmcm.MenuKeybind.tooltip": "The keybinding to open the Nightshade menu.", "gmcm.Explainer.text": "Nightshade has its own config menu, which provides live preview of the slider settings. Please use that menu after configuring an appropriate keybinding.", + "EOF": "", + "menu.DirectionalFading.Text": "Directional fading", "menu.DirectionalFading.Hover": "Bias the in-focus band based on facing direction (Up/Down).", "menu.DirectionalFadingDuringEvents.Text": "Enable during events", From 099f7233c789ffe2a34145868dcecf51f6bf22fc Mon Sep 17 00:00:00 2001 From: SoulSilverJD <165866563+SoulSilverJD@users.noreply.github.com> Date: Fri, 13 Feb 2026 10:48:31 -0500 Subject: [PATCH 5/7] Add files via upload --- ui/ShaderMenu.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/ShaderMenu.cs b/ui/ShaderMenu.cs index 24751dc..844858b 100644 --- a/ui/ShaderMenu.cs +++ b/ui/ShaderMenu.cs @@ -376,7 +376,6 @@ public void LoadDepthOfFieldPreset(DepthOfFieldPreset set) } } - public void SaveSettings() { ModConfig built = new(); From 054faec0c7ec15da1c551d38dbe68ec47cdf84f7 Mon Sep 17 00:00:00 2001 From: SoulSilverJD <165866563+SoulSilverJD@users.noreply.github.com> Date: Fri, 13 Feb 2026 10:51:43 -0500 Subject: [PATCH 6/7] Add files via upload --- src/ModConfig.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ModConfig.cs b/src/ModConfig.cs index 058edbc..3bdddc5 100644 --- a/src/ModConfig.cs +++ b/src/ModConfig.cs @@ -53,7 +53,6 @@ public sealed class DepthOfFieldPreset public float DirectionalFadingMinStrength = 0.30f; } - public enum LumaType { BT709 = 0, BT601 = 1, From 0634f667e22d3a325de31dd696630686e1a99554 Mon Sep 17 00:00:00 2001 From: SoulSilverJD <165866563+SoulSilverJD@users.noreply.github.com> Date: Fri, 13 Feb 2026 10:52:47 -0500 Subject: [PATCH 7/7] Update default.json --- i18n/default.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/i18n/default.json b/i18n/default.json index 1cdaf56..60f056a 100644 --- a/i18n/default.json +++ b/i18n/default.json @@ -38,4 +38,4 @@ "menu.DirectionalFadingTime.Hover": "How quickly the focus bias changes when you turn (seconds).", "menu.DirectionalFadingMinStrength.Text": "Minimum strength", "menu.DirectionalFadingMinStrength.Hover": "Lower = stronger directional effect. Higher = more subtle." -} \ No newline at end of file +}