Files @ r26254:4dd185cf8a2d
Branch filter:

Location: cpp/openttd-patchpack/source/src/waypoint_cmd.cpp - annotation

Theleruby
Merge with master
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
r12768:980ae0491352
r12768:980ae0491352
r12768:980ae0491352
r12768:980ae0491352
r12768:980ae0491352
r12768:980ae0491352
r12768:980ae0491352
r18845:66bf168f1100
r11348:d08d1098a28e
r11348:d08d1098a28e
r11348:d08d1098a28e
r11348:d08d1098a28e
r11348:d08d1098a28e
r11348:d08d1098a28e
r11348:d08d1098a28e
r12465:c1b760a56db7
r13840:e35c72858ba2
r11348:d08d1098a28e
r17267:4f82ca9bd495
r23437:6ce81d75a8ed
r11348:d08d1098a28e
r11348:d08d1098a28e
r11348:d08d1098a28e
r11348:d08d1098a28e
r11348:d08d1098a28e
r11348:d08d1098a28e
r13176:254c27d58bd6
r12476:b14b8e966cd2
r18559:ff940f072d80
r26094:7572e88decb3
r26102:502baaa2877d
r11348:d08d1098a28e
r11348:d08d1098a28e
r11348:d08d1098a28e
r21383:942c32fb8b0e
r21383:942c32fb8b0e
r11348:d08d1098a28e
r12381:5e06b887e97b
r12381:5e06b887e97b
r12381:5e06b887e97b
r11348:d08d1098a28e
r12381:5e06b887e97b
r23942:2580b984b22e
r23942:2580b984b22e
r12381:5e06b887e97b
r18465:95d781aa9dc8
r23942:2580b984b22e
r23942:2580b984b22e
r23942:2580b984b22e
r12570:7e7470a9c171
r12570:7e7470a9c171
r11348:d08d1098a28e
r11348:d08d1098a28e
r11348:d08d1098a28e
r23787:94603685a34b
r23787:94603685a34b
r23787:94603685a34b
r23787:94603685a34b
r23787:94603685a34b
r23787:94603685a34b
r23787:94603685a34b
r23787:94603685a34b
r23787:94603685a34b
r23787:94603685a34b
r23787:94603685a34b
r11348:d08d1098a28e
r11348:d08d1098a28e
r12476:b14b8e966cd2
r14135:449354db4961
r12476:b14b8e966cd2
r11348:d08d1098a28e
r14135:449354db4961
r11348:d08d1098a28e
r23954:8e43140b9a66
r11348:d08d1098a28e
r11348:d08d1098a28e
r23954:8e43140b9a66
r14135:449354db4961
r11348:d08d1098a28e
r11348:d08d1098a28e
r11348:d08d1098a28e
r11348:d08d1098a28e
r11348:d08d1098a28e
r11348:d08d1098a28e
r11348:d08d1098a28e
r11348:d08d1098a28e
r11348:d08d1098a28e
r11348:d08d1098a28e
r11348:d08d1098a28e
r11348:d08d1098a28e
r12545:0db872ffb197
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r17624:f2c5f47dceaa
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12545:0db872ffb197
r12545:0db872ffb197
r12545:0db872ffb197
r13060:e57594b0ca84
r11348:d08d1098a28e
r12552:8db41d79d6a8
r11348:d08d1098a28e
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r23607:36c15679007d
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r11348:d08d1098a28e
r11348:d08d1098a28e
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r11348:d08d1098a28e
r14805:8c416234ced2
r14888:176181db43bf
r14721:f3e02434b6fb
r11348:d08d1098a28e
r18247:d176683304d1
r11348:d08d1098a28e
r11348:d08d1098a28e
r11725:57bc99fdc1bc
r11348:d08d1098a28e
r11348:d08d1098a28e
r21736:0da55aa599c8
r11348:d08d1098a28e
r12545:0db872ffb197
r12545:0db872ffb197
r12545:0db872ffb197
r25365:e85b6eb3c9d9
r12552:8db41d79d6a8
r14683:3efbea14e64e
r12545:0db872ffb197
r15610:623a23fb6560
r15610:623a23fb6560
r12545:0db872ffb197
r26095:00c14d52e378
r12545:0db872ffb197
r26112:8e77e1979a31
r26112:8e77e1979a31
r26112:8e77e1979a31
r26112:8e77e1979a31
r26112:8e77e1979a31
r26112:8e77e1979a31
r26112:8e77e1979a31
r13057:58af81fcdcf8
r12545:0db872ffb197
r26112:8e77e1979a31
r12545:0db872ffb197
r26169:fdb7183fad74
r12545:0db872ffb197
r12545:0db872ffb197
r19263:8bc163eb7eb2
r12545:0db872ffb197
r12545:0db872ffb197
r12545:0db872ffb197
r12545:0db872ffb197
r12545:0db872ffb197
r12545:0db872ffb197
r12545:0db872ffb197
r12554:54cdb18a4143
r12554:54cdb18a4143
r12554:54cdb18a4143
r12554:54cdb18a4143
r12554:54cdb18a4143
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12545:0db872ffb197
r12545:0db872ffb197
r12545:0db872ffb197
r12545:0db872ffb197
r17204:d124a8d2470e
r12545:0db872ffb197
r12545:0db872ffb197
r12545:0db872ffb197
r23607:36c15679007d
r23617:216c443321e5
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r11348:d08d1098a28e
r12545:0db872ffb197
r23607:36c15679007d
r12552:8db41d79d6a8
r23607:36c15679007d
r14135:449354db4961
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r14135:449354db4961
r14683:3efbea14e64e
r14683:3efbea14e64e
r14683:3efbea14e64e
r14683:3efbea14e64e
r12552:8db41d79d6a8
r14752:ebcf936bf2ae
r14752:ebcf936bf2ae
r12552:8db41d79d6a8
r14135:449354db4961
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r11348:d08d1098a28e
r11348:d08d1098a28e
r23607:36c15679007d
r12545:0db872ffb197
r12545:0db872ffb197
r11348:d08d1098a28e
r12545:0db872ffb197
r11348:d08d1098a28e
r12545:0db872ffb197
r12515:abd5af2c2638
r12545:0db872ffb197
r11348:d08d1098a28e
r12406:d60713d1c3ad
r12456:451e8e6b0dc4
r11348:d08d1098a28e
r12462:a7cb564c6c56
r12552:8db41d79d6a8
r11348:d08d1098a28e
r23607:36c15679007d
r11348:d08d1098a28e
r12381:5e06b887e97b
r12545:0db872ffb197
r19263:8bc163eb7eb2
r12545:0db872ffb197
r23607:36c15679007d
r12545:0db872ffb197
r12545:0db872ffb197
r12545:0db872ffb197
r12545:0db872ffb197
r12545:0db872ffb197
r12545:0db872ffb197
r12545:0db872ffb197
r12545:0db872ffb197
r18559:ff940f072d80
r12545:0db872ffb197
r12545:0db872ffb197
r14354:83c45130f3ef
r18559:ff940f072d80
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12552:8db41d79d6a8
r12545:0db872ffb197
r12545:0db872ffb197
r12545:0db872ffb197
r12545:0db872ffb197
r12545:0db872ffb197
r12552:8db41d79d6a8
r12545:0db872ffb197
r12545:0db872ffb197
r18559:ff940f072d80
r11348:d08d1098a28e
r11348:d08d1098a28e
r26194:f7347205838e
r11348:d08d1098a28e
r11348:d08d1098a28e
r15610:623a23fb6560
r15610:623a23fb6560
r26095:00c14d52e378
r17059:dd74f9982768
r13057:58af81fcdcf8
r12476:b14b8e966cd2
r26112:8e77e1979a31
r12476:b14b8e966cd2
r16552:2145cf9259fa
r21736:0da55aa599c8
r12476:b14b8e966cd2
r20805:06eb3eae002c
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r14135:449354db4961
r23607:36c15679007d
r12476:b14b8e966cd2
r26194:f7347205838e
r16046:d66d1b4228b3
r26118:2b515956558b
r16046:d66d1b4228b3
r16046:d66d1b4228b3
r16046:d66d1b4228b3
r16046:d66d1b4228b3
r12476:b14b8e966cd2
r23607:36c15679007d
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12515:abd5af2c2638
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r23607:36c15679007d
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r23760:58e1a032cdaf
r22574:2b47621019bd
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r16046:d66d1b4228b3
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r15886:9bf3f985d8c3
r12476:b14b8e966cd2
r14721:f3e02434b6fb
r14721:f3e02434b6fb
r14721:f3e02434b6fb
r14721:f3e02434b6fb
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12515:abd5af2c2638
r12515:abd5af2c2638
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r26194:f7347205838e
r12476:b14b8e966cd2
r12476:b14b8e966cd2
r17624:f2c5f47dceaa
r17624:f2c5f47dceaa
r17624:f2c5f47dceaa
r17624:f2c5f47dceaa
r17624:f2c5f47dceaa
r25561:3defb050f30b
r11348:d08d1098a28e
r23954:8e43140b9a66
r24214:a65c412aafcc
r11348:d08d1098a28e
r11348:d08d1098a28e
r11348:d08d1098a28e
r11348:d08d1098a28e
r11348:d08d1098a28e
r11348:d08d1098a28e
r11348:d08d1098a28e
r26095:00c14d52e378
r26112:8e77e1979a31
r13057:58af81fcdcf8
r13057:58af81fcdcf8
r11348:d08d1098a28e
r26112:8e77e1979a31
r11348:d08d1098a28e
r26112:8e77e1979a31
r23607:36c15679007d
r14805:8c416234ced2
r14805:8c416234ced2
r14805:8c416234ced2
r14805:8c416234ced2
r14805:8c416234ced2
r11348:d08d1098a28e
r25562:30716ba6a396
r11348:d08d1098a28e
r11348:d08d1098a28e
r16674:a2c7f634c52e
r12622:202e83a6cee7
r11348:d08d1098a28e
r11348:d08d1098a28e
r11348:d08d1098a28e
r24214:a65c412aafcc
r24214:a65c412aafcc
r24214:a65c412aafcc
r24214:a65c412aafcc
r24214:a65c412aafcc
r11348:d08d1098a28e
r12381:5e06b887e97b
r11348:d08d1098a28e
r11348:d08d1098a28e
r11348:d08d1098a28e
/*
 * This file is part of OpenTTD.
 * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
 * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
 */

