feat(WulaFallenEmpire): 新增自定义 UI 布局并优化事件系统

- 添加新布局配置和相关代码,支持自定义 UI 样式
- 实现新的事件定义和对话选项,增加游戏互动性
- 优化事件处理逻辑,提高代码复用性和可维护性
- 更新事件配置文件,引入新布局参数
This commit is contained in:
2025-08-30 23:21:07 +08:00
parent ddf817046c
commit 30a4f64230
12 changed files with 554 additions and 25 deletions

View File

@@ -1,3 +1,4 @@
using System; // Required for Activator
using RimWorld;
using Verse;
using System.Collections.Generic;
@@ -46,7 +47,7 @@ namespace WulaFallenEmpire
EventDef uiDef = DefDatabase<EventDef>.GetNamed(Props.uiDefName, false);
if (uiDef != null)
{
Find.WindowStack.Add(new Dialog_CustomDisplay(uiDef));
Find.WindowStack.Add((Window)Activator.CreateInstance(uiDef.windowType, uiDef));
}
else
{

View File

@@ -1,3 +1,4 @@
using System; // Required for Activator
using System.Collections.Generic;
using LudeonTK;
using Verse;
@@ -45,7 +46,7 @@ namespace WulaFallenEmpire
}
else
{
Find.WindowStack.Add(new Dialog_CustomDisplay(currentDef));
Find.WindowStack.Add((Window)Activator.CreateInstance(currentDef.windowType, currentDef));
}
}));
}

View File

