Skip to content

feat(wetness): add weather presets#1179

Merged
alandtse merged 4 commits into
community-shaders:devfrom
alandtse:wetness_effects_weather_picker
Jun 24, 2025
Merged

feat(wetness): add weather presets#1179
alandtse merged 4 commits into
community-shaders:devfrom
alandtse:wetness_effects_weather_picker

Conversation

@alandtse
Copy link
Copy Markdown
Collaborator

@alandtse alandtse commented Jun 23, 2025

Depends on #1167

  • Requires Weather Picker
  • Adds feature presets including Legacy (for original defaults)
  • Fix different weathers having same intensity
  • Feature version bump needed from but also fine since same release (feat: add raindrop ripples on water #577)

Summary by CodeRabbit

  • New Features

    • Introduced a structured climate preset system with six selectable presets for wetness and rain effects.
    • Added a new UI section for climate preset selection and detailed rain system analysis, including precipitation rates and intensity categories.
    • Enhanced tooltips and interactivity for wetness and raindrop effect settings.
    • Integrated automatic detection and application of climate presets when loading or restoring settings.
  • Improvements

    • Refined rain intensity and wetness calculations for increased accuracy and realism.
    • Improved UI feedback and analysis to help users understand the impact of their settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 23, 2025

Walkthrough

A structured climate preset system was added to the WetnessEffects feature, introducing six climate presets with detailed settings and descriptions. The UI now allows preset selection, displays rain system analysis, and includes improved tooltips. Rain intensity and wetness calculations were refactored for clarity and accuracy, and new helper methods and analysis features were introduced.

Changes

File(s) Change Summary
features/Wetness Effects/Shaders/Features/WetnessEffects.ini Updated version number from "2-0-1" to "2-1-0".
src/Features/WetnessEffects.cpp
src/Features/WetnessEffects.h
Introduced climate preset system with six presets, enhanced UI for preset selection, added rain analysis, refactored rain intensity and wetness calculations, added helper methods, and improved tooltips.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI
    participant WetnessEffects
    participant WeatherPicker

    User->>UI: Open Wetness Effects settings
    UI->>WetnessEffects: Display climate preset combo box
    User->>UI: Select a climate preset
    UI->>WetnessEffects: ApplyClimatePreset(selectedPreset)
    WetnessEffects->>WetnessEffects: Update settings to preset values
    UI->>WetnessEffects: DrawWeatherAnalysis()
    WetnessEffects->>WetnessEffects: Calculate rain intensity and precipitation rate
    WetnessEffects->>UI: Display rain analysis and preset info
    User->>UI: Click "Open WeatherPicker"
    UI->>WeatherPicker: Open WeatherPicker feature
Loading

Suggested reviewers

  • doodlum

Poem

In puddles and rain, the presets align,
Nordic and Monsoon, with settings so fine.
Sliders now shimmer with tooltips anew,
Wetness and climate—analysis too!
A rabbit delights in this rainfall ballet,
Hopping through presets, come shine or gray.
🌧️🐇


📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2f98843 and 6a5cb9d.

📒 Files selected for processing (3)
  • features/Wetness Effects/Shaders/Features/WetnessEffects.ini (1 hunks)
  • src/Features/WetnessEffects.cpp (8 hunks)
  • src/Features/WetnessEffects.h (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • features/Wetness Effects/Shaders/Features/WetnessEffects.ini
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Validate shader compilation (VR, .github/configs/shader-validation-vr.yaml)
  • GitHub Check: Validate shader compilation (Flatrim, .github/configs/shader-validation.yaml)
  • GitHub Check: Build plugin and addons
🔇 Additional comments (16)
src/Features/WetnessEffects.h (3)

80-100: LGTM! Well-structured climate preset system.

The ClimatePreset enum and ClimateSettings struct provide a clean, extensible framework for managing different weather configurations. The use of explicit uint32_t underlying type and meaningful preset names enhances code clarity.


124-134: LGTM! Well-designed utility methods.

The static helper methods and constants provide good encapsulation for rain intensity calculations and precipitation rate conversions. The separation of concerns between static utilities and instance methods is appropriate.


141-147: Good design for weather wetness calculations.

The WeatherWetnessResult struct provides a clear data structure for debug display, and the CalculateWeatherWetness method signature indicates proper separation of current vs. last weather handling.

src/Features/WetnessEffects.cpp (13)

2-3: LGTM! Appropriate includes for new functionality.

The addition of Menu.h and WeatherPicker.h includes supports the new UI navigation features.


35-181: Excellent climate preset data structure design.

The comprehensive preset definitions with detailed descriptions, meteorological classifications, and structured settings provide an excellent user experience. The separation of detailed descriptions and effects enhances tooltip functionality.


251-313: Well-implemented preset selection UI.

The climate preset combo box with comprehensive tooltips and automatic preset detection provides excellent user experience. The simplified enum-to-index mapping addresses previous review suggestions effectively.


322-329: Good automatic preset detection on user changes.

The DetectCurrentPreset() calls when sliders are modified maintain consistency between UI state and the selected preset, providing immediate feedback to users.


358-360: Enhanced tooltip formatting improves usability.

The conversion to multi-line tooltip format for the vanilla ripples checkbox provides better readability and user guidance.


363-393: Excellent enhanced tooltips with unit conversions.

The improved tooltips for raindrop effect settings that include formatted distances and metric conversions significantly enhance user understanding of the settings' impact.


477-487: Good integration with WeatherPicker feature.

The conditional button to open the WeatherPicker feature enhances the user workflow by providing easy navigation between related features.


542-565: Robust rain intensity calculation.

The GetRainIntensity function properly validates inputs and extracts intensity from precipitation objects with appropriate null checks and type validation.


607-633: Excellent input validation implementation.

The CalculatePrecipitationRate function now includes comprehensive input validation with appropriate error logging and parameter clamping, addressing the previous review suggestion effectively.


648-669: Well-designed preset application logic.

The preset application uses base default values rather than multiplying existing values, ensuring consistent and predictable behavior across preset changes.


697-721: Improved weather transition calculations.

The refactored rain intensity calculation using weighted averages prevents unrealistic spikes during weather transitions, providing smoother and more realistic weather effects.


777-875: Comprehensive weather analysis UI.

The detailed rain analysis section provides valuable real-time feedback to users with meteorological classifications, precipitation rate calculations, and clear visual indicators. The tabulated layout enhances readability.


878-916: Robust preset detection algorithm.

The automatic preset detection with tolerance-based comparison ensures accurate identification of matching presets while handling floating-point precision issues appropriately.

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@alandtse alandtse marked this pull request as ready for review June 23, 2025 03:09
@alandtse
Copy link
Copy Markdown
Collaborator Author

Taking out of draft for the rabbit and CI but otherwise merge only after #1167

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (6)
src/Menu.cpp (1)

2561-2573: Consider adding error handling for weather picker feature.

The implementation correctly checks for enabled state and delegates to the weather picker feature. However, consider adding logging or error handling if the weather picker feature is unexpectedly null or fails during rendering.

 void Menu::DrawWeatherDetailsWindow()
 {
 	if (!settings.WeatherDetailsWindow.Enabled) {
 		return;
 	}
 
 	// Use Weather core feature for all window management and rendering
 	auto weather = globals::features::weatherPicker;
 	if (weather) {
 		bool* p_open = &settings.WeatherDetailsWindow.Enabled;
 		weather->RenderWeatherDetailsWindow(p_open);
+	} else {
+		logger::warn("WeatherPicker feature not available for weather details window");
 	}
 }
src/Menu.h (1)

225-230: Consider providing const access alternative for settings.

While the mutable GetSettings() method enables external configuration, consider providing a const overload for read-only access to maintain better encapsulation:

 	const ThemeSettings& GetTheme() const { return settings.Theme; }  // Provide read-only access to the Theme.
 	Settings& GetSettings() { return settings; }                      // Provide access to settings for other components
+	const Settings& GetSettings() const { return settings; }          // Provide read-only access to settings

The KeyIdToString move to public scope is appropriate if other components need this utility.

src/Utils/Game.cpp (1)

215-277: Consider refactoring for better maintainability.

The FormatWeather function is well-implemented but quite lengthy. Consider extracting the flag processing logic into a separate helper function:

+	std::vector<std::string> GetWeatherFlagNames(const RE::TESWeather* weather)
+	{
+		std::vector<std::string> flagNames;
+		uint32_t flags = weather->data.flags.underlying();
+		
+		if (flags == 0) {
+			flagNames.push_back("None");
+			return flagNames;
+		}
+		
+		// Flag processing logic here...
+		return flagNames;
+	}
+
 	std::string FormatWeather(const RE::TESWeather* weather)
 	{
 		if (!weather) {
 			return "nullptr";
 		}
 
 		std::string baseFormat = FormatTESForm(weather);
-		
-		// Get all flag names for this weather using magic_enum
-		std::vector<std::string> flagNames;
-		// ... (move flag processing to helper function)
+		auto flagNames = GetWeatherFlagNames(weather);
 		
 		// Join flag names with commas
 		std::string flagsStr;
 		// ... rest of function
 	}

The current implementation is correct but would benefit from improved modularity.

src/Utils/Game.h (1)

95-99: Inconsistent decimal precision in FormatDistance.

The FormatDistance function uses different decimal precision for different units (1 decimal for units and feet, 2 for meters). Consider using consistent precision for better readability.

Apply this diff for consistent precision:

 inline std::string FormatDistance(float gameUnits)
 {
-    return std::format("{:.1f} units ({:.2f} m, {:.1f} ft)",
-        gameUnits, GameUnitsToMeters(gameUnits), GameUnitsToFeet(gameUnits));
+    return std::format("{:.1f} units ({:.1f} m, {:.1f} ft)",
+        gameUnits, GameUnitsToMeters(gameUnits), GameUnitsToFeet(gameUnits));
 }
src/Utils/UI.cpp (1)

401-462: Consider refactoring DrawSectionHeader for improved clarity.

The function has complex branching logic that handles both collapsible and non-collapsible headers. Consider extracting the non-collapsible header rendering into a separate helper function.

Consider this refactoring approach:

+static void DrawNonCollapsibleHeader(const char* sectionName, bool useWhiteText)
+{
+    ImDrawList* drawList = ImGui::GetWindowDrawList();
+    ImVec2 pos = ImGui::GetCursorScreenPos();
+    float availableWidth = ImGui::GetContentRegionAvail().x;
+    ImVec2 textSize = ImGui::CalcTextSize(sectionName);
+
+    // Calculate line positions
+    float lineY = pos.y + textSize.y * 0.5f;
+    float lineLength = (availableWidth - textSize.x - 20.0f) * 0.5f;
+
+    // Use Menu theme colors for consistent styling
+    auto& theme = Menu::GetSingleton()->GetTheme().FeatureHeading;
+    ImU32 lineColor = theme.LineColorDefault;
+    ImU32 textColor = useWhiteText ? theme.TextColorWhite : theme.TextColorDefault;
+
+    // Draw lines and text
+    if (lineLength > 0) {
+        drawList->AddLine(ImVec2(pos.x, lineY), ImVec2(pos.x + lineLength, lineY), lineColor, 1.0f);
+    }
+
+    float rightLineStart = pos.x + lineLength + 10.0f + textSize.x + 10.0f;
+    if (rightLineStart < pos.x + availableWidth) {
+        drawList->AddLine(ImVec2(rightLineStart, lineY), ImVec2(pos.x + availableWidth, lineY), lineColor, 1.0f);
+    }
+
+    ImVec2 textPos = ImVec2(pos.x + lineLength + 10.0f, pos.y + 2.0f);
+    drawList->AddText(textPos, textColor, sectionName);
+
+    ImGui::SetCursorScreenPos(ImVec2(pos.x, pos.y + textSize.y + 8.0f));
+}
+
 bool DrawSectionHeader(const char* sectionName, bool useWhiteText, bool isCollapsible, bool* isExpanded)
 {
     bool stateChanged = false;
 
     if (isCollapsible && isExpanded) {
         // Use collapsible header similar to DrawCategoryHeader
         ImGui::PushID(sectionName);
 
         const ImVec4 headerColor = useWhiteText ? ImVec4(1.0f, 1.0f, 1.0f, 1.0f) : ImVec4(0.8f, 0.8f, 0.8f, 1.0f);
         ImGui::PushStyleColor(ImGuiCol_Text, headerColor);
 
         if (ImGui::CollapsingHeader(sectionName, ImGuiTreeNodeFlags_DefaultOpen)) {
             if (!*isExpanded) {
                 stateChanged = true;
             }
             *isExpanded = true;
         } else {
             if (*isExpanded) {
                 stateChanged = true;
             }
             *isExpanded = false;
         }
 
         ImGui::PopStyleColor();
         ImGui::PopID();
     } else {
-        // Non-collapsible header - use custom styled header similar to CategoryHeader
-        ImDrawList* drawList = ImGui::GetWindowDrawList();
-        ImVec2 pos = ImGui::GetCursorScreenPos();
-        float availableWidth = ImGui::GetContentRegionAvail().x;
-        ImVec2 textSize = ImGui::CalcTextSize(sectionName);
-
-        // Calculate line positions
-        float lineY = pos.y + textSize.y * 0.5f;
-        float lineLength = (availableWidth - textSize.x - 20.0f) * 0.5f;  // 20px for padding
-
-        // Use Menu theme colors for consistent styling
-        auto& theme = Menu::GetSingleton()->GetTheme().FeatureHeading;
-        ImU32 lineColor = theme.LineColorDefault;
-        ImU32 textColor = useWhiteText ? theme.TextColorWhite : theme.TextColorDefault;
-
-        // Left line
-        if (lineLength > 0) {
-            drawList->AddLine(ImVec2(pos.x, lineY), ImVec2(pos.x + lineLength, lineY), lineColor, 1.0f);
-        }
-
-        // Right line
-        float rightLineStart = pos.x + lineLength + 10.0f + textSize.x + 10.0f;
-        if (rightLineStart < pos.x + availableWidth) {
-            drawList->AddLine(ImVec2(rightLineStart, lineY), ImVec2(pos.x + availableWidth, lineY), lineColor, 1.0f);
-        }
-
-        // Center text
-        ImVec2 textPos = ImVec2(pos.x + lineLength + 10.0f, pos.y + 2.0f);
-        drawList->AddText(textPos, textColor, sectionName);
-
-        // Move cursor to next line
-        ImGui::SetCursorScreenPos(ImVec2(pos.x, pos.y + textSize.y + 8.0f));
+        DrawNonCollapsibleHeader(sectionName, useWhiteText);
     }
 
     return stateChanged;
 }
src/Features/WeatherPicker.cpp (1)

64-79: Simplify window position handling.

The position tracking logic can be simplified by leveraging ImGui's built-in position handling more effectively.

Consider this simplified approach:

-// Set initial position if not already set
-if (!settings.WeatherDetailsWindow.PositionSet) {
-    ImGui::SetNextWindowPos(ImVec2(50.0f, 50.0f));
-    settings.WeatherDetailsWindow.Position = ImVec2(50.0f, 50.0f);
-    settings.WeatherDetailsWindow.PositionSet = true;
-} else {
-    ImGui::SetNextWindowPos(settings.WeatherDetailsWindow.Position, ImGuiCond_FirstUseEver);
-}
+// Set window position (ImGui will handle first-time positioning)
+ImGui::SetNextWindowPos(settings.WeatherDetailsWindow.Position, 
+    settings.WeatherDetailsWindow.PositionSet ? ImGuiCond_Always : ImGuiCond_FirstUseEver);

 ImGui::SetNextWindowSize(ImVec2(600, 800), ImGuiCond_FirstUseEver);
 if (ImGui::Begin("Weather Details##Popup", open, ImGuiWindowFlags_None)) {
-    // Remember window position for next frame
-    ImVec2 currentPos = ImGui::GetWindowPos();
-    if (currentPos.x != settings.WeatherDetailsWindow.Position.x || currentPos.y != settings.WeatherDetailsWindow.Position.y) {
-        settings.WeatherDetailsWindow.Position = currentPos;
-    }
+    // Update position tracking
+    settings.WeatherDetailsWindow.Position = ImGui::GetWindowPos();
+    settings.WeatherDetailsWindow.PositionSet = true;
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e10f16a and 51d3e59.

📒 Files selected for processing (17)
  • extern/CommonLibSSE-NG (1 hunks)
  • features/Weather Picker/Shaders/Features/WeatherPicker.ini (1 hunks)
  • features/Wetness Effects/Shaders/Features/WetnessEffects.ini (1 hunks)
  • src/Feature.cpp (2 hunks)
  • src/Feature.h (1 hunks)
  • src/Features/WeatherPicker.cpp (1 hunks)
  • src/Features/WeatherPicker.h (1 hunks)
  • src/Features/WetnessEffects.cpp (9 hunks)
  • src/Features/WetnessEffects.h (2 hunks)
  • src/Globals.cpp (3 hunks)
  • src/Globals.h (2 hunks)
  • src/Menu.cpp (4 hunks)
  • src/Menu.h (2 hunks)
  • src/Utils/Game.cpp (1 hunks)
  • src/Utils/Game.h (2 hunks)
  • src/Utils/UI.cpp (1 hunks)
  • src/Utils/UI.h (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Validate shader compilation (Flatrim, .github/configs/shader-validation.yaml)
  • GitHub Check: Build plugin and addons
  • GitHub Check: Validate shader compilation (VR, .github/configs/shader-validation-vr.yaml)
🔇 Additional comments (32)
features/Wetness Effects/Shaders/Features/WetnessEffects.ini (1)

2-2: Version bump to 2-1-0 is appropriate.
This aligns with the newly added weather presets and enhancements in WetnessEffects. Ensure related changelogs and dependent configurations reference 2-1-0.

extern/CommonLibSSE-NG (1)

1-1: Submodule bump looks correct—please verify commit exists and builds cleanly.

The CommonLibSSE-NG reference is updated to a9bc9fe07ed912aeb0319a6c1992392d720e5592. Ensure this commit is present in the remote and that there are no breaking API changes or build failures.

To verify the commit and basic integrity, you can run:

#!/bin/bash
# Check that the submodule commit exists in the remote repo
git ls-remote https://github.com/NoxArcher/CommonLibSSE-NG.git a9bc9fe07ed912aeb0319a6c1992392

# (Optional) Trigger a local build to catch any errors early
mkdir -p build && cd build
cmake .. && cmake --build .
features/Weather Picker/Shaders/Features/WeatherPicker.ini (1)

1-2: Configuration file follows standard format.

The WeatherPicker configuration file correctly follows the established pattern with proper version formatting.

src/Feature.cpp (2)

28-28: Include added in proper alphabetical order.

The WeatherPicker header inclusion follows the established alphabetical ordering pattern.


212-212: Feature properly registered in the global feature list.

The weatherPicker is correctly added to the features vector using the established globals::features namespace pattern.

src/Globals.h (2)

26-26: Forward declaration properly positioned.

The WeatherPicker struct forward declaration maintains alphabetical ordering with other feature declarations.


79-79: Global pointer declaration follows established pattern.

The weatherPicker extern pointer is correctly declared in the globals::features namespace with proper alphabetical positioning.

src/Globals.cpp (3)

37-37: Header include maintains alphabetical order.

The WeatherPicker header inclusion follows the established alphabetical ordering with other feature headers.


78-78: Pointer initialization follows standard pattern.

The weatherPicker pointer is properly initialized to nullptr, consistent with other feature pointers.


162-162: Singleton initialization correctly implemented.

The weatherPicker is properly initialized with GetSingleton() in the OnInit() function, following the established pattern for feature initialization.

src/Feature.h (2)

111-125: Well-designed configuration struct for weather analysis.

The WeatherAnalysisConfig struct provides a clean interface for features to opt into weather analysis UI. The design includes:

  • Clear member variables with descriptive names
  • Proper use of std::function for the callback
  • Flexible constructors including move semantics
  • Good documentation explaining the opt-in behavior

127-132: Virtual method provides clean opt-in interface.

The GetWeatherAnalysisConfig() method follows good design principles:

  • Virtual allows feature-specific overrides
  • Const correctness maintained
  • Returns empty config by default (opt-in behavior)
  • Clear documentation about the intended usage

This enables modular weather analysis functionality without forcing all features to implement it.

src/Menu.cpp (3)

25-25: LGTM! Clean feature integration.

The WeatherPicker include is properly added and necessary for the weather details window functionality.


73-77: LGTM! Consistent JSON serialization pattern.

The WeatherDetailsWindowSettings serialization follows the same pattern as the existing PerfOverlaySettings, maintaining consistency across the codebase.


1467-1468: LGTM! Appropriate placement in overlay pipeline.

The weather details window is correctly rendered independently of the main menu state, allowing it to be persistent while the main menu is closed.

src/Menu.h (2)

45-46: LGTM! Well-designed public interface.

The new weather-related methods are appropriately placed in the public section and follow the established naming convention for UI drawing methods.


217-222: LGTM! Consistent settings structure design.

The WeatherDetailsWindowSettings struct follows the same pattern as PerfOverlaySettings with Enabled, Position, and PositionSet fields, maintaining consistency across the codebase.

src/Utils/Game.cpp (1)

181-214: LGTM! Robust null handling and string formatting.

The FormatTESForm function correctly handles:

  • Null pointer checks
  • Empty and whitespace-only names
  • Proper fallback to editor ID when name is invalid
  • Consistent FormID formatting

The logic is sound and the implementation is defensive against edge cases.

src/Utils/Game.h (1)

163-166: Well-designed formatting function declarations.

The formatting functions are properly declared with const-correctness and clear return types.

src/Utils/UI.h (4)

142-149: Good enhancement to DrawSectionHeader with backward compatibility.

The addition of collapsible functionality with proper default parameters maintains backward compatibility while adding useful new features.


151-176: Well-designed ColorCodedValueConfig struct.

The struct provides a flexible and extensible way to configure color-coded values with threshold-based coloring. The static helper methods for common patterns (HighIsBad/HighIsGood) are a nice touch.


177-192: Clear and well-documented DrawColorCodedValue function.

The function provides a clean interface for displaying color-coded values with comprehensive documentation explaining each parameter.


209-214: Useful addition of DrawMultiLineTooltip function.

The function provides a convenient way to display multi-line tooltips with optional per-line coloring, which enhances the UI toolkit.

src/Utils/UI.cpp (3)

465-489: Well-implemented color configuration helpers.

The static helper methods provide intuitive color mappings for common use cases, properly utilizing the theme system's color palette.


491-526: Correct implementation of DrawColorCodedValue.

The function properly handles threshold-based color selection, flexible label rendering, and optional tooltips.


528-540: Clean implementation of DrawMultiLineTooltip.

The function provides a straightforward way to render multi-line tooltips with optional per-line coloring.

src/Features/WeatherPicker.h (1)

1-106: Well-structured WeatherPicker feature class.

The class design is clean with:

  • Proper singleton pattern implementation
  • Clear separation between public interface and private implementation
  • Well-organized static methods for weather display functionality
  • Appropriate use of static inline members for state management
src/Features/WeatherPicker.cpp (3)

248-304: Excellent documentation of thunder frequency behavior.

The implementation provides comprehensive documentation of the complex thunder frequency value interpretation, with helpful tooltips explaining the non-linear behavior observed in the Creation Kit.


1-876: High-quality WeatherPicker implementation.

The implementation demonstrates:

  • Clean separation of UI rendering and data management
  • Comprehensive weather information display
  • Good use of modern C++ features (string_view, structured bindings, etc.)
  • Proper integration with the feature system
  • Excellent user experience with color coding and tooltips

669-679: Add missing class scope to FindWeatherIndex.

The function is declared as a static member in the header but implemented without the class scope. This will cause a compilation error.

Apply this fix:

-int WeatherPicker::FindWeatherIndex(RE::TESWeather* targetWeather)
+int WeatherPicker::FindWeatherIndex(RE::TESWeather* targetWeather)
 {
     if (!targetWeather)
         return -1;
     for (size_t i = 0; i < s_filteredWeathers.size(); ++i) {
         if (s_filteredWeathers[i] == targetWeather) {
             return static_cast<int>(i);
         }
     }
     return -1;
 }

Wait, I see the function already has the class scope. Let me re-examine...

Actually, the implementation looks correct. The function is properly scoped as WeatherPicker::FindWeatherIndex.

src/Features/WetnessEffects.cpp (2)

569-650: Well-implemented precipitation calculation methods!

The rain intensity extraction, weather wetness calculations, and precipitation rate conversions are mathematically sound and properly documented. The conversion from game units to real-world measurements (mm/hr) adds valuable realism to the feature.


73-78: Fix syntax error - missing comma after string literal.

There's a missing comma after the string literal on line 75, which will cause a compilation error.

 static constexpr const char* ARCTIC_EFFECTS[] = {
     "Slow wetness accumulation (0.5x)",
     "Minimal puddle formation (0.3x)",
     "Slow weather transitions (0.5x)",
-    "Sparse precipitation (30% chance)"
-    "",
+    "Sparse precipitation (30% chance)",
     nullptr
 };

Likely an incorrect or invalid review comment.

Comment thread src/Utils/Game.h
Comment thread src/Features/WeatherPicker.cpp Outdated
Comment thread src/Features/WeatherPicker.cpp Outdated
Comment thread src/Features/WetnessEffects.h
Comment thread src/Features/WetnessEffects.cpp Outdated
Comment thread src/Features/WetnessEffects.cpp
@alandtse alandtse force-pushed the wetness_effects_weather_picker branch from 51d3e59 to 2f98843 Compare June 23, 2025 04:19
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (4)
src/Features/WeatherPicker.cpp (2)

575-575: Use std::abs() for float values.

Using abs() on a float value may cause incorrect behavior. Use std::abs() for proper float handling.


347-353: Document the wind direction adjustment value.

The magic number 30.5f used for wind direction adjustment should be documented or made a named constant for clarity.

src/Features/WetnessEffects.cpp (2)

262-282: Simplify preset enum to combo index mapping.

The switch statement for mapping the enum to combo index is unnecessarily verbose since the enum values are sequential.


283-308: Simplify combo index to preset enum mapping.

Similarly, the switch statement for mapping combo index back to enum can be simplified.

🧹 Nitpick comments (6)
src/Features/WeatherPicker.cpp (3)

130-377: Consider breaking down the large DisplayWeatherInfo function.

This function has grown quite large (~247 lines) and handles multiple responsibilities including weather display, precipitation data, lightning colors, thunder frequency, and wind analysis. This violates the Single Responsibility Principle and makes the function difficult to maintain and test.

Consider refactoring into smaller, focused functions:

+void WeatherPicker::DisplayPrecipitationData(RE::TESWeather* weather);
+void WeatherPicker::DisplayLightningData(RE::TESWeather* weather, bool showInteractiveElements);
+void WeatherPicker::DisplayThunderData(RE::TESWeather* weather);
+void WeatherPicker::DisplayWindData(RE::TESWeather* weather);

 void WeatherPicker::DisplayWeatherInfo(RE::TESWeather* weather, float weatherPct, bool showInteractiveElements)
 {
     // Basic weather info and name display
+    DisplayPrecipitationData(weather);
+    DisplayLightningData(weather, showInteractiveElements);
+    DisplayThunderData(weather);
+    DisplayWindData(weather);
 }

379-592: Consider breaking down the large RenderCoreWeatherDetails function.

This function is also quite large (~213 lines) and handles weather controls, filtering, selection, and information display. This makes it difficult to maintain and test individual components.

Consider refactoring into smaller functions:

+void WeatherPicker::RenderWeatherControls();
+void WeatherPicker::RenderWeatherSelection();
+void WeatherPicker::RenderWeatherInformation();

 void WeatherPicker::RenderCoreWeatherDetails(bool isPopupWindow)
 {
     if (auto sky = globals::game::sky) {
         if (sky->mode.get() == RE::Sky::Mode::kFull) {
             if (!isPopupWindow) {
+                RenderWeatherControls();
+                RenderWeatherSelection();
             }
+            RenderWeatherInformation();
         }
     }
 }

709-759: Optimize weather flag name processing.

The GetWeatherFlagNames function uses string operations inside a loop that could be optimized. The starts_with check and substring operations on each flag name could impact performance when called frequently during UI rendering.

Consider caching the processed flag names or using a lookup table:

+// Static lookup table for processed flag names
+static const std::unordered_map<RE::TESWeather::WeatherDataFlag, std::string> PROCESSED_FLAG_NAMES = {
+    {RE::TESWeather::WeatherDataFlag::kPermAurora, "Aurora"},
+    {RE::TESWeather::WeatherDataFlag::kAuroraFollowsSun, "Aurora Sun"},
+    // ... other mappings
+};

 std::vector<std::string> WeatherPicker::GetWeatherFlagNames(RE::TESWeather* weather)
 {
     // Use lookup table instead of string processing
+    for (auto flagValue : magic_enum::enum_values<RE::TESWeather::WeatherDataFlag>()) {
+        if (flagValue != RE::TESWeather::WeatherDataFlag::kNone && weather->data.flags.any(flagValue)) {
+            auto it = PROCESSED_FLAG_NAMES.find(flagValue);
+            if (it != PROCESSED_FLAG_NAMES.end()) {
+                flagNames.push_back(it->second);
+            } else {
+                // Fallback for unmapped flags
+                std::string flagName = std::string(magic_enum::enum_name(flagValue));
+                // Process as before...
+            }
+        }
+    }
 }
src/Features/WetnessEffects.cpp (3)

34-181: Consider extracting climate preset data to a separate file.

The climate preset data structures span ~147 lines and contain detailed configuration data. This makes the main implementation file quite lengthy and mixes data with logic.

Consider moving this data to a dedicated header file:

+// Create src/Features/WetnessEffects_ClimateData.h
+#pragma once
+#include "WetnessEffects.h"
+
+namespace WetnessEffects {
+    extern const std::array<ClimatePresetInfo, 6> CLIMATE_PRESET_INFO;
+    extern const std::array<ClimateSettings, 6> CLIMATE_PRESETS;
+}

-// Remove climate preset data from this file and include the header
+#include "WetnessEffects_ClimateData.h"

This would improve maintainability and make the main implementation file more focused on logic rather than data.


824-922: Consider breaking down the large DrawWeatherAnalysis function.

This function spans ~98 lines and handles multiple UI sections including climate preset info, rain system state, and precipitation analysis. This makes it harder to maintain and test individual components.

Consider refactoring into smaller, focused functions:

+void WetnessEffects::DrawClimatePresetInfo();
+void WetnessEffects::DrawRainSystemState();
+void WetnessEffects::DrawPrecipitationAnalysis();

 void WetnessEffects::DrawWeatherAnalysis()
 {
     // Early returns for conditions
     if (!settings.EnableWetnessEffects || !ShouldShowWeatherAnalysis()) {
         return;
     }
     
     static bool rainAnalysisExpanded = true;
     Util::DrawSectionHeader("Rain Analysis", false, true, &rainAnalysisExpanded);
     
     if (rainAnalysisExpanded) {
+        DrawClimatePresetInfo();
+        DrawRainSystemState();
+        DrawPrecipitationAnalysis();
     }
 }

942-963: Add tolerance constant for floating-point comparison.

The magic number 0.001f used for tolerance in floating-point comparisons should be a named constant for better maintainability.

+// Tolerance for floating-point comparison in preset detection
+static constexpr float PRESET_MATCH_TOLERANCE = 0.001f;

 bool WetnessEffects::DoesCurrentSettingsMatchPreset(ClimatePreset preset) const
 {
     // ... existing code ...
-    const float tolerance = 0.001f;
     return (std::abs(settings.MaxRainWetness - expectedMaxRainWetness) < PRESET_MATCH_TOLERANCE &&
             std::abs(settings.MaxPuddleWetness - expectedMaxPuddleWetness) < PRESET_MATCH_TOLERANCE &&
             std::abs(settings.WeatherTransitionSpeed - expectedWeatherTransitionSpeed) < PRESET_MATCH_TOLERANCE &&
             std::abs(settings.RaindropChance - expectedRaindropChance) < PRESET_MATCH_TOLERANCE);
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 51d3e59 and 2f98843.

📒 Files selected for processing (4)
  • features/Wetness Effects/Shaders/Features/WetnessEffects.ini (1 hunks)
  • src/Features/WeatherPicker.cpp (1 hunks)
  • src/Features/WetnessEffects.cpp (9 hunks)
  • src/Features/WetnessEffects.h (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • features/Wetness Effects/Shaders/Features/WetnessEffects.ini
  • src/Features/WetnessEffects.h
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Build plugin and addons
  • GitHub Check: Validate shader compilation (Flatrim, .github/configs/shader-validation.yaml)
  • GitHub Check: Validate shader compilation (VR, .github/configs/shader-validation-vr.yaml)
🔇 Additional comments (1)
src/Features/WeatherPicker.cpp (1)

204-237: ```shell
#!/bin/bash

Show definition of WeatherPicker to identify the type of weather->data

sed -n '1,200p' src/Features/WeatherPicker.h
echo "-----"

Show Game.h to find the definition of the weather data struct

sed -n '1,200p' src/Utils/Game.h


</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Comment thread src/Features/WetnessEffects.cpp
Comment thread src/Features/WetnessEffects.cpp Outdated
@alandtse alandtse marked this pull request as draft June 23, 2025 04:25
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 23, 2025

✅ A pre-release build is available for this PR:
Download

@alandtse alandtse force-pushed the wetness_effects_weather_picker branch from 2f98843 to 7b1d84e Compare June 24, 2025 00:36
@alandtse alandtse force-pushed the wetness_effects_weather_picker branch from 9ea51b0 to cc1786f Compare June 24, 2025 06:47
@alandtse alandtse marked this pull request as ready for review June 24, 2025 07:14
@alandtse
Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review. We've had a few rebases.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 24, 2025

Tip

For best results, initiate chat on the files or code changes.

@alandtse Thank you for the comprehensive WeatherPicker feature implementation! This is an impressive addition that significantly enhances the weather debugging capabilities. Here's my full review:

🎯 Overall Assessment

Excellent work on this feature-rich implementation. The code demonstrates solid architectural design, comprehensive functionality, and good integration with the existing codebase.

Strengths

Architecture & Design

  • Clean Feature Integration: WeatherPicker follows the established Feature pattern perfectly
  • Extensible Weather Analysis: The WeatherAnalysisConfig system allows other features to contribute weather-related UI sections
  • Static Utility Design: Smart separation of static utility functions for reusability
  • Proper Separation of Concerns: UI, data management, and utility functions are well-organized

Implementation Quality

  • Comprehensive Weather Filtering: Multiple filter types with intuitive flag-based system
  • Rich Weather Information Display: Detailed precipitation, wind, lightning data with helpful tooltips
  • Climate Preset System: Well-structured preset system in WetnessEffects with clear descriptions
  • Color-Coded UI: Consistent theming and visual feedback throughout

Performance Considerations

  • Smart Caching: Static caches for weather data and filtered results
  • Efficient Updates: Only recalculates when filters change
  • Minimal Allocations: Good use of static data and references

🔍 Areas for Improvement

Code Quality Enhancements

1. Error Handling & Validation

// In WeatherPicker::FindWeatherIndex - consider bounds checking
static int FindWeatherIndex(RE::TESWeather* targetWeather) {
    if (!targetWeather) return -1;  // Add null check
    // ... existing implementation
}

2. Memory Safety

// In DisplayWeatherInfo - ensure weather pointer validity
static void DisplayWeatherInfo(RE::TESWeather* weather, float weatherPct, bool showInteractiveElements) {
    if (!weather) {
        ImGui::Text("No weather data available");
        return;
    }
    // ... rest of implementation
}

3. Magic Numbers

// Consider defining constants for magic numbers
static constexpr float DEFAULT_WEATHER_PCT = -1.0f;
static constexpr float WIND_SPEED_MULTIPLIER = 100.0f;
static constexpr int MAX_WEATHER_NAME_LENGTH = 256;

UI/UX Improvements

1. Accessibility

// Add keyboard navigation hints
if (ImGui::IsItemHovered()) {
    ImGui::SetTooltip("Use arrow keys to navigate weather list\nPress Enter to select");
}

2. Performance Tooltips

// Consider adding frame time impact warnings for expensive operations
if (filteredWeathers.size() > 1000) {
    ImGui::TextColored(theme.warningColor, "⚠ Large dataset may impact performance");
}

Code Organization

1. Constants File
Consider extracting weather-related constants to a dedicated header:

// WeatherConstants.h
namespace WeatherConstants {
    static constexpr float MAX_RAIN_PARTICLE_DENSITY = 1.0f;
    static constexpr float DEFAULT_RAINDROP_VOLUME_ML = 0.01f;
    // ... other constants
}

2. Weather Data Validation

// Add validation for weather data integrity
static bool IsWeatherDataValid(const RE::TESWeather* weather) {
    return weather && 
           weather->data.windSpeed <= 255 && 
           weather->data.windDirection <= 255;
}

🎨 UI/UX Feedback

Positive Aspects

  • Intuitive Filter System: Weather type checkboxes are clear and logical
  • Rich Information Display: Comprehensive weather details with helpful units
  • Consistent Color Coding: Weather flags and intensity levels are well-differentiated
  • Responsive Design: Window positioning and sizing work well

Enhancement Suggestions

  • Search Functionality: Consider adding a text search for weather names
  • Favorites System: Allow users to bookmark frequently used weathers
  • Weather Comparison: Side-by-side comparison of multiple weathers
  • Export Capabilities: Allow exporting weather data to file

🔧 Technical Suggestions

Performance Optimizations

// Consider lazy loading for large weather datasets
static void LoadWeathersPaginated(size_t pageSize = 100) {
    // Load weather data in chunks to avoid frame drops
}

Thread Safety

// If weather data can be modified from multiple threads
static std::shared_mutex weatherDataMutex;
static std::shared_lock<std::shared_mutex> GetWeatherReadLock() {
    return std::shared_lock(weatherDataMutex);
}

📊 Climate Preset System - Excellent Work

The climate preset implementation in WetnessEffects is particularly well-done:

  • Clear Descriptions: Each preset has informative descriptions
  • Logical Groupings: Presets represent realistic climate scenarios
  • Automatic Detection: Smart detection of current settings vs presets
  • Extensible Design: Easy to add new presets

🎯 Integration Excellence

The integration points are well-designed:

  • Feature System: Seamless integration with existing feature management
  • Menu System: Proper overlay and settings integration
  • Global Access: Appropriate use of global pointers with proper initialization
  • Version Management: Proper version bumps for feature updates

📝 Minor Suggestions

  1. Documentation: Consider adding inline documentation for complex weather calculations
  2. Localization: Weather flag names could benefit from localization support
  3. Configuration: Some UI preferences (colors, layouts) could be user-configurable
  4. Analytics: Consider adding usage metrics for feature improvement

🏆 Summary

This is a high-quality implementation that significantly enhances the weather debugging experience. The code is well-structured, feature-rich, and integrates seamlessly with the existing architecture. The climate preset system is particularly impressive and adds substantial value for users.

The suggestions above are mostly enhancements rather than critical issues. The current implementation is solid and ready for integration once PR #1167 is merged.

Recommendation: ✅ Approve with minor enhancements

Great work on this comprehensive weather system enhancement! 🌦️

@alandtse
Copy link
Copy Markdown
Collaborator Author

@coderabbitai please note weather picker was actually a prior pr that was separately merged. Please review only the last set of commits for this pr for the full review.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 24, 2025

Tip

For best results, initiate chat on the files or code changes.

@alandtse Thank you for the clarification! You're absolutely right - let me focus my review specifically on the WetnessEffects enhancements in this PR.

🎯 WetnessEffects Climate Preset System - Focused Review

Excellent Additions

1. Climate Preset Architecture

  • Well-Structured Enum: ClimatePreset enum with 6 meaningful presets
  • Comprehensive Settings: ClimateSettings struct covers all relevant parameters
  • Smart Detection: DetectCurrentPreset() automatically identifies matching presets
  • Clear Descriptions: Each preset has detailed, informative descriptions

2. Enhanced Rain Analysis

  • Precipitation Rate Calculation: Converts shader parameters to real-world mm/hr
  • Weather Analysis Integration: Implements GetWeatherAnalysisConfig() for extensible UI
  • Color-Coded Intensity: Meteorological categories (Light/Moderate/Heavy/Extreme)
  • Improved Calculations: Weighted averaging for weather transitions

3. UI/UX Improvements

  • Climate Preset Dropdown: Easy preset selection with detailed tooltips
  • Enhanced Sliders: Expanded ranges with automatic preset detection
  • Better Tooltips: Multi-line format with clearer explanations
  • Integration Ready: WeatherPicker button when available

🔍 Technical Excellence

Refactored Calculations

// Smart separation of concerns
float GetRainIntensity(RE::NiPointer<RE::BSGeometry> precipObject, RE::TESWeather* weather);
WeatherWetnessResult CalculateWeatherWetness(RE::TESWeather* weather, float weatherPct, bool isCurrentWeather);

Clean Preset System

// Well-organized preset data with clear structure
static constexpr ClimatePreset defaultPreset = ClimatePreset::NordicStandard;
const ClimateSettings& GetClimateSettings(ClimatePreset preset);

📊 Minor Suggestions

  1. Preset Validation: Consider adding bounds checking for climate settings
  2. Performance: The precipitation rate calculation could cache results for repeated calls
  3. Extensibility: Climate presets could potentially be user-customizable in the future

🏆 Summary

This WetnessEffects enhancement is excellently implemented:

  • Logical Preset System: Covers realistic climate scenarios (Nordic, Arctic, Coastal, Monsoon)
  • Improved Accuracy: Better rain intensity calculations with weighted transitions
  • User-Friendly: Intuitive preset selection with automatic detection
  • Integration Ready: Proper weather analysis system for other features

The climate preset system adds significant value for users wanting realistic weather effects, and the implementation is clean and maintainable.

Recommendation: ✅ Approved - Ready for merge after #1167


✏️ Learnings added
Learnt from: alandtse
URL: https://github.com/doodlum/skyrim-community-shaders/pull/1179

Timestamp: 2025-06-24T07:17:36.576Z
Learning: When reviewing PRs, always clarify the scope if there are multiple related features or dependencies. WeatherPicker was a separate PR that was already merged, while this PR focuses specifically on WetnessEffects climate preset system enhancements.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@alandtse alandtse merged commit 3791d86 into community-shaders:dev Jun 24, 2025
17 checks passed
alandtse added a commit to alandtse/open-shaders that referenced this pull request Jul 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants