补全部分 EventComponent 设计

This commit is contained in:
2026-03-16 11:53:24 +08:00
parent c76fd85a2c
commit dc2aa59d58
26 changed files with 2028 additions and 59 deletions
@@ -15,6 +15,7 @@ namespace GeometryTD.CustomComponent
public class EventNodeComponent : GameFrameworkComponent
{
private RunNodeExecutionContext _activeContext;
private EventItem _activeEvent;
private readonly List<EventItem> _eventItems = new List<EventItem>();
@@ -64,17 +65,21 @@ namespace GeometryTD.CustomComponent
return;
}
int index = Random.Range(0, _eventItems.Count);
EventItem randomEvent = _eventItems[index];
if (_eventFormUseCase == null)
{
Log.Warning("EventNodeComponent StartEvent failed. Event form is not initialized.");
return;
}
_activeContext = context != null ? context.Clone() : null;
_eventFormUseCase.SetCurrentEvent(randomEvent);
_activeEvent = SelectActiveEvent(_activeContext);
if (_activeEvent == null)
{
Log.Warning("EventNodeComponent StartEvent failed. No active event could be resolved.");
return;
}
_eventFormUseCase.BindEvent(_activeEvent, _activeContext);
GameEntry.UIRouter.OpenUI(UIFormType.EventForm);
GameEntry.Event.Fire(
this,
@@ -107,6 +112,8 @@ namespace GeometryTD.CustomComponent
private void ClearActiveNodeContext()
{
_activeContext = null;
_activeEvent = null;
_eventFormUseCase?.Clear();
}
private static EventOption[] ParseOptions(string optionsRaw)
@@ -200,5 +207,34 @@ namespace GeometryTD.CustomComponent
return effects.ToArray();
}
private EventItem SelectActiveEvent(RunNodeExecutionContext context)
{
if (_eventItems.Count <= 0)
{
return null;
}
if (context == null)
{
int randomIndex = Random.Range(0, _eventItems.Count);
return _eventItems[randomIndex];
}
System.Random random = new System.Random(BuildSelectionSeed(context));
return _eventItems[random.Next(0, _eventItems.Count)];
}
private static int BuildSelectionSeed(RunNodeExecutionContext context)
{
unchecked
{
int seed = 17;
seed = seed * 31 + context.RunSeed;
seed = seed * 31 + context.SequenceIndex;
seed = seed * 31 + context.NodeId;
return seed;
}
}
}
}
@@ -102,18 +102,78 @@ namespace GeometryTD.CustomComponent
}
}
public IReadOnlyList<TowerCompItemData> BuildEventRewardComponents(
int count,
RarityType rarity,
int runSeed,
int sequenceIndex,
int eventId,
int optionIndex,
int effectIndex)
{
return BuildEventRewardComponents(
count,
rarity,
rarity,
runSeed,
sequenceIndex,
eventId,
optionIndex,
effectIndex);
}
public IReadOnlyList<TowerCompItemData> BuildEventRewardComponents(
int count,
RarityType minRarity,
RarityType maxRarity,
int runSeed,
int sequenceIndex,
int eventId,
int optionIndex,
int effectIndex)
{
EnsureComponentTables();
int resolvedCount = Mathf.Max(0, count);
if (resolvedCount <= 0)
{
return System.Array.Empty<TowerCompItemData>();
}
RarityType normalizedMinRarity = InventoryRarityRuleService.NormalizeComponentRarity(minRarity);
RarityType normalizedMaxRarity = InventoryRarityRuleService.NormalizeComponentRarity(maxRarity);
if (normalizedMinRarity > normalizedMaxRarity)
{
throw new System.InvalidOperationException(
$"Event reward rarity range is invalid: {normalizedMinRarity} > {normalizedMaxRarity}.");
}
List<TowerCompItemData> result = new List<TowerCompItemData>(resolvedCount);
for (int i = 0; i < resolvedCount; i++)
{
InventoryGenerationRandomContext randomContext = CreateEventRandomContext(
runSeed,
sequenceIndex,
eventId,
optionIndex,
effectIndex,
i);
Random random = randomContext.CreateRandom();
result.Add(BuildRandomEventComponentItem(normalizedMinRarity, normalizedMaxRarity, randomContext, random));
}
return result;
}
private void EnsureShopTables()
{
_shopPriceTable ??= GameEntry.DataTable.GetDataTable<DRShopPrice>();
_muzzleCompTable ??= GameEntry.DataTable.GetDataTable<DRMuzzleComp>();
_bearingCompTable ??= GameEntry.DataTable.GetDataTable<DRBearingComp>();
_baseCompTable ??= GameEntry.DataTable.GetDataTable<DRBaseComp>();
EnsureComponentTables();
if (_shopPriceTable == null || _muzzleCompTable == null || _bearingCompTable == null ||
_baseCompTable == null)
if (_shopPriceTable == null)
{
throw new System.InvalidOperationException(
"InventoryGenerationComponent requires ShopPrice, MuzzleComp, BearingComp, and BaseComp data tables.");
"InventoryGenerationComponent requires ShopPrice data table.");
}
if (_shopPriceRows.Count > 0)
@@ -155,14 +215,12 @@ namespace GeometryTD.CustomComponent
private void EnsureDropTables()
{
_dropPoolTable ??= GameEntry.DataTable.GetDataTable<DROutGameDropPool>();
_muzzleCompTable ??= GameEntry.DataTable.GetDataTable<DRMuzzleComp>();
_bearingCompTable ??= GameEntry.DataTable.GetDataTable<DRBearingComp>();
_baseCompTable ??= GameEntry.DataTable.GetDataTable<DRBaseComp>();
EnsureComponentTables();
if (_dropPoolTable == null || _muzzleCompTable == null || _bearingCompTable == null || _baseCompTable == null)
if (_dropPoolTable == null)
{
throw new System.InvalidOperationException(
"InventoryGenerationComponent requires OutGameDropPool, MuzzleComp, BearingComp, and BaseComp data tables.");
"InventoryGenerationComponent requires OutGameDropPool data table.");
}
}
@@ -224,5 +282,118 @@ namespace GeometryTD.CustomComponent
return droppedItem;
}
private void EnsureComponentTables()
{
_muzzleCompTable ??= GameEntry.DataTable.GetDataTable<DRMuzzleComp>();
_bearingCompTable ??= GameEntry.DataTable.GetDataTable<DRBearingComp>();
_baseCompTable ??= GameEntry.DataTable.GetDataTable<DRBaseComp>();
if (_muzzleCompTable == null || _bearingCompTable == null || _baseCompTable == null)
{
throw new System.InvalidOperationException(
"InventoryGenerationComponent requires MuzzleComp, BearingComp, and BaseComp data tables.");
}
}
private TowerCompItemData BuildRandomEventComponentItem(
RarityType minRarity,
RarityType maxRarity,
InventoryGenerationRandomContext randomContext,
Random random)
{
RarityType rarity = ResolveEventRewardRarity(minRarity, maxRarity, random);
int slotRoll = random.Next(0, 3);
return slotRoll switch
{
0 => BuildRandomEventMuzzleItem(rarity, randomContext, random),
1 => BuildRandomEventBearingItem(rarity, randomContext, random),
_ => BuildRandomEventBaseItem(rarity, randomContext, random)
};
}
private static RarityType ResolveEventRewardRarity(
RarityType minRarity,
RarityType maxRarity,
Random random)
{
if (minRarity >= maxRarity)
{
return minRarity;
}
int rarityValue = random.Next((int)minRarity, (int)maxRarity + 1);
return (RarityType)rarityValue;
}
private MuzzleCompItemData BuildRandomEventMuzzleItem(
RarityType rarity,
InventoryGenerationRandomContext randomContext,
Random random)
{
DRMuzzleComp[] rows = _muzzleCompTable.GetAllDataRows();
DRMuzzleComp config = rows[random.Next(0, rows.Length)];
return ComponentItemFactory.CreateMuzzle(
config,
randomContext.CreateStableItemInstanceId(),
rarity,
randomContext.CreateTagRandomContext(config.Id));
}
private BearingCompItemData BuildRandomEventBearingItem(
RarityType rarity,
InventoryGenerationRandomContext randomContext,
Random random)
{
DRBearingComp[] rows = _bearingCompTable.GetAllDataRows();
DRBearingComp config = rows[random.Next(0, rows.Length)];
return ComponentItemFactory.CreateBearing(
config,
randomContext.CreateStableItemInstanceId(),
rarity,
randomContext.CreateTagRandomContext(config.Id));
}
private BaseCompItemData BuildRandomEventBaseItem(
RarityType rarity,
InventoryGenerationRandomContext randomContext,
Random random)
{
DRBaseComp[] rows = _baseCompTable.GetAllDataRows();
DRBaseComp config = rows[random.Next(0, rows.Length)];
return ComponentItemFactory.CreateBase(
config,
randomContext.CreateStableItemInstanceId(),
rarity,
randomContext.CreateTagRandomContext(config.Id));
}
private static InventoryGenerationRandomContext CreateEventRandomContext(
int runSeed,
int sequenceIndex,
int eventId,
int optionIndex,
int effectIndex,
int itemIndex)
{
return new InventoryGenerationRandomContext(
runSeed,
sequenceIndex,
InventoryTagSourceType.Event,
BuildEventLocalOrdinal(eventId, optionIndex, effectIndex, itemIndex));
}
private static int BuildEventLocalOrdinal(int eventId, int optionIndex, int effectIndex, int itemIndex)
{
unchecked
{
int value = 17;
value = value * 31 + eventId;
value = value * 31 + optionIndex;
value = value * 31 + effectIndex;
value = value * 31 + itemIndex;
return value & int.MaxValue;
}
}
}
}
@@ -44,6 +44,7 @@ namespace GeometryTD.Definition
{
InventoryTagSourceType.Shop => InventoryTagRandomContext.CreateShop(RunSeed, NodeSequenceIndex, LocalOrdinal, configId),
InventoryTagSourceType.Reward => InventoryTagRandomContext.CreateReward(RunSeed, NodeSequenceIndex, LocalOrdinal, configId),
InventoryTagSourceType.Event => InventoryTagRandomContext.CreateEvent(RunSeed, NodeSequenceIndex, LocalOrdinal, configId),
_ => InventoryTagRandomContext.CreateDrop(RunSeed, NodeSequenceIndex, LocalOrdinal, configId)
};
}
@@ -35,14 +35,27 @@ namespace GeometryTD.CustomComponent
}
public int ReduceTowerEndurance(IReadOnlyList<long> towerInstanceIds, float enduranceLoss)
{
return InventoryTowerEnduranceUtility.ReduceTowerEndurance(
_queryModel.Inventory,
towerInstanceIds,
enduranceLoss);
}
}
public static class InventoryTowerEnduranceUtility
{
public static int ReduceTowerEndurance(
BackpackInventoryData inventory,
IReadOnlyList<long> towerInstanceIds,
float enduranceLoss)
{
float resolvedLoss = Mathf.Max(0f, enduranceLoss);
BackpackInventoryData inventory = _queryModel.Inventory;
if (resolvedLoss <= 0f ||
if (inventory?.Towers == null ||
inventory.Towers.Count <= 0 ||
resolvedLoss <= 0f ||
towerInstanceIds == null ||
towerInstanceIds.Count <= 0 ||
inventory.Towers == null ||
inventory.Towers.Count <= 0)
towerInstanceIds.Count <= 0)
{
return 0;
}