/**
 * Is there a Chunnel in the way in the given direction?
 * Only between height level 0 and 1.
 * Used to avoid building bridge or tunnel between existing chunnel.
 * @param tile the tile to search from.
 * @param dir  the direction to start searching to.
 * @return true if and only if there is a chunnel.
 */
bool IsBetweenChunnelPortals(TileIndex tile, DiagDirection dir)
{
	uint h = 0;
	TileIndexDiff delta = TileOffsByDiagDir(dir);
	if (GetTileZ(tile) > 0) return false;

	do {
		if (dir == DIAGDIR_NE || dir == DIAGDIR_NW) {
			do {
				tile += delta;
			if (!IsValidTile(tile)) return false;
			} while (TileHeight(tile) != h);
		} else {
			tile += delta;
			do {
				tile += delta;
			if (!IsValidTile(tile)) return false;
			} while (TileHeight(tile) != h);
			tile -= delta;
		}
		(h == 0) ? h = 1 : h = 0;
	} while (!IsTunnelTile(tile));

	if (GetTunnelBridgeDirection(tile) != ReverseDiagDir(dir)) return false;

	return true;
}
	virtual int GetTileTargetHeight() const
	{
		uint ret = this->target_height;
		switch (this->variant) {
			case CPLV_LEVEL_ABOVE: ret = min(ret, TileHeight(*this)); break;
			case CPLV_LEVEL_BELOW: ret = max(ret, TileHeight(*this)); break;
		}
		return ret;
	}
Exemple #3
0
/**
 * Get top height of the tile
 * @param t Tile to compute height of
 * @return Maximum height of the tile */
uint GetTileMaxZ(TileIndex t)
{
	if (TileX(t) == MapMaxX() || TileY(t) == MapMaxY()) return 0;

	uint h = TileHeight(t); // N corner
	h = max(h, TileHeight(t + TileDiffXY(1, 0))); // W corner
	h = max(h, TileHeight(t + TileDiffXY(0, 1))); // E corner
	h = max(h, TileHeight(t + TileDiffXY(1, 1))); // S corner

	return h * TILE_HEIGHT;
}
Exemple #4
0
/**
 * Get bottom height of the tile
 * @param tile Tile to compute height of
 * @return Minimum height of the tile
 */
int GetTileZ(TileIndex tile)
{
	if (TileX(tile) == MapMaxX() || TileY(tile) == MapMaxY()) return 0;

	int h =    TileHeight(tile);                     // N corner
	h = min(h, TileHeight(tile + TileDiffXY(1, 0))); // W corner
	h = min(h, TileHeight(tile + TileDiffXY(0, 1))); // E corner
	h = min(h, TileHeight(tile + TileDiffXY(1, 1))); // S corner

	return h;
}
Exemple #5
0
/**
 * Get top height of the tile inside the map.
 * @param t Tile to compute height of
 * @return Maximum height of the tile
 */
int GetTileMaxZ(TileIndex t)
{
	if (TileX(t) == MapMaxX() || TileY(t) == MapMaxY()) return TileHeightOutsideMap(TileX(t), TileY(t));

	int h =         TileHeight(t);                     // N corner
	h = max<int>(h, TileHeight(t + TileDiffXY(1, 0))); // W corner
	h = max<int>(h, TileHeight(t + TileDiffXY(0, 1))); // E corner
	h = max<int>(h, TileHeight(t + TileDiffXY(1, 1))); // S corner

	return h;
}
Exemple #6
0
/**
 * Return the slope of a given tile inside the map.
 * @param tile Tile to compute slope of
 * @param h    If not \c NULL, pointer to storage of z height
 * @return Slope of the tile, except for the HALFTILE part
 */
Slope GetTileSlope(TileIndex tile, int *h)
{
	assert(tile < MapSize());

	if (!IsInnerTile(tile)) {
		if (h != NULL) *h = TileHeight(tile);
		return SLOPE_FLAT;
	}

	int hnorth = TileHeight(tile);                    // Height of the North corner.
	int hwest  = TileHeight(tile + TileDiffXY(1, 0)); // Height of the West corner.
	int heast  = TileHeight(tile + TileDiffXY(0, 1)); // Height of the East corner.
	int hsouth = TileHeight(tile + TileDiffXY(1, 1)); // Height of the South corner.

	return GetTileSlopeGivenHeight(hnorth, hwest, heast, hsouth, h);
}
Exemple #7
0
int TerrainSet::Load( MemBuffer &file, const char *setname ) {
  int rc = -1;

  if ( file.Read32() == FID_TERRAINSET ) {
    num_tiles = file.Read16();

    for ( int i = 0; i < 10; ++i )
      classid[i] = file.Read16();

    if ( !LoadTerrainTypes( file ) && !TileSet::Load( file, setname ) )
      // create the fog surface. This is kept separate from the tiles
      // surface so that we don't need to modify the alpha value each
      // time we want fog
      fog.Create( TileWidth(), TileHeight(), 16, SDL_SWSURFACE );
      fog.SetAlpha( FOG_ALPHA, SDL_SRCALPHA );
      fog.SetColorKey( Color(CF_COLOR_WHITE) );
      fog.DisplayFormat();
      fog.Flood( Color(CF_COLOR_WHITE) );
      DrawTile( IMG_FOG, &fog, 0, 0, fog );

      rc = 0;
  }

  return rc;
}
/**
 * Terraform multiple tiles.
 *
 * @param iter Iterator pointing tiles to terraform and their target heights.
 * @return The cost of all successfull operations and the last error.
 *
 * @note _terraform_err_tile will be set to the tile where the last error occured
 *
 * @warning Note non-standard return behaviour - booth the cost \b and the error combined.
 */
