fix in autoweapon draw/ hide, bLock, bMeleeCombat and Input

This commit is contained in:
2025-06-06 12:23:52 +02:00
parent 342f85335a
commit 27d5c63751
7 changed files with 983 additions and 589 deletions

View File

@@ -106100,6 +106100,9 @@ MonoBehaviour:
lastTimeTheButtonWasPressed: 0 lastTimeTheButtonWasPressed: 0
inButtomTimer: 0 inButtomTimer: 0
lockMeleeInput: 0 lockMeleeInput: 0
dashRotationSpeed: 15
dashFacingAngleThreshold: 10
maxDashRotationTime: 0.5
--- !u!114 &9202663234309573817 --- !u!114 &9202663234309573817
MonoBehaviour: MonoBehaviour:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0

View File

@@ -18997,6 +18997,9 @@ MonoBehaviour:
deselectHighlightColor: {r: 0, g: 0, b: 0, a: 1} deselectHighlightColor: {r: 0, g: 0, b: 0, a: 1}
highlightFadeDuration: 0.3 highlightFadeDuration: 0.3
preferSkinnedMeshRenderer: 1 preferSkinnedMeshRenderer: 1
autoLockSelectedTarget: 0
targetLockSystem: {fileID: 0}
manualSwitchCooldownDuration: 0.75
--- !u!4 &6425420852750441961 stripped --- !u!4 &6425420852750441961 stripped
Transform: Transform:
m_CorrespondingSourceObject: {fileID: 5744920779938531276, guid: 60b79e23a507e0c48a94b7e3d5138383, m_CorrespondingSourceObject: {fileID: 5744920779938531276, guid: 60b79e23a507e0c48a94b7e3d5138383,

View File

@@ -1,359 +1,467 @@
using UnityEngine; using UnityEngine;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; // Required for List.AddRange and ToArray using System.Linq;
using Invector.vCharacterController.AI.FSMBehaviour; // For vFSMBehaviourController using Invector.vCharacterController.AI.FSMBehaviour;
using Beyond; // For GameStateManager and Player (ensure Player.cs is in this namespace or adjust) using Beyond;
using System; // For Action using System;
public class AutoTargetting : MonoBehaviour namespace Beyond
{ {
[Header("Targeting Parameters")] public class AutoTargetting : MonoBehaviour
[Tooltip("Maximum distance AutoTargetting will consider an enemy for selection.")]
public float maxTargetingDistance = 20f;
[Tooltip("How often (in seconds) to re-evaluate for a new target during combat.")]
public float targetingInterval = 0.25f;
[Tooltip("Maximum angle (in degrees from player's forward) within which an enemy can be auto-targeted.")]
public float targetingAngleThreshold = 90f;
[Header("Rotation Parameters")]
[Tooltip("Speed at which the player rotates towards the current target when rotation is explicitly called (e.g., by MagicAttacks).")]
public float playerRotationSpeed = 10f; // This will be used by MagicAttacks
[Header("Visuals")]
[Tooltip("Name of the material color property to animate for Fresnel effect.")]
public string materialHighlightPropertyName = "_FresnelColor";
[Tooltip("HDR Color to use for Fresnel highlight when a target is selected (fade-in target).")]
[ColorUsage(true, true)]
public Color highlightColor = Color.white;
[Tooltip("HDR Color to use for Fresnel when a target is deselected (fade-out target).")]
[ColorUsage(true, true)]
public Color deselectHighlightColor = Color.black;
[Tooltip("Duration of the fade in/out animation for the highlight.")]
public float highlightFadeDuration = 0.3f;
[Tooltip("If true, was previously used to prefer SkinnedMeshRenderer. Now GetTargetRenderers collects both SkinnedMeshRenderers and MeshRenderers regardless of this flag. This flag might be repurposed or removed in the future.")]
public bool preferSkinnedMeshRenderer = true; // Note: Its effect on GetTargetRenderers is changed.
public vFSMBehaviourController CurrentTarget { get; private set; }
public event Action<vFSMBehaviourController> OnTargetSelected;
public event Action<vFSMBehaviourController> OnTargetDeselected;
private GameStateManager _gameStateManager;
private Coroutine _targetingLoopCoroutine;
private Dictionary<Material, Color> _originalMaterialColors = new Dictionary<Material, Color>();
private Dictionary<Material, Coroutine> _materialToFadeCoroutineMap = new Dictionary<Material, Coroutine>();
private Transform _playerTransform;
void Start()
{ {
if (Player.Instance == null) // ... (other headers and variables)
{ [Header("Targeting Parameters")]
Debug.LogError("AutoTargetting: Player.Instance is not available at Start! Ensure Player script with static Instance exists and runs before AutoTargetting."); [Tooltip("Maximum distance AutoTargetting will consider an enemy for selection.")]
enabled = false; public float maxTargetingDistance = 20f;
return; [Tooltip("How often (in seconds) to re-evaluate for a new target during combat.")]
} public float targetingInterval = 0.25f;
_playerTransform = Player.Instance.transform; [Tooltip("Maximum angle (in degrees from player's forward) within which an enemy can be auto-targeted.")]
public float targetingAngleThreshold = 90f;
_gameStateManager = GameStateManager.Instance; [Header("Rotation Parameters")]
if (_gameStateManager != null) [Tooltip("Speed at which the player rotates towards the current target when rotation is explicitly called (e.g., by MagicAttacks).")]
{ public float playerRotationSpeed = 10f;
_gameStateManager.m_OnStateChanged.AddListener(HandleGameStateChanged);
HandleGameStateChanged(_gameStateManager.CurrentState);
}
else
{
Debug.LogError("AutoTargetting: GameStateManager.Instance not found! Disabling script.");
enabled = false;
}
}
void OnDestroy() [Header("Visuals")]
{ // ... (visual parameters)
if (_gameStateManager != null) public string materialHighlightPropertyName = "_FresnelColor";
{ [ColorUsage(true, true)]
_gameStateManager.m_OnStateChanged.RemoveListener(HandleGameStateChanged); public Color highlightColor = Color.white;
} [ColorUsage(true, true)]
StopAndClearAllFadeCoroutines(); public Color deselectHighlightColor = Color.black;
if (_targetingLoopCoroutine != null) public float highlightFadeDuration = 0.3f;
{ public bool preferSkinnedMeshRenderer = true;
StopCoroutine(_targetingLoopCoroutine);
_targetingLoopCoroutine = null;
}
}
private void StopAndClearAllFadeCoroutines()
{
foreach (var pair in _materialToFadeCoroutineMap)
{
if (pair.Value != null) StopCoroutine(pair.Value);
}
_materialToFadeCoroutineMap.Clear();
}
private void HandleGameStateChanged(GameStateManager.State newState) [Header("Lock-On Integration")]
{ [Tooltip("If true, automatically locks onto the target selected by AutoTargetting.")]
if (newState == GameStateManager.State.COMBAT) public bool autoLockSelectedTarget = false;
[Tooltip("Reference to the bLockOn script, usually on the player. Will try to find if not set.")]
public bLockOn targetLockSystem;
[Tooltip("How long (in seconds) AutoTargetting will pause after a manual target switch before re-evaluating.")]
public float manualSwitchCooldownDuration = 0.75f; // Cooldown duration
public vFSMBehaviourController CurrentTarget { get; private set; }
public event Action<vFSMBehaviourController> OnTargetSelected;
public event Action<vFSMBehaviourController> OnTargetDeselected;
private GameStateManager _gameStateManager;
private Coroutine _targetingLoopCoroutine;
private Dictionary<Material, Color> _originalMaterialColors = new Dictionary<Material, Color>();
private Dictionary<Material, Coroutine> _materialToFadeCoroutineMap = new Dictionary<Material, Coroutine>();
private Transform _playerTransform;
// Cooldown variables
private bool _manualSwitchCooldownActive = false;
private float _manualSwitchCooldownTimer = 0f;
void Start()
{ {
if (_targetingLoopCoroutine == null) // ... (Start logic mostly the same)
if (Player.Instance == null)
{ {
_targetingLoopCoroutine = StartCoroutine(TargetingLoop()); Debug.LogError("AutoTargetting: Player.Instance is not available at Start!");
enabled = false; return;
}
_playerTransform = Player.Instance.transform;
_gameStateManager = GameStateManager.Instance;
if (_gameStateManager != null)
{
_gameStateManager.m_OnStateChanged.AddListener(HandleGameStateChanged);
HandleGameStateChanged(_gameStateManager.CurrentState);
}
else
{
Debug.LogError("AutoTargetting: GameStateManager.Instance not found!");
enabled = false; return;
}
if (targetLockSystem == null)
{
if (Player.Instance != null) targetLockSystem = Player.Instance.GetComponentInChildren<bLockOn>(true);
if (targetLockSystem == null) targetLockSystem = GetComponent<bLockOn>(); // Fallback
if (targetLockSystem == null)
{
Debug.LogWarning("AutoTargetting: bLockOn system not found. Auto-lock and target sync will be disabled.");
autoLockSelectedTarget = false;
}
}
if (targetLockSystem != null)
{
targetLockSystem.onLockOnTarget.AddListener(HandleLockOnSystemTargetChanged);
targetLockSystem.onUnLockOnTarget.AddListener(HandleLockOnSystemTargetUnlocked);
} }
} }
else
void OnDestroy()
{ {
// ... (Cleanup logic)
if (_gameStateManager != null)
{
_gameStateManager.m_OnStateChanged.RemoveListener(HandleGameStateChanged);
}
StopAndClearAllFadeCoroutines();
if (_targetingLoopCoroutine != null) if (_targetingLoopCoroutine != null)
{ {
StopCoroutine(_targetingLoopCoroutine); StopCoroutine(_targetingLoopCoroutine);
_targetingLoopCoroutine = null; _targetingLoopCoroutine = null;
} }
if (CurrentTarget != null)
if (targetLockSystem != null)
{
targetLockSystem.onLockOnTarget.RemoveListener(HandleLockOnSystemTargetChanged);
targetLockSystem.onUnLockOnTarget.RemoveListener(HandleLockOnSystemTargetUnlocked);
}
}
private void StopAndClearAllFadeCoroutines()
{
foreach (var pair in _materialToFadeCoroutineMap)
{
if (pair.Value != null) StopCoroutine(pair.Value);
}
_materialToFadeCoroutineMap.Clear();
}
private void HandleGameStateChanged(GameStateManager.State newState)
{
if (newState == GameStateManager.State.COMBAT)
{
if (_targetingLoopCoroutine == null)
{
_targetingLoopCoroutine = StartCoroutine(TargetingLoop());
}
}
else
{
if (_targetingLoopCoroutine != null)
{
StopCoroutine(_targetingLoopCoroutine);
_targetingLoopCoroutine = null;
}
if (CurrentTarget != null)
{
SetNewTarget(null, true); // Force immediate update if leaving combat
}
_manualSwitchCooldownActive = false; // Reset cooldown when leaving combat
}
}
private IEnumerator TargetingLoop()
{
while (true)
{
if (_manualSwitchCooldownActive)
{
_manualSwitchCooldownTimer -= targetingInterval; // Or Time.deltaTime if loop is faster
if (_manualSwitchCooldownTimer <= 0)
{
_manualSwitchCooldownActive = false;
// Debug.Log("AutoTargetting: Manual switch cooldown ended.");
}
}
if (!_manualSwitchCooldownActive) // Only update target if cooldown is not active
{
if (_playerTransform == null && Player.Instance != null)
{
_playerTransform = Player.Instance.transform;
}
if (_playerTransform != null)
{
UpdateTarget();
}
}
yield return new WaitForSeconds(targetingInterval);
}
}
public bool IsTargetInAngle(Transform sourceTransform, vFSMBehaviourController targetAI, float angleThreshold)
{
// ... (IsTargetInAngle logic - unchanged)
if (targetAI == null || sourceTransform == null) return false;
Vector3 directionToTarget = (targetAI.transform.position - sourceTransform.position);
directionToTarget.y = 0;
if (directionToTarget.sqrMagnitude < 0.0001f) return true;
directionToTarget.Normalize();
float angle = Vector3.Angle(sourceTransform.forward, directionToTarget);
return angle <= angleThreshold;
}
private void UpdateTarget()
{
if (_playerTransform == null || _gameStateManager == null) return;
if (_manualSwitchCooldownActive) return; // Double check, though TargetingLoop should handle it
vFSMBehaviourController bestCandidate = null;
float minDistanceSqr = maxTargetingDistance * maxTargetingDistance;
HashSet<vFSMBehaviourController> combatControllers = _gameStateManager.GetActiveCombatcontrollers();
if (combatControllers == null || combatControllers.Count == 0)
{
if (CurrentTarget != null) SetNewTarget(null);
return;
}
foreach (var controller in combatControllers)
{
if (controller == null || !controller.gameObject.activeInHierarchy || controller.aiController.currentHealth <= 0) continue;
if (!IsTargetInAngle(_playerTransform, controller, targetingAngleThreshold)) continue;
float distSqr = (controller.transform.position - _playerTransform.position).sqrMagnitude;
if (distSqr <= minDistanceSqr)
{
minDistanceSqr = distSqr;
bestCandidate = controller;
}
}
if (CurrentTarget != bestCandidate)
{
SetNewTarget(bestCandidate);
}
else if (CurrentTarget != null && !IsTargetValid(CurrentTarget))
{ {
SetNewTarget(null); SetNewTarget(null);
} }
} }
}
private IEnumerator TargetingLoop() private bool IsTargetValid(vFSMBehaviourController target)
{
while (true)
{ {
if (_playerTransform == null && Player.Instance != null) // ... (IsTargetValid logic - unchanged)
if (target == null || !target.gameObject.activeInHierarchy || target.aiController.currentHealth <= 0) return false;
if (_playerTransform == null) return false;
if (!IsTargetInAngle(_playerTransform, target, targetingAngleThreshold)) return false;
float distSqr = (target.transform.position - _playerTransform.position).sqrMagnitude;
return distSqr <= (maxTargetingDistance * maxTargetingDistance);
}
/// <summary>
/// Sets a new target for AutoTargetting.
/// </summary>
/// <param name="newTarget">The new target. Can be null.</param>
/// <param name="forceLockSystemUpdate">If true, ManuallySetLockOnTarget will be called even if newTarget is same as CurrentTarget (used for re-assertion).</param>
private void SetNewTarget(vFSMBehaviourController newTarget, bool forceLockSystemUpdate = false)
{
if (_manualSwitchCooldownActive && !forceLockSystemUpdate) // Don't change target if cooldown is active, unless forced
{ {
_playerTransform = Player.Instance.transform; // If cooldown is active and this SetNewTarget call is NOT from a HandleLockOnSystem... event,
// it means AutoTargeting's own loop tried to change target. We prevent this.
// If it IS from HandleLockOnSystem... it means bLockOn initiated, and we need to sync.
// The 'isManualSwitch' parameter in HandleLockOnSystemTargetChanged handles activating cooldown.
return;
} }
if (_playerTransform != null)
if (CurrentTarget == newTarget && !forceLockSystemUpdate)
{ {
UpdateTarget(); // If auto-lock is enabled, ensure the bLockOn system is actually locked on this target.
} if (autoLockSelectedTarget && targetLockSystem != null && CurrentTarget != null)
yield return new WaitForSeconds(targetingInterval);
}
}
public bool IsTargetInAngle(Transform sourceTransform, vFSMBehaviourController targetAI, float angleThreshold)
{
if (targetAI == null || sourceTransform == null)
{
return false;
}
Vector3 directionToTarget = (targetAI.transform.position - sourceTransform.position);
directionToTarget.y = 0;
if (directionToTarget.sqrMagnitude < 0.0001f) return true;
directionToTarget.Normalize();
float angle = Vector3.Angle(sourceTransform.forward, directionToTarget);
return angle <= angleThreshold;
}
private void UpdateTarget()
{
if (_playerTransform == null || _gameStateManager == null) return;
vFSMBehaviourController bestCandidate = null;
float minDistanceSqr = maxTargetingDistance * maxTargetingDistance;
HashSet<vFSMBehaviourController> combatControllers = _gameStateManager.GetActiveCombatcontrollers();
if (combatControllers == null || combatControllers.Count == 0)
{
if (CurrentTarget != null) SetNewTarget(null);
return;
}
foreach (var controller in combatControllers)
{
if (controller == null || !controller.gameObject.activeInHierarchy || controller.aiController.currentHealth <= 0)
{
continue;
}
if (!IsTargetInAngle(_playerTransform, controller, targetingAngleThreshold))
{
continue;
}
float distSqr = (controller.transform.position - _playerTransform.position).sqrMagnitude;
if (distSqr <= minDistanceSqr)
{
minDistanceSqr = distSqr;
bestCandidate = controller;
}
}
if (CurrentTarget != bestCandidate)
{
SetNewTarget(bestCandidate);
}
else if (CurrentTarget != null && !IsTargetValid(CurrentTarget))
{
SetNewTarget(null);
}
}
private bool IsTargetValid(vFSMBehaviourController target)
{
if (target == null || !target.gameObject.activeInHierarchy || target.aiController.currentHealth <= 0)
return false;
if (_playerTransform == null) return false;
if (!IsTargetInAngle(_playerTransform, target, targetingAngleThreshold))
return false;
float distSqr = (target.transform.position - _playerTransform.position).sqrMagnitude;
return distSqr <= maxTargetingDistance * maxTargetingDistance;
}
private void SetNewTarget(vFSMBehaviourController newTarget)
{
if (CurrentTarget == newTarget) return;
if (CurrentTarget != null)
{
ApplyHighlight(CurrentTarget, false);
OnTargetDeselected?.Invoke(CurrentTarget);
}
CurrentTarget = newTarget;
if (CurrentTarget != null)
{
ApplyHighlight(CurrentTarget, true);
OnTargetSelected?.Invoke(CurrentTarget);
}
}
public void ExecuteRotationTowardsCurrentTarget(float deltaTime)
{
if (CurrentTarget == null || _playerTransform == null)
{
return;
}
Vector3 directionToTarget = CurrentTarget.transform.position - _playerTransform.position;
directionToTarget.y = 0f;
if (directionToTarget.sqrMagnitude < 0.0001f) return;
directionToTarget.Normalize();
Quaternion targetRotation = Quaternion.LookRotation(directionToTarget);
_playerTransform.rotation = Quaternion.Lerp(_playerTransform.rotation, targetRotation, deltaTime * playerRotationSpeed);
}
public float GetCurrentTargetHealth()
{
if (CurrentTarget != null && CurrentTarget.aiController != null)
{
return CurrentTarget.aiController.currentHealth;
}
return -1f;
}
public void ClearTarget(bool findNewOneImmediately)
{
SetNewTarget(null);
if (findNewOneImmediately && _gameStateManager != null && _gameStateManager.CurrentState == GameStateManager.State.COMBAT)
{
UpdateTarget();
}
}
// *** MODIFIED GetTargetRenderers METHOD ***
private Renderer[] GetTargetRenderers(vFSMBehaviourController targetController)
{
if (targetController == null) return new Renderer[0];
List<Renderer> collectedRenderers = new List<Renderer>();
// Collect all SkinnedMeshRenderers
SkinnedMeshRenderer[] smrs = targetController.GetComponentsInChildren<SkinnedMeshRenderer>(true);
if (smrs != null && smrs.Length > 0)
{
collectedRenderers.AddRange(smrs);
}
// Collect all MeshRenderers
MeshRenderer[] mrs = targetController.GetComponentsInChildren<MeshRenderer>(true);
if (mrs != null && mrs.Length > 0)
{
collectedRenderers.AddRange(mrs);
}
// If no specific SMRs or MRs were found, fall back to all Renderers (optional, but good for safety)
// Or, if the goal is *only* SMRs and MRs, this fallback can be removed.
// For now, let's assume we want to highlight *something* if possible.
// If you strictly want ONLY SMRs and MRs, and nothing else, remove this 'if' block.
if (collectedRenderers.Count == 0)
{
Renderer[] allRenderers = targetController.GetComponentsInChildren<Renderer>(true);
if (allRenderers != null && allRenderers.Length > 0)
{
collectedRenderers.AddRange(allRenderers);
}
}
// The preferSkinnedMeshRenderer flag is not directly used here for filtering types anymore.
// It could potentially be used for ordering or other logic if needed in the future.
return collectedRenderers.Distinct().ToArray(); // Distinct() to avoid duplicates if an object somehow has multiple relevant renderer types
}
private void ApplyHighlight(vFSMBehaviourController targetController, bool fadeIn)
{
if (targetController == null || string.IsNullOrEmpty(materialHighlightPropertyName)) return;
Renderer[] renderers = GetTargetRenderers(targetController);
foreach (Renderer rend in renderers)
{
if (rend == null) continue;
foreach (Material mat in rend.materials)
{
if (mat == null || !mat.HasProperty(materialHighlightPropertyName)) continue;
if (_materialToFadeCoroutineMap.TryGetValue(mat, out Coroutine existingCoroutine) && existingCoroutine != null)
{ {
StopCoroutine(existingCoroutine); if (targetLockSystem.GetCurrentLockOnTarget() != CurrentTarget.transform)
}
Color currentColor = mat.GetColor(materialHighlightPropertyName);
Color targetColor = fadeIn ? highlightColor : deselectHighlightColor;
if (fadeIn)
{
if (!_originalMaterialColors.ContainsKey(mat) ||
(_originalMaterialColors[mat] != currentColor && currentColor != deselectHighlightColor && currentColor != highlightColor) )
{ {
_originalMaterialColors[mat] = currentColor; targetLockSystem.ManuallySetLockOnTarget(CurrentTarget.transform, true);
} }
} }
Coroutine newFadeCoroutine = StartCoroutine(FadeMaterialPropertyCoroutine(mat, currentColor, targetColor, highlightFadeDuration)); return;
_materialToFadeCoroutineMap[mat] = newFadeCoroutine; }
if (CurrentTarget != null)
{
ApplyHighlight(CurrentTarget, false);
OnTargetDeselected?.Invoke(CurrentTarget);
if (autoLockSelectedTarget && targetLockSystem != null &&
targetLockSystem.GetCurrentLockOnTarget() == CurrentTarget.transform &&
(newTarget == null || newTarget.transform != CurrentTarget.transform))
{
targetLockSystem.ManuallySetLockOnTarget(null, false);
}
}
CurrentTarget = newTarget;
if (CurrentTarget != null)
{
ApplyHighlight(CurrentTarget, true);
OnTargetSelected?.Invoke(CurrentTarget);
if (autoLockSelectedTarget && targetLockSystem != null)
{
targetLockSystem.ManuallySetLockOnTarget(CurrentTarget.transform, true);
}
}
else // CurrentTarget is now null
{
// Deselection logic above handles unlocking if autoLockSelectedTarget was true
// and if bLockOn was on the CurrentTarget that just became null.
} }
} }
}
private IEnumerator FadeMaterialPropertyCoroutine(Material material, Color fromValue, Color toValue, float duration) private void HandleLockOnSystemTargetChanged(Transform lockedTargetTransform)
{
float timer = 0f;
while (timer < duration)
{ {
if (material == null) yield break; if (lockedTargetTransform == null || targetLockSystem == null) return;
timer += Time.deltaTime; // This event means bLockOn changed target (e.g., player pressed Next/Previous)
float progress = Mathf.Clamp01(timer / duration); // Activate the cooldown for AutoTargetting.
material.SetColor(materialHighlightPropertyName, Color.Lerp(fromValue, toValue, progress)); _manualSwitchCooldownActive = true;
yield return null; _manualSwitchCooldownTimer = manualSwitchCooldownDuration;
// Debug.Log($"AutoTargetting: Manual switch detected via bLockOn. Cooldown activated for {manualSwitchCooldownDuration}s.");
vFSMBehaviourController fsmTarget = lockedTargetTransform.GetComponentInParent<vFSMBehaviourController>();
if (fsmTarget == null) fsmTarget = lockedTargetTransform.GetComponentInChildren<vFSMBehaviourController>(true);
if (CurrentTarget != null && CurrentTarget.transform == lockedTargetTransform)
{
// AutoTargetting was already on this target, but bLockOn re-confirmed.
// Cooldown is still active due to bLockOn's event.
return;
}
if (fsmTarget != null)
{
// Debug.Log($"AutoTargeting: Syncing to bLockOn's new target: {fsmTarget.name}.");
// We need to update AutoTargetting's CurrentTarget to match bLockOn,
// even during cooldown, because bLockOn IS the source of truth now.
// The 'forceLockSystemUpdate = true' ensures highlights are updated correctly.
SetNewTarget(fsmTarget, true);
}
else
{
// bLockOn locked something without an FSM, or unlocked.
// If AutoTargeting had a target, it should now clear it to sync.
// Debug.LogWarning($"AutoTargeting: bLockOn locked {lockedTargetTransform.name} (no FSM) or unlocked. Clearing AutoTarget.");
SetNewTarget(null, true);
}
} }
if (material != null) private void HandleLockOnSystemTargetUnlocked(Transform previouslyLockedTargetTransform)
{ {
material.SetColor(materialHighlightPropertyName, toValue); if (targetLockSystem == null) return;
// This event means bLockOn explicitly unlocked (e.g., player pressed Tab again).
// If player manually unlocks, we should respect that and also stop AutoTargetting's autoLock behavior for a bit.
// Or, if autoLockSelectedTarget is false, this is just a notification.
_manualSwitchCooldownActive = true; // Treat manual unlock like a manual switch
_manualSwitchCooldownTimer = manualSwitchCooldownDuration;
// Debug.Log($"AutoTargeting: Manual unlock detected via bLockOn. Cooldown activated.");
string targetName = previouslyLockedTargetTransform ? previouslyLockedTargetTransform.name : "an unknown target";
// Debug.Log($"AutoTargeting: bLockOn unlocked from '{targetName}'.");
if (CurrentTarget != null && previouslyLockedTargetTransform == CurrentTarget.transform)
{
// AutoTargeting's current target was the one that bLockOn just unlocked.
// We should clear AutoTargeting's target to be in sync.
// The cooldown will prevent immediate re-auto-targeting.
SetNewTarget(null, true);
}
// If previouslyLockedTargetTransform was something else, AutoTargeting might already be on null or a different target.
// The cooldown ensures AutoTargeting doesn't fight this unlock.
}
// ... (ExecuteRotationTowardsCurrentTarget, GetCurrentTargetHealth, ClearTarget, Renderer methods, Fade coroutine remain the same)
public void ExecuteRotationTowardsCurrentTarget(float deltaTime)
{
if (CurrentTarget == null || _playerTransform == null) return;
Vector3 directionToTarget = CurrentTarget.transform.position - _playerTransform.position;
directionToTarget.y = 0f;
if (directionToTarget.sqrMagnitude < 0.0001f) return;
directionToTarget.Normalize();
Quaternion targetRotation = Quaternion.LookRotation(directionToTarget);
_playerTransform.rotation = Quaternion.Lerp(_playerTransform.rotation, targetRotation, deltaTime * playerRotationSpeed);
}
public float GetCurrentTargetHealth()
{
if (CurrentTarget != null && CurrentTarget.aiController != null)
{
return CurrentTarget.aiController.currentHealth;
}
return -1f;
}
public void ClearTarget(bool findNewOneImmediately)
{
SetNewTarget(null, true); // Force update if clearing target
if (findNewOneImmediately && _gameStateManager != null && _gameStateManager.CurrentState == GameStateManager.State.COMBAT)
{
// UpdateTarget will respect cooldown, so immediate find might be delayed
// If you need it to bypass cooldown here, more logic is needed.
// For now, let's assume standard behavior.
if (!_manualSwitchCooldownActive) UpdateTarget();
}
}
private Renderer[] GetTargetRenderers(vFSMBehaviourController targetController)
{
if (targetController == null) return new Renderer[0];
List<Renderer> collectedRenderers = new List<Renderer>();
SkinnedMeshRenderer[] smrs = targetController.GetComponentsInChildren<SkinnedMeshRenderer>(true);
if (smrs != null && smrs.Length > 0) collectedRenderers.AddRange(smrs);
MeshRenderer[] mrs = targetController.GetComponentsInChildren<MeshRenderer>(true);
if (mrs != null && mrs.Length > 0) collectedRenderers.AddRange(mrs);
if (collectedRenderers.Count == 0)
{
Renderer[] allRenderers = targetController.GetComponentsInChildren<Renderer>(true);
if (allRenderers != null && allRenderers.Length > 0) collectedRenderers.AddRange(allRenderers);
}
return collectedRenderers.Distinct().ToArray();
}
private void ApplyHighlight(vFSMBehaviourController targetController, bool fadeIn)
{
if (targetController == null || string.IsNullOrEmpty(materialHighlightPropertyName)) return;
Renderer[] renderers = GetTargetRenderers(targetController);
foreach (Renderer rend in renderers)
{
if (rend == null) continue;
foreach (Material mat in rend.materials)
{
if (mat == null || !mat.HasProperty(materialHighlightPropertyName)) continue;
if (_materialToFadeCoroutineMap.TryGetValue(mat, out Coroutine existingCoroutine) && existingCoroutine != null)
{
StopCoroutine(existingCoroutine);
}
Color currentColor = mat.GetColor(materialHighlightPropertyName);
Color targetColor = fadeIn ? highlightColor : deselectHighlightColor;
if (fadeIn)
{
if (!_originalMaterialColors.ContainsKey(mat) ||
(_originalMaterialColors[mat] != currentColor && currentColor != deselectHighlightColor && currentColor != highlightColor))
{
_originalMaterialColors[mat] = currentColor;
}
}
Coroutine newFadeCoroutine = StartCoroutine(FadeMaterialPropertyCoroutine(mat, currentColor, targetColor, highlightFadeDuration));
_materialToFadeCoroutineMap[mat] = newFadeCoroutine;
}
}
}
private IEnumerator FadeMaterialPropertyCoroutine(Material material, Color fromValue, Color toValue, float duration)
{
float timer = 0f;
while (timer < duration)
{
if (material == null) yield break;
timer += Time.deltaTime;
float progress = Mathf.Clamp01(timer / duration);
material.SetColor(materialHighlightPropertyName, Color.Lerp(fromValue, toValue, progress));
yield return null;
}
if (material != null)
{
material.SetColor(materialHighlightPropertyName, toValue);
}
} }
} }
} }

View File

@@ -65,6 +65,27 @@ namespace Beyond
BindWeaponDrawEffectsOnEquipped(); BindWeaponDrawEffectsOnEquipped();
HideWeapons(true); HideWeapons(true);
GameStateManager.Instance.m_OnStateChanged.AddListener(OnGameStateChanged);
}
private void OnGameStateChanged(GameStateManager.State arg0)
{
if (weaponsHided && arg0 == GameStateManager.State.NORMAL)
{
if (debugMode)
{
Debug.Log("Returning to normal state, drawing weapons");
}
HideWeapons(true);
}
else if (!weaponsHided && arg0 == GameStateManager.State.COMBAT)
{
if (debugMode)
{
Debug.Log("Entering combat state, hiding weapons");
}
DrawWeapons(true);
}
} }
private void OnDestroy() private void OnDestroy()
@@ -78,6 +99,10 @@ namespace Beyond
itemManager.onEquipItem.RemoveListener(PlayWeaponHideEffect); itemManager.onEquipItem.RemoveListener(PlayWeaponHideEffect);
itemManager.onFinishEquipItem.RemoveListener(BindNewlyEquippedWeaponDrawHideEffects); itemManager.onFinishEquipItem.RemoveListener(BindNewlyEquippedWeaponDrawHideEffects);
} }
if (GameStateManager.Instance != null)
{
GameStateManager.Instance.m_OnStateChanged.RemoveListener(OnGameStateChanged);
}
} }
private void BindWeaponDrawEffectsOnEquipped() private void BindWeaponDrawEffectsOnEquipped()
@@ -256,7 +281,7 @@ namespace Beyond
protected virtual bool CanHideWeapons() protected virtual bool CanHideWeapons()
{ {
return melee && melee.meleeManager && (forceHide || (!melee.isAttacking && !melee.isBlocking && (melee.meleeManager.rightWeapon || melee.meleeManager.leftWeapon))); return melee && melee.meleeManager && (forceHide || (!melee.isAttacking && !melee.isBlocking && (GameStateManager.Instance.CurrentState != GameStateManager.State.COMBAT) && (melee.meleeManager.rightWeapon || melee.meleeManager.leftWeapon)));
} }
protected virtual bool CanDrawWeapons() protected virtual bool CanDrawWeapons()

