00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013
00014 #include "cmd_helper.h"
00015 #include "command_func.h"
00016 #include "landscape.h"
00017 #include "bridge_map.h"
00018 #include "town.h"
00019 #include "waypoint_base.h"
00020 #include "pathfinder/yapf/yapf_cache.h"
00021 #include "strings_func.h"
00022 #include "functions.h"
00023 #include "window_func.h"
00024 #include "date_func.h"
00025 #include "vehicle_func.h"
00026 #include "string_func.h"
00027 #include "company_func.h"
00028 #include "newgrf_station.h"
00029 #include "company_base.h"
00030 #include "water.h"
00031
00032 #include "table/strings.h"
00033
00037 void Waypoint::UpdateVirtCoord()
00038 {
00039 Point pt = RemapCoords2(TileX(this->xy) * TILE_SIZE, TileY(this->xy) * TILE_SIZE);
00040 SetDParam(0, this->index);
00041 this->sign.UpdatePosition(pt.x, pt.y - 0x20, STR_VIEWPORT_WAYPOINT);
00042
00043 InvalidateWindowData(WC_WAYPOINT_VIEW, this->index);
00044 }
00045
00053 static Waypoint *FindDeletedWaypointCloseTo(TileIndex tile, StringID str, CompanyID cid)
00054 {
00055 Waypoint *wp, *best = NULL;
00056 uint thres = 8;
00057
00058 FOR_ALL_WAYPOINTS(wp) {
00059 if (!wp->IsInUse() && wp->string_id == str && wp->owner == cid) {
00060 uint cur_dist = DistanceManhattan(tile, wp->xy);
00061
00062 if (cur_dist < thres) {
00063 thres = cur_dist;
00064 best = wp;
00065 }
00066 }
00067 }
00068
00069 return best;
00070 }
00071
00079 Axis GetAxisForNewWaypoint(TileIndex tile)
00080 {
00081
00082 if (IsRailWaypointTile(tile)) return GetRailStationAxis(tile);
00083
00084
00085 if (!IsTileType(tile, MP_RAILWAY) || GetRailTileType(tile) != RAIL_TILE_NORMAL) return INVALID_AXIS;
00086
00087 switch (GetTrackBits(tile)) {
00088 case TRACK_BIT_X: return AXIS_X;
00089 case TRACK_BIT_Y: return AXIS_Y;
00090 default: return INVALID_AXIS;
00091 }
00092 }
00093
00094 CommandCost ClearTile_Station(TileIndex tile, DoCommandFlag flags);
00095
00102 static CommandCost IsValidTileForWaypoint(TileIndex tile, Axis axis, StationID *waypoint)
00103 {
00104
00105
00106
00107 if (waypoint != NULL && IsTileType(tile, MP_STATION)) {
00108 if (!IsRailWaypoint(tile)) {
00109 return ClearTile_Station(tile, DC_AUTO);
00110 } else {
00111 StationID wp = GetStationIndex(tile);
00112 if (*waypoint == INVALID_STATION) {
00113 *waypoint = wp;
00114 } else if (*waypoint != wp) {
00115 return_cmd_error(STR_ERROR_WAYPOINT_ADJOINS_MORE_THAN_ONE_EXISTING);
00116 }
00117 }
00118 }
00119
00120 if (GetAxisForNewWaypoint(tile) != axis) return_cmd_error(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK);
00121
00122 Owner owner = GetTileOwner(tile);
00123 CommandCost ret = CheckOwnership(owner);
00124 if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile);
00125 if (ret.Failed()) return ret;
00126
00127 Slope tileh = GetTileSlope(tile, NULL);
00128 if (tileh != SLOPE_FLAT &&
00129 (!_settings_game.construction.build_on_slopes || IsSteepSlope(tileh) || !(tileh & (0x3 << axis)) || !(tileh & ~(0x3 << axis)))) {
00130 return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
00131 }
00132
00133 if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
00134
00135 return CommandCost();
00136 }
00137
00138 extern void GetStationLayout(byte *layout, int numtracks, int plat_len, const StationSpec *statspec);
00139 extern CommandCost FindJoiningWaypoint(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Waypoint **wp);
00140 extern CommandCost CanExpandRailStation(const BaseStation *st, TileArea &new_ta, Axis axis);
00141
00158 CommandCost CmdBuildRailWaypoint(TileIndex start_tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00159 {
00160
00161 Axis axis = Extract<Axis, 4, 1>(p1);
00162 byte width = GB(p1, 8, 8);
00163 byte height = GB(p1, 16, 8);
00164 bool adjacent = HasBit(p1, 24);
00165
00166 StationClassID spec_class = Extract<StationClassID, 0, 8>(p2);
00167 byte spec_index = GB(p2, 8, 8);
00168 StationID station_to_join = GB(p2, 16, 16);
00169
00170
00171 if (spec_class != STAT_CLASS_WAYP) return CMD_ERROR;
00172 if (spec_index >= StationClass::GetCount(spec_class)) return CMD_ERROR;
00173
00174
00175 byte count = axis == AXIS_X ? height : width;
00176
00177 if ((axis == AXIS_X ? width : height) != 1) return CMD_ERROR;
00178 if (count == 0 || count > _settings_game.station.station_spread) return CMD_ERROR;
00179
00180 bool reuse = (station_to_join != NEW_STATION);
00181 if (!reuse) station_to_join = INVALID_STATION;
00182 bool distant_join = (station_to_join != INVALID_STATION);
00183
00184 if (distant_join && (!_settings_game.station.distant_join_stations || !Waypoint::IsValidID(station_to_join))) return CMD_ERROR;
00185
00186
00187 StationID est = INVALID_STATION;
00188
00189
00190 TileIndexDiff offset = TileOffsByDiagDir(AxisToDiagDir(OtherAxis(axis)));
00191 for (int i = 0; i < count; i++) {
00192 TileIndex tile = start_tile + i * offset;
00193 CommandCost ret = IsValidTileForWaypoint(tile, axis, &est);
00194 if (ret.Failed()) return ret;
00195 }
00196
00197 Waypoint *wp = NULL;
00198 TileArea new_location(TileArea(start_tile, width, height));
00199 CommandCost ret = FindJoiningWaypoint(est, station_to_join, adjacent, new_location, &wp);
00200 if (ret.Failed()) return ret;
00201
00202
00203 TileIndex center_tile = start_tile + (count / 2) * offset;
00204 if (wp == NULL && reuse) wp = FindDeletedWaypointCloseTo(center_tile, STR_SV_STNAME_WAYPOINT, _current_company);
00205
00206 if (wp != NULL) {
00207
00208 if (wp->owner != _current_company) return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_WAYPOINT);
00209
00210
00211 if (wp->train_station.tile != INVALID_TILE) {
00212 CommandCost ret = CanExpandRailStation(wp, new_location, axis);
00213 if (ret.Failed()) return ret;
00214 }
00215
00216 CommandCost ret = wp->rect.BeforeAddRect(start_tile, width, height, StationRect::ADD_TEST);
00217 if (ret.Failed()) return ret;
00218 } else {
00219
00220 if (!Waypoint::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_STATIONS_LOADING);
00221 }
00222
00223 if (flags & DC_EXEC) {
00224 if (wp == NULL) {
00225 wp = new Waypoint(start_tile);
00226 } else if (!wp->IsInUse()) {
00227
00228 wp->xy = start_tile;
00229 }
00230 wp->owner = GetTileOwner(start_tile);
00231
00232 wp->rect.BeforeAddRect(start_tile, width, height, StationRect::ADD_TRY);
00233
00234 wp->delete_ctr = 0;
00235 wp->facilities |= FACIL_TRAIN;
00236 wp->build_date = _date;
00237 wp->string_id = STR_SV_STNAME_WAYPOINT;
00238 wp->train_station = new_location;
00239
00240 if (wp->town == NULL) MakeDefaultName(wp);
00241
00242 wp->UpdateVirtCoord();
00243
00244 const StationSpec *spec = StationClass::Get(spec_class, spec_index);
00245 byte *layout_ptr = AllocaM(byte, count);
00246 if (spec == NULL) {
00247
00248 memset(layout_ptr, 0, count);
00249 } else {
00250
00251 GetStationLayout(layout_ptr, count, 1, spec);
00252 }
00253 byte map_spec_index = AllocateSpecToStation(spec, wp, true);
00254
00255 for (int i = 0; i < count; i++) {
00256 TileIndex tile = start_tile + i * offset;
00257 byte old_specindex = HasStationTileRail(tile) ? GetCustomStationSpecIndex(tile) : 0;
00258 bool reserved = IsTileType(tile, MP_RAILWAY) ?
00259 HasBit(GetRailReservationTrackBits(tile), AxisToTrack(axis)) :
00260 HasStationReservation(tile);
00261 MakeRailWaypoint(tile, wp->owner, wp->index, axis, layout_ptr[i], GetRailType(tile));
00262 SetCustomStationSpecIndex(tile, map_spec_index);
00263 SetRailStationReservation(tile, reserved);
00264 MarkTileDirtyByTile(tile);
00265
00266 DeallocateSpecFromStation(wp, old_specindex);
00267 YapfNotifyTrackLayoutChange(tile, AxisToTrack(axis));
00268 }
00269 }
00270
00271 return CommandCost(EXPENSES_CONSTRUCTION, count * _price[PR_BUILD_WAYPOINT_RAIL]);
00272 }
00273
00283 CommandCost CmdBuildBuoy(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00284 {
00285 if (tile == 0 || !HasTileWaterGround(tile)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
00286 if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
00287
00288 if (GetTileSlope(tile, NULL) != SLOPE_FLAT) return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
00289
00290
00291 Waypoint *wp = FindDeletedWaypointCloseTo(tile, STR_SV_STNAME_BUOY, OWNER_NONE);
00292 if (wp == NULL && !Waypoint::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_STATIONS_LOADING);
00293
00294 CommandCost cost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_WAYPOINT_BUOY]);
00295 if (!IsWaterTile(tile)) {
00296 CommandCost ret = DoCommand(tile, 0, 0, flags | DC_AUTO, CMD_LANDSCAPE_CLEAR);
00297 if (ret.Failed()) return ret;
00298 cost.AddCost(ret);
00299 }
00300
00301 if (flags & DC_EXEC) {
00302 if (wp == NULL) {
00303 wp = new Waypoint(tile);
00304 } else {
00305
00306 wp->xy = tile;
00307 InvalidateWindowData(WC_WAYPOINT_VIEW, wp->index);
00308 }
00309 wp->rect.BeforeAddTile(tile, StationRect::ADD_TRY);
00310
00311 wp->string_id = STR_SV_STNAME_BUOY;
00312
00313 wp->facilities |= FACIL_DOCK;
00314 wp->owner = OWNER_NONE;
00315
00316 wp->build_date = _date;
00317
00318 if (wp->town == NULL) MakeDefaultName(wp);
00319
00320 MakeBuoy(tile, wp->index, GetWaterClass(tile));
00321
00322 wp->UpdateVirtCoord();
00323 InvalidateWindowData(WC_WAYPOINT_VIEW, wp->index);
00324 }
00325
00326 return cost;
00327 }
00328
00336 CommandCost RemoveBuoy(TileIndex tile, DoCommandFlag flags)
00337 {
00338
00339 if (!Company::IsValidID(_current_company) && !(flags & DC_BANKRUPT)) return_cmd_error(INVALID_STRING_ID);
00340
00341 Waypoint *wp = Waypoint::GetByTile(tile);
00342
00343 if (HasStationInUse(wp->index, false, _current_company)) return_cmd_error(STR_ERROR_BUOY_IS_IN_USE);
00344
00345 if (!(flags & DC_BANKRUPT)) {
00346 CommandCost ret = EnsureNoVehicleOnGround(tile);
00347 if (ret.Failed()) return ret;
00348 }
00349
00350 if (flags & DC_EXEC) {
00351 wp->facilities &= ~FACIL_DOCK;
00352
00353 InvalidateWindowData(WC_WAYPOINT_VIEW, wp->index);
00354
00355
00356
00357
00358 MakeWaterKeepingClass(tile, GetTileOwner(tile));
00359
00360 wp->rect.AfterRemoveTile(wp, tile);
00361
00362 wp->UpdateVirtCoord();
00363 wp->delete_ctr = 0;
00364 }
00365
00366 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_WAYPOINT_BUOY]);
00367 }
00368
00369
00370 static bool IsUniqueWaypointName(const char *name)
00371 {
00372 const Waypoint *wp;
00373
00374 FOR_ALL_WAYPOINTS(wp) {
00375 if (wp->name != NULL && strcmp(wp->name, name) == 0) return false;
00376 }
00377
00378 return true;
00379 }
00380
00390 CommandCost CmdRenameWaypoint(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
00391 {
00392 Waypoint *wp = Waypoint::GetIfValid(p1);
00393 if (wp == NULL) return CMD_ERROR;
00394
00395 if (wp->owner != OWNER_NONE) {
00396 CommandCost ret = CheckOwnership(wp->owner);
00397 if (ret.Failed()) return ret;
00398 }
00399
00400 bool reset = StrEmpty(text);
00401
00402 if (!reset) {
00403 if (Utf8StringLength(text) >= MAX_LENGTH_STATION_NAME_CHARS) return CMD_ERROR;
00404 if (!IsUniqueWaypointName(text)) return_cmd_error(STR_ERROR_NAME_MUST_BE_UNIQUE);
00405 }
00406
00407 if (flags & DC_EXEC) {
00408 free(wp->name);
00409 wp->name = reset ? NULL : strdup(text);
00410
00411 wp->UpdateVirtCoord();
00412 }
00413 return CommandCost();
00414 }