static TerraformTilesResult TerraformTiles(TerraformingIterator *iter, DoCommandFlag flags, Money available_money)
{
	TerraformTilesResult result = {
		0,       // cost
		false,   // had_success
		STR_NULL // last_error
	};
	TileIndex last_err_tile = INVALID_TILE;

	const Company *c = Company::GetIfValid(_current_company);
	int limit = (c == NULL ? INT32_MAX : GB(c->terraform_limit, 16, 16));
	if (limit == 0) result.last_error = STR_ERROR_TERRAFORM_LIMIT_REACHED;

	for (; *iter != INVALID_TILE && limit > 0; ++(*iter)) {
		int h = iter->GetTileTargetHeight();
		TileIndex t = *iter;
		for (int curh = TileHeight(t); curh != h; curh += (curh > h) ? -1 : 1) {
			CommandCost ret = DoCommand(t, SLOPE_N, (curh > h) ? 0 : 1, flags & ~DC_EXEC, CMD_TERRAFORM_LAND);
			if (ret.Failed()) {
				result.last_error = ret.GetErrorMessage();
				last_err_tile = _terraform_err_tile;

				/* Did we reach the limit? */
				if (ret.GetErrorMessage() == STR_ERROR_TERRAFORM_LIMIT_REACHED) limit = 0;
				break;
			}

			if (flags & DC_EXEC) {
				available_money -= ret.GetCost();
				if (available_money < 0) {
					_additional_cash_required = ret.GetCost();
					_terraform_err_tile = t;
					return result;
				}
				DoCommand(t, SLOPE_N, (curh > h) ? 0 : 1, flags, CMD_TERRAFORM_LAND);
			} else {
				/* When we're at the terraform limit we better bail (unneeded) testing as well.
				 * This will probably cause the terraforming cost to be underestimated, but only
				 * when it's near the terraforming limit. Even then, the estimation is
				 * completely off due to it basically counting terraforming double, so it being
				 * cut off earlier might even give a better estimate in some cases. */
				if (--limit <= 0)  {
					result.had_success = true;
					break;
				}
			}

			result.cost += ret.GetCost();
			result.had_success = true;
		}
	}

	if (!result.had_success && result.last_error == STR_NULL) {
		result.last_error = STR_ERROR_ALREADY_LEVELLED;
		last_err_tile = INVALID_TILE;
	}

	_terraform_err_tile = last_err_tile;
	return result;
}
/**
 * Copy and paste heights from one map to another.
 *
 * @param src_area Area to read heights from. It consists of tiles, not of tile corners
 *                  e.g. if you pass a single tile area then 4 corners will be terraformed.
 * @param dst_area_north Norhern tile of the area to write heigths at.
 * @param transformation Transformation to perform on tile indices.
 * @param height_delta Offset, number of units to add to each height.
 */
void CopyPasteHeights(const GenericTileArea &src_area, GenericTileIndex dst_area_north, DirTransformation transformation, int height_delta)
{
	/* include also corners at SW and SE edges */
	GenericTileArea src_corners(src_area.tile, src_area.w + 1, src_area.h + 1);
	/* transform the most northern corner */
	GenericTileIndex transformed_north_corner = src_corners.TransformedNorth(dst_area_north, transformation);

#ifdef WITH_ASSERT
	{
		assert(IsValidTileIndex(dst_area_north));
		uint x = TileX(dst_area_north);
		uint y = TileY(dst_area_north);
		assert(!IsMainMapTile(dst_area_north) || !_settings_game.construction.freeform_edges || (x > 0 && y > 0));
		Dimension dst_dim = { src_corners.w, src_corners.h };
		dst_dim = TransformDimension(dst_dim, transformation);
		assert(x + dst_dim.width <= MapSizeX(MapOf(dst_area_north)) && y + dst_dim.height <= MapSizeY(MapOf(dst_area_north)));
	}
#endif /* WITH_ASSERT */

	if (IsMainMapTile(dst_area_north)) {
		HeightsCopyPastingIterator iter(src_corners, AsMainMapTile(transformed_north_corner), transformation, height_delta);
		TerraformPasteTiles(&iter);
	} else {
		for (TransformationTileIteratorT<true, true> iter(src_corners, transformed_north_corner, transformation); IsValidTileIndex(iter); ++iter) {
			SetTileHeight(iter.DstTile(), TileHeight(iter.SrcTile()));
		}
	}
}
/**
 * Levels a selected (rectangle) area of land
 * @param tile end tile of area-drag
 * @param flags for this command type
 * @param p1 start tile of area drag
 * @param p2 various bitstuffed data.
 *  bit      0: Whether to use the Orthogonal (0) or Diagonal (1) iterator.
 *  bits 1 - 2: Mode of leveling \c LevelMode.
 * @param text unused
 * @return the cost of this operation or an error
 */
