S5-01 + S5-02 + S5-03
This commit is contained in:
+16
-1
@@ -7,7 +7,7 @@ using UnityEngine;
|
||||
|
||||
namespace GeometryTD.CustomComponent
|
||||
{
|
||||
internal sealed class CombatRunResourceStore
|
||||
public sealed class CombatRunResourceStore
|
||||
{
|
||||
private readonly List<TowerStatsData> _buildTowerStatsSnapshot = new();
|
||||
private readonly List<TowerItemData> _participantTowerSnapshot = new();
|
||||
@@ -88,6 +88,21 @@ namespace GeometryTD.CustomComponent
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
public IReadOnlyList<long> GetParticipantTowerInstanceIdSnapshot()
|
||||
{
|
||||
List<long> snapshot = new List<long>(_participantTowerSnapshot.Count);
|
||||
for (int i = 0; i < _participantTowerSnapshot.Count; i++)
|
||||
{
|
||||
TowerItemData tower = _participantTowerSnapshot[i];
|
||||
if (tower != null && tower.InstanceId > 0)
|
||||
{
|
||||
snapshot.Add(tower.InstanceId);
|
||||
}
|
||||
}
|
||||
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
public bool TryConsumeCoin(int coin)
|
||||
{
|
||||
int requiredCoin = Mathf.Max(0, coin);
|
||||
|
||||
+9
-8
@@ -1,22 +1,23 @@
|
||||
using System.Collections.Generic;
|
||||
using GeometryTD.Definition;
|
||||
|
||||
namespace GeometryTD.CustomComponent
|
||||
{
|
||||
internal sealed class CombatSettlementContext
|
||||
public sealed class CombatSettlementContext
|
||||
{
|
||||
public CombatSettlementFlags Flags { get; } = new();
|
||||
public CombatSettlementResult Result { get; } = new();
|
||||
public CombatSettlementSummary Summary { get; } = new();
|
||||
}
|
||||
|
||||
internal sealed class CombatSettlementFlags
|
||||
public sealed class CombatSettlementFlags
|
||||
{
|
||||
public bool ShouldOpenRewardSelection;
|
||||
public bool DidEnterRewardSelection;
|
||||
public bool IsCommitted;
|
||||
}
|
||||
|
||||
internal sealed class CombatSettlementResult
|
||||
public sealed class CombatSettlementResult
|
||||
{
|
||||
public bool DidCombatWin;
|
||||
public int FinalCoin;
|
||||
@@ -25,17 +26,17 @@ namespace GeometryTD.CustomComponent
|
||||
public int DefeatedEnemyCount;
|
||||
public int GainedGold;
|
||||
public BackpackInventoryData RewardInventory;
|
||||
public CombatSettlementPenaltyResult Penalty { get; } = new();
|
||||
public CombatSettlementEnduranceResult Endurance { get; } = new();
|
||||
}
|
||||
|
||||
internal sealed class CombatSettlementPenaltyResult
|
||||
public sealed class CombatSettlementEnduranceResult
|
||||
{
|
||||
public bool ShouldApplyLowBaseHpPenalty;
|
||||
public float LowBaseHpEndurancePenaltyValue;
|
||||
public List<long> TargetTowerInstanceIds { get; } = new();
|
||||
public float EnduranceLossPerComponent;
|
||||
public int AffectedTowerCount;
|
||||
}
|
||||
|
||||
internal sealed class CombatSettlementSummary
|
||||
public sealed class CombatSettlementSummary
|
||||
{
|
||||
public int DefeatedEnemyCount;
|
||||
public int GainedGold;
|
||||
|
||||
+41
-20
@@ -8,14 +8,13 @@ using UnityGameFramework.Runtime;
|
||||
|
||||
namespace GeometryTD.CustomComponent
|
||||
{
|
||||
internal sealed class CombatSettlementService
|
||||
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 MidBaseHpThreshold = 0.5f;
|
||||
private const float LowBaseHpTowerEndurancePenalty = 10f;
|
||||
private const float SettlementTowerEnduranceLoss = 1f;
|
||||
|
||||
public CombatSettlementContext BuildSettlementContext(
|
||||
bool didCombatWin,
|
||||
@@ -33,7 +32,6 @@ namespace GeometryTD.CustomComponent
|
||||
out int levelRewardGold,
|
||||
out float bonusRate,
|
||||
out int bonusGold,
|
||||
out bool appliedLowBaseHpPenalty,
|
||||
out shouldOpenFullBaseHpRewardSelect);
|
||||
|
||||
CombatSettlementContext settlementContext = new CombatSettlementContext
|
||||
@@ -48,9 +46,7 @@ namespace GeometryTD.CustomComponent
|
||||
settlementContext.Result.RewardInventory = resourceStore != null
|
||||
? resourceStore.GetRewardInventorySnapshot()
|
||||
: new BackpackInventoryData();
|
||||
settlementContext.Result.Penalty.ShouldApplyLowBaseHpPenalty = appliedLowBaseHpPenalty;
|
||||
settlementContext.Result.Penalty.LowBaseHpEndurancePenaltyValue =
|
||||
appliedLowBaseHpPenalty ? LowBaseHpTowerEndurancePenalty : 0f;
|
||||
PopulateEnduranceSettlement(settlementContext, resourceStore);
|
||||
settlementContext.Flags.ShouldOpenRewardSelection = shouldOpenFullBaseHpRewardSelect;
|
||||
settlementContext.Flags.DidEnterRewardSelection = false;
|
||||
settlementContext.Summary.DefeatedEnemyCount = settlementContext.Result.DefeatedEnemyCount;
|
||||
@@ -58,7 +54,7 @@ namespace GeometryTD.CustomComponent
|
||||
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}, LowHpPenalty={7}.",
|
||||
"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,
|
||||
@@ -66,7 +62,7 @@ namespace GeometryTD.CustomComponent
|
||||
bonusRate,
|
||||
bonusGold,
|
||||
shouldOpenFullBaseHpRewardSelect,
|
||||
appliedLowBaseHpPenalty);
|
||||
settlementContext.Result.Endurance.TargetTowerInstanceIds.Count);
|
||||
return settlementContext;
|
||||
}
|
||||
|
||||
@@ -81,7 +77,7 @@ namespace GeometryTD.CustomComponent
|
||||
GameEntry.PlayerInventory?.MergeInventory(rewardInventory);
|
||||
settlementContext.Result.RewardInventory = rewardInventory;
|
||||
settlementContext.Summary.RewardInventory = rewardInventory;
|
||||
settlementContext.Result.Penalty.AffectedTowerCount = ApplyDeferredSettlementPenalty(settlementContext);
|
||||
settlementContext.Result.Endurance.AffectedTowerCount = ApplyDeferredSettlementEndurance(settlementContext);
|
||||
settlementContext.Flags.IsCommitted = true;
|
||||
}
|
||||
|
||||
@@ -181,7 +177,6 @@ namespace GeometryTD.CustomComponent
|
||||
out int levelRewardGold,
|
||||
out float bonusRate,
|
||||
out int bonusGold,
|
||||
out bool appliedLowBaseHpPenalty,
|
||||
out bool shouldOpenFullBaseHpRewardSelect)
|
||||
{
|
||||
currentBaseHp = Mathf.Max(0, resourceStore != null ? resourceStore.CurrentBaseHp : 0);
|
||||
@@ -194,7 +189,6 @@ namespace GeometryTD.CustomComponent
|
||||
levelRewardGold = currentLevel != null ? Mathf.Max(0, currentLevel.RewardGold) : 0;
|
||||
bonusRate = 0f;
|
||||
bonusGold = 0;
|
||||
appliedLowBaseHpPenalty = false;
|
||||
shouldOpenFullBaseHpRewardSelect = false;
|
||||
|
||||
if (!didCombatWin || resourceStore == null)
|
||||
@@ -214,10 +208,6 @@ namespace GeometryTD.CustomComponent
|
||||
{
|
||||
bonusRate = HighBaseHpGoldBonusRate;
|
||||
}
|
||||
else if (hpRate < MidBaseHpThreshold)
|
||||
{
|
||||
appliedLowBaseHpPenalty = true;
|
||||
}
|
||||
}
|
||||
|
||||
int goldForBonusCalculation = Mathf.Max(0, resourceStore.GainedGold) + levelRewardGold;
|
||||
@@ -226,11 +216,40 @@ namespace GeometryTD.CustomComponent
|
||||
resourceStore.AddSettlementGold(settlementGold);
|
||||
}
|
||||
|
||||
private static int ApplyDeferredSettlementPenalty(CombatSettlementContext settlementContext)
|
||||
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.Penalty.ShouldApplyLowBaseHpPenalty ||
|
||||
settlementContext.Result.Penalty.LowBaseHpEndurancePenaltyValue <= 0f)
|
||||
settlementContext.Result.Endurance.EnduranceLossPerComponent <= 0f ||
|
||||
settlementContext.Result.Endurance.TargetTowerInstanceIds.Count <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -241,7 +260,9 @@ namespace GeometryTD.CustomComponent
|
||||
return 0;
|
||||
}
|
||||
|
||||
return inventory.ReduceAllTowerEndurance(settlementContext.Result.Penalty.LowBaseHpEndurancePenaltyValue);
|
||||
return inventory.ReduceTowerEndurance(
|
||||
settlementContext.Result.Endurance.TargetTowerInstanceIds,
|
||||
settlementContext.Result.Endurance.EnduranceLossPerComponent);
|
||||
}
|
||||
|
||||
private static bool TryAppendRewardComponent(BackpackInventoryData rewardInventory, TowerCompItemData selectedItem)
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ using GeometryTD.Definition;
|
||||
|
||||
namespace GeometryTD.CustomComponent
|
||||
{
|
||||
internal readonly struct EnemyDropContext
|
||||
public readonly struct EnemyDropContext
|
||||
{
|
||||
public EnemyDropContext(DREnemy enemy, int displayPhaseIndex, LevelThemeType themeType)
|
||||
{
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ using Random = UnityEngine.Random;
|
||||
|
||||
namespace GeometryTD.CustomComponent
|
||||
{
|
||||
internal sealed class EnemyDropResolver
|
||||
public sealed class EnemyDropResolver
|
||||
{
|
||||
private const float DropChanceBase = 0.05f;
|
||||
private const float DropChancePerPhase = 0.2f;
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ using GeometryTD.Definition;
|
||||
|
||||
namespace GeometryTD.CustomComponent
|
||||
{
|
||||
internal readonly struct EnemyDropResult
|
||||
public readonly struct EnemyDropResult
|
||||
{
|
||||
public static EnemyDropResult Empty => new(0, 0, null);
|
||||
|
||||
|
||||
@@ -132,10 +132,10 @@ namespace GeometryTD.CustomComponent
|
||||
out assembledTower);
|
||||
}
|
||||
|
||||
public int ReduceAllTowerEndurance(float enduranceLoss)
|
||||
public int ReduceTowerEndurance(IReadOnlyList<long> towerInstanceIds, float enduranceLoss)
|
||||
{
|
||||
EnsureInitialized();
|
||||
return _towerRosterService.ReduceAllTowerEndurance(enduranceLoss);
|
||||
return _towerRosterService.ReduceTowerEndurance(towerInstanceIds, enduranceLoss);
|
||||
}
|
||||
|
||||
private void EnsureInitialized()
|
||||
@@ -158,4 +158,3 @@ namespace GeometryTD.CustomComponent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -6,7 +6,7 @@ using UnityEngine;
|
||||
|
||||
namespace GeometryTD.CustomComponent
|
||||
{
|
||||
internal struct PlayerInventoryMergeSummary
|
||||
public struct PlayerInventoryMergeSummary
|
||||
{
|
||||
public int GainedGold;
|
||||
public int GainedMuzzleCount;
|
||||
@@ -19,14 +19,14 @@ namespace GeometryTD.CustomComponent
|
||||
GainedTowerCount > 0;
|
||||
}
|
||||
|
||||
internal sealed class PlayerInventoryState
|
||||
public sealed class PlayerInventoryState
|
||||
{
|
||||
public BackpackInventoryData Inventory = new BackpackInventoryData();
|
||||
public long NextInstanceId = 1;
|
||||
public bool IsInitialized;
|
||||
}
|
||||
|
||||
internal sealed class PlayerInventoryQueryModel
|
||||
public sealed class PlayerInventoryQueryModel
|
||||
{
|
||||
private readonly PlayerInventoryState _state;
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace GeometryTD.CustomComponent
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class PlayerInventoryCommandModel
|
||||
public sealed class PlayerInventoryCommandModel
|
||||
{
|
||||
private readonly PlayerInventoryState _state;
|
||||
|
||||
|
||||
+17
-6
@@ -5,7 +5,7 @@ using UnityEngine;
|
||||
|
||||
namespace GeometryTD.CustomComponent
|
||||
{
|
||||
internal sealed class PlayerInventoryTowerRosterService
|
||||
public sealed class PlayerInventoryTowerRosterService
|
||||
{
|
||||
private readonly PlayerInventoryQueryModel _queryModel;
|
||||
private readonly int _maxParticipantTowerCount;
|
||||
@@ -34,11 +34,15 @@ namespace GeometryTD.CustomComponent
|
||||
_maxParticipantTowerCount);
|
||||
}
|
||||
|
||||
public int ReduceAllTowerEndurance(float enduranceLoss)
|
||||
public int ReduceTowerEndurance(IReadOnlyList<long> towerInstanceIds, float enduranceLoss)
|
||||
{
|
||||
float resolvedLoss = Mathf.Max(0f, enduranceLoss);
|
||||
BackpackInventoryData inventory = _queryModel.Inventory;
|
||||
if (resolvedLoss <= 0f || inventory.Towers == null || inventory.Towers.Count <= 0)
|
||||
if (resolvedLoss <= 0f ||
|
||||
towerInstanceIds == null ||
|
||||
towerInstanceIds.Count <= 0 ||
|
||||
inventory.Towers == null ||
|
||||
inventory.Towers.Count <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -46,12 +50,19 @@ namespace GeometryTD.CustomComponent
|
||||
Dictionary<long, MuzzleCompItemData> muzzleMap = BuildComponentMap(inventory.MuzzleComponents);
|
||||
Dictionary<long, BearingCompItemData> bearingMap = BuildComponentMap(inventory.BearingComponents);
|
||||
Dictionary<long, BaseCompItemData> baseMap = BuildComponentMap(inventory.BaseComponents);
|
||||
HashSet<long> processedTowerIds = new HashSet<long>();
|
||||
|
||||
int affectedCount = 0;
|
||||
for (int i = 0; i < inventory.Towers.Count; i++)
|
||||
for (int i = 0; i < towerInstanceIds.Count; i++)
|
||||
{
|
||||
TowerItemData tower = inventory.Towers[i];
|
||||
if (tower == null)
|
||||
long towerInstanceId = towerInstanceIds[i];
|
||||
if (towerInstanceId <= 0 || !processedTowerIds.Add(towerInstanceId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!InventoryParticipantUtility.TryGetTowerById(inventory, towerInstanceId, out TowerItemData tower) ||
|
||||
tower == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,10 @@ namespace GeometryTD.Definition
|
||||
TowerMissing = 1,
|
||||
MissingMuzzleComponent = 2,
|
||||
MissingBearingComponent = 3,
|
||||
MissingBaseComponent = 4
|
||||
MissingBaseComponent = 4,
|
||||
BrokenMuzzleComponent = 5,
|
||||
BrokenBearingComponent = 6,
|
||||
BrokenBaseComponent = 7
|
||||
}
|
||||
|
||||
public sealed class CombatParticipantTowerValidationResult
|
||||
@@ -64,7 +67,7 @@ namespace GeometryTD.Definition
|
||||
};
|
||||
}
|
||||
|
||||
if (!HasComponent(inventory?.MuzzleComponents, tower.MuzzleComponentInstanceId))
|
||||
if (!TryGetComponentById(inventory?.MuzzleComponents, tower.MuzzleComponentInstanceId, out MuzzleCompItemData muzzleComponent))
|
||||
{
|
||||
return new CombatParticipantTowerValidationResult
|
||||
{
|
||||
@@ -74,7 +77,17 @@ namespace GeometryTD.Definition
|
||||
};
|
||||
}
|
||||
|
||||
if (!HasComponent(inventory?.BearingComponents, tower.BearingComponentInstanceId))
|
||||
if (muzzleComponent.Endurance <= 0f)
|
||||
{
|
||||
return new CombatParticipantTowerValidationResult
|
||||
{
|
||||
TowerInstanceId = tower.InstanceId,
|
||||
Tower = tower,
|
||||
FailureReason = CombatParticipantTowerValidationFailureReason.BrokenMuzzleComponent
|
||||
};
|
||||
}
|
||||
|
||||
if (!TryGetComponentById(inventory?.BearingComponents, tower.BearingComponentInstanceId, out BearingCompItemData bearingComponent))
|
||||
{
|
||||
return new CombatParticipantTowerValidationResult
|
||||
{
|
||||
@@ -84,7 +97,17 @@ namespace GeometryTD.Definition
|
||||
};
|
||||
}
|
||||
|
||||
if (!HasComponent(inventory?.BaseComponents, tower.BaseComponentInstanceId))
|
||||
if (bearingComponent.Endurance <= 0f)
|
||||
{
|
||||
return new CombatParticipantTowerValidationResult
|
||||
{
|
||||
TowerInstanceId = tower.InstanceId,
|
||||
Tower = tower,
|
||||
FailureReason = CombatParticipantTowerValidationFailureReason.BrokenBearingComponent
|
||||
};
|
||||
}
|
||||
|
||||
if (!TryGetComponentById(inventory?.BaseComponents, tower.BaseComponentInstanceId, out BaseCompItemData baseComponent))
|
||||
{
|
||||
return new CombatParticipantTowerValidationResult
|
||||
{
|
||||
@@ -94,6 +117,16 @@ namespace GeometryTD.Definition
|
||||
};
|
||||
}
|
||||
|
||||
if (baseComponent.Endurance <= 0f)
|
||||
{
|
||||
return new CombatParticipantTowerValidationResult
|
||||
{
|
||||
TowerInstanceId = tower.InstanceId,
|
||||
Tower = tower,
|
||||
FailureReason = CombatParticipantTowerValidationFailureReason.BrokenBaseComponent
|
||||
};
|
||||
}
|
||||
|
||||
return new CombatParticipantTowerValidationResult
|
||||
{
|
||||
TowerInstanceId = tower.InstanceId,
|
||||
@@ -165,9 +198,13 @@ namespace GeometryTD.Definition
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool HasComponent<TComponent>(IReadOnlyList<TComponent> components, long componentInstanceId)
|
||||
private static bool TryGetComponentById<TComponent>(
|
||||
IReadOnlyList<TComponent> components,
|
||||
long componentInstanceId,
|
||||
out TComponent resolvedComponent)
|
||||
where TComponent : TowerCompItemData
|
||||
{
|
||||
resolvedComponent = null;
|
||||
if (components == null || componentInstanceId <= 0)
|
||||
{
|
||||
return false;
|
||||
@@ -178,6 +215,7 @@ namespace GeometryTD.Definition
|
||||
TComponent component = components[i];
|
||||
if (component != null && component.InstanceId == componentInstanceId)
|
||||
{
|
||||
resolvedComponent = component;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -132,9 +132,15 @@ namespace GeometryTD.Procedure
|
||||
return "缺少轴承组件。";
|
||||
case CombatParticipantTowerValidationFailureReason.MissingBaseComponent:
|
||||
return "缺少底座组件。";
|
||||
case CombatParticipantTowerValidationFailureReason.BrokenMuzzleComponent:
|
||||
return "枪口组件耐久为 0,无法参战。";
|
||||
case CombatParticipantTowerValidationFailureReason.BrokenBearingComponent:
|
||||
return "轴承组件耐久为 0,无法参战。";
|
||||
case CombatParticipantTowerValidationFailureReason.BrokenBaseComponent:
|
||||
return "底座组件耐久为 0,无法参战。";
|
||||
default:
|
||||
return "不满足当前参战条件。";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user