using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using UnityEngine; using UnityEngine.Events; using UnityEngine.Networking; using UnityEngine.Serialization; namespace RoR2.Projectile; [RequireComponent(typeof(ProjectileController))] public class CleaverProjectile : NetworkBehaviour, IProjectileImpactBehavior { private enum BoomerangState { FlyOut, Transition, FlyBack, Stopped } public float travelSpeed = 40f; public float charge; public float transitionDuration; private float maxFlyStopwatch; public GameObject impactSpark; public GameObject returnImpactSpark; public GameObject crosshairPrefab; public bool canHitCharacters; public bool canHitWorld; public bool returnOnHitWorldOrHover; public ProjectileController projectileController; [SyncVar] private BoomerangState boomerangState; private Transform ownerTransform; private ProjectileDamage projectileDamage; private Rigidbody rigidbody; private float stopwatch; private float fireAge; private float fireFrequency; [FormerlySerializedAs("distanceMultiplier")] public float travelTime = 2f; public UnityEvent onFlyBack; private bool setScale; private bool recallDamageFired; private float stoppedStopwatch = 3f; private ChefController chefController; private ushort predictionId; private bool recalled; private bool hasEffectiveAuthority; private bool disableOnRcall_ReelingCache; public Collider[] disableOnRecall; private static int kCmdCmdDestroyAndCleanup; public BoomerangState NetworkboomerangState { get { return boomerangState; } [param: In] set { ulong newValueAsUlong = (ulong)value; ulong fieldValueAsUlong = (ulong)boomerangState; SetSyncVarEnum(value, newValueAsUlong, ref boomerangState, fieldValueAsUlong, 1u); } } private void Awake() { rigidbody = GetComponent(); projectileController = GetComponent(); projectileDamage = GetComponent(); maxFlyStopwatch = travelTime; } private void Start() { if ((bool)projectileController) { predictionId = projectileController.predictionId; if ((bool)projectileController.owner) { ownerTransform = projectileController.owner.transform; chefController = projectileController.owner.GetComponent(); if ((bool)chefController) { chefController.AddLocalProjectileReference(predictionId, this); if (base.hasAuthority && !NetworkServer.active && chefController.TryRetrieveCachedProjectileFireInfo(out var fireProjectileInfo)) { ApplyCachedFireProjectileInfo(fireProjectileInfo); } } } } hasEffectiveAuthority = Util.HasEffectiveAuthority(chefController.gameObject); } private void ApplyCachedFireProjectileInfo(FireProjectileInfo fireProjectileInfo) { ProjectileDamage component = GetComponent(); TeamFilter component2 = GetComponent(); ProjectileSimple component3 = GetComponent(); if ((bool)component2) { component2.teamIndex = TeamComponent.GetObjectTeam(fireProjectileInfo.owner); } if (fireProjectileInfo.useSpeedOverride && (bool)component3) { component3.desiredForwardSpeed = fireProjectileInfo.speedOverride; } if ((bool)component) { component.damage = fireProjectileInfo.damage; component.force = fireProjectileInfo.force; component.crit = fireProjectileInfo.crit; component.damageColorIndex = fireProjectileInfo.damageColorIndex; if (fireProjectileInfo.damageTypeOverride.HasValue) { component.damageType = fireProjectileInfo.damageTypeOverride.Value; } } projectileController.DispatchOnInitialized(); } private void OnEnable() { recalled = false; recallDamageFired = false; } public void OnProjectileImpact(ProjectileImpactInfo impactInfo) { if (!canHitWorld) { return; } if (impactInfo.collider.gameObject.layer == LayerIndex.world.intVal) { if (returnOnHitWorldOrHover) { NetworkboomerangState = BoomerangState.FlyBack; } else { NetworkboomerangState = BoomerangState.Stopped; } } if (boomerangState != BoomerangState.Stopped) { EffectManager.SimpleImpactEffect(recalled ? returnImpactSpark : impactSpark, impactInfo.estimatedPointOfImpact, -base.transform.forward, transmit: true); } } private bool Reel() { Vector3 vector = projectileController.owner.transform.position - base.transform.position; _ = vector.normalized; float magnitude = vector.magnitude; if (!chefController) { chefController = projectileController.owner.GetComponent(); } return magnitude <= 2f; } public void FixedUpdate() { if (!hasEffectiveAuthority) { return; } if (!setScale) { setScale = true; } if (!projectileController.owner) { Object.Destroy(base.gameObject); return; } if (!chefController) { chefController = projectileController.owner.GetComponent(); } recalled = recalled || chefController.recallCleaver || boomerangState == BoomerangState.FlyBack; if ((bool)chefController && recalled && boomerangState != BoomerangState.Transition) { NetworkboomerangState = BoomerangState.FlyBack; } if (returnOnHitWorldOrHover && boomerangState == BoomerangState.Stopped) { NetworkboomerangState = BoomerangState.FlyBack; recalled = true; } if (!recalled) { ToggleThrowOnlyObjects(isReeling: false); switch (boomerangState) { case BoomerangState.FlyOut: rigidbody.velocity = travelSpeed * base.transform.forward; stopwatch += Time.fixedDeltaTime; if (stopwatch >= maxFlyStopwatch) { stopwatch = 0f; NetworkboomerangState = BoomerangState.Stopped; } break; case BoomerangState.Transition: { stopwatch += Time.fixedDeltaTime; float num = stopwatch / transitionDuration; Vector3 vector = CalculatePullDirection(); rigidbody.velocity = Vector3.Lerp(travelSpeed * base.transform.forward, travelSpeed * vector, num); if (num >= 0.1f) { NetworkboomerangState = BoomerangState.FlyBack; onFlyBack?.Invoke(); } break; } case BoomerangState.Stopped: chefController = projectileController.owner.GetComponent(); rigidbody.velocity = new Vector3(0f, 0f, 0f); stopwatch += Time.deltaTime; if (stopwatch > stoppedStopwatch) { CallCmdDestroyAndCleanup(); } break; case BoomerangState.FlyBack: break; } return; } ToggleThrowOnlyObjects(isReeling: true); if (!recallDamageFired) { rigidbody.velocity = new Vector3(0f, 0f, 0f); GetComponent().ResetOverlapAttack(); projectileDamage.damage *= 1.5f; recallDamageFired = true; } bool num2 = Reel(); Vector3 vector2 = CalculatePullDirection(); rigidbody.velocity = travelSpeed * vector2; if (num2) { if (chefController != null) { chefController.NetworkcatchDirtied = true; } CallCmdDestroyAndCleanup(); } } private void ToggleThrowOnlyObjects(bool isReeling) { if (disableOnRcall_ReelingCache != isReeling) { int num = disableOnRecall.Length; for (int i = 0; i < num; i++) { disableOnRecall[i].enabled = !isReeling; } disableOnRcall_ReelingCache = isReeling; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] [Command] public void CmdDestroyAndCleanup() { Object.Destroy(base.gameObject); } private Vector3 CalculatePullDirection() { if ((bool)projectileController.owner) { return (projectileController.owner.transform.position - base.transform.position).normalized; } return base.transform.forward; } private void OnDestroy() { if ((bool)chefController) { chefController.RemoveLocalProjectileReference(predictionId); } } private void UNetVersion() { } protected static void InvokeCmdCmdDestroyAndCleanup(NetworkBehaviour obj, NetworkReader reader) { if (!NetworkServer.active) { Debug.LogError("Command CmdDestroyAndCleanup called on client."); } else { ((CleaverProjectile)obj).CmdDestroyAndCleanup(); } } public void CallCmdDestroyAndCleanup() { if (!NetworkClient.active) { Debug.LogError("Command function CmdDestroyAndCleanup called on server."); return; } if (base.isServer) { CmdDestroyAndCleanup(); return; } NetworkWriter networkWriter = new NetworkWriter(); networkWriter.Write((short)0); networkWriter.Write((short)5); networkWriter.WritePackedUInt32((uint)kCmdCmdDestroyAndCleanup); networkWriter.Write(GetComponent().netId); SendCommandInternal(networkWriter, 0, "CmdDestroyAndCleanup"); } static CleaverProjectile() { kCmdCmdDestroyAndCleanup = 978502080; NetworkBehaviour.RegisterCommandDelegate(typeof(CleaverProjectile), kCmdCmdDestroyAndCleanup, InvokeCmdCmdDestroyAndCleanup); NetworkCRC.RegisterBehaviour("CleaverProjectile", 0); } public override bool OnSerialize(NetworkWriter writer, bool forceAll) { if (forceAll) { writer.Write((int)boomerangState); return true; } bool flag = false; if ((base.syncVarDirtyBits & (true ? 1u : 0u)) != 0) { if (!flag) { writer.WritePackedUInt32(base.syncVarDirtyBits); flag = true; } writer.Write((int)boomerangState); } if (!flag) { writer.WritePackedUInt32(base.syncVarDirtyBits); } return flag; } public override void OnDeserialize(NetworkReader reader, bool initialState) { if (initialState) { boomerangState = (BoomerangState)reader.ReadInt32(); return; } int num = (int)reader.ReadPackedUInt32(); if (((uint)num & (true ? 1u : 0u)) != 0) { boomerangState = (BoomerangState)reader.ReadInt32(); } } public override void PreStartClient() { } }