CommandCost CmdLevelLand(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
	if (p1 >= MapSize()) return CMD_ERROR;

	/* compute new height */
	int h = TileHeight(p1);
	switch ((LevelMode)GB(p2, 1, 2)) {
		case LM_LEVEL: break;
		case LM_RAISE: h++; break;
		case LM_LOWER: h--; break;
		default: return CMD_ERROR;
	}

	TerraformTilesResult ret;
	if (HasBit(p2, 0)) {
		DiagonalLandLevelingIterator iter(tile, p1, h);
		ret = TerraformTiles(&iter, flags);
	} else {
		OrthogonalLandLevelingIterator iter(TileArea(tile, p1), h);
		ret = TerraformTiles(&iter, flags);
	}

	/* If there were only errors then fail with the last one. */
	if (!ret.had_success && ret.last_error != STR_NULL) return_cmd_error(ret.last_error);
	/* Return overal cost. */
	return CommandCost(EXPENSES_CONSTRUCTION, ret.cost);
}
Exemple #11
0
/**
 * Check if a given tile is flat
 * @param tile Tile to check
 * @param h If not \c NULL, pointer to storage of z height (only if tile is flat)
 * @return Whether the tile is flat
 */
bool IsTileFlat(TileIndex tile, int *h)
{
	assert(tile < MapSize());

	if (!IsInnerTile(tile)) {
		if (h != NULL) *h = TileHeight(tile);
		return true;
	}

	uint z = TileHeight(tile);
	if (TileHeight(tile + TileDiffXY(1, 0)) != z) return false;
	if (TileHeight(tile + TileDiffXY(0, 1)) != z) return false;
	if (TileHeight(tile + TileDiffXY(1, 1)) != z) return false;

	if (h != NULL) *h = z;
	return true;
}
/**
 * Gets the TileHeight (height of north corner) of a tile as of current terraforming progress.
 *
 * @param ts TerraformerState.
 * @param tile Tile.
 * @return TileHeight.
 */
static int TerraformGetHeightOfTile(TerraformerState *ts, TileIndex tile)
{
	if (ts->tile_to_new_height.find(tile) != ts->tile_to_new_height.end()) {
		return ts->tile_to_new_height[tile];
	} else {
		return TileHeight(tile);
	}
}
/**
 * Test if this is safe to copy and paste contents of the map instantly, without
 * using an intermediate buffer.
 *
 * If the copy and the paste areas are close enough (especially when they intersect),
 * sequential copy-pasting may alter at some point of time those tile of the copy
 * area which hasn't been copied yet. In this case, further copy-pasting will read
 * modified values, not the original and this is somthing we don't want to happen.
 * We can deal with it by firstly copying all the content to the clipboard buffer and
 * then pasting it onto the map. This function tels us whether we should use the
 * clipboard as an intermediate buffer because there may happen such a colision.
 *
 * @param copy_paste What, where and how we are copying.
 * @return \c true if intermediate buffer might be required, \c false if it's surely not required
 *
 * @pre booth the source area and the destination area are on the main map
 *
 * @see CalcMaxPasteRange
 */
static bool CopyPasteAreasMayColide(const CopyPasteParams &copy_paste)
{
	/* No need to check surroundings if we are not terraforming. Just test for content intersection. */
	if ((copy_paste.mode & CPM_TERRAFORM_MASK) == CPM_TERRAFORM_NONE) return copy_paste.src_area.Intersects(copy_paste.dst_area);

	/* As we are interested in tile heights, increase areas to include all tile
	 * corners, also these at SW and SE borders. */
	TileArea src_corner_area(AsMainMapTile(copy_paste.src_area.tile), copy_paste.src_area.w + 1, copy_paste.src_area.h + 1);
	TileArea dst_corner_area(AsMainMapTile(copy_paste.dst_area.tile), copy_paste.dst_area.w + 1, copy_paste.dst_area.h + 1);

	DirTransformation inv_transformation = InvertDirTransform(copy_paste.transformation);
	/* source of the destination area most northern tile corner */
	TileIndex source_of_north = dst_corner_area.TransformedNorth(src_corner_area.tile, inv_transformation);

	/* calculate current and new heights on destination area corners */
	/* N */
	TileIndex dst_corner = dst_corner_area.tile;
	TileIndex src_corner = source_of_north;
	uint curr_n = TileHeight(dst_corner);
	uint new_n = TileHeight(src_corner) + copy_paste.height_delta;
	/* W */
	dst_corner = TILE_ADDXY(dst_corner_area.tile, dst_corner_area.w, 0);
	src_corner = dst_corner_area.TransformTile(dst_corner, source_of_north, inv_transformation);
	uint curr_w = TileHeight(dst_corner);
	uint new_w = TileHeight(src_corner) + copy_paste.height_delta;
	/* S */
	dst_corner = TILE_ADDXY(dst_corner_area.tile, dst_corner_area.w, dst_corner_area.h);
	src_corner = dst_corner_area.TransformTile(dst_corner, source_of_north, inv_transformation);
	uint curr_s = TileHeight(dst_corner);
	uint new_s = TileHeight(src_corner) + copy_paste.height_delta;
	/* E */
	dst_corner = TILE_ADDXY(dst_corner_area.tile, 0, dst_corner_area.h);
	src_corner = dst_corner_area.TransformTile(dst_corner, source_of_north, inv_transformation);
	uint curr_e = TileHeight(dst_corner);
	uint new_e = TileHeight(src_corner) + copy_paste.height_delta;

	/* calculate how far tiles can be altered beyon the paste area (safe approximation) */
	uint range_ne = CalcMaxPasteRange(curr_n, new_n, curr_e, new_e, dst_corner_area.h - 1);
	uint range_sw = CalcMaxPasteRange(curr_s, new_s, curr_w, new_w, dst_corner_area.h - 1);
	uint range_nw = CalcMaxPasteRange(curr_n, new_n, curr_w, new_w, dst_corner_area.w - 1);
	uint range_se = CalcMaxPasteRange(curr_s, new_s, curr_e, new_e, dst_corner_area.w - 1);

	/* calculate the exact area which may be altered by the paste process */
	uint x = TileX(dst_corner_area.tile);
	uint y = TileY(dst_corner_area.tile);
	range_ne = max(range_ne, x); // cut the area at the NE border (don't let x to go below 0)
	range_nw = max(range_nw, y); // cut the area at the NW border (don't let y to go below 0)
	TileArea forbidden_area(
			TileXY(x - range_ne, y - range_nw),
			dst_corner_area.w + range_ne + range_sw,
			dst_corner_area.h + range_nw + range_se);

	/* test if the source area is within the paste range */
	return src_corner_area.Intersects(forbidden_area);
}
Exemple #14
0
/**
 * This function takes care of the fact that land in OpenTTD can never differ
 * more than 1 in height
 */
