public void TakeDamage(BodyPartDamage damage)
    {
        // Select a limb to take damage, weighing the draw by total volume.
        // Start by getting an ordered list of the total volume of each limb.
        float[] limb_volumes = (from limb in Limbs
                                select(from layer in limb.Layers
                                       select(from part in layer
                                              select part.Volume).Sum()).Sum()).ToArray();
        Limb target = (Limb)Util.WeightedRandomDraw(Limbs, limb_volumes);

        // Apply DamageLimb and update that limb
        Limb damaged_tgt = DamageLimb(damage: damage,
                                      limb: target);
        int tgt_index = Array.IndexOf(Limbs, target);

        Limbs[tgt_index] = damaged_tgt;

        // If that limb's innermost layer is totally destroyed, destroy it and any limbs linked through it.
        // DEVNOTE: This approach REQUIRES that limbs be uniquely named.
        BodyPart[] tgt_innermost_layer          = damaged_tgt.Layers[0];
        bool       target_inner_layer_destroyed = (from part in tgt_innermost_layer select part.Integrity).Sum() == 0;

        if (target_inner_layer_destroyed)
        {
            DestroyLimb(damaged_tgt.Name);
        }

        // TODO: See if this entity should die as a result of the damage.

        // TODO: If necessary, call whatever method handles death.
    }
    /// <summary>
    /// Returns a BodyPart instance with damage applied to it. Draws a floor at 0 integrity.
    /// </summary>
    /// <param name="damage">A struct with values of impact, shear, corrosive, and energy damage.</param>
    /// <param name="part">A BodyPart to be transmuted and returned</param>
    /// <returns>A transmuted BodyPart with the given damage applied.</returns>
    public BodyPart DamagePart(BodyPartDamage damage, BodyPart part)
    {
        // Since part is a struct, it'll be passed to this method by value instead of reference. Thus, we can mutate it freely and return it.
        // A resistance of 1 will negate damage, and a resistance of -1 will double it.
        part.Integrity = Math.Max(0f, part.Integrity - damage.Impact * (1 - part.ImpactResist));
        part.Integrity = Math.Max(0f, part.Integrity - damage.Shear * (1 - part.ShearResist));
        part.Integrity = Math.Max(0f, part.Integrity - damage.Corrosive * (1 - part.CorrosiveResist));
        part.Integrity = Math.Max(0f, part.Integrity - damage.Energy * (1 - part.EnergyResist));

        return(part);
    }
    /// <summary>
    /// Returns a limb with the specified damage applied to it, including layer penetration/spillover.
    /// </summary>
    /// <param name="damage"></param>
    /// <param name="limb"></param>
    /// <returns></returns>
    public Limb DamageLimb(BodyPartDamage damage, Limb limb)
    {
        // Pick a hypothetical full-penetration path, rolling weighted draws based on volume at each layer
        BodyPart[] targets = (from layer in limb.Layers
                              select(BodyPart) Util.WeightedRandomDraw(choices: layer,
                                                                       weights: (from p in layer select p.Volume).ToArray())).ToArray();

        // Record targets' current integrities as an initial-state reference
        float[] initial_integrities = (from tgt in targets select tgt.Integrity).ToArray();

        // Create a tally of remaining damage to be distributed
        BodyPartDamage dmg_left = damage;

        // Iterate through targets, going outside in and breaking early if we run out of damage to distribute.
        for (int i = targets.Length - 1; i >= 0; i--)
        {
            // Identify the index of this part in its own layer array
            int part_index = Array.IndexOf(limb.Layers[i], targets[i]);

            // Apply impact damage, in whole or in part
            if (dmg_left.Impact / initial_integrities[i] > ImpactSpilloverPoint)
            {
                // With spillover
                float impact_amt = dmg_left.Impact * ImpactSpilloverPoint;
                limb.Layers[i][part_index] = DamagePart(damage: new BodyPartDamage(impact: impact_amt),
                                                        part: targets[i]);
                dmg_left.Impact -= impact_amt;
            }
            else
            {
                // Without spillover
                limb.Layers[i][part_index] = DamagePart(damage: new BodyPartDamage(impact: dmg_left.Impact),
                                                        part: limb.Layers[i][part_index]);

                dmg_left.Impact = 0f;
            }

            // Apply shear damage, in whole or part
            if (dmg_left.Shear / initial_integrities[i] > ShearSpilloverPoint)
            {
                // With spillover
                float shear_amt = dmg_left.Shear * ShearSpilloverPoint;
                limb.Layers[i][part_index] = DamagePart(damage: new BodyPartDamage(shear: shear_amt),
                                                        part: limb.Layers[i][part_index]);

                dmg_left.Shear -= shear_amt;
            }
            else
            {
                // Without spillover
                limb.Layers[i][part_index] = DamagePart(damage: new BodyPartDamage(shear: dmg_left.Shear),
                                                        part: limb.Layers[i][part_index]);

                dmg_left.Shear = 0f;
            }

            // Apply corrosive damage, in whole or part
            if (dmg_left.Corrosive / initial_integrities[i] > CorrosiveSpilloverPoint)
            {
                // With spillover
                float corrosive_amt = dmg_left.Corrosive * CorrosiveSpilloverPoint;
                limb.Layers[i][part_index] = DamagePart(damage: new BodyPartDamage(corrosive: corrosive_amt),
                                                        part: limb.Layers[i][part_index]);

                dmg_left.Corrosive -= corrosive_amt;
            }
            else
            {
                // Without spillover
                limb.Layers[i][part_index] = DamagePart(damage: new BodyPartDamage(corrosive: dmg_left.Corrosive),
                                                        part: limb.Layers[i][part_index]);

                dmg_left.Corrosive = 0f;
            }

            // Apply energy damage, in whole or part
            if (dmg_left.Energy / initial_integrities[i] > EnergySpilloverPoint)
            {
                // With spillover
                float energy_amt = dmg_left.Energy * EnergySpilloverPoint;
                limb.Layers[i][part_index] = DamagePart(damage: new BodyPartDamage(energy: energy_amt),
                                                        part: limb.Layers[i][part_index]);

                dmg_left.Energy -= energy_amt;
            }
            else
            {
                // Without spillover
                limb.Layers[i][part_index] = DamagePart(damage: new BodyPartDamage(energy: dmg_left.Energy),
                                                        part: limb.Layers[i][part_index]);

                dmg_left.Energy = 0f;
            }

            // Lastly, if no further damage is left to spill over, break the loop.
            // Otherwise, we'll carry that damage forward one layer deeper.
            if (dmg_left.Impact + dmg_left.Shear + dmg_left.Corrosive + dmg_left.Energy == 0)
            {
                break;
            }
        }

        // If any damage remains after all penetration has been accounted for,
        // then divide it evenly among parts which were damaged by this attack.
        if (dmg_left.Impact + dmg_left.Shear + dmg_left.Corrosive + dmg_left.Energy != 0)
        {
            for (int i = targets.Length - 1; i >= 0; i--)
            {
                int part_index = Array.IndexOf(limb.Layers[i], targets[i]);
                limb.Layers[i][part_index] = DamagePart(damage: new BodyPartDamage(impact: dmg_left.Impact / targets.Length,
                                                                                   shear: dmg_left.Shear / targets.Length,
                                                                                   corrosive: dmg_left.Corrosive / targets.Length,
                                                                                   energy: dmg_left.Energy / targets.Length),
                                                        part: limb.Layers[i][part_index]);
            }
        }

        // Finally, return the transmuted Limb.
        return(limb);
    }