/// <summary> /// [GET / SET] The collision shape on a given layer index from zero to (<see cref="LayerCount"/> - 1). /// </summary> /// <param name="layerIndex"></param> /// <returns></returns> public TileCollisionShape this[int layerIndex] { get { switch (layerIndex) { case 0: return this.Layer0; case 1: return this.Layer1; case 2: return this.Layer2; case 3: return this.Layer3; } throw new IndexOutOfRangeException("Invalid collision layer index"); } set { switch (layerIndex) { case 0: this.Layer0 = value; return; case 1: this.Layer1 = value; return; case 2: this.Layer2 = value; return; case 3: this.Layer3 = value; return; } throw new IndexOutOfRangeException("Invalid collision layer index"); } }
/// <summary> /// [GET / SET] The collision shape on a given layer index from zero to (<see cref="LayerCount"/> - 1). /// </summary> /// <param name="layerIndex"></param> /// <returns></returns> public TileCollisionShape this[int layerIndex] { get { switch (layerIndex) { case 0: return(this.Layer0); case 1: return(this.Layer1); case 2: return(this.Layer2); case 3: return(this.Layer3); } throw new IndexOutOfRangeException("Invalid collision layer index"); } set { switch (layerIndex) { case 0: this.Layer0 = value; return; case 1: this.Layer1 = value; return; case 2: this.Layer2 = value; return; case 3: this.Layer3 = value; return; } throw new IndexOutOfRangeException("Invalid collision layer index"); } }
private static void AddFenceCollisionEdges(Grid <TileCollisionShape> collisionData, TileEdgeMap targetEdgeMap) { // Populate the edge map with all the collision fences for (int y = 0; y < SectorSize; y++) { for (int x = 0; x < SectorSize; x++) { TileCollisionShape collision = collisionData[x, y]; // Skip both free and completely solid tiles if (collision == TileCollisionShape.Free) { continue; } if ((collision & TileCollisionShape.Solid) == TileCollisionShape.Solid) { continue; } // Add the various fence collision types if ((collision & TileCollisionShape.Top) != TileCollisionShape.Free) { targetEdgeMap.AddEdge(new Point2(x, y), new Point2(x + 1, y)); } if ((collision & TileCollisionShape.Bottom) != TileCollisionShape.Free) { targetEdgeMap.AddEdge(new Point2(x, y + 1), new Point2(x + 1, y + 1)); } if ((collision & TileCollisionShape.Left) != TileCollisionShape.Free) { targetEdgeMap.AddEdge(new Point2(x, y), new Point2(x, y + 1)); } if ((collision & TileCollisionShape.Right) != TileCollisionShape.Free) { targetEdgeMap.AddEdge(new Point2(x + 1, y), new Point2(x + 1, y + 1)); } if ((collision & TileCollisionShape.DiagonalDown) != TileCollisionShape.Free) { targetEdgeMap.AddEdge(new Point2(x, y), new Point2(x + 1, y + 1)); } if ((collision & TileCollisionShape.DiagonalUp) != TileCollisionShape.Free) { targetEdgeMap.AddEdge(new Point2(x, y + 1), new Point2(x + 1, y)); } } } }
private int MergeCollisionData(int sectorX, int sectorY, Grid <TileCollisionShape> target) { Point2 beginTile = new Point2(sectorX * SectorSize, sectorY * SectorSize); Point2 endTile = new Point2((sectorX + 1) * SectorSize, (sectorY + 1) * SectorSize); endTile.X = Math.Min(endTile.X, this.tileCount.X); endTile.Y = Math.Min(endTile.Y, this.tileCount.Y); TileInfo[][] tileData = GetRawTileData(this.sourceTilemaps); // Start with a non-zero checksum, so it doesn't equal the uninitialized sector checksum int checksum = 1; for (int y = beginTile.Y; y < endTile.Y; y++) { for (int x = beginTile.X; x < endTile.X; x++) { TileCollisionShape mergedCollision = TileCollisionShape.Free; for (int i = 0; i < this.sourceTilemaps.Length; i++) { if (this.sourceTilemaps[i] == null) { continue; } if (tileData[i] == null) { continue; } Tile tile = this.sourceTilemaps[i].Tiles[x, y]; TileCollisionShape collision = tileData[i][tile.Index].Collision[this.source[i].Layers]; mergedCollision |= collision; } target[x - beginTile.X, y - beginTile.Y] = mergedCollision; MathF.CombineHashCode(ref checksum, (int)mergedCollision); } } return(checksum); }
private void PerformUserDrawAction() { Tileset tileset = this.SelectedTileset.Res; if (tileset == null) { return; } int tileIndex = this.TilesetView.HoveredTileIndex; if (tileIndex < 0 || tileIndex > tileset.TileCount) { return; } TileInput input = tileset.TileInput.Count > tileIndex ? tileset.TileInput[tileIndex] : default(TileInput); TileCollisionShape lastCollision = input.Collision[this.editLayerIndex]; if (this.drawMode == CollisionDrawMode.Add) { input.Collision[this.editLayerIndex] |= this.drawShape; } else if (this.drawMode == CollisionDrawMode.Remove) { input.Collision[this.editLayerIndex] &= ~this.drawShape; } else { input.Collision[this.editLayerIndex] = this.drawShape; } TileCollisionShape newCollision = input.Collision[this.editLayerIndex]; if (lastCollision != newCollision) { UndoRedoManager.Do(new EditTilesetTileInputAction(tileset, tileIndex, input)); } }
/// <summary> /// [GET] The collision shape on the specified (set of) layer(s). /// </summary> /// <param name="layerMask"></param> /// <returns></returns> public TileCollisionShape this[TileCollisionLayer layerMask] { get { TileCollisionShape result = TileCollisionShape.Free; if ((layerMask & TileCollisionLayer.Layer0) != TileCollisionLayer.None) { result |= this.Layer0; } if ((layerMask & TileCollisionLayer.Layer1) != TileCollisionLayer.None) { result |= this.Layer1; } if ((layerMask & TileCollisionLayer.Layer2) != TileCollisionLayer.None) { result |= this.Layer2; } if ((layerMask & TileCollisionLayer.Layer3) != TileCollisionLayer.None) { result |= this.Layer3; } return(result); } }
private static void AddBlockCollisionEdges(Grid <TileCollisionShape> collisionData, TileEdgeMap targetEdgeMap, Point2 edgeMapPos, Point2 totalSize) { int leftBorderPos = 0 - edgeMapPos.X; int rightBorderPos = totalSize.X - edgeMapPos.X; int topBorderPos = 0 - edgeMapPos.Y; int bottomBorderPos = totalSize.Y - edgeMapPos.Y; // Add block geometry to the specified edge map for (int y = 0; y < SectorSize; y++) { for (int x = 0; x < SectorSize; x++) { // Skip non-solid blocks bool center = (collisionData[x, y] & TileCollisionShape.Solid) == TileCollisionShape.Solid; if (!center) { continue; } // A filled block will always overwrite its inner diagonal edges targetEdgeMap.RemoveEdge(new Point2(x, y), new Point2(x + 1, y + 1)); targetEdgeMap.RemoveEdge(new Point2(x, y + 1), new Point2(x + 1, y)); // Determine block collision neighbourhood bool left = (x == 0) ? (x == leftBorderPos) : (collisionData[x - 1, y] & TileCollisionShape.Solid) == TileCollisionShape.Solid; bool right = (x == SectorSize - 1) ? (x == rightBorderPos) : (collisionData[x + 1, y] & TileCollisionShape.Solid) == TileCollisionShape.Solid; bool top = (y == 0) ? (y == topBorderPos) : (collisionData[x, y - 1] & TileCollisionShape.Solid) == TileCollisionShape.Solid; bool bottom = (y == SectorSize - 1) ? (y == bottomBorderPos) : (collisionData[x, y + 1] & TileCollisionShape.Solid) == TileCollisionShape.Solid; // Adjust outer edge states if (center != left) { targetEdgeMap.AddEdge(new Point2(x, y), new Point2(x, y + 1)); } else { targetEdgeMap.RemoveEdge(new Point2(x, y), new Point2(x, y + 1)); } if (center != right) { targetEdgeMap.AddEdge(new Point2(x + 1, y), new Point2(x + 1, y + 1)); } else { targetEdgeMap.RemoveEdge(new Point2(x + 1, y), new Point2(x + 1, y + 1)); } if (center != top) { targetEdgeMap.AddEdge(new Point2(x, y), new Point2(x + 1, y)); } else { targetEdgeMap.RemoveEdge(new Point2(x, y), new Point2(x + 1, y)); } if (center != bottom) { targetEdgeMap.AddEdge(new Point2(x, y + 1), new Point2(x + 1, y + 1)); } else { targetEdgeMap.RemoveEdge(new Point2(x, y + 1), new Point2(x + 1, y + 1)); } } } // Detect diagonal fences next to solid blocks and remove the // edges that might have become redundant. This can't be done // in the above loop without complicating control flow, so it's // done here. for (int y = 0; y < SectorSize; y++) { for (int x = 0; x < SectorSize; x++) { TileCollisionShape centerShape = collisionData[x, y]; bool diagonalDown = (centerShape & TileCollisionShape.DiagonalDown) == TileCollisionShape.DiagonalDown; bool diagonalUp = (centerShape & TileCollisionShape.DiagonalUp) == TileCollisionShape.DiagonalUp; // Skip tiles that aren't diagonal fences if (!diagonalDown && !diagonalUp) { continue; } // Determine block collision neighbourhood bool left = (x == 0) ? (x == leftBorderPos) : (collisionData[x - 1, y] & TileCollisionShape.Solid) == TileCollisionShape.Solid; bool right = (x == SectorSize - 1) ? (x == rightBorderPos) : (collisionData[x + 1, y] & TileCollisionShape.Solid) == TileCollisionShape.Solid; bool top = (y == 0) ? (y == topBorderPos) : (collisionData[x, y - 1] & TileCollisionShape.Solid) == TileCollisionShape.Solid; bool bottom = (y == SectorSize - 1) ? (y == bottomBorderPos) : (collisionData[x, y + 1] & TileCollisionShape.Solid) == TileCollisionShape.Solid; // Remove perpendicular edges that are redundant because of the diagonal fence // connecting two adjacent solid blocks. if (diagonalDown) { if (top && right) { targetEdgeMap.RemoveEdge(new Point2(x, y), new Point2(x + 1, y)); targetEdgeMap.RemoveEdge(new Point2(x + 1, y), new Point2(x + 1, y + 1)); } if (bottom && left) { targetEdgeMap.RemoveEdge(new Point2(x, y + 1), new Point2(x + 1, y + 1)); targetEdgeMap.RemoveEdge(new Point2(x, y), new Point2(x, y + 1)); } } else { if (top && left) { targetEdgeMap.RemoveEdge(new Point2(x, y), new Point2(x + 1, y)); targetEdgeMap.RemoveEdge(new Point2(x, y), new Point2(x, y + 1)); } if (bottom && right) { targetEdgeMap.RemoveEdge(new Point2(x, y + 1), new Point2(x + 1, y + 1)); targetEdgeMap.RemoveEdge(new Point2(x + 1, y), new Point2(x + 1, y + 1)); } } } } }
private void TilesetView_MouseDown(object sender, MouseEventArgs e) { Tileset tileset = this.SelectedTileset.Res; if (tileset == null) { return; } int tileIndex = this.TilesetView.HoveredTileIndex; if (tileIndex < 0 || tileIndex > tileset.TileCount) { return; } // Conditional toggle operation on left click if (e.Button == MouseButtons.Left) { TileInput input = tileset.TileInput.Count > tileIndex ? tileset.TileInput[tileIndex] : default(TileInput); TileCollisionShape collision = input.Collision[this.editLayerIndex]; this.drawSimple = false; switch (this.hoveredArea) { case TileHotSpot.Left: this.drawShape = TileCollisionShape.Left; this.drawMode = !collision.HasFlag(TileCollisionShape.Left) ? CollisionDrawMode.Add : CollisionDrawMode.Remove; break; case TileHotSpot.Right: this.drawShape = TileCollisionShape.Right; this.drawMode = !collision.HasFlag(TileCollisionShape.Right) ? CollisionDrawMode.Add : CollisionDrawMode.Remove; break; case TileHotSpot.Top: this.drawShape = TileCollisionShape.Top; this.drawMode = !collision.HasFlag(TileCollisionShape.Top) ? CollisionDrawMode.Add : CollisionDrawMode.Remove; break; case TileHotSpot.Bottom: this.drawShape = TileCollisionShape.Bottom; this.drawMode = !collision.HasFlag(TileCollisionShape.Bottom) ? CollisionDrawMode.Add : CollisionDrawMode.Remove; break; default: if (collision == TileCollisionShape.Free) { this.drawSimple = true; this.drawMode = CollisionDrawMode.Set; this.drawShape = TileCollisionShape.Solid; } else if (collision == TileCollisionShape.Solid) { this.drawMode = CollisionDrawMode.Set; this.drawShape = TileCollisionShape.DiagonalUp; } else if (collision.HasFlag(TileCollisionShape.DiagonalUp)) { this.drawMode = CollisionDrawMode.Set; this.drawShape = TileCollisionShape.DiagonalDown; } else if (collision.HasFlag(TileCollisionShape.DiagonalDown)) { this.drawMode = CollisionDrawMode.Set; this.drawShape = TileCollisionShape.Free; } else { this.drawMode = CollisionDrawMode.Set; this.drawShape = TileCollisionShape.Solid; } break; } this.isUserDrawing = true; } // Clear operation on right click else if (e.Button == MouseButtons.Right) { this.drawSimple = true; this.drawShape = TileCollisionShape.Free; this.drawMode = CollisionDrawMode.Set; this.isUserDrawing = true; } // Perform the drawing operation this.PerformUserDrawAction(); this.TilesetView.InvalidateTile(tileIndex, 0); }
private void TilesetView_PaintTiles(object sender, TilesetViewPaintTilesEventArgs e) { Color colorFree = Color.White; Color colorCollision = Color.FromArgb(128, 192, 255); TileInput[] tileInput = e.Tileset.TileInput.Data; for (int i = 0; i < e.PaintedTiles.Count; i++) { TilesetViewPaintTileData paintData = e.PaintedTiles[i]; // Prepare some data we'll need for drawing the tile collision info overlay TileCollisionShape collision = TileCollisionShape.Free; if (tileInput.Length > paintData.TileIndex) { collision = tileInput[paintData.TileIndex].Collision[this.editLayerIndex]; } bool tileHovered = this.TilesetView.HoveredTileIndex == paintData.TileIndex; bool simpleCollision = collision == TileCollisionShape.Solid || collision == TileCollisionShape.Free; // Draw the center icon indicating the tiles simple solid / free state, as well as diagonal slopes { bool centerIsCollision = collision == TileCollisionShape.Solid || collision.HasFlag(TileCollisionShape.DiagonalUp) || collision.HasFlag(TileCollisionShape.DiagonalDown); Bitmap centerImage; if (collision == TileCollisionShape.Solid) { centerImage = TilemapsResCache.TilesetCollisionBit; } else if (collision.HasFlag(TileCollisionShape.DiagonalUp)) { centerImage = TilemapsResCache.TilesetCollisionDiagUp; } else if (collision.HasFlag(TileCollisionShape.DiagonalDown)) { centerImage = TilemapsResCache.TilesetCollisionDiagDown; } else { centerImage = TilemapsResCache.TilesetCollisionBit; } Color centerColor; if (centerIsCollision) { centerColor = colorCollision; } else { centerColor = colorFree; } e.Graphics.DrawImageTint( centerImage, Color.FromArgb(GetCollisionIconAlpha( tileHovered, this.hoveredArea == TileHotSpot.Center, centerIsCollision), centerColor), paintData.ViewRect.X + (paintData.ViewRect.Width - centerImage.Width) / 2, paintData.ViewRect.Y + (paintData.ViewRect.Height - centerImage.Height) / 2); } // Draw collision icons for specific directional passability. if (!simpleCollision || (tileHovered && (!this.isUserDrawing || !this.drawSimple))) { e.Graphics.DrawImageTint( TilemapsResCache.TilesetCollisionVertical, Color.FromArgb(GetCollisionIconAlpha( tileHovered, this.hoveredArea == TileHotSpot.Right, !simpleCollision && collision.HasFlag(TileCollisionShape.Right)), collision.HasFlag(TileCollisionShape.Right) ? colorCollision : colorFree), paintData.ViewRect.X + paintData.ViewRect.Width - TilemapsResCache.TilesetCollisionVertical.Width - 1, paintData.ViewRect.Y + (paintData.ViewRect.Height - TilemapsResCache.TilesetCollisionVertical.Height) / 2); e.Graphics.DrawImageTint( TilemapsResCache.TilesetCollisionVertical, Color.FromArgb(GetCollisionIconAlpha( tileHovered, this.hoveredArea == TileHotSpot.Left, !simpleCollision && collision.HasFlag(TileCollisionShape.Left)), collision.HasFlag(TileCollisionShape.Left) ? colorCollision : colorFree), paintData.ViewRect.X + 1, paintData.ViewRect.Y + (paintData.ViewRect.Height - TilemapsResCache.TilesetCollisionVertical.Height) / 2); e.Graphics.DrawImageTint( TilemapsResCache.TilesetCollisionHorizontal, Color.FromArgb(GetCollisionIconAlpha( tileHovered, this.hoveredArea == TileHotSpot.Top, !simpleCollision && collision.HasFlag(TileCollisionShape.Top)), collision.HasFlag(TileCollisionShape.Top) ? colorCollision : colorFree), paintData.ViewRect.X + (paintData.ViewRect.Width - TilemapsResCache.TilesetCollisionHorizontal.Width) / 2, paintData.ViewRect.Y + 1); e.Graphics.DrawImageTint( TilemapsResCache.TilesetCollisionHorizontal, Color.FromArgb(GetCollisionIconAlpha( tileHovered, this.hoveredArea == TileHotSpot.Bottom, !simpleCollision && collision.HasFlag(TileCollisionShape.Bottom)), collision.HasFlag(TileCollisionShape.Bottom) ? colorCollision : colorFree), paintData.ViewRect.X + (paintData.ViewRect.Width - TilemapsResCache.TilesetCollisionHorizontal.Width) / 2, paintData.ViewRect.Y + paintData.ViewRect.Height - TilemapsResCache.TilesetCollisionHorizontal.Height - 1); } } }
private static bool IsSolid(TileCollisionShape tileCollisionShape) { return(tileCollisionShape == TileCollisionShape.Solid || tileCollisionShape == TileCollisionShape.DiagonalDown || tileCollisionShape == TileCollisionShape.DiagonalUp); }
private void TilesetView_MouseDown(object sender, MouseEventArgs e) { Tileset tileset = this.SelectedTileset.Res; if (tileset == null) return; int tileIndex = this.TilesetView.HoveredTileIndex; if (tileIndex < 0 || tileIndex > tileset.TileCount) return; // Conditional toggle operation on left click if (e.Button == MouseButtons.Left) { TileInput input = tileset.TileInput.Count > tileIndex ? tileset.TileInput[tileIndex] : default(TileInput); TileCollisionShape collision = input.Collision[this.editLayerIndex]; this.drawSimple = false; switch (this.hoveredArea) { case TileHotSpot.Left: this.drawShape = TileCollisionShape.Left; this.drawMode = !collision.HasFlag(TileCollisionShape.Left) ? CollisionDrawMode.Add : CollisionDrawMode.Remove; break; case TileHotSpot.Right: this.drawShape = TileCollisionShape.Right; this.drawMode = !collision.HasFlag(TileCollisionShape.Right) ? CollisionDrawMode.Add : CollisionDrawMode.Remove; break; case TileHotSpot.Top: this.drawShape = TileCollisionShape.Top; this.drawMode = !collision.HasFlag(TileCollisionShape.Top) ? CollisionDrawMode.Add : CollisionDrawMode.Remove; break; case TileHotSpot.Bottom: this.drawShape = TileCollisionShape.Bottom; this.drawMode = !collision.HasFlag(TileCollisionShape.Bottom) ? CollisionDrawMode.Add : CollisionDrawMode.Remove; break; default: if (collision == TileCollisionShape.Free) { this.drawSimple = true; this.drawMode = CollisionDrawMode.Set; this.drawShape = TileCollisionShape.Solid; } else if (collision == TileCollisionShape.Solid) { this.drawMode = CollisionDrawMode.Set; this.drawShape = TileCollisionShape.DiagonalUp; } else if (collision.HasFlag(TileCollisionShape.DiagonalUp)) { this.drawMode = CollisionDrawMode.Set; this.drawShape = TileCollisionShape.DiagonalDown; } else if (collision.HasFlag(TileCollisionShape.DiagonalDown)) { this.drawMode = CollisionDrawMode.Set; this.drawShape = TileCollisionShape.Free; } else { this.drawMode = CollisionDrawMode.Set; this.drawShape = TileCollisionShape.Solid; } break; } this.isUserDrawing = true; } // Clear operation on right click else if (e.Button == MouseButtons.Right) { this.drawSimple = true; this.drawShape = TileCollisionShape.Free; this.drawMode = CollisionDrawMode.Set; this.isUserDrawing = true; } // Perform the drawing operation this.PerformUserDrawAction(); this.TilesetView.InvalidateTile(tileIndex, 0); }