| | | 1 | | //======================================================================= |
| | | 2 | | // TraversalTransitionDefinition.cs |
| | | 3 | | //======================================================================= |
| | | 4 | | // MIT License, Copyright (c) 2024-present David Oravsky (mrdav30) |
| | | 5 | | // See LICENSE file in the project root for full license information. |
| | | 6 | | //======================================================================= |
| | | 7 | | |
| | | 8 | | using System; |
| | | 9 | | using FixedMathSharp; |
| | | 10 | | using GridForge.Spatial; |
| | | 11 | | using SwiftCollections.Utility; |
| | | 12 | | |
| | | 13 | | namespace Trailblazer.Pathing; |
| | | 14 | | |
| | | 15 | | /// <summary> |
| | | 16 | | /// Describes one directed, source-map-owned semantic traversal action. |
| | | 17 | | /// </summary> |
| | | 18 | | public readonly struct TraversalTransitionDefinition : IEquatable<TraversalTransitionDefinition> |
| | | 19 | | { |
| | | 20 | | /// <summary> |
| | | 21 | | /// The stable map-local transition identifier. |
| | | 22 | | /// </summary> |
| | | 23 | | public string Id { get; } |
| | | 24 | | |
| | | 25 | | /// <summary> |
| | | 26 | | /// The semantic action performed by this transition. |
| | | 27 | | /// </summary> |
| | | 28 | | public TraversalTransitionType Type { get; } |
| | | 29 | | |
| | | 30 | | /// <summary> |
| | | 31 | | /// The local source-cell index in the owning map. |
| | | 32 | | /// </summary> |
| | | 33 | | public VoxelIndex SourceIndex { get; } |
| | | 34 | | |
| | | 35 | | /// <summary> |
| | | 36 | | /// The traversal medium used at the source endpoint. |
| | | 37 | | /// </summary> |
| | | 38 | | public TraversalMedium SourceMedium { get; } |
| | | 39 | | |
| | | 40 | | /// <summary> |
| | | 41 | | /// The durable destination-cell address. |
| | | 42 | | /// </summary> |
| | | 43 | | public NavigationCellAddress Destination { get; } |
| | | 44 | | |
| | | 45 | | /// <summary> |
| | | 46 | | /// The traversal medium used at the destination endpoint. |
| | | 47 | | /// </summary> |
| | | 48 | | public TraversalMedium DestinationMedium { get; } |
| | | 49 | | |
| | | 50 | | /// <summary> |
| | | 51 | | /// Whether the source endpoint overrides its cell anchor. |
| | | 52 | | /// </summary> |
| | | 53 | | public bool HasSourcePointOverride { get; } |
| | | 54 | | |
| | | 55 | | /// <summary> |
| | | 56 | | /// The exact source endpoint when <see cref="HasSourcePointOverride"/> is true. |
| | | 57 | | /// </summary> |
| | | 58 | | public Vector3d SourcePointOverride { get; } |
| | | 59 | | |
| | | 60 | | /// <summary> |
| | | 61 | | /// Whether the destination endpoint overrides its cell anchor. |
| | | 62 | | /// </summary> |
| | | 63 | | public bool HasDestinationPointOverride { get; } |
| | | 64 | | |
| | | 65 | | /// <summary> |
| | | 66 | | /// The exact destination endpoint when <see cref="HasDestinationPointOverride"/> is true. |
| | | 67 | | /// </summary> |
| | | 68 | | public Vector3d DestinationPointOverride { get; } |
| | | 69 | | |
| | | 70 | | /// <summary> |
| | | 71 | | /// The complete set of capabilities required to use this transition. |
| | | 72 | | /// </summary> |
| | | 73 | | public TraversalCapability RequiredCapabilities { get; } |
| | | 74 | | |
| | | 75 | | /// <summary> |
| | | 76 | | /// The non-negative surcharge for performing this semantic action. |
| | | 77 | | /// </summary> |
| | | 78 | | public Fixed64 ActionCost { get; } |
| | | 79 | | |
| | | 80 | | /// <summary> |
| | | 81 | | /// The authored built-in locomotion intent for this semantic action. |
| | | 82 | | /// </summary> |
| | | 83 | | public TraversalTransitionLocomotionHints LocomotionHints { get; } |
| | | 84 | | |
| | | 85 | | /// <summary> |
| | | 86 | | /// Creates one complete directed semantic transition definition. |
| | | 87 | | /// </summary> |
| | | 88 | | public TraversalTransitionDefinition( |
| | | 89 | | string id, |
| | | 90 | | TraversalTransitionType type, |
| | | 91 | | VoxelIndex sourceIndex, |
| | | 92 | | TraversalMedium sourceMedium, |
| | | 93 | | NavigationCellAddress destination, |
| | | 94 | | TraversalMedium destinationMedium, |
| | | 95 | | TraversalCapability requiredCapabilities = TraversalCapability.None, |
| | | 96 | | Fixed64 actionCost = default, |
| | | 97 | | TraversalTransitionLocomotionHints locomotionHints = TraversalTransitionLocomotionHints.None, |
| | | 98 | | Vector3d sourcePointOverride = default, |
| | | 99 | | bool hasSourcePointOverride = false, |
| | | 100 | | Vector3d destinationPointOverride = default, |
| | | 101 | | bool hasDestinationPointOverride = false) |
| | | 102 | | { |
| | 4297 | 103 | | SwiftThrowHelper.ThrowIfArgument( |
| | 4297 | 104 | | string.IsNullOrWhiteSpace(id), |
| | 4297 | 105 | | nameof(id), |
| | 4297 | 106 | | "Transition ID cannot be null, empty, or whitespace."); |
| | 4297 | 107 | | SwiftThrowHelper.ThrowIfArgument( |
| | 4297 | 108 | | !IsKnownType(type), |
| | 4297 | 109 | | nameof(type), |
| | 4297 | 110 | | "Transition type is unknown."); |
| | 4296 | 111 | | SwiftThrowHelper.ThrowIfArgument( |
| | 4296 | 112 | | !IsKnownMedium(sourceMedium), |
| | 4296 | 113 | | nameof(sourceMedium), |
| | 4296 | 114 | | "Source medium must identify one known traversal medium."); |
| | 4296 | 115 | | SwiftThrowHelper.ThrowIfArgument( |
| | 4296 | 116 | | !IsKnownMedium(destinationMedium), |
| | 4296 | 117 | | nameof(destinationMedium), |
| | 4296 | 118 | | "Destination medium must identify one known traversal medium."); |
| | 4296 | 119 | | SwiftThrowHelper.ThrowIfArgument( |
| | 4296 | 120 | | (requiredCapabilities & ~NavigationCell.KnownCapabilities) != 0, |
| | 4296 | 121 | | nameof(requiredCapabilities), |
| | 4296 | 122 | | "Required capabilities contain an unknown bit."); |
| | 4296 | 123 | | SwiftThrowHelper.ThrowIfArgument( |
| | 4296 | 124 | | actionCost < Fixed64.Zero, |
| | 4296 | 125 | | nameof(actionCost), |
| | 4296 | 126 | | "Action cost must be non-negative."); |
| | 4296 | 127 | | SwiftThrowHelper.ThrowIfArgument( |
| | 4296 | 128 | | (locomotionHints & ~TraversalTransitionRule.KnownLocomotionHints) != 0, |
| | 4296 | 129 | | nameof(locomotionHints), |
| | 4296 | 130 | | "Locomotion hints contain an unknown bit."); |
| | 4296 | 131 | | SwiftThrowHelper.ThrowIfArgument( |
| | 4296 | 132 | | string.IsNullOrWhiteSpace(destination.MapId), |
| | 4296 | 133 | | nameof(destination), |
| | 4296 | 134 | | "Destination must contain a valid map ID."); |
| | | 135 | | |
| | 4296 | 136 | | Id = id; |
| | 4296 | 137 | | Type = type; |
| | 4296 | 138 | | SourceIndex = sourceIndex; |
| | 4296 | 139 | | SourceMedium = sourceMedium; |
| | 4296 | 140 | | Destination = destination; |
| | 4296 | 141 | | DestinationMedium = destinationMedium; |
| | 4296 | 142 | | RequiredCapabilities = requiredCapabilities; |
| | 4296 | 143 | | ActionCost = actionCost; |
| | 4296 | 144 | | LocomotionHints = locomotionHints; |
| | 4296 | 145 | | SourcePointOverride = hasSourcePointOverride ? sourcePointOverride : default; |
| | 4296 | 146 | | HasSourcePointOverride = hasSourcePointOverride; |
| | 4296 | 147 | | DestinationPointOverride = hasDestinationPointOverride ? destinationPointOverride : default; |
| | 4296 | 148 | | HasDestinationPointOverride = hasDestinationPointOverride; |
| | 4296 | 149 | | } |
| | | 150 | | |
| | | 151 | | /// <inheritdoc/> |
| | | 152 | | public bool Equals(TraversalTransitionDefinition other) => |
| | 4215 | 153 | | string.Equals(Id, other.Id, StringComparison.Ordinal) |
| | 4215 | 154 | | && Type == other.Type |
| | 4215 | 155 | | && SourceIndex.Equals(other.SourceIndex) |
| | 4215 | 156 | | && SourceMedium == other.SourceMedium |
| | 4215 | 157 | | && Destination.Equals(other.Destination) |
| | 4215 | 158 | | && DestinationMedium == other.DestinationMedium |
| | 4215 | 159 | | && HasSourcePointOverride == other.HasSourcePointOverride |
| | 4215 | 160 | | && SourcePointOverride == other.SourcePointOverride |
| | 4215 | 161 | | && HasDestinationPointOverride == other.HasDestinationPointOverride |
| | 4215 | 162 | | && DestinationPointOverride == other.DestinationPointOverride |
| | 4215 | 163 | | && RequiredCapabilities == other.RequiredCapabilities |
| | 4215 | 164 | | && ActionCost == other.ActionCost |
| | 4215 | 165 | | && LocomotionHints == other.LocomotionHints; |
| | | 166 | | |
| | | 167 | | /// <inheritdoc/> |
| | | 168 | | public override bool Equals(object? obj) => |
| | 14 | 169 | | obj is TraversalTransitionDefinition other && Equals(other); |
| | | 170 | | |
| | | 171 | | /// <inheritdoc/> |
| | | 172 | | public override int GetHashCode() |
| | | 173 | | { |
| | 15 | 174 | | int idHash = Id == null |
| | 15 | 175 | | ? 0 |
| | 15 | 176 | | : SwiftHashTools.GetDeterministicStringEqualityComparer().GetHashCode(Id); |
| | 15 | 177 | | int hash = SwiftHashTools.CombineHashCodes(idHash, (int)Type); |
| | 15 | 178 | | hash = SwiftHashTools.CombineHashCodes(hash, SourceIndex.GetHashCode()); |
| | 15 | 179 | | hash = SwiftHashTools.CombineHashCodes(hash, (int)SourceMedium); |
| | 15 | 180 | | hash = SwiftHashTools.CombineHashCodes(hash, Destination.GetHashCode()); |
| | 15 | 181 | | hash = SwiftHashTools.CombineHashCodes(hash, (int)DestinationMedium); |
| | 15 | 182 | | hash = SwiftHashTools.CombineHashCodes(hash, HasSourcePointOverride ? 1 : 0); |
| | 15 | 183 | | hash = SwiftHashTools.CombineHashCodes(hash, SourcePointOverride.GetHashCode()); |
| | 15 | 184 | | hash = SwiftHashTools.CombineHashCodes(hash, HasDestinationPointOverride ? 1 : 0); |
| | 15 | 185 | | hash = SwiftHashTools.CombineHashCodes(hash, DestinationPointOverride.GetHashCode()); |
| | 15 | 186 | | hash = SwiftHashTools.CombineHashCodes(hash, (int)RequiredCapabilities); |
| | 15 | 187 | | hash = SwiftHashTools.CombineHashCodes(hash, ActionCost.GetHashCode()); |
| | 15 | 188 | | return SwiftHashTools.CombineHashCodes(hash, (int)LocomotionHints); |
| | | 189 | | } |
| | | 190 | | |
| | | 191 | | internal static bool IsKnownMedium(TraversalMedium medium) => |
| | 12239 | 192 | | medium == TraversalMedium.Solid |
| | 12239 | 193 | | || medium == TraversalMedium.Gas |
| | 12239 | 194 | | || medium == TraversalMedium.Liquid; |
| | | 195 | | |
| | | 196 | | internal static bool IsKnownType(TraversalTransitionType type) => |
| | 4467 | 197 | | type >= TraversalTransitionType.Custom && type <= TraversalTransitionType.Climb; |
| | | 198 | | |
| | | 199 | | /// <summary> |
| | | 200 | | /// Tests two definitions for value equality. |
| | | 201 | | /// </summary> |
| | | 202 | | public static bool operator ==( |
| | | 203 | | TraversalTransitionDefinition left, |
| | 1 | 204 | | TraversalTransitionDefinition right) => left.Equals(right); |
| | | 205 | | |
| | | 206 | | /// <summary> |
| | | 207 | | /// Tests two definitions for value inequality. |
| | | 208 | | /// </summary> |
| | | 209 | | public static bool operator !=( |
| | | 210 | | TraversalTransitionDefinition left, |
| | 1 | 211 | | TraversalTransitionDefinition right) => !left.Equals(right); |
| | | 212 | | } |