using System; using Cielonos.MainGame.Characters; using UnityEngine; namespace Cielonos.MainGame { public class BoomerangMoveSubmodule : MoveSubmoduleBase { public Transform projectile => owner.topParent; public CharacterBase target; public float moveSpeed; public float moveAcceleration; public float angularSpeed; public float angularAcceleration; public bool isReturning; public float catchRadius; public float autoReturnTime; // Time in seconds after which it automatically returns if not triggered manually public bool autoConnect; public float detectRadius; public bool keepFlat; public bool straightThrowInitially; public float straightThrowDuration; public Action onCatchAction; private float _timer; private float deltaTime => owner.creator.selfTimeSm.DeltaTime; private float projectileSpeedMultiplier => owner.creator.attributeSm[CharacterAttribute.ProjectileSpeedMultiplier]; public BoomerangMoveSubmodule(AttackAreaBase attackArea, CharacterBase target, float moveSpeed, float moveAcceleration, float angularSpeed, float angularAcceleration, Vector3 initialDirection, float autoReturnTime = float.MaxValue, float catchRadius = 1.5f, bool autoConnect = true, float detectRadius = 20f, bool keepFlat = true, bool straightThrowInitially = false, float straightThrowDuration = 1f, bool stopWhenHit = false) : base(attackArea, stopWhenHit) { this.target = target; this.moveSpeed = moveSpeed; this.moveAcceleration = moveAcceleration; this.angularSpeed = angularSpeed; this.angularAcceleration = angularAcceleration; this.autoReturnTime = autoReturnTime; this.catchRadius = catchRadius; this.autoConnect = autoConnect; this.detectRadius = detectRadius; this.keepFlat = keepFlat; this.straightThrowInitially = straightThrowInitially; this.straightThrowDuration = straightThrowDuration; this.isReturning = false; projectile.forward = initialDirection; } public BoomerangMoveSubmodule(AttackAreaBase attackArea, CharacterBase target, float moveSpeed, float angularSpeed, Vector3 initialDirection) : this(attackArea, target, moveSpeed, 0f, angularSpeed, 0f, initialDirection) { } public BoomerangMoveSubmodule WithAcceleration(float moveAcceleration, float angularAcceleration) { this.moveAcceleration = moveAcceleration; this.angularAcceleration = angularAcceleration; return this; } public BoomerangMoveSubmodule WithAutoReturn(float autoReturnTime) { this.autoReturnTime = autoReturnTime; return this; } public BoomerangMoveSubmodule WithCatch(float catchRadius, Action onCatchAction = null) { this.catchRadius = catchRadius; if (onCatchAction != null) { this.onCatchAction = onCatchAction; } return this; } public BoomerangMoveSubmodule WithTargeting(bool autoConnect, float detectRadius = 20f) { this.autoConnect = autoConnect; this.detectRadius = detectRadius; return this; } public BoomerangMoveSubmodule WithKeepFlat(bool keepFlat) { this.keepFlat = keepFlat; return this; } public BoomerangMoveSubmodule WithStraightThrow(bool straightThrowInitially, float straightThrowDuration = 1f) { this.straightThrowInitially = straightThrowInitially; this.straightThrowDuration = straightThrowDuration; return this; } public BoomerangMoveSubmodule WithStopWhenHit(bool stopWhenHit) { this.stopWhenHit = stopWhenHit; return this; } public BoomerangMoveSubmodule WithTimeScale(float timeScaleCoefficient) { this.timeScaleCoefficient = timeScaleCoefficient; return this; } public void TriggerReturn(float moveSpeed = -1f, float moveAcceleration = -1f, float angularSpeed = -1f, float angularAcceleration = -1f) { if (isReturning || !canMove) return; isReturning = true; target = owner.creator; // Start tracing the player/creator if (moveSpeed >= 0f) this.moveSpeed = moveSpeed; if (moveAcceleration >= 0f) this.moveAcceleration = moveAcceleration; if (angularSpeed >= 0f) this.angularSpeed = angularSpeed; if (angularAcceleration >= 0f) this.angularAcceleration = angularAcceleration; autoConnect = false; } public override void Update() { if (!canMove) return; float speedMultiplier = projectileSpeedMultiplier; _timer += deltaTime; // Check auto-return condition if (!isReturning && _timer >= autoReturnTime) { TriggerReturn(); } // Check catch distance if (isReturning && target != null && target.CenterPoint != null) { float distanceToCreator = Vector3.Distance(projectile.position, target.CenterPoint.position); if (distanceToCreator <= catchRadius) { isReturning = false; canMove = false; owner.isEnabling = false; owner.canTriggerHitEvent = false; if (owner.timeSm != null) { owner.timeSm.isEnabling = false; // Stop the lifetime timer } if (onCatchAction != null) { onCatchAction.Invoke(); } else if (owner.timeSm != null) { owner.timeSm.timeOutAction?.Invoke(); } return; } } // Update speed & angular speed moveSpeed += moveAcceleration * deltaTime; moveSpeed = Mathf.Max(moveSpeed, 0f); angularSpeed += angularAcceleration * deltaTime; angularSpeed = Mathf.Max(angularSpeed, 0f); // Handle target selection if target is dead/missing (and not returning) if (!isReturning && !straightThrowInitially && (target == null || target.statusSm.isDead)) { if (autoConnect && owner.creator == MainGameManager.Player) { CharacterBase detectEnemy = CombatManager.EnemySm.GetBestEnemy(detectRadius, owner.transform); if (detectEnemy != null) { target = detectEnemy; } } } // Calculate movement direction bool shouldTrack = isReturning || !straightThrowInitially || (_timer >= straightThrowDuration); if (shouldTrack && target != null && !target.statusSm.isDead && target.CenterPoint != null) { Vector3 direction = target.CenterPoint.position - projectile.position; if (keepFlat) { direction.y = 0f; } if (direction != Vector3.zero) { Quaternion targetRotation = Quaternion.LookRotation(direction); projectile.rotation = Quaternion.RotateTowards(projectile.rotation, targetRotation, angularSpeed * speedMultiplier * deltaTime); } } // Translate position unscaledVelocity = projectile.forward * (moveSpeed * speedMultiplier); scaledVelocity = unscaledVelocity * timeScaleCoefficient; projectile.position += scaledVelocity * deltaTime; } } }