init
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
using GameFramework;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class DialogFormContext : UIContext
|
||||
{
|
||||
public int Mode;
|
||||
public string Title;
|
||||
public string Message;
|
||||
public bool PauseGame;
|
||||
public string ConfirmText;
|
||||
public GameFrameworkAction<object> OnClickConfirm;
|
||||
public string CancelText;
|
||||
public GameFrameworkAction<object> OnClickCancel;
|
||||
public string OtherText;
|
||||
public GameFrameworkAction<object> OnClickOther;
|
||||
public object UserData;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using GeometryTD.Definition;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
[Serializable]
|
||||
public class IconAreaContext : UIContext
|
||||
{
|
||||
public RarityType Rarity;
|
||||
public TowerCompSlotType ComponentSlotType;
|
||||
public Color Color = Color.white;
|
||||
public Sprite Icon;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class ItemDescFormContext : UIContext
|
||||
{
|
||||
public string Title;
|
||||
public string TypeText;
|
||||
public string Description;
|
||||
public int Price;
|
||||
public Vector2 ScreenPosition;
|
||||
public TagItemContext[] Tags;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class RewardItemContext : UIContext
|
||||
{
|
||||
public int Index;
|
||||
public IconAreaContext IconArea;
|
||||
public string Title;
|
||||
public string TypeText;
|
||||
public string Description;
|
||||
public TagItemContext[] Tags;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class RewardSelectFormContext : UIContext
|
||||
{
|
||||
public string TipText;
|
||||
public string RefreshButtonText;
|
||||
public bool CanRefresh;
|
||||
public bool CanGiveUp;
|
||||
public RewardItemContext[] RewardItems;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class TagItemContext : UIContext
|
||||
{
|
||||
public string TagName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using GeometryTD.Definition;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
[Serializable]
|
||||
public class TowerIconAreaContext : IconAreaContext
|
||||
{
|
||||
public Color MuzzleColor = Color.white;
|
||||
public Color BearingColor = Color.white;
|
||||
public Color BaseColor = Color.white;
|
||||
|
||||
public TowerIconAreaContext()
|
||||
{
|
||||
ComponentSlotType = TowerCompSlotType.None;
|
||||
Color = Color.white;
|
||||
Icon = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
using GeometryTD.Definition;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class DialogFormController : UIFormControllerCommonBase<DialogFormContext, DialogForm>
|
||||
{
|
||||
protected override UIFormType UIFormTypeId => UIFormType.DialogForm;
|
||||
|
||||
protected override void RefreshUI(DialogForm form, DialogFormContext context)
|
||||
{
|
||||
form.RefreshUI(context);
|
||||
}
|
||||
|
||||
protected override void CloseLoadedFormDirect(DialogForm form)
|
||||
{
|
||||
GameEntry.UI.CloseUIForm(form);
|
||||
}
|
||||
|
||||
private static DialogFormContext BuildContext(DialogFormRawData rawData)
|
||||
{
|
||||
if (rawData == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new DialogFormContext
|
||||
{
|
||||
Mode = rawData.Mode,
|
||||
Title = rawData.Title,
|
||||
Message = rawData.Message,
|
||||
PauseGame = rawData.PauseGame,
|
||||
ConfirmText = rawData.ConfirmText,
|
||||
OnClickConfirm = rawData.OnClickConfirm,
|
||||
CancelText = rawData.CancelText,
|
||||
OnClickCancel = rawData.OnClickCancel,
|
||||
OtherText = rawData.OtherText,
|
||||
OnClickOther = rawData.OnClickOther,
|
||||
UserData = rawData.UserData
|
||||
};
|
||||
}
|
||||
|
||||
public int? OpenUI(DialogFormRawData rawData)
|
||||
{
|
||||
DialogFormContext context = BuildContext(rawData);
|
||||
return OpenUIInternal(context);
|
||||
}
|
||||
|
||||
public override int? OpenUI(object userData = null)
|
||||
{
|
||||
if (userData is DialogFormContext context)
|
||||
{
|
||||
return OpenUIInternal(context);
|
||||
}
|
||||
|
||||
if (userData is DialogFormRawData rawData)
|
||||
{
|
||||
return OpenUI(rawData);
|
||||
}
|
||||
|
||||
if (userData != null)
|
||||
{
|
||||
Log.Warning("DialogFormController.OpenUI() userData type is invalid.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return OpenUIInternal(Context);
|
||||
}
|
||||
|
||||
public override void BindUseCase(IUIUseCase useCase)
|
||||
{
|
||||
if (useCase != null)
|
||||
{
|
||||
Log.Warning("DialogFormController does not use a use case.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
using GameFramework.Event;
|
||||
using GeometryTD.CustomUtility;
|
||||
using GeometryTD.Definition;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class ItemDescFormController : UIFormControllerCommonBase<
|
||||
ItemDescFormContext, ItemDescForm>
|
||||
{
|
||||
protected override UIFormType UIFormTypeId => UIFormType.ItemDescForm;
|
||||
|
||||
|
||||
protected override void RefreshUI(ItemDescForm form, ItemDescFormContext context)
|
||||
{
|
||||
form.RefreshUI(context);
|
||||
}
|
||||
|
||||
protected override void CloseLoadedFormDirect(ItemDescForm form)
|
||||
{
|
||||
GameEntry.UI.CloseUIForm(form);
|
||||
}
|
||||
|
||||
private static ItemDescFormContext BuildContext(ItemDescFormRawData rawData)
|
||||
{
|
||||
if (rawData == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ItemDescFormContext
|
||||
{
|
||||
Title = rawData.Title,
|
||||
TypeText = rawData.TypeText,
|
||||
Description = BuildDescription(rawData),
|
||||
Price = rawData.Price,
|
||||
ScreenPosition = rawData.ScreenPosition,
|
||||
Tags = BuildTags(rawData)
|
||||
};
|
||||
}
|
||||
|
||||
private static string BuildDescription(ItemDescFormRawData rawData)
|
||||
{
|
||||
string baseDescription = rawData?.Description ?? string.Empty;
|
||||
string tagDescription = rawData?.TagRuntimes != null && rawData.TagRuntimes.Length > 0
|
||||
? TagDisplayUtility.BuildTagDescriptionText(rawData.TagRuntimes)
|
||||
: TagDisplayUtility.BuildTagDescriptionText(rawData?.Tags);
|
||||
if (string.IsNullOrWhiteSpace(tagDescription))
|
||||
{
|
||||
return baseDescription;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(baseDescription))
|
||||
{
|
||||
return tagDescription;
|
||||
}
|
||||
|
||||
return $"{baseDescription}\n{tagDescription}";
|
||||
}
|
||||
|
||||
private static TagItemContext[] BuildTags(ItemDescFormRawData rawData)
|
||||
{
|
||||
string[] tagTexts = rawData?.TagRuntimes != null && rawData.TagRuntimes.Length > 0
|
||||
? TagDisplayUtility.BuildTagTexts(rawData.TagRuntimes)
|
||||
: rawData?.Tags != null
|
||||
? TagDisplayUtility.BuildTagTexts(rawData.Tags)
|
||||
: null;
|
||||
|
||||
if (tagTexts == null || tagTexts.Length <= 0)
|
||||
{
|
||||
return System.Array.Empty<TagItemContext>();
|
||||
}
|
||||
|
||||
TagItemContext[] contexts = new TagItemContext[tagTexts.Length];
|
||||
for (int i = 0; i < tagTexts.Length; i++)
|
||||
{
|
||||
contexts[i] = new TagItemContext
|
||||
{
|
||||
TagName = tagTexts[i] ?? string.Empty
|
||||
};
|
||||
}
|
||||
|
||||
return contexts;
|
||||
}
|
||||
|
||||
public int? OpenUI(ItemDescFormRawData rawData)
|
||||
{
|
||||
ItemDescFormContext context = BuildContext(rawData);
|
||||
return OpenUIInternal(context);
|
||||
}
|
||||
|
||||
public override int? OpenUI(object userData = null)
|
||||
{
|
||||
if (userData is ItemDescFormContext context)
|
||||
{
|
||||
return OpenUIInternal(context);
|
||||
}
|
||||
|
||||
if (userData is ItemDescFormRawData rawData)
|
||||
{
|
||||
return OpenUI(rawData);
|
||||
}
|
||||
|
||||
if (userData != null)
|
||||
{
|
||||
Log.Warning("ItemDescFormController.OpenUI() userData type is invalid.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return OpenUIInternal(Context);
|
||||
}
|
||||
|
||||
public override void BindUseCase(IUIUseCase useCase)
|
||||
{
|
||||
if (!(useCase is ItemDescFormUseCase))
|
||||
{
|
||||
Log.Error("ItemDescForm.BindUseCase() useCase is invalid.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
using GeometryTD.CustomEvent;
|
||||
using GeometryTD.Definition;
|
||||
using GameFramework.Event;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public partial class RewardSelectFormController : UIFormControllerCommonBase<RewardSelectFormContext, RewardSelectForm>
|
||||
{
|
||||
private RewardSelectFormUseCase _useCase;
|
||||
|
||||
protected override UIFormType UIFormTypeId => UIFormType.RewardSelectForm;
|
||||
|
||||
protected override void RefreshUI(RewardSelectForm form, RewardSelectFormContext context)
|
||||
{
|
||||
form.RefreshUI(context);
|
||||
}
|
||||
|
||||
protected override void SubscribeCustomEvents()
|
||||
{
|
||||
GameEntry.Event.Subscribe(RewardSelectItemSelectedEventArgs.EventId, OnRewardSelected);
|
||||
GameEntry.Event.Subscribe(RewardSelectRefreshEventArgs.EventId, OnRefreshClicked);
|
||||
GameEntry.Event.Subscribe(RewardSelectGiveUpEventArgs.EventId, OnGiveUpClicked);
|
||||
}
|
||||
|
||||
protected override void UnsubscribeCustomEvents()
|
||||
{
|
||||
GameEntry.Event.Unsubscribe(RewardSelectItemSelectedEventArgs.EventId, OnRewardSelected);
|
||||
GameEntry.Event.Unsubscribe(RewardSelectRefreshEventArgs.EventId, OnRefreshClicked);
|
||||
GameEntry.Event.Unsubscribe(RewardSelectGiveUpEventArgs.EventId, OnGiveUpClicked);
|
||||
}
|
||||
|
||||
public override int? OpenUI(object userData = null)
|
||||
{
|
||||
if (userData is RewardSelectFormContext context)
|
||||
{
|
||||
return OpenUIInternal(context);
|
||||
}
|
||||
|
||||
if (userData is RewardSelectFormRawData rawDataFromUserData)
|
||||
{
|
||||
return OpenUI(rawDataFromUserData);
|
||||
}
|
||||
|
||||
if (userData != null)
|
||||
{
|
||||
Log.Warning("RewardSelectFormController.OpenUI() userData type is invalid.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_useCase == null)
|
||||
{
|
||||
Log.Error("RewardSelectFormController.OpenUI() useCase is null.");
|
||||
return null;
|
||||
}
|
||||
|
||||
RewardSelectFormRawData rawData = _useCase.CreateInitialModel();
|
||||
return OpenUI(rawData);
|
||||
}
|
||||
|
||||
public int? OpenUI(RewardSelectFormRawData rawData)
|
||||
{
|
||||
RewardSelectFormContext context = BuildContext(rawData);
|
||||
return OpenUIInternal(context);
|
||||
}
|
||||
|
||||
public override void BindUseCase(IUIUseCase useCase)
|
||||
{
|
||||
if (!(useCase is RewardSelectFormUseCase rewardSelectUseCase))
|
||||
{
|
||||
Log.Error("RewardSelectFormController.BindUseCase() useCase is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
_useCase = rewardSelectUseCase;
|
||||
}
|
||||
|
||||
private void OnRewardSelected(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!IsEventFromCurrentForm(sender) || !(e is RewardSelectItemSelectedEventArgs args))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_useCase == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RewardSelectFormRawData nextRawData = _useCase.SelectReward(args.SelectedIndex);
|
||||
if (nextRawData == null)
|
||||
{
|
||||
CloseUI();
|
||||
return;
|
||||
}
|
||||
|
||||
OpenUI(nextRawData);
|
||||
}
|
||||
|
||||
private void OnRefreshClicked(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!IsEventFromCurrentForm(sender) || !(e is RewardSelectRefreshEventArgs))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_useCase == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RewardSelectFormRawData nextRawData = _useCase.TryRotateSelection();
|
||||
if (nextRawData == null)
|
||||
{
|
||||
CloseUI();
|
||||
return;
|
||||
}
|
||||
|
||||
OpenUI(nextRawData);
|
||||
}
|
||||
|
||||
private void OnGiveUpClicked(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!IsEventFromCurrentForm(sender) || !(e is RewardSelectGiveUpEventArgs))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_useCase == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_useCase.TryGiveUp())
|
||||
{
|
||||
CloseUI();
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsEventFromCurrentForm(object sender)
|
||||
{
|
||||
if (Form == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(sender, Form))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (sender is Component component)
|
||||
{
|
||||
RewardSelectForm ownerForm = component.GetComponentInParent<RewardSelectForm>();
|
||||
return ownerForm == Form;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
using GameFramework;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// 对话框显示数据。
|
||||
/// </summary>
|
||||
public class DialogFormRawData
|
||||
{
|
||||
/// <summary>
|
||||
/// 模式,即按钮数量。取值 1、2、3。
|
||||
/// </summary>
|
||||
public int Mode
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标题。
|
||||
/// </summary>
|
||||
public string Title
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 消息内容。
|
||||
/// </summary>
|
||||
public string Message
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 弹出窗口时是否暂停游戏。
|
||||
/// </summary>
|
||||
public bool PauseGame
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 确认按钮文本。
|
||||
/// </summary>
|
||||
public string ConfirmText
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 确定按钮回调。
|
||||
/// </summary>
|
||||
public GameFrameworkAction<object> OnClickConfirm
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取消按钮文本。
|
||||
/// </summary>
|
||||
public string CancelText
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取消按钮回调。
|
||||
/// </summary>
|
||||
public GameFrameworkAction<object> OnClickCancel
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 中立按钮文本。
|
||||
/// </summary>
|
||||
public string OtherText
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 其它按钮回调。
|
||||
/// </summary>
|
||||
public GameFrameworkAction<object> OnClickOther
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 用户自定义数据。
|
||||
/// </summary>
|
||||
public object UserData
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using GeometryTD.Definition;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class ItemDescFormRawData
|
||||
{
|
||||
public string Title;
|
||||
public string TypeText;
|
||||
public string Description;
|
||||
public int Price;
|
||||
public Vector2 ScreenPosition;
|
||||
public TagType[] Tags;
|
||||
public TagRuntimeData[] TagRuntimes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class RewardSelectFormRawData
|
||||
{
|
||||
public string TipText;
|
||||
public RewardSelectItemRawData[] RewardItems;
|
||||
public int RefreshCost;
|
||||
public bool CanRefresh;
|
||||
public bool CanGiveUp;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using GeometryTD.Definition;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class RewardSelectItemRawData
|
||||
{
|
||||
public TowerCompSlotType SlotType;
|
||||
public string Title;
|
||||
public string TypeText;
|
||||
public string Description;
|
||||
public RarityType Rarity;
|
||||
public TagType[] Tags;
|
||||
public Sprite Icon;
|
||||
public TowerCompItemData SourceItem;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using GeometryTD.CustomUtility;
|
||||
using GeometryTD.Definition;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public static class RewardSelectItemRawDataBuilder
|
||||
{
|
||||
public static RewardSelectItemRawData Build(TowerCompItemData item)
|
||||
{
|
||||
return new RewardSelectItemRawData
|
||||
{
|
||||
SlotType = item.SlotType,
|
||||
Title = item.Name,
|
||||
TypeText = BuildTypeText(item.SlotType),
|
||||
Description = BuildDescription(item),
|
||||
Rarity = item.Rarity,
|
||||
Tags = item.Tags != null ? (TagType[])item.Tags.Clone() : Array.Empty<TagType>(),
|
||||
Icon = null,
|
||||
SourceItem = item
|
||||
};
|
||||
}
|
||||
|
||||
private static string BuildTypeText(TowerCompSlotType slotType)
|
||||
{
|
||||
return slotType switch
|
||||
{
|
||||
TowerCompSlotType.Muzzle => "Muzzle Component",
|
||||
TowerCompSlotType.Bearing => "Bearing Component",
|
||||
TowerCompSlotType.Base => "Base Component",
|
||||
TowerCompSlotType.Accessory => "Accessory",
|
||||
_ => "Component"
|
||||
};
|
||||
}
|
||||
|
||||
private static string BuildDescription(TowerCompItemData item)
|
||||
{
|
||||
if (item is MuzzleCompItemData muzzle)
|
||||
{
|
||||
return ItemDescUtility.BuildMuzzleDesc(muzzle);
|
||||
}
|
||||
|
||||
if (item is BearingCompItemData bearing)
|
||||
{
|
||||
return ItemDescUtility.BuildBearingDesc(bearing);
|
||||
}
|
||||
|
||||
if (item is BaseCompItemData baseComp)
|
||||
{
|
||||
return ItemDescUtility.BuildBaseDesc(baseComp);
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class ItemDescFormUseCase : IUIUseCase
|
||||
{
|
||||
public ItemDescFormUseCase()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GeometryTD.Definition;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class RewardSelectFormUseCase : IUIUseCase
|
||||
{
|
||||
private readonly List<RewardSelectItemRawData> _rewardPool = new List<RewardSelectItemRawData>();
|
||||
|
||||
private RewardSelectFormRawData _currentModel;
|
||||
private Action<RewardSelectItemRawData> _onRewardSelected;
|
||||
private Action _onGiveUp;
|
||||
|
||||
private int _displayCount = 3;
|
||||
private int _refreshCost;
|
||||
private bool _allowRotateOnce = true;
|
||||
private bool _allowGiveUp = true;
|
||||
private bool _hasRotated;
|
||||
private int _selectionOffset;
|
||||
private string _tipText = "Select one reward";
|
||||
|
||||
// RewardSelectForm keeps a fixed reward pool for the current node.
|
||||
// The "refresh" action only rotates which slice of that pool is shown so save/load
|
||||
// can reopen the same node with the exact same reward contents.
|
||||
public void ConfigureRewardPool(
|
||||
IReadOnlyList<RewardSelectItemRawData> rewardPool,
|
||||
int displayCount = 3,
|
||||
int refreshCost = 0,
|
||||
bool allowRotateOnce = true,
|
||||
bool allowGiveUp = true,
|
||||
string tipText = null)
|
||||
{
|
||||
_rewardPool.Clear();
|
||||
if (rewardPool != null)
|
||||
{
|
||||
foreach (var item in rewardPool)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_rewardPool.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
_displayCount = Mathf.Max(1, displayCount);
|
||||
_refreshCost = Mathf.Max(0, refreshCost);
|
||||
_allowRotateOnce = allowRotateOnce;
|
||||
_allowGiveUp = allowGiveUp;
|
||||
_tipText = string.IsNullOrWhiteSpace(tipText) ? "Select one reward" : tipText;
|
||||
_hasRotated = false;
|
||||
_selectionOffset = 0;
|
||||
_currentModel = null;
|
||||
}
|
||||
|
||||
public void ConfigureRewardCandidates(
|
||||
IReadOnlyList<TowerCompItemData> rewardCandidates,
|
||||
int displayCount = 3,
|
||||
int refreshCost = 0,
|
||||
bool allowRotateOnce = true,
|
||||
bool allowGiveUp = true,
|
||||
string tipText = null)
|
||||
{
|
||||
_rewardPool.Clear();
|
||||
if (rewardCandidates != null)
|
||||
{
|
||||
foreach (var item in rewardCandidates)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_rewardPool.Add(RewardSelectItemRawDataBuilder.Build(item));
|
||||
}
|
||||
}
|
||||
|
||||
_displayCount = Mathf.Max(1, displayCount);
|
||||
_refreshCost = Mathf.Max(0, refreshCost);
|
||||
_allowRotateOnce = allowRotateOnce;
|
||||
_allowGiveUp = allowGiveUp;
|
||||
_tipText = string.IsNullOrWhiteSpace(tipText) ? "Select one reward" : tipText;
|
||||
_hasRotated = false;
|
||||
_selectionOffset = 0;
|
||||
_currentModel = null;
|
||||
}
|
||||
|
||||
public void SetCallbacks(Action<RewardSelectItemRawData> onRewardSelected, Action onGiveUp = null)
|
||||
{
|
||||
_onRewardSelected = onRewardSelected;
|
||||
_onGiveUp = onGiveUp;
|
||||
}
|
||||
|
||||
public RewardSelectFormRawData CreateInitialModel()
|
||||
{
|
||||
_hasRotated = false;
|
||||
_currentModel = BuildModel();
|
||||
return _currentModel;
|
||||
}
|
||||
|
||||
public RewardSelectFormRawData TryRotateSelection()
|
||||
{
|
||||
if (_currentModel == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!CanRotateSelectionInternal())
|
||||
{
|
||||
return _currentModel;
|
||||
}
|
||||
|
||||
if (!TryConsumeRotateCost())
|
||||
{
|
||||
_currentModel.CanRefresh = false;
|
||||
return _currentModel;
|
||||
}
|
||||
|
||||
_hasRotated = true;
|
||||
if (_rewardPool.Count > 0)
|
||||
{
|
||||
_selectionOffset = (_selectionOffset + _displayCount) % _rewardPool.Count;
|
||||
}
|
||||
|
||||
_currentModel = BuildModel();
|
||||
return _currentModel;
|
||||
}
|
||||
|
||||
public RewardSelectFormRawData SelectReward(int selectedIndex)
|
||||
{
|
||||
if (_currentModel?.RewardItems == null || _currentModel.RewardItems.Length <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (selectedIndex < 0 || selectedIndex >= _currentModel.RewardItems.Length)
|
||||
{
|
||||
return _currentModel;
|
||||
}
|
||||
|
||||
RewardSelectItemRawData selectedReward = _currentModel.RewardItems[selectedIndex];
|
||||
_onRewardSelected?.Invoke(selectedReward);
|
||||
|
||||
_currentModel = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool TryGiveUp()
|
||||
{
|
||||
if (!_allowGiveUp)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_onGiveUp?.Invoke();
|
||||
_currentModel = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
private RewardSelectFormRawData BuildModel()
|
||||
{
|
||||
RewardSelectItemRawData[] selectedRewards = SelectRewards();
|
||||
|
||||
return new RewardSelectFormRawData
|
||||
{
|
||||
TipText = _tipText,
|
||||
RewardItems = selectedRewards,
|
||||
RefreshCost = _refreshCost,
|
||||
CanRefresh = selectedRewards.Length > 0 && CanRotateSelectionInternal(),
|
||||
CanGiveUp = _allowGiveUp
|
||||
};
|
||||
}
|
||||
|
||||
private RewardSelectItemRawData[] SelectRewards()
|
||||
{
|
||||
if (_rewardPool.Count <= 0)
|
||||
{
|
||||
return Array.Empty<RewardSelectItemRawData>();
|
||||
}
|
||||
|
||||
int finalCount = Mathf.Clamp(_displayCount, 1, _rewardPool.Count);
|
||||
RewardSelectItemRawData[] results = new RewardSelectItemRawData[finalCount];
|
||||
for (int i = 0; i < finalCount; i++)
|
||||
{
|
||||
int sourceIndex = (_selectionOffset + i) % _rewardPool.Count;
|
||||
RewardSelectItemRawData source = _rewardPool[sourceIndex];
|
||||
results[i] = source;
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private bool CanRotateSelectionInternal()
|
||||
{
|
||||
if (!_allowRotateOnce || _hasRotated)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return CanPayRotateCost();
|
||||
}
|
||||
|
||||
private bool CanPayRotateCost()
|
||||
{
|
||||
if (_refreshCost <= 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (GameEntry.PlayerInventory == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return GameEntry.PlayerInventory.Gold >= _refreshCost;
|
||||
}
|
||||
|
||||
private bool TryConsumeRotateCost()
|
||||
{
|
||||
if (_refreshCost <= 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (GameEntry.PlayerInventory == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return GameEntry.PlayerInventory.TryConsumeGold(_refreshCost);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
using GameFramework;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class DialogForm : UGuiForm
|
||||
{
|
||||
[SerializeField] private TMP_Text _titleText = null;
|
||||
[SerializeField] private TMP_Text _messageText = null;
|
||||
[SerializeField] private GameObject[] _modeObjects = null;
|
||||
[SerializeField] private TMP_Text[] _confirmTexts = null;
|
||||
[SerializeField] private TMP_Text[] _cancelTexts = null;
|
||||
[SerializeField] private TMP_Text[] _otherTexts = null;
|
||||
|
||||
private int _dialogMode = 1;
|
||||
private bool _pauseGame = false;
|
||||
private object _userData = null;
|
||||
private GameFrameworkAction<object> _onClickConfirmGFAction = null;
|
||||
private GameFrameworkAction<object> _onClickCancelGFAction = null;
|
||||
private GameFrameworkAction<object> _onClickOtherGFAction = null;
|
||||
private DialogFormContext _context = null;
|
||||
|
||||
public int DialogMode => _dialogMode;
|
||||
|
||||
public bool PauseGame => _pauseGame;
|
||||
|
||||
public object UserData => _userData;
|
||||
|
||||
public void OnConfirmButtonClick()
|
||||
{
|
||||
Close();
|
||||
|
||||
if (_onClickConfirmGFAction != null)
|
||||
{
|
||||
_onClickConfirmGFAction(_userData);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnCancelButtonClick()
|
||||
{
|
||||
Close();
|
||||
|
||||
if (_onClickCancelGFAction != null)
|
||||
{
|
||||
_onClickCancelGFAction(_userData);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnOtherButtonClick()
|
||||
{
|
||||
Close();
|
||||
|
||||
if (_onClickOtherGFAction != null)
|
||||
{
|
||||
_onClickOtherGFAction(_userData);
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_2017_3_OR_NEWER
|
||||
protected override void OnOpen(object userData)
|
||||
#else
|
||||
protected internal override void OnOpen(object userData)
|
||||
#endif
|
||||
{
|
||||
base.OnOpen(userData);
|
||||
|
||||
if (userData is DialogFormContext context)
|
||||
{
|
||||
RefreshUI(context);
|
||||
return;
|
||||
}
|
||||
|
||||
if (userData is DialogFormRawData dialogParams)
|
||||
{
|
||||
RefreshUI(BuildContext(dialogParams));
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Warning("DialogForm requires DialogFormContext or DialogParams as userData.");
|
||||
}
|
||||
|
||||
#if UNITY_2017_3_OR_NEWER
|
||||
protected override void OnClose(bool isShutdown, object userData)
|
||||
#else
|
||||
protected internal override void OnClose(bool isShutdown, object userData)
|
||||
#endif
|
||||
{
|
||||
if (_pauseGame)
|
||||
{
|
||||
GameEntry.Base.ResumeGame();
|
||||
}
|
||||
|
||||
_dialogMode = 1;
|
||||
_titleText.text = string.Empty;
|
||||
_messageText.text = string.Empty;
|
||||
_pauseGame = false;
|
||||
_userData = null;
|
||||
_context = null;
|
||||
|
||||
RefreshConfirmText(string.Empty);
|
||||
_onClickConfirmGFAction = null;
|
||||
|
||||
RefreshCancelText(string.Empty);
|
||||
_onClickCancelGFAction = null;
|
||||
|
||||
RefreshOtherText(string.Empty);
|
||||
_onClickOtherGFAction = null;
|
||||
|
||||
base.OnClose(isShutdown, userData);
|
||||
}
|
||||
|
||||
public void RefreshUI(DialogFormContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
Log.Warning("DialogForm context is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
_context = context;
|
||||
|
||||
_dialogMode = context.Mode;
|
||||
RefreshDialogMode();
|
||||
|
||||
_titleText.text = context.Title;
|
||||
_messageText.text = context.Message;
|
||||
|
||||
_pauseGame = context.PauseGame;
|
||||
RefreshPauseGame();
|
||||
|
||||
_userData = context.UserData;
|
||||
|
||||
RefreshConfirmText(context.ConfirmText);
|
||||
_onClickConfirmGFAction = context.OnClickConfirm;
|
||||
|
||||
RefreshCancelText(context.CancelText);
|
||||
_onClickCancelGFAction = context.OnClickCancel;
|
||||
|
||||
RefreshOtherText(context.OtherText);
|
||||
_onClickOtherGFAction = context.OnClickOther;
|
||||
}
|
||||
|
||||
private static DialogFormContext BuildContext(DialogFormRawData rawData)
|
||||
{
|
||||
if (rawData == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new DialogFormContext
|
||||
{
|
||||
Mode = rawData.Mode,
|
||||
Title = rawData.Title,
|
||||
Message = rawData.Message,
|
||||
PauseGame = rawData.PauseGame,
|
||||
ConfirmText = rawData.ConfirmText,
|
||||
OnClickConfirm = rawData.OnClickConfirm,
|
||||
CancelText = rawData.CancelText,
|
||||
OnClickCancel = rawData.OnClickCancel,
|
||||
OtherText = rawData.OtherText,
|
||||
OnClickOther = rawData.OnClickOther,
|
||||
UserData = rawData.UserData
|
||||
};
|
||||
}
|
||||
|
||||
private void RefreshDialogMode()
|
||||
{
|
||||
for (int i = 1; i <= _modeObjects.Length; i++)
|
||||
{
|
||||
_modeObjects[i - 1].SetActive(i == _dialogMode);
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshPauseGame()
|
||||
{
|
||||
if (_pauseGame)
|
||||
{
|
||||
GameEntry.Base.PauseGame();
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshConfirmText(string confirmText)
|
||||
{
|
||||
foreach (var text in _confirmTexts)
|
||||
{
|
||||
text.text = confirmText;
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshCancelText(string cancelText)
|
||||
{
|
||||
foreach (var text in _cancelTexts)
|
||||
{
|
||||
text.text = cancelText;
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshOtherText(string otherText)
|
||||
{
|
||||
foreach (var text in _otherTexts)
|
||||
{
|
||||
text.text = otherText;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
using GeometryTD.Definition;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class IconArea : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private Image _board;
|
||||
|
||||
[SerializeField] private Image _icon;
|
||||
|
||||
private IconAreaContext _context;
|
||||
|
||||
public virtual Sprite CurrentIconSprite => _icon != null ? _icon.sprite : null;
|
||||
|
||||
public virtual Color CurrentIconColor => _icon != null ? _icon.color : Color.white;
|
||||
|
||||
public virtual Vector2 CurrentIconSize => _icon != null ? _icon.rectTransform.rect.size : Vector2.zero;
|
||||
|
||||
public virtual Material CurrentIconMaterial => _icon != null ? _icon.material : null;
|
||||
|
||||
public virtual RectTransform IconRectTransform => _icon != null ? _icon.rectTransform : null;
|
||||
|
||||
public virtual void OnInit(IconAreaContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
Log.Error("Icon area need IconAreaContext.");
|
||||
return;
|
||||
}
|
||||
|
||||
_context = context;
|
||||
|
||||
switch (context.ComponentSlotType)
|
||||
{
|
||||
case TowerCompSlotType.Base:
|
||||
case TowerCompSlotType.Bearing:
|
||||
case TowerCompSlotType.Muzzle:
|
||||
GameEntry.SpriteCache.GetSprite(context.ComponentSlotType.ToString(), SetIcon);
|
||||
break;
|
||||
default:
|
||||
SetIcon(_context.Icon);
|
||||
break;
|
||||
}
|
||||
|
||||
SetRarity(_context.Rarity);
|
||||
SetIconColor(_context.Color);
|
||||
SetIconVisible(true);
|
||||
}
|
||||
|
||||
public virtual void SetIcon(Sprite sprite)
|
||||
{
|
||||
if (_icon != null)
|
||||
{
|
||||
_icon.sprite = sprite;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void SetRarity(RarityType rarity)
|
||||
{
|
||||
if (_board == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_board.color = rarity switch
|
||||
{
|
||||
RarityType.White => Color.white,
|
||||
RarityType.Green => Color.green,
|
||||
RarityType.Blue => Color.blue,
|
||||
RarityType.Purple => Color.magenta,
|
||||
RarityType.Red => Color.red,
|
||||
_ => Color.clear,
|
||||
};
|
||||
}
|
||||
|
||||
public virtual void SetIconColor(Color color)
|
||||
{
|
||||
if (_icon == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_icon.color = color;
|
||||
}
|
||||
|
||||
public virtual void SetIconVisible(bool visible)
|
||||
{
|
||||
if (_icon != null)
|
||||
{
|
||||
_icon.enabled = visible;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void OnReset()
|
||||
{
|
||||
SetIcon(null);
|
||||
SetRarity(RarityType.None);
|
||||
SetIconColor(Color.clear);
|
||||
SetIconVisible(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,392 @@
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.UI;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class ItemDescForm : UGuiForm
|
||||
{
|
||||
[SerializeField] private RectTransform _content;
|
||||
|
||||
[SerializeField] private TMP_Text _itemTitle;
|
||||
|
||||
[SerializeField] private TMP_Text _itemTypeText;
|
||||
|
||||
[SerializeField] private RectTransform _descScrollView;
|
||||
|
||||
[SerializeField] private TMP_Text _itemDescription;
|
||||
|
||||
[SerializeField] private TMP_Text _itemPrice;
|
||||
|
||||
[SerializeField] private Transform _tagAreaParent;
|
||||
|
||||
[SerializeField] private GameObject _tagItemPrefab;
|
||||
|
||||
[SerializeField] private bool _autoResizeHeight = true;
|
||||
|
||||
[SerializeField] private float _fixedTopHeight = 150f;
|
||||
|
||||
[SerializeField] private float _fixedBottomHeight = 150f;
|
||||
|
||||
[SerializeField] private float _fixedWidth = 300f;
|
||||
|
||||
[SerializeField] private float _maxDescriptionViewportHeight = 0f;
|
||||
|
||||
[SerializeField] private float _screenEdgePadding = 0f;
|
||||
|
||||
[SerializeField] private float _sideGap = 16f;
|
||||
|
||||
[SerializeField] private float _anchorItemWidth = 0f;
|
||||
|
||||
private readonly List<GameObject> _runtimeTagItems = new List<GameObject>();
|
||||
|
||||
private ItemDescFormContext _context;
|
||||
private Vector2 _targetPos;
|
||||
private bool _isOpened;
|
||||
|
||||
public void RefreshUI(ItemDescFormContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
Log.Warning("ItemDescForm context is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
_context = context;
|
||||
|
||||
_targetPos = ConvertScreenToAnchored(_context.ScreenPosition);
|
||||
|
||||
if (_content != null)
|
||||
{
|
||||
_content.anchoredPosition3D = _targetPos;
|
||||
}
|
||||
|
||||
if (_itemTitle != null) _itemTitle.text = _context.Title ?? string.Empty;
|
||||
if (_itemTypeText != null) _itemTypeText.text = _context.TypeText ?? string.Empty;
|
||||
if (_itemDescription != null) _itemDescription.text = _context.Description ?? string.Empty;
|
||||
if (_itemPrice != null) _itemPrice.text = $"Price: {_context.Price} Gold";
|
||||
|
||||
RefreshTags(_context.Tags);
|
||||
|
||||
ResizeToFitContent();
|
||||
ApplySideAnchoredTargetPos();
|
||||
}
|
||||
|
||||
protected override void OnOpen(object userData)
|
||||
{
|
||||
base.OnOpen(userData);
|
||||
_isOpened = true;
|
||||
|
||||
if (!(userData is ItemDescFormContext context))
|
||||
{
|
||||
Log.Error("ItemDescFormContext is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
RefreshUI(context);
|
||||
}
|
||||
|
||||
protected override void OnClose(bool isShutdown, object userData)
|
||||
{
|
||||
_isOpened = false;
|
||||
ClearTags();
|
||||
_context = null;
|
||||
base.OnClose(isShutdown, userData);
|
||||
}
|
||||
|
||||
public void OnBlankAreaClick()
|
||||
{
|
||||
CloseSelf();
|
||||
}
|
||||
|
||||
public void OnBackgroundClick()
|
||||
{
|
||||
CloseSelf();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!_isOpened || _content == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryGetPointerDownScreenPosition(out Vector2 screenPoint))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsPointerInContent(screenPoint))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CloseSelf();
|
||||
}
|
||||
|
||||
private void ResizeToFitContent()
|
||||
{
|
||||
if (!_autoResizeHeight || _content == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_itemDescription == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_itemDescription.ForceMeshUpdate();
|
||||
float descriptionHeight = Mathf.Max(0f, _itemDescription.preferredHeight);
|
||||
SetRectHeight(_itemDescription.rectTransform, descriptionHeight);
|
||||
|
||||
RectTransform scrollContent = _itemDescription.rectTransform.parent as RectTransform;
|
||||
if (scrollContent != null)
|
||||
{
|
||||
SetRectHeight(scrollContent, descriptionHeight);
|
||||
}
|
||||
|
||||
float viewportHeight = descriptionHeight;
|
||||
if (_descScrollView != null)
|
||||
{
|
||||
viewportHeight = Mathf.Min(descriptionHeight, ResolveMaxDescriptionViewportHeight());
|
||||
SetRectHeight(_descScrollView, viewportHeight);
|
||||
|
||||
if (_descScrollView.TryGetComponent(out ScrollRect scrollRect))
|
||||
{
|
||||
scrollRect.verticalNormalizedPosition = 1f;
|
||||
}
|
||||
}
|
||||
|
||||
float targetHeight = _fixedTopHeight + viewportHeight + _fixedBottomHeight;
|
||||
|
||||
Vector2 size = new Vector2(_fixedWidth, targetHeight);
|
||||
_content.sizeDelta = size;
|
||||
}
|
||||
|
||||
private float ResolveMaxDescriptionViewportHeight()
|
||||
{
|
||||
float maxViewportHeight = _maxDescriptionViewportHeight;
|
||||
RectTransform parent = _content.parent as RectTransform;
|
||||
if (parent != null)
|
||||
{
|
||||
float availableHeight = parent.rect.height - _fixedTopHeight - _fixedBottomHeight
|
||||
- _screenEdgePadding * 2f;
|
||||
availableHeight = Mathf.Max(0f, availableHeight);
|
||||
maxViewportHeight = maxViewportHeight > 0f
|
||||
? Mathf.Min(maxViewportHeight, availableHeight)
|
||||
: availableHeight;
|
||||
}
|
||||
|
||||
return maxViewportHeight > 0f ? maxViewportHeight : float.MaxValue;
|
||||
}
|
||||
|
||||
private static void SetRectHeight(RectTransform rectTransform, float height)
|
||||
{
|
||||
if (rectTransform == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 size = rectTransform.sizeDelta;
|
||||
size.y = height;
|
||||
rectTransform.sizeDelta = size;
|
||||
}
|
||||
|
||||
private void ApplySideAnchoredTargetPos()
|
||||
{
|
||||
if (_content == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RectTransform parent = _content.parent as RectTransform;
|
||||
if (parent == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 size = _content.sizeDelta;
|
||||
if (size.x <= 0f || size.y <= 0f)
|
||||
{
|
||||
size = _content.rect.size;
|
||||
}
|
||||
|
||||
Vector2 pivot = _content.pivot;
|
||||
float halfWidth = parent.rect.width * 0.5f;
|
||||
float halfHeight = parent.rect.height * 0.5f;
|
||||
|
||||
float minX = -halfWidth + size.x * pivot.x + _screenEdgePadding;
|
||||
float maxX = halfWidth - size.x * (1f - pivot.x) - _screenEdgePadding;
|
||||
float minY = -halfHeight + size.y * pivot.y + _screenEdgePadding;
|
||||
float maxY = halfHeight - size.y * (1f - pivot.y) - _screenEdgePadding;
|
||||
|
||||
float clampedY = minY <= maxY ? Mathf.Clamp(_targetPos.y, minY, maxY) : 0f;
|
||||
|
||||
float anchorHalfWidth = Mathf.Max(0f, _anchorItemWidth * 0.5f);
|
||||
float sideGap = Mathf.Max(0f, _sideGap);
|
||||
float horizontalOffset = anchorHalfWidth + size.x * 0.5f + sideGap;
|
||||
|
||||
float leftCandidateX = _targetPos.x - horizontalOffset;
|
||||
float rightCandidateX = _targetPos.x + horizontalOffset;
|
||||
|
||||
float finalX;
|
||||
if (IsInsideBounds(leftCandidateX, minX, maxX))
|
||||
{
|
||||
finalX = leftCandidateX;
|
||||
}
|
||||
else if (IsInsideBounds(rightCandidateX, minX, maxX))
|
||||
{
|
||||
finalX = rightCandidateX;
|
||||
}
|
||||
else
|
||||
{
|
||||
finalX = minX <= maxX ? Mathf.Clamp(leftCandidateX, minX, maxX) : 0f;
|
||||
}
|
||||
|
||||
_content.anchoredPosition = new Vector2(finalX, clampedY);
|
||||
}
|
||||
|
||||
private Vector2 ConvertScreenToAnchored(Vector2 screenPosition)
|
||||
{
|
||||
if (_content == null)
|
||||
{
|
||||
return screenPosition;
|
||||
}
|
||||
|
||||
RectTransform parent = _content.parent as RectTransform;
|
||||
if (parent == 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(parent, screenPosition, uiCamera,
|
||||
out Vector2 localPoint))
|
||||
{
|
||||
return screenPosition;
|
||||
}
|
||||
|
||||
return localPoint;
|
||||
}
|
||||
|
||||
private static bool IsInsideBounds(float value, float min, float max)
|
||||
{
|
||||
return value >= min && value <= max;
|
||||
}
|
||||
|
||||
private void RefreshTags(TagItemContext[] tags)
|
||||
{
|
||||
ClearTags();
|
||||
|
||||
if (_tagAreaParent == null || _tagItemPrefab == null || tags == null || tags.Length <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_tagItemPrefab.scene.IsValid() && _tagItemPrefab.transform.IsChildOf(_tagAreaParent))
|
||||
{
|
||||
_tagItemPrefab.SetActive(false);
|
||||
}
|
||||
|
||||
for (int i = 0; i < tags.Length; i++)
|
||||
{
|
||||
TagItemContext tagContext = tags[i];
|
||||
if (tagContext == null || string.IsNullOrWhiteSpace(tagContext.TagName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
GameObject tagGo = Instantiate(_tagItemPrefab, _tagAreaParent);
|
||||
tagGo.SetActive(true);
|
||||
|
||||
if (tagGo.TryGetComponent(out TagItem tagItem))
|
||||
{
|
||||
tagItem.OnInit(tagContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
TMP_Text tagText = tagGo.GetComponentInChildren<TMP_Text>(true);
|
||||
if (tagText != null)
|
||||
{
|
||||
tagText.text = tagContext.TagName;
|
||||
}
|
||||
}
|
||||
|
||||
_runtimeTagItems.Add(tagGo);
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearTags()
|
||||
{
|
||||
for (int i = _runtimeTagItems.Count - 1; i >= 0; i--)
|
||||
{
|
||||
GameObject tagGo = _runtimeTagItems[i];
|
||||
if (tagGo != null)
|
||||
{
|
||||
Destroy(tagGo);
|
||||
}
|
||||
}
|
||||
|
||||
_runtimeTagItems.Clear();
|
||||
}
|
||||
|
||||
private void CloseSelf()
|
||||
{
|
||||
GameEntry.UI.CloseUIForm(this);
|
||||
}
|
||||
|
||||
private bool IsPointerInContent(Vector2 screenPoint)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
return RectTransformUtility.RectangleContainsScreenPoint(_content, screenPoint, uiCamera);
|
||||
}
|
||||
|
||||
private static bool TryGetPointerDownScreenPosition(out Vector2 screenPoint)
|
||||
{
|
||||
Touchscreen touchscreen = Touchscreen.current;
|
||||
if (touchscreen != null)
|
||||
{
|
||||
var touches = touchscreen.touches;
|
||||
for (int i = 0; i < touches.Count; i++)
|
||||
{
|
||||
var touch = touches[i];
|
||||
if (!touch.press.wasPressedThisFrame)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
screenPoint = touch.position.ReadValue();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Mouse mouse = Mouse.current;
|
||||
if (mouse != null && mouse.leftButton.wasPressedThisFrame)
|
||||
{
|
||||
screenPoint = mouse.position.ReadValue();
|
||||
return true;
|
||||
}
|
||||
|
||||
screenPoint = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
using System.Collections.Generic;
|
||||
using GeometryTD.CustomEvent;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class RewardItem : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private IconArea _iconArea;
|
||||
|
||||
[SerializeField] private TMP_Text _titleText;
|
||||
|
||||
[SerializeField] private TMP_Text _typeText;
|
||||
|
||||
[SerializeField] private TMP_Text _descriptionText;
|
||||
|
||||
[SerializeField] private RectTransform _descriptionScrollView;
|
||||
|
||||
[SerializeField] private RectTransform _descriptionContent;
|
||||
|
||||
[SerializeField] private float _maxDescriptionViewportHeight = 0f;
|
||||
|
||||
[SerializeField] private Transform _tagItemParent;
|
||||
|
||||
[SerializeField] private TagItem _tagItemTemplate;
|
||||
|
||||
private readonly List<TagItem> _runtimeTagItems = new List<TagItem>();
|
||||
|
||||
private RewardItemContext _context;
|
||||
|
||||
|
||||
public void OnInit(RewardItemContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
OnReset();
|
||||
gameObject.SetActive(false);
|
||||
return;
|
||||
}
|
||||
|
||||
_context = context;
|
||||
gameObject.SetActive(true);
|
||||
|
||||
if (_iconArea != null)
|
||||
{
|
||||
if (context.IconArea != null)
|
||||
{
|
||||
_iconArea.OnInit(context.IconArea);
|
||||
}
|
||||
else
|
||||
{
|
||||
_iconArea.OnReset();
|
||||
}
|
||||
}
|
||||
|
||||
if (_titleText != null)
|
||||
{
|
||||
_titleText.text = context.Title ?? string.Empty;
|
||||
}
|
||||
|
||||
if (_typeText != null)
|
||||
{
|
||||
_typeText.text = context.TypeText ?? string.Empty;
|
||||
}
|
||||
|
||||
if (_descriptionText != null)
|
||||
{
|
||||
_descriptionText.text = context.Description ?? string.Empty;
|
||||
}
|
||||
|
||||
ResizeDescriptionArea();
|
||||
|
||||
RefreshTags(context.Tags);
|
||||
}
|
||||
|
||||
public void OnReset()
|
||||
{
|
||||
_context = null;
|
||||
|
||||
if (_iconArea != null)
|
||||
{
|
||||
_iconArea.OnReset();
|
||||
}
|
||||
|
||||
if (_titleText != null)
|
||||
{
|
||||
_titleText.text = string.Empty;
|
||||
}
|
||||
|
||||
if (_typeText != null)
|
||||
{
|
||||
_typeText.text = string.Empty;
|
||||
}
|
||||
|
||||
if (_descriptionText != null)
|
||||
{
|
||||
_descriptionText.text = string.Empty;
|
||||
}
|
||||
|
||||
ResetDescriptionScroll();
|
||||
|
||||
ClearTags();
|
||||
}
|
||||
|
||||
public void OnClick()
|
||||
{
|
||||
if (_context == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GameEntry.Event.Fire(this, RewardSelectItemSelectedEventArgs.Create(_context.Index));
|
||||
}
|
||||
|
||||
private void RefreshTags(TagItemContext[] tagContexts)
|
||||
{
|
||||
ClearTags();
|
||||
|
||||
if (_tagItemParent == null || tagContexts == null || tagContexts.Length <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TagItem template = ResolveTagTemplate();
|
||||
if (template == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < tagContexts.Length; i++)
|
||||
{
|
||||
TagItemContext tagContext = tagContexts[i];
|
||||
if (tagContext == null || string.IsNullOrWhiteSpace(tagContext.TagName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
TagItem tagItem = Instantiate(template, _tagItemParent);
|
||||
tagItem.gameObject.SetActive(true);
|
||||
tagItem.OnInit(tagContext);
|
||||
_runtimeTagItems.Add(tagItem);
|
||||
}
|
||||
}
|
||||
|
||||
private void ResizeDescriptionArea()
|
||||
{
|
||||
if (_descriptionText == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_descriptionText.ForceMeshUpdate();
|
||||
float descriptionHeight = Mathf.Max(0f, _descriptionText.preferredHeight);
|
||||
SetRectHeight(_descriptionText.rectTransform, descriptionHeight);
|
||||
|
||||
if (_descriptionContent != null)
|
||||
{
|
||||
SetRectHeight(_descriptionContent, descriptionHeight);
|
||||
}
|
||||
|
||||
if (_descriptionScrollView != null)
|
||||
{
|
||||
float viewportHeight = _maxDescriptionViewportHeight > 0f
|
||||
? Mathf.Min(descriptionHeight, _maxDescriptionViewportHeight)
|
||||
: descriptionHeight;
|
||||
SetRectHeight(_descriptionScrollView, viewportHeight);
|
||||
}
|
||||
|
||||
ResetDescriptionScroll();
|
||||
}
|
||||
|
||||
private void ResetDescriptionScroll()
|
||||
{
|
||||
if (_descriptionScrollView != null &&
|
||||
_descriptionScrollView.TryGetComponent(out ScrollRect scrollRect))
|
||||
{
|
||||
scrollRect.verticalNormalizedPosition = 1f;
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetRectHeight(RectTransform rectTransform, float height)
|
||||
{
|
||||
if (rectTransform == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 size = rectTransform.sizeDelta;
|
||||
size.y = height;
|
||||
rectTransform.sizeDelta = size;
|
||||
}
|
||||
|
||||
private TagItem ResolveTagTemplate()
|
||||
{
|
||||
if (_tagItemTemplate != null)
|
||||
{
|
||||
return _tagItemTemplate;
|
||||
}
|
||||
|
||||
if (_tagItemParent == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
_tagItemTemplate = _tagItemParent.GetComponentInChildren<TagItem>(true);
|
||||
if (_tagItemTemplate != null)
|
||||
{
|
||||
_tagItemTemplate.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
return _tagItemTemplate;
|
||||
}
|
||||
|
||||
private void ClearTags()
|
||||
{
|
||||
for (int i = _runtimeTagItems.Count - 1; i >= 0; i--)
|
||||
{
|
||||
TagItem tagItem = _runtimeTagItems[i];
|
||||
if (tagItem != null)
|
||||
{
|
||||
Destroy(tagItem.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
_runtimeTagItems.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
using GeometryTD.CustomEvent;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class RewardSelectForm : UGuiForm
|
||||
{
|
||||
[SerializeField] private Transform _selectItemParent;
|
||||
|
||||
[SerializeField] private RewardItem[] _rewardItems;
|
||||
|
||||
[SerializeField] private TMP_Text _tipText;
|
||||
|
||||
[SerializeField] private TMP_Text _refreshButtonText;
|
||||
|
||||
[SerializeField] private CommonButton _refreshButton;
|
||||
|
||||
[SerializeField] private CommonButton _giveUpButton;
|
||||
|
||||
private RewardSelectFormContext _context;
|
||||
|
||||
public void OnInit(RewardSelectFormContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
Log.Error("RewardSelectForm need Context.");
|
||||
return;
|
||||
}
|
||||
|
||||
RefreshUI(context);
|
||||
}
|
||||
|
||||
public void RefreshUI(RewardSelectFormContext context)
|
||||
{
|
||||
_context = context;
|
||||
EnsureRewardItems();
|
||||
|
||||
if (_tipText != null)
|
||||
{
|
||||
_tipText.text = context?.TipText ?? string.Empty;
|
||||
}
|
||||
|
||||
if (_refreshButtonText != null)
|
||||
{
|
||||
_refreshButtonText.text = context?.RefreshButtonText ?? string.Empty;
|
||||
}
|
||||
|
||||
if (_refreshButton != null)
|
||||
{
|
||||
_refreshButton.Interactive = context?.CanRefresh ?? false;
|
||||
}
|
||||
|
||||
if (_giveUpButton != null)
|
||||
{
|
||||
_giveUpButton.Interactive = context?.CanGiveUp ?? false;
|
||||
}
|
||||
|
||||
if (_rewardItems == null || _rewardItems.Length <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < _rewardItems.Length; i++)
|
||||
{
|
||||
RewardItem rewardItem = _rewardItems[i];
|
||||
if (rewardItem == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
RewardItemContext itemContext = null;
|
||||
if (context?.RewardItems != null && i < context.RewardItems.Length)
|
||||
{
|
||||
itemContext = context.RewardItems[i];
|
||||
}
|
||||
|
||||
rewardItem.OnInit(itemContext);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnRefreshButtonClick()
|
||||
{
|
||||
if (_context != null && !_context.CanRefresh)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GameEntry.Event.Fire(this, RewardSelectRefreshEventArgs.Create());
|
||||
}
|
||||
|
||||
public void OnGiveUpButtonClick()
|
||||
{
|
||||
if (_context != null && !_context.CanGiveUp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GameEntry.Event.Fire(this, RewardSelectGiveUpEventArgs.Create());
|
||||
}
|
||||
|
||||
protected override void OnOpen(object userData)
|
||||
{
|
||||
base.OnOpen(userData);
|
||||
|
||||
if (userData is RewardSelectFormContext context)
|
||||
{
|
||||
RefreshUI(context);
|
||||
return;
|
||||
}
|
||||
|
||||
Log.Warning("RewardSelectForm requires RewardSelectFormContext as userData.");
|
||||
}
|
||||
|
||||
protected override void OnClose(bool isShutdown, object userData)
|
||||
{
|
||||
_context = null;
|
||||
|
||||
if (_tipText != null)
|
||||
{
|
||||
_tipText.text = string.Empty;
|
||||
}
|
||||
|
||||
if (_refreshButtonText != null)
|
||||
{
|
||||
_refreshButtonText.text = string.Empty;
|
||||
}
|
||||
|
||||
if (_rewardItems != null)
|
||||
{
|
||||
for (int i = 0; i < _rewardItems.Length; i++)
|
||||
{
|
||||
RewardItem rewardItem = _rewardItems[i];
|
||||
if (rewardItem == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
rewardItem.OnReset();
|
||||
rewardItem.gameObject.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
base.OnClose(isShutdown, userData);
|
||||
}
|
||||
|
||||
private void EnsureRewardItems()
|
||||
{
|
||||
if (_rewardItems != null && _rewardItems.Length > 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_selectItemParent != null)
|
||||
{
|
||||
_rewardItems = _selectItemParent.GetComponentsInChildren<RewardItem>(true);
|
||||
}
|
||||
|
||||
if (_rewardItems == null || _rewardItems.Length <= 0)
|
||||
{
|
||||
_rewardItems = GetComponentsInChildren<RewardItem>(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class TagItem : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private RectTransform _bgRect;
|
||||
|
||||
[SerializeField] private TMP_Text _tagName;
|
||||
|
||||
[SerializeField] private float _bgExtraWidth = 12f;
|
||||
|
||||
private TagItemContext _context;
|
||||
|
||||
public void OnInit(TagItemContext context)
|
||||
{
|
||||
_context = context;
|
||||
_tagName.text = _context?.TagName ?? string.Empty;
|
||||
|
||||
_tagName.ForceMeshUpdate();
|
||||
var targetWidth = _tagName.preferredWidth + _bgExtraWidth;
|
||||
_bgRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, targetWidth);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class TowerIconArea : IconArea
|
||||
{
|
||||
[SerializeField] private Image _baseIcon;
|
||||
|
||||
[SerializeField] private Image _bearingIcon;
|
||||
|
||||
[SerializeField] private Image _muzzleIcon;
|
||||
|
||||
public Sprite BaseIconSprite => _baseIcon != null ? _baseIcon.sprite : null;
|
||||
|
||||
public Sprite BearingIconSprite => _bearingIcon != null ? _bearingIcon.sprite : null;
|
||||
|
||||
public Sprite MuzzleIconSprite => _muzzleIcon != null ? _muzzleIcon.sprite : null;
|
||||
|
||||
public Color BaseIconColor => _baseIcon != null ? _baseIcon.color : Color.white;
|
||||
|
||||
public Color BearingIconColor => _bearingIcon != null ? _bearingIcon.color : Color.white;
|
||||
|
||||
public Color MuzzleIconColor => _muzzleIcon != null ? _muzzleIcon.color : Color.white;
|
||||
|
||||
public Material BaseIconMaterial => _baseIcon != null ? _baseIcon.material : null;
|
||||
|
||||
public Material BearingIconMaterial => _bearingIcon != null ? _bearingIcon.material : null;
|
||||
|
||||
public Material MuzzleIconMaterial => _muzzleIcon != null ? _muzzleIcon.material : null;
|
||||
|
||||
public override void OnInit(IconAreaContext context)
|
||||
{
|
||||
if (!(context is TowerIconAreaContext towerContext))
|
||||
{
|
||||
base.OnInit(context);
|
||||
return;
|
||||
}
|
||||
|
||||
SetRarity(towerContext.Rarity);
|
||||
SetIconVisible(false);
|
||||
|
||||
SetLayerColor(_baseIcon, towerContext.BaseColor);
|
||||
SetLayerColor(_bearingIcon, towerContext.BearingColor);
|
||||
SetLayerColor(_muzzleIcon, towerContext.MuzzleColor);
|
||||
}
|
||||
|
||||
public void OnInit(TowerIconAreaContext context)
|
||||
{
|
||||
OnInit((IconAreaContext)context);
|
||||
}
|
||||
|
||||
public override void OnReset()
|
||||
{
|
||||
base.OnReset();
|
||||
SetLayerColor(_baseIcon, Color.clear);
|
||||
SetLayerColor(_bearingIcon, Color.clear);
|
||||
SetLayerColor(_muzzleIcon, Color.clear);
|
||||
SetIconVisible(false);
|
||||
}
|
||||
|
||||
private static void SetLayerColor(Image icon, Color color)
|
||||
{
|
||||
if (icon == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
icon.color = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user