This commit is contained in:
2026-04-30 19:33:16 +08:00
commit fb2252f688
556 changed files with 69721 additions and 0 deletions
@@ -0,0 +1,10 @@
namespace GeometryTD.UI
{
public class CombatFinishFormContext : UIContext
{
public string EnemyKilledText;
public string GoldGainedText;
public RepoItemContext[] RewardItems;
public bool CanReturn;
}
}
@@ -0,0 +1,13 @@
namespace GeometryTD.UI
{
public class CombatInfoFormContext : UIContext
{
public string LevelMetaText;
public string LevelPhaseText;
public string CoinText;
public string BaseHpText;
public string EnemyHpRateText;
public bool CanPause;
public bool CanEnd;
}
}
@@ -0,0 +1,10 @@
namespace GeometryTD.UI
{
public enum CombatSelectActionType : byte
{
None = 0,
BuildTower = 1,
UpgradeTower = 2,
DestroyTower = 3
}
}
@@ -0,0 +1,15 @@
using UnityEngine;
namespace GeometryTD.UI
{
public class CombatSelectFormContext : UIContext
{
public bool IsVisible;
public Vector2 ScreenPosition;
public bool ShowBuildArea;
public bool ShowFuncArea;
public TowerSelectItemContext[] BuildItems;
public TowerSelectItemContext UpgradeItem;
public TowerSelectItemContext DestroyItem;
}
}
@@ -0,0 +1,15 @@
using UnityEngine;
namespace GeometryTD.UI
{
public class TowerSelectItemContext : UIContext
{
public Sprite Icon;
public string PriceText;
public bool IsVisible;
public bool IsInteractable;
public CombatSelectActionType ActionType;
public int ActionIndex;
public TowerIconAreaContext TowerIconAreaContext;
}
}
@@ -0,0 +1,269 @@
using System.Collections.Generic;
using GeometryTD.CustomUtility;
using GeometryTD.Definition;
using UnityEngine;
namespace GeometryTD.UI
{
public partial class CombatFinishFormController
{
private CombatFinishFormContext BuildContext(CombatFinishFormRawData rawData)
{
_itemDescSeedMap.Clear();
if (rawData == null)
{
return null;
}
return new CombatFinishFormContext
{
EnemyKilledText = rawData.DefeatedEnemyCount.ToString(),
GoldGainedText = rawData.GainedGold.ToString(),
RewardItems = BuildRewardItems(rawData.RewardInventory),
CanReturn = rawData.CanReturn
};
}
private RepoItemContext[] BuildRewardItems(BackpackInventoryData inventory)
{
if (inventory == null)
{
return System.Array.Empty<RepoItemContext>();
}
Dictionary<long, MuzzleCompItemData> muzzleMap = BuildComponentMap(inventory.MuzzleComponents);
Dictionary<long, BearingCompItemData> bearingMap = BuildComponentMap(inventory.BearingComponents);
Dictionary<long, BaseCompItemData> baseMap = BuildComponentMap(inventory.BaseComponents);
List<RepoItemContext> itemContexts = new List<RepoItemContext>();
if (inventory.Towers != null)
{
foreach (TowerItemData tower in inventory.Towers)
{
if (tower == null)
{
continue;
}
itemContexts.Add(new RepoItemContext
{
InstanceId = tower.InstanceId,
CanDrag = false,
EnduranceRate01 = ItemDescUtility.ResolveTowerEnduranceRate(tower, muzzleMap, bearingMap, baseMap),
ClickActionType = RepoItemClickActionType.OpenDetail,
ComponentSlotType = TowerCompSlotType.None,
IconAreaContext = BuildTowerIconContext(tower, muzzleMap, bearingMap, baseMap)
});
AddItemDescSeed(
tower.InstanceId,
tower.Name,
"Tower",
ItemDescUtility.BuildTowerDesc(tower, muzzleMap, bearingMap, baseMap) ?? string.Empty,
tower.Stats?.Tags,
tower.Stats?.TagRuntimes);
}
}
if (inventory.MuzzleComponents != null)
{
foreach (MuzzleCompItemData item in inventory.MuzzleComponents)
{
if (item == null)
{
continue;
}
itemContexts.Add(new RepoItemContext
{
InstanceId = item.InstanceId,
CanDrag = false,
EnduranceRate01 = ItemDescUtility.ResolveComponentEnduranceRate(item),
ClickActionType = RepoItemClickActionType.OpenDetail,
ComponentSlotType = TowerCompSlotType.Muzzle,
IconAreaContext = BuildIconAreaContext(item)
});
AddItemDescSeed(
item.InstanceId,
item.Name,
BuildComponentTypeText(item.SlotType),
ItemDescUtility.BuildMuzzleDesc(item) ?? string.Empty,
item.Tags,
null);
}
}
if (inventory.BearingComponents != null)
{
foreach (BearingCompItemData item in inventory.BearingComponents)
{
if (item == null)
{
continue;
}
itemContexts.Add(new RepoItemContext
{
InstanceId = item.InstanceId,
CanDrag = false,
EnduranceRate01 = ItemDescUtility.ResolveComponentEnduranceRate(item),
ClickActionType = RepoItemClickActionType.OpenDetail,
ComponentSlotType = TowerCompSlotType.Bearing,
IconAreaContext = BuildIconAreaContext(item)
});
AddItemDescSeed(
item.InstanceId,
item.Name,
BuildComponentTypeText(item.SlotType),
ItemDescUtility.BuildBearingDesc(item) ?? string.Empty,
item.Tags,
null);
}
}
if (inventory.BaseComponents != null)
{
foreach (BaseCompItemData item in inventory.BaseComponents)
{
if (item == null)
{
continue;
}
itemContexts.Add(new RepoItemContext
{
InstanceId = item.InstanceId,
CanDrag = false,
EnduranceRate01 = ItemDescUtility.ResolveComponentEnduranceRate(item),
ClickActionType = RepoItemClickActionType.OpenDetail,
ComponentSlotType = TowerCompSlotType.Base,
IconAreaContext = BuildIconAreaContext(item)
});
AddItemDescSeed(
item.InstanceId,
item.Name,
BuildComponentTypeText(item.SlotType),
ItemDescUtility.BuildBaseDesc(item) ?? string.Empty,
item.Tags,
null);
}
}
return itemContexts.ToArray();
}
private static TowerIconAreaContext BuildTowerIconContext(
TowerItemData tower,
IReadOnlyDictionary<long, MuzzleCompItemData> muzzleMap,
IReadOnlyDictionary<long, BearingCompItemData> bearingMap,
IReadOnlyDictionary<long, BaseCompItemData> baseMap)
{
if (tower == null)
{
return null;
}
return new TowerIconAreaContext
{
Rarity = tower.Rarity,
MuzzleColor = ResolveComponentColor(tower.MuzzleComponentInstanceId, muzzleMap),
BearingColor = ResolveComponentColor(tower.BearingComponentInstanceId, bearingMap),
BaseColor = ResolveComponentColor(tower.BaseComponentInstanceId, baseMap)
};
}
private static Color ResolveComponentColor<TComp>(long instanceId, IReadOnlyDictionary<long, TComp> componentMap)
where TComp : TowerCompItemData
{
if (instanceId > 0 && componentMap != null && componentMap.TryGetValue(instanceId, out TComp comp) &&
comp != null)
{
return IconColorGenerator.GenerateForComponent(comp);
}
return Color.white;
}
private void AddItemDescSeed(
long itemId,
string title,
string typeText,
string description,
TagType[] tags,
TagRuntimeData[] tagRuntimes)
{
if (itemId <= 0)
{
return;
}
_itemDescSeedMap[itemId] = new ItemDescSeed
{
Title = string.IsNullOrWhiteSpace(title) ? $"Item {itemId}" : title,
TypeText = typeText ?? string.Empty,
Description = description ?? string.Empty,
Tags = tags,
TagRuntimes = tagRuntimes
};
}
private static string BuildComponentTypeText(TowerCompSlotType slotType)
{
return slotType switch
{
TowerCompSlotType.Muzzle => "Muzzle Component",
TowerCompSlotType.Bearing => "Bearing Component",
TowerCompSlotType.Base => "Base Component",
TowerCompSlotType.Accessory => "Accessory",
_ => "Component"
};
}
private static Dictionary<long, TComp> BuildComponentMap<TComp>(IReadOnlyList<TComp> items)
where TComp : TowerCompItemData
{
Dictionary<long, TComp> map = new Dictionary<long, TComp>();
if (items == null)
{
return map;
}
foreach (TComp item in items)
{
if (item == null || item.InstanceId <= 0)
{
continue;
}
map[item.InstanceId] = item;
}
return map;
}
private static IconAreaContext BuildIconAreaContext(TowerCompItemData item)
{
if (item == null)
{
return new IconAreaContext
{
ComponentSlotType = TowerCompSlotType.None,
Rarity = RarityType.None,
Color = Color.white,
Icon = null
};
}
return new IconAreaContext
{
ComponentSlotType = item.SlotType,
Rarity = item.Rarity,
Color = IconColorGenerator.GenerateForComponent(item),
Icon = null
};
}
}
}
@@ -0,0 +1,76 @@
using GeometryTD.Definition;
using UnityEngine;
namespace GeometryTD.UI
{
public partial class CombatInfoFormController
{
private static CombatInfoFormContext BuildContext(CombatInfoFormRawData rawData)
{
if (rawData == null)
{
return null;
}
return new CombatInfoFormContext
{
LevelMetaText = BuildLevelMetaText(rawData.LevelThemeType, rawData.LevelId),
LevelPhaseText = BuildPhaseText(rawData.CurrentPhaseIndex, rawData.TotalPhaseCount),
CoinText = BuildCoinText(rawData.Coin),
BaseHpText = BuildBaseHpText(rawData.BaseHp, rawData.BaseHpMax),
EnemyHpRateText = BuildEnemyHpRateText(rawData.EnemyHpRateMultiplier),
CanPause = rawData.CanPause,
CanEnd = rawData.CanEnd
};
}
private static string BuildLevelMetaText(LevelThemeType themeType, int levelId)
{
if (levelId <= 0)
{
return "Map -";
}
return $"{themeType} Map.{levelId}";
}
private static string BuildPhaseText(int currentPhaseIndex, int totalPhaseCount)
{
int phaseIndex = currentPhaseIndex < 0 ? 0 : currentPhaseIndex;
if (totalPhaseCount <= 0)
{
return $"{phaseIndex}/?";
}
return $"Wave: {phaseIndex}/{totalPhaseCount}";
}
private static string BuildCoinText(int coin)
{
if (coin < 0)
{
return "Coin: -";
}
return $"Coin: {coin}";
}
private static string BuildBaseHpText(int baseHp, int baseHpMax)
{
if (baseHpMax <= 0)
{
return "\u57FA\u5730\uFF1A-";
}
int clampedHp = Mathf.Clamp(baseHp, 0, baseHpMax);
int percent = Mathf.RoundToInt((float)clampedHp / baseHpMax * 100f);
return $"\u57FA\u5730\uFF1A{percent}%";
}
private static string BuildEnemyHpRateText(int enemyHpRateMultiplier)
{
int resolvedMultiplier = enemyHpRateMultiplier > 0 ? enemyHpRateMultiplier : 1;
return $"\u96BE\u5EA6\uFF1A{resolvedMultiplier}x";
}
}
}
@@ -0,0 +1,77 @@
using UnityEngine;
namespace GeometryTD.UI
{
public partial class CombatSelectFormController
{
private static CombatSelectFormContext BuildContext(CombatSelectFormRawData rawData)
{
if (rawData == null)
{
return null;
}
return new CombatSelectFormContext
{
IsVisible = rawData.IsVisible,
ScreenPosition = rawData.ScreenPosition,
ShowBuildArea = rawData.IsVisible && rawData.DisplayMode == CombatSelectDisplayMode.Build,
ShowFuncArea = rawData.IsVisible && rawData.DisplayMode == CombatSelectDisplayMode.Func,
BuildItems = BuildItemContexts(rawData.BuildItems),
UpgradeItem = BuildItemContext(rawData.UpgradeItem),
DestroyItem = BuildItemContext(rawData.DestroyItem)
};
}
private static TowerSelectItemContext[] BuildItemContexts(TowerSelectItemRawData[] itemRawData)
{
if (itemRawData == null || itemRawData.Length <= 0)
{
return System.Array.Empty<TowerSelectItemContext>();
}
TowerSelectItemContext[] contexts = new TowerSelectItemContext[itemRawData.Length];
for (int i = 0; i < itemRawData.Length; i++)
{
contexts[i] = BuildItemContext(itemRawData[i]);
}
return contexts;
}
private static TowerSelectItemContext BuildItemContext(TowerSelectItemRawData rawData)
{
if (rawData == null)
{
return null;
}
return new TowerSelectItemContext
{
Icon = rawData.Icon,
PriceText = BuildPriceText(rawData.Price, rawData.IsGain),
IsVisible = rawData.IsVisible,
IsInteractable = rawData.IsInteractable,
ActionType = rawData.ActionType,
ActionIndex = rawData.ActionIndex,
TowerIconAreaContext = new TowerIconAreaContext
{
BaseColor = rawData.BaseColor,
BearingColor = rawData.BearingColor,
MuzzleColor = rawData.MuzzleColor
}
};
}
private static string BuildPriceText(int price, bool isGain)
{
int positivePrice = Mathf.Max(0, price);
if (isGain)
{
return $"+{positivePrice}";
}
return positivePrice.ToString();
}
}
}
@@ -0,0 +1,152 @@
using System.Collections.Generic;
using GameFramework.Event;
using GeometryTD.CustomEvent;
using GeometryTD.Definition;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace GeometryTD.UI
{
public partial class CombatFinishFormController : UIFormControllerCommonBase<CombatFinishFormContext, CombatFinishForm>
{
private CombatFinishFormUseCase _useCase;
private readonly Dictionary<long, ItemDescSeed> _itemDescSeedMap = new Dictionary<long, ItemDescSeed>();
private sealed class ItemDescSeed
{
public string Title;
public string TypeText;
public string Description;
public TagType[] Tags;
public TagRuntimeData[] TagRuntimes;
}
protected override UIFormType UIFormTypeId => UIFormType.CombatFinishForm;
protected override void RefreshUI(CombatFinishForm form, CombatFinishFormContext context)
{
form.RefreshUI(context);
}
protected override void SubscribeCustomEvents()
{
GameEntry.Event.Subscribe(CombatFinishReturnEventArgs.EventId, OnCombatFinishReturnButtonClicked);
GameEntry.Event.Subscribe(RepoItemClickedEventArgs.EventId, OnRepoItemClicked);
}
protected override void UnsubscribeCustomEvents()
{
GameEntry.Event.Unsubscribe(CombatFinishReturnEventArgs.EventId, OnCombatFinishReturnButtonClicked);
GameEntry.Event.Unsubscribe(RepoItemClickedEventArgs.EventId, OnRepoItemClicked);
}
public override int? OpenUI(object userData = null)
{
if (userData is CombatFinishFormContext context)
{
return OpenUIInternal(context);
}
if (userData is CombatFinishFormRawData rawDataFromUserData)
{
return OpenUI(rawDataFromUserData);
}
if (userData != null)
{
Log.Warning("CombatFinishFormController.OpenUI() userData type is invalid.");
return null;
}
if (_useCase == null)
{
Log.Error("CombatFinishFormController.OpenUI() useCase is null.");
return null;
}
CombatFinishFormRawData rawData = _useCase.CreateInitialModel();
return OpenUI(rawData);
}
public int? OpenUI(CombatFinishFormRawData rawData)
{
CombatFinishFormContext context = BuildContext(rawData);
return OpenUIInternal(context);
}
public override void BindUseCase(IUIUseCase useCase)
{
if (!(useCase is CombatFinishFormUseCase combatFinishFormUseCase))
{
Log.Error("CombatFinishFormController.BindUseCase() useCase is invalid.");
return;
}
_useCase = combatFinishFormUseCase;
}
#region Event Handlers
private void OnCombatFinishReturnButtonClicked(object sender, GameEventArgs e)
{
if (!IsEventFromCurrentForm(sender) || !(e is CombatFinishReturnEventArgs))
{
return;
}
_useCase?.TryReturnToMenu();
}
private void OnRepoItemClicked(object sender, GameEventArgs e)
{
if (!IsEventFromCurrentForm(sender))
{
return;
}
if (!(e is RepoItemClickedEventArgs args))
{
return;
}
if (!_itemDescSeedMap.TryGetValue(args.ItemId, out ItemDescSeed seed))
{
return;
}
GameEntry.UIRouter.OpenUI(UIFormType.ItemDescForm, new ItemDescFormRawData
{
Title = seed.Title,
TypeText = seed.TypeText,
Description = seed.Description ?? string.Empty,
Price = 0,
ScreenPosition = args.ScreenPosition,
Tags = seed.Tags,
TagRuntimes = seed.TagRuntimes
});
}
#endregion
private bool IsEventFromCurrentForm(object sender)
{
if (Form == null)
{
return false;
}
if (ReferenceEquals(sender, Form))
{
return true;
}
if (sender is Component component)
{
CombatFinishForm ownerForm = component.GetComponentInParent<CombatFinishForm>();
return ownerForm == Form;
}
return false;
}
}
}
@@ -0,0 +1,181 @@
using GeometryTD.CustomEvent;
using GeometryTD.Definition;
using GameFramework.Event;
using UnityGameFramework.Runtime;
namespace GeometryTD.UI
{
public partial class CombatInfoFormController : UIFormControllerCommonBase<CombatInfoFormContext, CombatInfoForm>
{
private CombatInfoFormUseCase _useCase;
protected override UIFormType UIFormTypeId => UIFormType.CombatInfoForm;
protected override void RefreshUI(CombatInfoForm form, CombatInfoFormContext context)
{
form.RefreshUI(context);
}
protected override void SubscribeCustomEvents()
{
GameEntry.Event.Subscribe(CombatPauseEventArgs.EventId, OnCombatPauseButtonClicked);
GameEntry.Event.Subscribe(CombatEndEventArgs.EventId, OnCombatEndButtonClicked);
GameEntry.Event.Subscribe(CombatDebugFailEventArgs.EventId, OnCombatDebugFailButtonClicked);
GameEntry.Event.Subscribe(CombatProcessEventArgs.EventId, OnCombatProcessChanged);
GameEntry.Event.Subscribe(CombatCoinChangedEventArgs.EventId, OnCombatCoinChanged);
GameEntry.Event.Subscribe(CombatBaseHpChangedEventArgs.EventId, OnCombatBaseHpChanged);
GameEntry.Event.Subscribe(CombatEnemyHpRateChangedEventArgs.EventId, OnCombatEnemyHpRateChanged);
}
protected override void UnsubscribeCustomEvents()
{
GameEntry.Event.Unsubscribe(CombatPauseEventArgs.EventId, OnCombatPauseButtonClicked);
GameEntry.Event.Unsubscribe(CombatEndEventArgs.EventId, OnCombatEndButtonClicked);
GameEntry.Event.Unsubscribe(CombatDebugFailEventArgs.EventId, OnCombatDebugFailButtonClicked);
GameEntry.Event.Unsubscribe(CombatProcessEventArgs.EventId, OnCombatProcessChanged);
GameEntry.Event.Unsubscribe(CombatCoinChangedEventArgs.EventId, OnCombatCoinChanged);
GameEntry.Event.Unsubscribe(CombatBaseHpChangedEventArgs.EventId, OnCombatBaseHpChanged);
GameEntry.Event.Unsubscribe(CombatEnemyHpRateChangedEventArgs.EventId, OnCombatEnemyHpRateChanged);
}
public int? OpenUI(CombatInfoFormRawData rawData)
{
CombatInfoFormContext context = BuildContext(rawData);
return OpenUIInternal(context);
}
public override int? OpenUI(object userData = null)
{
if (userData is CombatInfoFormContext context)
{
return OpenUIInternal(context);
}
if (userData is CombatInfoFormRawData rawDataFromUserData)
{
return OpenUI(rawDataFromUserData);
}
if (userData != null)
{
Log.Warning("CombatInfoFormController.OpenUI() userData type is invalid.");
return null;
}
if (_useCase == null)
{
Log.Error("CombatInfoFormController.OpenUI() useCase is null.");
return null;
}
CombatInfoFormRawData rawData = _useCase.CreateInitialModel();
return OpenUI(rawData);
}
public override void BindUseCase(IUIUseCase useCase)
{
if (!(useCase is CombatInfoFormUseCase combatInfoFormUseCase))
{
Log.Error("CombatInfoFormController.BindUseCase() useCase is invalid.");
return;
}
_useCase = combatInfoFormUseCase;
}
private void OnCombatPauseButtonClicked(object sender, GameEventArgs e)
{
if (!(sender is CombatInfoForm) || !(e is CombatPauseEventArgs))
{
return;
}
GameEntry.UIRouter.OpenUI(UIFormType.DialogForm, new DialogFormRawData
{
Mode = 1,
Title = "Pause",
Message = "Game paused.",
PauseGame = true,
ConfirmText = "Continue"
});
}
private void OnCombatEndButtonClicked(object sender, GameEventArgs e)
{
if (!(sender is CombatInfoForm) || !(e is CombatEndEventArgs))
{
return;
}
_useCase?.TryEndCombat();
}
private void OnCombatDebugFailButtonClicked(object sender, GameEventArgs e)
{
if (!(sender is CombatInfoForm) || !(e is CombatDebugFailEventArgs))
{
return;
}
_useCase?.TryDebugFail();
}
private void OnCombatProcessChanged(object sender, GameEventArgs e)
{
if (!(e is CombatProcessEventArgs))
{
return;
}
RefreshFromUseCase();
}
private void OnCombatCoinChanged(object sender, GameEventArgs e)
{
if (!(e is CombatCoinChangedEventArgs))
{
return;
}
RefreshFromUseCase();
}
private void OnCombatBaseHpChanged(object sender, GameEventArgs e)
{
if (!(e is CombatBaseHpChangedEventArgs))
{
return;
}
RefreshFromUseCase();
}
private void OnCombatEnemyHpRateChanged(object sender, GameEventArgs e)
{
if (!(e is CombatEnemyHpRateChangedEventArgs))
{
return;
}
RefreshFromUseCase();
}
private void RefreshFromUseCase()
{
if (_useCase == null)
{
return;
}
CombatInfoFormRawData rawData = _useCase.TryRefresh();
CombatInfoFormContext context = BuildContext(rawData);
if (context == null)
{
return;
}
SetContext(context);
RefreshCurrentUI();
}
}
}
@@ -0,0 +1,223 @@
using GameFramework.Event;
using GeometryTD.CustomEvent;
using GeometryTD.Definition;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace GeometryTD.UI
{
public partial class CombatSelectFormController : UIFormControllerCommonBase<CombatSelectFormContext, CombatSelectForm>
{
private CombatSelectFormUseCase _useCase;
protected override UIFormType UIFormTypeId => UIFormType.CombatSelectForm;
protected override void RefreshUI(CombatSelectForm form, CombatSelectFormContext context)
{
form.RefreshUI(context);
}
protected override void SubscribeCustomEvents()
{
GameEntry.Event.Subscribe(CombatSelectItemClickEventArgs.EventId, OnSelectItemClicked);
GameEntry.Event.Subscribe(CombatCoinChangedEventArgs.EventId, OnCombatCoinChanged);
}
protected override void UnsubscribeCustomEvents()
{
GameEntry.Event.Unsubscribe(CombatSelectItemClickEventArgs.EventId, OnSelectItemClicked);
GameEntry.Event.Unsubscribe(CombatCoinChangedEventArgs.EventId, OnCombatCoinChanged);
}
public int? OpenUI(CombatSelectFormRawData rawData)
{
CombatSelectFormContext context = BuildContext(rawData);
if (context == null || !context.IsVisible)
{
CloseUI();
return null;
}
return OpenUIInternal(context);
}
public override int? OpenUI(object userData = null)
{
if (userData is CombatSelectFormUserData clickUserData)
{
return OpenUI(clickUserData);
}
if (userData is CombatSelectFormContext context)
{
if (context == null || !context.IsVisible)
{
CloseUI();
return null;
}
return OpenUIInternal(context);
}
if (userData is CombatSelectFormRawData rawDataFromUserData)
{
return OpenUI(rawDataFromUserData);
}
if (userData != null)
{
Log.Warning("CombatSelectFormController.OpenUI() userData type is invalid.");
return null;
}
if (_useCase == null)
{
Log.Error("CombatSelectFormController.OpenUI() useCase is null.");
return null;
}
CombatSelectFormRawData rawData = _useCase.CreateInitialModel();
return OpenUI(rawData);
}
public int? OpenUI(CombatSelectFormUserData userData)
{
if (_useCase == null)
{
Log.Error("CombatSelectFormController.OpenUI() useCase is null.");
return null;
}
if (userData == null)
{
_useCase.Hide();
CloseUI();
return null;
}
switch (userData.ClickObjectType)
{
case CombatSelectClickObjectType.Foundation:
_useCase.SetUpgradeVisible(true);
_useCase.ShowForFoundation(userData.ScreenPosition);
break;
case CombatSelectClickObjectType.Tower:
_useCase.SetUpgradePrice(userData.UpgradeCost);
_useCase.SetUpgradeVisible(!userData.IsTowerAtMaxLevel);
_useCase.SetDestroyGain(userData.DestroyGain);
_useCase.ShowForTower(userData.ScreenPosition);
break;
default:
_useCase.Hide();
CloseUI();
return null;
}
return OpenUI(_useCase.TryRefresh());
}
public override void BindUseCase(IUIUseCase useCase)
{
if (!(useCase is CombatSelectFormUseCase combatSelectFormUseCase))
{
Log.Error("CombatSelectFormController.BindUseCase() useCase is invalid.");
return;
}
_useCase = combatSelectFormUseCase;
}
public int? ShowForFoundation(Vector2 screenPosition)
{
if (_useCase == null)
{
Log.Error("CombatSelectFormController.ShowForFoundation() useCase is null.");
return null;
}
_useCase.SetUpgradeVisible(true);
_useCase.ShowForFoundation(screenPosition);
return OpenUI(_useCase.TryRefresh());
}
public int? ShowForTower(Vector2 screenPosition, int upgradeCost, int destroyGain)
{
if (_useCase == null)
{
Log.Error("CombatSelectFormController.ShowForTower() useCase is null.");
return null;
}
_useCase.SetUpgradePrice(upgradeCost);
_useCase.SetUpgradeVisible(true);
_useCase.SetDestroyGain(destroyGain);
_useCase.ShowForTower(screenPosition);
return OpenUI(_useCase.TryRefresh());
}
public void HideSelection()
{
_useCase?.Hide();
CloseUI();
}
public void RefreshFromUseCase()
{
if (_useCase == null)
{
return;
}
CombatSelectFormRawData rawData = _useCase.TryRefresh();
CombatSelectFormContext context = BuildContext(rawData);
if (context == null || !context.IsVisible)
{
CloseUI();
return;
}
SetContext(context);
RefreshCurrentUI();
}
private void OnSelectItemClicked(object sender, GameEventArgs e)
{
if (!(e is CombatSelectItemClickEventArgs args))
{
return;
}
if (_useCase == null)
{
return;
}
if (Form != null && sender is Component component)
{
CombatSelectForm ownerForm = component.GetComponentInParent<CombatSelectForm>();
if (ownerForm != Form)
{
return;
}
}
_useCase.TryExecuteAction(args.ActionType, args.ActionIndex);
RefreshFromUseCase();
}
private void OnCombatCoinChanged(object sender, GameEventArgs e)
{
if (!(e is CombatCoinChangedEventArgs))
{
return;
}
if (Form == null)
{
return;
}
RefreshFromUseCase();
}
}
}
@@ -0,0 +1,12 @@
using GeometryTD.Definition;
namespace GeometryTD.UI
{
public class CombatFinishFormRawData
{
public int DefeatedEnemyCount;
public int GainedGold;
public BackpackInventoryData RewardInventory;
public bool CanReturn;
}
}
@@ -0,0 +1,18 @@
using GeometryTD.Definition;
namespace GeometryTD.UI
{
public class CombatInfoFormRawData
{
public LevelThemeType LevelThemeType;
public int LevelId;
public int CurrentPhaseIndex;
public int TotalPhaseCount;
public int Coin;
public int BaseHp;
public int BaseHpMax;
public int EnemyHpRateMultiplier;
public bool CanPause;
public bool CanEnd;
}
}
@@ -0,0 +1,9 @@
namespace GeometryTD.UI
{
public enum CombatSelectClickObjectType : byte
{
None = 0,
Foundation = 1,
Tower = 2
}
}
@@ -0,0 +1,9 @@
namespace GeometryTD.UI
{
public enum CombatSelectDisplayMode : byte
{
Hidden = 0,
Build = 1,
Func = 2
}
}
@@ -0,0 +1,14 @@
using UnityEngine;
namespace GeometryTD.UI
{
public class CombatSelectFormRawData
{
public bool IsVisible;
public Vector2 ScreenPosition;
public CombatSelectDisplayMode DisplayMode;
public TowerSelectItemRawData[] BuildItems;
public TowerSelectItemRawData UpgradeItem;
public TowerSelectItemRawData DestroyItem;
}
}
@@ -0,0 +1,16 @@
using UnityEngine;
namespace GeometryTD.UI
{
public class CombatSelectFormUserData
{
public CombatSelectClickObjectType ClickObjectType;
public Vector2 ScreenPosition;
public Vector3 WorldPosition;
public Vector3Int CellPosition;
public int TowerEntityId;
public bool IsTowerAtMaxLevel;
public int UpgradeCost;
public int DestroyGain;
}
}
@@ -0,0 +1,19 @@
using UnityEngine;
namespace GeometryTD.UI
{
public class TowerSelectItemRawData
{
public Sprite Icon;
public int Price;
public bool IsGain;
public bool IsVisible;
public bool IsInteractable;
public CombatSelectActionType ActionType;
public int ActionIndex;
public bool UseTowerLayers;
public Color BaseColor = Color.white;
public Color BearingColor = Color.white;
public Color MuzzleColor = Color.white;
}
}
@@ -0,0 +1,62 @@
using GeometryTD.CustomComponent;
using GeometryTD.CustomUtility;
using GeometryTD.Definition;
using UnityEngine;
namespace GeometryTD.UI
{
public class CombatFinishFormUseCase : IUIUseCase
{
private ICombatSchedulerPort _combatSchedulerPort;
private CombatSettlementContext _settlementContext;
private bool _isSummaryPrepared;
public CombatFinishFormRawData CreateInitialModel()
{
return BuildModel();
}
public CombatFinishFormRawData TryRefresh()
{
return BuildModel();
}
internal void SetSummary(CombatSettlementContext settlementContext)
{
_settlementContext = settlementContext;
_isSummaryPrepared = true;
}
internal CombatFinishFormUseCase(ICombatSchedulerPort combatSchedulerPort)
{
_combatSchedulerPort = combatSchedulerPort;
}
public bool TryReturnToMenu()
{
return _combatSchedulerPort != null && _combatSchedulerPort.OnCombatFinishReturnRequested();
}
private CombatFinishFormRawData BuildModel()
{
if (!_isSummaryPrepared)
{
_settlementContext = null;
}
BackpackInventoryData rewardInventory = _settlementContext?.Summary.RewardInventory;
if (rewardInventory == null)
{
rewardInventory = InventorySeedUtility.CreateSampleInventory();
}
return new CombatFinishFormRawData
{
DefeatedEnemyCount = Mathf.Max(0, _settlementContext?.Summary.DefeatedEnemyCount ?? 0),
GainedGold = Mathf.Max(0, _settlementContext?.Summary.GainedGold ?? 0),
RewardInventory = rewardInventory,
CanReturn = true
};
}
}
}
@@ -0,0 +1,96 @@
using System;
using GeometryTD.Definition;
using UnityEngine;
namespace GeometryTD.UI
{
public class CombatInfoFormUseCase : IUIUseCase
{
private Func<CombatInfoFormRawData> _modelProvider;
private Func<bool> _tryEndCombat;
private Func<bool> _tryDebugFail;
public CombatInfoFormRawData CreateInitialModel()
{
return BuildModel();
}
public CombatInfoFormRawData TryRefresh()
{
return BuildModel();
}
public void Configure(Func<CombatInfoFormRawData> modelProvider, Func<bool> tryEndCombat, Func<bool> tryDebugFail)
{
_modelProvider = modelProvider;
_tryEndCombat = tryEndCombat;
_tryDebugFail = tryDebugFail;
}
public bool TryEndCombat()
{
if (_tryEndCombat == null)
{
return false;
}
return _tryEndCombat.Invoke();
}
public bool TryDebugFail()
{
if (_tryDebugFail == null)
{
return false;
}
return _tryDebugFail.Invoke();
}
private CombatInfoFormRawData BuildModel()
{
return _modelProvider != null ? _modelProvider.Invoke() : null;
}
public static CombatInfoFormRawData BuildRawData(
LevelThemeType themeType,
int levelId,
int currentPhaseIndex,
int totalPhaseCount,
int coin,
int baseHp,
int baseHpMax,
bool canEnd)
{
return new CombatInfoFormRawData
{
LevelThemeType = themeType,
LevelId = levelId,
CurrentPhaseIndex = Mathf.Max(0, currentPhaseIndex),
TotalPhaseCount = Mathf.Max(0, totalPhaseCount),
Coin = Mathf.Max(0, coin),
BaseHp = Mathf.Max(0, baseHp),
BaseHpMax = Mathf.Max(0, baseHpMax),
EnemyHpRateMultiplier = ResolveEnemyHpRateMultiplier(currentPhaseIndex, totalPhaseCount),
CanPause = true,
CanEnd = canEnd
};
}
private static int ResolveEnemyHpRateMultiplier(int currentPhaseIndex, int totalPhaseCount)
{
if (currentPhaseIndex <= 0 || totalPhaseCount <= 0)
{
return 1;
}
int completedLoopCount = Mathf.Max(0, (currentPhaseIndex - 1) / totalPhaseCount);
if (completedLoopCount >= 30)
{
return int.MaxValue;
}
return 1 << completedLoopCount;
}
}
}
@@ -0,0 +1,337 @@
using System;
using UnityEngine;
namespace GeometryTD.UI
{
public class CombatSelectFormUseCase : IUIUseCase
{
private const int BuildOptionCount = 4;
private readonly Option[] _buildOptions = new Option[BuildOptionCount];
private readonly Option _upgradeOption = new Option(CombatSelectActionType.UpgradeTower, 0, true);
private readonly Option _destroyOption = new Option(CombatSelectActionType.DestroyTower, 0, false);
private Func<int> _coinProvider;
private bool _isVisible;
private CombatSelectDisplayMode _displayMode = CombatSelectDisplayMode.Hidden;
private Vector2 _screenPosition;
public CombatSelectFormUseCase()
{
_coinProvider = DefaultCoinProvider;
for (int i = 0; i < BuildOptionCount; i++)
{
_buildOptions[i] = new Option(CombatSelectActionType.BuildTower, i, true);
}
}
public CombatSelectFormRawData CreateInitialModel()
{
return BuildModel();
}
public CombatSelectFormRawData TryRefresh()
{
return BuildModel();
}
public void SetCoinProvider(Func<int> coinProvider)
{
_coinProvider = coinProvider ?? DefaultCoinProvider;
}
public void SetBuildAction(int buildIndex, Func<bool> buildAction, int cost, Sprite icon = null)
{
SetBuildAction(buildIndex, buildAction, cost, icon, false, Color.white, Color.white, Color.white);
}
public void SetBuildAction(
int buildIndex,
Func<bool> buildAction,
int cost,
Sprite icon,
bool useTowerLayers,
Color baseColor,
Color bearingColor,
Color muzzleColor)
{
if (buildIndex < 0 || buildIndex >= _buildOptions.Length)
{
return;
}
Option option = _buildOptions[buildIndex];
option.Action = buildAction;
option.Price = Mathf.Max(0, cost);
option.Icon = icon;
option.IsGain = false;
option.IsVisible = true;
option.UseTowerLayers = useTowerLayers;
option.BaseColor = baseColor;
option.BearingColor = bearingColor;
option.MuzzleColor = muzzleColor;
}
public void SetBuildAction(int buildIndex, Action buildAction, int cost, Sprite icon = null)
{
if (buildAction == null)
{
SetBuildAction(buildIndex, (Func<bool>)null, cost, icon);
return;
}
SetBuildAction(
buildIndex,
() =>
{
buildAction.Invoke();
return true;
},
cost,
icon);
}
public void SetBuildVisible(int buildIndex, bool visible)
{
if (buildIndex < 0 || buildIndex >= _buildOptions.Length)
{
return;
}
_buildOptions[buildIndex].IsVisible = visible;
}
public void SetUpgradeAction(Func<bool> upgradeAction, int cost, Sprite icon = null)
{
_upgradeOption.Action = upgradeAction;
_upgradeOption.Price = Mathf.Max(0, cost);
_upgradeOption.Icon = icon;
_upgradeOption.IsGain = false;
_upgradeOption.IsVisible = true;
_upgradeOption.UseTowerLayers = false;
}
public void SetUpgradeAction(Action upgradeAction, int cost, Sprite icon = null)
{
if (upgradeAction == null)
{
SetUpgradeAction((Func<bool>)null, cost, icon);
return;
}
SetUpgradeAction(
() =>
{
upgradeAction.Invoke();
return true;
},
cost,
icon);
}
public void SetDestroyAction(Func<bool> destroyAction, int gain, Sprite icon = null)
{
_destroyOption.Action = destroyAction;
_destroyOption.Price = Mathf.Max(0, gain);
_destroyOption.Icon = icon;
_destroyOption.IsGain = true;
_destroyOption.IsVisible = true;
_destroyOption.UseTowerLayers = false;
}
public void SetDestroyAction(Action destroyAction, int gain, Sprite icon = null)
{
if (destroyAction == null)
{
SetDestroyAction((Func<bool>)null, gain, icon);
return;
}
SetDestroyAction(
() =>
{
destroyAction.Invoke();
return true;
},
gain,
icon);
}
public void SetUpgradePrice(int cost)
{
_upgradeOption.Price = Mathf.Max(0, cost);
}
public void SetUpgradeVisible(bool visible)
{
_upgradeOption.IsVisible = visible;
}
public void SetDestroyGain(int gain)
{
_destroyOption.Price = Mathf.Max(0, gain);
_destroyOption.IsGain = true;
}
public void ShowForFoundation(Vector2 screenPosition)
{
_screenPosition = screenPosition;
_displayMode = CombatSelectDisplayMode.Build;
_isVisible = true;
}
public void ShowForTower(Vector2 screenPosition)
{
_screenPosition = screenPosition;
_displayMode = CombatSelectDisplayMode.Func;
_isVisible = true;
}
public void Hide()
{
_isVisible = false;
_displayMode = CombatSelectDisplayMode.Hidden;
}
public bool TryExecuteAction(CombatSelectActionType actionType, int actionIndex)
{
if (!_isVisible)
{
return false;
}
Option option = GetOption(actionType, actionIndex);
if (option == null || !option.IsVisible)
{
return false;
}
int currentCoin = Mathf.Max(0, _coinProvider != null ? _coinProvider.Invoke() : 0);
if (!CanExecute(option, currentCoin))
{
return false;
}
if (option.Action == null)
{
return false;
}
bool result = option.Action.Invoke();
if (result)
{
Hide();
}
return result;
}
private CombatSelectFormRawData BuildModel()
{
int currentCoin = Mathf.Max(0, _coinProvider != null ? _coinProvider.Invoke() : 0);
TowerSelectItemRawData[] buildItems = new TowerSelectItemRawData[_buildOptions.Length];
for (int i = 0; i < _buildOptions.Length; i++)
{
buildItems[i] = BuildOptionRawData(_buildOptions[i], currentCoin);
}
return new CombatSelectFormRawData
{
IsVisible = _isVisible,
ScreenPosition = _screenPosition,
DisplayMode = _displayMode,
BuildItems = buildItems,
UpgradeItem = BuildOptionRawData(_upgradeOption, currentCoin),
DestroyItem = BuildOptionRawData(_destroyOption, currentCoin)
};
}
private Option GetOption(CombatSelectActionType actionType, int actionIndex)
{
switch (actionType)
{
case CombatSelectActionType.BuildTower:
if (actionIndex < 0 || actionIndex >= _buildOptions.Length)
{
return null;
}
return _buildOptions[actionIndex];
case CombatSelectActionType.UpgradeTower:
return _upgradeOption;
case CombatSelectActionType.DestroyTower:
return _destroyOption;
default:
return null;
}
}
private static int DefaultCoinProvider()
{
return 0;
}
private static bool CanExecute(Option option, int currentCoin)
{
if (option == null)
{
return false;
}
if (option.RequiresAffordable && currentCoin < option.Price)
{
return false;
}
return true;
}
private static TowerSelectItemRawData BuildOptionRawData(Option option, int currentCoin)
{
if (option == null)
{
return null;
}
return new TowerSelectItemRawData
{
Icon = option.Icon,
Price = option.Price,
IsGain = option.IsGain,
IsVisible = option.IsVisible,
IsInteractable = option.Action != null && CanExecute(option, currentCoin),
ActionType = option.ActionType,
ActionIndex = option.ActionIndex,
UseTowerLayers = option.UseTowerLayers,
BaseColor = option.BaseColor,
BearingColor = option.BearingColor,
MuzzleColor = option.MuzzleColor
};
}
private sealed class Option
{
public readonly CombatSelectActionType ActionType;
public readonly int ActionIndex;
public readonly bool RequiresAffordable;
public int Price;
public bool IsGain;
public bool IsVisible = true;
public Sprite Icon;
public Func<bool> Action;
public bool UseTowerLayers;
public Color BaseColor = Color.white;
public Color BearingColor = Color.white;
public Color MuzzleColor = Color.white;
public Option(CombatSelectActionType actionType, int actionIndex, bool requiresAffordable)
{
ActionType = actionType;
ActionIndex = actionIndex;
RequiresAffordable = requiresAffordable;
}
}
}
}
+173
View File
@@ -0,0 +1,173 @@
using System.Collections.Generic;
using GameFramework.ObjectPool;
using GeometryTD.CustomEvent;
using GeometryTD.PoolObjectBase;
using TMPro;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace GeometryTD.UI
{
public class CombatFinishForm : UGuiForm
{
[SerializeField] private TMP_Text _enemyKilledText;
[SerializeField] private TMP_Text _goldGainedText;
[SerializeField] private Transform _itemGainedParent;
[SerializeField] private RepoItem _itemTemplate;
[SerializeField] private int _instancePoolCapacity = 16;
private readonly List<RepoItem> _activeItems = new List<RepoItem>();
private IObjectPool<RepoItemObject> _itemPool;
private string _poolName;
private CombatFinishFormContext _context;
public void RefreshUI(CombatFinishFormContext context)
{
_context = context;
if (_enemyKilledText != null)
{
_enemyKilledText.text = context?.EnemyKilledText ?? "0";
}
if (_goldGainedText != null)
{
_goldGainedText.text = context?.GoldGainedText ?? "0";
}
RefreshRewardItems(context?.RewardItems);
}
public void OnReturnButtonClick()
{
if (_context != null && !_context.CanReturn)
{
return;
}
GameEntry.Event.Fire(this, CombatFinishReturnEventArgs.Create());
}
protected override void OnOpen(object userData)
{
base.OnOpen(userData);
if (userData is CombatFinishFormContext context)
{
RefreshUI(context);
return;
}
Log.Warning("CombatFinishForm requires CombatFinishFormContext as userData.");
}
protected override void OnClose(bool isShutdown, object userData)
{
_context = null;
if (_enemyKilledText != null)
{
_enemyKilledText.text = string.Empty;
}
if (_goldGainedText != null)
{
_goldGainedText.text = string.Empty;
}
ClearRewardItems();
base.OnClose(isShutdown, userData);
}
private void OnDestroy()
{
ClearRewardItems();
_itemPool?.ReleaseAllUnused();
}
private void EnsurePool()
{
if (_itemPool != null)
{
return;
}
_poolName = $"CombatFinishRepoItemPool_{GetInstanceID()}";
_itemPool = GameEntry.ObjectPool.CreateSingleSpawnObjectPool<RepoItemObject>(_poolName, _instancePoolCapacity);
}
private void RefreshRewardItems(RepoItemContext[] rewardItems)
{
ClearRewardItems();
if (rewardItems == null || rewardItems.Length <= 0)
{
return;
}
EnsurePool();
for (int i = 0; i < rewardItems.Length; i++)
{
RepoItem item = CreateOrSpawnItem();
if (item == null)
{
continue;
}
item.gameObject.SetActive(true);
item.OnInit(rewardItems[i]);
_activeItems.Add(item);
}
}
private void ClearRewardItems()
{
for (int i = _activeItems.Count - 1; i >= 0; i--)
{
RepoItem item = _activeItems[i];
if (item == null)
{
continue;
}
item.OnReset();
item.gameObject.SetActive(false);
_itemPool?.Unspawn(item);
}
_activeItems.Clear();
}
private RepoItem CreateOrSpawnItem()
{
if (_itemGainedParent == null)
{
Log.Warning("CombatFinishForm requires an item gained parent.");
return null;
}
if (_itemPool == null)
{
return null;
}
RepoItemObject itemObject = _itemPool.Spawn();
RepoItem item = itemObject != null ? (RepoItem)itemObject.Target : null;
if (item == null)
{
if (_itemTemplate == null)
{
Log.Warning("CombatFinishForm requires an item template.");
return null;
}
item = Instantiate(_itemTemplate);
_itemPool.Register(RepoItemObject.Create(item), true);
}
item.transform.SetParent(_itemGainedParent, false);
return item;
}
}
}
+136
View File
@@ -0,0 +1,136 @@
using GeometryTD.CustomEvent;
using TMPro;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace GeometryTD.UI
{
public class CombatInfoForm : UGuiForm
{
[SerializeField] private TMP_Text _levelMetaText;
[SerializeField] private TMP_Text _levelPhaseText;
[SerializeField] private TMP_Text _coinText;
[SerializeField] private TMP_Text _baseHpText;
[SerializeField] private TMP_Text _enemyHpRateText;
[SerializeField] private CommonButton _pauseButton;
[SerializeField] private CommonButton _endButton;
private CombatInfoFormContext _context;
public void RefreshUI(CombatInfoFormContext context)
{
_context = context;
if (_levelMetaText != null)
{
_levelMetaText.text = context?.LevelMetaText ?? string.Empty;
}
if (_levelPhaseText != null)
{
_levelPhaseText.text = context?.LevelPhaseText ?? string.Empty;
}
if (_coinText != null)
{
_coinText.text = context?.CoinText ?? "0";
}
if (_baseHpText != null)
{
_baseHpText.text = context?.BaseHpText ?? string.Empty;
}
if (_enemyHpRateText != null)
{
_enemyHpRateText.text = context?.EnemyHpRateText ?? string.Empty;
}
if (_pauseButton != null)
{
_pauseButton.Interactive = context?.CanPause ?? false;
}
if (_endButton != null)
{
_endButton.gameObject.SetActive(context?.CanEnd ?? false);
}
}
public void OnPauseButtonClick()
{
if (_context != null && !_context.CanPause)
{
return;
}
GameEntry.Event.Fire(this, CombatPauseEventArgs.Create());
}
public void OnEndButtonClick()
{
if (_context != null && !_context.CanEnd)
{
return;
}
GameEntry.Event.Fire(this, CombatEndEventArgs.Create());
}
public void OnFailButtonClick()
{
GameEntry.Event.Fire(this, CombatDebugFailEventArgs.Create());
}
protected override void OnOpen(object userData)
{
base.OnOpen(userData);
if (userData is CombatInfoFormContext context)
{
RefreshUI(context);
return;
}
Log.Warning("CombatInfoForm requires CombatInfoFormContext as userData.");
}
protected override void OnClose(bool isShutdown, object userData)
{
_context = null;
if (_levelMetaText != null)
{
_levelMetaText.text = string.Empty;
}
if (_levelPhaseText != null)
{
_levelPhaseText.text = string.Empty;
}
if (_coinText != null)
{
_coinText.text = string.Empty;
}
if (_baseHpText != null)
{
_baseHpText.text = string.Empty;
}
if (_enemyHpRateText != null)
{
_enemyHpRateText.text = string.Empty;
}
base.OnClose(isShutdown, userData);
}
}
}
@@ -0,0 +1,60 @@
using UnityEngine;
namespace GeometryTD.UI
{
public class CombatSelectBuildArea : MonoBehaviour
{
[SerializeField] private TowerSelectItem[] _towerBuildItems;
public void OnInit(TowerSelectItemContext[] itemContexts)
{
if (_towerBuildItems == null)
{
return;
}
for (int i = 0; i < _towerBuildItems.Length; i++)
{
TowerSelectItem item = _towerBuildItems[i];
if (item == null)
{
continue;
}
TowerSelectItemContext itemContext =
itemContexts != null && i < itemContexts.Length ? itemContexts[i] : null;
bool isVisible = itemContext != null && itemContext.IsVisible;
item.gameObject.SetActive(isVisible);
if (isVisible)
{
item.OnInit(itemContext);
}
else
{
item.OnReset();
}
}
}
public void OnReset()
{
if (_towerBuildItems == null)
{
return;
}
for (int i = 0; i < _towerBuildItems.Length; i++)
{
TowerSelectItem item = _towerBuildItems[i];
if (item == null)
{
continue;
}
item.OnReset();
item.gameObject.SetActive(false);
}
}
}
}
+141
View File
@@ -0,0 +1,141 @@
using UnityEngine;
using UnityGameFramework.Runtime;
namespace GeometryTD.UI
{
public class CombatSelectForm : UGuiForm
{
[SerializeField] private RectTransform _content;
[SerializeField] private CombatSelectBuildArea _buildArea;
[SerializeField] private CombatSelectFuncArea _funcArea;
private CombatSelectFormContext _context;
public void RefreshUI(CombatSelectFormContext context)
{
_context = context;
EnsureContentReference();
if (_content == null)
{
Log.Warning("CombatSelectForm content is missing.");
return;
}
bool isVisible = context != null && context.IsVisible;
_content.gameObject.SetActive(isVisible);
if (!isVisible)
{
_buildArea?.OnReset();
_funcArea?.OnReset();
return;
}
_content.anchoredPosition = ResolveContentAnchoredPosition(context.ScreenPosition);
if (_buildArea != null)
{
bool showBuildArea = context.ShowBuildArea;
_buildArea.gameObject.SetActive(showBuildArea);
if (showBuildArea)
{
_buildArea.OnInit(context.BuildItems);
}
else
{
_buildArea.OnReset();
}
}
if (_funcArea != null)
{
bool showFuncArea = context.ShowFuncArea;
_funcArea.gameObject.SetActive(showFuncArea);
if (showFuncArea)
{
_funcArea.OnInit(context.UpgradeItem, context.DestroyItem);
}
else
{
_funcArea.OnReset();
}
}
}
protected override void OnOpen(object userData)
{
base.OnOpen(userData);
if (userData is CombatSelectFormContext context)
{
RefreshUI(context);
return;
}
Log.Warning("CombatSelectForm requires CombatSelectFormContext as userData.");
RefreshUI(null);
}
protected override void OnClose(bool isShutdown, object userData)
{
_context = null;
_buildArea?.OnReset();
_funcArea?.OnReset();
if (_content != null)
{
_content.gameObject.SetActive(false);
}
base.OnClose(isShutdown, userData);
}
private void Awake()
{
EnsureContentReference();
}
private void EnsureContentReference()
{
if (_content != null)
{
return;
}
Transform contentTransform = transform.Find("Content");
_content = contentTransform as RectTransform;
}
private Vector2 ResolveContentAnchoredPosition(Vector2 screenPosition)
{
if (_content == null)
{
return Vector2.zero;
}
RectTransform parentRect = _content.parent as RectTransform;
if (parentRect == null)
{
return screenPosition;
}
Canvas canvas = _content.GetComponentInParent<Canvas>();
Canvas rootCanvas = canvas != null ? canvas.rootCanvas : null;
Camera uiCamera = null;
if (rootCanvas != null && rootCanvas.renderMode != RenderMode.ScreenSpaceOverlay)
{
uiCamera = rootCanvas.worldCamera != null ? rootCanvas.worldCamera : Camera.main;
}
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(parentRect, screenPosition, uiCamera,
out Vector2 localPoint))
{
return Vector2.zero;
}
return localPoint;
}
}
}
@@ -0,0 +1,53 @@
using UnityEngine;
namespace GeometryTD.UI
{
public class CombatSelectFuncArea : MonoBehaviour
{
[SerializeField] private TowerSelectItem _upgradeItem;
[SerializeField] private TowerSelectItem _destroyItem;
public void OnInit(TowerSelectItemContext upgradeItemContext, TowerSelectItemContext destroyItemContext)
{
RefreshItem(_upgradeItem, upgradeItemContext);
RefreshItem(_destroyItem, destroyItemContext);
}
public void OnReset()
{
ResetItem(_upgradeItem);
ResetItem(_destroyItem);
}
private static void RefreshItem(TowerSelectItem item, TowerSelectItemContext context)
{
if (item == null)
{
return;
}
bool isVisible = context != null && context.IsVisible;
item.gameObject.SetActive(isVisible);
if (isVisible)
{
item.OnInit(context);
}
else
{
item.OnReset();
}
}
private static void ResetItem(TowerSelectItem item)
{
if (item == null)
{
return;
}
item.OnReset();
item.gameObject.SetActive(false);
}
}
}
+118
View File
@@ -0,0 +1,118 @@
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using GeometryTD.CustomEvent;
namespace GeometryTD.UI
{
public class TowerSelectItem : MonoBehaviour
{
[SerializeField] private Image _icon;
[SerializeField] private TMP_Text _price;
[SerializeField] private CommonButton _button;
[SerializeField] private TowerIconArea _towerIconArea;
private TowerSelectItemContext _context;
private Sprite _defaultIcon;
private bool _hasCachedDefaultIcon;
public void OnInit(TowerSelectItemContext context)
{
CacheDefaultIcon();
_context = context;
if (context.ActionType == CombatSelectActionType.BuildTower)
{
_icon?.gameObject.SetActive(false);
_towerIconArea?.OnInit(_context.TowerIconAreaContext);
if (_price != null)
{
_price.text = context.PriceText ?? string.Empty;
}
if (_button != null)
{
_button.Interactive = context.IsInteractable;
}
}
else if (context.ActionType is CombatSelectActionType.UpgradeTower or CombatSelectActionType.DestroyTower)
{
if (_icon != null)
{
_icon.sprite = _context.Icon ?? _defaultIcon;
_icon.gameObject.SetActive(true);
}
_towerIconArea?.OnReset();
if (_price != null)
{
_price.text = context.PriceText ?? string.Empty;
}
if (_button != null)
{
_button.Interactive = context.IsInteractable;
}
}
else
{
_icon?.gameObject.SetActive(false);
_towerIconArea?.OnReset();
}
}
public void OnReset()
{
_context = null;
CacheDefaultIcon();
if (_icon != null)
{
_icon.sprite = _defaultIcon;
_icon.enabled = _icon.sprite != null;
}
_towerIconArea.OnReset();
if (_price != null)
{
_price.text = string.Empty;
}
if (_button != null)
{
_button.Interactive = false;
}
}
public void OnClick()
{
if (_context == null || !_context.IsInteractable)
{
return;
}
GameEntry.Event.Fire(
this,
CombatSelectItemClickEventArgs.Create(_context.ActionType, _context.ActionIndex));
}
private void CacheDefaultIcon()
{
if (_hasCachedDefaultIcon || _icon == null)
{
return;
}
_defaultIcon = _icon.sprite;
_hasCachedDefaultIcon = true;
}
}
}