/** @file waypoint_cmd.cpp %Command Handling for waypoints. */

#include "stdafx.h"

#include "command_func.h"
#include "landscape.h"
#include "bridge_map.h"
#include "town.h"
#include "waypoint_base.h"
#include "pathfinder/yapf/yapf_cache.h"
#include "strings_func.h"
#include "viewport_func.h"
#include "viewport_kdtree.h"
#include "window_func.h"
#include "date_func.h"
#include "vehicle_func.h"
#include "string_func.h"
#include "company_func.h"
#include "newgrf_station.h"
#include "company_base.h"
#include "water.h"
#include "company_gui.h"
#include "waypoint_cmd.h"
#include "landscape_cmd.h"

#include "table/strings.h"

#include "safeguards.h"

/**
 * Update the virtual coords needed to draw the waypoint sign.
 */
void Waypoint::UpdateVirtCoord()
{
	Point pt = RemapCoords2(TileX(this->xy) * TILE_SIZE, TileY(this->xy) * TILE_SIZE);
	if (this->sign.kdtree_valid) _viewport_sign_kdtree.Remove(ViewportSignKdtreeItem::MakeWaypoint(this->index));

	SetDParam(0, this->index);
	this->sign.UpdatePosition(pt.x, pt.y - 32 * ZOOM_LVL_BASE, STR_VIEWPORT_WAYPOINT);

	_viewport_sign_kdtree.Insert(ViewportSignKdtreeItem::MakeWaypoint(this->index));

	/* Recenter viewport */
	InvalidateWindowData(WC_WAYPOINT_VIEW, this->index);
}

