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); }