/// <summary> /// Allows the game component to perform any initialization it needs to before starting /// to run. This is where it can query for any required services and load content. /// </summary> public override void Initialize() { // We don't want XNA calling this method each time we resume from the menu, // unfortunately, it'll call it whatever we try. So the only thing // we can do is check if it has been called already and return. Yes, it's ugly. if (spriteBatch_ != null) { GhostSoundsManager.ResumeLoops(); return; } // Otherwise, this is the first time this component is Initialized, so proceed. GhostSoundsManager.Init(Game); Grid.Reset(); Constants.Level = 1; spriteBatch_ = (SpriteBatch)Game.Services.GetService(typeof(SpriteBatch)); graphics_ = (GraphicsDeviceManager)Game.Services.GetService(typeof(GraphicsDeviceManager)); soundBank_ = (SoundBank)Game.Services.GetService(typeof(SoundBank)); scoreFont_ = Game.Content.Load<SpriteFont>("Score"); scoreEventFont_ = Game.Content.Load<SpriteFont>("ScoreEvent"); xlife_ = Game.Content.Load<Texture2D>("sprites/ExtraLife"); ppill_ = Game.Content.Load<Texture2D>("sprites/PowerPill"); crump_ = Game.Content.Load<Texture2D>("sprites/Crump"); board_ = Game.Content.Load<Texture2D>("sprites/Board"); boardFlash_ = Game.Content.Load<Texture2D>("sprites/BoardFlash"); bonusEaten_ = new Dictionary<string, int>(); bonus_ = new Dictionary<string, Texture2D>(9); bonus_.Add("Apple", Game.Content.Load<Texture2D>("bonus/Apple")); bonus_.Add("Banana", Game.Content.Load<Texture2D>("bonus/Banana")); bonus_.Add("Bell", Game.Content.Load<Texture2D>("bonus/Bell")); bonus_.Add("Cherry", Game.Content.Load<Texture2D>("bonus/Cherry")); bonus_.Add("Key", Game.Content.Load<Texture2D>("bonus/Key")); bonus_.Add("Orange", Game.Content.Load<Texture2D>("bonus/Orange")); bonus_.Add("Pear", Game.Content.Load<Texture2D>("bonus/Pear")); bonus_.Add("Pretzel", Game.Content.Load<Texture2D>("bonus/Pretzel")); bonus_.Add("Strawberry", Game.Content.Load<Texture2D>("bonus/Strawberry")); scoreEvents_ = new List<ScoreEvent>(5); bonusPresent_ = false; bonusSpawned_ = 0; eatenGhosts_ = 0; Score = 0; xlives_ = 2; paChomp_ = true; playerDied_ = false; player_ = new Player(Game); ghosts_ = new List<Ghost> { new Ghost(Game, player_, Ghosts.Blinky), new Ghost(Game, player_, Ghosts.Clyde), new Ghost(Game, player_, Ghosts.Inky), new Ghost(Game, player_, Ghosts.Pinky)}; ghosts_[2].SetBlinky(ghosts_[0]); // Oh, dirty hack. Inky needs this for his AI. soundBank_.PlayCue("Intro"); LockTimer = TimeSpan.FromMilliseconds(4500); base.Initialize(); }
/// <summary> /// Put the ghosts back in their home, ready to begin a new attack /// </summary> /// <param name="newLevel">True at level start, false otherwise</param> /// <param name="player">The pac man. Pac Man is often respawned with the ghosts, so they need to know about the new Pac Man.</param> public void Reset(bool newLevel, Player player) { State = GhostState.Home; // Ghosts start at home previousState_ = GhostState.Home; // Sounds are played when currentState != previousState. // So to get them playing at level start, we do this simple hack. updateCount_ = 0; initialJumps_ = Constants.InitialJumps(identity_, newLevel); position_ = Constants.startPosition(identity_); scheduleStateEval_ = true; lastJunction_ = new Point(); player_ = player; scatterModesLeft_ = 4; UpdateSpeed(); }
/// <summary> /// Instantiates a ghost. /// </summary> /// <param name="game">A reference to the Game object, needed for access to services.</param> /// <param name="player">A reference to the Pac Man, needed for AI.</param> /// <param name="identity">Which ghost, needed for appearance and behavior.</param> public Ghost(Game game, Player player, Ghosts identity) { spriteBatch_ = (SpriteBatch)game.Services.GetService(typeof(SpriteBatch)); ghostBase1_ = game.Content.Load<Texture2D>("sprites/GhostBase"); ghostBase2_ = game.Content.Load<Texture2D>("sprites/GhostBase2"); ghostChased_ = game.Content.Load<Texture2D>("sprites/GhostChased"); eyesBase_ = game.Content.Load<Texture2D>("sprites/GhostEyes"); eyesCenter_ = game.Content.Load<Texture2D>("sprites/GhostEyesCenter"); colorBase_ = Constants.colors(identity); identity_ = identity; previousNumCrumps_ = 0; Reset(true, player); wiggle_ = true; direction_ = new Direction(); lastJunction_ = new Point(); scatterTiles_ = Constants.scatterTiles(identity); }
/// <summary> /// Allows the game component to update itself. /// </summary> /// <param name="gameTime">Provides a snapshot of timing values.</param> public override void Update(GameTime gameTime) { // Some events (death, new level, etc.) lock the game for a few moments. if (DateTime.Now - eventTimer_ < LockTimer) { ghosts_.ForEach(i => i.LockTimer(gameTime)); // Also we need to do the same thing for our own timer concerning bonuses bonusSpawnedTime_ += gameTime.ElapsedGameTime; return; } // Remove special events older than 5 seconds scoreEvents_.RemoveAll(i => DateTime.Now - i.When > TimeSpan.FromSeconds(5)); // If the player had died, spawn a new one or end game. if (playerDied_) { // extra lives are decremented here, at the same time the pac man is spawned; this makes those // events seem linked. xlives_--; //xlives_++; // Give infinite lives to the evil developer; if (xlives_ >= 0) { playerDied_ = false; player_ = new Player(Game); ghosts_.ForEach(i => i.Reset(false, player_)); scoreEvents_.Clear(); } else { // The game is over Menu.SaveHighScore(Score); Game.Components.Add(new Menu(Game, null)); Game.Components.Remove(this); GhostSoundsManager.StopLoops(); return; } } // When all crumps have been eaten, wait a few seconds and then spawn a new level if (noCrumpsLeft()) { if (Constants.Level < 21) { bonusSpawned_ = 0; Grid.Reset(); player_ = new Player(Game); ghosts_.ForEach(i => i.Reset(true, player_)); soundBank_.PlayCue("NewLevel"); LockTimer = TimeSpan.FromSeconds(2); Constants.Level++; return; } else { // Game over, you win. Menu.SaveHighScore(Score); Game.Components.Add(new Menu(Game, null)); Game.Components.Remove(this); GhostSoundsManager.StopLoops(); return; } } Keys[] inputKeys = Keyboard.GetState().GetPressedKeys(); // The user may escape to the main menu with the escape key if (inputKeys.Contains(Keys.Escape)) { Game.Components.Add(new Menu(Game, this)); Game.Components.Remove(this); GhostSoundsManager.PauseLoops(); // will be resumed in Initialize(). No need for stopping them // if the player subsequently quits the game, since we'll re-initialize GhostSoundManager in // Initialize() if the player wants to start a new game. return; } // Eat crumps and power pills. if (player_.Position.DeltaPixel == Point.Zero) { Point playerTile = player_.Position.Tile; if (Grid.TileGrid[playerTile.X, playerTile.Y].HasCrump) { soundBank_.PlayCue(paChomp_ ? "PacMAnEat1" : "PacManEat2"); paChomp_ = !paChomp_; Score += 10; Grid.TileGrid[playerTile.X, playerTile.Y].HasCrump = false; if (Grid.TileGrid[playerTile.X, playerTile.Y].HasPowerPill) { Score += 40; eatenGhosts_ = 0; for (int i = 0; i < ghosts_.Count; i++) { if (ghosts_[i].State == GhostState.Attack || ghosts_[i].State == GhostState.Scatter || ghosts_[i].State == GhostState.Blue) { ghosts_[i].State = GhostState.Blue; } } Grid.TileGrid[playerTile.X, playerTile.Y].HasPowerPill = false; } // If that was the last crump, lock the game for a while if (noCrumpsLeft()) { GhostSoundsManager.StopLoops(); LockTimer = TimeSpan.FromSeconds(2); return; } } } // Eat bonuses if (bonusPresent_ && player_.Position.Tile.Y == 17 && ((player_.Position.Tile.X == 13 && player_.Position.DeltaPixel.X == 8) || (player_.Position.Tile.X == 14 && player_.Position.DeltaPixel.X == -8))) { LockTimer = TimeSpan.FromSeconds(1.5); Score += Constants.BonusScores(); scoreEvents_.Add(new ScoreEvent(player_.Position, DateTime.Now, Constants.BonusScores())); soundBank_.PlayCue("fruiteat"); bonusPresent_ = false; if (bonusEaten_.ContainsKey(Constants.BonusSprite())) { bonusEaten_[Constants.BonusSprite()]++; } else { bonusEaten_.Add(Constants.BonusSprite(), 1); } } // Remove bonus if time's up if (bonusPresent_ && ((DateTime.Now - bonusSpawnedTime_) > TimeSpan.FromSeconds(10))) { bonusPresent_ = false; } // Detect collision between ghosts and the player foreach (Ghost ghost in ghosts_) { Rectangle playerArea = new Rectangle((player_.Position.Tile.X * 16) + player_.Position.DeltaPixel.X, (player_.Position.Tile.Y * 16) + player_.Position.DeltaPixel.Y, 26, 26); Rectangle ghostArea = new Rectangle((ghost.Position.Tile.X * 16) + ghost.Position.DeltaPixel.X, (ghost.Position.Tile.Y * 16) + ghost.Position.DeltaPixel.Y, 22, 22); if (!Rectangle.Intersect(playerArea, ghostArea).IsEmpty) { // If collision detected, either kill the ghost or kill the pac man, depending on state. if (ghost.State == GhostState.Blue) { GhostSoundsManager.StopLoops(); soundBank_.PlayCue("EatGhost"); ghost.State = GhostState.Dead; eatenGhosts_++; int bonus = (int)(100 * Math.Pow(2, eatenGhosts_)); Score += bonus; scoreEvents_.Add(new ScoreEvent(ghost.Position, DateTime.Now, bonus)); LockTimer = TimeSpan.FromMilliseconds(900); return; } else if (ghost.State != GhostState.Dead ) { KillPacMan(); return; } // Otherwise ( = the ghost is dead), don't do anything special. } } // Periodically spawn a fruit, when the player isn't on the spawn location // otherwise we get an infinite fruit spawning bug if ((Grid.NumCrumps == 180 || Grid.NumCrumps == 80) && bonusSpawned_ < 2 && ! (player_.Position.Tile.Y == 17 && ((player_.Position.Tile.X == 13 && player_.Position.DeltaPixel.X == 8) || (player_.Position.Tile.X == 14 && player_.Position.DeltaPixel.X == -8)))) { bonusPresent_ = true; bonusSpawned_++; bonusSpawnedTime_ = DateTime.Now; } // Now is the time to move player based on inputs and ghosts based on AI // If we have returned earlier in the method, they stay in place player_.Update(gameTime); ghosts_.ForEach(i => i.Update(gameTime)); base.Update(gameTime); }