/**
 * Move the waypoint main coordinate somewhere else.
 * @param new_xy new tile location of the sign
 */
void Waypoint::MoveSign(TileIndex new_xy)
{
	if (this->xy == new_xy) return;

	this->BaseStation::MoveSign(new_xy);
}

/**
 * Find a deleted waypoint close to a tile.
 * @param tile to search from
 * @param str  the string to get the 'type' of
 * @param cid previous owner of the waypoint
 * @return the deleted nearby waypoint
 */
static Waypoint *FindDeletedWaypointCloseTo(TileIndex tile, StringID str, CompanyID cid)
{
	Waypoint *best = nullptr;
	uint thres = 8;

	for (Waypoint *wp : Waypoint::Iterate()) {
		if (!wp->IsInUse() && wp->string_id == str && wp->owner == cid) {
			uint cur_dist = DistanceManhattan(tile, wp->xy);

			if (cur_dist < thres) {
				thres = cur_dist;
				best = wp;
			}
		}
	}

	return best;
}

/**
 * Get the axis for a new waypoint. This means that if it is a valid
 * tile to build a waypoint on it returns a valid Axis, otherwise an
 * invalid one.
 * @param tile the tile to look at.
 * @return the axis for the to-be-build waypoint.
 */
Axis GetAxisForNewWaypoint(TileIndex tile)
{
	/* The axis for rail waypoints is easy. */
	if (IsRailWaypointTile(tile)) return GetRailStationAxis(tile);

	/* Non-plain rail type, no valid axis for waypoints. */
	if (!IsTileType(tile, MP_RAILWAY) || GetRailTileType(tile) != RAIL_TILE_NORMAL) return INVALID_AXIS;

	switch (GetTrackBits(tile)) {
		case TRACK_BIT_X: return AXIS_X;
		case TRACK_BIT_Y: return AXIS_Y;
		default:          return INVALID_AXIS;
	}
}

