Exemplo n.º 1
0
    public override void OnHit(RaycastHit rh)
    {
        if (origin.CanDamage(Character.FromObject(rh.collider.gameObject), allowFriendlyFire, allowSelfDamage))
        {
            // If the target has health, damage it
            DamageHitbox hitbox = rh.collider.GetComponent <DamageHitbox>();
            if (hitbox != null)
            {
                // Calculate damage
                int damageToDeal = damage;
                if (hitbox.critical)
                {
                    damageToDeal = Mathf.CeilToInt(damageToDeal * criticalMultiplier);
                }
                hitbox.Damage(damageToDeal, origin, type);
                // Play damage effects
            }

            // If the target has physics, knock it around
            Rigidbody rb = rh.collider.GetComponentInParent <Rigidbody>();
            if (rb != null)
            {
                rb.AddForceAtPosition(transform.forward * knockback, rh.point, ForceMode.Impulse);
                // Play knockback effects
            }

            base.OnHit(rh);
        }

        // Otherwise, ignore and resume normal operation
    }
Exemplo n.º 2
0
    /*
     * // Uses raycast grids and bound checks to scan for partially concealed colliders
     * public static bool LineOfSightCheckForVisionCone(Collider c, Vector3 origin, Vector3 forward, Vector3 worldUp, float angle, out RaycastHit checkInfo, float range, LayerMask viewable, float raycastSpacing = 0.2f)
     * {
     #region Create raycast grid data
     *  // Finds the largest of the bounds' 3 size axes and produces a value that always exceeds that distance, regardless of the shape and angle.
     *  float maxBoundsSize = Mathf.Max(Mathf.Max(c.bounds.size.x, c.bounds.size.y), c.bounds.size.z) * 2;
     *
     *  Vector3 originUp = Misc.PerpendicularUp(forward, worldUp);
     *  Vector3 originRight = Misc.PerpendicularRight(forward, worldUp);
     *
     *  // Use Bounds.ClosestPoint four times, with points to the left, right, up and down of the bounding box (relative to the cone centre). Then use Vector3.Distance to calculate the distances and produce a rectangle of certain dimensions.
     *  Vector3 upPoint = c.bounds.center + originUp * maxBoundsSize;
     *  Vector3 downPoint = c.bounds.center + -originUp * maxBoundsSize;
     *  Vector3 leftPoint = c.bounds.center + -originRight * maxBoundsSize;
     *  Vector3 rightPoint = c.bounds.center + originRight * maxBoundsSize;
     *  upPoint = c.bounds.ClosestPoint(upPoint);
     *  downPoint = c.bounds.ClosestPoint(downPoint);
     *  leftPoint = c.bounds.ClosestPoint(leftPoint);
     *  rightPoint = c.bounds.ClosestPoint(rightPoint);
     *
     *  // Produces dimensions for a rectangular area to sweep with raycasts
     *  float scanAreaY = Vector3.Distance(upPoint, downPoint);
     *  float scanAreaX = Vector3.Distance(leftPoint, rightPoint);
     *
     *
     *
     *
     *  // Divide the rectangle dimensions by the sphereCastDiameter to obtain the amount of spherecasts necessary to cover the area.
     *  int raycastArrayLength = Mathf.CeilToInt(scanAreaX / raycastSpacing);
     *  int raycastArrayHeight = Mathf.CeilToInt(scanAreaY / raycastSpacing);
     *
     *  // Creates variables to determine how far apart to space the raycasts on each axis
     *  float spacingX = scanAreaX / raycastArrayLength;
     *  float spacingY = scanAreaY / raycastArrayHeight;
     *
     *
     *
     #region Check that the gap between raycasts is not too small for the attack zone itself
     *  float distance = Vector3.Distance(origin, c.bounds.center);
     *  Vector3 l = Misc.AngledDirection(new Vector3(0, -angle, 0), forward, worldUp);
     *  Vector3 r = Misc.AngledDirection(new Vector3(0, angle, 0), forward, worldUp);
     *  l = origin + l.normalized * distance;
     *  r = origin + r.normalized * distance;
     *  // Obtains the actual size of the area that needs to be covered
     *  float maxDistanceToCover = Vector3.Distance(l, r);
     *
     *  Debug.Log("Spacing = " + raycastSpacing + ", adjusted = " + spacingX + ", " + spacingY + ", cover = " + maxDistanceToCover);
     *
     *
     *  float diagonalSpaceBetweenRaycasts = Vector2.Distance(Vector2.zero, new Vector2(spacingX, spacingY));
     *  if (diagonalSpaceBetweenRaycasts > maxDistanceToCover) // If the gap between spaces is bigger than the distance
     *  {
     *      // This means it's possible for none of the raycasts to land inside the actual hit zone, and are all excluded.
     *      // Reverse the code used to get maxGapBetweenRaycasts from spacingX and spacingY, replacing with maxDistanceToCover
     *      // This should produce an appropriate grid spacing small enough to actually cover the cone of fire.
     *      Vector2 correctedDiagonal = new Vector2(spacingX, spacingY).normalized * maxDistanceToCover;
     *      spacingX = correctedDiagonal.x;
     *      spacingY = correctedDiagonal.y;
     *  }
     *
     *  Debug.Log("Spacing = " + raycastSpacing + ", adjusted = " + spacingX + ", " + spacingY + ", cover = " + maxDistanceToCover);
     *
     *
     *  Vector3 u = Misc.AngledDirection(new Vector3(-angle, 0, 0), forward, worldUp);
     *  Vector3 d = Misc.AngledDirection(new Vector3(angle, 0, 0), forward, worldUp);
     *  u = origin + u.normalized * distance;
     *  d = origin + d.normalized * distance;
     *  Debug.DrawLine(origin, l, Colours.darkGreen, 10);
     *  Debug.DrawLine(origin, r, Colours.darkGreen, 10);
     *  Debug.DrawLine(origin, u, Colours.darkGreen, 10);
     *  Debug.DrawLine(origin, d, Colours.darkGreen, 10);
     *  Debug.DrawLine(l, c.bounds.center, Colours.darkGreen, 10);
     *  Debug.DrawLine(r, c.bounds.center, Colours.darkGreen, 10);
     *  Debug.DrawLine(u, c.bounds.center, Colours.darkGreen, 10);
     *  Debug.DrawLine(d, c.bounds.center, Colours.darkGreen, 10);
     *
     #endregion
     *
     *
     *
     *
     *
     *  // Creates axes along which to align the raycasts
     *  Vector3 raycastGridAxisX = (rightPoint - c.bounds.center).normalized;
     *  Vector3 raycastGridAxisY = (upPoint - c.bounds.center).normalized;
     #endregion
     *
     #region Perform raycast command batch processing
     *  // Creates a list of Vector3 directions
     *  List<Vector3> directions = new List<Vector3>();
     *
     *
     *  // Cast an array of rays to 'sweep' the square for line of sight.
     *  for (int y = 0; y < raycastArrayHeight; y++)
     *  {
     *      for (int x = 0; x < raycastArrayLength; x++)
     *      {
     *          // Creates coordinates along the sweep area for the raycast. 0,0 would be the centre, so for each axis I am taking away a value equivalent to half of that axis dimension, so I can use the bottom-left corner as 0,0
     *          float distanceX = spacingX * x - scanAreaX / 2;
     *          float distanceY = spacingY * y - scanAreaY / 2;
     *
     *          // Starts with c.bounds.centre, then adds direction values multiplied by the appropriate distance value for each axis, to create the point that you want the raycast to hit.
     *          Vector3 raycastAimPoint = c.bounds.center + (raycastGridAxisX * distanceX) + (raycastGridAxisY * distanceY);
     *
     *          // From that point, it creates a direction value for the raycast to aim in.
     *          Vector3 raycastAimDirection = raycastAimPoint - origin;
     *
     *          // Checks if the raycast is still detecting a point that is actually inside the cone
     *          if (Vector3.Angle(forward, raycastAimDirection) < angle)
     *          {
     *              Debug.DrawLine(origin, raycastAimPoint, Color.white, 10);
     *
     *              directions.Add(raycastAimDirection);
     *          }
     *          else
     *          {
     *              Debug.DrawLine(origin, raycastAimPoint, Colours.scarlet, 10);
     *          }
     *      }
     *  }
     *
     *  Debug.Log("Direction count = " + directions.Count);
     *
     *  // Create RaycastCommand
     *  var results = new NativeArray<RaycastHit>(directions.Count, Allocator.TempJob);
     *  var commands = new NativeArray<RaycastCommand>(directions.Count, Allocator.TempJob);
     *
     *  // Assign directions to each RaycastCommand
     *  for (int i = 0; i < commands.Length; i++)
     *  {
     *      commands[i] = new RaycastCommand(origin, directions[i], range, viewable);
     *  }
     *
     *  // Schedule the batch of raycasts, and wait for the batch processing job to complete
     *  JobHandle handle = RaycastCommand.ScheduleBatch(commands, results, 1);
     *  handle.Complete();
     *
     *  // Converts the NativeArray of results to a regular array
     *  RaycastHit[] hits = results.ToArray();
     *  Debug.Log("Results length = " + hits.Length);
     *
     *  // Dispose the buffers
     *  results.Dispose();
     *  commands.Dispose();
     #endregion
     *
     #region Evaluate results and determine true or false
     *
     *  // Look through each result in the list of raycast hits
     *  for (int i = 0; i < hits.Length; i++)
     *  {
     *      Debug.DrawLine(origin, hits[i].point, Colours.turquoise, 10);
     *      // If a RaycastHit is found that matches the collider being checked for, store it and return true
     *      if (hits[i].collider == c)
     *      {
     *          checkInfo = hits[i];
     *          return true;
     *      }
     *  }
     *
     *  // In the event that no results were found, tokenly assign a redundant RaycastHit so the function completes properly
     *  if (Physics.Raycast(origin, forward, out checkInfo, range, viewable))
     *  {
     *
     *  }
     *
     *  return false;
     #endregion
     * }
     */

    #region Deprecated but replacing them with the newer ones made things stop working and I'm not sure why

    public static bool LineOfSight(Vector3 origin, Transform target, LayerMask coverCriteria, float overlap = 0.01f)
    {
        // Launches a raycast between the cover position and the attacker
        RaycastHit lineOfSightCheck;

        if (Physics.Raycast(origin, target.position - origin, out lineOfSightCheck, Vector3.Distance(origin, target.position) + overlap, coverCriteria))
        {
            Transform t = lineOfSightCheck.collider.transform; // Gets transform of object


            DamageHitbox dh = t.GetComponent <DamageHitbox>(); // If object is a DamageHitbox, find the root object, which is the actual thing being tracked if it's an enemy
            if (dh != null)
            {
                if (dh.healthScript != null)
                {
                    if (dh.healthScript.transform == target)
                    {
                        return(true);
                    }
                }
            }

            if (t == target)
            {
                return(true);
            }
        }

        return(false);
    }
