添加“奖励 3 选 1”UI
- RewardSelectForm:其中包括 3 个可选的 RewardItem ,刷新和放弃按钮 - 应用战斗节点基地生命产生的额外事件
This commit is contained in:
+78
-6
@@ -66,6 +66,18 @@ namespace GeometryTD.CustomComponent
|
||||
GameEntry.CombatNode?.ApplyEnemyDropReward(coin, gold);
|
||||
}
|
||||
|
||||
public void AddSettlementGold(int gainedGold)
|
||||
{
|
||||
int gold = Mathf.Max(0, gainedGold);
|
||||
if (gold <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GainedGold += gold;
|
||||
_rewardInventory.Gold += gold;
|
||||
}
|
||||
|
||||
public bool TryRollOutGameItemDrop(int displayPhaseIndex, LevelThemeType themeType)
|
||||
{
|
||||
int phaseIndex = Mathf.Max(1, displayPhaseIndex);
|
||||
@@ -75,12 +87,7 @@ namespace GeometryTD.CustomComponent
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryPickDropPoolRow(phaseIndex, themeType, out DROutGameDropPool selectedRow))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!TryBuildDropItem(selectedRow, out TowerCompItemData droppedItem))
|
||||
if (!TryRollOutGameItem(phaseIndex, themeType, out TowerCompItemData droppedItem))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -106,6 +113,71 @@ namespace GeometryTD.CustomComponent
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryRollOutGameItem(int displayPhaseIndex, LevelThemeType themeType, out TowerCompItemData droppedItem)
|
||||
{
|
||||
droppedItem = null;
|
||||
int phaseIndex = Mathf.Max(1, displayPhaseIndex);
|
||||
if (!TryPickDropPoolRow(phaseIndex, themeType, out DROutGameDropPool selectedRow))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return TryBuildDropItem(selectedRow, out droppedItem);
|
||||
}
|
||||
|
||||
public IReadOnlyList<TowerCompItemData> RollSettlementRewardCandidates(
|
||||
int displayPhaseIndex,
|
||||
LevelThemeType themeType,
|
||||
int candidateCount)
|
||||
{
|
||||
int resolvedCount = Mathf.Max(0, candidateCount);
|
||||
if (resolvedCount <= 0)
|
||||
{
|
||||
return Array.Empty<TowerCompItemData>();
|
||||
}
|
||||
|
||||
List<TowerCompItemData> candidates = new List<TowerCompItemData>(resolvedCount);
|
||||
HashSet<int> selectedPoolRowIds = new HashSet<int>();
|
||||
int maxAttempts = Mathf.Max(resolvedCount * 6, resolvedCount);
|
||||
int phaseIndex = Mathf.Max(1, displayPhaseIndex);
|
||||
|
||||
int attempts = 0;
|
||||
while (candidates.Count < resolvedCount && attempts < maxAttempts)
|
||||
{
|
||||
attempts++;
|
||||
if (!TryPickDropPoolRow(phaseIndex, themeType, out DROutGameDropPool selectedRow) || selectedRow == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (!selectedPoolRowIds.Add(selectedRow.Id))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!TryBuildDropItem(selectedRow, out TowerCompItemData droppedItem) || droppedItem == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
candidates.Add(droppedItem);
|
||||
}
|
||||
|
||||
attempts = 0;
|
||||
while (candidates.Count < resolvedCount && attempts < maxAttempts)
|
||||
{
|
||||
attempts++;
|
||||
if (!TryRollOutGameItem(phaseIndex, themeType, out TowerCompItemData droppedItem) || droppedItem == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
candidates.Add(droppedItem);
|
||||
}
|
||||
|
||||
return candidates;
|
||||
}
|
||||
|
||||
private bool TryPickDropPoolRow(int displayPhaseIndex, LevelThemeType themeType, out DROutGameDropPool selectedRow)
|
||||
{
|
||||
selectedRow = null;
|
||||
|
||||
+300
-3
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GeometryTD.CustomEvent;
|
||||
using GeometryTD.DataTable;
|
||||
@@ -11,6 +12,13 @@ namespace GeometryTD.CustomComponent
|
||||
{
|
||||
public class CombatScheduler
|
||||
{
|
||||
private const int RewardSelectDisplayCount = 3;
|
||||
private const float FullBaseHpGoldBonusRate = 0.3f;
|
||||
private const float HighBaseHpGoldBonusRate = 0.1f;
|
||||
private const float HighBaseHpThreshold = 0.8f;
|
||||
private const float MidBaseHpThreshold = 0.5f;
|
||||
private const float LowBaseHpTowerEndurancePenalty = 10f;
|
||||
|
||||
private enum SchedulerState : byte
|
||||
{
|
||||
Idle = 0,
|
||||
@@ -32,9 +40,14 @@ namespace GeometryTD.CustomComponent
|
||||
private EntityComponent _entity;
|
||||
private DRLevel _currentLevel;
|
||||
private CombatFinishFormUseCase _combatFinishFormUseCase;
|
||||
private RewardSelectFormUseCase _rewardSelectFormUseCase;
|
||||
private SchedulerState _state = SchedulerState.Idle;
|
||||
private bool _initialized;
|
||||
private bool _isFinishAsVictory = true;
|
||||
private int _pendingFinishDefeatedEnemyCount;
|
||||
private int _pendingFinishGainedGold;
|
||||
private BackpackInventoryData _pendingFinishRewardInventory;
|
||||
private bool _hasPendingFinishSettlement;
|
||||
|
||||
public bool IsRunning => _state == SchedulerState.WaitingForLoading || _state == SchedulerState.RunningPhase;
|
||||
public bool IsCompleted => _state == SchedulerState.Completed;
|
||||
@@ -63,6 +76,7 @@ namespace GeometryTD.CustomComponent
|
||||
_enemyManager.OnInit(this);
|
||||
_loadSession.OnInit(_entity);
|
||||
EnsureCombatFinishFormUseCaseBound();
|
||||
EnsureRewardSelectFormUseCaseBound();
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
@@ -86,6 +100,7 @@ namespace GeometryTD.CustomComponent
|
||||
|
||||
CleanupAllCombatEntities();
|
||||
CloseCombatFinishForm();
|
||||
CloseRewardSelectForm();
|
||||
_enemyManager.EndPhase();
|
||||
_enemyManager.ResetCombatStats();
|
||||
ResetRuntime();
|
||||
@@ -156,10 +171,12 @@ namespace GeometryTD.CustomComponent
|
||||
|
||||
CleanupAllCombatEntities();
|
||||
CloseCombatFinishForm();
|
||||
CloseRewardSelectForm();
|
||||
_enemyManager.OnDestroy();
|
||||
ResetRuntime();
|
||||
_eventBridge.Unbind();
|
||||
_combatFinishFormUseCase = null;
|
||||
_rewardSelectFormUseCase = null;
|
||||
|
||||
_entity = null;
|
||||
_initialized = false;
|
||||
@@ -244,8 +261,6 @@ namespace GeometryTD.CustomComponent
|
||||
private void EnterFinishFlow(string reason, bool isVictory)
|
||||
{
|
||||
int defeatedEnemyCount = _enemyManager.DefeatedEnemyCount;
|
||||
BackpackInventoryData rewardInventory = _combatResourceManager.GetRewardInventorySnapshot();
|
||||
GameEntry.PlayerInventory?.MergeInventory(rewardInventory);
|
||||
|
||||
// Step 1: stop runtime and clear enemy entities only.
|
||||
_enemyManager.EndPhase();
|
||||
@@ -253,12 +268,21 @@ namespace GeometryTD.CustomComponent
|
||||
_isFinishAsVictory = isVictory;
|
||||
_enemyManager.CleanupTrackedEnemies();
|
||||
|
||||
bool shouldOpenFullBaseHpRewardSelect = false;
|
||||
ApplySettlementModifierByBaseHp(isVictory, out shouldOpenFullBaseHpRewardSelect);
|
||||
PreparePendingFinishSummary(defeatedEnemyCount);
|
||||
|
||||
Log.Info(
|
||||
"CombatScheduler entered finish flow. Level={0}. Reason={1}",
|
||||
_currentLevel != null ? _currentLevel.Id : 0,
|
||||
reason);
|
||||
|
||||
OpenCombatFinishForm(defeatedEnemyCount, _combatResourceManager.GainedGold, rewardInventory);
|
||||
if (shouldOpenFullBaseHpRewardSelect && TryOpenFullBaseHpRewardSelect())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CommitPendingSettlementAndOpenFinishForm();
|
||||
}
|
||||
|
||||
public void OnEnemyReachedBase(int baseDamage)
|
||||
@@ -296,6 +320,7 @@ namespace GeometryTD.CustomComponent
|
||||
_phaseLoopRuntime.Reset();
|
||||
_loadSession.Reset();
|
||||
_combatResourceManager.Reset();
|
||||
ClearPendingFinishContext();
|
||||
_currentLevel = null;
|
||||
_isFinishAsVictory = true;
|
||||
_state = SchedulerState.Idle;
|
||||
@@ -314,6 +339,12 @@ namespace GeometryTD.CustomComponent
|
||||
GameEntry.UIRouter.BindUIUseCase(UIFormType.CombatFinishForm, _combatFinishFormUseCase);
|
||||
}
|
||||
|
||||
private void EnsureRewardSelectFormUseCaseBound()
|
||||
{
|
||||
_rewardSelectFormUseCase ??= new RewardSelectFormUseCase();
|
||||
GameEntry.UIRouter.BindUIUseCase(UIFormType.RewardSelectForm, _rewardSelectFormUseCase);
|
||||
}
|
||||
|
||||
private void OpenCombatFinishForm(int defeatedEnemyCount, int gainedGold, BackpackInventoryData rewardInventory)
|
||||
{
|
||||
EnsureCombatFinishFormUseCaseBound();
|
||||
@@ -324,6 +355,264 @@ namespace GeometryTD.CustomComponent
|
||||
GameEntry.UIRouter.OpenUI(UIFormType.CombatFinishForm);
|
||||
}
|
||||
|
||||
private void PreparePendingFinishSummary(int defeatedEnemyCount)
|
||||
{
|
||||
_pendingFinishDefeatedEnemyCount = Mathf.Max(0, defeatedEnemyCount);
|
||||
_pendingFinishGainedGold = Mathf.Max(0, _combatResourceManager.GainedGold);
|
||||
_pendingFinishRewardInventory = _combatResourceManager.GetRewardInventorySnapshot();
|
||||
_hasPendingFinishSettlement = true;
|
||||
}
|
||||
|
||||
private void CommitPendingSettlementAndOpenFinishForm()
|
||||
{
|
||||
if (!_hasPendingFinishSettlement)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BackpackInventoryData rewardInventory = _pendingFinishRewardInventory ?? new BackpackInventoryData();
|
||||
int defeatedEnemyCount = _pendingFinishDefeatedEnemyCount;
|
||||
int gainedGold = _pendingFinishGainedGold;
|
||||
|
||||
GameEntry.PlayerInventory?.MergeInventory(rewardInventory);
|
||||
OpenCombatFinishForm(defeatedEnemyCount, gainedGold, rewardInventory);
|
||||
ClearPendingFinishContext();
|
||||
}
|
||||
|
||||
private void ClearPendingFinishContext()
|
||||
{
|
||||
_pendingFinishDefeatedEnemyCount = 0;
|
||||
_pendingFinishGainedGold = 0;
|
||||
_pendingFinishRewardInventory = null;
|
||||
_hasPendingFinishSettlement = false;
|
||||
}
|
||||
|
||||
private void ApplySettlementModifierByBaseHp(bool isVictory, out bool shouldOpenFullBaseHpRewardSelect)
|
||||
{
|
||||
shouldOpenFullBaseHpRewardSelect = false;
|
||||
if (!isVictory)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int levelRewardGold = _currentLevel != null ? Mathf.Max(0, _currentLevel.RewardGold) : 0;
|
||||
int currentBaseHp = 0;
|
||||
int maxBaseHp = 0;
|
||||
float bonusRate = 0f;
|
||||
bool appliedLowBaseHpPenalty = false;
|
||||
|
||||
ResolveBaseHpSnapshot(out currentBaseHp, out maxBaseHp);
|
||||
if (maxBaseHp > 0 && currentBaseHp >= maxBaseHp)
|
||||
{
|
||||
bonusRate = FullBaseHpGoldBonusRate;
|
||||
shouldOpenFullBaseHpRewardSelect = true;
|
||||
}
|
||||
else if (maxBaseHp > 0)
|
||||
{
|
||||
float hpRate = (float)Mathf.Clamp(currentBaseHp, 0, maxBaseHp) / maxBaseHp;
|
||||
if (hpRate >= HighBaseHpThreshold)
|
||||
{
|
||||
bonusRate = HighBaseHpGoldBonusRate;
|
||||
}
|
||||
else if (hpRate < MidBaseHpThreshold)
|
||||
{
|
||||
appliedLowBaseHpPenalty = ApplyLowBaseHpPenalty();
|
||||
}
|
||||
}
|
||||
|
||||
int goldForBonusCalculation = Mathf.Max(0, _combatResourceManager.GainedGold) + levelRewardGold;
|
||||
int bonusGold = bonusRate > 0f ? Mathf.FloorToInt(goldForBonusCalculation * bonusRate) : 0;
|
||||
int settlementGold = levelRewardGold + bonusGold;
|
||||
_combatResourceManager.AddSettlementGold(settlementGold);
|
||||
|
||||
Log.Info(
|
||||
"Combat settlement resolved. BaseHp={0}/{1}, LevelReward={2}, BonusRate={3:P0}, BonusGold={4}, FullHpRewardSelect={5}, LowHpPenalty={6}.",
|
||||
currentBaseHp,
|
||||
maxBaseHp,
|
||||
levelRewardGold,
|
||||
bonusRate,
|
||||
bonusGold,
|
||||
shouldOpenFullBaseHpRewardSelect,
|
||||
appliedLowBaseHpPenalty);
|
||||
}
|
||||
|
||||
private void ResolveBaseHpSnapshot(out int currentBaseHp, out int maxBaseHp)
|
||||
{
|
||||
currentBaseHp = 0;
|
||||
maxBaseHp = _currentLevel != null ? Mathf.Max(0, _currentLevel.BaseHp) : 0;
|
||||
|
||||
CombatNodeComponent combatNode = GameEntry.CombatNode;
|
||||
if (combatNode != null)
|
||||
{
|
||||
currentBaseHp = Mathf.Max(0, combatNode.CurrentBaseHp);
|
||||
if (maxBaseHp <= 0 && combatNode.CurrentLevel != null)
|
||||
{
|
||||
maxBaseHp = Mathf.Max(0, combatNode.CurrentLevel.BaseHp);
|
||||
}
|
||||
}
|
||||
|
||||
if (maxBaseHp > 0)
|
||||
{
|
||||
currentBaseHp = Mathf.Clamp(currentBaseHp, 0, maxBaseHp);
|
||||
}
|
||||
}
|
||||
|
||||
private bool ApplyLowBaseHpPenalty()
|
||||
{
|
||||
PlayerInventoryComponent inventory = GameEntry.PlayerInventory;
|
||||
if (inventory == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int affectedTowerCount = inventory.ReduceAllTowerEndurance(LowBaseHpTowerEndurancePenalty);
|
||||
return affectedTowerCount > 0;
|
||||
}
|
||||
|
||||
private bool TryOpenFullBaseHpRewardSelect()
|
||||
{
|
||||
IReadOnlyList<TowerCompItemData> candidateItems = _combatResourceManager.RollSettlementRewardCandidates(
|
||||
_phaseLoopRuntime.DisplayPhaseIndex,
|
||||
ResolveCurrentThemeType(),
|
||||
RewardSelectDisplayCount);
|
||||
if (candidateItems == null || candidateItems.Count <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
List<RewardSelectItemRawData> rewardPool = new List<RewardSelectItemRawData>(candidateItems.Count);
|
||||
for (int i = 0; i < candidateItems.Count; i++)
|
||||
{
|
||||
TowerCompItemData item = candidateItems[i];
|
||||
if (item == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
rewardPool.Add(BuildRewardSelectRawData(item));
|
||||
}
|
||||
|
||||
if (rewardPool.Count <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
EnsureRewardSelectFormUseCaseBound();
|
||||
_rewardSelectFormUseCase.SetCallbacks(OnFullBaseHpRewardSelected, OnFullBaseHpRewardGiveUp);
|
||||
_rewardSelectFormUseCase.ConfigureRewardPool(
|
||||
rewardPool,
|
||||
displayCount: RewardSelectDisplayCount,
|
||||
refreshCost: 0,
|
||||
allowRefreshOnce: false,
|
||||
allowGiveUp: false,
|
||||
tipText: "基地满血奖励:请选择 1 个组件");
|
||||
|
||||
RewardSelectFormRawData rawData = _rewardSelectFormUseCase.CreateInitialModel();
|
||||
if (rawData == null || rawData.RewardItems == null || rawData.RewardItems.Length <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
GameEntry.UIRouter.OpenUI(UIFormType.RewardSelectForm, rawData);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnFullBaseHpRewardSelected(RewardSelectItemRawData selectedReward)
|
||||
{
|
||||
if (_pendingFinishRewardInventory != null && selectedReward?.SourceItem != null)
|
||||
{
|
||||
TryAppendRewardComponent(_pendingFinishRewardInventory, selectedReward.SourceItem);
|
||||
}
|
||||
|
||||
CommitPendingSettlementAndOpenFinishForm();
|
||||
}
|
||||
|
||||
private void OnFullBaseHpRewardGiveUp()
|
||||
{
|
||||
CommitPendingSettlementAndOpenFinishForm();
|
||||
}
|
||||
|
||||
private static bool TryAppendRewardComponent(BackpackInventoryData rewardInventory, TowerCompItemData selectedItem)
|
||||
{
|
||||
if (rewardInventory == null || selectedItem == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (selectedItem is MuzzleCompItemData muzzleComp)
|
||||
{
|
||||
rewardInventory.MuzzleComponents.Add(muzzleComp);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (selectedItem is BearingCompItemData bearingComp)
|
||||
{
|
||||
rewardInventory.BearingComponents.Add(bearingComp);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (selectedItem is BaseCompItemData baseComp)
|
||||
{
|
||||
rewardInventory.BaseComponents.Add(baseComp);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static RewardSelectItemRawData BuildRewardSelectRawData(TowerCompItemData item)
|
||||
{
|
||||
return new RewardSelectItemRawData
|
||||
{
|
||||
RewardId = item.InstanceId,
|
||||
SlotType = item.SlotType,
|
||||
Title = item.Name,
|
||||
TypeText = BuildRewardTypeText(item.SlotType),
|
||||
Description = BuildRewardDescription(item),
|
||||
Rarity = item.Rarity,
|
||||
Tags = item.Tags != null ? (TagType[])item.Tags.Clone() : Array.Empty<TagType>(),
|
||||
Icon = null,
|
||||
IsSelectable = true,
|
||||
SourceItem = item
|
||||
};
|
||||
}
|
||||
|
||||
private static string BuildRewardTypeText(TowerCompSlotType slotType)
|
||||
{
|
||||
return slotType switch
|
||||
{
|
||||
TowerCompSlotType.Muzzle => "Muzzle Component",
|
||||
TowerCompSlotType.Bearing => "Bearing Component",
|
||||
TowerCompSlotType.Base => "Base Component",
|
||||
TowerCompSlotType.Accessory => "Accessory",
|
||||
_ => "Component"
|
||||
};
|
||||
}
|
||||
|
||||
private static string BuildRewardDescription(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;
|
||||
}
|
||||
|
||||
public void OnEnemyDefeatedRewardResolved(int gainedCoin, int gainedGold)
|
||||
{
|
||||
_combatResourceManager.AddEnemyDefeatedReward(gainedCoin, gainedGold);
|
||||
@@ -358,6 +647,11 @@ namespace GeometryTD.CustomComponent
|
||||
GameEntry.UIRouter.CloseUI(UIFormType.CombatFinishForm);
|
||||
}
|
||||
|
||||
private void CloseRewardSelectForm()
|
||||
{
|
||||
GameEntry.UIRouter.CloseUI(UIFormType.RewardSelectForm);
|
||||
}
|
||||
|
||||
public bool OnCombatFinishReturnRequested()
|
||||
{
|
||||
if (_state != SchedulerState.WaitingForFinishReturn)
|
||||
@@ -368,6 +662,7 @@ namespace GeometryTD.CustomComponent
|
||||
// Step 2: clear remaining map/UI resources and close finish form.
|
||||
_loadSession.Cleanup();
|
||||
CloseCombatFinishForm();
|
||||
CloseRewardSelectForm();
|
||||
_state = SchedulerState.Completed;
|
||||
GameEntry.CombatNode?.OnCombatEndedByScheduler(_isFinishAsVictory);
|
||||
return true;
|
||||
@@ -380,6 +675,7 @@ namespace GeometryTD.CustomComponent
|
||||
_enemyManager.EndPhase();
|
||||
CleanupAllCombatEntities();
|
||||
CloseCombatFinishForm();
|
||||
CloseRewardSelectForm();
|
||||
ResetRuntime();
|
||||
return false;
|
||||
}
|
||||
@@ -397,6 +693,7 @@ namespace GeometryTD.CustomComponent
|
||||
_enemyManager.EndPhase();
|
||||
CleanupAllCombatEntities();
|
||||
CloseCombatFinishForm();
|
||||
CloseRewardSelectForm();
|
||||
GameEntry.CombatNode?.OnCombatEndedByScheduler(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using GameFramework.Event;
|
||||
using GeometryTD.DataTable;
|
||||
using GeometryTD.Definition;
|
||||
using GeometryTD.Entity;
|
||||
using GeometryTD.Entity.EntityData;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -171,6 +171,38 @@ namespace GeometryTD.CustomComponent
|
||||
_inventory.Gold += resolvedGain;
|
||||
}
|
||||
|
||||
public int ReduceAllTowerEndurance(float enduranceLoss)
|
||||
{
|
||||
EnsureInitialized();
|
||||
float resolvedLoss = Mathf.Max(0f, enduranceLoss);
|
||||
if (resolvedLoss <= 0f || _inventory.Towers == null || _inventory.Towers.Count <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int affectedCount = 0;
|
||||
for (int i = 0; i < _inventory.Towers.Count; i++)
|
||||
{
|
||||
DefenseTowerItemData tower = _inventory.Towers[i];
|
||||
if (tower == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
float originalEndurance = tower.Endurance;
|
||||
float nextEndurance = Mathf.Clamp(originalEndurance - resolvedLoss, 0f, 100f);
|
||||
if (nextEndurance >= originalEndurance)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
tower.Endurance = nextEndurance;
|
||||
affectedCount++;
|
||||
}
|
||||
|
||||
return affectedCount;
|
||||
}
|
||||
|
||||
private void EnsureInitialized()
|
||||
{
|
||||
if (_initialized)
|
||||
|
||||
@@ -31,12 +31,22 @@
|
||||
/// 主界面。
|
||||
/// </summary>
|
||||
MainForm = 110,
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 仓库界面。
|
||||
/// </summary>
|
||||
RepoForm = 111,
|
||||
|
||||
/// <summary>
|
||||
/// 道具详细信息界面。
|
||||
/// </summary>
|
||||
ItemDescForm = 112,
|
||||
|
||||
/// <summary>
|
||||
/// 奖励三选一界面。
|
||||
/// </summary>
|
||||
RewardSelectForm = 113,
|
||||
|
||||
/// <summary>
|
||||
/// 事件节点界面。
|
||||
/// </summary>
|
||||
@@ -51,15 +61,15 @@
|
||||
/// 战斗结算界面。
|
||||
/// </summary>
|
||||
CombatFinishForm = 141,
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 战斗选择界面
|
||||
/// </summary>
|
||||
CombatSelectForm = 142,
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 测试菜单。
|
||||
/// </summary>
|
||||
TestMenuForm = 200,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 775ed8967a23327419c664150ce1fad3
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,21 @@
|
||||
using GameFramework;
|
||||
using GameFramework.Event;
|
||||
|
||||
namespace GeometryTD.CustomEvent
|
||||
{
|
||||
public class RewardSelectGiveUpEventArgs : GameEventArgs
|
||||
{
|
||||
public static int EventId => typeof(RewardSelectGiveUpEventArgs).GetHashCode();
|
||||
|
||||
public override int Id => EventId;
|
||||
|
||||
public static RewardSelectGiveUpEventArgs Create()
|
||||
{
|
||||
return ReferencePool.Acquire<RewardSelectGiveUpEventArgs>();
|
||||
}
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 36ac796e2a3b8054fb6c8a62fa83a961
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,26 @@
|
||||
using GameFramework;
|
||||
using GameFramework.Event;
|
||||
|
||||
namespace GeometryTD.CustomEvent
|
||||
{
|
||||
public class RewardSelectItemSelectedEventArgs : GameEventArgs
|
||||
{
|
||||
public static int EventId => typeof(RewardSelectItemSelectedEventArgs).GetHashCode();
|
||||
|
||||
public override int Id => EventId;
|
||||
|
||||
public int SelectedIndex { get; private set; } = -1;
|
||||
|
||||
public static RewardSelectItemSelectedEventArgs Create(int selectedIndex)
|
||||
{
|
||||
RewardSelectItemSelectedEventArgs args = ReferencePool.Acquire<RewardSelectItemSelectedEventArgs>();
|
||||
args.SelectedIndex = selectedIndex;
|
||||
return args;
|
||||
}
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
SelectedIndex = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 926df4c8b4422c84496ee81b0a69963c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,21 @@
|
||||
using GameFramework;
|
||||
using GameFramework.Event;
|
||||
|
||||
namespace GeometryTD.CustomEvent
|
||||
{
|
||||
public class RewardSelectRefreshEventArgs : GameEventArgs
|
||||
{
|
||||
public static int EventId => typeof(RewardSelectRefreshEventArgs).GetHashCode();
|
||||
|
||||
public override int Id => EventId;
|
||||
|
||||
public static RewardSelectRefreshEventArgs Create()
|
||||
{
|
||||
return ReferencePool.Acquire<RewardSelectRefreshEventArgs>();
|
||||
}
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b3e2fd7e3e22b2d4399f90e7d5064f50
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -111,7 +111,8 @@ namespace GeometryTD.UI
|
||||
{
|
||||
Title = $"[Tower] {tower.Name} ({tower.Rarity})",
|
||||
InstanceId = tower.InstanceId,
|
||||
ComponentSlotType = TowerCompSlotType.None
|
||||
ComponentSlotType = TowerCompSlotType.None,
|
||||
IconAreaContext = BuildIconAreaContext(tower.Rarity)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -130,7 +131,8 @@ namespace GeometryTD.UI
|
||||
{
|
||||
Title = $"[Muzzle] {item.Name} ({item.Rarity})",
|
||||
InstanceId = item.InstanceId,
|
||||
ComponentSlotType = TowerCompSlotType.Muzzle
|
||||
ComponentSlotType = TowerCompSlotType.Muzzle,
|
||||
IconAreaContext = BuildIconAreaContext(item.Rarity)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -149,7 +151,8 @@ namespace GeometryTD.UI
|
||||
{
|
||||
Title = $"[Bearing] {item.Name} ({item.Rarity})",
|
||||
InstanceId = item.InstanceId,
|
||||
ComponentSlotType = TowerCompSlotType.Bearing
|
||||
ComponentSlotType = TowerCompSlotType.Bearing,
|
||||
IconAreaContext = BuildIconAreaContext(item.Rarity)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -168,7 +171,8 @@ namespace GeometryTD.UI
|
||||
{
|
||||
Title = $"[Base] {item.Name} ({item.Rarity})",
|
||||
InstanceId = item.InstanceId,
|
||||
ComponentSlotType = TowerCompSlotType.Base
|
||||
ComponentSlotType = TowerCompSlotType.Base,
|
||||
IconAreaContext = BuildIconAreaContext(item.Rarity)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -176,6 +180,15 @@ namespace GeometryTD.UI
|
||||
return itemContexts.ToArray();
|
||||
}
|
||||
|
||||
private static IconAreaContext BuildIconAreaContext(RarityType rarity)
|
||||
{
|
||||
return new IconAreaContext
|
||||
{
|
||||
Rarity = rarity,
|
||||
Icon = null
|
||||
};
|
||||
}
|
||||
|
||||
private void OnCombatFinishReturnButtonClicked(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(sender is CombatFinishForm) || !(e is CombatFinishReturnEventArgs))
|
||||
|
||||
@@ -7,5 +7,6 @@ namespace GeometryTD.UI
|
||||
public string Title;
|
||||
public long InstanceId;
|
||||
public TowerCompSlotType ComponentSlotType;
|
||||
public IconAreaContext IconAreaContext;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +93,8 @@ namespace GeometryTD.UI
|
||||
{
|
||||
Title = $"[Tower] {tower.Name} ({tower.Rarity})",
|
||||
InstanceId = tower.InstanceId,
|
||||
ComponentSlotType = TowerCompSlotType.None
|
||||
ComponentSlotType = TowerCompSlotType.None,
|
||||
IconAreaContext = BuildIconAreaContext(tower.Rarity)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -106,7 +107,8 @@ namespace GeometryTD.UI
|
||||
{
|
||||
Title = $"[Muzzle] {item.Name} ({item.Rarity})",
|
||||
InstanceId = item.InstanceId,
|
||||
ComponentSlotType = TowerCompSlotType.Muzzle
|
||||
ComponentSlotType = TowerCompSlotType.Muzzle,
|
||||
IconAreaContext = BuildIconAreaContext(item.Rarity)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -119,7 +121,8 @@ namespace GeometryTD.UI
|
||||
{
|
||||
Title = $"[Bearing] {item.Name} ({item.Rarity})",
|
||||
InstanceId = item.InstanceId,
|
||||
ComponentSlotType = TowerCompSlotType.Bearing
|
||||
ComponentSlotType = TowerCompSlotType.Bearing,
|
||||
IconAreaContext = BuildIconAreaContext(item.Rarity)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -132,7 +135,8 @@ namespace GeometryTD.UI
|
||||
{
|
||||
Title = $"[Base] {item.Name} ({item.Rarity})",
|
||||
InstanceId = item.InstanceId,
|
||||
ComponentSlotType = TowerCompSlotType.Base
|
||||
ComponentSlotType = TowerCompSlotType.Base,
|
||||
IconAreaContext = BuildIconAreaContext(item.Rarity)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -158,6 +162,15 @@ namespace GeometryTD.UI
|
||||
_itemContextMap[itemContext.InstanceId] = itemContext;
|
||||
}
|
||||
|
||||
private static IconAreaContext BuildIconAreaContext(RarityType rarity)
|
||||
{
|
||||
return new IconAreaContext
|
||||
{
|
||||
Rarity = rarity,
|
||||
Icon = null
|
||||
};
|
||||
}
|
||||
|
||||
private void OnRepoItemSelected(object sender, GameEventArgs e)
|
||||
{
|
||||
if (!(e is RepoItemSelectedEventArgs args))
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Collections.Generic;
|
||||
using GeometryTD.Definition;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
|
||||
@@ -2,7 +2,6 @@ using GeometryTD.CustomEvent;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
@@ -10,6 +9,7 @@ namespace GeometryTD.UI
|
||||
{
|
||||
[SerializeField] private TMP_Text _titleText;
|
||||
[SerializeField] private Image _highlightImage;
|
||||
[SerializeField] private IconArea _iconArea;
|
||||
|
||||
private static readonly Color NormalColor = new Color32(40, 40, 40, 180);
|
||||
private static readonly Color SelectedColor = new Color32(255, 216, 102, 255);
|
||||
@@ -31,6 +31,8 @@ namespace GeometryTD.UI
|
||||
_titleText.text = context.Title ?? string.Empty;
|
||||
}
|
||||
|
||||
_iconArea.OnInit(context.IconAreaContext);
|
||||
|
||||
SetSelected(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using GeometryTD.Definition;
|
||||
using GeometryTD.UI;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class IconAreaContext : UIContext
|
||||
{
|
||||
public RarityType Rarity;
|
||||
public Sprite Icon;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c5d39fc6e1ad1fe4ba9d2454eb9a0cb3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class RewardItemContext : UIContext
|
||||
{
|
||||
public int Index;
|
||||
public long RewardId;
|
||||
public IconAreaContext IconArea;
|
||||
public string Title;
|
||||
public string TypeText;
|
||||
public string Description;
|
||||
public TagItemContext[] Tags;
|
||||
public bool IsSelectable;
|
||||
public object UserData;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8ab52d2f24634cd47bd8344b69dc6ee9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f4ec685dcc5a07458a149792aa422f1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 83fe4616261e1614e8fc6c049cbe4606
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,290 @@
|
||||
using System;
|
||||
using GeometryTD.CustomEvent;
|
||||
using GeometryTD.DataTable;
|
||||
using GeometryTD.Definition;
|
||||
using GameFramework.Event;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public 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 static RewardSelectFormContext BuildContext(RewardSelectFormRawData rawData)
|
||||
{
|
||||
if (rawData == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new RewardSelectFormContext
|
||||
{
|
||||
TipText = string.IsNullOrWhiteSpace(rawData.TipText) ? "Select one reward" : rawData.TipText,
|
||||
RefreshButtonText = BuildRefreshButtonText(rawData.RefreshCost, rawData.CanRefresh),
|
||||
CanRefresh = rawData.CanRefresh,
|
||||
CanGiveUp = rawData.CanGiveUp,
|
||||
RewardItems = BuildRewardItemContexts(rawData.RewardItems)
|
||||
};
|
||||
}
|
||||
|
||||
private static string BuildRefreshButtonText(int refreshCost, bool canRefresh)
|
||||
{
|
||||
if (!canRefresh)
|
||||
{
|
||||
return "Refreshed";
|
||||
}
|
||||
|
||||
if (refreshCost <= 0)
|
||||
{
|
||||
return "Refresh";
|
||||
}
|
||||
|
||||
return $"Refresh -{refreshCost}";
|
||||
}
|
||||
|
||||
private static RewardItemContext[] BuildRewardItemContexts(RewardSelectItemRawData[] rawItems)
|
||||
{
|
||||
if (rawItems == null || rawItems.Length <= 0)
|
||||
{
|
||||
return System.Array.Empty<RewardItemContext>();
|
||||
}
|
||||
|
||||
RewardItemContext[] contexts = new RewardItemContext[rawItems.Length];
|
||||
for (int i = 0; i < rawItems.Length; i++)
|
||||
{
|
||||
RewardSelectItemRawData rawItem = rawItems[i];
|
||||
if (rawItem == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
contexts[i] = new RewardItemContext
|
||||
{
|
||||
Index = i,
|
||||
RewardId = rawItem.RewardId,
|
||||
IconArea = new IconAreaContext
|
||||
{
|
||||
Icon = rawItem.Icon,
|
||||
Rarity = rawItem.Rarity
|
||||
},
|
||||
Title = rawItem.Title ?? string.Empty,
|
||||
TypeText = ResolveTypeText(rawItem),
|
||||
Description = rawItem.Description ?? string.Empty,
|
||||
Tags = BuildTagContexts(rawItem.Tags),
|
||||
IsSelectable = rawItem.IsSelectable,
|
||||
UserData = rawItem.UserData
|
||||
};
|
||||
}
|
||||
|
||||
return contexts;
|
||||
}
|
||||
|
||||
private static string ResolveTypeText(RewardSelectItemRawData rawItem)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(rawItem.TypeText))
|
||||
{
|
||||
return rawItem.TypeText;
|
||||
}
|
||||
|
||||
return rawItem.SlotType switch
|
||||
{
|
||||
TowerCompSlotType.Muzzle => "Muzzle Component",
|
||||
TowerCompSlotType.Bearing => "Bearing Component",
|
||||
TowerCompSlotType.Base => "Base Component",
|
||||
TowerCompSlotType.Accessory => "Accessory",
|
||||
_ => "Component"
|
||||
};
|
||||
}
|
||||
|
||||
private static TagItemContext[] BuildTagContexts(TagType[] tags)
|
||||
{
|
||||
if (tags == null || tags.Length <= 0)
|
||||
{
|
||||
return System.Array.Empty<TagItemContext>();
|
||||
}
|
||||
|
||||
TagItemContext[] contexts = new TagItemContext[tags.Length];
|
||||
for (int i = 0; i < tags.Length; i++)
|
||||
{
|
||||
TagType tagType = tags[i];
|
||||
contexts[i] = new TagItemContext
|
||||
{
|
||||
TagName = ResolveTagName(tagType)
|
||||
};
|
||||
}
|
||||
|
||||
return contexts;
|
||||
}
|
||||
|
||||
private static string ResolveTagName(TagType tagType)
|
||||
{
|
||||
if (tagType == TagType.None)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
var tagTable = GameEntry.DataTable.GetDataTable<DRTag>();
|
||||
if (tagTable != null)
|
||||
{
|
||||
DRTag tagRow = tagTable.GetDataRow((int)tagType);
|
||||
if (tagRow != null && !string.IsNullOrWhiteSpace(tagRow.Name))
|
||||
{
|
||||
return tagRow.Name;
|
||||
}
|
||||
}
|
||||
|
||||
return tagType.ToString();
|
||||
}
|
||||
|
||||
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.TryRefresh();
|
||||
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,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8d4d6617e96d6984dac4f279d4f194e5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 642bc21ab1e2948468912ffa6e051a6a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,20 @@
|
||||
using GeometryTD.Definition;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GeometryTD.UI
|
||||
{
|
||||
public class RewardSelectItemRawData
|
||||
{
|
||||
public long RewardId;
|
||||
public TowerCompSlotType SlotType;
|
||||
public string Title;
|
||||
public string TypeText;
|
||||
public string Description;
|
||||
public RarityType Rarity;
|
||||
public TagType[] Tags;
|
||||
public Sprite Icon;
|
||||
public bool IsSelectable = true;
|
||||
public TowerCompItemData SourceItem;
|
||||
public object UserData;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d00c968c551de7b4a8d8841cf5e98d66
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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:
|
||||
@@ -0,0 +1,53 @@
|
||||
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 void OnInit(IconAreaContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
Log.Error("Icon area need IconAreaContext.");
|
||||
return;
|
||||
}
|
||||
|
||||
_context = context;
|
||||
SetIcon(_context.Icon);
|
||||
SetRarity(_context.Rarity);
|
||||
}
|
||||
|
||||
public void SetIcon(Sprite sprite)
|
||||
{
|
||||
_icon.sprite = sprite;
|
||||
}
|
||||
|
||||
public void SetRarity(RarityType rarity)
|
||||
{
|
||||
_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 void OnReset()
|
||||
{
|
||||
SetIcon(null);
|
||||
SetRarity(RarityType.None);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7ed74147813b96e4e95b5418f21999b7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,171 @@
|
||||
using System.Collections.Generic;
|
||||
using GeometryTD.CustomEvent;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
ClearTags();
|
||||
}
|
||||
|
||||
public void OnClick()
|
||||
{
|
||||
if (_context == null || !_context.IsSelectable)
|
||||
{
|
||||
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 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,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 60b499ea8a70a7f489bbd5bf0cec5768
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0beb75e5722a1db47af08437f98a0e63
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -1,32 +0,0 @@
|
||||
// using TMPro;
|
||||
// using UnityEngine;
|
||||
// using UnityEngine.UI;
|
||||
//
|
||||
// namespace UI
|
||||
// {
|
||||
// public class ShopGoodsItem : MonoBehaviour
|
||||
// {
|
||||
// [SerializeField] private IconArea _iconArea;
|
||||
//
|
||||
// [SerializeField] private TMP_Text _titleText;
|
||||
//
|
||||
// [SerializeField] private TMP_Text _typeText;
|
||||
//
|
||||
// [SerializeField] private TMP_Text _descriptionText;
|
||||
//
|
||||
// [SerializeField] private TMP_Text _costText;
|
||||
//
|
||||
// #region Init
|
||||
//
|
||||
// public void Init(GoodsItemContext data)
|
||||
// {
|
||||
// _iconArea.OnInit(data.Icon, data.Rarity);
|
||||
// _titleText.text = data.Title;
|
||||
// _typeText.text = data.Type;
|
||||
// _descriptionText.text = data.Description;
|
||||
// _costText.text = $"{data.Price} <sprite name=\"coin\" index=0>";
|
||||
// }
|
||||
//
|
||||
// #endregion
|
||||
// }
|
||||
// }
|
||||
Reference in New Issue
Block a user