@@ -74,7 +74,7 @@ namespace WulaFallenEmpire
{
// This logic is simplified from Effect_OpenCustomUI.OpenUI
// It assumes delayed actions always open a new dialog.
Find.WindowStack.Add(new Dialog_CustomDisplay(nextDef));
Find.WindowStack.Add((Window)Activator.CreateInstance(nextDef.windowType, nextDef));
}
else
{

View File

@@ -0,0 +1,277 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEngine;
using Verse;
namespace WulaFallenEmpire
{
public class Dialog_NewLayoutDisplay : Window
{
private EventDef def;
private Texture2D portrait;
private Texture2D background;
private string selectedDescription;
private static EventUIConfigDef config;
public static EventUIConfigDef Config
{
get
{
if (config == null)
{
config = DefDatabase<EventUIConfigDef>.GetNamed("Wula_EventUIConfig");
}
return config;
}
}
public override Vector2 InitialSize
{
get
{
if (def.windowSize != Vector2.zero)
{
return def.windowSize;
}
return Config.defaultWindowSize;
}
}
public Dialog_NewLayoutDisplay(EventDef def)
{
this.def = def;
this.forcePause = true;
this.absorbInputAroundWindow = true;
this.doCloseX = true;
var eventVarManager = Find.World.GetComponent<EventVariableManager>();
if (!def.descriptions.NullOrEmpty())
{
if (def.descriptionMode == DescriptionSelectionMode.Random)
{
selectedDescription = def.descriptions.RandomElement();
}
else
{
string indexVarName = $"_seq_desc_index_{def.defName}";
int currentIndex = eventVarManager.GetVariable<int>(indexVarName, 0);
selectedDescription = def.descriptions[currentIndex];
int nextIndex = (currentIndex + 1) % def.descriptions.Count;
eventVarManager.SetVariable(indexVarName, nextIndex);
}
}
else
{
selectedDescription = "Error: No descriptions found in def.";
}
}
public override void PreOpen()
{
base.PreOpen();
if (!def.portraitPath.NullOrEmpty())
{
portrait = ContentFinder<Texture2D>.Get(def.portraitPath);
}
string bgPath = !def.backgroundImagePath.NullOrEmpty() ? def.backgroundImagePath : Config.defaultBackgroundImagePath;
if (!bgPath.NullOrEmpty())
{
background = ContentFinder<Texture2D>.Get(bgPath);
}
HandleAction(def.immediateEffects);
if (!def.conditionalDescriptions.NullOrEmpty())
{
foreach (var condDesc in def.conditionalDescriptions)
{
string reason;
if (AreConditionsMet(condDesc.conditions, out reason))
{
selectedDescription += "\n\n" + condDesc.text;
}
}
}
selectedDescription = FormatDescription(selectedDescription);
}
public override void DoWindowContents(Rect inRect)
{
if (background != null)
{
GUI.DrawTexture(inRect, background, ScaleMode.ScaleToFit);
}
if (Config.showDefName)
{
Text.Font = GameFont.Tiny;
GUI.color = Color.gray;
Widgets.Label(new Rect(5, 5, inRect.width - 10, 20f), def.defName);
GUI.color = Color.white;
}
if (Config.showLabel)
{
Text.Font = Config.labelFont;
Widgets.Label(new Rect(5, 20f, inRect.width - 10, 30f), def.label);
Text.Font = GameFont.Small;
}
// 假设一个统一的边距
float padding = Config.newLayoutPadding;
// 名称区域
float nameHeight = Config.newLayoutNameSize.y;
float nameWidth = Config.newLayoutNameSize.x;
Rect nameRect = new Rect(inRect.x + (inRect.width - nameWidth) / 2f, inRect.y + padding, nameWidth, nameHeight);
if (Config.drawBorders)
{
Widgets.DrawBox(nameRect);
}
Text.Anchor = TextAnchor.MiddleCenter;
Text.Font = GameFont.Medium;
Widgets.Label(nameRect, def.characterName);
Text.Font = GameFont.Small;
Text.Anchor = TextAnchor.UpperLeft;
// 立绘区域
float lihuiWidth = Config.newLayoutLihuiSize.x;
float lihuiHeight = Config.newLayoutLihuiSize.y;
Rect lihuiRect = new Rect(inRect.x + (inRect.width - lihuiWidth) / 2f, nameRect.yMax + padding, lihuiWidth, lihuiHeight);
if (portrait != null)
{
GUI.DrawTexture(lihuiRect, portrait, ScaleMode.ScaleToFit);
}
if (Config.drawBorders)
{
Widgets.DrawBox(lihuiRect);
}
// 选项区域 (预先计算高度)
float optionButtonHeight = 30f; // 每个按钮的高度
float optionSpacing = 5f; // 按钮之间的间距
float calculatedOptionHeight = 0f;
if (def.options != null && def.options.Any())
{
calculatedOptionHeight = def.options.Count * optionButtonHeight + (def.options.Count - 1) * optionSpacing;
}
calculatedOptionHeight = Mathf.Max(calculatedOptionHeight, 100f); // 最小高度
float optionsWidth = Config.newLayoutOptionsWidth;
Rect optionRect = new Rect(inRect.x + (inRect.width - optionsWidth) / 2f, inRect.yMax - padding - calculatedOptionHeight, optionsWidth, calculatedOptionHeight);
// 描述区域
float textWidth = Config.newLayoutTextSize.x;
Rect textRect = new Rect(inRect.x + (inRect.width - textWidth) / 2f, lihuiRect.yMax + padding, textWidth, optionRect.y - (lihuiRect.yMax + padding) - padding);
if (Config.drawBorders)
{
Widgets.DrawBox(textRect);
}
Rect textInnerRect = textRect.ContractedBy(padding);
Widgets.Label(textInnerRect, selectedDescription);
// 选项列表的绘制
Listing_Standard listing = new Listing_Standard();
listing.Begin(optionRect); // 使用完整的 optionRect
if (def.options != null)
{
foreach (var option in def.options)
{
string reason;
bool conditionsMet = AreConditionsMet(option.conditions, out reason);
if (conditionsMet)
{
if (listing.ButtonText(option.label))
{
HandleAction(option.optionEffects);
}
}
else
{
if (option.hideWhenDisabled)
{
continue;
}
Rect rect = listing.GetRect(30f);
Widgets.ButtonText(rect, option.label, false, true, false);
TooltipHandler.TipRegion(rect, GetDisabledReason(option, reason));
}
}
}
listing.End();
}
private void HandleAction(List<ConditionalEffects> conditionalEffects)
{
if (conditionalEffects.NullOrEmpty())
{
return;
}
foreach (var ce in conditionalEffects)
{
if (AreConditionsMet(ce.conditions, out _))
{
ce.Execute(this);
}
}
}
private bool AreConditionsMet(List<Condition> conditions, out string reason)
{
reason = "";
if (conditions.NullOrEmpty())
{
return true;
}
foreach (var condition in conditions)
{
if (!condition.IsMet(out string singleReason))
{
reason = singleReason;
return false;
}
}
return true;
}
private string GetDisabledReason(EventOption option, string reason)
{
if (!option.disabledReason.NullOrEmpty())
{
return option.disabledReason;
}
return reason;
}
public override void PostClose()
{
base.PostClose();
HandleAction(def.dismissEffects);
}
private string FormatDescription(string description)
{
var eventVarManager = Find.World.GetComponent<EventVariableManager>();
// Use regex to find all placeholders like {variableName}
return Regex.Replace(description, @"\{(.+?)\}", match =>
{
string varName = match.Groups[1].Value;
if (eventVarManager.HasVariable(varName))
{
// Important: GetVariable<object> to get any type
return eventVarManager.GetVariable<object>(varName)?.ToString() ?? "";
}
return match.Value; // Keep placeholder if variable not found
});
}
}
}

View File

@@ -1,3 +1,4 @@
using System; // Required for Activator
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
@@ -9,7 +10,7 @@ namespace WulaFallenEmpire
public abstract class Effect
{
public float weight = 1.0f;
public abstract void Execute(Dialog_CustomDisplay dialog = null);
public abstract void Execute(Window dialog = null);
}
public class Effect_OpenCustomUI : Effect
@@ -17,7 +18,7 @@ namespace WulaFallenEmpire
public string defName;
public int delayTicks = 0;
public override void Execute(Dialog_CustomDisplay dialog = null)
public override void Execute(Window dialog = null)
{
if (delayTicks > 0)
{
@@ -58,7 +59,7 @@ namespace WulaFallenEmpire
}
else
{
Find.WindowStack.Add(new Dialog_CustomDisplay(nextDef));
Find.WindowStack.Add((Window)Activator.CreateInstance(nextDef.windowType, nextDef));
}
}
else
@@ -89,7 +90,7 @@ namespace WulaFallenEmpire
public class Effect_CloseDialog : Effect
{
public override void Execute(Dialog_CustomDisplay dialog = null)
public override void Execute(Window dialog = null)
{
dialog?.Close();
}
@@ -100,7 +101,7 @@ namespace WulaFallenEmpire
public string message;
public MessageTypeDef messageTypeDef;
public override void Execute(Dialog_CustomDisplay dialog = null)
public override void Execute(Window dialog = null)
{
if (messageTypeDef == null)
{
@@ -114,7 +115,7 @@ namespace WulaFallenEmpire
{
public IncidentDef incident;
public override void Execute(Dialog_CustomDisplay dialog = null)
public override void Execute(Window dialog = null)
{
if (incident == null)
{
@@ -140,7 +141,7 @@ namespace WulaFallenEmpire
public FactionDef faction;
public int goodwillChange;
public override void Execute(Dialog_CustomDisplay dialog = null)
public override void Execute(Window dialog = null)
{
if (faction == null)
{
@@ -166,7 +167,7 @@ namespace WulaFallenEmpire
public string type; // Int, Float, String, Bool
public bool forceSet = false;
public override void Execute(Dialog_CustomDisplay dialog = null)
public override void Execute(Window dialog = null)
{
var eventVarManager = Find.World.GetComponent<EventVariableManager>();
if (!forceSet && eventVarManager.HasVariable(name))
@@ -199,7 +200,7 @@ namespace WulaFallenEmpire
public FactionDef faction;
public string goodwillVariableName;
public override void Execute(Dialog_CustomDisplay dialog = null)
public override void Execute(Window dialog = null)
{
if (faction == null)
{
@@ -225,7 +226,7 @@ namespace WulaFallenEmpire
public int count = 1;
public string storeAs;
public override void Execute(Dialog_CustomDisplay dialog = null)
public override void Execute(Window dialog = null)
{
if (kindDef == null)
{
@@ -264,7 +265,7 @@ namespace WulaFallenEmpire
public ThingDef thingDef;
public int count = 1;
public override void Execute(Dialog_CustomDisplay dialog = null)
public override void Execute(Window dialog = null)
{
if (thingDef == null)
{
@@ -298,7 +299,7 @@ namespace WulaFallenEmpire
public string letterText;
public LetterDef letterDef;
public override void Execute(Dialog_CustomDisplay dialog = null)
public override void Execute(Window dialog = null)
{
if (kindDef == null)
{
@@ -356,7 +357,7 @@ namespace WulaFallenEmpire
public string valueVariableName;
public VariableOperation operation;
public override void Execute(Dialog_CustomDisplay dialog = null)
public override void Execute(Window dialog = null)
{
if (string.IsNullOrEmpty(name))
{
@@ -434,7 +435,7 @@ namespace WulaFallenEmpire
{
public string name;
public override void Execute(Dialog_CustomDisplay dialog = null)
public override void Execute(Window dialog = null)
{
if (string.IsNullOrEmpty(name))
{
@@ -449,7 +450,7 @@ namespace WulaFallenEmpire
{
public QuestScriptDef quest;
public override void Execute(Dialog_CustomDisplay dialog = null)
public override void Execute(Window dialog = null)
{
if (quest == null)
{
@@ -468,7 +469,7 @@ namespace WulaFallenEmpire
{
public ResearchProjectDef research;
public override void Execute(Dialog_CustomDisplay dialog = null)
public override void Execute(Window dialog = null)
{
if (research == null)
{
@@ -490,7 +491,7 @@ namespace WulaFallenEmpire
public string letterLabel;
public string letterText;
public override void Execute(Dialog_CustomDisplay dialog = null)
public override void Execute(Window dialog = null)
{
Map map = Find.CurrentMap;
if (map == null)
@@ -563,7 +564,7 @@ namespace WulaFallenEmpire
public FactionDef factionDef;
public string variableName;
public override void Execute(Dialog_CustomDisplay dialog = null)
public override void Execute(Window dialog = null)
{
if (factionDef == null || string.IsNullOrEmpty(variableName))
{
@@ -592,7 +593,7 @@ namespace WulaFallenEmpire
{
public string variableName;
public override void Execute(Dialog_CustomDisplay dialog = null)
public override void Execute(Window dialog = null)
{
if (string.IsNullOrEmpty(variableName))
{
@@ -611,7 +612,7 @@ namespace WulaFallenEmpire
{
public string variableName;
public override void Execute(Dialog_CustomDisplay dialog = null)
public override void Execute(Window dialog = null)
{
if (string.IsNullOrEmpty(variableName))
{
@@ -630,7 +631,7 @@ namespace WulaFallenEmpire
{
public string variableName;
public override void Execute(Dialog_CustomDisplay dialog = null)
public override void Execute(Window dialog = null)
{
if (string.IsNullOrEmpty(variableName))
{

View File

@@ -1,3 +1,4 @@
using System; // Add this line
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
@@ -26,6 +27,7 @@ namespace WulaFallenEmpire
public Vector2 windowSize = Vector2.zero;
public Type windowType = typeof(Dialog_CustomDisplay); // 默认窗口类型
public List<EventOption> options;
public string backgroundImagePath;
public List<ConditionalEffects> immediateEffects;
@@ -83,7 +85,7 @@ namespace WulaFallenEmpire
public List<Effect> randomlistEffects;
public List<LoopEffects> loopEffects;
public void Execute(Dialog_CustomDisplay dialog)
public void Execute(Window dialog)
{
// Execute all standard effects
if (!effects.NullOrEmpty())

View File

@@ -22,5 +22,13 @@ namespace WulaFallenEmpire
// Virtual Layout Offsets
public float textNameOffset = 20f;
public float optionsTextOffset = 20f;
// New Layout Dimensions
public Vector2 newLayoutNameSize = new Vector2(200f, 50f);
public Vector2 newLayoutLihuiSize = new Vector2(300f, 400f);
public Vector2 newLayoutTextSize = new Vector2(600f, 200f);
public float newLayoutOptionsWidth = 600f;
public float newLayoutPadding = 20f;
public float newLayoutTextNameOffset = 20f;
public float newLayoutOptionsTextOffset = 20f;
}
}

View File

@@ -90,6 +90,7 @@
<Compile Include="WULA_EventSystem\Dialog_ManageEventVariables.cs" />
<Compile Include="WULA_EventSystem\DelayedActionManager.cs" />
<Compile Include="WULA_EventSystem\Dialog_CustomDisplay.cs" />
<Compile Include="WULA_EventSystem\Dialog_NewLayoutDisplay.cs" />
<Compile Include="WULA_EventSystem\Effect.cs" />
<Compile Include="WULA_EventSystem\EventVariableManager.cs" />
<Compile Include="WULA_EventSystem\EventDef.cs" />