Exemplo n.º 3
0
    protected override void OnTriggerEnter2D(Collider2D collision)
    {
        Actor otherActor = collision.GetComponentInParent <Actor>();

        if (otherActor)
        {
            otherActor.TakeDamage(this, damageValue + damageScaling, Owner, thisDamageInfo);

            if (otherActor is DamageHitbox)
            {
                DamageHitbox hitBox = (DamageHitbox)otherActor;

                if (hitBox.mainActor is BossPawn)
                {
                    damageScaling += damageScaleAmount;

                    Vector2 vel = new Vector2(player.GetVelocity().x, 0f);

                    player.PawnRB_SetVelocity(vel);

                    player.MainStateMachine.ChangeMoveState <PlayerMState_Idle>();

                    player.MainStateMachine.CurrentMoveState.PlayerJump(10);
                }
            }
        }
    }
Exemplo n.º 4
0
    public static void PointDamage(GameObject origin, Faction originFaction, GameObject attackedObject, int damage, DamageType cause, bool isSevere)
    {
        DamageHitbox hitbox = attackedObject.GetComponent <DamageHitbox>(); // Checks collider gameObject for a damageHitbox script

        if (hitbox != null)
        {
            hitbox.Damage(damage, origin, originFaction, cause, isSevere);
        }
    }
