Exemple #1
0
    //use expectedDamage() to initiate the chainAttack object and send warnings to everything
    public override void expectedDamage(ref DamageEventData d)
    {
        //spawn a chainAttack object.  We borrow a prefab reference from the level manager since we cant have one of our own
        attackObject = ((GameObject)GameObject.Instantiate(LevelManagerScript.instance.chainAttackPrefab)).GetComponent <ChainAttackScript>();

        //set the attack color, if there is one
        if (d.effects.propertyEffects.attackColor != null)
        {
            attackObject.lineRenderer.startColor = d.effects.propertyEffects.attackColor.Value;
            attackObject.lineRenderer.endColor   = d.effects.propertyEffects.attackColor.Value / 2;
        }

        //create a DamageEventData for the attack object to base its attacks on
        DamageEventData baseEvent = new DamageEventData();

        baseEvent.dest      = null;
        baseEvent.rawDamage = d.rawDamage;
        baseEvent.source    = d.source;

        //the chained attack contains all effects except for this one
        baseEvent.effects = d.effects.clone();
        baseEvent.effects.removeEffect("chainHit");

        //have it spread warnings around to everything that will get hit
        attackObject.ChainAttackWarn(baseEvent, d.dest, strength, targetCap);
    }
        private void OnHit(AttackEventData data, DeferredMessageContext messageContext)
        {
            var damageData = new DamageEventData {
                Damage = data.Damage.Value, DamagedBy = data.Attacker
            };

            var damaged = _systemContainer.EventSystem.Try(EventType.Damage, data.Defender, damageData);

            if (damaged)
            {
                DescribeSuccessfulAttack(data, messageContext, damageData.Damage);
            }
            else
            {
                if (string.IsNullOrEmpty(data.SuccessfulDefenceType))
                {
                    if (damageData.Absorbed)
                    {
                        data.SuccessfulDefenceType = "Absorbed";
                    }
                    else
                    {
                        data.SuccessfulDefenceType = "NoDamage";
                    }
                }

                DescribeMissedAttack(data, messageContext);
            }
        }
Exemple #3
0
    }                                                                               //default constructor inits internal variables to 0

    public override void UpdateEnemy(EnemyScript e, float deltaTime)
    {
        //do nothing if the effect time is already over
        if (curPoisonTime > maxPoisonTime)
        {
            return;
        }

        //update timer
        curPoisonTime += Time.deltaTime;

        //calculate damage
        float damage        = (strength * Time.deltaTime) + carryOver;
        int   roundedDamage = Mathf.FloorToInt(damage);

        carryOver = damage - roundedDamage;

        //return early if zero damage
        if (roundedDamage == 0)
        {
            return;
        }

        //construct event
        DamageEventData damageEvent = new DamageEventData();

        damageEvent.source    = effectSource;
        damageEvent.dest      = e;
        damageEvent.rawDamage = roundedDamage;
        damageEvent.effects   = null;

        //deal damage
        e.onExpectedDamage(ref damageEvent);
        e.onDamage(damageEvent, false); //hide particles from poison damage
    }
Exemple #4
0
        // Get Damage Event Data (to use only on Damage Event Script)
        public static DamageEventData GetDamageEventData()
        {
            Internal.NativeFunctions.nwnxSetFunction(PLUGIN_NAME, "GetDamageEventData");
            Internal.NativeFunctions.nwnxCallFunction();

            var data = new DamageEventData
            {
                Damager     = Internal.NativeFunctions.nwnxPopObject(),
                Bludgeoning = Internal.NativeFunctions.nwnxPopInt(),
                Pierce      = Internal.NativeFunctions.nwnxPopInt(),
                Slash       = Internal.NativeFunctions.nwnxPopInt(),
                Magical     = Internal.NativeFunctions.nwnxPopInt(),
                Acid        = Internal.NativeFunctions.nwnxPopInt(),
                Cold        = Internal.NativeFunctions.nwnxPopInt(),
                Divine      = Internal.NativeFunctions.nwnxPopInt(),
                Electrical  = Internal.NativeFunctions.nwnxPopInt(),
                Fire        = Internal.NativeFunctions.nwnxPopInt(),
                Negative    = Internal.NativeFunctions.nwnxPopInt(),
                Positive    = Internal.NativeFunctions.nwnxPopInt(),
                Sonic       = Internal.NativeFunctions.nwnxPopInt(),
                Base        = Internal.NativeFunctions.nwnxPopInt()
            };

            return(data);
        }
