geometry-tower-defense-base/src-ref/CustomComponent/EventNodeComponent.cs

241 lines
8.2 KiB
C#

using System.Collections.Generic;
using GameFramework.DataTable;
using GeometryTD.CustomEvent;
using GeometryTD.DataTable;
using GeometryTD.Definition;
using GeometryTD.Factory;
using GeometryTD.Procedure;
using Newtonsoft.Json.Linq;
using GeometryTD.UI;
using UnityEngine;
using UnityGameFramework.Runtime;
namespace GeometryTD.CustomComponent
{
public class EventNodeComponent : GameFrameworkComponent
{
private RunNodeExecutionContext _activeContext;
private EventItem _activeEvent;
private readonly List<EventItem> _eventItems = new List<EventItem>();
private EventFormUseCase _eventFormUseCase;
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();
}
GameEntry.UIRouter.BindUIUseCase(UIFormType.EventForm, _eventFormUseCase);
_initialized = true;
Log.Info("EventNodeComponent initialized with {0} events.", _eventItems.Count);
}
public void StartEvent(RunNodeExecutionContext context = null)
{
if (!_initialized)
{
OnInit();
}
if (_eventItems.Count <= 0)
{
Log.Warning("EventNodeComponent has no event data.");
return;
}
if (_eventFormUseCase == null)
{
Log.Warning("EventNodeComponent StartEvent failed. Event form is not initialized.");
return;
}
_activeContext = context != null ? context.Clone() : null;
_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,
NodeEnterEventArgs.Create(
_activeContext?.RunId,
_activeContext?.NodeId ?? 0,
_activeContext?.NodeType ?? RunNodeType.None,
_activeContext?.SequenceIndex ?? -1));
}
public void EndEvent()
{
GameEntry.UIRouter.CloseUI(UIFormType.EventForm);
GameEntry.Event.Fire(
this,
NodeCompleteEventArgs.Create(
_activeContext?.RunId,
_activeContext?.NodeId ?? 0,
_activeContext?.NodeType ?? RunNodeType.None,
_activeContext?.SequenceIndex ?? -1,
RunNodeCompletionStatus.Completed,
true,
_activeContext != null
? _activeContext.CreateCompletionSnapshot(
GameEntry.PlayerInventory != null ? GameEntry.PlayerInventory.GetInventorySnapshot() : null)
: null));
ClearActiveNodeContext();
}
private void ClearActiveNodeContext()
{
_activeContext = null;
_activeEvent = null;
_eventFormUseCase?.Clear();
}
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();
}
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;
}
}
}
}