This commit is contained in:
2026-03-11 16:48:26 +08:00
parent 113decb414
commit 9c5871b518
21 changed files with 611 additions and 47 deletions
@@ -38,4 +38,4 @@ namespace GeometryTD.DataTable
return true;
}
}
}
}
@@ -0,0 +1,28 @@
namespace GeometryTD.Definition
{
public static class CombatParticipantTowerValidationText
{
public static string GetFailureReasonMessage(CombatParticipantTowerValidationFailureReason failureReason)
{
switch (failureReason)
{
case CombatParticipantTowerValidationFailureReason.TowerMissing:
return "已不存在,无法参战。";
case CombatParticipantTowerValidationFailureReason.MissingMuzzleComponent:
return "缺少枪口组件。";
case CombatParticipantTowerValidationFailureReason.MissingBearingComponent:
return "缺少轴承组件。";
case CombatParticipantTowerValidationFailureReason.MissingBaseComponent:
return "缺少底座组件。";
case CombatParticipantTowerValidationFailureReason.BrokenMuzzleComponent:
return "枪口组件耐久为 0,无法参战。";
case CombatParticipantTowerValidationFailureReason.BrokenBearingComponent:
return "轴承组件耐久为 0,无法参战。";
case CombatParticipantTowerValidationFailureReason.BrokenBaseComponent:
return "底座组件耐久为 0,无法参战。";
default:
return "不满足当前参战条件。";
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d7cba7cf0b7a4bfe96f172620db8d30d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -10,6 +10,8 @@ namespace GeometryTD.Procedure
{
public class ProcedureMain : ProcedureBase
{
private const int MaxParticipantTowerCount = 4;
public override bool UseNativeDialog => false;
private RepoFormUseCase _repoFormUseCase;
@@ -188,8 +190,23 @@ namespace GeometryTD.Procedure
snapshot = GameEntry.PlayerInventory.GetInventorySnapshot();
}
HandleRunAdvanceResult(
ProcedureMainRunFlowService.TryAdvanceRun(_currentRunState, args.CompletionStatus, snapshot));
ProcedureMainParticipantTowerCleanupResult cleanupResult =
ProcedureMainParticipantTowerCleanupService.RemoveBrokenParticipantTowers(
snapshot,
MaxParticipantTowerCount);
if (cleanupResult.HasAnyRemovedTower && GameEntry.PlayerInventory != null)
{
GameEntry.PlayerInventory.ReplaceInventorySnapshot(snapshot);
}
ProcedureMainRunAdvanceResult advanceResult =
ProcedureMainRunFlowService.TryAdvanceRun(_currentRunState, args.CompletionStatus, snapshot);
HandleRunAdvanceResult(advanceResult);
if (cleanupResult.HasAnyRemovedTower &&
advanceResult != ProcedureMainRunAdvanceResult.RunCompleted)
{
OpenRemovedParticipantTowerDialog(cleanupResult);
}
}
private void OnNodeMapNodeEnterRequested(object sender, GameEventArgs e)
@@ -404,6 +421,14 @@ namespace GeometryTD.Procedure
});
}
private void OpenRemovedParticipantTowerDialog(ProcedureMainParticipantTowerCleanupResult cleanupResult)
{
GameEntry.UIRouter.CloseUI(UIFormType.DialogForm);
GameEntry.UIRouter.OpenUI(
UIFormType.DialogForm,
ProcedureMainParticipantTowerCleanupService.BuildRemovedTowerDialogRawData(cleanupResult));
}
private void OnRunCompleteDialogConfirmed(object userData)
{
_ = userData;
@@ -423,4 +448,4 @@ namespace GeometryTD.Procedure
_isReturnToMenuPending = true;
}
}
}
}
@@ -114,33 +114,10 @@ namespace GeometryTD.Procedure
builder.Append("塔 #");
builder.Append(result.TowerInstanceId);
builder.Append(' ');
builder.Append(GetFailureReasonMessage(result.FailureReason));
builder.Append(CombatParticipantTowerValidationText.GetFailureReasonMessage(result.FailureReason));
}
return builder.ToString();
}
private static string GetFailureReasonMessage(CombatParticipantTowerValidationFailureReason failureReason)
{
switch (failureReason)
{
case CombatParticipantTowerValidationFailureReason.TowerMissing:
return "已不存在,无法参战。";
case CombatParticipantTowerValidationFailureReason.MissingMuzzleComponent:
return "缺少枪口组件。";
case CombatParticipantTowerValidationFailureReason.MissingBearingComponent:
return "缺少轴承组件。";
case CombatParticipantTowerValidationFailureReason.MissingBaseComponent:
return "缺少底座组件。";
case CombatParticipantTowerValidationFailureReason.BrokenMuzzleComponent:
return "枪口组件耐久为 0,无法参战。";
case CombatParticipantTowerValidationFailureReason.BrokenBearingComponent:
return "轴承组件耐久为 0,无法参战。";
case CombatParticipantTowerValidationFailureReason.BrokenBaseComponent:
return "底座组件耐久为 0,无法参战。";
default:
return "不满足当前参战条件。";
}
}
}
}
@@ -0,0 +1,106 @@
using System.Collections.Generic;
using GeometryTD.CustomUtility;
using GeometryTD.Definition;
using GeometryTD.UI;
namespace GeometryTD.Procedure
{
public sealed class ProcedureMainParticipantTowerCleanupResult
{
public List<CombatParticipantTowerValidationResult> RemovedResults { get; } = new();
public bool HasAnyRemovedTower => RemovedResults.Count > 0;
}
public static class ProcedureMainParticipantTowerCleanupService
{
public static ProcedureMainParticipantTowerCleanupResult RemoveBrokenParticipantTowers(
BackpackInventoryData inventory,
int maxParticipantCount)
{
ProcedureMainParticipantTowerCleanupResult result = new ProcedureMainParticipantTowerCleanupResult();
if (inventory == null)
{
return result;
}
CombatParticipantTowerValidationSummary summary =
CombatParticipantTowerValidationService.ValidateParticipantTowers(inventory);
if (summary.InvalidResults == null || summary.InvalidResults.Count <= 0)
{
return result;
}
HashSet<long> removedTowerIds = new HashSet<long>();
for (int i = 0; i < summary.InvalidResults.Count; i++)
{
CombatParticipantTowerValidationResult invalidResult = summary.InvalidResults[i];
if (invalidResult == null ||
invalidResult.TowerInstanceId <= 0 ||
!IsBrokenFailureReason(invalidResult.FailureReason) ||
!removedTowerIds.Add(invalidResult.TowerInstanceId))
{
continue;
}
if (InventoryParticipantUtility.TryRemoveParticipantTower(
inventory,
invalidResult.TowerInstanceId,
maxParticipantCount))
{
result.RemovedResults.Add(invalidResult);
}
}
return result;
}
public static DialogFormRawData BuildRemovedTowerDialogRawData(
ProcedureMainParticipantTowerCleanupResult cleanupResult)
{
return new DialogFormRawData
{
Mode = 1,
Title = "出战塔已损坏",
Message = BuildRemovedTowerDialogMessage(cleanupResult),
PauseGame = false,
ConfirmText = "知道了"
};
}
private static string BuildRemovedTowerDialogMessage(
ProcedureMainParticipantTowerCleanupResult cleanupResult)
{
if (cleanupResult == null || !cleanupResult.HasAnyRemovedTower)
{
return "当前没有需要移出参战区的损坏防御塔。";
}
System.Text.StringBuilder builder = new System.Text.StringBuilder();
builder.Append("以下防御塔已损坏,已自动移出参战区:");
for (int i = 0; i < cleanupResult.RemovedResults.Count; i++)
{
CombatParticipantTowerValidationResult removedResult = cleanupResult.RemovedResults[i];
if (removedResult == null)
{
continue;
}
builder.Append('\n');
builder.Append("塔 #");
builder.Append(removedResult.TowerInstanceId);
builder.Append(' ');
builder.Append(CombatParticipantTowerValidationText.GetFailureReasonMessage(removedResult.FailureReason));
}
return builder.ToString();
}
private static bool IsBrokenFailureReason(CombatParticipantTowerValidationFailureReason failureReason)
{
return failureReason == CombatParticipantTowerValidationFailureReason.BrokenMuzzleComponent ||
failureReason == CombatParticipantTowerValidationFailureReason.BrokenBearingComponent ||
failureReason == CombatParticipantTowerValidationFailureReason.BrokenBaseComponent;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 597a543063e14117a16e3530f9cb4949
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -185,7 +185,10 @@ namespace GeometryTD.UI
return;
}
Form.SetRepoItemSelected(args.ItemId, args.Assigned);
bool isSelected = _compAreaTowerIds.Contains(args.ItemId)
? _participantTowerIds.Contains(args.ItemId)
: args.Assigned;
Form.SetRepoItemSelected(args.ItemId, isSelected);
}
private void OnCombineSlotClicked(object sender, GameEventArgs e)
@@ -284,6 +287,9 @@ namespace GeometryTD.UI
result.TowerInstanceId,
result.FailureReason,
result.ValidationFailureReason);
GameEntry.UIRouter.OpenUI(
UIFormType.DialogForm,
RepoParticipantAssignDialogUtility.BuildDialog(result, MaxParticipantCount));
}
return;
@@ -0,0 +1,45 @@
using GeometryTD.Definition;
namespace GeometryTD.UI
{
public static class RepoParticipantAssignDialogUtility
{
public static DialogFormRawData BuildDialog(
ParticipantTowerAssignResult result,
int maxParticipantCount)
{
return new DialogFormRawData
{
Mode = 1,
Title = "无法加入参战区",
Message = BuildMessage(result, maxParticipantCount),
PauseGame = false,
ConfirmText = "知道了"
};
}
private static string BuildMessage(
ParticipantTowerAssignResult result,
int maxParticipantCount)
{
if (result == null)
{
return "当前无法加入参战区,请稍后重试。";
}
switch (result.FailureReason)
{
case ParticipantTowerAssignFailureReason.TowerMissing:
return $"塔 #{result.TowerInstanceId} 已不存在,无法加入参战区。";
case ParticipantTowerAssignFailureReason.InvalidTower:
return $"塔 #{result.TowerInstanceId} {CombatParticipantTowerValidationText.GetFailureReasonMessage(result.ValidationFailureReason)}";
case ParticipantTowerAssignFailureReason.AlreadyAssigned:
return $"塔 #{result.TowerInstanceId} 已在参战区中。";
case ParticipantTowerAssignFailureReason.ParticipantAreaFull:
return $"参战区已满,最多只能放入 {maxParticipantCount} 座塔。";
default:
return "当前无法加入参战区,请稍后重试。";
}
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6e6500e5312e4f2cb788f03f0aa8ab0c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -117,7 +117,7 @@ namespace GeometryTD.CustomUtility
ConfigId = 3,
Name = "穿透枪口",
Rarity = InventoryRarityRuleService.NormalizeComponentRarity(RarityType.Purple),
Endurance = 97f,
Endurance = 1f,
IsAssembledIntoTower = false,
AttackDamage = new[] { 50, 55, 60, 80, 90 },
DamageRandomRate = 0.02f,
@@ -70,6 +70,14 @@ namespace GeometryTD.CustomUtility
sb.Append(BuildTowerDesc(tower.Stats ?? new TowerStatsData()));
float enduranceRate = ResolveTowerEnduranceRate(tower, muzzleMap, bearingMap, baseMap);
sb.AppendLine($"平均耐久: {enduranceRate * 100f:0.#}");
CombatParticipantTowerValidationFailureReason failureReason =
ResolveTowerValidationFailureReason(tower, muzzleMap, bearingMap, baseMap);
if (failureReason != CombatParticipantTowerValidationFailureReason.None)
{
sb.AppendLine("状态:已损坏");
sb.Append($"参战限制:{CombatParticipantTowerValidationText.GetFailureReasonMessage(failureReason)}");
}
return sb.ToString();
}
@@ -139,6 +147,7 @@ namespace GeometryTD.CustomUtility
sb.AppendLine($"攻击方式:{ConvertAttackMethod(muzzleData.AttackMethodType)}");
sb.AppendLine($"当前耐久:{muzzleData.Endurance}");
AppendComponentBrokenStatus(sb, muzzleData);
return sb.ToString();
}
@@ -164,6 +173,7 @@ namespace GeometryTD.CustomUtility
sb.Append('\n');
sb.AppendLine($"当前耐久:{bearingData.Endurance}");
AppendComponentBrokenStatus(sb, bearingData);
return sb.ToString();
}
@@ -183,10 +193,65 @@ namespace GeometryTD.CustomUtility
sb.AppendLine($"伤害属性:{ConvertAttackProperty(baseData.AttackPropertyType)}");
sb.AppendLine($"当前耐久:{baseData.Endurance}");
AppendComponentBrokenStatus(sb, baseData);
return sb.ToString();
}
private static void AppendComponentBrokenStatus(StringBuilder sb, TowerCompItemData component)
{
if (sb == null || component == null || component.Endurance > 0f)
{
return;
}
sb.AppendLine("状态:已损坏,无法参战。");
}
private static CombatParticipantTowerValidationFailureReason ResolveTowerValidationFailureReason(
TowerItemData tower,
IReadOnlyDictionary<long, MuzzleCompItemData> muzzleMap,
IReadOnlyDictionary<long, BearingCompItemData> bearingMap,
IReadOnlyDictionary<long, BaseCompItemData> baseMap)
{
if (tower == null)
{
return CombatParticipantTowerValidationFailureReason.TowerMissing;
}
if (muzzleMap == null || !muzzleMap.TryGetValue(tower.MuzzleComponentInstanceId, out MuzzleCompItemData muzzle))
{
return CombatParticipantTowerValidationFailureReason.MissingMuzzleComponent;
}
if (muzzle.Endurance <= 0f)
{
return CombatParticipantTowerValidationFailureReason.BrokenMuzzleComponent;
}
if (bearingMap == null || !bearingMap.TryGetValue(tower.BearingComponentInstanceId, out BearingCompItemData bearing))
{
return CombatParticipantTowerValidationFailureReason.MissingBearingComponent;
}
if (bearing.Endurance <= 0f)
{
return CombatParticipantTowerValidationFailureReason.BrokenBearingComponent;
}
if (baseMap == null || !baseMap.TryGetValue(tower.BaseComponentInstanceId, out BaseCompItemData baseComp))
{
return CombatParticipantTowerValidationFailureReason.MissingBaseComponent;
}
if (baseComp.Endurance <= 0f)
{
return CombatParticipantTowerValidationFailureReason.BrokenBaseComponent;
}
return CombatParticipantTowerValidationFailureReason.None;
}
public static string ConvertAttackMethod(AttackMethodType type)
{
return type switch
@@ -215,4 +280,3 @@ namespace GeometryTD.CustomUtility
}
}