[Raid] Add start command
This commit is contained in:
@@ -11,4 +11,8 @@
|
|||||||
<PackageReference Include="Discord.Net" Version="3.8.1" />
|
<PackageReference Include="Discord.Net" Version="3.8.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Modules" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -8,12 +8,13 @@ using Discord;
|
|||||||
using Discord.Interactions;
|
using Discord.Interactions;
|
||||||
using Discord.WebSocket;
|
using Discord.WebSocket;
|
||||||
|
|
||||||
namespace Cocotte.Modules;
|
namespace Cocotte.Modules.Ping;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Module containing different test and debug commands
|
/// Module containing different test and debug commands
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RequireOwner]
|
[RequireOwner]
|
||||||
|
[Group("ping", "Debug related commands")]
|
||||||
public class PingModule : InteractionModuleBase<SocketInteractionContext>
|
public class PingModule : InteractionModuleBase<SocketInteractionContext>
|
||||||
{
|
{
|
||||||
private readonly ILogger<PingModule> _logger;
|
private readonly ILogger<PingModule> _logger;
|
||||||
@@ -291,7 +292,7 @@ public class FoodModal : IModal
|
|||||||
[ModalTextInput("food_name", placeholder: "Pizza", maxLength: 20)]
|
[ModalTextInput("food_name", placeholder: "Pizza", maxLength: 20)]
|
||||||
public string? Food { get; set; }
|
public string? Food { get; set; }
|
||||||
|
|
||||||
// Additional paremeters can be specified to further customize the input.
|
// Additional paremeters can be specified to further customize the input.
|
||||||
// Parameters can be optional
|
// Parameters can be optional
|
||||||
[RequiredInput(false)]
|
[RequiredInput(false)]
|
||||||
[InputLabel("Why??")]
|
[InputLabel("Why??")]
|
||||||
8
Cocotte/Modules/Raids/IRaidsRepository.cs
Normal file
8
Cocotte/Modules/Raids/IRaidsRepository.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Cocotte.Modules.Raids;
|
||||||
|
|
||||||
|
public interface IRaidsRepository
|
||||||
|
{
|
||||||
|
Raid this[ulong raidId] { get; }
|
||||||
|
|
||||||
|
bool AddNewRaid(ulong raidId, DateTime dateTime);
|
||||||
|
}
|
||||||
18
Cocotte/Modules/Raids/MemoryRaidRepository.cs
Normal file
18
Cocotte/Modules/Raids/MemoryRaidRepository.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
namespace Cocotte.Modules.Raids;
|
||||||
|
|
||||||
|
public class MemoryRaidRepository : IRaidsRepository
|
||||||
|
{
|
||||||
|
private readonly Dictionary<ulong, Raid> _raids;
|
||||||
|
|
||||||
|
public Raid this[ulong raidId] => _raids[raidId];
|
||||||
|
|
||||||
|
public MemoryRaidRepository()
|
||||||
|
{
|
||||||
|
_raids = new Dictionary<ulong, Raid>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool AddNewRaid(ulong raidId, DateTime dateTime)
|
||||||
|
{
|
||||||
|
return _raids.TryAdd(raidId, new Raid(raidId, dateTime));
|
||||||
|
}
|
||||||
|
}
|
||||||
38
Cocotte/Modules/Raids/Raid.cs
Normal file
38
Cocotte/Modules/Raids/Raid.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using Cocotte.Utils;
|
||||||
|
|
||||||
|
namespace Cocotte.Modules.Raids;
|
||||||
|
|
||||||
|
public class Raid
|
||||||
|
{
|
||||||
|
public ulong Id { get; }
|
||||||
|
public DateTime DateTime { get; }
|
||||||
|
|
||||||
|
private readonly RosterManager _rosterManager = new();
|
||||||
|
|
||||||
|
public IEnumerable<IGrouping<int, RosterPlayer>> Rosters => _rosterManager.Rosters;
|
||||||
|
|
||||||
|
public Raid(ulong id, DateTime dateTime)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
DateTime = dateTime;
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
this.AddTestPlayers();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool AddPlayer(string name, PlayerRole role, int fc, bool substitute = false)
|
||||||
|
{
|
||||||
|
return _rosterManager.AddPlayer(new RosterPlayer(name, role, fc, substitute));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object? other)
|
||||||
|
{
|
||||||
|
return other is Raid roster && roster.Id == Id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return (int) (Id % int.MaxValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
79
Cocotte/Modules/Raids/RaidModule.cs
Normal file
79
Cocotte/Modules/Raids/RaidModule.cs
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Cocotte.Utils;
|
||||||
|
using Discord;
|
||||||
|
using Discord.Interactions;
|
||||||
|
|
||||||
|
namespace Cocotte.Modules.Raids;
|
||||||
|
|
||||||
|
[Group("raid", "Raid related commands")]
|
||||||
|
public class RaidModule : InteractionModuleBase<SocketInteractionContext>
|
||||||
|
{
|
||||||
|
private readonly ILogger<RaidModule> _logger;
|
||||||
|
private readonly IRaidsRepository _raidsRepository;
|
||||||
|
|
||||||
|
public RaidModule(ILogger<RaidModule> logger, IRaidsRepository raidsRepository)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_raidsRepository = raidsRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
[SlashCommand("start", "Start a raid formation")]
|
||||||
|
public async Task Ping()
|
||||||
|
{
|
||||||
|
// Raids are identified using their original message id
|
||||||
|
await RespondAsync("`Creating a new raid...`");
|
||||||
|
|
||||||
|
var response = await GetOriginalResponseAsync();
|
||||||
|
var raidId = response.Id;
|
||||||
|
|
||||||
|
_logger.LogInformation("Created new raid with id {RaidId}", raidId);
|
||||||
|
|
||||||
|
// New raid instance
|
||||||
|
// TODO: Ask for date
|
||||||
|
if (!_raidsRepository.AddNewRaid(raidId, DateTime.Now))
|
||||||
|
{
|
||||||
|
// A raid with this message id already exists, how??
|
||||||
|
_logger.LogWarning("Tried to create a new raid with already existing id: {RaidId}", raidId);
|
||||||
|
|
||||||
|
await FollowupAsync(ephemeral: true, embed: EmbedUtils.ErrorEmbed("Can't create a new raid with same raid id").Build());
|
||||||
|
await DeleteOriginalResponseAsync();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the raid message
|
||||||
|
var embed = RaidEmbed(raidId);
|
||||||
|
|
||||||
|
await ModifyOriginalResponseAsync(m =>
|
||||||
|
{
|
||||||
|
m.Content = "";
|
||||||
|
m.Embed = embed.Build();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[SuppressMessage("ReSharper", "PossibleMultipleEnumeration")]
|
||||||
|
private EmbedBuilder RaidEmbed(ulong raidId)
|
||||||
|
{
|
||||||
|
EmbedFieldBuilder RosterEmbed(int rosterNumber, IEnumerable<RosterPlayer> players)
|
||||||
|
{
|
||||||
|
var nonSubstitute = players.Where(p => !p.Substitue);
|
||||||
|
var substitute = players.Where(p => p.Substitue);
|
||||||
|
|
||||||
|
var separatorLength = Math.Max(nonSubstitute.Select(p => p.Name.Length).Max(), substitute.Select(p => p.Name.Length).Max());
|
||||||
|
separatorLength = (int) ((separatorLength + 13) * 0.49); // Don't ask why, it just works
|
||||||
|
|
||||||
|
return new EmbedFieldBuilder()
|
||||||
|
.WithName($"Roster {rosterNumber}")
|
||||||
|
.WithValue($"{string.Join("\n", nonSubstitute)}\n{new string('━', separatorLength)}\n{string.Join("\n", substitute)}")
|
||||||
|
.WithIsInline(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
var raid = _raidsRepository[raidId];
|
||||||
|
|
||||||
|
return new EmbedBuilder()
|
||||||
|
.WithColor(Colors.CocotteBlue)
|
||||||
|
.WithTitle(":crossed_swords: Raid")
|
||||||
|
.WithDescription($"**Date:** {TimestampTag.FromDateTime(raid.DateTime, TimestampTagStyles.LongDateTime)}")
|
||||||
|
.WithFields(raid.Rosters.Select(r => RosterEmbed(r.Key, r)));
|
||||||
|
}
|
||||||
|
}
|
||||||
16
Cocotte/Modules/Raids/RosterManager.cs
Normal file
16
Cocotte/Modules/Raids/RosterManager.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
namespace Cocotte.Modules.Raids;
|
||||||
|
|
||||||
|
public class RosterManager
|
||||||
|
{
|
||||||
|
private readonly ISet<RosterPlayer> _rosters = new HashSet<RosterPlayer>();
|
||||||
|
|
||||||
|
public IEnumerable<IGrouping<int, RosterPlayer>> Rosters => _rosters.GroupBy(p => p.RosterNumber);
|
||||||
|
|
||||||
|
public bool AddPlayer(RosterPlayer rosterPlayer)
|
||||||
|
{
|
||||||
|
// TODO add logic to split player in multiple rosters
|
||||||
|
rosterPlayer.RosterNumber = 1;
|
||||||
|
|
||||||
|
return _rosters.Add(rosterPlayer);
|
||||||
|
}
|
||||||
|
}
|
||||||
33
Cocotte/Modules/Raids/RosterPlayer.cs
Normal file
33
Cocotte/Modules/Raids/RosterPlayer.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
namespace Cocotte.Modules.Raids;
|
||||||
|
|
||||||
|
public record RosterPlayer(string Name, PlayerRole Role, int Fc, bool Substitue = false)
|
||||||
|
{
|
||||||
|
public int RosterNumber { get; set; }
|
||||||
|
|
||||||
|
private static string RoleToEmote(PlayerRole role) => role switch
|
||||||
|
{
|
||||||
|
PlayerRole.Dps => ":red_circle:",
|
||||||
|
PlayerRole.Tank => ":yellow_circle:",
|
||||||
|
PlayerRole.Healer => ":green_circle:",
|
||||||
|
_ => ":question:"
|
||||||
|
};
|
||||||
|
|
||||||
|
public static string FcFormat(int fc) => fc switch
|
||||||
|
{
|
||||||
|
< 1_000 => $"{fc}",
|
||||||
|
_ => $"{fc/1000}k"
|
||||||
|
};
|
||||||
|
|
||||||
|
public override string ToString() => Substitue switch
|
||||||
|
{
|
||||||
|
false => $"{RoleToEmote(Role)} {Name} ({FcFormat(Fc)} FC)",
|
||||||
|
true => $"*{RoleToEmote(Role)} {Name} ({FcFormat(Fc)} FC)*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum PlayerRole
|
||||||
|
{
|
||||||
|
Dps,
|
||||||
|
Healer,
|
||||||
|
Tank
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Cocotte.Modules.Raids;
|
||||||
using Cocotte.Options;
|
using Cocotte.Options;
|
||||||
using Cocotte.Services;
|
using Cocotte.Services;
|
||||||
using Discord;
|
using Discord;
|
||||||
@@ -31,6 +32,9 @@ IHost host = Host.CreateDefaultBuilder(args)
|
|||||||
|
|
||||||
services.AddHostedService<CocotteService>();
|
services.AddHostedService<CocotteService>();
|
||||||
|
|
||||||
|
// Data
|
||||||
|
services.AddSingleton<IRaidsRepository, MemoryRaidRepository>();
|
||||||
|
|
||||||
// Custom
|
// Custom
|
||||||
services.AddSingleton<SharedCounter>();
|
services.AddSingleton<SharedCounter>();
|
||||||
services.AddTransient<TransientCounter>();
|
services.AddTransient<TransientCounter>();
|
||||||
|
|||||||
13
Cocotte/Utils/Colors.cs
Normal file
13
Cocotte/Utils/Colors.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using Discord;
|
||||||
|
|
||||||
|
namespace Cocotte.Utils;
|
||||||
|
|
||||||
|
public static class Colors
|
||||||
|
{
|
||||||
|
// Main Cocotte colors
|
||||||
|
public static Color CocotteBlue => new(0x3196c8);
|
||||||
|
|
||||||
|
// Colors used in embeds
|
||||||
|
public static Color ErrorColor => new(0xFB6060);
|
||||||
|
public static Color InfoColor => new(0x66D9EF);
|
||||||
|
}
|
||||||
28
Cocotte/Utils/EmbedUtils.cs
Normal file
28
Cocotte/Utils/EmbedUtils.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using Discord;
|
||||||
|
|
||||||
|
namespace Cocotte.Utils;
|
||||||
|
|
||||||
|
public static class EmbedUtils
|
||||||
|
{
|
||||||
|
public static EmbedBuilder ErrorEmbed(string message)
|
||||||
|
{
|
||||||
|
return new EmbedBuilder()
|
||||||
|
.WithColor(Colors.ErrorColor)
|
||||||
|
.WithAuthor(a => a
|
||||||
|
.WithName("Error")
|
||||||
|
.WithIconUrl("https://sage.cdn.ilysix.fr/assets/Cocotte/icons/error.webp")
|
||||||
|
)
|
||||||
|
.WithDescription(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EmbedBuilder InfoEmbed(string message)
|
||||||
|
{
|
||||||
|
return new EmbedBuilder()
|
||||||
|
.WithColor(Colors.InfoColor)
|
||||||
|
.WithAuthor(a => a
|
||||||
|
.WithName("Info")
|
||||||
|
.WithIconUrl("https://sage.cdn.ilysix.fr/assets/Cocotte/icons/info.webp")
|
||||||
|
)
|
||||||
|
.WithDescription(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
14
Cocotte/Utils/RaidExtensions.cs
Normal file
14
Cocotte/Utils/RaidExtensions.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using Cocotte.Modules.Raids;
|
||||||
|
|
||||||
|
namespace Cocotte.Utils;
|
||||||
|
|
||||||
|
public static class RaidExtensions
|
||||||
|
{
|
||||||
|
public static void AddTestPlayers(this Raid raid)
|
||||||
|
{
|
||||||
|
raid.AddPlayer("YamaRaja", PlayerRole.Healer, 30000, false);
|
||||||
|
raid.AddPlayer("Zaku", PlayerRole.Dps, 40000, false);
|
||||||
|
raid.AddPlayer("Juchi", PlayerRole.Tank, 40000, false);
|
||||||
|
raid.AddPlayer("Akeno", PlayerRole.Dps, 40000, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user