~ruther/NosSmooth

ref: 84a3b6fe29ea3fecdeb02b89c3f642174223f1f3 NosSmooth/Extensions/NosSmooth.Extensions.Combat/Policies/EnemyPolicy.cs -rw-r--r-- 5.0 KiB
84a3b6fe — Rutherther chore: add rest of version prefixes 2 years ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
//
//  EnemyPolicy.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.SqlTypes;
using NosSmooth.Extensions.Combat.Errors;
using NosSmooth.Extensions.Combat.Selectors;
using NosSmooth.Game.Data.Characters;
using NosSmooth.Game.Data.Entities;
using NosSmooth.Game.Data.Info;
using Remora.Results;

namespace NosSmooth.Extensions.Combat.Policies;

/// <summary>
/// An enemy selector policy that selects the monsters based on their vnums.
/// </summary>
/// <param name="SelectPolicy">The policy to select an enemy.</param>
/// <param name="MonsterVNums">The vnums of the monsters to target.</param>
/// <param name="CombatArea">The area in which to get enemies from.</param>
public record EnemyPolicy
(
    EnemySelectPolicy SelectPolicy,
    int[]? MonsterVNums = default,
    CombatArea? CombatArea = default
) : IEnemySelector
{
    /// <inheritdoc />
    public Result<ILivingEntity> GetSelectedEntity(ICombatState combatState, ICollection<ILivingEntity> possibleTargets)
    {
        var targets = possibleTargets.OfType<Monster>();
        if (MonsterVNums is not null)
        {
            targets = targets.Where(x => MonsterVNums.Contains(x.VNum));
        }

        if (!targets.Any())
        {
            return new EntityNotFoundError();
        }

        if (combatState.Game.Character is null)
        {
            return new CharacterNotInitializedError();
        }

        var position = combatState.Game.Character.Position;
        if (position is null)
        {
            return new CharacterNotInitializedError();
        }

        var characterPosition = position.Value;
        ILivingEntity? target = null;
        switch (SelectPolicy)
        {
            case EnemySelectPolicy.Aggressive:
                throw new NotImplementedException(); // TODO: implement aggressive policy
            case EnemySelectPolicy.Closest:
                target = targets
                    .Where(x => x.Position is not null && (CombatArea?.IsInside(x.Position.Value) ?? true))
                    .MinBy(x => x.Position!.Value.DistanceSquared(characterPosition))!;
                break;
            case EnemySelectPolicy.LowestHealth:
                target = targets.MinBy
                (
                    x =>
                    {
                        if (x.Hp is null)
                        {
                            return int.MaxValue;
                        }

                        if (x.Hp.Amount is not null)
                        {
                            return x.Hp.Amount;
                        }

                        if (x.Hp.Percentage is not null && x.Level is not null)
                        {
                            return x.Hp.Percentage * 100 * x.Level;
                        }

                        if (x.Hp.Maximum is not null)
                        {
                            return x.Hp.Maximum; // Assume max health, best guess.
                        }

                        return int.MaxValue;
                    }
                );
                break;
        }

        if (target is null)
        {
            return new EntityNotFoundError();
        }

        return Result<ILivingEntity>.FromSuccess(target);
    }
}

/// <summary>
/// A policy enemy selector.
/// </summary>
public enum EnemySelectPolicy
{
    /// <summary>
    /// Select the enemy with the lowest health.
    /// </summary>
    LowestHealth,

    /// <summary>
    /// Selects the enemy that targets the user.
    /// </summary>
    Aggressive,

    /// <summary>
    /// Selects the enemy that is the closest to the character.
    /// </summary>
    Closest
}

/// <summary>
/// The combat area around which to find enemies.
/// </summary>
/// <param name="CenterX">The area center x coordinate.</param>
/// <param name="CenterY">The area center y coordinate.</param>
/// <param name="Range">The maximum range from the center.</param>
public record CombatArea(short CenterX, short CenterY, short Range)
{
    /// <summary>
    /// Create a combat area around a specified entity.
    /// </summary>
    /// <param name="entity">The entity.</param>
    /// <param name="range">The range.</param>
    /// <returns>The combat area.</returns>
    /// <exception cref="ArgumentException">If the entity does not have a position.</exception>
    public static CombatArea CreateAroundEntity(IEntity entity, short range)
    {
        var position = entity.Position;
        if (position is null)
        {
            throw new ArgumentException(nameof(entity));
        }

        return new CombatArea(position.Value.X, position.Value.Y, range);
    }

    /// <summary>
    /// Gets whether the position is inside of the combat area.
    /// </summary>
    /// <param name="position">The position.</param>
    /// <returns>Whether the position is inside.</returns>
    public bool IsInside(Position position)
    {
        return position.DistanceSquared(new Position(CenterX, CenterY)) < Range * Range;
    }
}
Do not follow this link