/// <summary> /// Constructor call. Creates a new Door object. /// </summary> /// <param name="content">The content manager to use when loading assets.</param> /// <param name="orient">The orientation of the room (either facing left or facing right).</param> /// <param name="roomName">The name of the room that the door leads to.</param> /// <param name="connectedDoorIndex">The index of the door that is linked to this one.</param> /// <param name="lockT">The type of lock that is on this door.</param> public Door(ContentManager content, AudioEngine audioEngine, DoorOrientations orient, string roomName, int connectedDoorIndex, Locks lockT) : base("Door", content) { Content = content; soundEngine = audioEngine; orientation = orient; linkedRoom = null; linkedRoomName = roomName; linkedDoorIndex = connectedDoorIndex; lockType = lockT; isOpen = false; isSolid = true; switch (lockType) { case Locks.Unlocked: animation = Animations.Unlocked; break; case Locks.Red: animation = Animations.RedLock; break; default: animation = Animations.Unlocked; break; } currentAnimation = (int)animation + (int)orientation; }
/// <summary> /// Checks for geometric collision with all of the enemies in the room. /// </summary> /// <param name="room">The room the player is currently in.</param> public void DetectEnemyCollisions(Room room) { foreach (Enemy enemy in room.EnemyArray) { if (enemy.IsSpawned && (position.Y + collisionYs.Last() >= enemy.Position.Y + enemy.CollisionYs.First()) && (position.Y + collisionYs.First() <= enemy.Position.Y + enemy.CollisionYs.Last()) && (position.X + collisionXs.Last() >= enemy.Position.X + enemy.CollisionXs.First()) && (position.X + collisionXs.First() <= enemy.Position.X + enemy.CollisionXs.Last())) { if ((position.X + collisionXs.First() <= enemy.Position.X + enemy.CollisionXs.Last()) && position.X + collisionXs.First() >= enemy.Position.X + enemy.CollisionXs.First()) { CollideWithEnemy(enemy, XDirection.Left); return; } else { CollideWithEnemy(enemy, XDirection.Right); return; } } } }
/// <summary> /// Loads the linked room in a seperate thread /// </summary> private void LoadRoom() { try { linkedRoom = new Room(linkedRoomName, linkedDoorIndex); isRoomLoaded = true; } catch (Exception e) { System.Diagnostics.Debug.WriteLine("There was an error loading the room: " + e.Message); } }
/// <summary> /// Checks to see if the object will walk over a ledge if they move /// with their attempted horizontal displacement. /// </summary> /// <param name="room">The room the object is in.</param> /// <returns></returns> public bool WillMovePastEdge(Room room) { int column; if (displacementX > 0) column = (int)(Position.X + displacementX + collisionXs.Last()); else if (displacementX < 0) column = (int)(Position.X + displacementX + collisionXs.First()); else return false; int row = (int)(Position.Y + displacementY + collisionYs.Last() + 1); if (row < 0 || row >= room.CollisionMap.Height) return false; if (room.collisionColors[column + row * room.CollisionMap.Width].R == 0) return false; foreach (GameObject obj in room.ObjectArray) { if (obj.IsSolid && obj.IsSpawned) { if ((column >= obj.Position.X) && (column <= obj.Position.X + obj.HitBox.Width) && (row >= obj.Position.Y) && (row <= obj.Position.Y + obj.HitBox.Height)) return false; } } return true; }
/// <summary> /// Checks if the player is touching any hazards in the room. /// </summary> /// <param name="room">The room in which the character is in.</param> /// <returns></returns> public bool CheckHazards(Room room) { foreach (GameObject obj in room.EnvironmentArray) { if (obj.IsHazard) { if ((position.X + collisionXs.Last() >= obj.Position.X) && (position.X + collisionXs.First() <= obj.Position.X + obj.HitBox.Width) && (position.Y + collisionYs.Last() >= obj.Position.Y) && (position.Y + collisionYs.First() <= obj.Position.Y + obj.HitBox.Height)) return true; } } return false; }
/// <summary> /// Checks to see if an object is stuck inside of a solid region or object at its /// current coordinates, OR if it *will be* stuck if its coordinates are adjusted /// by specified amounts in the x and/or y positions. /// Non-zero values may be input for dX and/or dY to test for stuckness at the /// coordinates equal to the object's coordinates plus the vector (dX, dY). /// </summary> /// <param name="room">The room in which to test for stuckness</param> /// <param name="dX">The value by which to adjust the x-coordinate in the test</param> /// <param name="dY">The value by which to adjust the y-coordinate in the test</param> /// <returns></returns> public virtual bool IsStuck(Room room, int dX, int dY) { //If dX != 0, it's added to the test x-coordinate int CoordinateX = (int)Position.X + dX; //If the leftmost collision points are past the left of the map, or //if the rightmost collision points are past the right of the map, //the object is stuck. if (CoordinateX + collisionXs.First() < 0 || CoordinateX + collisionXs.Last() >= room.CollisionMap.Width) return true; //If dY != 0, it's added to the test y-coordinate int CoordinateY = (int)Position.Y + dY; foreach (int intY in collisionYs) { //Collision points above or below the map will not cause stuckness. if (CoordinateY + intY >= 0 && CoordinateY + intY < room.CollisionMap.Height) { foreach (int intX in collisionXs) { //Walls/grounds/ceilings are black on the collision map, i.e. the R value is 0 if (room.collisionColors[CoordinateX + intX + (CoordinateY + intY) * room.CollisionMap.Width].R == 0) return true; } } } foreach (GameObject obj in room.ObjectArray) { if (obj.IsSolid && obj.IsSpawned && this != obj) { if ((CoordinateX + collisionXs.First() <= obj.Position.X + obj.HitBox.Width) && (CoordinateX + collisionXs.Last() >= obj.Position.X) && (CoordinateY + collisionYs.First() <= obj.Position.Y + obj.HitBox.Height) && (CoordinateY + collisionYs.Last() >= obj.Position.Y)) { if (dX == 0 & obj.IsPhysicsObject) riding = (PhysicsObject)obj; return true; } } } return false; }
/// <summary> /// Tests whether an object that sticks to surfaces will lose contact with all /// surfaces if its horizontal position in the room were adjusted by its horizontal /// displacement for this frame. /// </summary> /// <param name="room">The room in which to test for surfaces.</param> /// <returns></returns> public bool WillLoseSurface(Room room) { int column = (int)position.X; int row = (int)position.Y; if (displacementX != 0) { if (isOnGround) { if (CheckGround(room, (int)displacementX, 0)) return false; row += collisionYs.Last() + 1; } else if (ceilingAbove) { if (CheckCeiling(room, (int)displacementX, 0)) return false; row += collisionYs.First() - 1; } else return false; column += (int)displacementX; if (displacementX > 0) column += collisionXs.First() - 1; else column += collisionXs.Last() + 1; } else if (displacementY != 0) { if (wallOnRight) { if (CheckWallRight(room, 0, (int)displacementY)) return false; column += collisionXs.Last() + 1; } else if (wallOnLeft) { if (CheckWallLeft(room, 0, (int)displacementY)) return false; column += collisionXs.First() - 1; } else return false; row += (int)displacementY; if (displacementY > 0) row += collisionXs.First() - 1; else row += collisionXs.Last() + 1; } else return false; foreach (GameObject obj in room.ObjectArray) { if (obj.IsSolid && obj.IsSpawned) { if ((column >= obj.Position.X) && (column <= obj.Position.X + obj.HitBox.Width) && (row >= obj.Position.Y) && (row <= obj.Position.Y + obj.HitBox.Height)) return false; } } if (room.collisionColors[column + row * room.CollisionMap.Width].R == 0) return false; return true; }
/// <summary> /// Checks to see if the object is touching ground at its current coordinates, OR /// if the object *will be* touching ground if its coordinates are adjusted by /// specified amounts in the x and/or y directions. /// Non-zero values may be input for dX and/or dY to test for ground at the coordinates /// equal to the object's coordinates plus the vector (dX, dY). /// </summary> /// <param name="room">The room in which to test for ground</param> /// <param name="dX">The value by which to adjust the x-coordinate in the test</param> /// <param name="dY">The value by which to adjust the y-coordinate in the test</param> /// <returns></returns> public virtual bool CheckGround(Room room, int dX, int dY) { //row is the y-coordinate just beneath the object's hitbox at its //(adjusted) coordinates. int row = (int)Position.Y + dY + collisionYs.Last() + 1; //All space above or below the map is not considered ground. if (row < 0 || row >= room.CollisionMap.Height) return false; //If the row is on the collision map, do a color test on //all collision points in the row: int coordinateX = (int)Position.X + dX; foreach (int intX in collisionXs) { //Walls/grounds/ceilings are black on the collision map, i.e. the R value is 0 if (room.collisionColors[(int)(coordinateX + intX + row * room.CollisionMap.Width)].R == 0) return true; } //If none of the collision points are in a black region, there is no ground. foreach (GameObject obj in room.ObjectArray) { if (obj.IsSolid && obj.IsSpawned && this != obj) { if ((coordinateX + collisionXs.First() <= obj.Position.X + obj.HitBox.Width) && (coordinateX + collisionXs.Last() >= obj.Position.X) && (row >= obj.Position.Y) && (row <= obj.Position.Y + obj.HitBox.Height)) { obj.StandingOn(objectName); if(obj.IsPhysicsObject) riding = (PhysicsObject)obj; return true; } } } return false; }
/// <summary> /// Checks to see if the object is touching a wall on its right side at its current /// coordinates, OR if the object *will be* touching a wall if its coordinates are /// adjusted by specified amounts in the x and/or y directions. /// Non-zero values may be input for dX and/or dY to test for walls at the coordinates /// equal to the object's coordinates plus the vector (dX, dY). /// </summary> /// <param name="room">The room in which to test for a wall</param> /// <param name="dX">The value by which to adjust the x-coordinate in the test</param> /// <param name="dY">The value by which to adjust the y-coordinate in the test</param> /// <returns></returns> public virtual bool CheckWallRight(Room room, int dX, int dY) { //column is the x-coordinate just right of the object's hitbox //at its (adjusted) coordinates. int column = (int)Position.X + dX + collisionXs.Last() + 1; //All space to the right of the map is not considered a wall. if (column >= room.CollisionMap.Width) return false; //If the column is on the collision map, do a color test on //all collision points in the column: int coordinateY = (int)Position.Y + dY; foreach (int intY in collisionYs) { //All space in the column that is above or below the map will not be considered //a wall. But any collision points in the column and on the map *will* be tested. //Walls/grounds/ceilings are black on the collision map, i.e. the R value is 0 if (coordinateY + intY >= 0 && coordinateY + intY < room.CollisionMap.Height && room.collisionColors[column + (coordinateY + intY) * room.CollisionMap.Width].R == 0) return true; } foreach (GameObject obj in room.ObjectArray) { if (obj.IsSolid && obj.IsSpawned && this != obj) { if ((coordinateY + collisionYs.First() <= obj.Position.Y + obj.HitBox.Height) && (coordinateY + collisionYs.Last() >= obj.Position.Y) && (column >= obj.Position.X) && (column <= obj.Position.X + obj.HitBox.Width)) return true; } } //If none of the collision points are in a black region, there is no wall. return false; }
/// <summary> /// Switches the current room to the designated room, unloading the old room. /// </summary> /// <param name="whichRoom">The room to switch to.</param> private void SwitchRooms(Room whichRoom) { oldRoom = currentRoom; currentRoom = whichRoom; StaticBGM.SwitchMusic(currentRoom.MusicName); currentRoom.PlaySoundInstance(); player.Spawn(currentRoom.SpawnLocation); gameHUD.DisplayRoomName(currentRoom.LongName); System.Threading.Thread roomDisposeThread = new System.Threading.Thread(DisposeRoom); roomDisposeThread.Start(); }
/// <summary> /// Checks to see that the object is fully grounded: instead of checking that at least /// one collision point in the bottom row is sitting on either a collision region of the /// map or a solid GameObject, it checks that *each* collision point in the bottom row is /// sitting on either a collision region or a solid GameObject. /// </summary> /// <param name="room">The room in to test for groundedness.</param> /// <param name="dX">The value by which to adjust the x-coordinate in the test</param> /// <param name="dY">The value by which to adjust the y-coordinate in the test</param> /// <returns></returns> public bool CheckFullyGrounded(Room room, int dX, int dY) { int row = (int)Position.Y + dY + collisionYs.Last() + 1; if (row < 0 || row >= room.CollisionMap.Height) return false; bool pointOnSolid = false; foreach (int intX in collisionXs) { int coordinateX = (int)Position.X + dX + intX; if (room.collisionColors[coordinateX + row * room.CollisionMap.Width].R == 0) continue; pointOnSolid = false; foreach (GameObject obj in room.ObjectArray) { if (obj.IsSolid && obj.IsSpawned && this != obj) { if ((coordinateX >= obj.Position.X) && (coordinateX <= obj.Position.X + obj.HitBox.Width) && (row >= obj.Position.Y) && (row <= obj.Position.Y + obj.HitBox.Height)) { pointOnSolid = true; break; } } } if (!pointOnSolid) return false; } return true; }
/// <summary> /// Starts a new game, getting rid of the menu, deleting old saves, and opening up the starting room. /// </summary> private void StartNewGame() { if (gameMenu != null) { gameMenu.Dispose(); gameMenu = null; } DeleteSave(); GameResources.RoomSaves = new List<RoomSaveData>(); StaticBGM.SwitchMusic("Level"); currentRoom = new Room("StartRoom", -1); player = new Player(gameContent, SaveGame, SwitchRooms, DisplayInfoBox); player.Spawn(currentRoom.SpawnLocation); gameHUD = new HUD(gameContent, player); gameHUD.DisplayRoomName(currentRoom.LongName); isPaused = false; inGame = true; }
/// <summary> /// Loads the previously saved game /// </summary> private void LoadGame() { try { currentStorageDevice = GetStorageDevice(); using (StorageContainer container = GetStorageContainer(currentStorageDevice)) { using (Stream stream = container.OpenFile("SaveGame.txt", FileMode.Open)) { using (StreamReader sr = new StreamReader(stream)) { string line = GameResources.getNextDataLine(sr, "#"); currentRoom = new Room(line, -1); int playerHealth = int.Parse(GameResources.getNextDataLine(sr, "#")); bool[] playerItems = new bool[int.Parse(GameResources.getNextDataLine(sr, "#"))]; for (int i = 0; i < playerItems.Length; i++) playerItems[i] = bool.Parse(GameResources.getNextDataLine(sr, "#")); RoomSaveData[] roomSaves = new RoomSaveData[int.Parse(GameResources.getNextDataLine(sr, "#"))]; for (int i = 0; i < roomSaves.Length; i ++) { roomSaves[i].roomName = GameResources.getNextDataLine(sr, "#"); bool[] objects = new bool[int.Parse(GameResources.getNextDataLine(sr, "#"))]; for (int j = 0; j < objects.Length; j++) objects[j] = bool.Parse(GameResources.getNextDataLine(sr, "#")); roomSaves[i].objectsAreSpawned = objects; } GameResources.RoomSaves = new List<RoomSaveData>(); foreach (RoomSaveData save in roomSaves) GameResources.RoomSaves.Add(save); player = new Player(gameContent, SaveGame, SwitchRooms, DisplayInfoBox); player.Spawn(currentRoom.SpawnLocation); player.LoadSave(playerHealth, playerItems); gameHUD = new HUD(gameContent, player); isPaused = false; inGame = true; sr.Close(); } } StaticBGM.SwitchMusic(currentRoom.MusicName); } } catch (FileNotFoundException e) { System.Diagnostics.Debug.WriteLine("The save file could not be found: " + e.Message); StartNewGame(); } }
/// <summary> /// Removes the previously loaded room from memory. /// </summary> private void DisposeRoom() { if (oldRoom != null) { oldRoom.Dispose(); oldRoom = null; } }
/// <summary> /// Tests to see if an object's intended x- and y-displacements are fully achievable in its /// current room and position. Causes a collision and modifies the displacement value if /// it would send the object into a collideable surface. /// </summary> /// <param name="obj">The object to be evaluated.</param> /// <param name="room">The room in which the object is located and trying to move.</param> /// <param name="time">The amount of time since the last frame.</param> public static void Update(PhysicsObject obj, Room room, GameTime time) { PhysicsObject ridingAtStart = obj.riding; float totalTime = (float)(time.ElapsedGameTime.TotalSeconds); //All ungrounded, gravity-affected objects will have the displacement caused by //gravity in this frame added to their y-displacement. if (obj.IsGravityAffected && !obj.IsOnGround) obj.DisplacementY = Math.Min(obj.DisplacementY + (GRAVITY / 2) * (totalTime * totalTime), MAX_FALLING_SPEED * totalTime); obj.DisplacementX = (float)((int)(obj.DisplacementX)); obj.DisplacementY = (float)((int)(obj.DisplacementY)); //If the object will get stuck trying to move the originally intended amount, a collision //has occurred. The appropriate velocity & acceleration variables as well as boolean flags //will be reset, by calling the appropriate collision function (HitCeiling(), Land(), or //HitWall()). //Then the intended displacement is decreased by one until the object finds a coordinate //at which it will no longer be stuck. if (obj.DisplacementY < 0) { if (obj.IsStuck(room, 0, (int)obj.DisplacementY)) { obj.HitCeiling(); obj.DisplacementY += 1; while (obj.DisplacementY != 0 && obj.IsStuck(room, 0, (int)obj.DisplacementY)) obj.DisplacementY += 1; } if (obj.SticksToSurfaces && obj.DisplacementX == 0) { if (obj.WillLoseSurface(room)) { while (obj.WillLoseSurface(room)) obj.DisplacementY += 1; } } } else if (obj.DisplacementY > 0) { if (obj.IsStuck(room, 0, (int)obj.DisplacementY)) { obj.Land(); obj.DisplacementY -= 1; while (obj.DisplacementY != 0 && obj.IsStuck(room, 0, (int)obj.DisplacementY)) obj.DisplacementY -= 1; } if (obj.SticksToSurfaces && obj.DisplacementX == 0) { if (obj.WillLoseSurface(room)) { while (obj.WillLoseSurface(room)) obj.DisplacementY -= 1; } } } if (obj.DisplacementX > 0) { if (obj.IsStuck(room, (int)obj.DisplacementX, (int)obj.DisplacementY)) { obj.CollideRight(); obj.DisplacementX -= 1; while (obj.DisplacementX != 0 && obj.IsStuck(room, (int)obj.DisplacementX, (int)obj.DisplacementY)) obj.DisplacementX -= 1; } if (!obj.MovesOffEdges && obj.IsOnGround && obj.DisplacementY == 0) { if (obj.WillMovePastEdge(room)) { obj.VelocityX *= -1; while (obj.WillMovePastEdge(room)) obj.DisplacementX -= 1; } } if (obj.SticksToSurfaces && obj.DisplacementY == 0) { if (obj.WillLoseSurface(room)) { while (obj.WillLoseSurface(room)) obj.DisplacementX -= 1; } } } else if (obj.DisplacementX < 0) { if (obj.IsStuck(room, (int)obj.DisplacementX, (int)obj.DisplacementY)) { obj.CollideLeft(); obj.DisplacementX += 1; while (obj.DisplacementX != 0 && obj.IsStuck(room, (int)obj.DisplacementX, (int)obj.DisplacementY)) obj.DisplacementX += 1; } if (!obj.MovesOffEdges && obj.IsOnGround && obj.DisplacementY == 0) { if (obj.WillMovePastEdge(room)) { obj.VelocityX *= -1; while (obj.WillMovePastEdge(room)) obj.DisplacementX += 1; } } if (obj.SticksToSurfaces && obj.DisplacementY == 0) { if (obj.WillLoseSurface(room)) { while (obj.WillLoseSurface(room)) obj.DisplacementX += 1; } } } else if (obj.DisplacementX == 0 && obj.DisplacementY == 0) { if (obj.IsStuck(room, 0, 0)) { if (obj.WallOnLeft && !obj.WallOnRight) obj.DisplacementX += STUCK_OBJ_DISPLACEMENT; else if (obj.WallOnRight && !obj.WallOnLeft) obj.DisplacementX -= STUCK_OBJ_DISPLACEMENT; } } //Even if a displacement value goes unchanged in the above test, it's still possible that the object //will (not) have a wall beside or ground beneath them after moving their full displacements. This last //test checks to see if this is the case, and changes the appropriate boolean flags accordingly. //If the object is touching a floor in its new position, the appropriate collision-causing function //is called. //If an object was airborne at the beginning of the frame and is still airborne, its y-velocity //is increased by the acceleration due to gravity. //If it's determined that the object is now in the air, it will become airborne. if (!obj.WallOnLeft) { if (obj.CheckWallLeft(room, (int)obj.DisplacementX, (int)obj.DisplacementY)) obj.CollideLeft(); } else { if (!obj.CheckWallLeft(room, (int)obj.DisplacementX, (int)obj.DisplacementY)) obj.WallOnLeft = false; } if (!obj.WallOnRight) { if (obj.CheckWallRight(room, (int)obj.DisplacementX, (int)obj.DisplacementY)) obj.CollideRight(); } else { if (!obj.CheckWallRight(room, (int)obj.DisplacementX, (int)obj.DisplacementY)) obj.WallOnRight = false; } if (!obj.IsOnGround) { if (obj.CheckGround(room, (int)obj.DisplacementX, (int)obj.DisplacementY)) obj.Land(); else if (obj.IsGravityAffected) obj.VelocityY = Math.Min(obj.VelocityY + GRAVITY * totalTime, MAX_FALLING_SPEED); } else { if (!obj.CheckGround(room, (int)obj.DisplacementX, (int)obj.DisplacementY)) obj.LeaveGround(); } if (!obj.CeilingAbove) { if (obj.CheckCeiling(room, (int)obj.DisplacementX, (int)obj.DisplacementY)) obj.HitCeiling(); } else { if (!obj.CheckCeiling(room, (int)obj.DisplacementX, (int)obj.DisplacementY)) obj.LeaveCeiling(); } if ((obj.riding != null) && (obj.riding == ridingAtStart)) { obj.DisplacementX += ridingAtStart.DisplacementX; obj.DisplacementY += ridingAtStart.DisplacementY; } }
/// <summary> /// Extension of PhysicsObject.IsStuckAt() for Enemies. Adds additional check for /// collisions with other enemy objects and walking off ledges before the other checks. /// </summary> /// <param name="room">The room the enemy is located in.</param> /// <param name="dX">An optional amount by which to adjust the Enemy's x-position for the test.</param> /// <param name="dY">An optional amount by which to adjust the Enemy's y-position for the test.</param> /// <returns></returns> public override bool IsStuck(Room room, int dX, int dY) { if (bumpsOtherEnemies) { for (int i = 0; i < room.EnemyArray.Length; i++) { if (!room.EnemyArray[i].BumpsOtherEnemies || !room.EnemyArray[i].isSpawned) continue; if (this == room.EnemyArray[i]) continue; if ((position.Y + dY + collisionYs.Last() >= room.EnemyArray[i].Position.Y + room.EnemyArray[i].collisionYs.First()) && (position.Y + dY + collisionYs.First() <= room.EnemyArray[i].Position.Y + room.EnemyArray[i].collisionYs.Last()) && (position.X + dX + collisionXs.Last() >= room.EnemyArray[i].Position.X + room.EnemyArray[i].collisionXs.First()) && (position.X + dX + collisionXs.First() <= room.EnemyArray[i].Position.X + room.EnemyArray[i].collisionXs.Last())) return true; } } return base.IsStuck(room, dX, dY); }