1
/* $Id: waypoint_cmd.cpp 15903 2009-03-30 23:15:05Z rubidium $ */
3
/** @file waypoint_cmp.cpp Command Handling for waypoints. */
7
#include "command_func.h"
9
#include "economy_func.h"
10
#include "bridge_map.h"
13
#include "yapf/yapf.h"
14
#include "strings_func.h"
16
#include "functions.h"
17
#include "window_func.h"
18
#include "date_func.h"
19
#include "vehicle_func.h"
20
#include "string_func.h"
21
#include "company_func.h"
22
#include "newgrf_station.h"
23
#include "viewport_func.h"
26
#include "table/strings.h"
29
* Update the sign for the waypoint
30
* @param wp Waypoint to update sign */
31
void UpdateWaypointSign(Waypoint *wp)
33
Point pt = RemapCoords2(TileX(wp->xy) * TILE_SIZE, TileY(wp->xy) * TILE_SIZE);
34
SetDParam(0, wp->index);
35
UpdateViewportSignPos(&wp->sign, pt.x, pt.y - 0x20, STR_WAYPOINT_VIEWPORT);
39
* Redraw the sign of a waypoint
40
* @param wp Waypoint to redraw sign */
41
void RedrawWaypointSign(const Waypoint *wp)
43
MarkAllViewportsDirty(
46
wp->sign.left + (wp->sign.width_1 << 2) + 12,
51
* Set the default name for a waypoint
52
* @param wp Waypoint to work on
54
static void MakeDefaultWaypointName(Waypoint *wp)
56
uint32 used = 0; // bitmap of used waypoint numbers, sliding window with 'next' as base
57
uint32 next = 0; // first waypoint number in the bitmap
58
WaypointID idx = 0; // index where we will stop
60
wp->town_index = ClosestTownFromTile(wp->xy, UINT_MAX)->index;
62
/* Find first unused waypoint number belonging to this town. This can never fail,
63
* as long as there can be at most 65535 waypoints in total.
65
* This does 'n * m' search, but with 32bit 'used' bitmap, it needs at most 'n * (1 + ceil(m / 32))'
66
* steps (n - number of waypoints in pool, m - number of waypoints near this town).
67
* Usually, it needs only 'n' steps.
69
* If it wasn't using 'used' and 'idx', it would just search for increasing 'next',
70
* but this way it is faster */
72
WaypointID cid = 0; // current index, goes to GetWaypointPoolSize()-1, then wraps to 0
74
Waypoint *lwp = GetWaypoint(cid);
76
/* check only valid waypoints... */
77
if (lwp->IsValid() && wp != lwp) {
78
/* only waypoints with 'generic' name within the same city */
79
if (lwp->name == NULL && lwp->town_index == wp->town_index) {
80
/* if lwp->town_cn < next, uint will overflow to '+inf' */
81
uint i = (uint)lwp->town_cn - next;
84
SetBit(used, i); // update bitmap
86
/* shift bitmap while the lowest bit is '1';
87
* increase the base of the bitmap too */
91
} while (HasBit(used, 0));
92
/* when we are at 'idx' again at end of the loop and
93
* 'next' hasn't changed, then no waypoint had town_cn == next,
94
* so we can safely use it */
102
if (cid == GetWaypointPoolSize()) cid = 0; // wrap to zero...
103
} while (cid != idx);
105
wp->town_cn = (uint16)next; // set index...
106
wp->name = NULL; // ... and use generic name
110
* Find a deleted waypoint close to a tile.
111
* @param tile to search from
113
static Waypoint *FindDeletedWaypointCloseTo(TileIndex tile)
115
Waypoint *wp, *best = NULL;
118
FOR_ALL_WAYPOINTS(wp) {
119
if (wp->deleted && wp->owner == _current_company) {
120
uint cur_dist = DistanceManhattan(tile, wp->xy);
122
if (cur_dist < thres) {
132
/** Convert existing rail to waypoint. Eg build a waypoint station over
134
* @param tile tile where waypoint will be built
135
* @param flags type of operation
136
* @param p1 graphics for waypoint type, 0 indicates standard graphics
139
* @todo When checking for the tile slope,
140
* distingush between "Flat land required" and "land sloped in wrong direction"
142
CommandCost CmdBuildTrainWaypoint(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
148
/* if custom gfx are used, make sure it is within bounds */
149
if (p1 >= GetNumCustomStations(STAT_CLASS_WAYP)) return CMD_ERROR;
151
if (!IsTileType(tile, MP_RAILWAY) ||
152
GetRailTileType(tile) != RAIL_TILE_NORMAL || (
153
(axis = AXIS_X, GetTrackBits(tile) != TRACK_BIT_X) &&
154
(axis = AXIS_Y, GetTrackBits(tile) != TRACK_BIT_Y)
156
return_cmd_error(STR_1005_NO_SUITABLE_RAILROAD_TRACK);
159
Owner owner = GetTileOwner(tile);
160
if (!CheckOwnership(owner)) return CMD_ERROR;
161
if (!EnsureNoVehicleOnGround(tile)) return CMD_ERROR;
163
tileh = GetTileSlope(tile, NULL);
164
if (tileh != SLOPE_FLAT &&
165
(!_settings_game.construction.build_on_slopes || IsSteepSlope(tileh) || !(tileh & (0x3 << axis)) || !(tileh & ~(0x3 << axis)))) {
166
return_cmd_error(STR_0007_FLAT_LAND_REQUIRED);
169
if (MayHaveBridgeAbove(tile) && IsBridgeAbove(tile)) return_cmd_error(STR_5007_MUST_DEMOLISH_BRIDGE_FIRST);
171
/* Check if there is an already existing, deleted, waypoint close to us that we can reuse. */
172
wp = FindDeletedWaypointCloseTo(tile);
173
if (wp == NULL && !Waypoint::CanAllocateItem()) return CMD_ERROR;
175
if (flags & DC_EXEC) {
177
wp = new Waypoint(tile);
179
wp->town_index = INVALID_TOWN;
183
/* Move existing (recently deleted) waypoint to the new location */
185
/* First we update the destination for all vehicles that
186
* have the old waypoint in their orders. */
188
FOR_ALL_VEHICLES(v) {
189
if (v->type == VEH_TRAIN &&
191
v->current_order.IsType(OT_GOTO_WAYPOINT) &&
192
v->dest_tile == wp->xy) {
197
RedrawWaypointSign(wp);
199
InvalidateWindowData(WC_WAYPOINT_VIEW, wp->index);
203
const StationSpec *statspec;
205
bool reserved = HasBit(GetTrackReservation(tile), AxisToTrack(axis));
206
MakeRailWaypoint(tile, owner, axis, GetRailType(tile), wp->index);
207
SetDepotWaypointReservation(tile, reserved);
208
MarkTileDirtyByTile(tile);
210
statspec = GetCustomStationSpec(STAT_CLASS_WAYP, p1);
212
if (statspec != NULL) {
214
wp->grfid = statspec->grffile->grfid;
215
wp->localidx = statspec->localidx;
217
/* Specified custom graphics do not exist, so use default. */
224
wp->build_date = _date;
226
if (wp->town_index == INVALID_TOWN) MakeDefaultWaypointName(wp);
228
UpdateWaypointSign(wp);
229
RedrawWaypointSign(wp);
230
YapfNotifyTrackLayoutChange(tile, AxisToTrack(axis));
233
return CommandCost(EXPENSES_CONSTRUCTION, _price.build_train_depot);
238
* @param tile from which to remove waypoint
239
* @param flags type of operation
240
* @param justremove will indicate if it is removed from rail or if rails are removed too
241
* @return cost of operation or error
243
CommandCost RemoveTrainWaypoint(TileIndex tile, DoCommandFlag flags, bool justremove)
247
/* Make sure it's a waypoint */
248
if (!IsRailWaypointTile(tile) ||
249
(!CheckTileOwnership(tile) && _current_company != OWNER_WATER) ||
250
!EnsureNoVehicleOnGround(tile)) {
254
if (flags & DC_EXEC) {
255
Track track = GetRailWaypointTrack(tile);
256
wp = GetWaypointByTile(tile);
258
wp->deleted = 30; // let it live for this many days before we do the actual deletion.
259
RedrawWaypointSign(wp);
263
TrackBits tracks = GetRailWaypointBits(tile);
264
bool reserved = GetDepotWaypointReservation(tile);
265
MakeRailNormal(tile, wp->owner, tracks, GetRailType(tile));
266
if (reserved) SetTrackReservation(tile, tracks);
267
MarkTileDirtyByTile(tile);
269
if (GetDepotWaypointReservation(tile)) {
270
v = GetTrainForReservation(tile, track);
271
if (v != NULL) FreeTrainTrackReservation(v);
274
AddTrackToSignalBuffer(tile, track, wp->owner);
276
YapfNotifyTrackLayoutChange(tile, track);
277
if (v != NULL) TryPathReserve(v, true);
280
return CommandCost(EXPENSES_CONSTRUCTION, _price.remove_train_depot);
285
* @param tile tile where waypoint is to be deleted
286
* @param flags type of operation
289
* @return cost of operation or error
291
CommandCost CmdRemoveTrainWaypoint(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
293
return RemoveTrainWaypoint(tile, flags, true);
296
static bool IsUniqueWaypointName(const char *name)
300
FOR_ALL_WAYPOINTS(wp) {
301
if (wp->name != NULL && strcmp(wp->name, name) == 0) return false;
310
* @param flags type of operation
311
* @param p1 id of waypoint
313
* @return cost of operation or error
315
CommandCost CmdRenameWaypoint(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
317
if (!IsValidWaypointID(p1)) return CMD_ERROR;
319
Waypoint *wp = GetWaypoint(p1);
320
if (!CheckOwnership(wp->owner)) return CMD_ERROR;
322
bool reset = StrEmpty(text);
325
if (strlen(text) >= MAX_LENGTH_WAYPOINT_NAME_BYTES) return CMD_ERROR;
326
if (!IsUniqueWaypointName(text)) return_cmd_error(STR_NAME_MUST_BE_UNIQUE);
329
if (flags & DC_EXEC) {
333
MakeDefaultWaypointName(wp); // sets wp->name = NULL
335
wp->name = strdup(text);
338
UpdateWaypointSign(wp);
339
MarkWholeScreenDirty();
341
return CommandCost();