extern CommandCost ClearTile_Station(TileIndex tile, DoCommandFlag flags);

/**
 * Check whether the given tile is suitable for a waypoint.
 * @param tile the tile to check for suitability
 * @param axis the axis of the waypoint
 * @param waypoint Waypoint the waypoint to check for is already joined to. If we find another waypoint it can join to it will throw an error.
 */
static CommandCost IsValidTileForWaypoint(TileIndex tile, Axis axis, StationID *waypoint)
{
	/* if waypoint is set, then we have special handling to allow building on top of already existing waypoints.
	 * so waypoint points to INVALID_STATION if we can build on any waypoint.
	 * Or it points to a waypoint if we're only allowed to build on exactly that waypoint. */
	if (waypoint != nullptr && IsTileType(tile, MP_STATION)) {
		if (!IsRailWaypoint(tile)) {
			return ClearTile_Station(tile, DC_AUTO); // get error message
		} else {
			StationID wp = GetStationIndex(tile);
			if (*waypoint == INVALID_STATION) {
				*waypoint = wp;
			} else if (*waypoint != wp) {
				return_cmd_error(STR_ERROR_WAYPOINT_ADJOINS_MORE_THAN_ONE_EXISTING);
			}
		}
	}

	if (GetAxisForNewWaypoint(tile) != axis) return_cmd_error(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK);

	Owner owner = GetTileOwner(tile);
	CommandCost ret = CheckOwnership(owner);
	if (ret.Succeeded()) ret = EnsureNoVehicleOnGround(tile);
	if (ret.Failed()) return ret;

	Slope tileh = GetTileSlope(tile);
	if (tileh != SLOPE_FLAT &&
			(!_settings_game.construction.build_on_slopes || IsSteepSlope(tileh) || !(tileh & (0x3 << axis)) || !(tileh & ~(0x3 << axis)))) {
		return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
	}

	if (IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);

	return CommandCost();
}