void FixSlopes()
{
	uint width, height;
	int row, col;
	byte current_tile;

	/* Adjust height difference to maximum one horizontal/vertical change. */
	width   = MapSizeX();
	height  = MapSizeY();

	/* Top and left edge */
	for (row = 0; (uint)row < height; row++) {
		for (col = 0; (uint)col < width; col++) {
			current_tile = MAX_TILE_HEIGHT;
			if (col != 0) {
				/* Find lowest tile; either the top or left one */
				current_tile = TileHeight(TileXY(col - 1, row)); // top edge
			}
			if (row != 0) {
				if (TileHeight(TileXY(col, row - 1)) < current_tile) {
					current_tile = TileHeight(TileXY(col, row - 1)); // left edge
				}
			}

			/* Does the height differ more than one? */
			if (TileHeight(TileXY(col, row)) >= (uint)current_tile + 2) {
				/* Then change the height to be no more than one */
				SetTileHeight(TileXY(col, row), current_tile + 1);
			}
		}
	}

	/* Bottom and right edge */
	for (row = height - 1; row >= 0; row--) {
		for (col = width - 1; col >= 0; col--) {
			current_tile = MAX_TILE_HEIGHT;
			if ((uint)col != width - 1) {
				/* Find lowest tile; either the bottom and right one */
				current_tile = TileHeight(TileXY(col + 1, row)); // bottom edge
			}

			if ((uint)row != height - 1) {
				if (TileHeight(TileXY(col, row + 1)) < current_tile) {
					current_tile = TileHeight(TileXY(col, row + 1)); // right edge
				}
			}

			/* Does the height differ more than one? */
			if (TileHeight(TileXY(col, row)) >= (uint)current_tile + 2) {
				/* Then change the height to be no more than one */
				SetTileHeight(TileXY(col, row), current_tile + 1);
			}
		}
	}
}
Exemple #15
0
/**
 * Return the slope of a given tile
 * @param tile Tile to compute slope of
 * @param h    If not \c NULL, pointer to storage of z height
 * @return Slope of the tile, except for the HALFTILE part
 */
Slope GetTileSlope(TileIndex tile, int *h)
{
	assert(tile < MapSize());

	uint x = TileX(tile);
	uint y = TileY(tile);

	if (x == MapMaxX() || y == MapMaxY() ||
			((x == 0 || y == 0) && _settings_game.construction.freeform_edges)) {
		if (h != NULL) *h = TileHeight(tile);
		return SLOPE_FLAT;
	}

	int a = TileHeight(tile); // Height of the N corner
	int min = a; // Minimal height of all corners examined so far
	int b = TileHeight(tile + TileDiffXY(1, 0)); // Height of the W corner
	if (min > b) min = b;
	int c = TileHeight(tile + TileDiffXY(0, 1)); // Height of the E corner
	if (min > c) min = c;
	int d = TileHeight(tile + TileDiffXY(1, 1)); // Height of the S corner
	if (min > d) min = d;

	/* Due to the fact that tiles must connect with each other without leaving gaps, the
	 * biggest difference in height between any corner and 'min' is between 0, 1, or 2.
	 *
	 * Also, there is at most 1 corner with height difference of 2.
	 */

	uint r = SLOPE_FLAT; // Computed slope of the tile

	/* For each corner if not equal to minimum height:
	 *  - set the SLOPE_STEEP flag if the difference is 2
	 *  - add the corresponding SLOPE_X constant to the computed slope
	 */
	if ((a -= min) != 0) r += (--a << 4) + SLOPE_N;
	if ((c -= min) != 0) r += (--c << 4) + SLOPE_E;
	if ((d -= min) != 0) r += (--d << 4) + SLOPE_S;
	if ((b -= min) != 0) r += (--b << 4) + SLOPE_W;

	if (h != NULL) *h = min;

	return (Slope)r;
}
Exemple #16
0
void TileSet::DrawTile( unsigned short n, Surface *dest,
                        short px, short py, const Rect &clip ) const {
  // set up destination
  Rect dstrect( px, py, TileWidth(), TileHeight() );
  if ( dstrect.x + dstrect.w < clip.x ) return;
  if ( dstrect.y + dstrect.h < clip.y ) return;
  if ( dstrect.x >= clip.x + clip.w ) return;
  if ( dstrect.y >= clip.y + clip.h ) return;

  // set up source
  Rect srcrect;
  unsigned short gfx_per_line = tiles.Width() / TileWidth();
  srcrect.x = (n % gfx_per_line) * TileWidth();
  srcrect.y = (n / gfx_per_line) * TileHeight();
  srcrect.w = dstrect.w;
  srcrect.h = dstrect.h;

  // clip and blit to surface
  dstrect.ClipBlit( srcrect, clip );
  tiles.LowerBlit( dest, srcrect, dstrect.x, dstrect.y );
}
Exemple #17
0
/**
 * Returns the tile height for a coordinate outside map.  Such a height is
 * needed for painting the area outside map using completely black tiles.
 * The idea is descending to heightlevel 0 as fast as possible.
 * @param x The X-coordinate (same unit as TileX).
 * @param y The Y-coordinate (same unit as TileY).
 * @return The height in the same unit as TileHeight.
 */
