Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 131 additions & 52 deletions Assets/Scripts/Game/DaggerfallMissile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,20 @@ public class DaggerfallMissile : MonoBehaviour
public const float SphereCastRadius = 0.25f;
public const float TouchRange = 3.0f;

private static int TouchMask = -1;

private static readonly Collider[] aoeBuffer = new Collider[64];
private readonly List<DaggerfallEntityBehaviour> tmpTargets = new List<DaggerfallEntityBehaviour>(32);

// Cached references
private GameManager gm;
private Camera mainCamera;
private WeaponManager weaponManager;
private Collider casterCollider;
private EnemySenses cachedEnemySenses;
private EnemyAttack cachedEnemyAttack;
private CharacterController casterController;

Vector3 direction;
Light myLight;
SphereCollider myCollider;
Expand Down Expand Up @@ -169,88 +183,106 @@ public DaggerfallEntityBehaviour[] Targets
private void Awake()
{
audioSource = transform.GetComponent<DaggerfallAudioSource>();

gm = GameManager.Instance;
mainCamera = gm.MainCamera;
weaponManager = gm.WeaponManager;
audioSource = GetComponent<DaggerfallAudioSource>();
}

private void Start()
{
// Setup light and shadows
myLight = GetComponent<Light>();
myLight.enabled = EnableLight;
myLight.enabled = EnableLight && DaggerfallUnity.Settings.EnableSpellLighting;
forceDisableSpellLighting = !DaggerfallUnity.Settings.EnableSpellLighting;
if (forceDisableSpellLighting) myLight.enabled = false;
if (!DaggerfallUnity.Settings.EnableSpellShadows) myLight.shadows = LightShadows.None;
if (!DaggerfallUnity.Settings.EnableSpellShadows)
{
myLight.shadows = LightShadows.None;
}

initialRange = myLight.range;
initialIntensity = myLight.intensity;

// Setup collider
// Setup collider and rigidbody
myCollider = GetComponent<SphereCollider>();
myCollider.radius = ColliderRadius;

// Setup rigidbody
myRigidbody = GetComponent<Rigidbody>();
myRigidbody.useGravity = false;

// Use payload when available
// Setup payload
if (payload != null)
{
// Set payload missile properties
caster = payload.CasterEntityBehaviour;
targetType = payload.Settings.TargetType;
elementType = payload.Settings.ElementType;

// Set spell billboard anims automatically from payload for mobile missiles
if (targetType == TargetTypes.SingleTargetAtRange ||
targetType == TargetTypes.AreaAtRange)
if (targetType == TargetTypes.SingleTargetAtRange || targetType == TargetTypes.AreaAtRange)
{
UseSpellBillboardAnims();
}
}

// Setup senses
if (caster && caster != GameManager.Instance.PlayerEntityBehaviour)
// Cache caster components
if (caster)
{
casterCollider = caster.GetComponent<Collider>();
cachedEnemySenses = caster.GetComponent<EnemySenses>();
cachedEnemyAttack = caster.GetComponent<EnemyAttack>();
casterController = caster.GetComponent<CharacterController>();

var missileCollider = GetComponent<Collider>();
if (casterCollider && missileCollider)
{
Physics.IgnoreCollision(casterCollider, missileCollider);
}
}

// Cache enemy senses (non-player casters only)
if (caster && caster != gm.PlayerEntityBehaviour)
{
enemySenses = caster.GetComponent<EnemySenses>();
enemySenses = cachedEnemySenses;
}

// Setup arrow
if (isArrow)
{
// Create and orient 3d arrow
goModel = GameObjectHelper.CreateDaggerfallMeshGameObject(99800, transform);
MeshCollider arrowCollider = goModel.GetComponent<MeshCollider>();
var arrowCollider = goModel.GetComponent<MeshCollider>();
arrowCollider.sharedMesh = goModel.GetComponent<MeshFilter>().sharedMesh;
arrowCollider.convex = true;
arrowCollider.isTrigger = true;

// Offset up so it comes from same place LOS check is done from
Vector3 adjust;
if (caster != GameManager.Instance.PlayerEntityBehaviour)
if (caster != gm.PlayerEntityBehaviour)
{
CharacterController controller = caster.transform.GetComponent<CharacterController>();
adjust = caster.transform.forward * 0.6f;
adjust.y += controller.height / 3;
if (casterController)
{
adjust.y += casterController.height / 3f;
}
}
else
{
// Adjust slightly downward to match bow animation
adjust = (GameManager.Instance.MainCamera.transform.rotation * -Caster.transform.up) * 0.11f;
// Offset forward to avoid collision with player
adjust += GameManager.Instance.MainCamera.transform.forward * 0.6f;
// Adjust to the right or left to match bow animation
if (!GameManager.Instance.WeaponManager.ScreenWeapon.FlipHorizontal)
adjust += GameManager.Instance.MainCamera.transform.right * 0.15f;
adjust = (gm.MainCamera.transform.rotation * -caster.transform.up) * 0.11f;
adjust += gm.MainCamera.transform.forward * 0.6f;

var right = gm.MainCamera.transform.right * 0.15f;
if (!gm.WeaponManager.ScreenWeapon.FlipHorizontal)
{
adjust += right;
}
else
adjust -= GameManager.Instance.MainCamera.transform.right * 0.15f;
{
adjust -= right;
}
}

goModel.transform.localPosition = adjust;
goModel.transform.rotation = Quaternion.LookRotation(GetAimDirection());
goModel.layer = gameObject.layer;
}

// Ignore missile collision with caster (this is a different check to AOE targets)
if (caster)
Physics.IgnoreCollision(caster.GetComponent<Collider>(), this.GetComponent<Collider>());
}

