/// <summary> /// Ends the item use. /// </summary> private void EndUse() { if (!InUse()) { return; } if (m_UseEvent != null) { Scheduler.Cancel(ref m_UseEvent); } if (m_CastParticles) { m_CastParticles.Stop(); } if (m_CastSound != null && m_CastSound.Length > 0 && m_CastMode == CastMode.Continuous) { m_AudioSource.Stop(); } if (m_RegenerateDelay != 0) { m_RegenerateEvent = Scheduler.Schedule(m_RegenerateDelay, RegenerateAmmo); } m_InUse = false; m_Used = false; m_StopContinuousUse = false; m_AimStates.NextState(); m_UseStates.NextState(); EventHandler.ExecuteEvent(m_Character, "OnItemStopUse"); }
/// <summary> /// Cancels the spawn from occurring. /// </summary> public void CancelSpawn() { if (m_RespawnEvent != null) { Scheduler.Cancel(ref m_RespawnEvent); } }
/// <summary> /// Cancel the scheduled activation if the timer isn't what caused the projectile to deactivate, and disable the TrailRenderer. /// </summary> private void OnDisable() { Scheduler.Cancel(ref m_ScheduledActivation); m_Rigidbody.velocity = Vector3.zero; m_Rigidbody.angularVelocity = Vector3.zero; // Set the TrailRenderer time to a negative value to prevent a trail from being added when the object pool changes the position of the projectile. if (m_TrailRenderer) { m_TrailRenderer.time = -m_TrailRenderer.time; } }
/// <summary> /// The projectile has collided. Apply damage to the object hit and spawn various effects (explosion, decal, etc). /// </summary> /// <param name="originator">The originator of the projectile.</param> /// <param name="collisionTransform">The collision object that the destructable collided with. Can be null.</param> /// <param name="collisionPoint">The point of destruction.</param> /// <param name="collisionNormal">The normal at the destruction point.</param> /// <param name="destroy">Should the projectile be destroyed and placed back in the ObjectPool?</param> protected override void Collide(GameObject originator, Transform collisionTransform, Vector3 collisionPoint, Vector3 collisionNormal, bool destroy) { base.Collide(originator, collisionTransform, collisionPoint, collisionNormal, destroy); enabled = false; if (!destroy) { // If the projectile isn't being destroyed then set the parent, make it kinematic, and deactivate the collider to prevent it from interfering with other objects. m_Transform.parent = collisionTransform; m_Rigidbody.isKinematic = true; m_Collider.enabled = false; Scheduler.Cancel(ref m_ScheduledActivation); } }
/// <summary> /// Do the explosion. /// </summary> /// <param name="originator">The originator of the object.</param> public void Explode(GameObject originator) { // Cancel the explosion event if it exists. Scheduler.Cancel(ref m_ExplosionEvent); // Loop through all of the nearby colliders and apply an explosion force and damage. Rigidbody colliderRigidbody = null; Health health = null; var hitCount = Physics.OverlapSphereNonAlloc(m_Transform.position, m_Radius, m_HitColliders, -1, QueryTriggerInteraction.Ignore); for (int i = 0; i < hitCount; ++i) { // A GameObject can contain multiple colliders. Prevent the explosion from occurring on the same GameObject multiple times. if (m_GameObjectExplosions.Contains(m_HitColliders[i].gameObject)) { continue; } m_GameObjectExplosions.Add(m_HitColliders[i].gameObject); // If the Health component exists it will apply an explosive force to the rigidbody in addition to deducting the health. Otherwise just apply the force to the rigidbody. if ((health = m_HitColliders[i].transform.GetComponentInParent <Health>()) != null) { // The further out the collider is, the less it is damaged. var direction = m_Transform.position - m_HitColliders[i].transform.position; var damageModifier = Mathf.Max(1 - (direction.magnitude / m_Radius), 0.01f); health.Damage(m_DamageAmount * damageModifier, m_Transform.position, direction.normalized * -m_ImpactForce * damageModifier, m_Radius, originator); } else if ((colliderRigidbody = Utility.GetComponentForType <Rigidbody>(m_HitColliders[i].gameObject)) != null) { colliderRigidbody.AddExplosionForce(m_ImpactForce, m_Transform.position, m_Radius); } // Execute any custom events. if (!string.IsNullOrEmpty(m_DamageEvent)) { EventHandler.ExecuteEvent(m_HitColliders[i].gameObject, m_DamageEvent, m_DamageAmount, m_Transform.position, m_Transform.forward * -m_ImpactForce, originator); } } m_GameObjectExplosions.Clear(); // Boom. if (m_Sound != null) { m_AudioSource.clip = m_Sound; m_AudioSource.Play(); } Scheduler.Schedule(m_Lifespan, Destroy); }
/// <summary> /// The object has been enabled. Set IsDepleted to false and initialize the rigidbody. /// </summary> private void OnEnable() { m_IsDepleted = false; m_Renderer.enabled = enabled; if (m_Rigidbody != null) { m_Rigidbody.isKinematic = false; m_Collider.enabled = true; if (m_TriggerEnableEvent != null) { Scheduler.Cancel(ref m_TriggerEnableEvent); } m_Trigger.enabled = false; } }
/// <summary> /// The object is no longer alive. Kill it. /// </summary> /// <param name="position">The position of the damage.</param> /// <param name="force">The amount of force applied to the object while taking the damage.</param> /// <param name="attacker">The GameObject that killed the character.</param> private void DieLocal(Vector3 position, Vector3 force, GameObject attacker) { // Notify those interested. EventHandler.ExecuteEvent(m_GameObject, "OnDeath"); EventHandler.ExecuteEvent <Vector3, Vector3, GameObject>(m_GameObject, "OnDeathDetails", force, position, attacker); // Spawn any objects on death, such as an explosion if the object is an explosive barrell. for (int i = 0; i < m_SpawnedObjectsOnDeath.Length; ++i) { var spawnedObject = ObjectPool.Instantiate(m_SpawnedObjectsOnDeath[i], transform.position, transform.rotation); Explosion explosion; if ((explosion = Utility.GetComponentForType <Explosion>(spawnedObject)) != null) { explosion.Originator = gameObject; } } // Destroy any objects on death. The objects will be placed back in the object pool if they were created within it otherwise the object will be destroyed. for (int i = 0; i < m_DestroyedObjectsOnDeath.Length; ++i) { if (ObjectPool.SpawnedWithPool(m_DestroyedObjectsOnDeath[i])) { ObjectPool.Destroy(m_DestroyedObjectsOnDeath[i]); } else { Object.Destroy(m_DestroyedObjectsOnDeath[i]); } } // Change the layer to a death layer. if (m_DeathLayer.value != 0) { m_AliveLayer = m_GameObject.layer; m_GameObject.layer = m_DeathLayer; } // Stop any scheduled events. Scheduler.Cancel(ref m_ShieldRegenerativeEvent); // Deactivate the object if requested. if (m_DeactivateOnDeath) { Scheduler.Schedule(m_DeactivateOnDeathDelay, Deactivate); } }
/// <summary> /// Try to fire the weapon. The weapon may not be able to be fired for various reasons such as it already firing or it being out of ammo. /// </summary> public override bool TryUse() { if (!m_IsFiring && m_CanFire && m_LastShootTime + m_ShootDelay < Time.time) { if (m_Inventory.GetItemCount(m_ConsumableItemType) > 0) { m_IsFiring = true; // Prevent the weapon from continuously firing if it not a fully automatic. AI agents do not have to follow this because they don't manually stop firing. m_CanFire = m_FireMode == FireMode.FullAuto || m_AIAgent.Invoke(); // Do not regenerate any more ammo after starting to fire. if (m_RegenerateEvent != null) { Scheduler.Cancel(ref m_RegenerateEvent); } // Wait until the used event is called before firing. if (!m_FireOnUsedEvent) { DoFire(); } else { EventHandler.ExecuteEvent(m_Character, "OnUpdateAnimator"); } return(true); } else { #if ENABLE_MULTIPLAYER m_NetworkMonitor.ExecuteItemEvent(m_ConsumableItemType.ID, "OnItemEmptyClip"); #else EmptyClip(); #endif } } return(false); }
/// <summary> /// Try to cast the magic. The item may not be able be casted if the last cast time was too recent. /// <returns>True if the item was used.</returns> /// </summary> public bool TryUse() { if (!m_InUse && m_LastCastTime + m_UseDelay < Time.time) { if (m_Inventory.GetItemCount(m_ItemType) >= m_CastAmount) { m_LastCastTime = Time.time; m_InUse = true; // Do not regenerate any more ammo after starting to use the item. if (m_RegenerateEvent != null) { Scheduler.Cancel(ref m_RegenerateEvent); } EventHandler.ExecuteEvent(m_Character, "OnItemUse"); return(true); } else { EventHandler.ExecuteEvent(m_Character, "OnItemStopUse"); } } return(false); }
/// <summary> /// Stop firing the weapon. Cancel and reset the affected variables. /// </summary> /// <param name="success">Did the item successfully fire?</param> private void StopFiring(bool success) { // Can't stop firing if the weapon isn't firing to begin with. if (!m_IsFiring) { EventHandler.ExecuteEvent(m_Character, "OnItemStopUse"); return; } m_IsFiring = false; m_CurrentBurst = m_BurstRate; m_UseStates.NextState(); if (m_FireEvent != null) { Scheduler.Cancel(ref m_FireEvent); } // Keep repeating the empty clip method until the stop used event is called. if (m_EmptyClipEvent == null && success && ((m_FireMode != FireMode.SemiAuto && m_Inventory.GetItemCount(m_ConsumableItemType, true) == 0) || (m_FireMode == FireMode.Burst && m_CurrentBurst == 0))) { m_EmptyClipEvent = Scheduler.Schedule(m_ShootDelay, EmptyClip); } if (m_FireParticles) { m_FireParticles.Stop(); } if (m_RegenerateDelay != 0) { m_RegenerateEvent = Scheduler.Schedule(m_RegenerateDelay, RegenerateAmmo); } EventHandler.ExecuteEvent(m_Character, "OnItemStopUse"); }
/// <summary> /// The weapon no longer has any ammo. Play the empty clip audio. /// </summary> private void EmptyClip() { // Don't play another empty clip sound effect if it is already scheduled. if (m_EmptyClipEvent != null) { Scheduler.Cancel(ref m_EmptyClipEvent); } if (m_EmptyFireSound != null && m_EmptyFireSound.Length > 0 && !m_AudioSource.isPlaying) { m_AudioSource.clip = m_EmptyFireSound[Random.Range(0, m_EmptyFireSound.Length)]; m_AudioSource.Play(); } // Keep repeating until the stop used event is called. if ((m_FireMode != FireMode.SemiAuto && m_Inventory.GetItemCount(m_ConsumableItemType, true) == 0) || (m_FireMode == FireMode.Burst && m_CurrentBurst == 0)) { m_EmptyClipEvent = Scheduler.Schedule(m_ShootDelay, EmptyClip); } else { EventHandler.ExecuteEvent(m_Character, "OnItemStopUse"); } }
/// <summary> /// Stops the empty clip from playing. /// </summary> private void StopEmptyClip() { Scheduler.Cancel(ref m_EmptyClipEvent); }
/// <summary> /// The object has been damaged. Update the health and shield. Execute the OnDeath events if the health and shield is equal to zero. /// </summary> /// <param name="amount">The amount of damage taken.</param> /// <param name="position">The position of the damage.</param> /// <param name="force">The amount of force applied to the object while taking the damage.</param> /// <param name="radius">The radius of the explosive damage. If 0 then a non-exposive force will be used.</param> /// <param name="attacker">The GameObject that did the damage.</param> /// <param name="hitGameObject">The GameObject that was hit.</param> private void DamageLocal(float amount, Vector3 position, Vector3 force, float radius, GameObject attacker, GameObject hitGameObject) { // Add a multiplier if a particular GameObject was hit. Do not apply a multiplier if the damage is applied through a radius because multiple // GameObjects are hit. if (radius == 0 && hitGameObject != null) { DamageMultiplier damageMultiplier; if (m_DamageMultiplierMap != null && m_DamageMultiplierMap.TryGetValue(hitGameObject, out damageMultiplier)) { amount *= damageMultiplier.Multiplier; } } // Apply the damage to the shield first because the shield can regenrate. if (m_CurrentShield > 0) { var shieldAmount = Mathf.Min(amount, m_CurrentShield); amount -= shieldAmount; SetShieldAmount(m_CurrentShield - shieldAmount); } // Decrement the health by remaining amount after the shield has taken damage. if (m_CurrentHealth > 0) { SetHealthAmount(m_CurrentHealth - Mathf.Min(amount, m_CurrentHealth)); } // Apply a force to the hit rigidbody if the force is greater than 0. if (m_Rigidbody != null && !m_Rigidbody.isKinematic && force.sqrMagnitude > 0) { if (radius == 0) { m_Rigidbody.AddForceAtPosition(force, position); } else { m_Rigidbody.AddExplosionForce(force.magnitude, position, radius); } } // Let other interested objects know that the object took damage. EventHandler.ExecuteEvent <float, Vector3, Vector3, GameObject>(m_GameObject, "OnHealthDamageDetails", amount, position, force, attacker); // The shield should stop regenerating when the object is taking damage. Scheduler.Cancel(ref m_ShieldRegenerativeEvent); // The object is dead when there is no more health or shield. if (!IsAlive()) { Die(position, force, attacker); // Regenerate the shield if the unit is still alive after taking damage. The server should be the only object regenerating the shield if on the network. #if ENABLE_MULTIPLAYER } else if (m_MaxShield > 0 && isServer) { #else } else if (m_MaxShield > 0) { #endif m_ShieldRegenerativeEvent = Scheduler.Schedule(m_ShieldRegenerativeInitialWait, RegenerateShield); } }