添加“奖励 3 选 1”UI
- RewardSelectForm:其中包括 3 个可选的 RewardItem ,刷新和放弃按钮 - 应用战斗节点基地生命产生的额外事件
This commit is contained in:
@@ -0,0 +1,338 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GeometryTD.Definition;
|
||||
using UnityEngine;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
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 _allowRefreshOnce = true;
|
||||
private bool _allowGiveUp = true;
|
||||
private bool _hasRefreshed;
|
||||
private string _tipText = "Select one reward";
|
||||
|
||||
public RewardSelectFormUseCase()
|
||||
{
|
||||
ConfigureRewardPool(BuildDefaultRewardPool());
|
||||
}
|
||||
|
||||
public void ConfigureRewardPool(
|
||||
IReadOnlyList<RewardSelectItemRawData> rewardPool,
|
||||
int displayCount = 3,
|
||||
int refreshCost = 0,
|
||||
bool allowRefreshOnce = true,
|
||||
bool allowGiveUp = true,
|
||||
string tipText = null)
|
||||
{
|
||||
_rewardPool.Clear();
|
||||
if (rewardPool != null)
|
||||
{
|
||||
for (int i = 0; i < rewardPool.Count; i++)
|
||||
{
|
||||
RewardSelectItemRawData item = rewardPool[i];
|
||||
if (item == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_rewardPool.Add(CloneRawItem(item));
|
||||
}
|
||||
}
|
||||
|
||||
_displayCount = Mathf.Max(1, displayCount);
|
||||
_refreshCost = Mathf.Max(0, refreshCost);
|
||||
_allowRefreshOnce = allowRefreshOnce;
|
||||
_allowGiveUp = allowGiveUp;
|
||||
_tipText = string.IsNullOrWhiteSpace(tipText) ? "Select one reward" : tipText;
|
||||
_hasRefreshed = false;
|
||||
_currentModel = null;
|
||||
}
|
||||
|
||||
public void SetCallbacks(Action<RewardSelectItemRawData> onRewardSelected, Action onGiveUp = null)
|
||||
{
|
||||
_onRewardSelected = onRewardSelected;
|
||||
_onGiveUp = onGiveUp;
|
||||
}
|
||||
|
||||
public RewardSelectFormRawData CreateInitialModel()
|
||||
{
|
||||
_hasRefreshed = false;
|
||||
_currentModel = BuildModel();
|
||||
return _currentModel;
|
||||
}
|
||||
|
||||
public RewardSelectFormRawData TryRefresh()
|
||||
{
|
||||
if (_currentModel == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!CanRefreshInternal())
|
||||
{
|
||||
return _currentModel;
|
||||
}
|
||||
|
||||
if (!TryConsumeRefreshCost())
|
||||
{
|
||||
_currentModel.CanRefresh = false;
|
||||
return _currentModel;
|
||||
}
|
||||
|
||||
_hasRefreshed = true;
|
||||
_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 && CanRefreshInternal(),
|
||||
CanGiveUp = _allowGiveUp
|
||||
};
|
||||
}
|
||||
|
||||
private RewardSelectItemRawData[] SelectRewards()
|
||||
{
|
||||
if (_rewardPool.Count <= 0)
|
||||
{
|
||||
return Array.Empty<RewardSelectItemRawData>();
|
||||
}
|
||||
|
||||
int finalCount = Mathf.Clamp(_displayCount, 1, _rewardPool.Count);
|
||||
int[] indexes = new int[_rewardPool.Count];
|
||||
for (int i = 0; i < indexes.Length; i++)
|
||||
{
|
||||
indexes[i] = i;
|
||||
}
|
||||
|
||||
for (int i = 0; i < finalCount; i++)
|
||||
{
|
||||
int randomIndex = Random.Range(i, indexes.Length);
|
||||
(indexes[i], indexes[randomIndex]) = (indexes[randomIndex], indexes[i]);
|
||||
}
|
||||
|
||||
RewardSelectItemRawData[] results = new RewardSelectItemRawData[finalCount];
|
||||
for (int i = 0; i < finalCount; i++)
|
||||
{
|
||||
RewardSelectItemRawData source = _rewardPool[indexes[i]];
|
||||
results[i] = CloneRawItem(source);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private bool CanRefreshInternal()
|
||||
{
|
||||
if (!_allowRefreshOnce || _hasRefreshed)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return CanPayRefreshCost();
|
||||
}
|
||||
|
||||
private bool CanPayRefreshCost()
|
||||
{
|
||||
if (_refreshCost <= 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (GameEntry.PlayerInventory == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return GameEntry.PlayerInventory.Gold >= _refreshCost;
|
||||
}
|
||||
|
||||
private bool TryConsumeRefreshCost()
|
||||
{
|
||||
if (_refreshCost <= 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (GameEntry.PlayerInventory == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return GameEntry.PlayerInventory.TryConsumeGold(_refreshCost);
|
||||
}
|
||||
|
||||
private static List<RewardSelectItemRawData> BuildDefaultRewardPool()
|
||||
{
|
||||
BackpackInventoryData inventory = RepoFormUseCase.SampleInventory();
|
||||
List<RewardSelectItemRawData> rewards = new List<RewardSelectItemRawData>();
|
||||
|
||||
if (inventory?.MuzzleComponents != null)
|
||||
{
|
||||
for (int i = 0; i < inventory.MuzzleComponents.Count; i++)
|
||||
{
|
||||
MuzzleCompItemData item = inventory.MuzzleComponents[i];
|
||||
if (item == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
rewards.Add(BuildRawItem(item));
|
||||
}
|
||||
}
|
||||
|
||||
if (inventory?.BearingComponents != null)
|
||||
{
|
||||
for (int i = 0; i < inventory.BearingComponents.Count; i++)
|
||||
{
|
||||
BearingCompItemData item = inventory.BearingComponents[i];
|
||||
if (item == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
rewards.Add(BuildRawItem(item));
|
||||
}
|
||||
}
|
||||
|
||||
if (inventory?.BaseComponents != null)
|
||||
{
|
||||
for (int i = 0; i < inventory.BaseComponents.Count; i++)
|
||||
{
|
||||
BaseCompItemData item = inventory.BaseComponents[i];
|
||||
if (item == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
rewards.Add(BuildRawItem(item));
|
||||
}
|
||||
}
|
||||
|
||||
return rewards;
|
||||
}
|
||||
|
||||
private static RewardSelectItemRawData BuildRawItem(TowerCompItemData item)
|
||||
{
|
||||
return new RewardSelectItemRawData
|
||||
{
|
||||
RewardId = item.InstanceId,
|
||||
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,
|
||||
IsSelectable = true,
|
||||
SourceItem = item
|
||||
};
|
||||
}
|
||||
|
||||
private static RewardSelectItemRawData CloneRawItem(RewardSelectItemRawData source)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new RewardSelectItemRawData
|
||||
{
|
||||
RewardId = source.RewardId,
|
||||
SlotType = source.SlotType,
|
||||
Title = source.Title,
|
||||
TypeText = source.TypeText,
|
||||
Description = source.Description,
|
||||
Rarity = source.Rarity,
|
||||
Tags = source.Tags != null ? (TagType[])source.Tags.Clone() : Array.Empty<TagType>(),
|
||||
Icon = source.Icon,
|
||||
IsSelectable = source.IsSelectable,
|
||||
SourceItem = source.SourceItem,
|
||||
UserData = source.UserData
|
||||
};
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
int damage = muzzle.AttackDamage != null && muzzle.AttackDamage.Length > 0 ? muzzle.AttackDamage[0] : 0;
|
||||
return $"Damage: {damage}, Spread: {muzzle.DamageRandomRate:P0}";
|
||||
}
|
||||
|
||||
if (item is BearingCompItemData bearing)
|
||||
{
|
||||
float range = bearing.AttackRange != null && bearing.AttackRange.Length > 0 ? bearing.AttackRange[0] : 0f;
|
||||
float rotateSpeed = bearing.RotateSpeed != null && bearing.RotateSpeed.Length > 0 ? bearing.RotateSpeed[0] : 0f;
|
||||
return $"Range: {range:0.##}, Rotate Speed: {rotateSpeed:0.##}";
|
||||
}
|
||||
|
||||
if (item is BaseCompItemData baseComp)
|
||||
{
|
||||
float attackSpeed = baseComp.AttackSpeed != null && baseComp.AttackSpeed.Length > 0 ? baseComp.AttackSpeed[0] : 0f;
|
||||
return $"Attack Speed: {attackSpeed:0.##}, Property: {baseComp.AttackPropertyType}";
|
||||
}
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ee056442ccca28c4da90ef00330d5a55
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user