feat(wetness): add weather presets#1179
Conversation
WalkthroughA 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
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
Suggested reviewers
Poem
📜 Recent review detailsConfiguration used: .coderabbit.yaml 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (1)
⏰ Context from checks skipped due to timeout of 90000ms (3)
🔇 Additional comments (16)
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed 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)
Other keywords and placeholders
Documentation and Community
|
|
Taking out of draft for the rabbit and CI but otherwise merge only after #1167 |
There was a problem hiding this comment.
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 settingsThe 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
📒 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.
51d3e59 to
2f98843
Compare
There was a problem hiding this comment.
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. Usestd::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
GetWeatherFlagNamesfunction uses string operations inside a loop that could be optimized. Thestarts_withcheck 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.001fused 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
📒 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/bashShow 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 -->
|
✅ A pre-release build is available for this PR: |
2f98843 to
7b1d84e
Compare
9ea51b0 to
cc1786f
Compare
|
@coderabbitai full review. We've had a few rebases. |
|
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 AssessmentExcellent work on this feature-rich implementation. The code demonstrates solid architectural design, comprehensive functionality, and good integration with the existing codebase. ✅ StrengthsArchitecture & Design
Implementation Quality
Performance Considerations
🔍 Areas for ImprovementCode Quality Enhancements1. 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 Improvements1. 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 Organization1. Constants File // 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 FeedbackPositive Aspects
Enhancement Suggestions
🔧 Technical SuggestionsPerformance 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 WorkThe climate preset implementation in WetnessEffects is particularly well-done:
🎯 Integration ExcellenceThe integration points are well-designed:
📝 Minor Suggestions
🏆 SummaryThis 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! 🌦️ |
|
@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. |
|
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 Additions1. Climate Preset Architecture
2. Enhanced Rain Analysis
3. UI/UX Improvements
🔍 Technical ExcellenceRefactored 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
🏆 SummaryThis WetnessEffects enhancement is excellently implemented:
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
|
Depends on #1167
Summary by CodeRabbit
New Features
Improvements