PkmnLibSharp/PkmnLibSharp/Battling/Events/BattleEventListener.cs

103 lines
3.9 KiB
C#
Raw Normal View History

2020-07-26 12:47:56 +00:00
using System;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
2020-07-26 12:47:56 +00:00
namespace PkmnLibSharp.Battling.Events
{
public class BattleEventListener
{
public delegate Task BattleEventDelegate(EventData evt);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
2020-07-26 12:47:56 +00:00
private delegate void BattleEventPtrDelegate(IntPtr ptr);
private readonly BattleEventDelegate _del;
// ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable
private readonly BattleEventPtrDelegate _innerDel;
internal readonly IntPtr FunctionPtr;
private Task? _currentTask;
2020-07-26 12:47:56 +00:00
public BattleEventListener(BattleEventDelegate del)
{
_del = del;
_innerDel = OnEventPtr;
2020-08-14 14:15:19 +00:00
FunctionPtr = Marshal.GetFunctionPointerForDelegate(_innerDel);
}
public void EnsureFinishedListening()
{
try
{
_currentTask?.Wait();
}
catch (AggregateException e)
{
LogHandler.Log(LogHandler.LogLevel.Error, e.GetBaseException().Message);
}
catch (Exception e)
{
LogHandler.Log(LogHandler.LogLevel.Error, e.Message);
}
2020-07-26 12:47:56 +00:00
}
private void OnEventPtr(IntPtr ptr)
{
// If we're still registering another event, wait until that's done. This ensures events always arrive in
// the correct order.
2020-08-14 14:15:19 +00:00
EnsureFinishedListening();
// We do however want to run the event handler async, so that the battle code can keep running up till its
// next event.
_currentTask = Task.Run(() => InternalRunner(ptr));
}
private async Task InternalRunner(IntPtr ptr)
2020-07-26 12:47:56 +00:00
{
if (ptr == IntPtr.Zero)
{
return;
}
2020-07-26 12:47:56 +00:00
var wrapped = WrapEventPtr(ptr);
await _del.Invoke(wrapped);
2020-07-26 12:47:56 +00:00
}
2020-08-04 15:32:20 +00:00
private static EventData WrapEventPtr(IntPtr ptr)
2020-07-26 12:47:56 +00:00
{
2020-08-04 15:32:20 +00:00
var evtType = (EventDataKind)Creaturelib.Generated.EventData.GetKind(ptr);
2020-07-26 12:47:56 +00:00
switch (evtType)
{
case EventDataKind.Damage:
2020-08-04 15:32:20 +00:00
return new DamageEvent(evtType, ptr);
2020-07-26 12:47:56 +00:00
case EventDataKind.Heal:
2020-08-07 10:41:02 +00:00
return new HealEvent(evtType, ptr);
2020-07-26 12:47:56 +00:00
case EventDataKind.Faint:
2020-08-07 10:41:02 +00:00
return new FaintEvent(evtType, ptr);
case EventDataKind.Switch:
return new SwitchEvent(evtType, ptr);
case EventDataKind.TurnStart:
return new TurnStartEvent(evtType, ptr);
case EventDataKind.TurnEnd:
return new TurnEndEvent(evtType, ptr);
case EventDataKind.ExperienceGain:
return new ExperienceGainEvent(evtType, ptr);
2020-07-26 12:47:56 +00:00
case EventDataKind.DisplayText:
2020-08-25 17:13:15 +00:00
return new DisplayTextEvent(evtType, ptr);
2020-08-14 14:15:19 +00:00
case EventDataKind.Miss:
2020-08-25 17:13:15 +00:00
return new MissEvent(evtType, ptr);
2020-08-14 14:15:19 +00:00
case EventDataKind.ChangeSpecies:
2020-08-25 17:13:15 +00:00
return new ChangeSpeciesEvent(evtType, ptr);
2020-08-14 14:15:19 +00:00
case EventDataKind.ChangeVariant:
2020-08-25 17:13:15 +00:00
return new ChangeFormeEvent(evtType, ptr);
2020-08-14 14:15:19 +00:00
case EventDataKind.AttackUse:
2020-08-25 17:13:15 +00:00
return new MoveUseEvent(evtType, ptr);
case EventDataKind.ChangeStatBoost:
return new ChangeStatBoostEvent(evtType, ptr);
2020-08-14 14:15:19 +00:00
case EventDataKind.WeatherChange:
break;
case EventDataKind.StatusChange:
break;
2020-08-04 15:32:20 +00:00
default:
throw new ArgumentOutOfRangeException();
2020-07-26 12:47:56 +00:00
}
throw new NotImplementedException($"Unhandled battle event: '{evtType}'");
}
}
}