Exemple #5
0
        private static void HandleWeaponStatBonuses()
        {
            DamageEventData data = NWNXDamage.GetDamageEventData();

            if (data.Total <= 0)
            {
                return;
            }

            NWPlayer player = data.Damager.Object;
            NWItem   weapon = _.GetLastWeaponUsed(player);

            if (weapon.CustomItemType == CustomItemType.BlasterPistol ||
                weapon.CustomItemType == CustomItemType.BlasterRifle)
            {
                int statBonus = (int)(player.DexterityModifier * 0.5f);
                data.Base += statBonus;
            }
            else if (weapon.CustomItemType == CustomItemType.Lightsaber ||
                     weapon.CustomItemType == CustomItemType.Saberstaff ||
                     weapon.GetLocalInt("LIGHTSABER") == TRUE)
            {
                int statBonus = (int)(player.CharismaModifier * 0.25f);
                data.Base += statBonus;
            }

            NWNXDamage.SetDamageEventData(data);
        }
 //we dont need to do anything on expected damage
 public override void expectedDamage(ref DamageEventData d)
 {
     if (innerEffect.triggersAs(EffectType.enemyDamaged))
     {
         base.expectedDamage(ref d);
     }
 }                                                                                                                                               //pass to child if it is also an enemyDamaged effect
 public virtual void trigger(ref DamageEventData d, int pointsOfOvercharge)
 {
     if (shouldApplyInnerEffect())
     {
         ((IEffectOvercharge)innerEffect).trigger(ref d, pointsOfOvercharge);
     }
 }
Exemple #8
0
        private static void HandleTranquilizerEffect()
        {
            DamageEventData data = NWNXDamage.GetDamageEventData();

            if (data.Total <= 0)
            {
                return;
            }
            NWObject self = NWGameObject.OBJECT_SELF;

            // Ignore the first damage because it occurred during the application of the effect.
            if (self.GetLocalInt("TRANQUILIZER_EFFECT_FIRST_RUN") > 0)
            {
                self.DeleteLocalInt("TRANQUILIZER_EFFECT_FIRST_RUN");
                return;
            }

            for (Effect effect = _.GetFirstEffect(self.Object); _.GetIsEffectValid(effect) == TRUE; effect = _.GetNextEffect(self.Object))
            {
                if (_.GetEffectTag(effect) == "TRANQUILIZER_EFFECT")
                {
                    _.RemoveEffect(self, effect);
                }
            }
        }
Exemple #9
0
        private static void HandleStances()
        {
            DamageEventData data          = NWNXDamage.GetDamageEventData();
            NWPlayer        damager       = data.Damager.Object;
            NWItem          damagerWeapon = _.GetLastWeaponUsed(damager);

            if (damager.IsPlayer)
            {
                CustomEffectType stance = CustomEffectService.GetCurrentStanceType(damager);

                switch (stance)
                {
                case CustomEffectType.ShieldOath:
                    data.AdjustAllByPercent(-0.30f);
                    break;

                case CustomEffectType.PrecisionTargeting:

                    if (damagerWeapon.CustomItemType == CustomItemType.BlasterPistol ||
                        damagerWeapon.CustomItemType == CustomItemType.BlasterRifle)
                    {
                        data.AdjustAllByPercent(0.20f);
                    }
                    break;
                }
            }

            NWNXDamage.SetDamageEventData(data);
        }
Exemple #10
0
        private static void HandleRecoveryBlast()
        {
            DamageEventData data     = NWNXDamage.GetDamageEventData();
            NWObject        damager  = data.Damager;
            bool            isActive = damager.GetLocalInt("RECOVERY_BLAST_ACTIVE") == TRUE;

            damager.DeleteLocalInt("RECOVERY_BLAST_ACTIVE");
            NWItem weapon = _.GetLastWeaponUsed(damager.Object);

            if (!isActive || weapon.CustomItemType != CustomItemType.BlasterRifle)
            {
                return;
            }

            data.Bludgeoning = 0;
            data.Pierce      = 0;
            data.Slash       = 0;
            data.Magical     = 0;
            data.Acid        = 0;
            data.Cold        = 0;
            data.Divine      = 0;
            data.Electrical  = 0;
            data.Fire        = 0;
            data.Negative    = 0;
            data.Positive    = 0;
            data.Sonic       = 0;
            data.Base        = 0;

            NWNXDamage.SetDamageEventData(data);
        }
