Skip to content
Open
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
24 changes: 24 additions & 0 deletions Common/ModSystems/ModIntegrationsSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,30 @@ public static object Call(params object[] args)
}
return false;
}
// 注册一个气候控制项
case "RegisterWeatherControl":
return Content.Functions.WeatherControl.WeatherControlCrossMod.RegisterControl(args);
// 移除一个气候控制项
case "UnregisterWeatherControl":
return Content.Functions.WeatherControl.WeatherControlCrossMod.UnregisterControl(args);
// 注册一个自定义内容栏
case "RegisterWeatherControlGroup":
return Content.Functions.WeatherControl.WeatherControlCrossMod.RegisterGroup(args);
// 移除一个自定义内容栏
case "UnregisterWeatherControlGroup":
return Content.Functions.WeatherControl.WeatherControlCrossMod.UnregisterGroup(args);
// 查询某控制项的当前档位,未知返回 -1
case "QueryWeatherControlStage":
return Content.Functions.WeatherControl.WeatherControlCrossMod.QueryStage(args[1] as string);
// 派发档位变更
case "SetWeatherControlStage":
return Content.Functions.WeatherControl.WeatherControlCrossMod.DispatchStage(args[1] as string, Convert.ToInt32(args[2]));
// 查询某控制项是否锁定
case "IsWeatherControlLocked":
return Content.Functions.WeatherControl.WeatherControlCrossMod.QueryLocked(args[1] as string);
// 派发锁定状态变更
case "SetWeatherControlLocked":
return Content.Functions.WeatherControl.WeatherControlCrossMod.DispatchLocked(args[1] as string, Convert.ToBoolean(args[2]));
default:
ImproveGame.Instance.Logger.Error($"Replacement type \"{msg}\" not found.");
return false;
Expand Down
63 changes: 63 additions & 0 deletions Content/Functions/WeatherControl/IWeatherControl.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
namespace ImproveGame.Content.Functions.WeatherControl;

/// <summary>
/// 一个气候控制项的抽象,内置项与外部模组项都实现这个接口
/// </summary>
public interface IWeatherControl
{
/// <summary> 注册源 Mod 的内部名 </summary>
string ModName { get; }

/// <summary> 项目的内部名 </summary>
string Name { get; }

/// <summary> 唯一标识,等于 ModName 加冒号加 Name </summary>
string Id => $"{ModName}:{Name}";

/// <summary> 槽位图标 </summary>
Texture2D Icon { get; }

/// <summary>
/// 所属内容栏的 Id,为空则归到默认的模组项网格
/// </summary>
string GroupId { get; }

/// <summary> 排序优先级,越高越靠前 </summary>
int Priority { get; }

/// <summary>
/// 档位名数组,至少包含两个
/// 名字是模组内部用的英文键,不直接显示
/// </summary>
IReadOnlyList<string> Stages { get; }

/// <summary> 是否支持锁定 </summary>
bool SupportsLock { get; }

/// <summary> 当前是否可用,不可用时 UI 灰显或隐藏 </summary>
bool IsAvailable { get; }

/// <summary> 显示名 </summary>
string GetDisplayName();

/// <summary> 悬停说明,可返回 null </summary>
string GetTooltip();

/// <summary> 取当前档位下标,未知返回 -1 </summary>
int GetStage();

/// <summary>
/// 应用一个档位到本地
/// 这一层不处理网络同步,网络同步在 <see cref="WeatherControlRegistry"/> 的派发层完成
/// </summary>
void SetStage(int stage);

/// <summary> 取当前是否锁定 </summary>
bool GetLocked();

/// <summary>
/// 应用锁定状态到本地
/// 这一层不处理网络同步
/// </summary>
void SetLocked(bool locked);
}
32 changes: 32 additions & 0 deletions Content/Functions/WeatherControl/IWeatherControlGroup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
namespace ImproveGame.Content.Functions.WeatherControl;

/// <summary>
/// 气候控制面板里的一整栏内容
/// 内置艺术画面与模组项网格各是一栏,外部 Mod 可以注册自己的栏
/// </summary>
public interface IWeatherControlGroup
{
/// <summary> 注册源 Mod 的内部名 </summary>
string ModName { get; }

/// <summary> 内容栏的内部名 </summary>
string Name { get; }

/// <summary> 唯一标识 </summary>
string Id => $"{ModName}:{Name}";

/// <summary> 排序优先级,越高越靠前 </summary>
int Priority { get; }

/// <summary> 该栏当前是否可用 </summary>
bool IsAvailable { get; }

/// <summary> 显示名 </summary>
string GetDisplayName();

/// <summary>
/// 创建该栏的视图,每次 WeatherGUI 打开时调用一次
/// 返回的 UIElement 会作为子元素挂到主面板下
/// </summary>
UIElement CreateView(IReadOnlyList<IWeatherControl> controls);
}
169 changes: 169 additions & 0 deletions Content/Functions/WeatherControl/WeatherBuiltinControls.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
using Terraria.GameContent.Events;

namespace ImproveGame.Content.Functions.WeatherControl;