Exemplo n.º 5
0
    public static Character FromHit(GameObject g)
    {
        DamageHitbox d = g.GetComponent <DamageHitbox>();

        if (d != null)
        {
            return(FromHitbox(d));
        }
        return(null);
    }
    public static Character FromObject(GameObject g)
    {
        DamageHitbox d = g.GetComponent <DamageHitbox>();

        if (d != null)
        {
            g = d.GetRootObject();
            return(g.GetComponent <Character>());
        }
        return(g.GetComponentInParent <Character>());
    }
    public void SingleAttack(Entity attacker, Vector3 position, Vector3 direction)
    {
        attackEffects.Invoke();

        bool          hitSuccessful      = false;
        Character     attackingCharacter = attacker as Character;
        List <Health> alreadyDamaged     = new List <Health>();

        Collider[] inRange = Physics.OverlapSphere(position, range, detection);
        for (int i = 0; i < inRange.Length; i++)
        {
            Vector3 closestPoint = Explosion.GetColliderPointFromCentre(inRange[i], position);

            bool inAngle = Vector3.Angle(direction, closestPoint - position) <= angle / 2;
            if (!inAngle)
            {
                continue;
            }

            Ray  checkRay    = new Ray(position, closestPoint - position);
            bool lineOfSight = Physics.Raycast(checkRay, out RaycastHit hit, range, detection) && hit.collider == inRange[i];
            if (!lineOfSight)
            {
                continue;
            }

            bool canAttack = attackingCharacter == null || attackingCharacter.CanDamage(hit.collider.GetComponent <Character>(), attackFriendlies, false);
            if (!canAttack)
            {
                continue;
            }


            hitSuccessful = true;

            DamageHitbox dh = hit.collider.GetComponent <DamageHitbox>();
            if (dh != null && !alreadyDamaged.Contains(dh.healthScript))
            {
                dh.Damage(damage, attacker, type);
                alreadyDamaged.Add(dh.healthScript);
            }

            Rigidbody rb = hit.collider.GetComponentInParent <Rigidbody>();
            if (rb != null)
            {
                rb.AddForceAtPosition(checkRay.direction * knockback, hit.point, ForceMode.Impulse);
            }
        }

        if (hitSuccessful == true)
        {
            hitEffects.Invoke();
        }
    }