Exemple #11
0
        private static void OnModuleApplyDamage()
        {
            DamageEventData data = NWNXDamage.GetDamageEventData();

            NWPlayer   player = data.Damager.Object;
            NWCreature target = NWGameObject.OBJECT_SELF;

            int attackType = target.GetLocalInt(AbilityService.LAST_ATTACK + player.GlobalID);

            LoggingService.Trace(TraceComponent.LastAttack, "Last attack from " + player.GlobalID + " on " + _.GetName(target) + " was type " + attackType);

            if (attackType == AbilityService.ATTACK_PHYSICAL)
            {
                // Only apply bonus damage from physical attacks.
                HandleWeaponStatBonuses();
                HandleEvadeOrDeflectBlasterFire();
                HandleApplySneakAttackDamage();
            }

            HandleDamageImmunity();
            HandleAbsorptionFieldEffect();
            HandleRecoveryBlast();
            HandleTranquilizerEffect();
            HandleStances();
        }
Exemple #12
0
    //recalculate speed
    public override void actualDamage(ref DamageEventData d)
    {
        EnemyScript e           = d.dest;                                                   //enemy reference
        float       healthRatio = (float)e.curHealth / (float)e.maxHealth;                  //how much health the unit still has (0: dead.  1: full health)

        e.unitSpeed = Mathf.CeilToInt(e.unitSpeedWhenSpawned * (healthRatio / strength));   //scale
        e.unitSpeed = Mathf.Min(e.unitSpeed, 1.0f);                                         //enforce minimum
    }
Exemple #13
0
        protected override void PrepareEvent(NwObject objSelf)
        {
            DamageEventData eventData = DamagePlugin.GetDamageEventData();

            DamageData = DamageData.FromNative(eventData);
            Attacker   = eventData.oDamager.ToNwObject <NwGameObject>();
            Target     = (NwGameObject)objSelf;
        }
Exemple #14
0
 public virtual void expectedDamage(ref DamageEventData d)
 {
     if (shouldApplyInnerEffect())
     {
         enemyDamageEFfectTriggers++;
         ((IEffectEnemyDamaged)innerEffect).expectedDamage(ref d);
     }
 }
Exemple #15
0
 public virtual void actualDamage(ref DamageEventData d)
 {
     if (enemyDamageEFfectTriggers > 0)
     {
         enemyDamageEFfectTriggers--;
         ((IEffectEnemyDamaged)innerEffect).actualDamage(ref d);
     }
 }
Exemple #16
0
    protected virtual void Attack(float _damage)
    {
        print("Attacking... and doing " + _damage + " points of damage");
        damageEventData = new DamageEventData(EventSystem.current);
        damageEventData.Initialize(_damage);

        player.OnDamage(damageEventData);
    }
Exemple #17
0
    //recalculate speed
    public override void actualDamage(ref DamageEventData d)
    {
        EnemyScript e           = d.dest;
        float       scaleRatio  = 1 - ((float)e.curHealth / (float)e.maxHealth); //ratio we are scaling by
        float       scaleFactor = ((scaleRatio - 1) * strength) + 1;             //factor to use for scaling

        e.unitSpeed = Mathf.RoundToInt(scaleFactor * e.unitSpeedWhenSpawned);    //scale
    }
