/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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); }
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); }
/** * 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 ©_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); }
/** * 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); } } } }
/** * 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; }
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 ); }
/** * 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]); }
/** * 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--; } }
/** 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]); }
/* 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; }
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; }
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; }