uint TileHeightOutsideMap(int x, int y)
{
	/* In all cases: Descend to heightlevel 0 as fast as possible.
	 * So: If we are at the 0-side of the map (x<0 or y<0), we must
	 * subtract the distance to coordinate 0 from the heightlevel at
	 * coordinate 0.
	 * In other words: Subtract e.g. -x. If we are at the MapMax
	 * side of the map, we also need to subtract the distance to
	 * the edge of map, e.g. MapMaxX - x.
	 *
	 * NOTE: Assuming constant heightlevel outside map would be
	 * simpler here. However, then we run into painting problems,
	 * since whenever a heightlevel change at the map border occurs,
	 * we would need to repaint anything outside map.
	 * In contrast, by doing it this way, we can localize this change,
	 * which means we may assume constant heightlevel for all tiles
	 * at more than <heightlevel at map border> distance from the
	 * map border.
	 */
	if (x < 0) {
		if (y < 0) {
			return max((int)TileHeight(TileXY(0, 0)) - (-x) - (-y), 0);
		} else if (y < (int)MapMaxY()) {
			return max((int)TileHeight(TileXY(0, y)) - (-x), 0);
		} else {
			return max((int)TileHeight(TileXY(0, (int)MapMaxY())) - (-x) - (y - (int)MapMaxY()), 0);
		}
	} else if (x < (int)MapMaxX()) {
		if (y < 0) {
			return max((int)TileHeight(TileXY(x, 0)) - (-y), 0);
		} else if (y < (int)MapMaxY()) {
			return TileHeight(TileXY(x, y));
		} else {
			return max((int)TileHeight(TileXY(x, (int)MapMaxY())) - (y - (int)MapMaxY()), 0);
		}
	} else {
		if (y < 0) {
			return max((int)TileHeight(TileXY((int)MapMaxX(), 0)) - (x - (int)MapMaxX()) - (-y), 0);
		} else if (y < (int)MapMaxY()) {
			return max((int)TileHeight(TileXY((int)MapMaxX(), y)) - (x - (int)MapMaxX()), 0);
		} else {
			return max((int)TileHeight(TileXY((int)MapMaxX(), (int)MapMaxY())) - (x - (int)MapMaxX()) - (y - (int)MapMaxY()), 0);
		}
	}
}
typename TileIndexT<Tgeneric>::T GetOtherTunnelEnd(typename TileIndexT<Tgeneric>::T tile)
{
	assert(IsTunnelTile(tile));

	DiagDirection dir = GetTunnelBridgeDirection(tile);
	TileIndexDiff delta = TileOffsByDiagDir<Tgeneric>(dir, MapOf(tile));
	uint h = TileHeight(tile);

	if (dir == DIAGDIR_NE || dir == DIAGDIR_NW) {
		h--;
continue_ne_nw:
	do {
		tile += delta;
		} while (TileHeight(tile) != h);
	} else {
continue_se_sw:
		tile += delta;
		do {
			tile += delta;
		} while (TileHeight(tile) != h);
		tile -= delta;
	}

	if (IsTunnelTile(tile) && GetTunnelBridgeDirection(tile) == ReverseDiagDir(dir)) {
	} else {
	/* Handle Chunnels.
	 * Only look for tunnel when hight level changes.
	 * And only at sea level.
	 */
		assert(h <= 1);
		(h == 0) ? h = 1 : h = 0;
		if (dir == DIAGDIR_NE || dir == DIAGDIR_NW) {
			goto continue_ne_nw;
		} else {
			goto continue_se_sw;
		}
	}

	return tile;
}
/**
 * Build a piece of canal.
 * @param tile end tile of stretch-dragging
 * @param flags type of operation
 * @param p1 start tile of stretch-dragging
 * @param p2 waterclass to build. sea and river can only be built in scenario editor
 * @param text unused
 * @return the cost of this operation or an error
 */