Exemple #18
0
    }                                                                                                                                               //pass to child if it is also an enemyDamaged effect

    //recalculate effect strength when attacks hit
    public override void actualDamage(ref DamageEventData d)
    {
        if (effectBaseStrength == null)
        {
            effectBaseStrength = innerEffect.strength;
        }

        scaleInnerEffect();
    }
    protected void Shoot()
    {
        fireAudio.Play(SoundsPlayer.Selection.Randomly, audioSource, 1f);

        if (muzzleFlash)
        {
            muzzleFlash.Play(true);
        }


        float      spread = spreadAim;
        RaycastHit hitInfo;

        var firePos = new Vector3(user.transform.position.x,
                                  user.transform.position.y + user.GetComponent <CapsuleCollider>().height / 2,
                                  user.transform.position.z);


        Ray     ray          = new Ray(firePos, transform.forward);
        Vector3 spreadVector = user.transform.TransformVector(new Vector3(Random.Range(-spread, spread), Random.Range(-spread, spread), 0f));

        ray.direction = Quaternion.Euler(spreadVector) * ray.direction;

        if (debugVisual)
        {
            Debug.DrawLine(firePos, firePos + user.transform.forward * distanceMax, Color.red, 1100);
        }

        if (Physics.Raycast(ray, out hitInfo, distanceMax, damageMask, QueryTriggerInteraction.Ignore))
        {
            if (hitInfo.collider.gameObject == user)
            {
                return;
            }

            float impulse = hitImpact.GetImpulseAtDistance(hitInfo.distance, distanceMax);
            float damage  = hitImpact.GetDamageAtDistance(hitInfo.distance, distanceMax);
            SpawnHitEffect(hitInfo);
            SpawnHitSound(hitInfo);
            var damageable = hitInfo.collider.GetComponent <IDamageable>();
            if (damageable != null)
            {
                var damageData = new DamageEventData(-damage, user, hitInfo.point, ray.direction, impulse);
                damageable.TakeDamage(damageData);
            }
            else if (hitInfo.rigidbody)
            {
                hitInfo.rigidbody.AddForceAtPosition(ray.direction * impulse, hitInfo.point, ForceMode.Impulse);
            }
        }

        if (tracer)
        {
            var temp = Instantiate(tracer, transform.position, Quaternion.LookRotation(ray.direction));
            Destroy(temp, 1);
        }
    }
Exemple #20
0
        private static void HandleApplySneakAttackDamage()
        {
            DamageEventData data = NWNXDamage.GetDamageEventData();

            if (data.Total <= 0)
            {
                return;
            }
            NWObject damager         = data.Damager;
            int      sneakAttackType = damager.GetLocalInt("SNEAK_ATTACK_ACTIVE");

            if (damager.IsPlayer && sneakAttackType > 0)
            {
                NWPlayer   player    = damager.Object;
                NWCreature target    = _.OBJECT_SELF;
                var        pcPerk    = PerkService.GetPCPerkByID(damager.GlobalID, (int)PerkType.SneakAttack);
                int        perkRank  = pcPerk?.PerkLevel ?? 0;
                int        perkBonus = 1;

                // Rank 4 increases damage bonus by 2x (total: 3x)
                if (perkRank == 4)
                {
                    perkBonus = 2;
                }

                float perkRate;
                if (sneakAttackType == 1) // Player is behind target.
                {
                    perkRate = 1.0f * perkBonus;
                }
                else // Player is anywhere else.
                {
                    perkRate = 0.5f * perkBonus;
                }

                var   effectiveStats = PlayerStatService.GetPlayerItemEffectiveStats(player);
                float damageRate     = 1.0f + perkRate + effectiveStats.SneakAttack * 0.05f;
                data.Base = (int)(data.Base * damageRate);

                if (target.IsNPC)
                {
                    EnmityService.AdjustEnmity(target, player, 5 * data.Base);
                }

                NWNXDamage.SetDamageEventData(data);
            }

            damager.DeleteLocalInt("SNEAK_ATTACK_ACTIVE");
        }
Exemple #21
0
    /// <summary>
    /// spawns a burstShot to attack all enemies in range
    /// </summary>
    /// <param name="targets">list of enemies in range.  The burstShot itself will catch enemies that enter the region during the attack</param>
    /// <param name="damageEvent">details of the attack.  damageEvent.dest is ignored.</param>
    private void burstFire(IEnumerable <EnemyScript> targets, DamageEventData damageEvent)
    {
        //construct burst shot event
        BurstShotData data = new BurstShotData();

        data.targetList  = targets;
        data.burstRange  = range;
        data.damageEvent = damageEvent;

        //create a burst shot and give it the data
        GameObject shot = Instantiate(burstShotPrefab); //create it

        shot.transform.position = transform.position;   //center it on the tower
        shot.SendMessage("SetData", data);              //initialize it
    }
Exemple #22
0
    /// <summary>
    /// spawns a directionalShot to attack all enemies in a straight line
    /// </summary>
    /// <param name="targets">list of enemies in range.  The DirectionalShot itself will find other enemies on its own as it goes by</param>
    /// <param name="damageEvent">details of the attack.  damageEvent.dest is ignored.</param>
    /// <param name="attackDir">direction the attack should travel</param>
    private void directionalShot(List <EnemyScript> targets, DamageEventData damageEvent, Vector2 attackDir)
    {
        //construct event
        DirectionalShotData data = new DirectionalShotData();

        data.attackDir   = attackDir;
        data.damageEvent = damageEvent;
        data.targetList  = targets;

        //spawn it
        GameObject shot = Instantiate(directionalShotPrefab);

        shot.transform.position = transform.position;   //center it on the tower
        shot.SendMessage("SetData", data);              //initialize it
    }
