private void RemoveUnit(Unit Unit, bool Animation) { Units.Remove(Unit); }
// checks if a unit can move to a target coord public bool UnitCanMoveToCoord(Unit Unit, Coord TargetCoord) { return TargetCoord.InList(Move.ConvertToCoordList(GetPossibleFieldsForUnit(Unit))); }
protected List<Move> GetPossibleFieldsForUnitHelper( Unit Unit, Coord Position, int RangeRemaining, bool AttackCalculation, Coord.Direction ComingFrom, List<Coord> Way) { List<Move> PossibleFields = new List<Move>(); if (Map.CoordOnMap(Position) /*&& (GetUnitByCoord(Position) == null || GetUnitByCoord(Position).Fraction == Unit.Fraction)*/) { int Cost = (AttackCalculation) ? 1 : Unit.MovementCostOn(this.Map.GetSurfaceByCoord(Position).Type); if (Cost != -1 && RangeRemaining - Cost >= 0) { if (AttackCalculation) { if (!UnitOnCoord(Position) || GetUnitByCoord(Position).Fraction != Unit.Fraction) PossibleFields.Add(new Move(Position, RangeRemaining - Cost, null)); PossibleFields.AddRange(GetPossibleFieldsForUnit(Unit, new Coord(Position), RangeRemaining - Cost, AttackCalculation, ComingFrom, FirstCall: false)); } else { Way.Add(Position); if (!UnitOnCoord(Position)) // field is only available if there is no unit PossibleFields.Add(new Move(Position, RangeRemaining - Cost, Way)); if (!UnitOnCoord(Position) || GetUnitByCoord(Position).Fraction == Unit.Fraction) // can move through own units PossibleFields.AddRange(GetPossibleFieldsForUnit(Unit, new Coord(Position), RangeRemaining - Cost, AttackCalculation, ComingFrom, Way, FirstCall: false)); } } } return PossibleFields; }
// returns a list of attackable fields for a unit public List<Coord> GetPossibleFieldsToAttack(Unit Unit) { if (Unit.AttackedThisRound || (!Unit.GetMoveAndFightInOneRound() && Unit.Range < Unit.GetDefaultRange()) || Unit.Ammunition == 0) return new List<Coord>(); return Move.ConvertToCoordList(GetPossibleFieldsForUnit(Unit, Unit.Position, Unit.GetFiringRange(), true)); }
// returns the range a unit can move after moving to given coord public Move GetShortestMoveToCoord(Unit Unit, Coord Coord) { int MostRemaining = -1; Move ShortestMove = null; List<Move> PossibleFields = GetPossibleFieldsForUnit(Unit); foreach (Move Move in PossibleFields) { if (Move.Target.Equals(Coord)) { if (Move.RangeRemaining > MostRemaining) { MostRemaining = Move.RangeRemaining; ShortestMove = Move; } } } return ShortestMove; }
// fight procedure public void Fight(Unit Attacker, Unit Defender) { int Distance = Coord.DistanceBetweenCoord(Attacker.Position, Defender.Position); Attacker.AttackedThisRound = true; Attacker.Range = 0; // attacker can't move after attacking int AttackerHealthBeforeFight = Attacker.Health; int AttackerPower = Attacker.GetCombatSkills()[0][(int)Defender.GetClass()], DefenderPower = Defender.GetCombatSkills()[1][(int)Attacker.GetClass()]; int AttackerBuildingBasedBonus = (Map.GetBuildingByCoord(Attacker.Position) != null) ? Map.GetBuildingByCoord(Attacker.Position).GetAttackBonus() : 0; int DefenderGroundBasedBonus = Map.GetSurfaceByCoord(Defender.Position).DefenseBonus; Attacker.DecrementAmmunition(); Defender.DecrementAmmunition(); if (!(Defender.GetFiringRange() < Distance)) if (!Attacker.DecreaseHealth((int)Math.Round(2 * ((Defender.Health / (double)Defender.GetMaxHealth()) * (DefenderPower / ((AttackerPower == 0) ? 1 : AttackerPower))) + DefenderGroundBasedBonus + 0.5, 0))) RemoveUnit(Attacker, true); if (!(Attacker.GetFiringRange() < Distance)) if (!Defender.DecreaseHealth((int)Math.Round(2 * ((AttackerHealthBeforeFight / (double)Attacker.GetMaxHealth()) * (AttackerPower / ((DefenderPower == 0) ? 1 : DefenderPower))) + AttackerBuildingBasedBonus + 0.5, 0))) RemoveUnit(Defender, true); }
public List<Move> GetPossibleFieldsForUnit( Unit Unit, Coord Position = null, int RangeRemaining = -1, bool AttackCalculation = false, Coord.Direction? ComingFrom = null, List<Coord> Way = null, bool FirstCall = true) { List<Move> PossibleFields = new List<Move>(); // first call parameter not set if (RangeRemaining == -1) RangeRemaining = Unit.Range; if (Position == null) Position = Unit.Position; if (Way == null) { Way = new List<Coord>(); Way.Add(new Coord(Unit.Position)); } if (FirstCall) ShortestMovesFoundYet = new List<Move>(); Move MoveToCoord = GetMoveToCoord(Position); if (MoveToCoord == null || (MoveToCoord.RangeRemaining < RangeRemaining)) { if (MoveToCoord != null && MoveToCoord.RangeRemaining > RangeRemaining) ShortestMovesFoundYet.Remove(MoveToCoord); ShortestMovesFoundYet.Add(new Move(Position, RangeRemaining, Way)); Coord Up = new Coord(Position.X, Position.Y - 1), Right = new Coord(Position.X + 1, Position.Y), Down = new Coord(Position.X, Position.Y + 1), Left = new Coord(Position.X - 1, Position.Y); if (ComingFrom != Coord.Direction.Down) PossibleFields.AddRange(GetPossibleFieldsForUnitHelper(Unit, Up, RangeRemaining, AttackCalculation, Coord.Direction.Up, ObjectTools.DeepCopy<List<Coord>>(Way))); // upward if (ComingFrom != Coord.Direction.Left) PossibleFields.AddRange(GetPossibleFieldsForUnitHelper(Unit, Right, RangeRemaining, AttackCalculation, Coord.Direction.Right, ObjectTools.DeepCopy<List<Coord>>(Way))); // right if (ComingFrom != Coord.Direction.Up) PossibleFields.AddRange(GetPossibleFieldsForUnitHelper(Unit, Down, RangeRemaining, AttackCalculation, Coord.Direction.Down, ObjectTools.DeepCopy<List<Coord>>(Way))); // bottom if (ComingFrom != Coord.Direction.Right) PossibleFields.AddRange(GetPossibleFieldsForUnitHelper(Unit, Left, RangeRemaining, AttackCalculation, Coord.Direction.Left, ObjectTools.DeepCopy<List<Coord>>(Way))); // left } if (FirstCall) { if (Unit.Food == 0) { for (int i = 0; i < PossibleFields.Count; i++) { if (PossibleFields[i].Way != null && PossibleFields[i].Way.Count > 2) { PossibleFields.RemoveAt(i); i--; } } } } return PossibleFields; }
public void ShowUnitTypeInformationPopup(Unit Unit) { Label_UnitInfoInfantryAttack.Content = Unit.GetCombatSkills()[0][0].ToString(); Label_UnitInfoInfantryDefend.Content = Unit.GetCombatSkills()[1][0].ToString(); Label_UnitInfoCavalryAttack.Content = Unit.GetCombatSkills()[0][1].ToString(); Label_UnitInfoCavalryDefend.Content = Unit.GetCombatSkills()[1][1].ToString(); Label_UnitInfoArtilleryAttack.Content = Unit.GetCombatSkills()[0][2].ToString(); Label_UnitInfoArtilleryDefend.Content = Unit.GetCombatSkills()[1][2].ToString(); Label_UnitInfoAirForceAttack.Content = Unit.GetCombatSkills()[0][3].ToString(); Label_UnitInfoAirForceDefend.Content = Unit.GetCombatSkills()[1][3].ToString(); Label_UnitInfoNavyAttack.Content = Unit.GetCombatSkills()[0][4].ToString(); Label_UnitInfoNavyDefend.Content = Unit.GetCombatSkills()[1][4].ToString(); Grid_UnitTypeInformationPopup.Visibility = Visibility.Visible; }
// returns whether a unit can attack a given field public bool FieldAttackable(Unit Unit, Coord Field) { return Field.InList(GetPossibleFieldsToAttack(Unit)); }
public void GetUnitCanvasContent(Unit Unit, out Label HealthPoints, out Label RangeLeft, out Label RangeUsed, out Image UnitIcon) { HealthPoints = new Label(); HealthPoints.Name = "Label_HealthPoints"; HealthPoints.Content = Unit.Health.ToString(); HealthPoints.Margin = new Thickness(FieldSize - 17, FieldSize - 17, 1, 1); HealthPoints.Padding = new Thickness(0); HealthPoints.FontSize = 12; HealthPoints.FontWeight = FontWeights.Bold; HealthPoints.Width = 16; HealthPoints.Height = 16; HealthPoints.Foreground = new SolidColorBrush(Colors.White); HealthPoints.Background = new SolidColorBrush(Colors.Black); HealthPoints.HorizontalContentAlignment = HorizontalAlignment.Center; HealthPoints.VerticalContentAlignment = VerticalAlignment.Center; // range left double RangeLeftPercentage = (double)Unit.Range / Unit.GetDefaultRange(); int MaxWidth = FieldSize - 16 - 2; RangeLeft = new Label(); RangeLeft.Margin = new Thickness(MaxWidth - RangeLeftPercentage * MaxWidth + 1, FieldSize - 4 - 1, 17, 1); RangeLeft.Height = 4; RangeLeft.Width = RangeLeftPercentage * MaxWidth; RangeLeft.Background = new SolidColorBrush((Unit.Fraction == GameControl.Game.Turn) ? Colors.DarkGreen : Colors.DarkRed); RangeUsed = new Label(); RangeUsed.Margin = new Thickness(1, FieldSize - 4 - 1, MaxWidth - RangeLeftPercentage * MaxWidth + 1 + 16, 1); RangeUsed.Height = 4; RangeUsed.Width = MaxWidth - RangeLeftPercentage * MaxWidth; RangeUsed.Background = new SolidColorBrush(Colors.Gray); // image UnitIcon = new Image(); UnitIcon.Name = "Image_UnitIcon"; UnitIcon.Width = FieldSize; UnitIcon.Height = FieldSize; UnitIcon.Source = new BitmapImage(new Uri("Resources/Units58/" + ((Unit.Fraction == Fraction.Saracens) ? "S" : "C") + "_" + Unit.GetName() + ".png", UriKind.Relative)); UnitIcon.Stretch = Stretch.Uniform; }
// hide info public void HideClickedFieldInfo() { this.ShownUnit = null; this.ShownBuilding = null; Label_Info_Name.Content = null; Label_Info_Type.Content = null; Label_TaxesPerRound.Visibility = Visibility.Hidden; Image_TaxesPerRoundMoney.Visibility = Visibility.Hidden; Image_Info_Unit.Source = null; Image_Info_Building.Source = null; Image_Info_BuildingBasement.Visibility = Visibility.Hidden; ListBox_Info_GeneralInformation_Description.Visibility = Visibility.Hidden; StackPanel_UnitInfoIcons.Visibility = Visibility.Hidden; ListBox_Info_GeneralInformation_Value.Items.Clear(); HideUnitTypeInformationPopup(); }
// display info of a unit public void DisplayClickedFieldInfo(Unit Unit) { HideClickedFieldInfo(); if (Unit == null) return; this.ShownUnit = Unit; Label_Info_Name.Content = R.String(Unit.GetName()); Label_Info_Type.Visibility = Visibility.Visible; Label_Info_Type.Content = Unit.UnitClassToString(Unit.GetClass()); Image_Info_Unit.Source = new BitmapImage(new Uri("Resources/Units/" + ((Unit.Fraction == Fraction.Saracens) ? "S" : "C") + "_" + Unit.GetName() + ".png", UriKind.Relative)); ; ListBox_Info_GeneralInformation_Description.Visibility = Visibility.Visible; StackPanel_UnitInfoIcons.Visibility = Visibility.Visible; ListBox_Info_GeneralInformation_Value.Items.Add(Unit.Health.ToString() + " / " + Unit.GetMaxHealth().ToString()); ListBox_Info_GeneralInformation_Value.Items.Add(Unit.Ammunition.ToString().Replace("-1", "++") + " / " + Unit.GetMaxAmmunition().ToString().Replace("-1", "++")); ListBox_Info_GeneralInformation_Value.Items.Add(Unit.Food.ToString().Replace("-1", "++") + " / " + Unit.GetMaxFood().ToString().Replace("-1", "++")); ListBox_Info_GeneralInformation_Value.Items.Add(Unit.Range.ToString() + " / " + Unit.GetDefaultRange().ToString()); ListBox_Info_GeneralInformation_Value.Items.Add(Unit.GetInitiative().ToString()); ListBox_Info_GeneralInformation_Value.Items.Add(Unit.GetFiringRange().ToString()); ListBox_Info_GeneralInformation_Value.Items.Add(Unit.GetPrice().ToString()); ShowUnitTypeInformationPopup(Unit); }
private void HandleUnitAnimation(Unit Unit) { if (Unit.AnimatedMovesRemaining.Count == 0 || Unit.ManagedByAnimationThread) // nothing to animate { return; } Unit.ManagedByAnimationThread = true; new Thread(() => { Thread.CurrentThread.IsBackground = true; Thread.CurrentThread.Priority = ThreadPriority.Normal; while (Unit.AnimatedMovesRemaining.Count > 0) { if (Unit.AnimatedMovesRemaining.Count == 0) { break; } for (int i = 1; i < Unit.AnimatedMovesRemaining[0].Way.Count; i++) { Coord Start = Unit.AnimatedMovesRemaining[0].Way[i - 1], Destination = Unit.AnimatedMovesRemaining[0].Way[i]; bool AnimationVertical = (Start.X == Destination.X); int AnimationDuration = (AnimationVertical) ? Math.Abs(GameScreen.MillisecondsPerSegment * (Start.Y - Destination.Y)) : Math.Abs(GameScreen.MillisecondsPerSegment * (Start.X - Destination.X)); Canvas AnimationControlReference = null; GameScreen.Dispatcher.BeginInvoke( (Action)(() => { Canvas Container = new Canvas(); AnimationControlReference = Container; Canvas.SetLeft(Container, GameScreen.FieldSize * Start.X); Canvas.SetTop(Container, GameScreen.FieldSize * Start.Y); Label HealthPoints, RangeLeft, RangeUsed; Image UnitIcon; GameScreen.GetUnitCanvasContent(Unit, out HealthPoints, out RangeLeft, out RangeUsed, out UnitIcon); Container.Children.Add(HealthPoints); Container.Children.Add(RangeLeft); Container.Children.Add(RangeUsed); Container.Children.Add(UnitIcon); GameScreen.Canvas_Inner.Children.Add(Container); DoubleAnimation MoveAnimation = new DoubleAnimation(); MoveAnimation.From = (AnimationVertical) ? GameScreen.FieldSize * Start.Y : GameScreen.FieldSize * Start.X; MoveAnimation.To = (AnimationVertical) ? GameScreen.FieldSize * Destination.Y : GameScreen.FieldSize * Destination.X; MoveAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(AnimationDuration)); Storyboard storyboard = new Storyboard(); storyboard.Children.Add(MoveAnimation); Storyboard.SetTarget(MoveAnimation, Container); Storyboard.SetTargetProperty( MoveAnimation, new PropertyPath((AnimationVertical) ? "(Canvas.Top)" : "(Canvas.Left)")); // Attached dependency property storyboard.Begin(); })); Thread.Sleep(AnimationDuration); GameScreen.Dispatcher.BeginInvoke( (Action)(() => { GameScreen.Canvas_Inner.Children.Remove(AnimationControlReference); })); } Unit.AnimatedMovesRemaining.RemoveAt(0); GameScreen.Dispatcher.BeginInvoke( (Action)(() => { GameScreen.RefreshMap(Game.Map); // refresh main window if (SelectedSegment == Unit.Position) // check if the user has selected another unit or clicked into the map { SegmentClicked(Unit.Position); // simulate a click on the unit which has just been moved to mark the attackable fields } else if (SelectedSegment != null) { SegmentClicked(SelectedSegment); } })); } Unit.ManagedByAnimationThread = false; // klar ist das nötig, bin ich dumm.... }).Start(); // start thread }
private void BackgroundWorkerDownloadLatestGameStateWork(object sender, DoWorkEventArgs e) { int? AvailableGameState = Server.GetGameState(MatchId); // get the match version if (AvailableGameState == null) // server response? { return; } if ((int)AvailableGameState > GameState) // newer version available { // download new game version if (Server.DownloadLatestGameState(MatchId, (int)AvailableGameState, GameState, ref DownloadTraffic)) { // update variable storing the local version GameState = (int)AvailableGameState; Control.MainWindow.Dispatcher.BeginInvoke( (Action)(() => { // refresh traffic label GameScreen.Label_Multiplayer_TrafficDownload.Content = DownloadTraffic.ToString(); GameScreen.UpdateMatchState(GameState); // update label content showing the match state // read downloaded file storing the new game state MultiplayerGameState State = Data.GetMultiplayerGameState(MatchId + "_" + GameState); if (this.Game.Turn != State.Turn && (State.Over == null || !(bool)State.Over)) // other player finished the round? { // TODO: add taskbar alert and sound Control.ShowLoadingScreen(R.String("ItsYourTurn")); this.YourTurnNotificationScreenShown = true; GameScreen.Button_EndTurn.IsEnabled = true; } // new round began? bool NewRoundBegan = (State.Rounds != null && State.Rounds > Game.Rounds); // refresh local game object Game.Map.MergeBuildingOwnersToBildingsList(State.BuildingOwners); List<Unit> GameUnitsBeforeDownload = ObjectTools.DeepCopy<List<Unit>>(Game.Units); Game.Units = State.Units ?? Game.Units; Game.Turn = State.Turn ?? Game.Turn; Game.Crusaders = State.Crusaders ?? Game.Crusaders; Game.Saracens = State.Saracens ?? Game.Saracens; Game.Over = State.Over ?? Game.Over; Game.Rounds = State.Rounds ?? Game.Rounds; foreach (Unit Unit in Game.Units) { Unit.ManagedByAnimationThread = false; HandleUnitAnimation(Unit); } // if a new round just began refresh stats and add statistic stamp to list // else refresh but don't save stamp RefreshStatistic(NewRoundBegan); if (Game.Over) // game over boolean set? { Control.ShowGameOverScreen(StatisticHistory, Game.Turn); } SelectedUnit = null; // refresh main window GameScreen.RefreshMap(Game.Map); GameScreen.RefreshMoneyAndFractionOnTheMove(); })); } } }
// user has clicked a segment on the map public void SegmentClicked(Coord Coord) { SelectedSegment = Coord; // selected segment if (SelectedUnit != null && AllowedToOperate()) { // unit has been selected before // move unit Move Move = Game.GetShortestMoveToCoord(SelectedUnit, SelectedSegment); if (Move != null) { if (SelectedUnit.Range == SelectedUnit.GetDefaultRange()) { // food can only be decremented once per round SelectedUnit.DecrementFood(); } SelectedUnit.Range = (SelectedUnit.Food == 0) ? 0 : Move.RangeRemaining; // set remaining range to 0 when the unit has no more food SelectedUnit.Position = new Coord(SelectedSegment); SelectedUnit.AnimatedMovesRemaining.Add(Move); UploadGameState(); HandleUnitAnimation(SelectedUnit); } // attack other unit if (Game.UnitOnCoord(Coord) && Game.GetUnitByCoord(Coord).Fraction != Game.Turn && Game.FieldAttackable(SelectedUnit, Coord)) { Game.Fight(SelectedUnit, Game.GetUnitByCoord(Coord)); UploadGameState(); // check if both units still exists, if not show animation } // deselect unit else // deselect only if user attacked; keep unit selected if unit has been moved { SelectedUnit = null; } GameScreen.RefreshMap(Game.Map); // refresh main window } // user clicked on an unit if (Game.UnitOnCoord(Coord)) { GameScreen.DisplayClickedFieldInfo(Game.GetUnitByCoord(Coord)); // update info field // unit on clicked coord and unit of fraction being on the move if (!MultiplayerMatch && Game.GetUnitByCoord(Coord).Fraction == Game.Turn || MultiplayerMatch && Game.GetUnitByCoord(Coord).Fraction == MultiplayerFraction && MultiplayerFraction == Game.Turn) { SelectedUnit = Game.GetUnitByCoord(Coord); // set selected unit List<Move> Moves = Game.GetPossibleFieldsForUnit(Game.GetUnitByCoord(Coord)); GameScreen.MarkFieldsAsAvailable(Move.ConvertToCoordList(Moves)); // mark moveable fields GameScreen.MarkFieldsAsAttackable(Game.GetPossibleFieldsToAttack(Game.GetUnitByCoord(Coord))); // mark attackable fields } } else // no unit on coord { // building Building SelectedBuilding = Game.Map.GetBuildingByCoord(Coord); if (SelectedBuilding != null) { GameScreen.DisplayClickedFieldInfo(SelectedBuilding); if (Game.Turn == SelectedBuilding.Fraction) // building on clicked coord? and show menu only if building belongs to fraction which is on turn { GameScreen.ShowMenuOf(SelectedBuilding.GetBuildingType()); // show building menu (like barracks recruitable unit selection, ...) } } else { GameScreen.HideClickedFieldInfo(); } } }
// ends turn public void EndTurn(bool Internal = false) { if (!AllowedToOperate()) return; // multiplayer: aborts current action if the user is not allowed to move if (MultiplayerMatch) // disable end turn button after clicking (will be reenabled when the other player ended his round) GameScreen.Button_EndTurn.IsEnabled = false; Fraction? Won = Game.EndTurn(); // call the actual end turn method //Control.SetTaskBarItemInfoOverlay(Game.Turn); UploadGameState(ForceUpload:true); if (Won != null) { (new Thread(() => { Thread.CurrentThread.Priority = ThreadPriority.Highest; Thread.CurrentThread.IsBackground = false; // dont't cancel this thread when the window is being closed // update database about the winner Server.SetWinningFraction(MatchId, Player.FractionToStringKey((Fraction)Won)); })).Start(); Control.ShowGameOverScreen(StatisticHistory, (Fraction)Won); return; } RefreshStatistic(true); SelectedUnit = null; // refresh main window GameScreen.RefreshMap(Game.Map); GameScreen.RefreshMoneyAndFractionOnTheMove(); }