I don’t understand, I think I have the checks for in range and can attack the same as what @sampattuzzi changed them too… why won’t this work?
Fighter.cs
using System;
using System.Collections.Generic;
using RPG.Core;
using RPG.Movement;
using RPG.Attributes;
using RPG.Saving;
using RPG.Stats;
using UnityEngine;
using UnityEngine.Events;
namespace RPG.Combat
{
public class Fighter : MonoBehaviour, IAction, ISaveable, IModifierProvider
{
// SHIPPING DATA //
ActionScheduler actionScheduler;
Animator animator;
BaseStats baseStatsScript;
// CHANGING DATA //
[SerializeField] UnityEvent launchProjectile;
[SerializeField] UnityEvent launchMagic;
Mover moverScript;
[SerializeField] float timeSinceLastAttack = Mathf.Infinity;
[Space(10)]
[SerializeField] Transform rightHandTransform;
[SerializeField] Transform leftHandTransform;
[Space(10)]
[SerializeField] WeaponSO weaponSO;
[Space(5)]
[SerializeField] WeaponSO defaultWeaponSO;
[Header("Debug only DO NOT CHANGE")]
[Space(10)]
[SerializeField] Health target;
[SerializeField] WeaponComponent currentWeapon;
// * CODE * //
private void Awake()
{
moverScript = GetComponent<Mover>();
actionScheduler = GetComponent<ActionScheduler>();
animator = GetComponent<Animator>();
baseStatsScript = GetComponent<BaseStats>();
}
void Start()
{
if (weaponSO == null)
{
EquipWeapon(defaultWeaponSO);
weaponSO = defaultWeaponSO;
//print(this + " script just loaded " + defaultWeapon + " on " + name);
}
}
void Update()
{
timeSinceLastAttack += Time.deltaTime;
if (target != null && target.IsDead()) target = null;
if (target == null) return;
if (!GetIsInRange(target.transform))
{
MoveToAttack();
}
else
{
moverScript.Cancel();
AttackBehavior();
}
}
public void EquipWeapon(WeaponSO weaponSO)
{
if (weaponSO == null) return;
Animator animator = GetComponent<Animator>();
weaponSO.SpawnWeapon(rightHandTransform, leftHandTransform, animator);
currentWeapon = weaponSO.GetWeaponEquipped();
}
private void MoveToAttack()
{
if (target == null) return;
moverScript.MoveToTarget(target.transform.position);
if (GetIsInRange(target.transform))
{
moverScript.Cancel();
AttackBehavior();
}
}
private void AttackBehavior()
{
transform.LookAt(target.transform);
if (timeSinceLastAttack >= weaponSO.GetTimeBetweenAttacks())
{
animator.SetBool("stopAttack", false);
animator.SetTrigger("attack");
timeSinceLastAttack = 0;
}
}
// Animation Event from * RPG-Character@Unarmed-Attack-L3 *
void Hit()
{
if (target == null) return;
currentWeapon.OnHit();
float damageTaken = baseStatsScript.GetStat(Stat.Damage);
target.TakeDamage(gameObject, damageTaken);
//print(name + " did " + damageTaken + " with " + weaponSO.name);
}
// Animation Event from * RPG-Character@2Hand-Bow-Attack1 *
void Shoot()
{
if (target == null) return;
SpawnProjectile();
if (weaponSO.GetIsMagic())
{
launchMagic.Invoke();
}
else
{
launchProjectile.Invoke();
}
}
private void SpawnProjectile()
{
WeaponComponent newestProjectile =
Instantiate(weaponSO.GetProjectile(), currentWeapon.transform.position, Quaternion.identity);
newestProjectile.GetComponent<Projectile>().SetTargetToHit(target);
newestProjectile.GetComponent<Projectile>().SetInstigator(gameObject);
newestProjectile.GetComponent<Projectile>().weaponSO = weaponSO;
}
private bool GetIsInRange(Transform targetInQuestion)
{
var answer = Vector3.Distance(transform.position, targetInQuestion.position) <= weaponSO.GetWeaponRange();
print("GetIsInRange() says " + answer);
return answer;
}
public bool CanAttack(GameObject combatTarget)
{
if (combatTarget == null) return false;
if (!GetComponent<Mover>().CanMoveTo(combatTarget.transform.position) && !GetIsInRange(combatTarget.transform)) return false;
Health targetToTest = combatTarget.GetComponent<Health>();
return targetToTest != null && !targetToTest.IsDead();
}
public void Attack(GameObject combatTarget)
{
actionScheduler.StartAction(this);
target = combatTarget.GetComponent<Health>();
//print("take that " + combatTarget + " you flat faced heathen!");
}
public void Cancel()
{
target = null;
animator.ResetTrigger("attack");
animator.SetBool("stopAttack", true);
moverScript.Cancel();
}
public WeaponSO SetWeaponSO(WeaponSO sO)
{
weaponSO = sO;
return null;
}
public GameObject SetCurrentWeapon(WeaponComponent weaponEquipped)
{
currentWeapon = weaponEquipped;
return null;
}
public object CaptureState()
{
return weaponSO.name;
}
public void RestoreState(object state)
{
string savedWeaponName = (string)state;
weaponSO = UnityEngine.Resources.Load<WeaponSO>(savedWeaponName);
EquipWeapon(weaponSO);
}
public Health GetTarget()
{
if (target == null) return null;
return target;
}
public IEnumerable<float> GetAdditiveModifiers(Stat stat)
{
if (stat == Stat.Damage)
{
yield return weaponSO.GetWeaponDamage();
}
}
public IEnumerable<float> GetPercentageModifiers(Stat stat)
{
if (stat == Stat.Damage)
{
yield return weaponSO.GetPercentageBonus();
}
}
}
}
Mover.cs
using RPG.Core;
using UnityEngine;
using UnityEngine.AI;
using RPG.Saving;
using System.Collections.Generic;
using RPG.Attributes;
namespace RPG.Movement
{
public class Mover : MonoBehaviour, IAction, ISaveable
{
// SHIPPING DATA //
NavMeshAgent navMeshAgent;
Animator playerAnimator;
ActionScheduler actionScheduler;
Health healthScript;
// CHANGING DATA //
[SerializeField] float maxNavPathLength = 40f;
// * CODE * //
void Start()
{
navMeshAgent = GetComponent<NavMeshAgent>();
playerAnimator = GetComponent<Animator>();
actionScheduler = GetComponent<ActionScheduler>();
healthScript = GetComponent<Health>();
}
void Update()
{
navMeshAgent.enabled = !healthScript.IsDead();
UpdateAnimator();
}
public void Cancel()
{
//playerAnimator.applyRootMotion = false;
navMeshAgent.isStopped = true;
}
public void StartMovementAction(Vector3 destination)
{
//playerAnimator.applyRootMotion = true;
actionScheduler.StartAction(this);
navMeshAgent.isStopped = false;
navMeshAgent.destination = destination;
}
public bool CanMoveTo(Vector3 destination)
{
NavMeshPath path = new NavMeshPath();
bool hasPath = NavMesh.CalculatePath(transform.position, destination, NavMesh.AllAreas, path);
if (!hasPath) return false;
if (path.status != NavMeshPathStatus.PathComplete) return false;
if (GetPathLength(path) > maxNavPathLength) return false;
return true;
}
private float GetPathLength(NavMeshPath path)
{
float total = 0;
if (path.corners.Length < 2) return total;
for (int i = 0; i < path.corners.Length - 1; i++)
{
total += Vector3.Distance(path.corners[i], path.corners[i + 1]);
}
return total;
}
public void MoveToTarget(Vector3 destination)
{
//playerAnimator.applyRootMotion = true;
navMeshAgent.isStopped = false;
navMeshAgent.destination = destination;
}
private void UpdateAnimator()
{
Vector3 velocity = navMeshAgent.velocity; // gets a global velocity of where we are in the world
Vector3 localVelocity = transform.InverseTransformDirection(velocity); // converts that into something local so the animator has a reference for what animation at what z speed
float speed = localVelocity.z; // our final z velocity
playerAnimator.SetFloat("fowardSpeed", speed);
}
public object CaptureState()
{
Dictionary<string, object> moverDataDict = new Dictionary<string, object>();
moverDataDict["position"] = new SerializableVector3(transform.position);
moverDataDict["rotation"] = new SerializableVector3(transform.eulerAngles);
return moverDataDict;
}
public void RestoreState(object state)
{
Dictionary<string, object> moverDataDict = (Dictionary<string, object>)state;
var loadedPosition = ((SerializableVector3)moverDataDict["position"]).ToVector();
var loadedRotation = ((SerializableVector3)moverDataDict["rotation"]).ToVector();
GetComponent<NavMeshAgent>().Warp(loadedPosition);
transform.eulerAngles = loadedRotation;
}
}
}
and just in case…
PlayerController.cs
using UnityEngine;
using RPG.Movement;
using RPG.Combat;
using RPG.Attributes;
using System;
using UnityEngine.EventSystems;
using UnityEngine.AI;
namespace RPG.Control
{
public class PlayerController : MonoBehaviour
{
// shipping data //
Health healthScript;
[Serializable]
struct CursorMapping
{
public CursorType type;
public Texture2D texture;
public Vector2 hotspot;
}
[SerializeField] CursorMapping[] cursorMappings;
[SerializeField] float maxNavMeshProjectionDistance = 1f;
[SerializeField] float SpherecastRadius = 0.5f;
// code //
void Awake()
{
healthScript = GetComponent<Health>();
}
void Update()
{
if (InteractWithUI())
{
SetCursor(CursorType.UI);
return;
}
if (healthScript.IsDead())
{
SetCursor(CursorType.None);
return;
}
if (InteractWithComponent())
{
return;
}
if (InteractWithMovement()) return;
SetCursor(CursorType.None);
}
private bool InteractWithComponent()
{
RaycastHit[] hits = RaycastAllSorted();
foreach (RaycastHit raycastHit in hits)
{
if (!GetComponent<Mover>().CanMoveTo(raycastHit.transform.position)) return false;
IRayCastable[] rayCastables = raycastHit.transform.GetComponents<IRayCastable>();
foreach (IRayCastable rayCastable in rayCastables)
{
if (rayCastable.HandleRaycast(this))
{
SetCursor(rayCastable.GetCursorType());
return true;
}
}
}
return false;
}
RaycastHit[] RaycastAllSorted()
{
RaycastHit[] hits = Physics.SphereCastAll(GetMouseRay(), SpherecastRadius);
float[] distances = new float[hits.Length];
for (int i = 0; i < hits.Length; i++)
{
distances[i] = hits[i].distance;
}
Array.Sort(distances, hits);
return hits;
}
private bool InteractWithUI()
{
return EventSystem.current.IsPointerOverGameObject(); // UI = true
}
private bool InteractWithMovement()
{
Vector3 target;
bool hasHit = RaycastNavmesh(out target);
if (hasHit)
{
if (!GetComponent<Mover>().CanMoveTo(target)) return false;
if (Input.GetMouseButton(0))
{
GetComponent<Mover>().StartMovementAction(target);
}
SetCursor(CursorType.Movement);
return true;
}
return false;
}
private bool RaycastNavmesh(out Vector3 target)
{
target = new Vector3();
RaycastHit hit;
bool hasHit = Physics.Raycast(GetMouseRay(), out hit);
if (!hasHit) return false;
NavMeshHit navMeshHit;
bool hasCasToNavMesh = NavMesh.SamplePosition(
hit.point, out navMeshHit, maxNavMeshProjectionDistance, NavMesh.AllAreas);
if (!hasCasToNavMesh) return false;
target = navMeshHit.position;
return true;
}
public void SetCursor(CursorType type)
{
CursorMapping mapping = GetCursorMapping(type);
Cursor.SetCursor(mapping.texture, mapping.hotspot, CursorMode.Auto);
}
private CursorMapping GetCursorMapping(CursorType type)
{
foreach (CursorMapping mapping in cursorMappings)
{
if (mapping.type == type)
{
return mapping;
}
}
return cursorMappings[0];
}
private static Ray GetMouseRay()
{
return Camera.main.ScreenPointToRay(Input.mousePosition);
}
}
}