using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using Microsoft.CodeAnalysis; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ItemShareFix.Core")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("ItemShareFix.Core")] [assembly: AssemblyTitle("ItemShareFix.Core")] [assembly: AssemblyVersion("1.0.0.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace ItemShareFix.Core { public static class BlockingModalLifecyclePolicy { public const string PauseScreenControllerTypeName = "RoR2.UI.PauseScreenController"; public const string SimpleDialogBoxTypeName = "RoR2.UI.SimpleDialogBox"; public const string PickupPickerPanelTypeName = "RoR2.UI.PickupPickerPanel"; public static bool IsKnownBlockingModalTypeName(string? typeName) { if (!(typeName == "RoR2.UI.PauseScreenController") && !(typeName == "RoR2.UI.SimpleDialogBox")) { return typeName == "RoR2.UI.PickupPickerPanel"; } return true; } public static bool ShouldSeedObservedCandidate(bool knownBlockingType, bool sceneObjectValid) { return knownBlockingType && sceneObjectValid; } public static bool ShouldAddObservedInstance(bool instanceAlreadyCached) { return !instanceAlreadyCached; } public static bool LifecycleSeedRequiresGlobalDiscovery(bool observedCandidateCached) { return !observedCandidateCached; } public static bool SuppressionStateChanged(bool previousSuppressed, bool currentSuppressed) { return previousSuppressed != currentSuppressed; } public static bool PreserveRareFallbackCadence(float fallbackSeconds) { return fallbackSeconds >= 5f; } } public sealed class ClaimLedger { private readonly Dictionary _records = new Dictionary(); private readonly Dictionary _history = new Dictionary(); public IReadOnlyCollection Records => _records.Values; public IReadOnlyCollection HistoricalRecords => _history.Values; public ClaimRecord Ensure(SharedPickupKey pickup, ParticipantKey participant, ParticipantState state, int stage) { if (!TryEnsure(pickup, participant, state, stage, out ClaimRecord record)) { throw new InvalidOperationException("Historical pickup barrier rejected participant generation: " + pickup.ToString() + "/" + participant); } return record; } public bool TryEnsure(SharedPickupKey pickup, ParticipantKey participant, ParticipantState state, int stage, out ClaimRecord record) { ClaimKey key = new ClaimKey(pickup, participant); if (_records.TryGetValue(key, out ClaimRecord value)) { record = value; return true; } if (IsHistoricallyBlocked(pickup, participant.StableUser)) { record = null; return false; } ClaimState claimState = ((state == ParticipantState.FullyDead) ? ClaimState.Deferred : ClaimState.Pending); if (state == ParticipantState.Disconnected) { claimState = ClaimState.CancelledDisconnected; } ClaimRecord claimRecord = new ClaimRecord(key, claimState, stage); if (claimState == ClaimState.CancelledDisconnected) { claimRecord.TerminalStage = stage; SetHistory(pickup, participant.StableUser, HistoricalClaimState.CancelledDisconnected, stage); } _records.Add(key, claimRecord); record = claimRecord; return true; } public bool TryGet(SharedPickupKey pickup, ParticipantKey participant, out ClaimRecord record) { return _records.TryGetValue(new ClaimKey(pickup, participant), out record); } public bool IsHistoricallyBlocked(SharedPickupKey pickup, StableUserKey stableUser) { return _history.ContainsKey(new HistoricalClaimKey(pickup, stableUser)); } public bool TryGetHistorical(SharedPickupKey pickup, StableUserKey stableUser, out HistoricalClaimRecord record) { return _history.TryGetValue(new HistoricalClaimKey(pickup, stableUser), out record); } public IReadOnlyList HistoricalFor(StableUserKey stableUser) { return (from x in _history.Values where x.Key.StableUser.Equals(stableUser) orderby x.Key.Pickup.Value select x).ToArray(); } public bool MarkCollected(SharedPickupKey pickup, ParticipantKey participant, int stage) { if (!TryGet(pickup, participant, out ClaimRecord record)) { return false; } if (record.State != ClaimState.Pending) { return false; } record.State = ClaimState.Collected; record.TerminalStage = stage; SetHistory(pickup, participant.StableUser, HistoricalClaimState.Collected, stage); return true; } public int TransitionParticipant(ParticipantKey participant, ParticipantState from, ParticipantState to, int stage) { int num = 0; ClaimRecord[] array = _records.Values.Where((ClaimRecord x) => x.Key.Participant.Equals(participant)).ToArray(); foreach (ClaimRecord claimRecord in array) { switch (to) { case ParticipantState.Disconnected: if (claimRecord.State == ClaimState.Pending || claimRecord.State == ClaimState.Deferred) { claimRecord.State = ClaimState.CancelledDisconnected; claimRecord.TerminalStage = stage; SetHistory(claimRecord.Key.Pickup, participant.StableUser, HistoricalClaimState.CancelledDisconnected, stage); num++; } break; case ParticipantState.FullyDead: if (claimRecord.State == ClaimState.Pending) { claimRecord.State = ClaimState.Deferred; num++; } break; } } return num; } public bool MarkDeferredGranted(ClaimKey key, int stage) { if (!_records.TryGetValue(key, out ClaimRecord value)) { return false; } if (value.State != ClaimState.Deferred) { return false; } value.State = ClaimState.GrantedDeferred; value.TerminalStage = stage; SetHistory(value.Key.Pickup, value.Key.Participant.StableUser, HistoricalClaimState.GrantedDeferred, stage); return true; } public IReadOnlyList DeferredFor(ParticipantKey participant, int beforeStage) { return (from x in _records.Values where x.Key.Participant.Equals(participant) && x.State == ClaimState.Deferred && x.CreatedStage < beforeStage orderby x.CreatedStage, x.Key.Pickup.Value select x).ToArray(); } public int TransferPickup(SharedPickupKey oldPickup, SharedPickupKey newPickup) { if (oldPickup.Equals(newPickup)) { return 0; } ClaimRecord[] array = _records.Values.Where((ClaimRecord x) => x.Key.Pickup.Equals(oldPickup)).ToArray(); HistoricalClaimRecord[] array2 = _history.Values.Where((HistoricalClaimRecord x) => x.Key.Pickup.Equals(oldPickup)).ToArray(); ClaimRecord[] array3 = array; foreach (ClaimRecord claimRecord in array3) { ClaimKey key = new ClaimKey(newPickup, claimRecord.Key.Participant); if (_records.ContainsKey(key)) { throw new InvalidOperationException("Transfer would create duplicate pickup/participant state: " + key); } } HistoricalClaimRecord[] array4 = array2; foreach (HistoricalClaimRecord historicalClaimRecord in array4) { HistoricalClaimKey key2 = new HistoricalClaimKey(newPickup, historicalClaimRecord.Key.StableUser); if (_history.TryGetValue(key2, out HistoricalClaimRecord value) && value.State != historicalClaimRecord.State) { throw new InvalidOperationException("Transfer would create conflicting historical pickup/stable-user state: " + key2); } } array3 = array; foreach (ClaimRecord claimRecord2 in array3) { ClaimKey key3 = claimRecord2.Key; ClaimKey key4 = new ClaimKey(newPickup, key3.Participant); _records.Remove(key3); claimRecord2.Key = key4; _records.Add(key4, claimRecord2); } array4 = array2; foreach (HistoricalClaimRecord historicalClaimRecord2 in array4) { HistoricalClaimKey key5 = historicalClaimRecord2.Key; HistoricalClaimKey key6 = new HistoricalClaimKey(newPickup, key5.StableUser); _history.Remove(key5); if (_history.TryGetValue(key6, out HistoricalClaimRecord value2)) { if (historicalClaimRecord2.Stage > value2.Stage) { value2.Stage = historicalClaimRecord2.Stage; } } else { historicalClaimRecord2.Key = key6; _history.Add(key6, historicalClaimRecord2); } } return array.Length; } public int OnStageTransition(int newStage) { int num = 0; KeyValuePair[] array = _records.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair keyValuePair = array[i]; ClaimState state = keyValuePair.Value.State; if (state == ClaimState.Pending || state == ClaimState.Collected || state == ClaimState.CancelledDisconnected || state == ClaimState.GrantedDeferred) { _records.Remove(keyValuePair.Key); num++; } } _history.Clear(); return num; } public int Clear() { int result = _records.Count + _history.Count; _records.Clear(); _history.Clear(); return result; } public int RemoveTerminalOlderThan(int stageExclusive) { int num = 0; KeyValuePair[] array = _records.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair keyValuePair = array[i]; int? terminalStage = keyValuePair.Value.TerminalStage; if (terminalStage.HasValue && terminalStage.Value < stageExclusive) { _records.Remove(keyValuePair.Key); num++; } } KeyValuePair[] array2 = _history.ToArray(); for (int i = 0; i < array2.Length; i++) { KeyValuePair keyValuePair2 = array2[i]; if (keyValuePair2.Value.Stage < stageExclusive) { _history.Remove(keyValuePair2.Key); num++; } } return num; } private void SetHistory(SharedPickupKey pickup, StableUserKey stableUser, HistoricalClaimState state, int stage) { HistoricalClaimKey key = new HistoricalClaimKey(pickup, stableUser); if (_history.TryGetValue(key, out HistoricalClaimRecord value)) { if (value.State != state) { if (value.State == HistoricalClaimState.Collected || value.State == HistoricalClaimState.GrantedDeferred) { return; } value.State = state; } if (stage > value.Stage) { value.Stage = stage; } } else { _history.Add(key, new HistoricalClaimRecord(key, state, stage)); } } } public enum CommandOptionPickupSource { None, NestedPickup, DirectCompatibilityFallback } public readonly struct CommandOptionPickupDecision { public bool HasValue { get; } public T Value { get; } public CommandOptionPickupSource Source { get; } public bool ExactSource { get; } public bool Disagreement { get; } public CommandOptionPickupDecision(bool hasValue, T value, CommandOptionPickupSource source, bool exactSource, bool disagreement) { HasValue = hasValue; Value = value; Source = source; ExactSource = exactSource; Disagreement = disagreement; } } public static class CommandOptionSourcePolicy { public const string AuthoritativeSourceToken = "nested-pickup"; public const string FallbackSourceToken = "direct-fallback"; public const string UnresolvedSourceToken = "unresolved"; public static CommandOptionPickupDecision Resolve(bool nestedAvailable, T nestedValue, bool directAvailable, T directValue) { if (nestedAvailable) { bool disagreement = directAvailable && !EqualityComparer.Default.Equals(nestedValue, directValue); return new CommandOptionPickupDecision(hasValue: true, nestedValue, CommandOptionPickupSource.NestedPickup, exactSource: true, disagreement); } if (directAvailable) { return new CommandOptionPickupDecision(hasValue: true, directValue, CommandOptionPickupSource.DirectCompatibilityFallback, exactSource: false, disagreement: false); } return new CommandOptionPickupDecision(hasValue: false, default(T), CommandOptionPickupSource.None, exactSource: false, disagreement: false); } public static string SourceToken(CommandOptionPickupSource source) { return source switch { CommandOptionPickupSource.NestedPickup => "nested-pickup", CommandOptionPickupSource.DirectCompatibilityFallback => "direct-fallback", _ => "unresolved", }; } } public enum CommandShareabilityState { Unresolved, AllShareable, AllUnshareable, Mixed } public readonly struct CommandShareabilityDecision { public CommandShareabilityState State { get; } public bool MarkerEligible => State == CommandShareabilityState.AllShareable; public string DiagnosticToken => State switch { CommandShareabilityState.AllShareable => "all-shareable", CommandShareabilityState.AllUnshareable => "all-unshareable", CommandShareabilityState.Mixed => "mixed", _ => "unresolved", }; public string FilterReason => State switch { CommandShareabilityState.AllUnshareable => "upstream-not-shareable", CommandShareabilityState.Mixed => "mixed-shareability", CommandShareabilityState.Unresolved => "shareability-unresolved", _ => string.Empty, }; public CommandShareabilityDecision(CommandShareabilityState state) { State = state; } } public static class CommandShareabilityPolicy { public static CommandShareabilityDecision Evaluate(IEnumerable? optionShareability) { if (optionShareability == null) { return new CommandShareabilityDecision(CommandShareabilityState.Unresolved); } bool?[] array = optionShareability.ToArray(); if (array.Length == 0 || array.Any((bool? x) => !x.HasValue)) { return new CommandShareabilityDecision(CommandShareabilityState.Unresolved); } bool flag = array.Any((bool? x) => x == true); bool flag2 = array.Any((bool? x) => x == false); if (flag && flag2) { return new CommandShareabilityDecision(CommandShareabilityState.Mixed); } if (flag) { return new CommandShareabilityDecision(CommandShareabilityState.AllShareable); } return new CommandShareabilityDecision(CommandShareabilityState.AllUnshareable); } } public enum NetworkDestroyDisposition { IgnoreStillAuthoritative, HoldForAuthoritativeConfirmation } public static class DisconnectConfirmationPolicy { public static bool AllowsImmediateCancelFromNetworkDestroy => false; public static bool UsesObjectNamePrefabOrBodyHeuristics => false; public static NetworkDestroyDisposition EvaluateNetworkDestroy(bool participantResolved, bool authoritativePresence) { if (!(participantResolved && authoritativePresence)) { return NetworkDestroyDisposition.HoldForAuthoritativeConfirmation; } return NetworkDestroyDisposition.IgnoreStillAuthoritative; } public static bool ShouldConfirmDisconnect(bool participantAbsentFromAuthoritativeControllerList, bool absenceGraceElapsed) { return participantAbsentFromAuthoritativeControllerList && absenceGraceElapsed; } } public enum GenerationProbeState { NeverResolved, Resolved, Frozen } public enum UnsupportedProbeDisposition { NoOwnershipEstablished, FreezePreserveExistingState } public sealed class GenerationProbeGate { private ParticipantKey? _provenParticipant; public GenerationProbeState State { get; private set; } public bool HasProvenParticipant => _provenParticipant.HasValue; public ParticipantKey ProvenParticipant => _provenParticipant ?? throw new InvalidOperationException("No proven participant exists for this generation."); public bool CanCreateClaims => State == GenerationProbeState.Resolved; public bool CanUseHistoricalBarrier => HasProvenParticipant; public bool CanGrantDeferred(ParticipantState exactState) { if (State == GenerationProbeState.Resolved) { return exactState == ParticipantState.Alive; } return false; } public bool TryResolve(ParticipantKey participant) { if (_provenParticipant.HasValue && !_provenParticipant.Value.Equals(participant)) { return false; } _provenParticipant = participant; State = GenerationProbeState.Resolved; return true; } public UnsupportedProbeDisposition ObserveUnsupported() { if (!_provenParticipant.HasValue) { State = GenerationProbeState.NeverResolved; return UnsupportedProbeDisposition.NoOwnershipEstablished; } State = GenerationProbeState.Frozen; return UnsupportedProbeDisposition.FreezePreserveExistingState; } } public enum ImmediateGiveDecision { AllowUpstream, SuppressHistoricalBarrier, SuppressFullyDeadDeferred, SuppressExistingDeferredEntitlement } public static class ImmediateGivePolicy { public static ImmediateGiveDecision Decide(bool stableIdentityResolved, ParticipantState? participantState, bool deadPlayerDeferredItemsEnabled, bool historicalBarrierForCurrentPickup, bool deferredEntitlementReadyForCurrentPickup = false) { if (!stableIdentityResolved) { return ImmediateGiveDecision.AllowUpstream; } if (historicalBarrierForCurrentPickup) { return ImmediateGiveDecision.SuppressHistoricalBarrier; } if (deferredEntitlementReadyForCurrentPickup) { if (participantState.HasValue && participantState.Value == ParticipantState.FullyDead && deadPlayerDeferredItemsEnabled) { return ImmediateGiveDecision.SuppressFullyDeadDeferred; } return ImmediateGiveDecision.SuppressExistingDeferredEntitlement; } return ImmediateGiveDecision.AllowUpstream; } public static bool ShouldSuppress(bool stableIdentityResolved, ParticipantState? participantState, bool deadPlayerDeferredItemsEnabled, bool historicalBarrierForCurrentPickup, bool deferredEntitlementReadyForCurrentPickup = false) { return Decide(stableIdentityResolved, participantState, deadPlayerDeferredItemsEnabled, historicalBarrierForCurrentPickup, deferredEntitlementReadyForCurrentPickup) != ImmediateGiveDecision.AllowUpstream; } } public static class ItemShareActiveGatePolicy { public static bool ShouldCorrectIsDown(bool upstreamOriginalIsDown, bool authoritativeControllerPresent, bool exactGenerationProven, ParticipantState participantState) { if (upstreamOriginalIsDown && authoritativeControllerPresent && exactGenerationProven) { return participantState == ParticipantState.SupportDrone; } return false; } public static bool CorrectIsDown(bool upstreamOriginalIsDown, bool authoritativeControllerPresent, bool exactGenerationProven, ParticipantState participantState) { if (!ShouldCorrectIsDown(upstreamOriginalIsDown, authoritativeControllerPresent, exactGenerationProven, participantState)) { return upstreamOriginalIsDown; } return false; } public static bool CountsAsOutstandingCollector(bool authoritativeControllerPresent, ParticipantState participantState) { if (authoritativeControllerPresent) { if (participantState != ParticipantState.Alive) { return participantState == ParticipantState.SupportDrone; } return true; } return false; } public static bool IsDeadAutoShareTarget(bool upstreamOriginalIsDown, bool authoritativeControllerPresent, bool exactGenerationProven, ParticipantState participantState) { return CorrectIsDown(upstreamOriginalIsDown, authoritativeControllerPresent, exactGenerationProven, participantState); } } public readonly struct LocalCommandPresentationDecision { public bool AllLocalStateResolved { get; } public bool AnyLocalPending { get; } public bool SuppressWorldPresentation { get; } public LocalCommandPresentationDecision(bool allLocalStateResolved, bool anyLocalPending, bool suppressWorldPresentation) { AllLocalStateResolved = allLocalStateResolved; AnyLocalPending = anyLocalPending; SuppressWorldPresentation = suppressWorldPresentation; } } public static class LocalCommandPresentationPolicy { public static LocalCommandPresentationDecision Evaluate(IReadOnlyList localPickedStates) { if (localPickedStates == null) { throw new ArgumentNullException("localPickedStates"); } if (localPickedStates.Count == 0) { return new LocalCommandPresentationDecision(allLocalStateResolved: false, anyLocalPending: false, suppressWorldPresentation: false); } bool flag = true; bool flag2 = false; for (int i = 0; i < localPickedStates.Count; i++) { bool? flag3 = localPickedStates[i]; if (!flag3.HasValue) { flag = false; } else if (!flag3.Value) { flag2 = true; } } bool suppressWorldPresentation = flag && !flag2; return new LocalCommandPresentationDecision(flag, flag2, suppressWorldPresentation); } } public static class LocalParticipantPresentationPolicy { public static ParticipantState Classify(bool masterAvailable, bool exactRemoteOperationSignal, bool bodyPresent) { if (!masterAvailable) { return ParticipantState.Disconnected; } if (exactRemoteOperationSignal) { return ParticipantState.SupportDrone; } if (bodyPresent) { return ParticipantState.Alive; } return ParticipantState.FullyDead; } } public static class LocalPickupSuppressionPolicy { public static bool AllowsAuthoritativePickupDestroy => false; public static bool AllowsSharedRootDisable => false; public static bool AllowsSharedColliderDisable => false; public static bool ShouldSuppressProcessVisual(bool featureEnabled, bool upstreamHideCollectedEnabled, int localParticipantCount, int collectedLocalParticipantCount) { if (featureEnabled && upstreamHideCollectedEnabled && localParticipantCount > 0) { return collectedLocalParticipantCount == localParticipantCount; } return false; } public static bool ShouldSuppressInteractor(bool featureEnabled, bool upstreamHideCollectedEnabled, bool shareablePickup, bool interactorIsLocal, bool interactorHasCollected) { return featureEnabled && upstreamHideCollectedEnabled && shareablePickup && interactorIsLocal && interactorHasCollected; } } public readonly struct MarkerExpansionCandidate { public long ClusterKey { get; } public int MemberCount { get; } public float DistanceMeters { get; } public float ReticleDistancePixels1080 { get; } public MarkerExpansionCandidate(long clusterKey, int memberCount, float distanceMeters, float reticleDistancePixels1080) { ClusterKey = clusterKey; MemberCount = Math.Max(0, memberCount); DistanceMeters = distanceMeters; ReticleDistancePixels1080 = reticleDistancePixels1080; } } public sealed class MarkerAdaptiveLodTracker { public const int DenseClusterMinimumMembers = 3; public const float FocusEnterPixels1080 = 72f; public const float FocusExitPixels1080 = 110f; public const float NearEnterMeters = 18f; public const float NearExitMeters = 22f; public const float UniqueNearestMarginMeters = 1f; public const double FocusAcquireDwellSeconds = 0.12; public const double NearAcquireDwellSeconds = 0.25; public const double ReleaseDwellSeconds = 0.2; private long? _expandedKey; private long? _pendingKey; private double _pendingSince; private bool _pendingRelease; public long? ExpandedKey => _expandedKey; public long? Update(IReadOnlyList candidates, double nowSeconds) { if (candidates == null) { throw new ArgumentNullException("candidates"); } if (double.IsNaN(nowSeconds) || double.IsInfinity(nowSeconds)) { nowSeconds = 0.0; } if (_expandedKey.HasValue && IsCurrentStillQualified(candidates, _expandedKey.Value)) { long? num = FindFocused(candidates, useExitForCurrent: true); if (!num.HasValue || num.Value == _expandedKey.Value) { ResetPending(); return _expandedKey; } } long? num2 = FindFocused(candidates, useExitForCurrent: false); if (num2.HasValue) { return AdvanceToward(num2.Value, nowSeconds, 0.12); } long? num3 = FindUniqueNearest(candidates); if (num3.HasValue) { return AdvanceToward(num3.Value, nowSeconds, 0.25); } if (!_expandedKey.HasValue) { ResetPending(); return null; } if (!_pendingRelease) { _pendingRelease = true; _pendingKey = null; _pendingSince = nowSeconds; return _expandedKey; } if (nowSeconds - _pendingSince < 0.2) { return _expandedKey; } _expandedKey = null; ResetPending(); return null; } public void Clear() { _expandedKey = null; ResetPending(); } private long? AdvanceToward(long desiredKey, double nowSeconds, double dwellSeconds) { _pendingRelease = false; if (_expandedKey.HasValue && _expandedKey.Value == desiredKey) { ResetPending(); return _expandedKey; } if (!_pendingKey.HasValue || _pendingKey.Value != desiredKey) { _pendingKey = desiredKey; _pendingSince = nowSeconds; return _expandedKey; } if (nowSeconds - _pendingSince < dwellSeconds) { return _expandedKey; } _expandedKey = desiredKey; ResetPending(); return _expandedKey; } private bool IsCurrentStillQualified(IReadOnlyList candidates, long key) { for (int i = 0; i < candidates.Count; i++) { MarkerExpansionCandidate markerExpansionCandidate = candidates[i]; if (markerExpansionCandidate.ClusterKey == key && markerExpansionCandidate.MemberCount >= 3) { if (!(markerExpansionCandidate.ReticleDistancePixels1080 <= 110f)) { return markerExpansionCandidate.DistanceMeters <= 22f; } return true; } } return false; } private long? FindFocused(IReadOnlyList candidates, bool useExitForCurrent) { float num = (useExitForCurrent ? 110f : 72f); bool flag = false; MarkerExpansionCandidate markerExpansionCandidate = default(MarkerExpansionCandidate); for (int i = 0; i < candidates.Count; i++) { MarkerExpansionCandidate markerExpansionCandidate2 = candidates[i]; if (markerExpansionCandidate2.MemberCount >= 3 && IsFiniteNonNegative(markerExpansionCandidate2.ReticleDistancePixels1080) && !(markerExpansionCandidate2.ReticleDistancePixels1080 > num) && (!flag || markerExpansionCandidate2.ReticleDistancePixels1080 < markerExpansionCandidate.ReticleDistancePixels1080 || (Math.Abs(markerExpansionCandidate2.ReticleDistancePixels1080 - markerExpansionCandidate.ReticleDistancePixels1080) <= 0.001f && (markerExpansionCandidate2.DistanceMeters < markerExpansionCandidate.DistanceMeters || (Math.Abs(markerExpansionCandidate2.DistanceMeters - markerExpansionCandidate.DistanceMeters) <= 0.001f && markerExpansionCandidate2.ClusterKey < markerExpansionCandidate.ClusterKey))))) { flag = true; markerExpansionCandidate = markerExpansionCandidate2; } } if (!flag) { return null; } return markerExpansionCandidate.ClusterKey; } private static long? FindUniqueNearest(IReadOnlyList candidates) { bool flag = false; MarkerExpansionCandidate markerExpansionCandidate = default(MarkerExpansionCandidate); float num = float.PositiveInfinity; for (int i = 0; i < candidates.Count; i++) { MarkerExpansionCandidate markerExpansionCandidate2 = candidates[i]; if (markerExpansionCandidate2.MemberCount < 3 || !IsFiniteNonNegative(markerExpansionCandidate2.DistanceMeters) || markerExpansionCandidate2.DistanceMeters > 18f) { continue; } if (!flag || markerExpansionCandidate2.DistanceMeters < markerExpansionCandidate.DistanceMeters || (Math.Abs(markerExpansionCandidate2.DistanceMeters - markerExpansionCandidate.DistanceMeters) <= 0.001f && markerExpansionCandidate2.ClusterKey < markerExpansionCandidate.ClusterKey)) { if (flag) { num = markerExpansionCandidate.DistanceMeters; } markerExpansionCandidate = markerExpansionCandidate2; flag = true; } else if (markerExpansionCandidate2.DistanceMeters < num) { num = markerExpansionCandidate2.DistanceMeters; } } if (!flag) { return null; } if (!float.IsPositiveInfinity(num) && num - markerExpansionCandidate.DistanceMeters < 1f) { return null; } return markerExpansionCandidate.ClusterKey; } private void ResetPending() { _pendingKey = null; _pendingRelease = false; _pendingSince = 0.0; } private static bool IsFiniteNonNegative(float value) { if (!float.IsNaN(value) && !float.IsInfinity(value)) { return value >= 0f; } return false; } } public enum MarkerCategorySortOrder { HighToLow, LowToHigh } public readonly struct MarkerCompactLayoutSlot { public int Row { get; } public int Column { get; } public int RowSize { get; } public float XUnits { get; } public float YUnits { get; } public MarkerCompactLayoutSlot(int row, int column, int rowSize, float xUnits, float yUnits) { Row = row; Column = column; RowSize = rowSize; XUnits = xUnits; YUnits = yUnits; } } public readonly struct MarkerCompactCellGeometry { public float BadgeSize { get; } public float CountWidth { get; } public float CountGap { get; } public float CellWidth { get; } public float HorizontalStride { get; } public float VerticalStride { get; } public MarkerCompactCellGeometry(float badgeSize, float countWidth, float countGap, float cellWidth, float horizontalStride, float verticalStride) { BadgeSize = badgeSize; CountWidth = countWidth; CountGap = countGap; CellWidth = cellWidth; HorizontalStride = horizontalStride; VerticalStride = verticalStride; } } public static class MarkerCategorySummaryPolicy { public const MarkerCategorySortOrder DefaultSortOrder = MarkerCategorySortOrder.HighToLow; public const float PyramidMaxVerticalOffsetUnits = 0.6f; public const float PyramidMinimumHorizontalSpacingFactor = 0.62f; public const int MaxCategories = 11; public static int DisplayPriority(MarkerSemanticCategory category) { return category switch { MarkerSemanticCategory.Tier3 => 110, MarkerSemanticCategory.Boss => 100, MarkerSemanticCategory.Lunar => 90, MarkerSemanticCategory.Void => 80, MarkerSemanticCategory.Tier2 => 70, MarkerSemanticCategory.Equipment => 60, MarkerSemanticCategory.LunarEquipment => 50, MarkerSemanticCategory.Tier1 => 40, MarkerSemanticCategory.CommandState => 30, MarkerSemanticCategory.Other => 20, _ => 10, }; } public static MarkerCompactCategoryBadge[] BuildCategoryEntries(IReadOnlyList composition, MarkerCategorySortOrder sortOrder) { if (composition == null) { throw new ArgumentNullException("composition"); } int num = 0; for (int i = 0; i < composition.Count; i++) { if (num >= 11) { break; } if (composition[i].Count > 0) { num++; } } MarkerCompactCategoryBadge[] array = new MarkerCompactCategoryBadge[num]; int num2 = 0; for (int j = 0; j < composition.Count; j++) { if (num2 >= array.Length) { break; } MarkerCategoryCount markerCategoryCount = composition[j]; if (markerCategoryCount.Count > 0) { array[num2++] = new MarkerCompactCategoryBadge(markerCategoryCount.Category, markerCategoryCount.Count, DisplayPriority(markerCategoryCount.Category)); } } for (int k = 1; k < array.Length; k++) { MarkerCompactCategoryBadge markerCompactCategoryBadge = array[k]; int num3 = k - 1; while (num3 >= 0 && Compare(markerCompactCategoryBadge, array[num3], sortOrder) < 0) { array[num3 + 1] = array[num3]; num3--; } array[num3 + 1] = markerCompactCategoryBadge; } return array; } public static float PyramidVerticalOffsetUnits(MarkerSemanticCategory category) { float num = ((float)DisplayPriority(category) - 10f) / 100f; if (num < 0f) { num = 0f; } if (num > 1f) { num = 1f; } return num * 0.6f; } public static float PyramidHorizontalSpacingFactor(int categoryCount) { if (categoryCount <= 3) { return 1f; } float num = 1f - (float)(categoryCount - 3) * 0.055f; if (!(num < 0.62f)) { return num; } return 0.62f; } public static float BuildCategoryGlyphSize(float screenWidth, float screenHeight, float markerScale, float indicatorSize, float fontSize) { float num = (IsFinitePositive(screenWidth) ? screenWidth : 1920f); float num2 = (IsFinitePositive(screenHeight) ? screenHeight : 1080f); float num3 = (IsFinitePositive(markerScale) ? markerScale : 1f); if (num3 < 0.75f) { num3 = 0.75f; } if (num3 > 1.25f) { num3 = 1.25f; } float num4 = Math.Min(num / 1920f, num2 / 1080f); if (!IsFinitePositive(num4)) { num4 = 1f; } float num5 = num4 * num3; float num6 = (IsFinitePositive(indicatorSize) ? indicatorSize : (24f * num5)); float num7 = (IsFinitePositive(fontSize) ? fontSize : (20f * num5)); return Math.Max(12f * num5, Math.Min(num6 * 0.58f, num7 * 0.78f)); } public static float BuildCompactLifetimeGroupGap(float screenWidth, float screenHeight, float markerScale, float indicatorSize, float fontSize) { float num = (IsFinitePositive(screenWidth) ? screenWidth : 1920f); float num2 = (IsFinitePositive(screenHeight) ? screenHeight : 1080f); float num3 = (IsFinitePositive(markerScale) ? markerScale : 1f); if (num3 < 0.75f) { num3 = 0.75f; } if (num3 > 1.25f) { num3 = 1.25f; } float num4 = Math.Min(num / 1920f, num2 / 1080f); if (!IsFinitePositive(num4)) { num4 = 1f; } float num5 = num4 * num3; float indicatorSize2 = (IsFinitePositive(indicatorSize) ? indicatorSize : (24f * num5)); float fontSize2 = (IsFinitePositive(fontSize) ? fontSize : (20f * num5)); float num6 = BuildCategoryGlyphSize(num, num2, num3, indicatorSize2, fontSize2); return Math.Max(4f * num5, num6 * 0.18f); } public static float BuildCompactCategoryCenterHorizontalStride(float screenWidth, float screenHeight, float markerScale, float indicatorSize, float fontSize, int categoryCount) { float num = (IsFinitePositive(screenWidth) ? screenWidth : 1920f); float num2 = (IsFinitePositive(screenHeight) ? screenHeight : 1080f); float num3 = (IsFinitePositive(markerScale) ? markerScale : 1f); if (num3 < 0.75f) { num3 = 0.75f; } if (num3 > 1.25f) { num3 = 1.25f; } float num4 = Math.Min(num / 1920f, num2 / 1080f); if (!IsFinitePositive(num4)) { num4 = 1f; } float num5 = num4 * num3; float indicatorSize2 = (IsFinitePositive(indicatorSize) ? indicatorSize : (24f * num5)); float fontSize2 = (IsFinitePositive(fontSize) ? fontSize : (20f * num5)); MarkerCompactCellGeometry markerCompactCellGeometry = BuildCompactCellGeometry(num, num2, num3, indicatorSize2, fontSize2, showCount: false); float num6 = BuildCompactLifetimeGroupGap(num, num2, num3, indicatorSize2, fontSize2); float num7 = Math.Max(4f * num5, markerCompactCellGeometry.BadgeSize * 0.18f); float num8 = markerCompactCellGeometry.HorizontalStride * PyramidHorizontalSpacingFactor(categoryCount); MarkerCompactLayoutSlot[] array = BuildCompactLayout(categoryCount); bool flag = false; for (int i = 0; i < array.Length; i++) { if (array[i].RowSize >= 3) { flag = true; break; } } if (!flag) { return num8; } float val = markerCompactCellGeometry.BadgeSize * 1.5f + num6 * 0.5f + num7; return Math.Max(num8, val); } public static float BuildCompactCategoryCenterVerticalStride(float screenWidth, float screenHeight, float markerScale, float indicatorSize, float fontSize) { return BuildCompactCellGeometry(screenWidth, screenHeight, markerScale, indicatorSize, fontSize, showCount: false).VerticalStride; } public static float BuildDetailedCategoryDistanceGap(float screenWidth, float screenHeight, float markerScale, float indicatorSize, float fontSize) { float num = (IsFinitePositive(screenWidth) ? screenWidth : 1920f); float num2 = (IsFinitePositive(screenHeight) ? screenHeight : 1080f); float num3 = (IsFinitePositive(markerScale) ? markerScale : 1f); if (num3 < 0.75f) { num3 = 0.75f; } if (num3 > 1.25f) { num3 = 1.25f; } float num4 = Math.Min(num / 1920f, num2 / 1080f); if (!IsFinitePositive(num4)) { num4 = 1f; } float num5 = num4 * num3; float indicatorSize2 = (IsFinitePositive(indicatorSize) ? indicatorSize : (24f * num5)); float num6 = (IsFinitePositive(fontSize) ? fontSize : (20f * num5)); float num7 = BuildCategoryGlyphSize(num, num2, num3, indicatorSize2, num6); float num8 = num6 * 1.18f; float num9 = Math.Max(num8, num7 + 2f * num5); return Math.Max(0f, num9 - (num7 + num8) * 0.5f); } public static float BuildCompactLifetimeGroupOffsetX(MarkerCompactLayoutSlot categorySlot, int groupOrdinal, int groupCount, float glyphSize, float lifetimeGroupGap) { int num = Math.Max(1, groupCount); int num2 = Math.Max(0, Math.Min(num - 1, groupOrdinal)); if (num == 1) { return 0f; } float num3 = (IsFinitePositive(glyphSize) ? glyphSize : 1f); float num4 = ((!float.IsNaN(lifetimeGroupGap) && !float.IsInfinity(lifetimeGroupGap) && lifetimeGroupGap > 0f) ? lifetimeGroupGap : 0f); float num5 = num3 + num4; float num6 = (float)(Math.Max(1, categorySlot.RowSize) - 1) * 0.5f; if (categorySlot.RowSize > 1 && (float)categorySlot.Column < num6) { return (float)(-num2) * num5; } if (categorySlot.RowSize > 1 && (float)categorySlot.Column > num6) { return (float)num2 * num5; } return ((float)num2 - (float)(num - 1) * 0.5f) * num5; } public static MarkerCompactCellGeometry BuildCompactCellGeometry(float screenWidth, float screenHeight, float markerScale, float indicatorSize, float fontSize, bool showCount) { float num = (IsFinitePositive(screenWidth) ? screenWidth : 1920f); float num2 = (IsFinitePositive(screenHeight) ? screenHeight : 1080f); float num3 = (IsFinitePositive(markerScale) ? markerScale : 1f); if (num3 < 0.75f) { num3 = 0.75f; } if (num3 > 1.25f) { num3 = 1.25f; } float num4 = Math.Min(num / 1920f, num2 / 1080f); if (!IsFinitePositive(num4)) { num4 = 1f; } float num5 = num4 * num3; float indicatorSize2 = (IsFinitePositive(indicatorSize) ? indicatorSize : (24f * num5)); float num6 = (IsFinitePositive(fontSize) ? fontSize : (20f * num5)); float num7 = BuildCategoryGlyphSize(num, num2, num3, indicatorSize2, num6); float num8 = (showCount ? Math.Max(24f * num5, num6 * 2.2f) : 0f); float num9 = (showCount ? Math.Max(5f * num5, num6 * 0.18f) : 0f); float num10 = num7 + num9 + num8; float num11 = Math.Max(10f * num5, num7 * 0.28f); float horizontalStride = num10 + num11; float verticalStride = Math.Max(num7 + 10f * num5, num6 * 1.45f); return new MarkerCompactCellGeometry(num7, num8, num9, num10, horizontalStride, verticalStride); } private static bool IsFinitePositive(float value) { if (!float.IsNaN(value) && !float.IsInfinity(value)) { return value > 0f; } return false; } public static MarkerCompactLayoutSlot[] BuildCompactLayout(int categoryCount) { int num = Math.Max(0, Math.Min(11, categoryCount)); if (num == 0) { return Array.Empty(); } int[] array = CompactRowPattern(num); MarkerCompactLayoutSlot[] array2 = new MarkerCompactLayoutSlot[num]; int num2 = 0; int num3 = array.Length; for (int i = 0; i < array.Length; i++) { int num4 = array[i]; float yUnits = (float)(num3 - 1) * 0.5f - (float)i; for (int j = 0; j < num4; j++) { if (num2 >= num) { break; } float xUnits = (float)j - (float)(num4 - 1) * 0.5f; array2[num2++] = new MarkerCompactLayoutSlot(i, j, num4, xUnits, yUnits); } } return array2; } private static int[] CompactRowPattern(int count) { return count switch { 1 => new int[1] { 1 }, 2 => new int[2] { 1, 1 }, 3 => new int[2] { 1, 2 }, 4 => new int[3] { 1, 2, 1 }, 5 => new int[3] { 1, 3, 1 }, 6 => new int[4] { 1, 2, 2, 1 }, 7 => new int[4] { 1, 2, 3, 1 }, 8 => new int[5] { 1, 2, 2, 2, 1 }, 9 => new int[5] { 1, 2, 3, 2, 1 }, 10 => new int[5] { 1, 2, 3, 3, 1 }, _ => new int[5] { 1, 3, 3, 3, 1 }, }; } private static int Compare(MarkerCompactCategoryBadge left, MarkerCompactCategoryBadge right, MarkerCategorySortOrder sortOrder) { int num = right.DisplayPriority.CompareTo(left.DisplayPriority); if (sortOrder == MarkerCategorySortOrder.LowToHigh) { num = -num; } if (num != 0) { return num; } int num2 = MarkerSemanticCategoryPolicy.StableOrder(left.Category).CompareTo(MarkerSemanticCategoryPolicy.StableOrder(right.Category)); if (sortOrder != MarkerCategorySortOrder.LowToHigh) { return num2; } return -num2; } } public enum MarkerClassKind { Unknown, Tier1, Tier2, Tier3, Boss, Lunar, Void, Equipment, LunarEquipment, Other } public readonly struct CommandClassPresentation { public MarkerClassKind Kind { get; } public string Label { get; } public bool ExactClass { get; } public CommandClassPresentation(MarkerClassKind kind, string label, bool exactClass) { Kind = kind; Label = (string.IsNullOrWhiteSpace(label) ? "Item choice" : label); ExactClass = exactClass; } } public static class MarkerClassPolicy { public static bool UsesCatalogPickupBaseColor => true; public static bool UsesArtifactCommandName => false; public static string CommandChoiceSource => "PickupPickerController.options/PickupIndex"; public static string AuthoritativeCommandChoiceSource => "PickupPickerController.options/option.pickup.pickupIndex"; public static MarkerClassKind Classify(string? itemTierName, bool isEquipment, bool isLunarEquipment) { if (isEquipment) { if (!isLunarEquipment) { return MarkerClassKind.Equipment; } return MarkerClassKind.LunarEquipment; } string text = itemTierName ?? string.Empty; if (string.Equals(text, "Tier1", StringComparison.Ordinal)) { return MarkerClassKind.Tier1; } if (string.Equals(text, "Tier2", StringComparison.Ordinal)) { return MarkerClassKind.Tier2; } if (string.Equals(text, "Tier3", StringComparison.Ordinal)) { return MarkerClassKind.Tier3; } if (string.Equals(text, "Boss", StringComparison.Ordinal)) { return MarkerClassKind.Boss; } if (string.Equals(text, "Lunar", StringComparison.Ordinal)) { return MarkerClassKind.Lunar; } if (text.StartsWith("Void", StringComparison.Ordinal)) { return MarkerClassKind.Void; } if (string.IsNullOrWhiteSpace(text) || string.Equals(text, "NoTier", StringComparison.Ordinal)) { return MarkerClassKind.Unknown; } return MarkerClassKind.Other; } public static string DiagnosticClassName(MarkerClassKind kind) { return kind switch { MarkerClassKind.Tier1 => "WHITE", MarkerClassKind.Tier2 => "GREEN", MarkerClassKind.Tier3 => "RED", MarkerClassKind.Boss => "YELLOW", MarkerClassKind.Lunar => "LUNAR_BLUE", MarkerClassKind.Void => "VOID_PURPLE", MarkerClassKind.Equipment => "EQUIPMENT", MarkerClassKind.LunarEquipment => "LUNAR_EQUIPMENT", MarkerClassKind.Other => "OTHER", _ => "UNKNOWN", }; } public static string LocalizedClassLabel(MarkerClassKind kind, bool russian) { if (russian) { return kind switch { MarkerClassKind.Tier1 => "Белый", MarkerClassKind.Tier2 => "Зелёный", MarkerClassKind.Tier3 => "Красный", MarkerClassKind.Boss => "Жёлтый", MarkerClassKind.Lunar => "Синий", MarkerClassKind.Void => "Фиолетовый", MarkerClassKind.Equipment => "Снаряжение", MarkerClassKind.LunarEquipment => "Лунное снаряжение", _ => "Выбор предмета", }; } return kind switch { MarkerClassKind.Tier1 => "White", MarkerClassKind.Tier2 => "Green", MarkerClassKind.Tier3 => "Red", MarkerClassKind.Boss => "Yellow", MarkerClassKind.Lunar => "Blue", MarkerClassKind.Void => "Purple", MarkerClassKind.Equipment => "Equipment", MarkerClassKind.LunarEquipment => "Lunar equipment", _ => "Item choice", }; } public static CommandClassPresentation ResolveCommandClass(IEnumerable? choiceClasses, bool russian) { if (choiceClasses == null) { return new CommandClassPresentation(MarkerClassKind.Unknown, LocalizedClassLabel(MarkerClassKind.Unknown, russian), exactClass: false); } MarkerClassKind[] array = choiceClasses.ToArray(); if (array.Length == 0 || array.Any((MarkerClassKind x) => x == MarkerClassKind.Unknown)) { return new CommandClassPresentation(MarkerClassKind.Unknown, LocalizedClassLabel(MarkerClassKind.Unknown, russian), exactClass: false); } MarkerClassKind[] array2 = array.Distinct().ToArray(); if (array2.Length != 1) { return new CommandClassPresentation(MarkerClassKind.Unknown, LocalizedClassLabel(MarkerClassKind.Unknown, russian), exactClass: false); } MarkerClassKind markerClassKind = array2[0]; if (markerClassKind == MarkerClassKind.Other) { return new CommandClassPresentation(MarkerClassKind.Other, LocalizedClassLabel(MarkerClassKind.Other, russian), exactClass: false); } return new CommandClassPresentation(markerClassKind, LocalizedClassLabel(markerClassKind, russian), exactClass: true); } public static string LocalizedSemanticClassLabel(MarkerClassKind kind, bool russian) { if (russian) { return kind switch { MarkerClassKind.Tier1 => "Белый", MarkerClassKind.Tier2 => "Зелёный", MarkerClassKind.Tier3 => "Красный", MarkerClassKind.Boss => "Жёлтый", MarkerClassKind.Lunar => "Лунный", MarkerClassKind.Void => "Предмет Бездны", MarkerClassKind.Equipment => "Снаряжение", MarkerClassKind.LunarEquipment => "Лунное снаряжение", _ => "Выбор предмета", }; } return kind switch { MarkerClassKind.Tier1 => "White", MarkerClassKind.Tier2 => "Green", MarkerClassKind.Tier3 => "Red", MarkerClassKind.Boss => "Yellow", MarkerClassKind.Lunar => "Lunar", MarkerClassKind.Void => "Void", MarkerClassKind.Equipment => "Equipment", MarkerClassKind.LunarEquipment => "Lunar equipment", _ => "Item choice", }; } public static CommandClassPresentation ResolveCommandClassForPresentation(IEnumerable? choiceClasses, bool russian) { if (choiceClasses == null) { return new CommandClassPresentation(MarkerClassKind.Unknown, LocalizedSemanticClassLabel(MarkerClassKind.Unknown, russian), exactClass: false); } MarkerClassKind[] array = choiceClasses.ToArray(); if (array.Length == 0 || array.Any((MarkerClassKind x) => x == MarkerClassKind.Unknown)) { return new CommandClassPresentation(MarkerClassKind.Unknown, LocalizedSemanticClassLabel(MarkerClassKind.Unknown, russian), exactClass: false); } MarkerClassKind[] array2 = array.Distinct().ToArray(); if (array2.Length != 1) { return new CommandClassPresentation(MarkerClassKind.Unknown, LocalizedSemanticClassLabel(MarkerClassKind.Unknown, russian), exactClass: false); } MarkerClassKind markerClassKind = array2[0]; if (markerClassKind == MarkerClassKind.Other) { return new CommandClassPresentation(MarkerClassKind.Other, LocalizedSemanticClassLabel(MarkerClassKind.Other, russian), exactClass: false); } return new CommandClassPresentation(markerClassKind, LocalizedSemanticClassLabel(markerClassKind, russian), exactClass: true); } public static string LocalizedReadableClassLabel(MarkerClassKind kind, bool russian) { return LocalizedReadableClassLabel(kind, MarkerTextLocalization.FromRussianCompatibility(russian)); } public static string LocalizedReadableClassLabel(MarkerClassKind kind, MarkerLanguage language) { return MarkerTextLocalization.ReadableClassLabel(kind, language); } public static CommandClassPresentation ResolveCommandClassForReadablePresentation(IEnumerable? choiceClasses, bool russian) { return ResolveCommandClassForReadablePresentation(choiceClasses, MarkerTextLocalization.FromRussianCompatibility(russian)); } public static CommandClassPresentation ResolveCommandClassForReadablePresentation(IEnumerable? choiceClasses, MarkerLanguage language) { if (choiceClasses == null) { return new CommandClassPresentation(MarkerClassKind.Unknown, LocalizedReadableClassLabel(MarkerClassKind.Unknown, language), exactClass: false); } MarkerClassKind[] array = choiceClasses.ToArray(); if (array.Length == 0 || array.Any((MarkerClassKind x) => x == MarkerClassKind.Unknown)) { return new CommandClassPresentation(MarkerClassKind.Unknown, LocalizedReadableClassLabel(MarkerClassKind.Unknown, language), exactClass: false); } MarkerClassKind[] array2 = array.Distinct().ToArray(); if (array2.Length != 1) { return new CommandClassPresentation(MarkerClassKind.Unknown, LocalizedReadableClassLabel(MarkerClassKind.Unknown, language), exactClass: false); } MarkerClassKind markerClassKind = array2[0]; if (markerClassKind == MarkerClassKind.Other) { return new CommandClassPresentation(MarkerClassKind.Other, LocalizedReadableClassLabel(MarkerClassKind.Other, language), exactClass: false); } return new CommandClassPresentation(markerClassKind, LocalizedReadableClassLabel(markerClassKind, language), exactClass: true); } } public enum MarkerPresentationMode { Detailed, Compact } public enum MarkerCompactMixedStyle { NeutralSingleDiamond, CategoryDiamonds, CategoryDiamondPyramid } public enum MarkerCountRenderSource { None, TotalCountText, CategorySubcounts, DetailedCategoryRows } public enum MarkerPresentationGlyphKind { Diamond, Clock } public readonly struct MarkerPresentationSettings { public MarkerPresentationMode Mode { get; } public bool ShowDistance { get; } public float Scale { get; } public int DetailRows { get; } public bool ShowCategoryDiamond { get; } public bool ShowTierComposition { get; } public bool CompactShowCount { get; } public MarkerCompactMixedStyle CompactMixedStyle { get; } public MarkerCategorySortOrder CategorySortOrder { get; } public bool UseCategorySummaryPresentation { get; } public MarkerPresentationSettings(MarkerPresentationMode mode, bool showDistance, float scale, int detailRows) : this(mode, showDistance, scale, detailRows, showCategoryDiamond: true, showTierComposition: true, compactShowCount: true, MarkerCompactMixedStyle.NeutralSingleDiamond, MarkerCategorySortOrder.HighToLow, useCategorySummaryPresentation: false) { } public MarkerPresentationSettings(MarkerPresentationMode mode, bool showDistance, float scale, int detailRows, bool showCategoryDiamond, bool showTierComposition, bool compactShowCount, MarkerCompactMixedStyle compactMixedStyle) : this(mode, showDistance, scale, detailRows, showCategoryDiamond, showTierComposition, compactShowCount, compactMixedStyle, MarkerCategorySortOrder.HighToLow, useCategorySummaryPresentation: false) { } public MarkerPresentationSettings(MarkerPresentationMode mode, bool showDistance, float scale, int detailRows, bool showCategoryDiamond, bool showTierComposition, bool compactShowCount, MarkerCompactMixedStyle compactMixedStyle, MarkerCategorySortOrder categorySortOrder) : this(mode, showDistance, scale, detailRows, showCategoryDiamond, showTierComposition, compactShowCount, compactMixedStyle, categorySortOrder, useCategorySummaryPresentation: true) { } private MarkerPresentationSettings(MarkerPresentationMode mode, bool showDistance, float scale, int detailRows, bool showCategoryDiamond, bool showTierComposition, bool compactShowCount, MarkerCompactMixedStyle compactMixedStyle, MarkerCategorySortOrder categorySortOrder, bool useCategorySummaryPresentation) { Mode = mode; ShowDistance = showDistance; Scale = MarkerClusterPresentationPolicy.ClampMarkerScale(scale); DetailRows = MarkerClusterPresentationPolicy.ClampDetailRows(detailRows); ShowCategoryDiamond = showCategoryDiamond; ShowTierComposition = showTierComposition; CompactShowCount = compactShowCount; CompactMixedStyle = compactMixedStyle; CategorySortOrder = categorySortOrder; UseCategorySummaryPresentation = useCategorySummaryPresentation; } } public readonly struct MarkerCompactCategoryBadge { public MarkerSemanticCategory Category { get; } public int Count { get; } public int DisplayPriority { get; } public MarkerLifetimeKind Lifetime { get; } public MarkerPresentationGlyphKind GlyphKind { get; } public MarkerLifetimeIndicatorSpec LifetimeIndicator { get; } public float PyramidVerticalOffsetUnits => MarkerCategorySummaryPolicy.PyramidVerticalOffsetUnits(Category); public MarkerCompactCategoryBadge(MarkerSemanticCategory category, int count) : this(category, count, MarkerCategorySummaryPolicy.DisplayPriority(category), MarkerLifetimeKind.Permanent, MarkerPresentationGlyphKind.Diamond) { } public MarkerCompactCategoryBadge(MarkerSemanticCategory category, int count, int displayPriority) : this(category, count, displayPriority, MarkerLifetimeKind.Permanent, MarkerPresentationGlyphKind.Diamond) { } public MarkerCompactCategoryBadge(MarkerSemanticCategory category, int count, int displayPriority, MarkerLifetimeIndicatorSpec lifetimeIndicator) : this(category, count, displayPriority, lifetimeIndicator.Visible ? MarkerLifetimeKind.Temporary : MarkerLifetimeKind.Permanent, lifetimeIndicator.Visible ? MarkerPresentationGlyphKind.Clock : MarkerPresentationGlyphKind.Diamond) { } public MarkerCompactCategoryBadge(MarkerSemanticCategory category, int count, int displayPriority, MarkerLifetimeKind lifetime, MarkerPresentationGlyphKind glyphKind) { Category = category; Count = Math.Max(0, count); DisplayPriority = displayPriority; Lifetime = lifetime; GlyphKind = glyphKind; LifetimeIndicator = MarkerLifetimeIndicatorSpec.Hidden; } } public readonly struct MarkerDetailedItemRow { public string ItemIdentity { get; } public string LocalizedName { get; } public MarkerSemanticCategory Category { get; } public int Count { get; } public MarkerLifetimeKind Lifetime { get; } public MarkerPresentationGlyphKind GlyphKind { get; } public string DisplayLabel { get; } public MarkerLifetimeIndicatorSpec LifetimeIndicator { get; } public MarkerDetailedItemRow(string itemIdentity, string localizedName, MarkerSemanticCategory category, int count) : this(itemIdentity, localizedName, category, count, MarkerLifetimeKind.Permanent, localizedName) { } public MarkerDetailedItemRow(string itemIdentity, string localizedName, MarkerSemanticCategory category, int count, MarkerLifetimeKind lifetime, string displayLabel) { ItemIdentity = itemIdentity ?? string.Empty; LocalizedName = (string.IsNullOrWhiteSpace(localizedName) ? "Pickup" : localizedName); Category = category; Count = Math.Max(0, count); Lifetime = lifetime; GlyphKind = ((lifetime == MarkerLifetimeKind.Temporary) ? MarkerPresentationGlyphKind.Clock : MarkerPresentationGlyphKind.Diamond); DisplayLabel = (string.IsNullOrWhiteSpace(displayLabel) ? LocalizedName : displayLabel); LifetimeIndicator = MarkerLifetimePolicy.BuildIndicator(lifetime, (lifetime == MarkerLifetimeKind.Temporary) ? Count : 0, (lifetime == MarkerLifetimeKind.Mixed) ? Count : 0, (lifetime == MarkerLifetimeKind.Unknown) ? Count : 0, allowCount: false, showSingleCountWhenMixed: false); } } public sealed class MarkerClusterPresentationPlan { public string Text { get; } public IReadOnlyList CompactBadges { get; } public IReadOnlyList CategoryEntries => CompactBadges; public IReadOnlyList DetailedItemRows { get; } public int TotalCount { get; } public int ShownDetailRows { get; } public int OverflowPhysicalCount { get; } public bool Expanded { get; } public bool NeutralMainSemantic { get; } public MarkerCountRenderSource CountRenderSource { get; } public bool ShowMainDiamond { get; } public bool ShowDetailedCategoryRowDiamonds { get; } public bool ShowCompactCategoryDiamonds { get; } public MarkerSemanticCategory MainCategory { get; } public MarkerLifetimeIndicatorSpec LifetimeIndicator { get; private set; } public bool RenderCategorySubcounts => CountRenderSource == MarkerCountRenderSource.CategorySubcounts; public bool RenderTotalCount => CountRenderSource == MarkerCountRenderSource.TotalCountText; internal MarkerClusterPresentationPlan(string text, MarkerCompactCategoryBadge[] compactBadges, int totalCount, int shownDetailRows, int overflowPhysicalCount, bool expanded, bool neutralMainSemantic, MarkerCountRenderSource countRenderSource, bool showMainDiamond, bool showDetailedCategoryRowDiamonds, bool showCompactCategoryDiamonds, MarkerSemanticCategory mainCategory) : this(text, compactBadges, Array.Empty(), totalCount, shownDetailRows, overflowPhysicalCount, expanded, neutralMainSemantic, countRenderSource, showMainDiamond, showDetailedCategoryRowDiamonds, showCompactCategoryDiamonds, mainCategory) { } internal MarkerClusterPresentationPlan(string text, MarkerCompactCategoryBadge[] compactBadges, MarkerDetailedItemRow[] detailedItemRows, int totalCount, int shownDetailRows, int overflowPhysicalCount, bool expanded, bool neutralMainSemantic, MarkerCountRenderSource countRenderSource, bool showMainDiamond, bool showDetailedCategoryRowDiamonds, bool showCompactCategoryDiamonds, MarkerSemanticCategory mainCategory) { Text = text ?? string.Empty; CompactBadges = compactBadges ?? Array.Empty(); DetailedItemRows = detailedItemRows ?? Array.Empty(); TotalCount = Math.Max(0, totalCount); ShownDetailRows = Math.Max(0, shownDetailRows); OverflowPhysicalCount = Math.Max(0, overflowPhysicalCount); Expanded = expanded; NeutralMainSemantic = neutralMainSemantic; CountRenderSource = countRenderSource; ShowMainDiamond = showMainDiamond; ShowDetailedCategoryRowDiamonds = showDetailedCategoryRowDiamonds; ShowCompactCategoryDiamonds = showCompactCategoryDiamonds; MainCategory = mainCategory; LifetimeIndicator = MarkerLifetimeIndicatorSpec.Hidden; } internal void SetLifetimeIndicator(MarkerLifetimeIndicatorSpec lifetimeIndicator) { LifetimeIndicator = lifetimeIndicator; } } public static class MarkerClusterPresentationPolicy { public const int TOTAL_COUNT_RENDER_SLOTS_PER_PRESENTATION_NODE = 1; public const float MarkerScaleMin = 0.75f; public const float MarkerScaleMax = 1.25f; public const float MarkerScaleDefault = 1f; public const int MarkerDetailRowsMin = 1; public const int MarkerDetailRowsMax = 12; public const int MarkerDetailRowsDefault = 5; public const int DetailedCompositionLineCharacterBudget = 48; public const int MaxCompositionLines = 2; public const int MaxCompactCategoryBadges = 11; public const int MaxCompactLifetimePresentationGroups = 22; public const float DetailedItemLabelMaxWidth1080 = 420f; public const float DetailedItemLabelMinWidth1080 = 180f; public static float ClampMarkerScale(float value) { if (float.IsNaN(value) || float.IsInfinity(value)) { return 1f; } if (value < 0.75f) { return 0.75f; } if (!(value > 1.25f)) { return value; } return 1.25f; } public static int ClampDetailRows(int value) { if (value < 1) { return 1; } if (value <= 12) { return value; } return 12; } public static float BuildDetailedItemLabelWidthLimit(float screenWidth, float screenHeight, float markerScale) { float num = ((float.IsNaN(screenWidth) || float.IsInfinity(screenWidth) || screenWidth <= 0f) ? 1920f : screenWidth); float num2 = ((float.IsNaN(screenHeight) || float.IsInfinity(screenHeight) || screenHeight <= 0f) ? 1080f : screenHeight); float num3 = Math.Min(num / 1920f, num2 / 1080f); if (num3 <= 0f || float.IsNaN(num3) || float.IsInfinity(num3)) { num3 = 1f; } float num4 = ClampMarkerScale(markerScale); float val = 420f * num3 * num4; float val2 = 180f * num3 * num4; float val3 = num * 0.34f; return Math.Max(val2, Math.Min(val, val3)); } public static MarkerClusterPresentationPlan Build(MarkerSemanticCluster cluster, MarkerPresentationSettings settings, int distanceMeters, bool expanded, bool russian) { return Build(cluster, settings, distanceMeters, expanded, MarkerTextLocalization.FromRussianCompatibility(russian)); } public static MarkerClusterPresentationPlan Build(MarkerSemanticCluster cluster, MarkerPresentationSettings settings, int distanceMeters, bool expanded, MarkerLanguage language) { if (cluster == null) { throw new ArgumentNullException("cluster"); } MarkerClusterPresentationPlan markerClusterPresentationPlan = ((!settings.UseCategorySummaryPresentation) ? ((settings.Mode == MarkerPresentationMode.Compact) ? BuildCompactLegacy(cluster, settings, distanceMeters, expanded, language) : BuildDetailedLegacy(cluster, settings, distanceMeters, expanded, language)) : ((settings.Mode == MarkerPresentationMode.Compact) ? BuildCompactCategoryPyramid(cluster, settings, distanceMeters, language) : BuildDetailedCategorySummary(cluster, settings, distanceMeters, language))); if ((settings.Mode == MarkerPresentationMode.Compact && !markerClusterPresentationPlan.ShowCompactCategoryDiamonds) || (settings.Mode == MarkerPresentationMode.Detailed && markerClusterPresentationPlan.DetailedItemRows.Count == 0 && !markerClusterPresentationPlan.ShowDetailedCategoryRowDiamonds)) { markerClusterPresentationPlan.SetLifetimeIndicator(BuildClusterLifetimeIndicator(cluster, settings.Mode == MarkerPresentationMode.Compact && settings.CompactShowCount, showSingleCountWhenMixed: true)); } return markerClusterPresentationPlan; } public static MarkerClusterPresentationPlan BuildOffscreen(int nearestDistanceMeters, int sectorTotalCount, bool showDistance, bool showTotalCount, bool russian) { return BuildOffscreen(nearestDistanceMeters, sectorTotalCount, showDistance, showTotalCount, MarkerTextLocalization.FromRussianCompatibility(russian), MarkerLifetimeKind.Permanent, 0, 0, 0); } public static MarkerClusterPresentationPlan BuildOffscreen(int nearestDistanceMeters, int sectorTotalCount, bool showDistance, bool showTotalCount, MarkerLanguage language) { return BuildOffscreen(nearestDistanceMeters, sectorTotalCount, showDistance, showTotalCount, language, MarkerLifetimeKind.Permanent, 0, 0, 0); } public static MarkerClusterPresentationPlan BuildOffscreen(int nearestDistanceMeters, int sectorTotalCount, bool showDistance, bool showTotalCount, bool russian, MarkerLifetimeKind lifetime, int temporaryPhysicalMembers, int mixedLifetimeMembers, int unknownLifetimeMembers) { return BuildOffscreen(nearestDistanceMeters, sectorTotalCount, showDistance, showTotalCount, MarkerTextLocalization.FromRussianCompatibility(russian), lifetime, temporaryPhysicalMembers, mixedLifetimeMembers, unknownLifetimeMembers); } public static MarkerClusterPresentationPlan BuildOffscreen(int nearestDistanceMeters, int sectorTotalCount, bool showDistance, bool showTotalCount, MarkerLanguage language, MarkerLifetimeKind lifetime, int temporaryPhysicalMembers, int mixedLifetimeMembers, int unknownLifetimeMembers) { StringBuilder stringBuilder = new StringBuilder(32); MarkerCountRenderSource countRenderSource = MarkerCountRenderSource.None; if (showTotalCount) { stringBuilder.Append('×').Append(Math.Max(0, sectorTotalCount).ToString(CultureInfo.InvariantCulture)); countRenderSource = MarkerCountRenderSource.TotalCountText; } if (showDistance) { if (stringBuilder.Length > 0) { stringBuilder.Append('\n'); } stringBuilder.Append(Math.Max(0, nearestDistanceMeters).ToString(CultureInfo.InvariantCulture)).Append(" ").Append(MarkerTextLocalization.DistanceUnit(language)); } MarkerClusterPresentationPlan markerClusterPresentationPlan = new MarkerClusterPresentationPlan(stringBuilder.ToString(), Array.Empty(), Math.Max(0, sectorTotalCount), 0, 0, expanded: false, neutralMainSemantic: true, countRenderSource, showMainDiamond: true, showDetailedCategoryRowDiamonds: false, showCompactCategoryDiamonds: false, MarkerSemanticCategory.Unknown); markerClusterPresentationPlan.SetLifetimeIndicator((lifetime == MarkerLifetimeKind.Temporary) ? MarkerLifetimePolicy.BuildIndicator(MarkerLifetimeKind.Temporary, temporaryPhysicalMembers, 0, 0, showTotalCount, showSingleCountWhenMixed: false) : MarkerLifetimeIndicatorSpec.Hidden); return markerClusterPresentationPlan; } public static string LocalizedCategoryPlural(MarkerSemanticCategory category, bool russian) { return LocalizedCategoryPlural(category, MarkerTextLocalization.FromRussianCompatibility(russian)); } public static string LocalizedCategoryPlural(MarkerSemanticCategory category, MarkerLanguage language) { return MarkerTextLocalization.CategoryPlural(category, language); } public static string LocalizedCompositionWord(MarkerSemanticCategory category, bool russian) { return LocalizedCompositionWord(category, MarkerTextLocalization.FromRussianCompatibility(russian)); } public static string LocalizedCompositionWord(MarkerSemanticCategory category, MarkerLanguage language) { return MarkerTextLocalization.CompositionLabel(category, language); } public static int CompositionCountSum(IReadOnlyList composition) { if (composition == null) { throw new ArgumentNullException("composition"); } int num = 0; for (int i = 0; i < composition.Count; i++) { num += Math.Max(0, composition[i].Count); } return num; } public static string LocalizedCategorySummaryLabel(MarkerSemanticCategory category, bool russian) { return LocalizedCategorySummaryLabel(category, MarkerTextLocalization.FromRussianCompatibility(russian)); } public static string LocalizedCategorySummaryLabel(MarkerSemanticCategory category, MarkerLanguage language) { return MarkerTextLocalization.CategorySummaryLabel(category, language); } private static MarkerClusterPresentationPlan BuildDetailedCategorySummary(MarkerSemanticCluster cluster, MarkerPresentationSettings settings, int distanceMeters, MarkerLanguage language) { if (!IsCommandChoiceCluster(cluster)) { return BuildDetailedOrdinaryItemSummary(cluster, settings, distanceMeters, language); } return BuildDetailedCommandCategorySummary(cluster, settings, distanceMeters, language); } private static MarkerClusterPresentationPlan BuildDetailedCommandCategorySummary(MarkerSemanticCluster cluster, MarkerPresentationSettings settings, int distanceMeters, MarkerLanguage language) { StringBuilder stringBuilder = new StringBuilder(160); int totalCount = cluster.TotalCount; bool flag = cluster.IsMixedCategory || cluster.HomogeneousCategory == MarkerSemanticCategory.Unknown || cluster.HomogeneousCategory == MarkerSemanticCategory.CommandState; MarkerSemanticCategory mainCategory = (flag ? MarkerSemanticCategory.Unknown : cluster.HomogeneousCategory); MarkerCompactCategoryBadge[] array = BuildCategoryEntriesWithLifetime(cluster, settings.CategorySortOrder); if (totalCount <= 1 && IsTruthfulConcreteSingleAggregate(cluster)) { stringBuilder.Append(cluster.ItemRows[0].LocalizedName).Append(" ×").Append(totalCount.ToString(CultureInfo.InvariantCulture)); AppendDistance(stringBuilder, settings.ShowDistance, distanceMeters, language); return new MarkerClusterPresentationPlan(stringBuilder.ToString(), array, totalCount, 0, 0, expanded: false, flag, MarkerCountRenderSource.TotalCountText, settings.ShowCategoryDiamond, showDetailedCategoryRowDiamonds: false, showCompactCategoryDiamonds: false, mainCategory); } for (int i = 0; i < array.Length; i++) { if (i > 0) { stringBuilder.Append('\n'); } stringBuilder.Append(LocalizedCategorySummaryLabel(array[i].Category, language)).Append(" ×").Append(array[i].Count.ToString(CultureInfo.InvariantCulture)); } AppendDistance(stringBuilder, settings.ShowDistance, distanceMeters, language); return new MarkerClusterPresentationPlan(stringBuilder.ToString(), array, totalCount, 0, 0, expanded: false, flag, MarkerCountRenderSource.DetailedCategoryRows, showMainDiamond: false, settings.ShowCategoryDiamond, showCompactCategoryDiamonds: false, mainCategory); } private static MarkerClusterPresentationPlan BuildDetailedOrdinaryItemSummary(MarkerSemanticCluster cluster, MarkerPresentationSettings settings, int distanceMeters, MarkerLanguage language) { List list = new List(cluster.ItemRows.Count); for (int i = 0; i < cluster.ItemRows.Count; i++) { MarkerItemAggregate markerItemAggregate = cluster.ItemRows[i]; if (markerItemAggregate.Count > 0) { list.Add(new MarkerDetailedItemRow(markerItemAggregate.ItemIdentity, markerItemAggregate.LocalizedName, markerItemAggregate.Category, markerItemAggregate.Count, markerItemAggregate.Lifetime, MarkerLifetimePolicy.BuildDetailedItemDisplayName(string.IsNullOrWhiteSpace(markerItemAggregate.LocalizedName) ? MarkerTextLocalization.FallbackPickup(language) : markerItemAggregate.LocalizedName, markerItemAggregate.Lifetime, language == MarkerLanguage.Russian))); } } list.Sort((MarkerDetailedItemRow left, MarkerDetailedItemRow right) => CompareDetailedItems(left, right, settings.CategorySortOrder)); int num = Math.Min(settings.DetailRows, list.Count); int num2 = Math.Max(0, list.Count - num); StringBuilder stringBuilder = new StringBuilder(192); for (int num3 = 0; num3 < num; num3++) { if (stringBuilder.Length > 0) { stringBuilder.Append('\n'); } stringBuilder.Append(list[num3].DisplayLabel).Append(" ×").Append(list[num3].Count.ToString(CultureInfo.InvariantCulture)); } if (num2 > 0) { if (stringBuilder.Length > 0) { stringBuilder.Append('\n'); } stringBuilder.Append(BuildDetailedOverflowText(num2, language)); } AppendDistance(stringBuilder, settings.ShowDistance, distanceMeters, language); MarkerDetailedItemRow[] array = new MarkerDetailedItemRow[num]; for (int num4 = 0; num4 < num; num4++) { array[num4] = list[num4]; } bool flag = cluster.IsMixedCategory || cluster.HomogeneousCategory == MarkerSemanticCategory.Unknown; MarkerSemanticCategory mainCategory = (flag ? MarkerSemanticCategory.Unknown : cluster.HomogeneousCategory); return new MarkerClusterPresentationPlan(stringBuilder.ToString(), Array.Empty(), array, cluster.TotalCount, num, num2, expanded: false, flag, MarkerCountRenderSource.None, showMainDiamond: false, showDetailedCategoryRowDiamonds: false, showCompactCategoryDiamonds: false, mainCategory); } public static string BuildDetailedOverflowText(int hiddenDistinctTypes, bool russian) { return BuildDetailedOverflowText(hiddenDistinctTypes, MarkerTextLocalization.FromRussianCompatibility(russian)); } public static string BuildDetailedOverflowText(int hiddenDistinctTypes, MarkerLanguage language) { return MarkerTextLocalization.FormatMoreTypes(hiddenDistinctTypes, language); } public static string RussianDistinctTypeWord(int hiddenDistinctTypes) { return MarkerTextLocalization.RussianDistinctTypeWord(hiddenDistinctTypes); } private static int CompareDetailedItems(MarkerDetailedItemRow left, MarkerDetailedItemRow right, MarkerCategorySortOrder sortOrder) { int value = MarkerCategorySummaryPolicy.DisplayPriority(left.Category); int num = MarkerCategorySummaryPolicy.DisplayPriority(right.Category).CompareTo(value); if (sortOrder == MarkerCategorySortOrder.LowToHigh) { num = -num; } if (num != 0) { return num; } int num2 = string.Compare(left.LocalizedName, right.LocalizedName, StringComparison.Ordinal); if (num2 != 0) { return num2; } int num3 = string.Compare(left.ItemIdentity, right.ItemIdentity, StringComparison.Ordinal); if (num3 != 0) { return num3; } return MarkerLifetimePolicy.DetailedSortRank(left.Lifetime).CompareTo(MarkerLifetimePolicy.DetailedSortRank(right.Lifetime)); } private static bool IsCommandChoiceCluster(MarkerSemanticCluster cluster) { if (cluster.ItemRows.Count == 0) { return cluster.HomogeneousCategory == MarkerSemanticCategory.CommandState; } for (int i = 0; i < cluster.ItemRows.Count; i++) { if (!(cluster.ItemRows[i].ItemIdentity ?? string.Empty).StartsWith("COMMAND:", StringComparison.OrdinalIgnoreCase)) { return false; } } return true; } private static MarkerClusterPresentationPlan BuildCompactCategoryPyramid(MarkerSemanticCluster cluster, MarkerPresentationSettings settings, int distanceMeters, MarkerLanguage language) { int totalCount = cluster.TotalCount; MarkerCompactCategoryBadge[] compactBadges = BuildCategoryEntriesWithLifetime(cluster, settings.CategorySortOrder); bool flag = cluster.IsMixedCategory || cluster.HomogeneousCategory == MarkerSemanticCategory.Unknown || cluster.HomogeneousCategory == MarkerSemanticCategory.CommandState; MarkerSemanticCategory mainCategory = (flag ? MarkerSemanticCategory.Unknown : cluster.HomogeneousCategory); StringBuilder stringBuilder = new StringBuilder(48); AppendDistance(stringBuilder, settings.ShowDistance, distanceMeters, language); return new MarkerClusterPresentationPlan(stringBuilder.ToString(), compactBadges, totalCount, 0, 0, expanded: false, flag, settings.CompactShowCount ? MarkerCountRenderSource.CategorySubcounts : MarkerCountRenderSource.None, showMainDiamond: false, showDetailedCategoryRowDiamonds: false, showCompactCategoryDiamonds: true, mainCategory); } private static MarkerClusterPresentationPlan BuildDetailedLegacy(MarkerSemanticCluster cluster, MarkerPresentationSettings settings, int distanceMeters, bool expanded, MarkerLanguage language) { StringBuilder stringBuilder = new StringBuilder(192); int totalCount = cluster.TotalCount; bool flag = cluster.IsMixedCategory || cluster.HomogeneousCategory == MarkerSemanticCategory.Unknown || cluster.HomogeneousCategory == MarkerSemanticCategory.CommandState; MarkerSemanticCategory mainCategory = (flag ? MarkerSemanticCategory.Unknown : cluster.HomogeneousCategory); if (IsTruthfulConcreteSingleAggregate(cluster)) { stringBuilder.Append(cluster.ItemRows[0].LocalizedName).Append(" ×").Append(totalCount.ToString(CultureInfo.InvariantCulture)); } else if (cluster.IsHomogeneousCategory) { stringBuilder.Append(LocalizedCategoryPlural(cluster.HomogeneousCategory, language)).Append(" ×").Append(totalCount.ToString(CultureInfo.InvariantCulture)); } else { stringBuilder.Append(MarkerTextLocalization.GenericItems(language)).Append(" ×").Append(totalCount.ToString(CultureInfo.InvariantCulture)); if (settings.ShowTierComposition) { AppendDetailedComposition(stringBuilder, cluster.Composition, language); } } int shownRows = 0; int overflowPhysicalCount = 0; if (expanded && totalCount > 1) { AppendExpandedDetailRows(stringBuilder, cluster.ItemRows, settings.DetailRows, totalCount, language, out shownRows, out overflowPhysicalCount); } AppendDistance(stringBuilder, settings.ShowDistance, distanceMeters, language); return new MarkerClusterPresentationPlan(stringBuilder.ToString(), Array.Empty(), totalCount, shownRows, overflowPhysicalCount, expanded, flag, MarkerCountRenderSource.TotalCountText, settings.ShowCategoryDiamond, showDetailedCategoryRowDiamonds: false, showCompactCategoryDiamonds: false, mainCategory); } private static MarkerClusterPresentationPlan BuildCompactLegacy(MarkerSemanticCluster cluster, MarkerPresentationSettings settings, int distanceMeters, bool expanded, MarkerLanguage language) { int totalCount = cluster.TotalCount; MarkerCompactCategoryBadge[] compactBadges = BuildCategoryEntriesWithLifetime(cluster, MarkerCategorySortOrder.HighToLow, preserveCompositionOrder: true); bool flag = cluster.IsMixedCategory && settings.CompactMixedStyle == MarkerCompactMixedStyle.CategoryDiamonds; MarkerCountRenderSource markerCountRenderSource = ((!flag) ? (settings.CompactShowCount ? MarkerCountRenderSource.TotalCountText : MarkerCountRenderSource.None) : (settings.CompactShowCount ? MarkerCountRenderSource.CategorySubcounts : MarkerCountRenderSource.None)); StringBuilder stringBuilder = new StringBuilder(160); if (markerCountRenderSource == MarkerCountRenderSource.TotalCountText) { stringBuilder.Append('×').Append(totalCount.ToString(CultureInfo.InvariantCulture)); } int shownRows = 0; int overflowPhysicalCount = 0; if (expanded) { AppendExpandedDetailRows(stringBuilder, cluster.ItemRows, settings.DetailRows, totalCount, language, out shownRows, out overflowPhysicalCount); } AppendDistance(stringBuilder, settings.ShowDistance, distanceMeters, language); bool flag2 = cluster.IsMixedCategory || cluster.HomogeneousCategory == MarkerSemanticCategory.Unknown || cluster.HomogeneousCategory == MarkerSemanticCategory.CommandState; MarkerSemanticCategory mainCategory = (flag2 ? MarkerSemanticCategory.Unknown : cluster.HomogeneousCategory); return new MarkerClusterPresentationPlan(stringBuilder.ToString(), compactBadges, totalCount, shownRows, overflowPhysicalCount, expanded, flag2, markerCountRenderSource, !flag, showDetailedCategoryRowDiamonds: false, flag, mainCategory); } private static bool IsTruthfulConcreteSingleAggregate(MarkerSemanticCluster cluster) { if (cluster.ItemRows.Count != 1 || !cluster.IsHomogeneousCategory) { return false; } if (cluster.HomogeneousCategory == MarkerSemanticCategory.CommandState || cluster.HomogeneousCategory == MarkerSemanticCategory.Unknown) { return false; } string text = cluster.ItemRows[0].ItemIdentity ?? string.Empty; if (!text.StartsWith("COMMAND:", StringComparison.OrdinalIgnoreCase)) { return !text.StartsWith("UNKNOWN:", StringComparison.OrdinalIgnoreCase); } return false; } private static void AppendExpandedDetailRows(StringBuilder builder, IReadOnlyList itemRows, int requestedRows, int totalPhysicalCount, MarkerLanguage language, out int shownRows, out int overflowPhysicalCount) { int num = Math.Min(ClampDetailRows(requestedRows), 12); Span span = stackalloc int[12]; for (int i = 0; i < span.Length; i++) { span[i] = -1; } int num2 = 0; for (int j = 0; j < itemRows.Count; j++) { MarkerItemAggregate left = itemRows[j]; if (left.Count <= 0) { continue; } int num3 = num2; for (int k = 0; k < num2; k++) { if (CompareExpandedDetailPriority(left, itemRows[span[k]]) < 0) { num3 = k; break; } } if (num3 < num) { if (num2 < num) { num2++; } for (int num4 = num2 - 1; num4 > num3; num4--) { span[num4] = span[num4 - 1]; } span[num3] = j; } } int num5 = 0; for (int l = 0; l < num2; l++) { MarkerItemAggregate markerItemAggregate = itemRows[span[l]]; if (builder.Length > 0) { builder.Append('\n'); } builder.Append(markerItemAggregate.LocalizedName); if (markerItemAggregate.Count > 1) { builder.Append(" ×").Append(markerItemAggregate.Count.ToString(CultureInfo.InvariantCulture)); } num5 += markerItemAggregate.Count; } shownRows = num2; overflowPhysicalCount = Math.Max(0, totalPhysicalCount - num5); if (overflowPhysicalCount > 0) { if (builder.Length > 0) { builder.Append('\n'); } builder.Append(MarkerTextLocalization.FormatPhysicalItems(overflowPhysicalCount, language)); } } private static int CompareExpandedDetailPriority(MarkerItemAggregate left, MarkerItemAggregate right) { int num = right.Count.CompareTo(left.Count); if (num != 0) { return num; } int num2 = MarkerSemanticCategoryPolicy.StableOrder(left.Category).CompareTo(MarkerSemanticCategoryPolicy.StableOrder(right.Category)); if (num2 != 0) { return num2; } int num3 = string.Compare(left.LocalizedName, right.LocalizedName, StringComparison.Ordinal); if (num3 != 0) { return num3; } return string.Compare(left.ItemIdentity, right.ItemIdentity, StringComparison.Ordinal); } private static void AppendDetailedComposition(StringBuilder builder, IReadOnlyList composition, MarkerLanguage language) { int num = 0; int num2 = 0; for (int i = 0; i < composition.Count; i++) { MarkerCategoryCount markerCategoryCount = composition[i]; if (markerCategoryCount.Count > 0) { string text = LocalizedCompositionWord(markerCategoryCount.Category, language) + " " + markerCategoryCount.Count.ToString(CultureInfo.InvariantCulture); int num3 = ((num != 0) ? 3 : 0); if (num > 0 && num + num3 + text.Length > 48 && num2 + 1 < 2) { builder.Append('\n'); num = 0; num2++; num3 = 0; } else if (num == 0) { builder.Append('\n'); } if (num > 0) { builder.Append(" · "); } builder.Append(text); num += num3 + text.Length; } } } private static MarkerCompactCategoryBadge[] BuildCategoryEntriesWithLifetime(MarkerSemanticCluster cluster, MarkerCategorySortOrder sortOrder, bool preserveCompositionOrder = false) { MarkerCompactCategoryBadge[] array; if (preserveCompositionOrder) { int num = 0; for (int i = 0; i < cluster.Composition.Count; i++) { if (num >= 11) { break; } if (cluster.Composition[i].Count > 0) { num++; } } array = new MarkerCompactCategoryBadge[num]; int num2 = 0; for (int j = 0; j < cluster.Composition.Count; j++) { if (num2 >= array.Length) { break; } MarkerCategoryCount markerCategoryCount = cluster.Composition[j]; if (markerCategoryCount.Count > 0) { array[num2++] = new MarkerCompactCategoryBadge(markerCategoryCount.Category, markerCategoryCount.Count, MarkerCategorySummaryPolicy.DisplayPriority(markerCategoryCount.Category)); } } } else { array = MarkerCategorySummaryPolicy.BuildCategoryEntries(cluster.Composition, sortOrder); } if (array.Length == 0) { return array; } List list = new List(Math.Min(22, array.Length * 2)); for (int k = 0; k < array.Length; k++) { if (list.Count >= 22) { break; } MarkerCompactCategoryBadge markerCompactCategoryBadge = array[k]; int num3 = 0; int num4 = 0; int num5 = 0; for (int l = 0; l < cluster.ItemRows.Count; l++) { MarkerItemAggregate markerItemAggregate = cluster.ItemRows[l]; if (markerItemAggregate.Category == markerCompactCategoryBadge.Category && markerItemAggregate.Count > 0) { switch (markerItemAggregate.Lifetime) { case MarkerLifetimeKind.Temporary: num3 += markerItemAggregate.Count; break; case MarkerLifetimeKind.Permanent: num4 += markerItemAggregate.Count; break; default: num5 += markerItemAggregate.Count; break; } } } int num6 = Math.Max(0, num4 + num5); if (num6 > 0 && list.Count < 22) { MarkerLifetimeKind lifetime = ((num5 != 0) ? MarkerLifetimeKind.Unknown : MarkerLifetimeKind.Permanent); list.Add(new MarkerCompactCategoryBadge(markerCompactCategoryBadge.Category, num6, markerCompactCategoryBadge.DisplayPriority, lifetime, MarkerPresentationGlyphKind.Diamond)); } if (num3 > 0 && list.Count < 22) { list.Add(new MarkerCompactCategoryBadge(markerCompactCategoryBadge.Category, num3, markerCompactCategoryBadge.DisplayPriority, MarkerLifetimeKind.Temporary, MarkerPresentationGlyphKind.Clock)); } } return list.ToArray(); } private static MarkerLifetimeIndicatorSpec BuildClusterLifetimeIndicator(MarkerSemanticCluster cluster, bool allowCount, bool showSingleCountWhenMixed) { return MarkerLifetimePolicy.BuildIndicator(cluster.LifetimeSummary, cluster.TemporaryPhysicalMemberCount, cluster.MixedLifetimeMemberCount, cluster.UnknownLifetimeMemberCount, allowCount, showSingleCountWhenMixed); } private static void AppendLifetimeSummary(StringBuilder builder, MarkerSemanticCluster cluster, bool showTemporaryCount, bool russian) { string value = MarkerLifetimePolicy.BuildSummaryLine(cluster.LifetimeSummary, cluster.TemporaryPhysicalMemberCount, showTemporaryCount, russian); if (!string.IsNullOrEmpty(value)) { if (builder.Length > 0) { builder.Append('\n'); } builder.Append(value); } } private static void AppendDistance(StringBuilder builder, bool showDistance, int distanceMeters, MarkerLanguage language) { if (showDistance) { if (builder.Length > 0) { builder.Append('\n'); } builder.Append(Math.Max(0, distanceMeters).ToString(CultureInfo.InvariantCulture)).Append(" ").Append(MarkerTextLocalization.DistanceUnit(language)); } } } public sealed class MarkerDenseAreaPresentationNode { public long StableKey { get; } public bool IsDenseSummary { get; } public IReadOnlyList PhysicalClusterKeys { get; } public MarkerSemanticCluster PresentationCluster { get; } public MarkerWorldPoint WorldAnchor => PresentationCluster.WorldAnchor; public IReadOnlyList MemberStableKeys => PresentationCluster.MemberStableKeys; public int TotalCount => PresentationCluster.TotalCount; public string MemberFingerprint => PresentationCluster.MemberFingerprint; internal MarkerDenseAreaPresentationNode(long stableKey, bool isDenseSummary, long[] physicalClusterKeys, MarkerSemanticCluster presentationCluster) { StableKey = stableKey; IsDenseSummary = isDenseSummary; PhysicalClusterKeys = physicalClusterKeys ?? Array.Empty(); PresentationCluster = presentationCluster ?? throw new ArgumentNullException("presentationCluster"); } } public sealed class MarkerDenseAreaUpdate { public IReadOnlyList Nodes { get; } public bool MembershipChanged { get; } internal MarkerDenseAreaUpdate(IReadOnlyList nodes, bool membershipChanged) { Nodes = nodes; MembershipChanged = membershipChanged; } } public sealed class MarkerDenseAreaSummaryTracker { private readonly struct PairKey : IEquatable { public long A { get; } public long B { get; } public PairKey(long a, long b) { if (a <= b) { A = a; B = b; } else { A = b; B = a; } } public bool Equals(PairKey other) { if (A == other.A) { return B == other.B; } return false; } public override bool Equals(object? obj) { if (obj is PairKey other) { return Equals(other); } return false; } public override int GetHashCode() { return (A.GetHashCode() * 397) ^ B.GetHashCode(); } } private sealed class PairState { public bool Linked; public bool Pending; public bool PendingTarget; public double PendingSince; } private sealed class PreviousDenseOwner { public long OwnerKey { get; } public long[] MemberKeys { get; } public PreviousDenseOwner(long ownerKey, long[] memberKeys) { OwnerKey = ownerKey; MemberKeys = memberKeys; } } private sealed class MutableAggregate { public string ItemIdentity { get; } public string LocalizedName { get; } public MarkerSemanticCategory Category { get; } public MarkerLifetimeKind Lifetime { get; } public int Count { get; set; } public MutableAggregate(string itemIdentity, string localizedName, MarkerSemanticCategory category, MarkerLifetimeKind lifetime) { ItemIdentity = itemIdentity; LocalizedName = localizedName; Category = category; Lifetime = lifetime; } } public const float MergeRadiusMeters = 12f; public const float SplitRadiusMeters = 16f; public const double ThresholdTransitionDwellSeconds = 0.35; public const int MinimumPhysicalClustersForSummary = 2; private readonly List _orderedClusters = new List(); private readonly Dictionary _pairStates = new Dictionary(); private readonly List _stalePairs = new List(); private readonly List _nodes = new List(); private readonly List _previousDenseOwners = new List(); private string _lastMembershipSignature = string.Empty; public IReadOnlyList Nodes => _nodes; public MarkerDenseAreaUpdate Update(IReadOnlyList physicalClusters, double nowSeconds) { if (physicalClusters == null) { throw new ArgumentNullException("physicalClusters"); } if (double.IsNaN(nowSeconds) || double.IsInfinity(nowSeconds)) { nowSeconds = 0.0; } _orderedClusters.Clear(); for (int i = 0; i < physicalClusters.Count; i++) { if (physicalClusters[i] != null) { _orderedClusters.Add(physicalClusters[i]); } } _orderedClusters.Sort((MarkerSemanticCluster left, MarkerSemanticCluster right) => left.StableKey.CompareTo(right.StableKey)); ReconcileNewPairsImmediately(); AdvanceExistingPairs(nowSeconds); PrunePairStates(); List list = BuildPresentationNodes(); string text = BuildMembershipSignature(list); bool membershipChanged = !string.Equals(text, _lastMembershipSignature, StringComparison.Ordinal); _lastMembershipSignature = text; _nodes.Clear(); _nodes.AddRange(list); CapturePreviousDenseOwners(); return new MarkerDenseAreaUpdate(_nodes, membershipChanged); } public void Clear() { _orderedClusters.Clear(); _pairStates.Clear(); _stalePairs.Clear(); _nodes.Clear(); _previousDenseOwners.Clear(); _lastMembershipSignature = string.Empty; } private void ReconcileNewPairsImmediately() { float num = 144f; for (int i = 0; i < _orderedClusters.Count; i++) { for (int j = i + 1; j < _orderedClusters.Count; j++) { PairKey key = new PairKey(_orderedClusters[i].StableKey, _orderedClusters[j].StableKey); if (!_pairStates.ContainsKey(key)) { _pairStates[key] = new PairState { Linked = (MarkerWorldPoint.DistanceSquared(_orderedClusters[i].WorldAnchor, _orderedClusters[j].WorldAnchor) <= num) }; } } } } private void AdvanceExistingPairs(double nowSeconds) { float num = 144f; float num2 = 256f; foreach (KeyValuePair pairState in _pairStates) { MarkerSemanticCluster markerSemanticCluster = FindCluster(pairState.Key.A); MarkerSemanticCluster markerSemanticCluster2 = FindCluster(pairState.Key.B); if (markerSemanticCluster == null || markerSemanticCluster2 == null) { continue; } float num3 = MarkerWorldPoint.DistanceSquared(markerSemanticCluster.WorldAnchor, markerSemanticCluster2.WorldAnchor); PairState value = pairState.Value; if (value.Linked) { if (num3 <= num2) { value.Pending = false; } else { AdvancePending(value, target: false, nowSeconds); } } else if (num3 >= num) { value.Pending = false; } else { AdvancePending(value, target: true, nowSeconds); } } } private static void AdvancePending(PairState state, bool target, double nowSeconds) { if (!state.Pending || state.PendingTarget != target) { state.Pending = true; state.PendingTarget = target; state.PendingSince = nowSeconds; } else if (!(nowSeconds - state.PendingSince < 0.35)) { state.Linked = target; state.Pending = false; } } private void PrunePairStates() { _stalePairs.Clear(); foreach (PairKey key in _pairStates.Keys) { if (FindCluster(key.A) == null || FindCluster(key.B) == null) { _stalePairs.Add(key); } } for (int i = 0; i < _stalePairs.Count; i++) { _pairStates.Remove(_stalePairs[i]); } } private MarkerSemanticCluster? FindCluster(long key) { int num = 0; int num2 = _orderedClusters.Count - 1; while (num <= num2) { int num3 = num + (num2 - num >> 1); long stableKey = _orderedClusters[num3].StableKey; if (stableKey == key) { return _orderedClusters[num3]; } if (stableKey < key) { num = num3 + 1; } else { num2 = num3 - 1; } } return null; } private List BuildPresentationNodes() { List list = new List(_orderedClusters.Count); if (_orderedClusters.Count == 0) { return list; } int[] array = new int[_orderedClusters.Count]; for (int i = 0; i < array.Length; i++) { array[i] = i; } foreach (KeyValuePair pairState in _pairStates) { if (pairState.Value.Linked) { int num = IndexOfCluster(pairState.Key.A); int num2 = IndexOfCluster(pairState.Key.B); if (num >= 0 && num2 >= 0) { Union(array, num, num2); } } } Dictionary> dictionary = new Dictionary>(); for (int j = 0; j < _orderedClusters.Count; j++) { int key = Find(array, j); if (!dictionary.TryGetValue(key, out var value)) { value = new List(); dictionary.Add(key, value); } value.Add(_orderedClusters[j]); } HashSet claimedPreviousOwners = new HashSet(); foreach (List value2 in dictionary.Values) { value2.Sort((MarkerSemanticCluster left, MarkerSemanticCluster right) => left.StableKey.CompareTo(right.StableKey)); long[] memberKeys = CollectMemberKeys(value2); if (value2.Count < 2) { if (TryClaimPreviousOwnerKey(memberKeys, claimedPreviousOwners, out var ownerKey)) { MarkerSemanticCluster presentationCluster = BuildSummaryCluster(ownerKey, value2, memberKeys); list.Add(new MarkerDenseAreaPresentationNode(ownerKey, isDenseSummary: true, new long[1] { value2[0].StableKey }, presentationCluster)); } else { MarkerSemanticCluster markerSemanticCluster = value2[0]; list.Add(new MarkerDenseAreaPresentationNode(markerSemanticCluster.StableKey, isDenseSummary: false, new long[1] { markerSemanticCluster.StableKey }, markerSemanticCluster)); } } else { long num3 = ResolvePersistentOwnerKey(memberKeys, claimedPreviousOwners); MarkerSemanticCluster presentationCluster2 = BuildSummaryCluster(num3, value2, memberKeys); long[] array2 = new long[value2.Count]; for (int num4 = 0; num4 < value2.Count; num4++) { array2[num4] = value2[num4].StableKey; } list.Add(new MarkerDenseAreaPresentationNode(num3, isDenseSummary: true, array2, presentationCluster2)); } } list.Sort((MarkerDenseAreaPresentationNode left, MarkerDenseAreaPresentationNode right) => left.StableKey.CompareTo(right.StableKey)); return list; } private long ResolvePersistentOwnerKey(long[] memberKeys, HashSet claimedPreviousOwners) { if (TryClaimPreviousOwnerKey(memberKeys, claimedPreviousOwners, out var ownerKey)) { return ownerKey; } long num = -MarkerWorldClusterTracker.StableClusterKey(memberKeys); if (num == 0L || num == long.MinValue) { num = -1L; } claimedPreviousOwners.Add(num); return num; } private bool TryClaimPreviousOwnerKey(long[] memberKeys, HashSet claimedPreviousOwners, out long ownerKey) { long num = 0L; int num2 = 0; for (int i = 0; i < _previousDenseOwners.Count; i++) { PreviousDenseOwner previousDenseOwner = _previousDenseOwners[i]; if (!claimedPreviousOwners.Contains(previousDenseOwner.OwnerKey)) { int num3 = CountOverlap(previousDenseOwner.MemberKeys, memberKeys); if (num3 > num2 || (num3 == num2 && num3 > 0 && previousDenseOwner.OwnerKey < num)) { num2 = num3; num = previousDenseOwner.OwnerKey; } } } if (num2 > 0) { claimedPreviousOwners.Add(num); ownerKey = num; return true; } ownerKey = 0L; return false; } private static MarkerSemanticCluster BuildSummaryCluster(long ownerKey, IReadOnlyList clusters, long[] memberKeys) { Dictionary dictionary = new Dictionary(); Dictionary dictionary2 = new Dictionary(); double num = 0.0; double num2 = 0.0; double num3 = 0.0; int num4 = 0; int num5 = 0; int num6 = 0; int num7 = 0; for (int i = 0; i < clusters.Count; i++) { MarkerSemanticCluster markerSemanticCluster = clusters[i]; int num8 = Math.Max(1, markerSemanticCluster.TotalCount); num += (double)(markerSemanticCluster.WorldAnchor.X * (float)num8); num2 += (double)(markerSemanticCluster.WorldAnchor.Y * (float)num8); num3 += (double)(markerSemanticCluster.WorldAnchor.Z * (float)num8); num4 += num8; num5 += markerSemanticCluster.TemporaryPhysicalMemberCount; num6 += markerSemanticCluster.MixedLifetimeMemberCount; num7 += markerSemanticCluster.UnknownLifetimeMemberCount; for (int j = 0; j < markerSemanticCluster.Composition.Count; j++) { MarkerCategoryCount markerCategoryCount = markerSemanticCluster.Composition[j]; dictionary.TryGetValue(markerCategoryCount.Category, out var value); dictionary[markerCategoryCount.Category] = value + markerCategoryCount.Count; } for (int k = 0; k < markerSemanticCluster.ItemRows.Count; k++) { MarkerItemAggregate markerItemAggregate = markerSemanticCluster.ItemRows[k]; MarkerItemLifetimeKey key = new MarkerItemLifetimeKey(markerItemAggregate.ItemIdentity, markerItemAggregate.Lifetime); if (!dictionary2.TryGetValue(key, out var value2)) { value2 = new MutableAggregate(markerItemAggregate.ItemIdentity, markerItemAggregate.LocalizedName, markerItemAggregate.Category, markerItemAggregate.Lifetime); dictionary2.Add(key, value2); } value2.Count += markerItemAggregate.Count; } } List list = new List(dictionary.Count); foreach (KeyValuePair item in dictionary) { list.Add(new MarkerCategoryCount(item.Key, item.Value)); } list.Sort((MarkerCategoryCount left, MarkerCategoryCount right) => MarkerSemanticCategoryPolicy.StableOrder(left.Category).CompareTo(MarkerSemanticCategoryPolicy.StableOrder(right.Category))); List list2 = new List(dictionary2.Count); foreach (MutableAggregate value3 in dictionary2.Values) { list2.Add(new MarkerItemAggregate(value3.ItemIdentity, value3.LocalizedName, value3.Category, value3.Count, value3.Lifetime)); } list2.Sort(delegate(MarkerItemAggregate left, MarkerItemAggregate right) { int num9 = MarkerSemanticCategoryPolicy.StableOrder(left.Category).CompareTo(MarkerSemanticCategoryPolicy.StableOrder(right.Category)); if (num9 != 0) { return num9; } int num10 = string.Compare(left.LocalizedName, right.LocalizedName, StringComparison.Ordinal); if (num10 != 0) { return num10; } int num11 = string.Compare(left.ItemIdentity, right.ItemIdentity, StringComparison.Ordinal); return (num11 != 0) ? num11 : MarkerLifetimePolicy.DetailedSortRank(left.Lifetime).CompareTo(MarkerLifetimePolicy.DetailedSortRank(right.Lifetime)); }); MarkerWorldPoint worldAnchor = ((num4 > 0) ? new MarkerWorldPoint((float)(num / (double)num4), (float)(num2 / (double)num4), (float)(num3 / (double)num4)) : default(MarkerWorldPoint)); return new MarkerSemanticCluster(ownerKey, "DENSE:" + MarkerWorldClusterTracker.MemberFingerprint(memberKeys), worldAnchor, memberKeys, list.ToArray(), list2.ToArray(), num5, num6, num7); } private static long[] CollectMemberKeys(IReadOnlyList clusters) { List list = new List(); for (int i = 0; i < clusters.Count; i++) { for (int j = 0; j < clusters[i].MemberStableKeys.Count; j++) { list.Add(clusters[i].MemberStableKeys[j]); } } list.Sort(); return list.ToArray(); } private void CapturePreviousDenseOwners() { _previousDenseOwners.Clear(); for (int i = 0; i < _nodes.Count; i++) { MarkerDenseAreaPresentationNode markerDenseAreaPresentationNode = _nodes[i]; if (markerDenseAreaPresentationNode.IsDenseSummary) { long[] array = new long[markerDenseAreaPresentationNode.MemberStableKeys.Count]; for (int j = 0; j < array.Length; j++) { array[j] = markerDenseAreaPresentationNode.MemberStableKeys[j]; } _previousDenseOwners.Add(new PreviousDenseOwner(markerDenseAreaPresentationNode.StableKey, array)); } } } private static int CountOverlap(IReadOnlyList left, IReadOnlyList right) { int num = 0; int num2 = 0; int num3 = 0; while (num < left.Count && num2 < right.Count) { if (left[num] == right[num2]) { num3++; num++; num2++; } else if (left[num] < right[num2]) { num++; } else { num2++; } } return num3; } private static string BuildMembershipSignature(IReadOnlyList nodes) { string text = string.Empty; for (int i = 0; i < nodes.Count; i++) { if (i > 0) { text += ";"; } text = text + nodes[i].StableKey.ToString(CultureInfo.InvariantCulture) + ":" + nodes[i].MemberFingerprint; } return text; } private int IndexOfCluster(long key) { int num = 0; int num2 = _orderedClusters.Count - 1; while (num <= num2) { int num3 = num + (num2 - num >> 1); long stableKey = _orderedClusters[num3].StableKey; if (stableKey == key) { return num3; } if (stableKey < key) { num = num3 + 1; } else { num2 = num3 - 1; } } return -1; } private static int Find(int[] parent, int index) { while (parent[index] != index) { parent[index] = parent[parent[index]]; index = parent[index]; } return index; } private static void Union(int[] parent, int left, int right) { int num = Find(parent, left); int num2 = Find(parent, right); if (num != num2) { if (num < num2) { parent[num2] = num; } else { parent[num] = num2; } } } } public readonly struct MarkerDensityCandidate { public long StableKey { get; } public PersonalMarkerKind MarkerKind { get; } public MarkerHudMode Mode { get; } public MarkerHudEdge Edge { get; } public float X { get; } public float Y { get; } public int DistanceMeters { get; } public MarkerDensityCandidate(long stableKey, PersonalMarkerKind markerKind, MarkerHudMode mode, MarkerHudEdge edge, float x, float y, int distanceMeters) { StableKey = stableKey; MarkerKind = markerKind; Mode = mode; Edge = edge; X = x; Y = y; DistanceMeters = Math.Max(0, distanceMeters); } } public readonly struct MarkerDensityDecision { public long StableKey { get; } public int HiddenMemberCount { get; } public int ClusterSize { get; } public bool CompactCluster { get; } public MarkerDensityDecision(long stableKey, int hiddenMemberCount, int clusterSize, bool compactCluster) { StableKey = stableKey; HiddenMemberCount = Math.Max(0, hiddenMemberCount); ClusterSize = Math.Max(1, clusterSize); CompactCluster = compactCluster; } } public readonly struct MarkerDensityCluster { public MarkerDensityCandidate Representative { get; } public int MemberCount { get; } public MarkerDensityCluster(MarkerDensityCandidate representative, int memberCount) { Representative = representative; MemberCount = Math.Max(1, memberCount); } public MarkerDensityCluster AddMember() { return new MarkerDensityCluster(Representative, MemberCount + 1); } } public static class MarkerDensityPolicy { private sealed class MarkerDensityCandidateComparer : IComparer { public static readonly MarkerDensityCandidateComparer Instance = new MarkerDensityCandidateComparer(); public int Compare(MarkerDensityCandidate left, MarkerDensityCandidate right) { int num = KindPriority(left.MarkerKind).CompareTo(KindPriority(right.MarkerKind)); if (num != 0) { return num; } int num2 = left.DistanceMeters.CompareTo(right.DistanceMeters); if (num2 != 0) { return num2; } return left.StableKey.CompareTo(right.StableKey); } private static int KindPriority(PersonalMarkerKind kind) { return (kind != PersonalMarkerKind.CommandPicker) ? 1 : 0; } } public const float ReferenceWidth = 1920f; public const float ReferenceHeight = 1080f; public const float BaseClusterWidth1080 = 230f; public const float BaseClusterHeight1080 = 92f; public const float EdgeClusterAxis1080 = 108f; public const float DensityCoverageFraction = 0.075f; public const float NominalFullLabelWidth1080 = 270f; public const float NominalFullLabelHeight1080 = 46f; public const int MinimumRepresentativeBudget = 8; public const int MaximumRepresentativeBudget = 24; public const int MaximumGroupingPasses = 5; public const float GroupingExpansionPerPass = 1.7f; public static int CalculateRepresentativeBudget(float screenWidth, float screenHeight) { if (!IsFinitePositive(screenWidth) || !IsFinitePositive(screenHeight)) { return 8; } float num = ResolutionScale(screenWidth, screenHeight); float num2 = 12420f * num * num; int num3 = ((num2 <= 0f) ? 8 : ((int)Math.Floor(screenWidth * screenHeight * 0.075f / num2))); if (num3 < 8) { return 8; } if (num3 <= 24) { return num3; } return 24; } public static void ResolveBuffered(IReadOnlyList candidates, float screenWidth, float screenHeight, List orderedBuffer, List clusterBuffer, List resultBuffer) { if (candidates == null) { throw new ArgumentNullException("candidates"); } if (orderedBuffer == null) { throw new ArgumentNullException("orderedBuffer"); } if (clusterBuffer == null) { throw new ArgumentNullException("clusterBuffer"); } if (resultBuffer == null) { throw new ArgumentNullException("resultBuffer"); } orderedBuffer.Clear(); clusterBuffer.Clear(); resultBuffer.Clear(); if (!IsFinitePositive(screenWidth) || !IsFinitePositive(screenHeight)) { return; } int num = Math.Min(candidates.Count, 96); for (int i = 0; i < num; i++) { orderedBuffer.Add(candidates[i]); } orderedBuffer.Sort(MarkerDensityCandidateComparer.Instance); if (orderedBuffer.Count == 0) { return; } int num2 = CalculateRepresentativeBudget(screenWidth, screenHeight); float num3 = 1f; for (int j = 0; j < 5; j++) { BuildClusters(orderedBuffer, screenWidth, screenHeight, num3, clusterBuffer); if (clusterBuffer.Count <= num2) { break; } num3 *= 1.7f; } if (clusterBuffer.Count > num2) { for (int num4 = clusterBuffer.Count - 1; num4 >= num2; num4--) { MarkerDensityCluster markerDensityCluster = clusterBuffer[num4]; int num5 = FindNearestCompatibleCluster(clusterBuffer, num2, markerDensityCluster.Representative); if (num5 < 0) { num5 = FindNearestCluster(clusterBuffer, num2, markerDensityCluster.Representative); } if (num5 >= 0) { MarkerDensityCluster markerDensityCluster2 = clusterBuffer[num5]; clusterBuffer[num5] = new MarkerDensityCluster(markerDensityCluster2.Representative, markerDensityCluster2.MemberCount + markerDensityCluster.MemberCount); } clusterBuffer.RemoveAt(num4); } } for (int k = 0; k < clusterBuffer.Count; k++) { MarkerDensityCluster markerDensityCluster3 = clusterBuffer[k]; resultBuffer.Add(new MarkerDensityDecision(markerDensityCluster3.Representative.StableKey, markerDensityCluster3.MemberCount - 1, markerDensityCluster3.MemberCount, markerDensityCluster3.MemberCount > 1)); } } public static int SumRepresentedIdentities(IReadOnlyList decisions) { if (decisions == null) { throw new ArgumentNullException("decisions"); } int num = 0; for (int i = 0; i < decisions.Count; i++) { num += decisions[i].ClusterSize; } return num; } private static void BuildClusters(IReadOnlyList ordered, float screenWidth, float screenHeight, float expansion, List clusters) { clusters.Clear(); float num = ResolutionScale(screenWidth, screenHeight); float num2 = 230f * num * expansion; float num3 = 92f * num * expansion; float num4 = 108f * num * expansion; for (int i = 0; i < ordered.Count; i++) { MarkerDensityCandidate markerDensityCandidate = ordered[i]; int num5 = -1; float num6 = float.PositiveInfinity; for (int j = 0; j < clusters.Count; j++) { MarkerDensityCandidate representative = clusters[j].Representative; if (!SamePresentationRail(representative, markerDensityCandidate)) { continue; } float num7 = Math.Abs(markerDensityCandidate.X - representative.X); float num8 = Math.Abs(markerDensityCandidate.Y - representative.Y); bool num9; if (markerDensityCandidate.Mode != MarkerHudMode.OffScreenEdge) { if (!(num7 <= num2)) { continue; } num9 = num8 <= num3; } else { num9 = AxisDistance(markerDensityCandidate.Edge, num7, num8) <= num4; } if (num9) { float num10 = num7 * num7 + num8 * num8; if (!(num10 >= num6)) { num5 = j; num6 = num10; } } } if (num5 < 0) { clusters.Add(new MarkerDensityCluster(markerDensityCandidate, 1)); } else { clusters[num5] = clusters[num5].AddMember(); } } } private static int FindNearestCompatibleCluster(IReadOnlyList clusters, int limit, MarkerDensityCandidate candidate) { int result = -1; float num = float.PositiveInfinity; for (int i = 0; i < limit; i++) { MarkerDensityCandidate representative = clusters[i].Representative; if (SamePresentationRail(representative, candidate)) { float num2 = representative.X - candidate.X; float num3 = representative.Y - candidate.Y; float num4 = num2 * num2 + num3 * num3; if (!(num4 >= num)) { num = num4; result = i; } } } return result; } private static int FindNearestCluster(IReadOnlyList clusters, int limit, MarkerDensityCandidate candidate) { int result = -1; float num = float.PositiveInfinity; for (int i = 0; i < limit; i++) { MarkerDensityCandidate representative = clusters[i].Representative; float num2 = representative.X - candidate.X; float num3 = representative.Y - candidate.Y; float num4 = num2 * num2 + num3 * num3; if (!(num4 >= num)) { num = num4; result = i; } } return result; } private static bool SamePresentationRail(MarkerDensityCandidate left, MarkerDensityCandidate right) { if (left.Mode == right.Mode) { if (left.Mode == MarkerHudMode.OffScreenEdge) { return left.Edge == right.Edge; } return true; } return false; } private static float AxisDistance(MarkerHudEdge edge, float dx, float dy) { if (edge != MarkerHudEdge.Left && edge != MarkerHudEdge.Right) { return dx; } return dy; } private static float ResolutionScale(float screenWidth, float screenHeight) { return Math.Max(0.55f, Math.Min(screenWidth / 1920f, screenHeight / 1080f)); } private static bool IsFinitePositive(float value) { if (!float.IsNaN(value) && !float.IsInfinity(value)) { return value > 0f; } return false; } } public enum MarkerDirectionSector { Right, UpRight, Up, UpLeft, Left, DownLeft, Down, DownRight } public readonly struct MarkerDirectionalInput { public long PresentationKey { get; } public float DirectionX { get; } public float DirectionY { get; } public float DistanceMeters { get; } public int TotalCount { get; } public MarkerDirectionalInput(long presentationKey, float directionX, float directionY, float distanceMeters, int totalCount) { PresentationKey = presentationKey; DirectionX = directionX; DirectionY = directionY; DistanceMeters = SanitizeDistance(distanceMeters); TotalCount = Math.Max(0, totalCount); } private static float SanitizeDistance(float value) { if (!float.IsNaN(value) && !float.IsInfinity(value) && !(value < 0f)) { return value; } return 0f; } } public readonly struct MarkerDirectionalSectorSummary { public MarkerDirectionSector Sector { get; } public long PresentationKey => MarkerDirectionalAggregationPolicy.SectorPresentationKey(Sector); public long NearestPresentationKey { get; } public float DirectionX { get; } public float DirectionY { get; } public float NearestDistanceMeters { get; } public int TotalCount { get; } public int RepresentedNodeCount { get; } public MarkerDirectionalSectorSummary(MarkerDirectionSector sector, long nearestPresentationKey, float directionX, float directionY, float nearestDistanceMeters, int totalCount, int representedNodeCount) { Sector = sector; NearestPresentationKey = nearestPresentationKey; DirectionX = directionX; DirectionY = directionY; NearestDistanceMeters = nearestDistanceMeters; TotalCount = Math.Max(0, totalCount); RepresentedNodeCount = Math.Max(0, representedNodeCount); } } public static class MarkerDirectionalAggregationPolicy { private sealed class Bucket { private readonly MarkerDirectionSector _sector; private float _sumX; private float _sumY; private float _nearestDistance = float.MaxValue; private long _nearestKey; private int _totalCount; public int Count { get; private set; } public Bucket(MarkerDirectionSector sector) { _sector = sector; } public void Add(MarkerDirectionalInput input) { Count++; float num = (float)Math.Sqrt(input.DirectionX * input.DirectionX + input.DirectionY * input.DirectionY); if (IsFinite(num) && num > 0.0001f) { _sumX += input.DirectionX / num; _sumY += input.DirectionY / num; } _totalCount += input.TotalCount; if (input.DistanceMeters < _nearestDistance || (Math.Abs(input.DistanceMeters - _nearestDistance) < 0.0001f && input.PresentationKey < _nearestKey)) { _nearestDistance = input.DistanceMeters; _nearestKey = input.PresentationKey; } } public MarkerDirectionalSectorSummary Build() { float num = (float)Math.Sqrt(_sumX * _sumX + _sumY * _sumY); float x = _sumX; float y = _sumY; if (!IsFinite(num) || num <= 0.0001f) { SectorCenter(_sector, out x, out y); } else { x /= num; y /= num; } return new MarkerDirectionalSectorSummary(_sector, _nearestKey, x, y, (_nearestDistance == float.MaxValue) ? 0f : _nearestDistance, _totalCount, Count); } } public const int BroadSectorCount = 8; private const long SectorKeyBase = -9223372036854771712L; public static long SectorPresentationKey(MarkerDirectionSector sector) { return -9223372036854771712L + (long)sector; } public static MarkerDirectionSector SectorForDirection(float directionX, float directionY) { if (!IsFinite(directionX) || !IsFinite(directionY) || (Math.Abs(directionX) < 0.0001f && Math.Abs(directionY) < 0.0001f)) { return MarkerDirectionSector.Up; } double num = Math.Atan2(directionY, directionX) * 180.0 / Math.PI; if (num < 0.0) { num += 360.0; } return ((int)Math.Floor((num + 22.5) / 45.0) & 7) switch { 0 => MarkerDirectionSector.Right, 1 => MarkerDirectionSector.UpRight, 2 => MarkerDirectionSector.Up, 3 => MarkerDirectionSector.UpLeft, 4 => MarkerDirectionSector.Left, 5 => MarkerDirectionSector.DownLeft, 6 => MarkerDirectionSector.Down, _ => MarkerDirectionSector.DownRight, }; } public static IReadOnlyList Aggregate(IReadOnlyList inputs) { if (inputs == null) { throw new ArgumentNullException("inputs"); } Bucket[] array = new Bucket[8]; for (int i = 0; i < inputs.Count; i++) { MarkerDirectionalInput input = inputs[i]; MarkerDirectionSector markerDirectionSector = SectorForDirection(input.DirectionX, input.DirectionY); int num = (int)markerDirectionSector; if (array[num] == null) { array[num] = new Bucket(markerDirectionSector); } array[num].Add(input); } List list = new List(8); foreach (Bucket bucket in array) { if (bucket != null && bucket.Count != 0) { list.Add(bucket.Build()); } } return list; } public static void SectorCenter(MarkerDirectionSector sector, out float x, out float y) { double num = (double)sector * 45.0 * Math.PI / 180.0; x = (float)Math.Cos(num); y = (float)Math.Sin(num); } private static bool IsFinite(float value) { if (!float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } } public sealed class MarkerFovPresentationHysteresisPolicy { public const float EnterAngleDegrees = 42f; public const float ExitAngleDegrees = 48f; private readonly Dictionary _states = new Dictionary(); public bool Update(long presentationKey, float angleFromForwardDegrees, bool projectedInsideNormalFov) { float angleFromForwardDegrees2 = SanitizeAngle(angleFromForwardDegrees); _states.TryGetValue(presentationKey, out var value); bool flag = NextState(value, angleFromForwardDegrees2, projectedInsideNormalFov); _states[presentationKey] = flag; return flag; } public static bool NextState(bool wasInFov, float angleFromForwardDegrees, bool projectedInsideNormalFov) { float num = SanitizeAngle(angleFromForwardDegrees); if (wasInFov) { if (!projectedInsideNormalFov) { return num <= 48f; } return true; } if (projectedInsideNormalFov) { return num <= 42f; } return false; } public void Prune(IEnumerable activePresentationKeys) { if (activePresentationKeys == null) { throw new ArgumentNullException("activePresentationKeys"); } HashSet hashSet = new HashSet(activePresentationKeys); List list = new List(); foreach (long key in _states.Keys) { if (!hashSet.Contains(key)) { list.Add(key); } } for (int i = 0; i < list.Count; i++) { _states.Remove(list[i]); } } public void Clear() { _states.Clear(); } private static float SanitizeAngle(float value) { if (float.IsNaN(value) || float.IsInfinity(value)) { return 180f; } if (value < 0f) { return 0f; } if (!(value > 180f)) { return value; } return 180f; } } public static class MarkerVisualSettingsPolicy { public const float OpacityMin = 0f; public const float OpacityMax = 1f; public const float MarkerOpacityDefault = 1f; public const float BackgroundOpacityDefault = 0f; public const float OffscreenScaleMin = 0.75f; public const float OffscreenScaleMax = 1.25f; public const float OffscreenScaleDefault = 1f; public const float OffscreenOpacityDefault = 1f; public const float OffscreenEdgePaddingMin = 12f; public const float OffscreenEdgePaddingMax = 160f; public const float OffscreenEdgePaddingDefault = 36f; public static float ClampOpacity(float value, float defaultValue) { if (float.IsNaN(value) || float.IsInfinity(value)) { return Clamp01(defaultValue); } return Clamp01(value); } public static float ClampOffscreenScale(float value) { if (float.IsNaN(value) || float.IsInfinity(value)) { return 1f; } if (value < 0.75f) { return 0.75f; } if (!(value > 1.25f)) { return value; } return 1.25f; } public static float ClampOffscreenEdgePadding(float value) { if (float.IsNaN(value) || float.IsInfinity(value)) { return 36f; } if (value < 12f) { return 12f; } if (!(value > 160f)) { return value; } return 160f; } private static float Clamp01(float value) { if (value < 0f) { return 0f; } if (!(value > 1f)) { return value; } return 1f; } } public static class MarkerFramePipelinePolicy { public const float ProjectionPositionEpsilonPixels = 2f; public const float ProjectionRotationEpsilonDegrees = 1f; public const float UiPositionWriteEpsilonPixels = 0.5f; public const float UiRotationWriteEpsilonDegrees = 0.5f; public const float HudRectEpsilonPixels = 0.75f; public const float MultiMarkerMotionSolveIntervalSeconds = 1f / 30f; public const float BlockingModalFallbackDiscoverySeconds = 5f; public const float MessageHudFallbackDiscoverySeconds = 8f; public static bool ShouldEnterActiveMarkerPipeline(int validMarkerCount) { return validMarkerCount > 0; } public static bool CanUseSingleMarkerFastPath(int validMarkerCount) { return validMarkerCount == 1; } public static bool ProjectionMateriallyChanged(MarkerHudProjection previous, MarkerHudProjection current) { if (previous.Valid != current.Valid) { return true; } if (!current.Valid) { return false; } if (previous.Mode != current.Mode || previous.Edge != current.Edge) { return true; } if (Math.Abs(previous.X - current.X) > 2f) { return true; } if (Math.Abs(previous.Y - current.Y) > 2f) { return true; } if (current.Mode == MarkerHudMode.OffScreenEdge && AngularDifference(previous.ArrowRotationDegrees, current.ArrowRotationDegrees) > 1f) { return true; } return false; } public static bool ShouldWriteScreenPosition(bool hasAppliedPosition, float previousX, float previousY, float nextX, float nextY) { if (hasAppliedPosition && !(Math.Abs(previousX - nextX) > 0.5f)) { return Math.Abs(previousY - nextY) > 0.5f; } return true; } public static bool ShouldWriteRotation(bool hasAppliedRotation, float previousDegrees, float nextDegrees) { if (hasAppliedRotation) { return AngularDifference(previousDegrees, nextDegrees) > 0.5f; } return true; } public static bool HudRectMateriallyChanged(bool hadRect, MarkerHudRect previous, bool hasRect, MarkerHudRect current) { if (hadRect != hasRect) { return true; } if (!hasRect) { return false; } if (!(Math.Abs(previous.CenterX - current.CenterX) > 0.75f) && !(Math.Abs(previous.CenterY - current.CenterY) > 0.75f) && !(Math.Abs(previous.Width - current.Width) > 0.75f)) { return Math.Abs(previous.Height - current.Height) > 0.75f; } return true; } public static bool ShouldRunMultiMarkerSolve(bool hasPlacementCache, bool structuralInvalidation, bool projectionMateriallyChanged, float now, float nextMotionSolveAt) { if (!hasPlacementCache || structuralInvalidation) { return true; } if (projectionMateriallyChanged) { return now >= nextMotionSolveAt; } return false; } public static bool ShouldRunGlobalUiDiscovery(bool lifecycleInvalidated, bool urgentUiSignal, bool cacheMissingOrStale, float now, float nextFallbackAt) { if (!(lifecycleInvalidated || urgentUiSignal)) { if (cacheMissingOrStale) { return now >= nextFallbackAt; } return false; } return true; } private static float AngularDifference(float left, float right) { float num = Math.Abs(left - right) % 360f; if (!(num > 180f)) { return num; } return 360f - num; } } public readonly struct MarkerRuntimePerformanceSnapshot { public long UnityUpdateCalls { get; } public long RenderFrameCalls { get; } public long FullPlacementSolves { get; } public long SingleMarkerFastPathCalls { get; } public long GlobalHudDiscoveries { get; } public long TmpPreferredMeasurements { get; } public long UiLayoutWrites { get; } public long DiagnosticRecords { get; } public long HeavySolveStopwatchTicks { get; } public long MaxHeavySolveStopwatchTicks { get; } public double HeavySolveMilliseconds => (double)HeavySolveStopwatchTicks * 1000.0 / (double)Stopwatch.Frequency; public double MaxHeavySolveMilliseconds => (double)MaxHeavySolveStopwatchTicks * 1000.0 / (double)Stopwatch.Frequency; public MarkerRuntimePerformanceSnapshot(long unityUpdateCalls, long renderFrameCalls, long fullPlacementSolves, long singleMarkerFastPathCalls, long globalHudDiscoveries, long tmpPreferredMeasurements, long uiLayoutWrites, long diagnosticRecords, long heavySolveStopwatchTicks, long maxHeavySolveStopwatchTicks) { UnityUpdateCalls = unityUpdateCalls; RenderFrameCalls = renderFrameCalls; FullPlacementSolves = fullPlacementSolves; SingleMarkerFastPathCalls = singleMarkerFastPathCalls; GlobalHudDiscoveries = globalHudDiscoveries; TmpPreferredMeasurements = tmpPreferredMeasurements; UiLayoutWrites = uiLayoutWrites; DiagnosticRecords = diagnosticRecords; HeavySolveStopwatchTicks = heavySolveStopwatchTicks; MaxHeavySolveStopwatchTicks = maxHeavySolveStopwatchTicks; } } public sealed class MarkerRuntimePerformanceCounters { private long _unityUpdateCalls; private long _renderFrameCalls; private long _fullPlacementSolves; private long _singleMarkerFastPathCalls; private long _globalHudDiscoveries; private long _tmpPreferredMeasurements; private long _uiLayoutWrites; private long _diagnosticRecords; private long _heavySolveStopwatchTicks; private long _maxHeavySolveStopwatchTicks; public void RecordUnityUpdate() { _unityUpdateCalls++; } public void RecordRenderFrame() { _renderFrameCalls++; } public void RecordFullPlacementSolve(long elapsedStopwatchTicks) { _fullPlacementSolves++; if (elapsedStopwatchTicks > 0) { _heavySolveStopwatchTicks += elapsedStopwatchTicks; if (elapsedStopwatchTicks > _maxHeavySolveStopwatchTicks) { _maxHeavySolveStopwatchTicks = elapsedStopwatchTicks; } } } public void RecordSingleMarkerFastPath() { _singleMarkerFastPathCalls++; } public void RecordGlobalHudDiscovery() { _globalHudDiscoveries++; } public void RecordTmpPreferredMeasurement() { _tmpPreferredMeasurements++; } public void RecordUiLayoutWrite(int count = 1) { if (count > 0) { _uiLayoutWrites += count; } } public void RecordDiagnosticRecord() { _diagnosticRecords++; } public MarkerRuntimePerformanceSnapshot Snapshot() { return new MarkerRuntimePerformanceSnapshot(_unityUpdateCalls, _renderFrameCalls, _fullPlacementSolves, _singleMarkerFastPathCalls, _globalHudDiscoveries, _tmpPreferredMeasurements, _uiLayoutWrites, _diagnosticRecords, _heavySolveStopwatchTicks, _maxHeavySolveStopwatchTicks); } } public enum MarkerHudMode { OnScreenWorldAnchor, OffScreenEdge } public enum MarkerHudEdge { None, Left, Right, Top, Bottom } public readonly struct MarkerHudSafeArea { public float Left { get; } public float Right { get; } public float Bottom { get; } public float Top { get; } public MarkerHudSafeArea(float left, float right, float bottom, float top) { Left = left; Right = right; Bottom = bottom; Top = top; } public bool Contains(float x, float y) { if (x >= Left && x <= Right && y >= Bottom) { return y <= Top; } return false; } } public readonly struct MarkerHudExclusionZone { public string Token { get; } public float Left { get; } public float Right { get; } public float Bottom { get; } public float Top { get; } public MarkerHudExclusionZone(string token, float left, float right, float bottom, float top) { Token = token ?? throw new ArgumentNullException("token"); Left = left; Right = right; Bottom = bottom; Top = top; } public bool Contains(float x, float y) { if (x >= Left && x <= Right && y >= Bottom) { return y <= Top; } return false; } } public readonly struct MarkerHudRect { public float CenterX { get; } public float CenterY { get; } public float Width { get; } public float Height { get; } public float Left => CenterX - Width * 0.5f; public float Right => CenterX + Width * 0.5f; public float Bottom => CenterY - Height * 0.5f; public float Top => CenterY + Height * 0.5f; public MarkerHudRect(float centerX, float centerY, float width, float height) { CenterX = centerX; CenterY = centerY; Width = Math.Max(0f, width); Height = Math.Max(0f, height); } public bool Intersects(MarkerHudRect other) { if (Left < other.Right && Right > other.Left && Bottom < other.Top) { return Top > other.Bottom; } return false; } public MarkerHudRect Inflated(float amount) { return new MarkerHudRect(CenterX, CenterY, Width + Math.Max(0f, amount) * 2f, Height + Math.Max(0f, amount) * 2f); } } public readonly struct MarkerHudVisualFootprint { public float Width { get; } public float Height { get; } public float IndicatorSize { get; } public float LabelWidth { get; } public float PaddingX { get; } public float Gap { get; } public MarkerHudVisualFootprint(float width, float height, float indicatorSize, float labelWidth, float paddingX, float gap) { Width = width; Height = height; IndicatorSize = indicatorSize; LabelWidth = labelWidth; PaddingX = paddingX; Gap = gap; } } public readonly struct MarkerHudProjection { public bool Valid { get; } public MarkerHudMode Mode { get; } public MarkerHudEdge Edge { get; } public float X { get; } public float Y { get; } public float DirectionX { get; } public float DirectionY { get; } public float ArrowRotationDegrees { get; } public MarkerHudProjection(bool valid, MarkerHudMode mode, MarkerHudEdge edge, float x, float y, float directionX, float directionY, float arrowRotationDegrees) { Valid = valid; Mode = mode; Edge = edge; X = x; Y = y; DirectionX = directionX; DirectionY = directionY; ArrowRotationDegrees = arrowRotationDegrees; } } public readonly struct MarkerHudEdgeCandidate { public long StableKey { get; } public MarkerHudProjection Projection { get; } public MarkerHudEdgeCandidate(long stableKey, MarkerHudProjection projection) { StableKey = stableKey; Projection = projection; } } public readonly struct MarkerHudEdgePlacement { public long StableKey { get; } public MarkerHudEdge Edge { get; } public float X { get; } public float Y { get; } public float ArrowRotationDegrees { get; } public int StackSlot { get; } public MarkerHudEdgePlacement(long stableKey, MarkerHudEdge edge, float x, float y, float arrowRotationDegrees, int stackSlot) { StableKey = stableKey; Edge = edge; X = x; Y = y; ArrowRotationDegrees = arrowRotationDegrees; StackSlot = stackSlot; } } public readonly struct MarkerHudPlacementCandidate { public long StableKey { get; } public MarkerHudProjection Projection { get; } public MarkerHudVisualFootprint Footprint { get; } public MarkerHudPlacementCandidate(long stableKey, MarkerHudProjection projection, MarkerHudVisualFootprint footprint) { StableKey = stableKey; Projection = projection; Footprint = footprint; } } public readonly struct MarkerHudPlacement { public long StableKey { get; } public MarkerHudMode Mode { get; } public MarkerHudEdge Edge { get; } public float X { get; } public float Y { get; } public float ArrowRotationDegrees { get; } public int LaneSlot { get; } public int RailSlot { get; } public int StackSlot => LaneSlot; public MarkerHudRect FinalRect { get; } public bool HudRelocated { get; } public bool CollisionRelocated { get; } public bool MessageHudRelocated { get; } public MarkerHudPlacement(long stableKey, MarkerHudMode mode, MarkerHudEdge edge, float x, float y, float arrowRotationDegrees, int laneSlot, int railSlot, MarkerHudRect finalRect, bool hudRelocated, bool collisionRelocated) : this(stableKey, mode, edge, x, y, arrowRotationDegrees, laneSlot, railSlot, finalRect, hudRelocated, collisionRelocated, messageHudRelocated: false) { } public MarkerHudPlacement(long stableKey, MarkerHudMode mode, MarkerHudEdge edge, float x, float y, float arrowRotationDegrees, int laneSlot, int railSlot, MarkerHudRect finalRect, bool hudRelocated, bool collisionRelocated, bool messageHudRelocated) { StableKey = stableKey; Mode = mode; Edge = edge; X = x; Y = y; ArrowRotationDegrees = arrowRotationDegrees; LaneSlot = laneSlot; RailSlot = railSlot; FinalRect = finalRect; HudRelocated = hudRelocated; CollisionRelocated = collisionRelocated; MessageHudRelocated = messageHudRelocated; } } public static class MarkerHudNavigationPolicy { private sealed class MarkerPlacementCandidateComparer : IComparer { public static readonly MarkerPlacementCandidateComparer Instance = new MarkerPlacementCandidateComparer(); public int Compare(MarkerHudPlacementCandidate left, MarkerHudPlacementCandidate right) { bool flag = left.Projection.Mode == MarkerHudMode.OffScreenEdge; bool flag2 = right.Projection.Mode == MarkerHudMode.OffScreenEdge; if (flag != flag2) { if (!flag) { return 1; } return -1; } if (flag) { int num = ((int)left.Projection.Edge).CompareTo((int)right.Projection.Edge); if (num != 0) { return num; } int num2 = Axis(left.Projection.Edge, left.Projection.X, left.Projection.Y).CompareTo(Axis(right.Projection.Edge, right.Projection.X, right.Projection.Y)); if (num2 != 0) { return num2; } } else { int num3 = left.Projection.Y.CompareTo(right.Projection.Y); if (num3 != 0) { return num3; } int num4 = left.Projection.X.CompareTo(right.Projection.X); if (num4 != 0) { return num4; } } return left.StableKey.CompareTo(right.StableKey); } } public const string NativeHudStyleToken = "ISF_ROR2_HUD_NATIVE_V5_C16"; public const string IndicatorVisualFamilyToken = "ISF_UI_GRAPHIC_INDICATOR_V1"; public const float ReferenceWidth = 1920f; public const float ReferenceHeight = 1080f; public const float HorizontalSafeInset1080 = 170f; public const float BottomSafeInset1080 = 104f; public const float TopSafeInset1080 = 88f; public const float MinimumEdgeSpacing1080 = 54f; public const float EdgeLaneSpacing1080 = 46f; public const float MarkerGap1080 = 10f; public const float ScreenRectMargin1080 = 12f; public const float MeasuredTextSafetyPadding1080 = 12f; public const float ConservativeFallbackLabelWidth1080 = 520f; public const int MaxDeconflictionLanes = 18; public const float MinOnScreenAnchorDisplacement1080 = 110f; public const float MaxOnScreenAnchorDisplacement1080 = 260f; private const float Epsilon = 0.0001f; public static MarkerHudSafeArea GetSafeArea(float screenWidth, float screenHeight) { if (!IsFinite(screenWidth) || !IsFinite(screenHeight) || screenWidth <= 0f || screenHeight <= 0f) { return new MarkerHudSafeArea(0f, 0f, 0f, 0f); } float num = ResolutionScale(screenWidth, screenHeight); float num2 = Math.Min(screenWidth * 0.22f, 170f * num); float bottom = Math.Min(screenHeight * 0.24f, 104f * num); float num3 = Math.Min(screenHeight * 0.22f, 88f * num); return new MarkerHudSafeArea(num2, screenWidth - num2, bottom, screenHeight - num3); } public static IReadOnlyList GetReservedHudZones(float screenWidth, float screenHeight) { if (!IsFinite(screenWidth) || !IsFinite(screenHeight) || screenWidth <= 0f || screenHeight <= 0f) { return Array.Empty(); } float num = ResolutionScale(screenWidth, screenHeight); return new MarkerHudExclusionZone[6] { new MarkerHudExclusionZone("health-bottom-left", 0f, Math.Min(screenWidth, 610f * num), 0f, Math.Min(screenHeight, 260f * num)), new MarkerHudExclusionZone("skills-bottom-right", Math.Max(0f, screenWidth - 710f * num), screenWidth, 0f, Math.Min(screenHeight, 300f * num)), new MarkerHudExclusionZone("objective-upper-right", Math.Max(0f, screenWidth - 590f * num), screenWidth, Math.Max(0f, screenHeight - 360f * num), screenHeight), new MarkerHudExclusionZone("items-top-center", Math.Max(0f, screenWidth * 0.5f - 430f * num), Math.Min(screenWidth, screenWidth * 0.5f + 430f * num), Math.Max(0f, screenHeight - 160f * num), screenHeight), new MarkerHudExclusionZone("money-player-upper-left", 0f, Math.Min(screenWidth, 500f * num), Math.Max(0f, screenHeight - 260f * num), screenHeight), new MarkerHudExclusionZone("stats-overlay-safe-right", Math.Max(0f, screenWidth - 390f * num), screenWidth, Math.Min(screenHeight, 250f * num), Math.Max(0f, screenHeight - 250f * num)) }; } public static bool IsReservedHudPoint(float x, float y, float screenWidth, float screenHeight) { if (!IsFinite(x) || !IsFinite(y) || !IsFinite(screenWidth) || !IsFinite(screenHeight) || screenWidth <= 0f || screenHeight <= 0f) { return true; } float num = ResolutionScale(screenWidth, screenHeight); float num2 = 145f * num; float num3 = 30f * num; bool num4 = x - num2 <= Math.Min(screenWidth, 610f * num) && y - num3 <= Math.Min(screenHeight, 260f * num); bool flag = x + num2 >= Math.Max(0f, screenWidth - 710f * num) && y - num3 <= Math.Min(screenHeight, 300f * num); bool flag2 = x + num2 >= Math.Max(0f, screenWidth - 590f * num) && y + num3 >= Math.Max(0f, screenHeight - 360f * num); float num5 = Math.Max(0f, screenWidth * 0.5f - 430f * num); float num6 = Math.Min(screenWidth, screenWidth * 0.5f + 430f * num); bool flag3 = x + num2 >= num5 && x - num2 <= num6 && y + num3 >= Math.Max(0f, screenHeight - 160f * num); return num4 || flag || flag2 || flag3; } public static MarkerHudVisualFootprint EstimateVisualFootprint(string? label, int roundedDistanceMeters, float screenWidth, float screenHeight) { float num = ResolutionScale(screenWidth, screenHeight); int num2 = MarkerPresentationPolicy.BuildNativeHudFontSize((int)Math.Max(1f, screenHeight)); float num3 = Clamp((float)(MarkerPresentationPolicy.BuildNativeHudLabelText(label, roundedDistanceMeters).Length * num2) * 0.56f, 72f * num, 430f * num); float num4 = Math.Max((float)num2 + 5f * num, 28f * num); float num5 = 10f * num; float num6 = 8f * num; float width = num5 * 2f + num4 + num6 + num3; float height = Math.Max(num4, (float)num2 + 10f * num) + 8f * num; return new MarkerHudVisualFootprint(width, height, num4, num3, num5, num6); } public static MarkerHudVisualFootprint BuildMeasuredVisualFootprint(float preferredLabelWidth, float preferredLabelHeight, float screenWidth, float screenHeight) { return BuildMeasuredVisualFootprint(preferredLabelWidth, preferredLabelHeight, screenWidth, screenHeight, 1f); } public static MarkerHudVisualFootprint BuildMeasuredVisualFootprint(float preferredLabelWidth, float preferredLabelHeight, float screenWidth, float screenHeight, float markerScale) { float num = ResolutionScale(screenWidth, screenHeight); float num2 = MarkerClusterPresentationPolicy.ClampMarkerScale(markerScale); float num3 = num * num2; int num4 = MarkerPresentationPolicy.BuildScaledNativeHudFontSize((int)Math.Max(1f, screenHeight), num2); float num5 = ((IsFinite(preferredLabelWidth) && preferredLabelWidth > 0f) ? preferredLabelWidth : (520f * num3)); float val = ((IsFinite(preferredLabelHeight) && preferredLabelHeight > 0f) ? preferredLabelHeight : ((float)num4 + 10f * num3)); float num6 = Math.Max(72f * num3, num5 + 12f * num3); float num7 = Math.Max((float)num4 + 5f * num3, 28f * num3); float num8 = 10f * num3; float num9 = 8f * num3; float width = num8 * 2f + num7 + num9 + num6; float height = Math.Max(num7, val) + 8f * num3; return new MarkerHudVisualFootprint(width, height, num7, num6, num8, num9); } public static bool IntersectsDynamicHud(MarkerHudRect rect, IEnumerable? dynamicHudZones, float screenWidth, float screenHeight) { if (!IsFiniteRect(rect) || screenWidth <= 0f || screenHeight <= 0f) { return true; } if (dynamicHudZones == null) { return false; } float amount = 4f * ResolutionScale(screenWidth, screenHeight); MarkerHudRect rect2 = rect.Inflated(amount); foreach (MarkerHudExclusionZone dynamicHudZone in dynamicHudZones) { if (RectIntersectsZone(rect2, dynamicHudZone)) { return true; } } return false; } public static bool IntersectsReservedHud(MarkerHudRect rect, float screenWidth, float screenHeight) { if (!IsFiniteRect(rect) || screenWidth <= 0f || screenHeight <= 0f) { return true; } float amount = 4f * ResolutionScale(screenWidth, screenHeight); MarkerHudRect rect2 = rect.Inflated(amount); foreach (MarkerHudExclusionZone reservedHudZone in GetReservedHudZones(screenWidth, screenHeight)) { if (RectIntersectsZone(rect2, reservedHudZone)) { return true; } } return false; } public static bool IsRectInsideScreen(MarkerHudRect rect, float screenWidth, float screenHeight) { if (!IsFiniteRect(rect) || !IsFinite(screenWidth) || !IsFinite(screenHeight) || screenWidth <= 0f || screenHeight <= 0f) { return false; } float num = 12f * ResolutionScale(screenWidth, screenHeight); if (rect.Left >= num && rect.Right <= screenWidth - num && rect.Bottom >= num) { return rect.Top <= screenHeight - num; } return false; } public static MarkerHudProjection ResolveProjection(float viewportX, float viewportY, float viewportDepth, float cameraLocalX, float cameraLocalY, float cameraLocalZ, float screenWidth, float screenHeight) { if (!IsFinite(viewportX) || !IsFinite(viewportY) || !IsFinite(viewportDepth) || !IsFinite(cameraLocalX) || !IsFinite(cameraLocalY) || !IsFinite(cameraLocalZ) || !IsFinite(screenWidth) || !IsFinite(screenHeight) || screenWidth <= 0f || screenHeight <= 0f) { return InvalidProjection(); } MarkerHudSafeArea safeArea = GetSafeArea(screenWidth, screenHeight); float x = viewportX * screenWidth; float y = viewportY * screenHeight; if (viewportDepth > 0.0001f && safeArea.Contains(x, y)) { return new MarkerHudProjection(valid: true, MarkerHudMode.OnScreenWorldAnchor, MarkerHudEdge.None, x, y, 0f, 0f, 0f); } if (!TryResolveDirection(viewportX, viewportY, viewportDepth, cameraLocalX, cameraLocalY, cameraLocalZ, out var directionX, out var directionY)) { return InvalidProjection(); } float num = screenWidth * 0.5f; float num2 = screenHeight * 0.5f; float num3 = ((Math.Abs(directionX) < 0.0001f) ? float.PositiveInfinity : (((directionX > 0f) ? (safeArea.Right - num) : (safeArea.Left - num)) / directionX)); float num4 = ((Math.Abs(directionY) < 0.0001f) ? float.PositiveInfinity : (((directionY > 0f) ? (safeArea.Top - num2) : (safeArea.Bottom - num2)) / directionY)); if (num3 <= 0f) { num3 = float.PositiveInfinity; } if (num4 <= 0f) { num4 = float.PositiveInfinity; } float num5 = Math.Min(num3, num4); if (!IsFinite(num5) || num5 <= 0f) { return InvalidProjection(); } MarkerHudEdge edge = ((!(num3 <= num4)) ? ((directionY >= 0f) ? MarkerHudEdge.Top : MarkerHudEdge.Bottom) : ((!(directionX >= 0f)) ? MarkerHudEdge.Left : MarkerHudEdge.Right)); float resolvedX = Clamp(num + directionX * num5, safeArea.Left, safeArea.Right); float resolvedY = Clamp(num2 + directionY * num5, safeArea.Bottom, safeArea.Top); ResolveReservedEdgePoint(edge, resolvedX, resolvedY, screenWidth, screenHeight, out resolvedX, out resolvedY); float arrowRotationDegrees = 0f - (float)(Math.Atan2(directionX, directionY) * 180.0 / Math.PI); return new MarkerHudProjection(valid: true, MarkerHudMode.OffScreenEdge, edge, resolvedX, resolvedY, directionX, directionY, arrowRotationDegrees); } public static IReadOnlyList ResolvePlacements(IEnumerable candidates, float screenWidth, float screenHeight) { return ResolvePlacements(candidates, screenWidth, screenHeight, Array.Empty()); } public static IReadOnlyList ResolvePlacements(IEnumerable candidates, float screenWidth, float screenHeight, IEnumerable? dynamicHudZones) { if (candidates == null) { throw new ArgumentNullException("candidates"); } if (!IsFinite(screenWidth) || !IsFinite(screenHeight) || screenWidth <= 0f || screenHeight <= 0f) { return Array.Empty(); } MarkerHudExclusionZone[] dynamicHudZones2 = dynamicHudZones?.ToArray() ?? Array.Empty(); MarkerHudPlacementCandidate[] array = (from c in candidates where c.Projection.Valid && IsValidFootprint(c.Footprint) orderby (c.Projection.Mode != MarkerHudMode.OffScreenEdge) ? 1 : 0, (int)((c.Projection.Mode == MarkerHudMode.OffScreenEdge) ? c.Projection.Edge : MarkerHudEdge.None), (c.Projection.Mode != MarkerHudMode.OffScreenEdge) ? c.Projection.Y : Axis(c.Projection.Edge, c.Projection.X, c.Projection.Y), (c.Projection.Mode != MarkerHudMode.OffScreenEdge) ? c.Projection.X : 0f, c.StableKey select c).ToArray(); List list = new List(array.Length); List list2 = new List(array.Length); MarkerHudPlacementCandidate[] array2 = array; for (int num = 0; num < array2.Length; num++) { MarkerHudPlacementCandidate candidate = array2[num]; MarkerHudPlacement item = ((candidate.Projection.Mode == MarkerHudMode.OffScreenEdge) ? ResolveEdgePlacement(candidate, list, dynamicHudZones2, screenWidth, screenHeight) : ResolveOnScreenPlacement(candidate, list, dynamicHudZones2, screenWidth, screenHeight)); list.Add(item.FinalRect); list2.Add(item); } return list2; } public static MarkerHudPlacement ResolveSinglePlacement(MarkerHudPlacementCandidate candidate, float screenWidth, float screenHeight, IReadOnlyList? dynamicHudZones = null) { if (!candidate.Projection.Valid || !IsValidFootprint(candidate.Footprint)) { throw new ArgumentException("Candidate projection/footprint must be valid.", "candidate"); } if (!IsFinite(screenWidth) || !IsFinite(screenHeight) || screenWidth <= 0f || screenHeight <= 0f) { throw new ArgumentOutOfRangeException("screenWidth"); } IReadOnlyList dynamicHudZones2 = dynamicHudZones ?? Array.Empty(); MarkerHudRect[] occupied = Array.Empty(); if (candidate.Projection.Mode != MarkerHudMode.OffScreenEdge) { return ResolveOnScreenPlacement(candidate, occupied, dynamicHudZones2, screenWidth, screenHeight); } return ResolveEdgePlacement(candidate, occupied, dynamicHudZones2, screenWidth, screenHeight); } public static void ResolvePlacementsBuffered(IReadOnlyList candidates, float screenWidth, float screenHeight, IReadOnlyList? dynamicHudZones, List orderedBuffer, List occupiedBuffer, List resultBuffer) { if (candidates == null) { throw new ArgumentNullException("candidates"); } if (orderedBuffer == null) { throw new ArgumentNullException("orderedBuffer"); } if (occupiedBuffer == null) { throw new ArgumentNullException("occupiedBuffer"); } if (resultBuffer == null) { throw new ArgumentNullException("resultBuffer"); } orderedBuffer.Clear(); occupiedBuffer.Clear(); resultBuffer.Clear(); if (!IsFinite(screenWidth) || !IsFinite(screenHeight) || screenWidth <= 0f || screenHeight <= 0f) { return; } for (int i = 0; i < candidates.Count; i++) { MarkerHudPlacementCandidate item = candidates[i]; if (item.Projection.Valid && IsValidFootprint(item.Footprint)) { orderedBuffer.Add(item); } } orderedBuffer.Sort(MarkerPlacementCandidateComparer.Instance); IReadOnlyList dynamicHudZones2 = dynamicHudZones ?? Array.Empty(); for (int j = 0; j < orderedBuffer.Count; j++) { MarkerHudPlacementCandidate candidate = orderedBuffer[j]; MarkerHudPlacement item2 = ((candidate.Projection.Mode == MarkerHudMode.OffScreenEdge) ? ResolveEdgePlacement(candidate, occupiedBuffer, dynamicHudZones2, screenWidth, screenHeight) : ResolveOnScreenPlacement(candidate, occupiedBuffer, dynamicHudZones2, screenWidth, screenHeight)); occupiedBuffer.Add(item2.FinalRect); resultBuffer.Add(item2); } } public static void ResolvePlacementsBufferedStable(IReadOnlyList candidates, float screenWidth, float screenHeight, IReadOnlyList? dynamicHudZones, IReadOnlyDictionary? previousRanks, List orderedBuffer, List occupiedBuffer, List resultBuffer) { if (candidates == null) { throw new ArgumentNullException("candidates"); } if (orderedBuffer == null) { throw new ArgumentNullException("orderedBuffer"); } if (occupiedBuffer == null) { throw new ArgumentNullException("occupiedBuffer"); } if (resultBuffer == null) { throw new ArgumentNullException("resultBuffer"); } occupiedBuffer.Clear(); resultBuffer.Clear(); if (!IsFinite(screenWidth) || !IsFinite(screenHeight) || screenWidth <= 0f || screenHeight <= 0f) { orderedBuffer.Clear(); return; } MarkerPlacementStabilityPolicy.BuildStableOrderBuffered(candidates, previousRanks, screenWidth, screenHeight, orderedBuffer); IReadOnlyList dynamicHudZones2 = dynamicHudZones ?? Array.Empty(); for (int i = 0; i < orderedBuffer.Count; i++) { MarkerHudPlacementCandidate candidate = orderedBuffer[i]; if (candidate.Projection.Valid && IsValidFootprint(candidate.Footprint)) { MarkerHudPlacement item = ((candidate.Projection.Mode == MarkerHudMode.OffScreenEdge) ? ResolveEdgePlacement(candidate, occupiedBuffer, dynamicHudZones2, screenWidth, screenHeight) : ResolveOnScreenPlacement(candidate, occupiedBuffer, dynamicHudZones2, screenWidth, screenHeight)); occupiedBuffer.Add(item.FinalRect); resultBuffer.Add(item); } } } public static float GetMaxOnScreenAnchorDisplacement(MarkerHudVisualFootprint footprint, float screenWidth, float screenHeight) { float num = ResolutionScale(screenWidth, screenHeight); return Clamp(Math.Max(footprint.Height * 2.6f, footprint.Width * 0.62f), 110f * num, 260f * num); } public static float OnScreenAnchorDisplacement(MarkerHudProjection projection, MarkerHudPlacement placement) { float num = placement.X - projection.X; float num2 = placement.Y - projection.Y; return (float)Math.Sqrt(num * num + num2 * num2); } public static IReadOnlyList Deconflict(IEnumerable candidates, float screenWidth, float screenHeight) { if (candidates == null) { throw new ArgumentNullException("candidates"); } float num = ResolutionScale(screenWidth, screenHeight); MarkerHudVisualFootprint compatibilityFootprint = new MarkerHudVisualFootprint(290f * num, 60f * num, 30f * num, 230f * num, 10f * num, 8f * num); return (from x in ResolvePlacements(candidates.Select((MarkerHudEdgeCandidate c) => new MarkerHudPlacementCandidate(c.StableKey, c.Projection, compatibilityFootprint)), screenWidth, screenHeight) where x.Mode == MarkerHudMode.OffScreenEdge select new MarkerHudEdgePlacement(x.StableKey, x.Edge, x.X, x.Y, x.ArrowRotationDegrees, x.LaneSlot)).ToArray(); } public static string StyleTokenForMode(MarkerHudMode mode) { return "ISF_ROR2_HUD_NATIVE_V5_C16"; } private static MarkerHudPlacement ResolveEdgePlacement(MarkerHudPlacementCandidate candidate, IReadOnlyList occupied, IReadOnlyList dynamicHudZones, float screenWidth, float screenHeight) { MarkerHudProjection projection = candidate.Projection; MarkerHudRect rect = new MarkerHudRect(projection.X, projection.Y, candidate.Footprint.Width, candidate.Footprint.Height); bool flag = IntersectsReservedHud(rect, screenWidth, screenHeight); bool flag2 = IntersectsDynamicHud(rect, dynamicHudZones, screenWidth, screenHeight); bool flag3 = flag || flag2; bool flag4 = IntersectsOccupied(rect, occupied, screenWidth, screenHeight); float num = ResolutionScale(screenWidth, screenHeight); MarkerHudSafeArea safeArea = GetSafeArea(screenWidth, screenHeight); float num2 = 10f * num; float num3 = Math.Max(54f * num, ((projection.Edge == MarkerHudEdge.Left || projection.Edge == MarkerHudEdge.Right) ? candidate.Footprint.Height : candidate.Footprint.Width) + num2); float num4 = ((projection.Edge == MarkerHudEdge.Left || projection.Edge == MarkerHudEdge.Right) ? candidate.Footprint.Width : candidate.Footprint.Height); float laneSpacing = Math.Max(46f * num, num4 * 0.72f + num2); float num5 = Axis(projection.Edge, projection.X, projection.Y); for (int i = 0; i < 18; i++) { for (int j = 0; j <= 80; j++) { int num6 = TrialOffset(j); float axis = num5 + (float)num6 * num3; if (TryBuildVisualRailCenter(projection.Edge, axis, i, laneSpacing, safeArea, candidate.Footprint, screenWidth, screenHeight, out var x, out var y)) { MarkerHudRect markerHudRect = new MarkerHudRect(x, y, candidate.Footprint.Width, candidate.Footprint.Height); if (IsPlacementFree(markerHudRect, occupied, dynamicHudZones, screenWidth, screenHeight)) { bool flag5 = !NearlyEqual(x, projection.X) || !NearlyEqual(y, projection.Y); return new MarkerHudPlacement(candidate.StableKey, MarkerHudMode.OffScreenEdge, projection.Edge, x, y, projection.ArrowRotationDegrees, i, num6, markerHudRect, flag3 && flag5, flag4 && flag5, flag2 && flag5); } } } } if (TryFindAnyFreePlacement(candidate.Footprint, projection.X, projection.Y, occupied, dynamicHudZones, screenWidth, screenHeight, out var resolvedX, out var resolvedY)) { MarkerHudRect finalRect = new MarkerHudRect(resolvedX, resolvedY, candidate.Footprint.Width, candidate.Footprint.Height); return new MarkerHudPlacement(candidate.StableKey, MarkerHudMode.OffScreenEdge, projection.Edge, resolvedX, resolvedY, projection.ArrowRotationDegrees, 18, 0, finalRect, flag3, flag4 || !NearlyEqual(resolvedX, projection.X) || !NearlyEqual(resolvedY, projection.Y), flag2); } float num7 = Clamp(projection.X, candidate.Footprint.Width * 0.5f, screenWidth - candidate.Footprint.Width * 0.5f); float num8 = Clamp(projection.Y, candidate.Footprint.Height * 0.5f, screenHeight - candidate.Footprint.Height * 0.5f); MarkerHudRect finalRect2 = new MarkerHudRect(num7, num8, candidate.Footprint.Width, candidate.Footprint.Height); return new MarkerHudPlacement(candidate.StableKey, MarkerHudMode.OffScreenEdge, projection.Edge, num7, num8, projection.ArrowRotationDegrees, 19, 0, finalRect2, flag3, flag4, flag2); } private static MarkerHudPlacement ResolveOnScreenPlacement(MarkerHudPlacementCandidate candidate, IReadOnlyList occupied, IReadOnlyList dynamicHudZones, float screenWidth, float screenHeight) { MarkerHudProjection projection = candidate.Projection; MarkerHudRect markerHudRect = new MarkerHudRect(projection.X, projection.Y, candidate.Footprint.Width, candidate.Footprint.Height); bool num = IntersectsReservedHud(markerHudRect, screenWidth, screenHeight); bool flag = IntersectsDynamicHud(markerHudRect, dynamicHudZones, screenWidth, screenHeight); bool flag2 = num || flag; bool collisionRelocated = IntersectsOccupied(markerHudRect, occupied, screenWidth, screenHeight); if (IsPlacementFree(markerHudRect, occupied, dynamicHudZones, screenWidth, screenHeight)) { return new MarkerHudPlacement(candidate.StableKey, MarkerHudMode.OnScreenWorldAnchor, MarkerHudEdge.None, projection.X, projection.Y, 0f, 0, 0, markerHudRect, hudRelocated: false, collisionRelocated: false); } float num2 = ResolutionScale(screenWidth, screenHeight); float num3 = Math.Max(42f * num2, Math.Min(112f * num2, candidate.Footprint.Width * 0.28f)); float num4 = Math.Max(40f * num2, candidate.Footprint.Height + 10f * num2); float maxOnScreenAnchorDisplacement = GetMaxOnScreenAnchorDisplacement(candidate.Footprint, screenWidth, screenHeight); float num5 = maxOnScreenAnchorDisplacement * maxOnScreenAnchorDisplacement; int num6 = Math.Max(1, (int)Math.Ceiling(maxOnScreenAnchorDisplacement / Math.Max(1f, Math.Min(num3, num4)))); for (int i = 1; i <= num6; i++) { float num7 = num3 * (float)i; float num8 = num4 * (float)i; for (int j = 0; j < 8; j++) { float num9; float num10; switch (j) { case 0: num9 = 0f; num10 = 0f - num8; break; case 1: num9 = num7; num10 = 0f; break; case 2: num9 = 0f - num7; num10 = 0f; break; case 3: num9 = 0f; num10 = num8; break; case 4: num9 = num7; num10 = 0f - num8; break; case 5: num9 = 0f - num7; num10 = 0f - num8; break; case 6: num9 = num7; num10 = num8; break; default: num9 = 0f - num7; num10 = num8; break; } if (!(num9 * num9 + num10 * num10 > num5 + 0.0001f)) { float num11 = projection.X + num9; float num12 = projection.Y + num10; MarkerHudRect markerHudRect2 = new MarkerHudRect(num11, num12, candidate.Footprint.Width, candidate.Footprint.Height); if (IsPlacementFree(markerHudRect2, occupied, dynamicHudZones, screenWidth, screenHeight)) { return new MarkerHudPlacement(candidate.StableKey, MarkerHudMode.OnScreenWorldAnchor, MarkerHudEdge.None, num11, num12, 0f, i, 0, markerHudRect2, flag2, collisionRelocated, flag); } } } } if (flag2 && TryFindNearestHardHudEscape(candidate.Footprint, projection.X, projection.Y, occupied, dynamicHudZones, screenWidth, screenHeight, out var resolvedX, out var resolvedY)) { MarkerHudRect finalRect = new MarkerHudRect(resolvedX, resolvedY, candidate.Footprint.Width, candidate.Footprint.Height); return new MarkerHudPlacement(candidate.StableKey, MarkerHudMode.OnScreenWorldAnchor, MarkerHudEdge.None, resolvedX, resolvedY, 0f, num6 + 1, 0, finalRect, hudRelocated: true, collisionRelocated, flag); } float num13 = Clamp(projection.X, candidate.Footprint.Width * 0.5f, screenWidth - candidate.Footprint.Width * 0.5f); float num14 = Clamp(projection.Y, candidate.Footprint.Height * 0.5f, screenHeight - candidate.Footprint.Height * 0.5f); float num15 = num13 - projection.X; float num16 = num14 - projection.Y; float num17 = (float)Math.Sqrt(num15 * num15 + num16 * num16); if (num17 > maxOnScreenAnchorDisplacement && num17 > 0.0001f) { float num18 = maxOnScreenAnchorDisplacement / num17; num13 = projection.X + num15 * num18; num14 = projection.Y + num16 * num18; } MarkerHudRect finalRect2 = new MarkerHudRect(num13, num14, candidate.Footprint.Width, candidate.Footprint.Height); return new MarkerHudPlacement(candidate.StableKey, MarkerHudMode.OnScreenWorldAnchor, MarkerHudEdge.None, num13, num14, 0f, num6 + 1, 0, finalRect2, flag2, collisionRelocated, flag); } private static bool TryFindNearestHardHudEscape(MarkerHudVisualFootprint footprint, float desiredX, float desiredY, IReadOnlyList occupied, IReadOnlyList dynamicHudZones, float screenWidth, float screenHeight, out float resolvedX, out float resolvedY) { resolvedX = 0f; resolvedY = 0f; if (!IsValidFootprint(footprint) || screenWidth <= 0f || screenHeight <= 0f) { return false; } float num = ResolutionScale(screenWidth, screenHeight); float num2 = 12f * num; float padding = 4f * num; float num3 = 10f * num; float num4 = footprint.Width * 0.5f; float num5 = footprint.Height * 0.5f; float num6 = num4 + num2; float num7 = screenWidth - num4 - num2; float num8 = num5 + num2; float num9 = screenHeight - num5 - num2; if (num6 > num7 || num8 > num9) { return false; } float x = Clamp(desiredX, num6, num7); float y = Clamp(desiredY, num8, num9); bool found = false; float bestDistanceSquared = float.PositiveInfinity; float bestX = 0f; float bestY = 0f; ConsiderHardHudEscapeCandidate(x, y, desiredX, desiredY, footprint, occupied, dynamicHudZones, screenWidth, screenHeight, num6, num7, num8, num9, ref found, ref bestDistanceSquared, ref bestX, ref bestY); ConsiderHardHudEscapeCandidate(num6, y, desiredX, desiredY, footprint, occupied, dynamicHudZones, screenWidth, screenHeight, num6, num7, num8, num9, ref found, ref bestDistanceSquared, ref bestX, ref bestY); ConsiderHardHudEscapeCandidate(num7, y, desiredX, desiredY, footprint, occupied, dynamicHudZones, screenWidth, screenHeight, num6, num7, num8, num9, ref found, ref bestDistanceSquared, ref bestX, ref bestY); ConsiderHardHudEscapeCandidate(x, num8, desiredX, desiredY, footprint, occupied, dynamicHudZones, screenWidth, screenHeight, num6, num7, num8, num9, ref found, ref bestDistanceSquared, ref bestX, ref bestY); ConsiderHardHudEscapeCandidate(x, num9, desiredX, desiredY, footprint, occupied, dynamicHudZones, screenWidth, screenHeight, num6, num7, num8, num9, ref found, ref bestDistanceSquared, ref bestX, ref bestY); foreach (MarkerHudExclusionZone reservedHudZone in GetReservedHudZones(screenWidth, screenHeight)) { ConsiderHardHudEscapeZone(reservedHudZone, padding, desiredX, desiredY, footprint, occupied, dynamicHudZones, screenWidth, screenHeight, num6, num7, num8, num9, ref found, ref bestDistanceSquared, ref bestX, ref bestY); } for (int i = 0; i < dynamicHudZones.Count; i++) { ConsiderHardHudEscapeZone(dynamicHudZones[i], padding, desiredX, desiredY, footprint, occupied, dynamicHudZones, screenWidth, screenHeight, num6, num7, num8, num9, ref found, ref bestDistanceSquared, ref bestX, ref bestY); } for (int j = 0; j < occupied.Count; j++) { MarkerHudRect markerHudRect = occupied[j]; ConsiderHardHudEscapeZone(new MarkerHudExclusionZone("occupied", markerHudRect.Left - num3 * 0.5f, markerHudRect.Right + num3 * 0.5f, markerHudRect.Bottom - num3 * 0.5f, markerHudRect.Top + num3 * 0.5f), num3 * 0.5f, desiredX, desiredY, footprint, occupied, dynamicHudZones, screenWidth, screenHeight, num6, num7, num8, num9, ref found, ref bestDistanceSquared, ref bestX, ref bestY); } float num10 = Math.Max(6f, 8f * num); float num11 = Math.Max(Math.Max(Math.Abs(desiredX - num6), Math.Abs(num7 - desiredX)), Math.Max(Math.Abs(desiredY - num8), Math.Abs(num9 - desiredY))); int num12 = Math.Max(1, (int)Math.Ceiling(num11 / num10)); for (int k = 1; k <= num12; k++) { float num13 = (float)k * num10; if (found && num13 * num13 > bestDistanceSquared + 0.0001f) { break; } for (int l = -k; l <= k; l++) { float x2 = desiredX + (float)l * num10; ConsiderHardHudEscapeCandidate(x2, desiredY - num13, desiredX, desiredY, footprint, occupied, dynamicHudZones, screenWidth, screenHeight, num6, num7, num8, num9, ref found, ref bestDistanceSquared, ref bestX, ref bestY); ConsiderHardHudEscapeCandidate(x2, desiredY + num13, desiredX, desiredY, footprint, occupied, dynamicHudZones, screenWidth, screenHeight, num6, num7, num8, num9, ref found, ref bestDistanceSquared, ref bestX, ref bestY); } for (int m = -k + 1; m <= k - 1; m++) { float y2 = desiredY + (float)m * num10; ConsiderHardHudEscapeCandidate(desiredX - num13, y2, desiredX, desiredY, footprint, occupied, dynamicHudZones, screenWidth, screenHeight, num6, num7, num8, num9, ref found, ref bestDistanceSquared, ref bestX, ref bestY); ConsiderHardHudEscapeCandidate(desiredX + num13, y2, desiredX, desiredY, footprint, occupied, dynamicHudZones, screenWidth, screenHeight, num6, num7, num8, num9, ref found, ref bestDistanceSquared, ref bestX, ref bestY); } } if (!found) { return false; } resolvedX = bestX; resolvedY = bestY; return true; } private static void ConsiderHardHudEscapeZone(MarkerHudExclusionZone zone, float padding, float desiredX, float desiredY, MarkerHudVisualFootprint footprint, IReadOnlyList occupied, IReadOnlyList dynamicHudZones, float screenWidth, float screenHeight, float minX, float maxX, float minY, float maxY, ref bool found, ref float bestDistanceSquared, ref float bestX, ref float bestY) { float num = footprint.Width * 0.5f; float num2 = footprint.Height * 0.5f; float x = zone.Left - num - padding; float x2 = zone.Right + num + padding; float y = zone.Bottom - num2 - padding; float y2 = zone.Top + num2 + padding; float x3 = Clamp(desiredX, minX, maxX); float y3 = Clamp(desiredY, minY, maxY); ConsiderHardHudEscapeCandidate(x, y3, desiredX, desiredY, footprint, occupied, dynamicHudZones, screenWidth, screenHeight, minX, maxX, minY, maxY, ref found, ref bestDistanceSquared, ref bestX, ref bestY); ConsiderHardHudEscapeCandidate(x2, y3, desiredX, desiredY, footprint, occupied, dynamicHudZones, screenWidth, screenHeight, minX, maxX, minY, maxY, ref found, ref bestDistanceSquared, ref bestX, ref bestY); ConsiderHardHudEscapeCandidate(x3, y, desiredX, desiredY, footprint, occupied, dynamicHudZones, screenWidth, screenHeight, minX, maxX, minY, maxY, ref found, ref bestDistanceSquared, ref bestX, ref bestY); ConsiderHardHudEscapeCandidate(x3, y2, desiredX, desiredY, footprint, occupied, dynamicHudZones, screenWidth, screenHeight, minX, maxX, minY, maxY, ref found, ref bestDistanceSquared, ref bestX, ref bestY); ConsiderHardHudEscapeCandidate(x, y, desiredX, desiredY, footprint, occupied, dynamicHudZones, screenWidth, screenHeight, minX, maxX, minY, maxY, ref found, ref bestDistanceSquared, ref bestX, ref bestY); ConsiderHardHudEscapeCandidate(x, y2, desiredX, desiredY, footprint, occupied, dynamicHudZones, screenWidth, screenHeight, minX, maxX, minY, maxY, ref found, ref bestDistanceSquared, ref bestX, ref bestY); ConsiderHardHudEscapeCandidate(x2, y, desiredX, desiredY, footprint, occupied, dynamicHudZones, screenWidth, screenHeight, minX, maxX, minY, maxY, ref found, ref bestDistanceSquared, ref bestX, ref bestY); ConsiderHardHudEscapeCandidate(x2, y2, desiredX, desiredY, footprint, occupied, dynamicHudZones, screenWidth, screenHeight, minX, maxX, minY, maxY, ref found, ref bestDistanceSquared, ref bestX, ref bestY); } private static void ConsiderHardHudEscapeCandidate(float x, float y, float desiredX, float desiredY, MarkerHudVisualFootprint footprint, IReadOnlyList occupied, IReadOnlyList dynamicHudZones, float screenWidth, float screenHeight, float minX, float maxX, float minY, float maxY, ref bool found, ref float bestDistanceSquared, ref float bestX, ref float bestY) { if (IsFinite(x) && IsFinite(y) && !(x < minX) && !(x > maxX) && !(y < minY) && !(y > maxY) && IsPlacementFree(new MarkerHudRect(x, y, footprint.Width, footprint.Height), occupied, dynamicHudZones, screenWidth, screenHeight)) { float num = x - desiredX; float num2 = y - desiredY; float num3 = num * num + num2 * num2; if (!(num3 > bestDistanceSquared + 0.0001f) && (!(Math.Abs(num3 - bestDistanceSquared) <= 0.0001f) || (!(y > bestY + 0.0001f) && (!(Math.Abs(y - bestY) <= 0.0001f) || !(x >= bestX))))) { found = true; bestDistanceSquared = num3; bestX = x; bestY = y; } } } private static bool TryFindAnyFreePlacement(MarkerHudVisualFootprint footprint, float desiredX, float desiredY, IReadOnlyList occupied, IReadOnlyList dynamicHudZones, float screenWidth, float screenHeight, out float resolvedX, out float resolvedY) { resolvedX = 0f; resolvedY = 0f; float num = ResolutionScale(screenWidth, screenHeight); float num2 = 12f * num; float num3 = footprint.Width * 0.5f + num2; float num4 = screenWidth - footprint.Width * 0.5f - num2; float num5 = footprint.Height * 0.5f + num2; float num6 = screenHeight - footprint.Height * 0.5f - num2; if (num3 > num4 || num5 > num6) { return false; } float num7 = Math.Max(footprint.Width * 0.55f, 80f * num); float num8 = Math.Max(footprint.Height + 10f * num, 48f * num); bool flag = false; float num9 = float.PositiveInfinity; float num10 = 0f; float num11 = 0f; for (float num12 = num5; num12 <= num6 + 0.01f; num12 += num8) { for (float num13 = num3; num13 <= num4 + 0.01f; num13 += num7) { if (IsPlacementFree(new MarkerHudRect(num13, num12, footprint.Width, footprint.Height), occupied, dynamicHudZones, screenWidth, screenHeight)) { float num14 = num13 - desiredX; float num15 = num12 - desiredY; float num16 = num14 * num14 + num15 * num15; if (!(num16 > num9 + 0.0001f) && (!(Math.Abs(num16 - num9) <= 0.0001f) || (!(num12 > num11 + 0.0001f) && (!(Math.Abs(num12 - num11) <= 0.0001f) || !(num13 >= num10))))) { flag = true; num9 = num16; num10 = num13; num11 = num12; } } } } if (!flag) { return false; } resolvedX = num10; resolvedY = num11; return true; } private static bool IsPlacementFree(MarkerHudRect rect, IReadOnlyList occupied, IReadOnlyList dynamicHudZones, float screenWidth, float screenHeight) { if (IsRectInsideScreen(rect, screenWidth, screenHeight) && !IntersectsReservedHud(rect, screenWidth, screenHeight) && !IntersectsDynamicHud(rect, dynamicHudZones, screenWidth, screenHeight)) { return !IntersectsOccupied(rect, occupied, screenWidth, screenHeight); } return false; } private static bool IntersectsOccupied(MarkerHudRect rect, IReadOnlyList occupied, float screenWidth, float screenHeight) { float num = 10f * ResolutionScale(screenWidth, screenHeight); MarkerHudRect markerHudRect = rect.Inflated(num * 0.5f); for (int i = 0; i < occupied.Count; i++) { if (markerHudRect.Intersects(occupied[i].Inflated(num * 0.5f))) { return true; } } return false; } private static bool TryResolveDirection(float viewportX, float viewportY, float viewportDepth, float localX, float localY, float localZ, out float directionX, out float directionY) { directionX = 0f; directionY = 0f; if (viewportDepth > 0.0001f) { directionX = viewportX - 0.5f; directionY = viewportY - 0.5f; } else { float num = (float)Math.Sqrt(localX * localX + localZ * localZ); float num2 = (float)Math.Sqrt(localX * localX + localY * localY + localZ * localZ); if (num2 <= 0.0001f) { return false; } directionX = ((num > 0.0001f) ? (localX / num) : 0f); float num3 = localY / num2; float num4 = ((num > 0.0001f) ? Math.Max(0f, (0f - localZ) / num) : 1f); directionY = num3 - num4 * 0.82f; if (Math.Abs(directionX) + Math.Abs(directionY) < 0.08f) { directionY = -1f; } } float num5 = (float)Math.Sqrt(directionX * directionX + directionY * directionY); if (!IsFinite(num5) || num5 <= 0.0001f) { return false; } directionX /= num5; directionY /= num5; return true; } private static void ResolveReservedEdgePoint(MarkerHudEdge edge, float x, float y, float screenWidth, float screenHeight, out float resolvedX, out float resolvedY) { resolvedX = x; resolvedY = y; if (!IsReservedHudPoint(x, y, screenWidth, screenHeight)) { return; } MarkerHudSafeArea safeArea = GetSafeArea(screenWidth, screenHeight); float num = ResolutionScale(screenWidth, screenHeight); float num2 = Math.Max(12f, 18f * num); float num3 = Axis(edge, x, y); for (int i = 1; i <= 160; i++) { float num4 = (float)TrialOffset(i) * num2; float axis = num3 + num4; if (TryBuildRailPoint(edge, axis, 0, 0f, safeArea, out var x2, out var y2) && !IsReservedHudPoint(x2, y2, screenWidth, screenHeight)) { resolvedX = x2; resolvedY = y2; break; } } } private static bool TryBuildRailPoint(MarkerHudEdge edge, float axis, int lane, float laneSpacing, MarkerHudSafeArea safe, out float x, out float y) { x = 0f; y = 0f; switch (edge) { case MarkerHudEdge.Left: if (axis < safe.Bottom || axis > safe.Top) { return false; } x = safe.Left + (float)lane * laneSpacing; y = axis; return x <= safe.Right; case MarkerHudEdge.Right: if (axis < safe.Bottom || axis > safe.Top) { return false; } x = safe.Right - (float)lane * laneSpacing; y = axis; return x >= safe.Left; case MarkerHudEdge.Top: if (axis < safe.Left || axis > safe.Right) { return false; } x = axis; y = safe.Top - (float)lane * laneSpacing; return y >= safe.Bottom; case MarkerHudEdge.Bottom: if (axis < safe.Left || axis > safe.Right) { return false; } x = axis; y = safe.Bottom + (float)lane * laneSpacing; return y <= safe.Top; default: return false; } } private static bool TryBuildVisualRailCenter(MarkerHudEdge edge, float axis, int lane, float laneSpacing, MarkerHudSafeArea safe, MarkerHudVisualFootprint footprint, float screenWidth, float screenHeight, out float x, out float y) { x = 0f; y = 0f; float num = ResolutionScale(screenWidth, screenHeight); float num2 = 12f * num; float num3 = footprint.Width * 0.5f; float num4 = footprint.Height * 0.5f; float num5 = Math.Max(safe.Left, num3 + num2); float num6 = Math.Min(safe.Right, screenWidth - num3 - num2); float num7 = Math.Max(safe.Bottom, num4 + num2); float num8 = Math.Min(safe.Top, screenHeight - num4 - num2); if (num5 > num6 || num7 > num8) { return false; } switch (edge) { case MarkerHudEdge.Left: if (axis < num7 || axis > num8) { return false; } x = num5 + (float)lane * laneSpacing; y = axis; return x <= num6; case MarkerHudEdge.Right: if (axis < num7 || axis > num8) { return false; } x = num6 - (float)lane * laneSpacing; y = axis; return x >= num5; case MarkerHudEdge.Top: if (axis < num5 || axis > num6) { return false; } x = axis; y = num8 - (float)lane * laneSpacing; return y >= num7; case MarkerHudEdge.Bottom: if (axis < num5 || axis > num6) { return false; } x = axis; y = num7 + (float)lane * laneSpacing; return y <= num8; default: return false; } } private static bool RectIntersectsZone(MarkerHudRect rect, MarkerHudExclusionZone zone) { if (rect.Left < zone.Right && rect.Right > zone.Left && rect.Bottom < zone.Top) { return rect.Top > zone.Bottom; } return false; } private static bool IsValidFootprint(MarkerHudVisualFootprint footprint) { if (IsFinite(footprint.Width) && IsFinite(footprint.Height) && footprint.Width > 0f) { return footprint.Height > 0f; } return false; } private static bool IsFiniteRect(MarkerHudRect rect) { if (IsFinite(rect.CenterX) && IsFinite(rect.CenterY) && IsFinite(rect.Width)) { return IsFinite(rect.Height); } return false; } private static float Axis(MarkerHudEdge edge, float x, float y) { if (edge != MarkerHudEdge.Left && edge != MarkerHudEdge.Right) { return x; } return y; } private static int TrialOffset(int trial) { if (trial <= 0) { return 0; } int num = (trial + 1) / 2; if (trial % 2 != 1) { return -num; } return num; } private static float ResolutionScale(float screenWidth, float screenHeight) { if (!IsFinite(screenWidth) || !IsFinite(screenHeight) || screenWidth <= 0f || screenHeight <= 0f) { return 1f; } return Clamp(Math.Min(screenWidth / 1920f, screenHeight / 1080f), 0.6f, 2f); } private static MarkerHudProjection InvalidProjection() { return new MarkerHudProjection(valid: false, MarkerHudMode.OffScreenEdge, MarkerHudEdge.None, 0f, 0f, 0f, 0f, 0f); } private static bool NearlyEqual(float a, float b) { return Math.Abs(a - b) <= 0.01f; } private static bool IsFinite(float value) { if (!float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } private static float Clamp(float value, float min, float max) { if (!(value < min)) { if (!(value > max)) { return value; } return max; } return min; } } public enum MarkerLifetimeKind { Permanent, Temporary, Mixed, Unknown } public readonly struct MarkerItemLifetimeKey : IEquatable { public string ItemIdentity { get; } public MarkerLifetimeKind Lifetime { get; } public MarkerItemLifetimeKey(string itemIdentity, MarkerLifetimeKind lifetime) { ItemIdentity = itemIdentity ?? string.Empty; Lifetime = lifetime; } public bool Equals(MarkerItemLifetimeKey other) { if (Lifetime == other.Lifetime) { return string.Equals(ItemIdentity, other.ItemIdentity, StringComparison.Ordinal); } return false; } public override bool Equals(object? obj) { if (obj is MarkerItemLifetimeKey other) { return Equals(other); } return false; } public override int GetHashCode() { return (((ItemIdentity != null) ? StringComparer.Ordinal.GetHashCode(ItemIdentity) : 0) * 397) ^ (int)Lifetime; } } public readonly struct MarkerLifetimeIndicatorSpec { public bool Visible { get; } public int TemporaryCount { get; } public bool CountKnown { get; } public bool ShowCount { get; } public string CountText { get { if (!ShowCount) { return string.Empty; } return "×" + TemporaryCount.ToString(CultureInfo.InvariantCulture); } } public static MarkerLifetimeIndicatorSpec Hidden => new MarkerLifetimeIndicatorSpec(visible: false, 0, countKnown: false, showCount: false); public MarkerLifetimeIndicatorSpec(bool visible, int temporaryCount, bool countKnown, bool showCount) { Visible = visible; TemporaryCount = Math.Max(0, temporaryCount); CountKnown = countKnown; ShowCount = visible && countKnown && showCount && TemporaryCount > 0; } } public static class MarkerLifetimePolicy { public static MarkerLifetimeKind FromTemporaryFlag(bool isTemporary) { if (!isTemporary) { return MarkerLifetimeKind.Permanent; } return MarkerLifetimeKind.Temporary; } public static bool IsMarkerEligible(MarkerLifetimeKind lifetime, bool shareTemporaryItems) { if (!shareTemporaryItems) { return lifetime != MarkerLifetimeKind.Temporary; } return true; } public static MarkerLifetimeKind FromExactOptionKinds(bool sawTemporary, bool sawPermanent) { if (sawTemporary && sawPermanent) { return MarkerLifetimeKind.Mixed; } if (sawTemporary) { return MarkerLifetimeKind.Temporary; } if (sawPermanent) { return MarkerLifetimeKind.Permanent; } return MarkerLifetimeKind.Unknown; } public static MarkerLifetimeKind SummarizeCluster(int totalPhysicalMembers, int temporaryPhysicalMembers, int mixedLifetimeMembers, int unknownLifetimeMembers) { int num = Math.Max(0, totalPhysicalMembers); int num2 = Math.Max(0, temporaryPhysicalMembers); int num3 = Math.Max(0, mixedLifetimeMembers); int num4 = Math.Max(0, unknownLifetimeMembers); int num5 = Math.Max(0, num - num2 - num3 - num4); if (num3 > 0 || (num2 > 0 && num5 > 0)) { return MarkerLifetimeKind.Mixed; } if (num2 > 0) { return MarkerLifetimeKind.Temporary; } if (num4 > 0) { return MarkerLifetimeKind.Unknown; } return MarkerLifetimeKind.Permanent; } public static MarkerLifetimeIndicatorSpec BuildIndicator(MarkerLifetimeKind lifetime, int temporaryPhysicalMembers, int mixedLifetimeMembers, int unknownLifetimeMembers, bool allowCount, bool showSingleCountWhenMixed) { if (lifetime != MarkerLifetimeKind.Temporary && lifetime != MarkerLifetimeKind.Mixed) { return MarkerLifetimeIndicatorSpec.Hidden; } int num = Math.Max(0, temporaryPhysicalMembers); bool flag = Math.Max(0, mixedLifetimeMembers) == 0 && Math.Max(0, unknownLifetimeMembers) == 0; bool showCount = allowCount && flag && num > 0 && (num > 1 || (showSingleCountWhenMixed && lifetime == MarkerLifetimeKind.Mixed)); return new MarkerLifetimeIndicatorSpec(visible: true, num, flag, showCount); } public static string BuildDetailedItemDisplayName(string localizedName, MarkerLifetimeKind lifetime, bool russian) { if (!string.IsNullOrWhiteSpace(localizedName)) { return localizedName; } return "Pickup"; } public static string BuildSummaryLine(MarkerLifetimeKind lifetime, int temporaryPhysicalMembers, bool showTemporaryCount, bool russian) { return string.Empty; } public static int DetailedSortRank(MarkerLifetimeKind lifetime) { return lifetime switch { MarkerLifetimeKind.Temporary => 0, MarkerLifetimeKind.Permanent => 1, MarkerLifetimeKind.Mixed => 2, _ => 3, }; } } public static class MarkerPlacementStabilityPolicy { public const float RankHysteresisPixels1080 = 14f; public const float RelocationResponsePerSecond = 13f; public const float MaxInterpolationDeltaSeconds = 0.1f; public static void BuildStableOrderBuffered(IReadOnlyList candidates, IReadOnlyDictionary? previousRanks, float screenWidth, float screenHeight, List output) { if (candidates == null) { throw new ArgumentNullException("candidates"); } if (output == null) { throw new ArgumentNullException("output"); } output.Clear(); for (int i = 0; i < candidates.Count; i++) { MarkerHudPlacementCandidate item = candidates[i]; if (item.Projection.Valid) { output.Add(item); } } InsertionSortRaw(output); if (previousRanks == null || previousRanks.Count == 0 || output.Count < 2) { return; } for (int j = 1; j < output.Count; j++) { MarkerHudPlacementCandidate markerHudPlacementCandidate = output[j]; int num = j - 1; while (num >= 0 && ShouldMoveBeforeByMemory(markerHudPlacementCandidate, output[num], previousRanks)) { output[num + 1] = output[num]; num--; } output[num + 1] = markerHudPlacementCandidate; } float hysteresis = 14f * ResolutionScale(screenWidth, screenHeight); for (int k = 0; k < output.Count; k++) { bool flag = false; for (int l = 0; l + 1 < output.Count; l++) { MarkerHudPlacementCandidate markerHudPlacementCandidate2 = output[l]; MarkerHudPlacementCandidate markerHudPlacementCandidate3 = output[l + 1]; if (SameRail(markerHudPlacementCandidate2, markerHudPlacementCandidate3) && MeaningfullyCrossed(markerHudPlacementCandidate2, markerHudPlacementCandidate3, hysteresis)) { output[l] = markerHudPlacementCandidate3; output[l + 1] = markerHudPlacementCandidate2; flag = true; } } if (!flag) { break; } } } public static float SmoothCoordinate(float current, float target, float unscaledDeltaTime) { if (!IsFinite(current) || !IsFinite(target)) { return target; } if (!IsFinite(unscaledDeltaTime) || unscaledDeltaTime <= 0f) { return current; } float num = Math.Min(0.1f, unscaledDeltaTime); float num2 = 1f - (float)Math.Exp(-13f * num); return current + (target - current) * num2; } public static void ClampDisplacementFromAnchor(float anchorX, float anchorY, float candidateX, float candidateY, float maxDisplacement, out float clampedX, out float clampedY) { clampedX = candidateX; clampedY = candidateY; if (IsFinite(anchorX) && IsFinite(anchorY) && IsFinite(candidateX) && IsFinite(candidateY)) { float num = Math.Max(0f, maxDisplacement); float num2 = candidateX - anchorX; float num3 = candidateY - anchorY; float num4 = num2 * num2 + num3 * num3; if (!(num4 <= num * num) && !(num4 <= 0f)) { float num5 = num / (float)Math.Sqrt(num4); clampedX = anchorX + num2 * num5; clampedY = anchorY + num3 * num5; } } } public static bool IsMeaningfulAxisCrossing(float previousLeaderAxis, float previousFollowerAxis, float currentLeaderAxis, float currentFollowerAxis, float hysteresisPixels) { if (!(previousLeaderAxis <= previousFollowerAxis)) { return currentLeaderAxis + Math.Max(0f, hysteresisPixels) < currentFollowerAxis; } return currentLeaderAxis > currentFollowerAxis + Math.Max(0f, hysteresisPixels); } private static void InsertionSortRaw(List output) { for (int i = 1; i < output.Count; i++) { MarkerHudPlacementCandidate markerHudPlacementCandidate = output[i]; int num = i - 1; while (num >= 0 && CompareRaw(markerHudPlacementCandidate, output[num]) < 0) { output[num + 1] = output[num]; num--; } output[num + 1] = markerHudPlacementCandidate; } } private static bool ShouldMoveBeforeByMemory(MarkerHudPlacementCandidate candidate, MarkerHudPlacementCandidate existing, IReadOnlyDictionary previousRanks) { int num = CompareRail(candidate, existing); if (num != 0) { return num < 0; } int value; bool flag = previousRanks.TryGetValue(candidate.StableKey, out value); int value2; bool flag2 = previousRanks.TryGetValue(existing.StableKey, out value2); if (flag && flag2) { return value < value2; } if (flag != flag2) { return flag; } return CompareRaw(candidate, existing) < 0; } private static bool MeaningfullyCrossed(MarkerHudPlacementCandidate left, MarkerHudPlacementCandidate right, float hysteresis) { float num = SortAxis(left); float num2 = SortAxis(right); return num > num2 + hysteresis; } private static float SortAxis(MarkerHudPlacementCandidate candidate) { if (candidate.Projection.Mode != MarkerHudMode.OffScreenEdge) { return candidate.Projection.Y; } if (candidate.Projection.Edge != MarkerHudEdge.Left && candidate.Projection.Edge != MarkerHudEdge.Right) { return candidate.Projection.X; } return candidate.Projection.Y; } private static int CompareRaw(MarkerHudPlacementCandidate left, MarkerHudPlacementCandidate right) { int num = CompareRail(left, right); if (num != 0) { return num; } int num2 = SortAxis(left).CompareTo(SortAxis(right)); if (num2 != 0) { return num2; } if (left.Projection.Mode == MarkerHudMode.OnScreenWorldAnchor) { int num3 = left.Projection.X.CompareTo(right.Projection.X); if (num3 != 0) { return num3; } } return left.StableKey.CompareTo(right.StableKey); } private static int CompareRail(MarkerHudPlacementCandidate left, MarkerHudPlacementCandidate right) { bool flag = left.Projection.Mode == MarkerHudMode.OffScreenEdge; bool flag2 = right.Projection.Mode == MarkerHudMode.OffScreenEdge; if (flag != flag2) { if (!flag) { return 1; } return -1; } if (!flag) { return 0; } return ((int)left.Projection.Edge).CompareTo((int)right.Projection.Edge); } private static bool SameRail(MarkerHudPlacementCandidate left, MarkerHudPlacementCandidate right) { return CompareRail(left, right) == 0; } private static float ResolutionScale(float screenWidth, float screenHeight) { if (!IsFinite(screenWidth) || !IsFinite(screenHeight) || screenWidth <= 0f || screenHeight <= 0f) { return 1f; } return Math.Max(0.55f, Math.Min(screenWidth / 1920f, screenHeight / 1080f)); } private static bool IsFinite(float value) { if (!float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } } public enum PersonalMarkerKind { OrdinaryPickup, CommandPicker } public readonly struct PersonalMarkerIdentity : IEquatable { public PersonalMarkerKind Kind { get; } public int InstanceId { get; } public PersonalMarkerIdentity(PersonalMarkerKind kind, int instanceId) { Kind = kind; InstanceId = instanceId; } public bool Equals(PersonalMarkerIdentity other) { if (Kind == other.Kind) { return InstanceId == other.InstanceId; } return false; } public override bool Equals(object? obj) { if (obj is PersonalMarkerIdentity other) { return Equals(other); } return false; } public override int GetHashCode() { return ((int)Kind * 397) ^ InstanceId; } public override string ToString() { return Kind.ToString() + ":" + InstanceId.ToString(CultureInfo.InvariantCulture); } } public sealed class PersonalMarkerDescriptor { public PersonalMarkerIdentity Identity { get; } public string Label { get; } public PersonalMarkerDescriptor(PersonalMarkerIdentity identity, string label) { Identity = identity; Label = label ?? throw new ArgumentNullException("label"); } } public enum PersonalMarkerTransition { None, Added, Updated, CapacityRejected } public sealed class PersonalMarkerRegistry { private readonly int _capacity; private readonly Dictionary _active = new Dictionary(); private readonly HashSet _seen = new HashSet(); public int Count => _active.Count; public int Capacity => _capacity; public IReadOnlyCollection Active => _active.Values; public PersonalMarkerRegistry(int capacity) { if (capacity <= 0) { throw new ArgumentOutOfRangeException("capacity"); } _capacity = capacity; } public void BeginSweep() { _seen.Clear(); } public PersonalMarkerTransition MarkPending(PersonalMarkerKind kind, int instanceId, string label) { PersonalMarkerIdentity personalMarkerIdentity = new PersonalMarkerIdentity(kind, instanceId); string text = MarkerPresentationPolicy.NormalizeLabel(label, (kind == PersonalMarkerKind.CommandPicker) ? "Command choice" : "Shared pickup"); if (_active.TryGetValue(personalMarkerIdentity, out PersonalMarkerDescriptor value)) { _seen.Add(personalMarkerIdentity); if (string.Equals(value.Label, text, StringComparison.Ordinal)) { return PersonalMarkerTransition.None; } _active[personalMarkerIdentity] = new PersonalMarkerDescriptor(personalMarkerIdentity, text); return PersonalMarkerTransition.Updated; } if (_active.Count >= _capacity) { return PersonalMarkerTransition.CapacityRejected; } _active.Add(personalMarkerIdentity, new PersonalMarkerDescriptor(personalMarkerIdentity, text)); _seen.Add(personalMarkerIdentity); return PersonalMarkerTransition.Added; } public IReadOnlyList EndSweep() { PersonalMarkerDescriptor[] array = (from x in _active where !_seen.Contains(x.Key) select x.Value).ToArray(); PersonalMarkerDescriptor[] array2 = array; foreach (PersonalMarkerDescriptor personalMarkerDescriptor in array2) { _active.Remove(personalMarkerDescriptor.Identity); } return array; } public bool Contains(PersonalMarkerKind kind, int instanceId) { return _active.ContainsKey(new PersonalMarkerIdentity(kind, instanceId)); } public bool Remove(PersonalMarkerKind kind, int instanceId) { PersonalMarkerIdentity personalMarkerIdentity = new PersonalMarkerIdentity(kind, instanceId); _seen.Remove(personalMarkerIdentity); return _active.Remove(personalMarkerIdentity); } public void Clear() { _active.Clear(); _seen.Clear(); } } public readonly struct MarkerVisualLayout { public string Text { get; } public float Width { get; } public float Height { get; } public int FontSize { get; } public MarkerVisualLayout(string text, float width, float height, int fontSize) { Text = text; Width = width; Height = height; FontSize = fontSize; } } public readonly struct MarkerReadabilityStyle { public string StyleToken { get; } public string SizingToken { get; } public int FontSize { get; } public float PlateAlpha { get; } public float PlatePaddingX { get; } public float PlatePaddingY { get; } public float HaloOffset { get; } public float ShadowOffset { get; } public bool PreservesNativeClassHue { get; } public MarkerReadabilityStyle(string styleToken, string sizingToken, int fontSize, float plateAlpha, float platePaddingX, float platePaddingY, float haloOffset, float shadowOffset, bool preservesNativeClassHue) { StyleToken = styleToken ?? throw new ArgumentNullException("styleToken"); SizingToken = sizingToken ?? throw new ArgumentNullException("sizingToken"); FontSize = fontSize; PlateAlpha = plateAlpha; PlatePaddingX = platePaddingX; PlatePaddingY = platePaddingY; HaloOffset = haloOffset; ShadowOffset = shadowOffset; PreservesNativeClassHue = preservesNativeClassHue; } } public static class MarkerPresentationPolicy { public const int MaxLabelCharacters = 42; public const int MaxLogicalMarkers = 96; public const int MaxOrdinaryMarkers = 64; public const int MaxCommandMarkers = 32; public const float MinMarkerWidth = 112f; public const float AbsoluteMaxMarkerWidth = 420f; public const float MinMarkerHeight = 30f; public const float MaxMarkerHeight = 42f; public const string UnifiedReadabilityStyleToken = "ISF_ROR2_PING_NATIVE_V2"; public const string PlayerPingClassSizingToken = "PLAYER_PING_CLASS_V1"; public const int ReadableMinFontSize = 18; public const int ReadableMaxFontSize = 24; public const int ReadableScreenHeightDivisor = 54; public const float UnifiedPlateAlpha = 0.18f; public const float UnifiedPlatePaddingX = 0f; public const float UnifiedPlatePaddingY = 0f; public const float UnifiedHaloOffset = 1f; public const float UnifiedShadowOffset = 2f; public const float PracticalVoidFloorY = -1000f; public const float MaximumWorldCoordinateMagnitude = 8192f; public const float MaximumPresentationDistanceMeters = 4096f; public static bool UsesSharedSkinBoxMutation => false; public static bool UsesVanillaPingSlot => false; public static bool RegistersPickupShareProvider => false; public static bool UsesFullRectangularPlate => false; public static bool UsesMarkerOnGuiRenderer => false; public static bool UsesTextMeshProHudCanvas => true; public static bool ReusesLiveRoR2HudTypographyWhenAvailable => true; public static bool EssentialIndicatorUsesTmpUnicodeGlyph => false; public static bool UsesDeterministicUiGraphicIndicator => true; public static bool ValidDistanceLabelsUseEllipsis => false; public static bool UsesTmpPreferredSizeForProductionFootprint => true; public static bool UsesInvalidationDrivenMarkerFramePipeline => true; public static bool UsesSingleMarkerPlacementFastPath => true; public static bool UsesBufferedProductionPlacementSolver => true; public static bool UsesGuardedUnityUiWrites => true; public static bool UsesStablePlacementHysteresis => true; public static bool UsesBoundedOnScreenAnchorDisplacement => true; public static bool UsesDeterministicDensityAggregation => true; public static bool PreservesLogicalIdentitiesDuringDeclutter => true; public static bool UsesPersistentWorldSpaceSemanticClusters => true; public static bool SemanticMembershipUsesCameraProjection => false; public static bool UsesAdaptiveClusterLod => true; public static bool UsesProjectionRelativePlacementCache => true; public static bool DenseSemanticClusterUsesSinglePresentationOwner => true; public static string IndicatorAssetSourceToken => "LOCAL_UI_GRAPHIC_GEOMETRY_V1"; public static string IndicatorVisualFamilyToken => "ISF_UI_GRAPHIC_INDICATOR_V1"; public static string NativeHudStyleToken => "ISF_ROR2_HUD_NATIVE_V5_C16"; public static IReadOnlyList MutatedGuiGlobals { get; } = new string[7] { "matrix", "color", "contentColor", "backgroundColor", "enabled", "depth", "changed" }; public static IReadOnlyList RestoredGuiGlobals { get; } = new string[7] { "matrix", "color", "contentColor", "backgroundColor", "enabled", "depth", "changed" }; public static string NormalizeLabel(string? label, string fallback) { string text = (string.IsNullOrWhiteSpace(label) ? fallback : label.Trim()); if (string.IsNullOrWhiteSpace(text)) { text = "Pickup"; } text = text.Replace('\r', ' ').Replace('\n', ' '); while (text.IndexOf(" ", StringComparison.Ordinal) >= 0) { text = text.Replace(" ", " "); } if (text.Length > 42) { text = text.Substring(0, 41) + "…"; } return text; } public static MarkerVisualLayout BuildVisualLayout(string? label, int roundedDistanceMeters, int screenWidth, int screenHeight) { string text = NormalizeLabel(label, "Shared pickup"); if (roundedDistanceMeters < 0) { roundedDistanceMeters = 0; } string text2 = "◆ " + text + " · " + roundedDistanceMeters.ToString(CultureInfo.InvariantCulture) + "m"; int fontSize = BuildReadabilityStyle(MarkerClassKind.Unknown, screenHeight).FontSize; float max = Math.Max(112f, Math.Min(420f, Math.Max(150f, (float)screenWidth * 0.42f))); float width = ClampFloat(22f + (float)(text2.Length * fontSize) * 0.6f, 112f, max); float height = ClampFloat((float)fontSize + 12f, 30f, 42f); return new MarkerVisualLayout(text2, width, height, fontSize); } public static int BuildNativeHudFontSize(int screenHeight) { return ClampInt(screenHeight / 48, 19, 27); } public static int BuildScaledNativeHudFontSize(int screenHeight, float markerScale) { return ClampInt((int)Math.Round((float)BuildNativeHudFontSize(screenHeight) * MarkerClusterPresentationPolicy.ClampMarkerScale(markerScale)), 14, 34); } public static string BuildNativeHudLabelText(string? label, int roundedDistanceMeters) { string text = NormalizeLabel(label, "Shared pickup"); if (roundedDistanceMeters < 0) { roundedDistanceMeters = 0; } return text + " · " + roundedDistanceMeters.ToString(CultureInfo.InvariantCulture) + "m"; } public static string BuildNativeHudClusterLabelText(string? label, int roundedDistanceMeters, int hiddenMemberCount) { string text = NormalizeLabel(label, "Shared pickup"); if (roundedDistanceMeters < 0) { roundedDistanceMeters = 0; } if (hiddenMemberCount <= 0) { return BuildNativeHudLabelText(text, roundedDistanceMeters); } return text + " +" + hiddenMemberCount.ToString(CultureInfo.InvariantCulture) + " · " + roundedDistanceMeters.ToString(CultureInfo.InvariantCulture) + "m"; } public static bool ShouldRenderHudMarkers(bool featureEnabled, bool localBlockingModalActive) { if (featureEnabled) { return !localBlockingModalActive; } return false; } public static MarkerReadabilityStyle BuildReadabilityStyle(MarkerClassKind markerClass, int screenHeight) { int fontSize = ClampInt(screenHeight / 54, 18, 24); return new MarkerReadabilityStyle("ISF_ROR2_PING_NATIVE_V2", "PLAYER_PING_CLASS_V1", fontSize, 0.18f, 0f, 0f, 1f, 2f, preservesNativeClassHue: true); } public static bool AreCoordinatesFinite(float x, float y, float z) { if (IsFinite(x) && IsFinite(y)) { return IsFinite(z); } return false; } public static string ValidateWorldPosition(float x, float y, float z) { if (!AreCoordinatesFinite(x, y, z)) { return "position-non-finite"; } if (y < -1000f) { return "below-void-floor"; } if (Math.Abs(x) > 8192f || Math.Abs(y) > 8192f || Math.Abs(z) > 8192f) { return "outside-world-bounds"; } return string.Empty; } public static string ValidatePresentationDistance(float distanceMeters) { if (!IsFinite(distanceMeters) || distanceMeters < 0f) { return "distance-non-finite"; } if (distanceMeters > 4096f) { return "distance-out-of-range"; } return string.Empty; } private static bool IsFinite(float value) { if (!float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } public static bool ShouldTrackCommandMarker(bool featureEnabled, bool individualMode, bool shareCommandPicks, bool classifierIsCommand, bool exactLocalStateResolved, bool anyLocalPending) { return featureEnabled && individualMode && shareCommandPicks && classifierIsCommand && exactLocalStateResolved && anyLocalPending; } public static bool RenderStateRestorationContractIsComplete() { if (MutatedGuiGlobals.Count == RestoredGuiGlobals.Count) { return MutatedGuiGlobals.All((string x) => RestoredGuiGlobals.Contains(x, StringComparer.Ordinal)); } return false; } private static int ClampInt(int value, int min, int max) { if (value >= min) { if (value <= max) { return value; } return max; } return min; } private static float ClampFloat(float value, float min, float max) { if (!(value < min)) { if (!(value > max)) { return value; } return max; } return min; } } public readonly struct MarkerRelativePlacement { public float OffsetX { get; } public float OffsetY { get; } public MarkerHudMode Mode { get; } public MarkerHudEdge Edge { get; } public float ArrowRotationDegrees { get; } public bool HudRelocated { get; } public bool CollisionRelocated { get; } public bool MessageHudRelocated { get; } public int LaneSlot { get; } public int RailSlot { get; } public MarkerRelativePlacement(float offsetX, float offsetY, MarkerHudMode mode, MarkerHudEdge edge, float arrowRotationDegrees, bool hudRelocated, bool collisionRelocated, bool messageHudRelocated, int laneSlot, int railSlot) { OffsetX = offsetX; OffsetY = offsetY; Mode = mode; Edge = edge; ArrowRotationDegrees = arrowRotationDegrees; HudRelocated = hudRelocated; CollisionRelocated = collisionRelocated; MessageHudRelocated = messageHudRelocated; LaneSlot = laneSlot; RailSlot = railSlot; } } public static class MarkerProjectionRelativePlacementPolicy { public const float FastFollowProjectionJumpPixels1080 = 180f; public static MarkerRelativePlacement Capture(MarkerHudProjection projection, MarkerHudPlacement placement) { return new MarkerRelativePlacement(placement.X - projection.X, placement.Y - projection.Y, placement.Mode, placement.Edge, placement.ArrowRotationDegrees, placement.HudRelocated, placement.CollisionRelocated, placement.MessageHudRelocated, placement.LaneSlot, placement.RailSlot); } public static MarkerHudPlacement Apply(long stableKey, MarkerHudProjection currentProjection, MarkerHudVisualFootprint footprint, MarkerRelativePlacement relative) { float num = currentProjection.X + relative.OffsetX; float num2 = currentProjection.Y + relative.OffsetY; return new MarkerHudPlacement(finalRect: new MarkerHudRect(num, num2, footprint.Width, footprint.Height), stableKey: stableKey, mode: currentProjection.Mode, edge: currentProjection.Edge, x: num, y: num2, arrowRotationDegrees: (currentProjection.Mode == MarkerHudMode.OffScreenEdge) ? currentProjection.ArrowRotationDegrees : relative.ArrowRotationDegrees, laneSlot: relative.LaneSlot, railSlot: relative.RailSlot, hudRelocated: relative.HudRelocated, collisionRelocated: relative.CollisionRelocated, messageHudRelocated: relative.MessageHudRelocated); } public static bool RequiresFastFollow(MarkerHudProjection previous, MarkerHudProjection current, float screenWidth, float screenHeight) { if (!previous.Valid || !current.Valid) { return true; } if (previous.Mode != current.Mode || previous.Edge != current.Edge) { return true; } float num = Math.Min(screenWidth / 1920f, screenHeight / 1080f); if (float.IsNaN(num) || float.IsInfinity(num) || num <= 0f) { num = 1f; } float num2 = 180f * num; float num3 = current.X - previous.X; float num4 = current.Y - previous.Y; return num3 * num3 + num4 * num4 >= num2 * num2; } } public readonly struct MarkerMeasurementCacheKey : IEquatable { public string SemanticText { get; } public int FontAssetIdentity { get; } public int MaterialIdentity { get; } public int FontSize { get; } public int ScreenWidth { get; } public int ScreenHeight { get; } public int TypographyRevision { get; } public MarkerMeasurementCacheKey(string semanticText, int fontAssetIdentity, int materialIdentity, int fontSize, int screenWidth, int screenHeight, int typographyRevision) { SemanticText = semanticText ?? throw new ArgumentNullException("semanticText"); FontAssetIdentity = fontAssetIdentity; MaterialIdentity = materialIdentity; FontSize = fontSize; ScreenWidth = screenWidth; ScreenHeight = screenHeight; TypographyRevision = typographyRevision; } public bool Equals(MarkerMeasurementCacheKey other) { if (string.Equals(SemanticText, other.SemanticText, StringComparison.Ordinal) && FontAssetIdentity == other.FontAssetIdentity && MaterialIdentity == other.MaterialIdentity && FontSize == other.FontSize && ScreenWidth == other.ScreenWidth && ScreenHeight == other.ScreenHeight) { return TypographyRevision == other.TypographyRevision; } return false; } public override bool Equals(object? obj) { if (obj is MarkerMeasurementCacheKey other) { return Equals(other); } return false; } public override int GetHashCode() { return (((((((((((((SemanticText != null) ? StringComparer.Ordinal.GetHashCode(SemanticText) : 0) * 397) ^ FontAssetIdentity) * 397) ^ MaterialIdentity) * 397) ^ FontSize) * 397) ^ ScreenWidth) * 397) ^ ScreenHeight) * 397) ^ TypographyRevision; } } public readonly struct MarkerHudDiagnosticState : IEquatable { public MarkerHudMode Mode { get; } public MarkerHudEdge Edge { get; } public int LaneSlot { get; } public int RailSlot { get; } public bool HudRelocated { get; } public bool MessageHudRelocated { get; } public bool CollisionRelocated { get; } public bool MeasurementFallback { get; } public MarkerHudDiagnosticState(MarkerHudMode mode, MarkerHudEdge edge, int laneSlot, int railSlot, bool hudRelocated, bool messageHudRelocated, bool collisionRelocated, bool measurementFallback) { Mode = mode; Edge = edge; LaneSlot = laneSlot; RailSlot = railSlot; HudRelocated = hudRelocated; MessageHudRelocated = messageHudRelocated; CollisionRelocated = collisionRelocated; MeasurementFallback = measurementFallback; } public bool Equals(MarkerHudDiagnosticState other) { if (Mode == other.Mode && Edge == other.Edge && LaneSlot == other.LaneSlot && RailSlot == other.RailSlot && HudRelocated == other.HudRelocated && MessageHudRelocated == other.MessageHudRelocated && CollisionRelocated == other.CollisionRelocated) { return MeasurementFallback == other.MeasurementFallback; } return false; } public override bool Equals(object? obj) { if (obj is MarkerHudDiagnosticState other) { return Equals(other); } return false; } public override int GetHashCode() { return (int)(((((((((((((uint)((int)Mode * 397) ^ (uint)Edge) * 397) ^ (uint)LaneSlot) * 397) ^ (uint)RailSlot) * 397) ^ (HudRelocated ? 1u : 0u)) * 397) ^ (MessageHudRelocated ? 1u : 0u)) * 397) ^ (CollisionRelocated ? 1u : 0u)) * 397) ^ (MeasurementFallback ? 1 : 0); } } public static class MarkerRuntimeHotPathPolicy { public static bool CanReuseMeasurement(bool hasCachedMeasurement, MarkerMeasurementCacheKey cachedKey, MarkerMeasurementCacheKey requestedKey) { if (hasCachedMeasurement) { return cachedKey.Equals(requestedKey); } return false; } public static bool ShouldApplyTypography(int appliedTypographyRevision, int appliedFontSize, int requestedTypographyRevision, int requestedFontSize) { if (appliedTypographyRevision == requestedTypographyRevision) { return appliedFontSize != requestedFontSize; } return true; } public static MarkerHudDiagnosticState BuildHudDiagnosticState(MarkerHudMode mode, MarkerHudEdge edge, int laneSlot, int railSlot, bool hudRelocated, bool messageHudRelocated, bool collisionRelocated, bool measurementFallback, float labelPreferredWidth, float footprintWidth) { return new MarkerHudDiagnosticState(mode, edge, laneSlot, railSlot, hudRelocated, messageHudRelocated, collisionRelocated, measurementFallback); } } public enum MarkerLanguage { English, French, Italian, German, SpanishSpain, Japanese, Korean, PortugueseBrazil, Russian, SimplifiedChinese, Turkish } public static class MarkerTextLocalization { public static readonly MarkerLanguage[] OfficialLanguages = new MarkerLanguage[11] { MarkerLanguage.English, MarkerLanguage.French, MarkerLanguage.Italian, MarkerLanguage.German, MarkerLanguage.SpanishSpain, MarkerLanguage.Japanese, MarkerLanguage.Korean, MarkerLanguage.PortugueseBrazil, MarkerLanguage.Russian, MarkerLanguage.SimplifiedChinese, MarkerLanguage.Turkish }; private static readonly Dictionary Text = new Dictionary(StringComparer.Ordinal) { ["readable.white"] = new string[11] { "White", "Blanc", "Bianco", "Weiß", "Blanco", "白", "흰색", "Branco", "Белый", "白色", "Beyaz" }, ["readable.green"] = new string[11] { "Green", "Vert", "Verde", "Grün", "Verde", "緑", "초록", "Verde", "Зелёный", "绿色", "Yeşil" }, ["readable.red"] = new string[11] { "Red", "Rouge", "Rosso", "Rot", "Rojo", "赤", "빨강", "Vermelho", "Красный", "红色", "Kırmızı" }, ["readable.boss_yellow"] = new string[11] { "Yellow", "Jaune", "Giallo", "Gelb", "Amarillo", "黄", "노랑", "Amarelo", "Жёлтый", "黄色", "Sarı" }, ["readable.lunar"] = new string[11] { "Lunar", "Lunaire", "Lunare", "Lunar", "Lunar", "ルナ", "루나", "Lunar", "Лунный", "月球", "Ay" }, ["readable.void"] = new string[11] { "Void", "Néant", "Vuoto", "Leere", "Vacío", "ヴォイド", "공허", "Vazio", "Бездна", "虚空", "Hiçlik" }, ["readable.equipment"] = new string[11] { "Equipment", "Équipement", "Equipaggiamento", "Ausrüstung", "Equipo", "装備", "장비", "Equipamento", "Снаряжение", "装备", "Ekipman" }, ["readable.lunar_equipment"] = new string[11] { "Equipment", "Équipement", "Equipaggiamento", "Ausrüstung", "Equipo", "装備", "장비", "Equipamento", "Снаряжение", "装备", "Ekipman" }, ["readable.item_choice"] = new string[11] { "Item choice", "Choix d’objet", "Scelta oggetto", "Gegenstandsauswahl", "Elección de objeto", "アイテム選択", "아이템 선택", "Escolha de item", "Выбор предмета", "物品选择", "Eşya seçimi" }, ["plural.white_items"] = new string[11] { "White items", "Objets blancs", "Oggetti bianchi", "Weiße Gegenstände", "Objetos blancos", "白アイテム", "흰색 아이템", "Itens brancos", "Белые предметы", "白色物品", "Beyaz eşyalar" }, ["plural.green_items"] = new string[11] { "Green items", "Objets verts", "Oggetti verdi", "Grüne Gegenstände", "Objetos verdes", "緑アイテム", "초록 아이템", "Itens verdes", "Зелёные предметы", "绿色物品", "Yeşil eşyalar" }, ["plural.red_items"] = new string[11] { "Red items", "Objets rouges", "Oggetti rossi", "Rote Gegenstände", "Objetos rojos", "赤アイテム", "빨간 아이템", "Itens vermelhos", "Красные предметы", "红色物品", "Kırmızı eşyalar" }, ["plural.boss_items"] = new string[11] { "Boss items", "Objets de boss", "Oggetti boss", "Boss-Gegenstände", "Objetos de jefe", "ボスアイテム", "보스 아이템", "Itens de chefe", "Предметы босса", "首领物品", "Boss eşyaları" }, ["plural.lunar_items"] = new string[11] { "Lunar items", "Objets lunaires", "Oggetti lunari", "Lunar-Gegenstände", "Objetos lunares", "ルナアイテム", "루나 아이템", "Itens lunares", "Лунные предметы", "月球物品", "Ay eşyaları" }, ["plural.void_items"] = new string[11] { "Void items", "Objets du Néant", "Oggetti del Vuoto", "Leere-Gegenstände", "Objetos del Vacío", "ヴォイドアイテム", "공허 아이템", "Itens do Vazio", "Предметы Бездны", "虚空物品", "Hiçlik eşyaları" }, ["plural.equipment"] = new string[11] { "Equipment", "Équipement", "Equipaggiamento", "Ausrüstung", "Equipo", "装備", "장비", "Equipamento", "Снаряжение", "装备", "Ekipman" }, ["plural.lunar_equipment"] = new string[11] { "Lunar equipment", "Équipement lunaire", "Equipaggiamento lunare", "Lunar-Ausrüstung", "Equipo lunar", "ルナ装備", "루나 장비", "Equipamento lunar", "Лунное снаряжение", "月球装备", "Ay ekipmanı" }, ["plural.other_items"] = new string[11] { "Other items", "Autres objets", "Altri oggetti", "Andere Gegenstände", "Otros objetos", "その他のアイテム", "기타 아이템", "Outros itens", "Другие предметы", "其他物品", "Diğer eşyalar" }, ["plural.item_choice"] = new string[11] { "Item choice", "Choix d’objet", "Scelta oggetto", "Gegenstandsauswahl", "Elección de objeto", "アイテム選択", "아이템 선택", "Escolha de item", "Выбор предмета", "物品选择", "Eşya seçimi" }, ["plural.unknown_items"] = new string[11] { "Unknown items", "Objets inconnus", "Oggetti sconosciuti", "Unbekannte Gegenstände", "Objetos desconocidos", "不明なアイテム", "알 수 없는 아이템", "Itens desconhecidos", "Неизвестные предметы", "未知物品", "Bilinmeyen eşyalar" }, ["composition.white"] = new string[11] { "White", "Blanc", "Bianco", "Weiß", "Blanco", "白", "흰색", "Branco", "Белые", "白色", "Beyaz" }, ["composition.green"] = new string[11] { "Green", "Vert", "Verde", "Grün", "Verde", "緑", "초록", "Verde", "Зелёные", "绿色", "Yeşil" }, ["composition.red"] = new string[11] { "Red", "Rouge", "Rosso", "Rot", "Rojo", "赤", "빨강", "Vermelho", "Красные", "红色", "Kırmızı" }, ["composition.boss"] = new string[11] { "Boss", "Boss", "Boss", "Boss", "Jefe", "ボス", "보스", "Chefe", "Босс", "首领", "Boss" }, ["composition.lunar"] = new string[11] { "Lunar", "Lunaire", "Lunare", "Lunar", "Lunar", "ルナ", "루나", "Lunar", "Лунные", "月球", "Ay" }, ["composition.void"] = new string[11] { "Void", "Néant", "Vuoto", "Leere", "Vacío", "ヴォイド", "공허", "Vazio", "Бездна", "虚空", "Hiçlik" }, ["composition.equipment"] = new string[11] { "Equipment", "Équipement", "Equipaggiamento", "Ausrüstung", "Equipo", "装備", "장비", "Equipamento", "Снаряжение", "装备", "Ekipman" }, ["composition.lunar_equipment"] = new string[11] { "Lunar equipment", "Équipement lunaire", "Equipaggiamento lunare", "Lunar-Ausrüstung", "Equipo lunar", "ルナ装備", "루나 장비", "Equipamento lunar", "Лунное снаряжение", "月球装备", "Ay ekipmanı" }, ["composition.other"] = new string[11] { "Other", "Autres", "Altro", "Andere", "Otros", "その他", "기타", "Outros", "Другие", "其他", "Diğer" }, ["composition.choice"] = new string[11] { "Choice", "Choix", "Scelta", "Auswahl", "Elección", "選択", "선택", "Escolha", "Выбор", "选择", "Seçim" }, ["composition.unknown"] = new string[11] { "Unknown", "Inconnu", "Sconosciuto", "Unbekannt", "Desconocido", "不明", "알 수 없음", "Desconhecido", "Неизвестные", "未知", "Bilinmeyen" }, ["summary.white"] = new string[11] { "White", "Blanc", "Bianco", "Weiß", "Blanco", "白", "흰색", "Branco", "Белый", "白色", "Beyaz" }, ["summary.green"] = new string[11] { "Green", "Vert", "Verde", "Grün", "Verde", "緑", "초록", "Verde", "Зелёный", "绿色", "Yeşil" }, ["summary.red"] = new string[11] { "Red", "Rouge", "Rosso", "Rot", "Rojo", "赤", "빨강", "Vermelho", "Красный", "红色", "Kırmızı" }, ["summary.boss"] = new string[11] { "Boss", "Boss", "Boss", "Boss", "Jefe", "ボス", "보스", "Chefe", "Босс", "首领", "Boss" }, ["summary.lunar"] = new string[11] { "Lunar", "Lunaire", "Lunare", "Lunar", "Lunar", "ルナ", "루나", "Lunar", "Лунный", "月球", "Ay" }, ["summary.void"] = new string[11] { "Void", "Néant", "Vuoto", "Leere", "Vacío", "ヴォイド", "공허", "Vazio", "Бездна", "虚空", "Hiçlik" }, ["summary.equipment"] = new string[11] { "Equipment", "Équipement", "Equipaggiamento", "Ausrüstung", "Equipo", "装備", "장비", "Equipamento", "Снаряжение", "装备", "Ekipman" }, ["summary.lunar_equipment"] = new string[11] { "Lunar equipment", "Équipement lunaire", "Equipaggiamento lunare", "Lunar-Ausrüstung", "Equipo lunar", "ルナ装備", "루나 장비", "Equipamento lunar", "Лунное снаряжение", "月球装备", "Ay ekipmanı" }, ["summary.other"] = new string[11] { "Other", "Autres", "Altro", "Andere", "Otros", "その他", "기타", "Outros", "Другие", "其他", "Diğer" }, ["summary.choice"] = new string[11] { "Choice", "Choix", "Scelta", "Auswahl", "Elección", "選択", "선택", "Escolha", "Выбор", "选择", "Seçim" }, ["summary.unknown"] = new string[11] { "Unknown", "Inconnu", "Sconosciuto", "Unbekannt", "Desconocido", "不明", "알 수 없음", "Desconhecido", "Неизвестные", "未知", "Bilinmeyen" }, ["generic.items"] = new string[11] { "Items", "Objets", "Oggetti", "Gegenstände", "Objetos", "アイテム", "아이템", "Itens", "Предметы", "物品", "Eşyalar" }, ["fallback.pickup"] = new string[11] { "Pickup", "Objet", "Oggetto", "Gegenstand", "Objeto", "アイテム", "아이템", "Item", "Предмет", "物品", "Eşya" }, ["fallback.shared_pickup"] = new string[11] { "Shared pickup", "Objet partagé", "Oggetto condiviso", "Geteilter Gegenstand", "Objeto compartido", "共有アイテム", "공유 아이템", "Item compartilhado", "Общий предмет", "共享物品", "Paylaşılan eşya" }, ["fallback.command_choice"] = new string[11] { "Command choice", "Choix de Commande", "Scelta Comando", "Command-Auswahl", "Elección de Comando", "コマンド選択", "지휘 선택", "Escolha de Comando", "Выбор предмета", "命令选择", "Komut seçimi" }, ["overflow.physical_items_format"] = new string[11] { "+{0} items", "+{0} objets", "+{0} oggetti", "+{0} Gegenstände", "+{0} objetos", "+{0} アイテム", "+{0} 아이템", "+{0} itens", "+{0} предметов", "+{0} 个物品", "+{0} eşya" }, ["overflow.more_types_format"] = new string[11] { "+ {0} more types", "+ {0} types supplémentaires", "+ altri {0} tipi", "+ {0} weitere Typen", "+ {0} tipos más", "+ 他{0}種類", "+ {0}종 더", "+ mais {0} tipos", "+ ещё {0} {RU_TYPE_WORD}", "+ 另外 {0} 种", "+ {0} tür daha" }, ["distance.unit"] = new string[11] { "m", "m", "m", "m", "m", "m", "m", "m", "м", "米", "m" } }; public static MarkerLanguage ResolveLanguage(string? raw) { switch ((raw ?? string.Empty).Trim().ToLowerInvariant()) { case "english": case "en": return MarkerLanguage.English; case "fr": case "french": return MarkerLanguage.French; case "italian": case "it": return MarkerLanguage.Italian; case "de": case "german": return MarkerLanguage.German; case "spanish": case "es": return MarkerLanguage.SpanishSpain; case "ja": case "japanese": return MarkerLanguage.Japanese; case "ko": case "korean": return MarkerLanguage.Korean; case "pt": case "pt-br": case "portuguese": return MarkerLanguage.PortugueseBrazil; case "russian": case "ru": return MarkerLanguage.Russian; case "chinese": case "zh": case "zh-cn": return MarkerLanguage.SimplifiedChinese; case "turkish": case "tr": return MarkerLanguage.Turkish; default: return MarkerLanguage.English; } } public static MarkerLanguage FromRussianCompatibility(bool russian) { if (!russian) { return MarkerLanguage.English; } return MarkerLanguage.Russian; } public static string ReadableClassLabel(MarkerClassKind kind, MarkerLanguage language) { return kind switch { MarkerClassKind.Tier1 => Get("readable.white", language), MarkerClassKind.Tier2 => Get("readable.green", language), MarkerClassKind.Tier3 => Get("readable.red", language), MarkerClassKind.Boss => Get("readable.boss_yellow", language), MarkerClassKind.Lunar => Get("readable.lunar", language), MarkerClassKind.Void => Get("readable.void", language), MarkerClassKind.Equipment => Get("readable.equipment", language), MarkerClassKind.LunarEquipment => Get("readable.lunar_equipment", language), _ => Get("readable.item_choice", language), }; } public static string CategoryPlural(MarkerSemanticCategory category, MarkerLanguage language) { return category switch { MarkerSemanticCategory.Tier1 => Get("plural.white_items", language), MarkerSemanticCategory.Tier2 => Get("plural.green_items", language), MarkerSemanticCategory.Tier3 => Get("plural.red_items", language), MarkerSemanticCategory.Boss => Get("plural.boss_items", language), MarkerSemanticCategory.Lunar => Get("plural.lunar_items", language), MarkerSemanticCategory.Void => Get("plural.void_items", language), MarkerSemanticCategory.Equipment => Get("plural.equipment", language), MarkerSemanticCategory.LunarEquipment => Get("plural.lunar_equipment", language), MarkerSemanticCategory.Other => Get("plural.other_items", language), MarkerSemanticCategory.CommandState => Get("plural.item_choice", language), _ => Get("plural.unknown_items", language), }; } public static string CompositionLabel(MarkerSemanticCategory category, MarkerLanguage language) { return category switch { MarkerSemanticCategory.Tier1 => Get("composition.white", language), MarkerSemanticCategory.Tier2 => Get("composition.green", language), MarkerSemanticCategory.Tier3 => Get("composition.red", language), MarkerSemanticCategory.Boss => Get("composition.boss", language), MarkerSemanticCategory.Lunar => Get("composition.lunar", language), MarkerSemanticCategory.Void => Get("composition.void", language), MarkerSemanticCategory.Equipment => Get("composition.equipment", language), MarkerSemanticCategory.LunarEquipment => Get("composition.lunar_equipment", language), MarkerSemanticCategory.Other => Get("composition.other", language), MarkerSemanticCategory.CommandState => Get("composition.choice", language), _ => Get("composition.unknown", language), }; } public static string CategorySummaryLabel(MarkerSemanticCategory category, MarkerLanguage language) { return category switch { MarkerSemanticCategory.Tier1 => Get("summary.white", language), MarkerSemanticCategory.Tier2 => Get("summary.green", language), MarkerSemanticCategory.Tier3 => Get("summary.red", language), MarkerSemanticCategory.Boss => Get("summary.boss", language), MarkerSemanticCategory.Lunar => Get("summary.lunar", language), MarkerSemanticCategory.Void => Get("summary.void", language), MarkerSemanticCategory.Equipment => Get("summary.equipment", language), MarkerSemanticCategory.LunarEquipment => Get("summary.lunar_equipment", language), MarkerSemanticCategory.Other => Get("summary.other", language), MarkerSemanticCategory.CommandState => Get("summary.choice", language), _ => Get("summary.unknown", language), }; } public static string GenericItems(MarkerLanguage language) { return Get("generic.items", language); } public static string FallbackPickup(MarkerLanguage language) { return Get("fallback.pickup", language); } public static string FallbackSharedPickup(MarkerLanguage language) { return Get("fallback.shared_pickup", language); } public static string FallbackCommandChoice(MarkerLanguage language) { return Get("fallback.command_choice", language); } public static string DistanceUnit(MarkerLanguage language) { return Get("distance.unit", language); } public static string FormatPhysicalItems(int count, MarkerLanguage language) { return string.Format(CultureInfo.InvariantCulture, Get("overflow.physical_items_format", language), Math.Max(0, count)); } public static string FormatMoreTypes(int count, MarkerLanguage language) { int num = Math.Max(0, count); string text = Get("overflow.more_types_format", language); if (language == MarkerLanguage.Russian) { text = text.Replace("{RU_TYPE_WORD}", RussianDistinctTypeWord(num)); } return string.Format(CultureInfo.InvariantCulture, text, num); } public static string RussianDistinctTypeWord(int count) { int num = Math.Abs(count); int num2 = num % 100; if (num2 >= 11 && num2 <= 14) { return "видов"; } switch (num % 10) { case 1: return "вид"; case 2: case 3: case 4: return "вида"; default: return "видов"; } } public static bool ContainsKey(string key) { if (key != null) { return Text.ContainsKey(key); } return false; } private static string Get(string key, MarkerLanguage language) { if (!Text.TryGetValue(key, out string[] value) || value.Length == 0) { return string.Empty; } int num = (int)language; if (num < 0 || num >= value.Length) { num = 0; } return value[num]; } } public readonly struct MarkerWorldPoint : IEquatable { public float X { get; } public float Y { get; } public float Z { get; } public MarkerWorldPoint(float x, float y, float z) { X = x; Y = y; Z = z; } public bool Equals(MarkerWorldPoint other) { if (X.Equals(other.X) && Y.Equals(other.Y)) { return Z.Equals(other.Z); } return false; } public override bool Equals(object? obj) { if (obj is MarkerWorldPoint other) { return Equals(other); } return false; } public override int GetHashCode() { return (((X.GetHashCode() * 397) ^ Y.GetHashCode()) * 397) ^ Z.GetHashCode(); } public override string ToString() { return X.ToString("F2", CultureInfo.InvariantCulture) + "," + Y.ToString("F2", CultureInfo.InvariantCulture) + "," + Z.ToString("F2", CultureInfo.InvariantCulture); } public static float DistanceSquared(MarkerWorldPoint left, MarkerWorldPoint right) { float num = left.X - right.X; float num2 = left.Y - right.Y; float num3 = left.Z - right.Z; return num * num + num2 * num2 + num3 * num3; } } public enum MarkerSemanticCategory { Tier1, Tier2, Tier3, Boss, Lunar, Void, Equipment, LunarEquipment, Other, Unknown, CommandState } public static class MarkerSemanticCategoryPolicy { public static MarkerSemanticCategory From(PersonalMarkerKind markerKind, MarkerClassKind classKind) { if (markerKind == PersonalMarkerKind.CommandPicker && classKind == MarkerClassKind.Unknown) { return MarkerSemanticCategory.CommandState; } return classKind switch { MarkerClassKind.Tier1 => MarkerSemanticCategory.Tier1, MarkerClassKind.Tier2 => MarkerSemanticCategory.Tier2, MarkerClassKind.Tier3 => MarkerSemanticCategory.Tier3, MarkerClassKind.Boss => MarkerSemanticCategory.Boss, MarkerClassKind.Lunar => MarkerSemanticCategory.Lunar, MarkerClassKind.Void => MarkerSemanticCategory.Void, MarkerClassKind.Equipment => MarkerSemanticCategory.Equipment, MarkerClassKind.LunarEquipment => MarkerSemanticCategory.LunarEquipment, MarkerClassKind.Other => MarkerSemanticCategory.Other, _ => MarkerSemanticCategory.Unknown, }; } public static int StableOrder(MarkerSemanticCategory category) { return category switch { MarkerSemanticCategory.Tier1 => 0, MarkerSemanticCategory.Tier2 => 1, MarkerSemanticCategory.Tier3 => 2, MarkerSemanticCategory.Boss => 3, MarkerSemanticCategory.Lunar => 4, MarkerSemanticCategory.Void => 5, MarkerSemanticCategory.Equipment => 6, MarkerSemanticCategory.LunarEquipment => 7, MarkerSemanticCategory.Other => 8, MarkerSemanticCategory.Unknown => 9, _ => 10, }; } } public readonly struct MarkerWorldMember { public long StableKey { get; } public PersonalMarkerKind MarkerKind { get; } public MarkerWorldPoint WorldPosition { get; } public string ItemIdentity { get; } public string LocalizedName { get; } public MarkerClassKind ClassKind { get; } public MarkerSemanticCategory Category { get; } public MarkerLifetimeKind Lifetime { get; } public MarkerWorldMember(long stableKey, PersonalMarkerKind markerKind, MarkerWorldPoint worldPosition, string itemIdentity, string localizedName, MarkerClassKind classKind) : this(stableKey, markerKind, worldPosition, itemIdentity, localizedName, classKind, MarkerLifetimeKind.Permanent) { } public MarkerWorldMember(long stableKey, PersonalMarkerKind markerKind, MarkerWorldPoint worldPosition, string itemIdentity, string localizedName, MarkerClassKind classKind, MarkerLifetimeKind lifetime) { StableKey = stableKey; MarkerKind = markerKind; WorldPosition = worldPosition; ItemIdentity = (string.IsNullOrWhiteSpace(itemIdentity) ? ("UNKNOWN:" + stableKey.ToString(CultureInfo.InvariantCulture)) : itemIdentity); LocalizedName = (string.IsNullOrWhiteSpace(localizedName) ? "Pickup" : localizedName); ClassKind = classKind; Category = MarkerSemanticCategoryPolicy.From(markerKind, classKind); Lifetime = lifetime; } } public readonly struct MarkerCategoryCount { public MarkerSemanticCategory Category { get; } public int Count { get; } public MarkerCategoryCount(MarkerSemanticCategory category, int count) { Category = category; Count = Math.Max(0, count); } } public readonly struct MarkerItemAggregate { public string ItemIdentity { get; } public string LocalizedName { get; } public MarkerSemanticCategory Category { get; } public int Count { get; } public MarkerLifetimeKind Lifetime { get; } public MarkerItemAggregate(string itemIdentity, string localizedName, MarkerSemanticCategory category, int count) : this(itemIdentity, localizedName, category, count, MarkerLifetimeKind.Permanent) { } public MarkerItemAggregate(string itemIdentity, string localizedName, MarkerSemanticCategory category, int count, MarkerLifetimeKind lifetime) { ItemIdentity = itemIdentity ?? string.Empty; LocalizedName = (string.IsNullOrWhiteSpace(localizedName) ? "Pickup" : localizedName); Category = category; Count = Math.Max(0, count); Lifetime = lifetime; } } public sealed class MarkerSemanticCluster { public long StableKey { get; } public string MemberFingerprint { get; } public MarkerWorldPoint WorldAnchor { get; private set; } public IReadOnlyList MemberStableKeys { get; } public IReadOnlyList Composition { get; } public IReadOnlyList ItemRows { get; } public int TotalCount => MemberStableKeys.Count; public int TemporaryPhysicalMemberCount { get; } public int MixedLifetimeMemberCount { get; } public int UnknownLifetimeMemberCount { get; } public int PermanentPhysicalMemberCount => Math.Max(0, TotalCount - TemporaryPhysicalMemberCount - MixedLifetimeMemberCount - UnknownLifetimeMemberCount); public bool HasTemporaryLifetime { get { if (TemporaryPhysicalMemberCount <= 0) { return MixedLifetimeMemberCount > 0; } return true; } } public MarkerLifetimeKind LifetimeSummary => MarkerLifetimePolicy.SummarizeCluster(TotalCount, TemporaryPhysicalMemberCount, MixedLifetimeMemberCount, UnknownLifetimeMemberCount); public bool IsMixedCategory => Composition.Count > 1; public bool IsHomogeneousCategory => Composition.Count == 1; public MarkerSemanticCategory HomogeneousCategory { get { if (Composition.Count != 1) { return MarkerSemanticCategory.Unknown; } return Composition[0].Category; } } internal MarkerSemanticCluster(long stableKey, string memberFingerprint, MarkerWorldPoint worldAnchor, long[] memberStableKeys, MarkerCategoryCount[] composition, MarkerItemAggregate[] itemRows) : this(stableKey, memberFingerprint, worldAnchor, memberStableKeys, composition, itemRows, 0, 0, 0) { } internal MarkerSemanticCluster(long stableKey, string memberFingerprint, MarkerWorldPoint worldAnchor, long[] memberStableKeys, MarkerCategoryCount[] composition, MarkerItemAggregate[] itemRows, int temporaryPhysicalMemberCount, int mixedLifetimeMemberCount, int unknownLifetimeMemberCount) { StableKey = stableKey; MemberFingerprint = memberFingerprint ?? string.Empty; WorldAnchor = worldAnchor; MemberStableKeys = memberStableKeys ?? Array.Empty(); Composition = composition ?? Array.Empty(); ItemRows = itemRows ?? Array.Empty(); TemporaryPhysicalMemberCount = Math.Max(0, temporaryPhysicalMemberCount); MixedLifetimeMemberCount = Math.Max(0, mixedLifetimeMemberCount); UnknownLifetimeMemberCount = Math.Max(0, unknownLifetimeMemberCount); } internal void RefreshWorldAnchor(MarkerWorldPoint worldAnchor) { WorldAnchor = worldAnchor; } } public enum MarkerSemanticLifecycleKind { Created, MembershipChanged, Merged, Split, CompositionChanged, Removed } public readonly struct MarkerSemanticLifecycleEvent { public MarkerSemanticLifecycleKind Kind { get; } public MarkerSemanticCluster Cluster { get; } public string Reason { get; } public MarkerSemanticLifecycleEvent(MarkerSemanticLifecycleKind kind, MarkerSemanticCluster cluster, string reason) { Kind = kind; Cluster = cluster ?? throw new ArgumentNullException("cluster"); Reason = reason ?? string.Empty; } } public sealed class MarkerSemanticUpdate { public IReadOnlyList Clusters { get; } public IReadOnlyList LifecycleEvents { get; } public bool MembershipChanged { get; } internal MarkerSemanticUpdate(IReadOnlyList clusters, IReadOnlyList lifecycleEvents, bool membershipChanged) { Clusters = clusters; LifecycleEvents = lifecycleEvents; MembershipChanged = membershipChanged; } } public sealed class MarkerWorldClusterTracker { private readonly struct PairKey : IEquatable { public long A { get; } public long B { get; } public PairKey(long first, long second) { if (first <= second) { A = first; B = second; } else { A = second; B = first; } } public bool Equals(PairKey other) { if (A == other.A) { return B == other.B; } return false; } public override bool Equals(object? obj) { if (obj is PairKey other) { return Equals(other); } return false; } public override int GetHashCode() { return (A.GetHashCode() * 397) ^ B.GetHashCode(); } } private sealed class PairState { public bool Linked; public bool Pending; public bool PendingTarget; public double PendingSince; } private sealed class MutableItemAggregate { public string ItemIdentity { get; } public string LocalizedName { get; } public MarkerSemanticCategory Category { get; } public MarkerLifetimeKind Lifetime { get; } public int Count { get; set; } public MutableItemAggregate(string itemIdentity, string localizedName, MarkerSemanticCategory category, MarkerLifetimeKind lifetime) { ItemIdentity = itemIdentity; LocalizedName = localizedName; Category = category; Lifetime = lifetime; } } private sealed class WorldMemberComparer : IComparer { public static readonly WorldMemberComparer Instance = new WorldMemberComparer(); public int Compare(MarkerWorldMember left, MarkerWorldMember right) { return left.StableKey.CompareTo(right.StableKey); } } private sealed class MarkerItemAggregateComparer : IComparer { public static readonly MarkerItemAggregateComparer Instance = new MarkerItemAggregateComparer(); public int Compare(MarkerItemAggregate left, MarkerItemAggregate right) { int num = MarkerSemanticCategoryPolicy.StableOrder(left.Category).CompareTo(MarkerSemanticCategoryPolicy.StableOrder(right.Category)); if (num != 0) { return num; } int num2 = string.Compare(left.LocalizedName, right.LocalizedName, StringComparison.Ordinal); if (num2 != 0) { return num2; } int num3 = string.Compare(left.ItemIdentity, right.ItemIdentity, StringComparison.Ordinal); if (num3 != 0) { return num3; } return MarkerLifetimePolicy.DetailedSortRank(left.Lifetime).CompareTo(MarkerLifetimePolicy.DetailedSortRank(right.Lifetime)); } } public const float MergeRadiusMeters = 4.5f; public const float SplitRadiusMeters = 6f; public const double ThresholdTransitionDwellSeconds = 0.35; public const double RecommendedSemanticSolveIntervalSeconds = 0.2; private readonly Dictionary _pairStates = new Dictionary(); private readonly List _ordered = new List(96); private readonly List _lastMemberKeys = new List(96); private readonly List _lastSemanticTokens = new List(96); private readonly List _clusters = new List(); private readonly List _previousClusters = new List(); private readonly List _events = new List(); private readonly List _stalePairs = new List(); public IReadOnlyList Clusters => _clusters; public MarkerSemanticUpdate Update(IReadOnlyList members, double nowSeconds) { if (members == null) { throw new ArgumentNullException("members"); } if (double.IsNaN(nowSeconds) || double.IsInfinity(nowSeconds)) { nowSeconds = 0.0; } _ordered.Clear(); int num = Math.Min(members.Count, 96); for (int i = 0; i < num; i++) { MarkerWorldMember item = members[i]; if (IsFinite(item.WorldPosition)) { _ordered.Add(item); } } _ordered.Sort(WorldMemberComparer.Instance); DeduplicateOrderedStableKeys(_ordered); bool flag = MemberSetChanged(_ordered, _lastMemberKeys); bool flag2 = SemanticContentChanged(_ordered, _lastSemanticTokens); _previousClusters.Clear(); for (int j = 0; j < _clusters.Count; j++) { _previousClusters.Add(_clusters[j]); } bool flag3 = false; if (flag) { PrunePairStates(_ordered); ReconcileNewPairsImmediately(_ordered); flag3 = UpdatePairLinksWithHysteresis(_ordered, nowSeconds); } else { flag3 = UpdatePairLinksWithHysteresis(_ordered, nowSeconds); } bool num2 = _clusters.Count == 0 || flag || flag2 || flag3; if (num2) { BuildClusters(_ordered, _pairStates, _clusters); } else { RefreshClusterAnchors(_ordered, _clusters); } bool membershipChanged = num2 && !SameClusterMembership(_previousClusters, _clusters); if (num2) { BuildLifecycleEvents(_previousClusters, _clusters, flag, _events); } else { _events.Clear(); } _lastMemberKeys.Clear(); _lastSemanticTokens.Clear(); for (int k = 0; k < _ordered.Count; k++) { _lastMemberKeys.Add(_ordered[k].StableKey); _lastSemanticTokens.Add(MemberSemanticToken(_ordered[k])); } return new MarkerSemanticUpdate(_clusters, _events, membershipChanged); } public void Clear() { _pairStates.Clear(); _ordered.Clear(); _lastMemberKeys.Clear(); _lastSemanticTokens.Clear(); _clusters.Clear(); _previousClusters.Clear(); _events.Clear(); _stalePairs.Clear(); } public static long StableClusterKey(IReadOnlyList sortedMemberKeys) { if (sortedMemberKeys == null) { throw new ArgumentNullException("sortedMemberKeys"); } ulong num = 14695981039346656037uL; for (int i = 0; i < sortedMemberKeys.Count; i++) { ulong num2 = (ulong)sortedMemberKeys[i]; for (int j = 0; j < 8; j++) { num ^= (byte)(num2 & 0xFF); num *= 1099511628211L; num2 >>= 8; } } long num3 = (long)(num & 0x7FFFFFFFFFFFFFFFL); if (num3 != 0L) { return num3; } return 1L; } public static string MemberFingerprint(IReadOnlyList sortedMemberKeys) { return StableClusterKey(sortedMemberKeys).ToString("X16", CultureInfo.InvariantCulture) + ":" + (sortedMemberKeys?.Count ?? 0).ToString(CultureInfo.InvariantCulture); } public static MarkerWorldPoint CurrentAnchor(IReadOnlyList members) { if (members == null || members.Count == 0) { return default(MarkerWorldPoint); } double num = 0.0; double num2 = 0.0; double num3 = 0.0; int num4 = 0; for (int i = 0; i < members.Count; i++) { if (IsFinite(members[i].WorldPosition)) { num += (double)members[i].WorldPosition.X; num2 += (double)members[i].WorldPosition.Y; num3 += (double)members[i].WorldPosition.Z; num4++; } } if (num4 == 0) { return default(MarkerWorldPoint); } return new MarkerWorldPoint((float)(num / (double)num4), (float)(num2 / (double)num4), (float)(num3 / (double)num4)); } private void ReconcileNewPairsImmediately(IReadOnlyList ordered) { float num = 20.25f; for (int i = 0; i < ordered.Count; i++) { for (int j = i + 1; j < ordered.Count; j++) { PairKey key = new PairKey(ordered[i].StableKey, ordered[j].StableKey); if (!_pairStates.ContainsKey(key)) { _pairStates[key] = new PairState { Linked = (MarkerWorldPoint.DistanceSquared(ordered[i].WorldPosition, ordered[j].WorldPosition) <= num) }; } } } } private bool UpdatePairLinksWithHysteresis(IReadOnlyList ordered, double nowSeconds) { bool result = false; float num = 20.25f; float num2 = 36f; for (int i = 0; i < ordered.Count; i++) { for (int j = i + 1; j < ordered.Count; j++) { PairKey key = new PairKey(ordered[i].StableKey, ordered[j].StableKey); if (!_pairStates.TryGetValue(key, out PairState value)) { value = new PairState { Linked = (MarkerWorldPoint.DistanceSquared(ordered[i].WorldPosition, ordered[j].WorldPosition) <= num) }; _pairStates[key] = value; continue; } float num3 = MarkerWorldPoint.DistanceSquared(ordered[i].WorldPosition, ordered[j].WorldPosition); if (value.Linked) { if (num3 <= num2) { value.Pending = false; } else if (AdvancePending(value, target: false, nowSeconds)) { result = true; } } else if (num3 >= num) { value.Pending = false; } else if (AdvancePending(value, target: true, nowSeconds)) { result = true; } } } return result; } private static bool AdvancePending(PairState state, bool target, double nowSeconds) { if (!state.Pending || state.PendingTarget != target) { state.Pending = true; state.PendingTarget = target; state.PendingSince = nowSeconds; return false; } if (nowSeconds - state.PendingSince < 0.35) { return false; } state.Linked = target; state.Pending = false; return true; } private void PrunePairStates(IReadOnlyList ordered) { _stalePairs.Clear(); foreach (PairKey key in _pairStates.Keys) { if (!ContainsStableKey(ordered, key.A) || !ContainsStableKey(ordered, key.B)) { _stalePairs.Add(key); } } for (int i = 0; i < _stalePairs.Count; i++) { _pairStates.Remove(_stalePairs[i]); } } private static bool ContainsStableKey(IReadOnlyList ordered, long key) { int num = 0; int num2 = ordered.Count - 1; while (num <= num2) { int num3 = num + (num2 - num >> 1); long stableKey = ordered[num3].StableKey; if (stableKey == key) { return true; } if (stableKey < key) { num = num3 + 1; } else { num2 = num3 - 1; } } return false; } private static void BuildClusters(IReadOnlyList ordered, IReadOnlyDictionary pairStates, List output) { output.Clear(); if (ordered.Count == 0) { return; } int[] array = new int[ordered.Count]; for (int i = 0; i < array.Length; i++) { array[i] = i; } foreach (KeyValuePair pairState in pairStates) { if (pairState.Value.Linked) { int num = IndexOfStableKey(ordered, pairState.Key.A); int num2 = IndexOfStableKey(ordered, pairState.Key.B); if (num >= 0 && num2 >= 0) { Union(array, num, num2); } } } Dictionary> dictionary = new Dictionary>(); for (int j = 0; j < ordered.Count; j++) { int key = Find(array, j); if (!dictionary.TryGetValue(key, out var value)) { value = new List(); dictionary.Add(key, value); } value.Add(ordered[j]); } List list = new List(dictionary.Count); foreach (List value2 in dictionary.Values) { list.Add(BuildCluster(value2)); } list.Sort((MarkerSemanticCluster left, MarkerSemanticCluster right) => left.StableKey.CompareTo(right.StableKey)); output.AddRange(list); } private static MarkerSemanticCluster BuildCluster(List members) { members.Sort(WorldMemberComparer.Instance); long[] array = new long[members.Count]; for (int i = 0; i < members.Count; i++) { array[i] = members[i].StableKey; } Dictionary dictionary = new Dictionary(); Dictionary dictionary2 = new Dictionary(); int num = 0; int num2 = 0; int num3 = 0; for (int j = 0; j < members.Count; j++) { MarkerWorldMember markerWorldMember = members[j]; dictionary.TryGetValue(markerWorldMember.Category, out var value); dictionary[markerWorldMember.Category] = value + 1; if (markerWorldMember.Lifetime == MarkerLifetimeKind.Temporary) { num++; } else if (markerWorldMember.Lifetime == MarkerLifetimeKind.Mixed) { num2++; } else if (markerWorldMember.Lifetime == MarkerLifetimeKind.Unknown) { num3++; } MarkerItemLifetimeKey key = new MarkerItemLifetimeKey(markerWorldMember.ItemIdentity, markerWorldMember.Lifetime); if (!dictionary2.TryGetValue(key, out var value2)) { value2 = new MutableItemAggregate(markerWorldMember.ItemIdentity, markerWorldMember.LocalizedName, markerWorldMember.Category, markerWorldMember.Lifetime); dictionary2.Add(key, value2); } value2.Count++; } List list = new List(dictionary.Count); foreach (KeyValuePair item in dictionary) { list.Add(new MarkerCategoryCount(item.Key, item.Value)); } list.Sort((MarkerCategoryCount left, MarkerCategoryCount right) => MarkerSemanticCategoryPolicy.StableOrder(left.Category).CompareTo(MarkerSemanticCategoryPolicy.StableOrder(right.Category))); List list2 = new List(dictionary2.Count); foreach (MutableItemAggregate value3 in dictionary2.Values) { list2.Add(new MarkerItemAggregate(value3.ItemIdentity, value3.LocalizedName, value3.Category, value3.Count, value3.Lifetime)); } list2.Sort(MarkerItemAggregateComparer.Instance); return new MarkerSemanticCluster(StableClusterKey(array), MemberFingerprint(array), CurrentAnchor(members), array, list.ToArray(), list2.ToArray(), num, num2, num3); } private static void BuildLifecycleEvents(IReadOnlyList previous, IReadOnlyList current, bool sourceSetChanged, List output) { output.Clear(); if (previous.Count == 0) { for (int i = 0; i < current.Count; i++) { output.Add(new MarkerSemanticLifecycleEvent(MarkerSemanticLifecycleKind.Created, current[i], "initial-or-add")); } return; } for (int j = 0; j < current.Count; j++) { MarkerSemanticCluster markerSemanticCluster = current[j]; int num = FindByFingerprint(previous, markerSemanticCluster.MemberFingerprint); if (num >= 0) { if (!SameSemanticComposition(previous[num], markerSemanticCluster)) { output.Add(new MarkerSemanticLifecycleEvent(MarkerSemanticLifecycleKind.CompositionChanged, markerSemanticCluster, "semantic-composition-changed")); } continue; } int num2 = 0; MarkerSemanticCluster markerSemanticCluster2 = null; for (int k = 0; k < previous.Count; k++) { if (Overlaps(previous[k].MemberStableKeys, markerSemanticCluster.MemberStableKeys)) { num2++; markerSemanticCluster2 = previous[k]; } } MarkerSemanticLifecycleKind kind = ((num2 > 1) ? MarkerSemanticLifecycleKind.Merged : ((num2 == 1 && CountCurrentOverlaps(current, markerSemanticCluster2.MemberStableKeys) > 1) ? MarkerSemanticLifecycleKind.Split : ((num2 == 1) ? MarkerSemanticLifecycleKind.MembershipChanged : MarkerSemanticLifecycleKind.Created))); output.Add(new MarkerSemanticLifecycleEvent(kind, markerSemanticCluster, sourceSetChanged ? "identity-lifecycle" : "world-hysteresis")); } for (int l = 0; l < previous.Count; l++) { MarkerSemanticCluster markerSemanticCluster3 = previous[l]; if (FindByFingerprint(current, markerSemanticCluster3.MemberFingerprint) >= 0) { continue; } bool flag = false; for (int m = 0; m < current.Count; m++) { if (Overlaps(markerSemanticCluster3.MemberStableKeys, current[m].MemberStableKeys)) { flag = true; break; } } if (!flag) { output.Add(new MarkerSemanticLifecycleEvent(MarkerSemanticLifecycleKind.Removed, markerSemanticCluster3, sourceSetChanged ? "identity-lifecycle" : "world-hysteresis")); } } } private static bool SameSemanticComposition(MarkerSemanticCluster left, MarkerSemanticCluster right) { if (left.TemporaryPhysicalMemberCount != right.TemporaryPhysicalMemberCount || left.MixedLifetimeMemberCount != right.MixedLifetimeMemberCount || left.UnknownLifetimeMemberCount != right.UnknownLifetimeMemberCount) { return false; } if (left.Composition.Count != right.Composition.Count || left.ItemRows.Count != right.ItemRows.Count) { return false; } for (int i = 0; i < left.Composition.Count; i++) { if (left.Composition[i].Category != right.Composition[i].Category || left.Composition[i].Count != right.Composition[i].Count) { return false; } } for (int j = 0; j < left.ItemRows.Count; j++) { MarkerItemAggregate markerItemAggregate = left.ItemRows[j]; MarkerItemAggregate markerItemAggregate2 = right.ItemRows[j]; if (!string.Equals(markerItemAggregate.ItemIdentity, markerItemAggregate2.ItemIdentity, StringComparison.Ordinal) || !string.Equals(markerItemAggregate.LocalizedName, markerItemAggregate2.LocalizedName, StringComparison.Ordinal) || markerItemAggregate.Category != markerItemAggregate2.Category || markerItemAggregate.Lifetime != markerItemAggregate2.Lifetime || markerItemAggregate.Count != markerItemAggregate2.Count) { return false; } } return true; } private static int CountCurrentOverlaps(IReadOnlyList current, IReadOnlyList previousMembers) { int num = 0; for (int i = 0; i < current.Count; i++) { if (Overlaps(current[i].MemberStableKeys, previousMembers)) { num++; } } return num; } private static int FindByFingerprint(IReadOnlyList clusters, string fingerprint) { for (int i = 0; i < clusters.Count; i++) { if (string.Equals(clusters[i].MemberFingerprint, fingerprint, StringComparison.Ordinal)) { return i; } } return -1; } private static bool Overlaps(IReadOnlyList left, IReadOnlyList right) { int num = 0; int num2 = 0; while (num < left.Count && num2 < right.Count) { if (left[num] == right[num2]) { return true; } if (left[num] < right[num2]) { num++; } else { num2++; } } return false; } private static bool SameClusterMembership(IReadOnlyList left, IReadOnlyList right) { if (left.Count != right.Count) { return false; } for (int i = 0; i < left.Count; i++) { if (FindByFingerprint(right, left[i].MemberFingerprint) < 0) { return false; } } return true; } private static void RefreshClusterAnchors(IReadOnlyList ordered, IReadOnlyList clusters) { for (int i = 0; i < clusters.Count; i++) { MarkerSemanticCluster markerSemanticCluster = clusters[i]; double num = 0.0; double num2 = 0.0; double num3 = 0.0; int num4 = 0; for (int j = 0; j < markerSemanticCluster.MemberStableKeys.Count; j++) { int num5 = IndexOfStableKey(ordered, markerSemanticCluster.MemberStableKeys[j]); if (num5 >= 0) { MarkerWorldPoint worldPosition = ordered[num5].WorldPosition; if (IsFinite(worldPosition)) { num += (double)worldPosition.X; num2 += (double)worldPosition.Y; num3 += (double)worldPosition.Z; num4++; } } } if (num4 > 0) { markerSemanticCluster.RefreshWorldAnchor(new MarkerWorldPoint((float)(num / (double)num4), (float)(num2 / (double)num4), (float)(num3 / (double)num4))); } } } private static bool SemanticContentChanged(IReadOnlyList ordered, IReadOnlyList previous) { if (ordered.Count != previous.Count) { return true; } for (int i = 0; i < ordered.Count; i++) { if (MemberSemanticToken(ordered[i]) != previous[i]) { return true; } } return false; } private static ulong MemberSemanticToken(MarkerWorldMember member) { ulong hash = 14695981039346656037uL; AddHash(ref hash, (ulong)member.StableKey); AddHash(ref hash, (uint)member.MarkerKind); AddHash(ref hash, (uint)member.ClassKind); AddHash(ref hash, (uint)member.Lifetime); AddHash(ref hash, StableStringHash(member.ItemIdentity)); AddHash(ref hash, StableStringHash(member.LocalizedName)); return hash; } private static ulong StableStringHash(string value) { ulong num = 14695981039346656037uL; if (value == null) { return num; } foreach (char c in value) { num ^= (byte)(c & 0xFF); num *= 1099511628211L; num ^= (byte)((int)c >> 8); num *= 1099511628211L; } return num; } private static void AddHash(ref ulong hash, ulong value) { for (int i = 0; i < 8; i++) { hash ^= (byte)(value & 0xFF); hash *= 1099511628211uL; value >>= 8; } } private static bool MemberSetChanged(IReadOnlyList ordered, IReadOnlyList previous) { if (ordered.Count != previous.Count) { return true; } for (int i = 0; i < ordered.Count; i++) { if (ordered[i].StableKey != previous[i]) { return true; } } return false; } private static void DeduplicateOrderedStableKeys(List ordered) { for (int num = ordered.Count - 1; num > 0; num--) { if (ordered[num].StableKey == ordered[num - 1].StableKey) { ordered.RemoveAt(num); } } } private static int IndexOfStableKey(IReadOnlyList ordered, long stableKey) { int num = 0; int num2 = ordered.Count - 1; while (num <= num2) { int num3 = num + (num2 - num >> 1); long stableKey2 = ordered[num3].StableKey; if (stableKey2 == stableKey) { return num3; } if (stableKey2 < stableKey) { num = num3 + 1; } else { num2 = num3 - 1; } } return -1; } private static int Find(int[] parent, int index) { while (parent[index] != index) { parent[index] = parent[parent[index]]; index = parent[index]; } return index; } private static void Union(int[] parent, int left, int right) { int num = Find(parent, left); int num2 = Find(parent, right); if (num != num2) { if (num < num2) { parent[num2] = num; } else { parent[num] = num2; } } } private static bool IsFinite(MarkerWorldPoint point) { if (IsFinite(point.X) && IsFinite(point.Y)) { return IsFinite(point.Z); } return false; } private static bool IsFinite(float value) { if (!float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } } public enum ParticipantState { Alive, SupportDrone, FullyDead, Disconnected } public enum ClaimState { Pending, Collected, Deferred, GrantedDeferred, CancelledDisconnected } public enum HistoricalClaimState { Collected, CancelledDisconnected, GrantedDeferred } public readonly struct StableUserKey : IEquatable { public string Value { get; } public StableUserKey(string value) { if (string.IsNullOrWhiteSpace(value)) { throw new ArgumentException("Stable user identity is required.", "value"); } Value = value; } public bool Equals(StableUserKey other) { return string.Equals(Value, other.Value, StringComparison.Ordinal); } public override bool Equals(object? obj) { if (obj is StableUserKey other) { return Equals(other); } return false; } public override int GetHashCode() { return StringComparer.Ordinal.GetHashCode(Value); } public override string ToString() { return Value; } } public readonly struct ParticipantKey : IEquatable { public StableUserKey StableUser { get; } public string Generation { get; } public string Value => StableUser.Value + "|generation=" + Generation; public ParticipantKey(StableUserKey stableUser, string generation) { if (string.IsNullOrWhiteSpace(generation)) { throw new ArgumentException("Participation generation is required.", "generation"); } StableUser = stableUser; Generation = generation; } public bool Equals(ParticipantKey other) { if (StableUser.Equals(other.StableUser)) { return string.Equals(Generation, other.Generation, StringComparison.Ordinal); } return false; } public override bool Equals(object? obj) { if (obj is ParticipantKey other) { return Equals(other); } return false; } public override int GetHashCode() { return (StableUser.GetHashCode() * 397) ^ StringComparer.Ordinal.GetHashCode(Generation); } public override string ToString() { return Value; } } public readonly struct SharedPickupKey : IEquatable { public int Value { get; } public SharedPickupKey(int value) { if (value == 0) { throw new ArgumentOutOfRangeException("value", "Unity instance id 0 is not a valid pickup identity."); } Value = value; } public bool Equals(SharedPickupKey other) { return Value == other.Value; } public override bool Equals(object? obj) { if (obj is SharedPickupKey other) { return Equals(other); } return false; } public override int GetHashCode() { return Value; } public override string ToString() { return Value.ToString(CultureInfo.InvariantCulture); } } public readonly struct ClaimKey : IEquatable { public SharedPickupKey Pickup { get; } public ParticipantKey Participant { get; } public ClaimKey(SharedPickupKey pickup, ParticipantKey participant) { Pickup = pickup; Participant = participant; } public bool Equals(ClaimKey other) { if (Pickup.Equals(other.Pickup)) { return Participant.Equals(other.Participant); } return false; } public override bool Equals(object? obj) { if (obj is ClaimKey other) { return Equals(other); } return false; } public override int GetHashCode() { return (Pickup.GetHashCode() * 397) ^ Participant.GetHashCode(); } public override string ToString() { return Pickup.ToString() + "/" + Participant; } } public readonly struct HistoricalClaimKey : IEquatable { public SharedPickupKey Pickup { get; } public StableUserKey StableUser { get; } public HistoricalClaimKey(SharedPickupKey pickup, StableUserKey stableUser) { Pickup = pickup; StableUser = stableUser; } public bool Equals(HistoricalClaimKey other) { if (Pickup.Equals(other.Pickup)) { return StableUser.Equals(other.StableUser); } return false; } public override bool Equals(object? obj) { if (obj is HistoricalClaimKey other) { return Equals(other); } return false; } public override int GetHashCode() { return (Pickup.GetHashCode() * 397) ^ StableUser.GetHashCode(); } public override string ToString() { return Pickup.ToString() + "/stable=" + StableUser; } } public sealed class ClaimRecord { public ClaimKey Key { get; internal set; } public ClaimState State { get; internal set; } public int CreatedStage { get; } public int? TerminalStage { get; internal set; } internal ClaimRecord(ClaimKey key, ClaimState state, int createdStage) { Key = key; State = state; CreatedStage = createdStage; } } public sealed class HistoricalClaimRecord { public HistoricalClaimKey Key { get; internal set; } public HistoricalClaimState State { get; internal set; } public int Stage { get; internal set; } internal HistoricalClaimRecord(HistoricalClaimKey key, HistoricalClaimState state, int stage) { Key = key; State = state; Stage = stage; } } public static class ProjectionPolicy { public static bool HideOrdinaryPickup(bool featureEnabled, bool upstreamPreferenceEnabled, IEnumerable localCollectedStates) { if (!featureEnabled || !upstreamPreferenceEnabled) { return false; } bool[] array = localCollectedStates.ToArray(); if (array.Length != 0) { return array.All((bool x) => x); } return false; } public static bool ShowPersonalMarker(bool featureEnabled, ParticipantState localParticipantState, bool collected, bool historicallyBlocked = false) { if (!featureEnabled || collected || historicallyBlocked) { return false; } if (localParticipantState != ParticipantState.Alive) { return localParticipantState == ParticipantState.SupportDrone; } return true; } } public static class RemoteOperationSignalPolicy { public static bool ShouldClassifySupportDrone(bool authoritativeMasterMatches, bool runtimeShapeCompatible, bool invocationSucceeded, bool exactSignalValue) { return authoritativeMasterMatches && runtimeShapeCompatible && invocationSucceeded && exactSignalValue; } } public static class TemporarySharingPolicy { public static bool ShouldUseVanillaBypass(bool isTemporary, bool shareTemporaryItems, bool upstreamInteractionAlreadyOwned) { if (isTemporary && !shareTemporaryItems) { return !upstreamInteractionAlreadyOwned; } return false; } } }