View File

@@ -1,7 +1,7 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using Beyond; using System.Linq;
using Invector; using Invector; // Assuming vIHealthController is in this namespace or vCharacterController
using Invector.vCharacterController; using Invector.vCharacterController;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
@@ -9,20 +9,18 @@ using UnityEngine.UI;
namespace Beyond namespace Beyond
{ {
[vClassHeader("MELEE LOCK-ON")] [vClassHeader("MELEE LOCK-ON")]
public class bLockOn : vLockOnBehaviour public class bLockOn : vLockOnBehaviour // Inherits from your provided vLockOnBehaviour
{ {
#region variables #region variables
[System.Serializable] [System.Serializable]
public class LockOnEvent : UnityEngine.Events.UnityEvent<Transform> public class LockOnEvent : UnityEngine.Events.UnityEvent<Transform> { }
{ }
[Tooltip("Make sure to disable or change the StrafeInput to a different key at the Player Input component")] [Tooltip("Make sure to disable or change the StrafeInput to a different key at the Player Input component")]
public bool strafeWhileLockOn = true; public bool strafeWhileLockOn = true;
[Tooltip("Create a Image inside the UI and assign here")] [Tooltip("Create a Image inside the UI and assign here")]
public RectTransform aimImagePrefab; public RectTransform aimImagePrefab;
public Canvas aimImageContainer; public Canvas aimImageContainer;
public Vector2 aimImageSize = new Vector2(30, 30); public Vector2 aimImageSize = new Vector2(30, 30);
@@ -33,70 +31,95 @@ namespace Beyond
[Range(-0.5f, 0.5f)] [Range(-0.5f, 0.5f)]
public float spriteHeight = 0.25f; public float spriteHeight = 0.25f;
[Tooltip("Offset for the camera height")] [Tooltip("Offset for the camera height when locking on")]
public float cameraHeightOffset; public float cameraHeightOffset;
[Tooltip("Transition Speed for the Camera")] [Tooltip("Transition Speed for the Camera to rotate towards the target when locking on.")]
public float lockSpeed = 0.5f; public float lockSpeed = 0.5f;
[Header("LockOn Inputs")] [Header("LockOn Inputs")]
public GenericInput lockOnInput = new GenericInput("Tab", "RightStickClick", "RightStickClick"); public GenericInput lockOnInput = new GenericInput("Tab", "RightStickClick", "RightStickClick");
// Inputs for Next/Previous target are now handled by calling base.ChangeTarget(1) or base.ChangeTarget(-1)
public GenericInput nexTargetInput = new GenericInput("X", false, false, "RightAnalogHorizontal", true, false, "X", false, false); public GenericInput nexTargetInput = new GenericInput("X", false, false, "RightAnalogHorizontal", true, false, "X", false, false);
public GenericInput previousTargetInput = new GenericInput("Z", false, false, "RightAnalogHorizontal", true, true, "Z", false, false); public GenericInput previousTargetInput = new GenericInput("Z", false, false, "RightAnalogHorizontal", true, true, "Z", false, false);
internal bool isLockingOn;
public bool isLockingOn { get; private set; } // Keep this for external systems like AutoTargetting
public LockOnEvent onLockOnTarget; public LockOnEvent onLockOnTarget;
public LockOnEvent onUnLockOnTarget; public LockOnEvent onUnLockOnTarget;
private Canvas _aimCanvas;
private RectTransform _aimImage;
protected bool inTarget; private RectTransform _aimImageInstance;
// 'inTarget' might be redundant if we rely on base.currentTarget != null and isLockingOn
// protected bool inTarget;
protected bMeleeCombatInput tpInput; protected bMeleeCombatInput tpInput;
private AutoTargetting autoTargetingSystem;
#endregion variables #endregion variables
protected virtual void Start() protected void Start() // Override if base has Start, otherwise just 'void Start()'
{ {
Init(); // If vLockOnBehaviour.Start is not virtual, you might not need base.Start()
// but Init() is crucial from the base class.
base.Init(); // Call the Init from vLockOnBehaviour
tpInput = GetComponent<bMeleeCombatInput>(); tpInput = GetComponent<bMeleeCombatInput>();
if (tpInput) if (tpInput)
{ {
tpInput.onUpdate -= UpdateLockOn; tpInput.onUpdate -= UpdateLockOn;
tpInput.onUpdate += UpdateLockOn; tpInput.onUpdate += UpdateLockOn;
// access the HealthController to Reset the LockOn when Dead // Player health for unlocking on death (assuming player has vIHealthController)
GetComponent<vHealthController>().onDead.AddListener((GameObject g) => var playerHealth = GetComponent<vIHealthController>();
if (playerHealth != null)
{ {
// action to reset lockOn // This part might need adjustment based on how vIHealthController handles death events
isLockingOn = false; // For now, let's assume a simple check or that other systems handle player death state.
LockOn(false); }
UpdateLockOn(); }
}); else
{
Debug.LogError("bLockOn: bMeleeCombatInput not found. Lock-on system disabled.");
enabled = false;
return;
} }
if (!aimImageContainer) if (!aimImageContainer) aimImageContainer = GetComponentInChildren<Canvas>(true);
if (aimImagePrefab && aimImageContainer && _aimImageInstance == null)
{ {
aimImageContainer = gameObject.GetComponentInChildren<Canvas>(true); _aimImageInstance = Instantiate(aimImagePrefab, aimImageContainer.transform, false);
_aimImageInstance.gameObject.SetActive(false);
}
// Get AutoTargetting system
if (Player.Instance != null) autoTargetingSystem = Player.Instance.GetComponentInChildren<AutoTargetting>(true);
if (autoTargetingSystem == null) autoTargetingSystem = FindObjectOfType<AutoTargetting>();
if (autoTargetingSystem != null)
{
// Set the 'range' field from the base class
base.range = autoTargetingSystem.maxTargetingDistance;
// Note: findTargetAngle from AutoTargetting is not directly used by this base class.
// The base class uses screen-space sorting. We can't easily inject world-space angle here
// without modifying vLockOnBehaviour.GetPossibleTargets() or SortTargets().
// For now, switching will respect AutoTargetting's *distance* but use base class's screen-angle logic.
}
else
{
Debug.LogWarning("bLockOn: AutoTargetting system not found. Using default vLockOnBehaviour range.");
} }
} }
public RectTransform aimImage public RectTransform AimImageDisplay
{ {
get get
{ {
if (_aimImage) return _aimImage; if (_aimImageInstance == null && aimImagePrefab && aimImageContainer)
if (aimImageContainer)
{ {
_aimImage = Instantiate(aimImagePrefab, Vector2.zero, Quaternion.identity) as RectTransform; _aimImageInstance = Instantiate(aimImagePrefab, aimImageContainer.transform, false);
_aimImage.SetParent(aimImageContainer.transform); _aimImageInstance.anchoredPosition = Vector2.zero;
return _aimImage; // ... other setup for _aimImageInstance
} }
else return _aimImageInstance;
{
Debug.LogWarning("Missing UI Canvas in the scene, please add one");
}
return null;
} }
} }
@@ -105,139 +128,271 @@ namespace Beyond
if (this.tpInput == null) return; if (this.tpInput == null) return;
LockOnInput(); LockOnInput();
SwitchTargetsInput(); SwitchTargetsInput();
CheckForCharacterAlive(); CheckForCharacterAlive(); // Checks base.currentTarget
UpdateAimImage(); UpdateAimImage();
} }
protected virtual void LockOnInput() protected virtual void LockOnInput()
{ {
if (tpInput.tpCamera == null || tpInput.cc == null) return; if (tpInput.tpCamera == null || tpInput.cc == null) return;
// lock the camera into a target, if there is any around
if (lockOnInput.GetButtonDown() && !tpInput.cc.customAction) if (lockOnInput.GetButtonDown() && !tpInput.cc.customAction)
{ {
isLockingOn = !isLockingOn; // Toggle lock-on state
LockOn(isLockingOn); if (isLockingOn)
}
// unlock the camera if the target is null
else if (isLockingOn && (tpInput.tpCamera.lockTarget == null))
{
isLockingOn = false;
LockOn(false);
}
// choose to use lock-on with strafe of free movement
if (strafeWhileLockOn && !tpInput.cc.locomotionType.Equals(vThirdPersonMotor.LocomotionType.OnlyStrafe))
{
if (isLockingOn && tpInput.tpCamera.lockTarget != null)
{ {
tpInput.cc.lockInStrafe = true; UnlockTarget();
tpInput.cc.isStrafing = true;
} }
else else
{ {
tpInput.cc.lockInStrafe = false; AttemptLockOn(null); // Attempt to find a new target
tpInput.cc.isStrafing = false;
} }
} }
} }
// This method is called by base.ChangeTargetRoutine when a target is selected
protected override void SetTarget() protected override void SetTarget()
{ {
if (tpInput.tpCamera != null) // base.currentTarget (which is base.target) has been set by ChangeTargetRoutine
// or by our AttemptLockOn/ManuallySetLockOnTarget.
// We just need to react to it.
if (tpInput.tpCamera != null && base.currentTarget != null)
{ {
tpInput.tpCamera.SetLockTarget(currentTarget.transform, cameraHeightOffset, lockSpeed); tpInput.tpCamera.SetLockTarget(base.currentTarget.transform, cameraHeightOffset, lockSpeed);
onLockOnTarget.Invoke(currentTarget); if (isLockingOn) // Only invoke if we are truly in a locking state
{
onLockOnTarget.Invoke(base.currentTarget.transform);
}
}
else if (base.currentTarget == null && isLockingOn) // Target became null while we thought we were locked
{
UnlockTarget(false); // Silently unlock if target disappeared
} }
} }
protected virtual void SwitchTargetsInput() protected virtual void SwitchTargetsInput()
{ {
if (tpInput.tpCamera == null) return; if (tpInput.tpCamera == null || !isLockingOn) return;
// base.currentTarget is the currently locked Transform
if (tpInput.tpCamera.lockTarget) if (base.currentTarget != null)
{ {
// switch between targets using Keyboard if (previousTargetInput.GetButtonDown()) base.ChangeTarget(-1); // Calls base class method
if (previousTargetInput.GetButtonDown()) PreviousTarget(); else if (nexTargetInput.GetButtonDown()) base.ChangeTarget(1); // Calls base class method
else if (nexTargetInput.GetButtonDown()) NextTarget();
} }
} }
// 'ChangeTarget' is already defined in the base class and handles index changes.
// We override SetTarget() which is called by the base ChangeTargetRoutine.
protected virtual void CheckForCharacterAlive() protected virtual void CheckForCharacterAlive()
{ {
if (currentTarget && !isCharacterAlive() && inTarget || (inTarget && !isCharacterAlive())) // base.isCharacterAlive() checks the base.currentTarget
if (isLockingOn && base.currentTarget != null && !base.isCharacterAlive())
{ {
ResetLockOn(); // Debug.Log($"bLockOn: Locked target {base.currentTarget.name} died. Attempting to find new one or unlock.");
inTarget = false; Transform oldDeadTarget = base.currentTarget;
LockOn(true); AttemptLockOn(null, oldDeadTarget); // Try to find a new target, excluding the dead one for this attempt
StopLockOn();
} }
} }
protected virtual void LockOn(bool value) /// <summary>
/// Attempts to lock onto a target. If preferredTarget is provided and valid, locks that.
/// Otherwise, finds the best available target.
/// </summary>
protected void AttemptLockOn(Transform preferredTarget, Transform excludeTarget = null)
{ {
base.UpdateLockOn(value); if (tpInput == null || tpInput.cc == null || tpInput.tpCamera == null) return;
if (!inTarget && currentTarget)
Transform targetToLock = null;
if (preferredTarget != null && base.isCharacterAlive(preferredTarget))
{ {
inTarget = true; targetToLock = preferredTarget;
// send current target if inTarget
SetTarget();
} }
else if (inTarget && !currentTarget) else
{ {
inTarget = false; // Update range from AutoTargetting before finding targets
// send message to clear current target if (autoTargetingSystem != null) base.range = autoTargetingSystem.maxTargetingDistance;
StopLockOn();
List<Transform> possible = base.GetPossibleTargets(); // This uses base.range
if (possible != null && possible.Count > 0)
{
// Filter out the excludeTarget if provided
if (excludeTarget != null)
{
possible.Remove(excludeTarget);
}
// Optional: Re-filter 'possible' list here using AutoTargetting's angle
// This would require iterating 'possible' and doing Vector3.Angle checks.
// For now, we rely on base.SortTargets() which is screen-based.
if (possible.Count > 0)
{
targetToLock = possible.FirstOrDefault(); // Base.GetPossibleTargets already sorts them
}
}
}
if (targetToLock != null)
{
// If we were previously locked on a different target
if (isLockingOn && base.currentTarget != null && base.currentTarget != targetToLock)
{
onUnLockOnTarget.Invoke(base.currentTarget); // Invoke for old target
}
// This directly sets the protected 'target' field in the base class
// Since we can't call a base.SelectTarget(), we mimic what base.UpdateLockOn(true) would do.
this.GetType().GetField("target", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)?.SetValue(this, targetToLock);
// this.target = targetToLock; // This would create a new field in bLockOn, not set base.target
isLockingOn = true;
base.UpdateLockOn(true); // This will set base.target if list not empty, and also set _inLockOn
// and might call SetTarget if list was empty and now has one.
// It's a bit of a dance because the base class design is restrictive.
// We need to ensure our 'isLockingOn' and the base class's internal lock state are synced.
// And that SetTarget() (our override) is called to update camera & events.
if (base.currentTarget == targetToLock) // If base.UpdateLockOn successfully set it
{
this.SetTarget(); // Manually call our override to ensure camera and events fire
UpdateStrafeState(true);
UpdateAimImage();
}
else // Fallback if base.UpdateLockOn didn't pick our target (e.g. list was empty for it)
{
// This case should ideally not happen if targetToLock was valid.
// If it does, it means base.GetPossibleTargets within UpdateLockOn failed.
UnlockTarget(false); // Silently unlock
}
}
else // No target found or preferred target was invalid
{
UnlockTarget();
} }
} }
public void SetLockOnToFalse() protected void UnlockTarget(bool invokeEvent = true)
{ {
LockOn(false); if (tpInput == null || tpInput.tpCamera == null) return;
Transform previouslyLocked = base.currentTarget; // Get before clearing
base.UpdateLockOn(false); // This sets base.target to null and base._inLockOn to false
isLockingOn = false;
if (previouslyLocked != null && invokeEvent)
{
onUnLockOnTarget.Invoke(previouslyLocked);
}
tpInput.tpCamera.RemoveLockTarget();
UpdateStrafeState(false);
UpdateAimImage();
} }
public void ManuallySetLockOnTarget(Transform newTargetTransform, bool shouldLock)
{
if (shouldLock)
{
AttemptLockOn(newTargetTransform);
}
else
{
UnlockTarget();
}
}
public Transform GetCurrentLockOnTarget()
{
// Relies on base.currentTarget which is the Transform 'target' from base class
return isLockingOn ? base.currentTarget : null;
}
private void UpdateStrafeState(bool shouldStrafe)
{
if (strafeWhileLockOn && tpInput?.cc != null && !tpInput.cc.locomotionType.Equals(vThirdPersonMotor.LocomotionType.OnlyStrafe))
{
bool canStrafe = shouldStrafe && isLockingOn && base.currentTarget != null;
tpInput.cc.lockInStrafe = canStrafe;
tpInput.cc.isStrafing = canStrafe;
}
}
public void SetLockOnToFalse() => UnlockTarget();
protected virtual void UpdateAimImage() protected virtual void UpdateAimImage()
{ {
if (!aimImageContainer || !aimImage) return; var displayImage = AimImageDisplay;
if (hideSprite) if (!aimImageContainer || displayImage == null) return;
displayImage.sizeDelta = aimImageSize;
// base.currentTarget is the Transform, base.isCharacterAlive() checks it
bool shouldShowAimImage = isLockingOn && base.currentTarget != null && base.isCharacterAlive(base.currentTarget);
if (hideSprite) displayImage.gameObject.SetActive(shouldShowAimImage);
else if (!displayImage.gameObject.activeSelf) displayImage.gameObject.SetActive(true);
if (shouldShowAimImage && tpCamera != null && base.currentTarget != null)
{ {
aimImage.sizeDelta = aimImageSize; // Using the extension method from vLockOnHelper
if (currentTarget && !aimImage.transform.gameObject.activeSelf && isCharacterAlive()) displayImage.anchoredPosition = base.currentTarget.GetScreenPointOffBoundsCenter(aimImageContainer, tpCamera.targetCamera, spriteHeight);
aimImage.transform.gameObject.SetActive(true);
else if (!currentTarget && aimImage.transform.gameObject.activeSelf)
aimImage.transform.gameObject.SetActive(false);
else if (_aimImage.transform.gameObject.activeSelf && !isCharacterAlive())
aimImage.transform.gameObject.SetActive(false);
} }
if (currentTarget && aimImage && aimImageContainer) else if (!hideSprite) displayImage.anchoredPosition = Vector2.zero;
aimImage.anchoredPosition = currentTarget.GetScreenPointOffBoundsCenter(aimImageContainer, tpCamera.targetCamera, spriteHeight);
else if (aimImageContainer)
aimImage.anchoredPosition = Vector2.zero;
} }
public virtual void StopLockOn() // StopLockOn is effectively UnlockTarget now
// public virtual void StopLockOn() { UnlockTarget(); }
public List<Transform> GetNearbyTargets() // For AutoTargetting/MagicAttacks
{ {
if (currentTarget == null && tpInput.tpCamera != null) if (autoTargetingSystem != null)
{ {
onUnLockOnTarget.Invoke(tpInput.tpCamera.lockTarget); base.range = autoTargetingSystem.maxTargetingDistance;
tpInput.tpCamera.RemoveLockTarget(); // Angle filtering from AutoTargetting would need to be applied manually here
isLockingOn = false; // if we want to override the base class's screen-based sorting/filtering for this specific call.
inTarget = false;
} }
} // base.GetPossibleTargets() returns List<Transform>
List<Transform> allTargetsInBaseRange = base.GetPossibleTargets();
public virtual void NextTarget() if (allTargetsInBaseRange == null) return new List<Transform>();
{
base.ChangeTarget(1);
}
public virtual void PreviousTarget() // Optional: Further filter allTargetsInBaseRange by AutoTargetting's angle
{ if (autoTargetingSystem != null && _playerTransform != null) // Assuming _playerTransform is accessible or passed
base.ChangeTarget(-1); {
} List<Transform> angleFilteredTargets = new List<Transform>();
Vector3 playerPos = _playerTransform.position; // Need player transform
Vector3 playerFwd = _playerTransform.forward;
public List<Transform> GetNearbyTargets() foreach (Transform t in allTargetsInBaseRange)
{
if (t == null) continue;
Vector3 dirToTarget = (t.position - playerPos);
dirToTarget.y = 0; // Assuming 2D angle check for simplicity
if (dirToTarget.sqrMagnitude < 0.001f) // Target is at player's feet
{
angleFilteredTargets.Add(t);
continue;
}
float angle = Vector3.Angle(playerFwd, dirToTarget.normalized);
if (angle <= autoTargetingSystem.targetingAngleThreshold)
{
angleFilteredTargets.Add(t);
}
}
return angleFilteredTargets;
}
return allTargetsInBaseRange;
}
// Need player transform for angle filtering in GetNearbyTargets
private Transform _playerTransform;
void Awake() // Get player transform
{ {
return base.GetPossibleTargets(); if (Player.Instance != null) _playerTransform = Player.Instance.transform;
else _playerTransform = transform; // Fallback if bLockOn is on player
} }
} }
} }

