// Draws the specified rectangle. public void DrawRectangle(Rectangle2F rect, float thickness, Color color, float depth = 0.0f) { DrawLine(new Line2F(rect.Point, rect.Point + new Vector2F(rect.Width - 1, 0.0f)), thickness, color, depth); DrawLine(new Line2F(rect.Point, rect.Point + new Vector2F(0.0f, rect.Height - 1)), thickness, color, depth); DrawLine(new Line2F(rect.Point + rect.Size - Vector2F.One, rect.Point + new Vector2F(rect.Width - 1, 0.0f)), thickness, color, depth); DrawLine(new Line2F(rect.Point + rect.Size - Vector2F.One, rect.Point + new Vector2F(0.0f, rect.Height - 1)), thickness, color, depth); }
// Draws the specified filled rectangle. public void FillRectangle(Rectangle2F rect, Color color, double depth = 0.0) { rect.X = GMath.Round(rect.X); rect.Y = GMath.Round(rect.Y); DrawImage(white1x1, rect, Vector2F.Zero, 0.0, color, SpriteEffects.None, depth); //DrawImage(white1x1, rect.Center + 0.5, new Vector2D(0.5, 0.5), rect.Size, 0, color, SpriteEffects.None, depth); }
public bool CanDodgeCollision(Rectangle2F block, int direction) { if (Math.Abs(velocity.X) > 0.001f && Math.Abs(velocity.Y) > 0.001f) { return(false); // Only dodge when moving horizontally or vertically. } float dodgeDist = autoDodgeDistance; Rectangle2F objBox = PositionedCollisionBox; Vector2F pos = entity.Position; Vector2F dirVect = Directions.ToVector(direction); for (int side = 0; side < 2; side++) { int moveDir = (direction + (side == 0 ? 1 : 3)) % 4; float distance = Math.Abs(objBox.GetEdge((moveDir + 2) % 4) - block.GetEdge(moveDir)); if (distance <= dodgeDist) { Vector2F checkPos = pos + dirVect + (Directions.ToVector(moveDir) * distance); Vector2F gotoPos = GMath.Round(pos) + Directions.ToVector(moveDir); if (!IsPlaceMeetingSolid(checkPos, collisionBox) && !IsPlaceMeetingSolid(gotoPos, collisionBox)) { return(true); } } } return(false); }
public virtual void Update() { UpdateMovement(); if (!isMoving) { CheckSurfaceTile(); } // Check if hurting the player. if (HasFlag(TileFlags.HurtPlayer) && roomControl.Player.IsOnGround) { Rectangle2F playerBox = roomControl.Player.Physics.PositionedCollisionBox; Rectangle2F hurtBox = tileData.TileData.HurtArea; hurtBox.Point += Position; if (hurtBox.Intersects(playerBox)) { roomControl.Player.Hurt(new DamageInfo(tileData.TileData.HurtDamage) { ApplyKnockBack = true, KnockbackDuration = 14, InvincibleDuration = 35, FlickerDuration = 35, HasSource = true, SourcePosition = Center }); } } }
//----------------------------------------------------------------------------- // Collisions //----------------------------------------------------------------------------- // Collide with the inside edges of a rectangle. // NOTE: At the moment, this is only used when player is doomed to fall in a hole. public void PerformInsideEdgeCollisions(Rectangle2F collisionBox, Rectangle2F rect) { Rectangle2F myBox = Rectangle2F.Translate(collisionBox, entity.Position); if (myBox.Left < rect.Left) { isColliding = true; entity.X = rect.Left - collisionBox.Left; velocity.X = 0; } else if (myBox.Right > rect.Right) { isColliding = true; entity.X = rect.Right - collisionBox.Right; velocity.X = 0; } if (myBox.Top < rect.Top) { isColliding = true; entity.Y = rect.Top - collisionBox.Top; velocity.Y = 0; } else if (myBox.Bottom > rect.Bottom) { isColliding = true; entity.Y = rect.Bottom - collisionBox.Bottom; velocity.Y = 0; } }
//----------------------------------------------------------------------------- // Debug drawing //----------------------------------------------------------------------------- // Draw a collision model as a solid color. public void DrawCollisionModel(CollisionModel model, Vector2F position, Color color) { for (int i = 0; i < model.Boxes.Count; i++) { FillRectangle(Rectangle2F.Translate(model.Boxes[i], position), color); } }
//----------------------------------------------------------------------------- // Static Collision Test Methods //----------------------------------------------------------------------------- // Perform a collision test between two entities. public static CollisionInfo PerformCollisionTest(Entity entity, Entity other, CollisionTestSettings settings) { CollisionInfo collisionInfo = new CollisionInfo(); // Check that the other entity meets the collision requirements. if ((entity != other) && (settings.RequiredType == null || settings.RequiredType.IsAssignableFrom(other.GetType())) && (settings.RequiredFlags == 0 || other.Physics.Flags.HasFlag(settings.RequiredFlags)) && (settings.MaxZDistance < 0.0f || Math.Abs(entity.ZPosition - other.ZPosition) <= settings.MaxZDistance)) { // Setup the collision boxes. Rectangle2F cbox1 = settings.GetCollisionBox1(entity); cbox1.Point += entity.Position; Rectangle2F cbox2 = settings.GetCollisionBox2(other); cbox2.Point += other.Position; // Perform the collision test. if (cbox1.Intersects(cbox2)) { collisionInfo.SetEntityCollision(other, 0); } } return(collisionInfo); }
//----------------------------------------------------------------------------- // Internal methods //----------------------------------------------------------------------------- private void Explode() { BombExplosion bombExplosion = new BombExplosion(); RoomControl.SpawnEntity(bombExplosion, Center, zPosition); AudioSystem.PlaySound(GameData.SOUND_BOMB_EXPLODE); // Explode nearby top tiles. if (zPosition < 4) { Rectangle2F tileExplodeArea = Rectangle2F.Zero.Inflated(12, 12); tileExplodeArea.Point += Center; Rectangle2I area = RoomControl.GetTileAreaFromRect(tileExplodeArea); for (int x = area.Left; x < area.Right; x++) { for (int y = area.Top; y < area.Bottom; y++) { Tile tile = RoomControl.GetTopTile(x, y); Rectangle2F tileRect = new Rectangle2F(x * 16, y * 16, 16, 16); if (tile != null && tileRect.Intersects(tileExplodeArea)) { tile.OnBombExplode(); } } } } DestroyAndTransform(bombExplosion); }
public override void Update() { Point2I loc = (Point2I)(Physics.PositionedCollisionBox.Center / GameSettings.TILE_SIZE); // Check if tile location has changed. if (loc != tileLocation) { Rectangle2F rect = new Rectangle2F(loc * GameSettings.TILE_SIZE, new Vector2F(GameSettings.TILE_SIZE, GameSettings.TILE_SIZE)); // Only change tile locations if the collision box is fully contained inside the tile. if (rect.Contains(physics.PositionedCollisionBox)) { tileLocation = loc; // Check for seed bouncers in the new location. foreach (Tile tile in RoomControl.TileManager.GetTilesAtLocation(tileLocation)) { TileSeedBouncer seedBouncer = tile as TileSeedBouncer; if (seedBouncer != null) { OnCollideTile(seedBouncer, false); break; } } } } base.Update(); }
public bool IsSoftMeetingEntity(Entity other, Rectangle2F collisionBox, int maxZDistance = 10) { collisionBox.Point += entity.Position; if (CanCollideWithEntity(other) && GMath.Abs(entity.ZPosition - other.ZPosition) < maxZDistance) { return(collisionBox.Intersects(other.Physics.PositionedSoftCollisionBox)); } return(false); }
public CollisionTestSettings(Type entityType, Rectangle2F myBox, CollisionBoxType otherBoxType, float maxZDistance = 10) { this.requiredType = entityType; this.collisionBox1 = myBox; this.collisionBoxType1 = CollisionBoxType.Custom; this.collisionBoxType2 = otherBoxType; this.requiredFlags = PhysicsFlags.None; this.maxZDistance = maxZDistance; }
// Translates the rectangle based on the translation and precision settings. private Rectangle NewRect(Rectangle2F destinationRect) { if (useTranslation) { return(UseIntegerPrecision ? (Rectangle) new Rectangle2F(GMath.Round(destinationRect.Point + translation), GMath.Round(destinationRect.Size)) : (Rectangle)(destinationRect + translation)); } else { return((Rectangle)destinationRect); } }
// Return an enumerable list of tiles touching the given rectangular bounds. public IEnumerable <Tile> GetTilesTouching(Rectangle2F bounds, TileLayerOrder layerOrder = TileLayerOrder.LowestToHighest) { Rectangle2I area = GetTileAreaFromRect(bounds); foreach (Tile tile in GetTilesInArea(area, layerOrder)) { if (tile.Bounds.Intersects(bounds)) { yield return(tile); } } }
// inflateAmount inflates the output rectangle. public Rectangle2I GetTileAreaFromRect(Rectangle2F rect, int inflateAmount = 0) { Rectangle2I area; area.Point = (Point2I)GMath.Floor(rect.TopLeft / tileGridCellSize); area.Size = (Point2I)GMath.Ceiling(rect.BottomRight / tileGridCellSize) - area.Point; if (inflateAmount != 0) { area.Inflate(inflateAmount, inflateAmount); } return(Rectangle2I.Intersect(area, new Rectangle2I(Point2I.Zero, gridDimensions))); }
// Translates the rectangle based on the translation and precision settings. private Rectangle NewRect(Vector2F position, Vector2F size) { Rectangle2F destinationRect = new Rectangle2F(position, size); if (useTranslation) { return(UseIntegerPrecision ? (Rectangle) new Rectangle2F(GMath.Round(destinationRect.Point + translation), GMath.Round(destinationRect.Size + translation)) : (Rectangle)(destinationRect + translation)); } else { return((Rectangle)destinationRect); } }
// Return an enumerable list of solid tiles colliding with the given collision box. public IEnumerable <Tile> GetSolidTilesColliding(Rectangle2F collisionBox, TileLayerOrder layerOrder = TileLayerOrder.LowestToHighest) { Rectangle2I area = GetTileAreaFromRect(collisionBox); foreach (Tile tile in GetTilesInArea(area, layerOrder)) { if (tile.IsSolid && tile.CollisionModel != null && CollisionModel.Intersecting(tile.CollisionModel, tile.Position, collisionBox)) { yield return(tile); } } }
public void DrawSprite(Sprite sprite, int variantID, Rectangle2F destination, Color color, float depth = 0.0f) { if (sprite.Image == null) { return; } for (Sprite part = sprite; part != null; part = part.NextPart) { Image image = sprite.Image.GetVariant(variantID); destination.Point = NewPos(destination.Point) + (Vector2F)part.DrawOffset; spriteBatch.Draw(image, (Rectangle)destination, (Rectangle)part.SourceRect, (XnaColor)color, 0.0f, Vector2.Zero, SpriteEffects.None, depth); } }
public bool CanDodgeCollision(Tile tile, int direction) { if (!CanCollideWithTile(tile)) { return(false); } for (int i = 0; i < tile.CollisionModel.Boxes.Count; i++) { if (CanDodgeCollision(Rectangle2F.Translate(tile.CollisionModel.Boxes[i], tile.Position), direction)) { return(true); } } return(false); }
public override void Update() { base.Update(); Player player = RoomControl.Player; int d = ACCEPT_TURN_MIN_DISTANCE_TO_CENTER; Rectangle2F[] sideRects = new Rectangle2F[] { new Rectangle2F(32, 16, d, 16), new Rectangle2F(16, 16 - d, 16, d), new Rectangle2F(16 - d, 16, d, 16), new Rectangle2F(16, 32, 16, d), }; if (player.IsOnGround) { int direction = -1; for (int dir = 0; dir < Directions.Count; dir++) { Rectangle2F sideRect = Rectangle2F.Translate(sideRects[dir], Position); if (player.Physics.PositionedCollisionBox.Intersects(sideRect)) { direction = dir; break; } } if (turnDirection != direction) { timer = 0; turnDirection = direction; } if (turnDirection >= 0) { timer++; if (timer >= ACCEPT_TURN_DELAY) { AudioSystem.PlaySound(GameData.SOUND_CHEST_OPEN); GameControl.PushRoomState(new RoomStateTurnstile(this, direction)); } } } }
private static void DrawTile(Graphics2D g, Tile tile) { if (TileDebugInfoMode == TileDrawInfo.CollisionBoxes) { if (tile.IsSolid && tile.CollisionModel != null) { foreach (Rectangle2F box in tile.CollisionModel.Boxes) { Rectangle2F r = Rectangle2F.Translate(box, tile.Position); g.FillRectangle(r, Color.Red); //g.DrawRectangle(r, 1, Color.Maroon); } } } else if (TileDebugInfoMode == TileDrawInfo.GridArea) { Rectangle2F tileBounds = (Rectangle2F)tile.TileGridArea; tileBounds.Point *= GameSettings.TILE_SIZE; tileBounds.Size *= GameSettings.TILE_SIZE; Color c = Color.Yellow; if (tile.Layer == 1) { c = Color.Blue; } else if (tile.Layer == 2) { c = Color.Red; } g.FillRectangle(tileBounds, c); tileBounds = new Rectangle2F(tile.Position, tile.Size * GameSettings.TILE_SIZE); c = Color.Olive; if (tile.Layer == 1) { c = Color.Cyan; } else if (tile.Layer == 2) { c = Color.Maroon; } g.DrawLine(new Line2F(tileBounds.TopLeft, tileBounds.BottomRight - new Point2I(1, 1)), 1, c); g.DrawLine(new Line2F(tileBounds.TopRight - new Point2I(1, 0), tileBounds.BottomLeft - new Point2I(0, 1)), 1, c); g.DrawRectangle(tileBounds, 1, Color.Black); } }
//----------------------------------------------------------------------------- // Tile Mutators //----------------------------------------------------------------------------- // Place a tile in highest empty layer at the given location. // Returns true if there was an empty space to place the tile. public bool PlaceTileOnHighestLayer(Tile tile, Point2I location) { Rectangle2F tileBounds = new Rectangle2F( location * GameSettings.TILE_SIZE, tile.Size * GameSettings.TILE_SIZE); Rectangle2I area = GetTileAreaFromRect(tileBounds); // Determine which layers are free. bool[] freeLayers = new bool[layerCount]; for (int i = 0; i < layerCount; i++) { freeLayers[i] = true; for (int x = area.Left; x < area.Right && freeLayers[i]; x++) { for (int y = area.Top; y < area.Bottom && freeLayers[i]; y++) { Tile t = tiles[x, y, i]; if (t != null) { freeLayers[i] = false; } } } } // Choose the highest free layer. int layer = -1; for (int i = layerCount - 1; i >= 0; i--) { if (freeLayers[i]) { layer = i; break; } } if (layer < 0) { return(false); } // Place the tile in that layer. PlaceTile(tile, location, layer); return(true); }
public override void Update() { base.Update(); bool isDown = isCovered; // Update the uncovered state. if (!isCovered) { // There is a small delay between being uncovered and becoming unpressed. uncoverTimer--; if (uncoverTimer > 0) { isDown = true; } else { // Raise certain tiles (pots) that are partially covering this button. foreach (Tile tile in tilesCovering) { if (tile.Bounds.Contains(Center) && tile.Properties.GetBoolean("raised_on_buttons", false)) { tile.Graphics.RaisedDrawOffset = new Point2I(0, -GameSettings.TILE_BUTTON_TILE_RAISE_AMOUNT); } } } } // Check if the player is on top of this button. if (!isDown) { Rectangle2F pressRect = Rectangle2F.Translate( GameSettings.TILE_BUTTON_PLAYER_PRESS_AREA, Position); if (pressRect.Contains(RoomControl.Player.Position)) { isDown = true; } } // Check if the pressed state needs to be changed. if (isPressed != isDown && (isDown || isReleasable)) { SetPressed(isDown); } }
// Return a list of tiles colliding with this entity (solidity is ignored). public IEnumerable <Tile> GetTilesMeeting(Vector2F position, CollisionBoxType collisionBoxType) { // Find the rectangular area of nearby tiles to collide with. Rectangle2F myBox = GetCollisionBox(collisionBoxType); myBox.Point += position; myBox.Inflate(2, 2); Rectangle2I area = entity.RoomControl.GetTileAreaFromRect(myBox, 1); // Iterate the tiles in the area. foreach (Tile t in entity.RoomControl.GetTilesInArea(area)) { if (t.CollisionModel != null && CollisionModel.Intersecting(t.CollisionModel, t.Position, collisionBox, position)) { yield return(t); } } }
// Return the highest surface tile at the given position. public Tile GetSurfaceTileAtPosition(Vector2F position, bool includePlatforms = false) { // Because tiles may have moved this frame, we need to check a 3x3 area. Point2I location = GetTileLocation(position); Rectangle2I area = new Rectangle2I(location, Point2I.One); area.Inflate(1, 1); foreach (Tile tile in GetTilesInArea(area, TileLayerOrder.HighestToLowest)) { Rectangle2F tileBounds = tile.Bounds; tileBounds.Point -= tile.Velocity; if (tileBounds.Contains(position) && (tile.IsSurface || (includePlatforms && tile.IsPlatform))) { return(tile); } } return(null); }
private Point2I ledgeTileLocation; // The tile location of the ledge we are currently passing over, or (-1, -1) if not passing over ledge. //----------------------------------------------------------------------------- // Constructors //----------------------------------------------------------------------------- // By default, physics are disabled. public PhysicsComponent(Entity entity) { this.isEnabled = false; this.flags = PhysicsFlags.None; this.entity = entity; this.velocity = Vector2F.Zero; this.zVelocity = 0.0f; this.previousVelocity = Vector2F.Zero; this.previousZVelocity = 0.0f; this.gravity = GameSettings.DEFAULT_GRAVITY; this.maxFallSpeed = GameSettings.DEFAULT_MAX_FALL_SPEED; this.collisionBox = new Rectangle2F(-1, -1, 2, 2); this.softCollisionBox = new Rectangle2F(-1, -1, 2, 2); this.topTile = null; this.isColliding = false; this.autoDodgeDistance = 6; this.autoDodgeSpeed = 1.0f; this.hasLanded = false; this.reboundVelocity = Vector2F.Zero; this.ledgeAltitude = 0; this.ledgeTileLocation = new Point2I(-1, -1); this.roomEdgeCollisionBoxType = CollisionBoxType.Hard; this.crushMaxGapSize = 0; this.edgeClipAmount = 1; this.collisionInfo = new CollisionInfo[Directions.Count]; this.previousCollisionInfo = new CollisionInfo[Directions.Count]; for (int i = 0; i < Directions.Count; i++) { collisionInfo[i].Clear(); previousCollisionInfo[i].Clear(); } this.MovementCollisions = new bool[4]; this.ClipCollisionInfo = new CollisionInfoNew[4]; for (int i = 0; i < 4; i++) { ClipCollisionInfo[i] = new CollisionInfoNew(); } }
// Return the closest solid tile positioned directly in front of the entity (if there is one). public Tile GetFacingSolidTile(int direction, float distance = 1.0f) // TODO: return closest tile. { Rectangle2F entityBox = PositionedCollisionBox; entityBox.ExtendEdge(direction, distance); Tile closestTile = null; int alignAxis = 1 - Directions.ToAxis(direction); float closestDistance = -1.0f; foreach (Tile tile in entity.RoomControl.TileManager.GetTilesTouching(entityBox)) { if (CanCollideWithTile(tile)) { foreach (Rectangle2F box in tile.CollisionModel.Boxes) { Rectangle2F solidBox = box; solidBox.Point += tile.Position; if (entityBox.Intersects(solidBox) && !IsSafeClippingInDirection(solidBox, (direction + 1) % 4) && !IsSafeClippingInDirection(solidBox, (direction + 2) % 4) && !IsSafeClippingInDirection(solidBox, (direction + 3) % 4) && !CanDodgeCollision(solidBox, direction)) { float distToEdge = Math.Max( entityBox.TopLeft[alignAxis] - solidBox.BottomRight[alignAxis], solidBox.TopLeft[alignAxis] - entityBox.BottomRight[alignAxis]); if (closestTile == null || distToEdge < closestDistance) { closestTile = tile; closestDistance = distToEdge; break; } } } } } return(closestTile); }
public override void Update() { // Return to normal if not in a hole. if (!monster.Physics.IsInHole) { monster.BeginNormalState(); return; } // Slip toward the hole's center. float slipSpeed = 0.2f; Point2I tileLocation = monster.RoomControl.GetTileLocation(monster.Position); Vector2F tilePosition = tileLocation * GameSettings.TILE_SIZE; Vector2F tileCenter = tilePosition + new Vector2F(8, 8); Rectangle2F holeRect = new Rectangle2F(tilePosition.X + 6, tilePosition.Y + 8, 4, 6); bool fallInHole = holeRect.Contains(monster.Position); Vector2F trajectory = tileCenter - monster.Center; if (trajectory.Length > slipSpeed) { monster.Physics.Velocity = (tileCenter - monster.Center).Normalized * slipSpeed; } else { fallInHole = true; monster.Physics.Velocity = Vector2F.Zero; monster.SetPositionByCenter(tileCenter); } if (fallInHole) { monster.RoomControl.SpawnEntity(new EffectFallingObject(), monster.Center); AudioSystem.PlaySound(GameData.SOUND_OBJECT_FALL); monster.Destroy(); } }
// Remove a tile from the room. public void RemoveTile(Tile tile) { Rectangle2I area = tile.TileGridArea; for (int x = area.Left; x < area.Right; x++) { for (int y = area.Top; y < area.Bottom; y++) { tiles[x, y, tile.Layer] = null; } } tile.IsAlive = false; tile.OnRemoveFromRoom(); // Check for uncovered tiles. Rectangle2F tileBounds = tile.Bounds; foreach (Tile t in GetTilesTouching(tileBounds)) { t.OnUncoverBegin(tile); t.OnUncoverComplete(tile); } }
/** <summary> Returns the width and height of a string. </summary> */ public Rectangle2F MeasureStringBounds(string text, Align alignment) { Rectangle2F stringBounds = new Rectangle2F(Vector2F.Zero, spriteFont.MeasureString(text)); bool intAlign = (alignment & Align.Int) != 0; if (((alignment & Align.Left) != 0) == ((alignment & Align.Right) != 0)) { stringBounds.X -= (intAlign ? (int)(stringBounds.Width / 2.0f) : (stringBounds.Width / 2.0f)); } else if ((alignment & Align.Right) != 0) { stringBounds.X -= stringBounds.Width; } if (((alignment & Align.Top) != 0) == ((alignment & Align.Bottom) != 0)) { stringBounds.Y -= (intAlign ? (int)(stringBounds.Height / 2.0f) : (stringBounds.Height / 2.0f)); } else if ((alignment & Align.Bottom) != 0) { stringBounds.Y -= stringBounds.Height; } return(stringBounds); }
// Return true if the entity would collide with a solid object using the // given collision box if it were placed at the given position. public bool IsPlaceMeetingSolid(Vector2F position, Rectangle2F collisionBox) { Room room = entity.RoomControl.Room; // Find the rectangular area of nearby tiles to collide with. Rectangle2F myBox = collisionBox; myBox.Point += position; myBox.Inflate(2, 2); int x1 = (int)(myBox.Left / (float)GameSettings.TILE_SIZE); int y1 = (int)(myBox.Top / (float)GameSettings.TILE_SIZE); int x2 = (int)(myBox.Right / (float)GameSettings.TILE_SIZE) + 1; int y2 = (int)(myBox.Bottom / (float)GameSettings.TILE_SIZE) + 1; Rectangle2I area; area.Point = (Point2I)(myBox.TopLeft / (float)GameSettings.TILE_SIZE); area.Size = ((Point2I)(myBox.BottomRight / (float)GameSettings.TILE_SIZE)) + Point2I.One - area.Point; area.Inflate(1, 1); area = Rectangle2I.Intersect(area, new Rectangle2I(Point2I.Zero, room.Size)); myBox.Inflate(-2, -2); foreach (Tile t in entity.RoomControl.GetTilesInArea(area)) { if (CanCollideWithTile(t)) { if (CollisionModel.Intersecting(t.CollisionModel, t.Position, collisionBox, position)) { return(true); } } } return(false); }