public CombatEventResult Attack(string attackerName, string defenderName, double easeOfAttack, double easeOfDodging = World.Mean, bool dodge = true, bool block = true, bool parry = true) { if (Defender.Has <ControllerComponent>()) { Defender.Get <ControllerComponent>().Disturb(); } double atkRoll = World.SkillRoll(); if (atkRoll > easeOfAttack) { Logger.InfoFormat("{0} misses {1} (needs:{2:0.00}, rolled:{3:0.00}, difficulty: {4:0.00}%)", attackerName, defenderName, easeOfAttack, atkRoll, World.ChanceOfSuccess(easeOfAttack)); return(CombatEventResult.Miss); } Logger.InfoFormat("{0} attacks {1} (needs:{2:0.00}, rolled:{3:0.00}, difficulty: {4:0.00}%)", attackerName, defenderName, easeOfAttack, atkRoll, World.ChanceOfSuccess(easeOfAttack)); if (dodge) { double defRoll = World.SkillRoll(); Logger.InfoFormat("{1} attempts to dodge {0}'s attack (needs:{2:0.00}, rolled:{3:0.00}, difficulty: {4:0.00}%)", attackerName, defenderName, easeOfDodging, defRoll, World.ChanceOfSuccess(easeOfDodging)); if (defRoll < easeOfDodging) { return(CombatEventResult.Dodge); } } // todo defenses, parries, etc return(CombatEventResult.Hit); }
public void Damage(int damage, double penetration, DamageType type, out int damageResistance, out int damageDealt) { Contract.Ensures(Contract.ValueAtReturn(out damageDealt) >= 0); Contract.Ensures(Contract.ValueAtReturn(out damageResistance) >= 0); damageDealt = damage; damageResistance = 0; if (Defender.Has <EquipmentComponent>()) { var equipment = Defender.Get <EquipmentComponent>(); if (equipment.ContainSlot(BodyPartTargetted.Name) && equipment.IsSlotEquipped(BodyPartTargetted.Name)) { var armorEntity = equipment.GetEquippedItemAt(BodyPartTargetted.Name); if (armorEntity.Has <ArmorComponent>()) { var armor = armorEntity.Get <ArmorComponent>(); if (armor.Defenses.ContainsKey(BodyPartTargetted.Name)) { if (Rng.Chance(armor.Defenses[BodyPartTargetted.Name].Coverage / 100.0)) { damageResistance = (int)Math.Floor(armor.Defenses[BodyPartTargetted.Name].Resistances[type] / penetration); damageDealt = Math.Max(damage - damageResistance, 0); Logger.InfoFormat("Damage: {3} reduced to {0} because of {1} [DR: {2}]", damageDealt, armor.OwnerUId, damageResistance, damage); } else { // we hit a chink in the armor damageResistance = 0; Logger.InfoFormat("Hit a chink in the armor, DR = 0"); } } } } } if (Defender.Has <ActorComponent>()) { Defender.Get <ActorComponent>().Disturb(); } if (damageDealt > BodyPartTargetted.MaxHealth) { damageDealt = Math.Min(damage, BodyPartTargetted.MaxHealth); Logger.DebugFormat("Damage reduced to {0} because of {1}'s max health", damageDealt, BodyPartTargetted.Name); } BodyPartTargetted.Health -= damageDealt; BodyPartTargetted.Owner.Health -= damageDealt; Logger.InfoFormat("{0}'s {1} was hurt ({2} damage)", Identifier.GetNameOrId(Defender), BodyPartTargetted.Name, damageDealt); }
public override ActionResult OnProcess() { var hitBonus = 0; var weapon = Weapon.Get <RangeComponent>(); var attackerName = Identifier.GetNameOrId(Attacker); var defenderName = Identifier.GetNameOrId(Defender); var attackerLocation = Attacker.Get <Location>(); var defenderLocation = Defender.Get <Location>(); //apply skill if (Attacker.Has <ActorComponent>()) { hitBonus += Attacker.Get <Person>().GetSkill(weapon.Skill).Rank; } else { hitBonus += World.MEAN; } if (weapon.ShotsRemaining <= 0) { World.Log.Normal(String.Format("{0} attempts to use the only to realize the weapon is not loaded", attackerName)); Attacker.Get <ActorComponent>().AP.ActionPoints -= weapon.APToAttack; return(ActionResult.Failed); } weapon.ShotsRemaining--; IEnumerable <Entity> entitiesOnPath; if (attackerLocation.Point == defenderLocation.Point) { // suicide? entitiesOnPath = attackerLocation.Level.GetEntitiesAt(defenderLocation.Point).Where(e => e.Has <DefendComponent>()).ToList(); } else { entitiesOnPath = GetTargetsOnPath(attackerLocation.Level, attackerLocation.Point, defenderLocation.Point).ToList(); } int targetsInTheWay = 0; foreach (var currentEntity in entitiesOnPath) { var targetLocation = currentEntity.Get <Location>(); double range = targetLocation.DistanceTo(attackerLocation) * World.TILE_LENGTH_IN_METER; double rangePenalty = Math.Min(0, -World.STANDARD_DEVIATION * RANGE_PENALTY_STD_DEV_MULT * Math.Log(range) + World.STANDARD_DEVIATION * 2 / 3); Logger.InfoFormat("Target: {2}, range to target: {0}, penalty: {1}", range, rangePenalty, defenderName); // not being targetted gives a sigma (std dev) penalty rangePenalty -= Defender.Id == currentEntity.Id ? 0 : World.STANDARD_DEVIATION; int difficultyOfShot = (int)Math.Round(hitBonus + rangePenalty + (targetsInTheWay * RANGE_PENALTY_TILE_OCCUPIED) - (TargettingPenalty ? BodyPartTargetted.TargettingPenalty : 0)); Logger.InfoFormat("Shot difficulty: {0}, targetting penalty: {1}, weapon bonus: {2}, is target: {3}", difficultyOfShot, BodyPartTargetted.TargettingPenalty, hitBonus, Defender.Id == currentEntity.Id); var result = Attack(attackerName, defenderName, difficultyOfShot); if (result == CombatEventResult.Hit) { var damage = Math.Max(weapon.Damage.Roll(), 1); int damageResistance, realDamage; Damage(weapon.Damage.Roll(), weapon.Penetration, weapon.DamageType, out damageResistance, out realDamage); World.Log.Normal(String.Format("{0} {1} {2}'s {3}.... and inflict {4} wounds.", attackerName, weapon.ActionDescriptionPlural, defenderName, BodyPartTargetted.Name, "todo-description")); Logger.Info(new CombatEventArgs(Attacker, Defender, Weapon, BodyPartTargetted, CombatEventResult.Hit, damage, damageResistance, realDamage)); return(ActionResult.Success); } else if (result == CombatEventResult.Miss) { if (Defender.Id == currentEntity.Id) // if this is where the actor targetted { World.Log.Normal(String.Format("{0} {1} {2}'s {3}.... and misses.", attackerName, weapon.ActionDescriptionPlural, defenderName, BodyPartTargetted.Name)); } Logger.Info(new CombatEventArgs(Attacker, Defender, Weapon, BodyPartTargetted)); return(ActionResult.Failed); } else if (result == CombatEventResult.Dodge) { if (Defender.Id == currentEntity.Id) // if this is where the actor targetted { World.Log.Normal(String.Format("{0} {1} {2}'s {3}.... and {2} dodges.", attackerName, weapon.ActionDescriptionPlural, defenderName, BodyPartTargetted.Name)); } Logger.Info(new CombatEventArgs(Attacker, Defender, Weapon, BodyPartTargetted, CombatEventResult.Dodge)); return(ActionResult.Failed); } targetsInTheWay++; } // todo drop ammo casing World.Log.Normal(String.Format("{0} {1} and hits nothing", attackerName, weapon.ActionDescriptionPlural)); return(ActionResult.Failed); }
public override ActionResult OnProcess() { var hitBonus = 0; var weapon = Weapon.Get <RangeComponent>(); var attackerName = Identifier.GetNameOrId(Attacker); var defenderName = Identifier.GetNameOrId(Defender); var attackerLocation = Attacker.Get <GameObject>(); var defenderLocation = Defender.Get <GameObject>(); //apply skill if (Attacker.Has <ControllerComponent>()) { hitBonus += Attacker.Get <Creature>().Skills[weapon.Skill]; } else { hitBonus += World.Mean; } if (weapon.ShotsRemaining <= 0) { Log.NormalFormat("{0} attempts to use the only to realize the weapon is not loaded", attackerName); return(ActionResult.Failed); } weapon.ShotsRemaining--; IEnumerable <Entity> entitiesOnPath; if (attackerLocation.Location == defenderLocation.Location) { // suicide? entitiesOnPath = attackerLocation.Level.GetEntitiesAt <BodyComponent>(defenderLocation.Location).ToList(); } else { entitiesOnPath = GetTargetsOnPath(attackerLocation.Level, attackerLocation.Location, defenderLocation.Location).ToList(); } int targetsInTheWay = 0; foreach (var currentEntity in entitiesOnPath) { var targetLocation = currentEntity.Get <GameObject>(); double range = targetLocation.DistanceTo(attackerLocation) * World.TileLengthInMeter; double rangePenalty = Math.Min(0, -World.StandardDeviation * RangePenaltyStdDevMultiplier * Math.Log(range) + World.StandardDeviation * 2 / 3); Logger.InfoFormat("Target: {2}, range to target: {0}, penalty: {1}", range, rangePenalty, defenderName); // not being targetted gives a sigma (std dev) penalty rangePenalty -= Defender.Id == currentEntity.Id ? 0 : World.StandardDeviation; int easeOfShot = (int)Math.Round(hitBonus + rangePenalty + (targetsInTheWay * RangePenaltyTileOccupied) + (TargettingPenalty ? BodyPartTargetted.TargettingPenalty : 0)); Logger.InfoFormat("Ease of Shot: {0}, targetting penalty: {1}, weapon bonus: {2}, is original target: {3}", easeOfShot, BodyPartTargetted.TargettingPenalty, hitBonus, Defender.Id == currentEntity.Id); var result = Attack(attackerName, defenderName, easeOfShot); if (result == CombatEventResult.Hit) { var damage = Math.Max(weapon.Damage.Roll(), 1); int damageResistance, realDamage; Damage(weapon.Damage.Roll(), weapon.Penetration, weapon.DamageType, out damageResistance, out realDamage); Log.NormalFormat("{0} {1} {2}'s {3}.... and inflict {4} wounds.", attackerName, weapon.ActionDescriptionPlural, defenderName, BodyPartTargetted.Name, "todo-description"); Logger.Info(new CombatEventArgs(Attacker, Defender, Weapon, BodyPartTargetted, CombatEventResult.Hit, damage, damageResistance, realDamage)); return(ActionResult.Success); } else if (result == CombatEventResult.Miss) { if (Defender.Id == currentEntity.Id) // if this is where the actor targetted { Log.NormalFormat("{0} {1} {2}'s {3}.... and misses.", attackerName, weapon.ActionDescriptionPlural, defenderName, BodyPartTargetted.Name); } Logger.Info(new CombatEventArgs(Attacker, Defender, Weapon, BodyPartTargetted)); return(ActionResult.Failed); } else if (result == CombatEventResult.Dodge) { if (Defender.Id == currentEntity.Id) // if this is where the actor targetted { Log.NormalFormat("{0} {1} {2}'s {3}.... and {2} dodges.", attackerName, weapon.ActionDescriptionPlural, defenderName, BodyPartTargetted.Name); } Logger.Info(new CombatEventArgs(Attacker, Defender, Weapon, BodyPartTargetted, CombatEventResult.Dodge)); return(ActionResult.Failed); } targetsInTheWay++; } // todo drop ammo casing Log.NormalFormat("{0} {1} and hits nothing", attackerName, weapon.ActionDescriptionPlural); return(ActionResult.Failed); }