CommandCost CmdBuildCanal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
	WaterClass wc = Extract<WaterClass, 0, 2>(p2);
	if (p1 >= MapSize() || wc == WATER_CLASS_INVALID) return CMD_ERROR;

	/* Outside of the editor you can only build canals, not oceans */
	if (wc != WATER_CLASS_CANAL && _game_mode != GM_EDITOR) return CMD_ERROR;

	TileArea ta(tile, p1);

	/* Outside the editor you can only drag canals, and not areas */
	if (_game_mode != GM_EDITOR && ta.w != 1 && ta.h != 1) return CMD_ERROR;

	CommandCost cost(EXPENSES_CONSTRUCTION);
	TILE_AREA_LOOP(tile, ta) {
		CommandCost ret;

		Slope slope = GetTileSlope(tile, NULL);
		if (slope != SLOPE_FLAT && (wc != WATER_CLASS_RIVER || !IsInclinedSlope(slope))) {
			return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
		}

		/* can't make water of water! */
		if (IsTileType(tile, MP_WATER) && (!IsTileOwner(tile, OWNER_WATER) || wc == WATER_CLASS_SEA)) continue;

		ret = DoCommand(tile, 0, 0, flags | DC_FORCE_CLEAR_TILE, CMD_LANDSCAPE_CLEAR);
		if (ret.Failed()) return ret;
		cost.AddCost(ret);

		if (flags & DC_EXEC) {
			switch (wc) {
				case WATER_CLASS_RIVER:
					MakeRiver(tile, Random());
					break;

				case WATER_CLASS_SEA:
					if (TileHeight(tile) == 0) {
						MakeSea(tile);
						break;
					}
					/* FALL THROUGH */

				default:
					MakeCanal(tile, _current_company, Random());
					break;
			}
			MarkTileDirtyByTile(tile);
			MarkCanalsAndRiversAroundDirty(tile);
		}

		cost.AddCost(_price[PR_BUILD_CANAL]);
	}
Exemple #20
0
/**
 * Callback for generating a heightmap. Supports 8bpp grayscale only.
 * @param userdata Pointer to user data.
 * @param buf      Destination buffer.
 * @param y        Line number of the first line to write.
 * @param pitch    Number of pixels to write (1 byte for 8bpp, 4 bytes for 32bpp). @see Colour
 * @param n        Number of lines to write.
 * @see ScreenshotCallback
 */
static void HeightmapCallback(void *userdata, void *buffer, uint y, uint pitch, uint n)
{
	byte *buf = (byte *)buffer;
	while (n > 0) {
		TileIndex ti = TileXY(MapMaxX(), y);
		for (uint x = MapMaxX(); true; x--) {
			*buf = 16 * TileHeight(ti);
			buf++;
			if (x == 0) break;
			ti = TILE_ADDXY(ti, -1, 0);
		}
		y++;
		n--;
	}
}
Exemple #21
0
/** Build a piece of canal.
 * @param tile end tile of stretch-dragging
 * @param flags type of operation
 * @param p1 start tile of stretch-dragging
 * @param p2 specifies canal (0), water (1) or river (2); last two can only be built in scenario editor
 * @param text unused
 * @return the cost of this operation or an error
 */
CommandCost CmdBuildCanal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
	CommandCost cost(EXPENSES_CONSTRUCTION);

	if (p1 >= MapSize()) return CMD_ERROR;

	/* Outside of the editor you can only build canals, not oceans */
	if (p2 != 0 && _game_mode != GM_EDITOR) return CMD_ERROR;

	TileArea ta(tile, p1);

	/* Outside the editor you can only drag canals, and not areas */
	if (_game_mode != GM_EDITOR && ta.w != 1 && ta.h != 1) return CMD_ERROR;

	TILE_AREA_LOOP(tile, ta) {
		CommandCost ret;

		Slope slope = GetTileSlope(tile, NULL);
		if (slope != SLOPE_FLAT && (p2 != 2 || !IsInclinedSlope(slope))) {
			return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
		}

		/* can't make water of water! */
		if (IsTileType(tile, MP_WATER) && (!IsTileOwner(tile, OWNER_WATER) || p2 == 1)) continue;

		ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
		if (CmdFailed(ret)) return ret;
		cost.AddCost(ret);

		if (flags & DC_EXEC) {
			if (TileHeight(tile) == 0 && p2 == 1) {
				MakeSea(tile);
			} else if (p2 == 2) {
				MakeRiver(tile, Random());
			} else {
				MakeCanal(tile, _current_company, Random());
			}
			MarkTileDirtyByTile(tile);
			MarkCanalsAndRiversAroundDirty(tile);
		}

		cost.AddCost(_price[PR_CLEAR_WATER]);
	}
Exemple #22
0
/* static */ uint32 ScriptTown::GetCargoGoal(TownID town_id, ScriptCargo::TownEffect towneffect_id)
{
	if (!IsValidTown(town_id)) return -1;
	if (!ScriptCargo::IsValidTownEffect(towneffect_id)) return -1;

	const Town *t = ::Town::Get(town_id);

	switch (t->goal[towneffect_id]) {
		case TOWN_GROWTH_WINTER:
			if (TileHeight(t->xy) >= GetSnowLine() && t->cache.population > 90) return 1;
			return 0;

		case TOWN_GROWTH_DESERT:
			if (GetTropicZone(t->xy) == TROPICZONE_DESERT && t->cache.population > 60) return 1;
			return 0;

		default: return t->goal[towneffect_id];
	}
}
/**
 * Terraform land
 * @param tile tile to terraform
 * @param flags for this command type
 * @param p1 corners to terraform (SLOPE_xxx)
 * @param p2 direction; eg up (non-zero) or down (zero)
 * @param text unused
 * @return the cost of this operation or an error
 */