Exemple #23
0
    }                                                                 //name used to refer to this effect in XML

    //alter damage calculations when we expect to deal damage, not when it actually happens, so that targeting etc. have an accurate number to work with
    public override void expectedDamage(ref DamageEventData d)
    {
        //skip if the attack ignores armor
        if (d.effects != null)
        {
            if (d.effects.propertyEffects.armorPierce)
            {
                return;
            }
        }

        if (d.rawDamage <= 1)
        {
            return;                                  //dont bother applying armor if the incoming attack is already at or below the min
        }
        d.rawDamage -= strength;                     //reduce damage by armor value
        d.rawDamage  = Mathf.Max(d.rawDamage, 1.0f); //but must deal at least one
    }
Exemple #24
0
    /// <summary>
    /// spawns a bullet to attack an enemy unit
    /// </summary>
    /// <param name="enemy">the enemy to attack</param>
    /// <param name="damageEvent">details of the attack the new bullet is meant to perform</param>
    private void spawnBullet(EnemyScript enemy, DamageEventData damageEvent)
    {
        //tell the event who our target is
        damageEvent.dest = enemy;

        GameObject bullet = (GameObject)Instantiate(bulletPrefab, transform.position, Quaternion.identity);

        bullet.SendMessage("InitBullet", damageEvent);

        //apply attackColor property, if it is present
        if (effects != null)
        {
            if (effects.propertyEffects.attackColor != null)
            {
                bullet.SendMessage("SetColor", effects.propertyEffects.attackColor);
            }
        }
    }
 // Set Damage Event Data (to use only on Damage Event Script)
 public static void SetDamageEventData(DamageEventData data)
 {
     Internal.NativeFunctions.nwnxSetFunction(PLUGIN_NAME, "SetDamageEventData");
     Internal.NativeFunctions.nwnxPushInt(data.Base);
     Internal.NativeFunctions.nwnxPushInt(data.Sonic);
     Internal.NativeFunctions.nwnxPushInt(data.Positive);
     Internal.NativeFunctions.nwnxPushInt(data.Negative);
     Internal.NativeFunctions.nwnxPushInt(data.Fire);
     Internal.NativeFunctions.nwnxPushInt(data.Electrical);
     Internal.NativeFunctions.nwnxPushInt(data.Divine);
     Internal.NativeFunctions.nwnxPushInt(data.Cold);
     Internal.NativeFunctions.nwnxPushInt(data.Acid);
     Internal.NativeFunctions.nwnxPushInt(data.Magical);
     Internal.NativeFunctions.nwnxPushInt(data.Slash);
     Internal.NativeFunctions.nwnxPushInt(data.Pierce);
     Internal.NativeFunctions.nwnxPushInt(data.Bludgeoning);
     Internal.NativeFunctions.nwnxCallFunction();
 }
Exemple #26
0
    public virtual void OnHit(HitBox hitBox, Collider other)
    {
        if (canApplyDamage &&
            !hitObjctCache[hitBox].Contains(other.gameObject) &&
            (user != null && other.gameObject != user.gameObject))
        {
            hitObjctCache[hitBox].Add(other.gameObject);

            SpawnHitEffect(other);
            SpawnHitSound(other);

            var damageable = other.GetComponent <IDamageable>();
            if (damageable != null)
            {
                var damageData = new DamageEventData(-hitImpact.GetDamage(), user);
                damageable.TakeDamage(damageData);
            }
        }
    }
