private void DrawPointingDirectionTriangle() { if (CurrentViewedTank == null || !CurrentViewedTank.Alive) { return; } //This code does exactly what it sounds like. //It draws an arrow to show which direction the cursor is pointing. //It's here because turning has a delayed rection (intentional) so we wanna //show where the turret is going to end up. //First, get the view rectangle var viewRect = ComputeDrawRectangle(CurrentViewedTank); //Begin drawing in that area //_spriteBatch.Begin(SpriteSortMode.Immediate, null, null, null, null, null, // Matrix.CreateOrthographicOffCenter(viewRect.Left, viewRect.Right, viewRect.Bottom, viewRect.Top, -1, 1)); var tankPos = CurrentViewedTank.Position; var lookPoint = InputDriver.GetInputState().LookDirection - MathHelper.PiOver2; //Check if we're spectating if (CurrentViewedTank != Client?.Player?.Tank) { lookPoint = CurrentViewedTank.InputState.LookDirection - MathHelper.PiOver2; } //Radius of circle = 5 //And solve the triangle var cursorPos = new Vector2( 5f * (float)Math.Cos(lookPoint), 5f * (float)Math.Sin(lookPoint) ) + tankPos; // // 0,.5 // //-0.5,-.5|0,-0.5|.5,-.5 //Triangle points var pts = new[] { new Vector2(0, 0.5f), new Vector2(-0.5f), new Vector2(.5f, -.5f) }; pts = pts.Select(a => RotatePoint(a, lookPoint - MathHelper.PiOver2, Vector2.Zero) + cursorPos).ToArray(); _fx.Alpha = 0.33f; _fx.VertexColorEnabled = true; _fx.World = Matrix.CreateOrthographicOffCenter(viewRect.Left, viewRect.Right, viewRect.Bottom, viewRect.Top, -1, 1); foreach (var pass in _fx.CurrentTechnique.Passes) { pass.Apply(); var vpc = pts.Select(a => new VertexPositionColor(new Vector3(a, 0), Color.Red)).ToArray(); GraphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleList, vpc, 0, 1); } }
/// <summary> /// Allows the game to run logic such as updating the world, /// checking for collisions, gathering input, and playing audio. /// </summary> /// <param name="gameTime">Provides a snapshot of timing values.</param> protected override void Update(GameTime gameTime) { _ui.Update(gameTime); SoundPlayer.Update(gameTime); if (!_hasInitialized || _closing) { return; } if (CurrentViewedTank != null && SoundPlayer != null) { SoundPlayer.PlayerPosition = CurrentViewedTank.Position; SoundPlayer.PlayerVelocity = CurrentViewedTank.LinearVelocity; } SoundPlayer?.Update(gameTime); Diagnostics.BeginMeasurement("Base.Update() & UI Update"); base.Update(gameTime); Diagnostics.EndMeasurement("Base.Update() & UI Update"); if (_modLoader.Running) { _ui.UpdateState(new { Header = "Loading mods...", Content = _modLoader.Status, Button = "Cancel" }); return; } else if (!_hasExecutedModLoaderInit) { _hasExecutedModLoaderInit = true; CreateGame(); return; } if (!_isInPauseMenu) { InputDriver.Update(gameTime); var state = InputDriver.GetInputState(); if (Client?.Input != null && state != Client.Input) { Client.Input = state; } } if (AOTConfig.IsGameHost) { Server.Update(gameTime); } Client.Update(gameTime); if (Client?.Player?.Tank != null && Client.Player.Tank.Alive) { CurrentViewedTank = Client.Player.Tank; } if (_isInPauseMenu) { return; //Don't mess with the pause menu } if (Client.IsInTankSelection) { DeactivateGameInput(); _ui.GoToPageIfNotThere("tankselectionpromptwithcountdown", page => { page.Element <Button>("UnReadyButton").Click += (a, b) => { page.Element <Button>("ConfirmButton").Visibility = EmptyKeys.UserInterface.Visibility.Visible; page.Element <StackPanel>("tankselectionarea").Visibility = EmptyKeys.UserInterface.Visibility.Visible; page.Element <StackPanel>("readyarea").Visibility = EmptyKeys.UserInterface.Visibility.Collapsed; Client.PlayerIsReady = false; }; page.Element <Button>("ConfirmButton").Click += (a, b) => { page.Element <Button>("ConfirmButton").Visibility = EmptyKeys.UserInterface.Visibility.Collapsed; page.Element <StackPanel>("tankselectionarea").Visibility = EmptyKeys.UserInterface.Visibility.Collapsed; page.Element <StackPanel>("readyarea").Visibility = EmptyKeys.UserInterface.Visibility.Visible; Client.PlayerIsReady = true; }; }, (page, state) => { page.Element <TextBlock>("Subscript").Text = page.State <double>("RemainingCountdown").ToString("N0") + " seconds remaining " + (Client.PlayerIsReady ? "until start" : "to choose"); //And update tank options var options = page.Element <StackPanel>("tankoptions"); if (page.State <string[]>("TankOptions") != null) { foreach (var opt in page.State <string[]>("TankOptions")) { if (options.Children.FirstOrDefault(a => a.Name == "opt_" + opt) != null) { //already in there //so we do nothing } else { string reflectionName = opt; //Copy to avoid problems with closures var info = Engine.Helpers.ReflectionHelper.GetGameObjectInfo(reflectionName); if (!info.Exists) { continue; //If the type doesn't exist, continue without showing it } //not in there, so make it var btn = new Button(); //Content is a stack panel var stackPnl = new StackPanel(); stackPnl.Orientation = EmptyKeys.UserInterface.Orientation.Vertical; stackPnl.HorizontalAlignment = EmptyKeys.UserInterface.HorizontalAlignment.Stretch; stackPnl.Children.Add(new TextBlock { FontFamily = new EmptyKeys.UserInterface.Media.FontFamily("JHUF"), FontSize = 20, Foreground = EmptyKeys.UserInterface.Media.Brushes.White, Text = info.DisplayName, Margin = new EmptyKeys.UserInterface.Thickness(0, 5, 0, 0), Width = 350, HorizontalAlignment = EmptyKeys.UserInterface.HorizontalAlignment.Stretch }); stackPnl.Children.Add(new TextBlock { FontFamily = new EmptyKeys.UserInterface.Media.FontFamily("JHUF"), FontSize = 16, Foreground = EmptyKeys.UserInterface.Media.Brushes.White, Margin = new EmptyKeys.UserInterface.Thickness(0, 5, 0, 5), Text = UserInterface.SplitStringIntoLines(info.DisplayDescription, 40), HorizontalAlignment = EmptyKeys.UserInterface.HorizontalAlignment.Center }); btn.Background = EmptyKeys.UserInterface.Media.Brushes.Gray; btn.Content = stackPnl; btn.Name = "opt_" + reflectionName; btn.HorizontalAlignment = EmptyKeys.UserInterface.HorizontalAlignment.Center; btn.Width = 400; btn.Click += (a, b) => { options.Children.Select(c => (c as Button).Background = EmptyKeys.UserInterface.Media.Brushes.Transparent); btn.Background = EmptyKeys.UserInterface.Media.Brushes.Green; Client.SelectTank(reflectionName); }; options.Children.Add(btn); } } } }); if (Client.Player != null) { _ui.UpdateState(new { TankOptions = Client.Player.AllowedTankTypes, RemainingCountdown = Client.RemainingCountdownTime.TotalSeconds }); } } else if (Client.IsInGame && !Client.Game.Ended && !_ui.IsOnEmpty()) { _ui.UnwindAndEmpty(); } else if (Client.IsInGame) { ActivateGameInput(); //Handle spectating if (!_spectateHasSwitchedTank && CurrentViewedTank != null && !CurrentViewedTank.Alive) { Client.Game.TimerFactory.CreateTimer((t) => { SpectateRandomTank(); _spectateHasSwitchedTank = false; }, TimeSpan.FromSeconds(3)); _spectateHasSwitchedTank = true; } if (Client.Game.Ended) { _ui.GoToPageIfNotThere("gameendedpage", page => { }, (page, state) => { var winningTeam = Client.Game.Gamemode.WinningTeam; bool winner = (Client.Player?.Tank.Team == winningTeam); page.Element <TextBlock>("Header").Text = winner ? "You're Winner" : "You Tried"; if (winningTeam == Engine.Gamemodes.Team.Indeterminate) { page.Element <TextBlock>("Subscript").Text = "It's a draw"; } else if (winningTeam == Engine.Gamemodes.Team.Null) { page.Element <TextBlock>("Subscript").Text = "This gamemode has a bug..."; //Wow, so much faith in the game's implementation } else { page.Element <TextBlock>("Subscript").Text = winningTeam.TeamName + " won"; } page.Element <Image>("Star").Visibility = winner ? EmptyKeys.UserInterface.Visibility.Collapsed : EmptyKeys.UserInterface.Visibility.Visible; if (winningTeam?.Players != null) { page.Element <TextBlock>("PlayerList").Text = string.Join("\n", winningTeam.Players.Select(a => a.Username)); } }, new { }); _ui.UpdateState(new object()); } } else { DeactivateGameInput(); if (!_ui.IsOnPage("settingupprompt")) { ShowSetupPrompt(); } switch (Client.Status) { case NetClient.ClientStatus.Authenticating: _ui.UpdateState(new { Header = "Logging in...", Content = "Authenticating with the ZSB servers", Button = "Cancel" }); break; case NetClient.ClientStatus.Connected: _ui.UpdateState(new { Header = "Connected...", Content = "Waiting for the server to respond", Button = "Leave server" }); break; case NetClient.ClientStatus.Connecting: _ui.UpdateState(new { Header = "Connecting to the server...", Content = Client.Message, Button = "Abort" }); break; case NetClient.ClientStatus.Disconnected: _closing = true; _ui.ShowMessageBox("Disconnected", Client.Message, UserInterface.MessageBoxType.ErrorMessageBox, UserInterface.MessageBoxButtons.Ok, a => Exit()); break; case NetClient.ClientStatus.DownloadingMods: _ui.UpdateState(new { Header = "Downloading mods...", Content = "This may take a a while", Button = "Leave server" }); break; case NetClient.ClientStatus.Errored: _ui.UpdateState(new { Header = "A fatal error has occured", Content = "", Button = "Set my hair on fire and leave" }); break; case NetClient.ClientStatus.NotStarted: _ui.UpdateState(new { Header = "Waiting to connect...", Content = "This shouldn't usually happen", Button = "Stare with contempt" }); break; } } if (GameSettings.Instance.ForceFullGCEveryFrame) { GC.Collect(2, GCCollectionMode.Forced, true); } }