extern void GetStationLayout(byte *layout, uint numtracks, uint plat_len, const StationSpec *statspec);
extern CommandCost FindJoiningWaypoint(StationID existing_station, StationID station_to_join, bool adjacent, TileArea ta, Waypoint **wp);
extern CommandCost CanExpandRailStation(const BaseStation *st, TileArea &new_ta, Axis axis);

/**
 * Convert existing rail to waypoint. Eg build a waypoint station over
 * piece of rail
 * @param flags type of operation
 * @param start_tile northern most tile where waypoint will be built
 * @param axis orientation (Axis)
 * @param width width of waypoint
 * @param height height of waypoint
 * @param spec_class custom station class
 * @param spec_index custom station id
 * @param station_to_join station ID to join (NEW_STATION if build new one)
 * @param adjacent allow waypoints directly adjacent to other waypoints.
 * @return the cost of this operation or an error
 */
CommandCost CmdBuildRailWaypoint(DoCommandFlag flags, TileIndex start_tile, Axis axis, byte width, byte height, StationClassID spec_class, byte spec_index, StationID station_to_join, bool adjacent)
{
	if (!IsValidAxis(axis)) return CMD_ERROR;
	/* Check if the given station class is valid */
	if (spec_class != STAT_CLASS_WAYP) return CMD_ERROR;
	if (spec_index >= StationClass::Get(spec_class)->GetSpecCount()) return CMD_ERROR;

	/* The number of parts to build */
	byte count = axis == AXIS_X ? height : width;

	if ((axis == AXIS_X ? width : height) != 1) return CMD_ERROR;
	if (count == 0 || count > _settings_game.station.station_spread) return CMD_ERROR;

	bool reuse = (station_to_join != NEW_STATION);
	if (!reuse) station_to_join = INVALID_STATION;
	bool distant_join = (station_to_join != INVALID_STATION);

	if (distant_join && (!_settings_game.station.distant_join_stations || !Waypoint::IsValidID(station_to_join))) return CMD_ERROR;

	/* Make sure the area below consists of clear tiles. (OR tiles belonging to a certain rail station) */
	StationID est = INVALID_STATION;

	/* Check whether the tiles we're building on are valid rail or not. */
	TileIndexDiff offset = TileOffsByDiagDir(AxisToDiagDir(OtherAxis(axis)));
	for (int i = 0; i < count; i++) {
		TileIndex tile = start_tile + i * offset;
		CommandCost ret = IsValidTileForWaypoint(tile, axis, &est);
		if (ret.Failed()) return ret;
	}

	Waypoint *wp = nullptr;
	TileArea new_location(start_tile, width, height);
	CommandCost ret = FindJoiningWaypoint(est, station_to_join, adjacent, new_location, &wp);
	if (ret.Failed()) return ret;

	/* Check if there is an already existing, deleted, waypoint close to us that we can reuse. */
	TileIndex center_tile = start_tile + (count / 2) * offset;
	if (wp == nullptr && reuse) wp = FindDeletedWaypointCloseTo(center_tile, STR_SV_STNAME_WAYPOINT, _current_company);

	if (wp != nullptr) {
		/* Reuse an existing waypoint. */
		if (wp->owner != _current_company) return_cmd_error(STR_ERROR_TOO_CLOSE_TO_ANOTHER_WAYPOINT);

		/* check if we want to expand an already existing waypoint? */
		if (wp->train_station.tile != INVALID_TILE) {
			CommandCost ret = CanExpandRailStation(wp, new_location, axis);
			if (ret.Failed()) return ret;
		}

		CommandCost ret = wp->rect.BeforeAddRect(start_tile, width, height, StationRect::ADD_TEST);
		if (ret.Failed()) return ret;
	} else {
		/* allocate and initialize new waypoint */
		if (!Waypoint::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_STATIONS_LOADING);
	}

	if (flags & DC_EXEC) {
		if (wp == nullptr) {
			wp = new Waypoint(start_tile);
		} else if (!wp->IsInUse()) {
			/* Move existing (recently deleted) waypoint to the new location */
			wp->xy = start_tile;
		}
		wp->owner = GetTileOwner(start_tile);

		wp->rect.BeforeAddRect(start_tile, width, height, StationRect::ADD_TRY);

		wp->delete_ctr = 0;
		wp->facilities |= FACIL_TRAIN;
		wp->build_date = _date;
		wp->string_id = STR_SV_STNAME_WAYPOINT;
		wp->train_station = new_location;

		if (wp->town == nullptr) MakeDefaultName(wp);

		wp->UpdateVirtCoord();

		const StationSpec *spec = StationClass::Get(spec_class)->GetSpec(spec_index);
		byte *layout_ptr = AllocaM(byte, count);
		if (spec == nullptr) {
			/* The layout must be 0 for the 'normal' waypoints by design. */
			memset(layout_ptr, 0, count);
		} else {
			/* But for NewGRF waypoints we like to have their style. */
			GetStationLayout(layout_ptr, count, 1, spec);
		}
		byte map_spec_index = AllocateSpecToStation(spec, wp, true);

		Company *c = Company::Get(wp->owner);
		for (int i = 0; i < count; i++) {
			TileIndex tile = start_tile + i * offset;
			byte old_specindex = HasStationTileRail(tile) ? GetCustomStationSpecIndex(tile) : 0;
			if (!HasStationTileRail(tile)) c->infrastructure.station++;
			bool reserved = IsTileType(tile, MP_RAILWAY) ?
					HasBit(GetRailReservationTrackBits(tile), AxisToTrack(axis)) :
					HasStationReservation(tile);
			MakeRailWaypoint(tile, wp->owner, wp->index, axis, layout_ptr[i], GetRailType(tile));
			SetCustomStationSpecIndex(tile, map_spec_index);
			SetRailStationReservation(tile, reserved);
			MarkTileDirtyByTile(tile);

			DeallocateSpecFromStation(wp, old_specindex);
			YapfNotifyTrackLayoutChange(tile, AxisToTrack(axis));
		}
		DirtyCompanyInfrastructureWindows(wp->owner);
	}

	return CommandCost(EXPENSES_T_TRAIN_CON, count * _price[PR_BUILD_WAYPOINT_RAIL]);
}

