P0-02 补齐数据表:组件、敌人、波次、节点、事件、商店商品

- 组件:
    - 底座:Assets\GameMain\DataTables\BaseComp.txt
    - 轴承:Assets\GameMain\DataTables\BearingComp.txt
    - 枪口:Assets\GameMain\DataTables\MuzzleComp.txt
- 敌人:Assets\GameMain\DataTables\Enemy.txt
- 波次:
    - 关卡:Assets\GameMain\DataTables\Level.txt
    - 关卡阶段(波次):Assets\GameMain\DataTables\LevelPhase.txt
    - 波次条目(波次出怪规则):Assets\GameMain\DataTables\LevelSpawnEntry.txt
- 事件:Assets\GameMain\DataTables\Event.txt
- 商店:Assets\GameMain\DataTables\ShopPrice.txt
This commit is contained in:
SepComet
2026-02-26 21:36:29 +08:00
parent bb0a4edfb7
commit 11e4c1c1f2
115 changed files with 3218 additions and 135 deletions
@@ -0,0 +1,145 @@
using System.Collections.Generic;
using GameFramework.DataTable;
using GeometryTD.DataTable;
using GeometryTD.Definition;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace GeometryTD.CustomComponent
{
/// <summary>
/// 战斗节点组件
/// </summary>
public class CombatNodeComponent : GameFrameworkComponent
{
// Level.Id => Level
private readonly Dictionary<int, DRLevel> _levelsById = new();
// LevelId => LevelPhases
private readonly Dictionary<int, List<DRLevelPhase>> _phasesByLevelId = new();
// LevelPhase.Id => LevelSpawnEntries
private readonly Dictionary<int, List<DRLevelSpawnEntry>> _spawnEntriesByPhaseId = new();
private readonly List<int> _levelIdBuffer = new();
public LevelThemeType CurrentThemeType { get; private set; }
public DRLevel CurrentLevel { get; private set; }
public void OnInit(LevelThemeType themeType)
{
CurrentThemeType = themeType;
CurrentLevel = null;
_levelsById.Clear();
_phasesByLevelId.Clear();
_spawnEntriesByPhaseId.Clear();
_levelIdBuffer.Clear();
IDataTable<DRLevel> dtLevel = GameEntry.DataTable.GetDataTable<DRLevel>();
IDataTable<DRLevelPhase> dtLevelPhase = GameEntry.DataTable.GetDataTable<DRLevelPhase>();
IDataTable<DRLevelSpawnEntry> dtSpawnEntry = GameEntry.DataTable.GetDataTable<DRLevelSpawnEntry>();
if (dtLevel == null || dtLevelPhase == null || dtSpawnEntry == null)
{
Log.Warning("CombatNodeComponent init failed. Missing data table(s).");
return;
}
DRLevel[] levels = dtLevel.GetAllDataRows();
foreach (var level in levels)
{
if (level.LevelThemeType != themeType)
{
continue;
}
_levelsById[level.Id] = level;
_phasesByLevelId[level.Id] = new List<DRLevelPhase>();
_levelIdBuffer.Add(level.Id);
}
DRLevelPhase[] levelPhases = dtLevelPhase.GetAllDataRows();
foreach (var phase in levelPhases)
{
int levelId = phase.Id / 1000;
if (!_levelsById.ContainsKey(levelId))
{
continue;
}
if (!_phasesByLevelId.TryGetValue(levelId, out List<DRLevelPhase> phases))
{
phases = new List<DRLevelPhase>();
_phasesByLevelId[levelId] = phases;
}
phases.Add(phase);
_spawnEntriesByPhaseId[phase.Id] = new List<DRLevelSpawnEntry>();
}
DRLevelSpawnEntry[] spawnEntries = dtSpawnEntry.GetAllDataRows();
for (int i = 0; i < spawnEntries.Length; i++)
{
DRLevelSpawnEntry spawnEntry = spawnEntries[i];
int phaseId = spawnEntry.Id / 1000;
int levelId = phaseId / 1000;
if (!_levelsById.ContainsKey(levelId))
{
continue;
}
if (!_spawnEntriesByPhaseId.TryGetValue(phaseId, out List<DRLevelSpawnEntry> entries))
{
entries = new List<DRLevelSpawnEntry>();
_spawnEntriesByPhaseId[phaseId] = entries;
}
entries.Add(spawnEntry);
}
Log.Info(
"CombatNodeComponent initialized. Theme={0}, Levels={1}, Phases={2}, Entries={3}",
themeType,
_levelsById.Count,
CountPhases(),
CountEntries());
}
public void StartCombat()
{
if (_levelIdBuffer.Count <= 0)
{
Log.Warning("CombatNodeComponent has no level cache. Call OnInit(levelThemeType) first.");
return;
}
int randomIndex = Random.Range(0, _levelIdBuffer.Count);
int randomLevelId = _levelIdBuffer[randomIndex];
CurrentLevel = _levelsById[randomLevelId];
// TODO: 在这里接入真实战斗节点玩法启动流程(场景进入、敌人生成、结算等)。
Log.Info("StartCombat selected level '{0}' (Theme={1}).", CurrentLevel.Id, CurrentThemeType);
}
private int CountPhases()
{
int count = 0;
foreach (List<DRLevelPhase> phases in _phasesByLevelId.Values)
{
count += phases.Count;
}
return count;
}
private int CountEntries()
{
int count = 0;
foreach (var list in _spawnEntriesByPhaseId.Values)
{
count += list.Count;
}
return count;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5e646500b9304d438746f2351c71a0f4
timeCreated: 1772103300
@@ -0,0 +1,172 @@
using System.Collections.Generic;
using GameFramework.DataTable;
using GeometryTD.DataTable;
using GeometryTD.Definition;
using Newtonsoft.Json.Linq;
using GeometryTD.UI;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace GeometryTD.CustomComponent
{
public class EventNodeComponent : GameFrameworkComponent
{
private readonly List<EventItem> _eventItems = new List<EventItem>();
private EventFormUseCase _eventFormUseCase;
private EventFormController _eventFormController;
private bool _initialized;
public void OnInit()
{
_eventItems.Clear();
IDataTable<DREvent> dtEvent = GameEntry.DataTable.GetDataTable<DREvent>();
if (dtEvent == null)
{
Log.Warning("Event data table is not loaded.");
_initialized = true;
return;
}
DREvent[] rows = dtEvent.GetAllDataRows();
foreach (var drEvent in rows)
{
EventOption[] options = ParseOptions(drEvent.OptionsRaw);
_eventItems.Add(new EventItem(drEvent.Id, drEvent.Title, drEvent.Description, options));
}
if (_eventFormUseCase == null)
{
_eventFormUseCase = new EventFormUseCase();
}
if (_eventFormController == null)
{
_eventFormController = new EventFormController();
_eventFormController.BindUseCase(_eventFormUseCase);
}
_initialized = true;
Log.Info("EventNodeComponent initialized with {0} events.", _eventItems.Count);
}
public void StartEvent()
{
if (!_initialized)
{
OnInit();
}
if (_eventItems.Count <= 0)
{
Log.Warning("EventNodeComponent has no event data.");
return;
}
int index = Random.Range(0, _eventItems.Count);
EventItem randomEvent = _eventItems[index];
if (_eventFormUseCase == null || _eventFormController == null)
{
Log.Warning("EventNodeComponent StartEvent failed. Event form is not initialized.");
return;
}
_eventFormUseCase.SetCurrentEvent(randomEvent);
_eventFormController.OpenUI();
}
private static EventOption[] ParseOptions(string optionsRaw)
{
if (string.IsNullOrWhiteSpace(optionsRaw))
{
return System.Array.Empty<EventOption>();
}
try
{
JArray array = JArray.Parse(optionsRaw);
List<EventOption> options = new List<EventOption>(array.Count);
for (int i = 0; i < array.Count; i++)
{
if (!(array[i] is JObject optionObj))
{
continue;
}
string optionText = optionObj.Value<string>("optionText") ?? string.Empty;
float probability = optionObj.Value<float?>("probability") ?? 1f;
EventRequirementBase[] requirements = ParseRequirements(optionObj["requirements"] as JArray);
EventEffectBase[] costEffects = ParseEffects(optionObj["costEffects"] as JArray, probability);
EventEffectBase[] rewardEffects = ParseEffects(optionObj["rewardEffects"] as JArray, probability);
options.Add(new EventOption(optionText, requirements, costEffects, rewardEffects, probability));
}
return options.ToArray();
}
catch (System.Exception e)
{
Log.Warning("Failed to parse event options json. {0}", e.Message);
return System.Array.Empty<EventOption>();
}
}
private static EventRequirementBase[] ParseRequirements(JArray requirementsArray)
{
if (requirementsArray == null || requirementsArray.Count == 0)
{
return System.Array.Empty<EventRequirementBase>();
}
List<EventRequirementBase> requirements = new List<EventRequirementBase>(requirementsArray.Count);
for (int i = 0; i < requirementsArray.Count; i++)
{
if (!(requirementsArray[i] is JObject reqObj))
{
continue;
}
string type = reqObj.Value<string>("type");
JObject param = reqObj["param"] as JObject;
EventRequirementBase requirement = EventRequirementFactory.Create(type, param);
if (requirement != null)
{
requirements.Add(requirement);
}
}
return requirements.ToArray();
}
private static EventEffectBase[] ParseEffects(JArray effectsArray, float probability)
{
if (effectsArray == null || effectsArray.Count == 0)
{
return System.Array.Empty<EventEffectBase>();
}
List<EventEffectBase> effects = new List<EventEffectBase>(effectsArray.Count);
for (int i = 0; i < effectsArray.Count; i++)
{
if (!(effectsArray[i] is JObject effectObj))
{
continue;
}
string type = effectObj.Value<string>("type");
JObject param = effectObj["param"] as JObject;
EventEffectBase effect = EventEffectFactory.Create(type, param, probability);
if (effect != null)
{
effects.Add(effect);
}
}
return effects.ToArray();
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0a0c1c547ca24c95819f5f62f0bd3ea3
timeCreated: 1772102402