/** * Converts a given grayscale map to something that fits in OTTD map system * and create a map of that data. * @param img_width the with of the image in pixels/tiles * @param img_height the height of the image in pixels/tiles * @param map the input map */ static void GrayscaleToMapHeights(uint img_width, uint img_height, byte *map) { /* Defines the detail of the aspect ratio (to avoid doubles) */ const uint num_div = 16384; uint width, height; uint row, col; uint row_pad = 0, col_pad = 0; uint img_scale; uint img_row, img_col; TileIndex tile; /* Get map size and calculate scale and padding values */ switch (_settings_game.game_creation.heightmap_rotation) { default: NOT_REACHED(); case HM_COUNTER_CLOCKWISE: width = MapSizeX(); height = MapSizeY(); break; case HM_CLOCKWISE: width = MapSizeY(); height = MapSizeX(); break; } if ((img_width * num_div) / img_height > ((width * num_div) / height)) { /* Image is wider than map - center vertically */ img_scale = (width * num_div) / img_width; row_pad = (1 + height - ((img_height * img_scale) / num_div)) / 2; } else { /* Image is taller than map - center horizontally */ img_scale = (height * num_div) / img_height; col_pad = (1 + width - ((img_width * img_scale) / num_div)) / 2; } if (_settings_game.construction.freeform_edges) { for (uint x = 0; x < MapSizeX(); x++) MakeVoid(TileXY(x, 0)); for (uint y = 0; y < MapSizeY(); y++) MakeVoid(TileXY(0, y)); } /* Form the landscape */ for (row = 0; row < height; row++) { for (col = 0; col < width; col++) { switch (_settings_game.game_creation.heightmap_rotation) { default: NOT_REACHED(); case HM_COUNTER_CLOCKWISE: tile = TileXY(col, row); break; case HM_CLOCKWISE: tile = TileXY(row, col); break; } /* Check if current tile is within the 1-pixel map edge or padding regions */ if ((!_settings_game.construction.freeform_edges && DistanceFromEdge(tile) <= 1) || (row < row_pad) || (row >= (height - row_pad - (_settings_game.construction.freeform_edges ? 0 : 1))) || (col < col_pad) || (col >= (width - col_pad - (_settings_game.construction.freeform_edges ? 0 : 1)))) { SetTileHeight(tile, 0); } else { /* Use nearest neighbor resizing to scale map data. * We rotate the map 45 degrees (counter)clockwise */ img_row = (((row - row_pad) * num_div) / img_scale); switch (_settings_game.game_creation.heightmap_rotation) { default: NOT_REACHED(); case HM_COUNTER_CLOCKWISE: img_col = (((width - 1 - col - col_pad) * num_div) / img_scale); break; case HM_CLOCKWISE: img_col = (((col - col_pad) * num_div) / img_scale); break; } assert(img_row < img_height); assert(img_col < img_width); /* Colour scales from 0 to 255, OpenTTD height scales from 0 to 15 */ SetTileHeight(tile, map[img_row * img_width + img_col] / 16); } /* Only clear the tiles within the map area. */ if (TileX(tile) != MapMaxX() && TileY(tile) != MapMaxY() && (!_settings_game.construction.freeform_edges || (TileX(tile) != 0 && TileY(tile) != 0))) { MakeClear(tile, CLEAR_GRASS, 3); } } } }
static void ShipController(Ship *v) { uint32 r; const byte *b; Direction dir; Track track; TrackBits tracks; v->tick_counter++; v->current_order_time++; if (v->breakdown_ctr != 0) { if (v->breakdown_ctr <= 2) { HandleBrokenShip(v); return; } if (!v->current_order.IsType(OT_LOADING)) v->breakdown_ctr--; } if (v->vehstatus & VS_STOPPED) return; ProcessOrders(v); v->HandleLoading(); if (v->current_order.IsType(OT_LOADING)) return; CheckShipLeaveDepot(v); if (!ShipAccelerate(v)) return; GetNewVehiclePosResult gp = GetNewVehiclePos(v); if (v->state != TRACK_BIT_WORMHOLE) { /* Not on a bridge */ if (gp.old_tile == gp.new_tile) { /* Staying in tile */ if (v->IsInDepot()) { gp.x = v->x_pos; gp.y = v->y_pos; } else { /* Not inside depot */ r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y); if (HasBit(r, VETS_CANNOT_ENTER)) goto reverse_direction; /* A leave station order only needs one tick to get processed, so we can * always skip ahead. */ if (v->current_order.IsType(OT_LEAVESTATION)) { v->current_order.Free(); SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH); } else if (v->dest_tile != 0) { /* We have a target, let's see if we reached it... */ if (v->current_order.IsType(OT_GOTO_WAYPOINT) && DistanceManhattan(v->dest_tile, gp.new_tile) <= 3) { /* We got within 3 tiles of our target buoy, so let's skip to our * next order */ UpdateVehicleTimetable(v, true); v->IncrementOrderIndex(); v->current_order.MakeDummy(); } else { /* Non-buoy orders really need to reach the tile */ if (v->dest_tile == gp.new_tile) { if (v->current_order.IsType(OT_GOTO_DEPOT)) { if ((gp.x & 0xF) == 8 && (gp.y & 0xF) == 8) { VehicleEnterDepot(v); return; } } else if (v->current_order.IsType(OT_GOTO_STATION)) { v->last_station_visited = v->current_order.GetDestination(); /* Process station in the orderlist. */ Station *st = Station::Get(v->current_order.GetDestination()); if (st->facilities & FACIL_DOCK) { // ugly, ugly workaround for problem with ships able to drop off cargo at wrong stations ShipArrivesAt(v, st); v->BeginLoading(); } else { // leave stations without docks right aways v->current_order.MakeLeaveStation(); v->IncrementOrderIndex(); } } } } } } } else { DiagDirection diagdir; /* New tile */ if (TileX(gp.new_tile) >= MapMaxX() || TileY(gp.new_tile) >= MapMaxY()) { goto reverse_direction; } dir = ShipGetNewDirectionFromTiles(gp.new_tile, gp.old_tile); assert(dir == DIR_NE || dir == DIR_SE || dir == DIR_SW || dir == DIR_NW); diagdir = DirToDiagDir(dir); tracks = GetAvailShipTracks(gp.new_tile, diagdir); if (tracks == TRACK_BIT_NONE) goto reverse_direction; /* Choose a direction, and continue if we find one */ track = ChooseShipTrack(v, gp.new_tile, diagdir, tracks); if (track == INVALID_TRACK) goto reverse_direction; b = _ship_subcoord[diagdir][track]; gp.x = (gp.x & ~0xF) | b[0]; gp.y = (gp.y & ~0xF) | b[1]; /* Call the landscape function and tell it that the vehicle entered the tile */ r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y); if (HasBit(r, VETS_CANNOT_ENTER)) goto reverse_direction; if (!HasBit(r, VETS_ENTERED_WORMHOLE)) { v->tile = gp.new_tile; v->state = TrackToTrackBits(track); } v->direction = (Direction)b[2]; } } else { /* On a bridge */ if (!IsTileType(gp.new_tile, MP_TUNNELBRIDGE) || !HasBit(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) { v->x_pos = gp.x; v->y_pos = gp.y; VehicleMove(v, !(v->vehstatus & VS_HIDDEN)); return; } } /* update image of ship, as well as delta XY */ dir = ShipGetNewDirection(v, gp.x, gp.y); v->x_pos = gp.x; v->y_pos = gp.y; v->z_pos = GetSlopeZ(gp.x, gp.y); getout: v->UpdateViewport(true, true); return; reverse_direction: dir = ReverseDir(v->direction); v->direction = dir; goto getout; }
/** * Create an effect vehicle above a particular location. * @param x The x location on the map. * @param y The y location on the map. * @param z The offset from the ground. * @param type The type of effect vehicle. * @return The effect vehicle. */ EffectVehicle *CreateEffectVehicleAbove(int x, int y, int z, EffectVehicleType type) { int safe_x = Clamp(x, 0, MapMaxX() * TILE_SIZE); int safe_y = Clamp(y, 0, MapMaxY() * TILE_SIZE); return CreateEffectVehicle(x, y, GetSlopePixelZ(safe_x, safe_y) + z, type); }
/** * Is there a tunnel in the way in any direction? * @param tile the tile to search from. * @param z the 'z' to search on. * @return true if and only if there is a tunnel. */ bool IsTunnelInWay(TileIndex tile, int z) { return IsTunnelInWayDir(tile, z, (TileX(tile) > (MapMaxX() / 2)) ? DIAGDIR_NE : DIAGDIR_SW) || IsTunnelInWayDir(tile, z, (TileY(tile) > (MapMaxY() / 2)) ? DIAGDIR_NW : DIAGDIR_SE); }
static void MarkTilesDirty(TerraformerState &ts) { for (std::set<TileIndex>::const_iterator it = ts.dirty_tiles.begin(); it != ts.dirty_tiles.end(); it++) { TileIndex ti = *it; MarkTileDirtyByTile(ti); int height = TerraformGetHeightOfTile(&ts, ti); /* Now, if we alter the height of the map edge, we need to take care * about repainting the affected areas outside map as well. * Remember: * Outside map, we assume that our landscape descends to * height zero as fast as possible. * Those simulated tiles (they don't exist as datastructure, * only as concept in code) need to be repainted properly, * otherwise we will get ugly glitches. * * Furthermore, note that we have to take care about the possibility, * that landscape was higher before the change, * so also tiles a bit outside need to be repainted. */ int x = TileX(ti); int y = TileY(ti); if (x == 0) { if (y == 0) { /* Height of the northern corner is altered. */ for (int cx = 0; cx >= -height - 1; cx--) { for (int cy = 0; cy >= -height - 1; cy--) { /* This means, tiles in the sector north of that * corner need to be repainted. */ if (cx + cy >= -height - 2) { /* But only tiles that actually might have changed. */ MarkTileDirtyByTileOutsideMap(cx, cy); } } } } else if (y < (int)MapMaxY()) { for (int cx = 0; cx >= -height - 1; cx--) { MarkTileDirtyByTileOutsideMap(cx, y); } } else { for (int cx = 0; cx >= -height - 1; cx--) { for (int cy = (int)MapMaxY(); cy <= (int)MapMaxY() + height + 1; cy++) { if (cx + ((int)MapMaxY() - cy) >= -height - 2) { MarkTileDirtyByTileOutsideMap(cx, cy); } } } } } else if (x < (int)MapMaxX()) { if (y == 0) { for (int cy = 0; cy >= -height - 1; cy--) { MarkTileDirtyByTileOutsideMap(x, cy); } } else if (y < (int)MapMaxY()) { /* Nothing to be done here, we are inside the map. */ } else { for (int cy = (int)MapMaxY(); cy <= (int)MapMaxY() + height + 1; cy++) { MarkTileDirtyByTileOutsideMap(x, cy); } } } else { if (y == 0) { for (int cx = (int)MapMaxX(); cx <= (int)MapMaxX() + height + 1; cx++) { for (int cy = 0; cy >= -height - 1; cy--) { if (((int)MapMaxX() - cx) + cy >= -height - 2) { MarkTileDirtyByTileOutsideMap(cx, cy); } } } } else if (y < (int)MapMaxY()) { for (int cx = (int)MapMaxX(); cx <= (int)MapMaxX() + height + 1; cx++) { MarkTileDirtyByTileOutsideMap(cx, y); } } else { for (int cx = (int)MapMaxX(); cx <= (int)MapMaxX() + height + 1; cx++) { for (int cy = (int)MapMaxY(); cy <= (int)MapMaxY() + height + 1; cy++) { if (((int)MapMaxX() - cx) + ((int)MapMaxY() - cy) >= -height - 2) { MarkTileDirtyByTileOutsideMap(cx, cy); } } } } } } }
/** * Terraform the north corner of a tile to a specific height. * * @param ts TerraformerState. * @param tile Tile. * @param height Aimed height. * @return Error code or cost. */ static CommandCost TerraformTileHeight(TerraformerState *ts, TileIndex tile, int height) { assert(tile < MapSize()); /* Check range of destination height */ if (height < 0) return_cmd_error(STR_ERROR_ALREADY_AT_SEA_LEVEL); if (height > _settings_game.construction.max_heightlevel) return_cmd_error(STR_ERROR_TOO_HIGH); /* * Check if the terraforming has any effect. * This can only be true, if multiple corners of the start-tile are terraformed (i.e. the terraforming is done by towns/industries etc.). * In this case the terraforming should fail. (Don't know why.) */ if (height == TerraformGetHeightOfTile(ts, tile)) return CMD_ERROR; /* Check "too close to edge of map". Only possible when freeform-edges is off. */ uint x = TileX(tile); uint y = TileY(tile); if (!_settings_game.construction.freeform_edges && ((x <= 1) || (y <= 1) || (x >= MapMaxX() - 1) || (y >= MapMaxY() - 1))) { /* * Determine a sensible error tile */ if (x == 1) x = 0; if (y == 1) y = 0; _terraform_err_tile = TileXY(x, y); return_cmd_error(STR_ERROR_TOO_CLOSE_TO_EDGE_OF_MAP); } /* Mark incident tiles that are involved in the terraforming. */ TerraformAddDirtyTileAround(ts, tile); /* Store the height modification */ TerraformSetHeightOfTile(ts, tile, height); CommandCost total_cost(EXPENSES_CONSTRUCTION); /* Increment cost */ total_cost.AddCost(_price[PR_TERRAFORM]); /* Recurse to neighboured corners if height difference is larger than 1 */ { const TileIndexDiffC *ttm; TileIndex orig_tile = tile; static const TileIndexDiffC _terraform_tilepos[] = { { 1, 0}, // move to tile in SE {-2, 0}, // undo last move, and move to tile in NW { 1, 1}, // undo last move, and move to tile in SW { 0, -2} // undo last move, and move to tile in NE }; for (ttm = _terraform_tilepos; ttm != endof(_terraform_tilepos); ttm++) { tile += ToTileIndexDiff(*ttm); if (tile >= MapSize()) continue; /* Make sure we don't wrap around the map */ if (Delta(TileX(orig_tile), TileX(tile)) == MapSizeX() - 1) continue; if (Delta(TileY(orig_tile), TileY(tile)) == MapSizeY() - 1) continue; /* Get TileHeight of neighboured tile as of current terraform progress */ int r = TerraformGetHeightOfTile(ts, tile); int height_diff = height - r; /* Is the height difference to the neighboured corner greater than 1? */ if (abs(height_diff) > 1) { /* Terraform the neighboured corner. The resulting height difference should be 1. */ height_diff += (height_diff < 0 ? 1 : -1); CommandCost cost = TerraformTileHeight(ts, tile, r + height_diff); if (cost.Failed()) return cost; total_cost.AddCost(cost); } } } return total_cost; }