G3 finish
This commit is contained in:
parent
53b6b261dd
commit
26aaa945a9
|
|
@ -11,10 +11,8 @@ namespace GeometryTD.CustomComponent
|
|||
public sealed class CombatSettlementService
|
||||
{
|
||||
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 SettlementTowerEnduranceLoss = 1f;
|
||||
private readonly CombatSettlementCalculator _calculator = new();
|
||||
private readonly CombatSettlementCommitter _committer = new();
|
||||
|
||||
public CombatSettlementContext BuildSettlementContext(
|
||||
bool didCombatWin,
|
||||
|
|
@ -22,63 +20,16 @@ namespace GeometryTD.CustomComponent
|
|||
int defeatedEnemyCount,
|
||||
CombatRunResourceStore resourceStore)
|
||||
{
|
||||
bool shouldOpenFullBaseHpRewardSelect = false;
|
||||
ResolveSettlementByBaseHp(
|
||||
return _calculator.BuildSettlementContext(
|
||||
didCombatWin,
|
||||
currentLevel,
|
||||
resourceStore,
|
||||
out int currentBaseHp,
|
||||
out int maxBaseHp,
|
||||
out int levelRewardGold,
|
||||
out float bonusRate,
|
||||
out int bonusGold,
|
||||
out shouldOpenFullBaseHpRewardSelect);
|
||||
|
||||
CombatSettlementContext settlementContext = new CombatSettlementContext
|
||||
{
|
||||
};
|
||||
settlementContext.Result.DidCombatWin = didCombatWin;
|
||||
settlementContext.Result.FinalCoin = Mathf.Max(0, resourceStore != null ? resourceStore.CurrentCoin : 0);
|
||||
settlementContext.Result.FinalBaseHp = currentBaseHp;
|
||||
settlementContext.Result.MaxBaseHp = maxBaseHp;
|
||||
settlementContext.Result.DefeatedEnemyCount = Mathf.Max(0, defeatedEnemyCount);
|
||||
settlementContext.Result.GainedGold = Mathf.Max(0, resourceStore != null ? resourceStore.GainedGold : 0);
|
||||
settlementContext.Result.RewardInventory = resourceStore != null
|
||||
? resourceStore.GetRewardInventorySnapshot()
|
||||
: new BackpackInventoryData();
|
||||
PopulateEnduranceSettlement(settlementContext, resourceStore);
|
||||
settlementContext.Flags.ShouldOpenRewardSelection = shouldOpenFullBaseHpRewardSelect;
|
||||
settlementContext.Flags.DidEnterRewardSelection = false;
|
||||
settlementContext.Summary.DefeatedEnemyCount = settlementContext.Result.DefeatedEnemyCount;
|
||||
settlementContext.Summary.GainedGold = settlementContext.Result.GainedGold;
|
||||
settlementContext.Summary.RewardInventory = settlementContext.Result.RewardInventory;
|
||||
|
||||
Log.Info(
|
||||
"Combat settlement resolved. Level={0}, BaseHp={1}/{2}, LevelReward={3}, BonusRate={4:P0}, BonusGold={5}, FullHpRewardSelect={6}, EnduranceTargets={7}.",
|
||||
currentLevel != null ? currentLevel.Id : 0,
|
||||
currentBaseHp,
|
||||
maxBaseHp,
|
||||
levelRewardGold,
|
||||
bonusRate,
|
||||
bonusGold,
|
||||
shouldOpenFullBaseHpRewardSelect,
|
||||
settlementContext.Result.Endurance.TargetTowerInstanceIds.Count);
|
||||
return settlementContext;
|
||||
defeatedEnemyCount,
|
||||
resourceStore);
|
||||
}
|
||||
|
||||
public void CommitSettlementInventory(CombatSettlementContext settlementContext)
|
||||
{
|
||||
if (settlementContext == null || settlementContext.Flags.IsCommitted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BackpackInventoryData rewardInventory = settlementContext.Result.RewardInventory ?? new BackpackInventoryData();
|
||||
GameEntry.PlayerInventory?.MergeInventory(rewardInventory);
|
||||
settlementContext.Result.RewardInventory = rewardInventory;
|
||||
settlementContext.Summary.RewardInventory = rewardInventory;
|
||||
settlementContext.Result.Endurance.AffectedTowerCount = ApplyDeferredSettlementEndurance(settlementContext);
|
||||
settlementContext.Flags.IsCommitted = true;
|
||||
_committer.CommitSettlementInventory(settlementContext);
|
||||
}
|
||||
|
||||
public bool TryPrepareRewardSelection(
|
||||
|
|
@ -104,26 +55,9 @@ namespace GeometryTD.CustomComponent
|
|||
return false;
|
||||
}
|
||||
|
||||
List<RewardSelectItemRawData> rewardPool = new List<RewardSelectItemRawData>(candidateItems.Count);
|
||||
foreach (var item in candidateItems)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
rewardPool.Add(BuildRewardSelectRawData(item));
|
||||
}
|
||||
|
||||
if (rewardPool.Count <= 0)
|
||||
{
|
||||
settlementContext.Flags.ShouldOpenRewardSelection = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
rewardSelectFormUseCase.SetCallbacks(onRewardSelected, onGiveUp);
|
||||
rewardSelectFormUseCase.ConfigureRewardPool(
|
||||
rewardPool,
|
||||
rewardSelectFormUseCase.ConfigureRewardCandidates(
|
||||
candidateItems,
|
||||
displayCount: RewardSelectDisplayCount,
|
||||
refreshCost: 0,
|
||||
allowRefreshOnce: false,
|
||||
|
|
@ -144,13 +78,7 @@ namespace GeometryTD.CustomComponent
|
|||
|
||||
public void ApplySelectedReward(CombatSettlementContext settlementContext, RewardSelectItemRawData selectedReward)
|
||||
{
|
||||
if (settlementContext?.Result.RewardInventory == null || selectedReward?.SourceItem == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TryAppendRewardComponent(settlementContext.Result.RewardInventory, selectedReward.SourceItem);
|
||||
settlementContext.Summary.RewardInventory = settlementContext.Result.RewardInventory;
|
||||
_committer.ApplySelectedReward(settlementContext, selectedReward);
|
||||
}
|
||||
|
||||
public void OpenCombatFinishForm(
|
||||
|
|
@ -166,134 +94,5 @@ namespace GeometryTD.CustomComponent
|
|||
GameEntry.UIRouter.OpenUI(UIFormType.CombatFinishForm);
|
||||
}
|
||||
|
||||
private static void ResolveSettlementByBaseHp(
|
||||
bool didCombatWin,
|
||||
DRLevel currentLevel,
|
||||
CombatRunResourceStore resourceStore,
|
||||
out int currentBaseHp,
|
||||
out int maxBaseHp,
|
||||
out int levelRewardGold,
|
||||
out float bonusRate,
|
||||
out int bonusGold,
|
||||
out bool shouldOpenFullBaseHpRewardSelect)
|
||||
{
|
||||
currentBaseHp = Mathf.Max(0, resourceStore != null ? resourceStore.CurrentBaseHp : 0);
|
||||
maxBaseHp = resourceStore != null ? Mathf.Max(0, resourceStore.MaxBaseHp) : 0;
|
||||
if (maxBaseHp > 0)
|
||||
{
|
||||
currentBaseHp = Mathf.Clamp(currentBaseHp, 0, maxBaseHp);
|
||||
}
|
||||
|
||||
levelRewardGold = currentLevel != null ? Mathf.Max(0, currentLevel.RewardGold) : 0;
|
||||
bonusRate = 0f;
|
||||
bonusGold = 0;
|
||||
shouldOpenFullBaseHpRewardSelect = false;
|
||||
|
||||
if (!didCombatWin || resourceStore == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (maxBaseHp > 0 && currentBaseHp >= maxBaseHp)
|
||||
{
|
||||
bonusRate = FullBaseHpGoldBonusRate;
|
||||
shouldOpenFullBaseHpRewardSelect = true;
|
||||
}
|
||||
else if (maxBaseHp > 0)
|
||||
{
|
||||
float hpRate = (float)currentBaseHp / maxBaseHp;
|
||||
if (hpRate >= HighBaseHpThreshold)
|
||||
{
|
||||
bonusRate = HighBaseHpGoldBonusRate;
|
||||
}
|
||||
}
|
||||
|
||||
int goldForBonusCalculation = Mathf.Max(0, resourceStore.GainedGold) + levelRewardGold;
|
||||
bonusGold = bonusRate > 0f ? Mathf.FloorToInt(goldForBonusCalculation * bonusRate) : 0;
|
||||
int settlementGold = levelRewardGold + bonusGold;
|
||||
resourceStore.AddSettlementGold(settlementGold);
|
||||
}
|
||||
|
||||
private static void PopulateEnduranceSettlement(
|
||||
CombatSettlementContext settlementContext,
|
||||
CombatRunResourceStore resourceStore)
|
||||
{
|
||||
if (settlementContext == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CombatSettlementEnduranceResult enduranceResult = settlementContext.Result.Endurance;
|
||||
enduranceResult.TargetTowerInstanceIds.Clear();
|
||||
enduranceResult.EnduranceLossPerComponent = SettlementTowerEnduranceLoss;
|
||||
|
||||
IReadOnlyList<long> participantTowerIds = resourceStore?.GetParticipantTowerInstanceIdSnapshot();
|
||||
if (participantTowerIds == null || participantTowerIds.Count <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < participantTowerIds.Count; i++)
|
||||
{
|
||||
long towerId = participantTowerIds[i];
|
||||
if (towerId > 0)
|
||||
{
|
||||
enduranceResult.TargetTowerInstanceIds.Add(towerId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int ApplyDeferredSettlementEndurance(CombatSettlementContext settlementContext)
|
||||
{
|
||||
if (settlementContext == null ||
|
||||
settlementContext.Result.Endurance.EnduranceLossPerComponent <= 0f ||
|
||||
settlementContext.Result.Endurance.TargetTowerInstanceIds.Count <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
PlayerInventoryComponent inventory = GameEntry.PlayerInventory;
|
||||
if (inventory == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return inventory.ReduceTowerEndurance(
|
||||
settlementContext.Result.Endurance.TargetTowerInstanceIds,
|
||||
settlementContext.Result.Endurance.EnduranceLossPerComponent);
|
||||
}
|
||||
|
||||
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 RewardSelectItemRawDataBuilder.Build(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,141 @@
|
|||
using System.Collections.Generic;
|
||||
using GeometryTD.DataTable;
|
||||
using GeometryTD.Definition;
|
||||
using UnityEngine;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace GeometryTD.CustomComponent
|
||||
{
|
||||
public sealed class CombatSettlementCalculator
|
||||
{
|
||||
private const float FullBaseHpGoldBonusRate = 0.3f;
|
||||
private const float HighBaseHpGoldBonusRate = 0.1f;
|
||||
private const float HighBaseHpThreshold = 0.8f;
|
||||
private const float SettlementTowerEnduranceLoss = 1f;
|
||||
|
||||
public CombatSettlementContext BuildSettlementContext(
|
||||
bool didCombatWin,
|
||||
DRLevel currentLevel,
|
||||
int defeatedEnemyCount,
|
||||
CombatRunResourceStore resourceStore)
|
||||
{
|
||||
bool shouldOpenFullBaseHpRewardSelect = false;
|
||||
ResolveSettlementByBaseHp(
|
||||
didCombatWin,
|
||||
currentLevel,
|
||||
resourceStore,
|
||||
out int currentBaseHp,
|
||||
out int maxBaseHp,
|
||||
out int levelRewardGold,
|
||||
out float bonusRate,
|
||||
out int bonusGold,
|
||||
out shouldOpenFullBaseHpRewardSelect);
|
||||
|
||||
CombatSettlementContext settlementContext = new CombatSettlementContext();
|
||||
settlementContext.Result.DidCombatWin = didCombatWin;
|
||||
settlementContext.Result.FinalCoin = Mathf.Max(0, resourceStore != null ? resourceStore.CurrentCoin : 0);
|
||||
settlementContext.Result.FinalBaseHp = currentBaseHp;
|
||||
settlementContext.Result.MaxBaseHp = maxBaseHp;
|
||||
settlementContext.Result.DefeatedEnemyCount = Mathf.Max(0, defeatedEnemyCount);
|
||||
settlementContext.Result.GainedGold = Mathf.Max(0, resourceStore != null ? resourceStore.GainedGold : 0);
|
||||
settlementContext.Result.RewardInventory = resourceStore != null
|
||||
? resourceStore.GetRewardInventorySnapshot()
|
||||
: new BackpackInventoryData();
|
||||
PopulateEnduranceSettlement(settlementContext, resourceStore);
|
||||
settlementContext.Flags.ShouldOpenRewardSelection = shouldOpenFullBaseHpRewardSelect;
|
||||
settlementContext.Flags.DidEnterRewardSelection = false;
|
||||
settlementContext.Summary.DefeatedEnemyCount = settlementContext.Result.DefeatedEnemyCount;
|
||||
settlementContext.Summary.GainedGold = settlementContext.Result.GainedGold;
|
||||
settlementContext.Summary.RewardInventory = settlementContext.Result.RewardInventory;
|
||||
|
||||
Log.Info(
|
||||
"Combat settlement resolved. Level={0}, BaseHp={1}/{2}, LevelReward={3}, BonusRate={4:P0}, BonusGold={5}, FullHpRewardSelect={6}, EnduranceTargets={7}.",
|
||||
currentLevel != null ? currentLevel.Id : 0,
|
||||
currentBaseHp,
|
||||
maxBaseHp,
|
||||
levelRewardGold,
|
||||
bonusRate,
|
||||
bonusGold,
|
||||
shouldOpenFullBaseHpRewardSelect,
|
||||
settlementContext.Result.Endurance.TargetTowerInstanceIds.Count);
|
||||
return settlementContext;
|
||||
}
|
||||
|
||||
private static void ResolveSettlementByBaseHp(
|
||||
bool didCombatWin,
|
||||
DRLevel currentLevel,
|
||||
CombatRunResourceStore resourceStore,
|
||||
out int currentBaseHp,
|
||||
out int maxBaseHp,
|
||||
out int levelRewardGold,
|
||||
out float bonusRate,
|
||||
out int bonusGold,
|
||||
out bool shouldOpenFullBaseHpRewardSelect)
|
||||
{
|
||||
currentBaseHp = Mathf.Max(0, resourceStore != null ? resourceStore.CurrentBaseHp : 0);
|
||||
maxBaseHp = resourceStore != null ? Mathf.Max(0, resourceStore.MaxBaseHp) : 0;
|
||||
if (maxBaseHp > 0)
|
||||
{
|
||||
currentBaseHp = Mathf.Clamp(currentBaseHp, 0, maxBaseHp);
|
||||
}
|
||||
|
||||
levelRewardGold = currentLevel != null ? Mathf.Max(0, currentLevel.RewardGold) : 0;
|
||||
bonusRate = 0f;
|
||||
bonusGold = 0;
|
||||
shouldOpenFullBaseHpRewardSelect = false;
|
||||
|
||||
if (!didCombatWin || resourceStore == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (maxBaseHp > 0 && currentBaseHp >= maxBaseHp)
|
||||
{
|
||||
bonusRate = FullBaseHpGoldBonusRate;
|
||||
shouldOpenFullBaseHpRewardSelect = true;
|
||||
}
|
||||
else if (maxBaseHp > 0)
|
||||
{
|
||||
float hpRate = (float)currentBaseHp / maxBaseHp;
|
||||
if (hpRate >= HighBaseHpThreshold)
|
||||
{
|
||||
bonusRate = HighBaseHpGoldBonusRate;
|
||||
}
|
||||
}
|
||||
|
||||
int goldForBonusCalculation = Mathf.Max(0, resourceStore.GainedGold) + levelRewardGold;
|
||||
bonusGold = bonusRate > 0f ? Mathf.FloorToInt(goldForBonusCalculation * bonusRate) : 0;
|
||||
int settlementGold = levelRewardGold + bonusGold;
|
||||
resourceStore.AddSettlementGold(settlementGold);
|
||||
}
|
||||
|
||||
private static void PopulateEnduranceSettlement(
|
||||
CombatSettlementContext settlementContext,
|
||||
CombatRunResourceStore resourceStore)
|
||||
{
|
||||
if (settlementContext == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CombatSettlementEnduranceResult enduranceResult = settlementContext.Result.Endurance;
|
||||
enduranceResult.TargetTowerInstanceIds.Clear();
|
||||
enduranceResult.EnduranceLossPerComponent = SettlementTowerEnduranceLoss;
|
||||
|
||||
IReadOnlyList<long> participantTowerIds = resourceStore?.GetParticipantTowerInstanceIdSnapshot();
|
||||
if (participantTowerIds == null || participantTowerIds.Count <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < participantTowerIds.Count; i++)
|
||||
{
|
||||
long towerId = participantTowerIds[i];
|
||||
if (towerId > 0)
|
||||
{
|
||||
enduranceResult.TargetTowerInstanceIds.Add(towerId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c8aa78b2f9ff4eb4bf83b8c4f652ce24
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
using GeometryTD.Definition;
|
||||
using GeometryTD.UI;
|
||||
using UnityGameFramework.Runtime;
|
||||
|
||||
namespace GeometryTD.CustomComponent
|
||||
{
|
||||
public sealed class CombatSettlementCommitter
|
||||
{
|
||||
public void CommitSettlementInventory(CombatSettlementContext settlementContext)
|
||||
{
|
||||
if (settlementContext == null || settlementContext.Flags.IsCommitted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BackpackInventoryData rewardInventory = settlementContext.Result.RewardInventory ?? new BackpackInventoryData();
|
||||
GameEntry.PlayerInventory?.MergeInventory(rewardInventory);
|
||||
settlementContext.Result.RewardInventory = rewardInventory;
|
||||
settlementContext.Summary.RewardInventory = rewardInventory;
|
||||
settlementContext.Result.Endurance.AffectedTowerCount = ApplyDeferredSettlementEndurance(settlementContext);
|
||||
settlementContext.Flags.IsCommitted = true;
|
||||
}
|
||||
|
||||
public void ApplySelectedReward(CombatSettlementContext settlementContext, RewardSelectItemRawData selectedReward)
|
||||
{
|
||||
if (settlementContext?.Result.RewardInventory == null || selectedReward?.SourceItem == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TryAppendRewardComponent(settlementContext.Result.RewardInventory, selectedReward.SourceItem);
|
||||
settlementContext.Summary.RewardInventory = settlementContext.Result.RewardInventory;
|
||||
}
|
||||
|
||||
private static int ApplyDeferredSettlementEndurance(CombatSettlementContext settlementContext)
|
||||
{
|
||||
if (settlementContext == null ||
|
||||
settlementContext.Result.Endurance.EnduranceLossPerComponent <= 0f ||
|
||||
settlementContext.Result.Endurance.TargetTowerInstanceIds.Count <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
PlayerInventoryComponent inventory = GameEntry.PlayerInventory;
|
||||
if (inventory == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return inventory.ReduceTowerEndurance(
|
||||
settlementContext.Result.Endurance.TargetTowerInstanceIds,
|
||||
settlementContext.Result.Endurance.EnduranceLossPerComponent);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1e4e69cf76f34e5baec16562d765fb85
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using GeometryTD.Definition;
|
||||
using UnityEngine;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
|
|
@ -51,6 +52,37 @@ namespace GeometryTD.UI
|
|||
_currentModel = null;
|
||||
}
|
||||
|
||||
public void ConfigureRewardCandidates(
|
||||
IReadOnlyList<TowerCompItemData> rewardCandidates,
|
||||
int displayCount = 3,
|
||||
int refreshCost = 0,
|
||||
bool allowRefreshOnce = 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);
|
||||
_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;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# CodeX TODO
|
||||
|
||||
最后更新:2026-03-12
|
||||
最后更新:2026-03-13
|
||||
|
||||
> 目标:把当前“随机组件产出 / 掉落 / 商店 / 3 选 1 奖励候选 / Tag 初始化入口”从分散实现收口成清晰模块。
|
||||
> 原则:先收运行时入口,再收重复领域逻辑,最后收 UI 编排;不把纯规则层误做成 `GameFrameworkComponent`。
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
## 这次重构要解决的问题
|
||||
|
||||
- 商店、敌人掉落、满血 3 选 1 候选都在各自生成组件实例,逻辑分散,后续改规则要改多处。
|
||||
- `ShopFormUseCase`、`EnemyDropResolver`、`CombatSettlementService` 都同时承担了“流程 + 规则 + 数据组装”的混合职责。
|
||||
- `ShopFormUseCase`、旧 `EnemyDropResolver`、`CombatSettlementService` 都曾同时承担“流程 + 规则 + 数据组装”的混合职责。
|
||||
- `runSeed` 目前只稳定了 Tag 生成,没有稳定整个组件产出流程。
|
||||
- Tag 模块本身已经分成 `Generation / Aggregation / Combat / Metadata / Presentation`,但初始化入口还散在流程代码里。
|
||||
|
||||
|
|
@ -47,22 +47,22 @@
|
|||
|
||||
### 组件层
|
||||
|
||||
- `Assets/GameMain/Scripts/CustomComponent/InventoryGenerationComponent.cs`
|
||||
- `Assets/GameMain/Scripts/CustomComponent/TagRegistryComponent.cs`
|
||||
- `Assets/GameMain/Scripts/CustomComponent/InventoryGeneration/InventoryGenerationComponent.cs`
|
||||
- `Assets/GameMain/Scripts/CustomComponent/TagRegistry/TagRegistryComponent.cs`
|
||||
|
||||
### 领域模块层
|
||||
|
||||
- `Assets/GameMain/Scripts/Definition/InventoryGeneration/ComponentItemFactory.cs`
|
||||
- `Assets/GameMain/Scripts/Factory/ComponentItemFactory.cs`
|
||||
- 唯一负责“从配置行构造 `TowerCompItemData`”。
|
||||
- `Assets/GameMain/Scripts/Definition/InventoryGeneration/ShopGoodsBuilder.cs`
|
||||
- `Assets/GameMain/Scripts/CustomComponent/InventoryGeneration/ShopGoodsBuilder.cs`
|
||||
- 只负责商店货物生成。
|
||||
- `Assets/GameMain/Scripts/Definition/InventoryGeneration/DropPoolRoller.cs`
|
||||
- `Assets/GameMain/Scripts/CustomComponent/InventoryGeneration/DropPoolRoller.cs`
|
||||
- 只负责掉落池筛选、权重抽样、稀有度曲线。
|
||||
- `Assets/GameMain/Scripts/Definition/InventoryGeneration/RewardCandidateBuilder.cs`
|
||||
- `Assets/GameMain/Scripts/CustomComponent/InventoryGeneration/RewardCandidateBuilder.cs`
|
||||
- 只负责 3 选 1 奖励候选生成。
|
||||
- `Assets/GameMain/Scripts/Definition/Combat/Settlement/CombatSettlementCalculator.cs`
|
||||
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatSettlementCalculator.cs`
|
||||
- 只负责结算计算。
|
||||
- `Assets/GameMain/Scripts/Definition/Combat/Settlement/CombatSettlementCommitter.cs`
|
||||
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatSettlementCommitter.cs`
|
||||
- 只负责把结算结果提交到玩家库存。
|
||||
|
||||
## 现有职责迁移目标
|
||||
|
|
@ -81,9 +81,9 @@
|
|||
- `CombatScheduler`
|
||||
- 不再直接持有复杂掉落生成细节。
|
||||
- 只在敌人死亡时调用 `GameEntry.InventoryGeneration.ResolveEnemyDrop(...)`。
|
||||
- `EnemyDropResolver`
|
||||
- 逐步被拆空或移除。
|
||||
- 其内部职责拆给:
|
||||
- 旧 `EnemyDropResolver`
|
||||
- 已从主链路移除,并已删除旧实现文件。
|
||||
- 其职责已拆给:
|
||||
- `DropPoolRoller`
|
||||
- `ComponentItemFactory`
|
||||
- `RewardCandidateBuilder`
|
||||
|
|
@ -126,25 +126,26 @@
|
|||
|
||||
| 状态 | ID | 任务 | 目标 |
|
||||
|-----|-------|----------------------------------------|---------------|
|
||||
| [ ] | G2-01 | 新增 `DropPoolRoller` | 收口掉落池与稀有度抽样 |
|
||||
| [ ] | G2-02 | 新增 `RewardCandidateBuilder` | 收口 3 选 1 候选生成 |
|
||||
| [ ] | G2-03 | 让战斗掉落改走 `InventoryGenerationComponent` | 战斗不再自己构造组件 |
|
||||
| [ ] | G2-04 | 让满血奖励候选改走统一入口 | 掉落与奖励候选共用产出体系 |
|
||||
| [x] | G2-01 | 新增 `DropPoolRoller` | 收口掉落池与稀有度抽样 |
|
||||
| [x] | G2-02 | 新增 `RewardCandidateBuilder` | 收口 3 选 1 候选生成 |
|
||||
| [x] | G2-03 | 让战斗掉落改走 `InventoryGenerationComponent` | 战斗不再自己构造组件 |
|
||||
| [x] | G2-04 | 让满血奖励候选改走统一入口 | 掉落与奖励候选共用产出体系 |
|
||||
|
||||
### G2 验收标准
|
||||
|
||||
- 敌人死亡后仍能正常掉 coin、gold、组件。
|
||||
- 满血奖励 3 选 1 仍能正常打开并选择。
|
||||
- 掉落与奖励候选不再重复维护组件实例构造逻辑。
|
||||
- `CombatScheduler` 与 `CombatSettlementService` 已改为统一调用 `GameEntry.InventoryGeneration`。
|
||||
|
||||
## 阶段 G3 - 收口结算模块边界
|
||||
|
||||
| 状态 | ID | 任务 | 目标 |
|
||||
|-----|-------|---------------------------------|-----------------|
|
||||
| [ ] | G3-01 | 新增 `CombatSettlementCalculator` | 只保留结算计算 |
|
||||
| [ ] | G3-02 | 新增 `CombatSettlementCommitter` | 只保留库存提交 |
|
||||
| [ ] | G3-03 | 精简 `CombatSettlementService` | 只剩流程编排 |
|
||||
| [ ] | G3-04 | 清理奖励 RawData 重复映射 | 候选生成与 UI 组装边界清晰 |
|
||||
| [x] | G3-01 | 新增 `CombatSettlementCalculator` | 只保留结算计算 |
|
||||
| [x] | G3-02 | 新增 `CombatSettlementCommitter` | 只保留库存提交 |
|
||||
| [x] | G3-03 | 精简 `CombatSettlementService` | 只剩流程编排 |
|
||||
| [x] | G3-04 | 清理奖励 RawData 重复映射 | 候选生成与 UI 组装边界清晰 |
|
||||
|
||||
### G3 验收标准
|
||||
|
||||
|
|
@ -185,21 +186,21 @@
|
|||
### 第一批直接改动文件
|
||||
|
||||
- `Assets/GameMain/Scripts/UI/Shop/UseCase/ShopFormUseCase.cs`
|
||||
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/EnemyDrop/EnemyDropResolver.cs`
|
||||
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatSettlementService.cs`
|
||||
- `Assets/GameMain/Scripts/Procedure/Base/ProcedurePreload.cs`
|
||||
- `Assets/GameMain/Scripts/Base/GameEntry.Custom.cs`
|
||||
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatScheduler/CombatScheduler.cs`
|
||||
|
||||
### 第二批新增文件
|
||||
|
||||
- `Assets/GameMain/Scripts/CustomComponent/InventoryGenerationComponent.cs`
|
||||
- `Assets/GameMain/Scripts/CustomComponent/TagRegistryComponent.cs`
|
||||
- `Assets/GameMain/Scripts/CustomComponent/InventoryGeneration/InventoryGenerationComponent.cs`
|
||||
- `Assets/GameMain/Scripts/CustomComponent/TagRegistry/TagRegistryComponent.cs`
|
||||
- `Assets/GameMain/Scripts/Factory/ComponentItemFactory.cs`
|
||||
- `Assets/GameMain/Scripts/CustomComponent/ShopGoodsBuilder.cs`
|
||||
- `Assets/GameMain/Scripts/CustomComponent/DropPoolRoller.cs`
|
||||
- `Assets/GameMain/Scripts/CustomComponent/RewardCandidateBuilder.cs`
|
||||
- `Assets/GameMain/Scripts/Definition/Combat/Settlement/CombatSettlementCalculator.cs`
|
||||
- `Assets/GameMain/Scripts/Definition/Combat/Settlement/CombatSettlementCommitter.cs`
|
||||
- `Assets/GameMain/Scripts/CustomComponent/InventoryGeneration/ShopGoodsBuilder.cs`
|
||||
- `Assets/GameMain/Scripts/CustomComponent/InventoryGeneration/DropPoolRoller.cs`
|
||||
- `Assets/GameMain/Scripts/CustomComponent/InventoryGeneration/RewardCandidateBuilder.cs`
|
||||
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatSettlementCalculator.cs`
|
||||
- `Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatSettlementCommitter.cs`
|
||||
|
||||
## 本次重构的限制
|
||||
|
||||
|
|
|
|||
|
|
@ -219,6 +219,7 @@
|
|||
- `Manager`:只用于子域 Facade/聚合入口,例如 `EnemyManager`。
|
||||
- `Coordinator`:只用于跨状态、跨服务的流程编排,不持有独立业务真值。
|
||||
- `Service`:只用于聚焦业务行为,不承担框架事件桥接或异步句柄跟踪。
|
||||
- `Calculator`:只用于纯计算与结果组装,不直接提交状态或驱动 UI。
|
||||
- `Session`:只用于一次加载/交互过程的生命周期对象。
|
||||
- `Bridge`:只用于框架边界适配器。
|
||||
- `Runtime`:只用于运行时可变状态承载。
|
||||
|
|
@ -316,7 +317,32 @@
|
|||
- `CombatScheduler` 与结算状态链只调用统一入口,不直接访问掉落池滚动或组件实例构造细节。
|
||||
- `InventoryGenerationComponent` 负责组件实例生成、Tag 随机上下文以及 `runSeed/sequenceIndex` 相关上下文。
|
||||
|
||||
### 4.6 IPhaseEndCondition
|
||||
### 4.6 CombatSettlementCalculator
|
||||
|
||||
文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatSettlementCalculator.cs`
|
||||
|
||||
目标职责:
|
||||
- 只负责结算计算与 `CombatSettlementContext` 组装。
|
||||
- 负责基地血量奖励、奖励选择准入、奖励背包快照与耐久扣减目标计算。
|
||||
|
||||
约束:
|
||||
- 不直接并包到玩家库存。
|
||||
- 不直接打开 UI。
|
||||
- 不承担奖励候选生成。
|
||||
|
||||
### 4.7 CombatSettlementCommitter
|
||||
|
||||
文件:`Assets/GameMain/Scripts/CustomComponent/CombatNode/CombatSettlementCommitter.cs`
|
||||
|
||||
目标职责:
|
||||
- 只负责把结算结果提交到玩家库存。
|
||||
- 负责结算背包并包与延迟耐久扣减落地。
|
||||
|
||||
约束:
|
||||
- 不重新计算结算上下文。
|
||||
- 不直接生成奖励候选或打开 UI。
|
||||
|
||||
### 4.8 IPhaseEndCondition
|
||||
|
||||
目标职责:
|
||||
- 作为 `PhaseEndType` 判定接口。
|
||||
|
|
|
|||
Loading…
Reference in New Issue