~ruther/NosSmooth

8f7c60d23d6b045eb9daae93c62d3f339472d6b1 — František Boháček 2 years ago e4b2b3c
feat(game): add raid handling
M Core/NosSmooth.Game/Data/Raids/Raid.cs => Core/NosSmooth.Game/Data/Raids/Raid.cs +16 -1
@@ 4,9 4,24 @@
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using NosSmooth.Game.Data.Entities;
using NosSmooth.Game.Data.Social;
using NosSmooth.Packets.Enums.Raids;

namespace NosSmooth.Game.Data.Raids;

/// <summary>
/// Represents nostale raid.
/// </summary>
public record Raid();
\ No newline at end of file
public record Raid
(
    RaidType Type,
    RaidState State,
    short MinimumLevel,
    short MaximumLevel,
    GroupMember? Leader,
    RaidProgress? Progress,
    Monster? Boss,
    IReadOnlyList<Monster>? Bosses,
    IReadOnlyList<GroupMember>? Members
);
\ No newline at end of file

A Core/NosSmooth.Game/Data/Raids/RaidProgress.cs => Core/NosSmooth.Game/Data/Raids/RaidProgress.cs +17 -0
@@ 0,0 1,17 @@
//
//  RaidProgress.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace NosSmooth.Game.Data.Raids;

public record RaidProgress
(
    short MonsterLockerInitial,
    short MonsterLockerCurrent,
    short ButtonLockerInitial,
    short ButtonLockerCurrent,
    short CurrentLives,
    short InitialLives
);
\ No newline at end of file

A Core/NosSmooth.Game/Data/Raids/RaidState.cs => Core/NosSmooth.Game/Data/Raids/RaidState.cs +52 -0
@@ 0,0 1,52 @@
//
//  RaidState.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace NosSmooth.Game.Data.Raids;

/// <summary>
/// A state of a <see cref="Raid"/>.
/// </summary>
public enum RaidState
{
    /// <summary>
    /// Waiting for a raid start.
    /// </summary>
    Waiting,

    /// <summary>
    /// The raid has started, the current room is not boss room.
    /// </summary>
    Started,

    /// <summary>
    /// The raid has started and the current room is boss room.
    /// </summary>
    BossFight,

    /// <summary>
    /// The raid has ended, successfully.
    /// </summary>
    EndedSuccessfully,

    /// <summary>
    /// The raid has ended unsuccessfully. The whole team has failed.
    /// </summary>
    TeamFailed,

    /// <summary>
    /// The raid has ended unsuccessfully for the character. He ran out of lifes.
    /// </summary>
    MemberFailed,

    /// <summary>
    /// The character has left the raid.
    /// </summary>
    /// <remarks>
    /// The previous state is needed to be able to tell whether
    /// the raid was already started or was in the <see cref="Waiting"/> state.
    /// </remarks>
    Left
}
\ No newline at end of file

M Core/NosSmooth.Game/Data/Social/GroupMember.cs => Core/NosSmooth.Game/Data/Social/GroupMember.cs +1 -1
@@ 43,7 43,7 @@ public record GroupMember(long PlayerId)
    /// <summary>
    /// Gets the morph vnum of the player.
    /// </summary>
    public long MorphVNum { get; internal set; }
    public int? MorphVNum { get; internal set; }

    /// <summary>
    /// Gets the hp of the member.

A Core/NosSmooth.Game/Events/Raids/RaidFinishedEvent.cs => Core/NosSmooth.Game/Events/Raids/RaidFinishedEvent.cs +11 -0
@@ 0,0 1,11 @@
//
//  RaidFinishedEvent.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using NosSmooth.Game.Data.Raids;

namespace NosSmooth.Game.Events.Raids;

public record RaidFinishedEvent(Raid Raid) : IGameEvent;
\ No newline at end of file

A Core/NosSmooth.Game/Events/Raids/RaidJoinedEvent.cs => Core/NosSmooth.Game/Events/Raids/RaidJoinedEvent.cs +11 -0
@@ 0,0 1,11 @@
//
//  RaidJoinedEvent.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using NosSmooth.Game.Data.Raids;

