diff --git a/1.6/1.6/Assemblies/WulaFallenEmpire.dll b/1.6/1.6/Assemblies/WulaFallenEmpire.dll index 122dd57b..e500a146 100644 Binary files a/1.6/1.6/Assemblies/WulaFallenEmpire.dll and b/1.6/1.6/Assemblies/WulaFallenEmpire.dll differ diff --git a/1.6/1.6/Defs/AbilityDefs/WULA_Flyover_Ability.xml b/1.6/1.6/Defs/AbilityDefs/WULA_Flyover_Ability.xml index 922a162e..c94a659c 100644 --- a/1.6/1.6/Defs/AbilityDefs/WULA_Flyover_Ability.xml +++ b/1.6/1.6/Defs/AbilityDefs/WULA_Flyover_Ability.xml @@ -8,6 +8,8 @@ 1 Misc12 false + true + false Verb_CastAbility true @@ -182,7 +184,6 @@ 1.5 1.0,0.3,0.1,0.3 ShipChunkIncoming - true
  • diff --git a/1.6/1.6/Defs/EventDefs/EventDef_Examples.xml b/1.6/1.6/Defs/EventDefs/EventDef_Examples.xml index 541d50bb..581808c2 100644 --- a/1.6/1.6/Defs/EventDefs/EventDef_Examples.xml +++ b/1.6/1.6/Defs/EventDefs/EventDef_Examples.xml @@ -6,7 +6,6 @@ Wula/Events/Portraits/WULA_Anisia_1 URa-1138「艾妮西娅」 - WulaFallenEmpire.Dialog_NewLayoutDisplay
  • 很高兴看到你们的殖民地没有变成荒野中的烂泥。那么,今天找我有什么事情?
  • 宇宙生存法则第一条:不要惹平胸火气大的女人。
  • diff --git a/1.6/1.6/Defs/EventDefs/EventDef_Wula/Wula_MainEvent.xml b/1.6/1.6/Defs/EventDefs/EventDef_Wula/Wula_MainEvent.xml index df640178..52c34cac 100644 --- a/1.6/1.6/Defs/EventDefs/EventDef_Wula/Wula_MainEvent.xml +++ b/1.6/1.6/Defs/EventDefs/EventDef_Wula/Wula_MainEvent.xml @@ -9,6 +9,61 @@
  • 这里是P.I.A,通讯信号良好,等待输入。
  • +
  • + + true + (133,206,219,185) + (133,206,219,185) + (255,255,255,255) + (255,255,255,255) + +
  • + +
  • + +
  • + + +
  • + + +
  • + +
  • + +
  • + + +
  • + + +
  • + +
  • + +
  • + + +
  • + + +
  • + +
  • + +
  • + + +
  • + + +
  • + +
  • + +
  • + +
  • diff --git a/1.6/1.6/Defs/HediffDefs/WULA_FM_Hediffs.xml b/1.6/1.6/Defs/HediffDefs/WULA_FM_Hediffs.xml index 9972c004..1e3efef3 100644 --- a/1.6/1.6/Defs/HediffDefs/WULA_FM_Hediffs.xml +++ b/1.6/1.6/Defs/HediffDefs/WULA_FM_Hediffs.xml @@ -26,7 +26,7 @@
  • Wula_FM_Switc_Aircraft
  • 0 - Wula/UI/Commands/WULA_NanoSwitch + Wula/UI/Commands/WULA_Antenna_Switch false diff --git a/1.6/1.6/Defs/ThingDefs/WULA_Item.xml b/1.6/1.6/Defs/ThingDefs/WULA_Item.xml index 86f1ae76..9802a6aa 100644 --- a/1.6/1.6/Defs/ThingDefs/WULA_Item.xml +++ b/1.6/1.6/Defs/ThingDefs/WULA_Item.xml @@ -1,5 +1,168 @@ + + WULA_Alloy + + + + Wula/Item/WULA_Neutronium + Graphic_Single + + None + false + Metal_Drop + Metal_Drop + false + false + + 1 + 1 + 3 + 3 + 1 + 0 + 0 + 2 + 3 + + +
  • ResourcesRaw
  • +
    + false + true + + +
  • Metallic
  • +
    + Metal + 0 + false + ConstructMetal + (63, 74, 70) + BulletImpact_Metal + MeleeHit_Metal_Sharp + MeleeHit_Metal_Blunt + Pawn_Melee_Punch_HitBuilding_Metal + + 0 + + + 5 + 0 + 10 + 10 + 10 + 1.1 + +
    + Heavy + 200 + 50 +
    + + WULA_Dark_Matter_Item + + 由乌拉帝国技术完成封装的一小块暗物质,这种神秘物质具有许多似乎可以打破所谓自然法则的性质。\n\n注意:暗物质约束装置很脆弱,受到冲击时将引发大范围湮灭反应! + + Wula/Item/WULA_Dark_Matter_Item + Graphic_Single + + Normal + false + false + Metal_Drop + Metal_Drop + false + false + + 1000 + 0.01 + 50 + + +
  • ResourcesRaw
  • +
    + false + Medium + 80 + 1 + +
  • + 30 + BombSuper + 1 + +
  • Bullet
  • +
  • Arrow
  • +
  • ArrowHighVelocity
  • + + 0.333 + + 1 + 1 + +
    +
    + + WULA_Neutronium + + 零素也称中子元素,是中子星的主要构成成分,相比常规合金更适合作为装甲和近战武器的铸造材料,乌拉帝国通过以暗物质驱动的科技进行材料压缩,可以人为地制造这种强大材料。 + + Wula/Item/WULA_Neutronium + Graphic_Single + + None + false + Metal_Drop + Metal_Drop + false + false + + 1 + 1 + 3 + 3 + 1 + 0 + 0 + 2 + 3 + + +
  • ResourcesRaw
  • +
    + false + true + + +
  • Metallic
  • +
    + Metal + 0 + false + ConstructMetal + (63, 74, 70) + BulletImpact_Metal + MeleeHit_Metal_Sharp + MeleeHit_Metal_Blunt + Pawn_Melee_Punch_HitBuilding_Metal + + 0 + + + 5 + 0 + 10 + 10 + 10 + 1.1 + +
    + Heavy + 200 + 50 +
    + WULA_Charge_Cube @@ -206,110 +369,6 @@ 50 - - WULA_Dark_Matter_Item - - 由乌拉帝国技术完成封装的一小块暗物质,这种神秘物质具有许多似乎可以打破所谓自然法则的性质。\n\n注意:暗物质约束装置很脆弱,受到冲击时将引发大范围湮灭反应! - - Wula/Item/WULA_Dark_Matter_Item - Graphic_Single - - Normal - false - false - Metal_Drop - Metal_Drop - false - false - - 1000 - 0.01 - 50 - - -
  • ResourcesRaw
  • -
    - false - Medium - 80 - 1 - -
  • - 30 - BombSuper - 1 - -
  • Bullet
  • -
  • Arrow
  • -
  • ArrowHighVelocity
  • - - 0.333 - - 1 - 1 - -
    -
    - - WULA_Neutronium - - 零素也称中子元素,是中子星的主要构成成分,相比常规合金更适合作为装甲和近战武器的铸造材料,乌拉帝国通过以暗物质驱动的科技进行材料压缩,可以人为地制造这种强大材料。 - - Wula/Item/WULA_Neutronium - Graphic_Single - - None - false - Metal_Drop - Metal_Drop - false - false - - 1 - 1 - 3 - 3 - 1 - 0 - 0 - 2 - 3 - - -
  • ResourcesRaw
  • -
    - false - true - - -
  • Metallic
  • -
    - Metal - 0 - false - ConstructMetal - (63, 74, 70) - BulletImpact_Metal - MeleeHit_Metal_Sharp - MeleeHit_Metal_Blunt - Pawn_Melee_Punch_HitBuilding_Metal - - 0 - - - 5 - 0 - 10 - 10 - 10 - 1.1 - -
    - Heavy - 200 - 50 -
    - Wula_Organic_Precursor diff --git a/1.6/1.6/Defs/ThingDefs_Misc/WULA_Flyover_Item.xml b/1.6/1.6/Defs/ThingDefs_Misc/WULA_Flyover_Item.xml index 32cd228b..5eac5c1e 100644 --- a/1.6/1.6/Defs/ThingDefs_Misc/WULA_Flyover_Item.xml +++ b/1.6/1.6/Defs/ThingDefs_Misc/WULA_Flyover_Item.xml @@ -58,6 +58,7 @@
  • WULA_Flyover_BaseBuilder true + 3
  • @@ -111,6 +112,13 @@ false false MetaOverlays + +
  • + WULA_Flyover_Airstrick + true + 0 +
  • +
    WULA_Fighter_A diff --git a/1.6/1.6/Defs/ThingDefs_Misc/Weapons/WULA_FE_Remake_Weapon.xml b/1.6/1.6/Defs/ThingDefs_Misc/Weapons/WULA_FE_Remake_Weapon.xml index 3181ff15..b007904d 100644 --- a/1.6/1.6/Defs/ThingDefs_Misc/Weapons/WULA_FE_Remake_Weapon.xml +++ b/1.6/1.6/Defs/ThingDefs_Misc/Weapons/WULA_FE_Remake_Weapon.xml @@ -69,27 +69,27 @@
  • Melee - 1.5 + 1 true WULA_ExperienceDataPack_Melee
  • - 1000 + 2000 Good WULA_WeaponEvolving
  • - 3000 + 6000 Excellent WULA_WeaponEvolving
  • - 6000 + 12000 Masterwork WULA_WeaponEvolving
  • - 10000 + 20000 Legendary WULA_WeaponMastering
  • @@ -435,27 +435,27 @@
  • Shooting - 1.5 + 1 true WULA_ExperienceDataPack_Shooting
  • - 1000 + 2000 Good WULA_WeaponEvolving
  • - 3000 + 6000 Excellent WULA_WeaponEvolving
  • - 6000 + 12000 Masterwork WULA_WeaponEvolving
  • - 10000 + 20000 Legendary WULA_WeaponMastering
  • diff --git a/1.6/1.6/Defs/WulaMiscSettingDefs/EventUIConfig.xml b/1.6/1.6/Defs/WulaMiscSettingDefs/EventUIConfig.xml index 88d27abe..257a266d 100644 --- a/1.6/1.6/Defs/WulaMiscSettingDefs/EventUIConfig.xml +++ b/1.6/1.6/Defs/WulaMiscSettingDefs/EventUIConfig.xml @@ -1,37 +1,53 @@ - + - Wula_EventUIConfig - - Small - true + + false + false + Wula/Events/tile_large_bg + + + (600,850) + + + true + false + true + true + true + + + false + + + (600,400) + (600,40) + (600,30) + (500,150) + (600,200) + (500,150) + + + (0,10) + (10,0) + (10,0) + (10,20) + (10,0) + + + (10,25) + + + (20,20) + + + 20 + + + false false - true - - - - (1093, 687) - (593, 530) - (593, 330) - 750 - - - 0 - 0 - - (693, 887) - - - (200, 50) - (600, 200) - (600, 200) - 600 - 20 - 20 - 20 - + Medium - - +
    \ No newline at end of file diff --git a/Content/Textures/Wula/Events/Portraits/PIA.png b/Content/Textures/Wula/Events/Portraits/PIA.png new file mode 100644 index 00000000..e59a9188 Binary files /dev/null and b/Content/Textures/Wula/Events/Portraits/PIA.png differ diff --git a/Content/Textures/Wula/Events/Portraits/WULA_Legion_1.png b/Content/Textures/Wula/Events/Portraits/WULA_Legion_1.png index 4893933c..fccf58df 100644 Binary files a/Content/Textures/Wula/Events/Portraits/WULA_Legion_1.png and b/Content/Textures/Wula/Events/Portraits/WULA_Legion_1.png differ diff --git a/Content/Textures/Wula/Events/tile_large_bg.png b/Content/Textures/Wula/Events/tile_large_bg.png new file mode 100644 index 00000000..51e97906 Binary files /dev/null and b/Content/Textures/Wula/Events/tile_large_bg.png differ diff --git a/Content/Textures/Wula/Storyteller/WULA_Legion.png b/Content/Textures/Wula/Storyteller/WULA_Legion.png index 328fe49f..0fe83e60 100644 Binary files a/Content/Textures/Wula/Storyteller/WULA_Legion.png and b/Content/Textures/Wula/Storyteller/WULA_Legion.png differ diff --git a/Content/Textures/Wula/Storyteller/WULA_Legion_TINY.png b/Content/Textures/Wula/Storyteller/WULA_Legion_TINY.png index 6f3f30dc..c1f30154 100644 Binary files a/Content/Textures/Wula/Storyteller/WULA_Legion_TINY.png and b/Content/Textures/Wula/Storyteller/WULA_Legion_TINY.png differ diff --git a/Content/Textures/Wula/UI/Commands/WULA_Antenna_Switch.png b/Content/Textures/Wula/UI/Commands/WULA_Antenna_Switch.png new file mode 100644 index 00000000..48d5ece7 Binary files /dev/null and b/Content/Textures/Wula/UI/Commands/WULA_Antenna_Switch.png differ diff --git a/Source/WulaFallenEmpire/EventSystem/Dialog_CustomDisplay.cs b/Source/WulaFallenEmpire/EventSystem/Dialog_CustomDisplay.cs index 0a660a56..a1663173 100644 --- a/Source/WulaFallenEmpire/EventSystem/Dialog_CustomDisplay.cs +++ b/Source/WulaFallenEmpire/EventSystem/Dialog_CustomDisplay.cs @@ -27,24 +27,28 @@ namespace WulaFallenEmpire } } + // 使用配置的窗口尺寸 public override Vector2 InitialSize { get { - if (def.windowSize != Vector2.zero) - { - return def.windowSize; - } - return Config.defaultWindowSize; + return def.windowSize != Vector2.zero ? def.windowSize : Config.windowSize; } } public Dialog_CustomDisplay(EventDef def) { this.def = def; - this.forcePause = true; + + // 关键修改:使用配置控制是否暂停游戏 + this.forcePause = Config.pauseGameOnOpen; + this.absorbInputAroundWindow = true; this.doCloseX = true; + + // 根据配置设置是否绘制窗口背景和阴影 + this.doWindowBackground = Config.showMainWindow; + this.drawShadow = Config.showMainWindow; var eventVarManager = Find.World.GetComponent(); if (!def.descriptions.NullOrEmpty()) @@ -73,12 +77,33 @@ namespace WulaFallenEmpire public override void PreOpen() { base.PreOpen(); - if (!def.portraitPath.NullOrEmpty()) + + // 加载立绘 + if (Config.showPortrait && !def.portraitPath.NullOrEmpty()) { portrait = ContentFinder.Get(def.portraitPath); } - string bgPath = !def.backgroundImagePath.NullOrEmpty() ? def.backgroundImagePath : Config.defaultBackgroundImagePath; + // 加载背景 - 优先级:事件定义背景 > 自定义背景 > 默认背景 + string bgPath = null; + + // 1. 首先检查事件定义中的背景 + if (!def.backgroundImagePath.NullOrEmpty()) + { + bgPath = def.backgroundImagePath; + } + // 2. 然后检查自定义背景 + else if (!string.IsNullOrEmpty(Config.customBackgroundImagePath)) + { + bgPath = Config.customBackgroundImagePath; + } + // 3. 最后检查是否使用默认背景 + else if (Config.useDefaultBackground) + { + // 这里可以设置一个默认背景路径,或者留空 + // bgPath = "UI/Backgrounds/DefaultEventBackground"; + } + if (!bgPath.NullOrEmpty()) { background = ContentFinder.Get(bgPath); @@ -103,60 +128,55 @@ namespace WulaFallenEmpire public override void DoWindowContents(Rect inRect) { - // 绘制背景 + // 绘制自定义背景(如果有) if (background != null) { - GUI.DrawTexture(inRect, background, ScaleMode.ScaleToFit); + GUI.DrawTexture(inRect, background, ScaleMode.StretchToFill); } - // 调试信息(def名称) - if (Config.showDefName) + float currentY = 0f; + + // 1. 立绘 + if (Config.showPortrait) { - Text.Font = GameFont.Tiny; - GUI.color = Color.gray; - Widgets.Label(new Rect(5, 5, inRect.width - 10, 20f), def.defName); - GUI.color = Color.white; + currentY += Config.GetScaledMargin(Config.portraitMargins.x, inRect); + + Rect portraitRect = Config.GetScaledRect(Config.portraitSize, inRect); + portraitRect.y = currentY; + + if (portrait != null) + { + // 保持图片比例 + float aspectRatio = (float)portrait.width / portrait.height; + float portraitWidth = portraitRect.height * aspectRatio; + + // 居中显示 + Rect centeredPortraitRect = new Rect( + portraitRect.center.x - portraitWidth / 2, + portraitRect.y, + portraitWidth, + portraitRect.height + ); + + GUI.DrawTexture(centeredPortraitRect, portrait, ScaleMode.ScaleToFit); + } + + if (Config.drawBorders) + { + Widgets.DrawBox(portraitRect); + } + + currentY += portraitRect.height + Config.GetScaledMargin(Config.portraitMargins.y, inRect); } - // 使用新的布局参数 - float scale = CalculateScale(inRect); - - // 使用新的布局尺寸 - float scaledLihuiWidth = Config.newLayoutLihuiSize.x * scale; - float scaledLihuiHeight = Config.newLayoutLihuiSize.y * scale; - float scaledTextWidth = Config.newLayoutTextSize.x * scale; - float scaledTextHeight = Config.newLayoutTextSize.y * scale; - float scaledOptionsWidth = Config.newLayoutOptionsWidth * scale; - - // 计算各元素高度 - float labelHeight = 30f * scale; - float characterNameHeight = 25f * scale; - float descriptionHeight = scaledTextHeight; - float optionsHeight = CalculateOptionsHeight(def.options, scaledOptionsWidth, scale); - - // 使用新的间距参数 - float topMargin = Config.newLayoutPadding * scale; - float elementSpacing = Config.newLayoutTextNameOffset * scale; - float textOptionsSpacing = Config.newLayoutOptionsTextOffset * scale; - - float currentY = topMargin; - - // 1. 立绘(水平居中,顶着顶部) - Rect lihuiRect = new Rect((inRect.width - scaledLihuiWidth) / 2, currentY, scaledLihuiWidth, scaledLihuiHeight); - if (portrait != null) - { - GUI.DrawTexture(lihuiRect, portrait, ScaleMode.ScaleToFit); - } - if (Config.drawBorders) - { - Widgets.DrawBox(lihuiRect); - } - currentY += scaledLihuiHeight + elementSpacing; - - // 2. Label(水平居中) + // 2. Label if (Config.showLabel) { - Rect labelRect = new Rect(0, currentY, inRect.width, labelHeight); + currentY += Config.GetScaledMargin(Config.labelMargins.x, inRect); + + Rect labelRect = Config.GetScaledRect(Config.labelSize, inRect); + labelRect.y = currentY; + Text.Anchor = TextAnchor.MiddleCenter; Text.Font = Config.labelFont; Widgets.Label(labelRect, def.label); @@ -167,68 +187,108 @@ namespace WulaFallenEmpire { Widgets.DrawBox(labelRect); } - currentY += labelHeight + elementSpacing; + + currentY += labelRect.height + Config.GetScaledMargin(Config.labelMargins.y, inRect); } - // 3. CharacterName(水平居中) - Rect nameRect = new Rect(0, currentY, inRect.width, characterNameHeight); - Text.Anchor = TextAnchor.MiddleCenter; - Text.Font = GameFont.Medium; - Widgets.Label(nameRect, def.characterName); - Text.Font = GameFont.Small; - Text.Anchor = TextAnchor.UpperLeft; - - if (Config.drawBorders) + // 3. CharacterName + if (Config.showCharacterName) { - Widgets.DrawBox(nameRect); + currentY += Config.GetScaledMargin(Config.characterNameMargins.x, inRect); + + Rect nameRect = Config.GetScaledRect(Config.characterNameSize, inRect); + nameRect.y = currentY; + + Text.Anchor = TextAnchor.MiddleCenter; + Text.Font = GameFont.Medium; + Widgets.Label(nameRect, def.characterName); + Text.Font = GameFont.Small; + Text.Anchor = TextAnchor.UpperLeft; + + if (Config.drawBorders) + { + Widgets.DrawBox(nameRect); + } + + currentY += nameRect.height + Config.GetScaledMargin(Config.characterNameMargins.y, inRect); } - currentY += characterNameHeight + elementSpacing; - // 4. Descriptions(水平居中) - Rect textRect = new Rect((inRect.width - scaledTextWidth) / 2, currentY, scaledTextWidth, descriptionHeight); - if (Config.drawBorders) + // 4. 描述 + if (Config.showDescriptions) { - Widgets.DrawBox(textRect); + currentY += Config.GetScaledMargin(Config.descriptionsMargins.x, inRect); + + Rect descriptionRect = Config.GetScaledRect(Config.descriptionsSize, inRect); + descriptionRect.y = currentY; + + if (Config.drawBorders) + { + Widgets.DrawBox(descriptionRect); + } + + // 应用描述区域内边距 + Vector2 scaledDescriptionsPadding = Config.GetScaledDescriptionsPadding(descriptionRect); + Rect descriptionInnerRect = descriptionRect.ContractedBy(scaledDescriptionsPadding.y, scaledDescriptionsPadding.x); + + // 使用可滚动的文本区域 + float textHeight = Text.CalcHeight(selectedDescription, descriptionInnerRect.width); + Rect viewRect = new Rect(0, 0, descriptionInnerRect.width, Mathf.Max(textHeight, descriptionInnerRect.height)); + + Widgets.BeginScrollView(descriptionInnerRect, ref descriptionScrollPosition, viewRect); + Widgets.Label(new Rect(0, 0, viewRect.width, viewRect.height), selectedDescription); + Widgets.EndScrollView(); + + currentY += descriptionRect.height + Config.GetScaledMargin(Config.descriptionsMargins.y, inRect); } - - // 增加内边距 - float textInnerPadding = 15f * scale; - Rect textInnerRect = textRect.ContractedBy(textInnerPadding); - Widgets.LabelScrollable(textInnerRect, selectedDescription, ref scrollPosition); - - currentY += descriptionHeight + textOptionsSpacing; - // 5. Options(水平居中) - Rect optionRect = new Rect((inRect.width - scaledOptionsWidth) / 2, currentY, scaledOptionsWidth, optionsHeight); - if (Config.drawBorders) + // 5. 选项列表 + if (Config.showOptions) { - Widgets.DrawBox(optionRect); + currentY += Config.GetScaledMargin(Config.optionsListMargins.x, inRect); + + Rect optionsRect = Config.GetScaledRect(Config.optionsListSize, inRect); + optionsRect.y = currentY; + + if (Config.drawBorders) + { + Widgets.DrawBox(optionsRect); + } + + DrawOptions(optionsRect, def.options); + + currentY += optionsRect.height + Config.GetScaledMargin(Config.optionsListMargins.y, inRect); + } + + // 调试信息 + 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; } - - // 增加内边距 - float optionsInnerPadding = 10f * scale; - DrawOptions(optionRect.ContractedBy(optionsInnerPadding), def.options, scale); } - // 计算缩放比例 - 使用新的布局参数 - private float CalculateScale(Rect inRect) - { - float virtualWidth = Mathf.Max( - Config.newLayoutLihuiSize.x, - Config.newLayoutTextSize.x, - Config.newLayoutOptionsWidth - ); - float scaleX = inRect.width / virtualWidth; - return Mathf.Min(scaleX, 1.0f) * 0.85f; // 稍微减少缩放以留出更多边距 - } + // 滚动位置 + private Vector2 descriptionScrollPosition = Vector2.zero; + private Vector2 optionsScrollPosition = Vector2.zero; - // 计算选项区域高度 - private float CalculateOptionsHeight(List options, float optionsWidth, float scale) + // 绘制选项区域 + private void DrawOptions(Rect rect, List options) { if (options == null || options.Count == 0) - return 0f; + return; - float totalHeight = 0f; + // 应用选项列表内边距 + Vector2 scaledPadding = Config.GetScaledOptionsListPadding(rect); + Rect optionsInnerRect = rect.ContractedBy(scaledPadding.x, scaledPadding.y); + + // 计算缩放后的选项尺寸和间距 + Vector2 scaledOptionSize = Config.GetScaledOptionSize(optionsInnerRect); + float scaledOptionSpacing = Config.GetScaledOptionSpacing(optionsInnerRect); + + // 计算可见的选项 + var visibleOptions = new List(); var eventVarManager = Find.World.GetComponent(); foreach (var option in options) @@ -240,56 +300,149 @@ namespace WulaFallenEmpire { continue; } - - // 增加选项高度和间距 - totalHeight += 35f * scale; // 每个选项高度 - totalHeight += 8f * scale; // 选项间距 + visibleOptions.Add(option); } - return totalHeight; - } - - // 绘制选项 - private void DrawOptions(Rect rect, List options, float scale) - { - if (options == null || options.Count == 0) + if (visibleOptions.Count == 0) return; - Listing_Standard listing = new Listing_Standard(); - listing.Begin(rect); - - // 增加选项之间的间距 - listing.verticalSpacing = 8f * scale; - - foreach (var option in options) + // 计算选项列表的总高度 + float totalOptionsHeight = (scaledOptionSize.y * visibleOptions.Count) + + (scaledOptionSpacing * (visibleOptions.Count - 1)); + + bool needsScroll = totalOptionsHeight > optionsInnerRect.height; + + // 如果需要滚动,使用滚动视图 + if (needsScroll) { - string reason; - bool conditionsMet = AreConditionsMet(option.conditions, out reason); - - if (conditionsMet) + Rect viewRect = new Rect(0, 0, optionsInnerRect.width - 20f, totalOptionsHeight); + Widgets.BeginScrollView(optionsInnerRect, ref optionsScrollPosition, viewRect); + + float currentY = 0f; + foreach (var option in visibleOptions) { - if (listing.ButtonText(option.label)) + DrawSingleOption(new Rect(0, currentY, viewRect.width, scaledOptionSize.y), option); + currentY += scaledOptionSize.y + scaledOptionSpacing; + } + + Widgets.EndScrollView(); + } + else + { + // 不需要滚动,垂直居中显示所有选项 + float totalHeight = (scaledOptionSize.y * visibleOptions.Count) + + (scaledOptionSpacing * (visibleOptions.Count - 1)); + float startY = optionsInnerRect.y + (optionsInnerRect.height - totalHeight) / 2; + + float currentY = startY; + foreach (var option in visibleOptions) + { + DrawSingleOption(new Rect(optionsInnerRect.x, currentY, optionsInnerRect.width, scaledOptionSize.y), option); + currentY += scaledOptionSize.y + scaledOptionSpacing; + } + } + } + + // 绘制单个选项 + private void DrawSingleOption(Rect rect, EventOption option) + { + string reason; + bool conditionsMet = AreConditionsMet(option.conditions, out reason); + + // 水平居中选项 + float optionWidth = Mathf.Min(rect.width, Config.optionSize.x * (rect.width / Config.windowSize.x)); + float optionX = rect.x + (rect.width - optionWidth) / 2; + Rect optionRect = new Rect(optionX, rect.y, optionWidth, rect.height); + + if (conditionsMet) + { + // 保存原始颜色状态 + Color originalColor = GUI.color; + GameFont originalFont = Text.Font; + Color originalTextColor = GUI.contentColor; + + try + { + // 应用自定义颜色 + if (option.useCustomColors) + { + ApplyOptionColors(option, optionRect); + } + + if (Widgets.ButtonText(optionRect, option.label)) { HandleAction(option.optionEffects); } } - else + finally { - if (option.hideWhenDisabled) - { - continue; - } - Rect buttonRect = listing.GetRect(35f * scale); // 增加按钮高度 - Widgets.ButtonText(buttonRect, option.label, false, true, false); - TooltipHandler.TipRegion(buttonRect, GetDisabledReason(option, reason)); + // 恢复原始颜色状态 + GUI.color = originalColor; + Text.Font = originalFont; + GUI.contentColor = originalTextColor; } } + else + { + // 禁用状态的选项 + Color originalColor = GUI.color; + GameFont originalFont = Text.Font; + Color originalTextColor = GUI.contentColor; - listing.End(); + try + { + // 应用禁用状态的自定义颜色 + if (option.useCustomColors && option.disabledColor.HasValue) + { + GUI.color = option.disabledColor.Value; + } + if (option.useCustomColors && option.textDisabledColor.HasValue) + { + GUI.contentColor = option.textDisabledColor.Value; + } + + Widgets.ButtonText(optionRect, option.label, false, true, false); + TooltipHandler.TipRegion(optionRect, GetDisabledReason(option, reason)); + } + finally + { + // 恢复原始颜色状态 + GUI.color = originalColor; + Text.Font = originalFont; + GUI.contentColor = originalTextColor; + } + } } - // 滚动位置用于描述文本 - private Vector2 scrollPosition = Vector2.zero; + // 应用选项颜色 + private void ApplyOptionColors(EventOption option, Rect rect) + { + if (!option.useCustomColors) + return; + + // 检查鼠标是否悬停在选项上 + bool isMouseOver = Mouse.IsOver(rect); + + // 设置按钮背景颜色 + if (isMouseOver && option.hoverColor.HasValue) + { + GUI.color = option.hoverColor.Value; + } + else if (option.normalColor.HasValue) + { + GUI.color = option.normalColor.Value; + } + + // 设置文本颜色 + if (isMouseOver && option.textHoverColor.HasValue) + { + GUI.contentColor = option.textHoverColor.Value; + } + else if (option.textColor.HasValue) + { + GUI.contentColor = option.textColor.Value; + } + } private void HandleAction(List conditionalEffects) { diff --git a/Source/WulaFallenEmpire/EventSystem/Dialog_NewLayoutDisplay.cs b/Source/WulaFallenEmpire/EventSystem/Dialog_NewLayoutDisplay.cs deleted file mode 100644 index 3816ecf8..00000000 --- a/Source/WulaFallenEmpire/EventSystem/Dialog_NewLayoutDisplay.cs +++ /dev/null @@ -1,277 +0,0 @@ -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.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(); - 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(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.Get(def.portraitPath); - } - - string bgPath = !def.backgroundImagePath.NullOrEmpty() ? def.backgroundImagePath : Config.defaultBackgroundImagePath; - if (!bgPath.NullOrEmpty()) - { - background = ContentFinder.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) - { - if (conditionalEffects.NullOrEmpty()) - { - return; - } - - foreach (var ce in conditionalEffects) - { - if (AreConditionsMet(ce.conditions, out _)) - { - ce.Execute(this); - } - } - } - - private bool AreConditionsMet(List 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(); - // 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 to get any type - return eventVarManager.GetVariable(varName)?.ToString() ?? ""; - } - return match.Value; // Keep placeholder if variable not found - }); - } - } -} diff --git a/Source/WulaFallenEmpire/EventSystem/EventDef.cs b/Source/WulaFallenEmpire/EventSystem/EventDef.cs index 25c26190..a9d6a589 100644 --- a/Source/WulaFallenEmpire/EventSystem/EventDef.cs +++ b/Source/WulaFallenEmpire/EventSystem/EventDef.cs @@ -33,6 +33,8 @@ namespace WulaFallenEmpire public List immediateEffects; public List dismissEffects; public List conditionalDescriptions; + public Color? defaultOptionColor = null; + public Color? defaultOptionTextColor = null; public override void PostLoad() { @@ -65,10 +67,25 @@ namespace WulaFallenEmpire public class EventOption { public string label; - public List optionEffects; public List conditions; public string disabledReason; - public bool hideWhenDisabled = false; + public bool hideWhenDisabled = true; + public List optionEffects; + + // 新增:选项颜色设置 + public Color? normalColor = null; // 正常状态颜色 + public Color? hoverColor = null; // 悬停状态颜色 + public Color? activeColor = null; // 激活状态颜色 + public Color? disabledColor = null; // 禁用状态颜色 + + // 新增:文本颜色设置 + public Color? textColor = null; // 文本颜色 + public Color? textHoverColor = null; // 悬停时文本颜色 + public Color? textActiveColor = null; // 激活时文本颜色 + public Color? textDisabledColor = null;// 禁用时文本颜色 + + // 新增:是否使用自定义颜色 + public bool useCustomColors = false; } public class LoopEffects diff --git a/Source/WulaFallenEmpire/EventSystem/EventUIConfigDef.cs b/Source/WulaFallenEmpire/EventSystem/EventUIConfigDef.cs index 6cbaf70c..cf1ddccd 100644 --- a/Source/WulaFallenEmpire/EventSystem/EventUIConfigDef.cs +++ b/Source/WulaFallenEmpire/EventSystem/EventUIConfigDef.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using UnityEngine; using Verse; @@ -5,30 +6,136 @@ namespace WulaFallenEmpire { public class EventUIConfigDef : Def { - // General Style - public GameFont labelFont = GameFont.Small; - public bool drawBorders = true; - public bool showDefName = true; - public bool showLabel = true; - public string defaultBackgroundImagePath; - public Vector2 defaultWindowSize = new Vector2(750f, 500f); - - // Virtual Layout Dimensions - public Vector2 lihuiSize = new Vector2(500f, 800f); - public Vector2 nameSize = new Vector2(260f, 130f); - public Vector2 textSize = new Vector2(650f, 500f); - public float optionsWidth = 610f; + // ===== 主窗口设置 ===== + public bool showMainWindow = true; + public Vector2 windowSize = new Vector2(600f, 750f); - // 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; + // ===== 游戏控制设置 ===== + public bool pauseGameOnOpen = true; // 新增:窗口打开时是否暂停游戏 + + // ===== 背景设置 ===== + public bool useDefaultBackground = true; + public string customBackgroundImagePath; + + // ===== 元素显示控制 ===== + public bool showPortrait = true; + public bool showLabel = true; + public bool showCharacterName = true; + public bool showDescriptions = true; + public bool showOptions = true; + + // ===== 元素尺寸设置 ===== + public Vector2 portraitSize = new Vector2(600f, 200f); + public Vector2 labelSize = new Vector2(600f, 30f); + public Vector2 characterNameSize = new Vector2(600f, 50f); + public Vector2 descriptionsSize = new Vector2(600f, 200f); + public Vector2 optionsListSize = new Vector2(600f, 200f); + public Vector2 optionSize = new Vector2(500f, 30f); + + // ===== 元素间距设置 (x=上间距, y=下间距) ===== + public Vector2 portraitMargins = new Vector2(0f, 20f); + public Vector2 labelMargins = new Vector2(20f, 20f); + public Vector2 characterNameMargins = new Vector2(20f, 20f); + public Vector2 descriptionsMargins = new Vector2(20f, 20f); + public Vector2 optionsListMargins = new Vector2(20f, 0f); + + // ===== 描述区域内边距 (x=上下间距, y=左右间距) ===== + public Vector2 descriptionsPadding = new Vector2(0f, 0f); + + // ===== 选项列表内边距 (x=左右间距, y=上下间距) ===== + public Vector2 optionsListPadding = new Vector2(50f, 20f); + + // ===== 选项设置 ===== + public float optionSpacing = 10f; + + // ===== 调试和样式设置 ===== + public bool drawBorders = false; + public bool showDefName = false; + public GameFont labelFont = GameFont.Small; + + // ===== 计算属性 ===== + public float TotalHeight + { + get + { + float height = 0f; + + if (showPortrait) + height += portraitSize.y + portraitMargins.x + portraitMargins.y; + + if (showLabel) + height += labelSize.y + labelMargins.x + labelMargins.y; + + if (showCharacterName) + height += characterNameSize.y + characterNameMargins.x + characterNameMargins.y; + + if (showDescriptions) + height += descriptionsSize.y + descriptionsMargins.x + descriptionsMargins.y; + + if (showOptions) + height += optionsListSize.y + optionsListMargins.x + optionsListMargins.y; + + return height; + } + } + + // ===== 辅助方法 ===== + public Rect GetScaledRect(Vector2 originalSize, Rect container, bool centerHorizontal = true) + { + float scaleX = container.width / windowSize.x; + float scaleY = container.height / windowSize.y; + float scale = Mathf.Min(scaleX, scaleY); + + Vector2 scaledSize = new Vector2(originalSize.x * scale, originalSize.y * scale); + Vector2 position = new Vector2( + centerHorizontal ? (container.width - scaledSize.x) / 2 : 0, + 0 + ); + + return new Rect(position.x, position.y, scaledSize.x, scaledSize.y); + } + + public float GetScaledMargin(float margin, Rect container) + { + float scaleY = container.height / windowSize.y; + return margin * scaleY; + } + + public Vector2 GetScaledOptionSize(Rect container) + { + float scaleX = container.width / windowSize.x; + float scaleY = container.height / windowSize.y; + float scale = Mathf.Min(scaleX, scaleY); + + return new Vector2(optionSize.x * scale, optionSize.y * scale); + } + + public float GetScaledOptionSpacing(Rect container) + { + float scaleY = container.height / windowSize.y; + return optionSpacing * scaleY; + } + + public Vector2 GetScaledOptionsListPadding(Rect container) + { + float scaleX = container.width / windowSize.x; + float scaleY = container.height / windowSize.y; + + return new Vector2( + optionsListPadding.x * scaleX, + optionsListPadding.y * scaleY + ); + } + + public Vector2 GetScaledDescriptionsPadding(Rect container) + { + float scaleX = container.width / windowSize.x; + float scaleY = container.height / windowSize.y; + + return new Vector2( + descriptionsPadding.x * scaleX, + descriptionsPadding.y * scaleY + ); + } } } diff --git a/Source/WulaFallenEmpire/Flyover/WULA_FlyOverType/CompProperties_FlyOverType.cs b/Source/WulaFallenEmpire/Flyover/WULA_FlyOverType/CompProperties_FlyOverType.cs index 01851a9b..c9243f0c 100644 --- a/Source/WulaFallenEmpire/Flyover/WULA_FlyOverType/CompProperties_FlyOverType.cs +++ b/Source/WulaFallenEmpire/Flyover/WULA_FlyOverType/CompProperties_FlyOverType.cs @@ -4,8 +4,9 @@ namespace WulaFallenEmpire { public class CompProperties_FlyOverType : CompProperties { - public string flyOverType = "default"; // FlyOver 类型标识符 - public bool isRequiredForDrop = true; // 是否是需要用于空投的类型 + public int laneLevel = 0; // 航道等级 + public string flyOverType = "default"; // FlyOver 类型标识符 + public bool isRequiredForDrop = true; // 是否是需要用于空投的类型 public CompProperties_FlyOverType() { @@ -17,6 +18,7 @@ namespace WulaFallenEmpire { private CompProperties_FlyOverType Props => (CompProperties_FlyOverType)props; + public int LaneLevel => Props.laneLevel; public string FlyOverType => Props.flyOverType; public bool IsRequiredForDrop => Props.isRequiredForDrop; @@ -27,7 +29,7 @@ namespace WulaFallenEmpire public override string CompInspectStringExtra() { - return $"FlyOver Type: {FlyOverType}"; + return $"FlyOver Type: {FlyOverType}, Lane Level: {LaneLevel}"; } } } diff --git a/Source/WulaFallenEmpire/Flyover/WULA_SpawnFlyOver/CompAbilityEffect_SpawnFlyOver.cs b/Source/WulaFallenEmpire/Flyover/WULA_SpawnFlyOver/CompAbilityEffect_SpawnFlyOver.cs index 13305c4e..6b0f401d 100644 --- a/Source/WulaFallenEmpire/Flyover/WULA_SpawnFlyOver/CompAbilityEffect_SpawnFlyOver.cs +++ b/Source/WulaFallenEmpire/Flyover/WULA_SpawnFlyOver/CompAbilityEffect_SpawnFlyOver.cs @@ -9,22 +9,128 @@ namespace WulaFallenEmpire public class CompAbilityEffect_SpawnFlyOver : CompAbilityEffect { public new CompProperties_AbilitySpawnFlyOver Props => (CompProperties_AbilitySpawnFlyOver)props; - + // 新增:检查航道等级限制 + public override bool CanApplyOn(LocalTargetInfo target, LocalTargetInfo dest) + { + if (!base.CanApplyOn(target, dest)) + return false; + // 检查航道等级系统 + if (Props.useLaneLevelSystem) + { + string disableReason; + if (!CanSpawnWithLaneLevelCheck(out disableReason)) + { + return false; + } + } + return true; + } + // 新增:航道等级检查逻辑 + private bool CanSpawnWithLaneLevelCheck(out string disableReason) + { + disableReason = null; + Map map = parent.pawn.Map; + if (map == null) + return true; + // 获取地图上所有的 FlyOver 物体 + var existingFlyOvers = map.listerThings.ThingsOfDef(Props.flyOverDef) + .OfType() + .Where(f => f.Spawned && !f.Destroyed) + .ToList(); + // 获取当前技能的航道等级和类型 + int currentLaneLevel = Props.laneLevel; + string currentFlyOverType = Props.flyOverTypeName; + Log.Message($"Lane Level Check: Current level={currentLaneLevel}, type={currentFlyOverType}, existing flyovers={existingFlyOvers.Count}"); + // 规则1:航道等级0可以不受限制召唤 + if (currentLaneLevel == 0) + { + return true; + } + // 规则2:检查是否有更高航道等级的flyover存在 + var higherLevelFlyOvers = existingFlyOvers + .Where(f => GetFlyOverLaneLevel(f) > currentLaneLevel) + .ToList(); + if (higherLevelFlyOvers.Any()) + { + disableReason = "Cannot summon: Higher lane level flyover exists"; + Log.Message($"Cannot summon: Found {higherLevelFlyOvers.Count} higher level flyovers"); + return false; + } + // 规则3:检查是否有相同航道等级和类型的flyover存在 + var sameTypeAndLevelFlyOvers = existingFlyOvers + .Where(f => GetFlyOverLaneLevel(f) == currentLaneLevel && + GetFlyOverType(f) == currentFlyOverType) + .ToList(); + if (sameTypeAndLevelFlyOvers.Any()) + { + disableReason = $"Cannot summon: Same lane level and type flyover already exists"; + Log.Message($"Cannot summon: Found {sameTypeAndLevelFlyOvers.Count} same type and level flyovers"); + return false; + } + return true; + } + // 新增:获取FlyOver的航道等级 + private int GetFlyOverLaneLevel(FlyOver flyOver) + { + var comp = flyOver.GetComp(); + return comp?.LaneLevel ?? 0; + } + // 新增:获取FlyOver的类型 + private string GetFlyOverType(FlyOver flyOver) + { + var comp = flyOver.GetComp(); + return comp?.FlyOverType ?? "default"; + } + // 新增:强制销毁低等级FlyOver + private void DestroyLowerLevelFlyOvers() + { + Map map = parent.pawn.Map; + if (map == null) return; + var existingFlyOvers = map.listerThings.ThingsOfDef(Props.flyOverDef) + .OfType() + .Where(f => f.Spawned && !f.Destroyed) + .ToList(); + int currentLaneLevel = Props.laneLevel; + string currentFlyOverType = Props.flyOverTypeName; + // 查找需要销毁的低等级FlyOver + var flyOversToDestroy = existingFlyOvers + .Where(f => GetFlyOverLaneLevel(f) < currentLaneLevel || + (GetFlyOverLaneLevel(f) == currentLaneLevel && + GetFlyOverType(f) != currentFlyOverType)) + .ToList(); + foreach (var flyOver in flyOversToDestroy) + { + Log.Message($"Destroying lower level flyover: Level={GetFlyOverLaneLevel(flyOver)}, Type={GetFlyOverType(flyOver)}"); + flyOver.EmergencyDestroy(); + } + if (flyOversToDestroy.Count > 0) + { + Log.Message($"Destroyed {flyOversToDestroy.Count} lower level flyovers"); + } + } public override void Apply(LocalTargetInfo target, LocalTargetInfo dest) { base.Apply(target, dest); - if (parent.pawn == null || parent.pawn.Map == null) return; - try { + // 检查航道等级系统 + if (Props.useLaneLevelSystem) + { + string disableReason; + if (!CanSpawnWithLaneLevelCheck(out disableReason)) + { + Messages.Message(disableReason, MessageTypeDefOf.RejectInput); + return; + } + // 强制销毁低等级FlyOver + DestroyLowerLevelFlyOvers(); + } Log.Message($"FlyOver skill activated by {parent.pawn.Label} at position {parent.pawn.Position}"); Log.Message($"Target cell: {target.Cell}, Dest: {dest.Cell}"); - // 计算起始和结束位置 IntVec3 startPos, endPos; - // 根据进场类型选择不同的计算方法 if (Props.approachType == ApproachType.Perpendicular) { @@ -35,35 +141,16 @@ namespace WulaFallenEmpire startPos = CalculateStartPosition(target); endPos = CalculateEndPosition(target, startPos); } - // 确保位置安全 startPos = GetSafeMapPosition(startPos, parent.pawn.Map); endPos = GetSafeMapPosition(endPos, parent.pawn.Map); - + // 验证并优化飞行路径 + if (!ValidateAndOptimizePath(ref startPos, ref endPos, target.Cell)) + { + Log.Warning("FlyOver path validation failed, using fallback path"); + CalculateFallbackPath(target, out startPos, out endPos); + } Log.Message($"Final positions - Start: {startPos}, End: {endPos}"); - - // 验证位置是否有效 - if (!startPos.InBounds(parent.pawn.Map)) - { - Log.Warning($"Start position {startPos} is out of bounds, adjusting to map center"); - startPos = parent.pawn.Map.Center; - } - - if (!endPos.InBounds(parent.pawn.Map)) - { - Log.Warning($"End position {endPos} is out of bounds, adjusting to map center"); - endPos = parent.pawn.Map.Center; - } - - // 确保起点和终点不同 - if (startPos == endPos) - { - Log.Warning($"FlyOver start and end positions are the same: {startPos}. Adjusting end position."); - IntVec3 randomOffset = new IntVec3(Rand.Range(-10, 11), 0, Rand.Range(-10, 11)); - endPos += randomOffset; - endPos = GetSafeMapPosition(endPos, parent.pawn.Map); - } - // 根据类型创建不同的飞越物体 switch (Props.flyOverType) { @@ -84,6 +171,533 @@ namespace WulaFallenEmpire Log.Error($"Error spawning fly over: {ex}"); } } + // 更新:技能提示信息,包含航道等级信息 + public override string ExtraLabelMouseAttachment(LocalTargetInfo target) + { + string baseInfo = ""; + if (Props.enableGroundStrafing) + { + baseInfo = $"扫射区域: {Props.strafeWidth * 2 + 1}格宽度"; + } + else if (Props.enableSectorSurveillance) + { + baseInfo = $"扇形监视: 约{Props.strafeWidth * 2 + 1}格宽度\n(具体参数在飞行物定义中)"; + } + // 添加航道等级信息 + if (Props.useLaneLevelSystem) + { + if (!string.IsNullOrEmpty(baseInfo)) + baseInfo += "\n"; + baseInfo += $"航道等级: {Props.laneLevel}"; + // 检查是否可以被召唤 + string disableReason; + if (!CanSpawnWithLaneLevelCheck(out disableReason)) + { + baseInfo += $"\n{disableReason}"; + } + } + return baseInfo; + } + // 更新:验证方法,包含航道等级检查 + public override bool Valid(LocalTargetInfo target, bool throwMessages = false) + { + if (!base.Valid(target, throwMessages)) + return false; + if (parent.pawn == null || parent.pawn.Map == null) + return false; + if (!target.Cell.IsValid || !target.Cell.InBounds(parent.pawn.Map)) + return false; + // 航道等级检查 + if (Props.useLaneLevelSystem) + { + string disableReason; + if (!CanSpawnWithLaneLevelCheck(out disableReason)) + { + if (throwMessages) + { + Messages.Message(disableReason, MessageTypeDefOf.RejectInput); + } + return false; + } + } + return true; + } + // 更新:创建标准飞越方法,添加航道等级组件 + private void CreateStandardFlyOver(IntVec3 startPos, IntVec3 endPos) + { + ThingDef flyOverDef = Props.flyOverDef ?? DefDatabase.GetNamedSilentFail("ARA_HiveShip"); + if (flyOverDef == null) + { + Log.Warning("No fly over def specified for standard fly over"); + return; + } + FlyOver flyOver = FlyOver.MakeFlyOver( + flyOverDef, + startPos, + endPos, + parent.pawn.Map, + Props.flightSpeed, + Props.altitude + ); + flyOver.spawnContentsOnImpact = Props.dropContentsOnImpact; + flyOver.playFlyOverSound = Props.playFlyOverSound; + // 设置航道等级信息 + if (Props.useLaneLevelSystem) + { + var laneComp = flyOver.GetComp(); + if (laneComp != null) + { + Log.Message($"FlyOver created with lane level: {Props.laneLevel}, type: {Props.flyOverTypeName}"); + } + else + { + Log.Warning("FlyOver def does not have CompFlyOverType component!"); + } + } + if (Props.customSound != null) + { + // 自定义音效逻辑 + } + Log.Message($"Standard FlyOver created: {flyOver} from {startPos} to {endPos}"); + } + // 更新:创建地面扫射飞越,添加航道等级组件 + private void CreateGroundStrafingFlyOver(IntVec3 startPos, IntVec3 endPos, IntVec3 targetCell) + { + ThingDef flyOverDef = Props.flyOverDef ?? DefDatabase.GetNamedSilentFail("ARA_HiveCorvette"); + if (flyOverDef == null) + { + Log.Warning("No fly over def specified for ground strafing fly over"); + return; + } + FlyOver flyOver = FlyOver.MakeFlyOver( + flyOverDef, + startPos, + endPos, + parent.pawn.Map, + Props.flightSpeed, + Props.altitude, + casterPawn: parent.pawn + ); + // 设置基本属性 + flyOver.spawnContentsOnImpact = Props.dropContentsOnImpact; + flyOver.playFlyOverSound = Props.playFlyOverSound; + // 设置航道等级信息 + if (Props.useLaneLevelSystem) + { + var laneComp = flyOver.GetComp(); + if (laneComp != null) + { + Log.Message($"Ground Strafing FlyOver created with lane level: {Props.laneLevel}, type: {Props.flyOverTypeName}"); + } + } + // 获取扫射组件并设置预处理后的目标单元格 + CompGroundStrafing strafingComp = flyOver.GetComp(); + if (strafingComp != null) + { + // 修复:计算扫射区域的所有单元格,以目标单元格为中心 + Vector3 flightDirection = (endPos.ToVector3() - startPos.ToVector3()).normalized; + List potentialTargetCells = CalculateStrafingImpactCells(targetCell, flightDirection); + if (potentialTargetCells.Count > 0) + { + // 预处理:根据概率筛选实际会被射击的单元格 + List confirmedTargetCells = PreprocessStrafingTargets( + potentialTargetCells, + Props.strafeFireChance + ); + if (confirmedTargetCells.Count > 0) + { + strafingComp.SetConfirmedTargets(confirmedTargetCells); + } + else + { + Log.Warning("No confirmed target cells after preprocessing!"); + } + } + else + { + Log.Error("No potential target cells calculated for ground strafing!"); + } + } + else + { + Log.Error("FlyOver def does not have CompGroundStrafing component!"); + } + } + // 更新:创建扇形监视飞越,添加航道等级组件 + private void CreateSectorSurveillanceFlyOver(IntVec3 startPos, IntVec3 endPos) + { + ThingDef flyOverDef = Props.flyOverDef ?? DefDatabase.GetNamedSilentFail("ARA_HiveCorvette"); + if (flyOverDef == null) + { + Log.Warning("No fly over def specified for sector surveillance fly over"); + return; + } + FlyOver flyOver = FlyOver.MakeFlyOver( + flyOverDef, + startPos, + endPos, + parent.pawn.Map, + Props.flightSpeed, + Props.altitude, + casterPawn: parent.pawn + ); + // 设置基本属性 + flyOver.spawnContentsOnImpact = Props.dropContentsOnImpact; + flyOver.playFlyOverSound = Props.playFlyOverSound; + // 设置航道等级信息 + if (Props.useLaneLevelSystem) + { + var laneComp = flyOver.GetComp(); + if (laneComp != null) + { + Log.Message($"Sector Surveillance FlyOver created with lane level: {Props.laneLevel}, type: {Props.flyOverTypeName}"); + } + } + Log.Message($"SectorSurveillance FlyOver created: {flyOver} from {startPos} to {endPos}"); + } + // 新增:验证和优化飞行路径 + private bool ValidateAndOptimizePath(ref IntVec3 startPos, ref IntVec3 endPos, IntVec3 targetCell) + { + Map map = parent.pawn.Map; + + // 检查路径是否有效 + if (!startPos.InBounds(map) || !endPos.InBounds(map)) + return false; + + // 检查起点和终点是否相同 + if (startPos == endPos) + return false; + + // 检查路径长度是否合理 + float distance = Vector3.Distance(startPos.ToVector3(), endPos.ToVector3()); + if (distance < 10f) // 最小飞行距离 + return false; + + // 检查路径是否经过目标区域附近 + if (!IsPathNearTarget(startPos, endPos, targetCell)) + { + // 优化路径使其经过目标区域 + OptimizePathForTarget(ref startPos, ref endPos, targetCell); + } + + return true; + } + // 新增:检查路径是否经过目标区域 + private bool IsPathNearTarget(IntVec3 startPos, IntVec3 endPos, IntVec3 targetCell) + { + Vector3 start = startPos.ToVector3(); + Vector3 end = endPos.ToVector3(); + Vector3 target = targetCell.ToVector3(); + + // 计算点到直线的距离 + Vector3 lineDirection = (end - start).normalized; + Vector3 pointToLine = target - start; + Vector3 projection = Vector3.Project(pointToLine, lineDirection); + Vector3 perpendicular = pointToLine - projection; + + float distanceToLine = perpendicular.magnitude; + + // 检查投影是否在线段内 + float projectionLength = projection.magnitude; + float lineLength = (end - start).magnitude; + bool isWithinSegment = projectionLength >= 0 && projectionLength <= lineLength; + + // 如果距离小于15格且在路径线段内,则认为路径经过目标 + return distanceToLine <= 15f && isWithinSegment; + } + // 新增:优化路径使其经过目标区域 + private void OptimizePathForTarget(ref IntVec3 startPos, ref IntVec3 endPos, IntVec3 targetCell) + { + Map map = parent.pawn.Map; + Vector3 target = targetCell.ToVector3(); + + // 计算从目标点到地图边缘的方向 + Vector3[] directions = { + new Vector3(1, 0, 0), // 右 + new Vector3(-1, 0, 0), // 左 + new Vector3(0, 0, 1), // 上 + new Vector3(0, 0, -1) // 下 + }; + + // 找到两个相对的方向,确保路径穿过目标 + Vector3 bestStartDir = Vector3.zero; + Vector3 bestEndDir = Vector3.zero; + float bestScore = float.MinValue; + + for (int i = 0; i < directions.Length; i++) + { + for (int j = i + 1; j < directions.Length; j++) + { + Vector3 dir1 = directions[i]; + Vector3 dir2 = directions[j]; + + // 检查两个方向是否相对(夹角接近180度) + float dot = Vector3.Dot(dir1, dir2); + if (dot < -0.5f) // 相对方向 + { + IntVec3 testStart = FindMapEdgeInDirection(map, targetCell, dir1); + IntVec3 testEnd = FindMapEdgeInDirection(map, targetCell, dir2); + + if (testStart.InBounds(map) && testEnd.InBounds(map)) + { + // 计算路径质量:路径长度和是否经过目标 + float pathLength = Vector3.Distance(testStart.ToVector3(), testEnd.ToVector3()); + bool passesNearTarget = IsPathNearTarget(testStart, testEnd, targetCell); + + float score = pathLength + (passesNearTarget ? 50f : 0f); + + if (score > bestScore) + { + bestScore = score; + bestStartDir = dir1; + bestEndDir = dir2; + } + } + } + } + } + + // 应用最佳路径 + if (bestStartDir != Vector3.zero && bestEndDir != Vector3.zero) + { + startPos = FindMapEdgeInDirection(map, targetCell, bestStartDir); + endPos = FindMapEdgeInDirection(map, targetCell, bestEndDir); + Log.Message($"Optimized path: {startPos} -> {targetCell} -> {endPos}"); + } + } + // 新增:备用路径计算方法 + private void CalculateFallbackPath(LocalTargetInfo target, out IntVec3 startPos, out IntVec3 endPos) + { + Map map = parent.pawn.Map; + IntVec3 targetPos = target.Cell; + + // 使用简单的对角路径 + if (Rand.Value < 0.5f) + { + // 从左下到右上 + startPos = new IntVec3(0, 0, 0); + endPos = new IntVec3(map.Size.x - 1, 0, map.Size.z - 1); + } + else + { + // 从右下到左上 + startPos = new IntVec3(map.Size.x - 1, 0, 0); + endPos = new IntVec3(0, 0, map.Size.z - 1); + } + + // 确保位置在地图范围内 + startPos = GetSafeMapPosition(startPos, map); + endPos = GetSafeMapPosition(endPos, map); + + Log.Message($"Fallback path: {startPos} -> {endPos}"); + } + // 改进的垂直线进场路径计算方法 + private void CalculatePerpendicularPath(LocalTargetInfo target, out IntVec3 startPos, out IntVec3 endPos) + { + Map map = parent.pawn.Map; + IntVec3 casterPos = parent.pawn.Position; + IntVec3 targetPos = target.Cell; + Log.Message($"Calculating perpendicular path: Caster={casterPos}, Target={targetPos}"); + // 计算施法者到目标的方向向量 + Vector3 directionToTarget = (targetPos.ToVector3() - casterPos.ToVector3()).normalized; + + // 如果方向为零向量,使用随机方向 + if (directionToTarget == Vector3.zero) + { + directionToTarget = new Vector3(Rand.Range(-1f, 1f), 0, Rand.Range(-1f, 1f)).normalized; + Log.Message($"Using random direction: {directionToTarget}"); + } + // 计算垂直于施法者-目标连线的方向(旋转90度) + Vector3 perpendicularDirection = new Vector3(-directionToTarget.z, 0, directionToTarget.x).normalized; + + Log.Message($"Perpendicular direction: {perpendicularDirection}"); + // 改进:从目标点出发,向垂直方向的两侧延伸找到地图边缘 + // 确保两个边缘点在目标点的相对两侧 + IntVec3 edge1 = FindMapEdgeInDirection(map, targetPos, perpendicularDirection); + IntVec3 edge2 = FindMapEdgeInDirection(map, targetPos, -perpendicularDirection); + // 验证路径质量 + Vector3 edge1Vec = edge1.ToVector3(); + Vector3 edge2Vec = edge2.ToVector3(); + Vector3 targetVec = targetPos.ToVector3(); + + // 检查两个边缘点是否在目标点的相对两侧 + Vector3 toEdge1 = (edge1Vec - targetVec).normalized; + Vector3 toEdge2 = (edge2Vec - targetVec).normalized; + float dot = Vector3.Dot(toEdge1, toEdge2); + + // 如果两个方向相似(夹角小于90度),说明路径有问题 + if (dot > -0.3f) + { + Log.Warning($"Perpendicular path may not cross target properly (dot: {dot}), adjusting..."); + + // 强制使用相对方向 + if (perpendicularDirection.x != 0) + { + // 如果原方向主要是水平,改用垂直方向 + perpendicularDirection = new Vector3(0, 0, 1f); + } + else + { + // 如果原方向主要是垂直,改用水平方向 + perpendicularDirection = new Vector3(1f, 0, 0); + } + + edge1 = FindMapEdgeInDirection(map, targetPos, perpendicularDirection); + edge2 = FindMapEdgeInDirection(map, targetPos, -perpendicularDirection); + } + // 随机选择起点和终点(确保目标点在路径上) + if (Rand.Value < 0.5f) + { + startPos = edge1; + endPos = edge2; + } + else + { + startPos = edge2; + endPos = edge1; + } + Log.Message($"Perpendicular path: {startPos} -> {targetPos} -> {endPos}, path length: {Vector3.Distance(startPos.ToVector3(), endPos.ToVector3())}"); + } + // 改进的地图边缘查找方法 + private IntVec3 FindMapEdgeInDirection(Map map, IntVec3 fromPos, Vector3 direction) + { + // 确保方向向量有效 + if (direction == Vector3.zero) + { + direction = new Vector3(Rand.Range(-1f, 1f), 0, Rand.Range(-1f, 1f)).normalized; + } + + // 使用精确的射线投射方法找到地图边缘 + Vector3 fromVec = fromPos.ToVector3(); + Vector3 dirNormalized = direction.normalized; + + // 计算与地图边界的交点 + float minT = float.MaxValue; + IntVec3? bestEdgePos = null; + + // 检查四个边界 + for (int i = 0; i < 4; i++) + { + float t = 0f; + IntVec3 edgePos = IntVec3.Invalid; + + switch (i) + { + case 0: // 左边界 (x = 0) + if (Mathf.Abs(dirNormalized.x) > 0.001f) + { + t = (0 - fromVec.x) / dirNormalized.x; + if (t > 0) + { + float z = fromVec.z + dirNormalized.z * t; + if (z >= 0 && z < map.Size.z) + { + edgePos = new IntVec3(0, 0, Mathf.RoundToInt(z)); + } + } + } + break; + + case 1: // 右边界 (x = map.Size.x - 1) + if (Mathf.Abs(dirNormalized.x) > 0.001f) + { + t = (map.Size.x - 1 - fromVec.x) / dirNormalized.x; + if (t > 0) + { + float z = fromVec.z + dirNormalized.z * t; + if (z >= 0 && z < map.Size.z) + { + edgePos = new IntVec3(map.Size.x - 1, 0, Mathf.RoundToInt(z)); + } + } + } + break; + + case 2: // 下边界 (z = 0) + if (Mathf.Abs(dirNormalized.z) > 0.001f) + { + t = (0 - fromVec.z) / dirNormalized.z; + if (t > 0) + { + float x = fromVec.x + dirNormalized.x * t; + if (x >= 0 && x < map.Size.x) + { + edgePos = new IntVec3(Mathf.RoundToInt(x), 0, 0); + } + } + } + break; + + case 3: // 上边界 (z = map.Size.z - 1) + if (Mathf.Abs(dirNormalized.z) > 0.001f) + { + t = (map.Size.z - 1 - fromVec.z) / dirNormalized.z; + if (t > 0) + { + float x = fromVec.x + dirNormalized.x * t; + if (x >= 0 && x < map.Size.x) + { + edgePos = new IntVec3(Mathf.RoundToInt(x), 0, map.Size.z - 1); + } + } + } + break; + } + + // 找到最近的有效边界点 + if (edgePos.IsValid && edgePos.InBounds(map) && t > 0 && t < minT) + { + minT = t; + bestEdgePos = edgePos; + } + } + + if (bestEdgePos.HasValue) + { + Log.Message($"Found map edge at {bestEdgePos.Value} in direction {direction}"); + return bestEdgePos.Value; + } + + // 如果没找到合适的边界点,使用随机边缘位置 + Log.Warning($"Could not find map edge in direction {direction}, using random edge"); + return GetRandomMapEdgePosition(map); + } + // 改进的随机地图边缘位置获取 + private IntVec3 GetRandomMapEdgePosition(Map map) + { + // 避免选择角落位置,因为角落容易导致路径问题 + int edge = Rand.Range(0, 4); + int x, z; + + switch (edge) + { + case 0: // 上边 (避免左右角落) + x = Rand.Range(5, map.Size.x - 5); + z = 0; + break; + case 1: // 右边 (避免上下角落) + x = map.Size.x - 1; + z = Rand.Range(5, map.Size.z - 5); + break; + case 2: // 下边 (避免左右角落) + x = Rand.Range(5, map.Size.x - 5); + z = map.Size.z - 1; + break; + case 3: // 左边 (避免上下角落) + default: + x = 0; + z = Rand.Range(5, map.Size.z - 5); + break; + } + + // 确保位置有效 + x = Mathf.Clamp(x, 0, map.Size.x - 1); + z = Mathf.Clamp(z, 0, map.Size.z - 1); + + IntVec3 edgePos = new IntVec3(x, 0, z); + Log.Message($"Random map edge position (avoiding corners): {edgePos}"); + return edgePos; + } // 修复的预览绘制方法 public override void DrawEffectPreview(LocalTargetInfo target) @@ -93,7 +707,7 @@ namespace WulaFallenEmpire if (parent.pawn != null && parent.pawn.Map != null) { Map map = parent.pawn.Map; - + try { // 计算飞行路径 @@ -139,11 +753,11 @@ namespace WulaFallenEmpire private IntVec3 GetSafeMapPosition(IntVec3 pos, Map map) { if (map == null) return pos; - + // 确保位置在地图范围内 pos.x = Mathf.Clamp(pos.x, 0, map.Size.x - 1); pos.z = Mathf.Clamp(pos.z, 0, map.Size.z - 1); - + return pos; } @@ -151,17 +765,17 @@ namespace WulaFallenEmpire private bool IsPreviewStable(IntVec3 startPos, IntVec3 endPos, Map map) { if (map == null) return false; - + // 检查位置是否有效 if (!startPos.IsValid || !endPos.IsValid) return false; - + // 检查位置是否在地图范围内 if (!startPos.InBounds(map) || !endPos.InBounds(map)) return false; - + // 检查距离是否合理(避免过短的路径) float distance = Vector3.Distance(startPos.ToVector3(), endPos.ToVector3()); if (distance < 5f) return false; - + return true; } @@ -201,45 +815,45 @@ namespace WulaFallenEmpire { List cells = new List(); Map map = parent.pawn.Map; - + // 计算垂直于飞行方向的方向 Vector3 perpendicular = new Vector3(-flightDirection.z, 0f, flightDirection.x).normalized; - + // 修复:以目标单元格为中心计算扫射区域 Vector3 targetCenter = targetCell.ToVector3(); - + // 计算扫射区域的起始和结束位置(基于扫射长度,以目标为中心) float strafeHalfLength = Props.strafeLength * 0.5f; Vector3 strafeStart = targetCenter - flightDirection * strafeHalfLength; Vector3 strafeEnd = targetCenter + flightDirection * strafeHalfLength; - + // 使用整数步进避免浮点精度问题 int steps = Mathf.Max(1, Mathf.CeilToInt(Props.strafeLength)); for (int i = 0; i <= steps; i++) { float progress = (float)i / steps; Vector3 centerPoint = Vector3.Lerp(strafeStart, strafeEnd, progress); - + // 在垂直方向扩展扫射宽度 for (int w = -Props.strafeWidth; w <= Props.strafeWidth; w++) { Vector3 offset = perpendicular * w; Vector3 cellPos = centerPoint + offset; - + // 使用更精确的单元格转换 IntVec3 cell = new IntVec3( Mathf.RoundToInt(cellPos.x), - Mathf.RoundToInt(cellPos.y), + Mathf.RoundToInt(cellPos.y), Mathf.RoundToInt(cellPos.z) ); - + if (cell.InBounds(map) && !cells.Contains(cell)) { cells.Add(cell); } } } - + Log.Message($"Strafing Area: Calculated {cells.Count} impact cells centered at {targetCell}"); return cells; } @@ -249,37 +863,37 @@ namespace WulaFallenEmpire { Map map = parent.pawn.Map; Vector3 perpendicular = new Vector3(-flightDirection.z, 0f, flightDirection.x).normalized; - + // 修复:以目标单元格为中心 Vector3 targetCenter = targetCell.ToVector3(); - + // 计算扫射区域的起始和结束位置 float strafeHalfLength = Props.strafeLength * 0.5f; Vector3 strafeStart = targetCenter - flightDirection * strafeHalfLength; Vector3 strafeEnd = targetCenter + flightDirection * strafeHalfLength; - + // 计算扫射区域的四个角 Vector3 startLeft = strafeStart + perpendicular * Props.strafeWidth; Vector3 startRight = strafeStart - perpendicular * Props.strafeWidth; Vector3 endLeft = strafeEnd + perpendicular * Props.strafeWidth; Vector3 endRight = strafeEnd - perpendicular * Props.strafeWidth; - + // 转换为 IntVec3 并确保在地图范围内 IntVec3 startLeftCell = GetSafeMapPosition(new IntVec3((int)startLeft.x, (int)startLeft.y, (int)startLeft.z), map); IntVec3 startRightCell = GetSafeMapPosition(new IntVec3((int)startRight.x, (int)startRight.y, (int)startRight.z), map); IntVec3 endLeftCell = GetSafeMapPosition(new IntVec3((int)endLeft.x, (int)endLeft.y, (int)endLeft.z), map); IntVec3 endRightCell = GetSafeMapPosition(new IntVec3((int)endRight.x, (int)endRight.y, (int)endRight.z), map); - + // 绘制边界线 - 只绘制在地图范围内的线段 if (startLeftCell.InBounds(map) && endLeftCell.InBounds(map)) GenDraw.DrawLineBetween(startLeftCell.ToVector3Shifted(), endLeftCell.ToVector3Shifted(), SimpleColor.Red, 0.2f); - + if (startRightCell.InBounds(map) && endRightCell.InBounds(map)) GenDraw.DrawLineBetween(startRightCell.ToVector3Shifted(), endRightCell.ToVector3Shifted(), SimpleColor.Red, 0.2f); - + if (startLeftCell.InBounds(map) && startRightCell.InBounds(map)) GenDraw.DrawLineBetween(startLeftCell.ToVector3Shifted(), startRightCell.ToVector3Shifted(), SimpleColor.Red, 0.2f); - + if (endLeftCell.InBounds(map) && endRightCell.InBounds(map)) GenDraw.DrawLineBetween(endLeftCell.ToVector3Shifted(), endRightCell.ToVector3Shifted(), SimpleColor.Red, 0.2f); } @@ -288,7 +902,7 @@ namespace WulaFallenEmpire private void DrawSectorAreaPreview(IntVec3 startPos, IntVec3 endPos) { Map map = parent.pawn.Map; - + // 计算飞行方向 Vector3 flightDirection = (endPos.ToVector3() - startPos.ToVector3()).normalized; if (flightDirection == Vector3.zero) @@ -301,7 +915,7 @@ namespace WulaFallenEmpire // 使用strafeWidth来近似扇形扫过的区域宽度 List previewCells = CalculateRectangularPreviewArea(startPos, endPos, flightDirection, perpendicular); - + // 绘制预览区域 foreach (IntVec3 cell in previewCells) { @@ -313,7 +927,7 @@ namespace WulaFallenEmpire // 绘制飞行路径线 GenDraw.DrawLineBetween(startPos.ToVector3Shifted(), endPos.ToVector3Shifted(), SimpleColor.Blue, 0.2f); - + // 绘制预览区域边界 DrawRectangularPreviewBoundaries(startPos, endPos, flightDirection, perpendicular); } @@ -323,37 +937,37 @@ namespace WulaFallenEmpire { List cells = new List(); Map map = parent.pawn.Map; - + // 计算飞行路径的总长度 float totalPathLength = Vector3.Distance(startPos.ToVector3(), endPos.ToVector3()); - + // 沿着飞行路径计算预览单元格 int steps = Mathf.Max(1, Mathf.CeilToInt(totalPathLength)); for (int i = 0; i <= steps; i++) { float progress = (float)i / steps; Vector3 centerPoint = Vector3.Lerp(startPos.ToVector3(), endPos.ToVector3(), progress); - + // 在垂直方向扩展预览宽度(使用strafeWidth) for (int w = -Props.strafeWidth; w <= Props.strafeWidth; w++) { Vector3 offset = perpendicular * w; Vector3 cellPos = centerPoint + offset; - + // 使用精确的单元格转换 IntVec3 cell = new IntVec3( Mathf.RoundToInt(cellPos.x), Mathf.RoundToInt(cellPos.y), Mathf.RoundToInt(cellPos.z) ); - + if (cell.InBounds(map) && !cells.Contains(cell)) { cells.Add(cell); } } } - + return cells; } @@ -361,29 +975,29 @@ namespace WulaFallenEmpire private void DrawRectangularPreviewBoundaries(IntVec3 startPos, IntVec3 endPos, Vector3 flightDirection, Vector3 perpendicular) { Map map = parent.pawn.Map; - + // 计算预览区域的四个角 Vector3 startLeft = startPos.ToVector3() + perpendicular * Props.strafeWidth; Vector3 startRight = startPos.ToVector3() - perpendicular * Props.strafeWidth; Vector3 endLeft = endPos.ToVector3() + perpendicular * Props.strafeWidth; Vector3 endRight = endPos.ToVector3() - perpendicular * Props.strafeWidth; - + // 转换为 IntVec3 并确保在地图范围内 IntVec3 startLeftCell = GetSafeMapPosition(new IntVec3((int)startLeft.x, (int)startLeft.y, (int)startLeft.z), map); IntVec3 startRightCell = GetSafeMapPosition(new IntVec3((int)startRight.x, (int)startRight.y, (int)startRight.z), map); IntVec3 endLeftCell = GetSafeMapPosition(new IntVec3((int)endLeft.x, (int)endLeft.y, (int)endLeft.z), map); IntVec3 endRightCell = GetSafeMapPosition(new IntVec3((int)endRight.x, (int)endRight.y, (int)endRight.z), map); - + // 绘制边界线 - 只绘制在地图范围内的线段 if (startLeftCell.InBounds(map) && endLeftCell.InBounds(map)) GenDraw.DrawLineBetween(startLeftCell.ToVector3Shifted(), endLeftCell.ToVector3Shifted(), SimpleColor.Blue, 0.2f); - + if (startRightCell.InBounds(map) && endRightCell.InBounds(map)) GenDraw.DrawLineBetween(startRightCell.ToVector3Shifted(), endRightCell.ToVector3Shifted(), SimpleColor.Blue, 0.2f); - + if (startLeftCell.InBounds(map) && startRightCell.InBounds(map)) GenDraw.DrawLineBetween(startLeftCell.ToVector3Shifted(), startRightCell.ToVector3Shifted(), SimpleColor.Blue, 0.2f); - + if (endLeftCell.InBounds(map) && endRightCell.InBounds(map)) GenDraw.DrawLineBetween(endLeftCell.ToVector3Shifted(), endRightCell.ToVector3Shifted(), SimpleColor.Blue, 0.2f); } @@ -411,7 +1025,7 @@ namespace WulaFallenEmpire // 如果超出最大值,随机丢弃一些目标 confirmedTargets = confirmedTargets.InRandomOrder().Take(Props.maxStrafeProjectiles).ToList(); } - + if (Props.minStrafeProjectiles > -1 && confirmedTargets.Count < Props.minStrafeProjectiles) { // 如果不足最小值,从之前未选中的格子里补充 @@ -421,268 +1035,30 @@ namespace WulaFallenEmpire confirmedTargets.AddRange(missedCells.InRandomOrder().Take(Mathf.Min(needed, missedCells.Count))); } } - + Log.Message($"Strafing Preprocess: {confirmedTargets.Count}/{potentialTargets.Count} cells confirmed after min/max adjustment."); return confirmedTargets; } - // 修复:创建地面扫射飞越,现在接受目标单元格参数 - private void CreateGroundStrafingFlyOver(IntVec3 startPos, IntVec3 endPos, IntVec3 targetCell) - { - ThingDef flyOverDef = Props.flyOverDef ?? DefDatabase.GetNamedSilentFail("ARA_HiveCorvette"); - if (flyOverDef == null) - { - Log.Warning("No fly over def specified for ground strafing fly over"); - return; - } - - FlyOver flyOver = FlyOver.MakeFlyOver( - flyOverDef, - startPos, - endPos, - parent.pawn.Map, - Props.flightSpeed, - Props.altitude, - casterPawn: parent.pawn - ); - - // 设置基本属性 - flyOver.spawnContentsOnImpact = Props.dropContentsOnImpact; - flyOver.playFlyOverSound = Props.playFlyOverSound; - - // 获取扫射组件并设置预处理后的目标单元格 - CompGroundStrafing strafingComp = flyOver.GetComp(); - if (strafingComp != null) - { - // 修复:计算扫射区域的所有单元格,以目标单元格为中心 - Vector3 flightDirection = (endPos.ToVector3() - startPos.ToVector3()).normalized; - List potentialTargetCells = CalculateStrafingImpactCells(targetCell, flightDirection); - - if (potentialTargetCells.Count > 0) - { - // 预处理:根据概率筛选实际会被射击的单元格 - List confirmedTargetCells = PreprocessStrafingTargets( - potentialTargetCells, - Props.strafeFireChance - ); - - if (confirmedTargetCells.Count > 0) - { - strafingComp.SetConfirmedTargets(confirmedTargetCells); - } - else - { - Log.Warning("No confirmed target cells after preprocessing!"); - } - } - else - { - Log.Error("No potential target cells calculated for ground strafing!"); - } - } - else - { - Log.Error("FlyOver def does not have CompGroundStrafing component!"); - } - } - - // 创建扇形监视飞越 - private void CreateSectorSurveillanceFlyOver(IntVec3 startPos, IntVec3 endPos) - { - ThingDef flyOverDef = Props.flyOverDef ?? DefDatabase.GetNamedSilentFail("ARA_HiveCorvette"); - if (flyOverDef == null) - { - Log.Warning("No fly over def specified for sector surveillance fly over"); - return; - } - - FlyOver flyOver = FlyOver.MakeFlyOver( - flyOverDef, - startPos, - endPos, - parent.pawn.Map, - Props.flightSpeed, - Props.altitude, - casterPawn: parent.pawn - ); - - // 设置基本属性 - flyOver.spawnContentsOnImpact = Props.dropContentsOnImpact; - flyOver.playFlyOverSound = Props.playFlyOverSound; - - Log.Message($"SectorSurveillance FlyOver created: {flyOver} from {startPos} to {endPos}"); - } - - // 计算垂直线进场路径 - private void CalculatePerpendicularPath(LocalTargetInfo target, out IntVec3 startPos, out IntVec3 endPos) - { - Map map = parent.pawn.Map; - IntVec3 casterPos = parent.pawn.Position; - IntVec3 targetPos = target.Cell; - - Log.Message($"Calculating perpendicular path: Caster={casterPos}, Target={targetPos}"); - - // 计算施法者到目标的方向向量 - Vector3 directionToTarget = (targetPos.ToVector3() - casterPos.ToVector3()).normalized; - - // 如果方向为零向量,使用随机方向 - if (directionToTarget == Vector3.zero) - { - directionToTarget = new Vector3(Rand.Range(-1f, 1f), 0, Rand.Range(-1f, 1f)).normalized; - Log.Message($"Using random direction: {directionToTarget}"); - } - - // 计算垂直于施法者-目标连线的方向(旋转90度) - Vector3 perpendicularDirection = new Vector3(-directionToTarget.z, 0, directionToTarget.x).normalized; - - Log.Message($"Perpendicular direction: {perpendicularDirection}"); - - // 从目标点出发,向垂直方向的两侧延伸找到地图边缘 - IntVec3 edge1 = FindMapEdgeInDirection(map, targetPos, perpendicularDirection); - IntVec3 edge2 = FindMapEdgeInDirection(map, targetPos, -perpendicularDirection); - - // 随机选择起点和终点(确保目标点在路径上) - if (Rand.Value < 0.5f) - { - startPos = edge1; - endPos = edge2; - } - else - { - startPos = edge2; - endPos = edge1; - } - - Log.Message($"Perpendicular path: {startPos} -> {targetPos} -> {endPos}"); - } - - // 在指定方向上找到地图边缘 - private IntVec3 FindMapEdgeInDirection(Map map, IntVec3 fromPos, Vector3 direction) - { - // 确保方向向量有效 - if (direction == Vector3.zero) - { - direction = new Vector3(Rand.Range(-1f, 1f), 0, Rand.Range(-1f, 1f)).normalized; - } - - // 使用更精确的地图边界计算 - IntVec3 mapCenter = map.Center; - IntVec3 mapSize = new IntVec3(map.Size.x, 0, map.Size.z); - - // 计算与地图边界的交点 - Vector3 fromVec = fromPos.ToVector3(); - Vector3 dirNormalized = direction.normalized; - - // 计算到各个边界的距离 - float tMin = float.MaxValue; - IntVec3? bestEdgePos = null; - - // 检查四个边界 - for (int i = 0; i < 4; i++) - { - float t = 0f; - IntVec3 edgePos = IntVec3.Invalid; - - switch (i) - { - case 0: // 左边界 (x = 0) - if (Mathf.Abs(dirNormalized.x) > 0.001f) - { - t = (0 - fromVec.x) / dirNormalized.x; - if (t > 0) - { - float z = fromVec.z + dirNormalized.z * t; - if (z >= 0 && z < map.Size.z) - { - edgePos = new IntVec3(0, 0, Mathf.RoundToInt(z)); - } - } - } - break; - - case 1: // 右边界 (x = map.Size.x - 1) - if (Mathf.Abs(dirNormalized.x) > 0.001f) - { - t = (map.Size.x - 1 - fromVec.x) / dirNormalized.x; - if (t > 0) - { - float z = fromVec.z + dirNormalized.z * t; - if (z >= 0 && z < map.Size.z) - { - edgePos = new IntVec3(map.Size.x - 1, 0, Mathf.RoundToInt(z)); - } - } - } - break; - - case 2: // 下边界 (z = 0) - if (Mathf.Abs(dirNormalized.z) > 0.001f) - { - t = (0 - fromVec.z) / dirNormalized.z; - if (t > 0) - { - float x = fromVec.x + dirNormalized.x * t; - if (x >= 0 && x < map.Size.x) - { - edgePos = new IntVec3(Mathf.RoundToInt(x), 0, 0); - } - } - } - break; - - case 3: // 上边界 (z = map.Size.z - 1) - if (Mathf.Abs(dirNormalized.z) > 0.001f) - { - t = (map.Size.z - 1 - fromVec.z) / dirNormalized.z; - if (t > 0) - { - float x = fromVec.x + dirNormalized.x * t; - if (x >= 0 && x < map.Size.x) - { - edgePos = new IntVec3(Mathf.RoundToInt(x), 0, map.Size.z - 1); - } - } - } - break; - } - - // 找到最近的有效边界点 - if (edgePos.IsValid && edgePos.InBounds(map) && t > 0 && t < tMin) - { - tMin = t; - bestEdgePos = edgePos; - } - } - - if (bestEdgePos.HasValue) - { - return bestEdgePos.Value; - } - - // 如果没找到合适的边界点,使用随机边缘位置 - Log.Warning($"Could not find map edge in direction {direction}, using random edge"); - return GetRandomMapEdgePosition(map); - } - // 原有的位置计算方法 private IntVec3 CalculateStartPosition(LocalTargetInfo target) { Map map = parent.pawn.Map; - + switch (Props.startPosition) { case StartPosition.Caster: return parent.pawn.Position; - + case StartPosition.MapEdge: return GetMapEdgePosition(map, GetDirectionFromCasterToTarget(target)); - + case StartPosition.CustomOffset: return GetSafeMapPosition(parent.pawn.Position + Props.customStartOffset, map); - + case StartPosition.RandomMapEdge: return GetRandomMapEdgePosition(map); - + default: return parent.pawn.Position; } @@ -729,16 +1105,16 @@ namespace WulaFallenEmpire { IntVec3 center = map.Center; Vector3 toCenter = (center.ToVector3() - startPos.ToVector3()).normalized; - + if (toCenter == Vector3.zero) { toCenter = new Vector3(Rand.Range(-1f, 1f), 0, Rand.Range(-1f, 1f)).normalized; Log.Message($"Using random direction to center: {toCenter}"); } - + Vector3 fromCenter = toCenter; IntVec3 oppositeEdge = GetMapEdgePositionFromCenter(map, fromCenter); - + Log.Message($"Found opposite edge through center: {oppositeEdge}"); return oppositeEdge; } @@ -747,14 +1123,14 @@ namespace WulaFallenEmpire { IntVec3 center = map.Center; float maxDist = Mathf.Max(map.Size.x, map.Size.z) * 0.6f; - + for (int i = 1; i <= maxDist; i++) { IntVec3 testPos = center + new IntVec3( Mathf.RoundToInt(direction.x * i), 0, Mathf.RoundToInt(direction.z * i)); - + if (!testPos.InBounds(map)) { IntVec3 edgePos = FindClosestValidPosition(testPos, map); @@ -762,7 +1138,7 @@ namespace WulaFallenEmpire return edgePos; } } - + Log.Warning("Could not find map edge from center, using random edge"); return GetRandomMapEdgePosition(map); } @@ -774,17 +1150,17 @@ namespace WulaFallenEmpire direction = new Vector3(Rand.Range(-1f, 1f), 0, Rand.Range(-1f, 1f)).normalized; Log.Message($"Using random direction: {direction}"); } - + IntVec3 center = map.Center; float maxDist = Mathf.Max(map.Size.x, map.Size.z) * 0.6f; - + for (int i = 1; i <= maxDist; i++) { IntVec3 testPos = center + new IntVec3( Mathf.RoundToInt(direction.x * i), 0, Mathf.RoundToInt(direction.z * i)); - + if (!testPos.InBounds(map)) { IntVec3 edgePos = FindClosestValidPosition(testPos, map); @@ -792,7 +1168,7 @@ namespace WulaFallenEmpire return edgePos; } } - + Log.Warning("Could not find map edge in direction, using random edge"); return GetRandomMapEdgePosition(map); } @@ -810,41 +1186,9 @@ namespace WulaFallenEmpire } } } - + return map.Center; } - - private IntVec3 GetRandomMapEdgePosition(Map map) - { - int edge = Rand.Range(0, 4); - int x, z; - - switch (edge) - { - case 0: // 上边 - x = Rand.Range(0, map.Size.x); - z = 0; - break; - case 1: // 右边 - x = map.Size.x - 1; - z = Rand.Range(0, map.Size.z); - break; - case 2: // 下边 - x = Rand.Range(0, map.Size.x); - z = map.Size.z - 1; - break; - case 3: // 左边 - default: - x = 0; - z = Rand.Range(0, map.Size.z); - break; - } - - IntVec3 edgePos = new IntVec3(x, 0, z); - Log.Message($"Random map edge position: {edgePos}"); - return edgePos; - } - private IntVec3 GetFixedDistancePosition(IntVec3 startPos, IntVec3 targetPos) { Vector3 direction = (targetPos.ToVector3() - startPos.ToVector3()).normalized; @@ -852,7 +1196,7 @@ namespace WulaFallenEmpire (int)(direction.x * Props.flyOverDistance), 0, (int)(direction.z * Props.flyOverDistance)); - + Log.Message($"Fixed distance position: {endPos} (from {startPos}, distance: {Props.flyOverDistance})"); return endPos; } @@ -860,67 +1204,14 @@ namespace WulaFallenEmpire private Vector3 GetDirectionFromCasterToTarget(LocalTargetInfo target) { Vector3 direction = (target.Cell.ToVector3() - parent.pawn.Position.ToVector3()).normalized; - + if (direction == Vector3.zero) { direction = new Vector3(Rand.Range(-1f, 1f), 0, Rand.Range(-1f, 1f)).normalized; Log.Message($"Using random direction: {direction}"); } - + return direction; } - - private void CreateStandardFlyOver(IntVec3 startPos, IntVec3 endPos) - { - ThingDef flyOverDef = Props.flyOverDef ?? DefDatabase.GetNamedSilentFail("ARA_HiveShip"); - if (flyOverDef == null) - { - Log.Warning("No fly over def specified for standard fly over"); - return; - } - - FlyOver flyOver = FlyOver.MakeFlyOver( - flyOverDef, - startPos, - endPos, - parent.pawn.Map, - Props.flightSpeed, - Props.altitude - ); - - flyOver.spawnContentsOnImpact = Props.dropContentsOnImpact; - flyOver.playFlyOverSound = Props.playFlyOverSound; - - if (Props.customSound != null) - { - // 自定义音效逻辑 - } - - Log.Message($"Standard FlyOver created: {flyOver} from {startPos} to {endPos}"); - } - - // 更新技能提示信息 - public override string ExtraLabelMouseAttachment(LocalTargetInfo target) - { - if (Props.enableGroundStrafing) - { - return $"扫射区域: {Props.strafeWidth * 2 + 1}格宽度"; - } - else if (Props.enableSectorSurveillance) - { - return $"扇形监视: 约{Props.strafeWidth * 2 + 1}格宽度\n(具体参数在飞行物定义中)"; - } - - return base.ExtraLabelMouseAttachment(target); - } - - public override bool Valid(LocalTargetInfo target, bool throwMessages = false) - { - return base.Valid(target, throwMessages) && - parent.pawn != null && - parent.pawn.Map != null && - target.Cell.IsValid && - target.Cell.InBounds(parent.pawn.Map); - } } } diff --git a/Source/WulaFallenEmpire/Flyover/WULA_SpawnFlyOver/CompProperties_AbilitySpawnFlyOver.cs b/Source/WulaFallenEmpire/Flyover/WULA_SpawnFlyOver/CompProperties_AbilitySpawnFlyOver.cs index 7d268b2e..188d3a7e 100644 --- a/Source/WulaFallenEmpire/Flyover/WULA_SpawnFlyOver/CompProperties_AbilitySpawnFlyOver.cs +++ b/Source/WulaFallenEmpire/Flyover/WULA_SpawnFlyOver/CompProperties_AbilitySpawnFlyOver.cs @@ -47,6 +47,11 @@ namespace WulaFallenEmpire public bool showSectorPreview = true; // 是否显示扇形预览 public Color sectorPreviewColor = new Color(0.3f, 0.7f, 1f, 0.3f); + // 航道等级系统配置 + public bool useLaneLevelSystem = false; // 是否使用航道等级系统 + public int laneLevel = 0; // 该技能的航道等级 + public string flyOverTypeName = "default"; // FlyOver类型名称 + public CompProperties_AbilitySpawnFlyOver() { this.compClass = typeof(CompAbilityEffect_SpawnFlyOver); diff --git a/Source/WulaFallenEmpire/WulaFallenEmpire.csproj b/Source/WulaFallenEmpire/WulaFallenEmpire.csproj index 534442e0..edc9ecbc 100644 --- a/Source/WulaFallenEmpire/WulaFallenEmpire.csproj +++ b/Source/WulaFallenEmpire/WulaFallenEmpire.csproj @@ -86,7 +86,6 @@ - diff --git a/Untitled-1.html b/Untitled-1.html new file mode 100644 index 00000000..42bc59f5 --- /dev/null +++ b/Untitled-1.html @@ -0,0 +1,88 @@ + + + + + + Document + + + +
    +
    这里是portraits
    +
    这里是label
    +
    这里是characterName
    +
    这里是descriptions
    +
    +
    这里是option
    +
    这里是option
    +
    这里是option
    +
    这里是option
    +
    +
    + + \ No newline at end of file diff --git a/美术与文本源文件/Wula/UI/Commands/WULA_Antenna_Switch.sai2 b/美术与文本源文件/Wula/UI/Commands/WULA_Antenna_Switch.sai2 new file mode 100644 index 00000000..d000d4a8 Binary files /dev/null and b/美术与文本源文件/Wula/UI/Commands/WULA_Antenna_Switch.sai2 differ diff --git a/美术与文本源文件/Wula/UI/EventUI/新建画布2.png b/美术与文本源文件/Wula/UI/EventUI/新建画布2.png index 9e0ecea0..1d2880ea 100644 Binary files a/美术与文本源文件/Wula/UI/EventUI/新建画布2.png and b/美术与文本源文件/Wula/UI/EventUI/新建画布2.png differ diff --git a/美术与文本源文件/Wula/UI/EventUI/新建画布2.sai2 b/美术与文本源文件/Wula/UI/EventUI/新建画布2.sai2 index 4a97189b..6669c884 100644 Binary files a/美术与文本源文件/Wula/UI/EventUI/新建画布2.sai2 and b/美术与文本源文件/Wula/UI/EventUI/新建画布2.sai2 differ diff --git a/美术与文本源文件/Wula/UI/EventUI/新建画布22.png b/美术与文本源文件/Wula/UI/EventUI/新建画布22.png index f30b6a5d..e3af9bea 100644 Binary files a/美术与文本源文件/Wula/UI/EventUI/新建画布22.png and b/美术与文本源文件/Wula/UI/EventUI/新建画布22.png differ