// Find where the PC should come to cast the spell, and the best target there (todo : optimize if needed) public SpellTarget FindBestTarget(PlayedFighter pc, IEnumerable <Cell> sourceCells, IEnumerable <Fighter> actors, Spell.SpellCategory category) { if (LevelTemplate.statesForbidden != null) { if (LevelTemplate.statesForbidden.Any(state => pc.HasState(state))) { logger.Debug("Spell {0} skipped : statesForbidden {1}", this, string.Join(",", LevelTemplate.statesForbidden)); pc.Character.SendWarning("Spell {0} skipped : statesForbidden {1}", this, string.Join(",", LevelTemplate.statesForbidden)); return(null); // Can't cast this : all the required states are not on the caster } } if (LevelTemplate.statesRequired != null) { if (!LevelTemplate.statesRequired.All(state => pc.HasState(state))) { logger.Debug("Spell {0} skipped : statesRequired {1}", this, string.Join(",", LevelTemplate.statesForbidden)); pc.Character.SendWarning("Spell {0} skipped : statesRequired {1}", this, string.Join(",", LevelTemplate.statesForbidden)); return(null); // Can't cast this : at least one required state is not on the caster } } if (IsMaitrise(null)) // If the spell is a maitrise, then ignore it if not of the proper type for equipped weapon. { Weapon weapon = pc.Character.Inventory.GetEquippedWeapon(); if (weapon == null) { return(null); } if (!IsMaitrise(weapon.typeId)) { return(null); } } _losMap = new LOSMap(pc.Fight); SpellTarget bestResult = null; #region optimisations IEnumerable <Fighter> enemies = pc.GetOpposedTeam().FightersAlive; IEnumerable <Fighter> friends = pc.Team.FightersAlive; bool goodSpell = (Categories & (SpellCategory.Buff | SpellCategory.Healing)) != 0; bool badSpell = (Categories & (SpellCategory.Damages | SpellCategory.Curse)) != 0; IEnumerable <Fighter> targets = null; int targetsCount = 0; if (goodSpell && badSpell) { goodSpell = badSpell = false; } else { targets = goodSpell ? friends : enemies; targetsCount = targets.Count(); } uint surface = this.GetArea(category); efficiencyCache = null; if (!areaDependsOnDirection) { efficiencyCache = new Dictionary <short, int>(); } #endregion if (surface == 1 && LevelTemplate.range == 0) // Hack fast Cure and protect self { var res = GetSpellDamages(pc, pc); if (res.Damage > 0) { bestResult = new SpellTarget(res.Damage, pc.Cell, pc.Cell, this); } } else { foreach (Cell source in sourceCells) { IEnumerable <Cell> destCells = GetCellsAtSpellRange(pc, source, actors); if (goodSpell || badSpell) { if (surface <= 1 && LevelTemplate.range > 0) { destCells = destCells.Intersect(targets.Select(fighter => fighter.Cell)); // for spells that have an area of effect of 1, just find enemies or friends as targets. No need to scan all the range. } } if (surface >= 560 && destCells.Count() > 1) // For spells that cover the full map, use only the first cell { destCells = destCells.Take(1); } SpellTarget newResult = FindBestTarget(pc, source, destCells, actors, category); if (newResult == null || newResult.Efficiency <= 0) { continue; } if (bestResult == null || bestResult.Efficiency < newResult.Efficiency) { bestResult = newResult; if (surface >= 560) { break; // if spell covers all map, and we have some hit, then no need to continue (first source cells are nearest) } if (targetsCount == 1 && surface == 1) { break; // only one target and 1 cell area spell => no need to loop further } } } } if (bestResult != null) { bestResult.Efficiency *= (pc.Stats.CurrentAP / LevelTemplate.apCost); bestResult.cast = false; } return(bestResult); }
/// <summary> /// Warning : this method says if this affect may affect this target. But NOT if the target can be the target of the spell /// (cf épée divine, where you cast it on yourself despite it effects only enemies around you) /// </summary> /// <param name="spellEffect"></param> /// <param name="spell"></param> /// <param name="caster"></param> /// <param name="target"></param> /// <returns></returns> public static bool canAffectTarget(EffectDice spellEffect, Spell spell, PlayedFighter caster, Fighter target) { if (spell.LevelTemplate.spellBreed == (uint)BreedEnum.Eniripsa && spell.Categories == Spell.SpellCategory.Healing && caster.HasState(76)) { return(false); } //if (!spell.IsAvailable(target == null ? null : (int?)target.Id)) return false; //BiM.Behaviors.Game.Spells.Spell.SpellCategory categories = 0; uint surface = spellEffect.Surface; //categories = BiM.Behaviors.Game.Spells.Spell.GetEffectCategories((uint)spellEffect.Id, spell.LevelTemplate.id); if (spellEffect.Targets == SpellTargetType.NONE) { spellEffect.Targets = SpellTargetType.ALL; } //if (target == null) return !spell.LevelTemplate.needTakenCell; if (caster == target) // Self { return((spellEffect.Targets & (SpellTargetType.ONLY_SELF | SpellTargetType.SELF)) != 0); } if (caster.Team == target.Team) // Ally { if (target.Summoned) { return((spellEffect.Targets & SpellTargetType.ALLIES_SUMMON) != 0); } else { return((spellEffect.Targets & SpellTargetType.ALLIES_NON_SUMMON) != 0); } } if (target.Summoned) // Enemies { return((spellEffect.Targets & SpellTargetType.ENEMIES_SUMMON) != 0); } else { return((spellEffect.Targets & SpellTargetType.ENEMIES_NON_SUMMON) != 0); } }