Decompiled source of BossMakerAPI v0.10.3
BepInEx/plugins/BossMakerAPI/BossMakerAPI.Contracts.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: InternalsVisibleTo("BossMakerAPI")] [assembly: InternalsVisibleTo("BossMakerAPI.Tests")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("BossMakerAPI contributors")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.10.3.0")] [assembly: AssemblyInformationalVersion("0.10.3")] [assembly: AssemblyProduct("BossMakerAPI")] [assembly: AssemblyTitle("BossMakerAPI.Contracts")] [assembly: AssemblyVersion("0.2.0.0")] [module: RefSafetyRules(11)] 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; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace HowToFish.BossMaker { public enum BossActionKind { StatMultiplier, SpawnMinions, ToggleVisualNode, SetVisualScale, PlayAudio, SpawnVisualEffect, AreaDamage, Force, Dash } public abstract class BossActionDefinition { public abstract BossActionKind Kind { get; } public string Id { get; set; } = string.Empty; } public sealed class StatMultiplierAction : BossActionDefinition { public override BossActionKind Kind => BossActionKind.StatMultiplier; public float? DamageMultiplier { get; set; } public float? MovementSpeedMultiplier { get; set; } public float? AttackCooldownMultiplier { get; set; } public float? DashForceMultiplier { get; set; } public float? DashIntervalMultiplier { get; set; } public string ModifierId { get; set; } = string.Empty; public BossModifierLifetime Lifetime { get; set; } public float DurationSeconds { get; set; } } public sealed class SpawnMinionsAction : BossActionDefinition { public override BossActionKind Kind => BossActionKind.SpawnMinions; public string? TemplateName { get; set; } public string? RegisteredBossId { get; set; } public int Count { get; set; } = 1; public float SpawnRadius { get; set; } = 4f; public int MaxAliveFromThisAction { get; set; } = 1; public string GroupId { get; set; } = "default"; public MinionCleanupPolicy CleanupPolicy { get; set; } = MinionCleanupPolicy.Persist; public float LifetimeSeconds { get; set; } public BossPlacementDefinition? Placement { get; set; } public BossFormationDefinition? Formation { get; set; } } public sealed class ToggleVisualNodeAction : BossActionDefinition { public override BossActionKind Kind => BossActionKind.ToggleVisualNode; public string RelativeTransformPath { get; set; } = string.Empty; public bool Enabled { get; set; } = true; public bool Persistent { get; set; } = true; } public sealed class SetVisualScaleAction : BossActionDefinition { public override BossActionKind Kind => BossActionKind.SetVisualScale; public float ScaleMultiplier { get; set; } = 1f; public bool Persistent { get; set; } = true; } public sealed class PlayAudioAction : BossActionDefinition { public override BossActionKind Kind => BossActionKind.PlayAudio; public AudioClip? Clip { get; set; } public float Volume { get; set; } = 1f; public float SpatialBlend { get; set; } = 1f; } public enum BossVisualEffectAnchor { Boss, AbilityOrigin, TargetPosition, AbilityEndpoint } public sealed class SpawnVisualEffectAction : BossActionDefinition { public override BossActionKind Kind => BossActionKind.SpawnVisualEffect; public GameObject? Prefab { get; set; } public BossVisualEffectAnchor Anchor { get; set; } public bool ParentToBoss { get; set; } = true; public bool AlignToAbilityDirection { get; set; } public Vector3 LocalOffset { get; set; } = Vector3.zero; public Vector3 LocalEulerAngles { get; set; } = Vector3.zero; public Vector3 LocalScale { get; set; } = Vector3.one; public float Lifetime { get; set; } = 5f; } public enum MinionCleanupPolicy { DespawnOnBossDefeat, DespawnOnOwnerDespawn, DespawnOnPhaseExit, Persist } public sealed class AreaDamageAction : BossActionDefinition { public override BossActionKind Kind => BossActionKind.AreaDamage; public int Damage { get; set; } = 1; public float DurationSeconds { get; set; } public float TickIntervalSeconds { get; set; } public int MaxHitsPerTarget { get; set; } = 1; public float VerticalTolerance { get; set; } = 4f; } public enum BossForceMode { Push, Pull, Directional } public sealed class ForceAction : BossActionDefinition { public override BossActionKind Kind => BossActionKind.Force; public BossForceMode Mode { get; set; } public float Magnitude { get; set; } = 5f; public float DurationSeconds { get; set; } public float VerticalTolerance { get; set; } = 4f; public float VerticalMultiplier { get; set; } = 1f; public float MaximumVelocityChange { get; set; } = 20f; } public sealed class DashAction : BossActionDefinition { public override BossActionKind Kind => BossActionKind.Dash; public float Impulse { get; set; } = 16f; public float VerticalImpulse { get; set; } public float RetainedVelocityFraction { get; set; } = 0.15f; public float MaximumSpeed { get; set; } = 40f; public float MaximumTravelDistance { get; set; } public float StoppingDistance { get; set; } public float BrakeDurationSeconds { get; set; } public float ExitVelocityFraction { get; set; } = 1f; } public enum BossActionExecutionStatus { Applied, Skipped, Failed } public enum BossActionResultCode { Succeeded, IdempotentReplay, NoEligibleTargets, BudgetExceeded, CapabilityUnavailable, PlacementUnavailable, AuthorityRequired, InvalidContext, Cancelled, ExecutionFailed } public sealed class BossActionResultSnapshot { public string EncounterId { get; } public string BossId { get; } public string ActionId { get; } public BossActionKind ActionKind { get; } public int ActionOrdinal { get; } public long Sequence { get; } public long ServerTick { get; } public double ServerTime { get; } public BossActionExecutionStatus Status { get; } public BossActionResultCode Code { get; } public int AttemptedTargets { get; } public int AffectedTargets { get; } public int SpawnedEntities { get; } public string? PhaseId { get; } public string? AbilityId { get; } public string Message { get; } public bool Succeeded => Status == BossActionExecutionStatus.Applied; public BossActionResultSnapshot(string encounterId, string bossId, string actionId, BossActionKind actionKind, int actionOrdinal, long sequence, long serverTick, double serverTime, BossActionExecutionStatus status, BossActionResultCode code, int attemptedTargets = 0, int affectedTargets = 0, int spawnedEntities = 0, string? phaseId = null, string? abilityId = null, string? message = null) { EncounterIdValidationResult encounterIdValidationResult = EncounterIdValidator.Validate(encounterId); if (!encounterIdValidationResult.IsValid) { throw new ArgumentException(encounterIdValidationResult.Message, "encounterId"); } if (!BossDefinitionValidator.IsValidBossId(bossId)) { throw new ArgumentException("BossId must be a normalized namespaced boss ID.", "bossId"); } EncounterContractValidation.RequireSafeToken(actionId, "actionId", 128); if (!Enum.IsDefined(typeof(BossActionKind), actionKind)) { throw new ArgumentOutOfRangeException("actionKind"); } if (actionOrdinal < 0 || actionOrdinal >= 32) { throw new ArgumentOutOfRangeException("actionOrdinal"); } if (sequence < 0) { throw new ArgumentOutOfRangeException("sequence"); } if (serverTick < 0) { throw new ArgumentOutOfRangeException("serverTick"); } if (!EncounterContractValidation.IsFinite(serverTime) || serverTime < 0.0) { throw new ArgumentOutOfRangeException("serverTime"); } if (!Enum.IsDefined(typeof(BossActionExecutionStatus), status)) { throw new ArgumentOutOfRangeException("status"); } if (!Enum.IsDefined(typeof(BossActionResultCode), code)) { throw new ArgumentOutOfRangeException("code"); } if (attemptedTargets < 0 || attemptedTargets > 1024 || affectedTargets < 0 || affectedTargets > attemptedTargets || spawnedEntities < 0 || spawnedEntities > 1024) { throw new ArgumentOutOfRangeException("attemptedTargets", "Action result counts are inconsistent or exceed their bound."); } if (phaseId != null) { EncounterContractValidation.RequireSafeToken(phaseId, "phaseId", 128); } if (abilityId != null) { EncounterContractValidation.RequireSafeToken(abilityId, "abilityId", 128); } if (message != null && message.Length > 1024) { throw new ArgumentOutOfRangeException("message"); } EncounterId = encounterId; BossId = bossId; ActionId = actionId; ActionKind = actionKind; ActionOrdinal = actionOrdinal; Sequence = sequence; ServerTick = serverTick; ServerTime = serverTime; Status = status; Code = code; AttemptedTargets = attemptedTargets; AffectedTargets = affectedTargets; SpawnedEntities = spawnedEntities; PhaseId = phaseId; AbilityId = abilityId; Message = message ?? string.Empty; } } public enum BossModifierLifetime { Permanent, UntilPhaseExit, UntilAbilityEnd, Timed } public sealed class BossActiveModifierSnapshot { public string ModifierId { get; } public string SourceActionId { get; } public long SourceSequence { get; } public BossModifierLifetime Lifetime { get; } public float? DamageMultiplier { get; } public float? MovementSpeedMultiplier { get; } public float? AttackCooldownMultiplier { get; } public float? DashForceMultiplier { get; } public float? DashIntervalMultiplier { get; } public string? PhaseId { get; } public string? AbilityId { get; } public double? ExpiresAtServerTime { get; } public BossActiveModifierSnapshot(string modifierId, string sourceActionId, long sourceSequence, BossModifierLifetime lifetime, float? damageMultiplier, float? movementSpeedMultiplier, float? attackCooldownMultiplier, string? phaseId = null, string? abilityId = null, double? expiresAtServerTime = null) : this(modifierId, sourceActionId, sourceSequence, lifetime, damageMultiplier, movementSpeedMultiplier, attackCooldownMultiplier, null, null, phaseId, abilityId, expiresAtServerTime) { } public BossActiveModifierSnapshot(string modifierId, string sourceActionId, long sourceSequence, BossModifierLifetime lifetime, float? damageMultiplier, float? movementSpeedMultiplier, float? attackCooldownMultiplier, float? dashForceMultiplier, float? dashIntervalMultiplier, string? phaseId = null, string? abilityId = null, double? expiresAtServerTime = null) { EncounterContractValidation.RequireSafeToken(modifierId, "modifierId", 128); EncounterContractValidation.RequireSafeToken(sourceActionId, "sourceActionId", 128); if (!Enum.IsDefined(typeof(BossModifierLifetime), lifetime)) { throw new ArgumentOutOfRangeException("lifetime"); } if (lifetime == BossModifierLifetime.Permanent) { throw new ArgumentException("Active modifier snapshots represent reversible layers and cannot use Permanent lifetime.", "lifetime"); } if (sourceSequence < 0) { throw new ArgumentOutOfRangeException("sourceSequence"); } ValidateMultiplier(damageMultiplier, "damageMultiplier"); ValidateMultiplier(movementSpeedMultiplier, "movementSpeedMultiplier"); ValidateMultiplier(attackCooldownMultiplier, "attackCooldownMultiplier"); ValidateMultiplier(dashForceMultiplier, "dashForceMultiplier"); ValidateMultiplier(dashIntervalMultiplier, "dashIntervalMultiplier"); if (!damageMultiplier.HasValue && !movementSpeedMultiplier.HasValue && !attackCooldownMultiplier.HasValue && !dashForceMultiplier.HasValue && !dashIntervalMultiplier.HasValue) { throw new ArgumentException("An active modifier must change at least one supported multiplier.", "damageMultiplier"); } if (phaseId != null) { EncounterContractValidation.RequireSafeToken(phaseId, "phaseId", 128); } if (abilityId != null) { EncounterContractValidation.RequireSafeToken(abilityId, "abilityId", 128); } if (expiresAtServerTime.HasValue && (!EncounterContractValidation.IsFinite(expiresAtServerTime.Value) || expiresAtServerTime.Value < 0.0)) { throw new ArgumentOutOfRangeException("expiresAtServerTime"); } if (lifetime == BossModifierLifetime.Timed != expiresAtServerTime.HasValue) { throw new ArgumentException("Timed modifiers require an expiry; other lifetimes cannot carry one.", "expiresAtServerTime"); } if (lifetime == BossModifierLifetime.UntilPhaseExit && phaseId == null) { throw new ArgumentException("UntilPhaseExit modifiers require an owning PhaseId.", "phaseId"); } if (lifetime == BossModifierLifetime.UntilAbilityEnd && (abilityId == null || sourceSequence <= 0)) { throw new ArgumentException("UntilAbilityEnd modifiers require an owning AbilityId and positive source sequence.", "abilityId"); } ModifierId = modifierId; SourceActionId = sourceActionId; SourceSequence = sourceSequence; Lifetime = lifetime; DamageMultiplier = damageMultiplier; MovementSpeedMultiplier = movementSpeedMultiplier; AttackCooldownMultiplier = attackCooldownMultiplier; DashForceMultiplier = dashForceMultiplier; DashIntervalMultiplier = dashIntervalMultiplier; PhaseId = phaseId; AbilityId = abilityId; ExpiresAtServerTime = expiresAtServerTime; } private static void ValidateMultiplier(float? value, string parameterName) { if (value.HasValue && (float.IsNaN(value.Value) || float.IsInfinity(value.Value) || value.Value < 0.01f || value.Value > 100f)) { throw new ArgumentOutOfRangeException(parameterName); } } } internal static class PhaseActionIdentity { internal const int MaximumEncounterIdLength = 256; private const int OrdinalBits = 5; private const uint OrdinalMask = 31u; internal static int CreateActionSeed(int parentSeed, string phaseId, int actionOrdinal) { ValidatePhaseId(phaseId); ValidateActionOrdinal(actionOrdinal); return (int)(CreateScope(parentSeed, phaseId) & 0x7FFFFFE0) | actionOrdinal; } internal static int CreateMinionSeed(int parentSeed, string phaseId, int actionOrdinal, int minionIndex) { ValidatePhaseId(phaseId); ValidateActionOrdinal(actionOrdinal); ValidateMinionIndex(minionIndex); uint num = CreateScope(parentSeed, phaseId); uint num2 = (uint)((actionOrdinal << 5) | minionIndex); return (int)((num & 0x7FFFFC00) | num2); } internal static string CreateMinionEncounterId(string parentEncounterId, string bossId, string phaseId, int actionOrdinal, int minionIndex) { if (parentEncounterId == null) { throw new ArgumentNullException("parentEncounterId"); } if (bossId == null) { throw new ArgumentNullException("bossId"); } ValidatePhaseId(phaseId); ValidateActionOrdinal(actionOrdinal); ValidateMinionIndex(minionIndex); string text = actionOrdinal.ToString(CultureInfo.InvariantCulture); string text2 = minionIndex.ToString(CultureInfo.InvariantCulture); string text3 = parentEncounterId + ":" + phaseId + ":minion:a" + text + ":i" + text2; if (text3.Length <= 256) { return text3; } string text4 = DeterministicIdentityAllocator.Fnv1A(text3).ToString("x8", CultureInfo.InvariantCulture); string text5 = ":minion:" + text4 + ":a" + text + ":i" + text2; return bossId[..Math.Min(bossId.Length, 256 - text5.Length)] + text5; } private static uint CreateScope(int parentSeed, string phaseId) { uint num = ((uint)parentSeed ^ DeterministicIdentityAllocator.Fnv1A(phaseId) ^ 0x9E3779B9u) * 16777619; return num ^ (num >> 16); } private static void ValidatePhaseId(string phaseId) { if (string.IsNullOrEmpty(phaseId)) { throw new ArgumentException("A phase ID is required.", "phaseId"); } } private static void ValidateActionOrdinal(int actionOrdinal) { if (actionOrdinal < 0 || actionOrdinal >= 32 || (uint)actionOrdinal > 31u) { throw new ArgumentOutOfRangeException("actionOrdinal"); } } private static void ValidateMinionIndex(int minionIndex) { if (minionIndex < 0 || minionIndex >= 32 || (uint)minionIndex > 31u) { throw new ArgumentOutOfRangeException("minionIndex"); } } } public static class BossMakerMetadata { public const string ProductName = "BossMakerAPI"; public const string AssemblyName = "BossMakerAPI"; public const string PublicNamespace = "HowToFish.BossMaker"; public const string PluginGuid = "com.skylin3.howtofish.bossmakerapi"; public const string Version = "0.10.3"; public const string TargetFramework = "netstandard2.1"; } public enum BossAbilityTrigger { PhaseEnter, OnSpawn, AfterDelay, Interval, Reactive } public enum BossReactiveRearmPolicy { AfterFalse, AfterCooldownWhileTrue } public enum BossTargetSelectorKind { BossPosition, NearestPlayer, FarthestPlayer, RandomPlayer } public enum BossAbilityGeometryKind { Circle, Ring, Line, Cone } public sealed class BossAbilityTimelineDefinition { public float TelegraphDurationSeconds { get; set; } = 1f; public float ResolveDurationSeconds { get; set; } public float RecoveryDurationSeconds { get; set; } } public sealed class BossTelegraphDefinition { public bool Enabled { get; set; } = true; public Color Color { get; set; } = new Color(1f, 0.15f, 0.05f, 0.65f); public Color BorderColor { get; set; } = new Color(1f, 0.8f, 0.2f, 1f); public float BorderWidth { get; set; } = 0.08f; public float PulseFrequency { get; set; } = 1f; public bool UseColorBlindPattern { get; set; } public AudioClip? AudioClip { get; set; } public float AudioVolume { get; set; } = 1f; } public sealed class BossTargetSelectorDefinition { public BossTargetSelectorKind Kind { get; set; } public float MaximumRange { get; set; } public bool LockPositionAtTelegraph { get; set; } = true; public float LockBeforeResolveSeconds { get; set; } } public abstract class BossAbilityGeometryDefinition { public abstract BossAbilityGeometryKind Kind { get; } public Vector3 LocalOffset { get; set; } = Vector3.zero; } public sealed class CircleGeometryDefinition : BossAbilityGeometryDefinition { public override BossAbilityGeometryKind Kind => BossAbilityGeometryKind.Circle; public float Radius { get; set; } = 4f; } public sealed class RingGeometryDefinition : BossAbilityGeometryDefinition { public override BossAbilityGeometryKind Kind => BossAbilityGeometryKind.Ring; public float InnerRadius { get; set; } = 2f; public float OuterRadius { get; set; } = 5f; } public sealed class LineGeometryDefinition : BossAbilityGeometryDefinition { public override BossAbilityGeometryKind Kind => BossAbilityGeometryKind.Line; public float Length { get; set; } = 8f; public float Width { get; set; } = 1f; } public sealed class ConeGeometryDefinition : BossAbilityGeometryDefinition { public override BossAbilityGeometryKind Kind => BossAbilityGeometryKind.Cone; public float Radius { get; set; } = 7f; public float AngleDegrees { get; set; } = 60f; } public sealed class BossAbilityDefinition { public string Id { get; set; } = string.Empty; public BossAbilityTrigger Trigger { get; set; } public float InitialDelaySeconds { get; set; } public float CooldownSeconds { get; set; } public int MaxRepeats { get; set; } public float Probability { get; set; } = 1f; public BossConditionDefinition? Condition { get; set; } public BossReactiveRearmPolicy ReactiveRearmPolicy { get; set; } public BossAbilityTimelineDefinition Timeline { get; set; } = new BossAbilityTimelineDefinition(); public BossTargetSelectorDefinition TargetSelector { get; set; } = new BossTargetSelectorDefinition(); public BossAbilityGeometryDefinition Geometry { get; set; } = new CircleGeometryDefinition(); public BossTelegraphDefinition Telegraph { get; set; } = new BossTelegraphDefinition(); public IReadOnlyList<BossActionDefinition> Actions { get; set; } = Array.Empty<BossActionDefinition>(); } public enum BossConditionKind { Health, DamageTaken, PlayerDistance, MinionCount, MinionDefeated, AbilityCompleted, TimeInPhase, BossHooked, All, Any, Not } public enum BossNumericComparison { LessThan, LessThanOrEqual, Equal, GreaterThanOrEqual, GreaterThan, NotEqual } public enum BossConditionScope { Encounter, ActivePhase, RollingWindow } public abstract class BossConditionDefinition { public abstract BossConditionKind Kind { get; } } public sealed class BossHealthConditionDefinition : BossConditionDefinition { public override BossConditionKind Kind => BossConditionKind.Health; public BossNumericComparison Comparison { get; set; } = BossNumericComparison.LessThanOrEqual; public float HealthFraction { get; set; } = 0.5f; } public sealed class BossDamageTakenConditionDefinition : BossConditionDefinition { public override BossConditionKind Kind => BossConditionKind.DamageTaken; public BossNumericComparison Comparison { get; set; } = BossNumericComparison.GreaterThanOrEqual; public int Damage { get; set; } = 1; public BossConditionScope Scope { get; set; } = BossConditionScope.ActivePhase; public float WindowSeconds { get; set; } } public sealed class BossPlayerDistanceConditionDefinition : BossConditionDefinition { public override BossConditionKind Kind => BossConditionKind.PlayerDistance; public BossNumericComparison Comparison { get; set; } = BossNumericComparison.LessThanOrEqual; public float Distance { get; set; } = 10f; public int RequiredPlayerCount { get; set; } = 1; } public sealed class BossMinionCountConditionDefinition : BossConditionDefinition { public override BossConditionKind Kind => BossConditionKind.MinionCount; public string? GroupId { get; set; } public BossNumericComparison Comparison { get; set; } = BossNumericComparison.LessThanOrEqual; public int Count { get; set; } } public sealed class BossMinionDefeatedConditionDefinition : BossConditionDefinition { public override BossConditionKind Kind => BossConditionKind.MinionDefeated; public string? GroupId { get; set; } public BossNumericComparison Comparison { get; set; } = BossNumericComparison.GreaterThanOrEqual; public int Count { get; set; } = 1; public BossConditionScope Scope { get; set; } = BossConditionScope.ActivePhase; public float WindowSeconds { get; set; } } public sealed class BossAbilityCompletedConditionDefinition : BossConditionDefinition { public override BossConditionKind Kind => BossConditionKind.AbilityCompleted; public string AbilityId { get; set; } = string.Empty; public BossNumericComparison Comparison { get; set; } = BossNumericComparison.GreaterThanOrEqual; public int Count { get; set; } = 1; public BossConditionScope Scope { get; set; } = BossConditionScope.ActivePhase; public float WindowSeconds { get; set; } } public sealed class BossTimeInPhaseConditionDefinition : BossConditionDefinition { public override BossConditionKind Kind => BossConditionKind.TimeInPhase; public BossNumericComparison Comparison { get; set; } = BossNumericComparison.GreaterThanOrEqual; public float Seconds { get; set; } = 1f; } public sealed class BossHookedConditionDefinition : BossConditionDefinition { public override BossConditionKind Kind => BossConditionKind.BossHooked; public BossHookLifecycleState? RequiredState { get; set; } = BossHookLifecycleState.Hooked; public float MinimumContinuousSeconds { get; set; } } public sealed class BossAllConditionDefinition : BossConditionDefinition { public override BossConditionKind Kind => BossConditionKind.All; public IReadOnlyList<BossConditionDefinition> Conditions { get; set; } = Array.Empty<BossConditionDefinition>(); } public sealed class BossAnyConditionDefinition : BossConditionDefinition { public override BossConditionKind Kind => BossConditionKind.Any; public IReadOnlyList<BossConditionDefinition> Conditions { get; set; } = Array.Empty<BossConditionDefinition>(); } public sealed class BossNotConditionDefinition : BossConditionDefinition { public override BossConditionKind Kind => BossConditionKind.Not; public BossConditionDefinition? Condition { get; set; } } public sealed class BossDefinition { public string Id { get; set; } = string.Empty; public string ContentVersion { get; set; } = "1.0.0"; public string DisplayName { get; set; } = string.Empty; public string Template { get; set; } = string.Empty; public BossVisualDefinition Visual { get; set; } = new BossVisualDefinition(); public BossStatOverrides Stats { get; set; } = new BossStatOverrides(); public IReadOnlyList<BossAbilityDefinition> Abilities { get; set; } = Array.Empty<BossAbilityDefinition>(); public IReadOnlyList<BossPhaseDefinition> Phases { get; set; } = Array.Empty<BossPhaseDefinition>(); public IReadOnlyList<BossRewardDefinition> Rewards { get; set; } = Array.Empty<BossRewardDefinition>(); public RewardPolicy RewardPolicy { get; set; } public BossEncounterDefinition Encounter { get; set; } = new BossEncounterDefinition(); public BossPersistenceDefinition Persistence { get; set; } = new BossPersistenceDefinition(); public BossVanillaIntegrationDefinition VanillaIntegration { get; set; } = new BossVanillaIntegrationDefinition(); public bool ShowBossBar { get; set; } = true; public bool Live { get; internal set; } public string FailureMessage { get; internal set; } = string.Empty; } public sealed class BossVisualDefinition { public GameObject? Prefab { get; set; } public Vector3 LocalPosition { get; set; } = Vector3.zero; public Vector3 LocalEulerAngles { get; set; } = Vector3.zero; public Vector3 LocalScale { get; set; } = Vector3.one; public bool DisableTemplateRenderers { get; set; } public bool RemoveVisualColliders { get; set; } = true; public string AssetIdentity { get; set; } = string.Empty; public string DeclaredAssetDigest { get; set; } = string.Empty; } public sealed class BossStatOverrides { public int? MaxHealth { get; set; } public float? DamageMultiplier { get; set; } public float? MovementSpeedMultiplier { get; set; } public float? AttackCooldownMultiplier { get; set; } public float? DashForceMultiplier { get; set; } public float? DashIntervalMultiplier { get; set; } public float? KnockbackResistanceMultiplier { get; set; } public float? MassMultiplier { get; set; } public float? ScaleMultiplier { get; set; } public float? VanillaCorpseValueMultiplier { get; set; } } public sealed class BossPhaseDefinition { public string Id { get; set; } = string.Empty; public string? DisplayName { get; set; } public float TriggerHealthFraction { get; set; } public IReadOnlyList<BossActionDefinition> Actions { get; set; } = Array.Empty<BossActionDefinition>(); public IReadOnlyList<BossActionDefinition> ExitActions { get; set; } = Array.Empty<BossActionDefinition>(); public IReadOnlyList<BossAbilityDefinition> Abilities { get; set; } = Array.Empty<BossAbilityDefinition>(); public BossPhaseGateDefinition Gate { get; set; } = new BossPhaseGateDefinition(); } public sealed class BossPhaseGateDefinition { public bool Enabled { get; set; } public float MinimumDurationSeconds { get; set; } public float MaximumDurationSeconds { get; set; } = 30f; public string RequiredAbilityId { get; set; } = string.Empty; public bool ProtectFromDefeat { get; set; } } internal static class BossDefinitionSnapshot { private sealed class ConditionReferenceComparer : IEqualityComparer<BossConditionDefinition> { internal static readonly ConditionReferenceComparer Instance = new ConditionReferenceComparer(); public bool Equals(BossConditionDefinition? left, BossConditionDefinition? right) { return left == right; } public int GetHashCode(BossConditionDefinition value) { return RuntimeHelpers.GetHashCode(value); } } internal static BossDefinition Clone(BossDefinition source) { if (source == null) { throw new ArgumentNullException("source"); } return new BossDefinition { Id = source.Id, ContentVersion = source.ContentVersion, DisplayName = source.DisplayName, Template = source.Template, Visual = Clone(source.Visual), Stats = Clone(source.Stats), Abilities = CloneAbilities(source.Abilities), Phases = ClonePhases(source.Phases), Rewards = CloneRewards(source.Rewards, 0), RewardPolicy = source.RewardPolicy, Encounter = Clone(source.Encounter), Persistence = Clone(source.Persistence), VanillaIntegration = Clone(source.VanillaIntegration), ShowBossBar = source.ShowBossBar, Live = source.Live, FailureMessage = source.FailureMessage }; } private static BossPersistenceDefinition Clone(BossPersistenceDefinition source) { if (source == null) { return null; } return new BossPersistenceDefinition { Mode = source.Mode }; } private static BossVanillaIntegrationDefinition Clone(BossVanillaIntegrationDefinition source) { if (source == null) { return null; } return new BossVanillaIntegrationDefinition { Mode = source.Mode, DurationSeconds = source.DurationSeconds }; } internal static BossPhaseDefinition Clone(BossPhaseDefinition source) { if (source == null) { return null; } return new BossPhaseDefinition { Id = source.Id, DisplayName = source.DisplayName, TriggerHealthFraction = source.TriggerHealthFraction, Actions = CloneActions(source.Actions), ExitActions = CloneActions(source.ExitActions), Abilities = CloneAbilities(source.Abilities), Gate = Clone(source.Gate) }; } private static BossPhaseGateDefinition Clone(BossPhaseGateDefinition source) { if (source == null) { return null; } return new BossPhaseGateDefinition { Enabled = source.Enabled, MinimumDurationSeconds = source.MinimumDurationSeconds, MaximumDurationSeconds = source.MaximumDurationSeconds, RequiredAbilityId = source.RequiredAbilityId, ProtectFromDefeat = source.ProtectFromDefeat }; } private static BossEncounterDefinition Clone(BossEncounterDefinition source) { if (source == null) { return null; } BossCatchTriggerDefinition[] array = ((source.CatchTriggers == null) ? null : new BossCatchTriggerDefinition[source.CatchTriggers.Count]); if (source.CatchTriggers != null) { for (int i = 0; i < source.CatchTriggers.Count; i++) { BossCatchTriggerDefinition bossCatchTriggerDefinition = source.CatchTriggers[i]; array[i] = ((bossCatchTriggerDefinition == null) ? null : new BossCatchTriggerDefinition { Id = bossCatchTriggerDefinition.Id, CaughtTemplate = bossCatchTriggerDefinition.CaughtTemplate, Probability = bossCatchTriggerDefinition.Probability, CooldownSeconds = bossCatchTriggerDefinition.CooldownSeconds, MaxActivations = bossCatchTriggerDefinition.MaxActivations, RequiredRod = bossCatchTriggerDefinition.RequiredRod, RequiredBait = bossCatchTriggerDefinition.RequiredBait, ReplaceCatch = bossCatchTriggerDefinition.ReplaceCatch, ResetRodAfterReplacement = bossCatchTriggerDefinition.ResetRodAfterReplacement, IntroductionGraceSeconds = bossCatchTriggerDefinition.IntroductionGraceSeconds, MinimumSpawnDistance = bossCatchTriggerDefinition.MinimumSpawnDistance, MaximumSpawnDistance = bossCatchTriggerDefinition.MaximumSpawnDistance, RequireWater = bossCatchTriggerDefinition.RequireWater, Placement = Clone(bossCatchTriggerDefinition.Placement) }); } } return new BossEncounterDefinition { CatchTriggers = array, MaximumOwnedMinions = source.MaximumOwnedMinions, MaximumOwnedHazards = source.MaximumOwnedHazards, MaximumConcurrentAbilities = source.MaximumConcurrentAbilities, HookStagger = Clone(source.HookStagger), Engagement = Clone(source.Engagement), AquaticRecovery = Clone(source.AquaticRecovery) }; } private static BossEngagementDefinition Clone(BossEngagementDefinition source) { if (source == null) { return null; } return new BossEngagementDefinition { Enabled = source.Enabled, MaximumTargetDistance = source.MaximumTargetDistance, ReengageSpeed = source.ReengageSpeed, MaximumSpeed = source.MaximumSpeed, PollIntervalSeconds = source.PollIntervalSeconds }; } private static BossAquaticRecoveryDefinition Clone(BossAquaticRecoveryDefinition source) { if (source == null) { return null; } return new BossAquaticRecoveryDefinition { Enabled = source.Enabled, MinimumDepth = source.MinimumDepth, IgnoreHorizontalProgress = source.IgnoreHorizontalProgress, MinimumTargetHeightDifference = source.MinimumTargetHeightDifference, StallDurationSeconds = source.StallDurationSeconds, MinimumProgressDistance = source.MinimumProgressDistance, SurfaceClearance = source.SurfaceClearance, MaximumSpeed = source.MaximumSpeed, Acceleration = source.Acceleration, MaximumDurationSeconds = source.MaximumDurationSeconds, RetryCooldownSeconds = source.RetryCooldownSeconds, MaximumLateralDistance = source.MaximumLateralDistance, ProbeIntervalSeconds = source.ProbeIntervalSeconds }; } private static BossVisualDefinition Clone(BossVisualDefinition source) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) if (source == null) { return null; } return new BossVisualDefinition { Prefab = source.Prefab, LocalPosition = source.LocalPosition, LocalEulerAngles = source.LocalEulerAngles, LocalScale = source.LocalScale, DisableTemplateRenderers = source.DisableTemplateRenderers, RemoveVisualColliders = source.RemoveVisualColliders, AssetIdentity = source.AssetIdentity, DeclaredAssetDigest = source.DeclaredAssetDigest }; } private static BossStatOverrides Clone(BossStatOverrides source) { if (source == null) { return null; } return new BossStatOverrides { MaxHealth = source.MaxHealth, DamageMultiplier = source.DamageMultiplier, MovementSpeedMultiplier = source.MovementSpeedMultiplier, AttackCooldownMultiplier = source.AttackCooldownMultiplier, DashForceMultiplier = source.DashForceMultiplier, DashIntervalMultiplier = source.DashIntervalMultiplier, KnockbackResistanceMultiplier = source.KnockbackResistanceMultiplier, MassMultiplier = source.MassMultiplier, ScaleMultiplier = source.ScaleMultiplier, VanillaCorpseValueMultiplier = source.VanillaCorpseValueMultiplier }; } private static BossPhaseDefinition[] ClonePhases(IReadOnlyList<BossPhaseDefinition> source) { if (source == null) { return null; } BossPhaseDefinition[] array = new BossPhaseDefinition[source.Count]; for (int i = 0; i < source.Count; i++) { array[i] = Clone(source[i]); } return array; } private static BossActionDefinition[] CloneActions(IReadOnlyList<BossActionDefinition> source) { if (source == null) { return null; } BossActionDefinition[] array = new BossActionDefinition[source.Count]; for (int i = 0; i < source.Count; i++) { array[i] = CloneAction(source[i]); } return array; } private static BossAbilityDefinition[] CloneAbilities(IReadOnlyList<BossAbilityDefinition> source) { if (source == null) { return null; } BossAbilityDefinition[] array = new BossAbilityDefinition[source.Count]; for (int i = 0; i < source.Count; i++) { BossAbilityDefinition bossAbilityDefinition = source[i]; array[i] = ((bossAbilityDefinition == null) ? null : new BossAbilityDefinition { Id = bossAbilityDefinition.Id, Trigger = bossAbilityDefinition.Trigger, InitialDelaySeconds = bossAbilityDefinition.InitialDelaySeconds, CooldownSeconds = bossAbilityDefinition.CooldownSeconds, MaxRepeats = bossAbilityDefinition.MaxRepeats, Probability = bossAbilityDefinition.Probability, Condition = CloneCondition(bossAbilityDefinition.Condition), ReactiveRearmPolicy = bossAbilityDefinition.ReactiveRearmPolicy, Timeline = Clone(bossAbilityDefinition.Timeline), TargetSelector = Clone(bossAbilityDefinition.TargetSelector), Geometry = CloneGeometry(bossAbilityDefinition.Geometry), Telegraph = Clone(bossAbilityDefinition.Telegraph), Actions = CloneActions(bossAbilityDefinition.Actions) }); } return array; } private static BossAbilityTimelineDefinition Clone(BossAbilityTimelineDefinition source) { if (source == null) { return null; } return new BossAbilityTimelineDefinition { TelegraphDurationSeconds = source.TelegraphDurationSeconds, ResolveDurationSeconds = source.ResolveDurationSeconds, RecoveryDurationSeconds = source.RecoveryDurationSeconds }; } private static BossTargetSelectorDefinition Clone(BossTargetSelectorDefinition source) { if (source == null) { return null; } return new BossTargetSelectorDefinition { Kind = source.Kind, MaximumRange = source.MaximumRange, LockPositionAtTelegraph = source.LockPositionAtTelegraph, LockBeforeResolveSeconds = source.LockBeforeResolveSeconds }; } private static BossTelegraphDefinition Clone(BossTelegraphDefinition source) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) if (source == null) { return null; } return new BossTelegraphDefinition { Enabled = source.Enabled, Color = source.Color, BorderColor = source.BorderColor, BorderWidth = source.BorderWidth, PulseFrequency = source.PulseFrequency, UseColorBlindPattern = source.UseColorBlindPattern, AudioClip = source.AudioClip, AudioVolume = source.AudioVolume }; } private static BossAbilityGeometryDefinition CloneGeometry(BossAbilityGeometryDefinition source) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) if (source == null) { return null; } if (!(source is CircleGeometryDefinition circleGeometryDefinition)) { if (!(source is RingGeometryDefinition ringGeometryDefinition)) { if (!(source is LineGeometryDefinition lineGeometryDefinition)) { if (source is ConeGeometryDefinition coneGeometryDefinition) { return new ConeGeometryDefinition { LocalOffset = coneGeometryDefinition.LocalOffset, Radius = coneGeometryDefinition.Radius, AngleDegrees = coneGeometryDefinition.AngleDegrees }; } return source; } return new LineGeometryDefinition { LocalOffset = lineGeometryDefinition.LocalOffset, Length = lineGeometryDefinition.Length, Width = lineGeometryDefinition.Width }; } return new RingGeometryDefinition { LocalOffset = ringGeometryDefinition.LocalOffset, InnerRadius = ringGeometryDefinition.InnerRadius, OuterRadius = ringGeometryDefinition.OuterRadius }; } return new CircleGeometryDefinition { LocalOffset = circleGeometryDefinition.LocalOffset, Radius = circleGeometryDefinition.Radius }; } private static BossActionDefinition CloneAction(BossActionDefinition source) { //IL_026f: Unknown result type (might be due to invalid IL or missing references) //IL_027c: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Unknown result type (might be due to invalid IL or missing references) if (source == null) { return null; } if (!(source is StatMultiplierAction statMultiplierAction)) { if (!(source is SpawnMinionsAction spawnMinionsAction)) { if (!(source is ToggleVisualNodeAction toggleVisualNodeAction)) { if (!(source is SetVisualScaleAction setVisualScaleAction)) { if (!(source is PlayAudioAction playAudioAction)) { if (!(source is SpawnVisualEffectAction spawnVisualEffectAction)) { if (!(source is AreaDamageAction areaDamageAction)) { if (!(source is ForceAction forceAction)) { if (source is DashAction dashAction) { return new DashAction { Id = dashAction.Id, Impulse = dashAction.Impulse, VerticalImpulse = dashAction.VerticalImpulse, RetainedVelocityFraction = dashAction.RetainedVelocityFraction, MaximumSpeed = dashAction.MaximumSpeed, MaximumTravelDistance = dashAction.MaximumTravelDistance, StoppingDistance = dashAction.StoppingDistance, BrakeDurationSeconds = dashAction.BrakeDurationSeconds, ExitVelocityFraction = dashAction.ExitVelocityFraction }; } return source; } return new ForceAction { Id = forceAction.Id, Mode = forceAction.Mode, Magnitude = forceAction.Magnitude, DurationSeconds = forceAction.DurationSeconds, VerticalTolerance = forceAction.VerticalTolerance, VerticalMultiplier = forceAction.VerticalMultiplier, MaximumVelocityChange = forceAction.MaximumVelocityChange }; } return new AreaDamageAction { Id = areaDamageAction.Id, Damage = areaDamageAction.Damage, DurationSeconds = areaDamageAction.DurationSeconds, TickIntervalSeconds = areaDamageAction.TickIntervalSeconds, MaxHitsPerTarget = areaDamageAction.MaxHitsPerTarget, VerticalTolerance = areaDamageAction.VerticalTolerance }; } return new SpawnVisualEffectAction { Id = spawnVisualEffectAction.Id, Prefab = spawnVisualEffectAction.Prefab, Anchor = spawnVisualEffectAction.Anchor, ParentToBoss = spawnVisualEffectAction.ParentToBoss, AlignToAbilityDirection = spawnVisualEffectAction.AlignToAbilityDirection, LocalOffset = spawnVisualEffectAction.LocalOffset, LocalEulerAngles = spawnVisualEffectAction.LocalEulerAngles, LocalScale = spawnVisualEffectAction.LocalScale, Lifetime = spawnVisualEffectAction.Lifetime }; } return new PlayAudioAction { Id = playAudioAction.Id, Clip = playAudioAction.Clip, Volume = playAudioAction.Volume, SpatialBlend = playAudioAction.SpatialBlend }; } return new SetVisualScaleAction { Id = setVisualScaleAction.Id, ScaleMultiplier = setVisualScaleAction.ScaleMultiplier, Persistent = setVisualScaleAction.Persistent }; } return new ToggleVisualNodeAction { Id = toggleVisualNodeAction.Id, RelativeTransformPath = toggleVisualNodeAction.RelativeTransformPath, Enabled = toggleVisualNodeAction.Enabled, Persistent = toggleVisualNodeAction.Persistent }; } return new SpawnMinionsAction { Id = spawnMinionsAction.Id, TemplateName = spawnMinionsAction.TemplateName, RegisteredBossId = spawnMinionsAction.RegisteredBossId, Count = spawnMinionsAction.Count, SpawnRadius = spawnMinionsAction.SpawnRadius, MaxAliveFromThisAction = spawnMinionsAction.MaxAliveFromThisAction, GroupId = spawnMinionsAction.GroupId, CleanupPolicy = spawnMinionsAction.CleanupPolicy, LifetimeSeconds = spawnMinionsAction.LifetimeSeconds, Placement = Clone(spawnMinionsAction.Placement), Formation = Clone(spawnMinionsAction.Formation) }; } return new StatMultiplierAction { Id = statMultiplierAction.Id, DamageMultiplier = statMultiplierAction.DamageMultiplier, MovementSpeedMultiplier = statMultiplierAction.MovementSpeedMultiplier, AttackCooldownMultiplier = statMultiplierAction.AttackCooldownMultiplier, DashForceMultiplier = statMultiplierAction.DashForceMultiplier, DashIntervalMultiplier = statMultiplierAction.DashIntervalMultiplier, ModifierId = statMultiplierAction.ModifierId, Lifetime = statMultiplierAction.Lifetime, DurationSeconds = statMultiplierAction.DurationSeconds }; } private static BossPlacementDefinition? Clone(BossPlacementDefinition? source) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) if (source == null) { return null; } return new BossPlacementDefinition { Mode = source.Mode, LocalOffset = source.LocalOffset, SearchRadius = source.SearchRadius, VerticalSearchDistance = source.VerticalSearchDistance, ClearanceRadius = source.ClearanceRadius, UseBossBodyClearance = source.UseBossBodyClearance, MaximumAttempts = source.MaximumAttempts, SocketPath = source.SocketPath, FailurePolicy = source.FailurePolicy }; } private static BossFormationDefinition? Clone(BossFormationDefinition? source) { if (source == null) { return null; } string[] array; if (source.SocketPaths == null) { array = null; } else { array = new string[source.SocketPaths.Count]; for (int i = 0; i < array.Length; i++) { array[i] = source.SocketPaths[i]; } } return new BossFormationDefinition { Kind = source.Kind, Radius = source.Radius, Spacing = source.Spacing, AngleDegrees = source.AngleDegrees, YawOffsetDegrees = source.YawOffsetDegrees, FaceCenter = source.FaceCenter, SocketPaths = array }; } private static BossHookStaggerDefinition Clone(BossHookStaggerDefinition source) { if (source == null) { return null; } return new BossHookStaggerDefinition { Enabled = source.Enabled, MinimumSafeTension = source.MinimumSafeTension, MaximumSafeTension = source.MaximumSafeTension, ProgressPerSecond = source.ProgressPerSecond, DecayPerSecond = source.DecayPerSecond, StaggerThreshold = source.StaggerThreshold, StaggerDurationSeconds = source.StaggerDurationSeconds, RecoveryDurationSeconds = source.RecoveryDurationSeconds, MaximumSimultaneousHooks = source.MaximumSimultaneousHooks, InterruptActiveAbilityOnStagger = source.InterruptActiveAbilityOnStagger }; } private static BossConditionDefinition? CloneCondition(BossConditionDefinition? source) { if (source == null) { return null; } return CloneCondition(source, 0, new HashSet<BossConditionDefinition>(ConditionReferenceComparer.Instance)); } private static BossConditionDefinition CloneCondition(BossConditionDefinition source, int depth, ISet<BossConditionDefinition> recursionStack) { if (depth > 8 || !recursionStack.Add(source)) { throw new ArgumentException("Condition graph is cyclic or exceeds the supported nesting depth.", "source"); } try { if (!(source is BossHealthConditionDefinition bossHealthConditionDefinition)) { if (!(source is BossDamageTakenConditionDefinition bossDamageTakenConditionDefinition)) { if (!(source is BossPlayerDistanceConditionDefinition bossPlayerDistanceConditionDefinition)) { if (!(source is BossMinionCountConditionDefinition bossMinionCountConditionDefinition)) { if (!(source is BossMinionDefeatedConditionDefinition bossMinionDefeatedConditionDefinition)) { if (!(source is BossAbilityCompletedConditionDefinition bossAbilityCompletedConditionDefinition)) { if (!(source is BossTimeInPhaseConditionDefinition bossTimeInPhaseConditionDefinition)) { if (!(source is BossHookedConditionDefinition bossHookedConditionDefinition)) { if (!(source is BossAllConditionDefinition bossAllConditionDefinition)) { if (!(source is BossAnyConditionDefinition bossAnyConditionDefinition)) { if (source is BossNotConditionDefinition bossNotConditionDefinition) { return new BossNotConditionDefinition { Condition = ((bossNotConditionDefinition.Condition == null) ? null : CloneCondition(bossNotConditionDefinition.Condition, depth + 1, recursionStack)) }; } return source; } return new BossAnyConditionDefinition { Conditions = CloneConditions(bossAnyConditionDefinition.Conditions, depth + 1, recursionStack) }; } return new BossAllConditionDefinition { Conditions = CloneConditions(bossAllConditionDefinition.Conditions, depth + 1, recursionStack) }; } return new BossHookedConditionDefinition { RequiredState = bossHookedConditionDefinition.RequiredState, MinimumContinuousSeconds = bossHookedConditionDefinition.MinimumContinuousSeconds }; } return new BossTimeInPhaseConditionDefinition { Comparison = bossTimeInPhaseConditionDefinition.Comparison, Seconds = bossTimeInPhaseConditionDefinition.Seconds }; } return new BossAbilityCompletedConditionDefinition { AbilityId = bossAbilityCompletedConditionDefinition.AbilityId, Comparison = bossAbilityCompletedConditionDefinition.Comparison, Count = bossAbilityCompletedConditionDefinition.Count, Scope = bossAbilityCompletedConditionDefinition.Scope, WindowSeconds = bossAbilityCompletedConditionDefinition.WindowSeconds }; } return new BossMinionDefeatedConditionDefinition { GroupId = bossMinionDefeatedConditionDefinition.GroupId, Comparison = bossMinionDefeatedConditionDefinition.Comparison, Count = bossMinionDefeatedConditionDefinition.Count, Scope = bossMinionDefeatedConditionDefinition.Scope, WindowSeconds = bossMinionDefeatedConditionDefinition.WindowSeconds }; } return new BossMinionCountConditionDefinition { GroupId = bossMinionCountConditionDefinition.GroupId, Comparison = bossMinionCountConditionDefinition.Comparison, Count = bossMinionCountConditionDefinition.Count }; } return new BossPlayerDistanceConditionDefinition { Comparison = bossPlayerDistanceConditionDefinition.Comparison, Distance = bossPlayerDistanceConditionDefinition.Distance, RequiredPlayerCount = bossPlayerDistanceConditionDefinition.RequiredPlayerCount }; } return new BossDamageTakenConditionDefinition { Comparison = bossDamageTakenConditionDefinition.Comparison, Damage = bossDamageTakenConditionDefinition.Damage, Scope = bossDamageTakenConditionDefinition.Scope, WindowSeconds = bossDamageTakenConditionDefinition.WindowSeconds }; } return new BossHealthConditionDefinition { Comparison = bossHealthConditionDefinition.Comparison, HealthFraction = bossHealthConditionDefinition.HealthFraction }; } finally { recursionStack.Remove(source); } } private static BossConditionDefinition[] CloneConditions(IReadOnlyList<BossConditionDefinition> source, int depth, ISet<BossConditionDefinition> recursionStack) { if (source == null) { return null; } BossConditionDefinition[] array = new BossConditionDefinition[source.Count]; for (int i = 0; i < array.Length; i++) { array[i] = ((source[i] == null) ? null : CloneCondition(source[i], depth, recursionStack)); } return array; } private static BossRewardDefinition[] CloneRewards(IReadOnlyList<BossRewardDefinition> source, int depth) { if (source == null) { return null; } BossRewardDefinition[] array = new BossRewardDefinition[source.Count]; for (int i = 0; i < source.Count; i++) { array[i] = CloneReward(source[i], depth); } return array; } private static BossRewardDefinition CloneReward(BossRewardDefinition source, int depth) { if (source == null) { return null; } if (depth > 8) { return source; } if (!(source is VanillaItemReward vanillaItemReward)) { if (!(source is MoneyReward moneyReward)) { if (!(source is MultipleReward multipleReward)) { if (!(source is WeightedReward weightedReward)) { if (source is CustomItemReward customItemReward) { return new CustomItemReward { ProviderId = customItemReward.ProviderId, ItemId = customItemReward.ItemId, Count = customItemReward.Count }; } return source; } WeightedRewardEntry[] array = ((weightedReward.Entries == null) ? null : new WeightedRewardEntry[weightedReward.Entries.Count]); if (weightedReward.Entries != null) { for (int i = 0; i < weightedReward.Entries.Count; i++) { WeightedRewardEntry weightedRewardEntry = weightedReward.Entries[i]; array[i] = ((weightedRewardEntry == null) ? null : new WeightedRewardEntry { Weight = weightedRewardEntry.Weight, Reward = ((weightedRewardEntry.Reward == null) ? null : CloneReward(weightedRewardEntry.Reward, depth + 1)) }); } } return new WeightedReward { Rolls = weightedReward.Rolls, Entries = array }; } return new MultipleReward { Rewards = CloneRewards(multipleReward.Rewards, depth + 1) }; } return new MoneyReward { Amount = moneyReward.Amount }; } return new VanillaItemReward { ItemName = vanillaItemReward.ItemName, Count = vanillaItemReward.Count, SpawnRadius = vanillaItemReward.SpawnRadius }; } } public static class BossDefinitionValidator { private sealed class ConditionReferenceComparer : IEqualityComparer<BossConditionDefinition> { internal static readonly ConditionReferenceComparer Instance = new ConditionReferenceComparer(); public bool Equals(BossConditionDefinition? left, BossConditionDefinition? right) { return left == right; } public int GetHashCode(BossConditionDefinition value) { return RuntimeHelpers.GetHashCode(value); } } public const int MaximumPhases = 32; public const int MaximumActionsPerPhase = 32; public const int MaximumAbilitiesPerPhase = 32; public const int MaximumAbilitiesPerDefinition = 128; public const int MaximumActionsPerAbility = 32; public const int MaximumAbilityRepeats = 1024; public const int MaximumCatchTriggers = 32; public const float MaximumCatchIntroductionGraceSeconds = 30f; public const int MaximumCatchActivations = 100000; public const int MaximumMinionsPerAction = 32; public const int MaximumConditionNodesPerAbility = 32; public const int MaximumConditionNodesPerDefinition = 256; public const int MaximumConditionNestingDepth = 8; public const int MaximumConditionChildren = 16; public const int MaximumConditionCounter = 1000000; public const int MaximumConditionPlayerCount = 64; public const int MaximumFormationSockets = 32; public const int MaximumPlacementAttempts = 32; public const int MaximumSimultaneousHooks = 8; public const int MaximumActiveModifiers = 64; public const int MaximumActionResultCount = 1024; public const int MaximumValidationIssues = 512; public const int MaximumDeclaredActiveMinions = 128; public const int MaximumTopLevelRewards = 64; public const int MaximumRewardChildren = 64; public const int MaximumRewardRolls = 64; public const int MaximumRewardNodes = 256; public const int MaximumRewardItems = 256; public const int MaximumRewardNestingDepth = 8; public const int MaximumHealth = 10000000; public const float MinimumMultiplier = 0.01f; public const float MaximumMultiplier = 100f; public const float MaximumSpawnRadius = 1000f; public const float MaximumAudioVolume = 4f; public const float MaximumVisualEffectLifetime = 300f; public const float MaximumVisualOffset = 100000f; public const float MaximumVisualScale = 1000f; public const float MaximumAbilityDuration = 3600f; public const float MaximumAbilityCooldown = 86400f; public const float MinimumVanillaBossDuration = 1f; public const float MaximumVanillaBossDuration = 86400f; public const float MaximumAbilityRange = 10000f; public const float MaximumGeometryExtent = 1000f; public const float MaximumForceMagnitude = 1000f; public const float MaximumVelocityChange = 1000f; public const float MaximumDashBrakeDuration = 10f; public const float MaximumEngagementPollInterval = 10f; public const float MinimumAquaticRecoveryDepth = 0.5f; public const float MaximumAquaticRecoveryDepth = 50f; public const float MinimumAquaticRecoveryTargetHeightDifference = 0.25f; public const float MaximumAquaticRecoveryTargetHeightDifference = 50f; public const float MinimumAquaticRecoveryStallDuration = 0.25f; public const float MaximumAquaticRecoveryStallDuration = 30f; public const float MinimumAquaticRecoveryProgressDistance = 0.05f; public const float MaximumAquaticRecoveryProgressDistance = 10f; public const float MinimumAquaticRecoverySurfaceClearance = 0.25f; public const float MaximumAquaticRecoverySurfaceClearance = 10f; public const float MinimumAquaticRecoverySpeed = 0.5f; public const float MaximumAquaticRecoverySpeed = 12f; public const float MinimumAquaticRecoveryAcceleration = 0.5f; public const float MaximumAquaticRecoveryAcceleration = 30f; public const float MinimumAquaticRecoveryDuration = 0.25f; public const float MaximumAquaticRecoveryDuration = 30f; public const float MinimumAquaticRecoveryRetryCooldown = 0.5f; public const float MaximumAquaticRecoveryRetryCooldown = 120f; public const float MinimumAquaticRecoveryLateralDistance = 0.5f; public const float MaximumAquaticRecoveryLateralDistance = 30f; public const float MinimumAquaticRecoveryProbeInterval = 0.1f; public const float MaximumAquaticRecoveryProbeInterval = 1f; public const float MaximumHookRate = 100f; public const int MaximumAreaDamage = 10000000; public const int MaximumHitsPerTarget = 1024; public const int MaximumOwnedMinionBudget = 256; public const int MaximumOwnedHazardBudget = 256; public const int MaximumConcurrentAbilityBudget = 1; public const int MaximumEncounterExecutionClaims = 4096; public const int MaximumDisplayNameLength = 256; public const int MaximumTemplateNameLength = 256; public const int MaximumPhaseIdLength = 128; public const int MaximumAssetIdentityLength = 1024; public const int MaximumRelativePathLength = 1024; public const int MaximumRewardIdentifierLength = 256; public const int MaximumAbilityIdLength = 128; public const int MaximumCatchTriggerIdLength = 128; public const int MaximumCatchFilterLength = 256; public const int MaximumMinionGroupIdLength = 128; public const int MaximumActionIdLength = 128; public const int MaximumModifierIdLength = 128; public const int MaximumResultMessageLength = 1024; private static readonly Regex NamespacedId = new Regex("^[a-z0-9][a-z0-9_-]*(?:\\.[a-z0-9][a-z0-9_-]*)+$", RegexOptions.CultureInvariant); private static readonly Regex SemanticVersion = new Regex("^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$", RegexOptions.CultureInvariant); public static bool IsValidBossId(string? id) { if (!string.IsNullOrEmpty(id) && id.Length <= 128) { return NamespacedId.IsMatch(id); } return false; } public static BossValidationReport Analyze(BossDefinition definition) { if (definition == null) { throw new ArgumentNullException("definition"); } IReadOnlyList<string> readOnlyList = ValidateLegacyMessages(definition); List<BossValidationIssue> list = new List<BossValidationIssue>(); for (int i = 0; i < readOnlyList.Count; i++) { if (list.Count >= 512) { break; } AddIssue(list, "legacy.definition_invalid", "$", readOnlyList[i]); } ValidateVersion4(definition, list); if (list.Count == 0) { try { if (!BossManifestBuilder.TryValidateDefinitionWireBudget(definition, out string error)) { AddIssue(list, "manifest.wire_budget_exceeded", "$", error); } } catch (Exception ex) { AddIssue(list, "manifest.encoding_failed", "$", "Definition could not be encoded safely: " + ex.GetType().Name + ": " + ex.Message); } } TrimIssueBudget(list); return new BossValidationReport(list); } public static IReadOnlyList<string> Validate(BossDefinition definition) { return Analyze(definition).Errors.Select((BossValidationIssue issue) => issue.Message).ToArray(); } private static IReadOnlyList<string> ValidateLegacyMessages(BossDefinition definition) { //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_0204: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) if (definition == null) { throw new ArgumentNullException("definition"); } List<string> list = new List<string>(); if (!IsValidBossId(definition.Id)) { list.Add("Id must be a lowercase namespaced identifier using letters, digits, '.', '-' or '_'."); } if (string.IsNullOrWhiteSpace(definition.ContentVersion) || definition.ContentVersion.Length > 128 || !SemanticVersion.IsMatch(definition.ContentVersion)) { list.Add("ContentVersion must be a semantic version such as 1.0.0 or 1.0.0-beta.1."); } if (string.IsNullOrWhiteSpace(definition.DisplayName) || definition.DisplayName.Length > 256) { list.Add($"DisplayName is required and cannot exceed {256} characters."); } if (string.IsNullOrWhiteSpace(definition.Template) || definition.Template.Length > 256) { list.Add($"Template is required and cannot exceed {256} characters."); } if (definition.Visual == null) { list.Add("Visual cannot be null."); } if (definition.Stats == null) { list.Add("Stats cannot be null."); } if (definition.Persistence == null) { list.Add("Persistence cannot be null."); } else if (!Enum.IsDefined(typeof(BossPersistenceMode), definition.Persistence.Mode)) { list.Add("Persistence Mode is not a supported value."); } if (definition.Visual != null) { if (definition.Visual.AssetIdentity != null && definition.Visual.AssetIdentity.Length > 1024) { list.Add($"Visual AssetIdentity cannot exceed {1024} characters."); } if (!string.IsNullOrEmpty(definition.Visual.DeclaredAssetDigest) && !BossManifestDigest.TryNormalizeSha256(definition.Visual.DeclaredAssetDigest, out string _)) { list.Add("Visual DeclaredAssetDigest must be a 64-character SHA-256 hexadecimal digest, optionally prefixed with 'sha256:'."); } if (!IsFinite(definition.Visual.LocalPosition) || !IsFinite(definition.Visual.LocalEulerAngles) || !IsFinite(definition.Visual.LocalScale)) { list.Add("Visual position, rotation and scale must contain only finite values."); } else { if (!IsWithinMagnitude(definition.Visual.LocalPosition, 100000f)) { list.Add($"Visual local position components cannot exceed {100000f} in magnitude."); } if (!IsWithinMagnitude(definition.Visual.LocalScale, 1000f)) { list.Add($"Visual local scale components cannot exceed {1000f} in magnitude."); } } } ValidateStats(definition.Stats, list); if (!Enum.IsDefined(typeof(RewardPolicy), definition.RewardPolicy)) { list.Add("RewardPolicy is not a supported value."); } ValidateVanillaIntegration(definition.VanillaIntegration, list); ValidateEncounter(definition.Encounter, list); HashSet<string> abilityIds = new HashSet<string>(StringComparer.Ordinal); long declaredActiveMinions = 0L; long declaredExecutionClaims = 1L; long num = 0L; if (definition.Abilities == null) { list.Add("Abilities cannot be null; use an empty collection."); } else if (definition.Abilities.Count > 32) { list.Add($"A boss may declare at most {32} global abilities."); } IReadOnlyList<BossAbilityDefinition> readOnlyList = definition.Abilities ?? Array.Empty<BossAbilityDefinition>(); num += readOnlyList.Count; for (int i = 0; i < Math.Min(readOnlyList.Count, 32); i++) { ValidateAbility(readOnlyList[i], "global", abilityIds, list, ref declaredActiveMinions, ref declaredExecutionClaims, isGlobal: true); } HashSet<float> hashSet = new HashSet<float>(); HashSet<string> hashSet2 = new HashSet<string>(StringComparer.Ordinal); if (definition.Phases == null) { list.Add("Phases cannot be null; use an empty collection."); } else if (definition.Phases.Count > 32) { list.Add($"A boss may declare at most {32} phases."); } IReadOnlyList<BossPhaseDefinition> readOnlyList2 = definition.Phases ?? Array.Empty<BossPhaseDefinition>(); for (int j = 0; j < Math.Min(readOnlyList2.Count, 32); j++) { BossPhaseDefinition phase = readOnlyList2[j]; if (phase == null) { list.Add("Phases cannot contain null entries."); continue; } if (!EncounterContractValidation.IsSafeToken(phase.Id, 128)) { list.Add($"Every phase requires a normalized, control-free Id no longer than {128} characters."); } else if (!hashSet2.Add(phase.Id)) { list.Add("Duplicate phase Id '" + phase.Id + "'."); } if (!IsFinite(phase.TriggerHealthFraction) || phase.TriggerHealthFraction < 0f || phase.TriggerHealthFraction > 1f) { list.Add("Phase '" + phase.Id + "' threshold must be between 0 and 1."); } else if (!hashSet.Add(phase.TriggerHealthFraction)) { list.Add($"Duplicate phase threshold {phase.TriggerHealthFraction}."); } if (phase.Actions == null) { list.Add("Phase '" + phase.Id + "' Actions cannot be null; use an empty collection."); } else if (phase.Actions.Count > 32) { list.Add($"Phase '{phase.Id}' may declare at most {32} actions."); } IReadOnlyList<BossActionDefinition> readOnlyList3 = phase.Actions ?? Array.Empty<BossActionDefinition>(); int num2 = Math.Min(readOnlyList3.Count, 32); declaredExecutionClaims += num2; for (int k = 0; k < num2; k++) { ValidateAction(readOnlyList3[k], phase.Id, list, ref declaredActiveMinions, isAbilityAction: false); } IReadOnlyList<BossActionDefinition> readOnlyList4 = phase.ExitActions ?? Array.Empty<BossActionDefinition>(); int num3 = Math.Min(readOnlyList4.Count, 32); declaredExecutionClaims += num3; for (int l = 0; l < num3; l++) { if (readOnlyList4[l] is SpawnMinionsAction { MaxAliveFromThisAction: >0 } spawnMinionsAction) { declaredActiveMinions = SaturatingAdd(declaredActiveMinions, spawnMinionsAction.MaxAliveFromThisAction); } } if (phase.Abilities == null) { list.Add("Phase '" + phase.Id + "' Abilities cannot be null; use an empty collection."); } else if (phase.Abilities.Count > 32) { list.Add($"Phase '{phase.Id}' may declare at most {32} abilities."); } IReadOnlyList<BossAbilityDefinition> readOnlyList5 = phase.Abilities ?? Array.Empty<BossAbilityDefinition>(); num += readOnlyList5.Count; for (int m = 0; m < Math.Min(readOnlyList5.Count, 32); m++) { ValidateAbility(readOnlyList5[m], phase.Id, abilityIds, list, ref declaredActiveMinions, ref declaredExecutionClaims, isGlobal: false); } ValidatePhaseGate(phase, readOnlyList5, !readOnlyList2.Any((BossPhaseDefinition candidate) => candidate != null && IsFinite(candidate.TriggerHealthFraction) && candidate.TriggerHealthFraction < phase.TriggerHealthFraction), list); } if (num > 128) { list.Add($"A boss may declare at most {128} abilities across its global and phase collections."); } if (declaredActiveMinions > 128) { list.Add($"A boss may declare at most {128} simultaneously active minions across all phase actions."); } if (definition.Encounter != null && definition.Encounter.MaximumOwnedMinions >= 0 && declaredActiveMinions > definition.Encounter.MaximumOwnedMinions) { list.Add($"Declared active minions ({declaredActiveMinions}) exceed the encounter MaximumOwnedMinions budget ({definition.Encounter.MaximumOwnedMinions})."); } if (definition.Rewards == null) { list.Add("Rewards cannot be null; use an empty collection."); } else if (definition.Rewards.Count > 64) { list.Add($"A boss may declare at most {64} top-level rewards."); } IReadOnlyList<BossRewardDefinition> readOnlyList6 = definition.Rewards ?? Array.Empty<BossRewardDefinition>(); declaredExecutionClaims += Math.Min(readOnlyList6.Count, 64); if (definition.RewardPolicy == RewardPolicy.PreserveVanilla && readOnlyList6.Count != 0) { list.Add("RewardPolicy PreserveVanilla requires Rewards to be empty so configured rewards are not silently ignored."); } int nodes = 0; long num4 = 0L; for (int num5 = 0; num5 < Math.Min(readOnlyList6.Count, 64); num5++) { num4 = SaturatingAdd(num4, ValidateReward(readOnlyList6[num5], list, 0, ref nodes)); } if (nodes > 256) { list.Add($"A boss reward graph may contain at most {256} nodes."); } if (num4 > 256) { list.Add($"A boss may spawn at most {256} reward items in the worst case."); } if (declaredExecutionClaims > 4096) { list.Add($"A boss may require at most {4096} exactly-once execution claims across ability actions, rewards and cleanup; " + $"this definition can require {declaredExecutionClaims} in the worst case."); } return list; } internal static IReadOnlyList<BossPhaseDefinition> OrderPhases(IEnumerable<BossPhaseDefinition>? phases) { return (phases ?? Array.Empty<BossPhaseDefinition>()).OrderByDescending((BossPhaseDefinition x) => x.TriggerHealthFraction).ThenBy<BossPhaseDefinition, string>((BossPhaseDefinition x) => x.Id, StringComparer.Ordinal).ToArray(); } private static void ValidateStats(BossStatOverrides? stats, ICollection<string> errors) { if (stats != null) { if (stats.MaxHealth.HasValue && (stats.MaxHealth <= 0 || stats.MaxHealth > 10000000)) { errors.Add($"MaxHealth must be between 1 and {10000000}."); } ValidateMultiplier(stats.DamageMultiplier, "DamageMultiplier", errors); ValidateMultiplier(stats.MovementSpeedMultiplier, "MovementSpeedMultiplier", errors); ValidateMultiplier(stats.AttackCooldownMultiplier, "AttackCooldownMultiplier", errors); ValidateMultiplier(stats.DashForceMultiplier, "DashForceMultiplier", errors); ValidateMultiplier(stats.DashIntervalMultiplier, "DashIntervalMultiplier", errors); ValidateMultiplier(stats.KnockbackResistanceMultiplier, "KnockbackResistanceMultiplier", errors); ValidateMultiplier(stats.MassMultiplier, "MassMultiplier", errors); ValidateMultiplier(stats.ScaleMultiplier, "ScaleMultiplier", errors); ValidateMultiplier(stats.VanillaCorpseValueMultiplier, "VanillaCorpseValueMultiplier", errors); } } private static void ValidateVanillaIntegration(BossVanillaIntegrationDefinition? integration, ICollection<string> errors) { if (integration == null) { errors.Add("VanillaIntegration cannot be null."); } else if (!Enum.IsDefined(typeof(BossVanillaMode), integration.Mode)) { errors.Add("VanillaIntegration Mode is not a supported value."); } else if (integration.Mode == BossVanillaMode.TemplateDefault) { if (integration.DurationSeconds.HasValue) { errors.Add("VanillaIntegration DurationSeconds must be null when Mode is TemplateDefault."); } } else if (!integration.DurationSeconds.HasValue) { errors.Add("VanillaIntegration DurationSeconds is required for VanillaMiniBoss and VanillaBoss modes."); } else if (!IsFinite(integration.DurationSeconds.Value) || integration.DurationSeconds.Value < 1f || integration.DurationSeconds.Value > 86400f) { errors.Add($"VanillaIntegration DurationSeconds must be a finite value between {1f} and {86400f}."); } } private static void ValidateEncounter(BossEncounterDefinition? encounter, ICollection<string> errors) { if (encounter == null) { errors.Add("Encounter cannot be null."); return; } if (encounter.MaximumOwnedMinions < 0 || encounter.MaximumOwnedMinions > 256) { errors.Add($"Encounter MaximumOwnedMinions must be between 0 and {256}."); } if (encounter.MaximumOwnedHazards < 0 || encounter.MaximumOwnedHazards > 256) { errors.Add($"Encounter MaximumOwnedHazards must be between 0 and {256}."); } if (encounter.MaximumConcurrentAbilities != 1) { errors.Add("Encounter MaximumConcurrentAbilities must be 1 because snapshots expose one active ability."); } ValidateEngagement(encounter.Engagement, errors); ValidateAquaticRecovery(encounter.AquaticRecovery, errors); if (encounter.CatchTriggers == null) { errors.Add("Encounter CatchTriggers cannot be null; use an empty collection."); return; } if (encounter.CatchTriggers.Count > 32) { errors.Add($"An encounter may declare at most {32} catch triggers."); } HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); for (int i = 0; i < Math.Min(encounter.CatchTriggers.Count, 32); i++) { BossCatchTriggerDefinition bossCatchTriggerDefinition = encounter.CatchTriggers[i]; if (bossCatchTriggerDefinition == null) { errors.Add("Encounter CatchTriggers cannot contain null entries."); continue; } if (!EncounterContractValidation.IsSafeToken(bossCatchTriggerDefinition.Id, 128)) { errors.Add($"Every catch trigger requires a normalized, control-free Id no longer than {128} characters."); } else if (!hashSet.Add(bossCatchTriggerDefinition.Id)) { errors.Add("Duplicate catch trigger Id '" + bossCatchTriggerDefinition.Id + "'."); } if (string.IsNullOrWhiteSpace(bossCatchTriggerDefinition.CaughtTemplate) || bossCatchTriggerDefinition.CaughtTemplate.Length > 256) { errors.Add($"Catch trigger '{bossCatchTriggerDefinition.Id}' requires a caught template no longer than {256} characters."); } if (!IsFinite(bossCatchTriggerDefinition.Probability) || bossCatchTriggerDefinition.Probability < 0f || bossCatchTriggerDefinition.Probability > 1f) { errors.Add("Catch trigger '" + bossCatchTriggerDefinition.Id + "' Probability must be between 0 and 1."); } if (!IsFinite(bossCatchTriggerDefinition.CooldownSeconds) || bossCatchTriggerDefinition.CooldownSeconds < 0f || bossCatchTriggerDefinition.CooldownSeconds > 86400f) { errors.Add($"Catch trigger '{bossCatchTriggerDefinition.Id}' CooldownSeconds must be between 0 and {86400f}."); } if (bossCatchTriggerDefinition.MaxActivations < 0 || bossCatchTriggerDefinition.MaxActivations > 100000) { errors.Add($"Catch trigger '{bossCatchTriggerDefinition.Id}' MaxActivations must be between 0 and {100000}."); } ValidateOptionalFilter(bossCatchTriggerDefinition.RequiredRod, "RequiredRod", bossCatchTriggerDefinition.Id, errors); ValidateOptionalFilter(bossCatchTriggerDefinition.RequiredBait, "RequiredBait", bossCatchTriggerDefinition.Id, errors); if (bossCatchTriggerDefinition.ResetRodAfterReplacement && !bossCatchTriggerDefinition.ReplaceCatch) { errors.Add("Catch trigger '" + bossCatchTriggerDefinition.Id + "' ResetRodAfterReplacement requires ReplaceCatch."); } BossPlacementDefinition? placement = bossCatchTriggerDefinition.Placement; if (placement != null && placement.UseBossBodyClearance && (!bossCatchTriggerDefinition.ReplaceCatch || !bossCatchTriggerDefinition.RequireWater)) { errors.Add("Catch trigger '" + bossCatchTriggerDefinition.Id + "' body-clearance placement requires ReplaceCatch and RequireWater."); } if (!IsFinite(bossCatchTriggerDefinition.IntroductionGraceSeconds) || bossCatchTriggerDefinition.IntroductionGraceSeconds < 0f || bossCatchTriggerDefinition.IntroductionGraceSeconds > 30f) { errors.Add("Catch trigger '" + bossCatchTriggerDefinition.Id + "' IntroductionGraceSeconds must be between 0 and " + $"{30f}."); } if (!IsFinite(bossCatchTriggerDefinition.MinimumSpawnDistance) || !IsFinite(bossCatchTriggerDefinition.MaximumSpawnDistance) || bossCatchTriggerDefinition.MinimumSpawnDistance < 0f || bossCatchTriggerDefinition.MaximumSpawnDistance < bossCatchTriggerDefinition.MinimumSpawnDistance || bossCatchTriggerDefinition.MaximumSpawnDistance > 1000f) { errors.Add($"Catch trigger '{bossCatchTriggerDefinition.Id}' spawn distances must be finite, ordered, non-negative and at most {1000f}."); } } } private static void ValidateEngagement(BossEngagementDefinition? engagement, ICollection<string> errors) { if (engagement == null) { errors.Add("Encounter Engagement cannot be null."); return; } if (!IsFinite(engagement.MaximumTargetDistance) || engagement.MaximumTargetDistance <= 0f || engagement.MaximumTargetDistance > 10000f) { errors.Add($"Engagement MaximumTargetDistance must be greater than zero and at most {10000f}."); } if (!IsFinite(engagement.ReengageSpeed) || engagement.ReengageSpeed <= 0f || engagement.ReengageSpeed > 1000f) { errors.Add($"Engagement ReengageSpeed must be greater than zero and at most {1000f}."); } if (!IsFinite(engagement.MaximumSpeed) || engagement.MaximumSpeed <= 0f || engagement.MaximumSpeed > 1000f) { errors.Add($"Engagement MaximumSpeed must be greater than zero and at most {1000f}."); } else if (IsFinite(engagement.ReengageSpeed) && engagement.ReengageSpeed > engagement.MaximumSpeed) { errors.Add("Engagement ReengageSpeed cannot exceed MaximumSpeed."); } if (!IsFinite(engagement.PollIntervalSeconds) || engagement.PollIntervalSeconds <= 0f || engagement.PollIntervalSeconds > 10f) { errors.Add($"Engagement PollIntervalSeconds must be greater than zero and at most {10f}."); } } private static void ValidateAquaticRecovery(BossAquaticRecoveryDefinition? recovery, ICollection<string> errors) { if (recovery == null) { errors.Add("Encounter AquaticRecovery cannot be null."); return; } ValidateAquaticRecoveryRange(recovery.MinimumDepth, 0.5f, 50f, "MinimumDepth", errors); ValidateAquaticRecoveryRange(recovery.MinimumTargetHeightDifference, 0.25f, 50f, "MinimumTargetHeightDifference", errors); ValidateAquaticRecoveryRange(recovery.StallDurationSeconds, 0.25f, 30f, "StallDurationSeconds", errors); ValidateAquaticRecoveryRange(recovery.MinimumProgressDistance, 0.05f, 10f, "MinimumProgressDistance", errors); ValidateAquaticRecoveryRange(recovery.SurfaceClearance, 0.25f, 10f, "SurfaceClearance", errors); ValidateAquaticRecoveryRange(recovery.MaximumSpeed, 0.5f, 12f, "MaximumSpeed", errors); ValidateAquaticRecoveryRange(recovery.Acceleration, 0.5f, 30f, "Acceleration", errors); ValidateAquaticRecoveryRange(recovery.MaximumDurationSeconds, 0.25f, 30f, "MaximumDurationSeconds", errors); ValidateAquaticRecoveryRange(recovery.RetryCooldownSeconds, 0.5f, 120f, "RetryCooldownSeconds", errors); ValidateAquaticRecoveryRange(recovery.MaximumLateralDistance, 0.5f, 30f, "MaximumLateralDistance", errors); ValidateAquaticRecoveryRange(recovery.ProbeIntervalSeconds, 0.1f, 1f, "ProbeIntervalSeconds", errors); if (IsFinite(recovery.MinimumDepth) && IsFinite(recovery.SurfaceClearance) && recovery.MinimumDepth <= recovery.SurfaceClearance) { errors.Add("AquaticRecovery MinimumDepth must be greater than SurfaceClearance."); } if (IsFinite(recovery.StallDurationSeconds) && IsFinite(recovery.ProbeIntervalSeconds) && recovery.StallDurationSeconds < recovery.ProbeIntervalSeconds) { errors.Add("AquaticRecovery StallDurationSeconds must be at least ProbeIntervalSeconds."); } if (IsFinite(recovery.MaximumDurationSeconds) && IsFinite(recovery.ProbeIntervalSeconds) && recovery.MaximumDurationSeconds < recovery.ProbeIntervalSeconds) { errors.Add("AquaticRecovery MaximumDurationSeconds must be at least ProbeIntervalSeconds."); } } private static void ValidateAquaticRecoveryRange(float value, float minimum, float maximum, string property, ICollection<string> errors) { if (!IsFinite(value) || value < minimum || value > maximum) { errors.Add($"AquaticRecovery {property} must be finite and between {minimum} and {maximum}."); } } private static void ValidatePhaseGate(BossPhaseDefinition phase, IReadOnlyList<BossAbilityDefinition> abilities, bool isFinalPhase, ICollection<string> errors) { BossPhaseGateDefinition gate = phase.Gate; if (gate == null) { errors.Add("Phase '" + phase.Id + "' Gate cannot be null."); return; } if (!IsDuration(gate.MinimumDurationSeconds)) { errors.Add($"Phase '{phase.Id}' gate MinimumDurationSeconds must be between 0 and {3600f}."); } if (!IsFinite(gate.MaximumDurationSeconds) || gate.MaximumDurationSeconds <= 0f || gate.MaximumDurationSeconds > 3600f || gate.MaximumDurationSeconds < gate.MinimumDurationSeconds) { errors.Add($"Phase '{phase.Id}' gate MaximumDurationSeconds must be positive, no greater than {3600f}, and not shorter than its minimum."); } if (!string.IsNullOrEmpty(gate.RequiredAbilityId)) { if (!EncounterContractValidation.IsSafeToken(gate.RequiredAbilityId, 128)) { errors.Add("Phase '" + phase.Id + "' gate RequiredAbilityId must be a normalized, control-free ability ID."); } else if (!abilities.Any((BossAbilityDefinition ability) => ability != null && string.Equals(ability.Id, gate.RequiredAbilityId, StringComparison.Ordinal))) { errors.Add("Phase '" + phase.Id + "' gate RequiredAbilityId must reference an ability owned by that phase."); } } if (gate.ProtectFromDefeat && !gate.Enabled) { errors.Add("Phase '" + phase.Id + "' gate ProtectFromDefeat requires Enabled=true."); } if (gate.ProtectFromDefeat && !isFinalPhase) { errors.Add("Phase '" + phase.Id + "' gate ProtectFromDefeat may only be used on the final phase."); } if (gate.Enabled && phase.TriggerHealthFraction <= 0f) { errors.Add("Phase '" + phase.Id + "' must have a positive health threshold when its progression gate is enabled."); } } private static void ValidateOptionalFilter(string? value, string name, string triggerId, ICollection<string> errors) { if (value != null && !EncounterContractValidation.IsSafeToken(value, 256)) { errors.Add($"Catch trigger '{triggerId}' {name} must be null or a normalized, control-free value no longer than {256} characters."); } } private static void ValidateHookStagger(BossHookStaggerDefinition? value, string path, ICollection<BossValidationIssue> issues) { if (value == null) { AddIssue(issues, "hook.missing_definition", path, "Encounter HookStagger cannot be null."); return; } if (!IsFinite(value.MinimumSafeTension) || !IsFinite(value.MaximumSafeTension) || value.MinimumSafeTension < 0f || value.MaximumSafeTension > 1f || value.MaximumSafeTension < value.MinimumSafeTension) { AddIssue(issues, "hook.invalid_tension_bounds", path, "HookStagger safe tension bounds must be finite, ordered and between 0 and 1."); } if (!IsFinite(value.ProgressPerSecond) || value.ProgressPerSecond < 0f || value.ProgressPerSecond > 100f || !IsFinite(value.DecayPerSecond) || value.DecayPerSecond < 0f || value.DecayPerSecond > 100f) { AddIssue(issues, "hook.invalid_rate", path, $"HookStagger progress and decay rates must be finite values between 0 and {100f}."); } if (!IsFinite(value.StaggerThreshold) || value.StaggerThreshold <= 0f || value.StaggerThreshold > 1f) { AddIssue(issues, "hook.invalid_threshold", path + ".staggerThreshold", "HookStagger StaggerThreshold must be between 0 (exclusive) and 1."); } if (!IsDuration(value.StaggerDurationSeconds) || !IsDuration(value.RecoveryDurationSeconds)) { AddIssue(issues, "hook.invalid_duration", path, $"HookStagger durations must be finite values between 0 and {3600f}."); } if (value.MaximumSimultaneousHooks <= 0 || value.MaximumSimultaneousHooks > 8) { AddIssue(issues, "hook.invalid_limit", path + ".maximumSimultaneousHooks", $"HookStagger MaximumSimultaneousHooks must be between 1 and {8}."); } } private static void ValidatePlacement(BossPlacementDefinition? value, string path, ICollection<BossValidationIssue> issues, bool allowBossRelative, bool allowBossBodyClearance = false) { //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) if (value == null) { return; } if (value.UseBossBodyClearance && (!allowBossBodyClearance || value.Mode != BossPlacementMode.Surface || value.FailurePolicy != BossPlacementFailurePolicy.SkipAction || value.SocketPath != null || value.LocalOffset.x != 0f || value.LocalOffset.z != 0f || value.LocalOffset.y > 0f)) { AddIssue(issues, "placement.invalid_body_clearance_context", path + ".useBossBodyClearance", "Body clearance is supported only for Surface replacement catches with SkipAction, no socket, and a non-positive vertical-only LocalOffset."); } if (!Enum.IsDefined(typeof(BossPlacementMode), value.Mode)) { AddIssue(issues, "placement.unsupported_mode", path + ".mode", "Placement Mode is not supported."); } else if (!allowBossRelative && value.Mode == BossPlacementMode.BossRelative) { AddIssue(issues, "placement.invalid_context", path + ".mode", "BossRelative placement cannot be used before the boss exists."); } if (!Enum.IsDefined(typeof(BossPlacementFailurePolicy), value.FailurePolicy)) { AddIssue(issues, "placement.unsupported_failure_policy", path + ".failurePolicy", "Placement FailurePolicy is not supported."); } if (!IsFinite(value.LocalOffset) || !IsWithinMagnitude(value.LocalOffset, 1000f)) { AddIssue(issues, "placement.invalid_offset", path + ".localOffset", $"Placement LocalOffset must be finite and bounded by {1000f}."); } if (!IsFinite(value.SearchRadius) || value.SearchRadius < 0f || value.SearchRadius > 1000f) { AddIssue(issues, "placement.invalid_search_radius", path + ".searchRadius", $"Placement SearchRadius must be between 0 and {1000f}."); } if (!IsFinite(value.VerticalSearchDistance) || value.VerticalSearchDistance < 0f || value.VerticalSearchDistance > 1000f) { AddIssue(issues, "placement.invalid_vertical_search", path + ".verticalSearchDistance", $"Placement VerticalSearchDistance must be between 0 and {1000f}."); } if (!IsFinite(value.ClearanceRadius) || value.ClearanceRadius < 0f || value.ClearanceRadius > 1000f) { AddIssue(issues, "placement.invalid_clearance", path + ".clearanceRadius", $"Placement ClearanceRadius must be between 0 and {1000f}."); } if (value.MaximumAttempts <= 0 || value.MaximumAttempts > 32) { AddIssue(issues, "placement.invalid_attempt_limit", path + ".maximumAttempts", $"Placement MaximumAttempts must be between 1 and {32}."); } if (value.Mode == BossPlacementMode.TransformSocket) { if (!IsSafeRelativePath(value.SocketPath)) { AddIssue(issues, "placement.invalid_socket", path + ".socketPath", "TransformSocket placement requires a safe relative SocketPath."); } } else if (value.SocketPath != null) { AddIssue(issues, "placement.unused_socket", path + ".socketPath", "SocketPath may only be supplied for TransformSocket mode."); } } private static void ValidateFormation(BossFormationDefinition? value, int count, string path, ICollection<BossValidationIssue> issues) { if (value == null) { return; } if (!Enum.IsDefined(typeof(BossFormationKind), value.Kind)) { AddIssue(issues, "formation.unsupported_kind", path + ".kind", "Formation Kind is not supported."); } if (!IsFinite(value.Radius) || value.Radius < 0f || value.Radius > 1000f) { AddIssue(issues, "formation.invalid_radius", path + ".radius", $"Formation Radius must be between 0 and {1000f}."); } if (!IsFinite(value.Spacing) || value.Spacing <= 0f || value.Spacing > 1000f) { AddIssue(issues, "formation.invalid_spacing", path + ".spacing", $"Formation Spacing must be between 0 (exclusive) and {1000f}."); } if (!IsFinite(value.AngleDegrees) || value.AngleDegrees <= 0f || value.AngleDegrees > 360f) { AddIssue(issues, "formation.invalid_angle", path + ".angleDegrees", "Formation AngleDegrees must be between 0 (exclusive) and 360."); } if (!IsFinite(value.YawOffsetDegrees) || Math.Abs(value.YawOffsetDegrees) > 36000f) { AddIssue(issues, "formation.invalid_yaw", path + ".yawOffsetDegrees", "Formation YawOffsetDegrees must be finite and bounded by 36000 degrees."); } if (value.SocketPaths == null) { AddIssue(issues, "formation.missing_sockets", path + ".socketPaths", "Formation SocketPaths cannot be null; use an empty collection."); return; } if (value.SocketPaths.Count > 32) { AddIssue(issues, "formation.socket_budget_exceeded", path + ".socketPaths", $"Formation may contain at most {32} socket paths."); } if (value.Kind == BossFormationKind.Sockets && value.SocketPaths.Count != count) { AddIssue(issues, "formation.socket_count_mismatch", path + ".socketPaths", "Sockets formation requires exactly one SocketPath per requested minion."); } if (value.Kind != BossFormationKind.Sockets && value.SocketPaths.Count != 0) { AddIssue(issues, "formation.unused_sockets", path + ".socketPaths", "SocketPaths may only be supplied for a Sockets formation."); } HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); for (int i = 0; i < Math.Min(value.SocketPaths.Count, 32); i++) { string text = value.SocketPaths[i]; if (!IsSafeRelativePath(text)) { AddIssue(issues, "formation.invalid_socket", path + ".socketPaths[" + i + "]", "Every formation SocketPath must be a safe relative transform path."); } else if (!hashSet.Add(text)) { AddIssue(issues, "formation.duplicate_socket", path + ".socketPaths[" + i + "]", "Duplicate formation SocketPath '" + text + "'."); } } if ((value.Kind == BossFormationKind.Ring || value.Kind == BossFormationKind.Cone) && value.Radius <= 0f) { AddIssue(issues, "formation.invalid_radius", path + ".radius", "Ring and Cone formations require a positive Radius."); } } private static bool IsSafeRelativePath(string? value) { if (!string.IsNullOrWhiteSpace(value) && value.Length <= 1024 && string.Equals(value, value.Trim(), StringComparison.Ordinal) && !value.StartsWith("/", StringComparison.Ordinal) && !value.StartsWith("\\", StringComparison.Ordinal) && !value.Contains("..")) { return EncounterContractValidation.IsSafeToken(value, 1024); } return false; } private static void ValidateAbility(BossAbilityDefinition? ability, string phaseId, ISet<string> abilityIds, ICollection<string> errors, ref long declaredActiveMinions, ref long declaredExecutionClaims, bool isGlobal) { if (ability == null) { errors.Add("Phase '" + phaseId + "' contains a null ability."); return; } if (!EncounterContractValidation.IsSafeToken(ability.Id, 128)) { errors.Add($"Every ability requires a normalized, control-free Id no longer than {128} characters."); } else if (!abilityIds.Add(ability.Id)) { errors.Add("Duplicate ability Id '" + ability.Id + "'."); } if (!Enum.IsDefined(typeof(BossAbilityTrigger), ability.Trigger)) { errors.Add("Ability '" + ability.Id + "' Trigger is not supported."); } else if (isGlobal && ability.Trigger == BossAbilityTrigger.PhaseEnter) { errors.Add("Global ability '" + ability.Id + "' cannot use PhaseEnter; place it on a phase."); } else if (!isGlobal && ability.Trigger == BossAbilityTrigger.OnSpawn) { errors.Add("Phase ability '" + ability.Id + "' cannot use OnSpawn; place it in BossDefinition.Abilities."); } if (!IsFinite(ability.InitialDelaySeconds) || ability.InitialDelaySeconds < 0f || ability.InitialDelaySeconds > 3600f) { errors.Add($"Ability '{ability.Id}' InitialDelaySeconds must be between 0 and {3600f}."); } if (!IsFinite(ability.CooldownSeconds) || ability.CooldownSeconds < 0f || ability.CooldownSeconds > 86400f) { errors.Add($"Ability '{ability.Id}' CooldownSeconds must be between 0 and {86400f}."); } if (ability.MaxRepeats < 0 || ability.MaxRepeats > 1024) { errors.Add($"Ability '{ability.Id}' MaxRepeats must be between 0 and {1024}."); } if (ability.MaxRepeats > 0 && (!IsFinite(ability.CooldownSeconds) || ability.CooldownSeconds <= 0f)) { errors.Add("Ability '" + ability.Id + "' requires a positive CooldownSeconds when MaxRepeats is positive."); } if (!IsFinite(ability.Probability) || ability.Probability < 0f || ability.Probability > 1f) { errors.Add("Ability '" + ability.Id + "' Probability must be between 0 and 1."); } ValidateTimeline(ability.Id, ability.Timeline, errors); ValidateTargetSelector(ability.Id, ability.TargetSelector, errors); ValidateGeometry(ability.Id, ability.Geometry, errors); ValidateTelegraph(ability.Id, ability.Telegraph, errors); if (ability.Actions == null) { errors.Add("Ability '" + ability.Id + "' Actions cannot be null; use an empty collection."); } else if (ability.Actions.Count > 32) { errors.Add($"Ability '{ability.Id}' may declare at most {32} actions."); } IReadOnlyList<BossActionDefinition> readOnlyList = ability.Actions ?? Array.Empty<BossActionDefinition>(); if (ability.Probability > 0f) { int num = Math.Min(Math.Max(ability.MaxRepeats, 0), 1024); declaredExecutionClaims += ((long)num + 1L) * Math.Min(readOnlyList.Count, 32); } for (int i = 0; i < Math.Min(readOnlyList.Count, 32); i++) { ValidateAction(readOnlyList[i], phaseId + "/" + ability.Id, errors, ref declaredActiveMinions, isAbilityAction: true); if (ability.Timeline != null) { ValidateAbilityActionTimeline(ability.Id, readOnlyList[i], ability.Timeline, errors); } } } private static void ValidateVersion4(BossDefinition definition, ICollection<BossValidationIssue> issues) { ValidateVersion4VanillaIntegration(definition.VanillaIntegration, issues); HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); IReadOnlyList<BossAbilityDefinition> readOnlyList = definition.Abilities ?? Array.Empty<BossAbilityDefinition>(); for (int i = 0; i < Math.Min(readOnlyList.Count, 32); i++) { BossAbilityDefinition bossAbilityDefinition = readOnlyList[i]; if (bossAbilityDefinition != null && EncounterContractValidation.IsSafeToken(bossAbilityDefinition.Id, 128)) { hashSet.Add(bossAbilityDefinition.Id); } } IReadOnlyList<BossPhaseDefinition> readOnlyList2 = definition.Phases ?? Array.Empty<BossPhaseDefinition>(); for (int j = 0; j < Math.Min(readOnlyList2.Count, 32); j++) { BossPhaseDefinition bossPhaseDefinition = readOnlyList2[j]; if (bossPhaseDefinition == null) { continue; } IReadOnlyList<BossAbilityDefinition> readOnlyList3 = bossPhaseDefinition.Abilities ?? Array.Empty<BossAbilityDefinition>(); for (int k = 0; k < Math.Min(readOnlyList3.Count, 32); k++) { BossAbilityDefinition bossAbilityDefinition2 = readOnlyList3[k]; if (bossAbilityDefinition2 != null && EncounterContractValidation.IsSafeToken(bossAbilityDefinition2.Id, 128)) { hashSet.Add(bossAbilityDefinition2.Id); } } } HashSet<string> actionIds = new HashSet<string>(StringComparer.Ordinal); HashSet<string> modifierIds = new HashSet<string>(StringComparer.Ordinal); int totalConditionNodes = 0; int reversibleModifiers = 0; if (definition.Encounter != null) { ValidateHookStagger(definition.Encounter.HookStagger, "encounter.hookStagger", issues); IReadOnlyList<BossCatchTriggerDefinition> readOnlyList4 = definition.Encounter.CatchTriggers ?? Array.Empty<BossCatchTriggerDefinition>(); for (int l = 0; l < Math.Min(readOnlyList4.Count, 32); l++) { if (readOnlyList4[l] != null) { ValidatePlacement(readOnlyList4[l].Placement, "encounter.catchTriggers[" + l + "].placement", issues, allowBossRelative: false, allowBossBodyClearance: true); } } } IReadOnlyList<BossAbilityDefinition> readOnlyList5 = definition.Abilities ?? Array.Empty<BossAbilityDefinition>(); for (int m = 0; m < Math.Min(readOnlyList5.Count, 32); m++) { ValidateVersion4Ability(readOnlyList5[m], "abilities[" + m + "]", null, hashSet, actionIds, modifierIds, issues, ref totalConditionNodes, ref reversibleModifiers); } IReadOnlyList<BossPhaseDefinition> readOnlyList6 = definition.Phases ?? Array.Empty<BossPhaseDefinition>(); for (int n = 0; n < Math.Min(readOnlyList6.Count, 32); n++) { BossPhaseDefinition bossPhaseDefinition2 = readOnlyList6[n]; if (bossPhaseDefinition2 != null) { string text = "phases[" + n + "]"; if (bossPhaseDefinition2.ExitActions == null) { AddIssue(issues, "phase.missing_exit_actions", text + ".exitActions", "ExitActions cannot be null; use an empty collection."); } else if (bossPhaseDefinition2.ExitActions.Count > 32) { AddIssue(issues, "phase.exit_action_budget_exceeded", text + ".exitActions", $"A phase may declare at most {32} exit actions."); } ValidateVersion4Actions(bossPhaseDefinition2.Actions, text + ".actions", bossPhaseDefinition2.Id, null, isExit: false, actionIds, modifierIds, issues, ref reversibleModifiers); ValidateVersion4Actions(bossPhaseDefinition2.ExitActions, text + ".exitActions", bossPhaseDefinition2.Id, null, isExit: true, actionIds, modifierIds, issues, ref reversibleModifiers); IReadOnlyList<BossAbilityDefinition> readOnlyList7 = bossPhaseDefinition2.Abilities ?? Array.Empty<BossAbilityDefinition>(); for (int num = 0; num < Math.Min(readOnlyList7.Count, 32); num++) { ValidateVersion4Ability(readOnlyList7[num], text + ".abilities[" + num + "]", bossPhaseDefinition2.Id, hashSet, actionIds, modifierIds, issues, ref totalConditionNodes, ref reversibleModifiers); } } } if (reversibleModifiers > 64) { AddIssue(issues
BepInEx/plugins/BossMakerAPI/BossMakerAPI.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using FishNet; using FishNet.Broadcast; using FishNet.Connection; using FishNet.Managing; using FishNet.Managing.Logging; using FishNet.Managing.Object; using FishNet.Managing.Observing; using FishNet.Managing.Server; using FishNet.Object; using FishNet.Observing; using FishNet.Serializing; using FishNet.Transporting; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: InternalsVisibleTo("BossMakerAPI.Tests")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("BossMakerAPI contributors")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Framework only. Configure each boss in its own tab. Startup and developer settings use com.skylin3.howtofish.bossmakerapi.advanced.cfg and require a restart.")] [assembly: AssemblyFileVersion("0.10.3.0")] [assembly: AssemblyInformationalVersion("0.10.3")] [assembly: AssemblyProduct("BossMakerAPI")] [assembly: AssemblyTitle("BossMakerAPI")] [assembly: AssemblyVersion("0.2.0.0")] [module: RefSafetyRules(11)] 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; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace HowToFish.BossMaker.Runtime { internal sealed class AbilityTargetCandidate { internal string EntityId { get; } internal int? NetworkObjectId { get; } internal Vector3 Position { get; } internal object? RuntimeEntity { get; } internal AbilityTargetCandidate(string entityId, int? networkObjectId, Vector3 position, object? runtimeEntity = null) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrWhiteSpace(entityId)) { throw new ArgumentException("Entity ID is required.", "entityId"); } EntityId = entityId; NetworkObjectId = networkObjectId; Position = position; RuntimeEntity = runtimeEntity; } internal EncounterEntityReference ToReference() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown return new EncounterEntityReference(EntityId, NetworkObjectId); } } internal sealed class AbilityTargetSelection { internal AbilityTargetCandidate Candidate { get; } internal Vector3 LockedPosition { get; } internal AbilityTargetSelection(AbilityTargetCandidate candidate, Vector3 lockedPosition) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) Candidate = candidate ?? throw new ArgumentNullException("candidate"); LockedPosition = lockedPosition; } } internal enum AbilityPlayerIneligibilityReason { MissingVitals, NonPositiveHealth, ServerNotInitialized, MissingOwner, OwnerInvalid, OwnerInactive, OwnerDisconnecting, NonFinitePosition } internal sealed class AbilityPlayerCapture { internal IReadOnlyList<AbilityTargetCandidate> EligibleCandidates { get; } internal int ObservedPlayerCount { get; } internal IReadOnlyDictionary<AbilityPlayerIneligibilityReason, int> IneligibilityReasons { get; } internal bool CaptureFailed { get; } internal AbilityPlayerCapture(IReadOnlyList<AbilityTargetCandidate> eligibleCandidates, int observedPlayerCount, IReadOnlyDictionary<AbilityPlayerIneligibilityReason, int>? ineligibilityReasons = null, bool captureFailed = false) { if (eligibleCandidates == null) { throw new ArgumentNullException("eligibleCandidates"); } if (observedPlayerCount < 0) { throw new ArgumentOutOfRangeException("observedPlayerCount"); } EligibleCandidates = eligibleCandidates.Where((AbilityTargetCandidate value) => value != null).OrderBy<AbilityTargetCandidate, string>((AbilityTargetCandidate value) => value.EntityId, StringComparer.Ordinal).ThenBy((AbilityTargetCandidate value) => value.NetworkObjectId ?? int.MaxValue) .ToArray(); ObservedPlayerCount = observedPlayerCount; CaptureFailed = captureFailed; Dictionary<AbilityPlayerIneligibilityReason, int> dictionary = new Dictionary<AbilityPlayerIneligibilityReason, int>(); if (ineligibilityReasons != null) { foreach (KeyValuePair<AbilityPlayerIneligibilityReason, int> ineligibilityReason in ineligibilityReasons) { if (ineligibilityReason.Value > 0) { dictionary[ineligibilityReason.Key] = ineligibilityReason.Value; } } } IneligibilityReasons = dictionary; } } internal enum AbilityTargetFailureKind { None, PlayerCaptureFailed, NoPlayersFound, PlayersIneligible, EligiblePlayersOutsideMaximumRange, Unclassified } internal readonly struct AbilityTargetFailureDiagnostic { internal AbilityTargetFailureKind Kind { get; } internal string Message { get; } internal AbilityTargetFailureDiagnostic(AbilityTargetFailureKind kind, string message) { Kind = kind; Message = message ?? string.Empty; } } internal static class DeterministicAbilityTargetResolver { private readonly struct RankedCandidate { internal AbilityTargetCandidate Candidate { get; } internal double DistanceSquared { get; } internal RankedCandidate(AbilityTargetCandidate candidate, double distanceSquared) { Candidate = candidate; DistanceSquared = distanceSquared; } } private const int MaximumReportedIneligibilityReasons = 5; internal static bool TrySelect(BossTargetSelectorDefinition selector, AbilityTargetCandidate boss, IReadOnlyList<AbilityTargetCandidate> candidates, int encounterSeed, string phaseId, string abilityId, int attemptOrdinal, out AbilityTargetSelection selection) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Expected I4, but got Unknown //IL_024f: Unknown result type (might be due to invalid IL or missing references) if (selector == null) { throw new ArgumentNullException("selector"); } if (boss == null) { throw new ArgumentNullException("boss"); } if (candidates == null) { throw new ArgumentNullException("candidates"); } if ((int)selector.Kind == 0) { selection = new AbilityTargetSelection(boss, boss.Position); return true; } double maximumSquared = ((selector.MaximumRange <= 0f) ? double.PositiveInfinity : ((double)selector.MaximumRange * (double)selector.MaximumRange)); RankedCandidate[] array = (from value in candidates where value != null && IsFinite(value.Position) select new RankedCandidate(value, PlanarSquared(value.Position - boss.Position)) into value where value.DistanceSquared <= maximumSquared select value).OrderBy<RankedCandidate, string>((RankedCandidate value) => value.Candidate.EntityId, StringComparer.Ordinal).ThenBy((RankedCandidate value) => value.Candidate.NetworkObjectId ?? int.MaxValue).ToArray(); if (array.Length == 0) { selection = null; return false; } BossTargetSelectorKind kind = selector.Kind; RankedCandidate rankedCandidate; switch (kind - 1) { case 0: rankedCandidate = array.OrderBy((RankedCandidate value) => value.DistanceSquared).ThenBy<RankedCandidate, string>((RankedCandidate value) => value.Candidate.EntityId, StringComparer.Ordinal).First(); break; case 1: rankedCandidate = array.OrderByDescending((RankedCandidate value) => value.DistanceSquared).ThenBy<RankedCandidate, string>((RankedCandidate value) => value.Candidate.EntityId, StringComparer.Ordinal).First(); break; case 2: { double num = DeterministicAbilityScheduler.DeterministicProbabilitySample(encounterSeed, phaseId, abilityId + ":target", 0L, attemptOrdinal); rankedCandidate = array[Math.Min(array.Length - 1, (int)(num * (double)array.Length))]; break; } default: throw new ArgumentOutOfRangeException("Kind"); } selection = new AbilityTargetSelection(rankedCandidate.Candidate, rankedCandidate.Candidate.Position); return true; } internal static AbilityTargetFailureDiagnostic DiagnoseFailure(BossTargetSelectorDefinition selector, AbilityTargetCandidate boss, AbilityPlayerCapture capture) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) if (selector == null) { throw new ArgumentNullException("selector"); } if (boss == null) { throw new ArgumentNullException("boss"); } if (capture == null) { throw new ArgumentNullException("capture"); } if ((int)selector.Kind == 0) { return new AbilityTargetFailureDiagnostic(AbilityTargetFailureKind.None, string.Empty); } if (capture.CaptureFailed) { return new AbilityTargetFailureDiagnostic(AbilityTargetFailureKind.PlayerCaptureFailed, "the authoritative Player snapshot could not be inspected completely; selection failed closed"); } if (capture.ObservedPlayerCount == 0) { return new AbilityTargetFailureDiagnostic(AbilityTargetFailureKind.NoPlayersFound, "no Player objects were found in the authoritative scene snapshot"); } if (capture.EligibleCandidates.Count == 0) { return new AbilityTargetFailureDiagnostic(AbilityTargetFailureKind.PlayersIneligible, capture.ObservedPlayerCount.ToString(CultureInfo.InvariantCulture) + " Player object(s) were found, but none was server-eligible" + FormatIneligibilityReasons(capture.IneligibilityReasons)); } if (selector.MaximumRange > 0f) { double num = double.PositiveInfinity; double num2 = double.PositiveInfinity; foreach (AbilityTargetCandidate eligibleCandidate in capture.EligibleCandidates) { if (eligibleCandidate != null && IsFinite(eligibleCandidate.Position)) { Vector3 val = eligibleCandidate.Position - boss.Position; num = Math.Min(num, Math.Sqrt(PlanarSquared(val))); num2 = Math.Min(num2, Math.Abs((double)val.y)); } } if (!double.IsInfinity(num) && !double.IsInfinity(num2)) { return new AbilityTargetFailureDiagnostic(AbilityTargetFailureKind.EligiblePlayersOutsideMaximumRange, capture.EligibleCandidates.Count.ToString(CultureInfo.InvariantCulture) + " server-eligible Player object(s) were all outside planar MaximumRange=" + FormatDistance(selector.MaximumRange) + "m (minimum horizontal distance=" + FormatDistance(num) + "m; minimum absolute vertical separation=" + FormatDistance(num2) + "m; vertical separation does not affect range selection)"); } } return new AbilityTargetFailureDiagnostic(AbilityTargetFailureKind.Unclassified, "target selection failed closed for a bounded, unclassified reason (observed=" + capture.ObservedPlayerCount.ToString(CultureInfo.InvariantCulture) + ", eligible=" + capture.EligibleCandidates.Count.ToString(CultureInfo.InvariantCulture) + ")"); } private static string FormatIneligibilityReasons(IReadOnlyDictionary<AbilityPlayerIneligibilityReason, int> reasons) { KeyValuePair<AbilityPlayerIneligibilityReason, int>[] array = (from pair in reasons where pair.Value > 0 orderby (int)pair.Key select pair).ToArray(); if (array.Length == 0) { return " (reason unavailable)"; } List<string> list = (from pair in array.Take(5) select ReasonLabel(pair.Key) + "=" + pair.Value.ToString(CultureInfo.InvariantCulture)).ToList(); if (array.Length > 5) { list.Add("other=" + array.Skip(5).Sum((KeyValuePair<AbilityPlayerIneligibilityReason, int> pair) => pair.Value).ToString(CultureInfo.InvariantCulture)); } return " (" + string.Join("; ", list) + ")"; } private static string ReasonLabel(AbilityPlayerIneligibilityReason reason) { return reason switch { AbilityPlayerIneligibilityReason.MissingVitals => "missing vitals", AbilityPlayerIneligibilityReason.NonPositiveHealth => "non-positive health", AbilityPlayerIneligibilityReason.ServerNotInitialized => "server not initialized", AbilityPlayerIneligibilityReason.MissingOwner => "missing owner", AbilityPlayerIneligibilityReason.OwnerInvalid => "invalid owner", AbilityPlayerIneligibilityReason.OwnerInactive => "inactive owner", AbilityPlayerIneligibilityReason.OwnerDisconnecting => "disconnecting owner", AbilityPlayerIneligibilityReason.NonFinitePosition => "non-finite position", _ => "unknown", }; } private static string FormatDistance(double value) { return value.ToString("0.###", CultureInfo.InvariantCulture); } private static double PlanarSquared(Vector3 value) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) return (double)value.x * (double)value.x + (double)value.z * (double)value.z; } internal static bool IsFinitePosition(Vector3 value) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return IsFinite(value); } private static bool IsFinite(Vector3 value) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (!float.IsNaN(value.x) && !float.IsInfinity(value.x) && !float.IsNaN(value.y) && !float.IsInfinity(value.y) && !float.IsNaN(value.z)) { return !float.IsInfinity(value.z); } return false; } } internal static class GameAbilityTargetSource { internal static IReadOnlyList<AbilityTargetCandidate> CaptureAlivePlayers(Action<string> warning) { return CapturePlayers(warning).EligibleCandidates; } internal static AbilityPlayerCapture CapturePlayers(Action<string> warning) { //IL_010e: Unknown result type (might be due to invalid IL or missing references) List<AbilityTargetCandidate> list = new List<AbilityTargetCandidate>(); Dictionary<AbilityPlayerIneligibilityReason, int> dictionary = new Dictionary<AbilityPlayerIneligibilityReason, int>(); int num = 0; bool captureFailed = false; try { List<Player> alivePlayers = PlayerManager.AlivePlayers; Player[] array = ((alivePlayers != null && alivePlayers.Count != 0) ? alivePlayers.ToArray() : Object.FindObjectsByType<Player>((FindObjectsInactive)0)); foreach (Player val in array) { if (!((Object)(object)val == (Object)null)) { num++; Vector3 position; AbilityPlayerIneligibilityReason? ineligibilityReason = GetIneligibilityReason(val, out position); if (ineligibilityReason.HasValue) { dictionary.TryGetValue(ineligibilityReason.Value, out var value); dictionary[ineligibilityReason.Value] = value + 1; } else { int? networkObjectId = (((Object)(object)((NetworkBehaviour)val).NetworkObject == (Object)null) ? ((int?)null) : new int?(((NetworkBehaviour)val).NetworkObject.ObjectId)); string entityId = (networkObjectId.HasValue ? ("player:" + networkObjectId.Value.ToString(CultureInfo.InvariantCulture)) : ("player-owner:" + ((NetworkBehaviour)val).Owner.ClientId.ToString(CultureInfo.InvariantCulture))); list.Add(new AbilityTargetCandidate(entityId, networkObjectId, position, val)); } } } } catch (Exception ex) { captureFailed = true; warning("Could not enumerate alive players for an ability: " + ex.Message); } return new AbilityPlayerCapture(list, num, dictionary, captureFailed); } internal static Player? ResolvePlayer(EncounterEntityReference reference, bool requireServerEligibility = true) { if (reference == null) { return null; } Player[] array; try { array = Object.FindObjectsByType<Player>((FindObjectsInactive)0); } catch { return null; } Player[] array2 = array; foreach (Player val in array2) { if (!((Object)(object)val == (Object)null) && (!requireServerEligibility || IsEligible(val))) { int? num = (((Object)(object)((NetworkBehaviour)val).NetworkObject == (Object)null) ? ((int?)null) : new int?(((NetworkBehaviour)val).NetworkObject.ObjectId)); if (string.Equals(num.HasValue ? ("player:" + num.Value.ToString(CultureInfo.InvariantCulture)) : ((((NetworkBehaviour)val).Owner == (NetworkConnection)null) ? string.Empty : ("player-owner:" + ((NetworkBehaviour)val).Owner.ClientId.ToString(CultureInfo.InvariantCulture))), reference.EntityId, StringComparison.Ordinal) || (reference.NetworkObjectId.HasValue && num == reference.NetworkObjectId)) { return val; } } } return null; } internal static bool IsEligible(Player? player) { if ((Object)(object)player == (Object)null || (Object)(object)player.Vitals == (Object)null || player.Vitals.Health <= 0 || !((NetworkBehaviour)player).IsServerInitialized) { return false; } NetworkConnection owner = ((NetworkBehaviour)player).Owner; if (owner != (NetworkConnection)null && owner.IsValid && owner.IsActive) { return !owner.Disconnecting; } return false; } private static AbilityPlayerIneligibilityReason? GetIneligibilityReason(Player player, out Vector3 position) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) position = default(Vector3); if ((Object)(object)player.Vitals == (Object)null) { return AbilityPlayerIneligibilityReason.MissingVitals; } if (player.Vitals.Health <= 0) { return AbilityPlayerIneligibilityReason.NonPositiveHealth; } if (!((NetworkBehaviour)player).IsServerInitialized) { return AbilityPlayerIneligibilityReason.ServerNotInitialized; } NetworkConnection owner = ((NetworkBehaviour)player).Owner; if (owner == (NetworkConnection)null) { return AbilityPlayerIneligibilityReason.MissingOwner; } if (!owner.IsValid) { return AbilityPlayerIneligibilityReason.OwnerInvalid; } if (!owner.IsActive) { return AbilityPlayerIneligibilityReason.OwnerInactive; } if (owner.Disconnecting) { return AbilityPlayerIneligibilityReason.OwnerDisconnecting; } if (!PlayerCombatAnchorAdapter.TryGetPosition(player, out position)) { return AbilityPlayerIneligibilityReason.NonFinitePosition; } return null; } } internal sealed class AbilityTelegraphPresenter : IDisposable { private const int CurvedSegments = 64; private const int SweepSegments = 32; private const float FillHeight = 0.006f; private const float ProgressHeight = 0.012f; private const float LineHeight = 0.018f; private const float AfterglowHeight = 0.024f; private const double AfterglowDurationSeconds = 0.32; private static bool _shaderWarmupSucceeded; private readonly Transform _owner; private readonly double _minimumLeadTime; private readonly List<Vector3> _scratchVertices = new List<Vector3>(132); private readonly List<int> _scratchTriangles = new List<int>(384); private readonly List<Color> _scratchColors = new List<Color>(132); private GameObject? _root; private GameObject? _afterglowRoot; private LineRenderer? _primary; private LineRenderer? _secondary; private LineRenderer? _pattern; private LineRenderer? _sweep; private LineRenderer? _impact; private Mesh? _fillMesh; private Mesh? _progressMesh; private Mesh? _afterglowMesh; private MeshRenderer? _fillRenderer; private MeshRenderer? _progressRenderer; private MeshRenderer? _afterglowRenderer; private MaterialPropertyBlock? _propertyBlock; private Material? _material; private GameObject? _audioObject; private BossTelegraphDefinition? _definition; private ResolvedAbilityGeometry _geometry; private ResolvedAbilityGeometry _afterglowGeometry; private double _telegraphTime; private double _resolveTime; private double _afterglowStartTime; private double _afterglowEndTime; private Color _afterglowColor; private Color _afterglowBorderColor; private float _afterglowBorderWidth; private float _progress; private bool _visible; private bool _afterglowVisible; internal bool IsVisible => _visible; internal AbilityTelegraphPresenter(Transform owner, double minimumLeadTime) { _owner = owner ?? throw new ArgumentNullException("owner"); if (double.IsNaN(minimumLeadTime) || double.IsInfinity(minimumLeadTime) || minimumLeadTime < 0.0) { throw new ArgumentOutOfRangeException("minimumLeadTime"); } _minimumLeadTime = minimumLeadTime; } internal bool TryPrepare(out string diagnostic) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown Stopwatch stopwatch = Stopwatch.StartNew(); try { bool flag = (Object)(object)_root != (Object)null; EnsureRig(); if (_propertyBlock == null) { _propertyBlock = new MaterialPropertyBlock(); } diagnostic = FormattableString.Invariant(FormattableStringFactory.Create("Telegraph presentation {0}: 10 objects, 3 meshes, 5 lines; {1:0.###} ms CPU.", flag ? "already prepared" : "prepared invisibly", stopwatch.Elapsed.TotalMilliseconds)); return true; } catch (Exception ex) { try { Dispose(); } catch { } diagnostic = FormattableString.Invariant($"Telegraph preparation failed after {stopwatch.Elapsed.TotalMilliseconds:0.###} ms CPU: {ex.GetType().Name}: {ex.Message}"); return false; } } internal static bool TryWarmUpShader(out string diagnostic) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Invalid comparison between Unknown and I4 //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown //IL_0071: Unknown result type (might be due to invalid IL or missing references) Stopwatch stopwatch = Stopwatch.StartNew(); ShaderVariantCollection val = null; try { if (_shaderWarmupSucceeded || (int)SystemInfo.graphicsDeviceType == 4) { diagnostic = (_shaderWarmupSucceeded ? "Telegraph shader was already warmed." : "Telegraph shader warm-up skipped: no graphics device."); return true; } Shader val2 = FindPresentationShader(); if ((Object)(object)val2 == (Object)null) { diagnostic = "Telegraph shader warm-up skipped: no supported unlit shader was found."; return false; } val = new ShaderVariantCollection { name = "BossMakerAPI.TelegraphShaderWarmup", hideFlags = (HideFlags)61 }; val.Add(new ShaderVariant(val2, (PassType)0, Array.Empty<string>())); val.WarmUp(); _shaderWarmupSucceeded = true; diagnostic = FormattableString.Invariant($"Telegraph shader warm-up: '{((Object)val2).name}', {val.variantCount} variant; {stopwatch.Elapsed.TotalMilliseconds:0.###} ms CPU."); return true; } catch (Exception ex) { diagnostic = FormattableString.Invariant($"Telegraph shader warm-up failed after {stopwatch.Elapsed.TotalMilliseconds:0.###} ms CPU: {ex.GetType().Name}: {ex.Message}"); return false; } finally { if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } } } private static Shader? FindPresentationShader() { return Shader.Find("Sprites/Default") ?? Shader.Find("Unlit/Color"); } internal void Show(BossTelegraphDefinition definition, ResolvedAbilityGeometry geometry, double telegraphServerTime, double resolveServerTime, double currentServerTime) { //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Expected O, but got Unknown //IL_00e3: Unknown result type (might be due to invalid IL or missing references) if (definition == null) { throw new ArgumentNullException("definition"); } Hide(); ClearAfterglow(); if (definition.Enabled && !(currentServerTime > resolveServerTime - _minimumLeadTime)) { EnsureRig(); _definition = definition; _geometry = geometry; _telegraphTime = telegraphServerTime; _resolveTime = resolveServerTime; _visible = true; if ((Object)(object)_root != (Object)null) { _root.SetActive(true); } double num = Math.Max(0.001, _resolveTime - _telegraphTime); _progress = Mathf.Clamp01((float)((currentServerTime - _telegraphTime) / num)); Draw(); UpdateTelegraphStyle(currentServerTime); if ((Object)(object)definition.AudioClip != (Object)null) { _audioObject = new GameObject("BossMakerAPI.AbilityTelegraphAudio"); _audioObject.transform.position = geometry.Origin; AudioSource obj = _audioObject.AddComponent<AudioSource>(); obj.clip = definition.AudioClip; obj.volume = definition.AudioVolume; obj.spatialBlend = 1f; obj.Play(); Object.Destroy((Object)(object)_audioObject, Math.Max(0.1f, definition.AudioClip.length + 0.1f)); } } } internal void Retarget(Vector3 targetPosition) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) if (_visible) { _geometry = _geometry.Retarget(targetPosition); Draw(); } } internal void UpdateGeometry(ResolvedAbilityGeometry geometry) { if (_visible) { _geometry = geometry; Draw(); } } internal void Tick(double currentServerTime) { TickAfterglow(currentServerTime); if (_visible && _definition != null) { if (currentServerTime >= _resolveTime) { BeginAfterglow(currentServerTime); Hide(); } else { double num = Math.Max(0.001, _resolveTime - _telegraphTime); _progress = Mathf.Clamp01((float)((currentServerTime - _telegraphTime) / num)); UpdateTelegraphStyle(currentServerTime); } } } internal void Hide() { _visible = false; _definition = null; if ((Object)(object)_root != (Object)null) { _root.SetActive(false); } if ((Object)(object)_audioObject != (Object)null) { AudioSource component = _audioObject.GetComponent<AudioSource>(); if ((Object)(object)component != (Object)null) { component.Stop(); } Object.Destroy((Object)(object)_audioObject); _audioObject = null; } } private void EnsureRig() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Expected O, but got Unknown if (!((Object)(object)_root != (Object)null)) { _root = new GameObject("BossMakerAPI.AbilityTelegraph"); _root.SetActive(false); _root.transform.SetParent(_owner, false); EnsureMaterial(); _fillRenderer = CreateMeshLayer("DangerFill", _root.transform, out _fillMesh, -2); _progressRenderer = CreateMeshLayer("ResolveProgress", _root.transform, out _progressMesh, -1); _primary = CreateLine("Primary", _root.transform, 1); _secondary = CreateLine("Secondary", _root.transform, 2); _pattern = CreateLine("ColorBlindPattern", _root.transform, 4); _sweep = CreateLine("ResolveSweep", _root.transform, 3); _afterglowRoot = new GameObject("BossMakerAPI.AbilityImpactAfterglow"); _afterglowRoot.SetActive(false); _afterglowRoot.transform.SetParent(_owner, false); _afterglowRenderer = CreateMeshLayer("ImpactFill", _afterglowRoot.transform, out _afterglowMesh, 5); _impact = CreateLine("ImpactEdge", _afterglowRoot.transform, 6); _afterglowRoot.SetActive(false); } } private void EnsureMaterial() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_material != (Object)null) { return; } Shader val = FindPresentationShader(); if (!((Object)(object)val == (Object)null)) { _material = new Material(val) { name = "BossMakerAPI.TelegraphMaterial" }; if (_material.HasProperty("_Color")) { _material.SetColor("_Color", Color.white); } if (_material.HasProperty("_BaseColor")) { _material.SetColor("_BaseColor", Color.white); } if (_material.HasProperty("_Surface")) { _material.SetFloat("_Surface", 1f); } if (_material.HasProperty("_SrcBlend")) { _material.SetFloat("_SrcBlend", 5f); } if (_material.HasProperty("_DstBlend")) { _material.SetFloat("_DstBlend", 10f); } if (_material.HasProperty("_ZWrite")) { _material.SetFloat("_ZWrite", 0f); } if (_material.HasProperty("_Cull")) { _material.SetFloat("_Cull", 0f); } _material.SetOverrideTag("RenderType", "Transparent"); _material.EnableKeyword("_SURFACE_TYPE_TRANSPARENT"); _material.renderQueue = 3000; } } private MeshRenderer CreateMeshLayer(string name, Transform parent, out Mesh mesh, int sortingOrder) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown GameObject val = new GameObject(name); val.transform.SetParent(parent, false); MeshFilter val2 = val.AddComponent<MeshFilter>(); MeshRenderer val3 = val.AddComponent<MeshRenderer>(); ((Renderer)val3).receiveShadows = false; ((Renderer)val3).shadowCastingMode = (ShadowCastingMode)0; ((Renderer)val3).sortingOrder = sortingOrder; if ((Object)(object)_material != (Object)null) { ((Renderer)val3).sharedMaterial = _material; } mesh = new Mesh { name = "BossMakerAPI." + name }; mesh.MarkDynamic(); val2.sharedMesh = mesh; return val3; } private LineRenderer CreateLine(string name, Transform parent, int sortingOrder) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name); val.transform.SetParent(parent, false); LineRenderer val2 = val.AddComponent<LineRenderer>(); val2.positionCount = 0; val2.useWorldSpace = true; ((Renderer)val2).receiveShadows = false; ((Renderer)val2).shadowCastingMode = (ShadowCastingMode)0; ((Renderer)val2).sortingOrder = sortingOrder; val2.numCapVertices = 2; val2.numCornerVertices = 2; if ((Object)(object)_material != (Object)null) { ((Renderer)val2).sharedMaterial = _material; } return val2; } private void Draw() { //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Expected I4, but got Unknown //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_primary == (Object)null || (Object)(object)_secondary == (Object)null) { return; } if ((Object)(object)_fillMesh != (Object)null && (Object)(object)_root != (Object)null) { BuildAreaMesh(_fillMesh, _root.transform, _geometry, 1f, 1f, 0.006f); } DrawProgress(); BossAbilityGeometryKind kind = _geometry.Kind; switch ((int)kind) { case 0: DrawRing(_primary, _geometry.Origin, _geometry.Primary, 0.018f); DrawRing(_secondary, _geometry.Origin, Math.Max(0.05f, _geometry.Primary - 0.12f), 0.02f); break; case 1: DrawRing(_primary, _geometry.Origin, _geometry.Primary, 0.018f); DrawRing(_secondary, _geometry.Origin, _geometry.Secondary, 0.02f); break; case 2: DrawRectangle(_primary, _geometry.Origin, _geometry.Direction, _geometry.Primary, _geometry.Secondary, 0.018f); DrawCenterLine(_secondary, _geometry.Origin, _geometry.Direction, _geometry.Primary, 0.02f); break; case 3: DrawCone(_primary, _geometry.Origin, _geometry.Direction, _geometry.Primary, _geometry.Secondary, 0.018f); DrawCenterLine(_secondary, _geometry.Origin, _geometry.Direction, _geometry.Primary, 0.02f); break; } if ((Object)(object)_pattern != (Object)null) { LineRenderer? pattern = _pattern; BossTelegraphDefinition? definition = _definition; ((Renderer)pattern).enabled = definition != null && definition.UseColorBlindPattern; if (((Renderer)_pattern).enabled) { DrawAccessibilityPattern(_pattern, _geometry); } } DrawSweep(); } private static void DrawAccessibilityPattern(LineRenderer line, ResolvedAbilityGeometry geometry) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected I4, but got Unknown //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Invalid comparison between Unknown and I4 //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) line.loop = false; BossAbilityGeometryKind kind = geometry.Kind; switch ((int)kind) { case 0: case 1: { float primary = geometry.Primary; float num5 = (((int)geometry.Kind == 1) ? geometry.Secondary : (geometry.Primary * 0.72f)); line.positionCount = 32; for (int j = 0; j < 32; j++) { float num6 = (float)j * MathF.PI * 2f / 31f; float num7 = ((j % 2 == 0) ? num5 : primary); line.SetPosition(j, geometry.Origin + new Vector3(Mathf.Cos(num6) * num7, 0.015f, Mathf.Sin(num6) * num7)); } break; } case 2: { Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(0f - geometry.Direction.z, 0f, geometry.Direction.x); line.positionCount = 16; for (int k = 0; k < 16; k++) { float num8 = (float)k / 15f; float num9 = ((k % 2 == 0) ? (-1f) : 1f) * geometry.Secondary * 0.42f; line.SetPosition(k, geometry.Origin + geometry.Direction * (geometry.Primary * num8) + val * num9 + Vector3.up * 0.015f); } break; } case 3: { float num = Mathf.Atan2(geometry.Direction.z, geometry.Direction.x); float num2 = geometry.Secondary * 0.5f * (MathF.PI / 180f); line.positionCount = 16; for (int i = 0; i < 16; i++) { float num3 = Mathf.Lerp(num - num2, num + num2, (float)i / 15f); float num4 = ((i % 2 == 0) ? (geometry.Primary * 0.35f) : (geometry.Primary * 0.92f)); line.SetPosition(i, geometry.Origin + new Vector3(Mathf.Cos(num3) * num4, 0.015f, Mathf.Sin(num3) * num4)); } break; } } } private void UpdateTelegraphStyle(double currentServerTime) { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) if (_definition != null) { float num = Smooth(_progress); float num2 = (float)(currentServerTime * Math.PI * 2.0 * (double)_definition.PulseFrequency); float num3 = 0.5f + 0.5f * Mathf.Sin(num2); float num4 = Mathf.Lerp(0.82f, 1.16f, num3); float num5 = Mathf.Lerp(0.48f, 1f, num) * num4; float num6 = _definition.BorderWidth * Mathf.Lerp(0.82f, 1.22f, num); ApplyStyle(_primary, _definition.Color, num5, num6, secondary: false); ApplyStyle(_secondary, _definition.BorderColor, num5 * 0.92f, num6 * 0.55f, secondary: true); ApplyStyle(_pattern, _definition.BorderColor, Mathf.Lerp(0.62f, 1f, num), num6 * 0.48f, secondary: true); ApplyStyle(_sweep, _definition.BorderColor, Mathf.Lerp(0.55f, 1f, num), num6 * 0.95f, secondary: false); ApplyMeshStyle(_fillRenderer, _definition.Color, Mathf.Lerp(0.1f, 0.24f, num) * Mathf.Lerp(0.86f, 1.08f, num3)); ApplyMeshStyle(_progressRenderer, Color.Lerp(_definition.Color, _definition.BorderColor, 0.35f), Mathf.Lerp(0.12f, 0.34f, num)); if ((Object)(object)_fillMesh != (Object)null && (Object)(object)_root != (Object)null) { BuildAreaMesh(_fillMesh, _root.transform, _geometry, 1f, 1f, 0.006f); } DrawProgress(); DrawSweep(); } } private void DrawProgress() { if (!((Object)(object)_progressMesh == (Object)null) && !((Object)(object)_root == (Object)null)) { BuildAreaMesh(_progressMesh, _root.transform, _geometry, Math.Max(0.001f, _progress), 1f, 0.012f); } } private void DrawSweep() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected I4, but got Unknown //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_sweep == (Object)null)) { float num = Smooth(_progress); BossAbilityGeometryKind kind = _geometry.Kind; switch ((int)kind) { case 0: DrawRing(_sweep, _geometry.Origin, Mathf.Lerp(_geometry.Primary * 1.04f, Math.Max(0.08f, _geometry.Primary * 0.1f), num), 0.022f); break; case 1: DrawRing(_sweep, _geometry.Origin, Mathf.Lerp(_geometry.Primary, _geometry.Secondary, num), 0.022f); break; case 2: DrawCrossbar(_sweep, _geometry.Origin, _geometry.Direction, _geometry.Primary * num, _geometry.Secondary, 0.022f); break; case 3: DrawConeArc(_sweep, _geometry.Origin, _geometry.Direction, Math.Max(0.08f, _geometry.Primary * num), _geometry.Secondary, 0.022f); break; } } } private void BeginAfterglow(double currentServerTime) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) if (_definition != null && !((Object)(object)_afterglowRoot == (Object)null) && !(currentServerTime > _resolveTime + 0.32)) { _afterglowGeometry = _geometry; _afterglowColor = _definition.Color; _afterglowBorderColor = _definition.BorderColor; _afterglowBorderWidth = _definition.BorderWidth; _afterglowStartTime = _resolveTime; _afterglowEndTime = _resolveTime + 0.32; _afterglowVisible = true; _afterglowRoot.SetActive(true); TickAfterglow(currentServerTime); } } private void TickAfterglow(double currentServerTime) { //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) if (!_afterglowVisible) { return; } if (currentServerTime >= _afterglowEndTime || (Object)(object)_afterglowRoot == (Object)null) { ClearAfterglow(); return; } float num = Mathf.Clamp01((float)((currentServerTime - _afterglowStartTime) / 0.32)); float num2 = 1f - (1f - num) * (1f - num); float num3 = 1f - Smooth(num); float expansion = 1f + num2 * 0.1f; if ((Object)(object)_afterglowMesh != (Object)null) { BuildAreaMesh(_afterglowMesh, _afterglowRoot.transform, _afterglowGeometry, 1f, expansion, 0.024f); } ApplyMeshStyle(_afterglowRenderer, Color.Lerp(_afterglowColor, _afterglowBorderColor, 0.55f), 0.42f * num3); DrawGeometryOutline(_impact, _afterglowGeometry, expansion, 0.028f); ApplyStyle(_impact, _afterglowBorderColor, num3, _afterglowBorderWidth * Mathf.Lerp(1.45f, 0.55f, num2), secondary: false); } private void ClearAfterglow() { _afterglowVisible = false; if ((Object)(object)_afterglowRoot != (Object)null) { _afterglowRoot.SetActive(false); } if ((Object)(object)_afterglowMesh != (Object)null) { _afterglowMesh.Clear(false); } if ((Object)(object)_impact != (Object)null) { _impact.positionCount = 0; } } private void BuildAreaMesh(Mesh mesh, Transform localSpace, ResolvedAbilityGeometry geometry, float fraction, float expansion, float height) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected I4, but got Unknown //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) _scratchVertices.Clear(); _scratchTriangles.Clear(); _scratchColors.Clear(); fraction = Mathf.Clamp01(fraction); expansion = Math.Max(0.01f, expansion); BossAbilityGeometryKind kind = geometry.Kind; switch ((int)kind) { case 0: AddDisc(localSpace, geometry.Origin, geometry.Primary * expansion, fraction, height); break; case 1: AddAnnulus(localSpace, geometry.Origin, geometry.Secondary * expansion, geometry.Primary * expansion, fraction, height); break; case 2: AddRectangle(localSpace, geometry.Origin, geometry.Direction, geometry.Primary * expansion * fraction, geometry.Secondary * expansion, height); break; case 3: AddConeSector(localSpace, geometry.Origin, geometry.Direction, geometry.Primary * expansion, geometry.Secondary, fraction, height); break; } mesh.Clear(false); if (_scratchVertices.Count != 0) { for (int i = 0; i < _scratchVertices.Count; i++) { _scratchColors.Add(Color.white); } mesh.SetVertices(_scratchVertices); mesh.SetColors(_scratchColors); mesh.SetTriangles(_scratchTriangles, 0, true); } } private void AddDisc(Transform localSpace, Vector3 center, float radius, float fraction, float height) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) float num = Math.Max(0.001f, fraction) * MathF.PI * 2f; _scratchVertices.Add(ToLocal(localSpace, center, height)); for (int i = 0; i <= 64; i++) { float num2 = -MathF.PI / 2f + num * (float)i / 64f; _scratchVertices.Add(ToLocal(localSpace, center + new Vector3(Mathf.Cos(num2) * radius, 0f, Mathf.Sin(num2) * radius), height)); if (i != 0) { _scratchTriangles.Add(0); _scratchTriangles.Add(i + 1); _scratchTriangles.Add(i); } } } private void AddAnnulus(Transform localSpace, Vector3 center, float innerRadius, float outerRadius, float fraction, float height) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) float num = Math.Max(0.001f, fraction) * MathF.PI * 2f; Vector3 val = default(Vector3); for (int i = 0; i <= 64; i++) { float num2 = -MathF.PI / 2f + num * (float)i / 64f; ((Vector3)(ref val))..ctor(Mathf.Cos(num2), 0f, Mathf.Sin(num2)); _scratchVertices.Add(ToLocal(localSpace, center + val * innerRadius, height)); _scratchVertices.Add(ToLocal(localSpace, center + val * outerRadius, height)); if (i != 0) { int num3 = (i - 1) * 2; int item = num3 + 1; int num4 = i * 2; int item2 = num4 + 1; _scratchTriangles.Add(num3); _scratchTriangles.Add(num4); _scratchTriangles.Add(item); _scratchTriangles.Add(item); _scratchTriangles.Add(num4); _scratchTriangles.Add(item2); } } } private void AddRectangle(Transform localSpace, Vector3 origin, Vector3 direction, float length, float width, float height) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) Vector3 val = new Vector3(0f - direction.z, 0f, direction.x) * (width * 0.5f); Vector3 val2 = origin + direction * Math.Max(0.001f, length); _scratchVertices.Add(ToLocal(localSpace, origin - val, height)); _scratchVertices.Add(ToLocal(localSpace, origin + val, height)); _scratchVertices.Add(ToLocal(localSpace, val2 + val, height)); _scratchVertices.Add(ToLocal(localSpace, val2 - val, height)); _scratchTriangles.Add(0); _scratchTriangles.Add(2); _scratchTriangles.Add(1); _scratchTriangles.Add(0); _scratchTriangles.Add(3); _scratchTriangles.Add(2); } private void AddConeSector(Transform localSpace, Vector3 origin, Vector3 direction, float radius, float angleDegrees, float fraction, float height) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Atan2(direction.z, direction.x); float num2 = angleDegrees * 0.5f * (MathF.PI / 180f); float num3 = num - num2; float num4 = angleDegrees * (MathF.PI / 180f) * Math.Max(0.001f, fraction); _scratchVertices.Add(ToLocal(localSpace, origin, height)); for (int i = 0; i <= 64; i++) { float num5 = num3 + num4 * (float)i / 64f; _scratchVertices.Add(ToLocal(localSpace, origin + new Vector3(Mathf.Cos(num5) * radius, 0f, Mathf.Sin(num5) * radius), height)); if (i != 0) { _scratchTriangles.Add(0); _scratchTriangles.Add(i + 1); _scratchTriangles.Add(i); } } } private void ApplyMeshStyle(MeshRenderer? renderer, Color color, float alphaMultiplier) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown if (!((Object)(object)renderer == (Object)null)) { color.a *= Mathf.Clamp01(alphaMultiplier); if (_propertyBlock == null) { _propertyBlock = new MaterialPropertyBlock(); } _propertyBlock.Clear(); _propertyBlock.SetColor("_Color", color); _propertyBlock.SetColor("_BaseColor", color); ((Renderer)renderer).SetPropertyBlock(_propertyBlock); } } private static Vector3 ToLocal(Transform localSpace, Vector3 worldPosition, float height) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) return localSpace.InverseTransformPoint(worldPosition + Vector3.up * height); } private static float Smooth(float value) { return value * value * (3f - 2f * value); } private static void DrawGeometryOutline(LineRenderer? line, ResolvedAbilityGeometry geometry, float expansion, float height) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected I4, but got Unknown //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)line == (Object)null)) { BossAbilityGeometryKind kind = geometry.Kind; switch ((int)kind) { case 0: case 1: DrawRing(line, geometry.Origin, geometry.Primary * expansion, height); break; case 2: DrawRectangle(line, geometry.Origin, geometry.Direction, geometry.Primary * expansion, geometry.Secondary * expansion, height); break; case 3: DrawCone(line, geometry.Origin, geometry.Direction, geometry.Primary * expansion, geometry.Secondary, height); break; } } } private static void DrawRing(LineRenderer line, Vector3 center, float radius, float height = 0f) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) line.loop = true; line.positionCount = 64; for (int i = 0; i < 64; i++) { float num = (float)i * MathF.PI * 2f / 64f; line.SetPosition(i, center + new Vector3(Mathf.Cos(num) * radius, height, Mathf.Sin(num) * radius)); } } private static void DrawRectangle(LineRenderer line, Vector3 origin, Vector3 direction, float length, float width, float height = 0f) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) Vector3 val = new Vector3(0f - direction.z, 0f, direction.x) * (width * 0.5f); Vector3 val2 = origin + direction * length; line.loop = true; line.positionCount = 4; Vector3 val3 = Vector3.up * height; line.SetPosition(0, origin - val + val3); line.SetPosition(1, origin + val + val3); line.SetPosition(2, val2 + val + val3); line.SetPosition(3, val2 - val + val3); } private static void DrawCenterLine(LineRenderer line, Vector3 origin, Vector3 direction, float length, float height = 0f) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) line.loop = false; line.positionCount = 2; Vector3 val = Vector3.up * height; line.SetPosition(0, origin + val); line.SetPosition(1, origin + direction * length + val); } private static void DrawCone(LineRenderer line, Vector3 origin, Vector3 direction, float radius, float angleDegrees, float height = 0f) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Atan2(direction.z, direction.x); float num2 = angleDegrees * 0.5f * (MathF.PI / 180f); line.loop = true; line.positionCount = 66; Vector3 val = Vector3.up * height; line.SetPosition(0, origin + val); for (int i = 0; i <= 64; i++) { float num3 = Mathf.Lerp(num - num2, num + num2, (float)i / 64f); line.SetPosition(i + 1, origin + new Vector3(Mathf.Cos(num3) * radius, height, Mathf.Sin(num3) * radius)); } } private static void DrawCrossbar(LineRenderer line, Vector3 origin, Vector3 direction, float distance, float width, float height) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) Vector3 val = origin + direction * distance + Vector3.up * height; Vector3 val2 = new Vector3(0f - direction.z, 0f, direction.x) * (width * 0.5f); line.loop = false; line.positionCount = 2; line.SetPosition(0, val - val2); line.SetPosition(1, val + val2); } private static void DrawConeArc(LineRenderer line, Vector3 origin, Vector3 direction, float radius, float angleDegrees, float height) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Atan2(direction.z, direction.x); float num2 = angleDegrees * 0.5f * (MathF.PI / 180f); line.loop = false; line.positionCount = 33; for (int i = 0; i <= 32; i++) { float num3 = Mathf.Lerp(num - num2, num + num2, (float)i / 32f); line.SetPosition(i, origin + new Vector3(Mathf.Cos(num3) * radius, height, Mathf.Sin(num3) * radius)); } } private static void ApplyStyle(LineRenderer? line, Color color, float alphaMultiplier, float width, bool secondary) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)line == (Object)null)) { color.a *= Mathf.Clamp01(alphaMultiplier); line.startColor = color; line.endColor = color; line.widthMultiplier = Math.Max(0.01f, secondary ? (width * 0.65f) : width); } } public void Dispose() { Hide(); ClearAfterglow(); if ((Object)(object)_root != (Object)null) { Object.Destroy((Object)(object)_root); } if ((Object)(object)_afterglowRoot != (Object)null) { Object.Destroy((Object)(object)_afterglowRoot); } if ((Object)(object)_fillMesh != (Object)null) { Object.Destroy((Object)(object)_fillMesh); } if ((Object)(object)_progressMesh != (Object)null) { Object.Destroy((Object)(object)_progressMesh); } if ((Object)(object)_afterglowMesh != (Object)null) { Object.Destroy((Object)(object)_afterglowMesh); } if ((Object)(object)_material != (Object)null) { Object.Destroy((Object)(object)_material); } _root = null; _afterglowRoot = null; _primary = null; _secondary = null; _pattern = null; _sweep = null; _impact = null; _fillMesh = null; _progressMesh = null; _afterglowMesh = null; _fillRenderer = null; _progressRenderer = null; _afterglowRenderer = null; _propertyBlock = null; _material = null; } } internal sealed class BossAbilityRuntime : IDisposable { private readonly BossRuntimeController _controller; private readonly BossRuntimeServices _services; private readonly DeterministicAbilityScheduler _scheduler; private readonly ServerCombatActionRunner _combat; private readonly AbilityTelegraphPresenter _telegraph; private readonly Dictionary<string, BossAbilityDefinition> _definitions = new Dictionary<string, BossAbilityDefinition>(StringComparer.Ordinal); private long _lastStartedEventSequence = -1L; private long _lastResolvedEventSequence = -1L; private long _lastCancelledEventSequence = -1L; private long _presentableSequence = -1L; private long _lastPresentedResolutionSequence = -1L; private readonly List<Action> _presentationCleanup = new List<Action>(); private EncounterAbilitySnapshot? _presentationSnapshot; private ResolvedAbilityGeometry _activeGeometry; private EncounterEntityReference? _activeTarget; private bool _activeFollowsTarget; private IReadOnlyCollection<Player> _eligiblePlayers = (IReadOnlyCollection<Player>)(object)Array.Empty<Player>(); private bool _initialized; private bool _disposed; private bool _schedulesReleased; private int _globalScheduleCount; internal BossAbilityRuntime(BossRuntimeController controller, BossRuntimeServices services) { _controller = controller ?? throw new ArgumentNullException("controller"); _services = services ?? throw new ArgumentNullException("services"); _scheduler = new DeterministicAbilityScheduler(controller.DeterministicSeed, services.Options.MinimumTelegraphLeadTimeSeconds); _combat = new ServerCombatActionRunner(delegate(string message) { services.Warning(message); }); _telegraph = new AbilityTelegraphPresenter(((Component)controller).transform, services.Options.MinimumTelegraphLeadTimeSeconds); IndexDefinitions(controller.Definition.Abilities); foreach (BossPhaseDefinition phase in controller.Definition.Phases) { IndexDefinitions(phase.Abilities); } if (InstanceFinder.IsClientStarted && _definitions.Values.Any((BossAbilityDefinition value) => value.Telegraph.Enabled)) { if (_telegraph.TryPrepare(out string diagnostic)) { _services.LogDebug(diagnostic); } else { _services.Warning("Ability telegraph preparation was deferred: " + diagnostic); } } } internal void InitializeAuthoritative(double serverTime) { if (!_initialized && InstanceFinder.IsServerStarted) { BossAbilityDefinition[] array = _controller.Definition.Abilities.Where((BossAbilityDefinition value) => (int)value.Trigger > 0).ToArray(); _globalScheduleCount = _services.ReserveAbilitySchedules(this, array.Length); _scheduler.Initialize(array.Take(_globalScheduleCount).ToArray(), serverTime); _initialized = true; } } internal AbilitySchedulerCheckpoint CaptureCheckpoint(double serverTime) { return _scheduler.CaptureCheckpoint(serverTime); } internal bool TryRestoreCheckpoint(AbilitySchedulerCheckpoint checkpoint, double serverTime, out string error) { int globalScheduleCount = _globalScheduleCount; if (_services.ReserveAbilitySchedules(this, checkpoint.Slots.Count) != checkpoint.Slots.Count) { _services.ReserveAbilitySchedules(this, globalScheduleCount); error = "The saved ability schedules exceed the available global budget."; return false; } if (!_scheduler.TryRestoreCheckpoint(checkpoint, _controller.Definition.Abilities.Where((BossAbilityDefinition value) => (int)value.Trigger > 0).ToArray(), _controller.Definition.Phases, serverTime, out error)) { _services.ReserveAbilitySchedules(this, globalScheduleCount); return false; } _globalScheduleCount = checkpoint.Slots.Count((AbilitySlotCheckpoint slot) => slot.IsGlobal); _initialized = true; EncounterStateSnapshot? encounterState = _controller.EncounterState; _lastStartedEventSequence = (_lastResolvedEventSequence = (_lastCancelledEventSequence = ((encounterState != null) ? encounterState.Sequence : 0))); return true; } internal void PreparePhaseChange(double serverTime) { if (!_initialized || !InstanceFinder.IsServerStarted) { return; } BossAbilityDefinition val = _scheduler.Cancel(clearSchedules: false, serverTime); EncounterStateSnapshot? encounterState = _controller.EncounterState; EncounterAbilitySnapshot val2 = ((encounterState != null) ? encounterState.ActiveAbility : null); if (val != null || (val2 != null && !val2.IsTerminal)) { _combat.CancelAll(); _telegraph.Hide(); CleanupTransientPresentation(); if (val2 != null && !val2.IsTerminal) { _services.TryCancelScheduledAbility(_controller, "The active phase changed."); } if (val2 != null) { _services.CleanupAbilityResources(_controller.EncounterId, val2.AbilityId); } if (val != null && !string.Equals(val.Id, (val2 != null) ? val2.AbilityId : null, StringComparison.Ordinal)) { _services.CleanupAbilityResources(_controller.EncounterId, val.Id); } ClearActiveTarget(); } } internal void EnterPhase(BossPhaseDefinition phase, bool isInitialSpawn, double serverTime) { if (phase == null) { throw new ArgumentNullException("phase"); } if (_initialized && InstanceFinder.IsServerStarted) { BossAbilityDefinition[] array = phase.Abilities.Where((BossAbilityDefinition value) => (int)value.Trigger != 1).ToArray(); int num = _services.ReserveAbilitySchedules(this, _globalScheduleCount + array.Length); int count = Math.Max(0, num - _globalScheduleCount); _scheduler.ActivatePhase(phase.Id, array.Take(count).ToArray(), serverTime, isInitialSpawn); } } internal void Tick(double serverTime) { if (_disposed) { return; } _telegraph.Tick(serverTime); RetargetPresentation(serverTime); TickReplicatedPresentation(serverTime); if (!InstanceFinder.IsServerStarted) { _combat.Tick(serverTime); return; } InitializeAuthoritative(serverTime); if (!_controller.IsAlive || !_controller.IsSpawned) { CancelTerminal("The boss is no longer active."); return; } _combat.Tick(serverTime); for (int i = 0; i < 8; i++) { if (!_scheduler.TryDequeueState(serverTime, (BossConditionDefinition condition) => _services.EvaluateConditionState(_controller, condition, serverTime), (BossAbilityDefinition ability) => _services.CanScheduleAbility(_controller, ability), out AbilitySchedulerCommand command)) { break; } switch (command.Kind) { case AbilitySchedulerCommandKind.BeginTelegraph: if (!TryBegin(command)) { _scheduler.RejectActive(serverTime); } break; case AbilitySchedulerCommandKind.Resolve: _services.ResolveScheduledAbility(_controller, command, _activeGeometry, _activeTarget, _activeFollowsTarget, _eligiblePlayers, _combat); break; case AbilitySchedulerCommandKind.BeginRecovery: _services.TryBeginAbilityRecovery(_controller); break; case AbilitySchedulerCommandKind.Complete: _services.TryCompleteScheduledAbility(_controller); _combat.CancelAll(); _services.CleanupAbilityResources(_controller.EncounterId, command.Ability.Id); ClearActiveTarget(); break; } } } internal void CancelTerminal(string reason) { if (!_disposed) { BossAbilityDefinition val = _scheduler.Cancel(); EncounterStateSnapshot? encounterState = _controller.EncounterState; EncounterAbilitySnapshot val2 = ((encounterState != null) ? encounterState.ActiveAbility : null); ReleaseSchedules(); _combat.CancelAll(); _telegraph.Hide(); CleanupTransientPresentation(); if (InstanceFinder.IsServerStarted && val2 != null && !val2.IsTerminal) { _services.TryCancelScheduledAbility(_controller, reason); } if (val2 != null) { _services.CleanupAbilityResources(_controller.EncounterId, val2.AbilityId); } if (val != null && !string.Equals(val.Id, (val2 != null) ? val2.AbilityId : null, StringComparison.Ordinal)) { _services.CleanupAbilityResources(_controller.EncounterId, val.Id); } ClearActiveTarget(); } } internal void InterruptForStagger(double serverTime) { if (!_disposed && InstanceFinder.IsServerStarted) { EncounterStateSnapshot? encounterState = _controller.EncounterState; EncounterAbilitySnapshot val = ((encounterState != null) ? encounterState.ActiveAbility : null); _scheduler.RejectActive(serverTime); _combat.CancelAll(); _telegraph.Hide(); CleanupTransientPresentation(); if (val != null && !val.IsTerminal) { _services.TryCancelScheduledAbility(_controller, "The boss entered stagger."); } if (val != null) { _services.CleanupAbilityResources(_controller.EncounterId, val.AbilityId); } ClearActiveTarget(); } } internal void ApplyPresentation(EncounterAbilitySnapshot? previous, EncounterAbilitySnapshot? current, bool authoritative, double serverTime) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Invalid comparison between Unknown and I4 //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Invalid comparison between Unknown and I4 //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Invalid comparison between Unknown and I4 if (_disposed) { return; } if (current == null) { _presentationSnapshot = null; _presentableSequence = -1L; _telegraph.Hide(); CleanupTransientPresentation(); ClearActiveTarget(); return; } _presentationSnapshot = current; if ((int)current.State == 0 && current.Sequence != ((previous != null) ? new long?(previous.Sequence) : ((long?)null)) && _definitions.TryGetValue(current.AbilityId, out BossAbilityDefinition value) && ResolvedAbilityGeometry.TryCreate(current, out var geometry)) { _activeGeometry = geometry; _activeTarget = current.Target; _activeFollowsTarget = !value.TargetSelector.LockPositionAtTelegraph; bool flag = authoritative || serverTime <= current.ResolveServerTime - (double)_services.Options.MinimumTelegraphLeadTimeSeconds; if (InstanceFinder.IsClientStarted && flag) { _telegraph.Show(value.Telegraph, geometry, current.TelegraphServerTime, current.ResolveServerTime, authoritative ? current.TelegraphServerTime : serverTime); _presentableSequence = current.Sequence; } if ((authoritative || flag) && TryAdvance(ref _lastStartedEventSequence, current.Sequence) && _controller.SpawnEventPublished) { BossEvents.RaiseAbilityStarted(_controller.Handle, current, authoritative); } } if ((int)current.State != 0) { _telegraph.Hide(); } if (current.IsTerminal) { CleanupTransientPresentation(); _services.CleanupAbilityResources(_controller.EncounterId, current.AbilityId); ClearActiveTarget(); } if (((int)current.State == 3 || (int)current.State == 1) && previous != null && (authoritative || _lastStartedEventSequence == current.Sequence) && TryAdvance(ref _lastResolvedEventSequence, current.Sequence) && _controller.SpawnEventPublished) { BossEvents.RaiseAbilityResolved(_controller.Handle, current, authoritative); } if ((int)current.State == 2 && previous != null && (authoritative || _lastStartedEventSequence == current.Sequence) && TryAdvance(ref _lastCancelledEventSequence, current.Sequence) && _controller.SpawnEventPublished) { BossEvents.RaiseAbilityCancelled(_controller.Handle, current, authoritative, (string)null); } } private bool TryBegin(AbilitySchedulerCommand command) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) AbilityTargetCandidate boss = new AbilityTargetCandidate(_controller.EncounterId, _controller.NetworkObject.ObjectId, ((Component)_controller).transform.position, _controller.Creature); AbilityPlayerCapture abilityPlayerCapture = GameAbilityTargetSource.CapturePlayers(delegate(string message) { _services.Warning(message); }); IReadOnlyList<AbilityTargetCandidate> eligibleCandidates = abilityPlayerCapture.EligibleCandidates; if (!DeterministicAbilityTargetResolver.TrySelect(command.Ability.TargetSelector, boss, eligibleCandidates, _controller.DeterministicSeed, _scheduler.ActivePhaseId, command.Ability.Id, command.AttemptOrdinal, out AbilityTargetSelection selection)) { AbilityTargetFailureDiagnostic abilityTargetFailureDiagnostic = DeterministicAbilityTargetResolver.DiagnoseFailure(command.Ability.TargetSelector, boss, abilityPlayerCapture); _services.Warning("Ability '" + command.Ability.Id + "' skipped: " + abilityTargetFailureDiagnostic.Message + "."); return false; } _activeGeometry = ResolvedAbilityGeometry.Create(command.Ability.Geometry, ((Component)_controller).transform, selection.LockedPosition, command.Ability.TargetSelector.Kind); _activeTarget = selection.Candidate.ToReference(); List<Player> list = new List<Player>(eligibleCandidates.Count); foreach (AbilityTargetCandidate item in eligibleCandidates) { object? runtimeEntity = item.RuntimeEntity; Player val = (Player)((runtimeEntity is Player) ? runtimeEntity : null); if (val != null) { list.Add(val); } } _eligiblePlayers = list; _activeFollowsTarget = !command.Ability.TargetSelector.LockPositionAtTelegraph; return _services.TryBeginScheduledAbility(_controller, command, _activeGeometry, _activeTarget); } private void RetargetPresentation(double serverTime) { //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) if (!_activeFollowsTarget || _presentationSnapshot == null || !_definitions.TryGetValue(_presentationSnapshot.AbilityId, out BossAbilityDefinition value)) { return; } double num = Math.Max(0.0, value.TargetSelector.LockBeforeResolveSeconds); if (num > 0.0 && serverTime + 1E-06 >= _presentationSnapshot.ResolveServerTime - num) { _activeFollowsTarget = false; return; } Vector3 position; if ((int)value.TargetSelector.Kind == 0) { position = ((Component)_controller).transform.position; } else if (_activeTarget == null || !PlayerCombatAnchorAdapter.TryGetPosition(GameAbilityTargetSource.ResolvePlayer(_activeTarget, requireServerEligibility: false), out position)) { return; } _activeGeometry = ResolvedAbilityGeometry.Create(value.Geometry, ((Component)_controller).transform, position, value.TargetSelector.Kind); _telegraph.UpdateGeometry(_activeGeometry); } private void TickReplicatedPresentation(double serverTime) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (InstanceFinder.IsClientStarted) { EncounterAbilitySnapshot presentationSnapshot = _presentationSnapshot; if (presentationSnapshot != null && (int)presentationSnapshot.State == 0 && !(serverTime < presentationSnapshot.ResolveServerTime) && !(serverTime > presentationSnapshot.ResolveEndServerTime + 1E-06) && _presentableSequence == presentationSnapshot.Sequence && TryAdvance(ref _lastPresentedResolutionSequence, presentationSnapshot.Sequence) && _definitions.TryGetValue(presentationSnapshot.AbilityId, out BossAbilityDefinition value)) { CleanupTransientPresentation(); _presentationCleanup.AddRange(_services.ExecuteReplicatedAbilityPresentation(_controller, value, _activeGeometry)); } } } private void CleanupTransientPresentation() { for (int num = _presentationCleanup.Count - 1; num >= 0; num--) { try { _presentationCleanup[num](); } catch (Exception ex) { _services.Warning("Ability presentation cleanup failed: " + ex.Message); } } _presentationCleanup.Clear(); } private void ClearActiveTarget() { _activeTarget = null; _activeFollowsTarget = false; _eligiblePlayers = (IReadOnlyCollection<Player>)(object)Array.Empty<Player>(); } private static bool TryAdvance(ref long highWatermark, long sequence) { if (sequence <= highWatermark) { return false; } highWatermark = sequence; return true; } private void IndexDefinitions(IReadOnlyList<BossAbilityDefinition> definitions) { foreach (BossAbilityDefinition definition in definitions) { if (definition != null && !_definitions.ContainsKey(definition.Id)) { _definitions.Add(definition.Id, definition); } } } public void Dispose() { if (!_disposed) { CancelTerminal("The encounter runtime was disposed."); _disposed = true; _telegraph.Dispose(); CleanupTransientPresentation(); ReleaseSchedules(); _definitions.Clear(); } } internal void ResetClientPresentation() { if (!_disposed) { _presentableSequence = -1L; _telegraph.Dispose(); CleanupTransientPresentation(); } } internal void ResumeClientPresentation(double serverTime) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) EncounterAbilitySnapshot presentationSnapshot = _presentationSnapshot; if (!_disposed && InstanceFinder.IsClientStarted && presentationSnapshot != null && (int)presentationSnapshot.State == 0 && !(serverTime > presentationSnapshot.ResolveServerTime - (double)_services.Options.MinimumTelegraphLeadTimeSeconds) && _definitions.TryGetValue(presentationSnapshot.AbilityId, out BossAbilityDefinition value)) { _telegraph.Show(value.Telegraph, _activeGeometry, presentationSnapshot.TelegraphServerTime, presentationSnapshot.ResolveServerTime, serverTime); _presentableSequence = presentationSnapshot.Sequence; } } internal void DisposeForSessionStop() { if (!_disposed) { _disposed = true; _scheduler.Cancel(); _combat.ClearForSessionStop(); _presentationSnapshot = null; _presentableSequence = -1L; _telegraph.Dispose(); CleanupTransientPresentation(); ReleaseSchedules(); ClearActiveTarget(); _definitions.Clear(); } } private void ReleaseSchedules() { if (!_schedulesReleased) { _schedulesReleased = true; _services.ReleaseAbilitySchedules(this); } } } internal readonly struct BossDashMotionPlan { internal Vector3 InitialVelocity { get; } internal Vector3 PlanarDirection { get; } internal float OwnedPlanarSpeed { get; } internal float MaximumTravelDistance { get; } internal float CoastTravelDistance { get; } internal float CoastDurationSeconds { get; } internal float BrakeDurationSeconds { get; } internal float ExitVelocityFraction { get; } internal bool Bounded { get; } internal BossDashMotionPlan(Vector3 initialVelocity, Vector3 planarDirection, float ownedPlanarSpeed, float maximumTravelDistance, float coastTravelDistance, float coastDurationSeconds, float brakeDurationSeconds, float exitVelocityFraction, bool bounded) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) InitialVelocity = initialVelocity; PlanarDirection = planarDirection; OwnedPlanarSpeed = ownedPlanarSpeed; MaximumTravelDistance = maximumTravelDistance; CoastTravelDistance = coastTravelDistance; CoastDurationSeconds = coastDurationSeconds; BrakeDurationSeconds = brakeDurationSeconds; ExitVelocityFraction = exitVelocityFraction; Bounded = bounded; } } internal static class BossDashMotionPlanner { private const float Epsilon = 1E-06f; internal static bool TryPlan(Vector3 bossPosition, Vector3 currentVelocity, ResolvedAbilityGeometry geometry, DashAction action, out BossDashMotionPlan plan, out string error) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) plan = default(BossDashMotionPlan); error = string.Empty; if (action == null) { error = "DashAction is required."; return false; } Vector3 direction = geometry.Direction; direction.y = 0f; if (!IsFinite(direction) || ((Vector3)(ref direction)).sqrMagnitude <= 1E-06f) { error = "The confirmed ability geometry has no usable planar dash direction."; return false; } ((Vector3)(ref direction)).Normalize(); Vector3 val = currentVelocity * action.RetainedVelocityFraction + direction * action.Impulse + Vector3.up * action.VerticalImpulse; if (!IsFinite(val)) { error = "The requested dash velocity is not finite."; return false; } float num = 1f; if (((Vector3)(ref val)).sqrMagnitude > action.MaximumSpeed * action.MaximumSpeed) { num = action.MaximumSpeed / ((Vector3)(ref val)).magnitude; val *= num; } float num2 = action.Impulse * num; if (action.MaximumTravelDistance <= 0f) { plan = new BossDashMotionPlan(val, direction, num2, 0f, 0f, 0f, 0f, 1f, bounded: false); return true; } Vector3 val2 = geometry.TargetPosition - bossPosition; val2.y = 0f; if (!IsFinite(val2)) { error = "The confirmed dash target position is not finite."; return false; } float num3 = Vector3.Dot(val2, direction); float num4 = Mathf.Max(0f, num3 - action.StoppingDistance); float num5 = Mathf.Min(action.MaximumTravelDistance, num4); if (num5 <= 0.001f) { error = "The confirmed target is already inside the dash stopping distance."; return false; } float num6 = Vector3.Dot(new Vector3(val.x, 0f, val.z), direction); if (!IsFinite(num6) || num6 <= 0.001f) { error = "The requested dash has no usable forward speed."; return false; } float num7 = Mathf.Max(0f, num6 - num2 * (1f - action.ExitVelocityFraction)); float num8 = (num6 + num7) * 0.5f; float num9 = Mathf.Max(0f, action.BrakeDurationSeconds); float num10 = ((num8 <= 0.001f) ? 0f : Mathf.Min(num9, num5 / num8)); float num11 = num8 * num10; float num12 = Mathf.Max(0f, num5 - num11); float coastDurationSeconds = num12 / num6; plan = new BossDashMotionPlan(val, direction, num2, num5, num12, coastDurationSeconds, num10, action.ExitVelocityFraction, bounded: true); return true; } internal static Vector3 RemoveOwnedPlanarVelocity(Vector3 currentVelocity, Vector3 planarDirection, float requestedRemoval, out float removedSpeed) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) removedSpeed = 0f; if (!IsFinite(currentVelocity) || !IsFinite(planarDirection) || !IsFinite(requestedRemoval) || requestedRemoval <= 0f) { return currentVelocity; } planarDirection.y = 0f; if (((Vector3)(ref planarDirection)).sqrMagnitude <= 1E-06f) { return currentVelocity; } ((Vector3)(ref planarDirection)).Normalize(); Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(currentVelocity.x, 0f, currentVelocity.z); float num = Mathf.Max(0f, Vector3.Dot(val, planarDirection)); removedSpeed = Mathf.Min(requestedRemoval, num); return currentVelocity - planarDirection * removedSpeed; } internal static float ProjectedTravelDistance(Vector3 startPosition, Vector3 currentPosition, Vector3 planarDirection) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) if (!IsFinite(startPosition) || !IsFinite(currentPosition) || !IsFinite(planarDirection)) { return 0f; } planarDirection.y = 0f; if (((Vector3)(ref planarDirection)).sqrMagnitude <= 1E-06f) { return 0f; } ((Vector3)(ref planarDirection)).Normalize(); Vector3 val = currentPosition - startPosition; val.y = 0f; return Mathf.Max(0f, Vector3.Dot(val, planarDirection)); } internal static float ClampOwnedPlanarSpeedToAvailableForward(Vector3 currentVelocity, Vector3 planarDirection, float remainingOwnedSpeed) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) if (!IsFinite(currentVelocity) || !IsFinite(planarDirection) || !IsFinite(remainingOwnedSpeed) || remainingOwnedSpeed <= 0f) { return 0f; } planarDirection.y = 0f; if (((Vector3)(ref planarDirection)).sqrMagnitude <= 1E-06f) { return 0f; } ((Vector3)(ref planarDirection)).Normalize(); Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(currentVelocity.x, 0f, currentVelocity.z); float num = Mathf.Max(0f, Vector3.Dot(val, planarDirection)); return Mathf.Min(remainingOwnedSpeed, num); } private static bool IsFinite(Vector3 value) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (IsFinite(value.x) && IsFinite(value.y)) { return IsFinite(value.z); } return false; } private static bool IsFinite(float value) { if (!float.IsNaN(value)) { retur