Exemplo n.º 8
0
    public static void PointDamage(GameObject origin, Faction originFaction, GameObject attackedObject, int damage, float criticalMultiplier, DamageType normalCause, DamageType criticalCause)
    {
        DamageHitbox hitbox = attackedObject.GetComponent <DamageHitbox>(); // Checks collider gameObject for a damageHitbox script

        if (hitbox != null)
        {
            Debug.Log("Hit");

            hitbox.Damage(damage, criticalMultiplier, origin, originFaction, normalCause, criticalCause);
        }
    }
Exemplo n.º 9
0
    /*
     * // Start is called before the first frame update
     * void Start()
     * {
     *
     * }
     *
     * // Update is called once per frame
     * void Update()
     * {
     *
     * }
     */

    private void OnTriggerStay(Collider c)
    {
        //print("Dealing damage at " + Time.time);
        DamageHitbox dh = c.GetComponent <DamageHitbox>();

        if (dh != null)
        {
            //print("Hitbox detected");
            dh.Damage(Mathf.RoundToInt(damagePerSecond * Time.deltaTime), gameObject, null, damageType, isSevere);
        }
    }
Exemplo n.º 10
0
    public static void InstantExplosion(GameObject origin, Faction originFaction, Transform explosionOrigin, int damage, float knockback, float blastRadius, float explosionTime, AnimationCurve damageFalloff, AnimationCurve knockbackFalloff, LayerMask blastDetection, DamageType cause, bool isSevere)
    {
        List <Health> alreadyDamaged = new List <Health>();

        Collider[] affectedObjects = Physics.OverlapSphere(explosionOrigin.position, blastRadius, blastDetection);
        foreach (Collider c in affectedObjects)
        {
            Vector3    targetDirection = c.transform.position - explosionOrigin.position;
            RaycastHit isVulnerable;
            if (Physics.Raycast(explosionOrigin.position, targetDirection, out isVulnerable, blastRadius, blastDetection))
            {
                if (isVulnerable.collider == c)
                {
                    float i = isVulnerable.distance / blastRadius;

                    DamageHitbox hitbox = c.GetComponent <DamageHitbox>(); // Checks collider gameObject for a damageHitbox script
                    if (hitbox != null)
                    {
                        bool undamagedCheck = false;
                        foreach (Health h in alreadyDamaged)
                        {
                            if (h == hitbox.healthScript)
                            {
                                undamagedCheck = true;
                            }
                        }

                        if (undamagedCheck == false)
                        {
                            int d = Mathf.RoundToInt(damage * damageFalloff.Evaluate(i));
                            hitbox.Damage(d, origin, originFaction, cause, isSevere);
                            Debug.Log("Dealt " + d + " damage to " + hitbox.name + " at " + hitbox.transform.position + ".");
                            alreadyDamaged.Add(hitbox.healthScript);
                        }
                    }

                    Knockback(isVulnerable.collider.gameObject, knockback * knockbackFalloff.Evaluate(i), targetDirection);
                }
            }
        }
    }
