// todo Implement PromtUser method // todo Implement ShowNotification/overload for AskUser method // todo Add overload taking string[] param for multiple-row message // todo Add param for more display positions (center, top-left etc..) /// <summary> /// Shows one-row long message /// </summary> /// <param name="message"></param> /// <param name="brush"></param> /// <returns></returns> public CancellationTokenSource ShowNotification(string message, PaintBrush brush) { CancellationTokenSource cts = new CancellationTokenSource(); Task.Run(() => { Canvas notificationCanvas = new Canvas { Width = message.Length + 4, Height = 5, RenderPosition = RenderPosition.TopLeft }; // render brush.RenderCanvas(notificationCanvas); for (int i = 0; i < message.Length; i++) { brush.Render( notificationCanvas, new Position(notificationCanvas.CenterXPosition - (int)Math.Ceiling(message.Length / 2.0) + i, 1), message[i]); } // wait for cancelation WaitHandle.WaitAll(new[] { cts.Token.WaitHandle }); // derender brush.DerenderCanvas(notificationCanvas); }, cts.Token); return(cts); }
// todo Implement PromtUser method // todo Implement ShowNotification/overload for AskUser method // todo Add overload taking string[] param for multiple-row message // todo Add param for more display positions (center, top-left etc..) /// <summary> /// Shows one-row long message /// </summary> /// <param name="message"></param> /// <param name="brush"></param> /// <param name="slowWrite"></param> /// <returns></returns> public CancellationTokenSource ShowNotification(string message, PaintBrush brush, bool slowWrite = false) { CancellationTokenSource cts = new CancellationTokenSource(); Task.Run(() => { Canvas notificationCanvas = new Canvas { Width = message.Length + 5, Height = 5, RenderPosition = RenderPosition.TopLeft }; // render lock (consoleGuardian) { brush.RenderCanvas(notificationCanvas); } lock (consoleGuardian) { if (slowWrite) { for (int i = 0; i < message.Length; i++) { brush.Render( notificationCanvas, new Position(notificationCanvas.CenterXPosition - (int)Math.Ceiling(message.Length / 2.0) + i, 1), message[i]); } } else { notificationCanvas.SetCursorPosition(notificationCanvas.CenterXPosition - (int)Math.Ceiling(message.Length / 2.0), 1); Console.Write(message); } } // wait for cancelation WaitHandle.WaitAll(new[] { cts.Token.WaitHandle }); // derender lock (consoleGuardian) { brush.DerenderCanvas(notificationCanvas); } }, cts.Token); return(cts); }
private static void Reload(GameContext ctx, PaintBrush brush, NPCProvider npcProvider, PathFinderProvider pathFinder, Timer timeTimer, Logger logger) { timeTimer.Change(UInt32.MaxValue, 0); Init(ctx, brush, npcProvider, pathFinder, logger).Wait(); int amountOfPeopleOnSquare = (int)GetAmountOfPeople(ctx.Square, ctx.Player.DifficultyLevel, logger); ctx.Enemies = npcProvider.GenerateEnemies( ctx.Square, new List <Position> { ctx.Player.Position }, amountOfPeopleOnSquare); brush.Render(ctx.Square, ctx.Enemies.Select(x => x.Position).ToList(), Enemy.BodyCharacter); ctx.Player.Position.X = 0; ctx.Player.Position.Y = 0; timeTimer.Change(0, 1000); }
public static async Task Init( GameContext ctx, PaintBrush brush, NPCProvider npcProvider, PathFinderProvider pathFinder, Logger logger) { logger.Trace("Init method called"); Configurations config = new Configurations(); Console.Title = config.GetConsoleTitle; Console.OutputEncoding = System.Text.Encoding.UTF8; // App.config doesnt have to be valid try { logger.Info("Setting up Console size"); Console.BufferWidth = Console.WindowWidth = config.GetConsoleWidth; Console.BufferHeight = Console.WindowHeight = config.GetConsoleHeight; Console.WindowLeft = Console.WindowTop = 0; logger.Info("Setting up Square"); // Console's grid is indexed from zero => doesnt need to add 1 to startX of ctx.Square ctx.Square.StartX = ctx.Square.StartY = config.GetComponentMargin; ctx.Square.Width = config.GetSquareWidth; ctx.Square.Height = config.GetSquareHeight; // TODO: Move Title to AppConfig ctx.Square.Title = "Main field - Square"; logger.Info("Setting up ScoreBoard"); ctx.ScoreBoard.StartX = (config.GetComponentMargin * 3) + ctx.Square.Width; ctx.ScoreBoard.StartY = ctx.Square.StartY; ctx.ScoreBoard.Width = config.GetScoreBoardWidth; ctx.ScoreBoard.Height = (int)Math.Ceiling(config.GetScoreBoardHeight / 2.0) - config.GetComponentMargin; ctx.ScoreBoard.Title = "Info board"; ctx.MovementLog.StartX = ctx.ScoreBoard.StartX; ctx.MovementLog.StartY = ctx.ScoreBoard.Height + (config.GetComponentMargin * 3); ctx.MovementLog.Width = ctx.ScoreBoard.Width; ctx.MovementLog.Height = ctx.ScoreBoard.Height - 1; ctx.MovementLog.Title = "Movement History"; } catch (FormatException ex) { logger.Error($"Some of App.config parameters were incorrect. Exception message: {ex.Message}"); } catch (ArgumentNullException ex) { logger.Error($"Some of App.config parameters were null. Exception message: {ex.Message}"); } Console.CursorVisible = false; DialogProvider dialogProvider = new DialogProvider(); logger.Info("Setting players base position"); ctx.Player.Position = new Position { X = 0, Y = 0 }; ctx.Target = new Position(ctx.Square.ContentWidth - 1, ctx.Square.ContentHeight - 1); logger.Info("User asked to choose difficulty"); // Ask for difficulty ctx.Player.DifficultyLevel = await dialogProvider.AskUser(new Menu <DifficultyLevel> { Question = "Select difficulty:", Choices = new Dictionary <DifficultyLevel, string> { { DifficultyLevel.Easy, "Sir, let me have some drink" }, { DifficultyLevel.Medium, "Alcoholic (professional)" }, { DifficultyLevel.Hard, "I alcohol therefor I am" } }, Position = RenderPosition.Center, Margin = 1 }, brush); logger.Debug($"User picked {ctx.Player.DifficultyLevel.ToString()}"); CancellationTokenSource notificationCts = dialogProvider.ShowNotification("Loading...", brush); #region PathFinder for securing game playability int amountOfPeopleOnSquare = (int)GetAmountOfPeople(ctx.Square, ctx.Player.DifficultyLevel, logger); PathSolution solution; do { ctx.Enemies = npcProvider.GenerateEnemies(ctx.Square, new List <Position> { ctx.Player.Position, ctx.Target }, amountOfPeopleOnSquare); solution = PathFindingProcess(logger, ctx, brush, npcProvider, pathFinder); } while (solution.Path.Count == 0); notificationCts.Cancel(); #endregion #region Rendering // Draws Walls for game field brush.RenderCanvas(ctx.Square); logger.Info($"canvas {ctx.Square.Title} rendered"); // Draws Walls for score board brush.RenderCanvas(ctx.ScoreBoard); logger.Info($"canvas {ctx.ScoreBoard.Title} rendered"); brush.RenderCanvas(ctx.MovementLog); logger.Info($"canvas {ctx.MovementLog.Title} rendered"); // Draws player brush.Render(ctx.Square, ctx.Player.Position, Player.BodyCharacter); logger.Info($"Player rendered in canvas {ctx.Square.Title}"); // render enemies brush.Render(ctx.Square, ctx.Enemies.Select(x => x.Position).ToList(), Enemy.BodyCharacter); // Show result path of path finding brush.ShowPath( ctx.Square, solution.Path, animated: true, animationDelay: 10, visibleLength: 40, visibleFor: 300, underlineTrail: true, underlineChar: CharacterMap.LightTrail, foregroundColor: ConsoleColor.Green, backgroundColor: ConsoleColor.Cyan); #endregion }
/// <summary> /// Checks if next position will be collision and handles it /// </summary> /// <param name="context"></param> /// <param name="lastPosition"></param> /// <param name="newPlayerPosition"></param> /// <param name="newPlayerDirection"></param> /// <param name="brush"></param> private static void TripAndCollisionLogic( GameContext context, Position lastPosition, Position newPlayerPosition, Direction newPlayerDirection, PaintBrush brush) { Random random = new Random(DateTime.Now.Millisecond); bool collided = newPlayerPosition.PredictCollision(context.Enemies.Select(enemy => enemy.Position)); bool tripped = false; if (!collided) { tripped = random.Next(1, 5) == 1; // 25% chance } if (collided) { SimulationResult simulationResult = null; do { // TODO: Move constnat numbres to App.config Simulation simulation = (simulationResult?.Obstacle ?? newPlayerPosition) .SimulateCollision( lastPosition, newPlayerDirection, 3, 4); newPlayerDirection = newPlayerDirection.Reverse(); simulationResult = context.Square.ExecuteSimulation(simulation, newPosition => { return(!newPosition.PredictCollision(context.Enemies.Select(enemy => enemy.Position))); }, brush); lastPosition = simulationResult.LastSafePosition; } while (!simulationResult.HasSuccessfulyFinished); context.Player.Position = simulationResult.LastSafePosition; } else if (tripped) { SimulationResult simulationResult = null; do { Simulation simulation; // TODO: Move constnat numbres to App.config if (!simulationResult?.HasSuccessfulyFinished ?? false) { simulation = lastPosition .SimulateCollision( newPlayerPosition, newPlayerDirection, 3, 4); newPlayerDirection = newPlayerDirection.Reverse(); } else { simulation = newPlayerPosition .SimulateTrip( simulationResult?.LastSafePosition ?? lastPosition, newPlayerDirection, 2, 4); } simulationResult = context.Square.ExecuteSimulation(simulation, newPosition => { return(!newPosition.PredictCollision(context.Enemies.Select(enemy => enemy.Position)) || newPosition.Y < 0 || newPosition.X < 0); // TODO: Finish validation }, brush, true); newPlayerPosition = simulationResult.Obstacle; lastPosition = simulationResult.LastSafePosition; } while (!simulationResult.HasSuccessfulyFinished); context.Player.Position = simulationResult.LastSafePosition; } else { brush.Derender(context.Square, context.Player.Position); // Update position and direction context.Player.Direction = newPlayerDirection; context.Player.Position = newPlayerPosition; brush.Render(context.Square, context.Player.Position, Player.BodyCharacter); } }