private void Update()
Expand Down Expand Up @@ -390,15 +422,19 @@ void DoCollision(Collision collision, Collider other)

public static DaggerfallEntityBehaviour GetEntityTargetInTouchRange(Vector3 aimPosition, Vector3 aimDirection)
{
// Fire ray along caster facing
// Origin point of ray is set back slightly to fix issue where strikes against target capsules touching caster capsule do not connect
// Nudge origin slightly forward to avoid intersecting the caster capsule when looking down
const float originNudge = 0.01f;
Vector3 origin = aimPosition + aimDirection * originNudge;

RaycastHit hit;
aimPosition -= aimDirection * 0.1f;
Ray ray = new Ray(aimPosition, aimDirection);
if (Physics.SphereCast(ray, SphereCastRadius, out hit, TouchRange))
Ray ray = new Ray(origin, aimDirection);

if (Physics.SphereCast(ray, SphereCastRadius, out hit, TouchRange, GetTouchMask(), QueryTriggerInteraction.Ignore))
{
return hit.transform.GetComponent<DaggerfallEntityBehaviour>();
else
return null;
}

return null;
}

#endregion
Expand Down Expand Up @@ -438,34 +474,70 @@ void DoMissile()
// AOE can strike any number of targets within range with an option to exclude caster
void DoAreaOfEffect(Vector3 position, bool ignoreCaster = false)
{
List<DaggerfallEntityBehaviour> entities = new List<DaggerfallEntityBehaviour>();

transform.position = position;

// Collect AOE targets and ignore duplicates
Collider[] overlaps = Physics.OverlapSphere(position, ExplosionRadius);
for (int i = 0; i < overlaps.Length; i++)
int count = Physics.OverlapSphereNonAlloc(position, ExplosionRadius, aoeBuffer, GetTouchMask(), QueryTriggerInteraction.Ignore);
tmpTargets.Clear();

for (int i = 0; i < count; i++)
{
DaggerfallEntityBehaviour aoeEntity = overlaps[i].GetComponent<DaggerfallEntityBehaviour>();
var beh = aoeBuffer[i].GetComponent<DaggerfallEntityBehaviour>();
if (!beh)
{
continue;
}

if (ignoreCaster && aoeEntity == caster)
if (ignoreCaster && beh == caster)
{
continue;
}

if (aoeEntity && !targetEntities.Contains(aoeEntity))
if (!targetEntities.Contains(beh))
{
entities.Add(aoeEntity);
//Debug.LogFormat("Missile hit target {0} by AOE", aoeEntity.name);
tmpTargets.Add(beh);
}
}

// Add collection to target entities
if (entities.Count > 0)
targetEntities.AddRange(entities);
if (tmpTargets.Count > 0)
{
targetEntities.AddRange(tmpTargets);
}

impactDetected = true;
missileReleased = true;
}

/// <summary>
/// Returns a cached layer mask for touch targeting. Built lazily to avoid Unity init errors.
/// Excludes Player, Ignore Raycast, and Automap so the cast never hits the caster or UI layers.
/// </summary>
private static int GetTouchMask()
{
if (TouchMask != -1)
{
return TouchMask;
}

int mask = Physics.DefaultRaycastLayers;

int player = LayerMask.NameToLayer("Player");
if (player >= 0)
{
mask &= ~(1 << player);
}

mask &= ~(1 << Physics.IgnoreRaycastLayer);

int automap = LayerMask.NameToLayer("Automap");
if (automap >= 0)
{
mask &= ~(1 << automap);
}

TouchMask = mask;
return TouchMask;
}

// Get missile aim position from player or enemy mobile
Vector3 GetAimPosition()
{
Expand Down Expand Up @@ -608,7 +680,14 @@ void AssignBowDamageToTarget(Collider arrowHitCollider)
else
{
Transform hitTransform = arrowHitCollider.gameObject.transform;
GameManager.Instance.WeaponManager.WeaponDamage(GameManager.Instance.WeaponManager.LastBowUsed, true, isArrowSummoned, hitTransform, hitTransform.position, goModel.transform.forward);

GameManager.Instance.WeaponManager.WeaponDamage(
GameManager.Instance.WeaponManager.LastBowUsed,
true,
isArrowSummoned,
hitTransform,
hitTransform.position,
goModel.transform.forward);
}
}

Expand Down
11 changes: 7 additions & 4 deletions Assets/Scripts/Game/WeaponManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -193,12 +193,15 @@ public enum MouseDirections

void Start()
{
//weaponSensitivity = DaggerfallUnity.Settings.WeaponSensitivity;
mainCamera = GameObject.FindGameObjectWithTag("MainCamera");
player = transform.gameObject;
playerLayerMask = ~(1 << LayerMask.NameToLayer("Player"));
player = gameObject;

playerLayerMask = Physics.DefaultRaycastLayers;
playerLayerMask &= ~(1 << LayerMask.NameToLayer("Player"));
playerLayerMask &= ~(1 << Physics.IgnoreRaycastLayer);

_gesture = new Gesture();
_longestDim = Math.Max(Screen.width, Screen.height);
_longestDim = Mathf.Max(Screen.width, Screen.height);
SetMelee(ScreenWeapon);
}

Expand Down