/// <summary> /// This constructor is used when creating a new world from scratch. /// </summary> /// <param name="minTile"></param> /// <param name="maxTile"></param> /// <param name="minHeight"></param> /// <param name="maxHeight"></param> /// <param name="defaultHeight"></param> public WorldMap(string worldName, CoordXZ minTile, CoordXZ maxTile, float minHeight, float maxHeight, float defaultHeight) { this.worldName = worldName; this.minTile = minTile; this.maxTile = maxTile; this.minHeight = minHeight; this.maxHeight = maxHeight; emptyOWP = new List <IObjectWithProperties>(); layerAndWorldOWP = new List <IObjectWithProperties>(); properties = new MapProperties(this); layers = new Dictionary <string, MapLayer>(); InitLayers(defaultHeight); // create map sections sections = new Dictionary <CoordXZ, MapSection>(); zones = new Dictionary <string, MapZone>(); CoordXZ minSection = new CoordXZ(minTile, sectionSize); CoordXZ maxSection = new CoordXZ(maxTile, sectionSize); for (int z = minSection.z; z <= maxSection.z; z++) { for (int x = minSection.x; x <= maxSection.x; x++) { CoordXZ sectionCoord = new CoordXZ(x, z, sectionSize); sections[sectionCoord] = new MapSection(this, sectionCoord); } } }
/// <summary> /// Copy a source buffer into the tile. The buffer must fit within a single /// tile. /// </summary> /// <param name="destCoordIn"></param> /// <param name="src"></param> public void CopyIn(CoordXZ destCoordIn, MapBuffer src) { Debug.Assert(metersPerSample == src.MetersPerSample); // convert to tile coordinates for this layer CoordXZ destTileCoord = new CoordXZ(destCoordIn, tileSize); // compute the offset in world coordinates within the CoordXZ worldDestCoord = new CoordXZ(destCoordIn, WorldMap.oneMeter); CoordXZ worldDestTileCoord = new CoordXZ(destTileCoord, WorldMap.oneMeter); CoordXZ tileOffset = worldDestCoord - worldDestTileCoord; if (TileExists(destTileCoord)) { // load the tile if necessary if (!TileLoaded(destTileCoord)) { LoadTile(destTileCoord); } } else { // If the tile doesn't exist, then create it. CreateTile(destTileCoord); } // copy the source image into the tile tiles[destTileCoord].Copy(tileOffset.x / metersPerSample, tileOffset.z / metersPerSample, src); }
/// <summary> /// This constructor is used when creating a new world from scratch. /// </summary> /// <param name="minTile"></param> /// <param name="maxTile"></param> /// <param name="minHeight"></param> /// <param name="maxHeight"></param> /// <param name="defaultHeight"></param> public WorldMap(string worldName, CoordXZ minTile, CoordXZ maxTile, float minHeight, float maxHeight, float defaultHeight) { this.worldName = worldName; this.minTile = minTile; this.maxTile = maxTile; this.minHeight = minHeight; this.maxHeight = maxHeight; emptyOWP = new List<IObjectWithProperties>(); layerAndWorldOWP = new List<IObjectWithProperties>(); properties = new MapProperties(this); layers = new Dictionary<string, MapLayer>(); InitLayers(defaultHeight); // create map sections sections = new Dictionary<CoordXZ, MapSection>(); zones = new Dictionary<string, MapZone>(); CoordXZ minSection = new CoordXZ(minTile, sectionSize); CoordXZ maxSection = new CoordXZ(maxTile, sectionSize); for (int z = minSection.z; z <= maxSection.z; z++) { for (int x = minSection.x; x <= maxSection.x; x++) { CoordXZ sectionCoord = new CoordXZ(x, z, sectionSize); sections[sectionCoord] = new MapSection(this, sectionCoord); } } }
protected void UpdateVisible() { if (worldMap != null) { MapLayer heightFieldLayer = worldMap.HeightFieldLayer; for (int z = minVisibleTile.z; z <= maxVisibleTile.z; z++) { for (int x = minVisibleTile.x; x < maxVisibleTile.x; x++) { CoordXZ tileCoord = new CoordXZ(x, z, WorldMap.tileSize); MapTile tile = worldMap.GetTile(tileCoord); if (tile != null) { ImageGridCell cell = imageGrid.GetCell(x - gridOffset.x, z - gridOffset.z); if (cell == null) { cell = imageGrid.CreateCell(x - gridOffset.x, z - gridOffset.z); } if (cell.Image == null) { cell.Image = currentViewLayer.CreateThumbnail(tileCoord, worldMap.TileSize, imageGrid.CellSize); } } } } } }
public void LoadTile(CoordXZ tileCoord) { if (!TileLoaded(tileCoord)) { tiles[tileCoord] = LoadTileImpl(tileCoord); } }
/// <summary> /// Create a new tile and fill it with the default value for the layer /// </summary> /// <param name="tileCoord"></param> /// <returns></returns> protected override MapBuffer CreateTileImpl(CoordXZ tileCoord) { MapBuffer16 buffer = new MapBuffer16(map, samplesPerTile, metersPerSample); buffer.Fill(defaultRawValue); return(buffer); }
/// <summary> /// Create a new tile and fill it with the default value for the layer /// </summary> /// <param name="tileCoord"></param> /// <returns></returns> protected override MapBuffer CreateTileImpl(CoordXZ tileCoord) { MapBuffer buffer = new MapBufferARGB(map, samplesPerTile, metersPerSample); buffer.Fill((uint)defaultColor.ToARGB()); return(buffer); }
public MapTile(WorldMap map, CoordXZ tileCoord) { this.map = map; this.tileCoord = tileCoord; zone = null; this.dirty = false; properties = new MapProperties(this); }
public MapTile GetTile(CoordXZ tileCoord) { // compute coordinates of tile within the section CoordXZ localTileCoord = tileCoord - this.tileCoord; MapTile tile = tiles[localTileCoord.x, localTileCoord.z]; return(tile); }
public MapSection GetSection(CoordXZ tileCoord) { CoordXZ sectionCoord = new CoordXZ(tileCoord, sectionSize); if (!sections.ContainsKey(sectionCoord)) { sections[sectionCoord] = new MapSection(this, sectionCoord, worldPath); } return(sections[sectionCoord]); }
/// <summary> /// This constructor is used when creating a new map. All tiles are null, since /// this is a blank world. /// </summary> /// <param name="map">the map</param> /// <param name="sectionCoord">the section coordinate of this object</param> public MapSection(WorldMap map, CoordXZ sectionCoord) { this.sectionCoord = sectionCoord; this.map = map; tileCoord = new CoordXZ(sectionCoord, map.TileSize); dirty = true; dirtyChildren = false; tiles = new MapTile[map.TilesPerSection, map.TilesPerSection]; }
public MapTile CreateTile(CoordXZ tileCoord) { MapTile tile = new MapTile(this, tileCoord); MapSection section = GetSection(tileCoord); section.AddTile(tile); OnNewTile(tile); return(tile); }
public void Flush() { foreach (KeyValuePair <CoordXZ, MapBuffer> kvp in tiles) { CoordXZ tileCoord = kvp.Key; MapBuffer buffer = kvp.Value; if ((buffer != null) && buffer.Dirty) { buffer.Save(TilePath(tileCoord)); } } }
public bool TileExists(CoordXZ tileCoord) { if (tiles.ContainsKey(tileCoord)) { return(tiles[tileCoord] != null); } else if (System.IO.File.Exists(TilePath(tileCoord))) { return(true); } return(false); }
/// <summary> /// Compute the tile coordinate and sample offset within the tile of a given coordinate /// </summary> /// <param name="coordIn">The input coordinate</param> /// <param name="tileCoord">The tile coordinate</param> /// <param name="sampleOffset">Sample offset within the tile</param> /// <returns>Whether the input coordinate exactly hits a sample</returns> protected bool CoordToTileOffset(CoordXZ coordIn, out CoordXZ tileCoord, out CoordXZ sampleOffset) { int sampleSize = metersPerSample * map.OneMeter; tileCoord = new CoordXZ(coordIn, tileSize); CoordXZ sampleCoord = new CoordXZ(coordIn, sampleSize); CoordXZ tileSampleCoord = new CoordXZ(tileCoord, sampleSize); sampleOffset = sampleCoord - tileSampleCoord; CoordXZ worldIn = new CoordXZ(coordIn, 1); CoordXZ worldSample = new CoordXZ(sampleCoord, 1); return(worldIn == worldSample); }
private void newMapToolStripMenuItem_Click(object sender, EventArgs e) { using (NewMapDialog dlg = new NewMapDialog()) { DialogResult result; result = dlg.ShowDialog(); if (result != DialogResult.OK) { return; } int tw = (dlg.MapWidth + WorldMap.tileSize - 1) / WorldMap.tileSize; int th = (dlg.MapHeight + WorldMap.tileSize - 1) / WorldMap.tileSize; imageGrid.WidthCells = tw; imageGrid.HeightCells = th; imageGrid.Enabled = true; int tx, tz; if ((tw <= WorldMap.tilesPerSection) && (th <= WorldMap.tilesPerSection)) { // if the world fits in a single section, then center it in section 0,0 tx = (WorldMap.tilesPerSection - tw) / 2; tz = (WorldMap.tilesPerSection - th) / 2; } else { // if the world doesn't fit in a single section, then center on the origin tx = -tw / 2; tz = -th / 2; } CoordXZ minTile = new CoordXZ(tx, tz, WorldMap.tileSize); CoordXZ maxTile = new CoordXZ(tx + tw - 1, tz + th - 1, WorldMap.tileSize); worldMap = new WorldMap(dlg.MapName, minTile, maxTile, dlg.MinTerrainHeight, dlg.MaxTerrainHeight, dlg.DefaultTerrainHeight); currentViewLayer = worldMap.GetLayer("heightfield"); gridOffset = minTile; InitTreeView(); } }
public void UnloadTile(CoordXZ tileCoord) { if (TileLoaded(tileCoord)) { MapBuffer buffer = tiles[tileCoord]; if (buffer != null) { if (buffer.Dirty) { buffer.Save(TilePath(tileCoord)); } } tiles.Remove(tileCoord); } }
/// <summary> /// Add a new tile to the section /// </summary> /// <param name="tile"></param> public void AddTile(MapTile tile) { // compute coordinates of tile within the section CoordXZ localTileCoord = tile.TileCoord - tileCoord; if (tiles[localTileCoord.x, localTileCoord.z] == null) { tiles[localTileCoord.x, localTileCoord.z] = tile; } else { throw new ArgumentException("AddTile: tile already exists at given coordinate"); } if (tile.Dirty) { dirtyChildren = true; } }
/// <summary> /// Create a thumbnail bitmap of the map area /// </summary> /// <param name="coord"></param> /// <param name="worldSize"></param> /// <param name="pixelSize"></param> /// <returns></returns> public System.Drawing.Bitmap CreateThumbnail(CoordXZ coord, int worldSize, int pixelSize) { int numSamples = worldSize / (metersPerSample * map.OneMeter); Debug.Assert(WorldMap.IsPowerOf2(numSamples)); Debug.Assert(WorldMap.IsPowerOf2(pixelSize)); CoordXZ tileCoord; CoordXZ sampleOffset; bool exact = CoordToTileOffset(coord, out tileCoord, out sampleOffset); // make sure that coordinate is an exact sample Debug.Assert(exact); if (!TileLoaded(tileCoord)) { LoadTile(tileCoord); } MapBuffer tile = tiles[tileCoord]; return(tile.CreateThumbnail(sampleOffset.x, sampleOffset.z, worldSize / (metersPerSample * map.OneMeter), pixelSize)); }
public void LoadMap() { using (OpenFileDialog dlg = new OpenFileDialog()) { dlg.Title = "Load Map"; dlg.DefaultExt = "mwm"; dlg.Filter = "Multiverse World Map files (*.mwm)|*.mwm|All files (*.*)|*.*"; dlg.RestoreDirectory = true; if (dlg.ShowDialog() == DialogResult.OK) { worldMap = new WorldMap(dlg.FileName); currentViewLayer = worldMap.GetLayer("heightfield"); gridOffset = worldMap.MinTile; imageGrid.WidthCells = worldMap.MaxTile.x - worldMap.MinTile.x + 1; imageGrid.HeightCells = worldMap.MaxTile.z - worldMap.MinTile.z + 1; imageGrid.Enabled = true; InitTreeView(); } } }
/// <summary> /// Create a new tile and fill it with the default value for the layer /// </summary> /// <param name="tileCoord"></param> /// <returns></returns> protected override MapBuffer CreateTileImpl(CoordXZ tileCoord) { MapBuffer16 buffer = new MapBuffer16(map, samplesPerTile, metersPerSample); buffer.Fill(defaultRawValue); return buffer; }
protected void loadLayerHandler(object sender, EventArgs args) { ToolStripButton button = sender as ToolStripButton; MapZone zone = button.Tag as MapZone; using (LoadLayerDialog dlg = new LoadLayerDialog()) { DialogResult result; dlg.LayerNames = worldMap.LayerNames; result = dlg.ShowDialog(); if (result != DialogResult.OK) { return; } int tilesWidth = zone.MaxTileCoord.x - zone.MinTileCoord.x + 1; int tilesHeight = zone.MaxTileCoord.z - zone.MinTileCoord.z + 1; int zoneWidthMeters = tilesWidth * worldMap.TileSize / worldMap.OneMeter; int zoneHeightMeters = tilesHeight * worldMap.TileSize / worldMap.OneMeter; int metersPerPixel = zoneWidthMeters / dlg.LayerMapImage.Width; Debug.Assert(metersPerPixel == (zoneHeightMeters / dlg.LayerMapImage.Height)); int samplesPerTile = WorldMap.metersPerTile / metersPerPixel; MapLayer layer = worldMap.GetLayer(dlg.LayerName); for (int z = 0; z < tilesHeight; z++) { int dz = z + zone.MinTileCoord.z; for (int x = 0; x < tilesWidth; x++) { int dx = x + zone.MinTileCoord.x; CoordXZ tileCoord = new CoordXZ(dx, dz, WorldMap.tileSize); MapBuffer tileMap; if (layer is ColorMapLayer) { ColorMapLayer tmpLayer = layer as ColorMapLayer; tileMap = tmpLayer.CreateCompatibleMapBuffer(dlg.LayerMapImage, metersPerPixel, x * samplesPerTile, z * samplesPerTile, samplesPerTile); } else { ValueMapLayer tmpLayer = layer as ValueMapLayer; tileMap = null; } MapTile tile = worldMap.GetTile(tileCoord); if (tileMap.MetersPerSample != layer.MetersPerSample) { tileMap = tileMap.Scale(worldMap.MetersPerTile / layer.MetersPerSample); } layer.CopyIn(tileCoord, tileMap); } } } }
public string TilePath(CoordXZ tileCoord) { return(string.Format("{0}/{1}-{2}({3},{4}).png", map.WorldPath, map.WorldName, layerName, tileCoord.x, tileCoord.z)); }
public MapTile GetTile(CoordXZ tileCoord) { MapSection section = GetSection(tileCoord); return(section.GetTile(tileCoord)); }
public MapTile GetTile(CoordXZ tileCoord) { // compute coordinates of tile within the section CoordXZ localTileCoord = tileCoord - this.tileCoord; MapTile tile = tiles[localTileCoord.x, localTileCoord.z]; return tile; }
public MapSection GetSection(CoordXZ tileCoord) { CoordXZ sectionCoord = new CoordXZ(tileCoord, sectionSize); if (!sections.ContainsKey(sectionCoord)) { sections[sectionCoord] = new MapSection(this, sectionCoord, worldPath); } return sections[sectionCoord]; }
public MapSection(WorldMap map, CoordXZ sectionCoord, string worldPath) : this(map, sectionCoord) { FromXml(worldPath); }
/// <summary> /// Convert the given coordinates to tile coordinates appropriate for this layer /// </summary> /// <param name="coordIn"></param> /// <returns></returns> public CoordXZ ToLayerTileCoords(CoordXZ coordIn) { return(new CoordXZ(coordIn, tileSize)); }
protected override MapBuffer LoadTileImpl(CoordXZ tileCoord) { return(new MapBufferARGB(map, TilePath(tileCoord))); }
/// <summary> /// Create a thumbnail bitmap of the map area /// </summary> /// <param name="coord"></param> /// <param name="worldSize"></param> /// <param name="pixelSize"></param> /// <returns></returns> public System.Drawing.Bitmap CreateThumbnail(CoordXZ coord, int worldSize, int pixelSize) { int numSamples = worldSize / (metersPerSample * map.OneMeter); Debug.Assert(WorldMap.IsPowerOf2(numSamples)); Debug.Assert(WorldMap.IsPowerOf2(pixelSize)); CoordXZ tileCoord; CoordXZ sampleOffset; bool exact = CoordToTileOffset(coord, out tileCoord, out sampleOffset); // make sure that coordinate is an exact sample Debug.Assert(exact); if (!TileLoaded(tileCoord)) { LoadTile(tileCoord); } MapBuffer tile = tiles[tileCoord]; return tile.CreateThumbnail(sampleOffset.x, sampleOffset.z, worldSize / (metersPerSample * map.OneMeter), pixelSize); }
/// <summary> /// Create a new tile and fill it with the default value for the layer /// </summary> /// <param name="tileCoord"></param> /// <returns></returns> protected override MapBuffer CreateTileImpl(CoordXZ tileCoord) { MapBuffer buffer = new MapBufferARGB(map, samplesPerTile, metersPerSample); buffer.Fill((uint)defaultColor.ToARGB()); return buffer; }
protected override MapBuffer LoadTileImpl(CoordXZ tileCoord) { return new MapBuffer16(map, TilePath(tileCoord)); }
public MapTile GetTile(CoordXZ tileCoord) { MapSection section = GetSection(tileCoord); return section.GetTile(tileCoord); }
protected void CreateTile(CoordXZ tileCoord) { tiles[tileCoord] = CreateTileImpl(tileCoord); }
protected abstract MapBuffer CreateTileImpl(CoordXZ tileCoord);
protected abstract MapBuffer LoadTileImpl(CoordXZ tileCoord);
/// <summary> /// Compute the tile coordinate and sample offset within the tile of a given coordinate /// </summary> /// <param name="coordIn">The input coordinate</param> /// <param name="tileCoord">The tile coordinate</param> /// <param name="sampleOffset">Sample offset within the tile</param> /// <returns>Whether the input coordinate exactly hits a sample</returns> protected bool CoordToTileOffset(CoordXZ coordIn, out CoordXZ tileCoord, out CoordXZ sampleOffset) { int sampleSize = metersPerSample * map.OneMeter; tileCoord = new CoordXZ(coordIn, tileSize); CoordXZ sampleCoord = new CoordXZ(coordIn, sampleSize); CoordXZ tileSampleCoord = new CoordXZ(tileCoord, sampleSize); sampleOffset = sampleCoord - tileSampleCoord; CoordXZ worldIn = new CoordXZ(coordIn, 1); CoordXZ worldSample = new CoordXZ(sampleCoord, 1); return (worldIn == worldSample); }
protected void UpdateVisible() { if ( worldMap != null ) { MapLayer heightFieldLayer = worldMap.HeightFieldLayer; for (int z = minVisibleTile.z; z <= maxVisibleTile.z; z++) { for (int x = minVisibleTile.x; x < maxVisibleTile.x; x++) { CoordXZ tileCoord = new CoordXZ(x, z, WorldMap.tileSize); MapTile tile = worldMap.GetTile(tileCoord); if (tile != null) { ImageGridCell cell = imageGrid.GetCell(x - gridOffset.x, z - gridOffset.z); if (cell == null) { cell = imageGrid.CreateCell(x - gridOffset.x, z - gridOffset.z); } if (cell.Image == null) { cell.Image = currentViewLayer.CreateThumbnail(tileCoord, worldMap.TileSize, imageGrid.CellSize); } } } } } }
private bool NewZonePlacementCallback(bool cancelled, Point location, object arg) { NewZoneData zoneData = (NewZoneData)arg; if (!cancelled) { // Check to see if the new zone overlaps any existing tiles for (int z = 0; z < zoneData.TilesHeight; z++) { int dz = z + gridOffset.z + location.Y; for (int x = 0; x < zoneData.TilesWidth; x++) { int dx = x + gridOffset.x + location.X; CoordXZ tileCoord = new CoordXZ(dx, dz, WorldMap.tileSize); MapTile tile = worldMap.GetTile(tileCoord); if (tile != null) { // overlaps existing tiles return false; } } } MapZone zone = worldMap.CreateZone(zoneData.ZoneName); int samplesPerTile = WorldMap.metersPerTile / zoneData.MetersPerSample; ValueMapLayer heightFieldLayer = worldMap.HeightFieldLayer as ValueMapLayer; for (int z = 0; z < zoneData.TilesHeight; z++) { int dz = z + gridOffset.z + location.Y; for (int x = 0; x < zoneData.TilesWidth; x++) { int dx = x + gridOffset.x + location.X; CoordXZ tileCoord = new CoordXZ(dx, dz, WorldMap.tileSize); MapBuffer tileHeightmap = heightFieldLayer.CreateCompatibleMapBuffer(zoneData.Heightmap, zoneData.MetersPerSample, x * samplesPerTile, z * samplesPerTile, samplesPerTile, zoneData.MinHeight, zoneData.MaxHeight); MapTile tile = worldMap.CreateTile(tileCoord); tile.Zone = zone; if (tileHeightmap.MetersPerSample != heightFieldLayer.MetersPerSample) { tileHeightmap = tileHeightmap.Scale(worldMap.MetersPerTile / heightFieldLayer.MetersPerSample); } heightFieldLayer.CopyIn(tileCoord, tileHeightmap); ImageGridCell cell = imageGrid.CreateCell(x + location.X, z + location.Y); cell.Image = currentViewLayer.CreateThumbnail(tileCoord, worldMap.TileSize, imageGrid.CellSize); } } AddZone(zone); } return true; }
public string TilePath(CoordXZ tileCoord) { return string.Format("{0}/{1}-{2}({3},{4}).png", map.WorldPath, map.WorldName, layerName, tileCoord.x, tileCoord.z); }
public bool TileLoaded(CoordXZ tileCoord) { return(tiles.ContainsKey(tileCoord)); }
private bool NewZonePlacementCallback(bool cancelled, Point location, object arg) { NewZoneData zoneData = (NewZoneData)arg; if (!cancelled) { // Check to see if the new zone overlaps any existing tiles for (int z = 0; z < zoneData.TilesHeight; z++) { int dz = z + gridOffset.z + location.Y; for (int x = 0; x < zoneData.TilesWidth; x++) { int dx = x + gridOffset.x + location.X; CoordXZ tileCoord = new CoordXZ(dx, dz, WorldMap.tileSize); MapTile tile = worldMap.GetTile(tileCoord); if (tile != null) { // overlaps existing tiles return(false); } } } MapZone zone = worldMap.CreateZone(zoneData.ZoneName); int samplesPerTile = WorldMap.metersPerTile / zoneData.MetersPerSample; ValueMapLayer heightFieldLayer = worldMap.HeightFieldLayer as ValueMapLayer; for (int z = 0; z < zoneData.TilesHeight; z++) { int dz = z + gridOffset.z + location.Y; for (int x = 0; x < zoneData.TilesWidth; x++) { int dx = x + gridOffset.x + location.X; CoordXZ tileCoord = new CoordXZ(dx, dz, WorldMap.tileSize); MapBuffer tileHeightmap = heightFieldLayer.CreateCompatibleMapBuffer(zoneData.Heightmap, zoneData.MetersPerSample, x * samplesPerTile, z * samplesPerTile, samplesPerTile, zoneData.MinHeight, zoneData.MaxHeight); MapTile tile = worldMap.CreateTile(tileCoord); tile.Zone = zone; if (tileHeightmap.MetersPerSample != heightFieldLayer.MetersPerSample) { tileHeightmap = tileHeightmap.Scale(worldMap.MetersPerTile / heightFieldLayer.MetersPerSample); } heightFieldLayer.CopyIn(tileCoord, tileHeightmap); ImageGridCell cell = imageGrid.CreateCell(x + location.X, z + location.Y); cell.Image = currentViewLayer.CreateThumbnail(tileCoord, worldMap.TileSize, imageGrid.CellSize); } } AddZone(zone); } return(true); }
/// <summary> /// Convert the given coordinates to tile coordinates appropriate for this layer /// </summary> /// <param name="coordIn"></param> /// <returns></returns> public CoordXZ ToLayerTileCoords(CoordXZ coordIn) { return new CoordXZ(coordIn, tileSize); }
public bool TileExists(CoordXZ tileCoord) { if (tiles.ContainsKey(tileCoord)) { return ( tiles[tileCoord] != null ); } else if (System.IO.File.Exists(TilePath(tileCoord))) { return true; } return false; }
public bool TileLoaded(CoordXZ tileCoord) { return tiles.ContainsKey(tileCoord); }
public MapTile CreateTile(CoordXZ tileCoord) { MapTile tile = new MapTile(this, tileCoord); MapSection section = GetSection(tileCoord); section.AddTile(tile); OnNewTile(tile); return tile; }