View File

@@ -3,20 +3,20 @@ using Invector.vCharacterController;
using Invector.vEventSystems; using Invector.vEventSystems;
using Invector.vMelee; using Invector.vMelee;
using UnityEngine; using UnityEngine;
using System.Collections;
namespace Beyond namespace Beyond
{ {
// here you can modify the Melee Combat inputs
// if you want to modify the Basic Locomotion inputs, go to the vThirdPersonInput
[vClassHeader("MELEE INPUT MANAGER", iconName = "inputIcon")] [vClassHeader("MELEE INPUT MANAGER", iconName = "inputIcon")]
public class bMeleeCombatInput : bThirdPersonInput, vIMeleeFighter public class bMeleeCombatInput : bThirdPersonInput, vIMeleeFighter
{ {
// ... (rest of your existing bMeleeCombatInput variables) ...
#region Variables #region Variables
[vEditorToolbar("Inputs")] [vEditorToolbar("Inputs")]
[Header("Melee Inputs")] [Header("Melee Inputs")]
public GenericInput weakAttackInput = new GenericInput("Mouse0", "RB", "RB"); public GenericInput weakAttackInput = new GenericInput("Mouse0", "RB", "RB");
public GenericInput strongAttackInput = new GenericInput("Alpha1", false, "RT", true, "RT", false); public GenericInput strongAttackInput = new GenericInput("Alpha1", false, "RT", true, "RT", false);
public GenericInput blockInput = new GenericInput("Mouse1", "LB", "LB"); public GenericInput blockInput = new GenericInput("Mouse1", "LB", "LB");
@@ -25,7 +25,7 @@ namespace Beyond
public bool isAttacking public bool isAttacking
{ {
get => _isAttacking || cc.IsAnimatorTag("Attack"); get => _isAttacking || (cc != null && cc.IsAnimatorTag("Attack"));
protected set { _isAttacking = value; } protected set { _isAttacking = value; }
} }
@@ -44,10 +44,20 @@ namespace Beyond
[HideInInspector] public bool lockMeleeInput; [HideInInspector] public bool lockMeleeInput;
[Header("Dash Settings")]
[Tooltip("Speed at which the player rotates towards the target before dashing.")]
public float dashRotationSpeed = 15f;
[Tooltip("Angle threshold (degrees). If angle to target is less than this, player dashes immediately without pre-rotation.")]
public float dashFacingAngleThreshold = 10f;
[Tooltip("Maximum time (seconds) the pre-dash rotation will attempt before dashing anyway.")]
public float maxDashRotationTime = 0.5f;
private AutoTargetting autoTargeting;
private bool isRotatingAndDashing = false;
public void SetLockMeleeInput(bool value) public void SetLockMeleeInput(bool value)
{ {
lockMeleeInput = value; lockMeleeInput = value;
if (value) if (value)
{ {
isAttacking = false; isAttacking = false;
@@ -63,17 +73,30 @@ namespace Beyond
private MagicAttacks magicAttacks; private MagicAttacks magicAttacks;
#endregion Variables #endregion
public virtual bool lockInventory public virtual bool lockInventory
{ {
get { return isAttacking || cc.isDead || (cc.customAction || cc.IsAnimatorTag("special")) || cc.isRolling; } get { return isAttacking || (cc != null && (cc.isDead || cc.customAction || cc.IsAnimatorTag("special") || cc.isRolling)); }
} }
protected override void Start() protected override void Start()
{ {
base.Start(); base.Start(); // This will call bThirdPersonInput's Start, which gets 'cc'
magicAttacks = GetComponent<MagicAttacks>(); magicAttacks = GetComponent<MagicAttacks>();
autoTargeting = GetComponent<AutoTargetting>();
if (autoTargeting == null && Player.Instance != null && Player.Instance.gameObject == this.gameObject)
{
autoTargeting = Player.Instance.GetComponent<AutoTargetting>();
}
if (autoTargeting == null && cc != null && cc.transform.root != null)
{
autoTargeting = cc.transform.root.GetComponentInChildren<AutoTargetting>(true);
}
if (autoTargeting == null)
{
Debug.LogWarning("bMeleeCombatInput: AutoTargetting component not found. Dash towards target will require manual aiming.");
}
} }
protected override void LateUpdate() protected override void LateUpdate()
@@ -85,17 +108,12 @@ namespace Beyond
protected override void FixedUpdate() protected override void FixedUpdate()
{ {
base.FixedUpdate(); base.FixedUpdate();
//force showing cursor
ShowCursor(showCursorOnStart); ShowCursor(showCursorOnStart);
isBlocking = Input.GetKeyDown(KeyCode.D);
} }
protected override void InputHandle() protected override void InputHandle()
{ {
if (cc == null || cc.isDead) if (cc == null || cc.isDead) return;
{
return;
}
base.InputHandle(); base.InputHandle();
@@ -108,22 +126,18 @@ namespace Beyond
else else
{ {
ResetAttackTriggers(); ResetAttackTriggers();
isBlocking = false; if (!blockInput.GetButton())
{
isBlocking = false;
}
} }
} }
#region MeleeCombat Input Methods #region MeleeCombat Input Methods
/// <summary>
/// WEAK ATK INPUT
/// </summary>
public virtual void MeleeWeakAttackInput() public virtual void MeleeWeakAttackInput()
{ {
if (cc.animator == null) if (cc.animator == null) return;
{
return;
}
if (weakAttackInput.GetButtonDown() && MeleeAttackStaminaConditions()) if (weakAttackInput.GetButtonDown() && MeleeAttackStaminaConditions())
{ {
TriggerWeakAttack(); TriggerWeakAttack();
@@ -136,16 +150,9 @@ namespace Beyond
cc.animator.SetTrigger(vAnimatorParameters.WeakAttack); cc.animator.SetTrigger(vAnimatorParameters.WeakAttack);
} }
/// <summary>
/// STRONG ATK INPUT
/// </summary>
public virtual void MeleeStrongAttackInput() public virtual void MeleeStrongAttackInput()
{ {
if (cc.animator == null) if (cc.animator == null) return;
{
return;
}
if (strongAttackInput.GetButtonDown() && if (strongAttackInput.GetButtonDown() &&
(!meleeManager.CurrentActiveAttackWeapon || meleeManager.CurrentActiveAttackWeapon.useStrongAttack) && (!meleeManager.CurrentActiveAttackWeapon || meleeManager.CurrentActiveAttackWeapon.useStrongAttack) &&
MeleeAttackStaminaConditions()) MeleeAttackStaminaConditions())
@@ -160,22 +167,17 @@ namespace Beyond
cc.animator.SetTrigger(vAnimatorParameters.StrongAttack); cc.animator.SetTrigger(vAnimatorParameters.StrongAttack);
} }
/// <summary>
/// BLOCK INPUT
/// </summary>
public virtual void BlockingInput() public virtual void BlockingInput()
{ {
if (cc.animator == null) if (cc.animator == null) return;
{
return;
}
isBlocking = blockInput.GetButton() && cc.currentStamina > 0 && !cc.customAction && isBlocking = blockInput.GetButton() && cc.currentStamina > 0 && !cc.customAction &&
!cc.IsAnimatorTag("special") && !isAttacking; !cc.IsAnimatorTag("special") && !isAttacking;
} }
public override void ControlRotation() public override void ControlRotation()
{ {
if (cc == null || cc.lockRotation) return;
if (cameraMain && !lockUpdateMoveDirection) if (cameraMain && !lockUpdateMoveDirection)
{ {
if (!cc.keepDirection) if (!cc.keepDirection)
@@ -184,147 +186,234 @@ namespace Beyond
} }
} }
//here
if (tpCamera != null && tpCamera.lockTarget && cc.isStrafing && !cc.isRolling) if (tpCamera != null && tpCamera.lockTarget && cc.isStrafing && !cc.isRolling)
{ {
cc.RotateToPosition(tpCamera.lockTarget.position); // rotate the character to a specific target cc.RotateToPosition(tpCamera.lockTarget.position);
} }
else else
{ {
cc.ControlRotationType(); // handle the controller rotation type (strafe or free) cc.ControlRotationType();
} }
} }
/// <summary>
/// Override the Sprint method to cancel Sprinting when Attacking
/// </summary>
protected override void SprintInput() protected override void SprintInput()
{ {
if (sprintInput.useInput) if (lockSprintInput) return; // Check local lock first
if (sprintInput.useInput && cc != null)
{ {
bool canSprint = cc.useContinuousSprint ? sprintInput.GetButtonDown() : sprintInput.GetButton(); bool canSprint = cc.useContinuousSprint ? sprintInput.GetButtonDown() : sprintInput.GetButton();
// Let base class (vThirdPersonInput) handle the cc.Sprint call if we don't override completely
// For now, let's assume your bThirdPersonInput's SprintInput or vThirdPersonInput's one is what you want.
// If it was base.SprintInput() before, and you have bThirdPersonInput overriding it, make sure that's intended.
// For simplicity, directly call cc.Sprint if bThirdPersonInput doesn't add much, or call base.SprintInput() if it does.
cc.Sprint(canSprint && !isAttacking); cc.Sprint(canSprint && !isAttacking);
} }
} }
protected override void CrouchInput()
{
if (lockCrouchInput) return;
base.CrouchInput(); // Call the base class logic from vThirdPersonInput/bThirdPersonInput
}
#endregion MeleeCombat Input Methods protected override void StrafeInput()
{
if (lockStrafeInput) return;
base.StrafeInput(); // Call the base class logic from vThirdPersonInput/bThirdPersonInput
}
#endregion
#region Conditions #region Conditions
protected virtual bool MeleeAttackStaminaConditions() protected virtual bool MeleeAttackStaminaConditions()
{ {
if (meleeManager == null) meleeManager = GetComponent<vMeleeManager>();
if (meleeManager == null || cc == null) return false;
var result = cc.currentStamina - meleeManager.GetAttackStaminaCost(); var result = cc.currentStamina - meleeManager.GetAttackStaminaCost();
return result >= 0; return result >= 0;
} }
public virtual bool MeleeAttackConditions() public virtual bool MeleeAttackConditions()
{ {
if (meleeManager == null) if (meleeManager == null) meleeManager = GetComponent<vMeleeManager>();
{ return meleeManager != null && cc != null && cc.isGrounded && !cc.customAction && !cc.IsAnimatorTag("special") &&
meleeManager = GetComponent<vMeleeManager>();
}
return meleeManager != null && cc.isGrounded && !cc.customAction && !cc.IsAnimatorTag("special") &&
!cc.isJumping && !cc.isCrouching && !cc.isRolling && !isEquipping && !cc.isJumping && !cc.isCrouching && !cc.isRolling && !isEquipping &&
!cc.animator.IsInTransition(cc.baseLayer); (cc.animator != null && !cc.animator.IsInTransition(cc.baseLayer)) && !isRotatingAndDashing;
} }
protected override bool JumpConditions() // We override JumpInput, so we'll use the base JumpConditions if needed, or reimplement.
// vThirdPersonInput.JumpConditions():
// return !cc.customAction && !cc.isCrouching && cc.isGrounded && cc.GroundAngle() < cc.slopeLimit && cc.currentStamina >= cc.jumpStamina && !cc.isJumping && !cc.isRolling;
protected override bool JumpConditions() // This is from vThirdPersonInput
{ {
return !isAttacking && base.JumpConditions(); if (cc == null) return false;
bool baseConditions = !cc.customAction && !cc.isCrouching && cc.isGrounded && cc.GroundAngle() < cc.slopeLimit && cc.currentStamina >= cc.jumpStamina && !cc.isJumping && !cc.isRolling;
return baseConditions && !isAttacking && !isRotatingAndDashing; // Add our specific conditions
} }
/*
protected override bool RollConditions() // We override RollInput, so we'll use the base RollConditions or reimplement.
// vThirdPersonInput.RollConditions():
// return (!cc.isRolling || cc.canRollAgain) && cc.isGrounded && !cc.customAction && cc.currentStamina > cc.rollStamina && !cc.isJumping && !cc.isSliding;
protected override bool RollConditions() // This is from vThirdPersonInput
{ {
return base.RollConditions() && !isAttacking && !cc.animator.IsInTransition(cc.upperBodyLayer) && if (cc == null) return false;
!cc.animator.IsInTransition(cc.fullbodyLayer); bool baseConditions = (!cc.isRolling || cc.canRollAgain) && cc.isGrounded && !cc.customAction && cc.currentStamina > cc.rollStamina && !cc.isJumping && !cc.isSliding;
// Add any bMeleeCombatInput specific conditions if necessary, like !isAttacking
return baseConditions && !isAttacking && !isRotatingAndDashing;
} }
*/
#endregion Conditions
#region Update Animations
protected virtual void UpdateMeleeAnimations() #endregion
// MODIFIED JumpInput
protected override void JumpInput()
{ {
if (cc.animator == null || meleeManager == null) if (lockJumpInput) return; // From bThirdPersonInput
if (jumpInput.GetButtonDown() && JumpConditions()) // JumpConditions now includes !isAttacking & !isRotatingAndDashing
{ {
return; cc.Jump(true);
} }
cc.animator.SetInteger(vAnimatorParameters.AttackID, AttackID);
cc.animator.SetInteger(vAnimatorParameters.DefenseID, DefenseID);
cc.animator.SetBool(vAnimatorParameters.IsBlocking, isBlocking);
cc.animator.SetFloat(vAnimatorParameters.MoveSet_ID, meleeMoveSetID, .2f, vTime.deltaTime);
isEquipping = cc.IsAnimatorTag("IsEquipping");
} }
/// <summary> // MODIFIED RollInput
/// Default moveset id used when is without weapon protected override void RollInput()
/// </summary>
public virtual int defaultMoveSetID { get; set; }
/// <summary>
/// Used to ignore the Weapon moveset id and use the <seealso cref="defaultMoveSetID"/>
/// </summary>
public virtual bool overrideWeaponMoveSetID { get; set; }
/// <summary>
/// Current moveset id based if is using weapon or not
/// </summary>
public virtual int meleeMoveSetID
{ {
get if (lockRollInput) return; // From bThirdPersonInput
if (cc == null) return;
if (isRotatingAndDashing) return;
// Use the overridden RollConditions which now includes !isAttacking and !isRotatingAndDashing
if (rollInput.GetButtonDown() && RollConditions())
{ {
int id = meleeManager.GetMoveSetID(); bThirdPersonController beyondController = cc as bThirdPersonController;
if (id == 0 || overrideWeaponMoveSetID) if (beyondController == null)
{ {
id = defaultMoveSetID; Debug.LogError("cc is not a bThirdPersonController instance! Cannot Dash/Roll.");
if(cc != null) cc.Roll(); // Fallback to cc's roll if cast fails
return;
} }
return id; bool isInputBackwards = cc.input.z * -1f >= 0 && Mathf.Abs(cc.input.x) < 0.2f; // Or cc.rawInput.z if you use raw input for this decision
if (!isInputBackwards)
{
beyondController.Roll(); // Or cc.Roll() if bThirdPersonController doesn't override it meaningfully
}
else // Forward, neutral, or sideways input
{
if (autoTargeting != null && autoTargeting.CurrentTarget != null)
{
Transform target = autoTargeting.CurrentTarget.transform;
Vector3 directionToTarget = (target.position - cc.transform.position);
directionToTarget.y = 0;
if (directionToTarget.sqrMagnitude < 0.01f) // Very close
{
beyondController.Dash();
}
else
{
float angleToTarget = Vector3.Angle(cc.transform.forward, directionToTarget.normalized);
if (angleToTarget <= dashFacingAngleThreshold)
{
beyondController.Dash();
}
else
{
if (!isRotatingAndDashing) // Should be redundant due to the check at the start of the method
{
StartCoroutine(RotateAndDashCoroutine(target, beyondController));
}
}
}
}
else
{
beyondController.Dash(); // No target, regular dash
}
}
} }
} }
public virtual void ResetMeleeAnimations() protected virtual IEnumerator RotateAndDashCoroutine(Transform target, bThirdPersonController controller)
{ {
if (meleeManager == null || !animator) if (isRotatingAndDashing || controller == null) yield break;
isRotatingAndDashing = true;
bool previousLockRotation = controller.lockRotation;
controller.lockRotation = true;
bool wasStrafing = controller.isStrafing;
bool originalKeepDirection = controller.keepDirection;
if (wasStrafing && controller.isStrafing)
{ {
return; controller.Strafe();
}
controller.keepDirection = false;
float startTime = Time.time;
Vector3 directionToTarget;
Quaternion targetRotationQuaternion;
while (Time.time < startTime + maxDashRotationTime)
{
if (target == null || !target.gameObject.activeInHierarchy) break;
if (controller == null) { isRotatingAndDashing = false; yield break; }
directionToTarget = (target.position - controller.transform.position);
directionToTarget.y = 0;
if (directionToTarget.sqrMagnitude < 0.001f) break;
targetRotationQuaternion = Quaternion.LookRotation(directionToTarget.normalized);
controller.transform.rotation = Quaternion.Slerp(controller.transform.rotation, targetRotationQuaternion, Time.deltaTime * dashRotationSpeed);
if (Vector3.Angle(controller.transform.forward, directionToTarget.normalized) <= dashFacingAngleThreshold)
{
controller.transform.rotation = targetRotationQuaternion;
break;
}
yield return null;
} }
cc.animator.SetBool(vAnimatorParameters.IsBlocking, false); if (controller == null) { isRotatingAndDashing = false; yield break; }
}
public virtual int AttackID if (target != null && target.gameObject.activeInHierarchy &&
{ (Time.time >= startTime + maxDashRotationTime || Vector3.Angle(controller.transform.forward, (target.position - controller.transform.position).normalized) > 0.1f) )
get { return meleeManager ? meleeManager.GetAttackID() : 0; } {
} directionToTarget = (target.position - controller.transform.position);
directionToTarget.y = 0;
if (directionToTarget.sqrMagnitude > 0.001f)
{
controller.transform.rotation = Quaternion.LookRotation(directionToTarget.normalized);
}
}
public virtual int DefenseID controller.lockRotation = previousLockRotation;
{ if (wasStrafing && !controller.isStrafing)
get { return meleeManager ? meleeManager.GetDefenseID() : 0; } {
} controller.Strafe();
}
controller.keepDirection = originalKeepDirection;
#endregion Update Animations controller.Dash();
isRotatingAndDashing = false;
}
#region Melee Methods #region Melee Methods
public virtual void OnEnableAttack() public virtual void OnEnableAttack()
{ {
if (meleeManager == null) if (meleeManager == null) meleeManager = GetComponent<vMeleeManager>();
{ if (meleeManager == null || cc == null) return;
meleeManager = GetComponent<vMeleeManager>();
}
if (meleeManager == null)
{
return;
}
cc.currentStaminaRecoveryDelay = meleeManager.GetAttackStaminaRecoveryDelay(); cc.currentStaminaRecoveryDelay = meleeManager.GetAttackStaminaRecoveryDelay();
cc.currentStamina -= meleeManager.GetAttackStaminaCost(); cc.currentStamina -= meleeManager.GetAttackStaminaCost();
isAttacking = true; isAttacking = true;
cc.isSprinting = false; if(cc!=null) cc.isSprinting = false;
} }
public virtual void OnDisableAttack() public virtual void OnDisableAttack()
@@ -334,8 +423,11 @@ namespace Beyond
public virtual void ResetAttackTriggers() public virtual void ResetAttackTriggers()
{ {
cc.animator.ResetTrigger(vAnimatorParameters.WeakAttack); if (cc != null && cc.animator != null)
cc.animator.ResetTrigger(vAnimatorParameters.StrongAttack); {
cc.animator.ResetTrigger(vAnimatorParameters.WeakAttack);
cc.animator.ResetTrigger(vAnimatorParameters.StrongAttack);
}
} }
public virtual void BreakAttack(int breakAtkID) public virtual void BreakAttack(int breakAtkID)
@@ -346,69 +438,71 @@ namespace Beyond
public virtual void OnRecoil(int recoilID) public virtual void OnRecoil(int recoilID)
{ {
cc.animator.SetInteger(vAnimatorParameters.RecoilID, recoilID); if (cc != null && cc.animator != null)
cc.animator.SetTrigger(vAnimatorParameters.TriggerRecoil);
cc.animator.SetTrigger(vAnimatorParameters.ResetState);
cc.animator.ResetTrigger(vAnimatorParameters.WeakAttack);
cc.animator.ResetTrigger(vAnimatorParameters.StrongAttack);
}
protected override void RollInput()
{
if (rollInput.GetButtonDown() && RollConditions())
{ {
// cc.Roll(); cc.animator.SetInteger(vAnimatorParameters.RecoilID, recoilID);
cc.animator.SetTrigger(vAnimatorParameters.TriggerRecoil);
if (cc.input.magnitude > 0.1f) cc.animator.SetTrigger(vAnimatorParameters.ResetState);
{ cc.animator.ResetTrigger(vAnimatorParameters.WeakAttack);
cc.Roll(); cc.animator.ResetTrigger(vAnimatorParameters.StrongAttack);
}
else
{
bThirdPersonController beyondController = (bThirdPersonController)cc;
beyondController.Dash();
}
} }
} }
public virtual void OnReceiveAttack(vDamage damage, vIMeleeFighter attacker) public virtual void OnReceiveAttack(vDamage damage, vIMeleeFighter attacker)
{ {
if (magicAttacks.shieldEffectIsActive) if (cc == null) return;
{ if (magicAttacks != null && magicAttacks.shieldEffectIsActive) return;
return;
}
// character is blocking
if (!damage.ignoreDefense && isBlocking && meleeManager != null && if (!damage.ignoreDefense && isBlocking && meleeManager != null &&
meleeManager.CanBlockAttack(damage.sender.position)) meleeManager.CanBlockAttack(damage.sender.position))
{ {
var damageReduction = meleeManager.GetDefenseRate(); var damageReduction = meleeManager.GetDefenseRate();
if (damageReduction > 0) if (damageReduction > 0) damage.ReduceDamage(damageReduction);
{
damage.ReduceDamage(damageReduction);
}
if (attacker != null && meleeManager != null && meleeManager.CanBreakAttack()) if (attacker != null && meleeManager != null && meleeManager.CanBreakAttack())
{ {
attacker.BreakAttack(meleeManager.GetDefenseRecoilID()); attacker.BreakAttack(meleeManager.GetDefenseRecoilID());
} }
meleeManager.OnDefense(); meleeManager.OnDefense();
cc.currentStaminaRecoveryDelay = damage.staminaRecoveryDelay; cc.currentStaminaRecoveryDelay = damage.staminaRecoveryDelay;
cc.currentStamina -= damage.staminaBlockCost; cc.currentStamina -= damage.staminaBlockCost;
} }
// apply damage
damage.hitReaction = !isBlocking || damage.ignoreDefense; damage.hitReaction = !isBlocking || damage.ignoreDefense;
cc.TakeDamage(damage); cc.TakeDamage(damage);
} }
public virtual vICharacter character public virtual vICharacter character { get { return cc; } }
{
get { return cc; }
}
#endregion Melee Methods #endregion
#region Update Animations
public virtual int defaultMoveSetID { get; set; }
public virtual bool overrideWeaponMoveSetID { get; set; }
public virtual int meleeMoveSetID
{
get
{
if (meleeManager == null) meleeManager = GetComponent<vMeleeManager>();
int id = meleeManager?.GetMoveSetID() ?? 0;
if (id == 0 || overrideWeaponMoveSetID) id = defaultMoveSetID;
return id;
}
}
public virtual void ResetMeleeAnimations()
{
if (meleeManager == null || cc == null || cc.animator == null) return;
cc.animator.SetBool(vAnimatorParameters.IsBlocking, false);
}
public virtual int AttackID { get { if (meleeManager == null) meleeManager = GetComponent<vMeleeManager>(); return meleeManager?.GetAttackID() ?? 0; } }
public virtual int DefenseID { get { if (meleeManager == null) meleeManager = GetComponent<vMeleeManager>(); return meleeManager?.GetDefenseID() ?? 0; } }
protected virtual void UpdateMeleeAnimations()
{
if (cc == null || cc.animator == null || meleeManager == null) return;
cc.animator.SetInteger(vAnimatorParameters.AttackID, AttackID);
cc.animator.SetInteger(vAnimatorParameters.DefenseID, DefenseID);
cc.animator.SetBool(vAnimatorParameters.IsBlocking, isBlocking);
cc.animator.SetFloat(vAnimatorParameters.MoveSet_ID, meleeMoveSetID, .2f, vTime.deltaTime);
isEquipping = cc.IsAnimatorTag("IsEquipping");
}
#endregion
} }
} }

View File

@@ -50,6 +50,12 @@ EditorBuildSettings:
- enabled: 1 - enabled: 1
path: Assets/Scenes/Land_01/Land_of_Death_River1_Valley02.unity path: Assets/Scenes/Land_01/Land_of_Death_River1_Valley02.unity
guid: dabdc796f82531d42b4dfb1bab670dda guid: dabdc796f82531d42b4dfb1bab670dda
- enabled: 1
path: Assets/Scenes/Fight_Arena/Fight_Arena.unity
guid: c403a1db592cfb34988bc76714980ecf
- enabled: 1
path: Assets/Scenes/Land_01/Land_Of_Death_ARENAS.unity
guid: 3ac39dab4b05aa540af7a4bbd73b2a5a
m_configObjects: m_configObjects:
com.unity.addressableassets: {fileID: 11400000, guid: 272caeb75b6504dcb987edb253f851b7, com.unity.addressableassets: {fileID: 11400000, guid: 272caeb75b6504dcb987edb253f851b7,
type: 2} type: 2}