namespace NosSmooth.Game.Events.Raids;

public record RaidJoinedEvent(Raid Raid) : IGameEvent;
\ No newline at end of file

A Core/NosSmooth.Game/Events/Raids/RaidStateChangedEvent.cs => Core/NosSmooth.Game/Events/Raids/RaidStateChangedEvent.cs +16 -0
@@ 0,0 1,16 @@
//
//  RaidStateChangedEvent.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using NosSmooth.Game.Data.Raids;

namespace NosSmooth.Game.Events.Raids;

public record RaidStateChangedEvent
(
    RaidState PreviousState,
    RaidState CurrentState,
    Raid Raid
) : IGameEvent;
\ No newline at end of file

M Core/NosSmooth.Game/Extensions/ServiceCollectionExtensions.cs => Core/NosSmooth.Game/Extensions/ServiceCollectionExtensions.cs +40 -6
@@ 18,9 18,11 @@ using NosSmooth.Game.PacketHandlers.Characters;
using NosSmooth.Game.PacketHandlers.Entities;
using NosSmooth.Game.PacketHandlers.Inventory;
using NosSmooth.Game.PacketHandlers.Map;
using NosSmooth.Game.PacketHandlers.Raids;
using NosSmooth.Game.PacketHandlers.Relations;
using NosSmooth.Game.PacketHandlers.Skills;
using NosSmooth.Game.PacketHandlers.Specialists;
using NosSmooth.Packets.Server.Raids;

namespace NosSmooth.Game.Extensions;



@@ 43,17 45,35 @@ public static class ServiceCollectionExtensions
        serviceCollection.TryAddSingleton<Game>();

        serviceCollection

            // act4
            .AddPacketResponder<FcResponder>()

            // character
            .AddPacketResponder<CharacterInitResponder>()
            .AddPacketResponder<WalkResponder>()

            // skills
            .AddPacketResponder<PlayerSkillResponder>()
            .AddPacketResponder<MatesSkillResponder>()
            .AddPacketResponder<WalkResponder>()
            .AddPacketResponder<SkillUsedResponder>()

            // friends
            .AddPacketResponder<FriendInitResponder>()

            // inventory
            .AddPacketResponder<InventoryInitResponder>()

            // groups
            .AddPacketResponder<GroupInitResponder>()

            // mates
            .AddPacketResponder<MatesInitResponder>()

            // skills
            .AddPacketResponder<AoeSkillUsedResponder>()

            // map
            .AddPacketResponder<AtResponder>()
            .AddPacketResponder<CMapResponder>()
            .AddPacketResponder<DropResponder>()


@@ 61,11 81,22 @@ public static class ServiceCollectionExtensions
            .AddPacketResponder<InResponder>()
            .AddPacketResponder<MoveResponder>()
            .AddPacketResponder<OutResponder>()

            // hp, mp
            .AddPacketResponder<StatPacketResponder>()
            .AddPacketResponder<StPacketResponder>()
            .AddPacketResponder<CondPacketResponder>()

            // equip
            .AddPacketResponder<SpResponder>()
            .AddPacketResponder<EqResponder>();
            .AddPacketResponder<EqResponder>()

            // raids
            .AddPacketResponder<RaidBfResponder>()
            .AddPacketResponder<RaidMbfResponder>()
            .AddPacketResponder<RaidResponder>()
            .AddPacketResponder<RbossResponder>()
            .AddPacketResponder<RdlstResponder>();

        serviceCollection
            .AddTransient<DialogHandler>()


@@ 102,13 133,16 @@ public static class ServiceCollectionExtensions
    /// <returns>The collection.</returns>
    public static IServiceCollection AddGameResponder(this IServiceCollection serviceCollection, Type gameResponder)
    {
        if (!gameResponder.GetInterfaces().Any(
        if (!gameResponder.GetInterfaces().Any
            (
                i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IGameResponder<>)
            ))
        {
            throw new ArgumentException(
            throw new ArgumentException
            (
                $"{nameof(gameResponder)} should implement IGameResponder.",
                nameof(gameResponder));
                nameof(gameResponder)
            );
        }

        var handlerTypeInterfaces = gameResponder.GetInterfaces();


@@ 124,4 158,4 @@ public static class ServiceCollectionExtensions

        return serviceCollection;
    }
}
}
\ No newline at end of file