/**
 * Build a buoy.
 * @param flags operation to perform
 * @param tile tile where to place the buoy
 * @return the cost of this operation or an error
 */
CommandCost CmdBuildBuoy(DoCommandFlag flags, TileIndex tile)
{
	if (tile == 0 || !HasTileWaterGround(tile)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE);
	if (IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);

	if (!IsTileFlat(tile)) return_cmd_error(STR_ERROR_SITE_UNSUITABLE);

	/* Check if there is an already existing, deleted, waypoint close to us that we can reuse. */
	Waypoint *wp = FindDeletedWaypointCloseTo(tile, STR_SV_STNAME_BUOY, OWNER_NONE);
	if (wp == nullptr && !Waypoint::CanAllocateItem()) return_cmd_error(STR_ERROR_TOO_MANY_STATIONS_LOADING);

	CommandCost cost(EXPENSES_T_SHIP_CON, _price[PR_BUILD_WAYPOINT_BUOY]);
	if (!IsWaterTile(tile)) {
		CommandCost ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags | DC_AUTO, tile);
		if (ret.Failed()) return ret;
		cost.AddCost(ret);
	}

	if (flags & DC_EXEC) {
		if (wp == nullptr) {
			wp = new Waypoint(tile);
		} else {
			/* Move existing (recently deleted) buoy to the new location */
			wp->xy = tile;
			InvalidateWindowData(WC_WAYPOINT_VIEW, wp->index);
		}
		wp->rect.BeforeAddTile(tile, StationRect::ADD_TRY);

		wp->string_id = STR_SV_STNAME_BUOY;

		wp->facilities |= FACIL_DOCK;
		wp->owner = OWNER_NONE;

		wp->build_date = _date;

		if (wp->town == nullptr) MakeDefaultName(wp);

		MakeBuoy(tile, wp->index, GetWaterClass(tile));
		CheckForDockingTile(tile);
		MarkTileDirtyByTile(tile);

		wp->UpdateVirtCoord();
		InvalidateWindowData(WC_WAYPOINT_VIEW, wp->index);
	}

	return cost;
}