CommandCost CmdTerraformLand(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
{
	_terraform_err_tile = INVALID_TILE;

	CommandCost total_cost(EXPENSES_CONSTRUCTION);
	int direction = (p2 != 0 ? 1 : -1);
	TerraformerState ts;

	/* Compute the costs and the terraforming result in a model of the landscape */
	if ((p1 & SLOPE_W) != 0 && tile + TileDiffXY(1, 0) < MapSize()) {
		TileIndex t = tile + TileDiffXY(1, 0);
		CommandCost cost = TerraformTileHeight(&ts, t, TileHeight(t) + direction);
		if (cost.Failed()) return cost;
		total_cost.AddCost(cost);
	}

	if ((p1 & SLOPE_S) != 0 && tile + TileDiffXY(1, 1) < MapSize()) {
		TileIndex t = tile + TileDiffXY(1, 1);
		CommandCost cost = TerraformTileHeight(&ts, t, TileHeight(t) + direction);
		if (cost.Failed()) return cost;
		total_cost.AddCost(cost);
	}

	if ((p1 & SLOPE_E) != 0 && tile + TileDiffXY(0, 1) < MapSize()) {
		TileIndex t = tile + TileDiffXY(0, 1);
		CommandCost cost = TerraformTileHeight(&ts, t, TileHeight(t) + direction);
		if (cost.Failed()) return cost;
		total_cost.AddCost(cost);
	}

	if ((p1 & SLOPE_N) != 0) {
		TileIndex t = tile + TileDiffXY(0, 0);
		CommandCost cost = TerraformTileHeight(&ts, t, TileHeight(t) + direction);
		if (cost.Failed()) return cost;
		total_cost.AddCost(cost);
	}

	/* Check if the terraforming is valid wrt. tunnels, bridges and objects on the surface
	 * Pass == 0: Collect tileareas which are caused to be auto-cleared.
	 * Pass == 1: Collect the actual cost. */
	for (int pass = 0; pass < 2; pass++) {
		for (std::set<TileIndex>::const_iterator it = ts.dirty_tiles.begin(); it != ts.dirty_tiles.end(); it++) {
			TileIndex tile = *it;

			assert(tile < MapSize());
			/* MP_VOID tiles can be terraformed but as tunnels and bridges
			 * cannot go under / over these tiles they don't need checking. */
			if (IsTileType(tile, MP_VOID)) continue;

			/* Find new heights of tile corners */
			int z_N = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(0, 0));
			int z_W = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(1, 0));
			int z_S = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(1, 1));
			int z_E = TerraformGetHeightOfTile(&ts, tile + TileDiffXY(0, 1));

			/* Find min and max height of tile */
			int z_min = min(min(z_N, z_W), min(z_S, z_E));
			int z_max = max(max(z_N, z_W), max(z_S, z_E));

			/* Compute tile slope */
			Slope tileh = (z_max > z_min + 1 ? SLOPE_STEEP : SLOPE_FLAT);
			if (z_W > z_min) tileh |= SLOPE_W;
			if (z_S > z_min) tileh |= SLOPE_S;
			if (z_E > z_min) tileh |= SLOPE_E;
			if (z_N > z_min) tileh |= SLOPE_N;

			if (pass == 0) {
				/* Check if bridge would take damage */
				if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) {
					int bridge_height = GetBridgeHeight(GetSouthernBridgeEnd(tile));

					/* Check if bridge would take damage. */
					if (direction == 1 && bridge_height <= z_max) {
						_terraform_err_tile = tile; ///< highlight the tile under the bridge
						return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
					}

					/* Is the bridge above not too high afterwards?
					 * @see tunnelbridge.h for a detailed discussion. */
					if (direction == -1 && bridge_height > (z_min + MAX_BRIDGE_HEIGHT)) {
						_terraform_err_tile = tile;
						return_cmd_error(STR_ERROR_BRIDGE_TOO_HIGH_AFTER_LOWER_LAND);
					}
				}
				/* Check if tunnel would take damage */
				if (direction == -1 && IsTunnelInWay(tile, z_min)) {
					_terraform_err_tile = tile; // highlight the tile above the tunnel
					return_cmd_error(STR_ERROR_EXCAVATION_WOULD_DAMAGE);
				}
			}

			/* Is the tile already cleared? */
			const ClearedObjectArea *coa = FindClearedObject(tile);
			bool indirectly_cleared = coa != NULL && coa->first_tile != tile;

			/* Check tiletype-specific things, and add extra-cost */
			const bool curr_gen = _generating_world;
			if (_game_mode == GM_EDITOR) _generating_world = true; // used to create green terraformed land
			DoCommandFlag tile_flags = flags | DC_AUTO | DC_FORCE_CLEAR_TILE;
			if (pass == 0) {
				tile_flags &= ~DC_EXEC;
				tile_flags |= DC_NO_MODIFY_TOWN_RATING;
			}
			CommandCost cost;
			if (indirectly_cleared) {
				cost = DoCommand(tile, 0, 0, tile_flags, CMD_LANDSCAPE_CLEAR);
			} else {
				cost = _tile_type_procs[GetTileType(tile)]->terraform_tile_proc(tile, tile_flags, z_min, tileh);
			}
			_generating_world = curr_gen;
			if (cost.Failed()) {
				_terraform_err_tile = tile;
				return cost;
			}
			if (pass == 1) total_cost.AddCost(cost);
		}
	}

	Company *c = Company::GetIfValid(_current_company);
	if (c != NULL && (int)GB(c->terraform_limit, 16, 16) < ts.tile_to_new_height.size()) {
		return_cmd_error(STR_ERROR_TERRAFORM_LIMIT_REACHED);
	}


	if (flags & DC_EXEC) {
		/* change the height */
		{
			for (std::map<TileIndex, int>::const_iterator it = ts.tile_to_new_height.begin();
					it != ts.tile_to_new_height.end(); it++) {
				TileIndex tile = it->first;
				int height = it->second;

				SetTileHeight(tile, (uint)height);
			}
		}

		/* finally mark the dirty tiles dirty */
		{
			MarkTilesDirty(ts);
		}

		if (c != NULL) c->terraform_limit -= ts.tile_to_new_height.size() << 16;
	}
	return total_cost;
}
	virtual int GetTileTargetHeight() const
	{
		return TileHeight(this->src_tile) + this->height_delta;
	}
