// Применяем шаг битвы internal void ApplyStepBattle() { CurrentHealth -= ReceivedDamage; if (State != StateHeroInBattle.Tumbstone) { if (CurrentHealth <= 0) { LastTarget = default; Target = null; DestinationForMove = null; if (TileForMove != null) { TileForMove.ReservedForMove = null; TileForMove = null; } State = StateHeroInBattle.Tumbstone; countAction = FormMain.Config.StepsHeroInTumbstone; timeAction = countAction; CurrentHealth = 0; inRollbackAfterAction = false; } } ReceivedDamage = 0; Debug.Assert(CurrentHealth <= Parameters.Health); Debug.Assert(CurrentMana <= Parameters.Mana); Debug.Assert(CurrentStamina <= Parameters.Stamina); }
public Missile(HeroInBattle hero, BattlefieldTile target) { Hero = hero; SourceTile = hero.CurrentTile; DestTile = target; StepsPassed = 0; Debug.Assert(SourceTile != null); Debug.Assert(DestTile != null); // Считаем количество шагов до цели // Для этого вычисляем расстояние в клетках и делим на скорость double distance = Utils.DistanceBetweenPoints(SourceTile.Coord, DestTile.Coord); // Костыль для магов if (hero.PlayerHero.RangeWeapon != null) { StepsToTarget = (int)(distance / hero.PlayerHero.RangeWeapon.VelocityMissile * FormMain.Config.StepsInSecond); } else { StepsToTarget = (int)(distance / 5 * FormMain.Config.StepsInSecond); } Debug.Assert(StepsToTarget > 0); }
internal void ClearPathData() { PathLengthFromStart = 0; HeuristicEstimatePathLength = 0; EstimateFullPathLength = int.MaxValue; State = StateTile.Undefined; PriorTile = null; }
internal int GetHeuristicPathLength(BattlefieldTile toTile) { int deltaX = Math.Abs(X - toTile.X); int deltaY = Math.Abs(Y - toTile.Y); int len; if (pathLength.TryGetValue(deltaX + 1000 + deltaY, out len)) { return(len); } len = Convert.ToInt32(Math.Sqrt(deltaX * deltaX + deltaY * deltaY) * 20); pathLength.Add(deltaX + 1000 + deltaY, len); return(len); }
private void BuildPathForNode(BattlefieldTile pathNode) { BattlefieldTile currentNode = pathNode; while (currentNode != null) { Debug.Assert(_path.IndexOf(currentNode) == -1); //Debug.Assert(currentNode.ReservedForMove == null); Debug.Assert((_path.Count == 0) || currentNode.IsNeighbourTile(_path.Last())); _path.Add(currentNode); currentNode = currentNode.PriorTile; } _path.Reverse(); _path.RemoveAt(0); }
public Battlefield(int width, int height) { Width = width; Height = height; Tiles = new BattlefieldTile[height, width]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { Tiles[y, x] = new BattlefieldTile(x, y); } } for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { Tiles[y, x].AddNeighboring(this); } } }
private void DrawFrame() { if (showGrid != chkbShowGrid.Checked) { showGrid = chkbShowGrid.Checked; DrawBackground(); } // Рисуем подложку gFrame.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy; gFrame.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; gFrame.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed; gFrame.DrawImageUnscaled(bmpLayBackground, 0, 0); // Рисуем полоски жизней героев игроков gFrame.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver; GuiUtils.DrawBand(gFrame, rectBandHealthPlayer1, brushHealth, brushNoneHealth, CalcHealthPlayer(battle.Player1), maxHealthPlayer1); GuiUtils.DrawBand(gFrame, rectBandHealthPlayer2, brushHealth, brushNoneHealth, CalcHealthPlayer(battle.Player2), maxHealthPlayer2); // Рисуем героев foreach (HeroInBattle hero in battle.ActiveHeroes) { Point coordIconHero = CellToClientCoord(hero.CurrentTile); Point shift = new Point(0, 0); if (hero.TileForMove != null) { BattlefieldTile tileforMove = hero.TileForMove; Point coordTileForMove = CellToClientCoord(tileforMove); Debug.Assert(hero.CurrentTile.IsNeighbourTile(tileforMove)); double percent = hero.PercentExecuteAction(); shift.X = (int)((coordTileForMove.X - coordIconHero.X) * percent); shift.Y = (int)((coordTileForMove.Y - coordIconHero.Y) * percent); } if (((hero.Target != null) || (hero.LastTarget != default)) && (hero.State == StateHeroInBattle.MeleeAttack) && (hero.DestinationForMove == null)) { Point coordTarget = hero.Target != null ? hero.Target.Coord : hero.LastTarget; double percent = hero.PercentExecuteAction(); if (hero.Target == null) { percent = 1 - percent; } int shiftX = (int)((coordTarget.X > hero.Coord.X ? 1 : coordTarget.X < hero.Coord.X ? -1 : 0) * FormMain.Config.GridSize * percent); int shiftY = (int)((coordTarget.Y > hero.Coord.Y ? 1 : coordTarget.Y < hero.Coord.Y ? -1 : 0) * FormMain.Config.GridSize * percent); shift.X += shiftX; shift.Y += shiftY; } Debug.Assert(coordIconHero.X + shift.X < Width); Debug.Assert(coordIconHero.Y + shift.Y >= topLeftCells.Y); Debug.Assert(coordIconHero.Y + shift.Y < Height); //cellHeroes[y, x].DrawToBitmap(bmpUnit, new Rectangle(0, 0, bmpUnit.Width, bmpUnit.Height)); gFrame.DrawImageUnscaled(hero.BmpIcon, coordIconHero.X + shift.X, coordIconHero.Y + shift.Y); // Рисуем стрелки атаки if ((hero.Target != null) || (hero.LastTarget != default) || (hero.DestinationForMove != null)) { if ((hero.State == StateHeroInBattle.RangeAttack) || (hero.State != StateHeroInBattle.Cast)) { if (hero.DestinationForMove == null) { if (hero.Target is null) { continue; } } } Point coordTarget; if (hero.DestinationForMove == null) { coordTarget = hero.Target != null ? hero.Target.Coord : hero.LastTarget; } else { coordTarget = new Point(hero.DestinationForMove.X, hero.DestinationForMove.Y); } Point p2 = CellToClientCoord(battle.Battlefield.Tiles[coordTarget.Y, coordTarget.X]); // Делаем расчет точки назначения в зависимости от процент выполнения удара Point pSource = new Point(coordIconHero.X + sizeCell.Width / 2, coordIconHero.Y + sizeCell.Height / 2); Point pTarget = new Point(p2.X + sizeCell.Width / 2, p2.Y + sizeCell.Height / 2); if (hero.DestinationForMove == null) { double percent = hero.PercentExecuteAction(); if (hero.State == StateHeroInBattle.MeleeAttack) { if (hero.InRollbackAction() == true) { percent = 1 - percent; } } pTarget.X = (int)(pSource.X + ((pTarget.X - pSource.X) * percent)); pTarget.Y = (int)(pSource.Y + ((pTarget.Y - pSource.Y) * percent)); } else { if (chkbShowPath.Checked) { pSource = new Point(topLeftGrid.X + (hero.Coord.X * sizeTile.Width) + (sizeTile.Width / 2), topLeftGrid.Y + (hero.Coord.Y * sizeTile.Height) + (sizeTile.Height / 2)); pSource.X += shift.X; pSource.Y += shift.Y; // Рисуем путь юнита к цели foreach (BattlefieldTile t in hero.PathToDestination) { penArrow.Color = hero.PlayerHero.BattleParticipant == battle.Player1 ? FormMain.Config.BattlefieldAllyColor : FormMain.Config.BattlefieldEnemyColor; pTarget = new Point(topLeftGrid.X + (t.Coord.X * sizeTile.Width) + (sizeTile.Width / 2), topLeftGrid.Y + (t.Coord.Y * sizeTile.Height) + (sizeTile.Height / 2) + WIDTH_LINE); gFrame.DrawLine(penArrow, pSource, pTarget); pSource = pTarget; } } } } } // Рисуем снаряды foreach (Missile m in battle.Missiles) { Point ph1 = CellToClientCoord(m.SourceTile); Point p1 = new Point(ph1.X + sizeCell.Width / 2, ph1.Y + sizeCell.Height / 2); Point ph2 = CellToClientCoord(m.DestTile); Point p2 = new Point(ph2.X + sizeCell.Width / 2, ph2.Y + sizeCell.Height / 2); m.Draw(gFrame, p1, p2); } ShowTimerBattle(); //e.Graphics.DrawImage(bmpLayBackground, e.ClipRectangle, e.ClipRectangle, GraphicsUnit.Pixel); //e.Graphics.DrawImageUnscaled(bmpLay0, 0, 0); }
private Point CellToClientCoord(BattlefieldTile t) { return(new Point(topLeftCells.X + (t.X * sizeTile.Width), topLeftCells.Y + (t.Y * sizeTile.Height))); }
// Поиск пути public bool Pathfind(BattlefieldTile fromTile, BattlefieldTile toTile, BattleParticipant throughPlayer) { Debug.Assert(fromTile != null); Debug.Assert(toTile != null); Debug.Assert(fromTile != toTile); _path = new List <BattlefieldTile>();// Наиболее легкий найденный путь // Если это соседняя ячейка, то не надо искать путь if (fromTile.IsNeighbourTile(toTile)) { // Если клетка зарезервирована или на ней есть юнит, идти на нее нельзя if ((toTile.ReservedForMove != null) || (toTile.Unit != null)) { return(false); } _path.Add(toTile); return(true); } // Если режим прохода сквозь юнита, то проверяем, есть ли вокруг ячейки для прохода // Если нет, выходиим if (throughPlayer != null) { bool foundedFree = false; foreach (BattlefieldTile t in fromTile.TilesAround) { if ((t.Unit == null) && (t.ReservedForMove == null)) { foundedFree = true; break; } } if (!foundedFree) { return(false); } } // Очистка данных foreach (BattlefieldTile t in closedSet) { t.ClearPathData(); } closedSet.Clear(); foreach (BattlefieldTile t in openSet) { t.ClearPathData(); } openSet.Clear(); // Ищем путь FindPath(fromTile, toTile, throughPlayer); Debug.Assert((_path.Count() == 0) || fromTile.IsNeighbourTile(_path.First())); Debug.Assert((_path.Count() == 0) || (_path.Last() == toTile)); return(_path.Count() > 0); }
public void FindPath(BattlefieldTile sourceTile, BattlefieldTile destTile, BattleParticipant throughPlayer) { Debug.Assert(sourceTile.Unit != null); BattlefieldTile currentNode; int lengthFromStart; //List<BattlefieldTile> path = new List<BattlefieldTile>(); BattlefieldTile bestTile = null; // Стартуем с начальной ячейки openSet.Add(sourceTile); sourceTile.State = StateTile.Opened; // Цикл, пока есть открытые (необработанные) клетки while (openSet.Count > 0) { // Текущая ячейка - с наименьшим числом отставшегося пути bestTile = openSet.First(); foreach (BattlefieldTile t in openSet) { if (t.EstimateFullPathLength < bestTile.EstimateFullPathLength) { currentNode = t; } } Debug.Assert(bestTile != null); currentNode = bestTile; // Если пришли к конечной точке - строим путь и выходим if (currentNode == destTile) { BuildPathForNode(currentNode); return; } // Убираем ячейку из необработанных и добавляем в обработанные if (!openSet.Remove(currentNode)) { throw new Exception("Ячейка не удалена из списка."); } closedSet.Add(currentNode);// Только для того, чтобы по ней потом очистить данные для поиска пути currentNode.State = StateTile.Closed; foreach (BattlefieldTile neighbourNode in currentNode.TilesAround) { // Если клетка недоступна, просто пропускаем ее if (neighbourNode.State == StateTile.Unavailable) { continue; } // Если зарезервирована соседняя от начала пути ячейки, то обходим её // Когда сделаем шаг, она может быть уже не зарезервирована, поэтому продолжим тот же путь if ((neighbourNode.ReservedForMove != null) && neighbourNode.IsNeighbourTile(sourceTile)) { neighbourNode.State = StateTile.Unavailable; openSet.Remove(neighbourNode); closedSet.Add(neighbourNode); continue; } // Если на клетке есть юнит и это не клетка назначения, то пропускаем её if ((neighbourNode.Unit != null) && (neighbourNode != destTile) && !((throughPlayer != null) && (neighbourNode.Unit.Player == throughPlayer))) { neighbourNode.State = StateTile.Unavailable; openSet.Remove(neighbourNode); closedSet.Add(neighbourNode); continue; } // lengthFromStart = currentNode.PathLengthFromStart + currentNode.GetDistanceToTile(neighbourNode); if (((neighbourNode.PathLengthFromStart == 0) || (lengthFromStart < neighbourNode.PathLengthFromStart)) && (neighbourNode != sourceTile)) { neighbourNode.PathLengthFromStart = lengthFromStart; neighbourNode.HeuristicEstimatePathLength = neighbourNode.GetHeuristicPathLength(destTile); neighbourNode.EstimateFullPathLength = neighbourNode.PathLengthFromStart + neighbourNode.HeuristicEstimatePathLength; neighbourNode.PriorTile = currentNode; } if (neighbourNode.State == StateTile.Undefined) { openSet.Add(neighbourNode); neighbourNode.State = StateTile.Opened; } } } return; }
// Ожидаемое полное расстояние до цели. internal int GetDistanceToTile(BattlefieldTile toTile) { Debug.Assert(this != toTile); return((X == toTile.X) || (Y == toTile.Y) ? 10 : 14); }
internal bool IsNeighbourTile(BattlefieldTile tile) { Debug.Assert(tile != null); return((tile.X >= X - 1) && (tile.X <= X + 1) && (tile.Y >= Y - 1) && (tile.Y <= Y + 1)); }
private bool SearchTargetForMove() { // Ищем ближайшего противника // Здесь переделать на перебор всех противников, до которых можно добраться, и учесть их вес foreach (HeroInBattle h in Battle.ActiveHeroes) { if ((h.Player != Player) && (h.currentTile != null) && (h.State != StateHeroInBattle.Tumbstone)) { bool pathFinded = Battle.Battlefield.Pathfind(CurrentTile, h.currentTile, null); // Если некуда идти, то надо идти в сторону противника. Возможно, после шага к нему можно будет пройти if (!pathFinded) { pathFinded = Battle.Battlefield.Pathfind(CurrentTile, h.currentTile, Player); } if (pathFinded && (Battle.Battlefield._path[0].Unit == null)) { PathToDestination = Battle.Battlefield._path; DestinationForMove = PathToDestination.Last(); Debug.Assert(DestinationForMove == h.currentTile); TileForMove = PathToDestination.First(); Debug.Assert(TileForMove.Unit == null); Debug.Assert(TileForMove.ReservedForMove == null); Debug.Assert(currentTile.IsNeighbourTile(TileForMove)); Debug.Assert(TileForMove.ReservedForMove == null); TileForMove.ReservedForMove = this; return(true); } } } //foreach (BattlefieldTile t in curTile.TilesAround) //{ // if (SearchAround(t) == true) // return true; //} return(false); bool SearchAround(BattlefieldTile tile) { foreach (BattlefieldTile t in tile.TilesAround) { if (t.Unit != null) { if (t.Unit.Player != Player) { if (t.Unit.IsLive == true) // if ((t.Unit.IsLive == true) && (t.Unit.State != StateHeroInBattle.Move)) { // Смотрим, можно ли к нему построить путь if (Battle.Battlefield.Pathfind(CurrentTile, t.Unit.currentTile, null) == true) { PathToDestination = Battle.Battlefield._path; DestinationForMove = PathToDestination.Last(); return(true); } } } } } return(false); } }
//internal int MoveStepPaa // Делает шаг битвы internal void DoStepBattle(Battle b) { Debug.Assert(IsLive == true); Debug.Assert(CurrentTile != null); priorState = State; if (inRollbackAfterAction == false) { switch (State) { case StateHeroInBattle.None: Debug.Assert(Target == null); Debug.Assert(countAction == 0); Debug.Assert(CurrentHealth > 0); Debug.Assert(IsLive == true); // Если сейчас ничего не выполняем, ищем, что можно сделать // Сначала пробуем атаковать стрелковым оружием if (((PlayerHero.RangeWeapon != null) && (QuantityArrows > 0)) || (PlayerHero.TypeCreature.ID == "Cleric") || (PlayerHero.TypeCreature.ID == "Mage")) { bool underMeleeAttack = false; // Если юнит не атакован врукопашную, можно атаковать стрелковой атакой foreach (HeroInBattle h in b.ActiveHeroes) { if ((h != this) && (h.Target == this) && (h.State == StateHeroInBattle.MeleeAttack)) { underMeleeAttack = true; break; } } if (!underMeleeAttack) { SearchTargetForShoot(); } } if (Target == null) { if (!SearchTargetForMelee()) { // Если целей нет, идем к ней if (Target == null) { if (SearchTargetForMove() == true) { State = StateHeroInBattle.Move; countAction = (int)(TimeMove() * 1.00 * ((TileForMove.X != 0) && (TileForMove.Y != 0) ? 1.4 : 1)); timeAction = countAction; inRollbackAfterAction = false; //State = StateHeroInBattle.PrepareMove; } } } } break; case StateHeroInBattle.MeleeAttack: countAction--; if (Target.State != StateHeroInBattle.Tumbstone) { if (countAction == 0) { // Делаем удар по противнику Target.GetDamage(CalcDamageMelee(Target), CalcDamageShoot(Target), CalcDamageMagic(Target)); LastTarget = Target.Coord; Target = null; // После удара делаем паузу длиной во время атаки countAction = TimeAttack(); inRollbackAfterAction = true; } } else { // Противника уже убили, пропускаем ход LastTarget = Target.Coord; Target = null; State = StateHeroInBattle.None; countAction = timeAction - countAction; timeAction = countAction; inRollbackAfterAction = true; } break; case StateHeroInBattle.RangeAttack: case StateHeroInBattle.Cast: countAction--; if (Target.State != StateHeroInBattle.Tumbstone) { if (countAction == 0) { // Делаем удар по противнику LastTarget = Target.Coord; Target = null; // После удара делаем паузу длиной во время атаки countAction = TimeAttack(); inRollbackAfterAction = true; } } else { // Противника уже убили, пропускаем ход LastTarget = Target.Coord; Target = null; State = StateHeroInBattle.None; countAction = timeAction - countAction; timeAction = countAction; inRollbackAfterAction = true; } break; case StateHeroInBattle.Tumbstone: Debug.Assert(Target == null); countAction--; if (countAction == 0) { IsLive = false; State = StateHeroInBattle.Dead; currentTile.Unit = null; currentTile = null; } else { // Если осталось шагов меньше, чем шагов на исчезновение, то надо перерисовать иконку if (countAction <= FormMain.Config.UnitStepsTimeToDisappearance) { inDisappearance = true; needRedraw = true; } } break; case StateHeroInBattle.Move: Debug.Assert(TileForMove != null); Debug.Assert(TileForMove.Unit == null); countAction--; if (countAction == 0) { Debug.Assert(TileForMove.ReservedForMove == this); // Пришли на тайл Target = null; CurrentTile = TileForMove; TileForMove.ReservedForMove = null; TileForMove = null; if (PathToDestination.Count() > 1) { // Заново осматриваемся State = StateHeroInBattle.None; /*TileForMove = PathToDestination.First(); * PathToDestination.RemoveAt(0); * countAction = TimeMove(); * timeAction = countAction;*/ } else { // Пришли на конечный тайл State = StateHeroInBattle.None; } PathToDestination = null; DestinationForMove = null; } break; default: break; } } else { countAction--; if (countAction == 0) { LastTarget = default; State = StateHeroInBattle.None; inRollbackAfterAction = false; } } if (priorState != State) { needRedraw = true; } bool SearchTargetForMelee() { // Ищем, кого атаковать List <HeroInBattle> targets = new List <HeroInBattle>(); foreach (HeroInBattle h in b.ActiveHeroes) { // Собираем список вражеских героев вокруг себя if (h.IsLive == true) { if (h.Player != Player) { if (h.CurrentHealth > 0) { if (IsNeighbour(h) == true) { targets.Add(h); } } } } } if (targets.Count > 0) { Debug.Assert(this != targets[0]); State = StateHeroInBattle.MeleeAttack; Target = targets[0]; countAction = TimeAttack(); timeAction = countAction; return(true); } else { return(false); } } bool SearchTargetForShoot() { //Debug.Assert(PlayerHero.RangeWeapon != null); // Если герой, по которому стреляли, жив, атакуем его снова if ((lastAttackedHero != null) && (lastAttackedHero.CurrentHealth > 0)) { Target = lastAttackedHero; } else { // Ищем, кого атаковать List <HeroInBattle> targets = new List <HeroInBattle>(); foreach (HeroInBattle h in b.ActiveHeroes) { // Собираем список вражеских героев if (h.Player != Player) { if (h.CurrentHealth > 0) { targets.Add(h); } } } if (targets.Count > 0) { Debug.Assert(this != targets[0]); Target = targets[0];// targets[Battle.Rnd.Next(0, targets.Count - 1)]; } } if (Target != null) { State = StateHeroInBattle.RangeAttack; countAction = TimeAttack(); timeAction = countAction; lastAttackedHero = Target; // Создаем выстрел if (PlayerHero.RangeWeapon != null) { Debug.Assert(QuantityArrows > 0); Battle.Missiles.Add(new Arrow(this, Target.CurrentTile)); QuantityArrows--; } else { Battle.Missiles.Add(new MagicStrike(this, Target.CurrentTile)); } return(true); } else { return(false); } } bool IsNeighbour(HeroInBattle hb) { Debug.Assert(this != hb); return(currentTile.IsNeighbourTile(hb.currentTile)); } }
public Arrow(HeroInBattle hero, BattlefieldTile target) : base(hero, target) { penArrow = new Pen(FormMain.Config.ColorEntity(Hero.PlayerHero.BattleParticipant == Hero.Battle.Player1)); penArrow.Width = 2; penArrow.CustomEndCap = new System.Drawing.Drawing2D.AdjustableArrowCap(4.0F, 8.0F, true); }
public MagicStrike(HeroInBattle hero, BattlefieldTile target) : base(hero, target) { brush = new SolidBrush(FormMain.Config.ColorEntity(Hero.PlayerHero.BattleParticipant == Hero.Battle.Player1)); }