namespace Theriapolis.Core.Rules.Combat; /// /// Per-attack situation modifiers. Flags compose: a Sneak Attack with /// Advantage and Disadvantage (e.g. attacker prone, target shadowed) /// cancels to a normal roll per d20 rules. /// /// Phase 5 M4 wires the basic six (Advantage/Disadvantage and the four /// resolver-time tags); class-feature flags like Reckless Attack come in /// M6 once the feature engine reads from this enum. /// [System.Flags] public enum SituationFlags : uint { None = 0, Advantage = 1u << 0, Disadvantage = 1u << 1, /// Attacker is at long range — disadvantage on the roll per d20. LongRange = 1u << 2, /// Attacker has reach + a melee weapon vs. a target that has cover. HalfCover = 1u << 3, ThreeQuartersCover= 1u << 4, /// Attacker meets the Sneak Attack precondition (advantage or ally adjacent). SneakAttackEligible = 1u << 5, /// Attacker is firing a ranged weapon at a target within 5 ft. — disadvantage. RangedInMelee = 1u << 6, } public static class SituationFlagsExtensions { /// /// True when the situation should roll the d20 with advantage. Per /// d20 rules, advantage and disadvantage cancel exactly (no doubling). /// public static bool RollsAdvantage(this SituationFlags f) { bool adv = (f & SituationFlags.Advantage) != 0; bool dis = (f & SituationFlags.Disadvantage) != 0 || (f & SituationFlags.LongRange) != 0 || (f & SituationFlags.RangedInMelee) != 0; return adv && !dis; } /// True when the situation rolls with disadvantage (and no compensating advantage). public static bool RollsDisadvantage(this SituationFlags f) { bool adv = (f & SituationFlags.Advantage) != 0; bool dis = (f & SituationFlags.Disadvantage) != 0 || (f & SituationFlags.LongRange) != 0 || (f & SituationFlags.RangedInMelee) != 0; return dis && !adv; } /// Cover modifier applied to AC: 0 / 2 / 5. public static int CoverAcBonus(this SituationFlags f) { if ((f & SituationFlags.ThreeQuartersCover) != 0) return 5; if ((f & SituationFlags.HalfCover) != 0) return 2; return 0; } }