private int CalculateWeaponStrike(Character attacker, Character attackedCharacter) { if (attackedCharacter == null) return -1; // First check against evade int evadeRoll = m_random.getInt(0, 99); if ((bool)Preferences.Instance["ShowAttackRolls"]) { CoreGameEngine.Instance.SendTextOutput(string.Format("{0} rolls to hit {1}. Rolled {2} needs above {3} to hit.", attacker.Name, attackedCharacter.Name, evadeRoll, attackedCharacter.Evade)); } if (evadeRoll < attackedCharacter.Evade) return -1; // Calculate damage float effectiveStrength = EffectiveStrengthAtPoint((Weapon)attacker.CurrentWeapon, attacker.Position, attacker.Vision, attackedCharacter.Position); double damageDone = (int)Math.Round(attacker.CurrentWeapon.Damage.Roll() * effectiveStrength); damageDone += attacker.GetTotalAttributeValue("BonusWeaponDamage"); damageDone += attacker.GetTotalAttributeValue("Bonus" + attacker.CurrentWeapon.Type + "Damage"); damageDone -= attackedCharacter.GetTotalAttributeValue("DamageReduction"); // If for some reason damage reduction reduces to < 0, do zero damage. damageDone = Math.Max(0, damageDone); return (int)Math.Round(damageDone); }
internal bool Attack(Character attacker, Point attackTarget) { if (!IsPositionInTargetablePoints((Weapon)attacker.CurrentWeapon, attacker.Position, attacker.Vision, attackTarget)) throw new ArgumentException("CombatEngine attacking something current weapon can't attack with?"); if (attacker.CurrentWeapon.IsRanged) { if (!attacker.CurrentWeapon.IsLoaded) throw new ArgumentException("CombatEngine attacking something with current weapon unloaded?"); AttackRanged(attacker, attackTarget, attacker.CurrentWeapon, (dmg, target, killed) => CoreGameEngine.Instance.SendTextOutput(CreateDamageString(dmg, attacker, target))); ((Weapon)attacker.CurrentWeapon).UnloadWeapon(); } else { Character attackedCharacter = FindTargetAtPosition(attackTarget); if (attackedCharacter != null) { int damageDone = CalculateWeaponStrike(attacker, attackedCharacter); CoreGameEngine.Instance.SendTextOutput(CreateDamageString(damageDone, attacker, attackedCharacter)); DamageTarget(attacker, damageDone, attackedCharacter); } } return true; }
internal bool UseSkill(Character invoker, MonsterSkillType skill, Point target) { // Find the method implementing the skill MethodInfo skillMethod = GetType().GetMethod("Handle" + skill.ToString(), BindingFlags.NonPublic | BindingFlags.Instance); // And invoke it return (bool)skillMethod.Invoke(this, new object[] { invoker, skill, target }); }
internal bool UseItemWithEffect(Character invoker, Item item, Point targetedPoint) { if (!item.ContainsAttribute("Invokable")) throw new System.InvalidOperationException("UseItemWithEffect without invokable object? - " + item.DisplayName); string effectString = string.Format(item.GetAttribute("OnInvokeString"), invoker.Name, item.DisplayName); Spell spellEffect = SpellFactory.Instance.CreateSpell(item.GetAttribute("InvokeSpellEffect")); return DoEffect(invoker, item, spellEffect, int.Parse(item.GetAttribute("CasterLevel"), CultureInfo.InvariantCulture), false, targetedPoint, effectString); }
private bool HandleDoubleSwing(Character invoker, MonsterSkillType skill, Point target) { Character targetCharacter = ValidTarget(invoker, target, 1); if (targetCharacter == null) return false; // If we get here, it's a valid double swing. Attack two times in a row. CoreGameEngine.Instance.SendTextOutput(String.Format("{0} wildly swings at {1} twice.", invoker.Name, targetCharacter.Name)); m_engine.Attack(invoker, target); m_engine.Attack(invoker, target); return true; }
private bool HandleRush(Character invoker, MonsterSkillType skill, Point target) { Character targetCharacter = ValidTarget(invoker, target, 2); if (targetCharacter == null) return false; // If we get here, it's a valid rush. Move towards target and attack at reduced time cost. CoreGameEngine.Instance.SendTextOutput(String.Format("{0} rushes towards {1} and attacks.", invoker.Name, targetCharacter.Name)); List<Point> pathToPoint = CoreGameEngine.Instance.PathToPoint(invoker, target, false, false, true); m_engine.Move(invoker, PointDirectionUtils.ConvertTwoPointsToDirection(invoker.Position, pathToPoint[0])); m_engine.Attack(invoker, target); return true; }
private bool HandleFirstAid(Character invoker, MonsterSkillType skill, Point target) { Character targetCharacter = ValidTargetLessThanOrEqualTo(invoker, target, 1); if (targetCharacter == null) return false; // If we get here, it's a valid first aid. Increase target's HP by amount string targetString = targetCharacter == invoker ? "themself" : "the " + targetCharacter.Name; CoreGameEngine.Instance.SendTextOutput(String.Format("The {0} applies some fast combat medicine on {1}.", invoker.Name, targetString)); int amountToHeal = (new DiceRoll(4, 3)).Roll(); targetCharacter.Heal(amountToHeal, false); CoreGameEngine.Instance.Wait(invoker); return true; }
private bool HandleSlingStone(Character invoker, MonsterSkillType skill, Point target) { Character targetCharacter = ValidTargetLessThanOrEqualTo(invoker, target, SlingDistance); if (targetCharacter == null) return false; List<Point> targetList = new List<Point>() { target }; CoreGameEngine.Instance.FilterNotTargetablePointsFromList(targetList, invoker.Position, invoker.Vision, true); CoreGameEngine.Instance.FilterNotVisibleBothWaysFromList(invoker.Position, targetList); if (targetList.Count < 1) return false; CoreGameEngine.Instance.SendTextOutput(String.Format("{0} slings a stone at {1}.", invoker.Name, targetCharacter.Name)); CoreGameEngine.Instance.CombatEngine.RangedBoltToLocation(invoker, target, (new DiceRoll(5, 3)).Roll(), null, null); // Rest to pass a turn CoreGameEngine.Instance.Wait(invoker); return true; }
// Alright, the behavior we're looking for is a bit unique. // We want to know if you can walk to a given position. // If there is a character there, ignore it so we can walk 'towards' it // If there are doors in the way, if we can operate, ignore them. public List<Point> Travel(Character actor, Point dest, bool canOperate, PhysicsEngine engine, bool usePlayerLOS, bool monstersBlockPath) { UpdateInternalFOV(actor.Position, dest, canOperate, engine, usePlayerLOS, monstersBlockPath); bool pathExists = m_pathFinding.Compute(actor.Position.X, actor.Position.Y, dest.X, dest.Y); if (!pathExists) return null; List<Point> path = new List<Point>(); int pathLength = m_pathFinding.Size(); for (int i = 0; i < pathLength; ++i) { int currentX; int currentY; m_pathFinding.GetPathElement(i, out currentX, out currentY); path.Add(new Point(currentX, currentY)); } return path; }
internal bool WarpToPosition(Character c, Point p) { c.Position = p; return true; }
internal bool Move(Character c, Direction direction) { bool didAnything = false; Point newPosition = PointDirectionUtils.ConvertDirectionToDestinationPoint(c.Position, direction); if (m_map.IsPointOnMap(newPosition) && IsMovablePointSingleShot(m_map, newPosition)) { c.Position = newPosition; m_timingEngine.ActorMadeMove(c); didAnything = true; } return didAnything; }
public List<Character> MonstersInCharactersLOS(Character chacter) { List<Character> returnList = new List<Character>(); FOVManager.CalculateForMultipleCalls(Map, chacter.Position, chacter.Vision); foreach (Monster m in Map.Monsters) { if (FOVManager.Visible(m.Position)) returnList.Add(m); } return returnList; }
private bool HandleRandomTeleport(Character caster, int range) { List<EffectivePoint> targetablePoints = PointListUtils.EffectivePointListFromBurstPosition(caster.Position, range); CoreGameEngine.Instance.FilterNotTargetablePointsFromList(targetablePoints, caster.Position, caster.Vision, false); // If there's no where to go, we're done if (targetablePoints.Count == 0) return true; Random random = new Random(); int element = random.getInt(0, targetablePoints.Count - 1); EffectivePoint pointToTeleport = targetablePoints[element]; CoreGameEngine.Instance.SendTextOutput(string.Format("Things become fuzzy as {0} shifts into a new position.", caster.Name)); m_physicsEngine.WarpToPosition(caster, pointToTeleport.Position); return true; }
private void ShowExplodingRangedPointAttack(Character invoker, object invokingMethod, Point target, int burstWidth) { List<Point> pointsInBallPath = RangedAttackPathfinder.RangedListOfPoints(CoreGameEngine.Instance.Map, invoker.Position, target, false, false); List<List<Point>> pointsInExplosion = new List<List<Point>>(); for (int i = 1; i <= burstWidth; ++i) { List<Point> explosionRing = PointListUtils.PointListFromBurstPosition(target, i); CoreGameEngine.Instance.FilterNotVisibleBothWaysFromList(target, explosionRing); CoreGameEngine.Instance.FilterNotTargetablePointsFromList(explosionRing, invoker.Position, invoker.Vision, true); pointsInExplosion.Add(explosionRing); } var pathData = new Pair<List<Point>, List<List<Point>>>(pointsInBallPath, pointsInExplosion); CoreGameEngine.Instance.ShowRangedAttack(invokingMethod, ShowRangedAttackType.RangedExplodingPoint, pathData, false); }
private static void DamageDoneDelegate(int damage, Character target, bool targetKilled) { string centerString = targetKilled ? "was killed ({0} damage)" : "took {0} damage"; string prefix = target is IPlayer ? "" : "The "; CoreGameEngine.Instance.SendTextOutput(string.Format("{0}{1} {2}.", prefix, target.Name, string.Format(centerString, damage))); }
internal bool Attack(Character attacker, Point target) { bool didAnything = m_combatEngine.Attack(attacker, target); if (didAnything) m_timingEngine.ActorDidWeaponAttack(attacker); return didAnything; }
public void DamageTarget(Character attacker, int damage, Character target) { DamageTarget(attacker, damage, target, null); }
internal bool RangedBoltToLocation(Character attacker, Point target, int damageDone, object attackingMethod, DamageDoneDelegate del) { if (attacker.Position == target) return false; List<Point> attackPath = m_physicsEngine.GenerateRangedAttackListOfPoints(m_map, attacker.Position, target); Character targetCharacter = FindTargetAtPosition(target); CoreGameEngine.Instance.ShowRangedAttack(attackingMethod, ShowRangedAttackType.RangedBolt, attackPath, targetCharacter != null); if (targetCharacter != null) DamageTarget(attacker, damageDone, targetCharacter, del); if (targetCharacter is Monster) ((Monster)targetCharacter).NoticeRangedAttack(attacker.Position); return true; }
private Character ValidTarget(Character invoker, Point targetSquare, int requiredDistance) { // First the distance between us and target must be requiredDistance. List<Point> pathToPoint = CoreGameEngine.Instance.PathToPoint(invoker, targetSquare, false, false, true); if (pathToPoint.Count != requiredDistance) return null; return m_engine.CombatEngine.FindTargetAtPosition(targetSquare); }
public bool Operate(Character characterOperating, Point pointToOperateAt) { // We can't operate if anyone is at that location. if (m_combatEngine.FindTargetAtPosition(pointToOperateAt) != null) return false; OperableMapObject operateObj = m_map.MapObjects.OfType<OperableMapObject>().SingleOrDefault(x => x.Position == pointToOperateAt); if (operateObj != null) { operateObj.Operate(characterOperating); m_timingEngine.ActorDidAction(characterOperating); return true; } return false; }
internal bool Wait(Character c) { m_timingEngine.ActorDidAction(c); return true; }
public void DamageTarget(Character attacker, int damage, Character target, DamageDoneDelegate del) { // Sometimes bouncy spells and other things can hit a creature two or more times. // If the creature is dead and the map agrees, return early, since the poor sob is already dead and gone. if (target.CurrentHP <= 0 && !m_map.Monsters.Contains(target)) return; // -1 damage is coded for a miss if (damage == -1) return; target.Damage(damage); bool targetKilled = target.IsDead; if (del != null) del(damage, target, targetKilled); if (targetKilled) { if (target is Monster) { if (attacker is Player) ((Player)attacker).SkillPoints += 1; m_map.RemoveMonster(target as Monster); TreasureGenerator.Instance.DropTreasureFromMonster(target as Monster); } else if (target is Player) { ((Player)target).EmptyStamina(); CoreGameEngine.Instance.PlayerDied(); } } }
internal bool ReloadWeapon(Character character) { if (!character.CurrentWeapon.IsRanged) throw new InvalidOperationException("ReloadWeapon on non-ranged weapon?"); ((Weapon)character.CurrentWeapon).LoadWeapon(); m_timingEngine.ActorDidMinorAction(character); return true; }
private string CreateDamageString(int damage, Character attacker, Character defender) { // "Cheat" to see if attacker or defense is the player to make text output // what is expected. The's should prepend monsters, not player. // If we have 'Proper' named monsters, like say Kyle the Dragon, this will have to be updated. bool attackerIsPlayer = attacker is Player; bool defenderIsPlayer = defender is Player; bool attackKillsTarget = defender.CurrentHP <= damage; string verb = ((IWeaponVerb)attacker.CurrentWeapon).AttackVerb; if (damage == -1) { if (attackerIsPlayer) return string.Format("{0} misses the {1}.", attacker.Name, defender.Name); else if (defenderIsPlayer) return string.Format("The {0} misses {1}.", attacker.Name, m_player.Name); else return string.Format("The {0} misses the {1}.", attacker.Name, defender.Name); } else if (damage == 0) { if (attackerIsPlayer) return string.Format("{0} {1} and does no damage to the {2}.", attacker.Name, verb, defender.Name); else if (defenderIsPlayer) return string.Format("The {0} {1} and does no damage to {2}.", attacker.Name, verb, defender.Name); else return string.Format("The {0} {1} and does no damage to the {2}.", attacker.Name, verb, defender.Name); } else if (attackKillsTarget) { if (attackerIsPlayer) return string.Format("{0} {1} and kills the {2} with {3} damage.", attacker.Name, verb, defender.Name, damage.ToString()); else if (defenderIsPlayer) return string.Format("The {0} {1} and kills {2} with {3} damage.", attacker.Name, verb, defender.Name, damage.ToString()); else return string.Format("The {0} {1} and kills the {3} with {3} damage.", attacker.Name, verb, defender.Name, damage.ToString()); } else { if (attackerIsPlayer) return string.Format("{0} {1} the {2} for {3} damage.", attacker.Name, verb, defender.Name, damage.ToString()); else if (defenderIsPlayer) return string.Format("The {0} {1} {2} for {3} damage.", attacker.Name, verb, defender.Name, damage.ToString()); else return string.Format("The {0} {1} the {2} for {3} damage.", attacker.Name, verb, defender.Name, damage.ToString()); } }
private bool DoEffect(Character invoker, object invokingMethod, Spell spell, int strength, bool couldBeLongTerm, Point target, string printOnEffect) { switch (spell.EffectType) { case "HealCaster": { CoreGameEngine.Instance.SendTextOutput(printOnEffect); int amountToHeal = (new DiceRoll(20, 3, 0)).Roll(); for (int i = 1 ; i < strength ; ++i) amountToHeal += (new DiceRoll(6, 3, 0)).Roll(); int healAmount = invoker.Heal(amountToHeal, true); CoreGameEngine.Instance.SendTextOutput(string.Format("{0} was healed for {1} health.", invoker.Name, healAmount)); return true; } case "HealMPCaster": { CoreGameEngine.Instance.SendTextOutput(printOnEffect); Player player = invoker as Player; if (player != null) { player.GainMP((new DiceRoll(strength, 3, 4)).Roll()); } return true; } case "RangedSingleTarget": { // This will call ShowRangedAttack inside. CoreGameEngine.Instance.SendTextOutput(printOnEffect); return m_combatEngine.RangedBoltToLocation(invoker, target, CalculateDamgeFromSpell(spell, strength), invokingMethod, DamageDoneDelegate); } case "Stream": { CoreGameEngine.Instance.SendTextOutput(printOnEffect); List<Point> pathOfBlast = m_physicsEngine.GenerateBlastListOfPoints(CoreGameEngine.Instance.Map, invoker.Position, target, false); TrimPath(5, pathOfBlast); List<Point> blastToShow = new List<Point>(pathOfBlast); m_physicsEngine.FilterNotTargetablePointsFromList(blastToShow, invoker.Position, CoreGameEngine.Instance.Player.Vision, true); bool targetAtLastPoint = m_combatEngine.FindTargetAtPosition(pathOfBlast.Last()) != null; CoreGameEngine.Instance.ShowRangedAttack(invokingMethod, ShowRangedAttackType.Stream, blastToShow, targetAtLastPoint); foreach (Point p in pathOfBlast) { Character hitCharacter = m_combatEngine.FindTargetAtPosition(p); if (hitCharacter != null) m_combatEngine.DamageTarget(invoker, CalculateDamgeFromSpell(spell, strength), hitCharacter, DamageDoneDelegate); } return true; } case "RangedBlast": { CoreGameEngine.Instance.SendTextOutput(printOnEffect); List<Point> pathOfBlast = m_physicsEngine.GenerateBlastListOfPoints(CoreGameEngine.Instance.Map, invoker.Position, target, true); TrimPathDueToSpellLength(strength, pathOfBlast); List<Point> blastToShow = new List<Point>(pathOfBlast); m_physicsEngine.FilterNotTargetablePointsFromList(blastToShow, invoker.Position, CoreGameEngine.Instance.Player.Vision, true); CoreGameEngine.Instance.ShowRangedAttack(invokingMethod, ShowRangedAttackType.RangedBlast, blastToShow, false); foreach (Point p in pathOfBlast) { Character hitCharacter = m_combatEngine.FindTargetAtPosition(p); if (hitCharacter != null) m_combatEngine.DamageTarget(invoker, CalculateDamgeFromSpell(spell, strength), hitCharacter, DamageDoneDelegate); } return true; } case "ConeAttack": { CoreGameEngine.Instance.SendTextOutput(printOnEffect); Direction direction = PointDirectionUtils.ConvertTwoPointsToDirection(invoker.Position, target); List<Point> pointsInConeAttack = PointListUtils.PointListFromCone(invoker.Position, direction, 3); CoreGameEngine.Instance.FilterNotVisibleBothWaysFromList(target, pointsInConeAttack); if (pointsInConeAttack == null || pointsInConeAttack.Count == 0) throw new InvalidOperationException("Cone magical attack with nothing to roast?"); ShowConeAttack(invoker, invokingMethod, pointsInConeAttack); foreach (Point p in pointsInConeAttack) { Character hitCharacter = m_combatEngine.FindTargetAtPosition(p); if (hitCharacter != null) m_combatEngine.DamageTarget(invoker, CalculateDamgeFromSpell(spell, strength), hitCharacter, DamageDoneDelegate); } return true; } case "ExplodingRangedPoint": { CoreGameEngine.Instance.SendTextOutput(printOnEffect); const int BurstWidth = 2; ShowExplodingRangedPointAttack(invoker, invokingMethod, target, BurstWidth); List<Point> pointsToEffect = PointListUtils.PointListFromBurstPosition(target, BurstWidth); CoreGameEngine.Instance.FilterNotVisibleBothWaysFromList(target, pointsToEffect); foreach (Point p in pointsToEffect) { Character hitCharacter = m_combatEngine.FindTargetAtPosition(p); if (hitCharacter != null) m_combatEngine.DamageTarget(invoker, CalculateDamgeFromSpell(spell, strength), hitCharacter, DamageDoneDelegate); } return true; } case "Haste": // Should also be added to GetLongTermEffectSpellWouldProduce() case "Light": case "ArmorOfLight": { // These spells can be long term return m_effectEngine.AddEffectToTarget(spell.EffectType, invoker, strength, couldBeLongTerm, target, printOnEffect); } case "Regeneration": case "WordOfHope": { // These spells can't be long term return m_effectEngine.AddEffectToTarget(spell.EffectType, invoker, strength, false, target, printOnEffect); } case "Poison Bolt": { CoreGameEngine.Instance.SendTextOutput(printOnEffect); bool successInRangedBolt = m_combatEngine.RangedBoltToLocation(invoker, target, 1, invokingMethod, DamageDoneDelegate); if (successInRangedBolt) m_effectEngine.AddEffectToTarget("Poison", invoker, strength, false, target); return successInRangedBolt; } case "Slow": { return m_effectEngine.AddEffectToTarget("Slow", invoker, strength, false, target, printOnEffect); } case "Blink": { CoreGameEngine.Instance.SendTextOutput(printOnEffect); HandleRandomTeleport(invoker, 5); return true; } case "Teleport": { CoreGameEngine.Instance.SendTextOutput(printOnEffect); HandleRandomTeleport(invoker, 25); return true; } default: throw new InvalidOperationException("MagicEffectsEngine::DoEffect - don't know how to do: " + spell.EffectType); } }
internal bool Wait(Character c) { return m_physicsEngine.Wait(c); }
private static void ShowConeAttack(Character invoker, object invokingMethod, List<Point> pointsInConeAttack) { List<Point> coneBlastList = new List<Point>(pointsInConeAttack); CoreGameEngine.Instance.FilterNotTargetablePointsFromList(coneBlastList, invoker.Position, invoker.Vision, true); CoreGameEngine.Instance.ShowRangedAttack(invokingMethod, ShowRangedAttackType.Cone, coneBlastList, false); }
private void AttackRanged(Character attacker, Point attackTarget, object attackingMethod, DamageDoneDelegate del) { Character targetCharacter = FindTargetAtPosition(attackTarget); int damageDone = CalculateWeaponStrike(attacker, targetCharacter); RangedBoltToLocation(attacker, attackTarget, damageDone, attackingMethod, del); }
internal List<Point> PathToPoint(Character actor, Point dest, bool canOperate, bool usePlayerLOS, bool monstersBlockPath) { return m_pathFinding.Travel(actor, dest, canOperate, m_physicsEngine, usePlayerLOS, monstersBlockPath); }
private Character ValidTargetLessThanOrEqualTo(Character invoker, Point targetSquare, int requiredDistance) { List<Point> pathToPoint = CoreGameEngine.Instance.PathToPoint(invoker, targetSquare, false, false, true); if (pathToPoint.Count > requiredDistance) return null; return m_engine.CombatEngine.FindTargetAtPosition(targetSquare); }