/// <summary>
/// 把内置 5 项注册进 <see cref="WeatherControlRegistry"/>,让外部 Mod 也能查询和操作它们
/// </summary>
internal static class WeatherBuiltinControls
{
public const string TimeId = "Time";
public const string MoonPhaseId = "MoonPhase";
public const string RainId = "Rain";
public const string SandstormId = "Sandstorm";
public const string WindId = "Wind";

public static string FullId(string name) => $"{WeatherControlRegistry.BuiltinModName}:{name}";

public static void RegisterAll(WeatherControlRegistry registry)
{
string groupId = WeatherControlRegistry.BuiltinGroupId;

registry.Register(new WeatherControlInfo
{
ModName = WeatherControlRegistry.BuiltinModName,
Name = TimeId,
GroupId = groupId,
Priority = 50,
Icon = ModAsset.ClockHighlight.Value,
Stages = ["Dawn", "Noon", "Dusk", "Midnight"],
SupportsLock = false,
DisplayNameProvider = () => GetText("UI.WeatherGUI.Time"),
AvailableProvider = () => Main.hardMode,
StageProvider = () => -1,
StageSetter = ApplyTime,
});

registry.Register(new WeatherControlInfo
{
ModName = WeatherControlRegistry.BuiltinModName,
Name = MoonPhaseId,
GroupId = groupId,
Priority = 40,
Icon = ModAsset.MoonPhaseHighlight.Value,
Stages = ["Phase0", "Phase1", "Phase2", "Phase3", "Phase4", "Phase5", "Phase6", "Phase7"],
SupportsLock = true,
DisplayNameProvider = () => GetText("UI.WeatherGUI.MoonPhase"),
StageProvider = () => Main.moonPhase,
StageSetter = ApplyMoonPhase,
LockedProvider = () => WeatherController.MoonPhaseLocked,
LockedSetter = v => WeatherController.MoonPhaseLocked = v,
});

registry.Register(new WeatherControlInfo
{
ModName = WeatherControlRegistry.BuiltinModName,
Name = RainId,
GroupId = groupId,
Priority = 30,
Icon = ModAsset.RainActive.Value,
Stages = ["Off", "On"],
SupportsLock = true,
DisplayNameProvider = () => GetText("UI.WeatherGUI." + (Main.raining ? "RainInactive" : "RainActive")),
StageProvider = () => Main.raining ? 1 : 0,
StageSetter = ApplyRain,
LockedProvider = () => WeatherController.RainLocked,
LockedSetter = v => WeatherController.RainLocked = v,
});

registry.Register(new WeatherControlInfo
{
ModName = WeatherControlRegistry.BuiltinModName,
Name = SandstormId,
GroupId = groupId,
Priority = 20,
Icon = ModAsset.SandstormActive.Value,
Stages = ["Off", "On"],
SupportsLock = true,
DisplayNameProvider = () => GetText("UI.WeatherGUI." + (Sandstorm.Happening ? "SandstormInactive" : "SandstormActive")),
StageProvider = () => Sandstorm.Happening ? 1 : 0,
StageSetter = ApplySandstorm,
LockedProvider = () => WeatherController.SandstormLocked,
LockedSetter = v => WeatherController.SandstormLocked = v,
});

registry.Register(new WeatherControlInfo
{
ModName = WeatherControlRegistry.BuiltinModName,
Name = WindId,
GroupId = groupId,
Priority = 10,
Icon = ModAsset.Wheel.Value,
Stages = ["West", "No", "East"],
SupportsLock = true,
DisplayNameProvider = () => GetText("UI.WeatherGUI.Wind"),
StageProvider = GetWindStage,
StageSetter = ApplyWind,
LockedProvider = () => WeatherController.WindLocked,
LockedSetter = v => WeatherController.WindLocked = v,
});
}

private static void ApplyTime(int stage)
{
switch (stage)
{
case 0: Main.SkipToTime(0, setIsDayTime: true); break;
case 1: Main.SkipToTime(27000, setIsDayTime: true); break;
case 2: Main.SkipToTime(0, setIsDayTime: false); break;
case 3: Main.SkipToTime(16200, setIsDayTime: false); break;
}
}

private static void ApplyMoonPhase(int stage)
{
if (stage < 0) stage = 0;
Main.moonPhase = stage % 8;
}

private static void ApplyRain(int stage)
{
bool target = stage != 0;
if (target == Main.raining) return;

if (target)
{
Main.StartRain();
Main.cloudAlpha = 0.7f;
Main.maxRaining = 0.7f;
}
else
{
Main.StopRain();
Main.cloudAlpha = 0f;
Main.maxRaining = 0f;
}
}

private static void ApplySandstorm(int stage)
{
bool target = stage != 0;
if (target == Sandstorm.Happening) return;

if (target)
Sandstorm.StartSandstorm();
else
Sandstorm.StopSandstorm();
}

private static int GetWindStage()
{
// 与原版 SetWindPacket 的 stage 数值对齐
// 0=West, 1=No, 2=East 三档,无风段按绝对值阈值划分
float w = Main.windSpeedCurrent;
if (w >= 0.4f) return 0;
if (w <= -0.4f) return 2;
return 1;
}

private static void ApplyWind(int stage)
{
float v = stage switch
{
0 => 0.61f,
2 => -0.61f,
_ => Main.rand.NextFloat(-0.04f, 0.04f)
};
Main.windSpeedCurrent = Main.windSpeedTarget = v;
}
}
Loading