Exemplo n.º 11
0
    public static void Knockback(GameObject attackedObject, float force, Vector3 direction)
    {
        if (force != 0) // Checks if knockback needs to be applied
        {
            Rigidbody    rb;
            DamageHitbox d = attackedObject.GetComponent <DamageHitbox>(); // Checks object hit for DamageHitbox script
            if (d != null)                                                 // If script is present, the object must be a DamageHitbox, so it checks its root object for a rigidbody component.
            {
                rb = d.GetRootObject().GetComponent <Rigidbody>();
            }
            else
            {
                rb = attackedObject.GetComponent <Rigidbody>(); // If object is not a hitbox, look for rigidbody script in object.
            }

            if (rb != null) // If a rigidbody is found, apply knockback force.
            {
                rb.AddForce(direction * force, ForceMode.Impulse);
            }
        }
    }
Exemplo n.º 12
0
        public IEnumerator HitboxTakesDamage()
        {
            // Instantiates 'attacker', adds Character script and Faction scriptableObject
            GameObject o = Object.Instantiate(GameObject.CreatePrimitive(PrimitiveType.Cube), new Vector3(0, 0, -3), Quaternion.identity);
            Character  c = o.AddComponent <Character>();

            c.faction = Resources.Load("Factions/Good Guys") as Faction;

            // Creates 'attacked' gameObject and adds health script
            GameObject g             = Object.Instantiate(GameObject.CreatePrimitive(PrimitiveType.Cube), Vector3.zero, Quaternion.identity);
            Health     h             = g.AddComponent <Health>();
            int        currentHealth = h.values.current; // Saves attacked gameObject's current health
            // Adds DamageHitbox script to object, and links it to health script
            DamageHitbox dh = g.AddComponent <DamageHitbox>();

            dh.healthScript = h;

            Debug.Log(h.values.current);
            Debug.Log(currentHealth);

            yield return(new WaitForEndOfFrame());

            Debug.Log("schlep");

            // Deals damage to attacked gameObject's hitbox
            dh.Damage(5, c, DamageType.Shot);

            Debug.Log("frank");

            yield return(new WaitForEndOfFrame());

            Debug.Log("hashbrown");
            Debug.Log(h.values.current);
            Debug.Log(currentHealth);

            // Checks if attacked object's health has changed
            Assert.IsTrue(h.values.current != currentHealth);
        }
    // Update is called once per frame
    void Update()
    {
        /*
         * if (prs.healthCurrent != healthPrev)
         * {
         *  //HealthMeterUpdate();
         * }
         * healthPrev = prs.healthCurrent;
         */


        #region Health HUD
        healthCounter.text = ph.ph.health.current.ToString();
        FillMeter(healthBar, ph.ph.health.current, ph.ph.health.max);
        if (ph.ph.health.IsCritical())
        {
            healthCounter.color = resourceCriticalColour;
            // Do other stuff for critical health e.g. greyscale screen, warnings
        }
        else
        {
            healthCounter.color = resourceNormalColour;
        }
        #endregion

        #region Basic reticle and interacting
        RaycastHit lookingAt;
        if (Physics.Raycast(ph.pc.head.transform.position, transform.forward, out lookingAt, lookRange, lookDetection))
        {
            Character    c = null;
            DamageHitbox d = lookingAt.collider.GetComponent <DamageHitbox>();
            if (d != null)
            {
                c = Character.FromHitbox(d);
                if (c != null)
                {
                    switch (ph.faction.Affiliation(c.faction))
                    {
                    case FactionState.Allied:
                        ColourReticle(allyColour);
                        break;

                    case FactionState.Hostile:
                        ColourReticle(enemyColour);
                        break;

                    default:
                        ColourReticle(reticleDefaultColour);
                        break;
                    }
                }
                else
                {
                    ColourReticle(reticleDefaultColour);
                }
            }
            else
            {
                ColourReticle(reticleDefaultColour);
            }

            // Do interacting stuff
        }
        else
        {
            ColourReticle(reticleDefaultColour);
        }
        #endregion

        #region Weapon HUD
        RangedWeapon rw           = ph.wh.CurrentWeapon();
        bool         activeWeapon = !ph.wh.IsSwitchingWeapon;
        reticleUp.gameObject.SetActive(activeWeapon);
        reticleDown.gameObject.SetActive(activeWeapon);
        reticleLeft.gameObject.SetActive(activeWeapon);
        reticleRight.gameObject.SetActive(activeWeapon);
        ammoDisplay.gameObject.SetActive(activeWeapon);
        opticsOverlay.gameObject.SetActive(activeWeapon);
        opticsTransition.gameObject.SetActive(activeWeapon);

        if (activeWeapon == true)
        {
            if (rw.optics == null)
            {
                ADSTransition(0, null);
            }

            float accuracy = ph.wh.accuracyModifier.NewFloat(ph.wh.standingAccuracy);
            float rp       = (accuracy + rw.accuracy.projectileSpread) * Screen.height / ph.pc.fieldOfView;
            reticleUp.rectTransform.anchoredPosition    = Vector3.up * rp;
            reticleDown.rectTransform.anchoredPosition  = Vector3.down * rp;
            reticleLeft.rectTransform.anchoredPosition  = Vector3.left * rp;
            reticleRight.rectTransform.anchoredPosition = Vector3.right * rp;

            firingMode.text = rw.firingModes[rw.firingModeIndex].name;

            ammoCounter.text = AmmoInfo(rw, rw.firingModeIndex);

            AmmunitionStats a = rw.GetAmmunitionStats(rw.firingModeIndex);
            MagazineStats   m = rw.GetMagazineStats(rw.firingModeIndex);

            if (a == null)
            {
                if (m == null)
                {
                    FillMeter(ammoBar, 1, 1);
                }
                else
                {
                    FillMeter(ammoBar, m.magazine.current, m.magazine.max);
                }
            }
            else
            {
                if (m == null)
                {
                    FillMeter(ammoBar, ph.a.GetStock(a.ammoType), ph.a.GetMax(a.ammoType));
                }
                else
                {
                    FillMeter(ammoBar, m.magazine.current, m.magazine.max);
                }
            }
        }
        #endregion
    }
