176 lines
5.6 KiB
C#

using Discord.Commands;
using System;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace NadekoBot.Classes.ClashOfClans
{
internal class Caller
{
public string CallUser { get; }
public DateTime TimeAdded { get; private set; }
public bool BaseDestroyed { get; internal set; }
public Caller(string callUser, DateTime timeAdded, bool baseDestroyed)
{
CallUser = callUser;
TimeAdded = timeAdded;
BaseDestroyed = baseDestroyed;
}
public void ResetTime()
{
TimeAdded = DateTime.Now;
}
public void Destroy()
{
BaseDestroyed = true;
}
}
internal class ClashWar
{
private static TimeSpan callExpire => new TimeSpan(2, 0, 0);
private CommandEventArgs e;
public string EnemyClan { get; }
public int Size { get; }
private Caller[] bases { get; }
private CancellationTokenSource[] baseCancelTokens;
private CancellationTokenSource endTokenSource { get; } = new CancellationTokenSource();
public event Action<string> OnUserTimeExpired = delegate { };
public event Action OnWarEnded = delegate { };
public bool Started { get; set; } = false;
public ClashWar(string enemyClan, int size, CommandEventArgs e)
{
this.EnemyClan = enemyClan;
this.Size = size;
this.bases = new Caller[size];
this.baseCancelTokens = new CancellationTokenSource[size];
}
internal void End()
{
if (endTokenSource.Token.IsCancellationRequested) return;
endTokenSource.Cancel();
OnWarEnded();
}
internal void Call(string u, int baseNumber)
{
if (baseNumber < 0 || baseNumber >= bases.Length)
throw new ArgumentException("Invalid base number");
if (bases[baseNumber] != null)
throw new ArgumentException("That base is already claimed.");
for (var i = 0; i < bases.Length; i++)
{
if (bases[i]?.BaseDestroyed == false && bases[i]?.CallUser == u)
throw new ArgumentException($"@{u} You already claimed a base #{i + 1}. You can't claim a new one.");
}
bases[baseNumber] = new Caller(u.Trim(), DateTime.Now, false);
}
internal async Task Start()
{
if (Started)
throw new InvalidOperationException();
try
{
Started = true;
foreach (var b in bases.Where(b => b != null))
{
b.ResetTime();
}
Task.Run(async () => await ClearArray()).ConfigureAwait(false);
await Task.Delay(new TimeSpan(24, 0, 0), endTokenSource.Token);
}
catch { }
finally
{
End();
}
}
internal int Uncall(string user)
{
user = user.Trim();
for (var i = 0; i < bases.Length; i++)
{
if (bases[i]?.CallUser != user) continue;
bases[i] = null;
return i;
}
throw new InvalidOperationException("You are not participating in that war.");
}
private async Task ClearArray()
{
while (!endTokenSource.IsCancellationRequested)
{
await Task.Delay(5000);
for (var i = 0; i < bases.Length; i++)
{
if (bases[i] == null) continue;
if (!bases[i].BaseDestroyed && DateTime.Now - bases[i].TimeAdded >= callExpire)
{
OnUserTimeExpired(bases[i].CallUser);
bases[i] = null;
}
}
}
}
public string ShortPrint() =>
$"`{EnemyClan}` ({Size} v {Size})";
public override string ToString()
{
var sb = new StringBuilder();
sb.AppendLine($"🔰**WAR AGAINST `{EnemyClan}` ({Size} v {Size}) INFO:**");
if (!Started)
sb.AppendLine("`not started`");
for (var i = 0; i < bases.Length; i++)
{
if (bases[i] == null)
{
sb.AppendLine($"`{i + 1}.` ❌*unclaimed*");
}
else
{
if (bases[i].BaseDestroyed)
{
sb.AppendLine($"`{i + 1}.` ✅ `{bases[i].CallUser}` ⭐ ⭐ ⭐");
}
else
{
var left = Started ? callExpire - (DateTime.Now - bases[i].TimeAdded) : callExpire;
sb.AppendLine($"`{i + 1}.` ✅ `{bases[i].CallUser}` {left.Hours}h {left.Minutes}m {left.Seconds}s left");
}
}
}
return sb.ToString();
}
internal int FinishClaim(string user)
{
user = user.Trim();
for (var i = 0; i < bases.Length; i++)
{
if (bases[i]?.BaseDestroyed != false || bases[i]?.CallUser != user) continue;
bases[i].BaseDestroyed = true;
return i;
}
throw new InvalidOperationException($"@{user} You are either not participating in that war, or you already destroyed a base.");
}
}
}