Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 38 additions & 3 deletions docs/weather-system-docs/WeatherVariableRegistration.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,49 @@ void MyFeature::RegisterWeatherVariables() override
}
```

#### Step 3: Implementation Complete
#### Step 3: Update DrawSettings() to Use Weather-Aware UI Controls

For weather-controlled settings, use the `Util::WeatherUI` helpers instead of direct ImGui calls. This automatically greys out controls when a per-weather override is active and displays tooltips:

```cpp
void MyFeature::DrawSettings()
{
// Weather-aware slider (will be disabled if current weather overrides it)
Util::WeatherUI::SliderFloat("Effect Intensity", this, "Intensity",
&settings.intensity, 0.0f, 2.0f, "%.2f");

// Weather-aware color picker
Util::WeatherUI::ColorEdit3("Effect Color", this, "Color",
(float*)&settings.color);

// Regular checkbox (not weather-controlled in this example)
ImGui::Checkbox("Enable Effect", (bool*)&settings.enabled);
}
```

**Available Weather-Aware Helpers:**

- `Util::WeatherUI::SliderFloat()` - Float slider with min/max
- `Util::WeatherUI::Checkbox()` - Boolean checkbox
- `Util::WeatherUI::ColorEdit3()` - RGB color picker
- `Util::WeatherUI::ColorEdit4()` - RGBA color picker with alpha

**Why Use These?**

- Automatically detects if the current weather has overridden the setting
- Disables and greys out the control to show it's weather-controlled
- Shows tooltip: "Weather Override Active - This setting is controlled by the current weather (WeatherName)"
- Prevents confusion when editing global settings that are overridden by weather

#### Step 4: Implementation Complete

The system now automatically:

- Saves/loads weather-specific settings to JSON
- Interpolates variables during weather transitions
- Appears in the weather editor UI
- Handles default values and missing dataanced Usage
- Appears in the weather editor UI with per-weather toggle buttons
- Handles default values and missing data
- Shows weather-controlled status in feature settings UI

### Custom Variable Types

Expand Down
4 changes: 3 additions & 1 deletion src/Feature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
#include "Menu.h"
#include "SettingsOverrideManager.h"
#include "Utils/Format.h"
#include "WeatherManager.h"
#include "WeatherVariableRegistry.h"

#include "State.h"

Expand Down Expand Up @@ -362,4 +364,4 @@ bool Feature::IsFeatureKnown(const std::string& shortName, REL::Version* outVers
}

return false;
}
}
249 changes: 249 additions & 0 deletions src/Utils/UI.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
#include "UI.h"

#include "../WeatherEditor/EditorWindow.h"
#include "FileSystem.h"
#include "Menu.h"
#include "Menu/IconLoader.h"
#include "WeatherManager.h"
#include "WeatherVariableRegistry.h"

#ifndef DIRECTINPUT_VERSION
# define DIRECTINPUT_VERSION 0x0800
Expand Down Expand Up @@ -1329,4 +1332,250 @@ namespace Util
return clicked;
}

namespace WeatherUI
{
bool IsWeatherControlled(Feature* feature, const char* settingName)
{
if (!feature || !settingName) {
return false;
}

auto* globalRegistry = WeatherVariables::GlobalWeatherRegistry::GetSingleton();
auto* weatherManager = WeatherManager::GetSingleton();

// Check if this feature has registered weather variables
std::string featureName = feature->GetShortName();
if (!globalRegistry->HasWeatherSupport(featureName)) {
return false;
}

// Check if current weather exists
auto currentWeathers = weatherManager->GetCurrentWeathers();
if (!currentWeathers.currentWeather) {
return false;
}

// Load weather settings for this feature
json weatherSettings;
if (!weatherManager->LoadSettingsFromWeather(currentWeathers.currentWeather, featureName, weatherSettings)) {
return false;
}

// Check if this specific setting has an override
return weatherSettings.contains(settingName) && !weatherSettings[settingName].is_null();
}

bool SliderFloat(const char* label, Feature* feature, const char* settingName, float* value, float min, float max, const char* format)
{
bool isControlled = IsWeatherControlled(feature, settingName);

if (isControlled) {
auto* weatherManager = WeatherManager::GetSingleton();
auto currentWeathers = weatherManager->GetCurrentWeathers();

// Make it look like a clickable button when weather-controlled
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.3f, 0.3f, 0.4f, 0.8f));
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(0.4f, 0.4f, 0.5f, 0.9f));
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImVec4(0.5f, 0.5f, 0.6f, 1.0f));
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.7f);
}

ImGuiSliderFlags flags = isControlled ? (static_cast<ImGuiSliderFlags>(ImGuiSliderFlags_NoInput) | static_cast<ImGuiSliderFlags>(ImGuiSliderFlags_ReadOnly)) : ImGuiSliderFlags_None;
bool changed = ImGui::SliderFloat(label, value, min, max, format, flags);

if (isControlled) {
ImGui::PopStyleVar();
ImGui::PopStyleColor(3);

// Check if clicked
if (ImGui::IsItemClicked()) {
auto* weatherManager = WeatherManager::GetSingleton();
auto* editorWindow = EditorWindow::GetSingleton();
auto currentWeathers = weatherManager->GetCurrentWeathers();

if (currentWeathers.currentWeather && editorWindow) {
editorWindow->OpenWeatherFeatureSetting(
currentWeathers.currentWeather,
feature->GetShortName(),
settingName);
}
}

if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
ImGui::BeginTooltip();
auto* weatherManager = WeatherManager::GetSingleton();
auto currentWeathers = weatherManager->GetCurrentWeathers();
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f), "Weather Override Active");
ImGui::TextWrapped("This setting is controlled by the current weather (%s).",
currentWeathers.currentWeather ? currentWeathers.currentWeather->GetFormEditorID() : "Unknown");
ImGui::Separator();
ImGui::TextColored(ImVec4(0.6f, 0.9f, 0.6f, 1.0f), "Click to open Weather Editor");
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}

return false; // Prevent changes when weather-controlled
}

return changed;
}

bool Checkbox(const char* label, Feature* feature, const char* settingName, bool* value)
{
bool isControlled = IsWeatherControlled(feature, settingName);

if (isControlled) {
auto* weatherManager = WeatherManager::GetSingleton();
auto currentWeathers = weatherManager->GetCurrentWeathers();

ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.3f, 0.3f, 0.4f, 0.8f));
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImVec4(0.4f, 0.4f, 0.5f, 0.9f));
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.7f);
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
}

bool changed = ImGui::Checkbox(label, value);

if (isControlled) {
ImGui::PopItemFlag();
ImGui::PopStyleVar();
ImGui::PopStyleColor(2);

if (ImGui::IsItemClicked()) {
auto* weatherManager = WeatherManager::GetSingleton();
auto* editorWindow = EditorWindow::GetSingleton();
auto currentWeathers = weatherManager->GetCurrentWeathers();

if (currentWeathers.currentWeather && editorWindow) {
editorWindow->OpenWeatherFeatureSetting(
currentWeathers.currentWeather,
feature->GetShortName(),
settingName);
}
}

if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
ImGui::BeginTooltip();
auto* weatherManager = WeatherManager::GetSingleton();
auto currentWeathers = weatherManager->GetCurrentWeathers();
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f), "Weather Override Active");
ImGui::TextWrapped("This setting is controlled by the current weather (%s).",
currentWeathers.currentWeather ? currentWeathers.currentWeather->GetFormEditorID() : "Unknown");
ImGui::Separator();
ImGui::TextColored(ImVec4(0.6f, 0.9f, 0.6f, 1.0f), "Click to open Weather Editor");
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}

return false;
}

return changed;
}

bool ColorEdit3(const char* label, Feature* feature, const char* settingName, float col[3])
{
bool isControlled = IsWeatherControlled(feature, settingName);

if (isControlled) {
auto* weatherManager = WeatherManager::GetSingleton();
auto currentWeathers = weatherManager->GetCurrentWeathers();

ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.7f);
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
}

bool changed = ImGui::ColorEdit3(label, col);

if (isControlled) {
ImGui::PopItemFlag();
ImGui::PopStyleVar();

if (ImGui::IsItemClicked()) {
auto* weatherManager = WeatherManager::GetSingleton();
auto* editorWindow = EditorWindow::GetSingleton();
auto currentWeathers = weatherManager->GetCurrentWeathers();

if (currentWeathers.currentWeather && editorWindow) {
editorWindow->OpenWeatherFeatureSetting(
currentWeathers.currentWeather,
feature->GetShortName(),
settingName);
}
}

if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
ImGui::BeginTooltip();
auto* weatherManager = WeatherManager::GetSingleton();
auto currentWeathers = weatherManager->GetCurrentWeathers();
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f), "Weather Override Active");
ImGui::TextWrapped("This setting is controlled by the current weather (%s).",
currentWeathers.currentWeather ? currentWeathers.currentWeather->GetFormEditorID() : "Unknown");
ImGui::Separator();
ImGui::TextColored(ImVec4(0.6f, 0.9f, 0.6f, 1.0f), "Click to open Weather Editor");
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}

return false;
}

return changed;
}

bool ColorEdit4(const char* label, Feature* feature, const char* settingName, float col[4])
{
bool isControlled = IsWeatherControlled(feature, settingName);

if (isControlled) {
auto* weatherManager = WeatherManager::GetSingleton();
auto currentWeathers = weatherManager->GetCurrentWeathers();

ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.7f);
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
}

bool changed = ImGui::ColorEdit4(label, col);

if (isControlled) {
ImGui::PopItemFlag();
ImGui::PopStyleVar();

if (ImGui::IsItemClicked()) {
auto* weatherManager = WeatherManager::GetSingleton();
auto* editorWindow = EditorWindow::GetSingleton();
auto currentWeathers = weatherManager->GetCurrentWeathers();

if (currentWeathers.currentWeather && editorWindow) {
editorWindow->OpenWeatherFeatureSetting(
currentWeathers.currentWeather,
feature->GetShortName(),
settingName);
}
}

if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled)) {
ImGui::BeginTooltip();
auto* weatherManager = WeatherManager::GetSingleton();
auto currentWeathers = weatherManager->GetCurrentWeathers();
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.2f, 1.0f), "Weather Override Active");
ImGui::TextWrapped("This setting is controlled by the current weather (%s).",
currentWeathers.currentWeather ? currentWeathers.currentWeather->GetFormEditorID() : "Unknown");
ImGui::Separator();
ImGui::TextColored(ImVec4(0.6f, 0.9f, 0.6f, 1.0f), "Click to open Weather Editor");
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}

return false;
}

return changed;
}
}

} // namespace Util
40 changes: 40 additions & 0 deletions src/Utils/UI.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,46 @@ namespace Util
*/
float GetCenterOffsetForContent(float contentWidth);

/**
* Weather-controlled UI helpers
* These functions automatically check if a setting has a weather-specific override
* and disable the control if it's being controlled by the current weather
*/
namespace WeatherUI
{
/**
* Check if a specific setting is currently controlled by weather
* @param feature The feature to check
* @param settingName The name of the setting (must match registered weather variable name)
* @return True if weather is overriding this setting
*/
bool IsWeatherControlled(Feature* feature, const char* settingName);

/**
* Weather-aware slider float that greys out when controlled by weather
* @param label The label for the slider
* @param feature The feature this setting belongs to
* @param settingName The name of the setting (must match registered weather variable name)
* @param value Pointer to the value
* @param min Minimum value
* @param max Maximum value
* @param format Display format
* @return True if value was changed (only possible when not weather-controlled)
*/
bool SliderFloat(const char* label, Feature* feature, const char* settingName, float* value, float min, float max, const char* format = "%.3f");

/**
* Weather-aware checkbox that greys out when controlled by weather
*/
bool Checkbox(const char* label, Feature* feature, const char* settingName, bool* value);

/**
* Weather-aware color edit that greys out when controlled by weather
*/
bool ColorEdit3(const char* label, Feature* feature, const char* settingName, float col[3]);
bool ColorEdit4(const char* label, Feature* feature, const char* settingName, float col[4]);
}

/**
* Draws a custom styled collapsible category header with lines extending from both sides
* @param categoryName The name of the category to display
Expand Down
Loading