Exemplo n.º 14
0
    public static Character FromHitbox(DamageHitbox d)
    {
        GameObject g = d.GetRootObject();

        return(g.GetComponent <Character>());
    }
Exemplo n.º 15
0
 // Gets the centre of a particular collider for the enemy to aim at.
 static Vector3 TargetEnemySpecificCollider(DamageHitbox dh)
 {
     return(dh.Collider.bounds.center);
 }
Exemplo n.º 16
0
    public void Detonate(Vector3 centre, Entity origin)
    {
        onExplosionStart.Invoke();

        Character     attacker       = origin as Character;
        List <Health> alreadyDamaged = new List <Health>();

        //RaycastHit[] affected = Physics.SphereCastAll(position, blastRadius, Vector3.forward, 0, hitDetection);
        Collider[] affected = Physics.OverlapSphere(centre, blastRadius, hitDetection);
        for (int i = 0; i < affected.Length; i++)
        {
            // Calculates the closest point and raycast towards it to check that it is not behind cover
            Vector3 closestPoint = GetColliderPointFromCentre(affected[i], centre);
            Ray     checkRay     = new Ray(centre, closestPoint - centre);
            bool    lineOfSight  = Physics.Raycast(checkRay, out RaycastHit hit, blastRadius, hitDetection) && hit.collider == affected[i];
            if (!lineOfSight)
            {
                continue;
            }


            // If raycast hits, collider is in the path of the explosion. Check if the object can be attacked by the user
            bool canAttack = attacker == null || attacker.CanDamage(hit.collider.GetComponent <Character>(), friendlyFire, selfDamage);
            if (!canAttack)
            {
                continue;
            }


            // If check is positive, attacker is not restricted from attacking the target (no faction or faction is hostile)
            float distanceZeroToOne = hit.distance / blastRadius;
            // If the collider is attached to an object that wasn't already damaged by the explosion
            DamageHitbox dh = hit.collider.GetComponent <DamageHitbox>();
            if (dh != null && !alreadyDamaged.Contains(dh.healthScript))
            {
                float multipliedDamage = damage * damageFalloff.Evaluate(distanceZeroToOne);
                Debug.Log(multipliedDamage);
                dh.Damage(Mathf.RoundToInt(multipliedDamage), origin, type);
                alreadyDamaged.Add(dh.healthScript);
            }
            // If the collider is affected by physics, knock it around
            Rigidbody rb = hit.collider.GetComponentInParent <Rigidbody>();
            if (rb != null)
            {
                float multipliedKnockback = knockback * knockbackFalloff.Evaluate(distanceZeroToOne);
                rb.AddForceAtPosition(checkRay.direction * multipliedKnockback, hit.point, ForceMode.Impulse);
            }
        }



        /*
         * List<Health> alreadyDamaged = new List<Health>();
         * List<Rigidbody> alreadyKnockedBack = new List<Rigidbody>();
         * Character attacker = origin as Character;
         *
         * Collider[] affectedObjects = Physics.OverlapSphere(position, blastRadius, hitDetection);
         * foreach (Collider c in affectedObjects)
         * {
         *  Ray check = new Ray(position, c.transform.position - position);
         *  // If a check is successful between the collider and the blast origin
         *  if (Physics.Raycast(check, out RaycastHit hit, blastRadius, hitDetection) && hit.collider == c)
         *  {
         *      // If the collider can be attacked
         *      if (Character.CanDamage(attacker, Character.FromObject(hit.collider.gameObject), friendlyFire, selfDamage))
         *      {
         *          float distanceFromOrigin = blastRadius / hit.distance;
         *
         *          // Check if entity can be damaged, and if it hasn't already
         *          DamageHitbox dh = hit.collider.GetComponent<DamageHitbox>();
         *          if (dh != null && !alreadyDamaged.Contains(dh.healthScript))
         *          {
         *              int damageToDeal = Mathf.RoundToInt(damage * damageFalloff.Evaluate(distanceFromOrigin));
         *              dh.Damage(damageToDeal, origin, type);
         *              alreadyDamaged.Add(dh.healthScript);
         *          }
         *
         *          // Check if entity can be knocked back, and if it hasn't already
         *          Rigidbody rb = hit.collider.GetComponentInParent<Rigidbody>();
         *          if (rb != null && !alreadyKnockedBack.Contains(rb))
         *          {
         *              float knockbackToInflict = knockback * knockbackFalloff.Evaluate(distanceFromOrigin);
         *              rb.AddForceAtPosition(check.direction * knockbackToInflict, hit.point, ForceMode.Impulse);
         *          }
         *      }
         *
         *
         *
         *
         *
         *
         *
         *
         *
         *
         *  }
         * }
         */

        onExplosionEnd.Invoke();
    }