/**
 * Remove a buoy
 * @param tile TileIndex been queried
 * @param flags operation to perform
 * @pre IsBuoyTile(tile)
 * @return cost or failure of operation
 */
CommandCost RemoveBuoy(TileIndex tile, DoCommandFlag flags)
{
	/* XXX: strange stuff, allow clearing as invalid company when clearing landscape */
	if (!Company::IsValidID(_current_company) && !(flags & DC_BANKRUPT)) return_cmd_error(INVALID_STRING_ID);

	Waypoint *wp = Waypoint::GetByTile(tile);

	if (HasStationInUse(wp->index, false, _current_company)) return_cmd_error(STR_ERROR_BUOY_IS_IN_USE);
	/* remove the buoy if there is a ship on tile when company goes bankrupt... */
	if (!(flags & DC_BANKRUPT)) {
		CommandCost ret = EnsureNoVehicleOnGround(tile);
		if (ret.Failed()) return ret;
	}

	if (flags & DC_EXEC) {
		wp->facilities &= ~FACIL_DOCK;

		InvalidateWindowData(WC_WAYPOINT_VIEW, wp->index);

		/* We have to set the water tile's state to the same state as before the
		 * buoy was placed. Otherwise one could plant a buoy on a canal edge,
		 * remove it and flood the land (if the canal edge is at level 0) */
		MakeWaterKeepingClass(tile, GetTileOwner(tile));

		wp->rect.AfterRemoveTile(wp, tile);

		wp->UpdateVirtCoord();
		wp->delete_ctr = 0;
	}

	return CommandCost(EXPENSES_T_SHIP_CON, _price[PR_CLEAR_WAYPOINT_BUOY]);
}

/**
 * Check whether the name is unique amongst the waypoints.
 * @param name The name to check.
 * @return True iff the name is unique.
 */
static bool IsUniqueWaypointName(const std::string &name)
{
	for (const Waypoint *wp : Waypoint::Iterate()) {
		if (!wp->name.empty() && wp->name == name) return false;
	}

	return true;
}

/**
 * Rename a waypoint.
 * @param flags type of operation
 * @param waypoint_id id of waypoint
 * @param text the new name or an empty string when resetting to the default
 * @return the cost of this operation or an error
 */
CommandCost CmdRenameWaypoint(DoCommandFlag flags, StationID waypoint_id, const std::string &text)
{
	Waypoint *wp = Waypoint::GetIfValid(waypoint_id);
	if (wp == nullptr) return CMD_ERROR;

	if (wp->owner != OWNER_NONE) {
		CommandCost ret = CheckOwnership(wp->owner);
		if (ret.Failed()) return ret;
	}

	bool reset = text.empty();

	if (!reset) {
		if (Utf8StringLength(text) >= MAX_LENGTH_STATION_NAME_CHARS) return CMD_ERROR;
		if (!IsUniqueWaypointName(text)) return_cmd_error(STR_ERROR_NAME_MUST_BE_UNIQUE);
	}

	if (flags & DC_EXEC) {
		if (reset) {
			wp->name.clear();
		} else {
			wp->name = text;
		}

		wp->UpdateVirtCoord();
	}
	return CommandCost();
}