Exemple #27
0
 /// Set Damage Event Data
 /// <param name="data">A NWNX_Damage_DamageEventData struct.</param>
 /// @note To use only in the Damage Event Script.
 public static void SetDamageEventData(DamageEventData data)
 {
     VM.NWNX.SetFunction(NWNX_Damage, "SetDamageEventData");
     VM.NWNX.StackPush(data.oDamager);
     VM.NWNX.StackPush(data.iBludgeoning);
     VM.NWNX.StackPush(data.iPierce);
     VM.NWNX.StackPush(data.iSlash);
     VM.NWNX.StackPush(data.iMagical);
     VM.NWNX.StackPush(data.iAcid);
     VM.NWNX.StackPush(data.iCold);
     VM.NWNX.StackPush(data.iDivine);
     VM.NWNX.StackPush(data.iElectrical);
     VM.NWNX.StackPush(data.iFire);
     VM.NWNX.StackPush(data.iNegative);
     VM.NWNX.StackPush(data.iPositive);
     VM.NWNX.StackPush(data.iSonic);
     VM.NWNX.StackPush(data.iBase);
     VM.NWNX.Call();
 }
    //initializes the attack
    public void SetData(DirectionalShotData data)
    {
        attackDir          = data.attackDir;
        transform.rotation = Quaternion.FromToRotation(Vector2.up, attackDir);
        baseDamageEvent    = data.damageEvent;

        //put the initial target list on the expected list and inform those enemies
        foreach (EnemyScript t in data.targetList)
        {
            //build event
            DamageEventData ded = new DamageEventData();
            ded.source    = baseDamageEvent.source;
            ded.rawDamage = baseDamageEvent.rawDamage;
            ded.effects   = baseDamageEvent.effects;
            ded.dest      = t;

            //trigger effects
            if (ded.effects != null)
            {
                foreach (IEffect ie in ded.effects.effects)
                {
                    if (ie.triggersAs(EffectType.enemyDamaged))
                    {
                        ((IEffectEnemyDamaged)ie).expectedDamage(ref ded);
                    }
                }
            }

            //warn enemy
            t.onExpectedDamage(ref ded);
            expectedToHit.Add(ded);
        }

        //play sound
        int soundToPlay = Random.Range(0, attackSounds.Length);

        audioSource.clip   = attackSounds[soundToPlay];
        audioSource.volume = MessageHandlerScript.instance.SFXVolumeSetting;
        audioSource.Play();

        initialized = true; //flag ready
    }
        /// <summary>
        /// Set Damage Event Data (to use only on Damage Event Script)
        /// </summary>
        /// <param name="data"></param>
        public static void SetDamageEventData(DamageEventData data)
        {
            string sFunc = "SetDamageEventData";

            NWNXCore.NWNX_PushArgumentInt(NWNX_Damage, sFunc, data.Base);
            NWNXCore.NWNX_PushArgumentInt(NWNX_Damage, sFunc, data.Sonic);
            NWNXCore.NWNX_PushArgumentInt(NWNX_Damage, sFunc, data.Positive);
            NWNXCore.NWNX_PushArgumentInt(NWNX_Damage, sFunc, data.Negative);
            NWNXCore.NWNX_PushArgumentInt(NWNX_Damage, sFunc, data.Fire);
            NWNXCore.NWNX_PushArgumentInt(NWNX_Damage, sFunc, data.Electrical);
            NWNXCore.NWNX_PushArgumentInt(NWNX_Damage, sFunc, data.Divine);
            NWNXCore.NWNX_PushArgumentInt(NWNX_Damage, sFunc, data.Cold);
            NWNXCore.NWNX_PushArgumentInt(NWNX_Damage, sFunc, data.Acid);
            NWNXCore.NWNX_PushArgumentInt(NWNX_Damage, sFunc, data.Magical);
            NWNXCore.NWNX_PushArgumentInt(NWNX_Damage, sFunc, data.Slash);
            NWNXCore.NWNX_PushArgumentInt(NWNX_Damage, sFunc, data.Pierce);
            NWNXCore.NWNX_PushArgumentInt(NWNX_Damage, sFunc, data.Bludgeoning);

            NWNXCore.NWNX_CallFunction(NWNX_Damage, sFunc);
        }
Exemple #30
0
    }                                                                                                                                               //pass to child if it is also an enemyDamaged effect

    //recalculate effect strength
    public override void actualDamage(ref DamageEventData d)
    {
        EnemyScript e = d.dest;

        //on first hit, cache references
        if (effectBaseStrength == null)
        {
            effectBaseStrength = innerEffect.strength;
        }

        float healthRatio = (float)e.curHealth / (float)e.maxHealth;                                 //how much health the unit still has (0: dead.  1: full health)

        innerEffect.strength = Mathf.CeilToInt(effectBaseStrength.Value * (healthRatio / strength)); //scale
        innerEffect.strength = Mathf.Max(innerEffect.strength, 1.0f);                                //enforce minimum

        if (innerEffect.triggersAs(EffectType.enemyDamaged))
        {
            base.expectedDamage(ref d);                                                  //pass to child if it is also an enemyDamaged effect
        }
    }