// Replaces "CheckTile" in modded TileHandler method. private bool TryGetTile(patch_TerrainType set, VirtualMap <char> mapData, int x, int y, Rectangle forceFill, char forceID, Behaviour behaviour, out char tile) { tile = '0'; if (forceFill.Contains(x, y)) { tile = forceID; return(true); } if (mapData == null) // Not entirely sure how this should be handled best. { return(behaviour.EdgesExtend); } if (x >= 0 && y >= 0 && x < mapData.Columns && y < mapData.Rows) { tile = mapData[x, y]; return(!IsEmpty(tile) && !set.Ignore(tile)); } if (!behaviour.EdgesExtend) { return(false); } tile = mapData[Calc.Clamp(x, 0, mapData.Columns - 1), Calc.Clamp(y, 0, mapData.Rows - 1)]; return(!IsEmpty(tile) && !set.Ignore(tile)); }
private void ReadInto(patch_TerrainType data, Tileset tileset, XmlElement xml) { orig_ReadInto(data, tileset, xml); if (xml.HasAttr("sound")) { SurfaceIndex.TileToIndex[xml.AttrChar("id")] = xml.AttrInt("sound"); } }
private int GetDepth(patch_TerrainType terrainType, VirtualMap <char> mapData, int x, int y, Rectangle forceFill, Behaviour behaviour, int depth) { int searchX = depth + terrainType.ScanWidth / 2; int searchY = depth + terrainType.ScanHeight / 2; if (CheckCross(terrainType, mapData, x, y, forceFill, behaviour, searchX, searchY) && depth < terrainType.CustomFills.Count) { return(GetDepth(terrainType, mapData, x, y, forceFill, behaviour, ++depth)); } return(depth); }
private bool CheckCross(patch_TerrainType terrainType, VirtualMap <char> mapData, int x, int y, Rectangle forceFill, Behaviour behaviour, int width, int height) { if (behaviour.PaddingIgnoreOutOfLevel) { return((CheckTile(terrainType, mapData, x - width, y, forceFill, behaviour) || !CheckForSameLevel(x, y, x - width, y)) && (CheckTile(terrainType, mapData, x + width, y, forceFill, behaviour) || !CheckForSameLevel(x, y, x + width, y)) && (CheckTile(terrainType, mapData, x, y - height, forceFill, behaviour) || !CheckForSameLevel(x, y, x, y - height)) && (CheckTile(terrainType, mapData, x, y + height, forceFill, behaviour) || !CheckForSameLevel(x, y, x, y + height))); } else { return(CheckTile(terrainType, mapData, x - width, y, forceFill, behaviour) && CheckTile(terrainType, mapData, x + width, y, forceFill, behaviour) && CheckTile(terrainType, mapData, x, y - height, forceFill, behaviour) && CheckTile(terrainType, mapData, x, y + height, forceFill, behaviour)); } }
private extern void orig_ReadInto(patch_TerrainType data, Tileset tileset, XmlElement xml);
private void ReadInto(patch_TerrainType data, Tileset tileset, XmlElement xml) { if (xml.HasAttr("scanWidth")) { int scanWidth = xml.AttrInt("scanWidth"); if (scanWidth <= 0 || scanWidth % 2 == 0) { throw new Exception("Tileset scan width must be a positive, odd integer."); } data.ScanWidth = scanWidth; } else { data.ScanWidth = 3; } if (xml.HasAttr("scanHeight")) { int scanHeight = xml.AttrInt("scanHeight"); if (scanHeight <= 0 || scanHeight % 2 == 0) { throw new Exception("Tileset scan height must be a positive, odd integer."); } data.ScanHeight = scanHeight; } else { data.ScanHeight = 3; } XmlNodeList fills = xml.SelectNodes("set[starts-with(@mask, 'fill')]"); // Faster to ask for an attr on the tileset node, but this is cleaner & easier to use. if (fills != null && fills.Count > 0) { data.CustomFills = new List <patch_Tiles>(); for (int i = 0; i < fills.Count; i++) { data.CustomFills.Add(new patch_Tiles()); } } if (data.CustomFills == null && data.ScanWidth == 3 && data.ScanHeight == 3 && !xml.HasChild("define")) // ReadIntoCustomTemplate can handle vanilla templates but meh { orig_ReadInto(data, tileset, xml); } else { Logger.Log(LogLevel.Debug, "Autotiler", $"Reading Tileset width scan height {data.ScanHeight} and scan width {data.ScanWidth}."); ReadIntoCustomTemplate(data, tileset, xml); } if (xml.HasAttr("sound")) { SurfaceIndex.TileToIndex[xml.AttrChar("id")] = xml.AttrInt("sound"); } if (xml.HasAttr("debris")) { data.Debris = xml.Attr("debris"); } }
private extern bool CheckTile(patch_TerrainType set, VirtualMap <char> mapData, int x, int y, Rectangle forceFill, Behaviour behaviour);
private void ReadIntoCustomTemplate(patch_TerrainType data, Tileset tileset, XmlElement xml) { foreach (XmlNode child in xml) { if (child is XmlElement node) { if (node.Name == "set") // Potential somewhat breaking change, although there is no reason for another name to have been used. { string text = node.Attr("mask"); patch_Tiles tiles; if (text == "center") { if (data.CustomFills != null) { Logger.Log(LogLevel.Warn, "Autotiler", "\"Center\" tiles will not be used if Custom Fills are present."); } tiles = data.Center; } else if (text == "padding") { if (data.CustomFills != null) { Logger.Log(LogLevel.Warn, "Autotiler", "\"Padding\" tiles will not be used if Custom Fills are present."); } tiles = data.Padded; } else if (text.StartsWith("fill")) { tiles = data.CustomFills[int.Parse(text.Substring(4))]; } else { patch_Masked masked = new patch_Masked(); masked.Mask = new byte[data.ScanWidth * data.ScanHeight]; tiles = masked.Tiles; try { // Allows for spacer characters like '-' in the xml int i = 0; foreach (char c in text) { switch (c) { case '0': masked.Mask[i++] = 0; // No tile break; case '1': masked.Mask[i++] = 1; // Tile break; case 'x': case 'X': masked.Mask[i++] = 2; // Any break; case 'y': case 'Y': masked.Mask[i++] = 3; // Not this tile break; case 'z': case 'Z': break; // Reserved default: // Custom filters if (char.IsLetter(c)) { masked.Mask[i++] = GetByteLookup(c); } break; /* * Error handling for characters that don't exist in a defined filter could be added, * but is slightly more likely to break old custom tilesets if someone has defined a mask that containes nonstandard spacers (usually '-') */ } } } catch (IndexOutOfRangeException e) { throw new IndexOutOfRangeException($"Mask size in tileset with id '{data.ID}' is greater than the size specified by scanWidth and scanHeight (defaults to 3x3).", e); } data.Masked.Add(masked); } foreach (string tile in node.Attr("tiles").Split(';')) { string[] subtexture = tile.Split(','); int x = int.Parse(subtexture[0]); int y = int.Parse(subtexture[1]); try { tiles.Textures.Add(tileset[x, y]); } catch (IndexOutOfRangeException e) { throw new IndexOutOfRangeException($"Tileset with id '{data.ID}' missing tile at ({x}, {y}).", e); } } if (node.HasAttr("sprites")) { foreach (string sprites in node.Attr("sprites").Split(',')) { tiles.OverlapSprites.Add(sprites); } tiles.HasOverlays = true; } } else if (node.Name == "define") { byte id = GetByteLookup(node.AttrChar("id")); string filter = node.Attr("filter"); if (node.AttrBool("ignore")) { data.blacklists[id] = filter; } else { data.whitelists[id] = filter; } } } } data.Masked.Sort((patch_Masked a, patch_Masked b) => { // Sorts the masks to give preference to more specific masks. // Order is Custom Filters -> "Not This" -> "Any" -> Everything else int aFilters = 0; int bFilters = 0; int aNots = 0; int bNots = 0; int aAnys = 0; int bAnys = 0; for (int i = 0; i < data.ScanWidth * data.ScanHeight; i++) { if (a.Mask[i] >= 10) { aFilters++; } if (b.Mask[i] >= 10) { bFilters++; } if (a.Mask[i] == 3) { aNots++; } if (b.Mask[i] == 3) { bNots++; } if (a.Mask[i] == 2) { aAnys++; } if (b.Mask[i] == 2) { bAnys++; } } if (aFilters > 0 || bFilters > 0) { return(aFilters - bFilters); } if (aNots > 0 || bNots > 0) { return(aNots - bNots); } return(aAnys - bAnys); }); }