Exemple #25
0
	ExtraViewportWindow(WindowDesc *desc, int window_number, TileIndex tile) : Window(desc)
	{
		this->InitNested(window_number);

		NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(WID_EV_VIEWPORT);
		nvp->InitializeViewport(this, 0, ZOOM_LVL_VIEWPORT);
		if (_settings_client.gui.zoom_min == ZOOM_LVL_VIEWPORT) this->DisableWidget(WID_EV_ZOOM_IN);

		Point pt;
		if (tile == INVALID_TILE) {
			/* No tile? Use center of main viewport. */
			const Window *w = FindWindowById(WC_MAIN_WINDOW, 0);

			/* center on same place as main window (zoom is maximum, no adjustment needed) */
			pt.x = w->viewport->scrollpos_x + w->viewport->virtual_width / 2;
			pt.y = w->viewport->scrollpos_y + w->viewport->virtual_height / 2;
		} else {
			pt = RemapCoords(TileX(tile) * TILE_SIZE + TILE_SIZE / 2, TileY(tile) * TILE_SIZE + TILE_SIZE / 2, TileHeight(tile));
		}

		this->viewport->scrollpos_x = pt.x - this->viewport->virtual_width / 2;
		this->viewport->scrollpos_y = pt.y - this->viewport->virtual_height / 2;
		this->viewport->dest_scrollpos_x = this->viewport->scrollpos_x;
		this->viewport->dest_scrollpos_y = this->viewport->scrollpos_y;
	}
Exemple #26
0
    ExtraViewportWindow(const WindowDesc *desc, int window_number, TileIndex tile) : Window()
    {
        this->InitNested(desc, window_number);

        NWidgetViewport *nvp = this->GetWidget<NWidgetViewport>(EVW_VIEWPORT);
        nvp->InitializeViewport(this, 0, ZOOM_LVL_NORMAL);
        this->DisableWidget(EVW_ZOOMIN);

        Point pt;
        if (tile == INVALID_TILE) {
            /* the main window with the main view */
            const Window *w = FindWindowById(WC_MAIN_WINDOW, 0);

            /* center on same place as main window (zoom is maximum, no adjustment needed) */
            pt.x = w->viewport->scrollpos_x + w->viewport->virtual_width / 2;
            pt.y = w->viewport->scrollpos_y + w->viewport->virtual_height / 2;
        } else {
            pt = RemapCoords(TileX(tile) * TILE_SIZE + TILE_SIZE / 2, TileY(tile) * TILE_SIZE + TILE_SIZE / 2, TileHeight(tile));
        }

        this->viewport->scrollpos_x = pt.x - this->viewport->virtual_width / 2;
        this->viewport->scrollpos_y = pt.y - this->viewport->virtual_height / 2;
        this->viewport->dest_scrollpos_x = this->viewport->scrollpos_x;
        this->viewport->dest_scrollpos_y = this->viewport->scrollpos_y;
    }
/**
 * Calculate how much to add to each height of a tile while copy-pasteing.
 * @param src_area Area we are copying from.
 * @param dst_area Area we are pasting at.
 * @param transformation Transformation to perform.
 * @param additional_height Extra amount of units to add.
 */
static inline int CalcCopyPasteHeightDelta(const GenericTileArea &src_area, const GenericTileArea &dst_area, DirTransformation transformation, int additional_height)
{
	GenericTileArea dst_corners(dst_area.tile, dst_area.w + 1, dst_area.h + 1);
	GenericTileIndex source_of_north = dst_corners.TransformedNorth(src_area.tile, InvertDirTransform(transformation));
	return TileHeight(dst_corners.tile) - TileHeight(source_of_north) + additional_height;
}