M Core/NosSmooth.Game/Game.cs => Core/NosSmooth.Game/Game.cs +54 -0
@@ 359,6 359,60 @@ public class Game : IStatefulEntity
        );
    }

    /// <summary>
    /// Updates the current raid, if it is not null.
    /// </summary>
    /// <param name="update">The function for updating the raid.</param>
    /// <param name="releaseSemaphore">Whether to release the semaphore used for changing the raid.</param>
    /// <param name="ct">The cancellation token for cancelling the operation.</param>
    /// <returns>The updated raid.</returns>
    internal Task<Raid?> UpdateRaidAsync
    (
        Func<Raid, Raid?> update,
        bool releaseSemaphore = true,
        CancellationToken ct = default
    )
    {
        return CreateOrUpdateAsync
        (
            GameSemaphoreType.Raid,
            () => CurrentRaid,
            m => CurrentRaid = m,
            () => null,
            update,
            releaseSemaphore,
            ct
        );
    }

    /// <summary>
    /// Creates the raid if it is null, or updates the current raid.
    /// </summary>
    /// <param name="create">The function for creating the raid.</param>
    /// <param name="update">The function for updating the raid.</param>
    /// <param name="releaseSemaphore">Whether to release the semaphore used for changing the raid.</param>
    /// <param name="ct">The cancellation token for cancelling the operation.</param>
    /// <returns>The updated raid.</returns>
    internal Task<Raid?> CreateOrUpdateRaidAsync
    (
        Func<Raid?> create,
        Func<Raid, Raid?> update,
        bool releaseSemaphore = true,
        CancellationToken ct = default
    )
    {
        return CreateOrUpdateAsync
        (
            GameSemaphoreType.Raid,
            () => CurrentRaid,
            m => CurrentRaid = m,
            create,
            update,
            releaseSemaphore,
            ct
        );
    }

    private async Task<T> CreateAsync<T>
    (
        GameSemaphoreType type,

A Core/NosSmooth.Game/PacketHandlers/Raids/RaidBfResponder.cs => Core/NosSmooth.Game/PacketHandlers/Raids/RaidBfResponder.cs +69 -0
@@ 0,0 1,69 @@
//
//  RaidBfResponder.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Data;
using NosSmooth.Core.Packets;
using NosSmooth.Game.Data.Raids;
using NosSmooth.Game.Events.Core;
using NosSmooth.Game.Events.Raids;
using NosSmooth.Packets.Enums.Raids;
using NosSmooth.Packets.Server.Raids;
using Remora.Results;

namespace NosSmooth.Game.PacketHandlers.Raids;

/// <summary>
/// A responder to <see cref="RaidBfPacket"/>.
/// </summary>
public class RaidBfResponder : IPacketResponder<RaidBfPacket>
{
    private readonly Game _game;
    private readonly EventDispatcher _eventDispatcher;

    /// <summary>
    /// Initializes a new instance of the <see cref="RaidBfResponder"/> class.
    /// </summary>
    /// <param name="game">The game.</param>
    /// <param name="eventDispatcher">The event dispatcher.</param>
    public RaidBfResponder(Game game, EventDispatcher eventDispatcher)
    {
        _game = game;
        _eventDispatcher = eventDispatcher;
    }

    /// <inheritdoc />
    public async Task<Result> Respond(PacketEventArgs<RaidBfPacket> packetArgs, CancellationToken ct = default)
    {
        var packet = packetArgs.Packet;
        Raid? previousRaid = null;
        var currentRaid = await _game.UpdateRaidAsync
        (
            raid =>
            {
                previousRaid = raid;
                return raid with
                {
                    State = packet.WindowType switch
                    {
                        RaidBfPacketType.MissionStarted => RaidState.Started,
                        RaidBfPacketType.MissionCleared => RaidState.EndedSuccessfully,
                        _ => RaidState
                            .TeamFailed // TODO: figure out whether OutOfLives is sent for both individual member and whole team
                    }
                };
            },
            ct: ct
        );

        if (previousRaid is not null && currentRaid is not null && previousRaid.State != currentRaid.State)
        {
            return await _eventDispatcher.DispatchEvent
                (new RaidStateChangedEvent(previousRaid.State, currentRaid.State, currentRaid), ct);
        }

        return Result.FromSuccess();
    }
}
\ No newline at end of file

A Core/NosSmooth.Game/PacketHandlers/Raids/RaidMbfResponder.cs => Core/NosSmooth.Game/PacketHandlers/Raids/RaidMbfResponder.cs +53 -0
@@ 0,0 1,53 @@
//
//  RaidMbfResponder.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using NosSmooth.Core.Packets;
using NosSmooth.Game.Data.Raids;
using NosSmooth.Packets.Server.Raids;
using Remora.Results;

namespace NosSmooth.Game.PacketHandlers.Raids;

/// <summary>
/// A responder to <see cref="RaidMbfPacket"/>.
/// </summary>
public class RaidMbfResponder : IPacketResponder<RaidMbfPacket>
{
    private readonly Game _game;

    /// <summary>
    /// Initializes a new instance of the <see cref="RaidMbfResponder"/> class.
    /// </summary>
    /// <param name="game">The game.</param>
    public RaidMbfResponder(Game game)
    {
        _game = game;
    }

    /// <inheritdoc />
    public async Task<Result> Respond(PacketEventArgs<RaidMbfPacket> packetArgs, CancellationToken ct = default)
    {
        var packet = packetArgs.Packet;

        await _game.UpdateRaidAsync
        (
            raid => raid with
            {
                Progress = new RaidProgress
                (
                    packet.MonsterLockerInitial,
                    packet.MonsterLockerCurrent,
                    packet.ButtonLockerInitial,
                    packet.ButtonLockerCurrent,
                    packet.CurrentLives,
                    packet.InitialLives
                )
            },
            ct: ct
        );
        return Result.FromSuccess();
    }
}
\ No newline at end of file

A Core/NosSmooth.Game/PacketHandlers/Raids/RaidResponder.cs => Core/NosSmooth.Game/PacketHandlers/Raids/RaidResponder.cs +114 -0
@@ 0,0 1,114 @@
//
//  RaidResponder.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Net.Http.Headers;
using NosSmooth.Core.Packets;
using NosSmooth.Game.Data.Info;
using NosSmooth.Game.Data.Raids;
using NosSmooth.Game.Data.Social;
using NosSmooth.Game.Events.Core;
using NosSmooth.Game.Events.Raids;
using NosSmooth.Packets.Enums.Raids;
using NosSmooth.Packets.Server.Raids;
using Remora.Results;

namespace NosSmooth.Game.PacketHandlers.Raids;

/// <summary>
/// A responder to <see cref="RaidPacket"/>.
/// </summary>
public class RaidResponder : IPacketResponder<RaidPacket>
{
    private readonly Game _game;
    private readonly EventDispatcher _eventDispatcher;

    /// <summary>
    /// Initializes a new instance of the <see cref="RaidResponder"/> class.
    /// </summary>
    /// <param name="game">The game.</param>
    /// <param name="eventDispatcher">The event dispatcher.</param>
    public RaidResponder(Game game, EventDispatcher eventDispatcher)
    {
        _game = game;
        _eventDispatcher = eventDispatcher;

    }

    /// <inheritdoc />
    public async Task<Result> Respond(PacketEventArgs<RaidPacket> packetArgs, CancellationToken ct = default)
    {
        var packet = packetArgs.Packet;
        if (packet.Type is not(RaidPacketType.Leader or RaidPacketType.ListMembers or RaidPacketType.PlayerHealths or RaidPacketType.Leave))
        {
            return Result.FromSuccess();
        }

        Raid? prevRaid = null;
        var currentRaid = await _game.UpdateRaidAsync
        (
            raid =>
            {
                prevRaid = raid;
                switch (packet.Type)
                {
                    case RaidPacketType.Leave:
                        if (packet.LeaveType is not null && packet.LeaveType == RaidLeaveType.PlayerLeft)
                        { // the player has left.
                            prevRaid = raid with
                            {
                                State = RaidState.Left
                            };

                            return null;
                        }

                        return raid;
                    case RaidPacketType.Leader:
                        if (packet.LeaderId is null)
                        { // set the raid to null.
                            return null;
                        }

                        return raid with
                        {
                            Leader = raid.Members?.FirstOrDefault(x => x.PlayerId == packet.LeaderId.Value)
                        };
                    case RaidPacketType.ListMembers:
                        return raid with
                        {
                            Members = raid.Members?.Where(x => packet.ListMembersPlayerIds?.Contains(x.PlayerId) ?? true).ToList()
                        };
                    case RaidPacketType.PlayerHealths:
                        // update healths
                        foreach (var member in raid.Members ?? (IReadOnlyList<GroupMember>)Array.Empty<GroupMember>())
                        {
                            var data = packet.PlayerHealths?.FirstOrDefault(x => x.PlayerId == member.PlayerId);

                            if (data is not null)
                            {
                                member.Hp ??= new Health();
                                member.Mp ??= new Health();

                                member.Hp.Percentage = data.HpPercentage;
                                member.Mp.Percentage = data.MpPercentage;
                            }
                        }
                        return raid;
                }

                return raid;
            },
            ct: ct
        );

        if (currentRaid == null && prevRaid != null)
        {
            return await _eventDispatcher.DispatchEvent(new RaidFinishedEvent(prevRaid), ct);
        }

        return Result.FromSuccess();
    }
}
\ No newline at end of file

A Core/NosSmooth.Game/PacketHandlers/Raids/RbossResponder.cs => Core/NosSmooth.Game/PacketHandlers/Raids/RbossResponder.cs +80 -0
@@ 0,0 1,80 @@
//
//  RbossResponder.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using NosSmooth.Core.Packets;
using NosSmooth.Game.Data.Entities;
using NosSmooth.Game.Data.Raids;
using NosSmooth.Game.Events.Core;
using NosSmooth.Game.Events.Raids;
using NosSmooth.Packets.Server.Raids;
using Remora.Results;

namespace NosSmooth.Game.PacketHandlers.Raids;

/// <summary>
/// A responder to <see cref="RbossPacket"/>.
/// </summary>
public class RbossResponder : IPacketResponder<RbossPacket>
{
    private readonly Game _game;
    private readonly EventDispatcher _eventDispatcher;

    /// <summary>
    /// Initializes a new instance of the <see cref="RbossResponder"/> class.
    /// </summary>
    /// <param name="game">The game.</param>
    /// <param name="eventDispatcher">The event dispatcher.</param>
    public RbossResponder(Game game, EventDispatcher eventDispatcher)
    {
        _game = game;
        _eventDispatcher = eventDispatcher;
    }

    /// <inheritdoc />
    public async Task<Result> Respond(PacketEventArgs<RbossPacket> packetArgs, CancellationToken ct = default)
    {
        var packet = packetArgs.Packet;
        var map = _game.CurrentMap;
        if (map is null)
        {
            return Result.FromSuccess();
        }

        var bossEntity = packet.EntityId is not null ? map.Entities.GetEntity<Monster>(packet.EntityId.Value) : null;

        RaidState? previousState = null;
        var currentRaid = await _game.UpdateRaidAsync
        (
            raid =>
            {
                previousState = raid.State;
                if (bossEntity is not null && (raid.Bosses is null || !raid.Bosses.Contains(bossEntity)))
                {
                    return raid with
                    {
                        Boss = bossEntity,
                        Bosses = (raid.Bosses ?? Array.Empty<Monster>()).Append(bossEntity).ToList(),
                        State = RaidState.BossFight
                    };
                }

                return raid with
                { // this will oscillate between more bosses ...
                    Boss = bossEntity
                };
            },
            ct: ct
        );

        if (currentRaid is not null && previousState is not null && previousState != currentRaid.State)
        {
            return await _eventDispatcher.DispatchEvent
                (new RaidStateChangedEvent(previousState.Value, currentRaid.State, currentRaid), ct);
        }

        return Result.FromSuccess();
    }
}
\ No newline at end of file

A Core/NosSmooth.Game/PacketHandlers/Raids/RdlstResponder.cs => Core/NosSmooth.Game/PacketHandlers/Raids/RdlstResponder.cs +98 -0
@@ 0,0 1,98 @@
//
//  RdlstResponder.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using NosSmooth.Core.Packets;
using NosSmooth.Game.Data.Raids;
using NosSmooth.Game.Data.Social;
using NosSmooth.Game.Events.Core;
using NosSmooth.Game.Events.Raids;
using NosSmooth.Packets.Server.Raids;
using Remora.Results;

namespace NosSmooth.Game.PacketHandlers.Raids;

/// <summary>
/// A responder to <see cref="RdlstPacket"/>.
/// </summary>
public class RdlstResponder : IPacketResponder<RdlstPacket>
{
    private readonly Game _game;
    private readonly EventDispatcher _eventDispatcher;

    /// <summary>
    /// Initializes a new instance of the <see cref="RdlstResponder"/> class.
    /// </summary>
    /// <param name="game">The game.</param>
    /// <param name="eventDispatcher">The event dispatcher.</param>
    public RdlstResponder(Game game, EventDispatcher eventDispatcher)
    {
        _game = game;
        _eventDispatcher = eventDispatcher;
    }

    /// <inheritdoc />
    public async Task<Result> Respond(PacketEventArgs<RdlstPacket> packetArgs, CancellationToken ct = default)
    {
        var packet = packetArgs.Packet;

        IReadOnlyList<GroupMember> UpdateMembers(IReadOnlyList<GroupMember>? currentMembers)
        {
            return packet.Players
                .Select
                (
                    packetMember =>
                    {
                        var newMember = currentMembers?.FirstOrDefault
                            (member => packetMember.Id == member.PlayerId) ?? new GroupMember(packetMember.Id);

                        newMember.Class = packetMember.Class;
                        newMember.Level = packetMember.Level;
                        newMember.HeroLevel = packetMember.HeroLevel;
                        newMember.Sex = packetMember.Sex;
                        newMember.MorphVNum = packetMember.MorphVNum;

                        return newMember;
                    }
                ).ToArray();
        }

        Raid? prevRaid = null;
        var currentRaid = await _game.CreateOrUpdateRaidAsync
        (
            () => new Raid
            (
                packet.RaidType,
                RaidState.Waiting,
                packet.MinimumLevel,
                packet.MaximumLevel,
                null,
                null,
                null,
                null,
                null
            ),
            raid =>
            {
                prevRaid = raid;
                return raid with
                {
                    Type = packet.RaidType,
                    MinimumLevel = packet.MinimumLevel,
                    MaximumLevel = packet.MaximumLevel,
                    Members = UpdateMembers(raid.Members),
                };
            },
            ct: ct
        );

        if (prevRaid is null && currentRaid is not null)
        {
            return await _eventDispatcher.DispatchEvent(new RaidJoinedEvent(currentRaid), ct);
        }

        return Result.FromSuccess();
    }
}
\ No newline at end of file

A Packets/NosSmooth.Packets/Enums/Raids/RaidBfPacketType.cs => Packets/NosSmooth.Packets/Enums/Raids/RaidBfPacketType.cs +25 -0
@@ 0,0 1,25 @@
//
//  RaidBfPacketType.cs
//
//  Copyright (c) František Boháček. All rights reserved.
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Diagnostics.CodeAnalysis;
using NosSmooth.Packets.Server.Raids;
#pragma warning disable CS1591

namespace NosSmooth.Packets.Enums.Raids;

/// <summary>
/// A type of <see cref="RaidBfPacket"/>.
/// </summary>
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1602:Enumeration items should be documented", Justification = "Self-explanatory.")]
public enum RaidBfPacketType
{
    MissionStarted = 0,
    MissionCleared = 1,
    TimeUp = 2,
    LeaderDied = 3,
    NoLivesLeft = 4,
    MissionFailed = 5
}
\ No newline at end of file

Do not follow this link